diff options
Diffstat (limited to 'compiler')
105 files changed, 2157 insertions, 11763 deletions
diff --git a/compiler/Android.bp b/compiler/Android.bp index c798d9782a..c50c1978ac 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -38,7 +38,6 @@ art_cc_defaults { "driver/dex_compilation_unit.cc", "linker/buffered_output_stream.cc", "linker/file_output_stream.cc", - "linker/multi_oat_relative_patcher.cc", "linker/output_stream.cc", "linker/vector_output_stream.cc", "linker/relative_patcher.cc", @@ -95,10 +94,6 @@ art_cc_defaults { "utils/jni_macro_assembler.cc", "utils/swap_space.cc", "compiler.cc", - "elf_writer.cc", - "elf_writer_quick.cc", - "image_writer.cc", - "oat_writer.cc", ], codegen: { @@ -198,19 +193,10 @@ art_cc_defaults { generated_sources: ["art_compiler_operator_srcs"], shared_libs: [ "libbase", - "liblz4", "liblzma", ], include_dirs: ["art/disassembler"], export_include_dirs: ["."], - - // For SHA-1 checksumming of build ID - static: { - whole_static_libs: ["libcrypto"], - }, - shared: { - shared_libs: ["libcrypto"], - }, } gensrcs { @@ -218,11 +204,10 @@ gensrcs { cmd: "$(location generate-operator-out.py) art/compiler $(in) > $(out)", tool_files: ["generate-operator-out.py"], srcs: [ - "compiled_method.h", "dex/dex_to_dex_compiler.h", "driver/compiler_driver.h", "driver/compiler_options.h", - "image_writer.h", + "linker/linker_patch.h", "optimizing/locations.h", "utils/arm/constants_arm.h", @@ -265,7 +250,6 @@ art_cc_library { }, shared_libs: [ "libart", - "libart-dexlayout", ], } @@ -305,7 +289,6 @@ art_cc_library { }, shared_libs: [ "libartd", - "libartd-dexlayout" ], } @@ -327,20 +310,16 @@ art_cc_test { "art_gtest_defaults", ], srcs: [ - "compiled_method_test.cc", "debug/dwarf/dwarf_test.cc", + "debug/src_map_elem_test.cc", "dex/dex_to_dex_decompiler_test.cc", "driver/compiled_method_storage_test.cc", "driver/compiler_driver_test.cc", - "elf_writer_test.cc", "exception_test.cc", - "image_test.cc", - "image_write_read_test.cc", "jni/jni_compiler_test.cc", + "linker/linker_patch_test.cc", "linker/method_bss_mapping_encoder_test.cc", - "linker/multi_oat_relative_patcher_test.cc", "linker/output_stream_test.cc", - "oat_test.cc", "optimizing/bounds_check_elimination_test.cc", "optimizing/dominator_test.cc", "optimizing/find_loops_test.cc", diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 0d38620b1a..500fc4ae9a 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -22,7 +22,7 @@ #include "base/callee_save_type.h" #include "base/enums.h" #include "class_linker.h" -#include "compiled_method.h" +#include "compiled_method-inl.h" #include "dex/quick_compiler_callbacks.h" #include "dex/verification_results.h" #include "driver/compiler_driver.h" diff --git a/compiler/compiled_method-inl.h b/compiler/compiled_method-inl.h new file mode 100644 index 0000000000..c43274782e --- /dev/null +++ b/compiler/compiled_method-inl.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_COMPILED_METHOD_INL_H_ +#define ART_COMPILER_COMPILED_METHOD_INL_H_ + +#include "compiled_method.h" + +#include "base/array_ref.h" +#include "base/length_prefixed_array.h" +#include "linker/linker_patch.h" + +namespace art { + +inline ArrayRef<const uint8_t> CompiledCode::GetQuickCode() const { + return GetArray(quick_code_); +} + +template <typename T> +inline ArrayRef<const T> CompiledCode::GetArray(const LengthPrefixedArray<T>* array) { + if (array == nullptr) { + return ArrayRef<const T>(); + } + DCHECK_NE(array->size(), 0u); + return ArrayRef<const T>(&array->At(0), array->size()); +} + +inline ArrayRef<const uint8_t> CompiledMethod::GetMethodInfo() const { + return GetArray(method_info_); +} + +inline ArrayRef<const uint8_t> CompiledMethod::GetVmapTable() const { + return GetArray(vmap_table_); +} + +inline ArrayRef<const uint8_t> CompiledMethod::GetCFIInfo() const { + return GetArray(cfi_info_); +} + +inline ArrayRef<const linker::LinkerPatch> CompiledMethod::GetPatches() const { + return GetArray(patches_); +} + +} // namespace art + +#endif // ART_COMPILER_COMPILED_METHOD_INL_H_ diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc index 0d9021fcfb..111469fe89 100644 --- a/compiler/compiled_method.cc +++ b/compiler/compiled_method.cc @@ -22,7 +22,8 @@ namespace art { -CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set, +CompiledCode::CompiledCode(CompilerDriver* compiler_driver, + InstructionSet instruction_set, const ArrayRef<const uint8_t>& quick_code) : compiler_driver_(compiler_driver), instruction_set_(instruction_set), @@ -77,8 +78,7 @@ size_t CompiledCode::CodeDelta(InstructionSet instruction_set) { } } -const void* CompiledCode::CodePointer(const void* code_pointer, - InstructionSet instruction_set) { +const void* CompiledCode::CodePointer(const void* code_pointer, InstructionSet instruction_set) { switch (instruction_set) { case kArm: case kArm64: @@ -108,7 +108,7 @@ CompiledMethod::CompiledMethod(CompilerDriver* driver, const ArrayRef<const uint8_t>& method_info, const ArrayRef<const uint8_t>& vmap_table, const ArrayRef<const uint8_t>& cfi_info, - const ArrayRef<const LinkerPatch>& patches) + const ArrayRef<const linker::LinkerPatch>& patches) : CompiledCode(driver, instruction_set, quick_code), frame_size_in_bytes_(frame_size_in_bytes), core_spill_mask_(core_spill_mask), @@ -129,7 +129,7 @@ CompiledMethod* CompiledMethod::SwapAllocCompiledMethod( const ArrayRef<const uint8_t>& method_info, const ArrayRef<const uint8_t>& vmap_table, const ArrayRef<const uint8_t>& cfi_info, - const ArrayRef<const LinkerPatch>& patches) { + const ArrayRef<const linker::LinkerPatch>& patches) { SwapAllocator<CompiledMethod> alloc(driver->GetCompiledMethodStorage()->GetSwapSpaceAllocator()); CompiledMethod* ret = alloc.allocate(1); alloc.construct(ret, diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 5ef6cbff78..892bc592db 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -17,27 +17,28 @@ #ifndef ART_COMPILER_COMPILED_METHOD_H_ #define ART_COMPILER_COMPILED_METHOD_H_ -#include <iosfwd> #include <memory> #include <string> #include <vector> #include "arch/instruction_set.h" -#include "base/array_ref.h" -#include "base/bit_utils.h" -#include "base/length_prefixed_array.h" -#include "dex_file_types.h" -#include "method_reference.h" namespace art { +template <typename T> class ArrayRef; class CompilerDriver; class CompiledMethodStorage; +template<typename T> class LengthPrefixedArray; + +namespace linker { +class LinkerPatch; +} // namespace linker class CompiledCode { public: // For Quick to supply an code blob - CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set, + CompiledCode(CompilerDriver* compiler_driver, + InstructionSet instruction_set, const ArrayRef<const uint8_t>& quick_code); virtual ~CompiledCode(); @@ -46,9 +47,7 @@ class CompiledCode { return instruction_set_; } - ArrayRef<const uint8_t> GetQuickCode() const { - return GetArray(quick_code_); - } + ArrayRef<const uint8_t> GetQuickCode() const; bool operator==(const CompiledCode& rhs) const; @@ -66,18 +65,11 @@ class CompiledCode { // Returns a pointer suitable for invoking the code at the argument // code_pointer address. Mainly to cope with kThumb2 where the // lower bit must be set to indicate Thumb mode. - static const void* CodePointer(const void* code_pointer, - InstructionSet instruction_set); + static const void* CodePointer(const void* code_pointer, InstructionSet instruction_set); protected: template <typename T> - static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array) { - if (array == nullptr) { - return ArrayRef<const T>(); - } - DCHECK_NE(array->size(), 0u); - return ArrayRef<const T>(&array->At(0), array->size()); - } + static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array); CompilerDriver* GetCompilerDriver() { return compiler_driver_; @@ -92,298 +84,6 @@ class CompiledCode { const LengthPrefixedArray<uint8_t>* const quick_code_; }; -class SrcMapElem { - public: - uint32_t from_; - int32_t to_; -}; - -inline bool operator<(const SrcMapElem& lhs, const SrcMapElem& rhs) { - if (lhs.from_ != rhs.from_) { - return lhs.from_ < rhs.from_; - } - return lhs.to_ < rhs.to_; -} - -inline bool operator==(const SrcMapElem& lhs, const SrcMapElem& rhs) { - return lhs.from_ == rhs.from_ && lhs.to_ == rhs.to_; -} - -class LinkerPatch { - public: - // Note: We explicitly specify the underlying type of the enum because GCC - // would otherwise select a bigger underlying type and then complain that - // 'art::LinkerPatch::patch_type_' is too small to hold all - // values of 'enum class art::LinkerPatch::Type' - // which is ridiculous given we have only a handful of values here. If we - // choose to squeeze the Type into fewer than 8 bits, we'll have to declare - // patch_type_ as an uintN_t and do explicit static_cast<>s. - enum class Type : uint8_t { - kMethodRelative, // NOTE: Actual patching is instruction_set-dependent. - kMethodBssEntry, // NOTE: Actual patching is instruction_set-dependent. - kCall, - kCallRelative, // NOTE: Actual patching is instruction_set-dependent. - kTypeRelative, // NOTE: Actual patching is instruction_set-dependent. - kTypeClassTable, // NOTE: Actual patching is instruction_set-dependent. - kTypeBssEntry, // NOTE: Actual patching is instruction_set-dependent. - kStringRelative, // NOTE: Actual patching is instruction_set-dependent. - kStringInternTable, // NOTE: Actual patching is instruction_set-dependent. - kStringBssEntry, // NOTE: Actual patching is instruction_set-dependent. - kBakerReadBarrierBranch, // NOTE: Actual patching is instruction_set-dependent. - }; - - static LinkerPatch RelativeMethodPatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t pc_insn_offset, - uint32_t target_method_idx) { - LinkerPatch patch(literal_offset, Type::kMethodRelative, target_dex_file); - patch.method_idx_ = target_method_idx; - patch.pc_insn_offset_ = pc_insn_offset; - return patch; - } - - static LinkerPatch MethodBssEntryPatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t pc_insn_offset, - uint32_t target_method_idx) { - LinkerPatch patch(literal_offset, Type::kMethodBssEntry, target_dex_file); - patch.method_idx_ = target_method_idx; - patch.pc_insn_offset_ = pc_insn_offset; - return patch; - } - - static LinkerPatch CodePatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t target_method_idx) { - LinkerPatch patch(literal_offset, Type::kCall, target_dex_file); - patch.method_idx_ = target_method_idx; - return patch; - } - - static LinkerPatch RelativeCodePatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t target_method_idx) { - LinkerPatch patch(literal_offset, Type::kCallRelative, target_dex_file); - patch.method_idx_ = target_method_idx; - return patch; - } - - static LinkerPatch RelativeTypePatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t pc_insn_offset, - uint32_t target_type_idx) { - LinkerPatch patch(literal_offset, Type::kTypeRelative, target_dex_file); - patch.type_idx_ = target_type_idx; - patch.pc_insn_offset_ = pc_insn_offset; - return patch; - } - - static LinkerPatch TypeClassTablePatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t pc_insn_offset, - uint32_t target_type_idx) { - LinkerPatch patch(literal_offset, Type::kTypeClassTable, target_dex_file); - patch.type_idx_ = target_type_idx; - patch.pc_insn_offset_ = pc_insn_offset; - return patch; - } - - static LinkerPatch TypeBssEntryPatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t pc_insn_offset, - uint32_t target_type_idx) { - LinkerPatch patch(literal_offset, Type::kTypeBssEntry, target_dex_file); - patch.type_idx_ = target_type_idx; - patch.pc_insn_offset_ = pc_insn_offset; - return patch; - } - - static LinkerPatch RelativeStringPatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t pc_insn_offset, - uint32_t target_string_idx) { - LinkerPatch patch(literal_offset, Type::kStringRelative, target_dex_file); - patch.string_idx_ = target_string_idx; - patch.pc_insn_offset_ = pc_insn_offset; - return patch; - } - - static LinkerPatch StringInternTablePatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t pc_insn_offset, - uint32_t target_string_idx) { - LinkerPatch patch(literal_offset, Type::kStringInternTable, target_dex_file); - patch.string_idx_ = target_string_idx; - patch.pc_insn_offset_ = pc_insn_offset; - return patch; - } - - static LinkerPatch StringBssEntryPatch(size_t literal_offset, - const DexFile* target_dex_file, - uint32_t pc_insn_offset, - uint32_t target_string_idx) { - LinkerPatch patch(literal_offset, Type::kStringBssEntry, target_dex_file); - patch.string_idx_ = target_string_idx; - patch.pc_insn_offset_ = pc_insn_offset; - return patch; - } - - static LinkerPatch BakerReadBarrierBranchPatch(size_t literal_offset, - uint32_t custom_value1 = 0u, - uint32_t custom_value2 = 0u) { - LinkerPatch patch(literal_offset, Type::kBakerReadBarrierBranch, nullptr); - patch.baker_custom_value1_ = custom_value1; - patch.baker_custom_value2_ = custom_value2; - return patch; - } - - LinkerPatch(const LinkerPatch& other) = default; - LinkerPatch& operator=(const LinkerPatch& other) = default; - - size_t LiteralOffset() const { - return literal_offset_; - } - - Type GetType() const { - return patch_type_; - } - - bool IsPcRelative() const { - switch (GetType()) { - case Type::kMethodRelative: - case Type::kMethodBssEntry: - case Type::kCallRelative: - case Type::kTypeRelative: - case Type::kTypeClassTable: - case Type::kTypeBssEntry: - case Type::kStringRelative: - case Type::kStringInternTable: - case Type::kStringBssEntry: - case Type::kBakerReadBarrierBranch: - return true; - default: - return false; - } - } - - MethodReference TargetMethod() const { - DCHECK(patch_type_ == Type::kMethodRelative || - patch_type_ == Type::kMethodBssEntry || - patch_type_ == Type::kCall || - patch_type_ == Type::kCallRelative); - return MethodReference(target_dex_file_, method_idx_); - } - - const DexFile* TargetTypeDexFile() const { - DCHECK(patch_type_ == Type::kTypeRelative || - patch_type_ == Type::kTypeClassTable || - patch_type_ == Type::kTypeBssEntry); - return target_dex_file_; - } - - dex::TypeIndex TargetTypeIndex() const { - DCHECK(patch_type_ == Type::kTypeRelative || - patch_type_ == Type::kTypeClassTable || - patch_type_ == Type::kTypeBssEntry); - return dex::TypeIndex(type_idx_); - } - - const DexFile* TargetStringDexFile() const { - DCHECK(patch_type_ == Type::kStringRelative || - patch_type_ == Type::kStringInternTable || - patch_type_ == Type::kStringBssEntry); - return target_dex_file_; - } - - dex::StringIndex TargetStringIndex() const { - DCHECK(patch_type_ == Type::kStringRelative || - patch_type_ == Type::kStringInternTable || - patch_type_ == Type::kStringBssEntry); - return dex::StringIndex(string_idx_); - } - - uint32_t PcInsnOffset() const { - DCHECK(patch_type_ == Type::kMethodRelative || - patch_type_ == Type::kMethodBssEntry || - patch_type_ == Type::kTypeRelative || - patch_type_ == Type::kTypeClassTable || - patch_type_ == Type::kTypeBssEntry || - patch_type_ == Type::kStringRelative || - patch_type_ == Type::kStringInternTable || - patch_type_ == Type::kStringBssEntry); - return pc_insn_offset_; - } - - uint32_t GetBakerCustomValue1() const { - DCHECK(patch_type_ == Type::kBakerReadBarrierBranch); - return baker_custom_value1_; - } - - uint32_t GetBakerCustomValue2() const { - DCHECK(patch_type_ == Type::kBakerReadBarrierBranch); - return baker_custom_value2_; - } - - private: - LinkerPatch(size_t literal_offset, Type patch_type, const DexFile* target_dex_file) - : target_dex_file_(target_dex_file), - literal_offset_(literal_offset), - patch_type_(patch_type) { - cmp1_ = 0u; - cmp2_ = 0u; - // The compiler rejects methods that are too big, so the compiled code - // of a single method really shouln't be anywhere close to 16MiB. - DCHECK(IsUint<24>(literal_offset)); - } - - const DexFile* target_dex_file_; - // TODO: Clean up naming. Some patched locations are literals but others are not. - uint32_t literal_offset_ : 24; // Method code size up to 16MiB. - Type patch_type_ : 8; - union { - uint32_t cmp1_; // Used for relational operators. - uint32_t method_idx_; // Method index for Call/Method patches. - uint32_t type_idx_; // Type index for Type patches. - uint32_t string_idx_; // String index for String patches. - uint32_t baker_custom_value1_; - static_assert(sizeof(method_idx_) == sizeof(cmp1_), "needed by relational operators"); - static_assert(sizeof(type_idx_) == sizeof(cmp1_), "needed by relational operators"); - static_assert(sizeof(string_idx_) == sizeof(cmp1_), "needed by relational operators"); - static_assert(sizeof(baker_custom_value1_) == sizeof(cmp1_), "needed by relational operators"); - }; - union { - // Note: To avoid uninitialized padding on 64-bit systems, we use `size_t` for `cmp2_`. - // This allows a hashing function to treat an array of linker patches as raw memory. - size_t cmp2_; // Used for relational operators. - // Literal offset of the insn loading PC (same as literal_offset if it's the same insn, - // may be different if the PC-relative addressing needs multiple insns). - uint32_t pc_insn_offset_; - uint32_t baker_custom_value2_; - static_assert(sizeof(pc_insn_offset_) <= sizeof(cmp2_), "needed by relational operators"); - static_assert(sizeof(baker_custom_value2_) <= sizeof(cmp2_), "needed by relational operators"); - }; - - friend bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs); - friend bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs); -}; -std::ostream& operator<<(std::ostream& os, const LinkerPatch::Type& type); - -inline bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs) { - return lhs.literal_offset_ == rhs.literal_offset_ && - lhs.patch_type_ == rhs.patch_type_ && - lhs.target_dex_file_ == rhs.target_dex_file_ && - lhs.cmp1_ == rhs.cmp1_ && - lhs.cmp2_ == rhs.cmp2_; -} - -inline bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs) { - return (lhs.literal_offset_ != rhs.literal_offset_) ? lhs.literal_offset_ < rhs.literal_offset_ - : (lhs.patch_type_ != rhs.patch_type_) ? lhs.patch_type_ < rhs.patch_type_ - : (lhs.target_dex_file_ != rhs.target_dex_file_) ? lhs.target_dex_file_ < rhs.target_dex_file_ - : (lhs.cmp1_ != rhs.cmp1_) ? lhs.cmp1_ < rhs.cmp1_ - : lhs.cmp2_ < rhs.cmp2_; -} - class CompiledMethod FINAL : public CompiledCode { public: // Constructs a CompiledMethod. @@ -398,7 +98,7 @@ class CompiledMethod FINAL : public CompiledCode { const ArrayRef<const uint8_t>& method_info, const ArrayRef<const uint8_t>& vmap_table, const ArrayRef<const uint8_t>& cfi_info, - const ArrayRef<const LinkerPatch>& patches); + const ArrayRef<const linker::LinkerPatch>& patches); virtual ~CompiledMethod(); @@ -412,7 +112,7 @@ class CompiledMethod FINAL : public CompiledCode { const ArrayRef<const uint8_t>& method_info, const ArrayRef<const uint8_t>& vmap_table, const ArrayRef<const uint8_t>& cfi_info, - const ArrayRef<const LinkerPatch>& patches); + const ArrayRef<const linker::LinkerPatch>& patches); static void ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m); @@ -428,21 +128,13 @@ class CompiledMethod FINAL : public CompiledCode { return fp_spill_mask_; } - ArrayRef<const uint8_t> GetMethodInfo() const { - return GetArray(method_info_); - } + ArrayRef<const uint8_t> GetMethodInfo() const; - ArrayRef<const uint8_t> GetVmapTable() const { - return GetArray(vmap_table_); - } + ArrayRef<const uint8_t> GetVmapTable() const; - ArrayRef<const uint8_t> GetCFIInfo() const { - return GetArray(cfi_info_); - } + ArrayRef<const uint8_t> GetCFIInfo() const; - ArrayRef<const LinkerPatch> GetPatches() const { - return GetArray(patches_); - } + ArrayRef<const linker::LinkerPatch> GetPatches() const; private: // For quick code, the size of the activation used by the code. @@ -458,7 +150,7 @@ class CompiledMethod FINAL : public CompiledCode { // For quick code, a FDE entry for the debug_frame section. const LengthPrefixedArray<uint8_t>* const cfi_info_; // For quick code, linker patches needed by the method. - const LengthPrefixedArray<LinkerPatch>* const patches_; + const LengthPrefixedArray<linker::LinkerPatch>* const patches_; }; } // namespace art diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h index e1f538d9a7..b30ff143d3 100644 --- a/compiler/debug/dwarf/dwarf_test.h +++ b/compiler/debug/dwarf/dwarf_test.h @@ -28,8 +28,8 @@ #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" -#include "elf_builder.h" #include "gtest/gtest.h" +#include "linker/elf_builder.h" #include "linker/file_output_stream.h" #include "os.h" @@ -62,8 +62,8 @@ class DwarfTest : public CommonRuntimeTest { // Write simple elf file with just the DWARF sections. InstructionSet isa = (sizeof(typename ElfTypes::Addr) == 8) ? kX86_64 : kX86; ScratchFile file; - FileOutputStream output_stream(file.GetFile()); - ElfBuilder<ElfTypes> builder(isa, nullptr, &output_stream); + linker::FileOutputStream output_stream(file.GetFile()); + linker::ElfBuilder<ElfTypes> builder(isa, nullptr, &output_stream); builder.Start(); if (!debug_info_data_.empty()) { builder.WriteSection(".debug_info", &debug_info_data_); diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h index f9d33c1c30..6dacdfa48c 100644 --- a/compiler/debug/elf_debug_frame_writer.h +++ b/compiler/debug/elf_debug_frame_writer.h @@ -24,7 +24,7 @@ #include "debug/dwarf/dwarf_constants.h" #include "debug/dwarf/headers.h" #include "debug/method_debug_info.h" -#include "elf_builder.h" +#include "linker/elf_builder.h" namespace art { namespace debug { @@ -168,7 +168,7 @@ static void WriteCIE(InstructionSet isa, } template<typename ElfTypes> -void WriteCFISection(ElfBuilder<ElfTypes>* builder, +void WriteCFISection(linker::ElfBuilder<ElfTypes>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, dwarf::CFIFormat format, bool write_oat_patches) { diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index 6c6bd63b14..2b617273b5 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -29,8 +29,9 @@ #include "debug/method_debug_info.h" #include "dex_file-inl.h" #include "dex_file.h" -#include "elf_builder.h" +#include "heap_poisoning.h" #include "linear_alloc.h" +#include "linker/elf_builder.h" #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/class.h" @@ -67,7 +68,7 @@ class ElfDebugInfoWriter { using Elf_Addr = typename ElfTypes::Addr; public: - explicit ElfDebugInfoWriter(ElfBuilder<ElfTypes>* builder) + explicit ElfDebugInfoWriter(linker::ElfBuilder<ElfTypes>* builder) : builder_(builder), debug_abbrev_(&debug_abbrev_buffer_) { } @@ -92,7 +93,7 @@ class ElfDebugInfoWriter { } private: - ElfBuilder<ElfTypes>* builder_; + linker::ElfBuilder<ElfTypes>* builder_; std::vector<uintptr_t> debug_info_patches_; std::vector<uint8_t> debug_abbrev_buffer_; dwarf::DebugAbbrevWriter<> debug_abbrev_; diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index cdd1e53f94..49d52c45c2 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -20,12 +20,12 @@ #include <unordered_set> #include <vector> -#include "compiled_method.h" #include "debug/dwarf/debug_line_opcode_writer.h" #include "debug/dwarf/headers.h" #include "debug/elf_compilation_unit.h" +#include "debug/src_map_elem.h" #include "dex_file-inl.h" -#include "elf_builder.h" +#include "linker/elf_builder.h" #include "stack_map.h" namespace art { @@ -43,7 +43,7 @@ class ElfDebugLineWriter { using Elf_Addr = typename ElfTypes::Addr; public: - explicit ElfDebugLineWriter(ElfBuilder<ElfTypes>* builder) : builder_(builder) { + explicit ElfDebugLineWriter(linker::ElfBuilder<ElfTypes>* builder) : builder_(builder) { } void Start() { @@ -280,7 +280,7 @@ class ElfDebugLineWriter { } private: - ElfBuilder<ElfTypes>* builder_; + linker::ElfBuilder<ElfTypes>* builder_; std::vector<uintptr_t> debug_line_patches_; }; diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc index c5ff85827e..33c46d7e1f 100644 --- a/compiler/debug/elf_debug_writer.cc +++ b/compiler/debug/elf_debug_writer.cc @@ -29,7 +29,7 @@ #include "debug/elf_gnu_debugdata_writer.h" #include "debug/elf_symtab_writer.h" #include "debug/method_debug_info.h" -#include "elf_builder.h" +#include "linker/elf_builder.h" #include "linker/vector_output_stream.h" #include "oat.h" @@ -37,7 +37,7 @@ namespace art { namespace debug { template <typename ElfTypes> -void WriteDebugInfo(ElfBuilder<ElfTypes>* builder, +void WriteDebugInfo(linker::ElfBuilder<ElfTypes>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, dwarf::CFIFormat cfi_format, bool write_oat_patches) { @@ -133,8 +133,9 @@ static std::vector<uint8_t> WriteDebugElfFileForMethodsInternal( const ArrayRef<const MethodDebugInfo>& method_infos) { std::vector<uint8_t> buffer; buffer.reserve(KB); - VectorOutputStream out("Debug ELF file", &buffer); - std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out)); + linker::VectorOutputStream out("Debug ELF file", &buffer); + std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder( + new linker::ElfBuilder<ElfTypes>(isa, features, &out)); // No program headers since the ELF file is not linked and has no allocated sections. builder->Start(false /* write_program_headers */); WriteDebugInfo(builder.get(), @@ -165,8 +166,9 @@ static std::vector<uint8_t> WriteDebugElfFileForClassesInternal( REQUIRES_SHARED(Locks::mutator_lock_) { std::vector<uint8_t> buffer; buffer.reserve(KB); - VectorOutputStream out("Debug ELF file", &buffer); - std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out)); + linker::VectorOutputStream out("Debug ELF file", &buffer); + std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder( + new linker::ElfBuilder<ElfTypes>(isa, features, &out)); // No program headers since the ELF file is not linked and has no allocated sections. builder->Start(false /* write_program_headers */); ElfDebugInfoWriter<ElfTypes> info_writer(builder.get()); @@ -192,12 +194,12 @@ std::vector<uint8_t> WriteDebugElfFileForClasses(InstructionSet isa, // Explicit instantiations template void WriteDebugInfo<ElfTypes32>( - ElfBuilder<ElfTypes32>* builder, + linker::ElfBuilder<ElfTypes32>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, dwarf::CFIFormat cfi_format, bool write_oat_patches); template void WriteDebugInfo<ElfTypes64>( - ElfBuilder<ElfTypes64>* builder, + linker::ElfBuilder<ElfTypes64>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, dwarf::CFIFormat cfi_format, bool write_oat_patches); diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h index 6e26ba36c4..d24ca9b203 100644 --- a/compiler/debug/elf_debug_writer.h +++ b/compiler/debug/elf_debug_writer.h @@ -23,7 +23,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "debug/dwarf/dwarf_constants.h" -#include "elf_builder.h" +#include "linker/elf_builder.h" namespace art { class OatHeader; @@ -35,7 +35,7 @@ struct MethodDebugInfo; template <typename ElfTypes> void WriteDebugInfo( - ElfBuilder<ElfTypes>* builder, + linker::ElfBuilder<ElfTypes>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, dwarf::CFIFormat cfi_format, bool write_oat_patches); diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h index fb63d62572..1cdf6b0ad1 100644 --- a/compiler/debug/elf_gnu_debugdata_writer.h +++ b/compiler/debug/elf_gnu_debugdata_writer.h @@ -20,7 +20,7 @@ #include <vector> #include "arch/instruction_set.h" -#include "elf_builder.h" +#include "linker/elf_builder.h" #include "linker/vector_output_stream.h" // liblzma. @@ -85,8 +85,9 @@ static std::vector<uint8_t> MakeMiniDebugInfoInternal( const ArrayRef<const MethodDebugInfo>& method_infos) { std::vector<uint8_t> buffer; buffer.reserve(KB); - VectorOutputStream out("Mini-debug-info ELF file", &buffer); - std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out)); + linker::VectorOutputStream out("Mini-debug-info ELF file", &buffer); + std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder( + new linker::ElfBuilder<ElfTypes>(isa, features, &out)); builder->Start(); // Mirror .rodata and .text as NOBITS sections. // It is needed to detected relocations after compression. diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h index abd2699a1f..b37f984860 100644 --- a/compiler/debug/elf_symtab_writer.h +++ b/compiler/debug/elf_symtab_writer.h @@ -20,7 +20,7 @@ #include <unordered_set> #include "debug/method_debug_info.h" -#include "elf_builder.h" +#include "linker/elf_builder.h" #include "utils.h" namespace art { @@ -36,7 +36,7 @@ namespace debug { constexpr bool kGenerateSingleArmMappingSymbol = true; template <typename ElfTypes> -static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, +static void WriteDebugSymbols(linker::ElfBuilder<ElfTypes>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, bool with_signature) { uint64_t mapping_symbol_address = std::numeric_limits<uint64_t>::max(); diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h index 567891087a..a8225fa2b4 100644 --- a/compiler/debug/method_debug_info.h +++ b/compiler/debug/method_debug_info.h @@ -19,7 +19,8 @@ #include <string> -#include "compiled_method.h" +#include "arch/instruction_set.h" +#include "base/array_ref.h" #include "dex_file.h" namespace art { diff --git a/compiler/image_write_read_test.cc b/compiler/debug/src_map_elem.h index 32c0b06766..5286b8c4dc 100644 --- a/compiler/image_write_read_test.cc +++ b/compiler/debug/src_map_elem.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2017 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. @@ -14,20 +14,30 @@ * limitations under the License. */ -#include "image_test.h" +#ifndef ART_COMPILER_DEBUG_SRC_MAP_ELEM_H_ +#define ART_COMPILER_DEBUG_SRC_MAP_ELEM_H_ + +#include <stdint.h> namespace art { -TEST_F(ImageTest, WriteReadUncompressed) { - TestWriteRead(ImageHeader::kStorageModeUncompressed); -} +class SrcMapElem { + public: + uint32_t from_; + int32_t to_; +}; -TEST_F(ImageTest, WriteReadLZ4) { - TestWriteRead(ImageHeader::kStorageModeLZ4); +inline bool operator<(const SrcMapElem& lhs, const SrcMapElem& rhs) { + if (lhs.from_ != rhs.from_) { + return lhs.from_ < rhs.from_; + } + return lhs.to_ < rhs.to_; } -TEST_F(ImageTest, WriteReadLZ4HC) { - TestWriteRead(ImageHeader::kStorageModeLZ4HC); +inline bool operator==(const SrcMapElem& lhs, const SrcMapElem& rhs) { + return lhs.from_ == rhs.from_ && lhs.to_ == rhs.to_; } } // namespace art + +#endif // ART_COMPILER_DEBUG_SRC_MAP_ELEM_H_ diff --git a/compiler/debug/src_map_elem_test.cc b/compiler/debug/src_map_elem_test.cc new file mode 100644 index 0000000000..ceaa53fa99 --- /dev/null +++ b/compiler/debug/src_map_elem_test.cc @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 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 <gtest/gtest.h> + +#include "src_map_elem.h" + +#include "base/macros.h" + +namespace art { +namespace debug { + +TEST(SrcMapElem, Operators) { + SrcMapElem elems[] = { + { 1u, -1 }, + { 1u, 0 }, + { 1u, 1 }, + { 2u, -1 }, + { 2u, 0 }, // Index 4. + { 2u, 1 }, + { 2u, 0u }, // Index 6: Arbitrarily add identical SrcMapElem with index 4. + }; + + for (size_t i = 0; i != arraysize(elems); ++i) { + for (size_t j = 0; j != arraysize(elems); ++j) { + bool expected = (i != 6u ? i : 4u) == (j != 6u ? j : 4u); + EXPECT_EQ(expected, elems[i] == elems[j]) << i << " " << j; + } + } + + for (size_t i = 0; i != arraysize(elems); ++i) { + for (size_t j = 0; j != arraysize(elems); ++j) { + bool expected = (i != 6u ? i : 4u) < (j != 6u ? j : 4u); + EXPECT_EQ(expected, elems[i] < elems[j]) << i << " " << j; + } + } +} + +} // namespace debug +} // namespace art diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 9d57b965ab..e49f83f943 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -395,7 +395,7 @@ CompiledMethod* ArtCompileDEX( ArrayRef<const uint8_t>(), // method_info ArrayRef<const uint8_t>(quicken_data), // vmap_table ArrayRef<const uint8_t>(), // cfi data - ArrayRef<const LinkerPatch>()); + ArrayRef<const linker::LinkerPatch>()); } return nullptr; } diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc index e36d416e9f..6637be2811 100644 --- a/compiler/dex/dex_to_dex_decompiler_test.cc +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -18,7 +18,7 @@ #include "class_linker.h" #include "common_compiler_test.h" -#include "compiled_method.h" +#include "compiled_method-inl.h" #include "compiler_callbacks.h" #include "dex_file.h" #include "driver/compiler_driver.h" diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc index 528b0a215b..c739333cee 100644 --- a/compiler/driver/compiled_method_storage.cc +++ b/compiler/driver/compiled_method_storage.cc @@ -21,6 +21,7 @@ #include "base/logging.h" #include "compiled_method.h" +#include "linker/linker_patch.h" #include "thread-current-inl.h" #include "utils.h" #include "utils/dedupe_set-inl.h" @@ -178,7 +179,7 @@ CompiledMethodStorage::CompiledMethodStorage(int swap_fd) LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())), dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())), dedupe_linker_patches_("dedupe cfi info", - LengthPrefixedArrayAlloc<LinkerPatch>(swap_space_.get())) { + LengthPrefixedArrayAlloc<linker::LinkerPatch>(swap_space_.get())) { } CompiledMethodStorage::~CompiledMethodStorage() { @@ -234,13 +235,13 @@ void CompiledMethodStorage::ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* c ReleaseArrayIfNotDeduplicated(cfi_info); } -const LengthPrefixedArray<LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches( - const ArrayRef<const LinkerPatch>& linker_patches) { +const LengthPrefixedArray<linker::LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches( + const ArrayRef<const linker::LinkerPatch>& linker_patches) { return AllocateOrDeduplicateArray(linker_patches, &dedupe_linker_patches_); } void CompiledMethodStorage::ReleaseLinkerPatches( - const LengthPrefixedArray<LinkerPatch>* linker_patches) { + const LengthPrefixedArray<linker::LinkerPatch>* linker_patches) { ReleaseArrayIfNotDeduplicated(linker_patches); } diff --git a/compiler/driver/compiled_method_storage.h b/compiler/driver/compiled_method_storage.h index 27011e8955..249f06c20f 100644 --- a/compiler/driver/compiled_method_storage.h +++ b/compiler/driver/compiled_method_storage.h @@ -28,7 +28,9 @@ namespace art { +namespace linker { class LinkerPatch; +} // namespace linker class CompiledMethodStorage { public: @@ -61,9 +63,9 @@ class CompiledMethodStorage { const LengthPrefixedArray<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info); void ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info); - const LengthPrefixedArray<LinkerPatch>* DeduplicateLinkerPatches( - const ArrayRef<const LinkerPatch>& linker_patches); - void ReleaseLinkerPatches(const LengthPrefixedArray<LinkerPatch>* linker_patches); + const LengthPrefixedArray<linker::LinkerPatch>* DeduplicateLinkerPatches( + const ArrayRef<const linker::LinkerPatch>& linker_patches); + void ReleaseLinkerPatches(const LengthPrefixedArray<linker::LinkerPatch>* linker_patches); private: template <typename T, typename DedupeSetType> @@ -98,7 +100,7 @@ class CompiledMethodStorage { ArrayDedupeSet<uint8_t> dedupe_method_info_; ArrayDedupeSet<uint8_t> dedupe_vmap_table_; ArrayDedupeSet<uint8_t> dedupe_cfi_info_; - ArrayDedupeSet<LinkerPatch> dedupe_linker_patches_; + ArrayDedupeSet<linker::LinkerPatch> dedupe_linker_patches_; DISALLOW_COPY_AND_ASSIGN(CompiledMethodStorage); }; diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc index 2ec2af587e..e1ea6304eb 100644 --- a/compiler/driver/compiled_method_storage_test.cc +++ b/compiler/driver/compiled_method_storage_test.cc @@ -18,7 +18,7 @@ #include <gtest/gtest.h> -#include "compiled_method.h" +#include "compiled_method-inl.h" #include "compiler_driver.h" #include "compiler_options.h" #include "dex/verification_results.h" @@ -70,17 +70,17 @@ TEST(CompiledMethodStorage, Deduplicate) { ArrayRef<const uint8_t>(raw_cfi_info1), ArrayRef<const uint8_t>(raw_cfi_info2), }; - const LinkerPatch raw_patches1[] = { - LinkerPatch::CodePatch(0u, nullptr, 1u), - LinkerPatch::RelativeMethodPatch(4u, nullptr, 0u, 1u), + const linker::LinkerPatch raw_patches1[] = { + linker::LinkerPatch::CodePatch(0u, nullptr, 1u), + linker::LinkerPatch::RelativeMethodPatch(4u, nullptr, 0u, 1u), }; - const LinkerPatch raw_patches2[] = { - LinkerPatch::CodePatch(0u, nullptr, 1u), - LinkerPatch::RelativeMethodPatch(4u, nullptr, 0u, 2u), + const linker::LinkerPatch raw_patches2[] = { + linker::LinkerPatch::CodePatch(0u, nullptr, 1u), + linker::LinkerPatch::RelativeMethodPatch(4u, nullptr, 0u, 2u), }; - ArrayRef<const LinkerPatch> patches[] = { - ArrayRef<const LinkerPatch>(raw_patches1), - ArrayRef<const LinkerPatch>(raw_patches2), + ArrayRef<const linker::LinkerPatch> patches[] = { + ArrayRef<const linker::LinkerPatch>(raw_patches1), + ArrayRef<const linker::LinkerPatch>(raw_patches2), }; std::vector<CompiledMethod*> compiled_methods; diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 678f090532..03d8ef5915 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -37,7 +37,7 @@ #include "base/time_utils.h" #include "base/timing_logger.h" #include "class_linker-inl.h" -#include "compiled_method.h" +#include "compiled_method-inl.h" #include "compiler.h" #include "compiler_callbacks.h" #include "compiler_driver-inl.h" @@ -55,6 +55,7 @@ #include "handle_scope-inl.h" #include "intrinsics_enum.h" #include "jni_internal.h" +#include "linker/linker_patch.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" @@ -618,7 +619,7 @@ static void CompileMethod(Thread* self, if (compiled_method != nullptr) { // Count non-relative linker patches. size_t non_relative_linker_patch_count = 0u; - for (const LinkerPatch& patch : compiled_method->GetPatches()) { + for (const linker::LinkerPatch& patch : compiled_method->GetPatches()) { if (!patch.IsPcRelative()) { ++non_relative_linker_patch_count; } diff --git a/compiler/elf_writer.cc b/compiler/elf_writer.cc deleted file mode 100644 index 37e4f113fa..0000000000 --- a/compiler/elf_writer.cc +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "elf_writer.h" - -#include "base/unix_file/fd_file.h" -#include "elf_file.h" - -namespace art { - -uintptr_t ElfWriter::GetOatDataAddress(ElfFile* elf_file) { - uintptr_t oatdata_address = elf_file->FindSymbolAddress(SHT_DYNSYM, - "oatdata", - false); - CHECK_NE(0U, oatdata_address); - return oatdata_address; -} - -void ElfWriter::GetOatElfInformation(File* file, - size_t* oat_loaded_size, - size_t* oat_data_offset) { - std::string error_msg; - std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, - false, - false, - /*low_4gb*/false, - &error_msg)); - CHECK(elf_file.get() != nullptr) << error_msg; - - bool success = elf_file->GetLoadedSize(oat_loaded_size, &error_msg); - CHECK(success) << error_msg; - CHECK_NE(0U, *oat_loaded_size); - *oat_data_offset = GetOatDataAddress(elf_file.get()); - CHECK_NE(0U, *oat_data_offset); -} - -bool ElfWriter::Fixup(File* file, uintptr_t oat_data_begin) { - std::string error_msg; - std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, /*low_4gb*/false, &error_msg)); - CHECK(elf_file.get() != nullptr) << error_msg; - - // Lookup "oatdata" symbol address. - uintptr_t oatdata_address = ElfWriter::GetOatDataAddress(elf_file.get()); - uintptr_t base_address = oat_data_begin - oatdata_address; - - return elf_file->Fixup(base_address); -} - -} // namespace art diff --git a/compiler/elf_writer.h b/compiler/elf_writer.h deleted file mode 100644 index a8a5bc32b7..0000000000 --- a/compiler/elf_writer.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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_ELF_WRITER_H_ -#define ART_COMPILER_ELF_WRITER_H_ - -#include <stdint.h> -#include <cstddef> -#include <string> -#include <vector> - -#include "base/array_ref.h" -#include "base/macros.h" -#include "base/mutex.h" -#include "os.h" - -namespace art { - -class ElfFile; -class OutputStream; - -namespace debug { -struct MethodDebugInfo; -} // namespace debug - -class ElfWriter { - public: - // Looks up information about location of oat file in elf file container. - // Used for ImageWriter to perform memory layout. - static void GetOatElfInformation(File* file, - size_t* oat_loaded_size, - size_t* oat_data_offset); - - // Returns runtime oat_data runtime address for an opened ElfFile. - static uintptr_t GetOatDataAddress(ElfFile* elf_file); - - static bool Fixup(File* file, uintptr_t oat_data_begin); - - virtual ~ElfWriter() {} - - virtual void Start() = 0; - virtual void PrepareDynamicSection(size_t rodata_size, - size_t text_size, - size_t bss_size, - size_t bss_methods_offset, - size_t bss_roots_offset) = 0; - virtual void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0; - virtual OutputStream* StartRoData() = 0; - virtual void EndRoData(OutputStream* rodata) = 0; - virtual OutputStream* StartText() = 0; - virtual void EndText(OutputStream* text) = 0; - virtual void WriteDynamicSection() = 0; - virtual void WriteDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0; - virtual bool End() = 0; - - // Get the ELF writer's stream. This stream can be used for writing data directly - // to a section after the section has been finished. When that's done, the user - // should Seek() back to the position where the stream was before this operation. - virtual OutputStream* GetStream() = 0; - - // Get the size that the loaded ELF file will occupy in memory. - virtual size_t GetLoadedSize() = 0; - - protected: - ElfWriter() = default; -}; - -} // namespace art - -#endif // ART_COMPILER_ELF_WRITER_H_ diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc deleted file mode 100644 index 5d6dd2e1d7..0000000000 --- a/compiler/elf_writer_quick.cc +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "elf_writer_quick.h" - -#include <openssl/sha.h> -#include <unordered_map> -#include <unordered_set> - -#include "base/casts.h" -#include "base/logging.h" -#include "compiled_method.h" -#include "debug/elf_debug_writer.h" -#include "debug/method_debug_info.h" -#include "driver/compiler_options.h" -#include "elf.h" -#include "elf_builder.h" -#include "elf_utils.h" -#include "globals.h" -#include "leb128.h" -#include "linker/buffered_output_stream.h" -#include "linker/file_output_stream.h" -#include "thread-current-inl.h" -#include "thread_pool.h" -#include "utils.h" - -namespace art { - -// .eh_frame and .debug_frame are almost identical. -// Except for some minor formatting differences, the main difference -// is that .eh_frame is allocated within the running program because -// it is used by C++ exception handling (which we do not use so we -// can choose either). C++ compilers generally tend to use .eh_frame -// because if they need it sometimes, they might as well always use it. -// Let's use .debug_frame because it is easier to strip or compress. -constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT; - -class DebugInfoTask : public Task { - public: - DebugInfoTask(InstructionSet isa, - const InstructionSetFeatures* features, - size_t rodata_section_size, - size_t text_section_size, - const ArrayRef<const debug::MethodDebugInfo>& method_infos) - : isa_(isa), - instruction_set_features_(features), - rodata_section_size_(rodata_section_size), - text_section_size_(text_section_size), - method_infos_(method_infos) { - } - - void Run(Thread*) { - result_ = debug::MakeMiniDebugInfo(isa_, - instruction_set_features_, - rodata_section_size_, - text_section_size_, - method_infos_); - } - - std::vector<uint8_t>* GetResult() { - return &result_; - } - - private: - InstructionSet isa_; - const InstructionSetFeatures* instruction_set_features_; - size_t rodata_section_size_; - size_t text_section_size_; - const ArrayRef<const debug::MethodDebugInfo> method_infos_; - std::vector<uint8_t> result_; -}; - -template <typename ElfTypes> -class ElfWriterQuick FINAL : public ElfWriter { - public: - ElfWriterQuick(InstructionSet instruction_set, - const InstructionSetFeatures* features, - const CompilerOptions* compiler_options, - File* elf_file); - ~ElfWriterQuick(); - - void Start() OVERRIDE; - void PrepareDynamicSection(size_t rodata_size, - size_t text_size, - size_t bss_size, - size_t bss_methods_offset, - size_t bss_roots_offset) OVERRIDE; - void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE; - OutputStream* StartRoData() OVERRIDE; - void EndRoData(OutputStream* rodata) OVERRIDE; - OutputStream* StartText() OVERRIDE; - void EndText(OutputStream* text) OVERRIDE; - void WriteDynamicSection() OVERRIDE; - void WriteDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE; - bool End() OVERRIDE; - - virtual OutputStream* GetStream() OVERRIDE; - - size_t GetLoadedSize() OVERRIDE; - - static void EncodeOatPatches(const std::vector<uintptr_t>& locations, - std::vector<uint8_t>* buffer); - - private: - const InstructionSetFeatures* instruction_set_features_; - const CompilerOptions* const compiler_options_; - File* const elf_file_; - size_t rodata_size_; - size_t text_size_; - size_t bss_size_; - std::unique_ptr<BufferedOutputStream> output_stream_; - std::unique_ptr<ElfBuilder<ElfTypes>> builder_; - std::unique_ptr<DebugInfoTask> debug_info_task_; - std::unique_ptr<ThreadPool> debug_info_thread_pool_; - - void ComputeFileBuildId(uint8_t (*build_id)[ElfBuilder<ElfTypes>::kBuildIdLen]); - - DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick); -}; - -std::unique_ptr<ElfWriter> CreateElfWriterQuick(InstructionSet instruction_set, - const InstructionSetFeatures* features, - const CompilerOptions* compiler_options, - File* elf_file) { - if (Is64BitInstructionSet(instruction_set)) { - return std::make_unique<ElfWriterQuick<ElfTypes64>>(instruction_set, - features, - compiler_options, - elf_file); - } else { - return std::make_unique<ElfWriterQuick<ElfTypes32>>(instruction_set, - features, - compiler_options, - elf_file); - } -} - -template <typename ElfTypes> -ElfWriterQuick<ElfTypes>::ElfWriterQuick(InstructionSet instruction_set, - const InstructionSetFeatures* features, - const CompilerOptions* compiler_options, - File* elf_file) - : ElfWriter(), - instruction_set_features_(features), - compiler_options_(compiler_options), - elf_file_(elf_file), - rodata_size_(0u), - text_size_(0u), - bss_size_(0u), - output_stream_( - std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(elf_file))), - builder_(new ElfBuilder<ElfTypes>(instruction_set, features, output_stream_.get())) {} - -template <typename ElfTypes> -ElfWriterQuick<ElfTypes>::~ElfWriterQuick() {} - -template <typename ElfTypes> -void ElfWriterQuick<ElfTypes>::Start() { - builder_->Start(); - if (compiler_options_->GetGenerateBuildId()) { - builder_->WriteBuildIdSection(); - } -} - -template <typename ElfTypes> -void ElfWriterQuick<ElfTypes>::PrepareDynamicSection(size_t rodata_size, - size_t text_size, - size_t bss_size, - size_t bss_methods_offset, - size_t bss_roots_offset) { - DCHECK_EQ(rodata_size_, 0u); - rodata_size_ = rodata_size; - DCHECK_EQ(text_size_, 0u); - text_size_ = text_size; - DCHECK_EQ(bss_size_, 0u); - bss_size_ = bss_size; - builder_->PrepareDynamicSection(elf_file_->GetPath(), - rodata_size_, - text_size_, - bss_size_, - bss_methods_offset, - bss_roots_offset); -} - -template <typename ElfTypes> -OutputStream* ElfWriterQuick<ElfTypes>::StartRoData() { - auto* rodata = builder_->GetRoData(); - rodata->Start(); - return rodata; -} - -template <typename ElfTypes> -void ElfWriterQuick<ElfTypes>::EndRoData(OutputStream* rodata) { - CHECK_EQ(builder_->GetRoData(), rodata); - builder_->GetRoData()->End(); -} - -template <typename ElfTypes> -OutputStream* ElfWriterQuick<ElfTypes>::StartText() { - auto* text = builder_->GetText(); - text->Start(); - return text; -} - -template <typename ElfTypes> -void ElfWriterQuick<ElfTypes>::EndText(OutputStream* text) { - CHECK_EQ(builder_->GetText(), text); - builder_->GetText()->End(); -} - -template <typename ElfTypes> -void ElfWriterQuick<ElfTypes>::WriteDynamicSection() { - if (bss_size_ != 0u) { - builder_->GetBss()->WriteNoBitsSection(bss_size_); - } - if (builder_->GetIsa() == kMips || builder_->GetIsa() == kMips64) { - builder_->WriteMIPSabiflagsSection(); - } - builder_->WriteDynamicSection(); -} - -template <typename ElfTypes> -void ElfWriterQuick<ElfTypes>::PrepareDebugInfo( - const ArrayRef<const debug::MethodDebugInfo>& method_infos) { - if (!method_infos.empty() && compiler_options_->GetGenerateMiniDebugInfo()) { - // Prepare the mini-debug-info in background while we do other I/O. - Thread* self = Thread::Current(); - debug_info_task_ = std::unique_ptr<DebugInfoTask>( - new DebugInfoTask(builder_->GetIsa(), - instruction_set_features_, - rodata_size_, - text_size_, - method_infos)); - debug_info_thread_pool_ = std::unique_ptr<ThreadPool>( - new ThreadPool("Mini-debug-info writer", 1)); - debug_info_thread_pool_->AddTask(self, debug_info_task_.get()); - debug_info_thread_pool_->StartWorkers(self); - } -} - -template <typename ElfTypes> -void ElfWriterQuick<ElfTypes>::WriteDebugInfo( - const ArrayRef<const debug::MethodDebugInfo>& method_infos) { - if (!method_infos.empty()) { - if (compiler_options_->GetGenerateDebugInfo()) { - // Generate all the debug information we can. - debug::WriteDebugInfo(builder_.get(), method_infos, kCFIFormat, true /* write_oat_patches */); - } - if (compiler_options_->GetGenerateMiniDebugInfo()) { - // Wait for the mini-debug-info generation to finish and write it to disk. - Thread* self = Thread::Current(); - DCHECK(debug_info_thread_pool_ != nullptr); - debug_info_thread_pool_->Wait(self, true, false); - builder_->WriteSection(".gnu_debugdata", debug_info_task_->GetResult()); - } - } -} - -template <typename ElfTypes> -bool ElfWriterQuick<ElfTypes>::End() { - builder_->End(); - if (compiler_options_->GetGenerateBuildId()) { - uint8_t build_id[ElfBuilder<ElfTypes>::kBuildIdLen]; - ComputeFileBuildId(&build_id); - builder_->WriteBuildId(build_id); - } - return builder_->Good(); -} - -template <typename ElfTypes> -void ElfWriterQuick<ElfTypes>::ComputeFileBuildId( - uint8_t (*build_id)[ElfBuilder<ElfTypes>::kBuildIdLen]) { - constexpr int kBufSize = 8192; - std::vector<char> buffer(kBufSize); - int64_t offset = 0; - SHA_CTX ctx; - SHA1_Init(&ctx); - while (true) { - int64_t bytes_read = elf_file_->Read(buffer.data(), kBufSize, offset); - CHECK_GE(bytes_read, 0); - if (bytes_read == 0) { - // End of file. - break; - } - SHA1_Update(&ctx, buffer.data(), bytes_read); - offset += bytes_read; - } - SHA1_Final(*build_id, &ctx); -} - -template <typename ElfTypes> -OutputStream* ElfWriterQuick<ElfTypes>::GetStream() { - return builder_->GetStream(); -} - -template <typename ElfTypes> -size_t ElfWriterQuick<ElfTypes>::GetLoadedSize() { - return builder_->GetLoadedSize(); -} - -// Explicit instantiations -template class ElfWriterQuick<ElfTypes32>; -template class ElfWriterQuick<ElfTypes64>; - -} // namespace art diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h deleted file mode 100644 index 3d5dd39a66..0000000000 --- a/compiler/elf_writer_quick.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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_ELF_WRITER_QUICK_H_ -#define ART_COMPILER_ELF_WRITER_QUICK_H_ - -#include <memory> - -#include "arch/instruction_set.h" -#include "elf_writer.h" -#include "os.h" - -namespace art { - -class CompilerOptions; -class InstructionSetFeatures; - -std::unique_ptr<ElfWriter> CreateElfWriterQuick(InstructionSet instruction_set, - const InstructionSetFeatures* features, - const CompilerOptions* compiler_options, - File* elf_file); - -} // namespace art - -#endif // ART_COMPILER_ELF_WRITER_QUICK_H_ diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc deleted file mode 100644 index 984e9ee4e9..0000000000 --- a/compiler/elf_writer_test.cc +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "elf_file.h" - -#include "base/unix_file/fd_file.h" -#include "common_compiler_test.h" -#include "elf_builder.h" -#include "elf_file.h" -#include "elf_file_impl.h" -#include "elf_writer_quick.h" -#include "oat.h" -#include "utils.h" - -namespace art { - -class ElfWriterTest : public CommonCompilerTest { - protected: - virtual void SetUp() { - ReserveImageSpace(); - CommonCompilerTest::SetUp(); - } -}; - -#define EXPECT_ELF_FILE_ADDRESS(ef, expected_value, symbol_name, build_map) \ - do { \ - void* addr = reinterpret_cast<void*>((ef)->FindSymbolAddress(SHT_DYNSYM, \ - symbol_name, \ - build_map)); \ - EXPECT_NE(nullptr, addr); \ - if ((expected_value) == nullptr) { \ - (expected_value) = addr; \ - } \ - EXPECT_EQ(expected_value, addr); \ - EXPECT_EQ(expected_value, (ef)->FindDynamicSymbolAddress(symbol_name)); \ - } while (false) - -TEST_F(ElfWriterTest, dlsym) { - std::string elf_location = GetCoreOatLocation(); - std::string elf_filename = GetSystemImageFilename(elf_location.c_str(), kRuntimeISA); - LOG(INFO) << "elf_filename=" << elf_filename; - - UnreserveImageSpace(); - void* dl_oatdata = nullptr; - void* dl_oatexec = nullptr; - void* dl_oatlastword = nullptr; - - std::unique_ptr<File> file(OS::OpenFileForReading(elf_filename.c_str())); - ASSERT_TRUE(file.get() != nullptr) << elf_filename; - { - std::string error_msg; - std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), - false, - false, - /*low_4gb*/false, - &error_msg)); - CHECK(ef.get() != nullptr) << error_msg; - EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", false); - EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", false); - EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", false); - } - { - std::string error_msg; - std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), - false, - false, - /*low_4gb*/false, - &error_msg)); - CHECK(ef.get() != nullptr) << error_msg; - EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", true); - EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", true); - EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", true); - } - { - uint8_t* base = reinterpret_cast<uint8_t*>(ART_BASE_ADDRESS); - std::string error_msg; - std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), - false, - true, - /*low_4gb*/false, - &error_msg, - base)); - CHECK(ef.get() != nullptr) << error_msg; - CHECK(ef->Load(file.get(), false, /*low_4gb*/false, &error_msg)) << error_msg; - EXPECT_EQ(reinterpret_cast<uintptr_t>(dl_oatdata) + reinterpret_cast<uintptr_t>(base), - reinterpret_cast<uintptr_t>(ef->FindDynamicSymbolAddress("oatdata"))); - EXPECT_EQ(reinterpret_cast<uintptr_t>(dl_oatexec) + reinterpret_cast<uintptr_t>(base), - reinterpret_cast<uintptr_t>(ef->FindDynamicSymbolAddress("oatexec"))); - EXPECT_EQ(reinterpret_cast<uintptr_t>(dl_oatlastword) + reinterpret_cast<uintptr_t>(base), - reinterpret_cast<uintptr_t>(ef->FindDynamicSymbolAddress("oatlastword"))); - } -} - -TEST_F(ElfWriterTest, CheckBuildIdPresent) { - std::string elf_location = GetCoreOatLocation(); - std::string elf_filename = GetSystemImageFilename(elf_location.c_str(), kRuntimeISA); - LOG(INFO) << "elf_filename=" << elf_filename; - - std::unique_ptr<File> file(OS::OpenFileForReading(elf_filename.c_str())); - ASSERT_TRUE(file.get() != nullptr); - { - std::string error_msg; - std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), - false, - false, - /*low_4gb*/false, - &error_msg)); - CHECK(ef.get() != nullptr) << error_msg; - EXPECT_TRUE(ef->HasSection(".note.gnu.build-id")); - } -} - -TEST_F(ElfWriterTest, EncodeDecodeOatPatches) { - const std::vector<std::vector<uintptr_t>> test_data { - { 0, 4, 8, 15, 128, 200 }, - { 8, 8 + 127 }, - { 8, 8 + 128 }, - { }, - }; - for (const auto& patch_locations : test_data) { - constexpr int32_t delta = 0x11235813; - - // Encode patch locations. - std::vector<uint8_t> oat_patches; - ElfBuilder<ElfTypes32>::EncodeOatPatches(ArrayRef<const uintptr_t>(patch_locations), - &oat_patches); - - // Create buffer to be patched. - std::vector<uint8_t> initial_data(256); - for (size_t i = 0; i < initial_data.size(); i++) { - initial_data[i] = i; - } - - // Patch manually. - std::vector<uint8_t> expected = initial_data; - for (uintptr_t location : patch_locations) { - typedef __attribute__((__aligned__(1))) uint32_t UnalignedAddress; - *reinterpret_cast<UnalignedAddress*>(expected.data() + location) += delta; - } - - // Decode and apply patch locations. - std::vector<uint8_t> actual = initial_data; - ElfFileImpl32::ApplyOatPatches( - oat_patches.data(), oat_patches.data() + oat_patches.size(), delta, - actual.data(), actual.data() + actual.size()); - - EXPECT_EQ(expected, actual); - } -} - -} // namespace art diff --git a/compiler/image_test.cc b/compiler/image_test.cc deleted file mode 100644 index 7b623dd979..0000000000 --- a/compiler/image_test.cc +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <string.h> -#include <vector> - -#include "image_test.h" - -#include "image.h" -#include "scoped_thread_state_change-inl.h" -#include "thread.h" - -namespace art { - -TEST_F(ImageTest, TestImageLayout) { - std::vector<size_t> image_sizes; - std::vector<size_t> image_sizes_extra; - // Compile multi-image with ImageLayoutA being the last image. - { - CompilationHelper helper; - Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutA", {"LMyClass;"}); - image_sizes = helper.GetImageObjectSectionSizes(); - } - TearDown(); - runtime_.reset(); - SetUp(); - // Compile multi-image with ImageLayoutB being the last image. - { - CompilationHelper helper; - Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutB", {"LMyClass;"}); - image_sizes_extra = helper.GetImageObjectSectionSizes(); - } - // Make sure that the new stuff in the clinit in ImageLayoutB is in the last image and not in the - // first two images. - ASSERT_EQ(image_sizes.size(), image_sizes.size()); - // Sizes of the object sections should be the same for all but the last image. - for (size_t i = 0; i < image_sizes.size() - 1; ++i) { - EXPECT_EQ(image_sizes[i], image_sizes_extra[i]); - } - // Last image should be larger since it has a hash map and a string. - EXPECT_LT(image_sizes.back(), image_sizes_extra.back()); -} - -TEST_F(ImageTest, ImageHeaderIsValid) { - uint32_t image_begin = ART_BASE_ADDRESS; - uint32_t image_size_ = 16 * KB; - uint32_t image_roots = ART_BASE_ADDRESS + (1 * KB); - uint32_t oat_checksum = 0; - uint32_t oat_file_begin = ART_BASE_ADDRESS + (4 * KB); // page aligned - uint32_t oat_data_begin = ART_BASE_ADDRESS + (8 * KB); // page aligned - uint32_t oat_data_end = ART_BASE_ADDRESS + (9 * KB); - uint32_t oat_file_end = ART_BASE_ADDRESS + (10 * KB); - ImageSection sections[ImageHeader::kSectionCount]; - ImageHeader image_header(image_begin, - image_size_, - sections, - image_roots, - oat_checksum, - oat_file_begin, - oat_data_begin, - oat_data_end, - oat_file_end, - /*boot_image_begin*/0U, - /*boot_image_size*/0U, - /*boot_oat_begin*/0U, - /*boot_oat_size_*/0U, - sizeof(void*), - /*compile_pic*/false, - /*is_pic*/false, - ImageHeader::kDefaultStorageMode, - /*data_size*/0u); - ASSERT_TRUE(image_header.IsValid()); - ASSERT_TRUE(!image_header.IsAppImage()); - - char* magic = const_cast<char*>(image_header.GetMagic()); - strcpy(magic, ""); // bad magic - ASSERT_FALSE(image_header.IsValid()); - strcpy(magic, "art\n000"); // bad version - ASSERT_FALSE(image_header.IsValid()); -} - -// Test that pointer to quick code is the same in -// a default method of an interface and in a copied method -// of a class which implements the interface. This should be true -// only if the copied method and the origin method are located in the -// same oat file. -TEST_F(ImageTest, TestDefaultMethods) { - CompilationHelper helper; - Compile(ImageHeader::kStorageModeUncompressed, - helper, - "DefaultMethods", - {"LIface;", "LImpl;", "LIterableBase;"}); - - PointerSize pointer_size = class_linker_->GetImagePointerSize(); - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - - // Test the pointer to quick code is the same in origin method - // and in the copied method form the same oat file. - mirror::Class* iface_klass = class_linker_->LookupClass( - self, "LIface;", ObjPtr<mirror::ClassLoader>()); - ASSERT_NE(nullptr, iface_klass); - ArtMethod* origin = iface_klass->FindInterfaceMethod("defaultMethod", "()V", pointer_size); - ASSERT_NE(nullptr, origin); - ASSERT_TRUE(origin->GetDeclaringClass() == iface_klass); - const void* code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); - // The origin method should have a pointer to quick code - ASSERT_NE(nullptr, code); - ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); - mirror::Class* impl_klass = class_linker_->LookupClass( - self, "LImpl;", ObjPtr<mirror::ClassLoader>()); - ASSERT_NE(nullptr, impl_klass); - ArtMethod* copied = FindCopiedMethod(origin, impl_klass); - ASSERT_NE(nullptr, copied); - // the copied method should have pointer to the same quick code as the origin method - ASSERT_EQ(code, copied->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)); - - // Test the origin method has pointer to quick code - // but the copied method has pointer to interpreter - // because these methods are in different oat files. - mirror::Class* iterable_klass = class_linker_->LookupClass( - self, "Ljava/lang/Iterable;", ObjPtr<mirror::ClassLoader>()); - ASSERT_NE(nullptr, iterable_klass); - origin = iterable_klass->FindClassMethod( - "forEach", "(Ljava/util/function/Consumer;)V", pointer_size); - ASSERT_NE(nullptr, origin); - ASSERT_FALSE(origin->IsDirect()); - ASSERT_TRUE(origin->GetDeclaringClass() == iterable_klass); - code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); - // the origin method should have a pointer to quick code - ASSERT_NE(nullptr, code); - ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); - mirror::Class* iterablebase_klass = class_linker_->LookupClass( - self, "LIterableBase;", ObjPtr<mirror::ClassLoader>()); - ASSERT_NE(nullptr, iterablebase_klass); - copied = FindCopiedMethod(origin, iterablebase_klass); - ASSERT_NE(nullptr, copied); - code = copied->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); - // the copied method should have a pointer to interpreter - ASSERT_TRUE(class_linker_->IsQuickToInterpreterBridge(code)); -} - -} // namespace art diff --git a/compiler/image_test.h b/compiler/image_test.h deleted file mode 100644 index f1adeddb69..0000000000 --- a/compiler/image_test.h +++ /dev/null @@ -1,500 +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_IMAGE_TEST_H_ -#define ART_COMPILER_IMAGE_TEST_H_ - -#include "image.h" - -#include <memory> -#include <string> -#include <vector> - -#include "android-base/stringprintf.h" - -#include "art_method-inl.h" -#include "base/unix_file/fd_file.h" -#include "class_linker-inl.h" -#include "common_compiler_test.h" -#include "compiler_callbacks.h" -#include "debug/method_debug_info.h" -#include "dex/quick_compiler_callbacks.h" -#include "driver/compiler_options.h" -#include "elf_writer.h" -#include "elf_writer_quick.h" -#include "gc/space/image_space.h" -#include "image_writer.h" -#include "linker/buffered_output_stream.h" -#include "linker/file_output_stream.h" -#include "linker/multi_oat_relative_patcher.h" -#include "lock_word.h" -#include "mirror/object-inl.h" -#include "oat_writer.h" -#include "scoped_thread_state_change-inl.h" -#include "signal_catcher.h" -#include "utils.h" - -namespace art { - -static const uintptr_t kRequestedImageBase = ART_BASE_ADDRESS; - -struct CompilationHelper { - std::vector<std::string> dex_file_locations; - std::vector<ScratchFile> image_locations; - std::vector<std::unique_ptr<const DexFile>> extra_dex_files; - std::vector<ScratchFile> image_files; - std::vector<ScratchFile> oat_files; - std::vector<ScratchFile> vdex_files; - std::string image_dir; - - void Compile(CompilerDriver* driver, - ImageHeader::StorageMode storage_mode); - - std::vector<size_t> GetImageObjectSectionSizes(); - - ~CompilationHelper(); -}; - -class ImageTest : public CommonCompilerTest { - protected: - virtual void SetUp() { - ReserveImageSpace(); - CommonCompilerTest::SetUp(); - } - - void TestWriteRead(ImageHeader::StorageMode storage_mode); - - void Compile(ImageHeader::StorageMode storage_mode, - CompilationHelper& out_helper, - const std::string& extra_dex = "", - const std::initializer_list<std::string>& image_classes = {}); - - void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { - CommonCompilerTest::SetUpRuntimeOptions(options); - QuickCompilerCallbacks* new_callbacks = - new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileBootImage); - new_callbacks->SetVerificationResults(verification_results_.get()); - callbacks_.reset(new_callbacks); - options->push_back(std::make_pair("compilercallbacks", callbacks_.get())); - } - - std::unordered_set<std::string>* GetImageClasses() OVERRIDE { - return new std::unordered_set<std::string>(image_classes_); - } - - ArtMethod* FindCopiedMethod(ArtMethod* origin, mirror::Class* klass) - REQUIRES_SHARED(Locks::mutator_lock_) { - PointerSize pointer_size = class_linker_->GetImagePointerSize(); - for (ArtMethod& m : klass->GetCopiedMethods(pointer_size)) { - if (strcmp(origin->GetName(), m.GetName()) == 0 && - origin->GetSignature() == m.GetSignature()) { - return &m; - } - } - return nullptr; - } - - private: - std::unordered_set<std::string> image_classes_; -}; - -inline CompilationHelper::~CompilationHelper() { - for (ScratchFile& image_file : image_files) { - image_file.Unlink(); - } - for (ScratchFile& oat_file : oat_files) { - oat_file.Unlink(); - } - for (ScratchFile& vdex_file : vdex_files) { - vdex_file.Unlink(); - } - const int rmdir_result = rmdir(image_dir.c_str()); - CHECK_EQ(0, rmdir_result); -} - -inline std::vector<size_t> CompilationHelper::GetImageObjectSectionSizes() { - std::vector<size_t> ret; - for (ScratchFile& image_file : image_files) { - std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str())); - CHECK(file.get() != nullptr); - ImageHeader image_header; - CHECK_EQ(file->ReadFully(&image_header, sizeof(image_header)), true); - CHECK(image_header.IsValid()); - ret.push_back(image_header.GetObjectsSection().Size()); - } - return ret; -} - -inline void CompilationHelper::Compile(CompilerDriver* driver, - ImageHeader::StorageMode storage_mode) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - std::vector<const DexFile*> class_path = class_linker->GetBootClassPath(); - - for (const std::unique_ptr<const DexFile>& dex_file : extra_dex_files) { - { - ScopedObjectAccess soa(Thread::Current()); - // Inject in boot class path so that the compiler driver can see it. - class_linker->AppendToBootClassPath(soa.Self(), *dex_file.get()); - } - class_path.push_back(dex_file.get()); - } - - // Enable write for dex2dex. - for (const DexFile* dex_file : class_path) { - dex_file_locations.push_back(dex_file->GetLocation()); - if (dex_file->IsReadOnly()) { - dex_file->EnableWrite(); - } - } - { - // Create a generic tmp file, to be the base of the .art and .oat temporary files. - ScratchFile location; - for (int i = 0; i < static_cast<int>(class_path.size()); ++i) { - std::string cur_location = - android::base::StringPrintf("%s-%d.art", location.GetFilename().c_str(), i); - image_locations.push_back(ScratchFile(cur_location)); - } - } - std::vector<std::string> image_filenames; - for (ScratchFile& file : image_locations) { - std::string image_filename(GetSystemImageFilename(file.GetFilename().c_str(), kRuntimeISA)); - image_filenames.push_back(image_filename); - size_t pos = image_filename.rfind('/'); - CHECK_NE(pos, std::string::npos) << image_filename; - if (image_dir.empty()) { - image_dir = image_filename.substr(0, pos); - int mkdir_result = mkdir(image_dir.c_str(), 0700); - CHECK_EQ(0, mkdir_result) << image_dir; - } - image_files.push_back(ScratchFile(OS::CreateEmptyFile(image_filename.c_str()))); - } - - std::vector<std::string> oat_filenames; - std::vector<std::string> vdex_filenames; - for (const std::string& image_filename : image_filenames) { - std::string oat_filename = ReplaceFileExtension(image_filename, "oat"); - oat_files.push_back(ScratchFile(OS::CreateEmptyFile(oat_filename.c_str()))); - oat_filenames.push_back(oat_filename); - std::string vdex_filename = ReplaceFileExtension(image_filename, "vdex"); - vdex_files.push_back(ScratchFile(OS::CreateEmptyFile(vdex_filename.c_str()))); - vdex_filenames.push_back(vdex_filename); - } - - std::unordered_map<const DexFile*, size_t> dex_file_to_oat_index_map; - std::vector<const char*> oat_filename_vector; - for (const std::string& file : oat_filenames) { - oat_filename_vector.push_back(file.c_str()); - } - std::vector<const char*> image_filename_vector; - for (const std::string& file : image_filenames) { - image_filename_vector.push_back(file.c_str()); - } - size_t image_idx = 0; - for (const DexFile* dex_file : class_path) { - dex_file_to_oat_index_map.emplace(dex_file, image_idx); - ++image_idx; - } - // TODO: compile_pic should be a test argument. - std::unique_ptr<ImageWriter> writer(new ImageWriter(*driver, - kRequestedImageBase, - /*compile_pic*/false, - /*compile_app_image*/false, - storage_mode, - oat_filename_vector, - dex_file_to_oat_index_map, - /*dirty_image_objects*/nullptr)); - { - { - jobject class_loader = nullptr; - TimingLogger timings("ImageTest::WriteRead", false, false); - TimingLogger::ScopedTiming t("CompileAll", &timings); - driver->SetDexFilesForOatFile(class_path); - driver->CompileAll(class_loader, class_path, &timings); - - t.NewTiming("WriteElf"); - SafeMap<std::string, std::string> key_value_store; - std::vector<const char*> dex_filename_vector; - for (size_t i = 0; i < class_path.size(); ++i) { - dex_filename_vector.push_back(""); - } - key_value_store.Put(OatHeader::kBootClassPathKey, - gc::space::ImageSpace::GetMultiImageBootClassPath( - dex_filename_vector, - oat_filename_vector, - image_filename_vector)); - - std::vector<std::unique_ptr<ElfWriter>> elf_writers; - std::vector<std::unique_ptr<OatWriter>> oat_writers; - for (ScratchFile& oat_file : oat_files) { - elf_writers.emplace_back(CreateElfWriterQuick(driver->GetInstructionSet(), - driver->GetInstructionSetFeatures(), - &driver->GetCompilerOptions(), - oat_file.GetFile())); - elf_writers.back()->Start(); - oat_writers.emplace_back(new OatWriter(/*compiling_boot_image*/true, - &timings, - /*profile_compilation_info*/nullptr)); - } - - std::vector<OutputStream*> rodata; - std::vector<std::unique_ptr<MemMap>> opened_dex_files_map; - std::vector<std::unique_ptr<const DexFile>> opened_dex_files; - // Now that we have finalized key_value_store_, start writing the oat file. - for (size_t i = 0, size = oat_writers.size(); i != size; ++i) { - const DexFile* dex_file = class_path[i]; - rodata.push_back(elf_writers[i]->StartRoData()); - ArrayRef<const uint8_t> raw_dex_file( - reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()), - dex_file->GetHeader().file_size_); - oat_writers[i]->AddRawDexFileSource(raw_dex_file, - dex_file->GetLocation().c_str(), - dex_file->GetLocationChecksum()); - - std::unique_ptr<MemMap> cur_opened_dex_files_map; - std::vector<std::unique_ptr<const DexFile>> cur_opened_dex_files; - bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles( - kIsVdexEnabled ? vdex_files[i].GetFile() : oat_files[i].GetFile(), - rodata.back(), - driver->GetInstructionSet(), - driver->GetInstructionSetFeatures(), - &key_value_store, - /* verify */ false, // Dex files may be dex-to-dex-ed, don't verify. - /* update_input_vdex */ false, - &cur_opened_dex_files_map, - &cur_opened_dex_files); - ASSERT_TRUE(dex_files_ok); - - if (cur_opened_dex_files_map != nullptr) { - opened_dex_files_map.push_back(std::move(cur_opened_dex_files_map)); - for (std::unique_ptr<const DexFile>& cur_dex_file : cur_opened_dex_files) { - // dex_file_oat_index_map_.emplace(dex_file.get(), i); - opened_dex_files.push_back(std::move(cur_dex_file)); - } - } else { - ASSERT_TRUE(cur_opened_dex_files.empty()); - } - } - bool image_space_ok = writer->PrepareImageAddressSpace(); - ASSERT_TRUE(image_space_ok); - - if (kIsVdexEnabled) { - for (size_t i = 0, size = vdex_files.size(); i != size; ++i) { - std::unique_ptr<BufferedOutputStream> vdex_out = - std::make_unique<BufferedOutputStream>( - std::make_unique<FileOutputStream>(vdex_files[i].GetFile())); - oat_writers[i]->WriteVerifierDeps(vdex_out.get(), nullptr); - oat_writers[i]->WriteChecksumsAndVdexHeader(vdex_out.get()); - } - } - - for (size_t i = 0, size = oat_files.size(); i != size; ++i) { - linker::MultiOatRelativePatcher patcher(driver->GetInstructionSet(), - driver->GetInstructionSetFeatures()); - OatWriter* const oat_writer = oat_writers[i].get(); - ElfWriter* const elf_writer = elf_writers[i].get(); - std::vector<const DexFile*> cur_dex_files(1u, class_path[i]); - oat_writer->Initialize(driver, writer.get(), cur_dex_files); - oat_writer->PrepareLayout(&patcher); - size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset(); - size_t text_size = oat_writer->GetOatSize() - rodata_size; - elf_writer->PrepareDynamicSection(rodata_size, - text_size, - oat_writer->GetBssSize(), - oat_writer->GetBssMethodsOffset(), - oat_writer->GetBssRootsOffset()); - - writer->UpdateOatFileLayout(i, - elf_writer->GetLoadedSize(), - oat_writer->GetOatDataOffset(), - oat_writer->GetOatSize()); - - bool rodata_ok = oat_writer->WriteRodata(rodata[i]); - ASSERT_TRUE(rodata_ok); - elf_writer->EndRoData(rodata[i]); - - OutputStream* text = elf_writer->StartText(); - bool text_ok = oat_writer->WriteCode(text); - ASSERT_TRUE(text_ok); - elf_writer->EndText(text); - - bool header_ok = oat_writer->WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u); - ASSERT_TRUE(header_ok); - - writer->UpdateOatFileHeader(i, oat_writer->GetOatHeader()); - - elf_writer->WriteDynamicSection(); - elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo()); - - bool success = elf_writer->End(); - ASSERT_TRUE(success); - } - } - - bool success_image = writer->Write(kInvalidFd, - image_filename_vector, - oat_filename_vector); - ASSERT_TRUE(success_image); - - for (size_t i = 0, size = oat_filenames.size(); i != size; ++i) { - const char* oat_filename = oat_filenames[i].c_str(); - std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename)); - ASSERT_TRUE(oat_file != nullptr); - bool success_fixup = ElfWriter::Fixup(oat_file.get(), - writer->GetOatDataBegin(i)); - ASSERT_TRUE(success_fixup); - ASSERT_EQ(oat_file->FlushCloseOrErase(), 0) << "Could not flush and close oat file " - << oat_filename; - } - } -} - -inline void ImageTest::Compile(ImageHeader::StorageMode storage_mode, - CompilationHelper& helper, - const std::string& extra_dex, - const std::initializer_list<std::string>& image_classes) { - for (const std::string& image_class : image_classes) { - image_classes_.insert(image_class); - } - CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U); - // Set inline filter values. - compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits); - image_classes_.clear(); - if (!extra_dex.empty()) { - helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str()); - } - helper.Compile(compiler_driver_.get(), storage_mode); - if (image_classes.begin() != image_classes.end()) { - // Make sure the class got initialized. - ScopedObjectAccess soa(Thread::Current()); - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - for (const std::string& image_class : image_classes) { - mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str()); - EXPECT_TRUE(klass != nullptr); - EXPECT_TRUE(klass->IsInitialized()); - } - } -} - -inline void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { - CompilationHelper helper; - Compile(storage_mode, /*out*/ helper); - std::vector<uint64_t> image_file_sizes; - for (ScratchFile& image_file : helper.image_files) { - std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str())); - ASSERT_TRUE(file.get() != nullptr); - ImageHeader image_header; - ASSERT_EQ(file->ReadFully(&image_header, sizeof(image_header)), true); - ASSERT_TRUE(image_header.IsValid()); - const auto& bitmap_section = image_header.GetImageBitmapSection(); - ASSERT_GE(bitmap_section.Offset(), sizeof(image_header)); - ASSERT_NE(0U, bitmap_section.Size()); - - gc::Heap* heap = Runtime::Current()->GetHeap(); - ASSERT_TRUE(heap->HaveContinuousSpaces()); - gc::space::ContinuousSpace* space = heap->GetNonMovingSpace(); - ASSERT_FALSE(space->IsImageSpace()); - ASSERT_TRUE(space != nullptr); - ASSERT_TRUE(space->IsMallocSpace()); - image_file_sizes.push_back(file->GetLength()); - } - - ASSERT_TRUE(compiler_driver_->GetImageClasses() != nullptr); - std::unordered_set<std::string> image_classes(*compiler_driver_->GetImageClasses()); - - // Need to delete the compiler since it has worker threads which are attached to runtime. - compiler_driver_.reset(); - - // Tear down old runtime before making a new one, clearing out misc state. - - // Remove the reservation of the memory for use to load the image. - // Need to do this before we reset the runtime. - UnreserveImageSpace(); - - helper.extra_dex_files.clear(); - runtime_.reset(); - java_lang_dex_file_ = nullptr; - - MemMap::Init(); - - RuntimeOptions options; - std::string image("-Ximage:"); - image.append(helper.image_locations[0].GetFilename()); - options.push_back(std::make_pair(image.c_str(), static_cast<void*>(nullptr))); - // By default the compiler this creates will not include patch information. - options.push_back(std::make_pair("-Xnorelocate", nullptr)); - - if (!Runtime::Create(options, false)) { - LOG(FATAL) << "Failed to create runtime"; - return; - } - runtime_.reset(Runtime::Current()); - // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start, - // give it away now and then switch to a more managable ScopedObjectAccess. - Thread::Current()->TransitionFromRunnableToSuspended(kNative); - ScopedObjectAccess soa(Thread::Current()); - ASSERT_TRUE(runtime_.get() != nullptr); - class_linker_ = runtime_->GetClassLinker(); - - gc::Heap* heap = Runtime::Current()->GetHeap(); - ASSERT_TRUE(heap->HasBootImageSpace()); - ASSERT_TRUE(heap->GetNonMovingSpace()->IsMallocSpace()); - - // We loaded the runtime with an explicit image, so it must exist. - ASSERT_EQ(heap->GetBootImageSpaces().size(), image_file_sizes.size()); - for (size_t i = 0; i < helper.dex_file_locations.size(); ++i) { - std::unique_ptr<const DexFile> dex( - LoadExpectSingleDexFile(helper.dex_file_locations[i].c_str())); - ASSERT_TRUE(dex != nullptr); - uint64_t image_file_size = image_file_sizes[i]; - gc::space::ImageSpace* image_space = heap->GetBootImageSpaces()[i]; - ASSERT_TRUE(image_space != nullptr); - if (storage_mode == ImageHeader::kStorageModeUncompressed) { - // Uncompressed, image should be smaller than file. - ASSERT_LE(image_space->GetImageHeader().GetImageSize(), image_file_size); - } else if (image_file_size > 16 * KB) { - // Compressed, file should be smaller than image. Not really valid for small images. - ASSERT_LE(image_file_size, image_space->GetImageHeader().GetImageSize()); - } - - image_space->VerifyImageAllocations(); - uint8_t* image_begin = image_space->Begin(); - uint8_t* image_end = image_space->End(); - if (i == 0) { - // This check is only valid for image 0. - CHECK_EQ(kRequestedImageBase, reinterpret_cast<uintptr_t>(image_begin)); - } - for (size_t j = 0; j < dex->NumClassDefs(); ++j) { - const DexFile::ClassDef& class_def = dex->GetClassDef(j); - const char* descriptor = dex->GetClassDescriptor(class_def); - mirror::Class* klass = class_linker_->FindSystemClass(soa.Self(), descriptor); - EXPECT_TRUE(klass != nullptr) << descriptor; - if (image_classes.find(descriptor) == image_classes.end()) { - EXPECT_TRUE(reinterpret_cast<uint8_t*>(klass) >= image_end || - reinterpret_cast<uint8_t*>(klass) < image_begin) << descriptor; - } else { - // Image classes should be located inside the image. - EXPECT_LT(image_begin, reinterpret_cast<uint8_t*>(klass)) << descriptor; - EXPECT_LT(reinterpret_cast<uint8_t*>(klass), image_end) << descriptor; - } - EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false))); - } - } -} - - -} // namespace art - -#endif // ART_COMPILER_IMAGE_TEST_H_ diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc deleted file mode 100644 index 4ffe238cc7..0000000000 --- a/compiler/image_writer.cc +++ /dev/null @@ -1,2839 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "image_writer.h" - -#include <lz4.h> -#include <lz4hc.h> -#include <sys/stat.h> - -#include <memory> -#include <numeric> -#include <unordered_set> -#include <vector> - -#include "art_field-inl.h" -#include "art_method-inl.h" -#include "base/callee_save_type.h" -#include "base/enums.h" -#include "base/logging.h" -#include "base/unix_file/fd_file.h" -#include "class_linker-inl.h" -#include "compiled_method.h" -#include "dex_file-inl.h" -#include "dex_file_types.h" -#include "driver/compiler_driver.h" -#include "elf_file.h" -#include "elf_utils.h" -#include "elf_writer.h" -#include "gc/accounting/card_table-inl.h" -#include "gc/accounting/heap_bitmap.h" -#include "gc/accounting/space_bitmap-inl.h" -#include "gc/collector/concurrent_copying.h" -#include "gc/heap-visit-objects-inl.h" -#include "gc/heap.h" -#include "gc/space/large_object_space.h" -#include "gc/space/space-inl.h" -#include "gc/verification.h" -#include "globals.h" -#include "handle_scope-inl.h" -#include "image.h" -#include "imt_conflict_table.h" -#include "jni_internal.h" -#include "linear_alloc.h" -#include "lock_word.h" -#include "mirror/array-inl.h" -#include "mirror/class-inl.h" -#include "mirror/class_ext.h" -#include "mirror/class_loader.h" -#include "mirror/dex_cache-inl.h" -#include "mirror/dex_cache.h" -#include "mirror/executable.h" -#include "mirror/method.h" -#include "mirror/object-inl.h" -#include "mirror/object-refvisitor-inl.h" -#include "mirror/object_array-inl.h" -#include "mirror/string-inl.h" -#include "oat.h" -#include "oat_file.h" -#include "oat_file_manager.h" -#include "runtime.h" -#include "scoped_thread_state_change-inl.h" -#include "utils/dex_cache_arrays_layout-inl.h" -#include "well_known_classes.h" - -using ::art::mirror::Class; -using ::art::mirror::DexCache; -using ::art::mirror::Object; -using ::art::mirror::ObjectArray; -using ::art::mirror::String; - -namespace art { - -// Separate objects into multiple bins to optimize dirty memory use. -static constexpr bool kBinObjects = true; - -// Return true if an object is already in an image space. -bool ImageWriter::IsInBootImage(const void* obj) const { - gc::Heap* const heap = Runtime::Current()->GetHeap(); - if (!compile_app_image_) { - DCHECK(heap->GetBootImageSpaces().empty()); - return false; - } - for (gc::space::ImageSpace* boot_image_space : heap->GetBootImageSpaces()) { - const uint8_t* image_begin = boot_image_space->Begin(); - // Real image end including ArtMethods and ArtField sections. - const uint8_t* image_end = image_begin + boot_image_space->GetImageHeader().GetImageSize(); - if (image_begin <= obj && obj < image_end) { - return true; - } - } - return false; -} - -bool ImageWriter::IsInBootOatFile(const void* ptr) const { - gc::Heap* const heap = Runtime::Current()->GetHeap(); - if (!compile_app_image_) { - DCHECK(heap->GetBootImageSpaces().empty()); - return false; - } - for (gc::space::ImageSpace* boot_image_space : heap->GetBootImageSpaces()) { - const ImageHeader& image_header = boot_image_space->GetImageHeader(); - if (image_header.GetOatFileBegin() <= ptr && ptr < image_header.GetOatFileEnd()) { - return true; - } - } - return false; -} - -static void ClearDexFileCookies() REQUIRES_SHARED(Locks::mutator_lock_) { - auto visitor = [](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(obj != nullptr); - Class* klass = obj->GetClass(); - if (klass == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexFile)) { - ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); - // Null out the cookie to enable determinism. b/34090128 - field->SetObject</*kTransactionActive*/false>(obj, nullptr); - } - }; - Runtime::Current()->GetHeap()->VisitObjects(visitor); -} - -bool ImageWriter::PrepareImageAddressSpace() { - target_ptr_size_ = InstructionSetPointerSize(compiler_driver_.GetInstructionSet()); - gc::Heap* const heap = Runtime::Current()->GetHeap(); - { - ScopedObjectAccess soa(Thread::Current()); - PruneNonImageClasses(); // Remove junk - if (compile_app_image_) { - // Clear dex file cookies for app images to enable app image determinism. This is required - // since the cookie field contains long pointers to DexFiles which are not deterministic. - // b/34090128 - ClearDexFileCookies(); - } else { - // Avoid for app image since this may increase RAM and image size. - ComputeLazyFieldsForImageClasses(); // Add useful information - } - } - heap->CollectGarbage(false); // Remove garbage. - - if (kIsDebugBuild) { - ScopedObjectAccess soa(Thread::Current()); - CheckNonImageClassesRemoved(); - } - - { - ScopedObjectAccess soa(Thread::Current()); - CalculateNewObjectOffsets(); - } - - // This needs to happen after CalculateNewObjectOffsets since it relies on intern_table_bytes_ and - // bin size sums being calculated. - if (!AllocMemory()) { - return false; - } - - return true; -} - -bool ImageWriter::Write(int image_fd, - const std::vector<const char*>& image_filenames, - const std::vector<const char*>& oat_filenames) { - // If image_fd or oat_fd are not kInvalidFd then we may have empty strings in image_filenames or - // oat_filenames. - CHECK(!image_filenames.empty()); - if (image_fd != kInvalidFd) { - CHECK_EQ(image_filenames.size(), 1u); - } - CHECK(!oat_filenames.empty()); - CHECK_EQ(image_filenames.size(), oat_filenames.size()); - - { - ScopedObjectAccess soa(Thread::Current()); - for (size_t i = 0; i < oat_filenames.size(); ++i) { - CreateHeader(i); - CopyAndFixupNativeData(i); - } - } - - { - // TODO: heap validation can't handle these fix up passes. - ScopedObjectAccess soa(Thread::Current()); - Runtime::Current()->GetHeap()->DisableObjectValidation(); - CopyAndFixupObjects(); - } - - for (size_t i = 0; i < image_filenames.size(); ++i) { - const char* image_filename = image_filenames[i]; - ImageInfo& image_info = GetImageInfo(i); - std::unique_ptr<File> image_file; - if (image_fd != kInvalidFd) { - if (strlen(image_filename) == 0u) { - image_file.reset(new File(image_fd, unix_file::kCheckSafeUsage)); - // Empty the file in case it already exists. - if (image_file != nullptr) { - TEMP_FAILURE_RETRY(image_file->SetLength(0)); - TEMP_FAILURE_RETRY(image_file->Flush()); - } - } else { - LOG(ERROR) << "image fd " << image_fd << " name " << image_filename; - } - } else { - image_file.reset(OS::CreateEmptyFile(image_filename)); - } - - if (image_file == nullptr) { - LOG(ERROR) << "Failed to open image file " << image_filename; - return false; - } - - if (!compile_app_image_ && fchmod(image_file->Fd(), 0644) != 0) { - PLOG(ERROR) << "Failed to make image file world readable: " << image_filename; - image_file->Erase(); - return EXIT_FAILURE; - } - - std::unique_ptr<char[]> compressed_data; - // Image data size excludes the bitmap and the header. - ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin()); - const size_t image_data_size = image_header->GetImageSize() - sizeof(ImageHeader); - char* image_data = reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader); - size_t data_size; - const char* image_data_to_write; - const uint64_t compress_start_time = NanoTime(); - - CHECK_EQ(image_header->storage_mode_, image_storage_mode_); - switch (image_storage_mode_) { - case ImageHeader::kStorageModeLZ4HC: // Fall-through. - case ImageHeader::kStorageModeLZ4: { - const size_t compressed_max_size = LZ4_compressBound(image_data_size); - compressed_data.reset(new char[compressed_max_size]); - data_size = LZ4_compress_default( - reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader), - &compressed_data[0], - image_data_size, - compressed_max_size); - - break; - } - /* - * Disabled due to image_test64 flakyness. Both use same decompression. b/27560444 - case ImageHeader::kStorageModeLZ4HC: { - // Bound is same as non HC. - const size_t compressed_max_size = LZ4_compressBound(image_data_size); - compressed_data.reset(new char[compressed_max_size]); - data_size = LZ4_compressHC( - reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader), - &compressed_data[0], - image_data_size); - break; - } - */ - case ImageHeader::kStorageModeUncompressed: { - data_size = image_data_size; - image_data_to_write = image_data; - break; - } - default: { - LOG(FATAL) << "Unsupported"; - UNREACHABLE(); - } - } - - if (compressed_data != nullptr) { - image_data_to_write = &compressed_data[0]; - VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size << " in " - << PrettyDuration(NanoTime() - compress_start_time); - if (kIsDebugBuild) { - std::unique_ptr<uint8_t[]> temp(new uint8_t[image_data_size]); - const size_t decompressed_size = LZ4_decompress_safe( - reinterpret_cast<char*>(&compressed_data[0]), - reinterpret_cast<char*>(&temp[0]), - data_size, - image_data_size); - CHECK_EQ(decompressed_size, image_data_size); - CHECK_EQ(memcmp(image_data, &temp[0], image_data_size), 0) << image_storage_mode_; - } - } - - // Write out the image + fields + methods. - const bool is_compressed = compressed_data != nullptr; - if (!image_file->PwriteFully(image_data_to_write, data_size, sizeof(ImageHeader))) { - PLOG(ERROR) << "Failed to write image file data " << image_filename; - image_file->Erase(); - return false; - } - - // Write out the image bitmap at the page aligned start of the image end, also uncompressed for - // convenience. - const ImageSection& bitmap_section = image_header->GetImageBitmapSection(); - // Align up since data size may be unaligned if the image is compressed. - size_t bitmap_position_in_file = RoundUp(sizeof(ImageHeader) + data_size, kPageSize); - if (!is_compressed) { - CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset()); - } - if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_bitmap_->Begin()), - bitmap_section.Size(), - bitmap_position_in_file)) { - PLOG(ERROR) << "Failed to write image file " << image_filename; - image_file->Erase(); - return false; - } - - int err = image_file->Flush(); - if (err < 0) { - PLOG(ERROR) << "Failed to flush image file " << image_filename << " with result " << err; - image_file->Erase(); - return false; - } - - // Write header last in case the compiler gets killed in the middle of image writing. - // We do not want to have a corrupted image with a valid header. - // The header is uncompressed since it contains whether the image is compressed or not. - image_header->data_size_ = data_size; - if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_->Begin()), - sizeof(ImageHeader), - 0)) { - PLOG(ERROR) << "Failed to write image file header " << image_filename; - image_file->Erase(); - return false; - } - - CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(), - static_cast<size_t>(image_file->GetLength())); - if (image_file->FlushCloseOrErase() != 0) { - PLOG(ERROR) << "Failed to flush and close image file " << image_filename; - return false; - } - } - return true; -} - -void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) { - DCHECK(object != nullptr); - DCHECK_NE(offset, 0U); - - // The object is already deflated from when we set the bin slot. Just overwrite the lock word. - object->SetLockWord(LockWord::FromForwardingAddress(offset), false); - DCHECK_EQ(object->GetLockWord(false).ReadBarrierState(), 0u); - DCHECK(IsImageOffsetAssigned(object)); -} - -void ImageWriter::UpdateImageOffset(mirror::Object* obj, uintptr_t offset) { - DCHECK(IsImageOffsetAssigned(obj)) << obj << " " << offset; - obj->SetLockWord(LockWord::FromForwardingAddress(offset), false); - DCHECK_EQ(obj->GetLockWord(false).ReadBarrierState(), 0u); -} - -void ImageWriter::AssignImageOffset(mirror::Object* object, ImageWriter::BinSlot bin_slot) { - DCHECK(object != nullptr); - DCHECK_NE(image_objects_offset_begin_, 0u); - - size_t oat_index = GetOatIndex(object); - ImageInfo& image_info = GetImageInfo(oat_index); - size_t bin_slot_offset = image_info.bin_slot_offsets_[bin_slot.GetBin()]; - size_t new_offset = bin_slot_offset + bin_slot.GetIndex(); - DCHECK_ALIGNED(new_offset, kObjectAlignment); - - SetImageOffset(object, new_offset); - DCHECK_LT(new_offset, image_info.image_end_); -} - -bool ImageWriter::IsImageOffsetAssigned(mirror::Object* object) const { - // Will also return true if the bin slot was assigned since we are reusing the lock word. - DCHECK(object != nullptr); - return object->GetLockWord(false).GetState() == LockWord::kForwardingAddress; -} - -size_t ImageWriter::GetImageOffset(mirror::Object* object) const { - DCHECK(object != nullptr); - DCHECK(IsImageOffsetAssigned(object)); - LockWord lock_word = object->GetLockWord(false); - size_t offset = lock_word.ForwardingAddress(); - size_t oat_index = GetOatIndex(object); - const ImageInfo& image_info = GetImageInfo(oat_index); - DCHECK_LT(offset, image_info.image_end_); - return offset; -} - -void ImageWriter::SetImageBinSlot(mirror::Object* object, BinSlot bin_slot) { - DCHECK(object != nullptr); - DCHECK(!IsImageOffsetAssigned(object)); - DCHECK(!IsImageBinSlotAssigned(object)); - - // Before we stomp over the lock word, save the hash code for later. - LockWord lw(object->GetLockWord(false)); - switch (lw.GetState()) { - case LockWord::kFatLocked: - FALLTHROUGH_INTENDED; - case LockWord::kThinLocked: { - std::ostringstream oss; - bool thin = (lw.GetState() == LockWord::kThinLocked); - oss << (thin ? "Thin" : "Fat") - << " locked object " << object << "(" << object->PrettyTypeOf() - << ") found during object copy"; - if (thin) { - oss << ". Lock owner:" << lw.ThinLockOwner(); - } - LOG(FATAL) << oss.str(); - break; - } - case LockWord::kUnlocked: - // No hash, don't need to save it. - break; - case LockWord::kHashCode: - DCHECK(saved_hashcode_map_.find(object) == saved_hashcode_map_.end()); - saved_hashcode_map_.emplace(object, lw.GetHashCode()); - break; - default: - LOG(FATAL) << "Unreachable."; - UNREACHABLE(); - } - object->SetLockWord(LockWord::FromForwardingAddress(bin_slot.Uint32Value()), false); - DCHECK_EQ(object->GetLockWord(false).ReadBarrierState(), 0u); - DCHECK(IsImageBinSlotAssigned(object)); -} - -void ImageWriter::PrepareDexCacheArraySlots() { - // Prepare dex cache array starts based on the ordering specified in the CompilerDriver. - // Set the slot size early to avoid DCHECK() failures in IsImageBinSlotAssigned() - // when AssignImageBinSlot() assigns their indexes out or order. - for (const DexFile* dex_file : compiler_driver_.GetDexFilesForOatFile()) { - auto it = dex_file_oat_index_map_.find(dex_file); - DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation(); - ImageInfo& image_info = GetImageInfo(it->second); - image_info.dex_cache_array_starts_.Put(dex_file, image_info.bin_slot_sizes_[kBinDexCacheArray]); - DexCacheArraysLayout layout(target_ptr_size_, dex_file); - image_info.bin_slot_sizes_[kBinDexCacheArray] += layout.Size(); - } - - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Thread* const self = Thread::Current(); - ReaderMutexLock mu(self, *Locks::dex_lock_); - for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { - ObjPtr<mirror::DexCache> dex_cache = - ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root)); - if (dex_cache == nullptr || IsInBootImage(dex_cache.Ptr())) { - continue; - } - const DexFile* dex_file = dex_cache->GetDexFile(); - CHECK(dex_file_oat_index_map_.find(dex_file) != dex_file_oat_index_map_.end()) - << "Dex cache should have been pruned " << dex_file->GetLocation() - << "; possibly in class path"; - DexCacheArraysLayout layout(target_ptr_size_, dex_file); - DCHECK(layout.Valid()); - size_t oat_index = GetOatIndexForDexCache(dex_cache); - ImageInfo& image_info = GetImageInfo(oat_index); - uint32_t start = image_info.dex_cache_array_starts_.Get(dex_file); - DCHECK_EQ(dex_file->NumTypeIds() != 0u, dex_cache->GetResolvedTypes() != nullptr); - AddDexCacheArrayRelocation(dex_cache->GetResolvedTypes(), - start + layout.TypesOffset(), - dex_cache); - DCHECK_EQ(dex_file->NumMethodIds() != 0u, dex_cache->GetResolvedMethods() != nullptr); - AddDexCacheArrayRelocation(dex_cache->GetResolvedMethods(), - start + layout.MethodsOffset(), - dex_cache); - DCHECK_EQ(dex_file->NumFieldIds() != 0u, dex_cache->GetResolvedFields() != nullptr); - AddDexCacheArrayRelocation(dex_cache->GetResolvedFields(), - start + layout.FieldsOffset(), - dex_cache); - DCHECK_EQ(dex_file->NumStringIds() != 0u, dex_cache->GetStrings() != nullptr); - AddDexCacheArrayRelocation(dex_cache->GetStrings(), start + layout.StringsOffset(), dex_cache); - - if (dex_cache->GetResolvedMethodTypes() != nullptr) { - AddDexCacheArrayRelocation(dex_cache->GetResolvedMethodTypes(), - start + layout.MethodTypesOffset(), - dex_cache); - } - if (dex_cache->GetResolvedCallSites() != nullptr) { - AddDexCacheArrayRelocation(dex_cache->GetResolvedCallSites(), - start + layout.CallSitesOffset(), - dex_cache); - } - } -} - -void ImageWriter::AddDexCacheArrayRelocation(void* array, - size_t offset, - ObjPtr<mirror::DexCache> dex_cache) { - if (array != nullptr) { - DCHECK(!IsInBootImage(array)); - size_t oat_index = GetOatIndexForDexCache(dex_cache); - native_object_relocations_.emplace(array, - NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeDexCacheArray }); - } -} - -void ImageWriter::AddMethodPointerArray(mirror::PointerArray* arr) { - DCHECK(arr != nullptr); - if (kIsDebugBuild) { - for (size_t i = 0, len = arr->GetLength(); i < len; i++) { - ArtMethod* method = arr->GetElementPtrSize<ArtMethod*>(i, target_ptr_size_); - if (method != nullptr && !method->IsRuntimeMethod()) { - mirror::Class* klass = method->GetDeclaringClass(); - CHECK(klass == nullptr || KeepClass(klass)) - << Class::PrettyClass(klass) << " should be a kept class"; - } - } - } - // kBinArtMethodClean picked arbitrarily, just required to differentiate between ArtFields and - // ArtMethods. - pointer_arrays_.emplace(arr, kBinArtMethodClean); -} - -void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { - DCHECK(object != nullptr); - size_t object_size = object->SizeOf(); - - // The magic happens here. We segregate objects into different bins based - // on how likely they are to get dirty at runtime. - // - // Likely-to-dirty objects get packed together into the same bin so that - // at runtime their page dirtiness ratio (how many dirty objects a page has) is - // maximized. - // - // This means more pages will stay either clean or shared dirty (with zygote) and - // the app will use less of its own (private) memory. - Bin bin = kBinRegular; - size_t current_offset = 0u; - - if (kBinObjects) { - // - // Changing the bin of an object is purely a memory-use tuning. - // It has no change on runtime correctness. - // - // Memory analysis has determined that the following types of objects get dirtied - // the most: - // - // * Dex cache arrays are stored in a special bin. The arrays for each dex cache have - // a fixed layout which helps improve generated code (using PC-relative addressing), - // so we pre-calculate their offsets separately in PrepareDexCacheArraySlots(). - // Since these arrays are huge, most pages do not overlap other objects and it's not - // really important where they are for the clean/dirty separation. Due to their - // special PC-relative addressing, we arbitrarily keep them at the end. - // * Class'es which are verified [their clinit runs only at runtime] - // - classes in general [because their static fields get overwritten] - // - initialized classes with all-final statics are unlikely to be ever dirty, - // so bin them separately - // * Art Methods that are: - // - native [their native entry point is not looked up until runtime] - // - have declaring classes that aren't initialized - // [their interpreter/quick entry points are trampolines until the class - // becomes initialized] - // - // We also assume the following objects get dirtied either never or extremely rarely: - // * Strings (they are immutable) - // * Art methods that aren't native and have initialized declared classes - // - // We assume that "regular" bin objects are highly unlikely to become dirtied, - // so packing them together will not result in a noticeably tighter dirty-to-clean ratio. - // - if (object->IsClass()) { - bin = kBinClassVerified; - mirror::Class* klass = object->AsClass(); - - // Add non-embedded vtable to the pointer array table if there is one. - auto* vtable = klass->GetVTable(); - if (vtable != nullptr) { - AddMethodPointerArray(vtable); - } - auto* iftable = klass->GetIfTable(); - if (iftable != nullptr) { - for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { - if (iftable->GetMethodArrayCount(i) > 0) { - AddMethodPointerArray(iftable->GetMethodArray(i)); - } - } - } - - // Move known dirty objects into their own sections. This includes: - // - classes with dirty static fields. - if (dirty_image_objects_ != nullptr && - dirty_image_objects_->find(klass->PrettyDescriptor()) != dirty_image_objects_->end()) { - bin = kBinKnownDirty; - } else if (klass->GetStatus() == Class::kStatusInitialized) { - bin = kBinClassInitialized; - - // If the class's static fields are all final, put it into a separate bin - // since it's very likely it will stay clean. - uint32_t num_static_fields = klass->NumStaticFields(); - if (num_static_fields == 0) { - bin = kBinClassInitializedFinalStatics; - } else { - // Maybe all the statics are final? - bool all_final = true; - for (uint32_t i = 0; i < num_static_fields; ++i) { - ArtField* field = klass->GetStaticField(i); - if (!field->IsFinal()) { - all_final = false; - break; - } - } - - if (all_final) { - bin = kBinClassInitializedFinalStatics; - } - } - } - } else if (object->GetClass<kVerifyNone>()->IsStringClass()) { - bin = kBinString; // Strings are almost always immutable (except for object header). - } else if (object->GetClass<kVerifyNone>() == - Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kJavaLangObject)) { - // Instance of java lang object, probably a lock object. This means it will be dirty when we - // synchronize on it. - bin = kBinMiscDirty; - } else if (object->IsDexCache()) { - // Dex file field becomes dirty when the image is loaded. - bin = kBinMiscDirty; - } - // else bin = kBinRegular - } - - // Assign the oat index too. - DCHECK(oat_index_map_.find(object) == oat_index_map_.end()); - oat_index_map_.emplace(object, oat_index); - - ImageInfo& image_info = GetImageInfo(oat_index); - - size_t offset_delta = RoundUp(object_size, kObjectAlignment); // 64-bit alignment - current_offset = image_info.bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned). - // Move the current bin size up to accommodate the object we just assigned a bin slot. - image_info.bin_slot_sizes_[bin] += offset_delta; - - BinSlot new_bin_slot(bin, current_offset); - SetImageBinSlot(object, new_bin_slot); - - ++image_info.bin_slot_count_[bin]; - - // Grow the image closer to the end by the object we just assigned. - image_info.image_end_ += offset_delta; -} - -bool ImageWriter::WillMethodBeDirty(ArtMethod* m) const { - if (m->IsNative()) { - return true; - } - mirror::Class* declaring_class = m->GetDeclaringClass(); - // Initialized is highly unlikely to dirty since there's no entry points to mutate. - return declaring_class == nullptr || declaring_class->GetStatus() != Class::kStatusInitialized; -} - -bool ImageWriter::IsImageBinSlotAssigned(mirror::Object* object) const { - DCHECK(object != nullptr); - - // We always stash the bin slot into a lockword, in the 'forwarding address' state. - // If it's in some other state, then we haven't yet assigned an image bin slot. - if (object->GetLockWord(false).GetState() != LockWord::kForwardingAddress) { - return false; - } else if (kIsDebugBuild) { - LockWord lock_word = object->GetLockWord(false); - size_t offset = lock_word.ForwardingAddress(); - BinSlot bin_slot(offset); - size_t oat_index = GetOatIndex(object); - const ImageInfo& image_info = GetImageInfo(oat_index); - DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]) - << "bin slot offset should not exceed the size of that bin"; - } - return true; -} - -ImageWriter::BinSlot ImageWriter::GetImageBinSlot(mirror::Object* object) const { - DCHECK(object != nullptr); - DCHECK(IsImageBinSlotAssigned(object)); - - LockWord lock_word = object->GetLockWord(false); - size_t offset = lock_word.ForwardingAddress(); // TODO: ForwardingAddress should be uint32_t - DCHECK_LE(offset, std::numeric_limits<uint32_t>::max()); - - BinSlot bin_slot(static_cast<uint32_t>(offset)); - size_t oat_index = GetOatIndex(object); - const ImageInfo& image_info = GetImageInfo(oat_index); - DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]); - - return bin_slot; -} - -bool ImageWriter::AllocMemory() { - for (ImageInfo& image_info : image_infos_) { - ImageSection unused_sections[ImageHeader::kSectionCount]; - const size_t length = RoundUp( - image_info.CreateImageSections(unused_sections, compile_app_image_), kPageSize); - - std::string error_msg; - image_info.image_.reset(MemMap::MapAnonymous("image writer image", - nullptr, - length, - PROT_READ | PROT_WRITE, - false, - false, - &error_msg)); - if (UNLIKELY(image_info.image_.get() == nullptr)) { - LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg; - return false; - } - - // Create the image bitmap, only needs to cover mirror object section which is up to image_end_. - CHECK_LE(image_info.image_end_, length); - image_info.image_bitmap_.reset(gc::accounting::ContinuousSpaceBitmap::Create( - "image bitmap", image_info.image_->Begin(), RoundUp(image_info.image_end_, kPageSize))); - if (image_info.image_bitmap_.get() == nullptr) { - LOG(ERROR) << "Failed to allocate memory for image bitmap"; - return false; - } - } - return true; -} - -class ImageWriter::ComputeLazyFieldsForClassesVisitor : public ClassVisitor { - public: - bool operator()(ObjPtr<Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { - StackHandleScope<1> hs(Thread::Current()); - mirror::Class::ComputeName(hs.NewHandle(c)); - return true; - } -}; - -void ImageWriter::ComputeLazyFieldsForImageClasses() { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ComputeLazyFieldsForClassesVisitor visitor; - class_linker->VisitClassesWithoutClassesLock(&visitor); -} - -static bool IsBootClassLoaderClass(ObjPtr<mirror::Class> klass) - REQUIRES_SHARED(Locks::mutator_lock_) { - return klass->GetClassLoader() == nullptr; -} - -bool ImageWriter::IsBootClassLoaderNonImageClass(mirror::Class* klass) { - return IsBootClassLoaderClass(klass) && !IsInBootImage(klass); -} - -// This visitor follows the references of an instance, recursively then prune this class -// if a type of any field is pruned. -class ImageWriter::PruneObjectReferenceVisitor { - public: - PruneObjectReferenceVisitor(ImageWriter* image_writer, - bool* early_exit, - std::unordered_set<mirror::Object*>* visited, - bool* result) - : image_writer_(image_writer), early_exit_(early_exit), visited_(visited), result_(result) {} - - ALWAYS_INLINE void VisitRootIfNonNull( - mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const - REQUIRES_SHARED(Locks::mutator_lock_) { } - - ALWAYS_INLINE void VisitRoot( - mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const - REQUIRES_SHARED(Locks::mutator_lock_) { } - - ALWAYS_INLINE void operator() (ObjPtr<mirror::Object> obj, - MemberOffset offset, - bool is_static ATTRIBUTE_UNUSED) const - REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Object* ref = - obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(offset); - if (ref == nullptr || visited_->find(ref) != visited_->end()) { - return; - } - - ObjPtr<mirror::Class> klass = ref->IsClass() ? ref->AsClass() : ref->GetClass(); - if (klass == mirror::Method::StaticClass() || klass == mirror::Constructor::StaticClass()) { - // Prune all classes using reflection because the content they held will not be fixup. - *result_ = true; - } - - if (ref->IsClass()) { - *result_ = *result_ || - image_writer_->PruneAppImageClassInternal(ref->AsClass(), early_exit_, visited_); - } else { - // Record the object visited in case of circular reference. - visited_->emplace(ref); - *result_ = *result_ || - image_writer_->PruneAppImageClassInternal(klass, early_exit_, visited_); - ref->VisitReferences(*this, *this); - // Clean up before exit for next call of this function. - visited_->erase(ref); - } - } - - ALWAYS_INLINE void operator() (ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED, - ObjPtr<mirror::Reference> ref) const - REQUIRES_SHARED(Locks::mutator_lock_) { - operator()(ref, mirror::Reference::ReferentOffset(), /* is_static */ false); - } - - ALWAYS_INLINE bool GetResult() const { - return result_; - } - - private: - ImageWriter* image_writer_; - bool* early_exit_; - std::unordered_set<mirror::Object*>* visited_; - bool* const result_; -}; - - -bool ImageWriter::PruneAppImageClass(ObjPtr<mirror::Class> klass) { - bool early_exit = false; - std::unordered_set<mirror::Object*> visited; - return PruneAppImageClassInternal(klass, &early_exit, &visited); -} - -bool ImageWriter::PruneAppImageClassInternal( - ObjPtr<mirror::Class> klass, - bool* early_exit, - std::unordered_set<mirror::Object*>* visited) { - DCHECK(early_exit != nullptr); - DCHECK(visited != nullptr); - DCHECK(compile_app_image_); - if (klass == nullptr || IsInBootImage(klass.Ptr())) { - return false; - } - auto found = prune_class_memo_.find(klass.Ptr()); - if (found != prune_class_memo_.end()) { - // Already computed, return the found value. - return found->second; - } - // Circular dependencies, return false but do not store the result in the memoization table. - if (visited->find(klass.Ptr()) != visited->end()) { - *early_exit = true; - return false; - } - visited->emplace(klass.Ptr()); - bool result = IsBootClassLoaderClass(klass); - std::string temp; - // Prune if not an image class, this handles any broken sets of image classes such as having a - // class in the set but not it's superclass. - result = result || !compiler_driver_.IsImageClass(klass->GetDescriptor(&temp)); - bool my_early_exit = false; // Only for ourselves, ignore caller. - // Remove classes that failed to verify since we don't want to have java.lang.VerifyError in the - // app image. - if (klass->IsErroneous()) { - result = true; - } else { - ObjPtr<mirror::ClassExt> ext(klass->GetExtData()); - CHECK(ext.IsNull() || ext->GetVerifyError() == nullptr) << klass->PrettyClass(); - } - if (!result) { - // Check interfaces since these wont be visited through VisitReferences.) - mirror::IfTable* if_table = klass->GetIfTable(); - for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) { - result = result || PruneAppImageClassInternal(if_table->GetInterface(i), - &my_early_exit, - visited); - } - } - if (klass->IsObjectArrayClass()) { - result = result || PruneAppImageClassInternal(klass->GetComponentType(), - &my_early_exit, - visited); - } - // Check static fields and their classes. - if (klass->IsResolved() && klass->NumReferenceStaticFields() != 0) { - size_t num_static_fields = klass->NumReferenceStaticFields(); - // Presumably GC can happen when we are cross compiling, it should not cause performance - // problems to do pointer size logic. - MemberOffset field_offset = klass->GetFirstReferenceStaticFieldOffset( - Runtime::Current()->GetClassLinker()->GetImagePointerSize()); - for (size_t i = 0u; i < num_static_fields; ++i) { - mirror::Object* ref = klass->GetFieldObject<mirror::Object>(field_offset); - if (ref != nullptr) { - if (ref->IsClass()) { - result = result || PruneAppImageClassInternal(ref->AsClass(), - &my_early_exit, - visited); - } else { - mirror::Class* type = ref->GetClass(); - result = result || PruneAppImageClassInternal(type, - &my_early_exit, - visited); - if (!result) { - // For non-class case, also go through all the types mentioned by it's fields' - // references recursively to decide whether to keep this class. - bool tmp = false; - PruneObjectReferenceVisitor visitor(this, &my_early_exit, visited, &tmp); - ref->VisitReferences(visitor, visitor); - result = result || tmp; - } - } - } - field_offset = MemberOffset(field_offset.Uint32Value() + - sizeof(mirror::HeapReference<mirror::Object>)); - } - } - result = result || PruneAppImageClassInternal(klass->GetSuperClass(), - &my_early_exit, - visited); - // Remove the class if the dex file is not in the set of dex files. This happens for classes that - // are from uses-library if there is no profile. b/30688277 - mirror::DexCache* dex_cache = klass->GetDexCache(); - if (dex_cache != nullptr) { - result = result || - dex_file_oat_index_map_.find(dex_cache->GetDexFile()) == dex_file_oat_index_map_.end(); - } - // Erase the element we stored earlier since we are exiting the function. - auto it = visited->find(klass.Ptr()); - DCHECK(it != visited->end()); - visited->erase(it); - // Only store result if it is true or none of the calls early exited due to circular - // dependencies. If visited is empty then we are the root caller, in this case the cycle was in - // a child call and we can remember the result. - if (result == true || !my_early_exit || visited->empty()) { - prune_class_memo_[klass.Ptr()] = result; - } - *early_exit |= my_early_exit; - return result; -} - -bool ImageWriter::KeepClass(ObjPtr<mirror::Class> klass) { - if (klass == nullptr) { - return false; - } - if (compile_app_image_ && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) { - // Already in boot image, return true. - return true; - } - std::string temp; - if (!compiler_driver_.IsImageClass(klass->GetDescriptor(&temp))) { - return false; - } - if (compile_app_image_) { - // For app images, we need to prune boot loader classes that are not in the boot image since - // these may have already been loaded when the app image is loaded. - // Keep classes in the boot image space since we don't want to re-resolve these. - return !PruneAppImageClass(klass); - } - return true; -} - -class ImageWriter::PruneClassesVisitor : public ClassVisitor { - public: - PruneClassesVisitor(ImageWriter* image_writer, ObjPtr<mirror::ClassLoader> class_loader) - : image_writer_(image_writer), - class_loader_(class_loader), - classes_to_prune_(), - defined_class_count_(0u) { } - - bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { - if (!image_writer_->KeepClass(klass.Ptr())) { - classes_to_prune_.insert(klass.Ptr()); - if (klass->GetClassLoader() == class_loader_) { - ++defined_class_count_; - } - } - return true; - } - - size_t Prune() REQUIRES_SHARED(Locks::mutator_lock_) { - ClassTable* class_table = - Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader_); - for (mirror::Class* klass : classes_to_prune_) { - std::string storage; - const char* descriptor = klass->GetDescriptor(&storage); - bool result = class_table->Remove(descriptor); - DCHECK(result); - DCHECK(!class_table->Remove(descriptor)) << descriptor; - } - return defined_class_count_; - } - - private: - ImageWriter* const image_writer_; - const ObjPtr<mirror::ClassLoader> class_loader_; - std::unordered_set<mirror::Class*> classes_to_prune_; - size_t defined_class_count_; -}; - -class ImageWriter::PruneClassLoaderClassesVisitor : public ClassLoaderVisitor { - public: - explicit PruneClassLoaderClassesVisitor(ImageWriter* image_writer) - : image_writer_(image_writer), removed_class_count_(0) {} - - virtual void Visit(ObjPtr<mirror::ClassLoader> class_loader) OVERRIDE - REQUIRES_SHARED(Locks::mutator_lock_) { - PruneClassesVisitor classes_visitor(image_writer_, class_loader); - ClassTable* class_table = - Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader); - class_table->Visit(classes_visitor); - removed_class_count_ += classes_visitor.Prune(); - - // Record app image class loader. The fake boot class loader should not get registered - // and we should end up with only one class loader for an app and none for boot image. - if (class_loader != nullptr && class_table != nullptr) { - DCHECK(class_loader_ == nullptr); - class_loader_ = class_loader; - } - } - - size_t GetRemovedClassCount() const { - return removed_class_count_; - } - - ObjPtr<mirror::ClassLoader> GetClassLoader() const REQUIRES_SHARED(Locks::mutator_lock_) { - return class_loader_; - } - - private: - ImageWriter* const image_writer_; - size_t removed_class_count_; - ObjPtr<mirror::ClassLoader> class_loader_; -}; - -void ImageWriter::VisitClassLoaders(ClassLoaderVisitor* visitor) { - WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - visitor->Visit(nullptr); // Visit boot class loader. - Runtime::Current()->GetClassLinker()->VisitClassLoaders(visitor); -} - -void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, - ObjPtr<mirror::ClassLoader> class_loader) { - // To ensure deterministic contents of the hash-based arrays, each slot shall contain - // the candidate with the lowest index. As we're processing entries in increasing index - // order, this means trying to look up the entry for the current index if the slot is - // empty or if it contains a higher index. - - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); - const DexFile& dex_file = *dex_cache->GetDexFile(); - // Prune methods. - mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods(); - dex::TypeIndex last_class_idx; // Initialized to invalid index. - ObjPtr<mirror::Class> last_class = nullptr; - for (size_t i = 0, num = dex_cache->GetDexFile()->NumMethodIds(); i != num; ++i) { - uint32_t slot_idx = dex_cache->MethodSlotIndex(i); - auto pair = - mirror::DexCache::GetNativePairPtrSize(resolved_methods, slot_idx, target_ptr_size_); - uint32_t stored_index = pair.index; - ArtMethod* method = pair.object; - if (method != nullptr && i > stored_index) { - continue; // Already checked. - } - // Check if the referenced class is in the image. Note that we want to check the referenced - // class rather than the declaring class to preserve the semantics, i.e. using a MethodId - // results in resolving the referenced class and that can for example throw OOME. - const DexFile::MethodId& method_id = dex_file.GetMethodId(i); - if (method_id.class_idx_ != last_class_idx) { - last_class_idx = method_id.class_idx_; - last_class = class_linker->LookupResolvedType( - dex_file, last_class_idx, dex_cache, class_loader); - if (last_class != nullptr && !KeepClass(last_class)) { - last_class = nullptr; - } - } - if (method == nullptr || i < stored_index) { - if (last_class != nullptr) { - const char* name = dex_file.StringDataByIdx(method_id.name_idx_); - Signature signature = dex_file.GetMethodSignature(method_id); - if (last_class->IsInterface()) { - method = last_class->FindInterfaceMethod(name, signature, target_ptr_size_); - } else { - method = last_class->FindClassMethod(name, signature, target_ptr_size_); - } - if (method != nullptr) { - // If the referenced class is in the image, the defining class must also be there. - DCHECK(KeepClass(method->GetDeclaringClass())); - dex_cache->SetResolvedMethod(i, method, target_ptr_size_); - } - } - } else { - DCHECK_EQ(i, stored_index); - if (last_class == nullptr) { - dex_cache->ClearResolvedMethod(stored_index, target_ptr_size_); - } - } - } - // Prune fields and make the contents of the field array deterministic. - mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields(); - last_class_idx = dex::TypeIndex(); // Initialized to invalid index. - last_class = nullptr; - for (size_t i = 0, end = dex_file.NumFieldIds(); i < end; ++i) { - uint32_t slot_idx = dex_cache->FieldSlotIndex(i); - auto pair = mirror::DexCache::GetNativePairPtrSize(resolved_fields, slot_idx, target_ptr_size_); - uint32_t stored_index = pair.index; - ArtField* field = pair.object; - if (field != nullptr && i > stored_index) { - continue; // Already checked. - } - // Check if the referenced class is in the image. Note that we want to check the referenced - // class rather than the declaring class to preserve the semantics, i.e. using a FieldId - // results in resolving the referenced class and that can for example throw OOME. - const DexFile::FieldId& field_id = dex_file.GetFieldId(i); - if (field_id.class_idx_ != last_class_idx) { - last_class_idx = field_id.class_idx_; - last_class = class_linker->LookupResolvedType( - dex_file, last_class_idx, dex_cache, class_loader); - if (last_class != nullptr && !KeepClass(last_class)) { - last_class = nullptr; - } - } - if (field == nullptr || i < stored_index) { - if (last_class != nullptr) { - const char* name = dex_file.StringDataByIdx(field_id.name_idx_); - const char* type = dex_file.StringByTypeIdx(field_id.type_idx_); - field = mirror::Class::FindField(Thread::Current(), last_class, name, type); - if (field != nullptr) { - // If the referenced class is in the image, the defining class must also be there. - DCHECK(KeepClass(field->GetDeclaringClass())); - dex_cache->SetResolvedField(i, field, target_ptr_size_); - } - } - } else { - DCHECK_EQ(i, stored_index); - if (last_class == nullptr) { - dex_cache->ClearResolvedField(stored_index, target_ptr_size_); - } - } - } - // Prune types and make the contents of the type array deterministic. - // This is done after fields and methods as their lookup can touch the types array. - for (size_t i = 0, end = dex_cache->GetDexFile()->NumTypeIds(); i < end; ++i) { - dex::TypeIndex type_idx(i); - uint32_t slot_idx = dex_cache->TypeSlotIndex(type_idx); - mirror::TypeDexCachePair pair = - dex_cache->GetResolvedTypes()[slot_idx].load(std::memory_order_relaxed); - uint32_t stored_index = pair.index; - ObjPtr<mirror::Class> klass = pair.object.Read(); - if (klass == nullptr || i < stored_index) { - klass = class_linker->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader); - if (klass != nullptr) { - DCHECK_EQ(dex_cache->GetResolvedType(type_idx), klass); - stored_index = i; // For correct clearing below if not keeping the `klass`. - } - } else if (i == stored_index && !KeepClass(klass)) { - dex_cache->ClearResolvedType(dex::TypeIndex(stored_index)); - } - } - // Strings do not need pruning, but the contents of the string array must be deterministic. - for (size_t i = 0, end = dex_cache->GetDexFile()->NumStringIds(); i < end; ++i) { - dex::StringIndex string_idx(i); - uint32_t slot_idx = dex_cache->StringSlotIndex(string_idx); - mirror::StringDexCachePair pair = - dex_cache->GetStrings()[slot_idx].load(std::memory_order_relaxed); - uint32_t stored_index = pair.index; - ObjPtr<mirror::String> string = pair.object.Read(); - if (string == nullptr || i < stored_index) { - string = class_linker->LookupString(dex_file, string_idx, dex_cache); - DCHECK(string == nullptr || dex_cache->GetResolvedString(string_idx) == string); - } - } -} - -void ImageWriter::PruneNonImageClasses() { - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); - Thread* self = Thread::Current(); - ScopedAssertNoThreadSuspension sa(__FUNCTION__); - - // Prune uses-library dex caches. Only prune the uses-library dex caches since we want to make - // sure the other ones don't get unloaded before the OatWriter runs. - class_linker->VisitClassTables( - [&](ClassTable* table) REQUIRES_SHARED(Locks::mutator_lock_) { - table->RemoveStrongRoots( - [&](GcRoot<mirror::Object> root) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::Object> obj = root.Read(); - if (obj->IsDexCache()) { - // Return true if the dex file is not one of the ones in the map. - return dex_file_oat_index_map_.find(obj->AsDexCache()->GetDexFile()) == - dex_file_oat_index_map_.end(); - } - // Return false to avoid removing. - return false; - }); - }); - - // Remove the undesired classes from the class roots. - ObjPtr<mirror::ClassLoader> class_loader; - { - PruneClassLoaderClassesVisitor class_loader_visitor(this); - VisitClassLoaders(&class_loader_visitor); - VLOG(compiler) << "Pruned " << class_loader_visitor.GetRemovedClassCount() << " classes"; - class_loader = class_loader_visitor.GetClassLoader(); - DCHECK_EQ(class_loader != nullptr, compile_app_image_); - } - - // Clear references to removed classes from the DexCaches. - std::vector<ObjPtr<mirror::DexCache>> dex_caches; - { - ReaderMutexLock mu2(self, *Locks::dex_lock_); - dex_caches.reserve(class_linker->GetDexCachesData().size()); - for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { - if (self->IsJWeakCleared(data.weak_root)) { - continue; - } - dex_caches.push_back(self->DecodeJObject(data.weak_root)->AsDexCache()); - } - } - for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) { - PruneAndPreloadDexCache(dex_cache, class_loader); - } - - // Drop the array class cache in the ClassLinker, as these are roots holding those classes live. - class_linker->DropFindArrayClassCache(); - - // Clear to save RAM. - prune_class_memo_.clear(); -} - -void ImageWriter::CheckNonImageClassesRemoved() { - if (compiler_driver_.GetImageClasses() != nullptr) { - auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { - if (obj->IsClass() && !IsInBootImage(obj)) { - Class* klass = obj->AsClass(); - if (!KeepClass(klass)) { - DumpImageClasses(); - std::string temp; - CHECK(KeepClass(klass)) - << Runtime::Current()->GetHeap()->GetVerification()->FirstPathFromRootSet(klass); - } - } - }; - gc::Heap* heap = Runtime::Current()->GetHeap(); - heap->VisitObjects(visitor); - } -} - -void ImageWriter::DumpImageClasses() { - auto image_classes = compiler_driver_.GetImageClasses(); - CHECK(image_classes != nullptr); - for (const std::string& image_class : *image_classes) { - LOG(INFO) << " " << image_class; - } -} - -mirror::String* ImageWriter::FindInternedString(mirror::String* string) { - Thread* const self = Thread::Current(); - for (const ImageInfo& image_info : image_infos_) { - ObjPtr<mirror::String> const found = image_info.intern_table_->LookupStrong(self, string); - DCHECK(image_info.intern_table_->LookupWeak(self, string) == nullptr) - << string->ToModifiedUtf8(); - if (found != nullptr) { - return found.Ptr(); - } - } - if (compile_app_image_) { - Runtime* const runtime = Runtime::Current(); - ObjPtr<mirror::String> found = runtime->GetInternTable()->LookupStrong(self, string); - // If we found it in the runtime intern table it could either be in the boot image or interned - // during app image compilation. If it was in the boot image return that, otherwise return null - // since it belongs to another image space. - if (found != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(found.Ptr())) { - return found.Ptr(); - } - DCHECK(runtime->GetInternTable()->LookupWeak(self, string) == nullptr) - << string->ToModifiedUtf8(); - } - return nullptr; -} - - -ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const { - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); - Thread* self = Thread::Current(); - StackHandleScope<3> hs(self); - Handle<Class> object_array_class(hs.NewHandle( - class_linker->FindSystemClass(self, "[Ljava/lang/Object;"))); - - std::unordered_set<const DexFile*> image_dex_files; - for (auto& pair : dex_file_oat_index_map_) { - const DexFile* image_dex_file = pair.first; - size_t image_oat_index = pair.second; - if (oat_index == image_oat_index) { - image_dex_files.insert(image_dex_file); - } - } - - // build an Object[] of all the DexCaches used in the source_space_. - // Since we can't hold the dex lock when allocating the dex_caches - // ObjectArray, we lock the dex lock twice, first to get the number - // of dex caches first and then lock it again to copy the dex - // caches. We check that the number of dex caches does not change. - size_t dex_cache_count = 0; - { - ReaderMutexLock mu(self, *Locks::dex_lock_); - // Count number of dex caches not in the boot image. - for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { - ObjPtr<mirror::DexCache> dex_cache = - ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root)); - if (dex_cache == nullptr) { - continue; - } - const DexFile* dex_file = dex_cache->GetDexFile(); - if (!IsInBootImage(dex_cache.Ptr())) { - dex_cache_count += image_dex_files.find(dex_file) != image_dex_files.end() ? 1u : 0u; - } - } - } - Handle<ObjectArray<Object>> dex_caches( - hs.NewHandle(ObjectArray<Object>::Alloc(self, object_array_class.Get(), dex_cache_count))); - CHECK(dex_caches != nullptr) << "Failed to allocate a dex cache array."; - { - ReaderMutexLock mu(self, *Locks::dex_lock_); - size_t non_image_dex_caches = 0; - // Re-count number of non image dex caches. - for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { - ObjPtr<mirror::DexCache> dex_cache = - ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root)); - if (dex_cache == nullptr) { - continue; - } - const DexFile* dex_file = dex_cache->GetDexFile(); - if (!IsInBootImage(dex_cache.Ptr())) { - non_image_dex_caches += image_dex_files.find(dex_file) != image_dex_files.end() ? 1u : 0u; - } - } - CHECK_EQ(dex_cache_count, non_image_dex_caches) - << "The number of non-image dex caches changed."; - size_t i = 0; - for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { - ObjPtr<mirror::DexCache> dex_cache = - ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root)); - if (dex_cache == nullptr) { - continue; - } - const DexFile* dex_file = dex_cache->GetDexFile(); - if (!IsInBootImage(dex_cache.Ptr()) && - image_dex_files.find(dex_file) != image_dex_files.end()) { - dex_caches->Set<false>(i, dex_cache.Ptr()); - ++i; - } - } - } - - // build an Object[] of the roots needed to restore the runtime - int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_); - auto image_roots(hs.NewHandle( - ObjectArray<Object>::Alloc(self, object_array_class.Get(), image_roots_size))); - image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get()); - image_roots->Set<false>(ImageHeader::kClassRoots, class_linker->GetClassRoots()); - // image_roots[ImageHeader::kClassLoader] will be set later for app image. - static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax, - "Class loader should be the last image root."); - for (int32_t i = 0; i < ImageHeader::kImageRootsMax - 1; ++i) { - CHECK(image_roots->Get(i) != nullptr); - } - return image_roots.Get(); -} - -mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, - mirror::Object* obj, - size_t oat_index) { - if (obj == nullptr || IsInBootImage(obj)) { - // Object is null or already in the image, there is no work to do. - return obj; - } - if (!IsImageBinSlotAssigned(obj)) { - // We want to intern all strings but also assign offsets for the source string. Since the - // pruning phase has already happened, if we intern a string to one in the image we still - // end up copying an unreachable string. - if (obj->IsString()) { - // Need to check if the string is already interned in another image info so that we don't have - // the intern tables of two different images contain the same string. - mirror::String* interned = FindInternedString(obj->AsString()); - if (interned == nullptr) { - // Not in another image space, insert to our table. - interned = - GetImageInfo(oat_index).intern_table_->InternStrongImageString(obj->AsString()).Ptr(); - DCHECK_EQ(interned, obj); - } - } else if (obj->IsDexCache()) { - oat_index = GetOatIndexForDexCache(obj->AsDexCache()); - } else if (obj->IsClass()) { - // Visit and assign offsets for fields and field arrays. - mirror::Class* as_klass = obj->AsClass(); - mirror::DexCache* dex_cache = as_klass->GetDexCache(); - DCHECK(!as_klass->IsErroneous()) << as_klass->GetStatus(); - if (compile_app_image_) { - // Extra sanity, no boot loader classes should be left! - CHECK(!IsBootClassLoaderClass(as_klass)) << as_klass->PrettyClass(); - } - LengthPrefixedArray<ArtField>* fields[] = { - as_klass->GetSFieldsPtr(), as_klass->GetIFieldsPtr(), - }; - // Overwrite the oat index value since the class' dex cache is more accurate of where it - // belongs. - oat_index = GetOatIndexForDexCache(dex_cache); - ImageInfo& image_info = GetImageInfo(oat_index); - if (!compile_app_image_) { - // Note: Avoid locking to prevent lock order violations from root visiting; - // image_info.class_table_ is only accessed from the image writer. - image_info.class_table_->InsertWithoutLocks(as_klass); - } - for (LengthPrefixedArray<ArtField>* cur_fields : fields) { - // Total array length including header. - if (cur_fields != nullptr) { - const size_t header_size = LengthPrefixedArray<ArtField>::ComputeSize(0); - // Forward the entire array at once. - auto it = native_object_relocations_.find(cur_fields); - CHECK(it == native_object_relocations_.end()) << "Field array " << cur_fields - << " already forwarded"; - size_t& offset = image_info.bin_slot_sizes_[kBinArtField]; - DCHECK(!IsInBootImage(cur_fields)); - native_object_relocations_.emplace( - cur_fields, - NativeObjectRelocation { - oat_index, offset, kNativeObjectRelocationTypeArtFieldArray - }); - offset += header_size; - // Forward individual fields so that we can quickly find where they belong. - for (size_t i = 0, count = cur_fields->size(); i < count; ++i) { - // Need to forward arrays separate of fields. - ArtField* field = &cur_fields->At(i); - auto it2 = native_object_relocations_.find(field); - CHECK(it2 == native_object_relocations_.end()) << "Field at index=" << i - << " already assigned " << field->PrettyField() << " static=" << field->IsStatic(); - DCHECK(!IsInBootImage(field)); - native_object_relocations_.emplace( - field, - NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeArtField }); - offset += sizeof(ArtField); - } - } - } - // Visit and assign offsets for methods. - size_t num_methods = as_klass->NumMethods(); - if (num_methods != 0) { - bool any_dirty = false; - for (auto& m : as_klass->GetMethods(target_ptr_size_)) { - if (WillMethodBeDirty(&m)) { - any_dirty = true; - break; - } - } - NativeObjectRelocationType type = any_dirty - ? kNativeObjectRelocationTypeArtMethodDirty - : kNativeObjectRelocationTypeArtMethodClean; - Bin bin_type = BinTypeForNativeRelocationType(type); - // Forward the entire array at once, but header first. - const size_t method_alignment = ArtMethod::Alignment(target_ptr_size_); - const size_t method_size = ArtMethod::Size(target_ptr_size_); - const size_t header_size = LengthPrefixedArray<ArtMethod>::ComputeSize(0, - method_size, - method_alignment); - LengthPrefixedArray<ArtMethod>* array = as_klass->GetMethodsPtr(); - auto it = native_object_relocations_.find(array); - CHECK(it == native_object_relocations_.end()) - << "Method array " << array << " already forwarded"; - size_t& offset = image_info.bin_slot_sizes_[bin_type]; - DCHECK(!IsInBootImage(array)); - native_object_relocations_.emplace(array, - NativeObjectRelocation { - oat_index, - offset, - any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty - : kNativeObjectRelocationTypeArtMethodArrayClean }); - offset += header_size; - for (auto& m : as_klass->GetMethods(target_ptr_size_)) { - AssignMethodOffset(&m, type, oat_index); - } - (any_dirty ? dirty_methods_ : clean_methods_) += num_methods; - } - // Assign offsets for all runtime methods in the IMT since these may hold conflict tables - // live. - if (as_klass->ShouldHaveImt()) { - ImTable* imt = as_klass->GetImt(target_ptr_size_); - if (TryAssignImTableOffset(imt, oat_index)) { - // Since imt's can be shared only do this the first time to not double count imt method - // fixups. - for (size_t i = 0; i < ImTable::kSize; ++i) { - ArtMethod* imt_method = imt->Get(i, target_ptr_size_); - DCHECK(imt_method != nullptr); - if (imt_method->IsRuntimeMethod() && - !IsInBootImage(imt_method) && - !NativeRelocationAssigned(imt_method)) { - AssignMethodOffset(imt_method, kNativeObjectRelocationTypeRuntimeMethod, oat_index); - } - } - } - } - } else if (obj->IsClassLoader()) { - // Register the class loader if it has a class table. - // The fake boot class loader should not get registered and we should end up with only one - // class loader. - mirror::ClassLoader* class_loader = obj->AsClassLoader(); - if (class_loader->GetClassTable() != nullptr) { - DCHECK(compile_app_image_); - DCHECK(class_loaders_.empty()); - class_loaders_.insert(class_loader); - ImageInfo& image_info = GetImageInfo(oat_index); - // Note: Avoid locking to prevent lock order violations from root visiting; - // image_info.class_table_ table is only accessed from the image writer - // and class_loader->GetClassTable() is iterated but not modified. - image_info.class_table_->CopyWithoutLocks(*class_loader->GetClassTable()); - } - } - AssignImageBinSlot(obj, oat_index); - work_stack.emplace(obj, oat_index); - } - if (obj->IsString()) { - // Always return the interned string if there exists one. - mirror::String* interned = FindInternedString(obj->AsString()); - if (interned != nullptr) { - return interned; - } - } - return obj; -} - -bool ImageWriter::NativeRelocationAssigned(void* ptr) const { - return native_object_relocations_.find(ptr) != native_object_relocations_.end(); -} - -bool ImageWriter::TryAssignImTableOffset(ImTable* imt, size_t oat_index) { - // No offset, or already assigned. - if (imt == nullptr || IsInBootImage(imt) || NativeRelocationAssigned(imt)) { - return false; - } - // If the method is a conflict method we also want to assign the conflict table offset. - ImageInfo& image_info = GetImageInfo(oat_index); - const size_t size = ImTable::SizeInBytes(target_ptr_size_); - native_object_relocations_.emplace( - imt, - NativeObjectRelocation { - oat_index, - image_info.bin_slot_sizes_[kBinImTable], - kNativeObjectRelocationTypeIMTable}); - image_info.bin_slot_sizes_[kBinImTable] += size; - return true; -} - -void ImageWriter::TryAssignConflictTableOffset(ImtConflictTable* table, size_t oat_index) { - // No offset, or already assigned. - if (table == nullptr || NativeRelocationAssigned(table)) { - return; - } - CHECK(!IsInBootImage(table)); - // If the method is a conflict method we also want to assign the conflict table offset. - ImageInfo& image_info = GetImageInfo(oat_index); - const size_t size = table->ComputeSize(target_ptr_size_); - native_object_relocations_.emplace( - table, - NativeObjectRelocation { - oat_index, - image_info.bin_slot_sizes_[kBinIMTConflictTable], - kNativeObjectRelocationTypeIMTConflictTable}); - image_info.bin_slot_sizes_[kBinIMTConflictTable] += size; -} - -void ImageWriter::AssignMethodOffset(ArtMethod* method, - NativeObjectRelocationType type, - size_t oat_index) { - DCHECK(!IsInBootImage(method)); - CHECK(!NativeRelocationAssigned(method)) << "Method " << method << " already assigned " - << ArtMethod::PrettyMethod(method); - if (method->IsRuntimeMethod()) { - TryAssignConflictTableOffset(method->GetImtConflictTable(target_ptr_size_), oat_index); - } - ImageInfo& image_info = GetImageInfo(oat_index); - size_t& offset = image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(type)]; - native_object_relocations_.emplace(method, NativeObjectRelocation { oat_index, offset, type }); - offset += ArtMethod::Size(target_ptr_size_); -} - -void ImageWriter::UnbinObjectsIntoOffset(mirror::Object* obj) { - DCHECK(!IsInBootImage(obj)); - CHECK(obj != nullptr); - - // We know the bin slot, and the total bin sizes for all objects by now, - // so calculate the object's final image offset. - - DCHECK(IsImageBinSlotAssigned(obj)); - BinSlot bin_slot = GetImageBinSlot(obj); - // Change the lockword from a bin slot into an offset - AssignImageOffset(obj, bin_slot); -} - -class ImageWriter::VisitReferencesVisitor { - public: - VisitReferencesVisitor(ImageWriter* image_writer, WorkStack* work_stack, size_t oat_index) - : image_writer_(image_writer), work_stack_(work_stack), oat_index_(oat_index) {} - - // Fix up separately since we also need to fix up method entrypoints. - ALWAYS_INLINE void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const - REQUIRES_SHARED(Locks::mutator_lock_) { - if (!root->IsNull()) { - VisitRoot(root); - } - } - - ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const - REQUIRES_SHARED(Locks::mutator_lock_) { - root->Assign(VisitReference(root->AsMirrorPtr())); - } - - ALWAYS_INLINE void operator() (ObjPtr<mirror::Object> obj, - MemberOffset offset, - bool is_static ATTRIBUTE_UNUSED) const - REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Object* ref = - obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(offset); - obj->SetFieldObject</*kTransactionActive*/false>(offset, VisitReference(ref)); - } - - ALWAYS_INLINE void operator() (ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED, - ObjPtr<mirror::Reference> ref) const - REQUIRES_SHARED(Locks::mutator_lock_) { - operator()(ref, mirror::Reference::ReferentOffset(), /* is_static */ false); - } - - private: - mirror::Object* VisitReference(mirror::Object* ref) const REQUIRES_SHARED(Locks::mutator_lock_) { - return image_writer_->TryAssignBinSlot(*work_stack_, ref, oat_index_); - } - - ImageWriter* const image_writer_; - WorkStack* const work_stack_; - const size_t oat_index_; -}; - -class ImageWriter::GetRootsVisitor : public RootVisitor { - public: - explicit GetRootsVisitor(std::vector<mirror::Object*>* roots) : roots_(roots) {} - - void VisitRoots(mirror::Object*** roots, - size_t count, - const RootInfo& info ATTRIBUTE_UNUSED) OVERRIDE - REQUIRES_SHARED(Locks::mutator_lock_) { - for (size_t i = 0; i < count; ++i) { - roots_->push_back(*roots[i]); - } - } - - void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, - size_t count, - const RootInfo& info ATTRIBUTE_UNUSED) OVERRIDE - REQUIRES_SHARED(Locks::mutator_lock_) { - for (size_t i = 0; i < count; ++i) { - roots_->push_back(roots[i]->AsMirrorPtr()); - } - } - - private: - std::vector<mirror::Object*>* const roots_; -}; - -void ImageWriter::ProcessWorkStack(WorkStack* work_stack) { - while (!work_stack->empty()) { - std::pair<mirror::Object*, size_t> pair(work_stack->top()); - work_stack->pop(); - VisitReferencesVisitor visitor(this, work_stack, /*oat_index*/ pair.second); - // Walk references and assign bin slots for them. - pair.first->VisitReferences</*kVisitNativeRoots*/true, kVerifyNone, kWithoutReadBarrier>( - visitor, - visitor); - } -} - -void ImageWriter::CalculateNewObjectOffsets() { - Thread* const self = Thread::Current(); - VariableSizedHandleScope handles(self); - std::vector<Handle<ObjectArray<Object>>> image_roots; - for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) { - image_roots.push_back(handles.NewHandle(CreateImageRoots(i))); - } - - Runtime* const runtime = Runtime::Current(); - gc::Heap* const heap = runtime->GetHeap(); - - // Leave space for the header, but do not write it yet, we need to - // know where image_roots is going to end up - image_objects_offset_begin_ = RoundUp(sizeof(ImageHeader), kObjectAlignment); // 64-bit-alignment - - const size_t method_alignment = ArtMethod::Alignment(target_ptr_size_); - // Write the image runtime methods. - image_methods_[ImageHeader::kResolutionMethod] = runtime->GetResolutionMethod(); - image_methods_[ImageHeader::kImtConflictMethod] = runtime->GetImtConflictMethod(); - image_methods_[ImageHeader::kImtUnimplementedMethod] = runtime->GetImtUnimplementedMethod(); - image_methods_[ImageHeader::kSaveAllCalleeSavesMethod] = - runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveAllCalleeSaves); - image_methods_[ImageHeader::kSaveRefsOnlyMethod] = - runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsOnly); - image_methods_[ImageHeader::kSaveRefsAndArgsMethod] = - runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs); - image_methods_[ImageHeader::kSaveEverythingMethod] = - runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverything); - image_methods_[ImageHeader::kSaveEverythingMethodForClinit] = - runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit); - image_methods_[ImageHeader::kSaveEverythingMethodForSuspendCheck] = - runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck); - // Visit image methods first to have the main runtime methods in the first image. - for (auto* m : image_methods_) { - CHECK(m != nullptr); - CHECK(m->IsRuntimeMethod()); - DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image"; - if (!IsInBootImage(m)) { - AssignMethodOffset(m, kNativeObjectRelocationTypeRuntimeMethod, GetDefaultOatIndex()); - } - } - - // Deflate monitors before we visit roots since deflating acquires the monitor lock. Acquiring - // this lock while holding other locks may cause lock order violations. - { - auto deflate_monitor = [](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { - Monitor::Deflate(Thread::Current(), obj); - }; - heap->VisitObjects(deflate_monitor); - } - - // Work list of <object, oat_index> for objects. Everything on the stack must already be - // assigned a bin slot. - WorkStack work_stack; - - // Special case interned strings to put them in the image they are likely to be resolved from. - for (const DexFile* dex_file : compiler_driver_.GetDexFilesForOatFile()) { - auto it = dex_file_oat_index_map_.find(dex_file); - DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation(); - const size_t oat_index = it->second; - InternTable* const intern_table = runtime->GetInternTable(); - for (size_t i = 0, count = dex_file->NumStringIds(); i < count; ++i) { - uint32_t utf16_length; - const char* utf8_data = dex_file->StringDataAndUtf16LengthByIdx(dex::StringIndex(i), - &utf16_length); - mirror::String* string = intern_table->LookupStrong(self, utf16_length, utf8_data).Ptr(); - TryAssignBinSlot(work_stack, string, oat_index); - } - } - - // Get the GC roots and then visit them separately to avoid lock violations since the root visitor - // visits roots while holding various locks. - { - std::vector<mirror::Object*> roots; - GetRootsVisitor root_visitor(&roots); - runtime->VisitRoots(&root_visitor); - for (mirror::Object* obj : roots) { - TryAssignBinSlot(work_stack, obj, GetDefaultOatIndex()); - } - } - ProcessWorkStack(&work_stack); - - // For app images, there may be objects that are only held live by the by the boot image. One - // example is finalizer references. Forward these objects so that EnsureBinSlotAssignedCallback - // does not fail any checks. TODO: We should probably avoid copying these objects. - if (compile_app_image_) { - for (gc::space::ImageSpace* space : heap->GetBootImageSpaces()) { - DCHECK(space->IsImageSpace()); - gc::accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap(); - live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()), - reinterpret_cast<uintptr_t>(space->Limit()), - [this, &work_stack](mirror::Object* obj) - REQUIRES_SHARED(Locks::mutator_lock_) { - VisitReferencesVisitor visitor(this, &work_stack, GetDefaultOatIndex()); - // Visit all references and try to assign bin slots for them (calls TryAssignBinSlot). - obj->VisitReferences</*kVisitNativeRoots*/true, kVerifyNone, kWithoutReadBarrier>( - visitor, - visitor); - }); - } - // Process the work stack in case anything was added by TryAssignBinSlot. - ProcessWorkStack(&work_stack); - - // Store the class loader in the class roots. - CHECK_EQ(class_loaders_.size(), 1u); - CHECK_EQ(image_roots.size(), 1u); - CHECK(*class_loaders_.begin() != nullptr); - image_roots[0]->Set<false>(ImageHeader::kClassLoader, *class_loaders_.begin()); - } - - // Verify that all objects have assigned image bin slots. - { - auto ensure_bin_slots_assigned = [&](mirror::Object* obj) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (!Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(obj)) { - CHECK(IsImageBinSlotAssigned(obj)) << mirror::Object::PrettyTypeOf(obj) << " " << obj; - } - }; - heap->VisitObjects(ensure_bin_slots_assigned); - } - - // Calculate size of the dex cache arrays slot and prepare offsets. - PrepareDexCacheArraySlots(); - - // Calculate the sizes of the intern tables, class tables, and fixup tables. - for (ImageInfo& image_info : image_infos_) { - // Calculate how big the intern table will be after being serialized. - InternTable* const intern_table = image_info.intern_table_.get(); - CHECK_EQ(intern_table->WeakSize(), 0u) << " should have strong interned all the strings"; - if (intern_table->StrongSize() != 0u) { - image_info.intern_table_bytes_ = intern_table->WriteToMemory(nullptr); - } - - // Calculate the size of the class table. - ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); - DCHECK_EQ(image_info.class_table_->NumReferencedZygoteClasses(), 0u); - if (image_info.class_table_->NumReferencedNonZygoteClasses() != 0u) { - image_info.class_table_bytes_ += image_info.class_table_->WriteToMemory(nullptr); - } - } - - // Calculate bin slot offsets. - for (ImageInfo& image_info : image_infos_) { - size_t bin_offset = image_objects_offset_begin_; - for (size_t i = 0; i != kBinSize; ++i) { - switch (i) { - case kBinArtMethodClean: - case kBinArtMethodDirty: { - bin_offset = RoundUp(bin_offset, method_alignment); - break; - } - case kBinDexCacheArray: - bin_offset = RoundUp(bin_offset, DexCacheArraysLayout::Alignment(target_ptr_size_)); - break; - case kBinImTable: - case kBinIMTConflictTable: { - bin_offset = RoundUp(bin_offset, static_cast<size_t>(target_ptr_size_)); - break; - } - default: { - // Normal alignment. - } - } - image_info.bin_slot_offsets_[i] = bin_offset; - bin_offset += image_info.bin_slot_sizes_[i]; - } - // NOTE: There may be additional padding between the bin slots and the intern table. - DCHECK_EQ(image_info.image_end_, - GetBinSizeSum(image_info, kBinMirrorCount) + image_objects_offset_begin_); - } - - // Calculate image offsets. - size_t image_offset = 0; - for (ImageInfo& image_info : image_infos_) { - image_info.image_begin_ = global_image_begin_ + image_offset; - image_info.image_offset_ = image_offset; - ImageSection unused_sections[ImageHeader::kSectionCount]; - image_info.image_size_ = - RoundUp(image_info.CreateImageSections(unused_sections, compile_app_image_), kPageSize); - // There should be no gaps until the next image. - image_offset += image_info.image_size_; - } - - // Transform each object's bin slot into an offset which will be used to do the final copy. - { - auto unbin_objects_into_offset = [&](mirror::Object* obj) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (!IsInBootImage(obj)) { - UnbinObjectsIntoOffset(obj); - } - }; - heap->VisitObjects(unbin_objects_into_offset); - } - - size_t i = 0; - for (ImageInfo& image_info : image_infos_) { - image_info.image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots[i].Get())); - i++; - } - - // Update the native relocations by adding their bin sums. - for (auto& pair : native_object_relocations_) { - NativeObjectRelocation& relocation = pair.second; - Bin bin_type = BinTypeForNativeRelocationType(relocation.type); - ImageInfo& image_info = GetImageInfo(relocation.oat_index); - relocation.offset += image_info.bin_slot_offsets_[bin_type]; - } -} - -size_t ImageWriter::ImageInfo::CreateImageSections(ImageSection* out_sections, - bool app_image) const { - DCHECK(out_sections != nullptr); - - // Do not round up any sections here that are represented by the bins since it will break - // offsets. - - // Objects section - ImageSection* objects_section = &out_sections[ImageHeader::kSectionObjects]; - *objects_section = ImageSection(0u, image_end_); - - // Add field section. - ImageSection* field_section = &out_sections[ImageHeader::kSectionArtFields]; - *field_section = ImageSection(bin_slot_offsets_[kBinArtField], bin_slot_sizes_[kBinArtField]); - CHECK_EQ(bin_slot_offsets_[kBinArtField], field_section->Offset()); - - // Add method section. - ImageSection* methods_section = &out_sections[ImageHeader::kSectionArtMethods]; - *methods_section = ImageSection( - bin_slot_offsets_[kBinArtMethodClean], - bin_slot_sizes_[kBinArtMethodClean] + bin_slot_sizes_[kBinArtMethodDirty]); - - // IMT section. - ImageSection* imt_section = &out_sections[ImageHeader::kSectionImTables]; - *imt_section = ImageSection(bin_slot_offsets_[kBinImTable], bin_slot_sizes_[kBinImTable]); - - // Conflict tables section. - ImageSection* imt_conflict_tables_section = &out_sections[ImageHeader::kSectionIMTConflictTables]; - *imt_conflict_tables_section = ImageSection(bin_slot_offsets_[kBinIMTConflictTable], - bin_slot_sizes_[kBinIMTConflictTable]); - - // Runtime methods section. - ImageSection* runtime_methods_section = &out_sections[ImageHeader::kSectionRuntimeMethods]; - *runtime_methods_section = ImageSection(bin_slot_offsets_[kBinRuntimeMethod], - bin_slot_sizes_[kBinRuntimeMethod]); - - // Add dex cache arrays section. - ImageSection* dex_cache_arrays_section = &out_sections[ImageHeader::kSectionDexCacheArrays]; - *dex_cache_arrays_section = ImageSection(bin_slot_offsets_[kBinDexCacheArray], - bin_slot_sizes_[kBinDexCacheArray]); - // For boot image, round up to the page boundary to separate the interned strings and - // class table from the modifiable data. We shall mprotect() these pages read-only when - // we load the boot image. This is more than sufficient for the string table alignment, - // namely sizeof(uint64_t). See HashSet::WriteToMemory. - static_assert(IsAligned<sizeof(uint64_t)>(kPageSize), "String table alignment check."); - size_t cur_pos = - RoundUp(dex_cache_arrays_section->End(), app_image ? sizeof(uint64_t) : kPageSize); - // Calculate the size of the interned strings. - ImageSection* interned_strings_section = &out_sections[ImageHeader::kSectionInternedStrings]; - *interned_strings_section = ImageSection(cur_pos, intern_table_bytes_); - cur_pos = interned_strings_section->End(); - // Round up to the alignment the class table expects. See HashSet::WriteToMemory. - cur_pos = RoundUp(cur_pos, sizeof(uint64_t)); - // Calculate the size of the class table section. - ImageSection* class_table_section = &out_sections[ImageHeader::kSectionClassTable]; - *class_table_section = ImageSection(cur_pos, class_table_bytes_); - cur_pos = class_table_section->End(); - // Image end goes right before the start of the image bitmap. - return cur_pos; -} - -void ImageWriter::CreateHeader(size_t oat_index) { - ImageInfo& image_info = GetImageInfo(oat_index); - const uint8_t* oat_file_begin = image_info.oat_file_begin_; - const uint8_t* oat_file_end = oat_file_begin + image_info.oat_loaded_size_; - const uint8_t* oat_data_end = image_info.oat_data_begin_ + image_info.oat_size_; - - // Create the image sections. - ImageSection sections[ImageHeader::kSectionCount]; - const size_t image_end = image_info.CreateImageSections(sections, compile_app_image_); - - // Finally bitmap section. - const size_t bitmap_bytes = image_info.image_bitmap_->Size(); - auto* bitmap_section = §ions[ImageHeader::kSectionImageBitmap]; - *bitmap_section = ImageSection(RoundUp(image_end, kPageSize), RoundUp(bitmap_bytes, kPageSize)); - if (VLOG_IS_ON(compiler)) { - LOG(INFO) << "Creating header for " << oat_filenames_[oat_index]; - size_t idx = 0; - for (const ImageSection& section : sections) { - LOG(INFO) << static_cast<ImageHeader::ImageSections>(idx) << " " << section; - ++idx; - } - LOG(INFO) << "Methods: clean=" << clean_methods_ << " dirty=" << dirty_methods_; - LOG(INFO) << "Image roots address=" << std::hex << image_info.image_roots_address_ << std::dec; - LOG(INFO) << "Image begin=" << std::hex << reinterpret_cast<uintptr_t>(global_image_begin_) - << " Image offset=" << image_info.image_offset_ << std::dec; - LOG(INFO) << "Oat file begin=" << std::hex << reinterpret_cast<uintptr_t>(oat_file_begin) - << " Oat data begin=" << reinterpret_cast<uintptr_t>(image_info.oat_data_begin_) - << " Oat data end=" << reinterpret_cast<uintptr_t>(oat_data_end) - << " Oat file end=" << reinterpret_cast<uintptr_t>(oat_file_end); - } - // Store boot image info for app image so that we can relocate. - uint32_t boot_image_begin = 0; - uint32_t boot_image_end = 0; - uint32_t boot_oat_begin = 0; - uint32_t boot_oat_end = 0; - gc::Heap* const heap = Runtime::Current()->GetHeap(); - heap->GetBootImagesSize(&boot_image_begin, &boot_image_end, &boot_oat_begin, &boot_oat_end); - - // Create the header, leave 0 for data size since we will fill this in as we are writing the - // image. - new (image_info.image_->Begin()) ImageHeader(PointerToLowMemUInt32(image_info.image_begin_), - image_end, - sections, - image_info.image_roots_address_, - image_info.oat_checksum_, - PointerToLowMemUInt32(oat_file_begin), - PointerToLowMemUInt32(image_info.oat_data_begin_), - PointerToLowMemUInt32(oat_data_end), - PointerToLowMemUInt32(oat_file_end), - boot_image_begin, - boot_image_end - boot_image_begin, - boot_oat_begin, - boot_oat_end - boot_oat_begin, - static_cast<uint32_t>(target_ptr_size_), - compile_pic_, - /*is_pic*/compile_app_image_, - image_storage_mode_, - /*data_size*/0u); -} - -ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) { - auto it = native_object_relocations_.find(method); - CHECK(it != native_object_relocations_.end()) << ArtMethod::PrettyMethod(method) << " @ " - << method; - size_t oat_index = GetOatIndex(method->GetDexCache()); - ImageInfo& image_info = GetImageInfo(oat_index); - CHECK_GE(it->second.offset, image_info.image_end_) << "ArtMethods should be after Objects"; - return reinterpret_cast<ArtMethod*>(image_info.image_begin_ + it->second.offset); -} - -class ImageWriter::FixupRootVisitor : public RootVisitor { - public: - explicit FixupRootVisitor(ImageWriter* image_writer) : image_writer_(image_writer) { - } - - void VisitRoots(mirror::Object*** roots ATTRIBUTE_UNUSED, - size_t count ATTRIBUTE_UNUSED, - const RootInfo& info ATTRIBUTE_UNUSED) - OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { - LOG(FATAL) << "Unsupported"; - } - - void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count, - const RootInfo& info ATTRIBUTE_UNUSED) - OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { - for (size_t i = 0; i < count; ++i) { - image_writer_->CopyReference(roots[i], roots[i]->AsMirrorPtr()); - } - } - - private: - ImageWriter* const image_writer_; -}; - -void ImageWriter::CopyAndFixupImTable(ImTable* orig, ImTable* copy) { - for (size_t i = 0; i < ImTable::kSize; ++i) { - ArtMethod* method = orig->Get(i, target_ptr_size_); - void** address = reinterpret_cast<void**>(copy->AddressOfElement(i, target_ptr_size_)); - CopyAndFixupPointer(address, method); - DCHECK_EQ(copy->Get(i, target_ptr_size_), NativeLocationInImage(method)); - } -} - -void ImageWriter::CopyAndFixupImtConflictTable(ImtConflictTable* orig, ImtConflictTable* copy) { - const size_t count = orig->NumEntries(target_ptr_size_); - for (size_t i = 0; i < count; ++i) { - ArtMethod* interface_method = orig->GetInterfaceMethod(i, target_ptr_size_); - ArtMethod* implementation_method = orig->GetImplementationMethod(i, target_ptr_size_); - CopyAndFixupPointer(copy->AddressOfInterfaceMethod(i, target_ptr_size_), interface_method); - CopyAndFixupPointer(copy->AddressOfImplementationMethod(i, target_ptr_size_), - implementation_method); - DCHECK_EQ(copy->GetInterfaceMethod(i, target_ptr_size_), - NativeLocationInImage(interface_method)); - DCHECK_EQ(copy->GetImplementationMethod(i, target_ptr_size_), - NativeLocationInImage(implementation_method)); - } -} - -void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { - const ImageInfo& image_info = GetImageInfo(oat_index); - // Copy ArtFields and methods to their locations and update the array for convenience. - for (auto& pair : native_object_relocations_) { - NativeObjectRelocation& relocation = pair.second; - // Only work with fields and methods that are in the current oat file. - if (relocation.oat_index != oat_index) { - continue; - } - auto* dest = image_info.image_->Begin() + relocation.offset; - DCHECK_GE(dest, image_info.image_->Begin() + image_info.image_end_); - DCHECK(!IsInBootImage(pair.first)); - switch (relocation.type) { - case kNativeObjectRelocationTypeArtField: { - memcpy(dest, pair.first, sizeof(ArtField)); - CopyReference( - reinterpret_cast<ArtField*>(dest)->GetDeclaringClassAddressWithoutBarrier(), - reinterpret_cast<ArtField*>(pair.first)->GetDeclaringClass().Ptr()); - break; - } - case kNativeObjectRelocationTypeRuntimeMethod: - case kNativeObjectRelocationTypeArtMethodClean: - case kNativeObjectRelocationTypeArtMethodDirty: { - CopyAndFixupMethod(reinterpret_cast<ArtMethod*>(pair.first), - reinterpret_cast<ArtMethod*>(dest), - image_info); - break; - } - // For arrays, copy just the header since the elements will get copied by their corresponding - // relocations. - case kNativeObjectRelocationTypeArtFieldArray: { - memcpy(dest, pair.first, LengthPrefixedArray<ArtField>::ComputeSize(0)); - break; - } - case kNativeObjectRelocationTypeArtMethodArrayClean: - case kNativeObjectRelocationTypeArtMethodArrayDirty: { - size_t size = ArtMethod::Size(target_ptr_size_); - size_t alignment = ArtMethod::Alignment(target_ptr_size_); - memcpy(dest, pair.first, LengthPrefixedArray<ArtMethod>::ComputeSize(0, size, alignment)); - // Clear padding to avoid non-deterministic data in the image (and placate valgrind). - reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(dest)->ClearPadding(size, alignment); - break; - } - case kNativeObjectRelocationTypeDexCacheArray: - // Nothing to copy here, everything is done in FixupDexCache(). - break; - case kNativeObjectRelocationTypeIMTable: { - ImTable* orig_imt = reinterpret_cast<ImTable*>(pair.first); - ImTable* dest_imt = reinterpret_cast<ImTable*>(dest); - CopyAndFixupImTable(orig_imt, dest_imt); - break; - } - case kNativeObjectRelocationTypeIMTConflictTable: { - auto* orig_table = reinterpret_cast<ImtConflictTable*>(pair.first); - CopyAndFixupImtConflictTable( - orig_table, - new(dest)ImtConflictTable(orig_table->NumEntries(target_ptr_size_), target_ptr_size_)); - break; - } - } - } - // Fixup the image method roots. - auto* image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin()); - for (size_t i = 0; i < ImageHeader::kImageMethodsCount; ++i) { - ArtMethod* method = image_methods_[i]; - CHECK(method != nullptr); - if (!IsInBootImage(method)) { - method = NativeLocationInImage(method); - } - image_header->SetImageMethod(static_cast<ImageHeader::ImageMethod>(i), method); - } - FixupRootVisitor root_visitor(this); - - // Write the intern table into the image. - if (image_info.intern_table_bytes_ > 0) { - const ImageSection& intern_table_section = image_header->GetInternedStringsSection(); - InternTable* const intern_table = image_info.intern_table_.get(); - uint8_t* const intern_table_memory_ptr = - image_info.image_->Begin() + intern_table_section.Offset(); - const size_t intern_table_bytes = intern_table->WriteToMemory(intern_table_memory_ptr); - CHECK_EQ(intern_table_bytes, image_info.intern_table_bytes_); - // Fixup the pointers in the newly written intern table to contain image addresses. - InternTable temp_intern_table; - // Note that we require that ReadFromMemory does not make an internal copy of the elements so that - // the VisitRoots() will update the memory directly rather than the copies. - // This also relies on visit roots not doing any verification which could fail after we update - // the roots to be the image addresses. - temp_intern_table.AddTableFromMemory(intern_table_memory_ptr); - CHECK_EQ(temp_intern_table.Size(), intern_table->Size()); - temp_intern_table.VisitRoots(&root_visitor, kVisitRootFlagAllRoots); - } - // Write the class table(s) into the image. class_table_bytes_ may be 0 if there are multiple - // class loaders. Writing multiple class tables into the image is currently unsupported. - if (image_info.class_table_bytes_ > 0u) { - const ImageSection& class_table_section = image_header->GetClassTableSection(); - uint8_t* const class_table_memory_ptr = - image_info.image_->Begin() + class_table_section.Offset(); - ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - - ClassTable* table = image_info.class_table_.get(); - CHECK(table != nullptr); - const size_t class_table_bytes = table->WriteToMemory(class_table_memory_ptr); - CHECK_EQ(class_table_bytes, image_info.class_table_bytes_); - // Fixup the pointers in the newly written class table to contain image addresses. See - // above comment for intern tables. - ClassTable temp_class_table; - temp_class_table.ReadFromMemory(class_table_memory_ptr); - CHECK_EQ(temp_class_table.NumReferencedZygoteClasses(), - table->NumReferencedNonZygoteClasses() + table->NumReferencedZygoteClasses()); - UnbufferedRootVisitor visitor(&root_visitor, RootInfo(kRootUnknown)); - temp_class_table.VisitRoots(visitor); - } -} - -void ImageWriter::CopyAndFixupObjects() { - auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(obj != nullptr); - CopyAndFixupObject(obj); - }; - Runtime::Current()->GetHeap()->VisitObjects(visitor); - // Fix up the object previously had hash codes. - for (const auto& hash_pair : saved_hashcode_map_) { - Object* obj = hash_pair.first; - DCHECK_EQ(obj->GetLockWord<kVerifyNone>(false).ReadBarrierState(), 0U); - obj->SetLockWord<kVerifyNone>(LockWord::FromHashCode(hash_pair.second, 0U), false); - } - saved_hashcode_map_.clear(); -} - -void ImageWriter::FixupPointerArray(mirror::Object* dst, - mirror::PointerArray* arr, - mirror::Class* klass, - Bin array_type) { - CHECK(klass->IsArrayClass()); - CHECK(arr->IsIntArray() || arr->IsLongArray()) << klass->PrettyClass() << " " << arr; - // Fixup int and long pointers for the ArtMethod or ArtField arrays. - const size_t num_elements = arr->GetLength(); - dst->SetClass(GetImageAddress(arr->GetClass())); - auto* dest_array = down_cast<mirror::PointerArray*>(dst); - for (size_t i = 0, count = num_elements; i < count; ++i) { - void* elem = arr->GetElementPtrSize<void*>(i, target_ptr_size_); - if (kIsDebugBuild && elem != nullptr && !IsInBootImage(elem)) { - auto it = native_object_relocations_.find(elem); - if (UNLIKELY(it == native_object_relocations_.end())) { - if (it->second.IsArtMethodRelocation()) { - auto* method = reinterpret_cast<ArtMethod*>(elem); - LOG(FATAL) << "No relocation entry for ArtMethod " << method->PrettyMethod() << " @ " - << method << " idx=" << i << "/" << num_elements << " with declaring class " - << Class::PrettyClass(method->GetDeclaringClass()); - } else { - CHECK_EQ(array_type, kBinArtField); - auto* field = reinterpret_cast<ArtField*>(elem); - LOG(FATAL) << "No relocation entry for ArtField " << field->PrettyField() << " @ " - << field << " idx=" << i << "/" << num_elements << " with declaring class " - << Class::PrettyClass(field->GetDeclaringClass()); - } - UNREACHABLE(); - } - } - CopyAndFixupPointer(dest_array->ElementAddress(i, target_ptr_size_), elem); - } -} - -void ImageWriter::CopyAndFixupObject(Object* obj) { - if (IsInBootImage(obj)) { - return; - } - size_t offset = GetImageOffset(obj); - size_t oat_index = GetOatIndex(obj); - ImageInfo& image_info = GetImageInfo(oat_index); - auto* dst = reinterpret_cast<Object*>(image_info.image_->Begin() + offset); - DCHECK_LT(offset, image_info.image_end_); - const auto* src = reinterpret_cast<const uint8_t*>(obj); - - image_info.image_bitmap_->Set(dst); // Mark the obj as live. - - const size_t n = obj->SizeOf(); - DCHECK_LE(offset + n, image_info.image_->Size()); - memcpy(dst, src, n); - - // Write in a hash code of objects which have inflated monitors or a hash code in their monitor - // word. - const auto it = saved_hashcode_map_.find(obj); - dst->SetLockWord(it != saved_hashcode_map_.end() ? - LockWord::FromHashCode(it->second, 0u) : LockWord::Default(), false); - if (kUseBakerReadBarrier && gc::collector::ConcurrentCopying::kGrayDirtyImmuneObjects) { - // Treat all of the objects in the image as marked to avoid unnecessary dirty pages. This is - // safe since we mark all of the objects that may reference non immune objects as gray. - CHECK(dst->AtomicSetMarkBit(0, 1)); - } - FixupObject(obj, dst); -} - -// Rewrite all the references in the copied object to point to their image address equivalent -class ImageWriter::FixupVisitor { - public: - FixupVisitor(ImageWriter* image_writer, Object* copy) : image_writer_(image_writer), copy_(copy) { - } - - // Ignore class roots since we don't have a way to map them to the destination. These are handled - // with other logic. - void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) - const {} - void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {} - - - void operator()(ObjPtr<Object> obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const - REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) { - ObjPtr<Object> ref = obj->GetFieldObject<Object, kVerifyNone>(offset); - // Copy the reference and record the fixup if necessary. - image_writer_->CopyReference( - copy_->GetFieldObjectReferenceAddr<kVerifyNone>(offset), - ref.Ptr()); - } - - // java.lang.ref.Reference visitor. - void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED, - ObjPtr<mirror::Reference> ref) const - REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) { - operator()(ref, mirror::Reference::ReferentOffset(), /* is_static */ false); - } - - protected: - ImageWriter* const image_writer_; - mirror::Object* const copy_; -}; - -class ImageWriter::FixupClassVisitor FINAL : public FixupVisitor { - public: - FixupClassVisitor(ImageWriter* image_writer, Object* copy) : FixupVisitor(image_writer, copy) { - } - - void operator()(ObjPtr<Object> obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const - REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { - DCHECK(obj->IsClass()); - FixupVisitor::operator()(obj, offset, /*is_static*/false); - } - - void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED, - ObjPtr<mirror::Reference> ref ATTRIBUTE_UNUSED) const - REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) { - LOG(FATAL) << "Reference not expected here."; - } -}; - -uintptr_t ImageWriter::NativeOffsetInImage(void* obj) { - DCHECK(obj != nullptr); - DCHECK(!IsInBootImage(obj)); - auto it = native_object_relocations_.find(obj); - CHECK(it != native_object_relocations_.end()) << obj << " spaces " - << Runtime::Current()->GetHeap()->DumpSpaces(); - const NativeObjectRelocation& relocation = it->second; - return relocation.offset; -} - -template <typename T> -std::string PrettyPrint(T* ptr) REQUIRES_SHARED(Locks::mutator_lock_) { - std::ostringstream oss; - oss << ptr; - return oss.str(); -} - -template <> -std::string PrettyPrint(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { - return ArtMethod::PrettyMethod(method); -} - -template <typename T> -T* ImageWriter::NativeLocationInImage(T* obj) { - if (obj == nullptr || IsInBootImage(obj)) { - return obj; - } else { - auto it = native_object_relocations_.find(obj); - CHECK(it != native_object_relocations_.end()) << obj << " " << PrettyPrint(obj) - << " spaces " << Runtime::Current()->GetHeap()->DumpSpaces(); - const NativeObjectRelocation& relocation = it->second; - ImageInfo& image_info = GetImageInfo(relocation.oat_index); - return reinterpret_cast<T*>(image_info.image_begin_ + relocation.offset); - } -} - -template <typename T> -T* ImageWriter::NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) { - if (obj == nullptr || IsInBootImage(obj)) { - return obj; - } else { - size_t oat_index = GetOatIndexForDexCache(dex_cache); - ImageInfo& image_info = GetImageInfo(oat_index); - return reinterpret_cast<T*>(image_info.image_->Begin() + NativeOffsetInImage(obj)); - } -} - -class ImageWriter::NativeLocationVisitor { - public: - explicit NativeLocationVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {} - - template <typename T> - T* operator()(T* ptr, void** dest_addr = nullptr) const REQUIRES_SHARED(Locks::mutator_lock_) { - if (dest_addr != nullptr) { - image_writer_->CopyAndFixupPointer(dest_addr, ptr); - } - return image_writer_->NativeLocationInImage(ptr); - } - - private: - ImageWriter* const image_writer_; -}; - -void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) { - orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this)); - FixupClassVisitor visitor(this, copy); - ObjPtr<mirror::Object>(orig)->VisitReferences(visitor, visitor); - - // Remove the clinitThreadId. This is required for image determinism. - copy->SetClinitThreadId(static_cast<pid_t>(0)); -} - -void ImageWriter::FixupObject(Object* orig, Object* copy) { - DCHECK(orig != nullptr); - DCHECK(copy != nullptr); - if (kUseBakerReadBarrier) { - orig->AssertReadBarrierState(); - } - auto* klass = orig->GetClass(); - if (klass->IsIntArrayClass() || klass->IsLongArrayClass()) { - // Is this a native pointer array? - auto it = pointer_arrays_.find(down_cast<mirror::PointerArray*>(orig)); - if (it != pointer_arrays_.end()) { - // Should only need to fixup every pointer array exactly once. - FixupPointerArray(copy, down_cast<mirror::PointerArray*>(orig), klass, it->second); - pointer_arrays_.erase(it); - return; - } - } - if (orig->IsClass()) { - FixupClass(orig->AsClass<kVerifyNone>(), down_cast<mirror::Class*>(copy)); - } else { - if (klass == mirror::Method::StaticClass() || klass == mirror::Constructor::StaticClass()) { - // Need to go update the ArtMethod. - auto* dest = down_cast<mirror::Executable*>(copy); - auto* src = down_cast<mirror::Executable*>(orig); - ArtMethod* src_method = src->GetArtMethod(); - dest->SetArtMethod(GetImageMethodAddress(src_method)); - } else if (!klass->IsArrayClass()) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - if (klass == class_linker->GetClassRoot(ClassLinker::kJavaLangDexCache)) { - FixupDexCache(down_cast<mirror::DexCache*>(orig), down_cast<mirror::DexCache*>(copy)); - } else if (klass->IsClassLoaderClass()) { - mirror::ClassLoader* copy_loader = down_cast<mirror::ClassLoader*>(copy); - // If src is a ClassLoader, set the class table to null so that it gets recreated by the - // ClassLoader. - copy_loader->SetClassTable(nullptr); - // Also set allocator to null to be safe. The allocator is created when we create the class - // table. We also never expect to unload things in the image since they are held live as - // roots. - copy_loader->SetAllocator(nullptr); - } - } - FixupVisitor visitor(this, copy); - orig->VisitReferences(visitor, visitor); - } -} - -class ImageWriter::ImageAddressVisitorForDexCacheArray { - public: - explicit ImageAddressVisitorForDexCacheArray(ImageWriter* image_writer) - : image_writer_(image_writer) {} - - template <typename T> - T* operator()(T* ptr) const REQUIRES_SHARED(Locks::mutator_lock_) { - return image_writer_->GetImageAddress(ptr); - } - - private: - ImageWriter* const image_writer_; -}; - -void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache, - mirror::DexCache* copy_dex_cache) { - ImageAddressVisitorForDexCacheArray fixup_visitor(this); - // Though the DexCache array fields are usually treated as native pointers, we set the full - // 64-bit values here, clearing the top 32 bits for 32-bit targets. The zero-extension is - // done by casting to the unsigned type uintptr_t before casting to int64_t, i.e. - // static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + offset))). - mirror::StringDexCacheType* orig_strings = orig_dex_cache->GetStrings(); - if (orig_strings != nullptr) { - copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::StringsOffset(), - NativeLocationInImage(orig_strings), - PointerSize::k64); - orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings, orig_dex_cache), fixup_visitor); - } - mirror::TypeDexCacheType* orig_types = orig_dex_cache->GetResolvedTypes(); - if (orig_types != nullptr) { - copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedTypesOffset(), - NativeLocationInImage(orig_types), - PointerSize::k64); - orig_dex_cache->FixupResolvedTypes(NativeCopyLocation(orig_types, orig_dex_cache), - fixup_visitor); - } - mirror::MethodDexCacheType* orig_methods = orig_dex_cache->GetResolvedMethods(); - if (orig_methods != nullptr) { - copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedMethodsOffset(), - NativeLocationInImage(orig_methods), - PointerSize::k64); - mirror::MethodDexCacheType* copy_methods = NativeCopyLocation(orig_methods, orig_dex_cache); - for (size_t i = 0, num = orig_dex_cache->NumResolvedMethods(); i != num; ++i) { - mirror::MethodDexCachePair orig_pair = - mirror::DexCache::GetNativePairPtrSize(orig_methods, i, target_ptr_size_); - // NativeLocationInImage also handles runtime methods since these have relocation info. - mirror::MethodDexCachePair copy_pair(NativeLocationInImage(orig_pair.object), - orig_pair.index); - mirror::DexCache::SetNativePairPtrSize(copy_methods, i, copy_pair, target_ptr_size_); - } - } - mirror::FieldDexCacheType* orig_fields = orig_dex_cache->GetResolvedFields(); - if (orig_fields != nullptr) { - copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedFieldsOffset(), - NativeLocationInImage(orig_fields), - PointerSize::k64); - mirror::FieldDexCacheType* copy_fields = NativeCopyLocation(orig_fields, orig_dex_cache); - for (size_t i = 0, num = orig_dex_cache->NumResolvedFields(); i != num; ++i) { - mirror::FieldDexCachePair orig = - mirror::DexCache::GetNativePairPtrSize(orig_fields, i, target_ptr_size_); - mirror::FieldDexCachePair copy = orig; - copy.object = NativeLocationInImage(orig.object); - mirror::DexCache::SetNativePairPtrSize(copy_fields, i, copy, target_ptr_size_); - } - } - mirror::MethodTypeDexCacheType* orig_method_types = orig_dex_cache->GetResolvedMethodTypes(); - if (orig_method_types != nullptr) { - copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedMethodTypesOffset(), - NativeLocationInImage(orig_method_types), - PointerSize::k64); - orig_dex_cache->FixupResolvedMethodTypes(NativeCopyLocation(orig_method_types, orig_dex_cache), - fixup_visitor); - } - GcRoot<mirror::CallSite>* orig_call_sites = orig_dex_cache->GetResolvedCallSites(); - if (orig_call_sites != nullptr) { - copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedCallSitesOffset(), - NativeLocationInImage(orig_call_sites), - PointerSize::k64); - orig_dex_cache->FixupResolvedCallSites(NativeCopyLocation(orig_call_sites, orig_dex_cache), - fixup_visitor); - } - - // Remove the DexFile pointers. They will be fixed up when the runtime loads the oat file. Leaving - // compiler pointers in here will make the output non-deterministic. - copy_dex_cache->SetDexFile(nullptr); -} - -const uint8_t* ImageWriter::GetOatAddress(OatAddress type) const { - DCHECK_LT(type, kOatAddressCount); - // If we are compiling an app image, we need to use the stubs of the boot image. - if (compile_app_image_) { - // Use the current image pointers. - const std::vector<gc::space::ImageSpace*>& image_spaces = - Runtime::Current()->GetHeap()->GetBootImageSpaces(); - DCHECK(!image_spaces.empty()); - const OatFile* oat_file = image_spaces[0]->GetOatFile(); - CHECK(oat_file != nullptr); - const OatHeader& header = oat_file->GetOatHeader(); - switch (type) { - // TODO: We could maybe clean this up if we stored them in an array in the oat header. - case kOatAddressQuickGenericJNITrampoline: - return static_cast<const uint8_t*>(header.GetQuickGenericJniTrampoline()); - case kOatAddressInterpreterToInterpreterBridge: - return static_cast<const uint8_t*>(header.GetInterpreterToInterpreterBridge()); - case kOatAddressInterpreterToCompiledCodeBridge: - return static_cast<const uint8_t*>(header.GetInterpreterToCompiledCodeBridge()); - case kOatAddressJNIDlsymLookup: - return static_cast<const uint8_t*>(header.GetJniDlsymLookup()); - case kOatAddressQuickIMTConflictTrampoline: - return static_cast<const uint8_t*>(header.GetQuickImtConflictTrampoline()); - case kOatAddressQuickResolutionTrampoline: - return static_cast<const uint8_t*>(header.GetQuickResolutionTrampoline()); - case kOatAddressQuickToInterpreterBridge: - return static_cast<const uint8_t*>(header.GetQuickToInterpreterBridge()); - default: - UNREACHABLE(); - } - } - const ImageInfo& primary_image_info = GetImageInfo(0); - return GetOatAddressForOffset(primary_image_info.oat_address_offsets_[type], primary_image_info); -} - -const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, - const ImageInfo& image_info, - bool* quick_is_interpreted) { - DCHECK(!method->IsResolutionMethod()) << method->PrettyMethod(); - DCHECK_NE(method, Runtime::Current()->GetImtConflictMethod()) << method->PrettyMethod(); - DCHECK(!method->IsImtUnimplementedMethod()) << method->PrettyMethod(); - DCHECK(method->IsInvokable()) << method->PrettyMethod(); - DCHECK(!IsInBootImage(method)) << method->PrettyMethod(); - - // Use original code if it exists. Otherwise, set the code pointer to the resolution - // trampoline. - - // Quick entrypoint: - const void* quick_oat_entry_point = - method->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_); - const uint8_t* quick_code; - - if (UNLIKELY(IsInBootImage(method->GetDeclaringClass()))) { - DCHECK(method->IsCopied()); - // If the code is not in the oat file corresponding to this image (e.g. default methods) - quick_code = reinterpret_cast<const uint8_t*>(quick_oat_entry_point); - } else { - uint32_t quick_oat_code_offset = PointerToLowMemUInt32(quick_oat_entry_point); - quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info); - } - - *quick_is_interpreted = false; - if (quick_code != nullptr && (!method->IsStatic() || method->IsConstructor() || - method->GetDeclaringClass()->IsInitialized())) { - // We have code for a non-static or initialized method, just use the code. - } else if (quick_code == nullptr && method->IsNative() && - (!method->IsStatic() || method->GetDeclaringClass()->IsInitialized())) { - // Non-static or initialized native method missing compiled code, use generic JNI version. - quick_code = GetOatAddress(kOatAddressQuickGenericJNITrampoline); - } else if (quick_code == nullptr && !method->IsNative()) { - // We don't have code at all for a non-native method, use the interpreter. - quick_code = GetOatAddress(kOatAddressQuickToInterpreterBridge); - *quick_is_interpreted = true; - } else { - CHECK(!method->GetDeclaringClass()->IsInitialized()); - // We have code for a static method, but need to go through the resolution stub for class - // initialization. - quick_code = GetOatAddress(kOatAddressQuickResolutionTrampoline); - } - if (!IsInBootOatFile(quick_code)) { - // DCHECK_GE(quick_code, oat_data_begin_); - } - return quick_code; -} - -void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, - ArtMethod* copy, - const ImageInfo& image_info) { - if (orig->IsAbstract()) { - // Ignore the single-implementation info for abstract method. - // Do this on orig instead of copy, otherwise there is a crash due to methods - // are copied before classes. - // TODO: handle fixup of single-implementation method for abstract method. - orig->SetHasSingleImplementation(false); - orig->SetSingleImplementation( - nullptr, Runtime::Current()->GetClassLinker()->GetImagePointerSize()); - } - - memcpy(copy, orig, ArtMethod::Size(target_ptr_size_)); - - CopyReference(copy->GetDeclaringClassAddressWithoutBarrier(), orig->GetDeclaringClassUnchecked()); - - // OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to - // oat_begin_ - - // The resolution method has a special trampoline to call. - Runtime* runtime = Runtime::Current(); - if (orig->IsRuntimeMethod()) { - ImtConflictTable* orig_table = orig->GetImtConflictTable(target_ptr_size_); - if (orig_table != nullptr) { - // Special IMT conflict method, normal IMT conflict method or unimplemented IMT method. - copy->SetEntryPointFromQuickCompiledCodePtrSize( - GetOatAddress(kOatAddressQuickIMTConflictTrampoline), target_ptr_size_); - copy->SetImtConflictTable(NativeLocationInImage(orig_table), target_ptr_size_); - } else if (UNLIKELY(orig == runtime->GetResolutionMethod())) { - copy->SetEntryPointFromQuickCompiledCodePtrSize( - GetOatAddress(kOatAddressQuickResolutionTrampoline), target_ptr_size_); - } else { - bool found_one = false; - for (size_t i = 0; i < static_cast<size_t>(CalleeSaveType::kLastCalleeSaveType); ++i) { - auto idx = static_cast<CalleeSaveType>(i); - if (runtime->HasCalleeSaveMethod(idx) && runtime->GetCalleeSaveMethod(idx) == orig) { - found_one = true; - break; - } - } - CHECK(found_one) << "Expected to find callee save method but got " << orig->PrettyMethod(); - CHECK(copy->IsRuntimeMethod()); - } - } else { - // We assume all methods have code. If they don't currently then we set them to the use the - // resolution trampoline. Abstract methods never have code and so we need to make sure their - // use results in an AbstractMethodError. We use the interpreter to achieve this. - if (UNLIKELY(!orig->IsInvokable())) { - copy->SetEntryPointFromQuickCompiledCodePtrSize( - GetOatAddress(kOatAddressQuickToInterpreterBridge), target_ptr_size_); - } else { - bool quick_is_interpreted; - const uint8_t* quick_code = GetQuickCode(orig, image_info, &quick_is_interpreted); - copy->SetEntryPointFromQuickCompiledCodePtrSize(quick_code, target_ptr_size_); - - // JNI entrypoint: - if (orig->IsNative()) { - // The native method's pointer is set to a stub to lookup via dlsym. - // Note this is not the code_ pointer, that is handled above. - copy->SetEntryPointFromJniPtrSize( - GetOatAddress(kOatAddressJNIDlsymLookup), target_ptr_size_); - } - } - } -} - -size_t ImageWriter::GetBinSizeSum(ImageWriter::ImageInfo& image_info, ImageWriter::Bin up_to) const { - DCHECK_LE(up_to, kBinSize); - return std::accumulate(&image_info.bin_slot_sizes_[0], - &image_info.bin_slot_sizes_[up_to], - /*init*/0); -} - -ImageWriter::BinSlot::BinSlot(uint32_t lockword) : lockword_(lockword) { - // These values may need to get updated if more bins are added to the enum Bin - static_assert(kBinBits == 3, "wrong number of bin bits"); - static_assert(kBinShift == 27, "wrong number of shift"); - static_assert(sizeof(BinSlot) == sizeof(LockWord), "BinSlot/LockWord must have equal sizes"); - - DCHECK_LT(GetBin(), kBinSize); - DCHECK_ALIGNED(GetIndex(), kObjectAlignment); -} - -ImageWriter::BinSlot::BinSlot(Bin bin, uint32_t index) - : BinSlot(index | (static_cast<uint32_t>(bin) << kBinShift)) { - DCHECK_EQ(index, GetIndex()); -} - -ImageWriter::Bin ImageWriter::BinSlot::GetBin() const { - return static_cast<Bin>((lockword_ & kBinMask) >> kBinShift); -} - -uint32_t ImageWriter::BinSlot::GetIndex() const { - return lockword_ & ~kBinMask; -} - -ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocationType type) { - switch (type) { - case kNativeObjectRelocationTypeArtField: - case kNativeObjectRelocationTypeArtFieldArray: - return kBinArtField; - case kNativeObjectRelocationTypeArtMethodClean: - case kNativeObjectRelocationTypeArtMethodArrayClean: - return kBinArtMethodClean; - case kNativeObjectRelocationTypeArtMethodDirty: - case kNativeObjectRelocationTypeArtMethodArrayDirty: - return kBinArtMethodDirty; - case kNativeObjectRelocationTypeDexCacheArray: - return kBinDexCacheArray; - case kNativeObjectRelocationTypeRuntimeMethod: - return kBinRuntimeMethod; - case kNativeObjectRelocationTypeIMTable: - return kBinImTable; - case kNativeObjectRelocationTypeIMTConflictTable: - return kBinIMTConflictTable; - } - UNREACHABLE(); -} - -size_t ImageWriter::GetOatIndex(mirror::Object* obj) const { - if (!IsMultiImage()) { - return GetDefaultOatIndex(); - } - auto it = oat_index_map_.find(obj); - DCHECK(it != oat_index_map_.end()) << obj; - return it->second; -} - -size_t ImageWriter::GetOatIndexForDexFile(const DexFile* dex_file) const { - if (!IsMultiImage()) { - return GetDefaultOatIndex(); - } - auto it = dex_file_oat_index_map_.find(dex_file); - DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation(); - return it->second; -} - -size_t ImageWriter::GetOatIndexForDexCache(ObjPtr<mirror::DexCache> dex_cache) const { - return (dex_cache == nullptr) - ? GetDefaultOatIndex() - : GetOatIndexForDexFile(dex_cache->GetDexFile()); -} - -void ImageWriter::UpdateOatFileLayout(size_t oat_index, - size_t oat_loaded_size, - size_t oat_data_offset, - size_t oat_data_size) { - const uint8_t* images_end = image_infos_.back().image_begin_ + image_infos_.back().image_size_; - for (const ImageInfo& info : image_infos_) { - DCHECK_LE(info.image_begin_ + info.image_size_, images_end); - } - DCHECK(images_end != nullptr); // Image space must be ready. - - ImageInfo& cur_image_info = GetImageInfo(oat_index); - cur_image_info.oat_file_begin_ = images_end + cur_image_info.oat_offset_; - cur_image_info.oat_loaded_size_ = oat_loaded_size; - cur_image_info.oat_data_begin_ = cur_image_info.oat_file_begin_ + oat_data_offset; - cur_image_info.oat_size_ = oat_data_size; - - if (compile_app_image_) { - CHECK_EQ(oat_filenames_.size(), 1u) << "App image should have no next image."; - return; - } - - // Update the oat_offset of the next image info. - if (oat_index + 1u != oat_filenames_.size()) { - // There is a following one. - ImageInfo& next_image_info = GetImageInfo(oat_index + 1u); - next_image_info.oat_offset_ = cur_image_info.oat_offset_ + oat_loaded_size; - } -} - -void ImageWriter::UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_header) { - ImageInfo& cur_image_info = GetImageInfo(oat_index); - cur_image_info.oat_checksum_ = oat_header.GetChecksum(); - - if (oat_index == GetDefaultOatIndex()) { - // Primary oat file, read the trampolines. - cur_image_info.oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] = - oat_header.GetInterpreterToInterpreterBridgeOffset(); - cur_image_info.oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] = - oat_header.GetInterpreterToCompiledCodeBridgeOffset(); - cur_image_info.oat_address_offsets_[kOatAddressJNIDlsymLookup] = - oat_header.GetJniDlsymLookupOffset(); - cur_image_info.oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] = - oat_header.GetQuickGenericJniTrampolineOffset(); - cur_image_info.oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] = - oat_header.GetQuickImtConflictTrampolineOffset(); - cur_image_info.oat_address_offsets_[kOatAddressQuickResolutionTrampoline] = - oat_header.GetQuickResolutionTrampolineOffset(); - cur_image_info.oat_address_offsets_[kOatAddressQuickToInterpreterBridge] = - oat_header.GetQuickToInterpreterBridgeOffset(); - } -} - -ImageWriter::ImageWriter( - const CompilerDriver& compiler_driver, - uintptr_t image_begin, - bool compile_pic, - bool compile_app_image, - ImageHeader::StorageMode image_storage_mode, - const std::vector<const char*>& oat_filenames, - const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map, - const std::unordered_set<std::string>* dirty_image_objects) - : compiler_driver_(compiler_driver), - global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)), - image_objects_offset_begin_(0), - compile_pic_(compile_pic), - compile_app_image_(compile_app_image), - target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())), - image_infos_(oat_filenames.size()), - dirty_methods_(0u), - clean_methods_(0u), - image_storage_mode_(image_storage_mode), - oat_filenames_(oat_filenames), - dex_file_oat_index_map_(dex_file_oat_index_map), - dirty_image_objects_(dirty_image_objects) { - CHECK_NE(image_begin, 0U); - std::fill_n(image_methods_, arraysize(image_methods_), nullptr); - CHECK_EQ(compile_app_image, !Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()) - << "Compiling a boot image should occur iff there are no boot image spaces loaded"; -} - -ImageWriter::ImageInfo::ImageInfo() - : intern_table_(new InternTable), - class_table_(new ClassTable) {} - -void ImageWriter::CopyReference(mirror::HeapReference<mirror::Object>* dest, - ObjPtr<mirror::Object> src) { - dest->Assign(GetImageAddress(src.Ptr())); -} - -void ImageWriter::CopyReference(mirror::CompressedReference<mirror::Object>* dest, - ObjPtr<mirror::Object> src) { - dest->Assign(GetImageAddress(src.Ptr())); -} - -void ImageWriter::CopyAndFixupPointer(void** target, void* value) { - void* new_value = value; - if (value != nullptr && !IsInBootImage(value)) { - auto it = native_object_relocations_.find(value); - CHECK(it != native_object_relocations_.end()) << value; - const NativeObjectRelocation& relocation = it->second; - ImageInfo& image_info = GetImageInfo(relocation.oat_index); - new_value = reinterpret_cast<void*>(image_info.image_begin_ + relocation.offset); - } - if (target_ptr_size_ == PointerSize::k32) { - *reinterpret_cast<uint32_t*>(target) = PointerToLowMemUInt32(new_value); - } else { - *reinterpret_cast<uint64_t*>(target) = reinterpret_cast<uintptr_t>(new_value); - } -} - - -} // namespace art diff --git a/compiler/image_writer.h b/compiler/image_writer.h deleted file mode 100644 index 2fc394e862..0000000000 --- a/compiler/image_writer.h +++ /dev/null @@ -1,625 +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_IMAGE_WRITER_H_ -#define ART_COMPILER_IMAGE_WRITER_H_ - -#include <stdint.h> -#include "base/memory_tool.h" - -#include <cstddef> -#include <memory> -#include <ostream> -#include <set> -#include <stack> -#include <string> - -#include "art_method.h" -#include "base/bit_utils.h" -#include "base/dchecked_vector.h" -#include "base/enums.h" -#include "base/length_prefixed_array.h" -#include "base/macros.h" -#include "class_table.h" -#include "driver/compiler_driver.h" -#include "image.h" -#include "intern_table.h" -#include "lock_word.h" -#include "mem_map.h" -#include "mirror/dex_cache.h" -#include "oat_file.h" -#include "obj_ptr.h" -#include "os.h" -#include "safe_map.h" -#include "utils.h" - -namespace art { -namespace gc { -namespace accounting { -template <size_t kAlignment> class SpaceBitmap; -typedef SpaceBitmap<kObjectAlignment> ContinuousSpaceBitmap; -} // namespace accounting -namespace space { -class ImageSpace; -} // namespace space -} // namespace gc - -namespace mirror { -class ClassLoader; -} // namespace mirror - -class ClassLoaderVisitor; -class ImtConflictTable; - -static constexpr int kInvalidFd = -1; - -// Write a Space built during compilation for use during execution. -class ImageWriter FINAL { - public: - ImageWriter(const CompilerDriver& compiler_driver, - uintptr_t image_begin, - bool compile_pic, - bool compile_app_image, - ImageHeader::StorageMode image_storage_mode, - const std::vector<const char*>& oat_filenames, - const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map, - const std::unordered_set<std::string>* dirty_image_objects); - - bool PrepareImageAddressSpace(); - - bool IsImageAddressSpaceReady() const { - DCHECK(!image_infos_.empty()); - for (const ImageInfo& image_info : image_infos_) { - if (image_info.image_roots_address_ == 0u) { - return false; - } - } - return true; - } - - ObjPtr<mirror::ClassLoader> GetClassLoader() { - CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u); - return compile_app_image_ ? *class_loaders_.begin() : nullptr; - } - - template <typename T> - T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) { - if (object == nullptr || IsInBootImage(object)) { - return object; - } else { - size_t oat_index = GetOatIndex(object); - const ImageInfo& image_info = GetImageInfo(oat_index); - return reinterpret_cast<T*>(image_info.image_begin_ + GetImageOffset(object)); - } - } - - ArtMethod* GetImageMethodAddress(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); - - size_t GetOatFileOffset(size_t oat_index) const { - return GetImageInfo(oat_index).oat_offset_; - } - - const uint8_t* GetOatFileBegin(size_t oat_index) const { - return GetImageInfo(oat_index).oat_file_begin_; - } - - // If image_fd is not kInvalidFd, then we use that for the image file. Otherwise we open - // the names in image_filenames. - // If oat_fd is not kInvalidFd, then we use that for the oat file. Otherwise we open - // the names in oat_filenames. - bool Write(int image_fd, - const std::vector<const char*>& image_filenames, - const std::vector<const char*>& oat_filenames) - REQUIRES(!Locks::mutator_lock_); - - uintptr_t GetOatDataBegin(size_t oat_index) { - return reinterpret_cast<uintptr_t>(GetImageInfo(oat_index).oat_data_begin_); - } - - // Get the index of the oat file containing the dex file. - // - // This "oat_index" is used to retrieve information about the the memory layout - // of the oat file and its associated image file, needed for link-time patching - // of references to the image or across oat files. - size_t GetOatIndexForDexFile(const DexFile* dex_file) const; - - // Get the index of the oat file containing the dex file served by the dex cache. - size_t GetOatIndexForDexCache(ObjPtr<mirror::DexCache> dex_cache) const - REQUIRES_SHARED(Locks::mutator_lock_); - - // Update the oat layout for the given oat file. - // This will make the oat_offset for the next oat file valid. - void UpdateOatFileLayout(size_t oat_index, - size_t oat_loaded_size, - size_t oat_data_offset, - size_t oat_data_size); - // Update information about the oat header, i.e. checksum and trampoline offsets. - void UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_header); - - private: - using WorkStack = std::stack<std::pair<mirror::Object*, size_t>>; - - bool AllocMemory(); - - // Mark the objects defined in this space in the given live bitmap. - void RecordImageAllocations() REQUIRES_SHARED(Locks::mutator_lock_); - - // Classify different kinds of bins that objects end up getting packed into during image writing. - // Ordered from dirtiest to cleanest (until ArtMethods). - enum Bin { - kBinKnownDirty, // Known dirty objects from --dirty-image-objects list - kBinMiscDirty, // Dex caches, object locks, etc... - kBinClassVerified, // Class verified, but initializers haven't been run - // Unknown mix of clean/dirty: - kBinRegular, - kBinClassInitialized, // Class initializers have been run - // All classes get their own bins since their fields often dirty - kBinClassInitializedFinalStatics, // Class initializers have been run, no non-final statics - // Likely-clean: - kBinString, // [String] Almost always immutable (except for obj header). - // Add more bins here if we add more segregation code. - // Non mirror fields must be below. - // ArtFields should be always clean. - kBinArtField, - // If the class is initialized, then the ArtMethods are probably clean. - kBinArtMethodClean, - // ArtMethods may be dirty if the class has native methods or a declaring class that isn't - // initialized. - kBinArtMethodDirty, - // IMT (clean) - kBinImTable, - // Conflict tables (clean). - kBinIMTConflictTable, - // Runtime methods (always clean, do not have a length prefix array). - kBinRuntimeMethod, - // Dex cache arrays have a special slot for PC-relative addressing. Since they are - // huge, and as such their dirtiness is not important for the clean/dirty separation, - // we arbitrarily keep them at the end of the native data. - kBinDexCacheArray, // Arrays belonging to dex cache. - kBinSize, - // Number of bins which are for mirror objects. - kBinMirrorCount = kBinArtField, - }; - friend std::ostream& operator<<(std::ostream& stream, const Bin& bin); - - enum NativeObjectRelocationType { - kNativeObjectRelocationTypeArtField, - kNativeObjectRelocationTypeArtFieldArray, - kNativeObjectRelocationTypeArtMethodClean, - kNativeObjectRelocationTypeArtMethodArrayClean, - kNativeObjectRelocationTypeArtMethodDirty, - kNativeObjectRelocationTypeArtMethodArrayDirty, - kNativeObjectRelocationTypeRuntimeMethod, - kNativeObjectRelocationTypeIMTable, - kNativeObjectRelocationTypeIMTConflictTable, - kNativeObjectRelocationTypeDexCacheArray, - }; - friend std::ostream& operator<<(std::ostream& stream, const NativeObjectRelocationType& type); - - enum OatAddress { - kOatAddressInterpreterToInterpreterBridge, - kOatAddressInterpreterToCompiledCodeBridge, - kOatAddressJNIDlsymLookup, - kOatAddressQuickGenericJNITrampoline, - kOatAddressQuickIMTConflictTrampoline, - kOatAddressQuickResolutionTrampoline, - kOatAddressQuickToInterpreterBridge, - // Number of elements in the enum. - kOatAddressCount, - }; - friend std::ostream& operator<<(std::ostream& stream, const OatAddress& oat_address); - - static constexpr size_t kBinBits = MinimumBitsToStore<uint32_t>(kBinMirrorCount - 1); - // uint32 = typeof(lockword_) - // Subtract read barrier bits since we want these to remain 0, or else it may result in DCHECK - // failures due to invalid read barrier bits during object field reads. - static const size_t kBinShift = BitSizeOf<uint32_t>() - kBinBits - LockWord::kGCStateSize; - // 111000.....0 - static const size_t kBinMask = ((static_cast<size_t>(1) << kBinBits) - 1) << kBinShift; - - // We use the lock word to store the bin # and bin index of the object in the image. - // - // The struct size must be exactly sizeof(LockWord), currently 32-bits, since this will end up - // stored in the lock word bit-for-bit when object forwarding addresses are being calculated. - struct BinSlot { - explicit BinSlot(uint32_t lockword); - BinSlot(Bin bin, uint32_t index); - - // The bin an object belongs to, i.e. regular, class/verified, class/initialized, etc. - Bin GetBin() const; - // The offset in bytes from the beginning of the bin. Aligned to object size. - uint32_t GetIndex() const; - // Pack into a single uint32_t, for storing into a lock word. - uint32_t Uint32Value() const { return lockword_; } - // Comparison operator for map support - bool operator<(const BinSlot& other) const { return lockword_ < other.lockword_; } - - private: - // Must be the same size as LockWord, any larger and we would truncate the data. - const uint32_t lockword_; - }; - - struct ImageInfo { - ImageInfo(); - ImageInfo(ImageInfo&&) = default; - - // Create the image sections into the out sections variable, returns the size of the image - // excluding the bitmap. - size_t CreateImageSections(ImageSection* out_sections, bool app_image) const; - - std::unique_ptr<MemMap> image_; // Memory mapped for generating the image. - - // Target begin of this image. Notes: It is not valid to write here, this is the address - // of the target image, not necessarily where image_ is mapped. The address is only valid - // after layouting (otherwise null). - uint8_t* image_begin_ = nullptr; - - // Offset to the free space in image_, initially size of image header. - size_t image_end_ = RoundUp(sizeof(ImageHeader), kObjectAlignment); - uint32_t image_roots_address_ = 0; // The image roots address in the image. - size_t image_offset_ = 0; // Offset of this image from the start of the first image. - - // Image size is the *address space* covered by this image. As the live bitmap is aligned - // to the page size, the live bitmap will cover more address space than necessary. But live - // bitmaps may not overlap, so an image has a "shadow," which is accounted for in the size. - // The next image may only start at image_begin_ + image_size_ (which is guaranteed to be - // page-aligned). - size_t image_size_ = 0; - - // Oat data. - // Offset of the oat file for this image from start of oat files. This is - // valid when the previous oat file has been written. - size_t oat_offset_ = 0; - // Layout of the loaded ELF file containing the oat file, valid after UpdateOatFileLayout(). - const uint8_t* oat_file_begin_ = nullptr; - size_t oat_loaded_size_ = 0; - const uint8_t* oat_data_begin_ = nullptr; - size_t oat_size_ = 0; // Size of the corresponding oat data. - // The oat header checksum, valid after UpdateOatFileHeader(). - uint32_t oat_checksum_ = 0u; - - // Image bitmap which lets us know where the objects inside of the image reside. - std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> image_bitmap_; - - // The start offsets of the dex cache arrays. - SafeMap<const DexFile*, size_t> dex_cache_array_starts_; - - // Offset from oat_data_begin_ to the stubs. - uint32_t oat_address_offsets_[kOatAddressCount] = {}; - - // Bin slot tracking for dirty object packing. - size_t bin_slot_sizes_[kBinSize] = {}; // Number of bytes in a bin. - size_t bin_slot_offsets_[kBinSize] = {}; // Number of bytes in previous bins. - size_t bin_slot_count_[kBinSize] = {}; // Number of objects in a bin. - - // Cached size of the intern table for when we allocate memory. - size_t intern_table_bytes_ = 0; - - // Number of image class table bytes. - size_t class_table_bytes_ = 0; - - // Number of object fixup bytes. - size_t object_fixup_bytes_ = 0; - - // Number of pointer fixup bytes. - size_t pointer_fixup_bytes_ = 0; - - // Intern table associated with this image for serialization. - std::unique_ptr<InternTable> intern_table_; - - // Class table associated with this image for serialization. - std::unique_ptr<ClassTable> class_table_; - }; - - // We use the lock word to store the offset of the object in the image. - void AssignImageOffset(mirror::Object* object, BinSlot bin_slot) - REQUIRES_SHARED(Locks::mutator_lock_); - void SetImageOffset(mirror::Object* object, size_t offset) - REQUIRES_SHARED(Locks::mutator_lock_); - bool IsImageOffsetAssigned(mirror::Object* object) const - REQUIRES_SHARED(Locks::mutator_lock_); - size_t GetImageOffset(mirror::Object* object) const REQUIRES_SHARED(Locks::mutator_lock_); - void UpdateImageOffset(mirror::Object* obj, uintptr_t offset) - REQUIRES_SHARED(Locks::mutator_lock_); - - void PrepareDexCacheArraySlots() REQUIRES_SHARED(Locks::mutator_lock_); - void AssignImageBinSlot(mirror::Object* object, size_t oat_index) - REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Object* TryAssignBinSlot(WorkStack& work_stack, mirror::Object* obj, size_t oat_index) - REQUIRES_SHARED(Locks::mutator_lock_); - void SetImageBinSlot(mirror::Object* object, BinSlot bin_slot) - REQUIRES_SHARED(Locks::mutator_lock_); - bool IsImageBinSlotAssigned(mirror::Object* object) const - REQUIRES_SHARED(Locks::mutator_lock_); - BinSlot GetImageBinSlot(mirror::Object* object) const REQUIRES_SHARED(Locks::mutator_lock_); - - void AddDexCacheArrayRelocation(void* array, size_t offset, ObjPtr<mirror::DexCache> dex_cache) - REQUIRES_SHARED(Locks::mutator_lock_); - void AddMethodPointerArray(mirror::PointerArray* arr) REQUIRES_SHARED(Locks::mutator_lock_); - - static void* GetImageAddressCallback(void* writer, mirror::Object* obj) - REQUIRES_SHARED(Locks::mutator_lock_) { - return reinterpret_cast<ImageWriter*>(writer)->GetImageAddress(obj); - } - - mirror::Object* GetLocalAddress(mirror::Object* object) const - REQUIRES_SHARED(Locks::mutator_lock_) { - size_t offset = GetImageOffset(object); - size_t oat_index = GetOatIndex(object); - const ImageInfo& image_info = GetImageInfo(oat_index); - uint8_t* dst = image_info.image_->Begin() + offset; - return reinterpret_cast<mirror::Object*>(dst); - } - - // Returns the address in the boot image if we are compiling the app image. - const uint8_t* GetOatAddress(OatAddress type) const; - - const uint8_t* GetOatAddressForOffset(uint32_t offset, const ImageInfo& image_info) const { - // With Quick, code is within the OatFile, as there are all in one - // .o ELF object. But interpret it as signed. - DCHECK_LE(static_cast<int32_t>(offset), static_cast<int32_t>(image_info.oat_size_)); - DCHECK(image_info.oat_data_begin_ != nullptr); - return offset == 0u ? nullptr : image_info.oat_data_begin_ + static_cast<int32_t>(offset); - } - - // Returns true if the class was in the original requested image classes list. - bool KeepClass(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); - - // Debug aid that list of requested image classes. - void DumpImageClasses(); - - // Preinitializes some otherwise lazy fields (such as Class name) to avoid runtime image dirtying. - void ComputeLazyFieldsForImageClasses() - REQUIRES_SHARED(Locks::mutator_lock_); - - // Visit all class loaders. - void VisitClassLoaders(ClassLoaderVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); - - // Remove unwanted classes from various roots. - void PruneNonImageClasses() REQUIRES_SHARED(Locks::mutator_lock_); - - // Remove unwanted classes from the DexCache roots and preload deterministic DexCache contents. - void PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, - ObjPtr<mirror::ClassLoader> class_loader) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Locks::classlinker_classes_lock_); - - // Verify unwanted classes removed. - void CheckNonImageClassesRemoved() REQUIRES_SHARED(Locks::mutator_lock_); - - // Lays out where the image objects will be at runtime. - void CalculateNewObjectOffsets() - REQUIRES_SHARED(Locks::mutator_lock_); - void ProcessWorkStack(WorkStack* work_stack) - REQUIRES_SHARED(Locks::mutator_lock_); - void CreateHeader(size_t oat_index) - REQUIRES_SHARED(Locks::mutator_lock_); - mirror::ObjectArray<mirror::Object>* CreateImageRoots(size_t oat_index) const - REQUIRES_SHARED(Locks::mutator_lock_); - void CalculateObjectBinSlots(mirror::Object* obj) - REQUIRES_SHARED(Locks::mutator_lock_); - void UnbinObjectsIntoOffset(mirror::Object* obj) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Creates the contiguous image in memory and adjusts pointers. - void CopyAndFixupNativeData(size_t oat_index) REQUIRES_SHARED(Locks::mutator_lock_); - void CopyAndFixupObjects() REQUIRES_SHARED(Locks::mutator_lock_); - void CopyAndFixupObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_); - void CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy, const ImageInfo& image_info) - REQUIRES_SHARED(Locks::mutator_lock_); - void CopyAndFixupImTable(ImTable* orig, ImTable* copy) REQUIRES_SHARED(Locks::mutator_lock_); - void CopyAndFixupImtConflictTable(ImtConflictTable* orig, ImtConflictTable* copy) - REQUIRES_SHARED(Locks::mutator_lock_); - void FixupClass(mirror::Class* orig, mirror::Class* copy) - REQUIRES_SHARED(Locks::mutator_lock_); - void FixupObject(mirror::Object* orig, mirror::Object* copy) - REQUIRES_SHARED(Locks::mutator_lock_); - void FixupDexCache(mirror::DexCache* orig_dex_cache, mirror::DexCache* copy_dex_cache) - REQUIRES_SHARED(Locks::mutator_lock_); - void FixupPointerArray(mirror::Object* dst, - mirror::PointerArray* arr, - mirror::Class* klass, - Bin array_type) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Get quick code for non-resolution/imt_conflict/abstract method. - const uint8_t* GetQuickCode(ArtMethod* method, - const ImageInfo& image_info, - bool* quick_is_interpreted) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins. - size_t GetBinSizeSum(ImageInfo& image_info, Bin up_to = kBinSize) const; - - // Return true if a method is likely to be dirtied at runtime. - bool WillMethodBeDirty(ArtMethod* m) const REQUIRES_SHARED(Locks::mutator_lock_); - - // Assign the offset for an ArtMethod. - void AssignMethodOffset(ArtMethod* method, - NativeObjectRelocationType type, - size_t oat_index) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Return true if imt was newly inserted. - bool TryAssignImTableOffset(ImTable* imt, size_t oat_index) REQUIRES_SHARED(Locks::mutator_lock_); - - // Assign the offset for an IMT conflict table. Does nothing if the table already has a native - // relocation. - void TryAssignConflictTableOffset(ImtConflictTable* table, size_t oat_index) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Return true if klass is loaded by the boot class loader but not in the boot image. - bool IsBootClassLoaderNonImageClass(mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); - - // Return true if klass depends on a boot class loader non image class. We want to prune these - // classes since we do not want any boot class loader classes in the image. This means that - // we also cannot have any classes which refer to these boot class loader non image classes. - // PruneAppImageClass also prunes if klass depends on a non-image class according to the compiler - // driver. - bool PruneAppImageClass(ObjPtr<mirror::Class> klass) - REQUIRES_SHARED(Locks::mutator_lock_); - - // early_exit is true if we had a cyclic dependency anywhere down the chain. - bool PruneAppImageClassInternal(ObjPtr<mirror::Class> klass, - bool* early_exit, - std::unordered_set<mirror::Object*>* visited) - REQUIRES_SHARED(Locks::mutator_lock_); - - bool IsMultiImage() const { - return image_infos_.size() > 1; - } - - static Bin BinTypeForNativeRelocationType(NativeObjectRelocationType type); - - uintptr_t NativeOffsetInImage(void* obj) REQUIRES_SHARED(Locks::mutator_lock_); - - // Location of where the object will be when the image is loaded at runtime. - template <typename T> - T* NativeLocationInImage(T* obj) REQUIRES_SHARED(Locks::mutator_lock_); - - // Location of where the temporary copy of the object currently is. - template <typename T> - T* NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) REQUIRES_SHARED(Locks::mutator_lock_); - - // Return true of obj is inside of the boot image space. This may only return true if we are - // compiling an app image. - bool IsInBootImage(const void* obj) const; - - // Return true if ptr is within the boot oat file. - bool IsInBootOatFile(const void* ptr) const; - - // Get the index of the oat file associated with the object. - size_t GetOatIndex(mirror::Object* object) const REQUIRES_SHARED(Locks::mutator_lock_); - - // The oat index for shared data in multi-image and all data in single-image compilation. - size_t GetDefaultOatIndex() const { - return 0u; - } - - ImageInfo& GetImageInfo(size_t oat_index) { - return image_infos_[oat_index]; - } - - const ImageInfo& GetImageInfo(size_t oat_index) const { - return image_infos_[oat_index]; - } - - // Find an already strong interned string in the other images or in the boot image. Used to - // remove duplicates in the multi image and app image case. - mirror::String* FindInternedString(mirror::String* string) REQUIRES_SHARED(Locks::mutator_lock_); - - // Return true if there already exists a native allocation for an object. - bool NativeRelocationAssigned(void* ptr) const; - - void CopyReference(mirror::HeapReference<mirror::Object>* dest, ObjPtr<mirror::Object> src) - REQUIRES_SHARED(Locks::mutator_lock_); - - void CopyReference(mirror::CompressedReference<mirror::Object>* dest, ObjPtr<mirror::Object> src) - REQUIRES_SHARED(Locks::mutator_lock_); - - void CopyAndFixupPointer(void** target, void* value); - - const CompilerDriver& compiler_driver_; - - // Beginning target image address for the first image. - uint8_t* global_image_begin_; - - // Offset from image_begin_ to where the first object is in image_. - size_t image_objects_offset_begin_; - - // Pointer arrays that need to be updated. Since these are only some int and long arrays, we need - // to keep track. These include vtable arrays, iftable arrays, and dex caches. - std::unordered_map<mirror::PointerArray*, Bin> pointer_arrays_; - - // Saved hash codes. We use these to restore lockwords which were temporarily used to have - // forwarding addresses as well as copying over hash codes. - std::unordered_map<mirror::Object*, uint32_t> saved_hashcode_map_; - - // Oat index map for objects. - std::unordered_map<mirror::Object*, uint32_t> oat_index_map_; - - // Boolean flags. - const bool compile_pic_; - const bool compile_app_image_; - - // Size of pointers on the target architecture. - PointerSize target_ptr_size_; - - // Image data indexed by the oat file index. - dchecked_vector<ImageInfo> image_infos_; - - // ArtField, ArtMethod relocating map. These are allocated as array of structs but we want to - // have one entry per art field for convenience. ArtFields are placed right after the end of the - // image objects (aka sum of bin_slot_sizes_). ArtMethods are placed right after the ArtFields. - struct NativeObjectRelocation { - size_t oat_index; - uintptr_t offset; - NativeObjectRelocationType type; - - bool IsArtMethodRelocation() const { - return type == kNativeObjectRelocationTypeArtMethodClean || - type == kNativeObjectRelocationTypeArtMethodDirty || - type == kNativeObjectRelocationTypeRuntimeMethod; - } - }; - std::unordered_map<void*, NativeObjectRelocation> native_object_relocations_; - - // Runtime ArtMethods which aren't reachable from any Class but need to be copied into the image. - ArtMethod* image_methods_[ImageHeader::kImageMethodsCount]; - - // Counters for measurements, used for logging only. - uint64_t dirty_methods_; - uint64_t clean_methods_; - - // Prune class memoization table to speed up ContainsBootClassLoaderNonImageClass. - std::unordered_map<mirror::Class*, bool> prune_class_memo_; - - // Class loaders with a class table to write out. There should only be one class loader because - // dex2oat loads the dex files to be compiled into a single class loader. For the boot image, - // null is a valid entry. - std::unordered_set<mirror::ClassLoader*> class_loaders_; - - // Which mode the image is stored as, see image.h - const ImageHeader::StorageMode image_storage_mode_; - - // The file names of oat files. - const std::vector<const char*>& oat_filenames_; - - // Map of dex files to the indexes of oat files that they were compiled into. - const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map_; - - // Set of objects known to be dirty in the image. Can be nullptr if there are none. - const std::unordered_set<std::string>* dirty_image_objects_; - - class ComputeLazyFieldsForClassesVisitor; - class FixupClassVisitor; - class FixupRootVisitor; - class FixupVisitor; - class GetRootsVisitor; - class ImageAddressVisitorForDexCacheArray; - class NativeLocationVisitor; - class PruneClassesVisitor; - class PruneClassLoaderClassesVisitor; - class RegisterBootClassPathClassesVisitor; - class VisitReferencesVisitor; - class PruneObjectReferenceVisitor; - - DISALLOW_COPY_AND_ASSIGN(ImageWriter); -}; - -} // namespace art - -#endif // ART_COMPILER_IMAGE_WRITER_H_ diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 5fdf9ff07c..511a44af04 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -175,7 +175,8 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) { DCHECK(!method->IsProxyMethod()); DCHECK(method->GetDeclaringClass()->IsResolved()); - TimingLogger logger("JIT compiler timing logger", true, VLOG_IS_ON(jit)); + TimingLogger logger( + "JIT compiler timing logger", true, VLOG_IS_ON(jit), TimingLogger::TimingKind::kThreadCpu); self->AssertNoPendingException(); Runtime* runtime = Runtime::Current(); diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index e7e4647866..c66a2a62eb 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -665,7 +665,7 @@ static CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, /* method_info */ ArrayRef<const uint8_t>(), /* vmap_table */ ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(*jni_asm->cfi().data()), - ArrayRef<const LinkerPatch>()); + ArrayRef<const linker::LinkerPatch>()); } // Copy a single parameter from the managed to the JNI calling convention. diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc index cb6522cbbb..2cb23d1710 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.cc +++ b/compiler/linker/arm/relative_patcher_arm_base.cc @@ -17,9 +17,10 @@ #include "linker/arm/relative_patcher_arm_base.h" #include "base/stl_util.h" -#include "compiled_method.h" +#include "compiled_method-inl.h" #include "debug/method_debug_info.h" #include "dex_file_types.h" +#include "linker/linker_patch.h" #include "linker/output_stream.h" #include "oat.h" #include "oat_quick_method_header.h" diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc index 704feeb387..f84fea378d 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.cc +++ b/compiler/linker/arm/relative_patcher_thumb2.cc @@ -21,6 +21,7 @@ #include "base/bit_utils.h" #include "compiled_method.h" #include "entrypoints/quick/quick_entrypoints_enum.h" +#include "linker/linker_patch.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/object.h" diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 270ba3c08d..828c99ba86 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -20,9 +20,11 @@ #include "arch/arm64/instruction_set_features_arm64.h" #include "art_method.h" #include "base/bit_utils.h" -#include "compiled_method.h" +#include "compiled_method-inl.h" #include "driver/compiler_driver.h" #include "entrypoints/quick/quick_entrypoints_enum.h" +#include "heap_poisoning.h" +#include "linker/linker_patch.h" #include "linker/output_stream.h" #include "lock_word.h" #include "mirror/array-inl.h" diff --git a/compiler/linker/buffered_output_stream.cc b/compiler/linker/buffered_output_stream.cc index 4c66c764a9..07066b76ac 100644 --- a/compiler/linker/buffered_output_stream.cc +++ b/compiler/linker/buffered_output_stream.cc @@ -19,6 +19,7 @@ #include <string.h> namespace art { +namespace linker { BufferedOutputStream::BufferedOutputStream(std::unique_ptr<OutputStream> out) : OutputStream(out->GetLocation()), // Before out is moved to out_. @@ -67,4 +68,5 @@ off_t BufferedOutputStream::Seek(off_t offset, Whence whence) { return out_->Seek(offset, whence); } +} // namespace linker } // namespace art diff --git a/compiler/linker/buffered_output_stream.h b/compiler/linker/buffered_output_stream.h index a2eefbbf17..66994e82a1 100644 --- a/compiler/linker/buffered_output_stream.h +++ b/compiler/linker/buffered_output_stream.h @@ -24,6 +24,7 @@ #include "globals.h" namespace art { +namespace linker { class BufferedOutputStream FINAL : public OutputStream { public: @@ -49,6 +50,7 @@ class BufferedOutputStream FINAL : public OutputStream { DISALLOW_COPY_AND_ASSIGN(BufferedOutputStream); }; +} // namespace linker } // namespace art #endif // ART_COMPILER_LINKER_BUFFERED_OUTPUT_STREAM_H_ diff --git a/compiler/elf_builder.h b/compiler/linker/elf_builder.h index 2ef9fa1ccb..79412370bc 100644 --- a/compiler/elf_builder.h +++ b/compiler/linker/elf_builder.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_ELF_BUILDER_H_ -#define ART_COMPILER_ELF_BUILDER_H_ +#ifndef ART_COMPILER_LINKER_ELF_BUILDER_H_ +#define ART_COMPILER_LINKER_ELF_BUILDER_H_ #include <vector> @@ -30,6 +30,7 @@ #include "linker/error_delaying_output_stream.h" namespace art { +namespace linker { // Writes ELF file. // @@ -1021,6 +1022,7 @@ class ElfBuilder FINAL { DISALLOW_COPY_AND_ASSIGN(ElfBuilder); }; +} // namespace linker } // namespace art -#endif // ART_COMPILER_ELF_BUILDER_H_ +#endif // ART_COMPILER_LINKER_ELF_BUILDER_H_ diff --git a/compiler/linker/error_delaying_output_stream.h b/compiler/linker/error_delaying_output_stream.h index 99410e4bb1..33e6b5ab23 100644 --- a/compiler/linker/error_delaying_output_stream.h +++ b/compiler/linker/error_delaying_output_stream.h @@ -22,6 +22,7 @@ #include "base/logging.h" namespace art { +namespace linker { // OutputStream wrapper that delays reporting an error until Flush(). class ErrorDelayingOutputStream FINAL : public OutputStream { @@ -96,6 +97,7 @@ class ErrorDelayingOutputStream FINAL : public OutputStream { off_t output_offset_; // Keep track of the current position in the stream. }; +} // namespace linker } // namespace art #endif // ART_COMPILER_LINKER_ERROR_DELAYING_OUTPUT_STREAM_H_ diff --git a/compiler/linker/file_output_stream.cc b/compiler/linker/file_output_stream.cc index bbfbdfdca8..477846ec65 100644 --- a/compiler/linker/file_output_stream.cc +++ b/compiler/linker/file_output_stream.cc @@ -22,6 +22,7 @@ #include "base/unix_file/fd_file.h" namespace art { +namespace linker { FileOutputStream::FileOutputStream(File* file) : OutputStream(file->GetPath()), file_(file) {} @@ -37,4 +38,5 @@ bool FileOutputStream::Flush() { return file_->Flush() == 0; } +} // namespace linker } // namespace art diff --git a/compiler/linker/file_output_stream.h b/compiler/linker/file_output_stream.h index f2d845379f..28296a47fd 100644 --- a/compiler/linker/file_output_stream.h +++ b/compiler/linker/file_output_stream.h @@ -22,6 +22,7 @@ #include "os.h" namespace art { +namespace linker { class FileOutputStream FINAL : public OutputStream { public: @@ -41,6 +42,7 @@ class FileOutputStream FINAL : public OutputStream { DISALLOW_COPY_AND_ASSIGN(FileOutputStream); }; +} // namespace linker } // namespace art #endif // ART_COMPILER_LINKER_FILE_OUTPUT_STREAM_H_ diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h new file mode 100644 index 0000000000..0ac149029a --- /dev/null +++ b/compiler/linker/linker_patch.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2017 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_LINKER_LINKER_PATCH_H_ +#define ART_COMPILER_LINKER_LINKER_PATCH_H_ + +#include <iosfwd> +#include <stdint.h> + +#include "base/bit_utils.h" +#include "base/logging.h" +#include "method_reference.h" + +namespace art { + +class DexFile; + +namespace linker { + +class LinkerPatch { + public: + // Note: We explicitly specify the underlying type of the enum because GCC + // would otherwise select a bigger underlying type and then complain that + // 'art::LinkerPatch::patch_type_' is too small to hold all + // values of 'enum class art::LinkerPatch::Type' + // which is ridiculous given we have only a handful of values here. If we + // choose to squeeze the Type into fewer than 8 bits, we'll have to declare + // patch_type_ as an uintN_t and do explicit static_cast<>s. + enum class Type : uint8_t { + kMethodRelative, // NOTE: Actual patching is instruction_set-dependent. + kMethodBssEntry, // NOTE: Actual patching is instruction_set-dependent. + kCall, + kCallRelative, // NOTE: Actual patching is instruction_set-dependent. + kTypeRelative, // NOTE: Actual patching is instruction_set-dependent. + kTypeClassTable, // NOTE: Actual patching is instruction_set-dependent. + kTypeBssEntry, // NOTE: Actual patching is instruction_set-dependent. + kStringRelative, // NOTE: Actual patching is instruction_set-dependent. + kStringInternTable, // NOTE: Actual patching is instruction_set-dependent. + kStringBssEntry, // NOTE: Actual patching is instruction_set-dependent. + kBakerReadBarrierBranch, // NOTE: Actual patching is instruction_set-dependent. + }; + + static LinkerPatch RelativeMethodPatch(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t target_method_idx) { + LinkerPatch patch(literal_offset, Type::kMethodRelative, target_dex_file); + patch.method_idx_ = target_method_idx; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + + static LinkerPatch MethodBssEntryPatch(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t target_method_idx) { + LinkerPatch patch(literal_offset, Type::kMethodBssEntry, target_dex_file); + patch.method_idx_ = target_method_idx; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + + static LinkerPatch CodePatch(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t target_method_idx) { + LinkerPatch patch(literal_offset, Type::kCall, target_dex_file); + patch.method_idx_ = target_method_idx; + return patch; + } + + static LinkerPatch RelativeCodePatch(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t target_method_idx) { + LinkerPatch patch(literal_offset, Type::kCallRelative, target_dex_file); + patch.method_idx_ = target_method_idx; + return patch; + } + + static LinkerPatch RelativeTypePatch(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t target_type_idx) { + LinkerPatch patch(literal_offset, Type::kTypeRelative, target_dex_file); + patch.type_idx_ = target_type_idx; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + + static LinkerPatch TypeClassTablePatch(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t target_type_idx) { + LinkerPatch patch(literal_offset, Type::kTypeClassTable, target_dex_file); + patch.type_idx_ = target_type_idx; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + + static LinkerPatch TypeBssEntryPatch(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t target_type_idx) { + LinkerPatch patch(literal_offset, Type::kTypeBssEntry, target_dex_file); + patch.type_idx_ = target_type_idx; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + + static LinkerPatch RelativeStringPatch(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t target_string_idx) { + LinkerPatch patch(literal_offset, Type::kStringRelative, target_dex_file); + patch.string_idx_ = target_string_idx; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + + static LinkerPatch StringInternTablePatch(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t target_string_idx) { + LinkerPatch patch(literal_offset, Type::kStringInternTable, target_dex_file); + patch.string_idx_ = target_string_idx; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + + static LinkerPatch StringBssEntryPatch(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t target_string_idx) { + LinkerPatch patch(literal_offset, Type::kStringBssEntry, target_dex_file); + patch.string_idx_ = target_string_idx; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + + static LinkerPatch BakerReadBarrierBranchPatch(size_t literal_offset, + uint32_t custom_value1 = 0u, + uint32_t custom_value2 = 0u) { + LinkerPatch patch(literal_offset, Type::kBakerReadBarrierBranch, nullptr); + patch.baker_custom_value1_ = custom_value1; + patch.baker_custom_value2_ = custom_value2; + return patch; + } + + LinkerPatch(const LinkerPatch& other) = default; + LinkerPatch& operator=(const LinkerPatch& other) = default; + + size_t LiteralOffset() const { + return literal_offset_; + } + + Type GetType() const { + return patch_type_; + } + + bool IsPcRelative() const { + switch (GetType()) { + case Type::kMethodRelative: + case Type::kMethodBssEntry: + case Type::kCallRelative: + case Type::kTypeRelative: + case Type::kTypeClassTable: + case Type::kTypeBssEntry: + case Type::kStringRelative: + case Type::kStringInternTable: + case Type::kStringBssEntry: + case Type::kBakerReadBarrierBranch: + return true; + default: + return false; + } + } + + MethodReference TargetMethod() const { + DCHECK(patch_type_ == Type::kMethodRelative || + patch_type_ == Type::kMethodBssEntry || + patch_type_ == Type::kCall || + patch_type_ == Type::kCallRelative); + return MethodReference(target_dex_file_, method_idx_); + } + + const DexFile* TargetTypeDexFile() const { + DCHECK(patch_type_ == Type::kTypeRelative || + patch_type_ == Type::kTypeClassTable || + patch_type_ == Type::kTypeBssEntry); + return target_dex_file_; + } + + dex::TypeIndex TargetTypeIndex() const { + DCHECK(patch_type_ == Type::kTypeRelative || + patch_type_ == Type::kTypeClassTable || + patch_type_ == Type::kTypeBssEntry); + return dex::TypeIndex(type_idx_); + } + + const DexFile* TargetStringDexFile() const { + DCHECK(patch_type_ == Type::kStringRelative || + patch_type_ == Type::kStringInternTable || + patch_type_ == Type::kStringBssEntry); + return target_dex_file_; + } + + dex::StringIndex TargetStringIndex() const { + DCHECK(patch_type_ == Type::kStringRelative || + patch_type_ == Type::kStringInternTable || + patch_type_ == Type::kStringBssEntry); + return dex::StringIndex(string_idx_); + } + + uint32_t PcInsnOffset() const { + DCHECK(patch_type_ == Type::kMethodRelative || + patch_type_ == Type::kMethodBssEntry || + patch_type_ == Type::kTypeRelative || + patch_type_ == Type::kTypeClassTable || + patch_type_ == Type::kTypeBssEntry || + patch_type_ == Type::kStringRelative || + patch_type_ == Type::kStringInternTable || + patch_type_ == Type::kStringBssEntry); + return pc_insn_offset_; + } + + uint32_t GetBakerCustomValue1() const { + DCHECK(patch_type_ == Type::kBakerReadBarrierBranch); + return baker_custom_value1_; + } + + uint32_t GetBakerCustomValue2() const { + DCHECK(patch_type_ == Type::kBakerReadBarrierBranch); + return baker_custom_value2_; + } + + private: + LinkerPatch(size_t literal_offset, Type patch_type, const DexFile* target_dex_file) + : target_dex_file_(target_dex_file), + literal_offset_(literal_offset), + patch_type_(patch_type) { + cmp1_ = 0u; + cmp2_ = 0u; + // The compiler rejects methods that are too big, so the compiled code + // of a single method really shouln't be anywhere close to 16MiB. + DCHECK(IsUint<24>(literal_offset)); + } + + const DexFile* target_dex_file_; + // TODO: Clean up naming. Some patched locations are literals but others are not. + uint32_t literal_offset_ : 24; // Method code size up to 16MiB. + Type patch_type_ : 8; + union { + uint32_t cmp1_; // Used for relational operators. + uint32_t method_idx_; // Method index for Call/Method patches. + uint32_t type_idx_; // Type index for Type patches. + uint32_t string_idx_; // String index for String patches. + uint32_t baker_custom_value1_; + static_assert(sizeof(method_idx_) == sizeof(cmp1_), "needed by relational operators"); + static_assert(sizeof(type_idx_) == sizeof(cmp1_), "needed by relational operators"); + static_assert(sizeof(string_idx_) == sizeof(cmp1_), "needed by relational operators"); + static_assert(sizeof(baker_custom_value1_) == sizeof(cmp1_), "needed by relational operators"); + }; + union { + // Note: To avoid uninitialized padding on 64-bit systems, we use `size_t` for `cmp2_`. + // This allows a hashing function to treat an array of linker patches as raw memory. + size_t cmp2_; // Used for relational operators. + // Literal offset of the insn loading PC (same as literal_offset if it's the same insn, + // may be different if the PC-relative addressing needs multiple insns). + uint32_t pc_insn_offset_; + uint32_t baker_custom_value2_; + static_assert(sizeof(pc_insn_offset_) <= sizeof(cmp2_), "needed by relational operators"); + static_assert(sizeof(baker_custom_value2_) <= sizeof(cmp2_), "needed by relational operators"); + }; + + friend bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs); + friend bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs); +}; +std::ostream& operator<<(std::ostream& os, const LinkerPatch::Type& type); + +inline bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs) { + return lhs.literal_offset_ == rhs.literal_offset_ && + lhs.patch_type_ == rhs.patch_type_ && + lhs.target_dex_file_ == rhs.target_dex_file_ && + lhs.cmp1_ == rhs.cmp1_ && + lhs.cmp2_ == rhs.cmp2_; +} + +inline bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs) { + return (lhs.literal_offset_ != rhs.literal_offset_) ? lhs.literal_offset_ < rhs.literal_offset_ + : (lhs.patch_type_ != rhs.patch_type_) ? lhs.patch_type_ < rhs.patch_type_ + : (lhs.target_dex_file_ != rhs.target_dex_file_) ? lhs.target_dex_file_ < rhs.target_dex_file_ + : (lhs.cmp1_ != rhs.cmp1_) ? lhs.cmp1_ < rhs.cmp1_ + : lhs.cmp2_ < rhs.cmp2_; +} + +} // namespace linker +} // namespace art + +#endif // ART_COMPILER_LINKER_LINKER_PATCH_H_ diff --git a/compiler/compiled_method_test.cc b/compiler/linker/linker_patch_test.cc index f4a72cf2cc..e87dc8de6b 100644 --- a/compiler/compiled_method_test.cc +++ b/compiler/linker/linker_patch_test.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2017 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. @@ -16,37 +16,12 @@ #include <gtest/gtest.h> -#include "compiled_method.h" +#include "linker_patch.h" namespace art { +namespace linker { -TEST(CompiledMethod, SrcMapElemOperators) { - SrcMapElem elems[] = { - { 1u, -1 }, - { 1u, 0 }, - { 1u, 1 }, - { 2u, -1 }, - { 2u, 0 }, // Index 4. - { 2u, 1 }, - { 2u, 0u }, // Index 6: Arbitrarily add identical SrcMapElem with index 4. - }; - - for (size_t i = 0; i != arraysize(elems); ++i) { - for (size_t j = 0; j != arraysize(elems); ++j) { - bool expected = (i != 6u ? i : 4u) == (j != 6u ? j : 4u); - EXPECT_EQ(expected, elems[i] == elems[j]) << i << " " << j; - } - } - - for (size_t i = 0; i != arraysize(elems); ++i) { - for (size_t j = 0; j != arraysize(elems); ++j) { - bool expected = (i != 6u ? i : 4u) < (j != 6u ? j : 4u); - EXPECT_EQ(expected, elems[i] < elems[j]) << i << " " << j; - } - } -} - -TEST(CompiledMethod, LinkerPatchOperators) { +TEST(LinkerPatch, LinkerPatchOperators) { const DexFile* dex_file1 = reinterpret_cast<const DexFile*>(1); const DexFile* dex_file2 = reinterpret_cast<const DexFile*>(2); LinkerPatch patches[] = { @@ -191,4 +166,5 @@ TEST(CompiledMethod, LinkerPatchOperators) { } } +} // namespace linker } // namespace art diff --git a/compiler/linker/mips/relative_patcher_mips.cc b/compiler/linker/mips/relative_patcher_mips.cc index 408ac22976..69e0846cb7 100644 --- a/compiler/linker/mips/relative_patcher_mips.cc +++ b/compiler/linker/mips/relative_patcher_mips.cc @@ -18,6 +18,7 @@ #include "compiled_method.h" #include "debug/method_debug_info.h" +#include "linker/linker_patch.h" namespace art { namespace linker { diff --git a/compiler/linker/mips64/relative_patcher_mips64.cc b/compiler/linker/mips64/relative_patcher_mips64.cc index 2bcd98a2b0..aae5746278 100644 --- a/compiler/linker/mips64/relative_patcher_mips64.cc +++ b/compiler/linker/mips64/relative_patcher_mips64.cc @@ -18,6 +18,7 @@ #include "compiled_method.h" #include "debug/method_debug_info.h" +#include "linker/linker_patch.h" namespace art { namespace linker { diff --git a/compiler/linker/multi_oat_relative_patcher.cc b/compiler/linker/multi_oat_relative_patcher.cc deleted file mode 100644 index 4ae75d61c7..0000000000 --- a/compiler/linker/multi_oat_relative_patcher.cc +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2016 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 "multi_oat_relative_patcher.h" - -#include "base/bit_utils.h" -#include "base/logging.h" -#include "globals.h" - -namespace art { -namespace linker { - -MultiOatRelativePatcher::MultiOatRelativePatcher(InstructionSet instruction_set, - const InstructionSetFeatures* features) - : method_offset_map_(), - relative_patcher_( - linker::RelativePatcher::Create(instruction_set, features, &method_offset_map_)), - adjustment_(0u), - instruction_set_(instruction_set), - start_size_code_alignment_(0u), - start_size_relative_call_thunks_(0u), - start_size_misc_thunks_(0u) { -} - -void MultiOatRelativePatcher::StartOatFile(uint32_t adjustment) { - DCHECK_ALIGNED(adjustment, kPageSize); - adjustment_ = adjustment; - - start_size_code_alignment_ = relative_patcher_->CodeAlignmentSize(); - start_size_relative_call_thunks_ = relative_patcher_->RelativeCallThunksSize(); - start_size_misc_thunks_ = relative_patcher_->MiscThunksSize(); -} - -uint32_t MultiOatRelativePatcher::CodeAlignmentSize() const { - DCHECK_GE(relative_patcher_->CodeAlignmentSize(), start_size_code_alignment_); - return relative_patcher_->CodeAlignmentSize() - start_size_code_alignment_; -} - -uint32_t MultiOatRelativePatcher::RelativeCallThunksSize() const { - DCHECK_GE(relative_patcher_->RelativeCallThunksSize(), start_size_relative_call_thunks_); - return relative_patcher_->RelativeCallThunksSize() - start_size_relative_call_thunks_; -} - -uint32_t MultiOatRelativePatcher::MiscThunksSize() const { - DCHECK_GE(relative_patcher_->MiscThunksSize(), start_size_misc_thunks_); - return relative_patcher_->MiscThunksSize() - start_size_misc_thunks_; -} - -std::pair<bool, uint32_t> MultiOatRelativePatcher::MethodOffsetMap::FindMethodOffset( - MethodReference ref) { - auto it = map.find(ref); - if (it == map.end()) { - return std::pair<bool, uint32_t>(false, 0u); - } else { - return std::pair<bool, uint32_t>(true, it->second); - } -} -} // namespace linker -} // namespace art diff --git a/compiler/linker/multi_oat_relative_patcher.h b/compiler/linker/multi_oat_relative_patcher.h deleted file mode 100644 index 02cd4b0118..0000000000 --- a/compiler/linker/multi_oat_relative_patcher.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2016 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_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_ -#define ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_ - -#include "arch/instruction_set.h" -#include "debug/method_debug_info.h" -#include "method_reference.h" -#include "relative_patcher.h" -#include "safe_map.h" - -namespace art { - -class CompiledMethod; -class LinkerPatch; -class InstructionSetFeatures; - -namespace linker { - -// MultiOatRelativePatcher is a helper class for handling patching across -// any number of oat files. It provides storage for method code offsets -// and wraps RelativePatcher calls, adjusting relative offsets according -// to the value set by SetAdjustment(). -class MultiOatRelativePatcher FINAL { - public: - using const_iterator = SafeMap<MethodReference, uint32_t>::const_iterator; - - MultiOatRelativePatcher(InstructionSet instruction_set, const InstructionSetFeatures* features); - - // Mark the start of a new oat file (for statistics retrieval) and set the - // adjustment for a new oat file to apply to all relative offsets that are - // passed to the MultiOatRelativePatcher. - // - // The adjustment should be the global offset of the base from which relative - // offsets are calculated, such as the start of .rodata for the current oat file. - // It must must never point directly to a method's code to avoid relative offsets - // with value 0 because this value is used as a missing offset indication in - // GetOffset() and an error indication in WriteThunks(). Additionally, it must be - // page-aligned, so that it does not skew alignment calculations, say arm64 ADRP. - void StartOatFile(uint32_t adjustment); - - // Get relative offset. Returns 0 when the offset has not been set yet. - uint32_t GetOffset(MethodReference method_ref) { - auto it = method_offset_map_.map.find(method_ref); - return (it != method_offset_map_.map.end()) ? it->second - adjustment_ : 0u; - } - - // Set the offset. - void SetOffset(MethodReference method_ref, uint32_t offset) { - method_offset_map_.map.Put(method_ref, offset + adjustment_); - } - - // Wrapper around RelativePatcher::ReserveSpace(), doing offset adjustment. - uint32_t ReserveSpace(uint32_t offset, - const CompiledMethod* compiled_method, - MethodReference method_ref) { - offset += adjustment_; - offset = relative_patcher_->ReserveSpace(offset, compiled_method, method_ref); - offset -= adjustment_; - return offset; - } - - // Wrapper around RelativePatcher::ReserveSpaceEnd(), doing offset adjustment. - uint32_t ReserveSpaceEnd(uint32_t offset) { - offset += adjustment_; - offset = relative_patcher_->ReserveSpaceEnd(offset); - offset -= adjustment_; - return offset; - } - - // Wrapper around RelativePatcher::WriteThunks(), doing offset adjustment. - uint32_t WriteThunks(OutputStream* out, uint32_t offset) { - offset += adjustment_; - offset = relative_patcher_->WriteThunks(out, offset); - if (offset != 0u) { // 0u indicates write error. - offset -= adjustment_; - } - return offset; - } - - // Wrapper around RelativePatcher::PatchCall(), doing offset adjustment. - void PatchCall(std::vector<uint8_t>* code, - uint32_t literal_offset, - uint32_t patch_offset, - uint32_t target_offset) { - patch_offset += adjustment_; - target_offset += adjustment_; - relative_patcher_->PatchCall(code, literal_offset, patch_offset, target_offset); - } - - // Wrapper around RelativePatcher::PatchPcRelativeReference(), doing offset adjustment. - void PatchPcRelativeReference(std::vector<uint8_t>* code, - const LinkerPatch& patch, - uint32_t patch_offset, - uint32_t target_offset) { - patch_offset += adjustment_; - target_offset += adjustment_; - relative_patcher_->PatchPcRelativeReference(code, patch, patch_offset, target_offset); - } - - void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code, - const LinkerPatch& patch, - uint32_t patch_offset) { - patch_offset += adjustment_; - relative_patcher_->PatchBakerReadBarrierBranch(code, patch, patch_offset); - } - - std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(size_t executable_offset) { - executable_offset += adjustment_; - return relative_patcher_->GenerateThunkDebugInfo(executable_offset); - } - - // Wrappers around RelativePatcher for statistics retrieval. - uint32_t CodeAlignmentSize() const; - uint32_t RelativeCallThunksSize() const; - uint32_t MiscThunksSize() const; - - private: - // Map method reference to assigned offset. - // Wrap the map in a class implementing linker::RelativePatcherTargetProvider. - class MethodOffsetMap : public linker::RelativePatcherTargetProvider { - public: - std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE; - SafeMap<MethodReference, uint32_t> map; - }; - - MethodOffsetMap method_offset_map_; - std::unique_ptr<RelativePatcher> relative_patcher_; - uint32_t adjustment_; - InstructionSet instruction_set_; - - uint32_t start_size_code_alignment_; - uint32_t start_size_relative_call_thunks_; - uint32_t start_size_misc_thunks_; - - friend class MultiOatRelativePatcherTest; - - DISALLOW_COPY_AND_ASSIGN(MultiOatRelativePatcher); -}; - -} // namespace linker -} // namespace art - -#endif // ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_ diff --git a/compiler/linker/multi_oat_relative_patcher_test.cc b/compiler/linker/multi_oat_relative_patcher_test.cc deleted file mode 100644 index 5c359dc9ca..0000000000 --- a/compiler/linker/multi_oat_relative_patcher_test.cc +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (C) 2016 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 "multi_oat_relative_patcher.h" - -#include "compiled_method.h" -#include "debug/method_debug_info.h" -#include "gtest/gtest.h" -#include "vector_output_stream.h" - -namespace art { -namespace linker { - -static const MethodReference kNullMethodRef = MethodReference(nullptr, 0u); - -class MultiOatRelativePatcherTest : public testing::Test { - protected: - class MockPatcher : public RelativePatcher { - public: - MockPatcher() { } - - uint32_t ReserveSpace(uint32_t offset, - const CompiledMethod* compiled_method ATTRIBUTE_UNUSED, - MethodReference method_ref) OVERRIDE { - last_reserve_offset_ = offset; - last_reserve_method_ = method_ref; - offset += next_reserve_adjustment_; - next_reserve_adjustment_ = 0u; - return offset; - } - - uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE { - last_reserve_offset_ = offset; - last_reserve_method_ = kNullMethodRef; - offset += next_reserve_adjustment_; - next_reserve_adjustment_ = 0u; - return offset; - } - - uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE { - last_write_offset_ = offset; - if (next_write_alignment_ != 0u) { - offset += next_write_alignment_; - bool success = WriteCodeAlignment(out, next_write_alignment_); - CHECK(success); - next_write_alignment_ = 0u; - } - if (next_write_call_thunk_ != 0u) { - offset += next_write_call_thunk_; - std::vector<uint8_t> thunk(next_write_call_thunk_, 'c'); - bool success = WriteThunk(out, ArrayRef<const uint8_t>(thunk)); - CHECK(success); - next_write_call_thunk_ = 0u; - } - if (next_write_misc_thunk_ != 0u) { - offset += next_write_misc_thunk_; - std::vector<uint8_t> thunk(next_write_misc_thunk_, 'm'); - bool success = WriteMiscThunk(out, ArrayRef<const uint8_t>(thunk)); - CHECK(success); - next_write_misc_thunk_ = 0u; - } - return offset; - } - - void PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, - uint32_t literal_offset, - uint32_t patch_offset, - uint32_t target_offset) OVERRIDE { - last_literal_offset_ = literal_offset; - last_patch_offset_ = patch_offset; - last_target_offset_ = target_offset; - } - - void PatchPcRelativeReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, - const LinkerPatch& patch, - uint32_t patch_offset, - uint32_t target_offset) OVERRIDE { - last_literal_offset_ = patch.LiteralOffset(); - last_patch_offset_ = patch_offset; - last_target_offset_ = target_offset; - } - - void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, - const LinkerPatch& patch ATTRIBUTE_UNUSED, - uint32_t patch_offset ATTRIBUTE_UNUSED) { - LOG(FATAL) << "UNIMPLEMENTED"; - } - - std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo( - uint32_t executable_offset ATTRIBUTE_UNUSED) { - LOG(FATAL) << "UNIMPLEMENTED"; - UNREACHABLE(); - } - - uint32_t last_reserve_offset_ = 0u; - MethodReference last_reserve_method_ = kNullMethodRef; - uint32_t next_reserve_adjustment_ = 0u; - - uint32_t last_write_offset_ = 0u; - uint32_t next_write_alignment_ = 0u; - uint32_t next_write_call_thunk_ = 0u; - uint32_t next_write_misc_thunk_ = 0u; - - uint32_t last_literal_offset_ = 0u; - uint32_t last_patch_offset_ = 0u; - uint32_t last_target_offset_ = 0u; - }; - - MultiOatRelativePatcherTest() - : instruction_set_features_(InstructionSetFeatures::FromCppDefines()), - patcher_(kRuntimeISA, instruction_set_features_.get()) { - std::unique_ptr<MockPatcher> mock(new MockPatcher()); - mock_ = mock.get(); - patcher_.relative_patcher_ = std::move(mock); - } - - std::unique_ptr<const InstructionSetFeatures> instruction_set_features_; - MultiOatRelativePatcher patcher_; - MockPatcher* mock_; -}; - -TEST_F(MultiOatRelativePatcherTest, Offsets) { - const DexFile* dex_file = reinterpret_cast<const DexFile*>(1); - MethodReference ref1(dex_file, 1u); - MethodReference ref2(dex_file, 2u); - EXPECT_EQ(0u, patcher_.GetOffset(ref1)); - EXPECT_EQ(0u, patcher_.GetOffset(ref2)); - - uint32_t adjustment1 = 0x1000; - patcher_.StartOatFile(adjustment1); - EXPECT_EQ(0u, patcher_.GetOffset(ref1)); - EXPECT_EQ(0u, patcher_.GetOffset(ref2)); - - uint32_t off1 = 0x1234; - patcher_.SetOffset(ref1, off1); - EXPECT_EQ(off1, patcher_.GetOffset(ref1)); - EXPECT_EQ(0u, patcher_.GetOffset(ref2)); - - uint32_t adjustment2 = 0x30000; - patcher_.StartOatFile(adjustment2); - EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1)); - EXPECT_EQ(0u, patcher_.GetOffset(ref2)); - - uint32_t off2 = 0x4321; - patcher_.SetOffset(ref2, off2); - EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1)); - EXPECT_EQ(off2, patcher_.GetOffset(ref2)); - - uint32_t adjustment3 = 0x78000; - patcher_.StartOatFile(adjustment3); - EXPECT_EQ(off1 + adjustment1 - adjustment3, patcher_.GetOffset(ref1)); - EXPECT_EQ(off2 + adjustment2 - adjustment3, patcher_.GetOffset(ref2)); -} - -TEST_F(MultiOatRelativePatcherTest, OffsetsInReserve) { - const DexFile* dex_file = reinterpret_cast<const DexFile*>(1); - MethodReference ref1(dex_file, 1u); - MethodReference ref2(dex_file, 2u); - MethodReference ref3(dex_file, 3u); - const CompiledMethod* method = reinterpret_cast<const CompiledMethod*>(-1); - - uint32_t adjustment1 = 0x1000; - patcher_.StartOatFile(adjustment1); - - uint32_t method1_offset = 0x100; - uint32_t method1_offset_check = patcher_.ReserveSpace(method1_offset, method, ref1); - ASSERT_EQ(adjustment1 + method1_offset, mock_->last_reserve_offset_); - ASSERT_TRUE(ref1 == mock_->last_reserve_method_); - ASSERT_EQ(method1_offset, method1_offset_check); - - uint32_t method2_offset = 0x1230; - uint32_t method2_reserve_adjustment = 0x10; - mock_->next_reserve_adjustment_ = method2_reserve_adjustment; - uint32_t method2_offset_adjusted = patcher_.ReserveSpace(method2_offset, method, ref2); - ASSERT_EQ(adjustment1 + method2_offset, mock_->last_reserve_offset_); - ASSERT_TRUE(ref2 == mock_->last_reserve_method_); - ASSERT_EQ(method2_offset + method2_reserve_adjustment, method2_offset_adjusted); - - uint32_t end1_offset = 0x4320; - uint32_t end1_offset_check = patcher_.ReserveSpaceEnd(end1_offset); - ASSERT_EQ(adjustment1 + end1_offset, mock_->last_reserve_offset_); - ASSERT_TRUE(kNullMethodRef == mock_->last_reserve_method_); - ASSERT_EQ(end1_offset, end1_offset_check); - - uint32_t adjustment2 = 0xd000; - patcher_.StartOatFile(adjustment2); - - uint32_t method3_offset = 0xf00; - uint32_t method3_offset_check = patcher_.ReserveSpace(method3_offset, method, ref3); - ASSERT_EQ(adjustment2 + method3_offset, mock_->last_reserve_offset_); - ASSERT_TRUE(ref3 == mock_->last_reserve_method_); - ASSERT_EQ(method3_offset, method3_offset_check); - - uint32_t end2_offset = 0x2400; - uint32_t end2_reserve_adjustment = 0x20; - mock_->next_reserve_adjustment_ = end2_reserve_adjustment; - uint32_t end2_offset_adjusted = patcher_.ReserveSpaceEnd(end2_offset); - ASSERT_EQ(adjustment2 + end2_offset, mock_->last_reserve_offset_); - ASSERT_TRUE(kNullMethodRef == mock_->last_reserve_method_); - ASSERT_EQ(end2_offset + end2_reserve_adjustment, end2_offset_adjusted); -} - -TEST_F(MultiOatRelativePatcherTest, Write) { - std::vector<uint8_t> output; - VectorOutputStream vos("output", &output); - - uint32_t adjustment1 = 0x1000; - patcher_.StartOatFile(adjustment1); - - uint32_t method1_offset = 0x100; - uint32_t method1_offset_check = patcher_.WriteThunks(&vos, method1_offset); - ASSERT_EQ(adjustment1 + method1_offset, mock_->last_write_offset_); - ASSERT_EQ(method1_offset, method1_offset_check); - vos.WriteFully("1", 1); // Mark method1. - - uint32_t method2_offset = 0x1230; - uint32_t method2_alignment_size = 1; - uint32_t method2_call_thunk_size = 2; - mock_->next_write_alignment_ = method2_alignment_size; - mock_->next_write_call_thunk_ = method2_call_thunk_size; - uint32_t method2_offset_adjusted = patcher_.WriteThunks(&vos, method2_offset); - ASSERT_EQ(adjustment1 + method2_offset, mock_->last_write_offset_); - ASSERT_EQ(method2_offset + method2_alignment_size + method2_call_thunk_size, - method2_offset_adjusted); - vos.WriteFully("2", 1); // Mark method2. - - EXPECT_EQ(method2_alignment_size, patcher_.CodeAlignmentSize()); - EXPECT_EQ(method2_call_thunk_size, patcher_.RelativeCallThunksSize()); - - uint32_t adjustment2 = 0xd000; - patcher_.StartOatFile(adjustment2); - - uint32_t method3_offset = 0xf00; - uint32_t method3_alignment_size = 2; - uint32_t method3_misc_thunk_size = 1; - mock_->next_write_alignment_ = method3_alignment_size; - mock_->next_write_misc_thunk_ = method3_misc_thunk_size; - uint32_t method3_offset_adjusted = patcher_.WriteThunks(&vos, method3_offset); - ASSERT_EQ(adjustment2 + method3_offset, mock_->last_write_offset_); - ASSERT_EQ(method3_offset + method3_alignment_size + method3_misc_thunk_size, - method3_offset_adjusted); - vos.WriteFully("3", 1); // Mark method3. - - EXPECT_EQ(method3_alignment_size, patcher_.CodeAlignmentSize()); - EXPECT_EQ(method3_misc_thunk_size, patcher_.MiscThunksSize()); - - uint8_t expected_output[] = { - '1', - 0, 'c', 'c', '2', - 0, 0, 'm', '3', - }; - ASSERT_EQ(arraysize(expected_output), output.size()); - for (size_t i = 0; i != arraysize(expected_output); ++i) { - ASSERT_EQ(expected_output[i], output[i]) << i; - } -} - -TEST_F(MultiOatRelativePatcherTest, Patch) { - std::vector<uint8_t> code(16); - - uint32_t adjustment1 = 0x1000; - patcher_.StartOatFile(adjustment1); - - uint32_t method1_literal_offset = 4u; - uint32_t method1_patch_offset = 0x1234u; - uint32_t method1_target_offset = 0x8888u; - patcher_.PatchCall(&code, method1_literal_offset, method1_patch_offset, method1_target_offset); - DCHECK_EQ(method1_literal_offset, mock_->last_literal_offset_); - DCHECK_EQ(method1_patch_offset + adjustment1, mock_->last_patch_offset_); - DCHECK_EQ(method1_target_offset + adjustment1, mock_->last_target_offset_); - - uint32_t method2_literal_offset = 12u; - uint32_t method2_patch_offset = 0x7654u; - uint32_t method2_target_offset = 0xccccu; - LinkerPatch method2_patch = - LinkerPatch::StringBssEntryPatch(method2_literal_offset, nullptr, 0u, 1u); - patcher_.PatchPcRelativeReference( - &code, method2_patch, method2_patch_offset, method2_target_offset); - DCHECK_EQ(method2_literal_offset, mock_->last_literal_offset_); - DCHECK_EQ(method2_patch_offset + adjustment1, mock_->last_patch_offset_); - DCHECK_EQ(method2_target_offset + adjustment1, mock_->last_target_offset_); - - uint32_t adjustment2 = 0xd000; - patcher_.StartOatFile(adjustment2); - - uint32_t method3_literal_offset = 8u; - uint32_t method3_patch_offset = 0x108u; - uint32_t method3_target_offset = 0x200u; - patcher_.PatchCall(&code, method3_literal_offset, method3_patch_offset, method3_target_offset); - DCHECK_EQ(method3_literal_offset, mock_->last_literal_offset_); - DCHECK_EQ(method3_patch_offset + adjustment2, mock_->last_patch_offset_); - DCHECK_EQ(method3_target_offset + adjustment2, mock_->last_target_offset_); -} - -} // namespace linker -} // namespace art diff --git a/compiler/linker/output_stream.cc b/compiler/linker/output_stream.cc index a8b64ca1ce..f5a19138a5 100644 --- a/compiler/linker/output_stream.cc +++ b/compiler/linker/output_stream.cc @@ -17,6 +17,7 @@ #include "output_stream.h" namespace art { +namespace linker { std::ostream& operator<<(std::ostream& os, const Whence& rhs) { switch (rhs) { @@ -28,4 +29,5 @@ std::ostream& operator<<(std::ostream& os, const Whence& rhs) { return os; } +} // namespace linker } // namespace art diff --git a/compiler/linker/output_stream.h b/compiler/linker/output_stream.h index 96a5f489f0..5310e2fa41 100644 --- a/compiler/linker/output_stream.h +++ b/compiler/linker/output_stream.h @@ -23,6 +23,7 @@ #include "base/macros.h" namespace art { +namespace linker { enum Whence { kSeekSet = SEEK_SET, @@ -59,6 +60,7 @@ class OutputStream { DISALLOW_COPY_AND_ASSIGN(OutputStream); }; +} // namespace linker } // namespace art #endif // ART_COMPILER_LINKER_OUTPUT_STREAM_H_ diff --git a/compiler/linker/output_stream_test.cc b/compiler/linker/output_stream_test.cc index 87cb10000b..ad298406be 100644 --- a/compiler/linker/output_stream_test.cc +++ b/compiler/linker/output_stream_test.cc @@ -23,6 +23,7 @@ #include "common_runtime_test.h" namespace art { +namespace linker { class OutputStreamTest : public CommonRuntimeTest { protected: @@ -133,4 +134,5 @@ TEST_F(OutputStreamTest, BufferedFlush) { ASSERT_TRUE(checking_output_stream->flush_called); } +} // namespace linker } // namespace art diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h index 53a096627f..548e12896a 100644 --- a/compiler/linker/relative_patcher.h +++ b/compiler/linker/relative_patcher.h @@ -28,8 +28,6 @@ namespace art { class CompiledMethod; -class LinkerPatch; -class OutputStream; namespace debug { struct MethodDebugInfo; @@ -37,6 +35,9 @@ struct MethodDebugInfo; namespace linker { +class LinkerPatch; +class OutputStream; + /** * @class RelativePatcherTargetProvider * @brief Interface for providing method offsets for relative call targets. diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index ca8743a561..6297dd0481 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -21,7 +21,7 @@ #include "arch/instruction_set_features.h" #include "base/array_ref.h" #include "base/macros.h" -#include "compiled_method.h" +#include "compiled_method-inl.h" #include "dex/verification_results.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" @@ -252,8 +252,8 @@ class RelativePatcherTest : public testing::Test { } // Map method reference to assinged offset. - // Wrap the map in a class implementing linker::RelativePatcherTargetProvider. - class MethodOffsetMap FINAL : public linker::RelativePatcherTargetProvider { + // Wrap the map in a class implementing RelativePatcherTargetProvider. + class MethodOffsetMap FINAL : public RelativePatcherTargetProvider { public: std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE { auto it = map.find(ref); diff --git a/compiler/linker/vector_output_stream.cc b/compiler/linker/vector_output_stream.cc index f758005c52..75f90e5f94 100644 --- a/compiler/linker/vector_output_stream.cc +++ b/compiler/linker/vector_output_stream.cc @@ -19,6 +19,7 @@ #include "base/logging.h" namespace art { +namespace linker { VectorOutputStream::VectorOutputStream(const std::string& location, std::vector<uint8_t>* vector) : OutputStream(location), offset_(vector->size()), vector_(vector) {} @@ -45,4 +46,5 @@ off_t VectorOutputStream::Seek(off_t offset, Whence whence) { return offset_; } +} // namespace linker } // namespace art diff --git a/compiler/linker/vector_output_stream.h b/compiler/linker/vector_output_stream.h index a9b93e7a83..92caf596ab 100644 --- a/compiler/linker/vector_output_stream.h +++ b/compiler/linker/vector_output_stream.h @@ -24,6 +24,7 @@ #include <vector> namespace art { +namespace linker { class VectorOutputStream FINAL : public OutputStream { public: @@ -64,6 +65,7 @@ class VectorOutputStream FINAL : public OutputStream { DISALLOW_COPY_AND_ASSIGN(VectorOutputStream); }; +} // namespace linker } // namespace art #endif // ART_COMPILER_LINKER_VECTOR_OUTPUT_STREAM_H_ diff --git a/compiler/linker/x86/relative_patcher_x86.cc b/compiler/linker/x86/relative_patcher_x86.cc index 6967b0b6c2..cdd2cef13a 100644 --- a/compiler/linker/x86/relative_patcher_x86.cc +++ b/compiler/linker/x86/relative_patcher_x86.cc @@ -17,6 +17,7 @@ #include "linker/x86/relative_patcher_x86.h" #include "compiled_method.h" +#include "linker/linker_patch.h" namespace art { namespace linker { diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.cc b/compiler/linker/x86_64/relative_patcher_x86_64.cc index 156ece9909..9633564999 100644 --- a/compiler/linker/x86_64/relative_patcher_x86_64.cc +++ b/compiler/linker/x86_64/relative_patcher_x86_64.cc @@ -17,6 +17,7 @@ #include "linker/x86_64/relative_patcher_x86_64.h" #include "compiled_method.h" +#include "linker/linker_patch.h" namespace art { namespace linker { diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc deleted file mode 100644 index 6f8904979d..0000000000 --- a/compiler/oat_test.cc +++ /dev/null @@ -1,870 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "android-base/stringprintf.h" - -#include "arch/instruction_set_features.h" -#include "art_method-inl.h" -#include "base/enums.h" -#include "base/stl_util.h" -#include "base/unix_file/fd_file.h" -#include "class_linker.h" -#include "common_compiler_test.h" -#include "compiled_method.h" -#include "compiler.h" -#include "debug/method_debug_info.h" -#include "dex/quick_compiler_callbacks.h" -#include "dex/verification_results.h" -#include "driver/compiler_driver.h" -#include "driver/compiler_options.h" -#include "elf_writer.h" -#include "elf_writer_quick.h" -#include "entrypoints/quick/quick_entrypoints.h" -#include "linker/buffered_output_stream.h" -#include "linker/file_output_stream.h" -#include "linker/multi_oat_relative_patcher.h" -#include "linker/vector_output_stream.h" -#include "mirror/class-inl.h" -#include "mirror/object-inl.h" -#include "mirror/object_array-inl.h" -#include "oat_file-inl.h" -#include "oat_writer.h" -#include "scoped_thread_state_change-inl.h" -#include "utils/test_dex_file_builder.h" - -namespace art { - -NO_RETURN static void Usage(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - std::string error; - android::base::StringAppendV(&error, fmt, ap); - LOG(FATAL) << error; - va_end(ap); - UNREACHABLE(); -} - -class OatTest : public CommonCompilerTest { - protected: - static const bool kCompile = false; // DISABLED_ due to the time to compile libcore - - void CheckMethod(ArtMethod* method, - const OatFile::OatMethod& oat_method, - const DexFile& dex_file) - REQUIRES_SHARED(Locks::mutator_lock_) { - const CompiledMethod* compiled_method = - compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, - method->GetDexMethodIndex())); - - if (compiled_method == nullptr) { - EXPECT_TRUE(oat_method.GetQuickCode() == nullptr) << method->PrettyMethod() << " " - << oat_method.GetQuickCode(); - EXPECT_EQ(oat_method.GetFrameSizeInBytes(), 0U); - EXPECT_EQ(oat_method.GetCoreSpillMask(), 0U); - EXPECT_EQ(oat_method.GetFpSpillMask(), 0U); - } else { - const void* quick_oat_code = oat_method.GetQuickCode(); - EXPECT_TRUE(quick_oat_code != nullptr) << method->PrettyMethod(); - EXPECT_EQ(oat_method.GetFrameSizeInBytes(), compiled_method->GetFrameSizeInBytes()); - EXPECT_EQ(oat_method.GetCoreSpillMask(), compiled_method->GetCoreSpillMask()); - EXPECT_EQ(oat_method.GetFpSpillMask(), compiled_method->GetFpSpillMask()); - uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(quick_oat_code), 2); - quick_oat_code = reinterpret_cast<const void*>(oat_code_aligned); - ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode(); - EXPECT_FALSE(quick_code.empty()); - size_t code_size = quick_code.size() * sizeof(quick_code[0]); - EXPECT_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size)) - << method->PrettyMethod() << " " << code_size; - CHECK_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size)); - } - } - - void SetupCompiler(Compiler::Kind compiler_kind, - InstructionSet insn_set, - const std::vector<std::string>& compiler_options, - /*out*/std::string* error_msg) { - ASSERT_TRUE(error_msg != nullptr); - insn_features_ = InstructionSetFeatures::FromVariant(insn_set, "default", error_msg); - ASSERT_TRUE(insn_features_ != nullptr) << error_msg; - compiler_options_.reset(new CompilerOptions); - for (const std::string& option : compiler_options) { - compiler_options_->ParseCompilerOption(option, Usage); - } - verification_results_.reset(new VerificationResults(compiler_options_.get())); - callbacks_.reset(new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp)); - callbacks_->SetVerificationResults(verification_results_.get()); - Runtime::Current()->SetCompilerCallbacks(callbacks_.get()); - timer_.reset(new CumulativeLogger("Compilation times")); - compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), - verification_results_.get(), - compiler_kind, - insn_set, - insn_features_.get(), - /* image_classes */ nullptr, - /* compiled_classes */ nullptr, - /* compiled_methods */ nullptr, - /* thread_count */ 2, - /* dump_stats */ true, - /* dump_passes */ true, - timer_.get(), - /* swap_fd */ -1, - /* profile_compilation_info */ nullptr)); - } - - bool WriteElf(File* vdex_file, - File* oat_file, - const std::vector<const DexFile*>& dex_files, - SafeMap<std::string, std::string>& key_value_store, - bool verify) { - TimingLogger timings("WriteElf", false, false); - OatWriter oat_writer(/*compiling_boot_image*/false, - &timings, - /*profile_compilation_info*/nullptr); - for (const DexFile* dex_file : dex_files) { - ArrayRef<const uint8_t> raw_dex_file( - reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()), - dex_file->GetHeader().file_size_); - if (!oat_writer.AddRawDexFileSource(raw_dex_file, - dex_file->GetLocation().c_str(), - dex_file->GetLocationChecksum())) { - return false; - } - } - return DoWriteElf(vdex_file, oat_file, oat_writer, key_value_store, verify); - } - - bool WriteElf(File* vdex_file, - File* oat_file, - const std::vector<const char*>& dex_filenames, - SafeMap<std::string, std::string>& key_value_store, - bool verify, - ProfileCompilationInfo* profile_compilation_info) { - TimingLogger timings("WriteElf", false, false); - OatWriter oat_writer(/*compiling_boot_image*/false, &timings, profile_compilation_info); - for (const char* dex_filename : dex_filenames) { - if (!oat_writer.AddDexFileSource(dex_filename, dex_filename)) { - return false; - } - } - return DoWriteElf(vdex_file, oat_file, oat_writer, key_value_store, verify); - } - - bool WriteElf(File* vdex_file, - File* oat_file, - File&& zip_fd, - const char* location, - SafeMap<std::string, std::string>& key_value_store, - bool verify) { - TimingLogger timings("WriteElf", false, false); - OatWriter oat_writer(/*compiling_boot_image*/false, - &timings, - /*profile_compilation_info*/nullptr); - if (!oat_writer.AddZippedDexFilesSource(std::move(zip_fd), location)) { - return false; - } - return DoWriteElf(vdex_file, oat_file, oat_writer, key_value_store, verify); - } - - bool DoWriteElf(File* vdex_file, - File* oat_file, - OatWriter& oat_writer, - SafeMap<std::string, std::string>& key_value_store, - bool verify) { - std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick( - compiler_driver_->GetInstructionSet(), - compiler_driver_->GetInstructionSetFeatures(), - &compiler_driver_->GetCompilerOptions(), - oat_file); - elf_writer->Start(); - OutputStream* oat_rodata = elf_writer->StartRoData(); - std::unique_ptr<MemMap> opened_dex_files_map; - std::vector<std::unique_ptr<const DexFile>> opened_dex_files; - if (!oat_writer.WriteAndOpenDexFiles(kIsVdexEnabled ? vdex_file : oat_file, - oat_rodata, - compiler_driver_->GetInstructionSet(), - compiler_driver_->GetInstructionSetFeatures(), - &key_value_store, - verify, - /* update_input_vdex */ false, - &opened_dex_files_map, - &opened_dex_files)) { - return false; - } - - Runtime* runtime = Runtime::Current(); - ClassLinker* const class_linker = runtime->GetClassLinker(); - std::vector<const DexFile*> dex_files; - for (const std::unique_ptr<const DexFile>& dex_file : opened_dex_files) { - dex_files.push_back(dex_file.get()); - ScopedObjectAccess soa(Thread::Current()); - class_linker->RegisterDexFile(*dex_file, nullptr); - } - linker::MultiOatRelativePatcher patcher(compiler_driver_->GetInstructionSet(), - instruction_set_features_.get()); - oat_writer.Initialize(compiler_driver_.get(), nullptr, dex_files); - oat_writer.PrepareLayout(&patcher); - size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset(); - size_t text_size = oat_writer.GetOatSize() - rodata_size; - elf_writer->PrepareDynamicSection(rodata_size, - text_size, - oat_writer.GetBssSize(), - oat_writer.GetBssMethodsOffset(), - oat_writer.GetBssRootsOffset()); - - if (kIsVdexEnabled) { - std::unique_ptr<BufferedOutputStream> vdex_out = - std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(vdex_file)); - if (!oat_writer.WriteVerifierDeps(vdex_out.get(), nullptr)) { - return false; - } - if (!oat_writer.WriteChecksumsAndVdexHeader(vdex_out.get())) { - return false; - } - } - - if (!oat_writer.WriteRodata(oat_rodata)) { - return false; - } - elf_writer->EndRoData(oat_rodata); - - OutputStream* text = elf_writer->StartText(); - if (!oat_writer.WriteCode(text)) { - return false; - } - elf_writer->EndText(text); - - if (!oat_writer.WriteHeader(elf_writer->GetStream(), 42U, 4096U, 0)) { - return false; - } - - elf_writer->WriteDynamicSection(); - elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo()); - - if (!elf_writer->End()) { - return false; - } - - opened_dex_files_maps_.emplace_back(std::move(opened_dex_files_map)); - for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files) { - opened_dex_files_.emplace_back(dex_file.release()); - } - return true; - } - - void TestDexFileInput(bool verify, bool low_4gb, bool use_profile); - void TestZipFileInput(bool verify); - void TestZipFileInputWithEmptyDex(); - - std::unique_ptr<const InstructionSetFeatures> insn_features_; - std::unique_ptr<QuickCompilerCallbacks> callbacks_; - - std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps_; - std::vector<std::unique_ptr<const DexFile>> opened_dex_files_; -}; - -class ZipBuilder { - public: - explicit ZipBuilder(File* zip_file) : zip_file_(zip_file) { } - - bool AddFile(const char* location, const void* data, size_t size) { - off_t offset = lseek(zip_file_->Fd(), 0, SEEK_CUR); - if (offset == static_cast<off_t>(-1)) { - return false; - } - - ZipFileHeader file_header; - file_header.crc32 = crc32(0u, reinterpret_cast<const Bytef*>(data), size); - file_header.compressed_size = size; - file_header.uncompressed_size = size; - file_header.filename_length = strlen(location); - - if (!zip_file_->WriteFully(&file_header, sizeof(file_header)) || - !zip_file_->WriteFully(location, file_header.filename_length) || - !zip_file_->WriteFully(data, size)) { - return false; - } - - CentralDirectoryFileHeader cdfh; - cdfh.crc32 = file_header.crc32; - cdfh.compressed_size = size; - cdfh.uncompressed_size = size; - cdfh.filename_length = file_header.filename_length; - cdfh.relative_offset_of_local_file_header = offset; - file_data_.push_back(FileData { cdfh, location }); - return true; - } - - bool Finish() { - off_t offset = lseek(zip_file_->Fd(), 0, SEEK_CUR); - if (offset == static_cast<off_t>(-1)) { - return false; - } - - size_t central_directory_size = 0u; - for (const FileData& file_data : file_data_) { - if (!zip_file_->WriteFully(&file_data.cdfh, sizeof(file_data.cdfh)) || - !zip_file_->WriteFully(file_data.location, file_data.cdfh.filename_length)) { - return false; - } - central_directory_size += sizeof(file_data.cdfh) + file_data.cdfh.filename_length; - } - EndOfCentralDirectoryRecord eocd_record; - eocd_record.number_of_central_directory_records_on_this_disk = file_data_.size(); - eocd_record.total_number_of_central_directory_records = file_data_.size(); - eocd_record.size_of_central_directory = central_directory_size; - eocd_record.offset_of_start_of_central_directory = offset; - return - zip_file_->WriteFully(&eocd_record, sizeof(eocd_record)) && - zip_file_->Flush() == 0; - } - - private: - struct PACKED(1) ZipFileHeader { - uint32_t signature = 0x04034b50; - uint16_t version_needed_to_extract = 10; - uint16_t general_purpose_bit_flag = 0; - uint16_t compression_method = 0; // 0 = store only. - uint16_t file_last_modification_time = 0u; - uint16_t file_last_modification_date = 0u; - uint32_t crc32; - uint32_t compressed_size; - uint32_t uncompressed_size; - uint16_t filename_length; - uint16_t extra_field_length = 0u; // No extra fields. - }; - - struct PACKED(1) CentralDirectoryFileHeader { - uint32_t signature = 0x02014b50; - uint16_t version_made_by = 10; - uint16_t version_needed_to_extract = 10; - uint16_t general_purpose_bit_flag = 0; - uint16_t compression_method = 0; // 0 = store only. - uint16_t file_last_modification_time = 0u; - uint16_t file_last_modification_date = 0u; - uint32_t crc32; - uint32_t compressed_size; - uint32_t uncompressed_size; - uint16_t filename_length; - uint16_t extra_field_length = 0u; // No extra fields. - uint16_t file_comment_length = 0u; // No file comment. - uint16_t disk_number_where_file_starts = 0u; - uint16_t internal_file_attributes = 0u; - uint32_t external_file_attributes = 0u; - uint32_t relative_offset_of_local_file_header; - }; - - struct PACKED(1) EndOfCentralDirectoryRecord { - uint32_t signature = 0x06054b50; - uint16_t number_of_this_disk = 0u; - uint16_t disk_where_central_directory_starts = 0u; - uint16_t number_of_central_directory_records_on_this_disk; - uint16_t total_number_of_central_directory_records; - uint32_t size_of_central_directory; - uint32_t offset_of_start_of_central_directory; - uint16_t comment_length = 0u; // No file comment. - }; - - struct FileData { - CentralDirectoryFileHeader cdfh; - const char* location; - }; - - File* zip_file_; - std::vector<FileData> file_data_; -}; - -TEST_F(OatTest, WriteRead) { - TimingLogger timings("OatTest::WriteRead", false, false); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - - // TODO: make selectable. - Compiler::Kind compiler_kind = Compiler::kQuick; - InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86; - std::string error_msg; - SetupCompiler(compiler_kind, insn_set, std::vector<std::string>(), /*out*/ &error_msg); - - jobject class_loader = nullptr; - if (kCompile) { - TimingLogger timings2("OatTest::WriteRead", false, false); - compiler_driver_->SetDexFilesForOatFile(class_linker->GetBootClassPath()); - compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2); - } - - ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex"); - SafeMap<std::string, std::string> key_value_store; - key_value_store.Put(OatHeader::kImageLocationKey, "lue.art"); - bool success = WriteElf(tmp_vdex.GetFile(), - tmp_oat.GetFile(), - class_linker->GetBootClassPath(), - key_value_store, - false); - ASSERT_TRUE(success); - - if (kCompile) { // OatWriter strips the code, regenerate to compare - compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings); - } - std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp_oat.GetFilename(), - tmp_oat.GetFilename(), - nullptr, - nullptr, - false, - /*low_4gb*/true, - nullptr, - &error_msg)); - ASSERT_TRUE(oat_file.get() != nullptr) << error_msg; - const OatHeader& oat_header = oat_file->GetOatHeader(); - ASSERT_TRUE(oat_header.IsValid()); - ASSERT_EQ(class_linker->GetBootClassPath().size(), oat_header.GetDexFileCount()); // core - ASSERT_EQ(42U, oat_header.GetImageFileLocationOatChecksum()); - ASSERT_EQ(4096U, oat_header.GetImageFileLocationOatDataBegin()); - ASSERT_EQ("lue.art", std::string(oat_header.GetStoreValueByKey(OatHeader::kImageLocationKey))); - - ASSERT_TRUE(java_lang_dex_file_ != nullptr); - const DexFile& dex_file = *java_lang_dex_file_; - uint32_t dex_file_checksum = dex_file.GetLocationChecksum(); - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(), - &dex_file_checksum); - ASSERT_TRUE(oat_dex_file != nullptr); - CHECK_EQ(dex_file.GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum()); - ScopedObjectAccess soa(Thread::Current()); - auto pointer_size = class_linker->GetImagePointerSize(); - for (size_t i = 0; i < dex_file.NumClassDefs(); i++) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(i); - const uint8_t* class_data = dex_file.GetClassData(class_def); - - size_t num_virtual_methods = 0; - if (class_data != nullptr) { - ClassDataItemIterator it(dex_file, class_data); - num_virtual_methods = it.NumVirtualMethods(); - } - - const char* descriptor = dex_file.GetClassDescriptor(class_def); - mirror::Class* klass = class_linker->FindClass(soa.Self(), - descriptor, - ScopedNullHandle<mirror::ClassLoader>()); - - const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(i); - CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class.GetStatus()) << descriptor; - CHECK_EQ(kCompile ? OatClassType::kOatClassAllCompiled : OatClassType::kOatClassNoneCompiled, - oat_class.GetType()) << descriptor; - - size_t method_index = 0; - for (auto& m : klass->GetDirectMethods(pointer_size)) { - CheckMethod(&m, oat_class.GetOatMethod(method_index), dex_file); - ++method_index; - } - size_t visited_virtuals = 0; - // TODO We should also check copied methods in this test. - for (auto& m : klass->GetDeclaredVirtualMethods(pointer_size)) { - if (!klass->IsInterface()) { - EXPECT_FALSE(m.IsCopied()); - } - CheckMethod(&m, oat_class.GetOatMethod(method_index), dex_file); - ++method_index; - ++visited_virtuals; - } - EXPECT_EQ(visited_virtuals, num_virtual_methods); - } -} - -TEST_F(OatTest, OatHeaderSizeCheck) { - // If this test is failing and you have to update these constants, - // it is time to update OatHeader::kOatVersion - EXPECT_EQ(76U, sizeof(OatHeader)); - EXPECT_EQ(4U, sizeof(OatMethodOffsets)); - EXPECT_EQ(24U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(161 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), - sizeof(QuickEntryPoints)); -} - -TEST_F(OatTest, OatHeaderIsValid) { - InstructionSet insn_set = kX86; - std::string error_msg; - std::unique_ptr<const InstructionSetFeatures> insn_features( - InstructionSetFeatures::FromVariant(insn_set, "default", &error_msg)); - ASSERT_TRUE(insn_features.get() != nullptr) << error_msg; - std::unique_ptr<OatHeader> oat_header(OatHeader::Create(insn_set, - insn_features.get(), - 0u, - nullptr)); - ASSERT_NE(oat_header.get(), nullptr); - ASSERT_TRUE(oat_header->IsValid()); - - char* magic = const_cast<char*>(oat_header->GetMagic()); - strcpy(magic, ""); // bad magic - ASSERT_FALSE(oat_header->IsValid()); - strcpy(magic, "oat\n000"); // bad version - ASSERT_FALSE(oat_header->IsValid()); -} - -TEST_F(OatTest, EmptyTextSection) { - TimingLogger timings("OatTest::EmptyTextSection", false, false); - - // TODO: make selectable. - Compiler::Kind compiler_kind = Compiler::kQuick; - InstructionSet insn_set = kRuntimeISA; - if (insn_set == kArm) insn_set = kThumb2; - std::string error_msg; - std::vector<std::string> compiler_options; - compiler_options.push_back("--compiler-filter=extract"); - SetupCompiler(compiler_kind, insn_set, compiler_options, /*out*/ &error_msg); - - jobject class_loader; - { - ScopedObjectAccess soa(Thread::Current()); - class_loader = LoadDex("Main"); - } - ASSERT_TRUE(class_loader != nullptr); - std::vector<const DexFile*> dex_files = GetDexFiles(class_loader); - ASSERT_TRUE(!dex_files.empty()); - - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - for (const DexFile* dex_file : dex_files) { - ScopedObjectAccess soa(Thread::Current()); - class_linker->RegisterDexFile(*dex_file, - soa.Decode<mirror::ClassLoader>(class_loader).Ptr()); - } - compiler_driver_->SetDexFilesForOatFile(dex_files); - compiler_driver_->CompileAll(class_loader, dex_files, &timings); - - ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex"); - SafeMap<std::string, std::string> key_value_store; - key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); - bool success = WriteElf(tmp_vdex.GetFile(), tmp_oat.GetFile(), dex_files, key_value_store, false); - ASSERT_TRUE(success); - - std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp_oat.GetFilename(), - tmp_oat.GetFilename(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - nullptr, - &error_msg)); - ASSERT_TRUE(oat_file != nullptr); - EXPECT_LT(static_cast<size_t>(oat_file->Size()), - static_cast<size_t>(tmp_oat.GetFile()->GetLength())); -} - -static void MaybeModifyDexFileToFail(bool verify, std::unique_ptr<const DexFile>& data) { - // If in verify mode (= fail the verifier mode), make sure we fail early. We'll fail already - // because of the missing map, but that may lead to out of bounds reads. - if (verify) { - const_cast<DexFile::Header*>(&data->GetHeader())->checksum_++; - } -} - -void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { - TimingLogger timings("OatTest::DexFileInput", false, false); - - std::vector<const char*> input_filenames; - - ScratchFile dex_file1; - TestDexFileBuilder builder1; - builder1.AddField("Lsome.TestClass;", "int", "someField"); - builder1.AddMethod("Lsome.TestClass;", "()I", "foo"); - std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename()); - - MaybeModifyDexFileToFail(verify, dex_file1_data); - - bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(), - dex_file1_data->GetHeader().file_size_); - ASSERT_TRUE(success); - success = dex_file1.GetFile()->Flush() == 0; - ASSERT_TRUE(success); - input_filenames.push_back(dex_file1.GetFilename().c_str()); - - ScratchFile dex_file2; - TestDexFileBuilder builder2; - builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField"); - builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar"); - std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename()); - - MaybeModifyDexFileToFail(verify, dex_file2_data); - - success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(), - dex_file2_data->GetHeader().file_size_); - ASSERT_TRUE(success); - success = dex_file2.GetFile()->Flush() == 0; - ASSERT_TRUE(success); - input_filenames.push_back(dex_file2.GetFilename().c_str()); - - ScratchFile oat_file, vdex_file(oat_file, ".vdex"); - SafeMap<std::string, std::string> key_value_store; - key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); - std::unique_ptr<ProfileCompilationInfo> - profile_compilation_info(use_profile ? new ProfileCompilationInfo() : nullptr); - success = WriteElf(vdex_file.GetFile(), - oat_file.GetFile(), - input_filenames, - key_value_store, - verify, - profile_compilation_info.get()); - - // In verify mode, we expect failure. - if (verify) { - ASSERT_FALSE(success); - return; - } - - ASSERT_TRUE(success); - - std::string error_msg; - std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(), - oat_file.GetFilename(), - nullptr, - nullptr, - false, - low_4gb, - nullptr, - &error_msg)); - if (low_4gb) { - uintptr_t begin = reinterpret_cast<uintptr_t>(opened_oat_file->Begin()); - EXPECT_EQ(begin, static_cast<uint32_t>(begin)); - } - ASSERT_TRUE(opened_oat_file != nullptr); - ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); - std::unique_ptr<const DexFile> opened_dex_file1 = - opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg); - std::unique_ptr<const DexFile> opened_dex_file2 = - opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg); - - ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_); - ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(), - &opened_dex_file1->GetHeader(), - dex_file1_data->GetHeader().file_size_)); - ASSERT_EQ(dex_file1_data->GetLocation(), opened_dex_file1->GetLocation()); - - ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_); - ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(), - &opened_dex_file2->GetHeader(), - dex_file2_data->GetHeader().file_size_)); - ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation()); -} - -TEST_F(OatTest, DexFileInputCheckOutput) { - TestDexFileInput(/*verify*/false, /*low_4gb*/false, /*use_profile*/false); -} - -TEST_F(OatTest, DexFileInputCheckOutputLow4GB) { - TestDexFileInput(/*verify*/false, /*low_4gb*/true, /*use_profile*/false); -} - -TEST_F(OatTest, DexFileInputCheckVerifier) { - TestDexFileInput(/*verify*/true, /*low_4gb*/false, /*use_profile*/false); -} - -TEST_F(OatTest, DexFileFailsVerifierWithLayout) { - TestDexFileInput(/*verify*/true, /*low_4gb*/false, /*use_profile*/true); -} - -void OatTest::TestZipFileInput(bool verify) { - TimingLogger timings("OatTest::DexFileInput", false, false); - - ScratchFile zip_file; - ZipBuilder zip_builder(zip_file.GetFile()); - - ScratchFile dex_file1; - TestDexFileBuilder builder1; - builder1.AddField("Lsome.TestClass;", "long", "someField"); - builder1.AddMethod("Lsome.TestClass;", "()D", "foo"); - std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename()); - - MaybeModifyDexFileToFail(verify, dex_file1_data); - - bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(), - dex_file1_data->GetHeader().file_size_); - ASSERT_TRUE(success); - success = dex_file1.GetFile()->Flush() == 0; - ASSERT_TRUE(success); - success = zip_builder.AddFile("classes.dex", - &dex_file1_data->GetHeader(), - dex_file1_data->GetHeader().file_size_); - ASSERT_TRUE(success); - - ScratchFile dex_file2; - TestDexFileBuilder builder2; - builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField"); - builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar"); - std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename()); - - MaybeModifyDexFileToFail(verify, dex_file2_data); - - success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(), - dex_file2_data->GetHeader().file_size_); - ASSERT_TRUE(success); - success = dex_file2.GetFile()->Flush() == 0; - ASSERT_TRUE(success); - success = zip_builder.AddFile("classes2.dex", - &dex_file2_data->GetHeader(), - dex_file2_data->GetHeader().file_size_); - ASSERT_TRUE(success); - - success = zip_builder.Finish(); - ASSERT_TRUE(success) << strerror(errno); - - SafeMap<std::string, std::string> key_value_store; - key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); - { - // Test using the AddDexFileSource() interface with the zip file. - std::vector<const char*> input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4] - - ScratchFile oat_file, vdex_file(oat_file, ".vdex"); - success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), input_filenames, - key_value_store, verify, /*profile_compilation_info*/nullptr); - - if (verify) { - ASSERT_FALSE(success); - } else { - ASSERT_TRUE(success); - - std::string error_msg; - std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(), - oat_file.GetFilename(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - nullptr, - &error_msg)); - ASSERT_TRUE(opened_oat_file != nullptr); - ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); - std::unique_ptr<const DexFile> opened_dex_file1 = - opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg); - std::unique_ptr<const DexFile> opened_dex_file2 = - opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg); - - ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_); - ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(), - &opened_dex_file1->GetHeader(), - dex_file1_data->GetHeader().file_size_)); - ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()), - opened_dex_file1->GetLocation()); - - ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_); - ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(), - &opened_dex_file2->GetHeader(), - dex_file2_data->GetHeader().file_size_)); - ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()), - opened_dex_file2->GetLocation()); - } - } - - { - // Test using the AddZipDexFileSource() interface with the zip file handle. - File zip_fd(dup(zip_file.GetFd()), /* check_usage */ false); - ASSERT_NE(-1, zip_fd.Fd()); - - ScratchFile oat_file, vdex_file(oat_file, ".vdex"); - success = WriteElf(vdex_file.GetFile(), - oat_file.GetFile(), - std::move(zip_fd), - zip_file.GetFilename().c_str(), - key_value_store, - verify); - if (verify) { - ASSERT_FALSE(success); - } else { - ASSERT_TRUE(success); - - std::string error_msg; - std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(), - oat_file.GetFilename(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - nullptr, - &error_msg)); - ASSERT_TRUE(opened_oat_file != nullptr); - ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); - std::unique_ptr<const DexFile> opened_dex_file1 = - opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg); - std::unique_ptr<const DexFile> opened_dex_file2 = - opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg); - - ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_); - ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(), - &opened_dex_file1->GetHeader(), - dex_file1_data->GetHeader().file_size_)); - ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()), - opened_dex_file1->GetLocation()); - - ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_); - ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(), - &opened_dex_file2->GetHeader(), - dex_file2_data->GetHeader().file_size_)); - ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()), - opened_dex_file2->GetLocation()); - } - } -} - -TEST_F(OatTest, ZipFileInputCheckOutput) { - TestZipFileInput(false); -} - -TEST_F(OatTest, ZipFileInputCheckVerifier) { - TestZipFileInput(true); -} - -void OatTest::TestZipFileInputWithEmptyDex() { - ScratchFile zip_file; - ZipBuilder zip_builder(zip_file.GetFile()); - bool success = zip_builder.AddFile("classes.dex", nullptr, 0); - ASSERT_TRUE(success); - success = zip_builder.Finish(); - ASSERT_TRUE(success) << strerror(errno); - - SafeMap<std::string, std::string> key_value_store; - key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); - std::vector<const char*> input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4] - ScratchFile oat_file, vdex_file(oat_file, ".vdex"); - std::unique_ptr<ProfileCompilationInfo> profile_compilation_info(new ProfileCompilationInfo()); - success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), input_filenames, - key_value_store, /*verify*/false, profile_compilation_info.get()); - ASSERT_FALSE(success); -} - -TEST_F(OatTest, ZipFileInputWithEmptyDex) { - TestZipFileInputWithEmptyDex(); -} - -TEST_F(OatTest, UpdateChecksum) { - InstructionSet insn_set = kX86; - std::string error_msg; - std::unique_ptr<const InstructionSetFeatures> insn_features( - InstructionSetFeatures::FromVariant(insn_set, "default", &error_msg)); - ASSERT_TRUE(insn_features.get() != nullptr) << error_msg; - std::unique_ptr<OatHeader> oat_header(OatHeader::Create(insn_set, - insn_features.get(), - 0u, - nullptr)); - // The starting adler32 value is 1. - EXPECT_EQ(1U, oat_header->GetChecksum()); - - oat_header->UpdateChecksum(OatHeader::kOatMagic, sizeof(OatHeader::kOatMagic)); - EXPECT_EQ(64291151U, oat_header->GetChecksum()); - - // Make sure that null data does not reset the checksum. - oat_header->UpdateChecksum(nullptr, 0); - EXPECT_EQ(64291151U, oat_header->GetChecksum()); - - oat_header->UpdateChecksum(OatHeader::kOatMagic, sizeof(OatHeader::kOatMagic)); - EXPECT_EQ(216138397U, oat_header->GetChecksum()); -} - -} // namespace art diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc deleted file mode 100644 index ce1b755a9d..0000000000 --- a/compiler/oat_writer.cc +++ /dev/null @@ -1,3632 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "oat_writer.h" - -#include <unistd.h> -#include <zlib.h> - -#include "arch/arm64/instruction_set_features_arm64.h" -#include "art_method-inl.h" -#include "base/allocator.h" -#include "base/bit_vector-inl.h" -#include "base/enums.h" -#include "base/file_magic.h" -#include "base/stl_util.h" -#include "base/unix_file/fd_file.h" -#include "class_linker.h" -#include "class_table-inl.h" -#include "compiled_method.h" -#include "debug/method_debug_info.h" -#include "dex/verification_results.h" -#include "dex_file-inl.h" -#include "dex_file_types.h" -#include "dexlayout.h" -#include "driver/compiler_driver-inl.h" -#include "driver/compiler_options.h" -#include "gc/space/image_space.h" -#include "gc/space/space.h" -#include "handle_scope-inl.h" -#include "image_writer.h" -#include "linker/buffered_output_stream.h" -#include "linker/file_output_stream.h" -#include "linker/method_bss_mapping_encoder.h" -#include "linker/multi_oat_relative_patcher.h" -#include "linker/output_stream.h" -#include "mirror/array.h" -#include "mirror/class_loader.h" -#include "mirror/dex_cache-inl.h" -#include "mirror/object-inl.h" -#include "oat_quick_method_header.h" -#include "os.h" -#include "safe_map.h" -#include "scoped_thread_state_change-inl.h" -#include "type_lookup_table.h" -#include "utils/dex_cache_arrays_layout-inl.h" -#include "vdex_file.h" -#include "verifier/verifier_deps.h" -#include "zip_archive.h" - -namespace art { - -namespace { // anonymous namespace - -// If we write dex layout info in the oat file. -static constexpr bool kWriteDexLayoutInfo = true; - -typedef DexFile::Header __attribute__((aligned(1))) UnalignedDexFileHeader; - -const UnalignedDexFileHeader* AsUnalignedDexFileHeader(const uint8_t* raw_data) { - return reinterpret_cast<const UnalignedDexFileHeader*>(raw_data); -} - -class ChecksumUpdatingOutputStream : public OutputStream { - public: - ChecksumUpdatingOutputStream(OutputStream* out, OatHeader* oat_header) - : OutputStream(out->GetLocation()), out_(out), oat_header_(oat_header) { } - - bool WriteFully(const void* buffer, size_t byte_count) OVERRIDE { - oat_header_->UpdateChecksum(buffer, byte_count); - return out_->WriteFully(buffer, byte_count); - } - - off_t Seek(off_t offset, Whence whence) OVERRIDE { - return out_->Seek(offset, whence); - } - - bool Flush() OVERRIDE { - return out_->Flush(); - } - - private: - OutputStream* const out_; - OatHeader* const oat_header_; -}; - -inline uint32_t CodeAlignmentSize(uint32_t header_offset, const CompiledMethod& compiled_method) { - // We want to align the code rather than the preheader. - uint32_t unaligned_code_offset = header_offset + sizeof(OatQuickMethodHeader); - uint32_t aligned_code_offset = compiled_method.AlignCode(unaligned_code_offset); - return aligned_code_offset - unaligned_code_offset; -} - -} // anonymous namespace - -// Defines the location of the raw dex file to write. -class OatWriter::DexFileSource { - public: - enum Type { - kNone, - kZipEntry, - kRawFile, - kRawData, - }; - - explicit DexFileSource(ZipEntry* zip_entry) - : type_(kZipEntry), source_(zip_entry) { - DCHECK(source_ != nullptr); - } - - explicit DexFileSource(File* raw_file) - : type_(kRawFile), source_(raw_file) { - DCHECK(source_ != nullptr); - } - - explicit DexFileSource(const uint8_t* dex_file) - : type_(kRawData), source_(dex_file) { - DCHECK(source_ != nullptr); - } - - Type GetType() const { return type_; } - bool IsZipEntry() const { return type_ == kZipEntry; } - bool IsRawFile() const { return type_ == kRawFile; } - bool IsRawData() const { return type_ == kRawData; } - - ZipEntry* GetZipEntry() const { - DCHECK(IsZipEntry()); - DCHECK(source_ != nullptr); - return static_cast<ZipEntry*>(const_cast<void*>(source_)); - } - - File* GetRawFile() const { - DCHECK(IsRawFile()); - DCHECK(source_ != nullptr); - return static_cast<File*>(const_cast<void*>(source_)); - } - - const uint8_t* GetRawData() const { - DCHECK(IsRawData()); - DCHECK(source_ != nullptr); - return static_cast<const uint8_t*>(source_); - } - - void Clear() { - type_ = kNone; - source_ = nullptr; - } - - private: - Type type_; - const void* source_; -}; - -// OatClassHeader is the header only part of the oat class that is required even when compilation -// is not enabled. -class OatWriter::OatClassHeader { - public: - OatClassHeader(uint32_t offset, - uint32_t num_non_null_compiled_methods, - uint32_t num_methods, - mirror::Class::Status status) - : status_(status), - offset_(offset) { - // We just arbitrarily say that 0 methods means kOatClassNoneCompiled and that we won't use - // kOatClassAllCompiled unless there is at least one compiled method. This means in an - // interpreter only system, we can assert that all classes are kOatClassNoneCompiled. - if (num_non_null_compiled_methods == 0) { - type_ = kOatClassNoneCompiled; - } else if (num_non_null_compiled_methods == num_methods) { - type_ = kOatClassAllCompiled; - } else { - type_ = kOatClassSomeCompiled; - } - } - - bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const; - - static size_t SizeOf() { - return sizeof(status_) + sizeof(type_); - } - - // Data to write. - static_assert(mirror::Class::Status::kStatusMax < (1 << 16), "class status won't fit in 16bits"); - int16_t status_; - - static_assert(OatClassType::kOatClassMax < (1 << 16), "oat_class type won't fit in 16bits"); - uint16_t type_; - - // Offset of start of OatClass from beginning of OatHeader. It is - // used to validate file position when writing. - uint32_t offset_; -}; - -// The actual oat class body contains the information about compiled methods. It is only required -// for compiler filters that have any compilation. -class OatWriter::OatClass { - public: - OatClass(const dchecked_vector<CompiledMethod*>& compiled_methods, - uint32_t compiled_methods_with_code, - uint16_t oat_class_type); - OatClass(OatClass&& src) = default; - size_t SizeOf() const; - bool Write(OatWriter* oat_writer, OutputStream* out) const; - - CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const { - return compiled_methods_[class_def_method_index]; - } - - // CompiledMethods for each class_def_method_index, or null if no method is available. - dchecked_vector<CompiledMethod*> compiled_methods_; - - // Offset from OatClass::offset_ to the OatMethodOffsets for the - // class_def_method_index. If 0, it means the corresponding - // CompiledMethod entry in OatClass::compiled_methods_ should be - // null and that the OatClass::type_ should be kOatClassBitmap. - dchecked_vector<uint32_t> oat_method_offsets_offsets_from_oat_class_; - - // Data to write. - uint32_t method_bitmap_size_; - - // bit vector indexed by ClassDef method index. When - // OatClassType::type_ is kOatClassBitmap, a set bit indicates the - // method has an OatMethodOffsets in methods_offsets_, otherwise - // the entry was ommited to save space. If OatClassType::type_ is - // not is kOatClassBitmap, the bitmap will be null. - std::unique_ptr<BitVector> method_bitmap_; - - // OatMethodOffsets and OatMethodHeaders for each CompiledMethod - // present in the OatClass. Note that some may be missing if - // OatClass::compiled_methods_ contains null values (and - // oat_method_offsets_offsets_from_oat_class_ should contain 0 - // values in this case). - dchecked_vector<OatMethodOffsets> method_offsets_; - dchecked_vector<OatQuickMethodHeader> method_headers_; - - private: - size_t GetMethodOffsetsRawSize() const { - return method_offsets_.size() * sizeof(method_offsets_[0]); - } - - DISALLOW_COPY_AND_ASSIGN(OatClass); -}; - -class OatWriter::OatDexFile { - public: - OatDexFile(const char* dex_file_location, - DexFileSource source, - CreateTypeLookupTable create_type_lookup_table); - OatDexFile(OatDexFile&& src) = default; - - const char* GetLocation() const { - return dex_file_location_data_; - } - - size_t SizeOf() const; - bool Write(OatWriter* oat_writer, OutputStream* out) const; - bool WriteClassOffsets(OatWriter* oat_writer, OutputStream* out); - - size_t GetClassOffsetsRawSize() const { - return class_offsets_.size() * sizeof(class_offsets_[0]); - } - - // The source of the dex file. - DexFileSource source_; - - // Whether to create the type lookup table. - CreateTypeLookupTable create_type_lookup_table_; - - // Dex file size. Initialized when writing the dex file. - size_t dex_file_size_; - - // Offset of start of OatDexFile from beginning of OatHeader. It is - // used to validate file position when writing. - size_t offset_; - - // Data to write. - uint32_t dex_file_location_size_; - const char* dex_file_location_data_; - uint32_t dex_file_location_checksum_; - uint32_t dex_file_offset_; - uint32_t class_offsets_offset_; - uint32_t lookup_table_offset_; - uint32_t method_bss_mapping_offset_; - uint32_t dex_sections_layout_offset_; - - // Data to write to a separate section. - dchecked_vector<uint32_t> class_offsets_; - - // Dex section layout info to serialize. - DexLayoutSections dex_sections_layout_; - - private: - DISALLOW_COPY_AND_ASSIGN(OatDexFile); -}; - -#define DCHECK_OFFSET() \ - 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)) \ - << "file_offset=" << file_offset << " offset_=" << offset_ - -OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings, ProfileCompilationInfo* info) - : write_state_(WriteState::kAddingDexFileSources), - timings_(timings), - raw_dex_files_(), - zip_archives_(), - zipped_dex_files_(), - zipped_dex_file_locations_(), - compiler_driver_(nullptr), - image_writer_(nullptr), - compiling_boot_image_(compiling_boot_image), - dex_files_(nullptr), - vdex_size_(0u), - vdex_dex_files_offset_(0u), - vdex_verifier_deps_offset_(0u), - vdex_quickening_info_offset_(0u), - oat_size_(0u), - bss_start_(0u), - bss_size_(0u), - bss_methods_offset_(0u), - bss_roots_offset_(0u), - bss_method_entry_references_(), - bss_method_entries_(), - bss_type_entries_(), - bss_string_entries_(), - map_boot_image_tables_to_bss_(false), - oat_data_offset_(0u), - oat_header_(nullptr), - size_vdex_header_(0), - size_vdex_checksums_(0), - size_dex_file_alignment_(0), - size_executable_offset_alignment_(0), - size_oat_header_(0), - size_oat_header_key_value_store_(0), - size_dex_file_(0), - size_verifier_deps_(0), - size_verifier_deps_alignment_(0), - size_quickening_info_(0), - size_quickening_info_alignment_(0), - size_interpreter_to_interpreter_bridge_(0), - size_interpreter_to_compiled_code_bridge_(0), - size_jni_dlsym_lookup_(0), - size_quick_generic_jni_trampoline_(0), - size_quick_imt_conflict_trampoline_(0), - size_quick_resolution_trampoline_(0), - size_quick_to_interpreter_bridge_(0), - size_trampoline_alignment_(0), - size_method_header_(0), - size_code_(0), - size_code_alignment_(0), - size_relative_call_thunks_(0), - size_misc_thunks_(0), - size_vmap_table_(0), - size_method_info_(0), - size_oat_dex_file_location_size_(0), - size_oat_dex_file_location_data_(0), - size_oat_dex_file_location_checksum_(0), - size_oat_dex_file_offset_(0), - size_oat_dex_file_class_offsets_offset_(0), - size_oat_dex_file_lookup_table_offset_(0), - size_oat_dex_file_dex_layout_sections_offset_(0), - size_oat_dex_file_dex_layout_sections_(0), - size_oat_dex_file_dex_layout_sections_alignment_(0), - size_oat_dex_file_method_bss_mapping_offset_(0), - size_oat_lookup_table_alignment_(0), - size_oat_lookup_table_(0), - size_oat_class_offsets_alignment_(0), - size_oat_class_offsets_(0), - size_oat_class_type_(0), - size_oat_class_status_(0), - size_oat_class_method_bitmaps_(0), - size_oat_class_method_offsets_(0), - size_method_bss_mappings_(0u), - relative_patcher_(nullptr), - absolute_patch_locations_(), - profile_compilation_info_(info) { -} - -bool OatWriter::AddDexFileSource(const char* filename, - const char* location, - CreateTypeLookupTable create_type_lookup_table) { - DCHECK(write_state_ == WriteState::kAddingDexFileSources); - uint32_t magic; - std::string error_msg; - File fd = OpenAndReadMagic(filename, &magic, &error_msg); - if (fd.Fd() == -1) { - PLOG(ERROR) << "Failed to read magic number from dex file: '" << filename << "'"; - return false; - } else if (IsDexMagic(magic)) { - // The file is open for reading, not writing, so it's OK to let the File destructor - // close it without checking for explicit Close(), so pass checkUsage = false. - raw_dex_files_.emplace_back(new File(fd.Release(), location, /* checkUsage */ false)); - oat_dex_files_.emplace_back(location, - DexFileSource(raw_dex_files_.back().get()), - create_type_lookup_table); - } else if (IsZipMagic(magic)) { - if (!AddZippedDexFilesSource(std::move(fd), location, create_type_lookup_table)) { - return false; - } - } else { - LOG(ERROR) << "Expected valid zip or dex file: '" << filename << "'"; - return false; - } - return true; -} - -// Add dex file source(s) from a zip file specified by a file handle. -bool OatWriter::AddZippedDexFilesSource(File&& zip_fd, - const char* location, - CreateTypeLookupTable create_type_lookup_table) { - DCHECK(write_state_ == WriteState::kAddingDexFileSources); - std::string error_msg; - zip_archives_.emplace_back(ZipArchive::OpenFromFd(zip_fd.Release(), location, &error_msg)); - ZipArchive* zip_archive = zip_archives_.back().get(); - if (zip_archive == nullptr) { - LOG(ERROR) << "Failed to open zip from file descriptor for '" << location << "': " - << error_msg; - return false; - } - for (size_t i = 0; ; ++i) { - std::string entry_name = DexFile::GetMultiDexClassesDexName(i); - std::unique_ptr<ZipEntry> entry(zip_archive->Find(entry_name.c_str(), &error_msg)); - if (entry == nullptr) { - break; - } - zipped_dex_files_.push_back(std::move(entry)); - zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location)); - const char* full_location = zipped_dex_file_locations_.back().c_str(); - oat_dex_files_.emplace_back(full_location, - DexFileSource(zipped_dex_files_.back().get()), - create_type_lookup_table); - } - if (zipped_dex_file_locations_.empty()) { - LOG(ERROR) << "No dex files in zip file '" << location << "': " << error_msg; - return false; - } - return true; -} - -// Add dex file source(s) from a vdex file specified by a file handle. -bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file, - const char* location, - CreateTypeLookupTable create_type_lookup_table) { - DCHECK(write_state_ == WriteState::kAddingDexFileSources); - const uint8_t* current_dex_data = nullptr; - for (size_t i = 0; i < vdex_file.GetHeader().GetNumberOfDexFiles(); ++i) { - current_dex_data = vdex_file.GetNextDexFileData(current_dex_data); - if (current_dex_data == nullptr) { - LOG(ERROR) << "Unexpected number of dex files in vdex " << location; - return false; - } - if (!DexFile::IsMagicValid(current_dex_data)) { - LOG(ERROR) << "Invalid magic in vdex file created from " << location; - return false; - } - // We used `zipped_dex_file_locations_` to keep the strings in memory. - zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location)); - const char* full_location = zipped_dex_file_locations_.back().c_str(); - oat_dex_files_.emplace_back(full_location, - DexFileSource(current_dex_data), - create_type_lookup_table); - oat_dex_files_.back().dex_file_location_checksum_ = vdex_file.GetLocationChecksum(i); - } - - if (vdex_file.GetNextDexFileData(current_dex_data) != nullptr) { - LOG(ERROR) << "Unexpected number of dex files in vdex " << location; - return false; - } - - if (oat_dex_files_.empty()) { - LOG(ERROR) << "No dex files in vdex file created from " << location; - return false; - } - return true; -} - -// Add dex file source from raw memory. -bool OatWriter::AddRawDexFileSource(const ArrayRef<const uint8_t>& data, - const char* location, - uint32_t location_checksum, - CreateTypeLookupTable create_type_lookup_table) { - DCHECK(write_state_ == WriteState::kAddingDexFileSources); - if (data.size() < sizeof(DexFile::Header)) { - LOG(ERROR) << "Provided data is shorter than dex file header. size: " - << data.size() << " File: " << location; - return false; - } - if (!ValidateDexFileHeader(data.data(), location)) { - return false; - } - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(data.data()); - if (data.size() < header->file_size_) { - LOG(ERROR) << "Truncated dex file data. Data size: " << data.size() - << " file size from header: " << header->file_size_ << " File: " << location; - return false; - } - - oat_dex_files_.emplace_back(location, DexFileSource(data.data()), create_type_lookup_table); - oat_dex_files_.back().dex_file_location_checksum_ = location_checksum; - return true; -} - -dchecked_vector<std::string> OatWriter::GetSourceLocations() const { - dchecked_vector<std::string> locations; - locations.reserve(oat_dex_files_.size()); - for (const OatDexFile& oat_dex_file : oat_dex_files_) { - locations.push_back(oat_dex_file.GetLocation()); - } - return locations; -} - -bool OatWriter::MayHaveCompiledMethods() const { - return CompilerFilter::IsAnyCompilationEnabled( - GetCompilerDriver()->GetCompilerOptions().GetCompilerFilter()); -} - -bool OatWriter::WriteAndOpenDexFiles( - File* vdex_file, - OutputStream* oat_rodata, - InstructionSet instruction_set, - const InstructionSetFeatures* instruction_set_features, - SafeMap<std::string, std::string>* key_value_store, - bool verify, - bool update_input_vdex, - /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, - /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { - CHECK(write_state_ == WriteState::kAddingDexFileSources); - - // Record the ELF rodata section offset, i.e. the beginning of the OAT data. - if (!RecordOatDataOffset(oat_rodata)) { - return false; - } - - std::unique_ptr<MemMap> dex_files_map; - std::vector<std::unique_ptr<const DexFile>> dex_files; - - // Initialize VDEX and OAT headers. - if (kIsVdexEnabled) { - // Reserve space for Vdex header and checksums. - vdex_size_ = sizeof(VdexFile::Header) + oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum); - } - oat_size_ = InitOatHeader(instruction_set, - instruction_set_features, - dchecked_integral_cast<uint32_t>(oat_dex_files_.size()), - key_value_store); - - ChecksumUpdatingOutputStream checksum_updating_rodata(oat_rodata, oat_header_.get()); - - if (kIsVdexEnabled) { - std::unique_ptr<BufferedOutputStream> vdex_out = - std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(vdex_file)); - // Write DEX files into VDEX, mmap and open them. - if (!WriteDexFiles(vdex_out.get(), vdex_file, update_input_vdex) || - !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) { - return false; - } - } else { - DCHECK(!update_input_vdex); - // Write DEX files into OAT, mmap and open them. - if (!WriteDexFiles(oat_rodata, vdex_file, update_input_vdex) || - !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) { - return false; - } - - // Do a bulk checksum update for Dex[]. Doing it piece by piece would be - // difficult because we're not using the OutputStream directly. - if (!oat_dex_files_.empty()) { - size_t size = oat_size_ - oat_dex_files_[0].dex_file_offset_; - oat_header_->UpdateChecksum(dex_files_map->Begin(), size); - } - } - - // Write type lookup tables into the oat file. - if (!WriteTypeLookupTables(&checksum_updating_rodata, dex_files)) { - return false; - } - - // Write dex layout sections into the oat file. - if (!WriteDexLayoutSections(&checksum_updating_rodata, dex_files)) { - return false; - } - - *opened_dex_files_map = std::move(dex_files_map); - *opened_dex_files = std::move(dex_files); - write_state_ = WriteState::kPrepareLayout; - return true; -} - -void OatWriter::PrepareLayout(linker::MultiOatRelativePatcher* relative_patcher) { - CHECK(write_state_ == WriteState::kPrepareLayout); - - relative_patcher_ = relative_patcher; - SetMultiOatRelativePatcherAdjustment(); - - if (compiling_boot_image_) { - CHECK(image_writer_ != nullptr); - } - InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); - CHECK_EQ(instruction_set, oat_header_->GetInstructionSet()); - - { - TimingLogger::ScopedTiming split("InitBssLayout", timings_); - InitBssLayout(instruction_set); - } - - uint32_t offset = oat_size_; - { - TimingLogger::ScopedTiming split("InitClassOffsets", timings_); - offset = InitClassOffsets(offset); - } - { - TimingLogger::ScopedTiming split("InitOatClasses", timings_); - offset = InitOatClasses(offset); - } - { - TimingLogger::ScopedTiming split("InitMethodBssMappings", timings_); - offset = InitMethodBssMappings(offset); - } - { - TimingLogger::ScopedTiming split("InitOatMaps", timings_); - offset = InitOatMaps(offset); - } - { - TimingLogger::ScopedTiming split("InitOatDexFiles", timings_); - oat_header_->SetOatDexFilesOffset(offset); - offset = InitOatDexFiles(offset); - } - { - TimingLogger::ScopedTiming split("InitOatCode", timings_); - offset = InitOatCode(offset); - } - { - TimingLogger::ScopedTiming split("InitOatCodeDexFiles", timings_); - offset = InitOatCodeDexFiles(offset); - } - oat_size_ = offset; - bss_start_ = (bss_size_ != 0u) ? RoundUp(oat_size_, kPageSize) : 0u; - - CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); - if (compiling_boot_image_) { - CHECK_EQ(image_writer_ != nullptr, - oat_header_->GetStoreValueByKey(OatHeader::kImageLocationKey) == nullptr); - } - - write_state_ = WriteState::kWriteRoData; -} - -OatWriter::~OatWriter() { -} - -class OatWriter::DexMethodVisitor { - public: - DexMethodVisitor(OatWriter* writer, size_t offset) - : writer_(writer), - offset_(offset), - dex_file_(nullptr), - class_def_index_(dex::kDexNoIndex) {} - - virtual bool StartClass(const DexFile* dex_file, size_t class_def_index) { - DCHECK(dex_file_ == nullptr); - DCHECK_EQ(class_def_index_, dex::kDexNoIndex); - dex_file_ = dex_file; - class_def_index_ = class_def_index; - return true; - } - - virtual bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) = 0; - - virtual bool EndClass() { - if (kIsDebugBuild) { - dex_file_ = nullptr; - class_def_index_ = dex::kDexNoIndex; - } - return true; - } - - size_t GetOffset() const { - return offset_; - } - - protected: - virtual ~DexMethodVisitor() { } - - OatWriter* const writer_; - - // The offset is usually advanced for each visited method by the derived class. - size_t offset_; - - // The dex file and class def index are set in StartClass(). - const DexFile* dex_file_; - size_t class_def_index_; -}; - -class OatWriter::OatDexMethodVisitor : public DexMethodVisitor { - public: - OatDexMethodVisitor(OatWriter* writer, size_t offset) - : DexMethodVisitor(writer, offset), - oat_class_index_(0u), - method_offsets_index_(0u) {} - - bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE { - DexMethodVisitor::StartClass(dex_file, class_def_index); - if (kIsDebugBuild && writer_->MayHaveCompiledMethods()) { - // There are no oat classes if there aren't any compiled methods. - CHECK_LT(oat_class_index_, writer_->oat_classes_.size()); - } - method_offsets_index_ = 0u; - return true; - } - - bool EndClass() OVERRIDE { - ++oat_class_index_; - return DexMethodVisitor::EndClass(); - } - - protected: - size_t oat_class_index_; - size_t method_offsets_index_; -}; - -static bool HasCompiledCode(const CompiledMethod* method) { - // The dextodexcompiler puts the quickening info table into the CompiledMethod - // for simplicity. For such methods, we will emit an OatQuickMethodHeader - // only when vdex is disabled. - return method != nullptr && (!method->GetQuickCode().empty() || !kIsVdexEnabled); -} - -static bool HasQuickeningInfo(const CompiledMethod* method) { - return method != nullptr && method->GetQuickCode().empty() && !method->GetVmapTable().empty(); -} - -class OatWriter::InitBssLayoutMethodVisitor : public DexMethodVisitor { - public: - explicit InitBssLayoutMethodVisitor(OatWriter* writer) - : DexMethodVisitor(writer, /* offset */ 0u) {} - - bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED, - const ClassDataItemIterator& it) OVERRIDE { - // Look for patches with .bss references and prepare maps with placeholders for their offsets. - CompiledMethod* compiled_method = writer_->compiler_driver_->GetCompiledMethod( - MethodReference(dex_file_, it.GetMemberIndex())); - if (HasCompiledCode(compiled_method)) { - for (const LinkerPatch& patch : compiled_method->GetPatches()) { - if (patch.GetType() == LinkerPatch::Type::kMethodBssEntry) { - MethodReference target_method = patch.TargetMethod(); - auto refs_it = writer_->bss_method_entry_references_.find(target_method.dex_file); - if (refs_it == writer_->bss_method_entry_references_.end()) { - refs_it = writer_->bss_method_entry_references_.Put( - target_method.dex_file, - BitVector(target_method.dex_file->NumMethodIds(), - /* expandable */ false, - Allocator::GetMallocAllocator())); - refs_it->second.ClearAllBits(); - } - refs_it->second.SetBit(target_method.index); - writer_->bss_method_entries_.Overwrite(target_method, /* placeholder */ 0u); - } else if (patch.GetType() == LinkerPatch::Type::kTypeBssEntry) { - TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex()); - writer_->bss_type_entries_.Overwrite(ref, /* placeholder */ 0u); - } else if (patch.GetType() == LinkerPatch::Type::kStringBssEntry) { - StringReference ref(patch.TargetStringDexFile(), patch.TargetStringIndex()); - writer_->bss_string_entries_.Overwrite(ref, /* placeholder */ 0u); - } else if (patch.GetType() == LinkerPatch::Type::kStringInternTable || - patch.GetType() == LinkerPatch::Type::kTypeClassTable) { - writer_->map_boot_image_tables_to_bss_ = true; - } - } - } else { - DCHECK(compiled_method == nullptr || compiled_method->GetPatches().empty()); - } - return true; - } -}; - -class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor { - public: - InitOatClassesMethodVisitor(OatWriter* writer, size_t offset) - : DexMethodVisitor(writer, offset), - compiled_methods_(), - compiled_methods_with_code_(0u) { - size_t num_classes = 0u; - for (const OatDexFile& oat_dex_file : writer_->oat_dex_files_) { - num_classes += oat_dex_file.class_offsets_.size(); - } - // If we aren't compiling only reserve headers. - writer_->oat_class_headers_.reserve(num_classes); - if (writer->MayHaveCompiledMethods()) { - writer->oat_classes_.reserve(num_classes); - } - compiled_methods_.reserve(256u); - // If there are any classes, the class offsets allocation aligns the offset. - DCHECK(num_classes == 0u || IsAligned<4u>(offset)); - } - - bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE { - DexMethodVisitor::StartClass(dex_file, class_def_index); - compiled_methods_.clear(); - compiled_methods_with_code_ = 0u; - return true; - } - - bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED, - const ClassDataItemIterator& it) OVERRIDE { - // Fill in the compiled_methods_ array for methods that have a - // CompiledMethod. We track the number of non-null entries in - // compiled_methods_with_code_ since we only want to allocate - // OatMethodOffsets for the compiled methods. - uint32_t method_idx = it.GetMemberIndex(); - CompiledMethod* compiled_method = - writer_->compiler_driver_->GetCompiledMethod(MethodReference(dex_file_, method_idx)); - compiled_methods_.push_back(compiled_method); - if (HasCompiledCode(compiled_method)) { - ++compiled_methods_with_code_; - } - return true; - } - - bool EndClass() OVERRIDE { - ClassReference class_ref(dex_file_, class_def_index_); - mirror::Class::Status status; - bool found = writer_->compiler_driver_->GetCompiledClass(class_ref, &status); - if (!found) { - VerificationResults* results = writer_->compiler_driver_->GetVerificationResults(); - if (results != nullptr && results->IsClassRejected(class_ref)) { - // The oat class status is used only for verification of resolved classes, - // so use kStatusErrorResolved whether the class was resolved or unresolved - // during compile-time verification. - status = mirror::Class::kStatusErrorResolved; - } else { - status = mirror::Class::kStatusNotReady; - } - } - - writer_->oat_class_headers_.emplace_back(offset_, - compiled_methods_with_code_, - compiled_methods_.size(), - status); - OatClassHeader& header = writer_->oat_class_headers_.back(); - offset_ += header.SizeOf(); - if (writer_->MayHaveCompiledMethods()) { - writer_->oat_classes_.emplace_back(compiled_methods_, - compiled_methods_with_code_, - header.type_); - offset_ += writer_->oat_classes_.back().SizeOf(); - } - return DexMethodVisitor::EndClass(); - } - - private: - dchecked_vector<CompiledMethod*> compiled_methods_; - size_t compiled_methods_with_code_; -}; - -class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { - public: - InitCodeMethodVisitor(OatWriter* writer, size_t offset) - : InitCodeMethodVisitor(writer, offset, writer->GetCompilerDriver()->GetCompilerOptions()) {} - - bool EndClass() OVERRIDE { - OatDexMethodVisitor::EndClass(); - if (oat_class_index_ == writer_->oat_classes_.size()) { - offset_ = relative_patcher_->ReserveSpaceEnd(offset_); - if (generate_debug_info_) { - std::vector<debug::MethodDebugInfo> thunk_infos = - relative_patcher_->GenerateThunkDebugInfo(executable_offset_); - writer_->method_info_.insert(writer_->method_info_.end(), - std::make_move_iterator(thunk_infos.begin()), - std::make_move_iterator(thunk_infos.end())); - } - } - return true; - } - - bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) OVERRIDE - REQUIRES_SHARED(Locks::mutator_lock_) { - OatClass* oat_class = &writer_->oat_classes_[oat_class_index_]; - CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); - - if (HasCompiledCode(compiled_method)) { - // Derived from CompiledMethod. - uint32_t quick_code_offset = 0; - - ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode(); - uint32_t code_size = quick_code.size() * sizeof(uint8_t); - uint32_t thumb_offset = compiled_method->CodeDelta(); - - // Deduplicate code arrays if we are not producing debuggable code. - bool deduped = true; - MethodReference method_ref(dex_file_, it.GetMemberIndex()); - if (debuggable_) { - quick_code_offset = relative_patcher_->GetOffset(method_ref); - if (quick_code_offset != 0u) { - // Duplicate methods, we want the same code for both of them so that the oat writer puts - // the same code in both ArtMethods so that we do not get different oat code at runtime. - } else { - quick_code_offset = NewQuickCodeOffset(compiled_method, it, thumb_offset); - deduped = false; - } - } else { - quick_code_offset = dedupe_map_.GetOrCreate( - compiled_method, - [this, &deduped, compiled_method, &it, thumb_offset]() { - deduped = false; - return NewQuickCodeOffset(compiled_method, it, thumb_offset); - }); - } - - if (code_size != 0) { - if (relative_patcher_->GetOffset(method_ref) != 0u) { - // TODO: Should this be a hard failure? - LOG(WARNING) << "Multiple definitions of " - << method_ref.PrettyMethod() - << " offsets " << relative_patcher_->GetOffset(method_ref) - << " " << quick_code_offset; - } else { - relative_patcher_->SetOffset(method_ref, quick_code_offset); - } - } - - // Update quick method header. - DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size()); - OatQuickMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_]; - uint32_t vmap_table_offset = method_header->GetVmapTableOffset(); - uint32_t method_info_offset = method_header->GetMethodInfoOffset(); - // The code offset was 0 when the mapping/vmap table offset was set, so it's set - // to 0-offset and we need to adjust it by code_offset. - uint32_t code_offset = quick_code_offset - thumb_offset; - if (!compiled_method->GetQuickCode().empty()) { - // If the code is compiled, we write the offset of the stack map relative - // to the code, - if (vmap_table_offset != 0u) { - vmap_table_offset += code_offset; - DCHECK_LT(vmap_table_offset, code_offset); - } - if (method_info_offset != 0u) { - method_info_offset += code_offset; - DCHECK_LT(method_info_offset, code_offset); - } - } else { - CHECK(!kIsVdexEnabled); - // We write the offset of the quickening info relative to the code. - vmap_table_offset += code_offset; - DCHECK_LT(vmap_table_offset, code_offset); - } - uint32_t frame_size_in_bytes = compiled_method->GetFrameSizeInBytes(); - uint32_t core_spill_mask = compiled_method->GetCoreSpillMask(); - uint32_t fp_spill_mask = compiled_method->GetFpSpillMask(); - *method_header = OatQuickMethodHeader(vmap_table_offset, - method_info_offset, - frame_size_in_bytes, - core_spill_mask, - fp_spill_mask, - code_size); - - if (!deduped) { - // Update offsets. (Checksum is updated when writing.) - offset_ += sizeof(*method_header); // Method header is prepended before code. - offset_ += code_size; - // Record absolute patch locations. - if (!compiled_method->GetPatches().empty()) { - uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset(); - for (const LinkerPatch& patch : compiled_method->GetPatches()) { - if (!patch.IsPcRelative()) { - writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset()); - } - } - } - } - - // Exclude quickened dex methods (code_size == 0) since they have no native code. - if (generate_debug_info_ && code_size != 0) { - bool has_code_info = method_header->IsOptimized(); - // Record debug information for this function if we are doing that. - debug::MethodDebugInfo info = {}; - DCHECK(info.trampoline_name.empty()); - info.dex_file = dex_file_; - info.class_def_index = class_def_index_; - info.dex_method_index = it.GetMemberIndex(); - info.access_flags = it.GetMethodAccessFlags(); - info.code_item = it.GetMethodCodeItem(); - info.isa = compiled_method->GetInstructionSet(); - info.deduped = deduped; - info.is_native_debuggable = native_debuggable_; - info.is_optimized = method_header->IsOptimized(); - info.is_code_address_text_relative = true; - info.code_address = code_offset - executable_offset_; - info.code_size = code_size; - info.frame_size_in_bytes = compiled_method->GetFrameSizeInBytes(); - info.code_info = has_code_info ? compiled_method->GetVmapTable().data() : nullptr; - info.cfi = compiled_method->GetCFIInfo(); - writer_->method_info_.push_back(info); - } - - DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size()); - OatMethodOffsets* offsets = &oat_class->method_offsets_[method_offsets_index_]; - offsets->code_offset_ = quick_code_offset; - ++method_offsets_index_; - } - - return true; - } - - private: - InitCodeMethodVisitor(OatWriter* writer, size_t offset, const CompilerOptions& compiler_options) - : OatDexMethodVisitor(writer, offset), - relative_patcher_(writer->relative_patcher_), - executable_offset_(writer->oat_header_->GetExecutableOffset()), - debuggable_(compiler_options.GetDebuggable()), - native_debuggable_(compiler_options.GetNativeDebuggable()), - generate_debug_info_(compiler_options.GenerateAnyDebugInfo()) { - writer->absolute_patch_locations_.reserve( - writer->GetCompilerDriver()->GetNonRelativeLinkerPatchCount()); - } - - struct CodeOffsetsKeyComparator { - bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const { - // Code is deduplicated by CompilerDriver, compare only data pointers. - if (lhs->GetQuickCode().data() != rhs->GetQuickCode().data()) { - return lhs->GetQuickCode().data() < rhs->GetQuickCode().data(); - } - // If the code is the same, all other fields are likely to be the same as well. - if (UNLIKELY(lhs->GetVmapTable().data() != rhs->GetVmapTable().data())) { - return lhs->GetVmapTable().data() < rhs->GetVmapTable().data(); - } - if (UNLIKELY(lhs->GetMethodInfo().data() != rhs->GetMethodInfo().data())) { - return lhs->GetMethodInfo().data() < rhs->GetMethodInfo().data(); - } - if (UNLIKELY(lhs->GetPatches().data() != rhs->GetPatches().data())) { - return lhs->GetPatches().data() < rhs->GetPatches().data(); - } - return false; - } - }; - - uint32_t NewQuickCodeOffset(CompiledMethod* compiled_method, - const ClassDataItemIterator& it, - uint32_t thumb_offset) { - offset_ = relative_patcher_->ReserveSpace( - offset_, compiled_method, MethodReference(dex_file_, it.GetMemberIndex())); - offset_ += CodeAlignmentSize(offset_, *compiled_method); - DCHECK_ALIGNED_PARAM(offset_ + sizeof(OatQuickMethodHeader), - GetInstructionSetAlignment(compiled_method->GetInstructionSet())); - return offset_ + sizeof(OatQuickMethodHeader) + thumb_offset; - } - - // Deduplication is already done on a pointer basis by the compiler driver, - // so we can simply compare the pointers to find out if things are duplicated. - SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_; - - // Cache writer_'s members and compiler options. - linker::MultiOatRelativePatcher* relative_patcher_; - uint32_t executable_offset_; - const bool debuggable_; - const bool native_debuggable_; - const bool generate_debug_info_; -}; - -class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor { - public: - InitMapMethodVisitor(OatWriter* writer, size_t offset) - : OatDexMethodVisitor(writer, offset) {} - - bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it ATTRIBUTE_UNUSED) - OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { - OatClass* oat_class = &writer_->oat_classes_[oat_class_index_]; - CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); - - if (HasCompiledCode(compiled_method)) { - DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size()); - DCHECK_EQ(oat_class->method_headers_[method_offsets_index_].GetVmapTableOffset(), 0u); - - ArrayRef<const uint8_t> map = compiled_method->GetVmapTable(); - uint32_t map_size = map.size() * sizeof(map[0]); - if (map_size != 0u) { - size_t offset = dedupe_map_.GetOrCreate( - map.data(), - [this, map_size]() { - uint32_t new_offset = offset_; - offset_ += map_size; - return new_offset; - }); - // Code offset is not initialized yet, so set the map offset to 0u-offset. - DCHECK_EQ(oat_class->method_offsets_[method_offsets_index_].code_offset_, 0u); - oat_class->method_headers_[method_offsets_index_].SetVmapTableOffset(0u - offset); - } - ++method_offsets_index_; - } - - return true; - } - - private: - // Deduplication is already done on a pointer basis by the compiler driver, - // so we can simply compare the pointers to find out if things are duplicated. - SafeMap<const uint8_t*, uint32_t> dedupe_map_; -}; - -class OatWriter::InitMethodInfoVisitor : public OatDexMethodVisitor { - public: - InitMethodInfoVisitor(OatWriter* writer, size_t offset) : OatDexMethodVisitor(writer, offset) {} - - bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it ATTRIBUTE_UNUSED) - OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { - OatClass* oat_class = &writer_->oat_classes_[oat_class_index_]; - CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); - - if (HasCompiledCode(compiled_method)) { - DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size()); - DCHECK_EQ(oat_class->method_headers_[method_offsets_index_].GetMethodInfoOffset(), 0u); - ArrayRef<const uint8_t> map = compiled_method->GetMethodInfo(); - const uint32_t map_size = map.size() * sizeof(map[0]); - if (map_size != 0u) { - size_t offset = dedupe_map_.GetOrCreate( - map.data(), - [this, map_size]() { - uint32_t new_offset = offset_; - offset_ += map_size; - return new_offset; - }); - // Code offset is not initialized yet, so set the map offset to 0u-offset. - DCHECK_EQ(oat_class->method_offsets_[method_offsets_index_].code_offset_, 0u); - oat_class->method_headers_[method_offsets_index_].SetMethodInfoOffset(0u - offset); - } - ++method_offsets_index_; - } - - return true; - } - - private: - // Deduplication is already done on a pointer basis by the compiler driver, - // so we can simply compare the pointers to find out if things are duplicated. - SafeMap<const uint8_t*, uint32_t> dedupe_map_; -}; - -class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { - public: - InitImageMethodVisitor(OatWriter* writer, - size_t offset, - const std::vector<const DexFile*>* dex_files) - : OatDexMethodVisitor(writer, offset), - pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())), - class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr), - dex_files_(dex_files), - class_linker_(Runtime::Current()->GetClassLinker()) {} - - // Handle copied methods here. Copy pointer to quick code from - // an origin method to a copied method only if they are - // in the same oat file. If the origin and the copied methods are - // in different oat files don't touch the copied method. - // References to other oat files are not supported yet. - bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE - REQUIRES_SHARED(Locks::mutator_lock_) { - OatDexMethodVisitor::StartClass(dex_file, class_def_index); - // Skip classes that are not in the image. - if (!IsImageClass()) { - return true; - } - ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(Thread::Current(), *dex_file); - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - mirror::Class* klass = dex_cache->GetResolvedType(class_def.class_idx_); - if (klass != nullptr) { - for (ArtMethod& method : klass->GetCopiedMethods(pointer_size_)) { - // Find origin method. Declaring class and dex_method_idx - // in the copied method should be the same as in the origin - // method. - mirror::Class* declaring_class = method.GetDeclaringClass(); - ArtMethod* origin = declaring_class->FindClassMethod( - declaring_class->GetDexCache(), - method.GetDexMethodIndex(), - pointer_size_); - CHECK(origin != nullptr); - CHECK(!origin->IsDirect()); - CHECK(origin->GetDeclaringClass() == declaring_class); - if (IsInOatFile(&declaring_class->GetDexFile())) { - const void* code_ptr = - origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_); - if (code_ptr == nullptr) { - methods_to_process_.push_back(std::make_pair(&method, origin)); - } else { - method.SetEntryPointFromQuickCompiledCodePtrSize( - code_ptr, pointer_size_); - } - } - } - } - return true; - } - - bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) OVERRIDE - REQUIRES_SHARED(Locks::mutator_lock_) { - // Skip methods that are not in the image. - if (!IsImageClass()) { - return true; - } - - OatClass* oat_class = &writer_->oat_classes_[oat_class_index_]; - CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); - - OatMethodOffsets offsets(0u); - if (HasCompiledCode(compiled_method)) { - DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size()); - offsets = oat_class->method_offsets_[method_offsets_index_]; - ++method_offsets_index_; - } - - Thread* self = Thread::Current(); - ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(self, *dex_file_); - ArtMethod* method; - if (writer_->HasBootImage()) { - const InvokeType invoke_type = it.GetMethodInvokeType( - dex_file_->GetClassDef(class_def_index_)); - // Unchecked as we hold mutator_lock_ on entry. - ScopedObjectAccessUnchecked soa(self); - StackHandleScope<1> hs(self); - method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - *dex_file_, - it.GetMemberIndex(), - hs.NewHandle(dex_cache), - ScopedNullHandle<mirror::ClassLoader>(), - nullptr, - invoke_type); - if (method == nullptr) { - LOG(FATAL_WITHOUT_ABORT) << "Unexpected failure to resolve a method: " - << dex_file_->PrettyMethod(it.GetMemberIndex(), true); - self->AssertPendingException(); - mirror::Throwable* exc = self->GetException(); - std::string dump = exc->Dump(); - LOG(FATAL) << dump; - UNREACHABLE(); - } - } else { - // Should already have been resolved by the compiler. - // It may not be resolved if the class failed to verify, in this case, don't set the - // entrypoint. This is not fatal since we shall use a resolution method. - method = class_linker_->LookupResolvedMethod(it.GetMemberIndex(), dex_cache, class_loader_); - } - if (method != nullptr && - compiled_method != nullptr && - compiled_method->GetQuickCode().size() != 0) { - method->SetEntryPointFromQuickCompiledCodePtrSize( - reinterpret_cast<void*>(offsets.code_offset_), pointer_size_); - } - - return true; - } - - // Check whether current class is image class - bool IsImageClass() { - const DexFile::TypeId& type_id = - dex_file_->GetTypeId(dex_file_->GetClassDef(class_def_index_).class_idx_); - const char* class_descriptor = dex_file_->GetTypeDescriptor(type_id); - return writer_->GetCompilerDriver()->IsImageClass(class_descriptor); - } - - // Check whether specified dex file is in the compiled oat file. - bool IsInOatFile(const DexFile* dex_file) { - return ContainsElement(*dex_files_, dex_file); - } - - // Assign a pointer to quick code for copied methods - // not handled in the method StartClass - void Postprocess() { - for (std::pair<ArtMethod*, ArtMethod*>& p : methods_to_process_) { - ArtMethod* method = p.first; - ArtMethod* origin = p.second; - const void* code_ptr = - origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_); - if (code_ptr != nullptr) { - method->SetEntryPointFromQuickCompiledCodePtrSize(code_ptr, pointer_size_); - } - } - } - - private: - const PointerSize pointer_size_; - ObjPtr<mirror::ClassLoader> class_loader_; - const std::vector<const DexFile*>* dex_files_; - ClassLinker* const class_linker_; - std::vector<std::pair<ArtMethod*, ArtMethod*>> methods_to_process_; -}; - -class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { - public: - WriteCodeMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset, - size_t relative_offset) SHARED_LOCK_FUNCTION(Locks::mutator_lock_) - : OatDexMethodVisitor(writer, relative_offset), - pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())), - class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr), - out_(out), - file_offset_(file_offset), - soa_(Thread::Current()), - no_thread_suspension_("OatWriter patching"), - class_linker_(Runtime::Current()->GetClassLinker()), - dex_cache_(nullptr) { - patched_code_.reserve(16 * KB); - if (writer_->HasBootImage()) { - // If we're creating the image, the address space must be ready so that we can apply patches. - CHECK(writer_->image_writer_->IsImageAddressSpaceReady()); - } - } - - ~WriteCodeMethodVisitor() UNLOCK_FUNCTION(Locks::mutator_lock_) { - } - - bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE - REQUIRES_SHARED(Locks::mutator_lock_) { - OatDexMethodVisitor::StartClass(dex_file, class_def_index); - if (writer_->GetCompilerDriver()->GetCompilerOptions().IsAotCompilationEnabled()) { - // Only need to set the dex cache if we have compilation. Other modes might have unloaded it. - if (dex_cache_ == nullptr || dex_cache_->GetDexFile() != dex_file) { - dex_cache_ = class_linker_->FindDexCache(Thread::Current(), *dex_file); - DCHECK(dex_cache_ != nullptr); - } - } - return true; - } - - bool EndClass() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { - bool result = OatDexMethodVisitor::EndClass(); - if (oat_class_index_ == writer_->oat_classes_.size()) { - DCHECK(result); // OatDexMethodVisitor::EndClass() never fails. - offset_ = writer_->relative_patcher_->WriteThunks(out_, offset_); - if (UNLIKELY(offset_ == 0u)) { - PLOG(ERROR) << "Failed to write final relative call thunks"; - result = false; - } - } - return result; - } - - bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) OVERRIDE - REQUIRES_SHARED(Locks::mutator_lock_) { - OatClass* oat_class = &writer_->oat_classes_[oat_class_index_]; - const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); - - // No thread suspension since dex_cache_ that may get invalidated if that occurs. - ScopedAssertNoThreadSuspension tsc(__FUNCTION__); - if (HasCompiledCode(compiled_method)) { - size_t file_offset = file_offset_; - OutputStream* out = out_; - - ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode(); - uint32_t code_size = quick_code.size() * sizeof(uint8_t); - - // Deduplicate code arrays. - const OatMethodOffsets& method_offsets = oat_class->method_offsets_[method_offsets_index_]; - if (method_offsets.code_offset_ > offset_) { - offset_ = writer_->relative_patcher_->WriteThunks(out, offset_); - if (offset_ == 0u) { - ReportWriteFailure("relative call thunk", it); - return false; - } - uint32_t alignment_size = CodeAlignmentSize(offset_, *compiled_method); - if (alignment_size != 0) { - if (!writer_->WriteCodeAlignment(out, alignment_size)) { - ReportWriteFailure("code alignment padding", it); - return false; - } - offset_ += alignment_size; - DCHECK_OFFSET_(); - } - DCHECK_ALIGNED_PARAM(offset_ + sizeof(OatQuickMethodHeader), - GetInstructionSetAlignment(compiled_method->GetInstructionSet())); - DCHECK_EQ(method_offsets.code_offset_, - offset_ + sizeof(OatQuickMethodHeader) + compiled_method->CodeDelta()) - << dex_file_->PrettyMethod(it.GetMemberIndex()); - const OatQuickMethodHeader& method_header = - oat_class->method_headers_[method_offsets_index_]; - if (!out->WriteFully(&method_header, sizeof(method_header))) { - ReportWriteFailure("method header", it); - return false; - } - writer_->size_method_header_ += sizeof(method_header); - offset_ += sizeof(method_header); - DCHECK_OFFSET_(); - - if (!compiled_method->GetPatches().empty()) { - patched_code_.assign(quick_code.begin(), quick_code.end()); - quick_code = ArrayRef<const uint8_t>(patched_code_); - for (const LinkerPatch& patch : compiled_method->GetPatches()) { - uint32_t literal_offset = patch.LiteralOffset(); - switch (patch.GetType()) { - case LinkerPatch::Type::kMethodBssEntry: { - uint32_t target_offset = - writer_->bss_start_ + writer_->bss_method_entries_.Get(patch.TargetMethod()); - writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, - patch, - offset_ + literal_offset, - target_offset); - break; - } - case LinkerPatch::Type::kCallRelative: { - // NOTE: Relative calls across oat files are not supported. - uint32_t target_offset = GetTargetOffset(patch); - writer_->relative_patcher_->PatchCall(&patched_code_, - literal_offset, - offset_ + literal_offset, - target_offset); - break; - } - case LinkerPatch::Type::kStringRelative: { - uint32_t target_offset = GetTargetObjectOffset(GetTargetString(patch)); - writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, - patch, - offset_ + literal_offset, - target_offset); - break; - } - case LinkerPatch::Type::kStringInternTable: { - uint32_t target_offset = GetInternTableEntryOffset(patch); - writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, - patch, - offset_ + literal_offset, - target_offset); - break; - } - case LinkerPatch::Type::kStringBssEntry: { - StringReference ref(patch.TargetStringDexFile(), patch.TargetStringIndex()); - uint32_t target_offset = - writer_->bss_start_ + writer_->bss_string_entries_.Get(ref); - writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, - patch, - offset_ + literal_offset, - target_offset); - break; - } - case LinkerPatch::Type::kTypeRelative: { - uint32_t target_offset = GetTargetObjectOffset(GetTargetType(patch)); - writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, - patch, - offset_ + literal_offset, - target_offset); - break; - } - case LinkerPatch::Type::kTypeClassTable: { - uint32_t target_offset = GetClassTableEntryOffset(patch); - writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, - patch, - offset_ + literal_offset, - target_offset); - break; - } - case LinkerPatch::Type::kTypeBssEntry: { - TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex()); - uint32_t target_offset = writer_->bss_start_ + writer_->bss_type_entries_.Get(ref); - writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, - patch, - offset_ + literal_offset, - target_offset); - break; - } - case LinkerPatch::Type::kCall: { - uint32_t target_offset = GetTargetOffset(patch); - PatchCodeAddress(&patched_code_, literal_offset, target_offset); - break; - } - case LinkerPatch::Type::kMethodRelative: { - uint32_t target_offset = GetTargetMethodOffset(GetTargetMethod(patch)); - writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, - patch, - offset_ + literal_offset, - target_offset); - break; - } - case LinkerPatch::Type::kBakerReadBarrierBranch: { - writer_->relative_patcher_->PatchBakerReadBarrierBranch(&patched_code_, - patch, - offset_ + literal_offset); - break; - } - default: { - DCHECK(false) << "Unexpected linker patch type: " << patch.GetType(); - break; - } - } - } - } - - if (!out->WriteFully(quick_code.data(), code_size)) { - ReportWriteFailure("method code", it); - return false; - } - writer_->size_code_ += code_size; - offset_ += code_size; - } - DCHECK_OFFSET_(); - ++method_offsets_index_; - } - - return true; - } - - private: - const PointerSize pointer_size_; - ObjPtr<mirror::ClassLoader> class_loader_; - OutputStream* const out_; - const size_t file_offset_; - const ScopedObjectAccess soa_; - const ScopedAssertNoThreadSuspension no_thread_suspension_; - ClassLinker* const class_linker_; - ObjPtr<mirror::DexCache> dex_cache_; - std::vector<uint8_t> patched_code_; - - void ReportWriteFailure(const char* what, const ClassDataItemIterator& it) { - PLOG(ERROR) << "Failed to write " << what << " for " - << dex_file_->PrettyMethod(it.GetMemberIndex()) << " to " << out_->GetLocation(); - } - - ArtMethod* GetTargetMethod(const LinkerPatch& patch) - REQUIRES_SHARED(Locks::mutator_lock_) { - MethodReference ref = patch.TargetMethod(); - ObjPtr<mirror::DexCache> dex_cache = - (dex_file_ == ref.dex_file) ? dex_cache_ : class_linker_->FindDexCache( - Thread::Current(), *ref.dex_file); - ArtMethod* method = class_linker_->LookupResolvedMethod(ref.index, dex_cache, class_loader_); - CHECK(method != nullptr); - return method; - } - - uint32_t GetTargetOffset(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { - uint32_t target_offset = writer_->relative_patcher_->GetOffset(patch.TargetMethod()); - // If there's no new compiled code, either we're compiling an app and the target method - // is in the boot image, or we need to point to the correct trampoline. - if (UNLIKELY(target_offset == 0)) { - ArtMethod* target = GetTargetMethod(patch); - DCHECK(target != nullptr); - const void* oat_code_offset = - target->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_); - if (oat_code_offset != 0) { - DCHECK(!writer_->HasBootImage()); - DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(oat_code_offset)); - DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(oat_code_offset)); - DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickGenericJniStub(oat_code_offset)); - target_offset = PointerToLowMemUInt32(oat_code_offset); - } else { - target_offset = target->IsNative() - ? writer_->oat_header_->GetQuickGenericJniTrampolineOffset() - : writer_->oat_header_->GetQuickToInterpreterBridgeOffset(); - } - } - return target_offset; - } - - ObjPtr<mirror::DexCache> GetDexCache(const DexFile* target_dex_file) - REQUIRES_SHARED(Locks::mutator_lock_) { - return (target_dex_file == dex_file_) - ? dex_cache_ - : class_linker_->FindDexCache(Thread::Current(), *target_dex_file); - } - - mirror::Class* GetTargetType(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(writer_->HasImage()); - ObjPtr<mirror::DexCache> dex_cache = GetDexCache(patch.TargetTypeDexFile()); - ObjPtr<mirror::Class> type = - ClassLinker::LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_); - CHECK(type != nullptr); - return type.Ptr(); - } - - mirror::String* GetTargetString(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - mirror::String* string = linker->LookupString(*patch.TargetStringDexFile(), - patch.TargetStringIndex(), - GetDexCache(patch.TargetStringDexFile())); - DCHECK(string != nullptr); - DCHECK(writer_->HasBootImage() || - Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(string)); - return string; - } - - uint32_t GetTargetMethodOffset(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(writer_->HasBootImage()); - method = writer_->image_writer_->GetImageMethodAddress(method); - size_t oat_index = writer_->image_writer_->GetOatIndexForDexFile(dex_file_); - uintptr_t oat_data_begin = writer_->image_writer_->GetOatDataBegin(oat_index); - // TODO: Clean up offset types. The target offset must be treated as signed. - return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method) - oat_data_begin); - } - - uint32_t GetTargetObjectOffset(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(writer_->HasBootImage()); - object = writer_->image_writer_->GetImageAddress(object); - size_t oat_index = writer_->image_writer_->GetOatIndexForDexFile(dex_file_); - uintptr_t oat_data_begin = writer_->image_writer_->GetOatDataBegin(oat_index); - // TODO: Clean up offset types. The target offset must be treated as signed. - return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(object) - oat_data_begin); - } - - void PatchObjectAddress(std::vector<uint8_t>* code, uint32_t offset, mirror::Object* object) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (writer_->HasBootImage()) { - object = writer_->image_writer_->GetImageAddress(object); - } else { - // NOTE: We're using linker patches for app->boot references when the image can - // be relocated and therefore we need to emit .oat_patches. We're not using this - // for app->app references, so check that the object is in the image space. - DCHECK(Runtime::Current()->GetHeap()->FindSpaceFromObject(object, false)->IsImageSpace()); - } - // Note: We only patch targeting Objects in image which is in the low 4gb. - uint32_t address = PointerToLowMemUInt32(object); - DCHECK_LE(offset + 4, code->size()); - uint8_t* data = &(*code)[offset]; - data[0] = address & 0xffu; - data[1] = (address >> 8) & 0xffu; - data[2] = (address >> 16) & 0xffu; - data[3] = (address >> 24) & 0xffu; - } - - void PatchCodeAddress(std::vector<uint8_t>* code, uint32_t offset, uint32_t target_offset) - REQUIRES_SHARED(Locks::mutator_lock_) { - uint32_t address = target_offset; - if (writer_->HasBootImage()) { - size_t oat_index = writer_->image_writer_->GetOatIndexForDexCache(dex_cache_); - // TODO: Clean up offset types. - // The target_offset must be treated as signed for cross-oat patching. - const void* target = reinterpret_cast<const void*>( - writer_->image_writer_->GetOatDataBegin(oat_index) + - static_cast<int32_t>(target_offset)); - address = PointerToLowMemUInt32(target); - } - DCHECK_LE(offset + 4, code->size()); - uint8_t* data = &(*code)[offset]; - data[0] = address & 0xffu; - data[1] = (address >> 8) & 0xffu; - data[2] = (address >> 16) & 0xffu; - data[3] = (address >> 24) & 0xffu; - } - - // Calculate the offset of the InternTable slot (GcRoot<String>) when mmapped to the .bss. - uint32_t GetInternTableEntryOffset(const LinkerPatch& patch) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!writer_->HasBootImage()); - const uint8_t* string_root = writer_->LookupBootImageInternTableSlot( - *patch.TargetStringDexFile(), patch.TargetStringIndex()); - DCHECK(string_root != nullptr); - return GetBootImageTableEntryOffset(string_root); - } - - // Calculate the offset of the ClassTable::TableSlot when mmapped to the .bss. - uint32_t GetClassTableEntryOffset(const LinkerPatch& patch) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(!writer_->HasBootImage()); - const uint8_t* table_slot = - writer_->LookupBootImageClassTableSlot(*patch.TargetTypeDexFile(), patch.TargetTypeIndex()); - DCHECK(table_slot != nullptr); - return GetBootImageTableEntryOffset(table_slot); - } - - uint32_t GetBootImageTableEntryOffset(const uint8_t* raw_root) { - uint32_t base_offset = writer_->bss_start_; - for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) { - const uint8_t* const_tables_begin = - space->Begin() + space->GetImageHeader().GetBootImageConstantTablesOffset(); - size_t offset = static_cast<size_t>(raw_root - const_tables_begin); - if (offset < space->GetImageHeader().GetBootImageConstantTablesSize()) { - DCHECK_LE(base_offset + offset, writer_->bss_start_ + writer_->bss_methods_offset_); - return base_offset + offset; - } - base_offset += space->GetImageHeader().GetBootImageConstantTablesSize(); - } - LOG(FATAL) << "Didn't find boot image string in boot image intern tables!"; - UNREACHABLE(); - } -}; - -class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor { - public: - WriteMapMethodVisitor(OatWriter* writer, - OutputStream* out, - const size_t file_offset, - size_t relative_offset) - : OatDexMethodVisitor(writer, relative_offset), - out_(out), - file_offset_(file_offset) {} - - bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) OVERRIDE { - OatClass* oat_class = &writer_->oat_classes_[oat_class_index_]; - const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); - - if (HasCompiledCode(compiled_method)) { - size_t file_offset = file_offset_; - OutputStream* out = out_; - - uint32_t map_offset = oat_class->method_headers_[method_offsets_index_].GetVmapTableOffset(); - uint32_t code_offset = oat_class->method_offsets_[method_offsets_index_].code_offset_; - ++method_offsets_index_; - - DCHECK((compiled_method->GetVmapTable().size() == 0u && map_offset == 0u) || - (compiled_method->GetVmapTable().size() != 0u && map_offset != 0u)) - << compiled_method->GetVmapTable().size() << " " << map_offset << " " - << dex_file_->PrettyMethod(it.GetMemberIndex()); - - // If vdex is enabled, only emit the map for compiled code. The quickening info - // is emitted in the vdex already. - if (map_offset != 0u) { - // Transform map_offset to actual oat data offset. - map_offset = (code_offset - compiled_method->CodeDelta()) - map_offset; - DCHECK_NE(map_offset, 0u); - DCHECK_LE(map_offset, offset_) << dex_file_->PrettyMethod(it.GetMemberIndex()); - - ArrayRef<const uint8_t> map = compiled_method->GetVmapTable(); - size_t map_size = map.size() * sizeof(map[0]); - if (map_offset == offset_) { - // Write deduplicated map (code info for Optimizing or transformation info for dex2dex). - if (UNLIKELY(!out->WriteFully(map.data(), map_size))) { - ReportWriteFailure(it); - return false; - } - offset_ += map_size; - } - } - DCHECK_OFFSET_(); - } - - return true; - } - - private: - OutputStream* const out_; - size_t const file_offset_; - - void ReportWriteFailure(const ClassDataItemIterator& it) { - PLOG(ERROR) << "Failed to write map for " - << dex_file_->PrettyMethod(it.GetMemberIndex()) << " to " << out_->GetLocation(); - } -}; - -class OatWriter::WriteMethodInfoVisitor : public OatDexMethodVisitor { - public: - WriteMethodInfoVisitor(OatWriter* writer, - OutputStream* out, - const size_t file_offset, - size_t relative_offset) - : OatDexMethodVisitor(writer, relative_offset), - out_(out), - file_offset_(file_offset) {} - - bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) OVERRIDE { - OatClass* oat_class = &writer_->oat_classes_[oat_class_index_]; - const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); - - if (HasCompiledCode(compiled_method)) { - size_t file_offset = file_offset_; - OutputStream* out = out_; - uint32_t map_offset = oat_class->method_headers_[method_offsets_index_].GetMethodInfoOffset(); - uint32_t code_offset = oat_class->method_offsets_[method_offsets_index_].code_offset_; - ++method_offsets_index_; - DCHECK((compiled_method->GetMethodInfo().size() == 0u && map_offset == 0u) || - (compiled_method->GetMethodInfo().size() != 0u && map_offset != 0u)) - << compiled_method->GetMethodInfo().size() << " " << map_offset << " " - << dex_file_->PrettyMethod(it.GetMemberIndex()); - if (map_offset != 0u) { - // Transform map_offset to actual oat data offset. - map_offset = (code_offset - compiled_method->CodeDelta()) - map_offset; - DCHECK_NE(map_offset, 0u); - DCHECK_LE(map_offset, offset_) << dex_file_->PrettyMethod(it.GetMemberIndex()); - - ArrayRef<const uint8_t> map = compiled_method->GetMethodInfo(); - size_t map_size = map.size() * sizeof(map[0]); - if (map_offset == offset_) { - // Write deduplicated map (code info for Optimizing or transformation info for dex2dex). - if (UNLIKELY(!out->WriteFully(map.data(), map_size))) { - ReportWriteFailure(it); - return false; - } - offset_ += map_size; - } - } - DCHECK_OFFSET_(); - } - - return true; - } - - private: - OutputStream* const out_; - size_t const file_offset_; - - void ReportWriteFailure(const ClassDataItemIterator& it) { - PLOG(ERROR) << "Failed to write map for " - << dex_file_->PrettyMethod(it.GetMemberIndex()) << " to " << out_->GetLocation(); - } -}; - -// Visit all methods from all classes in all dex files with the specified visitor. -bool OatWriter::VisitDexMethods(DexMethodVisitor* visitor) { - for (const DexFile* dex_file : *dex_files_) { - const size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index != class_def_count; ++class_def_index) { - if (UNLIKELY(!visitor->StartClass(dex_file, class_def_index))) { - return false; - } - if (MayHaveCompiledMethods()) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data != nullptr) { // ie not an empty class, such as a marker interface - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - size_t class_def_method_index = 0u; - while (it.HasNextDirectMethod()) { - if (!visitor->VisitMethod(class_def_method_index, it)) { - return false; - } - ++class_def_method_index; - it.Next(); - } - while (it.HasNextVirtualMethod()) { - if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) { - return false; - } - ++class_def_method_index; - it.Next(); - } - } - } - if (UNLIKELY(!visitor->EndClass())) { - return false; - } - } - } - return true; -} - -size_t OatWriter::InitOatHeader(InstructionSet instruction_set, - const InstructionSetFeatures* instruction_set_features, - uint32_t num_dex_files, - SafeMap<std::string, std::string>* key_value_store) { - TimingLogger::ScopedTiming split("InitOatHeader", timings_); - oat_header_.reset(OatHeader::Create(instruction_set, - instruction_set_features, - num_dex_files, - key_value_store)); - size_oat_header_ += sizeof(OatHeader); - size_oat_header_key_value_store_ += oat_header_->GetHeaderSize() - sizeof(OatHeader); - return oat_header_->GetHeaderSize(); -} - -size_t OatWriter::InitClassOffsets(size_t offset) { - // Reserve space for class offsets in OAT and update class_offsets_offset_. - for (OatDexFile& oat_dex_file : oat_dex_files_) { - DCHECK_EQ(oat_dex_file.class_offsets_offset_, 0u); - if (!oat_dex_file.class_offsets_.empty()) { - // Class offsets are required to be 4 byte aligned. - offset = RoundUp(offset, 4u); - oat_dex_file.class_offsets_offset_ = offset; - offset += oat_dex_file.GetClassOffsetsRawSize(); - DCHECK_ALIGNED(offset, 4u); - } - } - return offset; -} - -size_t OatWriter::InitOatClasses(size_t offset) { - // calculate the offsets within OatDexFiles to OatClasses - InitOatClassesMethodVisitor visitor(this, offset); - bool success = VisitDexMethods(&visitor); - CHECK(success); - offset = visitor.GetOffset(); - - // Update oat_dex_files_. - auto oat_class_it = oat_class_headers_.begin(); - for (OatDexFile& oat_dex_file : oat_dex_files_) { - for (uint32_t& class_offset : oat_dex_file.class_offsets_) { - DCHECK(oat_class_it != oat_class_headers_.end()); - class_offset = oat_class_it->offset_; - ++oat_class_it; - } - } - CHECK(oat_class_it == oat_class_headers_.end()); - - return offset; -} - -size_t OatWriter::InitOatMaps(size_t offset) { - if (!MayHaveCompiledMethods()) { - return offset; - } - { - InitMapMethodVisitor visitor(this, offset); - bool success = VisitDexMethods(&visitor); - DCHECK(success); - offset = visitor.GetOffset(); - } - { - InitMethodInfoVisitor visitor(this, offset); - bool success = VisitDexMethods(&visitor); - DCHECK(success); - offset = visitor.GetOffset(); - } - return offset; -} - -size_t OatWriter::InitMethodBssMappings(size_t offset) { - size_t number_of_dex_files = 0u; - for (size_t i = 0, size = dex_files_->size(); i != size; ++i) { - const DexFile* dex_file = (*dex_files_)[i]; - auto it = bss_method_entry_references_.find(dex_file); - if (it != bss_method_entry_references_.end()) { - const BitVector& method_indexes = it->second; - ++number_of_dex_files; - // If there are any classes, the class offsets allocation aligns the offset - // and we cannot have method bss mappings without class offsets. - static_assert(alignof(MethodBssMapping) == 4u, "MethodBssMapping alignment check."); - DCHECK_ALIGNED(offset, 4u); - oat_dex_files_[i].method_bss_mapping_offset_ = offset; - - linker::MethodBssMappingEncoder encoder( - GetInstructionSetPointerSize(oat_header_->GetInstructionSet())); - size_t number_of_entries = 0u; - bool first_index = true; - for (uint32_t method_index : method_indexes.Indexes()) { - uint32_t bss_offset = bss_method_entries_.Get(MethodReference(dex_file, method_index)); - if (first_index || !encoder.TryMerge(method_index, bss_offset)) { - encoder.Reset(method_index, bss_offset); - ++number_of_entries; - first_index = false; - } - } - DCHECK_NE(number_of_entries, 0u); - offset += MethodBssMapping::ComputeSize(number_of_entries); - } - } - // Check that all dex files targeted by method bss entries are in `*dex_files_`. - CHECK_EQ(number_of_dex_files, bss_method_entry_references_.size()); - return offset; -} - -size_t OatWriter::InitOatDexFiles(size_t offset) { - // Initialize offsets of oat dex files. - for (OatDexFile& oat_dex_file : oat_dex_files_) { - oat_dex_file.offset_ = offset; - offset += oat_dex_file.SizeOf(); - } - return offset; -} - -size_t OatWriter::InitOatCode(size_t offset) { - // calculate the offsets within OatHeader to executable code - size_t old_offset = offset; - // required to be on a new page boundary - offset = RoundUp(offset, kPageSize); - oat_header_->SetExecutableOffset(offset); - size_executable_offset_alignment_ = offset - old_offset; - // TODO: Remove unused trampoline offsets from the OatHeader (requires oat version change). - oat_header_->SetInterpreterToInterpreterBridgeOffset(0); - oat_header_->SetInterpreterToCompiledCodeBridgeOffset(0); - if (compiler_driver_->GetCompilerOptions().IsBootImage()) { - InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); - const bool generate_debug_info = compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo(); - size_t adjusted_offset = offset; - - #define DO_TRAMPOLINE(field, fn_name) \ - offset = CompiledCode::AlignCode(offset, instruction_set); \ - adjusted_offset = offset + CompiledCode::CodeDelta(instruction_set); \ - oat_header_->Set ## fn_name ## Offset(adjusted_offset); \ - (field) = compiler_driver_->Create ## fn_name(); \ - if (generate_debug_info) { \ - debug::MethodDebugInfo info = {}; \ - info.trampoline_name = #fn_name; \ - info.isa = instruction_set; \ - info.is_code_address_text_relative = true; \ - /* Use the code offset rather than the `adjusted_offset`. */ \ - info.code_address = offset - oat_header_->GetExecutableOffset(); \ - info.code_size = (field)->size(); \ - method_info_.push_back(std::move(info)); \ - } \ - offset += (field)->size(); - - DO_TRAMPOLINE(jni_dlsym_lookup_, JniDlsymLookup); - DO_TRAMPOLINE(quick_generic_jni_trampoline_, QuickGenericJniTrampoline); - DO_TRAMPOLINE(quick_imt_conflict_trampoline_, QuickImtConflictTrampoline); - DO_TRAMPOLINE(quick_resolution_trampoline_, QuickResolutionTrampoline); - DO_TRAMPOLINE(quick_to_interpreter_bridge_, QuickToInterpreterBridge); - - #undef DO_TRAMPOLINE - } else { - oat_header_->SetJniDlsymLookupOffset(0); - oat_header_->SetQuickGenericJniTrampolineOffset(0); - oat_header_->SetQuickImtConflictTrampolineOffset(0); - oat_header_->SetQuickResolutionTrampolineOffset(0); - oat_header_->SetQuickToInterpreterBridgeOffset(0); - } - return offset; -} - -size_t OatWriter::InitOatCodeDexFiles(size_t offset) { - if (!compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) { - return offset; - } - InitCodeMethodVisitor code_visitor(this, offset); - bool success = VisitDexMethods(&code_visitor); - DCHECK(success); - offset = code_visitor.GetOffset(); - - if (HasImage()) { - InitImageMethodVisitor image_visitor(this, offset, dex_files_); - success = VisitDexMethods(&image_visitor); - image_visitor.Postprocess(); - DCHECK(success); - offset = image_visitor.GetOffset(); - } - - return offset; -} - -void OatWriter::InitBssLayout(InstructionSet instruction_set) { - { - InitBssLayoutMethodVisitor visitor(this); - bool success = VisitDexMethods(&visitor); - DCHECK(success); - } - - DCHECK_EQ(bss_size_, 0u); - if (HasBootImage()) { - DCHECK(!map_boot_image_tables_to_bss_); - DCHECK(bss_string_entries_.empty()); - } - if (!map_boot_image_tables_to_bss_ && - bss_method_entries_.empty() && - bss_type_entries_.empty() && - bss_string_entries_.empty()) { - // Nothing to put to the .bss section. - return; - } - - // Allocate space for boot image tables in the .bss section. - PointerSize pointer_size = GetInstructionSetPointerSize(instruction_set); - if (map_boot_image_tables_to_bss_) { - for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) { - bss_size_ += space->GetImageHeader().GetBootImageConstantTablesSize(); - } - } - - bss_methods_offset_ = bss_size_; - - // Prepare offsets for .bss ArtMethod entries. - for (auto& entry : bss_method_entries_) { - DCHECK_EQ(entry.second, 0u); - entry.second = bss_size_; - bss_size_ += static_cast<size_t>(pointer_size); - } - - bss_roots_offset_ = bss_size_; - - // Prepare offsets for .bss Class entries. - for (auto& entry : bss_type_entries_) { - DCHECK_EQ(entry.second, 0u); - entry.second = bss_size_; - bss_size_ += sizeof(GcRoot<mirror::Class>); - } - // Prepare offsets for .bss String entries. - for (auto& entry : bss_string_entries_) { - DCHECK_EQ(entry.second, 0u); - entry.second = bss_size_; - bss_size_ += sizeof(GcRoot<mirror::String>); - } -} - -bool OatWriter::WriteRodata(OutputStream* out) { - CHECK(write_state_ == WriteState::kWriteRoData); - - size_t file_offset = oat_data_offset_; - off_t current_offset = out->Seek(0, kSeekCurrent); - if (current_offset == static_cast<off_t>(-1)) { - PLOG(ERROR) << "Failed to retrieve current position in " << out->GetLocation(); - } - DCHECK_GE(static_cast<size_t>(current_offset), file_offset + oat_header_->GetHeaderSize()); - size_t relative_offset = current_offset - file_offset; - - // Wrap out to update checksum with each write. - ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get()); - out = &checksum_updating_out; - - relative_offset = WriteClassOffsets(out, file_offset, relative_offset); - if (relative_offset == 0) { - PLOG(ERROR) << "Failed to write class offsets to " << out->GetLocation(); - return false; - } - - relative_offset = WriteClasses(out, file_offset, relative_offset); - if (relative_offset == 0) { - PLOG(ERROR) << "Failed to write classes to " << out->GetLocation(); - return false; - } - - relative_offset = WriteMethodBssMappings(out, file_offset, relative_offset); - if (relative_offset == 0) { - PLOG(ERROR) << "Failed to write method bss mappings to " << out->GetLocation(); - return false; - } - - relative_offset = WriteMaps(out, file_offset, relative_offset); - if (relative_offset == 0) { - PLOG(ERROR) << "Failed to write oat code to " << out->GetLocation(); - return false; - } - - relative_offset = WriteOatDexFiles(out, file_offset, relative_offset); - if (relative_offset == 0) { - PLOG(ERROR) << "Failed to write oat dex information to " << out->GetLocation(); - return false; - } - - // Write padding. - off_t new_offset = out->Seek(size_executable_offset_alignment_, kSeekCurrent); - relative_offset += size_executable_offset_alignment_; - DCHECK_EQ(relative_offset, oat_header_->GetExecutableOffset()); - 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(); - return 0; - } - DCHECK_OFFSET(); - - write_state_ = WriteState::kWriteText; - return true; -} - -class OatWriter::WriteQuickeningInfoMethodVisitor : public DexMethodVisitor { - public: - WriteQuickeningInfoMethodVisitor(OatWriter* writer, - OutputStream* out, - uint32_t offset, - SafeMap<const uint8_t*, uint32_t>* offset_map) - : DexMethodVisitor(writer, offset), - out_(out), - written_bytes_(0u), - offset_map_(offset_map) {} - - bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED, const ClassDataItemIterator& it) - OVERRIDE { - uint32_t method_idx = it.GetMemberIndex(); - CompiledMethod* compiled_method = - writer_->compiler_driver_->GetCompiledMethod(MethodReference(dex_file_, method_idx)); - - if (HasQuickeningInfo(compiled_method)) { - ArrayRef<const uint8_t> map = compiled_method->GetVmapTable(); - // Deduplication is already done on a pointer basis by the compiler driver, - // so we can simply compare the pointers to find out if things are duplicated. - if (offset_map_->find(map.data()) == offset_map_->end()) { - uint32_t length = map.size() * sizeof(map.front()); - offset_map_->Put(map.data(), written_bytes_); - if (!out_->WriteFully(&length, sizeof(length)) || - !out_->WriteFully(map.data(), length)) { - PLOG(ERROR) << "Failed to write quickening info for " - << dex_file_->PrettyMethod(it.GetMemberIndex()) << " to " - << out_->GetLocation(); - return false; - } - written_bytes_ += sizeof(length) + length; - offset_ += sizeof(length) + length; - } - } - - return true; - } - - size_t GetNumberOfWrittenBytes() const { - return written_bytes_; - } - - private: - OutputStream* const out_; - size_t written_bytes_; - // Maps quickening map to its offset in the file. - SafeMap<const uint8_t*, uint32_t>* offset_map_; -}; - -class OatWriter::WriteQuickeningIndicesMethodVisitor { - public: - WriteQuickeningIndicesMethodVisitor(OutputStream* out, - uint32_t indices_offset, - const SafeMap<const uint8_t*, uint32_t>& offset_map, - std::vector<uint32_t>* dex_files_offset) - : out_(out), - indices_offset_(indices_offset), - written_bytes_(0u), - dex_files_offset_(dex_files_offset), - offset_map_(offset_map) {} - - bool VisitDexMethods(const std::vector<const DexFile*>& dex_files, const CompilerDriver& driver) { - for (const DexFile* dex_file : dex_files) { - // Record the offset for this current dex file. It will be written in the vdex file - // later. - dex_files_offset_->push_back(indices_offset_ + GetNumberOfWrittenBytes()); - const size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index != class_def_count; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - continue; - } - for (ClassDataItemIterator class_it(*dex_file, class_data); - class_it.HasNext(); - class_it.Next()) { - if (!class_it.IsAtMethod()) { - continue; - } - uint32_t method_idx = class_it.GetMemberIndex(); - CompiledMethod* compiled_method = - driver.GetCompiledMethod(MethodReference(dex_file, method_idx)); - if (HasQuickeningInfo(compiled_method)) { - uint32_t code_item_offset = class_it.GetMethodCodeItemOffset(); - uint32_t offset = offset_map_.Get(compiled_method->GetVmapTable().data()); - if (!out_->WriteFully(&code_item_offset, sizeof(code_item_offset)) || - !out_->WriteFully(&offset, sizeof(offset))) { - PLOG(ERROR) << "Failed to write quickening info for " - << dex_file->PrettyMethod(method_idx) << " to " - << out_->GetLocation(); - return false; - } - written_bytes_ += sizeof(code_item_offset) + sizeof(offset); - } - } - } - } - return true; - } - - size_t GetNumberOfWrittenBytes() const { - return written_bytes_; - } - - private: - OutputStream* const out_; - const uint32_t indices_offset_; - size_t written_bytes_; - std::vector<uint32_t>* dex_files_offset_; - // Maps quickening map to its offset in the file. - const SafeMap<const uint8_t*, uint32_t>& offset_map_; -}; - -bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { - if (!kIsVdexEnabled) { - return true; - } - - size_t initial_offset = vdex_size_; - size_t start_offset = RoundUp(initial_offset, 4u); - - vdex_size_ = start_offset; - vdex_quickening_info_offset_ = vdex_size_; - size_quickening_info_alignment_ = start_offset - initial_offset; - - off_t actual_offset = vdex_out->Seek(start_offset, kSeekSet); - if (actual_offset != static_cast<off_t>(start_offset)) { - PLOG(ERROR) << "Failed to seek to quickening info section. Actual: " << actual_offset - << " Expected: " << start_offset - << " Output: " << vdex_out->GetLocation(); - return false; - } - - if (compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) { - std::vector<uint32_t> dex_files_indices; - SafeMap<const uint8_t*, uint32_t> offset_map; - WriteQuickeningInfoMethodVisitor visitor1(this, vdex_out, start_offset, &offset_map); - if (!VisitDexMethods(&visitor1)) { - PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation(); - return false; - } - - WriteQuickeningIndicesMethodVisitor visitor2(vdex_out, - visitor1.GetNumberOfWrittenBytes(), - offset_map, - &dex_files_indices); - if (!visitor2.VisitDexMethods(*dex_files_, *compiler_driver_)) { - PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation(); - return false; - } - - DCHECK_EQ(dex_files_->size(), dex_files_indices.size()); - if (!vdex_out->WriteFully( - dex_files_indices.data(), sizeof(dex_files_indices[0]) * dex_files_indices.size())) { - PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation(); - return false; - } - - if (!vdex_out->Flush()) { - PLOG(ERROR) << "Failed to flush stream after writing quickening info." - << " File: " << vdex_out->GetLocation(); - return false; - } - size_quickening_info_ = visitor1.GetNumberOfWrittenBytes() + - visitor2.GetNumberOfWrittenBytes() + - dex_files_->size() * sizeof(uint32_t); - } else { - // We know we did not quicken. - size_quickening_info_ = 0; - } - - vdex_size_ += size_quickening_info_; - return true; -} - -bool OatWriter::WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps) { - if (!kIsVdexEnabled) { - return true; - } - - if (verifier_deps == nullptr) { - // Nothing to write. Record the offset, but no need - // for alignment. - vdex_verifier_deps_offset_ = vdex_size_; - return true; - } - - size_t initial_offset = vdex_size_; - size_t start_offset = RoundUp(initial_offset, 4u); - - vdex_size_ = start_offset; - vdex_verifier_deps_offset_ = vdex_size_; - size_verifier_deps_alignment_ = start_offset - initial_offset; - - off_t actual_offset = vdex_out->Seek(start_offset, kSeekSet); - if (actual_offset != static_cast<off_t>(start_offset)) { - PLOG(ERROR) << "Failed to seek to verifier deps section. Actual: " << actual_offset - << " Expected: " << start_offset - << " Output: " << vdex_out->GetLocation(); - return false; - } - - std::vector<uint8_t> buffer; - verifier_deps->Encode(*dex_files_, &buffer); - - if (!vdex_out->WriteFully(buffer.data(), buffer.size())) { - PLOG(ERROR) << "Failed to write verifier deps." - << " File: " << vdex_out->GetLocation(); - return false; - } - if (!vdex_out->Flush()) { - PLOG(ERROR) << "Failed to flush stream after writing verifier deps." - << " File: " << vdex_out->GetLocation(); - return false; - } - - size_verifier_deps_ = buffer.size(); - vdex_size_ += size_verifier_deps_; - return true; -} - -bool OatWriter::WriteCode(OutputStream* out) { - CHECK(write_state_ == WriteState::kWriteText); - - // Wrap out to update checksum with each write. - ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get()); - out = &checksum_updating_out; - - SetMultiOatRelativePatcherAdjustment(); - - const size_t file_offset = oat_data_offset_; - size_t relative_offset = oat_header_->GetExecutableOffset(); - DCHECK_OFFSET(); - - relative_offset = WriteCode(out, file_offset, relative_offset); - if (relative_offset == 0) { - 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(); - return false; - } - - const off_t oat_end_file_offset = out->Seek(0, kSeekCurrent); - if (oat_end_file_offset == static_cast<off_t>(-1)) { - LOG(ERROR) << "Failed to get oat end file offset in " << out->GetLocation(); - return false; - } - - if (kIsDebugBuild) { - uint32_t size_total = 0; - #define DO_STAT(x) \ - VLOG(compiler) << #x "=" << PrettySize(x) << " (" << (x) << "B)"; \ - size_total += (x); - - DO_STAT(size_vdex_header_); - DO_STAT(size_vdex_checksums_); - DO_STAT(size_dex_file_alignment_); - DO_STAT(size_executable_offset_alignment_); - DO_STAT(size_oat_header_); - DO_STAT(size_oat_header_key_value_store_); - DO_STAT(size_dex_file_); - DO_STAT(size_verifier_deps_); - DO_STAT(size_verifier_deps_alignment_); - DO_STAT(size_quickening_info_); - DO_STAT(size_quickening_info_alignment_); - DO_STAT(size_interpreter_to_interpreter_bridge_); - DO_STAT(size_interpreter_to_compiled_code_bridge_); - DO_STAT(size_jni_dlsym_lookup_); - DO_STAT(size_quick_generic_jni_trampoline_); - DO_STAT(size_quick_imt_conflict_trampoline_); - DO_STAT(size_quick_resolution_trampoline_); - DO_STAT(size_quick_to_interpreter_bridge_); - DO_STAT(size_trampoline_alignment_); - DO_STAT(size_method_header_); - DO_STAT(size_code_); - DO_STAT(size_code_alignment_); - DO_STAT(size_relative_call_thunks_); - DO_STAT(size_misc_thunks_); - DO_STAT(size_vmap_table_); - DO_STAT(size_method_info_); - DO_STAT(size_oat_dex_file_location_size_); - DO_STAT(size_oat_dex_file_location_data_); - DO_STAT(size_oat_dex_file_location_checksum_); - DO_STAT(size_oat_dex_file_offset_); - DO_STAT(size_oat_dex_file_class_offsets_offset_); - DO_STAT(size_oat_dex_file_lookup_table_offset_); - DO_STAT(size_oat_dex_file_dex_layout_sections_offset_); - DO_STAT(size_oat_dex_file_dex_layout_sections_); - DO_STAT(size_oat_dex_file_dex_layout_sections_alignment_); - DO_STAT(size_oat_dex_file_method_bss_mapping_offset_); - DO_STAT(size_oat_lookup_table_alignment_); - DO_STAT(size_oat_lookup_table_); - DO_STAT(size_oat_class_offsets_alignment_); - DO_STAT(size_oat_class_offsets_); - DO_STAT(size_oat_class_type_); - DO_STAT(size_oat_class_status_); - DO_STAT(size_oat_class_method_bitmaps_); - DO_STAT(size_oat_class_method_offsets_); - DO_STAT(size_method_bss_mappings_); - #undef DO_STAT - - VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; - - CHECK_EQ(vdex_size_ + oat_size_, size_total); - CHECK_EQ(file_offset + size_total - vdex_size_, static_cast<size_t>(oat_end_file_offset)); - } - - CHECK_EQ(file_offset + oat_size_, static_cast<size_t>(oat_end_file_offset)); - CHECK_EQ(oat_size_, relative_offset); - - write_state_ = WriteState::kWriteHeader; - return true; -} - -bool OatWriter::WriteHeader(OutputStream* out, - uint32_t image_file_location_oat_checksum, - uintptr_t image_file_location_oat_begin, - int32_t image_patch_delta) { - CHECK(write_state_ == WriteState::kWriteHeader); - - oat_header_->SetImageFileLocationOatChecksum(image_file_location_oat_checksum); - oat_header_->SetImageFileLocationOatDataBegin(image_file_location_oat_begin); - if (compiler_driver_->GetCompilerOptions().IsBootImage()) { - CHECK_EQ(image_patch_delta, 0); - CHECK_EQ(oat_header_->GetImagePatchDelta(), 0); - } else { - CHECK_ALIGNED(image_patch_delta, kPageSize); - oat_header_->SetImagePatchDelta(image_patch_delta); - } - oat_header_->UpdateChecksumWithHeaderData(); - - const size_t file_offset = oat_data_offset_; - - off_t current_offset = out->Seek(0, kSeekCurrent); - if (current_offset == static_cast<off_t>(-1)) { - PLOG(ERROR) << "Failed to get current offset from " << out->GetLocation(); - return false; - } - if (out->Seek(file_offset, kSeekSet) == static_cast<off_t>(-1)) { - PLOG(ERROR) << "Failed to seek to oat header position in " << out->GetLocation(); - return false; - } - DCHECK_EQ(file_offset, static_cast<size_t>(out->Seek(0, kSeekCurrent))); - - // Flush all other data before writing the header. - if (!out->Flush()) { - PLOG(ERROR) << "Failed to flush before writing oat header to " << out->GetLocation(); - return false; - } - // Write the header. - size_t header_size = oat_header_->GetHeaderSize(); - if (!out->WriteFully(oat_header_.get(), header_size)) { - PLOG(ERROR) << "Failed to write oat header to " << out->GetLocation(); - return false; - } - // Flush the header data. - if (!out->Flush()) { - PLOG(ERROR) << "Failed to flush after writing oat header to " << out->GetLocation(); - return false; - } - - if (out->Seek(current_offset, kSeekSet) == static_cast<off_t>(-1)) { - PLOG(ERROR) << "Failed to seek back after writing oat header to " << out->GetLocation(); - return false; - } - DCHECK_EQ(current_offset, out->Seek(0, kSeekCurrent)); - - write_state_ = WriteState::kDone; - return true; -} - -size_t OatWriter::WriteClassOffsets(OutputStream* out, size_t file_offset, size_t relative_offset) { - for (OatDexFile& oat_dex_file : oat_dex_files_) { - if (oat_dex_file.class_offsets_offset_ != 0u) { - // Class offsets are required to be 4 byte aligned. - if (UNLIKELY(!IsAligned<4u>(relative_offset))) { - size_t padding_size = RoundUp(relative_offset, 4u) - relative_offset; - if (!WriteUpTo16BytesAlignment(out, padding_size, &size_oat_class_offsets_alignment_)) { - return 0u; - } - relative_offset += padding_size; - } - DCHECK_OFFSET(); - if (!oat_dex_file.WriteClassOffsets(this, out)) { - return 0u; - } - relative_offset += oat_dex_file.GetClassOffsetsRawSize(); - } - } - return relative_offset; -} - -size_t OatWriter::WriteClasses(OutputStream* out, size_t file_offset, size_t relative_offset) { - const bool may_have_compiled = MayHaveCompiledMethods(); - if (may_have_compiled) { - CHECK_EQ(oat_class_headers_.size(), oat_classes_.size()); - } - for (size_t i = 0; i < oat_class_headers_.size(); ++i) { - // If there are any classes, the class offsets allocation aligns the offset. - DCHECK_ALIGNED(relative_offset, 4u); - DCHECK_OFFSET(); - if (!oat_class_headers_[i].Write(this, out, oat_data_offset_)) { - return 0u; - } - relative_offset += oat_class_headers_[i].SizeOf(); - if (may_have_compiled) { - if (!oat_classes_[i].Write(this, out)) { - return 0u; - } - relative_offset += oat_classes_[i].SizeOf(); - } - } - return relative_offset; -} - -size_t OatWriter::WriteMaps(OutputStream* out, size_t file_offset, size_t relative_offset) { - { - size_t vmap_tables_offset = relative_offset; - WriteMapMethodVisitor visitor(this, out, file_offset, relative_offset); - if (UNLIKELY(!VisitDexMethods(&visitor))) { - return 0; - } - relative_offset = visitor.GetOffset(); - size_vmap_table_ = relative_offset - vmap_tables_offset; - } - { - size_t method_infos_offset = relative_offset; - WriteMethodInfoVisitor visitor(this, out, file_offset, relative_offset); - if (UNLIKELY(!VisitDexMethods(&visitor))) { - return 0; - } - relative_offset = visitor.GetOffset(); - size_method_info_ = relative_offset - method_infos_offset; - } - - return relative_offset; -} - -size_t OatWriter::WriteMethodBssMappings(OutputStream* out, - size_t file_offset, - size_t relative_offset) { - TimingLogger::ScopedTiming split("WriteMethodBssMappings", timings_); - - for (size_t i = 0, size = dex_files_->size(); i != size; ++i) { - const DexFile* dex_file = (*dex_files_)[i]; - OatDexFile* oat_dex_file = &oat_dex_files_[i]; - auto it = bss_method_entry_references_.find(dex_file); - if (it != bss_method_entry_references_.end()) { - const BitVector& method_indexes = it->second; - // If there are any classes, the class offsets allocation aligns the offset - // and we cannot have method bss mappings without class offsets. - static_assert(alignof(MethodBssMapping) == sizeof(uint32_t), - "MethodBssMapping alignment check."); - DCHECK_ALIGNED(relative_offset, sizeof(uint32_t)); - - linker::MethodBssMappingEncoder encoder( - GetInstructionSetPointerSize(oat_header_->GetInstructionSet())); - // Allocate a sufficiently large MethodBssMapping. - size_t number_of_method_indexes = method_indexes.NumSetBits(); - DCHECK_NE(number_of_method_indexes, 0u); - size_t max_mappings_size = MethodBssMapping::ComputeSize(number_of_method_indexes); - DCHECK_ALIGNED(max_mappings_size, sizeof(uint32_t)); - std::unique_ptr<uint32_t[]> storage(new uint32_t[max_mappings_size / sizeof(uint32_t)]); - MethodBssMapping* mappings = new(storage.get()) MethodBssMapping(number_of_method_indexes); - mappings->ClearPadding(); - // Encode the MethodBssMapping. - auto init_it = mappings->begin(); - bool first_index = true; - for (uint32_t method_index : method_indexes.Indexes()) { - size_t bss_offset = bss_method_entries_.Get(MethodReference(dex_file, method_index)); - if (first_index) { - first_index = false; - encoder.Reset(method_index, bss_offset); - } else if (!encoder.TryMerge(method_index, bss_offset)) { - *init_it = encoder.GetEntry(); - ++init_it; - encoder.Reset(method_index, bss_offset); - } - } - // Store the last entry and shrink the mapping to the actual size. - *init_it = encoder.GetEntry(); - ++init_it; - DCHECK(init_it <= mappings->end()); - mappings->SetSize(std::distance(mappings->begin(), init_it)); - size_t mappings_size = MethodBssMapping::ComputeSize(mappings->size()); - - DCHECK_EQ(relative_offset, oat_dex_file->method_bss_mapping_offset_); - DCHECK_OFFSET(); - if (!out->WriteFully(storage.get(), mappings_size)) { - return 0u; - } - size_method_bss_mappings_ += mappings_size; - relative_offset += mappings_size; - } else { - DCHECK_EQ(0u, oat_dex_file->method_bss_mapping_offset_); - } - } - return relative_offset; -} - -size_t OatWriter::WriteOatDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset) { - TimingLogger::ScopedTiming split("WriteOatDexFiles", timings_); - - for (size_t i = 0, size = oat_dex_files_.size(); i != size; ++i) { - OatDexFile* oat_dex_file = &oat_dex_files_[i]; - DCHECK_EQ(relative_offset, oat_dex_file->offset_); - DCHECK_OFFSET(); - - // Write OatDexFile. - if (!oat_dex_file->Write(this, out)) { - return 0u; - } - relative_offset += oat_dex_file->SizeOf(); - } - - return relative_offset; -} - -size_t OatWriter::WriteCode(OutputStream* out, size_t file_offset, size_t relative_offset) { - if (compiler_driver_->GetCompilerOptions().IsBootImage()) { - InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); - - #define DO_TRAMPOLINE(field) \ - do { \ - uint32_t aligned_offset = CompiledCode::AlignCode(relative_offset, instruction_set); \ - uint32_t alignment_padding = aligned_offset - relative_offset; \ - out->Seek(alignment_padding, kSeekCurrent); \ - size_trampoline_alignment_ += alignment_padding; \ - if (!out->WriteFully((field)->data(), (field)->size())) { \ - PLOG(ERROR) << "Failed to write " # field " to " << out->GetLocation(); \ - return false; \ - } \ - size_ ## field += (field)->size(); \ - relative_offset += alignment_padding + (field)->size(); \ - DCHECK_OFFSET(); \ - } while (false) - - DO_TRAMPOLINE(jni_dlsym_lookup_); - DO_TRAMPOLINE(quick_generic_jni_trampoline_); - DO_TRAMPOLINE(quick_imt_conflict_trampoline_); - DO_TRAMPOLINE(quick_resolution_trampoline_); - DO_TRAMPOLINE(quick_to_interpreter_bridge_); - #undef DO_TRAMPOLINE - } - return relative_offset; -} - -size_t OatWriter::WriteCodeDexFiles(OutputStream* out, - size_t file_offset, - size_t relative_offset) { - #define VISIT(VisitorType) \ - do { \ - VisitorType visitor(this, out, file_offset, relative_offset); \ - if (UNLIKELY(!VisitDexMethods(&visitor))) { \ - return 0; \ - } \ - relative_offset = visitor.GetOffset(); \ - } while (false) - - VISIT(WriteCodeMethodVisitor); - - #undef VISIT - - size_code_alignment_ += relative_patcher_->CodeAlignmentSize(); - size_relative_call_thunks_ += relative_patcher_->RelativeCallThunksSize(); - size_misc_thunks_ += relative_patcher_->MiscThunksSize(); - - return relative_offset; -} - -bool OatWriter::RecordOatDataOffset(OutputStream* out) { - // Get the elf file offset of the oat file. - const off_t raw_file_offset = out->Seek(0, kSeekCurrent); - if (raw_file_offset == static_cast<off_t>(-1)) { - LOG(ERROR) << "Failed to get file offset in " << out->GetLocation(); - return false; - } - oat_data_offset_ = static_cast<size_t>(raw_file_offset); - return true; -} - -bool OatWriter::ReadDexFileHeader(File* file, OatDexFile* oat_dex_file) { - // Read the dex file header and perform minimal verification. - uint8_t raw_header[sizeof(DexFile::Header)]; - if (!file->ReadFully(&raw_header, sizeof(DexFile::Header))) { - PLOG(ERROR) << "Failed to read dex file header. Actual: " - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - if (!ValidateDexFileHeader(raw_header, oat_dex_file->GetLocation())) { - return false; - } - - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); - oat_dex_file->dex_file_size_ = header->file_size_; - oat_dex_file->dex_file_location_checksum_ = header->checksum_; - oat_dex_file->class_offsets_.resize(header->class_defs_size_); - return true; -} - -bool OatWriter::ValidateDexFileHeader(const uint8_t* raw_header, const char* location) { - if (!DexFile::IsMagicValid(raw_header)) { - LOG(ERROR) << "Invalid magic number in dex file header. " << " File: " << location; - return false; - } - if (!DexFile::IsVersionValid(raw_header)) { - LOG(ERROR) << "Invalid version number in dex file header. " << " File: " << location; - return false; - } - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); - if (header->file_size_ < sizeof(DexFile::Header)) { - LOG(ERROR) << "Dex file header specifies file size insufficient to contain the header." - << " File: " << location; - return false; - } - return true; -} - -bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_vdex) { - TimingLogger::ScopedTiming split("Write Dex files", timings_); - - vdex_dex_files_offset_ = vdex_size_; - - // Write dex files. - for (OatDexFile& oat_dex_file : oat_dex_files_) { - if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) { - return false; - } - } - - CloseSources(); - return true; -} - -void OatWriter::CloseSources() { - for (OatDexFile& oat_dex_file : oat_dex_files_) { - oat_dex_file.source_.Clear(); // Get rid of the reference, it's about to be invalidated. - } - zipped_dex_files_.clear(); - zip_archives_.clear(); - raw_dex_files_.clear(); -} - -bool OatWriter::WriteDexFile(OutputStream* out, - File* file, - OatDexFile* oat_dex_file, - bool update_input_vdex) { - if (!SeekToDexFile(out, file, oat_dex_file)) { - return false; - } - if (profile_compilation_info_ != nullptr) { - CHECK(!update_input_vdex) << "We should never update the input vdex when doing dexlayout"; - if (!LayoutAndWriteDexFile(out, oat_dex_file)) { - return false; - } - } else if (oat_dex_file->source_.IsZipEntry()) { - DCHECK(!update_input_vdex); - if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) { - return false; - } - } else if (oat_dex_file->source_.IsRawFile()) { - DCHECK(!update_input_vdex); - if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetRawFile())) { - return false; - } - } else { - DCHECK(oat_dex_file->source_.IsRawData()); - if (!WriteDexFile(out, oat_dex_file, oat_dex_file->source_.GetRawData(), update_input_vdex)) { - return false; - } - } - - // Update current size and account for the written data. - if (kIsVdexEnabled) { - DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_); - vdex_size_ += oat_dex_file->dex_file_size_; - } else { - DCHECK(!update_input_vdex); - DCHECK_EQ(oat_size_, oat_dex_file->dex_file_offset_); - oat_size_ += oat_dex_file->dex_file_size_; - } - size_dex_file_ += oat_dex_file->dex_file_size_; - return true; -} - -bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file) { - // Dex files are required to be 4 byte aligned. - size_t initial_offset = kIsVdexEnabled ? vdex_size_ : oat_size_; - size_t start_offset = RoundUp(initial_offset, 4); - size_t file_offset = kIsVdexEnabled ? start_offset : (oat_data_offset_ + start_offset); - size_dex_file_alignment_ += start_offset - initial_offset; - - // Seek to the start of the dex file and flush any pending operations in the stream. - // Verify that, after flushing the stream, the file is at the same offset as the stream. - off_t actual_offset = out->Seek(file_offset, kSeekSet); - if (actual_offset != static_cast<off_t>(file_offset)) { - PLOG(ERROR) << "Failed to seek to dex file section. Actual: " << actual_offset - << " Expected: " << file_offset - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - if (!out->Flush()) { - PLOG(ERROR) << "Failed to flush before writing dex file." - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - actual_offset = lseek(file->Fd(), 0, SEEK_CUR); - if (actual_offset != static_cast<off_t>(file_offset)) { - PLOG(ERROR) << "Stream/file position mismatch! Actual: " << actual_offset - << " Expected: " << file_offset - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - - if (kIsVdexEnabled) { - vdex_size_ = start_offset; - } else { - oat_size_ = start_offset; - } - oat_dex_file->dex_file_offset_ = start_offset; - return true; -} - -bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file) { - TimingLogger::ScopedTiming split("Dex Layout", timings_); - std::string error_msg; - std::string location(oat_dex_file->GetLocation()); - std::unique_ptr<const DexFile> dex_file; - if (oat_dex_file->source_.IsZipEntry()) { - ZipEntry* zip_entry = oat_dex_file->source_.GetZipEntry(); - std::unique_ptr<MemMap> mem_map( - zip_entry->ExtractToMemMap(location.c_str(), "classes.dex", &error_msg)); - if (mem_map == nullptr) { - LOG(ERROR) << "Failed to extract dex file to mem map for layout: " << error_msg; - return false; - } - dex_file = DexFile::Open(location, - zip_entry->GetCrc32(), - std::move(mem_map), - /* verify */ true, - /* verify_checksum */ true, - &error_msg); - } else if (oat_dex_file->source_.IsRawFile()) { - File* raw_file = oat_dex_file->source_.GetRawFile(); - int dup_fd = dup(raw_file->Fd()); - if (dup_fd < 0) { - PLOG(ERROR) << "Failed to dup dex file descriptor (" << raw_file->Fd() << ") at " << location; - return false; - } - dex_file = DexFile::OpenDex(dup_fd, location, /* verify_checksum */ true, &error_msg); - } else { - // The source data is a vdex file. - CHECK(oat_dex_file->source_.IsRawData()) - << static_cast<size_t>(oat_dex_file->source_.GetType()); - const uint8_t* raw_dex_file = oat_dex_file->source_.GetRawData(); - // Note: The raw data has already been checked to contain the header - // and all the data that the header specifies as the file size. - DCHECK(raw_dex_file != nullptr); - DCHECK(ValidateDexFileHeader(raw_dex_file, oat_dex_file->GetLocation())); - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); - // Since the source may have had its layout changed, or may be quickened, don't verify it. - dex_file = DexFile::Open(raw_dex_file, - header->file_size_, - location, - oat_dex_file->dex_file_location_checksum_, - nullptr, - /* verify */ false, - /* verify_checksum */ false, - &error_msg); - } - if (dex_file == nullptr) { - LOG(ERROR) << "Failed to open dex file for layout: " << error_msg; - return false; - } - Options options; - options.output_to_memmap_ = true; - DexLayout dex_layout(options, profile_compilation_info_, nullptr); - dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0); - std::unique_ptr<MemMap> mem_map(dex_layout.GetAndReleaseMemMap()); - if (!WriteDexFile(out, oat_dex_file, mem_map->Begin(), /* update_input_vdex */ false)) { - return false; - } - oat_dex_file->dex_sections_layout_ = dex_layout.GetSections(); - // Set the checksum of the new oat dex file to be the original file's checksum. - oat_dex_file->dex_file_location_checksum_ = dex_file->GetLocationChecksum(); - return true; -} - -bool OatWriter::WriteDexFile(OutputStream* out, - File* file, - OatDexFile* oat_dex_file, - ZipEntry* dex_file) { - size_t start_offset = kIsVdexEnabled ? vdex_size_ : oat_data_offset_ + oat_size_; - DCHECK_EQ(static_cast<off_t>(start_offset), out->Seek(0, kSeekCurrent)); - - // Extract the dex file and get the extracted size. - std::string error_msg; - if (!dex_file->ExtractToFile(*file, &error_msg)) { - LOG(ERROR) << "Failed to extract dex file from ZIP entry: " << error_msg - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - if (file->Flush() != 0) { - PLOG(ERROR) << "Failed to flush dex file from ZIP entry." - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - off_t extracted_end = lseek(file->Fd(), 0, SEEK_CUR); - if (extracted_end == static_cast<off_t>(-1)) { - PLOG(ERROR) << "Failed get end offset after writing dex file from ZIP entry." - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - if (extracted_end < static_cast<off_t>(start_offset)) { - LOG(ERROR) << "Dex file end position is before start position! End: " << extracted_end - << " Start: " << start_offset - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - uint64_t extracted_size = static_cast<uint64_t>(extracted_end - start_offset); - if (extracted_size < sizeof(DexFile::Header)) { - LOG(ERROR) << "Extracted dex file is shorter than dex file header. size: " - << extracted_size << " File: " << oat_dex_file->GetLocation(); - return false; - } - - // Read the dex file header and extract required data to OatDexFile. - off_t actual_offset = lseek(file->Fd(), start_offset, SEEK_SET); - if (actual_offset != static_cast<off_t>(start_offset)) { - PLOG(ERROR) << "Failed to seek back to dex file header. Actual: " << actual_offset - << " Expected: " << start_offset - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - if (!ReadDexFileHeader(file, oat_dex_file)) { - return false; - } - if (extracted_size < oat_dex_file->dex_file_size_) { - LOG(ERROR) << "Extracted truncated dex file. Extracted size: " << extracted_size - << " file size from header: " << oat_dex_file->dex_file_size_ - << " File: " << oat_dex_file->GetLocation(); - return false; - } - - // Override the checksum from header with the CRC from ZIP entry. - oat_dex_file->dex_file_location_checksum_ = dex_file->GetCrc32(); - - // Seek both file and stream to the end offset. - size_t end_offset = start_offset + oat_dex_file->dex_file_size_; - actual_offset = lseek(file->Fd(), end_offset, SEEK_SET); - if (actual_offset != static_cast<off_t>(end_offset)) { - PLOG(ERROR) << "Failed to seek to end of dex file. Actual: " << actual_offset - << " Expected: " << end_offset - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - actual_offset = out->Seek(end_offset, kSeekSet); - if (actual_offset != static_cast<off_t>(end_offset)) { - PLOG(ERROR) << "Failed to seek stream to end of dex file. Actual: " << actual_offset - << " Expected: " << end_offset << " File: " << oat_dex_file->GetLocation(); - return false; - } - if (!out->Flush()) { - PLOG(ERROR) << "Failed to flush stream after seeking over dex file." - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - - // If we extracted more than the size specified in the header, truncate the file. - if (extracted_size > oat_dex_file->dex_file_size_) { - if (file->SetLength(end_offset) != 0) { - PLOG(ERROR) << "Failed to truncate excessive dex file length." - << " File: " << oat_dex_file->GetLocation() - << " Output: " << file->GetPath(); - return false; - } - } - - return true; -} - -bool OatWriter::WriteDexFile(OutputStream* out, - File* file, - OatDexFile* oat_dex_file, - File* dex_file) { - size_t start_offset = kIsVdexEnabled ? vdex_size_ : oat_data_offset_ + oat_size_; - DCHECK_EQ(static_cast<off_t>(start_offset), out->Seek(0, kSeekCurrent)); - - off_t input_offset = lseek(dex_file->Fd(), 0, SEEK_SET); - if (input_offset != static_cast<off_t>(0)) { - PLOG(ERROR) << "Failed to seek to dex file header. Actual: " << input_offset - << " Expected: 0" - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - if (!ReadDexFileHeader(dex_file, oat_dex_file)) { - return false; - } - - // Copy the input dex file using sendfile(). - if (!file->Copy(dex_file, 0, oat_dex_file->dex_file_size_)) { - PLOG(ERROR) << "Failed to copy dex file to oat file." - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - if (file->Flush() != 0) { - PLOG(ERROR) << "Failed to flush dex file." - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - - // Check file position and seek the stream to the end offset. - size_t end_offset = start_offset + oat_dex_file->dex_file_size_; - off_t actual_offset = lseek(file->Fd(), 0, SEEK_CUR); - if (actual_offset != static_cast<off_t>(end_offset)) { - PLOG(ERROR) << "Unexpected file position after copying dex file. Actual: " << actual_offset - << " Expected: " << end_offset - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - actual_offset = out->Seek(end_offset, kSeekSet); - if (actual_offset != static_cast<off_t>(end_offset)) { - PLOG(ERROR) << "Failed to seek stream to end of dex file. Actual: " << actual_offset - << " Expected: " << end_offset << " File: " << oat_dex_file->GetLocation(); - return false; - } - if (!out->Flush()) { - PLOG(ERROR) << "Failed to flush stream after seeking over dex file." - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - - return true; -} - -bool OatWriter::WriteDexFile(OutputStream* out, - OatDexFile* oat_dex_file, - const uint8_t* dex_file, - bool update_input_vdex) { - // Note: The raw data has already been checked to contain the header - // and all the data that the header specifies as the file size. - DCHECK(dex_file != nullptr); - DCHECK(ValidateDexFileHeader(dex_file, oat_dex_file->GetLocation())); - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(dex_file); - - if (update_input_vdex) { - // The vdex already contains the dex code, no need to write it again. - } else { - if (!out->WriteFully(dex_file, header->file_size_)) { - PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation() - << " to " << out->GetLocation(); - return false; - } - if (!out->Flush()) { - PLOG(ERROR) << "Failed to flush stream after writing dex file." - << " File: " << oat_dex_file->GetLocation(); - return false; - } - } - - // Update dex file size and resize class offsets in the OatDexFile. - // Note: For raw data, the checksum is passed directly to AddRawDexFileSource(). - // Note: For vdex, the checksum is copied from the existing vdex file. - oat_dex_file->dex_file_size_ = header->file_size_; - oat_dex_file->class_offsets_.resize(header->class_defs_size_); - return true; -} - -bool OatWriter::OpenDexFiles( - File* file, - bool verify, - /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, - /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { - TimingLogger::ScopedTiming split("OpenDexFiles", timings_); - - if (oat_dex_files_.empty()) { - // Nothing to do. - return true; - } - - size_t map_offset = oat_dex_files_[0].dex_file_offset_; - size_t length = kIsVdexEnabled ? (vdex_size_ - map_offset) : (oat_size_ - map_offset); - - std::string error_msg; - std::unique_ptr<MemMap> dex_files_map(MemMap::MapFile( - length, - PROT_READ | PROT_WRITE, - MAP_SHARED, - file->Fd(), - kIsVdexEnabled ? map_offset : (oat_data_offset_ + map_offset), - /* low_4gb */ false, - file->GetPath().c_str(), - &error_msg)); - if (dex_files_map == nullptr) { - LOG(ERROR) << "Failed to mmap() dex files from oat file. File: " << file->GetPath() - << " error: " << error_msg; - return false; - } - std::vector<std::unique_ptr<const DexFile>> dex_files; - for (OatDexFile& oat_dex_file : oat_dex_files_) { - // Make sure no one messed with input files while we were copying data. - // At the very least we need consistent file size and number of class definitions. - const uint8_t* raw_dex_file = - dex_files_map->Begin() + oat_dex_file.dex_file_offset_ - map_offset; - if (!ValidateDexFileHeader(raw_dex_file, oat_dex_file.GetLocation())) { - // Note: ValidateDexFileHeader() already logged an error message. - LOG(ERROR) << "Failed to verify written dex file header!" - << " Output: " << file->GetPath() << " ~ " << std::hex << map_offset - << " ~ " << static_cast<const void*>(raw_dex_file); - return false; - } - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); - if (header->file_size_ != oat_dex_file.dex_file_size_) { - LOG(ERROR) << "File size mismatch in written dex file header! Expected: " - << oat_dex_file.dex_file_size_ << " Actual: " << header->file_size_ - << " Output: " << file->GetPath(); - return false; - } - if (header->class_defs_size_ != oat_dex_file.class_offsets_.size()) { - LOG(ERROR) << "Class defs size mismatch in written dex file header! Expected: " - << oat_dex_file.class_offsets_.size() << " Actual: " << header->class_defs_size_ - << " Output: " << file->GetPath(); - return false; - } - - // Now, open the dex file. - dex_files.emplace_back(DexFile::Open(raw_dex_file, - oat_dex_file.dex_file_size_, - oat_dex_file.GetLocation(), - oat_dex_file.dex_file_location_checksum_, - /* oat_dex_file */ nullptr, - verify, - verify, - &error_msg)); - if (dex_files.back() == nullptr) { - LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation() - << " Error: " << error_msg; - return false; - } - } - - *opened_dex_files_map = std::move(dex_files_map); - *opened_dex_files = std::move(dex_files); - return true; -} - -bool OatWriter::WriteTypeLookupTables( - OutputStream* oat_rodata, - const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files) { - TimingLogger::ScopedTiming split("WriteTypeLookupTables", timings_); - - uint32_t expected_offset = oat_data_offset_ + oat_size_; - off_t actual_offset = oat_rodata->Seek(expected_offset, kSeekSet); - if (static_cast<uint32_t>(actual_offset) != expected_offset) { - PLOG(ERROR) << "Failed to seek to TypeLookupTable section. Actual: " << actual_offset - << " Expected: " << expected_offset << " File: " << oat_rodata->GetLocation(); - return false; - } - - DCHECK_EQ(opened_dex_files.size(), oat_dex_files_.size()); - for (size_t i = 0, size = opened_dex_files.size(); i != size; ++i) { - OatDexFile* oat_dex_file = &oat_dex_files_[i]; - DCHECK_EQ(oat_dex_file->lookup_table_offset_, 0u); - - if (oat_dex_file->create_type_lookup_table_ != CreateTypeLookupTable::kCreate || - oat_dex_file->class_offsets_.empty()) { - continue; - } - - size_t table_size = TypeLookupTable::RawDataLength(oat_dex_file->class_offsets_.size()); - if (table_size == 0u) { - continue; - } - - // Create the lookup table. When `nullptr` is given as the storage buffer, - // TypeLookupTable allocates its own and OatDexFile takes ownership. - const DexFile& dex_file = *opened_dex_files[i]; - { - std::unique_ptr<TypeLookupTable> type_lookup_table = - TypeLookupTable::Create(dex_file, /* storage */ nullptr); - type_lookup_table_oat_dex_files_.push_back( - std::make_unique<art::OatDexFile>(std::move(type_lookup_table))); - dex_file.SetOatDexFile(type_lookup_table_oat_dex_files_.back().get()); - } - TypeLookupTable* const table = type_lookup_table_oat_dex_files_.back()->GetTypeLookupTable(); - - // Type tables are required to be 4 byte aligned. - size_t initial_offset = oat_size_; - size_t rodata_offset = RoundUp(initial_offset, 4); - size_t padding_size = rodata_offset - initial_offset; - - if (padding_size != 0u) { - std::vector<uint8_t> buffer(padding_size, 0u); - if (!oat_rodata->WriteFully(buffer.data(), padding_size)) { - PLOG(ERROR) << "Failed to write lookup table alignment padding." - << " File: " << oat_dex_file->GetLocation() - << " Output: " << oat_rodata->GetLocation(); - return false; - } - } - - DCHECK_EQ(oat_data_offset_ + rodata_offset, - static_cast<size_t>(oat_rodata->Seek(0u, kSeekCurrent))); - DCHECK_EQ(table_size, table->RawDataLength()); - - if (!oat_rodata->WriteFully(table->RawData(), table_size)) { - PLOG(ERROR) << "Failed to write lookup table." - << " File: " << oat_dex_file->GetLocation() - << " Output: " << oat_rodata->GetLocation(); - return false; - } - - oat_dex_file->lookup_table_offset_ = rodata_offset; - - oat_size_ += padding_size + table_size; - size_oat_lookup_table_ += table_size; - size_oat_lookup_table_alignment_ += padding_size; - } - - if (!oat_rodata->Flush()) { - PLOG(ERROR) << "Failed to flush stream after writing type lookup tables." - << " File: " << oat_rodata->GetLocation(); - return false; - } - - return true; -} - -bool OatWriter::WriteDexLayoutSections( - OutputStream* oat_rodata, - const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files) { - TimingLogger::ScopedTiming split(__FUNCTION__, timings_); - - if (!kWriteDexLayoutInfo) { - return true;; - } - - uint32_t expected_offset = oat_data_offset_ + oat_size_; - off_t actual_offset = oat_rodata->Seek(expected_offset, kSeekSet); - if (static_cast<uint32_t>(actual_offset) != expected_offset) { - PLOG(ERROR) << "Failed to seek to dex layout section offset section. Actual: " << actual_offset - << " Expected: " << expected_offset << " File: " << oat_rodata->GetLocation(); - return false; - } - - DCHECK_EQ(opened_dex_files.size(), oat_dex_files_.size()); - size_t rodata_offset = oat_size_; - for (size_t i = 0, size = opened_dex_files.size(); i != size; ++i) { - OatDexFile* oat_dex_file = &oat_dex_files_[i]; - DCHECK_EQ(oat_dex_file->dex_sections_layout_offset_, 0u); - - // Write dex layout section alignment bytes. - const size_t padding_size = - RoundUp(rodata_offset, alignof(DexLayoutSections)) - rodata_offset; - if (padding_size != 0u) { - std::vector<uint8_t> buffer(padding_size, 0u); - if (!oat_rodata->WriteFully(buffer.data(), padding_size)) { - PLOG(ERROR) << "Failed to write lookup table alignment padding." - << " File: " << oat_dex_file->GetLocation() - << " Output: " << oat_rodata->GetLocation(); - return false; - } - size_oat_dex_file_dex_layout_sections_alignment_ += padding_size; - rodata_offset += padding_size; - } - - DCHECK_ALIGNED(rodata_offset, alignof(DexLayoutSections)); - DCHECK_EQ(oat_data_offset_ + rodata_offset, - static_cast<size_t>(oat_rodata->Seek(0u, kSeekCurrent))); - DCHECK(oat_dex_file != nullptr); - if (!oat_rodata->WriteFully(&oat_dex_file->dex_sections_layout_, - sizeof(oat_dex_file->dex_sections_layout_))) { - PLOG(ERROR) << "Failed to write dex layout sections." - << " File: " << oat_dex_file->GetLocation() - << " Output: " << oat_rodata->GetLocation(); - return false; - } - oat_dex_file->dex_sections_layout_offset_ = rodata_offset; - size_oat_dex_file_dex_layout_sections_ += sizeof(oat_dex_file->dex_sections_layout_); - rodata_offset += sizeof(oat_dex_file->dex_sections_layout_); - } - oat_size_ = rodata_offset; - - if (!oat_rodata->Flush()) { - PLOG(ERROR) << "Failed to flush stream after writing type dex layout sections." - << " File: " << oat_rodata->GetLocation(); - return false; - } - - return true; -} - -bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) { - if (!kIsVdexEnabled) { - return true; - } - // Write checksums - off_t actual_offset = vdex_out->Seek(sizeof(VdexFile::Header), kSeekSet); - if (actual_offset != sizeof(VdexFile::Header)) { - PLOG(ERROR) << "Failed to seek to the checksum location of vdex file. Actual: " << actual_offset - << " File: " << vdex_out->GetLocation(); - return false; - } - - for (size_t i = 0, size = oat_dex_files_.size(); i != size; ++i) { - OatDexFile* oat_dex_file = &oat_dex_files_[i]; - if (!vdex_out->WriteFully( - &oat_dex_file->dex_file_location_checksum_, sizeof(VdexFile::VdexChecksum))) { - PLOG(ERROR) << "Failed to write dex file location checksum. File: " - << vdex_out->GetLocation(); - return false; - } - size_vdex_checksums_ += sizeof(VdexFile::VdexChecksum); - } - - // Write header. - actual_offset = vdex_out->Seek(0, kSeekSet); - if (actual_offset != 0) { - PLOG(ERROR) << "Failed to seek to the beginning of vdex file. Actual: " << actual_offset - << " File: " << vdex_out->GetLocation(); - return false; - } - - DCHECK_NE(vdex_dex_files_offset_, 0u); - DCHECK_NE(vdex_verifier_deps_offset_, 0u); - - size_t dex_section_size = vdex_verifier_deps_offset_ - vdex_dex_files_offset_; - size_t verifier_deps_section_size = vdex_quickening_info_offset_ - vdex_verifier_deps_offset_; - size_t quickening_info_section_size = vdex_size_ - vdex_quickening_info_offset_; - - VdexFile::Header vdex_header(oat_dex_files_.size(), - dex_section_size, - verifier_deps_section_size, - quickening_info_section_size); - if (!vdex_out->WriteFully(&vdex_header, sizeof(VdexFile::Header))) { - PLOG(ERROR) << "Failed to write vdex header. File: " << vdex_out->GetLocation(); - return false; - } - size_vdex_header_ = sizeof(VdexFile::Header); - - if (!vdex_out->Flush()) { - PLOG(ERROR) << "Failed to flush stream after writing to vdex file." - << " File: " << vdex_out->GetLocation(); - return false; - } - - return true; -} - -bool OatWriter::WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta) { - return WriteUpTo16BytesAlignment(out, aligned_code_delta, &size_code_alignment_); -} - -bool OatWriter::WriteUpTo16BytesAlignment(OutputStream* out, uint32_t size, uint32_t* stat) { - static const uint8_t kPadding[] = { - 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u - }; - DCHECK_LE(size, sizeof(kPadding)); - if (UNLIKELY(!out->WriteFully(kPadding, size))) { - return false; - } - *stat += size; - return true; -} - -void OatWriter::SetMultiOatRelativePatcherAdjustment() { - DCHECK(dex_files_ != nullptr); - DCHECK(relative_patcher_ != nullptr); - DCHECK_NE(oat_data_offset_, 0u); - if (image_writer_ != nullptr && !dex_files_->empty()) { - // The oat data begin may not be initialized yet but the oat file offset is ready. - size_t oat_index = image_writer_->GetOatIndexForDexFile(dex_files_->front()); - size_t elf_file_offset = image_writer_->GetOatFileOffset(oat_index); - relative_patcher_->StartOatFile(elf_file_offset + oat_data_offset_); - } -} - -OatWriter::OatDexFile::OatDexFile(const char* dex_file_location, - DexFileSource source, - CreateTypeLookupTable create_type_lookup_table) - : source_(source), - create_type_lookup_table_(create_type_lookup_table), - dex_file_size_(0), - offset_(0), - dex_file_location_size_(strlen(dex_file_location)), - dex_file_location_data_(dex_file_location), - dex_file_location_checksum_(0u), - dex_file_offset_(0u), - class_offsets_offset_(0u), - lookup_table_offset_(0u), - method_bss_mapping_offset_(0u), - dex_sections_layout_offset_(0u), - class_offsets_() { -} - -size_t OatWriter::OatDexFile::SizeOf() const { - return sizeof(dex_file_location_size_) - + dex_file_location_size_ - + sizeof(dex_file_location_checksum_) - + sizeof(dex_file_offset_) - + sizeof(class_offsets_offset_) - + sizeof(lookup_table_offset_) - + sizeof(method_bss_mapping_offset_) - + sizeof(dex_sections_layout_offset_); -} - -bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream* out) const { - const size_t file_offset = oat_writer->oat_data_offset_; - 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(); - 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(); - 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(); - 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(); - return false; - } - oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_); - - if (!out->WriteFully(&class_offsets_offset_, sizeof(class_offsets_offset_))) { - PLOG(ERROR) << "Failed to write class offsets offset to " << out->GetLocation(); - return false; - } - oat_writer->size_oat_dex_file_class_offsets_offset_ += sizeof(class_offsets_offset_); - - if (!out->WriteFully(&lookup_table_offset_, sizeof(lookup_table_offset_))) { - PLOG(ERROR) << "Failed to write lookup table offset to " << out->GetLocation(); - return false; - } - oat_writer->size_oat_dex_file_lookup_table_offset_ += sizeof(lookup_table_offset_); - - if (!out->WriteFully(&dex_sections_layout_offset_, sizeof(dex_sections_layout_offset_))) { - PLOG(ERROR) << "Failed to write dex section layout info to " << out->GetLocation(); - return false; - } - oat_writer->size_oat_dex_file_dex_layout_sections_offset_ += sizeof(dex_sections_layout_offset_); - - if (!out->WriteFully(&method_bss_mapping_offset_, sizeof(method_bss_mapping_offset_))) { - PLOG(ERROR) << "Failed to write method bss mapping offset to " << out->GetLocation(); - return false; - } - oat_writer->size_oat_dex_file_method_bss_mapping_offset_ += sizeof(method_bss_mapping_offset_); - - return true; -} - -bool OatWriter::OatDexFile::WriteClassOffsets(OatWriter* oat_writer, OutputStream* out) { - if (!out->WriteFully(class_offsets_.data(), GetClassOffsetsRawSize())) { - PLOG(ERROR) << "Failed to write oat class offsets for " << GetLocation() - << " to " << out->GetLocation(); - return false; - } - oat_writer->size_oat_class_offsets_ += GetClassOffsetsRawSize(); - return true; -} - -OatWriter::OatClass::OatClass(const dchecked_vector<CompiledMethod*>& compiled_methods, - uint32_t compiled_methods_with_code, - uint16_t oat_class_type) - : compiled_methods_(compiled_methods) { - const uint32_t num_methods = compiled_methods.size(); - CHECK_LE(compiled_methods_with_code, num_methods); - - oat_method_offsets_offsets_from_oat_class_.resize(num_methods); - - method_offsets_.resize(compiled_methods_with_code); - method_headers_.resize(compiled_methods_with_code); - - uint32_t oat_method_offsets_offset_from_oat_class = OatClassHeader::SizeOf(); - // We only create this instance if there are at least some compiled. - if (oat_class_type == kOatClassSomeCompiled) { - method_bitmap_.reset(new BitVector(num_methods, false, Allocator::GetMallocAllocator())); - method_bitmap_size_ = method_bitmap_->GetSizeOf(); - oat_method_offsets_offset_from_oat_class += sizeof(method_bitmap_size_); - oat_method_offsets_offset_from_oat_class += method_bitmap_size_; - } else { - method_bitmap_ = nullptr; - method_bitmap_size_ = 0; - } - - for (size_t i = 0; i < num_methods; i++) { - CompiledMethod* compiled_method = compiled_methods_[i]; - if (HasCompiledCode(compiled_method)) { - oat_method_offsets_offsets_from_oat_class_[i] = oat_method_offsets_offset_from_oat_class; - oat_method_offsets_offset_from_oat_class += sizeof(OatMethodOffsets); - if (oat_class_type == kOatClassSomeCompiled) { - method_bitmap_->SetBit(i); - } - } else { - oat_method_offsets_offsets_from_oat_class_[i] = 0; - } - } -} - -size_t OatWriter::OatClass::SizeOf() const { - return ((method_bitmap_size_ == 0) ? 0 : sizeof(method_bitmap_size_)) - + method_bitmap_size_ - + (sizeof(method_offsets_[0]) * method_offsets_.size()); -} - -bool OatWriter::OatClassHeader::Write(OatWriter* oat_writer, - 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(); - 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(); - return false; - } - oat_writer->size_oat_class_type_ += sizeof(type_); - return true; -} - -bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream* out) const { - if (method_bitmap_size_ != 0) { - 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(); - return false; - } - oat_writer->size_oat_class_method_bitmaps_ += method_bitmap_size_; - } - - if (!out->WriteFully(method_offsets_.data(), GetMethodOffsetsRawSize())) { - PLOG(ERROR) << "Failed to write method offsets to " << out->GetLocation(); - return false; - } - oat_writer->size_oat_class_method_offsets_ += GetMethodOffsetsRawSize(); - return true; -} - -const uint8_t* OatWriter::LookupBootImageInternTableSlot(const DexFile& dex_file, - dex::StringIndex string_idx) - NO_THREAD_SAFETY_ANALYSIS { // Single-threaded OatWriter can avoid locking. - uint32_t utf16_length; - const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); - DCHECK_EQ(utf16_length, CountModifiedUtf8Chars(utf8_data)); - InternTable::Utf8String string(utf16_length, - utf8_data, - ComputeUtf16HashFromModifiedUtf8(utf8_data, utf16_length)); - const InternTable* intern_table = Runtime::Current()->GetClassLinker()->intern_table_; - for (const InternTable::Table::UnorderedSet& table : intern_table->strong_interns_.tables_) { - auto it = table.Find(string); - if (it != table.end()) { - return reinterpret_cast<const uint8_t*>(std::addressof(*it)); - } - } - LOG(FATAL) << "Did not find boot image string " << utf8_data; - UNREACHABLE(); -} - -const uint8_t* OatWriter::LookupBootImageClassTableSlot(const DexFile& dex_file, - dex::TypeIndex type_idx) - NO_THREAD_SAFETY_ANALYSIS { // Single-threaded OatWriter can avoid locking. - const char* descriptor = dex_file.StringByTypeIdx(type_idx); - ClassTable::DescriptorHashPair pair(descriptor, ComputeModifiedUtf8Hash(descriptor)); - ClassTable* table = Runtime::Current()->GetClassLinker()->boot_class_table_.get(); - for (const ClassTable::ClassSet& class_set : table->classes_) { - auto it = class_set.Find(pair); - if (it != class_set.end()) { - return reinterpret_cast<const uint8_t*>(std::addressof(*it)); - } - } - LOG(FATAL) << "Did not find boot image class " << descriptor; - UNREACHABLE(); -} - -} // namespace art diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h deleted file mode 100644 index ef0ce52743..0000000000 --- a/compiler/oat_writer.h +++ /dev/null @@ -1,494 +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_OAT_WRITER_H_ -#define ART_COMPILER_OAT_WRITER_H_ - -#include <stdint.h> -#include <cstddef> -#include <memory> - -#include "base/array_ref.h" -#include "base/dchecked_vector.h" -#include "linker/relative_patcher.h" // For linker::RelativePatcherTargetProvider. -#include "mem_map.h" -#include "method_reference.h" -#include "mirror/class.h" -#include "oat.h" -#include "os.h" -#include "safe_map.h" -#include "string_reference.h" -#include "type_reference.h" - -namespace art { - -class BitVector; -class CompiledMethod; -class CompilerDriver; -class ImageWriter; -class ProfileCompilationInfo; -class OutputStream; -class TimingLogger; -class TypeLookupTable; -class VdexFile; -class ZipEntry; - -namespace debug { -struct MethodDebugInfo; -} // namespace debug - -namespace linker { -class MultiOatRelativePatcher; -} // namespace linker - -namespace verifier { - class VerifierDeps; -} // namespace verifier - -// OatHeader variable length with count of D OatDexFiles -// -// TypeLookupTable[0] one descriptor to class def index hash table for each OatDexFile. -// TypeLookupTable[1] -// ... -// TypeLookupTable[D] -// -// ClassOffsets[0] one table of OatClass offsets for each class def for each OatDexFile. -// ClassOffsets[1] -// ... -// ClassOffsets[D] -// -// OatClass[0] one variable sized OatClass for each of C DexFile::ClassDefs -// OatClass[1] contains OatClass entries with class status, offsets to code, etc. -// ... -// OatClass[C] -// -// MethodBssMapping one variable sized MethodBssMapping for each dex file, optional. -// MethodBssMapping -// ... -// MethodBssMapping -// -// VmapTable one variable sized VmapTable blob (CodeInfo or QuickeningInfo). -// VmapTable VmapTables are deduplicated. -// ... -// VmapTable -// -// MethodInfo one variable sized blob with MethodInfo. -// MethodInfo MethodInfos are deduplicated. -// ... -// MethodInfo -// -// OatDexFile[0] one variable sized OatDexFile with offsets to Dex and OatClasses -// OatDexFile[1] -// ... -// OatDexFile[D] -// -// padding if necessary so that the following code will be page aligned -// -// OatMethodHeader fixed size header for a CompiledMethod including the size of the MethodCode. -// MethodCode one variable sized blob with the code of a CompiledMethod. -// OatMethodHeader (OatMethodHeader, MethodCode) pairs are deduplicated. -// MethodCode -// ... -// OatMethodHeader -// MethodCode -// -class OatWriter { - public: - enum class CreateTypeLookupTable { - kCreate, - kDontCreate, - kDefault = kCreate - }; - - OatWriter(bool compiling_boot_image, TimingLogger* timings, ProfileCompilationInfo* info); - - // To produce a valid oat file, the user must first add sources with any combination of - // - AddDexFileSource(), - // - AddZippedDexFilesSource(), - // - AddRawDexFileSource(), - // - AddVdexDexFilesSource(). - // Then the user must call in order - // - WriteAndOpenDexFiles() - // - Initialize() - // - WriteVerifierDeps() - // - WriteQuickeningInfo() - // - WriteChecksumsAndVdexHeader() - // - PrepareLayout(), - // - WriteRodata(), - // - WriteCode(), - // - WriteHeader(). - - // Add dex file source(s) from a file, either a plain dex file or - // a zip file with one or more dex files. - bool AddDexFileSource( - const char* filename, - const char* location, - CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); - // Add dex file source(s) from a zip file specified by a file handle. - bool AddZippedDexFilesSource( - File&& zip_fd, - const char* location, - CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); - // Add dex file source from raw memory. - bool AddRawDexFileSource( - const ArrayRef<const uint8_t>& data, - const char* location, - uint32_t location_checksum, - CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); - // Add dex file source(s) from a vdex file. - bool AddVdexDexFilesSource( - const VdexFile& vdex_file, - const char* location, - CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); - dchecked_vector<std::string> GetSourceLocations() const; - - // Write raw dex files to the vdex file, mmap the file and open the dex files from it. - // Supporting data structures are written into the .rodata section of the oat file. - // The `verify` setting dictates whether the dex file verifier should check the dex files. - // This is generally the case, and should only be false for tests. - // If `update_input_vdex` is true, then this method won't actually write the dex files, - // and the compiler will just re-use the existing vdex file. - bool WriteAndOpenDexFiles(File* vdex_file, - OutputStream* oat_rodata, - InstructionSet instruction_set, - const InstructionSetFeatures* instruction_set_features, - SafeMap<std::string, std::string>* key_value_store, - bool verify, - bool update_input_vdex, - /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, - /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files); - bool WriteQuickeningInfo(OutputStream* vdex_out); - bool WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps); - bool WriteChecksumsAndVdexHeader(OutputStream* vdex_out); - // Initialize the writer with the given parameters. - void Initialize(const CompilerDriver* compiler, - ImageWriter* image_writer, - const std::vector<const DexFile*>& dex_files) { - compiler_driver_ = compiler; - image_writer_ = image_writer; - dex_files_ = &dex_files; - } - - // Prepare layout of remaining data. - void PrepareLayout(linker::MultiOatRelativePatcher* relative_patcher); - // Write the rest of .rodata section (ClassOffsets[], OatClass[], maps). - bool WriteRodata(OutputStream* out); - // Write the code to the .text section. - bool WriteCode(OutputStream* out); - // Write the oat header. This finalizes the oat file. - bool WriteHeader(OutputStream* out, - uint32_t image_file_location_oat_checksum, - uintptr_t image_file_location_oat_begin, - int32_t image_patch_delta); - - // Returns whether the oat file has an associated image. - bool HasImage() const { - // Since the image is being created at the same time as the oat file, - // check if there's an image writer. - return image_writer_ != nullptr; - } - - bool HasBootImage() const { - return compiling_boot_image_; - } - - const OatHeader& GetOatHeader() const { - return *oat_header_; - } - - size_t GetOatSize() const { - return oat_size_; - } - - size_t GetBssSize() const { - return bss_size_; - } - - size_t GetBssMethodsOffset() const { - return bss_methods_offset_; - } - - size_t GetBssRootsOffset() const { - return bss_roots_offset_; - } - - size_t GetOatDataOffset() const { - return oat_data_offset_; - } - - ~OatWriter(); - - ArrayRef<const debug::MethodDebugInfo> GetMethodDebugInfo() const { - return ArrayRef<const debug::MethodDebugInfo>(method_info_); - } - - const CompilerDriver* GetCompilerDriver() const { - return compiler_driver_; - } - - private: - class DexFileSource; - class OatClassHeader; - class OatClass; - class OatDexFile; - - // The function VisitDexMethods() below iterates through all the methods in all - // the compiled dex files in order of their definitions. The method visitor - // classes provide individual bits of processing for each of the passes we need to - // first collect the data we want to write to the oat file and then, in later passes, - // to actually write it. - class DexMethodVisitor; - class OatDexMethodVisitor; - class InitBssLayoutMethodVisitor; - class InitOatClassesMethodVisitor; - class InitCodeMethodVisitor; - class InitMapMethodVisitor; - class InitMethodInfoVisitor; - class InitImageMethodVisitor; - class WriteCodeMethodVisitor; - class WriteMapMethodVisitor; - class WriteMethodInfoVisitor; - class WriteQuickeningInfoMethodVisitor; - class WriteQuickeningIndicesMethodVisitor; - - // Visit all the methods in all the compiled dex files in their definition order - // with a given DexMethodVisitor. - bool VisitDexMethods(DexMethodVisitor* visitor); - - // If `update_input_vdex` is true, then this method won't actually write the dex files, - // and the compiler will just re-use the existing vdex file. - bool WriteDexFiles(OutputStream* out, File* file, bool update_input_vdex); - bool WriteDexFile(OutputStream* out, - File* file, - OatDexFile* oat_dex_file, - bool update_input_vdex); - bool SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file); - bool LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file); - bool WriteDexFile(OutputStream* out, - File* file, - OatDexFile* oat_dex_file, - ZipEntry* dex_file); - bool WriteDexFile(OutputStream* out, - File* file, - OatDexFile* oat_dex_file, - File* dex_file); - bool WriteDexFile(OutputStream* out, - OatDexFile* oat_dex_file, - const uint8_t* dex_file, - bool update_input_vdex); - bool OpenDexFiles(File* file, - bool verify, - /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, - /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files); - - size_t InitOatHeader(InstructionSet instruction_set, - const InstructionSetFeatures* instruction_set_features, - uint32_t num_dex_files, - SafeMap<std::string, std::string>* key_value_store); - size_t InitClassOffsets(size_t offset); - size_t InitOatClasses(size_t offset); - size_t InitOatMaps(size_t offset); - size_t InitMethodBssMappings(size_t offset); - size_t InitOatDexFiles(size_t offset); - size_t InitOatCode(size_t offset); - size_t InitOatCodeDexFiles(size_t offset); - void InitBssLayout(InstructionSet instruction_set); - - size_t WriteClassOffsets(OutputStream* out, size_t file_offset, size_t relative_offset); - size_t WriteClasses(OutputStream* out, size_t file_offset, size_t relative_offset); - size_t WriteMaps(OutputStream* out, size_t file_offset, size_t relative_offset); - size_t WriteMethodBssMappings(OutputStream* out, size_t file_offset, size_t relative_offset); - size_t WriteOatDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset); - size_t WriteCode(OutputStream* out, size_t file_offset, size_t relative_offset); - size_t WriteCodeDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset); - - bool RecordOatDataOffset(OutputStream* out); - bool ReadDexFileHeader(File* oat_file, OatDexFile* oat_dex_file); - bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location); - bool WriteTypeLookupTables(OutputStream* oat_rodata, - const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files); - bool WriteDexLayoutSections(OutputStream* oat_rodata, - const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files); - bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta); - bool WriteUpTo16BytesAlignment(OutputStream* out, uint32_t size, uint32_t* stat); - void SetMultiOatRelativePatcherAdjustment(); - void CloseSources(); - - bool MayHaveCompiledMethods() const; - - // Find the address of the GcRoot<String> in the InternTable for a boot image string. - const uint8_t* LookupBootImageInternTableSlot(const DexFile& dex_file, - dex::StringIndex string_idx); - // Find the address of the ClassTable::TableSlot for a boot image class. - const uint8_t* LookupBootImageClassTableSlot(const DexFile& dex_file, dex::TypeIndex type_idx); - - enum class WriteState { - kAddingDexFileSources, - kPrepareLayout, - kWriteRoData, - kWriteText, - kWriteHeader, - kDone - }; - - WriteState write_state_; - TimingLogger* timings_; - - std::vector<std::unique_ptr<File>> raw_dex_files_; - std::vector<std::unique_ptr<ZipArchive>> zip_archives_; - std::vector<std::unique_ptr<ZipEntry>> zipped_dex_files_; - - // Using std::list<> which doesn't move elements around on push/emplace_back(). - // We need this because we keep plain pointers to the strings' c_str(). - std::list<std::string> zipped_dex_file_locations_; - - dchecked_vector<debug::MethodDebugInfo> method_info_; - - const CompilerDriver* compiler_driver_; - ImageWriter* image_writer_; - const bool compiling_boot_image_; - - // note OatFile does not take ownership of the DexFiles - const std::vector<const DexFile*>* dex_files_; - - // Size required for Vdex data structures. - size_t vdex_size_; - - // Offset of section holding Dex files inside Vdex. - size_t vdex_dex_files_offset_; - - // Offset of section holding VerifierDeps inside Vdex. - size_t vdex_verifier_deps_offset_; - - // Offset of section holding quickening info inside Vdex. - size_t vdex_quickening_info_offset_; - - // Size required for Oat data structures. - size_t oat_size_; - - // The start of the required .bss section. - size_t bss_start_; - - // The size of the required .bss section holding the DexCache data and GC roots. - size_t bss_size_; - - // The offset of the methods in .bss section. - size_t bss_methods_offset_; - - // The offset of the GC roots in .bss section. - size_t bss_roots_offset_; - - // Map for recording references to ArtMethod entries in .bss. - SafeMap<const DexFile*, BitVector> bss_method_entry_references_; - - // Map for allocating ArtMethod entries in .bss. Indexed by MethodReference for the target - // method in the dex file with the "method reference value comparator" for deduplication. - // The value is the target offset for patching, starting at `bss_start_ + bss_methods_offset_`. - SafeMap<MethodReference, size_t, MethodReferenceValueComparator> bss_method_entries_; - - // Map for allocating Class entries in .bss. Indexed by TypeReference for the source - // type in the dex file with the "type value comparator" for deduplication. The value - // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`. - SafeMap<TypeReference, size_t, TypeReferenceValueComparator> bss_type_entries_; - - // Map for allocating String entries in .bss. Indexed by StringReference for the source - // string in the dex file with the "string value comparator" for deduplication. The value - // is the target offset for patching, starting at `bss_start_ + bss_roots_offset_`. - SafeMap<StringReference, size_t, StringReferenceValueComparator> bss_string_entries_; - - // Whether boot image tables should be mapped to the .bss. This is needed for compiled - // code that reads from these tables with PC-relative instructions. - bool map_boot_image_tables_to_bss_; - - // Offset of the oat data from the start of the mmapped region of the elf file. - size_t oat_data_offset_; - - // Fake OatDexFiles to hold type lookup tables for the compiler. - std::vector<std::unique_ptr<art::OatDexFile>> type_lookup_table_oat_dex_files_; - - // data to write - std::unique_ptr<OatHeader> oat_header_; - dchecked_vector<OatDexFile> oat_dex_files_; - dchecked_vector<OatClassHeader> oat_class_headers_; - dchecked_vector<OatClass> oat_classes_; - std::unique_ptr<const std::vector<uint8_t>> jni_dlsym_lookup_; - std::unique_ptr<const std::vector<uint8_t>> quick_generic_jni_trampoline_; - std::unique_ptr<const std::vector<uint8_t>> quick_imt_conflict_trampoline_; - std::unique_ptr<const std::vector<uint8_t>> quick_resolution_trampoline_; - std::unique_ptr<const std::vector<uint8_t>> quick_to_interpreter_bridge_; - - // output stats - uint32_t size_vdex_header_; - uint32_t size_vdex_checksums_; - uint32_t size_dex_file_alignment_; - uint32_t size_executable_offset_alignment_; - uint32_t size_oat_header_; - uint32_t size_oat_header_key_value_store_; - uint32_t size_dex_file_; - uint32_t size_verifier_deps_; - uint32_t size_verifier_deps_alignment_; - uint32_t size_quickening_info_; - uint32_t size_quickening_info_alignment_; - uint32_t size_interpreter_to_interpreter_bridge_; - uint32_t size_interpreter_to_compiled_code_bridge_; - uint32_t size_jni_dlsym_lookup_; - uint32_t size_quick_generic_jni_trampoline_; - uint32_t size_quick_imt_conflict_trampoline_; - uint32_t size_quick_resolution_trampoline_; - uint32_t size_quick_to_interpreter_bridge_; - uint32_t size_trampoline_alignment_; - uint32_t size_method_header_; - uint32_t size_code_; - uint32_t size_code_alignment_; - uint32_t size_relative_call_thunks_; - uint32_t size_misc_thunks_; - uint32_t size_vmap_table_; - uint32_t size_method_info_; - uint32_t size_oat_dex_file_location_size_; - uint32_t size_oat_dex_file_location_data_; - uint32_t size_oat_dex_file_location_checksum_; - uint32_t size_oat_dex_file_offset_; - uint32_t size_oat_dex_file_class_offsets_offset_; - uint32_t size_oat_dex_file_lookup_table_offset_; - uint32_t size_oat_dex_file_dex_layout_sections_offset_; - uint32_t size_oat_dex_file_dex_layout_sections_; - uint32_t size_oat_dex_file_dex_layout_sections_alignment_; - uint32_t size_oat_dex_file_method_bss_mapping_offset_; - uint32_t size_oat_lookup_table_alignment_; - uint32_t size_oat_lookup_table_; - uint32_t size_oat_class_offsets_alignment_; - uint32_t size_oat_class_offsets_; - uint32_t size_oat_class_type_; - uint32_t size_oat_class_status_; - uint32_t size_oat_class_method_bitmaps_; - uint32_t size_oat_class_method_offsets_; - uint32_t size_method_bss_mappings_; - - // The helper for processing relative patches is external so that we can patch across oat files. - linker::MultiOatRelativePatcher* relative_patcher_; - - // The locations of absolute patches relative to the start of the executable section. - dchecked_vector<uintptr_t> absolute_patch_locations_; - - // Profile info used to generate new layout of files. - ProfileCompilationInfo* profile_compilation_info_; - - DISALLOW_COPY_AND_ASSIGN(OatWriter); -}; - -} // namespace art - -#endif // ART_COMPILER_OAT_WRITER_H_ diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 1e5f1ec00f..6533e2b9f7 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -288,7 +288,8 @@ void CodeGenerator::Finalize(CodeAllocator* allocator) { GetAssembler()->FinalizeInstructions(code); } -void CodeGenerator::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches ATTRIBUTE_UNUSED) { +void CodeGenerator::EmitLinkerPatches( + ArenaVector<linker::LinkerPatch>* linker_patches ATTRIBUTE_UNUSED) { // No linker patches by default. } diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 30c2b52242..4b4abdfaa3 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -61,9 +61,12 @@ class Assembler; class CodeGenerator; class CompilerDriver; class CompilerOptions; -class LinkerPatch; class ParallelMoveResolver; +namespace linker { +class LinkerPatch; +} // namespace linker + class CodeAllocator { public: CodeAllocator() {} @@ -205,7 +208,7 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { virtual void Initialize() = 0; virtual void Finalize(CodeAllocator* allocator); - virtual void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches); + virtual void EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches); virtual void GenerateFrameEntry() = 0; virtual void GenerateFrameExit() = 0; virtual void Bind(HBasicBlock* block) = 0; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index c61ef0a0bc..aaea7c1025 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -27,9 +27,11 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gc/accounting/card_table.h" +#include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_arm64.h" #include "linker/arm64/relative_patcher_arm64.h" +#include "linker/linker_patch.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" @@ -4753,10 +4755,10 @@ void CodeGeneratorARM64::EmitLdrOffsetPlaceholder(vixl::aarch64::Label* fixup_la __ ldr(out, MemOperand(base, /* offset placeholder */ 0)); } -template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> +template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> inline void CodeGeneratorARM64::EmitPcRelativeLinkerPatches( const ArenaDeque<PcRelativePatchInfo>& infos, - ArenaVector<LinkerPatch>* linker_patches) { + ArenaVector<linker::LinkerPatch>* linker_patches) { for (const PcRelativePatchInfo& info : infos) { linker_patches->push_back(Factory(info.label.GetLocation(), &info.target_dex_file, @@ -4765,7 +4767,7 @@ inline void CodeGeneratorARM64::EmitPcRelativeLinkerPatches( } } -void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { +void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) { DCHECK(linker_patches->empty()); size_t size = pc_relative_method_patches_.size() + @@ -4777,28 +4779,28 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patc baker_read_barrier_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeMethodPatch>(pc_relative_method_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_, - linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>( + pc_relative_method_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>( + pc_relative_type_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>( + pc_relative_string_patches_, linker_patches); } else { DCHECK(pc_relative_method_patches_.empty()); - EmitPcRelativeLinkerPatches<LinkerPatch::TypeClassTablePatch>(pc_relative_type_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::StringInternTablePatch>(pc_relative_string_patches_, - linker_patches); - } - EmitPcRelativeLinkerPatches<LinkerPatch::MethodBssEntryPatch>(method_bss_entry_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(string_bss_entry_patches_, - linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>( + pc_relative_type_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>( + pc_relative_string_patches_, linker_patches); + } + EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>( + method_bss_entry_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>( + type_bss_entry_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>( + string_bss_entry_patches_, linker_patches); for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) { - linker_patches->push_back(LinkerPatch::BakerReadBarrierBranchPatch(info.label.GetLocation(), - info.custom_data)); + linker_patches->push_back(linker::LinkerPatch::BakerReadBarrierBranchPatch( + info.label.GetLocation(), info.custom_data)); } DCHECK_EQ(size, linker_patches->size()); } diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 69c511907e..cebdaa102c 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -627,7 +627,7 @@ class CodeGeneratorARM64 : public CodeGenerator { vixl::aarch64::Register out, vixl::aarch64::Register base); - void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) OVERRIDE; void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; @@ -805,9 +805,9 @@ class CodeGeneratorARM64 : public CodeGenerator { void EmitJumpTables(); - template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> + template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> static void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos, - ArenaVector<LinkerPatch>* linker_patches); + ArenaVector<linker::LinkerPatch>* linker_patches); // Labels for each block that will be compiled. // We use a deque so that the `vixl::aarch64::Label` objects do not move in memory. diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 6147259bd3..e1ea08073f 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -27,8 +27,10 @@ #include "compiled_method.h" #include "entrypoints/quick/quick_entrypoints.h" #include "gc/accounting/card_table.h" +#include "heap_poisoning.h" #include "intrinsics_arm_vixl.h" #include "linker/arm/relative_patcher_thumb2.h" +#include "linker/linker_patch.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" #include "thread.h" @@ -9190,10 +9192,10 @@ VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFil }); } -template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> +template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches( const ArenaDeque<PcRelativePatchInfo>& infos, - ArenaVector<LinkerPatch>* linker_patches) { + ArenaVector<linker::LinkerPatch>* linker_patches) { for (const PcRelativePatchInfo& info : infos) { const DexFile& dex_file = info.target_dex_file; size_t offset_or_index = info.offset_or_index; @@ -9210,7 +9212,7 @@ inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches( } } -void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { +void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) { DCHECK(linker_patches->empty()); size_t size = /* MOVW+MOVT for each entry */ 2u * pc_relative_method_patches_.size() + @@ -9222,28 +9224,28 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pa baker_read_barrier_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeMethodPatch>(pc_relative_method_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_, - linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>( + pc_relative_method_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>( + pc_relative_type_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>( + pc_relative_string_patches_, linker_patches); } else { DCHECK(pc_relative_method_patches_.empty()); - EmitPcRelativeLinkerPatches<LinkerPatch::TypeClassTablePatch>(pc_relative_type_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::StringInternTablePatch>(pc_relative_string_patches_, - linker_patches); - } - EmitPcRelativeLinkerPatches<LinkerPatch::MethodBssEntryPatch>(method_bss_entry_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(string_bss_entry_patches_, - linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>( + pc_relative_type_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>( + pc_relative_string_patches_, linker_patches); + } + EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>( + method_bss_entry_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>( + type_bss_entry_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>( + string_bss_entry_patches_, linker_patches); for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) { - linker_patches->push_back(LinkerPatch::BakerReadBarrierBranchPatch(info.label.GetLocation(), - info.custom_data)); + linker_patches->push_back(linker::LinkerPatch::BakerReadBarrierBranchPatch( + info.label.GetLocation(), info.custom_data)); } DCHECK_EQ(size, linker_patches->size()); } diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index e78bc15614..337ecf1163 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -594,7 +594,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { dex::TypeIndex type_index, Handle<mirror::Class> handle); - void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) OVERRIDE; void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; @@ -778,9 +778,9 @@ class CodeGeneratorARMVIXL : public CodeGenerator { PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches); - template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> + template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> static void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos, - ArenaVector<LinkerPatch>* linker_patches); + ArenaVector<linker::LinkerPatch>* linker_patches); // Labels for each block that will be compiled. // We use a deque so that the `vixl::aarch32::Label` objects do not move in memory. diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 9db2bd35ca..0e6d210f10 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -26,8 +26,10 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gc/accounting/card_table.h" +#include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_mips.h" +#include "linker/linker_patch.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" #include "offsets.h" @@ -289,7 +291,9 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { // For non-Baker read barriers we need to re-calculate the address of // the class entry. const bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6(); - Register base = isR6 ? ZERO : locations->InAt(0).AsRegister<Register>(); + const bool has_irreducible_loops = codegen->GetGraph()->HasIrreducibleLoops(); + Register base = + (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister<Register>(); CodeGeneratorMIPS::PcRelativePatchInfo* info_high = mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = @@ -379,7 +383,9 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { // For non-Baker read barriers we need to re-calculate the address of // the string entry. const bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6(); - Register base = isR6 ? ZERO : locations->InAt(0).AsRegister<Register>(); + const bool has_irreducible_loops = codegen->GetGraph()->HasIrreducibleLoops(); + Register base = + (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister<Register>(); CodeGeneratorMIPS::PcRelativePatchInfo* info_high = mips_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = @@ -1627,10 +1633,10 @@ void CodeGeneratorMIPS::AddLocationAsTemp(Location location, LocationSummary* lo } } -template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> +template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> inline void CodeGeneratorMIPS::EmitPcRelativeLinkerPatches( const ArenaDeque<PcRelativePatchInfo>& infos, - ArenaVector<LinkerPatch>* linker_patches) { + ArenaVector<linker::LinkerPatch>* linker_patches) { for (const PcRelativePatchInfo& info : infos) { const DexFile& dex_file = info.target_dex_file; size_t offset_or_index = info.offset_or_index; @@ -1646,7 +1652,7 @@ inline void CodeGeneratorMIPS::EmitPcRelativeLinkerPatches( } } -void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { +void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) { DCHECK(linker_patches->empty()); size_t size = pc_relative_method_patches_.size() + @@ -1657,25 +1663,25 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patch string_bss_entry_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeMethodPatch>(pc_relative_method_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_, - linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>( + pc_relative_method_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>( + pc_relative_type_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>( + pc_relative_string_patches_, linker_patches); } else { DCHECK(pc_relative_method_patches_.empty()); - EmitPcRelativeLinkerPatches<LinkerPatch::TypeClassTablePatch>(pc_relative_type_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::StringInternTablePatch>(pc_relative_string_patches_, - linker_patches); - } - EmitPcRelativeLinkerPatches<LinkerPatch::MethodBssEntryPatch>(method_bss_entry_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(string_bss_entry_patches_, - linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>( + pc_relative_type_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>( + pc_relative_string_patches_, linker_patches); + } + EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>( + method_bss_entry_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>( + type_bss_entry_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>( + string_bss_entry_patches_, linker_patches); DCHECK_EQ(size, linker_patches->size()); } @@ -7331,7 +7337,8 @@ void LocationsBuilderMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invo DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); bool is_r6 = codegen_->GetInstructionSetFeatures().IsR6(); - bool has_extra_input = invoke->HasPcRelativeMethodLoadKind() && !is_r6; + bool has_irreducible_loops = codegen_->GetGraph()->HasIrreducibleLoops(); + bool has_extra_input = invoke->HasPcRelativeMethodLoadKind() && !is_r6 && !has_irreducible_loops; IntrinsicLocationsBuilderMIPS intrinsic(codegen_); if (intrinsic.TryDispatch(invoke)) { @@ -7369,75 +7376,49 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen HLoadString::LoadKind CodeGeneratorMIPS::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { - // We disable PC-relative load on pre-R6 when there is an irreducible loop, as the optimization - // is incompatible with it. - // TODO: Create as many HMipsComputeBaseMethodAddress instructions as needed for methods - // with irreducible loops. - bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops(); - bool is_r6 = GetInstructionSetFeatures().IsR6(); - bool fallback_load = has_irreducible_loops && !is_r6; switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: case HLoadString::LoadKind::kBootImageInternTable: case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; - case HLoadString::LoadKind::kBootImageAddress: - break; case HLoadString::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); - fallback_load = false; break; + case HLoadString::LoadKind::kBootImageAddress: case HLoadString::LoadKind::kRuntimeCall: - fallback_load = false; break; } - if (fallback_load) { - desired_string_load_kind = HLoadString::LoadKind::kRuntimeCall; - } return desired_string_load_kind; } HLoadClass::LoadKind CodeGeneratorMIPS::GetSupportedLoadClassKind( HLoadClass::LoadKind desired_class_load_kind) { - // We disable PC-relative load on pre-R6 when there is an irreducible loop, as the optimization - // is incompatible with it. - // TODO: Create as many HMipsComputeBaseMethodAddress instructions as needed for methods - // with irreducible loops. - bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops(); - bool is_r6 = GetInstructionSetFeatures().IsR6(); - bool fallback_load = has_irreducible_loops && !is_r6; switch (desired_class_load_kind) { case HLoadClass::LoadKind::kInvalid: LOG(FATAL) << "UNREACHABLE"; UNREACHABLE(); case HLoadClass::LoadKind::kReferrersClass: - fallback_load = false; break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: case HLoadClass::LoadKind::kBootImageClassTable: case HLoadClass::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; - case HLoadClass::LoadKind::kBootImageAddress: - break; case HLoadClass::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); - fallback_load = false; break; + case HLoadClass::LoadKind::kBootImageAddress: case HLoadClass::LoadKind::kRuntimeCall: - fallback_load = false; break; } - if (fallback_load) { - desired_class_load_kind = HLoadClass::LoadKind::kRuntimeCall; - } return desired_class_load_kind; } Register CodeGeneratorMIPS::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp) { CHECK(!GetInstructionSetFeatures().IsR6()); + CHECK(!GetGraph()->HasIrreducibleLoops()); CHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u); Location location = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); if (!invoke->GetLocations()->Intrinsified()) { @@ -7465,27 +7446,7 @@ Register CodeGeneratorMIPS::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticO HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticOrDirectDispatch( const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) { - HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info; - // We disable PC-relative load on pre-R6 when there is an irreducible loop, as the optimization - // is incompatible with it. - // TODO: Create as many HMipsComputeBaseMethodAddress instructions as needed for methods - // with irreducible loops. - bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops(); - bool is_r6 = GetInstructionSetFeatures().IsR6(); - bool fallback_load = has_irreducible_loops && !is_r6; - switch (dispatch_info.method_load_kind) { - case HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative: - case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: - break; - default: - fallback_load = false; - break; - } - if (fallback_load) { - dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall; - dispatch_info.method_load_data = 0; - } - return dispatch_info; + return desired_dispatch_info; } void CodeGeneratorMIPS::GenerateStaticOrDirectCall( @@ -7495,7 +7456,8 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall( HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind(); HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation(); bool is_r6 = GetInstructionSetFeatures().IsR6(); - Register base_reg = (invoke->HasPcRelativeMethodLoadKind() && !is_r6) + bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops(); + Register base_reg = (invoke->HasPcRelativeMethodLoadKind() && !is_r6 && !has_irreducible_loops) ? GetInvokeStaticOrDirectExtraParameter(invoke, temp.AsRegister<Register>()) : ZERO; @@ -7634,6 +7596,7 @@ void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) { } DCHECK(!cls->NeedsAccessCheck()); const bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); + const bool has_irreducible_loops = codegen_->GetGraph()->HasIrreducibleLoops(); const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage(); LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier) ? LocationSummary::kCallOnSlowPath @@ -7651,6 +7614,10 @@ void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) { if (isR6) { break; } + if (has_irreducible_loops) { + codegen_->ClobberRA(); + break; + } FALLTHROUGH_INTENDED; case HLoadClass::LoadKind::kReferrersClass: locations->SetInAt(0, Location::RequiresRegister()); @@ -7689,12 +7656,15 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF Register out = out_loc.AsRegister<Register>(); Register base_or_current_method_reg; bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); + bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops(); switch (load_kind) { // We need an extra register for PC-relative literals on R2. case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: case HLoadClass::LoadKind::kBootImageAddress: + case HLoadClass::LoadKind::kBootImageClassTable: case HLoadClass::LoadKind::kBssEntry: - base_or_current_method_reg = isR6 ? ZERO : locations->InAt(0).AsRegister<Register>(); + base_or_current_method_reg = + (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister<Register>(); break; case HLoadClass::LoadKind::kReferrersClass: case HLoadClass::LoadKind::kRuntimeCall: @@ -7740,9 +7710,13 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF uint32_t address = dchecked_integral_cast<uint32_t>( reinterpret_cast<uintptr_t>(cls->GetClass().Get())); DCHECK_NE(address, 0u); - __ LoadLiteral(out, - base_or_current_method_reg, - codegen_->DeduplicateBootImageAddressLiteral(address)); + if (isR6 || !has_irreducible_loops) { + __ LoadLiteral(out, + base_or_current_method_reg, + codegen_->DeduplicateBootImageAddressLiteral(address)); + } else { + __ LoadConst32(out, address); + } break; } case HLoadClass::LoadKind::kBootImageClassTable: { @@ -7847,6 +7821,7 @@ void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); HLoadString::LoadKind load_kind = load->GetLoadKind(); const bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); + const bool has_irreducible_loops = codegen_->GetGraph()->HasIrreducibleLoops(); switch (load_kind) { // We need an extra register for PC-relative literals on R2. case HLoadString::LoadKind::kBootImageAddress: @@ -7856,6 +7831,10 @@ void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) { if (isR6) { break; } + if (has_irreducible_loops) { + codegen_->ClobberRA(); + break; + } FALLTHROUGH_INTENDED; // We need an extra register for PC-relative dex cache accesses. case HLoadString::LoadKind::kRuntimeCall: @@ -7894,13 +7873,15 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ Register out = out_loc.AsRegister<Register>(); Register base_or_current_method_reg; bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); + bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops(); switch (load_kind) { // We need an extra register for PC-relative literals on R2. case HLoadString::LoadKind::kBootImageAddress: case HLoadString::LoadKind::kBootImageLinkTimePcRelative: case HLoadString::LoadKind::kBootImageInternTable: case HLoadString::LoadKind::kBssEntry: - base_or_current_method_reg = isR6 ? ZERO : locations->InAt(0).AsRegister<Register>(); + base_or_current_method_reg = + (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister<Register>(); break; default: base_or_current_method_reg = ZERO; @@ -7924,9 +7905,13 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ uint32_t address = dchecked_integral_cast<uint32_t>( reinterpret_cast<uintptr_t>(load->GetString().Get())); DCHECK_NE(address, 0u); - __ LoadLiteral(out, - base_or_current_method_reg, - codegen_->DeduplicateBootImageAddressLiteral(address)); + if (isR6 || !has_irreducible_loops) { + __ LoadLiteral(out, + base_or_current_method_reg, + codegen_->DeduplicateBootImageAddressLiteral(address)); + } else { + __ LoadConst32(out, address); + } return; } case HLoadString::LoadKind::kBootImageInternTable: { diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index f15f8c672a..2b1075d12b 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -395,7 +395,7 @@ class CodeGeneratorMIPS : public CodeGenerator { const MipsAssembler& GetAssembler() const OVERRIDE { return assembler_; } // Emit linker patches. - void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) OVERRIDE; void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; // Fast path implementation of ReadBarrier::Barrier for a heap @@ -679,9 +679,9 @@ class CodeGeneratorMIPS : public CodeGenerator { const PcRelativePatchInfo* info_high, ArenaDeque<PcRelativePatchInfo>* patches); - template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> + template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos, - ArenaVector<LinkerPatch>* linker_patches); + ArenaVector<linker::LinkerPatch>* linker_patches); // Labels for each block that will be compiled. MipsLabel* block_labels_; diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index a27cbce3db..119e0f6b76 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -24,8 +24,10 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gc/accounting/card_table.h" +#include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_mips64.h" +#include "linker/linker_patch.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" #include "offsets.h" @@ -1540,10 +1542,10 @@ void CodeGeneratorMIPS64::MarkGCCard(GpuRegister object, } } -template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> +template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> inline void CodeGeneratorMIPS64::EmitPcRelativeLinkerPatches( const ArenaDeque<PcRelativePatchInfo>& infos, - ArenaVector<LinkerPatch>* linker_patches) { + ArenaVector<linker::LinkerPatch>* linker_patches) { for (const PcRelativePatchInfo& info : infos) { const DexFile& dex_file = info.target_dex_file; size_t offset_or_index = info.offset_or_index; @@ -1555,7 +1557,7 @@ inline void CodeGeneratorMIPS64::EmitPcRelativeLinkerPatches( } } -void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { +void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) { DCHECK(linker_patches->empty()); size_t size = pc_relative_method_patches_.size() + @@ -1566,25 +1568,25 @@ void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pat string_bss_entry_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeMethodPatch>(pc_relative_method_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_, - linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>( + pc_relative_method_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>( + pc_relative_type_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>( + pc_relative_string_patches_, linker_patches); } else { DCHECK(pc_relative_method_patches_.empty()); - EmitPcRelativeLinkerPatches<LinkerPatch::TypeClassTablePatch>(pc_relative_type_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::StringInternTablePatch>(pc_relative_string_patches_, - linker_patches); - } - EmitPcRelativeLinkerPatches<LinkerPatch::MethodBssEntryPatch>(method_bss_entry_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(string_bss_entry_patches_, - linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>( + pc_relative_type_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>( + pc_relative_string_patches_, linker_patches); + } + EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>( + method_bss_entry_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>( + type_bss_entry_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>( + string_bss_entry_patches_, linker_patches); DCHECK_EQ(size, linker_patches->size()); } diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 3035621972..9fe47ee297 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -374,7 +374,7 @@ class CodeGeneratorMIPS64 : public CodeGenerator { const Mips64Assembler& GetAssembler() const OVERRIDE { return assembler_; } // Emit linker patches. - void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) OVERRIDE; void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; // Fast path implementation of ReadBarrier::Barrier for a heap @@ -643,9 +643,9 @@ class CodeGeneratorMIPS64 : public CodeGenerator { const PcRelativePatchInfo* info_high, ArenaDeque<PcRelativePatchInfo>* patches); - template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> + template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos, - ArenaVector<LinkerPatch>* linker_patches); + ArenaVector<linker::LinkerPatch>* linker_patches); // Labels for each block that will be compiled. Mips64Label* block_labels_; // Indexed by block id. diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc index 18a55c8b09..3f576c82b3 100644 --- a/compiler/optimizing/code_generator_vector_arm64.cc +++ b/compiler/optimizing/code_generator_vector_arm64.cc @@ -949,20 +949,18 @@ void InstructionCodeGeneratorARM64::VisitVecSetScalars(HVecSetScalars* instructi } } -void LocationsBuilderARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) { - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); - switch (instr->GetPackedType()) { +// Helper to set up locations for vector accumulations. +static void CreateVecAccumLocations(ArenaAllocator* arena, HVecOperation* instruction) { + LocationSummary* locations = new (arena) LocationSummary(instruction); + switch (instruction->GetPackedType()) { case Primitive::kPrimByte: case Primitive::kPrimChar: case Primitive::kPrimShort: case Primitive::kPrimInt: - locations->SetInAt( - HVecMultiplyAccumulate::kInputAccumulatorIndex, Location::RequiresFpuRegister()); - locations->SetInAt( - HVecMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresFpuRegister()); - locations->SetInAt( - HVecMultiplyAccumulate::kInputMulRightIndex, Location::RequiresFpuRegister()); - DCHECK_EQ(HVecMultiplyAccumulate::kInputAccumulatorIndex, 0); + case Primitive::kPrimLong: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetInAt(2, Location::RequiresFpuRegister()); locations->SetOut(Location::SameAsFirstInput()); break; default: @@ -971,18 +969,25 @@ void LocationsBuilderARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* i } } +void LocationsBuilderARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) { + CreateVecAccumLocations(GetGraph()->GetArena(), instruction); +} + // Some early revisions of the Cortex-A53 have an erratum (835769) whereby it is possible for a // 64-bit scalar multiply-accumulate instruction in AArch64 state to generate an incorrect result. // However vector MultiplyAccumulate instruction is not affected. -void InstructionCodeGeneratorARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) { - LocationSummary* locations = instr->GetLocations(); - VRegister acc = VRegisterFrom(locations->InAt(HVecMultiplyAccumulate::kInputAccumulatorIndex)); - VRegister left = VRegisterFrom(locations->InAt(HVecMultiplyAccumulate::kInputMulLeftIndex)); - VRegister right = VRegisterFrom(locations->InAt(HVecMultiplyAccumulate::kInputMulRightIndex)); - switch (instr->GetPackedType()) { +void InstructionCodeGeneratorARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) { + LocationSummary* locations = instruction->GetLocations(); + VRegister acc = VRegisterFrom(locations->InAt(0)); + VRegister left = VRegisterFrom(locations->InAt(1)); + VRegister right = VRegisterFrom(locations->InAt(2)); + + DCHECK(locations->InAt(0).Equals(locations->Out())); + + switch (instruction->GetPackedType()) { case Primitive::kPrimByte: - DCHECK_EQ(16u, instr->GetVectorLength()); - if (instr->GetOpKind() == HInstruction::kAdd) { + DCHECK_EQ(16u, instruction->GetVectorLength()); + if (instruction->GetOpKind() == HInstruction::kAdd) { __ Mla(acc.V16B(), left.V16B(), right.V16B()); } else { __ Mls(acc.V16B(), left.V16B(), right.V16B()); @@ -990,16 +995,16 @@ void InstructionCodeGeneratorARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccum break; case Primitive::kPrimChar: case Primitive::kPrimShort: - DCHECK_EQ(8u, instr->GetVectorLength()); - if (instr->GetOpKind() == HInstruction::kAdd) { + DCHECK_EQ(8u, instruction->GetVectorLength()); + if (instruction->GetOpKind() == HInstruction::kAdd) { __ Mla(acc.V8H(), left.V8H(), right.V8H()); } else { __ Mls(acc.V8H(), left.V8H(), right.V8H()); } break; case Primitive::kPrimInt: - DCHECK_EQ(4u, instr->GetVectorLength()); - if (instr->GetOpKind() == HInstruction::kAdd) { + DCHECK_EQ(4u, instruction->GetVectorLength()); + if (instruction->GetOpKind() == HInstruction::kAdd) { __ Mla(acc.V4S(), left.V4S(), right.V4S()); } else { __ Mls(acc.V4S(), left.V4S(), right.V4S()); @@ -1007,6 +1012,186 @@ void InstructionCodeGeneratorARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccum break; default: LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } +} + +void LocationsBuilderARM64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { + CreateVecAccumLocations(GetGraph()->GetArena(), instruction); + // Some conversions require temporary registers. + LocationSummary* locations = instruction->GetLocations(); + HVecOperation* a = instruction->InputAt(1)->AsVecOperation(); + HVecOperation* b = instruction->InputAt(2)->AsVecOperation(); + DCHECK_EQ(a->GetPackedType(), b->GetPackedType()); + switch (a->GetPackedType()) { + case Primitive::kPrimByte: + switch (instruction->GetPackedType()) { + case Primitive::kPrimLong: + locations->AddTemp(Location::RequiresFpuRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); + FALLTHROUGH_INTENDED; + case Primitive::kPrimInt: + locations->AddTemp(Location::RequiresFpuRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); + break; + default: + break; + } + break; + case Primitive::kPrimChar: + case Primitive::kPrimShort: + if (instruction->GetPackedType() == Primitive::kPrimLong) { + locations->AddTemp(Location::RequiresFpuRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); + } + break; + case Primitive::kPrimInt: + case Primitive::kPrimLong: + if (instruction->GetPackedType() == a->GetPackedType()) { + locations->AddTemp(Location::RequiresFpuRegister()); + } + break; + default: + break; + } +} + +void InstructionCodeGeneratorARM64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { + LocationSummary* locations = instruction->GetLocations(); + VRegister acc = VRegisterFrom(locations->InAt(0)); + VRegister left = VRegisterFrom(locations->InAt(1)); + VRegister right = VRegisterFrom(locations->InAt(2)); + + DCHECK(locations->InAt(0).Equals(locations->Out())); + + // Handle all feasible acc_T += sad(a_S, b_S) type combinations (T x S). + HVecOperation* a = instruction->InputAt(1)->AsVecOperation(); + HVecOperation* b = instruction->InputAt(2)->AsVecOperation(); + DCHECK_EQ(a->GetPackedType(), b->GetPackedType()); + switch (a->GetPackedType()) { + case Primitive::kPrimByte: + DCHECK_EQ(16u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case Primitive::kPrimChar: + case Primitive::kPrimShort: + DCHECK_EQ(8u, instruction->GetVectorLength()); + __ Sabal(acc.V8H(), left.V8B(), right.V8B()); + __ Sabal2(acc.V8H(), left.V16B(), right.V16B()); + break; + case Primitive::kPrimInt: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + VRegister tmp1 = VRegisterFrom(locations->GetTemp(0)); + VRegister tmp2 = VRegisterFrom(locations->GetTemp(1)); + __ Sxtl(tmp1.V8H(), left.V8B()); + __ Sxtl(tmp2.V8H(), right.V8B()); + __ Sabal(acc.V4S(), tmp1.V4H(), tmp2.V4H()); + __ Sabal2(acc.V4S(), tmp1.V8H(), tmp2.V8H()); + __ Sxtl2(tmp1.V8H(), left.V16B()); + __ Sxtl2(tmp2.V8H(), right.V16B()); + __ Sabal(acc.V4S(), tmp1.V4H(), tmp2.V4H()); + __ Sabal2(acc.V4S(), tmp1.V8H(), tmp2.V8H()); + break; + } + case Primitive::kPrimLong: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VRegister tmp1 = VRegisterFrom(locations->GetTemp(0)); + VRegister tmp2 = VRegisterFrom(locations->GetTemp(1)); + VRegister tmp3 = VRegisterFrom(locations->GetTemp(2)); + VRegister tmp4 = VRegisterFrom(locations->GetTemp(3)); + __ Sxtl(tmp1.V8H(), left.V8B()); + __ Sxtl(tmp2.V8H(), right.V8B()); + __ Sxtl(tmp3.V4S(), tmp1.V4H()); + __ Sxtl(tmp4.V4S(), tmp2.V4H()); + __ Sabal(acc.V2D(), tmp3.V2S(), tmp4.V2S()); + __ Sabal2(acc.V2D(), tmp3.V4S(), tmp4.V4S()); + __ Sxtl2(tmp3.V4S(), tmp1.V8H()); + __ Sxtl2(tmp4.V4S(), tmp2.V8H()); + __ Sabal(acc.V2D(), tmp3.V2S(), tmp4.V2S()); + __ Sabal2(acc.V2D(), tmp3.V4S(), tmp4.V4S()); + __ Sxtl2(tmp1.V8H(), left.V16B()); + __ Sxtl2(tmp2.V8H(), right.V16B()); + __ Sxtl(tmp3.V4S(), tmp1.V4H()); + __ Sxtl(tmp4.V4S(), tmp2.V4H()); + __ Sabal(acc.V2D(), tmp3.V2S(), tmp4.V2S()); + __ Sabal2(acc.V2D(), tmp3.V4S(), tmp4.V4S()); + __ Sxtl2(tmp3.V4S(), tmp1.V8H()); + __ Sxtl2(tmp4.V4S(), tmp2.V8H()); + __ Sabal(acc.V2D(), tmp3.V2S(), tmp4.V2S()); + __ Sabal2(acc.V2D(), tmp3.V4S(), tmp4.V4S()); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case Primitive::kPrimChar: + case Primitive::kPrimShort: + DCHECK_EQ(8u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case Primitive::kPrimInt: + DCHECK_EQ(4u, instruction->GetVectorLength()); + __ Sabal(acc.V4S(), left.V4H(), right.V4H()); + __ Sabal2(acc.V4S(), left.V8H(), right.V8H()); + break; + case Primitive::kPrimLong: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VRegister tmp1 = VRegisterFrom(locations->GetTemp(0)); + VRegister tmp2 = VRegisterFrom(locations->GetTemp(1)); + __ Sxtl(tmp1.V4S(), left.V4H()); + __ Sxtl(tmp2.V4S(), right.V4H()); + __ Sabal(acc.V2D(), tmp1.V2S(), tmp2.V2S()); + __ Sabal2(acc.V2D(), tmp1.V4S(), tmp2.V4S()); + __ Sxtl2(tmp1.V4S(), left.V8H()); + __ Sxtl2(tmp2.V4S(), right.V8H()); + __ Sabal(acc.V2D(), tmp1.V2S(), tmp2.V2S()); + __ Sabal2(acc.V2D(), tmp1.V4S(), tmp2.V4S()); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case Primitive::kPrimInt: + DCHECK_EQ(4u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case Primitive::kPrimInt: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + VRegister tmp = VRegisterFrom(locations->GetTemp(0)); + __ Sub(tmp.V4S(), left.V4S(), right.V4S()); + __ Abs(tmp.V4S(), tmp.V4S()); + __ Add(acc.V4S(), acc.V4S(), tmp.V4S()); + break; + } + case Primitive::kPrimLong: + DCHECK_EQ(2u, instruction->GetVectorLength()); + __ Sabal(acc.V2D(), left.V2S(), right.V2S()); + __ Sabal2(acc.V2D(), left.V4S(), right.V4S()); + break; + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case Primitive::kPrimLong: + DCHECK_EQ(2u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case Primitive::kPrimLong: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VRegister tmp = VRegisterFrom(locations->GetTemp(0)); + __ Sub(tmp.V2D(), left.V2D(), right.V2D()); + __ Abs(tmp.V2D(), tmp.V2D()); + __ Add(acc.V2D(), acc.V2D(), tmp.V2D()); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + default: + LOG(FATAL) << "Unsupported SIMD type"; } } diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc index 7a11dff41e..069054c2f5 100644 --- a/compiler/optimizing/code_generator_vector_arm_vixl.cc +++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc @@ -629,12 +629,40 @@ void InstructionCodeGeneratorARMVIXL::VisitVecSetScalars(HVecSetScalars* instruc LOG(FATAL) << "No SIMD for " << instruction->GetId(); } -void LocationsBuilderARMVIXL::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) { - LOG(FATAL) << "No SIMD for " << instr->GetId(); +// Helper to set up locations for vector accumulations. +static void CreateVecAccumLocations(ArenaAllocator* arena, HVecOperation* instruction) { + LocationSummary* locations = new (arena) LocationSummary(instruction); + switch (instruction->GetPackedType()) { + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimLong: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetInAt(2, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } +} + +void LocationsBuilderARMVIXL::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) { + CreateVecAccumLocations(GetGraph()->GetArena(), instruction); +} + +void InstructionCodeGeneratorARMVIXL::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) { + LOG(FATAL) << "No SIMD for " << instruction->GetId(); } -void InstructionCodeGeneratorARMVIXL::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) { - LOG(FATAL) << "No SIMD for " << instr->GetId(); +void LocationsBuilderARMVIXL::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { + CreateVecAccumLocations(GetGraph()->GetArena(), instruction); +} + +void InstructionCodeGeneratorARMVIXL::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { + LOG(FATAL) << "No SIMD for " << instruction->GetId(); } // Return whether the vector memory access operation is guaranteed to be word-aligned (ARM word diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc index c2fbf7f04b..0bedafcc81 100644 --- a/compiler/optimizing/code_generator_vector_mips.cc +++ b/compiler/optimizing/code_generator_vector_mips.cc @@ -826,21 +826,18 @@ void InstructionCodeGeneratorMIPS::VisitVecSetScalars(HVecSetScalars* instructio LOG(FATAL) << "No SIMD for " << instruction->GetId(); } -void LocationsBuilderMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) { - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); - switch (instr->GetPackedType()) { +// Helper to set up locations for vector accumulations. +static void CreateVecAccumLocations(ArenaAllocator* arena, HVecOperation* instruction) { + LocationSummary* locations = new (arena) LocationSummary(instruction); + switch (instruction->GetPackedType()) { case Primitive::kPrimByte: case Primitive::kPrimChar: case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimLong: - locations->SetInAt( - HVecMultiplyAccumulate::kInputAccumulatorIndex, Location::RequiresFpuRegister()); - locations->SetInAt( - HVecMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresFpuRegister()); - locations->SetInAt( - HVecMultiplyAccumulate::kInputMulRightIndex, Location::RequiresFpuRegister()); - DCHECK_EQ(HVecMultiplyAccumulate::kInputAccumulatorIndex, 0); + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetInAt(2, Location::RequiresFpuRegister()); locations->SetOut(Location::SameAsFirstInput()); break; default: @@ -849,18 +846,19 @@ void LocationsBuilderMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* in } } -void InstructionCodeGeneratorMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) { - LocationSummary* locations = instr->GetLocations(); - VectorRegister acc = - VectorRegisterFrom(locations->InAt(HVecMultiplyAccumulate::kInputAccumulatorIndex)); - VectorRegister left = - VectorRegisterFrom(locations->InAt(HVecMultiplyAccumulate::kInputMulLeftIndex)); - VectorRegister right = - VectorRegisterFrom(locations->InAt(HVecMultiplyAccumulate::kInputMulRightIndex)); - switch (instr->GetPackedType()) { +void LocationsBuilderMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) { + CreateVecAccumLocations(GetGraph()->GetArena(), instruction); +} + +void InstructionCodeGeneratorMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) { + LocationSummary* locations = instruction->GetLocations(); + VectorRegister acc = VectorRegisterFrom(locations->InAt(0)); + VectorRegister left = VectorRegisterFrom(locations->InAt(1)); + VectorRegister right = VectorRegisterFrom(locations->InAt(2)); + switch (instruction->GetPackedType()) { case Primitive::kPrimByte: - DCHECK_EQ(16u, instr->GetVectorLength()); - if (instr->GetOpKind() == HInstruction::kAdd) { + DCHECK_EQ(16u, instruction->GetVectorLength()); + if (instruction->GetOpKind() == HInstruction::kAdd) { __ MaddvB(acc, left, right); } else { __ MsubvB(acc, left, right); @@ -868,24 +866,24 @@ void InstructionCodeGeneratorMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumu break; case Primitive::kPrimChar: case Primitive::kPrimShort: - DCHECK_EQ(8u, instr->GetVectorLength()); - if (instr->GetOpKind() == HInstruction::kAdd) { + DCHECK_EQ(8u, instruction->GetVectorLength()); + if (instruction->GetOpKind() == HInstruction::kAdd) { __ MaddvH(acc, left, right); } else { __ MsubvH(acc, left, right); } break; case Primitive::kPrimInt: - DCHECK_EQ(4u, instr->GetVectorLength()); - if (instr->GetOpKind() == HInstruction::kAdd) { + DCHECK_EQ(4u, instruction->GetVectorLength()); + if (instruction->GetOpKind() == HInstruction::kAdd) { __ MaddvW(acc, left, right); } else { __ MsubvW(acc, left, right); } break; case Primitive::kPrimLong: - DCHECK_EQ(2u, instr->GetVectorLength()); - if (instr->GetOpKind() == HInstruction::kAdd) { + DCHECK_EQ(2u, instruction->GetVectorLength()); + if (instruction->GetOpKind() == HInstruction::kAdd) { __ MaddvD(acc, left, right); } else { __ MsubvD(acc, left, right); @@ -897,6 +895,15 @@ void InstructionCodeGeneratorMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumu } } +void LocationsBuilderMIPS::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { + CreateVecAccumLocations(GetGraph()->GetArena(), instruction); +} + +void InstructionCodeGeneratorMIPS::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { + LOG(FATAL) << "No SIMD for " << instruction->GetId(); + // TODO: implement this, location helper already filled out (shared with MulAcc). +} + // Helper to set up locations for vector memory operations. static void CreateVecMemLocations(ArenaAllocator* arena, HVecMemoryOperation* instruction, diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc index 9d3a777c13..db31bdcc92 100644 --- a/compiler/optimizing/code_generator_vector_mips64.cc +++ b/compiler/optimizing/code_generator_vector_mips64.cc @@ -830,21 +830,18 @@ void InstructionCodeGeneratorMIPS64::VisitVecSetScalars(HVecSetScalars* instruct LOG(FATAL) << "No SIMD for " << instruction->GetId(); } -void LocationsBuilderMIPS64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) { - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); - switch (instr->GetPackedType()) { +// Helper to set up locations for vector accumulations. +static void CreateVecAccumLocations(ArenaAllocator* arena, HVecOperation* instruction) { + LocationSummary* locations = new (arena) LocationSummary(instruction); + switch (instruction->GetPackedType()) { case Primitive::kPrimByte: case Primitive::kPrimChar: case Primitive::kPrimShort: case Primitive::kPrimInt: case Primitive::kPrimLong: - locations->SetInAt( - HVecMultiplyAccumulate::kInputAccumulatorIndex, Location::RequiresFpuRegister()); - locations->SetInAt( - HVecMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresFpuRegister()); - locations->SetInAt( - HVecMultiplyAccumulate::kInputMulRightIndex, Location::RequiresFpuRegister()); - DCHECK_EQ(HVecMultiplyAccumulate::kInputAccumulatorIndex, 0); + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetInAt(2, Location::RequiresFpuRegister()); locations->SetOut(Location::SameAsFirstInput()); break; default: @@ -853,18 +850,19 @@ void LocationsBuilderMIPS64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* } } -void InstructionCodeGeneratorMIPS64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) { - LocationSummary* locations = instr->GetLocations(); - VectorRegister acc = - VectorRegisterFrom(locations->InAt(HVecMultiplyAccumulate::kInputAccumulatorIndex)); - VectorRegister left = - VectorRegisterFrom(locations->InAt(HVecMultiplyAccumulate::kInputMulLeftIndex)); - VectorRegister right = - VectorRegisterFrom(locations->InAt(HVecMultiplyAccumulate::kInputMulRightIndex)); - switch (instr->GetPackedType()) { +void LocationsBuilderMIPS64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) { + CreateVecAccumLocations(GetGraph()->GetArena(), instruction); +} + +void InstructionCodeGeneratorMIPS64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) { + LocationSummary* locations = instruction->GetLocations(); + VectorRegister acc = VectorRegisterFrom(locations->InAt(0)); + VectorRegister left = VectorRegisterFrom(locations->InAt(1)); + VectorRegister right = VectorRegisterFrom(locations->InAt(2)); + switch (instruction->GetPackedType()) { case Primitive::kPrimByte: - DCHECK_EQ(16u, instr->GetVectorLength()); - if (instr->GetOpKind() == HInstruction::kAdd) { + DCHECK_EQ(16u, instruction->GetVectorLength()); + if (instruction->GetOpKind() == HInstruction::kAdd) { __ MaddvB(acc, left, right); } else { __ MsubvB(acc, left, right); @@ -872,24 +870,24 @@ void InstructionCodeGeneratorMIPS64::VisitVecMultiplyAccumulate(HVecMultiplyAccu break; case Primitive::kPrimChar: case Primitive::kPrimShort: - DCHECK_EQ(8u, instr->GetVectorLength()); - if (instr->GetOpKind() == HInstruction::kAdd) { + DCHECK_EQ(8u, instruction->GetVectorLength()); + if (instruction->GetOpKind() == HInstruction::kAdd) { __ MaddvH(acc, left, right); } else { __ MsubvH(acc, left, right); } break; case Primitive::kPrimInt: - DCHECK_EQ(4u, instr->GetVectorLength()); - if (instr->GetOpKind() == HInstruction::kAdd) { + DCHECK_EQ(4u, instruction->GetVectorLength()); + if (instruction->GetOpKind() == HInstruction::kAdd) { __ MaddvW(acc, left, right); } else { __ MsubvW(acc, left, right); } break; case Primitive::kPrimLong: - DCHECK_EQ(2u, instr->GetVectorLength()); - if (instr->GetOpKind() == HInstruction::kAdd) { + DCHECK_EQ(2u, instruction->GetVectorLength()); + if (instruction->GetOpKind() == HInstruction::kAdd) { __ MaddvD(acc, left, right); } else { __ MsubvD(acc, left, right); @@ -901,6 +899,15 @@ void InstructionCodeGeneratorMIPS64::VisitVecMultiplyAccumulate(HVecMultiplyAccu } } +void LocationsBuilderMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { + CreateVecAccumLocations(GetGraph()->GetArena(), instruction); +} + +void InstructionCodeGeneratorMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { + LOG(FATAL) << "No SIMD for " << instruction->GetId(); + // TODO: implement this, location helper already filled out (shared with MulAcc). +} + // Helper to set up locations for vector memory operations. static void CreateVecMemLocations(ArenaAllocator* arena, HVecMemoryOperation* instruction, diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc index 37190f8363..5a012e7298 100644 --- a/compiler/optimizing/code_generator_vector_x86.cc +++ b/compiler/optimizing/code_generator_vector_x86.cc @@ -51,7 +51,6 @@ void LocationsBuilderX86::VisitVecReplicateScalar(HVecReplicateScalar* instructi : Location::RequiresFpuRegister()); locations->SetOut(is_zero ? Location::RequiresFpuRegister() : Location::SameAsFirstInput()); - break; default: LOG(FATAL) << "Unsupported SIMD type"; @@ -1033,12 +1032,42 @@ void InstructionCodeGeneratorX86::VisitVecSetScalars(HVecSetScalars* instruction } } -void LocationsBuilderX86::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) { - LOG(FATAL) << "No SIMD for " << instr->GetId(); +// Helper to set up locations for vector accumulations. +static void CreateVecAccumLocations(ArenaAllocator* arena, HVecOperation* instruction) { + LocationSummary* locations = new (arena) LocationSummary(instruction); + switch (instruction->GetPackedType()) { + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimLong: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetInAt(2, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } +} + +void LocationsBuilderX86::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) { + CreateVecAccumLocations(GetGraph()->GetArena(), instruction); +} + +void InstructionCodeGeneratorX86::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) { + // TODO: pmaddwd? + LOG(FATAL) << "No SIMD for " << instruction->GetId(); +} + +void LocationsBuilderX86::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { + CreateVecAccumLocations(GetGraph()->GetArena(), instruction); } -void InstructionCodeGeneratorX86::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) { - LOG(FATAL) << "No SIMD for " << instr->GetId(); +void InstructionCodeGeneratorX86::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { + // TODO: psadbw for unsigned? + LOG(FATAL) << "No SIMD for " << instruction->GetId(); } // Helper to set up locations for vector memory operations. diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc index edd0209f10..3698b7fb85 100644 --- a/compiler/optimizing/code_generator_vector_x86_64.cc +++ b/compiler/optimizing/code_generator_vector_x86_64.cc @@ -1005,11 +1005,41 @@ void InstructionCodeGeneratorX86_64::VisitVecSetScalars(HVecSetScalars* instruct } } +// Helper to set up locations for vector accumulations. +static void CreateVecAccumLocations(ArenaAllocator* arena, HVecOperation* instruction) { + LocationSummary* locations = new (arena) LocationSummary(instruction); + switch (instruction->GetPackedType()) { + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimLong: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetInAt(2, Location::RequiresFpuRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } +} + void LocationsBuilderX86_64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) { - LOG(FATAL) << "No SIMD for " << instruction->GetId(); + CreateVecAccumLocations(GetGraph()->GetArena(), instruction); } void InstructionCodeGeneratorX86_64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) { + // TODO: pmaddwd? + LOG(FATAL) << "No SIMD for " << instruction->GetId(); +} + +void LocationsBuilderX86_64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { + CreateVecAccumLocations(GetGraph()->GetArena(), instruction); +} + +void InstructionCodeGeneratorX86_64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { + // TODO: psadbw for unsigned? LOG(FATAL) << "No SIMD for " << instruction->GetId(); } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index c153cf78da..99581ee9b8 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -23,8 +23,10 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gc/accounting/card_table.h" +#include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_x86.h" +#include "linker/linker_patch.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" @@ -4674,10 +4676,10 @@ Label* CodeGeneratorX86::NewStringBssEntryPatch(HLoadString* load_string) { // for method patch needs to point to the embedded constant which occupies the last 4 bytes. constexpr uint32_t kLabelPositionToLiteralOffsetAdjustment = 4u; -template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> +template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> inline void CodeGeneratorX86::EmitPcRelativeLinkerPatches( const ArenaDeque<X86PcRelativePatchInfo>& infos, - ArenaVector<LinkerPatch>* linker_patches) { + ArenaVector<linker::LinkerPatch>* linker_patches) { for (const X86PcRelativePatchInfo& info : infos) { uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; linker_patches->push_back(Factory( @@ -4685,7 +4687,7 @@ inline void CodeGeneratorX86::EmitPcRelativeLinkerPatches( } } -void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { +void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) { DCHECK(linker_patches->empty()); size_t size = boot_image_method_patches_.size() + @@ -4696,24 +4698,25 @@ void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche string_bss_entry_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeMethodPatch>(boot_image_method_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(boot_image_type_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(string_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>( + boot_image_method_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>( + boot_image_type_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>( + string_patches_, linker_patches); } else { DCHECK(boot_image_method_patches_.empty()); - EmitPcRelativeLinkerPatches<LinkerPatch::TypeClassTablePatch>(boot_image_type_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::StringInternTablePatch>(string_patches_, - linker_patches); - } - EmitPcRelativeLinkerPatches<LinkerPatch::MethodBssEntryPatch>(method_bss_entry_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(string_bss_entry_patches_, - linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>( + boot_image_type_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>( + string_patches_, linker_patches); + } + EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>( + method_bss_entry_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>( + type_bss_entry_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>( + string_bss_entry_patches_, linker_patches); DCHECK_EQ(size, linker_patches->size()); } diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index b32d57a774..e8f919d122 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -431,7 +431,7 @@ class CodeGeneratorX86 : public CodeGenerator { void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; // Emit linker patches. - void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) OVERRIDE; void PatchJitRootUse(uint8_t* code, const uint8_t* roots_data, @@ -617,9 +617,9 @@ class CodeGeneratorX86 : public CodeGenerator { HX86ComputeBaseMethodAddress* method_address; }; - template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> + template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> void EmitPcRelativeLinkerPatches(const ArenaDeque<X86PcRelativePatchInfo>& infos, - ArenaVector<LinkerPatch>* linker_patches); + ArenaVector<linker::LinkerPatch>* linker_patches); Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index bbf05a70d6..65b3f62104 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -22,8 +22,10 @@ #include "compiled_method.h" #include "entrypoints/quick/quick_entrypoints.h" #include "gc/accounting/card_table.h" +#include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_x86_64.h" +#include "linker/linker_patch.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" @@ -1105,10 +1107,10 @@ Label* CodeGeneratorX86_64::NewStringBssEntryPatch(HLoadString* load_string) { // for method patch needs to point to the embedded constant which occupies the last 4 bytes. constexpr uint32_t kLabelPositionToLiteralOffsetAdjustment = 4u; -template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> +template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> inline void CodeGeneratorX86_64::EmitPcRelativeLinkerPatches( const ArenaDeque<PatchInfo<Label>>& infos, - ArenaVector<LinkerPatch>* linker_patches) { + ArenaVector<linker::LinkerPatch>* linker_patches) { for (const PatchInfo<Label>& info : infos) { uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; linker_patches->push_back( @@ -1116,7 +1118,7 @@ inline void CodeGeneratorX86_64::EmitPcRelativeLinkerPatches( } } -void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { +void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) { DCHECK(linker_patches->empty()); size_t size = boot_image_method_patches_.size() + @@ -1127,24 +1129,25 @@ void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pat string_bss_entry_patches_.size(); linker_patches->reserve(size); if (GetCompilerOptions().IsBootImage()) { - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeMethodPatch>(boot_image_method_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(boot_image_type_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(string_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>( + boot_image_method_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>( + boot_image_type_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>( + string_patches_, linker_patches); } else { DCHECK(boot_image_method_patches_.empty()); - EmitPcRelativeLinkerPatches<LinkerPatch::TypeClassTablePatch>(boot_image_type_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::StringInternTablePatch>(string_patches_, - linker_patches); - } - EmitPcRelativeLinkerPatches<LinkerPatch::MethodBssEntryPatch>(method_bss_entry_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::TypeBssEntryPatch>(type_bss_entry_patches_, - linker_patches); - EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(string_bss_entry_patches_, - linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeClassTablePatch>( + boot_image_type_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringInternTablePatch>( + string_patches_, linker_patches); + } + EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>( + method_bss_entry_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>( + type_bss_entry_patches_, linker_patches); + EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>( + string_bss_entry_patches_, linker_patches); DCHECK_EQ(size, linker_patches->size()); } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index f5fa86bf23..8e8e695a64 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -424,7 +424,7 @@ class CodeGeneratorX86_64 : public CodeGenerator { void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; - void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) OVERRIDE; void PatchJitRootUse(uint8_t* code, const uint8_t* roots_data, @@ -586,9 +586,9 @@ class CodeGeneratorX86_64 : public CodeGenerator { static constexpr int32_t kDummy32BitOffset = 256; private: - template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> + template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> static void EmitPcRelativeLinkerPatches(const ArenaDeque<PatchInfo<Label>>& infos, - ArenaVector<LinkerPatch>* linker_patches); + ArenaVector<linker::LinkerPatch>* linker_patches); // Labels for each block that will be compiled. Label* block_labels_; // Indexed by block id. diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 1ed1b7537e..96efe7f3b1 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -21,6 +21,7 @@ #include "code_generator_arm64.h" #include "common_arm64.h" #include "entrypoints/quick/quick_entrypoints.h" +#include "heap_poisoning.h" #include "intrinsics.h" #include "lock_word.h" #include "mirror/array-inl.h" diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index d2dc88a73b..e2494f0ce8 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -20,6 +20,7 @@ #include "art_method.h" #include "code_generator_arm_vixl.h" #include "common_arm.h" +#include "heap_poisoning.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/object_array-inl.h" diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 2669d97d82..fe5579c8be 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -20,6 +20,7 @@ #include "art_method.h" #include "code_generator_mips.h" #include "entrypoints/quick/quick_entrypoints.h" +#include "heap_poisoning.h" #include "intrinsics.h" #include "mirror/array-inl.h" #include "mirror/object_array-inl.h" diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 74be954a75..80448f1389 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -20,6 +20,7 @@ #include "art_method.h" #include "code_generator_mips64.h" #include "entrypoints/quick/quick_entrypoints.h" +#include "heap_poisoning.h" #include "intrinsics.h" #include "mirror/array-inl.h" #include "mirror/object_array-inl.h" diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index a18b0cc400..abd9014438 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -23,6 +23,7 @@ #include "base/bit_utils.h" #include "code_generator_x86.h" #include "entrypoints/quick/quick_entrypoints.h" +#include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_utils.h" #include "lock_word.h" diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 5abdb1d1bd..7798c0d99e 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -23,6 +23,7 @@ #include "base/bit_utils.h" #include "code_generator_x86_64.h" #include "entrypoints/quick/quick_entrypoints.h" +#include "heap_poisoning.h" #include "intrinsics.h" #include "intrinsics_utils.h" #include "lock_word.h" diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index baa045390b..6f8743bd53 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -71,10 +71,13 @@ static bool IsEarlyExit(HLoopInformation* loop_info) { return false; } -// Detect a sign extension from the given type. Returns the promoted operand on success. +// Detect a sign extension in instruction from the given type. The to64 parameter +// denotes if result is long, and thus sign extension from int can be included. +// Returns the promoted operand on success. static bool IsSignExtensionAndGet(HInstruction* instruction, Primitive::Type type, - /*out*/ HInstruction** operand) { + /*out*/ HInstruction** operand, + bool to64 = false) { // Accept any already wider constant that would be handled properly by sign // extension when represented in the *width* of the given narrower data type // (the fact that char normally zero extends does not matter here). @@ -82,20 +85,24 @@ static bool IsSignExtensionAndGet(HInstruction* instruction, if (IsInt64AndGet(instruction, /*out*/ &value)) { switch (type) { case Primitive::kPrimByte: - if (std::numeric_limits<int8_t>::min() <= value && - std::numeric_limits<int8_t>::max() >= value) { + if (IsInt<8>(value)) { *operand = instruction; return true; } return false; case Primitive::kPrimChar: case Primitive::kPrimShort: - if (std::numeric_limits<int16_t>::min() <= value && - std::numeric_limits<int16_t>::max() <= value) { + if (IsInt<16>(value)) { *operand = instruction; return true; } return false; + case Primitive::kPrimInt: + if (IsInt<32>(value)) { + *operand = instruction; + return to64; + } + return false; default: return false; } @@ -110,40 +117,52 @@ static bool IsSignExtensionAndGet(HInstruction* instruction, case Primitive::kPrimShort: *operand = instruction; return true; + case Primitive::kPrimInt: + *operand = instruction; + return to64; default: return false; } } - // TODO: perhaps explicit conversions later too? - // (this may return something different from instruction) + // Explicit type conversion to long. + if (instruction->IsTypeConversion() && instruction->GetType() == Primitive::kPrimLong) { + return IsSignExtensionAndGet(instruction->InputAt(0), type, /*out*/ operand, /*to64*/ true); + } return false; } -// Detect a zero extension from the given type. Returns the promoted operand on success. +// Detect a zero extension in instruction from the given type. The to64 parameter +// denotes if result is long, and thus zero extension from int can be included. +// Returns the promoted operand on success. static bool IsZeroExtensionAndGet(HInstruction* instruction, Primitive::Type type, - /*out*/ HInstruction** operand) { + /*out*/ HInstruction** operand, + bool to64 = false) { // Accept any already wider constant that would be handled properly by zero // extension when represented in the *width* of the given narrower data type - // (the fact that byte/short normally sign extend does not matter here). + // (the fact that byte/short/int normally sign extend does not matter here). int64_t value = 0; if (IsInt64AndGet(instruction, /*out*/ &value)) { switch (type) { case Primitive::kPrimByte: - if (std::numeric_limits<uint8_t>::min() <= value && - std::numeric_limits<uint8_t>::max() >= value) { + if (IsUint<8>(value)) { *operand = instruction; return true; } return false; case Primitive::kPrimChar: case Primitive::kPrimShort: - if (std::numeric_limits<uint16_t>::min() <= value && - std::numeric_limits<uint16_t>::max() <= value) { + if (IsUint<16>(value)) { *operand = instruction; return true; } return false; + case Primitive::kPrimInt: + if (IsUint<32>(value)) { + *operand = instruction; + return to64; + } + return false; default: return false; } @@ -170,14 +189,21 @@ static bool IsZeroExtensionAndGet(HInstruction* instruction, (IsInt64AndGet(b, /*out*/ &mask) && (IsSignExtensionAndGet(a, type, /*out*/ operand) || IsZeroExtensionAndGet(a, type, /*out*/ operand)))) { switch ((*operand)->GetType()) { - case Primitive::kPrimByte: return mask == std::numeric_limits<uint8_t>::max(); + case Primitive::kPrimByte: + return mask == std::numeric_limits<uint8_t>::max(); case Primitive::kPrimChar: - case Primitive::kPrimShort: return mask == std::numeric_limits<uint16_t>::max(); + case Primitive::kPrimShort: + return mask == std::numeric_limits<uint16_t>::max(); + case Primitive::kPrimInt: + return mask == std::numeric_limits<uint32_t>::max() && to64; default: return false; } } } - // TODO: perhaps explicit conversions later too? + // Explicit type conversion to long. + if (instruction->IsTypeConversion() && instruction->GetType() == Primitive::kPrimLong) { + return IsZeroExtensionAndGet(instruction->InputAt(0), type, /*out*/ operand, /*to64*/ true); + } return false; } @@ -214,6 +240,55 @@ static bool IsNarrowerOperand(HInstruction* a, return false; } +// Compute relative vector length based on type difference. +static size_t GetOtherVL(Primitive::Type other_type, Primitive::Type vector_type, size_t vl) { + switch (other_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + switch (vector_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: return vl; + default: break; + } + return vl; + case Primitive::kPrimChar: + case Primitive::kPrimShort: + switch (vector_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: return vl >> 1; + case Primitive::kPrimChar: + case Primitive::kPrimShort: return vl; + default: break; + } + break; + case Primitive::kPrimInt: + switch (vector_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: return vl >> 2; + case Primitive::kPrimChar: + case Primitive::kPrimShort: return vl >> 1; + case Primitive::kPrimInt: return vl; + default: break; + } + break; + case Primitive::kPrimLong: + switch (vector_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: return vl >> 3; + case Primitive::kPrimChar: + case Primitive::kPrimShort: return vl >> 2; + case Primitive::kPrimInt: return vl >> 1; + case Primitive::kPrimLong: return vl; + default: break; + } + break; + default: + break; + } + LOG(FATAL) << "Unsupported idiom conversion"; + UNREACHABLE(); +} + // Detect up to two instructions a and b, and an acccumulated constant c. static bool IsAddConstHelper(HInstruction* instruction, /*out*/ HInstruction** a, @@ -260,16 +335,16 @@ static bool IsAddConst(HInstruction* instruction, } // Detect reductions of the following forms, -// under assumption phi has only *one* use: // x = x_phi + .. // x = x_phi - .. // x = max(x_phi, ..) // x = min(x_phi, ..) static bool HasReductionFormat(HInstruction* reduction, HInstruction* phi) { if (reduction->IsAdd()) { - return reduction->InputAt(0) == phi || reduction->InputAt(1) == phi; + return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi) || + (reduction->InputAt(0) != phi && reduction->InputAt(1) == phi); } else if (reduction->IsSub()) { - return reduction->InputAt(0) == phi; + return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi); } else if (reduction->IsInvokeStaticOrDirect()) { switch (reduction->AsInvokeStaticOrDirect()->GetIntrinsic()) { case Intrinsics::kMathMinIntInt: @@ -280,7 +355,8 @@ static bool HasReductionFormat(HInstruction* reduction, HInstruction* phi) { case Intrinsics::kMathMaxLongLong: case Intrinsics::kMathMaxFloatFloat: case Intrinsics::kMathMaxDoubleDouble: - return reduction->InputAt(0) == phi || reduction->InputAt(1) == phi; + return (reduction->InputAt(0) == phi && reduction->InputAt(1) != phi) || + (reduction->InputAt(0) != phi && reduction->InputAt(1) == phi); default: return false; } @@ -288,9 +364,9 @@ static bool HasReductionFormat(HInstruction* reduction, HInstruction* phi) { return false; } -// Translates operation to reduction kind. -static HVecReduce::ReductionKind GetReductionKind(HInstruction* reduction) { - if (reduction->IsVecAdd() || reduction->IsVecSub()) { +// Translates vector operation to reduction kind. +static HVecReduce::ReductionKind GetReductionKind(HVecOperation* reduction) { + if (reduction->IsVecAdd() || reduction->IsVecSub() || reduction->IsVecSADAccumulate()) { return HVecReduce::kSum; } else if (reduction->IsVecMin()) { return HVecReduce::kMin; @@ -720,7 +796,6 @@ void HLoopOptimization::Vectorize(LoopNode* node, HBasicBlock* block, HBasicBlock* exit, int64_t trip_count) { - Primitive::Type induc_type = Primitive::kPrimInt; HBasicBlock* header = node->loop_info->GetHeader(); HBasicBlock* preheader = node->loop_info->GetPreHeader(); @@ -739,6 +814,10 @@ void HLoopOptimization::Vectorize(LoopNode* node, vector_header_ = header; vector_body_ = block; + // Loop induction type. + Primitive::Type induc_type = main_phi->GetType(); + DCHECK(induc_type == Primitive::kPrimInt || induc_type == Primitive::kPrimLong) << induc_type; + // Generate dynamic loop peeling trip count, if needed, under the assumption // that the Android runtime guarantees at least "component size" alignment: // ptc = (ALIGN - (&a[initial] % ALIGN)) / type-size @@ -767,10 +846,10 @@ void HLoopOptimization::Vectorize(LoopNode* node, HInstruction* rem = Insert( preheader, new (global_allocator_) HAnd(induc_type, diff, - graph_->GetIntConstant(chunk - 1))); + graph_->GetConstant(induc_type, chunk - 1))); vtc = Insert(preheader, new (global_allocator_) HSub(induc_type, stc, rem)); } - vector_index_ = graph_->GetIntConstant(0); + vector_index_ = graph_->GetConstant(induc_type, 0); // Generate runtime disambiguation test: // vtc = a != b ? vtc : 0; @@ -779,7 +858,8 @@ void HLoopOptimization::Vectorize(LoopNode* node, preheader, new (global_allocator_) HNotEqual(vector_runtime_test_a_, vector_runtime_test_b_)); vtc = Insert(preheader, - new (global_allocator_) HSelect(rt, vtc, graph_->GetIntConstant(0), kNoDexPc)); + new (global_allocator_) + HSelect(rt, vtc, graph_->GetConstant(induc_type, 0), kNoDexPc)); needs_cleanup = true; } @@ -793,7 +873,7 @@ void HLoopOptimization::Vectorize(LoopNode* node, graph_->TransformLoopForVectorization(vector_header_, vector_body_, exit), vector_index_, ptc, - graph_->GetIntConstant(1), + graph_->GetConstant(induc_type, 1), kNoUnrollingFactor); } @@ -806,7 +886,7 @@ void HLoopOptimization::Vectorize(LoopNode* node, graph_->TransformLoopForVectorization(vector_header_, vector_body_, exit), vector_index_, vtc, - graph_->GetIntConstant(vector_length_), // increment per unroll + graph_->GetConstant(induc_type, vector_length_), // increment per unroll unroll); HLoopInformation* vloop = vector_header_->GetLoopInformation(); @@ -820,14 +900,20 @@ void HLoopOptimization::Vectorize(LoopNode* node, graph_->TransformLoopForVectorization(vector_header_, vector_body_, exit), vector_index_, stc, - graph_->GetIntConstant(1), + graph_->GetConstant(induc_type, 1), kNoUnrollingFactor); } // Link reductions to their final uses. for (auto i = reductions_->begin(); i != reductions_->end(); ++i) { if (i->first->IsPhi()) { - i->first->ReplaceWith(ReduceAndExtractIfNeeded(i->second)); + HInstruction* phi = i->first; + HInstruction* repl = ReduceAndExtractIfNeeded(i->second); + // Deal with regular uses. + for (const HUseListNode<HInstruction*>& use : phi->GetUses()) { + induction_range_.Replace(use.GetUser(), phi, repl); // update induction use + } + phi->ReplaceWith(repl); } } @@ -853,7 +939,7 @@ void HLoopOptimization::GenerateNewLoop(LoopNode* node, HInstruction* step, uint32_t unroll) { DCHECK(unroll == 1 || vector_mode_ == kVector); - Primitive::Type induc_type = Primitive::kPrimInt; + Primitive::Type induc_type = lo->GetType(); // Prepare new loop. vector_preheader_ = new_preheader, vector_header_ = vector_preheader_->GetSingleSuccessor(); @@ -942,8 +1028,10 @@ bool HLoopOptimization::VectorizeDef(LoopNode* node, auto redit = reductions_->find(instruction); if (redit != reductions_->end()) { Primitive::Type type = instruction->GetType(); - if (TrySetVectorType(type, &restrictions) && - VectorizeUse(node, instruction, generate_code, type, restrictions)) { + // Recognize SAD idiom or direct reduction. + if (VectorizeSADIdiom(node, instruction, generate_code, type, restrictions) || + (TrySetVectorType(type, &restrictions) && + VectorizeUse(node, instruction, generate_code, type, restrictions))) { if (generate_code) { HInstruction* new_red = vector_map_->Get(instruction); vector_permanent_map_->Put(new_red, vector_map_->Get(redit->second)); @@ -1029,14 +1117,20 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, HInstruction* opa = conversion->InputAt(0); Primitive::Type from = conversion->GetInputType(); Primitive::Type to = conversion->GetResultType(); - if ((to == Primitive::kPrimByte || - to == Primitive::kPrimChar || - to == Primitive::kPrimShort) && from == Primitive::kPrimInt) { - // Accept a "narrowing" type conversion from a "wider" computation for - // (1) conversion into final required type, - // (2) vectorizable operand, - // (3) "wider" operations cannot bring in higher order bits. - if (to == type && VectorizeUse(node, opa, generate_code, type, restrictions | kNoHiBits)) { + if (Primitive::IsIntegralType(from) && Primitive::IsIntegralType(to)) { + size_t size_vec = Primitive::ComponentSize(type); + size_t size_from = Primitive::ComponentSize(from); + size_t size_to = Primitive::ComponentSize(to); + // Accept an integral conversion + // (1a) narrowing into vector type, "wider" operations cannot bring in higher order bits, or + // (1b) widening from at least vector type, and + // (2) vectorizable operand. + if ((size_to < size_from && + size_to == size_vec && + VectorizeUse(node, opa, generate_code, type, restrictions | kNoHiBits)) || + (size_to >= size_from && + size_from >= size_vec && + VectorizeUse(node, opa, generate_code, type, restrictions))) { if (generate_code) { if (vector_mode_ == kVector) { vector_map_->Put(instruction, vector_map_->Get(opa)); // operand pass-through @@ -1088,7 +1182,7 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, return true; } } else if (instruction->IsShl() || instruction->IsShr() || instruction->IsUShr()) { - // Recognize vectorization idioms. + // Recognize halving add idiom. if (VectorizeHalvingAddIdiom(node, instruction, generate_code, type, restrictions)) { return true; } @@ -1181,7 +1275,8 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, return false; // reject, unless all operands are same-extension narrower } // Accept MIN/MAX(x, y) for vectorizable operands. - DCHECK(r != nullptr && s != nullptr); + DCHECK(r != nullptr); + DCHECK(s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = opa; s = opb; @@ -1232,11 +1327,11 @@ bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restric switch (type) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: - *restrictions |= kNoDiv | kNoReduction; + *restrictions |= kNoDiv; return TrySetVectorLength(16); case Primitive::kPrimChar: case Primitive::kPrimShort: - *restrictions |= kNoDiv | kNoReduction; + *restrictions |= kNoDiv; return TrySetVectorLength(8); case Primitive::kPrimInt: *restrictions |= kNoDiv; @@ -1261,17 +1356,17 @@ bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restric case Primitive::kPrimBoolean: case Primitive::kPrimByte: *restrictions |= - kNoMul | kNoDiv | kNoShift | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd | kNoReduction; + kNoMul | kNoDiv | kNoShift | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd | kNoSAD; return TrySetVectorLength(16); case Primitive::kPrimChar: case Primitive::kPrimShort: - *restrictions |= kNoDiv | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd | kNoReduction; + *restrictions |= kNoDiv | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd | kNoSAD; return TrySetVectorLength(8); case Primitive::kPrimInt: - *restrictions |= kNoDiv; + *restrictions |= kNoDiv | kNoSAD; return TrySetVectorLength(4); case Primitive::kPrimLong: - *restrictions |= kNoMul | kNoDiv | kNoShr | kNoAbs | kNoMinMax; + *restrictions |= kNoMul | kNoDiv | kNoShr | kNoAbs | kNoMinMax | kNoSAD; return TrySetVectorLength(2); case Primitive::kPrimFloat: *restrictions |= kNoMinMax | kNoReduction; // minmax: -0.0 vs +0.0 @@ -1289,17 +1384,17 @@ bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restric switch (type) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: - *restrictions |= kNoDiv | kNoReduction; + *restrictions |= kNoDiv | kNoReduction | kNoSAD; return TrySetVectorLength(16); case Primitive::kPrimChar: case Primitive::kPrimShort: - *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction; + *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction | kNoSAD; return TrySetVectorLength(8); case Primitive::kPrimInt: - *restrictions |= kNoDiv | kNoReduction; + *restrictions |= kNoDiv | kNoReduction | kNoSAD; return TrySetVectorLength(4); case Primitive::kPrimLong: - *restrictions |= kNoDiv | kNoReduction; + *restrictions |= kNoDiv | kNoReduction | kNoSAD; return TrySetVectorLength(2); case Primitive::kPrimFloat: *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) @@ -1317,17 +1412,17 @@ bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restric switch (type) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: - *restrictions |= kNoDiv | kNoReduction; + *restrictions |= kNoDiv | kNoReduction | kNoSAD; return TrySetVectorLength(16); case Primitive::kPrimChar: case Primitive::kPrimShort: - *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction; + *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction | kNoSAD; return TrySetVectorLength(8); case Primitive::kPrimInt: - *restrictions |= kNoDiv | kNoReduction; + *restrictions |= kNoDiv | kNoReduction | kNoSAD; return TrySetVectorLength(4); case Primitive::kPrimLong: - *restrictions |= kNoDiv | kNoReduction; + *restrictions |= kNoDiv | kNoReduction | kNoSAD; return TrySetVectorLength(2); case Primitive::kPrimFloat: *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) @@ -1371,8 +1466,16 @@ void HLoopOptimization::GenerateVecInv(HInstruction* org, Primitive::Type type) if (it != vector_permanent_map_->end()) { vector = it->second; // reuse during unrolling } else { - vector = new (global_allocator_) HVecReplicateScalar( - global_allocator_, org, type, vector_length_); + // Generates ReplicateScalar( (optional_type_conv) org ). + HInstruction* input = org; + Primitive::Type input_type = input->GetType(); + if (type != input_type && (type == Primitive::kPrimLong || + input_type == Primitive::kPrimLong)) { + input = Insert(vector_preheader_, + new (global_allocator_) HTypeConversion(type, input, kNoDexPc)); + } + vector = new (global_allocator_) + HVecReplicateScalar(global_allocator_, input, type, vector_length_); vector_permanent_map_->Put(org, Insert(vector_preheader_, vector)); } vector_map_->Put(org, vector); @@ -1465,10 +1568,15 @@ void HLoopOptimization::GenerateVecReductionPhiInputs(HPhi* phi, HInstruction* r // Prepare the new initialization. if (vector_mode_ == kVector) { // Generate a [initial, 0, .., 0] vector. - new_init = Insert( - vector_preheader_, - new (global_allocator_) HVecSetScalars( - global_allocator_, &new_init, phi->GetType(), vector_length_, 1)); + HVecOperation* red_vector = new_red->AsVecOperation(); + size_t vector_length = red_vector->GetVectorLength(); + Primitive::Type type = red_vector->GetPackedType(); + new_init = Insert(vector_preheader_, + new (global_allocator_) HVecSetScalars(global_allocator_, + &new_init, + type, + vector_length, + 1)); } else { new_init = ReduceAndExtractIfNeeded(new_init); } @@ -1484,18 +1592,20 @@ HInstruction* HLoopOptimization::ReduceAndExtractIfNeeded(HInstruction* instruct if (instruction->IsPhi()) { HInstruction* input = instruction->InputAt(1); if (input->IsVecOperation()) { - Primitive::Type type = input->AsVecOperation()->GetPackedType(); + HVecOperation* input_vector = input->AsVecOperation(); + size_t vector_length = input_vector->GetVectorLength(); + Primitive::Type type = input_vector->GetPackedType(); + HVecReduce::ReductionKind kind = GetReductionKind(input_vector); HBasicBlock* exit = instruction->GetBlock()->GetSuccessors()[0]; // Generate a vector reduction and scalar extract // x = REDUCE( [x_1, .., x_n] ) // y = x_1 // along the exit of the defining loop. - HVecReduce::ReductionKind kind = GetReductionKind(input); HInstruction* reduce = new (global_allocator_) HVecReduce( - global_allocator_, instruction, type, vector_length_, kind); + global_allocator_, instruction, type, vector_length, kind); exit->InsertInstructionBefore(reduce, exit->GetFirstInstruction()); instruction = new (global_allocator_) HVecExtractScalar( - global_allocator_, reduce, type, vector_length_, 0); + global_allocator_, reduce, type, vector_length, 0); exit->InsertInstructionAfter(instruction, reduce); } } @@ -1516,27 +1626,19 @@ void HLoopOptimization::GenerateVecOp(HInstruction* org, HInstruction* opb, Primitive::Type type, bool is_unsigned) { - if (vector_mode_ == kSequential) { - // Non-converting scalar code follows implicit integral promotion. - if (!org->IsTypeConversion() && (type == Primitive::kPrimBoolean || - type == Primitive::kPrimByte || - type == Primitive::kPrimChar || - type == Primitive::kPrimShort)) { - type = Primitive::kPrimInt; - } - } HInstruction* vector = nullptr; + Primitive::Type org_type = org->GetType(); switch (org->GetKind()) { case HInstruction::kNeg: DCHECK(opb == nullptr); GENERATE_VEC( new (global_allocator_) HVecNeg(global_allocator_, opa, type, vector_length_), - new (global_allocator_) HNeg(type, opa)); + new (global_allocator_) HNeg(org_type, opa)); case HInstruction::kNot: DCHECK(opb == nullptr); GENERATE_VEC( new (global_allocator_) HVecNot(global_allocator_, opa, type, vector_length_), - new (global_allocator_) HNot(type, opa)); + new (global_allocator_) HNot(org_type, opa)); case HInstruction::kBooleanNot: DCHECK(opb == nullptr); GENERATE_VEC( @@ -1546,47 +1648,47 @@ void HLoopOptimization::GenerateVecOp(HInstruction* org, DCHECK(opb == nullptr); GENERATE_VEC( new (global_allocator_) HVecCnv(global_allocator_, opa, type, vector_length_), - new (global_allocator_) HTypeConversion(type, opa, kNoDexPc)); + new (global_allocator_) HTypeConversion(org_type, opa, kNoDexPc)); case HInstruction::kAdd: GENERATE_VEC( new (global_allocator_) HVecAdd(global_allocator_, opa, opb, type, vector_length_), - new (global_allocator_) HAdd(type, opa, opb)); + new (global_allocator_) HAdd(org_type, opa, opb)); case HInstruction::kSub: GENERATE_VEC( new (global_allocator_) HVecSub(global_allocator_, opa, opb, type, vector_length_), - new (global_allocator_) HSub(type, opa, opb)); + new (global_allocator_) HSub(org_type, opa, opb)); case HInstruction::kMul: GENERATE_VEC( new (global_allocator_) HVecMul(global_allocator_, opa, opb, type, vector_length_), - new (global_allocator_) HMul(type, opa, opb)); + new (global_allocator_) HMul(org_type, opa, opb)); case HInstruction::kDiv: GENERATE_VEC( new (global_allocator_) HVecDiv(global_allocator_, opa, opb, type, vector_length_), - new (global_allocator_) HDiv(type, opa, opb, kNoDexPc)); + new (global_allocator_) HDiv(org_type, opa, opb, kNoDexPc)); case HInstruction::kAnd: GENERATE_VEC( new (global_allocator_) HVecAnd(global_allocator_, opa, opb, type, vector_length_), - new (global_allocator_) HAnd(type, opa, opb)); + new (global_allocator_) HAnd(org_type, opa, opb)); case HInstruction::kOr: GENERATE_VEC( new (global_allocator_) HVecOr(global_allocator_, opa, opb, type, vector_length_), - new (global_allocator_) HOr(type, opa, opb)); + new (global_allocator_) HOr(org_type, opa, opb)); case HInstruction::kXor: GENERATE_VEC( new (global_allocator_) HVecXor(global_allocator_, opa, opb, type, vector_length_), - new (global_allocator_) HXor(type, opa, opb)); + new (global_allocator_) HXor(org_type, opa, opb)); case HInstruction::kShl: GENERATE_VEC( new (global_allocator_) HVecShl(global_allocator_, opa, opb, type, vector_length_), - new (global_allocator_) HShl(type, opa, opb)); + new (global_allocator_) HShl(org_type, opa, opb)); case HInstruction::kShr: GENERATE_VEC( new (global_allocator_) HVecShr(global_allocator_, opa, opb, type, vector_length_), - new (global_allocator_) HShr(type, opa, opb)); + new (global_allocator_) HShr(org_type, opa, opb)); case HInstruction::kUShr: GENERATE_VEC( new (global_allocator_) HVecUShr(global_allocator_, opa, opb, type, vector_length_), - new (global_allocator_) HUShr(type, opa, opb)); + new (global_allocator_) HUShr(org_type, opa, opb)); case HInstruction::kInvokeStaticOrDirect: { HInvokeStaticOrDirect* invoke = org->AsInvokeStaticOrDirect(); if (vector_mode_ == kVector) { @@ -1667,8 +1769,8 @@ void HLoopOptimization::GenerateVecOp(HInstruction* org, // // Method recognizes the following idioms: -// rounding halving add (a + b + 1) >> 1 for unsigned/signed operands a, b -// regular halving add (a + b) >> 1 for unsigned/signed operands a, b +// rounding halving add (a + b + 1) >> 1 for unsigned/signed operands a, b +// truncated halving add (a + b) >> 1 for unsigned/signed operands a, b // Provided that the operands are promoted to a wider form to do the arithmetic and // then cast back to narrower form, the idioms can be mapped into efficient SIMD // implementation that operates directly in narrower form (plus one extra bit). @@ -1712,7 +1814,8 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node, } // Accept recognized halving add for vectorizable operands. Vectorized code uses the // shorthand idiomatic operation. Sequential code uses the original scalar expressions. - DCHECK(r != nullptr && s != nullptr); + DCHECK(r != nullptr); + DCHECK(s != nullptr); if (generate_code && vector_mode_ != kVector) { // de-idiom r = instruction->InputAt(0); s = instruction->InputAt(1); @@ -1741,6 +1844,88 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node, return false; } +// Method recognizes the following idiom: +// q += ABS(a - b) for signed operands a, b +// Provided that the operands have the same type or are promoted to a wider form. +// Since this may involve a vector length change, the idiom is handled by going directly +// to a sad-accumulate node (rather than relying combining finer grained nodes later). +// TODO: unsigned SAD too? +bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node, + HInstruction* instruction, + bool generate_code, + Primitive::Type reduction_type, + uint64_t restrictions) { + // Filter integral "q += ABS(a - b);" reduction, where ABS and SUB + // are done in the same precision (either int or long). + if (!instruction->IsAdd() || + (reduction_type != Primitive::kPrimInt && reduction_type != Primitive::kPrimLong)) { + return false; + } + HInstruction* q = instruction->InputAt(0); + HInstruction* v = instruction->InputAt(1); + HInstruction* a = nullptr; + HInstruction* b = nullptr; + if (v->IsInvokeStaticOrDirect() && + (v->AsInvokeStaticOrDirect()->GetIntrinsic() == Intrinsics::kMathAbsInt || + v->AsInvokeStaticOrDirect()->GetIntrinsic() == Intrinsics::kMathAbsLong)) { + HInstruction* x = v->InputAt(0); + if (x->IsSub() && x->GetType() == reduction_type) { + a = x->InputAt(0); + b = x->InputAt(1); + } + } + if (a == nullptr || b == nullptr) { + return false; + } + // Accept same-type or consistent sign extension for narrower-type on operands a and b. + // The same-type or narrower operands are called r (a or lower) and s (b or lower). + HInstruction* r = a; + HInstruction* s = b; + bool is_unsigned = false; + Primitive::Type sub_type = a->GetType(); + if (a->IsTypeConversion()) { + sub_type = a->InputAt(0)->GetType(); + } else if (b->IsTypeConversion()) { + sub_type = b->InputAt(0)->GetType(); + } + if (reduction_type != sub_type && + (!IsNarrowerOperands(a, b, sub_type, &r, &s, &is_unsigned) || is_unsigned)) { + return false; + } + // Try same/narrower type and deal with vector restrictions. + if (!TrySetVectorType(sub_type, &restrictions) || HasVectorRestrictions(restrictions, kNoSAD)) { + return false; + } + // Accept SAD idiom for vectorizable operands. Vectorized code uses the shorthand + // idiomatic operation. Sequential code uses the original scalar expressions. + DCHECK(r != nullptr); + DCHECK(s != nullptr); + if (generate_code && vector_mode_ != kVector) { // de-idiom + r = s = v->InputAt(0); + } + if (VectorizeUse(node, q, generate_code, sub_type, restrictions) && + VectorizeUse(node, r, generate_code, sub_type, restrictions) && + VectorizeUse(node, s, generate_code, sub_type, restrictions)) { + if (generate_code) { + if (vector_mode_ == kVector) { + vector_map_->Put(instruction, new (global_allocator_) HVecSADAccumulate( + global_allocator_, + vector_map_->Get(q), + vector_map_->Get(r), + vector_map_->Get(s), + reduction_type, + GetOtherVL(reduction_type, sub_type, vector_length_))); + MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorizedIdiom); + } else { + GenerateVecOp(v, vector_map_->Get(r), nullptr, reduction_type); + GenerateVecOp(instruction, vector_map_->Get(q), vector_map_->Get(v), reduction_type); + } + } + return true; + } + return false; +} + // // Vectorization heuristics. // diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index f34751815b..ae2ea76f47 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -75,6 +75,7 @@ class HLoopOptimization : public HOptimization { kNoMinMax = 1 << 8, // no min/max kNoStringCharAt = 1 << 9, // no StringCharAt kNoReduction = 1 << 10, // no reduction + kNoSAD = 1 << 11, // no sum of absolute differences (SAD) }; /* @@ -172,6 +173,11 @@ class HLoopOptimization : public HOptimization { bool generate_code, Primitive::Type type, uint64_t restrictions); + bool VectorizeSADIdiom(LoopNode* node, + HInstruction* instruction, + bool generate_code, + Primitive::Type type, + uint64_t restrictions); // Vectorization heuristics. bool IsVectorizationProfitable(int64_t trip_count); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index a6d0da1c96..6bc5111de2 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1396,6 +1396,7 @@ class HLoopInformationOutwardIterator : public ValueObject { M(VecUShr, VecBinaryOperation) \ M(VecSetScalars, VecOperation) \ M(VecMultiplyAccumulate, VecOperation) \ + M(VecSADAccumulate, VecOperation) \ M(VecLoad, VecMemoryOperation) \ M(VecStore, VecMemoryOperation) \ diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 886d75e5c7..1488b7086a 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -461,8 +461,8 @@ class HVecAdd FINAL : public HVecBinaryOperation { }; // Performs halving add on every component in the two vectors, viz. -// rounded [ x1, .. , xn ] hradd [ y1, .. , yn ] = [ (x1 + y1 + 1) >> 1, .. , (xn + yn + 1) >> 1 ] -// or [ x1, .. , xn ] hadd [ y1, .. , yn ] = [ (x1 + y1) >> 1, .. , (xn + yn ) >> 1 ] +// rounded [ x1, .. , xn ] hradd [ y1, .. , yn ] = [ (x1 + y1 + 1) >> 1, .. , (xn + yn + 1) >> 1 ] +// truncated [ x1, .. , xn ] hadd [ y1, .. , yn ] = [ (x1 + y1) >> 1, .. , (xn + yn ) >> 1 ] // for signed operands x, y (sign extension) or unsigned operands x, y (zero extension). class HVecHalvingAdd FINAL : public HVecBinaryOperation { public: @@ -810,12 +810,12 @@ class HVecUShr FINAL : public HVecBinaryOperation { // // Assigns the given scalar elements to a vector, -// viz. set( array(x1, .., xn) ) = [ x1, .. , xn ] if n == m, -// set( array(x1, .., xm) ) = [ x1, .. , xm, 0, .., 0 ] if m < n. +// viz. set( array(x1, .. , xn) ) = [ x1, .. , xn ] if n == m, +// set( array(x1, .. , xm) ) = [ x1, .. , xm, 0, .. , 0 ] if m < n. class HVecSetScalars FINAL : public HVecOperation { public: HVecSetScalars(ArenaAllocator* arena, - HInstruction** scalars, // array + HInstruction* scalars[], Primitive::Type packed_type, size_t vector_length, size_t number_of_scalars, @@ -827,7 +827,7 @@ class HVecSetScalars FINAL : public HVecOperation { vector_length, dex_pc) { for (size_t i = 0; i < number_of_scalars; i++) { - DCHECK(!scalars[i]->IsVecOperation()); + DCHECK(!scalars[i]->IsVecOperation() || scalars[i]->IsVecExtractScalar()); SetRawInputAt(0, scalars[i]); } } @@ -842,9 +842,8 @@ class HVecSetScalars FINAL : public HVecOperation { DISALLOW_COPY_AND_ASSIGN(HVecSetScalars); }; -// Multiplies every component in the two vectors, adds the result vector to the accumulator vector. -// viz. [ acc1, .., accn ] + [ x1, .. , xn ] * [ y1, .. , yn ] = -// [ acc1 + x1 * y1, .. , accn + xn * yn ]. +// Multiplies every component in the two vectors, adds the result vector to the accumulator vector, +// viz. [ a1, .. , an ] + [ x1, .. , xn ] * [ y1, .. , yn ] = [ a1 + x1 * y1, .. , an + xn * yn ]. class HVecMultiplyAccumulate FINAL : public HVecOperation { public: HVecMultiplyAccumulate(ArenaAllocator* arena, @@ -866,15 +865,11 @@ class HVecMultiplyAccumulate FINAL : public HVecOperation { DCHECK(HasConsistentPackedTypes(accumulator, packed_type)); DCHECK(HasConsistentPackedTypes(mul_left, packed_type)); DCHECK(HasConsistentPackedTypes(mul_right, packed_type)); - SetRawInputAt(kInputAccumulatorIndex, accumulator); - SetRawInputAt(kInputMulLeftIndex, mul_left); - SetRawInputAt(kInputMulRightIndex, mul_right); + SetRawInputAt(0, accumulator); + SetRawInputAt(1, mul_left); + SetRawInputAt(2, mul_right); } - static constexpr int kInputAccumulatorIndex = 0; - static constexpr int kInputMulLeftIndex = 1; - static constexpr int kInputMulRightIndex = 2; - bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { @@ -894,6 +889,42 @@ class HVecMultiplyAccumulate FINAL : public HVecOperation { DISALLOW_COPY_AND_ASSIGN(HVecMultiplyAccumulate); }; +// Takes the absolute difference of two vectors, and adds the results to +// same-precision or wider-precision components in the accumulator, +// viz. SAD([ a1, .. , am ], [ x1, .. , xn ], [ y1, .. , yn ] = +// [ a1 + sum abs(xi-yi), .. , am + sum abs(xj-yj) ], +// for m <= n and non-overlapping sums. +class HVecSADAccumulate FINAL : public HVecOperation { + public: + HVecSADAccumulate(ArenaAllocator* arena, + HInstruction* accumulator, + HInstruction* sad_left, + HInstruction* sad_right, + Primitive::Type packed_type, + size_t vector_length, + uint32_t dex_pc = kNoDexPc) + : HVecOperation(arena, + packed_type, + SideEffects::None(), + /* number_of_inputs */ 3, + vector_length, + dex_pc) { + DCHECK(HasConsistentPackedTypes(accumulator, packed_type)); + DCHECK(sad_left->IsVecOperation()); + DCHECK(sad_right->IsVecOperation()); + DCHECK_EQ(sad_left->AsVecOperation()->GetPackedType(), + sad_right->AsVecOperation()->GetPackedType()); + SetRawInputAt(0, accumulator); + SetRawInputAt(1, sad_left); + SetRawInputAt(2, sad_right); + } + + DECLARE_INSTRUCTION(VecSADAccumulate); + + private: + DISALLOW_COPY_AND_ASSIGN(HVecSADAccumulate); +}; + // Loads a vector from memory, viz. load(mem, 1) // yield the vector [ mem(1), .. , mem(n) ]. class HVecLoad FINAL : public HVecMemoryOperation { diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index e128a15cfd..7451196677 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -63,7 +63,6 @@ #include "driver/compiler_driver-inl.h" #include "driver/compiler_options.h" #include "driver/dex_compilation_unit.h" -#include "elf_writer_quick.h" #include "graph_checker.h" #include "graph_visualizer.h" #include "gvn.h" @@ -78,6 +77,7 @@ #include "jit/jit_logger.h" #include "jni/quick/jni_compiler.h" #include "licm.h" +#include "linker/linker_patch.h" #include "load_store_analysis.h" #include "load_store_elimination.h" #include "loop_optimization.h" @@ -834,13 +834,13 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, RunArchOptimizations(driver->GetInstructionSet(), graph, codegen, pass_observer); } -static ArenaVector<LinkerPatch> EmitAndSortLinkerPatches(CodeGenerator* codegen) { - ArenaVector<LinkerPatch> linker_patches(codegen->GetGraph()->GetArena()->Adapter()); +static ArenaVector<linker::LinkerPatch> EmitAndSortLinkerPatches(CodeGenerator* codegen) { + ArenaVector<linker::LinkerPatch> linker_patches(codegen->GetGraph()->GetArena()->Adapter()); codegen->EmitLinkerPatches(&linker_patches); // Sort patches by literal offset. Required for .oat_patches encoding. std::sort(linker_patches.begin(), linker_patches.end(), - [](const LinkerPatch& lhs, const LinkerPatch& rhs) { + [](const linker::LinkerPatch& lhs, const linker::LinkerPatch& rhs) { return lhs.LiteralOffset() < rhs.LiteralOffset(); }); @@ -852,7 +852,7 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* arena, CodeGenerator* codegen, CompilerDriver* compiler_driver, const DexFile::CodeItem* code_item) const { - ArenaVector<LinkerPatch> linker_patches = EmitAndSortLinkerPatches(codegen); + ArenaVector<linker::LinkerPatch> linker_patches = EmitAndSortLinkerPatches(codegen); ArenaVector<uint8_t> stack_map(arena->Adapter(kArenaAllocStackMaps)); ArenaVector<uint8_t> method_info(arena->Adapter(kArenaAllocStackMaps)); size_t stack_map_size = 0; @@ -877,7 +877,7 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* arena, ArrayRef<const uint8_t>(method_info), ArrayRef<const uint8_t>(stack_map), ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data()), - ArrayRef<const LinkerPatch>(linker_patches)); + ArrayRef<const linker::LinkerPatch>(linker_patches)); return compiled_method; } diff --git a/compiler/optimizing/scheduler_arm.cc b/compiler/optimizing/scheduler_arm.cc index d6eb6e3c52..66756a5fc7 100644 --- a/compiler/optimizing/scheduler_arm.cc +++ b/compiler/optimizing/scheduler_arm.cc @@ -19,6 +19,7 @@ #include "arch/arm/instruction_set_features_arm.h" #include "code_generator_utils.h" #include "common_arm.h" +#include "heap_poisoning.h" #include "mirror/array-inl.h" #include "mirror/string.h" diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 185303bc8c..754a762214 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -474,7 +474,9 @@ size_t LiveInterval::NumberOfSpillSlotsNeeded() const { // For a SIMD operation, compute the number of needed spill slots. // TODO: do through vector type? HInstruction* definition = GetParent()->GetDefinedBy(); - if (definition != nullptr && definition->IsVecOperation()) { + if (definition != nullptr && + definition->IsVecOperation() && + !definition->IsVecExtractScalar()) { return definition->AsVecOperation()->GetVectorNumberOfBytes() / kVRegSize; } // Return number of needed spill slots based on type. diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc index 9df1b7434a..34849cd58d 100644 --- a/compiler/utils/arm/assembler_arm_vixl.cc +++ b/compiler/utils/arm/assembler_arm_vixl.cc @@ -21,6 +21,7 @@ #include "base/bit_utils.h" #include "base/bit_utils_iterator.h" #include "entrypoints/quick/quick_entrypoints.h" +#include "heap_poisoning.h" #include "thread.h" using namespace vixl::aarch32; // NOLINT(build/namespaces) diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc index d8a48a563c..bb989588d6 100644 --- a/compiler/utils/arm64/assembler_arm64.cc +++ b/compiler/utils/arm64/assembler_arm64.cc @@ -17,6 +17,7 @@ #include "assembler_arm64.h" #include "base/logging.h" #include "entrypoints/quick/quick_entrypoints.h" +#include "heap_poisoning.h" #include "offsets.h" #include "thread.h" diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h index 12954a4c32..227954e21b 100644 --- a/compiler/utils/assembler_test.h +++ b/compiler/utils/assembler_test.h @@ -114,6 +114,24 @@ class AssemblerTest : public testing::Test { fmt); } + std::string Repeatww(void (Ass::*f)(Reg, Reg), const std::string& fmt) { + return RepeatTemplatedRegisters<Reg, Reg>(f, + GetRegisters(), + GetRegisters(), + &AssemblerTest::GetRegName<RegisterView::kUseTertiaryName>, + &AssemblerTest::GetRegName<RegisterView::kUseTertiaryName>, + fmt); + } + + std::string Repeatbb(void (Ass::*f)(Reg, Reg), const std::string& fmt) { + return RepeatTemplatedRegisters<Reg, Reg>(f, + GetRegisters(), + GetRegisters(), + &AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>, + &AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>, + fmt); + } + std::string RepeatRRR(void (Ass::*f)(Reg, Reg, Reg), const std::string& fmt) { return RepeatTemplatedRegisters<Reg, Reg, Reg>(f, GetRegisters(), @@ -147,10 +165,18 @@ class AssemblerTest : public testing::Test { return RepeatRegisterImm<RegisterView::kUsePrimaryName>(f, imm_bytes, fmt); } - std::string Repeatri(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) { + std::string RepeatrI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) { return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt); } + std::string RepeatwI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) { + return RepeatRegisterImm<RegisterView::kUseTertiaryName>(f, imm_bytes, fmt); + } + + std::string RepeatbI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) { + return RepeatRegisterImm<RegisterView::kUseQuaternaryName>(f, imm_bytes, fmt); + } + template <typename Reg1, typename Reg2, typename ImmType> std::string RepeatTemplatedRegistersImmBits(void (Ass::*f)(Reg1, Reg2, ImmType), int imm_bits, @@ -909,6 +935,63 @@ class AssemblerTest : public testing::Test { fmt); } + // Repeats over secondary registers and addresses provided by fixture. + std::string RepeatrA(void (Ass::*f)(Reg, const Addr&), const std::string& fmt) { + return RepeatrA(f, GetAddresses(), fmt); + } + + // Variant that takes explicit vector of addresss + // (to test restricted addressing modes set). + std::string RepeatrA(void (Ass::*f)(Reg, const Addr&), + const std::vector<Addr>& a, + const std::string& fmt) { + return RepeatTemplatedRegMem<Reg, Addr>( + f, + GetRegisters(), + a, + &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, + &AssemblerTest::GetAddrName, + fmt); + } + + // Repeats over tertiary registers and addresses provided by fixture. + std::string RepeatwA(void (Ass::*f)(Reg, const Addr&), const std::string& fmt) { + return RepeatwA(f, GetAddresses(), fmt); + } + + // Variant that takes explicit vector of addresss + // (to test restricted addressing modes set). + std::string RepeatwA(void (Ass::*f)(Reg, const Addr&), + const std::vector<Addr>& a, + const std::string& fmt) { + return RepeatTemplatedRegMem<Reg, Addr>( + f, + GetRegisters(), + a, + &AssemblerTest::GetRegName<RegisterView::kUseTertiaryName>, + &AssemblerTest::GetAddrName, + fmt); + } + + // Repeats over quaternary registers and addresses provided by fixture. + std::string RepeatbA(void (Ass::*f)(Reg, const Addr&), const std::string& fmt) { + return RepeatbA(f, GetAddresses(), fmt); + } + + // Variant that takes explicit vector of addresss + // (to test restricted addressing modes set). + std::string RepeatbA(void (Ass::*f)(Reg, const Addr&), + const std::vector<Addr>& a, + const std::string& fmt) { + return RepeatTemplatedRegMem<Reg, Addr>( + f, + GetRegisters(), + a, + &AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>, + &AssemblerTest::GetAddrName, + fmt); + } + // Repeats over fp-registers and addresses provided by fixture. std::string RepeatFA(void (Ass::*f)(FPReg, const Addr&), const std::string& fmt) { return RepeatFA(f, GetAddresses(), fmt); @@ -947,6 +1030,63 @@ class AssemblerTest : public testing::Test { fmt); } + // Repeats over addresses and secondary registers provided by fixture. + std::string RepeatAr(void (Ass::*f)(const Addr&, Reg), const std::string& fmt) { + return RepeatAr(f, GetAddresses(), fmt); + } + + // Variant that takes explicit vector of addresss + // (to test restricted addressing modes set). + std::string RepeatAr(void (Ass::*f)(const Addr&, Reg), + const std::vector<Addr>& a, + const std::string& fmt) { + return RepeatTemplatedMemReg<Addr, Reg>( + f, + a, + GetRegisters(), + &AssemblerTest::GetAddrName, + &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>, + fmt); + } + + // Repeats over addresses and tertiary registers provided by fixture. + std::string RepeatAw(void (Ass::*f)(const Addr&, Reg), const std::string& fmt) { + return RepeatAw(f, GetAddresses(), fmt); + } + + // Variant that takes explicit vector of addresss + // (to test restricted addressing modes set). + std::string RepeatAw(void (Ass::*f)(const Addr&, Reg), + const std::vector<Addr>& a, + const std::string& fmt) { + return RepeatTemplatedMemReg<Addr, Reg>( + f, + a, + GetRegisters(), + &AssemblerTest::GetAddrName, + &AssemblerTest::GetRegName<RegisterView::kUseTertiaryName>, + fmt); + } + + // Repeats over addresses and quaternary registers provided by fixture. + std::string RepeatAb(void (Ass::*f)(const Addr&, Reg), const std::string& fmt) { + return RepeatAb(f, GetAddresses(), fmt); + } + + // Variant that takes explicit vector of addresss + // (to test restricted addressing modes set). + std::string RepeatAb(void (Ass::*f)(const Addr&, Reg), + const std::vector<Addr>& a, + const std::string& fmt) { + return RepeatTemplatedMemReg<Addr, Reg>( + f, + a, + GetRegisters(), + &AssemblerTest::GetAddrName, + &AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>, + fmt); + } + // Repeats over addresses and fp-registers provided by fixture. std::string RepeatAF(void (Ass::*f)(const Addr&, FPReg), const std::string& fmt) { return RepeatAF(f, GetAddresses(), fmt); diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 0f163ac83f..0b4eb9ca55 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -28,6 +28,7 @@ #include "base/stl_util_identity.h" #include "constants_mips.h" #include "globals.h" +#include "heap_poisoning.h" #include "managed_register_mips.h" #include "offsets.h" #include "utils/assembler.h" diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h index dd6dcd1896..bb54382811 100644 --- a/compiler/utils/mips64/assembler_mips64.h +++ b/compiler/utils/mips64/assembler_mips64.h @@ -28,6 +28,7 @@ #include "base/stl_util_identity.h" #include "constants_mips64.h" #include "globals.h" +#include "heap_poisoning.h" #include "managed_register_mips64.h" #include "offsets.h" #include "utils/assembler.h" diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index 3162a32994..9fcede5e97 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -35,25 +35,25 @@ std::ostream& operator<<(std::ostream& os, const X87Register& reg) { std::ostream& operator<<(std::ostream& os, const Address& addr) { switch (addr.mod()) { case 0: - if (addr.rm() == ESP && addr.index() != ESP) { - return os << "(%" << addr.base() << ",%" - << addr.index() << "," << (1 << addr.scale()) << ")"; + if (addr.rm() != ESP || addr.index() == ESP) { + return os << "(%" << addr.rm() << ")"; + } else if (addr.base() == EBP) { + return os << static_cast<int>(addr.disp32()) << "(,%" << addr.index() + << "," << (1 << addr.scale()) << ")"; } - return os << "(%" << addr.rm() << ")"; + return os << "(%" << addr.base() << ",%" << addr.index() << "," << (1 << addr.scale()) << ")"; case 1: - if (addr.rm() == ESP && addr.index() != ESP) { - return os << static_cast<int>(addr.disp8()) - << "(%" << addr.base() << ",%" - << addr.index() << "," << (1 << addr.scale()) << ")"; + if (addr.rm() != ESP || addr.index() == ESP) { + return os << static_cast<int>(addr.disp8()) << "(%" << addr.rm() << ")"; } - return os << static_cast<int>(addr.disp8()) << "(%" << addr.rm() << ")"; + return os << static_cast<int>(addr.disp8()) << "(%" << addr.base() << ",%" + << addr.index() << "," << (1 << addr.scale()) << ")"; case 2: - if (addr.rm() == ESP && addr.index() != ESP) { - return os << static_cast<int>(addr.disp32()) - << "(%" << addr.base() << ",%" - << addr.index() << "," << (1 << addr.scale()) << ")"; + if (addr.rm() != ESP || addr.index() == ESP) { + return os << static_cast<int>(addr.disp32()) << "(%" << addr.rm() << ")"; } - return os << static_cast<int>(addr.disp32()) << "(%" << addr.rm() << ")"; + return os << static_cast<int>(addr.disp32()) << "(%" << addr.base() << ",%" + << addr.index() << "," << (1 << addr.scale()) << ")"; default: return os << "<address?>"; } diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 2964dbaabc..dce3ad228c 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -26,6 +26,7 @@ #include "base/macros.h" #include "constants_x86.h" #include "globals.h" +#include "heap_poisoning.h" #include "managed_register_x86.h" #include "offsets.h" #include "utils/assembler.h" diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index c28ed3b815..cccde37548 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -148,21 +148,14 @@ class AssemblerX86Test : public AssemblerTest<x86::X86Assembler, }; // -// Test repeat drivers used in the tests. +// Test some repeat drivers used in the tests. // TEST_F(AssemblerX86Test, RepeatRR) { - EXPECT_EQ("%eax %eax\n%eax %ebx\n%eax %ecx\n%eax %edx\n%eax %ebp\n%eax %esp\n%eax %esi\n" - "%eax %edi\n%ebx %eax\n%ebx %ebx\n%ebx %ecx\n%ebx %edx\n%ebx %ebp\n%ebx %esp\n" - "%ebx %esi\n%ebx %edi\n%ecx %eax\n%ecx %ebx\n%ecx %ecx\n%ecx %edx\n%ecx %ebp\n" - "%ecx %esp\n%ecx %esi\n%ecx %edi\n%edx %eax\n%edx %ebx\n%edx %ecx\n%edx %edx\n" - "%edx %ebp\n%edx %esp\n%edx %esi\n%edx %edi\n%ebp %eax\n%ebp %ebx\n%ebp %ecx\n" - "%ebp %edx\n%ebp %ebp\n%ebp %esp\n%ebp %esi\n%ebp %edi\n%esp %eax\n%esp %ebx\n" - "%esp %ecx\n%esp %edx\n%esp %ebp\n%esp %esp\n%esp %esi\n%esp %edi\n%esi %eax\n" - "%esi %ebx\n%esi %ecx\n%esi %edx\n%esi %ebp\n%esi %esp\n%esi %esi\n%esi %edi\n" - "%edi %eax\n%edi %ebx\n%edi %ecx\n%edi %edx\n%edi %ebp\n%edi %esp\n%edi %esi\n" - "%edi %edi\n", - RepeatRR(/*f*/ nullptr, "%{reg1} %{reg2}")); + EXPECT_NE(RepeatRR(/*f*/ nullptr, "%{reg1} %{reg2}") + .find("%eax %eax\n%eax %ebx\n%eax %ecx\n%eax %edx\n%eax %ebp\n%eax %esp\n%eax %esi\n" + "%eax %edi\n%ebx %eax\n%ebx %ebx\n%ebx %ecx\n%ebx %edx\n%ebx %ebp\n%ebx %esp\n"), + std::string::npos); } TEST_F(AssemblerX86Test, RepeatRI) { @@ -173,18 +166,10 @@ TEST_F(AssemblerX86Test, RepeatRI) { } TEST_F(AssemblerX86Test, RepeatFF) { - EXPECT_EQ("%XMM0 %XMM0\n%XMM0 %XMM1\n%XMM0 %XMM2\n%XMM0 %XMM3\n%XMM0 %XMM4\n%XMM0 %XMM5\n" - "%XMM0 %XMM6\n%XMM0 %XMM7\n%XMM1 %XMM0\n%XMM1 %XMM1\n%XMM1 %XMM2\n%XMM1 %XMM3\n" - "%XMM1 %XMM4\n%XMM1 %XMM5\n%XMM1 %XMM6\n%XMM1 %XMM7\n%XMM2 %XMM0\n%XMM2 %XMM1\n" - "%XMM2 %XMM2\n%XMM2 %XMM3\n%XMM2 %XMM4\n%XMM2 %XMM5\n%XMM2 %XMM6\n%XMM2 %XMM7\n" - "%XMM3 %XMM0\n%XMM3 %XMM1\n%XMM3 %XMM2\n%XMM3 %XMM3\n%XMM3 %XMM4\n%XMM3 %XMM5\n" - "%XMM3 %XMM6\n%XMM3 %XMM7\n%XMM4 %XMM0\n%XMM4 %XMM1\n%XMM4 %XMM2\n%XMM4 %XMM3\n" - "%XMM4 %XMM4\n%XMM4 %XMM5\n%XMM4 %XMM6\n%XMM4 %XMM7\n%XMM5 %XMM0\n%XMM5 %XMM1\n" - "%XMM5 %XMM2\n%XMM5 %XMM3\n%XMM5 %XMM4\n%XMM5 %XMM5\n%XMM5 %XMM6\n%XMM5 %XMM7\n" - "%XMM6 %XMM0\n%XMM6 %XMM1\n%XMM6 %XMM2\n%XMM6 %XMM3\n%XMM6 %XMM4\n%XMM6 %XMM5\n" - "%XMM6 %XMM6\n%XMM6 %XMM7\n%XMM7 %XMM0\n%XMM7 %XMM1\n%XMM7 %XMM2\n%XMM7 %XMM3\n" - "%XMM7 %XMM4\n%XMM7 %XMM5\n%XMM7 %XMM6\n%XMM7 %XMM7\n", - RepeatFF(/*f*/ nullptr, "%{reg1} %{reg2}")); + EXPECT_NE(RepeatFF(/*f*/ nullptr, "%{reg1} %{reg2}") + .find("%XMM0 %XMM0\n%XMM0 %XMM1\n%XMM0 %XMM2\n%XMM0 %XMM3\n%XMM0 %XMM4\n%XMM0 %XMM5\n" + "%XMM0 %XMM6\n%XMM0 %XMM7\n%XMM1 %XMM0\n%XMM1 %XMM1\n%XMM1 %XMM2\n%XMM1 %XMM3\n"), + std::string::npos); } TEST_F(AssemblerX86Test, RepeatFFI) { @@ -235,6 +220,36 @@ TEST_F(AssemblerX86Test, RepeatAF) { // Actual x86 instruction assembler tests. // +TEST_F(AssemblerX86Test, PoplAllAddresses) { + // Make sure all addressing modes combinations are tested at least once. + std::vector<x86::Address> all_addresses; + for (x86::Register* base : GetRegisters()) { + // Base only. + all_addresses.push_back(x86::Address(*base, -1)); + all_addresses.push_back(x86::Address(*base, 0)); + all_addresses.push_back(x86::Address(*base, 1)); + all_addresses.push_back(x86::Address(*base, 123456789)); + for (x86::Register* index : GetRegisters()) { + if (*index == x86::ESP) { + // Index cannot be ESP. + continue; + } else if (*base == *index) { + // Index only. + all_addresses.push_back(x86::Address(*index, x86::TIMES_1, -1)); + all_addresses.push_back(x86::Address(*index, x86::TIMES_2, 0)); + all_addresses.push_back(x86::Address(*index, x86::TIMES_4, 1)); + all_addresses.push_back(x86::Address(*index, x86::TIMES_8, 123456789)); + } + // Base and index. + all_addresses.push_back(x86::Address(*base, *index, x86::TIMES_1, -1)); + all_addresses.push_back(x86::Address(*base, *index, x86::TIMES_2, 0)); + all_addresses.push_back(x86::Address(*base, *index, x86::TIMES_4, 1)); + all_addresses.push_back(x86::Address(*base, *index, x86::TIMES_8, 123456789)); + } + } + DriverStr(RepeatA(&x86::X86Assembler::popl, all_addresses, "popl {mem}"), "popq"); +} + TEST_F(AssemblerX86Test, Movl) { DriverStr(RepeatRR(&x86::X86Assembler::movl, "movl %{reg2}, %{reg1}"), "movl"); } @@ -370,7 +385,7 @@ TEST_F(AssemblerX86Test, RorlReg) { } TEST_F(AssemblerX86Test, RorlImm) { - DriverStr(RepeatRI(&x86::X86Assembler::rorl, 1U, "rorl ${imm}, %{reg}"), "rorli"); + DriverStr(RepeatRI(&x86::X86Assembler::rorl, /*imm_bytes*/ 1U, "rorl ${imm}, %{reg}"), "rorli"); } // Roll only allows CL as the shift count. @@ -390,7 +405,7 @@ TEST_F(AssemblerX86Test, RollReg) { } TEST_F(AssemblerX86Test, RollImm) { - DriverStr(RepeatRI(&x86::X86Assembler::roll, 1U, "roll ${imm}, %{reg}"), "rolli"); + DriverStr(RepeatRI(&x86::X86Assembler::roll, /*imm_bytes*/ 1U, "roll ${imm}, %{reg}"), "rolli"); } TEST_F(AssemblerX86Test, Cvtdq2ps) { @@ -418,12 +433,12 @@ TEST_F(AssemblerX86Test, UComisdAddr) { } TEST_F(AssemblerX86Test, RoundSS) { - DriverStr(RepeatFFI(&x86::X86Assembler::roundss, 1U, + DriverStr(RepeatFFI(&x86::X86Assembler::roundss, /*imm_bytes*/ 1U, "roundss ${imm}, %{reg2}, %{reg1}"), "roundss"); } TEST_F(AssemblerX86Test, RoundSD) { - DriverStr(RepeatFFI(&x86::X86Assembler::roundsd, 1U, + DriverStr(RepeatFFI(&x86::X86Assembler::roundsd, /*imm_bytes*/ 1U, "roundsd ${imm}, %{reg2}, %{reg1}"), "roundsd"); } @@ -896,7 +911,15 @@ TEST_F(AssemblerX86Test, NearLabel) { } TEST_F(AssemblerX86Test, Cmpb) { - DriverStr(RepeatAI(&x86::X86Assembler::cmpb, /*imm_bytes*/ 1U, "cmpb ${imm}, {mem}"), "cmpb"); + DriverStr(RepeatAI(&x86::X86Assembler::cmpb, + /*imm_bytes*/ 1U, + "cmpb ${imm}, {mem}"), "cmpb"); +} + +TEST_F(AssemblerX86Test, Cmpw) { + DriverStr(RepeatAI(&x86::X86Assembler::cmpw, + /*imm_bytes*/ 1U, + "cmpw ${imm}, {mem}"), "cmpw"); // TODO: only imm8? } } // namespace art diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 3bff67d2f2..51f61ca756 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -36,6 +36,34 @@ std::ostream& operator<<(std::ostream& os, const X87Register& reg) { return os << "ST" << static_cast<int>(reg); } +std::ostream& operator<<(std::ostream& os, const Address& addr) { + switch (addr.mod()) { + case 0: + if (addr.rm() != RSP || addr.cpu_index().AsRegister() == RSP) { + return os << "(%" << addr.cpu_rm() << ")"; + } else if (addr.base() == RBP) { + return os << static_cast<int>(addr.disp32()) << "(,%" << addr.cpu_index() + << "," << (1 << addr.scale()) << ")"; + } + return os << "(%" << addr.cpu_base() << ",%" + << addr.cpu_index() << "," << (1 << addr.scale()) << ")"; + case 1: + if (addr.rm() != RSP || addr.cpu_index().AsRegister() == RSP) { + return os << static_cast<int>(addr.disp8()) << "(%" << addr.cpu_rm() << ")"; + } + return os << static_cast<int>(addr.disp8()) << "(%" << addr.cpu_base() << ",%" + << addr.cpu_index() << "," << (1 << addr.scale()) << ")"; + case 2: + if (addr.rm() != RSP || addr.cpu_index().AsRegister() == RSP) { + return os << static_cast<int>(addr.disp32()) << "(%" << addr.cpu_rm() << ")"; + } + return os << static_cast<int>(addr.disp32()) << "(%" << addr.cpu_base() << ",%" + << addr.cpu_index() << "," << (1 << addr.scale()) << ")"; + default: + return os << "<address?>"; + } +} + void X86_64Assembler::call(CpuRegister reg) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitOptionalRex32(reg); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 3dab235d1c..11304443e0 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -25,6 +25,7 @@ #include "base/macros.h" #include "constants_x86_64.h" #include "globals.h" +#include "heap_poisoning.h" #include "managed_register_x86_64.h" #include "offsets.h" #include "utils/assembler.h" @@ -79,6 +80,21 @@ class Operand : public ValueObject { return static_cast<Register>(encoding_at(1) & 7); } + CpuRegister cpu_rm() const { + int ext = (rex_ & 1) != 0 ? x86_64::R8 : x86_64::RAX; + return static_cast<CpuRegister>(rm() + ext); + } + + CpuRegister cpu_index() const { + int ext = (rex_ & 2) != 0 ? x86_64::R8 : x86_64::RAX; + return static_cast<CpuRegister>(index() + ext); + } + + CpuRegister cpu_base() const { + int ext = (rex_ & 1) != 0 ? x86_64::R8 : x86_64::RAX; + return static_cast<CpuRegister>(base() + ext); + } + uint8_t rex() const { return rex_; } @@ -267,6 +283,7 @@ class Address : public Operand { Address() {} }; +std::ostream& operator<<(std::ostream& os, const Address& addr); /** * Class to handle constant area values. diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 3e6110d4d0..aff8871025 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -153,6 +153,55 @@ class AssemblerX86_64Test : public AssemblerTest<x86_64::X86_64Assembler, } void SetUpHelpers() OVERRIDE { + if (addresses_singleton_.size() == 0) { + // One addressing mode to test the repeat drivers. + addresses_singleton_.push_back( + x86_64::Address(x86_64::CpuRegister(x86_64::RAX), + x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_1, -1)); + } + + if (addresses_.size() == 0) { + // Several addressing modes. + addresses_.push_back( + x86_64::Address(x86_64::CpuRegister(x86_64::RDI), + x86_64::CpuRegister(x86_64::RAX), x86_64::TIMES_1, 15)); + addresses_.push_back( + x86_64::Address(x86_64::CpuRegister(x86_64::RDI), + x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_2, 16)); + addresses_.push_back( + x86_64::Address(x86_64::CpuRegister(x86_64::RDI), + x86_64::CpuRegister(x86_64::RCX), x86_64::TIMES_4, 17)); + addresses_.push_back( + x86_64::Address(x86_64::CpuRegister(x86_64::RDI), + x86_64::CpuRegister(x86_64::RDX), x86_64::TIMES_8, 18)); + addresses_.push_back(x86_64::Address(x86_64::CpuRegister(x86_64::RAX), -1)); + addresses_.push_back(x86_64::Address(x86_64::CpuRegister(x86_64::RBX), 0)); + addresses_.push_back(x86_64::Address(x86_64::CpuRegister(x86_64::RSI), 1)); + addresses_.push_back(x86_64::Address(x86_64::CpuRegister(x86_64::RDI), 987654321)); + // Several addressing modes with the special ESP. + addresses_.push_back( + x86_64::Address(x86_64::CpuRegister(x86_64::RSP), + x86_64::CpuRegister(x86_64::RAX), x86_64::TIMES_1, 15)); + addresses_.push_back( + x86_64::Address(x86_64::CpuRegister(x86_64::RSP), + x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_2, 16)); + addresses_.push_back( + x86_64::Address(x86_64::CpuRegister(x86_64::RSP), + x86_64::CpuRegister(x86_64::RCX), x86_64::TIMES_4, 17)); + addresses_.push_back( + x86_64::Address(x86_64::CpuRegister(x86_64::RSP), + x86_64::CpuRegister(x86_64::RDX), x86_64::TIMES_8, 18)); + addresses_.push_back(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), -1)); + addresses_.push_back(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 0)); + addresses_.push_back(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 1)); + addresses_.push_back(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 987654321)); + // Several addressing modes with the higher registers. + addresses_.push_back( + x86_64::Address(x86_64::CpuRegister(x86_64::R8), + x86_64::CpuRegister(x86_64::R15), x86_64::TIMES_2, -1)); + addresses_.push_back(x86_64::Address(x86_64::CpuRegister(x86_64::R15), 123456789)); + } + if (registers_.size() == 0) { registers_.push_back(new x86_64::CpuRegister(x86_64::RAX)); registers_.push_back(new x86_64::CpuRegister(x86_64::RBX)); @@ -248,8 +297,7 @@ class AssemblerX86_64Test : public AssemblerTest<x86_64::X86_64Assembler, } std::vector<x86_64::Address> GetAddresses() { - UNIMPLEMENTED(FATAL) << "Feature not implemented yet"; - UNREACHABLE(); + return addresses_; } std::vector<x86_64::CpuRegister*> GetRegisters() OVERRIDE { @@ -279,29 +327,31 @@ class AssemblerX86_64Test : public AssemblerTest<x86_64::X86_64Assembler, return quaternary_register_names_[reg]; } + std::vector<x86_64::Address> addresses_singleton_; + private: + std::vector<x86_64::Address> addresses_; std::vector<x86_64::CpuRegister*> registers_; std::map<x86_64::CpuRegister, std::string, X86_64CpuRegisterCompare> secondary_register_names_; std::map<x86_64::CpuRegister, std::string, X86_64CpuRegisterCompare> tertiary_register_names_; std::map<x86_64::CpuRegister, std::string, X86_64CpuRegisterCompare> quaternary_register_names_; - std::vector<x86_64::XmmRegister*> fp_registers_; }; // -// Test repeat drivers used in the tests. +// Test some repeat drivers used in the tests. // TEST_F(AssemblerX86_64Test, RepeatI4) { - EXPECT_EQ("%0\n%-1\n%18\n%4660\n%-4660\n%305419896\n%-305419896\n", - RepeatI(/*f*/ nullptr, /*imm_bytes*/ 4U, "%{imm}")); + EXPECT_EQ("$0\n$-1\n$18\n$4660\n$-4660\n$305419896\n$-305419896\n", + RepeatI(/*f*/ nullptr, /*imm_bytes*/ 4U, "${imm}")); } TEST_F(AssemblerX86_64Test, RepeatI8) { - EXPECT_EQ("%0\n%-1\n%18\n%4660\n%-4660\n%305419896\n%-305419896\n" - "%20015998343868\n%-20015998343868\n%1311768467463790320\n" - "%-1311768467463790320\n", - RepeatI(/*f*/ nullptr, /*imm_bytes*/ 8U, "%{imm}")); + EXPECT_EQ("$0\n$-1\n$18\n$4660\n$-4660\n$305419896\n$-305419896\n" + "$20015998343868\n$-20015998343868\n$1311768467463790320\n" + "$-1311768467463790320\n", + RepeatI(/*f*/ nullptr, /*imm_bytes*/ 8U, "${imm}")); } TEST_F(AssemblerX86_64Test, Repeatr) { @@ -310,10 +360,10 @@ TEST_F(AssemblerX86_64Test, Repeatr) { Repeatr(/*f*/ nullptr, "%{reg}")); } -TEST_F(AssemblerX86_64Test, Repeatri) { - EXPECT_NE(Repeatri(/*f*/ nullptr, /*imm_bytes*/ 1U, "%{reg} %{imm}"). - find("%eax %0\n%eax %-1\n%eax %18\n%ebx %0\n%ebx %-1\n%ebx %18\n" - "%ecx %0\n%ecx %-1\n%ecx %18\n%edx %0\n%edx %-1\n%edx %18\n"), +TEST_F(AssemblerX86_64Test, RepeatrI) { + EXPECT_NE(RepeatrI(/*f*/ nullptr, /*imm_bytes*/ 1U, "%{reg} ${imm}"). + find("%eax $0\n%eax $-1\n%eax $18\n%ebx $0\n%ebx $-1\n%ebx $18\n" + "%ecx $0\n%ecx $-1\n%ecx $18\n%edx $0\n%edx $-1\n%edx $18\n"), std::string::npos); } @@ -334,10 +384,7 @@ TEST_F(AssemblerX86_64Test, Repeatrb) { TEST_F(AssemblerX86_64Test, RepeatrF) { EXPECT_NE(RepeatrF(/*f*/ nullptr, "%{reg1} %{reg2}") .find("%eax %xmm0\n%eax %xmm1\n%eax %xmm2\n%eax %xmm3\n" - "%eax %xmm4\n%eax %xmm5\n%eax %xmm6\n%eax %xmm7\n" - "%eax %xmm8\n%eax %xmm9\n%eax %xmm10\n%eax %xmm11\n" - "%eax %xmm12\n%eax %xmm13\n%eax %xmm14\n%eax %xmm15\n" - "%ebx %xmm0\n%ebx %xmm1\n%ebx %xmm2\n%ebx %xmm3\n%ebx %xmm4\n"), + "%eax %xmm4\n%eax %xmm5\n%eax %xmm6\n%eax %xmm7\n"), std::string::npos); } @@ -348,59 +395,103 @@ TEST_F(AssemblerX86_64Test, RepeatR) { } TEST_F(AssemblerX86_64Test, RepeatRI) { - EXPECT_EQ("%rax %0\n%rax %-1\n%rax %18\n%rbx %0\n%rbx %-1\n%rbx %18\n" - "%rcx %0\n%rcx %-1\n%rcx %18\n%rdx %0\n%rdx %-1\n%rdx %18\n" - "%rbp %0\n%rbp %-1\n%rbp %18\n%rsp %0\n%rsp %-1\n%rsp %18\n" - "%rsi %0\n%rsi %-1\n%rsi %18\n%rdi %0\n%rdi %-1\n%rdi %18\n" - "%r8 %0\n%r8 %-1\n%r8 %18\n%r9 %0\n%r9 %-1\n%r9 %18\n" - "%r10 %0\n%r10 %-1\n%r10 %18\n%r11 %0\n%r11 %-1\n%r11 %18\n" - "%r12 %0\n%r12 %-1\n%r12 %18\n%r13 %0\n%r13 %-1\n%r13 %18\n" - "%r14 %0\n%r14 %-1\n%r14 %18\n%r15 %0\n%r15 %-1\n%r15 %18\n", - RepeatRI(/*f*/ nullptr, /*imm_bytes*/ 1U, "%{reg} %{imm}")); + EXPECT_NE(RepeatRI(/*f*/ nullptr, /*imm_bytes*/ 1U, "%{reg} ${imm}") + .find("%rax $0\n%rax $-1\n%rax $18\n%rbx $0\n%rbx $-1\n%rbx $18\n" + "%rcx $0\n%rcx $-1\n%rcx $18\n%rdx $0\n%rdx $-1\n%rdx $18\n"), + std::string::npos); } TEST_F(AssemblerX86_64Test, RepeatRr) { EXPECT_NE(RepeatRr(/*f*/ nullptr, "%{reg1} %{reg2}") .find("%rax %eax\n%rax %ebx\n%rax %ecx\n%rax %edx\n%rax %ebp\n" - "%rax %esp\n%rax %esi\n%rax %edi\n%rax %r8d\n%rax %r9d\n" - "%rax %r10d\n%rax %r11d\n%rax %r12d\n%rax %r13d\n%rax %r14d\n" - "%rax %r15d\n%rbx %eax\n%rbx %ebx\n%rbx %ecx\n%rbx %edx\n"), + "%rax %esp\n%rax %esi\n%rax %edi\n%rax %r8d\n%rax %r9d\n"), std::string::npos); } TEST_F(AssemblerX86_64Test, RepeatRR) { EXPECT_NE(RepeatRR(/*f*/ nullptr, "%{reg1} %{reg2}") .find("%rax %rax\n%rax %rbx\n%rax %rcx\n%rax %rdx\n%rax %rbp\n" - "%rax %rsp\n%rax %rsi\n%rax %rdi\n%rax %r8\n%rax %r9\n" - "%rax %r10\n%rax %r11\n%rax %r12\n%rax %r13\n%rax %r14\n" - "%rax %r15\n%rbx %rax\n%rbx %rbx\n%rbx %rcx\n%rbx %rdx\n"), + "%rax %rsp\n%rax %rsi\n%rax %rdi\n%rax %r8\n%rax %r9\n"), std::string::npos); } TEST_F(AssemblerX86_64Test, RepeatRF) { EXPECT_NE(RepeatRF(/*f*/ nullptr, "%{reg1} %{reg2}") .find("%rax %xmm0\n%rax %xmm1\n%rax %xmm2\n%rax %xmm3\n%rax %xmm4\n" - "%rax %xmm5\n%rax %xmm6\n%rax %xmm7\n%rax %xmm8\n%rax %xmm9\n" - "%rax %xmm10\n%rax %xmm11\n%rax %xmm12\n%rax %xmm13\n%rax %xmm14\n" - "%rax %xmm15\n%rbx %xmm0\n%rbx %xmm1\n%rbx %xmm2\n%rbx %xmm3\n"), + "%rax %xmm5\n%rax %xmm6\n%rax %xmm7\n%rax %xmm8\n%rax %xmm9\n"), std::string::npos); } TEST_F(AssemblerX86_64Test, RepeatFF) { EXPECT_NE(RepeatFF(/*f*/ nullptr, "%{reg1} %{reg2}") .find("%xmm0 %xmm0\n%xmm0 %xmm1\n%xmm0 %xmm2\n%xmm0 %xmm3\n%xmm0 %xmm4\n" - "%xmm0 %xmm5\n%xmm0 %xmm6\n%xmm0 %xmm7\n%xmm0 %xmm8\n%xmm0 %xmm9\n" - "%xmm0 %xmm10\n%xmm0 %xmm11\n%xmm0 %xmm12\n%xmm0 %xmm13\n%xmm0 %xmm14\n" - "%xmm0 %xmm15\n%xmm1 %xmm0\n%xmm1 %xmm1\n%xmm1 %xmm2\n%xmm1 %xmm3\n"), + "%xmm0 %xmm5\n%xmm0 %xmm6\n%xmm0 %xmm7\n%xmm0 %xmm8\n%xmm0 %xmm9\n"), std::string::npos); } TEST_F(AssemblerX86_64Test, RepeatFFI) { - EXPECT_NE(RepeatFFI(/*f*/ nullptr, /*imm_bytes*/ 1U, "%{reg1} %{reg2} %{imm}") - .find("%xmm0 %xmm0 %0\n%xmm0 %xmm0 %-1\n%xmm0 %xmm0 %18\n" - "%xmm0 %xmm1 %0\n%xmm0 %xmm1 %-1\n%xmm0 %xmm1 %18\n" - "%xmm0 %xmm2 %0\n%xmm0 %xmm2 %-1\n%xmm0 %xmm2 %18\n" - "%xmm0 %xmm3 %0\n%xmm0 %xmm3 %-1\n%xmm0 %xmm3 %18\n"), + EXPECT_NE(RepeatFFI(/*f*/ nullptr, /*imm_bytes*/ 1U, "%{reg1} %{reg2} ${imm}") + .find("%xmm0 %xmm0 $0\n%xmm0 %xmm0 $-1\n%xmm0 %xmm0 $18\n" + "%xmm0 %xmm1 $0\n%xmm0 %xmm1 $-1\n%xmm0 %xmm1 $18\n"), + std::string::npos); +} + +TEST_F(AssemblerX86_64Test, RepeatA) { + EXPECT_EQ("-1(%rax,%rbx,1)\n", RepeatA(/*f*/ nullptr, addresses_singleton_, "{mem}")); +} + +TEST_F(AssemblerX86_64Test, RepeatAFull) { + EXPECT_EQ("15(%rdi,%rax,1)\n16(%rdi,%rbx,2)\n17(%rdi,%rcx,4)\n18(%rdi,%rdx,8)\n" + "-1(%rax)\n(%rbx)\n1(%rsi)\n987654321(%rdi)\n15(%rsp,%rax,1)\n" + "16(%rsp,%rbx,2)\n17(%rsp,%rcx,4)\n18(%rsp,%rdx,8)\n-1(%rsp)\n" + "(%rsp)\n1(%rsp)\n987654321(%rsp)\n-1(%r8,%r15,2)\n123456789(%r15)\n", + RepeatA(/*f*/ nullptr, "{mem}")); +} + +TEST_F(AssemblerX86_64Test, RepeatAI) { + EXPECT_EQ("-1(%rax,%rbx,1) $0\n-1(%rax,%rbx,1) $-1\n-1(%rax,%rbx,1) $18\n", + RepeatAI(/*f*/ nullptr, /*imm_bytes*/ 1U, addresses_singleton_, "{mem} ${imm}")); +} + +TEST_F(AssemblerX86_64Test, RepeatRA) { + EXPECT_NE(RepeatRA(/*f*/ nullptr, addresses_singleton_, "%{reg} {mem}") + .find("%rax -1(%rax,%rbx,1)\n%rbx -1(%rax,%rbx,1)\n%rcx -1(%rax,%rbx,1)\n" + "%rdx -1(%rax,%rbx,1)\n%rbp -1(%rax,%rbx,1)\n%rsp -1(%rax,%rbx,1)\n"), + std::string::npos); +} + +TEST_F(AssemblerX86_64Test, RepeatrA) { + EXPECT_NE(RepeatrA(/*f*/ nullptr, addresses_singleton_, "%{reg} {mem}") + .find("%eax -1(%rax,%rbx,1)\n%ebx -1(%rax,%rbx,1)\n%ecx -1(%rax,%rbx,1)\n" + "%edx -1(%rax,%rbx,1)\n%ebp -1(%rax,%rbx,1)\n%esp -1(%rax,%rbx,1)\n"), + std::string::npos); +} + +TEST_F(AssemblerX86_64Test, RepeatAR) { + EXPECT_NE(RepeatAR(/*f*/ nullptr, addresses_singleton_, "{mem} %{reg}") + .find("-1(%rax,%rbx,1) %rax\n-1(%rax,%rbx,1) %rbx\n-1(%rax,%rbx,1) %rcx\n" + "-1(%rax,%rbx,1) %rdx\n-1(%rax,%rbx,1) %rbp\n-1(%rax,%rbx,1) %rsp\n"), + std::string::npos); +} + +TEST_F(AssemblerX86_64Test, RepeatAr) { + EXPECT_NE(RepeatAr(/*f*/ nullptr, addresses_singleton_, "{mem} %{reg}") + .find("-1(%rax,%rbx,1) %eax\n-1(%rax,%rbx,1) %ebx\n-1(%rax,%rbx,1) %ecx\n" + "-1(%rax,%rbx,1) %edx\n-1(%rax,%rbx,1) %ebp\n-1(%rax,%rbx,1) %esp\n"), + std::string::npos); +} + +TEST_F(AssemblerX86_64Test, RepeatFA) { + EXPECT_NE(RepeatFA(/*f*/ nullptr, addresses_singleton_, "%{reg} {mem}"). + find("%xmm0 -1(%rax,%rbx,1)\n%xmm1 -1(%rax,%rbx,1)\n%xmm2 -1(%rax,%rbx,1)\n" + "%xmm3 -1(%rax,%rbx,1)\n%xmm4 -1(%rax,%rbx,1)\n%xmm5 -1(%rax,%rbx,1)\n"), + std::string::npos); +} + +TEST_F(AssemblerX86_64Test, RepeatAF) { + EXPECT_NE(RepeatAF(/*f*/ nullptr, addresses_singleton_, "{mem} %{reg}") + .find("-1(%rax,%rbx,1) %xmm0\n-1(%rax,%rbx,1) %xmm1\n-1(%rax,%rbx,1) %xmm2\n" + "-1(%rax,%rbx,1) %xmm3\n-1(%rax,%rbx,1) %xmm4\n-1(%rax,%rbx,1) %xmm5\n"), std::string::npos); } @@ -412,12 +503,43 @@ TEST_F(AssemblerX86_64Test, Toolchain) { EXPECT_TRUE(CheckTools()); } +TEST_F(AssemblerX86_64Test, PopqAllAddresses) { + // Make sure all addressing modes combinations are tested at least once. + std::vector<x86_64::Address> all_addresses; + for (x86_64::CpuRegister* base : GetRegisters()) { + // Base only. + all_addresses.push_back(x86_64::Address(*base, -1)); + all_addresses.push_back(x86_64::Address(*base, 0)); + all_addresses.push_back(x86_64::Address(*base, 1)); + all_addresses.push_back(x86_64::Address(*base, 123456789)); + for (x86_64::CpuRegister* index : GetRegisters()) { + if (index->AsRegister() == x86_64::RSP) { + // Index cannot be RSP. + continue; + } else if (base->AsRegister() == index->AsRegister()) { + // Index only. + all_addresses.push_back(x86_64::Address(*index, x86_64::TIMES_1, -1)); + all_addresses.push_back(x86_64::Address(*index, x86_64::TIMES_2, 0)); + all_addresses.push_back(x86_64::Address(*index, x86_64::TIMES_4, 1)); + all_addresses.push_back(x86_64::Address(*index, x86_64::TIMES_8, 123456789)); + } + // Base and index. + all_addresses.push_back(x86_64::Address(*base, *index, x86_64::TIMES_1, -1)); + all_addresses.push_back(x86_64::Address(*base, *index, x86_64::TIMES_2, 0)); + all_addresses.push_back(x86_64::Address(*base, *index, x86_64::TIMES_4, 1)); + all_addresses.push_back(x86_64::Address(*base, *index, x86_64::TIMES_8, 123456789)); + } + } + DriverStr(RepeatA(&x86_64::X86_64Assembler::popq, all_addresses, "popq {mem}"), "popq"); +} + TEST_F(AssemblerX86_64Test, PushqRegs) { DriverStr(RepeatR(&x86_64::X86_64Assembler::pushq, "pushq %{reg}"), "pushq"); } TEST_F(AssemblerX86_64Test, PushqImm) { - DriverStr(RepeatI(&x86_64::X86_64Assembler::pushq, 4U, "pushq ${imm}"), "pushqi"); + DriverStr(RepeatI(&x86_64::X86_64Assembler::pushq, /*imm_bytes*/ 4U, + "pushq ${imm}"), "pushqi"); } TEST_F(AssemblerX86_64Test, MovqRegs) { @@ -425,7 +547,8 @@ TEST_F(AssemblerX86_64Test, MovqRegs) { } TEST_F(AssemblerX86_64Test, MovqImm) { - DriverStr(RepeatRI(&x86_64::X86_64Assembler::movq, 8U, "movq ${imm}, %{reg}"), "movqi"); + DriverStr(RepeatRI(&x86_64::X86_64Assembler::movq, /*imm_bytes*/ 8U, + "movq ${imm}, %{reg}"), "movqi"); } TEST_F(AssemblerX86_64Test, MovlRegs) { @@ -433,7 +556,8 @@ TEST_F(AssemblerX86_64Test, MovlRegs) { } TEST_F(AssemblerX86_64Test, MovlImm) { - DriverStr(Repeatri(&x86_64::X86_64Assembler::movl, 4U, "mov ${imm}, %{reg}"), "movli"); + DriverStr(RepeatrI(&x86_64::X86_64Assembler::movl, /*imm_bytes*/ 4U, + "mov ${imm}, %{reg}"), "movli"); } TEST_F(AssemblerX86_64Test, AddqRegs) { @@ -441,7 +565,8 @@ TEST_F(AssemblerX86_64Test, AddqRegs) { } TEST_F(AssemblerX86_64Test, AddqImm) { - DriverStr(RepeatRI(&x86_64::X86_64Assembler::addq, 4U, "addq ${imm}, %{reg}"), "addqi"); + DriverStr(RepeatRI(&x86_64::X86_64Assembler::addq, /*imm_bytes*/ 4U, + "addq ${imm}, %{reg}"), "addqi"); } TEST_F(AssemblerX86_64Test, AddlRegs) { @@ -449,7 +574,8 @@ TEST_F(AssemblerX86_64Test, AddlRegs) { } TEST_F(AssemblerX86_64Test, AddlImm) { - DriverStr(Repeatri(&x86_64::X86_64Assembler::addl, 4U, "add ${imm}, %{reg}"), "addli"); + DriverStr(RepeatrI(&x86_64::X86_64Assembler::addl, /*imm_bytes*/ 4U, + "add ${imm}, %{reg}"), "addli"); } TEST_F(AssemblerX86_64Test, ImulqReg1) { @@ -461,7 +587,8 @@ TEST_F(AssemblerX86_64Test, ImulqRegs) { } TEST_F(AssemblerX86_64Test, ImulqImm) { - DriverStr(RepeatRI(&x86_64::X86_64Assembler::imulq, 4U, "imulq ${imm}, %{reg}, %{reg}"), + DriverStr(RepeatRI(&x86_64::X86_64Assembler::imulq, /*imm_bytes*/ 4U, + "imulq ${imm}, %{reg}, %{reg}"), "imulqi"); } @@ -470,7 +597,8 @@ TEST_F(AssemblerX86_64Test, ImullRegs) { } TEST_F(AssemblerX86_64Test, ImullImm) { - DriverStr(Repeatri(&x86_64::X86_64Assembler::imull, 4U, "imull ${imm}, %{reg}, %{reg}"), + DriverStr(RepeatrI(&x86_64::X86_64Assembler::imull, /*imm_bytes*/ 4U, + "imull ${imm}, %{reg}, %{reg}"), "imulli"); } @@ -483,7 +611,8 @@ TEST_F(AssemblerX86_64Test, SubqRegs) { } TEST_F(AssemblerX86_64Test, SubqImm) { - DriverStr(RepeatRI(&x86_64::X86_64Assembler::subq, 4U, "subq ${imm}, %{reg}"), "subqi"); + DriverStr(RepeatRI(&x86_64::X86_64Assembler::subq, /*imm_bytes*/ 4U, + "subq ${imm}, %{reg}"), "subqi"); } TEST_F(AssemblerX86_64Test, SublRegs) { @@ -491,21 +620,19 @@ TEST_F(AssemblerX86_64Test, SublRegs) { } TEST_F(AssemblerX86_64Test, SublImm) { - DriverStr(Repeatri(&x86_64::X86_64Assembler::subl, 4U, "sub ${imm}, %{reg}"), "subli"); + DriverStr(RepeatrI(&x86_64::X86_64Assembler::subl, /*imm_bytes*/ 4U, + "sub ${imm}, %{reg}"), "subli"); } // Shll only allows CL as the shift count. std::string shll_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { std::ostringstream str; - std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters(); - x86_64::CpuRegister shifter(x86_64::RCX); for (auto reg : registers) { assembler->shll(*reg, shifter); str << "shll %cl, %" << assembler_test->GetSecondaryRegisterName(*reg) << "\n"; } - return str.str(); } @@ -514,21 +641,19 @@ TEST_F(AssemblerX86_64Test, ShllReg) { } TEST_F(AssemblerX86_64Test, ShllImm) { - DriverStr(Repeatri(&x86_64::X86_64Assembler::shll, 1U, "shll ${imm}, %{reg}"), "shlli"); + DriverStr(RepeatrI(&x86_64::X86_64Assembler::shll, /*imm_bytes*/ 1U, + "shll ${imm}, %{reg}"), "shlli"); } // Shlq only allows CL as the shift count. std::string shlq_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { std::ostringstream str; - std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters(); - x86_64::CpuRegister shifter(x86_64::RCX); for (auto reg : registers) { assembler->shlq(*reg, shifter); str << "shlq %cl, %" << assembler_test->GetRegisterName(*reg) << "\n"; } - return str.str(); } @@ -537,21 +662,19 @@ TEST_F(AssemblerX86_64Test, ShlqReg) { } TEST_F(AssemblerX86_64Test, ShlqImm) { - DriverStr(RepeatRI(&x86_64::X86_64Assembler::shlq, 1U, "shlq ${imm}, %{reg}"), "shlqi"); + DriverStr(RepeatRI(&x86_64::X86_64Assembler::shlq, /*imm_bytes*/ 1U, + "shlq ${imm}, %{reg}"), "shlqi"); } // Shrl only allows CL as the shift count. std::string shrl_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { std::ostringstream str; - std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters(); - x86_64::CpuRegister shifter(x86_64::RCX); for (auto reg : registers) { assembler->shrl(*reg, shifter); str << "shrl %cl, %" << assembler_test->GetSecondaryRegisterName(*reg) << "\n"; } - return str.str(); } @@ -560,21 +683,18 @@ TEST_F(AssemblerX86_64Test, ShrlReg) { } TEST_F(AssemblerX86_64Test, ShrlImm) { - DriverStr(Repeatri(&x86_64::X86_64Assembler::shrl, 1U, "shrl ${imm}, %{reg}"), "shrli"); + DriverStr(RepeatrI(&x86_64::X86_64Assembler::shrl, /*imm_bytes*/ 1U, "shrl ${imm}, %{reg}"), "shrli"); } // Shrq only allows CL as the shift count. std::string shrq_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { std::ostringstream str; - std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters(); - x86_64::CpuRegister shifter(x86_64::RCX); for (auto reg : registers) { assembler->shrq(*reg, shifter); str << "shrq %cl, %" << assembler_test->GetRegisterName(*reg) << "\n"; } - return str.str(); } @@ -583,21 +703,18 @@ TEST_F(AssemblerX86_64Test, ShrqReg) { } TEST_F(AssemblerX86_64Test, ShrqImm) { - DriverStr(RepeatRI(&x86_64::X86_64Assembler::shrq, 1U, "shrq ${imm}, %{reg}"), "shrqi"); + DriverStr(RepeatRI(&x86_64::X86_64Assembler::shrq, /*imm_bytes*/ 1U, "shrq ${imm}, %{reg}"), "shrqi"); } // Sarl only allows CL as the shift count. std::string sarl_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { std::ostringstream str; - std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters(); - x86_64::CpuRegister shifter(x86_64::RCX); for (auto reg : registers) { assembler->sarl(*reg, shifter); str << "sarl %cl, %" << assembler_test->GetSecondaryRegisterName(*reg) << "\n"; } - return str.str(); } @@ -606,21 +723,18 @@ TEST_F(AssemblerX86_64Test, SarlReg) { } TEST_F(AssemblerX86_64Test, SarlImm) { - DriverStr(Repeatri(&x86_64::X86_64Assembler::sarl, 1U, "sarl ${imm}, %{reg}"), "sarli"); + DriverStr(RepeatrI(&x86_64::X86_64Assembler::sarl, /*imm_bytes*/ 1U, "sarl ${imm}, %{reg}"), "sarli"); } // Sarq only allows CL as the shift count. std::string sarq_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { std::ostringstream str; - std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters(); - x86_64::CpuRegister shifter(x86_64::RCX); for (auto reg : registers) { assembler->sarq(*reg, shifter); str << "sarq %cl, %" << assembler_test->GetRegisterName(*reg) << "\n"; } - return str.str(); } @@ -629,21 +743,18 @@ TEST_F(AssemblerX86_64Test, SarqReg) { } TEST_F(AssemblerX86_64Test, SarqImm) { - DriverStr(RepeatRI(&x86_64::X86_64Assembler::sarq, 1U, "sarq ${imm}, %{reg}"), "sarqi"); + DriverStr(RepeatRI(&x86_64::X86_64Assembler::sarq, /*imm_bytes*/ 1U, "sarq ${imm}, %{reg}"), "sarqi"); } // Rorl only allows CL as the shift count. std::string rorl_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { std::ostringstream str; - std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters(); - x86_64::CpuRegister shifter(x86_64::RCX); for (auto reg : registers) { assembler->rorl(*reg, shifter); str << "rorl %cl, %" << assembler_test->GetSecondaryRegisterName(*reg) << "\n"; } - return str.str(); } @@ -652,21 +763,18 @@ TEST_F(AssemblerX86_64Test, RorlReg) { } TEST_F(AssemblerX86_64Test, RorlImm) { - DriverStr(Repeatri(&x86_64::X86_64Assembler::rorl, 1U, "rorl ${imm}, %{reg}"), "rorli"); + DriverStr(RepeatrI(&x86_64::X86_64Assembler::rorl, /*imm_bytes*/ 1U, "rorl ${imm}, %{reg}"), "rorli"); } // Roll only allows CL as the shift count. std::string roll_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { std::ostringstream str; - std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters(); - x86_64::CpuRegister shifter(x86_64::RCX); for (auto reg : registers) { assembler->roll(*reg, shifter); str << "roll %cl, %" << assembler_test->GetSecondaryRegisterName(*reg) << "\n"; } - return str.str(); } @@ -675,21 +783,18 @@ TEST_F(AssemblerX86_64Test, RollReg) { } TEST_F(AssemblerX86_64Test, RollImm) { - DriverStr(Repeatri(&x86_64::X86_64Assembler::roll, 1U, "roll ${imm}, %{reg}"), "rolli"); + DriverStr(RepeatrI(&x86_64::X86_64Assembler::roll, /*imm_bytes*/ 1U, "roll ${imm}, %{reg}"), "rolli"); } // Rorq only allows CL as the shift count. std::string rorq_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { std::ostringstream str; - std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters(); - x86_64::CpuRegister shifter(x86_64::RCX); for (auto reg : registers) { assembler->rorq(*reg, shifter); str << "rorq %cl, %" << assembler_test->GetRegisterName(*reg) << "\n"; } - return str.str(); } @@ -698,21 +803,18 @@ TEST_F(AssemblerX86_64Test, RorqReg) { } TEST_F(AssemblerX86_64Test, RorqImm) { - DriverStr(RepeatRI(&x86_64::X86_64Assembler::rorq, 1U, "rorq ${imm}, %{reg}"), "rorqi"); + DriverStr(RepeatRI(&x86_64::X86_64Assembler::rorq, /*imm_bytes*/ 1U, "rorq ${imm}, %{reg}"), "rorqi"); } // Rolq only allows CL as the shift count. std::string rolq_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { std::ostringstream str; - std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters(); - x86_64::CpuRegister shifter(x86_64::RCX); for (auto reg : registers) { assembler->rolq(*reg, shifter); str << "rolq %cl, %" << assembler_test->GetRegisterName(*reg) << "\n"; } - return str.str(); } @@ -721,7 +823,7 @@ TEST_F(AssemblerX86_64Test, RolqReg) { } TEST_F(AssemblerX86_64Test, RolqImm) { - DriverStr(RepeatRI(&x86_64::X86_64Assembler::rolq, 1U, "rolq ${imm}, %{reg}"), "rolqi"); + DriverStr(RepeatRI(&x86_64::X86_64Assembler::rolq, /*imm_bytes*/ 1U, "rolq ${imm}, %{reg}"), "rolqi"); } TEST_F(AssemblerX86_64Test, CmpqRegs) { @@ -729,8 +831,9 @@ TEST_F(AssemblerX86_64Test, CmpqRegs) { } TEST_F(AssemblerX86_64Test, CmpqImm) { - DriverStr(RepeatRI(&x86_64::X86_64Assembler::cmpq, 4U /* cmpq only supports 32b imm */, - "cmpq ${imm}, %{reg}"), "cmpqi"); + DriverStr(RepeatRI(&x86_64::X86_64Assembler::cmpq, + /*imm_bytes*/ 4U, + "cmpq ${imm}, %{reg}"), "cmpqi"); // only imm32 } TEST_F(AssemblerX86_64Test, CmplRegs) { @@ -738,7 +841,7 @@ TEST_F(AssemblerX86_64Test, CmplRegs) { } TEST_F(AssemblerX86_64Test, CmplImm) { - DriverStr(Repeatri(&x86_64::X86_64Assembler::cmpl, 4U, "cmpl ${imm}, %{reg}"), "cmpli"); + DriverStr(RepeatrI(&x86_64::X86_64Assembler::cmpl, /*imm_bytes*/ 4U, "cmpl ${imm}, %{reg}"), "cmpli"); } TEST_F(AssemblerX86_64Test, Testl) { @@ -768,8 +871,9 @@ TEST_F(AssemblerX86_64Test, AndqRegs) { } TEST_F(AssemblerX86_64Test, AndqImm) { - DriverStr(RepeatRI(&x86_64::X86_64Assembler::andq, 4U /* andq only supports 32b imm */, - "andq ${imm}, %{reg}"), "andqi"); + DriverStr(RepeatRI(&x86_64::X86_64Assembler::andq, + /*imm_bytes*/ 4U, + "andq ${imm}, %{reg}"), "andqi"); // only imm32 } TEST_F(AssemblerX86_64Test, AndlRegs) { @@ -777,7 +881,9 @@ TEST_F(AssemblerX86_64Test, AndlRegs) { } TEST_F(AssemblerX86_64Test, AndlImm) { - DriverStr(Repeatri(&x86_64::X86_64Assembler::andl, 4U, "andl ${imm}, %{reg}"), "andli"); + DriverStr(RepeatrI(&x86_64::X86_64Assembler::andl, + /*imm_bytes*/ 4U, + "andl ${imm}, %{reg}"), "andli"); } TEST_F(AssemblerX86_64Test, OrqRegs) { @@ -789,7 +895,8 @@ TEST_F(AssemblerX86_64Test, OrlRegs) { } TEST_F(AssemblerX86_64Test, OrlImm) { - DriverStr(Repeatri(&x86_64::X86_64Assembler::orl, 4U, "orl ${imm}, %{reg}"), "orli"); + DriverStr(RepeatrI(&x86_64::X86_64Assembler::orl, + /*imm_bytes*/ 4U, "orl ${imm}, %{reg}"), "orli"); } TEST_F(AssemblerX86_64Test, XorqRegs) { @@ -797,7 +904,8 @@ TEST_F(AssemblerX86_64Test, XorqRegs) { } TEST_F(AssemblerX86_64Test, XorqImm) { - DriverStr(RepeatRI(&x86_64::X86_64Assembler::xorq, 4U, "xorq ${imm}, %{reg}"), "xorqi"); + DriverStr(RepeatRI(&x86_64::X86_64Assembler::xorq, + /*imm_bytes*/ 4U, "xorq ${imm}, %{reg}"), "xorqi"); } TEST_F(AssemblerX86_64Test, XorlRegs) { @@ -805,7 +913,8 @@ TEST_F(AssemblerX86_64Test, XorlRegs) { } TEST_F(AssemblerX86_64Test, XorlImm) { - DriverStr(Repeatri(&x86_64::X86_64Assembler::xorl, 4U, "xor ${imm}, %{reg}"), "xorli"); + DriverStr(RepeatrI(&x86_64::X86_64Assembler::xorl, + /*imm_bytes*/ 4U, "xor ${imm}, %{reg}"), "xorli"); } TEST_F(AssemblerX86_64Test, Xchgq) { @@ -813,167 +922,87 @@ TEST_F(AssemblerX86_64Test, Xchgq) { } TEST_F(AssemblerX86_64Test, Xchgl) { - // Test is disabled because GCC generates 0x87 0xC0 for xchgl eax, eax. All other cases are the - // same. Anyone know why it doesn't emit a simple 0x90? It does so for xchgq rax, rax... + // TODO: Test is disabled because GCC generates 0x87 0xC0 for xchgl eax, eax. All other cases + // are the same. Anyone know why it doesn't emit a simple 0x90? It does so for xchgq rax, rax... // DriverStr(Repeatrr(&x86_64::X86_64Assembler::xchgl, "xchgl %{reg2}, %{reg1}"), "xchgl"); } TEST_F(AssemblerX86_64Test, LockCmpxchgl) { - GetAssembler()->LockCmpxchgl(x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), - x86_64::CpuRegister(x86_64::RSI)); - GetAssembler()->LockCmpxchgl(x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), - x86_64::CpuRegister(x86_64::RSI)); - GetAssembler()->LockCmpxchgl(x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), - x86_64::CpuRegister(x86_64::R8)); - GetAssembler()->LockCmpxchgl(x86_64::Address( - x86_64::CpuRegister(x86_64::R13), 0), x86_64::CpuRegister(x86_64::RSI)); - GetAssembler()->LockCmpxchgl(x86_64::Address( - x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0), - x86_64::CpuRegister(x86_64::RSI)); - const char* expected = - "lock cmpxchgl %ESI, 0xc(%RDI,%RBX,4)\n" - "lock cmpxchgl %ESI, 0xc(%RDI,%R9,4)\n" - "lock cmpxchgl %R8d, 0xc(%RDI,%R9,4)\n" - "lock cmpxchgl %ESI, (%R13)\n" - "lock cmpxchgl %ESI, (%R13,%R9,1)\n"; - - DriverStr(expected, "lock_cmpxchgl"); + DriverStr(RepeatAr(&x86_64::X86_64Assembler::LockCmpxchgl, + "lock cmpxchgl %{reg}, {mem}"), "lock_cmpxchgl"); } TEST_F(AssemblerX86_64Test, LockCmpxchgq) { - GetAssembler()->LockCmpxchgq(x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), - x86_64::CpuRegister(x86_64::RSI)); - GetAssembler()->LockCmpxchgq(x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), - x86_64::CpuRegister(x86_64::RSI)); - GetAssembler()->LockCmpxchgq(x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), - x86_64::CpuRegister(x86_64::R8)); - GetAssembler()->LockCmpxchgq(x86_64::Address( - x86_64::CpuRegister(x86_64::R13), 0), x86_64::CpuRegister(x86_64::RSI)); - GetAssembler()->LockCmpxchgq(x86_64::Address( - x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0), - x86_64::CpuRegister(x86_64::RSI)); - const char* expected = - "lock cmpxchg %RSI, 0xc(%RDI,%RBX,4)\n" - "lock cmpxchg %RSI, 0xc(%RDI,%R9,4)\n" - "lock cmpxchg %R8, 0xc(%RDI,%R9,4)\n" - "lock cmpxchg %RSI, (%R13)\n" - "lock cmpxchg %RSI, (%R13,%R9,1)\n"; - - DriverStr(expected, "lock_cmpxchg"); -} - -TEST_F(AssemblerX86_64Test, Movl) { - GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); - GetAssembler()->movl(x86_64::CpuRegister(x86_64::R8), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); - GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::Address( - x86_64::CpuRegister(x86_64::R13), 0)); - GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::Address( - x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0)); - const char* expected = - "movl 0xc(%RDI,%RBX,4), %EAX\n" - "movl 0xc(%RDI,%R9,4), %EAX\n" - "movl 0xc(%RDI,%R9,4), %R8d\n" - "movl (%R13), %EAX\n" - "movl (%R13,%R9,1), %EAX\n"; - - DriverStr(expected, "movl"); -} - -TEST_F(AssemblerX86_64Test, Movw) { - GetAssembler()->movw(x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0), - x86_64::CpuRegister(x86_64::R9)); - GetAssembler()->movw(x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0), - x86_64::Immediate(0)); - GetAssembler()->movw(x86_64::Address(x86_64::CpuRegister(x86_64::R9), 0), - x86_64::Immediate(0)); - GetAssembler()->movw(x86_64::Address(x86_64::CpuRegister(x86_64::R14), 0), - x86_64::Immediate(0)); - const char* expected = - "movw %R9w, 0(%RAX)\n" - "movw $0, 0(%RAX)\n" - "movw $0, 0(%R9)\n" - "movw $0, 0(%R14)\n"; - DriverStr(expected, "movw"); + DriverStr(RepeatAR(&x86_64::X86_64Assembler::LockCmpxchgq, + "lock cmpxchg %{reg}, {mem}"), "lock_cmpxchg"); +} + +TEST_F(AssemblerX86_64Test, MovqStore) { + DriverStr(RepeatAR(&x86_64::X86_64Assembler::movq, "movq %{reg}, {mem}"), "movq_s"); +} + +TEST_F(AssemblerX86_64Test, MovqLoad) { + DriverStr(RepeatRA(&x86_64::X86_64Assembler::movq, "movq {mem}, %{reg}"), "movq_l"); +} + +TEST_F(AssemblerX86_64Test, MovlStore) { + DriverStr(RepeatAr(&x86_64::X86_64Assembler::movl, "movl %{reg}, {mem}"), "movl_s"); +} + +TEST_F(AssemblerX86_64Test, MovlLoad) { + DriverStr(RepeatrA(&x86_64::X86_64Assembler::movl, "movl {mem}, %{reg}"), "movl_l"); +} + +TEST_F(AssemblerX86_64Test, MovwStore) { + DriverStr(RepeatAw(&x86_64::X86_64Assembler::movw, "movw %{reg}, {mem}"), "movw_s"); +} + +TEST_F(AssemblerX86_64Test, MovbStore) { + DriverStr(RepeatAb(&x86_64::X86_64Assembler::movb, "movb %{reg}, {mem}"), "movb_s"); } TEST_F(AssemblerX86_64Test, Cmpw) { - GetAssembler()->cmpw(x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0), - x86_64::Immediate(0)); - GetAssembler()->cmpw(x86_64::Address(x86_64::CpuRegister(x86_64::R9), 0), - x86_64::Immediate(0)); - GetAssembler()->cmpw(x86_64::Address(x86_64::CpuRegister(x86_64::R14), 0), - x86_64::Immediate(0)); - const char* expected = - "cmpw $0, 0(%RAX)\n" - "cmpw $0, 0(%R9)\n" - "cmpw $0, 0(%R14)\n"; - DriverStr(expected, "cmpw"); + DriverStr(RepeatAI(&x86_64::X86_64Assembler::cmpw, + /*imm_bytes*/ 1U, + "cmpw ${imm}, {mem}"), "cmpw"); // TODO: only imm8? } TEST_F(AssemblerX86_64Test, MovqAddrImm) { - GetAssembler()->movq(x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0), - x86_64::Immediate(-5)); - const char* expected = "movq $-5, 0(%RAX)\n"; - DriverStr(expected, "movq"); + DriverStr(RepeatAI(&x86_64::X86_64Assembler::movq, + /*imm_bytes*/ 4U, + "movq ${imm}, {mem}"), "movq"); // only imm32 } -TEST_F(AssemblerX86_64Test, Movntl) { - GetAssembler()->movntl(x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), x86_64::CpuRegister(x86_64::RAX)); - GetAssembler()->movntl(x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), x86_64::CpuRegister(x86_64::RAX)); - GetAssembler()->movntl(x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), x86_64::CpuRegister(x86_64::RAX)); - GetAssembler()->movntl(x86_64::Address(x86_64::CpuRegister(x86_64::R13), 0), x86_64::CpuRegister(x86_64::RAX)); - GetAssembler()->movntl(x86_64::Address( - x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0), x86_64::CpuRegister(x86_64::R9)); - const char* expected = - "movntil %EAX, 0xc(%RDI,%RBX,4)\n" - "movntil %EAX, 0xc(%RDI,%R9,4)\n" - "movntil %EAX, 0xc(%RDI,%R9,4)\n" - "movntil %EAX, (%R13)\n" - "movntil %R9d, (%R13,%R9,1)\n"; +TEST_F(AssemblerX86_64Test, MovlAddrImm) { + DriverStr(RepeatAI(&x86_64::X86_64Assembler::movl, + /*imm_bytes*/ 4U, "movl ${imm}, {mem}"), "movl"); +} - DriverStr(expected, "movntl"); +TEST_F(AssemblerX86_64Test, MovwAddrImm) { + DriverStr(RepeatAI(&x86_64::X86_64Assembler::movw, + /*imm_bytes*/ 2U, "movw ${imm}, {mem}"), "movw"); } -TEST_F(AssemblerX86_64Test, Movntq) { - GetAssembler()->movntq(x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), x86_64::CpuRegister(x86_64::RAX)); - GetAssembler()->movntq(x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), x86_64::CpuRegister(x86_64::RAX)); - GetAssembler()->movntq(x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), x86_64::CpuRegister(x86_64::RAX)); - GetAssembler()->movntq(x86_64::Address(x86_64::CpuRegister(x86_64::R13), 0), x86_64::CpuRegister(x86_64::RAX)); - GetAssembler()->movntq(x86_64::Address( - x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0), x86_64::CpuRegister(x86_64::R9)); - const char* expected = - "movntiq %RAX, 0xc(%RDI,%RBX,4)\n" - "movntiq %RAX, 0xc(%RDI,%R9,4)\n" - "movntiq %RAX, 0xc(%RDI,%R9,4)\n" - "movntiq %RAX, (%R13)\n" - "movntiq %R9, (%R13,%R9,1)\n"; +TEST_F(AssemblerX86_64Test, MovbAddrImm) { + DriverStr(RepeatAI(&x86_64::X86_64Assembler::movb, + /*imm_bytes*/ 1U, "movb ${imm}, {mem}"), "movb"); +} - DriverStr(expected, "movntq"); +TEST_F(AssemblerX86_64Test, Movntl) { + DriverStr(RepeatAr(&x86_64::X86_64Assembler::movntl, "movntil %{reg}, {mem}"), "movntl"); +} + +TEST_F(AssemblerX86_64Test, Movntq) { + DriverStr(RepeatAR(&x86_64::X86_64Assembler::movntq, "movntiq %{reg}, {mem}"), "movntq"); } TEST_F(AssemblerX86_64Test, Cvtsi2ssAddr) { GetAssembler()->cvtsi2ss(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0), - false); + /*is64bit*/ false); GetAssembler()->cvtsi2ss(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0), - true); + /*is64bit*/ true); const char* expected = "cvtsi2ss 0(%RAX), %xmm0\n" "cvtsi2ssq 0(%RAX), %xmm0\n"; DriverStr(expected, "cvtsi2ss"); @@ -982,111 +1011,69 @@ TEST_F(AssemblerX86_64Test, Cvtsi2ssAddr) { TEST_F(AssemblerX86_64Test, Cvtsi2sdAddr) { GetAssembler()->cvtsi2sd(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0), - false); + /*is64bit*/ false); GetAssembler()->cvtsi2sd(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0), - true); + /*is64bit*/ true); const char* expected = "cvtsi2sd 0(%RAX), %xmm0\n" "cvtsi2sdq 0(%RAX), %xmm0\n"; DriverStr(expected, "cvtsi2sd"); } TEST_F(AssemblerX86_64Test, CmpqAddr) { - GetAssembler()->cmpq(x86_64::CpuRegister(x86_64::R12), - x86_64::Address(x86_64::CpuRegister(x86_64::R9), 0)); - const char* expected = "cmpq 0(%R9), %R12\n"; - DriverStr(expected, "cmpq"); + DriverStr(RepeatRA(&x86_64::X86_64Assembler::cmpq, "cmpq {mem}, %{reg}"), "cmpq"); } TEST_F(AssemblerX86_64Test, MovsxdAddr) { - GetAssembler()->movsxd(x86_64::CpuRegister(x86_64::R12), - x86_64::Address(x86_64::CpuRegister(x86_64::R9), 0)); - const char* expected = "movslq 0(%R9), %R12\n"; - DriverStr(expected, "movsxd"); + DriverStr(RepeatRA(&x86_64::X86_64Assembler::movsxd, "movslq {mem}, %{reg}"), "movsxd"); } TEST_F(AssemblerX86_64Test, TestqAddr) { - GetAssembler()->testq(x86_64::CpuRegister(x86_64::R12), - x86_64::Address(x86_64::CpuRegister(x86_64::R9), 0)); - const char* expected = "testq 0(%R9), %R12\n"; - DriverStr(expected, "testq"); + DriverStr(RepeatRA(&x86_64::X86_64Assembler::testq, "testq {mem}, %{reg}"), "testq"); } TEST_F(AssemblerX86_64Test, AddqAddr) { - GetAssembler()->addq(x86_64::CpuRegister(x86_64::R12), - x86_64::Address(x86_64::CpuRegister(x86_64::R9), 0)); - const char* expected = "addq 0(%R9), %R12\n"; - DriverStr(expected, "addq"); + DriverStr(RepeatRA(&x86_64::X86_64Assembler::addq, "addq {mem}, %{reg}"), "addq"); } TEST_F(AssemblerX86_64Test, SubqAddr) { - GetAssembler()->subq(x86_64::CpuRegister(x86_64::R12), - x86_64::Address(x86_64::CpuRegister(x86_64::R9), 0)); - const char* expected = "subq 0(%R9), %R12\n"; - DriverStr(expected, "subq"); + DriverStr(RepeatRA(&x86_64::X86_64Assembler::subq, "subq {mem}, %{reg}"), "subq"); } TEST_F(AssemblerX86_64Test, Cvtss2sdAddr) { - GetAssembler()->cvtss2sd(x86_64::XmmRegister(x86_64::XMM0), - x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0)); - const char* expected = "cvtss2sd 0(%RAX), %xmm0\n"; - DriverStr(expected, "cvtss2sd"); + DriverStr(RepeatFA(&x86_64::X86_64Assembler::cvtss2sd, "cvtss2sd {mem}, %{reg}"), "cvtss2sd"); } TEST_F(AssemblerX86_64Test, Cvtsd2ssAddr) { - GetAssembler()->cvtsd2ss(x86_64::XmmRegister(x86_64::XMM0), - x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0)); - const char* expected = "cvtsd2ss 0(%RAX), %xmm0\n"; - DriverStr(expected, "cvtsd2ss"); + DriverStr(RepeatFA(&x86_64::X86_64Assembler::cvtsd2ss, "cvtsd2ss {mem}, %{reg}"), "cvtsd2ss"); } TEST_F(AssemblerX86_64Test, ComissAddr) { - GetAssembler()->comiss(x86_64::XmmRegister(x86_64::XMM14), - x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0)); - const char* expected = "comiss 0(%RAX), %xmm14\n"; - DriverStr(expected, "comiss"); + DriverStr(RepeatFA(&x86_64::X86_64Assembler::comiss, "comiss {mem}, %{reg}"), "comiss"); } TEST_F(AssemblerX86_64Test, ComisdAddr) { - GetAssembler()->comisd(x86_64::XmmRegister(x86_64::XMM0), - x86_64::Address(x86_64::CpuRegister(x86_64::R9), 0)); - const char* expected = "comisd 0(%R9), %xmm0\n"; - DriverStr(expected, "comisd"); + DriverStr(RepeatFA(&x86_64::X86_64Assembler::comisd, "comisd {mem}, %{reg}"), "comisd"); } TEST_F(AssemblerX86_64Test, UComissAddr) { - GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM0), - x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0)); - const char* expected = "ucomiss 0(%RAX), %xmm0\n"; - DriverStr(expected, "ucomiss"); + DriverStr(RepeatFA(&x86_64::X86_64Assembler::ucomiss, "ucomiss {mem}, %{reg}"), "ucomiss"); } TEST_F(AssemblerX86_64Test, UComisdAddr) { - GetAssembler()->ucomisd(x86_64::XmmRegister(x86_64::XMM0), - x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0)); - const char* expected = "ucomisd 0(%RAX), %xmm0\n"; - DriverStr(expected, "ucomisd"); + DriverStr(RepeatFA(&x86_64::X86_64Assembler::ucomisd, "ucomisd {mem}, %{reg}"), "ucomisd"); } TEST_F(AssemblerX86_64Test, Andq) { - GetAssembler()->andq(x86_64::CpuRegister(x86_64::R9), - x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0)); - const char* expected = "andq 0(%RAX), %r9\n"; - DriverStr(expected, "andq"); + DriverStr(RepeatRA(&x86_64::X86_64Assembler::andq, "andq {mem}, %{reg}"), "andq"); } TEST_F(AssemblerX86_64Test, Orq) { - GetAssembler()->orq(x86_64::CpuRegister(x86_64::R9), - x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0)); - const char* expected = "orq 0(%RAX), %r9\n"; - DriverStr(expected, "orq"); + DriverStr(RepeatRA(&x86_64::X86_64Assembler::orq, "orq {mem}, %{reg}"), "orq"); } TEST_F(AssemblerX86_64Test, Xorq) { - GetAssembler()->xorq(x86_64::CpuRegister(x86_64::R9), - x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0)); - const char* expected = "xorq 0(%RAX), %r9\n"; - DriverStr(expected, "xorq"); + DriverStr(RepeatRA(&x86_64::X86_64Assembler::xorq, "xorq {mem}, %{reg}"), "xorq"); } TEST_F(AssemblerX86_64Test, RepneScasb) { @@ -1115,22 +1102,20 @@ TEST_F(AssemblerX86_64Test, Movaps) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::movaps, "movaps %{reg2}, %{reg1}"), "movaps"); } -TEST_F(AssemblerX86_64Test, MovapsAddr) { - GetAssembler()->movaps(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); - GetAssembler()->movaps(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); - const char* expected = - "movaps 0x4(%RSP), %xmm0\n" - "movaps %xmm1, 0x2(%RSP)\n"; - DriverStr(expected, "movaps_address"); +TEST_F(AssemblerX86_64Test, MovapsStore) { + DriverStr(RepeatAF(&x86_64::X86_64Assembler::movaps, "movaps %{reg}, {mem}"), "movaps_s"); } -TEST_F(AssemblerX86_64Test, MovupsAddr) { - GetAssembler()->movups(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); - GetAssembler()->movups(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); - const char* expected = - "movups 0x4(%RSP), %xmm0\n" - "movups %xmm1, 0x2(%RSP)\n"; - DriverStr(expected, "movups_address"); +TEST_F(AssemblerX86_64Test, MovapsLoad) { + DriverStr(RepeatFA(&x86_64::X86_64Assembler::movaps, "movaps {mem}, %{reg}"), "movaps_l"); +} + +TEST_F(AssemblerX86_64Test, MovupsStore) { + DriverStr(RepeatAF(&x86_64::X86_64Assembler::movups, "movups %{reg}, {mem}"), "movups_s"); +} + +TEST_F(AssemblerX86_64Test, MovupsLoad) { + DriverStr(RepeatFA(&x86_64::X86_64Assembler::movups, "movups {mem}, %{reg}"), "movups_l"); } TEST_F(AssemblerX86_64Test, Movss) { @@ -1141,22 +1126,20 @@ TEST_F(AssemblerX86_64Test, Movapd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::movapd, "movapd %{reg2}, %{reg1}"), "movapd"); } -TEST_F(AssemblerX86_64Test, MovapdAddr) { - GetAssembler()->movapd(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); - GetAssembler()->movapd(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); - const char* expected = - "movapd 0x4(%RSP), %xmm0\n" - "movapd %xmm1, 0x2(%RSP)\n"; - DriverStr(expected, "movapd_address"); +TEST_F(AssemblerX86_64Test, MovapdStore) { + DriverStr(RepeatAF(&x86_64::X86_64Assembler::movapd, "movapd %{reg}, {mem}"), "movapd_s"); } -TEST_F(AssemblerX86_64Test, MovupdAddr) { - GetAssembler()->movupd(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); - GetAssembler()->movupd(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); - const char* expected = - "movupd 0x4(%RSP), %xmm0\n" - "movupd %xmm1, 0x2(%RSP)\n"; - DriverStr(expected, "movupd_address"); +TEST_F(AssemblerX86_64Test, MovapdLoad) { + DriverStr(RepeatFA(&x86_64::X86_64Assembler::movapd, "movapd {mem}, %{reg}"), "movapd_l"); +} + +TEST_F(AssemblerX86_64Test, MovupdStore) { + DriverStr(RepeatAF(&x86_64::X86_64Assembler::movupd, "movupd %{reg}, {mem}"), "movupd_s"); +} + +TEST_F(AssemblerX86_64Test, MovupdLoad) { + DriverStr(RepeatFA(&x86_64::X86_64Assembler::movupd, "movupd {mem}, %{reg}"), "movupd_l"); } TEST_F(AssemblerX86_64Test, Movsd) { @@ -1164,25 +1147,23 @@ TEST_F(AssemblerX86_64Test, Movsd) { } TEST_F(AssemblerX86_64Test, Movdqa) { - DriverStr(RepeatFF(&x86_64::X86_64Assembler::movdqa, "movdqa %{reg2}, %{reg1}"), "movapd"); + DriverStr(RepeatFF(&x86_64::X86_64Assembler::movdqa, "movdqa %{reg2}, %{reg1}"), "movdqa"); } -TEST_F(AssemblerX86_64Test, MovdqaAddr) { - GetAssembler()->movdqa(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); - GetAssembler()->movdqa(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); - const char* expected = - "movdqa 0x4(%RSP), %xmm0\n" - "movdqa %xmm1, 0x2(%RSP)\n"; - DriverStr(expected, "movdqa_address"); +TEST_F(AssemblerX86_64Test, MovdqaStore) { + DriverStr(RepeatAF(&x86_64::X86_64Assembler::movdqa, "movdqa %{reg}, {mem}"), "movdqa_s"); } -TEST_F(AssemblerX86_64Test, MovdquAddr) { - GetAssembler()->movdqu(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); - GetAssembler()->movdqu(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); - const char* expected = - "movdqu 0x4(%RSP), %xmm0\n" - "movdqu %xmm1, 0x2(%RSP)\n"; - DriverStr(expected, "movdqu_address"); +TEST_F(AssemblerX86_64Test, MovdqaLoad) { + DriverStr(RepeatFA(&x86_64::X86_64Assembler::movdqa, "movdqa {mem}, %{reg}"), "movdqa_l"); +} + +TEST_F(AssemblerX86_64Test, MovdquStore) { + DriverStr(RepeatAF(&x86_64::X86_64Assembler::movdqu, "movdqu %{reg}, {mem}"), "movdqu_s"); +} + +TEST_F(AssemblerX86_64Test, MovdquLoad) { + DriverStr(RepeatFA(&x86_64::X86_64Assembler::movdqu, "movdqu {mem}, %{reg}"), "movdqu_l"); } TEST_F(AssemblerX86_64Test, Movd1) { @@ -1364,11 +1345,13 @@ TEST_F(AssemblerX86_64Test, Sqrtsd) { } TEST_F(AssemblerX86_64Test, Roundss) { - DriverStr(RepeatFFI(&x86_64::X86_64Assembler::roundss, 1, "roundss ${imm}, %{reg2}, %{reg1}"), "roundss"); + DriverStr(RepeatFFI(&x86_64::X86_64Assembler::roundss, /*imm_bytes*/ 1U, + "roundss ${imm}, %{reg2}, %{reg1}"), "roundss"); } TEST_F(AssemblerX86_64Test, Roundsd) { - DriverStr(RepeatFFI(&x86_64::X86_64Assembler::roundsd, 1, "roundsd ${imm}, %{reg2}, %{reg1}"), "roundsd"); + DriverStr(RepeatFFI(&x86_64::X86_64Assembler::roundsd, /*imm_bytes*/ 1U, + "roundsd ${imm}, %{reg2}, %{reg1}"), "roundsd"); } TEST_F(AssemblerX86_64Test, Xorps) { @@ -1564,47 +1547,58 @@ TEST_F(AssemblerX86_64Test, PCmpgtq) { } TEST_F(AssemblerX86_64Test, Shufps) { - DriverStr(RepeatFFI(&x86_64::X86_64Assembler::shufps, 1, "shufps ${imm}, %{reg2}, %{reg1}"), "shufps"); + DriverStr(RepeatFFI(&x86_64::X86_64Assembler::shufps, /*imm_bytes*/ 1U, + "shufps ${imm}, %{reg2}, %{reg1}"), "shufps"); } TEST_F(AssemblerX86_64Test, Shufpd) { - DriverStr(RepeatFFI(&x86_64::X86_64Assembler::shufpd, 1, "shufpd ${imm}, %{reg2}, %{reg1}"), "shufpd"); + DriverStr(RepeatFFI(&x86_64::X86_64Assembler::shufpd, /*imm_bytes*/ 1U, + "shufpd ${imm}, %{reg2}, %{reg1}"), "shufpd"); } TEST_F(AssemblerX86_64Test, PShufd) { - DriverStr(RepeatFFI(&x86_64::X86_64Assembler::pshufd, 1, "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd"); + DriverStr(RepeatFFI(&x86_64::X86_64Assembler::pshufd, /*imm_bytes*/ 1U, + "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd"); } TEST_F(AssemblerX86_64Test, Punpcklbw) { - DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpcklbw, "punpcklbw %{reg2}, %{reg1}"), "punpcklbw"); + DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpcklbw, + "punpcklbw %{reg2}, %{reg1}"), "punpcklbw"); } TEST_F(AssemblerX86_64Test, Punpcklwd) { - DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpcklwd, "punpcklwd %{reg2}, %{reg1}"), "punpcklwd"); + DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpcklwd, + "punpcklwd %{reg2}, %{reg1}"), "punpcklwd"); } TEST_F(AssemblerX86_64Test, Punpckldq) { - DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpckldq, "punpckldq %{reg2}, %{reg1}"), "punpckldq"); + DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpckldq, + "punpckldq %{reg2}, %{reg1}"), "punpckldq"); } TEST_F(AssemblerX86_64Test, Punpcklqdq) { - DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpcklqdq, "punpcklqdq %{reg2}, %{reg1}"), "punpcklqdq"); + DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpcklqdq, + "punpcklqdq %{reg2}, %{reg1}"), "punpcklqdq"); } TEST_F(AssemblerX86_64Test, Punpckhbw) { - DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpckhbw, "punpckhbw %{reg2}, %{reg1}"), "punpckhbw"); + DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpckhbw, + "punpckhbw %{reg2}, %{reg1}"), "punpckhbw"); } TEST_F(AssemblerX86_64Test, Punpckhwd) { - DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpckhwd, "punpckhwd %{reg2}, %{reg1}"), "punpckhwd"); + DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpckhwd, + "punpckhwd %{reg2}, %{reg1}"), "punpckhwd"); } TEST_F(AssemblerX86_64Test, Punpckhdq) { - DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpckhdq, "punpckhdq %{reg2}, %{reg1}"), "punpckhdq"); + DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpckhdq, + "punpckhdq %{reg2}, %{reg1}"), "punpckhdq"); } TEST_F(AssemblerX86_64Test, Punpckhqdq) { - DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpckhqdq, "punpckhqdq %{reg2}, %{reg1}"), "punpckhqdq"); + DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpckhqdq, + "punpckhqdq %{reg2}, %{reg1}"), "punpckhqdq"); } TEST_F(AssemblerX86_64Test, Psllw) { @@ -1653,63 +1647,21 @@ TEST_F(AssemblerX86_64Test, Psrld) { GetAssembler()->psrld(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1)); GetAssembler()->psrld(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2)); DriverStr("psrld $1, %xmm0\n" - "psrld $2, %xmm15\n", "pslldi"); + "psrld $2, %xmm15\n", "psrldi"); } TEST_F(AssemblerX86_64Test, Psrlq) { GetAssembler()->psrlq(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1)); GetAssembler()->psrlq(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2)); DriverStr("psrlq $1, %xmm0\n" - "psrlq $2, %xmm15\n", "pslrqi"); + "psrlq $2, %xmm15\n", "psrlqi"); } TEST_F(AssemblerX86_64Test, Psrldq) { GetAssembler()->psrldq(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1)); GetAssembler()->psrldq(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2)); DriverStr("psrldq $1, %xmm0\n" - "psrldq $2, %xmm15\n", "pslrdqi"); -} - -TEST_F(AssemblerX86_64Test, UcomissAddress) { - GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM1), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); - GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM2), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); - GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM3), x86_64::Address( - x86_64::CpuRegister(x86_64::R13), 0)); - GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM4), x86_64::Address( - x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0)); - const char* expected = - "ucomiss 0xc(%RDI,%RBX,4), %xmm0\n" - "ucomiss 0xc(%RDI,%R9,4), %xmm1\n" - "ucomiss 0xc(%RDI,%R9,4), %xmm2\n" - "ucomiss (%R13), %xmm3\n" - "ucomiss (%R13,%R9,1), %xmm4\n"; - - DriverStr(expected, "ucomiss_address"); -} - -TEST_F(AssemblerX86_64Test, UcomisdAddress) { - GetAssembler()->ucomisd(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->ucomisd(x86_64::XmmRegister(x86_64::XMM1), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); - GetAssembler()->ucomisd(x86_64::XmmRegister(x86_64::XMM2), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); - GetAssembler()->ucomisd(x86_64::XmmRegister(x86_64::XMM3), x86_64::Address( - x86_64::CpuRegister(x86_64::R13), 0)); - GetAssembler()->ucomisd(x86_64::XmmRegister(x86_64::XMM4), x86_64::Address( - x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0)); - const char* expected = - "ucomisd 0xc(%RDI,%RBX,4), %xmm0\n" - "ucomisd 0xc(%RDI,%R9,4), %xmm1\n" - "ucomisd 0xc(%RDI,%R9,4), %xmm2\n" - "ucomisd (%R13), %xmm3\n" - "ucomisd (%R13,%R9,1), %xmm4\n"; - - DriverStr(expected, "ucomisd_address"); + "psrldq $2, %xmm15\n", "psrldqi"); } std::string x87_fn(AssemblerX86_64Test::Base* assembler_test ATTRIBUTE_UNUSED, @@ -1735,22 +1687,28 @@ TEST_F(AssemblerX86_64Test, X87) { DriverFn(&x87_fn, "x87"); } -TEST_F(AssemblerX86_64Test, FPUIntegerLoad) { - GetAssembler()->filds(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); - GetAssembler()->fildl(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 12)); - const char* expected = - "fildl 0x4(%RSP)\n" - "fildll 0xc(%RSP)\n"; - DriverStr(expected, "FPUIntegerLoad"); +TEST_F(AssemblerX86_64Test, FPUIntegerLoads) { + DriverStr(RepeatA(&x86_64::X86_64Assembler::filds, + addresses_singleton_, // no ext addressing + "fildl {mem}"), "filds"); } -TEST_F(AssemblerX86_64Test, FPUIntegerStore) { - GetAssembler()->fistps(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 16)); - GetAssembler()->fistpl(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 24)); - const char* expected = - "fistpl 0x10(%RSP)\n" - "fistpll 0x18(%RSP)\n"; - DriverStr(expected, "FPUIntegerStore"); +TEST_F(AssemblerX86_64Test, FPUIntegerLoadl) { + DriverStr(RepeatA(&x86_64::X86_64Assembler::fildl, + addresses_singleton_, // no ext addressing + "fildll {mem}"), "fildl"); +} + +TEST_F(AssemblerX86_64Test, FPUIntegerStores) { + DriverStr(RepeatA(&x86_64::X86_64Assembler::fistps, + addresses_singleton_, // no ext addressing + "fistpl {mem}"), "fistps"); +} + +TEST_F(AssemblerX86_64Test, FPUIntegerStorel) { + DriverStr(RepeatA(&x86_64::X86_64Assembler::fistpl, + addresses_singleton_, // no ext addressing + "fistpll {mem}"), "fistpl"); } TEST_F(AssemblerX86_64Test, Call) { @@ -1762,13 +1720,15 @@ TEST_F(AssemblerX86_64Test, Jmp) { } TEST_F(AssemblerX86_64Test, Enter) { - DriverStr(RepeatI(&x86_64::X86_64Assembler::enter, 2U /* 16b immediate */, "enter ${imm}, $0", - true /* Only non-negative number */), "enter"); + DriverStr(RepeatI(&x86_64::X86_64Assembler::enter, + /*imm_bytes*/ 2U, + "enter ${imm}, $0", /*non-negative*/ true), "enter"); } TEST_F(AssemblerX86_64Test, RetImm) { - DriverStr(RepeatI(&x86_64::X86_64Assembler::ret, 2U /* 16b immediate */, "ret ${imm}", - true /* Only non-negative number */), "reti"); + DriverStr(RepeatI(&x86_64::X86_64Assembler::ret, + /*imm_bytes*/ 2U, + "ret ${imm}", /*non-negative*/ true), "ret"); } std::string ret_and_leave_fn(AssemblerX86_64Test::Base* assembler_test ATTRIBUTE_UNUSED, @@ -1801,18 +1761,7 @@ TEST_F(AssemblerX86_64Test, Bsfl) { } TEST_F(AssemblerX86_64Test, BsflAddress) { - GetAssembler()->bsfl(x86_64::CpuRegister(x86_64::R10), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->bsfl(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( - x86_64::CpuRegister(x86_64::R10), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->bsfl(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); - const char* expected = - "bsfl 0xc(%RDI,%RBX,4), %R10d\n" - "bsfl 0xc(%R10,%RBX,4), %edi\n" - "bsfl 0xc(%RDI,%R9,4), %edi\n"; - - DriverStr(expected, "bsfl_address"); + DriverStr(RepeatrA(&x86_64::X86_64Assembler::bsfl, "bsfl {mem}, %{reg}"), "bsfl_address"); } TEST_F(AssemblerX86_64Test, Bsfq) { @@ -1820,18 +1769,7 @@ TEST_F(AssemblerX86_64Test, Bsfq) { } TEST_F(AssemblerX86_64Test, BsfqAddress) { - GetAssembler()->bsfq(x86_64::CpuRegister(x86_64::R10), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->bsfq(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( - x86_64::CpuRegister(x86_64::R10), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->bsfq(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); - const char* expected = - "bsfq 0xc(%RDI,%RBX,4), %R10\n" - "bsfq 0xc(%R10,%RBX,4), %RDI\n" - "bsfq 0xc(%RDI,%R9,4), %RDI\n"; - - DriverStr(expected, "bsfq_address"); + DriverStr(RepeatRA(&x86_64::X86_64Assembler::bsfq, "bsfq {mem}, %{reg}"), "bsfq_address"); } TEST_F(AssemblerX86_64Test, Bsrl) { @@ -1839,18 +1777,7 @@ TEST_F(AssemblerX86_64Test, Bsrl) { } TEST_F(AssemblerX86_64Test, BsrlAddress) { - GetAssembler()->bsrl(x86_64::CpuRegister(x86_64::R10), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->bsrl(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( - x86_64::CpuRegister(x86_64::R10), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->bsrl(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); - const char* expected = - "bsrl 0xc(%RDI,%RBX,4), %R10d\n" - "bsrl 0xc(%R10,%RBX,4), %edi\n" - "bsrl 0xc(%RDI,%R9,4), %edi\n"; - - DriverStr(expected, "bsrl_address"); + DriverStr(RepeatrA(&x86_64::X86_64Assembler::bsrl, "bsrl {mem}, %{reg}"), "bsrl_address"); } TEST_F(AssemblerX86_64Test, Bsrq) { @@ -1858,18 +1785,7 @@ TEST_F(AssemblerX86_64Test, Bsrq) { } TEST_F(AssemblerX86_64Test, BsrqAddress) { - GetAssembler()->bsrq(x86_64::CpuRegister(x86_64::R10), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->bsrq(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( - x86_64::CpuRegister(x86_64::R10), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->bsrq(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); - const char* expected = - "bsrq 0xc(%RDI,%RBX,4), %R10\n" - "bsrq 0xc(%R10,%RBX,4), %RDI\n" - "bsrq 0xc(%RDI,%R9,4), %RDI\n"; - - DriverStr(expected, "bsrq_address"); + DriverStr(RepeatRA(&x86_64::X86_64Assembler::bsrq, "bsrq {mem}, %{reg}"), "bsrq_address"); } TEST_F(AssemblerX86_64Test, Popcntl) { @@ -1877,18 +1793,7 @@ TEST_F(AssemblerX86_64Test, Popcntl) { } TEST_F(AssemblerX86_64Test, PopcntlAddress) { - GetAssembler()->popcntl(x86_64::CpuRegister(x86_64::R10), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->popcntl(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( - x86_64::CpuRegister(x86_64::R10), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->popcntl(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); - const char* expected = - "popcntl 0xc(%RDI,%RBX,4), %R10d\n" - "popcntl 0xc(%R10,%RBX,4), %edi\n" - "popcntl 0xc(%RDI,%R9,4), %edi\n"; - - DriverStr(expected, "popcntl_address"); + DriverStr(RepeatrA(&x86_64::X86_64Assembler::popcntl, "popcntl {mem}, %{reg}"), "popcntl_address"); } TEST_F(AssemblerX86_64Test, Popcntq) { @@ -1896,18 +1801,7 @@ TEST_F(AssemblerX86_64Test, Popcntq) { } TEST_F(AssemblerX86_64Test, PopcntqAddress) { - GetAssembler()->popcntq(x86_64::CpuRegister(x86_64::R10), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->popcntq(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( - x86_64::CpuRegister(x86_64::R10), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); - GetAssembler()->popcntq(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( - x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); - const char* expected = - "popcntq 0xc(%RDI,%RBX,4), %R10\n" - "popcntq 0xc(%R10,%RBX,4), %RDI\n" - "popcntq 0xc(%RDI,%R9,4), %RDI\n"; - - DriverStr(expected, "popcntq_address"); + DriverStr(RepeatRA(&x86_64::X86_64Assembler::popcntq, "popcntq {mem}, %{reg}"), "popcntq_address"); } TEST_F(AssemblerX86_64Test, CmovlAddress) { @@ -1921,7 +1815,6 @@ TEST_F(AssemblerX86_64Test, CmovlAddress) { "cmovzl 0xc(%RDI,%RBX,4), %R10d\n" "cmovnzl 0xc(%R10,%RBX,4), %edi\n" "cmovzl 0xc(%RDI,%R9,4), %edi\n"; - DriverStr(expected, "cmovl_address"); } @@ -1936,7 +1829,6 @@ TEST_F(AssemblerX86_64Test, CmovqAddress) { "cmovzq 0xc(%RDI,%RBX,4), %R10\n" "cmovnzq 0xc(%R10,%RBX,4), %rdi\n" "cmovzq 0xc(%RDI,%R9,4), %rdi\n"; - DriverStr(expected, "cmovq_address"); } @@ -2050,52 +1942,21 @@ TEST_F(AssemblerX86_64Test, Repecmpsq) { } TEST_F(AssemblerX86_64Test, Cmpb) { - GetAssembler()->cmpb(x86_64::Address(x86_64::CpuRegister(x86_64::RDI), 128), - x86_64::Immediate(0)); - const char* expected = "cmpb $0, 128(%RDI)\n"; - DriverStr(expected, "cmpb"); + DriverStr(RepeatAI(&x86_64::X86_64Assembler::cmpb, + /*imm_bytes*/ 1U, + "cmpb ${imm}, {mem}"), "cmpb"); } TEST_F(AssemblerX86_64Test, TestbAddressImmediate) { - GetAssembler()->testb( - x86_64::Address(x86_64::CpuRegister(x86_64::RDI), - x86_64::CpuRegister(x86_64::RBX), - x86_64::TIMES_4, - 12), - x86_64::Immediate(1)); - GetAssembler()->testb( - x86_64::Address(x86_64::CpuRegister(x86_64::RSP), FrameOffset(7)), - x86_64::Immediate(-128)); - GetAssembler()->testb( - x86_64::Address(x86_64::CpuRegister(x86_64::RBX), MemberOffset(130)), - x86_64::Immediate(127)); - const char* expected = - "testb $1, 0xc(%RDI,%RBX,4)\n" - "testb $-128, 0x7(%RSP)\n" - "testb $127, 0x82(%RBX)\n"; - - DriverStr(expected, "TestbAddressImmediate"); + DriverStr(RepeatAI(&x86_64::X86_64Assembler::testb, + /*imm_bytes*/ 1U, + "testb ${imm}, {mem}"), "testbi"); } TEST_F(AssemblerX86_64Test, TestlAddressImmediate) { - GetAssembler()->testl( - x86_64::Address(x86_64::CpuRegister(x86_64::RDI), - x86_64::CpuRegister(x86_64::RBX), - x86_64::TIMES_4, - 12), - x86_64::Immediate(1)); - GetAssembler()->testl( - x86_64::Address(x86_64::CpuRegister(x86_64::RSP), FrameOffset(7)), - x86_64::Immediate(-100000)); - GetAssembler()->testl( - x86_64::Address(x86_64::CpuRegister(x86_64::RBX), MemberOffset(130)), - x86_64::Immediate(77777777)); - const char* expected = - "testl $1, 0xc(%RDI,%RBX,4)\n" - "testl $-100000, 0x7(%RSP)\n" - "testl $77777777, 0x82(%RBX)\n"; - - DriverStr(expected, "TestlAddressImmediate"); + DriverStr(RepeatAI(&x86_64::X86_64Assembler::testl, + /*imm_bytes*/ 4U, + "testl ${imm}, {mem}"), "testli"); } class JNIMacroAssemblerX86_64Test : public JNIMacroAssemblerTest<x86_64::X86_64JNIMacroAssembler> { @@ -2150,15 +2011,15 @@ std::string buildframe_test_fn(JNIMacroAssemblerX86_64Test::Base* assembler_test // Construct assembly text counterpart. std::ostringstream str; - // 1) Push the spill_regs. + // (1) Push the spill_regs. str << "pushq %rsi\n"; str << "pushq %r10\n"; - // 2) Move down the stack pointer. + // (2) Move down the stack pointer. ssize_t displacement = static_cast<ssize_t>(frame_size) - (spill_regs.size() * 8 + 8); str << "subq $" << displacement << ", %rsp\n"; - // 3) Store method reference. + // (3) Store method reference. str << "movq %rdi, (%rsp)\n"; - // 4) Entry spills. + // (4) Entry spills. str << "movq %rax, " << frame_size + 0 << "(%rsp)\n"; str << "movq %rbx, " << frame_size + 8 << "(%rsp)\n"; str << "movsd %xmm1, " << frame_size + 16 << "(%rsp)\n"; @@ -2186,10 +2047,10 @@ std::string removeframe_test_fn(JNIMacroAssemblerX86_64Test::Base* assembler_tes // Construct assembly text counterpart. std::ostringstream str; - // 1) Move up the stack pointer. + // (1) Move up the stack pointer. ssize_t displacement = static_cast<ssize_t>(frame_size) - spill_regs.size() * 8 - 8; str << "addq $" << displacement << ", %rsp\n"; - // 2) Pop spill regs. + // (2) Pop spill regs. str << "popq %r10\n"; str << "popq %rsi\n"; str << "ret\n"; |