diff options
326 files changed, 10221 insertions, 4947 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk new file mode 100644 index 0000000000..341df78400 --- /dev/null +++ b/CleanSpec.mk @@ -0,0 +1,55 @@ +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# Switching to jemalloc requires deleting these files. +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libart_*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libartd_*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libart_*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libartd_*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 3a19c402db..ee51fcd92e 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -179,6 +179,42 @@ ifeq ($(ART_USE_PORTABLE_COMPILER),true) ART_TEST_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 endif +include $(CLEAR_VARS) +LOCAL_MODULE := libart-gtest +LOCAL_MODULE_TAGS := optional +LOCAL_CPP_EXTENSION := cc +LOCAL_CFLAGS := $(ART_TARGET_CFLAGS) +LOCAL_SRC_FILES := runtime/common_runtime_test.cc compiler/common_compiler_test.cc +LOCAL_C_INCLUDES := $(ART_C_INCLUDES) art/runtime art/compiler +LOCAL_SHARED_LIBRARIES := libcutils libartd libartd-compiler libdl +LOCAL_STATIC_LIBRARIES += libgtest_libc++ +LOCAL_CLANG := $(ART_TARGET_CLANG) +LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk +LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk +include external/libcxx/libcxx.mk +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := libart-gtest +LOCAL_MODULE_TAGS := optional +LOCAL_CPP_EXTENSION := cc +LOCAL_CFLAGS := $(ART_HOST_CFLAGS) +LOCAL_SRC_FILES := runtime/common_runtime_test.cc compiler/common_compiler_test.cc +LOCAL_C_INCLUDES := $(ART_C_INCLUDES) art/runtime art/compiler +LOCAL_SHARED_LIBRARIES := libartd libartd-compiler +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_LDLIBS += -ldl -lpthread +LOCAL_MULTILIB := both +LOCAL_CLANG := $(ART_HOST_CLANG) +LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk +LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk +include external/libcxx/libcxx.mk +include $(BUILD_HOST_SHARED_LIBRARY) + # Variables holding collections of gtest pre-requisits used to run a number of gtests. ART_TEST_HOST_GTEST$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_GTEST$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := @@ -237,7 +273,7 @@ define define-art-gtest-rule-host .PHONY: $$(gtest_rule) $$(gtest_rule): $$(gtest_exe) $$(ART_GTEST_$(1)_HOST_DEPS) $(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_HOST_GTEST_$(file)_DEX)) $$(gtest_deps) - $(hide) ($$(call ART_TEST_SKIP,$$@) && $$< && $$(call ART_TEST_PASSED,$$@)) \ + $(hide) ($$(call ART_TEST_SKIP,$$@) && LD_PRELOAD=libsigchain$$(ART_HOST_SHLIB_EXTENSION) $$< && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@) ART_TEST_HOST_GTEST$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(gtest_rule) @@ -285,12 +321,12 @@ define define-art-gtest LOCAL_MODULE_TAGS := tests endif LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION) - LOCAL_SRC_FILES := $$(art_gtest_filename) runtime/common_runtime_test.cc + LOCAL_SRC_FILES := $$(art_gtest_filename) LOCAL_C_INCLUDES += $$(ART_C_INCLUDES) art/runtime $$(art_gtest_extra_c_includes) - LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) + LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) libart-gtest - # LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk - # LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk + LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk # Mac OS linker doesn't understand --export-dynamic. ifneq ($$(HOST_OS)-$$(art_target_or_host),darwin-host) @@ -304,7 +340,6 @@ define define-art-gtest $$(eval $$(call set-target-local-clang-vars)) $$(eval $$(call set-target-local-cflags-vars,debug)) LOCAL_SHARED_LIBRARIES += libdl libicuuc libicui18n libnativehelper libz libcutils libvixl - LOCAL_STATIC_LIBRARIES += libgtest_libc++ LOCAL_MODULE_PATH_32 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_32) LOCAL_MODULE_PATH_64 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_64) LOCAL_MULTILIB := both @@ -328,10 +363,6 @@ 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 - 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_LDLIBS += -lpthread -ldl LOCAL_IS_HOST_MODULE := true LOCAL_MULTILIB := both diff --git a/build/Android.oat.mk b/build/Android.oat.mk index 61a2cde534..10936a45d6 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -48,11 +48,6 @@ ifneq ($(HOST_PREFER_32_BIT),true) $(eval $(call create-core-oat-host-rules,2ND_)) endif -IMPLICIT_CHECKS_arm := null,stack -IMPLICIT_CHECKS_arm64 := none -IMPLICIT_CHECKS_x86 := none -IMPLICIT_CHECKS_x86_64 := none -IMPLICIT_CHECKS_mips := none define create-core-oat-target-rules $$($(1)TARGET_CORE_IMG_OUT): $$($(1)TARGET_CORE_DEX_FILES) $$(DEX2OATD_DEPENDENCY) @echo "target dex2oat: $$@ ($$?)" @@ -63,7 +58,6 @@ $$($(1)TARGET_CORE_IMG_OUT): $$($(1)TARGET_CORE_DEX_FILES) $$(DEX2OATD_DEPENDENC --oat-location=$$($(1)TARGET_CORE_OAT) --image=$$($(1)TARGET_CORE_IMG_OUT) \ --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(1)TARGET_ARCH) \ --instruction-set-features=$$($(1)TARGET_INSTRUCTION_SET_FEATURES) \ - --implicit-checks=$(IMPLICIT_CHECKS_$($(1)TARGET_ARCH)) \ --android-root=$$(PRODUCT_OUT)/system --include-patch-information # This "renaming" eases declaration in art/Android.mk diff --git a/compiler/Android.mk b/compiler/Android.mk index b469946d20..02dad2ae36 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -72,6 +72,7 @@ LIBART_COMPILER_SRC_FILES := \ dex/verification_results.cc \ dex/vreg_analysis.cc \ dex/ssa_transformation.cc \ + dex/quick_compiler_callbacks.cc \ driver/compiler_driver.cc \ driver/dex_compilation_unit.cc \ jni/quick/arm/calling_convention_arm.cc \ @@ -95,6 +96,7 @@ LIBART_COMPILER_SRC_FILES := \ optimizing/ssa_builder.cc \ optimizing/ssa_liveness_analysis.cc \ optimizing/ssa_type_propagation.cc \ + optimizing/ssa_phi_elimination.cc \ trampolines/trampoline_compiler.cc \ utils/arena_allocator.cc \ utils/arena_bit_vector.cc \ diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc new file mode 100644 index 0000000000..051cfb6dbc --- /dev/null +++ b/compiler/common_compiler_test.cc @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "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" +#include "mirror/object-inl.h" +#include "scoped_thread_state_change.h" +#include "thread-inl.h" +#include "utils.h" + +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 byte* base; + uint32_t code_offset, gc_map_offset; + if (gc_map == nullptr) { + base = reinterpret_cast<const byte*>(code); // Base of data points at code. + base -= kPointerSize; // Move backward so that code_offset != 0. + code_offset = kPointerSize; + 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); + + const CompiledMethod* compiled_method = nullptr; + if (!method->IsAbstract()) { + mirror::DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache(); + const DexFile& dex_file = *dex_cache->GetDexFile(); + compiled_method = + compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, + method->GetDexMethodIndex())); + } + if (compiled_method != nullptr) { + const std::vector<uint8_t>* code = compiled_method->GetQuickCode(); + const void* code_ptr; + if (code != nullptr) { + uint32_t code_size = code->size(); + CHECK_NE(0u, code_size); + const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable(); + uint32_t vmap_table_offset = vmap_table.empty() ? 0u + : sizeof(OatQuickMethodHeader) + vmap_table.size(); + const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable(); + uint32_t mapping_table_offset = mapping_table.empty() ? 0u + : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table.size(); + OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset, + compiled_method->GetFrameSizeInBytes(), + compiled_method->GetCoreSpillMask(), + compiled_method->GetFpSpillMask(), code_size); + + header_code_and_maps_chunks_.push_back(std::vector<uint8_t>()); + std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back(); + size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table.size(); + size_t code_offset = compiled_method->AlignCode(size - code_size); + size_t padding = code_offset - (size - code_size); + chunk->reserve(padding + size); + chunk->resize(sizeof(method_header)); + memcpy(&(*chunk)[0], &method_header, sizeof(method_header)); + chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end()); + chunk->insert(chunk->begin(), mapping_table.begin(), mapping_table.end()); + chunk->insert(chunk->begin(), padding, 0); + chunk->insert(chunk->end(), code->begin(), code->end()); + CHECK_EQ(padding + size, chunk->size()); + code_ptr = &(*chunk)[code_offset]; + } else { + code = compiled_method->GetPortableCode(); + code_ptr = &(*code)[0]; + } + MakeExecutable(code_ptr, code->size()); + 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); + } 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(); + } +} + +void CommonCompilerTest::MakeExecutable(const void* code_start, size_t code_length) { + CHECK(code_start != nullptr); + CHECK_NE(code_length, 0U); + uintptr_t data = reinterpret_cast<uintptr_t>(code_start); + uintptr_t base = RoundDown(data, kPageSize); + uintptr_t limit = RoundUp(data + code_length, kPageSize); + uintptr_t len = limit - base; + int result = mprotect(reinterpret_cast<void*>(base), len, PROT_READ | PROT_WRITE | PROT_EXEC); + CHECK_EQ(result, 0); + + // Flush instruction cache + // Only uses __builtin___clear_cache if GCC >= 4.3.3 +#if GCC_VERSION >= 40303 + __builtin___clear_cache(reinterpret_cast<void*>(base), reinterpret_cast<void*>(base + len)); +#else + // Only warn if not Intel as Intel doesn't have cache flush instructions. +#if !defined(__i386__) && !defined(__x86_64__) + LOG(WARNING) << "UNIMPLEMENTED: cache flush"; +#endif +#endif +} + +void CommonCompilerTest::MakeExecutable(mirror::ClassLoader* class_loader, const char* class_name) { + std::string class_descriptor(DotToDescriptor(class_name)); + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> loader(hs.NewHandle(class_loader)); + mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); + CHECK(klass != nullptr) << "Class not found " << class_name; + for (size_t i = 0; i < klass->NumDirectMethods(); i++) { + MakeExecutable(klass->GetDirectMethod(i)); + } + for (size_t i = 0; i < klass->NumVirtualMethods(); i++) { + MakeExecutable(klass->GetVirtualMethod(i)); + } +} + +void CommonCompilerTest::SetUp() { + CommonRuntimeTest::SetUp(); + { + ScopedObjectAccess soa(Thread::Current()); + + 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 + + runtime_->SetInstructionSet(instruction_set); + for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { + Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i); + if (!runtime_->HasCalleeSaveMethod(type)) { + runtime_->SetCalleeSaveMethod( + runtime_->CreateCalleeSaveMethod(type), type); + } + } + + // TODO: make selectable + 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, + true, new CompilerDriver::DescriptorSet, + 2, true, true, timer_.get())); + } + // We typically don't generate an image in unit tests, disable this optimization by default. + compiler_driver_->SetSupportBootImageFixup(false); +} + +void CommonCompilerTest::SetUpRuntimeOptions(RuntimeOptions* options) { + CommonRuntimeTest::SetUpRuntimeOptions(options); + + compiler_options_.reset(new CompilerOptions); + verification_results_.reset(new VerificationResults(compiler_options_.get())); + method_inliner_map_.reset(new DexFileToMethodInlinerMap); + callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(), + method_inliner_map_.get())); + options->push_back(std::make_pair("compilercallbacks", callbacks_.get())); +} + +void CommonCompilerTest::TearDown() { + timer_.reset(); + compiler_driver_.reset(); + callbacks_.reset(); + method_inliner_map_.reset(); + verification_results_.reset(); + compiler_options_.reset(); + + CommonRuntimeTest::TearDown(); +} + +void CommonCompilerTest::CompileClass(mirror::ClassLoader* class_loader, const char* class_name) { + std::string class_descriptor(DotToDescriptor(class_name)); + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> loader(hs.NewHandle(class_loader)); + mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); + CHECK(klass != nullptr) << "Class not found " << class_name; + for (size_t i = 0; i < klass->NumDirectMethods(); i++) { + CompileMethod(klass->GetDirectMethod(i)); + } + for (size_t i = 0; i < klass->NumVirtualMethods(); i++) { + CompileMethod(klass->GetVirtualMethod(i)); + } +} + +void CommonCompilerTest::CompileMethod(mirror::ArtMethod* method) { + CHECK(method != nullptr); + TimingLogger timings("CommonTest::CompileMethod", false, false); + TimingLogger::ScopedTiming t(__FUNCTION__, &timings); + compiler_driver_->CompileOne(method, &timings); + TimingLogger::ScopedTiming t2("MakeExecutable", &timings); + MakeExecutable(method); +} + +void CommonCompilerTest::CompileDirectMethod(Handle<mirror::ClassLoader> class_loader, + const char* class_name, const char* method_name, + const char* signature) { + std::string class_descriptor(DotToDescriptor(class_name)); + Thread* self = Thread::Current(); + mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); + CHECK(klass != nullptr) << "Class not found " << class_name; + mirror::ArtMethod* method = klass->FindDirectMethod(method_name, signature); + CHECK(method != nullptr) << "Direct method not found: " + << class_name << "." << method_name << signature; + CompileMethod(method); +} + +void CommonCompilerTest::CompileVirtualMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name, + const char* method_name, const char* signature) +SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + std::string class_descriptor(DotToDescriptor(class_name)); + Thread* self = Thread::Current(); + mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); + CHECK(klass != nullptr) << "Class not found " << class_name; + mirror::ArtMethod* method = klass->FindVirtualMethod(method_name, signature); + CHECK(method != NULL) << "Virtual method not found: " + << class_name << "." << method_name << signature; + CompileMethod(method); +} + +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; + image_reservation_.reset(MemMap::MapAnonymous("image reservation", + reinterpret_cast<byte*>(ART_BASE_ADDRESS), + (size_t)100 * 1024 * 1024, // 100MB + PROT_NONE, + false /* no need for 4gb flag with fixed mmap*/, + &error_msg)); + CHECK(image_reservation_.get() != nullptr) << error_msg; +} + +void CommonCompilerTest::UnreserveImageSpace() { + image_reservation_.reset(); +} + +} // namespace art diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index e11f61a285..df06b71c7d 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -17,409 +17,68 @@ #ifndef ART_COMPILER_COMMON_COMPILER_TEST_H_ #define ART_COMPILER_COMMON_COMPILER_TEST_H_ -#include "compiler.h" -#include "compiler_callbacks.h" +#include <list> +#include <vector> + #include "common_runtime_test.h" -#include "dex/quick/dex_file_to_method_inliner_map.h" -#include "dex/verification_results.h" -#include "driver/compiler_callbacks_impl.h" -#include "driver/compiler_driver.h" -#include "driver/compiler_options.h" +#include "oat_file.h" namespace art { +namespace mirror { + class ClassLoader; +} // namespace mirror -#if defined(__arm__) - -#include <sys/ucontext.h> - -// 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 inline 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 inline 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 inline 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; -} +class CompilerDriver; +class CompilerOptions; +class CumulativeLogger; +class DexFileToMethodInlinerMap; +class VerificationResults; -// Normally the ClassLinker supplies this. -extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*); +template<class T> class Handle; class CommonCompilerTest : public CommonRuntimeTest { public: - // Create an OatMethod based on pointers (for unit tests). - OatFile::OatMethod CreateOatMethod(const void* code, - const uint8_t* gc_map) { - CHECK(code != nullptr); - const byte* base; - uint32_t code_offset, gc_map_offset; - if (gc_map == nullptr) { - base = reinterpret_cast<const byte*>(code); // Base of data points at code. - base -= kPointerSize; // Move backward so that code_offset != 0. - code_offset = kPointerSize; - 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 MakeExecutable(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CHECK(method != nullptr); - - const CompiledMethod* compiled_method = nullptr; - if (!method->IsAbstract()) { - mirror::DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache(); - const DexFile& dex_file = *dex_cache->GetDexFile(); - compiled_method = - compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, - method->GetDexMethodIndex())); - } - if (compiled_method != nullptr) { - const std::vector<uint8_t>* code = compiled_method->GetQuickCode(); - const void* code_ptr; - if (code != nullptr) { - uint32_t code_size = code->size(); - CHECK_NE(0u, code_size); - const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable(); - uint32_t vmap_table_offset = vmap_table.empty() ? 0u - : sizeof(OatQuickMethodHeader) + vmap_table.size(); - const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable(); - uint32_t mapping_table_offset = mapping_table.empty() ? 0u - : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table.size(); - OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset, - compiled_method->GetFrameSizeInBytes(), - compiled_method->GetCoreSpillMask(), - compiled_method->GetFpSpillMask(), code_size); - - header_code_and_maps_chunks_.push_back(std::vector<uint8_t>()); - std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back(); - size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table.size(); - size_t code_offset = compiled_method->AlignCode(size - code_size); - size_t padding = code_offset - (size - code_size); - chunk->reserve(padding + size); - chunk->resize(sizeof(method_header)); - memcpy(&(*chunk)[0], &method_header, sizeof(method_header)); - chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end()); - chunk->insert(chunk->begin(), mapping_table.begin(), mapping_table.end()); - chunk->insert(chunk->begin(), padding, 0); - chunk->insert(chunk->end(), code->begin(), code->end()); - CHECK_EQ(padding + size, chunk->size()); - code_ptr = &(*chunk)[code_offset]; - } else { - code = compiled_method->GetPortableCode(); - code_ptr = &(*code)[0]; - } - MakeExecutable(code_ptr, code->size()); - 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); - } 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); + CommonCompilerTest(); + ~CommonCompilerTest(); - 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(); - } - } + // Create an OatMethod based on pointers (for unit tests). + OatFile::OatMethod CreateOatMethod(const void* code, const uint8_t* gc_map); - static void MakeExecutable(const void* code_start, size_t code_length) { - CHECK(code_start != nullptr); - CHECK_NE(code_length, 0U); - uintptr_t data = reinterpret_cast<uintptr_t>(code_start); - uintptr_t base = RoundDown(data, kPageSize); - uintptr_t limit = RoundUp(data + code_length, kPageSize); - uintptr_t len = limit - base; - int result = mprotect(reinterpret_cast<void*>(base), len, PROT_READ | PROT_WRITE | PROT_EXEC); - CHECK_EQ(result, 0); + void MakeExecutable(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Flush instruction cache - // Only uses __builtin___clear_cache if GCC >= 4.3.3 -#if GCC_VERSION >= 40303 - __builtin___clear_cache(reinterpret_cast<void*>(base), reinterpret_cast<void*>(base + len)); -#else - LOG(WARNING) << "UNIMPLEMENTED: cache flush"; -#endif - } + static void MakeExecutable(const void* code_start, size_t code_length); void MakeExecutable(mirror::ClassLoader* class_loader, const char* class_name) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - std::string class_descriptor(DotToDescriptor(class_name)); - Thread* self = Thread::Current(); - StackHandleScope<1> hs(self); - Handle<mirror::ClassLoader> loader(hs.NewHandle(class_loader)); - mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); - CHECK(klass != nullptr) << "Class not found " << class_name; - for (size_t i = 0; i < klass->NumDirectMethods(); i++) { - MakeExecutable(klass->GetDirectMethod(i)); - } - for (size_t i = 0; i < klass->NumVirtualMethods(); i++) { - MakeExecutable(klass->GetVirtualMethod(i)); - } - } + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); protected: - virtual void SetUp() { - CommonRuntimeTest::SetUp(); - { - ScopedObjectAccess soa(Thread::Current()); - - InstructionSet instruction_set = kNone; - - // Take the default set of instruction features from the build. - InstructionSetFeatures instruction_set_features = - ParseFeatureList(Runtime::GetDefaultInstructionSetFeatures()); - -#if defined(__arm__) - instruction_set = kThumb2; - 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); -#elif defined(__aarch64__) - instruction_set = kArm64; -#elif defined(__mips__) - instruction_set = kMips; -#elif defined(__i386__) - instruction_set = kX86; -#elif defined(__x86_64__) - instruction_set = kX86_64; -#endif - - runtime_->SetInstructionSet(instruction_set); - for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { - Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i); - if (!runtime_->HasCalleeSaveMethod(type)) { - runtime_->SetCalleeSaveMethod( - runtime_->CreateCalleeSaveMethod(type), type); - } - } - - // TODO: make selectable - 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, - true, new CompilerDriver::DescriptorSet, - 2, true, true, timer_.get())); - } - // We typically don't generate an image in unit tests, disable this optimization by default. - compiler_driver_->SetSupportBootImageFixup(false); - } - - virtual void SetUpRuntimeOptions(Runtime::Options *options) { - CommonRuntimeTest::SetUpRuntimeOptions(options); - - compiler_options_.reset(new CompilerOptions); - verification_results_.reset(new VerificationResults(compiler_options_.get())); - method_inliner_map_.reset(new DexFileToMethodInlinerMap); - callbacks_.reset(new CompilerCallbacksImpl(verification_results_.get(), - method_inliner_map_.get())); - options->push_back(std::make_pair("compilercallbacks", callbacks_.get())); - } + virtual void SetUp(); - virtual void TearDown() { - timer_.reset(); - compiler_driver_.reset(); - callbacks_.reset(); - method_inliner_map_.reset(); - verification_results_.reset(); - compiler_options_.reset(); + virtual void SetUpRuntimeOptions(RuntimeOptions *options); - CommonRuntimeTest::TearDown(); - } + virtual void TearDown(); void CompileClass(mirror::ClassLoader* class_loader, const char* class_name) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - std::string class_descriptor(DotToDescriptor(class_name)); - Thread* self = Thread::Current(); - StackHandleScope<1> hs(self); - Handle<mirror::ClassLoader> loader(hs.NewHandle(class_loader)); - mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader); - CHECK(klass != nullptr) << "Class not found " << class_name; - for (size_t i = 0; i < klass->NumDirectMethods(); i++) { - CompileMethod(klass->GetDirectMethod(i)); - } - for (size_t i = 0; i < klass->NumVirtualMethods(); i++) { - CompileMethod(klass->GetVirtualMethod(i)); - } - } + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CompileMethod(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CHECK(method != nullptr); - TimingLogger timings("CommonTest::CompileMethod", false, false); - TimingLogger::ScopedTiming t(__FUNCTION__, &timings); - compiler_driver_->CompileOne(method, &timings); - TimingLogger::ScopedTiming t2("MakeExecutable", &timings); - MakeExecutable(method); - } + void CompileMethod(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void CompileDirectMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name, const char* method_name, const char* signature) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - std::string class_descriptor(DotToDescriptor(class_name)); - Thread* self = Thread::Current(); - mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); - CHECK(klass != nullptr) << "Class not found " << class_name; - mirror::ArtMethod* method = klass->FindDirectMethod(method_name, signature); - CHECK(method != nullptr) << "Direct method not found: " - << class_name << "." << method_name << signature; - CompileMethod(method); - } + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void CompileVirtualMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name, const char* method_name, const char* signature) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - std::string class_descriptor(DotToDescriptor(class_name)); - Thread* self = Thread::Current(); - mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); - CHECK(klass != nullptr) << "Class not found " << class_name; - mirror::ArtMethod* method = klass->FindVirtualMethod(method_name, signature); - CHECK(method != NULL) << "Virtual method not found: " - << class_name << "." << method_name << signature; - CompileMethod(method); - } + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void 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; - image_reservation_.reset(MemMap::MapAnonymous("image reservation", - reinterpret_cast<byte*>(ART_BASE_ADDRESS), - (size_t)100 * 1024 * 1024, // 100MB - PROT_NONE, - false /* no need for 4gb flag with fixed mmap*/, - &error_msg)); - CHECK(image_reservation_.get() != nullptr) << error_msg; - } + void ReserveImageSpace(); - void UnreserveImageSpace() { - image_reservation_.reset(); - } + void UnreserveImageSpace(); std::unique_ptr<CompilerOptions> compiler_options_; std::unique_ptr<VerificationResults> verification_results_; std::unique_ptr<DexFileToMethodInlinerMap> method_inliner_map_; - std::unique_ptr<CompilerCallbacksImpl> callbacks_; + std::unique_ptr<CompilerCallbacks> callbacks_; std::unique_ptr<CompilerDriver> compiler_driver_; std::unique_ptr<CumulativeLogger> timer_; diff --git a/runtime/compiled_class.h b/compiler/compiled_class.h index c53d500502..b88d613ad5 100644 --- a/runtime/compiled_class.h +++ b/compiler/compiled_class.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_COMPILED_CLASS_H_ -#define ART_RUNTIME_COMPILED_CLASS_H_ +#ifndef ART_COMPILER_COMPILED_CLASS_H_ +#define ART_COMPILER_COMPILED_CLASS_H_ #include "mirror/class.h" @@ -34,4 +34,4 @@ class CompiledClass { } // namespace art -#endif // ART_RUNTIME_COMPILED_CLASS_H_ +#endif // ART_COMPILER_COMPILED_CLASS_H_ diff --git a/compiler/compilers.cc b/compiler/compilers.cc index f940b54f22..bac1f12823 100644 --- a/compiler/compilers.cc +++ b/compiler/compilers.cc @@ -15,6 +15,7 @@ */ #include "compilers.h" + #include "dex/mir_graph.h" #include "dex/quick/mir_to_lir.h" #include "elf_writer_quick.h" diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h index eb897f00e3..d1d5ad9715 100644 --- a/compiler/dex/bb_optimizations.h +++ b/compiler/dex/bb_optimizations.h @@ -71,26 +71,28 @@ class CacheMethodLoweringInfo : public PassME { }; /** - * @class CallInlining - * @brief Perform method inlining pass. + * @class SpecialMethodInliner + * @brief Performs method inlining pass on special kinds of methods. + * @details Special methods are methods that fall in one of the following categories: + * empty, instance getter, instance setter, argument return, and constant return. */ -class CallInlining : public PassME { +class SpecialMethodInliner : public PassME { public: - CallInlining() : PassME("CallInlining") { + SpecialMethodInliner() : PassME("SpecialMethodInliner") { } bool Gate(const PassDataHolder* data) const { DCHECK(data != nullptr); CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit; DCHECK(cUnit != nullptr); - return cUnit->mir_graph->InlineCallsGate(); + return cUnit->mir_graph->InlineSpecialMethodsGate(); } void Start(PassDataHolder* data) const { DCHECK(data != nullptr); CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit; DCHECK(cUnit != nullptr); - cUnit->mir_graph->InlineCallsStart(); + cUnit->mir_graph->InlineSpecialMethodsStart(); } bool Worker(const PassDataHolder* data) const { @@ -100,7 +102,7 @@ class CallInlining : public PassME { DCHECK(cUnit != nullptr); BasicBlock* bb = pass_me_data_holder->bb; DCHECK(bb != nullptr); - cUnit->mir_graph->InlineCalls(bb); + cUnit->mir_graph->InlineSpecialMethods(bb); // No need of repeating, so just return false. return false; } @@ -109,7 +111,7 @@ class CallInlining : public PassME { DCHECK(data != nullptr); CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit; DCHECK(cUnit != nullptr); - cUnit->mir_graph->InlineCallsEnd(); + cUnit->mir_graph->InlineSpecialMethodsEnd(); } }; diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 799a742032..69adb35766 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -440,19 +440,23 @@ std::ostream& operator<<(std::ostream& os, const DividePattern& pattern); /** * @brief Memory barrier types (see "The JSR-133 Cookbook for Compiler Writers"). - * @details Without context sensitive analysis, the most conservative set of barriers - * must be issued to ensure the Java Memory Model. Thus the recipe is as follows: - * -# Use StoreStore barrier before volatile store. - * -# Use StoreLoad barrier after volatile store. - * -# Use LoadLoad and LoadStore barrier after each volatile load. + * @details We define the combined barrier types that are actually required + * by the Java Memory Model, rather than using exactly the terminology from + * the JSR-133 cookbook. These should, in many cases, be replaced by acquire/release + * primitives. Note that the JSR-133 cookbook generally does not deal with + * store atomicity issues, and the recipes there are not always entirely sufficient. + * The current recipe is as follows: + * -# Use AnyStore ~= (LoadStore | StoreStore) ~= release barrier before volatile store. + * -# Use AnyAny barrier after volatile store. (StoreLoad is as expensive.) + * -# Use LoadAny barrier ~= (LoadLoad | LoadStore) ~= acquire barrierafter each volatile load. * -# Use StoreStore barrier after all stores but before return from any constructor whose - * class has final fields. + * class has final fields. */ enum MemBarrierKind { - kLoadStore, - kLoadLoad, + kAnyStore, + kLoadAny, kStoreStore, - kStoreLoad + kAnyAny }; std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind); @@ -544,6 +548,14 @@ enum VolatileKind { std::ostream& operator<<(std::ostream& os, const VolatileKind& kind); +enum WideKind { + kNotWide, // Non-wide view + kWide, // Wide view + kRef // Ref width +}; + +std::ostream& operator<<(std::ostream& os, const WideKind& kind); + } // namespace art #endif // ART_COMPILER_DEX_COMPILER_ENUMS_H_ diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index f3ef796480..d097500a77 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -96,8 +96,6 @@ static constexpr uint32_t kDisabledOptimizationsPerISA[] = { ~0U, // 2 = kArm64. TODO(Arm64): enable optimizations once backend is mature enough. (1 << kLoadStoreElimination) | - (1 << kLoadHoisting) | - (1 << kBBOpt) | 0, // 3 = kThumb2. 0, @@ -575,7 +573,7 @@ static bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, // Check if we support the byte code. if (std::find(unsupport_list, unsupport_list + unsupport_list_size, opcode) != unsupport_list + unsupport_list_size) { - if (!cu.mir_graph->IsPseudoMirOp(opcode)) { + if (!MIR::DecodedInstruction::IsPseudoMirOp(opcode)) { VLOG(compiler) << "Unsupported dalvik byte code : " << mir->dalvikInsn.opcode; } else { diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc index e372206228..3de448344a 100644 --- a/compiler/dex/mir_analysis.cc +++ b/compiler/dex/mir_analysis.cc @@ -902,7 +902,7 @@ void MIRGraph::AnalyzeBlock(BasicBlock* bb, MethodStats* stats) { while (!done) { tbb->visited = true; for (MIR* mir = tbb->first_mir_insn; mir != NULL; mir = mir->next) { - if (IsPseudoMirOp(mir->dalvikInsn.opcode)) { + if (MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) { // Skip any MIR pseudo-op. continue; } diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index bc99a272a6..b82c5c7f00 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -909,6 +909,16 @@ void MIRGraph::HandleDef(ArenaBitVector* def_v, int dalvik_reg_id) { def_v->SetBit(dalvik_reg_id); } +void MIRGraph::HandleExtended(ArenaBitVector* use_v, ArenaBitVector* def_v, + ArenaBitVector* live_in_v, + const MIR::DecodedInstruction& d_insn) { + switch (static_cast<int>(d_insn.opcode)) { + default: + LOG(ERROR) << "Unexpected Extended Opcode " << d_insn.opcode; + break; + } +} + /* * Find out live-in variables for natural loops. Variables that are live-in in * the main loop body are considered to be defined in the entry block. @@ -966,6 +976,9 @@ bool MIRGraph::FindLocalLiveIn(BasicBlock* bb) { HandleDef(def_v, d_insn->vA+1); } } + if (df_attributes & DF_FORMAT_EXTENDED) { + HandleExtended(use_v, def_v, live_in_v, mir->dalvikInsn); + } } return true; } @@ -1048,6 +1061,14 @@ void MIRGraph::DataFlowSSAFormat3RC(MIR* mir) { } } +void MIRGraph::DataFlowSSAFormatExtended(MIR* mir) { + switch (static_cast<int>(mir->dalvikInsn.opcode)) { + default: + LOG(ERROR) << "Missing case for extended MIR: " << mir->dalvikInsn.opcode; + break; + } +} + /* Entry function to convert a block into SSA representation */ bool MIRGraph::DoSSAConversion(BasicBlock* bb) { MIR* mir; @@ -1063,7 +1084,7 @@ bool MIRGraph::DoSSAConversion(BasicBlock* bb) { uint64_t df_attributes = GetDataFlowAttributes(mir); // If not a pseudo-op, note non-leaf or can throw - if (!IsPseudoMirOp(mir->dalvikInsn.opcode)) { + if (!MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) { int flags = Instruction::FlagsOf(mir->dalvikInsn.opcode); if ((flags & Instruction::kInvoke) != 0 && (mir->optimization_flags & MIR_INLINED) == 0) { @@ -1083,6 +1104,11 @@ bool MIRGraph::DoSSAConversion(BasicBlock* bb) { continue; } + if (df_attributes & DF_FORMAT_EXTENDED) { + DataFlowSSAFormatExtended(mir); + continue; + } + if (df_attributes & DF_HAS_USES) { if (df_attributes & DF_UA) { num_uses++; diff --git a/compiler/dex/mir_field_info.cc b/compiler/dex/mir_field_info.cc index 98866d9c2d..68247b7807 100644 --- a/compiler/dex/mir_field_info.cc +++ b/compiler/dex/mir_field_info.cc @@ -62,9 +62,9 @@ void MirIFieldLoweringInfo::Resolve(CompilerDriver* compiler_driver, compiler_driver->GetResolvedFieldDexFileLocation(resolved_field, &it->declaring_dex_file_, &it->declaring_class_idx_, &it->declaring_field_idx_); bool is_volatile = compiler_driver->IsFieldVolatile(resolved_field); - + it->field_offset_ = resolved_field->GetOffset(); std::pair<bool, bool> fast_path = compiler_driver->IsFastInstanceField( - dex_cache.Get(), referrer_class.Get(), resolved_field, field_idx, &it->field_offset_); + dex_cache.Get(), referrer_class.Get(), resolved_field, field_idx); it->flags_ = 0u | // Without kFlagIsStatic. (is_volatile ? kFlagIsVolatile : 0u) | (fast_path.first ? kFlagFastGet : 0u) | diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 4fbace26e7..1c8a9b5079 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -193,14 +193,16 @@ BasicBlock* MIRGraph::SplitBlock(DexOffset code_offset, bottom_block->successor_block_list_type = orig_block->successor_block_list_type; bottom_block->successor_blocks = orig_block->successor_blocks; orig_block->successor_block_list_type = kNotUsed; - orig_block->successor_blocks = NULL; + orig_block->successor_blocks = nullptr; GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bottom_block->successor_blocks); while (true) { SuccessorBlockInfo* successor_block_info = iterator.Next(); - if (successor_block_info == NULL) break; + if (successor_block_info == nullptr) break; BasicBlock* bb = GetBasicBlock(successor_block_info->block); - bb->predecessors->Delete(orig_block->id); - bb->predecessors->Insert(bottom_block->id); + if (bb != nullptr) { + bb->predecessors->Delete(orig_block->id); + bb->predecessors->Insert(bottom_block->id); + } } } @@ -222,7 +224,7 @@ BasicBlock* MIRGraph::SplitBlock(DexOffset code_offset, DCHECK(insn == bottom_block->first_mir_insn); DCHECK_EQ(insn->offset, bottom_block->start_offset); DCHECK(static_cast<int>(insn->dalvikInsn.opcode) == kMirOpCheck || - !IsPseudoMirOp(insn->dalvikInsn.opcode)); + !MIR::DecodedInstruction::IsPseudoMirOp(insn->dalvikInsn.opcode)); DCHECK_EQ(dex_pc_to_block_map_.Get(insn->offset), orig_block->id); MIR* p = insn; dex_pc_to_block_map_.Put(p->offset, bottom_block->id); @@ -237,7 +239,7 @@ BasicBlock* MIRGraph::SplitBlock(DexOffset code_offset, * CHECK and work portions. Since the 2nd half of a split operation is always * the first in a BasicBlock, we can't hit it here. */ - if ((opcode == kMirOpCheck) || !IsPseudoMirOp(opcode)) { + if ((opcode == kMirOpCheck) || !MIR::DecodedInstruction::IsPseudoMirOp(opcode)) { DCHECK_EQ(dex_pc_to_block_map_.Get(p->offset), orig_block->id); dex_pc_to_block_map_.Put(p->offset, bottom_block->id); } @@ -861,11 +863,17 @@ uint64_t MIRGraph::GetDataFlowAttributes(MIR* mir) { /* Dump the CFG into a DOT graph */ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suffix) { FILE* file; + static AtomicInteger cnt(0); + + // Increment counter to get a unique file number. + cnt++; + std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file)); ReplaceSpecialChars(fname); - fname = StringPrintf("%s%s%x%s.dot", dir_prefix, fname.c_str(), + fname = StringPrintf("%s%s%x%s_%d.dot", dir_prefix, fname.c_str(), GetBasicBlock(GetEntryBlock()->fall_through)->start_offset, - suffix == nullptr ? "" : suffix); + suffix == nullptr ? "" : suffix, + cnt.LoadRelaxed()); file = fopen(fname.c_str(), "w"); if (file == NULL) { return; @@ -882,6 +890,7 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suff BasicBlock* bb = GetBasicBlock(block_idx); if (bb == NULL) continue; if (bb->block_type == kDead) continue; + if (bb->hidden) continue; if (bb->block_type == kEntryBlock) { fprintf(file, " entry_%d [shape=Mdiamond];\n", bb->id); } else if (bb->block_type == kExitBlock) { @@ -916,7 +925,8 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suff } else { fprintf(file, " {%04x %s %s %s %s\\l}%s\\\n", mir->offset, mir->ssa_rep ? GetDalvikDisassembly(mir) : - !IsPseudoMirOp(opcode) ? Instruction::Name(mir->dalvikInsn.opcode) : + !MIR::DecodedInstruction::IsPseudoMirOp(opcode) ? + Instruction::Name(mir->dalvikInsn.opcode) : extended_mir_op_names_[opcode - kMirOpFirst], (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0 ? " no_rangecheck" : " ", (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0 ? " no_nullcheck" : " ", @@ -1222,7 +1232,7 @@ char* MIRGraph::GetDalvikDisassembly(const MIR* mir) { nop = true; } - if (IsPseudoMirOp(opcode)) { + if (MIR::DecodedInstruction::IsPseudoMirOp(opcode)) { str.append(extended_mir_op_names_[opcode - kMirOpFirst]); } else { dalvik_format = Instruction::FormatOf(insn.opcode); @@ -1693,11 +1703,13 @@ BasicBlock* ChildBlockIterator::Next() { // We visited both taken and fallthrough. Now check if we have successors we need to visit. if (have_successors_ == true) { // Get information about next successor block. - SuccessorBlockInfo* successor_block_info = successor_iter_.Next(); - - // If we don't have anymore successors, return nullptr. - if (successor_block_info != nullptr) { - return mir_graph_->GetBasicBlock(successor_block_info->block); + for (SuccessorBlockInfo* successor_block_info = successor_iter_.Next(); + successor_block_info != nullptr; + successor_block_info = successor_iter_.Next()) { + // If block was replaced by zero block, take next one. + if (successor_block_info->block != NullBasicBlockId) { + return mir_graph_->GetBasicBlock(successor_block_info->block); + } } } diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index d09732891c..1556a19da7 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -80,6 +80,7 @@ enum DataFlowAttributePos { kSetsConst, 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]. @@ -118,6 +119,7 @@ enum DataFlowAttributePos { #define DF_SETS_CONST (UINT64_C(1) << kSetsConst) #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) @@ -284,34 +286,46 @@ struct MIR { */ bool GetConstant(int64_t* ptr_value, bool* wide) const; + static bool IsPseudoMirOp(Instruction::Code opcode) { + return static_cast<int>(opcode) >= static_cast<int>(kMirOpFirst); + } + + static bool IsPseudoMirOp(int opcode) { + return opcode >= static_cast<int>(kMirOpFirst); + } + + bool IsInvoke() const { + return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kInvoke) == Instruction::kInvoke); + } + bool IsStore() const { - return ((Instruction::FlagsOf(opcode) & Instruction::kStore) == Instruction::kStore); + return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kStore) == Instruction::kStore); } bool IsLoad() const { - return ((Instruction::FlagsOf(opcode) & Instruction::kLoad) == Instruction::kLoad); + return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kLoad) == Instruction::kLoad); } bool IsConditionalBranch() const { - return (Instruction::FlagsOf(opcode) == (Instruction::kContinue | Instruction::kBranch)); + return !IsPseudoMirOp(opcode) && (Instruction::FlagsOf(opcode) == (Instruction::kContinue | Instruction::kBranch)); } /** * @brief Is the register C component of the decoded instruction a constant? */ bool IsCFieldOrConstant() const { - return ((Instruction::FlagsOf(opcode) & Instruction::kRegCFieldOrConstant) == Instruction::kRegCFieldOrConstant); + return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kRegCFieldOrConstant) == Instruction::kRegCFieldOrConstant); } /** * @brief Is the register C component of the decoded instruction a constant? */ bool IsBFieldOrConstant() const { - return ((Instruction::FlagsOf(opcode) & Instruction::kRegBFieldOrConstant) == Instruction::kRegBFieldOrConstant); + return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kRegBFieldOrConstant) == Instruction::kRegBFieldOrConstant); } bool IsCast() const { - return ((Instruction::FlagsOf(opcode) & Instruction::kCast) == Instruction::kCast); + return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kCast) == Instruction::kCast); } /** @@ -321,11 +335,11 @@ struct MIR { * when crossing such an instruction. */ bool Clobbers() const { - return ((Instruction::FlagsOf(opcode) & Instruction::kClobber) == Instruction::kClobber); + return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kClobber) == Instruction::kClobber); } bool IsLinear() const { - return (Instruction::FlagsOf(opcode) & (Instruction::kAdd | Instruction::kSubtract)) != 0; + return !IsPseudoMirOp(opcode) && (Instruction::FlagsOf(opcode) & (Instruction::kAdd | Instruction::kSubtract)) != 0; } } dalvikInsn; @@ -877,14 +891,6 @@ class MIRGraph { return backward_branches_ + forward_branches_; } - static bool IsPseudoMirOp(Instruction::Code opcode) { - return static_cast<int>(opcode) >= static_cast<int>(kMirOpFirst); - } - - static bool IsPseudoMirOp(int opcode) { - return opcode >= static_cast<int>(kMirOpFirst); - } - // Is this vreg in the in set? bool IsInVReg(int vreg) { return (vreg >= cu_->num_regs); @@ -956,10 +962,10 @@ class MIRGraph { void ComputeTopologicalSortOrder(); BasicBlock* CreateNewBB(BBType block_type); - bool InlineCallsGate(); - void InlineCallsStart(); - void InlineCalls(BasicBlock* bb); - void InlineCallsEnd(); + bool InlineSpecialMethodsGate(); + void InlineSpecialMethodsStart(); + void InlineSpecialMethods(BasicBlock* bb); + void InlineSpecialMethodsEnd(); /** * @brief Perform the initial preparation for the Method Uses. @@ -1059,6 +1065,9 @@ class MIRGraph { void HandleLiveInUse(ArenaBitVector* use_v, ArenaBitVector* def_v, ArenaBitVector* live_in_v, int dalvik_reg_id); void HandleDef(ArenaBitVector* def_v, int dalvik_reg_id); + void HandleExtended(ArenaBitVector* use_v, ArenaBitVector* def_v, + ArenaBitVector* live_in_v, + const MIR::DecodedInstruction& d_insn); bool DoSSAConversion(BasicBlock* bb); bool InvokeUsesMethodStar(MIR* mir); int ParseInsn(const uint16_t* code_ptr, MIR::DecodedInstruction* decoded_instruction); @@ -1080,6 +1089,7 @@ class MIRGraph { void HandleSSAUse(int* uses, int dalvik_reg, int reg_index); void DataFlowSSAFormat35C(MIR* mir); void DataFlowSSAFormat3RC(MIR* mir); + void DataFlowSSAFormatExtended(MIR* mir); bool FindLocalLiveIn(BasicBlock* bb); bool VerifyPredInfo(BasicBlock* bb); BasicBlock* NeedsVisit(BasicBlock* bb); diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index dc1057f277..869c48f66c 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -137,7 +137,7 @@ MIR* MIRGraph::FindMoveResult(BasicBlock* bb, MIR* mir) { break; } // Keep going if pseudo op, otherwise terminate - if (IsPseudoMirOp(mir->dalvikInsn.opcode)) { + if (MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) { mir = AdvanceMIR(&tbb, mir); } else { mir = NULL; @@ -877,7 +877,7 @@ bool MIRGraph::EliminateNullChecksAndInferTypes(BasicBlock* bb) { struct BasicBlock* next_bb = GetBasicBlock(bb->fall_through); for (MIR* tmir = next_bb->first_mir_insn; tmir != NULL; tmir =tmir->next) { - if (IsPseudoMirOp(tmir->dalvikInsn.opcode)) { + if (MIR::DecodedInstruction::IsPseudoMirOp(tmir->dalvikInsn.opcode)) { continue; } // First non-pseudo should be MOVE_RESULT_OBJECT @@ -1220,7 +1220,7 @@ void MIRGraph::ComputeInlineIFieldLoweringInfo(uint16_t field_idx, MIR* invoke, iget_or_iput->meta.ifield_lowering_info = field_info_index; } -bool MIRGraph::InlineCallsGate() { +bool MIRGraph::InlineSpecialMethodsGate() { if ((cu_->disable_opt & (1 << kSuppressMethodInlining)) != 0 || method_lowering_infos_.Size() == 0u) { return false; @@ -1232,7 +1232,7 @@ bool MIRGraph::InlineCallsGate() { return true; } -void MIRGraph::InlineCallsStart() { +void MIRGraph::InlineSpecialMethodsStart() { // Prepare for inlining getters/setters. Since we're inlining at most 1 IGET/IPUT from // each INVOKE, we can index the data by the MIR::meta::method_lowering_info index. @@ -1246,12 +1246,12 @@ void MIRGraph::InlineCallsStart() { temp_bit_vector_size_ * sizeof(*temp_insn_data_), kArenaAllocGrowableArray)); } -void MIRGraph::InlineCalls(BasicBlock* bb) { +void MIRGraph::InlineSpecialMethods(BasicBlock* bb) { if (bb->block_type != kDalvikByteCode) { return; } for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { - if (IsPseudoMirOp(mir->dalvikInsn.opcode)) { + if (MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) { continue; } if (!(Instruction::FlagsOf(mir->dalvikInsn.opcode) & Instruction::kInvoke)) { @@ -1270,17 +1270,17 @@ void MIRGraph::InlineCalls(BasicBlock* bb) { MethodReference target = method_info.GetTargetMethod(); if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(target.dex_file) ->GenInline(this, bb, mir, target.dex_method_index)) { - if (cu_->verbose) { - LOG(INFO) << "In \"" << PrettyMethod(cu_->method_idx, *cu_->dex_file) - << "\" @0x" << std::hex << mir->offset - << " inlined " << method_info.GetInvokeType() << " (" << sharp_type << ") call to \"" - << PrettyMethod(target.dex_method_index, *target.dex_file) << "\""; + if (cu_->verbose || cu_->print_pass) { + LOG(INFO) << "SpecialMethodInliner: Inlined " << method_info.GetInvokeType() << " (" + << sharp_type << ") call to \"" << PrettyMethod(target.dex_method_index, *target.dex_file) + << "\" from \"" << PrettyMethod(cu_->method_idx, *cu_->dex_file) + << "\" @0x" << std::hex << mir->offset; } } } } -void MIRGraph::InlineCallsEnd() { +void MIRGraph::InlineSpecialMethodsEnd() { DCHECK(temp_insn_data_ != nullptr); temp_insn_data_ = nullptr; DCHECK(temp_bit_vector_ != nullptr); diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc index 4c9bed65dc..c72a4a667e 100644 --- a/compiler/dex/pass_driver_me_opts.cc +++ b/compiler/dex/pass_driver_me_opts.cc @@ -35,7 +35,7 @@ template<> const Pass* const PassDriver<PassDriverMEOpts>::g_passes[] = { GetPassInstance<CacheFieldLoweringInfo>(), GetPassInstance<CacheMethodLoweringInfo>(), - GetPassInstance<CallInlining>(), + GetPassInstance<SpecialMethodInliner>(), GetPassInstance<CodeLayout>(), GetPassInstance<NullCheckEliminationAndTypeInference>(), GetPassInstance<ClassInitCheckElimination>(), diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 04d6898e36..6b96e929aa 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -190,7 +190,7 @@ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { null_check_branch = nullptr; // No null check. } else { // If the null-check fails its handled by the slow-path to reduce exception related meta-data. - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { null_check_branch = OpCmpImmBranch(kCondEq, rs_r0, 0, NULL); } } @@ -218,7 +218,7 @@ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { LIR* success_target = NewLIR0(kPseudoTargetLabel); lock_success_branch->target = success_target; - GenMemBarrier(kLoadLoad); + GenMemBarrier(kLoadAny); } else { // Explicit null-check as slow-path is entered using an IT. GenNullCheck(rs_r0, opt_flags); @@ -240,7 +240,7 @@ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { LIR* call_inst = OpReg(kOpBlx/*ne*/, rs_rARM_LR); OpEndIT(it); MarkSafepointPC(call_inst); - GenMemBarrier(kLoadLoad); + GenMemBarrier(kLoadAny); } } @@ -261,7 +261,7 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { null_check_branch = nullptr; // No null check. } else { // If the null-check fails its handled by the slow-path to reduce exception related meta-data. - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { null_check_branch = OpCmpImmBranch(kCondEq, rs_r0, 0, NULL); } } @@ -269,7 +269,7 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { MarkPossibleNullPointerException(opt_flags); LoadConstantNoClobber(rs_r3, 0); LIR* slow_unlock_branch = OpCmpBranch(kCondNe, rs_r1, rs_r2, NULL); - GenMemBarrier(kStoreLoad); + GenMemBarrier(kAnyStore); Store32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3); LIR* unlock_success_branch = OpUnconditionalBranch(NULL); @@ -298,7 +298,7 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { OpRegReg(kOpCmp, rs_r1, rs_r2); LIR* it = OpIT(kCondEq, "EE"); - if (GenMemBarrier(kStoreLoad)) { + if (GenMemBarrier(kAnyStore)) { UpdateIT(it, "TEE"); } Store32Disp/*eq*/(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3); @@ -362,7 +362,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { Thread::kStackOverflowSignalReservedBytes; bool large_frame = (static_cast<size_t>(frame_size_) > kStackOverflowReservedUsableBytes); if (!skip_overflow_check) { - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitStackOverflowChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { if (!large_frame) { /* Load stack limit */ LockTemp(rs_r12); @@ -401,7 +401,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { const int spill_size = spill_count * 4; const int frame_size_without_spills = frame_size_ - spill_size; if (!skip_overflow_check) { - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitStackOverflowChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { class StackOverflowSlowPath : public LIRSlowPath { public: StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, bool restore_lr, size_t sp_displace) diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index 43db24cad4..582af510f3 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -85,8 +85,6 @@ class ArmMir2Lir FINAL : public Mir2Lir { size_t GetInsnSize(LIR* lir) OVERRIDE; bool IsUnconditionalBranch(LIR* lir); - // Check support for volatile load/store of a given size. - bool SupportsVolatileLoadStore(OpSize size) OVERRIDE; // Get the register class for load/store of a field. RegisterClass RegClassForFieldLoadStore(OpSize size, bool is_volatile) OVERRIDE; @@ -198,6 +196,13 @@ class ArmMir2Lir FINAL : public Mir2Lir { RegStorage AllocPreservedDouble(int s_reg); RegStorage AllocPreservedSingle(int s_reg); + bool WideGPRsAreAliases() OVERRIDE { + return false; // Wide GPRs are formed by pairing. + } + bool WideFPRsAreAliases() OVERRIDE { + return false; // Wide FPRs are formed by pairing. + } + private: void GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, int64_t val, ConditionCode ccode); diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 95071d92b2..2fcc3a5abc 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -341,7 +341,7 @@ void ArmMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { * is responsible for setting branch target field. */ LIR* ArmMir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value, LIR* target) { - LIR* branch; + LIR* branch = nullptr; ArmConditionCode arm_cond = ArmConditionEncoding(cond); /* * A common use of OpCmpImmBranch is for null checks, and using the Thumb 16-bit @@ -354,14 +354,22 @@ LIR* ArmMir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_va */ bool skip = ((target != NULL) && (target->opcode == kPseudoThrowTarget)); skip &= ((cu_->code_item->insns_size_in_code_units_ - current_dalvik_offset_) > 64); - if (!skip && reg.Low8() && (check_value == 0) && - ((arm_cond == kArmCondEq) || (arm_cond == kArmCondNe))) { - branch = NewLIR2((arm_cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz, - reg.GetReg(), 0); - } else { + if (!skip && reg.Low8() && (check_value == 0)) { + if (arm_cond == kArmCondEq || arm_cond == kArmCondNe) { + branch = NewLIR2((arm_cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz, + reg.GetReg(), 0); + } else if (arm_cond == kArmCondLs) { + // kArmCondLs is an unsigned less or equal. A comparison r <= 0 is then the same as cbz. + // This case happens for a bounds check of array[0]. + branch = NewLIR2(kThumb2Cbz, reg.GetReg(), 0); + } + } + + if (branch == nullptr) { OpRegImm(kOpCmp, reg, check_value); branch = NewLIR2(kThumbBCond, 0, arm_cond); } + branch->target = target; return branch; } @@ -764,6 +772,7 @@ void ArmMir2Lir::OpTlsCmp(ThreadOffset<8> offset, int val) { UNIMPLEMENTED(FATAL) << "Should not be called."; } +// Generate a CAS with memory_order_seq_cst semantics. bool ArmMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { DCHECK_EQ(cu_->instruction_set, kThumb2); // Unused - RegLocation rl_src_unsafe = info->args[0]; @@ -818,8 +827,8 @@ bool ArmMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { } } - // Release store semantics, get the barrier out of the way. TODO: revisit - GenMemBarrier(kStoreLoad); + // Prevent reordering with prior memory operations. + GenMemBarrier(kAnyStore); RegLocation rl_object = LoadValue(rl_src_obj, kRefReg); RegLocation rl_new_value; @@ -908,6 +917,9 @@ bool ArmMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { FreeTemp(rl_expected.reg); // Now unneeded. } + // Prevent reordering with subsequent memory operations. + GenMemBarrier(kLoadAny); + // result := (tmp1 != 0) ? 0 : 1; RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); OpRegRegImm(kOpRsub, rl_result.reg, r_tmp, 1); @@ -987,10 +999,10 @@ bool ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { int dmb_flavor; // TODO: revisit Arm barrier kinds switch (barrier_kind) { - case kLoadStore: dmb_flavor = kISH; break; - case kLoadLoad: dmb_flavor = kISH; break; + case kAnyStore: dmb_flavor = kISH; break; + case kLoadAny: dmb_flavor = kISH; break; case kStoreStore: dmb_flavor = kISHST; break; - case kStoreLoad: dmb_flavor = kISH; break; + case kAnyAny: dmb_flavor = kISH; break; default: LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind; dmb_flavor = kSY; // quiet gcc. diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index ef94bbc3cc..8cc7596c6d 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -538,10 +538,6 @@ bool ArmMir2Lir::IsUnconditionalBranch(LIR* lir) { return ((lir->opcode == kThumbBUncond) || (lir->opcode == kThumb2BUncond)); } -bool ArmMir2Lir::SupportsVolatileLoadStore(OpSize size) { - return true; -} - RegisterClass ArmMir2Lir::RegClassForFieldLoadStore(OpSize size, bool is_volatile) { if (UNLIKELY(is_volatile)) { // On arm, atomic 64-bit load/store requires a core register pair. diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index 2d5e291442..9cbf7b89f2 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -986,10 +986,7 @@ LIR* ArmMir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_ } if (UNLIKELY(is_volatile == kVolatile)) { - // Without context sensitive analysis, we must issue the most conservative barriers. - // In this case, either a load or store may follow so we issue both barriers. - GenMemBarrier(kLoadLoad); - GenMemBarrier(kLoadStore); + GenMemBarrier(kLoadAny); } return load; @@ -1091,8 +1088,8 @@ LIR* ArmMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStora LIR* ArmMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size, VolatileKind is_volatile) { if (UNLIKELY(is_volatile == kVolatile)) { - // There might have been a store before this volatile one so insert StoreStore barrier. - GenMemBarrier(kStoreStore); + // Ensure that prior accesses become visible to other threads first. + GenMemBarrier(kAnyStore); } LIR* store; @@ -1135,8 +1132,9 @@ LIR* ArmMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r } if (UNLIKELY(is_volatile == kVolatile)) { - // A load might follow the volatile store so insert a StoreLoad barrier. - GenMemBarrier(kStoreLoad); + // Preserve order with respect to any subsequent volatile loads. + // We need StoreLoad, but that generally requires the most expensive barrier. + GenMemBarrier(kAnyAny); } return store; diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h index 5077d11a73..d0633afc9e 100644 --- a/compiler/dex/quick/arm64/arm64_lir.h +++ b/compiler/dex/quick/arm64/arm64_lir.h @@ -245,6 +245,7 @@ enum ArmOpcode { kA64Cmp3RdT, // cmp [01110001] shift[23-22] imm_12[21-10] rn[9-5] [11111]. kA64Csel4rrrc, // csel[s0011010100] rm[20-16] cond[15-12] [00] rn[9-5] rd[4-0]. kA64Csinc4rrrc, // csinc [s0011010100] rm[20-16] cond[15-12] [01] rn[9-5] rd[4-0]. + kA64Csinv4rrrc, // csinv [s1011010100] rm[20-16] cond[15-12] [00] rn[9-5] rd[4-0]. kA64Csneg4rrrc, // csneg [s1011010100] rm[20-16] cond[15-12] [01] rn[9-5] rd[4-0]. kA64Dmb1B, // dmb [11010101000000110011] CRm[11-8] [10111111]. kA64Eor3Rrl, // eor [s10100100] N[22] imm_r[21-16] imm_s[15-10] rn[9-5] rd[4-0]. @@ -318,6 +319,7 @@ enum ArmOpcode { kA64Scvtf2fx, // scvtf [100111100s100010000000] rn[9-5] rd[4-0]. kA64Sdiv3rrr, // sdiv[s0011010110] rm[20-16] [000011] rn[9-5] rd[4-0]. kA64Smaddl4xwwx, // smaddl [10011011001] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0]. + kA64Smulh3xxx, // smulh [10011011010] rm[20-16] [011111] rn[9-5] rd[4-0]. kA64Stp4ffXD, // stp [0s10110100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0]. kA64Stp4rrXD, // stp [s010100100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0]. kA64StpPost4rrXD, // stp [s010100010] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0]. @@ -375,6 +377,7 @@ enum ArmOpDmbOptions { kST = 0xe, kISH = 0xb, kISHST = 0xa, + kISHLD = 0x9, kNSH = 0x7, kNSHST = 0x6 }; diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc index e10f7cfe67..462be54e57 100644 --- a/compiler/dex/quick/arm64/assemble_arm64.cc +++ b/compiler/dex/quick/arm64/assemble_arm64.cc @@ -105,7 +105,7 @@ namespace art { const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { ENCODING_MAP(WIDE(kA64Adc3rrr), SF_VARIANTS(0x1a000000), kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, "adc", "!0r, !1r, !2r", kFixupNone), ENCODING_MAP(WIDE(kA64Add4RRdT), SF_VARIANTS(0x11000000), kFmtRegROrSp, 4, 0, kFmtRegROrSp, 9, 5, kFmtBitBlt, 21, 10, @@ -113,7 +113,7 @@ const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { "add", "!0R, !1R, #!2d!3T", kFixupNone), ENCODING_MAP(WIDE(kA64Add4rrro), SF_VARIANTS(0x0b000000), kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, - kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE1, + kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12, "add", "!0r, !1r, !2r!3o", kFixupNone), ENCODING_MAP(WIDE(kA64Add4RRre), SF_VARIANTS(0x0b200000), kFmtRegROrSp, 4, 0, kFmtRegROrSp, 9, 5, kFmtRegR, 20, 16, @@ -204,6 +204,10 @@ const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, kFmtBitBlt, 15, 12, IS_QUAD_OP | REG_DEF0_USE12 | USES_CCODES, "csinc", "!0r, !1r, !2r, !3c", kFixupNone), + ENCODING_MAP(WIDE(kA64Csinv4rrrc), SF_VARIANTS(0x5a800000), + kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, + kFmtBitBlt, 15, 12, IS_QUAD_OP | REG_DEF0_USE12 | USES_CCODES, + "csinv", "!0r, !1r, !2r, !3c", kFixupNone), ENCODING_MAP(WIDE(kA64Csneg4rrrc), SF_VARIANTS(0x5a800400), kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, kFmtBitBlt, 15, 12, IS_QUAD_OP | REG_DEF0_USE12 | USES_CCODES, @@ -498,6 +502,10 @@ const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { kFmtRegX, 4, 0, kFmtRegW, 9, 5, kFmtRegW, 20, 16, kFmtRegX, 14, 10, IS_QUAD_OP | REG_DEF0_USE123, "smaddl", "!0x, !1w, !2w, !3x", kFixupNone), + ENCODING_MAP(kA64Smulh3xxx, NO_VARIANTS(0x9b407c00), + kFmtRegX, 4, 0, kFmtRegX, 9, 5, kFmtRegX, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "smulh", "!0x, !1x, !2x", kFixupNone), ENCODING_MAP(WIDE(kA64Stp4ffXD), CUSTOM_VARIANTS(0x2d000000, 0x6d000000), kFmtRegF, 4, 0, kFmtRegF, 14, 10, kFmtRegXOrSp, 9, 5, kFmtBitBlt, 21, 15, IS_QUAD_OP | REG_USE012 | IS_STORE, @@ -647,10 +655,10 @@ uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { if (kIsDebugBuild && (kFailOnSizeError || kReportSizeError)) { // Register usage checks: First establish register usage requirements based on the // format in `kind'. - bool want_float = false; - bool want_64_bit = false; - bool want_var_size = true; - bool want_zero = false; + bool want_float = false; // Want a float (rather than core) register. + bool want_64_bit = false; // Want a 64-bit (rather than 32-bit) register. + bool want_var_size = true; // Want register with variable size (kFmtReg{R,F}). + bool want_zero = false; // Want the zero (rather than sp) register. switch (kind) { case kFmtRegX: want_64_bit = true; @@ -709,9 +717,6 @@ uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { } } - // TODO(Arm64): if !want_size_match, then we still should compare the size of the - // register with the size required by the instruction width (kA64Wide). - // Fail, if `expected' contains an unsatisfied requirement. if (expected != nullptr) { LOG(WARNING) << "Method: " << PrettyMethod(cu_->method_idx, *cu_->dex_file) @@ -726,11 +731,12 @@ uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { } } - // TODO(Arm64): this may or may not be necessary, depending on how wzr, xzr are - // defined. - if (is_zero) { - operand = 31; - } + // In the lines below, we rely on (operand & 0x1f) == 31 to be true for register sp + // and zr. This means that these two registers do not need any special treatment, as + // their bottom 5 bits are correctly set to 31 == 0b11111, which is the right + // value for encoding both sp and zr. + COMPILE_ASSERT((rxzr & 0x1f) == 0x1f, rzr_register_number_must_be_31); + COMPILE_ASSERT((rsp & 0x1f) == 0x1f, rsp_register_number_must_be_31); } value = (operand << encoder->field_loc[i].start) & diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc index 56dcbe59e9..d946ee39ef 100644 --- a/compiler/dex/quick/arm64/call_arm64.cc +++ b/compiler/dex/quick/arm64/call_arm64.cc @@ -195,14 +195,14 @@ void Arm64Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { // TUNING: How much performance we get when we inline this? // Since we've already flush all register. FlushAllRegs(); - LoadValueDirectFixed(rl_src, rs_x0); // = TargetRefReg(kArg0) + LoadValueDirectFixed(rl_src, rs_x0); // = TargetReg(kArg0, kRef) LockCallTemps(); // Prepare for explicit register usage LIR* null_check_branch = nullptr; if ((opt_flags & MIR_IGNORE_NULL_CHECK) && !(cu_->disable_opt & (1 << kNullCheckElimination))) { null_check_branch = nullptr; // No null check. } else { // If the null-check fails its handled by the slow-path to reduce exception related meta-data. - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { null_check_branch = OpCmpImmBranch(kCondEq, rs_x0, 0, NULL); } } @@ -228,7 +228,7 @@ void Arm64Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { LIR* success_target = NewLIR0(kPseudoTargetLabel); lock_success_branch->target = success_target; - GenMemBarrier(kLoadLoad); + GenMemBarrier(kLoadAny); } /* @@ -250,7 +250,7 @@ void Arm64Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { null_check_branch = nullptr; // No null check. } else { // If the null-check fails its handled by the slow-path to reduce exception related meta-data. - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { null_check_branch = OpCmpImmBranch(kCondEq, rs_x0, 0, NULL); } } @@ -258,7 +258,7 @@ void Arm64Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { Load32Disp(rs_x0, mirror::Object::MonitorOffset().Int32Value(), rs_w2); MarkPossibleNullPointerException(opt_flags); LIR* slow_unlock_branch = OpCmpBranch(kCondNe, rs_w1, rs_w2, NULL); - GenMemBarrier(kStoreLoad); + GenMemBarrier(kAnyStore); Store32Disp(rs_x0, mirror::Object::MonitorOffset().Int32Value(), rs_wzr); LIR* unlock_success_branch = OpUnconditionalBranch(NULL); @@ -338,7 +338,7 @@ void Arm64Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) const int frame_size_without_spills = frame_size_ - spill_size; if (!skip_overflow_check) { - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitStackOverflowChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { if (!large_frame) { // Load stack limit LoadWordDisp(rs_xSELF, Thread::StackEndOffset<8>().Int32Value(), rs_x9); @@ -371,7 +371,7 @@ void Arm64Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) } if (!skip_overflow_check) { - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitStackOverflowChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { class StackOverflowSlowPath: public LIRSlowPath { public: StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, size_t sp_displace) : diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h index 7d75da91d8..f51145cfe9 100644 --- a/compiler/dex/quick/arm64/codegen_arm64.h +++ b/compiler/dex/quick/arm64/codegen_arm64.h @@ -65,8 +65,12 @@ class Arm64Mir2Lir FINAL : public Mir2Lir { // Required for target - codegen helpers. bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src, RegLocation rl_dest, int lit) OVERRIDE; + bool SmallLiteralDivRem64(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src, + RegLocation rl_dest, int64_t lit); bool HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src, RegLocation rl_dest, int lit) OVERRIDE; + bool HandleEasyDivRem64(Instruction::Code dalvik_opcode, bool is_div, + RegLocation rl_src, RegLocation rl_dest, int64_t lit); bool EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) OVERRIDE; LIR* CheckSuspendUsingLoad() OVERRIDE; RegStorage LoadHelper(ThreadOffset<4> offset) OVERRIDE; @@ -96,25 +100,19 @@ class Arm64Mir2Lir FINAL : public Mir2Lir { RegStorage r_src, OpSize size) OVERRIDE; void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) OVERRIDE; LIR* OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, - int offset, int check_value, LIR* target) OVERRIDE; + int offset, int check_value, LIR* target, LIR** compare) OVERRIDE; // Required for target - register utilities. RegStorage TargetReg(SpecialTargetRegister reg) OVERRIDE; - RegStorage TargetReg(SpecialTargetRegister symbolic_reg, bool is_wide) OVERRIDE { - RegStorage reg = TargetReg(symbolic_reg); - if (is_wide) { - return (reg.Is64Bit()) ? reg : As64BitReg(reg); + RegStorage TargetReg(SpecialTargetRegister symbolic_reg, WideKind wide_kind) OVERRIDE { + if (wide_kind == kWide || wide_kind == kRef) { + return As64BitReg(TargetReg(symbolic_reg)); } else { - return (reg.Is32Bit()) ? reg : As32BitReg(reg); + return Check32BitReg(TargetReg(symbolic_reg)); } } - RegStorage TargetRefReg(SpecialTargetRegister symbolic_reg) OVERRIDE { - RegStorage reg = TargetReg(symbolic_reg); - return (reg.Is64Bit() ? reg : As64BitReg(reg)); - } RegStorage TargetPtrReg(SpecialTargetRegister symbolic_reg) OVERRIDE { - RegStorage reg = TargetReg(symbolic_reg); - return (reg.Is64Bit() ? reg : As64BitReg(reg)); + return As64BitReg(TargetReg(symbolic_reg)); } RegStorage GetArgMappingToPhysicalReg(int arg_num); RegLocation GetReturnAlt(); @@ -148,8 +146,6 @@ class Arm64Mir2Lir FINAL : public Mir2Lir { size_t GetInsnSize(LIR* lir) OVERRIDE; bool IsUnconditionalBranch(LIR* lir); - // Check support for volatile load/store of a given size. - bool SupportsVolatileLoadStore(OpSize size) OVERRIDE; // Get the register class for load/store of a field. RegisterClass RegClassForFieldLoadStore(OpSize size, bool is_volatile) OVERRIDE; @@ -298,6 +294,13 @@ class Arm64Mir2Lir FINAL : public Mir2Lir { bool skip_this); InToRegStorageMapping in_to_reg_storage_mapping_; + bool WideGPRsAreAliases() OVERRIDE { + return true; // 64b architecture. + } + bool WideFPRsAreAliases() OVERRIDE { + return true; // 64b architecture. + } + private: /** * @brief Given register xNN (dNN), returns register wNN (sNN). diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc index e8f5cb9f09..2b78e81f46 100644 --- a/compiler/dex/quick/arm64/int_arm64.cc +++ b/compiler/dex/quick/arm64/int_arm64.cc @@ -91,17 +91,134 @@ void Arm64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { RegLocation rl_dest = mir_graph_->GetDest(mir); RegisterClass src_reg_class = rl_src.ref ? kRefReg : kCoreReg; RegisterClass result_reg_class = rl_dest.ref ? kRefReg : kCoreReg; + rl_src = LoadValue(rl_src, src_reg_class); + // rl_src may be aliased with rl_result/rl_dest, so do compare early. + OpRegImm(kOpCmp, rl_src.reg, 0); + ArmConditionCode code = ArmConditionEncoding(mir->meta.ccode); - RegLocation rl_true = mir_graph_->reg_location_[mir->ssa_rep->uses[1]]; - RegLocation rl_false = mir_graph_->reg_location_[mir->ssa_rep->uses[2]]; - rl_true = LoadValue(rl_true, result_reg_class); - rl_false = LoadValue(rl_false, result_reg_class); - rl_result = EvalLoc(rl_dest, result_reg_class, true); - OpRegImm(kOpCmp, rl_src.reg, 0); - NewLIR4(kA64Csel4rrrc, rl_result.reg.GetReg(), rl_true.reg.GetReg(), - rl_false.reg.GetReg(), code); + // The kMirOpSelect has two variants, one for constants and one for moves. + bool is_wide = rl_dest.ref || rl_dest.wide; + + if (mir->ssa_rep->num_uses == 1) { + uint32_t true_val = mir->dalvikInsn.vB; + uint32_t false_val = mir->dalvikInsn.vC; + + int opcode; // The opcode. + int left_op, right_op; // The operands. + bool rl_result_evaled = false; + + // Check some simple cases. + // TODO: Improve this. + int zero_reg = (is_wide ? rs_xzr : rs_wzr).GetReg(); + + if ((true_val == 0 && false_val == 1) || (true_val == 1 && false_val == 0)) { + // CSInc cheap based on wzr. + if (true_val == 1) { + // Negate. + code = ArmConditionEncoding(NegateComparison(mir->meta.ccode)); + } + + left_op = right_op = zero_reg; + opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc; + } else if ((true_val == 0 && false_val == 0xFFFFFFFF) || + (true_val == 0xFFFFFFFF && false_val == 0)) { + // CSneg cheap based on wzr. + if (true_val == 0xFFFFFFFF) { + // Negate. + code = ArmConditionEncoding(NegateComparison(mir->meta.ccode)); + } + + left_op = right_op = zero_reg; + opcode = is_wide ? WIDE(kA64Csinv4rrrc) : kA64Csinv4rrrc; + } else if (true_val == 0 || false_val == 0) { + // Csel half cheap based on wzr. + rl_result = EvalLoc(rl_dest, result_reg_class, true); + rl_result_evaled = true; + if (false_val == 0) { + // Negate. + code = ArmConditionEncoding(NegateComparison(mir->meta.ccode)); + } + LoadConstantNoClobber(rl_result.reg, true_val == 0 ? false_val : true_val); + left_op = zero_reg; + right_op = rl_result.reg.GetReg(); + opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc; + } else if (true_val == 1 || false_val == 1) { + // CSInc half cheap based on wzr. + rl_result = EvalLoc(rl_dest, result_reg_class, true); + rl_result_evaled = true; + if (true_val == 1) { + // Negate. + code = ArmConditionEncoding(NegateComparison(mir->meta.ccode)); + } + LoadConstantNoClobber(rl_result.reg, true_val == 1 ? false_val : true_val); + left_op = rl_result.reg.GetReg(); + right_op = zero_reg; + opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc; + } else if (true_val == 0xFFFFFFFF || false_val == 0xFFFFFFFF) { + // CSneg half cheap based on wzr. + rl_result = EvalLoc(rl_dest, result_reg_class, true); + rl_result_evaled = true; + if (true_val == 0xFFFFFFFF) { + // Negate. + code = ArmConditionEncoding(NegateComparison(mir->meta.ccode)); + } + LoadConstantNoClobber(rl_result.reg, true_val == 0xFFFFFFFF ? false_val : true_val); + left_op = rl_result.reg.GetReg(); + right_op = zero_reg; + opcode = is_wide ? WIDE(kA64Csinv4rrrc) : kA64Csinv4rrrc; + } else if ((true_val + 1 == false_val) || (false_val + 1 == true_val)) { + // Load a constant and use CSinc. Use rl_result. + if (false_val + 1 == true_val) { + // Negate. + code = ArmConditionEncoding(NegateComparison(mir->meta.ccode)); + true_val = false_val; + } + + rl_result = EvalLoc(rl_dest, result_reg_class, true); + rl_result_evaled = true; + LoadConstantNoClobber(rl_result.reg, true_val); + left_op = right_op = rl_result.reg.GetReg(); + opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc; + } else { + // Csel. The rest. Use rl_result and a temp. + // TODO: To minimize the constants being loaded, check whether one can be inexpensively + // loaded as n - 1 or ~n. + rl_result = EvalLoc(rl_dest, result_reg_class, true); + rl_result_evaled = true; + LoadConstantNoClobber(rl_result.reg, true_val); + RegStorage t_reg2 = AllocTypedTemp(false, result_reg_class); + if (rl_dest.wide) { + if (t_reg2.Is32Bit()) { + t_reg2 = As64BitReg(t_reg2); + } + } + LoadConstantNoClobber(t_reg2, false_val); + + // Use csel. + left_op = rl_result.reg.GetReg(); + right_op = t_reg2.GetReg(); + opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc; + } + + if (!rl_result_evaled) { + rl_result = EvalLoc(rl_dest, result_reg_class, true); + } + + NewLIR4(opcode, rl_result.reg.GetReg(), left_op, right_op, code); + } else { + RegLocation rl_true = mir_graph_->reg_location_[mir->ssa_rep->uses[1]]; + RegLocation rl_false = mir_graph_->reg_location_[mir->ssa_rep->uses[2]]; + + rl_true = LoadValue(rl_true, result_reg_class); + rl_false = LoadValue(rl_false, result_reg_class); + rl_result = EvalLoc(rl_dest, result_reg_class, true); + + int opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc; + NewLIR4(opcode, rl_result.reg.GetReg(), + rl_true.reg.GetReg(), rl_false.reg.GetReg(), code); + } StoreValue(rl_dest, rl_result); } @@ -110,7 +227,6 @@ void Arm64Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2); LIR* taken = &block_label_list_[bb->taken]; LIR* not_taken = &block_label_list_[bb->fall_through]; - rl_src1 = LoadValueWide(rl_src1, kCoreReg); // Normalize such that if either operand is constant, src2 will be constant. ConditionCode ccode = mir->meta.ccode; if (rl_src1.is_const) { @@ -118,16 +234,22 @@ void Arm64Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { ccode = FlipComparisonOrder(ccode); } + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + if (rl_src2.is_const) { - rl_src2 = UpdateLocWide(rl_src2); + // TODO: Optimize for rl_src1.is_const? (Does happen in the boot image at the moment.) + int64_t val = mir_graph_->ConstantValueWide(rl_src2); // Special handling using cbz & cbnz. if (val == 0 && (ccode == kCondEq || ccode == kCondNe)) { OpCmpImmBranch(ccode, rl_src1.reg, 0, taken); OpCmpImmBranch(NegateComparison(ccode), rl_src1.reg, 0, not_taken); return; + } + // Only handle Imm if src2 is not already in a register. - } else if (rl_src2.location != kLocPhysReg) { + rl_src2 = UpdateLocWide(rl_src2); + if (rl_src2.location != kLocPhysReg) { OpRegImm64(kOpCmp, rl_src1.reg, val); OpCondBranch(ccode, taken); OpCondBranch(NegateComparison(ccode), not_taken); @@ -147,23 +269,35 @@ void Arm64Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { */ LIR* Arm64Mir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value, LIR* target) { - LIR* branch; + LIR* branch = nullptr; ArmConditionCode arm_cond = ArmConditionEncoding(cond); - if (check_value == 0 && (arm_cond == kArmCondEq || arm_cond == kArmCondNe)) { - ArmOpcode opcode = (arm_cond == kArmCondEq) ? kA64Cbz2rt : kA64Cbnz2rt; - ArmOpcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0); - branch = NewLIR2(opcode | wide, reg.GetReg(), 0); - } else { + if (check_value == 0) { + if (arm_cond == kArmCondEq || arm_cond == kArmCondNe) { + ArmOpcode opcode = (arm_cond == kArmCondEq) ? kA64Cbz2rt : kA64Cbnz2rt; + ArmOpcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0); + branch = NewLIR2(opcode | wide, reg.GetReg(), 0); + } else if (arm_cond == kArmCondLs) { + // kArmCondLs is an unsigned less or equal. A comparison r <= 0 is then the same as cbz. + // This case happens for a bounds check of array[0]. + ArmOpcode opcode = kA64Cbz2rt; + ArmOpcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0); + branch = NewLIR2(opcode | wide, reg.GetReg(), 0); + } + } + + if (branch == nullptr) { OpRegImm(kOpCmp, reg, check_value); branch = NewLIR2(kA64B2ct, arm_cond, 0); } + branch->target = target; return branch; } LIR* Arm64Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, int offset, int check_value, - LIR* target) { + LIR* target, LIR** compare) { + DCHECK(compare == nullptr); // It is possible that temp register is 64-bit. (ArgReg or RefReg) // Always compare 32-bit value no matter what temp_reg is. if (temp_reg.Is64Bit()) { @@ -248,28 +382,31 @@ void Arm64Mir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) { // Table of magic divisors struct MagicTable { - uint32_t magic; + int magic64_base; + int magic64_eor; + uint64_t magic64; + uint32_t magic32; uint32_t shift; DividePattern pattern; }; static const MagicTable magic_table[] = { - {0, 0, DivideNone}, // 0 - {0, 0, DivideNone}, // 1 - {0, 0, DivideNone}, // 2 - {0x55555556, 0, Divide3}, // 3 - {0, 0, DivideNone}, // 4 - {0x66666667, 1, Divide5}, // 5 - {0x2AAAAAAB, 0, Divide3}, // 6 - {0x92492493, 2, Divide7}, // 7 - {0, 0, DivideNone}, // 8 - {0x38E38E39, 1, Divide5}, // 9 - {0x66666667, 2, Divide5}, // 10 - {0x2E8BA2E9, 1, Divide5}, // 11 - {0x2AAAAAAB, 1, Divide5}, // 12 - {0x4EC4EC4F, 2, Divide5}, // 13 - {0x92492493, 3, Divide7}, // 14 - {0x88888889, 3, Divide7}, // 15 + { 0, 0, 0, 0, 0, DivideNone}, // 0 + { 0, 0, 0, 0, 0, DivideNone}, // 1 + { 0, 0, 0, 0, 0, DivideNone}, // 2 + {0x3c, -1, 0x5555555555555556, 0x55555556, 0, Divide3}, // 3 + { 0, 0, 0, 0, 0, DivideNone}, // 4 + {0xf9, -1, 0x6666666666666667, 0x66666667, 1, Divide5}, // 5 + {0x7c, 0x1041, 0x2AAAAAAAAAAAAAAB, 0x2AAAAAAB, 0, Divide3}, // 6 + { -1, -1, 0x924924924924924A, 0x92492493, 2, Divide7}, // 7 + { 0, 0, 0, 0, 0, DivideNone}, // 8 + { -1, -1, 0x38E38E38E38E38E4, 0x38E38E39, 1, Divide5}, // 9 + {0xf9, -1, 0x6666666666666667, 0x66666667, 2, Divide5}, // 10 + { -1, -1, 0x2E8BA2E8BA2E8BA3, 0x2E8BA2E9, 1, Divide5}, // 11 + {0x7c, 0x1041, 0x2AAAAAAAAAAAAAAB, 0x2AAAAAAB, 1, Divide5}, // 12 + { -1, -1, 0x4EC4EC4EC4EC4EC5, 0x4EC4EC4F, 2, Divide5}, // 13 + { -1, -1, 0x924924924924924A, 0x92492493, 3, Divide7}, // 14 + {0x78, -1, 0x8888888888888889, 0x88888889, 3, Divide7}, // 15 }; // Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4) @@ -288,7 +425,7 @@ bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_d } RegStorage r_magic = AllocTemp(); - LoadConstant(r_magic, magic_table[lit].magic); + LoadConstant(r_magic, magic_table[lit].magic32); rl_src = LoadValue(rl_src, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); RegStorage r_long_mul = AllocTemp(); @@ -317,49 +454,144 @@ bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_d return true; } +bool Arm64Mir2Lir::SmallLiteralDivRem64(Instruction::Code dalvik_opcode, bool is_div, + RegLocation rl_src, RegLocation rl_dest, int64_t lit) { + if ((lit < 0) || (lit >= static_cast<int>(arraysize(magic_table)))) { + return false; + } + DividePattern pattern = magic_table[lit].pattern; + if (pattern == DivideNone) { + return false; + } + // Tuning: add rem patterns + if (!is_div) { + return false; + } + + RegStorage r_magic = AllocTempWide(); + rl_src = LoadValueWide(rl_src, kCoreReg); + RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); + RegStorage r_long_mul = AllocTempWide(); + + if (magic_table[lit].magic64_base >= 0) { + // Check that the entry in the table is correct. + if (kIsDebugBuild) { + uint64_t reconstructed_imm; + uint64_t base = DecodeLogicalImmediate(/*is_wide*/true, magic_table[lit].magic64_base); + if (magic_table[lit].magic64_eor >= 0) { + uint64_t eor = DecodeLogicalImmediate(/*is_wide*/true, magic_table[lit].magic64_eor); + reconstructed_imm = base ^ eor; + } else { + reconstructed_imm = base + 1; + } + DCHECK_EQ(reconstructed_imm, magic_table[lit].magic64) << " for literal " << lit; + } + + // Load the magic constant in two instructions. + NewLIR3(WIDE(kA64Orr3Rrl), r_magic.GetReg(), rxzr, magic_table[lit].magic64_base); + if (magic_table[lit].magic64_eor >= 0) { + NewLIR3(WIDE(kA64Eor3Rrl), r_magic.GetReg(), r_magic.GetReg(), + magic_table[lit].magic64_eor); + } else { + NewLIR4(WIDE(kA64Add4RRdT), r_magic.GetReg(), r_magic.GetReg(), 1, 0); + } + } else { + LoadConstantWide(r_magic, magic_table[lit].magic64); + } + + NewLIR3(kA64Smulh3xxx, r_long_mul.GetReg(), r_magic.GetReg(), rl_src.reg.GetReg()); + switch (pattern) { + case Divide3: + OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 63)); + break; + case Divide5: + OpRegRegImm(kOpAsr, r_long_mul, r_long_mul, magic_table[lit].shift); + OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 63)); + break; + case Divide7: + OpRegRegReg(kOpAdd, r_long_mul, rl_src.reg, r_long_mul); + OpRegRegImm(kOpAsr, r_long_mul, r_long_mul, magic_table[lit].shift); + OpRegRegRegShift(kOpSub, rl_result.reg, r_long_mul, rl_src.reg, EncodeShift(kA64Asr, 63)); + break; + default: + LOG(FATAL) << "Unexpected pattern: " << pattern; + } + StoreValueWide(rl_dest, rl_result); + return true; +} + // Returns true if it added instructions to 'cu' to divide 'rl_src' by 'lit' // and store the result in 'rl_dest'. bool Arm64Mir2Lir::HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src, RegLocation rl_dest, int lit) { + return HandleEasyDivRem64(dalvik_opcode, is_div, rl_src, rl_dest, static_cast<int>(lit)); +} + +// Returns true if it added instructions to 'cu' to divide 'rl_src' by 'lit' +// and store the result in 'rl_dest'. +bool Arm64Mir2Lir::HandleEasyDivRem64(Instruction::Code dalvik_opcode, bool is_div, + RegLocation rl_src, RegLocation rl_dest, int64_t lit) { + const bool is_64bit = rl_dest.wide; + const int nbits = (is_64bit) ? 64 : 32; + if (lit < 2) { return false; } if (!IsPowerOfTwo(lit)) { - return SmallLiteralDivRem(dalvik_opcode, is_div, rl_src, rl_dest, lit); + if (is_64bit) { + return SmallLiteralDivRem64(dalvik_opcode, is_div, rl_src, rl_dest, lit); + } else { + return SmallLiteralDivRem(dalvik_opcode, is_div, rl_src, rl_dest, static_cast<int32_t>(lit)); + } } int k = LowestSetBit(lit); - if (k >= 30) { + if (k >= nbits - 2) { // Avoid special cases. return false; } - rl_src = LoadValue(rl_src, kCoreReg); - RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + + RegLocation rl_result; + RegStorage t_reg; + if (is_64bit) { + rl_src = LoadValueWide(rl_src, kCoreReg); + rl_result = EvalLocWide(rl_dest, kCoreReg, true); + t_reg = AllocTempWide(); + } else { + rl_src = LoadValue(rl_src, kCoreReg); + rl_result = EvalLoc(rl_dest, kCoreReg, true); + t_reg = AllocTemp(); + } + + int shift = EncodeShift(kA64Lsr, nbits - k); if (is_div) { - RegStorage t_reg = AllocTemp(); if (lit == 2) { // Division by 2 is by far the most common division by constant. - OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, rl_src.reg, EncodeShift(kA64Lsr, 32 - k)); + OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, rl_src.reg, shift); OpRegRegImm(kOpAsr, rl_result.reg, t_reg, k); } else { - OpRegRegImm(kOpAsr, t_reg, rl_src.reg, 31); - OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, t_reg, EncodeShift(kA64Lsr, 32 - k)); + OpRegRegImm(kOpAsr, t_reg, rl_src.reg, nbits - 1); + OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, t_reg, shift); OpRegRegImm(kOpAsr, rl_result.reg, t_reg, k); } } else { - RegStorage t_reg = AllocTemp(); if (lit == 2) { - OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, rl_src.reg, EncodeShift(kA64Lsr, 32 - k)); - OpRegRegImm(kOpAnd, t_reg, t_reg, lit - 1); - OpRegRegRegShift(kOpSub, rl_result.reg, t_reg, rl_src.reg, EncodeShift(kA64Lsr, 32 - k)); + OpRegRegRegShift(kOpAdd, t_reg, rl_src.reg, rl_src.reg, shift); + OpRegRegImm64(kOpAnd, t_reg, t_reg, lit - 1); + OpRegRegRegShift(kOpSub, rl_result.reg, t_reg, rl_src.reg, shift); } else { - RegStorage t_reg2 = AllocTemp(); - OpRegRegImm(kOpAsr, t_reg, rl_src.reg, 31); - OpRegRegRegShift(kOpAdd, t_reg2, rl_src.reg, t_reg, EncodeShift(kA64Lsr, 32 - k)); - OpRegRegImm(kOpAnd, t_reg2, t_reg2, lit - 1); - OpRegRegRegShift(kOpSub, rl_result.reg, t_reg2, t_reg, EncodeShift(kA64Lsr, 32 - k)); + RegStorage t_reg2 = (is_64bit) ? AllocTempWide() : AllocTemp(); + OpRegRegImm(kOpAsr, t_reg, rl_src.reg, nbits - 1); + OpRegRegRegShift(kOpAdd, t_reg2, rl_src.reg, t_reg, shift); + OpRegRegImm64(kOpAnd, t_reg2, t_reg2, lit - 1); + OpRegRegRegShift(kOpSub, rl_result.reg, t_reg2, t_reg, shift); } } - StoreValue(rl_dest, rl_result); + + if (is_64bit) { + StoreValueWide(rl_dest, rl_result); + } else { + StoreValue(rl_dest, rl_result); + } return true; } @@ -368,12 +600,6 @@ bool Arm64Mir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit return false; } -RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2, bool is_div, bool check_zero) { - LOG(FATAL) << "Unexpected use of GenDivRem for Arm64"; - return rl_dest; -} - RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) { LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm64"; return rl_dest; @@ -393,8 +619,14 @@ RegLocation Arm64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int return rl_result; } +RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, bool is_div, bool check_zero) { + LOG(FATAL) << "Unexpected use of GenDivRem for Arm64"; + return rl_dest; +} + RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage r_src1, RegStorage r_src2, - bool is_div) { + bool is_div) { CHECK_EQ(r_src1.Is64Bit(), r_src2.Is64Bit()); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); @@ -631,10 +863,16 @@ bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { int dmb_flavor; // TODO: revisit Arm barrier kinds switch (barrier_kind) { - case kLoadStore: dmb_flavor = kISH; break; - case kLoadLoad: dmb_flavor = kISH; break; + case kAnyStore: dmb_flavor = kISH; break; + case kLoadAny: dmb_flavor = kISH; break; + // We conjecture that kISHLD is insufficient. It is documented + // to provide LoadLoad | StoreStore ordering. But if this were used + // to implement volatile loads, we suspect that the lack of store + // atomicity on ARM would cause us to allow incorrect results for + // the canonical IRIW example. But we're not sure. + // We should be using acquire loads instead. case kStoreStore: dmb_flavor = kISHST; break; - case kStoreLoad: dmb_flavor = kISH; break; + case kAnyAny: dmb_flavor = kISH; break; default: LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind; dmb_flavor = kSY; // quiet gcc. @@ -670,6 +908,14 @@ void Arm64Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) { void Arm64Mir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2, bool is_div) { + if (rl_src2.is_const) { + DCHECK(rl_src2.wide); + int64_t lit = mir_graph_->ConstantValueWide(rl_src2); + if (HandleEasyDivRem64(opcode, is_div, rl_src1, rl_dest, lit)) { + return; + } + } + RegLocation rl_result; rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); @@ -935,7 +1181,7 @@ void Arm64Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } void Arm64Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode, - RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift) { + RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift) { OpKind op = kOpBkpt; // Per spec, we only care about low 6 bits of shift amount. int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f; diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc index ef9dbddbde..f1dc77ad28 100644 --- a/compiler/dex/quick/arm64/target_arm64.cc +++ b/compiler/dex/quick/arm64/target_arm64.cc @@ -108,19 +108,19 @@ RegLocation Arm64Mir2Lir::LocCReturnDouble() { RegStorage Arm64Mir2Lir::TargetReg(SpecialTargetRegister reg) { RegStorage res_reg = RegStorage::InvalidReg(); switch (reg) { - case kSelf: res_reg = rs_xSELF; break; - case kSuspend: res_reg = rs_xSUSPEND; break; - case kLr: res_reg = rs_xLR; break; + case kSelf: res_reg = rs_wSELF; break; + case kSuspend: res_reg = rs_wSUSPEND; break; + case kLr: res_reg = rs_wLR; break; case kPc: res_reg = RegStorage::InvalidReg(); break; - case kSp: res_reg = rs_sp; break; - case kArg0: res_reg = rs_x0; break; - case kArg1: res_reg = rs_x1; break; - case kArg2: res_reg = rs_x2; break; - case kArg3: res_reg = rs_x3; break; - case kArg4: res_reg = rs_x4; break; - case kArg5: res_reg = rs_x5; break; - case kArg6: res_reg = rs_x6; break; - case kArg7: res_reg = rs_x7; break; + case kSp: res_reg = rs_wsp; break; + case kArg0: res_reg = rs_w0; break; + case kArg1: res_reg = rs_w1; break; + case kArg2: res_reg = rs_w2; break; + case kArg3: res_reg = rs_w3; break; + case kArg4: res_reg = rs_w4; break; + case kArg5: res_reg = rs_w5; break; + case kArg6: res_reg = rs_w6; break; + case kArg7: res_reg = rs_w7; break; case kFArg0: res_reg = rs_f0; break; case kFArg1: res_reg = rs_f1; break; case kFArg2: res_reg = rs_f2; break; @@ -129,10 +129,10 @@ RegStorage Arm64Mir2Lir::TargetReg(SpecialTargetRegister reg) { case kFArg5: res_reg = rs_f5; break; case kFArg6: res_reg = rs_f6; break; case kFArg7: res_reg = rs_f7; break; - case kRet0: res_reg = rs_x0; break; - case kRet1: res_reg = rs_x1; break; - case kInvokeTgt: res_reg = rs_xLR; break; - case kHiddenArg: res_reg = rs_x12; break; + case kRet0: res_reg = rs_w0; break; + case kRet1: res_reg = rs_w1; break; + case kInvokeTgt: res_reg = rs_wLR; break; + case kHiddenArg: res_reg = rs_w12; break; case kHiddenFpArg: res_reg = RegStorage::InvalidReg(); break; case kCount: res_reg = RegStorage::InvalidReg(); break; default: res_reg = RegStorage::InvalidReg(); @@ -158,7 +158,9 @@ ResourceMask Arm64Mir2Lir::GetRegMaskCommon(const RegStorage& reg) const { } ResourceMask Arm64Mir2Lir::GetPCUseDefEncoding() const { - LOG(FATAL) << "Unexpected call to GetPCUseDefEncoding for Arm64"; + // Note: On arm64, we are not able to set pc except branch instructions, which is regarded as a + // kind of barrier. All other instructions only use pc, which has no dependency between any + // of them. So it is fine to just return kEncodeNone here. return kEncodeNone; } @@ -168,6 +170,7 @@ void Arm64Mir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags, DCHECK_EQ(cu_->instruction_set, kArm64); DCHECK(!lir->flags.use_def_invalid); + // Note: REG_USE_PC is ignored, the reason is the same with what we do in GetPCUseDefEncoding(). // These flags are somewhat uncommon - bypass if we can. if ((flags & (REG_DEF_SP | REG_USE_SP | REG_DEF_LR)) != 0) { if (flags & REG_DEF_SP) { @@ -566,10 +569,6 @@ bool Arm64Mir2Lir::IsUnconditionalBranch(LIR* lir) { return (lir->opcode == kA64B1t); } -bool Arm64Mir2Lir::SupportsVolatileLoadStore(OpSize size) { - return true; -} - RegisterClass Arm64Mir2Lir::RegClassForFieldLoadStore(OpSize size, bool is_volatile) { if (UNLIKELY(is_volatile)) { // On arm64, fp register load/store is atomic only for single bytes. @@ -926,13 +925,13 @@ void Arm64Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { */ RegLocation rl_src = rl_method; rl_src.location = kLocPhysReg; - rl_src.reg = TargetReg(kArg0); + rl_src.reg = TargetReg(kArg0, kRef); rl_src.home = false; MarkLive(rl_src); StoreValue(rl_method, rl_src); // If Method* has been promoted, explicitly flush if (rl_method.location == kLocPhysReg) { - StoreRefDisp(TargetReg(kSp), 0, TargetReg(kArg0), kNotVolatile); + StoreRefDisp(TargetPtrReg(kSp), 0, rl_src.reg, kNotVolatile); } if (cu_->num_ins == 0) { @@ -958,9 +957,9 @@ void Arm64Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { } else { // Needs flush. if (t_loc->ref) { - StoreRefDisp(TargetReg(kSp), SRegOffset(start_vreg + i), reg, kNotVolatile); + StoreRefDisp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), reg, kNotVolatile); } else { - StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), reg, t_loc->wide ? k64 : k32, + StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), reg, t_loc->wide ? k64 : k32, kNotVolatile); } } @@ -968,9 +967,9 @@ void Arm64Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { // If arriving in frame & promoted. if (t_loc->location == kLocPhysReg) { if (t_loc->ref) { - LoadRefDisp(TargetReg(kSp), SRegOffset(start_vreg + i), t_loc->reg, kNotVolatile); + LoadRefDisp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), t_loc->reg, kNotVolatile); } else { - LoadBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), t_loc->reg, + LoadBaseDisp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), t_loc->reg, t_loc->wide ? k64 : k32, kNotVolatile); } } @@ -1067,7 +1066,7 @@ int Arm64Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, loc = UpdateLocWide(loc); if (loc.location == kLocPhysReg) { ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile); + StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile); } next_arg += 2; } else { @@ -1075,9 +1074,10 @@ int Arm64Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, if (loc.location == kLocPhysReg) { ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); if (loc.ref) { - StoreRefDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kNotVolatile); + StoreRefDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kNotVolatile); } else { - StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k32, kNotVolatile); + StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k32, + kNotVolatile); } } next_arg++; @@ -1108,11 +1108,11 @@ int Arm64Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, // Instead of allocating a new temp, simply reuse one of the registers being used // for argument passing. - RegStorage temp = TargetReg(kArg3, false); + RegStorage temp = TargetReg(kArg3, kNotWide); // Now load the argument VR and store to the outs. - Load32Disp(TargetReg(kSp), current_src_offset, temp); - Store32Disp(TargetReg(kSp), current_dest_offset, temp); + Load32Disp(TargetPtrReg(kSp), current_src_offset, temp); + Store32Disp(TargetPtrReg(kSp), current_dest_offset, temp); current_src_offset += bytes_to_move; current_dest_offset += bytes_to_move; @@ -1123,8 +1123,7 @@ int Arm64Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, // Now handle rest not registers if they are if (in_to_reg_storage_mapping.IsThereStackMapped()) { - RegStorage regSingle = TargetReg(kArg2); - RegStorage regWide = RegStorage::Solo64(TargetReg(kArg3).GetReg()); + RegStorage regWide = TargetReg(kArg3, kWide); for (int i = start_index; i <= last_mapped_in + regs_left_to_pass_via_stack; i++) { RegLocation rl_arg = info->args[i]; rl_arg = UpdateRawLoc(rl_arg); @@ -1136,25 +1135,27 @@ int Arm64Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); if (rl_arg.wide) { if (rl_arg.location == kLocPhysReg) { - StoreBaseDisp(TargetReg(kSp), out_offset, rl_arg.reg, k64, kNotVolatile); + StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k64, kNotVolatile); } else { LoadValueDirectWideFixed(rl_arg, regWide); - StoreBaseDisp(TargetReg(kSp), out_offset, regWide, k64, kNotVolatile); + StoreBaseDisp(TargetPtrReg(kSp), out_offset, regWide, k64, kNotVolatile); } } else { if (rl_arg.location == kLocPhysReg) { if (rl_arg.ref) { - StoreRefDisp(TargetReg(kSp), out_offset, rl_arg.reg, kNotVolatile); + StoreRefDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, kNotVolatile); } else { - StoreBaseDisp(TargetReg(kSp), out_offset, rl_arg.reg, k32, kNotVolatile); + StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k32, kNotVolatile); } } else { if (rl_arg.ref) { + RegStorage regSingle = TargetReg(kArg2, kRef); LoadValueDirectFixed(rl_arg, regSingle); - StoreRefDisp(TargetReg(kSp), out_offset, regSingle, kNotVolatile); + StoreRefDisp(TargetPtrReg(kSp), out_offset, regSingle, kNotVolatile); } else { - LoadValueDirectFixed(rl_arg, As32BitReg(regSingle)); - StoreBaseDisp(TargetReg(kSp), out_offset, As32BitReg(regSingle), k32, kNotVolatile); + RegStorage regSingle = TargetReg(kArg2, kNotWide); + LoadValueDirectFixed(rl_arg, regSingle); + StoreBaseDisp(TargetPtrReg(kSp), out_offset, regSingle, k32, kNotVolatile); } } } @@ -1190,14 +1191,14 @@ int Arm64Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); if (pcrLabel) { - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { - *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1), info->opt_flags); + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags); } else { *pcrLabel = nullptr; // In lieu of generating a check for kArg1 being null, we need to // perform a load when doing implicit checks. RegStorage tmp = AllocTemp(); - Load32Disp(TargetReg(kArg1), 0, tmp); + Load32Disp(TargetReg(kArg1, kRef), 0, tmp); MarkPossibleNullPointerException(info->opt_flags); FreeTemp(tmp); } diff --git a/compiler/dex/quick/arm64/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc index 22a4ec4d49..fdebb929f7 100644 --- a/compiler/dex/quick/arm64/utility_arm64.cc +++ b/compiler/dex/quick/arm64/utility_arm64.cc @@ -488,7 +488,9 @@ LIR* Arm64Mir2Lir::OpRegRegShift(OpKind op, RegStorage r_dest_src1, RegStorage r break; case kOpRevsh: // Binary, but rm is encoded twice. - return NewLIR2(kA64Rev162rr | wide, r_dest_src1.GetReg(), r_src2.GetReg()); + NewLIR2(kA64Rev162rr | wide, r_dest_src1.GetReg(), r_src2.GetReg()); + // "sxth r1, r2" is "sbfm r1, r2, #0, #15" + return NewLIR4(kA64Sbfm4rrdd | wide, r_dest_src1.GetReg(), r_dest_src1.GetReg(), 0, 15); break; case kOp2Byte: DCHECK_EQ(shift, ENCODE_NO_SHIFT); @@ -1145,10 +1147,8 @@ LIR* Arm64Mir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage LIR* load = LoadBaseDispBody(r_base, displacement, r_dest, size); if (UNLIKELY(is_volatile == kVolatile)) { - // Without context sensitive analysis, we must issue the most conservative barriers. - // In this case, either a load or store may follow so we issue both barriers. - GenMemBarrier(kLoadLoad); - GenMemBarrier(kLoadStore); + // TODO: This should generate an acquire load instead of the barrier. + GenMemBarrier(kLoadAny); } return load; @@ -1232,9 +1232,10 @@ LIR* Arm64Mir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegSto LIR* Arm64Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size, VolatileKind is_volatile) { + // TODO: This should generate a release store and no barriers. if (UNLIKELY(is_volatile == kVolatile)) { - // There might have been a store before this volatile one so insert StoreStore barrier. - GenMemBarrier(kStoreStore); + // Ensure that prior accesses become visible to other threads first. + GenMemBarrier(kAnyStore); } // StoreBaseDisp() will emit correct insn for atomic store on arm64 @@ -1243,8 +1244,9 @@ LIR* Arm64Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage LIR* store = StoreBaseDispBody(r_base, displacement, r_src, size); if (UNLIKELY(is_volatile == kVolatile)) { - // A load might follow the volatile store so insert a StoreLoad barrier. - GenMemBarrier(kStoreLoad); + // Preserve order with respect to any subsequent volatile loads. + // We need StoreLoad, but that generally requires the most expensive barrier. + GenMemBarrier(kAnyAny); } return store; diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 5870d22208..463f277e54 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -1046,9 +1046,19 @@ CompiledMethod* Mir2Lir::GetCompiledMethod() { } // Push a marker to take place of lr. vmap_encoder.PushBackUnsigned(VmapTable::kAdjustedFpMarker); - // fp regs already sorted. - for (uint32_t i = 0; i < fp_vmap_table_.size(); i++) { - vmap_encoder.PushBackUnsigned(fp_vmap_table_[i] + VmapTable::kEntryAdjustment); + if (cu_->instruction_set == kThumb2) { + // fp regs already sorted. + for (uint32_t i = 0; i < fp_vmap_table_.size(); i++) { + vmap_encoder.PushBackUnsigned(fp_vmap_table_[i] + VmapTable::kEntryAdjustment); + } + } else { + // For other platforms regs may have been inserted out of order - sort first. + std::sort(fp_vmap_table_.begin(), fp_vmap_table_.end()); + for (size_t i = 0 ; i < fp_vmap_table_.size(); ++i) { + // Copy, stripping out the phys register sort key. + vmap_encoder.PushBackUnsigned( + ~(-1 << VREG_NUM_WIDTH) & (fp_vmap_table_[i] + VmapTable::kEntryAdjustment)); + } } } else { DCHECK_EQ(POPCOUNT(core_spill_mask_), 0); @@ -1162,9 +1172,12 @@ bool Mir2Lir::BadOverlap(RegLocation rl_src, RegLocation rl_dest) { } LIR *Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, - int offset, int check_value, LIR* target) { + int offset, int check_value, LIR* target, LIR** compare) { // Handle this for architectures that can't compare to memory. - Load32Disp(base_reg, offset, temp_reg); + LIR* inst = Load32Disp(base_reg, offset, temp_reg); + if (compare != nullptr) { + *compare = inst; + } LIR* branch = OpCmpImmBranch(cond, temp_reg, check_value, target); return branch; } @@ -1202,7 +1215,7 @@ void Mir2Lir::LoadMethodAddress(const MethodReference& target_method, InvokeType data_target->operands[2] = type; } // Loads an ArtMethod pointer, which is a reference as it lives in the heap. - LIR* load_pc_rel = OpPcRelLoad(TargetRefReg(symbolic_reg), data_target); + LIR* load_pc_rel = OpPcRelLoad(TargetReg(symbolic_reg, kRef), data_target); AppendLIR(load_pc_rel); DCHECK_NE(cu_->instruction_set, kMips) << reinterpret_cast<void*>(data_target); } @@ -1214,7 +1227,7 @@ void Mir2Lir::LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_re data_target = AddWordData(&class_literal_list_, type_idx); } // Loads a Class pointer, which is a reference as it lives in the heap. - LIR* load_pc_rel = OpPcRelLoad(TargetRefReg(symbolic_reg), data_target); + LIR* load_pc_rel = OpPcRelLoad(TargetReg(symbolic_reg, kRef), data_target); AppendLIR(load_pc_rel); } diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index 6191e4b0a1..0e46c96501 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -48,6 +48,7 @@ static constexpr bool kIntrinsicIsStatic[] = { true, // kIntrinsicMinMaxFloat true, // kIntrinsicMinMaxDouble true, // kIntrinsicSqrt + false, // kIntrinsicGet false, // kIntrinsicCharAt false, // kIntrinsicCompareTo false, // kIntrinsicIsEmptyOrLength @@ -74,6 +75,7 @@ COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxLong], MinMaxLong_must_be_stat COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxFloat], MinMaxFloat_must_be_static); COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicMinMaxDouble], MinMaxDouble_must_be_static); COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicSqrt], Sqrt_must_be_static); +COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicGet], Get_must_not_be_static); COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicCharAt], CharAt_must_not_be_static); COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicCompareTo], CompareTo_must_not_be_static); COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicIsEmptyOrLength], IsEmptyOrLength_must_not_be_static); @@ -96,7 +98,7 @@ MIR* AllocReplacementMIR(MIRGraph* mir_graph, MIR* invoke, MIR* move_return) { uint32_t GetInvokeReg(MIR* invoke, uint32_t arg) { DCHECK_LT(arg, invoke->dalvikInsn.vA); - DCHECK(!MIRGraph::IsPseudoMirOp(invoke->dalvikInsn.opcode)); + DCHECK(!MIR::DecodedInstruction::IsPseudoMirOp(invoke->dalvikInsn.opcode)); if (Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc) { return invoke->dalvikInsn.vC + arg; // Non-range invoke. } else { @@ -107,7 +109,7 @@ uint32_t GetInvokeReg(MIR* invoke, uint32_t arg) { bool WideArgIsInConsecutiveDalvikRegs(MIR* invoke, uint32_t arg) { DCHECK_LT(arg + 1, invoke->dalvikInsn.vA); - DCHECK(!MIRGraph::IsPseudoMirOp(invoke->dalvikInsn.opcode)); + DCHECK(!MIR::DecodedInstruction::IsPseudoMirOp(invoke->dalvikInsn.opcode)); return Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc || invoke->dalvikInsn.arg[arg + 1u] == invoke->dalvikInsn.arg[arg] + 1u; } @@ -126,6 +128,7 @@ const char* const DexFileMethodInliner::kClassCacheNames[] = { "D", // kClassCacheDouble "V", // kClassCacheVoid "Ljava/lang/Object;", // kClassCacheJavaLangObject + "Ljava/lang/ref/Reference;", // kClassCacheJavaLangRefReference "Ljava/lang/String;", // kClassCacheJavaLangString "Ljava/lang/Double;", // kClassCacheJavaLangDouble "Ljava/lang/Float;", // kClassCacheJavaLangFloat @@ -152,6 +155,7 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = { "max", // kNameCacheMax "min", // kNameCacheMin "sqrt", // kNameCacheSqrt + "get", // kNameCacheGet "charAt", // kNameCacheCharAt "compareTo", // kNameCacheCompareTo "isEmpty", // kNameCacheIsEmpty @@ -220,6 +224,8 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { { kClassCacheBoolean, 0, { } }, // kProtoCache_I { kClassCacheInt, 0, { } }, + // kProtoCache_Object + { kClassCacheJavaLangObject, 0, { } }, // kProtoCache_Thread { kClassCacheJavaLangThread, 0, { } }, // kProtoCacheJ_B @@ -308,6 +314,8 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), + INTRINSIC(JavaLangRefReference, Get, _Object, kIntrinsicGet, 0), + INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty), @@ -428,6 +436,8 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { return backend->GenInlinedMinMaxFP(info, intrinsic.d.data & kIntrinsicFlagMin, true /* is_double */); case kIntrinsicSqrt: return backend->GenInlinedSqrt(info); + case kIntrinsicGet: + return backend->GenInlinedGet(info); case kIntrinsicCharAt: return backend->GenInlinedCharAt(info); case kIntrinsicCompareTo: diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 5b3b104150..cb8c165ce5 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -107,6 +107,7 @@ class DexFileMethodInliner { kClassCacheDouble, kClassCacheVoid, kClassCacheJavaLangObject, + kClassCacheJavaLangRefReference, kClassCacheJavaLangString, kClassCacheJavaLangDouble, kClassCacheJavaLangFloat, @@ -140,6 +141,7 @@ class DexFileMethodInliner { kNameCacheMax, kNameCacheMin, kNameCacheSqrt, + kNameCacheGet, kNameCacheCharAt, kNameCacheCompareTo, kNameCacheIsEmpty, @@ -199,6 +201,7 @@ class DexFileMethodInliner { kProtoCacheString_I, kProtoCache_Z, kProtoCache_I, + kProtoCache_Object, kProtoCache_Thread, kProtoCacheJ_B, kProtoCacheJ_I, diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 6dc019ac53..502859a72b 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -127,8 +127,8 @@ void Mir2Lir::GenArrayBoundsCheck(int index, RegStorage length) { m2l_->ResetDefTracking(); GenerateTargetLabel(kPseudoThrowTarget); - RegStorage arg1_32 = m2l_->TargetReg(kArg1, false); - RegStorage arg0_32 = m2l_->TargetReg(kArg0, false); + RegStorage arg1_32 = m2l_->TargetReg(kArg1, kNotWide); + RegStorage arg0_32 = m2l_->TargetReg(kArg0, kNotWide); m2l_->OpRegCopy(arg1_32, length_); m2l_->LoadConstant(arg0_32, index_); @@ -176,7 +176,7 @@ LIR* Mir2Lir::GenNullCheck(RegStorage reg) { /* Perform null-check on a register. */ LIR* Mir2Lir::GenNullCheck(RegStorage m_reg, int opt_flags) { - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { return GenExplicitNullCheck(m_reg, opt_flags); } return nullptr; @@ -191,16 +191,17 @@ LIR* Mir2Lir::GenExplicitNullCheck(RegStorage m_reg, int opt_flags) { } void Mir2Lir::MarkPossibleNullPointerException(int opt_flags) { - if (!cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { return; } + // Insert after last instruction. MarkSafepointPC(last_lir_insn_); } } void Mir2Lir::MarkPossibleNullPointerExceptionAfter(int opt_flags, LIR* after) { - if (!cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { return; } @@ -209,13 +210,13 @@ void Mir2Lir::MarkPossibleNullPointerExceptionAfter(int opt_flags, LIR* after) { } void Mir2Lir::MarkPossibleStackOverflowException() { - if (!cu_->compiler_driver->GetCompilerOptions().GetExplicitStackOverflowChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { MarkSafepointPC(last_lir_insn_); } } void Mir2Lir::ForceImplicitNullCheck(RegStorage reg, int opt_flags) { - if (!cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { return; } @@ -368,7 +369,8 @@ static void GenNewArrayImpl(Mir2Lir* mir_to_lir, CompilationUnit* cu, if (!use_direct_type_ptr) { mir_to_lir->LoadClassType(type_idx, kArg0); func_offset = QUICK_ENTRYPOINT_OFFSET(pointer_size, pAllocArrayResolved); - mir_to_lir->CallRuntimeHelperRegMethodRegLocation(func_offset, mir_to_lir->TargetReg(kArg0, false), + mir_to_lir->CallRuntimeHelperRegMethodRegLocation(func_offset, + mir_to_lir->TargetReg(kArg0, kNotWide), rl_src, true); } else { // Use the direct pointer. @@ -431,8 +433,8 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) { } else { GenFilledNewArrayCall<4>(this, cu_, elems, type_idx); } - FreeTemp(TargetReg(kArg2, false)); - FreeTemp(TargetReg(kArg1, false)); + FreeTemp(TargetReg(kArg2, kNotWide)); + FreeTemp(TargetReg(kArg1, kNotWide)); /* * NOTE: the implicit target for Instruction::FILLED_NEW_ARRAY is the * return region. Because AllocFromCode placed the new array @@ -440,7 +442,7 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) { * added, it may be necessary to additionally copy all return * values to a home location in thread-local storage */ - RegStorage ref_reg = TargetRefReg(kRet0); + RegStorage ref_reg = TargetReg(kRet0, kRef); LockTemp(ref_reg); // TODO: use the correct component size, currently all supported types @@ -477,7 +479,7 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) { switch (cu_->instruction_set) { case kThumb2: case kArm64: - r_val = TargetReg(kLr, false); + r_val = TargetReg(kLr, kNotWide); break; case kX86: case kX86_64: @@ -553,7 +555,7 @@ class StaticFieldSlowPath : public Mir2Lir::LIRSlowPath { storage_index_, true); } // Copy helper's result into r_base, a no-op on all but MIPS. - m2l_->OpRegCopy(r_base_, m2l_->TargetRefReg(kRet0)); + m2l_->OpRegCopy(r_base_, m2l_->TargetReg(kRet0, kRef)); m2l_->OpUnconditionalBranch(cont_); } @@ -580,8 +582,7 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double, const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir); cu_->compiler_driver->ProcessedStaticField(field_info.FastPut(), field_info.IsReferrersClass()); OpSize store_size = LoadStoreOpSize(is_long_or_double, is_object); - if (!SLOW_FIELD_PATH && field_info.FastPut() && - (!field_info.IsVolatile() || SupportsVolatileLoadStore(store_size))) { + if (!SLOW_FIELD_PATH && field_info.FastPut()) { DCHECK_GE(field_info.FieldOffset().Int32Value(), 0); RegStorage r_base; if (field_info.IsReferrersClass()) { @@ -601,10 +602,10 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double, // May do runtime call so everything to home locations. FlushAllRegs(); // Using fixed register to sync with possible call to runtime support. - RegStorage r_method = TargetRefReg(kArg1); + RegStorage r_method = TargetReg(kArg1, kRef); LockTemp(r_method); LoadCurrMethodDirect(r_method); - r_base = TargetRefReg(kArg0); + r_base = TargetReg(kArg0, kRef); LockTemp(r_base); LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base, kNotVolatile); @@ -618,19 +619,23 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double, // The slow path is invoked if the r_base is NULL or the class pointed // to by it is not initialized. LIR* unresolved_branch = OpCmpImmBranch(kCondEq, r_base, 0, NULL); - RegStorage r_tmp = TargetReg(kArg2, false); + RegStorage r_tmp = TargetReg(kArg2, kNotWide); LockTemp(r_tmp); LIR* uninit_branch = OpCmpMemImmBranch(kCondLt, r_tmp, r_base, mirror::Class::StatusOffset().Int32Value(), - mirror::Class::kStatusInitialized, NULL); + mirror::Class::kStatusInitialized, nullptr, nullptr); LIR* cont = NewLIR0(kPseudoTargetLabel); AddSlowPath(new (arena_) StaticFieldSlowPath(this, unresolved_branch, uninit_branch, cont, field_info.StorageIndex(), r_base)); FreeTemp(r_tmp); - // Ensure load of status and load of value don't re-order. - GenMemBarrier(kLoadLoad); + // Ensure load of status and store of value don't re-order. + // TODO: Presumably the actual value store is control-dependent on the status load, + // and will thus not be reordered in any case, since stores are never speculated. + // Does later code "know" that the class is now initialized? If so, we still + // need the barrier to guard later static loads. + GenMemBarrier(kLoadAny); } FreeTemp(r_method); } @@ -677,8 +682,7 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir); cu_->compiler_driver->ProcessedStaticField(field_info.FastGet(), field_info.IsReferrersClass()); OpSize load_size = LoadStoreOpSize(is_long_or_double, is_object); - if (!SLOW_FIELD_PATH && field_info.FastGet() && - (!field_info.IsVolatile() || SupportsVolatileLoadStore(load_size))) { + if (!SLOW_FIELD_PATH && field_info.FastGet()) { DCHECK_GE(field_info.FieldOffset().Int32Value(), 0); RegStorage r_base; if (field_info.IsReferrersClass()) { @@ -694,10 +698,10 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, // May do runtime call so everything to home locations. FlushAllRegs(); // Using fixed register to sync with possible call to runtime support. - RegStorage r_method = TargetRefReg(kArg1); + RegStorage r_method = TargetReg(kArg1, kRef); LockTemp(r_method); LoadCurrMethodDirect(r_method); - r_base = TargetRefReg(kArg0); + r_base = TargetReg(kArg0, kRef); LockTemp(r_base); LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base, kNotVolatile); @@ -711,11 +715,11 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, // The slow path is invoked if the r_base is NULL or the class pointed // to by it is not initialized. LIR* unresolved_branch = OpCmpImmBranch(kCondEq, r_base, 0, NULL); - RegStorage r_tmp = TargetReg(kArg2, false); + RegStorage r_tmp = TargetReg(kArg2, kNotWide); LockTemp(r_tmp); LIR* uninit_branch = OpCmpMemImmBranch(kCondLt, r_tmp, r_base, mirror::Class::StatusOffset().Int32Value(), - mirror::Class::kStatusInitialized, NULL); + mirror::Class::kStatusInitialized, nullptr, nullptr); LIR* cont = NewLIR0(kPseudoTargetLabel); AddSlowPath(new (arena_) StaticFieldSlowPath(this, unresolved_branch, uninit_branch, cont, @@ -723,7 +727,7 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, FreeTemp(r_tmp); // Ensure load of status and load of value don't re-order. - GenMemBarrier(kLoadLoad); + GenMemBarrier(kLoadAny); } FreeTemp(r_method); } @@ -782,6 +786,8 @@ static void GenIgetCall(Mir2Lir* mir_to_lir, bool is_long_or_double, bool is_obj is_long_or_double ? QUICK_ENTRYPOINT_OFFSET(pointer_size, pGet64Instance) : (is_object ? QUICK_ENTRYPOINT_OFFSET(pointer_size, pGetObjInstance) : QUICK_ENTRYPOINT_OFFSET(pointer_size, pGet32Instance)); + // Second argument of pGetXXInstance is always a reference. + DCHECK_EQ(static_cast<unsigned int>(rl_obj.wide), 0U); mir_to_lir->CallRuntimeHelperImmRegLocation(getter_offset, field_info->FieldIndex(), rl_obj, true); } @@ -792,8 +798,7 @@ void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size, const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir); cu_->compiler_driver->ProcessedInstanceField(field_info.FastGet()); OpSize load_size = LoadStoreOpSize(is_long_or_double, is_object); - if (!SLOW_FIELD_PATH && field_info.FastGet() && - (!field_info.IsVolatile() || SupportsVolatileLoadStore(load_size))) { + if (!SLOW_FIELD_PATH && field_info.FastGet()) { RegisterClass reg_class = RegClassForFieldLoadStore(load_size, field_info.IsVolatile()); DCHECK_GE(field_info.FieldOffset().Int32Value(), 0); rl_obj = LoadValue(rl_obj, kRefReg); @@ -849,8 +854,7 @@ void Mir2Lir::GenIPut(MIR* mir, int opt_flags, OpSize size, const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir); cu_->compiler_driver->ProcessedInstanceField(field_info.FastPut()); OpSize store_size = LoadStoreOpSize(is_long_or_double, is_object); - if (!SLOW_FIELD_PATH && field_info.FastPut() && - (!field_info.IsVolatile() || SupportsVolatileLoadStore(store_size))) { + if (!SLOW_FIELD_PATH && field_info.FastPut()) { RegisterClass reg_class = RegClassForFieldLoadStore(store_size, field_info.IsVolatile()); DCHECK_GE(field_info.FieldOffset().Int32Value(), 0); rl_obj = LoadValue(rl_obj, kRefReg); @@ -957,7 +961,7 @@ void Mir2Lir::GenConstClass(uint32_t type_idx, RegLocation rl_dest) { m2l_->CallRuntimeHelperImmReg(QUICK_ENTRYPOINT_OFFSET(4, pInitializeType), type_idx_, rl_method_.reg, true); } - m2l_->OpRegCopy(rl_result_.reg, m2l_->TargetRefReg(kRet0)); + m2l_->OpRegCopy(rl_result_.reg, m2l_->TargetReg(kRet0, kRef)); m2l_->OpUnconditionalBranch(cont_); } @@ -997,15 +1001,15 @@ void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) { DCHECK(!IsTemp(rl_method.reg)); r_method = rl_method.reg; } else { - r_method = TargetRefReg(kArg2); + r_method = TargetReg(kArg2, kRef); LoadCurrMethodDirect(r_method); } LoadRefDisp(r_method, mirror::ArtMethod::DexCacheStringsOffset().Int32Value(), - TargetRefReg(kArg0), kNotVolatile); + TargetReg(kArg0, kRef), kNotVolatile); // Might call out to helper, which will return resolved string in kRet0 - LoadRefDisp(TargetRefReg(kArg0), offset_of_string, TargetRefReg(kRet0), kNotVolatile); - LIR* fromfast = OpCmpImmBranch(kCondEq, TargetRefReg(kRet0), 0, NULL); + LoadRefDisp(TargetReg(kArg0, kRef), offset_of_string, TargetReg(kRet0, kRef), kNotVolatile); + LIR* fromfast = OpCmpImmBranch(kCondEq, TargetReg(kRet0, kRef), 0, NULL); LIR* cont = NewLIR0(kPseudoTargetLabel); { @@ -1074,10 +1078,12 @@ static void GenNewInstanceImpl(Mir2Lir* mir_to_lir, CompilationUnit* cu, uint32_ mir_to_lir->LoadClassType(type_idx, kArg0); if (!is_type_initialized) { func_offset = QUICK_ENTRYPOINT_OFFSET(pointer_size, pAllocObjectResolved); - mir_to_lir->CallRuntimeHelperRegMethod(func_offset, mir_to_lir->TargetRefReg(kArg0), true); + mir_to_lir->CallRuntimeHelperRegMethod(func_offset, mir_to_lir->TargetReg(kArg0, kRef), + true); } else { func_offset = QUICK_ENTRYPOINT_OFFSET(pointer_size, pAllocObjectInitialized); - mir_to_lir->CallRuntimeHelperRegMethod(func_offset, mir_to_lir->TargetRefReg(kArg0), true); + mir_to_lir->CallRuntimeHelperRegMethod(func_offset, mir_to_lir->TargetReg(kArg0, kRef), + true); } } else { // Use the direct pointer. @@ -1196,9 +1202,9 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know FlushAllRegs(); // May generate a call - use explicit registers LockCallTemps(); - RegStorage method_reg = TargetRefReg(kArg1); + RegStorage method_reg = TargetReg(kArg1, kRef); LoadCurrMethodDirect(method_reg); // kArg1 <= current Method* - RegStorage class_reg = TargetRefReg(kArg2); // kArg2 will hold the Class* + RegStorage class_reg = TargetReg(kArg2, kRef); // kArg2 will hold the Class* if (needs_access_check) { // Check we have access to type_idx and if not throw IllegalAccessError, // returns Class* in kArg0 @@ -1209,15 +1215,15 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(4, pInitializeTypeAndVerifyAccess), type_idx, true); } - OpRegCopy(class_reg, TargetRefReg(kRet0)); // Align usage with fast path - LoadValueDirectFixed(rl_src, TargetRefReg(kArg0)); // kArg0 <= ref + OpRegCopy(class_reg, TargetReg(kRet0, kRef)); // Align usage with fast path + LoadValueDirectFixed(rl_src, TargetReg(kArg0, kRef)); // kArg0 <= ref } else if (use_declaring_class) { - LoadValueDirectFixed(rl_src, TargetRefReg(kArg0)); // kArg0 <= ref + LoadValueDirectFixed(rl_src, TargetReg(kArg0, kRef)); // kArg0 <= ref LoadRefDisp(method_reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), class_reg, kNotVolatile); } else { // Load dex cache entry into class_reg (kArg2) - LoadValueDirectFixed(rl_src, TargetRefReg(kArg0)); // kArg0 <= ref + LoadValueDirectFixed(rl_src, TargetReg(kArg0, kRef)); // kArg0 <= ref LoadRefDisp(method_reg, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), class_reg, kNotVolatile); int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value(); @@ -1232,8 +1238,8 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know } else { CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(4, pInitializeType), type_idx, true); } - OpRegCopy(TargetRefReg(kArg2), TargetRefReg(kRet0)); // Align usage with fast path - LoadValueDirectFixed(rl_src, TargetRefReg(kArg0)); /* reload Ref */ + OpRegCopy(TargetReg(kArg2, kRef), TargetReg(kRet0, kRef)); // Align usage with fast path + LoadValueDirectFixed(rl_src, TargetReg(kArg0, kRef)); /* reload Ref */ // Rejoin code paths LIR* hop_target = NewLIR0(kPseudoTargetLabel); hop_branch->target = hop_target; @@ -1245,25 +1251,25 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know // On MIPS rArg0 != rl_result, place false in result if branch is taken. LoadConstant(rl_result.reg, 0); } - LIR* branch1 = OpCmpImmBranch(kCondEq, TargetRefReg(kArg0), 0, NULL); + LIR* branch1 = OpCmpImmBranch(kCondEq, TargetReg(kArg0, kRef), 0, NULL); /* load object->klass_ */ DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0); - LoadRefDisp(TargetRefReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetRefReg(kArg1), - kNotVolatile); + LoadRefDisp(TargetReg(kArg0, kRef), mirror::Object::ClassOffset().Int32Value(), + TargetReg(kArg1, kRef), kNotVolatile); /* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class */ LIR* branchover = NULL; if (type_known_final) { // rl_result == ref == null == 0. if (cu_->instruction_set == kThumb2) { - OpRegReg(kOpCmp, TargetRefReg(kArg1), TargetRefReg(kArg2)); // Same? + OpRegReg(kOpCmp, TargetReg(kArg1, kRef), TargetReg(kArg2, kRef)); // Same? LIR* it = OpIT(kCondEq, "E"); // if-convert the test LoadConstant(rl_result.reg, 1); // .eq case - load true LoadConstant(rl_result.reg, 0); // .ne case - load false OpEndIT(it); } else { LoadConstant(rl_result.reg, 0); // ne case - load false - branchover = OpCmpBranch(kCondNe, TargetRefReg(kArg1), TargetRefReg(kArg2), NULL); + branchover = OpCmpBranch(kCondNe, TargetReg(kArg1, kRef), TargetReg(kArg2, kRef), NULL); LoadConstant(rl_result.reg, 1); // eq case - load true } } else { @@ -1274,11 +1280,11 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know LIR* it = nullptr; if (!type_known_abstract) { /* Uses conditional nullification */ - OpRegReg(kOpCmp, TargetRefReg(kArg1), TargetRefReg(kArg2)); // Same? + OpRegReg(kOpCmp, TargetReg(kArg1, kRef), TargetReg(kArg2, kRef)); // Same? it = OpIT(kCondEq, "EE"); // if-convert the test - LoadConstant(TargetReg(kArg0, false), 1); // .eq case - load true + LoadConstant(TargetReg(kArg0, kNotWide), 1); // .eq case - load true } - OpRegCopy(TargetRefReg(kArg0), TargetRefReg(kArg2)); // .ne case - arg0 <= class + OpRegCopy(TargetReg(kArg0, kRef), TargetReg(kArg2, kRef)); // .ne case - arg0 <= class OpReg(kOpBlx, r_tgt); // .ne case: helper(class, ref->class) if (it != nullptr) { OpEndIT(it); @@ -1288,12 +1294,12 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know if (!type_known_abstract) { /* Uses branchovers */ LoadConstant(rl_result.reg, 1); // assume true - branchover = OpCmpBranch(kCondEq, TargetRefReg(kArg1), TargetRefReg(kArg2), NULL); + branchover = OpCmpBranch(kCondEq, TargetReg(kArg1, kRef), TargetReg(kArg2, kRef), NULL); } RegStorage r_tgt = cu_->target64 ? LoadHelper(QUICK_ENTRYPOINT_OFFSET(8, pInstanceofNonTrivial)) : LoadHelper(QUICK_ENTRYPOINT_OFFSET(4, pInstanceofNonTrivial)); - OpRegCopy(TargetRefReg(kArg0), TargetRefReg(kArg2)); // .ne case - arg0 <= class + OpRegCopy(TargetReg(kArg0, kRef), TargetReg(kArg2, kRef)); // .ne case - arg0 <= class OpReg(kOpBlx, r_tgt); // .ne case: helper(class, ref->class) FreeTemp(r_tgt); } @@ -1347,9 +1353,9 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ FlushAllRegs(); // May generate a call - use explicit registers LockCallTemps(); - RegStorage method_reg = TargetRefReg(kArg1); + RegStorage method_reg = TargetReg(kArg1, kRef); LoadCurrMethodDirect(method_reg); // kArg1 <= current Method* - RegStorage class_reg = TargetRefReg(kArg2); // kArg2 will hold the Class* + RegStorage class_reg = TargetReg(kArg2, kRef); // kArg2 will hold the Class* if (needs_access_check) { // Check we have access to type_idx and if not throw IllegalAccessError, // returns Class* in kRet0 @@ -1361,7 +1367,7 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(4, pInitializeTypeAndVerifyAccess), type_idx, true); } - OpRegCopy(class_reg, TargetRefReg(kRet0)); // Align usage with fast path + OpRegCopy(class_reg, TargetReg(kRet0, kRef)); // Align usage with fast path } else if (use_declaring_class) { LoadRefDisp(method_reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), class_reg, kNotVolatile); @@ -1392,12 +1398,12 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ // InitializeTypeFromCode(idx, method) if (m2l_->cu_->target64) { m2l_->CallRuntimeHelperImmReg(QUICK_ENTRYPOINT_OFFSET(8, pInitializeType), type_idx_, - m2l_->TargetRefReg(kArg1), true); + m2l_->TargetReg(kArg1, kRef), true); } else { m2l_->CallRuntimeHelperImmReg(QUICK_ENTRYPOINT_OFFSET(4, pInitializeType), type_idx_, - m2l_->TargetRefReg(kArg1), true); + m2l_->TargetReg(kArg1, kRef), true); } - m2l_->OpRegCopy(class_reg_, m2l_->TargetRefReg(kRet0)); // Align usage with fast path + m2l_->OpRegCopy(class_reg_, m2l_->TargetReg(kRet0, kRef)); // Align usage with fast path m2l_->OpUnconditionalBranch(cont_); } @@ -1410,7 +1416,7 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ } } // At this point, class_reg (kArg2) has class - LoadValueDirectFixed(rl_src, TargetRefReg(kArg0)); // kArg0 <= ref + LoadValueDirectFixed(rl_src, TargetReg(kArg0, kRef)); // kArg0 <= ref // Slow path for the case where the classes are not equal. In this case we need // to call a helper function to do the check. @@ -1424,15 +1430,17 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ GenerateTargetLabel(); if (load_) { - m2l_->LoadRefDisp(m2l_->TargetRefReg(kArg0), mirror::Object::ClassOffset().Int32Value(), - m2l_->TargetRefReg(kArg1), kNotVolatile); + m2l_->LoadRefDisp(m2l_->TargetReg(kArg0, kRef), mirror::Object::ClassOffset().Int32Value(), + m2l_->TargetReg(kArg1, kRef), kNotVolatile); } if (m2l_->cu_->target64) { - m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(8, pCheckCast), m2l_->TargetRefReg(kArg2), - m2l_->TargetRefReg(kArg1), true); + m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(8, pCheckCast), + m2l_->TargetReg(kArg2, kRef), m2l_->TargetReg(kArg1, kRef), + true); } else { - m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pCheckCast), m2l_->TargetRefReg(kArg2), - m2l_->TargetRefReg(kArg1), true); + m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pCheckCast), + m2l_->TargetReg(kArg2, kRef), m2l_->TargetReg(kArg1, kRef), + true); } m2l_->OpUnconditionalBranch(cont_); @@ -1444,7 +1452,7 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ if (type_known_abstract) { // Easier case, run slow path if target is non-null (slow path will load from target) - LIR* branch = OpCmpImmBranch(kCondNe, TargetRefReg(kArg0), 0, nullptr); + LIR* branch = OpCmpImmBranch(kCondNe, TargetReg(kArg0, kRef), 0, nullptr); LIR* cont = NewLIR0(kPseudoTargetLabel); AddSlowPath(new (arena_) SlowPath(this, branch, cont, true)); } else { @@ -1453,13 +1461,13 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ // slow path if the classes are not equal. /* Null is OK - continue */ - LIR* branch1 = OpCmpImmBranch(kCondEq, TargetRefReg(kArg0), 0, nullptr); + LIR* branch1 = OpCmpImmBranch(kCondEq, TargetReg(kArg0, kRef), 0, nullptr); /* load object->klass_ */ DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0); - LoadRefDisp(TargetRefReg(kArg0), mirror::Object::ClassOffset().Int32Value(), - TargetRefReg(kArg1), kNotVolatile); + LoadRefDisp(TargetReg(kArg0, kRef), mirror::Object::ClassOffset().Int32Value(), + TargetReg(kArg1, kRef), kNotVolatile); - LIR* branch2 = OpCmpBranch(kCondNe, TargetRefReg(kArg1), class_reg, nullptr); + LIR* branch2 = OpCmpBranch(kCondNe, TargetReg(kArg1, kRef), class_reg, nullptr); LIR* cont = NewLIR0(kPseudoTargetLabel); // Add the slow path that will not perform load since this is already done. @@ -1482,8 +1490,8 @@ void Mir2Lir::GenLong3Addr(OpKind first_op, OpKind second_op, RegLocation rl_des * lr is used explicitly elsewhere in the code generator and cannot * normally be used as a general temp register. */ - MarkTemp(TargetReg(kLr)); // Add lr to the temp pool - FreeTemp(TargetReg(kLr)); // and make it available + MarkTemp(TargetReg(kLr, kNotWide)); // Add lr to the temp pool + FreeTemp(TargetReg(kLr, kNotWide)); // and make it available } rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); @@ -1510,8 +1518,8 @@ void Mir2Lir::GenLong3Addr(OpKind first_op, OpKind second_op, RegLocation rl_des FreeRegLocTemps(rl_result, rl_src2); StoreValueWide(rl_dest, rl_result); if (cu_->instruction_set == kThumb2) { - Clobber(TargetReg(kLr)); - UnmarkTemp(TargetReg(kLr)); // Remove lr from the temp pool + Clobber(TargetReg(kLr, kNotWide)); + UnmarkTemp(TargetReg(kLr, kNotWide)); // Remove lr from the temp pool } } @@ -1675,13 +1683,13 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, // If we haven't already generated the code use the callout function. if (!done) { FlushAllRegs(); /* Send everything to home location */ - LoadValueDirectFixed(rl_src2, TargetReg(kArg1, false)); + LoadValueDirectFixed(rl_src2, TargetReg(kArg1, kNotWide)); RegStorage r_tgt = cu_->target64 ? CallHelperSetup(QUICK_ENTRYPOINT_OFFSET(8, pIdivmod)) : CallHelperSetup(QUICK_ENTRYPOINT_OFFSET(4, pIdivmod)); - LoadValueDirectFixed(rl_src1, TargetReg(kArg0, false)); + LoadValueDirectFixed(rl_src1, TargetReg(kArg0, kNotWide)); if (check_zero) { - GenDivZeroCheck(TargetReg(kArg1, false)); + GenDivZeroCheck(TargetReg(kArg1, kNotWide)); } // NOTE: callout here is not a safepoint. if (cu_->target64) { @@ -1945,14 +1953,14 @@ void Mir2Lir::GenArithOpIntLit(Instruction::Code opcode, RegLocation rl_dest, Re if (!done) { FlushAllRegs(); /* Everything to home location. */ - LoadValueDirectFixed(rl_src, TargetReg(kArg0, false)); - Clobber(TargetReg(kArg0, false)); + LoadValueDirectFixed(rl_src, TargetReg(kArg0, kNotWide)); + Clobber(TargetReg(kArg0, kNotWide)); if (cu_->target64) { - CallRuntimeHelperRegImm(QUICK_ENTRYPOINT_OFFSET(8, pIdivmod), TargetReg(kArg0, false), lit, - false); + CallRuntimeHelperRegImm(QUICK_ENTRYPOINT_OFFSET(8, pIdivmod), TargetReg(kArg0, kNotWide), + lit, false); } else { - CallRuntimeHelperRegImm(QUICK_ENTRYPOINT_OFFSET(4, pIdivmod), TargetReg(kArg0, false), lit, - false); + CallRuntimeHelperRegImm(QUICK_ENTRYPOINT_OFFSET(4, pIdivmod), TargetReg(kArg0, kNotWide), + lit, false); } if (is_div) rl_result = GetReturn(kCoreReg); @@ -1985,7 +1993,7 @@ static void GenArithOpLongImpl(Mir2Lir* mir_to_lir, CompilationUnit* cu, Instruc bool call_out = false; bool check_zero = false; ThreadOffset<pointer_size> func_offset(-1); - int ret_reg = mir_to_lir->TargetReg(kRet0, false).GetReg(); + int ret_reg = mir_to_lir->TargetReg(kRet0, kNotWide).GetReg(); switch (opcode) { case Instruction::NOT_LONG: @@ -2033,7 +2041,7 @@ static void GenArithOpLongImpl(Mir2Lir* mir_to_lir, CompilationUnit* cu, Instruc return; } else { call_out = true; - ret_reg = mir_to_lir->TargetReg(kRet0, false).GetReg(); + ret_reg = mir_to_lir->TargetReg(kRet0, kNotWide).GetReg(); func_offset = QUICK_ENTRYPOINT_OFFSET(pointer_size, pLmul); } break; @@ -2045,7 +2053,7 @@ static void GenArithOpLongImpl(Mir2Lir* mir_to_lir, CompilationUnit* cu, Instruc } call_out = true; check_zero = true; - ret_reg = mir_to_lir->TargetReg(kRet0, false).GetReg(); + ret_reg = mir_to_lir->TargetReg(kRet0, kNotWide).GetReg(); func_offset = QUICK_ENTRYPOINT_OFFSET(pointer_size, pLdiv); break; case Instruction::REM_LONG: @@ -2058,8 +2066,8 @@ static void GenArithOpLongImpl(Mir2Lir* mir_to_lir, CompilationUnit* cu, Instruc check_zero = true; func_offset = QUICK_ENTRYPOINT_OFFSET(pointer_size, pLmod); /* NOTE - for Arm, result is in kArg2/kArg3 instead of kRet0/kRet1 */ - ret_reg = (cu->instruction_set == kThumb2) ? mir_to_lir->TargetReg(kArg2, false).GetReg() : - mir_to_lir->TargetReg(kRet0, false).GetReg(); + ret_reg = (cu->instruction_set == kThumb2) ? mir_to_lir->TargetReg(kArg2, kNotWide).GetReg() : + mir_to_lir->TargetReg(kRet0, kNotWide).GetReg(); break; case Instruction::AND_LONG_2ADDR: case Instruction::AND_LONG: @@ -2102,11 +2110,11 @@ static void GenArithOpLongImpl(Mir2Lir* mir_to_lir, CompilationUnit* cu, Instruc } else { mir_to_lir->FlushAllRegs(); /* Send everything to home location */ if (check_zero) { - RegStorage r_tmp1 = mir_to_lir->TargetReg(kArg0, kArg1); - RegStorage r_tmp2 = mir_to_lir->TargetReg(kArg2, kArg3); + RegStorage r_tmp1 = mir_to_lir->TargetReg(kArg0, kWide); + RegStorage r_tmp2 = mir_to_lir->TargetReg(kArg2, kWide); mir_to_lir->LoadValueDirectWideFixed(rl_src2, r_tmp2); RegStorage r_tgt = mir_to_lir->CallHelperSetup(func_offset); - mir_to_lir->GenDivZeroCheckWide(mir_to_lir->TargetReg(kArg2, kArg3)); + mir_to_lir->GenDivZeroCheckWide(r_tmp2); mir_to_lir->LoadValueDirectWideFixed(rl_src1, r_tmp1); // NOTE: callout here is not a safepoint mir_to_lir->CallHelper(r_tgt, func_offset, false /* not safepoint */); @@ -2114,7 +2122,7 @@ static void GenArithOpLongImpl(Mir2Lir* mir_to_lir, CompilationUnit* cu, Instruc mir_to_lir->CallRuntimeHelperRegLocationRegLocation(func_offset, rl_src1, rl_src2, false); } // Adjust return regs in to handle case of rem returning kArg2/kArg3 - if (ret_reg == mir_to_lir->TargetReg(kRet0, false).GetReg()) + if (ret_reg == mir_to_lir->TargetReg(kRet0, kNotWide).GetReg()) rl_result = mir_to_lir->GetReturnWide(kCoreReg); else rl_result = mir_to_lir->GetReturnWideAlt(); @@ -2189,7 +2197,7 @@ class SuspendCheckSlowPath : public Mir2Lir::LIRSlowPath { /* Check if we need to check for pending suspend request */ void Mir2Lir::GenSuspendTest(int opt_flags) { - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitSuspendChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitSuspendChecks()) { if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) { return; } @@ -2209,7 +2217,7 @@ void Mir2Lir::GenSuspendTest(int opt_flags) { /* Check if we need to check for pending suspend request */ void Mir2Lir::GenSuspendTestAndBranch(int opt_flags, LIR* target) { - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitSuspendChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitSuspendChecks()) { if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) { OpUnconditionalBranch(target); return; diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 6c0dfe80a6..8ce6e1a206 100755 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -22,9 +22,13 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "invoke_type.h" #include "mirror/array.h" +#include "mirror/class-inl.h" +#include "mirror/dex_cache.h" #include "mirror/object_array-inl.h" +#include "mirror/reference-inl.h" #include "mirror/string.h" #include "mir_to_lir-inl.h" +#include "scoped_thread_state_change.h" #include "x86/codegen_x86.h" namespace art { @@ -130,9 +134,10 @@ void Mir2Lir::CallRuntimeHelper(ThreadOffset<pointer_size> helper_offset, bool s INSTANTIATE(void Mir2Lir::CallRuntimeHelper, bool safepoint_pc) template <size_t pointer_size> -void Mir2Lir::CallRuntimeHelperImm(ThreadOffset<pointer_size> helper_offset, int arg0, bool safepoint_pc) { +void Mir2Lir::CallRuntimeHelperImm(ThreadOffset<pointer_size> helper_offset, int arg0, + bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); - LoadConstant(TargetReg(kArg0, false), arg0); + LoadConstant(TargetReg(kArg0, kNotWide), arg0); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); } @@ -142,7 +147,7 @@ template <size_t pointer_size> void Mir2Lir::CallRuntimeHelperReg(ThreadOffset<pointer_size> helper_offset, RegStorage arg0, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); - OpRegCopy(TargetReg(kArg0, arg0.Is64Bit()), arg0); + OpRegCopy(TargetReg(kArg0, arg0.GetWideKind()), arg0); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); } @@ -155,13 +160,7 @@ void Mir2Lir::CallRuntimeHelperRegLocation(ThreadOffset<pointer_size> helper_off if (arg0.wide == 0) { LoadValueDirectFixed(arg0, TargetReg(arg0.fp ? kFArg0 : kArg0, arg0)); } else { - RegStorage r_tmp; - if (cu_->target64) { - r_tmp = TargetReg(kArg0, true); - } else { - r_tmp = TargetReg(arg0.fp ? kFArg0 : kArg0, arg0.fp ? kFArg1 : kArg1); - } - LoadValueDirectWideFixed(arg0, r_tmp); + LoadValueDirectWideFixed(arg0, TargetReg(arg0.fp ? kFArg0 : kArg0, kWide)); } ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); @@ -172,8 +171,8 @@ template <size_t pointer_size> void Mir2Lir::CallRuntimeHelperImmImm(ThreadOffset<pointer_size> helper_offset, int arg0, int arg1, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); - LoadConstant(TargetReg(kArg0, false), arg0); - LoadConstant(TargetReg(kArg1, false), arg1); + LoadConstant(TargetReg(kArg0, kNotWide), arg0); + LoadConstant(TargetReg(kArg1, kNotWide), arg1); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); } @@ -183,23 +182,14 @@ template <size_t pointer_size> void Mir2Lir::CallRuntimeHelperImmRegLocation(ThreadOffset<pointer_size> helper_offset, int arg0, RegLocation arg1, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); + DCHECK(!arg1.fp); if (arg1.wide == 0) { LoadValueDirectFixed(arg1, TargetReg(kArg1, arg1)); } else { - RegStorage r_tmp; - if (cu_->target64) { - r_tmp = TargetReg(kArg1, true); - } else { - if (cu_->instruction_set == kMips) { - // skip kArg1 for stack alignment. - r_tmp = TargetReg(kArg2, kArg3); - } else { - r_tmp = TargetReg(kArg1, kArg2); - } - } + RegStorage r_tmp = TargetReg(cu_->instruction_set == kMips ? kArg2 : kArg1, kWide); LoadValueDirectWideFixed(arg1, r_tmp); } - LoadConstant(TargetReg(kArg0, false), arg0); + LoadConstant(TargetReg(kArg0, kNotWide), arg0); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); } @@ -212,7 +202,7 @@ void Mir2Lir::CallRuntimeHelperRegLocationImm(ThreadOffset<pointer_size> helper_ RegStorage r_tgt = CallHelperSetup(helper_offset); DCHECK(!arg0.wide); LoadValueDirectFixed(arg0, TargetReg(kArg0, arg0)); - LoadConstant(TargetReg(kArg1, false), arg1); + LoadConstant(TargetReg(kArg1, kNotWide), arg1); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); } @@ -223,8 +213,8 @@ template <size_t pointer_size> void Mir2Lir::CallRuntimeHelperImmReg(ThreadOffset<pointer_size> helper_offset, int arg0, RegStorage arg1, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); - OpRegCopy(TargetReg(kArg1, arg1.Is64Bit()), arg1); - LoadConstant(TargetReg(kArg0, false), arg0); + OpRegCopy(TargetReg(kArg1, arg1.GetWideKind()), arg1); + LoadConstant(TargetReg(kArg0, kNotWide), arg0); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); } @@ -234,8 +224,8 @@ template <size_t pointer_size> void Mir2Lir::CallRuntimeHelperRegImm(ThreadOffset<pointer_size> helper_offset, RegStorage arg0, int arg1, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); - OpRegCopy(TargetReg(kArg0, arg0.Is64Bit()), arg0); - LoadConstant(TargetReg(kArg1, false), arg1); + OpRegCopy(TargetReg(kArg0, arg0.GetWideKind()), arg0); + LoadConstant(TargetReg(kArg1, kNotWide), arg1); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); } @@ -245,8 +235,8 @@ template <size_t pointer_size> void Mir2Lir::CallRuntimeHelperImmMethod(ThreadOffset<pointer_size> helper_offset, int arg0, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); - LoadCurrMethodDirect(TargetRefReg(kArg1)); - LoadConstant(TargetReg(kArg0, false), arg0); + LoadCurrMethodDirect(TargetReg(kArg1, kRef)); + LoadConstant(TargetReg(kArg0, kNotWide), arg0); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); } @@ -256,11 +246,12 @@ template <size_t pointer_size> void Mir2Lir::CallRuntimeHelperRegMethod(ThreadOffset<pointer_size> helper_offset, RegStorage arg0, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); - DCHECK(!IsSameReg(TargetReg(kArg1, arg0.Is64Bit()), arg0)); - if (TargetReg(kArg0, arg0.Is64Bit()).NotExactlyEquals(arg0)) { - OpRegCopy(TargetReg(kArg0, arg0.Is64Bit()), arg0); + DCHECK(!IsSameReg(TargetReg(kArg1, arg0.GetWideKind()), arg0)); + RegStorage r_tmp = TargetReg(kArg0, arg0.GetWideKind()); + if (r_tmp.NotExactlyEquals(arg0)) { + OpRegCopy(r_tmp, arg0); } - LoadCurrMethodDirect(TargetRefReg(kArg1)); + LoadCurrMethodDirect(TargetReg(kArg1, kRef)); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); } @@ -271,11 +262,12 @@ void Mir2Lir::CallRuntimeHelperRegMethodRegLocation(ThreadOffset<pointer_size> h RegStorage arg0, RegLocation arg2, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); - DCHECK(!IsSameReg(TargetReg(kArg1, arg0.Is64Bit()), arg0)); - if (TargetReg(kArg0, arg0.Is64Bit()).NotExactlyEquals(arg0)) { - OpRegCopy(TargetReg(kArg0, arg0.Is64Bit()), arg0); + DCHECK(!IsSameReg(TargetReg(kArg1, arg0.GetWideKind()), arg0)); + RegStorage r_tmp = TargetReg(kArg0, arg0.GetWideKind()); + if (r_tmp.NotExactlyEquals(arg0)) { + OpRegCopy(r_tmp, arg0); } - LoadCurrMethodDirect(TargetRefReg(kArg1)); + LoadCurrMethodDirect(TargetReg(kArg1, kRef)); LoadValueDirectFixed(arg2, TargetReg(kArg2, arg2)); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); @@ -312,47 +304,26 @@ void Mir2Lir::CallRuntimeHelperRegLocationRegLocation(ThreadOffset<pointer_size> } else { DCHECK(!cu_->target64); if (arg0.wide == 0) { - LoadValueDirectFixed(arg0, arg0.fp ? TargetReg(kFArg0, false) : TargetReg(kArg0, false)); + LoadValueDirectFixed(arg0, TargetReg(arg0.fp ? kFArg0 : kArg0, kNotWide)); if (arg1.wide == 0) { if (cu_->instruction_set == kMips) { - LoadValueDirectFixed(arg1, arg1.fp ? TargetReg(kFArg2, false) : TargetReg(kArg1, false)); + LoadValueDirectFixed(arg1, TargetReg(arg1.fp ? kFArg2 : kArg1, kNotWide)); } else { - LoadValueDirectFixed(arg1, TargetReg(kArg1, false)); + LoadValueDirectFixed(arg1, TargetReg(kArg1, kNotWide)); } } else { if (cu_->instruction_set == kMips) { - RegStorage r_tmp; - if (arg1.fp) { - r_tmp = TargetReg(kFArg2, kFArg3); - } else { - // skip kArg1 for stack alignment. - r_tmp = TargetReg(kArg2, kArg3); - } - LoadValueDirectWideFixed(arg1, r_tmp); + LoadValueDirectWideFixed(arg1, TargetReg(arg1.fp ? kFArg2 : kArg2, kWide)); } else { - RegStorage r_tmp; - r_tmp = TargetReg(kArg1, kArg2); - LoadValueDirectWideFixed(arg1, r_tmp); + LoadValueDirectWideFixed(arg1, TargetReg(kArg1, kWide)); } } } else { - RegStorage r_tmp; - if (arg0.fp) { - r_tmp = TargetReg(kFArg0, kFArg1); - } else { - r_tmp = TargetReg(kArg0, kArg1); - } - LoadValueDirectWideFixed(arg0, r_tmp); + LoadValueDirectWideFixed(arg0, TargetReg(arg0.fp ? kFArg0 : kArg0, kWide)); if (arg1.wide == 0) { - LoadValueDirectFixed(arg1, arg1.fp ? TargetReg(kFArg2, false) : TargetReg(kArg2, false)); + LoadValueDirectFixed(arg1, TargetReg(arg1.fp ? kFArg2 : kArg2, kNotWide)); } else { - RegStorage r_tmp; - if (arg1.fp) { - r_tmp = TargetReg(kFArg2, kFArg3); - } else { - r_tmp = TargetReg(kArg2, kArg3); - } - LoadValueDirectWideFixed(arg1, r_tmp); + LoadValueDirectWideFixed(arg1, TargetReg(arg1.fp ? kFArg2 : kArg2, kWide)); } } } @@ -363,19 +334,21 @@ INSTANTIATE(void Mir2Lir::CallRuntimeHelperRegLocationRegLocation, RegLocation a RegLocation arg1, bool safepoint_pc) void Mir2Lir::CopyToArgumentRegs(RegStorage arg0, RegStorage arg1) { - if (IsSameReg(arg1, TargetReg(kArg0, arg1.Is64Bit()))) { - if (IsSameReg(arg0, TargetReg(kArg1, arg0.Is64Bit()))) { + WideKind arg0_kind = arg0.GetWideKind(); + WideKind arg1_kind = arg1.GetWideKind(); + if (IsSameReg(arg1, TargetReg(kArg0, arg1_kind))) { + if (IsSameReg(arg0, TargetReg(kArg1, arg0_kind))) { // Swap kArg0 and kArg1 with kArg2 as temp. - OpRegCopy(TargetReg(kArg2, arg1.Is64Bit()), arg1); - OpRegCopy(TargetReg(kArg0, arg0.Is64Bit()), arg0); - OpRegCopy(TargetReg(kArg1, arg1.Is64Bit()), TargetReg(kArg2, arg1.Is64Bit())); + OpRegCopy(TargetReg(kArg2, arg1_kind), arg1); + OpRegCopy(TargetReg(kArg0, arg0_kind), arg0); + OpRegCopy(TargetReg(kArg1, arg1_kind), TargetReg(kArg2, arg1_kind)); } else { - OpRegCopy(TargetReg(kArg1, arg1.Is64Bit()), arg1); - OpRegCopy(TargetReg(kArg0, arg0.Is64Bit()), arg0); + OpRegCopy(TargetReg(kArg1, arg1_kind), arg1); + OpRegCopy(TargetReg(kArg0, arg0_kind), arg0); } } else { - OpRegCopy(TargetReg(kArg0, arg0.Is64Bit()), arg0); - OpRegCopy(TargetReg(kArg1, arg1.Is64Bit()), arg1); + OpRegCopy(TargetReg(kArg0, arg0_kind), arg0); + OpRegCopy(TargetReg(kArg1, arg1_kind), arg1); } } @@ -395,7 +368,7 @@ void Mir2Lir::CallRuntimeHelperRegRegImm(ThreadOffset<pointer_size> helper_offse RegStorage arg1, int arg2, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); CopyToArgumentRegs(arg0, arg1); - LoadConstant(TargetReg(kArg2, false), arg2); + LoadConstant(TargetReg(kArg2, kNotWide), arg2); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); } @@ -407,8 +380,8 @@ void Mir2Lir::CallRuntimeHelperImmMethodRegLocation(ThreadOffset<pointer_size> h int arg0, RegLocation arg2, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); LoadValueDirectFixed(arg2, TargetReg(kArg2, arg2)); - LoadCurrMethodDirect(TargetRefReg(kArg1)); - LoadConstant(TargetReg(kArg0, false), arg0); + LoadCurrMethodDirect(TargetReg(kArg1, kRef)); + LoadConstant(TargetReg(kArg0, kNotWide), arg0); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); } @@ -419,9 +392,9 @@ template <size_t pointer_size> void Mir2Lir::CallRuntimeHelperImmMethodImm(ThreadOffset<pointer_size> helper_offset, int arg0, int arg2, bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); - LoadCurrMethodDirect(TargetRefReg(kArg1)); - LoadConstant(TargetReg(kArg2, false), arg2); - LoadConstant(TargetReg(kArg0, false), arg0); + LoadCurrMethodDirect(TargetReg(kArg1, kRef)); + LoadConstant(TargetReg(kArg2, kNotWide), arg2); + LoadConstant(TargetReg(kArg0, kNotWide), arg0); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); } @@ -438,15 +411,9 @@ void Mir2Lir::CallRuntimeHelperImmRegLocationRegLocation(ThreadOffset<pointer_si if (arg2.wide == 0) { LoadValueDirectFixed(arg2, TargetReg(kArg2, arg2)); } else { - RegStorage r_tmp; - if (cu_->target64) { - r_tmp = TargetReg(kArg2, true); - } else { - r_tmp = TargetReg(kArg2, kArg3); - } - LoadValueDirectWideFixed(arg2, r_tmp); + LoadValueDirectWideFixed(arg2, TargetReg(kArg2, kWide)); } - LoadConstant(TargetReg(kArg0, false), arg0); + LoadConstant(TargetReg(kArg0, kNotWide), arg0); ClobberCallerSave(); CallHelper<pointer_size>(r_tgt, helper_offset, safepoint_pc); } @@ -454,10 +421,12 @@ INSTANTIATE(void Mir2Lir::CallRuntimeHelperImmRegLocationRegLocation, int arg0, RegLocation arg2, bool safepoint_pc) template <size_t pointer_size> -void Mir2Lir::CallRuntimeHelperRegLocationRegLocationRegLocation(ThreadOffset<pointer_size> helper_offset, - RegLocation arg0, RegLocation arg1, - RegLocation arg2, - bool safepoint_pc) { +void Mir2Lir::CallRuntimeHelperRegLocationRegLocationRegLocation( + ThreadOffset<pointer_size> helper_offset, + RegLocation arg0, + RegLocation arg1, + RegLocation arg2, + bool safepoint_pc) { RegStorage r_tgt = CallHelperSetup(helper_offset); LoadValueDirectFixed(arg0, TargetReg(kArg0, arg0)); LoadValueDirectFixed(arg1, TargetReg(kArg1, arg1)); @@ -484,7 +453,7 @@ void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { */ RegLocation rl_src = rl_method; rl_src.location = kLocPhysReg; - rl_src.reg = TargetRefReg(kArg0); + rl_src.reg = TargetReg(kArg0, kRef); rl_src.home = false; MarkLive(rl_src); StoreValue(rl_method, rl_src); @@ -558,15 +527,44 @@ void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { } else { // If arriving in frame & promoted if (v_map->core_location == kLocPhysReg) { - Load32Disp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), RegStorage::Solo32(v_map->core_reg)); + Load32Disp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), + RegStorage::Solo32(v_map->core_reg)); } if (v_map->fp_location == kLocPhysReg) { - Load32Disp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), RegStorage::Solo32(v_map->fp_reg)); + Load32Disp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), + RegStorage::Solo32(v_map->fp_reg)); } } } } +static void CommonCallCodeLoadThisIntoArg1(const CallInfo* info, Mir2Lir* cg) { + RegLocation rl_arg = info->args[0]; + cg->LoadValueDirectFixed(rl_arg, cg->TargetReg(kArg1, kRef)); +} + +static void CommonCallCodeLoadClassIntoArg0(const CallInfo* info, Mir2Lir* cg) { + cg->GenNullCheck(cg->TargetReg(kArg1, kRef), info->opt_flags); + // get this->klass_ [use kArg1, set kArg0] + cg->LoadRefDisp(cg->TargetReg(kArg1, kRef), mirror::Object::ClassOffset().Int32Value(), + cg->TargetReg(kArg0, kRef), + kNotVolatile); + cg->MarkPossibleNullPointerException(info->opt_flags); +} + +static bool CommonCallCodeLoadCodePointerIntoInvokeTgt(const CallInfo* info, + const RegStorage* alt_from, + const CompilationUnit* cu, Mir2Lir* cg) { + if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { + // Get the compiled code address [use *alt_from or kArg0, set kInvokeTgt] + cg->LoadWordDisp(alt_from == nullptr ? cg->TargetReg(kArg0, kRef) : *alt_from, + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(), + cg->TargetPtrReg(kInvokeTgt)); + return true; + } + return false; +} + /* * Bit of a hack here - in the absence of a real scheduling pass, * emit the next instruction in static & direct invoke sequences. @@ -588,7 +586,7 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, cg->LoadCodeAddress(target_method, type, kInvokeTgt); } if (direct_method != static_cast<uintptr_t>(-1)) { - cg->LoadConstant(cg->TargetRefReg(kArg0), direct_method); + cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method); } else { cg->LoadMethodAddress(target_method, type, kArg0); } @@ -597,7 +595,7 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, return -1; } } else { - RegStorage arg0_ref = cg->TargetRefReg(kArg0); + RegStorage arg0_ref = cg->TargetReg(kArg0, kRef); switch (state) { case 0: // Get the current Method* [sets kArg0] // TUNING: we can save a reg copy if Method* has been promoted. @@ -626,12 +624,11 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, kNotVolatile); break; case 3: // Grab the code from the method* - if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { - if (direct_code == 0) { - cg->LoadWordDisp(arg0_ref, - mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(), - cg->TargetPtrReg(kInvokeTgt)); + if (direct_code == 0) { + if (CommonCallCodeLoadCodePointerIntoInvokeTgt(info, &arg0_ref, cu, cg)) { + break; // kInvokeTgt := arg0_ref->entrypoint } + } else if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { break; } // Intentional fallthrough for x86 @@ -659,36 +656,24 @@ static int NextVCallInsn(CompilationUnit* cu, CallInfo* info, * fully resolved at compile time. */ switch (state) { - case 0: { // Get "this" [set kArg1] - RegLocation rl_arg = info->args[0]; - cg->LoadValueDirectFixed(rl_arg, cg->TargetRefReg(kArg1)); - break; - } - case 1: // Is "this" null? [use kArg1] - cg->GenNullCheck(cg->TargetRefReg(kArg1), info->opt_flags); - // get this->klass_ [use kArg1, set kInvokeTgt] - cg->LoadRefDisp(cg->TargetRefReg(kArg1), mirror::Object::ClassOffset().Int32Value(), - cg->TargetPtrReg(kInvokeTgt), - kNotVolatile); - cg->MarkPossibleNullPointerException(info->opt_flags); + case 0: + CommonCallCodeLoadThisIntoArg1(info, cg); // kArg1 := this break; - case 2: // Get this->klass_->vtable [usr kInvokeTgt, set kInvokeTgt] - cg->LoadRefDisp(cg->TargetPtrReg(kInvokeTgt), mirror::Class::VTableOffset().Int32Value(), - cg->TargetPtrReg(kInvokeTgt), - kNotVolatile); + case 1: + CommonCallCodeLoadClassIntoArg0(info, cg); // kArg0 := kArg1->class + // Includes a null-check. break; - case 3: // Get target method [use kInvokeTgt, set kArg0] - cg->LoadRefDisp(cg->TargetPtrReg(kInvokeTgt), - ObjArray::OffsetOfElement(method_idx).Int32Value(), - cg->TargetRefReg(kArg0), - kNotVolatile); + case 2: { + // Get this->klass_.embedded_vtable[method_idx] [usr kArg0, set kArg0] + int32_t offset = mirror::Class::EmbeddedVTableOffset().Uint32Value() + + method_idx * sizeof(mirror::Class::VTableEntry); + // Load target method from embedded vtable to kArg0 [use kArg0, set kArg0] + cg->LoadRefDisp(cg->TargetReg(kArg0, kRef), offset, cg->TargetReg(kArg0, kRef), kNotVolatile); break; - case 4: // Get the compiled code address [uses kArg0, sets kInvokeTgt] - if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { - cg->LoadWordDisp(cg->TargetRefReg(kArg0), - mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(), - cg->TargetPtrReg(kInvokeTgt)); - break; + } + case 3: + if (CommonCallCodeLoadCodePointerIntoInvokeTgt(info, nullptr, cu, cg)) { + break; // kInvokeTgt := kArg0->entrypoint } // Intentional fallthrough for X86 default: @@ -712,43 +697,28 @@ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, switch (state) { case 0: // Set target method index in case of conflict [set kHiddenArg, kHiddenFpArg (x86)] CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds()); - cg->LoadConstant(cg->TargetReg(kHiddenArg, false), target_method.dex_method_index); + cg->LoadConstant(cg->TargetReg(kHiddenArg, kNotWide), target_method.dex_method_index); if (cu->instruction_set == kX86) { - cg->OpRegCopy(cg->TargetReg(kHiddenFpArg, false), cg->TargetReg(kHiddenArg, false)); + cg->OpRegCopy(cg->TargetReg(kHiddenFpArg, kNotWide), cg->TargetReg(kHiddenArg, kNotWide)); } break; - case 1: { // Get "this" [set kArg1] - RegLocation rl_arg = info->args[0]; - cg->LoadValueDirectFixed(rl_arg, cg->TargetRefReg(kArg1)); + case 1: + CommonCallCodeLoadThisIntoArg1(info, cg); // kArg1 := this break; - } - case 2: // Is "this" null? [use kArg1] - cg->GenNullCheck(cg->TargetRefReg(kArg1), info->opt_flags); - // Get this->klass_ [use kArg1, set kInvokeTgt] - cg->LoadRefDisp(cg->TargetRefReg(kArg1), mirror::Object::ClassOffset().Int32Value(), - cg->TargetPtrReg(kInvokeTgt), - kNotVolatile); - cg->MarkPossibleNullPointerException(info->opt_flags); - break; - case 3: // Get this->klass_->imtable [use kInvokeTgt, set kInvokeTgt] - // NOTE: native pointer. - cg->LoadRefDisp(cg->TargetPtrReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(), - cg->TargetPtrReg(kInvokeTgt), - kNotVolatile); + case 2: + CommonCallCodeLoadClassIntoArg0(info, cg); // kArg0 := kArg1->class + // Includes a null-check. break; - case 4: // Get target method [use kInvokeTgt, set kArg0] - // NOTE: native pointer. - cg->LoadRefDisp(cg->TargetPtrReg(kInvokeTgt), - ObjArray::OffsetOfElement(method_idx % ClassLinker::kImtSize).Int32Value(), - cg->TargetRefReg(kArg0), - kNotVolatile); + case 3: { // Get target method [use kInvokeTgt, set kArg0] + int32_t offset = mirror::Class::EmbeddedImTableOffset().Uint32Value() + + (method_idx % mirror::Class::kImtSize) * sizeof(mirror::Class::ImTableEntry); + // Load target method from embedded imtable to kArg0 [use kArg0, set kArg0] + cg->LoadRefDisp(cg->TargetReg(kArg0, kRef), offset, cg->TargetReg(kArg0, kRef), kNotVolatile); break; - case 5: // Get the compiled code address [use kArg0, set kInvokeTgt] - if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { - cg->LoadWordDisp(cg->TargetRefReg(kArg0), - mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(), - cg->TargetPtrReg(kInvokeTgt)); - break; + } + case 4: + if (CommonCallCodeLoadCodePointerIntoInvokeTgt(info, nullptr, cu, cg)) { + break; // kInvokeTgt := kArg0->entrypoint } // Intentional fallthrough for X86 default: @@ -758,9 +728,9 @@ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, } template <size_t pointer_size> -static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, ThreadOffset<pointer_size> trampoline, - int state, const MethodReference& target_method, - uint32_t method_idx) { +static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, + ThreadOffset<pointer_size> trampoline, int state, + const MethodReference& target_method, uint32_t method_idx) { Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); /* * This handles the case in which the base method is not fully @@ -769,11 +739,12 @@ static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, ThreadOffset<po if (state == 0) { if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { // Load trampoline target - cg->LoadWordDisp(cg->TargetPtrReg(kSelf), trampoline.Int32Value(), cg->TargetPtrReg(kInvokeTgt)); + cg->LoadWordDisp(cg->TargetPtrReg(kSelf), trampoline.Int32Value(), + cg->TargetPtrReg(kInvokeTgt)); } // Load kArg0 with method index CHECK_EQ(cu->dex_file, target_method.dex_file); - cg->LoadConstant(cg->TargetReg(kArg0, false), target_method.dex_method_index); + cg->LoadConstant(cg->TargetReg(kArg0, kNotWide), target_method.dex_method_index); return 1; } return -1; @@ -824,10 +795,12 @@ static int NextVCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, uint32_t unused, uintptr_t unused2, uintptr_t unused3, InvokeType unused4) { if (cu->target64) { - ThreadOffset<8> trampoline = QUICK_ENTRYPOINT_OFFSET(8, pInvokeVirtualTrampolineWithAccessCheck); + ThreadOffset<8> trampoline = QUICK_ENTRYPOINT_OFFSET(8, + pInvokeVirtualTrampolineWithAccessCheck); return NextInvokeInsnSP<8>(cu, info, trampoline, state, target_method, 0); } else { - ThreadOffset<4> trampoline = QUICK_ENTRYPOINT_OFFSET(4, pInvokeVirtualTrampolineWithAccessCheck); + ThreadOffset<4> trampoline = QUICK_ENTRYPOINT_OFFSET(4, + pInvokeVirtualTrampolineWithAccessCheck); return NextInvokeInsnSP<4>(cu, info, trampoline, state, target_method, 0); } } @@ -838,10 +811,12 @@ static int NextInterfaceCallInsnWithAccessCheck(CompilationUnit* cu, uint32_t unused, uintptr_t unused2, uintptr_t unused3, InvokeType unused4) { if (cu->target64) { - ThreadOffset<8> trampoline = QUICK_ENTRYPOINT_OFFSET(8, pInvokeInterfaceTrampolineWithAccessCheck); + ThreadOffset<8> trampoline = QUICK_ENTRYPOINT_OFFSET(8, + pInvokeInterfaceTrampolineWithAccessCheck); return NextInvokeInsnSP<8>(cu, info, trampoline, state, target_method, 0); } else { - ThreadOffset<4> trampoline = QUICK_ENTRYPOINT_OFFSET(4, pInvokeInterfaceTrampolineWithAccessCheck); + ThreadOffset<4> trampoline = QUICK_ENTRYPOINT_OFFSET(4, + pInvokeInterfaceTrampolineWithAccessCheck); return NextInvokeInsnSP<4>(cu, info, trampoline, state, target_method, 0); } } @@ -852,7 +827,8 @@ int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state, uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this) { int last_arg_reg = 3 - 1; - int arg_regs[3] = {TargetReg(kArg1, false).GetReg(), TargetReg(kArg2, false).GetReg(), TargetReg(kArg3, false).GetReg()}; + int arg_regs[3] = {TargetReg(kArg1, kNotWide).GetReg(), TargetReg(kArg2, kNotWide).GetReg(), + TargetReg(kArg3, kNotWide).GetReg()}; int next_reg = 0; int next_arg = 0; @@ -927,7 +903,7 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, } } else { // kArg2 & rArg3 can safely be used here - reg = TargetReg(kArg3, false); + reg = TargetReg(kArg3, kNotWide); { ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); Load32Disp(TargetPtrReg(kSp), SRegOffset(rl_arg.s_reg_low) + 4, reg); @@ -951,7 +927,7 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, if (rl_arg.location == kLocPhysReg) { arg_reg = rl_arg.reg; } else { - arg_reg = rl_arg.wide ? TargetReg(kArg2, kArg3) : TargetReg(kArg2, false); + arg_reg = TargetReg(kArg2, rl_arg.wide ? kWide : kNotWide); if (rl_arg.wide) { LoadValueDirectWideFixed(rl_arg, arg_reg); } else { @@ -981,21 +957,35 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, type, skip_this); if (pcrLabel) { - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { - *pcrLabel = GenExplicitNullCheck(TargetRefReg(kArg1), info->opt_flags); + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags); } else { *pcrLabel = nullptr; + if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && + (info->opt_flags & MIR_IGNORE_NULL_CHECK)) { + return call_state; + } // In lieu of generating a check for kArg1 being null, we need to // perform a load when doing implicit checks. - RegStorage tmp = AllocTemp(); - Load32Disp(TargetRefReg(kArg1), 0, tmp); - MarkPossibleNullPointerException(info->opt_flags); - FreeTemp(tmp); + GenImplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags); } } return call_state; } +// Default implementation of implicit null pointer check. +// Overridden by arch specific as necessary. +void Mir2Lir::GenImplicitNullCheck(RegStorage reg, int opt_flags) { + if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { + return; + } + RegStorage tmp = AllocTemp(); + Load32Disp(reg, 0, tmp); + MarkPossibleNullPointerException(opt_flags); + FreeTemp(tmp); +} + + /* * May have 0+ arguments (also used for jumbo). Note that * source virtual registers may be in physical registers, so may @@ -1062,23 +1052,23 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, // Use vldm/vstm pair using kArg3 as a temp call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); - OpRegRegImm(kOpAdd, TargetRefReg(kArg3), TargetPtrReg(kSp), start_offset); + OpRegRegImm(kOpAdd, TargetReg(kArg3, kRef), TargetPtrReg(kSp), start_offset); LIR* ld = nullptr; { ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - ld = OpVldm(TargetRefReg(kArg3), regs_left_to_pass_via_stack); + ld = OpVldm(TargetReg(kArg3, kRef), regs_left_to_pass_via_stack); } // TUNING: loosen barrier ld->u.m.def_mask = &kEncodeAll; call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); - OpRegRegImm(kOpAdd, TargetRefReg(kArg3), TargetPtrReg(kSp), 4 /* Method* */ + (3 * 4)); + OpRegRegImm(kOpAdd, TargetReg(kArg3, kRef), TargetPtrReg(kSp), 4 /* Method* */ + (3 * 4)); call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); LIR* st = nullptr; { ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - st = OpVstm(TargetRefReg(kArg3), regs_left_to_pass_via_stack); + st = OpVstm(TargetReg(kArg3, kRef), regs_left_to_pass_via_stack); } st->u.m.def_mask = &kEncodeAll; call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, @@ -1152,7 +1142,8 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, if (ld2 != nullptr) { // For 64-bit load we can actually set up the aliasing information. AnnotateDalvikRegAccess(ld1, current_src_offset >> 2, true, true); - AnnotateDalvikRegAccess(ld2, (current_src_offset + (bytes_to_move >> 1)) >> 2, true, true); + AnnotateDalvikRegAccess(ld2, (current_src_offset + (bytes_to_move >> 1)) >> 2, true, + true); } else { // Set barrier for 128-bit load. ld1->u.m.def_mask = &kEncodeAll; @@ -1162,7 +1153,8 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, if (st2 != nullptr) { // For 64-bit store we can actually set up the aliasing information. AnnotateDalvikRegAccess(st1, current_dest_offset >> 2, false, true); - AnnotateDalvikRegAccess(st2, (current_dest_offset + (bytes_to_move >> 1)) >> 2, false, true); + AnnotateDalvikRegAccess(st2, (current_dest_offset + (bytes_to_move >> 1)) >> 2, false, + true); } else { // Set barrier for 128-bit store. st1->u.m.def_mask = &kEncodeAll; @@ -1177,7 +1169,7 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, // Instead of allocating a new temp, simply reuse one of the registers being used // for argument passing. - RegStorage temp = TargetReg(kArg3, false); + RegStorage temp = TargetReg(kArg3, kNotWide); // Now load the argument VR and store to the outs. Load32Disp(TargetPtrReg(kSp), current_src_offset, temp); @@ -1190,14 +1182,14 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, } } else { // Generate memcpy - OpRegRegImm(kOpAdd, TargetRefReg(kArg0), TargetPtrReg(kSp), outs_offset); - OpRegRegImm(kOpAdd, TargetRefReg(kArg1), TargetPtrReg(kSp), start_offset); + OpRegRegImm(kOpAdd, TargetReg(kArg0, kRef), TargetPtrReg(kSp), outs_offset); + OpRegRegImm(kOpAdd, TargetReg(kArg1, kRef), TargetPtrReg(kSp), start_offset); if (cu_->target64) { - CallRuntimeHelperRegRegImm(QUICK_ENTRYPOINT_OFFSET(8, pMemcpy), TargetRefReg(kArg0), - TargetRefReg(kArg1), (info->num_arg_words - 3) * 4, false); + CallRuntimeHelperRegRegImm(QUICK_ENTRYPOINT_OFFSET(8, pMemcpy), TargetReg(kArg0, kRef), + TargetReg(kArg1, kRef), (info->num_arg_words - 3) * 4, false); } else { - CallRuntimeHelperRegRegImm(QUICK_ENTRYPOINT_OFFSET(4, pMemcpy), TargetRefReg(kArg0), - TargetRefReg(kArg1), (info->num_arg_words - 3) * 4, false); + CallRuntimeHelperRegRegImm(QUICK_ENTRYPOINT_OFFSET(4, pMemcpy), TargetReg(kArg0, kRef), + TargetReg(kArg1, kRef), (info->num_arg_words - 3) * 4, false); } } @@ -1208,16 +1200,17 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); if (pcrLabel) { - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { - *pcrLabel = GenExplicitNullCheck(TargetRefReg(kArg1), info->opt_flags); + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags); } else { *pcrLabel = nullptr; + if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && + (info->opt_flags & MIR_IGNORE_NULL_CHECK)) { + return call_state; + } // In lieu of generating a check for kArg1 being null, we need to // perform a load when doing implicit checks. - RegStorage tmp = AllocTemp(); - Load32Disp(TargetRefReg(kArg1), 0, tmp); - MarkPossibleNullPointerException(info->opt_flags); - FreeTemp(tmp); + GenImplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags); } } return call_state; @@ -1243,6 +1236,88 @@ RegLocation Mir2Lir::InlineTargetWide(CallInfo* info) { return res; } +bool Mir2Lir::GenInlinedGet(CallInfo* info) { + if (cu_->instruction_set == kMips) { + // TODO - add Mips implementation + return false; + } + + // the refrence class is stored in the image dex file which might not be the same as the cu's + // dex file. Query the reference class for the image dex file then reset to starting dex file + // in after loading class type. + uint16_t type_idx = 0; + const DexFile* ref_dex_file = nullptr; + { + ScopedObjectAccess soa(Thread::Current()); + type_idx = mirror::Reference::GetJavaLangRefReference()->GetDexTypeIndex(); + ref_dex_file = mirror::Reference::GetJavaLangRefReference()->GetDexCache()->GetDexFile(); + } + CHECK(LIKELY(ref_dex_file != nullptr)); + + // address is either static within the image file, or needs to be patched up after compilation. + bool unused_type_initialized; + bool use_direct_type_ptr; + uintptr_t direct_type_ptr; + bool is_finalizable; + const DexFile* old_dex = cu_->dex_file; + cu_->dex_file = ref_dex_file; + RegStorage reg_class = TargetPtrReg(kArg1); + if (!cu_->compiler_driver->CanEmbedTypeInCode(*ref_dex_file, type_idx, &unused_type_initialized, + &use_direct_type_ptr, &direct_type_ptr, + &is_finalizable) || is_finalizable) { + cu_->dex_file = old_dex; + // address is not known and post-compile patch is not possible, cannot insert intrinsic. + return false; + } + if (use_direct_type_ptr) { + LoadConstant(reg_class, direct_type_ptr); + } else { + LoadClassType(type_idx, kArg1); + } + cu_->dex_file = old_dex; + + // get the offset for flags in reference class. + uint32_t slow_path_flag_offset = 0; + uint32_t disable_flag_offset = 0; + { + ScopedObjectAccess soa(Thread::Current()); + mirror::Class* reference_class = mirror::Reference::GetJavaLangRefReference(); + slow_path_flag_offset = reference_class->GetSlowPathFlagOffset().Uint32Value(); + disable_flag_offset = reference_class->GetDisableIntrinsicFlagOffset().Uint32Value(); + } + CHECK(slow_path_flag_offset && disable_flag_offset && + (slow_path_flag_offset != disable_flag_offset)); + + // intrinsic logic start. + RegLocation rl_obj = info->args[0]; + rl_obj = LoadValue(rl_obj); + + RegStorage reg_slow_path = AllocTemp(); + RegStorage reg_disabled = AllocTemp(); + Load32Disp(reg_class, slow_path_flag_offset, reg_slow_path); + Load32Disp(reg_class, disable_flag_offset, reg_disabled); + OpRegRegReg(kOpOr, reg_slow_path, reg_slow_path, reg_disabled); + FreeTemp(reg_disabled); + + // if slow path, jump to JNI path target + LIR* slow_path_branch = OpCmpImmBranch(kCondNe, reg_slow_path, 0, nullptr); + FreeTemp(reg_slow_path); + + // slow path not enabled, simply load the referent of the reference object + RegLocation rl_dest = InlineTarget(info); + RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true); + GenNullCheck(rl_obj.reg, info->opt_flags); + LoadRefDisp(rl_obj.reg, mirror::Reference::ReferentOffset().Int32Value(), rl_result.reg, + kNotVolatile); + MarkPossibleNullPointerException(info->opt_flags); + StoreValue(rl_dest, rl_result); + + LIR* intrinsic_finish = NewLIR0(kPseudoTargetLabel); + AddIntrinsicSlowPath(info, slow_path_branch, intrinsic_finish); + + return true; +} + bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { if (cu_->instruction_set == kMips) { // TODO - add Mips implementation @@ -1293,11 +1368,14 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { // On x86, we can compare to memory directly // Set up a launch pad to allow retry in case of bounds violation */ if (rl_idx.is_const) { + LIR* comparison; range_check_branch = OpCmpMemImmBranch( kCondUlt, RegStorage::InvalidReg(), rl_obj.reg, count_offset, - mir_graph_->ConstantValue(rl_idx.orig_sreg), nullptr); - } else { + mir_graph_->ConstantValue(rl_idx.orig_sreg), nullptr, &comparison); + MarkPossibleNullPointerExceptionAfter(0, comparison); + } else { OpRegMem(kOpCmp, rl_idx.reg, rl_obj.reg, count_offset); + MarkPossibleNullPointerException(0); range_check_branch = OpCondBranch(kCondUge, nullptr); } } @@ -1367,8 +1445,8 @@ bool Mir2Lir::GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty) { } bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) { - if (cu_->instruction_set == kMips || cu_->instruction_set == kArm64) { - // TODO - add Mips implementation; Enable Arm64. + if (cu_->instruction_set == kMips) { + // TODO - add Mips implementation. return false; } RegLocation rl_src_i = info->args[0]; @@ -1554,9 +1632,9 @@ bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { ClobberCallerSave(); LockCallTemps(); // Using fixed registers - RegStorage reg_ptr = TargetRefReg(kArg0); - RegStorage reg_char = TargetReg(kArg1, false); - RegStorage reg_start = TargetReg(kArg2, false); + RegStorage reg_ptr = TargetReg(kArg0, kRef); + RegStorage reg_char = TargetReg(kArg1, kNotWide); + RegStorage reg_start = TargetReg(kArg2, kNotWide); LoadValueDirectFixed(rl_obj, reg_ptr); LoadValueDirectFixed(rl_char, reg_char); @@ -1598,8 +1676,8 @@ bool Mir2Lir::GenInlinedStringCompareTo(CallInfo* info) { } ClobberCallerSave(); LockCallTemps(); // Using fixed registers - RegStorage reg_this = TargetRefReg(kArg0); - RegStorage reg_cmp = TargetRefReg(kArg1); + RegStorage reg_this = TargetReg(kArg0, kRef); + RegStorage reg_cmp = TargetReg(kArg1, kRef); RegLocation rl_this = info->args[0]; RegLocation rl_cmp = info->args[1]; @@ -1711,10 +1789,7 @@ bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info, } if (is_volatile) { - // Without context sensitive analysis, we must issue the most conservative barriers. - // In this case, either a load or store may follow so we issue both barriers. - GenMemBarrier(kLoadLoad); - GenMemBarrier(kLoadStore); + GenMemBarrier(kLoadAny); } if (is_long) { @@ -1737,8 +1812,7 @@ bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, rl_src_offset = NarrowRegLoc(rl_src_offset); // ignore high half in info->args[3] RegLocation rl_src_value = info->args[4]; // value to store if (is_volatile || is_ordered) { - // There might have been a store before this volatile one so insert StoreStore barrier. - GenMemBarrier(kStoreStore); + GenMemBarrier(kAnyStore); } RegLocation rl_object = LoadValue(rl_src_obj, kRefReg); RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg); @@ -1767,8 +1841,9 @@ bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, FreeTemp(rl_offset.reg); if (is_volatile) { - // A load might follow the volatile store so insert a StoreLoad barrier. - GenMemBarrier(kStoreLoad); + // Prevent reordering with a subsequent volatile load. + // May also be needed to address store atomicity issues. + GenMemBarrier(kAnyAny); } if (is_object) { MarkGCCard(rl_value.reg, rl_object.reg); @@ -1884,7 +1959,7 @@ void Mir2Lir::GenInvokeNoInline(CallInfo* info) { call_inst = reinterpret_cast<X86Mir2Lir*>(this)->CallWithLinkerFixup(target_method, info->type); } else { - call_inst = OpMem(kOpBlx, TargetRefReg(kArg0), + call_inst = OpMem(kOpBlx, TargetReg(kArg0, kRef), mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()); } } else { diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc index b97ff2a447..289315747a 100644 --- a/compiler/dex/quick/local_optimizations.cc +++ b/compiler/dex/quick/local_optimizations.cc @@ -121,20 +121,22 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { } ResourceMask stop_def_reg_mask = this_lir->u.m.def_mask->Without(kEncodeMem); - ResourceMask stop_use_reg_mask; - if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) { + + /* + * Add pc to the resource mask to prevent this instruction + * from sinking past branch instructions. Also take out the memory + * region bits since stop_mask is used to check data/control + * dependencies. + * + * Note: on x86(-64) and Arm64 we use the IsBranch bit, as the PC is not exposed. + */ + ResourceMask pc_encoding = GetPCUseDefEncoding(); + if (pc_encoding == kEncodeNone) { // TODO: Stop the abuse of kIsBranch as a bit specification for ResourceMask. - stop_use_reg_mask = ResourceMask::Bit(kIsBranch).Union(*this_lir->u.m.use_mask).Without( - kEncodeMem); - } else { - /* - * Add pc to the resource mask to prevent this instruction - * from sinking past branch instructions. Also take out the memory - * region bits since stop_mask is used to check data/control - * dependencies. - */ - stop_use_reg_mask = GetPCUseDefEncoding().Union(*this_lir->u.m.use_mask).Without(kEncodeMem); + pc_encoding = ResourceMask::Bit(kIsBranch); } + ResourceMask stop_use_reg_mask = pc_encoding.Union(*this_lir->u.m.use_mask). + Without(kEncodeMem); for (check_lir = NEXT_LIR(this_lir); check_lir != tail_lir; check_lir = NEXT_LIR(check_lir)) { /* @@ -310,16 +312,17 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) { ResourceMask stop_use_all_mask = *this_lir->u.m.use_mask; - if (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) { - /* - * Branches for null/range checks are marked with the true resource - * bits, and loads to Dalvik registers, constant pools, and non-alias - * locations are safe to be hoisted. So only mark the heap references - * conservatively here. - */ - if (stop_use_all_mask.HasBit(ResourceMask::kHeapRef)) { - stop_use_all_mask.SetBits(GetPCUseDefEncoding()); - } + /* + * Branches for null/range checks are marked with the true resource + * bits, and loads to Dalvik registers, constant pools, and non-alias + * locations are safe to be hoisted. So only mark the heap references + * conservatively here. + * + * Note: on x86(-64) and Arm64 this will add kEncodeNone. + * TODO: Sanity check. LoadStoreElimination uses kBranchBit to fake a PC. + */ + if (stop_use_all_mask.HasBit(ResourceMask::kHeapRef)) { + stop_use_all_mask.SetBits(GetPCUseDefEncoding()); } /* Similar as above, but just check for pure register dependency */ diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 025f97a282..4a060863df 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -47,6 +47,8 @@ class MipsMir2Lir FINAL : public Mir2Lir { OpSize size) OVERRIDE; LIR* StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement, RegStorage r_src, OpSize size) OVERRIDE; + LIR* GenAtomic64Load(RegStorage r_base, int displacement, RegStorage r_dest); + LIR* GenAtomic64Store(RegStorage r_base, int displacement, RegStorage r_src); void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg); // Required for target - register utilities. @@ -83,8 +85,6 @@ class MipsMir2Lir FINAL : public Mir2Lir { size_t GetInsnSize(LIR* lir) OVERRIDE; bool IsUnconditionalBranch(LIR* lir); - // Check support for volatile load/store of a given size. - bool SupportsVolatileLoadStore(OpSize size) OVERRIDE; // Get the register class for load/store of a field. RegisterClass RegClassForFieldLoadStore(OpSize size, bool is_volatile) OVERRIDE; @@ -192,6 +192,13 @@ class MipsMir2Lir FINAL : public Mir2Lir { bool InexpensiveConstantLong(int64_t value); bool InexpensiveConstantDouble(int64_t value); + bool WideGPRsAreAliases() OVERRIDE { + return false; // Wide GPRs are formed by pairing. + } + bool WideFPRsAreAliases() OVERRIDE { + return false; // Wide FPRs are formed by pairing. + } + private: void ConvertShortToLongBranch(LIR* lir); RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1, diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc index a5b7824cf6..4ba94c4781 100644 --- a/compiler/dex/quick/mips/target_mips.cc +++ b/compiler/dex/quick/mips/target_mips.cc @@ -496,6 +496,39 @@ LIR* MipsMir2Lir::CheckSuspendUsingLoad() { return inst; } +LIR* MipsMir2Lir::GenAtomic64Load(RegStorage r_base, int displacement, RegStorage r_dest) { + DCHECK(!r_dest.IsFloat()); // See RegClassForFieldLoadStore(). + DCHECK(r_dest.IsPair()); + ClobberCallerSave(); + LockCallTemps(); // Using fixed registers + RegStorage reg_ptr = TargetReg(kArg0); + OpRegRegImm(kOpAdd, reg_ptr, r_base, displacement); + RegStorage r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(4, pA64Load)); + LIR *ret = OpReg(kOpBlx, r_tgt); + RegStorage reg_ret = RegStorage::MakeRegPair(TargetReg(kRet0), TargetReg(kRet1)); + OpRegCopyWide(r_dest, reg_ret); + return ret; +} + +LIR* MipsMir2Lir::GenAtomic64Store(RegStorage r_base, int displacement, RegStorage r_src) { + DCHECK(!r_src.IsFloat()); // See RegClassForFieldLoadStore(). + DCHECK(r_src.IsPair()); + ClobberCallerSave(); + LockCallTemps(); // Using fixed registers + RegStorage temp_ptr = AllocTemp(); + OpRegRegImm(kOpAdd, temp_ptr, r_base, displacement); + RegStorage temp_value = AllocTempWide(); + OpRegCopyWide(temp_value, r_src); + RegStorage reg_ptr = TargetReg(kArg0); + OpRegCopy(reg_ptr, temp_ptr); + RegStorage reg_value = RegStorage::MakeRegPair(TargetReg(kArg2), TargetReg(kArg3)); + OpRegCopyWide(reg_value, temp_value); + FreeTemp(temp_ptr); + FreeTemp(temp_value); + RegStorage r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(4, pA64Store)); + return OpReg(kOpBlx, r_tgt); +} + void MipsMir2Lir::SpillCoreRegs() { if (num_core_spills_ == 0) { return; @@ -530,17 +563,12 @@ bool MipsMir2Lir::IsUnconditionalBranch(LIR* lir) { return (lir->opcode == kMipsB); } -bool MipsMir2Lir::SupportsVolatileLoadStore(OpSize size) { - // No support for 64-bit atomic load/store on mips. - return size != k64 && size != kDouble; -} - RegisterClass MipsMir2Lir::RegClassForFieldLoadStore(OpSize size, bool is_volatile) { if (UNLIKELY(is_volatile)) { - // On Mips, atomic 64-bit load/store requires an fp register. + // On Mips, atomic 64-bit load/store requires a core register. // Smaller aligned load/store is atomic for both core and fp registers. if (size == k64 || size == kDouble) { - return kFPReg; + return kCoreReg; } } // TODO: Verify that both core and fp registers are suitable for smaller sizes. diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc index 129a696625..0e8188b9d2 100644 --- a/compiler/dex/quick/mips/utility_mips.cc +++ b/compiler/dex/quick/mips/utility_mips.cc @@ -551,8 +551,9 @@ LIR* MipsMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStora LIR* MipsMir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, OpSize size, VolatileKind is_volatile) { - if (is_volatile == kVolatile) { - DCHECK(size != k64 && size != kDouble); + if (UNLIKELY(is_volatile == kVolatile && (size == k64 || size == kDouble))) { + // Do atomic 64-bit load. + return GenAtomic64Load(r_base, displacement, r_dest); } // TODO: base this on target. @@ -563,10 +564,7 @@ LIR* MipsMir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r load = LoadBaseDispBody(r_base, displacement, r_dest, size); if (UNLIKELY(is_volatile == kVolatile)) { - // Without context sensitive analysis, we must issue the most conservative barriers. - // In this case, either a load or store may follow so we issue both barriers. - GenMemBarrier(kLoadLoad); - GenMemBarrier(kLoadStore); + GenMemBarrier(kLoadAny); } return load; @@ -657,21 +655,26 @@ LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, LIR* MipsMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size, VolatileKind is_volatile) { if (is_volatile == kVolatile) { - DCHECK(size != k64 && size != kDouble); - // There might have been a store before this volatile one so insert StoreStore barrier. - GenMemBarrier(kStoreStore); + // Ensure that prior accesses become visible to other threads first. + GenMemBarrier(kAnyStore); } - // TODO: base this on target. - if (size == kWord) { - size = k32; - } LIR* store; - store = StoreBaseDispBody(r_base, displacement, r_src, size); + if (UNLIKELY(is_volatile == kVolatile && (size == k64 || size == kDouble))) { + // Do atomic 64-bit load. + store = GenAtomic64Store(r_base, displacement, r_src); + } else { + // TODO: base this on target. + if (size == kWord) { + size = k32; + } + store = StoreBaseDispBody(r_base, displacement, r_src, size); + } if (UNLIKELY(is_volatile == kVolatile)) { - // A load might follow the volatile store so insert a StoreLoad barrier. - GenMemBarrier(kStoreLoad); + // Preserve order with respect to any subsequent volatile loads. + // We need StoreLoad, but that generally requires the most expensive barrier. + GenMemBarrier(kAnyAny); } return store; diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index caadc0ad89..ed7fcdd556 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -18,7 +18,6 @@ #include "dex/dataflow_iterator-inl.h" #include "dex/quick/dex_file_method_inliner.h" #include "mir_to_lir-inl.h" -#include "object_utils.h" #include "thread-inl.h" namespace art { @@ -227,9 +226,6 @@ bool Mir2Lir::GenSpecialIGet(MIR* mir, const InlineMethod& special) { bool wide = (data.op_variant == InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE)); bool ref = (data.op_variant == InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT)); OpSize size = LoadStoreOpSize(wide, ref); - if (data.is_volatile && !SupportsVolatileLoadStore(size)) { - return false; - } // Point of no return - no aborts after this GenPrintLabel(mir); @@ -274,9 +270,6 @@ bool Mir2Lir::GenSpecialIPut(MIR* mir, const InlineMethod& special) { bool wide = (data.op_variant == InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE)); bool ref = (data.op_variant == InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT)); OpSize size = LoadStoreOpSize(wide, ref); - if (data.is_volatile && !SupportsVolatileLoadStore(size)) { - return false; - } // Point of no return - no aborts after this GenPrintLabel(mir); @@ -1185,7 +1178,7 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { work_half->meta.throw_insn = mir; } - if (MIRGraph::IsPseudoMirOp(opcode)) { + if (MIR::DecodedInstruction::IsPseudoMirOp(opcode)) { HandleExtendedMethodMIR(bb, mir); continue; } diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 48855012c3..5b5663353c 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -531,7 +531,7 @@ class Mir2Lir : public Backend { LIRSlowPath(Mir2Lir* m2l, const DexOffset dexpc, LIR* fromfast, LIR* cont = nullptr) : m2l_(m2l), cu_(m2l->cu_), current_dex_pc_(dexpc), fromfast_(fromfast), cont_(cont) { - m2l->StartSlowPath(cont); + m2l->StartSlowPath(this); } virtual ~LIRSlowPath() {} virtual void Compile() = 0; @@ -705,17 +705,17 @@ class Mir2Lir : public Backend { int AssignLiteralOffset(CodeOffset offset); int AssignSwitchTablesOffset(CodeOffset offset); int AssignFillArrayDataOffset(CodeOffset offset); - LIR* InsertCaseLabel(DexOffset vaddr, int keyVal); + virtual LIR* InsertCaseLabel(DexOffset vaddr, int keyVal); void MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec); void MarkSparseCaseLabels(Mir2Lir::SwitchTable* tab_rec); - virtual void StartSlowPath(LIR *label) {} + virtual void StartSlowPath(LIRSlowPath* slowpath) {} virtual void BeginInvoke(CallInfo* info) {} virtual void EndInvoke(CallInfo* info) {} // Handle bookkeeping to convert a wide RegLocation to a narrow RegLocation. No code generated. - RegLocation NarrowRegLoc(RegLocation loc); + virtual RegLocation NarrowRegLoc(RegLocation loc); // Shared by all targets - implemented in local_optimizations.cc void ConvertMemOpIntoMove(LIR* orig_lir, RegStorage dest, RegStorage src); @@ -763,7 +763,7 @@ class Mir2Lir : public Backend { virtual bool IsTemp(RegStorage reg); bool IsPromoted(RegStorage reg); bool IsDirty(RegStorage reg); - void LockTemp(RegStorage reg); + virtual void LockTemp(RegStorage reg); void ResetDef(RegStorage reg); void NullifyRange(RegStorage reg, int s_reg); void MarkDef(RegLocation rl, LIR *start, LIR *finish); @@ -838,6 +838,7 @@ class Mir2Lir : public Backend { LIR* GenImmedCheck(ConditionCode c_code, RegStorage reg, int imm_val, ThrowKind kind); LIR* GenNullCheck(RegStorage m_reg, int opt_flags); LIR* GenExplicitNullCheck(RegStorage m_reg, int opt_flags); + virtual void GenImplicitNullCheck(RegStorage reg, int opt_flags); void GenCompareAndBranch(Instruction::Code opcode, RegLocation rl_src1, RegLocation rl_src2, LIR* taken, LIR* fall_through); void GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_src, @@ -982,6 +983,7 @@ class Mir2Lir : public Backend { */ RegLocation InlineTargetWide(CallInfo* info); + bool GenInlinedGet(CallInfo* info); bool GenInlinedCharAt(CallInfo* info); bool GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty); virtual bool GenInlinedReverseBits(CallInfo* info, OpSize size); @@ -1147,10 +1149,12 @@ class Mir2Lir : public Backend { * @param base_reg The register holding the base address. * @param offset The offset from the base. * @param check_value The immediate to compare to. + * @param target branch target (or nullptr) + * @param compare output for getting LIR for comparison (or nullptr) * @returns The branch instruction that was generated. */ virtual LIR* OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, - int offset, int check_value, LIR* target); + int offset, int check_value, LIR* target, LIR** compare); // Required for target - codegen helpers. virtual bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, @@ -1198,29 +1202,28 @@ class Mir2Lir : public Backend { /** * @brief Portable way of getting special registers from the backend. * @param reg Enumeration describing the purpose of the register. - * @param is_wide Whether the view should be 64-bit (rather than 32-bit). + * @param wide_kind What kind of view of the special register is required. * @return Return the #RegStorage corresponding to the given purpose @p reg. + * + * @note For 32b system, wide (kWide) views only make sense for the argument registers and the + * return. In that case, this function should return a pair where the first component of + * the result will be the indicated special register. */ - virtual RegStorage TargetReg(SpecialTargetRegister reg, bool is_wide) { - return TargetReg(reg); - } - - /** - * @brief Portable way of getting special register pair from the backend. - * @param reg Enumeration describing the purpose of the first register. - * @param reg Enumeration describing the purpose of the second register. - * @return Return the #RegStorage corresponding to the given purpose @p reg. - */ - virtual RegStorage TargetReg(SpecialTargetRegister reg1, SpecialTargetRegister reg2) { - return RegStorage::MakeRegPair(TargetReg(reg1, false), TargetReg(reg2, false)); - } - - /** - * @brief Portable way of getting a special register for storing a reference. - * @see TargetReg() - */ - virtual RegStorage TargetRefReg(SpecialTargetRegister reg) { - return TargetReg(reg); + virtual RegStorage TargetReg(SpecialTargetRegister reg, WideKind wide_kind) { + if (wide_kind == kWide) { + DCHECK((kArg0 <= reg && reg < kArg7) || (kFArg0 <= reg && reg < kFArg7) || (kRet0 == reg)); + COMPILE_ASSERT((kArg1 == kArg0 + 1) && (kArg2 == kArg1 + 1) && (kArg3 == kArg2 + 1) && + (kArg4 == kArg3 + 1) && (kArg5 == kArg4 + 1) && (kArg6 == kArg5 + 1) && + (kArg7 == kArg6 + 1), kargs_range_unexpected); + COMPILE_ASSERT((kFArg1 == kFArg0 + 1) && (kFArg2 == kFArg1 + 1) && (kFArg3 == kFArg2 + 1) && + (kFArg4 == kFArg3 + 1) && (kFArg5 == kFArg4 + 1) && (kFArg6 == kFArg5 + 1) && + (kFArg7 == kFArg6 + 1), kfargs_range_unexpected); + COMPILE_ASSERT(kRet1 == kRet0 + 1, kret_range_unexpected); + return RegStorage::MakeRegPair(TargetReg(reg), + TargetReg(static_cast<SpecialTargetRegister>(reg + 1))); + } else { + return TargetReg(reg); + } } /** @@ -1234,9 +1237,9 @@ class Mir2Lir : public Backend { // Get a reg storage corresponding to the wide & ref flags of the reg location. virtual RegStorage TargetReg(SpecialTargetRegister reg, RegLocation loc) { if (loc.ref) { - return TargetRefReg(reg); + return TargetReg(reg, kRef); } else { - return TargetReg(reg, loc.wide); + return TargetReg(reg, loc.wide ? kWide : kNotWide); } } @@ -1263,13 +1266,14 @@ class Mir2Lir : public Backend { virtual const char* GetTargetInstFmt(int opcode) = 0; virtual const char* GetTargetInstName(int opcode) = 0; virtual std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr) = 0; + + // Note: This may return kEncodeNone on architectures that do not expose a PC. The caller must + // take care of this. virtual ResourceMask GetPCUseDefEncoding() const = 0; virtual uint64_t GetTargetInstFlags(int opcode) = 0; virtual size_t GetInsnSize(LIR* lir) = 0; virtual bool IsUnconditionalBranch(LIR* lir) = 0; - // Check support for volatile load/store of a given size. - virtual bool SupportsVolatileLoadStore(OpSize size) = 0; // Get the register class for load/store of a field. virtual RegisterClass RegClassForFieldLoadStore(OpSize size, bool is_volatile) = 0; @@ -1635,6 +1639,17 @@ class Mir2Lir : public Backend { */ virtual void GenConst(RegLocation rl_dest, int value); + /** + * Returns true iff wide GPRs are just different views on the same physical register. + */ + virtual bool WideGPRsAreAliases() = 0; + + /** + * Returns true iff wide FPRs are just different views on the same physical register. + */ + virtual bool WideFPRsAreAliases() = 0; + + enum class WidenessCheck { // private kIgnoreWide, kCheckWide, diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc index e8fc919d5f..fa1c36eaa6 100644 --- a/compiler/dex/quick/ralloc_util.cc +++ b/compiler/dex/quick/ralloc_util.cc @@ -1157,20 +1157,23 @@ void Mir2Lir::CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num int use_count = mir_graph_->GetUseCount(i); if (loc.fp) { if (loc.wide) { - // Treat doubles as a unit, using upper half of fp_counts array. - counts[p_map_idx + num_regs].count += use_count; + if (WideFPRsAreAliases()) { + // Floats and doubles can be counted together. + counts[p_map_idx].count += use_count; + } else { + // Treat doubles as a unit, using upper half of fp_counts array. + counts[p_map_idx + num_regs].count += use_count; + } i++; } else { counts[p_map_idx].count += use_count; } } else if (!IsInexpensiveConstant(loc)) { - if (loc.wide && cu_->target64) { - // Treat long as a unit, using upper half of core_counts array. - counts[p_map_idx + num_regs].count += use_count; + if (loc.wide && WideGPRsAreAliases()) { + // Longs and doubles can be counted together. i++; - } else { - counts[p_map_idx].count += use_count; } + counts[p_map_idx].count += use_count; } } } diff --git a/compiler/dex/quick/resource_mask.h b/compiler/dex/quick/resource_mask.h index 12ce98adc4..436cdb5440 100644 --- a/compiler/dex/quick/resource_mask.h +++ b/compiler/dex/quick/resource_mask.h @@ -63,6 +63,11 @@ class ResourceMask { ResourceMask(const ResourceMask& other) = default; ResourceMask& operator=(const ResourceMask& other) = default; + // Comparable by content. + bool operator==(const ResourceMask& other) { + return masks_[0] == other.masks_[0] && masks_[1] == other.masks_[1]; + } + static constexpr ResourceMask RawMask(uint64_t mask1, uint64_t mask2) { return ResourceMask(mask1, mask2); } diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 8df5b6dfdf..ebe3f0a9fc 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -271,21 +271,22 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86Shrd64RRI, kRegRegImmStore, IS_TERTIARY_OP | REG_DEF0_USE01 | SETS_CCODES, { REX_W, 0, 0x0F, 0xAC, 0, 0, 0, 1, false }, "Shrd64RRI", "!0r,!1r,!2d" }, { kX86Shrd64MRI, kMemRegImm, IS_QUAD_OP | REG_USE02 | IS_LOAD | IS_STORE | SETS_CCODES, { REX_W, 0, 0x0F, 0xAC, 0, 0, 0, 1, false }, "Shrd64MRI", "[!0r+!1d],!2r,!3d" }, - { kX86Test8RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1, true }, "Test8RI", "!0r,!1d" }, - { kX86Test8MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1, true }, "Test8MI", "[!0r+!1d],!2d" }, - { kX86Test8AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1, true }, "Test8AI", "[!0r+!1r<<!2d+!3d],!4d" }, - { kX86Test16RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2, false }, "Test16RI", "!0r,!1d" }, - { kX86Test16MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2, false }, "Test16MI", "[!0r+!1d],!2d" }, - { kX86Test16AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2, false }, "Test16AI", "[!0r+!1r<<!2d+!3d],!4d" }, - { kX86Test32RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test32RI", "!0r,!1d" }, - { kX86Test32MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test32MI", "[!0r+!1d],!2d" }, - { kX86Test32AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test32AI", "[!0r+!1r<<!2d+!3d],!4d" }, + { kX86Test8RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1, true }, "Test8RI", "!0r,!1d" }, + { kX86Test8MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1, true }, "Test8MI", "[!0r+!1d],!2d" }, + { kX86Test8AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1, true }, "Test8AI", "[!0r+!1r<<!2d+!3d],!4d" }, + { kX86Test16RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2, false }, "Test16RI", "!0r,!1d" }, + { kX86Test16MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2, false }, "Test16MI", "[!0r+!1d],!2d" }, + { kX86Test16AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2, false }, "Test16AI", "[!0r+!1r<<!2d+!3d],!4d" }, + { kX86Test32RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test32RI", "!0r,!1d" }, + { kX86Test32MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test32MI", "[!0r+!1d],!2d" }, + { kX86Test32AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test32AI", "[!0r+!1r<<!2d+!3d],!4d" }, { kX86Test64RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { REX_W, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test64RI", "!0r,!1d" }, { kX86Test64MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { REX_W, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test64MI", "[!0r+!1d],!2d" }, { kX86Test64AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { REX_W, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test64AI", "[!0r+!1r<<!2d+!3d],!4d" }, - { kX86Test32RR, kRegReg, IS_BINARY_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0x85, 0, 0, 0, 0, 0, false }, "Test32RR", "!0r,!1r" }, + { kX86Test32RR, kRegReg, IS_BINARY_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0x85, 0, 0, 0, 0, 0, false }, "Test32RR", "!0r,!1r" }, { kX86Test64RR, kRegReg, IS_BINARY_OP | REG_USE01 | SETS_CCODES, { REX_W, 0, 0x85, 0, 0, 0, 0, 0, false }, "Test64RR", "!0r,!1r" }, + { kX86Test32RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0x85, 0, 0, 0, 0, 0, false }, "Test32RM", "!0r,[!1r+!1d]" }, #define UNARY_ENCODING_MAP(opname, modrm, is_store, sets_ccodes, \ reg, reg_kind, reg_flags, \ diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index 9000514856..40dd9cc105 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -151,7 +151,7 @@ void X86Mir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) { // Making a call - use explicit registers FlushAllRegs(); /* Everything to home location */ - RegStorage array_ptr = TargetRefReg(kArg0); + RegStorage array_ptr = TargetReg(kArg0, kRef); RegStorage payload = TargetPtrReg(kArg1); RegStorage method_start = TargetPtrReg(kArg2); @@ -222,20 +222,32 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { LockTemp(rs_rX86_ARG1); LockTemp(rs_rX86_ARG2); - /* Build frame, return address already on stack */ - stack_decrement_ = OpRegImm(kOpSub, rs_rX86_SP, frame_size_ - GetInstructionSetPointerSize(cu_->instruction_set)); - /* * We can safely skip the stack overflow check if we're * a leaf *and* our frame size < fudge factor. */ - const bool skip_overflow_check = mir_graph_->MethodIsLeaf() && - !IsLargeFrame(frame_size_, cu_->target64 ? kX86_64 : kX86); + InstructionSet isa = cu_->target64 ? kX86_64 : kX86; + const bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, isa); + + // If we doing an implicit stack overflow check, perform the load immediately + // before the stack pointer is decremented and anything is saved. + if (!skip_overflow_check && + cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { + // Implicit stack overflow check. + // test eax,[esp + -overflow] + int overflow = GetStackOverflowReservedBytes(isa); + NewLIR3(kX86Test32RM, rs_rAX.GetReg(), rs_rX86_SP.GetReg(), -overflow); + MarkPossibleStackOverflowException(); + } + + /* Build frame, return address already on stack */ + stack_decrement_ = OpRegImm(kOpSub, rs_rX86_SP, frame_size_ - + GetInstructionSetPointerSize(cu_->instruction_set)); + NewLIR0(kPseudoMethodEntry); /* Spill core callee saves */ SpillCoreRegs(); - /* NOTE: promotion of FP regs currently unsupported, thus no FP spill */ - DCHECK_EQ(num_fp_spills_, 0); + SpillFPRegs(); if (!skip_overflow_check) { class StackOverflowSlowPath : public LIRSlowPath { public: @@ -261,25 +273,27 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { private: const size_t sp_displace_; }; - // TODO: for large frames we should do something like: - // spill ebp - // lea ebp, [esp + frame_size] - // cmp ebp, fs:[stack_end_] - // jcc stack_overflow_exception - // mov esp, ebp - // in case a signal comes in that's not using an alternate signal stack and the large frame may - // have moved us outside of the reserved area at the end of the stack. - // cmp rs_rX86_SP, fs:[stack_end_]; jcc throw_slowpath - if (cu_->target64) { - OpRegThreadMem(kOpCmp, rs_rX86_SP, Thread::StackEndOffset<8>()); - } else { - OpRegThreadMem(kOpCmp, rs_rX86_SP, Thread::StackEndOffset<4>()); - } - LIR* branch = OpCondBranch(kCondUlt, nullptr); - AddSlowPath( + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { + // TODO: for large frames we should do something like: + // spill ebp + // lea ebp, [esp + frame_size] + // cmp ebp, fs:[stack_end_] + // jcc stack_overflow_exception + // mov esp, ebp + // in case a signal comes in that's not using an alternate signal stack and the large frame + // may have moved us outside of the reserved area at the end of the stack. + // cmp rs_rX86_SP, fs:[stack_end_]; jcc throw_slowpath + if (cu_->target64) { + OpRegThreadMem(kOpCmp, rs_rX86_SP, Thread::StackEndOffset<8>()); + } else { + OpRegThreadMem(kOpCmp, rs_rX86_SP, Thread::StackEndOffset<4>()); + } + LIR* branch = OpCondBranch(kCondUlt, nullptr); + AddSlowPath( new(arena_)StackOverflowSlowPath(this, branch, frame_size_ - GetInstructionSetPointerSize(cu_->instruction_set))); + } } FlushIns(ArgLocs, rl_method); @@ -309,6 +323,7 @@ void X86Mir2Lir::GenExitSequence() { NewLIR0(kPseudoMethodExit); UnSpillCoreRegs(); + UnSpillFPRegs(); /* Remove frame except for return address */ stack_increment_ = OpRegImm(kOpAdd, rs_rX86_SP, frame_size_ - GetInstructionSetPointerSize(cu_->instruction_set)); NewLIR0(kX86Ret); @@ -318,4 +333,14 @@ void X86Mir2Lir::GenSpecialExitSequence() { NewLIR0(kX86Ret); } +void X86Mir2Lir::GenImplicitNullCheck(RegStorage reg, int opt_flags) { + if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { + return; + } + // Implicit null pointer check. + // test eax,[arg1+0] + NewLIR3(kX86Test32RM, rs_rAX.GetReg(), reg.GetReg(), 0); + MarkPossibleNullPointerException(opt_flags); +} + } // namespace art diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index ff7b30eeec..cf4521ae61 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -85,23 +85,31 @@ class X86Mir2Lir : public Mir2Lir { LIR* StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement, RegStorage r_src, OpSize size) OVERRIDE; void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg); + void GenImplicitNullCheck(RegStorage reg, int opt_flags); // Required for target - register utilities. RegStorage TargetReg(SpecialTargetRegister reg) OVERRIDE; RegStorage TargetReg32(SpecialTargetRegister reg); - RegStorage TargetReg(SpecialTargetRegister symbolic_reg, bool is_wide) OVERRIDE { - RegStorage reg = TargetReg32(symbolic_reg); - if (is_wide) { - return (reg.Is64Bit()) ? reg : As64BitReg(reg); + RegStorage TargetReg(SpecialTargetRegister symbolic_reg, WideKind wide_kind) OVERRIDE { + if (wide_kind == kWide) { + if (cu_->target64) { + return As64BitReg(TargetReg32(symbolic_reg)); + } else { + // x86: construct a pair. + DCHECK((kArg0 <= symbolic_reg && symbolic_reg < kArg3) || + (kFArg0 <= symbolic_reg && symbolic_reg < kFArg3) || + (kRet0 == symbolic_reg)); + return RegStorage::MakeRegPair(TargetReg32(symbolic_reg), + TargetReg32(static_cast<SpecialTargetRegister>(symbolic_reg + 1))); + } + } else if (wide_kind == kRef && cu_->target64) { + return As64BitReg(TargetReg32(symbolic_reg)); } else { - return (reg.Is32Bit()) ? reg : As32BitReg(reg); + return TargetReg32(symbolic_reg); } } - RegStorage TargetRefReg(SpecialTargetRegister symbolic_reg) OVERRIDE { - return TargetReg(symbolic_reg, cu_->target64); - } RegStorage TargetPtrReg(SpecialTargetRegister symbolic_reg) OVERRIDE { - return TargetReg(symbolic_reg, cu_->target64); + return TargetReg(symbolic_reg, cu_->target64 ? kWide : kNotWide); } RegStorage GetArgMappingToPhysicalReg(int arg_num); RegStorage GetCoreArgMappingToPhysicalReg(int core_arg_num); @@ -137,8 +145,6 @@ class X86Mir2Lir : public Mir2Lir { size_t GetInsnSize(LIR* lir) OVERRIDE; bool IsUnconditionalBranch(LIR* lir); - // Check support for volatile load/store of a given size. - bool SupportsVolatileLoadStore(OpSize size) OVERRIDE; // Get the register class for load/store of a field. RegisterClass RegClassForFieldLoadStore(OpSize size, bool is_volatile) OVERRIDE; @@ -319,6 +325,8 @@ class X86Mir2Lir : public Mir2Lir { void OpRegThreadMem(OpKind op, RegStorage r_dest, ThreadOffset<8> thread_offset); void SpillCoreRegs(); void UnSpillCoreRegs(); + void UnSpillFPRegs(); + void SpillFPRegs(); static const X86EncodingMap EncodingMap[kX86Last]; bool InexpensiveConstantInt(int32_t value); bool InexpensiveConstantFloat(int32_t value); @@ -796,9 +804,11 @@ class X86Mir2Lir : public Mir2Lir { * @param base_reg The register holding the base address. * @param offset The offset from the base. * @param check_value The immediate to compare to. + * @param target branch target (or nullptr) + * @param compare output for getting LIR for comparison (or nullptr) */ LIR* OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, - int offset, int check_value, LIR* target); + int offset, int check_value, LIR* target, LIR** compare); /* * Can this operation be using core registers without temporaries? @@ -931,6 +941,13 @@ class X86Mir2Lir : public Mir2Lir { InToRegStorageMapping in_to_reg_storage_mapping_; + bool WideGPRsAreAliases() OVERRIDE { + return cu_->target64; // On 64b, we have 64b GPRs. + } + bool WideFPRsAreAliases() OVERRIDE { + return true; // xmm registers have 64b views even on x86. + } + private: // The number of vector registers [0..N] reserved by a call to ReserveVectorRegisters int num_reserved_vector_regs_; diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index f1166f6263..3f1df189de 100755 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -19,6 +19,7 @@ #include "codegen_x86.h" #include "dex/quick/mir_to_lir-inl.h" #include "dex/reg_storage_eq.h" +#include "mirror/art_method.h" #include "mirror/array.h" #include "x86_lir.h" @@ -857,11 +858,12 @@ bool X86Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { RegLocation rl_new_value = LoadValueWide(rl_src_new_value, kCoreReg); RegLocation rl_offset = LoadValueWide(rl_src_offset, kCoreReg); LoadValueDirectWide(rl_src_expected, rs_r0q); - NewLIR5(kX86LockCmpxchg64AR, rl_object.reg.GetReg(), rl_offset.reg.GetReg(), 0, 0, rl_new_value.reg.GetReg()); + NewLIR5(kX86LockCmpxchg64AR, rl_object.reg.GetReg(), rl_offset.reg.GetReg(), 0, 0, + rl_new_value.reg.GetReg()); // After a store we need to insert barrier in case of potential load. Since the // locked cmpxchg has full barrier semantics, only a scheduling barrier will be generated. - GenMemBarrier(kStoreLoad); + GenMemBarrier(kAnyAny); FreeTemp(rs_r0q); } else if (is_long) { @@ -913,10 +915,11 @@ bool X86Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { } NewLIR4(kX86LockCmpxchg64A, rs_obj.GetReg(), rs_off.GetReg(), 0, 0); - // After a store we need to insert barrier in case of potential load. Since the - // locked cmpxchg has full barrier semantics, only a scheduling barrier will be generated. - GenMemBarrier(kStoreLoad); - + // After a store we need to insert barrier to prevent reordering with either + // earlier or later memory accesses. Since + // locked cmpxchg has full barrier semantics, only a scheduling barrier will be generated, + // and it will be associated with the cmpxchg instruction, preventing both. + GenMemBarrier(kAnyAny); if (push_si) { FreeTemp(rs_rSI); @@ -952,11 +955,14 @@ bool X86Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { rl_offset = LoadValue(rl_src_offset, kCoreReg); } LoadValueDirect(rl_src_expected, rs_r0); - NewLIR5(kX86LockCmpxchgAR, rl_object.reg.GetReg(), rl_offset.reg.GetReg(), 0, 0, rl_new_value.reg.GetReg()); + NewLIR5(kX86LockCmpxchgAR, rl_object.reg.GetReg(), rl_offset.reg.GetReg(), 0, 0, + rl_new_value.reg.GetReg()); - // After a store we need to insert barrier in case of potential load. Since the - // locked cmpxchg has full barrier semantics, only a scheduling barrier will be generated. - GenMemBarrier(kStoreLoad); + // After a store we need to insert barrier to prevent reordering with either + // earlier or later memory accesses. Since + // locked cmpxchg has full barrier semantics, only a scheduling barrier will be generated, + // and it will be associated with the cmpxchg instruction, preventing both. + GenMemBarrier(kAnyAny); FreeTemp(rs_r0); } @@ -1065,23 +1071,23 @@ void X86Mir2Lir::GenArrayBoundsCheck(RegStorage index, RegStorage new_index = index_; // Move index out of kArg1, either directly to kArg0, or to kArg2. // TODO: clean-up to check not a number but with type - if (index_ == m2l_->TargetReg(kArg1, false)) { - if (array_base_ == m2l_->TargetRefReg(kArg0)) { - m2l_->OpRegCopy(m2l_->TargetReg(kArg2, false), index_); - new_index = m2l_->TargetReg(kArg2, false); + if (index_ == m2l_->TargetReg(kArg1, kNotWide)) { + if (array_base_ == m2l_->TargetReg(kArg0, kRef)) { + m2l_->OpRegCopy(m2l_->TargetReg(kArg2, kNotWide), index_); + new_index = m2l_->TargetReg(kArg2, kNotWide); } else { - m2l_->OpRegCopy(m2l_->TargetReg(kArg0, false), index_); - new_index = m2l_->TargetReg(kArg0, false); + m2l_->OpRegCopy(m2l_->TargetReg(kArg0, kNotWide), index_); + new_index = m2l_->TargetReg(kArg0, kNotWide); } } // Load array length to kArg1. - m2l_->OpRegMem(kOpMov, m2l_->TargetReg(kArg1, false), array_base_, len_offset_); + m2l_->OpRegMem(kOpMov, m2l_->TargetReg(kArg1, kNotWide), array_base_, len_offset_); if (cu_->target64) { m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(8, pThrowArrayBounds), - new_index, m2l_->TargetReg(kArg1, false), true); + new_index, m2l_->TargetReg(kArg1, kNotWide), true); } else { m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds), - new_index, m2l_->TargetReg(kArg1, false), true); + new_index, m2l_->TargetReg(kArg1, kNotWide), true); } } @@ -1092,6 +1098,7 @@ void X86Mir2Lir::GenArrayBoundsCheck(RegStorage index, }; OpRegMem(kOpCmp, index, array_base, len_offset); + MarkPossibleNullPointerException(0); LIR* branch = OpCondBranch(kCondUge, nullptr); AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, index, array_base, len_offset)); @@ -1114,14 +1121,16 @@ void X86Mir2Lir::GenArrayBoundsCheck(int32_t index, GenerateTargetLabel(kPseudoThrowTarget); // Load array length to kArg1. - m2l_->OpRegMem(kOpMov, m2l_->TargetReg(kArg1, false), array_base_, len_offset_); - m2l_->LoadConstant(m2l_->TargetReg(kArg0, false), index_); + m2l_->OpRegMem(kOpMov, m2l_->TargetReg(kArg1, kNotWide), array_base_, len_offset_); + m2l_->LoadConstant(m2l_->TargetReg(kArg0, kNotWide), index_); if (cu_->target64) { m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(8, pThrowArrayBounds), - m2l_->TargetReg(kArg0, false), m2l_->TargetReg(kArg1, false), true); + m2l_->TargetReg(kArg0, kNotWide), + m2l_->TargetReg(kArg1, kNotWide), true); } else { m2l_->CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(4, pThrowArrayBounds), - m2l_->TargetReg(kArg0, false), m2l_->TargetReg(kArg1, false), true); + m2l_->TargetReg(kArg0, kNotWide), + m2l_->TargetReg(kArg1, kNotWide), true); } } @@ -1132,6 +1141,7 @@ void X86Mir2Lir::GenArrayBoundsCheck(int32_t index, }; NewLIR3(IS_SIMM8(index) ? kX86Cmp32MI8 : kX86Cmp32MI, array_base.GetReg(), len_offset, index); + MarkPossibleNullPointerException(0); LIR* branch = OpCondBranch(kCondLs, nullptr); AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, index, array_base, len_offset)); @@ -1467,7 +1477,8 @@ void X86Mir2Lir::GenLongRegOrMemOp(RegLocation rl_dest, RegLocation rl_src, int displacement = SRegOffset(rl_src.s_reg_low); ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); - LIR *lir = NewLIR3(x86op, cu_->target64 ? rl_dest.reg.GetReg() : rl_dest.reg.GetLowReg(), r_base, displacement + LOWORD_OFFSET); + LIR *lir = NewLIR3(x86op, cu_->target64 ? rl_dest.reg.GetReg() : rl_dest.reg.GetLowReg(), + r_base, displacement + LOWORD_OFFSET); AnnotateDalvikRegAccess(lir, (displacement + LOWORD_OFFSET) >> 2, true /* is_load */, true /* is64bit */); if (!cu_->target64) { @@ -2346,8 +2357,9 @@ void X86Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, // If Method* is already in a register, we can save a copy. RegLocation rl_method = mir_graph_->GetMethodLoc(); - int32_t offset_of_type = mirror::Array::DataOffset(sizeof(mirror::HeapReference<mirror::Class*>)).Int32Value() + - (sizeof(mirror::HeapReference<mirror::Class*>) * type_idx); + int32_t offset_of_type = mirror::Array::DataOffset( + sizeof(mirror::HeapReference<mirror::Class*>)).Int32Value() + + (sizeof(mirror::HeapReference<mirror::Class*>) * type_idx); if (rl_method.location == kLocPhysReg) { if (use_declaring_class) { @@ -2395,10 +2407,10 @@ void X86Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_k FlushAllRegs(); // May generate a call - use explicit registers. LockCallTemps(); - RegStorage method_reg = TargetRefReg(kArg1); // kArg1 gets current Method*. + RegStorage method_reg = TargetReg(kArg1, kRef); // kArg1 gets current Method*. LoadCurrMethodDirect(method_reg); - RegStorage class_reg = TargetRefReg(kArg2); // kArg2 will hold the Class*. - RegStorage ref_reg = TargetRefReg(kArg0); // kArg2 will hold the ref. + RegStorage class_reg = TargetReg(kArg2, kRef); // kArg2 will hold the Class*. + RegStorage ref_reg = TargetReg(kArg0, kRef); // kArg2 will hold the ref. // Reference must end up in kArg0. if (needs_access_check) { // Check we have access to type_idx and if not throw IllegalAccessError, @@ -2410,7 +2422,7 @@ void X86Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_k CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(4, pInitializeTypeAndVerifyAccess), type_idx, true); } - OpRegCopy(class_reg, TargetRefReg(kRet0)); + OpRegCopy(class_reg, TargetReg(kRet0, kRef)); LoadValueDirectFixed(rl_src, ref_reg); } else if (use_declaring_class) { LoadValueDirectFixed(rl_src, ref_reg); @@ -2434,7 +2446,7 @@ void X86Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_k } else { CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(4, pInitializeType), type_idx, true); } - OpRegCopy(class_reg, TargetRefReg(kRet0)); // Align usage with fast path. + OpRegCopy(class_reg, TargetReg(kRet0, kRef)); // Align usage with fast path. LoadValueDirectFixed(rl_src, ref_reg); /* Reload Ref. */ // Rejoin code paths LIR* hop_target = NewLIR0(kPseudoTargetLabel); @@ -2455,7 +2467,7 @@ void X86Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_k // Is the class NULL? LIR* branch1 = OpCmpImmBranch(kCondEq, ref_reg, 0, NULL); - RegStorage ref_class_reg = TargetRefReg(kArg1); // kArg2 will hold the Class*. + RegStorage ref_class_reg = TargetReg(kArg1, kRef); // kArg2 will hold the Class*. /* Load object->klass_. */ DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0); LoadRefDisp(ref_reg, mirror::Object::ClassOffset().Int32Value(), ref_class_reg, @@ -2473,7 +2485,7 @@ void X86Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_k LoadConstant(rl_result.reg, 1); // Assume result succeeds. branchover = OpCmpBranch(kCondEq, ref_class_reg, class_reg, NULL); } - OpRegCopy(TargetRefReg(kArg0), class_reg); + OpRegCopy(TargetReg(kArg0, kRef), class_reg); if (cu_->target64) { OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(8, pInstanceofNonTrivial)); } else { @@ -2613,7 +2625,7 @@ void X86Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, } else { if (shift_op) { // X86 doesn't require masking and must use ECX. - RegStorage t_reg = TargetReg(kCount, false); // rCX + RegStorage t_reg = TargetReg(kCount, kNotWide); // rCX LoadValueDirectFixed(rl_rhs, t_reg); if (is_two_addr) { // Can we do this directly into memory? @@ -2801,7 +2813,7 @@ void X86Mir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest, } // X86 doesn't require masking and must use ECX. - RegStorage t_reg = TargetReg(kCount, false); // rCX + RegStorage t_reg = TargetReg(kCount, kNotWide); // rCX LoadValueDirectFixed(rl_shift, t_reg); if (is_two_addr) { // Can we do this directly into memory? diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index e81f505f2f..06001d7f9d 100755 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -52,6 +52,13 @@ static constexpr RegStorage dp_regs_arr_64[] = { rs_dr0, rs_dr1, rs_dr2, rs_dr3, rs_dr4, rs_dr5, rs_dr6, rs_dr7, rs_dr8, rs_dr9, rs_dr10, rs_dr11, rs_dr12, rs_dr13, rs_dr14, rs_dr15 }; +static constexpr RegStorage xp_regs_arr_32[] = { + rs_xr0, rs_xr1, rs_xr2, rs_xr3, rs_xr4, rs_xr5, rs_xr6, rs_xr7, +}; +static constexpr RegStorage xp_regs_arr_64[] = { + rs_xr0, rs_xr1, rs_xr2, rs_xr3, rs_xr4, rs_xr5, rs_xr6, rs_xr7, + rs_xr8, rs_xr9, rs_xr10, rs_xr11, rs_xr12, rs_xr13, rs_xr14, rs_xr15 +}; static constexpr RegStorage reserved_regs_arr_32[] = {rs_rX86_SP_32}; static constexpr RegStorage reserved_regs_arr_64[] = {rs_rX86_SP_32}; static constexpr RegStorage reserved_regs_arr_64q[] = {rs_rX86_SP_64}; @@ -60,6 +67,24 @@ static constexpr RegStorage core_temps_arr_64[] = { rs_rAX, rs_rCX, rs_rDX, rs_rSI, rs_rDI, rs_r8, rs_r9, rs_r10, rs_r11 }; + +// How to add register to be available for promotion: +// 1) Remove register from array defining temp +// 2) Update ClobberCallerSave +// 3) Update JNI compiler ABI: +// 3.1) add reg in JniCallingConvention method +// 3.2) update CoreSpillMask/FpSpillMask +// 4) Update entrypoints +// 4.1) Update constants in asm_support_x86_64.h for new frame size +// 4.2) Remove entry in SmashCallerSaves +// 4.3) Update jni_entrypoints to spill/unspill new callee save reg +// 4.4) Update quick_entrypoints to spill/unspill new callee save reg +// 5) Update runtime ABI +// 5.1) Update quick_method_frame_info with new required spills +// 5.2) Update QuickArgumentVisitor with new offsets to gprs and xmms +// Note that you cannot use register corresponding to incoming args +// according to ABI and QCG needs one additional XMM temp for +// bulk copy in preparation to call. static constexpr RegStorage core_temps_arr_64q[] = { rs_r0q, rs_r1q, rs_r2q, rs_r6q, rs_r7q, rs_r8q, rs_r9q, rs_r10q, rs_r11q @@ -69,14 +94,14 @@ static constexpr RegStorage sp_temps_arr_32[] = { }; static constexpr RegStorage sp_temps_arr_64[] = { rs_fr0, rs_fr1, rs_fr2, rs_fr3, rs_fr4, rs_fr5, rs_fr6, rs_fr7, - rs_fr8, rs_fr9, rs_fr10, rs_fr11, rs_fr12, rs_fr13, rs_fr14, rs_fr15 + rs_fr8, rs_fr9, rs_fr10, rs_fr11 }; static constexpr RegStorage dp_temps_arr_32[] = { rs_dr0, rs_dr1, rs_dr2, rs_dr3, rs_dr4, rs_dr5, rs_dr6, rs_dr7, }; static constexpr RegStorage dp_temps_arr_64[] = { rs_dr0, rs_dr1, rs_dr2, rs_dr3, rs_dr4, rs_dr5, rs_dr6, rs_dr7, - rs_dr8, rs_dr9, rs_dr10, rs_dr11, rs_dr12, rs_dr13, rs_dr14, rs_dr15 + rs_dr8, rs_dr9, rs_dr10, rs_dr11 }; static constexpr RegStorage xp_temps_arr_32[] = { @@ -84,7 +109,7 @@ static constexpr RegStorage xp_temps_arr_32[] = { }; static constexpr RegStorage xp_temps_arr_64[] = { rs_xr0, rs_xr1, rs_xr2, rs_xr3, rs_xr4, rs_xr5, rs_xr6, rs_xr7, - rs_xr8, rs_xr9, rs_xr10, rs_xr11, rs_xr12, rs_xr13, rs_xr14, rs_xr15 + rs_xr8, rs_xr9, rs_xr10, rs_xr11 }; static constexpr ArrayRef<const RegStorage> empty_pool; @@ -95,6 +120,8 @@ static constexpr ArrayRef<const RegStorage> sp_regs_32(sp_regs_arr_32); static constexpr ArrayRef<const RegStorage> sp_regs_64(sp_regs_arr_64); static constexpr ArrayRef<const RegStorage> dp_regs_32(dp_regs_arr_32); static constexpr ArrayRef<const RegStorage> dp_regs_64(dp_regs_arr_64); +static constexpr ArrayRef<const RegStorage> xp_regs_32(xp_regs_arr_32); +static constexpr ArrayRef<const RegStorage> xp_regs_64(xp_regs_arr_64); static constexpr ArrayRef<const RegStorage> reserved_regs_32(reserved_regs_arr_32); static constexpr ArrayRef<const RegStorage> reserved_regs_64(reserved_regs_arr_64); static constexpr ArrayRef<const RegStorage> reserved_regs_64q(reserved_regs_arr_64q); @@ -177,7 +204,8 @@ RegStorage X86Mir2Lir::TargetReg32(SpecialTargetRegister reg) { case kSuspend: res_reg = RegStorage::InvalidReg(); break; case kLr: res_reg = RegStorage::InvalidReg(); break; case kPc: res_reg = RegStorage::InvalidReg(); break; - case kSp: res_reg = rs_rX86_SP; break; + case kSp: res_reg = rs_rX86_SP_32; break; // This must be the concrete one, as _SP is target- + // specific size. case kArg0: res_reg = rs_rX86_ARG0; break; case kArg1: res_reg = rs_rX86_ARG1; break; case kArg2: res_reg = rs_rX86_ARG2; break; @@ -219,11 +247,6 @@ ResourceMask X86Mir2Lir::GetRegMaskCommon(const RegStorage& reg) const { } ResourceMask X86Mir2Lir::GetPCUseDefEncoding() const { - /* - * FIXME: might make sense to use a virtual resource encoding bit for pc. Might be - * able to clean up some of the x86/Arm_Mips differences - */ - LOG(FATAL) << "Unexpected call to GetPCUseDefEncoding for x86"; return kEncodeNone; } @@ -437,21 +460,13 @@ bool X86Mir2Lir::IsByteRegister(RegStorage reg) { /* Clobber all regs that might be used by an external C call */ void X86Mir2Lir::ClobberCallerSave() { - Clobber(rs_rAX); - Clobber(rs_rCX); - Clobber(rs_rDX); - Clobber(rs_rBX); - - Clobber(rs_fr0); - Clobber(rs_fr1); - Clobber(rs_fr2); - Clobber(rs_fr3); - Clobber(rs_fr4); - Clobber(rs_fr5); - Clobber(rs_fr6); - Clobber(rs_fr7); - if (cu_->target64) { + Clobber(rs_rAX); + Clobber(rs_rCX); + Clobber(rs_rDX); + Clobber(rs_rSI); + Clobber(rs_rDI); + Clobber(rs_r8); Clobber(rs_r9); Clobber(rs_r10); @@ -461,11 +476,21 @@ void X86Mir2Lir::ClobberCallerSave() { Clobber(rs_fr9); Clobber(rs_fr10); Clobber(rs_fr11); - Clobber(rs_fr12); - Clobber(rs_fr13); - Clobber(rs_fr14); - Clobber(rs_fr15); + } else { + Clobber(rs_rAX); + Clobber(rs_rCX); + Clobber(rs_rDX); + Clobber(rs_rBX); } + + Clobber(rs_fr0); + Clobber(rs_fr1); + Clobber(rs_fr2); + Clobber(rs_fr3); + Clobber(rs_fr4); + Clobber(rs_fr5); + Clobber(rs_fr6); + Clobber(rs_fr7); } RegLocation X86Mir2Lir::GetReturnWideAlt() { @@ -553,11 +578,11 @@ bool X86Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { bool ret = false; /* - * According to the JSR-133 Cookbook, for x86 only StoreLoad barriers need memory fence. All other barriers - * (LoadLoad, LoadStore, StoreStore) are nops due to the x86 memory model. For those cases, all we need - * to ensure is that there is a scheduling barrier in place. + * According to the JSR-133 Cookbook, for x86 only StoreLoad/AnyAny barriers need memory fence. + * All other barriers (LoadAny, AnyStore, StoreStore) are nops due to the x86 memory model. + * For those cases, all we need to ensure is that there is a scheduling barrier in place. */ - if (barrier_kind == kStoreLoad) { + if (barrier_kind == kAnyAny) { // If no LIR exists already that can be used a barrier, then generate an mfence. if (mem_barrier == nullptr) { mem_barrier = NewLIR0(kX86Mfence); @@ -599,11 +624,15 @@ void X86Mir2Lir::CompilerInitializeRegAlloc() { // Target-specific adjustments. // Add in XMM registers. - const ArrayRef<const RegStorage> *xp_temps = cu_->target64 ? &xp_temps_64 : &xp_temps_32; - for (RegStorage reg : *xp_temps) { + const ArrayRef<const RegStorage> *xp_regs = cu_->target64 ? &xp_regs_64 : &xp_regs_32; + for (RegStorage reg : *xp_regs) { RegisterInfo* info = new (arena_) RegisterInfo(reg, GetRegMaskCommon(reg)); reginfo_map_.Put(reg.GetReg(), info); - info->SetIsTemp(true); + } + const ArrayRef<const RegStorage> *xp_temps = cu_->target64 ? &xp_temps_64 : &xp_temps_32; + for (RegStorage reg : *xp_temps) { + RegisterInfo* xp_reg_info = GetRegInfo(reg); + xp_reg_info->SetIsTemp(true); } // Alias single precision xmm to double xmms. @@ -665,9 +694,11 @@ void X86Mir2Lir::SpillCoreRegs() { // Spill mask not including fake return address register uint32_t mask = core_spill_mask_ & ~(1 << rs_rRET.GetRegNum()); int offset = frame_size_ - (GetInstructionSetPointerSize(cu_->instruction_set) * num_core_spills_); + OpSize size = cu_->target64 ? k64 : k32; for (int reg = 0; mask; mask >>= 1, reg++) { if (mask & 0x1) { - StoreWordDisp(rs_rX86_SP, offset, RegStorage::Solo32(reg)); + StoreBaseDisp(rs_rX86_SP, offset, cu_->target64 ? RegStorage::Solo64(reg) : RegStorage::Solo32(reg), + size, kNotVolatile); offset += GetInstructionSetPointerSize(cu_->instruction_set); } } @@ -680,20 +711,48 @@ void X86Mir2Lir::UnSpillCoreRegs() { // Spill mask not including fake return address register uint32_t mask = core_spill_mask_ & ~(1 << rs_rRET.GetRegNum()); int offset = frame_size_ - (GetInstructionSetPointerSize(cu_->instruction_set) * num_core_spills_); + OpSize size = cu_->target64 ? k64 : k32; for (int reg = 0; mask; mask >>= 1, reg++) { if (mask & 0x1) { - LoadWordDisp(rs_rX86_SP, offset, RegStorage::Solo32(reg)); + LoadBaseDisp(rs_rX86_SP, offset, cu_->target64 ? RegStorage::Solo64(reg) : RegStorage::Solo32(reg), + size, kNotVolatile); offset += GetInstructionSetPointerSize(cu_->instruction_set); } } } -bool X86Mir2Lir::IsUnconditionalBranch(LIR* lir) { - return (lir->opcode == kX86Jmp8 || lir->opcode == kX86Jmp32); +void X86Mir2Lir::SpillFPRegs() { + if (num_fp_spills_ == 0) { + return; + } + uint32_t mask = fp_spill_mask_; + int offset = frame_size_ - (GetInstructionSetPointerSize(cu_->instruction_set) * (num_fp_spills_ + num_core_spills_)); + for (int reg = 0; mask; mask >>= 1, reg++) { + if (mask & 0x1) { + StoreBaseDisp(rs_rX86_SP, offset, RegStorage::FloatSolo64(reg), + k64, kNotVolatile); + offset += sizeof(double); + } + } +} +void X86Mir2Lir::UnSpillFPRegs() { + if (num_fp_spills_ == 0) { + return; + } + uint32_t mask = fp_spill_mask_; + int offset = frame_size_ - (GetInstructionSetPointerSize(cu_->instruction_set) * (num_fp_spills_ + num_core_spills_)); + for (int reg = 0; mask; mask >>= 1, reg++) { + if (mask & 0x1) { + LoadBaseDisp(rs_rX86_SP, offset, RegStorage::FloatSolo64(reg), + k64, kNotVolatile); + offset += sizeof(double); + } + } } -bool X86Mir2Lir::SupportsVolatileLoadStore(OpSize size) { - return true; + +bool X86Mir2Lir::IsUnconditionalBranch(LIR* lir) { + return (lir->opcode == kX86Jmp8 || lir->opcode == kX86Jmp32); } RegisterClass X86Mir2Lir::RegClassForFieldLoadStore(OpSize size, bool is_volatile) { @@ -825,8 +884,12 @@ RegStorage X86Mir2Lir::LoadHelper(ThreadOffset<8> offset) { } LIR* X86Mir2Lir::CheckSuspendUsingLoad() { - LOG(FATAL) << "Unexpected use of CheckSuspendUsingLoad in x86"; - return nullptr; + // First load the pointer in fs:[suspend-trigger] into eax + // Then use a test instruction to indirect via that address. + NewLIR2(kX86Mov32RT, rs_rAX.GetReg(), cu_->target64 ? + Thread::ThreadSuspendTriggerOffset<8>().Int32Value() : + Thread::ThreadSuspendTriggerOffset<4>().Int32Value()); + return NewLIR3(kX86Test32RM, rs_rAX.GetReg(), rs_rAX.GetReg(), 0); } uint64_t X86Mir2Lir::GetTargetInstFlags(int opcode) { @@ -906,7 +969,8 @@ void X86Mir2Lir::LoadMethodAddress(const MethodReference& target_method, InvokeT uintptr_t target_method_id_ptr = reinterpret_cast<uintptr_t>(&target_method_id); // Generate the move instruction with the unique pointer and save index, dex_file, and type. - LIR *move = RawLIR(current_dalvik_offset_, kX86Mov32RI, TargetReg(symbolic_reg, false).GetReg(), + LIR *move = RawLIR(current_dalvik_offset_, kX86Mov32RI, + TargetReg(symbolic_reg, kNotWide).GetReg(), static_cast<int>(target_method_id_ptr), target_method_idx, WrapPointer(const_cast<DexFile*>(target_dex_file)), type); AppendLIR(move); @@ -923,7 +987,8 @@ void X86Mir2Lir::LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic uintptr_t ptr = reinterpret_cast<uintptr_t>(&id); // Generate the move instruction with the unique pointer and save index and type. - LIR *move = RawLIR(current_dalvik_offset_, kX86Mov32RI, TargetReg(symbolic_reg, false).GetReg(), + LIR *move = RawLIR(current_dalvik_offset_, kX86Mov32RI, + TargetReg(symbolic_reg, kNotWide).GetReg(), static_cast<int>(ptr), type_idx); AppendLIR(move); class_type_address_insns_.Insert(move); @@ -1189,6 +1254,7 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { // Is the string non-NULL? LoadValueDirectFixed(rl_obj, rs_rDX); GenNullCheck(rs_rDX, info->opt_flags); + // uint32_t opt_flags = info->opt_flags; info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've null checked. // Does the character fit in 16 bits? @@ -1215,12 +1281,20 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { // Character is in EAX. // Object pointer is in EDX. + // Compute the number of words to search in to rCX. + Load32Disp(rs_rDX, count_offset, rs_rCX); + + // Possible signal here due to null pointer dereference. + // Note that the signal handler will expect the top word of + // the stack to be the ArtMethod*. If the PUSH edi instruction + // below is ahead of the load above then this will not be true + // and the signal handler will not work. + MarkPossibleNullPointerException(0); + // We need to preserve EDI, but have no spare registers, so push it on the stack. // We have to remember that all stack addresses after this are offset by sizeof(EDI). NewLIR1(kX86Push32R, rs_rDI.GetReg()); - // Compute the number of words to search in to rCX. - Load32Disp(rs_rDX, count_offset, rs_rCX); LIR *length_compare = nullptr; int start_value = 0; bool is_index_on_stack = false; @@ -1259,7 +1333,8 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { } } else { // Load the start index from stack, remembering that we pushed EDI. - int displacement = SRegOffset(rl_start.s_reg_low) + (cu_->target64 ? 2 : 1) * sizeof(uint32_t); + int displacement = SRegOffset(rl_start.s_reg_low) + + (cu_->target64 ? 2 : 1) * sizeof(uint32_t); { ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg); Load32Disp(rs_rX86_SP, displacement, tmpReg); @@ -2179,21 +2254,24 @@ LIR *X86Mir2Lir::AddVectorLiteral(MIR *mir) { } // ------------ ABI support: mapping of args to physical registers ------------- -RegStorage X86Mir2Lir::InToRegStorageX86_64Mapper::GetNextReg(bool is_double_or_float, bool is_wide, bool is_ref) { +RegStorage X86Mir2Lir::InToRegStorageX86_64Mapper::GetNextReg(bool is_double_or_float, bool is_wide, + bool is_ref) { const SpecialTargetRegister coreArgMappingToPhysicalReg[] = {kArg1, kArg2, kArg3, kArg4, kArg5}; - const int coreArgMappingToPhysicalRegSize = sizeof(coreArgMappingToPhysicalReg) / sizeof(SpecialTargetRegister); + const int coreArgMappingToPhysicalRegSize = sizeof(coreArgMappingToPhysicalReg) / + sizeof(SpecialTargetRegister); const SpecialTargetRegister fpArgMappingToPhysicalReg[] = {kFArg0, kFArg1, kFArg2, kFArg3, - kFArg4, kFArg5, kFArg6, kFArg7}; - const int fpArgMappingToPhysicalRegSize = sizeof(fpArgMappingToPhysicalReg) / sizeof(SpecialTargetRegister); + kFArg4, kFArg5, kFArg6, kFArg7}; + const int fpArgMappingToPhysicalRegSize = sizeof(fpArgMappingToPhysicalReg) / + sizeof(SpecialTargetRegister); if (is_double_or_float) { if (cur_fp_reg_ < fpArgMappingToPhysicalRegSize) { - return ml_->TargetReg(fpArgMappingToPhysicalReg[cur_fp_reg_++], is_wide); + return ml_->TargetReg(fpArgMappingToPhysicalReg[cur_fp_reg_++], is_wide ? kWide : kNotWide); } } else { if (cur_core_reg_ < coreArgMappingToPhysicalRegSize) { - return is_ref ? ml_->TargetRefReg(coreArgMappingToPhysicalReg[cur_core_reg_++]) : - ml_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++], is_wide); + return ml_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++], + is_ref ? kRef : (is_wide ? kWide : kNotWide)); } } return RegStorage::InvalidReg(); @@ -2205,7 +2283,8 @@ RegStorage X86Mir2Lir::InToRegStorageMapping::Get(int in_position) { return res != mapping_.end() ? res->second : RegStorage::InvalidReg(); } -void X86Mir2Lir::InToRegStorageMapping::Initialize(RegLocation* arg_locs, int count, InToRegStorageMapper* mapper) { +void X86Mir2Lir::InToRegStorageMapping::Initialize(RegLocation* arg_locs, int count, + InToRegStorageMapper* mapper) { DCHECK(mapper != nullptr); max_mapped_in_ = -1; is_there_stack_mapped_ = false; @@ -2276,13 +2355,13 @@ void X86Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { RegLocation rl_src = rl_method; rl_src.location = kLocPhysReg; - rl_src.reg = TargetRefReg(kArg0); + rl_src.reg = TargetReg(kArg0, kRef); rl_src.home = false; MarkLive(rl_src); StoreValue(rl_method, rl_src); // If Method* has been promoted, explicitly flush if (rl_method.location == kLocPhysReg) { - StoreRefDisp(rs_rX86_SP, 0, As32BitReg(TargetRefReg(kArg0)), kNotVolatile); + StoreRefDisp(rs_rX86_SP, 0, As32BitReg(TargetReg(kArg0, kRef)), kNotVolatile); } if (cu_->num_ins == 0) { @@ -2440,7 +2519,8 @@ int X86Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, // The rest can be copied together int start_offset = SRegOffset(info->args[last_mapped_in + size_of_the_last_mapped].s_reg_low); - int outs_offset = StackVisitor::GetOutVROffset(last_mapped_in + size_of_the_last_mapped, cu_->instruction_set); + int outs_offset = StackVisitor::GetOutVROffset(last_mapped_in + size_of_the_last_mapped, + cu_->instruction_set); int current_src_offset = start_offset; int current_dest_offset = outs_offset; @@ -2536,7 +2616,7 @@ int X86Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, // Instead of allocating a new temp, simply reuse one of the registers being used // for argument passing. - RegStorage temp = TargetReg(kArg3, false); + RegStorage temp = TargetReg(kArg3, kNotWide); // Now load the argument VR and store to the outs. Load32Disp(rs_rX86_SP, current_src_offset, temp); @@ -2552,8 +2632,8 @@ int X86Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, // Now handle rest not registers if they are if (in_to_reg_storage_mapping.IsThereStackMapped()) { - RegStorage regSingle = TargetReg(kArg2, false); - RegStorage regWide = TargetReg(kArg3, true); + RegStorage regSingle = TargetReg(kArg2, kNotWide); + RegStorage regWide = TargetReg(kArg3, kWide); for (int i = start_index; i < last_mapped_in + size_of_the_last_mapped + regs_left_to_pass_via_stack; i++) { RegLocation rl_arg = info->args[i]; @@ -2611,14 +2691,14 @@ int X86Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); if (pcrLabel) { - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { - *pcrLabel = GenExplicitNullCheck(TargetRefReg(kArg1), info->opt_flags); + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags); } else { *pcrLabel = nullptr; // In lieu of generating a check for kArg1 being null, we need to // perform a load when doing implicit checks. RegStorage tmp = AllocTemp(); - Load32Disp(TargetRefReg(kArg1), 0, tmp); + Load32Disp(TargetReg(kArg1, kRef), 0, tmp); MarkPossibleNullPointerException(info->opt_flags); FreeTemp(tmp); } diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc index 657160ffd1..047a65d585 100644 --- a/compiler/dex/quick/x86/utility_x86.cc +++ b/compiler/dex/quick/x86/utility_x86.cc @@ -684,9 +684,9 @@ LIR* X86Mir2Lir::LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int } else { DCHECK(!r_dest.IsFloat()); // Make sure we're not still using a pair here. if (r_base == r_dest.GetLow()) { - load2 = NewLIR3(opcode, r_dest.GetHighReg(), r_base.GetReg(), + load = NewLIR3(opcode, r_dest.GetHighReg(), r_base.GetReg(), displacement + HIWORD_OFFSET); - load = NewLIR3(opcode, r_dest.GetLowReg(), r_base.GetReg(), displacement + LOWORD_OFFSET); + load2 = NewLIR3(opcode, r_dest.GetLowReg(), r_base.GetReg(), displacement + LOWORD_OFFSET); } else { load = NewLIR3(opcode, r_dest.GetLowReg(), r_base.GetReg(), displacement + LOWORD_OFFSET); load2 = NewLIR3(opcode, r_dest.GetHighReg(), r_base.GetReg(), @@ -712,16 +712,16 @@ LIR* X86Mir2Lir::LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int if (r_dest.GetHigh() == r_index) { // We can't use either register for the first load. RegStorage temp = AllocTemp(); - load2 = NewLIR5(opcode, temp.GetReg(), r_base.GetReg(), r_index.GetReg(), scale, + load = NewLIR5(opcode, temp.GetReg(), r_base.GetReg(), r_index.GetReg(), scale, displacement + HIWORD_OFFSET); - load = NewLIR5(opcode, r_dest.GetLowReg(), r_base.GetReg(), r_index.GetReg(), scale, + load2 = NewLIR5(opcode, r_dest.GetLowReg(), r_base.GetReg(), r_index.GetReg(), scale, displacement + LOWORD_OFFSET); OpRegCopy(r_dest.GetHigh(), temp); FreeTemp(temp); } else { - load2 = NewLIR5(opcode, r_dest.GetHighReg(), r_base.GetReg(), r_index.GetReg(), scale, + load = NewLIR5(opcode, r_dest.GetHighReg(), r_base.GetReg(), r_index.GetReg(), scale, displacement + HIWORD_OFFSET); - load = NewLIR5(opcode, r_dest.GetLowReg(), r_base.GetReg(), r_index.GetReg(), scale, + load2 = NewLIR5(opcode, r_dest.GetLowReg(), r_base.GetReg(), r_index.GetReg(), scale, displacement + LOWORD_OFFSET); } } else { @@ -744,6 +744,7 @@ LIR* X86Mir2Lir::LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int } } + // Always return first load generated as this might cause a fault if base is nullptr. return load; } @@ -762,10 +763,7 @@ LIR* X86Mir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_ size); if (UNLIKELY(is_volatile == kVolatile)) { - // Without context sensitive analysis, we must issue the most conservative barriers. - // In this case, either a load or store may follow so we issue both barriers. - GenMemBarrier(kLoadLoad); - GenMemBarrier(kLoadStore); + GenMemBarrier(kLoadAny); // Only a scheduling barrier. } return load; @@ -863,8 +861,7 @@ LIR* X86Mir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor LIR* X86Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size, VolatileKind is_volatile) { if (UNLIKELY(is_volatile == kVolatile)) { - // There might have been a store before this volatile one so insert StoreStore barrier. - GenMemBarrier(kStoreStore); + GenMemBarrier(kAnyStore); // Only a scheduling barrier. } // StoreBaseDisp() will emit correct insn for atomic store on x86 @@ -873,17 +870,21 @@ LIR* X86Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r LIR* store = StoreBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, r_src, size); if (UNLIKELY(is_volatile == kVolatile)) { - // A load might follow the volatile store so insert a StoreLoad barrier. - GenMemBarrier(kStoreLoad); + // A volatile load might follow the volatile store so insert a StoreLoad barrier. + // This does require a fence, even on x86. + GenMemBarrier(kAnyAny); } return store; } LIR* X86Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, - int offset, int check_value, LIR* target) { - NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base_reg.GetReg(), offset, - check_value); + int offset, int check_value, LIR* target, LIR** compare) { + LIR* inst = NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base_reg.GetReg(), + offset, check_value); + if (compare != nullptr) { + *compare = inst; + } LIR* branch = OpCondBranch(cond, target); return branch; } @@ -917,7 +918,7 @@ void X86Mir2Lir::AnalyzeBB(BasicBlock * bb) { for (MIR *mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { int opcode = mir->dalvikInsn.opcode; - if (MIRGraph::IsPseudoMirOp(opcode)) { + if (MIR::DecodedInstruction::IsPseudoMirOp(opcode)) { AnalyzeExtendedMIR(opcode, bb, mir); } else { AnalyzeMIR(opcode, bb, mir); diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index 2789923bb9..17f9b916d4 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -66,7 +66,9 @@ namespace art { * XMM6: caller | caller, arg7 | caller, scratch | caller, arg7, scratch * XMM7: caller | caller, arg8 | caller, scratch | caller, arg8, scratch * --- x86-64/x32 registers - * XMM8 .. 15: caller save available as scratch registers for ART. + * XMM8 .. 11: caller save available as scratch registers for ART. + * XMM12 .. 15: callee save available as promoted registers for ART. + * This change (XMM12..15) is for QCG only, for others they are caller save. * * X87 is a necessary evil outside of ART code for x86: * ST0: x86 float/double native return value, caller save @@ -497,6 +499,7 @@ enum X86OpCode { UnaryOpcode(kX86Test, RI, MI, AI), kX86Test32RR, kX86Test64RR, + kX86Test32RM, UnaryOpcode(kX86Not, R, M, A), UnaryOpcode(kX86Neg, R, M, A), UnaryOpcode(kX86Mul, DaR, DaM, DaA), diff --git a/compiler/dex/quick_compiler_callbacks.cc b/compiler/dex/quick_compiler_callbacks.cc new file mode 100644 index 0000000000..03bda78498 --- /dev/null +++ b/compiler/dex/quick_compiler_callbacks.cc @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "quick_compiler_callbacks.h" + +#include "quick/dex_file_to_method_inliner_map.h" +#include "verifier/method_verifier-inl.h" +#include "verification_results.h" + +namespace art { + +bool QuickCompilerCallbacks::MethodVerified(verifier::MethodVerifier* verifier) { + bool result = verification_results_->ProcessVerifiedMethod(verifier); + if (result) { + MethodReference ref = verifier->GetMethodReference(); + method_inliner_map_->GetMethodInliner(ref.dex_file) + ->AnalyseMethodCode(verifier); + } + return result; +} + +void QuickCompilerCallbacks::ClassRejected(ClassReference ref) { + verification_results_->AddRejectedClass(ref); +} + +} // namespace art diff --git a/compiler/driver/compiler_callbacks_impl.h b/compiler/dex/quick_compiler_callbacks.h index 92adb20c1f..7c9614f73a 100644 --- a/compiler/driver/compiler_callbacks_impl.h +++ b/compiler/dex/quick_compiler_callbacks.h @@ -14,48 +14,38 @@ * limitations under the License. */ -#ifndef ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_ -#define ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_ +#ifndef ART_COMPILER_DEX_QUICK_COMPILER_CALLBACKS_H_ +#define ART_COMPILER_DEX_QUICK_COMPILER_CALLBACKS_H_ #include "compiler_callbacks.h" -#include "dex/quick/dex_file_to_method_inliner_map.h" -#include "verifier/method_verifier-inl.h" namespace art { -class CompilerCallbacksImpl FINAL : public CompilerCallbacks { +class VerificationResults; +class DexFileToMethodInlinerMap; + +class QuickCompilerCallbacks FINAL : public CompilerCallbacks { public: - CompilerCallbacksImpl(VerificationResults* verification_results, - DexFileToMethodInlinerMap* method_inliner_map) + QuickCompilerCallbacks(VerificationResults* verification_results, + DexFileToMethodInlinerMap* method_inliner_map) : verification_results_(verification_results), method_inliner_map_(method_inliner_map) { CHECK(verification_results != nullptr); CHECK(method_inliner_map != nullptr); } - ~CompilerCallbacksImpl() { } + ~QuickCompilerCallbacks() { } bool MethodVerified(verifier::MethodVerifier* verifier) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE; - void ClassRejected(ClassReference ref) OVERRIDE { - verification_results_->AddRejectedClass(ref); - } + + void ClassRejected(ClassReference ref) OVERRIDE; private: VerificationResults* const verification_results_; DexFileToMethodInlinerMap* const method_inliner_map_; }; -inline bool CompilerCallbacksImpl::MethodVerified(verifier::MethodVerifier* verifier) { - bool result = verification_results_->ProcessVerifiedMethod(verifier); - if (result) { - MethodReference ref = verifier->GetMethodReference(); - method_inliner_map_->GetMethodInliner(ref.dex_file) - ->AnalyseMethodCode(verifier); - } - return result; -} - } // namespace art -#endif // ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_ +#endif // ART_COMPILER_DEX_QUICK_COMPILER_CALLBACKS_H_ diff --git a/compiler/dex/reg_storage.h b/compiler/dex/reg_storage.h index 8ed3adc1f1..addd6283a0 100644 --- a/compiler/dex/reg_storage.h +++ b/compiler/dex/reg_storage.h @@ -18,6 +18,7 @@ #define ART_COMPILER_DEX_REG_STORAGE_H_ #include "base/logging.h" +#include "compiler_enums.h" // For WideKind namespace art { @@ -149,6 +150,10 @@ class RegStorage { return ((reg_ & k64BitMask) == k64Bits); } + constexpr WideKind GetWideKind() const { + return Is64Bit() ? kWide : kNotWide; + } + constexpr bool Is64BitSolo() const { return ((reg_ & kShapeMask) == k64BitSolo); } diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc index db383c4d0b..892b30284f 100644 --- a/compiler/dex/vreg_analysis.cc +++ b/compiler/dex/vreg_analysis.cc @@ -251,7 +251,8 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb, MIR* mir, bool changed) { // Special-case handling for format 35c/3rc invokes Instruction::Code opcode = mir->dalvikInsn.opcode; - int flags = IsPseudoMirOp(opcode) ? 0 : Instruction::FlagsOf(mir->dalvikInsn.opcode); + int flags = MIR::DecodedInstruction::IsPseudoMirOp(opcode) ? + 0 : Instruction::FlagsOf(mir->dalvikInsn.opcode); if ((flags & Instruction::kInvoke) && (attrs & (DF_FORMAT_35C | DF_FORMAT_3RC))) { DCHECK_EQ(next, 0); diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 324f7172a6..89295f2786 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -18,13 +18,12 @@ #define ART_COMPILER_DRIVER_COMPILER_DRIVER_INL_H_ #include "compiler_driver.h" + #include "dex/compiler_ir.h" -#include "mirror/art_field.h" +#include "field_helper.h" #include "mirror/art_field-inl.h" -#include "mirror/art_method.h" #include "mirror/art_method-inl.h" #include "mirror/class_loader.h" -#include "mirror/dex_cache.h" #include "mirror/dex_cache-inl.h" #include "mirror/art_field-inl.h" #include "scoped_thread_state_change.h" @@ -42,7 +41,7 @@ inline mirror::ClassLoader* CompilerDriver::GetClassLoader(ScopedObjectAccess& s } inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass( - ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, + const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); DCHECK_EQ(class_loader.Get(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); @@ -59,7 +58,7 @@ inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass( } inline mirror::ArtField* CompilerDriver::ResolveField( - ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, + const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit, uint32_t field_idx, bool is_static) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); @@ -95,14 +94,13 @@ inline bool CompilerDriver::IsFieldVolatile(mirror::ArtField* field) { inline std::pair<bool, bool> CompilerDriver::IsFastInstanceField( mirror::DexCache* dex_cache, mirror::Class* referrer_class, - mirror::ArtField* resolved_field, uint16_t field_idx, MemberOffset* field_offset) { + mirror::ArtField* resolved_field, uint16_t field_idx) { DCHECK(!resolved_field->IsStatic()); mirror::Class* fields_class = resolved_field->GetDeclaringClass(); bool fast_get = referrer_class != nullptr && referrer_class->CanAccessResolvedField(fields_class, resolved_field, dex_cache, field_idx); bool fast_put = fast_get && (!resolved_field->IsFinal() || fields_class == referrer_class); - *field_offset = fast_get ? resolved_field->GetOffset() : MemberOffset(0u); return std::make_pair(fast_get, fast_put); } diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 9bf51359cf..9e88c8d875 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -25,6 +25,7 @@ #include "base/stl_util.h" #include "base/timing_logger.h" #include "class_linker.h" +#include "compiled_class.h" #include "compiler.h" #include "compiler_driver-inl.h" #include "dex_compilation_unit.h" @@ -34,7 +35,7 @@ #include "dex/quick/dex_file_method_inliner.h" #include "driver/compiler_options.h" #include "jni_internal.h" -#include "object_utils.h" +#include "object_lock.h" #include "profiler.h" #include "runtime.h" #include "gc/accounting/card_table-inl.h" @@ -989,10 +990,10 @@ void CompilerDriver::ProcessedInvoke(InvokeType invoke_type, int flags) { stats_->ProcessedInvoke(invoke_type, flags); } -bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, - bool is_put, MemberOffset* field_offset, - bool* is_volatile) { - ScopedObjectAccess soa(Thread::Current()); +mirror::ArtField* CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, + const DexCompilationUnit* mUnit, + bool is_put, + const ScopedObjectAccess& soa) { // Try to resolve the field and compiling method's class. mirror::ArtField* resolved_field; mirror::Class* referrer_class; @@ -1010,20 +1011,34 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi resolved_field = resolved_field_handle.Get(); dex_cache = dex_cache_handle.Get(); } - bool result = false; + bool can_link = false; if (resolved_field != nullptr && referrer_class != nullptr) { - *is_volatile = IsFieldVolatile(resolved_field); std::pair<bool, bool> fast_path = IsFastInstanceField( - dex_cache, referrer_class, resolved_field, field_idx, field_offset); - result = is_put ? fast_path.second : fast_path.first; + dex_cache, referrer_class, resolved_field, field_idx); + can_link = is_put ? fast_path.second : fast_path.first; } - if (!result) { + ProcessedInstanceField(can_link); + return can_link ? resolved_field : nullptr; +} + +bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, + bool is_put, MemberOffset* field_offset, + bool* is_volatile) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ArtField> resolved_field = + hs.NewHandle(ComputeInstanceFieldInfo(field_idx, mUnit, is_put, soa)); + + if (resolved_field.Get() == nullptr) { // Conservative defaults. *is_volatile = true; *field_offset = MemberOffset(static_cast<size_t>(-1)); + return false; + } else { + *is_volatile = resolved_field->IsVolatile(); + *field_offset = resolved_field->GetOffset(); + return true; } - ProcessedInstanceField(result); - return result; } bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 9903421338..6dae398372 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -24,7 +24,6 @@ #include "base/mutex.h" #include "base/timing_logger.h" #include "class_reference.h" -#include "compiled_class.h" #include "compiled_method.h" #include "compiler.h" #include "dex_file.h" @@ -32,6 +31,7 @@ #include "instruction_set.h" #include "invoke_type.h" #include "method_reference.h" +#include "mirror/class.h" // For mirror::Class::Status. #include "os.h" #include "profiler.h" #include "runtime.h" @@ -46,6 +46,7 @@ namespace verifier { class MethodVerifier; } // namespace verifier +class CompiledClass; class CompilerOptions; class DexCompilationUnit; class DexFileToMethodInlinerMap; @@ -221,14 +222,14 @@ class CompilerDriver { // Resolve compiling method's class. Returns nullptr on failure. mirror::Class* ResolveCompilingMethodsClass( - ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, + const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Resolve a field. Returns nullptr on failure, including incompatible class change. // NOTE: Unlike ClassLinker's ResolveField(), this method enforces is_static. mirror::ArtField* ResolveField( - ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, + const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit, uint32_t field_idx, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -244,7 +245,7 @@ class CompilerDriver { // Can we fast-path an IGET/IPUT access to an instance field? If yes, compute the field offset. std::pair<bool, bool> IsFastInstanceField( mirror::DexCache* dex_cache, mirror::Class* referrer_class, - mirror::ArtField* resolved_field, uint16_t field_idx, MemberOffset* field_offset) + mirror::ArtField* resolved_field, uint16_t field_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Can we fast-path an SGET/SPUT access to a static field? If yes, compute the field offset, @@ -297,6 +298,13 @@ class CompilerDriver { MemberOffset* field_offset, bool* is_volatile) LOCKS_EXCLUDED(Locks::mutator_lock_); + mirror::ArtField* ComputeInstanceFieldInfo(uint32_t field_idx, + const DexCompilationUnit* mUnit, + bool is_put, + const ScopedObjectAccess& soa) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Can we fastpath static field access? Computes field's offset, volatility and whether the // field is within the referrer (which can avoid checking class initialization). bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put, diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 5325a68b37..9ae9bd400a 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -25,12 +25,13 @@ #include "dex_file.h" #include "gc/heap.h" #include "mirror/art_method-inl.h" -#include "mirror/class.h" #include "mirror/class-inl.h" +#include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" #include "handle_scope-inl.h" +#include "scoped_thread_state_change.h" namespace art { diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 92b2feeb7f..c0f91d1646 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -56,9 +56,9 @@ class CompilerOptions { include_patch_information_(kDefaultIncludePatchInformation), top_k_profile_threshold_(kDefaultTopKProfileThreshold), include_debug_symbols_(kDefaultIncludeDebugSymbols), - explicit_null_checks_(true), - explicit_so_checks_(true), - explicit_suspend_checks_(true) + implicit_null_checks_(false), + implicit_so_checks_(false), + implicit_suspend_checks_(false) #ifdef ART_SEA_IR_MODE , sea_ir_mode_(false) #endif @@ -74,9 +74,9 @@ class CompilerOptions { bool include_patch_information, double top_k_profile_threshold, bool include_debug_symbols, - bool explicit_null_checks, - bool explicit_so_checks, - bool explicit_suspend_checks + bool implicit_null_checks, + bool implicit_so_checks, + bool implicit_suspend_checks #ifdef ART_SEA_IR_MODE , bool sea_ir_mode #endif @@ -91,9 +91,9 @@ class CompilerOptions { include_patch_information_(include_patch_information), top_k_profile_threshold_(top_k_profile_threshold), include_debug_symbols_(include_debug_symbols), - explicit_null_checks_(explicit_null_checks), - explicit_so_checks_(explicit_so_checks), - explicit_suspend_checks_(explicit_suspend_checks) + implicit_null_checks_(implicit_null_checks), + implicit_so_checks_(implicit_so_checks), + implicit_suspend_checks_(implicit_suspend_checks) #ifdef ART_SEA_IR_MODE , sea_ir_mode_(sea_ir_mode) #endif @@ -160,28 +160,28 @@ class CompilerOptions { return include_debug_symbols_; } - bool GetExplicitNullChecks() const { - return explicit_null_checks_; + bool GetImplicitNullChecks() const { + return implicit_null_checks_; } - void SetExplicitNullChecks(bool new_val) { - explicit_null_checks_ = new_val; + void SetImplicitNullChecks(bool new_val) { + implicit_null_checks_ = new_val; } - bool GetExplicitStackOverflowChecks() const { - return explicit_so_checks_; + bool GetImplicitStackOverflowChecks() const { + return implicit_so_checks_; } - void SetExplicitStackOverflowChecks(bool new_val) { - explicit_so_checks_ = new_val; + void SetImplicitStackOverflowChecks(bool new_val) { + implicit_so_checks_ = new_val; } - bool GetExplicitSuspendChecks() const { - return explicit_suspend_checks_; + bool GetImplicitSuspendChecks() const { + return implicit_suspend_checks_; } - void SetExplicitSuspendChecks(bool new_val) { - explicit_suspend_checks_ = new_val; + void SetImplicitSuspendChecks(bool new_val) { + implicit_suspend_checks_ = new_val; } #ifdef ART_SEA_IR_MODE @@ -208,9 +208,9 @@ class CompilerOptions { // When using a profile file only the top K% of the profiled samples will be compiled. double top_k_profile_threshold_; bool include_debug_symbols_; - bool explicit_null_checks_; - bool explicit_so_checks_; - bool explicit_suspend_checks_; + bool implicit_null_checks_; + bool implicit_so_checks_; + bool implicit_suspend_checks_; #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 e637cfbe1d..e479322238 100644 --- a/compiler/elf_writer_test.cc +++ b/compiler/elf_writer_test.cc @@ -16,8 +16,10 @@ #include "elf_file.h" +#include "base/stringprintf.h" #include "common_compiler_test.h" #include "oat.h" +#include "utils.h" namespace art { diff --git a/compiler/image_test.cc b/compiler/image_test.cc index d52ec0ad5a..fe4fcd4177 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -20,13 +20,15 @@ #include <string> #include <vector> +#include "base/unix_file/fd_file.h" #include "common_compiler_test.h" -#include "compiler/elf_fixup.h" -#include "compiler/image_writer.h" -#include "compiler/oat_writer.h" +#include "elf_fixup.h" #include "gc/space/image_space.h" +#include "image_writer.h" #include "lock_word.h" #include "mirror/object-inl.h" +#include "oat_writer.h" +#include "scoped_thread_state_change.h" #include "signal_catcher.h" #include "utils.h" #include "vector_output_stream.h" @@ -77,8 +79,9 @@ TEST_F(ImageTest, WriteRead) { t.NewTiming("WriteElf"); ScopedObjectAccess soa(Thread::Current()); - OatWriter oat_writer(class_linker->GetBootClassPath(), - 0, 0, "", compiler_driver_.get(), &timings); + SafeMap<std::string, std::string> key_value_store; + OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, compiler_driver_.get(), &timings, + &key_value_store); bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(), !kIsTargetBuild, class_linker->GetBootClassPath(), @@ -134,7 +137,7 @@ TEST_F(ImageTest, WriteRead) { // Remove the reservation of the memory for use to load the image. UnreserveImageSpace(); - Runtime::Options options; + RuntimeOptions options; std::string image("-Ximage:"); image.append(image_location.GetFilename()); options.push_back(std::make_pair(image.c_str(), reinterpret_cast<void*>(NULL))); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index acfa607f39..8ef2964270 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -51,7 +51,6 @@ #include "mirror/string-inl.h" #include "oat.h" #include "oat_file.h" -#include "object_utils.h" #include "runtime.h" #include "scoped_thread_state_change.h" #include "handle_scope-inl.h" @@ -630,11 +629,33 @@ class FixupVisitor { mirror::Reference::ReferentOffset(), image_writer_->GetImageAddress(ref->GetReferent())); } - private: + protected: ImageWriter* const image_writer_; mirror::Object* const copy_; }; +class FixupClassVisitor FINAL : public FixupVisitor { + public: + FixupClassVisitor(ImageWriter* image_writer, Object* copy) : FixupVisitor(image_writer, copy) { + } + + void operator()(Object* obj, MemberOffset offset, bool /*is_static*/) const + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { + DCHECK(obj->IsClass()); + FixupVisitor::operator()(obj, offset, false); + + if (offset.Uint32Value() < mirror::Class::EmbeddedVTableOffset().Uint32Value()) { + return; + } + } + + void operator()(mirror::Class* /*klass*/, mirror::Reference* ref) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + LOG(FATAL) << "Reference not expected here."; + } +}; + void ImageWriter::FixupObject(Object* orig, Object* copy) { DCHECK(orig != nullptr); DCHECK(copy != nullptr); @@ -646,13 +667,68 @@ void ImageWriter::FixupObject(Object* orig, Object* copy) { DCHECK_EQ(copy->GetReadBarrierPointer(), GetImageAddress(orig)); } } - FixupVisitor visitor(this, copy); - orig->VisitReferences<true /*visit class*/>(visitor, visitor); + if (orig->IsClass() && orig->AsClass()->ShouldHaveEmbeddedImtAndVTable()) { + FixupClassVisitor visitor(this, copy); + orig->VisitReferences<true /*visit class*/>(visitor, visitor); + } else { + FixupVisitor visitor(this, copy); + orig->VisitReferences<true /*visit class*/>(visitor, visitor); + } if (orig->IsArtMethod<kVerifyNone>()) { FixupMethod(orig->AsArtMethod<kVerifyNone>(), down_cast<ArtMethod*>(copy)); } } +const byte* ImageWriter::GetQuickCode(mirror::ArtMethod* method, bool* quick_is_interpreted) { + DCHECK(!method->IsResolutionMethod() && !method->IsImtConflictMethod() && + !method->IsAbstract()) << PrettyMethod(method); + + // Use original code if it exists. Otherwise, set the code pointer to the resolution + // trampoline. + + // Quick entrypoint: + const byte* quick_code = GetOatAddress(method->GetQuickOatCodeOffset()); + *quick_is_interpreted = false; + if (quick_code != nullptr && + (!method->IsStatic() || method->IsConstructor() || method->GetDeclaringClass()->IsInitialized())) { + // We have code for a non-static or initialized method, just use the code. + } else if (quick_code == nullptr && method->IsNative() && + (!method->IsStatic() || method->GetDeclaringClass()->IsInitialized())) { + // Non-static or initialized native method missing compiled code, use generic JNI version. + quick_code = GetOatAddress(quick_generic_jni_trampoline_offset_); + } else if (quick_code == nullptr && !method->IsNative()) { + // We don't have code at all for a non-native method, use the interpreter. + quick_code = GetOatAddress(quick_to_interpreter_bridge_offset_); + *quick_is_interpreted = true; + } else { + CHECK(!method->GetDeclaringClass()->IsInitialized()); + // We have code for a static method, but need to go through the resolution stub for class + // initialization. + quick_code = GetOatAddress(quick_resolution_trampoline_offset_); + } + return quick_code; +} + +const byte* ImageWriter::GetQuickEntryPoint(mirror::ArtMethod* method) { + // Calculate the quick entry point following the same logic as FixupMethod() below. + // The resolution method has a special trampoline to call. + if (UNLIKELY(method == Runtime::Current()->GetResolutionMethod())) { + return GetOatAddress(quick_resolution_trampoline_offset_); + } else if (UNLIKELY(method == Runtime::Current()->GetImtConflictMethod())) { + return GetOatAddress(quick_imt_conflict_trampoline_offset_); + } else { + // We assume all methods have code. If they don't currently then we set them to the use the + // resolution trampoline. Abstract methods never have code and so we need to make sure their + // use results in an AbstractMethodError. We use the interpreter to achieve this. + if (UNLIKELY(method->IsAbstract())) { + return GetOatAddress(quick_to_interpreter_bridge_offset_); + } else { + bool quick_is_interpreted; + return GetQuickCode(method, &quick_is_interpreted); + } + } +} + void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) { // OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to // oat_begin_ @@ -674,29 +750,8 @@ void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) { copy->SetEntryPointFromInterpreter<kVerifyNone>(reinterpret_cast<EntryPointFromInterpreter*> (const_cast<byte*>(GetOatAddress(interpreter_to_interpreter_bridge_offset_)))); } else { - // Use original code if it exists. Otherwise, set the code pointer to the resolution - // trampoline. - - // Quick entrypoint: - const byte* quick_code = GetOatAddress(orig->GetQuickOatCodeOffset()); - bool quick_is_interpreted = false; - if (quick_code != nullptr && - (!orig->IsStatic() || orig->IsConstructor() || orig->GetDeclaringClass()->IsInitialized())) { - // We have code for a non-static or initialized method, just use the code. - } else if (quick_code == nullptr && orig->IsNative() && - (!orig->IsStatic() || orig->GetDeclaringClass()->IsInitialized())) { - // Non-static or initialized native method missing compiled code, use generic JNI version. - quick_code = GetOatAddress(quick_generic_jni_trampoline_offset_); - } else if (quick_code == nullptr && !orig->IsNative()) { - // We don't have code at all for a non-native method, use the interpreter. - quick_code = GetOatAddress(quick_to_interpreter_bridge_offset_); - quick_is_interpreted = true; - } else { - CHECK(!orig->GetDeclaringClass()->IsInitialized()); - // We have code for a static method, but need to go through the resolution stub for class - // initialization. - quick_code = GetOatAddress(quick_resolution_trampoline_offset_); - } + bool quick_is_interpreted; + const byte* quick_code = GetQuickCode(orig, &quick_is_interpreted); copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(quick_code); // Portable entrypoint: diff --git a/compiler/image_writer.h b/compiler/image_writer.h index 2bcb41e3fe..cf5bc930d1 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -149,6 +149,13 @@ class ImageWriter { void FixupObject(mirror::Object* orig, mirror::Object* copy) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Get quick code for non-resolution/imt_conflict/abstract method. + const byte* GetQuickCode(mirror::ArtMethod* method, bool* quick_is_interpreted) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + const byte* GetQuickEntryPoint(mirror::ArtMethod* method) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Patches references in OatFile to expect runtime addresses. void PatchOatCodeAndMethods(File* elf_file) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -192,6 +199,7 @@ class ImageWriter { uint32_t quick_to_interpreter_bridge_offset_; friend class FixupVisitor; + friend class FixupClassVisitor; DISALLOW_COPY_AND_ASSIGN(ImageWriter); }; diff --git a/compiler/jni/quick/calling_convention.h b/compiler/jni/quick/calling_convention.h index efc0b42db4..6db0c3b8b3 100644 --- a/compiler/jni/quick/calling_convention.h +++ b/compiler/jni/quick/calling_convention.h @@ -19,6 +19,7 @@ #include <vector> #include "handle_scope.h" +#include "primitive.h" #include "thread.h" #include "utils/managed_register.h" diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 3bbb723bcf..dec84f1b5c 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -28,6 +28,7 @@ #include "driver/compiler_driver.h" #include "entrypoints/quick/quick_entrypoints.h" #include "jni_internal.h" +#include "mirror/art_method.h" #include "utils/assembler.h" #include "utils/managed_register.h" #include "utils/arm/managed_register_arm.h" diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc index 5febed24fe..525f05c522 100644 --- a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc +++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc @@ -130,6 +130,10 @@ X86_64JniCallingConvention::X86_64JniCallingConvention(bool is_static, bool is_s callee_save_regs_.push_back(X86_64ManagedRegister::FromCpuRegister(R13)); callee_save_regs_.push_back(X86_64ManagedRegister::FromCpuRegister(R14)); callee_save_regs_.push_back(X86_64ManagedRegister::FromCpuRegister(R15)); + callee_save_regs_.push_back(X86_64ManagedRegister::FromXmmRegister(XMM12)); + callee_save_regs_.push_back(X86_64ManagedRegister::FromXmmRegister(XMM13)); + callee_save_regs_.push_back(X86_64ManagedRegister::FromXmmRegister(XMM14)); + callee_save_regs_.push_back(X86_64ManagedRegister::FromXmmRegister(XMM15)); } uint32_t X86_64JniCallingConvention::CoreSpillMask() const { @@ -137,6 +141,10 @@ uint32_t X86_64JniCallingConvention::CoreSpillMask() const { 1 << kNumberOfCpuRegisters; } +uint32_t X86_64JniCallingConvention::FpSpillMask() const { + return 1 << XMM12 | 1 << XMM13 | 1 << XMM14 | 1 << XMM15; +} + size_t X86_64JniCallingConvention::FrameSize() { // Method*, return address and callee save area size, local reference segment state size_t frame_data_size = sizeof(StackReference<mirror::ArtMethod>) + diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.h b/compiler/jni/quick/x86_64/calling_convention_x86_64.h index 1ba5353289..7a90c6e94e 100644 --- a/compiler/jni/quick/x86_64/calling_convention_x86_64.h +++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.h @@ -61,9 +61,7 @@ class X86_64JniCallingConvention FINAL : public JniCallingConvention { } ManagedRegister ReturnScratchRegister() const OVERRIDE; uint32_t CoreSpillMask() const OVERRIDE; - uint32_t FpSpillMask() const OVERRIDE { - return 0; - } + uint32_t FpSpillMask() const OVERRIDE; bool IsCurrentParamInRegister() OVERRIDE; bool IsCurrentParamOnStack() OVERRIDE; ManagedRegister CurrentParamRegister() OVERRIDE; diff --git a/compiler/llvm/gbc_expander.cc b/compiler/llvm/gbc_expander.cc index f8dca66de0..902f8dd4f5 100644 --- a/compiler/llvm/gbc_expander.cc +++ b/compiler/llvm/gbc_expander.cc @@ -1648,7 +1648,7 @@ llvm::Value* GBCExpanderPass::Expand_HLIGet(llvm::CallInst& call_inst, field_value = SignOrZeroExtendCat1Types(field_value, field_jty); if (is_volatile) { - irb_.CreateMemoryBarrier(art::kLoadLoad); + irb_.CreateMemoryBarrier(art::kLoadAny); } } @@ -1702,7 +1702,7 @@ void GBCExpanderPass::Expand_HLIPut(llvm::CallInst& call_inst, DCHECK_GE(field_offset.Int32Value(), 0); if (is_volatile) { - irb_.CreateMemoryBarrier(art::kStoreStore); + irb_.CreateMemoryBarrier(art::kAnyStore); } llvm::PointerType* field_type = @@ -1717,7 +1717,7 @@ void GBCExpanderPass::Expand_HLIPut(llvm::CallInst& call_inst, irb_.CreateStore(new_value, field_addr, kTBAAHeapInstance, field_jty); if (is_volatile) { - irb_.CreateMemoryBarrier(art::kLoadLoad); + irb_.CreateMemoryBarrier(art::kAnyAny); } if (field_jty == kObject) { // If put an object, mark the GC card table. @@ -1870,7 +1870,7 @@ llvm::Value* GBCExpanderPass::EmitLoadStaticStorage(uint32_t dex_pc, phi->addIncoming(loaded_storage_object_addr, block_after_load_static); // Ensure load of status and load of value don't re-order. - irb_.CreateMemoryBarrier(art::kLoadLoad); + irb_.CreateMemoryBarrier(art::kLoadAny); return phi; } @@ -1948,7 +1948,7 @@ llvm::Value* GBCExpanderPass::Expand_HLSget(llvm::CallInst& call_inst, static_field_value = SignOrZeroExtendCat1Types(static_field_value, field_jty); if (is_volatile) { - irb_.CreateMemoryBarrier(art::kLoadLoad); + irb_.CreateMemoryBarrier(art::kLoadAny); } } @@ -2025,7 +2025,7 @@ void GBCExpanderPass::Expand_HLSput(llvm::CallInst& call_inst, } if (is_volatile) { - irb_.CreateMemoryBarrier(art::kStoreStore); + irb_.CreateMemoryBarrier(art::kAnyStore); } llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value()); @@ -2038,7 +2038,7 @@ void GBCExpanderPass::Expand_HLSput(llvm::CallInst& call_inst, irb_.CreateStore(new_value, static_field_addr, kTBAAHeapStatic, field_jty); if (is_volatile) { - irb_.CreateMemoryBarrier(art::kStoreLoad); + irb_.CreateMemoryBarrier(art::kAnyAny); } if (field_jty == kObject) { // If put an object, mark the GC card table. diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 254faac796..84f0b3c001 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -15,14 +15,18 @@ */ #include "common_compiler_test.h" -#include "compiler/compiler.h" -#include "compiler/oat_writer.h" +#include "compiler.h" +#include "dex/verification_results.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" +#include "dex/quick_compiler_callbacks.h" #include "entrypoints/quick/quick_entrypoints.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" -#include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "mirror/object-inl.h" #include "oat_file-inl.h" +#include "oat_writer.h" +#include "scoped_thread_state_change.h" #include "vector_output_stream.h" namespace art { @@ -94,8 +98,8 @@ TEST_F(OatTest, WriteRead) { compiler_options_.reset(new CompilerOptions); verification_results_.reset(new VerificationResults(compiler_options_.get())); method_inliner_map_.reset(new DexFileToMethodInlinerMap); - callbacks_.reset(new CompilerCallbacksImpl(verification_results_.get(), - method_inliner_map_.get())); + callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(), + method_inliner_map_.get())); timer_.reset(new CumulativeLogger("Compilation times")); compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), @@ -111,12 +115,14 @@ TEST_F(OatTest, WriteRead) { ScopedObjectAccess soa(Thread::Current()); ScratchFile tmp; + SafeMap<std::string, std::string> key_value_store; + key_value_store.Put(OatHeader::kImageLocationKey, "lue.art"); OatWriter oat_writer(class_linker->GetBootClassPath(), 42U, 4096U, - "lue.art", compiler_driver_.get(), - &timings); + &timings, + &key_value_store); bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(), !kIsTargetBuild, class_linker->GetBootClassPath(), @@ -136,7 +142,7 @@ TEST_F(OatTest, WriteRead) { ASSERT_EQ(1U, oat_header.GetDexFileCount()); // core ASSERT_EQ(42U, oat_header.GetImageFileLocationOatChecksum()); ASSERT_EQ(4096U, oat_header.GetImageFileLocationOatDataBegin()); - ASSERT_EQ("lue.art", oat_header.GetImageFileLocation()); + ASSERT_EQ("lue.art", std::string(oat_header.GetStoreValueByKey(OatHeader::kImageLocationKey))); const DexFile* dex_file = java_lang_dex_file_; uint32_t dex_file_checksum = dex_file->GetLocationChecksum(); @@ -180,7 +186,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(80U, sizeof(OatHeader)); EXPECT_EQ(8U, sizeof(OatMethodOffsets)); EXPECT_EQ(24U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(77 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); + EXPECT_EQ(79 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); } TEST_F(OatTest, OatHeaderIsValid) { @@ -189,20 +195,20 @@ TEST_F(OatTest, OatHeaderIsValid) { std::vector<const DexFile*> dex_files; uint32_t image_file_location_oat_checksum = 0; uint32_t image_file_location_oat_begin = 0; - const std::string image_file_location; - OatHeader oat_header(instruction_set, - instruction_set_features, - &dex_files, - image_file_location_oat_checksum, - image_file_location_oat_begin, - image_file_location); - ASSERT_TRUE(oat_header.IsValid()); - - char* magic = const_cast<char*>(oat_header.GetMagic()); + OatHeader* oat_header = OatHeader::Create(instruction_set, + instruction_set_features, + &dex_files, + image_file_location_oat_checksum, + image_file_location_oat_begin, + nullptr); + ASSERT_NE(oat_header, nullptr); + ASSERT_TRUE(oat_header->IsValid()); + + char* magic = const_cast<char*>(oat_header->GetMagic()); strcpy(magic, ""); // bad magic - ASSERT_FALSE(oat_header.IsValid()); + ASSERT_FALSE(oat_header->IsValid()); strcpy(magic, "oat\n000"); // bad version - ASSERT_FALSE(oat_header.IsValid()); + ASSERT_FALSE(oat_header->IsValid()); } } // namespace art diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 4b6d501724..63a3c8ccdc 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -22,6 +22,7 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" +#include "compiled_class.h" #include "dex_file-inl.h" #include "dex/verification_results.h" #include "gc/space/space.h" @@ -49,19 +50,19 @@ namespace art { OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, uint32_t image_file_location_oat_checksum, uintptr_t image_file_location_oat_begin, - const std::string& image_file_location, const CompilerDriver* compiler, - TimingLogger* timings) + TimingLogger* timings, + SafeMap<std::string, std::string>* key_value_store) : compiler_driver_(compiler), dex_files_(&dex_files), image_file_location_oat_checksum_(image_file_location_oat_checksum), image_file_location_oat_begin_(image_file_location_oat_begin), - image_file_location_(image_file_location), + key_value_store_(key_value_store), oat_header_(NULL), size_dex_file_alignment_(0), size_executable_offset_alignment_(0), size_oat_header_(0), - size_oat_header_image_file_location_(0), + size_oat_header_key_value_store_(0), size_dex_file_(0), size_interpreter_to_interpreter_bridge_(0), size_interpreter_to_compiled_code_bridge_(0), @@ -89,6 +90,8 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, size_oat_class_status_(0), size_oat_class_method_bitmaps_(0), size_oat_class_method_offsets_(0) { + CHECK(key_value_store != nullptr); + size_t offset; { TimingLogger::ScopedTiming split("InitOatHeader", timings); @@ -121,7 +124,8 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, size_ = offset; CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); - CHECK(image_file_location.empty() == compiler->IsImage()); + CHECK_EQ(compiler->IsImage(), + key_value_store_->find(OatHeader::kImageLocationKey) == key_value_store_->end()); } OatWriter::~OatWriter() { @@ -716,16 +720,14 @@ bool OatWriter::VisitDexMethods(DexMethodVisitor* visitor) { } size_t OatWriter::InitOatHeader() { - // create the OatHeader - oat_header_ = new OatHeader(compiler_driver_->GetInstructionSet(), - compiler_driver_->GetInstructionSetFeatures(), - dex_files_, - image_file_location_oat_checksum_, - image_file_location_oat_begin_, - image_file_location_); - size_t offset = sizeof(*oat_header_); - offset += image_file_location_.size(); - return offset; + oat_header_ = OatHeader::Create(compiler_driver_->GetInstructionSet(), + compiler_driver_->GetInstructionSetFeatures(), + dex_files_, + image_file_location_oat_checksum_, + image_file_location_oat_begin_, + key_value_store_); + + return oat_header_->GetHeaderSize(); } size_t OatWriter::InitOatDexFiles(size_t offset) { @@ -864,17 +866,13 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { bool OatWriter::Write(OutputStream* out) { const size_t file_offset = out->Seek(0, kSeekCurrent); - if (!out->WriteFully(oat_header_, sizeof(*oat_header_))) { + size_t header_size = oat_header_->GetHeaderSize(); + if (!out->WriteFully(oat_header_, header_size)) { PLOG(ERROR) << "Failed to write oat header to " << out->GetLocation(); return false; } - size_oat_header_ += sizeof(*oat_header_); - - if (!out->WriteFully(image_file_location_.data(), image_file_location_.size())) { - PLOG(ERROR) << "Failed to write oat header image file location to " << out->GetLocation(); - return false; - } - size_oat_header_image_file_location_ += image_file_location_.size(); + size_oat_header_ += sizeof(OatHeader); + size_oat_header_key_value_store_ += oat_header_->GetHeaderSize() - sizeof(OatHeader); if (!WriteTables(out, file_offset)) { LOG(ERROR) << "Failed to write oat tables to " << out->GetLocation(); @@ -909,7 +907,7 @@ bool OatWriter::Write(OutputStream* out) { DO_STAT(size_dex_file_alignment_); DO_STAT(size_executable_offset_alignment_); DO_STAT(size_oat_header_); - DO_STAT(size_oat_header_image_file_location_); + DO_STAT(size_oat_header_key_value_store_); DO_STAT(size_dex_file_); DO_STAT(size_interpreter_to_interpreter_bridge_); DO_STAT(size_interpreter_to_compiled_code_bridge_); diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index dbecb95362..3d34956651 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -79,9 +79,9 @@ class OatWriter { OatWriter(const std::vector<const DexFile*>& dex_files, uint32_t image_file_location_oat_checksum, uintptr_t image_file_location_oat_begin, - const std::string& image_file_location, const CompilerDriver* compiler, - TimingLogger* timings); + TimingLogger* timings, + SafeMap<std::string, std::string>* key_value_store); const OatHeader& GetOatHeader() const { return *oat_header_; @@ -253,9 +253,9 @@ class OatWriter { // dependencies on the image. uint32_t image_file_location_oat_checksum_; uintptr_t image_file_location_oat_begin_; - std::string image_file_location_; // data to write + SafeMap<std::string, std::string>* key_value_store_; OatHeader* oat_header_; std::vector<OatDexFile*> oat_dex_files_; std::vector<OatClass*> oat_classes_; @@ -274,7 +274,7 @@ class OatWriter { uint32_t size_dex_file_alignment_; uint32_t size_executable_offset_alignment_; uint32_t size_oat_header_; - uint32_t size_oat_header_image_file_location_; + uint32_t size_oat_header_key_value_store_; uint32_t size_dex_file_; uint32_t size_interpreter_to_interpreter_bridge_; uint32_t size_interpreter_to_compiled_code_bridge_; diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index cc995f72a1..f5941291e7 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -15,16 +15,29 @@ * limitations under the License. */ +#include "builder.h" + +#include "class_linker.h" #include "dex_file.h" #include "dex_file-inl.h" #include "dex_instruction.h" #include "dex_instruction-inl.h" -#include "builder.h" +#include "driver/compiler_driver-inl.h" +#include "mirror/art_field.h" +#include "mirror/art_field-inl.h" +#include "mirror/class_loader.h" +#include "mirror/dex_cache.h" #include "nodes.h" #include "primitive.h" +#include "scoped_thread_state_change.h" +#include "thread.h" namespace art { +static bool IsTypeSupported(Primitive::Type type) { + return type != Primitive::kPrimFloat && type != Primitive::kPrimDouble; +} + void HGraphBuilder::InitializeLocals(uint16_t count) { graph_->SetNumberOfVRegs(count); locals_.SetSize(count); @@ -93,7 +106,7 @@ static bool CanHandleCodeItem(const DexFile::CodeItem& code_item) { } template<typename T> -void HGraphBuilder::If_22t(const Instruction& instruction, int32_t dex_offset) { +void HGraphBuilder::If_22t(const Instruction& instruction, uint32_t dex_offset) { HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt); HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt); T* comparison = new (arena_) T(first, second); @@ -110,7 +123,7 @@ void HGraphBuilder::If_22t(const Instruction& instruction, int32_t dex_offset) { } template<typename T> -void HGraphBuilder::If_21t(const Instruction& instruction, int32_t dex_offset) { +void HGraphBuilder::If_21t(const Instruction& instruction, uint32_t dex_offset) { HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt); T* comparison = new (arena_) T(value, GetIntConstant(0)); current_block_->AddInstruction(comparison); @@ -305,28 +318,23 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, uint32_t argument_index = start_index; for (size_t i = start_index; i < number_of_vreg_arguments; i++, argument_index++) { Primitive::Type type = Primitive::GetType(descriptor[descriptor_index++]); - switch (type) { - case Primitive::kPrimFloat: - case Primitive::kPrimDouble: - return false; - - default: { - if (!is_range && type == Primitive::kPrimLong && args[i] + 1 != args[i + 1]) { - LOG(WARNING) << "Non sequential register pair in " << dex_compilation_unit_->GetSymbol() - << " at " << dex_offset; - // We do not implement non sequential register pair. - return false; - } - HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type); - invoke->SetArgumentAt(argument_index, arg); - if (type == Primitive::kPrimLong) { - i++; - } - } + if (!IsTypeSupported(type)) { + return false; + } + if (!is_range && type == Primitive::kPrimLong && args[i] + 1 != args[i + 1]) { + LOG(WARNING) << "Non sequential register pair in " << dex_compilation_unit_->GetSymbol() + << " at " << dex_offset; + // We do not implement non sequential register pair. + return false; + } + HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type); + invoke->SetArgumentAt(argument_index, arg); + if (type == Primitive::kPrimLong) { + i++; } } - if (return_type == Primitive::kPrimDouble || return_type == Primitive::kPrimFloat) { + if (!IsTypeSupported(return_type)) { return false; } @@ -335,6 +343,84 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, return true; } +/** + * Helper class to add HTemporary instructions. This class is used when + * converting a DEX instruction to multiple HInstruction, and where those + * instructions do not die at the following instruction, but instead spans + * multiple instructions. + */ +class Temporaries : public ValueObject { + public: + Temporaries(HGraph* graph, size_t count) : graph_(graph), count_(count), index_(0) { + graph_->UpdateNumberOfTemporaries(count_); + } + + void Add(HInstruction* instruction) { + // We currently only support vreg size temps. + DCHECK(instruction->GetType() != Primitive::kPrimLong + && instruction->GetType() != Primitive::kPrimDouble); + HInstruction* temp = new (graph_->GetArena()) HTemporary(index_++); + instruction->GetBlock()->AddInstruction(temp); + DCHECK(temp->GetPrevious() == instruction); + } + + private: + HGraph* const graph_; + + // The total number of temporaries that will be used. + const size_t count_; + + // Current index in the temporary stack, updated by `Add`. + size_t index_; +}; + +bool HGraphBuilder::BuildFieldAccess(const Instruction& instruction, + uint32_t dex_offset, + bool is_put) { + uint32_t source_or_dest_reg = instruction.VRegA_22c(); + uint32_t obj_reg = instruction.VRegB_22c(); + uint16_t field_index = instruction.VRegC_22c(); + + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ArtField> resolved_field(hs.NewHandle( + compiler_driver_->ComputeInstanceFieldInfo(field_index, dex_compilation_unit_, is_put, soa))); + + if (resolved_field.Get() == nullptr) { + return false; + } + if (resolved_field->IsVolatile()) { + return false; + } + + Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType(); + if (!IsTypeSupported(field_type)) { + return false; + } + + HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot); + current_block_->AddInstruction(new (arena_) HNullCheck(object, dex_offset)); + if (is_put) { + Temporaries temps(graph_, 1); + HInstruction* null_check = current_block_->GetLastInstruction(); + // We need one temporary for the null check. + temps.Add(null_check); + HInstruction* value = LoadLocal(source_or_dest_reg, field_type); + current_block_->AddInstruction(new (arena_) HInstanceFieldSet( + null_check, + value, + resolved_field->GetOffset())); + } else { + current_block_->AddInstruction(new (arena_) HInstanceFieldGet( + current_block_->GetLastInstruction(), + field_type, + resolved_field->GetOffset())); + + UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction()); + } + return true; +} + bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset) { if (current_block_ == nullptr) { return true; // Dead code @@ -581,6 +667,32 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_ case Instruction::NOP: break; + case Instruction::IGET: + case Instruction::IGET_WIDE: + case Instruction::IGET_OBJECT: + case Instruction::IGET_BOOLEAN: + case Instruction::IGET_BYTE: + case Instruction::IGET_CHAR: + case Instruction::IGET_SHORT: { + if (!BuildFieldAccess(instruction, dex_offset, false)) { + return false; + } + break; + } + + case Instruction::IPUT: + case Instruction::IPUT_WIDE: + case Instruction::IPUT_OBJECT: + case Instruction::IPUT_BOOLEAN: + case Instruction::IPUT_BYTE: + case Instruction::IPUT_CHAR: + case Instruction::IPUT_SHORT: { + if (!BuildFieldAccess(instruction, dex_offset, true)) { + return false; + } + break; + } + default: return false; } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index ee32ca80ac..f94b8e810a 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_BUILDER_H_ #include "dex_file.h" +#include "driver/compiler_driver.h" #include "driver/dex_compilation_unit.h" #include "primitive.h" #include "utils/allocation.h" @@ -32,7 +33,8 @@ class HGraphBuilder : public ValueObject { public: HGraphBuilder(ArenaAllocator* arena, DexCompilationUnit* dex_compilation_unit = nullptr, - const DexFile* dex_file = nullptr) + const DexFile* dex_file = nullptr, + CompilerDriver* driver = nullptr) : arena_(arena), branch_targets_(arena, 0), locals_(arena, 0), @@ -43,7 +45,8 @@ class HGraphBuilder : public ValueObject { constant0_(nullptr), constant1_(nullptr), dex_file_(dex_file), - dex_compilation_unit_(dex_compilation_unit) { } + dex_compilation_unit_(dex_compilation_unit), + compiler_driver_(driver) {} HGraph* BuildGraph(const DexFile::CodeItem& code); @@ -84,11 +87,13 @@ class HGraphBuilder : public ValueObject { template<typename T> void Binop_22s(const Instruction& instruction, bool reverse); - template<typename T> void If_21t(const Instruction& instruction, int32_t dex_offset); - template<typename T> void If_22t(const Instruction& instruction, int32_t dex_offset); + template<typename T> void If_21t(const Instruction& instruction, uint32_t dex_offset); + template<typename T> void If_22t(const Instruction& instruction, uint32_t dex_offset); void BuildReturn(const Instruction& instruction, Primitive::Type type); + bool BuildFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_get); + // Builds an invocation node and returns whether the instruction is supported. bool BuildInvoke(const Instruction& instruction, uint32_t dex_offset, @@ -117,6 +122,7 @@ class HGraphBuilder : public ValueObject { const DexFile* const dex_file_; DexCompilationUnit* const dex_compilation_unit_; + CompilerDriver* const compiler_driver_; DISALLOW_COPY_AND_ASSIGN(HGraphBuilder); }; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index b8332ad2a3..e0db0f18be 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -38,7 +38,8 @@ void CodeGenerator::CompileBaseline(CodeAllocator* allocator) { DCHECK_EQ(frame_size_, kUninitializedFrameSize); ComputeFrameSize(GetGraph()->GetMaximumNumberOfOutVRegs() - + GetGraph()->GetNumberOfVRegs() + + GetGraph()->GetNumberOfLocalVRegs() + + GetGraph()->GetNumberOfTemporaries() + 1 /* filler */); GenerateFrameEntry(); @@ -54,6 +55,7 @@ void CodeGenerator::CompileBaseline(CodeAllocator* allocator) { current->Accept(instruction_visitor); } } + GenerateSlowPaths(); size_t code_size = GetAssembler()->CodeSize(); uint8_t* buffer = allocator->Allocate(code_size); @@ -79,6 +81,7 @@ void CodeGenerator::CompileOptimized(CodeAllocator* allocator) { current->Accept(instruction_visitor); } } + GenerateSlowPaths(); size_t code_size = GetAssembler()->CodeSize(); uint8_t* buffer = allocator->Allocate(code_size); @@ -86,6 +89,12 @@ void CodeGenerator::CompileOptimized(CodeAllocator* allocator) { GetAssembler()->FinalizeInstructions(code); } +void CodeGenerator::GenerateSlowPaths() { + for (size_t i = 0, e = slow_paths_.Size(); i < e; ++i) { + slow_paths_.Get(i)->EmitNativeCode(this); + } +} + size_t CodeGenerator::AllocateFreeRegisterInternal( bool* blocked_registers, size_t number_of_registers) const { for (size_t regno = 0; regno < number_of_registers; regno++) { @@ -94,10 +103,42 @@ size_t CodeGenerator::AllocateFreeRegisterInternal( return regno; } } - LOG(FATAL) << "Unreachable"; return -1; } +void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots) { + SetFrameSize(RoundUp( + number_of_spill_slots * kVRegSize + + kVRegSize // Art method + + FrameEntrySpillSize(), + kStackAlignment)); +} + +Location CodeGenerator::GetTemporaryLocation(HTemporary* temp) const { + uint16_t number_of_locals = GetGraph()->GetNumberOfLocalVRegs(); + // Use the temporary region (right below the dex registers). + int32_t slot = GetFrameSize() - FrameEntrySpillSize() + - kVRegSize // filler + - (number_of_locals * kVRegSize) + - ((1 + temp->GetIndex()) * kVRegSize); + return Location::StackSlot(slot); +} + +int32_t CodeGenerator::GetStackSlot(HLocal* local) const { + uint16_t reg_number = local->GetRegNumber(); + uint16_t number_of_locals = GetGraph()->GetNumberOfLocalVRegs(); + if (reg_number >= number_of_locals) { + // Local is a parameter of the method. It is stored in the caller's frame. + return GetFrameSize() + kVRegSize // ART method + + (reg_number - number_of_locals) * kVRegSize; + } else { + // Local is a temporary in this method. It is stored in this method's frame. + return GetFrameSize() - FrameEntrySpillSize() + - kVRegSize // filler. + - (number_of_locals * kVRegSize) + + (reg_number * kVRegSize); + } +} void CodeGenerator::AllocateRegistersLocally(HInstruction* instruction) const { LocationSummary* locations = instruction->GetLocations(); @@ -162,13 +203,6 @@ void CodeGenerator::AllocateRegistersLocally(HInstruction* instruction) const { locations->SetTempAt(i, loc); } } - - // Make all registers available for the return value. - for (size_t i = 0, e = GetNumberOfRegisters(); i < e; ++i) { - blocked_registers_[i] = false; - } - SetupBlockedRegisters(blocked_registers_); - Location result_location = locations->Out(); if (result_location.IsUnallocated()) { switch (result_location.GetPolicy()) { @@ -187,6 +221,12 @@ void CodeGenerator::AllocateRegistersLocally(HInstruction* instruction) const { void CodeGenerator::InitLocations(HInstruction* instruction) { if (instruction->GetLocations() == nullptr) { + if (instruction->IsTemporary()) { + HInstruction* previous = instruction->GetPrevious(); + Location temp_location = GetTemporaryLocation(instruction->AsTemporary()); + Move(previous, temp_location, instruction); + previous->GetLocations()->SetOut(temp_location); + } return; } AllocateRegistersLocally(instruction); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index ae2f03080e..18e3e5a056 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -30,12 +30,13 @@ namespace art { static size_t constexpr kVRegSize = 4; static size_t constexpr kUninitializedFrameSize = 0; +class CodeGenerator; class DexCompilationUnit; class CodeAllocator { public: - CodeAllocator() { } - virtual ~CodeAllocator() { } + CodeAllocator() {} + virtual ~CodeAllocator() {} virtual uint8_t* Allocate(size_t size) = 0; @@ -48,6 +49,23 @@ struct PcInfo { uintptr_t native_pc; }; +class SlowPathCode : public ArenaObject { + public: + SlowPathCode() : entry_label_(), exit_label_() {} + 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); +}; + class CodeGenerator : public ArenaObject { public: // Compiles the graph to executable instructions. Returns whether the compilation @@ -78,7 +96,10 @@ class CodeGenerator : public ArenaObject { virtual HGraphVisitor* GetInstructionVisitor() = 0; virtual Assembler* GetAssembler() = 0; virtual size_t GetWordSize() const = 0; - virtual void ComputeFrameSize(size_t number_of_spill_slots) = 0; + void ComputeFrameSize(size_t number_of_spill_slots); + virtual size_t FrameEntrySpillSize() const = 0; + int32_t GetStackSlot(HLocal* local) const; + Location GetTemporaryLocation(HTemporary* temp) const; uint32_t GetFrameSize() const { return frame_size_; } void SetFrameSize(uint32_t size) { frame_size_ = size; } @@ -99,6 +120,12 @@ class CodeGenerator : public ArenaObject { pc_infos_.Add(pc_info); } + void AddSlowPath(SlowPathCode* slow_path) { + slow_paths_.Add(slow_path); + } + + void GenerateSlowPaths(); + void BuildMappingTable(std::vector<uint8_t>* vector) const; void BuildVMapTable(std::vector<uint8_t>* vector) const; void BuildNativeGCMap( @@ -110,6 +137,7 @@ class CodeGenerator : public ArenaObject { graph_(graph), block_labels_(graph->GetArena(), 0), pc_infos_(graph->GetArena(), 32), + slow_paths_(graph->GetArena(), 8), blocked_registers_(graph->GetArena()->AllocArray<bool>(number_of_registers)) {} ~CodeGenerator() {} @@ -138,6 +166,7 @@ class CodeGenerator : public ArenaObject { // Labels for each block that will be compiled. GrowableArray<Label> block_labels_; GrowableArray<PcInfo> pc_infos_; + GrowableArray<SlowPathCode*> slow_paths_; // Temporary data structure used when doing register allocation. bool* const blocked_registers_; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index d87c14b4db..73c2d48320 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -24,8 +24,6 @@ #include "utils/arm/assembler_arm.h" #include "utils/arm/managed_register_arm.h" -#define __ reinterpret_cast<ArmAssembler*>(GetAssembler())-> - namespace art { arm::ArmManagedRegister Location::AsArm() const { @@ -34,6 +32,27 @@ arm::ArmManagedRegister Location::AsArm() const { namespace arm { +#define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())-> + +class NullCheckSlowPathARM : public SlowPathCode { + public: + explicit NullCheckSlowPathARM(uint32_t dex_pc) : dex_pc_(dex_pc) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowNullPointer).Int32Value(); + __ ldr(LR, Address(TR, offset)); + __ blx(LR); + codegen->RecordPcInfo(dex_pc_); + } + + private: + const uint32_t dex_pc_; + DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM); +}; + +#undef __ +#define __ reinterpret_cast<ArmAssembler*>(GetAssembler())-> inline Condition ARMCondition(IfCondition cond) { switch (cond) { @@ -63,7 +82,7 @@ inline Condition ARMOppositeCondition(IfCondition cond) { return EQ; // Unreachable. } -static constexpr int kNumberOfPushedRegistersAtEntry = 1; +static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2; // LR, R6, R7 static constexpr int kCurrentMethodStackOffset = 0; void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const { @@ -80,6 +99,10 @@ CodeGeneratorARM::CodeGeneratorARM(HGraph* graph) instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this) {} +size_t CodeGeneratorARM::FrameEntrySpillSize() const { + return kNumberOfPushedRegistersAtEntry * kArmWordSize; +} + static bool* GetBlockedRegisterPairs(bool* blocked_registers) { return blocked_registers + kNumberOfAllocIds; } @@ -88,12 +111,23 @@ ManagedRegister CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type, bool* blocked_registers) const { switch (type) { case Primitive::kPrimLong: { - size_t reg = AllocateFreeRegisterInternal( - GetBlockedRegisterPairs(blocked_registers), kNumberOfRegisterPairs); + bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers); + size_t reg = AllocateFreeRegisterInternal(blocked_register_pairs, kNumberOfRegisterPairs); ArmManagedRegister pair = ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg)); blocked_registers[pair.AsRegisterPairLow()] = true; blocked_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; + } + } return pair; } @@ -103,7 +137,16 @@ ManagedRegister CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type, case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimNot: { - size_t reg = AllocateFreeRegisterInternal(blocked_registers, kNumberOfCoreRegisters); + int reg = AllocateFreeRegisterInternal(blocked_registers, kNumberOfCoreRegisters); + // Block all register pairs that contain `reg`. + bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers); + for (int i = 0; i < kNumberOfRegisterPairs; i++) { + ArmManagedRegister current = + ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); + if (current.AsRegisterPairLow() == reg || current.AsRegisterPairHigh() == reg) { + blocked_register_pairs[i] = true; + } + } return ArmManagedRegister::FromCoreRegister(static_cast<Register>(reg)); } @@ -140,13 +183,12 @@ void CodeGeneratorARM::SetupBlockedRegisters(bool* blocked_registers) const { blocked_registers[IP] = true; // TODO: We currently don't use Quick's callee saved registers. + // We always save and restore R6 and R7 to make sure we can use three + // register pairs for long operations. blocked_registers[R5] = true; - blocked_registers[R6] = true; - blocked_registers[R7] = true; blocked_registers[R8] = true; blocked_registers[R10] = true; blocked_registers[R11] = true; - blocked_register_pairs[R6_R7] = true; } size_t CodeGeneratorARM::GetNumberOfRegisters() const { @@ -162,17 +204,9 @@ InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGene assembler_(codegen->GetAssembler()), codegen_(codegen) {} -void CodeGeneratorARM::ComputeFrameSize(size_t number_of_spill_slots) { - SetFrameSize(RoundUp( - number_of_spill_slots * kVRegSize - + kVRegSize // Art method - + kNumberOfPushedRegistersAtEntry * kArmWordSize, - kStackAlignment)); -} - void CodeGeneratorARM::GenerateFrameEntry() { - core_spill_mask_ |= (1 << LR); - __ PushList((1 << LR)); + core_spill_mask_ |= (1 << LR | 1 << R6 | 1 << R7); + __ PushList(1 << LR | 1 << R6 | 1 << R7); // The return PC has already been pushed on the stack. __ AddConstant(SP, -(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize)); @@ -181,30 +215,13 @@ void CodeGeneratorARM::GenerateFrameEntry() { void CodeGeneratorARM::GenerateFrameExit() { __ AddConstant(SP, GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize); - __ PopList((1 << PC)); + __ PopList(1 << PC | 1 << R6 | 1 << R7); } void CodeGeneratorARM::Bind(Label* label) { __ Bind(label); } -int32_t CodeGeneratorARM::GetStackSlot(HLocal* local) const { - uint16_t reg_number = local->GetRegNumber(); - uint16_t number_of_vregs = GetGraph()->GetNumberOfVRegs(); - uint16_t number_of_in_vregs = GetGraph()->GetNumberOfInVRegs(); - if (reg_number >= number_of_vregs - number_of_in_vregs) { - // Local is a parameter of the method. It is stored in the caller's frame. - return GetFrameSize() + kVRegSize // ART method - + (reg_number - number_of_vregs + number_of_in_vregs) * kVRegSize; - } else { - // Local is a temporary in this method. It is stored in this method's frame. - return GetFrameSize() - (kNumberOfPushedRegistersAtEntry * kArmWordSize) - - kVRegSize // filler. - - (number_of_vregs * kVRegSize) - + (reg_number * kVRegSize); - } -} - Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const { switch (load->GetType()) { case Primitive::kPrimLong: @@ -399,9 +416,7 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr LOG(FATAL) << "Unimplemented type " << instruction->GetType(); } } else { - // This can currently only happen when the instruction that requests the move - // is the next to be compiled. - DCHECK_EQ(instruction->GetNext(), move_for); + DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary()); switch (instruction->GetType()) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: @@ -448,7 +463,12 @@ void InstructionCodeGeneratorARM::VisitExit(HExit* exit) { void LocationsBuilderARM::VisitIf(HIf* if_instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); - locations->SetInAt(0, Location::Any()); + HInstruction* cond = if_instr->InputAt(0); + DCHECK(cond->IsCondition()); + HCondition* condition = cond->AsCondition(); + if (condition->NeedsMaterialization()) { + locations->SetInAt(0, Location::Any()); + } if_instr->SetLocations(locations); } @@ -482,7 +502,9 @@ void LocationsBuilderARM::VisitCondition(HCondition* comp) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister()); + if (comp->NeedsMaterialization()) { + locations->SetOut(Location::RequiresRegister()); + } comp->SetLocations(locations); } @@ -960,6 +982,147 @@ void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) { LOG(FATAL) << "Unreachable"; } +void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register obj = locations->InAt(0).AsArm().AsCoreRegister(); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + Primitive::Type field_type = instruction->InputAt(1)->GetType(); + + switch (field_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + Register value = locations->InAt(1).AsArm().AsCoreRegister(); + __ StoreToOffset(kStoreByte, value, obj, offset); + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + Register value = locations->InAt(1).AsArm().AsCoreRegister(); + __ StoreToOffset(kStoreHalfword, value, obj, offset); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + Register value = locations->InAt(1).AsArm().AsCoreRegister(); + __ StoreToOffset(kStoreWord, value, obj, offset); + break; + } + + case Primitive::kPrimLong: { + ArmManagedRegister value = locations->InAt(1).AsArm(); + __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow(), obj, offset); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << field_type; + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << field_type; + } +} + +void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register obj = locations->InAt(0).AsArm().AsCoreRegister(); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: { + Register out = locations->Out().AsArm().AsCoreRegister(); + __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset); + break; + } + + case Primitive::kPrimByte: { + Register out = locations->Out().AsArm().AsCoreRegister(); + __ LoadFromOffset(kLoadSignedByte, out, obj, offset); + break; + } + + case Primitive::kPrimShort: { + Register out = locations->Out().AsArm().AsCoreRegister(); + __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset); + break; + } + + case Primitive::kPrimChar: { + Register out = locations->Out().AsArm().AsCoreRegister(); + __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + Register out = locations->Out().AsArm().AsCoreRegister(); + __ LoadFromOffset(kLoadWord, out, obj, offset); + break; + } + + case Primitive::kPrimLong: { + // TODO: support volatile. + ArmManagedRegister out = locations->Out().AsArm(); + __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow(), obj, offset); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + // TODO: Have a normalization phase that makes this instruction never used. + locations->SetOut(Location::SameAsFirstInput()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { + SlowPathCode* slow_path = + new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction->GetDexPc()); + codegen_->AddSlowPath(slow_path); + + LocationSummary* locations = instruction->GetLocations(); + Location obj = locations->InAt(0); + DCHECK(obj.Equals(locations->Out())); + + if (obj.IsRegister()) { + __ cmp(obj.AsArm().AsCoreRegister(), ShifterOperand(0)); + } + __ b(slow_path->GetEntryLabel(), EQ); +} + +void LocationsBuilderARM::VisitTemporary(HTemporary* temp) { + temp->SetLocations(nullptr); +} + +void InstructionCodeGeneratorARM::VisitTemporary(HTemporary* temp) { + // Nothing to do, this is driven by the code generator. +} + void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction) { LOG(FATAL) << "Unreachable"; } diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index c46c1b131c..1b5974f9a2 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -126,7 +126,6 @@ class CodeGeneratorARM : public CodeGenerator { explicit CodeGeneratorARM(HGraph* graph); virtual ~CodeGeneratorARM() { } - virtual void ComputeFrameSize(size_t number_of_spill_slots) OVERRIDE; virtual void GenerateFrameEntry() OVERRIDE; virtual void GenerateFrameExit() OVERRIDE; virtual void Bind(Label* label) OVERRIDE; @@ -136,6 +135,8 @@ class CodeGeneratorARM : public CodeGenerator { return kArmWordSize; } + virtual size_t FrameEntrySpillSize() const OVERRIDE; + virtual HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; } @@ -153,7 +154,6 @@ class CodeGeneratorARM : public CodeGenerator { Primitive::Type type, bool* blocked_registers) const OVERRIDE; virtual size_t GetNumberOfRegisters() const OVERRIDE; - int32_t GetStackSlot(HLocal* local) const; virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE; virtual size_t GetNumberOfCoreRegisters() const OVERRIDE { diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 572d494719..4e69a0cad8 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -24,8 +24,6 @@ #include "mirror/art_method.h" #include "thread.h" -#define __ reinterpret_cast<X86Assembler*>(GetAssembler())-> - namespace art { x86::X86ManagedRegister Location::AsX86() const { @@ -34,6 +32,26 @@ x86::X86ManagedRegister Location::AsX86() const { namespace x86 { +#define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())-> + +class NullCheckSlowPathX86 : public SlowPathCode { + public: + explicit NullCheckSlowPathX86(uint32_t dex_pc) : dex_pc_(dex_pc) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowNullPointer))); + codegen->RecordPcInfo(dex_pc_); + } + + private: + const uint32_t dex_pc_; + DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86); +}; + +#undef __ +#define __ reinterpret_cast<X86Assembler*>(GetAssembler())-> + inline Condition X86Condition(IfCondition cond) { switch (cond) { case kCondEQ: return kEqual; @@ -65,12 +83,8 @@ CodeGeneratorX86::CodeGeneratorX86(HGraph* graph) instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this) {} -void CodeGeneratorX86::ComputeFrameSize(size_t number_of_spill_slots) { - SetFrameSize(RoundUp( - number_of_spill_slots * kVRegSize - + kVRegSize // Art method - + kNumberOfPushedRegistersAtEntry * kX86WordSize, - kStackAlignment)); +size_t CodeGeneratorX86::FrameEntrySpillSize() const { + return kNumberOfPushedRegistersAtEntry * kX86WordSize; } static bool* GetBlockedRegisterPairs(bool* blocked_registers) { @@ -107,8 +121,18 @@ ManagedRegister CodeGeneratorX86::AllocateFreeRegister(Primitive::Type type, case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimNot: { - size_t reg = AllocateFreeRegisterInternal(blocked_registers, kNumberOfCpuRegisters); - return X86ManagedRegister::FromCpuRegister(static_cast<Register>(reg)); + Register reg = static_cast<Register>( + AllocateFreeRegisterInternal(blocked_registers, kNumberOfCpuRegisters)); + // Block all register pairs that contain `reg`. + bool* blocked_register_pairs = GetBlockedRegisterPairs(blocked_registers); + for (int i = 0; i < kNumberOfRegisterPairs; i++) { + X86ManagedRegister current = + X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); + if (current.AsRegisterPairLow() == reg || current.AsRegisterPairHigh() == reg) { + blocked_register_pairs[i] = true; + } + } + return X86ManagedRegister::FromCpuRegister(reg); } case Primitive::kPrimFloat: @@ -176,24 +200,6 @@ void InstructionCodeGeneratorX86::LoadCurrentMethod(Register reg) { __ movl(reg, Address(ESP, kCurrentMethodStackOffset)); } -int32_t CodeGeneratorX86::GetStackSlot(HLocal* local) const { - uint16_t reg_number = local->GetRegNumber(); - uint16_t number_of_vregs = GetGraph()->GetNumberOfVRegs(); - uint16_t number_of_in_vregs = GetGraph()->GetNumberOfInVRegs(); - if (reg_number >= number_of_vregs - number_of_in_vregs) { - // Local is a parameter of the method. It is stored in the caller's frame. - return GetFrameSize() + kVRegSize // ART method - + (reg_number - number_of_vregs + number_of_in_vregs) * kVRegSize; - } else { - // Local is a temporary in this method. It is stored in this method's frame. - return GetFrameSize() - (kNumberOfPushedRegistersAtEntry * kX86WordSize) - - kVRegSize // filler. - - (number_of_vregs * kVRegSize) - + (reg_number * kVRegSize); - } -} - - Location CodeGeneratorX86::GetStackLocation(HLoadLocal* load) const { switch (load->GetType()) { case Primitive::kPrimLong: @@ -392,9 +398,7 @@ void CodeGeneratorX86::Move(HInstruction* instruction, Location location, HInstr LOG(FATAL) << "Unimplemented local type " << instruction->GetType(); } } else { - // This can currently only happen when the instruction that requests the move - // is the next to be compiled. - DCHECK_EQ(instruction->GetNext(), move_for); + DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary()); switch (instruction->GetType()) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: @@ -441,7 +445,12 @@ void InstructionCodeGeneratorX86::VisitExit(HExit* exit) { void LocationsBuilderX86::VisitIf(HIf* if_instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); - locations->SetInAt(0, Location::Any()); + HInstruction* cond = if_instr->InputAt(0); + DCHECK(cond->IsCondition()); + HCondition* condition = cond->AsCondition(); + if (condition->NeedsMaterialization()) { + locations->SetInAt(0, Location::Any()); + } if_instr->SetLocations(locations); } @@ -520,7 +529,9 @@ void LocationsBuilderX86::VisitCondition(HCondition* comp) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::Any()); - locations->SetOut(Location::SameAsFirstInput()); + if (comp->NeedsMaterialization()) { + locations->SetOut(Location::SameAsFirstInput()); + } comp->SetLocations(locations); } @@ -915,7 +926,7 @@ void InstructionCodeGeneratorX86::VisitNot(HNot* instruction) { void LocationsBuilderX86::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); locations->SetOut(Location::RequiresRegister()); compare->SetLocations(locations); } @@ -928,11 +939,21 @@ void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) { Label less, greater, done; Register output = locations->Out().AsX86().AsCpuRegister(); X86ManagedRegister left = locations->InAt(0).AsX86(); - X86ManagedRegister right = locations->InAt(1).AsX86(); - __ cmpl(left.AsRegisterPairHigh(), right.AsRegisterPairHigh()); + Location right = locations->InAt(1); + if (right.IsRegister()) { + __ cmpl(left.AsRegisterPairHigh(), right.AsX86().AsRegisterPairHigh()); + } else { + DCHECK(right.IsDoubleStackSlot()); + __ cmpl(left.AsRegisterPairHigh(), Address(ESP, right.GetHighStackIndex(kX86WordSize))); + } __ j(kLess, &less); // Signed compare. __ j(kGreater, &greater); // Signed compare. - __ cmpl(left.AsRegisterPairLow(), right.AsRegisterPairLow()); + if (right.IsRegister()) { + __ cmpl(left.AsRegisterPairLow(), right.AsX86().AsRegisterPairLow()); + } else { + DCHECK(right.IsDoubleStackSlot()); + __ cmpl(left.AsRegisterPairLow(), Address(ESP, right.GetStackIndex())); + } __ movl(output, Immediate(0)); __ j(kEqual, &done); __ j(kBelow, &less); // Unsigned compare. @@ -965,6 +986,158 @@ void InstructionCodeGeneratorX86::VisitPhi(HPhi* instruction) { LOG(FATAL) << "Unreachable"; } +void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + Primitive::Type field_type = instruction->InputAt(1)->GetType(); + if (field_type == Primitive::kPrimBoolean || field_type == Primitive::kPrimByte) { + // Ensure the value is in a byte register. + locations->SetInAt(1, X86CpuLocation(EAX)); + } else { + locations->SetInAt(1, Location::RequiresRegister()); + } + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register obj = locations->InAt(0).AsX86().AsCpuRegister(); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + Primitive::Type field_type = instruction->InputAt(1)->GetType(); + + switch (field_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + ByteRegister value = locations->InAt(1).AsX86().AsByteRegister(); + __ movb(Address(obj, offset), value); + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + Register value = locations->InAt(1).AsX86().AsCpuRegister(); + __ movw(Address(obj, offset), value); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + Register value = locations->InAt(1).AsX86().AsCpuRegister(); + __ movl(Address(obj, offset), value); + break; + } + + case Primitive::kPrimLong: { + X86ManagedRegister value = locations->InAt(1).AsX86(); + __ movl(Address(obj, offset), value.AsRegisterPairLow()); + __ movl(Address(obj, kX86WordSize + offset), value.AsRegisterPairHigh()); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << field_type; + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << field_type; + } +} + +void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register obj = locations->InAt(0).AsX86().AsCpuRegister(); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: { + Register out = locations->Out().AsX86().AsCpuRegister(); + __ movzxb(out, Address(obj, offset)); + break; + } + + case Primitive::kPrimByte: { + Register out = locations->Out().AsX86().AsCpuRegister(); + __ movsxb(out, Address(obj, offset)); + break; + } + + case Primitive::kPrimShort: { + Register out = locations->Out().AsX86().AsCpuRegister(); + __ movsxw(out, Address(obj, offset)); + break; + } + + case Primitive::kPrimChar: { + Register out = locations->Out().AsX86().AsCpuRegister(); + __ movzxw(out, Address(obj, offset)); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + Register out = locations->Out().AsX86().AsCpuRegister(); + __ movl(out, Address(obj, offset)); + break; + } + + case Primitive::kPrimLong: { + // TODO: support volatile. + X86ManagedRegister out = locations->Out().AsX86(); + __ movl(out.AsRegisterPairLow(), Address(obj, offset)); + __ movl(out.AsRegisterPairHigh(), Address(obj, kX86WordSize + offset)); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::Any()); + // TODO: Have a normalization phase that makes this instruction never used. + locations->SetOut(Location::SameAsFirstInput()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) { + SlowPathCode* slow_path = + new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction->GetDexPc()); + codegen_->AddSlowPath(slow_path); + + LocationSummary* locations = instruction->GetLocations(); + Location obj = locations->InAt(0); + DCHECK(obj.Equals(locations->Out())); + + if (obj.IsRegister()) { + __ cmpl(obj.AsX86().AsCpuRegister(), Immediate(0)); + } else { + DCHECK(locations->InAt(0).IsStackSlot()); + __ cmpl(Address(ESP, obj.GetStackIndex()), Immediate(0)); + } + __ j(kEqual, slow_path->GetEntryLabel()); +} + +void LocationsBuilderX86::VisitTemporary(HTemporary* temp) { + temp->SetLocations(nullptr); +} + +void InstructionCodeGeneratorX86::VisitTemporary(HTemporary* temp) { + // Nothing to do, this is driven by the code generator. +} + void LocationsBuilderX86::VisitParallelMove(HParallelMove* instruction) { LOG(FATAL) << "Unreachable"; } diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 8a8216a56d..d622d2a685 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -128,7 +128,6 @@ class CodeGeneratorX86 : public CodeGenerator { explicit CodeGeneratorX86(HGraph* graph); virtual ~CodeGeneratorX86() { } - virtual void ComputeFrameSize(size_t number_of_spill_slots) OVERRIDE; virtual void GenerateFrameEntry() OVERRIDE; virtual void GenerateFrameExit() OVERRIDE; virtual void Bind(Label* label) OVERRIDE; @@ -138,6 +137,8 @@ class CodeGeneratorX86 : public CodeGenerator { return kX86WordSize; } + virtual size_t FrameEntrySpillSize() const OVERRIDE; + virtual HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; } @@ -155,7 +156,6 @@ class CodeGeneratorX86 : public CodeGenerator { virtual ManagedRegister AllocateFreeRegister( Primitive::Type type, bool* blocked_registers) const OVERRIDE; - int32_t GetStackSlot(HLocal* local) const; virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE; virtual size_t GetNumberOfCoreRegisters() const OVERRIDE { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index dc1d6164b1..e3ce5ceb4f 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -25,8 +25,6 @@ #include "utils/x86_64/assembler_x86_64.h" #include "utils/x86_64/managed_register_x86_64.h" -#define __ reinterpret_cast<X86_64Assembler*>(GetAssembler())-> - namespace art { x86_64::X86_64ManagedRegister Location::AsX86_64() const { @@ -35,6 +33,26 @@ x86_64::X86_64ManagedRegister Location::AsX86_64() const { namespace x86_64 { +#define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())-> + +class NullCheckSlowPathX86_64 : public SlowPathCode { + public: + explicit NullCheckSlowPathX86_64(uint32_t dex_pc) : dex_pc_(dex_pc) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowNullPointer), true)); + codegen->RecordPcInfo(dex_pc_); + } + + private: + const uint32_t dex_pc_; + DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64); +}; + +#undef __ +#define __ reinterpret_cast<X86_64Assembler*>(GetAssembler())-> + inline Condition X86_64Condition(IfCondition cond) { switch (cond) { case kCondEQ: return kEqual; @@ -73,6 +91,10 @@ CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph) instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this) {} +size_t CodeGeneratorX86_64::FrameEntrySpillSize() const { + return kNumberOfPushedRegistersAtEntry * kX86_64WordSize; +} + InstructionCodeGeneratorX86_64::InstructionCodeGeneratorX86_64(HGraph* graph, CodeGeneratorX86_64* codegen) : HGraphVisitor(graph), assembler_(codegen->GetAssembler()), @@ -119,16 +141,6 @@ void CodeGeneratorX86_64::SetupBlockedRegisters(bool* blocked_registers) const { blocked_registers[R15] = true; } -void CodeGeneratorX86_64::ComputeFrameSize(size_t number_of_spill_slots) { - // Add the current ART method to the frame size, the return PC, and the filler. - SetFrameSize(RoundUp( - number_of_spill_slots * kVRegSize - + kVRegSize // filler - + kVRegSize // Art method - + kNumberOfPushedRegistersAtEntry * kX86_64WordSize, - kStackAlignment)); -} - void CodeGeneratorX86_64::GenerateFrameEntry() { // Create a fake register to mimic Quick. static const int kFakeReturnRegister = 16; @@ -152,23 +164,6 @@ void InstructionCodeGeneratorX86_64::LoadCurrentMethod(CpuRegister reg) { __ movl(reg, Address(CpuRegister(RSP), kCurrentMethodStackOffset)); } -int32_t CodeGeneratorX86_64::GetStackSlot(HLocal* local) const { - uint16_t reg_number = local->GetRegNumber(); - uint16_t number_of_vregs = GetGraph()->GetNumberOfVRegs(); - uint16_t number_of_in_vregs = GetGraph()->GetNumberOfInVRegs(); - if (reg_number >= number_of_vregs - number_of_in_vregs) { - // Local is a parameter of the method. It is stored in the caller's frame. - return GetFrameSize() + kVRegSize // ART method - + (reg_number - number_of_vregs + number_of_in_vregs) * kVRegSize; - } else { - // Local is a temporary in this method. It is stored in this method's frame. - return GetFrameSize() - (kNumberOfPushedRegistersAtEntry * kX86_64WordSize) - - kVRegSize - - (number_of_vregs * kVRegSize) - + (reg_number * kVRegSize); - } -} - Location CodeGeneratorX86_64::GetStackLocation(HLoadLocal* load) const { switch (load->GetType()) { case Primitive::kPrimLong: @@ -265,9 +260,7 @@ void CodeGeneratorX86_64::Move(HInstruction* instruction, LOG(FATAL) << "Unimplemented local type " << instruction->GetType(); } } else { - // This can currently only happen when the instruction that requests the move - // is the next to be compiled. - DCHECK_EQ(instruction->GetNext(), move_for); + DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary()); switch (instruction->GetType()) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: @@ -311,7 +304,12 @@ void InstructionCodeGeneratorX86_64::VisitExit(HExit* exit) { void LocationsBuilderX86_64::VisitIf(HIf* if_instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr); - locations->SetInAt(0, Location::Any()); + HInstruction* cond = if_instr->InputAt(0); + DCHECK(cond->IsCondition()); + HCondition* condition = cond->AsCondition(); + if (condition->NeedsMaterialization()) { + locations->SetInAt(0, Location::Any()); + } if_instr->SetLocations(locations); } @@ -385,7 +383,9 @@ void LocationsBuilderX86_64::VisitCondition(HCondition* comp) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister()); + if (comp->NeedsMaterialization()) { + locations->SetOut(Location::RequiresRegister()); + } comp->SetLocations(locations); } @@ -827,6 +827,141 @@ void InstructionCodeGeneratorX86_64::VisitPhi(HPhi* instruction) { LOG(FATAL) << "Unimplemented"; } +void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister(); + CpuRegister value = locations->InAt(1).AsX86_64().AsCpuRegister(); + size_t offset = instruction->GetFieldOffset().SizeValue(); + Primitive::Type field_type = instruction->InputAt(1)->GetType(); + + switch (field_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: { + __ movb(Address(obj, offset), value); + break; + } + + case Primitive::kPrimShort: + case Primitive::kPrimChar: { + __ movw(Address(obj, offset), value); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + __ movl(Address(obj, offset), value); + break; + } + + case Primitive::kPrimLong: { + __ movq(Address(obj, offset), value); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << field_type; + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << field_type; + } +} + +void LocationsBuilderX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { + LocationSummary* locations = instruction->GetLocations(); + CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister(); + CpuRegister out = locations->Out().AsX86_64().AsCpuRegister(); + size_t offset = instruction->GetFieldOffset().SizeValue(); + + switch (instruction->GetType()) { + case Primitive::kPrimBoolean: { + __ movzxb(out, Address(obj, offset)); + break; + } + + case Primitive::kPrimByte: { + __ movsxb(out, Address(obj, offset)); + break; + } + + case Primitive::kPrimShort: { + __ movsxw(out, Address(obj, offset)); + break; + } + + case Primitive::kPrimChar: { + __ movzxw(out, Address(obj, offset)); + break; + } + + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + __ movl(out, Address(obj, offset)); + break; + } + + case Primitive::kPrimLong: { + __ movq(out, Address(obj, offset)); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Unimplemented register type " << instruction->GetType(); + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << instruction->GetType(); + } +} + +void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); + locations->SetInAt(0, Location::Any()); + // TODO: Have a normalization phase that makes this instruction never used. + locations->SetOut(Location::SameAsFirstInput()); + instruction->SetLocations(locations); +} + +void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) { + SlowPathCode* slow_path = + new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction->GetDexPc()); + codegen_->AddSlowPath(slow_path); + + LocationSummary* locations = instruction->GetLocations(); + Location obj = locations->InAt(0); + DCHECK(obj.Equals(locations->Out())); + + if (obj.IsRegister()) { + __ cmpl(obj.AsX86_64().AsCpuRegister(), Immediate(0)); + } else { + DCHECK(locations->InAt(0).IsStackSlot()); + __ cmpl(Address(CpuRegister(RSP), obj.GetStackIndex()), Immediate(0)); + } + __ j(kEqual, slow_path->GetEntryLabel()); +} + +void LocationsBuilderX86_64::VisitTemporary(HTemporary* temp) { + temp->SetLocations(nullptr); +} + +void InstructionCodeGeneratorX86_64::VisitTemporary(HTemporary* temp) { + // Nothing to do, this is driven by the code generator. +} + void LocationsBuilderX86_64::VisitParallelMove(HParallelMove* instruction) { LOG(FATAL) << "Unimplemented"; } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index d347a4f121..8283dda4a5 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -125,7 +125,6 @@ class CodeGeneratorX86_64 : public CodeGenerator { explicit CodeGeneratorX86_64(HGraph* graph); virtual ~CodeGeneratorX86_64() {} - virtual void ComputeFrameSize(size_t number_of_spill_slots) OVERRIDE; virtual void GenerateFrameEntry() OVERRIDE; virtual void GenerateFrameExit() OVERRIDE; virtual void Bind(Label* label) OVERRIDE; @@ -135,6 +134,8 @@ class CodeGeneratorX86_64 : public CodeGenerator { return kX86_64WordSize; } + virtual size_t FrameEntrySpillSize() const OVERRIDE; + virtual HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; } @@ -151,7 +152,6 @@ class CodeGeneratorX86_64 : public CodeGenerator { return &move_resolver_; } - int32_t GetStackSlot(HLocal* local) const; virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE; virtual size_t GetNumberOfRegisters() const OVERRIDE { diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc index 987c5f27b7..a6e5ca9e46 100644 --- a/compiler/optimizing/live_ranges_test.cc +++ b/compiler/optimizing/live_ranges_test.cc @@ -182,9 +182,9 @@ TEST(LiveRangesTest, CFG3) { ASSERT_TRUE(range->GetNext() == nullptr); // Test for the phi. - interval = liveness.GetInstructionFromSsaIndex(3)->GetLiveInterval(); + interval = liveness.GetInstructionFromSsaIndex(2)->GetLiveInterval(); range = interval->GetFirstRange(); - ASSERT_EQ(22u, liveness.GetInstructionFromSsaIndex(3)->GetLifetimePosition()); + ASSERT_EQ(22u, liveness.GetInstructionFromSsaIndex(2)->GetLifetimePosition()); ASSERT_EQ(22u, range->GetStart()); ASSERT_EQ(25u, range->GetEnd()); ASSERT_TRUE(range->GetNext() == nullptr); diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc index 2d0bc39cd5..1a4d74536b 100644 --- a/compiler/optimizing/liveness_test.cc +++ b/compiler/optimizing/liveness_test.cc @@ -152,32 +152,32 @@ TEST(LivenessTest, CFG4) { // return a; // // Bitsets are made of: - // (constant0, constant4, constant5, phi, equal test) + // (constant0, constant4, constant5, phi) const char* expected = "Block 0\n" // entry block - " live in: (00000)\n" - " live out: (11100)\n" - " kill: (11100)\n" + " live in: (0000)\n" + " live out: (1110)\n" + " kill: (1110)\n" "Block 1\n" // block with if - " live in: (11100)\n" - " live out: (01100)\n" - " kill: (00010)\n" + " live in: (1110)\n" + " live out: (0110)\n" + " kill: (0000)\n" "Block 2\n" // else block - " live in: (01000)\n" - " live out: (00000)\n" - " kill: (00000)\n" + " live in: (0100)\n" + " live out: (0000)\n" + " kill: (0000)\n" "Block 3\n" // then block - " live in: (00100)\n" - " live out: (00000)\n" - " kill: (00000)\n" + " live in: (0010)\n" + " live out: (0000)\n" + " kill: (0000)\n" "Block 4\n" // return block - " live in: (00000)\n" - " live out: (00000)\n" - " kill: (00001)\n" + " live in: (0000)\n" + " live out: (0000)\n" + " kill: (0001)\n" "Block 5\n" // exit block - " live in: (00000)\n" - " live out: (00000)\n" - " kill: (00000)\n"; + " live in: (0000)\n" + " live out: (0000)\n" + " kill: (0000)\n"; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, @@ -197,31 +197,34 @@ TEST(LivenessTest, CFG5) { // a = 4; // } // return a; + // + // Bitsets are made of: + // (constant0, constant4, phi) const char* expected = "Block 0\n" // entry block - " live in: (0000)\n" - " live out: (1100)\n" - " kill: (1100)\n" + " live in: (000)\n" + " live out: (110)\n" + " kill: (110)\n" "Block 1\n" // block with if - " live in: (1100)\n" - " live out: (1100)\n" - " kill: (0010)\n" + " live in: (110)\n" + " live out: (110)\n" + " kill: (000)\n" "Block 2\n" // else block - " live in: (0100)\n" - " live out: (0000)\n" - " kill: (0000)\n" + " live in: (010)\n" + " live out: (000)\n" + " kill: (000)\n" "Block 3\n" // return block - " live in: (0000)\n" - " live out: (0000)\n" - " kill: (0001)\n" + " live in: (000)\n" + " live out: (000)\n" + " kill: (001)\n" "Block 4\n" // exit block - " live in: (0000)\n" - " live out: (0000)\n" - " kill: (0000)\n" + " live in: (000)\n" + " live out: (000)\n" + " kill: (000)\n" "Block 5\n" // block to avoid critical edge. Predecessor is 1, successor is 3. - " live in: (1000)\n" - " live out: (0000)\n" - " kill: (0000)\n"; + " live in: (100)\n" + " live out: (000)\n" + " kill: (000)\n"; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, @@ -239,31 +242,33 @@ TEST(LivenessTest, Loop1) { // a = 4; // } // return; + // Bitsets are made of: + // (constant0, constant4, phi) const char* expected = "Block 0\n" // entry block - " live in: (0000)\n" - " live out: (1100)\n" - " kill: (1100)\n" + " live in: (000)\n" + " live out: (110)\n" + " kill: (110)\n" "Block 1\n" // pre header - " live in: (1100)\n" - " live out: (0100)\n" - " kill: (0000)\n" + " live in: (110)\n" + " live out: (010)\n" + " kill: (000)\n" "Block 2\n" // loop header - " live in: (0100)\n" - " live out: (0100)\n" - " kill: (0011)\n" + " live in: (010)\n" + " live out: (010)\n" + " kill: (001)\n" "Block 3\n" // back edge - " live in: (0100)\n" - " live out: (0100)\n" - " kill: (0000)\n" + " live in: (010)\n" + " live out: (010)\n" + " kill: (000)\n" "Block 4\n" // return block - " live in: (0000)\n" - " live out: (0000)\n" - " kill: (0000)\n" + " live in: (000)\n" + " live out: (000)\n" + " kill: (000)\n" "Block 5\n" // exit block - " live in: (0000)\n" - " live out: (0000)\n" - " kill: (0000)\n"; + " live in: (000)\n" + " live out: (000)\n" + " kill: (000)\n"; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( @@ -283,31 +288,33 @@ TEST(LivenessTest, Loop3) { // a = 4; // } // return 5; + // Bitsets are made of: + // (constant0, constant4, constant5, phi) const char* expected = "Block 0\n" - " live in: (00000)\n" - " live out: (11100)\n" - " kill: (11100)\n" + " live in: (0000)\n" + " live out: (1110)\n" + " kill: (1110)\n" "Block 1\n" - " live in: (11100)\n" - " live out: (01100)\n" - " kill: (00000)\n" + " live in: (1110)\n" + " live out: (0110)\n" + " kill: (0000)\n" "Block 2\n" // loop header - " live in: (01100)\n" - " live out: (01100)\n" - " kill: (00011)\n" + " live in: (0110)\n" + " live out: (0110)\n" + " kill: (0001)\n" "Block 3\n" // back edge - " live in: (01100)\n" - " live out: (01100)\n" - " kill: (00000)\n" + " live in: (0110)\n" + " live out: (0110)\n" + " kill: (0000)\n" "Block 4\n" // return block - " live in: (00100)\n" - " live out: (00000)\n" - " kill: (00000)\n" + " live in: (0010)\n" + " live out: (0000)\n" + " kill: (0000)\n" "Block 5\n" // exit block - " live in: (00000)\n" - " live out: (00000)\n" - " kill: (00000)\n"; + " live in: (0000)\n" + " live out: (0000)\n" + " kill: (0000)\n"; const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, @@ -330,36 +337,36 @@ TEST(LivenessTest, Loop4) { // } // return a; // Bitsets are made of: - // (constant0, constant4, phi, equal test) + // (constant0, constant4, phi) const char* expected = "Block 0\n" - " live in: (0000)\n" - " live out: (1100)\n" - " kill: (1100)\n" + " live in: (000)\n" + " live out: (110)\n" + " kill: (110)\n" "Block 1\n" - " live in: (1100)\n" - " live out: (1100)\n" - " kill: (0000)\n" + " live in: (110)\n" + " live out: (110)\n" + " kill: (000)\n" "Block 2\n" // loop header - " live in: (0100)\n" - " live out: (0110)\n" - " kill: (0011)\n" + " live in: (010)\n" + " live out: (011)\n" + " kill: (001)\n" "Block 3\n" // back edge - " live in: (0100)\n" - " live out: (0100)\n" - " kill: (0000)\n" + " live in: (010)\n" + " live out: (010)\n" + " kill: (000)\n" "Block 4\n" // pre loop header - " live in: (1100)\n" - " live out: (0100)\n" - " kill: (0000)\n" + " live in: (110)\n" + " live out: (010)\n" + " kill: (000)\n" "Block 5\n" // return block - " live in: (0010)\n" - " live out: (0000)\n" - " kill: (0000)\n" + " live in: (001)\n" + " live out: (000)\n" + " kill: (000)\n" "Block 6\n" // exit block - " live in: (0000)\n" - " live out: (0000)\n" - " kill: (0000)\n"; + " live in: (000)\n" + " live out: (000)\n" + " kill: (000)\n"; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, @@ -377,45 +384,44 @@ TEST(LivenessTest, Loop5) { // Make sure we create a preheader of a loop when a header originally has two // incoming blocks and one back edge. // Bitsets are made of: - // (constant0, constant4, constant5, equal in block 1, phi in block 8, phi in block 4, - // equal in block 4) + // (constant0, constant4, constant5, phi in block 8, phi in block 4) const char* expected = "Block 0\n" - " live in: (0000000)\n" - " live out: (1110000)\n" - " kill: (1110000)\n" + " live in: (00000)\n" + " live out: (11100)\n" + " kill: (11100)\n" "Block 1\n" - " live in: (1110000)\n" - " live out: (0110000)\n" - " kill: (0001000)\n" + " live in: (11100)\n" + " live out: (01100)\n" + " kill: (00000)\n" "Block 2\n" - " live in: (0100000)\n" - " live out: (0000000)\n" - " kill: (0000000)\n" + " live in: (01000)\n" + " live out: (00000)\n" + " kill: (00000)\n" "Block 3\n" - " live in: (0010000)\n" - " live out: (0000000)\n" - " kill: (0000000)\n" + " live in: (00100)\n" + " live out: (00000)\n" + " kill: (00000)\n" "Block 4\n" // loop header - " live in: (0000000)\n" - " live out: (0000010)\n" - " kill: (0000011)\n" + " live in: (00000)\n" + " live out: (00001)\n" + " kill: (00001)\n" "Block 5\n" // back edge - " live in: (0000010)\n" - " live out: (0000000)\n" - " kill: (0000000)\n" + " live in: (00001)\n" + " live out: (00000)\n" + " kill: (00000)\n" "Block 6\n" // return block - " live in: (0000010)\n" - " live out: (0000000)\n" - " kill: (0000000)\n" + " live in: (00001)\n" + " live out: (00000)\n" + " kill: (00000)\n" "Block 7\n" // exit block - " live in: (0000000)\n" - " live out: (0000000)\n" - " kill: (0000000)\n" + " live in: (00000)\n" + " live out: (00000)\n" + " kill: (00000)\n" "Block 8\n" // synthesized pre header - " live in: (0000000)\n" - " live out: (0000000)\n" - " kill: (0000100)\n"; + " live in: (00000)\n" + " live out: (00000)\n" + " kill: (00010)\n"; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, @@ -432,45 +438,44 @@ TEST(LivenessTest, Loop5) { TEST(LivenessTest, Loop6) { // Bitsets are made of: - // (constant0, constant4, constant5, phi in block 2, equal in block 2, equal in block 3, - // phi in block 8) + // (constant0, constant4, constant5, phi in block 2, phi in block 8) const char* expected = "Block 0\n" - " live in: (0000000)\n" - " live out: (1110000)\n" - " kill: (1110000)\n" + " live in: (00000)\n" + " live out: (11100)\n" + " kill: (11100)\n" "Block 1\n" - " live in: (1110000)\n" - " live out: (0110000)\n" - " kill: (0000000)\n" + " live in: (11100)\n" + " live out: (01100)\n" + " kill: (00000)\n" "Block 2\n" // loop header - " live in: (0110000)\n" - " live out: (0111000)\n" - " kill: (0001100)\n" + " live in: (01100)\n" + " live out: (01110)\n" + " kill: (00010)\n" "Block 3\n" - " live in: (0110000)\n" - " live out: (0110000)\n" - " kill: (0000010)\n" + " live in: (01100)\n" + " live out: (01100)\n" + " kill: (00000)\n" "Block 4\n" // original back edge - " live in: (0110000)\n" - " live out: (0110000)\n" - " kill: (0000000)\n" + " live in: (01100)\n" + " live out: (01100)\n" + " kill: (00000)\n" "Block 5\n" // original back edge - " live in: (0110000)\n" - " live out: (0110000)\n" - " kill: (0000000)\n" + " live in: (01100)\n" + " live out: (01100)\n" + " kill: (00000)\n" "Block 6\n" // return block - " live in: (0001000)\n" - " live out: (0000000)\n" - " kill: (0000000)\n" + " live in: (00010)\n" + " live out: (00000)\n" + " kill: (00000)\n" "Block 7\n" // exit block - " live in: (0000000)\n" - " live out: (0000000)\n" - " kill: (0000000)\n" + " live in: (00000)\n" + " live out: (00000)\n" + " kill: (00000)\n" "Block 8\n" // synthesized back edge - " live in: (0110000)\n" - " live out: (0110000)\n" - " kill: (0000001)\n"; + " live in: (01100)\n" + " live out: (01100)\n" + " kill: (00001)\n"; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, @@ -488,45 +493,44 @@ TEST(LivenessTest, Loop6) { TEST(LivenessTest, Loop7) { // Bitsets are made of: - // (constant0, constant4, constant5, phi in block 2, equal in block 2, equal in block 3, - // phi in block 6) + // (constant0, constant4, constant5, phi in block 2, phi in block 6) const char* expected = "Block 0\n" - " live in: (0000000)\n" - " live out: (1110000)\n" - " kill: (1110000)\n" + " live in: (00000)\n" + " live out: (11100)\n" + " kill: (11100)\n" "Block 1\n" - " live in: (1110000)\n" - " live out: (0110000)\n" - " kill: (0000000)\n" + " live in: (11100)\n" + " live out: (01100)\n" + " kill: (00000)\n" "Block 2\n" // loop header - " live in: (0110000)\n" - " live out: (0111000)\n" - " kill: (0001100)\n" + " live in: (01100)\n" + " live out: (01110)\n" + " kill: (00010)\n" "Block 3\n" - " live in: (0110000)\n" - " live out: (0110000)\n" - " kill: (0000010)\n" + " live in: (01100)\n" + " live out: (01100)\n" + " kill: (00000)\n" "Block 4\n" // loop exit - " live in: (0010000)\n" - " live out: (0000000)\n" - " kill: (0000000)\n" + " live in: (00100)\n" + " live out: (00000)\n" + " kill: (00000)\n" "Block 5\n" // back edge - " live in: (0110000)\n" - " live out: (0110000)\n" - " kill: (0000000)\n" + " live in: (01100)\n" + " live out: (01100)\n" + " kill: (00000)\n" "Block 6\n" // return block - " live in: (0000000)\n" - " live out: (0000000)\n" - " kill: (0000001)\n" + " live in: (00000)\n" + " live out: (00000)\n" + " kill: (00001)\n" "Block 7\n" // exit block - " live in: (0000000)\n" - " live out: (0000000)\n" - " kill: (0000000)\n" + " live in: (00000)\n" + " live out: (00000)\n" + " kill: (00000)\n" "Block 8\n" // synthesized block to avoid critical edge. - " live in: (0001000)\n" - " live out: (0000000)\n" - " kill: (0000000)\n"; + " live in: (00010)\n" + " live out: (00000)\n" + " kill: (00000)\n"; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 92920845c3..e87b044cc9 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -18,6 +18,8 @@ #define ART_COMPILER_OPTIMIZING_NODES_H_ #include "locations.h" +#include "offsets.h" +#include "primitive.h" #include "utils/allocation.h" #include "utils/arena_bit_vector.h" #include "utils/growable_array.h" @@ -75,6 +77,7 @@ class HGraph : public ArenaObject { maximum_number_of_out_vregs_(0), number_of_vregs_(0), number_of_in_vregs_(0), + number_of_temporaries_(0), current_instruction_id_(0) {} ArenaAllocator* GetArena() const { return arena_; } @@ -112,6 +115,14 @@ class HGraph : public ArenaObject { maximum_number_of_out_vregs_ = std::max(new_value, maximum_number_of_out_vregs_); } + void UpdateNumberOfTemporaries(size_t count) { + number_of_temporaries_ = std::max(count, number_of_temporaries_); + } + + size_t GetNumberOfTemporaries() const { + return number_of_temporaries_; + } + void SetNumberOfVRegs(uint16_t number_of_vregs) { number_of_vregs_ = number_of_vregs; } @@ -128,6 +139,10 @@ class HGraph : public ArenaObject { return number_of_in_vregs_; } + uint16_t GetNumberOfLocalVRegs() const { + return number_of_vregs_ - number_of_in_vregs_; + } + const GrowableArray<HBasicBlock*>& GetReversePostOrder() const { return reverse_post_order_; } @@ -163,6 +178,9 @@ class HGraph : public ArenaObject { // The number of virtual registers used by parameters of this method. uint16_t number_of_in_vregs_; + // The number of temporaries that will be needed for the baseline compiler. + size_t number_of_temporaries_; + // The current id to assign to a newly added instruction. See HInstruction.id_. int current_instruction_id_; @@ -364,6 +382,8 @@ class HBasicBlock : public ArenaObject { } } + bool IsInLoop() const { return loop_information_ != nullptr; } + // Returns wheter this block dominates the blocked passed as parameter. bool Dominates(HBasicBlock* block) const; @@ -415,6 +435,10 @@ class HBasicBlock : public ArenaObject { M(StoreLocal) \ M(Sub) \ M(Compare) \ + M(InstanceFieldGet) \ + M(InstanceFieldSet) \ + M(NullCheck) \ + M(Temporary) \ #define FORWARD_DECLARATION(type) class H##type; @@ -468,6 +492,8 @@ class HInstruction : public ArenaObject { HBasicBlock* GetBlock() const { return block_; } void SetBlock(HBasicBlock* block) { block_ = block; } + bool IsInBlock() const { return block_ != nullptr; } + bool IsInLoop() const { return block_->IsInLoop(); } virtual size_t InputCount() const = 0; virtual HInstruction* InputAt(size_t i) const = 0; @@ -496,6 +522,7 @@ class HInstruction : public ArenaObject { HUseListNode<HEnvironment>* GetEnvUses() const { return env_uses_; } bool HasUses() const { return uses_ != nullptr || env_uses_ != nullptr; } + bool HasEnvironmentUses() const { return env_uses_ != nullptr; } size_t NumberOfUses() const { // TODO: Optimize this method if it is used outside of the HGraphVisualizer. @@ -1225,7 +1252,8 @@ class HPhi : public HInstruction { HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type) : inputs_(arena, number_of_inputs), reg_number_(reg_number), - type_(type) { + type_(type), + is_live_(false) { inputs_.SetSize(number_of_inputs); } @@ -1243,17 +1271,113 @@ class HPhi : public HInstruction { uint32_t GetRegNumber() const { return reg_number_; } + void SetDead() { is_live_ = false; } + void SetLive() { is_live_ = true; } + bool IsDead() const { return !is_live_; } + bool IsLive() const { return is_live_; } + DECLARE_INSTRUCTION(Phi); protected: GrowableArray<HInstruction*> inputs_; const uint32_t reg_number_; Primitive::Type type_; + bool is_live_; private: DISALLOW_COPY_AND_ASSIGN(HPhi); }; +class HNullCheck : public HExpression<1> { + public: + HNullCheck(HInstruction* value, uint32_t dex_pc) + : HExpression(value->GetType()), dex_pc_(dex_pc) { + SetRawInputAt(0, value); + } + + virtual bool NeedsEnvironment() const { return true; } + + uint32_t GetDexPc() const { return dex_pc_; } + + DECLARE_INSTRUCTION(NullCheck); + + private: + const uint32_t dex_pc_; + + DISALLOW_COPY_AND_ASSIGN(HNullCheck); +}; + +class FieldInfo : public ValueObject { + public: + explicit FieldInfo(MemberOffset field_offset) + : field_offset_(field_offset) {} + + MemberOffset GetFieldOffset() const { return field_offset_; } + + private: + const MemberOffset field_offset_; +}; + +class HInstanceFieldGet : public HExpression<1> { + public: + HInstanceFieldGet(HInstruction* value, + Primitive::Type field_type, + MemberOffset field_offset) + : HExpression(field_type), field_info_(field_offset) { + SetRawInputAt(0, value); + } + + MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } + + DECLARE_INSTRUCTION(InstanceFieldGet); + + private: + const FieldInfo field_info_; + + DISALLOW_COPY_AND_ASSIGN(HInstanceFieldGet); +}; + +class HInstanceFieldSet : public HTemplateInstruction<2> { + public: + HInstanceFieldSet(HInstruction* object, + HInstruction* value, + MemberOffset field_offset) + : field_info_(field_offset) { + SetRawInputAt(0, object); + SetRawInputAt(1, value); + } + + MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } + + DECLARE_INSTRUCTION(InstanceFieldSet); + + private: + const FieldInfo field_info_; + + DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet); +}; + +/** + * Some DEX instructions are folded into multiple HInstructions that need + * to stay live until the last HInstruction. This class + * is used as a marker for the baseline compiler to ensure its preceding + * HInstruction stays live. `index` is the temporary number that is used + * for knowing the stack offset where to store the instruction. + */ +class HTemporary : public HTemplateInstruction<0> { + public: + explicit HTemporary(size_t index) : index_(index) {} + + size_t GetIndex() const { return index_; } + + DECLARE_INSTRUCTION(Temporary); + + private: + const size_t index_; + + DISALLOW_COPY_AND_ASSIGN(HTemporary); +}; + class MoveOperands : public ArenaObject { public: MoveOperands(Location source, Location destination) diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index b4d7fff178..b621e510f3 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -25,6 +25,7 @@ #include "graph_visualizer.h" #include "nodes.h" #include "register_allocator.h" +#include "ssa_phi_elimination.h" #include "ssa_liveness_analysis.h" #include "utils/arena_allocator.h" @@ -101,7 +102,7 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite ArenaPool pool; ArenaAllocator arena(&pool); - HGraphBuilder builder(&arena, &dex_compilation_unit, &dex_file); + HGraphBuilder builder(&arena, &dex_compilation_unit, &dex_file, GetCompilerDriver()); HGraph* graph = builder.BuildGraph(*code_item); if (graph == nullptr) { @@ -129,8 +130,11 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite graph->BuildDominatorTree(); graph->TransformToSSA(); visualizer.DumpGraph("ssa"); - graph->FindNaturalLoops(); + + SsaRedundantPhiElimination(graph).Run(); + SsaDeadPhiElimination(graph).Run(); + SsaLivenessAnalysis liveness(*graph, codegen); liveness.Analyze(); visualizer.DumpGraph(kLivenessPassName); diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h index 7d4cd1a862..e35ff56c75 100644 --- a/compiler/optimizing/register_allocator.h +++ b/compiler/optimizing/register_allocator.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_REGISTER_ALLOCATOR_H_ #include "base/macros.h" +#include "primitive.h" #include "utils/growable_array.h" namespace art { diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 50ea00f4cd..fbdc0b9593 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -204,9 +204,12 @@ void SsaLivenessAnalysis::ComputeLiveRanges() { // All inputs of an instruction must be live. for (size_t i = 0, e = current->InputCount(); i < e; ++i) { HInstruction* input = current->InputAt(i); - DCHECK(input->HasSsaIndex()); - live_in->SetBit(input->GetSsaIndex()); - input->GetLiveInterval()->AddUse(current, i, false); + // Some instructions 'inline' their inputs, that is they do not need + // to be materialized. + if (input->HasSsaIndex()) { + live_in->SetBit(input->GetSsaIndex()); + input->GetLiveInterval()->AddUse(current, i, false); + } } if (current->HasEnvironment()) { diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc new file mode 100644 index 0000000000..13fa03f9a3 --- /dev/null +++ b/compiler/optimizing/ssa_phi_elimination.cc @@ -0,0 +1,126 @@ +/* + * 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 "ssa_phi_elimination.h" + +namespace art { + +void SsaDeadPhiElimination::Run() { + // Add to the worklist phis referenced by non-phi instructions. + for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { + HBasicBlock* block = it.Current(); + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { + HPhi* phi = it.Current()->AsPhi(); + if (phi->HasEnvironmentUses()) { + // TODO: Do we want to keep that phi alive? + continue; + } + for (HUseIterator<HInstruction> it(phi->GetUses()); !it.Done(); it.Advance()) { + HUseListNode<HInstruction>* current = it.Current(); + HInstruction* user = current->GetUser(); + if (!user->IsPhi()) { + worklist_.Add(phi); + phi->SetLive(); + } else { + phi->SetDead(); + } + } + } + } + + // Process the worklist by propagating liveness to phi inputs. + while (!worklist_.IsEmpty()) { + HPhi* phi = worklist_.Pop(); + for (HInputIterator it(phi); !it.Done(); it.Advance()) { + HInstruction* input = it.Current(); + if (input->IsPhi() && input->AsPhi()->IsDead()) { + worklist_.Add(input->AsPhi()); + input->AsPhi()->SetLive(); + } + } + } + + // Remove phis that are not live. Visit in post order to ensure + // we only remove phis with no users (dead phis might use dead phis). + for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) { + HBasicBlock* block = it.Current(); + HInstruction* current = block->GetFirstPhi(); + HInstruction* next = nullptr; + while (current != nullptr) { + next = current->GetNext(); + if (current->AsPhi()->IsDead()) { + block->RemovePhi(current->AsPhi()); + } + current = next; + } + } +} + +void SsaRedundantPhiElimination::Run() { + // Add all phis in the worklist. + for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { + HBasicBlock* block = it.Current(); + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { + worklist_.Add(it.Current()->AsPhi()); + } + } + + while (!worklist_.IsEmpty()) { + HPhi* phi = worklist_.Pop(); + + // If the phi has already been processed, continue. + if (!phi->IsInBlock()) { + continue; + } + + // Find if the inputs of the phi are the same instruction. + HInstruction* candidate = phi->InputAt(0); + // A loop phi cannot have itself as the first phi. + DCHECK_NE(phi, candidate); + + for (size_t i = 1; i < phi->InputCount(); ++i) { + HInstruction* input = phi->InputAt(i); + // For a loop phi, If the input is the phi, the phi is still candidate for + // elimination. + if (input != candidate && input != phi) { + candidate = nullptr; + break; + } + } + + // If the inputs are not the same, continue. + if (candidate == nullptr) { + continue; + } + + if (phi->IsInLoop()) { + // Because we're updating the users of this phi, we may have new + // phis candidate for elimination if this phi is in a loop. Add phis that + // used this phi to the worklist. + for (HUseIterator<HInstruction> it(phi->GetUses()); !it.Done(); it.Advance()) { + HUseListNode<HInstruction>* current = it.Current(); + HInstruction* user = current->GetUser(); + if (user->IsPhi()) { + worklist_.Add(user->AsPhi()); + } + } + } + phi->ReplaceWith(candidate); + phi->GetBlock()->RemovePhi(phi); + } +} + +} // namespace art diff --git a/compiler/optimizing/ssa_phi_elimination.h b/compiler/optimizing/ssa_phi_elimination.h new file mode 100644 index 0000000000..5274f09f3f --- /dev/null +++ b/compiler/optimizing/ssa_phi_elimination.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_SSA_PHI_ELIMINATION_H_ +#define ART_COMPILER_OPTIMIZING_SSA_PHI_ELIMINATION_H_ + +#include "nodes.h" + +namespace art { + +/** + * Optimization phase that removes dead phis from the graph. Dead phis are unused + * phis, or phis only used by other phis. + */ +class SsaDeadPhiElimination : public ValueObject { + public: + explicit SsaDeadPhiElimination(HGraph* graph) + : graph_(graph), worklist_(graph->GetArena(), kDefaultWorklistSize) {} + + void Run(); + + private: + HGraph* const graph_; + GrowableArray<HPhi*> worklist_; + + static constexpr size_t kDefaultWorklistSize = 8; + + DISALLOW_COPY_AND_ASSIGN(SsaDeadPhiElimination); +}; + +/** + * Removes redundant phis that may have been introduced when doing SSA conversion. + * For example, when entering a loop, we create phis for all live registers. These + * registers might be updated with the same value, or not updated at all. We can just + * replace the phi with the value when entering the loop. + */ +class SsaRedundantPhiElimination : public ValueObject { + public: + explicit SsaRedundantPhiElimination(HGraph* graph) + : graph_(graph), worklist_(graph->GetArena(), kDefaultWorklistSize) {} + + void Run(); + + private: + HGraph* const graph_; + GrowableArray<HPhi*> worklist_; + + static constexpr size_t kDefaultWorklistSize = 8; + + DISALLOW_COPY_AND_ASSIGN(SsaRedundantPhiElimination); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_SSA_PHI_ELIMINATION_H_ diff --git a/compiler/output_stream_test.cc b/compiler/output_stream_test.cc index 5fa0ccb143..315ca09e59 100644 --- a/compiler/output_stream_test.cc +++ b/compiler/output_stream_test.cc @@ -17,6 +17,7 @@ #include "file_output_stream.h" #include "vector_output_stream.h" +#include "base/unix_file/fd_file.h" #include "base/logging.h" #include "buffered_output_stream.h" #include "common_runtime_test.h" diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc index 009b227209..5b97ba0a02 100644 --- a/compiler/utils/arm64/assembler_arm64.cc +++ b/compiler/utils/arm64/assembler_arm64.cc @@ -595,8 +595,7 @@ void Arm64Assembler::LoadReferenceFromHandleScope(ManagedRegister m_out_reg, // FIXME: Who sets the flags here? LoadImmediate(out_reg.AsCoreRegister(), 0, EQ); } - ___ Cmp(reg_x(in_reg.AsCoreRegister()), 0); - ___ B(&exit, COND_OP(EQ)); + ___ Cbz(reg_x(in_reg.AsCoreRegister()), &exit); LoadFromOffset(out_reg.AsCoreRegister(), in_reg.AsCoreRegister(), 0); ___ Bind(&exit); } @@ -607,8 +606,7 @@ void Arm64Assembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjus Arm64Exception *current_exception = new Arm64Exception(scratch, stack_adjust); exception_blocks_.push_back(current_exception); LoadFromOffset(scratch.AsCoreRegister(), ETR, Thread::ExceptionOffset<8>().Int32Value()); - ___ Cmp(reg_x(scratch.AsCoreRegister()), 0); - ___ B(current_exception->Entry(), COND_OP(NE)); + ___ Cbnz(reg_x(scratch.AsCoreRegister()), current_exception->Entry()); } void Arm64Assembler::EmitExceptionPoll(Arm64Exception *exception) { diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 4d5d613015..78738d8934 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -1671,16 +1671,31 @@ void X86_64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, const std::vector<ManagedRegister>& spill_regs, const ManagedRegisterEntrySpills& entry_spills) { CHECK_ALIGNED(frame_size, kStackAlignment); + int gpr_count = 0; for (int i = spill_regs.size() - 1; i >= 0; --i) { - pushq(spill_regs.at(i).AsX86_64().AsCpuRegister()); + x86_64::X86_64ManagedRegister spill = spill_regs.at(i).AsX86_64(); + if (spill.IsCpuRegister()) { + pushq(spill.AsCpuRegister()); + gpr_count++; + } } // return address then method on stack - addq(CpuRegister(RSP), Immediate(-static_cast<int64_t>(frame_size) + (spill_regs.size() * kFramePointerSize) + - sizeof(StackReference<mirror::ArtMethod>) /*method*/ + - kFramePointerSize /*return address*/)); + int64_t rest_of_frame = static_cast<int64_t>(frame_size) + - (gpr_count * kFramePointerSize) + - kFramePointerSize /*return address*/; + subq(CpuRegister(RSP), Immediate(rest_of_frame)); + // spill xmms + int64_t offset = rest_of_frame; + for (int i = spill_regs.size() - 1; i >= 0; --i) { + x86_64::X86_64ManagedRegister spill = spill_regs.at(i).AsX86_64(); + if (spill.IsXmmRegister()) { + offset -= sizeof(double); + movsd(Address(CpuRegister(RSP), offset), spill.AsXmmRegister()); + } + } DCHECK_EQ(4U, sizeof(StackReference<mirror::ArtMethod>)); - subq(CpuRegister(RSP), Immediate(4)); + movl(Address(CpuRegister(RSP), 0), method_reg.AsX86_64().AsCpuRegister()); for (size_t i = 0; i < entry_spills.size(); ++i) { @@ -1707,9 +1722,24 @@ void X86_64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, void X86_64Assembler::RemoveFrame(size_t frame_size, const std::vector<ManagedRegister>& spill_regs) { CHECK_ALIGNED(frame_size, kStackAlignment); - addq(CpuRegister(RSP), Immediate(static_cast<int64_t>(frame_size) - (spill_regs.size() * kFramePointerSize) - kFramePointerSize)); + int gpr_count = 0; + // unspill xmms + int64_t offset = static_cast<int64_t>(frame_size) - (spill_regs.size() * kFramePointerSize) - 2 * kFramePointerSize; for (size_t i = 0; i < spill_regs.size(); ++i) { - popq(spill_regs.at(i).AsX86_64().AsCpuRegister()); + x86_64::X86_64ManagedRegister spill = spill_regs.at(i).AsX86_64(); + if (spill.IsXmmRegister()) { + offset += sizeof(double); + movsd(spill.AsXmmRegister(), Address(CpuRegister(RSP), offset)); + } else { + gpr_count++; + } + } + addq(CpuRegister(RSP), Immediate(static_cast<int64_t>(frame_size) - (gpr_count * kFramePointerSize) - kFramePointerSize)); + for (size_t i = 0; i < spill_regs.size(); ++i) { + x86_64::X86_64ManagedRegister spill = spill_regs.at(i).AsX86_64(); + if (spill.IsCpuRegister()) { + popq(spill.AsCpuRegister()); + } } ret(); } diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index f7bad8b057..dc1758ffdf 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -246,11 +246,9 @@ std::string buildframe_test_fn(x86_64::X86_64Assembler* assembler) { str << "pushq %rsi\n"; str << "pushq %r10\n"; // 2) Move down the stack pointer. - ssize_t displacement = -static_cast<ssize_t>(frame_size) + spill_regs.size() * 8 + - sizeof(StackReference<mirror::ArtMethod>) + 8; - str << "addq $" << displacement << ", %rsp\n"; - // 3) Make space for method reference, and store it. - str << "subq $4, %rsp\n"; + ssize_t displacement = static_cast<ssize_t>(frame_size) - (spill_regs.size() * 8 + 8); + str << "subq $" << displacement << ", %rsp\n"; + // 3) Store method reference. str << "movl %edi, (%rsp)\n"; // 4) Entry spills. str << "movq %rax, " << frame_size + 0 << "(%rsp)\n"; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 80e77245fc..6d861d4bfe 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -40,7 +40,8 @@ #include "dex_file-inl.h" #include "dex/pass_driver_me_opts.h" #include "dex/verification_results.h" -#include "driver/compiler_callbacks_impl.h" +#include "dex/quick_compiler_callbacks.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "elf_fixup.h" @@ -55,7 +56,6 @@ #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "oat_writer.h" -#include "object_utils.h" #include "os.h" #include "runtime.h" #include "ScopedLocalRef.h" @@ -232,7 +232,7 @@ static void Usage(const char* fmt, ...) { class Dex2Oat { public: static bool Create(Dex2Oat** p_dex2oat, - const Runtime::Options& runtime_options, + const RuntimeOptions& runtime_options, const CompilerOptions& compiler_options, Compiler::Kind compiler_kind, InstructionSet instruction_set, @@ -336,7 +336,10 @@ class Dex2Oat { bool dump_passes, TimingLogger& timings, CumulativeLogger& compiler_phases_timings, - std::string profile_file) { + std::string profile_file, + SafeMap<std::string, std::string>* key_value_store) { + CHECK(key_value_store != nullptr); + // Handle and ClassLoader creation needs to come after Runtime::Create jobject class_loader = nullptr; Thread* self = Thread::Current(); @@ -356,18 +359,18 @@ class Dex2Oat { } std::unique_ptr<CompilerDriver> driver(new CompilerDriver(compiler_options_, - verification_results_, - method_inliner_map_, - compiler_kind_, - instruction_set_, - instruction_set_features_, - image, - image_classes.release(), - thread_count_, - dump_stats, - dump_passes, - &compiler_phases_timings, - profile_file)); + verification_results_, + method_inliner_map_, + compiler_kind_, + instruction_set_, + instruction_set_features_, + image, + image_classes.release(), + thread_count_, + dump_stats, + dump_passes, + &compiler_phases_timings, + profile_file)); driver->GetCompiler()->SetBitcodeFileName(*driver.get(), bitcode_filename); @@ -386,11 +389,15 @@ class Dex2Oat { image_file_location = image_space->GetImageFilename(); } + if (!image_file_location.empty()) { + key_value_store->Put(OatHeader::kImageLocationKey, image_file_location); + } + OatWriter oat_writer(dex_files, image_file_location_oat_checksum, image_file_location_oat_data_begin, - image_file_location, driver.get(), - &timings); + &timings, + key_value_store); t2.NewTiming("Writing ELF"); if (!driver->WriteElf(android_root, is_host, dex_files, &oat_writer, oat_file)) { @@ -452,7 +459,7 @@ class Dex2Oat { CHECK(method_inliner_map != nullptr); } - bool CreateRuntime(const Runtime::Options& runtime_options, InstructionSet instruction_set) + bool CreateRuntime(const RuntimeOptions& runtime_options, InstructionSet instruction_set) SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) { if (!Runtime::Create(runtime_options, false)) { LOG(ERROR) << "Failed to create runtime"; @@ -467,6 +474,7 @@ class Dex2Oat { } } runtime->GetClassLinker()->FixupDexCaches(runtime->GetResolutionMethod()); + runtime->GetClassLinker()->RunRootClinits(); runtime_ = runtime; return true; } @@ -733,20 +741,6 @@ void ParseDouble(const std::string& option, char after_char, *parsed_value = value; } -void CheckExplicitCheckOptions(InstructionSet isa, bool* explicit_null_checks, - bool* explicit_so_checks, bool* explicit_suspend_checks) { - switch (isa) { - case kArm: - case kThumb2: - break; // All checks implemented, leave as is. - - default: // No checks implemented, reset all to explicit checks. - *explicit_null_checks = true; - *explicit_so_checks = true; - *explicit_suspend_checks = true; - } -} - static int dex2oat(int argc, char** argv) { #if defined(__linux__) && defined(__arm__) int major, minor; @@ -830,10 +824,10 @@ static int dex2oat(int argc, char** argv) { bool watch_dog_enabled = !kIsTargetBuild; bool generate_gdb_information = kIsDebugBuild; - bool explicit_null_checks = true; - bool explicit_so_checks = true; - bool explicit_suspend_checks = true; - bool has_explicit_checks_options = false; + // Checks are all explicit until we know the architecture. + bool implicit_null_checks = false; + bool implicit_so_checks = false; + bool implicit_suspend_checks = false; for (int i = 0; i < argc; i++) { const StringPiece option(argv[i]); @@ -1010,31 +1004,6 @@ static int dex2oat(int argc, char** argv) { } else if (option.starts_with("--dump-cfg-passes=")) { std::string dump_passes = option.substr(strlen("--dump-cfg-passes=")).data(); PassDriverMEOpts::SetDumpPassList(dump_passes); - } else if (option.starts_with("--implicit-checks=")) { - std::string checks = option.substr(strlen("--implicit-checks=")).data(); - std::vector<std::string> checkvec; - Split(checks, ',', checkvec); - for (auto& str : checkvec) { - std::string val = Trim(str); - if (val == "none") { - explicit_null_checks = true; - explicit_so_checks = true; - explicit_suspend_checks = true; - } else if (val == "null") { - explicit_null_checks = false; - } else if (val == "suspend") { - explicit_suspend_checks = false; - } else if (val == "stack") { - explicit_so_checks = false; - } else if (val == "all") { - explicit_null_checks = false; - explicit_so_checks = false; - explicit_suspend_checks = false; - } else { - Usage("--implicit-checks passed non-recognized value %s", val.c_str()); - } - } - has_explicit_checks_options = true; } else if (option == "--include-patch-information") { include_patch_information = true; explicit_include_patch_information = true; @@ -1167,31 +1136,43 @@ static int dex2oat(int argc, char** argv) { Usage("Unknown --compiler-filter value %s", compiler_filter_string); } - CheckExplicitCheckOptions(instruction_set, &explicit_null_checks, &explicit_so_checks, - &explicit_suspend_checks); - if (!explicit_include_patch_information) { include_patch_information = (compiler_kind == Compiler::kQuick && CompilerOptions::kDefaultIncludePatchInformation); } - CompilerOptions compiler_options(compiler_filter, - huge_method_threshold, - large_method_threshold, - small_method_threshold, - tiny_method_threshold, - num_dex_methods_threshold, - generate_gdb_information, - include_patch_information, - top_k_profile_threshold, - include_debug_symbols, - explicit_null_checks, - explicit_so_checks, - explicit_suspend_checks + // Set the compilation target's implicit checks options. + switch (instruction_set) { + case kArm: + case kThumb2: + case kX86: + implicit_null_checks = true; + implicit_so_checks = true; + break; + + default: + // Defaults are correct. + break; + } + + std::unique_ptr<CompilerOptions> compiler_options(new CompilerOptions(compiler_filter, + huge_method_threshold, + large_method_threshold, + small_method_threshold, + tiny_method_threshold, + num_dex_methods_threshold, + generate_gdb_information, + include_patch_information, + top_k_profile_threshold, + include_debug_symbols, + implicit_null_checks, + implicit_so_checks, + implicit_suspend_checks #ifdef ART_SEA_IR_MODE - , compiler_options.sea_ir_ = true; + , compiler_options.sea_ir_ = + true; #endif - ); // NOLINT(whitespace/parens) + )); // NOLINT(whitespace/parens) // Done with usage checks, enable watchdog if requested WatchDog watch_dog(watch_dog_enabled); @@ -1220,7 +1201,7 @@ static int dex2oat(int argc, char** argv) { timings.StartTiming("dex2oat Setup"); LOG(INFO) << CommandLine(); - Runtime::Options runtime_options; + RuntimeOptions runtime_options; std::vector<const DexFile*> boot_class_path; if (boot_image_option.empty()) { size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path); @@ -1236,9 +1217,10 @@ static int dex2oat(int argc, char** argv) { runtime_options.push_back(std::make_pair(runtime_args[i], nullptr)); } - VerificationResults verification_results(&compiler_options); + std::unique_ptr<VerificationResults> verification_results(new VerificationResults( + compiler_options.get())); DexFileToMethodInlinerMap method_inliner_map; - CompilerCallbacksImpl callbacks(&verification_results, &method_inliner_map); + 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", @@ -1247,11 +1229,11 @@ static int dex2oat(int argc, char** argv) { Dex2Oat* p_dex2oat; if (!Dex2Oat::Create(&p_dex2oat, runtime_options, - compiler_options, + *compiler_options, compiler_kind, instruction_set, instruction_set_features, - &verification_results, + verification_results.get(), &method_inliner_map, thread_count)) { LOG(ERROR) << "Failed to create dex2oat"; @@ -1259,27 +1241,6 @@ static int dex2oat(int argc, char** argv) { } std::unique_ptr<Dex2Oat> dex2oat(p_dex2oat); - // TODO: Not sure whether it's a good idea to allow anything else but the runtime option in - // this case at all, as we'll have to throw away produced code for a mismatch. - if (!has_explicit_checks_options) { - bool cross_compiling = true; - switch (kRuntimeISA) { - case kArm: - case kThumb2: - cross_compiling = instruction_set != kArm && instruction_set != kThumb2; - break; - default: - cross_compiling = instruction_set != kRuntimeISA; - break; - } - if (!cross_compiling) { - Runtime* runtime = Runtime::Current(); - compiler_options.SetExplicitNullChecks(runtime->ExplicitNullChecks()); - compiler_options.SetExplicitStackOverflowChecks(runtime->ExplicitStackOverflowChecks()); - compiler_options.SetExplicitSuspendChecks(runtime->ExplicitSuspendChecks()); - } - } - // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start, // give it away now so that we don't starve GC. Thread* self = Thread::Current(); @@ -1365,32 +1326,50 @@ static int dex2oat(int argc, char** argv) { * If we're not in interpret-only or verify-none mode, go ahead and compile small applications. * Don't bother to check if we're doing the image. */ - if (!image && compiler_options.IsCompilationEnabled()) { + if (!image && compiler_options->IsCompilationEnabled()) { size_t num_methods = 0; for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != nullptr); num_methods += dex_file->NumMethodIds(); } - if (num_methods <= compiler_options.GetNumDexMethodsThreshold()) { - compiler_options.SetCompilerFilter(CompilerOptions::kSpeed); + if (num_methods <= compiler_options->GetNumDexMethodsThreshold()) { + compiler_options->SetCompilerFilter(CompilerOptions::kSpeed); VLOG(compiler) << "Below method threshold, compiling anyways"; } } + // Fill some values into the key-value store for the oat header. + std::unique_ptr<SafeMap<std::string, std::string> > key_value_store( + new SafeMap<std::string, std::string>()); + + // Insert some compiler things. + std::ostringstream oss; + for (int i = 0; i < argc; ++i) { + if (i > 0) { + oss << ' '; + } + oss << argv[i]; + } + key_value_store->Put(OatHeader::kDex2OatCmdLineKey, oss.str()); + oss.str(""); // Reset. + oss << kRuntimeISA; + key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str()); + std::unique_ptr<const CompilerDriver> compiler(dex2oat->CreateOatFile(boot_image_option, - android_root, - is_host, - dex_files, - oat_file.get(), - bitcode_filename, - image, - image_classes, - dump_stats, - dump_passes, - timings, - compiler_phases_timings, - profile_file)); + android_root, + is_host, + dex_files, + oat_file.get(), + bitcode_filename, + image, + image_classes, + dump_stats, + dump_passes, + timings, + compiler_phases_timings, + profile_file, + key_value_store.get())); if (compiler.get() == nullptr) { LOG(ERROR) << "Failed to create oat file: " << oat_location; diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index a6f9a8a22b..101a55d377 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -125,10 +125,11 @@ static void DumpIndexReg(std::ostream& os, uint8_t rex, uint8_t reg) { DumpAddrReg(os, rex, reg_num); } -static void DumpOpcodeReg(std::ostream& os, uint8_t rex, uint8_t reg) { +static void DumpOpcodeReg(std::ostream& os, uint8_t rex, uint8_t reg, + bool byte_operand, uint8_t size_override) { bool rex_b = (rex & REX_B) != 0; size_t reg_num = rex_b ? (reg + 8) : reg; - DumpReg0(os, rex, reg_num, false, 0); + DumpReg0(os, rex, reg_num, byte_operand, size_override); } enum SegmentPrefix { @@ -955,6 +956,7 @@ DISASSEMBLER_ENTRY(cmp, immediate_bytes = 1; byte_operand = true; reg_in_opcode = true; + byte_operand = true; break; case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF: if (rex == 0x48) { @@ -1079,7 +1081,7 @@ DISASSEMBLER_ENTRY(cmp, uint8_t rex_w = (supports_rex_ && target_specific) ? (rex | 0x48) : rex; if (reg_in_opcode) { DCHECK(!has_modrm); - DumpOpcodeReg(args, rex_w, *instr & 0x7); + DumpOpcodeReg(args, rex_w, *instr & 0x7, byte_operand, prefix[2]); } instr++; uint32_t address_bits = 0; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 12970fcaab..b8f20f3650 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -29,6 +29,7 @@ #include "dex_file-inl.h" #include "dex_instruction.h" #include "disassembler.h" +#include "field_helper.h" #include "gc_map.h" #include "gc/space/image_space.h" #include "gc/space/large_object_space.h" @@ -45,7 +46,6 @@ #include "noop_compiler_callbacks.h" #include "oat.h" #include "oat_file-inl.h" -#include "object_utils.h" #include "os.h" #include "runtime.h" #include "safe_map.h" @@ -171,10 +171,18 @@ class OatDumper { os << "IMAGE FILE LOCATION OAT BEGIN:\n"; os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatDataBegin()); - os << "IMAGE FILE LOCATION:\n"; - const std::string image_file_location(oat_header.GetImageFileLocation()); - os << image_file_location; - os << "\n\n"; + // Print the key-value store. + { + os << "KEY VALUE STORE:\n"; + size_t index = 0; + const char* key; + const char* value; + while (oat_header.GetStoreKeyValuePairByIndex(index, &key, &value)) { + os << key << " = " << value << "\n"; + index++; + } + os << "\n"; + } os << "BEGIN:\n"; os << reinterpret_cast<const void*>(oat_file_.Begin()) << "\n\n"; @@ -976,7 +984,7 @@ class ImageDumper { const void* GetQuickOatCodeBegin(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const void* quick_code = m->GetEntryPointFromQuickCompiledCode(); - if (quick_code == GetQuickResolutionTrampoline(Runtime::Current()->GetClassLinker())) { + if (quick_code == Runtime::Current()->GetClassLinker()->GetQuickResolutionTrampoline()) { quick_code = oat_dumper_->GetQuickOatCode(m); } if (oat_dumper_->GetInstructionSet() == kThumb2) { @@ -1540,7 +1548,7 @@ static int oatdump(int argc, char** argv) { return EXIT_SUCCESS; } - Runtime::Options options; + RuntimeOptions options; std::string image_option; std::string oat_option; std::string boot_image_option; diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index dcf8c70501..85b4e6df32 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -26,6 +26,7 @@ #include "base/stringprintf.h" #include "elf_utils.h" #include "elf_file.h" +#include "gc/space/image_space.h" #include "image.h" #include "instruction_set.h" #include "mirror/art_field.h" @@ -92,7 +93,7 @@ bool PatchOat::Patch(const std::string& image_location, off_t delta, } // Set up the runtime - Runtime::Options options; + RuntimeOptions options; NoopCompilerCallbacks callbacks; options.push_back(std::make_pair("compilercallbacks", &callbacks)); std::string img = "-Ximage:" + image_location; @@ -176,7 +177,7 @@ bool PatchOat::Patch(const File* input_oat, const std::string& image_location, o } // Set up the runtime - Runtime::Options options; + RuntimeOptions options; NoopCompilerCallbacks callbacks; options.push_back(std::make_pair("compilercallbacks", &callbacks)); std::string img = "-Ximage:" + image_location; diff --git a/runtime/Android.mk b/runtime/Android.mk index 7f5cf0c56f..d2fc2298e2 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -19,300 +19,304 @@ LOCAL_PATH := $(call my-dir) include art/build/Android.common_build.mk LIBART_COMMON_SRC_FILES := \ - atomic.cc.arm \ - barrier.cc \ - base/allocator.cc \ - base/bit_vector.cc \ - base/hex_dump.cc \ - base/logging.cc \ - base/mutex.cc \ - base/scoped_flock.cc \ - base/stringpiece.cc \ - base/stringprintf.cc \ - base/timing_logger.cc \ - base/unix_file/fd_file.cc \ - base/unix_file/mapped_file.cc \ - base/unix_file/null_file.cc \ - base/unix_file/random_access_file_utils.cc \ - base/unix_file/string_file.cc \ - check_jni.cc \ - class_linker.cc \ - common_throws.cc \ - debugger.cc \ - dex_file.cc \ - dex_file_verifier.cc \ - dex_instruction.cc \ - elf_file.cc \ - gc/allocator/dlmalloc.cc \ - gc/allocator/rosalloc.cc \ - gc/accounting/card_table.cc \ - gc/accounting/gc_allocator.cc \ - gc/accounting/heap_bitmap.cc \ - gc/accounting/mod_union_table.cc \ - gc/accounting/remembered_set.cc \ - gc/accounting/space_bitmap.cc \ - gc/collector/concurrent_copying.cc \ - gc/collector/garbage_collector.cc \ - gc/collector/immune_region.cc \ - gc/collector/mark_compact.cc \ - gc/collector/mark_sweep.cc \ - gc/collector/partial_mark_sweep.cc \ - gc/collector/semi_space.cc \ - gc/collector/sticky_mark_sweep.cc \ - gc/gc_cause.cc \ - gc/heap.cc \ - gc/reference_processor.cc \ - gc/reference_queue.cc \ - gc/space/bump_pointer_space.cc \ - gc/space/dlmalloc_space.cc \ - gc/space/image_space.cc \ - gc/space/large_object_space.cc \ - gc/space/malloc_space.cc \ - gc/space/rosalloc_space.cc \ - gc/space/space.cc \ - gc/space/zygote_space.cc \ - hprof/hprof.cc \ - image.cc \ - indirect_reference_table.cc \ - instruction_set.cc \ - instrumentation.cc \ - intern_table.cc \ - interpreter/interpreter.cc \ - interpreter/interpreter_common.cc \ - interpreter/interpreter_switch_impl.cc \ - jdwp/jdwp_event.cc \ - jdwp/jdwp_expand_buf.cc \ - jdwp/jdwp_handler.cc \ - jdwp/jdwp_main.cc \ - jdwp/jdwp_request.cc \ - jdwp/jdwp_socket.cc \ - jdwp/object_registry.cc \ - jni_internal.cc \ - jobject_comparator.cc \ - mem_map.cc \ - memory_region.cc \ - mirror/art_field.cc \ - mirror/art_method.cc \ - mirror/array.cc \ - mirror/class.cc \ - mirror/dex_cache.cc \ - mirror/object.cc \ - mirror/stack_trace_element.cc \ - mirror/string.cc \ - mirror/throwable.cc \ - monitor.cc \ - native/dalvik_system_DexFile.cc \ - native/dalvik_system_VMDebug.cc \ - native/dalvik_system_VMRuntime.cc \ - native/dalvik_system_VMStack.cc \ - native/dalvik_system_ZygoteHooks.cc \ - native/java_lang_Class.cc \ - native/java_lang_DexCache.cc \ - native/java_lang_Object.cc \ - native/java_lang_Runtime.cc \ - native/java_lang_String.cc \ - native/java_lang_System.cc \ - native/java_lang_Thread.cc \ - native/java_lang_Throwable.cc \ - native/java_lang_VMClassLoader.cc \ - native/java_lang_ref_Reference.cc \ - native/java_lang_reflect_Array.cc \ - native/java_lang_reflect_Constructor.cc \ - native/java_lang_reflect_Field.cc \ - native/java_lang_reflect_Method.cc \ - native/java_lang_reflect_Proxy.cc \ - native/java_util_concurrent_atomic_AtomicLong.cc \ - native/org_apache_harmony_dalvik_ddmc_DdmServer.cc \ - native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc \ - native/sun_misc_Unsafe.cc \ - oat.cc \ - oat_file.cc \ - offsets.cc \ - os_linux.cc \ - parsed_options.cc \ - primitive.cc \ - quick_exception_handler.cc \ - quick/inline_method_analyser.cc \ - reference_table.cc \ - reflection.cc \ - runtime.cc \ - signal_catcher.cc \ - stack.cc \ - thread.cc \ - thread_list.cc \ - thread_pool.cc \ - throw_location.cc \ - trace.cc \ - transaction.cc \ - profiler.cc \ - fault_handler.cc \ - utf.cc \ - utils.cc \ - verifier/dex_gc_map.cc \ - verifier/instruction_flags.cc \ - verifier/method_verifier.cc \ - verifier/reg_type.cc \ - verifier/reg_type_cache.cc \ - verifier/register_line.cc \ - well_known_classes.cc \ - zip_archive.cc + atomic.cc.arm \ + barrier.cc \ + base/allocator.cc \ + base/bit_vector.cc \ + base/hex_dump.cc \ + base/logging.cc \ + base/mutex.cc \ + base/scoped_flock.cc \ + base/stringpiece.cc \ + base/stringprintf.cc \ + base/timing_logger.cc \ + base/unix_file/fd_file.cc \ + base/unix_file/mapped_file.cc \ + base/unix_file/null_file.cc \ + base/unix_file/random_access_file_utils.cc \ + base/unix_file/string_file.cc \ + check_jni.cc \ + class_linker.cc \ + common_throws.cc \ + debugger.cc \ + dex_file.cc \ + dex_file_verifier.cc \ + dex_instruction.cc \ + elf_file.cc \ + field_helper.cc \ + gc/allocator/dlmalloc.cc \ + gc/allocator/rosalloc.cc \ + gc/accounting/card_table.cc \ + gc/accounting/gc_allocator.cc \ + gc/accounting/heap_bitmap.cc \ + gc/accounting/mod_union_table.cc \ + gc/accounting/remembered_set.cc \ + gc/accounting/space_bitmap.cc \ + gc/collector/concurrent_copying.cc \ + gc/collector/garbage_collector.cc \ + gc/collector/immune_region.cc \ + gc/collector/mark_compact.cc \ + gc/collector/mark_sweep.cc \ + gc/collector/partial_mark_sweep.cc \ + gc/collector/semi_space.cc \ + gc/collector/sticky_mark_sweep.cc \ + gc/gc_cause.cc \ + gc/heap.cc \ + gc/reference_processor.cc \ + gc/reference_queue.cc \ + gc/space/bump_pointer_space.cc \ + gc/space/dlmalloc_space.cc \ + gc/space/image_space.cc \ + gc/space/large_object_space.cc \ + gc/space/malloc_space.cc \ + gc/space/rosalloc_space.cc \ + gc/space/space.cc \ + gc/space/zygote_space.cc \ + hprof/hprof.cc \ + image.cc \ + indirect_reference_table.cc \ + instruction_set.cc \ + instrumentation.cc \ + intern_table.cc \ + interpreter/interpreter.cc \ + interpreter/interpreter_common.cc \ + interpreter/interpreter_switch_impl.cc \ + jdwp/jdwp_event.cc \ + jdwp/jdwp_expand_buf.cc \ + jdwp/jdwp_handler.cc \ + jdwp/jdwp_main.cc \ + jdwp/jdwp_request.cc \ + jdwp/jdwp_socket.cc \ + jdwp/object_registry.cc \ + jni_internal.cc \ + jobject_comparator.cc \ + mem_map.cc \ + memory_region.cc \ + method_helper.cc \ + mirror/art_field.cc \ + mirror/art_method.cc \ + mirror/array.cc \ + mirror/class.cc \ + mirror/dex_cache.cc \ + mirror/object.cc \ + mirror/reference.cc \ + mirror/stack_trace_element.cc \ + mirror/string.cc \ + mirror/throwable.cc \ + monitor.cc \ + native/dalvik_system_DexFile.cc \ + native/dalvik_system_VMDebug.cc \ + native/dalvik_system_VMRuntime.cc \ + native/dalvik_system_VMStack.cc \ + native/dalvik_system_ZygoteHooks.cc \ + native/java_lang_Class.cc \ + native/java_lang_DexCache.cc \ + native/java_lang_Object.cc \ + native/java_lang_Runtime.cc \ + native/java_lang_String.cc \ + native/java_lang_System.cc \ + native/java_lang_Thread.cc \ + native/java_lang_Throwable.cc \ + native/java_lang_VMClassLoader.cc \ + native/java_lang_ref_Reference.cc \ + native/java_lang_reflect_Array.cc \ + native/java_lang_reflect_Constructor.cc \ + native/java_lang_reflect_Field.cc \ + native/java_lang_reflect_Method.cc \ + native/java_lang_reflect_Proxy.cc \ + native/java_util_concurrent_atomic_AtomicLong.cc \ + native/org_apache_harmony_dalvik_ddmc_DdmServer.cc \ + native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc \ + native/sun_misc_Unsafe.cc \ + oat.cc \ + oat_file.cc \ + object_lock.cc \ + offsets.cc \ + os_linux.cc \ + parsed_options.cc \ + primitive.cc \ + quick_exception_handler.cc \ + quick/inline_method_analyser.cc \ + reference_table.cc \ + reflection.cc \ + runtime.cc \ + signal_catcher.cc \ + stack.cc \ + thread.cc \ + thread_list.cc \ + thread_pool.cc \ + throw_location.cc \ + trace.cc \ + transaction.cc \ + profiler.cc \ + fault_handler.cc \ + utf.cc \ + utils.cc \ + verifier/dex_gc_map.cc \ + verifier/instruction_flags.cc \ + verifier/method_verifier.cc \ + verifier/reg_type.cc \ + verifier/reg_type_cache.cc \ + verifier/register_line.cc \ + well_known_classes.cc \ + zip_archive.cc LIBART_COMMON_SRC_FILES += \ - arch/context.cc \ - arch/memcmp16.cc \ - arch/arm/registers_arm.cc \ - arch/arm64/registers_arm64.cc \ - arch/x86/registers_x86.cc \ - arch/mips/registers_mips.cc \ - entrypoints/entrypoint_utils.cc \ - entrypoints/interpreter/interpreter_entrypoints.cc \ - entrypoints/jni/jni_entrypoints.cc \ - entrypoints/math_entrypoints.cc \ - entrypoints/portable/portable_alloc_entrypoints.cc \ - entrypoints/portable/portable_cast_entrypoints.cc \ - entrypoints/portable/portable_dexcache_entrypoints.cc \ - entrypoints/portable/portable_field_entrypoints.cc \ - entrypoints/portable/portable_fillarray_entrypoints.cc \ - entrypoints/portable/portable_invoke_entrypoints.cc \ - entrypoints/portable/portable_jni_entrypoints.cc \ - entrypoints/portable/portable_lock_entrypoints.cc \ - entrypoints/portable/portable_thread_entrypoints.cc \ - entrypoints/portable/portable_throw_entrypoints.cc \ - entrypoints/portable/portable_trampoline_entrypoints.cc \ - entrypoints/quick/quick_alloc_entrypoints.cc \ - entrypoints/quick/quick_cast_entrypoints.cc \ - entrypoints/quick/quick_deoptimization_entrypoints.cc \ - entrypoints/quick/quick_dexcache_entrypoints.cc \ - entrypoints/quick/quick_field_entrypoints.cc \ - entrypoints/quick/quick_fillarray_entrypoints.cc \ - entrypoints/quick/quick_instrumentation_entrypoints.cc \ - entrypoints/quick/quick_jni_entrypoints.cc \ - entrypoints/quick/quick_lock_entrypoints.cc \ - entrypoints/quick/quick_math_entrypoints.cc \ - entrypoints/quick/quick_thread_entrypoints.cc \ - entrypoints/quick/quick_throw_entrypoints.cc \ - entrypoints/quick/quick_trampoline_entrypoints.cc + arch/context.cc \ + arch/memcmp16.cc \ + arch/arm/registers_arm.cc \ + arch/arm64/registers_arm64.cc \ + arch/x86/registers_x86.cc \ + arch/mips/registers_mips.cc \ + entrypoints/entrypoint_utils.cc \ + entrypoints/interpreter/interpreter_entrypoints.cc \ + entrypoints/jni/jni_entrypoints.cc \ + entrypoints/math_entrypoints.cc \ + entrypoints/portable/portable_alloc_entrypoints.cc \ + entrypoints/portable/portable_cast_entrypoints.cc \ + entrypoints/portable/portable_dexcache_entrypoints.cc \ + entrypoints/portable/portable_field_entrypoints.cc \ + entrypoints/portable/portable_fillarray_entrypoints.cc \ + entrypoints/portable/portable_invoke_entrypoints.cc \ + entrypoints/portable/portable_jni_entrypoints.cc \ + entrypoints/portable/portable_lock_entrypoints.cc \ + entrypoints/portable/portable_thread_entrypoints.cc \ + entrypoints/portable/portable_throw_entrypoints.cc \ + entrypoints/portable/portable_trampoline_entrypoints.cc \ + entrypoints/quick/quick_alloc_entrypoints.cc \ + entrypoints/quick/quick_cast_entrypoints.cc \ + entrypoints/quick/quick_deoptimization_entrypoints.cc \ + entrypoints/quick/quick_dexcache_entrypoints.cc \ + entrypoints/quick/quick_field_entrypoints.cc \ + entrypoints/quick/quick_fillarray_entrypoints.cc \ + entrypoints/quick/quick_instrumentation_entrypoints.cc \ + entrypoints/quick/quick_jni_entrypoints.cc \ + entrypoints/quick/quick_lock_entrypoints.cc \ + entrypoints/quick/quick_math_entrypoints.cc \ + entrypoints/quick/quick_thread_entrypoints.cc \ + entrypoints/quick/quick_throw_entrypoints.cc \ + entrypoints/quick/quick_trampoline_entrypoints.cc # Source files that only compile with GCC. LIBART_GCC_ONLY_SRC_FILES := \ - interpreter/interpreter_goto_table_impl.cc + interpreter/interpreter_goto_table_impl.cc LIBART_TARGET_LDFLAGS := LIBART_HOST_LDFLAGS := LIBART_TARGET_SRC_FILES := \ - $(LIBART_COMMON_SRC_FILES) \ - base/logging_android.cc \ - jdwp/jdwp_adb.cc \ - monitor_android.cc \ - runtime_android.cc \ - thread_android.cc + $(LIBART_COMMON_SRC_FILES) \ + base/logging_android.cc \ + jdwp/jdwp_adb.cc \ + monitor_android.cc \ + runtime_android.cc \ + thread_android.cc LIBART_TARGET_SRC_FILES_arm := \ - arch/arm/context_arm.cc.arm \ - arch/arm/entrypoints_init_arm.cc \ - 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 + arch/arm/context_arm.cc.arm \ + arch/arm/entrypoints_init_arm.cc \ + 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 LIBART_TARGET_SRC_FILES_arm64 := \ - arch/arm64/context_arm64.cc \ - arch/arm64/entrypoints_init_arm64.cc \ - arch/arm64/jni_entrypoints_arm64.S \ - arch/arm64/memcmp16_arm64.S \ - arch/arm64/portable_entrypoints_arm64.S \ - arch/arm64/quick_entrypoints_arm64.S \ - arch/arm64/thread_arm64.cc \ - monitor_pool.cc \ - arch/arm64/fault_handler_arm64.cc + arch/arm64/context_arm64.cc \ + arch/arm64/entrypoints_init_arm64.cc \ + arch/arm64/jni_entrypoints_arm64.S \ + arch/arm64/memcmp16_arm64.S \ + arch/arm64/portable_entrypoints_arm64.S \ + arch/arm64/quick_entrypoints_arm64.S \ + arch/arm64/thread_arm64.cc \ + monitor_pool.cc \ + arch/arm64/fault_handler_arm64.cc LIBART_SRC_FILES_x86 := \ - arch/x86/context_x86.cc \ - arch/x86/entrypoints_init_x86.cc \ - arch/x86/jni_entrypoints_x86.S \ - arch/x86/portable_entrypoints_x86.S \ - arch/x86/quick_entrypoints_x86.S \ - arch/x86/thread_x86.cc \ - arch/x86/fault_handler_x86.cc + arch/x86/context_x86.cc \ + arch/x86/entrypoints_init_x86.cc \ + arch/x86/jni_entrypoints_x86.S \ + arch/x86/portable_entrypoints_x86.S \ + arch/x86/quick_entrypoints_x86.S \ + arch/x86/thread_x86.cc \ + arch/x86/fault_handler_x86.cc LIBART_TARGET_SRC_FILES_x86 := \ - $(LIBART_SRC_FILES_x86) + $(LIBART_SRC_FILES_x86) LIBART_SRC_FILES_x86_64 := \ - arch/x86_64/context_x86_64.cc \ - arch/x86_64/entrypoints_init_x86_64.cc \ - arch/x86_64/jni_entrypoints_x86_64.S \ - arch/x86_64/portable_entrypoints_x86_64.S \ - arch/x86_64/quick_entrypoints_x86_64.S \ - arch/x86_64/thread_x86_64.cc \ - monitor_pool.cc \ - arch/x86_64/fault_handler_x86_64.cc + arch/x86_64/context_x86_64.cc \ + arch/x86_64/entrypoints_init_x86_64.cc \ + arch/x86_64/jni_entrypoints_x86_64.S \ + arch/x86_64/portable_entrypoints_x86_64.S \ + arch/x86_64/quick_entrypoints_x86_64.S \ + arch/x86_64/thread_x86_64.cc \ + monitor_pool.cc \ + arch/x86_64/fault_handler_x86_64.cc LIBART_TARGET_SRC_FILES_x86_64 := \ - $(LIBART_SRC_FILES_x86_64) \ + $(LIBART_SRC_FILES_x86_64) \ LIBART_TARGET_SRC_FILES_mips := \ - arch/mips/context_mips.cc \ - arch/mips/entrypoints_init_mips.cc \ - arch/mips/jni_entrypoints_mips.S \ - arch/mips/memcmp16_mips.S \ - arch/mips/portable_entrypoints_mips.S \ - arch/mips/quick_entrypoints_mips.S \ - arch/mips/thread_mips.cc \ - arch/mips/fault_handler_mips.cc + arch/mips/context_mips.cc \ + arch/mips/entrypoints_init_mips.cc \ + arch/mips/jni_entrypoints_mips.S \ + arch/mips/memcmp16_mips.S \ + arch/mips/portable_entrypoints_mips.S \ + arch/mips/quick_entrypoints_mips.S \ + arch/mips/thread_mips.cc \ + arch/mips/fault_handler_mips.cc ifeq ($(TARGET_ARCH),mips64) $(info TODOMips64: $(LOCAL_PATH)/Android.mk Add mips64 specific runtime files) endif # TARGET_ARCH != mips64 LIBART_HOST_SRC_FILES := \ - $(LIBART_COMMON_SRC_FILES) \ - base/logging_linux.cc \ - monitor_linux.cc \ - runtime_linux.cc \ - thread_linux.cc + $(LIBART_COMMON_SRC_FILES) \ + base/logging_linux.cc \ + monitor_linux.cc \ + runtime_linux.cc \ + thread_linux.cc LIBART_HOST_SRC_FILES_32 := \ - $(LIBART_SRC_FILES_x86) + $(LIBART_SRC_FILES_x86) LIBART_HOST_SRC_FILES_64 := \ - $(LIBART_SRC_FILES_x86_64) + $(LIBART_SRC_FILES_x86_64) LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := \ - arch/x86_64/registers_x86_64.h \ - base/mutex.h \ - dex_file.h \ - dex_instruction.h \ - gc/collector/gc_type.h \ - gc/space/space.h \ - gc/heap.h \ - indirect_reference_table.h \ - instruction_set.h \ - invoke_type.h \ - jdwp/jdwp.h \ - jdwp/jdwp_constants.h \ - lock_word.h \ - mirror/class.h \ - oat.h \ - object_callbacks.h \ - quick/inline_method_analyser.h \ - thread.h \ - thread_state.h \ - verifier/method_verifier.h + arch/x86_64/registers_x86_64.h \ + base/mutex.h \ + dex_file.h \ + dex_instruction.h \ + gc/collector/gc_type.h \ + gc/space/space.h \ + gc/heap.h \ + indirect_reference_table.h \ + instruction_set.h \ + invoke_type.h \ + jdwp/jdwp.h \ + jdwp/jdwp_constants.h \ + lock_word.h \ + mirror/class.h \ + oat.h \ + object_callbacks.h \ + quick/inline_method_analyser.h \ + thread.h \ + thread_state.h \ + verifier/method_verifier.h LIBART_CFLAGS := ifeq ($(ART_USE_PORTABLE_COMPILER),true) LIBART_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 endif -ifeq ($(MALLOC_IMPL),jemalloc) - LIBART_CFLAGS += -DUSE_JEMALLOC -else +ifeq ($(MALLOC_IMPL),dlmalloc) LIBART_CFLAGS += -DUSE_DLMALLOC +else + LIBART_CFLAGS += -DUSE_JEMALLOC endif # $(1): target or host @@ -412,6 +416,7 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT LOCAL_STATIC_LIBRARIES := libziparchive libz 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 diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc index 3fa09cbed5..8c6afd66bd 100644 --- a/runtime/arch/arm/entrypoints_init_arm.cc +++ b/runtime/arch/arm/entrypoints_init_arm.cc @@ -15,6 +15,7 @@ */ #include "entrypoints/interpreter/interpreter_entrypoints.h" +#include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/portable/portable_entrypoints.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" @@ -25,11 +26,11 @@ namespace art { // Interpreter entrypoints. extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); + 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); + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); // Portable entrypoints. extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*); diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc index 2a82129511..e22c56ec69 100644 --- a/runtime/arch/arm/fault_handler_arm.cc +++ b/runtime/arch/arm/fault_handler_arm.cc @@ -46,9 +46,10 @@ static uint32_t GetInstructionSize(uint8_t* pc) { return instr_size; } -void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, +void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, + mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp) { - struct ucontext *uc = (struct ucontext *)context; + struct ucontext* uc = reinterpret_cast<struct ucontext*>(context); struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); *out_sp = static_cast<uintptr_t>(sc->arm_sp); VLOG(signals) << "sp: " << *out_sp; @@ -114,7 +115,7 @@ bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { uint32_t checkinst1 = 0xf8d90000 + Thread::ThreadSuspendTriggerOffset<4>().Int32Value(); uint16_t checkinst2 = 0x6800; - struct ucontext *uc = (struct ucontext *)context; + struct ucontext* uc = reinterpret_cast<struct ucontext*>(context); struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); uint8_t* ptr2 = reinterpret_cast<uint8_t*>(sc->arm_pc); uint8_t* ptr1 = ptr2 - 4; @@ -178,7 +179,7 @@ bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { // to the overflow region below the protected region. bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { - struct ucontext *uc = (struct ucontext *)context; + struct ucontext* uc = reinterpret_cast<struct ucontext*>(context); struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); VLOG(signals) << "stack overflow handler with sp at " << std::hex << &uc; VLOG(signals) << "sigcontext: " << std::hex << sc; @@ -205,7 +206,7 @@ bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { } // We know this is a stack overflow. We need to move the sp to the overflow region - // the exists below the protected region. Determine the address of the next + // that exists below the protected region. Determine the address of the next // available valid address below the protected region. uintptr_t prevsp = sp; sp = pregion; diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h index 422e20cf44..f353408baa 100644 --- a/runtime/arch/arm64/asm_support_arm64.h +++ b/runtime/arch/arm64/asm_support_arm64.h @@ -21,6 +21,7 @@ // TODO Thread offsets need to be checked when on Aarch64. +// Note: these callee save methods loads require read barriers. // Offset of field Runtime::callee_save_methods_[kSaveAll] #define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 0 // Offset of field Runtime::callee_save_methods_[kRefsOnly] diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc index c19b79efef..0c33d9ce83 100644 --- a/runtime/arch/arm64/entrypoints_init_arm64.cc +++ b/runtime/arch/arm64/entrypoints_init_arm64.cc @@ -15,6 +15,7 @@ */ #include "entrypoints/interpreter/interpreter_entrypoints.h" +#include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/portable/portable_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/entrypoint_utils.h" diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc index 74c3023aff..34eede605c 100644 --- a/runtime/arch/arm64/fault_handler_arm64.cc +++ b/runtime/arch/arm64/fault_handler_arm64.cc @@ -29,7 +29,8 @@ namespace art { -void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, +void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, + mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp) { } diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 7907b6ee13..2201b55849 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -31,6 +31,7 @@ ldr x9,[x9] // x9 = & (art::Runtime * art::Runtime.instance_) . // x9 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] . + THIS_LOAD_REQUIRES_READ_BARRIER ldr x9, [x9, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET ] sub sp, sp, #368 @@ -109,6 +110,7 @@ ldr x9,[x9] // x9 = & (art::Runtime * art::Runtime.instance_) . // x9 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] . + THIS_LOAD_REQUIRES_READ_BARRIER ldr x9, [x9, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET ] sub sp, sp, #176 @@ -280,6 +282,7 @@ ldr x9,[x9] // x9 = & (art::Runtime * art::Runtime.instance_) . // x9 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] . + THIS_LOAD_REQUIRES_READ_BARRIER ldr x9, [x9, RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET ] SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 70a9619447..d3e7d5e904 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -14,11 +14,14 @@ * limitations under the License. */ +#include "entrypoints/interpreter/interpreter_entrypoints.h" +#include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/portable/portable_entrypoints.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/entrypoint_utils.h" #include "entrypoints/math_entrypoints.h" +#include "atomic.h" namespace art { @@ -194,11 +197,11 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pCmplDouble = CmplDouble; qpoints->pCmplFloat = CmplFloat; qpoints->pFmod = fmod; - qpoints->pL2d = __floatdidf; + qpoints->pL2d = art_l2d; qpoints->pFmodf = fmodf; - qpoints->pL2f = __floatdisf; - qpoints->pD2iz = __fixdfsi; - qpoints->pF2iz = __fixsfsi; + qpoints->pL2f = art_l2f; + qpoints->pD2iz = art_d2i; + qpoints->pF2iz = art_f2i; qpoints->pIdivmod = NULL; qpoints->pD2l = art_d2l; qpoints->pF2l = art_f2l; @@ -234,6 +237,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method; qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception; qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow; + + // Atomic 64-bit load/store + qpoints->pA64Load = QuasiAtomic::Read64; + qpoints->pA64Store = QuasiAtomic::Write64; }; } // namespace art diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc index 1ecd7d964b..5a64a698f1 100644 --- a/runtime/arch/mips/fault_handler_mips.cc +++ b/runtime/arch/mips/fault_handler_mips.cc @@ -29,7 +29,8 @@ namespace art { -void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, +void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, + mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp) { } diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index eb490eb500..25f9a5a251 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -14,13 +14,14 @@ * limitations under the License. */ +#include <cstdio> + #include "common_runtime_test.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/string-inl.h" - -#include <cstdio> +#include "scoped_thread_state_change.h" namespace art { @@ -45,7 +46,7 @@ class StubTest : public CommonRuntimeTest { } } - void SetUpRuntimeOptions(Runtime::Options *options) OVERRIDE { + void SetUpRuntimeOptions(RuntimeOptions *options) OVERRIDE { // Use a smaller heap for (std::pair<std::string, const void*>& pair : *options) { if (pair.first.find("-Xmx") == 0) { @@ -1739,8 +1740,8 @@ TEST_F(StubTest, IMT) { // Sanity check: check that there is a conflict for List.contains in ArrayList. mirror::Class* arraylist_class = soa.Decode<mirror::Class*>(arraylist_jclass); - mirror::ArtMethod* m = arraylist_class->GetImTable()->Get( - inf_contains->GetDexMethodIndex() % ClassLinker::kImtSize); + mirror::ArtMethod* m = arraylist_class->GetEmbeddedImTableEntry( + inf_contains->GetDexMethodIndex() % mirror::Class::kImtSize); if (!m->IsImtConflictMethod()) { LOG(WARNING) << "Test is meaningless, no IMT conflict in setup: " << diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc index b217cd66b0..a072996504 100644 --- a/runtime/arch/x86/entrypoints_init_x86.cc +++ b/runtime/arch/x86/entrypoints_init_x86.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "entrypoints/interpreter/interpreter_entrypoints.h" +#include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/portable/portable_entrypoints.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc index 7c1980e57b..435f280a6b 100644 --- a/runtime/arch/x86/fault_handler_x86.cc +++ b/runtime/arch/x86/fault_handler_x86.cc @@ -21,7 +21,21 @@ #include "globals.h" #include "base/logging.h" #include "base/hex_dump.h" +#include "mirror/art_method.h" +#include "mirror/art_method-inl.h" +#include "thread.h" +#include "thread-inl.h" +#if defined(__APPLE__) +#define ucontext __darwin_ucontext +#define CTX_ESP uc_mcontext->__ss.__esp +#define CTX_EIP uc_mcontext->__ss.__eip +#define CTX_EAX uc_mcontext->__ss.__eax +#else +#define CTX_ESP uc_mcontext.gregs[REG_ESP] +#define CTX_EIP uc_mcontext.gregs[REG_EIP] +#define CTX_EAX uc_mcontext.gregs[REG_EAX] +#endif // // X86 specific fault handler functions. @@ -29,19 +43,292 @@ namespace art { -void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, +extern "C" void art_quick_throw_null_pointer_exception(); +extern "C" void art_quick_throw_stack_overflow_from_signal(); +extern "C" void art_quick_test_suspend(); + +// From the x86 disassembler... +enum SegmentPrefix { + kCs = 0x2e, + kSs = 0x36, + kDs = 0x3e, + kEs = 0x26, + kFs = 0x64, + kGs = 0x65, +}; + +// Get the size of an instruction in bytes. +static uint32_t GetInstructionSize(uint8_t* pc) { + uint8_t* instruction_start = pc; + bool have_prefixes = true; + bool two_byte = false; + + // Skip all the prefixes. + do { + switch (*pc) { + // Group 1 - lock and repeat prefixes: + case 0xF0: + case 0xF2: + case 0xF3: + // Group 2 - segment override prefixes: + case kCs: + case kSs: + case kDs: + case kEs: + case kFs: + case kGs: + // Group 3 - operand size override: + case 0x66: + // Group 4 - address size override: + case 0x67: + break; + default: + have_prefixes = false; + break; + } + if (have_prefixes) { + pc++; + } + } while (have_prefixes); + +#if defined(__x86_64__) + // Skip REX is present. + if (*pc >= 0x40 && *pc <= 0x4F) { + ++pc; + } +#endif + + // Check for known instructions. + uint32_t known_length = 0; + switch (*pc) { + case 0x83: // cmp [r + v], b: 4 byte instruction + known_length = 4; + break; + } + + if (known_length > 0) { + VLOG(signals) << "known instruction with length " << known_length; + return known_length; + } + + // Unknown instruction, work out length. + + // Work out if we have a ModR/M byte. + uint8_t opcode = *pc++; + if (opcode == 0xf) { + two_byte = true; + opcode = *pc++; + } + + bool has_modrm = false; // Is ModR/M byte present? + uint8_t hi = opcode >> 4; // Opcode high nybble. + uint8_t lo = opcode & 0b1111; // Opcode low nybble. + + // From the Intel opcode tables. + if (two_byte) { + has_modrm = true; // TODO: all of these? + } else if (hi < 4) { + has_modrm = lo < 4 || (lo >= 8 && lo <= 0xb); + } else if (hi == 6) { + has_modrm = lo == 3 || lo == 9 || lo == 0xb; + } else if (hi == 8) { + has_modrm = lo != 0xd; + } else if (hi == 0xc) { + has_modrm = lo == 1 || lo == 2 || lo == 6 || lo == 7; + } else if (hi == 0xd) { + has_modrm = lo < 4; + } else if (hi == 0xf) { + has_modrm = lo == 6 || lo == 7; + } + + if (has_modrm) { + uint8_t modrm = *pc++; + uint8_t mod = (modrm >> 6) & 0b11; + uint8_t reg = (modrm >> 3) & 0b111; + switch (mod) { + case 0: + break; + case 1: + if (reg == 4) { + // SIB + 1 byte displacement. + pc += 2; + } else { + pc += 1; + } + break; + case 2: + // SIB + 4 byte displacement. + pc += 5; + break; + case 3: + break; + } + } + + VLOG(signals) << "calculated X86 instruction size is " << (pc - instruction_start); + return pc - instruction_start; +} + +void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, + mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp) { + struct ucontext* uc = reinterpret_cast<struct ucontext*>(context); + *out_sp = static_cast<uintptr_t>(uc->CTX_ESP); + VLOG(signals) << "sp: " << std::hex << *out_sp; + if (*out_sp == 0) { + return; + } + + // In the case of a stack overflow, the stack is not valid and we can't + // get the method from the top of the stack. However it's in EAX. + uintptr_t* fault_addr = reinterpret_cast<uintptr_t*>(siginfo->si_addr); + uintptr_t* overflow_addr = reinterpret_cast<uintptr_t*>( + reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(kX86)); + if (overflow_addr == fault_addr) { + *out_method = reinterpret_cast<mirror::ArtMethod*>(uc->CTX_EAX); + } else { + // The method is at the top of the stack. + *out_method = reinterpret_cast<mirror::ArtMethod*>(reinterpret_cast<uintptr_t*>(*out_sp)[0]); + } + + uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP); + VLOG(signals) << HexDump(pc, 32, true, "PC "); + + uint32_t instr_size = GetInstructionSize(pc); + *out_return_pc = reinterpret_cast<uintptr_t>(pc + instr_size); } bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) { - return false; + struct ucontext *uc = reinterpret_cast<struct ucontext*>(context); + uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP); + uint8_t* sp = reinterpret_cast<uint8_t*>(uc->CTX_ESP); + + uint32_t instr_size = GetInstructionSize(pc); + // We need to arrange for the signal handler to return to the null pointer + // exception generator. The return address must be the address of the + // next instruction (this instruction + instruction size). The return address + // is on the stack at the top address of the current frame. + + // Push the return address onto the stack. + uint32_t retaddr = reinterpret_cast<uint32_t>(pc + instr_size); + uint32_t* next_sp = reinterpret_cast<uint32_t*>(sp - 4); + *next_sp = retaddr; + uc->CTX_ESP = reinterpret_cast<uint32_t>(next_sp); + + uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception); + VLOG(signals) << "Generating null pointer exception"; + return true; } +// A suspend check is done using the following instruction sequence: +// 0xf720f1df: 648B058C000000 mov eax, fs:[0x8c] ; suspend_trigger +// .. some intervening instructions. +// 0xf720f1e6: 8500 test eax, [eax] + +// The offset from fs is Thread::ThreadSuspendTriggerOffset(). +// To check for a suspend check, we examine the instructions that caused +// the fault. bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { + // These are the instructions to check for. The first one is the mov eax, fs:[xxx] + // where xxx is the offset of the suspend trigger. + uint32_t trigger = Thread::ThreadSuspendTriggerOffset<4>().Int32Value(); + + VLOG(signals) << "Checking for suspension point"; + uint8_t checkinst1[] = {0x64, 0x8b, 0x05, static_cast<uint8_t>(trigger & 0xff), + static_cast<uint8_t>((trigger >> 8) & 0xff), 0, 0}; + uint8_t checkinst2[] = {0x85, 0x00}; + + struct ucontext *uc = reinterpret_cast<struct ucontext*>(context); + uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP); + uint8_t* sp = reinterpret_cast<uint8_t*>(uc->CTX_ESP); + + if (pc[0] != checkinst2[0] || pc[1] != checkinst2[1]) { + // Second instruction is not correct (test eax,[eax]). + VLOG(signals) << "Not a suspension point"; + return false; + } + + // The first instruction can a little bit up the stream due to load hoisting + // in the compiler. + uint8_t* limit = pc - 100; // Compiler will hoist to a max of 20 instructions. + uint8_t* ptr = pc - sizeof(checkinst1); + bool found = false; + while (ptr > limit) { + if (memcmp(ptr, checkinst1, sizeof(checkinst1)) == 0) { + found = true; + break; + } + ptr -= 1; + } + + if (found) { + VLOG(signals) << "suspend check match"; + + // We need to arrange for the signal handler to return to the null pointer + // exception generator. The return address must be the address of the + // next instruction (this instruction + 2). The return address + // is on the stack at the top address of the current frame. + + // Push the return address onto the stack. + uint32_t retaddr = reinterpret_cast<uint32_t>(pc + 2); + uint32_t* next_sp = reinterpret_cast<uint32_t*>(sp - 4); + *next_sp = retaddr; + uc->CTX_ESP = reinterpret_cast<uint32_t>(next_sp); + + uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_quick_test_suspend); + + // Now remove the suspend trigger that caused this fault. + Thread::Current()->RemoveSuspendTrigger(); + VLOG(signals) << "removed suspend trigger invoking test suspend"; + return true; + } + VLOG(signals) << "Not a suspend check match, first instruction mismatch"; return false; } +// The stack overflow check is done using the following instruction: +// test eax, [esp+ -xxx] +// where 'xxx' is the size of the overflow area. +// +// This is done before any frame is established in the method. The return +// address for the previous method is on the stack at ESP. + bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { - return false; + struct ucontext *uc = reinterpret_cast<struct ucontext*>(context); + uintptr_t sp = static_cast<uintptr_t>(uc->CTX_ESP); + + uintptr_t fault_addr = reinterpret_cast<uintptr_t>(info->si_addr); + VLOG(signals) << "fault_addr: " << std::hex << fault_addr; + VLOG(signals) << "checking for stack overflow, sp: " << std::hex << sp << + ", fault_addr: " << fault_addr; + + uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kX86); + + Thread* self = Thread::Current(); + uintptr_t pregion = reinterpret_cast<uintptr_t>(self->GetStackEnd()) - + Thread::kStackOverflowProtectedSize; + + // Check that the fault address is the value expected for a stack overflow. + if (fault_addr != overflow_addr) { + VLOG(signals) << "Not a stack overflow"; + return false; + } + + // We know this is a stack overflow. We need to move the sp to the overflow region + // that exists below the protected region. Determine the address of the next + // available valid address below the protected region. + VLOG(signals) << "setting sp to overflow region at " << std::hex << pregion; + + // Since the compiler puts the implicit overflow + // check before the callee save instructions, the SP is already pointing to + // the previous frame. + + // Tell the stack overflow code where the new stack pointer should be. + uc->CTX_EAX = pregion; + + // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from_signal. + uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow_from_signal); + + return true; } } // namespace art diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 24b9e465e8..68f46ad26c 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -173,6 +173,21 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode */ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode +// On entry to this function, EAX contains the ESP value for the overflow region. +DEFINE_FUNCTION art_quick_throw_stack_overflow_from_signal + // Here, the ESP is above the protected region. We need to create a + // callee save frame and then move ESP down to the overflow region. + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + mov %esp, %ecx // get current stack pointer + mov %eax, %esp // move ESP to the overflow region. + PUSH ecx // pass SP + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + CFI_ADJUST_CFA_OFFSET(4) + SETUP_GOT_NOSAVE // clobbers ebx (harmless here) + call PLT_SYMBOL(artThrowStackOverflowFromCode) // artThrowStackOverflowFromCode(Thread*, SP) + int3 // unreached +END_FUNCTION art_quick_throw_stack_overflow_from_signal + /* * Called by managed code, saves callee saves and then calls artThrowException * that will place a mock Method* at the bottom of the stack. Arg1 holds the exception. diff --git a/runtime/arch/x86_64/asm_support_x86_64.h b/runtime/arch/x86_64/asm_support_x86_64.h index bff8501cf2..c3637ef7ae 100644 --- a/runtime/arch/x86_64/asm_support_x86_64.h +++ b/runtime/arch/x86_64/asm_support_x86_64.h @@ -19,6 +19,7 @@ #include "asm_support.h" +// Note: these callee save methods loads require read barriers. // Offset of field Runtime::callee_save_methods_[kSaveAll] #define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 0 // Offset of field Runtime::callee_save_methods_[kRefsOnly] @@ -35,9 +36,9 @@ // Offset of field Thread::thin_lock_thread_id_ verified in InitCpu #define THREAD_ID_OFFSET 12 -#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 64 -#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 64 -#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 176 +#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 64 + 4*8 +#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 64 + 4*8 +#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 176 + 4*8 // Expected size of a heap reference #define HEAP_REFERENCE_SIZE 4 diff --git a/runtime/arch/x86_64/context_x86_64.cc b/runtime/arch/x86_64/context_x86_64.cc index e1f47ee3d4..7699eaf9d4 100644 --- a/runtime/arch/x86_64/context_x86_64.cc +++ b/runtime/arch/x86_64/context_x86_64.cc @@ -78,6 +78,18 @@ void X86_64Context::SmashCallerSaves() { gprs_[R9] = nullptr; gprs_[R10] = nullptr; gprs_[R11] = nullptr; + fprs_[XMM0] = nullptr; + fprs_[XMM1] = nullptr; + fprs_[XMM2] = nullptr; + fprs_[XMM3] = nullptr; + fprs_[XMM4] = nullptr; + fprs_[XMM5] = nullptr; + fprs_[XMM6] = nullptr; + fprs_[XMM7] = nullptr; + fprs_[XMM8] = nullptr; + fprs_[XMM9] = nullptr; + fprs_[XMM10] = nullptr; + fprs_[XMM11] = nullptr; } bool X86_64Context::SetGPR(uint32_t reg, uintptr_t value) { @@ -102,41 +114,26 @@ bool X86_64Context::SetFPR(uint32_t reg, uintptr_t value) { } } +extern "C" void art_quick_do_long_jump(uintptr_t*, uintptr_t*); + void X86_64Context::DoLongJump() { #if defined(__x86_64__) - // Array of GPR values, filled from the context backward for the long jump pop. We add a slot at - // the top for the stack pointer that doesn't get popped in a pop-all. - volatile uintptr_t gprs[kNumberOfCpuRegisters + 1]; + uintptr_t gprs[kNumberOfCpuRegisters + 1]; + uintptr_t fprs[kNumberOfFloatRegisters]; + for (size_t i = 0; i < kNumberOfCpuRegisters; ++i) { gprs[kNumberOfCpuRegisters - i - 1] = gprs_[i] != nullptr ? *gprs_[i] : X86_64Context::kBadGprBase + i; } + for (size_t i = 0; i < kNumberOfFloatRegisters; ++i) { + fprs[i] = fprs_[i] != nullptr ? *fprs_[i] : X86_64Context::kBadFprBase + i; + } + // We want to load the stack pointer one slot below so that the ret will pop eip. uintptr_t rsp = gprs[kNumberOfCpuRegisters - RSP - 1] - kWordSize; gprs[kNumberOfCpuRegisters] = rsp; *(reinterpret_cast<uintptr_t*>(rsp)) = rip_; - __asm__ __volatile__( - "movq %0, %%rsp\n\t" // RSP points to gprs. - "popq %%r15\n\t" // Load all registers except RSP and RIP with values in gprs. - "popq %%r14\n\t" - "popq %%r13\n\t" - "popq %%r12\n\t" - "popq %%r11\n\t" - "popq %%r10\n\t" - "popq %%r9\n\t" - "popq %%r8\n\t" - "popq %%rdi\n\t" - "popq %%rsi\n\t" - "popq %%rbp\n\t" - "addq $8, %%rsp\n\t" - "popq %%rbx\n\t" - "popq %%rdx\n\t" - "popq %%rcx\n\t" - "popq %%rax\n\t" - "popq %%rsp\n\t" // Load stack pointer. - "ret\n\t" // From higher in the stack pop rip. - : // output. - : "g"(&gprs[0]) // input. - :); // clobber. + + art_quick_do_long_jump(gprs, fprs); #else UNIMPLEMENTED(FATAL); #endif diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc index 609d1c6500..35a0cf4f2c 100644 --- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc +++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "entrypoints/interpreter/interpreter_entrypoints.h" +#include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/portable/portable_entrypoints.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" @@ -27,15 +29,15 @@ 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); + 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*); // Cast entrypoints. -extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass, +extern "C" uint32_t art_quick_assignable_from_code(const mirror::Class* klass, const mirror::Class* ref_class); extern "C" void art_quick_check_cast(void*, void*); @@ -129,7 +131,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, ResetQuickAllocEntryPoints(qpoints); // Cast - qpoints->pInstanceofNonTrivial = artIsAssignableFromCode; + qpoints->pInstanceofNonTrivial = art_quick_assignable_from_code; qpoints->pCheckCast = art_quick_check_cast; // DexCache diff --git a/runtime/arch/x86_64/fault_handler_x86_64.cc b/runtime/arch/x86_64/fault_handler_x86_64.cc index 233d3c7d1a..88ae7f3711 100644 --- a/runtime/arch/x86_64/fault_handler_x86_64.cc +++ b/runtime/arch/x86_64/fault_handler_x86_64.cc @@ -29,7 +29,8 @@ namespace art { -void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, +void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, + mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp) { } diff --git a/runtime/arch/x86_64/jni_entrypoints_x86_64.S b/runtime/arch/x86_64/jni_entrypoints_x86_64.S index d668797ba4..f6736df11f 100644 --- a/runtime/arch/x86_64/jni_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/jni_entrypoints_x86_64.S @@ -28,8 +28,8 @@ DEFINE_FUNCTION art_jni_dlsym_lookup_stub PUSH rdx // Arg. PUSH rcx // Arg. // Create space for FPR args, plus padding for alignment - subq LITERAL(72), %rsp - CFI_ADJUST_CFA_OFFSET(72) + subq LITERAL(72 + 4 * 8), %rsp + CFI_ADJUST_CFA_OFFSET(72 + 4 * 8) // Save FPRs. movq %xmm0, 0(%rsp) movq %xmm1, 8(%rsp) @@ -39,6 +39,10 @@ DEFINE_FUNCTION art_jni_dlsym_lookup_stub movq %xmm5, 40(%rsp) movq %xmm6, 48(%rsp) movq %xmm7, 56(%rsp) + movq %xmm12, 64(%rsp) + movq %xmm13, 72(%rsp) + movq %xmm14, 80(%rsp) + movq %xmm15, 88(%rsp) // prepare call movq %gs:THREAD_SELF_OFFSET, %rdi // RDI := Thread::Current() // call @@ -52,8 +56,12 @@ DEFINE_FUNCTION art_jni_dlsym_lookup_stub movq 40(%rsp), %xmm5 movq 48(%rsp), %xmm6 movq 56(%rsp), %xmm7 - addq LITERAL(72), %rsp - CFI_ADJUST_CFA_OFFSET(-72) + movq 64(%rsp), %xmm12 + movq 72(%rsp), %xmm13 + movq 80(%rsp), %xmm14 + movq 88(%rsp), %xmm15 + addq LITERAL(72 + 4 * 8), %rsp + CFI_ADJUST_CFA_OFFSET(-72 - 4 * 8) POP rcx // Arg. POP rdx // Arg. POP rsi // Arg. diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 8fa947c9b3..50b2de4a4f 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -16,6 +16,26 @@ #include "asm_support_x86_64.S" +MACRO0(SETUP_FP_CALLEE_SAVE_FRAME) + // Create space for ART FP callee-saved registers + subq MACRO_LITERAL(4 * 8), %rsp + CFI_ADJUST_CFA_OFFSET(4 * 8) + movq %xmm12, 0(%rsp) + movq %xmm13, 8(%rsp) + movq %xmm14, 16(%rsp) + movq %xmm15, 24(%rsp) +END_MACRO + +MACRO0(RESTORE_FP_CALLEE_SAVE_FRAME) + // Restore ART FP callee-saved registers + movq 0(%rsp), %xmm12 + movq 8(%rsp), %xmm13 + movq 16(%rsp), %xmm14 + movq 24(%rsp), %xmm15 + addq MACRO_LITERAL(4 * 8), %rsp + CFI_ADJUST_CFA_OFFSET(- 4 * 8) +END_MACRO + // For x86, the CFA is esp+4, the address above the pushed return address on the stack. /* @@ -37,16 +57,25 @@ MACRO0(SETUP_SAVE_ALL_CALLEE_SAVE_FRAME) PUSH r12 // Callee save. PUSH rbp // Callee save. PUSH rbx // Callee save. + // Create space for FPR args, plus padding for alignment + subq LITERAL(4 * 8), %rsp + CFI_ADJUST_CFA_OFFSET(4 * 8) + // Save FPRs. + movq %xmm12, 0(%rsp) + movq %xmm13, 8(%rsp) + movq %xmm14, 16(%rsp) + movq %xmm15, 24(%rsp) subq MACRO_LITERAL(8), %rsp // Space for Method* (also aligns the frame). CFI_ADJUST_CFA_OFFSET(8) // R10 := ArtMethod* for save all callee save frame method. + THIS_LOAD_REQUIRES_READ_BARRIER movq RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10 // Store ArtMethod* to bottom of stack. movq %r10, 0(%rsp) // Ugly compile-time check, but we only have the preprocessor. // Last +8: implicit return address pushed on stack when caller made call. -#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 6*8 + 8 + 8) +#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 6*8 + 4*8 + 8 + 8) #error "SAVE_ALL_CALLEE_SAVE_FRAME(X86_64) size not as expected." #endif #endif // __APPLE__ @@ -71,24 +100,35 @@ MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME) PUSH r12 // Callee save. PUSH rbp // Callee save. PUSH rbx // Callee save. - subq MACRO_LITERAL(8), %rsp // Space for Method* (also aligns the frame). - CFI_ADJUST_CFA_OFFSET(8) + // Create space for FPR args, plus padding for alignment + subq LITERAL(8 + 4*8), %rsp + CFI_ADJUST_CFA_OFFSET(8 + 4*8) + // Save FPRs. + movq %xmm12, 8(%rsp) + movq %xmm13, 16(%rsp) + movq %xmm14, 24(%rsp) + movq %xmm15, 32(%rsp) // R10 := ArtMethod* for refs only callee save frame method. + THIS_LOAD_REQUIRES_READ_BARRIER movq RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10 // Store ArtMethod* to bottom of stack. movq %r10, 0(%rsp) // Ugly compile-time check, but we only have the preprocessor. // Last +8: implicit return address pushed on stack when caller made call. -#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 6*8 + 8 + 8) +#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 6*8 + 4*8 + 8 + 8) #error "REFS_ONLY_CALLEE_SAVE_FRAME(X86_64) size not as expected." #endif #endif // __APPLE__ END_MACRO MACRO0(RESTORE_REF_ONLY_CALLEE_SAVE_FRAME) - addq MACRO_LITERAL(8), %rsp - CFI_ADJUST_CFA_OFFSET(-8) + movq 8(%rsp), %xmm12 + movq 16(%rsp), %xmm13 + movq 24(%rsp), %xmm14 + movq 32(%rsp), %xmm15 + addq LITERAL(8 + 4*8), %rsp + CFI_ADJUST_CFA_OFFSET(-8 - 4*8) // TODO: optimize by not restoring callee-saves restored by the ABI POP rbx POP rbp @@ -123,9 +163,10 @@ MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME) PUSH rdx // Quick arg 2. PUSH rcx // Quick arg 3. // Create space for FPR args and create 2 slots, 1 of padding and 1 for the ArtMethod*. - subq MACRO_LITERAL(80), %rsp - CFI_ADJUST_CFA_OFFSET(80) + subq MACRO_LITERAL(80 + 4 * 8), %rsp + CFI_ADJUST_CFA_OFFSET(80 + 4 * 8) // R10 := ArtMethod* for ref and args callee save frame method. + THIS_LOAD_REQUIRES_READ_BARRIER movq RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10 // Save FPRs. movq %xmm0, 16(%rsp) @@ -136,12 +177,16 @@ MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME) movq %xmm5, 56(%rsp) movq %xmm6, 64(%rsp) movq %xmm7, 72(%rsp) + movq %xmm12, 80(%rsp) + movq %xmm13, 88(%rsp) + movq %xmm14, 96(%rsp) + movq %xmm15, 104(%rsp) // Store ArtMethod* to bottom of stack. movq %r10, 0(%rsp) // Ugly compile-time check, but we only have the preprocessor. // Last +8: implicit return address pushed on stack when caller made call. -#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 11*8 + 80 + 8) +#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 11*8 + 4*8 + 80 + 8) #error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(X86_64) size not as expected." #endif #endif // __APPLE__ @@ -157,8 +202,12 @@ MACRO0(RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME) movq 56(%rsp), %xmm5 movq 64(%rsp), %xmm6 movq 72(%rsp), %xmm7 - addq MACRO_LITERAL(80), %rsp - CFI_ADJUST_CFA_OFFSET(-80) + movq 80(%rsp), %xmm12 + movq 88(%rsp), %xmm13 + movq 96(%rsp), %xmm14 + movq 104(%rsp), %xmm15 + addq MACRO_LITERAL(80 + 4 * 8), %rsp + CFI_ADJUST_CFA_OFFSET(-(80 + 4 * 8)) // Restore callee and GPR args, mixed together to agree with core spills bitmap. POP rcx POP rdx @@ -536,6 +585,58 @@ DEFINE_FUNCTION art_quick_invoke_static_stub #endif // __APPLE__ END_FUNCTION art_quick_invoke_static_stub + /* + * Long jump stub. + * On entry: + * rdi = gprs + * rsi = fprs + */ +DEFINE_FUNCTION art_quick_do_long_jump +#if defined(__APPLE__) + int3 + int3 +#else + // Restore FPRs. + movq 0(%rsi), %xmm0 + movq 8(%rsi), %xmm1 + movq 16(%rsi), %xmm2 + movq 24(%rsi), %xmm3 + movq 32(%rsi), %xmm4 + movq 40(%rsi), %xmm5 + movq 48(%rsi), %xmm6 + movq 56(%rsi), %xmm7 + movq 64(%rsi), %xmm8 + movq 72(%rsi), %xmm9 + movq 80(%rsi), %xmm10 + movq 88(%rsi), %xmm11 + movq 96(%rsi), %xmm12 + movq 104(%rsi), %xmm13 + movq 112(%rsi), %xmm14 + movq 120(%rsi), %xmm15 + // Restore FPRs. + movq %rdi, %rsp // RSP points to gprs. + // Load all registers except RSP and RIP with values in gprs. + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %r11 + popq %r10 + popq %r9 + popq %r8 + popq %rdi + popq %rsi + popq %rbp + addq LITERAL(8), %rsp // Skip rsp + popq %rbx + popq %rdx + popq %rcx + popq %rax + popq %rsp // Load stack pointer. + ret // From higher in the stack pop rip. +#endif // __APPLE__ +END_FUNCTION art_quick_do_long_jump + MACRO3(NO_ARG_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION VAR(c_name, 0) SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC @@ -820,13 +921,17 @@ END_FUNCTION art_quick_unlock_object DEFINE_FUNCTION art_quick_check_cast PUSH rdi // Save args for exc PUSH rsi + SETUP_FP_CALLEE_SAVE_FRAME call PLT_SYMBOL(artIsAssignableFromCode) // (Class* klass, Class* ref_klass) testq %rax, %rax jz 1f // jump forward if not assignable + RESTORE_FP_CALLEE_SAVE_FRAME addq LITERAL(16), %rsp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) + ret 1: + RESTORE_FP_CALLEE_SAVE_FRAME POP rsi // Pop arguments POP rdi SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context @@ -907,6 +1012,7 @@ DEFINE_FUNCTION art_quick_aput_obj PUSH rdx subq LITERAL(8), %rsp // Alignment padding. CFI_ADJUST_CFA_OFFSET(8) + SETUP_FP_CALLEE_SAVE_FRAME // "Uncompress" = do nothing, as already zero-extended on load. movl CLASS_OFFSET(%edx), %esi // Pass arg2 = value's class. @@ -918,6 +1024,7 @@ DEFINE_FUNCTION art_quick_aput_obj testq %rax, %rax jz .Lthrow_array_store_exception + RESTORE_FP_CALLEE_SAVE_FRAME // Restore arguments. addq LITERAL(8), %rsp CFI_ADJUST_CFA_OFFSET(-8) @@ -934,6 +1041,7 @@ DEFINE_FUNCTION art_quick_aput_obj // movb %dl, (%rdx, %rdi) ret .Lthrow_array_store_exception: + RESTORE_FP_CALLEE_SAVE_FRAME // Restore arguments. addq LITERAL(8), %rsp CFI_ADJUST_CFA_OFFSET(-8) @@ -1012,8 +1120,8 @@ DEFINE_FUNCTION art_quick_proxy_invoke_handler PUSH rdx // Quick arg 2. PUSH rcx // Quick arg 3. // Create space for FPR args and create 2 slots, 1 of padding and 1 for the ArtMethod*. - subq LITERAL(80), %rsp - CFI_ADJUST_CFA_OFFSET(80) + subq LITERAL(80 + 4*8), %rsp + CFI_ADJUST_CFA_OFFSET(80 + 4*8) // Save FPRs. movq %xmm0, 16(%rsp) movq %xmm1, 24(%rsp) @@ -1023,14 +1131,18 @@ DEFINE_FUNCTION art_quick_proxy_invoke_handler movq %xmm5, 56(%rsp) movq %xmm6, 64(%rsp) movq %xmm7, 72(%rsp) + movq %xmm12, 80(%rsp) + movq %xmm13, 88(%rsp) + movq %xmm14, 96(%rsp) + movq %xmm15, 104(%rsp) // Store proxy method to bottom of stack. movq %rdi, 0(%rsp) movq %gs:THREAD_SELF_OFFSET, %rdx // Pass Thread::Current(). movq %rsp, %rcx // Pass SP. call PLT_SYMBOL(artQuickProxyInvokeHandler) // (proxy method, receiver, Thread*, SP) movq %rax, %xmm0 // Copy return value in case of float returns. - addq LITERAL(168), %rsp // Pop arguments. - CFI_ADJUST_CFA_OFFSET(-168) + addq LITERAL(168 + 4*8), %rsp // Pop arguments. + CFI_ADJUST_CFA_OFFSET(-168 - 4*8) RETURN_OR_DELIVER_PENDING_EXCEPTION END_FUNCTION art_quick_proxy_invoke_handler @@ -1156,8 +1268,8 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline PUSH rdx // Quick arg 2. PUSH rcx // Quick arg 3. // Create space for FPR args and create 2 slots, 1 of padding and 1 for the ArtMethod*. - subq LITERAL(80), %rsp - CFI_ADJUST_CFA_OFFSET(80) + subq LITERAL(80 + 4*8), %rsp + CFI_ADJUST_CFA_OFFSET(80 + 4*8) // Save FPRs. movq %xmm0, 16(%rsp) movq %xmm1, 24(%rsp) @@ -1167,6 +1279,10 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline movq %xmm5, 56(%rsp) movq %xmm6, 64(%rsp) movq %xmm7, 72(%rsp) + movq %xmm12, 80(%rsp) + movq %xmm13, 88(%rsp) + movq %xmm14, 96(%rsp) + movq %xmm15, 104(%rsp) movq %rdi, 0(%rsp) // Store native ArtMethod* to bottom of stack. movq %rsp, %rbp // save SP at (old) callee-save frame CFI_DEF_CFA_REGISTER(rbp) @@ -1260,9 +1376,13 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline movq 56(%rsp), %xmm5 movq 64(%rsp), %xmm6 movq 72(%rsp), %xmm7 + movq 80(%rsp), %xmm12 + movq 88(%rsp), %xmm13 + movq 96(%rsp), %xmm14 + movq 104(%rsp), %xmm15 // was 80 bytes - addq LITERAL(80), %rsp - CFI_ADJUST_CFA_OFFSET(-80) + addq LITERAL(80 + 4*8), %rsp + CFI_ADJUST_CFA_OFFSET(-80 - 4*8) // Save callee and GPR args, mixed together to agree with core spills bitmap. POP rcx // Arg. POP rdx // Arg. @@ -1292,9 +1412,13 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline movq 56(%rsp), %xmm5 movq 64(%rsp), %xmm6 movq 72(%rsp), %xmm7 - // was 80 bytes - addq LITERAL(80), %rsp - CFI_ADJUST_CFA_OFFSET(-80) + movq 80(%rsp), %xmm12 + movq 88(%rsp), %xmm13 + movq 96(%rsp), %xmm14 + movq 104(%rsp), %xmm15 + // was 80 + 32 bytes + addq LITERAL(80 + 4*8), %rsp + CFI_ADJUST_CFA_OFFSET(-80 - 4*8) // Save callee and GPR args, mixed together to agree with core spills bitmap. POP rcx // Arg. POP rdx // Arg. @@ -1450,3 +1574,10 @@ DEFINE_FUNCTION art_quick_string_compareto END_FUNCTION art_quick_string_compareto UNIMPLEMENTED art_quick_memcmp16 + +DEFINE_FUNCTION art_quick_assignable_from_code + SETUP_FP_CALLEE_SAVE_FRAME + call PLT_SYMBOL(artIsAssignableFromCode) // (const mirror::Class*, const mirror::Class*) + RESTORE_FP_CALLEE_SAVE_FRAME + ret +END_FUNCTION art_quick_assignable_from_code diff --git a/runtime/arch/x86_64/quick_method_frame_info_x86_64.h b/runtime/arch/x86_64/quick_method_frame_info_x86_64.h index 618390903b..53aa212a88 100644 --- a/runtime/arch/x86_64/quick_method_frame_info_x86_64.h +++ b/runtime/arch/x86_64/quick_method_frame_info_x86_64.h @@ -34,6 +34,9 @@ static constexpr uint32_t kX86_64CalleeSaveFpArgSpills = (1 << art::x86_64::XMM0) | (1 << art::x86_64::XMM1) | (1 << art::x86_64::XMM2) | (1 << art::x86_64::XMM3) | (1 << art::x86_64::XMM4) | (1 << art::x86_64::XMM5) | (1 << art::x86_64::XMM6) | (1 << art::x86_64::XMM7); +static constexpr uint32_t kX86_64CalleeSaveFpSpills = + (1 << art::x86_64::XMM12) | (1 << art::x86_64::XMM13) | + (1 << art::x86_64::XMM14) | (1 << art::x86_64::XMM15); constexpr uint32_t X86_64CalleeSaveCoreSpills(Runtime::CalleeSaveType type) { return kX86_64CalleeSaveRefSpills | @@ -42,7 +45,8 @@ constexpr uint32_t X86_64CalleeSaveCoreSpills(Runtime::CalleeSaveType type) { } constexpr uint32_t X86_64CalleeSaveFpSpills(Runtime::CalleeSaveType type) { - return (type == Runtime::kRefsAndArgs ? kX86_64CalleeSaveFpArgSpills : 0); + return kX86_64CalleeSaveFpSpills | + (type == Runtime::kRefsAndArgs ? kX86_64CalleeSaveFpArgSpills : 0); } constexpr uint32_t X86_64CalleeSaveFrameSize(Runtime::CalleeSaveType type) { diff --git a/runtime/arch/x86_64/registers_x86_64.cc b/runtime/arch/x86_64/registers_x86_64.cc index 38f3494502..f29c42652b 100644 --- a/runtime/arch/x86_64/registers_x86_64.cc +++ b/runtime/arch/x86_64/registers_x86_64.cc @@ -34,5 +34,14 @@ std::ostream& operator<<(std::ostream& os, const Register& rhs) { return os; } +std::ostream& operator<<(std::ostream& os, const FloatRegister& rhs) { + if (rhs >= XMM0 && rhs <= XMM15) { + os << "xmm" << static_cast<int>(rhs); + } else { + os << "Register[" << static_cast<int>(rhs) << "]"; + } + return os; +} + } // namespace x86_64 } // namespace art diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc index 086ef440a3..de348dc79d 100644 --- a/runtime/barrier_test.cc +++ b/runtime/barrier_test.cc @@ -22,6 +22,7 @@ #include "common_runtime_test.h" #include "mirror/object_array-inl.h" #include "thread_pool.h" +#include "thread-inl.h" namespace art { class CheckWaitTask : public Task { diff --git a/runtime/base/macros.h b/runtime/base/macros.h index fe5a2ef4fd..fae9271d9e 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -176,6 +176,7 @@ char (&ArraySizeHelper(T (&array)[N]))[N]; #endif #define PURE __attribute__ ((__pure__)) +#define WARN_UNUSED __attribute__((warn_unused_result)) template<typename T> void UNUSED(const T&) {} diff --git a/runtime/base/mutex_test.cc b/runtime/base/mutex_test.cc index ee0b1beba9..289d3ef8c1 100644 --- a/runtime/base/mutex_test.cc +++ b/runtime/base/mutex_test.cc @@ -17,6 +17,7 @@ #include "mutex.h" #include "common_runtime_test.h" +#include "thread-inl.h" namespace art { diff --git a/runtime/base/scoped_flock_test.cc b/runtime/base/scoped_flock_test.cc index 8fa181ab6b..1fa7a12d17 100644 --- a/runtime/base/scoped_flock_test.cc +++ b/runtime/base/scoped_flock_test.cc @@ -15,9 +15,8 @@ */ #include "scoped_flock.h" -#include "common_runtime_test.h" -#include "gtest/gtest.h" +#include "common_runtime_test.h" namespace art { diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index fefb907422..a530594d7c 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -23,6 +23,7 @@ #include "class_linker.h" #include "class_linker-inl.h" #include "dex_file-inl.h" +#include "field_helper.h" #include "gc/space/space.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" @@ -31,7 +32,6 @@ #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" #include "mirror/throwable.h" -#include "object_utils.h" #include "runtime.h" #include "scoped_thread_state_change.h" #include "thread.h" @@ -209,7 +209,7 @@ class ScopedCheck { // obj will be NULL. Otherwise, obj should always be non-NULL // and valid. if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) { - Runtime::Current()->GetHeap()->DumpSpaces(); + Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); JniAbortF(function_name_, "field operation on invalid %s: %p", ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object); return; @@ -248,7 +248,7 @@ class ScopedCheck { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::Object* o = soa_.Decode<mirror::Object*>(java_object); if (o == nullptr || !Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) { - Runtime::Current()->GetHeap()->DumpSpaces(); + Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); JniAbortF(function_name_, "field operation on invalid %s: %p", ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object); return; @@ -628,7 +628,7 @@ class ScopedCheck { mirror::Object* obj = soa_.Decode<mirror::Object*>(java_object); if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) { - Runtime::Current()->GetHeap()->DumpSpaces(); + Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); JniAbortF(function_name_, "%s is an invalid %s: %p (%p)", what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object, obj); return false; @@ -682,7 +682,7 @@ class ScopedCheck { mirror::Array* a = soa_.Decode<mirror::Array*>(java_array); if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(a)) { - Runtime::Current()->GetHeap()->DumpSpaces(); + Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); JniAbortF(function_name_, "jarray is an invalid %s: %p (%p)", ToStr<IndirectRefKind>(GetIndirectRefKind(java_array)).c_str(), java_array, a); } else if (!a->IsArrayInstance()) { @@ -703,7 +703,7 @@ class ScopedCheck { } mirror::ArtField* f = soa_.DecodeField(fid); if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(f) || !f->IsArtField()) { - Runtime::Current()->GetHeap()->DumpSpaces(); + Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); JniAbortF(function_name_, "invalid jfieldID: %p", fid); return nullptr; } @@ -717,7 +717,7 @@ class ScopedCheck { } mirror::ArtMethod* m = soa_.DecodeMethod(mid); if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(m) || !m->IsArtMethod()) { - Runtime::Current()->GetHeap()->DumpSpaces(); + Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); JniAbortF(function_name_, "invalid jmethodID: %p", mid); return nullptr; } @@ -738,7 +738,7 @@ class ScopedCheck { mirror::Object* o = soa_.Decode<mirror::Object*>(java_object); if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) { - Runtime::Current()->GetHeap()->DumpSpaces(); + Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); // TODO: when we remove work_around_app_jni_bugs, this should be impossible. JniAbortF(function_name_, "native code passing in reference to invalid %s: %p", ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object); diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index a40a2e4cd2..25eb3a342d 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -24,7 +24,6 @@ #include "mirror/dex_cache-inl.h" #include "mirror/iftable.h" #include "mirror/object_array.h" -#include "object_utils.h" #include "handle_scope-inl.h" namespace art { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 5180e34313..2c11f8b89c 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -42,8 +42,10 @@ #include "intern_table.h" #include "interpreter/interpreter.h" #include "leb128.h" +#include "method_helper.h" #include "oat.h" #include "oat_file.h" +#include "object_lock.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class.h" @@ -54,8 +56,9 @@ #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/proxy.h" +#include "mirror/reference-inl.h" #include "mirror/stack_trace_element.h" -#include "object_utils.h" +#include "mirror/string-inl.h" #include "os.h" #include "runtime.h" #include "entrypoints/entrypoint_utils.h" @@ -207,7 +210,8 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class heap->IncrementDisableMovingGC(self); StackHandleScope<64> hs(self); // 64 is picked arbitrarily. Handle<mirror::Class> java_lang_Class(hs.NewHandle(down_cast<mirror::Class*>( - heap->AllocNonMovableObject<true>(self, nullptr, sizeof(mirror::ClassClass), + heap->AllocNonMovableObject<true>(self, nullptr, + mirror::Class::ClassClassSize(), VoidFunctor())))); CHECK(java_lang_Class.Get() != NULL); mirror::Class::SetClassClass(java_lang_Class.Get()); @@ -215,45 +219,53 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class if (kUseBakerOrBrooksReadBarrier) { java_lang_Class->AssertReadBarrierPointer(); } - java_lang_Class->SetClassSize(sizeof(mirror::ClassClass)); + java_lang_Class->SetClassSize(mirror::Class::ClassClassSize()); heap->DecrementDisableMovingGC(self); // AllocClass(mirror::Class*) can now be used // Class[] is used for reflection support. - Handle<mirror::Class> class_array_class( - hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class)))); + Handle<mirror::Class> class_array_class(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::ObjectArray<mirror::Class>::ClassSize()))); class_array_class->SetComponentType(java_lang_Class.Get()); // java_lang_Object comes next so that object_array_class can be created. - Handle<mirror::Class> java_lang_Object( - hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class)))); + Handle<mirror::Class> java_lang_Object(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::Object::ClassSize()))); CHECK(java_lang_Object.Get() != NULL); // backfill Object as the super class of Class. java_lang_Class->SetSuperClass(java_lang_Object.Get()); java_lang_Object->SetStatus(mirror::Class::kStatusLoaded, self); // Object[] next to hold class roots. - Handle<mirror::Class> object_array_class( - hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class)))); + Handle<mirror::Class> object_array_class(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::ObjectArray<mirror::Object>::ClassSize()))); object_array_class->SetComponentType(java_lang_Object.Get()); - // Setup the char class to be used for char[]. - Handle<mirror::Class> char_class(hs.NewHandle(AllocClass(self, java_lang_Class.Get(), - sizeof(mirror::Class)))); + // Setup the char (primitive) class to be used for char[]. + Handle<mirror::Class> char_class(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::Class::PrimitiveClassSize()))); // Setup the char[] class to be used for String. - Handle<mirror::Class> char_array_class(hs.NewHandle(AllocClass(self, java_lang_Class.Get(), - sizeof(mirror::Class)))); + Handle<mirror::Class> char_array_class(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), + mirror::Array::ClassSize()))); char_array_class->SetComponentType(char_class.Get()); mirror::CharArray::SetArrayClass(char_array_class.Get()); // Setup String. - Handle<mirror::Class> java_lang_String(hs.NewHandle(AllocClass(self, java_lang_Class.Get(), - sizeof(mirror::StringClass)))); + Handle<mirror::Class> java_lang_String(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::String::ClassSize()))); mirror::String::SetClass(java_lang_String.Get()); - java_lang_String->SetObjectSize(sizeof(mirror::String)); + java_lang_String->SetObjectSize(mirror::String::InstanceSize()); java_lang_String->SetStatus(mirror::Class::kStatusResolved, self); + // Setup Reference. + Handle<mirror::Class> java_lang_ref_Reference(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::Reference::ClassSize()))); + mirror::Reference::SetClass(java_lang_ref_Reference.Get()); + java_lang_ref_Reference->SetObjectSize(mirror::Reference::InstanceSize()); + java_lang_ref_Reference->SetStatus(mirror::Class::kStatusResolved, self); + // Create storage for root classes, save away our work so far (requires descriptors). class_roots_ = mirror::ObjectArray<mirror::Class>::Alloc(self, object_array_class.Get(), kClassRootsMax); @@ -264,6 +276,7 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class SetClassRoot(kObjectArrayClass, object_array_class.Get()); SetClassRoot(kCharArrayClass, char_array_class.Get()); SetClassRoot(kJavaLangString, java_lang_String.Get()); + SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference.Get()); // Setup the primitive type classes. SetClassRoot(kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean)); @@ -279,8 +292,8 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class array_iftable_ = AllocIfTable(self, 2); // Create int array type for AllocDexCache (done in AppendToBootClassPath). - Handle<mirror::Class> int_array_class( - hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class)))); + Handle<mirror::Class> int_array_class(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize()))); int_array_class->SetComponentType(GetClassRoot(kPrimitiveInt)); mirror::IntArray::SetArrayClass(int_array_class.Get()); SetClassRoot(kIntArrayClass, int_array_class.Get()); @@ -288,44 +301,47 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class // now that these are registered, we can use AllocClass() and AllocObjectArray // Set up DexCache. This cannot be done later since AppendToBootClassPath calls AllocDexCache. - Handle<mirror::Class> java_lang_DexCache( - hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::DexCacheClass)))); + Handle<mirror::Class> java_lang_DexCache(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::DexCache::ClassSize()))); SetClassRoot(kJavaLangDexCache, java_lang_DexCache.Get()); - java_lang_DexCache->SetObjectSize(sizeof(mirror::DexCache)); + java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize()); java_lang_DexCache->SetStatus(mirror::Class::kStatusResolved, self); // Constructor, Field, Method, and AbstractMethod are necessary so // that FindClass can link members. - Handle<mirror::Class> java_lang_reflect_ArtField( - hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::ArtFieldClass)))); + Handle<mirror::Class> java_lang_reflect_ArtField(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::ArtField::ClassSize()))); CHECK(java_lang_reflect_ArtField.Get() != NULL); - java_lang_reflect_ArtField->SetObjectSize(sizeof(mirror::ArtField)); + java_lang_reflect_ArtField->SetObjectSize(mirror::ArtField::InstanceSize()); SetClassRoot(kJavaLangReflectArtField, java_lang_reflect_ArtField.Get()); java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusResolved, self); mirror::ArtField::SetClass(java_lang_reflect_ArtField.Get()); - Handle<mirror::Class> java_lang_reflect_ArtMethod( - hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::ArtMethodClass)))); + Handle<mirror::Class> java_lang_reflect_ArtMethod(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::ArtMethod::ClassSize()))); CHECK(java_lang_reflect_ArtMethod.Get() != NULL); - java_lang_reflect_ArtMethod->SetObjectSize(sizeof(mirror::ArtMethod)); + java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize()); SetClassRoot(kJavaLangReflectArtMethod, java_lang_reflect_ArtMethod.Get()); java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved, self); mirror::ArtMethod::SetClass(java_lang_reflect_ArtMethod.Get()); // Set up array classes for string, field, method - Handle<mirror::Class> object_array_string( - hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class)))); + Handle<mirror::Class> object_array_string(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), + mirror::ObjectArray<mirror::String>::ClassSize()))); object_array_string->SetComponentType(java_lang_String.Get()); SetClassRoot(kJavaLangStringArrayClass, object_array_string.Get()); - Handle<mirror::Class> object_array_art_method( - hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class)))); + Handle<mirror::Class> object_array_art_method(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), + mirror::ObjectArray<mirror::ArtMethod>::ClassSize()))); object_array_art_method->SetComponentType(java_lang_reflect_ArtMethod.Get()); SetClassRoot(kJavaLangReflectArtMethodArrayClass, object_array_art_method.Get()); - Handle<mirror::Class> object_array_art_field( - hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class)))); + Handle<mirror::Class> object_array_art_field(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), + mirror::ObjectArray<mirror::ArtField>::ClassSize()))); object_array_art_field->SetComponentType(java_lang_reflect_ArtField.Get()); SetClassRoot(kJavaLangReflectArtFieldArrayClass, object_array_art_field.Get()); @@ -359,16 +375,19 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self); mirror::Class* Object_class = FindSystemClass(self, "Ljava/lang/Object;"); CHECK_EQ(java_lang_Object.Get(), Object_class); - CHECK_EQ(java_lang_Object->GetObjectSize(), sizeof(mirror::Object)); + CHECK_EQ(java_lang_Object->GetObjectSize(), mirror::Object::InstanceSize()); java_lang_String->SetStatus(mirror::Class::kStatusNotReady, self); mirror::Class* String_class = FindSystemClass(self, "Ljava/lang/String;"); - CHECK_EQ(java_lang_String.Get(), String_class); - CHECK_EQ(java_lang_String->GetObjectSize(), sizeof(mirror::String)); + std::ostringstream os1, os2; + java_lang_String->DumpClass(os1, mirror::Class::kDumpClassFullDetail); + String_class->DumpClass(os2, mirror::Class::kDumpClassFullDetail); + CHECK_EQ(java_lang_String.Get(), String_class) << os1.str() << "\n\n" << os2.str(); + CHECK_EQ(java_lang_String->GetObjectSize(), mirror::String::InstanceSize()); java_lang_DexCache->SetStatus(mirror::Class::kStatusNotReady, self); mirror::Class* DexCache_class = FindSystemClass(self, "Ljava/lang/DexCache;"); CHECK_EQ(java_lang_String.Get(), String_class); CHECK_EQ(java_lang_DexCache.Get(), DexCache_class); - CHECK_EQ(java_lang_DexCache->GetObjectSize(), sizeof(mirror::DexCache)); + CHECK_EQ(java_lang_DexCache->GetObjectSize(), mirror::DexCache::InstanceSize()); // Setup the primitive array type classes - can't be done until Object has a vtable. SetClassRoot(kBooleanArrayClass, FindSystemClass(self, "[Z")); @@ -452,8 +471,12 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class SetClassRoot(kJavaLangReflectProxy, java_lang_reflect_Proxy); // java.lang.ref classes need to be specially flagged, but otherwise are normal classes - mirror::Class* java_lang_ref_Reference = FindSystemClass(self, "Ljava/lang/ref/Reference;"); - SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference); + // finish initializing Reference class + java_lang_ref_Reference->SetStatus(mirror::Class::kStatusNotReady, self); + mirror::Class* Reference_class = FindSystemClass(self, "Ljava/lang/ref/Reference;"); + CHECK_EQ(java_lang_ref_Reference.Get(), Reference_class); + CHECK_EQ(java_lang_ref_Reference->GetObjectSize(), mirror::Reference::InstanceSize()); + CHECK_EQ(java_lang_ref_Reference->GetClassSize(), mirror::Reference::ClassSize()); mirror::Class* java_lang_ref_FinalizerReference = FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;"); java_lang_ref_FinalizerReference->SetAccessFlags( @@ -476,7 +499,7 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class // Setup the ClassLoader, verifying the object_size_. mirror::Class* java_lang_ClassLoader = FindSystemClass(self, "Ljava/lang/ClassLoader;"); - CHECK_EQ(java_lang_ClassLoader->GetObjectSize(), sizeof(mirror::ClassLoader)); + CHECK_EQ(java_lang_ClassLoader->GetObjectSize(), mirror::ClassLoader::InstanceSize()); SetClassRoot(kJavaLangClassLoader, java_lang_ClassLoader); // Set up java.lang.Throwable, java.lang.ClassNotFoundException, and @@ -802,9 +825,20 @@ bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_ } } else { // TODO: What to lock here? + bool obsolete_file_cleanup_failed; open_oat_file.reset(FindOatFileContainingDexFileFromDexLocation(dex_location, dex_location_checksum_pointer, - kRuntimeISA, error_msgs)); + kRuntimeISA, error_msgs, + &obsolete_file_cleanup_failed)); + // There's no point in going forward and eventually try to regenerate the + // file if we couldn't remove the obsolete one. Mostly likely we will fail + // with the same error when trying to write the new file. + // In case the clean up failure is due to permission issues it's *mandatory* + // to stop to avoid regenerating under the wrong user. + // TODO: should we maybe do this only when we get permission issues? (i.e. EACCESS). + if (obsolete_file_cleanup_failed) { + return false; + } } needs_registering = true; } @@ -1062,7 +1096,9 @@ const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation( const char* dex_location, const uint32_t* const dex_location_checksum, InstructionSet isa, - std::vector<std::string>* error_msgs) { + std::vector<std::string>* error_msgs, + bool* obsolete_file_cleanup_failed) { + *obsolete_file_cleanup_failed = false; // Look for an existing file next to dex. for example, for // /foo/bar/baz.jar, look for /foo/bar/<isa>/baz.odex. std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa)); @@ -1089,9 +1125,18 @@ const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation( if (oat_file != nullptr) { return oat_file; } + if (!open_failed && TEMP_FAILURE_RETRY(unlink(cache_location.c_str())) != 0) { - PLOG(FATAL) << "Failed to remove obsolete oat file from " << cache_location; + std::string error_msg = StringPrintf("Failed to remove obsolete file from %s when searching" + "for dex file %s: %s", + cache_location.c_str(), dex_location, strerror(errno)); + error_msgs->push_back(error_msg); + VLOG(class_linker) << error_msg; + // Let the caller know that we couldn't remove the obsolete file. + // This is a good indication that further writes may fail as well. + *obsolete_file_cleanup_failed = true; } + std::string compound_msg = StringPrintf("Failed to open oat file from %s (error '%s') or %s " "(error '%s').", odex_filename.c_str(), error_msg.c_str(), cache_location.c_str(), cache_error_msg.c_str()); @@ -1159,7 +1204,9 @@ void ClassLinker::InitFromImage() { OatFile& oat_file = GetImageOatFile(space); CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatChecksum(), 0U); CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatDataBegin(), 0U); - CHECK(oat_file.GetOatHeader().GetImageFileLocation().empty()); + const char* image_file_location = oat_file.GetOatHeader(). + GetStoreValueByKey(OatHeader::kImageLocationKey); + CHECK(image_file_location == nullptr || *image_file_location == 0); portable_resolution_trampoline_ = oat_file.GetOatHeader().GetPortableResolutionTrampoline(); quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline(); portable_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetPortableImtConflictTrampoline(); @@ -1220,6 +1267,7 @@ void ClassLinker::InitFromImage() { array_iftable_ = GetClassRoot(kObjectArrayClass)->GetIfTable(); DCHECK(array_iftable_ == GetClassRoot(kBooleanArrayClass)->GetIfTable()); // String class root was set above + mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference)); mirror::ArtField::SetClass(GetClassRoot(kJavaLangReflectArtField)); mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); @@ -1343,6 +1391,7 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* ar ClassLinker::~ClassLinker() { mirror::Class::ResetClass(); mirror::String::ResetClass(); + mirror::Reference::ResetClass(); mirror::ArtField::ResetClass(); mirror::ArtMethod::ResetClass(); mirror::BooleanArray::ResetArrayClass(); @@ -1400,36 +1449,11 @@ mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_fi return dex_cache.Get(); } -// Used to initialize a class in the allocation code path to ensure it is guarded by a StoreStore -// fence. -class InitializeClassVisitor { - public: - explicit InitializeClassVisitor(uint32_t class_size) : class_size_(class_size) { - } - - void operator()(mirror::Object* obj, size_t usable_size) const - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK_LE(class_size_, usable_size); - // Avoid AsClass as object is not yet in live bitmap or allocation stack. - mirror::Class* klass = down_cast<mirror::Class*>(obj); - // DCHECK(klass->IsClass()); - klass->SetClassSize(class_size_); - klass->SetPrimitiveType(Primitive::kPrimNot); // Default to not being primitive. - klass->SetDexClassDefIndex(DexFile::kDexNoIndex16); // Default to no valid class def index. - klass->SetDexTypeIndex(DexFile::kDexNoIndex16); // Default to no valid type index. - } - - private: - const uint32_t class_size_; - - DISALLOW_COPY_AND_ASSIGN(InitializeClassVisitor); -}; - mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Class, uint32_t class_size) { DCHECK_GE(class_size, sizeof(mirror::Class)); gc::Heap* heap = Runtime::Current()->GetHeap(); - InitializeClassVisitor visitor(class_size); + mirror::Class::InitializeClassVisitor visitor(class_size); mirror::Object* k = kMovingClasses ? heap->AllocObject<true>(self, java_lang_Class, class_size, visitor) : heap->AllocNonMovableObject<true>(self, java_lang_Class, class_size, visitor); @@ -1460,9 +1484,33 @@ mirror::ObjectArray<mirror::StackTraceElement>* ClassLinker::AllocStackTraceElem self, GetClassRoot(kJavaLangStackTraceElementArrayClass), length); } -static mirror::Class* EnsureResolved(Thread* self, mirror::Class* klass) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +mirror::Class* ClassLinker::EnsureResolved(Thread* self, const char* descriptor, + mirror::Class* klass) { DCHECK(klass != NULL); + + // For temporary classes we must wait for them to be retired. + if (init_done_ && klass->IsTemp()) { + CHECK(!klass->IsResolved()); + if (klass->IsErroneous()) { + ThrowEarlierClassFailure(klass); + return nullptr; + } + StackHandleScope<1> hs(self); + Handle<mirror::Class> h_class(hs.NewHandle(klass)); + ObjectLock<mirror::Class> lock(self, h_class); + // Loop and wait for the resolving thread to retire this class. + while (!h_class->IsRetired() && !h_class->IsErroneous()) { + lock.WaitIgnoringInterrupts(); + } + if (h_class->IsErroneous()) { + ThrowEarlierClassFailure(h_class.Get()); + return nullptr; + } + CHECK(h_class->IsRetired()); + // Get the updated class from class table. + klass = LookupClass(descriptor, h_class.Get()->GetClassLoader()); + } + // Wait for the class if it has not already been linked. if (!klass->IsResolved() && !klass->IsErroneous()) { StackHandleScope<1> hs(self); @@ -1479,6 +1527,7 @@ static mirror::Class* EnsureResolved(Thread* self, mirror::Class* klass) lock.WaitIgnoringInterrupts(); } } + if (klass->IsErroneous()) { ThrowEarlierClassFailure(klass); return nullptr; @@ -1502,7 +1551,7 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, // Find the class in the loaded classes table. mirror::Class* klass = LookupClass(descriptor, class_loader.Get()); if (klass != NULL) { - return EnsureResolved(self, klass); + return EnsureResolved(self, descriptor, klass); } // Class is not yet loaded. if (descriptor[0] == '[') { @@ -1576,8 +1625,10 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) { Thread* self = Thread::Current(); - StackHandleScope<2> hs(self); + StackHandleScope<3> hs(self); auto klass = hs.NewHandle<mirror::Class>(nullptr); + bool should_allocate = false; + // Load the class from the dex file. if (UNLIKELY(!init_done_)) { // finish up init of hand crafted class_roots_ @@ -1587,6 +1638,8 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor, klass.Assign(GetClassRoot(kJavaLangClass)); } else if (strcmp(descriptor, "Ljava/lang/String;") == 0) { klass.Assign(GetClassRoot(kJavaLangString)); + } else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) { + klass.Assign(GetClassRoot(kJavaLangRefReference)); } else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) { klass.Assign(GetClassRoot(kJavaLangDexCache)); } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtField;") == 0) { @@ -1594,10 +1647,18 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor, } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) { klass.Assign(GetClassRoot(kJavaLangReflectArtMethod)); } else { - klass.Assign(AllocClass(self, SizeOfClass(dex_file, dex_class_def))); + should_allocate = true; } } else { - klass.Assign(AllocClass(self, SizeOfClass(dex_file, dex_class_def))); + should_allocate = true; + } + + if (should_allocate) { + // Allocate a class with the status of not ready. + // Interface object should get the right size here. Regular class will + // figure out the right size later and be replaced with one of the right + // size when the class becomes resolved. + klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def))); } if (UNLIKELY(klass.Get() == NULL)) { CHECK(self->IsExceptionPending()); // Expect an OOME. @@ -1612,13 +1673,15 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor, } ObjectLock<mirror::Class> lock(self, klass); klass->SetClinitThreadId(self->GetTid()); + // Add the newly loaded class to the loaded classes table. mirror::Class* existing = InsertClass(descriptor, klass.Get(), Hash(descriptor)); if (existing != NULL) { // We failed to insert because we raced with another thread. Calling EnsureResolved may cause // this thread to block. - return EnsureResolved(self, existing); + return EnsureResolved(self, descriptor, existing); } + // Finish loading (if necessary) by finding parents CHECK(!klass->IsLoaded()); if (!LoadSuperAndInterfaces(klass, dex_file)) { @@ -1631,12 +1694,17 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor, CHECK(!klass->IsResolved()); // TODO: Use fast jobjects? auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr); - if (!LinkClass(self, klass, interfaces)) { + + mirror::Class* new_class = nullptr; + if (!LinkClass(self, descriptor, klass, interfaces, &new_class)) { // Linking failed. klass->SetStatus(mirror::Class::kStatusError, self); return NULL; } - CHECK(klass->IsResolved()); + CHECK(new_class != nullptr) << descriptor; + CHECK(new_class->IsResolved()) << descriptor; + + Handle<mirror::Class> new_class_h(hs.NewHandle(new_class)); /* * We send CLASS_PREPARE events to the debugger from here. The @@ -1649,14 +1717,13 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor, * The class has been prepared and resolved but possibly not yet verified * at this point. */ - Dbg::PostClassPrepare(klass.Get()); + Dbg::PostClassPrepare(new_class_h.Get()); - return klass.Get(); + return new_class_h.Get(); } -// Precomputes size that will be needed for Class, matching LinkStaticFields -uint32_t ClassLinker::SizeOfClass(const DexFile& dex_file, - const DexFile::ClassDef& dex_class_def) { +uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, + const DexFile::ClassDef& dex_class_def) { const byte* class_data = dex_file.GetClassData(dex_class_def); size_t num_ref = 0; size_t num_32 = 0; @@ -1675,24 +1742,7 @@ uint32_t ClassLinker::SizeOfClass(const DexFile& dex_file, } } } - // start with generic class data - uint32_t size = sizeof(mirror::Class); - // follow with reference fields which must be contiguous at start - size += (num_ref * sizeof(uint32_t)); - // if there are 64-bit fields to add, make sure they are aligned - if (num_64 != 0 && size != RoundUp(size, 8)) { // for 64-bit alignment - if (num_32 != 0) { - // use an available 32-bit field for padding - num_32--; - } - size += sizeof(uint32_t); // either way, we are adding a word - DCHECK_EQ(size, RoundUp(size, 8)); - } - // tack on any 64-bit fields now that alignment is assured - size += (num_64 * sizeof(uint64_t)); - // tack on any remaining 32-bit fields - size += (num_32 * sizeof(uint32_t)); - return size; + return mirror::Class::ComputeClassSize(false, 0, num_32, num_64, num_ref); } OatFile::OatClass ClassLinker::GetOatClass(const DexFile& dex_file, uint16_t class_def_idx) { @@ -2306,7 +2356,7 @@ void ClassLinker::FixupDexCaches(mirror::ArtMethod* resolution_method) { } mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) { - mirror::Class* klass = AllocClass(self, sizeof(mirror::Class)); + mirror::Class* klass = AllocClass(self, mirror::Class::PrimitiveClassSize()); if (UNLIKELY(klass == NULL)) { return NULL; } @@ -2411,7 +2461,7 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto } } if (new_class.Get() == nullptr) { - new_class.Assign(AllocClass(self, sizeof(mirror::Class))); + new_class.Assign(AllocClass(self, mirror::Array::ClassSize())); if (new_class.Get() == nullptr) { return nullptr; } @@ -2424,6 +2474,8 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto new_class->SetVTable(java_lang_Object->GetVTable()); new_class->SetPrimitiveType(Primitive::kPrimNot); new_class->SetClassLoader(component_type->GetClassLoader()); + new_class->SetStatus(mirror::Class::kStatusLoaded, self); + new_class->PopulateEmbeddedImtAndVTable(); new_class->SetStatus(mirror::Class::kStatusInitialized, self); // don't need to set new_class->SetObjectSize(..) // because Object::SizeOf delegates to Array::SizeOf @@ -2517,7 +2569,8 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* k if (existing != NULL) { return existing; } - if (kIsDebugBuild && klass->GetClassLoader() == NULL && dex_cache_image_class_lookup_required_) { + if (kIsDebugBuild && !klass->IsTemp() && klass->GetClassLoader() == NULL && + dex_cache_image_class_lookup_required_) { // Check a class loaded with the system class loader matches one in the image if the class // is in the image. existing = LookupClassFromImage(descriptor); @@ -2533,6 +2586,50 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* k return NULL; } +mirror::Class* ClassLinker::UpdateClass(const char* descriptor, mirror::Class* klass, + size_t hash) { + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + mirror::Class* existing = + LookupClassFromTableLocked(descriptor, klass->GetClassLoader(), hash); + + if (existing == nullptr) { + CHECK(klass->IsProxyClass()); + return nullptr; + } + + CHECK_NE(existing, klass) << descriptor; + CHECK(!existing->IsResolved()) << descriptor; + CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusResolving) << descriptor; + + for (auto it = class_table_.lower_bound(hash), end = class_table_.end(); it != end && it->first == hash; + ++it) { + mirror::Class* entry = it->second; + if (entry == existing) { + class_table_.erase(it); + break; + } + } + + CHECK(!klass->IsTemp()) << descriptor; + if (kIsDebugBuild && klass->GetClassLoader() == nullptr && + dex_cache_image_class_lookup_required_) { + // Check a class loaded with the system class loader matches one in the image if the class + // is in the image. + existing = LookupClassFromImage(descriptor); + if (existing != nullptr) { + CHECK(klass == existing) << descriptor; + } + } + VerifyObject(klass); + + class_table_.insert(std::make_pair(hash, klass)); + if (log_new_class_table_roots_) { + new_class_roots_.push_back(std::make_pair(hash, klass)); + } + + return existing; +} + bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* class_loader) { size_t hash = Hash(descriptor); WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); @@ -2952,8 +3049,8 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& jobjectArray methods, jobjectArray throws) { Thread* self = soa.Self(); StackHandleScope<8> hs(self); - Handle<mirror::Class> klass(hs.NewHandle(AllocClass(self, GetClassRoot(kJavaLangClass), - sizeof(mirror::SynthesizedProxyClass)))); + Handle<mirror::Class> klass(hs.NewHandle( + AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class)))); if (klass.Get() == NULL) { CHECK(self->IsExceptionPending()); // OOME. return NULL; @@ -3044,20 +3141,31 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& klass->SetStatus(mirror::Class::kStatusLoaded, self); // Now effectively in the loaded state. self->AssertNoPendingException(); + std::string descriptor(GetDescriptorForProxy(klass.Get())); + mirror::Class* new_class = nullptr; { - ObjectLock<mirror::Class> lock(self, klass); // Must hold lock on object when resolved. + ObjectLock<mirror::Class> resolution_lock(self, klass); // Must hold lock on object when resolved. // Link the fields and virtual methods, creating vtable and iftables - Handle<mirror::ObjectArray<mirror::Class>> h_interfaces( + Handle<mirror::ObjectArray<mirror::Class> > h_interfaces( hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces))); - if (!LinkClass(self, klass, h_interfaces)) { + if (!LinkClass(self, descriptor.c_str(), klass, h_interfaces, &new_class)) { klass->SetStatus(mirror::Class::kStatusError, self); return nullptr; } + } - interfaces_sfield->SetObject<false>( - klass.Get(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)); - throws_sfield->SetObject<false>( - klass.Get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws)); + CHECK(klass->IsRetired()); + CHECK_NE(klass.Get(), new_class); + klass.Assign(new_class); + + CHECK_EQ(interfaces_sfield->GetDeclaringClass(), new_class); + interfaces_sfield->SetObject<false>(klass.Get(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)); + CHECK_EQ(throws_sfield->GetDeclaringClass(), new_class); + throws_sfield->SetObject<false>(klass.Get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws)); + + { + // Lock on klass is released. Lock new class object. + ObjectLock<mirror::Class> initialization_lock(self, klass); klass->SetStatus(mirror::Class::kStatusInitialized, self); } @@ -3083,14 +3191,11 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& decoded_name->ToModifiedUtf8().c_str())); CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name); - mirror::SynthesizedProxyClass* synth_proxy_class = - down_cast<mirror::SynthesizedProxyClass*>(klass.Get()); - CHECK_EQ(synth_proxy_class->GetInterfaces(), + CHECK_EQ(klass.Get()->GetInterfaces(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)); - CHECK_EQ(synth_proxy_class->GetThrows(), + CHECK_EQ(klass.Get()->GetThrows(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws)); } - std::string descriptor(GetDescriptorForProxy(klass.Get())); mirror::Class* existing = InsertClass(descriptor.c_str(), klass.Get(), Hash(descriptor.c_str())); CHECK(existing == nullptr); return klass.Get(); @@ -3524,9 +3629,49 @@ void ClassLinker::ConstructFieldMap(const DexFile& dex_file, const DexFile::Clas } } -bool ClassLinker::LinkClass(Thread* self, Handle<mirror::Class> klass, - Handle<mirror::ObjectArray<mirror::Class>> interfaces) { +void ClassLinker::FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class) { + mirror::ObjectArray<mirror::ArtField>* fields = new_class->GetIFields(); + if (fields != nullptr) { + for (int index = 0; index < fields->GetLength(); index ++) { + if (fields->Get(index)->GetDeclaringClass() == temp_class) { + fields->Get(index)->SetDeclaringClass(new_class); + } + } + } + + fields = new_class->GetSFields(); + if (fields != nullptr) { + for (int index = 0; index < fields->GetLength(); index ++) { + if (fields->Get(index)->GetDeclaringClass() == temp_class) { + fields->Get(index)->SetDeclaringClass(new_class); + } + } + } + + mirror::ObjectArray<mirror::ArtMethod>* methods = new_class->GetDirectMethods(); + if (methods != nullptr) { + for (int index = 0; index < methods->GetLength(); index ++) { + if (methods->Get(index)->GetDeclaringClass() == temp_class) { + methods->Get(index)->SetDeclaringClass(new_class); + } + } + } + + methods = new_class->GetVirtualMethods(); + if (methods != nullptr) { + for (int index = 0; index < methods->GetLength(); index ++) { + if (methods->Get(index)->GetDeclaringClass() == temp_class) { + methods->Get(index)->SetDeclaringClass(new_class); + } + } + } +} + +bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass, + Handle<mirror::ObjectArray<mirror::Class>> interfaces, + mirror::Class** new_class) { CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus()); + if (!LinkSuperClass(klass)) { return false; } @@ -3536,13 +3681,60 @@ bool ClassLinker::LinkClass(Thread* self, Handle<mirror::Class> klass, if (!LinkInstanceFields(klass)) { return false; } - if (!LinkStaticFields(klass)) { + size_t class_size; + if (!LinkStaticFields(klass, &class_size)) { return false; } CreateReferenceInstanceOffsets(klass); CreateReferenceStaticOffsets(klass); CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus()); - klass->SetStatus(mirror::Class::kStatusResolved, self); + + if (!klass->IsTemp() || (!init_done_ && klass->GetClassSize() == class_size)) { + // We don't need to retire this class as it has no embedded tables or it was created the + // correct size during class linker initialization. + CHECK_EQ(klass->GetClassSize(), class_size) << PrettyDescriptor(klass.Get()); + + if (klass->ShouldHaveEmbeddedImtAndVTable()) { + klass->PopulateEmbeddedImtAndVTable(); + } + + // This will notify waiters on klass that saw the not yet resolved + // class in the class_table_ during EnsureResolved. + klass->SetStatus(mirror::Class::kStatusResolved, self); + *new_class = klass.Get(); + } else { + CHECK(!klass->IsResolved()); + // Retire the temporary class and create the correctly sized resolved class. + *new_class = klass->CopyOf(self, class_size); + if (UNLIKELY(*new_class == NULL)) { + CHECK(self->IsExceptionPending()); // Expect an OOME. + klass->SetStatus(mirror::Class::kStatusError, self); + return false; + } + + CHECK_EQ((*new_class)->GetClassSize(), class_size); + StackHandleScope<1> hs(self); + auto new_class_h = hs.NewHandleWrapper<mirror::Class>(new_class); + ObjectLock<mirror::Class> lock(self, new_class_h); + + FixupTemporaryDeclaringClass(klass.Get(), new_class_h.Get()); + + mirror::Class* existing = UpdateClass(descriptor, new_class_h.Get(), Hash(descriptor)); + CHECK(existing == NULL || existing == klass.Get()); + + // This will notify waiters on temp class that saw the not yet resolved class in the + // class_table_ during EnsureResolved. + klass->SetStatus(mirror::Class::kStatusRetired, self); + + CHECK_EQ(new_class_h->GetStatus(), mirror::Class::kStatusResolving); + // This will notify waiters on new_class that saw the not yet resolved + // class in the class_table_ during EnsureResolved. + new_class_h->SetStatus(mirror::Class::kStatusResolved, self); + + // Only embedded imt should be used from this point. + new_class_h->SetImTable(NULL); + // TODO: remove vtable and only use embedded vtable. + } return true; } @@ -3563,6 +3755,7 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexF PrettyDescriptor(klass.Get()).c_str()); return false; } + CHECK(super_class->IsResolved()); klass->SetSuperClass(super_class); } const DexFile::TypeList* interfaces = dex_file.GetInterfacesList(class_def); @@ -3876,7 +4069,7 @@ bool ClassLinker::LinkInterfaceMethods(Handle<mirror::Class> klass, // Allocate imtable bool imtable_changed = false; Handle<mirror::ObjectArray<mirror::ArtMethod>> imtable( - hs.NewHandle(AllocArtMethodArray(self, kImtSize))); + hs.NewHandle(AllocArtMethodArray(self, mirror::Class::kImtSize))); if (UNLIKELY(imtable.Get() == NULL)) { CHECK(self->IsExceptionPending()); // OOME. return false; @@ -3923,7 +4116,7 @@ bool ClassLinker::LinkInterfaceMethods(Handle<mirror::Class> klass, } method_array->Set<false>(j, vtable_method); // Place method in imt if entry is empty, place conflict otherwise. - uint32_t imt_index = interface_method->GetDexMethodIndex() % kImtSize; + uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize; if (imtable->Get(imt_index) == NULL) { imtable->Set<false>(imt_index, vtable_method); imtable_changed = true; @@ -3961,7 +4154,7 @@ bool ClassLinker::LinkInterfaceMethods(Handle<mirror::Class> klass, if (imtable_changed) { // Fill in empty entries in interface method table with conflict. mirror::ArtMethod* imt_conflict_method = runtime->GetImtConflictMethod(); - for (size_t i = 0; i < kImtSize; i++) { + for (size_t i = 0; i < mirror::Class::kImtSize; i++) { if (imtable->Get(i) == NULL) { imtable->Set<false>(i, imt_conflict_method); } @@ -4018,15 +4211,12 @@ bool ClassLinker::LinkInterfaceMethods(Handle<mirror::Class> klass, bool ClassLinker::LinkInstanceFields(Handle<mirror::Class> klass) { CHECK(klass.Get() != NULL); - return LinkFields(klass, false); + return LinkFields(klass, false, nullptr); } -bool ClassLinker::LinkStaticFields(Handle<mirror::Class> klass) { +bool ClassLinker::LinkStaticFields(Handle<mirror::Class> klass, size_t* class_size) { CHECK(klass.Get() != NULL); - size_t allocated_class_size = klass->GetClassSize(); - bool success = LinkFields(klass, true); - CHECK_EQ(allocated_class_size, klass->GetClassSize()); - return success; + return LinkFields(klass, true, class_size); } struct LinkFieldsComparator { @@ -4056,19 +4246,23 @@ struct LinkFieldsComparator { } }; -bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static) { +bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static, size_t* class_size) { size_t num_fields = is_static ? klass->NumStaticFields() : klass->NumInstanceFields(); mirror::ObjectArray<mirror::ArtField>* fields = is_static ? klass->GetSFields() : klass->GetIFields(); - // Initialize size and field_offset - size_t size; + // Initialize field_offset MemberOffset field_offset(0); if (is_static) { - size = klass->GetClassSize(); - field_offset = mirror::Class::FieldsOffset(); + uint32_t base = sizeof(mirror::Class); // Static fields come after the class. + if (klass->ShouldHaveEmbeddedImtAndVTable()) { + // Static fields come after the embedded tables. + base = mirror::Class::ComputeClassSize(true, klass->GetVTableDuringLinking()->GetLength(), + 0, 0, 0); + } + field_offset = MemberOffset(base); } else { mirror::Class* super_class = klass->GetSuperClass(); if (super_class != NULL) { @@ -4076,7 +4270,6 @@ bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static) { << PrettyClass(klass.Get()) << " " << PrettyClass(super_class); field_offset = MemberOffset(super_class->GetObjectSize()); } - size = field_offset.Uint32Value(); } CHECK_EQ(num_fields == 0, fields == NULL) << PrettyClass(klass.Get()); @@ -4189,11 +4382,12 @@ bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static) { DCHECK_EQ(num_fields, num_reference_fields) << PrettyClass(klass.Get()); } } - size = field_offset.Uint32Value(); + + size_t size = field_offset.Uint32Value(); // Update klass if (is_static) { klass->SetNumReferenceStaticFields(num_reference_fields); - klass->SetClassSize(size); + *class_size = size; } else { klass->SetNumReferenceInstanceFields(num_reference_fields); if (!klass->IsVariableSize()) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 60dad7b930..c17f88d6d8 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -31,6 +31,7 @@ #include "read_barrier.h" namespace art { + namespace gc { namespace space { class ImageSpace; @@ -56,11 +57,6 @@ enum VisitRootFlags : uint8_t; class ClassLinker { public: - // Interface method table size. Increasing this value reduces the chance of two interface methods - // colliding in the interface method table but increases the size of classes that implement - // (non-marker) interfaces. - static constexpr size_t kImtSize = 64; - explicit ClassLinker(InternTable* intern_table); ~ClassLinker(); @@ -385,6 +381,14 @@ class ClassLinker { // Special code to allocate an art method, use this instead of class->AllocObject. mirror::ArtMethod* AllocArtMethod(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::ObjectArray<mirror::Class>* GetClassRoots() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ObjectArray<mirror::Class>* class_roots = + ReadBarrier::BarrierForRoot<mirror::ObjectArray<mirror::Class>, kWithReadBarrier>( + &class_roots_); + DCHECK(class_roots != NULL); + return class_roots; + } + private: const OatFile::OatMethod GetOatMethodFor(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -427,8 +431,10 @@ class ClassLinker { mirror::Class* c, SafeMap<uint32_t, mirror::ArtField*>& field_map) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - uint32_t SizeOfClass(const DexFile& dex_file, - const DexFile::ClassDef& dex_class_def); + // Precomputes size needed for Class, in the case of a non-temporary class this size must be + // sufficient to hold all static fields. + uint32_t SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, + const DexFile::ClassDef& dex_class_def); void LoadClass(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, @@ -481,8 +487,9 @@ class ClassLinker { mirror::Class* klass2) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkClass(Thread* self, Handle<mirror::Class> klass, - Handle<mirror::ObjectArray<mirror::Class>> interfaces) + bool LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass, + Handle<mirror::ObjectArray<mirror::Class>> interfaces, + mirror::Class** new_class) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool LinkSuperClass(Handle<mirror::Class> klass) @@ -502,17 +509,16 @@ class ClassLinker { Handle<mirror::ObjectArray<mirror::Class>> interfaces) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkStaticFields(Handle<mirror::Class> klass) + bool LinkStaticFields(Handle<mirror::Class> klass, size_t* class_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool LinkInstanceFields(Handle<mirror::Class> klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkFields(Handle<mirror::Class> klass, bool is_static) + bool LinkFields(Handle<mirror::Class> klass, bool is_static, size_t* class_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class, const DexFile& dex_file, uint32_t dex_method_index, uint32_t method_index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CreateReferenceInstanceOffsets(Handle<mirror::Class> klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void CreateReferenceStaticOffsets(Handle<mirror::Class> klass) @@ -565,7 +571,8 @@ class ClassLinker { const OatFile* FindOatFileContainingDexFileFromDexLocation(const char* location, const uint32_t* const location_checksum, InstructionSet isa, - std::vector<std::string>* error_msgs) + std::vector<std::string>* error_msgs, + bool* obsolete_file_cleanup_failed) LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_); // Find a verify an oat file with the given dex file. Will return nullptr when the oat file @@ -612,11 +619,27 @@ class ClassLinker { size_t hash) SHARED_LOCKS_REQUIRED(Locks::classlinker_classes_lock_, Locks::mutator_lock_); + mirror::Class* UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash) + LOCKS_EXCLUDED(Locks::classlinker_classes_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void MoveImageClassesToClassTable() LOCKS_EXCLUDED(Locks::classlinker_classes_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::Class* LookupClassFromImage(const char* descriptor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // EnsureResolved is called to make sure that a class in the class_table_ has been resolved + // before returning it to the caller. Its the responsibility of the thread that placed the class + // in the table to make it resolved. The thread doing resolution must notify on the class' lock + // when resolution has occurred. This happens in mirror::Class::SetStatus. As resolution may + // retire a class, the version of the class in the table is returned and this may differ from + // the class passed in. + mirror::Class* EnsureResolved(Thread* self, const char* descriptor, mirror::Class* klass) + WARN_UNUSED SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // indexes into class_roots_. // needs to be kept in sync with class_roots_descriptors_. enum ClassRoot { @@ -664,14 +687,6 @@ class ClassLinker { void SetClassRoot(ClassRoot class_root, mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::ObjectArray<mirror::Class>* GetClassRoots() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::ObjectArray<mirror::Class>* class_roots = - ReadBarrier::BarrierForRoot<mirror::ObjectArray<mirror::Class>, kWithReadBarrier>( - &class_roots_); - DCHECK(class_roots != NULL); - return class_roots; - } - static const char* class_roots_descriptors_[]; const char* GetClassRootDescriptor(ClassRoot class_root) { @@ -695,6 +710,8 @@ class ClassLinker { InternTable* intern_table_; + // Trampolines within the image the bounce to runtime entrypoints. Done so that there is a single + // patch point within the image. TODO: make these proper relocations. const void* portable_resolution_trampoline_; const void* quick_resolution_trampoline_; const void* portable_imt_conflict_trampoline_; diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 04f6946aa8..21fe0067ed 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -22,7 +22,8 @@ #include "class_linker-inl.h" #include "common_runtime_test.h" #include "dex_file.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" +#include "field_helper.h" #include "gc/heap.h" #include "mirror/art_field-inl.h" #include "mirror/art_method.h" @@ -34,7 +35,10 @@ #include "mirror/proxy.h" #include "mirror/reference.h" #include "mirror/stack_trace_element.h" +#include "mirror/string-inl.h" #include "handle_scope-inl.h" +#include "scoped_thread_state_change.h" +#include "thread-inl.h" namespace art { @@ -572,37 +576,6 @@ struct ProxyOffsets : public CheckOffsets<mirror::Proxy> { }; }; -struct ClassClassOffsets : public CheckOffsets<mirror::ClassClass> { - ClassClassOffsets() : CheckOffsets<mirror::ClassClass>(true, "Ljava/lang/Class;") { - // alphabetical 64-bit - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ClassClass, serialVersionUID_), "serialVersionUID")); - }; -}; - -struct StringClassOffsets : public CheckOffsets<mirror::StringClass> { - StringClassOffsets() : CheckOffsets<mirror::StringClass>(true, "Ljava/lang/String;") { - // alphabetical references - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, ASCII_), "ASCII")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, CASE_INSENSITIVE_ORDER_), "CASE_INSENSITIVE_ORDER")); - - // alphabetical 32-bit - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, REPLACEMENT_CHAR_), "REPLACEMENT_CHAR")); - - // alphabetical 64-bit - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, serialVersionUID_), "serialVersionUID")); - }; -}; - -struct ArtFieldClassOffsets : public CheckOffsets<mirror::ArtFieldClass> { - ArtFieldClassOffsets() : CheckOffsets<mirror::ArtFieldClass>(true, "Ljava/lang/reflect/ArtField;") { - }; -}; - -struct ArtMethodClassOffsets : public CheckOffsets<mirror::ArtMethodClass> { - ArtMethodClassOffsets() : CheckOffsets<mirror::ArtMethodClass>(true, "Ljava/lang/reflect/ArtMethod;") { - }; -}; - struct DexCacheOffsets : public CheckOffsets<mirror::DexCache> { DexCacheOffsets() : CheckOffsets<mirror::DexCache>(false, "Ljava/lang/DexCache;") { // alphabetical references @@ -652,11 +625,6 @@ TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) { EXPECT_TRUE(DexCacheOffsets().Check()); EXPECT_TRUE(ReferenceOffsets().Check()); EXPECT_TRUE(FinalizerReferenceOffsets().Check()); - - EXPECT_TRUE(ClassClassOffsets().Check()); - EXPECT_TRUE(StringClassOffsets().Check()); - EXPECT_TRUE(ArtFieldClassOffsets().Check()); - EXPECT_TRUE(ArtMethodClassOffsets().Check()); } TEST_F(ClassLinkerTest, FindClassNonexistent) { @@ -1091,4 +1059,28 @@ TEST_F(ClassLinkerTest, ClassRootDescriptors) { } } +TEST_F(ClassLinkerTest, ValidatePredefinedClassSizes) { + ScopedObjectAccess soa(Thread::Current()); + NullHandle<mirror::ClassLoader> class_loader; + mirror::Class* c; + + c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Class;", class_loader); + EXPECT_EQ(c->GetClassSize(), mirror::Class::ClassClassSize()); + + c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Object;", class_loader); + EXPECT_EQ(c->GetClassSize(), mirror::Object::ClassSize()); + + c = class_linker_->FindClass(soa.Self(), "Ljava/lang/String;", class_loader); + EXPECT_EQ(c->GetClassSize(), mirror::String::ClassSize()); + + c = class_linker_->FindClass(soa.Self(), "Ljava/lang/DexCache;", class_loader); + EXPECT_EQ(c->GetClassSize(), mirror::DexCache::ClassSize()); + + c = class_linker_->FindClass(soa.Self(), "Ljava/lang/reflect/ArtField;", class_loader); + EXPECT_EQ(c->GetClassSize(), mirror::ArtField::ClassSize()); + + c = class_linker_->FindClass(soa.Self(), "Ljava/lang/reflect/ArtMethod;", class_loader); + EXPECT_EQ(c->GetClassSize(), mirror::ArtMethod::ClassSize()); +} + } // namespace art diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 0ed8b63b4d..f47f13d4b5 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -14,8 +14,31 @@ * limitations under the License. */ +#include "common_runtime_test.h" + +#include <dirent.h> +#include <dlfcn.h> +#include <fcntl.h> +#include <ScopedLocalRef.h> + +#include "../../external/icu/icu4c/source/common/unicode/uvernum.h" #include "base/logging.h" +#include "base/stl_util.h" +#include "base/stringprintf.h" +#include "base/unix_file/fd_file.h" +#include "class_linker.h" +#include "compiler_callbacks.h" +#include "dex_file.h" +#include "gc/heap.h" #include "gtest/gtest.h" +#include "jni_internal.h" +#include "mirror/class_loader.h" +#include "noop_compiler_callbacks.h" +#include "os.h" +#include "runtime-inl.h" +#include "scoped_thread_state_change.h" +#include "thread.h" +#include "well_known_classes.h" int main(int argc, char **argv) { art::InitLogging(argv); @@ -23,3 +46,293 @@ int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } + +namespace art { + +ScratchFile::ScratchFile() { + // ANDROID_DATA needs to be set + CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA")) << + "Are you subclassing RuntimeTest?"; + filename_ = getenv("ANDROID_DATA"); + filename_ += "/TmpFile-XXXXXX"; + int fd = mkstemp(&filename_[0]); + CHECK_NE(-1, fd); + file_.reset(new File(fd, GetFilename())); +} + +ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix) { + filename_ = other.GetFilename(); + filename_ += suffix; + int fd = open(filename_.c_str(), O_RDWR | O_CREAT, 0666); + CHECK_NE(-1, fd); + file_.reset(new File(fd, GetFilename())); +} + +ScratchFile::ScratchFile(File* file) { + CHECK(file != NULL); + filename_ = file->GetPath(); + file_.reset(file); +} + +ScratchFile::~ScratchFile() { + Unlink(); +} + +int ScratchFile::GetFd() const { + return file_->Fd(); +} + +void ScratchFile::Unlink() { + if (!OS::FileExists(filename_.c_str())) { + return; + } + int unlink_result = unlink(filename_.c_str()); + CHECK_EQ(0, unlink_result); +} + +CommonRuntimeTest::CommonRuntimeTest() {} +CommonRuntimeTest::~CommonRuntimeTest() {} + +void CommonRuntimeTest::SetEnvironmentVariables(std::string& android_data) { + if (IsHost()) { + // $ANDROID_ROOT is set on the device, but not necessarily on the host. + // But it needs to be set so that icu4c can find its locale data. + const char* android_root_from_env = getenv("ANDROID_ROOT"); + if (android_root_from_env == nullptr) { + // Use ANDROID_HOST_OUT for ANDROID_ROOT if it is set. + const char* android_host_out = getenv("ANDROID_HOST_OUT"); + if (android_host_out != nullptr) { + setenv("ANDROID_ROOT", android_host_out, 1); + } else { + // Build it from ANDROID_BUILD_TOP or cwd + std::string root; + const char* android_build_top = getenv("ANDROID_BUILD_TOP"); + if (android_build_top != nullptr) { + root += android_build_top; + } else { + // Not set by build server, so default to current directory + char* cwd = getcwd(nullptr, 0); + setenv("ANDROID_BUILD_TOP", cwd, 1); + root += cwd; + free(cwd); + } +#if defined(__linux__) + root += "/out/host/linux-x86"; +#elif defined(__APPLE__) + root += "/out/host/darwin-x86"; +#else +#error unsupported OS +#endif + setenv("ANDROID_ROOT", root.c_str(), 1); + } + } + setenv("LD_LIBRARY_PATH", ":", 0); // Required by java.lang.System.<clinit>. + + // Not set by build server, so default + if (getenv("ANDROID_HOST_OUT") == nullptr) { + setenv("ANDROID_HOST_OUT", getenv("ANDROID_ROOT"), 1); + } + } + + // On target, Cannot use /mnt/sdcard because it is mounted noexec, so use subdir of dalvik-cache + android_data = (IsHost() ? "/tmp/art-data-XXXXXX" : "/data/dalvik-cache/art-data-XXXXXX"); + if (mkdtemp(&android_data[0]) == nullptr) { + PLOG(FATAL) << "mkdtemp(\"" << &android_data[0] << "\") failed"; + } + setenv("ANDROID_DATA", android_data.c_str(), 1); +} + +const DexFile* CommonRuntimeTest::LoadExpectSingleDexFile(const char* location) { + std::vector<const DexFile*> dex_files; + std::string error_msg; + if (!DexFile::Open(location, location, &error_msg, &dex_files)) { + LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n"; + return nullptr; + } else { + CHECK_EQ(1U, dex_files.size()) << "Expected only one dex file in " << location; + return dex_files[0]; + } +} + +void CommonRuntimeTest::SetUp() { + SetEnvironmentVariables(android_data_); + dalvik_cache_.append(android_data_.c_str()); + dalvik_cache_.append("/dalvik-cache"); + int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700); + ASSERT_EQ(mkdir_result, 0); + + std::string error_msg; + java_lang_dex_file_ = LoadExpectSingleDexFile(GetLibCoreDexFileName().c_str()); + boot_class_path_.push_back(java_lang_dex_file_); + + std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB)); + std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB)); + + callbacks_.reset(new NoopCompilerCallbacks()); + + RuntimeOptions options; + options.push_back(std::make_pair("bootclasspath", &boot_class_path_)); + options.push_back(std::make_pair("-Xcheck:jni", nullptr)); + options.push_back(std::make_pair(min_heap_string.c_str(), nullptr)); + options.push_back(std::make_pair(max_heap_string.c_str(), nullptr)); + options.push_back(std::make_pair("compilercallbacks", callbacks_.get())); + SetUpRuntimeOptions(&options); + if (!Runtime::Create(options, false)) { + LOG(FATAL) << "Failed to create runtime"; + return; + } + runtime_.reset(Runtime::Current()); + class_linker_ = runtime_->GetClassLinker(); + class_linker_->FixupDexCaches(runtime_->GetResolutionMethod()); + class_linker_->RunRootClinits(); + + // Runtime::Create acquired the mutator_lock_ that is normally given away when we + // Runtime::Start, give it away now and then switch to a more managable ScopedObjectAccess. + Thread::Current()->TransitionFromRunnableToSuspended(kNative); + + // We're back in native, take the opportunity to initialize well known classes. + WellKnownClasses::Init(Thread::Current()->GetJniEnv()); + + // Create the heap thread pool so that the GC runs in parallel for tests. Normally, the thread + // pool is created by the runtime. + runtime_->GetHeap()->CreateThreadPool(); + runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption before the test +} + +void CommonRuntimeTest::TearDown() { + const char* android_data = getenv("ANDROID_DATA"); + ASSERT_TRUE(android_data != nullptr); + DIR* dir = opendir(dalvik_cache_.c_str()); + ASSERT_TRUE(dir != nullptr); + dirent* e; + while ((e = readdir(dir)) != nullptr) { + if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) { + continue; + } + std::string filename(dalvik_cache_); + filename.push_back('/'); + filename.append(e->d_name); + int unlink_result = unlink(filename.c_str()); + ASSERT_EQ(0, unlink_result); + } + closedir(dir); + int rmdir_cache_result = rmdir(dalvik_cache_.c_str()); + ASSERT_EQ(0, rmdir_cache_result); + int rmdir_data_result = rmdir(android_data_.c_str()); + ASSERT_EQ(0, rmdir_data_result); + + // icu4c has a fixed 10-element array "gCommonICUDataArray". + // If we run > 10 tests, we fill that array and u_setCommonData fails. + // There's a function to clear the array, but it's not public... + typedef void (*IcuCleanupFn)(); + void* sym = dlsym(RTLD_DEFAULT, "u_cleanup_" U_ICU_VERSION_SHORT); + CHECK(sym != nullptr) << dlerror(); + IcuCleanupFn icu_cleanup_fn = reinterpret_cast<IcuCleanupFn>(sym); + (*icu_cleanup_fn)(); + + STLDeleteElements(&opened_dex_files_); + + Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test +} + +std::string CommonRuntimeTest::GetLibCoreDexFileName() { + return GetDexFileName("core-libart"); +} + +std::string CommonRuntimeTest::GetDexFileName(const std::string& jar_prefix) { + if (IsHost()) { + const char* host_dir = getenv("ANDROID_HOST_OUT"); + CHECK(host_dir != nullptr); + return StringPrintf("%s/framework/%s-hostdex.jar", host_dir, jar_prefix.c_str()); + } + return StringPrintf("%s/framework/%s.jar", GetAndroidRoot(), jar_prefix.c_str()); +} + +std::string CommonRuntimeTest::GetTestAndroidRoot() { + if (IsHost()) { + const char* host_dir = getenv("ANDROID_HOST_OUT"); + CHECK(host_dir != nullptr); + return host_dir; + } + return GetAndroidRoot(); +} + +std::vector<const DexFile*> CommonRuntimeTest::OpenTestDexFiles(const char* name) { + CHECK(name != nullptr); + std::string filename; + if (IsHost()) { + filename += getenv("ANDROID_HOST_OUT"); + filename += "/framework/"; + } else { + filename += "/data/nativetest/art/"; + } + filename += "art-gtest-"; + filename += name; + filename += ".jar"; + std::string error_msg; + std::vector<const DexFile*> dex_files; + bool success = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg, &dex_files); + CHECK(success) << "Failed to open '" << filename << "': " << error_msg; + for (const DexFile* dex_file : dex_files) { + CHECK_EQ(PROT_READ, dex_file->GetPermissions()); + CHECK(dex_file->IsReadOnly()); + } + opened_dex_files_.insert(opened_dex_files_.end(), dex_files.begin(), dex_files.end()); + return dex_files; +} + +const DexFile* CommonRuntimeTest::OpenTestDexFile(const char* name) { + std::vector<const DexFile*> vector = OpenTestDexFiles(name); + EXPECT_EQ(1U, vector.size()); + return vector[0]; +} + +jobject CommonRuntimeTest::LoadDex(const char* dex_name) { + std::vector<const DexFile*> dex_files = OpenTestDexFiles(dex_name); + CHECK_NE(0U, dex_files.size()); + for (const DexFile* dex_file : dex_files) { + class_linker_->RegisterDexFile(*dex_file); + } + ScopedObjectAccessUnchecked soa(Thread::Current()); + ScopedLocalRef<jobject> class_loader_local(soa.Env(), + soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader)); + jobject class_loader = soa.Env()->NewGlobalRef(class_loader_local.get()); + soa.Self()->SetClassLoaderOverride(soa.Decode<mirror::ClassLoader*>(class_loader_local.get())); + Runtime::Current()->SetCompileTimeClassPath(class_loader, dex_files); + return class_loader; +} + +CheckJniAbortCatcher::CheckJniAbortCatcher() : vm_(Runtime::Current()->GetJavaVM()) { + vm_->check_jni_abort_hook = Hook; + vm_->check_jni_abort_hook_data = &actual_; +} + +CheckJniAbortCatcher::~CheckJniAbortCatcher() { + vm_->check_jni_abort_hook = nullptr; + vm_->check_jni_abort_hook_data = nullptr; + EXPECT_TRUE(actual_.empty()) << actual_; +} + +void CheckJniAbortCatcher::Check(const char* expected_text) { + EXPECT_TRUE(actual_.find(expected_text) != std::string::npos) << "\n" + << "Expected to find: " << expected_text << "\n" + << "In the output : " << actual_; + actual_.clear(); +} + +void CheckJniAbortCatcher::Hook(void* data, const std::string& reason) { + // We use += because when we're hooking the aborts like this, multiple problems can be found. + *reinterpret_cast<std::string*>(data) += reason; +} + +} // namespace art + +namespace std { + +template <typename T> +std::ostream& operator<<(std::ostream& os, const std::vector<T>& rhs) { +os << ::art::ToString(rhs); +return os; +} + +} // namespace std diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 289dc1de4e..d0450317a3 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -17,73 +17,33 @@ #ifndef ART_RUNTIME_COMMON_RUNTIME_TEST_H_ #define ART_RUNTIME_COMMON_RUNTIME_TEST_H_ -#include <dirent.h> -#include <dlfcn.h> -#include <stdlib.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <fstream> -#include <memory> - -#include "../../external/icu/icu4c/source/common/unicode/uvernum.h" -#include "base/macros.h" -#include "base/stl_util.h" -#include "base/stringprintf.h" -#include "base/unix_file/fd_file.h" -#include "class_linker.h" -#include "dex_file-inl.h" -#include "entrypoints/entrypoint_utils.h" -#include "gc/heap.h" -#include "gtest/gtest.h" -#include "instruction_set.h" -#include "interpreter/interpreter.h" -#include "mirror/class_loader.h" -#include "noop_compiler_callbacks.h" -#include "oat_file.h" -#include "object_utils.h" +#include <gtest/gtest.h> +#include <jni.h> + +#include <string> + +#include "base/mutex.h" +#include "globals.h" #include "os.h" -#include "runtime.h" -#include "scoped_thread_state_change.h" -#include "ScopedLocalRef.h" -#include "thread.h" -#include "utils.h" -#include "verifier/method_verifier.h" -#include "verifier/method_verifier-inl.h" -#include "well_known_classes.h" namespace art { +class ClassLinker; +class CompilerCallbacks; +class DexFile; +class JavaVMExt; +class Runtime; +typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions; + class ScratchFile { public: - ScratchFile() { - // ANDROID_DATA needs to be set - CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA")) << - "Are you subclassing RuntimeTest?"; - filename_ = getenv("ANDROID_DATA"); - filename_ += "/TmpFile-XXXXXX"; - int fd = mkstemp(&filename_[0]); - CHECK_NE(-1, fd); - file_.reset(new File(fd, GetFilename())); - } + ScratchFile(); - ScratchFile(const ScratchFile& other, const char* suffix) { - filename_ = other.GetFilename(); - filename_ += suffix; - int fd = open(filename_.c_str(), O_RDWR | O_CREAT, 0666); - CHECK_NE(-1, fd); - file_.reset(new File(fd, GetFilename())); - } + ScratchFile(const ScratchFile& other, const char* suffix); - explicit ScratchFile(File* file) { - CHECK(file != NULL); - filename_ = file->GetPath(); - file_.reset(file); - } + explicit ScratchFile(File* file); - ~ScratchFile() { - Unlink(); - } + ~ScratchFile(); const std::string& GetFilename() const { return filename_; @@ -93,17 +53,9 @@ class ScratchFile { return file_.get(); } - int GetFd() const { - return file_->Fd(); - } + int GetFd() const; - void Unlink() { - if (!OS::FileExists(filename_.c_str())) { - return; - } - int unlink_result = unlink(filename_.c_str()); - CHECK_EQ(0, unlink_result); - } + void Unlink(); private: std::string filename_; @@ -112,221 +64,37 @@ class ScratchFile { class CommonRuntimeTest : public testing::Test { public: - static void SetEnvironmentVariables(std::string& android_data) { - if (IsHost()) { - // $ANDROID_ROOT is set on the device, but not necessarily on the host. - // But it needs to be set so that icu4c can find its locale data. - const char* android_root_from_env = getenv("ANDROID_ROOT"); - if (android_root_from_env == nullptr) { - // Use ANDROID_HOST_OUT for ANDROID_ROOT if it is set. - const char* android_host_out = getenv("ANDROID_HOST_OUT"); - if (android_host_out != nullptr) { - setenv("ANDROID_ROOT", android_host_out, 1); - } else { - // Build it from ANDROID_BUILD_TOP or cwd - std::string root; - const char* android_build_top = getenv("ANDROID_BUILD_TOP"); - if (android_build_top != nullptr) { - root += android_build_top; - } else { - // Not set by build server, so default to current directory - char* cwd = getcwd(nullptr, 0); - setenv("ANDROID_BUILD_TOP", cwd, 1); - root += cwd; - free(cwd); - } -#if defined(__linux__) - root += "/out/host/linux-x86"; -#elif defined(__APPLE__) - root += "/out/host/darwin-x86"; -#else -#error unsupported OS -#endif - setenv("ANDROID_ROOT", root.c_str(), 1); - } - } - setenv("LD_LIBRARY_PATH", ":", 0); // Required by java.lang.System.<clinit>. - - // Not set by build server, so default - if (getenv("ANDROID_HOST_OUT") == nullptr) { - setenv("ANDROID_HOST_OUT", getenv("ANDROID_ROOT"), 1); - } - } - - // On target, Cannot use /mnt/sdcard because it is mounted noexec, so use subdir of dalvik-cache - android_data = (IsHost() ? "/tmp/art-data-XXXXXX" : "/data/dalvik-cache/art-data-XXXXXX"); - if (mkdtemp(&android_data[0]) == nullptr) { - PLOG(FATAL) << "mkdtemp(\"" << &android_data[0] << "\") failed"; - } - setenv("ANDROID_DATA", android_data.c_str(), 1); - } + static void SetEnvironmentVariables(std::string& android_data); + + CommonRuntimeTest(); + ~CommonRuntimeTest(); protected: static bool IsHost() { return !kIsTargetBuild; } - const DexFile* LoadExpectSingleDexFile(const char* location) { - std::vector<const DexFile*> dex_files; - std::string error_msg; - if (!DexFile::Open(location, location, &error_msg, &dex_files)) { - LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n"; - return nullptr; - } else { - CHECK_EQ(1U, dex_files.size()) << "Expected only one dex file in " << location; - return dex_files[0]; - } - } + const DexFile* LoadExpectSingleDexFile(const char* location); - virtual void SetUp() { - SetEnvironmentVariables(android_data_); - dalvik_cache_.append(android_data_.c_str()); - dalvik_cache_.append("/dalvik-cache"); - int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700); - ASSERT_EQ(mkdir_result, 0); - - std::string error_msg; - java_lang_dex_file_ = LoadExpectSingleDexFile(GetLibCoreDexFileName().c_str()); - boot_class_path_.push_back(java_lang_dex_file_); - - std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB)); - std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB)); - - Runtime::Options options; - options.push_back(std::make_pair("bootclasspath", &boot_class_path_)); - options.push_back(std::make_pair("-Xcheck:jni", nullptr)); - options.push_back(std::make_pair(min_heap_string.c_str(), nullptr)); - options.push_back(std::make_pair(max_heap_string.c_str(), nullptr)); - options.push_back(std::make_pair("compilercallbacks", &callbacks_)); - SetUpRuntimeOptions(&options); - if (!Runtime::Create(options, false)) { - LOG(FATAL) << "Failed to create runtime"; - return; - } - runtime_.reset(Runtime::Current()); - class_linker_ = runtime_->GetClassLinker(); - class_linker_->FixupDexCaches(runtime_->GetResolutionMethod()); - - // Runtime::Create acquired the mutator_lock_ that is normally given away when we - // Runtime::Start, give it away now and then switch to a more managable ScopedObjectAccess. - Thread::Current()->TransitionFromRunnableToSuspended(kNative); - - // We're back in native, take the opportunity to initialize well known classes. - WellKnownClasses::Init(Thread::Current()->GetJniEnv()); - - // Create the heap thread pool so that the GC runs in parallel for tests. Normally, the thread - // pool is created by the runtime. - runtime_->GetHeap()->CreateThreadPool(); - runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption before the test - } + virtual void SetUp(); // Allow subclases such as CommonCompilerTest to add extra options. - virtual void SetUpRuntimeOptions(Runtime::Options *options) {} - - virtual void TearDown() { - const char* android_data = getenv("ANDROID_DATA"); - ASSERT_TRUE(android_data != nullptr); - DIR* dir = opendir(dalvik_cache_.c_str()); - ASSERT_TRUE(dir != nullptr); - dirent* e; - while ((e = readdir(dir)) != nullptr) { - if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) { - continue; - } - std::string filename(dalvik_cache_); - filename.push_back('/'); - filename.append(e->d_name); - int unlink_result = unlink(filename.c_str()); - ASSERT_EQ(0, unlink_result); - } - closedir(dir); - int rmdir_cache_result = rmdir(dalvik_cache_.c_str()); - ASSERT_EQ(0, rmdir_cache_result); - int rmdir_data_result = rmdir(android_data_.c_str()); - ASSERT_EQ(0, rmdir_data_result); - - // icu4c has a fixed 10-element array "gCommonICUDataArray". - // If we run > 10 tests, we fill that array and u_setCommonData fails. - // There's a function to clear the array, but it's not public... - typedef void (*IcuCleanupFn)(); - void* sym = dlsym(RTLD_DEFAULT, "u_cleanup_" U_ICU_VERSION_SHORT); - CHECK(sym != nullptr) << dlerror(); - IcuCleanupFn icu_cleanup_fn = reinterpret_cast<IcuCleanupFn>(sym); - (*icu_cleanup_fn)(); - - STLDeleteElements(&opened_dex_files_); - - Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test - } + virtual void SetUpRuntimeOptions(RuntimeOptions* options) {} - std::string GetLibCoreDexFileName() { - return GetDexFileName("core-libart"); - } + virtual void TearDown(); - std::string GetDexFileName(const std::string& jar_prefix) { - if (IsHost()) { - const char* host_dir = getenv("ANDROID_HOST_OUT"); - CHECK(host_dir != nullptr); - return StringPrintf("%s/framework/%s-hostdex.jar", host_dir, jar_prefix.c_str()); - } - return StringPrintf("%s/framework/%s.jar", GetAndroidRoot(), jar_prefix.c_str()); - } + std::string GetLibCoreDexFileName(); - std::string GetTestAndroidRoot() { - if (IsHost()) { - const char* host_dir = getenv("ANDROID_HOST_OUT"); - CHECK(host_dir != nullptr); - return host_dir; - } - return GetAndroidRoot(); - } + std::string GetDexFileName(const std::string& jar_prefix); + + std::string GetTestAndroidRoot(); std::vector<const DexFile*> OpenTestDexFiles(const char* name) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CHECK(name != nullptr); - std::string filename; - if (IsHost()) { - filename += getenv("ANDROID_HOST_OUT"); - filename += "/framework/"; - } else { - filename += "/data/nativetest/art/"; - } - filename += "art-gtest-"; - filename += name; - filename += ".jar"; - std::string error_msg; - std::vector<const DexFile*> dex_files; - bool success = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg, &dex_files); - CHECK(success) << "Failed to open '" << filename << "': " << error_msg; - for (const DexFile* dex_file : dex_files) { - CHECK_EQ(PROT_READ, dex_file->GetPermissions()); - CHECK(dex_file->IsReadOnly()); - } - opened_dex_files_.insert(opened_dex_files_.end(), dex_files.begin(), dex_files.end()); - return dex_files; - } + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const DexFile* OpenTestDexFile(const char* name) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - std::vector<const DexFile*> vector = OpenTestDexFiles(name); - EXPECT_EQ(1U, vector.size()); - return vector[0]; - } + const DexFile* OpenTestDexFile(const char* name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - jobject LoadDex(const char* dex_name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - std::vector<const DexFile*> dex_files = OpenTestDexFiles(dex_name); - CHECK_NE(0U, dex_files.size()); - for (const DexFile* dex_file : dex_files) { - class_linker_->RegisterDexFile(*dex_file); - } - ScopedObjectAccessUnchecked soa(Thread::Current()); - ScopedLocalRef<jobject> class_loader_local(soa.Env(), - soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader)); - jobject class_loader = soa.Env()->NewGlobalRef(class_loader_local.get()); - soa.Self()->SetClassLoaderOverride(soa.Decode<mirror::ClassLoader*>(class_loader_local.get())); - Runtime::Current()->SetCompileTimeClassPath(class_loader, dex_files); - return class_loader; - } + jobject LoadDex(const char* dex_name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); std::string android_data_; std::string dalvik_cache_; @@ -337,7 +105,7 @@ class CommonRuntimeTest : public testing::Test { ClassLinker* class_linker_; private: - NoopCompilerCallbacks callbacks_; + std::unique_ptr<CompilerCallbacks> callbacks_; std::vector<const DexFile*> opened_dex_files_; }; @@ -345,29 +113,14 @@ class CommonRuntimeTest : public testing::Test { // rather than aborting, so be careful! class CheckJniAbortCatcher { public: - CheckJniAbortCatcher() : vm_(Runtime::Current()->GetJavaVM()) { - vm_->check_jni_abort_hook = Hook; - vm_->check_jni_abort_hook_data = &actual_; - } + CheckJniAbortCatcher(); - ~CheckJniAbortCatcher() { - vm_->check_jni_abort_hook = nullptr; - vm_->check_jni_abort_hook_data = nullptr; - EXPECT_TRUE(actual_.empty()) << actual_; - } + ~CheckJniAbortCatcher(); - void Check(const char* expected_text) { - EXPECT_TRUE(actual_.find(expected_text) != std::string::npos) << "\n" - << "Expected to find: " << expected_text << "\n" - << "In the output : " << actual_; - actual_.clear(); - } + void Check(const char* expected_text); private: - static void Hook(void* data, const std::string& reason) { - // We use += because when we're hooking the aborts like this, multiple problems can be found. - *reinterpret_cast<std::string*>(data) += reason; - } + static void Hook(void* data, const std::string& reason); JavaVMExt* vm_; std::string actual_; @@ -398,10 +151,7 @@ namespace std { // TODO: isn't gtest supposed to be able to print STL types for itself? template <typename T> -std::ostream& operator<<(std::ostream& os, const std::vector<T>& rhs) { - os << ::art::ToString(rhs); - return os; -} +std::ostream& operator<<(std::ostream& os, const std::vector<T>& rhs); } // namespace std diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 8de3068dca..970593d119 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -16,6 +16,8 @@ #include "common_throws.h" +#include <sstream> + #include "base/logging.h" #include "class_linker-inl.h" #include "dex_file-inl.h" @@ -25,12 +27,9 @@ #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" -#include "object_utils.h" #include "thread.h" #include "verifier/method_verifier.h" -#include <sstream> - namespace art { static void AddReferrerLocation(std::ostream& os, mirror::Class* referrer) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index c95be0154a..4cf4c099b2 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -25,11 +25,13 @@ #include "class_linker-inl.h" #include "dex_file-inl.h" #include "dex_instruction.h" +#include "field_helper.h" #include "gc/accounting/card_table-inl.h" #include "gc/space/large_object_space.h" #include "gc/space/space-inl.h" #include "handle_scope.h" #include "jdwp/object_registry.h" +#include "method_helper.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class.h" @@ -39,7 +41,6 @@ #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" #include "mirror/throwable.h" -#include "object_utils.h" #include "quick/inline_method_analyser.h" #include "reflection.h" #include "safe_map.h" diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index c1e00fcb0a..284aa89d99 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -18,7 +18,12 @@ #include <memory> +#include "base/stl_util.h" +#include "base/unix_file/fd_file.h" #include "common_runtime_test.h" +#include "os.h" +#include "scoped_thread_state_change.h" +#include "thread-inl.h" namespace art { diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index 93faeaee7c..d475d426ff 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -16,11 +16,15 @@ #include "dex_file_verifier.h" -#include <memory> +#include "sys/mman.h" #include "zlib.h" +#include <memory> -#include "common_runtime_test.h" +#include "base/unix_file/fd_file.h" #include "base/macros.h" +#include "common_runtime_test.h" +#include "scoped_thread_state_change.h" +#include "thread-inl.h" namespace art { diff --git a/runtime/dex_method_iterator_test.cc b/runtime/dex_method_iterator_test.cc index 0d00cc3134..b8f180b46f 100644 --- a/runtime/dex_method_iterator_test.cc +++ b/runtime/dex_method_iterator_test.cc @@ -16,7 +16,10 @@ #include "dex_method_iterator.h" +#include "base/stl_util.h" #include "common_runtime_test.h" +#include "scoped_thread_state_change.h" +#include "thread-inl.h" namespace art { diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h new file mode 100644 index 0000000000..90c8fcf980 --- /dev/null +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -0,0 +1,663 @@ +/* + * 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_ENTRYPOINT_UTILS_INL_H_ +#define ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_INL_H_ + +#include "entrypoint_utils.h" + +#include "class_linker-inl.h" +#include "common_throws.h" +#include "dex_file.h" +#include "indirect_reference_table.h" +#include "invoke_type.h" +#include "jni_internal.h" +#include "mirror/art_method.h" +#include "mirror/array.h" +#include "mirror/class-inl.h" +#include "mirror/object-inl.h" +#include "mirror/throwable.h" +#include "handle_scope-inl.h" +#include "thread.h" + +namespace art { + +// TODO: Fix no thread safety analysis when GCC can handle template specialization. +template <const bool kAccessCheck> +static inline mirror::Class* CheckObjectAlloc(uint32_t type_idx, + mirror::ArtMethod* method, + Thread* self, bool* slow_path) { + mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx); + if (UNLIKELY(klass == NULL)) { + klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method); + *slow_path = true; + if (klass == NULL) { + DCHECK(self->IsExceptionPending()); + return nullptr; // Failure + } + } + if (kAccessCheck) { + if (UNLIKELY(!klass->IsInstantiable())) { + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewException(throw_location, "Ljava/lang/InstantiationError;", + PrettyDescriptor(klass).c_str()); + *slow_path = true; + return nullptr; // Failure + } + mirror::Class* referrer = method->GetDeclaringClass(); + if (UNLIKELY(!referrer->CanAccess(klass))) { + ThrowIllegalAccessErrorClass(referrer, klass); + *slow_path = true; + return nullptr; // Failure + } + } + if (UNLIKELY(!klass->IsInitialized())) { + StackHandleScope<1> hs(self); + Handle<mirror::Class> h_klass(hs.NewHandle(klass)); + // EnsureInitialized (the class initializer) might cause a GC. + // may cause us to suspend meaning that another thread may try to + // change the allocator while we are stuck in the entrypoints of + // an old allocator. Also, the class initialization may fail. To + // handle these cases we mark the slow path boolean as true so + // that the caller knows to check the allocator type to see if it + // has changed and to null-check the return value in case the + // initialization fails. + *slow_path = true; + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_klass, true, true)) { + DCHECK(self->IsExceptionPending()); + return nullptr; // Failure + } + return h_klass.Get(); + } + return klass; +} + +// TODO: Fix no thread safety analysis when annotalysis is smarter. +static inline mirror::Class* CheckClassInitializedForObjectAlloc(mirror::Class* klass, + Thread* self, + bool* slow_path) { + if (UNLIKELY(!klass->IsInitialized())) { + StackHandleScope<1> hs(self); + Handle<mirror::Class> h_class(hs.NewHandle(klass)); + // EnsureInitialized (the class initializer) might cause a GC. + // may cause us to suspend meaning that another thread may try to + // change the allocator while we are stuck in the entrypoints of + // an old allocator. Also, the class initialization may fail. To + // handle these cases we mark the slow path boolean as true so + // that the caller knows to check the allocator type to see if it + // has changed and to null-check the return value in case the + // initialization fails. + *slow_path = true; + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true)) { + DCHECK(self->IsExceptionPending()); + return nullptr; // Failure + } + return h_class.Get(); + } + return klass; +} + +// Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it +// cannot be resolved, throw an error. If it can, use it to create an instance. +// When verification/compiler hasn't been able to verify access, optionally perform an access +// check. +// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter. +template <bool kAccessCheck, bool kInstrumented> +static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, + mirror::ArtMethod* method, + Thread* self, + gc::AllocatorType allocator_type) { + bool slow_path = false; + mirror::Class* klass = CheckObjectAlloc<kAccessCheck>(type_idx, method, self, &slow_path); + if (UNLIKELY(slow_path)) { + if (klass == nullptr) { + return nullptr; + } + return klass->Alloc<kInstrumented>(self, Runtime::Current()->GetHeap()->GetCurrentAllocator()); + } + DCHECK(klass != nullptr); + return klass->Alloc<kInstrumented>(self, allocator_type); +} + +// Given the context of a calling Method and a resolved class, create an instance. +// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter. +template <bool kInstrumented> +static inline mirror::Object* AllocObjectFromCodeResolved(mirror::Class* klass, + mirror::ArtMethod* method, + Thread* self, + gc::AllocatorType allocator_type) { + DCHECK(klass != nullptr); + bool slow_path = false; + klass = CheckClassInitializedForObjectAlloc(klass, self, &slow_path); + if (UNLIKELY(slow_path)) { + if (klass == nullptr) { + return nullptr; + } + gc::Heap* heap = Runtime::Current()->GetHeap(); + // Pass in false since the object can not be finalizable. + return klass->Alloc<kInstrumented, false>(self, heap->GetCurrentAllocator()); + } + // Pass in false since the object can not be finalizable. + return klass->Alloc<kInstrumented, false>(self, allocator_type); +} + +// Given the context of a calling Method and an initialized class, create an instance. +// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter. +template <bool kInstrumented> +static inline mirror::Object* AllocObjectFromCodeInitialized(mirror::Class* klass, + mirror::ArtMethod* method, + Thread* self, + gc::AllocatorType allocator_type) { + DCHECK(klass != nullptr); + // Pass in false since the object can not be finalizable. + return klass->Alloc<kInstrumented, false>(self, allocator_type); +} + + +// TODO: Fix no thread safety analysis when GCC can handle template specialization. +template <bool kAccessCheck> +static inline mirror::Class* CheckArrayAlloc(uint32_t type_idx, + mirror::ArtMethod* method, + int32_t component_count, + bool* slow_path) { + if (UNLIKELY(component_count < 0)) { + ThrowNegativeArraySizeException(component_count); + *slow_path = true; + return nullptr; // Failure + } + mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx); + if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve + klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method); + *slow_path = true; + if (klass == nullptr) { // Error + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; // Failure + } + CHECK(klass->IsArrayClass()) << PrettyClass(klass); + } + if (kAccessCheck) { + mirror::Class* referrer = method->GetDeclaringClass(); + if (UNLIKELY(!referrer->CanAccess(klass))) { + ThrowIllegalAccessErrorClass(referrer, klass); + *slow_path = true; + return nullptr; // Failure + } + } + return klass; +} + +// Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If +// it cannot be resolved, throw an error. If it can, use it to create an array. +// When verification/compiler hasn't been able to verify access, optionally perform an access +// check. +// TODO: Fix no thread safety analysis when GCC can handle template specialization. +template <bool kAccessCheck, bool kInstrumented> +static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, + mirror::ArtMethod* method, + int32_t component_count, + Thread* self, + gc::AllocatorType allocator_type) { + bool slow_path = false; + mirror::Class* klass = CheckArrayAlloc<kAccessCheck>(type_idx, method, component_count, + &slow_path); + if (UNLIKELY(slow_path)) { + if (klass == nullptr) { + return nullptr; + } + gc::Heap* heap = Runtime::Current()->GetHeap(); + return mirror::Array::Alloc<kInstrumented>(self, klass, component_count, + klass->GetComponentSize(), + heap->GetCurrentAllocator()); + } + return mirror::Array::Alloc<kInstrumented>(self, klass, component_count, + klass->GetComponentSize(), allocator_type); +} + +template <bool kAccessCheck, bool kInstrumented> +static inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass, + mirror::ArtMethod* method, + int32_t component_count, + Thread* self, + gc::AllocatorType allocator_type) { + DCHECK(klass != nullptr); + if (UNLIKELY(component_count < 0)) { + ThrowNegativeArraySizeException(component_count); + return nullptr; // Failure + } + if (kAccessCheck) { + mirror::Class* referrer = method->GetDeclaringClass(); + if (UNLIKELY(!referrer->CanAccess(klass))) { + ThrowIllegalAccessErrorClass(referrer, klass); + return nullptr; // Failure + } + } + // No need to retry a slow-path allocation as the above code won't cause a GC or thread + // suspension. + return mirror::Array::Alloc<kInstrumented>(self, klass, component_count, + klass->GetComponentSize(), allocator_type); +} + +template<FindFieldType type, bool access_check> +static inline mirror::ArtField* FindFieldFromCode(uint32_t field_idx, mirror::ArtMethod* referrer, + Thread* self, size_t expected_size) { + bool is_primitive; + bool is_set; + bool is_static; + switch (type) { + case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break; + case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break; + case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break; + case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break; + case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break; + case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break; + case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break; + case StaticPrimitiveWrite: // Keep GCC happy by having a default handler, fall-through. + default: is_primitive = true; is_set = true; is_static = true; break; + } + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::ArtField* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static); + if (UNLIKELY(resolved_field == nullptr)) { + DCHECK(self->IsExceptionPending()); // Throw exception and unwind. + return nullptr; // Failure. + } + mirror::Class* fields_class = resolved_field->GetDeclaringClass(); + if (access_check) { + if (UNLIKELY(resolved_field->IsStatic() != is_static)) { + ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer); + return nullptr; + } + mirror::Class* referring_class = referrer->GetDeclaringClass(); + if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class, resolved_field, + field_idx))) { + DCHECK(self->IsExceptionPending()); // Throw exception and unwind. + return nullptr; // Failure. + } + if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) { + ThrowIllegalAccessErrorFinalField(referrer, resolved_field); + return nullptr; // Failure. + } else { + if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive || + resolved_field->FieldSize() != expected_size)) { + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + DCHECK(throw_location.GetMethod() == referrer); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;", + "Attempted read of %zd-bit %s on field '%s'", + expected_size * (32 / sizeof(int32_t)), + is_primitive ? "primitive" : "non-primitive", + PrettyField(resolved_field, true).c_str()); + return nullptr; // Failure. + } + } + } + if (!is_static) { + // instance fields must be being accessed on an initialized class + return resolved_field; + } else { + // If the class is initialized we're done. + if (LIKELY(fields_class->IsInitialized())) { + return resolved_field; + } else { + StackHandleScope<1> hs(self); + Handle<mirror::Class> h_class(hs.NewHandle(fields_class)); + if (LIKELY(class_linker->EnsureInitialized(h_class, true, true))) { + // Otherwise let's ensure the class is initialized before resolving the field. + return resolved_field; + } + DCHECK(self->IsExceptionPending()); // Throw exception and unwind + return nullptr; // Failure. + } + } +} + +// Explicit template declarations of FindFieldFromCode for all field access types. +#define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \ +template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ +mirror::ArtField* FindFieldFromCode<_type, _access_check>(uint32_t field_idx, \ + mirror::ArtMethod* referrer, \ + Thread* self, size_t expected_size) \ + +#define EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \ + EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, false); \ + EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, true) + +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectRead); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectWrite); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveRead); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveWrite); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectRead); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectWrite); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveRead); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveWrite); + +#undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL +#undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL + +template<InvokeType type, bool access_check> +static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, + mirror::Object** this_object, + mirror::ArtMethod** referrer, Thread* self) { + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + mirror::ArtMethod* resolved_method = class_linker->GetResolvedMethod(method_idx, *referrer, type); + if (resolved_method == nullptr) { + StackHandleScope<1> hs(self); + mirror::Object* null_this = nullptr; + HandleWrapper<mirror::Object> h_this( + hs.NewHandleWrapper(type == kStatic ? &null_this : this_object)); + resolved_method = class_linker->ResolveMethod(self, method_idx, referrer, type); + } + if (UNLIKELY(resolved_method == nullptr)) { + DCHECK(self->IsExceptionPending()); // Throw exception and unwind. + return nullptr; // Failure. + } else if (UNLIKELY(*this_object == nullptr && type != kStatic)) { + // Maintain interpreter-like semantics where NullPointerException is thrown + // after potential NoSuchMethodError from class linker. + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + DCHECK_EQ(*referrer, throw_location.GetMethod()); + ThrowNullPointerExceptionForMethodAccess(throw_location, method_idx, type); + return nullptr; // Failure. + } else if (access_check) { + // Incompatible class change should have been handled in resolve method. + if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) { + ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method, + *referrer); + return nullptr; // Failure. + } + mirror::Class* methods_class = resolved_method->GetDeclaringClass(); + mirror::Class* referring_class = (*referrer)->GetDeclaringClass(); + bool can_access_resolved_method = + referring_class->CheckResolvedMethodAccess<type>(methods_class, resolved_method, + method_idx); + if (UNLIKELY(!can_access_resolved_method)) { + DCHECK(self->IsExceptionPending()); // Throw exception and unwind. + return nullptr; // Failure. + } + } + switch (type) { + case kStatic: + case kDirect: + return resolved_method; + case kVirtual: { + mirror::ObjectArray<mirror::ArtMethod>* vtable = (*this_object)->GetClass()->GetVTable(); + uint16_t vtable_index = resolved_method->GetMethodIndex(); + if (access_check && + (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength()))) { + // Behavior to agree with that of the verifier. + ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), + resolved_method->GetName(), resolved_method->GetSignature()); + return nullptr; // Failure. + } + DCHECK(vtable != nullptr); + return vtable->GetWithoutChecks(vtable_index); + } + case kSuper: { + mirror::Class* super_class = (*referrer)->GetDeclaringClass()->GetSuperClass(); + uint16_t vtable_index = resolved_method->GetMethodIndex(); + mirror::ObjectArray<mirror::ArtMethod>* vtable; + if (access_check) { + // Check existence of super class. + vtable = (super_class != nullptr) ? super_class->GetVTable() : nullptr; + if (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength())) { + // Behavior to agree with that of the verifier. + ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), + resolved_method->GetName(), resolved_method->GetSignature()); + return nullptr; // Failure. + } + } else { + // Super class must exist. + DCHECK(super_class != nullptr); + vtable = super_class->GetVTable(); + } + DCHECK(vtable != nullptr); + return vtable->GetWithoutChecks(vtable_index); + } + case kInterface: { + uint32_t imt_index = resolved_method->GetDexMethodIndex() % mirror::Class::kImtSize; + mirror::ArtMethod* imt_method = (*this_object)->GetClass()->GetEmbeddedImTableEntry(imt_index); + if (!imt_method->IsImtConflictMethod()) { + return imt_method; + } else { + mirror::ArtMethod* interface_method = + (*this_object)->GetClass()->FindVirtualMethodForInterface(resolved_method); + if (UNLIKELY(interface_method == nullptr)) { + ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, + *this_object, *referrer); + return nullptr; // Failure. + } + return interface_method; + } + } + default: + LOG(FATAL) << "Unknown invoke type " << type; + return nullptr; // Failure. + } +} + +// Explicit template declarations of FindMethodFromCode for all invoke types. +#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + mirror::ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx, \ + mirror::Object** this_object, \ + mirror::ArtMethod** referrer, \ + Thread* self) +#define EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \ + EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, false); \ + EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, true) + +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kStatic); +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kDirect); +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kVirtual); +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kSuper); +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kInterface); + +#undef EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL +#undef EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL + +// Fast path field resolution that can't initialize classes or throw exceptions. +static inline mirror::ArtField* FindFieldFast(uint32_t field_idx, + mirror::ArtMethod* referrer, + FindFieldType type, size_t expected_size) { + mirror::ArtField* resolved_field = + referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx); + if (UNLIKELY(resolved_field == nullptr)) { + return nullptr; + } + // Check for incompatible class change. + bool is_primitive; + bool is_set; + bool is_static; + switch (type) { + case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break; + case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break; + case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break; + case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break; + case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break; + case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break; + case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break; + case StaticPrimitiveWrite: is_primitive = true; is_set = true; is_static = true; break; + default: + LOG(FATAL) << "UNREACHABLE"; // Assignment below to avoid GCC warnings. + is_primitive = true; + is_set = true; + is_static = true; + break; + } + if (UNLIKELY(resolved_field->IsStatic() != is_static)) { + // Incompatible class change. + return nullptr; + } + mirror::Class* fields_class = resolved_field->GetDeclaringClass(); + if (is_static) { + // Check class is initialized else fail so that we can contend to initialize the class with + // other threads that may be racing to do this. + if (UNLIKELY(!fields_class->IsInitialized())) { + return nullptr; + } + } + mirror::Class* referring_class = referrer->GetDeclaringClass(); + if (UNLIKELY(!referring_class->CanAccess(fields_class) || + !referring_class->CanAccessMember(fields_class, + resolved_field->GetAccessFlags()) || + (is_set && resolved_field->IsFinal() && (fields_class != referring_class)))) { + // Illegal access. + return nullptr; + } + if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive || + resolved_field->FieldSize() != expected_size)) { + return nullptr; + } + return resolved_field; +} + +// Fast path method resolution that can't throw exceptions. +static inline mirror::ArtMethod* FindMethodFast(uint32_t method_idx, + mirror::Object* this_object, + mirror::ArtMethod* referrer, + bool access_check, InvokeType type) { + bool is_direct = type == kStatic || type == kDirect; + if (UNLIKELY(this_object == NULL && !is_direct)) { + return NULL; + } + mirror::ArtMethod* resolved_method = + referrer->GetDeclaringClass()->GetDexCache()->GetResolvedMethod(method_idx); + if (UNLIKELY(resolved_method == NULL)) { + return NULL; + } + if (access_check) { + // Check for incompatible class change errors and access. + bool icce = resolved_method->CheckIncompatibleClassChange(type); + if (UNLIKELY(icce)) { + return NULL; + } + mirror::Class* methods_class = resolved_method->GetDeclaringClass(); + mirror::Class* referring_class = referrer->GetDeclaringClass(); + if (UNLIKELY(!referring_class->CanAccess(methods_class) || + !referring_class->CanAccessMember(methods_class, + resolved_method->GetAccessFlags()))) { + // Potential illegal access, may need to refine the method's class. + return NULL; + } + } + if (type == kInterface) { // Most common form of slow path dispatch. + return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method); + } else if (is_direct) { + return resolved_method; + } else if (type == kSuper) { + return referrer->GetDeclaringClass()->GetSuperClass()->GetVTable()-> + Get(resolved_method->GetMethodIndex()); + } else { + DCHECK(type == kVirtual); + return this_object->GetClass()->GetVTable()->Get(resolved_method->GetMethodIndex()); + } +} + +static inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx, + mirror::ArtMethod* referrer, + Thread* self, bool can_run_clinit, + bool verify_access) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::Class* klass = class_linker->ResolveType(type_idx, referrer); + if (UNLIKELY(klass == nullptr)) { + CHECK(self->IsExceptionPending()); + return nullptr; // Failure - Indicate to caller to deliver exception + } + // Perform access check if necessary. + mirror::Class* referring_class = referrer->GetDeclaringClass(); + if (verify_access && UNLIKELY(!referring_class->CanAccess(klass))) { + ThrowIllegalAccessErrorClass(referring_class, klass); + return nullptr; // Failure - Indicate to caller to deliver exception + } + // If we're just implementing const-class, we shouldn't call <clinit>. + if (!can_run_clinit) { + return klass; + } + // If we are the <clinit> of this class, just return our storage. + // + // Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished + // running. + if (klass == referring_class && referrer->IsConstructor() && referrer->IsStatic()) { + return klass; + } + StackHandleScope<1> hs(self); + Handle<mirror::Class> h_class(hs.NewHandle(klass)); + if (!class_linker->EnsureInitialized(h_class, true, true)) { + CHECK(self->IsExceptionPending()); + return nullptr; // Failure - Indicate to caller to deliver exception + } + return h_class.Get(); +} + +static inline mirror::String* ResolveStringFromCode(mirror::ArtMethod* referrer, + uint32_t string_idx) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + return class_linker->ResolveString(string_idx, referrer); +} + +static inline void UnlockJniSynchronizedMethod(jobject locked, Thread* self) { + // Save any pending exception over monitor exit call. + mirror::Throwable* saved_exception = NULL; + ThrowLocation saved_throw_location; + bool is_exception_reported = self->IsExceptionReportedToInstrumentation(); + if (UNLIKELY(self->IsExceptionPending())) { + saved_exception = self->GetException(&saved_throw_location); + self->ClearException(); + } + // Decode locked object and unlock, before popping local references. + self->DecodeJObject(locked)->MonitorExit(self); + if (UNLIKELY(self->IsExceptionPending())) { + LOG(FATAL) << "Synchronized JNI code returning with an exception:\n" + << saved_exception->Dump() + << "\nEncountered second exception during implicit MonitorExit:\n" + << self->GetException(NULL)->Dump(); + } + // Restore pending exception. + if (saved_exception != NULL) { + self->SetException(saved_throw_location, saved_exception); + self->SetExceptionReportedToInstrumentation(is_exception_reported); + } +} + +static inline void CheckSuspend(Thread* thread) { + for (;;) { + if (thread->ReadFlag(kCheckpointRequest)) { + thread->RunCheckpointFunction(); + } else if (thread->ReadFlag(kSuspendRequest)) { + thread->FullSuspendCheck(); + } else { + break; + } + } +} + +template <typename INT_TYPE, typename FLOAT_TYPE> +static inline INT_TYPE art_float_to_integral(FLOAT_TYPE f) { + const INT_TYPE kMaxInt = static_cast<INT_TYPE>(std::numeric_limits<INT_TYPE>::max()); + const INT_TYPE kMinInt = static_cast<INT_TYPE>(std::numeric_limits<INT_TYPE>::min()); + const FLOAT_TYPE kMaxIntAsFloat = static_cast<FLOAT_TYPE>(kMaxInt); + const FLOAT_TYPE kMinIntAsFloat = static_cast<FLOAT_TYPE>(kMinInt); + if (LIKELY(f > kMinIntAsFloat)) { + if (LIKELY(f < kMaxIntAsFloat)) { + return static_cast<INT_TYPE>(f); + } else { + return kMaxInt; + } + } else { + return (f != f) ? 0 : kMinInt; // f != f implies NaN + } +} + +} // namespace art + +#endif // ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_INL_H_ diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index a0e32f520d..0fa0e410bf 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -16,16 +16,16 @@ #include "entrypoints/entrypoint_utils.h" +#include "base/mutex.h" #include "class_linker-inl.h" #include "dex_file-inl.h" #include "gc/accounting/card_table-inl.h" +#include "method_helper-inl.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "mirror/object_array-inl.h" -#include "mirror/proxy.h" #include "reflection.h" #include "scoped_thread_state_change.h" #include "ScopedLocalRef.h" @@ -139,6 +139,25 @@ void ThrowStackOverflowError(Thread* self) { self->ResetDefaultStackEnd(!explicit_overflow_check); // Return to default stack size. } +void CheckReferenceResult(mirror::Object* o, Thread* self) { + if (o == NULL) { + return; + } + mirror::ArtMethod* m = self->GetCurrentMethod(NULL); + if (o == kInvalidIndirectRefObject) { + JniAbortF(NULL, "invalid reference returned from %s", PrettyMethod(m).c_str()); + } + // Make sure that the result is an instance of the type this method was expected to return. + StackHandleScope<1> hs(self); + Handle<mirror::ArtMethod> h_m(hs.NewHandle(m)); + mirror::Class* return_type = MethodHelper(h_m).GetReturnType(); + + if (!o->InstanceOf(return_type)) { + JniAbortF(NULL, "attempt to return an instance of %s from %s", PrettyTypeOf(o).c_str(), + PrettyMethod(h_m.Get()).c_str()); + } +} + JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, const char* shorty, jobject rcvr_jobj, jobject interface_method_jobj, std::vector<jvalue>& args) { @@ -219,8 +238,7 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons mirror::Throwable* exception = soa.Self()->GetException(NULL); if (exception->IsCheckedException()) { mirror::Object* rcvr = soa.Decode<mirror::Object*>(rcvr_jobj); - mirror::SynthesizedProxyClass* proxy_class = - down_cast<mirror::SynthesizedProxyClass*>(rcvr->GetClass()); + mirror::Class* proxy_class = rcvr->GetClass(); mirror::ArtMethod* interface_method = soa.Decode<mirror::ArtMethod*>(interface_method_jobj); mirror::ArtMethod* proxy_method = diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index ff836a4745..c5d67aa1df 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -17,105 +17,40 @@ #ifndef ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_ #define ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_ +#include <jni.h> +#include <stdint.h> + #include "base/macros.h" -#include "class_linker-inl.h" -#include "common_throws.h" -#include "dex_file.h" -#include "indirect_reference_table.h" +#include "base/mutex.h" +#include "gc/allocator_type.h" #include "invoke_type.h" -#include "jni_internal.h" -#include "mirror/art_method.h" -#include "mirror/array.h" -#include "mirror/class-inl.h" -#include "mirror/object-inl.h" -#include "mirror/throwable.h" -#include "object_utils.h" -#include "handle_scope-inl.h" -#include "thread.h" +#include "jvalue.h" namespace art { namespace mirror { class Class; + class Array; class ArtField; + class ArtMethod; class Object; + class String; } // namespace mirror +class ScopedObjectAccessAlreadyRunnable; +class Thread; + // TODO: Fix no thread safety analysis when GCC can handle template specialization. template <const bool kAccessCheck> ALWAYS_INLINE static inline mirror::Class* CheckObjectAlloc(uint32_t type_idx, mirror::ArtMethod* method, Thread* self, bool* slow_path) - NO_THREAD_SAFETY_ANALYSIS { - mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx); - if (UNLIKELY(klass == NULL)) { - klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method); - *slow_path = true; - if (klass == NULL) { - DCHECK(self->IsExceptionPending()); - return nullptr; // Failure - } - } - if (kAccessCheck) { - if (UNLIKELY(!klass->IsInstantiable())) { - ThrowLocation throw_location = self->GetCurrentLocationForThrow(); - self->ThrowNewException(throw_location, "Ljava/lang/InstantiationError;", - PrettyDescriptor(klass).c_str()); - *slow_path = true; - return nullptr; // Failure - } - mirror::Class* referrer = method->GetDeclaringClass(); - if (UNLIKELY(!referrer->CanAccess(klass))) { - ThrowIllegalAccessErrorClass(referrer, klass); - *slow_path = true; - return nullptr; // Failure - } - } - if (UNLIKELY(!klass->IsInitialized())) { - StackHandleScope<1> hs(self); - Handle<mirror::Class> h_klass(hs.NewHandle(klass)); - // EnsureInitialized (the class initializer) might cause a GC. - // may cause us to suspend meaning that another thread may try to - // change the allocator while we are stuck in the entrypoints of - // an old allocator. Also, the class initialization may fail. To - // handle these cases we mark the slow path boolean as true so - // that the caller knows to check the allocator type to see if it - // has changed and to null-check the return value in case the - // initialization fails. - *slow_path = true; - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_klass, true, true)) { - DCHECK(self->IsExceptionPending()); - return nullptr; // Failure - } - return h_klass.Get(); - } - return klass; -} + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // TODO: Fix no thread safety analysis when annotalysis is smarter. ALWAYS_INLINE static inline mirror::Class* CheckClassInitializedForObjectAlloc(mirror::Class* klass, Thread* self, bool* slow_path) - NO_THREAD_SAFETY_ANALYSIS { - if (UNLIKELY(!klass->IsInitialized())) { - StackHandleScope<1> hs(self); - Handle<mirror::Class> h_class(hs.NewHandle(klass)); - // EnsureInitialized (the class initializer) might cause a GC. - // may cause us to suspend meaning that another thread may try to - // change the allocator while we are stuck in the entrypoints of - // an old allocator. Also, the class initialization may fail. To - // handle these cases we mark the slow path boolean as true so - // that the caller knows to check the allocator type to see if it - // has changed and to null-check the return value in case the - // initialization fails. - *slow_path = true; - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true)) { - DCHECK(self->IsExceptionPending()); - return nullptr; // Failure - } - return h_class.Get(); - } - return klass; -} + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it // cannot be resolved, throw an error. If it can, use it to create an instance. @@ -127,18 +62,7 @@ ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCode(uint32_t type_id mirror::ArtMethod* method, Thread* self, gc::AllocatorType allocator_type) - NO_THREAD_SAFETY_ANALYSIS { - bool slow_path = false; - mirror::Class* klass = CheckObjectAlloc<kAccessCheck>(type_idx, method, self, &slow_path); - if (UNLIKELY(slow_path)) { - if (klass == nullptr) { - return nullptr; - } - return klass->Alloc<kInstrumented>(self, Runtime::Current()->GetHeap()->GetCurrentAllocator()); - } - DCHECK(klass != nullptr); - return klass->Alloc<kInstrumented>(self, allocator_type); -} + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Given the context of a calling Method and a resolved class, create an instance. // TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter. @@ -147,21 +71,7 @@ ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCodeResolved(mirror:: mirror::ArtMethod* method, Thread* self, gc::AllocatorType allocator_type) - NO_THREAD_SAFETY_ANALYSIS { - DCHECK(klass != nullptr); - bool slow_path = false; - klass = CheckClassInitializedForObjectAlloc(klass, self, &slow_path); - if (UNLIKELY(slow_path)) { - if (klass == nullptr) { - return nullptr; - } - gc::Heap* heap = Runtime::Current()->GetHeap(); - // Pass in false since the object can not be finalizable. - return klass->Alloc<kInstrumented, false>(self, heap->GetCurrentAllocator()); - } - // Pass in false since the object can not be finalizable. - return klass->Alloc<kInstrumented, false>(self, allocator_type); -} + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Given the context of a calling Method and an initialized class, create an instance. // TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter. @@ -170,11 +80,7 @@ ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCodeInitialized(mirro mirror::ArtMethod* method, Thread* self, gc::AllocatorType allocator_type) - NO_THREAD_SAFETY_ANALYSIS { - DCHECK(klass != nullptr); - // Pass in false since the object can not be finalizable. - return klass->Alloc<kInstrumented, false>(self, allocator_type); -} + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // TODO: Fix no thread safety analysis when GCC can handle template specialization. @@ -183,32 +89,7 @@ ALWAYS_INLINE static inline mirror::Class* CheckArrayAlloc(uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, bool* slow_path) - NO_THREAD_SAFETY_ANALYSIS { - if (UNLIKELY(component_count < 0)) { - ThrowNegativeArraySizeException(component_count); - *slow_path = true; - return nullptr; // Failure - } - mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx); - if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve - klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method); - *slow_path = true; - if (klass == nullptr) { // Error - DCHECK(Thread::Current()->IsExceptionPending()); - return nullptr; // Failure - } - CHECK(klass->IsArrayClass()) << PrettyClass(klass); - } - if (kAccessCheck) { - mirror::Class* referrer = method->GetDeclaringClass(); - if (UNLIKELY(!referrer->CanAccess(klass))) { - ThrowIllegalAccessErrorClass(referrer, klass); - *slow_path = true; - return nullptr; // Failure - } - } - return klass; -} + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If // it cannot be resolved, throw an error. If it can, use it to create an array. @@ -221,22 +102,7 @@ ALWAYS_INLINE static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, int32_t component_count, Thread* self, gc::AllocatorType allocator_type) - NO_THREAD_SAFETY_ANALYSIS { - bool slow_path = false; - mirror::Class* klass = CheckArrayAlloc<kAccessCheck>(type_idx, method, component_count, - &slow_path); - if (UNLIKELY(slow_path)) { - if (klass == nullptr) { - return nullptr; - } - gc::Heap* heap = Runtime::Current()->GetHeap(); - return mirror::Array::Alloc<kInstrumented>(self, klass, component_count, - klass->GetComponentSize(), - heap->GetCurrentAllocator()); - } - return mirror::Array::Alloc<kInstrumented>(self, klass, component_count, - klass->GetComponentSize(), allocator_type); -} + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template <bool kAccessCheck, bool kInstrumented> ALWAYS_INLINE static inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass, @@ -244,24 +110,7 @@ ALWAYS_INLINE static inline mirror::Array* AllocArrayFromCodeResolved(mirror::Cl int32_t component_count, Thread* self, gc::AllocatorType allocator_type) - NO_THREAD_SAFETY_ANALYSIS { - DCHECK(klass != nullptr); - if (UNLIKELY(component_count < 0)) { - ThrowNegativeArraySizeException(component_count); - return nullptr; // Failure - } - if (kAccessCheck) { - mirror::Class* referrer = method->GetDeclaringClass(); - if (UNLIKELY(!referrer->CanAccess(klass))) { - ThrowIllegalAccessErrorClass(referrer, klass); - return nullptr; // Failure - } - } - // No need to retry a slow-path allocation as the above code won't cause a GC or thread - // suspension. - return mirror::Array::Alloc<kInstrumented>(self, klass, component_count, - klass->GetComponentSize(), allocator_type); -} + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, @@ -290,422 +139,48 @@ enum FindFieldType { template<FindFieldType type, bool access_check> static inline mirror::ArtField* FindFieldFromCode(uint32_t field_idx, mirror::ArtMethod* referrer, - Thread* self, size_t expected_size) { - bool is_primitive; - bool is_set; - bool is_static; - switch (type) { - case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break; - case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break; - case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break; - case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break; - case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break; - case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break; - case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break; - case StaticPrimitiveWrite: // Keep GCC happy by having a default handler, fall-through. - default: is_primitive = true; is_set = true; is_static = true; break; - } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::ArtField* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static); - if (UNLIKELY(resolved_field == nullptr)) { - DCHECK(self->IsExceptionPending()); // Throw exception and unwind. - return nullptr; // Failure. - } - mirror::Class* fields_class = resolved_field->GetDeclaringClass(); - if (access_check) { - if (UNLIKELY(resolved_field->IsStatic() != is_static)) { - ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer); - return nullptr; - } - mirror::Class* referring_class = referrer->GetDeclaringClass(); - if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class, resolved_field, - field_idx))) { - DCHECK(self->IsExceptionPending()); // Throw exception and unwind. - return nullptr; // Failure. - } - if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) { - ThrowIllegalAccessErrorFinalField(referrer, resolved_field); - return nullptr; // Failure. - } else { - if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive || - resolved_field->FieldSize() != expected_size)) { - ThrowLocation throw_location = self->GetCurrentLocationForThrow(); - DCHECK(throw_location.GetMethod() == referrer); - self->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;", - "Attempted read of %zd-bit %s on field '%s'", - expected_size * (32 / sizeof(int32_t)), - is_primitive ? "primitive" : "non-primitive", - PrettyField(resolved_field, true).c_str()); - return nullptr; // Failure. - } - } - } - if (!is_static) { - // instance fields must be being accessed on an initialized class - return resolved_field; - } else { - // If the class is initialized we're done. - if (LIKELY(fields_class->IsInitialized())) { - return resolved_field; - } else { - StackHandleScope<1> hs(self); - Handle<mirror::Class> h_class(hs.NewHandle(fields_class)); - if (LIKELY(class_linker->EnsureInitialized(h_class, true, true))) { - // Otherwise let's ensure the class is initialized before resolving the field. - return resolved_field; - } - DCHECK(self->IsExceptionPending()); // Throw exception and unwind - return nullptr; // Failure. - } - } -} - -// Explicit template declarations of FindFieldFromCode for all field access types. -#define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \ -template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ -mirror::ArtField* FindFieldFromCode<_type, _access_check>(uint32_t field_idx, \ - mirror::ArtMethod* referrer, \ - Thread* self, size_t expected_size) \ - -#define EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \ - EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, false); \ - EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, true) - -EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectRead); -EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectWrite); -EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveRead); -EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveWrite); -EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectRead); -EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectWrite); -EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveRead); -EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveWrite); - -#undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL -#undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL + Thread* self, size_t expected_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<InvokeType type, bool access_check> static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object** this_object, - mirror::ArtMethod** referrer, Thread* self) { - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - mirror::ArtMethod* resolved_method = class_linker->GetResolvedMethod(method_idx, *referrer, type); - if (resolved_method == nullptr) { - StackHandleScope<1> hs(self); - mirror::Object* null_this = nullptr; - HandleWrapper<mirror::Object> h_this( - hs.NewHandleWrapper(type == kStatic ? &null_this : this_object)); - resolved_method = class_linker->ResolveMethod(self, method_idx, referrer, type); - } - if (UNLIKELY(resolved_method == nullptr)) { - DCHECK(self->IsExceptionPending()); // Throw exception and unwind. - return nullptr; // Failure. - } else if (UNLIKELY(*this_object == nullptr && type != kStatic)) { - // Maintain interpreter-like semantics where NullPointerException is thrown - // after potential NoSuchMethodError from class linker. - ThrowLocation throw_location = self->GetCurrentLocationForThrow(); - DCHECK_EQ(*referrer, throw_location.GetMethod()); - ThrowNullPointerExceptionForMethodAccess(throw_location, method_idx, type); - return nullptr; // Failure. - } else if (access_check) { - // Incompatible class change should have been handled in resolve method. - if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) { - ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method, - *referrer); - return nullptr; // Failure. - } - mirror::Class* methods_class = resolved_method->GetDeclaringClass(); - mirror::Class* referring_class = (*referrer)->GetDeclaringClass(); - bool can_access_resolved_method = - referring_class->CheckResolvedMethodAccess<type>(methods_class, resolved_method, - method_idx); - if (UNLIKELY(!can_access_resolved_method)) { - DCHECK(self->IsExceptionPending()); // Throw exception and unwind. - return nullptr; // Failure. - } - } - switch (type) { - case kStatic: - case kDirect: - return resolved_method; - case kVirtual: { - mirror::ObjectArray<mirror::ArtMethod>* vtable = (*this_object)->GetClass()->GetVTable(); - uint16_t vtable_index = resolved_method->GetMethodIndex(); - if (access_check && - (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength()))) { - // Behavior to agree with that of the verifier. - ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), - resolved_method->GetName(), resolved_method->GetSignature()); - return nullptr; // Failure. - } - DCHECK(vtable != nullptr); - return vtable->GetWithoutChecks(vtable_index); - } - case kSuper: { - mirror::Class* super_class = (*referrer)->GetDeclaringClass()->GetSuperClass(); - uint16_t vtable_index = resolved_method->GetMethodIndex(); - mirror::ObjectArray<mirror::ArtMethod>* vtable; - if (access_check) { - // Check existence of super class. - vtable = (super_class != nullptr) ? super_class->GetVTable() : nullptr; - if (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength())) { - // Behavior to agree with that of the verifier. - ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), - resolved_method->GetName(), resolved_method->GetSignature()); - return nullptr; // Failure. - } - } else { - // Super class must exist. - DCHECK(super_class != nullptr); - vtable = super_class->GetVTable(); - } - DCHECK(vtable != nullptr); - return vtable->GetWithoutChecks(vtable_index); - } - case kInterface: { - uint32_t imt_index = resolved_method->GetDexMethodIndex() % ClassLinker::kImtSize; - mirror::ObjectArray<mirror::ArtMethod>* imt_table = (*this_object)->GetClass()->GetImTable(); - mirror::ArtMethod* imt_method = imt_table->Get(imt_index); - if (!imt_method->IsImtConflictMethod()) { - return imt_method; - } else { - mirror::ArtMethod* interface_method = - (*this_object)->GetClass()->FindVirtualMethodForInterface(resolved_method); - if (UNLIKELY(interface_method == nullptr)) { - ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, - *this_object, *referrer); - return nullptr; // Failure. - } - return interface_method; - } - } - default: - LOG(FATAL) << "Unknown invoke type " << type; - return nullptr; // Failure. - } -} - -// Explicit template declarations of FindMethodFromCode for all invoke types. -#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \ - template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ - mirror::ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx, \ - mirror::Object** this_object, \ - mirror::ArtMethod** referrer, \ - Thread* self) -#define EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \ - EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, false); \ - EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, true) - -EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kStatic); -EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kDirect); -EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kVirtual); -EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kSuper); -EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kInterface); - -#undef EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL -#undef EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL + mirror::ArtMethod** referrer, Thread* self) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Fast path field resolution that can't initialize classes or throw exceptions. static inline mirror::ArtField* FindFieldFast(uint32_t field_idx, mirror::ArtMethod* referrer, FindFieldType type, size_t expected_size) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::ArtField* resolved_field = - referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx); - if (UNLIKELY(resolved_field == nullptr)) { - return nullptr; - } - // Check for incompatible class change. - bool is_primitive; - bool is_set; - bool is_static; - switch (type) { - case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break; - case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break; - case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break; - case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break; - case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break; - case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break; - case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break; - case StaticPrimitiveWrite: is_primitive = true; is_set = true; is_static = true; break; - default: - LOG(FATAL) << "UNREACHABLE"; // Assignment below to avoid GCC warnings. - is_primitive = true; - is_set = true; - is_static = true; - break; - } - if (UNLIKELY(resolved_field->IsStatic() != is_static)) { - // Incompatible class change. - return nullptr; - } - mirror::Class* fields_class = resolved_field->GetDeclaringClass(); - if (is_static) { - // Check class is initialized else fail so that we can contend to initialize the class with - // other threads that may be racing to do this. - if (UNLIKELY(!fields_class->IsInitialized())) { - return nullptr; - } - } - mirror::Class* referring_class = referrer->GetDeclaringClass(); - if (UNLIKELY(!referring_class->CanAccess(fields_class) || - !referring_class->CanAccessMember(fields_class, - resolved_field->GetAccessFlags()) || - (is_set && resolved_field->IsFinal() && (fields_class != referring_class)))) { - // Illegal access. - return nullptr; - } - if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive || - resolved_field->FieldSize() != expected_size)) { - return nullptr; - } - return resolved_field; -} + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Fast path method resolution that can't throw exceptions. static inline mirror::ArtMethod* FindMethodFast(uint32_t method_idx, mirror::Object* this_object, mirror::ArtMethod* referrer, bool access_check, InvokeType type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - bool is_direct = type == kStatic || type == kDirect; - if (UNLIKELY(this_object == NULL && !is_direct)) { - return NULL; - } - mirror::ArtMethod* resolved_method = - referrer->GetDeclaringClass()->GetDexCache()->GetResolvedMethod(method_idx); - if (UNLIKELY(resolved_method == NULL)) { - return NULL; - } - if (access_check) { - // Check for incompatible class change errors and access. - bool icce = resolved_method->CheckIncompatibleClassChange(type); - if (UNLIKELY(icce)) { - return NULL; - } - mirror::Class* methods_class = resolved_method->GetDeclaringClass(); - mirror::Class* referring_class = referrer->GetDeclaringClass(); - if (UNLIKELY(!referring_class->CanAccess(methods_class) || - !referring_class->CanAccessMember(methods_class, - resolved_method->GetAccessFlags()))) { - // Potential illegal access, may need to refine the method's class. - return NULL; - } - } - if (type == kInterface) { // Most common form of slow path dispatch. - return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method); - } else if (is_direct) { - return resolved_method; - } else if (type == kSuper) { - return referrer->GetDeclaringClass()->GetSuperClass()->GetVTable()-> - Get(resolved_method->GetMethodIndex()); - } else { - DCHECK(type == kVirtual); - return this_object->GetClass()->GetVTable()->Get(resolved_method->GetMethodIndex()); - } -} + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx, mirror::ArtMethod* referrer, Thread* self, bool can_run_clinit, bool verify_access) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* klass = class_linker->ResolveType(type_idx, referrer); - if (UNLIKELY(klass == nullptr)) { - CHECK(self->IsExceptionPending()); - return nullptr; // Failure - Indicate to caller to deliver exception - } - // Perform access check if necessary. - mirror::Class* referring_class = referrer->GetDeclaringClass(); - if (verify_access && UNLIKELY(!referring_class->CanAccess(klass))) { - ThrowIllegalAccessErrorClass(referring_class, klass); - return nullptr; // Failure - Indicate to caller to deliver exception - } - // If we're just implementing const-class, we shouldn't call <clinit>. - if (!can_run_clinit) { - return klass; - } - // If we are the <clinit> of this class, just return our storage. - // - // Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished - // running. - if (klass == referring_class && referrer->IsConstructor() && referrer->IsStatic()) { - return klass; - } - StackHandleScope<1> hs(self); - Handle<mirror::Class> h_class(hs.NewHandle(klass)); - if (!class_linker->EnsureInitialized(h_class, true, true)) { - CHECK(self->IsExceptionPending()); - return nullptr; // Failure - Indicate to caller to deliver exception - } - return h_class.Get(); -} + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); extern void ThrowStackOverflowError(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static inline mirror::String* ResolveStringFromCode(mirror::ArtMethod* referrer, uint32_t string_idx) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - return class_linker->ResolveString(string_idx, referrer); -} + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +// TODO: annotalysis disabled as monitor semantics are maintained in Java code. static inline void UnlockJniSynchronizedMethod(jobject locked, Thread* self) - NO_THREAD_SAFETY_ANALYSIS /* SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) */ { - // Save any pending exception over monitor exit call. - mirror::Throwable* saved_exception = NULL; - ThrowLocation saved_throw_location; - bool is_exception_reported = self->IsExceptionReportedToInstrumentation(); - if (UNLIKELY(self->IsExceptionPending())) { - saved_exception = self->GetException(&saved_throw_location); - self->ClearException(); - } - // Decode locked object and unlock, before popping local references. - self->DecodeJObject(locked)->MonitorExit(self); - if (UNLIKELY(self->IsExceptionPending())) { - LOG(FATAL) << "Synchronized JNI code returning with an exception:\n" - << saved_exception->Dump() - << "\nEncountered second exception during implicit MonitorExit:\n" - << self->GetException(NULL)->Dump(); - } - // Restore pending exception. - if (saved_exception != NULL) { - self->SetException(saved_throw_location, saved_exception); - self->SetExceptionReportedToInstrumentation(is_exception_reported); - } -} + NO_THREAD_SAFETY_ANALYSIS; -static inline void CheckReferenceResult(mirror::Object* o, Thread* self) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (o == NULL) { - return; - } - mirror::ArtMethod* m = self->GetCurrentMethod(NULL); - if (o == kInvalidIndirectRefObject) { - JniAbortF(NULL, "invalid reference returned from %s", PrettyMethod(m).c_str()); - } - // Make sure that the result is an instance of the type this method was expected to return. - StackHandleScope<1> hs(self); - Handle<mirror::ArtMethod> h_m(hs.NewHandle(m)); - mirror::Class* return_type = MethodHelper(h_m).GetReturnType(); - - if (!o->InstanceOf(return_type)) { - JniAbortF(NULL, "attempt to return an instance of %s from %s", PrettyTypeOf(o).c_str(), - PrettyMethod(h_m.Get()).c_str()); - } -} +void CheckReferenceResult(mirror::Object* o, Thread* self) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -static inline void CheckSuspend(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - for (;;) { - if (thread->ReadFlag(kCheckpointRequest)) { - thread->RunCheckpointFunction(); - } else if (thread->ReadFlag(kSuspendRequest)) { - thread->FullSuspendCheck(); - } else { - break; - } - } -} +static inline void CheckSuspend(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, const char* shorty, jobject rcvr_jobj, jobject interface_art_method_jobj, @@ -750,26 +225,6 @@ static inline const void* GetQuickToPortableBridge() { return GetQuickToInterpreterBridge(); } -static inline const void* GetPortableResolutionTrampoline(ClassLinker* class_linker) { - return class_linker->GetPortableResolutionTrampoline(); -} - -static inline const void* GetQuickResolutionTrampoline(ClassLinker* class_linker) { - return class_linker->GetQuickResolutionTrampoline(); -} - -static inline const void* GetPortableImtConflictTrampoline(ClassLinker* class_linker) { - return class_linker->GetPortableImtConflictTrampoline(); -} - -static inline const void* GetQuickImtConflictTrampoline(ClassLinker* class_linker) { - return class_linker->GetQuickImtConflictTrampoline(); -} - -static inline const void* GetQuickToInterpreterBridgeTrampoline(ClassLinker* class_linker) { - return class_linker->GetQuickToInterpreterBridgeTrampoline(); -} - extern "C" void art_portable_proxy_invoke_handler(); static inline const void* GetPortableProxyInvokeHandler() { return reinterpret_cast<void*>(art_portable_proxy_invoke_handler); @@ -786,21 +241,7 @@ static inline void* GetJniDlsymLookupStub() { } template <typename INT_TYPE, typename FLOAT_TYPE> -static inline INT_TYPE art_float_to_integral(FLOAT_TYPE f) { - const INT_TYPE kMaxInt = static_cast<INT_TYPE>(std::numeric_limits<INT_TYPE>::max()); - const INT_TYPE kMinInt = static_cast<INT_TYPE>(std::numeric_limits<INT_TYPE>::min()); - const FLOAT_TYPE kMaxIntAsFloat = static_cast<FLOAT_TYPE>(kMaxInt); - const FLOAT_TYPE kMinIntAsFloat = static_cast<FLOAT_TYPE>(kMinInt); - if (LIKELY(f > kMinIntAsFloat)) { - if (LIKELY(f < kMaxIntAsFloat)) { - return static_cast<INT_TYPE>(f); - } else { - return kMaxInt; - } - } else { - return (f != f) ? 0 : kMinInt; // f != f implies NaN - } -} +static inline INT_TYPE art_float_to_integral(FLOAT_TYPE f); } // namespace art diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc index 329c175986..64faf76213 100644 --- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc +++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc @@ -18,7 +18,6 @@ #include "interpreter/interpreter.h" #include "mirror/art_method-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "reflection.h" #include "runtime.h" #include "stack.h" diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index bae4023c1d..edb3b723ef 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -18,7 +18,6 @@ #include "entrypoints/entrypoint_utils.h" #include "mirror/art_method-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "scoped_thread_state_change.h" #include "thread.h" diff --git a/runtime/entrypoints/math_entrypoints.cc b/runtime/entrypoints/math_entrypoints.cc index b839b6317d..b0eaf1ed03 100644 --- a/runtime/entrypoints/math_entrypoints.cc +++ b/runtime/entrypoints/math_entrypoints.cc @@ -16,7 +16,7 @@ #include "math_entrypoints.h" -#include "entrypoint_utils.h" +#include "entrypoint_utils-inl.h" namespace art { diff --git a/runtime/entrypoints/portable/portable_alloc_entrypoints.cc b/runtime/entrypoints/portable/portable_alloc_entrypoints.cc index 4c05e755ae..de95f7dfbc 100644 --- a/runtime/entrypoints/portable/portable_alloc_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_alloc_entrypoints.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "mirror/art_method-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/entrypoints/portable/portable_cast_entrypoints.cc b/runtime/entrypoints/portable/portable_cast_entrypoints.cc index a553a22df8..151b1785c9 100644 --- a/runtime/entrypoints/portable/portable_cast_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_cast_entrypoints.cc @@ -15,7 +15,7 @@ */ #include "common_throws.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "mirror/object-inl.h" namespace art { diff --git a/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc b/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc index b37ebcf785..9364c46abf 100644 --- a/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "gc/accounting/card_table-inl.h" #include "mirror/art_method-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/entrypoints/portable/portable_field_entrypoints.cc b/runtime/entrypoints/portable/portable_field_entrypoints.cc index f48f1a9016..371aca4b2d 100644 --- a/runtime/entrypoints/portable/portable_field_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_field_entrypoints.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc b/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc index 335a61770c..686954bc33 100644 --- a/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc @@ -15,7 +15,7 @@ */ #include "dex_instruction.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "mirror/art_method-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc index eb50ec3272..6f9c083c80 100644 --- a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "mirror/art_method-inl.h" #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/entrypoints/portable/portable_jni_entrypoints.cc b/runtime/entrypoints/portable/portable_jni_entrypoints.cc index 3e7b30a577..0d0f21b795 100644 --- a/runtime/entrypoints/portable/portable_jni_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_jni_entrypoints.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "mirror/art_method-inl.h" #include "mirror/object-inl.h" #include "thread-inl.h" diff --git a/runtime/entrypoints/portable/portable_lock_entrypoints.cc b/runtime/entrypoints/portable/portable_lock_entrypoints.cc index 358ac233dc..fcd3e9d49e 100644 --- a/runtime/entrypoints/portable/portable_lock_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_lock_entrypoints.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "mirror/object-inl.h" namespace art { diff --git a/runtime/entrypoints/portable/portable_thread_entrypoints.cc b/runtime/entrypoints/portable/portable_thread_entrypoints.cc index 9e62e0e9ff..23e1c3640a 100644 --- a/runtime/entrypoints/portable/portable_thread_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_thread_entrypoints.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "mirror/art_method.h" #include "mirror/object-inl.h" #include "verifier/dex_gc_map.h" diff --git a/runtime/entrypoints/portable/portable_throw_entrypoints.cc b/runtime/entrypoints/portable/portable_throw_entrypoints.cc index 189e6b5903..be6231cae5 100644 --- a/runtime/entrypoints/portable/portable_throw_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_throw_entrypoints.cc @@ -15,7 +15,7 @@ */ #include "dex_instruction.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "mirror/art_method-inl.h" #include "mirror/object-inl.h" @@ -80,7 +80,6 @@ extern "C" int32_t art_portable_find_catch_block_from_code(mirror::ArtMethod* cu } mirror::Class* exception_type = exception->GetClass(); StackHandleScope<1> hs(self); - MethodHelper mh(hs.NewHandle(current_method)); const DexFile::CodeItem* code_item = current_method->GetCodeItem(); DCHECK_LT(ti_offset, code_item->tries_size_); const DexFile::TryItem* try_item = DexFile::GetTryItems(*code_item, ti_offset); @@ -98,7 +97,8 @@ extern "C" int32_t art_portable_find_catch_block_from_code(mirror::ArtMethod* cu break; } // Does this catch exception type apply? - mirror::Class* iter_exception_type = mh.GetDexCacheResolvedType(iter_type_idx); + mirror::Class* iter_exception_type = + current_method->GetDexCacheResolvedTypes()->Get(iter_type_idx); if (UNLIKELY(iter_exception_type == NULL)) { // TODO: check, the verifier (class linker?) should take care of resolving all exception // classes early. diff --git a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc index 2da016f798..9f75b0fcf0 100644 --- a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc @@ -18,11 +18,10 @@ #define ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ARGUMENT_VISITOR_H_ #include "dex_instruction-inl.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "interpreter/interpreter.h" #include "mirror/art_method-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "scoped_thread_state_change.h" namespace art { @@ -431,7 +430,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 != GetPortableResolutionTrampoline(linker)); + DCHECK(code != linker->GetPortableResolutionTrampoline()); // Set up entry into main method *called_addr = called; } diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index dde74de87a..1f2713a4f7 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -17,7 +17,7 @@ #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "callee_save_frame.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc index 47fb9d66f8..f9f62c2721 100644 --- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc @@ -21,7 +21,6 @@ #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "stack.h" #include "thread.h" #include "verifier/method_verifier.h" diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index 53c9b97c29..704db0503f 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -15,7 +15,7 @@ */ #include "callee_save_frame.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "class_linker-inl.h" #include "dex_file-inl.h" #include "gc/accounting/card_table-inl.h" diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h index 032f6bebad..473687c396 100644 --- a/runtime/entrypoints/quick/quick_entrypoints.h +++ b/runtime/entrypoints/quick/quick_entrypoints.h @@ -138,6 +138,10 @@ struct PACKED(4) QuickEntryPoints { void (*pThrowNoSuchMethod)(int32_t); void (*pThrowNullPointer)(); void (*pThrowStackOverflow)(void*); + + // Atomic 64-bit load/store + int64_t (*pA64Load)(volatile const int64_t *); + void (*pA64Store)(volatile int64_t *, int64_t); }; diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc index 5cb0f3662f..cd1e2470cf 100644 --- a/runtime/entrypoints/quick/quick_field_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc @@ -16,7 +16,7 @@ #include "callee_save_frame.h" #include "dex_file-inl.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" diff --git a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc index d161d0b9ed..9a22c154f5 100644 --- a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc @@ -32,10 +32,15 @@ extern "C" const void* artInstrumentationMethodEntryFromCode(mirror::ArtMethod* SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - const void* result = instrumentation->GetQuickCodeFor(method); - DCHECK(result != GetQuickToInterpreterBridgeTrampoline(Runtime::Current()->GetClassLinker())); + const void* result; + if (instrumentation->IsDeoptimized(method)) { + result = GetQuickToInterpreterBridge(); + } else { + result = instrumentation->GetQuickCodeFor(method); + } + DCHECK(result != Runtime::Current()->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline()); bool interpreter_entry = (result == GetQuickToInterpreterBridge()); - instrumentation->PushInstrumentationStackFrame(self, method->IsStatic() ? NULL : this_object, + instrumentation->PushInstrumentationStackFrame(self, method->IsStatic() ? nullptr : this_object, method, lr, interpreter_entry); CHECK(result != NULL) << PrettyMethod(method); return result; diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc index 140b0754b4..653724989a 100644 --- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc @@ -15,13 +15,12 @@ */ #include "dex_file-inl.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/object.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" -#include "object_utils.h" #include "scoped_thread_state_change.h" #include "thread.h" #include "verify_object-inl.h" diff --git a/runtime/entrypoints/quick/quick_thread_entrypoints.cc b/runtime/entrypoints/quick/quick_thread_entrypoints.cc index 5c48fc74c0..118cd7fca1 100644 --- a/runtime/entrypoints/quick/quick_thread_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_thread_entrypoints.cc @@ -15,7 +15,7 @@ */ #include "callee_save_frame.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "thread.h" #include "thread_list.h" diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc index e6f294ace7..879010e36e 100644 --- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc @@ -16,9 +16,8 @@ #include "callee_save_frame.h" #include "common_throws.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "thread.h" #include "well_known_classes.h" diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 95cb85eefc..338bd06f7c 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -18,7 +18,7 @@ #include "common_throws.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "gc/accounting/card_table-inl.h" #include "instruction_set.h" #include "interpreter/interpreter.h" @@ -27,7 +27,6 @@ #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" -#include "object_utils.h" #include "runtime.h" #include "scoped_thread_state_change.h" @@ -175,8 +174,8 @@ class QuickArgumentVisitor { static constexpr size_t kNumQuickGprArgs = 5; // 5 arguments passed in GPRs. static constexpr size_t kNumQuickFprArgs = 8; // 8 arguments passed in FPRs. static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 16; // Offset of first FPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80; // Offset of first GPR arg. - static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 168; // Offset of return address. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80 + 4*8; // Offset of first GPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 168 + 4*8; // Offset of return address. static size_t GprIndexToGprOffset(uint32_t gpr_index) { switch (gpr_index) { case 0: return (4 * GetBytesPerGprSpillLocation(kRuntimeISA)); diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index c572baf937..ae1b94fa90 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -121,7 +121,7 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, kPointerSize); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, rosalloc_runs, kPointerSize); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, rosalloc_runs, thread_local_alloc_stack_top, - kPointerSize * gc::allocator::RosAlloc::kNumThreadLocalSizeBrackets); + kPointerSize * kNumRosAllocThreadLocalSizeBrackets); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_top, thread_local_alloc_stack_end, kPointerSize); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_end, held_mutexes, kPointerSize); @@ -259,8 +259,10 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowDivZero, pThrowNoSuchMethod, kPointerSize); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNoSuchMethod, pThrowNullPointer, kPointerSize); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNullPointer, pThrowStackOverflow, kPointerSize); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStackOverflow, pA64Load, kPointerSize); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pA64Load, pA64Store, kPointerSize); - CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pThrowStackOverflow) + CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pA64Store) + kPointerSize == sizeof(QuickEntryPoints), QuickEntryPoints_all); } }; diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index 3112bc0a28..1b916284c5 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -15,23 +15,13 @@ */ #include "fault_handler.h" + #include <sys/mman.h> #include <sys/ucontext.h> -#include "base/macros.h" -#include "globals.h" -#include "base/logging.h" -#include "base/hex_dump.h" -#include "thread.h" -#include "mirror/art_method-inl.h" -#include "mirror/class-inl.h" -#include "mirror/dex_cache.h" -#include "mirror/object_array-inl.h" -#include "mirror/object-inl.h" -#include "object_utils.h" -#include "scoped_thread_state_change.h" -#ifdef HAVE_ANDROID_OS +#include "mirror/art_method.h" +#include "mirror/class.h" #include "sigchain.h" -#endif +#include "thread-inl.h" #include "verify_object-inl.h" namespace art { @@ -47,6 +37,7 @@ void art_sigsegv_fault() { // Signal handler called on SIGSEGV. static void art_fault_handler(int sig, siginfo_t* info, void* context) { + // std::cout << "handling fault in ART handler\n"; fault_manager.HandleFault(sig, info, context); } @@ -55,10 +46,6 @@ FaultManager::FaultManager() { } FaultManager::~FaultManager() { -#ifdef HAVE_ANDROID_OS - UnclaimSignalChain(SIGSEGV); -#endif - sigaction(SIGSEGV, &oldaction_, nullptr); // Restore old handler. } @@ -72,11 +59,12 @@ void FaultManager::Init() { #endif // Set our signal handler now. - sigaction(SIGSEGV, &action, &oldaction_); -#ifdef HAVE_ANDROID_OS + int e = sigaction(SIGSEGV, &action, &oldaction_); + if (e != 0) { + VLOG(signals) << "Failed to claim SEGV: " << strerror(errno); + } // Make sure our signal handler is called before any user handlers. ClaimSignalChain(SIGSEGV, &oldaction_); -#endif } void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { @@ -84,8 +72,12 @@ 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. + + // Also, there is only an 8K stack available here to logging can cause memory + // overwrite issues if you are unlucky. If you want to enable logging and + // are getting crashes, allocate more space for the alternate signal stack. VLOG(signals) << "Handling fault"; - if (IsInGeneratedCode(context, true)) { + if (IsInGeneratedCode(info, context, true)) { VLOG(signals) << "in generated code, looking for handler"; for (const auto& handler : generated_code_handlers_) { VLOG(signals) << "invoking Action on handler " << handler; @@ -101,11 +93,8 @@ void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { } art_sigsegv_fault(); -#ifdef HAVE_ANDROID_OS + // Pass this on to the next handler in the chain, or the default if none. InvokeUserSignalHandler(sig, info, context); -#else - oldaction_.sa_sigaction(sig, info, context); -#endif } void FaultManager::AddHandler(FaultHandler* handler, bool generated_code) { @@ -132,7 +121,7 @@ void FaultManager::RemoveHandler(FaultHandler* handler) { // This function is called within the signal handler. It checks that // the mutator_lock is held (shared). No annotalysis is done. -bool FaultManager::IsInGeneratedCode(void* context, bool check_dex_pc) { +bool FaultManager::IsInGeneratedCode(siginfo_t* siginfo, void* context, bool check_dex_pc) { // We can only be running Java code in the current thread if it // is in Runnable state. VLOG(signals) << "Checking for generated code"; @@ -161,7 +150,7 @@ bool FaultManager::IsInGeneratedCode(void* context, bool check_dex_pc) { // Get the architecture specific method address and return address. These // are in architecture specific files in arch/<arch>/fault_handler_<arch>. - GetMethodAndReturnPCAndSP(context, &method_obj, &return_pc, &sp); + GetMethodAndReturnPCAndSP(siginfo, context, &method_obj, &return_pc, &sp); // If we don't have a potential method, we're outta here. VLOG(signals) << "potential method: " << method_obj; @@ -242,12 +231,12 @@ JavaStackTraceHandler::JavaStackTraceHandler(FaultManager* manager) : FaultHandl bool JavaStackTraceHandler::Action(int sig, siginfo_t* siginfo, void* context) { // Make sure that we are in the generated code, but we may not have a dex pc. - if (manager_->IsInGeneratedCode(context, false)) { + if (manager_->IsInGeneratedCode(siginfo, context, false)) { LOG(ERROR) << "Dumping java stack trace for crash in generated code"; mirror::ArtMethod* method = nullptr; uintptr_t return_pc = 0; uintptr_t sp = 0; - manager_->GetMethodAndReturnPCAndSP(context, &method, &return_pc, &sp); + manager_->GetMethodAndReturnPCAndSP(siginfo, context, &method, &return_pc, &sp); Thread* self = Thread::Current(); // Inside of generated code, sp[0] is the method, so sp is the frame. StackReference<mirror::ArtMethod>* frame = diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h index 026f5b9c4a..71c99771cd 100644 --- a/runtime/fault_handler.h +++ b/runtime/fault_handler.h @@ -43,9 +43,10 @@ class FaultManager { void HandleFault(int sig, siginfo_t* info, void* context); void AddHandler(FaultHandler* handler, bool generated_code); void RemoveHandler(FaultHandler* handler); - void GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, + void GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp); - bool IsInGeneratedCode(void *context, bool check_dex_pc) NO_THREAD_SAFETY_ANALYSIS; + bool IsInGeneratedCode(siginfo_t* siginfo, void *context, bool check_dex_pc) + NO_THREAD_SAFETY_ANALYSIS; private: std::vector<FaultHandler*> generated_code_handlers_; diff --git a/runtime/field_helper.cc b/runtime/field_helper.cc new file mode 100644 index 0000000000..40daa6db3f --- /dev/null +++ b/runtime/field_helper.cc @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "field_helper.h" + +#include "class_linker-inl.h" +#include "dex_file.h" +#include "mirror/dex_cache.h" +#include "runtime.h" +#include "thread-inl.h" + +namespace art { + +mirror::Class* FieldHelper::GetType(bool resolve) { + uint32_t field_index = field_->GetDexFieldIndex(); + if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) { + return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), + field_->GetTypeDescriptor()); + } + const DexFile* dex_file = field_->GetDexFile(); + const DexFile::FieldId& field_id = dex_file->GetFieldId(field_index); + mirror::Class* type = field_->GetDexCache()->GetResolvedType(field_id.type_idx_); + if (resolve && (type == nullptr)) { + type = Runtime::Current()->GetClassLinker()->ResolveType(field_id.type_idx_, field_.Get()); + CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); + } + return type; +} + +const char* FieldHelper::GetDeclaringClassDescriptor() { + uint32_t field_index = field_->GetDexFieldIndex(); + if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) { + DCHECK(field_->IsStatic()); + DCHECK_LT(field_index, 2U); + // 0 == Class[] interfaces; 1 == Class[][] throws; + declaring_class_descriptor_ = field_->GetDeclaringClass()->GetDescriptor(); + return declaring_class_descriptor_.c_str(); + } + const DexFile* dex_file = field_->GetDexFile(); + const DexFile::FieldId& field_id = dex_file->GetFieldId(field_index); + return dex_file->GetFieldDeclaringClassDescriptor(field_id); +} + +} // namespace art diff --git a/runtime/field_helper.h b/runtime/field_helper.h new file mode 100644 index 0000000000..5eae55e09c --- /dev/null +++ b/runtime/field_helper.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_FIELD_HELPER_H_ +#define ART_RUNTIME_FIELD_HELPER_H_ + +#include "base/macros.h" +#include "handle.h" +#include "mirror/art_field.h" + +namespace art { + +class FieldHelper { + public: + explicit FieldHelper(Handle<mirror::ArtField> f) : field_(f) {} + + void ChangeField(mirror::ArtField* new_f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(new_f != nullptr); + field_.Assign(new_f); + } + + mirror::ArtField* GetField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return field_.Get(); + } + + mirror::Class* GetType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // The returned const char* is only guaranteed to be valid for the lifetime of the FieldHelper. + // If you need it longer, copy it into a std::string. + const char* GetDeclaringClassDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + Handle<mirror::ArtField> field_; + std::string declaring_class_descriptor_; + + DISALLOW_COPY_AND_ASSIGN(FieldHelper); +}; + +} // namespace art + +#endif // ART_RUNTIME_FIELD_HELPER_H_ diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h index ad0a4f438a..46b9363b9a 100644 --- a/runtime/gc/accounting/card_table-inl.h +++ b/runtime/gc/accounting/card_table-inl.h @@ -50,8 +50,9 @@ static inline bool byte_cas(byte old_value, byte new_value, byte* address) { template <typename Visitor> inline size_t CardTable::Scan(ContinuousSpaceBitmap* bitmap, byte* scan_begin, byte* scan_end, const Visitor& visitor, const byte minimum_age) const { - DCHECK(bitmap->HasAddress(scan_begin)); - DCHECK(bitmap->HasAddress(scan_end - 1)); // scan_end is the byte after the last byte we scan. + DCHECK_GE(scan_begin, reinterpret_cast<byte*>(bitmap->HeapBegin())); + // scan_end is the byte after the last byte we scan. + DCHECK_LE(scan_end, reinterpret_cast<byte*>(bitmap->HeapLimit())); byte* card_cur = CardFromAddr(scan_begin); byte* card_end = CardFromAddr(scan_end); CheckCardValid(card_cur); diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc index a95c0038a4..ceb42e5936 100644 --- a/runtime/gc/accounting/card_table.cc +++ b/runtime/gc/accounting/card_table.cc @@ -83,8 +83,6 @@ CardTable* CardTable::Create(const byte* heap_begin, size_t heap_capacity) { CardTable::CardTable(MemMap* mem_map, byte* biased_begin, size_t offset) : mem_map_(mem_map), biased_begin_(biased_begin), offset_(offset) { - byte* __attribute__((unused)) begin = mem_map_->Begin() + offset_; - byte* __attribute__((unused)) end = mem_map_->End(); } void CardTable::ClearSpaceCards(space::ContinuousSpace* space) { diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc index 228d1dc668..2686af0529 100644 --- a/runtime/gc/accounting/mod_union_table.cc +++ b/runtime/gc/accounting/mod_union_table.cc @@ -185,7 +185,7 @@ class CheckReferenceVisitor { << from_space->GetGcRetentionPolicy(); LOG(INFO) << "ToSpace " << to_space->GetName() << " type " << to_space->GetGcRetentionPolicy(); - heap->DumpSpaces(); + heap->DumpSpaces(LOG(INFO)); LOG(FATAL) << "FATAL ERROR"; } } diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h index 1e9556a076..fc4213e8c6 100644 --- a/runtime/gc/accounting/space_bitmap-inl.h +++ b/runtime/gc/accounting/space_bitmap-inl.h @@ -23,14 +23,6 @@ #include "atomic.h" #include "base/logging.h" -#include "dex_file-inl.h" -#include "heap_bitmap.h" -#include "mirror/art_field-inl.h" -#include "mirror/class-inl.h" -#include "mirror/object-inl.h" -#include "mirror/object_array-inl.h" -#include "object_utils.h" -#include "space_bitmap-inl.h" #include "utils.h" namespace art { diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc index c0aa43ea41..39d1f9e132 100644 --- a/runtime/gc/accounting/space_bitmap.cc +++ b/runtime/gc/accounting/space_bitmap.cc @@ -16,6 +16,13 @@ #include "space_bitmap-inl.h" +#include "base/stringprintf.h" +#include "mem_map.h" +#include "mirror/object-inl.h" +#include "mirror/class.h" +#include "mirror/art_field.h" +#include "mirror/object_array.h" + namespace art { namespace gc { namespace accounting { @@ -46,6 +53,9 @@ SpaceBitmap<kAlignment>::SpaceBitmap(const std::string& name, MemMap* mem_map, u } template<size_t kAlignment> +SpaceBitmap<kAlignment>::~SpaceBitmap() {} + +template<size_t kAlignment> SpaceBitmap<kAlignment>* SpaceBitmap<kAlignment>::Create( const std::string& name, byte* heap_begin, size_t heap_capacity) { // Round up since heap_capacity is not necessarily a multiple of kAlignment * kBitsPerWord. diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h index 6d1ba870db..a3073bda07 100644 --- a/runtime/gc/accounting/space_bitmap.h +++ b/runtime/gc/accounting/space_bitmap.h @@ -54,8 +54,7 @@ class SpaceBitmap { static SpaceBitmap* CreateFromMemMap(const std::string& name, MemMap* mem_map, byte* heap_begin, size_t heap_capacity); - ~SpaceBitmap() { - } + ~SpaceBitmap(); // <offset> is the difference from .base to a pointer address. // <index> is the index of .bits that contains the bit representing diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index 722576f164..ad22a2eae4 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -68,9 +68,9 @@ RosAlloc::RosAlloc(void* base, size_t capacity, size_t max_capacity, << ", capacity=" << std::dec << capacity_ << ", max_capacity=" << std::dec << max_capacity_; for (size_t i = 0; i < kNumOfSizeBrackets; i++) { - size_bracket_lock_names[i] = + size_bracket_lock_names_[i] = StringPrintf("an rosalloc size bracket %d lock", static_cast<int>(i)); - size_bracket_locks_[i] = new Mutex(size_bracket_lock_names[i].c_str(), kRosAllocBracketLock); + size_bracket_locks_[i] = new Mutex(size_bracket_lock_names_[i].c_str(), kRosAllocBracketLock); current_runs_[i] = dedicated_full_run_; } DCHECK_EQ(footprint_, capacity_); @@ -2112,30 +2112,40 @@ size_t RosAlloc::ReleasePages() { // result in occasionally not releasing pages which we could release. byte pm = page_map_[i]; switch (pm) { + case kPageMapReleased: + // Fall through. case kPageMapEmpty: { - // Only lock if we have an empty page since we want to prevent other threads racing in. + // This is currently the start of a free page run. + // Acquire the lock to prevent other threads racing in and modifying the page map. MutexLock mu(self, lock_); // Check that it's still empty after we acquired the lock since another thread could have // raced in and placed an allocation here. - pm = page_map_[i]; - if (LIKELY(pm == kPageMapEmpty)) { - // The start of a free page run. Release pages. + if (IsFreePage(i)) { + // Free page runs can start with a released page if we coalesced a released page free + // page run with an empty page run. FreePageRun* fpr = reinterpret_cast<FreePageRun*>(base_ + i * kPageSize); - DCHECK(free_page_runs_.find(fpr) != free_page_runs_.end()); - size_t fpr_size = fpr->ByteSize(this); - DCHECK(IsAligned<kPageSize>(fpr_size)); - byte* start = reinterpret_cast<byte*>(fpr); - reclaimed_bytes += ReleasePageRange(start, start + fpr_size); - i += fpr_size / kPageSize; - DCHECK_LE(i, page_map_size_); + // There is a race condition where FreePage can coalesce fpr with the previous + // free page run before we acquire lock_. In that case free_page_runs_.find will not find + // a run starting at fpr. To handle this race, we skip reclaiming the page range and go + // to the next page. + if (free_page_runs_.find(fpr) != free_page_runs_.end()) { + size_t fpr_size = fpr->ByteSize(this); + DCHECK(IsAligned<kPageSize>(fpr_size)); + byte* start = reinterpret_cast<byte*>(fpr); + reclaimed_bytes += ReleasePageRange(start, start + fpr_size); + size_t pages = fpr_size / kPageSize; + CHECK_GT(pages, 0U) << "Infinite loop probable"; + i += pages; + DCHECK_LE(i, page_map_size_); + break; + } } - break; + // Fall through. } case kPageMapLargeObject: // Fall through. case kPageMapLargeObjectPart: // Fall through. case kPageMapRun: // Fall through. case kPageMapRunPart: // Fall through. - case kPageMapReleased: // Fall through since it is already released. ++i; break; // Skip. default: @@ -2175,6 +2185,34 @@ size_t RosAlloc::ReleasePageRange(byte* start, byte* end) { return reclaimed_bytes; } +void RosAlloc::LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) { + Thread* self = Thread::Current(); + size_t largest_continuous_free_pages = 0; + WriterMutexLock wmu(self, bulk_free_lock_); + MutexLock mu(self, lock_); + for (FreePageRun* fpr : free_page_runs_) { + largest_continuous_free_pages = std::max(largest_continuous_free_pages, + fpr->ByteSize(this)); + } + if (failed_alloc_bytes > kLargeSizeThreshold) { + // Large allocation. + size_t required_bytes = RoundUp(failed_alloc_bytes, kPageSize); + if (required_bytes > largest_continuous_free_pages) { + os << "; failed due to fragmentation (required continguous free " + << required_bytes << " bytes where largest contiguous free " + << largest_continuous_free_pages << " bytes)"; + } + } else { + // Non-large allocation. + size_t required_bytes = numOfPages[SizeToIndex(failed_alloc_bytes)] * kPageSize; + if (required_bytes > largest_continuous_free_pages) { + os << "; failed due to fragmentation (required continguous free " + << required_bytes << " bytes for a new buffer where largest contiguous free " + << largest_continuous_free_pages << " bytes)"; + } + } +} + } // namespace allocator } // namespace gc } // namespace art diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index fad0dc888e..b2a5a3c96c 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -30,6 +30,7 @@ #include "base/logging.h" #include "globals.h" #include "mem_map.h" +#include "thread.h" #include "utils.h" namespace art { @@ -261,7 +262,7 @@ class RosAlloc { // The magic number for free pages. static const byte kMagicNumFree = 43; // The number of size brackets. Sync this with the length of Thread::rosalloc_runs_. - static const size_t kNumOfSizeBrackets = 34; + static const size_t kNumOfSizeBrackets = kNumRosAllocThreadLocalSizeBrackets; // The number of smaller size brackets that are 16 bytes apart. static const size_t kNumOfQuantumSizeBrackets = 32; // The sizes (the slot sizes, in bytes) of the size brackets. @@ -440,7 +441,7 @@ class RosAlloc { // The mutexes, one per size bracket. Mutex* size_bracket_locks_[kNumOfSizeBrackets]; // Bracket lock names (since locks only have char* names). - std::string size_bracket_lock_names[kNumOfSizeBrackets]; + std::string size_bracket_lock_names_[kNumOfSizeBrackets]; // The types of page map entries. enum { kPageMapReleased = 0, // Zero and released back to the OS. @@ -590,6 +591,8 @@ class RosAlloc { // Verify for debugging. void Verify() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + + void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes); }; } // namespace allocator diff --git a/runtime/gc/collector/immune_region.h b/runtime/gc/collector/immune_region.h index 0c0a89b253..277525e831 100644 --- a/runtime/gc/collector/immune_region.h +++ b/runtime/gc/collector/immune_region.h @@ -19,7 +19,6 @@ #include "base/macros.h" #include "base/mutex.h" -#include "gc/space/space-inl.h" namespace art { namespace mirror { diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h index 25cfe0fbfd..bb85fa0a81 100644 --- a/runtime/gc/collector/mark_compact.h +++ b/runtime/gc/collector/mark_compact.h @@ -49,6 +49,7 @@ namespace accounting { } // namespace accounting namespace space { + class BumpPointerSpace; class ContinuousMemMapAllocSpace; class ContinuousSpace; } // namespace space diff --git a/runtime/gc/collector/mark_sweep-inl.h b/runtime/gc/collector/mark_sweep-inl.h index 974952d992..104ed36014 100644 --- a/runtime/gc/collector/mark_sweep-inl.h +++ b/runtime/gc/collector/mark_sweep-inl.h @@ -32,10 +32,7 @@ namespace collector { template<typename MarkVisitor, typename ReferenceVisitor> inline void MarkSweep::ScanObjectVisit(mirror::Object* obj, const MarkVisitor& visitor, const ReferenceVisitor& ref_visitor) { - if (kIsDebugBuild && !IsMarked(obj)) { - heap_->DumpSpaces(); - LOG(FATAL) << "Scanning unmarked object " << obj; - } + DCHECK(IsMarked(obj)) << "Scanning unmarked object " << obj << "\n" << heap_->DumpSpaces(); obj->VisitReferences<false>(visitor, ref_visitor); if (kCountScannedTypes) { mirror::Class* klass = obj->GetClass<kVerifyNone>(); diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 7e97b3b16b..95530be202 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -313,10 +313,8 @@ void MarkSweep::FindDefaultSpaceBitmap() { } } } - if (current_space_bitmap_ == nullptr) { - heap_->DumpSpaces(); - LOG(FATAL) << "Could not find a default mark bitmap"; - } + CHECK(current_space_bitmap_ != nullptr) << "Could not find a default mark bitmap\n" + << heap_->DumpSpaces(); } void MarkSweep::ExpandMarkStack() { @@ -943,12 +941,9 @@ mirror::Object* MarkSweep::VerifySystemWeakIsLiveCallback(Object* obj, void* arg void MarkSweep::VerifyIsLive(const Object* obj) { if (!heap_->GetLiveBitmap()->Test(obj)) { - if (std::find(heap_->allocation_stack_->Begin(), heap_->allocation_stack_->End(), obj) == - heap_->allocation_stack_->End()) { - // Object not found! - heap_->DumpSpaces(); - LOG(FATAL) << "Found dead object " << obj; - } + accounting::ObjectStack* allocation_stack = heap_->allocation_stack_.get(); + CHECK(std::find(allocation_stack->Begin(), allocation_stack->End(), obj) != + allocation_stack->End()) << "Found dead object " << obj << "\n" << heap_->DumpSpaces(); } } diff --git a/runtime/gc/collector/semi_space-inl.h b/runtime/gc/collector/semi_space-inl.h index 47682cc584..922a71ceb2 100644 --- a/runtime/gc/collector/semi_space-inl.h +++ b/runtime/gc/collector/semi_space-inl.h @@ -64,34 +64,25 @@ inline void SemiSpace::MarkObject( // Verify all the objects have the correct forward pointer installed. obj->AssertReadBarrierPointer(); } - if (!immune_region_.ContainsObject(obj)) { - if (from_space_->HasAddress(obj)) { - mirror::Object* forward_address = GetForwardingAddressInFromSpace(obj); - // If the object has already been moved, return the new forward address. - if (UNLIKELY(forward_address == nullptr)) { - forward_address = MarkNonForwardedObject(obj); - DCHECK(forward_address != nullptr); - // Make sure to only update the forwarding address AFTER you copy the object so that the - // monitor word doesn't Get stomped over. - obj->SetLockWord( - LockWord::FromForwardingAddress(reinterpret_cast<size_t>(forward_address)), false); - // Push the object onto the mark stack for later processing. - MarkStackPush(forward_address); - } - obj_ptr->Assign(forward_address); - } else { - BitmapSetSlowPathVisitor visitor(this); - if (kIsDebugBuild && mark_bitmap_->GetContinuousSpaceBitmap(obj) != nullptr) { - // If a bump pointer space only collection, we should not - // reach here as we don't/won't mark the objects in the - // non-moving space (except for the promoted objects.) Note - // the non-moving space is added to the immune space. - DCHECK(!generational_ || whole_heap_collection_); - } - if (!mark_bitmap_->Set(obj, visitor)) { - // This object was not previously marked. - MarkStackPush(obj); - } + if (from_space_->HasAddress(obj)) { + mirror::Object* forward_address = GetForwardingAddressInFromSpace(obj); + // If the object has already been moved, return the new forward address. + if (UNLIKELY(forward_address == nullptr)) { + forward_address = MarkNonForwardedObject(obj); + DCHECK(forward_address != nullptr); + // Make sure to only update the forwarding address AFTER you copy the object so that the + // monitor word doesn't Get stomped over. + obj->SetLockWord( + LockWord::FromForwardingAddress(reinterpret_cast<size_t>(forward_address)), false); + // Push the object onto the mark stack for later processing. + MarkStackPush(forward_address); + } + obj_ptr->Assign(forward_address); + } else if (!collect_from_space_only_ && !immune_region_.ContainsObject(obj)) { + BitmapSetSlowPathVisitor visitor(this); + if (!mark_bitmap_->Set(obj, visitor)) { + // This object was not previously marked. + MarkStackPush(obj); } } } diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index cabfe2176c..c7c567f2bd 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -63,23 +63,23 @@ void SemiSpace::BindBitmaps() { WriterMutexLock mu(self_, *Locks::heap_bitmap_lock_); // Mark all of the spaces we never collect as immune. for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->GetLiveBitmap() != nullptr) { - if (space == to_space_) { - CHECK(to_space_->IsContinuousMemMapAllocSpace()); - to_space_->AsContinuousMemMapAllocSpace()->BindLiveToMarkBitmap(); - } else if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyNeverCollect - || space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect - // Add the main free list space and the non-moving - // space to the immune space if a bump pointer space - // only collection. - || (generational_ && !whole_heap_collection_ && - (space == GetHeap()->GetNonMovingSpace() || - space == GetHeap()->GetPrimaryFreeListSpace()))) { - CHECK(immune_region_.AddContinuousSpace(space)) << "Failed to add space " << *space; + if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyNeverCollect || + space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) { + CHECK(immune_region_.AddContinuousSpace(space)) << "Failed to add space " << *space; + } else if (space->GetLiveBitmap() != nullptr) { + if (space == to_space_ || collect_from_space_only_) { + if (collect_from_space_only_) { + // Bind the main free list space and the non-moving space to the immune space if a bump + // pointer space only collection. + CHECK(space == to_space_ || space == GetHeap()->GetPrimaryFreeListSpace() || + space == GetHeap()->GetNonMovingSpace()); + } + CHECK(space->IsContinuousMemMapAllocSpace()); + space->AsContinuousMemMapAllocSpace()->BindLiveToMarkBitmap(); } } } - if (generational_ && !whole_heap_collection_) { + if (collect_from_space_only_) { // We won't collect the large object space if a bump pointer space only collection. is_large_object_space_immune_ = true; } @@ -95,7 +95,7 @@ SemiSpace::SemiSpace(Heap* heap, bool generational, const std::string& name_pref bytes_promoted_(0), bytes_promoted_since_last_whole_heap_collection_(0), large_object_bytes_allocated_at_last_whole_heap_collection_(0), - whole_heap_collection_(true), + collect_from_space_only_(generational), collector_name_(name_), swap_semi_spaces_(true) { } @@ -147,6 +147,10 @@ void SemiSpace::InitializePhase() { ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); mark_bitmap_ = heap_->GetMarkBitmap(); } + if (generational_) { + promo_dest_space_ = GetHeap()->GetPrimaryFreeListSpace(); + } + fallback_space_ = GetHeap()->GetNonMovingSpace(); } void SemiSpace::ProcessReferences(Thread* self) { @@ -180,9 +184,9 @@ void SemiSpace::MarkingPhase() { GetCurrentIteration()->GetClearSoftReferences()) { // If an explicit, native allocation-triggered, or last attempt // collection, collect the whole heap. - whole_heap_collection_ = true; + collect_from_space_only_ = false; } - if (whole_heap_collection_) { + if (!collect_from_space_only_) { VLOG(heap) << "Whole heap collection"; name_ = collector_name_ + " whole"; } else { @@ -191,7 +195,7 @@ void SemiSpace::MarkingPhase() { } } - if (!generational_ || whole_heap_collection_) { + if (!collect_from_space_only_) { // If non-generational, always clear soft references. // If generational, clear soft references if a whole heap collection. GetCurrentIteration()->SetClearSoftReferences(true); @@ -227,8 +231,6 @@ void SemiSpace::MarkingPhase() { { WriterMutexLock mu(self_, *Locks::heap_bitmap_lock_); MarkRoots(); - // Mark roots of immune spaces. - UpdateAndMarkModUnion(); // Recursively mark remaining objects. MarkReachableObjects(); } @@ -259,46 +261,6 @@ void SemiSpace::MarkingPhase() { } } -void SemiSpace::UpdateAndMarkModUnion() { - for (auto& space : heap_->GetContinuousSpaces()) { - // If the space is immune then we need to mark the references to other spaces. - if (immune_region_.ContainsSpace(space)) { - accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space); - if (table != nullptr) { - // TODO: Improve naming. - TimingLogger::ScopedTiming t( - space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : - "UpdateAndMarkImageModUnionTable", - GetTimings()); - table->UpdateAndMarkReferences(MarkHeapReferenceCallback, this); - } else if (heap_->FindRememberedSetFromSpace(space) != nullptr) { - DCHECK(kUseRememberedSet); - // If a bump pointer space only collection, the non-moving - // space is added to the immune space. The non-moving space - // doesn't have a mod union table, but has a remembered - // set. Its dirty cards will be scanned later in - // MarkReachableObjects(). - DCHECK(generational_ && !whole_heap_collection_ && - (space == heap_->GetNonMovingSpace() || space == heap_->GetPrimaryFreeListSpace())) - << "Space " << space->GetName() << " " - << "generational_=" << generational_ << " " - << "whole_heap_collection_=" << whole_heap_collection_ << " "; - } else { - DCHECK(!kUseRememberedSet); - // If a bump pointer space only collection, the non-moving - // space is added to the immune space. But the non-moving - // space doesn't have a mod union table. Instead, its live - // bitmap will be scanned later in MarkReachableObjects(). - DCHECK(generational_ && !whole_heap_collection_ && - (space == heap_->GetNonMovingSpace() || space == heap_->GetPrimaryFreeListSpace())) - << "Space " << space->GetName() << " " - << "generational_=" << generational_ << " " - << "whole_heap_collection_=" << whole_heap_collection_ << " "; - } - } - } -} - class SemiSpaceScanObjectVisitor { public: explicit SemiSpaceScanObjectVisitor(SemiSpace* ss) : semi_space_(ss) {} @@ -355,20 +317,30 @@ void SemiSpace::MarkReachableObjects() { heap_->MarkAllocStackAsLive(live_stack); live_stack->Reset(); } - t.NewTiming("UpdateAndMarkRememberedSets"); for (auto& space : heap_->GetContinuousSpaces()) { - // If the space is immune and has no mod union table (the - // non-moving space when the bump pointer space only collection is - // enabled,) then we need to scan its live bitmap or dirty cards as roots - // (including the objects on the live stack which have just marked - // in the live bitmap above in MarkAllocStackAsLive().) - if (immune_region_.ContainsSpace(space) && - heap_->FindModUnionTableFromSpace(space) == nullptr) { - DCHECK(generational_ && !whole_heap_collection_ && - (space == GetHeap()->GetNonMovingSpace() || space == GetHeap()->GetPrimaryFreeListSpace())); - accounting::RememberedSet* rem_set = heap_->FindRememberedSetFromSpace(space); - if (kUseRememberedSet) { - DCHECK(rem_set != nullptr); + // If the space is immune then we need to mark the references to other spaces. + accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space); + if (table != nullptr) { + // TODO: Improve naming. + TimingLogger::ScopedTiming t2( + space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : + "UpdateAndMarkImageModUnionTable", + GetTimings()); + table->UpdateAndMarkReferences(MarkHeapReferenceCallback, this); + DCHECK(GetHeap()->FindRememberedSetFromSpace(space) == nullptr); + } else if (collect_from_space_only_ && space->GetLiveBitmap() != nullptr) { + // If the space has no mod union table (the non-moving space and main spaces when the bump + // pointer space only collection is enabled,) then we need to scan its live bitmap or dirty + // cards as roots (including the objects on the live stack which have just marked in the live + // bitmap above in MarkAllocStackAsLive().) + DCHECK(space == heap_->GetNonMovingSpace() || space == heap_->GetPrimaryFreeListSpace()) + << "Space " << space->GetName() << " " + << "generational_=" << generational_ << " " + << "collect_from_space_only_=" << collect_from_space_only_; + accounting::RememberedSet* rem_set = GetHeap()->FindRememberedSetFromSpace(space); + CHECK_EQ(rem_set != nullptr, kUseRememberedSet); + if (rem_set != nullptr) { + TimingLogger::ScopedTiming t2("UpdateAndMarkRememberedSet", GetTimings()); rem_set->UpdateAndMarkReferences(MarkHeapReferenceCallback, DelayReferenceReferentCallback, from_space_, this); if (kIsDebugBuild) { @@ -383,7 +355,7 @@ void SemiSpace::MarkReachableObjects() { visitor); } } else { - DCHECK(rem_set == nullptr); + TimingLogger::ScopedTiming t2("VisitLiveBits", GetTimings()); accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap(); SemiSpaceScanObjectVisitor visitor(this); live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()), @@ -393,9 +365,10 @@ void SemiSpace::MarkReachableObjects() { } } + CHECK_EQ(is_large_object_space_immune_, collect_from_space_only_); if (is_large_object_space_immune_) { TimingLogger::ScopedTiming t("VisitLargeObjects", GetTimings()); - DCHECK(generational_ && !whole_heap_collection_); + DCHECK(collect_from_space_only_); // Delay copying the live set to the marked set until here from // BindBitmaps() as the large objects on the allocation stack may // be newly added to the live set above in MarkAllocStackAsLive(). @@ -506,19 +479,20 @@ static inline size_t CopyAvoidingDirtyingPages(void* dest, const void* src, size } mirror::Object* SemiSpace::MarkNonForwardedObject(mirror::Object* obj) { - size_t object_size = obj->SizeOf(); + const size_t object_size = obj->SizeOf(); size_t bytes_allocated; mirror::Object* forward_address = nullptr; if (generational_ && reinterpret_cast<byte*>(obj) < last_gc_to_space_end_) { // If it's allocated before the last GC (older), move // (pseudo-promote) it to the main free list space (as sort // of an old generation.) - space::MallocSpace* promo_dest_space = GetHeap()->GetPrimaryFreeListSpace(); - forward_address = promo_dest_space->AllocThreadUnsafe(self_, object_size, &bytes_allocated, - nullptr); + forward_address = promo_dest_space_->AllocThreadUnsafe(self_, object_size, &bytes_allocated, + nullptr); if (UNLIKELY(forward_address == nullptr)) { // If out of space, fall back to the to-space. forward_address = to_space_->AllocThreadUnsafe(self_, object_size, &bytes_allocated, nullptr); + // No logic for marking the bitmap, so it must be null. + DCHECK(to_space_->GetLiveBitmap() == nullptr); } else { bytes_promoted_ += bytes_allocated; // Dirty the card at the destionation as it may contain @@ -526,12 +500,12 @@ mirror::Object* SemiSpace::MarkNonForwardedObject(mirror::Object* obj) { // space. GetHeap()->WriteBarrierEveryFieldOf(forward_address); // Handle the bitmaps marking. - accounting::ContinuousSpaceBitmap* live_bitmap = promo_dest_space->GetLiveBitmap(); + accounting::ContinuousSpaceBitmap* live_bitmap = promo_dest_space_->GetLiveBitmap(); DCHECK(live_bitmap != nullptr); - accounting::ContinuousSpaceBitmap* mark_bitmap = promo_dest_space->GetMarkBitmap(); + accounting::ContinuousSpaceBitmap* mark_bitmap = promo_dest_space_->GetMarkBitmap(); DCHECK(mark_bitmap != nullptr); DCHECK(!live_bitmap->Test(forward_address)); - if (!whole_heap_collection_) { + if (collect_from_space_only_) { // If collecting the bump pointer spaces only, live_bitmap == mark_bitmap. DCHECK_EQ(live_bitmap, mark_bitmap); @@ -559,12 +533,23 @@ mirror::Object* SemiSpace::MarkNonForwardedObject(mirror::Object* obj) { mark_bitmap->Set(forward_address); } } - DCHECK(forward_address != nullptr); } else { // If it's allocated after the last GC (younger), copy it to the to-space. forward_address = to_space_->AllocThreadUnsafe(self_, object_size, &bytes_allocated, nullptr); + if (forward_address != nullptr && to_space_live_bitmap_ != nullptr) { + to_space_live_bitmap_->Set(forward_address); + } + } + // If it's still null, attempt to use the fallback space. + if (UNLIKELY(forward_address == nullptr)) { + forward_address = fallback_space_->AllocThreadUnsafe(self_, object_size, &bytes_allocated, + nullptr); + CHECK(forward_address != nullptr) << "Out of memory in the to-space and fallback space."; + accounting::ContinuousSpaceBitmap* bitmap = fallback_space_->GetLiveBitmap(); + if (bitmap != nullptr) { + bitmap->Set(forward_address); + } } - CHECK(forward_address != nullptr) << "Out of memory in the to-space."; ++objects_moved_; bytes_moved_ += bytes_allocated; // Copy over the object and add it to the mark stack since we still need to update its @@ -579,11 +564,10 @@ mirror::Object* SemiSpace::MarkNonForwardedObject(mirror::Object* obj) { } forward_address->AssertReadBarrierPointer(); } - if (to_space_live_bitmap_ != nullptr) { - to_space_live_bitmap_->Set(forward_address); - } DCHECK(to_space_->HasAddress(forward_address) || - (generational_ && GetHeap()->GetPrimaryFreeListSpace()->HasAddress(forward_address))); + fallback_space_->HasAddress(forward_address) || + (generational_ && promo_dest_space_->HasAddress(forward_address))) + << forward_address << "\n" << GetHeap()->DumpSpaces(); return forward_address; } @@ -648,7 +632,7 @@ void SemiSpace::SweepSystemWeaks() { } bool SemiSpace::ShouldSweepSpace(space::ContinuousSpace* space) const { - return space != from_space_ && space != to_space_ && !immune_region_.ContainsSpace(space); + return space != from_space_ && space != to_space_; } void SemiSpace::Sweep(bool swap_bitmaps) { @@ -714,22 +698,20 @@ void SemiSpace::ScanObject(Object* obj) { // Scan anything that's on the mark stack. void SemiSpace::ProcessMarkStack() { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - space::MallocSpace* promo_dest_space = nullptr; accounting::ContinuousSpaceBitmap* live_bitmap = nullptr; - if (generational_ && !whole_heap_collection_) { + if (collect_from_space_only_) { // If a bump pointer space only collection (and the promotion is // enabled,) we delay the live-bitmap marking of promoted objects // from MarkObject() until this function. - promo_dest_space = GetHeap()->GetPrimaryFreeListSpace(); - live_bitmap = promo_dest_space->GetLiveBitmap(); + live_bitmap = promo_dest_space_->GetLiveBitmap(); DCHECK(live_bitmap != nullptr); - accounting::ContinuousSpaceBitmap* mark_bitmap = promo_dest_space->GetMarkBitmap(); + accounting::ContinuousSpaceBitmap* mark_bitmap = promo_dest_space_->GetMarkBitmap(); DCHECK(mark_bitmap != nullptr); DCHECK_EQ(live_bitmap, mark_bitmap); } while (!mark_stack_->IsEmpty()) { Object* obj = mark_stack_->PopBack(); - if (generational_ && !whole_heap_collection_ && promo_dest_space->HasAddress(obj)) { + if (collect_from_space_only_ && promo_dest_space_->HasAddress(obj)) { // obj has just been promoted. Mark the live bitmap for it, // which is delayed from MarkObject(). DCHECK(!live_bitmap->Test(obj)); @@ -742,16 +724,12 @@ void SemiSpace::ProcessMarkStack() { inline Object* SemiSpace::GetMarkedForwardAddress(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { // All immune objects are assumed marked. - if (immune_region_.ContainsObject(obj)) { - return obj; - } if (from_space_->HasAddress(obj)) { // Returns either the forwarding address or nullptr. return GetForwardingAddressInFromSpace(obj); - } else if (to_space_->HasAddress(obj)) { - // Should be unlikely. - // Already forwarded, must be marked. - return obj; + } else if (collect_from_space_only_ || immune_region_.ContainsObject(obj) || + to_space_->HasAddress(obj)) { + return obj; // Already forwarded, must be marked. } return mark_bitmap_->Test(obj) ? obj : nullptr; } @@ -777,9 +755,9 @@ void SemiSpace::FinishPhase() { if (generational_) { // Decide whether to do a whole heap collection or a bump pointer // only space collection at the next collection by updating - // whole_heap_collection. - if (!whole_heap_collection_) { - // Enable whole_heap_collection if the bytes promoted since the + // collect_from_space_only_. + if (collect_from_space_only_) { + // Disable collect_from_space_only_ if the bytes promoted since the // last whole heap collection or the large object bytes // allocated exceeds a threshold. bytes_promoted_since_last_whole_heap_collection_ += bytes_promoted_; @@ -792,14 +770,14 @@ void SemiSpace::FinishPhase() { current_los_bytes_allocated >= last_los_bytes_allocated + kLargeObjectBytesAllocatedThreshold; if (bytes_promoted_threshold_exceeded || large_object_bytes_threshold_exceeded) { - whole_heap_collection_ = true; + collect_from_space_only_ = false; } } else { // Reset the counters. bytes_promoted_since_last_whole_heap_collection_ = bytes_promoted_; large_object_bytes_allocated_at_last_whole_heap_collection_ = GetHeap()->GetLargeObjectsSpace()->GetBytesAllocated(); - whole_heap_collection_ = false; + collect_from_space_only_ = true; } } // Clear all of the spaces' mark bitmaps. diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h index bff08478e0..71a83f2624 100644 --- a/runtime/gc/collector/semi_space.h +++ b/runtime/gc/collector/semi_space.h @@ -25,6 +25,7 @@ #include "garbage_collector.h" #include "gc/accounting/heap_bitmap.h" #include "immune_region.h" +#include "mirror/object_reference.h" #include "object_callbacks.h" #include "offsets.h" @@ -243,9 +244,14 @@ class SemiSpace : public GarbageCollector { // large objects were allocated at the last whole heap collection. uint64_t large_object_bytes_allocated_at_last_whole_heap_collection_; - // Used for the generational mode. When true, collect the whole - // heap. When false, collect only the bump pointer spaces. - bool whole_heap_collection_; + // Used for generational mode. When true, we only collect the from_space_. + bool collect_from_space_only_; + + // The space which we are promoting into, only used for GSS. + space::ContinuousMemMapAllocSpace* promo_dest_space_; + + // The space which we copy to if the to_space_ is full. + space::ContinuousMemMapAllocSpace* fallback_space_; // How many objects and bytes we moved, used so that we don't need to Get the size of the // to_space_ when calculating how many objects and bytes we freed. diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h index 530a3c9e72..ef5d56ec97 100644 --- a/runtime/gc/collector_type.h +++ b/runtime/gc/collector_type.h @@ -40,6 +40,9 @@ enum CollectorType { kCollectorTypeHeapTrim, // A (mostly) concurrent copying collector. kCollectorTypeCC, + // A homogeneous space compaction collector used in background transition + // when both foreground and background collector are CMS. + kCollectorTypeHomogeneousSpaceCompact, }; std::ostream& operator<<(std::ostream& os, const CollectorType& collector_type); diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc index 9e73f1499c..f0e1512adf 100644 --- a/runtime/gc/gc_cause.cc +++ b/runtime/gc/gc_cause.cc @@ -31,6 +31,7 @@ const char* PrettyCause(GcCause cause) { case kGcCauseForNativeAlloc: return "NativeAlloc"; case kGcCauseCollectorTransition: return "CollectorTransition"; case kGcCauseDisableMovingGc: return "DisableMovingGc"; + case kGcCauseHomogeneousSpaceCompact: return "HomogeneousSpaceCompact"; case kGcCauseTrim: return "HeapTrim"; default: LOG(FATAL) << "Unreachable"; diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h index 10e6667e11..1f2643a9ac 100644 --- a/runtime/gc/gc_cause.h +++ b/runtime/gc/gc_cause.h @@ -39,6 +39,8 @@ enum GcCause { kGcCauseDisableMovingGc, // Not a real GC cause, used when we trim the heap. kGcCauseTrim, + // GC triggered for background transition when both foreground and background collector are CMS. + kGcCauseHomogeneousSpaceCompact, }; const char* PrettyCause(GcCause cause); diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 58ba61bc24..7d3fd2d23a 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -41,11 +41,11 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Clas const PreFenceVisitor& pre_fence_visitor) { if (kIsDebugBuild) { CheckPreconditionsForAllocObject(klass, byte_count); + // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are + // done in the runnable state where suspension is expected. + CHECK_EQ(self->GetState(), kRunnable); + self->AssertThreadSuspensionIsAllowable(); } - // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are - // done in the runnable state where suspension is expected. - DCHECK_EQ(self->GetState(), kRunnable); - self->AssertThreadSuspensionIsAllowable(); // Need to check that we arent the large object allocator since the large object allocation code // path this function. If we didn't check we would have an infinite loop. if (kCheckLargeObject && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) { @@ -54,49 +54,72 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Clas } mirror::Object* obj; AllocationTimer alloc_timer(this, &obj); - size_t bytes_allocated, usable_size; - obj = TryToAllocate<kInstrumented, false>(self, allocator, byte_count, &bytes_allocated, - &usable_size); - if (UNLIKELY(obj == nullptr)) { - bool is_current_allocator = allocator == GetCurrentAllocator(); - obj = AllocateInternalWithGc(self, allocator, byte_count, &bytes_allocated, &usable_size, - &klass); - if (obj == nullptr) { - bool after_is_current_allocator = allocator == GetCurrentAllocator(); - if (is_current_allocator && !after_is_current_allocator) { - // If the allocator changed, we need to restart the allocation. - return AllocObject<kInstrumented>(self, klass, byte_count, pre_fence_visitor); + size_t bytes_allocated; + size_t usable_size; + size_t new_num_bytes_allocated = 0; + if (allocator == kAllocatorTypeTLAB) { + byte_count = RoundUp(byte_count, space::BumpPointerSpace::kAlignment); + } + // If we have a thread local allocation we don't need to update bytes allocated. + if (allocator == kAllocatorTypeTLAB && byte_count <= self->TlabSize()) { + obj = self->AllocTlab(byte_count); + DCHECK(obj != nullptr) << "AllocTlab can't fail"; + obj->SetClass(klass); + if (kUseBakerOrBrooksReadBarrier) { + if (kUseBrooksReadBarrier) { + obj->SetReadBarrierPointer(obj); } - return nullptr; + obj->AssertReadBarrierPointer(); } - } - DCHECK_GT(bytes_allocated, 0u); - DCHECK_GT(usable_size, 0u); - obj->SetClass(klass); - if (kUseBakerOrBrooksReadBarrier) { - if (kUseBrooksReadBarrier) { - obj->SetReadBarrierPointer(obj); + bytes_allocated = byte_count; + usable_size = bytes_allocated; + pre_fence_visitor(obj, usable_size); + QuasiAtomic::ThreadFenceForConstructor(); + } else { + obj = TryToAllocate<kInstrumented, false>(self, allocator, byte_count, &bytes_allocated, + &usable_size); + if (UNLIKELY(obj == nullptr)) { + bool is_current_allocator = allocator == GetCurrentAllocator(); + obj = AllocateInternalWithGc(self, allocator, byte_count, &bytes_allocated, &usable_size, + &klass); + if (obj == nullptr) { + bool after_is_current_allocator = allocator == GetCurrentAllocator(); + if (is_current_allocator && !after_is_current_allocator) { + // If the allocator changed, we need to restart the allocation. + return AllocObject<kInstrumented>(self, klass, byte_count, pre_fence_visitor); + } + return nullptr; + } } - obj->AssertReadBarrierPointer(); - } - if (collector::SemiSpace::kUseRememberedSet && UNLIKELY(allocator == kAllocatorTypeNonMoving)) { - // (Note this if statement will be constant folded away for the - // fast-path quick entry points.) Because SetClass() has no write - // barrier, if a non-moving space allocation, we need a write - // barrier as the class pointer may point to the bump pointer - // space (where the class pointer is an "old-to-young" reference, - // though rare) under the GSS collector with the remembered set - // enabled. We don't need this for kAllocatorTypeRosAlloc/DlMalloc - // cases because we don't directly allocate into the main alloc - // space (besides promotions) under the SS/GSS collector. - WriteBarrierField(obj, mirror::Object::ClassOffset(), klass); + DCHECK_GT(bytes_allocated, 0u); + DCHECK_GT(usable_size, 0u); + obj->SetClass(klass); + if (kUseBakerOrBrooksReadBarrier) { + if (kUseBrooksReadBarrier) { + obj->SetReadBarrierPointer(obj); + } + obj->AssertReadBarrierPointer(); + } + if (collector::SemiSpace::kUseRememberedSet && UNLIKELY(allocator == kAllocatorTypeNonMoving)) { + // (Note this if statement will be constant folded away for the + // fast-path quick entry points.) Because SetClass() has no write + // barrier, if a non-moving space allocation, we need a write + // barrier as the class pointer may point to the bump pointer + // space (where the class pointer is an "old-to-young" reference, + // though rare) under the GSS collector with the remembered set + // enabled. We don't need this for kAllocatorTypeRosAlloc/DlMalloc + // cases because we don't directly allocate into the main alloc + // space (besides promotions) under the SS/GSS collector. + WriteBarrierField(obj, mirror::Object::ClassOffset(), klass); + } + pre_fence_visitor(obj, usable_size); + new_num_bytes_allocated = + static_cast<size_t>(num_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes_allocated)) + + bytes_allocated; } - pre_fence_visitor(obj, usable_size); if (kIsDebugBuild && Runtime::Current()->IsStarted()) { CHECK_LE(obj->SizeOf(), usable_size); } - const size_t new_num_bytes_allocated = - static_cast<size_t>(num_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes_allocated)) + bytes_allocated; // TODO: Deprecate. if (kInstrumented) { if (Runtime::Current()->HasStatsEnabled()) { @@ -158,7 +181,8 @@ template <const bool kInstrumented, const bool kGrow> inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator_type, size_t alloc_size, size_t* bytes_allocated, size_t* usable_size) { - if (UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size))) { + if (allocator_type != kAllocatorTypeTLAB && + UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size))) { return nullptr; } mirror::Object* ret; @@ -206,18 +230,24 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator break; } case kAllocatorTypeTLAB: { - alloc_size = RoundUp(alloc_size, space::BumpPointerSpace::kAlignment); + DCHECK_ALIGNED(alloc_size, space::BumpPointerSpace::kAlignment); if (UNLIKELY(self->TlabSize() < alloc_size)) { + const size_t new_tlab_size = alloc_size + kDefaultTLABSize; + if (UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, new_tlab_size))) { + return nullptr; + } // Try allocating a new thread local buffer, if the allocaiton fails the space must be // full so return nullptr. - if (!bump_pointer_space_->AllocNewTlab(self, alloc_size + kDefaultTLABSize)) { + if (!bump_pointer_space_->AllocNewTlab(self, new_tlab_size)) { return nullptr; } + *bytes_allocated = new_tlab_size; + } else { + *bytes_allocated = 0; } // The allocation can't fail. ret = self->AllocTlab(alloc_size); DCHECK(ret != nullptr); - *bytes_allocated = alloc_size; *usable_size = alloc_size; break; } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 1e053eba7f..ff7a188f74 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -58,7 +58,6 @@ #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/reference-inl.h" -#include "object_utils.h" #include "os.h" #include "reflection.h" #include "runtime.h" @@ -93,6 +92,10 @@ static constexpr size_t kNonMovingSpaceCapacity = 64 * MB; static constexpr size_t kAllocationStackReserveSize = 1024; // Default mark stack size in bytes. static const size_t kDefaultMarkStackSize = 64 * KB; +// Define space name. +static const char* kDlMallocSpaceName[2] = {"main dlmalloc space", "main dlmalloc space 1"}; +static const char* kRosAllocSpaceName[2] = {"main rosalloc space", "main rosalloc space 1"}; +static const char* kMemMapSpaceName[2] = {"main space", "main space 1"}; Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, double target_utilization, double foreground_heap_growth_multiplier, size_t capacity, @@ -103,7 +106,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max bool ignore_max_footprint, bool use_tlab, bool verify_pre_gc_heap, bool verify_pre_sweeping_heap, bool verify_post_gc_heap, bool verify_pre_gc_rosalloc, bool verify_pre_sweeping_rosalloc, - bool verify_post_gc_rosalloc) + bool verify_post_gc_rosalloc, bool use_homogeneous_space_compaction_for_oom, + uint64_t min_interval_homogeneous_space_compaction_by_oom) : non_moving_space_(nullptr), rosalloc_space_(nullptr), dlmalloc_space_(nullptr), @@ -173,7 +177,11 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max verify_object_mode_(kVerifyObjectModeDisabled), disable_moving_gc_count_(0), running_on_valgrind_(Runtime::Current()->RunningOnValgrind()), - use_tlab_(use_tlab) { + use_tlab_(use_tlab), + main_space_backup_(nullptr), + min_interval_homogeneous_space_compaction_by_oom_(min_interval_homogeneous_space_compaction_by_oom), + last_time_homogeneous_space_compaction_by_oom_(NanoTime()), + use_homogeneous_space_compaction_for_oom_(use_homogeneous_space_compaction_for_oom) { if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Heap() entering"; } @@ -205,30 +213,90 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max CHECK_GT(oat_file_end_addr, image_space->End()); requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize); } + + /* + requested_alloc_space_begin -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + +- nonmoving space (kNonMovingSpaceCapacity) +- + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + +- main alloc space (capacity_) +- + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + +- main alloc space 1 (capacity_) +- + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + */ + bool create_backup_main_space = + background_collector_type == gc::kCollectorTypeHomogeneousSpaceCompact || + use_homogeneous_space_compaction_for_oom; if (is_zygote) { // Reserve the address range before we create the non moving space to make sure bitmaps don't // take it. std::string error_str; - MemMap* mem_map = MemMap::MapAnonymous( - "main space", requested_alloc_space_begin + kNonMovingSpaceCapacity, capacity, + MemMap* main_space_map = MemMap::MapAnonymous( + kMemMapSpaceName[0], requested_alloc_space_begin + kNonMovingSpaceCapacity, capacity_, PROT_READ | PROT_WRITE, true, &error_str); - CHECK(mem_map != nullptr) << error_str; + CHECK(main_space_map != nullptr) << error_str; + MemMap* main_space_1_map = nullptr; + // Attempt to reserve an extra mem_map for homogeneous space compaction right after the main space map. + if (create_backup_main_space) { + main_space_1_map = MemMap::MapAnonymous(kMemMapSpaceName[1], main_space_map->End(), capacity_, + PROT_READ | PROT_WRITE, true, &error_str); + if (main_space_1_map == nullptr) { + LOG(WARNING) << "Failed to create map " << kMemMapSpaceName[1] << " with error " + << error_str; + } + } // Non moving space is always dlmalloc since we currently don't have support for multiple - // rosalloc spaces. + // active rosalloc spaces. non_moving_space_ = space::DlMallocSpace::Create( - "zygote / non moving space", initial_size, kNonMovingSpaceCapacity, kNonMovingSpaceCapacity, - requested_alloc_space_begin, false); + "zygote / non moving space", initial_size, kNonMovingSpaceCapacity, + kNonMovingSpaceCapacity, requested_alloc_space_begin, false); non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity()); - CreateMainMallocSpace(mem_map, initial_size, growth_limit, capacity); + CreateMainMallocSpace(main_space_map, initial_size, growth_limit_, capacity_); + if (main_space_1_map != nullptr) { + const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1]; + main_space_backup_ = CreateMallocSpaceFromMemMap(main_space_1_map, initial_size, + growth_limit_, capacity_, name, true); + } } else { std::string error_str; - MemMap* mem_map = MemMap::MapAnonymous("main/non-moving space", requested_alloc_space_begin, - capacity, PROT_READ | PROT_WRITE, true, &error_str); - CHECK(mem_map != nullptr) << error_str; + byte* request_begin = requested_alloc_space_begin; + if (request_begin == nullptr) { + // Disable homogeneous space compaction since we don't have an image. + create_backup_main_space = false; + } + MemMap* main_space_1_map = nullptr; + if (create_backup_main_space) { + request_begin += kNonMovingSpaceCapacity; + // Attempt to reserve an extra mem_map for homogeneous space compaction right after the main space map. + main_space_1_map = MemMap::MapAnonymous(kMemMapSpaceName[1], request_begin + capacity_, + capacity_, PROT_READ | PROT_WRITE, true, &error_str); + if (main_space_1_map == nullptr) { + LOG(WARNING) << "Failed to create map " << kMemMapSpaceName[1] << " with error " + << error_str; + request_begin = requested_alloc_space_begin; + } + } + MemMap* main_space_map = MemMap::MapAnonymous(kMemMapSpaceName[0], request_begin, capacity_, + PROT_READ | PROT_WRITE, true, &error_str); + CHECK(main_space_map != nullptr) << error_str; + // Introduce a seperate non moving space. + if (main_space_1_map != nullptr) { + // Do this before creating the main malloc space to prevent bitmaps from being placed here. + non_moving_space_ = space::DlMallocSpace::Create( + "non moving space", kDefaultInitialSize, kNonMovingSpaceCapacity, kNonMovingSpaceCapacity, + requested_alloc_space_begin, false); + non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity()); + } // Create the main free list space, which doubles as the non moving space. We can do this since // non zygote means that we won't have any background compaction. - CreateMainMallocSpace(mem_map, initial_size, growth_limit, capacity); - non_moving_space_ = main_space_; + CreateMainMallocSpace(main_space_map, initial_size, growth_limit_, capacity_); + if (main_space_1_map != nullptr) { + const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1]; + main_space_backup_ = CreateMallocSpaceFromMemMap(main_space_1_map, initial_size, + growth_limit_, capacity_, name, true); + CHECK(main_space_backup_ != nullptr); + } else { + non_moving_space_ = main_space_; + } } CHECK(non_moving_space_ != nullptr); @@ -240,7 +308,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max (IsMovingGc(foreground_collector_type_) || IsMovingGc(background_collector_type_))) { // TODO: Place bump-pointer spaces somewhere to minimize size of card table. // Divide by 2 for a temporary fix for reducing virtual memory usage. - const size_t bump_pointer_space_capacity = capacity / 2; + const size_t bump_pointer_space_capacity = capacity_ / 2; bump_pointer_space_ = space::BumpPointerSpace::Create("Bump pointer space", bump_pointer_space_capacity, nullptr); CHECK(bump_pointer_space_ != nullptr) << "Failed to create bump pointer space"; @@ -253,13 +321,25 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max if (non_moving_space_ != main_space_) { AddSpace(non_moving_space_); } + if (main_space_backup_ != nullptr) { + AddSpace(main_space_backup_); + } else { + const char* disable_msg = "Disabling homogenous space compact due to no backup main space"; + if (background_collector_type_ == gc::kCollectorTypeHomogeneousSpaceCompact) { + background_collector_type_ = collector_type_; + LOG(WARNING) << disable_msg; + } else if (use_homogeneous_space_compaction_for_oom_) { + LOG(WARNING) << disable_msg; + } + use_homogeneous_space_compaction_for_oom_ = false; + } if (main_space_ != nullptr) { AddSpace(main_space_); } // Allocate the large object space. if (kUseFreeListSpaceForLOS) { - large_object_space_ = space::FreeListSpace::Create("large object space", nullptr, capacity); + large_object_space_ = space::FreeListSpace::Create("large object space", nullptr, capacity_); } else { large_object_space_ = space::LargeObjectMapSpace::Create("large object space"); } @@ -328,7 +408,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } if (kMovingCollector) { // TODO: Clean this up. - bool generational = foreground_collector_type_ == kCollectorTypeGSS; + const bool generational = foreground_collector_type_ == kCollectorTypeGSS; semi_space_collector_ = new collector::SemiSpace(this, generational, generational ? "generational" : ""); garbage_collectors_.push_back(semi_space_collector_); @@ -339,9 +419,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } if (GetImageSpace() != nullptr && main_space_ != nullptr) { - // Check that there's no gap between the image space and the main - // space so that the immune region won't break (eg. due to a large - // object allocated in the gap). + // Check that there's no gap between the image space and the main space so that the immune + // region won't break (eg. due to a large object allocated in the gap). bool no_gap = MemMap::CheckNoGaps(GetImageSpace()->GetMemMap(), main_space_->GetMemMap()); if (!no_gap) { MemMap::DumpMaps(LOG(ERROR)); @@ -358,11 +437,36 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } } +space::MallocSpace* Heap::CreateMallocSpaceFromMemMap(MemMap* mem_map, size_t initial_size, + size_t growth_limit, size_t capacity, + const char* name, bool can_move_objects) { + space::MallocSpace* malloc_space = nullptr; + if (kUseRosAlloc) { + // Create rosalloc space. + malloc_space = space::RosAllocSpace::CreateFromMemMap(mem_map, name, kDefaultStartingSize, + initial_size, growth_limit, capacity, + low_memory_mode_, can_move_objects); + } else { + malloc_space = space::DlMallocSpace::CreateFromMemMap(mem_map, name, kDefaultStartingSize, + initial_size, growth_limit, capacity, + can_move_objects); + } + if (collector::SemiSpace::kUseRememberedSet) { + accounting::RememberedSet* rem_set = + new accounting::RememberedSet(std::string(name) + " remembered set", this, malloc_space); + CHECK(rem_set != nullptr) << "Failed to create main space remembered set"; + AddRememberedSet(rem_set); + } + CHECK(malloc_space != nullptr) << "Failed to create " << name; + malloc_space->SetFootprintLimit(malloc_space->Capacity()); + return malloc_space; +} + void Heap::CreateMainMallocSpace(MemMap* mem_map, size_t initial_size, size_t growth_limit, size_t capacity) { // Is background compaction is enabled? bool can_move_objects = IsMovingGc(background_collector_type_) != - IsMovingGc(foreground_collector_type_); + IsMovingGc(foreground_collector_type_) || use_homogeneous_space_compaction_for_oom_; // If we are the zygote and don't yet have a zygote space, it means that the zygote fork will // happen in the future. If this happens and we have kCompactZygote enabled we wish to compact // from the main space to the zygote space. If background compaction is enabled, always pass in @@ -375,26 +479,10 @@ void Heap::CreateMainMallocSpace(MemMap* mem_map, size_t initial_size, size_t gr if (collector::SemiSpace::kUseRememberedSet && main_space_ != nullptr) { RemoveRememberedSet(main_space_); } - if (kUseRosAlloc) { - rosalloc_space_ = space::RosAllocSpace::CreateFromMemMap( - mem_map, "main rosalloc space", kDefaultStartingSize, initial_size, growth_limit, capacity, - low_memory_mode_, can_move_objects); - main_space_ = rosalloc_space_; - CHECK(main_space_ != nullptr) << "Failed to create rosalloc space"; - } else { - dlmalloc_space_ = space::DlMallocSpace::CreateFromMemMap( - mem_map, "main dlmalloc space", kDefaultStartingSize, initial_size, growth_limit, capacity, - can_move_objects); - main_space_ = dlmalloc_space_; - CHECK(main_space_ != nullptr) << "Failed to create dlmalloc space"; - } - main_space_->SetFootprintLimit(main_space_->Capacity()); - if (collector::SemiSpace::kUseRememberedSet) { - accounting::RememberedSet* main_space_rem_set = - new accounting::RememberedSet("Main space remembered set", this, main_space_); - CHECK(main_space_rem_set != nullptr) << "Failed to create main space remembered set"; - AddRememberedSet(main_space_rem_set); - } + const char* name = kUseRosAlloc ? kRosAllocSpaceName[0] : kDlMallocSpaceName[0]; + main_space_ = CreateMallocSpaceFromMemMap(mem_map, initial_size, growth_limit, capacity, name, + can_move_objects); + SetSpaceAsDefault(main_space_); VLOG(heap) << "Created main space " << main_space_; } @@ -547,8 +635,11 @@ void Heap::UpdateProcessState(ProcessState process_state) { RequestCollectorTransition(foreground_collector_type_, 0); } else { // Don't delay for debug builds since we may want to stress test the GC. - RequestCollectorTransition(background_collector_type_, kIsDebugBuild ? 0 : - kCollectorTransitionWait); + // If background_collector_type_ is kCollectorTypeHomogeneousSpaceCompact then we have + // special handling which does a homogenous space compaction once but then doesn't transition + // the collector. + RequestCollectorTransition(background_collector_type_, + kIsDebugBuild ? 0 : kCollectorTransitionWait); } } } @@ -605,7 +696,7 @@ void Heap::DeleteThreadPool() { } void Heap::AddSpace(space::Space* space) { - DCHECK(space != nullptr); + CHECK(space != nullptr); WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); if (space->IsContinuousSpace()) { DCHECK(!space->IsDiscontinuousSpace()); @@ -801,37 +892,23 @@ space::ImageSpace* Heap::GetImageSpace() const { return NULL; } -static void MSpaceChunkCallback(void* start, void* end, size_t used_bytes, void* arg) { - size_t chunk_size = reinterpret_cast<uint8_t*>(end) - reinterpret_cast<uint8_t*>(start); - if (used_bytes < chunk_size) { - size_t chunk_free_bytes = chunk_size - used_bytes; - size_t& max_contiguous_allocation = *reinterpret_cast<size_t*>(arg); - max_contiguous_allocation = std::max(max_contiguous_allocation, chunk_free_bytes); - } -} - -void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation) { +void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) { std::ostringstream oss; size_t total_bytes_free = GetFreeMemory(); oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free << " free bytes"; // If the allocation failed due to fragmentation, print out the largest continuous allocation. - if (!large_object_allocation && total_bytes_free >= byte_count) { - size_t max_contiguous_allocation = 0; - for (const auto& space : continuous_spaces_) { - if (space->IsMallocSpace()) { - // To allow the Walk/InspectAll() to exclusively-lock the mutator - // lock, temporarily release the shared access to the mutator - // lock here by transitioning to the suspended state. - Locks::mutator_lock_->AssertSharedHeld(self); - self->TransitionFromRunnableToSuspended(kSuspended); - space->AsMallocSpace()->Walk(MSpaceChunkCallback, &max_contiguous_allocation); - self->TransitionFromSuspendedToRunnable(); - Locks::mutator_lock_->AssertSharedHeld(self); - } + if (total_bytes_free >= byte_count) { + space::MallocSpace* space = nullptr; + if (allocator_type == kAllocatorTypeNonMoving) { + space = non_moving_space_; + } else if (allocator_type == kAllocatorTypeRosAlloc || + allocator_type == kAllocatorTypeDlMalloc) { + space = main_space_; + } + if (space != nullptr) { + space->LogFragmentationAllocFailure(oss, byte_count); } - oss << "; failed due to fragmentation (largest possible contiguous allocation " - << max_contiguous_allocation << " bytes)"; } self->ThrowOutOfMemoryError(oss.str().c_str()); } @@ -854,6 +931,15 @@ void Heap::DoPendingTransitionOrTrim() { ScopedThreadStateChange tsc(self, kSleeping); usleep(wait_time / 1000); // Usleep takes microseconds. } + // Launch homogeneous space compaction if it is desired. + if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) { + if (!CareAboutPauseTimes()) { + PerformHomogeneousSpaceCompact(); + } + // No need to Trim(). Homogeneous space compaction may free more virtual and physical memory. + desired_collector_type = collector_type_; + return; + } // Transition the collector if the desired collector type is not the same as the current // collector type. TransitionCollector(desired_collector_type); @@ -1035,7 +1121,13 @@ bool Heap::IsLiveObjectLocked(mirror::Object* obj, bool search_allocation_stack, return false; } -void Heap::DumpSpaces(std::ostream& stream) { +std::string Heap::DumpSpaces() const { + std::ostringstream oss; + DumpSpaces(oss); + return oss.str(); +} + +void Heap::DumpSpaces(std::ostream& stream) const { for (const auto& space : continuous_spaces_) { accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap(); accounting::ContinuousSpaceBitmap* mark_bitmap = space->GetMarkBitmap(); @@ -1065,10 +1157,7 @@ void Heap::VerifyObjectBody(mirror::Object* obj) { if (verify_object_mode_ > kVerifyObjectModeFast) { // Note: the bitmap tests below are racy since we don't hold the heap bitmap lock. - if (!IsLiveObjectLocked(obj)) { - DumpSpaces(); - LOG(FATAL) << "Object is dead: " << obj; - } + CHECK(IsLiveObjectLocked(obj)) << "Object is dead " << obj << "\n" << DumpSpaces(); } } @@ -1099,6 +1188,17 @@ void Heap::RecordFree(uint64_t freed_objects, int64_t freed_bytes) { } } +space::RosAllocSpace* Heap::GetRosAllocSpace(gc::allocator::RosAlloc* rosalloc) const { + for (const auto& space : continuous_spaces_) { + if (space->AsContinuousSpace()->IsRosAllocSpace()) { + if (space->AsContinuousSpace()->AsRosAllocSpace()->GetRosAlloc() == rosalloc) { + return space->AsContinuousSpace()->AsRosAllocSpace(); + } + } + } + return nullptr; +} + mirror::Object* Heap::AllocateInternalWithGc(Thread* self, AllocatorType allocator, size_t alloc_size, size_t* bytes_allocated, size_t* usable_size, @@ -1180,8 +1280,46 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, AllocatorType allocat return nullptr; } ptr = TryToAllocate<true, true>(self, allocator, alloc_size, bytes_allocated, usable_size); + if (ptr == nullptr && use_homogeneous_space_compaction_for_oom_) { + const uint64_t current_time = NanoTime(); + if ((allocator == kAllocatorTypeRosAlloc || allocator == kAllocatorTypeDlMalloc) && + current_time - last_time_homogeneous_space_compaction_by_oom_ > + min_interval_homogeneous_space_compaction_by_oom_) { + last_time_homogeneous_space_compaction_by_oom_ = current_time; + HomogeneousSpaceCompactResult result = PerformHomogeneousSpaceCompact(); + switch (result) { + case HomogeneousSpaceCompactResult::kSuccess: + // If the allocation succeeded, we delayed an oom. + ptr = TryToAllocate<true, true>(self, allocator, alloc_size, bytes_allocated, usable_size); + if (ptr != nullptr) { + count_delayed_oom_++; + } + break; + case HomogeneousSpaceCompactResult::kErrorReject: + // Reject due to disabled moving GC. + break; + case HomogeneousSpaceCompactResult::kErrorVMShuttingDown: + // Throw OOM by default. + break; + default: { + LOG(FATAL) << "Unimplemented homogeneous space compaction result " << static_cast<size_t>(result); + } + } + // Always print that we ran homogeneous space compation since this can cause jank. + VLOG(heap) << "Ran heap homogeneous space compaction, " + << " requested defragmentation " + << count_requested_homogeneous_space_compaction_.LoadSequentiallyConsistent() + << " performed defragmentation " + << count_performed_homogeneous_space_compaction_.LoadSequentiallyConsistent() + << " ignored homogeneous space compaction " + << count_ignored_homogeneous_space_compaction_.LoadSequentiallyConsistent() + << " delayed count = " + << count_delayed_oom_.LoadSequentiallyConsistent(); + } + } + // If the allocation hasn't succeeded by this point, throw an OOM error. if (ptr == nullptr) { - ThrowOutOfMemoryError(self, alloc_size, allocator == kAllocatorTypeLOS); + ThrowOutOfMemoryError(self, alloc_size, allocator); } return ptr; } @@ -1338,6 +1476,66 @@ void Heap::CollectGarbage(bool clear_soft_references) { CollectGarbageInternal(gc_plan_.back(), kGcCauseExplicit, clear_soft_references); } +HomogeneousSpaceCompactResult Heap::PerformHomogeneousSpaceCompact() { + Thread* self = Thread::Current(); + // Inc requested homogeneous space compaction. + count_requested_homogeneous_space_compaction_++; + // Store performed homogeneous space compaction at a new request arrival. + ThreadList* tl = Runtime::Current()->GetThreadList(); + ScopedThreadStateChange tsc(self, kWaitingPerformingGc); + Locks::mutator_lock_->AssertNotHeld(self); + { + ScopedThreadStateChange tsc(self, kWaitingForGcToComplete); + MutexLock mu(self, *gc_complete_lock_); + // Ensure there is only one GC at a time. + WaitForGcToCompleteLocked(kGcCauseHomogeneousSpaceCompact, self); + // Homogeneous space compaction is a copying transition, can't run it if the moving GC disable count + // is non zero. + // If the collecotr type changed to something which doesn't benefit from homogeneous space compaction, + // exit. + if (disable_moving_gc_count_ != 0 || IsMovingGc(collector_type_)) { + return HomogeneousSpaceCompactResult::kErrorReject; + } + collector_type_running_ = kCollectorTypeHomogeneousSpaceCompact; + } + if (Runtime::Current()->IsShuttingDown(self)) { + // Don't allow heap transitions to happen if the runtime is shutting down since these can + // cause objects to get finalized. + FinishGC(self, collector::kGcTypeNone); + return HomogeneousSpaceCompactResult::kErrorVMShuttingDown; + } + // Suspend all threads. + tl->SuspendAll(); + uint64_t start_time = NanoTime(); + // Launch compaction. + space::MallocSpace* to_space = main_space_backup_; + space::MallocSpace* from_space = main_space_; + to_space->GetMemMap()->Protect(PROT_READ | PROT_WRITE); + const uint64_t space_size_before_compaction = from_space->Size(); + Compact(to_space, from_space, kGcCauseHomogeneousSpaceCompact); + // Leave as prot read so that we can still run ROSAlloc verification on this space. + from_space->GetMemMap()->Protect(PROT_READ); + const uint64_t space_size_after_compaction = to_space->Size(); + std::swap(main_space_, main_space_backup_); + SetSpaceAsDefault(main_space_); // Set as default to reset the proper dlmalloc space. + // Update performed homogeneous space compaction count. + count_performed_homogeneous_space_compaction_++; + // Print statics log and resume all threads. + uint64_t duration = NanoTime() - start_time; + LOG(INFO) << "Heap homogeneous space compaction took " << PrettyDuration(duration) << " size: " + << PrettySize(space_size_before_compaction) << " -> " + << PrettySize(space_size_after_compaction) << " compact-ratio: " + << std::fixed << static_cast<double>(space_size_after_compaction) / + static_cast<double>(space_size_before_compaction); + tl->ResumeAll(); + // Finish GC. + reference_processor_.EnqueueClearedReferences(self); + GrowForUtilization(semi_space_collector_); + FinishGC(self, collector::kGcTypeFull); + return HomogeneousSpaceCompactResult::kSuccess; +} + + void Heap::TransitionCollector(CollectorType collector_type) { if (collector_type == collector_type_) { return; @@ -1392,7 +1590,7 @@ void Heap::TransitionCollector(CollectorType collector_type) { // We are transitioning from non moving GC -> moving GC, since we copied from the bump // pointer space last transition it will be protected. bump_pointer_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); - Compact(bump_pointer_space_, main_space_); + Compact(bump_pointer_space_, main_space_, kGcCauseCollectorTransition); // Remove the main space so that we don't try to trim it, this doens't work for debug // builds since RosAlloc attempts to read the magic number from a protected page. RemoveSpace(main_space_); @@ -1406,7 +1604,7 @@ void Heap::TransitionCollector(CollectorType collector_type) { // Compact to the main space from the bump pointer space, don't need to swap semispaces. AddSpace(main_space_); main_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); - Compact(main_space_, bump_pointer_space_); + Compact(main_space_, bump_pointer_space_, kGcCauseCollectorTransition); } break; } @@ -1732,14 +1930,15 @@ void Heap::SwapSemiSpaces() { } void Heap::Compact(space::ContinuousMemMapAllocSpace* target_space, - space::ContinuousMemMapAllocSpace* source_space) { + space::ContinuousMemMapAllocSpace* source_space, + GcCause gc_cause) { CHECK(kMovingCollector); if (target_space != source_space) { // Don't swap spaces since this isn't a typical semi space collection. semi_space_collector_->SetSwapSemiSpaces(false); semi_space_collector_->SetFromSpace(source_space); semi_space_collector_->SetToSpace(target_space); - semi_space_collector_->Run(kGcCauseCollectorTransition, false); + semi_space_collector_->Run(gc_cause, false); } else { CHECK(target_space->IsBumpPointerSpace()) << "In-place compaction is only supported for bump pointer spaces"; @@ -2150,7 +2349,7 @@ size_t Heap::VerifyHeapReferences(bool verify_referents) { accounting::RememberedSet* remembered_set = table_pair.second; remembered_set->Dump(LOG(ERROR) << remembered_set->GetName() << ": "); } - DumpSpaces(); + DumpSpaces(LOG(ERROR)); } return visitor.GetFailureCount(); } @@ -2267,12 +2466,7 @@ bool Heap::VerifyMissingCardMarks() { visitor(*it); } } - - if (visitor.Failed()) { - DumpSpaces(); - return false; - } - return true; + return !visitor.Failed(); } void Heap::SwapStacks(Thread* self) { @@ -2370,9 +2564,8 @@ void Heap::PreGcVerificationPaused(collector::GarbageCollector* gc) { ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); SwapStacks(self); // Sort the live stack so that we can quickly binary search it later. - if (!VerifyMissingCardMarks()) { - LOG(FATAL) << "Pre " << gc->GetName() << " missing card mark verification failed"; - } + CHECK(VerifyMissingCardMarks()) << "Pre " << gc->GetName() + << " missing card mark verification failed\n" << DumpSpaces(); SwapStacks(self); } if (verify_mod_union_table_) { diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index c9ea03e45c..0da113f2c6 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -72,6 +72,10 @@ namespace collector { class SemiSpace; } // namespace collector +namespace allocator { + class RosAlloc; +} // namespace allocator + namespace space { class AllocSpace; class BumpPointerSpace; @@ -97,6 +101,15 @@ class AgeCardVisitor { } }; +enum HomogeneousSpaceCompactResult { + // Success. + kSuccess, + // Reject due to disabled moving GC. + kErrorReject, + // System is shutting down. + kErrorVMShuttingDown, +}; + // If true, use rosalloc/RosAllocSpace instead of dlmalloc/DlMallocSpace static constexpr bool kUseRosAlloc = true; @@ -120,7 +133,7 @@ class Heap { static constexpr size_t kDefaultStartingSize = kPageSize; static constexpr size_t kDefaultInitialSize = 2 * MB; - static constexpr size_t kDefaultMaximumSize = 32 * MB; + static constexpr size_t kDefaultMaximumSize = 256 * MB; static constexpr size_t kDefaultMaxFree = 2 * MB; static constexpr size_t kDefaultMinFree = kDefaultMaxFree / 4; static constexpr size_t kDefaultLongPauseLogThreshold = MsToNs(5); @@ -151,7 +164,8 @@ class Heap { bool ignore_max_footprint, bool use_tlab, bool verify_pre_gc_heap, bool verify_pre_sweeping_heap, bool verify_post_gc_heap, bool verify_pre_gc_rosalloc, bool verify_pre_sweeping_rosalloc, - bool verify_post_gc_rosalloc); + bool verify_post_gc_rosalloc, bool use_homogeneous_space_compaction, + uint64_t min_interval_homogeneous_space_compaction_by_oom); ~Heap(); @@ -194,7 +208,6 @@ class Heap { void CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void ThrowOutOfMemoryError(size_t byte_count, bool large_object_allocation); void RegisterNativeAllocation(JNIEnv* env, int bytes); void RegisterNativeFree(JNIEnv* env, int bytes); @@ -500,6 +513,9 @@ class Heap { return rosalloc_space_; } + // Return the corresponding rosalloc space. + space::RosAllocSpace* GetRosAllocSpace(gc::allocator::RosAlloc* rosalloc) const; + space::MallocSpace* GetNonMovingSpace() const { return non_moving_space_; } @@ -523,7 +539,8 @@ class Heap { } } - void DumpSpaces(std::ostream& stream = LOG(INFO)); + std::string DumpSpaces() const WARN_UNUSED; + void DumpSpaces(std::ostream& stream) const; // Dump object should only be used by the signal handler. void DumpObject(std::ostream& stream, mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS; @@ -569,12 +586,19 @@ class Heap { } private: + // Compact source space to target space. void Compact(space::ContinuousMemMapAllocSpace* target_space, - space::ContinuousMemMapAllocSpace* source_space) + space::ContinuousMemMapAllocSpace* source_space, + GcCause gc_cause) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); void FinishGC(Thread* self, collector::GcType gc_type) LOCKS_EXCLUDED(gc_complete_lock_); + bool SupportHSpaceCompaction() const { + // Returns true if we can do hspace compaction. + return main_space_backup_ != nullptr; + } + static ALWAYS_INLINE bool AllocatorHasAllocationStack(AllocatorType allocator_type) { return allocator_type != kAllocatorTypeBumpPointer && @@ -585,7 +609,8 @@ class Heap { } static bool IsMovingGc(CollectorType collector_type) { return collector_type == kCollectorTypeSS || collector_type == kCollectorTypeGSS || - collector_type == kCollectorTypeCC || collector_type == kCollectorTypeMC; + collector_type == kCollectorTypeCC || collector_type == kCollectorTypeMC || + collector_type == kCollectorTypeHomogeneousSpaceCompact; } bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -628,7 +653,7 @@ class Heap { size_t* usable_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation) + void ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template <bool kGrow> @@ -683,10 +708,18 @@ class Heap { // Find a collector based on GC type. collector::GarbageCollector* FindCollectorByGcType(collector::GcType gc_type); - // Create the main free list space, typically either a RosAlloc space or DlMalloc space. + // Create a new alloc space and compact default alloc space to it. + HomogeneousSpaceCompactResult PerformHomogeneousSpaceCompact(); + + // Create the main free list malloc space, either a RosAlloc space or DlMalloc space. void CreateMainMallocSpace(MemMap* mem_map, size_t initial_size, size_t growth_limit, size_t capacity); + // Create a malloc space based on a mem map. Does not set the space as default. + space::MallocSpace* CreateMallocSpaceFromMemMap(MemMap* mem_map, size_t initial_size, + size_t growth_limit, size_t capacity, + const char* name, bool can_move_objects); + // Given the current contents of the alloc space, increase the allowed heap footprint to match // the target utilization ratio. This should only be called immediately after a full garbage // collection. @@ -973,6 +1006,30 @@ class Heap { const bool running_on_valgrind_; const bool use_tlab_; + // Pointer to the space which becomes the new main space when we do homogeneous space compaction. + space::MallocSpace* main_space_backup_; + + // Minimal interval allowed between two homogeneous space compactions caused by OOM. + uint64_t min_interval_homogeneous_space_compaction_by_oom_; + + // Times of the last homogeneous space compaction caused by OOM. + uint64_t last_time_homogeneous_space_compaction_by_oom_; + + // Saved OOMs by homogeneous space compaction. + Atomic<size_t> count_delayed_oom_; + + // Count for requested homogeneous space compaction. + Atomic<size_t> count_requested_homogeneous_space_compaction_; + + // Count for ignored homogeneous space compaction. + Atomic<size_t> count_ignored_homogeneous_space_compaction_; + + // Count for performed homogeneous space compaction. + Atomic<size_t> count_performed_homogeneous_space_compaction_; + + // Whether or not we use homogeneous space compaction to avoid OOM errors. + bool use_homogeneous_space_compaction_for_oom_; + friend class collector::GarbageCollector; friend class collector::MarkCompact; friend class collector::MarkSweep; diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc index 4176f4ad08..e6b5c75f4f 100644 --- a/runtime/gc/heap_test.cc +++ b/runtime/gc/heap_test.cc @@ -17,10 +17,11 @@ #include "common_runtime_test.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/space_bitmap-inl.h" +#include "handle_scope-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" -#include "handle_scope-inl.h" +#include "scoped_thread_state_change.h" namespace art { namespace gc { diff --git a/runtime/gc/reference_processor-inl.h b/runtime/gc/reference_processor-inl.h new file mode 100644 index 0000000000..f619a15f74 --- /dev/null +++ b/runtime/gc/reference_processor-inl.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_REFERENCE_PROCESSOR_INL_H_ +#define ART_RUNTIME_GC_REFERENCE_PROCESSOR_INL_H_ + +#include "reference_processor.h" + +namespace art { +namespace gc { + +inline bool ReferenceProcessor::SlowPathEnabled() { + return mirror::Reference::GetJavaLangRefReference()->GetSlowPathEnabled(); +} + +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_REFERENCE_PROCESSOR_INL_H_ diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index e52bc1fd1e..d3641d196f 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -17,7 +17,9 @@ #include "reference_processor.h" #include "mirror/object-inl.h" +#include "mirror/reference.h" #include "mirror/reference-inl.h" +#include "reference_processor-inl.h" #include "reflection.h" #include "ScopedLocalRef.h" #include "scoped_thread_state_change.h" @@ -27,18 +29,17 @@ namespace art { namespace gc { ReferenceProcessor::ReferenceProcessor() - : process_references_args_(nullptr, nullptr, nullptr), slow_path_enabled_(false), + : process_references_args_(nullptr, nullptr, nullptr), preserving_references_(false), lock_("reference processor lock", kReferenceProcessorLock), condition_("reference processor condition", lock_) { } void ReferenceProcessor::EnableSlowPath() { - Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); - slow_path_enabled_ = true; + mirror::Reference::GetJavaLangRefReference()->SetSlowPath(true); } void ReferenceProcessor::DisableSlowPath(Thread* self) { - slow_path_enabled_ = false; + mirror::Reference::GetJavaLangRefReference()->SetSlowPath(false); condition_.Broadcast(self); } @@ -46,11 +47,11 @@ mirror::Object* ReferenceProcessor::GetReferent(Thread* self, mirror::Reference* mirror::Object* const referent = reference->GetReferent(); // If the referent is null then it is already cleared, we can just return null since there is no // scenario where it becomes non-null during the reference processing phase. - if (LIKELY(!slow_path_enabled_) || referent == nullptr) { + if (UNLIKELY(!SlowPathEnabled()) || referent == nullptr) { return referent; } MutexLock mu(self, lock_); - while (slow_path_enabled_) { + while (SlowPathEnabled()) { mirror::HeapReference<mirror::Object>* const referent_addr = reference->GetReferentReferenceAddr(); // If the referent became cleared, return it. Don't need barrier since thread roots can't get @@ -117,7 +118,7 @@ void ReferenceProcessor::ProcessReferences(bool concurrent, TimingLogger* timing process_references_args_.is_marked_callback_ = is_marked_callback; process_references_args_.mark_callback_ = mark_object_callback; process_references_args_.arg_ = arg; - CHECK_EQ(slow_path_enabled_, concurrent) << "Slow path must be enabled iff concurrent"; + CHECK_EQ(SlowPathEnabled(), concurrent) << "Slow path must be enabled iff concurrent"; } // Unless required to clear soft references with white references, preserve some white referents. if (!clear_soft_references) { @@ -182,7 +183,7 @@ void ReferenceProcessor::DelayReferenceReferent(mirror::Class* klass, mirror::Re void* arg) { // klass can be the class of the old object if the visitor already updated the class of ref. DCHECK(klass != nullptr); - DCHECK(klass->IsReferenceClass()); + DCHECK(klass->IsTypeOfReferenceClass()); mirror::HeapReference<mirror::Object>* referent = ref->GetReferentReferenceAddr(); if (referent->AsMirrorPtr() != nullptr && !is_marked_callback(referent, arg)) { Thread* self = Thread::Current(); diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h index 2771ea809b..7274457a5d 100644 --- a/runtime/gc/reference_processor.h +++ b/runtime/gc/reference_processor.h @@ -49,6 +49,7 @@ class ReferenceProcessor { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) LOCKS_EXCLUDED(lock_); + // The slow path bool is contained in the reference class object, can only be set once // Only allow setting this with mutators suspended so that we can avoid using a lock in the // GetReferent fast path as an optimization. void EnableSlowPath() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -60,7 +61,7 @@ class ReferenceProcessor { IsHeapReferenceMarkedCallback* is_marked_callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void UpdateRoots(IsMarkedCallback* callback, void* arg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); private: class ProcessReferencesArgs { @@ -75,8 +76,10 @@ class ReferenceProcessor { MarkObjectCallback* mark_callback_; void* arg_; }; + bool SlowPathEnabled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Called by ProcessReferences. - void DisableSlowPath(Thread* self) EXCLUSIVE_LOCKS_REQUIRED(lock_); + void DisableSlowPath(Thread* self) EXCLUSIVE_LOCKS_REQUIRED(lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // If we are preserving references it means that some dead objects may become live, we use start // and stop preserving to block mutators using GetReferrent from getting access to these // referents. @@ -84,8 +87,6 @@ class ReferenceProcessor { void StopPreservingReferences(Thread* self) LOCKS_EXCLUDED(lock_); // Process args, used by the GetReferent to return referents which are already marked. ProcessReferencesArgs process_references_args_ GUARDED_BY(lock_); - // Boolean for whether or not we need to go slow path in GetReferent. - volatile bool slow_path_enabled_; // Boolean for whether or not we are preserving references (either soft references or finalizers). // If this is true, then we cannot return a referent (see comment in GetReferent). bool preserving_references_ GUARDED_BY(lock_); diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 5123e4787d..456d1b31e2 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -304,6 +304,30 @@ void DlMallocSpace::CheckMoreCoreForPrecondition() { } #endif +static void MSpaceChunkCallback(void* start, void* end, size_t used_bytes, void* arg) { + size_t chunk_size = reinterpret_cast<uint8_t*>(end) - reinterpret_cast<uint8_t*>(start); + if (used_bytes < chunk_size) { + size_t chunk_free_bytes = chunk_size - used_bytes; + size_t& max_contiguous_allocation = *reinterpret_cast<size_t*>(arg); + max_contiguous_allocation = std::max(max_contiguous_allocation, chunk_free_bytes); + } +} + +void DlMallocSpace::LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) { + Thread* self = Thread::Current(); + size_t max_contiguous_allocation = 0; + // To allow the Walk/InspectAll() to exclusively-lock the mutator + // lock, temporarily release the shared access to the mutator + // lock here by transitioning to the suspended state. + Locks::mutator_lock_->AssertSharedHeld(self); + self->TransitionFromRunnableToSuspended(kSuspended); + Walk(MSpaceChunkCallback, &max_contiguous_allocation); + self->TransitionFromSuspendedToRunnable(); + Locks::mutator_lock_->AssertSharedHeld(self); + os << "; failed due to fragmentation (largest possible contiguous allocation " + << max_contiguous_allocation << " bytes)"; +} + } // namespace space } // namespace gc } // namespace art diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h index accd26bd21..7aff14b665 100644 --- a/runtime/gc/space/dlmalloc_space.h +++ b/runtime/gc/space/dlmalloc_space.h @@ -124,6 +124,9 @@ class DlMallocSpace : public MallocSpace { return this; } + void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + protected: DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end, byte* limit, size_t growth_limit, bool can_move_objects, size_t starting_size, diff --git a/runtime/gc/space/dlmalloc_space_base_test.cc b/runtime/gc/space/dlmalloc_space_base_test.cc index 129eace199..02fc4a52e1 100644 --- a/runtime/gc/space/dlmalloc_space_base_test.cc +++ b/runtime/gc/space/dlmalloc_space_base_test.cc @@ -15,7 +15,9 @@ */ #include "space_test.h" + #include "dlmalloc_space.h" +#include "scoped_thread_state_change.h" namespace art { namespace gc { diff --git a/runtime/gc/space/dlmalloc_space_random_test.cc b/runtime/gc/space/dlmalloc_space_random_test.cc index c4f8baedfb..4b1a1b1cc0 100644 --- a/runtime/gc/space/dlmalloc_space_random_test.cc +++ b/runtime/gc/space/dlmalloc_space_random_test.cc @@ -15,6 +15,7 @@ */ #include "space_test.h" + #include "dlmalloc_space.h" namespace art { diff --git a/runtime/gc/space/dlmalloc_space_static_test.cc b/runtime/gc/space/dlmalloc_space_static_test.cc index edaa1988ab..d17d0a7d54 100644 --- a/runtime/gc/space/dlmalloc_space_static_test.cc +++ b/runtime/gc/space/dlmalloc_space_static_test.cc @@ -15,6 +15,7 @@ */ #include "space_test.h" + #include "dlmalloc_space.h" namespace art { diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index d534bcb74d..a87aa890c8 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -341,6 +341,10 @@ bool ImageSpace::ValidateOatFile(std::string* error_msg) const { return true; } +const OatFile* ImageSpace::GetOatFile() const { + return oat_file_.get(); +} + OatFile* ImageSpace::ReleaseOatFile() { CHECK(oat_file_.get() != NULL); return oat_file_.release(); diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h index 372db3a580..dd9b58084d 100644 --- a/runtime/gc/space/image_space.h +++ b/runtime/gc/space/image_space.h @@ -51,6 +51,9 @@ class ImageSpace : public MemMapSpace { static ImageHeader* ReadImageHeaderOrDie(const char* image_location, InstructionSet image_isa); + // Give access to the OatFile. + const OatFile* GetOatFile() const; + // Releases the OatFile from the ImageSpace so it can be transfer to // the caller, presumably the ClassLinker. OatFile* ReleaseOatFile() diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h index d24016cb18..6f49fbf203 100644 --- a/runtime/gc/space/malloc_space.h +++ b/runtime/gc/space/malloc_space.h @@ -19,6 +19,7 @@ #include "space.h" +#include <iostream> #include <valgrind.h> #include <memcheck/memcheck.h> @@ -132,6 +133,8 @@ class MallocSpace : public ContinuousMemMapAllocSpace { return can_move_objects_; } + virtual void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) = 0; + protected: MallocSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end, byte* limit, size_t growth_limit, bool create_bitmaps, bool can_move_objects, diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index 5738d47562..92c6f534cb 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -227,7 +227,7 @@ size_t RosAllocSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** p // Callback from rosalloc when it needs to increase the footprint extern "C" void* art_heap_rosalloc_morecore(allocator::RosAlloc* rosalloc, intptr_t increment) { Heap* heap = Runtime::Current()->GetHeap(); - RosAllocSpace* rosalloc_space = heap->GetRosAllocSpace(); + RosAllocSpace* rosalloc_space = heap->GetRosAllocSpace(rosalloc); DCHECK(rosalloc_space != nullptr); DCHECK_EQ(rosalloc_space->GetRosAlloc(), rosalloc); return rosalloc_space->MoreCore(increment); diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h index 2934af87c6..f50530576b 100644 --- a/runtime/gc/space/rosalloc_space.h +++ b/runtime/gc/space/rosalloc_space.h @@ -120,6 +120,10 @@ class RosAllocSpace : public MallocSpace { virtual ~RosAllocSpace(); + void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE { + rosalloc_->LogFragmentationAllocFailure(os, failed_alloc_bytes); + } + protected: RosAllocSpace(const std::string& name, MemMap* mem_map, allocator::RosAlloc* rosalloc, byte* begin, byte* end, byte* limit, size_t growth_limit, bool can_move_objects, diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index fff4df1e0e..71c8eb5a84 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -407,11 +407,11 @@ class ContinuousMemMapAllocSpace : public MemMapSpace, public AllocSpace { // Clear the space back to an empty space. virtual void Clear() = 0; - accounting::ContinuousSpaceBitmap* GetLiveBitmap() const { + accounting::ContinuousSpaceBitmap* GetLiveBitmap() const OVERRIDE { return live_bitmap_.get(); } - accounting::ContinuousSpaceBitmap* GetMarkBitmap() const { + accounting::ContinuousSpaceBitmap* GetMarkBitmap() const OVERRIDE { return mark_bitmap_.get(); } diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h index a2d4942c8d..02911557f3 100644 --- a/runtime/gc/space/space_test.h +++ b/runtime/gc/space/space_test.h @@ -17,8 +17,6 @@ #ifndef ART_RUNTIME_GC_SPACE_SPACE_TEST_H_ #define ART_RUNTIME_GC_SPACE_SPACE_TEST_H_ -#include "zygote_space.h" - #include <stdint.h> #include <memory> @@ -26,6 +24,8 @@ #include "globals.h" #include "mirror/array-inl.h" #include "mirror/object-inl.h" +#include "scoped_thread_state_change.h" +#include "zygote_space.h" namespace art { namespace gc { diff --git a/runtime/globals.h b/runtime/globals.h index 3a906f15f5..1d9f22c35e 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -105,6 +105,19 @@ static constexpr bool kUseBakerOrBrooksReadBarrier = kUseBakerReadBarrier || kUs // If true, references within the heap are poisoned (negated). static constexpr bool kPoisonHeapReferences = false; +// Kinds of tracing clocks. +enum TraceClockSource { + kTraceClockSourceThreadCpu, + kTraceClockSourceWall, + kTraceClockSourceDual, // Both wall and thread CPU clocks. +}; + +#if defined(HAVE_POSIX_CLOCKS) +static constexpr TraceClockSource kDefaultTraceClockSource = kTraceClockSourceDual; +#else +static constexpr TraceClockSource kDefaultTraceClockSource = kTraceClockSourceWall; +#endif + } // namespace art #endif // ART_RUNTIME_GLOBALS_H_ diff --git a/runtime/handle.h b/runtime/handle.h index 7e13601af9..f70faf40d0 100644 --- a/runtime/handle.h +++ b/runtime/handle.h @@ -28,29 +28,40 @@ class Thread; template<class T> class Handle; +// Handles are memory locations that contain GC roots. As the mirror::Object*s within a handle are +// GC visible then the GC may move the references within them, something that couldn't be done with +// a wrap pointer. Handles are generally allocated within HandleScopes. ConstHandle is a super-class +// of Handle and doesn't support assignment operations. template<class T> class ConstHandle { public: ConstHandle() : reference_(nullptr) { } - ConstHandle(const ConstHandle<T>& handle) ALWAYS_INLINE : reference_(handle.reference_) { + + ALWAYS_INLINE ConstHandle(const ConstHandle<T>& handle) : reference_(handle.reference_) { } - ConstHandle<T>& operator=(const ConstHandle<T>& handle) ALWAYS_INLINE { + + ALWAYS_INLINE ConstHandle<T>& operator=(const ConstHandle<T>& handle) { reference_ = handle.reference_; return *this; } - explicit ConstHandle(StackReference<T>* reference) ALWAYS_INLINE : reference_(reference) { + + ALWAYS_INLINE explicit ConstHandle(StackReference<T>* reference) : reference_(reference) { } - T& operator*() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE { + + ALWAYS_INLINE T& operator*() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return *Get(); } - T* operator->() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE { + + ALWAYS_INLINE T* operator->() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return Get(); } - T* Get() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE { + + ALWAYS_INLINE T* Get() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return reference_->AsMirrorPtr(); } - jobject ToJObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE { + + ALWAYS_INLINE jobject ToJObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(reference_->AsMirrorPtr() == nullptr)) { // Special case so that we work with NullHandles. return nullptr; @@ -73,8 +84,8 @@ class ConstHandle { StackReference<T>* GetReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE { return reference_; } - const StackReference<T>* GetReference() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - ALWAYS_INLINE { + ALWAYS_INLINE const StackReference<T>* GetReference() const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return reference_; } @@ -86,47 +97,54 @@ class ConstHandle { template<size_t kNumReferences> friend class StackHandleScope; }; +// Handles that support assignment. template<class T> class Handle : public ConstHandle<T> { public: Handle() { } - Handle(const Handle<T>& handle) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE + + ALWAYS_INLINE Handle(const Handle<T>& handle) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : ConstHandle<T>(handle.reference_) { } - Handle<T>& operator=(const Handle<T>& handle) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - ALWAYS_INLINE { + + ALWAYS_INLINE Handle<T>& operator=(const Handle<T>& handle) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ConstHandle<T>::operator=(handle); return *this; } - explicit Handle(StackReference<T>* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - ALWAYS_INLINE : ConstHandle<T>(reference) { + + ALWAYS_INLINE explicit Handle(StackReference<T>* reference) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : ConstHandle<T>(reference) { } - T* Assign(T* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE { + + ALWAYS_INLINE T* Assign(T* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { StackReference<T>* ref = ConstHandle<T>::GetReference(); T* const old = ref->AsMirrorPtr(); ref->Assign(reference); return old; } + template<typename S> + explicit Handle(const Handle<S>& handle) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : ConstHandle<T>(handle) { + } + protected: template<typename S> explicit Handle(StackReference<S>* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : ConstHandle<T>(reference) { } - template<typename S> - explicit Handle(const Handle<S>& handle) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : ConstHandle<T>(handle) { - } private: friend class BuildGenericJniFrameVisitor; - template<class S> friend class Handle; friend class HandleScope; template<class S> friend class HandleWrapper; template<size_t kNumReferences> friend class StackHandleScope; }; +// A special case of Handle that only holds references to null. template<class T> class NullHandle : public Handle<T> { public: diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h index 62c761475a..7bc811db87 100644 --- a/runtime/handle_scope-inl.h +++ b/runtime/handle_scope-inl.h @@ -17,7 +17,7 @@ #ifndef ART_RUNTIME_HANDLE_SCOPE_INL_H_ #define ART_RUNTIME_HANDLE_SCOPE_INL_H_ -#include "handle_scope-inl.h" +#include "handle_scope.h" #include "handle.h" #include "thread.h" diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h index 629e4ecf2f..42ef77927c 100644 --- a/runtime/handle_scope.h +++ b/runtime/handle_scope.h @@ -27,10 +27,12 @@ namespace art { namespace mirror { class Object; } + class Thread; -// HandleScopes can be allocated within the bridge frame between managed and native code backed by -// stack storage or manually allocated in native. +// HandleScopes are scoped objects containing a number of Handles. They are used to allocate +// handles, for these handles (and the objects contained within them) to be visible/roots for the +// GC. It is most common to stack allocate HandleScopes using StackHandleScope. class PACKED(4) HandleScope { public: ~HandleScope() {} @@ -130,6 +132,7 @@ class PACKED(4) HandleScope { private: template<size_t kNumReferences> friend class StackHandleScope; + DISALLOW_COPY_AND_ASSIGN(HandleScope); }; @@ -152,7 +155,7 @@ class HandleWrapper : public Handle<T> { // Scoped handle storage of a fixed size that is usually stack allocated. template<size_t kNumReferences> -class PACKED(4) StackHandleScope : public HandleScope { +class PACKED(4) StackHandleScope FINAL : public HandleScope { public: explicit StackHandleScope(Thread* self); ~StackHandleScope(); @@ -181,20 +184,29 @@ class PACKED(4) StackHandleScope : public HandleScope { template<class T> Handle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { SetReference(pos_, object); - return Handle<T>(GetHandle(pos_++)); + Handle<T> h(GetHandle(pos_)); + pos_++; + return h; } template<class T> HandleWrapper<T> NewHandleWrapper(T** object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { SetReference(pos_, *object); - Handle<T> h(GetHandle(pos_++)); + Handle<T> h(GetHandle(pos_)); + pos_++; return HandleWrapper<T>(object, h); } private: - // references_storage_ needs to be first so that it matches the address of references_. + // References_storage_ needs to be first so that it appears in the same location as + // HandleScope::references_. StackReference<mirror::Object> references_storage_[kNumReferences]; + + // The thread that the stack handle scope is a linked list upon. The stack handle scope will + // push and pop itself from this thread. Thread* const self_; + + // Position new handles will be created. size_t pos_; template<size_t kNumRefs> friend class StackHandleScope; diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index 33339f8f6c..7e3b6bab26 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -52,7 +52,6 @@ #include "mirror/class.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "os.h" #include "safe_map.h" #include "scoped_thread_state_change.h" diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc index 449817add6..a33a981545 100644 --- a/runtime/indirect_reference_table_test.cc +++ b/runtime/indirect_reference_table_test.cc @@ -18,6 +18,7 @@ #include "common_runtime_test.h" #include "mirror/object-inl.h" +#include "scoped_thread_state_change.h" namespace art { diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h index 6e10a4c036..dce1c150ba 100644 --- a/runtime/instruction_set.h +++ b/runtime/instruction_set.h @@ -181,7 +181,7 @@ static constexpr size_t kArm64StackOverflowReservedBytes = 32 * KB; // TODO: Bumped to workaround regression (http://b/14982147) Specifically to fix: // test-art-host-run-test-interpreter-018-stack-overflow // test-art-host-run-test-interpreter-107-int-math2 -static constexpr size_t kX86StackOverflowReservedBytes = 24 * KB; +static constexpr size_t kX86StackOverflowReservedBytes = (kIsDebugBuild ? 32 : 24) * KB; static constexpr size_t kX86_64StackOverflowReservedBytes = 32 * KB; static constexpr size_t GetStackOverflowReservedBytes(InstructionSet isa) { diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index f459b590eb..f4eaa61c1e 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -18,6 +18,7 @@ #include <sys/uio.h> +#include "arch/context.h" #include "atomic.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" @@ -34,7 +35,6 @@ #if !defined(ART_USE_PORTABLE_COMPILER) #include "entrypoints/quick/quick_entrypoints.h" #endif -#include "object_utils.h" #include "os.h" #include "scoped_thread_state_change.h" #include "thread.h" @@ -93,16 +93,17 @@ static void UpdateEntrypoints(mirror::ArtMethod* method, const void* quick_code, method->ClearIsPortableCompiled(); } if (!method->IsResolutionMethod()) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (quick_code == GetQuickToInterpreterBridge() || - quick_code == GetQuickToInterpreterBridgeTrampoline(Runtime::Current()->GetClassLinker()) || - (quick_code == GetQuickResolutionTrampoline(Runtime::Current()->GetClassLinker()) && + quick_code == class_linker->GetQuickToInterpreterBridgeTrampoline() || + (quick_code == class_linker->GetQuickResolutionTrampoline() && Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly() && !method->IsNative() && !method->IsProxyMethod())) { if (kIsDebugBuild) { if (quick_code == GetQuickToInterpreterBridge()) { DCHECK(portable_code == GetPortableToInterpreterBridge()); - } else if (quick_code == GetQuickResolutionTrampoline(Runtime::Current()->GetClassLinker())) { - DCHECK(portable_code == GetPortableResolutionTrampoline(Runtime::Current()->GetClassLinker())); + } else if (quick_code == class_linker->GetQuickResolutionTrampoline()) { + DCHECK(portable_code == class_linker->GetPortableResolutionTrampoline()); } } DCHECK(!method->IsNative()) << PrettyMethod(method); @@ -133,8 +134,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 = GetPortableResolutionTrampoline(class_linker); - new_quick_code = GetQuickResolutionTrampoline(class_linker); + new_portable_code = class_linker->GetPortableResolutionTrampoline(); + new_quick_code = class_linker->GetQuickResolutionTrampoline(); } } else { // !uninstall if ((interpreter_stubs_installed_ || forced_interpret_only_ || IsDeoptimized(method)) && @@ -146,20 +147,17 @@ void Instrumentation::InstallStubsForMethod(mirror::ArtMethod* method) { // class, all its static methods code will be set to the instrumentation entry point. // For more details, see ClassLinker::FixupStaticTrampolines. if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) { - // Do not overwrite interpreter to prevent from posting method entry/exit events twice. - new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code); - new_quick_code = class_linker->GetQuickOatCodeFor(method); - DCHECK(new_quick_code != GetQuickToInterpreterBridgeTrampoline(class_linker)); - if (entry_exit_stubs_installed_ && new_quick_code != GetQuickToInterpreterBridge()) { - // TODO: portable to quick bridge. Bug: 8196384. We cannot enable the check below as long - // as GetPortableToQuickBridge() == GetPortableToInterpreterBridge(). - // DCHECK(new_portable_code != GetPortableToInterpreterBridge()); + if (entry_exit_stubs_installed_) { new_portable_code = GetPortableToInterpreterBridge(); new_quick_code = GetQuickInstrumentationEntryPoint(); + } else { + new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code); + new_quick_code = class_linker->GetQuickOatCodeFor(method); + DCHECK(new_quick_code != class_linker->GetQuickToInterpreterBridgeTrampoline()); } } else { - new_portable_code = GetPortableResolutionTrampoline(class_linker); - new_quick_code = GetQuickResolutionTrampoline(class_linker); + new_portable_code = class_linker->GetPortableResolutionTrampoline(); + new_quick_code = class_linker->GetQuickResolutionTrampoline(); } } } @@ -175,7 +173,6 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) struct InstallStackVisitor : public StackVisitor { InstallStackVisitor(Thread* thread, Context* context, uintptr_t instrumentation_exit_pc) : StackVisitor(thread, context), instrumentation_stack_(thread->GetInstrumentationStack()), - existing_instrumentation_frames_count_(instrumentation_stack_->size()), instrumentation_exit_pc_(instrumentation_exit_pc), reached_existing_instrumentation_frames_(false), instrumentation_stack_depth_(0), last_return_pc_(0) { @@ -190,18 +187,10 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) last_return_pc_ = 0; return true; // Ignore upcalls. } - if (m->IsRuntimeMethod()) { - if (kVerboseInstrumentation) { - LOG(INFO) << " Skipping runtime method. Frame " << GetFrameId(); - } - last_return_pc_ = GetReturnPc(); - return true; // Ignore unresolved methods since they will be instrumented after resolution. - } - if (kVerboseInstrumentation) { - LOG(INFO) << " Installing exit stub in " << DescribeLocation(); - } if (GetCurrentQuickFrame() == NULL) { - InstrumentationStackFrame instrumentation_frame(GetThisObject(), m, 0, GetFrameId(), false); + bool interpreter_frame = !m->IsPortableCompiled(); + InstrumentationStackFrame instrumentation_frame(GetThisObject(), m, 0, GetFrameId(), + interpreter_frame); if (kVerboseInstrumentation) { LOG(INFO) << "Pushing shadow frame " << instrumentation_frame.Dump(); } @@ -209,6 +198,32 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) return true; // Continue. } uintptr_t return_pc = GetReturnPc(); + if (m->IsRuntimeMethod()) { + if (return_pc == instrumentation_exit_pc_) { + if (kVerboseInstrumentation) { + LOG(INFO) << " Handling quick to interpreter transition. Frame " << GetFrameId(); + } + CHECK_LT(instrumentation_stack_depth_, instrumentation_stack_->size()); + const InstrumentationStackFrame& frame = instrumentation_stack_->at(instrumentation_stack_depth_); + CHECK(frame.interpreter_entry_); + // This is an interpreter frame so method enter event must have been reported. However we + // need to push a DEX pc into the dex_pcs_ list to match size of instrumentation stack. + // Since we won't report method entry here, we can safely push any DEX pc. + dex_pcs_.push_back(0); + last_return_pc_ = frame.return_pc_; + ++instrumentation_stack_depth_; + return true; + } else { + if (kVerboseInstrumentation) { + LOG(INFO) << " Skipping runtime method. Frame " << GetFrameId(); + } + last_return_pc_ = GetReturnPc(); + return true; // Ignore unresolved methods since they will be instrumented after resolution. + } + } + if (kVerboseInstrumentation) { + LOG(INFO) << " Installing exit stub in " << DescribeLocation(); + } if (return_pc == instrumentation_exit_pc_) { // We've reached a frame which has already been installed with instrumentation exit stub. // We should have already installed instrumentation on previous frames. @@ -231,8 +246,15 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) LOG(INFO) << "Pushing frame " << instrumentation_frame.Dump(); } - // Insert frame before old ones so we do not corrupt the instrumentation stack. - auto it = instrumentation_stack_->end() - existing_instrumentation_frames_count_; + // Insert frame at the right position so we do not corrupt the instrumentation stack. + // Instrumentation stack frames are in descending frame id order. + auto it = instrumentation_stack_->begin(); + for (auto end = instrumentation_stack_->end(); it != end; ++it) { + const InstrumentationStackFrame& current = *it; + if (instrumentation_frame.frame_id_ >= current.frame_id_) { + break; + } + } instrumentation_stack_->insert(it, instrumentation_frame); SetReturnPc(instrumentation_exit_pc_); } @@ -243,7 +265,6 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) } std::deque<InstrumentationStackFrame>* const instrumentation_stack_; std::vector<InstrumentationStackFrame> shadow_stack_; - const size_t existing_instrumentation_frames_count_; std::vector<uint32_t> dex_pcs_; const uintptr_t instrumentation_exit_pc_; bool reached_existing_instrumentation_frames_; @@ -275,7 +296,9 @@ static void InstrumentationInstallStack(Thread* thread, void* arg) } uint32_t dex_pc = visitor.dex_pcs_.back(); visitor.dex_pcs_.pop_back(); - instrumentation->MethodEnterEvent(thread, (*isi).this_object_, (*isi).method_, dex_pc); + if (!isi->interpreter_entry_) { + instrumentation->MethodEnterEvent(thread, (*isi).this_object_, (*isi).method_, dex_pc); + } } } thread->VerifyStack(); @@ -570,22 +593,25 @@ void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* q new_portable_code = GetPortableToInterpreterBridge(); new_quick_code = GetQuickToInterpreterBridge(); new_have_portable_code = false; - } else if (quick_code == GetQuickResolutionTrampoline(Runtime::Current()->GetClassLinker()) || - quick_code == GetQuickToInterpreterBridgeTrampoline(Runtime::Current()->GetClassLinker()) || - quick_code == GetQuickToInterpreterBridge()) { - DCHECK((portable_code == GetPortableResolutionTrampoline(Runtime::Current()->GetClassLinker())) || - (portable_code == GetPortableToInterpreterBridge())); - new_portable_code = portable_code; - new_quick_code = quick_code; - new_have_portable_code = have_portable_code; - } else if (entry_exit_stubs_installed_) { - new_quick_code = GetQuickInstrumentationEntryPoint(); - new_portable_code = GetPortableToInterpreterBridge(); - new_have_portable_code = false; } else { - new_portable_code = portable_code; - new_quick_code = quick_code; - new_have_portable_code = have_portable_code; + 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())); + new_portable_code = portable_code; + new_quick_code = quick_code; + new_have_portable_code = have_portable_code; + } else if (entry_exit_stubs_installed_) { + new_quick_code = GetQuickInstrumentationEntryPoint(); + new_portable_code = GetPortableToInterpreterBridge(); + new_have_portable_code = false; + } else { + new_portable_code = portable_code; + new_quick_code = quick_code; + new_have_portable_code = have_portable_code; + } } } UpdateEntrypoints(method, new_quick_code, new_portable_code, new_have_portable_code); @@ -606,7 +632,7 @@ void Instrumentation::Deoptimize(mirror::ArtMethod* method) { CHECK(!already_deoptimized) << "Method " << PrettyMethod(method) << " is already deoptimized"; if (!interpreter_stubs_installed_) { - UpdateEntrypoints(method, GetQuickToInterpreterBridge(), GetPortableToInterpreterBridge(), + UpdateEntrypoints(method, GetQuickInstrumentationEntryPoint(), GetPortableToInterpreterBridge(), false); // Install instrumentation exit stub and instrumentation frames. We may already have installed @@ -639,8 +665,9 @@ void Instrumentation::Undeoptimize(mirror::ArtMethod* method) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (method->IsStatic() && !method->IsConstructor() && !method->GetDeclaringClass()->IsInitialized()) { - UpdateEntrypoints(method, GetQuickResolutionTrampoline(class_linker), - GetPortableResolutionTrampoline(class_linker), false); + // TODO: we're updating to entrypoints in the image here, we can avoid the trampoline. + UpdateEntrypoints(method, class_linker->GetQuickResolutionTrampoline(), + class_linker->GetPortableResolutionTrampoline(), false); } else { bool have_portable_code = false; const void* quick_code = class_linker->GetQuickOatCodeFor(method); @@ -720,8 +747,9 @@ const void* Instrumentation::GetQuickCodeFor(mirror::ArtMethod* method) const { if (LIKELY(!instrumentation_stubs_installed_)) { const void* code = method->GetEntryPointFromQuickCompiledCode(); DCHECK(code != nullptr); - if (LIKELY(code != GetQuickResolutionTrampoline(runtime->GetClassLinker())) && - LIKELY(code != GetQuickToInterpreterBridgeTrampoline(runtime->GetClassLinker())) && + ClassLinker* class_linker = runtime->GetClassLinker(); + if (LIKELY(code != class_linker->GetQuickResolutionTrampoline()) && + LIKELY(code != class_linker->GetQuickToInterpreterBridgeTrampoline()) && LIKELY(code != GetQuickToInterpreterBridge())) { return code; } @@ -844,7 +872,9 @@ void Instrumentation::PushInstrumentationStackFrame(Thread* self, mirror::Object frame_id, interpreter_entry); stack->push_front(instrumentation_frame); - MethodEnterEvent(self, this_object, method, 0); + if (!interpreter_entry) { + MethodEnterEvent(self, this_object, method, 0); + } } TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self, uintptr_t* return_pc, @@ -875,7 +905,9 @@ TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self, uintpt // return_pc. uint32_t dex_pc = DexFile::kDexNoIndex; mirror::Object* this_object = instrumentation_frame.this_object_; - MethodExitEvent(self, this_object, instrumentation_frame.method_, dex_pc, return_value); + if (!instrumentation_frame.interpreter_entry_) { + MethodExitEvent(self, this_object, instrumentation_frame.method_, dex_pc, return_value); + } // Deoptimize if the caller needs to continue execution in the interpreter. Do nothing if we get // back to an upcall. diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc index 5995d9e5d3..d462e1444f 100644 --- a/runtime/intern_table_test.cc +++ b/runtime/intern_table_test.cc @@ -19,6 +19,8 @@ #include "common_runtime_test.h" #include "mirror/object.h" #include "handle_scope-inl.h" +#include "mirror/string.h" +#include "scoped_thread_state_change.h" namespace art { diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 729444e4f6..e3068b338d 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -95,11 +95,11 @@ static void UnstartedRuntimeJni(Thread* self, ArtMethod* method, jint newValue = args[4]; bool success; if (Runtime::Current()->IsActiveTransaction()) { - success = obj->CasFieldWeakSequentiallyConsistent32<true>(MemberOffset(offset), - expectedValue, newValue); + success = obj->CasFieldStrongSequentiallyConsistent32<true>(MemberOffset(offset), + expectedValue, newValue); } else { - success = obj->CasFieldWeakSequentiallyConsistent32<false>(MemberOffset(offset), - expectedValue, newValue); + success = obj->CasFieldStrongSequentiallyConsistent32<false>(MemberOffset(offset), + expectedValue, newValue); } result->SetZ(success ? JNI_TRUE : JNI_FALSE); } else if (name == "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)") { @@ -356,6 +356,7 @@ static inline JValue Execute(Thread* self, MethodHelper& mh, const DexFile::Code shadow_frame.GetMethod()->GetDeclaringClass()->IsProxyClass()); DCHECK(!shadow_frame.GetMethod()->IsAbstract()); DCHECK(!shadow_frame.GetMethod()->IsNative()); + shadow_frame.GetMethod()->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); bool transaction_active = Runtime::Current()->IsActiveTransaction(); if (LIKELY(shadow_frame.GetMethod()->IsPreverified())) { diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 9f04b90c55..b35da0cf1b 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -15,6 +15,8 @@ */ #include "interpreter_common.h" + +#include "field_helper.h" #include "mirror/array-inl.h" namespace art { @@ -45,6 +47,7 @@ bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst return false; } } + f->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); // Report this field access to instrumentation if needed. instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasFieldReadListeners())) { @@ -221,6 +224,7 @@ bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction return false; } } + f->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); // Report this field access to instrumentation if needed. Since we only have the offset of // the field from the base of the object, we need to look for it first. @@ -757,40 +761,64 @@ void RecordArrayElementsInTransaction(mirror::Array* array, int32_t count) } } +// Helper function to deal with class loading in an unstarted runtime. +static void UnstartedRuntimeFindClass(Thread* self, Handle<mirror::String> className, + Handle<mirror::ClassLoader> class_loader, JValue* result, + const std::string& method_name, bool initialize_class, + bool abort_if_not_found) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(className.Get() != nullptr); + std::string descriptor(DotToDescriptor(className->ToModifiedUtf8().c_str())); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + + Class* found = class_linker->FindClass(self, descriptor.c_str(), class_loader); + if (found == nullptr && abort_if_not_found) { + if (!self->IsExceptionPending()) { + AbortTransaction(self, "%s failed in un-started runtime for class: %s", + method_name.c_str(), PrettyDescriptor(descriptor).c_str()); + } + return; + } + if (found != nullptr && initialize_class) { + StackHandleScope<1> hs(self); + Handle<mirror::Class> h_class(hs.NewHandle(found)); + if (!class_linker->EnsureInitialized(h_class, true, true)) { + CHECK(self->IsExceptionPending()); + return; + } + } + result->SetL(found); +} + 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 // problems in core libraries. std::string name(PrettyMethod(shadow_frame->GetMethod())); - if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)" - || name == "java.lang.Class java.lang.VMClassLoader.loadClass(java.lang.String, boolean)") { - // TODO Class#forName should actually call Class::EnsureInitialized always. Support for the - // other variants that take more arguments should also be added. - std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str())); - - // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader(); - Class* found = Runtime::Current()->GetClassLinker()->FindClass( - self, descriptor.c_str(), NullHandle<mirror::ClassLoader>()); - if (found == NULL) { - if (!self->IsExceptionPending()) { - AbortTransaction(self, "Class.forName failed in un-started runtime for class: %s", - PrettyDescriptor(descriptor).c_str()); - } - return; - } - result->SetL(found); + if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") { + // TODO: Support for the other variants that take more arguments should also be added. + mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset)->AsString(); + StackHandleScope<1> hs(self); + Handle<mirror::String> h_class_name(hs.NewHandle(class_name)); + UnstartedRuntimeFindClass(self, h_class_name, NullHandle<mirror::ClassLoader>(), result, name, + true, true); + } else if (name == "java.lang.Class java.lang.VMClassLoader.loadClass(java.lang.String, boolean)") { + mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset)->AsString(); + StackHandleScope<1> hs(self); + Handle<mirror::String> h_class_name(hs.NewHandle(class_name)); + UnstartedRuntimeFindClass(self, h_class_name, NullHandle<mirror::ClassLoader>(), result, name, + false, true); + } else if (name == "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") { + mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); + mirror::ClassLoader* class_loader = + down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset)); + StackHandleScope<2> hs(self); + Handle<mirror::String> h_class_name(hs.NewHandle(class_name)); + Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader)); + UnstartedRuntimeFindClass(self, h_class_name, h_class_loader, result, name, false, false); } else if (name == "java.lang.Class java.lang.Void.lookupType()") { result->SetL(Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V')); - } else if (name == "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") { - StackHandleScope<1> hs(self); - Handle<ClassLoader> class_loader( - hs.NewHandle(down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset)))); - std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset + 1)->AsString()->ToModifiedUtf8().c_str())); - - Class* found = Runtime::Current()->GetClassLinker()->FindClass(self, descriptor.c_str(), - class_loader); - result->SetL(found); } else if (name == "java.lang.Object java.lang.Class.newInstance()") { Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); ArtMethod* c = klass->FindDeclaredDirectMethod("<init>", "()V"); diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 5277330409..1bcd27e2d4 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -27,9 +27,10 @@ #include "dex_file-inl.h" #include "dex_instruction-inl.h" #include "dex_instruction.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "gc/accounting/card_table-inl.h" #include "handle_scope-inl.h" +#include "method_helper-inl.h" #include "nth_caller_visitor.h" #include "mirror/art_field-inl.h" #include "mirror/art_method.h" @@ -39,7 +40,6 @@ #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" -#include "object_utils.h" #include "ScopedLocalRef.h" #include "scoped_thread_state_change.h" #include "thread.h" diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index cb4868c957..abd4b44d38 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -536,6 +536,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { + obj->GetClass()->AssertInitializedOrInitializingInThread(self); // Don't allow finalizable objects to be allocated during a transaction since these can't be // finalized without a started runtime. if (transaction_active && obj->GetClass()->IsFinalizable()) { diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index bdf2a20192..c6356485a9 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -449,6 +449,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { + obj->GetClass()->AssertInitializedOrInitializingInThread(self); // Don't allow finalizable objects to be allocated during a transaction since these can't // be finalized without a started runtime. if (transaction_active && obj->GetClass()->IsFinalizable()) { diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc index 29d3c8ab8e..ad18d8a2ce 100644 --- a/runtime/jdwp/object_registry.cc +++ b/runtime/jdwp/object_registry.cc @@ -16,6 +16,7 @@ #include "object_registry.h" +#include "mirror/class.h" #include "scoped_thread_state_change.h" namespace art { diff --git a/runtime/jdwp/object_registry.h b/runtime/jdwp/object_registry.h index e1a687544a..f0314a382f 100644 --- a/runtime/jdwp/object_registry.h +++ b/runtime/jdwp/object_registry.h @@ -17,20 +17,21 @@ #ifndef ART_RUNTIME_JDWP_OBJECT_REGISTRY_H_ #define ART_RUNTIME_JDWP_OBJECT_REGISTRY_H_ +#include <jni.h> #include <stdint.h> #include <map> #include "jdwp/jdwp.h" -#include "mirror/art_field-inl.h" -#include "mirror/class.h" -#include "mirror/class-inl.h" -#include "mirror/object-inl.h" -#include "object_callbacks.h" #include "safe_map.h" namespace art { +namespace mirror { + class Object; + class Class; +} // namespace mirror + struct ObjectRegistryEntry { // Is jni_reference a weak global or a regular global reference? jobjectRefType jni_reference_type; diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 845691d182..f9c7ec692c 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -41,7 +41,6 @@ #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" #include "mirror/throwable.h" -#include "object_utils.h" #include "parsed_options.h" #include "reflection.h" #include "runtime.h" @@ -3002,7 +3001,7 @@ extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version; return JNI_EVERSION; } - Runtime::Options options; + RuntimeOptions options; for (int i = 0; i < args->nOptions; ++i) { JavaVMOption* option = &args->options[i]; options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo)); diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 8ef1cb6115..7c7e60c5ee 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -19,6 +19,7 @@ #include "common_compiler_test.h" #include "mirror/art_method-inl.h" #include "mirror/string-inl.h" +#include "scoped_thread_state_change.h" #include "ScopedLocalRef.h" namespace art { diff --git a/runtime/method_helper-inl.h b/runtime/method_helper-inl.h new file mode 100644 index 0000000000..4f95a28e81 --- /dev/null +++ b/runtime/method_helper-inl.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_METHOD_HELPER_INL_H_ +#define ART_RUNTIME_METHOD_HELPER_INL_H_ + +#include "method_helper.h" + +#include "class_linker.h" +#include "mirror/object_array.h" +#include "runtime.h" +#include "thread-inl.h" + +namespace art { + +inline mirror::Class* MethodHelper::GetClassFromTypeIdx(uint16_t type_idx, bool resolve) { + mirror::ArtMethod* method = GetMethod(); + mirror::Class* type = method->GetDexCacheResolvedTypes()->Get(type_idx); + if (type == nullptr && resolve) { + type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method); + CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); + } + return type; +} + +inline mirror::Class* MethodHelper::GetReturnType(bool resolve) { + mirror::ArtMethod* method = GetMethod(); + const DexFile* dex_file = method->GetDexFile(); + const DexFile::MethodId& method_id = dex_file->GetMethodId(method->GetDexMethodIndex()); + const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); + uint16_t return_type_idx = proto_id.return_type_idx_; + return GetClassFromTypeIdx(return_type_idx, resolve); +} + +inline mirror::String* MethodHelper::ResolveString(uint32_t string_idx) { + mirror::ArtMethod* method = GetMethod(); + mirror::String* s = method->GetDexCacheStrings()->Get(string_idx); + if (UNLIKELY(s == nullptr)) { + StackHandleScope<1> hs(Thread::Current()); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache())); + s = Runtime::Current()->GetClassLinker()->ResolveString(*method->GetDexFile(), string_idx, + dex_cache); + } + return s; +} + +} // namespace art + +#endif // ART_RUNTIME_METHOD_HELPER_INL_H_ diff --git a/runtime/method_helper.cc b/runtime/method_helper.cc new file mode 100644 index 0000000000..1bd2f9020c --- /dev/null +++ b/runtime/method_helper.cc @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "method_helper-inl.h" + +#include "class_linker.h" +#include "dex_file-inl.h" +#include "handle_scope-inl.h" +#include "mirror/art_method-inl.h" +#include "mirror/dex_cache.h" +#include "runtime.h" + +namespace art { + +mirror::String* MethodHelper::GetNameAsString(Thread* self) { + const DexFile* dex_file = method_->GetDexFile(); + mirror::ArtMethod* method = method_->GetInterfaceMethodIfProxy(); + uint32_t dex_method_idx = method->GetDexMethodIndex(); + const DexFile::MethodId& method_id = dex_file->GetMethodId(dex_method_idx); + StackHandleScope<1> hs(self); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache())); + return Runtime::Current()->GetClassLinker()->ResolveString(*dex_file, method_id.name_idx_, + dex_cache); +} + +bool MethodHelper::HasSameNameAndSignature(MethodHelper* other) { + const DexFile* dex_file = method_->GetDexFile(); + const DexFile::MethodId& mid = dex_file->GetMethodId(GetMethod()->GetDexMethodIndex()); + if (method_->GetDexCache() == other->method_->GetDexCache()) { + const DexFile::MethodId& other_mid = + dex_file->GetMethodId(other->GetMethod()->GetDexMethodIndex()); + return mid.name_idx_ == other_mid.name_idx_ && mid.proto_idx_ == other_mid.proto_idx_; + } + const DexFile* other_dex_file = other->method_->GetDexFile(); + const DexFile::MethodId& other_mid = + other_dex_file->GetMethodId(other->GetMethod()->GetDexMethodIndex()); + if (!DexFileStringEquals(dex_file, mid.name_idx_, other_dex_file, other_mid.name_idx_)) { + return false; // Name mismatch. + } + return dex_file->GetMethodSignature(mid) == other_dex_file->GetMethodSignature(other_mid); +} + +bool MethodHelper::HasSameSignatureWithDifferentClassLoaders(MethodHelper* other) { + if (UNLIKELY(GetReturnType() != other->GetReturnType())) { + return false; + } + const DexFile::TypeList* types = method_->GetParameterTypeList(); + const DexFile::TypeList* other_types = other->method_->GetParameterTypeList(); + if (types == nullptr) { + return (other_types == nullptr) || (other_types->Size() == 0); + } else if (UNLIKELY(other_types == nullptr)) { + return types->Size() == 0; + } + uint32_t num_types = types->Size(); + if (UNLIKELY(num_types != other_types->Size())) { + return false; + } + for (uint32_t i = 0; i < num_types; ++i) { + mirror::Class* param_type = GetClassFromTypeIdx(types->GetTypeItem(i).type_idx_); + mirror::Class* other_param_type = + other->GetClassFromTypeIdx(other_types->GetTypeItem(i).type_idx_); + if (UNLIKELY(param_type != other_param_type)) { + return false; + } + } + return true; +} + +uint32_t MethodHelper::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ArtMethod* method = GetMethod(); + const DexFile* dexfile = method->GetDexFile(); + if (dexfile == &other_dexfile) { + return method->GetDexMethodIndex(); + } + const DexFile::MethodId& mid = dexfile->GetMethodId(method->GetDexMethodIndex()); + const char* mid_declaring_class_descriptor = dexfile->StringByTypeIdx(mid.class_idx_); + const DexFile::StringId* other_descriptor = + other_dexfile.FindStringId(mid_declaring_class_descriptor); + if (other_descriptor != nullptr) { + const DexFile::TypeId* other_type_id = + other_dexfile.FindTypeId(other_dexfile.GetIndexForStringId(*other_descriptor)); + if (other_type_id != nullptr) { + const char* mid_name = dexfile->GetMethodName(mid); + const DexFile::StringId* other_name = other_dexfile.FindStringId(mid_name); + if (other_name != nullptr) { + uint16_t other_return_type_idx; + std::vector<uint16_t> other_param_type_idxs; + bool success = other_dexfile.CreateTypeList( + dexfile->GetMethodSignature(mid).ToString(), &other_return_type_idx, + &other_param_type_idxs); + if (success) { + const DexFile::ProtoId* other_sig = + other_dexfile.FindProtoId(other_return_type_idx, other_param_type_idxs); + if (other_sig != nullptr) { + const DexFile::MethodId* other_mid = other_dexfile.FindMethodId( + *other_type_id, *other_name, *other_sig); + if (other_mid != nullptr) { + return other_dexfile.GetIndexForMethodId(*other_mid); + } + } + } + } + } + } + return DexFile::kDexNoIndex; +} + +uint32_t MethodHelper::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile, + uint32_t name_and_signature_idx) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ArtMethod* method = GetMethod(); + const DexFile* dexfile = method->GetDexFile(); + const uint32_t dex_method_idx = method->GetDexMethodIndex(); + const DexFile::MethodId& mid = dexfile->GetMethodId(dex_method_idx); + const DexFile::MethodId& name_and_sig_mid = other_dexfile.GetMethodId(name_and_signature_idx); + DCHECK_STREQ(dexfile->GetMethodName(mid), other_dexfile.GetMethodName(name_and_sig_mid)); + DCHECK_EQ(dexfile->GetMethodSignature(mid), other_dexfile.GetMethodSignature(name_and_sig_mid)); + if (dexfile == &other_dexfile) { + return dex_method_idx; + } + const char* mid_declaring_class_descriptor = dexfile->StringByTypeIdx(mid.class_idx_); + const DexFile::StringId* other_descriptor = + other_dexfile.FindStringId(mid_declaring_class_descriptor); + if (other_descriptor != nullptr) { + const DexFile::TypeId* other_type_id = + other_dexfile.FindTypeId(other_dexfile.GetIndexForStringId(*other_descriptor)); + if (other_type_id != nullptr) { + const DexFile::MethodId* other_mid = other_dexfile.FindMethodId( + *other_type_id, other_dexfile.GetStringId(name_and_sig_mid.name_idx_), + other_dexfile.GetProtoId(name_and_sig_mid.proto_idx_)); + if (other_mid != nullptr) { + return other_dexfile.GetIndexForMethodId(*other_mid); + } + } + } + return DexFile::kDexNoIndex; +} + +} // namespace art diff --git a/runtime/method_helper.h b/runtime/method_helper.h new file mode 100644 index 0000000000..62465be513 --- /dev/null +++ b/runtime/method_helper.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_METHOD_HELPER_H_ +#define ART_RUNTIME_METHOD_HELPER_H_ + +#include "base/macros.h" +#include "handle.h" +#include "mirror/art_method.h" +#include "primitive.h" + +namespace art { + +class MethodHelper { + public: + explicit MethodHelper(Handle<mirror::ArtMethod> m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : method_(m), shorty_(nullptr), shorty_len_(0) { + SetMethod(m.Get()); + } + + void ChangeMethod(mirror::ArtMethod* new_m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(new_m != nullptr); + SetMethod(new_m); + shorty_ = nullptr; + } + + mirror::ArtMethod* GetMethod() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return method_->GetInterfaceMethodIfProxy(); + } + + mirror::String* GetNameAsString(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + const char* GetShorty() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const char* result = shorty_; + if (result == nullptr) { + result = method_->GetShorty(&shorty_len_); + shorty_ = result; + } + return result; + } + + uint32_t GetShortyLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (shorty_ == nullptr) { + GetShorty(); + } + return shorty_len_; + } + + // Counts the number of references in the parameter list of the corresponding method. + // Note: Thus does _not_ include "this" for non-static methods. + uint32_t GetNumberOfReferenceArgsWithoutReceiver() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const char* shorty = GetShorty(); + uint32_t refs = 0; + for (uint32_t i = 1; i < shorty_len_ ; ++i) { + if (shorty[i] == 'L') { + refs++; + } + } + + return refs; + } + + // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large + // number of bugs at call sites. + mirror::Class* GetReturnType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + size_t NumArgs() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // "1 +" because the first in Args is the receiver. + // "- 1" because we don't count the return type. + return (method_->IsStatic() ? 0 : 1) + GetShortyLength() - 1; + } + + // Get the primitive type associated with the given parameter. + Primitive::Type GetParamPrimitiveType(size_t param) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK_LT(param, NumArgs()); + if (GetMethod()->IsStatic()) { + param++; // 0th argument must skip return value at start of the shorty + } else if (param == 0) { + return Primitive::kPrimNot; + } + return Primitive::GetType(GetShorty()[param]); + } + + // Is the specified parameter a long or double, where parameter 0 is 'this' for instance methods. + bool IsParamALongOrDouble(size_t param) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Primitive::Type type = GetParamPrimitiveType(param); + return type == Primitive::kPrimLong || type == Primitive::kPrimDouble; + } + + // Is the specified parameter a reference, where parameter 0 is 'this' for instance methods. + bool IsParamAReference(size_t param) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetParamPrimitiveType(param) == Primitive::kPrimNot; + } + + bool HasSameNameAndSignature(MethodHelper* other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + bool HasSameSignatureWithDifferentClassLoaders(MethodHelper* other) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + mirror::Class* GetClassFromTypeIdx(uint16_t type_idx, bool resolve = true) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + mirror::String* ResolveString(uint32_t string_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + uint32_t FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // The name_and_signature_idx MUST point to a MethodId with the same name and signature in the + // other_dexfile, such as the method index used to resolve this method in the other_dexfile. + uint32_t FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile, + uint32_t name_and_signature_idx) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + // Set the method_ field, for proxy methods looking up the interface method via the resolved + // methods table. + void SetMethod(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + method_.Assign(method); + } + + Handle<mirror::ArtMethod> method_; + const char* shorty_; + uint32_t shorty_len_; + + DISALLOW_COPY_AND_ASSIGN(MethodHelper); +}; + +} // namespace art + +#endif // ART_RUNTIME_METHOD_HELPER_H_ diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 43bdf49c78..f3c8250db3 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -27,6 +27,11 @@ namespace art { namespace mirror { +inline uint32_t Array::ClassSize() { + uint32_t vtable_entries = Object::kVTableLength; + return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0); +} + template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline size_t Array::SizeOf() { // This is safe from overflow because the array was already allocated, so we know it's sane. diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc index f7b573729b..63f9860278 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -25,7 +25,6 @@ #include "object-inl.h" #include "object_array.h" #include "object_array-inl.h" -#include "object_utils.h" #include "handle_scope-inl.h" #include "thread.h" #include "utils.h" diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index 25a4535f14..6588b57b8a 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -30,6 +30,9 @@ namespace mirror { class MANAGED Array : public Object { public: + // The size of a java.lang.Class representing an array. + static uint32_t ClassSize(); + // Allocates an array with the given properties, if fill_usable is true the array will be of at // least component_count size, however, if there's usable space at the end of the allocation the // array will fill it. diff --git a/runtime/mirror/art_field-inl.h b/runtime/mirror/art_field-inl.h index 686fded404..00bed92cb3 100644 --- a/runtime/mirror/art_field-inl.h +++ b/runtime/mirror/art_field-inl.h @@ -20,15 +20,20 @@ #include "art_field.h" #include "base/logging.h" +#include "dex_cache.h" #include "gc/accounting/card_table-inl.h" #include "jvalue.h" #include "object-inl.h" -#include "object_utils.h" #include "primitive.h" namespace art { namespace mirror { +inline uint32_t ArtField::ClassSize() { + uint32_t vtable_entries = Object::kVTableLength + 6; + return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0); +} + inline Class* ArtField::GetDeclaringClass() { Class* result = GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(ArtField, declaring_class_)); DCHECK(result != NULL); diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc index f2729f660e..da21dfef06 100644 --- a/runtime/mirror/art_field.cc +++ b/runtime/mirror/art_field.cc @@ -20,7 +20,6 @@ #include "gc/accounting/card_table-inl.h" #include "object-inl.h" #include "object_array-inl.h" -#include "object_utils.h" #include "runtime.h" #include "scoped_thread_state_change.h" #include "utils.h" diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h index 502cec735f..741c6eb8a8 100644 --- a/runtime/mirror/art_field.h +++ b/runtime/mirror/art_field.h @@ -19,22 +19,33 @@ #include <jni.h> -#include "class.h" #include "modifiers.h" #include "object.h" #include "object_callbacks.h" +#include "primitive.h" #include "read_barrier.h" namespace art { struct ArtFieldOffsets; +class DexFile; class ScopedObjectAccessAlreadyRunnable; namespace mirror { +class DexCache; + // C++ mirror of java.lang.reflect.ArtField -class MANAGED ArtField : public Object { +class MANAGED ArtField FINAL : public Object { public: + // Size of java.lang.reflect.ArtField.class. + static uint32_t ClassSize(); + + // Size of an instance of java.lang.reflect.ArtField not including its value array. + static constexpr uint32_t InstanceSize() { + return sizeof(ArtField); + } + static ArtField* FromReflectedField(const ScopedObjectAccessAlreadyRunnable& soa, jobject jlr_field) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -143,11 +154,17 @@ class MANAGED ArtField : public Object { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const char* GetTypeDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + Primitive::Type GetTypeAsPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + size_t FieldSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const DexFile* GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: @@ -169,11 +186,6 @@ class MANAGED ArtField : public Object { DISALLOW_IMPLICIT_CONSTRUCTORS(ArtField); }; -class MANAGED ArtFieldClass : public Class { - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(ArtFieldClass); -}; - } // namespace mirror } // namespace art diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index 8fcacc2a15..01b05a6e6f 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -19,16 +19,33 @@ #include "art_method.h" +#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" #include "oat.h" #include "quick/quick_method_frame_info.h" +#include "read_barrier-inl.h" #include "runtime-inl.h" namespace art { namespace mirror { +inline uint32_t ArtMethod::ClassSize() { + uint32_t vtable_entries = Object::kVTableLength + 8; + return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0); +} + +template<ReadBarrierOption kReadBarrierOption> +inline Class* ArtMethod::GetJavaLangReflectArtMethod() { + DCHECK(java_lang_reflect_ArtMethod_ != nullptr); + return ReadBarrier::BarrierForRoot<mirror::Class, kReadBarrierOption>( + &java_lang_reflect_ArtMethod_); +} + inline Class* ArtMethod::GetDeclaringClass() { Class* result = GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, declaring_class_)); DCHECK(result != NULL) << this; @@ -122,8 +139,8 @@ inline void ArtMethod::AssertPcIsWithinQuickCode(uintptr_t pc) { return; } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - if (code == GetQuickResolutionTrampoline(class_linker) || - code == GetQuickToInterpreterBridgeTrampoline(class_linker)) { + if (code == class_linker->GetQuickResolutionTrampoline() || + code == class_linker->GetQuickToInterpreterBridgeTrampoline()) { return; } DCHECK(IsWithinQuickCode(pc)) @@ -162,7 +179,7 @@ inline const void* ArtMethod::GetQuickOatEntryPoint() { // 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 != GetQuickToInterpreterBridgeTrampoline(runtime->GetClassLinker())); + DCHECK(entry_point != runtime->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline()); if (UNLIKELY(entry_point == GetQuickToInterpreterBridge()) || UNLIKELY(entry_point == runtime->GetClassLinker()->GetQuickGenericJniTrampoline())) { return nullptr; @@ -289,7 +306,7 @@ inline QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() { // 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 != GetQuickToInterpreterBridgeTrampoline(runtime->GetClassLinker())); + DCHECK(entry_point != runtime->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline()); CHECK(entry_point != GetQuickToInterpreterBridge()); if (UNLIKELY(entry_point == runtime->GetClassLinker()->GetQuickGenericJniTrampoline())) { diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 4821e294f3..167f848824 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -16,6 +16,7 @@ #include "art_method.h" +#include "arch/context.h" #include "art_field-inl.h" #include "art_method-inl.h" #include "base/stringpiece.h" @@ -26,12 +27,12 @@ #include "interpreter/interpreter.h" #include "jni_internal.h" #include "mapping_table.h" -#include "object-inl.h" -#include "object_array.h" +#include "method_helper.h" #include "object_array-inl.h" +#include "object_array.h" +#include "object-inl.h" #include "scoped_thread_state_change.h" #include "string.h" -#include "object_utils.h" #include "well_known_classes.h" namespace art { @@ -355,14 +356,6 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* self->PopManagedStackFragment(fragment); } -bool ArtMethod::IsRegistered() { - void* native_method = - GetFieldPtr<void*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_)); - CHECK(native_method != nullptr); - void* jni_stub = GetJniDlsymLookupStub(); - return native_method != jni_stub; -} - void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_fast) { DCHECK(Thread::Current() == self); CHECK(IsNative()) << PrettyMethod(this); diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index a55c48b87f..081bee1d88 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -17,21 +17,19 @@ #ifndef ART_RUNTIME_MIRROR_ART_METHOD_H_ #define ART_RUNTIME_MIRROR_ART_METHOD_H_ -#include "class.h" #include "dex_file.h" #include "invoke_type.h" #include "modifiers.h" #include "object.h" #include "object_callbacks.h" #include "quick/quick_method_frame_info.h" -#include "read_barrier.h" +#include "read_barrier_option.h" namespace art { struct ArtMethodOffsets; struct ConstructorMethodOffsets; union JValue; -struct MethodClassOffsets; class MethodHelper; class ScopedObjectAccessAlreadyRunnable; class StringPiece; @@ -39,14 +37,20 @@ class ShadowFrame; namespace mirror { -class StaticStorageBase; - typedef void (EntryPointFromInterpreter)(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result); -// C++ mirror of java.lang.reflect.Method and java.lang.reflect.Constructor -class MANAGED ArtMethod : public Object { +// C++ mirror of java.lang.reflect.ArtMethod. +class MANAGED ArtMethod FINAL : public Object { public: + // Size of java.lang.reflect.ArtMethod.class. + static uint32_t ClassSize(); + + // Size of an instance of java.lang.reflect.ArtMethod not including its value array. + static constexpr uint32_t InstanceSize() { + return sizeof(ArtMethod); + } + static ArtMethod* FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject jlr_method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -357,8 +361,6 @@ class MANAGED ArtMethod : public Object { return kPointerSize; } - bool IsRegistered() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void RegisterNative(Thread* self, const void* native_method, bool is_fast) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -411,11 +413,7 @@ class MANAGED ArtMethod : public Object { static void SetClass(Class* java_lang_reflect_ArtMethod); template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier> - static Class* GetJavaLangReflectArtMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(java_lang_reflect_ArtMethod_ != nullptr); - return ReadBarrier::BarrierForRoot<mirror::Class, kReadBarrierOption>( - &java_lang_reflect_ArtMethod_); - } + static Class* GetJavaLangReflectArtMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void ResetClass(); @@ -423,27 +421,45 @@ class MANAGED ArtMethod : public Object { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const DexFile* GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const char* GetDeclaringClassDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const char* GetShorty() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { uint32_t unused_length; return GetShorty(&unused_length); } + const char* GetShorty(uint32_t* out_length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const Signature GetSignature() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const DexFile::CodeItem* GetCodeItem() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsResolvedTypeIdx(uint16_t type_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + int32_t GetLineNumFromDexPC(uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const DexFile::ProtoId& GetPrototype() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const DexFile::TypeList* GetParameterTypeList() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const char* GetDeclaringClassSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + uint16_t GetClassDefIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const DexFile::ClassDef& GetClassDef() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const char* GetReturnTypeDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const char* GetTypeDescriptorFromTypeIdx(uint16_t type_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::ClassLoader* GetClassLoader() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ArtMethod* GetInterfaceMethodIfProxy() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); protected: @@ -505,11 +521,6 @@ class MANAGED ArtMethod : public Object { DISALLOW_IMPLICIT_CONSTRUCTORS(ArtMethod); }; -class MANAGED ArtMethodClass : public Class { - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(ArtMethodClass); -}; - } // namespace mirror } // namespace art diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 451235c327..329a984842 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -19,8 +19,8 @@ #include "class.h" -#include "art_field.h" -#include "art_method.h" +#include "art_field-inl.h" +#include "art_method-inl.h" #include "class_linker-inl.h" #include "class_loader.h" #include "common_throws.h" @@ -29,6 +29,8 @@ #include "gc/heap-inl.h" #include "iftable.h" #include "object_array-inl.h" +#include "read_barrier-inl.h" +#include "reference-inl.h" #include "runtime.h" #include "string.h" @@ -148,6 +150,23 @@ inline void Class::SetImTable(ObjectArray<ArtMethod>* new_imtable) { SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), new_imtable); } +inline ArtMethod* Class::GetEmbeddedImTableEntry(uint32_t i) { + uint32_t offset = EmbeddedImTableOffset().Uint32Value() + i * sizeof(ImTableEntry); + return GetFieldObject<mirror::ArtMethod>(MemberOffset(offset)); +} + +inline void Class::SetEmbeddedImTableEntry(uint32_t i, ArtMethod* method) { + uint32_t offset = EmbeddedImTableOffset().Uint32Value() + i * sizeof(ImTableEntry); + SetFieldObject<false>(MemberOffset(offset), method); + CHECK(method == GetImTable()->Get(i)); +} + +inline void Class::SetEmbeddedVTableEntry(uint32_t i, ArtMethod* method) { + uint32_t offset = EmbeddedVTableOffset().Uint32Value() + i * sizeof(VTableEntry); + SetFieldObject<false>(MemberOffset(offset), method); + CHECK(method == GetVTableDuringLinking()->Get(i)); +} + inline bool Class::Implements(Class* klass) { DCHECK(klass != NULL); DCHECK(klass->IsInterface()) << PrettyClass(this); @@ -373,7 +392,8 @@ inline ObjectArray<ArtField>* Class::GetSFields() { inline void Class::SetSFields(ObjectArray<ArtField>* new_sfields) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(NULL == GetFieldObject<ObjectArray<ArtField>>(OFFSET_OF_OBJECT_MEMBER(Class, sfields_))); + DCHECK((IsRetired() && new_sfields == nullptr) || + (NULL == GetFieldObject<ObjectArray<ArtField>>(OFFSET_OF_OBJECT_MEMBER(Class, sfields_)))); SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, sfields_), new_sfields); } @@ -435,9 +455,9 @@ inline void Class::SetVerifyErrorClass(Class* klass) { template<VerifyObjectFlags kVerifyFlags> inline uint32_t Class::GetAccessFlags() { - // Check class is loaded or this is java.lang.String that has a + // Check class is loaded/retired or this is java.lang.String that has a // circularity issue during loading the names of its members - DCHECK(IsLoaded<kVerifyFlags>() || + DCHECK(IsIdxLoaded<kVerifyFlags>() || IsRetired<kVerifyFlags>() || IsErroneous<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>() || this == String::GetJavaLangString() || this == ArtField::GetJavaLangReflectArtField() || @@ -503,12 +523,63 @@ inline Object* Class::AllocNonMovableObject(Thread* self) { return Alloc<true>(self, Runtime::Current()->GetHeap()->GetCurrentNonMovingAllocator()); } +inline uint32_t Class::ComputeClassSize(bool has_embedded_tables, + uint32_t num_vtable_entries, + uint32_t num_32bit_static_fields, + uint32_t num_64bit_static_fields, + uint32_t num_ref_static_fields) { + // Space used by java.lang.Class and its instance fields. + uint32_t size = sizeof(Class); + // Space used by embedded tables. + if (has_embedded_tables) { + uint32_t embedded_imt_size = kImtSize * sizeof(ImTableEntry); + uint32_t embedded_vtable_size = num_vtable_entries * sizeof(VTableEntry); + size += embedded_imt_size + embedded_vtable_size; + } + // Space used by reference statics. + size += num_ref_static_fields * sizeof(HeapReference<Object>); + // Possible pad for alignment. + if (((size & 7) != 0) && (num_64bit_static_fields > 0) && (num_32bit_static_fields == 0)) { + size += sizeof(uint32_t); + } + // Space used for primitive static fields. + size += (num_32bit_static_fields * sizeof(uint32_t)) + + (num_64bit_static_fields * sizeof(uint64_t)); + return size; +} + template <bool kVisitClass, typename Visitor> inline void Class::VisitReferences(mirror::Class* klass, const Visitor& visitor) { // Visit the static fields first so that we don't overwrite the SFields / IFields instance // fields. - VisitStaticFieldsReferences<kVisitClass>(this, visitor); VisitInstanceFieldsReferences<kVisitClass>(klass, visitor); + if (!IsTemp()) { + // Temp classes don't ever populate imt/vtable or static fields and they are not even + // allocated with the right size for those. + VisitStaticFieldsReferences<kVisitClass>(this, visitor); + if (ShouldHaveEmbeddedImtAndVTable()) { + VisitEmbeddedImtAndVTable(visitor); + } + } +} + +template<typename Visitor> +inline void Class::VisitEmbeddedImtAndVTable(const Visitor& visitor) { + uint32_t pos = sizeof(mirror::Class); + + size_t count = kImtSize; + for (size_t i = 0; i < count; ++i) { + MemberOffset offset = MemberOffset(pos); + visitor(this, offset, true); + pos += sizeof(ImTableEntry); + } + + count = ((GetVTable() != NULL) ? GetVTable()->GetLength() : 0); + for (size_t i = 0; i < count; ++i) { + MemberOffset offset = MemberOffset(pos); + visitor(this, offset, true); + pos += sizeof(VTableEntry); + } } template<ReadBarrierOption kReadBarrierOption> @@ -521,6 +592,11 @@ inline bool Class::IsArtMethodClass() const { return this == ArtMethod::GetJavaLangReflectArtMethod<kReadBarrierOption>(); } +template<ReadBarrierOption kReadBarrierOption> +inline bool Class::IsReferenceClass() const { + return this == Reference::GetJavaLangRefReference<kReadBarrierOption>(); +} + template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline bool Class::IsClassClass() { Class* java_lang_Class = GetClass<kVerifyFlags, kReadBarrierOption>()-> @@ -546,6 +622,68 @@ inline bool Class::DescriptorEquals(const char* match) { } } +inline void Class::AssertInitializedOrInitializingInThread(Thread* self) { + if (kIsDebugBuild && !IsInitialized()) { + CHECK(IsInitializing()) << PrettyClass(this) << " is not initializing: " << GetStatus(); + CHECK_EQ(GetClinitThreadId(), self->GetTid()) << PrettyClass(this) + << " is initializing in a different thread"; + } +} + +inline ObjectArray<Class>* Class::GetInterfaces() { + CHECK(IsProxyClass()); + // First static field. + DCHECK(GetSFields()->Get(0)->IsArtField()); + DCHECK_STREQ(GetSFields()->Get(0)->GetName(), "interfaces"); + MemberOffset field_offset = GetSFields()->Get(0)->GetOffset(); + return GetFieldObject<ObjectArray<Class>>(field_offset); +} + +inline ObjectArray<ObjectArray<Class>>* Class::GetThrows() { + CHECK(IsProxyClass()); + // Second static field. + DCHECK(GetSFields()->Get(1)->IsArtField()); + DCHECK_STREQ(GetSFields()->Get(1)->GetName(), "throws"); + MemberOffset field_offset = GetSFields()->Get(1)->GetOffset(); + return GetFieldObject<ObjectArray<ObjectArray<Class>>>(field_offset); +} + +inline MemberOffset Class::GetDisableIntrinsicFlagOffset() { + CHECK(IsReferenceClass()); + // First static field + DCHECK(GetSFields()->Get(0)->IsArtField()); + DCHECK_STREQ(GetSFields()->Get(0)->GetName(), "disableIntrinsic"); + return GetSFields()->Get(0)->GetOffset(); +} + +inline MemberOffset Class::GetSlowPathFlagOffset() { + CHECK(IsReferenceClass()); + // Second static field + DCHECK(GetSFields()->Get(1)->IsArtField()); + DCHECK_STREQ(GetSFields()->Get(1)->GetName(), "slowPathEnabled"); + return GetSFields()->Get(1)->GetOffset(); +} + +inline bool Class::GetSlowPathEnabled() { + return GetField32(GetSlowPathFlagOffset()); +} + +inline void Class::SetSlowPath(bool enabled) { + SetField32<false>(GetSlowPathFlagOffset(), enabled); +} + +inline void Class::InitializeClassVisitor::operator()( + mirror::Object* obj, size_t usable_size) const { + DCHECK_LE(class_size_, usable_size); + // Avoid AsClass as object is not yet in live bitmap or allocation stack. + mirror::Class* klass = down_cast<mirror::Class*>(obj); + // DCHECK(klass->IsClass()); + klass->SetClassSize(class_size_); + klass->SetPrimitiveType(Primitive::kPrimNot); // Default to not being primitive. + klass->SetDexClassDefIndex(DexFile::kDexNoIndex16); // Default to no valid class def index. + klass->SetDexTypeIndex(DexFile::kDexNoIndex16); // Default to no valid type index. +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index c6472c66d1..fadf80ebcf 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -18,17 +18,16 @@ #include "art_field-inl.h" #include "art_method-inl.h" -#include "class-inl.h" #include "class_linker.h" #include "class_loader.h" +#include "class-inl.h" #include "dex_cache.h" #include "dex_file-inl.h" #include "gc/accounting/card_table-inl.h" -#include "object-inl.h" +#include "handle_scope-inl.h" #include "object_array-inl.h" -#include "object_utils.h" +#include "object-inl.h" #include "runtime.h" -#include "handle_scope-inl.h" #include "thread.h" #include "throwable.h" #include "utils.h" @@ -63,7 +62,8 @@ void Class::SetStatus(Status new_status, Thread* self) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); bool class_linker_initialized = class_linker != nullptr && class_linker->IsInitialized(); if (LIKELY(class_linker_initialized)) { - if (UNLIKELY(new_status <= old_status && new_status != kStatusError)) { + if (UNLIKELY(new_status <= old_status && new_status != kStatusError && + new_status != kStatusRetired)) { LOG(FATAL) << "Unexpected change back of class status for " << PrettyClass(this) << " " << old_status << " -> " << new_status; } @@ -87,18 +87,22 @@ void Class::SetStatus(Status new_status, Thread* self) { Handle<mirror::ArtMethod> old_throw_method(hs.NewHandle(old_throw_location.GetMethod())); uint32_t old_throw_dex_pc = old_throw_location.GetDexPc(); bool is_exception_reported = self->IsExceptionReportedToInstrumentation(); - // clear exception to call FindSystemClass - self->ClearException(); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Class* eiie_class = class_linker->FindSystemClass(self, - "Ljava/lang/ExceptionInInitializerError;"); - CHECK(!self->IsExceptionPending()); - - // Only verification errors, not initialization problems, should set a verify error. - // This is to ensure that ThrowEarlierClassFailure will throw NoClassDefFoundError in that case. - Class* exception_class = old_exception->GetClass(); - if (!eiie_class->IsAssignableFrom(exception_class)) { - SetVerifyErrorClass(exception_class); + Class* eiie_class; + // Do't attempt to use FindClass if we have an OOM error since this can try to do more + // allocations and may cause infinite loops. + if (old_exception.Get() == nullptr || + old_exception->GetClass()->GetDescriptor() != "Ljava/lang/OutOfMemoryError;") { + // Clear exception to call FindSystemClass. + self->ClearException(); + eiie_class = Runtime::Current()->GetClassLinker()->FindSystemClass( + self, "Ljava/lang/ExceptionInInitializerError;"); + CHECK(!self->IsExceptionPending()); + // Only verification errors, not initialization problems, should set a verify error. + // This is to ensure that ThrowEarlierClassFailure will throw NoClassDefFoundError in that case. + Class* exception_class = old_exception->GetClass(); + if (!eiie_class->IsAssignableFrom(exception_class)) { + SetVerifyErrorClass(exception_class); + } } // Restore exception. @@ -113,11 +117,27 @@ void Class::SetStatus(Status new_status, Thread* self) { } else { SetField32Volatile<false>(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status); } - // Classes that are being resolved or initialized need to notify waiters that the class status - // changed. See ClassLinker::EnsureResolved and ClassLinker::WaitForInitializeClass. - if ((old_status >= kStatusResolved || new_status >= kStatusResolved) && - class_linker_initialized) { - NotifyAll(self); + + if (!class_linker_initialized) { + // When the class linker is being initialized its single threaded and by definition there can be + // no waiters. During initialization classes may appear temporary but won't be retired as their + // size was statically computed. + } else { + // Classes that are being resolved or initialized need to notify waiters that the class status + // changed. See ClassLinker::EnsureResolved and ClassLinker::WaitForInitializeClass. + if (IsTemp()) { + // Class is a temporary one, ensure that waiters for resolution get notified of retirement + // so that they can grab the new version of the class from the class linker's table. + CHECK_LT(new_status, kStatusResolved) << PrettyDescriptor(this); + if (new_status == kStatusRetired || new_status == kStatusError) { + NotifyAll(self); + } + } else { + CHECK_NE(new_status, kStatusRetired); + if (old_status >= kStatusResolved || new_status >= kStatusResolved) { + NotifyAll(self); + } + } } } @@ -217,35 +237,39 @@ void Class::DumpClass(std::ostream& os, int flags) { os << StringPrintf(" %2zd: %s (cl=%p)\n", i, PrettyClass(interface).c_str(), cl); } } - // After this point, this may have moved due to GetDirectInterface. - os << " vtable (" << h_this->NumVirtualMethods() << " entries, " - << (h_super.Get() != nullptr ? h_super->NumVirtualMethods() : 0) << " in super):\n"; - for (size_t i = 0; i < NumVirtualMethods(); ++i) { - os << StringPrintf(" %2zd: %s\n", i, - PrettyMethod(h_this->GetVirtualMethodDuringLinking(i)).c_str()); - } - os << " direct methods (" << h_this->NumDirectMethods() << " entries):\n"; - for (size_t i = 0; i < h_this->NumDirectMethods(); ++i) { - os << StringPrintf(" %2zd: %s\n", i, PrettyMethod(h_this->GetDirectMethod(i)).c_str()); - } - if (h_this->NumStaticFields() > 0) { - os << " static fields (" << h_this->NumStaticFields() << " entries):\n"; - if (h_this->IsResolved() || h_this->IsErroneous()) { - for (size_t i = 0; i < h_this->NumStaticFields(); ++i) { - os << StringPrintf(" %2zd: %s\n", i, PrettyField(h_this->GetStaticField(i)).c_str()); + if (!IsLoaded()) { + os << " class not yet loaded"; + } else { + // After this point, this may have moved due to GetDirectInterface. + os << " vtable (" << h_this->NumVirtualMethods() << " entries, " + << (h_super.Get() != nullptr ? h_super->NumVirtualMethods() : 0) << " in super):\n"; + for (size_t i = 0; i < NumVirtualMethods(); ++i) { + os << StringPrintf(" %2zd: %s\n", i, + PrettyMethod(h_this->GetVirtualMethodDuringLinking(i)).c_str()); + } + os << " direct methods (" << h_this->NumDirectMethods() << " entries):\n"; + for (size_t i = 0; i < h_this->NumDirectMethods(); ++i) { + os << StringPrintf(" %2zd: %s\n", i, PrettyMethod(h_this->GetDirectMethod(i)).c_str()); + } + if (h_this->NumStaticFields() > 0) { + os << " static fields (" << h_this->NumStaticFields() << " entries):\n"; + if (h_this->IsResolved() || h_this->IsErroneous()) { + for (size_t i = 0; i < h_this->NumStaticFields(); ++i) { + os << StringPrintf(" %2zd: %s\n", i, PrettyField(h_this->GetStaticField(i)).c_str()); + } + } else { + os << " <not yet available>"; } - } else { - os << " <not yet available>"; } - } - if (h_this->NumInstanceFields() > 0) { - os << " instance fields (" << h_this->NumInstanceFields() << " entries):\n"; - if (h_this->IsResolved() || h_this->IsErroneous()) { - for (size_t i = 0; i < h_this->NumInstanceFields(); ++i) { - os << StringPrintf(" %2zd: %s\n", i, PrettyField(h_this->GetInstanceField(i)).c_str()); + if (h_this->NumInstanceFields() > 0) { + os << " instance fields (" << h_this->NumInstanceFields() << " entries):\n"; + if (h_this->IsResolved() || h_this->IsErroneous()) { + for (size_t i = 0; i < h_this->NumInstanceFields(); ++i) { + os << StringPrintf(" %2zd: %s\n", i, PrettyField(h_this->GetInstanceField(i)).c_str()); + } + } else { + os << " <not yet available>"; } - } else { - os << " <not yet available>"; } } } @@ -721,9 +745,7 @@ uint32_t Class::NumDirectInterfaces() { } else if (IsArrayClass()) { return 2; } else if (IsProxyClass()) { - mirror::SynthesizedProxyClass* proxy_class= - reinterpret_cast<mirror::SynthesizedProxyClass*>(this); - mirror::ObjectArray<mirror::Class>* interfaces = proxy_class->GetInterfaces(); + mirror::ObjectArray<mirror::Class>* interfaces = GetInterfaces(); return interfaces != nullptr ? interfaces->GetLength() : 0; } else { const DexFile::TypeList* interfaces = GetInterfaceTypeList(); @@ -753,9 +775,7 @@ mirror::Class* Class::GetDirectInterface(Thread* self, Handle<mirror::Class> kla return class_linker->FindSystemClass(self, "Ljava/io/Serializable;"); } } else if (klass->IsProxyClass()) { - mirror::SynthesizedProxyClass* proxy_class = - reinterpret_cast<mirror::SynthesizedProxyClass*>(klass.Get()); - mirror::ObjectArray<mirror::Class>* interfaces = proxy_class->GetInterfaces(); + mirror::ObjectArray<mirror::Class>* interfaces = klass.Get()->GetInterfaces(); DCHECK(interfaces != nullptr); return interfaces->Get(idx); } else { @@ -798,5 +818,49 @@ const DexFile::TypeList* Class::GetInterfaceTypeList() { return GetDexFile().GetInterfacesList(*class_def); } +void Class::PopulateEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ObjectArray<ArtMethod>* table = GetImTable(); + if (table != nullptr) { + for (uint32_t i = 0; i < kImtSize; i++) { + SetEmbeddedImTableEntry(i, table->Get(i)); + } + } + + table = GetVTableDuringLinking(); + CHECK(table != nullptr); + for (int32_t i = 0; i < table->GetLength(); i++) { + SetEmbeddedVTableEntry(i, table->Get(i)); + } +} + +Class* Class::CopyOf(Thread* self, int32_t new_length) { + DCHECK_GE(new_length, static_cast<int32_t>(sizeof(Class))); + // We may get copied by a compacting GC. + StackHandleScope<1> hs(self); + Handle<mirror::Class> h_this(hs.NewHandle(this)); + gc::Heap* heap = Runtime::Current()->GetHeap(); + InitializeClassVisitor visitor(new_length); + + mirror::Object* new_class = + kMovingClasses ? heap->AllocObject<true>(self, java_lang_Class_, new_length, visitor) + : heap->AllocNonMovableObject<true>(self, java_lang_Class_, new_length, visitor); + if (UNLIKELY(new_class == nullptr)) { + CHECK(self->IsExceptionPending()); // Expect an OOME. + return NULL; + } + + mirror::Class* new_class_obj = new_class->AsClass(); + memcpy(new_class_obj, h_this.Get(), sizeof(Class)); + + new_class_obj->SetStatus(kStatusResolving, self); + new_class_obj->PopulateEmbeddedImtAndVTable(); + // Correct some fields. + new_class_obj->SetLockWord(LockWord(), false); + new_class_obj->SetClassSize(new_length); + + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(new_class_obj); + return new_class_obj; +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index e735c45390..648bddeca5 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -22,6 +22,7 @@ #include "invoke_type.h" #include "modifiers.h" #include "object.h" +#include "object_array.h" #include "object_callbacks.h" #include "primitive.h" #include "read_barrier.h" @@ -62,7 +63,6 @@ namespace art { -struct ClassClassOffsets; struct ClassOffsets; class Signature; class StringPiece; @@ -70,13 +70,29 @@ class StringPiece; namespace mirror { class ArtField; +class ArtMethod; class ClassLoader; class DexCache; class IfTable; // C++ mirror of java.lang.Class -class MANAGED Class : public Object { +class MANAGED Class FINAL : public Object { public: + // Interface method table size. Increasing this value reduces the chance of two interface methods + // colliding in the interface method table but increases the size of classes that implement + // (non-marker) interfaces. + static constexpr size_t kImtSize = 64; + + // imtable entry embedded in class object. + struct MANAGED ImTableEntry { + HeapReference<ArtMethod> method; + }; + + // vtable entry embedded in class object. + struct MANAGED VTableEntry { + HeapReference<ArtMethod> method; + }; + // Class Status // // kStatusNotReady: If a Class cannot be found in the class table by @@ -95,6 +111,11 @@ class MANAGED Class : public Object { // using ResolveClass to initialize the super_class_ and ensuring the // interfaces are resolved. // + // kStatusResolving: Class is just cloned with the right size from + // temporary class that's acting as a placeholder for linking. The old + // class will be retired. New class is set to this status first before + // moving on to being resolved. + // // kStatusResolved: Still holding the lock on Class, the ClassLinker // shows linking is complete and fields of the Class populated by making // it kStatusResolved. Java allows circularities of the form where a super @@ -109,18 +130,20 @@ class MANAGED Class : public Object { // // TODO: Explain the other states enum Status { + kStatusRetired = -2, kStatusError = -1, kStatusNotReady = 0, kStatusIdx = 1, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_. kStatusLoaded = 2, // DEX idx values resolved. - kStatusResolved = 3, // Part of linking. - kStatusVerifying = 4, // In the process of being verified. - kStatusRetryVerificationAtRuntime = 5, // Compile time verification failed, retry at runtime. - kStatusVerifyingAtRuntime = 6, // Retrying verification at runtime. - kStatusVerified = 7, // Logically part of linking; done pre-init. - kStatusInitializing = 8, // Class init in progress. - kStatusInitialized = 9, // Ready to go. - kStatusMax = 10, + kStatusResolving = 3, // Just cloned from temporary class object. + kStatusResolved = 4, // Part of linking. + kStatusVerifying = 5, // In the process of being verified. + kStatusRetryVerificationAtRuntime = 6, // Compile time verification failed, retry at runtime. + kStatusVerifyingAtRuntime = 7, // Retrying verification at runtime. + kStatusVerified = 8, // Logically part of linking; done pre-init. + kStatusInitializing = 9, // Class init in progress. + kStatusInitialized = 10, // Ready to go. + kStatusMax = 11, }; template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> @@ -136,6 +159,12 @@ class MANAGED Class : public Object { return OFFSET_OF_OBJECT_MEMBER(Class, status_); } + // Returns true if the class has been retired. + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + bool IsRetired() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetStatus<kVerifyFlags>() == kStatusRetired; + } + // Returns true if the class has failed to link. template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool IsErroneous() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -232,7 +261,7 @@ class MANAGED Class : public Object { } template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - bool IsReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + bool IsTypeOfReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return (GetAccessFlags<kVerifyFlags>() & kAccClassIsReference) != 0; } @@ -274,6 +303,13 @@ class MANAGED Class : public Object { } } + // Returns true if this class is the placeholder and should retire and + // be replaced with a class with the right size for embedded imt/vtable. + bool IsTemp() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Status s = GetStatus(); + return s < Status::kStatusResolving && ShouldHaveEmbeddedImtAndVTable(); + } + String* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Returns the cached name. void SetName(String* name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Sets the cached name. // Computes the name, then sets the cached value. @@ -383,6 +419,9 @@ class MANAGED Class : public Object { template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsArtMethodClass() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier> + bool IsReferenceClass() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static MemberOffset ComponentTypeOffset() { return OFFSET_OF_OBJECT_MEMBER(Class, component_type_); } @@ -451,6 +490,25 @@ class MANAGED Class : public Object { void SetClassSize(uint32_t new_class_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Compute how many bytes would be used a class with the given elements. + static uint32_t ComputeClassSize(bool has_embedded_tables, + uint32_t num_vtable_entries, + uint32_t num_32bit_static_fields, + uint32_t num_64bit_static_fields, + uint32_t num_ref_static_fields); + + // 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; + return ComputeClassSize(true, vtable_entries, 0, 1, 0); + } + + // The size of a java.lang.Class representing a primitive such as int.class. + static uint32_t PrimitiveClassSize() { + return ComputeClassSize(false, 0, 0, 0, 0); + } + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, ReadBarrierOption kReadBarrierOption = kWithReadBarrier> uint32_t GetObjectSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -623,8 +681,6 @@ class MANAGED Class : public Object { return OFFSET_OF_OBJECT_MEMBER(Class, vtable_); } - ObjectArray<ArtMethod>* GetImTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void SetImTable(ObjectArray<ArtMethod>* new_imtable) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -632,6 +688,26 @@ class MANAGED Class : public Object { return OFFSET_OF_OBJECT_MEMBER(Class, imtable_); } + static MemberOffset EmbeddedImTableOffset() { + return MemberOffset(sizeof(Class)); + } + + static MemberOffset EmbeddedVTableOffset() { + return MemberOffset(sizeof(Class) + kImtSize * sizeof(mirror::Class::ImTableEntry)); + } + + bool ShouldHaveEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return IsInstantiable(); + } + + ArtMethod* GetEmbeddedImTableEntry(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void SetEmbeddedImTableEntry(uint32_t i, ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void SetEmbeddedVTableEntry(uint32_t i, ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void PopulateEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Given a method implemented by this class but potentially from a super class, return the // specific implementation method for this class. ArtMethod* FindVirtualMethodForVirtual(ArtMethod* method) @@ -739,11 +815,6 @@ class MANAGED Class : public Object { void SetReferenceInstanceOffsets(uint32_t new_reference_offsets) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Beginning of static field data - static MemberOffset FieldsOffset() { - return OFFSET_OF_OBJECT_MEMBER(Class, fields_); - } - // Returns the number of static fields containing reference types. uint32_t NumReferenceStaticFields() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(IsResolved() || IsErroneous()); @@ -751,7 +822,7 @@ class MANAGED Class : public Object { } uint32_t NumReferenceStaticFieldsDuringLinking() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(IsLoaded() || IsErroneous()); + DCHECK(IsLoaded() || IsErroneous() || IsRetired()); return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_)); } @@ -865,21 +936,71 @@ class MANAGED Class : public Object { template <bool kVisitClass, typename Visitor> void VisitReferences(mirror::Class* klass, const Visitor& visitor) - NO_THREAD_SAFETY_ANALYSIS; + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Visit references within the embedded tables of the class. + // TODO: remove NO_THREAD_SAFETY_ANALYSIS when annotalysis handles visitors better. + template<typename Visitor> + void VisitEmbeddedImtAndVTable(const Visitor& visitor) NO_THREAD_SAFETY_ANALYSIS; std::string GetDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool DescriptorEquals(const char* match) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + std::string GetArrayDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const DexFile::ClassDef* GetClassDef() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + uint32_t NumDirectInterfaces() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + uint16_t GetDirectInterfaceTypeIdx(uint32_t idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static mirror::Class* GetDirectInterface(Thread* self, Handle<mirror::Class> klass, uint32_t idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const char* GetSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + std::string GetLocation() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const DexFile& GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const DexFile::TypeList* GetInterfaceTypeList() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Asserts we are initialized or initializing in the given thread. + void AssertInitializedOrInitializingInThread(Thread* self) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + Class* CopyOf(Thread* self, int32_t new_length) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // For proxy class only. + ObjectArray<Class>* GetInterfaces() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // For proxy class only. + ObjectArray<ObjectArray<Class>>* GetThrows() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // For reference class only. + MemberOffset GetDisableIntrinsicFlagOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + MemberOffset GetSlowPathFlagOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool GetSlowPathEnabled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SetSlowPath(bool enabled) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Used to initialize a class in the allocation code path to ensure it is guarded by a StoreStore + // fence. + class InitializeClassVisitor { + public: + explicit InitializeClassVisitor(uint32_t class_size) : class_size_(class_size) { + } + + void operator()(mirror::Object* obj, size_t usable_size) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + const uint32_t class_size_; + + DISALLOW_COPY_AND_ASSIGN(InitializeClassVisitor); + }; + private: void SetVerifyErrorClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -898,6 +1019,8 @@ class MANAGED Class : public Object { void CheckObjectAlloc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ObjectArray<ArtMethod>* GetImTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // defining class loader, or NULL for the "bootstrap" system loader HeapReference<ClassLoader> class_loader_; @@ -1008,7 +1131,12 @@ class MANAGED Class : public Object { // values are kept in a table in gDvm. // InitiatingLoaderList initiating_loader_list_; - // Location of first static field. + // The following data exist in real class objects. + // Embedded Imtable, for class object that's not an interface, fixed size. + ImTableEntry embedded_imtable_[0]; + // Embedded Vtable, for class object that's not an interface, variable size. + VTableEntry embedded_vtable_[0]; + // Static fields, variable size. uint32_t fields_[0]; // java.lang.Class @@ -1020,14 +1148,6 @@ class MANAGED Class : public Object { std::ostream& operator<<(std::ostream& os, const Class::Status& rhs); -class MANAGED ClassClass : public Class { - private: - int32_t pad_; - int64_t serialVersionUID_; - friend struct art::ClassClassOffsets; // for verifying offset information - DISALLOW_IMPLICIT_CONSTRUCTORS(ClassClass); -}; - } // namespace mirror } // namespace art diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h index 74dae386f3..f3594e40d5 100644 --- a/runtime/mirror/class_loader.h +++ b/runtime/mirror/class_loader.h @@ -27,6 +27,12 @@ namespace mirror { // C++ mirror of java.lang.ClassLoader class MANAGED ClassLoader : public Object { + public: + // Size of an instance of java.lang.ClassLoader. + static constexpr uint32_t InstanceSize() { + return sizeof(ClassLoader); + } + private: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". HeapReference<Object> packages_; diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 7e40f643cb..08cff999b1 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -24,6 +24,11 @@ namespace art { namespace mirror { +inline uint32_t DexCache::ClassSize() { + uint32_t vtable_entries = Object::kVTableLength + 1; + return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0); +} + inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ArtMethod* method = GetResolvedMethods()->Get(method_idx); diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index 65a50260f9..bfd603a185 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -18,10 +18,8 @@ #define ART_RUNTIME_MIRROR_DEX_CACHE_H_ #include "art_method.h" -#include "class.h" #include "object.h" #include "object_array.h" -#include "string.h" namespace art { @@ -33,15 +31,21 @@ union JValue; namespace mirror { class ArtField; +class ArtMethod; class Class; +class String; -class MANAGED DexCacheClass : public Class { - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(DexCacheClass); -}; - -class MANAGED DexCache : public Object { +// C++ mirror of java.lang.DexCache. +class MANAGED DexCache FINAL : public Object { public: + // Size of java.lang.DexCache.class. + static uint32_t ClassSize(); + + // Size of an instance of java.lang.DexCache not including referenced values. + static constexpr uint32_t InstanceSize() { + return sizeof(DexCache); + } + void Init(const DexFile* dex_file, String* location, ObjectArray<String>* strings, diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index 3d28dc603f..ef6fc67c75 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -24,6 +24,7 @@ #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" #include "handle_scope-inl.h" +#include "scoped_thread_state_change.h" namespace art { namespace mirror { diff --git a/runtime/mirror/iftable.h b/runtime/mirror/iftable.h index ad312ed229..5feb602a9e 100644 --- a/runtime/mirror/iftable.h +++ b/runtime/mirror/iftable.h @@ -23,7 +23,7 @@ namespace art { namespace mirror { -class MANAGED IfTable : public ObjectArray<Object> { +class MANAGED IfTable FINAL : public ObjectArray<Object> { public: Class* GetInterface(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Class* interface = Get((i * kMax) + kInterface)->AsClass(); diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 089ef57310..9dbfb56c79 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -35,6 +35,11 @@ namespace art { namespace mirror { +inline uint32_t Object::ClassSize() { + uint32_t vtable_entries = kVTableLength; + return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0); +} + template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline Class* Object::GetClass() { return GetFieldObject<Class, kVerifyFlags, kReadBarrierOption>( @@ -75,6 +80,12 @@ inline bool Object::CasLockWordWeakSequentiallyConsistent(LockWord old_val, Lock OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(), new_val.GetValue()); } +inline bool Object::CasLockWordWeakRelaxed(LockWord old_val, LockWord new_val) { + // Force use of non-transactional mode and do not check. + return CasFieldWeakRelaxed32<false, false>( + OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(), new_val.GetValue()); +} + inline uint32_t Object::GetLockOwnerThreadId() { return Monitor::GetLockOwnerThreadId(this); } @@ -238,7 +249,7 @@ inline ArtMethod* Object::AsArtMethod() { template<VerifyObjectFlags kVerifyFlags> inline bool Object::IsReferenceInstance() { - return GetClass<kVerifyFlags>()->IsReferenceClass(); + return GetClass<kVerifyFlags>()->IsTypeOfReferenceClass(); } template<VerifyObjectFlags kVerifyFlags> @@ -443,6 +454,8 @@ inline void Object::SetField32Volatile(MemberOffset field_offset, int32_t new_va SetField32<kTransactionActive, kCheckTransaction, kVerifyFlags, true>(field_offset, new_value); } +// TODO: Pass memory_order_ and strong/weak as arguments to avoid code duplication? + template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> inline bool Object::CasFieldWeakSequentiallyConsistent32(MemberOffset field_offset, int32_t old_value, int32_t new_value) { @@ -461,6 +474,42 @@ inline bool Object::CasFieldWeakSequentiallyConsistent32(MemberOffset field_offs return atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_value, new_value); } +template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> +inline bool Object::CasFieldWeakRelaxed32(MemberOffset field_offset, + int32_t old_value, int32_t new_value) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true); + } + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value(); + AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr); + + return atomic_addr->CompareExchangeWeakRelaxed(old_value, new_value); +} + +template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> +inline bool Object::CasFieldStrongSequentiallyConsistent32(MemberOffset field_offset, + int32_t old_value, int32_t new_value) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true); + } + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value(); + AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr); + + return atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_value, new_value); +} + template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile> inline int64_t Object::GetField64(MemberOffset field_offset) { if (kVerifyFlags & kVerifyThis) { @@ -526,6 +575,23 @@ inline bool Object::CasFieldWeakSequentiallyConsistent64(MemberOffset field_offs return atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_value, new_value); } +template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> +inline bool Object::CasFieldStrongSequentiallyConsistent64(MemberOffset field_offset, + int64_t old_value, int64_t new_value) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + Runtime::Current()->RecordWriteField64(this, field_offset, old_value, true); + } + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value(); + Atomic<int64_t>* atomic_addr = reinterpret_cast<Atomic<int64_t>*>(raw_addr); + return atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_value, new_value); +} + template<class T, VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption, bool kIsVolatile> inline T* Object::GetFieldObject(MemberOffset field_offset) { @@ -644,6 +710,38 @@ inline bool Object::CasFieldWeakSequentiallyConsistentObject(MemberOffset field_ return success; } +template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags> +inline bool Object::CasFieldStrongSequentiallyConsistentObject(MemberOffset field_offset, + Object* old_value, Object* new_value) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + if (kVerifyFlags & kVerifyWrites) { + VerifyObject(new_value); + } + if (kVerifyFlags & kVerifyReads) { + VerifyObject(old_value); + } + if (kTransactionActive) { + Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true); + } + HeapReference<Object> old_ref(HeapReference<Object>::FromMirrorPtr(old_value)); + HeapReference<Object> new_ref(HeapReference<Object>::FromMirrorPtr(new_value)); + byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value(); + Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr); + + bool success = atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_ref.reference_, + new_ref.reference_); + + if (success) { + Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value); + } + return success; +} + template<bool kVisitClass, bool kIsStatic, typename Visitor> inline void Object::VisitFieldsReferences(uint32_t ref_offsets, const Visitor& visitor) { if (LIKELY(ref_offsets != CLASS_WALK_SUPER)) { @@ -687,6 +785,7 @@ inline void Object::VisitInstanceFieldsReferences(mirror::Class* klass, const Vi template<bool kVisitClass, typename Visitor> inline void Object::VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor) { + DCHECK(!klass->IsTemp()); klass->VisitFieldsReferences<kVisitClass, true>( klass->GetReferenceStaticOffsets<kVerifyNone>(), visitor); } @@ -707,7 +806,7 @@ inline void Object::VisitReferences(const Visitor& visitor, } else { DCHECK(!klass->IsVariableSize()); VisitInstanceFieldsReferences<kVisitClass>(klass, visitor); - if (UNLIKELY(klass->IsReferenceClass<kVerifyNone>())) { + if (UNLIKELY(klass->IsTypeOfReferenceClass<kVerifyNone>())) { ref_visitor(klass, AsReference()); } } diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index e58091fe09..961bc64819 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -24,13 +24,13 @@ #include "class.h" #include "class-inl.h" #include "class_linker-inl.h" +#include "field_helper.h" #include "gc/accounting/card_table-inl.h" #include "gc/heap.h" #include "iftable-inl.h" #include "monitor.h" #include "object-inl.h" #include "object_array-inl.h" -#include "object_utils.h" #include "runtime.h" #include "handle_scope-inl.h" #include "throwable.h" @@ -57,7 +57,7 @@ class CopyReferenceFieldsWithReadBarrierVisitor { ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Copy java.lang.ref.Reference.referent which isn't visited in // Object::VisitReferences(). - DCHECK(klass->IsReferenceClass()); + DCHECK(klass->IsTypeOfReferenceClass()); this->operator()(ref, mirror::Reference::ReferentOffset(), false); } @@ -156,7 +156,7 @@ int32_t Object::IdentityHashCode() const { // loop iteration. LockWord hash_word(LockWord::FromHashCode(GenerateIdentityHashCode())); DCHECK_EQ(hash_word.GetState(), LockWord::kHashCode); - if (const_cast<Object*>(this)->CasLockWordWeakSequentiallyConsistent(lw, hash_word)) { + if (const_cast<Object*>(this)->CasLockWordWeakRelaxed(lw, hash_word)) { return hash_word.GetHashCode(); } break; diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index d29011a4b5..4fae4704bf 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -63,13 +63,24 @@ static constexpr bool kCheckFieldAssignments = false; // C++ mirror of java.lang.Object class MANAGED LOCKABLE Object { public: + // The number of vtable entries in java.lang.Object. + static constexpr size_t kVTableLength = 11; + + // The size of the java.lang.Class representing a java.lang.Object. + static uint32_t ClassSize(); + + // Size of an instance of java.lang.Object. + static constexpr uint32_t InstanceSize() { + return sizeof(Object); + } + static MemberOffset ClassOffset() { return OFFSET_OF_OBJECT_MEMBER(Object, klass_); } template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, ReadBarrierOption kReadBarrierOption = kWithReadBarrier> - Class* GetClass() ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ALWAYS_INLINE Class* GetClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> void SetClass(Class* new_klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -99,19 +110,14 @@ class MANAGED LOCKABLE Object { return OFFSET_OF_OBJECT_MEMBER(Object, monitor_); } - // As volatile can be false if the mutators are suspended. This is an optimization since it + // As_volatile can be false if the mutators are suspended. This is an optimization since it // avoids the barriers. LockWord GetLockWord(bool as_volatile) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void SetLockWord(LockWord new_val, bool as_volatile) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // All Cas operations defined here have C++11 memory_order_seq_cst ordering - // semantics: Preceding memory operations become visible to other threads - // before the CAS, and subsequent operations become visible after the CAS. - // The Cas operations defined here do not fail spuriously, i.e. they - // have C++11 "strong" semantics. - // TODO: In most, possibly all, cases, these assumptions are too strong. - // Confirm and weaken the implementation. bool CasLockWordWeakSequentiallyConsistent(LockWord old_val, LockWord new_val) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool CasLockWordWeakRelaxed(LockWord old_val, LockWord new_val) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); uint32_t GetLockOwnerThreadId(); mirror::Object* MonitorEnter(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) @@ -202,27 +208,27 @@ class MANAGED LOCKABLE Object { // Accessor for Java type fields. template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, ReadBarrierOption kReadBarrierOption = kWithReadBarrier, bool kIsVolatile = false> - T* GetFieldObject(MemberOffset field_offset) ALWAYS_INLINE + ALWAYS_INLINE T* GetFieldObject(MemberOffset field_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, ReadBarrierOption kReadBarrierOption = kWithReadBarrier> - T* GetFieldObjectVolatile(MemberOffset field_offset) ALWAYS_INLINE + ALWAYS_INLINE T* GetFieldObjectVolatile(MemberOffset field_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<bool kTransactionActive, bool kCheckTransaction = true, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false> - void SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value) - ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ALWAYS_INLINE void SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<bool kTransactionActive, bool kCheckTransaction = true, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false> - void SetFieldObject(MemberOffset field_offset, Object* new_value) ALWAYS_INLINE + ALWAYS_INLINE void SetFieldObject(MemberOffset field_offset, Object* new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<bool kTransactionActive, bool kCheckTransaction = true, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - void SetFieldObjectVolatile(MemberOffset field_offset, Object* new_value) ALWAYS_INLINE + ALWAYS_INLINE void SetFieldObjectVolatile(MemberOffset field_offset, Object* new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<bool kTransactionActive, bool kCheckTransaction = true, @@ -231,49 +237,67 @@ class MANAGED LOCKABLE Object { Object* new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive, bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + bool CasFieldStrongSequentiallyConsistentObject(MemberOffset field_offset, Object* old_value, + Object* new_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> HeapReference<Object>* GetFieldObjectReferenceAddr(MemberOffset field_offset); template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false> - int32_t GetField32(MemberOffset field_offset) ALWAYS_INLINE + ALWAYS_INLINE int32_t GetField32(MemberOffset field_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - int32_t GetField32Volatile(MemberOffset field_offset) ALWAYS_INLINE + ALWAYS_INLINE int32_t GetField32Volatile(MemberOffset field_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<bool kTransactionActive, bool kCheckTransaction = true, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false> - void SetField32(MemberOffset field_offset, int32_t new_value) ALWAYS_INLINE + ALWAYS_INLINE void SetField32(MemberOffset field_offset, int32_t new_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + template<bool kTransactionActive, bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + ALWAYS_INLINE void SetField32Volatile(MemberOffset field_offset, int32_t new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<bool kTransactionActive, bool kCheckTransaction = true, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - void SetField32Volatile(MemberOffset field_offset, int32_t new_value) ALWAYS_INLINE + ALWAYS_INLINE bool CasFieldWeakSequentiallyConsistent32(MemberOffset field_offset, + int32_t old_value, int32_t new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<bool kTransactionActive, bool kCheckTransaction = true, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - bool CasFieldWeakSequentiallyConsistent32(MemberOffset field_offset, int32_t old_value, - int32_t new_value) ALWAYS_INLINE + bool CasFieldWeakRelaxed32(MemberOffset field_offset, int32_t old_value, + int32_t new_value) ALWAYS_INLINE + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + template<bool kTransactionActive, bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + bool CasFieldStrongSequentiallyConsistent32(MemberOffset field_offset, int32_t old_value, + int32_t new_value) ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false> - int64_t GetField64(MemberOffset field_offset) ALWAYS_INLINE + ALWAYS_INLINE int64_t GetField64(MemberOffset field_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - int64_t GetField64Volatile(MemberOffset field_offset) ALWAYS_INLINE + ALWAYS_INLINE int64_t GetField64Volatile(MemberOffset field_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<bool kTransactionActive, bool kCheckTransaction = true, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false> - void SetField64(MemberOffset field_offset, int64_t new_value) ALWAYS_INLINE + ALWAYS_INLINE void SetField64(MemberOffset field_offset, int64_t new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<bool kTransactionActive, bool kCheckTransaction = true, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - void SetField64Volatile(MemberOffset field_offset, int64_t new_value) ALWAYS_INLINE + ALWAYS_INLINE void SetField64Volatile(MemberOffset field_offset, int64_t new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<bool kTransactionActive, bool kCheckTransaction = true, @@ -283,6 +307,12 @@ class MANAGED LOCKABLE Object { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<bool kTransactionActive, bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + bool CasFieldStrongSequentiallyConsistent64(MemberOffset field_offset, int64_t old_value, + int64_t new_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + template<bool kTransactionActive, bool kCheckTransaction = true, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, typename T> void SetFieldPtr(MemberOffset field_offset, T new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h index 54d12401fe..7012b197b7 100644 --- a/runtime/mirror/object_array.h +++ b/runtime/mirror/object_array.h @@ -23,8 +23,13 @@ namespace art { namespace mirror { template<class T> -class MANAGED ObjectArray : public Array { +class MANAGED ObjectArray: public Array { public: + // The size of Object[].class. + static uint32_t ClassSize() { + return Array::ClassSize(); + } + static ObjectArray<T>* Alloc(Thread* self, Class* object_array_class, int32_t length, gc::AllocatorType allocator_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index f85fb2748b..a7ea6c9c08 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -28,7 +28,7 @@ #include "class_linker-inl.h" #include "common_runtime_test.h" #include "dex_file.h" -#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "gc/accounting/card_table-inl.h" #include "gc/heap.h" #include "iftable-inl.h" @@ -36,6 +36,7 @@ #include "object-inl.h" #include "object_array-inl.h" #include "handle_scope-inl.h" +#include "scoped_thread_state_change.h" #include "string-inl.h" namespace art { diff --git a/runtime/mirror/proxy.h b/runtime/mirror/proxy.h index 6e4947ebf8..db511d6425 100644 --- a/runtime/mirror/proxy.h +++ b/runtime/mirror/proxy.h @@ -25,28 +25,8 @@ struct ProxyOffsets; namespace mirror { -// All proxy objects have a class which is a synthesized proxy class. The synthesized proxy class -// has the static fields used to implement reflection on proxy objects. -class MANAGED SynthesizedProxyClass : public Class { - public: - ObjectArray<Class>* GetInterfaces() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetFieldObject<ObjectArray<Class>>(OFFSET_OF_OBJECT_MEMBER(SynthesizedProxyClass, - interfaces_)); - } - - ObjectArray<ObjectArray<Class>>* GetThrows() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetFieldObject<ObjectArray<ObjectArray<Class>>>(OFFSET_OF_OBJECT_MEMBER(SynthesizedProxyClass, - throws_)); - } - - private: - HeapReference<ObjectArray<Class>> interfaces_; - HeapReference<ObjectArray<ObjectArray<Class>>> throws_; - DISALLOW_IMPLICIT_CONSTRUCTORS(SynthesizedProxyClass); -}; - // C++ mirror of java.lang.reflect.Proxy. -class MANAGED Proxy : public Object { +class MANAGED Proxy FINAL : public Object { private: HeapReference<Object> h_; diff --git a/runtime/mirror/reference-inl.h b/runtime/mirror/reference-inl.h index 43767c8792..b353402602 100644 --- a/runtime/mirror/reference-inl.h +++ b/runtime/mirror/reference-inl.h @@ -22,6 +22,11 @@ namespace art { namespace mirror { +inline uint32_t Reference::ClassSize() { + uint32_t vtable_entries = Object::kVTableLength + 5; + return Class::ComputeClassSize(false, vtable_entries, 2, 0, 0); +} + inline bool Reference::IsEnqueuable() { // Not using volatile reads as an optimization since this is only called with all the mutators // suspended. diff --git a/runtime/mirror/reference.cc b/runtime/mirror/reference.cc new file mode 100644 index 0000000000..077cd4b913 --- /dev/null +++ b/runtime/mirror/reference.cc @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "reference.h" + +namespace art { +namespace mirror { + +Class* Reference::java_lang_ref_Reference_ = nullptr; + +void Reference::SetClass(Class* java_lang_ref_Reference) { + CHECK(java_lang_ref_Reference_ == nullptr); + CHECK(java_lang_ref_Reference != nullptr); + java_lang_ref_Reference_ = java_lang_ref_Reference; +} + +void Reference::ResetClass() { + CHECK(java_lang_ref_Reference_ != nullptr); + java_lang_ref_Reference_ = nullptr; +} + +void Reference::VisitRoots(RootCallback* callback, void* arg) { + if (java_lang_ref_Reference_ != nullptr) { + callback(reinterpret_cast<mirror::Object**>(&java_lang_ref_Reference_), + arg, 0, kRootStickyClass); + } +} + +} // namespace mirror +} // namespace art diff --git a/runtime/mirror/reference.h b/runtime/mirror/reference.h index 9c9d87be01..07d47d31e7 100644 --- a/runtime/mirror/reference.h +++ b/runtime/mirror/reference.h @@ -17,7 +17,11 @@ #ifndef ART_RUNTIME_MIRROR_REFERENCE_H_ #define ART_RUNTIME_MIRROR_REFERENCE_H_ +#include "class.h" #include "object.h" +#include "object_callbacks.h" +#include "read_barrier.h" +#include "thread.h" namespace art { @@ -36,6 +40,14 @@ namespace mirror { // C++ mirror of java.lang.ref.Reference class MANAGED Reference : public Object { public: + // Size of java.lang.ref.Reference.class. + static uint32_t ClassSize(); + + // Size of an instance of java.lang.ref.Reference. + static constexpr uint32_t InstanceSize() { + return sizeof(Reference); + } + static MemberOffset PendingNextOffset() { return OFFSET_OF_OBJECT_MEMBER(Reference, pending_next_); } @@ -80,6 +92,16 @@ class MANAGED Reference : public Object { bool IsEnqueuable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier> + static Class* GetJavaLangRefReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(java_lang_ref_Reference_ != nullptr); + return ReadBarrier::BarrierForRoot<mirror::Class, kReadBarrierOption>( + &java_lang_ref_Reference_); + } + static void SetClass(Class* klass); + static void ResetClass(void); + static void VisitRoots(RootCallback* callback, void* arg); + private: // Note: This avoids a read barrier, it should only be used by the GC. HeapReference<Object>* GetReferentReferenceAddr() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -92,6 +114,8 @@ class MANAGED Reference : public Object { HeapReference<Reference> queue_next_; // Note this is Java volatile: HeapReference<Object> referent_; // Note this is Java volatile: + static Class* java_lang_ref_Reference_; + friend struct art::ReferenceOffsets; // for verifying offset information friend class gc::ReferenceProcessor; friend class gc::ReferenceQueue; diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h index abecbc5339..52b0927756 100644 --- a/runtime/mirror/stack_trace_element.h +++ b/runtime/mirror/stack_trace_element.h @@ -29,7 +29,7 @@ struct StackTraceElementOffsets; namespace mirror { // C++ mirror of java.lang.StackTraceElement -class MANAGED StackTraceElement : public Object { +class MANAGED StackTraceElement FINAL : public Object { public: String* GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, declaring_class_)); diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index 315f7b1951..6736497593 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_MIRROR_STRING_INL_H_ #include "array.h" +#include "class.h" #include "intern_table.h" #include "runtime.h" #include "string.h" @@ -26,6 +27,11 @@ namespace art { namespace mirror { +inline uint32_t String::ClassSize() { + uint32_t vtable_entries = Object::kVTableLength + 51; + return Class::ComputeClassSize(true, vtable_entries, 1, 1, 2); +} + inline CharArray* String::GetCharArray() { return GetFieldObject<CharArray>(ValueOffset()); } diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index b8acede63b..46bdd59b5c 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -19,22 +19,29 @@ #include <gtest/gtest.h> -#include "class.h" +#include "object.h" #include "object_callbacks.h" #include "read_barrier.h" namespace art { template<class T> class Handle; -struct StringClassOffsets; struct StringOffsets; class StringPiece; namespace mirror { // C++ mirror of java.lang.String -class MANAGED String : public Object { +class MANAGED String FINAL : public Object { public: + // Size of java.lang.String.class. + static uint32_t ClassSize(); + + // Size of an instance of java.lang.String not including its value array. + static constexpr uint32_t InstanceSize() { + return sizeof(String); + } + static MemberOffset CountOffset() { return OFFSET_OF_OBJECT_MEMBER(String, count_); } @@ -160,16 +167,6 @@ class MANAGED String : public Object { DISALLOW_IMPLICIT_CONSTRUCTORS(String); }; -class MANAGED StringClass : public Class { - private: - HeapReference<CharArray> ASCII_; - HeapReference<Object> CASE_INSENSITIVE_ORDER_; - uint32_t REPLACEMENT_CHAR_; - int64_t serialVersionUID_; - friend struct art::StringClassOffsets; // for verifying offset information - DISALLOW_IMPLICIT_CONSTRUCTORS(StringClass); -}; - } // namespace mirror } // namespace art diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc index 6efc9e2f54..1c3f1ed5bf 100644 --- a/runtime/mirror/throwable.cc +++ b/runtime/mirror/throwable.cc @@ -23,7 +23,6 @@ #include "object-inl.h" #include "object_array.h" #include "object_array-inl.h" -#include "object_utils.h" #include "stack_trace_element.h" #include "utils.h" #include "well_known_classes.h" diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 5633a77b6f..4b26edac61 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -28,7 +28,6 @@ #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" -#include "object_utils.h" #include "scoped_thread_state_change.h" #include "thread.h" #include "thread_list.h" @@ -746,7 +745,11 @@ mirror::Object* Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { contention_count++; Runtime* runtime = Runtime::Current(); if (contention_count <= runtime->GetMaxSpinsBeforeThinkLockInflation()) { - NanoSleep(1000); // Sleep for 1us and re-attempt. + // TODO: Consider switch thread state to kBlocked when we are yielding. + // Use sched_yield instead of NanoSleep since NanoSleep can wait much longer than the + // parameter you pass in. This can cause thread suspension to take excessively long + // make long pauses. See b/16307460. + sched_yield(); } else { contention_count = 0; InflateThinLocked(self, h_obj, lock_word, 0); diff --git a/runtime/monitor_pool_test.cc b/runtime/monitor_pool_test.cc index cddc245a42..e1837f52ab 100644 --- a/runtime/monitor_pool_test.cc +++ b/runtime/monitor_pool_test.cc @@ -17,6 +17,8 @@ #include "monitor_pool.h" #include "common_runtime_test.h" +#include "scoped_thread_state_change.h" +#include "thread-inl.h" namespace art { diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc index bdba494e14..af24368e2f 100644 --- a/runtime/monitor_test.cc +++ b/runtime/monitor_test.cc @@ -24,6 +24,7 @@ #include "handle_scope-inl.h" #include "mirror/class-inl.h" #include "mirror/string-inl.h" // Strings are easiest to allocate +#include "scoped_thread_state_change.h" #include "thread_pool.h" #include "utils.h" @@ -31,7 +32,7 @@ namespace art { class MonitorTest : public CommonRuntimeTest { protected: - void SetUpRuntimeOptions(Runtime::Options *options) OVERRIDE { + void SetUpRuntimeOptions(RuntimeOptions *options) OVERRIDE { // Use a smaller heap for (std::pair<std::string, const void*>& pair : *options) { if (pair.first.find("-Xmx") == 0) { diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index f1a987f80a..b0b64aac16 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -30,7 +30,6 @@ #include "mirror/class-inl.h" #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "runtime.h" #include "scoped_fast_native_object_access.h" #include "scoped_thread_state_change.h" diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index e619dda133..e577c2c960 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -21,8 +21,6 @@ #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" -#include "mirror/proxy.h" -#include "object_utils.h" #include "scoped_thread_state_change.h" #include "scoped_fast_native_object_access.h" #include "ScopedLocalRef.h" @@ -91,8 +89,7 @@ static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) { static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) { ScopedFastNativeObjectAccess soa(env); - mirror::SynthesizedProxyClass* c = - down_cast<mirror::SynthesizedProxyClass*>(DecodeClass(soa, javaThis)); + mirror::Class* c = DecodeClass(soa, javaThis); return soa.AddLocalReference<jobjectArray>(c->GetInterfaces()->Clone(soa.Self())); } diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc index eae4584019..f94e42b260 100644 --- a/runtime/native/java_lang_reflect_Array.cc +++ b/runtime/native/java_lang_reflect_Array.cc @@ -20,7 +20,6 @@ #include "jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "scoped_fast_native_object_access.h" #include "handle_scope-inl.h" diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index 1981bfd5ab..34cb93ae94 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -20,7 +20,6 @@ #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "reflection.h" #include "scoped_fast_native_object_access.h" #include "well_known_classes.h" diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 3564dfdf47..3903ffcd6a 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -18,11 +18,11 @@ #include "class_linker-inl.h" #include "common_throws.h" #include "dex_file-inl.h" +#include "field_helper.h" #include "jni_internal.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" -#include "object_utils.h" #include "reflection.h" #include "scoped_fast_native_object_access.h" diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index 22e81e4b3a..f029b16746 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -21,8 +21,6 @@ #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" -#include "mirror/proxy.h" -#include "object_utils.h" #include "reflection.h" #include "scoped_fast_native_object_access.h" #include "well_known_classes.h" @@ -39,8 +37,7 @@ static jobject Method_getExceptionTypesNative(JNIEnv* env, jobject javaMethod) { ScopedFastNativeObjectAccess soa(env); mirror::ArtMethod* proxy_method = mirror::ArtMethod::FromReflectedMethod(soa, javaMethod); CHECK(proxy_method->GetDeclaringClass()->IsProxyClass()); - mirror::SynthesizedProxyClass* proxy_class = - down_cast<mirror::SynthesizedProxyClass*>(proxy_method->GetDeclaringClass()); + mirror::Class* proxy_class = proxy_method->GetDeclaringClass(); int throws_index = -1; size_t num_virt_methods = proxy_class->NumVirtualMethods(); for (size_t i = 0; i < num_virt_methods; i++) { diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index 7cc4cac83f..65dece04e6 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -28,8 +28,8 @@ static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj, ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); // JNI must use non transactional mode. - bool success = obj->CasFieldWeakSequentiallyConsistent32<false>(MemberOffset(offset), - expectedValue, newValue); + bool success = obj->CasFieldStrongSequentiallyConsistent32<false>(MemberOffset(offset), + expectedValue, newValue); return success ? JNI_TRUE : JNI_FALSE; } @@ -38,8 +38,8 @@ static jboolean Unsafe_compareAndSwapLong(JNIEnv* env, jobject, jobject javaObj, ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); // JNI must use non transactional mode. - bool success = obj->CasFieldWeakSequentiallyConsistent64<false>(MemberOffset(offset), - expectedValue, newValue); + bool success = obj->CasFieldStrongSequentiallyConsistent64<false>(MemberOffset(offset), + expectedValue, newValue); return success ? JNI_TRUE : JNI_FALSE; } @@ -50,8 +50,8 @@ static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaOb mirror::Object* expectedValue = soa.Decode<mirror::Object*>(javaExpectedValue); mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue); // JNI must use non transactional mode. - bool success = obj->CasFieldWeakSequentiallyConsistentObject<false>(MemberOffset(offset), - expectedValue, newValue); + bool success = obj->CasFieldStrongSequentiallyConsistentObject<false>(MemberOffset(offset), + expectedValue, newValue); return success ? JNI_TRUE : JNI_FALSE; } diff --git a/runtime/noop_compiler_callbacks.h b/runtime/noop_compiler_callbacks.h index 702b2e1f5c..65498deaab 100644 --- a/runtime/noop_compiler_callbacks.h +++ b/runtime/noop_compiler_callbacks.h @@ -25,10 +25,15 @@ class NoopCompilerCallbacks FINAL : public CompilerCallbacks { public: NoopCompilerCallbacks() {} ~NoopCompilerCallbacks() {} + bool MethodVerified(verifier::MethodVerifier* verifier) OVERRIDE { return true; } + void ClassRejected(ClassReference ref) OVERRIDE {} + + private: + DISALLOW_COPY_AND_ASSIGN(NoopCompilerCallbacks); }; } // namespace art diff --git a/runtime/oat.cc b/runtime/oat.cc index 857c0a24e2..1421baffcf 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -17,15 +17,46 @@ #include "oat.h" #include "utils.h" +#include <string.h> #include <zlib.h> namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '3', '6', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '3', '7', '\0' }; + +static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) { + size_t estimate = 0U; + if (variable_data != nullptr) { + SafeMap<std::string, std::string>::const_iterator it = variable_data->begin(); + SafeMap<std::string, std::string>::const_iterator end = variable_data->end(); + for ( ; it != end; ++it) { + estimate += it->first.length() + 1; + estimate += it->second.length() + 1; + } + } + return sizeof(OatHeader) + estimate; +} + +OatHeader* OatHeader::Create(InstructionSet instruction_set, + 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, + const SafeMap<std::string, std::string>* variable_data) { + // Estimate size of optional data. + size_t needed_size = ComputeOatHeaderSize(variable_data); + + // Reserve enough memory. + void* memory = operator new (needed_size); -OatHeader::OatHeader() { - memset(this, 0, sizeof(*this)); + // Create the OatHeader in-place. + return new (memory) OatHeader(instruction_set, + instruction_set_features, + dex_files, + image_file_location_oat_checksum, + image_file_location_oat_data_begin, + variable_data); } OatHeader::OatHeader(InstructionSet instruction_set, @@ -33,7 +64,7 @@ OatHeader::OatHeader(InstructionSet instruction_set, const std::vector<const DexFile*>* dex_files, uint32_t image_file_location_oat_checksum, uint32_t image_file_location_oat_data_begin, - const std::string& image_file_location) { + const SafeMap<std::string, std::string>* variable_data) { memcpy(magic_, kOatMagic, sizeof(kOatMagic)); memcpy(version_, kOatVersion, sizeof(kOatVersion)); @@ -56,9 +87,16 @@ OatHeader::OatHeader(InstructionSet instruction_set, image_file_location_oat_data_begin_ = image_file_location_oat_data_begin; UpdateChecksum(&image_file_location_oat_data_begin_, sizeof(image_file_location_oat_data_begin_)); - image_file_location_size_ = image_file_location.size(); - UpdateChecksum(&image_file_location_size_, sizeof(image_file_location_size_)); - UpdateChecksum(image_file_location.data(), image_file_location_size_); + // Flatten the map. Will also update variable_size_data_size_. + Flatten(variable_data); + + // Update checksum for variable data size. + UpdateChecksum(&key_value_store_size_, sizeof(key_value_store_size_)); + + // Update for data, if existing. + if (key_value_store_size_ > 0U) { + UpdateChecksum(&key_value_store_, key_value_store_size_); + } executable_offset_ = 0; interpreter_to_interpreter_bridge_offset_ = 0; @@ -327,20 +365,97 @@ uint32_t OatHeader::GetImageFileLocationOatDataBegin() const { return image_file_location_oat_data_begin_; } -uint32_t OatHeader::GetImageFileLocationSize() const { +uint32_t OatHeader::GetKeyValueStoreSize() const { CHECK(IsValid()); - return image_file_location_size_; + return key_value_store_size_; } -const uint8_t* OatHeader::GetImageFileLocationData() const { +const uint8_t* OatHeader::GetKeyValueStore() const { CHECK(IsValid()); - return image_file_location_data_; + return key_value_store_; } -std::string OatHeader::GetImageFileLocation() const { - CHECK(IsValid()); - return std::string(reinterpret_cast<const char*>(GetImageFileLocationData()), - GetImageFileLocationSize()); +// Advance start until it is either end or \0. +static const char* ParseString(const char* start, const char* end) { + while (start < end && *start != 0) { + start++; + } + return start; +} + +const char* OatHeader::GetStoreValueByKey(const char* key) const { + const char* ptr = reinterpret_cast<const char*>(&key_value_store_); + const char* end = ptr + key_value_store_size_; + + while (ptr < end) { + // Scan for a closing zero. + const char* str_end = ParseString(ptr, end); + if (str_end < end) { + if (strcmp(key, ptr) == 0) { + // Same as key. Check if value is OK. + if (ParseString(str_end + 1, end) < end) { + return str_end + 1; + } + } else { + // Different from key. Advance over the value. + ptr = ParseString(str_end + 1, end) + 1; + } + } else { + break; + } + } + // Not found. + return nullptr; +} + +bool OatHeader::GetStoreKeyValuePairByIndex(size_t index, const char** key, + const char** value) const { + const char* ptr = reinterpret_cast<const char*>(&key_value_store_); + const char* end = ptr + key_value_store_size_; + ssize_t counter = static_cast<ssize_t>(index); + + while (ptr < end && counter >= 0) { + // Scan for a closing zero. + const char* str_end = ParseString(ptr, end); + if (str_end < end) { + const char* maybe_key = ptr; + ptr = ParseString(str_end + 1, end) + 1; + if (ptr <= end) { + if (counter == 0) { + *key = maybe_key; + *value = str_end + 1; + return true; + } else { + counter--; + } + } else { + return false; + } + } else { + break; + } + } + // Not found. + return false; +} + +size_t OatHeader::GetHeaderSize() const { + return sizeof(OatHeader) + key_value_store_size_; +} + +void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) { + char* data_ptr = reinterpret_cast<char*>(&key_value_store_); + if (key_value_store != nullptr) { + SafeMap<std::string, std::string>::const_iterator it = key_value_store->begin(); + SafeMap<std::string, std::string>::const_iterator end = key_value_store->end(); + for ( ; it != end; ++it) { + strcpy(data_ptr, it->first.c_str()); + data_ptr += it->first.length() + 1; + strcpy(data_ptr, it->second.c_str()); + data_ptr += it->second.length() + 1; + } + } + key_value_store_size_ = data_ptr - reinterpret_cast<char*>(&key_value_store_); } OatMethodOffsets::OatMethodOffsets() diff --git a/runtime/oat.h b/runtime/oat.h index 7be768c5cb..fbed596d33 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -23,6 +23,7 @@ #include "dex_file.h" #include "instruction_set.h" #include "quick/quick_method_frame_info.h" +#include "safe_map.h" namespace art { @@ -31,13 +32,16 @@ class PACKED(4) OatHeader { static const uint8_t kOatMagic[4]; static const uint8_t kOatVersion[4]; - OatHeader(); - OatHeader(InstructionSet instruction_set, - 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, - const std::string& image_file_location); + static constexpr const char* kImageLocationKey = "image-location"; + static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; + static constexpr const char* kDex2OatHostKey = "dex2oat-host"; + + static OatHeader* Create(InstructionSet instruction_set, + 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, + const SafeMap<std::string, std::string>* variable_data); bool IsValid() const; const char* GetMagic() const; @@ -88,11 +92,24 @@ class PACKED(4) OatHeader { const InstructionSetFeatures& GetInstructionSetFeatures() const; uint32_t GetImageFileLocationOatChecksum() const; uint32_t GetImageFileLocationOatDataBegin() const; - uint32_t GetImageFileLocationSize() const; - const uint8_t* GetImageFileLocationData() const; - std::string GetImageFileLocation() const; + + uint32_t GetKeyValueStoreSize() const; + const uint8_t* GetKeyValueStore() const; + const char* GetStoreValueByKey(const char* key) const; + bool GetStoreKeyValuePairByIndex(size_t index, const char** key, const char** value) const; + + size_t GetHeaderSize() const; private: + OatHeader(InstructionSet instruction_set, + 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, + const SafeMap<std::string, std::string>* variable_data); + + void Flatten(const SafeMap<std::string, std::string>* variable_data); + uint8_t magic_[4]; uint8_t version_[4]; uint32_t adler32_checksum_; @@ -114,8 +131,9 @@ class PACKED(4) OatHeader { uint32_t image_file_location_oat_checksum_; uint32_t image_file_location_oat_data_begin_; - uint32_t image_file_location_size_; - uint8_t image_file_location_data_[0]; // note variable width data at end + + uint32_t key_value_store_size_; + uint8_t key_value_store_[0]; // note variable width data at end DISALLOW_COPY_AND_ASSIGN(OatHeader); }; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 6c44aa91bd..86c1baef27 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -17,6 +17,7 @@ #include "oat_file.h" #include <dlfcn.h> +#include <sstream> #include "base/bit_vector.h" #include "base/stl_util.h" @@ -28,6 +29,7 @@ #include "mirror/class.h" #include "mirror/object-inl.h" #include "os.h" +#include "runtime.h" #include "utils.h" #include "vmap_table.h" @@ -55,28 +57,29 @@ OatFile* OatFile::Open(const std::string& filename, std::string* error_msg) { CHECK(!filename.empty()) << location; CheckLocation(filename); - if (kUsePortableCompiler) { + std::unique_ptr<OatFile> ret; + if (kUsePortableCompiler && executable) { // If we are using PORTABLE, use dlopen to deal with relocations. // // We use our own ELF loader for Quick to deal with legacy apps that // open a generated dex file by name, remove the file, then open // another generated dex file with the same name. http://b/10614658 - if (executable) { - return OpenDlopen(filename, location, requested_base, error_msg); + ret.reset(OpenDlopen(filename, location, requested_base, error_msg)); + } else { + // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons: + // + // On target, dlopen may fail when compiling due to selinux restrictions on installd. + // + // On host, dlopen is expected to fail when cross compiling, so fall back to OpenElfFile. + // This won't work for portable runtime execution because it doesn't process relocations. + std::unique_ptr<File> file(OS::OpenFileForReading(filename.c_str())); + if (file.get() == NULL) { + *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno)); + return nullptr; } + ret.reset(OpenElfFile(file.get(), location, requested_base, false, executable, error_msg)); } - // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons: - // - // On target, dlopen may fail when compiling due to selinux restrictions on installd. - // - // On host, dlopen is expected to fail when cross compiling, so fall back to OpenElfFile. - // This won't work for portable runtime execution because it doesn't process relocations. - std::unique_ptr<File> file(OS::OpenFileForReading(filename.c_str())); - if (file.get() == NULL) { - *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno)); - return NULL; - } - return OpenElfFile(file.get(), location, requested_base, false, executable, error_msg); + return ret.release(); } OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) { @@ -206,11 +209,11 @@ bool OatFile::Setup(std::string* error_msg) { return false; } - oat += GetOatHeader().GetImageFileLocationSize(); + oat += GetOatHeader().GetKeyValueStoreSize(); if (oat > End()) { - *error_msg = StringPrintf("In oat file '%s' found truncated image file location: " + *error_msg = StringPrintf("In oat file '%s' found truncated variable-size data: " "%p + %zd + %ud <= %p", GetLocation().c_str(), - Begin(), sizeof(OatHeader), GetOatHeader().GetImageFileLocationSize(), + Begin(), sizeof(OatHeader), GetOatHeader().GetKeyValueStoreSize(), End()); return false; } diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 70253af614..44f4466724 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -24,7 +24,7 @@ #include "dex_file.h" #include "invoke_type.h" #include "mem_map.h" -#include "mirror/art_method.h" +#include "mirror/class.h" #include "oat.h" #include "os.h" diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h index d8c1c402b9..592deed1a7 100644 --- a/runtime/object_callbacks.h +++ b/runtime/object_callbacks.h @@ -24,12 +24,14 @@ // For size_t. #include <stdlib.h> +#include "base/macros.h" + namespace art { namespace mirror { -class Class; -class Object; -template<class MirrorType> class HeapReference; -class Reference; + class Class; + class Object; + template<class MirrorType> class HeapReference; + class Reference; } // namespace mirror class StackVisitor; @@ -57,8 +59,7 @@ typedef void (RootCallback)(mirror::Object** root, void* arg, uint32_t thread_id // A callback for visiting an object in the heap. typedef void (ObjectCallback)(mirror::Object* obj, void* arg); // A callback used for marking an object, returns the new address of the object if the object moved. -typedef mirror::Object* (MarkObjectCallback)(mirror::Object* obj, void* arg) - __attribute__((warn_unused_result)); +typedef mirror::Object* (MarkObjectCallback)(mirror::Object* obj, void* arg) WARN_UNUSED; // A callback for verifying roots. typedef void (VerifyRootCallback)(const mirror::Object* root, void* arg, size_t vreg, const StackVisitor* visitor, RootType root_type); @@ -68,13 +69,12 @@ typedef void (DelayReferenceReferentCallback)(mirror::Class* klass, mirror::Refe // A callback for testing if an object is marked, returns nullptr if not marked, otherwise the new // address the object (if the object didn't move, returns the object input parameter). -typedef mirror::Object* (IsMarkedCallback)(mirror::Object* object, void* arg) - __attribute__((warn_unused_result)); +typedef mirror::Object* (IsMarkedCallback)(mirror::Object* object, void* arg) WARN_UNUSED; // Returns true if the object in the heap reference is marked, if it is marked and has moved the // callback updates the heap reference contain the new value. typedef bool (IsHeapReferenceMarkedCallback)(mirror::HeapReference<mirror::Object>* object, - void* arg) __attribute__((warn_unused_result)); + void* arg) WARN_UNUSED; typedef void (ProcessMarkStackCallback)(void* arg); } // namespace art diff --git a/runtime/object_lock.cc b/runtime/object_lock.cc new file mode 100644 index 0000000000..f7accc0f31 --- /dev/null +++ b/runtime/object_lock.cc @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "object_lock.h" + +#include "mirror/object-inl.h" +#include "monitor.h" + +namespace art { + +template <typename T> +ObjectLock<T>::ObjectLock(Thread* self, Handle<T> object) : self_(self), obj_(object) { + CHECK(object.Get() != nullptr); + obj_->MonitorEnter(self_); +} + +template <typename T> +ObjectLock<T>::~ObjectLock() { + obj_->MonitorExit(self_); +} + +template <typename T> +void ObjectLock<T>::WaitIgnoringInterrupts() { + Monitor::Wait(self_, obj_.Get(), 0, 0, false, kWaiting); +} + +template <typename T> +void ObjectLock<T>::Notify() { + obj_->Notify(self_); +} + +template <typename T> +void ObjectLock<T>::NotifyAll() { + obj_->NotifyAll(self_); +} + +template class ObjectLock<mirror::Class>; +template class ObjectLock<mirror::Object>; + +} // namespace art diff --git a/runtime/object_lock.h b/runtime/object_lock.h new file mode 100644 index 0000000000..acddc03e29 --- /dev/null +++ b/runtime/object_lock.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_OBJECT_LOCK_H_ +#define ART_RUNTIME_OBJECT_LOCK_H_ + +#include "base/macros.h" +#include "base/mutex.h" +#include "handle.h" + +namespace art { + +class Thread; + +template <typename T> +class ObjectLock { + public: + ObjectLock(Thread* self, Handle<T> object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + ~ObjectLock() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void WaitIgnoringInterrupts() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void Notify() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void NotifyAll() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + Thread* const self_; + Handle<T> const obj_; + + DISALLOW_COPY_AND_ASSIGN(ObjectLock); +}; + +} // namespace art + +#endif // ART_RUNTIME_OBJECT_LOCK_H_ diff --git a/runtime/object_utils.h b/runtime/object_utils.h deleted file mode 100644 index 28ce8f34ac..0000000000 --- a/runtime/object_utils.h +++ /dev/null @@ -1,380 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_OBJECT_UTILS_H_ -#define ART_RUNTIME_OBJECT_UTILS_H_ - -#include "class_linker.h" -#include "dex_file.h" -#include "monitor.h" -#include "mirror/art_field.h" -#include "mirror/art_method.h" -#include "mirror/class.h" -#include "mirror/dex_cache.h" -#include "mirror/iftable.h" -#include "mirror/proxy.h" -#include "mirror/string.h" - -#include "runtime.h" -#include "handle_scope-inl.h" - -#include <string> - -namespace art { - -template <typename T> -class ObjectLock { - public: - ObjectLock(Thread* self, Handle<T> object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : self_(self), obj_(object) { - CHECK(object.Get() != nullptr); - obj_->MonitorEnter(self_); - } - - ~ObjectLock() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - obj_->MonitorExit(self_); - } - - void WaitIgnoringInterrupts() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Monitor::Wait(self_, obj_.Get(), 0, 0, false, kWaiting); - } - - void Notify() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - obj_->Notify(self_); - } - - void NotifyAll() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - obj_->NotifyAll(self_); - } - - private: - Thread* const self_; - Handle<T> const obj_; - DISALLOW_COPY_AND_ASSIGN(ObjectLock); -}; - -class FieldHelper { - public: - explicit FieldHelper(Handle<mirror::ArtField> f) : field_(f) {} - - void ChangeField(mirror::ArtField* new_f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(new_f != nullptr); - field_.Assign(new_f); - } - - mirror::ArtField* GetField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return field_.Get(); - } - - mirror::Class* GetType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - uint32_t field_index = field_->GetDexFieldIndex(); - if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) { - return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), - field_->GetTypeDescriptor()); - } - const DexFile* dex_file = field_->GetDexFile(); - const DexFile::FieldId& field_id = dex_file->GetFieldId(field_index); - mirror::Class* type = field_->GetDexCache()->GetResolvedType(field_id.type_idx_); - if (resolve && (type == nullptr)) { - type = Runtime::Current()->GetClassLinker()->ResolveType(field_id.type_idx_, field_.Get()); - CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); - } - return type; - } - - // The returned const char* is only guaranteed to be valid for the lifetime of the FieldHelper. - // If you need it longer, copy it into a std::string. - const char* GetDeclaringClassDescriptor() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - uint32_t field_index = field_->GetDexFieldIndex(); - if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) { - DCHECK(field_->IsStatic()); - DCHECK_LT(field_index, 2U); - // 0 == Class[] interfaces; 1 == Class[][] throws; - declaring_class_descriptor_ = field_->GetDeclaringClass()->GetDescriptor(); - return declaring_class_descriptor_.c_str(); - } - const DexFile* dex_file = field_->GetDexFile(); - const DexFile::FieldId& field_id = dex_file->GetFieldId(field_index); - return dex_file->GetFieldDeclaringClassDescriptor(field_id); - } - - private: - Handle<mirror::ArtField> field_; - std::string declaring_class_descriptor_; - - DISALLOW_COPY_AND_ASSIGN(FieldHelper); -}; - -class MethodHelper { - public: - explicit MethodHelper(Handle<mirror::ArtMethod> m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : method_(m), shorty_(nullptr), shorty_len_(0) { - SetMethod(m.Get()); - } - - void ChangeMethod(mirror::ArtMethod* new_m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(new_m != nullptr); - SetMethod(new_m); - shorty_ = nullptr; - } - - mirror::ArtMethod* GetMethod() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return method_->GetInterfaceMethodIfProxy(); - } - - mirror::String* GetNameAsString(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const DexFile* dex_file = method_->GetDexFile(); - mirror::ArtMethod* method = method_->GetInterfaceMethodIfProxy(); - uint32_t dex_method_idx = method->GetDexMethodIndex(); - const DexFile::MethodId& method_id = dex_file->GetMethodId(dex_method_idx); - StackHandleScope<1> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache())); - return Runtime::Current()->GetClassLinker()->ResolveString(*dex_file, method_id.name_idx_, - dex_cache); - } - - const char* GetShorty() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const char* result = shorty_; - if (result == nullptr) { - result = method_->GetShorty(&shorty_len_); - shorty_ = result; - } - return result; - } - - uint32_t GetShortyLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (shorty_ == nullptr) { - GetShorty(); - } - return shorty_len_; - } - - // Counts the number of references in the parameter list of the corresponding method. - // Note: Thus does _not_ include "this" for non-static methods. - uint32_t GetNumberOfReferenceArgsWithoutReceiver() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const char* shorty = GetShorty(); - uint32_t refs = 0; - for (uint32_t i = 1; i < shorty_len_ ; ++i) { - if (shorty[i] == 'L') { - refs++; - } - } - - return refs; - } - - // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large - // number of bugs at call sites. - mirror::Class* GetReturnType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::ArtMethod* method = GetMethod(); - const DexFile* dex_file = method->GetDexFile(); - const DexFile::MethodId& method_id = dex_file->GetMethodId(method->GetDexMethodIndex()); - const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); - uint16_t return_type_idx = proto_id.return_type_idx_; - return GetClassFromTypeIdx(return_type_idx, resolve); - } - - size_t NumArgs() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // "1 +" because the first in Args is the receiver. - // "- 1" because we don't count the return type. - return (method_->IsStatic() ? 0 : 1) + GetShortyLength() - 1; - } - - // Get the primitive type associated with the given parameter. - Primitive::Type GetParamPrimitiveType(size_t param) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CHECK_LT(param, NumArgs()); - if (GetMethod()->IsStatic()) { - param++; // 0th argument must skip return value at start of the shorty - } else if (param == 0) { - return Primitive::kPrimNot; - } - return Primitive::GetType(GetShorty()[param]); - } - - // Is the specified parameter a long or double, where parameter 0 is 'this' for instance methods. - bool IsParamALongOrDouble(size_t param) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Primitive::Type type = GetParamPrimitiveType(param); - return type == Primitive::kPrimLong || type == Primitive::kPrimDouble; - } - - // Is the specified parameter a reference, where parameter 0 is 'this' for instance methods. - bool IsParamAReference(size_t param) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetParamPrimitiveType(param) == Primitive::kPrimNot; - } - - bool HasSameNameAndSignature(MethodHelper* other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const DexFile* dex_file = method_->GetDexFile(); - const DexFile::MethodId& mid = dex_file->GetMethodId(GetMethod()->GetDexMethodIndex()); - if (method_->GetDexCache() == other->method_->GetDexCache()) { - const DexFile::MethodId& other_mid = - dex_file->GetMethodId(other->GetMethod()->GetDexMethodIndex()); - return mid.name_idx_ == other_mid.name_idx_ && mid.proto_idx_ == other_mid.proto_idx_; - } - const DexFile* other_dex_file = other->method_->GetDexFile(); - const DexFile::MethodId& other_mid = - other_dex_file->GetMethodId(other->GetMethod()->GetDexMethodIndex()); - if (!DexFileStringEquals(dex_file, mid.name_idx_, other_dex_file, other_mid.name_idx_)) { - return false; // Name mismatch. - } - return dex_file->GetMethodSignature(mid) == other_dex_file->GetMethodSignature(other_mid); - } - - bool HasSameSignatureWithDifferentClassLoaders(MethodHelper* other) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (UNLIKELY(GetReturnType() != other->GetReturnType())) { - return false; - } - const DexFile::TypeList* types = method_->GetParameterTypeList(); - const DexFile::TypeList* other_types = other->method_->GetParameterTypeList(); - if (types == nullptr) { - return (other_types == nullptr) || (other_types->Size() == 0); - } else if (UNLIKELY(other_types == nullptr)) { - return types->Size() == 0; - } - uint32_t num_types = types->Size(); - if (UNLIKELY(num_types != other_types->Size())) { - return false; - } - for (uint32_t i = 0; i < num_types; ++i) { - mirror::Class* param_type = GetClassFromTypeIdx(types->GetTypeItem(i).type_idx_); - mirror::Class* other_param_type = - other->GetClassFromTypeIdx(other_types->GetTypeItem(i).type_idx_); - if (UNLIKELY(param_type != other_param_type)) { - return false; - } - } - return true; - } - - mirror::Class* GetClassFromTypeIdx(uint16_t type_idx, bool resolve = true) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::ArtMethod* method = GetMethod(); - mirror::Class* type = method->GetDexCacheResolvedTypes()->Get(type_idx); - if (type == nullptr && resolve) { - type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method); - CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); - } - return type; - } - - mirror::Class* GetDexCacheResolvedType(uint16_t type_idx) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetMethod()->GetDexCacheResolvedTypes()->Get(type_idx); - } - - mirror::String* ResolveString(uint32_t string_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::ArtMethod* method = GetMethod(); - mirror::String* s = method->GetDexCacheStrings()->Get(string_idx); - if (UNLIKELY(s == nullptr)) { - StackHandleScope<1> hs(Thread::Current()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache())); - s = Runtime::Current()->GetClassLinker()->ResolveString(*method->GetDexFile(), string_idx, - dex_cache); - } - return s; - } - - uint32_t FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::ArtMethod* method = GetMethod(); - const DexFile* dexfile = method->GetDexFile(); - if (dexfile == &other_dexfile) { - return method->GetDexMethodIndex(); - } - const DexFile::MethodId& mid = dexfile->GetMethodId(method->GetDexMethodIndex()); - const char* mid_declaring_class_descriptor = dexfile->StringByTypeIdx(mid.class_idx_); - const DexFile::StringId* other_descriptor = - other_dexfile.FindStringId(mid_declaring_class_descriptor); - if (other_descriptor != nullptr) { - const DexFile::TypeId* other_type_id = - other_dexfile.FindTypeId(other_dexfile.GetIndexForStringId(*other_descriptor)); - if (other_type_id != nullptr) { - const char* mid_name = dexfile->GetMethodName(mid); - const DexFile::StringId* other_name = other_dexfile.FindStringId(mid_name); - if (other_name != nullptr) { - uint16_t other_return_type_idx; - std::vector<uint16_t> other_param_type_idxs; - bool success = other_dexfile.CreateTypeList( - dexfile->GetMethodSignature(mid).ToString(), &other_return_type_idx, - &other_param_type_idxs); - if (success) { - const DexFile::ProtoId* other_sig = - other_dexfile.FindProtoId(other_return_type_idx, other_param_type_idxs); - if (other_sig != nullptr) { - const DexFile::MethodId* other_mid = other_dexfile.FindMethodId( - *other_type_id, *other_name, *other_sig); - if (other_mid != nullptr) { - return other_dexfile.GetIndexForMethodId(*other_mid); - } - } - } - } - } - } - return DexFile::kDexNoIndex; - } - - // The name_and_signature_idx MUST point to a MethodId with the same name and signature in the - // other_dexfile, such as the method index used to resolve this method in the other_dexfile. - uint32_t FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile, - uint32_t name_and_signature_idx) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::ArtMethod* method = GetMethod(); - const DexFile* dexfile = method->GetDexFile(); - const uint32_t dex_method_idx = method->GetDexMethodIndex(); - const DexFile::MethodId& mid = dexfile->GetMethodId(dex_method_idx); - const DexFile::MethodId& name_and_sig_mid = other_dexfile.GetMethodId(name_and_signature_idx); - DCHECK_STREQ(dexfile->GetMethodName(mid), other_dexfile.GetMethodName(name_and_sig_mid)); - DCHECK_EQ(dexfile->GetMethodSignature(mid), other_dexfile.GetMethodSignature(name_and_sig_mid)); - if (dexfile == &other_dexfile) { - return dex_method_idx; - } - const char* mid_declaring_class_descriptor = dexfile->StringByTypeIdx(mid.class_idx_); - const DexFile::StringId* other_descriptor = - other_dexfile.FindStringId(mid_declaring_class_descriptor); - if (other_descriptor != nullptr) { - const DexFile::TypeId* other_type_id = - other_dexfile.FindTypeId(other_dexfile.GetIndexForStringId(*other_descriptor)); - if (other_type_id != nullptr) { - const DexFile::MethodId* other_mid = other_dexfile.FindMethodId( - *other_type_id, other_dexfile.GetStringId(name_and_sig_mid.name_idx_), - other_dexfile.GetProtoId(name_and_sig_mid.proto_idx_)); - if (other_mid != nullptr) { - return other_dexfile.GetIndexForMethodId(*other_mid); - } - } - } - return DexFile::kDexNoIndex; - } - - private: - // Set the method_ field, for proxy methods looking up the interface method via the resolved - // methods table. - void SetMethod(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - method_.Assign(method); - } - - Handle<mirror::ArtMethod> method_; - const char* shorty_; - uint32_t shorty_len_; - - DISALLOW_COPY_AND_ASSIGN(MethodHelper); -}; - -} // namespace art - -#endif // ART_RUNTIME_OBJECT_UTILS_H_ diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index e1e133f1be..9a1d0f71aa 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -24,11 +24,13 @@ #include "debugger.h" #include "gc/heap.h" #include "monitor.h" +#include "runtime.h" +#include "trace.h" #include "utils.h" namespace art { -ParsedOptions* ParsedOptions::Create(const Runtime::Options& options, bool ignore_unrecognized) { +ParsedOptions* ParsedOptions::Create(const RuntimeOptions& options, bool ignore_unrecognized) { std::unique_ptr<ParsedOptions> parsed(new ParsedOptions()); if (parsed->Parse(options, ignore_unrecognized)) { return parsed.release(); @@ -164,7 +166,7 @@ bool ParsedOptions::ParseXGcOption(const std::string& option) { return true; } -bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecognized) { +bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognized) { const char* boot_class_path_string = getenv("BOOTCLASSPATH"); if (boot_class_path_string != NULL) { boot_class_path_string_ = boot_class_path_string; @@ -197,13 +199,18 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni #else #error "ART default GC type must be set" #endif + // If we are using homogeneous space compaction then default background compaction to off since + // homogeneous space compactions when we transition to not jank perceptible. + use_homogeneous_space_compaction_for_oom_ = false; // If background_collector_type_ is kCollectorTypeNone, it defaults to the collector_type_ after - // parsing options. + // parsing options. If you set this to kCollectorTypeHSpaceCompact then we will do an hspace + // compaction when we transition to background instead of a normal collector transition. background_collector_type_ = gc::kCollectorTypeSS; stack_size_ = 0; // 0 means default. max_spins_before_thin_lock_inflation_ = Monitor::kDefaultMaxSpinsBeforeThinLockInflation; low_memory_mode_ = false; use_tlab_ = false; + min_interval_homogeneous_space_compaction_by_oom_ = MsToNs(100 * 1000); // 100s. verify_pre_gc_heap_ = false; // Pre sweeping is the one that usually fails if the GC corrupted the heap. verify_pre_sweeping_heap_ = kIsDebugBuild; @@ -253,43 +260,11 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni method_trace_file_ = "/data/method-trace-file.bin"; method_trace_file_size_ = 10 * MB; - profile_clock_source_ = kDefaultProfilerClockSource; + profile_clock_source_ = kDefaultTraceClockSource; verify_ = true; image_isa_ = kRuntimeISA; - // Default to explicit checks. Switch off with -implicit-checks:. - // or setprop dalvik.vm.implicit_checks check1,check2,... -#ifdef HAVE_ANDROID_OS - { - char buf[PROP_VALUE_MAX]; - property_get("dalvik.vm.implicit_checks", buf, "null,stack"); - std::string checks(buf); - std::vector<std::string> checkvec; - Split(checks, ',', checkvec); - explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | - kExplicitStackOverflowCheck; - for (auto& str : checkvec) { - std::string val = Trim(str); - if (val == "none") { - explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | - kExplicitStackOverflowCheck; - } else if (val == "null") { - explicit_checks_ &= ~kExplicitNullCheck; - } else if (val == "suspend") { - explicit_checks_ &= ~kExplicitSuspendCheck; - } else if (val == "stack") { - explicit_checks_ &= ~kExplicitStackOverflowCheck; - } else if (val == "all") { - explicit_checks_ = 0; - } - } - } -#else - explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | - kExplicitStackOverflowCheck; -#endif - for (size_t i = 0; i < options.size(); ++i) { if (true && options[0].first == "-Xzygote") { LOG(INFO) << "option[" << i << "]=" << options[i].first; @@ -305,6 +280,7 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni Exit(0); } else if (StartsWith(option, "-Xbootclasspath:")) { boot_class_path_string_ = option.substr(strlen("-Xbootclasspath:")).data(); + LOG(INFO) << "setting boot class path to " << boot_class_path_string_; } else if (option == "-classpath" || option == "-cp") { // TODO: support -Djava.class.path i++; @@ -416,6 +392,10 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni low_memory_mode_ = true; } else if (option == "-XX:UseTLAB") { use_tlab_ = true; + } else if (option == "-XX:EnableHSpaceCompactForOOM") { + use_homogeneous_space_compaction_for_oom_ = true; + } else if (option == "-XX:DisableHSpaceCompactForOOM") { + use_homogeneous_space_compaction_for_oom_ = false; } else if (StartsWith(option, "-D")) { properties_.push_back(option.substr(strlen("-D"))); } else if (StartsWith(option, "-Xjnitrace:")) { @@ -439,12 +419,17 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni if (!ParseStringAfterChar(option, '=', &substring)) { return false; } - gc::CollectorType collector_type = ParseCollectorType(substring); - if (collector_type != gc::kCollectorTypeNone) { - background_collector_type_ = collector_type; + // Special handling for HSpaceCompact since this is only valid as a background GC type. + if (substring == "HSpaceCompact") { + background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact; } else { - Usage("Unknown -XX:BackgroundGC option %s\n", substring.c_str()); - return false; + gc::CollectorType collector_type = ParseCollectorType(substring); + if (collector_type != gc::kCollectorTypeNone) { + background_collector_type_ = collector_type; + } else { + Usage("Unknown -XX:BackgroundGC option %s\n", substring.c_str()); + return false; + } } } else if (option == "-XX:+DisableExplicitGC") { is_explicit_gc_disabled_ = true; @@ -528,11 +513,11 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni return false; } } else if (option == "-Xprofile:threadcpuclock") { - Trace::SetDefaultClockSource(kProfilerClockSourceThreadCpu); + Trace::SetDefaultClockSource(kTraceClockSourceThreadCpu); } else if (option == "-Xprofile:wallclock") { - Trace::SetDefaultClockSource(kProfilerClockSourceWall); + Trace::SetDefaultClockSource(kTraceClockSourceWall); } else if (option == "-Xprofile:dualclock") { - Trace::SetDefaultClockSource(kProfilerClockSourceDual); + Trace::SetDefaultClockSource(kTraceClockSourceDual); } else if (option == "-Xenable-profiler") { profiler_options_.enabled_ = true; } else if (StartsWith(option, "-Xprofile-filename:")) { @@ -573,54 +558,6 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni if (!ParseUnsignedInteger(option, ':', &profiler_options_.max_stack_depth_)) { return false; } - } else if (StartsWith(option, "-implicit-checks:")) { - std::string checks; - if (!ParseStringAfterChar(option, ':', &checks)) { - return false; - } - std::vector<std::string> checkvec; - Split(checks, ',', checkvec); - for (auto& str : checkvec) { - std::string val = Trim(str); - if (val == "none") { - explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | - kExplicitStackOverflowCheck; - } else if (val == "null") { - explicit_checks_ &= ~kExplicitNullCheck; - } else if (val == "suspend") { - explicit_checks_ &= ~kExplicitSuspendCheck; - } else if (val == "stack") { - explicit_checks_ &= ~kExplicitStackOverflowCheck; - } else if (val == "all") { - explicit_checks_ = 0; - } else { - return false; - } - } - } else if (StartsWith(option, "-explicit-checks:")) { - std::string checks; - if (!ParseStringAfterChar(option, ':', &checks)) { - return false; - } - std::vector<std::string> checkvec; - Split(checks, ',', checkvec); - for (auto& str : checkvec) { - std::string val = Trim(str); - if (val == "none") { - explicit_checks_ = 0; - } else if (val == "null") { - explicit_checks_ |= kExplicitNullCheck; - } else if (val == "suspend") { - explicit_checks_ |= kExplicitSuspendCheck; - } else if (val == "stack") { - explicit_checks_ |= kExplicitStackOverflowCheck; - } else if (val == "all") { - explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | - kExplicitStackOverflowCheck; - } else { - return false; - } - } } else if (StartsWith(option, "-Xcompiler:")) { if (!ParseStringAfterChar(option, ':', &compiler_executable_)) { return false; diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h index d0f3c125c7..23f2bcfa62 100644 --- a/runtime/parsed_options.h +++ b/runtime/parsed_options.h @@ -18,17 +18,26 @@ #define ART_RUNTIME_PARSED_OPTIONS_H_ #include <string> +#include <vector> +#include <jni.h> + +#include "globals.h" #include "gc/collector_type.h" -#include "runtime.h" -#include "trace.h" +#include "instruction_set.h" +#include "profiler_options.h" namespace art { +class CompilerCallbacks; +class DexFile; + +typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions; + class ParsedOptions { public: // returns null if problem parsing and ignore_unrecognized is false - static ParsedOptions* Create(const Runtime::Options& options, bool ignore_unrecognized); + static ParsedOptions* Create(const RuntimeOptions& options, bool ignore_unrecognized); const std::vector<const DexFile*>* boot_class_path_; std::string boot_class_path_string_; @@ -80,14 +89,16 @@ class ParsedOptions { std::vector<std::string> image_compiler_options_; ProfilerOptions profiler_options_; std::string profile_output_filename_; - ProfilerClockSource profile_clock_source_; + TraceClockSource profile_clock_source_; bool verify_; InstructionSet image_isa_; - static constexpr uint32_t kExplicitNullCheck = 1; - static constexpr uint32_t kExplicitSuspendCheck = 2; - static constexpr uint32_t kExplicitStackOverflowCheck = 4; - uint32_t explicit_checks_; + // Whether or not we use homogeneous space compaction to avoid OOM errors. If enabled, + // the heap will attempt to create an extra space which enables compacting from a malloc space to + // another malloc space when we are about to throw OOM. + bool use_homogeneous_space_compaction_for_oom_; + // Minimal interval allowed between two homogeneous space compactions caused by OOM. + uint64_t min_interval_homogeneous_space_compaction_by_oom_; private: ParsedOptions() {} @@ -99,7 +110,7 @@ class ParsedOptions { void Exit(int status); void Abort(); - bool Parse(const Runtime::Options& options, bool ignore_unrecognized); + bool Parse(const RuntimeOptions& options, bool ignore_unrecognized); bool ParseXGcOption(const std::string& option); bool ParseStringAfterChar(const std::string& option, char after_char, std::string* parsed_value); bool ParseInteger(const std::string& option, char after_char, int* parsed_value); diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc index b58a29c471..5154d69292 100644 --- a/runtime/parsed_options_test.cc +++ b/runtime/parsed_options_test.cc @@ -36,7 +36,7 @@ TEST_F(ParsedOptionsTest, ParsedOptions) { boot_class_path += "-Xbootclasspath:"; boot_class_path += lib_core; - Runtime::Options options; + RuntimeOptions options; options.push_back(std::make_pair(boot_class_path.c_str(), null)); options.push_back(std::make_pair("-classpath", null)); options.push_back(std::make_pair(lib_core.c_str(), null)); diff --git a/runtime/profiler.cc b/runtime/profiler.cc index 7a7a92a7c0..951444812b 100644 --- a/runtime/profiler.cc +++ b/runtime/profiler.cc @@ -32,7 +32,6 @@ #include "mirror/dex_cache.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "os.h" #include "scoped_thread_state_change.h" #include "ScopedLocalRef.h" diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index 093c129add..bd6656dda1 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -14,12 +14,14 @@ * limitations under the License. */ -#include "common_compiler_test.h" -#include "mirror/art_field-inl.h" - #include <jni.h> #include <vector> +#include "common_compiler_test.h" +#include "field_helper.h" +#include "mirror/art_field-inl.h" +#include "scoped_thread_state_change.h" + namespace art { class ProxyTest : public CommonCompilerTest { diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h index 5128b19d12..982553d3af 100644 --- a/runtime/quick/inline_method_analyser.h +++ b/runtime/quick/inline_method_analyser.h @@ -48,6 +48,7 @@ enum InlineMethodOpcode : uint16_t { kIntrinsicMinMaxFloat, kIntrinsicMinMaxDouble, kIntrinsicSqrt, + kIntrinsicGet, kIntrinsicCharAt, kIntrinsicCompareTo, kIntrinsicIsEmptyOrLength, diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 103492334c..6581f9b627 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -16,10 +16,14 @@ #include "quick_exception_handler.h" +#include "arch/context.h" #include "dex_instruction.h" #include "entrypoints/entrypoint_utils.h" #include "handle_scope-inl.h" #include "mirror/art_method-inl.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" +#include "mirror/throwable.h" #include "verifier/method_verifier.h" namespace art { diff --git a/runtime/read_barrier_c.h b/runtime/read_barrier_c.h index f4af61f517..1385c60f11 100644 --- a/runtime/read_barrier_c.h +++ b/runtime/read_barrier_c.h @@ -35,4 +35,9 @@ #error "Only one of Baker or Brooks can be enabled at a time." #endif +// A placeholder marker to indicate places to add read barriers in the +// assembly code. This is a development time aid and to be removed +// after read barriers are added. +#define THIS_LOAD_REQUIRES_READ_BARRIER + #endif // ART_RUNTIME_READ_BARRIER_C_H_ diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc index 32290393ce..d2877f98d1 100644 --- a/runtime/reference_table_test.cc +++ b/runtime/reference_table_test.cc @@ -17,6 +17,10 @@ #include "reference_table.h" #include "common_runtime_test.h" +#include "mirror/array.h" +#include "mirror/string.h" +#include "scoped_thread_state_change.h" +#include "thread-inl.h" namespace art { diff --git a/runtime/reflection.cc b/runtime/reflection.cc index fe5e1043a9..758c1bbd1f 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -20,14 +20,14 @@ #include "common_throws.h" #include "dex_file-inl.h" #include "jni_internal.h" +#include "method_helper-inl.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" -#include "mirror/class.h" #include "mirror/class-inl.h" -#include "mirror/object_array.h" +#include "mirror/class.h" #include "mirror/object_array-inl.h" +#include "mirror/object_array.h" #include "nth_caller_visitor.h" -#include "object_utils.h" #include "scoped_thread_state_change.h" #include "stack.h" #include "well_known_classes.h" @@ -567,6 +567,11 @@ bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) { return true; } +static std::string PrettyDescriptor(Primitive::Type type) { + std::string descriptor_string(Primitive::Descriptor(type)); + return PrettyDescriptor(descriptor_string); +} + bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result, Primitive::Type srcType, Primitive::Type dstType, const JValue& src, JValue* dst) { diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc index 3b66abee2a..9d10daaffc 100644 --- a/runtime/reflection_test.cc +++ b/runtime/reflection_test.cc @@ -18,9 +18,11 @@ #include <float.h> #include <limits.h> +#include "ScopedLocalRef.h" #include "common_compiler_test.h" #include "mirror/art_method-inl.h" +#include "scoped_thread_state_change.h" namespace art { @@ -109,7 +111,16 @@ class ReflectionTest : public CommonCompilerTest { : c->FindVirtualMethod(method_name, method_signature); CHECK(method != nullptr); - *receiver = (is_static ? nullptr : c->AllocObject(self)); + if (is_static) { + *receiver = nullptr; + } else { + // Ensure class is initialized before allocating object + StackHandleScope<1> hs(self); + Handle<mirror::Class> h_class(hs.NewHandle(c)); + bool initialized = class_linker_->EnsureInitialized(h_class, true, true); + CHECK(initialized); + *receiver = c->AllocObject(self); + } // Start runtime. bool started = runtime_->Start(); diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h index 29ddd1dc0b..f776bcd336 100644 --- a/runtime/runtime-inl.h +++ b/runtime/runtime-inl.h @@ -19,24 +19,56 @@ #include "runtime.h" +#include "read_barrier-inl.h" + namespace art { -inline QuickMethodFrameInfo Runtime::GetRuntimeMethodFrameInfo(mirror::ArtMethod* method) const { +inline QuickMethodFrameInfo Runtime::GetRuntimeMethodFrameInfo(mirror::ArtMethod* method) { DCHECK(method != nullptr); // Cannot be imt-conflict-method or resolution-method. DCHECK(method != GetImtConflictMethod()); DCHECK(method != GetResolutionMethod()); // Don't use GetCalleeSaveMethod(), some tests don't set all callee save methods. - if (method == callee_save_methods_[Runtime::kRefsAndArgs]) { + if (method == GetCalleeSaveMethodUnchecked(Runtime::kRefsAndArgs)) { return GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs); - } else if (method == callee_save_methods_[Runtime::kSaveAll]) { + } else if (method == GetCalleeSaveMethodUnchecked(Runtime::kSaveAll)) { return GetCalleeSaveMethodFrameInfo(Runtime::kSaveAll); } else { - DCHECK(method == callee_save_methods_[Runtime::kRefsOnly]); + DCHECK(method == GetCalleeSaveMethodUnchecked(Runtime::kRefsOnly)); return GetCalleeSaveMethodFrameInfo(Runtime::kRefsOnly); } } +inline mirror::ArtMethod* Runtime::GetResolutionMethod() { + CHECK(HasResolutionMethod()); + return ReadBarrier::BarrierForRoot<mirror::ArtMethod, kWithReadBarrier>(&resolution_method_); +} + +inline mirror::ArtMethod* Runtime::GetImtConflictMethod() { + CHECK(HasImtConflictMethod()); + return ReadBarrier::BarrierForRoot<mirror::ArtMethod, kWithReadBarrier>(&imt_conflict_method_); +} + +inline mirror::ObjectArray<mirror::ArtMethod>* Runtime::GetDefaultImt() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(HasDefaultImt()); + return ReadBarrier::BarrierForRoot<mirror::ObjectArray<mirror::ArtMethod>, kWithReadBarrier>( + &default_imt_); +} + +inline mirror::ArtMethod* Runtime::GetCalleeSaveMethod(CalleeSaveType type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(HasCalleeSaveMethod(type)); + return ReadBarrier::BarrierForRoot<mirror::ArtMethod, kWithReadBarrier>( + &callee_save_methods_[type]); +} + +inline mirror::ArtMethod* Runtime::GetCalleeSaveMethodUnchecked(CalleeSaveType type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return ReadBarrier::BarrierForRoot<mirror::ArtMethod, kWithReadBarrier>( + &callee_save_methods_[type]); +} + } // namespace art #endif // ART_RUNTIME_RUNTIME_INL_H_ diff --git a/runtime/runtime.cc b/runtime/runtime.cc index efa205e079..aca2607bfc 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -49,6 +49,7 @@ #include "fault_handler.h" #include "gc/accounting/card_table-inl.h" #include "gc/heap.h" +#include "gc/space/image_space.h" #include "gc/space/space.h" #include "image.h" #include "instrumentation.h" @@ -140,7 +141,10 @@ Runtime::Runtime() suspend_handler_(nullptr), stack_overflow_handler_(nullptr), verify_(false), - target_sdk_version_(0) { + target_sdk_version_(0), + implicit_null_checks_(false), + implicit_so_checks_(false), + implicit_suspend_checks_(false) { for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { callee_save_methods_[i] = nullptr; } @@ -324,7 +328,7 @@ void Runtime::SweepSystemWeaks(IsMarkedCallback* visitor, void* arg) { GetJavaVM()->SweepJniWeakGlobals(visitor, arg); } -bool Runtime::Create(const Options& options, bool ignore_unrecognized) { +bool Runtime::Create(const RuntimeOptions& options, bool ignore_unrecognized) { // TODO: acquire a static mutex on Runtime to avoid racing. if (Runtime::instance_ != NULL) { return false; @@ -534,7 +538,7 @@ void Runtime::StartDaemonThreads() { VLOG(startup) << "Runtime::StartDaemonThreads exiting"; } -bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { +bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) { CHECK_EQ(sysconf(_SC_PAGE_SIZE), kPageSize); std::unique_ptr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized)); @@ -580,41 +584,6 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { GetInstrumentation()->ForceInterpretOnly(); } - bool implicit_checks_supported = false; - switch (kRuntimeISA) { - case kArm: - case kThumb2: - implicit_checks_supported = true; - break; - default: - break; - } - - if (!options->interpreter_only_ && implicit_checks_supported && - (options->explicit_checks_ != (ParsedOptions::kExplicitSuspendCheck | - ParsedOptions::kExplicitNullCheck | - ParsedOptions::kExplicitStackOverflowCheck) || kEnableJavaStackTraceHandler)) { - fault_manager.Init(); - - // These need to be in a specific order. The null point check handler must be - // after the suspend check and stack overflow check handlers. - if ((options->explicit_checks_ & ParsedOptions::kExplicitSuspendCheck) == 0) { - suspend_handler_ = new SuspensionHandler(&fault_manager); - } - - if ((options->explicit_checks_ & ParsedOptions::kExplicitStackOverflowCheck) == 0) { - stack_overflow_handler_ = new StackOverflowHandler(&fault_manager); - } - - if ((options->explicit_checks_ & ParsedOptions::kExplicitNullCheck) == 0) { - null_pointer_handler_ = new NullPointerHandler(&fault_manager); - } - - if (kEnableJavaStackTraceHandler) { - new JavaStackTraceHandler(&fault_manager); - } - } - heap_ = new gc::Heap(options->heap_initial_size_, options->heap_growth_limit_, options->heap_min_free_, @@ -638,13 +607,51 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { options->verify_post_gc_heap_, options->verify_pre_gc_rosalloc_, options->verify_pre_sweeping_rosalloc_, - options->verify_post_gc_rosalloc_); + options->verify_post_gc_rosalloc_, + options->use_homogeneous_space_compaction_for_oom_, + options->min_interval_homogeneous_space_compaction_by_oom_); dump_gc_performance_on_shutdown_ = options->dump_gc_performance_on_shutdown_; BlockSignals(); InitPlatformSignalHandlers(); + // Change the implicit checks flags based on runtime architecture. + switch (kRuntimeISA) { + case kArm: + case kThumb2: + case kX86: + implicit_null_checks_ = true; + implicit_so_checks_ = true; + break; + default: + // Keep the defaults. + break; + } + + if (!options->interpreter_only_ && + (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_)) { + fault_manager.Init(); + + // These need to be in a specific order. The null point check handler must be + // after the suspend check and stack overflow check handlers. + if (implicit_suspend_checks_) { + suspend_handler_ = new SuspensionHandler(&fault_manager); + } + + if (implicit_so_checks_) { + stack_overflow_handler_ = new StackOverflowHandler(&fault_manager); + } + + if (implicit_null_checks_) { + null_pointer_handler_ = new NullPointerHandler(&fault_manager); + } + + if (kEnableJavaStackTraceHandler) { + new JavaStackTraceHandler(&fault_manager); + } + } + java_vm_ = new JavaVMExt(this, options.get()); Thread::Startup(); @@ -908,11 +915,13 @@ void Runtime::DetachCurrentThread() { thread_list_->Unregister(self); } - mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryError() const { - if (pre_allocated_OutOfMemoryError_ == NULL) { +mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryError() { + mirror::Throwable* oome = ReadBarrier::BarrierForRoot<mirror::Throwable, kWithReadBarrier>( + &pre_allocated_OutOfMemoryError_); + if (oome == NULL) { LOG(ERROR) << "Failed to return pre-allocated OOME"; } - return pre_allocated_OutOfMemoryError_; + return oome; } void Runtime::VisitConstantRoots(RootCallback* callback, void* arg) { @@ -921,6 +930,7 @@ void Runtime::VisitConstantRoots(RootCallback* callback, void* arg) { mirror::ArtField::VisitRoots(callback, arg); mirror::ArtMethod::VisitRoots(callback, arg); mirror::Class::VisitRoots(callback, arg); + mirror::Reference::VisitRoots(callback, arg); mirror::StackTraceElement::VisitRoots(callback, arg); mirror::String::VisitRoots(callback, arg); mirror::Throwable::VisitRoots(callback, arg); @@ -1013,8 +1023,8 @@ mirror::ArtMethod* Runtime::CreateImtConflictMethod() { method->SetEntryPointFromPortableCompiledCode(nullptr); method->SetEntryPointFromQuickCompiledCode(nullptr); } else { - method->SetEntryPointFromPortableCompiledCode(GetPortableImtConflictTrampoline(class_linker)); - method->SetEntryPointFromQuickCompiledCode(GetQuickImtConflictTrampoline(class_linker)); + method->SetEntryPointFromPortableCompiledCode(class_linker->GetPortableImtConflictTrampoline()); + method->SetEntryPointFromQuickCompiledCode(class_linker->GetQuickImtConflictTrampoline()); } return method.Get(); } @@ -1033,8 +1043,8 @@ mirror::ArtMethod* Runtime::CreateResolutionMethod() { method->SetEntryPointFromPortableCompiledCode(nullptr); method->SetEntryPointFromQuickCompiledCode(nullptr); } else { - method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionTrampoline(class_linker)); - method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionTrampoline(class_linker)); + method->SetEntryPointFromPortableCompiledCode(class_linker->GetPortableResolutionTrampoline()); + method->SetEntryPointFromQuickCompiledCode(class_linker->GetQuickResolutionTrampoline()); } return method.Get(); } @@ -1216,37 +1226,6 @@ void Runtime::AddCurrentRuntimeFeaturesAsDex2OatArguments(std::vector<std::strin argv->push_back("--compiler-filter=interpret-only"); } - argv->push_back("--runtime-arg"); - std::string checkstr = "-implicit-checks"; - - int nchecks = 0; - char checksep = ':'; - - if (!ExplicitNullChecks()) { - checkstr += checksep; - checksep = ','; - checkstr += "null"; - ++nchecks; - } - if (!ExplicitSuspendChecks()) { - checkstr += checksep; - checksep = ','; - checkstr += "suspend"; - ++nchecks; - } - - if (!ExplicitStackOverflowChecks()) { - checkstr += checksep; - checksep = ','; - checkstr += "stack"; - ++nchecks; - } - - if (nchecks == 0) { - checkstr += ":none"; - } - argv->push_back(checkstr); - // Make the dex2oat instruction set match that of the launching runtime. If we have multiple // architecture support, dex2oat may be compiled as a different instruction-set than that // currently being executed. diff --git a/runtime/runtime.h b/runtime/runtime.h index f839be1421..284e4ffe30 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -68,6 +68,8 @@ class ThreadList; class Trace; class Transaction; +typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions; + // Not all combinations of flags are valid. You may not visit all roots as well as the new roots // (no logical reason to do this). You also may not start logging new roots and stop logging new // roots (also no logical reason to do this). @@ -81,10 +83,8 @@ enum VisitRootFlags : uint8_t { class Runtime { public: - typedef std::vector<std::pair<std::string, const void*>> Options; - // Creates and initializes a new runtime. - static bool Create(const Options& options, bool ignore_unrecognized) + static bool Create(const RuntimeOptions& options, bool ignore_unrecognized) SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_); bool IsCompiler() const { @@ -219,8 +219,7 @@ class Runtime { return monitor_pool_; } - mirror::Throwable* GetPreAllocatedOutOfMemoryError() const - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Throwable* GetPreAllocatedOutOfMemoryError() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const std::vector<std::string>& GetProperties() const { return properties_; @@ -266,13 +265,10 @@ class Runtime { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Returns a special method that calls into a trampoline for runtime method resolution - mirror::ArtMethod* GetResolutionMethod() const { - CHECK(HasResolutionMethod()); - return resolution_method_; - } + mirror::ArtMethod* GetResolutionMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool HasResolutionMethod() const { - return resolution_method_ != NULL; + return resolution_method_ != nullptr; } void SetResolutionMethod(mirror::ArtMethod* method) { @@ -281,14 +277,11 @@ class Runtime { mirror::ArtMethod* CreateResolutionMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Returns a special method that calls into a trampoline for runtime imt conflicts - mirror::ArtMethod* GetImtConflictMethod() const { - CHECK(HasImtConflictMethod()); - return imt_conflict_method_; - } + // Returns a special method that calls into a trampoline for runtime imt conflicts. + mirror::ArtMethod* GetImtConflictMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool HasImtConflictMethod() const { - return imt_conflict_method_ != NULL; + return imt_conflict_method_ != nullptr; } void SetImtConflictMethod(mirror::ArtMethod* method) { @@ -298,13 +291,11 @@ class Runtime { mirror::ArtMethod* CreateImtConflictMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Returns an imt with every entry set to conflict, used as default imt for all classes. - mirror::ObjectArray<mirror::ArtMethod>* GetDefaultImt() const { - CHECK(HasDefaultImt()); - return default_imt_; - } + mirror::ObjectArray<mirror::ArtMethod>* GetDefaultImt() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool HasDefaultImt() const { - return default_imt_ != NULL; + return default_imt_ != nullptr; } void SetDefaultImt(mirror::ObjectArray<mirror::ArtMethod>* imt) { @@ -326,16 +317,18 @@ class Runtime { return callee_save_methods_[type] != NULL; } - mirror::ArtMethod* GetCalleeSaveMethod(CalleeSaveType type) const { - DCHECK(HasCalleeSaveMethod(type)); - return callee_save_methods_[type]; - } + mirror::ArtMethod* GetCalleeSaveMethod(CalleeSaveType type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + mirror::ArtMethod* GetCalleeSaveMethodUnchecked(CalleeSaveType type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); QuickMethodFrameInfo GetCalleeSaveMethodFrameInfo(CalleeSaveType type) const { return callee_save_method_frame_infos_[type]; } - QuickMethodFrameInfo GetRuntimeMethodFrameInfo(mirror::ArtMethod* method) const; + QuickMethodFrameInfo GetRuntimeMethodFrameInfo(mirror::ArtMethod* method) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static size_t GetCalleeSaveMethodOffset(CalleeSaveType type) { return OFFSETOF_MEMBER(Runtime, callee_save_methods_[type]); @@ -463,7 +456,7 @@ class Runtime { void BlockSignals(); - bool Init(const Options& options, bool ignore_unrecognized) + bool Init(const RuntimeOptions& options, bool ignore_unrecognized) SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_); void InitNativeMethods() LOCKS_EXCLUDED(Locks::mutator_lock_); void InitThreadGroups(Thread* self); @@ -596,6 +589,11 @@ class Runtime { // Specifies target SDK version to allow workarounds for certain API levels. int32_t target_sdk_version_; + // Implicit checks flags. + bool implicit_null_checks_; // NullPointer checks are implicit. + bool implicit_so_checks_; // StackOverflow checks are implicit. + bool implicit_suspend_checks_; // Thread suspension checks are implicit. + DISALLOW_COPY_AND_ASSIGN(Runtime); }; diff --git a/runtime/stack.cc b/runtime/stack.cc index d5405fbdf0..71e566efc3 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -16,13 +16,13 @@ #include "stack.h" +#include "arch/context.h" #include "base/hex_dump.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/object.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" -#include "object_utils.h" #include "quick/quick_method_frame_info.h" #include "runtime.h" #include "thread.h" @@ -366,9 +366,10 @@ std::string StackVisitor::DescribeLocation() const { return result; } -instrumentation::InstrumentationStackFrame& StackVisitor::GetInstrumentationStackFrame(uint32_t depth) const { - CHECK_LT(depth, thread_->GetInstrumentationStack()->size()); - return thread_->GetInstrumentationStack()->at(depth); +static instrumentation::InstrumentationStackFrame& GetInstrumentationStackFrame(Thread* thread, + uint32_t depth) { + CHECK_LT(depth, thread->GetInstrumentationStack()->size()); + return thread->GetInstrumentationStack()->at(depth); } void StackVisitor::SanityCheckFrame() const { @@ -431,7 +432,7 @@ void StackVisitor::WalkStack(bool include_transitions) { // the stack for an exception where the side stack will be unwound in VisitFrame. if (GetQuickInstrumentationExitPc() == return_pc) { const instrumentation::InstrumentationStackFrame& instrumentation_frame = - GetInstrumentationStackFrame(instrumentation_stack_depth); + GetInstrumentationStackFrame(thread_, instrumentation_stack_depth); instrumentation_stack_depth++; if (GetMethod() == Runtime::Current()->GetCalleeSaveMethod(Runtime::kSaveAll)) { // Skip runtime save all callee frames which are used to deliver exceptions. diff --git a/runtime/stack.h b/runtime/stack.h index 9402cddf56..ef498ef06f 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -17,20 +17,16 @@ #ifndef ART_RUNTIME_STACK_H_ #define ART_RUNTIME_STACK_H_ +#include <stdint.h> +#include <string> + #include "dex_file.h" -#include "instrumentation.h" -#include "arch/context.h" -#include "base/casts.h" -#include "base/macros.h" #include "instruction_set.h" -#include "mirror/object.h" #include "mirror/object_reference.h" +#include "throw_location.h" #include "utils.h" #include "verify_object.h" -#include <stdint.h> -#include <string> - namespace art { namespace mirror { @@ -711,8 +707,6 @@ class StackVisitor { bool GetFPR(uint32_t reg, uintptr_t* val) const; bool SetFPR(uint32_t reg, uintptr_t value); - instrumentation::InstrumentationStackFrame& GetInstrumentationStackFrame(uint32_t depth) const; - void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); Thread* const thread_; diff --git a/runtime/thread.cc b/runtime/thread.cc index 7827dfb650..f888029af1 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -32,27 +32,29 @@ #include "arch/context.h" #include "base/mutex.h" -#include "class_linker.h" #include "class_linker-inl.h" +#include "class_linker.h" #include "debugger.h" #include "dex_file-inl.h" #include "entrypoints/entrypoint_utils.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "gc_map.h" #include "gc/accounting/card_table-inl.h" +#include "gc/allocator/rosalloc.h" #include "gc/heap.h" #include "gc/space/space.h" +#include "handle_scope-inl.h" #include "handle_scope.h" #include "indirect_reference_table-inl.h" #include "jni_internal.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" -#include "mirror/class-inl.h" #include "mirror/class_loader.h" +#include "mirror/class-inl.h" #include "mirror/object_array-inl.h" #include "mirror/stack_trace_element.h" #include "monitor.h" -#include "object_utils.h" +#include "object_lock.h" #include "quick_exception_handler.h" #include "quick/quick_method_frame_info.h" #include "reflection.h" @@ -60,10 +62,9 @@ #include "scoped_thread_state_change.h" #include "ScopedLocalRef.h" #include "ScopedUtfChars.h" -#include "handle_scope-inl.h" #include "stack.h" -#include "thread-inl.h" #include "thread_list.h" +#include "thread-inl.h" #include "utils.h" #include "verifier/dex_gc_map.h" #include "verify_object-inl.h" @@ -231,47 +232,95 @@ static size_t FixStackSize(size_t stack_size) { return stack_size; } +// Global variable to prevent the compiler optimizing away the page reads for the stack. +byte dont_optimize_this; + // Install a protected region in the stack. This is used to trigger a SIGSEGV if a stack // overflow is detected. It is located right below the stack_end_. Just below that // is the StackOverflow reserved region used when creating the StackOverflow // exception. +// +// There is a little complexity here that deserves a special mention. When running on the +// host (glibc), the process's main thread's stack is allocated with a special flag +// to prevent memory being allocated when it's not needed. This flag makes the +// kernel only allocate memory for the stack by growing down in memory. Because we +// want to put an mprotected region far away from that at the stack top, we need +// to make sure the pages for the stack are mapped in before we call mprotect. We do +// this by reading every page from the stack bottom (highest address) to the stack top. +// We then madvise this away. void Thread::InstallImplicitProtection(bool is_main_stack) { byte* pregion = tlsPtr_.stack_end; + byte* stack_lowmem = tlsPtr_.stack_begin; + byte* stack_top = reinterpret_cast<byte*>(reinterpret_cast<uintptr_t>(&pregion) & + ~(kPageSize - 1)); // Page containing current top of stack. + + const bool running_on_intel = (kRuntimeISA == kX86) || (kRuntimeISA == kX86_64); + + if (running_on_intel) { + // On Intel, we need to map in the main stack. This must be done by reading from the + // current stack pointer downwards as the stack is mapped using VM_GROWSDOWN + // in the kernel. Any access more than a page below the current SP will cause + // a segv. + if (is_main_stack) { + // First we need to unprotect the protected region because this may + // be called more than once for a particular stack and we will crash + // if we try to read the protected page. + mprotect(pregion - kStackOverflowProtectedSize, kStackOverflowProtectedSize, PROT_READ); + + // Read every page from the high address to the low. + for (byte* p = stack_top; p > stack_lowmem; p -= kPageSize) { + dont_optimize_this = *p; + } + } + } + // Check and place a marker word at the lowest usable address in the stack. This + // is used to prevent a double protection. constexpr uint32_t kMarker = 0xdadadada; uintptr_t *marker = reinterpret_cast<uintptr_t*>(pregion); if (*marker == kMarker) { - // The region has already been set up. + // The region has already been set up. But on the main stack on the host we have + // removed the protected region in order to read the stack memory. We need to put + // this back again. + if (is_main_stack && running_on_intel) { + mprotect(pregion - kStackOverflowProtectedSize, kStackOverflowProtectedSize, PROT_NONE); + madvise(stack_lowmem, stack_top - stack_lowmem, MADV_DONTNEED); + } return; } // Add marker so that we can detect a second attempt to do this. *marker = kMarker; - pregion -= kStackOverflowProtectedSize; - - // Touch the pages in the region to map them in. Otherwise mprotect fails. Only - // need to do this on the main stack. We only need to touch one byte per page. - if (is_main_stack) { - byte* start = pregion; - byte* end = pregion + kStackOverflowProtectedSize; - while (start < end) { - *start = static_cast<byte>(0); - start += kPageSize; + if (!running_on_intel) { + // Running on !Intel, stacks are mapped cleanly. The protected region for the + // main stack just needs to be mapped in. We do this by writing one byte per page. + for (byte* p = pregion - kStackOverflowProtectedSize; p < pregion; p += kPageSize) { + *p = 0; } } + pregion -= kStackOverflowProtectedSize; + VLOG(threads) << "installing stack protected region at " << std::hex << static_cast<void*>(pregion) << " to " << static_cast<void*>(pregion + kStackOverflowProtectedSize - 1); + if (mprotect(pregion, kStackOverflowProtectedSize, PROT_NONE) == -1) { LOG(FATAL) << "Unable to create protected region in stack for implicit overflow check. Reason:" << strerror(errno); } // Tell the kernel that we won't be needing these pages any more. + // NB. madvise will probably write zeroes into the memory (on linux it does). if (is_main_stack) { - madvise(pregion, kStackOverflowProtectedSize, MADV_DONTNEED); + if (running_on_intel) { + // On the host, it's the whole stack (minus a page to prevent overwrite of stack top). + madvise(stack_lowmem, stack_top - stack_lowmem - kPageSize, MADV_DONTNEED); + } else { + // On Android, just the protected region. + madvise(pregion, kStackOverflowProtectedSize, MADV_DONTNEED); + } } } @@ -532,13 +581,17 @@ void Thread::InitStackHwm() { // Install the protected region if we are doing implicit overflow checks. if (implicit_stack_check) { if (is_main_thread) { - // The main thread has a 16K protected region at the bottom. We need + size_t guardsize; + pthread_attr_t attributes; + CHECK_PTHREAD_CALL(pthread_attr_init, (&attributes), "guard size query"); + CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, &guardsize), "guard size query"); + CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), "guard size query"); + // The main thread might have protected region at the bottom. We need // to install our own region so we need to move the limits // of the stack to make room for it. - constexpr uint32_t kDelta = 16 * KB; - tlsPtr_.stack_begin += kDelta; - tlsPtr_.stack_end += kDelta; - tlsPtr_.stack_size -= kDelta; + tlsPtr_.stack_begin += guardsize; + tlsPtr_.stack_end += guardsize; + tlsPtr_.stack_size -= guardsize; } InstallImplicitProtection(is_main_thread); } @@ -1055,7 +1108,7 @@ Thread::Thread(bool daemon) : tls32_(daemon), wait_monitor_(nullptr), interrupte tls32_.state_and_flags.as_struct.state = kNative; memset(&tlsPtr_.held_mutexes[0], 0, sizeof(tlsPtr_.held_mutexes)); std::fill(tlsPtr_.rosalloc_runs, - tlsPtr_.rosalloc_runs + gc::allocator::RosAlloc::kNumThreadLocalSizeBrackets, + tlsPtr_.rosalloc_runs + kNumRosAllocThreadLocalSizeBrackets, gc::allocator::RosAlloc::GetDedicatedFullRun()); for (uint32_t i = 0; i < kMaxCheckpoints; ++i) { tlsPtr_.checkpoint_functions[i] = nullptr; @@ -1878,6 +1931,8 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { QUICK_ENTRY_POINT_INFO(pThrowNoSuchMethod) QUICK_ENTRY_POINT_INFO(pThrowNullPointer) QUICK_ENTRY_POINT_INFO(pThrowStackOverflow) + QUICK_ENTRY_POINT_INFO(pA64Load) + QUICK_ENTRY_POINT_INFO(pA64Store) #undef QUICK_ENTRY_POINT_INFO os << offset; diff --git a/runtime/thread.h b/runtime/thread.h index 4312741a51..d08c2fce82 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -31,7 +31,6 @@ #include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/portable/portable_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "gc/allocator/rosalloc.h" #include "globals.h" #include "handle_scope.h" #include "instruction_set.h" @@ -47,7 +46,7 @@ namespace art { namespace gc { namespace collector { -class SemiSpace; + class SemiSpace; } // namespace collector } // namespace gc @@ -61,7 +60,6 @@ namespace mirror { template<class T> class PrimitiveArray; typedef PrimitiveArray<int32_t> IntArray; class StackTraceElement; - class StaticStorageBase; class Throwable; } // namespace mirror class BaseMutex; @@ -94,6 +92,8 @@ enum ThreadFlag { kCheckpointRequest = 2 // Request that the thread do some checkpoint work and then continue. }; +static constexpr size_t kNumRosAllocThreadLocalSizeBrackets = 34; + class Thread { public: // How much of the reserved bytes is reserved for incoming signals. @@ -781,7 +781,7 @@ class Thread { void RevokeThreadLocalAllocationStack(); size_t GetThreadLocalBytesAllocated() const { - return tlsPtr_.thread_local_pos - tlsPtr_.thread_local_start; + return tlsPtr_.thread_local_end - tlsPtr_.thread_local_start; } size_t GetThreadLocalObjectsAllocated() const { @@ -900,7 +900,7 @@ class Thread { // first if possible. /***********************************************************************************************/ - struct PACKED(4) tls_32bit_sized_values { + struct PACKED(4) tls_32bit_sized_values { // We have no control over the size of 'bool', but want our boolean fields // to be 4-byte quantities. typedef uint32_t bool32_t; @@ -1077,7 +1077,7 @@ class Thread { size_t thread_local_objects; // There are RosAlloc::kNumThreadLocalSizeBrackets thread-local size brackets per thread. - void* rosalloc_runs[gc::allocator::RosAlloc::kNumThreadLocalSizeBrackets]; + void* rosalloc_runs[kNumRosAllocThreadLocalSizeBrackets]; // Thread-local allocation stack data/routines. mirror::Object** thread_local_alloc_stack_top; diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 54732fae04..b649b626ca 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -39,6 +39,8 @@ namespace art { +static constexpr uint64_t kLongThreadSuspendThreshold = MsToNs(5); + ThreadList::ThreadList() : suspend_all_count_(0), debug_suspend_all_count_(0), thread_exit_cond_("thread exit condition variable", *Locks::thread_list_lock_) { @@ -304,8 +306,8 @@ void ThreadList::SuspendAll() { DCHECK(self != nullptr); VLOG(threads) << *self << " SuspendAll starting..."; - ATRACE_BEGIN("Suspending mutator threads"); + uint64_t start_time = NanoTime(); Locks::mutator_lock_->AssertNotHeld(self); Locks::thread_list_lock_->AssertNotHeld(self); @@ -338,6 +340,11 @@ void ThreadList::SuspendAll() { Locks::mutator_lock_->ExclusiveLock(self); #endif + uint64_t end_time = NanoTime(); + if (end_time - start_time > kLongThreadSuspendThreshold) { + LOG(WARNING) << "Suspending all threads took: " << PrettyDuration(end_time - start_time); + } + if (kDebugLocking) { // Debug check that all threads are suspended. AssertThreadsAreSuspended(self, self); diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc index 292c94f64b..4bd44dcb0f 100644 --- a/runtime/thread_pool_test.cc +++ b/runtime/thread_pool_test.cc @@ -20,6 +20,7 @@ #include "atomic.h" #include "common_runtime_test.h" +#include "thread-inl.h" namespace art { diff --git a/runtime/throw_location.cc b/runtime/throw_location.cc index a1347a49bb..04abe64453 100644 --- a/runtime/throw_location.cc +++ b/runtime/throw_location.cc @@ -19,7 +19,6 @@ #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "utils.h" namespace art { diff --git a/runtime/trace.cc b/runtime/trace.cc index 1a450c4cb9..f51b8c435a 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -30,7 +30,6 @@ #include "mirror/dex_cache.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "os.h" #include "scoped_thread_state_change.h" #include "ScopedLocalRef.h" @@ -115,7 +114,7 @@ static const uint16_t kTraceVersionDualClock = 3; static const uint16_t kTraceRecordSizeSingleClock = 10; // using v2 static const uint16_t kTraceRecordSizeDualClock = 14; // using v3 with two timestamps -ProfilerClockSource Trace::default_clock_source_ = kDefaultProfilerClockSource; +TraceClockSource Trace::default_clock_source_ = kDefaultTraceClockSource; Trace* volatile Trace::the_trace_ = NULL; pthread_t Trace::sampling_pthread_ = 0U; @@ -149,34 +148,34 @@ void Trace::FreeStackTrace(std::vector<mirror::ArtMethod*>* stack_trace) { temp_stack_trace_.reset(stack_trace); } -void Trace::SetDefaultClockSource(ProfilerClockSource clock_source) { +void Trace::SetDefaultClockSource(TraceClockSource clock_source) { #if defined(HAVE_POSIX_CLOCKS) default_clock_source_ = clock_source; #else - if (clock_source != kProfilerClockSourceWall) { + if (clock_source != kTraceClockSourceWall) { LOG(WARNING) << "Ignoring tracing request to use CPU time."; } #endif } -static uint16_t GetTraceVersion(ProfilerClockSource clock_source) { - return (clock_source == kProfilerClockSourceDual) ? kTraceVersionDualClock +static uint16_t GetTraceVersion(TraceClockSource clock_source) { + return (clock_source == kTraceClockSourceDual) ? kTraceVersionDualClock : kTraceVersionSingleClock; } -static uint16_t GetRecordSize(ProfilerClockSource clock_source) { - return (clock_source == kProfilerClockSourceDual) ? kTraceRecordSizeDualClock +static uint16_t GetRecordSize(TraceClockSource clock_source) { + return (clock_source == kTraceClockSourceDual) ? kTraceRecordSizeDualClock : kTraceRecordSizeSingleClock; } bool Trace::UseThreadCpuClock() { - return (clock_source_ == kProfilerClockSourceThreadCpu) || - (clock_source_ == kProfilerClockSourceDual); + return (clock_source_ == kTraceClockSourceThreadCpu) || + (clock_source_ == kTraceClockSourceDual); } bool Trace::UseWallClock() { - return (clock_source_ == kProfilerClockSourceWall) || - (clock_source_ == kProfilerClockSourceDual); + return (clock_source_ == kTraceClockSourceWall) || + (clock_source_ == kTraceClockSourceDual); } static void MeasureClockOverhead(Trace* trace) { @@ -462,7 +461,7 @@ Trace::Trace(File* trace_file, int buffer_size, int flags, bool sampling_enabled cur_offset_.StoreRelaxed(kTraceHeaderLength); } -static void DumpBuf(uint8_t* buf, size_t buf_size, ProfilerClockSource clock_source) +static void DumpBuf(uint8_t* buf, size_t buf_size, TraceClockSource clock_source) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { uint8_t* ptr = buf + kTraceHeaderLength; uint8_t* end = buf + buf_size; diff --git a/runtime/trace.h b/runtime/trace.h index 9c8d35bb93..d7836b8965 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -36,19 +36,8 @@ namespace mirror { class ArtField; class ArtMethod; } // namespace mirror -class Thread; - -enum ProfilerClockSource { - kProfilerClockSourceThreadCpu, - kProfilerClockSourceWall, - kProfilerClockSourceDual, // Both wall and thread CPU clocks. -}; -#if defined(HAVE_POSIX_CLOCKS) -const ProfilerClockSource kDefaultProfilerClockSource = kProfilerClockSourceDual; -#else -const ProfilerClockSource kDefaultProfilerClockSource = kProfilerClockSourceWall; -#endif +class Thread; enum TracingMode { kTracingInactive, @@ -62,7 +51,7 @@ class Trace FINAL : public instrumentation::InstrumentationListener { kTraceCountAllocs = 1, }; - static void SetDefaultClockSource(ProfilerClockSource clock_source); + static void SetDefaultClockSource(TraceClockSource clock_source); static void Start(const char* trace_filename, int trace_fd, int buffer_size, int flags, bool direct_to_ddms, bool sampling_enabled, int interval_us) @@ -138,7 +127,7 @@ class Trace FINAL : public instrumentation::InstrumentationListener { static Trace* volatile the_trace_ GUARDED_BY(Locks::trace_lock_); // The default profiler clock source. - static ProfilerClockSource default_clock_source_; + static TraceClockSource default_clock_source_; // Sampling thread, non-zero when sampling. static pthread_t sampling_pthread_; @@ -158,7 +147,7 @@ class Trace FINAL : public instrumentation::InstrumentationListener { // True if traceview should sample instead of instrumenting method entry/exit. const bool sampling_enabled_; - const ProfilerClockSource clock_source_; + const TraceClockSource clock_source_; // Size of buf_. const int buffer_size_; diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc index a03b389e0b..691aec4f2c 100644 --- a/runtime/transaction_test.cc +++ b/runtime/transaction_test.cc @@ -20,6 +20,7 @@ #include "mirror/array-inl.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" +#include "scoped_thread_state_change.h" namespace art { diff --git a/runtime/utils.cc b/runtime/utils.cc index d038571e96..8b1ad39edc 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -28,6 +28,7 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "dex_file-inl.h" +#include "field_helper.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" @@ -35,7 +36,6 @@ #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string.h" -#include "object_utils.h" #include "os.h" #include "scoped_thread_state_change.h" #include "utf-inl.h" @@ -281,11 +281,6 @@ std::string PrettyDescriptor(const std::string& descriptor) { return result; } -std::string PrettyDescriptor(Primitive::Type type) { - std::string descriptor_string(Primitive::Descriptor(type)); - return PrettyDescriptor(descriptor_string); -} - std::string PrettyField(mirror::ArtField* f, bool with_type) { if (f == NULL) { return "null"; diff --git a/runtime/utils.h b/runtime/utils.h index 448c591f2b..c920050234 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -26,7 +26,7 @@ #include "base/logging.h" #include "globals.h" #include "instruction_set.h" -#include "primitive.h" +#include "base/mutex.h" #ifdef HAVE_ANDROID_OS #include "cutils/properties.h" @@ -167,6 +167,9 @@ struct TypeIdentity { // For rounding integers. template<typename T> +static constexpr T RoundDown(T x, typename TypeIdentity<T>::type n) WARN_UNUSED; + +template<typename T> static constexpr T RoundDown(T x, typename TypeIdentity<T>::type n) { return DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0)) @@ -174,17 +177,26 @@ static constexpr T RoundDown(T x, typename TypeIdentity<T>::type n) { } template<typename T> +static constexpr T RoundUp(T x, typename TypeIdentity<T>::type n) WARN_UNUSED; + +template<typename T> static constexpr T RoundUp(T x, typename TypeIdentity<T>::type n) { return RoundDown(x + n - 1, n); } // For aligning pointers. template<typename T> +static inline T* AlignDown(T* x, uintptr_t n) WARN_UNUSED; + +template<typename T> static inline T* AlignDown(T* x, uintptr_t n) { return reinterpret_cast<T*>(RoundDown(reinterpret_cast<uintptr_t>(x), n)); } template<typename T> +static inline T* AlignUp(T* x, uintptr_t n) WARN_UNUSED; + +template<typename T> static inline T* AlignUp(T* x, uintptr_t n) { return reinterpret_cast<T*>(RoundUp(reinterpret_cast<uintptr_t>(x), n)); } @@ -265,7 +277,6 @@ bool EndsWith(const std::string& s, const char* suffix); std::string PrettyDescriptor(mirror::String* descriptor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); std::string PrettyDescriptor(const std::string& descriptor); -std::string PrettyDescriptor(Primitive::Type type); std::string PrettyDescriptor(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index eabb993879..f1b5afd235 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -25,10 +25,12 @@ #include "dex_file-inl.h" #include "dex_instruction-inl.h" #include "dex_instruction_visitor.h" +#include "field_helper.h" #include "gc/accounting/card_table-inl.h" #include "indenter.h" #include "intern_table.h" #include "leb128.h" +#include "method_helper-inl.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class.h" @@ -36,7 +38,6 @@ #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" -#include "object_utils.h" #include "register_line-inl.h" #include "runtime.h" #include "scoped_thread_state_change.h" diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc index 9ac04d7067..a5895e6dbf 100644 --- a/runtime/verifier/method_verifier_test.cc +++ b/runtime/verifier/method_verifier_test.cc @@ -19,9 +19,10 @@ #include <stdio.h> #include <memory> -#include "class_linker.h" +#include "class_linker-inl.h" #include "common_runtime_test.h" #include "dex_file.h" +#include "scoped_thread_state_change.h" namespace art { namespace verifier { diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index e24c92091c..f0729e4271 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -24,7 +24,6 @@ #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" -#include "object_utils.h" #include "reg_type_cache-inl.h" #include "scoped_thread_state_change.h" diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index ff9edbbeaf..91fba4d2bb 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -21,7 +21,6 @@ #include "dex_file-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" namespace art { namespace verifier { diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc index 1935a5b1bc..9dc0df13fb 100644 --- a/runtime/verifier/reg_type_test.cc +++ b/runtime/verifier/reg_type_test.cc @@ -21,6 +21,8 @@ #include "base/casts.h" #include "common_runtime_test.h" #include "reg_type_cache-inl.h" +#include "scoped_thread_state_change.h" +#include "thread-inl.h" namespace art { namespace verifier { diff --git a/runtime/verify_object.h b/runtime/verify_object.h index 6640e0dd4a..8e1653ddb9 100644 --- a/runtime/verify_object.h +++ b/runtime/verify_object.h @@ -52,10 +52,10 @@ static constexpr VerifyObjectFlags kDefaultVerifyFlags = kVerifyNone; static constexpr VerifyObjectMode kVerifyObjectSupport = kDefaultVerifyFlags != 0 ? kVerifyObjectModeFast : kVerifyObjectModeDisabled; -void VerifyObject(mirror::Object* obj) ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS; +ALWAYS_INLINE void VerifyObject(mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS; // Check that c.getClass() == c.getClass().getClass(). -bool VerifyClassClass(mirror::Class* c) ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS; +ALWAYS_INLINE bool VerifyClassClass(mirror::Class* c) NO_THREAD_SAFETY_ANALYSIS; } // namespace art diff --git a/runtime/zip_archive_test.cc b/runtime/zip_archive_test.cc index d303d1e502..96abee2dc3 100644 --- a/runtime/zip_archive_test.cc +++ b/runtime/zip_archive_test.cc @@ -22,6 +22,7 @@ #include <zlib.h> #include <memory> +#include "base/unix_file/fd_file.h" #include "common_runtime_test.h" #include "os.h" diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk index 8e25339d9e..d86735d120 100644 --- a/sigchainlib/Android.mk +++ b/sigchainlib/Android.mk @@ -23,8 +23,23 @@ LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) LOCAL_SRC_FILES := sigchain.cc +LOCAL_CLANG = $(ART_TARGET_CLANG) LOCAL_MODULE:= libsigchain LOCAL_SHARED_LIBRARIES := liblog libdl LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk include $(BUILD_SHARED_LIBRARY) + +# Build host library. +include $(CLEAR_VARS) +LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) +LOCAL_MODULE_TAGS := optional +LOCAL_IS_HOST_MODULE := true +LOCAL_CFLAGS += $(ART_HOST_CFLAGS) +LOCAL_CLANG = $(ART_HOST_CLANG) +LOCAL_SRC_FILES := sigchain.cc +LOCAL_MODULE:= libsigchain +LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk +LOCAL_LDLIBS = -ldl +LOCAL_MULTILIB := both +include $(BUILD_HOST_SHARED_LIBRARY) diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index 5a5805fe4f..6f93083832 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -14,12 +14,22 @@ * limitations under the License. */ +#ifdef HAVE_ANDROID_OS #include <android/log.h> +#else +#include <stdarg.h> +#include <iostream> +#endif + #include <dlfcn.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> +#if defined(__APPLE__) +#define _NSIG NSIG +#endif + namespace art { class SignalAction { @@ -67,7 +77,11 @@ static void log(const char* format, ...) { va_list ap; va_start(ap, format); vsnprintf(buf, sizeof(buf), format, ap); +#ifdef HAVE_ANDROID_OS __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf); +#else + std::cout << buf << "\n"; +#endif va_end(ap); } @@ -104,10 +118,16 @@ void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) { if ((action.sa_flags & SA_SIGINFO) == 0) { if (action.sa_handler != NULL) { action.sa_handler(sig); + } else { + signal(sig, SIG_DFL); + raise(sig); } } else { if (action.sa_sigaction != NULL) { action.sa_sigaction(sig, info, context); + } else { + signal(sig, SIG_DFL); + raise(sig); } } } diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h index f6f2253d72..a4ce81ce4c 100644 --- a/sigchainlib/sigchain.h +++ b/sigchainlib/sigchain.h @@ -18,10 +18,13 @@ #define ART_SIGCHAINLIB_SIGCHAIN_H_ #include <signal.h> + namespace art { void ClaimSignalChain(int signal, struct sigaction* oldaction); + void UnclaimSignalChain(int signal); + void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context); } // namespace art diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index f412034de2..1c3c89ea3d 100644 --- a/test/082-inline-execute/src/Main.java +++ b/test/082-inline-execute/src/Main.java @@ -34,6 +34,9 @@ public class Main { test_Math_max_F(); test_Math_min_D(); test_Math_max_D(); + test_Short_reverseBytes(); + test_Integer_reverseBytes(); + test_Long_reverseBytes(); test_Integer_reverse(); test_Long_reverse(); test_StrictMath_abs_I(); @@ -61,9 +64,6 @@ public class Main { test_Memory_pokeShort(); test_Memory_pokeInt(); test_Memory_pokeLong(); - test_AtomicBoolean_compareAndSet(); - test_AtomicInteger_compareAndSet(); - test_AtomicLong_compareAndSet(); } /* @@ -96,60 +96,6 @@ public class Main { Assert.assertNotNull(Thread.currentThread()); } - /** - * Will test inlining CAS, by inclusion of AtomicBoolean in core.oat. - */ - public static void test_AtomicBoolean_compareAndSet() { - java.util.concurrent.atomic.AtomicBoolean ab = new java.util.concurrent.atomic.AtomicBoolean(); - Assert.assertEquals(ab.compareAndSet(false, false), true); - Assert.assertEquals(ab.compareAndSet(true, false), false); - Assert.assertEquals(ab.compareAndSet(true, true), false); - Assert.assertEquals(ab.compareAndSet(false, true), true); - Assert.assertEquals(ab.compareAndSet(false, true), false); - Assert.assertEquals(ab.compareAndSet(false, false), false); - Assert.assertEquals(ab.compareAndSet(true, true), true); - Assert.assertEquals(ab.compareAndSet(true, false), true); - Assert.assertEquals(ab.compareAndSet(true, false), false); - Assert.assertEquals(ab.compareAndSet(true, true), false); - Assert.assertEquals(ab.compareAndSet(false, false), true); - } - - /** - * Will test inlining CAS, by inclusion of AtomicInteger in core.oat. - */ - public static void test_AtomicInteger_compareAndSet() { - java.util.concurrent.atomic.AtomicInteger ab = new java.util.concurrent.atomic.AtomicInteger(); - Assert.assertEquals(ab.compareAndSet(0, 0), true); - Assert.assertEquals(ab.compareAndSet(0x12345678, 0), false); - Assert.assertEquals(ab.compareAndSet(0x12345678, 0x12345678), false); - Assert.assertEquals(ab.compareAndSet(0, 0x12345678), true); - Assert.assertEquals(ab.compareAndSet(0, 0x12345678), false); - Assert.assertEquals(ab.compareAndSet(0, 0), false); - Assert.assertEquals(ab.compareAndSet(0x12345678, 0x12345678), true); - Assert.assertEquals(ab.compareAndSet(0x12345678, 0), true); - Assert.assertEquals(ab.compareAndSet(0x12345678, 0), false); - Assert.assertEquals(ab.compareAndSet(0x12345678, 0x12345678), false); - Assert.assertEquals(ab.compareAndSet(0, 0), true); - } - - /** - * Will test inlining CAS, by inclusion of AtomicLong in core.oat. - */ - public static void test_AtomicLong_compareAndSet() { - java.util.concurrent.atomic.AtomicLong ab = new java.util.concurrent.atomic.AtomicLong(); - Assert.assertEquals(ab.compareAndSet(0l, 0l), true); - Assert.assertEquals(ab.compareAndSet(0x1234567890l, 0l), false); - Assert.assertEquals(ab.compareAndSet(0x1234567890l, 0x1234567890l), false); - Assert.assertEquals(ab.compareAndSet(0l, 0x1234567890l), true); - Assert.assertEquals(ab.compareAndSet(0l, 0x1234567890l), false); - Assert.assertEquals(ab.compareAndSet(0l, 0l), false); - Assert.assertEquals(ab.compareAndSet(0x1234567890l, 0x1234567890l), true); - Assert.assertEquals(ab.compareAndSet(0x1234567890l, 0l), true); - Assert.assertEquals(ab.compareAndSet(0x1234567890l, 0l), false); - Assert.assertEquals(ab.compareAndSet(0x1234567890l, 0x1234567890l), false); - Assert.assertEquals(ab.compareAndSet(0l, 0l), true); - } - public static void test_String_length() { String str0 = ""; String str1 = "x"; @@ -556,6 +502,34 @@ public class Main { Assert.assertEquals(Double.longBitsToDouble(0xfff0000000000000L), Double.NEGATIVE_INFINITY); } + public static void test_Short_reverseBytes() { + Assert.assertEquals(Short.reverseBytes((short)0x0000), (short)0x0000); + Assert.assertEquals(Short.reverseBytes((short)0xffff), (short)0xffff); + Assert.assertEquals(Short.reverseBytes((short)0x8000), (short)0x0080); + Assert.assertEquals(Short.reverseBytes((short)0x0080), (short)0x8000); + Assert.assertEquals(Short.reverseBytes((short)0x0123), (short)0x2301); + Assert.assertEquals(Short.reverseBytes((short)0x4567), (short)0x6745); + Assert.assertEquals(Short.reverseBytes((short)0x89ab), (short)0xab89); + Assert.assertEquals(Short.reverseBytes((short)0xcdef), (short)0xefcd); + } + + public static void test_Integer_reverseBytes() { + Assert.assertEquals(Integer.reverseBytes(0x00000000), 0x00000000); + Assert.assertEquals(Integer.reverseBytes(0xffffffff), 0xffffffff); + Assert.assertEquals(Integer.reverseBytes(0x80000000), 0x00000080); + Assert.assertEquals(Integer.reverseBytes(0x00000080), 0x80000000); + Assert.assertEquals(Integer.reverseBytes(0x01234567), 0x67452301); + Assert.assertEquals(Integer.reverseBytes(0x89abcdef), 0xefcdab89); + } + + public static void test_Long_reverseBytes() { + Assert.assertEquals(Long.reverseBytes(0x0000000000000000L), 0x0000000000000000L); + Assert.assertEquals(Long.reverseBytes(0xffffffffffffffffL), 0xffffffffffffffffL); + Assert.assertEquals(Long.reverseBytes(0x8000000000000000L), 0x0000000000000080L); + Assert.assertEquals(Long.reverseBytes(0x0000000000000080L), 0x8000000000000000L); + Assert.assertEquals(Long.reverseBytes(0x0123456789abcdefL), 0xefcdab8967452301L); + } + public static void test_Integer_reverse() { Assert.assertEquals(Integer.reverse(1), 0x80000000); Assert.assertEquals(Integer.reverse(-1), 0xffffffff); @@ -570,16 +544,15 @@ public class Main { Assert.assertEquals(Long.reverse(1L), 0x8000000000000000L); Assert.assertEquals(Long.reverse(-1L), 0xffffffffffffffffL); Assert.assertEquals(Long.reverse(0L), 0L); - // FIXME: This asserts fail with or without this patch. I have collected - // the expected results on my host machine. - // Assert.assertEquals(Long.reverse(0x1234567812345678L), 0x1e6a2c481e6a2c48L); - // Assert.assertEquals(Long.reverse(0x8765432187654321L), 0x84c2a6e184c2a6e1L); - // Assert.assertEquals(Long.reverse(Long.MAX_VALUE), 0xfffffffffffffffeL); + Assert.assertEquals(Long.reverse(0x1234567812345678L), 0x1e6a2c481e6a2c48L); + Assert.assertEquals(Long.reverse(0x8765432187654321L), 0x84c2a6e184c2a6e1L); + Assert.assertEquals(Long.reverse(Long.MAX_VALUE), 0xfffffffffffffffeL); Assert.assertEquals(Long.reverse(Long.MIN_VALUE), 1L); } static Object runtime; static Method address_of; + static Method new_non_movable_array; static Method peek_byte; static Method peek_short; static Method peek_int; @@ -594,6 +567,7 @@ public class Main { Method get_runtime = vm_runtime.getDeclaredMethod("getRuntime"); runtime = get_runtime.invoke(null); address_of = vm_runtime.getDeclaredMethod("addressOf", Object.class); + new_non_movable_array = vm_runtime.getDeclaredMethod("newNonMovableArray", Class.class, Integer.TYPE); Class<?> io_memory = Class.forName("libcore.io.Memory"); peek_byte = io_memory.getDeclaredMethod("peekByte", Long.TYPE); @@ -607,7 +581,7 @@ public class Main { } public static void test_Memory_peekByte() throws Exception { - byte[] b = new byte [2]; + byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 2); b[0] = 0x12; b[1] = 0x11; long address = (long)address_of.invoke(runtime, b); @@ -616,7 +590,7 @@ public class Main { } public static void test_Memory_peekShort() throws Exception { - byte[] b = new byte [3]; + byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 3); b[0] = 0x13; b[1] = 0x12; b[2] = 0x11; @@ -626,7 +600,7 @@ public class Main { } public static void test_Memory_peekInt() throws Exception { - byte[] b = new byte [5]; + byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 5); b[0] = 0x15; b[1] = 0x14; b[2] = 0x13; @@ -638,7 +612,7 @@ public class Main { } public static void test_Memory_peekLong() throws Exception { - byte[] b = new byte [9]; + byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 9); b[0] = 0x19; b[1] = 0x18; b[2] = 0x17; @@ -655,7 +629,7 @@ public class Main { public static void test_Memory_pokeByte() throws Exception { byte[] r = {0x11, 0x12}; - byte[] b = new byte [2]; + byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 2); long address = (long)address_of.invoke(runtime, b); poke_byte.invoke(null, address, (byte)0x11); poke_byte.invoke(null, address + 1, (byte)0x12); @@ -665,7 +639,7 @@ public class Main { public static void test_Memory_pokeShort() throws Exception { byte[] ra = {0x12, 0x11, 0x13}; byte[] ru = {0x12, 0x22, 0x21}; - byte[] b = new byte [3]; + byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 3); long address = (long)address_of.invoke(runtime, b); // Aligned write @@ -681,7 +655,7 @@ public class Main { public static void test_Memory_pokeInt() throws Exception { byte[] ra = {0x14, 0x13, 0x12, 0x11, 0x15}; byte[] ru = {0x14, 0x24, 0x23, 0x22, 0x21}; - byte[] b = new byte [5]; + byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 5); long address = (long)address_of.invoke(runtime, b); b[4] = 0x15; @@ -695,7 +669,7 @@ public class Main { public static void test_Memory_pokeLong() throws Exception { byte[] ra = {0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x19}; byte[] ru = {0x18, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21}; - byte[] b = new byte [9]; + byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 9); long address = (long)address_of.invoke(runtime, b); b[8] = 0x19; diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java index 0f7527cd45..18bc674b0a 100644 --- a/test/083-compiler-regressions/src/Main.java +++ b/test/083-compiler-regressions/src/Main.java @@ -9638,6 +9638,7 @@ class MirOpSelectTests { private static int ifGezThen7Else4(int i) { return (i >= 0) ? 7 : 4; } private static int ifGtzThen2Else9(int i) { return (i > 0) ? 2 : 9; } private static int ifLezThen8Else0(int i) { return (i <= 0) ? 8 : 0; } + private static int ifGtzThen8Else9(int i) { return (i > 0) ? 8 : 9; } private static int ifEqz(int src, int thn, int els) { return (src == 0) ? thn : els; } private static int ifNez(int src, int thn, int els) { return (src != 0) ? thn : els; } @@ -9714,6 +9715,8 @@ class MirOpSelectTests { ifLez(-1, 116, 216), 116, ifLez(0, 117, 217), 117, ifLez(1, 118, 218), 218, + ifGtzThen8Else9(0), 9, + ifGtzThen8Else9(1), 8 }; boolean success = true; diff --git a/test/401-optimizing-compiler/expected.txt b/test/401-optimizing-compiler/expected.txt index 97492a411f..d6ef64b96b 100644 --- a/test/401-optimizing-compiler/expected.txt +++ b/test/401-optimizing-compiler/expected.txt @@ -11,3 +11,4 @@ Forced GC Forced GC Forced GC Forced GC +Forced GC diff --git a/test/401-optimizing-compiler/src/Main.java b/test/401-optimizing-compiler/src/Main.java index e5706a52fe..a5192e1e64 100644 --- a/test/401-optimizing-compiler/src/Main.java +++ b/test/401-optimizing-compiler/src/Main.java @@ -71,6 +71,10 @@ public class Main { if (m.$opt$TestOtherParameter(new Main()) == m) { throw new Error("Unexpected value returned"); } + + if (m.$opt$TestReturnNewObject(m) == m) { + throw new Error("Unexpected value returned"); + } } static int $opt$TestInvokeIntParameter(int param) { @@ -108,6 +112,12 @@ public class Main { return other; } + Object $opt$TestReturnNewObject(Object other) { + Object o = new Object(); + forceGCStaticMethod(); + return o; + } + public static void $opt$TestInvokeStatic() { printStaticMethod(); printStaticMethodWith2Args(1, 2); diff --git a/test/406-fields/expected.txt b/test/406-fields/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/406-fields/expected.txt diff --git a/test/406-fields/info.txt b/test/406-fields/info.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/406-fields/info.txt diff --git a/test/406-fields/src/Main.java b/test/406-fields/src/Main.java new file mode 100644 index 0000000000..3e94e42945 --- /dev/null +++ b/test/406-fields/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. + */ + +// Simple test for field accesses. + +public class Main extends TestCase { + public static void main(String[] args) { + $opt$testAll(); + } + + static void $opt$testAll() { + AllFields fields = new AllFields(); + + assertEquals(false, fields.iZ); + assertEquals(0, fields.iB); + assertEquals(0, fields.iC); + assertEquals(0, fields.iI); + assertEquals(0, fields.iJ); + assertEquals(0, fields.iS); + assertNull(fields.iObject); + + long longValue = -1122198787987987987L; + fields.iZ = true; + fields.iB = -2; + fields.iC = 'c'; + fields.iI = 42; + fields.iJ = longValue; + fields.iS = 68; + fields.iObject = fields; + + assertEquals(true, fields.iZ); + assertEquals(-2, fields.iB); + assertEquals('c', fields.iC); + assertEquals(42, fields.iI); + assertEquals(longValue, fields.iJ); + assertEquals(68, fields.iS); + assertEquals(fields, fields.iObject); + } + + static class AllFields { + boolean iZ; + byte iB; + char iC; + double iD; + float iF; + int iI; + long iJ; + short iS; + Object iObject; + } +} diff --git a/test/406-fields/src/TestCase.java b/test/406-fields/src/TestCase.java new file mode 100644 index 0000000000..ef77f71f3f --- /dev/null +++ b/test/406-fields/src/TestCase.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common superclass for test cases. + */ + +import java.util.Arrays; + +public abstract class TestCase { + public static void assertSame(Object expected, Object value) { + if (expected != value) { + throw new AssertionError("Objects are not the same: expected " + + String.valueOf(expected) + ", got " + String.valueOf(value)); + } + } + + public static void assertNotSame(Object expected, Object value) { + if (expected == value) { + throw new AssertionError( + "Objects are the same: " + String.valueOf(expected)); + } + } + + public static void assertEquals(String message, int expected, int actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(int expected, int actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertTrue(String message, boolean condition) { + if (!condition) { + throw new AssertionError(message); + } + } + + public static void assertTrue(boolean condition) { + assertTrue("Expected true", condition); + } + + public static void assertFalse(String message, boolean condition) { + if (condition) { + throw new AssertionError(message); + } + } + + public static void assertFalse(boolean condition) { + assertFalse("Expected false", condition); + } + + public static void assertEquals(Object expected, Object actual) { + if (!expected.equals(actual)) { + String msg = "Expected \"" + expected + "\" but got \"" + actual + "\""; + throw new AssertionError(msg); + } + } + + public static void assertNotEquals(int expected, int actual) { + if (expected == actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertNotEquals(Object expected, Object actual) { + if (expected.equals(actual)) { + String msg = "Objects are the same: " + String.valueOf(expected); + throw new AssertionError(msg); + } + } + + public static <T> void assertArrayEquals(T[] actual, T... expected) { + assertTrue(Arrays.equals(expected, actual)); + } + + public static void assertEquals( + String message, Object expected, Object actual) { + if (!expected.equals(actual)) { + throw new AssertionError(message); + } + } + + public static void assertEquals( + String message, long expected, long actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(long expected, long actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertEquals( + String message, boolean expected, boolean actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(boolean expected, boolean actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertEquals( + String message, float expected, float actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(float expected, float actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertEquals(float expected, float actual, + float tolerance) { + if ((actual < expected - tolerance) || (expected + tolerance < actual)) { + throw new AssertionError("Expected " + expected + " got " + actual + + " tolerance " + tolerance); + } + } + + public static void assertEquals( + String message, double expected, double actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertEquals(double expected, double actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } + + public static void assertEquals(double expected, double actual, + double tolerance) { + if ((actual < expected - tolerance) || (expected + tolerance < actual)) { + throw new AssertionError("Expected " + expected + " got " + actual + + " tolerance " + tolerance); + } + } + + public static void assertSame( + String message, Object expected, Object actual) { + if (expected != actual) { + throw new AssertionError(message); + } + } + + public static void assertNull(String message, Object object) { + if (object != null) { + throw new AssertionError(message); + } + } + + public static void assertNull(Object object) { + assertNull("Expected null", object); + } + + public static void assertNotNull(String message, Object object) { + if (object == null) { + throw new AssertionError(message); + } + } + + public static void assertNotNull(Object object) { + assertNotNull("Expected non-null", object); + } + + public static void fail(String msg) { + throw new AssertionError(msg); + } +} diff --git a/test/701-easy-div-rem/expected.txt b/test/701-easy-div-rem/expected.txt new file mode 100644 index 0000000000..97be3436b0 --- /dev/null +++ b/test/701-easy-div-rem/expected.txt @@ -0,0 +1,8 @@ +Begin +Int: checking some equally spaced dividends... +Int: checking small dividends... +Int: checking big dividends... +Long: checking some equally spaced dividends... +Long: checking small dividends... +Long: checking big dividends... +End diff --git a/test/701-easy-div-rem/genMain.py b/test/701-easy-div-rem/genMain.py new file mode 100644 index 0000000000..80eac34463 --- /dev/null +++ b/test/701-easy-div-rem/genMain.py @@ -0,0 +1,155 @@ +# 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. + +all_tests = [ + ({'@INT@': 'int', '@SUFFIX@':''}, + [('CheckDiv', 'idiv_by_pow2_', [2**i for i in range(31)]), + ('CheckDiv', 'idiv_by_small_', [i for i in range(3, 16) if i not in (4, 8)]), + ('CheckRem', 'irem_by_pow2_', [2**i for i in range(31)])]), + ({'@INT@': 'long', '@SUFFIX@': 'l'}, + [('CheckDiv', 'ldiv_by_pow2_', [2**i for i in range(63)]), + ('CheckDiv', 'ldiv_by_small_', [i for i in range(3, 16) if i not in (4, 8)]), + ('CheckRem', 'lrem_by_pow2_', [2**i for i in range(63)])]) +] + +def subst_vars(variables, text): + '''Substitute variables in text.''' + for key, value in variables.iteritems(): + text = text.replace(str(key), str(value)) + return text + +# Generate all the function bodies (in decls) and all the function calls (in calls). +decls, calls = '', {} +for default_vars, tests in all_tests: + local_vars = default_vars.copy() + int_type = local_vars['@INT@'] + for checker, name, values in tests: + local_vars['@CHECKER@'] = checker + for i, value in enumerate(values): + local_vars['@NAME@'] = name + str(i) + local_vars['@VALUE@'] = value + local_vars['@OP@'] = '/' if 'div' in name else '%' + + # Function body. + decls += subst_vars(local_vars, ''' + public static @INT@ @NAME@(@INT@ x) {return x @OP@ @VALUE@@SUFFIX@;}''') + + # Function call and test. + calls[int_type] = calls.get(int_type, '') + subst_vars(local_vars, ''' + @INT@@CHECKER@("@NAME@", @NAME@(x), x, @VALUE@@SUFFIX@);''') + +# Generate the checkers. +checkers = '' +local_vars = {} +for int_type in ('int', 'long'): + local_vars['@INT@'] = int_type + for op, op_name in (('/', 'Div'), ('%', 'Rem')): + local_vars['@OP@'] = op + local_vars['@OP_NAME@'] = op_name + checkers += subst_vars(local_vars, ''' + public static void @INT@Check@OP_NAME@(String desc, @INT@ result, @INT@ dividend, @INT@ divisor) { + @INT@ correct_result = dividend @OP@ divisor; + if (result != correct_result) { + reportError(desc + "(" + dividend + ") == " + result + + " should be " + correct_result); + } + }''') + + +code = \ +'''/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static int num_errors = 0; + + public static void reportError(String message) { + if (num_errors == 10) { + System.out.println("Omitting other error messages..."); + } else if (num_errors < 10) { + System.out.println(message); + } + num_errors += 1; + } +%s +%s + + public static void intCheckAll(int x) {%s + } + + public static void longCheckAll(long x) {%s + } + + public static void main(String[] args) { + int i; + long l; + + System.out.println("Begin"); + + System.out.println("Int: checking some equally spaced dividends..."); + for (i = -1000; i < 1000; i += 300) { + intCheckAll(i); + intCheckAll(-i); + } + + System.out.println("Int: checking small dividends..."); + for (i = 1; i < 100; i += 1) { + intCheckAll(i); + intCheckAll(-i); + } + + System.out.println("Int: checking big dividends..."); + for (i = 0; i < 100; i += 1) { + intCheckAll(Integer.MAX_VALUE - i); + intCheckAll(Integer.MIN_VALUE + i); + } + + System.out.println("Long: checking some equally spaced dividends..."); + for (l = 0l; l < 1000000000000l; l += 300000000000l) { + longCheckAll(l); + longCheckAll(-l); + } + + System.out.println("Long: checking small dividends..."); + for (l = 1l; l < 100l; l += 1l) { + longCheckAll(l); + longCheckAll(-l); + } + + System.out.println("Long: checking big dividends..."); + for (l = 0l; l < 100l; l += 1l) { + longCheckAll(Long.MAX_VALUE - l); + longCheckAll(Long.MIN_VALUE + l); + } + + System.out.println("End"); + } +} +''' % (checkers, decls, calls['int'], calls['long']) + +with open('src/Main.java', 'w') as f: + f.write(code) diff --git a/test/701-easy-div-rem/info.txt b/test/701-easy-div-rem/info.txt new file mode 100644 index 0000000000..56d17860c1 --- /dev/null +++ b/test/701-easy-div-rem/info.txt @@ -0,0 +1 @@ +Simple tests for checking easy division/reminder for int and longs. diff --git a/test/701-easy-div-rem/src/Main.java b/test/701-easy-div-rem/src/Main.java new file mode 100644 index 0000000000..f995f61953 --- /dev/null +++ b/test/701-easy-div-rem/src/Main.java @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static int num_errors = 0; + + public static void reportError(String message) { + if (num_errors == 10) { + System.out.println("Omitting other error messages..."); + } else if (num_errors < 10) { + System.out.println(message); + } + num_errors += 1; + } + + public static void intCheckDiv(String desc, int result, int dividend, int divisor) { + int correct_result = dividend / divisor; + if (result != correct_result) { + reportError(desc + "(" + dividend + ") == " + result + + " should be " + correct_result); + } + } + public static void intCheckRem(String desc, int result, int dividend, int divisor) { + int correct_result = dividend % divisor; + if (result != correct_result) { + reportError(desc + "(" + dividend + ") == " + result + + " should be " + correct_result); + } + } + public static void longCheckDiv(String desc, long result, long dividend, long divisor) { + long correct_result = dividend / divisor; + if (result != correct_result) { + reportError(desc + "(" + dividend + ") == " + result + + " should be " + correct_result); + } + } + public static void longCheckRem(String desc, long result, long dividend, long divisor) { + long correct_result = dividend % divisor; + if (result != correct_result) { + reportError(desc + "(" + dividend + ") == " + result + + " should be " + correct_result); + } + } + + public static int idiv_by_pow2_0(int x) {return x / 1;} + public static int idiv_by_pow2_1(int x) {return x / 2;} + public static int idiv_by_pow2_2(int x) {return x / 4;} + public static int idiv_by_pow2_3(int x) {return x / 8;} + public static int idiv_by_pow2_4(int x) {return x / 16;} + public static int idiv_by_pow2_5(int x) {return x / 32;} + public static int idiv_by_pow2_6(int x) {return x / 64;} + public static int idiv_by_pow2_7(int x) {return x / 128;} + public static int idiv_by_pow2_8(int x) {return x / 256;} + public static int idiv_by_pow2_9(int x) {return x / 512;} + public static int idiv_by_pow2_10(int x) {return x / 1024;} + public static int idiv_by_pow2_11(int x) {return x / 2048;} + public static int idiv_by_pow2_12(int x) {return x / 4096;} + public static int idiv_by_pow2_13(int x) {return x / 8192;} + public static int idiv_by_pow2_14(int x) {return x / 16384;} + public static int idiv_by_pow2_15(int x) {return x / 32768;} + public static int idiv_by_pow2_16(int x) {return x / 65536;} + public static int idiv_by_pow2_17(int x) {return x / 131072;} + public static int idiv_by_pow2_18(int x) {return x / 262144;} + public static int idiv_by_pow2_19(int x) {return x / 524288;} + public static int idiv_by_pow2_20(int x) {return x / 1048576;} + public static int idiv_by_pow2_21(int x) {return x / 2097152;} + public static int idiv_by_pow2_22(int x) {return x / 4194304;} + public static int idiv_by_pow2_23(int x) {return x / 8388608;} + public static int idiv_by_pow2_24(int x) {return x / 16777216;} + public static int idiv_by_pow2_25(int x) {return x / 33554432;} + public static int idiv_by_pow2_26(int x) {return x / 67108864;} + public static int idiv_by_pow2_27(int x) {return x / 134217728;} + public static int idiv_by_pow2_28(int x) {return x / 268435456;} + public static int idiv_by_pow2_29(int x) {return x / 536870912;} + public static int idiv_by_pow2_30(int x) {return x / 1073741824;} + public static int idiv_by_small_0(int x) {return x / 3;} + public static int idiv_by_small_1(int x) {return x / 5;} + public static int idiv_by_small_2(int x) {return x / 6;} + public static int idiv_by_small_3(int x) {return x / 7;} + public static int idiv_by_small_4(int x) {return x / 9;} + public static int idiv_by_small_5(int x) {return x / 10;} + public static int idiv_by_small_6(int x) {return x / 11;} + public static int idiv_by_small_7(int x) {return x / 12;} + public static int idiv_by_small_8(int x) {return x / 13;} + public static int idiv_by_small_9(int x) {return x / 14;} + public static int idiv_by_small_10(int x) {return x / 15;} + public static int irem_by_pow2_0(int x) {return x % 1;} + public static int irem_by_pow2_1(int x) {return x % 2;} + public static int irem_by_pow2_2(int x) {return x % 4;} + public static int irem_by_pow2_3(int x) {return x % 8;} + public static int irem_by_pow2_4(int x) {return x % 16;} + public static int irem_by_pow2_5(int x) {return x % 32;} + public static int irem_by_pow2_6(int x) {return x % 64;} + public static int irem_by_pow2_7(int x) {return x % 128;} + public static int irem_by_pow2_8(int x) {return x % 256;} + public static int irem_by_pow2_9(int x) {return x % 512;} + public static int irem_by_pow2_10(int x) {return x % 1024;} + public static int irem_by_pow2_11(int x) {return x % 2048;} + public static int irem_by_pow2_12(int x) {return x % 4096;} + public static int irem_by_pow2_13(int x) {return x % 8192;} + public static int irem_by_pow2_14(int x) {return x % 16384;} + public static int irem_by_pow2_15(int x) {return x % 32768;} + public static int irem_by_pow2_16(int x) {return x % 65536;} + public static int irem_by_pow2_17(int x) {return x % 131072;} + public static int irem_by_pow2_18(int x) {return x % 262144;} + public static int irem_by_pow2_19(int x) {return x % 524288;} + public static int irem_by_pow2_20(int x) {return x % 1048576;} + public static int irem_by_pow2_21(int x) {return x % 2097152;} + public static int irem_by_pow2_22(int x) {return x % 4194304;} + public static int irem_by_pow2_23(int x) {return x % 8388608;} + public static int irem_by_pow2_24(int x) {return x % 16777216;} + public static int irem_by_pow2_25(int x) {return x % 33554432;} + public static int irem_by_pow2_26(int x) {return x % 67108864;} + public static int irem_by_pow2_27(int x) {return x % 134217728;} + public static int irem_by_pow2_28(int x) {return x % 268435456;} + public static int irem_by_pow2_29(int x) {return x % 536870912;} + public static int irem_by_pow2_30(int x) {return x % 1073741824;} + public static long ldiv_by_pow2_0(long x) {return x / 1l;} + public static long ldiv_by_pow2_1(long x) {return x / 2l;} + public static long ldiv_by_pow2_2(long x) {return x / 4l;} + public static long ldiv_by_pow2_3(long x) {return x / 8l;} + public static long ldiv_by_pow2_4(long x) {return x / 16l;} + public static long ldiv_by_pow2_5(long x) {return x / 32l;} + public static long ldiv_by_pow2_6(long x) {return x / 64l;} + public static long ldiv_by_pow2_7(long x) {return x / 128l;} + public static long ldiv_by_pow2_8(long x) {return x / 256l;} + public static long ldiv_by_pow2_9(long x) {return x / 512l;} + public static long ldiv_by_pow2_10(long x) {return x / 1024l;} + public static long ldiv_by_pow2_11(long x) {return x / 2048l;} + public static long ldiv_by_pow2_12(long x) {return x / 4096l;} + public static long ldiv_by_pow2_13(long x) {return x / 8192l;} + public static long ldiv_by_pow2_14(long x) {return x / 16384l;} + public static long ldiv_by_pow2_15(long x) {return x / 32768l;} + public static long ldiv_by_pow2_16(long x) {return x / 65536l;} + public static long ldiv_by_pow2_17(long x) {return x / 131072l;} + public static long ldiv_by_pow2_18(long x) {return x / 262144l;} + public static long ldiv_by_pow2_19(long x) {return x / 524288l;} + public static long ldiv_by_pow2_20(long x) {return x / 1048576l;} + public static long ldiv_by_pow2_21(long x) {return x / 2097152l;} + public static long ldiv_by_pow2_22(long x) {return x / 4194304l;} + public static long ldiv_by_pow2_23(long x) {return x / 8388608l;} + public static long ldiv_by_pow2_24(long x) {return x / 16777216l;} + public static long ldiv_by_pow2_25(long x) {return x / 33554432l;} + public static long ldiv_by_pow2_26(long x) {return x / 67108864l;} + public static long ldiv_by_pow2_27(long x) {return x / 134217728l;} + public static long ldiv_by_pow2_28(long x) {return x / 268435456l;} + public static long ldiv_by_pow2_29(long x) {return x / 536870912l;} + public static long ldiv_by_pow2_30(long x) {return x / 1073741824l;} + public static long ldiv_by_pow2_31(long x) {return x / 2147483648l;} + public static long ldiv_by_pow2_32(long x) {return x / 4294967296l;} + public static long ldiv_by_pow2_33(long x) {return x / 8589934592l;} + public static long ldiv_by_pow2_34(long x) {return x / 17179869184l;} + public static long ldiv_by_pow2_35(long x) {return x / 34359738368l;} + public static long ldiv_by_pow2_36(long x) {return x / 68719476736l;} + public static long ldiv_by_pow2_37(long x) {return x / 137438953472l;} + public static long ldiv_by_pow2_38(long x) {return x / 274877906944l;} + public static long ldiv_by_pow2_39(long x) {return x / 549755813888l;} + public static long ldiv_by_pow2_40(long x) {return x / 1099511627776l;} + public static long ldiv_by_pow2_41(long x) {return x / 2199023255552l;} + public static long ldiv_by_pow2_42(long x) {return x / 4398046511104l;} + public static long ldiv_by_pow2_43(long x) {return x / 8796093022208l;} + public static long ldiv_by_pow2_44(long x) {return x / 17592186044416l;} + public static long ldiv_by_pow2_45(long x) {return x / 35184372088832l;} + public static long ldiv_by_pow2_46(long x) {return x / 70368744177664l;} + public static long ldiv_by_pow2_47(long x) {return x / 140737488355328l;} + public static long ldiv_by_pow2_48(long x) {return x / 281474976710656l;} + public static long ldiv_by_pow2_49(long x) {return x / 562949953421312l;} + public static long ldiv_by_pow2_50(long x) {return x / 1125899906842624l;} + public static long ldiv_by_pow2_51(long x) {return x / 2251799813685248l;} + public static long ldiv_by_pow2_52(long x) {return x / 4503599627370496l;} + public static long ldiv_by_pow2_53(long x) {return x / 9007199254740992l;} + public static long ldiv_by_pow2_54(long x) {return x / 18014398509481984l;} + public static long ldiv_by_pow2_55(long x) {return x / 36028797018963968l;} + public static long ldiv_by_pow2_56(long x) {return x / 72057594037927936l;} + public static long ldiv_by_pow2_57(long x) {return x / 144115188075855872l;} + public static long ldiv_by_pow2_58(long x) {return x / 288230376151711744l;} + public static long ldiv_by_pow2_59(long x) {return x / 576460752303423488l;} + public static long ldiv_by_pow2_60(long x) {return x / 1152921504606846976l;} + public static long ldiv_by_pow2_61(long x) {return x / 2305843009213693952l;} + public static long ldiv_by_pow2_62(long x) {return x / 4611686018427387904l;} + public static long ldiv_by_small_0(long x) {return x / 3l;} + public static long ldiv_by_small_1(long x) {return x / 5l;} + public static long ldiv_by_small_2(long x) {return x / 6l;} + public static long ldiv_by_small_3(long x) {return x / 7l;} + public static long ldiv_by_small_4(long x) {return x / 9l;} + public static long ldiv_by_small_5(long x) {return x / 10l;} + public static long ldiv_by_small_6(long x) {return x / 11l;} + public static long ldiv_by_small_7(long x) {return x / 12l;} + public static long ldiv_by_small_8(long x) {return x / 13l;} + public static long ldiv_by_small_9(long x) {return x / 14l;} + public static long ldiv_by_small_10(long x) {return x / 15l;} + public static long lrem_by_pow2_0(long x) {return x % 1l;} + public static long lrem_by_pow2_1(long x) {return x % 2l;} + public static long lrem_by_pow2_2(long x) {return x % 4l;} + public static long lrem_by_pow2_3(long x) {return x % 8l;} + public static long lrem_by_pow2_4(long x) {return x % 16l;} + public static long lrem_by_pow2_5(long x) {return x % 32l;} + public static long lrem_by_pow2_6(long x) {return x % 64l;} + public static long lrem_by_pow2_7(long x) {return x % 128l;} + public static long lrem_by_pow2_8(long x) {return x % 256l;} + public static long lrem_by_pow2_9(long x) {return x % 512l;} + public static long lrem_by_pow2_10(long x) {return x % 1024l;} + public static long lrem_by_pow2_11(long x) {return x % 2048l;} + public static long lrem_by_pow2_12(long x) {return x % 4096l;} + public static long lrem_by_pow2_13(long x) {return x % 8192l;} + public static long lrem_by_pow2_14(long x) {return x % 16384l;} + public static long lrem_by_pow2_15(long x) {return x % 32768l;} + public static long lrem_by_pow2_16(long x) {return x % 65536l;} + public static long lrem_by_pow2_17(long x) {return x % 131072l;} + public static long lrem_by_pow2_18(long x) {return x % 262144l;} + public static long lrem_by_pow2_19(long x) {return x % 524288l;} + public static long lrem_by_pow2_20(long x) {return x % 1048576l;} + public static long lrem_by_pow2_21(long x) {return x % 2097152l;} + public static long lrem_by_pow2_22(long x) {return x % 4194304l;} + public static long lrem_by_pow2_23(long x) {return x % 8388608l;} + public static long lrem_by_pow2_24(long x) {return x % 16777216l;} + public static long lrem_by_pow2_25(long x) {return x % 33554432l;} + public static long lrem_by_pow2_26(long x) {return x % 67108864l;} + public static long lrem_by_pow2_27(long x) {return x % 134217728l;} + public static long lrem_by_pow2_28(long x) {return x % 268435456l;} + public static long lrem_by_pow2_29(long x) {return x % 536870912l;} + public static long lrem_by_pow2_30(long x) {return x % 1073741824l;} + public static long lrem_by_pow2_31(long x) {return x % 2147483648l;} + public static long lrem_by_pow2_32(long x) {return x % 4294967296l;} + public static long lrem_by_pow2_33(long x) {return x % 8589934592l;} + public static long lrem_by_pow2_34(long x) {return x % 17179869184l;} + public static long lrem_by_pow2_35(long x) {return x % 34359738368l;} + public static long lrem_by_pow2_36(long x) {return x % 68719476736l;} + public static long lrem_by_pow2_37(long x) {return x % 137438953472l;} + public static long lrem_by_pow2_38(long x) {return x % 274877906944l;} + public static long lrem_by_pow2_39(long x) {return x % 549755813888l;} + public static long lrem_by_pow2_40(long x) {return x % 1099511627776l;} + public static long lrem_by_pow2_41(long x) {return x % 2199023255552l;} + public static long lrem_by_pow2_42(long x) {return x % 4398046511104l;} + public static long lrem_by_pow2_43(long x) {return x % 8796093022208l;} + public static long lrem_by_pow2_44(long x) {return x % 17592186044416l;} + public static long lrem_by_pow2_45(long x) {return x % 35184372088832l;} + public static long lrem_by_pow2_46(long x) {return x % 70368744177664l;} + public static long lrem_by_pow2_47(long x) {return x % 140737488355328l;} + public static long lrem_by_pow2_48(long x) {return x % 281474976710656l;} + public static long lrem_by_pow2_49(long x) {return x % 562949953421312l;} + public static long lrem_by_pow2_50(long x) {return x % 1125899906842624l;} + public static long lrem_by_pow2_51(long x) {return x % 2251799813685248l;} + public static long lrem_by_pow2_52(long x) {return x % 4503599627370496l;} + public static long lrem_by_pow2_53(long x) {return x % 9007199254740992l;} + public static long lrem_by_pow2_54(long x) {return x % 18014398509481984l;} + public static long lrem_by_pow2_55(long x) {return x % 36028797018963968l;} + public static long lrem_by_pow2_56(long x) {return x % 72057594037927936l;} + public static long lrem_by_pow2_57(long x) {return x % 144115188075855872l;} + public static long lrem_by_pow2_58(long x) {return x % 288230376151711744l;} + public static long lrem_by_pow2_59(long x) {return x % 576460752303423488l;} + public static long lrem_by_pow2_60(long x) {return x % 1152921504606846976l;} + public static long lrem_by_pow2_61(long x) {return x % 2305843009213693952l;} + public static long lrem_by_pow2_62(long x) {return x % 4611686018427387904l;} + + public static void intCheckAll(int x) { + intCheckDiv("idiv_by_pow2_0", idiv_by_pow2_0(x), x, 1); + intCheckDiv("idiv_by_pow2_1", idiv_by_pow2_1(x), x, 2); + intCheckDiv("idiv_by_pow2_2", idiv_by_pow2_2(x), x, 4); + intCheckDiv("idiv_by_pow2_3", idiv_by_pow2_3(x), x, 8); + intCheckDiv("idiv_by_pow2_4", idiv_by_pow2_4(x), x, 16); + intCheckDiv("idiv_by_pow2_5", idiv_by_pow2_5(x), x, 32); + intCheckDiv("idiv_by_pow2_6", idiv_by_pow2_6(x), x, 64); + intCheckDiv("idiv_by_pow2_7", idiv_by_pow2_7(x), x, 128); + intCheckDiv("idiv_by_pow2_8", idiv_by_pow2_8(x), x, 256); + intCheckDiv("idiv_by_pow2_9", idiv_by_pow2_9(x), x, 512); + intCheckDiv("idiv_by_pow2_10", idiv_by_pow2_10(x), x, 1024); + intCheckDiv("idiv_by_pow2_11", idiv_by_pow2_11(x), x, 2048); + intCheckDiv("idiv_by_pow2_12", idiv_by_pow2_12(x), x, 4096); + intCheckDiv("idiv_by_pow2_13", idiv_by_pow2_13(x), x, 8192); + intCheckDiv("idiv_by_pow2_14", idiv_by_pow2_14(x), x, 16384); + intCheckDiv("idiv_by_pow2_15", idiv_by_pow2_15(x), x, 32768); + intCheckDiv("idiv_by_pow2_16", idiv_by_pow2_16(x), x, 65536); + intCheckDiv("idiv_by_pow2_17", idiv_by_pow2_17(x), x, 131072); + intCheckDiv("idiv_by_pow2_18", idiv_by_pow2_18(x), x, 262144); + intCheckDiv("idiv_by_pow2_19", idiv_by_pow2_19(x), x, 524288); + intCheckDiv("idiv_by_pow2_20", idiv_by_pow2_20(x), x, 1048576); + intCheckDiv("idiv_by_pow2_21", idiv_by_pow2_21(x), x, 2097152); + intCheckDiv("idiv_by_pow2_22", idiv_by_pow2_22(x), x, 4194304); + intCheckDiv("idiv_by_pow2_23", idiv_by_pow2_23(x), x, 8388608); + intCheckDiv("idiv_by_pow2_24", idiv_by_pow2_24(x), x, 16777216); + intCheckDiv("idiv_by_pow2_25", idiv_by_pow2_25(x), x, 33554432); + intCheckDiv("idiv_by_pow2_26", idiv_by_pow2_26(x), x, 67108864); + intCheckDiv("idiv_by_pow2_27", idiv_by_pow2_27(x), x, 134217728); + intCheckDiv("idiv_by_pow2_28", idiv_by_pow2_28(x), x, 268435456); + intCheckDiv("idiv_by_pow2_29", idiv_by_pow2_29(x), x, 536870912); + intCheckDiv("idiv_by_pow2_30", idiv_by_pow2_30(x), x, 1073741824); + intCheckDiv("idiv_by_small_0", idiv_by_small_0(x), x, 3); + intCheckDiv("idiv_by_small_1", idiv_by_small_1(x), x, 5); + intCheckDiv("idiv_by_small_2", idiv_by_small_2(x), x, 6); + intCheckDiv("idiv_by_small_3", idiv_by_small_3(x), x, 7); + intCheckDiv("idiv_by_small_4", idiv_by_small_4(x), x, 9); + intCheckDiv("idiv_by_small_5", idiv_by_small_5(x), x, 10); + intCheckDiv("idiv_by_small_6", idiv_by_small_6(x), x, 11); + intCheckDiv("idiv_by_small_7", idiv_by_small_7(x), x, 12); + intCheckDiv("idiv_by_small_8", idiv_by_small_8(x), x, 13); + intCheckDiv("idiv_by_small_9", idiv_by_small_9(x), x, 14); + intCheckDiv("idiv_by_small_10", idiv_by_small_10(x), x, 15); + intCheckRem("irem_by_pow2_0", irem_by_pow2_0(x), x, 1); + intCheckRem("irem_by_pow2_1", irem_by_pow2_1(x), x, 2); + intCheckRem("irem_by_pow2_2", irem_by_pow2_2(x), x, 4); + intCheckRem("irem_by_pow2_3", irem_by_pow2_3(x), x, 8); + intCheckRem("irem_by_pow2_4", irem_by_pow2_4(x), x, 16); + intCheckRem("irem_by_pow2_5", irem_by_pow2_5(x), x, 32); + intCheckRem("irem_by_pow2_6", irem_by_pow2_6(x), x, 64); + intCheckRem("irem_by_pow2_7", irem_by_pow2_7(x), x, 128); + intCheckRem("irem_by_pow2_8", irem_by_pow2_8(x), x, 256); + intCheckRem("irem_by_pow2_9", irem_by_pow2_9(x), x, 512); + intCheckRem("irem_by_pow2_10", irem_by_pow2_10(x), x, 1024); + intCheckRem("irem_by_pow2_11", irem_by_pow2_11(x), x, 2048); + intCheckRem("irem_by_pow2_12", irem_by_pow2_12(x), x, 4096); + intCheckRem("irem_by_pow2_13", irem_by_pow2_13(x), x, 8192); + intCheckRem("irem_by_pow2_14", irem_by_pow2_14(x), x, 16384); + intCheckRem("irem_by_pow2_15", irem_by_pow2_15(x), x, 32768); + intCheckRem("irem_by_pow2_16", irem_by_pow2_16(x), x, 65536); + intCheckRem("irem_by_pow2_17", irem_by_pow2_17(x), x, 131072); + intCheckRem("irem_by_pow2_18", irem_by_pow2_18(x), x, 262144); + intCheckRem("irem_by_pow2_19", irem_by_pow2_19(x), x, 524288); + intCheckRem("irem_by_pow2_20", irem_by_pow2_20(x), x, 1048576); + intCheckRem("irem_by_pow2_21", irem_by_pow2_21(x), x, 2097152); + intCheckRem("irem_by_pow2_22", irem_by_pow2_22(x), x, 4194304); + intCheckRem("irem_by_pow2_23", irem_by_pow2_23(x), x, 8388608); + intCheckRem("irem_by_pow2_24", irem_by_pow2_24(x), x, 16777216); + intCheckRem("irem_by_pow2_25", irem_by_pow2_25(x), x, 33554432); + intCheckRem("irem_by_pow2_26", irem_by_pow2_26(x), x, 67108864); + intCheckRem("irem_by_pow2_27", irem_by_pow2_27(x), x, 134217728); + intCheckRem("irem_by_pow2_28", irem_by_pow2_28(x), x, 268435456); + intCheckRem("irem_by_pow2_29", irem_by_pow2_29(x), x, 536870912); + intCheckRem("irem_by_pow2_30", irem_by_pow2_30(x), x, 1073741824); + } + + public static void longCheckAll(long x) { + longCheckDiv("ldiv_by_pow2_0", ldiv_by_pow2_0(x), x, 1l); + longCheckDiv("ldiv_by_pow2_1", ldiv_by_pow2_1(x), x, 2l); + longCheckDiv("ldiv_by_pow2_2", ldiv_by_pow2_2(x), x, 4l); + longCheckDiv("ldiv_by_pow2_3", ldiv_by_pow2_3(x), x, 8l); + longCheckDiv("ldiv_by_pow2_4", ldiv_by_pow2_4(x), x, 16l); + longCheckDiv("ldiv_by_pow2_5", ldiv_by_pow2_5(x), x, 32l); + longCheckDiv("ldiv_by_pow2_6", ldiv_by_pow2_6(x), x, 64l); + longCheckDiv("ldiv_by_pow2_7", ldiv_by_pow2_7(x), x, 128l); + longCheckDiv("ldiv_by_pow2_8", ldiv_by_pow2_8(x), x, 256l); + longCheckDiv("ldiv_by_pow2_9", ldiv_by_pow2_9(x), x, 512l); + longCheckDiv("ldiv_by_pow2_10", ldiv_by_pow2_10(x), x, 1024l); + longCheckDiv("ldiv_by_pow2_11", ldiv_by_pow2_11(x), x, 2048l); + longCheckDiv("ldiv_by_pow2_12", ldiv_by_pow2_12(x), x, 4096l); + longCheckDiv("ldiv_by_pow2_13", ldiv_by_pow2_13(x), x, 8192l); + longCheckDiv("ldiv_by_pow2_14", ldiv_by_pow2_14(x), x, 16384l); + longCheckDiv("ldiv_by_pow2_15", ldiv_by_pow2_15(x), x, 32768l); + longCheckDiv("ldiv_by_pow2_16", ldiv_by_pow2_16(x), x, 65536l); + longCheckDiv("ldiv_by_pow2_17", ldiv_by_pow2_17(x), x, 131072l); + longCheckDiv("ldiv_by_pow2_18", ldiv_by_pow2_18(x), x, 262144l); + longCheckDiv("ldiv_by_pow2_19", ldiv_by_pow2_19(x), x, 524288l); + longCheckDiv("ldiv_by_pow2_20", ldiv_by_pow2_20(x), x, 1048576l); + longCheckDiv("ldiv_by_pow2_21", ldiv_by_pow2_21(x), x, 2097152l); + longCheckDiv("ldiv_by_pow2_22", ldiv_by_pow2_22(x), x, 4194304l); + longCheckDiv("ldiv_by_pow2_23", ldiv_by_pow2_23(x), x, 8388608l); + longCheckDiv("ldiv_by_pow2_24", ldiv_by_pow2_24(x), x, 16777216l); + longCheckDiv("ldiv_by_pow2_25", ldiv_by_pow2_25(x), x, 33554432l); + longCheckDiv("ldiv_by_pow2_26", ldiv_by_pow2_26(x), x, 67108864l); + longCheckDiv("ldiv_by_pow2_27", ldiv_by_pow2_27(x), x, 134217728l); + longCheckDiv("ldiv_by_pow2_28", ldiv_by_pow2_28(x), x, 268435456l); + longCheckDiv("ldiv_by_pow2_29", ldiv_by_pow2_29(x), x, 536870912l); + longCheckDiv("ldiv_by_pow2_30", ldiv_by_pow2_30(x), x, 1073741824l); + longCheckDiv("ldiv_by_pow2_31", ldiv_by_pow2_31(x), x, 2147483648l); + longCheckDiv("ldiv_by_pow2_32", ldiv_by_pow2_32(x), x, 4294967296l); + longCheckDiv("ldiv_by_pow2_33", ldiv_by_pow2_33(x), x, 8589934592l); + longCheckDiv("ldiv_by_pow2_34", ldiv_by_pow2_34(x), x, 17179869184l); + longCheckDiv("ldiv_by_pow2_35", ldiv_by_pow2_35(x), x, 34359738368l); + longCheckDiv("ldiv_by_pow2_36", ldiv_by_pow2_36(x), x, 68719476736l); + longCheckDiv("ldiv_by_pow2_37", ldiv_by_pow2_37(x), x, 137438953472l); + longCheckDiv("ldiv_by_pow2_38", ldiv_by_pow2_38(x), x, 274877906944l); + longCheckDiv("ldiv_by_pow2_39", ldiv_by_pow2_39(x), x, 549755813888l); + longCheckDiv("ldiv_by_pow2_40", ldiv_by_pow2_40(x), x, 1099511627776l); + longCheckDiv("ldiv_by_pow2_41", ldiv_by_pow2_41(x), x, 2199023255552l); + longCheckDiv("ldiv_by_pow2_42", ldiv_by_pow2_42(x), x, 4398046511104l); + longCheckDiv("ldiv_by_pow2_43", ldiv_by_pow2_43(x), x, 8796093022208l); + longCheckDiv("ldiv_by_pow2_44", ldiv_by_pow2_44(x), x, 17592186044416l); + longCheckDiv("ldiv_by_pow2_45", ldiv_by_pow2_45(x), x, 35184372088832l); + longCheckDiv("ldiv_by_pow2_46", ldiv_by_pow2_46(x), x, 70368744177664l); + longCheckDiv("ldiv_by_pow2_47", ldiv_by_pow2_47(x), x, 140737488355328l); + longCheckDiv("ldiv_by_pow2_48", ldiv_by_pow2_48(x), x, 281474976710656l); + longCheckDiv("ldiv_by_pow2_49", ldiv_by_pow2_49(x), x, 562949953421312l); + longCheckDiv("ldiv_by_pow2_50", ldiv_by_pow2_50(x), x, 1125899906842624l); + longCheckDiv("ldiv_by_pow2_51", ldiv_by_pow2_51(x), x, 2251799813685248l); + longCheckDiv("ldiv_by_pow2_52", ldiv_by_pow2_52(x), x, 4503599627370496l); + longCheckDiv("ldiv_by_pow2_53", ldiv_by_pow2_53(x), x, 9007199254740992l); + longCheckDiv("ldiv_by_pow2_54", ldiv_by_pow2_54(x), x, 18014398509481984l); + longCheckDiv("ldiv_by_pow2_55", ldiv_by_pow2_55(x), x, 36028797018963968l); + longCheckDiv("ldiv_by_pow2_56", ldiv_by_pow2_56(x), x, 72057594037927936l); + longCheckDiv("ldiv_by_pow2_57", ldiv_by_pow2_57(x), x, 144115188075855872l); + longCheckDiv("ldiv_by_pow2_58", ldiv_by_pow2_58(x), x, 288230376151711744l); + longCheckDiv("ldiv_by_pow2_59", ldiv_by_pow2_59(x), x, 576460752303423488l); + longCheckDiv("ldiv_by_pow2_60", ldiv_by_pow2_60(x), x, 1152921504606846976l); + longCheckDiv("ldiv_by_pow2_61", ldiv_by_pow2_61(x), x, 2305843009213693952l); + longCheckDiv("ldiv_by_pow2_62", ldiv_by_pow2_62(x), x, 4611686018427387904l); + longCheckDiv("ldiv_by_small_0", ldiv_by_small_0(x), x, 3l); + longCheckDiv("ldiv_by_small_1", ldiv_by_small_1(x), x, 5l); + longCheckDiv("ldiv_by_small_2", ldiv_by_small_2(x), x, 6l); + longCheckDiv("ldiv_by_small_3", ldiv_by_small_3(x), x, 7l); + longCheckDiv("ldiv_by_small_4", ldiv_by_small_4(x), x, 9l); + longCheckDiv("ldiv_by_small_5", ldiv_by_small_5(x), x, 10l); + longCheckDiv("ldiv_by_small_6", ldiv_by_small_6(x), x, 11l); + longCheckDiv("ldiv_by_small_7", ldiv_by_small_7(x), x, 12l); + longCheckDiv("ldiv_by_small_8", ldiv_by_small_8(x), x, 13l); + longCheckDiv("ldiv_by_small_9", ldiv_by_small_9(x), x, 14l); + longCheckDiv("ldiv_by_small_10", ldiv_by_small_10(x), x, 15l); + longCheckRem("lrem_by_pow2_0", lrem_by_pow2_0(x), x, 1l); + longCheckRem("lrem_by_pow2_1", lrem_by_pow2_1(x), x, 2l); + longCheckRem("lrem_by_pow2_2", lrem_by_pow2_2(x), x, 4l); + longCheckRem("lrem_by_pow2_3", lrem_by_pow2_3(x), x, 8l); + longCheckRem("lrem_by_pow2_4", lrem_by_pow2_4(x), x, 16l); + longCheckRem("lrem_by_pow2_5", lrem_by_pow2_5(x), x, 32l); + longCheckRem("lrem_by_pow2_6", lrem_by_pow2_6(x), x, 64l); + longCheckRem("lrem_by_pow2_7", lrem_by_pow2_7(x), x, 128l); + longCheckRem("lrem_by_pow2_8", lrem_by_pow2_8(x), x, 256l); + longCheckRem("lrem_by_pow2_9", lrem_by_pow2_9(x), x, 512l); + longCheckRem("lrem_by_pow2_10", lrem_by_pow2_10(x), x, 1024l); + longCheckRem("lrem_by_pow2_11", lrem_by_pow2_11(x), x, 2048l); + longCheckRem("lrem_by_pow2_12", lrem_by_pow2_12(x), x, 4096l); + longCheckRem("lrem_by_pow2_13", lrem_by_pow2_13(x), x, 8192l); + longCheckRem("lrem_by_pow2_14", lrem_by_pow2_14(x), x, 16384l); + longCheckRem("lrem_by_pow2_15", lrem_by_pow2_15(x), x, 32768l); + longCheckRem("lrem_by_pow2_16", lrem_by_pow2_16(x), x, 65536l); + longCheckRem("lrem_by_pow2_17", lrem_by_pow2_17(x), x, 131072l); + longCheckRem("lrem_by_pow2_18", lrem_by_pow2_18(x), x, 262144l); + longCheckRem("lrem_by_pow2_19", lrem_by_pow2_19(x), x, 524288l); + longCheckRem("lrem_by_pow2_20", lrem_by_pow2_20(x), x, 1048576l); + longCheckRem("lrem_by_pow2_21", lrem_by_pow2_21(x), x, 2097152l); + longCheckRem("lrem_by_pow2_22", lrem_by_pow2_22(x), x, 4194304l); + longCheckRem("lrem_by_pow2_23", lrem_by_pow2_23(x), x, 8388608l); + longCheckRem("lrem_by_pow2_24", lrem_by_pow2_24(x), x, 16777216l); + longCheckRem("lrem_by_pow2_25", lrem_by_pow2_25(x), x, 33554432l); + longCheckRem("lrem_by_pow2_26", lrem_by_pow2_26(x), x, 67108864l); + longCheckRem("lrem_by_pow2_27", lrem_by_pow2_27(x), x, 134217728l); + longCheckRem("lrem_by_pow2_28", lrem_by_pow2_28(x), x, 268435456l); + longCheckRem("lrem_by_pow2_29", lrem_by_pow2_29(x), x, 536870912l); + longCheckRem("lrem_by_pow2_30", lrem_by_pow2_30(x), x, 1073741824l); + longCheckRem("lrem_by_pow2_31", lrem_by_pow2_31(x), x, 2147483648l); + longCheckRem("lrem_by_pow2_32", lrem_by_pow2_32(x), x, 4294967296l); + longCheckRem("lrem_by_pow2_33", lrem_by_pow2_33(x), x, 8589934592l); + longCheckRem("lrem_by_pow2_34", lrem_by_pow2_34(x), x, 17179869184l); + longCheckRem("lrem_by_pow2_35", lrem_by_pow2_35(x), x, 34359738368l); + longCheckRem("lrem_by_pow2_36", lrem_by_pow2_36(x), x, 68719476736l); + longCheckRem("lrem_by_pow2_37", lrem_by_pow2_37(x), x, 137438953472l); + longCheckRem("lrem_by_pow2_38", lrem_by_pow2_38(x), x, 274877906944l); + longCheckRem("lrem_by_pow2_39", lrem_by_pow2_39(x), x, 549755813888l); + longCheckRem("lrem_by_pow2_40", lrem_by_pow2_40(x), x, 1099511627776l); + longCheckRem("lrem_by_pow2_41", lrem_by_pow2_41(x), x, 2199023255552l); + longCheckRem("lrem_by_pow2_42", lrem_by_pow2_42(x), x, 4398046511104l); + longCheckRem("lrem_by_pow2_43", lrem_by_pow2_43(x), x, 8796093022208l); + longCheckRem("lrem_by_pow2_44", lrem_by_pow2_44(x), x, 17592186044416l); + longCheckRem("lrem_by_pow2_45", lrem_by_pow2_45(x), x, 35184372088832l); + longCheckRem("lrem_by_pow2_46", lrem_by_pow2_46(x), x, 70368744177664l); + longCheckRem("lrem_by_pow2_47", lrem_by_pow2_47(x), x, 140737488355328l); + longCheckRem("lrem_by_pow2_48", lrem_by_pow2_48(x), x, 281474976710656l); + longCheckRem("lrem_by_pow2_49", lrem_by_pow2_49(x), x, 562949953421312l); + longCheckRem("lrem_by_pow2_50", lrem_by_pow2_50(x), x, 1125899906842624l); + longCheckRem("lrem_by_pow2_51", lrem_by_pow2_51(x), x, 2251799813685248l); + longCheckRem("lrem_by_pow2_52", lrem_by_pow2_52(x), x, 4503599627370496l); + longCheckRem("lrem_by_pow2_53", lrem_by_pow2_53(x), x, 9007199254740992l); + longCheckRem("lrem_by_pow2_54", lrem_by_pow2_54(x), x, 18014398509481984l); + longCheckRem("lrem_by_pow2_55", lrem_by_pow2_55(x), x, 36028797018963968l); + longCheckRem("lrem_by_pow2_56", lrem_by_pow2_56(x), x, 72057594037927936l); + longCheckRem("lrem_by_pow2_57", lrem_by_pow2_57(x), x, 144115188075855872l); + longCheckRem("lrem_by_pow2_58", lrem_by_pow2_58(x), x, 288230376151711744l); + longCheckRem("lrem_by_pow2_59", lrem_by_pow2_59(x), x, 576460752303423488l); + longCheckRem("lrem_by_pow2_60", lrem_by_pow2_60(x), x, 1152921504606846976l); + longCheckRem("lrem_by_pow2_61", lrem_by_pow2_61(x), x, 2305843009213693952l); + longCheckRem("lrem_by_pow2_62", lrem_by_pow2_62(x), x, 4611686018427387904l); + } + + public static void main(String[] args) { + int i; + long l; + + System.out.println("Begin"); + + System.out.println("Int: checking some equally spaced dividends..."); + for (i = -1000; i < 1000; i += 300) { + intCheckAll(i); + intCheckAll(-i); + } + + System.out.println("Int: checking small dividends..."); + for (i = 1; i < 100; i += 1) { + intCheckAll(i); + intCheckAll(-i); + } + + System.out.println("Int: checking big dividends..."); + for (i = 0; i < 100; i += 1) { + intCheckAll(Integer.MAX_VALUE - i); + intCheckAll(Integer.MIN_VALUE + i); + } + + System.out.println("Long: checking some equally spaced dividends..."); + for (l = 0l; l < 1000000000000l; l += 300000000000l) { + longCheckAll(l); + longCheckAll(-l); + } + + System.out.println("Long: checking small dividends..."); + for (l = 1l; l < 100l; l += 1l) { + longCheckAll(l); + longCheckAll(-l); + } + + System.out.println("Long: checking big dividends..."); + for (l = 0l; l < 100l; l += 1l) { + longCheckAll(Long.MAX_VALUE - l); + longCheckAll(Long.MIN_VALUE + l); + } + + System.out.println("End"); + } +} diff --git a/test/Android.oat.mk b/test/Android.oat.mk index 16300bba54..2b142db890 100644 --- a/test/Android.oat.mk +++ b/test/Android.oat.mk @@ -203,6 +203,7 @@ $(3): $$(ART_TEST_HOST_OAT_$(1)_DEX) $(ART_TEST_HOST_OAT_DEPENDENCIES) ANDROID_ROOT=$(HOST_OUT) \ ANDROID_LOG_TAGS='*:d' \ LD_LIBRARY_PATH=$$($(2)ART_HOST_OUT_SHARED_LIBRARIES) \ + LD_PRELOAD=libsigchain$$(ART_HOST_SHLIB_EXTENSION) \ $(HOST_OUT_EXECUTABLES)/dalvikvm$$($(2)ART_PHONY_TEST_HOST_SUFFIX) $(DALVIKVM_FLAGS) $(5) \ -XXlib:libartd$(HOST_SHLIB_SUFFIX) -Ximage:$$(HOST_CORE_IMG_LOCATION) \ -classpath $(ART_HOST_TEST_DIR)/android-data-$$@/oat-test-dex-$(1).jar \ diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 25bcf0a790..78312d1245 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -21,26 +21,63 @@ include art/build/Android.common_test.mk TEST_ART_RUN_TESTS := $(wildcard $(LOCAL_PATH)/[0-9]*) TEST_ART_RUN_TESTS := $(subst $(LOCAL_PATH)/,, $(TEST_ART_RUN_TESTS)) +# List all the test names for host and target excluding the -trace suffix +# $(1): test name, e.g. 003-omnibus-opcodes +# $(2): undefined or -trace +define all-run-test-names + test-art-host-run-test$(2)-default-$(1)32 \ + test-art-host-run-test$(2)-optimizing-$(1)32 \ + test-art-host-run-test$(2)-interpreter-$(1)32 \ + test-art-host-run-test$(2)-default-$(1)64 \ + test-art-host-run-test$(2)-optimizing-$(1)64 \ + test-art-host-run-test$(2)-interpreter-$(1)64 \ + test-art-target-run-test$(2)-default-$(1)32 \ + test-art-target-run-test$(2)-optimizing-$(1)32 \ + test-art-target-run-test$(2)-interpreter-$(1)32 \ + test-art-target-run-test$(2)-default-$(1)64 \ + test-art-target-run-test$(2)-optimizing-$(1)64 \ + test-art-target-run-test$(2)-interpreter-$(1)64 +endef # all-run-test-names + # Tests that are timing sensitive and flaky on heavily loaded systems. TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \ - test-art-host-run-test-default-053-wait-some32 \ - test-art-host-run-test-default-053-wait-some64 \ - test-art-host-run-test-interpreter-053-wait-some32 \ - test-art-host-run-test-interpreter-053-wait-some64 \ - test-art-host-run-test-optimizing-053-wait-some32 \ - test-art-host-run-test-optimizing-053-wait-some64 \ - test-art-host-run-test-default-055-enum-performance32 \ - test-art-host-run-test-default-055-enum-performance64 \ - test-art-host-run-test-interpreter-055-enum-performance32 \ - test-art-host-run-test-interpreter-055-enum-performance64 \ - test-art-host-run-test-optimizing-055-enum-performance32 \ - test-art-host-run-test-optimizing-055-enum-performance64 + 053-wait-some \ + 055-enum-performance # disable timing sensitive tests on "dist" builds. ifdef dist_goal - ART_TEST_KNOWN_BROKEN += $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS) + ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),)) + ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace)) endif +# Tests that are broken in --trace mode. +TEST_ART_BROKEN_TRACE_RUN_TESTS := \ + 003-omnibus-opcodes \ + 004-annotations \ + 018-stack-overflow \ + 023-many-interfaces \ + 031-class-attributes \ + 037-inherit \ + 044-proxy \ + 046-reflect \ + 051-thread \ + 055-enum-performance \ + 064-field-access \ + 078-polymorphic-virtual \ + 080-oom-throw \ + 082-inline-execute \ + 083-compiler-regressions \ + 093-serialization \ + 097-duplicate-method \ + 100-reflect2 \ + 102-concurrent-gc \ + 103-string-append \ + 107-int-math2 \ + 112-double-math \ + 701-easy-div-rem + +ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace)) + # The path where build only targets will be output, e.g. # out/target/product/generic_x86_64/obj/PACKAGING/art-run-tests_intermediates/DATA art_run_tests_dir := $(call intermediates-dir-for,PACKAGING,art-run-tests)/DATA @@ -96,9 +133,11 @@ ART_TEST_HOST_RUN_TEST_ALL_RULES := ART_TEST_HOST_RUN_TEST_DEFAULT_RULES := ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES := ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES := +ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := @@ -124,8 +163,10 @@ endif # $(2): host or target # $(3): default, optimizing or interpreter # $(4): 32 or 64 +# $(5): run tests with tracing enabled or not: trace or undefined define define-test-art-run-test run_test_options := $(addprefix --runtime-option ,$(DALVIKVM_FLAGS)) + run_test_rule_name := test-art-$(2)-run-test-$(3)-$(1)$(4) uc_host_or_target := prereq_rule := ifeq ($(2),host) @@ -163,7 +204,14 @@ define define-test-art-run-test $$(error found $(4) expected 32 or 64) endif endif - run_test_rule_name := test-art-$(2)-run-test-$(3)-$(1)$(4) + ifeq ($(5),trace) + run_test_options += --trace + run_test_rule_name := test-art-$(2)-run-test-trace-$(3)-$(1)$(4) + else + ifneq (,$(5)) + $$(error found $(5) expected undefined or -trace) + endif + endif run_test_options := --output-path $(ART_HOST_TEST_DIR)/run-test-output/$$(run_test_rule_name) \ $$(run_test_options) $$(run_test_rule_name): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options) @@ -222,9 +270,13 @@ define define-test-art-run-test-group ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES := ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES := ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES := - $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX))) - $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX))) - $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX))) + $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),)) + $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),)) + $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),)) + ifeq ($(2),host) + # For now just test tracing on the host with default. + $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace)) + endif do_second := false ifeq ($(2),host) ifneq ($$(HOST_PREFER_32_BIT),true) @@ -236,9 +288,13 @@ define define-test-art-run-test-group endif endif ifeq (true,$$(do_second)) - $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX))) - $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX))) - $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX))) + $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),)) + $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),)) + $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),)) + ifeq ($(2),host) + # For now just test tracing on the host with default. + $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace)) + endif endif $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-default-$(1), \ @@ -319,6 +375,7 @@ endif define-test-art-run-test := define-test-art-run-test-group-rule := define-test-art-run-test-group := +all-run-test-names := ART_TEST_TARGET_RUN_TEST_ALL_RULES := ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES := ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES := @@ -335,9 +392,11 @@ ART_TEST_HOST_RUN_TEST_ALL_RULES := ART_TEST_HOST_RUN_TEST_DEFAULT_RULES := ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES := ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES := +ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES := +ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES := diff --git a/test/ReferenceMap/stack_walk_refmap_jni.cc b/test/ReferenceMap/stack_walk_refmap_jni.cc index 87187ed19d..e5a1786102 100644 --- a/test/ReferenceMap/stack_walk_refmap_jni.cc +++ b/test/ReferenceMap/stack_walk_refmap_jni.cc @@ -20,12 +20,10 @@ #include "class_linker.h" #include "dex_file-inl.h" #include "gc_map.h" -#include "mirror/art_method.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "scoped_thread_state_change.h" #include "thread.h" #include "jni.h" diff --git a/test/StackWalk/stack_walk_jni.cc b/test/StackWalk/stack_walk_jni.cc index c849c54bbe..e404f6ab7d 100644 --- a/test/StackWalk/stack_walk_jni.cc +++ b/test/StackWalk/stack_walk_jni.cc @@ -19,12 +19,10 @@ #include "class_linker.h" #include "gc_map.h" -#include "mirror/art_method.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" -#include "object_utils.h" #include "jni.h" #include "scoped_thread_state_change.h" diff --git a/tools/generate-operator-out.py b/tools/generate-operator-out.py index 6baa6e3048..f666ad154b 100755 --- a/tools/generate-operator-out.py +++ b/tools/generate-operator-out.py @@ -163,35 +163,35 @@ def main(): header_files.append(header_file) ProcessFile(header_file) - print '#include <iostream>' - print + print('#include <iostream>') + print('') for header_file in header_files: header_file = header_file.replace(local_path + '/', '') - print '#include "%s"' % header_file + print('#include "%s"' % header_file) - print + print('') for enum_name in _ENUMS: - print '// This was automatically generated by %s --- do not edit!' % sys.argv[0] + print('// This was automatically generated by %s --- do not edit!' % sys.argv[0]) namespaces = _NAMESPACES[enum_name].split('::') for namespace in namespaces: - print 'namespace %s {' % namespace + print('namespace %s {' % namespace) - print 'std::ostream& operator<<(std::ostream& os, const %s& rhs) {' % enum_name - print ' switch (rhs) {' + print('std::ostream& operator<<(std::ostream& os, const %s& rhs) {' % enum_name) + print(' switch (rhs) {') for (enum_value, enum_text) in _ENUMS[enum_name]: - print ' case %s: os << "%s"; break;' % (enum_value, enum_text) + print(' case %s: os << "%s"; break;' % (enum_value, enum_text)) if not _ENUM_CLASSES[enum_name]: - print ' default: os << "%s[" << static_cast<int>(rhs) << "]"; break;' % enum_name - print ' }' - print ' return os;' - print '}' + print(' default: os << "%s[" << static_cast<int>(rhs) << "]"; break;' % enum_name) + print(' }') + print(' return os;') + print('}') for namespace in reversed(namespaces): - print '} // namespace %s' % namespace - print + print('} // namespace %s' % namespace) + print('') sys.exit(0) |