diff options
Diffstat (limited to 'compiler')
83 files changed, 3335 insertions, 1184 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index 77dc367c7d..2f785ce5d7 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -21,7 +21,6 @@ include art/build/Android.common.mk LIBART_COMPILER_SRC_FILES := \ compiled_method.cc \ dex/local_value_numbering.cc \ - dex/arena_bit_vector.cc \ dex/quick/arm/assemble_arm.cc \ dex/quick/arm/call_arm.cc \ dex/quick/arm/fp_arm.cc \ @@ -51,9 +50,11 @@ LIBART_COMPILER_SRC_FILES := \ dex/quick/x86/utility_x86.cc \ dex/dex_to_dex_compiler.cc \ dex/mir_dataflow.cc \ + dex/mir_field_info.cc \ dex/mir_optimization.cc \ dex/pass_driver.cc \ dex/bb_optimizations.cc \ + dex/bit_vector_block_iterator.cc \ dex/frontend.cc \ dex/mir_graph.cc \ dex/mir_analysis.cc \ @@ -72,6 +73,7 @@ LIBART_COMPILER_SRC_FILES := \ optimizing/nodes.cc \ trampolines/trampoline_compiler.cc \ utils/arena_allocator.cc \ + utils/arena_bit_vector.cc \ utils/arm/assembler_arm.cc \ utils/arm/managed_register_arm.cc \ utils/assembler.cc \ @@ -105,7 +107,7 @@ endif LIBART_COMPILER_CFLAGS := ifeq ($(ART_USE_PORTABLE_COMPILER),true) -LIBART_COMPILER_SRC_FILES += +LIBART_COMPILER_SRC_FILES += \ dex/portable/mir_to_gbc.cc \ elf_writer_mclinker.cc \ jni/portable/jni_compiler.cc \ @@ -119,11 +121,12 @@ LIBART_COMPILER_SRC_FILES += llvm/runtime_support_builder.cc \ llvm/runtime_support_builder_arm.cc \ llvm/runtime_support_builder_x86.cc - LIBART_COMPILER_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 +LIBART_COMPILER_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 endif LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES := \ - dex/compiler_enums.h + dex/compiler_enums.h \ + dex/quick/dex_file_method_inliner.h # $(1): target or host # $(2): ndebug or debug @@ -210,12 +213,15 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT ifeq ($(TARGET_ARCH),arm64) $$(info TODOAArch64: $$(LOCAL_PATH)/Android.mk Add Arm64 specific MCLinker libraries) endif # TARGET_ARCH != arm64 + include $(LLVM_DEVICE_BUILD_MK) else # host LOCAL_STATIC_LIBRARIES += libmcldARMInfo libmcldARMTarget LOCAL_STATIC_LIBRARIES += libmcldX86Info libmcldX86Target LOCAL_STATIC_LIBRARIES += libmcldMipsInfo libmcldMipsTarget + include $(LLVM_HOST_BUILD_MK) endif LOCAL_STATIC_LIBRARIES += libmcldCore libmcldObject libmcldADT libmcldFragment libmcldTarget libmcldCodeGen libmcldLDVariant libmcldMC libmcldSupport libmcldLD + include $(LLVM_GEN_INTRINSICS_MK) endif LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime @@ -227,13 +233,9 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk ifeq ($$(art_target_or_host),target) LOCAL_SHARED_LIBRARIES += libcutils - include $(LLVM_GEN_INTRINSICS_MK) - include $(LLVM_DEVICE_BUILD_MK) include $(BUILD_SHARED_LIBRARY) else # host LOCAL_STATIC_LIBRARIES += libcutils - include $(LLVM_GEN_INTRINSICS_MK) - include $(LLVM_HOST_BUILD_MK) include $(BUILD_HOST_SHARED_LIBRARY) endif diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h new file mode 100644 index 0000000000..d034b7922e --- /dev/null +++ b/compiler/common_compiler_test.h @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_COMMON_COMPILER_TEST_H_ +#define ART_COMPILER_COMMON_COMPILER_TEST_H_ + +#include "compiler_backend.h" +#include "compiler_callbacks.h" +#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" + +namespace art { + +#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 void baddivideinst(int signo, siginfo *si, void *data) { + (void)signo; + (void)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; +} + +class CommonCompilerTest : public CommonRuntimeTest { + public: + static void MakeExecutable(const std::vector<uint8_t>& code) { + CHECK_NE(code.size(), 0U); + MakeExecutable(&code[0], code.size()); + } + + // Create an OatMethod based on pointers (for unit tests). + OatFile::OatMethod CreateOatMethod(const void* code, + const size_t frame_size_in_bytes, + const uint32_t core_spill_mask, + const uint32_t fp_spill_mask, + const uint8_t* mapping_table, + const uint8_t* vmap_table, + const uint8_t* gc_map) { + const byte* base; + uint32_t code_offset, mapping_table_offset, vmap_table_offset, gc_map_offset; + if (mapping_table == nullptr && vmap_table == nullptr && 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; + mapping_table_offset = 0; + vmap_table_offset = 0; + gc_map_offset = 0; + } else { + // TODO: 64bit support. + base = nullptr; // Base of data in oat file, ie 0. + code_offset = PointerToLowMemUInt32(code); + mapping_table_offset = PointerToLowMemUInt32(mapping_table); + vmap_table_offset = PointerToLowMemUInt32(vmap_table); + gc_map_offset = PointerToLowMemUInt32(gc_map); + } + return OatFile::OatMethod(base, + code_offset, + frame_size_in_bytes, + core_spill_mask, + fp_spill_mask, + mapping_table_offset, + vmap_table_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(); + if (code == nullptr) { + code = compiled_method->GetPortableCode(); + } + MakeExecutable(*code); + const void* method_code = CompiledMethod::CodePointer(&(*code)[0], + compiled_method->GetInstructionSet()); + LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code; + OatFile::OatMethod oat_method = CreateOatMethod(method_code, + compiled_method->GetFrameSizeInBytes(), + compiled_method->GetCoreSpillMask(), + compiled_method->GetFpSpillMask(), + &compiled_method->GetMappingTable()[0], + &compiled_method->GetVmapTable()[0], + nullptr); + oat_method.LinkMethod(method); + method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); + } else { + // No code? You must mean to go into the interpreter. + const void* method_code = kUsePortableCompiler ? GetPortableToInterpreterBridge() + : GetQuickToInterpreterBridge(); + OatFile::OatMethod oat_method = CreateOatMethod(method_code, + kStackAlignment, + 0, + 0, + nullptr, + nullptr, + nullptr); + oat_method.LinkMethod(method); + method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge); + } + // 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(); + } + } + + 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); + + // 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 + } + + 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(); + SirtRef<mirror::ClassLoader> loader(self, 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)); + } + } + + 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(STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES)); + +#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_EQ(instruction_set_features, runtime_features); +#elif defined(__mips__) + instruction_set = kMips; +#elif defined(__i386__) + instruction_set = kX86; +#elif defined(__x86_64__) + instruction_set = kX86_64; + // TODO: x86_64 compilation support. + compiler_options_->SetCompilerFilter(CompilerOptions::kInterpretOnly); +#endif + + for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { + Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i); + if (!runtime_->HasCalleeSaveMethod(type)) { + runtime_->SetCalleeSaveMethod( + runtime_->CreateCalleeSaveMethod(instruction_set, type), type); + } + } + + // TODO: make selectable + CompilerBackend::Kind compiler_backend + = (kUsePortableCompiler) ? CompilerBackend::kPortable : CompilerBackend::kQuick; + timer_.reset(new CumulativeLogger("Compilation times")); + compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), + verification_results_.get(), + method_inliner_map_.get(), + compiler_backend, 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 TearDown() { + timer_.reset(); + compiler_driver_.reset(); + callbacks_.reset(); + method_inliner_map_.reset(); + verification_results_.reset(); + compiler_options_.reset(); + + CommonRuntimeTest::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(); + SirtRef<mirror::ClassLoader> loader(self, 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 CompileMethod(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(method != nullptr); + TimingLogger timings("CommonTest::CompileMethod", false, false); + timings.StartSplit("CompileOne"); + compiler_driver_->CompileOne(method, &timings); + MakeExecutable(method); + timings.EndSplit(); + } + + void CompileDirectMethod(SirtRef<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); + } + + void CompileVirtualMethod(SirtRef<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 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 UnreserveImageSpace() { + image_reservation_.reset(); + } + + UniquePtr<CompilerOptions> compiler_options_; + UniquePtr<VerificationResults> verification_results_; + UniquePtr<DexFileToMethodInlinerMap> method_inliner_map_; + UniquePtr<CompilerCallbacksImpl> callbacks_; + UniquePtr<CompilerDriver> compiler_driver_; + UniquePtr<CumulativeLogger> timer_; + + private: + UniquePtr<MemMap> image_reservation_; +}; + +} // namespace art + +#endif // ART_COMPILER_COMMON_COMPILER_TEST_H_ diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc index f6d724ab56..d884bc0ef8 100644 --- a/compiler/compiled_method.cc +++ b/compiler/compiled_method.cc @@ -153,12 +153,14 @@ CompiledMethod::CompiledMethod(CompilerDriver& driver, const uint32_t fp_spill_mask, const std::vector<uint8_t>& mapping_table, const std::vector<uint8_t>& vmap_table, - const std::vector<uint8_t>& native_gc_map) + const std::vector<uint8_t>& native_gc_map, + const std::vector<uint8_t>* cfi_info) : CompiledCode(&driver, instruction_set, quick_code), frame_size_in_bytes_(frame_size_in_bytes), core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask), mapping_table_(driver.DeduplicateMappingTable(mapping_table)), vmap_table_(driver.DeduplicateVMapTable(vmap_table)), - gc_map_(driver.DeduplicateGCMap(native_gc_map)) { + gc_map_(driver.DeduplicateGCMap(native_gc_map)), + cfi_info_(driver.DeduplicateCFIInfo(cfi_info)) { } CompiledMethod::CompiledMethod(CompilerDriver& driver, @@ -169,10 +171,11 @@ CompiledMethod::CompiledMethod(CompilerDriver& driver, const uint32_t fp_spill_mask) : CompiledCode(&driver, instruction_set, code), frame_size_in_bytes_(frame_size_in_bytes), - core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask) { - mapping_table_ = driver.DeduplicateMappingTable(std::vector<uint8_t>()); - vmap_table_ = driver.DeduplicateVMapTable(std::vector<uint8_t>()); - gc_map_ = driver.DeduplicateGCMap(std::vector<uint8_t>()); + core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask), + mapping_table_(driver.DeduplicateMappingTable(std::vector<uint8_t>())), + vmap_table_(driver.DeduplicateVMapTable(std::vector<uint8_t>())), + gc_map_(driver.DeduplicateGCMap(std::vector<uint8_t>())), + cfi_info_(nullptr) { } // Constructs a CompiledMethod for the Portable compiler. diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 611230509a..90ae6eeae8 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -110,7 +110,8 @@ class CompiledMethod : public CompiledCode { const uint32_t fp_spill_mask, const std::vector<uint8_t>& mapping_table, const std::vector<uint8_t>& vmap_table, - const std::vector<uint8_t>& native_gc_map); + const std::vector<uint8_t>& native_gc_map, + const std::vector<uint8_t>* cfi_info); // Constructs a CompiledMethod for the QuickJniCompiler. CompiledMethod(CompilerDriver& driver, @@ -157,6 +158,10 @@ class CompiledMethod : public CompiledCode { return *gc_map_; } + const std::vector<uint8_t>* GetCFIInfo() const { + return cfi_info_; + } + private: // For quick code, the size of the activation used by the code. const size_t frame_size_in_bytes_; @@ -172,6 +177,8 @@ class CompiledMethod : public CompiledCode { // For quick code, a map keyed by native PC indices to bitmaps describing what dalvik registers // are live. For portable code, the key is a dalvik PC. std::vector<uint8_t>* gc_map_; + // For quick code, a FDE entry for the debug_frame section. + std::vector<uint8_t>* cfi_info_; }; } // namespace art diff --git a/compiler/compiler_backend.cc b/compiler/compiler_backend.cc index eaa39f83c1..0afa665eb7 100644 --- a/compiler/compiler_backend.cc +++ b/compiler/compiler_backend.cc @@ -83,6 +83,9 @@ static CompiledMethod* TryCompileWithSeaIR(art::CompilerDriver& compiler, } +// Hack for CFI CIE initialization +extern std::vector<uint8_t>* X86CFIInitialization(); + class QuickBackend : public CompilerBackend { public: QuickBackend() : CompilerBackend(100) {} @@ -135,10 +138,11 @@ class QuickBackend : public CompilerBackend { } bool WriteElf(art::File* file, - OatWriter& oat_writer, + OatWriter* oat_writer, const std::vector<const art::DexFile*>& dex_files, const std::string& android_root, bool is_host, const CompilerDriver& driver) const + OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return art::ElfWriterQuick::Create(file, oat_writer, dex_files, android_root, is_host, driver); } @@ -165,11 +169,27 @@ class QuickBackend : public CompilerBackend { bool set_max = cu->mir_graph->SetMaxAvailableNonSpecialCompilerTemps(max_temps); CHECK(set_max); } - return mir_to_lir;; + return mir_to_lir; } void InitCompilationUnit(CompilationUnit& cu) const {} + /* + * @brief Generate and return Dwarf CFI initialization, if supported by the + * backend. + * @param driver CompilerDriver for this compile. + * @returns nullptr if not supported by backend or a vector of bytes for CFI DWARF + * information. + * @note This is used for backtrace information in generated code. + */ + std::vector<uint8_t>* GetCallFrameInformationInitialization(const CompilerDriver& driver) const + OVERRIDE { + if (driver.GetInstructionSet() == kX86) { + return X86CFIInitialization(); + } + return nullptr; + } + private: DISALLOW_COPY_AND_ASSIGN(QuickBackend); }; @@ -249,11 +269,12 @@ class LLVMBackend : public CompilerBackend { } bool WriteElf(art::File* file, - OatWriter& oat_writer, + OatWriter* oat_writer, const std::vector<const art::DexFile*>& dex_files, const std::string& android_root, bool is_host, const CompilerDriver& driver) const - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + OVERRIDE + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return art::ElfWriterMclinker::Create( file, oat_writer, dex_files, android_root, is_host, driver); } @@ -271,15 +292,17 @@ class LLVMBackend : public CompilerBackend { (1 << kSuppressExceptionEdges); } - bool isPortable() const { return true; } + bool IsPortable() const OVERRIDE { + return true; + } - void SetBitcodeFileName(std::string const& filename) { - typedef void (*SetBitcodeFileNameFn)(CompilerDriver&, std::string const&); + void SetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) { + typedef void (*SetBitcodeFileNameFn)(const CompilerDriver&, const std::string&); SetBitcodeFileNameFn set_bitcode_file_name = reinterpret_cast<SetBitcodeFileNameFn>(compilerLLVMSetBitcodeFileName); - set_bitcode_file_name(*this, filename); + set_bitcode_file_name(driver, filename); } private: diff --git a/compiler/compiler_backend.h b/compiler/compiler_backend.h index 01a69afc89..b473806bba 100644 --- a/compiler/compiler_backend.h +++ b/compiler/compiler_backend.h @@ -23,7 +23,7 @@ namespace art { class Backend; -class CompilationUnit; +struct CompilationUnit; class CompilerDriver; class CompiledMethod; class MIRGraph; @@ -40,8 +40,9 @@ class CompilerBackend { kPortable }; - explicit CompilerBackend(int warning) - : maximum_compilation_time_before_warning_(warning) {} + explicit CompilerBackend(uint64_t warning) + : maximum_compilation_time_before_warning_(warning) { + } static CompilerBackend* Create(Kind kind); @@ -49,7 +50,7 @@ class CompilerBackend { virtual void UnInit(CompilerDriver& driver) const = 0; - virtual CompiledMethod* Compile(CompilerDriver& compiler, + virtual CompiledMethod* Compile(CompilerDriver& driver, const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, @@ -66,7 +67,7 @@ class CompilerBackend { virtual uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const = 0; virtual bool WriteElf(art::File* file, - OatWriter& oat_writer, + OatWriter* oat_writer, const std::vector<const art::DexFile*>& dex_files, const std::string& android_root, bool is_host, const CompilerDriver& driver) const @@ -79,8 +80,12 @@ class CompilerBackend { return maximum_compilation_time_before_warning_; } - virtual bool IsPortable() const { return false; } - void SetBitcodeFileName(std::string const& filename) { + virtual bool IsPortable() const { + return false; + } + + void SetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) { + UNUSED(driver); UNUSED(filename); } @@ -88,8 +93,21 @@ class CompilerBackend { virtual ~CompilerBackend() {} + /* + * @brief Generate and return Dwarf CFI initialization, if supported by the + * backend. + * @param driver CompilerDriver for this compile. + * @returns nullptr if not supported by backend or a vector of bytes for CFI DWARF + * information. + * @note This is used for backtrace information in generated code. + */ + virtual std::vector<uint8_t>* GetCallFrameInformationInitialization(const CompilerDriver& driver) + const { + return nullptr; + } + private: - uint64_t maximum_compilation_time_before_warning_; + const uint64_t maximum_compilation_time_before_warning_; DISALLOW_COPY_AND_ASSIGN(CompilerBackend); }; diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h index 1286a8e52e..bd7c40ba5b 100644 --- a/compiler/dex/bb_optimizations.h +++ b/compiler/dex/bb_optimizations.h @@ -23,6 +23,20 @@ namespace art { /** + * @class CacheFieldLoweringInfo + * @brief Cache the lowering info for fields used by IGET/IPUT/SGET/SPUT insns. + */ +class CacheFieldLoweringInfo : public Pass { + public: + CacheFieldLoweringInfo() : Pass("CacheFieldLoweringInfo", kNoNodes) { + } + + void Start(CompilationUnit* cUnit) const { + cUnit->mir_graph->DoCacheFieldLoweringInfo(); + } +}; + +/** * @class CodeLayout * @brief Perform the code layout pass. */ diff --git a/compiler/dex/bit_vector_block_iterator.cc b/compiler/dex/bit_vector_block_iterator.cc new file mode 100644 index 0000000000..32d7d71ede --- /dev/null +++ b/compiler/dex/bit_vector_block_iterator.cc @@ -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. + */ + +#include "bit_vector_block_iterator.h" +#include "mir_graph.h" + +namespace art { + +BasicBlock* BitVectorBlockIterator::Next() { + int idx = internal_iterator_.Next(); + + if (idx == -1) { + return nullptr; + } + + return mir_graph_->GetBasicBlock(idx); +} + +} // namespace art diff --git a/compiler/dex/bit_vector_block_iterator.h b/compiler/dex/bit_vector_block_iterator.h new file mode 100644 index 0000000000..0821e9e238 --- /dev/null +++ b/compiler/dex/bit_vector_block_iterator.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DEX_BIT_VECTOR_BLOCK_ITERATOR_H_ +#define ART_COMPILER_DEX_BIT_VECTOR_BLOCK_ITERATOR_H_ + +#include "base/bit_vector.h" +#include "compiler_enums.h" +#include "utils/arena_bit_vector.h" +#include "utils/arena_allocator.h" +#include "compiler_ir.h" + +namespace art { + +class MIRGraph; + +/** + * @class BasicBlockIterator + * @brief Helper class to get the BasicBlocks when iterating through the ArenaBitVector. + */ +class BitVectorBlockIterator { + public: + explicit BitVectorBlockIterator(BitVector* bv, MIRGraph* mir_graph) + : mir_graph_(mir_graph), + internal_iterator_(bv) {} + + explicit BitVectorBlockIterator(BitVector* bv, CompilationUnit* c_unit) + : mir_graph_(c_unit->mir_graph.get()), + internal_iterator_(bv) {} + + BasicBlock* Next(); + + void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(size, ArenaAllocator::kAllocGrowableArray); + }; + void operator delete(void* p) {} // Nop. + + private: + MIRGraph* const mir_graph_; + BitVector::Iterator internal_iterator_; +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_BIT_VECTOR_BLOCK_ITERATOR_H_ diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 2bc36a5e9f..0cd9ba3603 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -402,29 +402,6 @@ enum SelectInstructionKind { std::ostream& operator<<(std::ostream& os, const SelectInstructionKind& kind); -// Type of growable bitmap for memory tuning. -enum OatBitMapKind { - kBitMapMisc = 0, - kBitMapUse, - kBitMapDef, - kBitMapLiveIn, - kBitMapBMatrix, - kBitMapDominators, - kBitMapIDominated, - kBitMapDomFrontier, - kBitMapPhi, - kBitMapTmpBlocks, - kBitMapInputBlocks, - kBitMapRegisterV, - kBitMapTempSSARegisterV, - kBitMapNullCheck, - kBitMapTmpBlockV, - kBitMapPredecessors, - kNumBitMapKinds -}; - -std::ostream& operator<<(std::ostream& os, const OatBitMapKind& kind); - // LIR fixup kinds for Arm enum FixupKind { kFixupNone, diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h index 8447d23ddc..b9a26d6dbb 100644 --- a/compiler/dex/compiler_ir.h +++ b/compiler/dex/compiler_ir.h @@ -64,7 +64,7 @@ struct CompilationUnit { const CompilerBackend* compiler_backend; InstructionSet instruction_set; - const InstructionSetFeatures& GetInstructionSetFeatures() { + InstructionSetFeatures GetInstructionSetFeatures() { return compiler_driver->GetInstructionSetFeatures(); } // TODO: much of this info available elsewhere. Go to the original source? diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index ff8fea0f88..b9f9437c95 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -208,21 +208,21 @@ void DexCompiler::CompileInstanceFieldAccess(Instruction* inst, return; } uint32_t field_idx = inst->VRegC_22c(); - int field_offset; + MemberOffset field_offset(0u); bool is_volatile; bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, is_put, &field_offset, &is_volatile); - if (fast_path && !is_volatile && IsUint(16, field_offset)) { + if (fast_path && !is_volatile && IsUint(16, field_offset.Int32Value())) { VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode()) << " to " << Instruction::Name(new_opcode) << " by replacing field index " << field_idx - << " by field offset " << field_offset + << " by field offset " << field_offset.Int32Value() << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method " << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true); // We are modifying 4 consecutive bytes. inst->SetOpcode(new_opcode); // Replace field index by field offset. - inst->SetVRegC_22c(static_cast<uint16_t>(field_offset)); + inst->SetVRegC_22c(static_cast<uint16_t>(field_offset.Int32Value())); } } diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 6800f7b2a4..26192589e1 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -128,7 +128,7 @@ void CompilationUnit::EndTiming() { } } -static CompiledMethod* CompileMethod(CompilerDriver& compiler, +static CompiledMethod* CompileMethod(CompilerDriver& driver, CompilerBackend* compiler_backend, const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, @@ -143,11 +143,11 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - CompilationUnit cu(&compiler.GetArenaPool()); + CompilationUnit cu(driver.GetArenaPool()); - cu.compiler_driver = &compiler; + cu.compiler_driver = &driver; cu.class_linker = class_linker; - cu.instruction_set = compiler.GetInstructionSet(); + cu.instruction_set = driver.GetInstructionSet(); cu.compiler_backend = compiler_backend; DCHECK((cu.instruction_set == kThumb2) || (cu.instruction_set == kX86) || @@ -216,8 +216,8 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, } /* Create the pass driver and launch it */ - PassDriver driver(&cu); - driver.Launch(); + PassDriver pass_driver(&cu); + pass_driver.Launch(); if (cu.enable_debug & (1 << kDebugDumpCheckStats)) { cu.mir_graph->DumpCheckStats(); @@ -257,9 +257,9 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, } cu.EndTiming(); - compiler.GetTimingsLogger().Start(); - compiler.GetTimingsLogger().AddLogger(cu.timings); - compiler.GetTimingsLogger().End(); + driver.GetTimingsLogger()->Start(); + driver.GetTimingsLogger()->AddLogger(cu.timings); + driver.GetTimingsLogger()->End(); return result; } diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h index 8ce12067ee..22a7b8cfb0 100644 --- a/compiler/dex/frontend.h +++ b/compiler/dex/frontend.h @@ -105,7 +105,7 @@ class LLVMInfo { UniquePtr<art::llvm::IRBuilder> ir_builder_; }; -struct CompiledMethod; +class CompiledMethod; class CompilerDriver; } // namespace art diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc index a3ea034902..61c676784f 100644 --- a/compiler/dex/local_value_numbering.cc +++ b/compiler/dex/local_value_numbering.cc @@ -16,6 +16,7 @@ #include "local_value_numbering.h" +#include "mir_field_info.h" #include "mir_graph.h" namespace art { @@ -534,16 +535,24 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::IGET_BYTE: case Instruction::IGET_CHAR: case Instruction::IGET_SHORT: { + uint16_t type = opcode - Instruction::IGET; uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]); HandleNullCheck(mir, base); + const MirFieldInfo& field_info = cu_->mir_graph->GetIFieldLoweringInfo(mir); uint16_t memory_version; uint16_t field_id; - // TODO: all gets treated as volatile. - // Volatile fields always get a new memory version; field id is irrelevant. - // Unresolved fields are always marked as volatile and handled the same way here. - field_id = 0u; - memory_version = next_memory_version_; - ++next_memory_version_; + if (!field_info.IsResolved() || field_info.IsVolatile()) { + // Volatile fields always get a new memory version; field id is irrelevant. + // Unresolved fields may be volatile, so handle them as such to be safe. + field_id = 0u; + memory_version = next_memory_version_; + ++next_memory_version_; + } else { + DCHECK(field_info.IsResolved()); + field_id = GetFieldId(field_info.DeclaringDexFile(), field_info.DeclaringFieldIndex()); + memory_version = std::max(unresolved_ifield_version_[type], + GetMemoryVersion(base, field_id, type)); + } if (opcode == Instruction::IGET_WIDE) { res = LookupValue(Instruction::IGET_WIDE, base, field_id, memory_version); SetOperandValueWide(mir->ssa_rep->defs[0], res); @@ -567,10 +576,18 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { int base_reg = (opcode == Instruction::IPUT_WIDE) ? 2 : 1; uint16_t base = GetOperandValue(mir->ssa_rep->uses[base_reg]); HandleNullCheck(mir, base); - // TODO: all puts treated as unresolved. - // Unresolved fields always alias with everything of the same type. - unresolved_ifield_version_[type] = next_memory_version_; - ++next_memory_version_; + const MirFieldInfo& field_info = cu_->mir_graph->GetIFieldLoweringInfo(mir); + if (!field_info.IsResolved()) { + // Unresolved fields always alias with everything of the same type. + unresolved_ifield_version_[type] = next_memory_version_; + ++next_memory_version_; + } else if (field_info.IsVolatile()) { + // Nothing to do, resolved volatile fields always get a new memory version anyway and + // can't alias with resolved non-volatile fields. + } else { + AdvanceMemoryVersion(base, GetFieldId(field_info.DeclaringDexFile(), + field_info.DeclaringFieldIndex()), type); + } } break; @@ -581,14 +598,22 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::SGET_BYTE: case Instruction::SGET_CHAR: case Instruction::SGET_SHORT: { + uint16_t type = opcode - Instruction::SGET; + const MirFieldInfo& field_info = cu_->mir_graph->GetSFieldLoweringInfo(mir); uint16_t memory_version; uint16_t field_id; - // TODO: all gets treated as volatile. - // Volatile fields always get a new memory version; field id is irrelevant. - // Unresolved fields are always marked as volatile and handled the same way here. - field_id = 0u; - memory_version = next_memory_version_; - ++next_memory_version_; + if (!field_info.IsResolved() || field_info.IsVolatile()) { + // Volatile fields always get a new memory version; field id is irrelevant. + // Unresolved fields may be volatile, so handle them as such to be safe. + field_id = 0u; + memory_version = next_memory_version_; + ++next_memory_version_; + } else { + DCHECK(field_info.IsResolved()); + field_id = GetFieldId(field_info.DeclaringDexFile(), field_info.DeclaringFieldIndex()); + memory_version = std::max(unresolved_sfield_version_[type], + GetMemoryVersion(NO_VALUE, field_id, type)); + } if (opcode == Instruction::SGET_WIDE) { res = LookupValue(Instruction::SGET_WIDE, NO_VALUE, field_id, memory_version); SetOperandValueWide(mir->ssa_rep->defs[0], res); @@ -609,10 +634,18 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::SPUT_CHAR: case Instruction::SPUT_SHORT: { uint16_t type = opcode - Instruction::SPUT; - // TODO: all puts treated as unresolved. - // Unresolved fields always alias with everything of the same type. - unresolved_sfield_version_[type] = next_memory_version_; - ++next_memory_version_; + const MirFieldInfo& field_info = cu_->mir_graph->GetSFieldLoweringInfo(mir); + if (!field_info.IsResolved()) { + // Unresolved fields always alias with everything of the same type. + unresolved_sfield_version_[type] = next_memory_version_; + ++next_memory_version_; + } else if (field_info.IsVolatile()) { + // Nothing to do, resolved volatile fields always get a new memory version anyway and + // can't alias with resolved non-volatile fields. + } else { + AdvanceMemoryVersion(NO_VALUE, GetFieldId(field_info.DeclaringDexFile(), + field_info.DeclaringFieldIndex()), type); + } } break; } diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc index 6ab6c51a1f..4599612db6 100644 --- a/compiler/dex/local_value_numbering_test.cc +++ b/compiler/dex/local_value_numbering_test.cc @@ -44,7 +44,7 @@ class LocalValueNumberingTest : public testing::Test { Instruction::Code opcode; int64_t value; - uint32_t field_annotation; + uint32_t field_info; size_t num_uses; int32_t uses[kMaxSsaUses]; size_t num_defs; @@ -55,28 +55,41 @@ class LocalValueNumberingTest : public testing::Test { { opcode, value, 0u, 0, { }, 1, { reg } } #define DEF_CONST_WIDE(opcode, reg, value) \ { opcode, value, 0u, 0, { }, 2, { reg, reg + 1 } } -#define DEF_IGET(opcode, reg, obj, field_annotation) \ - { opcode, 0u, field_annotation, 1, { obj }, 1, { reg } } -#define DEF_IGET_WIDE(opcode, reg, obj, field_annotation) \ - { opcode, 0u, field_annotation, 1, { obj }, 2, { reg, reg + 1 } } -#define DEF_IPUT(opcode, reg, obj, field_annotation) \ - { opcode, 0u, field_annotation, 2, { reg, obj }, 0, { } } -#define DEF_IPUT_WIDE(opcode, reg, obj, field_annotation) \ - { opcode, 0u, field_annotation, 3, { reg, reg + 1, obj }, 0, { } } -#define DEF_SGET(opcode, reg, field_annotation) \ - { opcode, 0u, field_annotation, 0, { }, 1, { reg } } -#define DEF_SGET_WIDE(opcode, reg, field_annotation) \ - { opcode, 0u, field_annotation, 0, { }, 2, { reg, reg + 1 } } -#define DEF_SPUT(opcode, reg, field_annotation) \ - { opcode, 0u, field_annotation, 1, { reg }, 0, { } } -#define DEF_SPUT_WIDE(opcode, reg, field_annotation) \ - { opcode, 0u, field_annotation, 2, { reg, reg + 1 }, 0, { } } +#define DEF_IGET(opcode, reg, obj, field_info) \ + { opcode, 0u, field_info, 1, { obj }, 1, { reg } } +#define DEF_IGET_WIDE(opcode, reg, obj, field_info) \ + { opcode, 0u, field_info, 1, { obj }, 2, { reg, reg + 1 } } +#define DEF_IPUT(opcode, reg, obj, field_info) \ + { opcode, 0u, field_info, 2, { reg, obj }, 0, { } } +#define DEF_IPUT_WIDE(opcode, reg, obj, field_info) \ + { opcode, 0u, field_info, 3, { reg, reg + 1, obj }, 0, { } } +#define DEF_SGET(opcode, reg, field_info) \ + { opcode, 0u, field_info, 0, { }, 1, { reg } } +#define DEF_SGET_WIDE(opcode, reg, field_info) \ + { opcode, 0u, field_info, 0, { }, 2, { reg, reg + 1 } } +#define DEF_SPUT(opcode, reg, field_info) \ + { opcode, 0u, field_info, 1, { reg }, 0, { } } +#define DEF_SPUT_WIDE(opcode, reg, field_info) \ + { opcode, 0u, field_info, 2, { reg, reg + 1 }, 0, { } } #define DEF_INVOKE1(opcode, reg) \ { opcode, 0u, 0u, 1, { reg }, 0, { } } #define DEF_UNIQUE_REF(opcode, reg) \ { opcode, 0u, 0u, 0, { }, 1, { reg } } // CONST_CLASS, CONST_STRING, NEW_ARRAY, ... void DoPrepareIFields(const IFieldDef* defs, size_t count) { + cu_.mir_graph->ifield_lowering_infos_.Reset(); + cu_.mir_graph->ifield_lowering_infos_.Resize(count); + for (size_t i = 0u; i != count; ++i) { + const IFieldDef* def = &defs[i]; + MirIFieldLoweringInfo field_info(def->field_idx); + if (def->declaring_dex_file != 0u) { + field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); + field_info.declaring_field_idx_ = def->declaring_field_idx; + field_info.flags_ = 0u | // Without kFlagIsStatic. + (def->is_volatile ? MirIFieldLoweringInfo::kFlagIsVolatile : 0u); + } + cu_.mir_graph->ifield_lowering_infos_.Insert(field_info); + } } template <size_t count> @@ -85,6 +98,19 @@ class LocalValueNumberingTest : public testing::Test { } void DoPrepareSFields(const SFieldDef* defs, size_t count) { + cu_.mir_graph->sfield_lowering_infos_.Reset(); + cu_.mir_graph->sfield_lowering_infos_.Resize(count); + for (size_t i = 0u; i != count; ++i) { + const SFieldDef* def = &defs[i]; + MirSFieldLoweringInfo field_info(def->field_idx); + if (def->declaring_dex_file != 0u) { + field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); + field_info.declaring_field_idx_ = def->declaring_field_idx; + field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic | + (def->is_volatile ? MirSFieldLoweringInfo::kFlagIsVolatile : 0u); + } + cu_.mir_graph->sfield_lowering_infos_.Insert(field_info); + } } template <size_t count> @@ -102,6 +128,13 @@ class LocalValueNumberingTest : public testing::Test { mir->dalvikInsn.opcode = def->opcode; mir->dalvikInsn.vB = static_cast<int32_t>(def->value); mir->dalvikInsn.vB_wide = def->value; + if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) { + ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.Size()); + mir->meta.ifield_lowering_info = def->field_info; + } else if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) { + ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.Size()); + mir->meta.sfield_lowering_info = def->field_info; + } mir->ssa_rep = &ssa_reps_[i]; mir->ssa_rep->num_uses = def->num_uses; mir->ssa_rep->uses = const_cast<int32_t*>(def->uses); // Not modified by LVN. @@ -146,7 +179,6 @@ class LocalValueNumberingTest : public testing::Test { LocalValueNumbering lvn_; }; -#if 0 // TODO: re-enable when LVN is handling memory igets. TEST_F(LocalValueNumberingTest, TestIGetIGetInvokeIGet) { static const IFieldDef ifields[] = { { 1u, 1u, 1u, false } @@ -169,7 +201,6 @@ TEST_F(LocalValueNumberingTest, TestIGetIGetInvokeIGet) { EXPECT_EQ(mirs_[2].optimization_flags, 0u); EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK); } -#endif TEST_F(LocalValueNumberingTest, TestIGetIPutIGetIGetIGet) { static const IFieldDef ifields[] = { @@ -197,7 +228,6 @@ TEST_F(LocalValueNumberingTest, TestIGetIPutIGetIGetIGet) { EXPECT_EQ(mirs_[4].optimization_flags, 0u); } -#if 0 // TODO: re-enable when LVN is handling memory igets. TEST_F(LocalValueNumberingTest, TestUniquePreserve1) { static const IFieldDef ifields[] = { { 1u, 1u, 1u, false }, @@ -218,9 +248,7 @@ TEST_F(LocalValueNumberingTest, TestUniquePreserve1) { EXPECT_EQ(mirs_[2].optimization_flags, 0u); EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK); } -#endif -#if 0 // TODO: re-enable when LVN is handling memory igets. TEST_F(LocalValueNumberingTest, TestUniquePreserve2) { static const IFieldDef ifields[] = { { 1u, 1u, 1u, false }, @@ -241,9 +269,7 @@ TEST_F(LocalValueNumberingTest, TestUniquePreserve2) { EXPECT_EQ(mirs_[2].optimization_flags, MIR_IGNORE_NULL_CHECK); EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK); } -#endif -#if 0 // TODO: re-enable when LVN is handling memory igets. TEST_F(LocalValueNumberingTest, TestUniquePreserveAndEscape) { static const IFieldDef ifields[] = { { 1u, 1u, 1u, false }, @@ -267,7 +293,6 @@ TEST_F(LocalValueNumberingTest, TestUniquePreserveAndEscape) { EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK); EXPECT_EQ(mirs_[5].optimization_flags, MIR_IGNORE_NULL_CHECK); } -#endif TEST_F(LocalValueNumberingTest, TestVolatile) { static const IFieldDef ifields[] = { diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc index 7ce8f696be..d159f49b3e 100644 --- a/compiler/dex/mir_analysis.cc +++ b/compiler/dex/mir_analysis.cc @@ -14,11 +14,15 @@ * limitations under the License. */ +#include <algorithm> #include "compiler_internals.h" #include "dataflow_iterator-inl.h" +#include "dex_instruction.h" +#include "dex_instruction-inl.h" #include "dex/quick/dex_file_method_inliner.h" #include "dex/quick/dex_file_to_method_inliner_map.h" #include "driver/compiler_options.h" +#include "UniquePtr.h" namespace art { @@ -1004,6 +1008,11 @@ bool MIRGraph::SkipCompilation() { return false; } + // Contains a pattern we don't want to compile? + if (punt_to_interpreter_) { + return true; + } + if (compiler_filter == CompilerOptions::kInterpretOnly) { LOG(WARNING) << "InterpretOnly should ideally be filtered out prior to parsing."; return true; @@ -1085,4 +1094,109 @@ bool MIRGraph::SkipCompilation() { return ComputeSkipCompilation(&stats, skip_compilation); } +void MIRGraph::DoCacheFieldLoweringInfo() { + // Try to use stack-allocated array, resort to heap if we exceed the initial size. + static constexpr size_t kInitialSize = 32; + uint16_t stack_idxs[kInitialSize]; + UniquePtr<uint16_t[]> allocated_idxs; + uint16_t* field_idxs = stack_idxs; + size_t size = kInitialSize; + + // Find IGET/IPUT/SGET/SPUT insns, store IGET/IPUT fields at the beginning, SGET/SPUT at the end. + size_t ifield_pos = 0u; + size_t sfield_pos = size; + AllNodesIterator iter(this); + for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { + if (bb->block_type != kDalvikByteCode) { + continue; + } + for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { + if (mir->dalvikInsn.opcode >= Instruction::IGET && + mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) { + bool need_alloc = false; + const Instruction* insn = Instruction::At(current_code_item_->insns_ + mir->offset); + uint16_t field_idx; + // Get field index and try to find it among existing indexes. If found, it's usually among + // the last few added, so we'll start the search from ifield_pos/sfield_pos. Though this + // is a linear search, it actually performs much better than map based approach. + if (mir->dalvikInsn.opcode <= Instruction::IPUT_SHORT) { + field_idx = insn->VRegC_22c(); + size_t i = ifield_pos; + while (i != 0u && field_idxs[i - 1] != field_idx) { + --i; + } + if (i != 0u) { + mir->meta.ifield_lowering_info = i - 1; + } else { + mir->meta.ifield_lowering_info = ifield_pos; + if (UNLIKELY(ifield_pos == sfield_pos)) { + need_alloc = true; + } else { + field_idxs[ifield_pos++] = field_idx; + } + } + } else { + field_idx = insn->VRegB_21c(); + size_t i = sfield_pos; + while (i != size && field_idxs[i] != field_idx) { + ++i; + } + if (i != size) { + mir->meta.sfield_lowering_info = size - i - 1u; + } else { + mir->meta.sfield_lowering_info = size - sfield_pos; + if (UNLIKELY(ifield_pos == sfield_pos)) { + need_alloc = true; + } else { + field_idxs[--sfield_pos] = field_idx; + } + } + } + if (UNLIKELY(need_alloc)) { + DCHECK(field_idxs == stack_idxs); + // All IGET/IPUT/SGET/SPUT instructions take 2 code units and there must also be a RETURN. + uint32_t max_refs = (current_code_item_->insns_size_in_code_units_ - 1u) / 2u; + allocated_idxs.reset(new uint16_t[max_refs]); + field_idxs = allocated_idxs.get(); + size_t sfield_count = size - sfield_pos; + sfield_pos = max_refs - sfield_count; + size = max_refs; + memcpy(field_idxs, stack_idxs, ifield_pos * sizeof(field_idxs[0])); + memcpy(field_idxs + sfield_pos, stack_idxs + ifield_pos, + sfield_count * sizeof(field_idxs[0])); + if (mir->dalvikInsn.opcode <= Instruction::IPUT_SHORT) { + field_idxs[ifield_pos++] = field_idx; + } else { + field_idxs[--sfield_pos] = field_idx; + } + } + DCHECK_LE(ifield_pos, sfield_pos); + } + } + } + + if (ifield_pos != 0u) { + // Resolve instance field infos. + DCHECK_EQ(ifield_lowering_infos_.Size(), 0u); + ifield_lowering_infos_.Resize(ifield_pos); + for (size_t pos = 0u; pos != ifield_pos; ++pos) { + ifield_lowering_infos_.Insert(MirIFieldLoweringInfo(field_idxs[pos])); + } + MirIFieldLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(), + ifield_lowering_infos_.GetRawStorage(), ifield_pos); + } + + if (sfield_pos != size) { + // Resolve static field infos. + DCHECK_EQ(sfield_lowering_infos_.Size(), 0u); + sfield_lowering_infos_.Resize(size - sfield_pos); + for (size_t pos = size; pos != sfield_pos;) { + --pos; + sfield_lowering_infos_.Insert(MirSFieldLoweringInfo(field_idxs[pos])); + } + MirSFieldLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(), + sfield_lowering_infos_.GetRawStorage(), size - sfield_pos); + } +} + } // namespace art diff --git a/compiler/dex/mir_field_info.cc b/compiler/dex/mir_field_info.cc new file mode 100644 index 0000000000..96eda01d1e --- /dev/null +++ b/compiler/dex/mir_field_info.cc @@ -0,0 +1,124 @@ +/* + * 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 "mir_field_info.h" + +#include <string.h> + +#include "base/logging.h" +#include "driver/compiler_driver.h" +#include "driver/compiler_driver-inl.h" +#include "mirror/class_loader.h" // Only to allow casts in SirtRef<ClassLoader>. +#include "mirror/dex_cache.h" // Only to allow casts in SirtRef<DexCache>. +#include "scoped_thread_state_change.h" +#include "sirt_ref.h" + +namespace art { + +void MirIFieldLoweringInfo::Resolve(CompilerDriver* compiler_driver, + const DexCompilationUnit* mUnit, + MirIFieldLoweringInfo* field_infos, size_t count) { + if (kIsDebugBuild) { + DCHECK(field_infos != nullptr); + DCHECK_NE(count, 0u); + for (auto it = field_infos, end = field_infos + count; it != end; ++it) { + MirIFieldLoweringInfo unresolved(it->field_idx_); + DCHECK_EQ(memcmp(&unresolved, &*it, sizeof(*it)), 0); + } + } + + // We're going to resolve fields and check access in a tight loop. It's better to hold + // the lock and needed references once than re-acquiring them again and again. + ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::DexCache> dex_cache(soa.Self(), compiler_driver->GetDexCache(mUnit)); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), + compiler_driver->GetClassLoader(soa, mUnit)); + SirtRef<mirror::Class> referrer_class(soa.Self(), + compiler_driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit)); + // Even if the referrer class is unresolved (i.e. we're compiling a method without class + // definition) we still want to resolve fields and record all available info. + + for (auto it = field_infos, end = field_infos + count; it != end; ++it) { + uint32_t field_idx = it->field_idx_; + mirror::ArtField* resolved_field = + compiler_driver->ResolveField(soa, dex_cache, class_loader, mUnit, field_idx, false); + if (UNLIKELY(resolved_field == nullptr)) { + continue; + } + 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); + + std::pair<bool, bool> fast_path = compiler_driver->IsFastInstanceField( + dex_cache.get(), referrer_class.get(), resolved_field, field_idx, &it->field_offset_); + it->flags_ = 0u | // Without kFlagIsStatic. + (is_volatile ? kFlagIsVolatile : 0u) | + (fast_path.first ? kFlagFastGet : 0u) | + (fast_path.second ? kFlagFastPut : 0u); + } +} + +void MirSFieldLoweringInfo::Resolve(CompilerDriver* compiler_driver, + const DexCompilationUnit* mUnit, + MirSFieldLoweringInfo* field_infos, size_t count) { + if (kIsDebugBuild) { + DCHECK(field_infos != nullptr); + DCHECK_NE(count, 0u); + for (auto it = field_infos, end = field_infos + count; it != end; ++it) { + MirSFieldLoweringInfo unresolved(it->field_idx_); + // In 64-bit builds, there's padding after storage_index_, don't include it in memcmp. + size_t size = OFFSETOF_MEMBER(MirSFieldLoweringInfo, storage_index_) + + sizeof(it->storage_index_); + DCHECK_EQ(memcmp(&unresolved, &*it, size), 0); + } + } + + // We're going to resolve fields and check access in a tight loop. It's better to hold + // the lock and needed references once than re-acquiring them again and again. + ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::DexCache> dex_cache(soa.Self(), compiler_driver->GetDexCache(mUnit)); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), + compiler_driver->GetClassLoader(soa, mUnit)); + SirtRef<mirror::Class> referrer_class(soa.Self(), + compiler_driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit)); + // Even if the referrer class is unresolved (i.e. we're compiling a method without class + // definition) we still want to resolve fields and record all available info. + + for (auto it = field_infos, end = field_infos + count; it != end; ++it) { + uint32_t field_idx = it->field_idx_; + mirror::ArtField* resolved_field = + compiler_driver->ResolveField(soa, dex_cache, class_loader, mUnit, field_idx, true); + if (UNLIKELY(resolved_field == nullptr)) { + continue; + } + 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) ? 1u : 0u; + + bool is_referrers_class, is_initialized; + std::pair<bool, bool> fast_path = compiler_driver->IsFastStaticField( + dex_cache.get(), referrer_class.get(), resolved_field, field_idx, &it->field_offset_, + &it->storage_index_, &is_referrers_class, &is_initialized); + it->flags_ = kFlagIsStatic | + (is_volatile ? kFlagIsVolatile : 0u) | + (fast_path.first ? kFlagFastGet : 0u) | + (fast_path.second ? kFlagFastPut : 0u) | + (is_referrers_class ? kFlagIsReferrersClass : 0u) | + (is_initialized ? kFlagIsInitialized : 0u); + } +} + +} // namespace art diff --git a/compiler/dex/mir_field_info.h b/compiler/dex/mir_field_info.h new file mode 100644 index 0000000000..41cb4cee14 --- /dev/null +++ b/compiler/dex/mir_field_info.h @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DEX_MIR_FIELD_INFO_H_ +#define ART_COMPILER_DEX_MIR_FIELD_INFO_H_ + +#include "base/macros.h" +#include "dex_file.h" +#include "offsets.h" + +namespace art { + +class CompilerDriver; +class DexCompilationUnit; + +/* + * Field info is calculated from the perspective of the compilation unit that accesses + * the field and stored in that unit's MIRGraph. Therefore it does not need to reference the + * dex file or method for which it has been calculated. However, we do store the declaring + * field index, class index and dex file of the resolved field to help distinguish between fields. + */ + +class MirFieldInfo { + public: + uint16_t FieldIndex() const { + return field_idx_; + } + + bool IsStatic() const { + return (flags_ & kFlagIsStatic) != 0u; + } + + bool IsResolved() const { + return declaring_dex_file_ != nullptr; + } + + const DexFile* DeclaringDexFile() const { + return declaring_dex_file_; + } + + uint16_t DeclaringClassIndex() const { + return declaring_class_idx_; + } + + uint16_t DeclaringFieldIndex() const { + return declaring_field_idx_; + } + + bool IsVolatile() const { + return (flags_ & kFlagIsVolatile) != 0u; + } + + protected: + enum { + kBitIsStatic = 0, + kBitIsVolatile, + kFieldInfoBitEnd + }; + static constexpr uint16_t kFlagIsVolatile = 1u << kBitIsVolatile; + static constexpr uint16_t kFlagIsStatic = 1u << kBitIsStatic; + + MirFieldInfo(uint16_t field_idx, uint16_t flags) + : field_idx_(field_idx), + flags_(flags), + declaring_field_idx_(0u), + declaring_class_idx_(0u), + declaring_dex_file_(nullptr) { + } + + // Make copy-ctor/assign/dtor protected to avoid slicing. + MirFieldInfo(const MirFieldInfo& other) = default; + MirFieldInfo& operator=(const MirFieldInfo& other) = default; + ~MirFieldInfo() = default; + + // The field index in the compiling method's dex file. + uint16_t field_idx_; + // Flags, for volatility and derived class data. + uint16_t flags_; + // The field index in the dex file that defines field, 0 if unresolved. + uint16_t declaring_field_idx_; + // The type index of the class declaring the field, 0 if unresolved. + uint16_t declaring_class_idx_; + // The dex file that defines the class containing the field and the field, nullptr if unresolved. + const DexFile* declaring_dex_file_; +}; + +class MirIFieldLoweringInfo : public MirFieldInfo { + public: + // For each requested instance field retrieve the field's declaring location (dex file, class + // index and field index) and volatility and compute the whether we can fast path the access + // with IGET/IPUT. For fast path fields, retrieve the field offset. + static void Resolve(CompilerDriver* compiler_driver, const DexCompilationUnit* mUnit, + MirIFieldLoweringInfo* field_infos, size_t count) + LOCKS_EXCLUDED(Locks::mutator_lock_); + + // Construct an unresolved instance field lowering info. + explicit MirIFieldLoweringInfo(uint16_t field_idx) + : MirFieldInfo(field_idx, kFlagIsVolatile), // Without kFlagIsStatic. + field_offset_(0u) { + } + + bool FastGet() const { + return (flags_ & kFlagFastGet) != 0u; + } + + bool FastPut() const { + return (flags_ & kFlagFastPut) != 0u; + } + + MemberOffset FieldOffset() const { + return field_offset_; + } + + private: + enum { + kBitFastGet = kFieldInfoBitEnd, + kBitFastPut, + kIFieldLoweringInfoBitEnd + }; + COMPILE_ASSERT(kIFieldLoweringInfoBitEnd <= 16, too_many_flags); + static constexpr uint16_t kFlagFastGet = 1u << kBitFastGet; + static constexpr uint16_t kFlagFastPut = 1u << kBitFastPut; + + // The member offset of the field, 0u if unresolved. + MemberOffset field_offset_; + + friend class LocalValueNumberingTest; +}; + +class MirSFieldLoweringInfo : public MirFieldInfo { + public: + // For each requested static field retrieve the field's declaring location (dex file, class + // index and field index) and volatility and compute the whether we can fast path the access with + // IGET/IPUT. For fast path fields (at least for IGET), retrieve the information needed for + // the field access, i.e. the field offset, whether the field is in the same class as the + // method being compiled, whether the declaring class can be safely assumed to be initialized + // and the type index of the declaring class in the compiled method's dex file. + static void Resolve(CompilerDriver* compiler_driver, const DexCompilationUnit* mUnit, + MirSFieldLoweringInfo* field_infos, size_t count) + LOCKS_EXCLUDED(Locks::mutator_lock_); + + // Construct an unresolved static field lowering info. + explicit MirSFieldLoweringInfo(uint16_t field_idx) + : MirFieldInfo(field_idx, kFlagIsVolatile | kFlagIsStatic), + field_offset_(0u), + storage_index_(DexFile::kDexNoIndex) { + } + + bool FastGet() const { + return (flags_ & kFlagFastGet) != 0u; + } + + bool FastPut() const { + return (flags_ & kFlagFastPut) != 0u; + } + + bool IsReferrersClass() const { + return (flags_ & kFlagIsReferrersClass) != 0u; + } + + bool IsInitialized() const { + return (flags_ & kFlagIsInitialized) != 0u; + } + + MemberOffset FieldOffset() const { + return field_offset_; + } + + uint32_t StorageIndex() const { + return storage_index_; + } + + private: + enum { + kBitFastGet = kFieldInfoBitEnd, + kBitFastPut, + kBitIsReferrersClass, + kBitIsInitialized, + kSFieldLoweringInfoBitEnd + }; + COMPILE_ASSERT(kSFieldLoweringInfoBitEnd <= 16, too_many_flags); + static constexpr uint16_t kFlagFastGet = 1u << kBitFastGet; + static constexpr uint16_t kFlagFastPut = 1u << kBitFastPut; + static constexpr uint16_t kFlagIsReferrersClass = 1u << kBitIsReferrersClass; + static constexpr uint16_t kFlagIsInitialized = 1u << kBitIsInitialized; + + // The member offset of the field, 0u if unresolved. + MemberOffset field_offset_; + // The type index of the declaring class in the compiling method's dex file, + // -1 if the field is unresolved or there's no appropriate TypeId in that dex file. + uint32_t storage_index_; + + friend class LocalValueNumberingTest; +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_MIR_FIELD_INFO_H_ diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index e4550d1e60..46e854fb2b 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -86,7 +86,10 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) forward_branches_(0), compiler_temps_(arena, 6, kGrowableArrayMisc), num_non_special_compiler_temps_(0), - max_available_non_special_compiler_temps_(0) { + max_available_non_special_compiler_temps_(0), + punt_to_interpreter_(false), + ifield_lowering_infos_(arena, 0u), + sfield_lowering_infos_(arena, 0u) { try_block_addr_ = new (arena_) ArenaBitVector(arena_, 0, true /* expandable */); max_available_special_compiler_temps_ = std::abs(static_cast<int>(kVRegNonSpecialTempBaseReg)) - std::abs(static_cast<int>(kVRegTempBaseReg)); @@ -610,6 +613,7 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ } int flags = Instruction::FlagsOf(insn->dalvikInsn.opcode); + int verify_flags = Instruction::VerifyFlagsOf(insn->dalvikInsn.opcode); uint64_t df_flags = oat_data_flow_attributes_[insn->dalvikInsn.opcode]; @@ -676,6 +680,19 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ } else if (flags & Instruction::kSwitch) { cur_block = ProcessCanSwitch(cur_block, insn, current_offset_, width, flags); } + if (verify_flags & Instruction::kVerifyVarArgRange) { + /* + * The Quick backend's runtime model includes a gap between a method's + * argument ("in") vregs and the rest of its vregs. Handling a range instruction + * which spans the gap is somewhat complicated, and should not happen + * in normal usage of dx. Punt to the interpreter. + */ + int first_reg_in_range = insn->dalvikInsn.vC; + int last_reg_in_range = first_reg_in_range + insn->dalvikInsn.vA - 1; + if (IsInVReg(first_reg_in_range) != IsInVReg(last_reg_in_range)) { + punt_to_interpreter_ = true; + } + } current_offset_ += width; BasicBlock *next_block = FindBlock(current_offset_, /* split */ false, /* create */ false, /* immed_pred_block_p */ NULL); diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 2174f679bf..d4aafbc0f4 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -20,7 +20,9 @@ #include "dex_file.h" #include "dex_instruction.h" #include "compiler_ir.h" -#include "arena_bit_vector.h" +#include "mir_field_info.h" +#include "invoke_type.h" +#include "utils/arena_bit_vector.h" #include "utils/growable_array.h" namespace art { @@ -258,6 +260,12 @@ struct MIR { MIR* throw_insn; // Fused cmp branch condition. ConditionCode ccode; + // IGET/IPUT lowering info index, points to MIRGraph::ifield_lowering_infos_. Due to limit on + // the number of code points (64K) and size of IGET/IPUT insn (2), this will never exceed 32K. + uint32_t ifield_lowering_info; + // SGET/SPUT lowering info index, points to MIRGraph::sfield_lowering_infos_. Due to limit on + // the number of code points (64K) and size of SGET/SPUT insn (2), this will never exceed 32K. + uint32_t sfield_lowering_info; } meta; }; @@ -466,6 +474,18 @@ class MIRGraph { */ void DumpCFG(const char* dir_prefix, bool all_blocks, const char* suffix = nullptr); + void DoCacheFieldLoweringInfo(); + + const MirIFieldLoweringInfo& GetIFieldLoweringInfo(MIR* mir) { + DCHECK_LT(mir->meta.ifield_lowering_info, ifield_lowering_infos_.Size()); + return ifield_lowering_infos_.GetRawStorage()[mir->meta.ifield_lowering_info]; + } + + const MirSFieldLoweringInfo& GetSFieldLoweringInfo(MIR* mir) { + DCHECK_LT(mir->meta.sfield_lowering_info, sfield_lowering_infos_.Size()); + return sfield_lowering_infos_.GetRawStorage()[mir->meta.sfield_lowering_info]; + } + void InitRegLocations(); void RemapRegLocations(); @@ -684,6 +704,11 @@ class MIRGraph { return opcode >= static_cast<int>(kMirOpFirst); } + // Is this vreg in the in set? + bool IsInVReg(int vreg) { + return (vreg >= cu_->num_regs); + } + void DumpCheckStats(); MIR* FindMoveResult(BasicBlock* bb, MIR* mir); int SRegToVReg(int ssa_reg) const; @@ -917,6 +942,9 @@ class MIRGraph { size_t num_non_special_compiler_temps_; size_t max_available_non_special_compiler_temps_; size_t max_available_special_compiler_temps_; + bool punt_to_interpreter_; // Difficult or not worthwhile - just interpret. + GrowableArray<MirIFieldLoweringInfo> ifield_lowering_infos_; + GrowableArray<MirSFieldLoweringInfo> sfield_lowering_infos_; friend class LocalValueNumberingTest; }; diff --git a/compiler/dex/pass.h b/compiler/dex/pass.h index 255892e324..9457d5be76 100644 --- a/compiler/dex/pass.h +++ b/compiler/dex/pass.h @@ -22,8 +22,8 @@ namespace art { // Forward declarations. -class BasicBlock; -class CompilationUnit; +struct BasicBlock; +struct CompilationUnit; class Pass; /** diff --git a/compiler/dex/pass_driver.cc b/compiler/dex/pass_driver.cc index b60f29697b..256bcb1473 100644 --- a/compiler/dex/pass_driver.cc +++ b/compiler/dex/pass_driver.cc @@ -91,6 +91,7 @@ void PassDriver::CreatePasses() { * - This is not yet an issue: no current pass would require it. */ static const Pass* const passes[] = { + GetPassInstance<CacheFieldLoweringInfo>(), GetPassInstance<CodeLayout>(), GetPassInstance<SSATransformation>(), GetPassInstance<ConstantPropagation>(), diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc index 3187fbb28c..70438ecd50 100644 --- a/compiler/dex/portable/mir_to_gbc.cc +++ b/compiler/dex/portable/mir_to_gbc.cc @@ -30,6 +30,7 @@ #include "dex/compiler_internals.h" #include "dex/dataflow_iterator-inl.h" #include "dex/frontend.h" +#include "llvm/ir_builder.h" #include "llvm/llvm_compilation_unit.h" #include "llvm/utils_llvm.h" #include "mir_to_gbc.h" diff --git a/compiler/dex/portable/mir_to_gbc.h b/compiler/dex/portable/mir_to_gbc.h index 2b681f6097..e97634c519 100644 --- a/compiler/dex/portable/mir_to_gbc.h +++ b/compiler/dex/portable/mir_to_gbc.h @@ -17,11 +17,18 @@ #ifndef ART_COMPILER_DEX_PORTABLE_MIR_TO_GBC_H_ #define ART_COMPILER_DEX_PORTABLE_MIR_TO_GBC_H_ +#include <llvm/ADT/ArrayRef.h> +#include <llvm/IR/BasicBlock.h> +#include <llvm/IR/IRBuilder.h> +#include <llvm/IR/LLVMContext.h> +#include <llvm/IR/Module.h> + #include "invoke_type.h" #include "compiled_method.h" #include "dex/compiler_enums.h" #include "dex/compiler_ir.h" #include "dex/backend.h" +#include "llvm/intrinsic_helper.h" #include "llvm/llvm_compilation_unit.h" #include "safe_map.h" diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h index 37b4ec6dc7..7aab55ffc4 100644 --- a/compiler/dex/quick/arm/arm_lir.h +++ b/compiler/dex/quick/arm/arm_lir.h @@ -451,7 +451,6 @@ enum ArmOpcode { kThumb2MovImm16LST, // Special purpose version for switch table use. kThumb2MovImm16HST, // Special purpose version for switch table use. kThumb2LdmiaWB, // ldmia [111010011001[ rn[19..16] mask[15..0]. - kThumb2SubsRRI12, // setflags encoding. kThumb2OrrRRRs, // orrs [111010100101] rn[19..16] [0000] rd[11..8] [0000] rm[3..0]. kThumb2Push1, // t3 encoding of push. kThumb2Pop1, // t3 encoding of pop. diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index 00939ec98b..2a9b5a50e2 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -995,11 +995,6 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD, "ldmia", "!0C!!, <!1R>", 4, kFixupNone), - ENCODING_MAP(kThumb2SubsRRI12, 0xf1b00000, - kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1, - kFmtUnused, -1, -1, - IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "subs", "!0C,!1C,#!2d", 4, kFixupNone), ENCODING_MAP(kThumb2OrrRRRs, 0xea500000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES, diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index b36dde98b2..9f75422a49 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -85,7 +85,8 @@ void ArmMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset, LIR* switch_branch = NewLIR1(kThumb2AddPCR, r_disp); tab_rec->anchor = switch_branch; // Needs to use setflags encoding here - NewLIR3(kThumb2SubsRRI12, r_idx, r_idx, 1); + OpRegRegImm(kOpSub, r_idx, r_idx, 1); // For value == 1, this should set flags. + DCHECK(last_lir_insn_->u.m.def_mask & ENCODE_CCODE); OpCondBranch(kCondNe, target); } diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 43928fc5e2..1e290ad302 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -753,7 +753,8 @@ LIR* ArmMir2Lir::OpTestSuspend(LIR* target) { // Decrement register and branch on condition LIR* ArmMir2Lir::OpDecAndBranch(ConditionCode c_code, int reg, LIR* target) { // Combine sub & test using sub setflags encoding here - NewLIR3(kThumb2SubsRRI12, reg, reg, 1); + OpRegRegImm(kOpSub, reg, reg, 1); // For value == 1, this should set flags. + DCHECK(last_lir_insn_->u.m.def_mask & ENCODE_CCODE); return OpCondBranch(c_code, target); } diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index 9d3968bff2..c2cfb4dbc1 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -499,12 +499,6 @@ LIR* ArmMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) { else opcode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3; return NewLIR3(opcode, r_dest, r_src1, abs_value); - } else if ((abs_value & 0x3ff) == abs_value) { - if (op == kOpAdd) - opcode = (neg) ? kThumb2SubRRI12 : kThumb2AddRRI12; - else - opcode = (neg) ? kThumb2AddRRI12 : kThumb2SubRRI12; - return NewLIR3(opcode, r_dest, r_src1, abs_value); } if (mod_imm < 0) { mod_imm = ModifiedImmediate(-value); @@ -512,6 +506,15 @@ LIR* ArmMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) { op = (op == kOpAdd) ? kOpSub : kOpAdd; } } + if (mod_imm < 0 && (abs_value & 0x3ff) == abs_value) { + // This is deliberately used only if modified immediate encoding is inadequate since + // we sometimes actually use the flags for small values but not necessarily low regs. + if (op == kOpAdd) + opcode = (neg) ? kThumb2SubRRI12 : kThumb2AddRRI12; + else + opcode = (neg) ? kThumb2AddRRI12 : kThumb2SubRRI12; + return NewLIR3(opcode, r_dest, r_src1, abs_value); + } if (op == kOpSub) { opcode = kThumb2SubRRI8M; alt_opcode = kThumb2SubRRR; diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 31854496ab..db7bdc84e8 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -78,11 +78,6 @@ void Mir2Lir::MarkSafepointPC(LIR* inst) { DCHECK_EQ(safepoint_pc->u.m.def_mask, ENCODE_ALL); } -bool Mir2Lir::FastInstance(uint32_t field_idx, bool is_put, int* field_offset, bool* is_volatile) { - return cu_->compiler_driver->ComputeInstanceFieldInfo( - field_idx, mir_graph_->GetCurrentDexCompilationUnit(), is_put, field_offset, is_volatile); -} - /* Remove a LIR from the list. */ void Mir2Lir::UnlinkLIR(LIR* lir) { if (UNLIKELY(lir == first_lir_insn_)) { @@ -1070,10 +1065,12 @@ CompiledMethod* Mir2Lir::GetCompiledMethod() { DCHECK_EQ(fp_vmap_table_.size(), 0u); vmap_encoder.PushBackUnsigned(0u); // Size is 0. } + + UniquePtr<std::vector<uint8_t> > cfi_info(ReturnCallFrameInformation()); CompiledMethod* result = new CompiledMethod(*cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_, core_spill_mask_, fp_spill_mask_, encoded_mapping_table_, - vmap_encoder.GetData(), native_gc_map_); + vmap_encoder.GetData(), native_gc_map_, cfi_info.get()); return result; } @@ -1216,4 +1213,9 @@ void Mir2Lir::LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_re AppendLIR(load_pc_rel); } +std::vector<uint8_t>* Mir2Lir::ReturnCallFrameInformation() { + // Default case is to do nothing. + return nullptr; +} + } // namespace art diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 3dcb964fab..b4d8dd6009 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -31,7 +31,7 @@ namespace verifier { class MethodVerifier; } // namespace verifier -class CallInfo; +struct CallInfo; class Mir2Lir; enum InlineMethodOpcode : uint16_t { @@ -61,6 +61,7 @@ enum InlineMethodOpcode : uint16_t { kInlineOpIGet, kInlineOpIPut, }; +std::ostream& operator<<(std::ostream& os, const InlineMethodOpcode& rhs); enum InlineMethodFlags : uint16_t { kNoInlineMethodFlags = 0x0000, @@ -78,13 +79,13 @@ enum IntrinsicFlags { // kIntrinsicIsEmptyOrLength kIntrinsicFlagLength = kIntrinsicFlagNone, - kIntrinsicFlagIsEmpty = 1, + kIntrinsicFlagIsEmpty = kIntrinsicFlagMin, // kIntrinsicIndexOf - kIntrinsicFlagBase0 = 1, + kIntrinsicFlagBase0 = kIntrinsicFlagMin, // kIntrinsicUnsafeGet, kIntrinsicUnsafePut, kIntrinsicUnsafeCas - kIntrinsicFlagIsLong = 1, + kIntrinsicFlagIsLong = kIntrinsicFlagMin, // kIntrinsicUnsafeGet, kIntrinsicUnsafePut kIntrinsicFlagIsVolatile = 2, // kIntrinsicUnsafePut, kIntrinsicUnsafeCas @@ -187,7 +188,6 @@ class DexFileMethodInliner { */ bool GenSpecial(Mir2Lir* backend, uint32_t method_idx); - private: /** * To avoid multiple lookups of a class by its descriptor, we cache its * type index in the IndexCache. These are the indexes into the IndexCache @@ -311,6 +311,7 @@ class DexFileMethodInliner { kProtoCacheLast }; + private: /** * The maximum number of method parameters we support in the ProtoDef. */ diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 0533fbfcd7..49e3c6f2cc 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -381,20 +381,14 @@ class StaticFieldSlowPath : public Mir2Lir::LIRSlowPath { const int r_base_; }; -void Mir2Lir::GenSput(uint32_t field_idx, RegLocation rl_src, bool is_long_or_double, +void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double, bool is_object) { - int field_offset; - int storage_index; - bool is_volatile; - bool is_referrers_class; - bool is_initialized; - bool fast_path = cu_->compiler_driver->ComputeStaticFieldInfo( - field_idx, mir_graph_->GetCurrentDexCompilationUnit(), true, - &field_offset, &storage_index, &is_referrers_class, &is_volatile, &is_initialized); - if (fast_path && !SLOW_FIELD_PATH) { - DCHECK_GE(field_offset, 0); + const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir); + cu_->compiler_driver->ProcessedStaticField(field_info.FastPut(), field_info.IsReferrersClass()); + if (field_info.FastPut() && !SLOW_FIELD_PATH) { + DCHECK_GE(field_info.FieldOffset().Int32Value(), 0); int r_base; - if (is_referrers_class) { + if (field_info.IsReferrersClass()) { // Fast path, static storage base is this method's class RegLocation rl_method = LoadCurrMethod(); r_base = AllocTemp(); @@ -407,7 +401,7 @@ void Mir2Lir::GenSput(uint32_t field_idx, RegLocation rl_src, bool is_long_or_do // Medium path, static storage base in a different class which requires checks that the other // class is initialized. // TODO: remove initialized check now that we are initializing classes in the compiler driver. - DCHECK_GE(storage_index, 0); + DCHECK_NE(field_info.StorageIndex(), DexFile::kDexNoIndex); // May do runtime call so everything to home locations. FlushAllRegs(); // Using fixed register to sync with possible call to runtime support. @@ -420,9 +414,9 @@ void Mir2Lir::GenSput(uint32_t field_idx, RegLocation rl_src, bool is_long_or_do mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base); LoadWordDisp(r_base, mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + - sizeof(int32_t*) * storage_index, r_base); + sizeof(int32_t*) * field_info.StorageIndex(), r_base); // r_base now points at static storage (Class*) or NULL if the type is not yet resolved. - if (!is_initialized) { + if (!field_info.IsInitialized()) { // Check if r_base is NULL or a not yet initialized class. // The slow path is invoked if the r_base is NULL or the class pointed @@ -437,7 +431,7 @@ void Mir2Lir::GenSput(uint32_t field_idx, RegLocation rl_src, bool is_long_or_do AddSlowPath(new (arena_) StaticFieldSlowPath(this, unresolved_branch, uninit_branch, cont, - storage_index, r_base)); + field_info.StorageIndex(), r_base)); FreeTemp(r_tmp); } @@ -449,16 +443,16 @@ void Mir2Lir::GenSput(uint32_t field_idx, RegLocation rl_src, bool is_long_or_do } else { rl_src = LoadValue(rl_src, kAnyReg); } - if (is_volatile) { + if (field_info.IsVolatile()) { GenMemBarrier(kStoreStore); } if (is_long_or_double) { - StoreBaseDispWide(r_base, field_offset, rl_src.low_reg, + StoreBaseDispWide(r_base, field_info.FieldOffset().Int32Value(), rl_src.low_reg, rl_src.high_reg); } else { - StoreWordDisp(r_base, field_offset, rl_src.low_reg); + StoreWordDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.low_reg); } - if (is_volatile) { + if (field_info.IsVolatile()) { GenMemBarrier(kStoreLoad); } if (is_object && !mir_graph_->IsConstantNullRef(rl_src)) { @@ -471,24 +465,18 @@ void Mir2Lir::GenSput(uint32_t field_idx, RegLocation rl_src, bool is_long_or_do is_long_or_double ? QUICK_ENTRYPOINT_OFFSET(pSet64Static) : (is_object ? QUICK_ENTRYPOINT_OFFSET(pSetObjStatic) : QUICK_ENTRYPOINT_OFFSET(pSet32Static)); - CallRuntimeHelperImmRegLocation(setter_offset, field_idx, rl_src, true); + CallRuntimeHelperImmRegLocation(setter_offset, field_info.FieldIndex(), rl_src, true); } } -void Mir2Lir::GenSget(uint32_t field_idx, RegLocation rl_dest, +void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, bool is_long_or_double, bool is_object) { - int field_offset; - int storage_index; - bool is_volatile; - bool is_referrers_class; - bool is_initialized; - bool fast_path = cu_->compiler_driver->ComputeStaticFieldInfo( - field_idx, mir_graph_->GetCurrentDexCompilationUnit(), false, - &field_offset, &storage_index, &is_referrers_class, &is_volatile, &is_initialized); - if (fast_path && !SLOW_FIELD_PATH) { - DCHECK_GE(field_offset, 0); + const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir); + cu_->compiler_driver->ProcessedStaticField(field_info.FastGet(), field_info.IsReferrersClass()); + if (field_info.FastGet() && !SLOW_FIELD_PATH) { + DCHECK_GE(field_info.FieldOffset().Int32Value(), 0); int r_base; - if (is_referrers_class) { + if (field_info.IsReferrersClass()) { // Fast path, static storage base is this method's class RegLocation rl_method = LoadCurrMethod(); r_base = AllocTemp(); @@ -497,7 +485,7 @@ void Mir2Lir::GenSget(uint32_t field_idx, RegLocation rl_dest, } else { // Medium path, static storage base in a different class which requires checks that the other // class is initialized - DCHECK_GE(storage_index, 0); + DCHECK_NE(field_info.StorageIndex(), DexFile::kDexNoIndex); // May do runtime call so everything to home locations. FlushAllRegs(); // Using fixed register to sync with possible call to runtime support. @@ -510,9 +498,9 @@ void Mir2Lir::GenSget(uint32_t field_idx, RegLocation rl_dest, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base); LoadWordDisp(r_base, mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + - sizeof(int32_t*) * storage_index, r_base); + sizeof(int32_t*) * field_info.StorageIndex(), r_base); // r_base now points at static storage (Class*) or NULL if the type is not yet resolved. - if (!is_initialized) { + if (!field_info.IsInitialized()) { // Check if r_base is NULL or a not yet initialized class. // The slow path is invoked if the r_base is NULL or the class pointed @@ -527,7 +515,7 @@ void Mir2Lir::GenSget(uint32_t field_idx, RegLocation rl_dest, AddSlowPath(new (arena_) StaticFieldSlowPath(this, unresolved_branch, uninit_branch, cont, - storage_index, r_base)); + field_info.StorageIndex(), r_base)); FreeTemp(r_tmp); } @@ -535,14 +523,14 @@ void Mir2Lir::GenSget(uint32_t field_idx, RegLocation rl_dest, } // r_base now holds static storage base RegLocation rl_result = EvalLoc(rl_dest, kAnyReg, true); - if (is_volatile) { + if (field_info.IsVolatile()) { GenMemBarrier(kLoadLoad); } if (is_long_or_double) { - LoadBaseDispWide(r_base, field_offset, rl_result.low_reg, + LoadBaseDispWide(r_base, field_info.FieldOffset().Int32Value(), rl_result.low_reg, rl_result.high_reg, INVALID_SREG); } else { - LoadWordDisp(r_base, field_offset, rl_result.low_reg); + LoadWordDisp(r_base, field_info.FieldOffset().Int32Value(), rl_result.low_reg); } FreeTemp(r_base); if (is_long_or_double) { @@ -556,7 +544,7 @@ void Mir2Lir::GenSget(uint32_t field_idx, RegLocation rl_dest, is_long_or_double ? QUICK_ENTRYPOINT_OFFSET(pGet64Static) :(is_object ? QUICK_ENTRYPOINT_OFFSET(pGetObjStatic) : QUICK_ENTRYPOINT_OFFSET(pGet32Static)); - CallRuntimeHelperImm(getterOffset, field_idx, true); + CallRuntimeHelperImm(getterOffset, field_info.FieldIndex(), true); if (is_long_or_double) { RegLocation rl_result = GetReturnWide(rl_dest.fp); StoreValueWide(rl_dest, rl_result); @@ -698,18 +686,15 @@ void Mir2Lir::HandleThrowLaunchPads() { } } -void Mir2Lir::GenIGet(uint32_t field_idx, int opt_flags, OpSize size, +void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size, RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double, bool is_object) { - int field_offset; - bool is_volatile; - - bool fast_path = FastInstance(field_idx, false, &field_offset, &is_volatile); - - if (fast_path && !SLOW_FIELD_PATH) { + const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir); + cu_->compiler_driver->ProcessedInstanceField(field_info.FastGet()); + if (field_info.FastGet() && !SLOW_FIELD_PATH) { RegLocation rl_result; RegisterClass reg_class = oat_reg_class_by_size(size); - DCHECK_GE(field_offset, 0); + DCHECK_GE(field_info.FieldOffset().Int32Value(), 0); rl_obj = LoadValue(rl_obj, kCoreReg); if (is_long_or_double) { DCHECK(rl_dest.wide); @@ -717,17 +702,17 @@ void Mir2Lir::GenIGet(uint32_t field_idx, int opt_flags, OpSize size, if (cu_->instruction_set == kX86) { rl_result = EvalLoc(rl_dest, reg_class, true); GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, opt_flags); - LoadBaseDispWide(rl_obj.low_reg, field_offset, rl_result.low_reg, + LoadBaseDispWide(rl_obj.low_reg, field_info.FieldOffset().Int32Value(), rl_result.low_reg, rl_result.high_reg, rl_obj.s_reg_low); - if (is_volatile) { + if (field_info.IsVolatile()) { GenMemBarrier(kLoadLoad); } } else { int reg_ptr = AllocTemp(); - OpRegRegImm(kOpAdd, reg_ptr, rl_obj.low_reg, field_offset); + OpRegRegImm(kOpAdd, reg_ptr, rl_obj.low_reg, field_info.FieldOffset().Int32Value()); rl_result = EvalLoc(rl_dest, reg_class, true); LoadBaseDispWide(reg_ptr, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG); - if (is_volatile) { + if (field_info.IsVolatile()) { GenMemBarrier(kLoadLoad); } FreeTemp(reg_ptr); @@ -736,9 +721,9 @@ void Mir2Lir::GenIGet(uint32_t field_idx, int opt_flags, OpSize size, } else { rl_result = EvalLoc(rl_dest, reg_class, true); GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, opt_flags); - LoadBaseDisp(rl_obj.low_reg, field_offset, rl_result.low_reg, + LoadBaseDisp(rl_obj.low_reg, field_info.FieldOffset().Int32Value(), rl_result.low_reg, kWord, rl_obj.s_reg_low); - if (is_volatile) { + if (field_info.IsVolatile()) { GenMemBarrier(kLoadLoad); } StoreValue(rl_dest, rl_result); @@ -748,7 +733,7 @@ void Mir2Lir::GenIGet(uint32_t field_idx, int opt_flags, OpSize size, is_long_or_double ? QUICK_ENTRYPOINT_OFFSET(pGet64Instance) : (is_object ? QUICK_ENTRYPOINT_OFFSET(pGetObjInstance) : QUICK_ENTRYPOINT_OFFSET(pGet32Instance)); - CallRuntimeHelperImmRegLocation(getterOffset, field_idx, rl_obj, true); + CallRuntimeHelperImmRegLocation(getterOffset, field_info.FieldIndex(), rl_obj, true); if (is_long_or_double) { RegLocation rl_result = GetReturnWide(rl_dest.fp); StoreValueWide(rl_dest, rl_result); @@ -759,39 +744,37 @@ void Mir2Lir::GenIGet(uint32_t field_idx, int opt_flags, OpSize size, } } -void Mir2Lir::GenIPut(uint32_t field_idx, int opt_flags, OpSize size, +void Mir2Lir::GenIPut(MIR* mir, int opt_flags, OpSize size, RegLocation rl_src, RegLocation rl_obj, bool is_long_or_double, bool is_object) { - int field_offset; - bool is_volatile; - - bool fast_path = FastInstance(field_idx, true, &field_offset, &is_volatile); - if (fast_path && !SLOW_FIELD_PATH) { + const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir); + cu_->compiler_driver->ProcessedInstanceField(field_info.FastPut()); + if (field_info.FastPut() && !SLOW_FIELD_PATH) { RegisterClass reg_class = oat_reg_class_by_size(size); - DCHECK_GE(field_offset, 0); + DCHECK_GE(field_info.FieldOffset().Int32Value(), 0); rl_obj = LoadValue(rl_obj, kCoreReg); if (is_long_or_double) { int reg_ptr; rl_src = LoadValueWide(rl_src, kAnyReg); GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, opt_flags); reg_ptr = AllocTemp(); - OpRegRegImm(kOpAdd, reg_ptr, rl_obj.low_reg, field_offset); - if (is_volatile) { + OpRegRegImm(kOpAdd, reg_ptr, rl_obj.low_reg, field_info.FieldOffset().Int32Value()); + if (field_info.IsVolatile()) { GenMemBarrier(kStoreStore); } StoreBaseDispWide(reg_ptr, 0, rl_src.low_reg, rl_src.high_reg); - if (is_volatile) { + if (field_info.IsVolatile()) { GenMemBarrier(kLoadLoad); } FreeTemp(reg_ptr); } else { rl_src = LoadValue(rl_src, reg_class); GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, opt_flags); - if (is_volatile) { + if (field_info.IsVolatile()) { GenMemBarrier(kStoreStore); } - StoreBaseDisp(rl_obj.low_reg, field_offset, rl_src.low_reg, kWord); - if (is_volatile) { + StoreBaseDisp(rl_obj.low_reg, field_info.FieldOffset().Int32Value(), rl_src.low_reg, kWord); + if (field_info.IsVolatile()) { GenMemBarrier(kLoadLoad); } if (is_object && !mir_graph_->IsConstantNullRef(rl_src)) { @@ -803,7 +786,8 @@ void Mir2Lir::GenIPut(uint32_t field_idx, int opt_flags, OpSize size, is_long_or_double ? QUICK_ENTRYPOINT_OFFSET(pSet64Instance) : (is_object ? QUICK_ENTRYPOINT_OFFSET(pSetObjInstance) : QUICK_ENTRYPOINT_OFFSET(pSet32Instance)); - CallRuntimeHelperImmRegLocationRegLocation(setter_offset, field_idx, rl_obj, rl_src, true); + CallRuntimeHelperImmRegLocationRegLocation(setter_offset, field_info.FieldIndex(), + rl_obj, rl_src, true); } } diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 8c2ed3667b..00518bdb2f 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -596,72 +596,72 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list break; case Instruction::IGET_OBJECT: - GenIGet(vC, opt_flags, kWord, rl_dest, rl_src[0], false, true); + GenIGet(mir, opt_flags, kWord, rl_dest, rl_src[0], false, true); break; case Instruction::IGET_WIDE: - GenIGet(vC, opt_flags, kLong, rl_dest, rl_src[0], true, false); + GenIGet(mir, opt_flags, kLong, rl_dest, rl_src[0], true, false); break; case Instruction::IGET: - GenIGet(vC, opt_flags, kWord, rl_dest, rl_src[0], false, false); + GenIGet(mir, opt_flags, kWord, rl_dest, rl_src[0], false, false); break; case Instruction::IGET_CHAR: - GenIGet(vC, opt_flags, kUnsignedHalf, rl_dest, rl_src[0], false, false); + GenIGet(mir, opt_flags, kUnsignedHalf, rl_dest, rl_src[0], false, false); break; case Instruction::IGET_SHORT: - GenIGet(vC, opt_flags, kSignedHalf, rl_dest, rl_src[0], false, false); + GenIGet(mir, opt_flags, kSignedHalf, rl_dest, rl_src[0], false, false); break; case Instruction::IGET_BOOLEAN: case Instruction::IGET_BYTE: - GenIGet(vC, opt_flags, kUnsignedByte, rl_dest, rl_src[0], false, false); + GenIGet(mir, opt_flags, kUnsignedByte, rl_dest, rl_src[0], false, false); break; case Instruction::IPUT_WIDE: - GenIPut(vC, opt_flags, kLong, rl_src[0], rl_src[1], true, false); + GenIPut(mir, opt_flags, kLong, rl_src[0], rl_src[1], true, false); break; case Instruction::IPUT_OBJECT: - GenIPut(vC, opt_flags, kWord, rl_src[0], rl_src[1], false, true); + GenIPut(mir, opt_flags, kWord, rl_src[0], rl_src[1], false, true); break; case Instruction::IPUT: - GenIPut(vC, opt_flags, kWord, rl_src[0], rl_src[1], false, false); + GenIPut(mir, opt_flags, kWord, rl_src[0], rl_src[1], false, false); break; case Instruction::IPUT_BOOLEAN: case Instruction::IPUT_BYTE: - GenIPut(vC, opt_flags, kUnsignedByte, rl_src[0], rl_src[1], false, false); + GenIPut(mir, opt_flags, kUnsignedByte, rl_src[0], rl_src[1], false, false); break; case Instruction::IPUT_CHAR: - GenIPut(vC, opt_flags, kUnsignedHalf, rl_src[0], rl_src[1], false, false); + GenIPut(mir, opt_flags, kUnsignedHalf, rl_src[0], rl_src[1], false, false); break; case Instruction::IPUT_SHORT: - GenIPut(vC, opt_flags, kSignedHalf, rl_src[0], rl_src[1], false, false); + GenIPut(mir, opt_flags, kSignedHalf, rl_src[0], rl_src[1], false, false); break; case Instruction::SGET_OBJECT: - GenSget(vB, rl_dest, false, true); + GenSget(mir, rl_dest, false, true); break; case Instruction::SGET: case Instruction::SGET_BOOLEAN: case Instruction::SGET_BYTE: case Instruction::SGET_CHAR: case Instruction::SGET_SHORT: - GenSget(vB, rl_dest, false, false); + GenSget(mir, rl_dest, false, false); break; case Instruction::SGET_WIDE: - GenSget(vB, rl_dest, true, false); + GenSget(mir, rl_dest, true, false); break; case Instruction::SPUT_OBJECT: - GenSput(vB, rl_src[0], false, true); + GenSput(mir, rl_src[0], false, true); break; case Instruction::SPUT: @@ -669,11 +669,11 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list case Instruction::SPUT_BYTE: case Instruction::SPUT_CHAR: case Instruction::SPUT_SHORT: - GenSput(vB, rl_src[0], false, false); + GenSput(mir, rl_src[0], false, false); break; case Instruction::SPUT_WIDE: - GenSput(vB, rl_src[0], true, false); + GenSput(mir, rl_src[0], true, false); break; case Instruction::INVOKE_STATIC_RANGE: diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 6e97c531cd..e230c9d67b 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -23,7 +23,7 @@ #include "dex/compiler_ir.h" #include "dex/backend.h" #include "driver/compiler_driver.h" -#include "leb128_encoder.h" +#include "leb128.h" #include "safe_map.h" #include "utils/arena_allocator.h" #include "utils/growable_array.h" @@ -395,7 +395,6 @@ class Mir2Lir : public Backend { virtual void Materialize(); virtual CompiledMethod* GetCompiledMethod(); void MarkSafepointPC(LIR* inst); - bool FastInstance(uint32_t field_idx, bool is_put, int* field_offset, bool* is_volatile); void SetupResourceMasks(LIR* lir); void SetMemRefType(LIR* lir, bool is_load, int mem_type); void AnnotateDalvikRegAccess(LIR* lir, int reg_id, bool is_load, bool is64bit); @@ -558,13 +557,13 @@ class Mir2Lir : public Backend { void GenNewArray(uint32_t type_idx, RegLocation rl_dest, RegLocation rl_src); void GenFilledNewArray(CallInfo* info); - void GenSput(uint32_t field_idx, RegLocation rl_src, + void GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double, bool is_object); - void GenSget(uint32_t field_idx, RegLocation rl_dest, + void GenSget(MIR* mir, RegLocation rl_dest, bool is_long_or_double, bool is_object); - void GenIGet(uint32_t field_idx, int opt_flags, OpSize size, + void GenIGet(MIR* mir, int opt_flags, OpSize size, RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double, bool is_object); - void GenIPut(uint32_t field_idx, int opt_flags, OpSize size, + void GenIPut(MIR* mir, int opt_flags, OpSize size, RegLocation rl_src, RegLocation rl_obj, bool is_long_or_double, bool is_object); void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index, RegLocation rl_src); @@ -1089,6 +1088,11 @@ class Mir2Lir : public Backend { bool can_assume_type_is_in_dex_cache, uint32_t type_idx, RegLocation rl_dest, RegLocation rl_src); + /* + * @brief Generate the debug_frame FDE information if possible. + * @returns pointer to vector containg CFE information, or NULL. + */ + virtual std::vector<uint8_t>* ReturnCallFrameInformation(); /** * @brief Used to insert marker that can be used to associate MIR with LIR. diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index 0613cdff7a..399001c397 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -198,7 +198,7 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { LockTemp(rX86_ARG2); /* Build frame, return address already on stack */ - OpRegImm(kOpSub, rX86_SP, frame_size_ - 4); + stack_decrement_ = OpRegImm(kOpSub, rX86_SP, frame_size_ - 4); /* * We can safely skip the stack overflow check if we're @@ -246,7 +246,7 @@ void X86Mir2Lir::GenExitSequence() { NewLIR0(kPseudoMethodExit); UnSpillCoreRegs(); /* Remove frame except for return address */ - OpRegImm(kOpAdd, rX86_SP, frame_size_ - 4); + stack_increment_ = OpRegImm(kOpAdd, rX86_SP, frame_size_ - 4); NewLIR0(kX86Ret); } diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 421d51e4fd..c97d0e6f4d 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -302,6 +302,18 @@ class X86Mir2Lir : public Mir2Lir { */ void InstallLiteralPools(); + /* + * @brief Generate the debug_frame CFI information. + * @returns pointer to vector containing CFE information + */ + static std::vector<uint8_t>* ReturnCommonCallFrameInformation(); + + /* + * @brief Generate the debug_frame FDE information. + * @returns pointer to vector containing CFE information + */ + std::vector<uint8_t>* ReturnCallFrameInformation(); + private: void EmitPrefix(const X86EncodingMap* entry); void EmitOpcode(const X86EncodingMap* entry); @@ -549,6 +561,12 @@ class X86Mir2Lir : public Mir2Lir { // Instructions needing patching with PC relative code addresses. GrowableArray<LIR*> call_method_insns_; + + // Prologue decrement of stack pointer. + LIR* stack_decrement_; + + // Epilogue increment of stack pointer. + LIR* stack_increment_; }; } // namespace art diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index eea7191c3b..7bb866d4aa 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -539,7 +539,8 @@ X86Mir2Lir::X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* : Mir2Lir(cu, mir_graph, arena), method_address_insns_(arena, 100, kGrowableArrayMisc), class_type_address_insns_(arena, 100, kGrowableArrayMisc), - call_method_insns_(arena, 100, kGrowableArrayMisc) { + call_method_insns_(arena, 100, kGrowableArrayMisc), + stack_decrement_(nullptr), stack_increment_(nullptr) { store_method_addr_used_ = false; for (int i = 0; i < kX86Last; i++) { if (X86Mir2Lir::EncodingMap[i].opcode != i) { @@ -1118,4 +1119,166 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { return true; } +/* + * @brief Enter a 32 bit quantity into the FDE buffer + * @param buf FDE buffer. + * @param data Data value. + */ +static void PushWord(std::vector<uint8_t>&buf, int data) { + buf.push_back(data & 0xff); + buf.push_back((data >> 8) & 0xff); + buf.push_back((data >> 16) & 0xff); + buf.push_back((data >> 24) & 0xff); +} + +/* + * @brief Enter an 'advance LOC' into the FDE buffer + * @param buf FDE buffer. + * @param increment Amount by which to increase the current location. + */ +static void AdvanceLoc(std::vector<uint8_t>&buf, uint32_t increment) { + if (increment < 64) { + // Encoding in opcode. + buf.push_back(0x1 << 6 | increment); + } else if (increment < 256) { + // Single byte delta. + buf.push_back(0x02); + buf.push_back(increment); + } else if (increment < 256 * 256) { + // Two byte delta. + buf.push_back(0x03); + buf.push_back(increment & 0xff); + buf.push_back((increment >> 8) & 0xff); + } else { + // Four byte delta. + buf.push_back(0x04); + PushWord(buf, increment); + } +} + + +std::vector<uint8_t>* X86CFIInitialization() { + return X86Mir2Lir::ReturnCommonCallFrameInformation(); +} + +std::vector<uint8_t>* X86Mir2Lir::ReturnCommonCallFrameInformation() { + std::vector<uint8_t>*cfi_info = new std::vector<uint8_t>; + + // Length of the CIE (except for this field). + PushWord(*cfi_info, 16); + + // CIE id. + PushWord(*cfi_info, 0xFFFFFFFFU); + + // Version: 3. + cfi_info->push_back(0x03); + + // Augmentation: empty string. + cfi_info->push_back(0x0); + + // Code alignment: 1. + cfi_info->push_back(0x01); + + // Data alignment: -4. + cfi_info->push_back(0x7C); + + // Return address register (R8). + cfi_info->push_back(0x08); + + // Initial return PC is 4(ESP): DW_CFA_def_cfa R4 4. + cfi_info->push_back(0x0C); + cfi_info->push_back(0x04); + cfi_info->push_back(0x04); + + // Return address location: 0(SP): DW_CFA_offset R8 1 (* -4);. + cfi_info->push_back(0x2 << 6 | 0x08); + cfi_info->push_back(0x01); + + // And 2 Noops to align to 4 byte boundary. + cfi_info->push_back(0x0); + cfi_info->push_back(0x0); + + DCHECK_EQ(cfi_info->size() & 3, 0U); + return cfi_info; +} + +static void EncodeUnsignedLeb128(std::vector<uint8_t>& buf, uint32_t value) { + uint8_t buffer[12]; + uint8_t *ptr = EncodeUnsignedLeb128(buffer, value); + for (uint8_t *p = buffer; p < ptr; p++) { + buf.push_back(*p); + } +} + +std::vector<uint8_t>* X86Mir2Lir::ReturnCallFrameInformation() { + std::vector<uint8_t>*cfi_info = new std::vector<uint8_t>; + + // Generate the FDE for the method. + DCHECK_NE(data_offset_, 0U); + + // Length (will be filled in later in this routine). + PushWord(*cfi_info, 0); + + // CIE_pointer (can be filled in by linker); might be left at 0 if there is only + // one CIE for the whole debug_frame section. + PushWord(*cfi_info, 0); + + // 'initial_location' (filled in by linker). + PushWord(*cfi_info, 0); + + // 'address_range' (number of bytes in the method). + PushWord(*cfi_info, data_offset_); + + // The instructions in the FDE. + if (stack_decrement_ != nullptr) { + // Advance LOC to just past the stack decrement. + uint32_t pc = NEXT_LIR(stack_decrement_)->offset; + AdvanceLoc(*cfi_info, pc); + + // Now update the offset to the call frame: DW_CFA_def_cfa_offset frame_size. + cfi_info->push_back(0x0e); + EncodeUnsignedLeb128(*cfi_info, frame_size_); + + // We continue with that stack until the epilogue. + if (stack_increment_ != nullptr) { + uint32_t new_pc = NEXT_LIR(stack_increment_)->offset; + AdvanceLoc(*cfi_info, new_pc - pc); + + // We probably have code snippets after the epilogue, so save the + // current state: DW_CFA_remember_state. + cfi_info->push_back(0x0a); + + // We have now popped the stack: DW_CFA_def_cfa_offset 4. There is only the return + // PC on the stack now. + cfi_info->push_back(0x0e); + EncodeUnsignedLeb128(*cfi_info, 4); + + // Everything after that is the same as before the epilogue. + // Stack bump was followed by RET instruction. + LIR *post_ret_insn = NEXT_LIR(NEXT_LIR(stack_increment_)); + if (post_ret_insn != nullptr) { + pc = new_pc; + new_pc = post_ret_insn->offset; + AdvanceLoc(*cfi_info, new_pc - pc); + // Restore the state: DW_CFA_restore_state. + cfi_info->push_back(0x0b); + } + } + } + + // Padding to a multiple of 4 + while ((cfi_info->size() & 3) != 0) { + // DW_CFA_nop is encoded as 0. + cfi_info->push_back(0); + } + + // Set the length of the FDE inside the generated bytes. + uint32_t length = cfi_info->size() - 4; + (*cfi_info)[0] = length; + (*cfi_info)[1] = length >> 8; + (*cfi_info)[2] = length >> 16; + (*cfi_info)[3] = length >> 24; + return cfi_info; +} + } // namespace art diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc index 0f79f4128f..4e258ef7c7 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "bit_vector_block_iterator.h" #include "compiler_internals.h" #include "dataflow_iterator-inl.h" @@ -248,9 +249,9 @@ bool MIRGraph::ComputeDominanceFrontier(BasicBlock* bb) { } /* Calculate DF_up */ - ArenaBitVector::BasicBlockIterator it(bb->i_dominated, cu_); + BitVectorBlockIterator it(bb->i_dominated, cu_); for (BasicBlock *dominated_bb = it.Next(); dominated_bb != nullptr; dominated_bb = it.Next()) { - ArenaBitVector::BasicBlockIterator inner_it(dominated_bb->dom_frontier, cu_); + BitVectorBlockIterator inner_it(dominated_bb->dom_frontier, cu_); for (BasicBlock *df_up_block = inner_it.Next(); df_up_block != nullptr; df_up_block = inner_it.Next()) { CheckForDominanceFrontier(bb, df_up_block); diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h new file mode 100644 index 0000000000..d401398ca4 --- /dev/null +++ b/compiler/driver/compiler_driver-inl.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DRIVER_COMPILER_DRIVER_INL_H_ +#define ART_COMPILER_DRIVER_COMPILER_DRIVER_INL_H_ + +#include "compiler_driver.h" +#include "dex/compiler_ir.h" +#include "mirror/art_field.h" +#include "mirror/art_field-inl.h" +#include "mirror/class_loader.h" +#include "mirror/dex_cache.h" +#include "mirror/art_field-inl.h" +#include "scoped_thread_state_change.h" + +namespace art { + +inline mirror::DexCache* CompilerDriver::GetDexCache(const DexCompilationUnit* mUnit) { + return mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile()); +} + +inline mirror::ClassLoader* CompilerDriver::GetClassLoader(ScopedObjectAccess& soa, + const DexCompilationUnit* mUnit) { + return soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()); +} + +inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass( + ScopedObjectAccess& soa, const SirtRef<mirror::DexCache>& dex_cache, + const SirtRef<mirror::ClassLoader>& class_loader, const DexCompilationUnit* mUnit) { + DCHECK(dex_cache->GetDexFile() == mUnit->GetDexFile()); + DCHECK(class_loader.get() == soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); + const DexFile::MethodId& referrer_method_id = + mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex()); + mirror::Class* referrer_class = mUnit->GetClassLinker()->ResolveType( + *mUnit->GetDexFile(), referrer_method_id.class_idx_, dex_cache, class_loader); + DCHECK_EQ(referrer_class == nullptr, soa.Self()->IsExceptionPending()); + if (UNLIKELY(referrer_class == nullptr)) { + // Clean up any exception left by type resolution. + soa.Self()->ClearException(); + } + return referrer_class; +} + +inline mirror::ArtField* CompilerDriver::ResolveField( + ScopedObjectAccess& soa, const SirtRef<mirror::DexCache>& dex_cache, + const SirtRef<mirror::ClassLoader>& class_loader, const DexCompilationUnit* mUnit, + uint32_t field_idx, bool is_static) { + DCHECK(dex_cache->GetDexFile() == mUnit->GetDexFile()); + DCHECK(class_loader.get() == soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); + mirror::ArtField* resolved_field = mUnit->GetClassLinker()->ResolveField( + *mUnit->GetDexFile(), field_idx, dex_cache, class_loader, is_static); + DCHECK_EQ(resolved_field == nullptr, soa.Self()->IsExceptionPending()); + if (UNLIKELY(resolved_field == nullptr)) { + // Clean up any exception left by type resolution. + soa.Self()->ClearException(); + return nullptr; + } + if (UNLIKELY(resolved_field->IsStatic() != is_static)) { + // ClassLinker can return a field of the wrong kind directly from the DexCache. + // Silently return nullptr on such incompatible class change. + return nullptr; + } + return resolved_field; +} + +inline void CompilerDriver::GetResolvedFieldDexFileLocation( + mirror::ArtField* resolved_field, const DexFile** declaring_dex_file, + uint16_t* declaring_class_idx, uint16_t* declaring_field_idx) { + mirror::Class* declaring_class = resolved_field->GetDeclaringClass(); + *declaring_dex_file = declaring_class->GetDexCache()->GetDexFile(); + *declaring_class_idx = declaring_class->GetDexTypeIndex(); + *declaring_field_idx = resolved_field->GetDexFieldIndex(); +} + +inline bool CompilerDriver::IsFieldVolatile(mirror::ArtField* field) { + return field->IsVolatile(); +} + +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) { + 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); +} + +inline std::pair<bool, bool> CompilerDriver::IsFastStaticField( + mirror::DexCache* dex_cache, mirror::Class* referrer_class, + mirror::ArtField* resolved_field, uint16_t field_idx, MemberOffset* field_offset, + uint32_t* storage_index, bool* is_referrers_class, bool* is_initialized) { + DCHECK(resolved_field->IsStatic()); + if (LIKELY(referrer_class != nullptr)) { + mirror::Class* fields_class = resolved_field->GetDeclaringClass(); + if (fields_class == referrer_class) { + *field_offset = resolved_field->GetOffset(); + *storage_index = fields_class->GetDexTypeIndex(); + *is_referrers_class = true; // implies no worrying about class initialization + *is_initialized = true; + return std::make_pair(true, true); + } + if (referrer_class->CanAccessResolvedField(fields_class, resolved_field, + dex_cache, field_idx)) { + // We have the resolved field, we must make it into a index for the referrer + // in its static storage (which may fail if it doesn't have a slot for it) + // TODO: for images we can elide the static storage base null check + // if we know there's a non-null entry in the image + const DexFile* dex_file = dex_cache->GetDexFile(); + uint32_t storage_idx = DexFile::kDexNoIndex; + if (LIKELY(fields_class->GetDexCache() == dex_cache)) { + // common case where the dex cache of both the referrer and the field are the same, + // no need to search the dex file + storage_idx = fields_class->GetDexTypeIndex(); + } else { + // Search dex file for localized ssb index, may fail if field's class is a parent + // of the class mentioned in the dex file and there is no dex cache entry. + const DexFile::StringId* string_id = + dex_file->FindStringId(FieldHelper(resolved_field).GetDeclaringClassDescriptor()); + if (string_id != nullptr) { + const DexFile::TypeId* type_id = + dex_file->FindTypeId(dex_file->GetIndexForStringId(*string_id)); + if (type_id != nullptr) { + // medium path, needs check of static storage base being initialized + storage_idx = dex_file->GetIndexForTypeId(*type_id); + } + } + } + if (storage_idx != DexFile::kDexNoIndex) { + *field_offset = resolved_field->GetOffset(); + *storage_index = storage_idx; + *is_referrers_class = false; + *is_initialized = fields_class->IsInitialized() && + CanAssumeTypeIsPresentInDexCache(*dex_file, storage_idx); + return std::make_pair(true, !resolved_field->IsFinal()); + } + } + } + // Conservative defaults. + *field_offset = MemberOffset(0u); + *storage_index = DexFile::kDexNoIndex; + *is_referrers_class = false; + *is_initialized = false; + return std::make_pair(false, false); +} + +} // namespace art + +#endif // ART_COMPILER_DRIVER_COMPILER_DRIVER_INL_H_ diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 1b284de9cc..501ea7c130 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -26,11 +26,13 @@ #include "base/timing_logger.h" #include "class_linker.h" #include "compiler_backend.h" +#include "compiler_driver-inl.h" #include "dex_compilation_unit.h" #include "dex_file-inl.h" #include "dex/verification_results.h" #include "dex/verified_method.h" #include "dex/quick/dex_file_method_inliner.h" +#include "driver/compiler_options.h" #include "jni_internal.h" #include "object_utils.h" #include "runtime.h" @@ -323,10 +325,12 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, compiler_enable_auto_elf_loading_(NULL), compiler_get_method_code_addr_(NULL), support_boot_image_fixup_(instruction_set != kMips), + cfi_info_(nullptr), dedupe_code_("dedupe code"), dedupe_mapping_table_("dedupe mapping table"), dedupe_vmap_table_("dedupe vmap table"), - dedupe_gc_map_("dedupe gc map") { + dedupe_gc_map_("dedupe gc map"), + dedupe_cfi_info_("dedupe cfi info") { DCHECK(compiler_options_ != nullptr); DCHECK(verification_results_ != nullptr); DCHECK(method_inliner_map_ != nullptr); @@ -341,6 +345,11 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, if (!image_) { CHECK(image_classes_.get() == NULL); } + + // Are we generating CFI information? + if (compiler_options->GetGenerateGDBInformation()) { + cfi_info_.reset(compiler_backend_->GetCallFrameInformationInitialization(*this)); + } } std::vector<uint8_t>* CompilerDriver::DeduplicateCode(const std::vector<uint8_t>& code) { @@ -359,6 +368,13 @@ std::vector<uint8_t>* CompilerDriver::DeduplicateGCMap(const std::vector<uint8_t return dedupe_gc_map_.Add(Thread::Current(), code); } +std::vector<uint8_t>* CompilerDriver::DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info) { + if (cfi_info == nullptr) { + return nullptr; + } + return dedupe_cfi_info_.Add(Thread::Current(), *cfi_info); +} + CompilerDriver::~CompilerDriver() { Thread* self = Thread::Current(); { @@ -441,11 +457,11 @@ const std::vector<uint8_t>* CompilerDriver::CreateQuickToInterpreterBridge() con void CompilerDriver::CompileAll(jobject class_loader, const std::vector<const DexFile*>& dex_files, - TimingLogger& timings) { + TimingLogger* timings) { DCHECK(!Runtime::Current()->IsStarted()); UniquePtr<ThreadPool> thread_pool(new ThreadPool("Compiler driver thread pool", thread_count_ - 1)); - PreCompile(class_loader, dex_files, *thread_pool.get(), timings); - Compile(class_loader, dex_files, *thread_pool.get(), timings); + PreCompile(class_loader, dex_files, thread_pool.get(), timings); + Compile(class_loader, dex_files, thread_pool.get(), timings); if (dump_stats_) { stats_->Dump(); } @@ -483,7 +499,7 @@ static DexToDexCompilationLevel GetDexToDexCompilationlevel( } } -void CompilerDriver::CompileOne(mirror::ArtMethod* method, TimingLogger& timings) { +void CompilerDriver::CompileOne(mirror::ArtMethod* method, TimingLogger* timings) { DCHECK(!Runtime::Current()->IsStarted()); Thread* self = Thread::Current(); jobject jclass_loader; @@ -510,7 +526,7 @@ void CompilerDriver::CompileOne(mirror::ArtMethod* method, TimingLogger& timings dex_files.push_back(dex_file); UniquePtr<ThreadPool> thread_pool(new ThreadPool("Compiler driver thread pool", 0U)); - PreCompile(jclass_loader, dex_files, *thread_pool.get(), timings); + PreCompile(jclass_loader, dex_files, thread_pool.get(), timings); // Can we run DEX-to-DEX compiler on this class ? DexToDexCompilationLevel dex_to_dex_compilation_level = kDontDexToDexCompile; @@ -531,7 +547,7 @@ void CompilerDriver::CompileOne(mirror::ArtMethod* method, TimingLogger& timings } void CompilerDriver::Resolve(jobject class_loader, const std::vector<const DexFile*>& dex_files, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool* thread_pool, TimingLogger* timings) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); @@ -540,7 +556,7 @@ void CompilerDriver::Resolve(jobject class_loader, const std::vector<const DexFi } void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool* thread_pool, TimingLogger* timings) { LoadImageClasses(timings); Resolve(class_loader, dex_files, thread_pool, timings); @@ -625,13 +641,13 @@ static bool RecordImageClassesVisitor(mirror::Class* klass, void* arg) } // Make a list of descriptors for classes to include in the image -void CompilerDriver::LoadImageClasses(TimingLogger& timings) +void CompilerDriver::LoadImageClasses(TimingLogger* timings) LOCKS_EXCLUDED(Locks::mutator_lock_) { if (!IsImage()) { return; } - timings.NewSplit("LoadImageClasses"); + timings->NewSplit("LoadImageClasses"); // Make a first class to load all classes explicitly listed in the file Thread* self = Thread::Current(); ScopedObjectAccess soa(self); @@ -713,9 +729,9 @@ void CompilerDriver::FindClinitImageClassesCallback(mirror::Object* object, void MaybeAddToImageClasses(object->GetClass(), compiler_driver->image_classes_.get()); } -void CompilerDriver::UpdateImageClasses(TimingLogger& timings) { +void CompilerDriver::UpdateImageClasses(TimingLogger* timings) { if (IsImage()) { - timings.NewSplit("UpdateImageClasses"); + timings->NewSplit("UpdateImageClasses"); // Update image_classes_ with classes for objects created by <clinit> methods. Thread* self = Thread::Current(); @@ -886,6 +902,24 @@ bool CompilerDriver::CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_i } } +void CompilerDriver::ProcessedInstanceField(bool resolved) { + if (!resolved) { + stats_->UnresolvedInstanceField(); + } else { + stats_->ResolvedInstanceField(); + } +} + +void CompilerDriver::ProcessedStaticField(bool resolved, bool local) { + if (!resolved) { + stats_->UnresolvedStaticField(); + } else if (local) { + stats_->ResolvedLocalStaticField(); + } else { + stats_->ResolvedStaticField(); + } +} + static mirror::Class* ComputeCompilingMethodsClass(ScopedObjectAccess& soa, SirtRef<mirror::DexCache>& dex_cache, const DexCompilationUnit* mUnit) @@ -903,15 +937,6 @@ static mirror::Class* ComputeCompilingMethodsClass(ScopedObjectAccess& soa, dex_cache, class_loader); } -static mirror::ArtField* ComputeFieldReferencedFromCompilingMethod( - ScopedObjectAccess& soa, const DexCompilationUnit* mUnit, uint32_t field_idx, bool is_static) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SirtRef<mirror::DexCache> dex_cache(soa.Self(), mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); - SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); - return mUnit->GetClassLinker()->ResolveField(*mUnit->GetDexFile(), field_idx, dex_cache, - class_loader, is_static); -} - static mirror::ArtMethod* ComputeMethodReferencedFromCompilingMethod(ScopedObjectAccess& soa, const DexCompilationUnit* mUnit, uint32_t method_idx, @@ -947,117 +972,80 @@ bool CompilerDriver::ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put, } bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, - bool is_put, int* field_offset, bool* is_volatile) { + bool is_put, MemberOffset* field_offset, + bool* is_volatile) { ScopedObjectAccess soa(Thread::Current()); - // Conservative defaults. - *field_offset = -1; - *is_volatile = true; - // Try to resolve field and ignore if an Incompatible Class Change Error (ie is static). - mirror::ArtField* resolved_field = - ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx, false); - if (resolved_field != NULL && !resolved_field->IsStatic()) { - SirtRef<mirror::DexCache> dex_cache(soa.Self(), - resolved_field->GetDeclaringClass()->GetDexCache()); - mirror::Class* referrer_class = - ComputeCompilingMethodsClass(soa, dex_cache, mUnit); - if (referrer_class != NULL) { - mirror::Class* fields_class = resolved_field->GetDeclaringClass(); - bool access_ok = referrer_class->CanAccessResolvedField(fields_class, resolved_field, - dex_cache.get(), field_idx); - bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal() && - fields_class != referrer_class; - if (access_ok && !is_write_to_final_from_wrong_class) { - *field_offset = resolved_field->GetOffset().Int32Value(); - *is_volatile = resolved_field->IsVolatile(); - stats_->ResolvedInstanceField(); - return true; // Fast path. - } - } - } - // Clean up any exception left by field/type resolution - if (soa.Self()->IsExceptionPending()) { - soa.Self()->ClearException(); + // Try to resolve the field and compiling method's class. + mirror::ArtField* resolved_field; + mirror::Class* referrer_class; + mirror::DexCache* dex_cache; + { + SirtRef<mirror::DexCache> dex_cache_sirt(soa.Self(), + mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); + SirtRef<mirror::ClassLoader> class_loader_sirt(soa.Self(), + soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); + SirtRef<mirror::ArtField> resolved_field_sirt(soa.Self(), + ResolveField(soa, dex_cache_sirt, class_loader_sirt, mUnit, field_idx, false)); + referrer_class = (resolved_field_sirt.get() != nullptr) + ? ResolveCompilingMethodsClass(soa, dex_cache_sirt, class_loader_sirt, mUnit) : nullptr; + resolved_field = resolved_field_sirt.get(); + dex_cache = dex_cache_sirt.get(); } - stats_->UnresolvedInstanceField(); - return false; // Incomplete knowledge needs slow path. + bool result = 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; + } + if (!result) { + // Conservative defaults. + *is_volatile = true; + *field_offset = MemberOffset(static_cast<size_t>(-1)); + } + ProcessedInstanceField(result); + return result; } bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, - bool is_put, int* field_offset, int* storage_index, - bool* is_referrers_class, bool* is_volatile, - bool* is_initialized) { + bool is_put, MemberOffset* field_offset, + uint32_t* storage_index, bool* is_referrers_class, + bool* is_volatile, bool* is_initialized) { ScopedObjectAccess soa(Thread::Current()); - // Conservative defaults. - *field_offset = -1; - *storage_index = -1; - *is_referrers_class = false; - *is_volatile = true; - *is_initialized = false; - // Try to resolve field and ignore if an Incompatible Class Change Error (ie isn't static). - mirror::ArtField* resolved_field = - ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx, true); - if (resolved_field != NULL && resolved_field->IsStatic()) { - SirtRef<mirror::DexCache> dex_cache(soa.Self(), resolved_field->GetDeclaringClass()->GetDexCache()); - mirror::Class* referrer_class = - ComputeCompilingMethodsClass(soa, dex_cache, mUnit); - if (referrer_class != NULL) { - mirror::Class* fields_class = resolved_field->GetDeclaringClass(); - if (fields_class == referrer_class) { - *is_referrers_class = true; // implies no worrying about class initialization - *is_initialized = true; - *field_offset = resolved_field->GetOffset().Int32Value(); - *is_volatile = resolved_field->IsVolatile(); - stats_->ResolvedLocalStaticField(); - return true; // fast path - } else { - bool access_ok = referrer_class->CanAccessResolvedField(fields_class, resolved_field, - dex_cache.get(), field_idx); - bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal(); - if (access_ok && !is_write_to_final_from_wrong_class) { - // We have the resolved field, we must make it into a index for the referrer - // in its static storage (which may fail if it doesn't have a slot for it) - // TODO: for images we can elide the static storage base null check - // if we know there's a non-null entry in the image - mirror::DexCache* dex_cache = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile()); - if (fields_class->GetDexCache() == dex_cache) { - // common case where the dex cache of both the referrer and the field are the same, - // no need to search the dex file - *storage_index = fields_class->GetDexTypeIndex(); - *field_offset = resolved_field->GetOffset().Int32Value(); - *is_volatile = resolved_field->IsVolatile(); - *is_initialized = fields_class->IsInitialized() && - CanAssumeTypeIsPresentInDexCache(*mUnit->GetDexFile(), *storage_index); - stats_->ResolvedStaticField(); - return true; - } - // Search dex file for localized ssb index, may fail if field's class is a parent - // of the class mentioned in the dex file and there is no dex cache entry. - const DexFile::StringId* string_id = - mUnit->GetDexFile()->FindStringId(FieldHelper(resolved_field).GetDeclaringClassDescriptor()); - if (string_id != NULL) { - const DexFile::TypeId* type_id = - mUnit->GetDexFile()->FindTypeId(mUnit->GetDexFile()->GetIndexForStringId(*string_id)); - if (type_id != NULL) { - // medium path, needs check of static storage base being initialized - *storage_index = mUnit->GetDexFile()->GetIndexForTypeId(*type_id); - *field_offset = resolved_field->GetOffset().Int32Value(); - *is_volatile = resolved_field->IsVolatile(); - *is_initialized = fields_class->IsInitialized() && - CanAssumeTypeIsPresentInDexCache(*mUnit->GetDexFile(), *storage_index); - stats_->ResolvedStaticField(); - return true; - } - } - } - } - } - } - // Clean up any exception left by field/type resolution - if (soa.Self()->IsExceptionPending()) { - soa.Self()->ClearException(); + // Try to resolve the field and compiling method's class. + mirror::ArtField* resolved_field; + mirror::Class* referrer_class; + mirror::DexCache* dex_cache; + { + SirtRef<mirror::DexCache> dex_cache_sirt(soa.Self(), + mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); + SirtRef<mirror::ClassLoader> class_loader_sirt(soa.Self(), + soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); + SirtRef<mirror::ArtField> resolved_field_sirt(soa.Self(), + ResolveField(soa, dex_cache_sirt, class_loader_sirt, mUnit, field_idx, true)); + referrer_class = (resolved_field_sirt.get() != nullptr) + ? ResolveCompilingMethodsClass(soa, dex_cache_sirt, class_loader_sirt, mUnit) : nullptr; + resolved_field = resolved_field_sirt.get(); + dex_cache = dex_cache_sirt.get(); } - stats_->UnresolvedStaticField(); - return false; // Incomplete knowledge needs slow path. + bool result = false; + if (resolved_field != nullptr && referrer_class != nullptr) { + *is_volatile = IsFieldVolatile(resolved_field); + std::pair<bool, bool> fast_path = IsFastStaticField( + dex_cache, referrer_class, resolved_field, field_idx, field_offset, + storage_index, is_referrers_class, is_initialized); + result = is_put ? fast_path.second : fast_path.first; + } + if (!result) { + // Conservative defaults. + *is_volatile = true; + *field_offset = MemberOffset(static_cast<size_t>(-1)); + *storage_index = -1; + *is_referrers_class = false; + *is_initialized = false; + } + ProcessedStaticField(result, *is_referrers_class); + return result; } void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type, @@ -1368,13 +1356,13 @@ class ParallelCompilationManager { jobject class_loader, CompilerDriver* compiler, const DexFile* dex_file, - ThreadPool& thread_pool) + ThreadPool* thread_pool) : index_(0), class_linker_(class_linker), class_loader_(class_loader), compiler_(compiler), dex_file_(dex_file), - thread_pool_(&thread_pool) {} + thread_pool_(thread_pool) {} ClassLinker* GetClassLinker() const { CHECK(class_linker_ != NULL); @@ -1628,7 +1616,7 @@ static void ResolveType(const ParallelCompilationManager* manager, size_t type_i } void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool* thread_pool, TimingLogger* timings) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // TODO: we could resolve strings here, although the string table is largely filled with class @@ -1638,16 +1626,16 @@ void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_fil if (IsImage()) { // For images we resolve all types, such as array, whereas for applications just those with // classdefs are resolved by ResolveClassFieldsAndMethods. - timings.NewSplit("Resolve Types"); + timings->NewSplit("Resolve Types"); context.ForAll(0, dex_file.NumTypeIds(), ResolveType, thread_count_); } - timings.NewSplit("Resolve MethodsAndFields"); + timings->NewSplit("Resolve MethodsAndFields"); context.ForAll(0, dex_file.NumClassDefs(), ResolveClassFieldsAndMethods, thread_count_); } void CompilerDriver::Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool* thread_pool, TimingLogger* timings) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); @@ -1702,8 +1690,8 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ } void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, TimingLogger& timings) { - timings.NewSplit("Verify Dex File"); + ThreadPool* thread_pool, TimingLogger* timings) { + timings->NewSplit("Verify Dex File"); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool); context.ForAll(0, dex_file.NumClassDefs(), VerifyClass, thread_count_); @@ -1805,8 +1793,8 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl } void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, TimingLogger& timings) { - timings.NewSplit("InitializeNoClinit"); + ThreadPool* thread_pool, TimingLogger* timings) { + timings->NewSplit("InitializeNoClinit"); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ParallelCompilationManager context(class_linker, jni_class_loader, this, &dex_file, thread_pool); size_t thread_count; @@ -1825,7 +1813,7 @@ void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& void CompilerDriver::InitializeClasses(jobject class_loader, const std::vector<const DexFile*>& dex_files, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool* thread_pool, TimingLogger* timings) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); @@ -1834,7 +1822,7 @@ void CompilerDriver::InitializeClasses(jobject class_loader, } void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool* thread_pool, TimingLogger* timings) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); @@ -1916,8 +1904,8 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz } void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, TimingLogger& timings) { - timings.NewSplit("Compile Dex File"); + ThreadPool* thread_pool, TimingLogger* timings) { + timings->NewSplit("Compile Dex File"); ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this, &dex_file, thread_pool); context.ForAll(0, dex_file.NumClassDefs(), CompilerDriver::CompileClass, thread_count_); @@ -2037,38 +2025,38 @@ bool CompilerDriver::RequiresConstructorBarrier(Thread* self, const DexFile* dex bool CompilerDriver::WriteElf(const std::string& android_root, bool is_host, const std::vector<const art::DexFile*>& dex_files, - OatWriter& oat_writer, + OatWriter* oat_writer, art::File* file) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return compiler_backend_->WriteElf(file, oat_writer, dex_files, android_root, is_host, *this); } void CompilerDriver::InstructionSetToLLVMTarget(InstructionSet instruction_set, - std::string& target_triple, - std::string& target_cpu, - std::string& target_attr) { + std::string* target_triple, + std::string* target_cpu, + std::string* target_attr) { switch (instruction_set) { case kThumb2: - target_triple = "thumb-none-linux-gnueabi"; - target_cpu = "cortex-a9"; - target_attr = "+thumb2,+neon,+neonfp,+vfp3,+db"; + *target_triple = "thumb-none-linux-gnueabi"; + *target_cpu = "cortex-a9"; + *target_attr = "+thumb2,+neon,+neonfp,+vfp3,+db"; break; case kArm: - target_triple = "armv7-none-linux-gnueabi"; + *target_triple = "armv7-none-linux-gnueabi"; // TODO: Fix for Nexus S. - target_cpu = "cortex-a9"; + *target_cpu = "cortex-a9"; // TODO: Fix for Xoom. - target_attr = "+v7,+neon,+neonfp,+vfp3,+db"; + *target_attr = "+v7,+neon,+neonfp,+vfp3,+db"; break; case kX86: - target_triple = "i386-pc-linux-gnu"; - target_attr = ""; + *target_triple = "i386-pc-linux-gnu"; + *target_attr = ""; break; case kMips: - target_triple = "mipsel-unknown-linux"; - target_attr = "mips32r2"; + *target_triple = "mipsel-unknown-linux"; + *target_attr = "mips32r2"; break; default: diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 377eb6fa34..57c2908bfa 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -48,9 +48,10 @@ class AOTCompilationStats; class CompilerOptions; class DexCompilationUnit; class DexFileToMethodInlinerMap; -class InlineIGetIPutData; +struct InlineIGetIPutData; class OatWriter; class ParallelCompilationManager; +class ScopedObjectAccess; class TimingLogger; class VerificationResults; class VerifiedMethod; @@ -108,11 +109,11 @@ class CompilerDriver { ~CompilerDriver(); void CompileAll(jobject class_loader, const std::vector<const DexFile*>& dex_files, - TimingLogger& timings) + TimingLogger* timings) LOCKS_EXCLUDED(Locks::mutator_lock_); // Compile a single Method. - void CompileOne(mirror::ArtMethod* method, TimingLogger& timings) + void CompileOne(mirror::ArtMethod* method, TimingLogger* timings) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); VerificationResults* GetVerificationResults() const { @@ -123,16 +124,15 @@ class CompilerDriver { return method_inliner_map_; } - const InstructionSet& GetInstructionSet() const { + InstructionSet GetInstructionSet() const { return instruction_set_; } - const InstructionSetFeatures& GetInstructionSetFeatures() const { + InstructionSetFeatures GetInstructionSetFeatures() const { return instruction_set_features_; } const CompilerOptions& GetCompilerOptions() const { - DCHECK(compiler_options_ != nullptr); return *compiler_options_; } @@ -204,6 +204,53 @@ class CompilerDriver { bool* is_type_initialized, bool* use_direct_type_ptr, uintptr_t* direct_type_ptr); + // Get the DexCache for the + mirror::DexCache* GetDexCache(const DexCompilationUnit* mUnit) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + mirror::ClassLoader* GetClassLoader(ScopedObjectAccess& soa, const DexCompilationUnit* mUnit) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Resolve compiling method's class. Returns nullptr on failure. + mirror::Class* ResolveCompilingMethodsClass( + ScopedObjectAccess& soa, const SirtRef<mirror::DexCache>& dex_cache, + const SirtRef<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, const SirtRef<mirror::DexCache>& dex_cache, + const SirtRef<mirror::ClassLoader>& class_loader, const DexCompilationUnit* mUnit, + uint32_t field_idx, bool is_static) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Get declaration location of a resolved field. + void GetResolvedFieldDexFileLocation( + mirror::ArtField* resolved_field, const DexFile** declaring_dex_file, + uint16_t* declaring_class_idx, uint16_t* declaring_field_idx) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + bool IsFieldVolatile(mirror::ArtField* field) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // 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) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Can we fast-path an SGET/SPUT access to a static field? If yes, compute the field offset, + // the type index of the declaring class in the referrer's dex file and whether the declaring + // class is the referrer's class or at least can be assumed to be initialized. + std::pair<bool, bool> IsFastStaticField( + mirror::DexCache* dex_cache, mirror::Class* referrer_class, + mirror::ArtField* resolved_field, uint16_t field_idx, MemberOffset* field_offset, + uint32_t* storage_index, bool* is_referrers_class, bool* is_initialized) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void ProcessedInstanceField(bool resolved); + void ProcessedStaticField(bool resolved, bool local); + // Can we fast path instance field access in a verified accessor? // If yes, computes field's offset and volatility and whether the method is static or not. static bool ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put, @@ -213,13 +260,13 @@ class CompilerDriver { // Can we fast path instance field access? Computes field's offset and volatility. bool ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put, - int* field_offset, bool* is_volatile) + MemberOffset* field_offset, bool* is_volatile) LOCKS_EXCLUDED(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, - int* field_offset, int* storage_index, + MemberOffset* field_offset, uint32_t* storage_index, bool* is_referrers_class, bool* is_volatile, bool* is_initialized) LOCKS_EXCLUDED(Locks::mutator_lock_); @@ -275,21 +322,21 @@ class CompilerDriver { support_boot_image_fixup_ = support_boot_image_fixup; } - ArenaPool& GetArenaPool() { - return arena_pool_; + ArenaPool* GetArenaPool() { + return &arena_pool_; } bool WriteElf(const std::string& android_root, bool is_host, const std::vector<const DexFile*>& dex_files, - OatWriter& oat_writer, + OatWriter* oat_writer, File* file); - // TODO: move to a common home for llvm helpers once quick/portable are merged + // TODO: move to a common home for llvm helpers once quick/portable are merged. static void InstructionSetToLLVMTarget(InstructionSet instruction_set, - std::string& target_triple, - std::string& target_cpu, - std::string& target_attr); + std::string* target_triple, + std::string* target_cpu, + std::string* target_attr); void SetCompilerContext(void* compiler_context) { compiler_context_ = compiler_context; @@ -310,8 +357,8 @@ class CompilerDriver { return dump_passes_; } - CumulativeLogger& GetTimingsLogger() const { - return *timings_logger_; + CumulativeLogger* GetTimingsLogger() const { + return timings_logger_; } class PatchInformation { @@ -494,6 +541,15 @@ class CompilerDriver { std::vector<uint8_t>* DeduplicateMappingTable(const std::vector<uint8_t>& code); std::vector<uint8_t>* DeduplicateVMapTable(const std::vector<uint8_t>& code); std::vector<uint8_t>* DeduplicateGCMap(const std::vector<uint8_t>& code); + std::vector<uint8_t>* DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info); + + /* + * @brief return the pointer to the Call Frame Information. + * @return pointer to call frame information for this compilation. + */ + std::vector<uint8_t>* GetCallFrameInformation() const { + return cfi_info_.get(); + } private: // Compute constant code and method pointers when possible @@ -507,43 +563,42 @@ class CompilerDriver { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files, - ThreadPool& thread_pool, TimingLogger& timings) + ThreadPool* thread_pool, TimingLogger* timings) LOCKS_EXCLUDED(Locks::mutator_lock_); - void LoadImageClasses(TimingLogger& timings); + void LoadImageClasses(TimingLogger* timings); // Attempt to resolve all type, methods, fields, and strings // referenced from code in the dex file following PathClassLoader // ordering semantics. void Resolve(jobject class_loader, const std::vector<const DexFile*>& dex_files, - ThreadPool& thread_pool, TimingLogger& timings) + ThreadPool* thread_pool, TimingLogger* timings) LOCKS_EXCLUDED(Locks::mutator_lock_); void ResolveDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, TimingLogger& timings) + ThreadPool* thread_pool, TimingLogger* timings) LOCKS_EXCLUDED(Locks::mutator_lock_); void Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files, - ThreadPool& thread_pool, TimingLogger& timings); + ThreadPool* thread_pool, TimingLogger* timings); void VerifyDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, TimingLogger& timings) + ThreadPool* thread_pool, TimingLogger* timings) LOCKS_EXCLUDED(Locks::mutator_lock_); void InitializeClasses(jobject class_loader, const std::vector<const DexFile*>& dex_files, - ThreadPool& thread_pool, TimingLogger& timings) + ThreadPool* thread_pool, TimingLogger* timings) LOCKS_EXCLUDED(Locks::mutator_lock_); void InitializeClasses(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, TimingLogger& timings) + ThreadPool* thread_pool, TimingLogger* timings) LOCKS_EXCLUDED(Locks::mutator_lock_, compiled_classes_lock_); - void UpdateImageClasses(TimingLogger& timings) - LOCKS_EXCLUDED(Locks::mutator_lock_); + void UpdateImageClasses(TimingLogger* timings) LOCKS_EXCLUDED(Locks::mutator_lock_); static void FindClinitImageClassesCallback(mirror::Object* object, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files, - ThreadPool& thread_pool, TimingLogger& timings); + ThreadPool* thread_pool, TimingLogger* timings); void CompileDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, TimingLogger& timings) + ThreadPool* thread_pool, TimingLogger* timings) LOCKS_EXCLUDED(Locks::mutator_lock_); void CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, @@ -627,6 +682,9 @@ class CompilerDriver { bool support_boot_image_fixup_; + // Call Frame Information, which might be generated to help stack tracebacks. + UniquePtr<std::vector<uint8_t> > cfi_info_; + // DeDuplication data structures, these own the corresponding byte arrays. class DedupeHashFunc { public: @@ -665,6 +723,7 @@ class CompilerDriver { DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_mapping_table_; DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_vmap_table_; DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_gc_map_; + DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_cfi_info_; DISALLOW_COPY_AND_ASSIGN(CompilerDriver); }; diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index ec0a8bdc63..2b3af6281f 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -21,7 +21,7 @@ #include "UniquePtr.h" #include "class_linker.h" -#include "common_test.h" +#include "common_compiler_test.h" #include "dex_file.h" #include "gc/heap.h" #include "mirror/art_method-inl.h" @@ -33,14 +33,14 @@ namespace art { -class CompilerDriverTest : public CommonTest { +class CompilerDriverTest : public CommonCompilerTest { protected: void CompileAll(jobject class_loader) LOCKS_EXCLUDED(Locks::mutator_lock_) { TimingLogger timings("CompilerDriverTest::CompileAll", false, false); timings.StartSplit("CompileAll"); compiler_driver_->CompileAll(class_loader, Runtime::Current()->GetCompileTimeClassPath(class_loader), - timings); + &timings); MakeAllExecutable(class_loader); timings.EndSplit(); } diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 9f6745b015..39738ab049 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -43,7 +43,8 @@ class CompilerOptions { large_method_threshold_(kDefaultLargeMethodThreshold), small_method_threshold_(kDefaultSmallMethodThreshold), tiny_method_threshold_(kDefaultTinyMethodThreshold), - num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold) + num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold), + generate_gdb_information_(false) #ifdef ART_SEA_IR_MODE , sea_ir_mode_(false) #endif @@ -54,7 +55,8 @@ class CompilerOptions { size_t large_method_threshold, size_t small_method_threshold, size_t tiny_method_threshold, - size_t num_dex_methods_threshold + size_t num_dex_methods_threshold, + bool generate_gdb_information #ifdef ART_SEA_IR_MODE , bool sea_ir_mode #endif @@ -64,7 +66,8 @@ class CompilerOptions { large_method_threshold_(large_method_threshold), small_method_threshold_(small_method_threshold), tiny_method_threshold_(tiny_method_threshold), - num_dex_methods_threshold_(num_dex_methods_threshold) + num_dex_methods_threshold_(num_dex_methods_threshold), + generate_gdb_information_(generate_gdb_information) #ifdef ART_SEA_IR_MODE , sea_ir_mode_(sea_ir_mode) #endif @@ -118,6 +121,10 @@ class CompilerOptions { bool GetSeaIrMode(); #endif + bool GetGenerateGDBInformation() const { + return generate_gdb_information_; + } + private: CompilerFilter compiler_filter_; size_t huge_method_threshold_; @@ -125,6 +132,7 @@ class CompilerOptions { size_t small_method_threshold_; size_t tiny_method_threshold_; size_t num_dex_methods_threshold_; + bool generate_gdb_information_; #ifdef ART_SEA_IR_MODE bool sea_ir_mode_; diff --git a/compiler/elf_writer.cc b/compiler/elf_writer.cc index 6db3fa1a11..ccc26a1baf 100644 --- a/compiler/elf_writer.cc +++ b/compiler/elf_writer.cc @@ -30,12 +30,7 @@ namespace art { -ElfWriter::ElfWriter(const CompilerDriver& driver, File* elf_file) - : compiler_driver_(&driver), elf_file_(elf_file) {} - -ElfWriter::~ElfWriter() {} - -Elf32_Addr ElfWriter::GetOatDataAddress(ElfFile* elf_file) { +uint32_t ElfWriter::GetOatDataAddress(ElfFile* elf_file) { Elf32_Addr oatdata_address = elf_file->FindSymbolAddress(SHT_DYNSYM, "oatdata", false); diff --git a/compiler/elf_writer.h b/compiler/elf_writer.h index 99dfc56d49..3610d1a8b2 100644 --- a/compiler/elf_writer.h +++ b/compiler/elf_writer.h @@ -23,7 +23,6 @@ #include <vector> #include "base/macros.h" -#include "elf_utils.h" #include "os.h" namespace art { @@ -42,21 +41,23 @@ class ElfWriter { size_t& oat_data_offset); // Returns runtime oat_data runtime address for an opened ElfFile. - static Elf32_Addr GetOatDataAddress(ElfFile* elf_file); + static uint32_t GetOatDataAddress(ElfFile* elf_file); protected: - ElfWriter(const CompilerDriver& driver, File* elf_file); - virtual ~ElfWriter(); + ElfWriter(const CompilerDriver& driver, File* elf_file) + : compiler_driver_(&driver), elf_file_(elf_file) { + } - virtual bool Write(OatWriter& oat_writer, + virtual ~ElfWriter() {} + + virtual bool Write(OatWriter* oat_writer, const std::vector<const DexFile*>& dex_files, const std::string& android_root, bool is_host) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; - // Setup by constructor - const CompilerDriver* compiler_driver_; - File* elf_file_; + const CompilerDriver* const compiler_driver_; + File* const elf_file_; }; } // namespace art diff --git a/compiler/elf_writer_mclinker.cc b/compiler/elf_writer_mclinker.cc index c7baf4f3e5..b2d3a69e74 100644 --- a/compiler/elf_writer_mclinker.cc +++ b/compiler/elf_writer_mclinker.cc @@ -16,6 +16,7 @@ #include "elf_writer_mclinker.h" +#include <llvm/Support/ELF.h> #include <llvm/Support/TargetSelect.h> #include <mcld/Environment.h> @@ -32,7 +33,6 @@ #include "class_linker.h" #include "dex_method_iterator.h" #include "driver/compiler_driver.h" -#include "elf_file.h" #include "globals.h" #include "mirror/art_method.h" #include "mirror/art_method-inl.h" @@ -44,12 +44,14 @@ namespace art { ElfWriterMclinker::ElfWriterMclinker(const CompilerDriver& driver, File* elf_file) - : ElfWriter(driver, elf_file), oat_input_(NULL) {} + : ElfWriter(driver, elf_file), oat_input_(nullptr) { +} -ElfWriterMclinker::~ElfWriterMclinker() {} +ElfWriterMclinker::~ElfWriterMclinker() { +} bool ElfWriterMclinker::Create(File* elf_file, - OatWriter& oat_writer, + OatWriter* oat_writer, const std::vector<const DexFile*>& dex_files, const std::string& android_root, bool is_host, @@ -58,29 +60,29 @@ bool ElfWriterMclinker::Create(File* elf_file, return elf_writer.Write(oat_writer, dex_files, android_root, is_host); } -bool ElfWriterMclinker::Write(OatWriter& oat_writer, +bool ElfWriterMclinker::Write(OatWriter* oat_writer, const std::vector<const DexFile*>& dex_files, const std::string& android_root, bool is_host) { std::vector<uint8_t> oat_contents; - oat_contents.reserve(oat_writer.GetSize()); + oat_contents.reserve(oat_writer->GetSize()); VectorOutputStream output_stream("oat contents", oat_contents); - CHECK(oat_writer.Write(output_stream)); - CHECK_EQ(oat_writer.GetSize(), oat_contents.size()); + CHECK(oat_writer->Write(&output_stream)); + CHECK_EQ(oat_writer->GetSize(), oat_contents.size()); Init(); AddOatInput(oat_contents); -#if defined(ART_USE_PORTABLE_COMPILER) - AddMethodInputs(dex_files); - AddRuntimeInputs(android_root, is_host); -#endif + if (kUsePortableCompiler) { + AddMethodInputs(dex_files); + AddRuntimeInputs(android_root, is_host); + } if (!Link()) { return false; } oat_contents.clear(); -#if defined(ART_USE_PORTABLE_COMPILER) - FixupOatMethodOffsets(dex_files); -#endif + if (kUsePortableCompiler) { + FixupOatMethodOffsets(dex_files); + } return true; } @@ -100,9 +102,9 @@ void ElfWriterMclinker::Init() { std::string target_cpu; std::string target_attr; CompilerDriver::InstructionSetToLLVMTarget(compiler_driver_->GetInstructionSet(), - target_triple, - target_cpu, - target_attr); + &target_triple, + &target_cpu, + &target_attr); // Based on mclinker's llvm-mcld.cpp main() and LinkerTest // @@ -236,7 +238,6 @@ void ElfWriterMclinker::AddOatInput(std::vector<uint8_t>& oat_contents) { text_section); } -#if defined(ART_USE_PORTABLE_COMPILER) void ElfWriterMclinker::AddMethodInputs(const std::vector<const DexFile*>& dex_files) { DCHECK(oat_input_ != NULL); @@ -320,7 +321,6 @@ void ElfWriterMclinker::AddRuntimeInputs(const std::string& android_root, bool i mcld::Input* libm_lib_input_input = ir_builder_->ReadInput(libm_lib, libm_lib); CHECK(libm_lib_input_input != NULL); } -#endif bool ElfWriterMclinker::Link() { // link inputs @@ -345,7 +345,6 @@ bool ElfWriterMclinker::Link() { return true; } -#if defined(ART_USE_PORTABLE_COMPILER) void ElfWriterMclinker::FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files) { std::string error_msg; UniquePtr<ElfFile> elf_file(ElfFile::Open(elf_file_, true, false, &error_msg)); @@ -409,6 +408,5 @@ uint32_t ElfWriterMclinker::FixupCompiledCodeOffset(ElfFile& elf_file, } return compiled_code_offset; } -#endif } // namespace art diff --git a/compiler/elf_writer_mclinker.h b/compiler/elf_writer_mclinker.h index 8ee7231f79..13757edecd 100644 --- a/compiler/elf_writer_mclinker.h +++ b/compiler/elf_writer_mclinker.h @@ -37,11 +37,11 @@ namespace art { class CompiledCode; -class ElfWriterMclinker : public ElfWriter { +class ElfWriterMclinker FINAL : public ElfWriter { public: // Write an ELF file. Returns true on success, false on failure. static bool Create(File* file, - OatWriter& oat_writer, + OatWriter* oat_writer, const std::vector<const DexFile*>& dex_files, const std::string& android_root, bool is_host, @@ -49,10 +49,11 @@ class ElfWriterMclinker : public ElfWriter { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); protected: - virtual bool Write(OatWriter& oat_writer, - const std::vector<const DexFile*>& dex_files, - const std::string& android_root, - bool is_host) + bool Write(OatWriter* oat_writer, + const std::vector<const DexFile*>& dex_files, + const std::string& android_root, + bool is_host) + OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: @@ -65,13 +66,11 @@ class ElfWriterMclinker : public ElfWriter { void AddCompiledCodeInput(const CompiledCode& compiled_code); void AddRuntimeInputs(const std::string& android_root, bool is_host); bool Link(); -#if defined(ART_USE_PORTABLE_COMPILER) void FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); uint32_t FixupCompiledCodeOffset(ElfFile& elf_file, - ::llvm::ELF::Elf32_Addr oatdata_address, + uint32_t oatdata_address, const CompiledCode& compiled_code); -#endif // Setup by Init() UniquePtr<mcld::LinkerConfig> linker_config_; diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index 3191374160..4b823ef5ec 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -29,13 +29,8 @@ namespace art { -ElfWriterQuick::ElfWriterQuick(const CompilerDriver& driver, File* elf_file) - : ElfWriter(driver, elf_file) {} - -ElfWriterQuick::~ElfWriterQuick() {} - bool ElfWriterQuick::Create(File* elf_file, - OatWriter& oat_writer, + OatWriter* oat_writer, const std::vector<const DexFile*>& dex_files, const std::string& android_root, bool is_host, @@ -44,7 +39,7 @@ bool ElfWriterQuick::Create(File* elf_file, return elf_writer.Write(oat_writer, dex_files, android_root, is_host); } -bool ElfWriterQuick::Write(OatWriter& oat_writer, +bool ElfWriterQuick::Write(OatWriter* oat_writer, const std::vector<const DexFile*>& dex_files_unused, const std::string& android_root_unused, bool is_host_unused) { @@ -103,6 +98,7 @@ bool ElfWriterQuick::Write(OatWriter& oat_writer, // | .rodata\0 | // | .text\0 | // | .shstrtab\0 | + // | .debug_frame\0 | // +-------------------------+ // | Elf32_Shdr NULL | // | Elf32_Shdr .dynsym | @@ -112,6 +108,9 @@ bool ElfWriterQuick::Write(OatWriter& oat_writer, // | Elf32_Shdr .rodata | // | Elf32_Shdr .dynamic | // | Elf32_Shdr .shstrtab | + // | Elf32_Shdr .debug_info | (Optional) + // | Elf32_Shdr .debug_abbrev| (Optional) + // | Elf32_Shdr .debug_frame | (Optional) // +-------------------------+ // phase 1: computing offsets @@ -197,7 +196,7 @@ bool ElfWriterQuick::Write(OatWriter& oat_writer, // .rodata uint32_t oat_data_alignment = kPageSize; uint32_t oat_data_offset = expected_offset = RoundUp(expected_offset, oat_data_alignment); - const OatHeader& oat_header = oat_writer.GetOatHeader(); + const OatHeader& oat_header = oat_writer->GetOatHeader(); CHECK(oat_header.IsValid()); uint32_t oat_data_size = oat_header.GetExecutableOffset(); expected_offset += oat_data_size; @@ -210,9 +209,9 @@ bool ElfWriterQuick::Write(OatWriter& oat_writer, uint32_t oat_exec_alignment = kPageSize; CHECK_ALIGNED(expected_offset, kPageSize); uint32_t oat_exec_offset = expected_offset = RoundUp(expected_offset, oat_exec_alignment); - uint32_t oat_exec_size = oat_writer.GetSize() - oat_data_size; + uint32_t oat_exec_size = oat_writer->GetSize() - oat_data_size; expected_offset += oat_exec_size; - CHECK_EQ(oat_data_offset + oat_writer.GetSize(), expected_offset); + CHECK_EQ(oat_data_offset + oat_writer->GetSize(), expected_offset); if (debug) { LOG(INFO) << "oat_exec_offset=" << oat_exec_offset << std::hex << " " << oat_exec_offset; LOG(INFO) << "oat_exec_size=" << oat_exec_size << std::hex << " " << oat_exec_size; @@ -264,6 +263,18 @@ bool ElfWriterQuick::Write(OatWriter& oat_writer, uint32_t shstrtab_shstrtab_offset = shstrtab.size(); shstrtab += ".shstrtab"; shstrtab += '\0'; + uint32_t shstrtab_debug_info_offset = shstrtab.size(); + shstrtab += ".debug_info"; + shstrtab += '\0'; + uint32_t shstrtab_debug_abbrev_offset = shstrtab.size(); + shstrtab += ".debug_abbrev"; + shstrtab += '\0'; + uint32_t shstrtab_debug_str_offset = shstrtab.size(); + shstrtab += ".debug_str"; + shstrtab += '\0'; + uint32_t shstrtab_debug_frame_offset = shstrtab.size(); + shstrtab += ".debug_frame"; + shstrtab += '\0'; uint32_t shstrtab_size = shstrtab.size(); expected_offset += shstrtab_size; if (debug) { @@ -271,6 +282,52 @@ bool ElfWriterQuick::Write(OatWriter& oat_writer, LOG(INFO) << "shstrtab_size=" << shstrtab_size << std::hex << " " << shstrtab_size; } + // Create debug informatin, if we have it. + bool generateDebugInformation = compiler_driver_->GetCallFrameInformation() != nullptr; + std::vector<uint8_t> dbg_info; + std::vector<uint8_t> dbg_abbrev; + std::vector<uint8_t> dbg_str; + if (generateDebugInformation) { + FillInCFIInformation(oat_writer, &dbg_info, &dbg_abbrev, &dbg_str); + } + + uint32_t shdbg_info_alignment = 1; + uint32_t shdbg_info_offset = expected_offset; + uint32_t shdbg_info_size = dbg_info.size(); + expected_offset += shdbg_info_size; + if (debug) { + LOG(INFO) << "shdbg_info_offset=" << shdbg_info_offset << std::hex << " " << shdbg_info_offset; + LOG(INFO) << "shdbg_info_size=" << shdbg_info_size << std::hex << " " << shdbg_info_size; + } + + uint32_t shdbg_abbrev_alignment = 1; + uint32_t shdbg_abbrev_offset = expected_offset; + uint32_t shdbg_abbrev_size = dbg_abbrev.size(); + expected_offset += shdbg_abbrev_size; + if (debug) { + LOG(INFO) << "shdbg_abbrev_offset=" << shdbg_abbrev_offset << std::hex << " " << shdbg_abbrev_offset; + LOG(INFO) << "shdbg_abbrev_size=" << shdbg_abbrev_size << std::hex << " " << shdbg_abbrev_size; + } + + uint32_t shdbg_frm_alignment = 4; + uint32_t shdbg_frm_offset = expected_offset = RoundUp(expected_offset, shdbg_frm_alignment); + uint32_t shdbg_frm_size = + generateDebugInformation ? compiler_driver_->GetCallFrameInformation()->size() : 0; + expected_offset += shdbg_frm_size; + if (debug) { + LOG(INFO) << "shdbg_frm_offset=" << shdbg_frm_offset << std::hex << " " << shdbg_frm_offset; + LOG(INFO) << "shdbg_frm_size=" << shdbg_frm_size << std::hex << " " << shdbg_frm_size; + } + + uint32_t shdbg_str_alignment = 1; + uint32_t shdbg_str_offset = expected_offset; + uint32_t shdbg_str_size = dbg_str.size(); + expected_offset += shdbg_str_size; + if (debug) { + LOG(INFO) << "shdbg_str_offset=" << shdbg_str_offset << std::hex << " " << shdbg_str_offset; + LOG(INFO) << "shdbg_str_size=" << shdbg_str_size << std::hex << " " << shdbg_str_size; + } + // section headers (after all sections) uint32_t shdr_alignment = sizeof(Elf32_Word); uint32_t shdr_offset = expected_offset = RoundUp(expected_offset, shdr_alignment); @@ -282,7 +339,11 @@ bool ElfWriterQuick::Write(OatWriter& oat_writer, const uint8_t SH_TEXT = 5; const uint8_t SH_DYNAMIC = 6; const uint8_t SH_SHSTRTAB = 7; - const uint8_t SH_NUM = 8; + const uint8_t SH_DBG_INFO = 8; + const uint8_t SH_DBG_ABRV = 9; + const uint8_t SH_DBG_FRM = 10; + const uint8_t SH_DBG_STR = 11; + const uint8_t SH_NUM = generateDebugInformation ? 12 : 8; uint32_t shdr_size = sizeof(Elf32_Shdr) * SH_NUM; expected_offset += shdr_size; if (debug) { @@ -559,6 +620,52 @@ bool ElfWriterQuick::Write(OatWriter& oat_writer, section_headers[SH_SHSTRTAB].sh_addralign = shstrtab_alignment; section_headers[SH_SHSTRTAB].sh_entsize = 0; + if (generateDebugInformation) { + section_headers[SH_DBG_INFO].sh_name = shstrtab_debug_info_offset; + section_headers[SH_DBG_INFO].sh_type = SHT_PROGBITS; + section_headers[SH_DBG_INFO].sh_flags = 0; + section_headers[SH_DBG_INFO].sh_addr = 0; + section_headers[SH_DBG_INFO].sh_offset = shdbg_info_offset; + section_headers[SH_DBG_INFO].sh_size = shdbg_info_size; + section_headers[SH_DBG_INFO].sh_link = 0; + section_headers[SH_DBG_INFO].sh_info = 0; + section_headers[SH_DBG_INFO].sh_addralign = shdbg_info_alignment; + section_headers[SH_DBG_INFO].sh_entsize = 0; + + section_headers[SH_DBG_ABRV].sh_name = shstrtab_debug_abbrev_offset; + section_headers[SH_DBG_ABRV].sh_type = SHT_PROGBITS; + section_headers[SH_DBG_ABRV].sh_flags = 0; + section_headers[SH_DBG_ABRV].sh_addr = 0; + section_headers[SH_DBG_ABRV].sh_offset = shdbg_abbrev_offset; + section_headers[SH_DBG_ABRV].sh_size = shdbg_abbrev_size; + section_headers[SH_DBG_ABRV].sh_link = 0; + section_headers[SH_DBG_ABRV].sh_info = 0; + section_headers[SH_DBG_ABRV].sh_addralign = shdbg_abbrev_alignment; + section_headers[SH_DBG_ABRV].sh_entsize = 0; + + section_headers[SH_DBG_FRM].sh_name = shstrtab_debug_frame_offset; + section_headers[SH_DBG_FRM].sh_type = SHT_PROGBITS; + section_headers[SH_DBG_FRM].sh_flags = 0; + section_headers[SH_DBG_FRM].sh_addr = 0; + section_headers[SH_DBG_FRM].sh_offset = shdbg_frm_offset; + section_headers[SH_DBG_FRM].sh_size = shdbg_frm_size; + section_headers[SH_DBG_FRM].sh_link = 0; + section_headers[SH_DBG_FRM].sh_info = 0; + section_headers[SH_DBG_FRM].sh_addralign = shdbg_frm_alignment; + section_headers[SH_DBG_FRM].sh_entsize = 0; + + section_headers[SH_DBG_STR].sh_name = shstrtab_debug_str_offset; + section_headers[SH_DBG_STR].sh_type = SHT_PROGBITS; + section_headers[SH_DBG_STR].sh_flags = 0; + section_headers[SH_DBG_STR].sh_addr = 0; + section_headers[SH_DBG_STR].sh_offset = shdbg_str_offset; + section_headers[SH_DBG_STR].sh_size = shdbg_str_size; + section_headers[SH_DBG_STR].sh_link = 0; + section_headers[SH_DBG_STR].sh_info = 0; + section_headers[SH_DBG_STR].sh_addralign = shdbg_str_alignment; + section_headers[SH_DBG_STR].sh_entsize = 0; + } + // phase 3: writing file // Elf32_Ehdr @@ -622,13 +729,13 @@ bool ElfWriterQuick::Write(OatWriter& oat_writer, return false; } BufferedOutputStream output_stream(new FileOutputStream(elf_file_)); - if (!oat_writer.Write(output_stream)) { + if (!oat_writer->Write(&output_stream)) { PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file_->GetPath(); return false; } // .dynamic - DCHECK_LE(oat_data_offset + oat_writer.GetSize(), dynamic_offset); + DCHECK_LE(oat_data_offset + oat_writer->GetSize(), dynamic_offset); if (static_cast<off_t>(dynamic_offset) != lseek(elf_file_->Fd(), dynamic_offset, SEEK_SET)) { PLOG(ERROR) << "Failed to seek to .dynamic offset " << dynamic_offset << " for " << elf_file_->GetPath(); @@ -651,8 +758,62 @@ bool ElfWriterQuick::Write(OatWriter& oat_writer, return false; } + if (generateDebugInformation) { + // .debug_info + DCHECK_LE(shstrtab_offset + shstrtab_size, shdbg_info_offset); + if (static_cast<off_t>(shdbg_info_offset) != lseek(elf_file_->Fd(), shdbg_info_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .shdbg_info offset " << shdbg_info_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(&dbg_info[0], shdbg_info_size)) { + PLOG(ERROR) << "Failed to write .debug_info for " << elf_file_->GetPath(); + return false; + } + + // .debug_abbrev + DCHECK_LE(shdbg_info_offset + shdbg_info_size, shdbg_abbrev_offset); + if (static_cast<off_t>(shdbg_abbrev_offset) != lseek(elf_file_->Fd(), shdbg_abbrev_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .shdbg_abbrev offset " << shdbg_abbrev_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(&dbg_abbrev[0], shdbg_abbrev_size)) { + PLOG(ERROR) << "Failed to write .debug_abbrev for " << elf_file_->GetPath(); + return false; + } + + // .debug_frame + DCHECK_LE(shdbg_abbrev_offset + shdbg_abbrev_size, shdbg_frm_offset); + if (static_cast<off_t>(shdbg_frm_offset) != lseek(elf_file_->Fd(), shdbg_frm_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .shdbg_frm offset " << shdbg_frm_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(&((*compiler_driver_->GetCallFrameInformation())[0]), shdbg_frm_size)) { + PLOG(ERROR) << "Failed to write .debug_frame for " << elf_file_->GetPath(); + return false; + } + + // .debug_str + DCHECK_LE(shdbg_frm_offset + shdbg_frm_size, shdbg_str_offset); + if (static_cast<off_t>(shdbg_str_offset) != lseek(elf_file_->Fd(), shdbg_str_offset, SEEK_SET)) { + PLOG(ERROR) << "Failed to seek to .shdbg_str offset " << shdbg_str_offset + << " for " << elf_file_->GetPath(); + return false; + } + if (!elf_file_->WriteFully(&dbg_str[0], shdbg_str_size)) { + PLOG(ERROR) << "Failed to write .debug_frame for " << elf_file_->GetPath(); + return false; + } + } + // section headers (after all sections) - DCHECK_LE(shstrtab_offset + shstrtab_size, shdr_offset); + if (generateDebugInformation) { + DCHECK_LE(shdbg_str_offset + shdbg_str_size, shdr_offset); + } else { + DCHECK_LE(shstrtab_offset + shstrtab_size, shdr_offset); + } if (static_cast<off_t>(shdr_offset) != lseek(elf_file_->Fd(), shdr_offset, SEEK_SET)) { PLOG(ERROR) << "Failed to seek to ELF section headers offset " << shdr_offset << " for " << elf_file_->GetPath(); @@ -665,6 +826,164 @@ bool ElfWriterQuick::Write(OatWriter& oat_writer, VLOG(compiler) << "ELF file written successfully: " << elf_file_->GetPath(); return true; +} // NOLINT(readability/fn_size) + +static void UpdateWord(std::vector<uint8_t>*buf, int offset, int data) { + (*buf)[offset+0] = data; + (*buf)[offset+1] = data >> 8; + (*buf)[offset+2] = data >> 16; + (*buf)[offset+3] = data >> 24; +} + +static void PushWord(std::vector<uint8_t>*buf, int data) { + buf->push_back(data & 0xff); + buf->push_back((data >> 8) & 0xff); + buf->push_back((data >> 16) & 0xff); + buf->push_back((data >> 24) & 0xff); +} + +static void PushHalf(std::vector<uint8_t>*buf, int data) { + buf->push_back(data & 0xff); + buf->push_back((data >> 8) & 0xff); +} + +// DWARF constants needed to generate CFI information. +enum { + // Tag encodings. + DW_TAG_compile_unit = 0x11, + DW_TAG_subprogram = 0X2e, + + // Attribute encodings. + DW_AT_name = 0x03, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + + // Constant encoding. + DW_CHILDREN_no = 0x00, + DW_CHILDREN_yes = 0x01, + + // Attribute form encodings. + DW_FORM_addr = 0x01, + DW_FORM_data1 = 0x0b, + DW_FORM_strp = 0x0e, + + // Language encoding. + DW_LANG_Java = 0x000b +}; + +void ElfWriterQuick::FillInCFIInformation(OatWriter* oat_writer, + std::vector<uint8_t>* dbg_info, + std::vector<uint8_t>* dbg_abbrev, + std::vector<uint8_t>* dbg_str) { + // Create the debug_abbrev section with boilerplate information. + // We only care about low_pc and high_pc right now for the compilation + // unit and methods. + + // Tag 1: Compilation unit: DW_TAG_compile_unit. + dbg_abbrev->push_back(1); + dbg_abbrev->push_back(DW_TAG_compile_unit); + + // There are children (the methods). + dbg_abbrev->push_back(DW_CHILDREN_yes); + + // DW_LANG_Java DW_FORM_data1. + dbg_abbrev->push_back(DW_AT_language); + dbg_abbrev->push_back(DW_FORM_data1); + + // DW_AT_low_pc DW_FORM_addr. + dbg_abbrev->push_back(DW_AT_low_pc); + dbg_abbrev->push_back(DW_FORM_addr); + + // DW_AT_high_pc DW_FORM_addr. + dbg_abbrev->push_back(DW_AT_high_pc); + dbg_abbrev->push_back(DW_FORM_addr); + + // End of DW_TAG_compile_unit. + PushHalf(dbg_abbrev, 0); + + // Tag 2: Compilation unit: DW_TAG_subprogram. + dbg_abbrev->push_back(2); + dbg_abbrev->push_back(DW_TAG_subprogram); + + // There are no children. + dbg_abbrev->push_back(DW_CHILDREN_no); + + // Name of the method. + dbg_abbrev->push_back(DW_AT_name); + dbg_abbrev->push_back(DW_FORM_strp); + + // DW_AT_low_pc DW_FORM_addr. + dbg_abbrev->push_back(DW_AT_low_pc); + dbg_abbrev->push_back(DW_FORM_addr); + + // DW_AT_high_pc DW_FORM_addr. + dbg_abbrev->push_back(DW_AT_high_pc); + dbg_abbrev->push_back(DW_FORM_addr); + + // End of DW_TAG_subprogram. + PushHalf(dbg_abbrev, 0); + + // Start the debug_info section with the header information + // 'unit_length' will be filled in later. + PushWord(dbg_info, 0); + + // 'version' - 3. + PushHalf(dbg_info, 3); + + // Offset into .debug_abbrev section (always 0). + PushWord(dbg_info, 0); + + // Address size: 4. + dbg_info->push_back(4); + + // Start the description for the compilation unit. + // This uses tag 1. + dbg_info->push_back(1); + + // The language is Java. + dbg_info->push_back(DW_LANG_Java); + + // Leave space for low_pc and high_pc. + int low_pc_offset = dbg_info->size(); + PushWord(dbg_info, 0); + PushWord(dbg_info, 0); + + // Walk through the information in the method table, and enter into dbg_info. + const std::vector<OatWriter::DebugInfo>& dbg = oat_writer->GetCFIMethodInfo(); + uint32_t low_pc = 0xFFFFFFFFU; + uint32_t high_pc = 0; + + for (uint32_t i = 0; i < dbg.size(); i++) { + const OatWriter::DebugInfo& info = dbg[i]; + if (info.low_pc_ < low_pc) { + low_pc = info.low_pc_; + } + if (info.high_pc_ > high_pc) { + high_pc = info.high_pc_; + } + + // Start a new TAG: subroutine (2). + dbg_info->push_back(2); + + // Enter the name into the string table (and NUL terminate). + uint32_t str_offset = dbg_str->size(); + dbg_str->insert(dbg_str->end(), info.method_name_.begin(), info.method_name_.end()); + dbg_str->push_back('\0'); + + // Enter name, low_pc, high_pc. + PushWord(dbg_info, str_offset); + PushWord(dbg_info, info.low_pc_); + PushWord(dbg_info, info.high_pc_); + } + + // One byte terminator + dbg_info->push_back(0); + + // We have now walked all the methods. Fill in lengths and low/high PCs. + UpdateWord(dbg_info, 0, dbg_info->size() - 4); + UpdateWord(dbg_info, low_pc_offset, low_pc); + UpdateWord(dbg_info, low_pc_offset + 4, high_pc); } } // namespace art diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h index f36d06f79d..dec75dc83f 100644 --- a/compiler/elf_writer_quick.h +++ b/compiler/elf_writer_quick.h @@ -21,11 +21,11 @@ namespace art { -class ElfWriterQuick : public ElfWriter { +class ElfWriterQuick FINAL : public ElfWriter { public: // Write an ELF file. Returns true on success, false on failure. static bool Create(File* file, - OatWriter& oat_writer, + OatWriter* oat_writer, const std::vector<const DexFile*>& dex_files, const std::string& android_root, bool is_host, @@ -33,15 +33,27 @@ class ElfWriterQuick : public ElfWriter { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); protected: - virtual bool Write(OatWriter& oat_writer, - const std::vector<const DexFile*>& dex_files, - const std::string& android_root, - bool is_host) + bool Write(OatWriter* oat_writer, + const std::vector<const DexFile*>& dex_files, + const std::string& android_root, + bool is_host) + OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: - ElfWriterQuick(const CompilerDriver& driver, File* elf_file); - ~ElfWriterQuick(); + ElfWriterQuick(const CompilerDriver& driver, File* elf_file) + : ElfWriter(driver, elf_file) {} + ~ElfWriterQuick() {} + + /* + * @brief Generate the DWARF debug_info and debug_abbrev sections + * @param oat_writer The Oat file Writer. + * @param dbg_info Compilation unit information. + * @param dbg_abbrev Abbreviations used to generate dbg_info. + * @param dbg_str Debug strings. + */ + void FillInCFIInformation(OatWriter* oat_writer, std::vector<uint8_t>* dbg_info, + std::vector<uint8_t>* dbg_abbrev, std::vector<uint8_t>* dbg_str); DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick); }; diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc index 5bad0d00ea..8175c35077 100644 --- a/compiler/elf_writer_test.cc +++ b/compiler/elf_writer_test.cc @@ -14,18 +14,18 @@ * limitations under the License. */ -#include "common_test.h" +#include "elf_file.h" +#include "common_compiler_test.h" #include "oat.h" -#include "elf_file.h" namespace art { -class ElfWriterTest : public CommonTest { +class ElfWriterTest : public CommonCompilerTest { protected: virtual void SetUp() { ReserveImageSpace(); - CommonTest::SetUp(); + CommonCompilerTest::SetUp(); } }; diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 49cabdce79..619b056f57 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -14,15 +14,16 @@ * limitations under the License. */ +#include "image.h" + #include <string> #include <vector> -#include "common_test.h" +#include "common_compiler_test.h" #include "compiler/elf_fixup.h" #include "compiler/image_writer.h" #include "compiler/oat_writer.h" #include "gc/space/image_space.h" -#include "image.h" #include "lock_word.h" #include "mirror/object-inl.h" #include "signal_catcher.h" @@ -32,11 +33,11 @@ namespace art { -class ImageTest : public CommonTest { +class ImageTest : public CommonCompilerTest { protected: virtual void SetUp() { ReserveImageSpace(); - CommonTest::SetUp(); + CommonCompilerTest::SetUp(); } }; @@ -48,15 +49,15 @@ TEST_F(ImageTest, WriteRead) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); TimingLogger timings("ImageTest::WriteRead", false, false); timings.StartSplit("CompileAll"); -#if defined(ART_USE_PORTABLE_COMPILER) - // TODO: we disable this for portable so the test executes in a reasonable amount of time. - // We shouldn't need to do this. - runtime_->SetCompilerFilter(Runtime::kInterpretOnly); -#endif + if (kUsePortableCompiler) { + // TODO: we disable this for portable so the test executes in a reasonable amount of time. + // We shouldn't need to do this. + compiler_options_->SetCompilerFilter(CompilerOptions::kInterpretOnly); + } for (const DexFile* dex_file : class_linker->GetBootClassPath()) { dex_file->EnableWrite(); } - compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings); + compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings); ScopedObjectAccess soa(Thread::Current()); OatWriter oat_writer(class_linker->GetBootClassPath(), @@ -64,7 +65,7 @@ TEST_F(ImageTest, WriteRead) { bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(), !kIsTargetBuild, class_linker->GetBootClassPath(), - oat_writer, + &oat_writer, tmp_elf.GetFile()); ASSERT_TRUE(success); timings.EndSplit(); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index c8447beaa5..aa16885039 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -583,6 +583,12 @@ void ImageWriter::FixupObject(Object* orig, Object* copy) { DCHECK(orig != NULL); DCHECK(copy != NULL); copy->SetClass<kVerifyNone>(down_cast<Class*>(GetImageAddress(orig->GetClass()))); + if (kUseBrooksPointer) { + orig->AssertSelfBrooksPointer(); + // Note the address 'copy' isn't the same as the image address of 'orig'. + copy->SetBrooksPointer(GetImageAddress(orig)); + DCHECK(copy->GetBrooksPointer() == GetImageAddress(orig)); + } // TODO: special case init of pointers to malloc data (or removal of these pointers) if (orig->IsClass<kVerifyNone>()) { FixupClass(orig->AsClass<kVerifyNone>(), down_cast<Class*>(copy)); diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 1bdff37f04..f48cf6c7b0 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -15,7 +15,7 @@ */ #include "class_linker.h" -#include "common_test.h" +#include "common_compiler_test.h" #include "dex_file.h" #include "gtest/gtest.h" #include "indirect_reference_table.h" @@ -43,7 +43,7 @@ extern "C" JNIEXPORT jint JNICALL Java_MyClassNatives_sbar(JNIEnv*, jclass, jint namespace art { -class JniCompilerTest : public CommonTest { +class JniCompilerTest : public CommonCompilerTest { protected: void CompileForTest(jobject class_loader, bool direct, const char* method_name, const char* method_sig) { diff --git a/compiler/leb128_encoder.h b/compiler/leb128_encoder.h deleted file mode 100644 index 67666831f0..0000000000 --- a/compiler/leb128_encoder.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_LEB128_ENCODER_H_ -#define ART_COMPILER_LEB128_ENCODER_H_ - -#include "base/macros.h" -#include "leb128.h" - -namespace art { - -static inline uint8_t* EncodeUnsignedLeb128(uint8_t* dest, uint32_t value) { - uint8_t out = value & 0x7f; - value >>= 7; - while (value != 0) { - *dest++ = out | 0x80; - out = value & 0x7f; - value >>= 7; - } - *dest++ = out; - return dest; -} - -static inline uint8_t* EncodeSignedLeb128(uint8_t* dest, int32_t value) { - uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6; - uint8_t out = value & 0x7f; - while (extra_bits != 0u) { - *dest++ = out | 0x80; - value >>= 7; - out = value & 0x7f; - extra_bits >>= 7; - } - *dest++ = out; - return dest; -} - -// An encoder with an API similar to vector<uint32_t> where the data is captured in ULEB128 format. -class Leb128EncodingVector { - public: - Leb128EncodingVector() { - } - - void Reserve(uint32_t size) { - data_.reserve(size); - } - - void PushBackUnsigned(uint32_t value) { - uint8_t out = value & 0x7f; - value >>= 7; - while (value != 0) { - data_.push_back(out | 0x80); - out = value & 0x7f; - value >>= 7; - } - data_.push_back(out); - } - - template<typename It> - void InsertBackUnsigned(It cur, It end) { - for (; cur != end; ++cur) { - PushBackUnsigned(*cur); - } - } - - void PushBackSigned(int32_t value) { - uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6; - uint8_t out = value & 0x7f; - while (extra_bits != 0u) { - data_.push_back(out | 0x80); - value >>= 7; - out = value & 0x7f; - extra_bits >>= 7; - } - data_.push_back(out); - } - - template<typename It> - void InsertBackSigned(It cur, It end) { - for (; cur != end; ++cur) { - PushBackSigned(*cur); - } - } - - const std::vector<uint8_t>& GetData() const { - return data_; - } - - private: - std::vector<uint8_t> data_; - - DISALLOW_COPY_AND_ASSIGN(Leb128EncodingVector); -}; - -} // namespace art - -#endif // ART_COMPILER_LEB128_ENCODER_H_ diff --git a/compiler/leb128_encoder_test.cc b/compiler/leb128_encoder_test.cc deleted file mode 100644 index 7af851803e..0000000000 --- a/compiler/leb128_encoder_test.cc +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "leb128.h" -#include "leb128_encoder.h" - -#include "gtest/gtest.h" -#include "base/histogram-inl.h" - -namespace art { - -struct DecodeUnsignedLeb128TestCase { - uint32_t decoded; - uint8_t leb128_data[5]; -}; - -static DecodeUnsignedLeb128TestCase uleb128_tests[] = { - {0, {0, 0, 0, 0, 0}}, - {1, {1, 0, 0, 0, 0}}, - {0x7F, {0x7F, 0, 0, 0, 0}}, - {0x80, {0x80, 1, 0, 0, 0}}, - {0x81, {0x81, 1, 0, 0, 0}}, - {0xFF, {0xFF, 1, 0, 0, 0}}, - {0x4000, {0x80, 0x80, 1, 0, 0}}, - {0x4001, {0x81, 0x80, 1, 0, 0}}, - {0x4081, {0x81, 0x81, 1, 0, 0}}, - {0x0FFFFFFF, {0xFF, 0xFF, 0xFF, 0x7F, 0}}, - {0xFFFFFFFF, {0xFF, 0xFF, 0xFF, 0xFF, 0xF}}, -}; - -struct DecodeSignedLeb128TestCase { - int32_t decoded; - uint8_t leb128_data[5]; -}; - -static DecodeSignedLeb128TestCase sleb128_tests[] = { - {0, {0, 0, 0, 0, 0}}, - {1, {1, 0, 0, 0, 0}}, - {0x3F, {0x3F, 0, 0, 0, 0}}, - {0x40, {0xC0, 0 /* sign bit */, 0, 0, 0}}, - {0x41, {0xC1, 0 /* sign bit */, 0, 0, 0}}, - {0x80, {0x80, 1, 0, 0, 0}}, - {0xFF, {0xFF, 1, 0, 0, 0}}, - {0x1FFF, {0xFF, 0x3F, 0, 0, 0}}, - {0x2000, {0x80, 0xC0, 0 /* sign bit */, 0, 0}}, - {0x2001, {0x81, 0xC0, 0 /* sign bit */, 0, 0}}, - {0x2081, {0x81, 0xC1, 0 /* sign bit */, 0, 0}}, - {0x4000, {0x80, 0x80, 1, 0, 0}}, - {0x0FFFFF, {0xFF, 0xFF, 0x3F, 0, 0}}, - {0x100000, {0x80, 0x80, 0xC0, 0 /* sign bit */, 0}}, - {0x100001, {0x81, 0x80, 0xC0, 0 /* sign bit */, 0}}, - {0x100081, {0x81, 0x81, 0xC0, 0 /* sign bit */, 0}}, - {0x104081, {0x81, 0x81, 0xC1, 0 /* sign bit */, 0}}, - {0x200000, {0x80, 0x80, 0x80, 1, 0}}, - {0x7FFFFFF, {0xFF, 0xFF, 0xFF, 0x3F, 0}}, - {0x8000000, {0x80, 0x80, 0x80, 0xC0, 0 /* sign bit */}}, - {0x8000001, {0x81, 0x80, 0x80, 0xC0, 0 /* sign bit */}}, - {0x8000081, {0x81, 0x81, 0x80, 0xC0, 0 /* sign bit */}}, - {0x8004081, {0x81, 0x81, 0x81, 0xC0, 0 /* sign bit */}}, - {0x8204081, {0x81, 0x81, 0x81, 0xC1, 0 /* sign bit */}}, - {0x0FFFFFFF, {0xFF, 0xFF, 0xFF, 0xFF, 0 /* sign bit */}}, - {0x10000000, {0x80, 0x80, 0x80, 0x80, 1}}, - {0x7FFFFFFF, {0xFF, 0xFF, 0xFF, 0xFF, 0x7}}, - {-1, {0x7F, 0, 0, 0, 0}}, - {-2, {0x7E, 0, 0, 0, 0}}, - {-0x3F, {0x41, 0, 0, 0, 0}}, - {-0x40, {0x40, 0, 0, 0, 0}}, - {-0x41, {0xBF, 0x7F, 0, 0, 0}}, - {-0x80, {0x80, 0x7F, 0, 0, 0}}, - {-0x81, {0xFF, 0x7E, 0, 0, 0}}, - {-0x00002000, {0x80, 0x40, 0, 0, 0}}, - {-0x00002001, {0xFF, 0xBF, 0x7F, 0, 0}}, - {-0x00100000, {0x80, 0x80, 0x40, 0, 0}}, - {-0x00100001, {0xFF, 0xFF, 0xBF, 0x7F, 0}}, - {-0x08000000, {0x80, 0x80, 0x80, 0x40, 0}}, - {-0x08000001, {0xFF, 0xFF, 0xFF, 0xBF, 0x7F}}, - {-0x20000000, {0x80, 0x80, 0x80, 0x80, 0x7E}}, - {(-1) << 31, {0x80, 0x80, 0x80, 0x80, 0x78}}, -}; - -TEST(Leb128Test, UnsignedSinglesVector) { - // Test individual encodings. - for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { - Leb128EncodingVector builder; - builder.PushBackUnsigned(uleb128_tests[i].decoded); - EXPECT_EQ(UnsignedLeb128Size(uleb128_tests[i].decoded), builder.GetData().size()); - const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0]; - const uint8_t* encoded_data_ptr = &builder.GetData()[0]; - for (size_t j = 0; j < 5; ++j) { - if (j < builder.GetData().size()) { - EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; - } else { - EXPECT_EQ(data_ptr[j], 0U) << " i = " << i << " j = " << j; - } - } - EXPECT_EQ(DecodeUnsignedLeb128(&data_ptr), uleb128_tests[i].decoded) << " i = " << i; - } -} - -TEST(Leb128Test, UnsignedSingles) { - // Test individual encodings. - for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { - uint8_t encoded_data[5]; - uint8_t* end = EncodeUnsignedLeb128(encoded_data, uleb128_tests[i].decoded); - size_t data_size = static_cast<size_t>(end - encoded_data); - EXPECT_EQ(UnsignedLeb128Size(uleb128_tests[i].decoded), data_size); - const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0]; - for (size_t j = 0; j < 5; ++j) { - if (j < data_size) { - EXPECT_EQ(data_ptr[j], encoded_data[j]) << " i = " << i << " j = " << j; - } else { - EXPECT_EQ(data_ptr[j], 0U) << " i = " << i << " j = " << j; - } - } - EXPECT_EQ(DecodeUnsignedLeb128(&data_ptr), uleb128_tests[i].decoded) << " i = " << i; - } -} - -TEST(Leb128Test, UnsignedStreamVector) { - // Encode a number of entries. - Leb128EncodingVector builder; - for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { - builder.PushBackUnsigned(uleb128_tests[i].decoded); - } - const uint8_t* encoded_data_ptr = &builder.GetData()[0]; - for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { - const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0]; - for (size_t j = 0; j < UnsignedLeb128Size(uleb128_tests[i].decoded); ++j) { - EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; - } - for (size_t j = UnsignedLeb128Size(uleb128_tests[i].decoded); j < 5; ++j) { - EXPECT_EQ(data_ptr[j], 0) << " i = " << i << " j = " << j; - } - EXPECT_EQ(DecodeUnsignedLeb128(&encoded_data_ptr), uleb128_tests[i].decoded) << " i = " << i; - } - EXPECT_EQ(builder.GetData().size(), - static_cast<size_t>(encoded_data_ptr - &builder.GetData()[0])); -} - -TEST(Leb128Test, UnsignedStream) { - // Encode a number of entries. - uint8_t encoded_data[5 * arraysize(uleb128_tests)]; - uint8_t* end = encoded_data; - for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { - end = EncodeUnsignedLeb128(end, uleb128_tests[i].decoded); - } - size_t data_size = static_cast<size_t>(end - encoded_data); - const uint8_t* encoded_data_ptr = encoded_data; - for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { - const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0]; - for (size_t j = 0; j < UnsignedLeb128Size(uleb128_tests[i].decoded); ++j) { - EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; - } - for (size_t j = UnsignedLeb128Size(uleb128_tests[i].decoded); j < 5; ++j) { - EXPECT_EQ(data_ptr[j], 0) << " i = " << i << " j = " << j; - } - EXPECT_EQ(DecodeUnsignedLeb128(&encoded_data_ptr), uleb128_tests[i].decoded) << " i = " << i; - } - EXPECT_EQ(data_size, static_cast<size_t>(encoded_data_ptr - encoded_data)); -} - -TEST(Leb128Test, SignedSinglesVector) { - // Test individual encodings. - for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { - Leb128EncodingVector builder; - builder.PushBackSigned(sleb128_tests[i].decoded); - EXPECT_EQ(SignedLeb128Size(sleb128_tests[i].decoded), builder.GetData().size()); - const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0]; - const uint8_t* encoded_data_ptr = &builder.GetData()[0]; - for (size_t j = 0; j < 5; ++j) { - if (j < builder.GetData().size()) { - EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; - } else { - EXPECT_EQ(data_ptr[j], 0U) << " i = " << i << " j = " << j; - } - } - EXPECT_EQ(DecodeSignedLeb128(&data_ptr), sleb128_tests[i].decoded) << " i = " << i; - } -} - -TEST(Leb128Test, SignedSingles) { - // Test individual encodings. - for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { - uint8_t encoded_data[5]; - uint8_t* end = EncodeSignedLeb128(encoded_data, sleb128_tests[i].decoded); - size_t data_size = static_cast<size_t>(end - encoded_data); - EXPECT_EQ(SignedLeb128Size(sleb128_tests[i].decoded), data_size); - const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0]; - for (size_t j = 0; j < 5; ++j) { - if (j < data_size) { - EXPECT_EQ(data_ptr[j], encoded_data[j]) << " i = " << i << " j = " << j; - } else { - EXPECT_EQ(data_ptr[j], 0U) << " i = " << i << " j = " << j; - } - } - EXPECT_EQ(DecodeSignedLeb128(&data_ptr), sleb128_tests[i].decoded) << " i = " << i; - } -} - -TEST(Leb128Test, SignedStreamVector) { - // Encode a number of entries. - Leb128EncodingVector builder; - for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { - builder.PushBackSigned(sleb128_tests[i].decoded); - } - const uint8_t* encoded_data_ptr = &builder.GetData()[0]; - for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { - const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0]; - for (size_t j = 0; j < SignedLeb128Size(sleb128_tests[i].decoded); ++j) { - EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; - } - for (size_t j = SignedLeb128Size(sleb128_tests[i].decoded); j < 5; ++j) { - EXPECT_EQ(data_ptr[j], 0) << " i = " << i << " j = " << j; - } - EXPECT_EQ(DecodeSignedLeb128(&encoded_data_ptr), sleb128_tests[i].decoded) << " i = " << i; - } - EXPECT_EQ(builder.GetData().size(), - static_cast<size_t>(encoded_data_ptr - &builder.GetData()[0])); -} - -TEST(Leb128Test, SignedStream) { - // Encode a number of entries. - uint8_t encoded_data[5 * arraysize(sleb128_tests)]; - uint8_t* end = encoded_data; - for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { - end = EncodeSignedLeb128(end, sleb128_tests[i].decoded); - } - size_t data_size = static_cast<size_t>(end - encoded_data); - const uint8_t* encoded_data_ptr = encoded_data; - for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { - const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0]; - for (size_t j = 0; j < SignedLeb128Size(sleb128_tests[i].decoded); ++j) { - EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; - } - for (size_t j = SignedLeb128Size(sleb128_tests[i].decoded); j < 5; ++j) { - EXPECT_EQ(data_ptr[j], 0) << " i = " << i << " j = " << j; - } - EXPECT_EQ(DecodeSignedLeb128(&encoded_data_ptr), sleb128_tests[i].decoded) << " i = " << i; - } - EXPECT_EQ(data_size, static_cast<size_t>(encoded_data_ptr - encoded_data)); -} - -TEST(Leb128Test, Speed) { - UniquePtr<Histogram<uint64_t> > enc_hist(new Histogram<uint64_t>("Leb128EncodeSpeedTest", 5)); - UniquePtr<Histogram<uint64_t> > dec_hist(new Histogram<uint64_t>("Leb128DecodeSpeedTest", 5)); - Leb128EncodingVector builder; - // Push back 1024 chunks of 1024 values measuring encoding speed. - uint64_t last_time = NanoTime(); - for (size_t i = 0; i < 1024; i++) { - for (size_t j = 0; j < 1024; j++) { - builder.PushBackUnsigned((i * 1024) + j); - } - uint64_t cur_time = NanoTime(); - enc_hist->AddValue(cur_time - last_time); - last_time = cur_time; - } - // Verify encoding and measure decode speed. - const uint8_t* encoded_data_ptr = &builder.GetData()[0]; - last_time = NanoTime(); - for (size_t i = 0; i < 1024; i++) { - for (size_t j = 0; j < 1024; j++) { - EXPECT_EQ(DecodeUnsignedLeb128(&encoded_data_ptr), (i * 1024) + j); - } - uint64_t cur_time = NanoTime(); - dec_hist->AddValue(cur_time - last_time); - last_time = cur_time; - } - - Histogram<uint64_t>::CumulativeData enc_data; - enc_hist->CreateHistogram(&enc_data); - enc_hist->PrintConfidenceIntervals(std::cout, 0.99, enc_data); - - Histogram<uint64_t>::CumulativeData dec_data; - dec_hist->CreateHistogram(&dec_data); - dec_hist->PrintConfidenceIntervals(std::cout, 0.99, dec_data); -} - -} // namespace art diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc index a5acd2a332..4ce714a183 100644 --- a/compiler/llvm/compiler_llvm.cc +++ b/compiler/llvm/compiler_llvm.cc @@ -175,16 +175,16 @@ CompileNativeMethod(DexCompilationUnit* dex_compilation_unit) { } // namespace llvm } // namespace art -inline static art::llvm::CompilerLLVM* ContextOf(art::CompilerDriver& driver) { +static art::llvm::CompilerLLVM* ContextOf(art::CompilerDriver& driver) { void *compiler_context = driver.GetCompilerContext(); CHECK(compiler_context != NULL); return reinterpret_cast<art::llvm::CompilerLLVM*>(compiler_context); } -inline static const art::llvm::CompilerLLVM* ContextOf(const art::CompilerDriver& driver) { +static art::llvm::CompilerLLVM* ContextOf(const art::CompilerDriver& driver) { void *compiler_context = driver.GetCompilerContext(); CHECK(compiler_context != NULL); - return reinterpret_cast<const art::llvm::CompilerLLVM*>(compiler_context); + return reinterpret_cast<art::llvm::CompilerLLVM*>(compiler_context); } extern "C" void ArtInitCompilerContext(art::CompilerDriver& driver) { @@ -233,7 +233,7 @@ extern "C" art::CompiledMethod* ArtLLVMJniCompileMethod(art::CompilerDriver& dri return result; } -extern "C" void compilerLLVMSetBitcodeFileName(art::CompilerDriver& driver, - std::string const& filename) { +extern "C" void compilerLLVMSetBitcodeFileName(const art::CompilerDriver& driver, + const std::string& filename) { ContextOf(driver)->SetBitcodeFileName(filename); } diff --git a/compiler/llvm/compiler_llvm.h b/compiler/llvm/compiler_llvm.h index 65bc16bcd8..c2211fb92c 100644 --- a/compiler/llvm/compiler_llvm.h +++ b/compiler/llvm/compiler_llvm.h @@ -70,7 +70,7 @@ class CompilerLLVM { return insn_set_; } - void SetBitcodeFileName(std::string const& filename) { + void SetBitcodeFileName(const std::string& filename) { bitcode_filename_ = filename; } diff --git a/compiler/llvm/gbc_expander.cc b/compiler/llvm/gbc_expander.cc index 8f22a97968..cf28db3bfc 100644 --- a/compiler/llvm/gbc_expander.cc +++ b/compiler/llvm/gbc_expander.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "dex_file.h" #include "dex_file-inl.h" #include "driver/compiler_driver.h" #include "driver/dex_compilation_unit.h" @@ -1602,7 +1603,7 @@ llvm::Value* GBCExpanderPass::Expand_HLIGet(llvm::CallInst& call_inst, llvm::Value* field_value; - int field_offset; + art::MemberOffset field_offset(0u); bool is_volatile; bool is_fast_path = driver_->ComputeInstanceFieldInfo( field_idx, dex_compilation_unit_, false, &field_offset, &is_volatile); @@ -1633,12 +1634,12 @@ llvm::Value* GBCExpanderPass::Expand_HLIGet(llvm::CallInst& call_inst, field_value = irb_.CreateBitCast(field_value, irb_.getJType(field_jty)); } } else { - DCHECK_GE(field_offset, 0); + DCHECK_GE(field_offset.Int32Value(), 0); llvm::PointerType* field_type = irb_.getJType(field_jty)->getPointerTo(); - llvm::ConstantInt* field_offset_value = irb_.getPtrEquivInt(field_offset); + llvm::ConstantInt* field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value()); llvm::Value* field_addr = irb_.CreatePtrDisp(object_addr, field_offset_value, field_type); @@ -1664,7 +1665,7 @@ void GBCExpanderPass::Expand_HLIPut(llvm::CallInst& call_inst, EmitGuard_NullPointerException(dex_pc, object_addr, opt_flags); - int field_offset; + art::MemberOffset field_offset(0u); bool is_volatile; bool is_fast_path = driver_->ComputeInstanceFieldInfo( field_idx, dex_compilation_unit_, true, &field_offset, &is_volatile); @@ -1698,7 +1699,7 @@ void GBCExpanderPass::Expand_HLIPut(llvm::CallInst& call_inst, EmitGuard_ExceptionLandingPad(dex_pc); } else { - DCHECK_GE(field_offset, 0); + DCHECK_GE(field_offset.Int32Value(), 0); if (is_volatile) { irb_.CreateMemoryBarrier(art::kStoreStore); @@ -1707,7 +1708,7 @@ void GBCExpanderPass::Expand_HLIPut(llvm::CallInst& call_inst, llvm::PointerType* field_type = irb_.getJType(field_jty)->getPointerTo(); - llvm::Value* field_offset_value = irb_.getPtrEquivInt(field_offset); + llvm::Value* field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value()); llvm::Value* field_addr = irb_.CreatePtrDisp(object_addr, field_offset_value, field_type); @@ -1875,8 +1876,8 @@ llvm::Value* GBCExpanderPass::Expand_HLSget(llvm::CallInst& call_inst, uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0)); uint32_t field_idx = LV2UInt(call_inst.getArgOperand(0)); - int field_offset; - int ssb_index; + art::MemberOffset field_offset(0u); + uint32_t ssb_index; bool is_referrers_class; bool is_volatile; bool is_initialized; @@ -1913,7 +1914,7 @@ llvm::Value* GBCExpanderPass::Expand_HLSget(llvm::CallInst& call_inst, static_field_value = irb_.CreateBitCast(static_field_value, irb_.getJType(field_jty)); } } else { - DCHECK_GE(field_offset, 0); + DCHECK_GE(field_offset.Int32Value(), 0); llvm::Value* static_storage_addr = NULL; @@ -1929,11 +1930,11 @@ llvm::Value* GBCExpanderPass::Expand_HLSget(llvm::CallInst& call_inst, } else { // Medium path, static storage base in a different class which // requires checks that the other class is initialized - DCHECK_GE(ssb_index, 0); + DCHECK_NE(ssb_index, art::DexFile::kDexNoIndex); static_storage_addr = EmitLoadStaticStorage(dex_pc, ssb_index); } - llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset); + llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value()); llvm::Value* static_field_addr = irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value, @@ -1960,8 +1961,8 @@ void GBCExpanderPass::Expand_HLSput(llvm::CallInst& call_inst, new_value = irb_.CreateBitCast(new_value, irb_.getJType(field_jty)); } - int field_offset; - int ssb_index; + art::MemberOffset field_offset(0u); + uint32_t ssb_index; bool is_referrers_class; bool is_volatile; bool is_initialized; @@ -1999,7 +2000,7 @@ void GBCExpanderPass::Expand_HLSput(llvm::CallInst& call_inst, EmitGuard_ExceptionLandingPad(dex_pc); } else { - DCHECK_GE(field_offset, 0); + DCHECK_GE(field_offset.Int32Value(), 0); llvm::Value* static_storage_addr = NULL; @@ -2015,7 +2016,7 @@ void GBCExpanderPass::Expand_HLSput(llvm::CallInst& call_inst, } else { // Medium path, static storage base in a different class which // requires checks that the other class is initialized - DCHECK_GE(ssb_index, 0); + DCHECK_NE(ssb_index, art::DexFile::kDexNoIndex); static_storage_addr = EmitLoadStaticStorage(dex_pc, ssb_index); } @@ -2023,7 +2024,7 @@ void GBCExpanderPass::Expand_HLSput(llvm::CallInst& call_inst, irb_.CreateMemoryBarrier(art::kStoreStore); } - llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset); + llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value()); llvm::Value* static_field_addr = irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value, diff --git a/compiler/llvm/llvm_compilation_unit.cc b/compiler/llvm/llvm_compilation_unit.cc index d23706d9f4..1d027f9d3b 100644 --- a/compiler/llvm/llvm_compilation_unit.cc +++ b/compiler/llvm/llvm_compilation_unit.cc @@ -199,7 +199,8 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea std::string target_triple; std::string target_cpu; std::string target_attr; - CompilerDriver::InstructionSetToLLVMTarget(GetInstructionSet(), target_triple, target_cpu, target_attr); + CompilerDriver::InstructionSetToLLVMTarget(GetInstructionSet(), &target_triple, &target_cpu, + &target_attr); std::string errmsg; const ::llvm::Target* target = diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index e91ffcb519..6dbba9fddd 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -14,20 +14,19 @@ * limitations under the License. */ -#include "compiler/oat_writer.h" +#include "common_compiler_test.h" #include "compiler/compiler_backend.h" +#include "compiler/oat_writer.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" -#include "mirror/object_array-inl.h" #include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" #include "oat_file.h" #include "vector_output_stream.h" -#include "common_test.h" - namespace art { -class OatTest : public CommonTest { +class OatTest : public CommonCompilerTest { protected: static const bool kCompile = false; // DISABLED_ due to the time to compile libcore @@ -81,7 +80,7 @@ class OatTest : public CommonTest { }; TEST_F(OatTest, WriteRead) { - TimingLogger timings("CommonTest::WriteRead", false, false); + TimingLogger timings("OatTest::WriteRead", false, false); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // TODO: make selectable. @@ -106,7 +105,7 @@ TEST_F(OatTest, WriteRead) { jobject class_loader = NULL; if (kCompile) { TimingLogger timings("OatTest::WriteRead", false, false); - compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings); + compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings); } ScopedObjectAccess soa(Thread::Current()); @@ -120,12 +119,12 @@ TEST_F(OatTest, WriteRead) { bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(), !kIsTargetBuild, class_linker->GetBootClassPath(), - oat_writer, + &oat_writer, tmp.GetFile()); ASSERT_TRUE(success); if (kCompile) { // OatWriter strips the code, regenerate to compare - compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings); + compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings); } std::string error_msg; UniquePtr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL, false, diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 7c5669a3ab..a400bdde6c 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -229,7 +229,7 @@ size_t OatWriter::InitOatClasses(size_t offset) { oat_classes_.push_back(oat_class); offset += oat_class->SizeOf(); } - oat_dex_files_[i]->UpdateChecksum(*oat_header_); + oat_dex_files_[i]->UpdateChecksum(oat_header_); } return offset; } @@ -293,7 +293,7 @@ size_t OatWriter::InitOatCodeDexFile(size_t offset, class_def_index++, (*oat_class_index)++) { const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); offset = InitOatCodeClassDef(offset, *oat_class_index, class_def_index, dex_file, class_def); - oat_classes_[*oat_class_index]->UpdateChecksum(*oat_header_); + oat_classes_[*oat_class_index]->UpdateChecksum(oat_header_); } return offset; } @@ -378,6 +378,27 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, uint32_t thumb_offset = compiled_method->CodeDelta(); quick_code_offset = offset + sizeof(code_size) + thumb_offset; + std::vector<uint8_t>* cfi_info = compiler_driver_->GetCallFrameInformation(); + if (cfi_info != nullptr) { + // Copy in the FDE, if present + const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo(); + if (fde != nullptr) { + // Copy the information into cfi_info and then fix the address in the new copy. + int cur_offset = cfi_info->size(); + cfi_info->insert(cfi_info->end(), fde->begin(), fde->end()); + + // Set the 'initial_location' field to address the start of the method. + uint32_t new_value = quick_code_offset - oat_header_->GetExecutableOffset(); + uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t); + (*cfi_info)[offset_to_update+0] = new_value; + (*cfi_info)[offset_to_update+1] = new_value >> 8; + (*cfi_info)[offset_to_update+2] = new_value >> 16; + (*cfi_info)[offset_to_update+3] = new_value >> 24; + method_info_.push_back(DebugInfo(PrettyMethod(class_def_method_index, dex_file, false), + new_value, new_value + code_size)); + } + } + // Deduplicate code arrays SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter = code_offsets_.find(quick_code); @@ -499,42 +520,42 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, } #define DCHECK_OFFSET() \ - DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out.Seek(0, kSeekCurrent)) \ + DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \ << "file_offset=" << file_offset << " relative_offset=" << relative_offset #define DCHECK_OFFSET_() \ - DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out.Seek(0, kSeekCurrent)) \ + DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out->Seek(0, kSeekCurrent)) \ << "file_offset=" << file_offset << " offset_=" << offset_ -bool OatWriter::Write(OutputStream& out) { - const size_t file_offset = out.Seek(0, kSeekCurrent); +bool OatWriter::Write(OutputStream* out) { + const size_t file_offset = out->Seek(0, kSeekCurrent); - if (!out.WriteFully(oat_header_, sizeof(*oat_header_))) { - PLOG(ERROR) << "Failed to write oat header to " << out.GetLocation(); + if (!out->WriteFully(oat_header_, sizeof(*oat_header_))) { + 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(); + 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(); if (!WriteTables(out, file_offset)) { - LOG(ERROR) << "Failed to write oat tables to " << out.GetLocation(); + LOG(ERROR) << "Failed to write oat tables to " << out->GetLocation(); return false; } size_t relative_offset = WriteCode(out, file_offset); if (relative_offset == 0) { - LOG(ERROR) << "Failed to write oat code to " << out.GetLocation(); + LOG(ERROR) << "Failed to write oat code to " << out->GetLocation(); return false; } relative_offset = WriteCodeDexFiles(out, file_offset, relative_offset); if (relative_offset == 0) { - LOG(ERROR) << "Failed to write oat code for dex files to " << out.GetLocation(); + LOG(ERROR) << "Failed to write oat code for dex files to " << out->GetLocation(); return false; } @@ -577,26 +598,26 @@ bool OatWriter::Write(OutputStream& out) { #undef DO_STAT VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; \ - CHECK_EQ(file_offset + size_total, static_cast<uint32_t>(out.Seek(0, kSeekCurrent))); + CHECK_EQ(file_offset + size_total, static_cast<uint32_t>(out->Seek(0, kSeekCurrent))); CHECK_EQ(size_, size_total); } - CHECK_EQ(file_offset + size_, static_cast<uint32_t>(out.Seek(0, kSeekCurrent))); + CHECK_EQ(file_offset + size_, static_cast<uint32_t>(out->Seek(0, kSeekCurrent))); CHECK_EQ(size_, relative_offset); return true; } -bool OatWriter::WriteTables(OutputStream& out, const size_t file_offset) { +bool OatWriter::WriteTables(OutputStream* out, const size_t file_offset) { for (size_t i = 0; i != oat_dex_files_.size(); ++i) { if (!oat_dex_files_[i]->Write(this, out, file_offset)) { - PLOG(ERROR) << "Failed to write oat dex information to " << out.GetLocation(); + PLOG(ERROR) << "Failed to write oat dex information to " << out->GetLocation(); return false; } } for (size_t i = 0; i != oat_dex_files_.size(); ++i) { uint32_t expected_offset = file_offset + oat_dex_files_[i]->dex_file_offset_; - off_t actual_offset = out.Seek(expected_offset, kSeekSet); + off_t actual_offset = out->Seek(expected_offset, kSeekSet); if (static_cast<uint32_t>(actual_offset) != expected_offset) { const DexFile* dex_file = (*dex_files_)[i]; PLOG(ERROR) << "Failed to seek to dex file section. Actual: " << actual_offset @@ -604,29 +625,29 @@ bool OatWriter::WriteTables(OutputStream& out, const size_t file_offset) { return false; } const DexFile* dex_file = (*dex_files_)[i]; - if (!out.WriteFully(&dex_file->GetHeader(), dex_file->GetHeader().file_size_)) { + if (!out->WriteFully(&dex_file->GetHeader(), dex_file->GetHeader().file_size_)) { PLOG(ERROR) << "Failed to write dex file " << dex_file->GetLocation() - << " to " << out.GetLocation(); + << " to " << out->GetLocation(); return false; } size_dex_file_ += dex_file->GetHeader().file_size_; } for (size_t i = 0; i != oat_classes_.size(); ++i) { if (!oat_classes_[i]->Write(this, out, file_offset)) { - PLOG(ERROR) << "Failed to write oat methods information to " << out.GetLocation(); + PLOG(ERROR) << "Failed to write oat methods information to " << out->GetLocation(); return false; } } return true; } -size_t OatWriter::WriteCode(OutputStream& out, const size_t file_offset) { +size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset) { size_t relative_offset = oat_header_->GetExecutableOffset(); - off_t new_offset = out.Seek(size_executable_offset_alignment_, kSeekCurrent); + off_t new_offset = out->Seek(size_executable_offset_alignment_, kSeekCurrent); size_t expected_file_offset = file_offset + relative_offset; if (static_cast<uint32_t>(new_offset) != expected_file_offset) { PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset - << " Expected: " << expected_file_offset << " File: " << out.GetLocation(); + << " Expected: " << expected_file_offset << " File: " << out->GetLocation(); return 0; } DCHECK_OFFSET(); @@ -637,10 +658,10 @@ size_t OatWriter::WriteCode(OutputStream& out, const size_t file_offset) { do { \ uint32_t aligned_offset = CompiledCode::AlignCode(relative_offset, instruction_set); \ uint32_t alignment_padding = aligned_offset - relative_offset; \ - out.Seek(alignment_padding, kSeekCurrent); \ + out->Seek(alignment_padding, kSeekCurrent); \ size_trampoline_alignment_ += alignment_padding; \ - if (!out.WriteFully(&(*field)[0], field->size())) { \ - PLOG(ERROR) << "Failed to write " # field " to " << out.GetLocation(); \ + if (!out->WriteFully(&(*field)[0], field->size())) { \ + PLOG(ERROR) << "Failed to write " # field " to " << out->GetLocation(); \ return false; \ } \ size_ ## field += field->size(); \ @@ -662,7 +683,7 @@ size_t OatWriter::WriteCode(OutputStream& out, const size_t file_offset) { return relative_offset; } -size_t OatWriter::WriteCodeDexFiles(OutputStream& out, +size_t OatWriter::WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset) { size_t oat_class_index = 0; @@ -678,7 +699,7 @@ size_t OatWriter::WriteCodeDexFiles(OutputStream& out, return relative_offset; } -size_t OatWriter::WriteCodeDexFile(OutputStream& out, const size_t file_offset, +size_t OatWriter::WriteCodeDexFile(OutputStream* out, const size_t file_offset, size_t relative_offset, size_t* oat_class_index, const DexFile& dex_file) { for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); @@ -694,12 +715,12 @@ size_t OatWriter::WriteCodeDexFile(OutputStream& out, const size_t file_offset, } void OatWriter::ReportWriteFailure(const char* what, uint32_t method_idx, - const DexFile& dex_file, OutputStream& out) const { + const DexFile& dex_file, const OutputStream& out) const { PLOG(ERROR) << "Failed to write " << what << " for " << PrettyMethod(method_idx, dex_file) << " to " << out.GetLocation(); } -size_t OatWriter::WriteCodeClassDef(OutputStream& out, +size_t OatWriter::WriteCodeClassDef(OutputStream* out, const size_t file_offset, size_t relative_offset, size_t oat_class_index, @@ -747,7 +768,7 @@ size_t OatWriter::WriteCodeClassDef(OutputStream& out, return relative_offset; } -size_t OatWriter::WriteCodeMethod(OutputStream& out, const size_t file_offset, +size_t OatWriter::WriteCodeMethod(OutputStream* out, const size_t file_offset, size_t relative_offset, size_t oat_class_index, size_t class_def_method_index, size_t* method_offsets_index, bool is_static, uint32_t method_idx, const DexFile& dex_file) { @@ -763,12 +784,12 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, const size_t file_offset, uint32_t aligned_offset = compiled_method->AlignCode(relative_offset); uint32_t aligned_code_delta = aligned_offset - relative_offset; if (aligned_code_delta != 0) { - off_t new_offset = out.Seek(aligned_code_delta, kSeekCurrent); + off_t new_offset = out->Seek(aligned_code_delta, kSeekCurrent); size_code_alignment_ += aligned_code_delta; uint32_t expected_offset = file_offset + aligned_offset; if (static_cast<uint32_t>(new_offset) != expected_offset) { PLOG(ERROR) << "Failed to seek to align oat code. Actual: " << new_offset - << " Expected: " << expected_offset << " File: " << out.GetLocation(); + << " Expected: " << expected_offset << " File: " << out->GetLocation(); return 0; } relative_offset += aligned_code_delta; @@ -787,15 +808,15 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, const size_t file_offset, << PrettyMethod(method_idx, dex_file); } else { DCHECK(code_offset == method_offsets.code_offset_) << PrettyMethod(method_idx, dex_file); - if (!out.WriteFully(&code_size, sizeof(code_size))) { - ReportWriteFailure("method code size", method_idx, dex_file, out); + if (!out->WriteFully(&code_size, sizeof(code_size))) { + ReportWriteFailure("method code size", method_idx, dex_file, *out); return 0; } size_code_size_ += sizeof(code_size); relative_offset += sizeof(code_size); DCHECK_OFFSET(); - if (!out.WriteFully(&(*quick_code)[0], code_size)) { - ReportWriteFailure("method code", method_idx, dex_file, out); + if (!out->WriteFully(&(*quick_code)[0], code_size)) { + ReportWriteFailure("method code", method_idx, dex_file, *out); return 0; } size_code_ += code_size; @@ -818,8 +839,8 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, const size_t file_offset, DCHECK((mapping_table_size == 0 && method_offsets.mapping_table_offset_ == 0) || relative_offset == method_offsets.mapping_table_offset_) << PrettyMethod(method_idx, dex_file); - if (!out.WriteFully(&mapping_table[0], mapping_table_size)) { - ReportWriteFailure("mapping table", method_idx, dex_file, out); + if (!out->WriteFully(&mapping_table[0], mapping_table_size)) { + ReportWriteFailure("mapping table", method_idx, dex_file, *out); return 0; } size_mapping_table_ += mapping_table_size; @@ -842,8 +863,8 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, const size_t file_offset, DCHECK((vmap_table_size == 0 && method_offsets.vmap_table_offset_ == 0) || relative_offset == method_offsets.vmap_table_offset_) << PrettyMethod(method_idx, dex_file); - if (!out.WriteFully(&vmap_table[0], vmap_table_size)) { - ReportWriteFailure("vmap table", method_idx, dex_file, out); + if (!out->WriteFully(&vmap_table[0], vmap_table_size)) { + ReportWriteFailure("vmap table", method_idx, dex_file, *out); return 0; } size_vmap_table_ += vmap_table_size; @@ -866,8 +887,8 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, const size_t file_offset, DCHECK((gc_map_size == 0 && method_offsets.gc_map_offset_ == 0) || relative_offset == method_offsets.gc_map_offset_) << PrettyMethod(method_idx, dex_file); - if (!out.WriteFully(&gc_map[0], gc_map_size)) { - ReportWriteFailure("GC map", method_idx, dex_file, out); + if (!out->WriteFully(&gc_map[0], gc_map_size)) { + ReportWriteFailure("GC map", method_idx, dex_file, *out); return 0; } size_gc_map_ += gc_map_size; @@ -897,42 +918,42 @@ size_t OatWriter::OatDexFile::SizeOf() const { + (sizeof(methods_offsets_[0]) * methods_offsets_.size()); } -void OatWriter::OatDexFile::UpdateChecksum(OatHeader& oat_header) const { - oat_header.UpdateChecksum(&dex_file_location_size_, sizeof(dex_file_location_size_)); - oat_header.UpdateChecksum(dex_file_location_data_, dex_file_location_size_); - oat_header.UpdateChecksum(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_)); - oat_header.UpdateChecksum(&dex_file_offset_, sizeof(dex_file_offset_)); - oat_header.UpdateChecksum(&methods_offsets_[0], +void OatWriter::OatDexFile::UpdateChecksum(OatHeader* oat_header) const { + oat_header->UpdateChecksum(&dex_file_location_size_, sizeof(dex_file_location_size_)); + oat_header->UpdateChecksum(dex_file_location_data_, dex_file_location_size_); + oat_header->UpdateChecksum(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_)); + oat_header->UpdateChecksum(&dex_file_offset_, sizeof(dex_file_offset_)); + oat_header->UpdateChecksum(&methods_offsets_[0], sizeof(methods_offsets_[0]) * methods_offsets_.size()); } bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, - OutputStream& out, + OutputStream* out, const size_t file_offset) const { DCHECK_OFFSET_(); - if (!out.WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) { - PLOG(ERROR) << "Failed to write dex file location length to " << out.GetLocation(); + if (!out->WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) { + PLOG(ERROR) << "Failed to write dex file location length to " << out->GetLocation(); return false; } oat_writer->size_oat_dex_file_location_size_ += sizeof(dex_file_location_size_); - if (!out.WriteFully(dex_file_location_data_, dex_file_location_size_)) { - PLOG(ERROR) << "Failed to write dex file location data to " << out.GetLocation(); + if (!out->WriteFully(dex_file_location_data_, dex_file_location_size_)) { + PLOG(ERROR) << "Failed to write dex file location data to " << out->GetLocation(); return false; } oat_writer->size_oat_dex_file_location_data_ += dex_file_location_size_; - if (!out.WriteFully(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_))) { - PLOG(ERROR) << "Failed to write dex file location checksum to " << out.GetLocation(); + if (!out->WriteFully(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_))) { + PLOG(ERROR) << "Failed to write dex file location checksum to " << out->GetLocation(); return false; } oat_writer->size_oat_dex_file_location_checksum_ += sizeof(dex_file_location_checksum_); - if (!out.WriteFully(&dex_file_offset_, sizeof(dex_file_offset_))) { - PLOG(ERROR) << "Failed to write dex file offset to " << out.GetLocation(); + if (!out->WriteFully(&dex_file_offset_, sizeof(dex_file_offset_))) { + PLOG(ERROR) << "Failed to write dex file offset to " << out->GetLocation(); return false; } oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_); - if (!out.WriteFully(&methods_offsets_[0], + if (!out->WriteFully(&methods_offsets_[0], sizeof(methods_offsets_[0]) * methods_offsets_.size())) { - PLOG(ERROR) << "Failed to write methods offsets to " << out.GetLocation(); + PLOG(ERROR) << "Failed to write methods offsets to " << out->GetLocation(); return false; } oat_writer->size_oat_dex_file_methods_offsets_ += @@ -1020,48 +1041,48 @@ size_t OatWriter::OatClass::SizeOf() const { + (sizeof(method_offsets_[0]) * method_offsets_.size()); } -void OatWriter::OatClass::UpdateChecksum(OatHeader& oat_header) const { - oat_header.UpdateChecksum(&status_, sizeof(status_)); - oat_header.UpdateChecksum(&type_, sizeof(type_)); +void OatWriter::OatClass::UpdateChecksum(OatHeader* oat_header) const { + oat_header->UpdateChecksum(&status_, sizeof(status_)); + oat_header->UpdateChecksum(&type_, sizeof(type_)); if (method_bitmap_size_ != 0) { CHECK_EQ(kOatClassSomeCompiled, type_); - oat_header.UpdateChecksum(&method_bitmap_size_, sizeof(method_bitmap_size_)); - oat_header.UpdateChecksum(method_bitmap_->GetRawStorage(), method_bitmap_size_); + oat_header->UpdateChecksum(&method_bitmap_size_, sizeof(method_bitmap_size_)); + oat_header->UpdateChecksum(method_bitmap_->GetRawStorage(), method_bitmap_size_); } - oat_header.UpdateChecksum(&method_offsets_[0], - sizeof(method_offsets_[0]) * method_offsets_.size()); + oat_header->UpdateChecksum(&method_offsets_[0], + sizeof(method_offsets_[0]) * method_offsets_.size()); } bool OatWriter::OatClass::Write(OatWriter* oat_writer, - OutputStream& out, + OutputStream* out, const size_t file_offset) const { DCHECK_OFFSET_(); - if (!out.WriteFully(&status_, sizeof(status_))) { - PLOG(ERROR) << "Failed to write class status to " << out.GetLocation(); + if (!out->WriteFully(&status_, sizeof(status_))) { + PLOG(ERROR) << "Failed to write class status to " << out->GetLocation(); return false; } oat_writer->size_oat_class_status_ += sizeof(status_); - if (!out.WriteFully(&type_, sizeof(type_))) { - PLOG(ERROR) << "Failed to write oat class type to " << out.GetLocation(); + if (!out->WriteFully(&type_, sizeof(type_))) { + PLOG(ERROR) << "Failed to write oat class type to " << out->GetLocation(); return false; } oat_writer->size_oat_class_type_ += sizeof(type_); if (method_bitmap_size_ != 0) { CHECK_EQ(kOatClassSomeCompiled, type_); - if (!out.WriteFully(&method_bitmap_size_, sizeof(method_bitmap_size_))) { - PLOG(ERROR) << "Failed to write method bitmap size to " << out.GetLocation(); + if (!out->WriteFully(&method_bitmap_size_, sizeof(method_bitmap_size_))) { + PLOG(ERROR) << "Failed to write method bitmap size to " << out->GetLocation(); return false; } oat_writer->size_oat_class_method_bitmaps_ += sizeof(method_bitmap_size_); - if (!out.WriteFully(method_bitmap_->GetRawStorage(), method_bitmap_size_)) { - PLOG(ERROR) << "Failed to write method bitmap to " << out.GetLocation(); + if (!out->WriteFully(method_bitmap_->GetRawStorage(), method_bitmap_size_)) { + PLOG(ERROR) << "Failed to write method bitmap to " << out->GetLocation(); return false; } oat_writer->size_oat_class_method_bitmaps_ += method_bitmap_size_; } - if (!out.WriteFully(&method_offsets_[0], + if (!out->WriteFully(&method_offsets_[0], sizeof(method_offsets_[0]) * method_offsets_.size())) { - PLOG(ERROR) << "Failed to write method offsets to " << out.GetLocation(); + PLOG(ERROR) << "Failed to write method offsets to " << out->GetLocation(); return false; } oat_writer->size_oat_class_method_offsets_ += sizeof(method_offsets_[0]) * method_offsets_.size(); diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 067c78971f..3d4b48ae42 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -78,10 +78,23 @@ class OatWriter { return size_; } - bool Write(OutputStream& out); + bool Write(OutputStream* out); ~OatWriter(); + struct DebugInfo { + DebugInfo(const std::string& method_name, uint32_t low_pc, uint32_t high_pc) + : method_name_(method_name), low_pc_(low_pc), high_pc_(high_pc) { + } + std::string method_name_; + uint32_t low_pc_; + uint32_t high_pc_; + }; + + const std::vector<DebugInfo>& GetCFIMethodInfo() const { + return method_info_; + } + private: size_t InitOatHeader(); size_t InitOatDexFiles(size_t offset); @@ -105,28 +118,28 @@ class OatWriter { bool is_native, InvokeType type, uint32_t method_idx, const DexFile&) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool WriteTables(OutputStream& out, const size_t file_offset); - size_t WriteCode(OutputStream& out, const size_t file_offset); - size_t WriteCodeDexFiles(OutputStream& out, const size_t file_offset, size_t relative_offset); - size_t WriteCodeDexFile(OutputStream& out, const size_t file_offset, size_t relative_offset, + bool WriteTables(OutputStream* out, const size_t file_offset); + size_t WriteCode(OutputStream* out, const size_t file_offset); + size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset); + size_t WriteCodeDexFile(OutputStream* out, const size_t file_offset, size_t relative_offset, size_t* oat_class_index, const DexFile& dex_file); - size_t WriteCodeClassDef(OutputStream& out, const size_t file_offset, size_t relative_offset, + size_t WriteCodeClassDef(OutputStream* out, const size_t file_offset, size_t relative_offset, size_t oat_class_index, const DexFile& dex_file, const DexFile::ClassDef& class_def); - size_t WriteCodeMethod(OutputStream& out, const size_t file_offset, size_t relative_offset, + size_t WriteCodeMethod(OutputStream* out, const size_t file_offset, size_t relative_offset, size_t oat_class_index, size_t class_def_method_index, size_t* method_offsets_index, bool is_static, uint32_t method_idx, const DexFile& dex_file); void ReportWriteFailure(const char* what, uint32_t method_idx, const DexFile& dex_file, - OutputStream& out) const; + const OutputStream& out) const; class OatDexFile { public: explicit OatDexFile(size_t offset, const DexFile& dex_file); size_t SizeOf() const; - void UpdateChecksum(OatHeader& oat_header) const; - bool Write(OatWriter* oat_writer, OutputStream& out, const size_t file_offset) const; + void UpdateChecksum(OatHeader* oat_header) const; + bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const; // Offset of start of OatDexFile from beginning of OatHeader. It is // used to validate file position when writing. @@ -153,8 +166,8 @@ class OatWriter { size_t GetOatMethodOffsetsOffsetFromOatHeader(size_t class_def_method_index_) const; size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const; size_t SizeOf() const; - void UpdateChecksum(OatHeader& oat_header) const; - bool Write(OatWriter* oat_writer, OutputStream& out, const size_t file_offset) const; + void UpdateChecksum(OatHeader* oat_header) const; + bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const; CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const { DCHECK(compiled_methods_ != NULL); @@ -205,6 +218,8 @@ class OatWriter { DISALLOW_COPY_AND_ASSIGN(OatClass); }; + std::vector<DebugInfo> method_info_; + const CompilerDriver* const compiler_driver_; // note OatFile does not take ownership of the DexFiles diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 2c1091c3c9..e6db7bccc3 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -22,39 +22,120 @@ namespace art { HGraph* HGraphBuilder::BuildGraph(const uint16_t* code_ptr, const uint16_t* code_end) { + // Setup the graph with the entry block and exit block. graph_ = new (arena_) HGraph(arena_); - entry_block_ = new (arena_) HBasicBlock(graph_); graph_->AddBlock(entry_block_); - + entry_block_->AddInstruction(new (arena_) HGoto()); exit_block_ = new (arena_) HBasicBlock(graph_); - // The exit block is added at the end of this method to ensure - // its id is the greatest. This is needed for dominator computation. - - entry_block_->AddInstruction(new (arena_) HGoto(entry_block_)); + exit_block_->AddInstruction(new (arena_) HExit()); - current_block_ = new (arena_) HBasicBlock(graph_); - graph_->AddBlock(current_block_); - entry_block_->AddSuccessor(current_block_); + // To avoid splitting blocks, we compute ahead of time the instructions that + // start a new block, and create these blocks. + ComputeBranchTargets(code_ptr, code_end); + size_t dex_offset = 0; while (code_ptr < code_end) { + // Update the current block if dex_offset starts a new block. + MaybeUpdateCurrentBlock(dex_offset); const Instruction& instruction = *Instruction::At(code_ptr); - if (!AnalyzeDexInstruction(instruction)) return nullptr; + if (!AnalyzeDexInstruction(instruction, dex_offset)) return nullptr; + dex_offset += instruction.SizeInCodeUnits(); code_ptr += instruction.SizeInCodeUnits(); } - exit_block_->AddInstruction(new (arena_) HExit(exit_block_)); + // Add the exit block at the end to give it the highest id. graph_->AddBlock(exit_block_); return graph_; } -bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction) { +void HGraphBuilder::MaybeUpdateCurrentBlock(size_t index) { + HBasicBlock* block = FindBlockStartingAt(index); + if (block == nullptr) return; + + if (current_block_ != nullptr) { + // Branching instructions clear current_block, so we know + // the last instruction of the current block is not a branching + // instruction. We add an unconditional goto to the found block. + current_block_->AddInstruction(new (arena_) HGoto()); + current_block_->AddSuccessor(block); + } + graph_->AddBlock(block); + current_block_ = block; +} + +void HGraphBuilder::ComputeBranchTargets(const uint16_t* code_ptr, const uint16_t* code_end) { + // TODO: Support switch instructions. + branch_targets_.SetSize(code_end - code_ptr); + + // Create the first block for the dex instructions, single successor of the entry block. + HBasicBlock* block = new (arena_) HBasicBlock(graph_); + branch_targets_.Put(0, block); + entry_block_->AddSuccessor(block); + + // Iterate over all instructions and find branching instructions. Create blocks for + // the locations these instructions branch to. + size_t dex_offset = 0; + while (code_ptr < code_end) { + const Instruction& instruction = *Instruction::At(code_ptr); + if (instruction.IsBranch()) { + int32_t target = instruction.GetTargetOffset() + dex_offset; + // Create a block for the target instruction. + if (FindBlockStartingAt(target) == nullptr) { + block = new (arena_) HBasicBlock(graph_); + branch_targets_.Put(target, block); + } + dex_offset += instruction.SizeInCodeUnits(); + code_ptr += instruction.SizeInCodeUnits(); + if ((code_ptr < code_end) && (FindBlockStartingAt(dex_offset) == nullptr)) { + block = new (arena_) HBasicBlock(graph_); + branch_targets_.Put(dex_offset, block); + } + } else { + code_ptr += instruction.SizeInCodeUnits(); + dex_offset += instruction.SizeInCodeUnits(); + } + } +} + +HBasicBlock* HGraphBuilder::FindBlockStartingAt(int32_t index) const { + DCHECK_GE(index, 0); + return branch_targets_.Get(index); +} + +bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset) { + if (current_block_ == nullptr) return true; // Dead code + switch (instruction.Opcode()) { case Instruction::RETURN_VOID: - current_block_->AddInstruction(new (arena_) HReturnVoid(current_block_)); + current_block_->AddInstruction(new (arena_) HReturnVoid()); current_block_->AddSuccessor(exit_block_); current_block_ = nullptr; break; + case Instruction::IF_EQ: { + // TODO: Read the dex register. + HBasicBlock* target = FindBlockStartingAt(instruction.GetTargetOffset() + dex_offset); + DCHECK(target != nullptr); + current_block_->AddInstruction(new (arena_) HIf()); + current_block_->AddSuccessor(target); + target = FindBlockStartingAt(dex_offset + instruction.SizeInCodeUnits()); + DCHECK(target != nullptr); + current_block_->AddSuccessor(target); + current_block_ = nullptr; + break; + } + case Instruction::GOTO: + case Instruction::GOTO_16: + case Instruction::GOTO_32: { + HBasicBlock* target = FindBlockStartingAt(instruction.GetTargetOffset() + dex_offset); + DCHECK(target != nullptr); + current_block_->AddInstruction(new (arena_) HGoto()); + current_block_->AddSuccessor(target); + current_block_ = nullptr; + break; + } + case Instruction::NOP: + break; default: return false; } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 3e94fba228..fbeb7a7dab 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_BUILDER_H_ #include "utils/allocation.h" +#include "utils/growable_array.h" namespace art { @@ -30,6 +31,7 @@ class HGraphBuilder : public ValueObject { public: explicit HGraphBuilder(ArenaAllocator* arena) : arena_(arena), + branch_targets_(arena, 0), entry_block_(nullptr), exit_block_(nullptr), current_block_(nullptr), @@ -41,9 +43,21 @@ class HGraphBuilder : public ValueObject { // Analyzes the dex instruction and adds HInstruction to the graph // to execute that instruction. Returns whether the instruction can // be handled. - bool AnalyzeDexInstruction(const Instruction& instruction); + bool AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset); + + // Finds all instructions that start a new block, and populates branch_targets_ with + // the newly created blocks. + void ComputeBranchTargets(const uint16_t* start, const uint16_t* end); + void MaybeUpdateCurrentBlock(size_t index); + HBasicBlock* FindBlockStartingAt(int32_t index) const; ArenaAllocator* const arena_; + + // A list of the size of the dex code holding block information for + // the method. If an entry contains a block, then the dex instruction + // starting at that entry is the first instruction of a new block. + GrowableArray<HBasicBlock*> branch_targets_; + HBasicBlock* entry_block_; HBasicBlock* exit_block_; HBasicBlock* current_block_; diff --git a/compiler/optimizing/dominator_test.cc b/compiler/optimizing/dominator_test.cc new file mode 100644 index 0000000000..30f288ff54 --- /dev/null +++ b/compiler/optimizing/dominator_test.cc @@ -0,0 +1,261 @@ +/* + * 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 "builder.h" +#include "dex_instruction.h" +#include "nodes.h" +#include "utils/arena_allocator.h" + +#include "gtest/gtest.h" + +namespace art { + +static void TestCode(const uint16_t* data, int length, const int* blocks, size_t blocks_length) { + ArenaPool pool; + ArenaAllocator allocator(&pool); + HGraphBuilder builder(&allocator); + HGraph* graph = builder.BuildGraph(data, data + length); + ASSERT_NE(graph, nullptr); + graph->BuildDominatorTree(); + ASSERT_EQ(graph->blocks()->Size(), blocks_length); + for (size_t i = 0; i < blocks_length; i++) { + if (blocks[i] == -1) { + ASSERT_EQ(nullptr, graph->blocks()->Get(i)->dominator()); + } else { + ASSERT_NE(nullptr, graph->blocks()->Get(i)->dominator()); + ASSERT_EQ(blocks[i], graph->blocks()->Get(i)->dominator()->block_id()); + } + } +} + +TEST(OptimizerTest, ReturnVoid) { + const uint16_t data[] = { + Instruction::RETURN_VOID // Block number 1 + }; + + const int dominators[] = { + -1, + 0, + 1 + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); +} + +TEST(OptimizerTest, CFG1) { + const uint16_t data[] = { + Instruction::GOTO | 0x100, // Block number 1 + Instruction::RETURN_VOID // Block number 2 + }; + + const int dominators[] = { + -1, + 0, + 1, + 2 + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); +} + +TEST(OptimizerTest, CFG2) { + const uint16_t data[] = { + Instruction::GOTO | 0x100, // Block number 1 + Instruction::GOTO | 0x100, // Block number 2 + Instruction::RETURN_VOID // Block number 3 + }; + + const int dominators[] = { + -1, + 0, + 1, + 2, + 3 + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); +} + +TEST(OptimizerTest, CFG3) { + const uint16_t data1[] = { + Instruction::GOTO | 0x200, // Block number 1 + Instruction::RETURN_VOID, // Block number 2 + Instruction::GOTO | 0xFF00 // Block number 3 + }; + const int dominators[] = { + -1, + 0, + 3, + 1, + 2 + }; + + TestCode(data1, sizeof(data1) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); + + const uint16_t data2[] = { + Instruction::GOTO_16, 3, + Instruction::RETURN_VOID, + Instruction::GOTO_16, 0xFFFF + }; + + TestCode(data2, sizeof(data2) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); + + const uint16_t data3[] = { + Instruction::GOTO_32, 4, 0, + Instruction::RETURN_VOID, + Instruction::GOTO_32, 0xFFFF, 0xFFFF + }; + + TestCode(data3, sizeof(data3) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); +} + +TEST(OptimizerTest, CFG4) { + const uint16_t data1[] = { + Instruction::NOP, + Instruction::GOTO | 0xFF00 + }; + + const int dominators[] = { + -1, + 0, + -1 + }; + + TestCode(data1, sizeof(data1) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); + + const uint16_t data2[] = { + Instruction::GOTO_32, 0, 0 + }; + + TestCode(data2, sizeof(data2) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); +} + +TEST(OptimizerTest, CFG5) { + const uint16_t data[] = { + Instruction::RETURN_VOID, // Block number 1 + Instruction::GOTO | 0x100, // Dead block + Instruction::GOTO | 0xFE00 // Block number 2 + }; + + const int dominators[] = { + -1, + 0, + -1, + 1 + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); +} + +TEST(OptimizerTest, CFG6) { + const uint16_t data[] = { + Instruction::IF_EQ, 3, + Instruction::GOTO | 0x100, + Instruction::RETURN_VOID + }; + + const int dominators[] = { + -1, + 0, + 1, + 1, + 3 + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); +} + +TEST(OptimizerTest, CFG7) { + const uint16_t data[] = { + Instruction::IF_EQ, 3, // Block number 1 + Instruction::GOTO | 0x100, // Block number 2 + Instruction::GOTO | 0xFF00 // Block number 3 + }; + + const int dominators[] = { + -1, + 0, + 1, + 1, + -1 // exit block is not dominated by any block due to the spin loop. + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); +} + +TEST(OptimizerTest, CFG8) { + const uint16_t data[] = { + Instruction::IF_EQ, 3, // Block number 1 + Instruction::GOTO | 0x200, // Block number 2 + Instruction::GOTO | 0x100, // Block number 3 + Instruction::GOTO | 0xFF00 // Block number 4 + }; + + const int dominators[] = { + -1, + 0, + 1, + 1, + 1, + -1 // exit block is not dominated by any block due to the spin loop. + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); +} + +TEST(OptimizerTest, CFG9) { + const uint16_t data[] = { + Instruction::IF_EQ, 3, // Block number 1 + Instruction::GOTO | 0x200, // Block number 2 + Instruction::GOTO | 0x100, // Block number 3 + Instruction::GOTO | 0xFE00 // Block number 4 + }; + + const int dominators[] = { + -1, + 0, + 1, + 1, + 1, + -1 // exit block is not dominated by any block due to the spin loop. + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); +} + +TEST(OptimizerTest, CFG10) { + const uint16_t data[] = { + Instruction::IF_EQ, 6, // Block number 1 + Instruction::IF_EQ, 3, // Block number 2 + Instruction::GOTO | 0x100, // Block number 3 + Instruction::GOTO | 0x100, // Block number 4 + Instruction::RETURN_VOID // Block number 5 + }; + + const int dominators[] = { + -1, + 0, + 1, + 2, + 2, + 1, + 5 // Block number 5 dominates exit block + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), dominators, sizeof(dominators) / sizeof(int)); +} + +} // namespace art diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index e7e9f4746a..9ec8e89ff6 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -24,6 +24,103 @@ void HGraph::AddBlock(HBasicBlock* block) { blocks_.Add(block); } +void HGraph::FindBackEdges(ArenaBitVector* visited) const { + ArenaBitVector visiting(arena_, blocks_.Size(), false); + VisitBlockForBackEdges(GetEntryBlock(), visited, &visiting); +} + +void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) const { + for (size_t i = 0; i < blocks_.Size(); i++) { + if (!visited.IsBitSet(i)) { + HBasicBlock* block = blocks_.Get(i); + for (size_t j = 0; j < block->successors()->Size(); j++) { + block->successors()->Get(j)->RemovePredecessor(block); + } + } + } +} + +void HGraph::VisitBlockForBackEdges(HBasicBlock* block, + ArenaBitVector* visited, + ArenaBitVector* visiting) const { + int id = block->block_id(); + if (visited->IsBitSet(id)) return; + + visited->SetBit(id); + visiting->SetBit(id); + for (size_t i = 0; i < block->successors()->Size(); i++) { + HBasicBlock* successor = block->successors()->Get(i); + if (visiting->IsBitSet(successor->block_id())) { + successor->AddBackEdge(block); + } else { + VisitBlockForBackEdges(successor, visited, visiting); + } + } + visiting->ClearBit(id); +} + +void HGraph::BuildDominatorTree() { + ArenaBitVector visited(arena_, blocks_.Size(), false); + + // (1) Find the back edges in the graph doing a DFS traversal. + FindBackEdges(&visited); + + // (2) Remove blocks not visited during the initial DFS. + // Step (3) requires dead blocks to be removed from the + // predecessors list of live blocks. + RemoveDeadBlocks(visited); + + // (3) Compute the immediate dominator of each block. We visit + // the successors of a block only when all its forward branches + // have been processed. + GrowableArray<size_t> visits(arena_, blocks_.Size()); + visits.SetSize(blocks_.Size()); + HBasicBlock* entry = GetEntryBlock(); + dominator_order_.Add(entry); + for (size_t i = 0; i < entry->successors()->Size(); i++) { + VisitBlockForDominatorTree(entry->successors()->Get(i), entry, &visits); + } +} + +HBasicBlock* HGraph::FindCommonDominator(HBasicBlock* first, HBasicBlock* second) const { + ArenaBitVector visited(arena_, blocks_.Size(), false); + // Walk the dominator tree of the first block and mark the visited blocks. + while (first != nullptr) { + visited.SetBit(first->block_id()); + first = first->dominator(); + } + // Walk the dominator tree of the second block until a marked block is found. + while (second != nullptr) { + if (visited.IsBitSet(second->block_id())) { + return second; + } + second = second->dominator(); + } + LOG(ERROR) << "Could not find common dominator"; + return nullptr; +} + +void HGraph::VisitBlockForDominatorTree(HBasicBlock* block, + HBasicBlock* predecessor, + GrowableArray<size_t>* visits) { + if (block->dominator() == nullptr) { + block->set_dominator(predecessor); + } else { + block->set_dominator(FindCommonDominator(block->dominator(), predecessor)); + } + + visits->Increment(block->block_id()); + // Once all the forward edges have been visited, we know the immediate + // dominator of the block. We can then start visiting its successors. + if (visits->Get(block->block_id()) == + block->predecessors()->Size() - block->NumberOfBackEdges()) { + dominator_order_.Add(block); + for (size_t i = 0; i < block->successors()->Size(); i++) { + VisitBlockForDominatorTree(block->successors()->Get(i), block, visits); + } + } +} + void HBasicBlock::AddInstruction(HInstruction* instruction) { if (first_instruction_ == nullptr) { DCHECK(last_instruction_ == nullptr); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 3d1c25e71b..3d5d531cfe 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_NODES_H_ #include "utils/allocation.h" +#include "utils/arena_bit_vector.h" #include "utils/growable_array.h" namespace art { @@ -29,26 +30,67 @@ class HGraphVisitor; static const int kDefaultNumberOfBlocks = 8; static const int kDefaultNumberOfSuccessors = 2; static const int kDefaultNumberOfPredecessors = 2; +static const int kDefaultNumberOfBackEdges = 1; // Control-flow graph of a method. Contains a list of basic blocks. class HGraph : public ArenaObject { public: explicit HGraph(ArenaAllocator* arena) : arena_(arena), - blocks_(arena, kDefaultNumberOfBlocks) { } + blocks_(arena, kDefaultNumberOfBlocks), + dominator_order_(arena, kDefaultNumberOfBlocks) { } ArenaAllocator* arena() const { return arena_; } const GrowableArray<HBasicBlock*>* blocks() const { return &blocks_; } void AddBlock(HBasicBlock* block); + void BuildDominatorTree(); private: + HBasicBlock* FindCommonDominator(HBasicBlock* first, HBasicBlock* second) const; + void VisitBlockForDominatorTree(HBasicBlock* block, + HBasicBlock* predecessor, + GrowableArray<size_t>* visits); + void FindBackEdges(ArenaBitVector* visited) const; + void VisitBlockForBackEdges(HBasicBlock* block, + ArenaBitVector* visited, + ArenaBitVector* visiting) const; + void RemoveDeadBlocks(const ArenaBitVector& visited) const; + + HBasicBlock* GetEntryBlock() const { return blocks_.Get(0); } + ArenaAllocator* const arena_; + + // List of blocks in insertion order. GrowableArray<HBasicBlock*> blocks_; + // List of blocks to perform a pre-order dominator tree traversal. + GrowableArray<HBasicBlock*> dominator_order_; + DISALLOW_COPY_AND_ASSIGN(HGraph); }; +class HLoopInformation : public ArenaObject { + public: + HLoopInformation(HBasicBlock* header, HGraph* graph) + : header_(header), + back_edges_(graph->arena(), kDefaultNumberOfBackEdges) { } + + void AddBackEdge(HBasicBlock* back_edge) { + back_edges_.Add(back_edge); + } + + int NumberOfBackEdges() const { + return back_edges_.Size(); + } + + private: + HBasicBlock* header_; + GrowableArray<HBasicBlock*> back_edges_; + + DISALLOW_COPY_AND_ASSIGN(HLoopInformation); +}; + // A block in a method. Contains the list of instructions represented // as a double linked list. Each block knows its predecessors and // successors. @@ -60,6 +102,8 @@ class HBasicBlock : public ArenaObject { successors_(graph->arena(), kDefaultNumberOfSuccessors), first_instruction_(nullptr), last_instruction_(nullptr), + loop_information_(nullptr), + dominator_(nullptr), block_id_(-1) { } const GrowableArray<HBasicBlock*>* predecessors() const { @@ -70,11 +114,27 @@ class HBasicBlock : public ArenaObject { return &successors_; } + void AddBackEdge(HBasicBlock* back_edge) { + if (loop_information_ == nullptr) { + loop_information_ = new (graph_->arena()) HLoopInformation(this, graph_); + } + loop_information_->AddBackEdge(back_edge); + } + HGraph* graph() const { return graph_; } int block_id() const { return block_id_; } void set_block_id(int id) { block_id_ = id; } + HBasicBlock* dominator() const { return dominator_; } + void set_dominator(HBasicBlock* dominator) { dominator_ = dominator; } + + int NumberOfBackEdges() const { + return loop_information_ == nullptr + ? 0 + : loop_information_->NumberOfBackEdges(); + } + HInstruction* first_instruction() const { return first_instruction_; } HInstruction* last_instruction() const { return last_instruction_; } @@ -83,6 +143,10 @@ class HBasicBlock : public ArenaObject { block->predecessors_.Add(this); } + void RemovePredecessor(HBasicBlock* block) { + predecessors_.Delete(block); + } + void AddInstruction(HInstruction* instruction); private: @@ -91,6 +155,8 @@ class HBasicBlock : public ArenaObject { GrowableArray<HBasicBlock*> successors_; HInstruction* first_instruction_; HInstruction* last_instruction_; + HLoopInformation* loop_information_; + HBasicBlock* dominator_; int block_id_; DISALLOW_COPY_AND_ASSIGN(HBasicBlock); @@ -99,6 +165,7 @@ class HBasicBlock : public ArenaObject { #define FOR_EACH_INSTRUCTION(M) \ M(Exit) \ M(Goto) \ + M(If) \ M(ReturnVoid) \ #define DECLARE_INSTRUCTION(type) \ @@ -107,9 +174,7 @@ class HBasicBlock : public ArenaObject { class HInstruction : public ArenaObject { public: - explicit HInstruction(HBasicBlock* block) - : previous_(nullptr), - next_(nullptr) { } + HInstruction() : previous_(nullptr), next_(nullptr) { } virtual ~HInstruction() { } HInstruction* next() const { return next_; } @@ -199,9 +264,7 @@ class EmbeddedArray<T, 0> { template<intptr_t N> class HTemplateInstruction: public HInstruction { public: - HTemplateInstruction<N>(HBasicBlock* block) - : HInstruction(block), - inputs_() { } + HTemplateInstruction<N>() : inputs_() { } virtual ~HTemplateInstruction() { } virtual intptr_t InputCount() const { return N; } @@ -215,7 +278,7 @@ class HTemplateInstruction: public HInstruction { // instruction that branches to the exit block. class HReturnVoid : public HTemplateInstruction<0> { public: - explicit HReturnVoid(HBasicBlock* block) : HTemplateInstruction<0>(block) { } + HReturnVoid() { } DECLARE_INSTRUCTION(ReturnVoid) @@ -228,7 +291,7 @@ class HReturnVoid : public HTemplateInstruction<0> { // exit block. class HExit : public HTemplateInstruction<0> { public: - explicit HExit(HBasicBlock* block) : HTemplateInstruction<0>(block) { } + HExit() { } DECLARE_INSTRUCTION(Exit) @@ -239,7 +302,7 @@ class HExit : public HTemplateInstruction<0> { // Jumps from one block to another. class HGoto : public HTemplateInstruction<0> { public: - explicit HGoto(HBasicBlock* block) : HTemplateInstruction<0>(block) { } + HGoto() { } DECLARE_INSTRUCTION(Goto) @@ -247,6 +310,19 @@ class HGoto : public HTemplateInstruction<0> { DISALLOW_COPY_AND_ASSIGN(HGoto); }; +// Conditional branch. A block ending with an HIf instruction must have +// two successors. +// TODO: Make it take an input. +class HIf : public HTemplateInstruction<0> { + public: + HIf() { } + + DECLARE_INSTRUCTION(If) + + private: + DISALLOW_COPY_AND_ASSIGN(HIf); +}; + class HGraphVisitor : public ValueObject { public: explicit HGraphVisitor(HGraph* graph) : graph_(graph) { } diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h index 62a5a2c0e0..d4b61651dc 100644 --- a/compiler/optimizing/pretty_printer.h +++ b/compiler/optimizing/pretty_printer.h @@ -34,6 +34,24 @@ class HPrettyPrinter : public HGraphVisitor { virtual void VisitBasicBlock(HBasicBlock* block) { PrintString("BasicBlock "); PrintInt(block->block_id()); + const GrowableArray<HBasicBlock*>* blocks = block->predecessors(); + if (!blocks->IsEmpty()) { + PrintString(", pred: "); + for (size_t i = 0; i < blocks->Size() -1; i++) { + PrintInt(blocks->Get(i)->block_id()); + PrintString(", "); + } + PrintInt(blocks->Peek()->block_id()); + } + blocks = block->successors(); + if (!blocks->IsEmpty()) { + PrintString(", succ: "); + for (size_t i = 0; i < blocks->Size() - 1; i++) { + PrintInt(blocks->Get(i)->block_id()); + PrintString(", "); + } + PrintInt(blocks->Peek()->block_id()); + } PrintNewLine(); HGraphVisitor::VisitBasicBlock(block); } diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc index 81a0d915aa..4c8201e53e 100644 --- a/compiler/optimizing/pretty_printer_test.cc +++ b/compiler/optimizing/pretty_printer_test.cc @@ -25,19 +25,10 @@ namespace art { -const uint16_t data[] = { Instruction::RETURN_VOID }; - -const char* expected = - "BasicBlock 0\n" - " Goto\n" - "BasicBlock 1\n" - " ReturnVoid\n" - "BasicBlock 2\n" - " Exit\n"; - class StringPrettyPrinter : public HPrettyPrinter { public: - explicit StringPrettyPrinter(HGraph* graph) : HPrettyPrinter(graph), str_("") { } + explicit StringPrettyPrinter(HGraph* graph) + : HPrettyPrinter(graph), str_(""), current_block_(nullptr) { } virtual void PrintInt(int value) { str_ += StringPrintf("%d", value); @@ -55,33 +46,213 @@ class StringPrettyPrinter : public HPrettyPrinter { std::string str() const { return str_; } + virtual void VisitBasicBlock(HBasicBlock* block) { + current_block_ = block; + HPrettyPrinter::VisitBasicBlock(block); + } + + virtual void VisitGoto(HGoto* gota) { + str_ += " Goto "; + PrintInt(current_block_->successors()->Get(0)->block_id()); + PrintNewLine(); + } + private: std::string str_; + HBasicBlock* current_block_; + DISALLOW_COPY_AND_ASSIGN(StringPrettyPrinter); }; -TEST(OptimizerTest, ReturnVoid) { + +static void TestCode(const uint16_t* data, int length, const char* expected) { ArenaPool pool; ArenaAllocator allocator(&pool); HGraphBuilder builder(&allocator); - HGraph* graph = builder.BuildGraph(data, data + 1); + HGraph* graph = builder.BuildGraph(data, data + length); ASSERT_NE(graph, nullptr); StringPrettyPrinter printer(graph); printer.VisitInsertionOrder(); ASSERT_STREQ(expected, printer.str().c_str()); +} + +TEST(PrettyPrinterTest, ReturnVoid) { + const uint16_t data[] = { Instruction::RETURN_VOID }; + + const char* expected = + "BasicBlock 0, succ: 1\n" + " Goto 1\n" + "BasicBlock 1, pred: 0, succ: 2\n" + " ReturnVoid\n" + "BasicBlock 2, pred: 1\n" + " Exit\n"; + + TestCode(data, sizeof(data) / sizeof(uint16_t), expected); +} + +TEST(PrettyPrinterTest, CFG1) { + const char* expected = + "BasicBlock 0, succ: 1\n" + " Goto 1\n" + "BasicBlock 1, pred: 0, succ: 2\n" + " Goto 2\n" + "BasicBlock 2, pred: 1, succ: 3\n" + " ReturnVoid\n" + "BasicBlock 3, pred: 2\n" + " Exit\n"; + + const uint16_t data[] = { + Instruction::GOTO | 0x100, + Instruction::RETURN_VOID + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), expected); +} + +TEST(PrettyPrinterTest, CFG2) { + const char* expected = + "BasicBlock 0, succ: 1\n" + " Goto 1\n" + "BasicBlock 1, pred: 0, succ: 2\n" + " Goto 2\n" + "BasicBlock 2, pred: 1, succ: 3\n" + " Goto 3\n" + "BasicBlock 3, pred: 2, succ: 4\n" + " ReturnVoid\n" + "BasicBlock 4, pred: 3\n" + " Exit\n"; + + const uint16_t data[] = { + Instruction::GOTO | 0x100, + Instruction::GOTO | 0x100, + Instruction::RETURN_VOID + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), expected); +} + +TEST(PrettyPrinterTest, CFG3) { + const char* expected = + "BasicBlock 0, succ: 1\n" + " Goto 1\n" + "BasicBlock 1, pred: 0, succ: 3\n" + " Goto 3\n" + "BasicBlock 2, pred: 3, succ: 4\n" + " ReturnVoid\n" + "BasicBlock 3, pred: 1, succ: 2\n" + " Goto 2\n" + "BasicBlock 4, pred: 2\n" + " Exit\n"; + + const uint16_t data1[] = { + Instruction::GOTO | 0x200, + Instruction::RETURN_VOID, + Instruction::GOTO | 0xFF00 + }; + + TestCode(data1, sizeof(data1) / sizeof(uint16_t), expected); + + const uint16_t data2[] = { + Instruction::GOTO_16, 3, + Instruction::RETURN_VOID, + Instruction::GOTO_16, 0xFFFF + }; + + TestCode(data2, sizeof(data2) / sizeof(uint16_t), expected); + + const uint16_t data3[] = { + Instruction::GOTO_32, 4, 0, + Instruction::RETURN_VOID, + Instruction::GOTO_32, 0xFFFF, 0xFFFF + }; + + TestCode(data3, sizeof(data3) / sizeof(uint16_t), expected); +} + +TEST(PrettyPrinterTest, CFG4) { + const char* expected = + "BasicBlock 0, succ: 1\n" + " Goto 1\n" + "BasicBlock 1, pred: 0, 1, succ: 1\n" + " Goto 1\n" + "BasicBlock 2\n" + " Exit\n"; + + const uint16_t data1[] = { + Instruction::NOP, + Instruction::GOTO | 0xFF00 + }; + + TestCode(data1, sizeof(data1) / sizeof(uint16_t), expected); + + const uint16_t data2[] = { + Instruction::GOTO_32, 0, 0 + }; + + TestCode(data2, sizeof(data2) / sizeof(uint16_t), expected); +} + +TEST(PrettyPrinterTest, CFG5) { + const char* expected = + "BasicBlock 0, succ: 1\n" + " Goto 1\n" + "BasicBlock 1, pred: 0, 2, succ: 3\n" + " ReturnVoid\n" + "BasicBlock 2, succ: 1\n" + " Goto 1\n" + "BasicBlock 3, pred: 1\n" + " Exit\n"; - const GrowableArray<HBasicBlock*>* blocks = graph->blocks(); - ASSERT_EQ(blocks->Get(0)->predecessors()->Size(), (size_t)0); - ASSERT_EQ(blocks->Get(1)->predecessors()->Size(), (size_t)1); - ASSERT_EQ(blocks->Get(1)->predecessors()->Get(0), blocks->Get(0)); - ASSERT_EQ(blocks->Get(2)->predecessors()->Size(), (size_t)1); - ASSERT_EQ(blocks->Get(2)->predecessors()->Get(0), blocks->Get(1)); - - ASSERT_EQ(blocks->Get(0)->successors()->Size(), (size_t)1); - ASSERT_EQ(blocks->Get(1)->successors()->Get(0), blocks->Get(2)); - ASSERT_EQ(blocks->Get(1)->successors()->Size(), (size_t)1); - ASSERT_EQ(blocks->Get(1)->successors()->Get(0), blocks->Get(2)); - ASSERT_EQ(blocks->Get(2)->successors()->Size(), (size_t)0); + const uint16_t data[] = { + Instruction::RETURN_VOID, + Instruction::GOTO | 0x100, + Instruction::GOTO | 0xFE00 + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), expected); } +TEST(OptimizerTest, CFG6) { + const char* expected = + "BasicBlock 0, succ: 1\n" + " Goto 1\n" + "BasicBlock 1, pred: 0, succ: 3, 2\n" + " If\n" + "BasicBlock 2, pred: 1, succ: 3\n" + " Goto 3\n" + "BasicBlock 3, pred: 1, 2, succ: 4\n" + " ReturnVoid\n" + "BasicBlock 4, pred: 3\n" + " Exit\n"; + + const uint16_t data[] = { + Instruction::IF_EQ, 3, + Instruction::GOTO | 0x100, + Instruction::RETURN_VOID + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), expected); +} + +TEST(OptimizerTest, CFG7) { + const char* expected = + "BasicBlock 0, succ: 1\n" + " Goto 1\n" + "BasicBlock 1, pred: 0, succ: 3, 2\n" + " If\n" + "BasicBlock 2, pred: 1, 3, succ: 3\n" + " Goto 3\n" + "BasicBlock 3, pred: 1, 2, succ: 2\n" + " Goto 2\n" + "BasicBlock 4\n" + " Exit\n"; + + const uint16_t data[] = { + Instruction::IF_EQ, 3, + Instruction::GOTO | 0x100, + Instruction::GOTO | 0xFF00 + }; + + TestCode(data, sizeof(data) / sizeof(uint16_t), expected); +} } // namespace art diff --git a/compiler/output_stream_test.cc b/compiler/output_stream_test.cc index a957ee375a..290bf2519d 100644 --- a/compiler/output_stream_test.cc +++ b/compiler/output_stream_test.cc @@ -14,15 +14,16 @@ * limitations under the License. */ -#include "base/logging.h" -#include "buffered_output_stream.h" -#include "common_test.h" #include "file_output_stream.h" #include "vector_output_stream.h" +#include "base/logging.h" +#include "buffered_output_stream.h" +#include "common_runtime_test.h" + namespace art { -class OutputStreamTest : public CommonTest { +class OutputStreamTest : public CommonRuntimeTest { protected: void CheckOffset(off_t expected) { off_t actual = output_stream_->Seek(0, kSeekCurrent); diff --git a/compiler/sea_ir/ir/regions_test.cc b/compiler/sea_ir/ir/regions_test.cc index 8ca51e4851..95bd31075e 100644 --- a/compiler/sea_ir/ir/regions_test.cc +++ b/compiler/sea_ir/ir/regions_test.cc @@ -14,15 +14,14 @@ * limitations under the License. */ -#include "common_test.h" +#include "common_compiler_test.h" #include "sea_ir/ir/sea.h" using utils::ScopedHashtable; namespace sea_ir { -class RegionsTest : public art::CommonTest { -}; +class RegionsTest : public art::CommonCompilerTest {}; TEST_F(RegionsTest, Basics) { sea_ir::SeaGraph sg(*java_lang_dex_file_); diff --git a/compiler/sea_ir/types/type_data_test.cc b/compiler/sea_ir/types/type_data_test.cc index f7a5362fa4..42c6973c61 100644 --- a/compiler/sea_ir/types/type_data_test.cc +++ b/compiler/sea_ir/types/type_data_test.cc @@ -14,13 +14,12 @@ * limitations under the License. */ -#include "common_test.h" +#include "common_compiler_test.h" #include "sea_ir/types/types.h" namespace sea_ir { -class TypeDataTest : public art::CommonTest { -}; +class TypeDataTest : public art::CommonCompilerTest {}; TEST_F(TypeDataTest, Basics) { TypeData td; diff --git a/compiler/sea_ir/types/type_inference_visitor_test.cc b/compiler/sea_ir/types/type_inference_visitor_test.cc index 77acb3d277..ccb699137e 100644 --- a/compiler/sea_ir/types/type_inference_visitor_test.cc +++ b/compiler/sea_ir/types/type_inference_visitor_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "common_test.h" +#include "common_compiler_test.h" #include "sea_ir/types/type_inference_visitor.h" #include "sea_ir/ir/sea.h" @@ -31,8 +31,7 @@ class TestInstructionNode:public InstructionNode { std::vector<InstructionNode*> producers_; }; -class TypeInferenceVisitorTest : public art::CommonTest { -}; +class TypeInferenceVisitorTest : public art::CommonCompilerTest {}; TEST_F(TypeInferenceVisitorTest, MergeIntWithByte) { TypeData td; diff --git a/compiler/utils/arena_allocator_test.cc b/compiler/utils/arena_allocator_test.cc index b76fe74988..71565407a2 100644 --- a/compiler/utils/arena_allocator_test.cc +++ b/compiler/utils/arena_allocator_test.cc @@ -14,9 +14,9 @@ * limitations under the License. */ -#include "dex/arena_bit_vector.h" #include "gtest/gtest.h" #include "utils/arena_allocator.h" +#include "utils/arena_bit_vector.h" namespace art { diff --git a/compiler/dex/arena_bit_vector.cc b/compiler/utils/arena_bit_vector.cc index 1b37b7149e..220ff14baa 100644 --- a/compiler/dex/arena_bit_vector.cc +++ b/compiler/utils/arena_bit_vector.cc @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "compiler_internals.h" -#include "dex_file-inl.h" +#include "arena_allocator.h" +#include "arena_bit_vector.h" namespace art { @@ -42,16 +42,8 @@ class ArenaBitVectorAllocator : public Allocator { ArenaBitVector::ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits, bool expandable, OatBitMapKind kind) - : BitVector(start_bits, expandable, new (arena) ArenaBitVectorAllocator(arena)), kind_(kind) {} - -BasicBlock* ArenaBitVector::BasicBlockIterator::Next() { - int idx = internal_iterator_.Next(); - - if (idx == -1) { - return nullptr; - } - - return mir_graph_->GetBasicBlock(idx); + : BitVector(start_bits, expandable, new (arena) ArenaBitVectorAllocator(arena)), kind_(kind) { + UNUSED(kind_); } } // namespace art diff --git a/compiler/dex/arena_bit_vector.h b/compiler/utils/arena_bit_vector.h index cdd5c68111..6c1461727a 100644 --- a/compiler/dex/arena_bit_vector.h +++ b/compiler/utils/arena_bit_vector.h @@ -14,51 +14,42 @@ * limitations under the License. */ -#ifndef ART_COMPILER_DEX_ARENA_BIT_VECTOR_H_ -#define ART_COMPILER_DEX_ARENA_BIT_VECTOR_H_ +#ifndef ART_COMPILER_UTILS_ARENA_BIT_VECTOR_H_ +#define ART_COMPILER_UTILS_ARENA_BIT_VECTOR_H_ #include "base/bit_vector.h" -#include "compiler_enums.h" #include "utils/arena_allocator.h" -#include "compiler_ir.h" namespace art { -// Forward declaration -class MIRGraph; +// Type of growable bitmap for memory tuning. +enum OatBitMapKind { + kBitMapMisc = 0, + kBitMapUse, + kBitMapDef, + kBitMapLiveIn, + kBitMapBMatrix, + kBitMapDominators, + kBitMapIDominated, + kBitMapDomFrontier, + kBitMapPhi, + kBitMapTmpBlocks, + kBitMapInputBlocks, + kBitMapRegisterV, + kBitMapTempSSARegisterV, + kBitMapNullCheck, + kBitMapTmpBlockV, + kBitMapPredecessors, + kNumBitMapKinds +}; + +std::ostream& operator<<(std::ostream& os, const OatBitMapKind& kind); /* * A BitVector implementation that uses Arena allocation. */ class ArenaBitVector : public BitVector { public: - /** - * @class BasicBlockIterator - * @brief Helper class to get the BasicBlocks when iterating through the ArenaBitVector. - */ - class BasicBlockIterator { - public: - explicit BasicBlockIterator(ArenaBitVector* bv, MIRGraph* mir_graph) - : mir_graph_(mir_graph), - internal_iterator_(bv) {} - - explicit BasicBlockIterator(ArenaBitVector* bv, CompilationUnit* c_unit) - : mir_graph_(c_unit->mir_graph.get()), - internal_iterator_(bv) {} - - BasicBlock* Next(); - - static void* operator new(size_t size, ArenaAllocator* arena) { - return arena->Alloc(sizeof(ArenaBitVector::BasicBlockIterator), - ArenaAllocator::kAllocGrowableArray); - }; - static void operator delete(void* p) {} // Nop. - - private: - MIRGraph* const mir_graph_; - Iterator internal_iterator_; - }; - ArenaBitVector(ArenaAllocator* arena, uint32_t start_bits, bool expandable, OatBitMapKind kind = kBitMapMisc); ~ArenaBitVector() {} @@ -75,4 +66,4 @@ class ArenaBitVector : public BitVector { } // namespace art -#endif // ART_COMPILER_DEX_ARENA_BIT_VECTOR_H_ +#endif // ART_COMPILER_UTILS_ARENA_BIT_VECTOR_H_ diff --git a/compiler/utils/arm/managed_register_arm.cc b/compiler/utils/arm/managed_register_arm.cc index 57c23059de..1fdc110dcf 100644 --- a/compiler/utils/arm/managed_register_arm.cc +++ b/compiler/utils/arm/managed_register_arm.cc @@ -21,16 +21,6 @@ namespace art { namespace arm { -// We need all registers for caching of locals. -// Register R9 .. R15 are reserved. -static const int kNumberOfAvailableCoreRegisters = (R8 - R0) + 1; -static const int kNumberOfAvailableSRegisters = kNumberOfSRegisters; -static const int kNumberOfAvailableDRegisters = kNumberOfDRegisters; -static const int kNumberOfAvailableOverlappingDRegisters = - kNumberOfOverlappingDRegisters; -static const int kNumberOfAvailableRegisterPairs = kNumberOfRegisterPairs; - - // Returns true if this managed-register overlaps the other managed-register. bool ArmManagedRegister::Overlaps(const ArmManagedRegister& other) const { if (IsNoRegister() || other.IsNoRegister()) return false; diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h index c9be4edbf8..296254d140 100644 --- a/compiler/utils/assembler.h +++ b/compiler/utils/assembler.h @@ -282,7 +282,9 @@ class AssemblerBuffer { byte* cursor_; byte* limit_; AssemblerFixup* fixup_; +#ifndef NDEBUG bool fixups_processed_; +#endif // Head of linked list of slow paths SlowPath* slow_path_; diff --git a/compiler/utils/growable_array.h b/compiler/utils/growable_array.h index b59187032e..82b6a607e7 100644 --- a/compiler/utils/growable_array.h +++ b/compiler/utils/growable_array.h @@ -161,6 +161,18 @@ class GrowableArray { size_t Size() const { return num_used_; } + bool IsEmpty() const { return num_used_ == 0; } + + T Pop() { + DCHECK_GE(num_used_, (size_t)0); + return elem_list_[--num_used_]; + } + + T Peek() const { + DCHECK_GE(num_used_, (size_t)0); + return elem_list_[num_used_ - 1]; + } + void SetSize(size_t new_size) { Resize(new_size); num_used_ = new_size; diff --git a/compiler/utils/mips/managed_register_mips.cc b/compiler/utils/mips/managed_register_mips.cc index 195dafb0a1..5a8c0481a5 100644 --- a/compiler/utils/mips/managed_register_mips.cc +++ b/compiler/utils/mips/managed_register_mips.cc @@ -21,17 +21,6 @@ namespace art { namespace mips { -// These core registers are never available for allocation. -static const Register kReservedCoreRegistersArray[] = { S0, S1 }; - -// We need all registers for caching. -static const int kNumberOfAvailableCoreRegisters = (S7 - T0) + 1; -static const int kNumberOfAvailableFRegisters = kNumberOfFRegisters; -static const int kNumberOfAvailableDRegisters = kNumberOfDRegisters; -static const int kNumberOfAvailableOverlappingDRegisters = - kNumberOfOverlappingDRegisters; -static const int kNumberOfAvailableRegisterPairs = kNumberOfRegisterPairs; - bool MipsManagedRegister::Overlaps(const MipsManagedRegister& other) const { if (IsNoRegister() || other.IsNoRegister()) return false; CHECK(IsValidManagedRegister()); diff --git a/compiler/utils/scoped_hashtable_test.cc b/compiler/utils/scoped_hashtable_test.cc index 68608f01d1..1c843ebef1 100644 --- a/compiler/utils/scoped_hashtable_test.cc +++ b/compiler/utils/scoped_hashtable_test.cc @@ -14,8 +14,9 @@ * limitations under the License. */ -#include "common_test.h" -#include "utils/scoped_hashtable.h" +#include "scoped_hashtable.h" + +#include "common_runtime_test.h" using utils::ScopedHashtable; @@ -27,8 +28,7 @@ class Value { int value_; }; -class ScopedHashtableTest : public CommonTest { -}; +class ScopedHashtableTest : public testing::Test {}; TEST_F(ScopedHashtableTest, Basics) { ScopedHashtable<int, Value*> sht; diff --git a/compiler/utils/x86/managed_register_x86.cc b/compiler/utils/x86/managed_register_x86.cc index 4697d06136..7fae7a8b6f 100644 --- a/compiler/utils/x86/managed_register_x86.cc +++ b/compiler/utils/x86/managed_register_x86.cc @@ -21,19 +21,6 @@ namespace art { namespace x86 { -// These cpu registers are never available for allocation. -static const Register kReservedCpuRegistersArray[] = { ESP }; - - -// We reduce the number of available registers for allocation in debug-code -// mode in order to increase register pressure. - -// We need all registers for caching. -static const int kNumberOfAvailableCpuRegisters = kNumberOfCpuRegisters; -static const int kNumberOfAvailableXmmRegisters = kNumberOfXmmRegisters; -static const int kNumberOfAvailableRegisterPairs = kNumberOfRegisterPairs; - - // Define register pairs. // This list must be kept in sync with the RegisterPair enum. #define REGISTER_PAIR_LIST(P) \ |