summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/Android.bp27
-rw-r--r--compiler/common_compiler_test.cc2
-rw-r--r--compiler/compiled_method-inl.h59
-rw-r--r--compiler/compiled_method.cc10
-rw-r--r--compiler/compiled_method.h344
-rw-r--r--compiler/debug/dwarf/dwarf_test.h6
-rw-r--r--compiler/debug/elf_debug_frame_writer.h4
-rw-r--r--compiler/debug/elf_debug_info_writer.h7
-rw-r--r--compiler/debug/elf_debug_line_writer.h8
-rw-r--r--compiler/debug/elf_debug_writer.cc18
-rw-r--r--compiler/debug/elf_debug_writer.h4
-rw-r--r--compiler/debug/elf_gnu_debugdata_writer.h7
-rw-r--r--compiler/debug/elf_symtab_writer.h4
-rw-r--r--compiler/debug/method_debug_info.h3
-rw-r--r--compiler/debug/src_map_elem.h (renamed from compiler/image_write_read_test.cc)28
-rw-r--r--compiler/debug/src_map_elem_test.cc53
-rw-r--r--compiler/dex/dex_to_dex_compiler.cc2
-rw-r--r--compiler/dex/dex_to_dex_decompiler_test.cc2
-rw-r--r--compiler/driver/compiled_method_storage.cc9
-rw-r--r--compiler/driver/compiled_method_storage.h10
-rw-r--r--compiler/driver/compiled_method_storage_test.cc20
-rw-r--r--compiler/driver/compiler_driver.cc5
-rw-r--r--compiler/elf_writer.cc62
-rw-r--r--compiler/elf_writer.h83
-rw-r--r--compiler/elf_writer_quick.cc318
-rw-r--r--compiler/elf_writer_quick.h38
-rw-r--r--compiler/elf_writer_test.cc164
-rw-r--r--compiler/image_test.cc156
-rw-r--r--compiler/image_test.h500
-rw-r--r--compiler/image_writer.cc2839
-rw-r--r--compiler/image_writer.h625
-rw-r--r--compiler/jit/jit_compiler.cc3
-rw-r--r--compiler/jni/quick/jni_compiler.cc2
-rw-r--r--compiler/linker/arm/relative_patcher_arm_base.cc3
-rw-r--r--compiler/linker/arm/relative_patcher_thumb2.cc1
-rw-r--r--compiler/linker/arm64/relative_patcher_arm64.cc4
-rw-r--r--compiler/linker/buffered_output_stream.cc2
-rw-r--r--compiler/linker/buffered_output_stream.h2
-rw-r--r--compiler/linker/elf_builder.h (renamed from compiler/elf_builder.h)8
-rw-r--r--compiler/linker/error_delaying_output_stream.h2
-rw-r--r--compiler/linker/file_output_stream.cc2
-rw-r--r--compiler/linker/file_output_stream.h2
-rw-r--r--compiler/linker/linker_patch.h311
-rw-r--r--compiler/linker/linker_patch_test.cc (renamed from compiler/compiled_method_test.cc)34
-rw-r--r--compiler/linker/mips/relative_patcher_mips.cc1
-rw-r--r--compiler/linker/mips64/relative_patcher_mips64.cc1
-rw-r--r--compiler/linker/multi_oat_relative_patcher.cc72
-rw-r--r--compiler/linker/multi_oat_relative_patcher.h158
-rw-r--r--compiler/linker/multi_oat_relative_patcher_test.cc309
-rw-r--r--compiler/linker/output_stream.cc2
-rw-r--r--compiler/linker/output_stream.h2
-rw-r--r--compiler/linker/output_stream_test.cc2
-rw-r--r--compiler/linker/relative_patcher.h5
-rw-r--r--compiler/linker/relative_patcher_test.h6
-rw-r--r--compiler/linker/vector_output_stream.cc2
-rw-r--r--compiler/linker/vector_output_stream.h2
-rw-r--r--compiler/linker/x86/relative_patcher_x86.cc1
-rw-r--r--compiler/linker/x86_64/relative_patcher_x86_64.cc1
-rw-r--r--compiler/oat_test.cc870
-rw-r--r--compiler/oat_writer.cc3632
-rw-r--r--compiler/oat_writer.h494
-rw-r--r--compiler/optimizing/code_generator.cc3
-rw-r--r--compiler/optimizing/code_generator.h7
-rw-r--r--compiler/optimizing/code_generator_arm64.cc46
-rw-r--r--compiler/optimizing/code_generator_arm64.h6
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc46
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.h6
-rw-r--r--compiler/optimizing/code_generator_mips.cc149
-rw-r--r--compiler/optimizing/code_generator_mips.h6
-rw-r--r--compiler/optimizing/code_generator_mips64.cc42
-rw-r--r--compiler/optimizing/code_generator_mips64.h6
-rw-r--r--compiler/optimizing/code_generator_vector_arm64.cc229
-rw-r--r--compiler/optimizing/code_generator_vector_arm_vixl.cc36
-rw-r--r--compiler/optimizing/code_generator_vector_mips.cc61
-rw-r--r--compiler/optimizing/code_generator_vector_mips64.cc61
-rw-r--r--compiler/optimizing/code_generator_vector_x86.cc39
-rw-r--r--compiler/optimizing/code_generator_vector_x86_64.cc32
-rw-r--r--compiler/optimizing/code_generator_x86.cc41
-rw-r--r--compiler/optimizing/code_generator_x86.h6
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc41
-rw-r--r--compiler/optimizing/code_generator_x86_64.h6
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc1
-rw-r--r--compiler/optimizing/intrinsics_arm_vixl.cc1
-rw-r--r--compiler/optimizing/intrinsics_mips.cc1
-rw-r--r--compiler/optimizing/intrinsics_mips64.cc1
-rw-r--r--compiler/optimizing/intrinsics_x86.cc1
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc1
-rw-r--r--compiler/optimizing/loop_optimization.cc375
-rw-r--r--compiler/optimizing/loop_optimization.h6
-rw-r--r--compiler/optimizing/nodes.h1
-rw-r--r--compiler/optimizing/nodes_vector.h63
-rw-r--r--compiler/optimizing/optimizing_compiler.cc12
-rw-r--r--compiler/optimizing/scheduler_arm.cc1
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.cc4
-rw-r--r--compiler/utils/arm/assembler_arm_vixl.cc1
-rw-r--r--compiler/utils/arm64/assembler_arm64.cc1
-rw-r--r--compiler/utils/assembler_test.h142
-rw-r--r--compiler/utils/mips/assembler_mips.h1
-rw-r--r--compiler/utils/mips64/assembler_mips64.h1
-rw-r--r--compiler/utils/x86/assembler_x86.cc28
-rw-r--r--compiler/utils/x86/assembler_x86.h1
-rw-r--r--compiler/utils/x86/assembler_x86_test.cc81
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.cc28
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.h17
-rw-r--r--compiler/utils/x86_64/assembler_x86_64_test.cc929
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 = &sections[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";