Merge "ARM64: Update to VIXL 1.9."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c5669c0..1a4c30c 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -188,6 +188,7 @@
compiler/dex/local_value_numbering_test.cc \
compiler/dex/mir_graph_test.cc \
compiler/dex/mir_optimization_test.cc \
+ compiler/dwarf/dwarf_test.cc \
compiler/driver/compiler_driver_test.cc \
compiler/elf_writer_test.cc \
compiler/image_test.cc \
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 1d0aad5..96d90bb 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -24,6 +24,7 @@
#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex/verification_results.h"
#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "interpreter/interpreter.h"
#include "mirror/art_method.h"
#include "mirror/dex_cache.h"
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index d6a07f6..2386914 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -185,6 +185,7 @@
kLinkerPatchCall,
kLinkerPatchCallRelative, // NOTE: Actual patching is instruction_set-dependent.
kLinkerPatchType,
+ kLinkerPatchDexCacheArray, // NOTE: Actual patching is instruction_set-dependent.
};
class LinkerPatch {
@@ -192,28 +193,44 @@
static LinkerPatch MethodPatch(size_t literal_offset,
const DexFile* target_dex_file,
uint32_t target_method_idx) {
- return LinkerPatch(literal_offset, kLinkerPatchMethod,
- target_method_idx, target_dex_file);
+ LinkerPatch patch(literal_offset, kLinkerPatchMethod, target_dex_file);
+ patch.method_idx_ = target_method_idx;
+ return patch;
}
static LinkerPatch CodePatch(size_t literal_offset,
const DexFile* target_dex_file,
uint32_t target_method_idx) {
- return LinkerPatch(literal_offset, kLinkerPatchCall,
- target_method_idx, target_dex_file);
+ LinkerPatch patch(literal_offset, kLinkerPatchCall, 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) {
- return LinkerPatch(literal_offset, kLinkerPatchCallRelative,
- target_method_idx, target_dex_file);
+ LinkerPatch patch(literal_offset, kLinkerPatchCallRelative, target_dex_file);
+ patch.method_idx_ = target_method_idx;
+ return patch;
}
static LinkerPatch TypePatch(size_t literal_offset,
const DexFile* target_dex_file,
uint32_t target_type_idx) {
- return LinkerPatch(literal_offset, kLinkerPatchType, target_type_idx, target_dex_file);
+ LinkerPatch patch(literal_offset, kLinkerPatchType, target_dex_file);
+ patch.type_idx_ = target_type_idx;
+ return patch;
+ }
+
+ static LinkerPatch DexCacheArrayPatch(size_t literal_offset,
+ const DexFile* target_dex_file,
+ uint32_t pc_insn_offset,
+ size_t element_offset) {
+ DCHECK(IsUint<32>(element_offset));
+ LinkerPatch patch(literal_offset, kLinkerPatchDexCacheArray, target_dex_file);
+ patch.pc_insn_offset_ = pc_insn_offset;
+ patch.element_offset_ = element_offset;
+ return patch;
}
LinkerPatch(const LinkerPatch& other) = default;
@@ -227,10 +244,14 @@
return patch_type_;
}
+ bool IsPcRelative() const {
+ return Type() == kLinkerPatchCallRelative || Type() == kLinkerPatchDexCacheArray;
+ }
+
MethodReference TargetMethod() const {
DCHECK(patch_type_ == kLinkerPatchMethod ||
patch_type_ == kLinkerPatchCall || patch_type_ == kLinkerPatchCallRelative);
- return MethodReference(target_dex_file_, target_idx_);
+ return MethodReference(target_dex_file_, method_idx_);
}
const DexFile* TargetTypeDexFile() const {
@@ -240,22 +261,52 @@
uint32_t TargetTypeIndex() const {
DCHECK(patch_type_ == kLinkerPatchType);
- return target_idx_;
+ return type_idx_;
+ }
+
+ const DexFile* TargetDexCacheDexFile() const {
+ DCHECK(patch_type_ == kLinkerPatchDexCacheArray);
+ return target_dex_file_;
+ }
+
+ size_t TargetDexCacheElementOffset() const {
+ DCHECK(patch_type_ == kLinkerPatchDexCacheArray);
+ return element_offset_;
+ }
+
+ uint32_t PcInsnOffset() const {
+ DCHECK(patch_type_ == kLinkerPatchDexCacheArray);
+ return pc_insn_offset_;
}
private:
- LinkerPatch(size_t literal_offset, LinkerPatchType patch_type,
- uint32_t target_idx, const DexFile* target_dex_file)
- : literal_offset_(literal_offset),
- patch_type_(patch_type),
- target_idx_(target_idx),
- target_dex_file_(target_dex_file) {
+ LinkerPatch(size_t literal_offset, LinkerPatchType 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));
}
- size_t literal_offset_;
- LinkerPatchType patch_type_;
- uint32_t target_idx_; // Method index (Call/Method patches) or type index (Type patches).
const DexFile* target_dex_file_;
+ uint32_t literal_offset_ : 24; // Method code size up to 16MiB.
+ LinkerPatchType 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 element_offset_; // Element offset in the dex cache arrays.
+ };
+ union {
+ uint32_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_;
+ static_assert(sizeof(pc_insn_offset_) == sizeof(cmp2_), "needed by relational operators");
+ };
friend bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs);
friend bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs);
@@ -264,15 +315,17 @@
inline bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs) {
return lhs.literal_offset_ == rhs.literal_offset_ &&
lhs.patch_type_ == rhs.patch_type_ &&
- lhs.target_idx_ == rhs.target_idx_ &&
- lhs.target_dex_file_ == rhs.target_dex_file_;
+ 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_idx_ != rhs.target_idx_) ? lhs.target_idx_ < rhs.target_idx_
- : lhs.target_dex_file_ < rhs.target_dex_file_;
+ : (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 {
diff --git a/compiler/dex/mir_field_info.cc b/compiler/dex/mir_field_info.cc
index d2079a2..a9ab3bb 100644
--- a/compiler/dex/mir_field_info.cc
+++ b/compiler/dex/mir_field_info.cc
@@ -19,6 +19,7 @@
#include <string.h>
#include "base/logging.h"
+#include "dex/verified_method.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_driver-inl.h"
#include "mirror/class_loader.h" // Only to allow casts in Handle<ClassLoader>.
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 3103f96..58f12c9 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -688,7 +688,7 @@
/* Parse a Dex method and insert it into the MIRGraph at the current insert point. */
void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
- InvokeType invoke_type, uint16_t class_def_idx,
+ InvokeType invoke_type ATTRIBUTE_UNUSED, uint16_t class_def_idx,
uint32_t method_idx, jobject class_loader, const DexFile& dex_file) {
current_code_item_ = code_item;
method_stack_.push_back(std::make_pair(current_method_, current_offset_));
@@ -726,13 +726,6 @@
null_block->hidden = true;
entry_block_ = CreateNewBB(kEntryBlock);
exit_block_ = CreateNewBB(kExitBlock);
- // TODO: deprecate all "cu->" fields; move what's left to wherever CompilationUnit is allocated.
- cu_->dex_file = &dex_file;
- cu_->class_def_idx = class_def_idx;
- cu_->method_idx = method_idx;
- cu_->access_flags = access_flags;
- cu_->invoke_type = invoke_type;
- cu_->shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
} else {
UNIMPLEMENTED(FATAL) << "Nested inlining not implemented.";
/*
diff --git a/compiler/dex/mir_method_info.cc b/compiler/dex/mir_method_info.cc
index 34fb1bf..831ad42 100644
--- a/compiler/dex/mir_method_info.cc
+++ b/compiler/dex/mir_method_info.cc
@@ -16,9 +16,11 @@
# include "mir_method_info.h"
+#include "dex/verified_method.h"
#include "driver/compiler_driver.h"
#include "driver/dex_compilation_unit.h"
#include "driver/compiler_driver-inl.h"
+#include "driver/compiler_options.h"
#include "mirror/class_loader.h" // Only to allow casts in Handle<ClassLoader>.
#include "mirror/dex_cache.h" // Only to allow casts in Handle<DexCache>.
#include "scoped_thread_state_change.h"
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index d46c25a..3081c9e 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -23,6 +23,7 @@
#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "gc/accounting/card_table.h"
#include "mirror/art_method.h"
#include "mirror/object_array-inl.h"
diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h
index d15412a..f6fa938 100644
--- a/compiler/dex/quick/arm64/arm64_lir.h
+++ b/compiler/dex/quick/arm64/arm64_lir.h
@@ -236,6 +236,7 @@
kA64Add4rrro, // add [00001011000] rm[20-16] imm_6[15-10] rn[9-5] rd[4-0].
kA64Add4RRre, // add [00001011001] rm[20-16] option[15-13] imm_3[12-10] rn[9-5] rd[4-0].
kA64Adr2xd, // adr [0] immlo[30-29] [10000] immhi[23-5] rd[4-0].
+ kA64Adrp2xd, // adrp [1] immlo[30-29] [10000] immhi[23-5] rd[4-0].
kA64And3Rrl, // and [00010010] N[22] imm_r[21-16] imm_s[15-10] rn[9-5] rd[4-0].
kA64And4rrro, // and [00001010] shift[23-22] [N=0] rm[20-16] imm_6[15-10] rn[9-5] rd[4-0].
kA64Asr3rrd, // asr [0001001100] immr[21-16] imms[15-10] rn[9-5] rd[4-0].
diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc
index 329bb1e..a59deb5 100644
--- a/compiler/dex/quick/arm64/assemble_arm64.cc
+++ b/compiler/dex/quick/arm64/assemble_arm64.cc
@@ -131,6 +131,10 @@
kFmtRegX, 4, 0, kFmtImm21, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | NEEDS_FIXUP,
"adr", "!0x, #!1d", kFixupAdr),
+ ENCODING_MAP(kA64Adrp2xd, NO_VARIANTS(0x90000000),
+ kFmtRegX, 4, 0, kFmtImm21, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | NEEDS_FIXUP,
+ "adrp", "!0x, #!1d", kFixupLabel),
ENCODING_MAP(WIDE(kA64And3Rrl), SF_VARIANTS(0x12000000),
kFmtRegROrSp, 4, 0, kFmtRegR, 9, 5, kFmtBitBlt, 22, 10,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 823cb60..3316945 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -23,10 +23,12 @@
#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "gc/accounting/card_table.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "mirror/art_method.h"
#include "mirror/object_array-inl.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
namespace art {
@@ -438,13 +440,13 @@
* Bit of a hack here - in the absence of a real scheduling pass,
* emit the next instruction in static & direct invoke sequences.
*/
-static int Arm64NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
- int state, const MethodReference& target_method,
- uint32_t unused_idx,
- uintptr_t direct_code, uintptr_t direct_method,
- InvokeType type) {
+int Arm64Mir2Lir::Arm64NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
+ int state, const MethodReference& target_method,
+ uint32_t unused_idx,
+ uintptr_t direct_code, uintptr_t direct_method,
+ InvokeType type) {
UNUSED(info, unused_idx);
- Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+ Arm64Mir2Lir* cg = static_cast<Arm64Mir2Lir*>(cu->cg.get());
if (direct_code != 0 && direct_method != 0) {
switch (state) {
case 0: // Get the current Method* [sets kArg0]
@@ -465,17 +467,24 @@
return -1;
}
} else {
+ bool use_pc_rel = cg->CanUseOpPcRelDexCacheArrayLoad();
RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
switch (state) {
case 0: // Get the current Method* [sets kArg0]
// TUNING: we can save a reg copy if Method* has been promoted.
- cg->LoadCurrMethodDirect(arg0_ref);
- break;
+ if (!use_pc_rel) {
+ cg->LoadCurrMethodDirect(arg0_ref);
+ break;
+ }
+ ++state;
+ FALLTHROUGH_INTENDED;
case 1: // Get method->dex_cache_resolved_methods_
- cg->LoadRefDisp(arg0_ref,
- mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
- arg0_ref,
- kNotVolatile);
+ if (!use_pc_rel) {
+ cg->LoadRefDisp(arg0_ref,
+ mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
+ arg0_ref,
+ kNotVolatile);
+ }
// Set up direct code if known.
if (direct_code != 0) {
if (direct_code != static_cast<uintptr_t>(-1)) {
@@ -487,14 +496,23 @@
cg->LoadCodeAddress(target_method, type, kInvokeTgt);
}
}
- break;
+ if (!use_pc_rel || direct_code != 0) {
+ break;
+ }
+ ++state;
+ FALLTHROUGH_INTENDED;
case 2: // Grab target method*
CHECK_EQ(cu->dex_file, target_method.dex_file);
- cg->LoadRefDisp(arg0_ref,
- mirror::ObjectArray<mirror::Object>::OffsetOfElement(
- target_method.dex_method_index).Int32Value(),
- arg0_ref,
- kNotVolatile);
+ if (!use_pc_rel) {
+ cg->LoadRefDisp(arg0_ref,
+ mirror::ObjectArray<mirror::Object>::OffsetOfElement(
+ target_method.dex_method_index).Int32Value(),
+ arg0_ref,
+ kNotVolatile);
+ } else {
+ size_t offset = cg->dex_cache_arrays_layout_.MethodOffset(target_method.dex_method_index);
+ cg->OpPcRelDexCacheArrayLoad(cu->dex_file, offset, arg0_ref);
+ }
break;
case 3: // Grab the code from the method*
if (direct_code == 0) {
diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h
index 54fd46d..8184f02 100644
--- a/compiler/dex/quick/arm64/codegen_arm64.h
+++ b/compiler/dex/quick/arm64/codegen_arm64.h
@@ -78,6 +78,9 @@
/// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage)
void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE;
+ bool CanUseOpPcRelDexCacheArrayLoad() const OVERRIDE;
+ void OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset, RegStorage r_dest) OVERRIDE;
+
LIR* OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg,
int offset, int check_value, LIR* target, LIR** compare) OVERRIDE;
@@ -393,9 +396,16 @@
void GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2, bool is_div, int flags);
+ static int Arm64NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
+ int state, const MethodReference& target_method,
+ uint32_t unused_idx,
+ uintptr_t direct_code, uintptr_t direct_method,
+ InvokeType type);
+
static const A64EncodingMap EncodingMap[kA64Last];
ArenaVector<LIR*> call_method_insns_;
+ ArenaVector<LIR*> dex_cache_access_insns_;
int GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) OVERRIDE;
};
diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc
index 2372ccc..a9d9f3d 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -943,6 +943,23 @@
lir->target = target;
}
+bool Arm64Mir2Lir::CanUseOpPcRelDexCacheArrayLoad() const {
+ return dex_cache_arrays_layout_.Valid();
+}
+
+void Arm64Mir2Lir::OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset,
+ RegStorage r_dest) {
+ LIR* adrp = NewLIR2(kA64Adrp2xd, r_dest.GetReg(), 0);
+ adrp->operands[2] = WrapPointer(dex_file);
+ adrp->operands[3] = offset;
+ adrp->operands[4] = WrapPointer(adrp);
+ dex_cache_access_insns_.push_back(adrp);
+ LIR* ldr = LoadBaseDisp(r_dest, 0, r_dest, kReference, kNotVolatile);
+ ldr->operands[4] = adrp->operands[4];
+ ldr->flags.fixup = kFixupLabel;
+ dex_cache_access_insns_.push_back(ldr);
+}
+
LIR* Arm64Mir2Lir::OpVldm(RegStorage r_base, int count) {
UNUSED(r_base, count);
LOG(FATAL) << "Unexpected use of OpVldm for Arm64";
diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc
index 09a34bf..c5c0dc5 100644
--- a/compiler/dex/quick/arm64/target_arm64.cc
+++ b/compiler/dex/quick/arm64/target_arm64.cc
@@ -606,7 +606,8 @@
Arm64Mir2Lir::Arm64Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
: Mir2Lir(cu, mir_graph, arena),
- call_method_insns_(arena->Adapter()) {
+ call_method_insns_(arena->Adapter()),
+ dex_cache_access_insns_(arena->Adapter()) {
// Sanity check - make sure encoding map lines up.
for (int i = 0; i < kA64Last; i++) {
DCHECK_EQ(UNWIDE(Arm64Mir2Lir::EncodingMap[i].opcode), i)
@@ -846,8 +847,9 @@
}
void Arm64Mir2Lir::InstallLiteralPools() {
+ patches_.reserve(call_method_insns_.size() + dex_cache_access_insns_.size());
+
// PC-relative calls to methods.
- patches_.reserve(call_method_insns_.size());
for (LIR* p : call_method_insns_) {
DCHECK_EQ(p->opcode, kA64Bl1t);
uint32_t target_method_idx = p->operands[1];
@@ -856,6 +858,18 @@
target_dex_file, target_method_idx));
}
+ // PC-relative references to dex cache arrays.
+ for (LIR* p : dex_cache_access_insns_) {
+ DCHECK(p->opcode == kA64Adrp2xd || p->opcode == kA64Ldr3rXD);
+ const LIR* adrp = UnwrapPointer<LIR>(p->operands[4]);
+ DCHECK_EQ(adrp->opcode, kA64Adrp2xd);
+ const DexFile* dex_file = UnwrapPointer<DexFile>(adrp->operands[2]);
+ uint32_t offset = adrp->operands[3];
+ DCHECK(!p->flags.is_nop);
+ DCHECK(!adrp->flags.is_nop);
+ patches_.push_back(LinkerPatch::DexCacheArrayPatch(p->offset, dex_file, adrp->offset, offset));
+ }
+
// And do the normal processing.
Mir2Lir::InstallLiteralPools();
}
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index df72830..509d448 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -29,6 +29,7 @@
#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex/verification_results.h"
#include "dex/verified_method.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
#include "verifier/dex_gc_map.h"
#include "verifier/method_verifier.h"
#include "vmap_table.h"
@@ -1053,6 +1054,7 @@
mem_ref_type_(ResourceMask::kHeapRef),
mask_cache_(arena),
safepoints_(arena->Adapter()),
+ dex_cache_arrays_layout_(cu->compiler_driver->GetDexCacheArraysLayout(cu->dex_file)),
in_to_reg_storage_mapping_(arena) {
switch_tables_.reserve(4);
fill_array_data_.reserve(4);
@@ -1304,6 +1306,17 @@
OpPcRelLoad(TargetReg(symbolic_reg, kRef), data_target);
}
+bool Mir2Lir::CanUseOpPcRelDexCacheArrayLoad() const {
+ return false;
+}
+
+void Mir2Lir::OpPcRelDexCacheArrayLoad(const DexFile* dex_file ATTRIBUTE_UNUSED,
+ int offset ATTRIBUTE_UNUSED,
+ RegStorage r_dest ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "No generic implementation.";
+ UNREACHABLE();
+}
+
std::vector<uint8_t>* Mir2Lir::ReturnFrameDescriptionEntry() {
// Default case is to do nothing.
return nullptr;
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index 2bcaaca..1813e09 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -24,12 +24,14 @@
#include "dex/mir_graph.h"
#include "dex/quick/arm/arm_lir.h"
#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "mirror/array.h"
#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_reference.h"
#include "utils.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
#include "verifier/method_verifier.h"
namespace art {
@@ -56,6 +58,42 @@
return (cu->enable_debug & (1 << kDebugSlowTypePath)) != 0;
}
+void Mir2Lir::GenIfNullUseHelperImmMethod(
+ RegStorage r_result, QuickEntrypointEnum trampoline, int imm, RegStorage r_method) {
+ class CallHelperImmMethodSlowPath : public LIRSlowPath {
+ public:
+ CallHelperImmMethodSlowPath(Mir2Lir* m2l, LIR* fromfast, LIR* cont,
+ QuickEntrypointEnum trampoline_in, int imm_in,
+ RegStorage r_method_in, RegStorage r_result_in)
+ : LIRSlowPath(m2l, fromfast, cont), trampoline_(trampoline_in),
+ imm_(imm_in), r_method_(r_method_in), r_result_(r_result_in) {
+ }
+
+ void Compile() {
+ GenerateTargetLabel();
+ if (r_method_.Valid()) {
+ m2l_->CallRuntimeHelperImmReg(trampoline_, imm_, r_method_, true);
+ } else {
+ m2l_->CallRuntimeHelperImmMethod(trampoline_, imm_, true);
+ }
+ m2l_->OpRegCopy(r_result_, m2l_->TargetReg(kRet0, kRef));
+ m2l_->OpUnconditionalBranch(cont_);
+ }
+
+ private:
+ QuickEntrypointEnum trampoline_;
+ const int imm_;
+ const RegStorage r_method_;
+ const RegStorage r_result_;
+ };
+
+ LIR* branch = OpCmpImmBranch(kCondEq, r_result, 0, NULL);
+ LIR* cont = NewLIR0(kPseudoTargetLabel);
+
+ AddSlowPath(new (arena_) CallHelperImmMethodSlowPath(this, branch, cont, trampoline, imm,
+ r_method, r_result));
+}
+
/*
* Generate a kPseudoBarrier marker to indicate the boundary of special
* blocks.
@@ -1022,64 +1060,41 @@
}
void Mir2Lir::GenConstClass(uint32_t type_idx, RegLocation rl_dest) {
- RegLocation rl_method = LoadCurrMethod();
- CheckRegLocation(rl_method);
- RegStorage res_reg = AllocTempRef();
+ RegLocation rl_result;
if (!cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx,
*cu_->dex_file,
type_idx)) {
// Call out to helper which resolves type and verifies access.
// Resolved type returned in kRet0.
- CallRuntimeHelperImmReg(kQuickInitializeTypeAndVerifyAccess, type_idx, rl_method.reg, true);
- RegLocation rl_result = GetReturn(kRefReg);
- StoreValue(rl_dest, rl_result);
+ CallRuntimeHelperImmMethod(kQuickInitializeTypeAndVerifyAccess, type_idx, true);
+ rl_result = GetReturn(kRefReg);
} else {
- RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
- // We're don't need access checks, load type from dex cache
- int32_t dex_cache_offset =
- mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value();
- LoadRefDisp(rl_method.reg, dex_cache_offset, res_reg, kNotVolatile);
- int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
- LoadRefDisp(res_reg, offset_of_type, rl_result.reg, kNotVolatile);
+ rl_result = EvalLoc(rl_dest, kRefReg, true);
+ // We don't need access checks, load type from dex cache
+ RegStorage r_method = RegStorage::InvalidReg();
+ if (CanUseOpPcRelDexCacheArrayLoad()) {
+ size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
+ OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, rl_result.reg);
+ } else {
+ RegLocation rl_method = LoadCurrMethod();
+ CheckRegLocation(rl_method);
+ r_method = rl_method.reg;
+ int32_t dex_cache_offset =
+ mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value();
+ RegStorage res_reg = AllocTempRef();
+ LoadRefDisp(r_method, dex_cache_offset, res_reg, kNotVolatile);
+ int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
+ LoadRefDisp(res_reg, offset_of_type, rl_result.reg, kNotVolatile);
+ FreeTemp(res_reg);
+ }
if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file,
type_idx) || ForceSlowTypePath(cu_)) {
// Slow path, at runtime test if type is null and if so initialize
FlushAllRegs();
- LIR* branch = OpCmpImmBranch(kCondEq, rl_result.reg, 0, NULL);
- LIR* cont = NewLIR0(kPseudoTargetLabel);
-
- // Object to generate the slow path for class resolution.
- class SlowPath : public LIRSlowPath {
- public:
- SlowPath(Mir2Lir* m2l, LIR* fromfast, LIR* cont_in, const int type_idx_in,
- const RegLocation& rl_method_in, const RegLocation& rl_result_in)
- : LIRSlowPath(m2l, fromfast, cont_in),
- type_idx_(type_idx_in), rl_method_(rl_method_in), rl_result_(rl_result_in) {
- }
-
- void Compile() {
- GenerateTargetLabel();
-
- m2l_->CallRuntimeHelperImmReg(kQuickInitializeType, type_idx_, rl_method_.reg, true);
- m2l_->OpRegCopy(rl_result_.reg, m2l_->TargetReg(kRet0, kRef));
- m2l_->OpUnconditionalBranch(cont_);
- }
-
- private:
- const int type_idx_;
- const RegLocation rl_method_;
- const RegLocation rl_result_;
- };
-
- // Add to list for future.
- AddSlowPath(new (arena_) SlowPath(this, branch, cont, type_idx, rl_method, rl_result));
-
- StoreValue(rl_dest, rl_result);
- } else {
- // Fast path, we're done - just store result
- StoreValue(rl_dest, rl_result);
+ GenIfNullUseHelperImmMethod(rl_result.reg, kQuickInitializeType, type_idx, r_method);
}
}
+ StoreValue(rl_dest, rl_result);
}
void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) {
@@ -1092,64 +1107,42 @@
FlushAllRegs();
LockCallTemps(); // Using explicit registers
- // If the Method* is already in a register, we can save a copy.
- RegLocation rl_method = mir_graph_->GetMethodLoc();
- RegStorage r_method;
- if (rl_method.location == kLocPhysReg) {
- // A temp would conflict with register use below.
- DCHECK(!IsTemp(rl_method.reg));
- r_method = rl_method.reg;
- } else {
- r_method = TargetReg(kArg2, kRef);
- LoadCurrMethodDirect(r_method);
- }
- // Method to declaring class.
- LoadRefDisp(r_method, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
- TargetReg(kArg0, kRef), kNotVolatile);
- // Declaring class to dex cache strings.
- LoadRefDisp(TargetReg(kArg0, kRef), mirror::Class::DexCacheStringsOffset().Int32Value(),
- TargetReg(kArg0, kRef), kNotVolatile);
-
// Might call out to helper, which will return resolved string in kRet0
- LoadRefDisp(TargetReg(kArg0, kRef), offset_of_string, TargetReg(kRet0, kRef), kNotVolatile);
- LIR* fromfast = OpCmpImmBranch(kCondEq, TargetReg(kRet0, kRef), 0, NULL);
- LIR* cont = NewLIR0(kPseudoTargetLabel);
+ RegStorage ret0 = TargetReg(kRet0, kRef);
+ RegStorage r_method = RegStorage::InvalidReg();
+ if (CanUseOpPcRelDexCacheArrayLoad()) {
+ size_t offset = dex_cache_arrays_layout_.StringOffset(string_idx);
+ OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, ret0);
+ } else {
+ r_method = LoadCurrMethodWithHint(TargetReg(kArg1, kRef));
+ // Method to declaring class.
+ RegStorage arg0 = TargetReg(kArg0, kRef);
+ LoadRefDisp(r_method, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
+ arg0, kNotVolatile);
+ // Declaring class to dex cache strings.
+ LoadRefDisp(arg0, mirror::Class::DexCacheStringsOffset().Int32Value(), arg0, kNotVolatile);
- {
- // Object to generate the slow path for string resolution.
- class SlowPath : public LIRSlowPath {
- public:
- SlowPath(Mir2Lir* m2l, LIR* fromfast_in, LIR* cont_in, RegStorage r_method_in,
- int32_t string_idx_in)
- : LIRSlowPath(m2l, fromfast_in, cont_in),
- r_method_(r_method_in), string_idx_(string_idx_in) {
- }
-
- void Compile() {
- GenerateTargetLabel();
- m2l_->CallRuntimeHelperImmReg(kQuickResolveString, string_idx_, r_method_, true);
- m2l_->OpUnconditionalBranch(cont_);
- }
-
- private:
- const RegStorage r_method_;
- const int32_t string_idx_;
- };
-
- AddSlowPath(new (arena_) SlowPath(this, fromfast, cont, r_method, string_idx));
+ LoadRefDisp(arg0, offset_of_string, ret0, kNotVolatile);
}
+ GenIfNullUseHelperImmMethod(ret0, kQuickResolveString, string_idx, r_method);
GenBarrier();
StoreValue(rl_dest, GetReturn(kRefReg));
} else {
- RegLocation rl_method = LoadCurrMethod();
- RegStorage res_reg = AllocTempRef();
RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
- LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), res_reg,
- kNotVolatile);
- LoadRefDisp(res_reg, mirror::Class::DexCacheStringsOffset().Int32Value(), res_reg,
- kNotVolatile);
- LoadRefDisp(res_reg, offset_of_string, rl_result.reg, kNotVolatile);
+ if (CanUseOpPcRelDexCacheArrayLoad()) {
+ size_t offset = dex_cache_arrays_layout_.StringOffset(string_idx);
+ OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, rl_result.reg);
+ } else {
+ RegLocation rl_method = LoadCurrMethod();
+ RegStorage res_reg = AllocTempRef();
+ LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), res_reg,
+ kNotVolatile);
+ LoadRefDisp(res_reg, mirror::Class::DexCacheStringsOffset().Int32Value(), res_reg,
+ kNotVolatile);
+ LoadRefDisp(res_reg, offset_of_string, rl_result.reg, kNotVolatile);
+ FreeTemp(res_reg);
+ }
StoreValue(rl_dest, rl_result);
}
}
@@ -1224,14 +1217,20 @@
RegStorage check_class = AllocTypedTemp(false, kRefReg);
RegStorage object_class = AllocTypedTemp(false, kRefReg);
- LoadCurrMethodDirect(check_class);
if (use_declaring_class) {
- LoadRefDisp(check_class, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), check_class,
+ RegStorage r_method = LoadCurrMethodWithHint(check_class);
+ LoadRefDisp(r_method, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), check_class,
kNotVolatile);
LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class,
kNotVolatile);
+ } else if (CanUseOpPcRelDexCacheArrayLoad()) {
+ size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
+ OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, check_class);
+ LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class,
+ kNotVolatile);
} else {
- LoadRefDisp(check_class, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
+ RegStorage r_method = LoadCurrMethodWithHint(check_class);
+ LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
check_class, kNotVolatile);
LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class,
kNotVolatile);
@@ -1267,20 +1266,19 @@
FlushAllRegs();
// May generate a call - use explicit registers
LockCallTemps();
- RegStorage method_reg = TargetReg(kArg1, kRef);
- LoadCurrMethodDirect(method_reg); // kArg1 <= current Method*
RegStorage class_reg = TargetReg(kArg2, kRef); // kArg2 will hold the Class*
RegStorage ref_reg = TargetReg(kArg0, kRef); // kArg0 will hold the ref.
RegStorage ret_reg = GetReturn(kRefReg).reg;
if (needs_access_check) {
// Check we have access to type_idx and if not throw IllegalAccessError,
// returns Class* in kArg0
- CallRuntimeHelperImm(kQuickInitializeTypeAndVerifyAccess, type_idx, true);
+ CallRuntimeHelperImmMethod(kQuickInitializeTypeAndVerifyAccess, type_idx, true);
OpRegCopy(class_reg, ret_reg); // Align usage with fast path
LoadValueDirectFixed(rl_src, ref_reg); // kArg0 <= ref
} else if (use_declaring_class) {
+ RegStorage r_method = LoadCurrMethodWithHint(TargetReg(kArg1, kRef));
LoadValueDirectFixed(rl_src, ref_reg); // kArg0 <= ref
- LoadRefDisp(method_reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
+ LoadRefDisp(r_method, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
class_reg, kNotVolatile);
} else {
if (can_assume_type_is_in_dex_cache) {
@@ -1288,42 +1286,23 @@
LoadValueDirectFixed(rl_src, ref_reg); // kArg0 <= ref
}
- // Load dex cache entry into class_reg (kArg2)
- LoadRefDisp(method_reg, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
- class_reg, kNotVolatile);
- int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
- LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
+ RegStorage r_method = RegStorage::InvalidReg();
+ if (CanUseOpPcRelDexCacheArrayLoad()) {
+ size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
+ OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, class_reg);
+ } else {
+ r_method = LoadCurrMethodWithHint(TargetReg(kArg1, kRef));
+ // Load dex cache entry into class_reg (kArg2)
+ LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
+ class_reg, kNotVolatile);
+ int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
+ LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
+ }
if (!can_assume_type_is_in_dex_cache) {
- LIR* slow_path_branch = OpCmpImmBranch(kCondEq, class_reg, 0, NULL);
- LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
+ GenIfNullUseHelperImmMethod(class_reg, kQuickInitializeType, type_idx, r_method);
// Should load value here.
LoadValueDirectFixed(rl_src, ref_reg); // kArg0 <= ref
-
- class InitTypeSlowPath : public Mir2Lir::LIRSlowPath {
- public:
- InitTypeSlowPath(Mir2Lir* m2l, LIR* branch, LIR* cont, uint32_t type_idx_in,
- RegLocation rl_src_in)
- : LIRSlowPath(m2l, branch, cont), type_idx_(type_idx_in),
- rl_src_(rl_src_in) {
- }
-
- void Compile() OVERRIDE {
- GenerateTargetLabel();
-
- m2l_->CallRuntimeHelperImm(kQuickInitializeType, type_idx_, true);
- m2l_->OpRegCopy(m2l_->TargetReg(kArg2, kRef),
- m2l_->TargetReg(kRet0, kRef)); // Align usage with fast path
- m2l_->OpUnconditionalBranch(cont_);
- }
-
- private:
- uint32_t type_idx_;
- RegLocation rl_src_;
- };
-
- AddSlowPath(new (arena_) InitTypeSlowPath(this, slow_path_branch, slow_path_target,
- type_idx, rl_src));
}
}
/* kArg0 is ref, kArg2 is class. If ref==null, use directly as bool result */
@@ -1426,55 +1405,34 @@
FlushAllRegs();
// May generate a call - use explicit registers
LockCallTemps();
- RegStorage method_reg = TargetReg(kArg1, kRef);
- LoadCurrMethodDirect(method_reg); // kArg1 <= current Method*
RegStorage class_reg = TargetReg(kArg2, kRef); // kArg2 will hold the Class*
if (needs_access_check) {
// Check we have access to type_idx and if not throw IllegalAccessError,
// returns Class* in kRet0
// InitializeTypeAndVerifyAccess(idx, method)
- CallRuntimeHelperImm(kQuickInitializeTypeAndVerifyAccess, type_idx, true);
+ CallRuntimeHelperImmMethod(kQuickInitializeTypeAndVerifyAccess, type_idx, true);
OpRegCopy(class_reg, TargetReg(kRet0, kRef)); // Align usage with fast path
} else if (use_declaring_class) {
+ RegStorage method_reg = LoadCurrMethodWithHint(TargetReg(kArg1, kRef));
LoadRefDisp(method_reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
class_reg, kNotVolatile);
} else {
// Load dex cache entry into class_reg (kArg2)
- LoadRefDisp(method_reg, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
- class_reg, kNotVolatile);
- int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
- LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
+ RegStorage r_method = RegStorage::InvalidReg();
+ if (CanUseOpPcRelDexCacheArrayLoad()) {
+ size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
+ OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, class_reg);
+ } else {
+ r_method = LoadCurrMethodWithHint(TargetReg(kArg1, kRef));
+
+ LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
+ class_reg, kNotVolatile);
+ int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
+ LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
+ }
if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx)) {
// Need to test presence of type in dex cache at runtime
- LIR* hop_branch = OpCmpImmBranch(kCondEq, class_reg, 0, NULL);
- LIR* cont = NewLIR0(kPseudoTargetLabel);
-
- // Slow path to initialize the type. Executed if the type is NULL.
- class SlowPath : public LIRSlowPath {
- public:
- SlowPath(Mir2Lir* m2l, LIR* fromfast, LIR* cont_in, const int type_idx_in,
- const RegStorage class_reg_in)
- : LIRSlowPath(m2l, fromfast, cont_in),
- type_idx_(type_idx_in), class_reg_(class_reg_in) {
- }
-
- void Compile() {
- GenerateTargetLabel();
-
- // Call out to helper, which will return resolved type in kArg0
- // InitializeTypeFromCode(idx, method)
- m2l_->CallRuntimeHelperImmReg(kQuickInitializeType, type_idx_,
- m2l_->TargetReg(kArg1, kRef), true);
- m2l_->OpRegCopy(class_reg_, m2l_->TargetReg(kRet0, kRef)); // Align usage with fast path
- m2l_->OpUnconditionalBranch(cont_);
- }
-
- public:
- const int type_idx_;
- const RegStorage class_reg_;
- };
-
- AddSlowPath(new (arena_) SlowPath(this, hop_branch, cont, type_idx, class_reg));
+ GenIfNullUseHelperImmMethod(class_reg, kQuickInitializeType, type_idx, r_method);
}
}
// At this point, class_reg (kArg2) has class
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 2d41ba1..e747239 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -24,6 +24,7 @@
#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex_file-inl.h"
#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "invoke_type.h"
#include "mirror/array.h"
diff --git a/compiler/dex/quick/gen_loadstore.cc b/compiler/dex/quick/gen_loadstore.cc
index b71691f..54e5742 100644
--- a/compiler/dex/quick/gen_loadstore.cc
+++ b/compiler/dex/quick/gen_loadstore.cc
@@ -340,6 +340,20 @@
LoadValueDirectFixed(mir_graph_->GetMethodLoc(), r_tgt);
}
+RegStorage Mir2Lir::LoadCurrMethodWithHint(RegStorage r_hint) {
+ // If the method is promoted to a register, return that register, otherwise load it to r_hint.
+ // (Replacement for LoadCurrMethod() usually used when LockCallTemps() is in effect.)
+ DCHECK(r_hint.Valid());
+ RegLocation rl_method = mir_graph_->GetMethodLoc();
+ if (rl_method.location == kLocPhysReg) {
+ DCHECK(!IsTemp(rl_method.reg));
+ return rl_method.reg;
+ } else {
+ LoadCurrMethodDirect(r_hint);
+ return r_hint;
+ }
+}
+
RegLocation Mir2Lir::LoadCurrMethod() {
return LoadValue(mir_graph_->GetMethodLoc(), kRefReg);
}
diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc
index e573899..6cdf567 100644
--- a/compiler/dex/quick/local_optimizations.cc
+++ b/compiler/dex/quick/local_optimizations.cc
@@ -493,15 +493,14 @@
/* Found a slot to hoist to */
if (slot >= 0) {
LIR* cur_lir = prev_inst_list[slot];
- LIR* new_load_lir =
- static_cast<LIR*>(arena_->Alloc(sizeof(LIR), kArenaAllocLIR));
- *new_load_lir = *this_lir;
+ LIR* prev_lir = PREV_LIR(this_lir);
+ UnlinkLIR(this_lir);
/*
* Insertion is guaranteed to succeed since check_lir
* is never the first LIR on the list
*/
- InsertLIRBefore(cur_lir, new_load_lir);
- NopLIR(this_lir);
+ InsertLIRBefore(cur_lir, this_lir);
+ this_lir = prev_lir; // Continue the loop with the next LIR.
}
}
}
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index cca4e5a..bb8fbae 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -32,6 +32,7 @@
#include "leb128.h"
#include "safe_map.h"
#include "utils/array_ref.h"
+#include "utils/dex_cache_arrays_layout.h"
#include "utils/stack_checks.h"
namespace art {
@@ -956,6 +957,7 @@
// Shared by all targets - implemented in gen_loadstore.cc.
RegLocation LoadCurrMethod();
void LoadCurrMethodDirect(RegStorage r_tgt);
+ RegStorage LoadCurrMethodWithHint(RegStorage r_hint);
virtual LIR* LoadConstant(RegStorage r_dest, int value);
// Natural word size.
LIR* LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) {
@@ -1093,6 +1095,18 @@
virtual void LoadClassType(const DexFile& dex_file, uint32_t type_idx,
SpecialTargetRegister symbolic_reg);
+ // TODO: Support PC-relative dex cache array loads on all platforms and
+ // replace CanUseOpPcRelDexCacheArrayLoad() with dex_cache_arrays_layout_.Valid().
+ virtual bool CanUseOpPcRelDexCacheArrayLoad() const;
+
+ /*
+ * @brief Load an element of one of the dex cache arrays.
+ * @param dex_file the dex file associated with the target dex cache.
+ * @param offset the offset of the element in the fixed dex cache arrays' layout.
+ * @param r_dest the register where to load the element.
+ */
+ virtual void OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset, RegStorage r_dest);
+
// Routines that work for the generic case, but may be overriden by target.
/*
* @brief Compare memory to immediate, and branch if condition true.
@@ -1596,7 +1610,6 @@
*/
virtual bool GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special);
- protected:
void ClobberBody(RegisterInfo* p);
void SetCurrentDexPc(DexOffset dexpc) {
current_dalvik_offset_ = dexpc;
@@ -1669,6 +1682,16 @@
*/
bool GenSpecialIdentity(MIR* mir, const InlineMethod& special);
+ /**
+ * @brief Generate code to check if result is null and, if it is, call helper to load it.
+ * @param r_result the result register.
+ * @param trampoline the helper to call in slow path.
+ * @param imm the immediate passed to the helper.
+ * @param r_method the register with ArtMethod* if available, otherwise RegStorage::Invalid().
+ */
+ void GenIfNullUseHelperImmMethod(
+ RegStorage r_result, QuickEntrypointEnum trampoline, int imm, RegStorage r_method);
+
void AddDivZeroCheckSlowPath(LIR* branch);
// Copy arg0 and arg1 to kArg0 and kArg1 safely, possibly using
@@ -1815,7 +1838,9 @@
// Record the MIR that generated a given safepoint (nullptr for prologue safepoints).
ArenaVector<std::pair<LIR*, MIR*>> safepoints_;
- protected:
+ // The layout of the cu_->dex_file's dex cache arrays for PC-relative addressing.
+ const DexCacheArraysLayout dex_cache_arrays_layout_;
+
// ABI support
class ShortyArg {
public:
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 6d28984..8baafc7 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -634,6 +634,12 @@
instruction_set = kThumb2;
}
CompilationUnit cu(runtime->GetArenaPool(), instruction_set, driver, class_linker);
+ cu.dex_file = &dex_file;
+ cu.class_def_idx = class_def_idx;
+ cu.method_idx = method_idx;
+ cu.access_flags = access_flags;
+ cu.invoke_type = invoke_type;
+ cu.shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
CHECK((cu.instruction_set == kThumb2) ||
(cu.instruction_set == kArm64) ||
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index abee872..e81228a 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -21,6 +21,7 @@
#include "base/logging.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "gc/accounting/card_table.h"
#include "mirror/art_method.h"
#include "mirror/object_array-inl.h"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 100d49a..670efee 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -31,6 +31,7 @@
#include "base/timing_logger.h"
#include "class_linker.h"
#include "compiled_class.h"
+#include "compiled_method.h"
#include "compiler.h"
#include "compiler_driver-inl.h"
#include "dex_compilation_unit.h"
@@ -62,6 +63,7 @@
#include "thread_pool.h"
#include "trampolines/trampoline_compiler.h"
#include "transaction.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
#include "utils/swap_space.h"
#include "verifier/method_verifier.h"
#include "verifier/method_verifier-inl.h"
@@ -1173,6 +1175,13 @@
return klass->GetDisableIntrinsicFlagOffset().Uint32Value();
}
+DexCacheArraysLayout CompilerDriver::GetDexCacheArraysLayout(const DexFile* dex_file) {
+ // Currently only image dex caches have fixed array layout.
+ return IsImage() && GetSupportBootImageFixup()
+ ? DexCacheArraysLayout(dex_file)
+ : DexCacheArraysLayout();
+}
+
void CompilerDriver::ProcessedInstanceField(bool resolved) {
if (!resolved) {
stats_->UnresolvedInstanceField();
@@ -2246,7 +2255,7 @@
// Count non-relative linker patches.
size_t non_relative_linker_patch_count = 0u;
for (const LinkerPatch& patch : compiled_method->GetPatches()) {
- if (patch.Type() != kLinkerPatchCallRelative) {
+ if (!patch.IsPcRelative()) {
++non_relative_linker_patch_count;
}
}
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index b825293..efcaae4 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -26,11 +26,8 @@
#include "base/mutex.h"
#include "base/timing_logger.h"
#include "class_reference.h"
-#include "compiled_method.h"
#include "compiler.h"
#include "dex_file.h"
-#include "dex/verified_method.h"
-#include "driver/compiler_options.h"
#include "invoke_type.h"
#include "method_reference.h"
#include "mirror/class.h" // For mirror::Class::Status.
@@ -39,7 +36,9 @@
#include "runtime.h"
#include "safe_map.h"
#include "thread_pool.h"
+#include "utils/array_ref.h"
#include "utils/dedupe_set.h"
+#include "utils/dex_cache_arrays_layout.h"
#include "utils/swap_space.h"
#include "utils.h"
@@ -54,6 +53,7 @@
} // namespace verifier
class CompiledClass;
+class CompiledMethod;
class CompilerOptions;
class DexCompilationUnit;
class DexFileToMethodInlinerMap;
@@ -62,6 +62,9 @@
class OatWriter;
class ParallelCompilationManager;
class ScopedObjectAccess;
+template <class Allocator> class SrcMap;
+class SrcMapElem;
+using SwapSrcMap = SrcMap<SwapAllocator<SrcMapElem>>;
template<class T> class Handle;
class TimingLogger;
class VerificationResults;
@@ -318,6 +321,10 @@
bool IsMethodsClassInitialized(mirror::Class* referrer_class, mirror::ArtMethod* resolved_method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Get the layout of dex cache arrays for a dex file. Returns invalid layout if the
+ // dex cache arrays don't have a fixed layout.
+ DexCacheArraysLayout GetDexCacheArraysLayout(const DexFile* dex_file);
+
void ProcessedInstanceField(bool resolved);
void ProcessedStaticField(bool resolved, bool local);
void ProcessedInvoke(InvokeType invoke_type, int flags);
diff --git a/compiler/dwarf/debug_frame_opcode_writer.h b/compiler/dwarf/debug_frame_opcode_writer.h
new file mode 100644
index 0000000..cc4ef8f
--- /dev/null
+++ b/compiler/dwarf/debug_frame_opcode_writer.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2015 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_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
+#define ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
+
+#include "dwarf.h"
+#include "register.h"
+#include "writer.h"
+
+namespace art {
+namespace dwarf {
+
+// Writer for .debug_frame opcodes (DWARF-3).
+// See the DWARF specification for the precise meaning of the opcodes.
+// The writer is very light-weight, however it will do the following for you:
+// * Choose the most compact encoding of a given opcode.
+// * Keep track of current state and convert absolute values to deltas.
+// * Divide by header-defined factors as appropriate.
+template<typename Allocator = std::allocator<uint8_t> >
+class DebugFrameOpCodeWriter : private Writer<Allocator> {
+ public:
+ // To save space, DWARF divides most offsets by header-defined factors.
+ // They are used in integer divisions, so we make them constants.
+ // We usually subtract from stack base pointer, so making the factor
+ // negative makes the encoded values positive and thus easier to encode.
+ static constexpr int kDataAlignmentFactor = -4;
+ static constexpr int kCodeAlignmentFactor = 1;
+
+ // Explicitely advance the program counter to given location.
+ void AdvancePC(int absolute_pc) {
+ DCHECK_GE(absolute_pc, current_pc_);
+ int delta = FactorCodeOffset(absolute_pc - current_pc_);
+ if (delta != 0) {
+ if (delta <= 0x3F) {
+ this->PushUint8(DW_CFA_advance_loc | delta);
+ } else if (delta <= UINT8_MAX) {
+ this->PushUint8(DW_CFA_advance_loc1);
+ this->PushUint8(delta);
+ } else if (delta <= UINT16_MAX) {
+ this->PushUint8(DW_CFA_advance_loc2);
+ this->PushUint16(delta);
+ } else {
+ this->PushUint8(DW_CFA_advance_loc4);
+ this->PushUint32(delta);
+ }
+ }
+ current_pc_ = absolute_pc;
+ }
+
+ // Override this method to automatically advance the PC before each opcode.
+ virtual void ImplicitlyAdvancePC() { }
+
+ // Common alias in assemblers - spill relative to current stack pointer.
+ void RelOffset(Reg reg, int offset) {
+ Offset(reg, offset - current_cfa_offset_);
+ }
+
+ // Common alias in assemblers - increase stack frame size.
+ void AdjustCFAOffset(int delta) {
+ DefCFAOffset(current_cfa_offset_ + delta);
+ }
+
+ // Custom alias - spill many registers based on bitmask.
+ void RelOffsetForMany(Reg reg_base, int offset, uint32_t reg_mask,
+ int reg_size) {
+ DCHECK(reg_size == 4 || reg_size == 8);
+ for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
+ if ((reg_mask & 1) != 0u) {
+ RelOffset(Reg(reg_base.num() + i), offset);
+ offset += reg_size;
+ }
+ }
+ }
+
+ // Custom alias - unspill many registers based on bitmask.
+ void RestoreMany(Reg reg_base, uint32_t reg_mask) {
+ for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) {
+ if ((reg_mask & 1) != 0u) {
+ Restore(Reg(reg_base.num() + i));
+ }
+ }
+ }
+
+ void Nop() {
+ this->PushUint8(DW_CFA_nop);
+ }
+
+ void Offset(Reg reg, int offset) {
+ ImplicitlyAdvancePC();
+ int factored_offset = FactorDataOffset(offset); // May change sign.
+ if (factored_offset >= 0) {
+ if (0 <= reg.num() && reg.num() <= 0x3F) {
+ this->PushUint8(DW_CFA_offset | reg.num());
+ this->PushUleb128(factored_offset);
+ } else {
+ this->PushUint8(DW_CFA_offset_extended);
+ this->PushUleb128(reg.num());
+ this->PushUleb128(factored_offset);
+ }
+ } else {
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_CFA_offset_extended_sf);
+ this->PushUleb128(reg.num());
+ this->PushSleb128(factored_offset);
+ }
+ }
+
+ void Restore(Reg reg) {
+ ImplicitlyAdvancePC();
+ if (0 <= reg.num() && reg.num() <= 0x3F) {
+ this->PushUint8(DW_CFA_restore | reg.num());
+ } else {
+ this->PushUint8(DW_CFA_restore_extended);
+ this->PushUleb128(reg.num());
+ }
+ }
+
+ void Undefined(Reg reg) {
+ ImplicitlyAdvancePC();
+ this->PushUint8(DW_CFA_undefined);
+ this->PushUleb128(reg.num());
+ }
+
+ void SameValue(Reg reg) {
+ ImplicitlyAdvancePC();
+ this->PushUint8(DW_CFA_same_value);
+ this->PushUleb128(reg.num());
+ }
+
+ // The previous value of "reg" is stored in register "new_reg".
+ void Register(Reg reg, Reg new_reg) {
+ ImplicitlyAdvancePC();
+ this->PushUint8(DW_CFA_register);
+ this->PushUleb128(reg.num());
+ this->PushUleb128(new_reg.num());
+ }
+
+ void RememberState() {
+ // Note that we do not need to advance the PC.
+ this->PushUint8(DW_CFA_remember_state);
+ }
+
+ void RestoreState() {
+ ImplicitlyAdvancePC();
+ this->PushUint8(DW_CFA_restore_state);
+ }
+
+ void DefCFA(Reg reg, int offset) {
+ ImplicitlyAdvancePC();
+ if (offset >= 0) {
+ this->PushUint8(DW_CFA_def_cfa);
+ this->PushUleb128(reg.num());
+ this->PushUleb128(offset); // Non-factored.
+ } else {
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_CFA_def_cfa_sf);
+ this->PushUleb128(reg.num());
+ this->PushSleb128(FactorDataOffset(offset));
+ }
+ current_cfa_offset_ = offset;
+ }
+
+ void DefCFARegister(Reg reg) {
+ ImplicitlyAdvancePC();
+ this->PushUint8(DW_CFA_def_cfa_register);
+ this->PushUleb128(reg.num());
+ }
+
+ void DefCFAOffset(int offset) {
+ if (current_cfa_offset_ != offset) {
+ ImplicitlyAdvancePC();
+ if (offset >= 0) {
+ this->PushUint8(DW_CFA_def_cfa_offset);
+ this->PushUleb128(offset); // Non-factored.
+ } else {
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_CFA_def_cfa_offset_sf);
+ this->PushSleb128(FactorDataOffset(offset));
+ }
+ current_cfa_offset_ = offset;
+ }
+ }
+
+ void ValOffset(Reg reg, int offset) {
+ ImplicitlyAdvancePC();
+ uses_dwarf3_features_ = true;
+ int factored_offset = FactorDataOffset(offset); // May change sign.
+ if (factored_offset >= 0) {
+ this->PushUint8(DW_CFA_val_offset);
+ this->PushUleb128(reg.num());
+ this->PushUleb128(factored_offset);
+ } else {
+ this->PushUint8(DW_CFA_val_offset_sf);
+ this->PushUleb128(reg.num());
+ this->PushSleb128(factored_offset);
+ }
+ }
+
+ void DefCFAExpression(void* expr, int expr_size) {
+ ImplicitlyAdvancePC();
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_CFA_def_cfa_expression);
+ this->PushUleb128(expr_size);
+ this->PushData(expr, expr_size);
+ }
+
+ void Expression(Reg reg, void* expr, int expr_size) {
+ ImplicitlyAdvancePC();
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_CFA_expression);
+ this->PushUleb128(reg.num());
+ this->PushUleb128(expr_size);
+ this->PushData(expr, expr_size);
+ }
+
+ void ValExpression(Reg reg, void* expr, int expr_size) {
+ ImplicitlyAdvancePC();
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_CFA_val_expression);
+ this->PushUleb128(reg.num());
+ this->PushUleb128(expr_size);
+ this->PushData(expr, expr_size);
+ }
+
+ int GetCurrentCFAOffset() const {
+ return current_cfa_offset_;
+ }
+
+ void SetCurrentCFAOffset(int offset) {
+ current_cfa_offset_ = offset;
+ }
+
+ using Writer<Allocator>::data;
+
+ DebugFrameOpCodeWriter(const Allocator& alloc = Allocator())
+ : Writer<Allocator>(&opcodes_),
+ opcodes_(alloc),
+ current_cfa_offset_(0),
+ current_pc_(0),
+ uses_dwarf3_features_(false) {
+ }
+
+ virtual ~DebugFrameOpCodeWriter() { }
+
+ protected:
+ int FactorDataOffset(int offset) const {
+ DCHECK_EQ(offset % kDataAlignmentFactor, 0);
+ return offset / kDataAlignmentFactor;
+ }
+
+ int FactorCodeOffset(int offset) const {
+ DCHECK_EQ(offset % kCodeAlignmentFactor, 0);
+ return offset / kCodeAlignmentFactor;
+ }
+
+ std::vector<uint8_t, Allocator> opcodes_;
+ int current_cfa_offset_;
+ int current_pc_;
+ bool uses_dwarf3_features_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DebugFrameOpCodeWriter);
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
diff --git a/compiler/dwarf/debug_frame_writer.h b/compiler/dwarf/debug_frame_writer.h
new file mode 100644
index 0000000..6de45f5
--- /dev/null
+++ b/compiler/dwarf/debug_frame_writer.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 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_DWARF_DEBUG_FRAME_WRITER_H_
+#define ART_COMPILER_DWARF_DEBUG_FRAME_WRITER_H_
+
+#include "debug_frame_opcode_writer.h"
+#include "dwarf.h"
+#include "writer.h"
+
+namespace art {
+namespace dwarf {
+
+// Writer for the .eh_frame section (which extends .debug_frame specification).
+template<typename Allocator = std::allocator<uint8_t>>
+class DebugFrameWriter FINAL : private Writer<Allocator> {
+ public:
+ void WriteCIE(Reg return_address_register,
+ const uint8_t* initial_opcodes,
+ int initial_opcodes_size) {
+ DCHECK(cie_header_start_ == ~0u);
+ cie_header_start_ = this->data()->size();
+ this->PushUint32(0); // Length placeholder.
+ this->PushUint32(0); // CIE id.
+ this->PushUint8(1); // Version.
+ this->PushString("zR");
+ this->PushUleb128(DebugFrameOpCodeWriter<Allocator>::kCodeAlignmentFactor);
+ this->PushSleb128(DebugFrameOpCodeWriter<Allocator>::kDataAlignmentFactor);
+ this->PushUleb128(return_address_register.num()); // ubyte in DWARF2.
+ this->PushUleb128(1); // z: Augmentation data size.
+ if (use_64bit_address_) {
+ this->PushUint8(0x04); // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata8).
+ } else {
+ this->PushUint8(0x03); // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4).
+ }
+ this->PushData(initial_opcodes, initial_opcodes_size);
+ this->Pad(use_64bit_address_ ? 8 : 4);
+ this->UpdateUint32(cie_header_start_, this->data()->size() - cie_header_start_ - 4);
+ }
+
+ void WriteCIE(Reg return_address_register,
+ const DebugFrameOpCodeWriter<Allocator>& opcodes) {
+ WriteCIE(return_address_register, opcodes.data()->data(), opcodes.data()->size());
+ }
+
+ void WriteFDE(uint64_t initial_address,
+ uint64_t address_range,
+ const uint8_t* unwind_opcodes,
+ int unwind_opcodes_size) {
+ DCHECK(cie_header_start_ != ~0u);
+ size_t fde_header_start = this->data()->size();
+ this->PushUint32(0); // Length placeholder.
+ this->PushUint32(this->data()->size() - cie_header_start_); // 'CIE_pointer'
+ if (use_64bit_address_) {
+ this->PushUint64(initial_address);
+ this->PushUint64(address_range);
+ } else {
+ this->PushUint32(initial_address);
+ this->PushUint32(address_range);
+ }
+ this->PushUleb128(0); // Augmentation data size.
+ this->PushData(unwind_opcodes, unwind_opcodes_size);
+ this->Pad(use_64bit_address_ ? 8 : 4);
+ this->UpdateUint32(fde_header_start, this->data()->size() - fde_header_start - 4);
+ }
+
+ DebugFrameWriter(std::vector<uint8_t, Allocator>* buffer, bool use_64bit_address)
+ : Writer<Allocator>(buffer),
+ use_64bit_address_(use_64bit_address),
+ cie_header_start_(~0u) {
+ }
+
+ private:
+ bool use_64bit_address_;
+ size_t cie_header_start_;
+
+ DISALLOW_COPY_AND_ASSIGN(DebugFrameWriter);
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DWARF_DEBUG_FRAME_WRITER_H_
diff --git a/compiler/dwarf/debug_line_opcode_writer.h b/compiler/dwarf/debug_line_opcode_writer.h
new file mode 100644
index 0000000..f34acee
--- /dev/null
+++ b/compiler/dwarf/debug_line_opcode_writer.h
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2015 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_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
+#define ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
+
+#include "dwarf.h"
+#include "writer.h"
+
+namespace art {
+namespace dwarf {
+
+// Writer for the .debug_line opcodes (DWARF-3).
+// The writer is very light-weight, however it will do the following for you:
+// * Choose the most compact encoding of a given opcode.
+// * Keep track of current state and convert absolute values to deltas.
+// * Divide by header-defined factors as appropriate.
+template<typename Allocator = std::allocator<uint8_t>>
+class DebugLineOpCodeWriter FINAL : private Writer<Allocator> {
+ public:
+ static constexpr int kOpcodeBase = 13;
+ static constexpr bool kDefaultIsStmt = true;
+ static constexpr int kLineBase = -5;
+ static constexpr int kLineRange = 14;
+
+ void AddRow() {
+ this->PushUint8(DW_LNS_copy);
+ }
+
+ void AdvancePC(uint64_t absolute_address) {
+ DCHECK_NE(current_address_, 0u); // Use SetAddress for the first advance.
+ DCHECK_GE(absolute_address, current_address_);
+ if (absolute_address != current_address_) {
+ uint64_t delta = FactorCodeOffset(absolute_address - current_address_);
+ if (delta <= INT32_MAX) {
+ this->PushUint8(DW_LNS_advance_pc);
+ this->PushUleb128(static_cast<int>(delta));
+ current_address_ = absolute_address;
+ } else {
+ SetAddress(absolute_address);
+ }
+ }
+ }
+
+ void AdvanceLine(int absolute_line) {
+ int delta = absolute_line - current_line_;
+ if (delta != 0) {
+ this->PushUint8(DW_LNS_advance_line);
+ this->PushSleb128(delta);
+ current_line_ = absolute_line;
+ }
+ }
+
+ void SetFile(int file) {
+ if (current_file_ != file) {
+ this->PushUint8(DW_LNS_set_file);
+ this->PushUleb128(file);
+ current_file_ = file;
+ }
+ }
+
+ void SetColumn(int column) {
+ this->PushUint8(DW_LNS_set_column);
+ this->PushUleb128(column);
+ }
+
+ void NegateStmt() {
+ this->PushUint8(DW_LNS_negate_stmt);
+ }
+
+ void SetBasicBlock() {
+ this->PushUint8(DW_LNS_set_basic_block);
+ }
+
+ void SetPrologueEnd() {
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_LNS_set_prologue_end);
+ }
+
+ void SetEpilogueBegin() {
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_LNS_set_epilogue_begin);
+ }
+
+ void SetISA(int isa) {
+ uses_dwarf3_features_ = true;
+ this->PushUint8(DW_LNS_set_isa);
+ this->PushUleb128(isa);
+ }
+
+ void EndSequence() {
+ this->PushUint8(0);
+ this->PushUleb128(1);
+ this->PushUint8(DW_LNE_end_sequence);
+ current_address_ = 0;
+ current_file_ = 1;
+ current_line_ = 1;
+ }
+
+ // Uncoditionally set address using the long encoding.
+ // This gives the linker opportunity to relocate the address.
+ void SetAddress(uint64_t absolute_address) {
+ DCHECK_GE(absolute_address, current_address_);
+ FactorCodeOffset(absolute_address); // Check if it is factorable.
+ this->PushUint8(0);
+ if (use_64bit_address_) {
+ this->PushUleb128(1 + 8);
+ this->PushUint8(DW_LNE_set_address);
+ this->PushUint64(absolute_address);
+ } else {
+ this->PushUleb128(1 + 4);
+ this->PushUint8(DW_LNE_set_address);
+ this->PushUint32(absolute_address);
+ }
+ current_address_ = absolute_address;
+ }
+
+ void DefineFile(const char* filename,
+ int directory_index,
+ int modification_time,
+ int file_size) {
+ int size = 1 +
+ strlen(filename) + 1 +
+ UnsignedLeb128Size(directory_index) +
+ UnsignedLeb128Size(modification_time) +
+ UnsignedLeb128Size(file_size);
+ this->PushUint8(0);
+ this->PushUleb128(size);
+ size_t start = data()->size();
+ this->PushUint8(DW_LNE_define_file);
+ this->PushString(filename);
+ this->PushUleb128(directory_index);
+ this->PushUleb128(modification_time);
+ this->PushUleb128(file_size);
+ DCHECK_EQ(start + size, data()->size());
+ }
+
+ // Compact address and line opcode.
+ void AddRow(uint64_t absolute_address, int absolute_line) {
+ DCHECK_GE(absolute_address, current_address_);
+
+ // If the address is definitely too far, use the long encoding.
+ uint64_t delta_address = FactorCodeOffset(absolute_address - current_address_);
+ if (delta_address > UINT8_MAX) {
+ AdvancePC(absolute_address);
+ delta_address = 0;
+ }
+
+ // If the line is definitely too far, use the long encoding.
+ int delta_line = absolute_line - current_line_;
+ if (!(kLineBase <= delta_line && delta_line < kLineBase + kLineRange)) {
+ AdvanceLine(absolute_line);
+ delta_line = 0;
+ }
+
+ // Both address and line should be reasonable now. Use the short encoding.
+ int opcode = kOpcodeBase + (delta_line - kLineBase) +
+ (static_cast<int>(delta_address) * kLineRange);
+ if (opcode > UINT8_MAX) {
+ // If the address is still too far, try to increment it by const amount.
+ int const_advance = (0xFF - kOpcodeBase) / kLineRange;
+ opcode -= (kLineRange * const_advance);
+ if (opcode <= UINT8_MAX) {
+ this->PushUint8(DW_LNS_const_add_pc);
+ } else {
+ // Give up and use long encoding for address.
+ AdvancePC(absolute_address);
+ // Still use the opcode to do line advance and copy.
+ opcode = kOpcodeBase + (delta_line - kLineBase);
+ }
+ }
+ DCHECK(kOpcodeBase <= opcode && opcode <= 0xFF);
+ this->PushUint8(opcode); // Special opcode.
+ current_line_ = absolute_line;
+ current_address_ = absolute_address;
+ }
+
+ int GetCodeFactorBits() const {
+ return code_factor_bits_;
+ }
+
+ uint64_t CurrentAddress() const {
+ return current_address_;
+ }
+
+ int CurrentFile() const {
+ return current_file_;
+ }
+
+ int CurrentLine() const {
+ return current_line_;
+ }
+
+ using Writer<Allocator>::data;
+
+ DebugLineOpCodeWriter(bool use64bitAddress,
+ int codeFactorBits,
+ const Allocator& alloc = Allocator())
+ : Writer<Allocator>(&opcodes_),
+ opcodes_(alloc),
+ uses_dwarf3_features_(false),
+ use_64bit_address_(use64bitAddress),
+ code_factor_bits_(codeFactorBits),
+ current_address_(0),
+ current_file_(1),
+ current_line_(1) {
+ }
+
+ private:
+ uint64_t FactorCodeOffset(uint64_t offset) const {
+ DCHECK_GE(code_factor_bits_, 0);
+ DCHECK_EQ((offset >> code_factor_bits_) << code_factor_bits_, offset);
+ return offset >> code_factor_bits_;
+ }
+
+ std::vector<uint8_t, Allocator> opcodes_;
+ bool uses_dwarf3_features_;
+ bool use_64bit_address_;
+ int code_factor_bits_;
+ uint64_t current_address_;
+ int current_file_;
+ int current_line_;
+
+ DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter);
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
diff --git a/compiler/dwarf/debug_line_writer.h b/compiler/dwarf/debug_line_writer.h
new file mode 100644
index 0000000..4b7d8d9
--- /dev/null
+++ b/compiler/dwarf/debug_line_writer.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 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_DWARF_DEBUG_LINE_WRITER_H_
+#define ART_COMPILER_DWARF_DEBUG_LINE_WRITER_H_
+
+#include "debug_line_opcode_writer.h"
+#include "dwarf.h"
+#include "writer.h"
+#include <string>
+
+namespace art {
+namespace dwarf {
+
+// Writer for the .debug_line section (DWARF-3).
+template<typename Allocator = std::allocator<uint8_t>>
+class DebugLineWriter FINAL : private Writer<Allocator> {
+ public:
+ struct FileEntry {
+ std::string file_name;
+ int directory_index;
+ int modification_time;
+ int file_size;
+ };
+
+ void WriteTable(const std::vector<std::string>& include_directories,
+ const std::vector<FileEntry>& files,
+ const DebugLineOpCodeWriter<Allocator>& opcodes) {
+ size_t header_start = this->data()->size();
+ this->PushUint32(0); // Section-length placeholder.
+ // Claim DWARF-2 version even though we use some DWARF-3 features.
+ // DWARF-2 consumers will ignore the unknown opcodes.
+ // This is what clang currently does.
+ this->PushUint16(2); // .debug_line version.
+ size_t header_length_pos = this->data()->size();
+ this->PushUint32(0); // Header-length placeholder.
+ this->PushUint8(1 << opcodes.GetCodeFactorBits());
+ this->PushUint8(DebugLineOpCodeWriter<Allocator>::kDefaultIsStmt ? 1 : 0);
+ this->PushInt8(DebugLineOpCodeWriter<Allocator>::kLineBase);
+ this->PushUint8(DebugLineOpCodeWriter<Allocator>::kLineRange);
+ this->PushUint8(DebugLineOpCodeWriter<Allocator>::kOpcodeBase);
+ static const int opcode_lengths[DebugLineOpCodeWriter<Allocator>::kOpcodeBase] = {
+ 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 };
+ for (int i = 1; i < DebugLineOpCodeWriter<Allocator>::kOpcodeBase; i++) {
+ this->PushUint8(opcode_lengths[i]);
+ }
+ for (const std::string& directory : include_directories) {
+ this->PushData(directory.data(), directory.size() + 1);
+ }
+ this->PushUint8(0); // Terminate include_directories list.
+ for (const FileEntry& file : files) {
+ this->PushData(file.file_name.data(), file.file_name.size() + 1);
+ this->PushUleb128(file.directory_index);
+ this->PushUleb128(file.modification_time);
+ this->PushUleb128(file.file_size);
+ }
+ this->PushUint8(0); // Terminate file list.
+ this->UpdateUint32(header_length_pos, this->data()->size() - header_length_pos - 4);
+ this->PushData(opcodes.data()->data(), opcodes.data()->size());
+ this->UpdateUint32(header_start, this->data()->size() - header_start - 4);
+ }
+
+ explicit DebugLineWriter(std::vector<uint8_t, Allocator>* buffer)
+ : Writer<Allocator>(buffer) {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DebugLineWriter);
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DWARF_DEBUG_LINE_WRITER_H_
diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc
new file mode 100644
index 0000000..f3553bc
--- /dev/null
+++ b/compiler/dwarf/dwarf_test.cc
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2015 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 "dwarf_test.h"
+
+#include "dwarf/debug_frame_opcode_writer.h"
+#include "dwarf/debug_frame_writer.h"
+#include "dwarf/debug_line_opcode_writer.h"
+#include "dwarf/debug_line_writer.h"
+#include "gtest/gtest.h"
+
+namespace art {
+namespace dwarf {
+
+// Run the tests only on host since we need objdump.
+#ifndef HAVE_ANDROID_OS
+
+TEST_F(DwarfTest, DebugFrame) {
+ const bool is64bit = false;
+
+ // Pick offset value which would catch Uleb vs Sleb errors.
+ const int offset = 40000;
+ ASSERT_EQ(UnsignedLeb128Size(offset / 4), 2u);
+ ASSERT_EQ(SignedLeb128Size(offset / 4), 3u);
+ DW_CHECK("Data alignment factor: -4");
+ const Reg reg(6);
+
+ // Test the opcodes in the order mentioned in the spec.
+ // There are usually several encoding variations of each opcode.
+ DebugFrameOpCodeWriter<> opcodes;
+ DW_CHECK("FDE");
+ int pc = 0;
+ for (int i : {0, 1, 0x3F, 0x40, 0xFF, 0x100, 0xFFFF, 0x10000}) {
+ pc += i;
+ opcodes.AdvancePC(pc);
+ }
+ DW_CHECK_NEXT("DW_CFA_advance_loc: 1 to 01000001");
+ DW_CHECK_NEXT("DW_CFA_advance_loc: 63 to 01000040");
+ DW_CHECK_NEXT("DW_CFA_advance_loc1: 64 to 01000080");
+ DW_CHECK_NEXT("DW_CFA_advance_loc1: 255 to 0100017f");
+ DW_CHECK_NEXT("DW_CFA_advance_loc2: 256 to 0100027f");
+ DW_CHECK_NEXT("DW_CFA_advance_loc2: 65535 to 0101027e");
+ DW_CHECK_NEXT("DW_CFA_advance_loc4: 65536 to 0102027e");
+ opcodes.DefCFA(reg, offset);
+ DW_CHECK_NEXT("DW_CFA_def_cfa: r6 (esi) ofs 40000");
+ opcodes.DefCFA(reg, -offset);
+ DW_CHECK_NEXT("DW_CFA_def_cfa_sf: r6 (esi) ofs -40000");
+ opcodes.DefCFARegister(reg);
+ DW_CHECK_NEXT("DW_CFA_def_cfa_register: r6 (esi)");
+ opcodes.DefCFAOffset(offset);
+ DW_CHECK_NEXT("DW_CFA_def_cfa_offset: 40000");
+ opcodes.DefCFAOffset(-offset);
+ DW_CHECK_NEXT("DW_CFA_def_cfa_offset_sf: -40000");
+ uint8_t expr[] = { 0 };
+ opcodes.DefCFAExpression(expr, arraysize(expr));
+ DW_CHECK_NEXT("DW_CFA_def_cfa_expression");
+ opcodes.Undefined(reg);
+ DW_CHECK_NEXT("DW_CFA_undefined: r6 (esi)");
+ opcodes.SameValue(reg);
+ DW_CHECK_NEXT("DW_CFA_same_value: r6 (esi)");
+ opcodes.Offset(Reg(0x3F), -offset);
+ // Bad register likely means that it does not exist on x86,
+ // but we want to test high register numbers anyway.
+ DW_CHECK_NEXT("DW_CFA_offset: bad register: r63 at cfa-40000");
+ opcodes.Offset(Reg(0x40), -offset);
+ DW_CHECK_NEXT("DW_CFA_offset_extended: bad register: r64 at cfa-40000");
+ opcodes.Offset(Reg(0x40), offset);
+ DW_CHECK_NEXT("DW_CFA_offset_extended_sf: bad register: r64 at cfa+40000");
+ opcodes.ValOffset(reg, -offset);
+ DW_CHECK_NEXT("DW_CFA_val_offset: r6 (esi) at cfa-40000");
+ opcodes.ValOffset(reg, offset);
+ DW_CHECK_NEXT("DW_CFA_val_offset_sf: r6 (esi) at cfa+40000");
+ opcodes.Register(reg, Reg(1));
+ DW_CHECK_NEXT("DW_CFA_register: r6 (esi) in r1 (ecx)");
+ opcodes.Expression(reg, expr, arraysize(expr));
+ DW_CHECK_NEXT("DW_CFA_expression: r6 (esi)");
+ opcodes.ValExpression(reg, expr, arraysize(expr));
+ DW_CHECK_NEXT("DW_CFA_val_expression: r6 (esi)");
+ opcodes.Restore(Reg(0x3F));
+ DW_CHECK_NEXT("DW_CFA_restore: bad register: r63");
+ opcodes.Restore(Reg(0x40));
+ DW_CHECK_NEXT("DW_CFA_restore_extended: bad register: r64");
+ opcodes.Restore(reg);
+ DW_CHECK_NEXT("DW_CFA_restore: r6 (esi)");
+ opcodes.RememberState();
+ DW_CHECK_NEXT("DW_CFA_remember_state");
+ opcodes.RestoreState();
+ DW_CHECK_NEXT("DW_CFA_restore_state");
+ opcodes.Nop();
+ DW_CHECK_NEXT("DW_CFA_nop");
+
+ // Also test helpers.
+ opcodes.DefCFA(Reg(4), 100); // ESP
+ DW_CHECK_NEXT("DW_CFA_def_cfa: r4 (esp) ofs 100");
+ opcodes.AdjustCFAOffset(8);
+ DW_CHECK_NEXT("DW_CFA_def_cfa_offset: 108");
+ opcodes.RelOffset(Reg(0), 0); // push R0
+ DW_CHECK_NEXT("DW_CFA_offset: r0 (eax) at cfa-108");
+ opcodes.RelOffset(Reg(1), 4); // push R1
+ DW_CHECK_NEXT("DW_CFA_offset: r1 (ecx) at cfa-104");
+ opcodes.RelOffsetForMany(Reg(2), 8, 1 | (1 << 3), 4); // push R2 and R5
+ DW_CHECK_NEXT("DW_CFA_offset: r2 (edx) at cfa-100");
+ DW_CHECK_NEXT("DW_CFA_offset: r5 (ebp) at cfa-96");
+ opcodes.RestoreMany(Reg(2), 1 | (1 << 3)); // pop R2 and R5
+ DW_CHECK_NEXT("DW_CFA_restore: r2 (edx)");
+ DW_CHECK_NEXT("DW_CFA_restore: r5 (ebp)");
+
+ DebugFrameWriter<> eh_frame(&eh_frame_data_, is64bit);
+ DebugFrameOpCodeWriter<> initial_opcodes;
+ eh_frame.WriteCIE(Reg(is64bit ? 16 : 8), // Return address register.
+ initial_opcodes); // Initial opcodes.
+ eh_frame.WriteFDE(0x01000000, 0x01000000,
+ opcodes.data()->data(), opcodes.data()->size());
+ CheckObjdumpOutput(is64bit, "-W");
+}
+
+TEST_F(DwarfTest, DebugFrame64) {
+ const bool is64bit = true;
+ DebugFrameWriter<> eh_frame(&eh_frame_data_, is64bit);
+ DebugFrameOpCodeWriter<> no_opcodes;
+ eh_frame.WriteCIE(Reg(16), no_opcodes);
+ eh_frame.WriteFDE(0x0100000000000000, 0x0200000000000000,
+ no_opcodes.data()->data(), no_opcodes.data()->size());
+ DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000");
+ CheckObjdumpOutput(is64bit, "-W");
+}
+
+TEST_F(DwarfTest, DebugLine) {
+ const bool is64bit = false;
+ const int code_factor_bits = 1;
+ DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits);
+
+ std::vector<std::string> include_directories;
+ include_directories.push_back("/path/to/source");
+ DW_CHECK("/path/to/source");
+
+ std::vector<DebugLineWriter<>::FileEntry> files {
+ { "file0.c", 0, 1000, 2000 },
+ { "file1.c", 1, 1000, 2000 },
+ { "file2.c", 1, 1000, 2000 },
+ };
+ DW_CHECK("1\t0\t1000\t2000\tfile0.c");
+ DW_CHECK_NEXT("2\t1\t1000\t2000\tfile1.c");
+ DW_CHECK_NEXT("3\t1\t1000\t2000\tfile2.c");
+
+ DW_CHECK("Line Number Statements");
+ opcodes.SetAddress(0x01000000);
+ DW_CHECK_NEXT("Extended opcode 2: set Address to 0x1000000");
+ opcodes.AddRow();
+ DW_CHECK_NEXT("Copy");
+ opcodes.AdvancePC(0x01000100);
+ DW_CHECK_NEXT("Advance PC by 256 to 0x1000100");
+ opcodes.SetFile(2);
+ DW_CHECK_NEXT("Set File Name to entry 2 in the File Name Table");
+ opcodes.AdvanceLine(3);
+ DW_CHECK_NEXT("Advance Line by 2 to 3");
+ opcodes.SetColumn(4);
+ DW_CHECK_NEXT("Set column to 4");
+ opcodes.NegateStmt();
+ DW_CHECK_NEXT("Set is_stmt to 0");
+ opcodes.SetBasicBlock();
+ DW_CHECK_NEXT("Set basic block");
+ opcodes.SetPrologueEnd();
+ DW_CHECK_NEXT("Set prologue_end to true");
+ opcodes.SetEpilogueBegin();
+ DW_CHECK_NEXT("Set epilogue_begin to true");
+ opcodes.SetISA(5);
+ DW_CHECK_NEXT("Set ISA to 5");
+ opcodes.EndSequence();
+ DW_CHECK_NEXT("Extended opcode 1: End of Sequence");
+ opcodes.DefineFile("file.c", 0, 1000, 2000);
+ DW_CHECK_NEXT("Extended opcode 3: define new File Table entry");
+ DW_CHECK_NEXT("Entry\tDir\tTime\tSize\tName");
+ DW_CHECK_NEXT("1\t0\t1000\t2000\tfile.c");
+
+ DebugLineWriter<> debug_line(&debug_line_data_);
+ debug_line.WriteTable(include_directories, files, opcodes);
+ CheckObjdumpOutput(is64bit, "-W");
+}
+
+// DWARF has special one byte codes which advance PC and line at the same time.
+TEST_F(DwarfTest, DebugLineSpecialOpcodes) {
+ const bool is64bit = false;
+ const int code_factor_bits = 1;
+ uint32_t pc = 0x01000000;
+ int line = 1;
+ DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits);
+ opcodes.SetAddress(pc);
+ size_t num_rows = 0;
+ DW_CHECK("Line Number Statements:");
+ DW_CHECK("Special opcode");
+ DW_CHECK("Advance PC by constant");
+ DW_CHECK("Decoded dump of debug contents of section .debug_line:");
+ DW_CHECK("Line number Starting address");
+ for (int addr_delta = 0; addr_delta < 80; addr_delta += 2) {
+ for (int line_delta = 16; line_delta >= -16; --line_delta) {
+ pc += addr_delta;
+ line += line_delta;
+ opcodes.AddRow(pc, line);
+ num_rows++;
+ ASSERT_EQ(opcodes.CurrentAddress(), pc);
+ ASSERT_EQ(opcodes.CurrentLine(), line);
+ char expected[1024];
+ sprintf(expected, "%i 0x%x", line, pc);
+ DW_CHECK_NEXT(expected);
+ }
+ }
+ EXPECT_LT(opcodes.data()->size(), num_rows * 3);
+
+ std::vector<std::string> directories;
+ std::vector<DebugLineWriter<>::FileEntry> files {
+ { "file.c", 0, 1000, 2000 },
+ };
+ DebugLineWriter<> debug_line(&debug_line_data_);
+ debug_line.WriteTable(directories, files, opcodes);
+ CheckObjdumpOutput(is64bit, "-W -WL");
+}
+
+#endif // HAVE_ANDROID_OS
+
+} // namespace dwarf
+} // namespace art
diff --git a/compiler/dwarf/dwarf_test.h b/compiler/dwarf/dwarf_test.h
new file mode 100644
index 0000000..dd5e0c2
--- /dev/null
+++ b/compiler/dwarf/dwarf_test.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2015 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_DWARF_DWARF_TEST_H_
+#define ART_COMPILER_DWARF_DWARF_TEST_H_
+
+#include <cstring>
+#include <dirent.h>
+#include <memory>
+#include <set>
+#include <stdio.h>
+#include <string>
+#include <sys/types.h>
+
+#include "utils.h"
+#include "base/unix_file/fd_file.h"
+#include "common_runtime_test.h"
+#include "elf_builder.h"
+#include "gtest/gtest.h"
+#include "os.h"
+
+namespace art {
+namespace dwarf {
+
+#define DW_CHECK(substring) Check(substring, false, __FILE__, __LINE__)
+#define DW_CHECK_NEXT(substring) Check(substring, true, __FILE__, __LINE__)
+
+class DwarfTest : public CommonRuntimeTest {
+ public:
+ static constexpr bool kPrintObjdumpOutput = false; // debugging.
+
+ struct ExpectedLine {
+ std::string substring;
+ bool next;
+ const char* at_file;
+ int at_line;
+ };
+
+ // Check that the objdump output contains given output.
+ // If next is true, it must be the next line. Otherwise lines are skipped.
+ void Check(const char* substr, bool next, const char* at_file, int at_line) {
+ expected_lines_.push_back(ExpectedLine {substr, next, at_file, at_line});
+ }
+
+ static std::string GetObjdumpPath() {
+ const char* android_build_top = getenv("ANDROID_BUILD_TOP");
+ if (android_build_top != nullptr) {
+ std::string host_prebuilts = std::string(android_build_top) +
+ "/prebuilts/gcc/linux-x86/host/";
+ // Read the content of the directory.
+ std::set<std::string> entries;
+ DIR* dir = opendir(host_prebuilts.c_str());
+ if (dir != nullptr) {
+ struct dirent* entry;
+ while ((entry = readdir(dir)) != nullptr) {
+ if (strstr(entry->d_name, "linux-glibc")) {
+ entries.insert(host_prebuilts + entry->d_name);
+ }
+ }
+ closedir(dir);
+ }
+ // Strings are sorted so the last one should be the most recent version.
+ if (!entries.empty()) {
+ std::string path = *entries.rbegin() + "/x86_64-linux/bin/objdump";
+ struct stat st;
+ if (stat(path.c_str(), &st) == 0) {
+ return path; // File exists.
+ }
+ }
+ }
+ ADD_FAILURE() << "Can not find prebuild objdump.";
+ return "objdump"; // Use the system objdump as fallback.
+ }
+
+ // Pretty-print the generated DWARF data using objdump.
+ template<typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Dyn,
+ typename Elf_Sym, typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr>
+ std::vector<std::string> Objdump(bool is64bit, const char* args) {
+ // Write simple elf file with just the DWARF sections.
+ class NoCode : public CodeOutput {
+ virtual void SetCodeOffset(size_t) { }
+ virtual bool Write(OutputStream*) { return true; }
+ } code;
+ ScratchFile file;
+ InstructionSet isa = is64bit ? kX86_64 : kX86;
+ ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr> builder(
+ &code, file.GetFile(), isa, 0, 0, 0, 0, 0, 0, false, false);
+ typedef ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> Section;
+ if (!debug_info_data_.empty()) {
+ Section debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
+ debug_info.SetBuffer(debug_info_data_);
+ builder.RegisterRawSection(debug_info);
+ }
+ if (!debug_abbrev_data_.empty()) {
+ Section debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
+ debug_abbrev.SetBuffer(debug_abbrev_data_);
+ builder.RegisterRawSection(debug_abbrev);
+ }
+ if (!debug_str_data_.empty()) {
+ Section debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
+ debug_str.SetBuffer(debug_str_data_);
+ builder.RegisterRawSection(debug_str);
+ }
+ if (!debug_line_data_.empty()) {
+ Section debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
+ debug_line.SetBuffer(debug_line_data_);
+ builder.RegisterRawSection(debug_line);
+ }
+ if (!eh_frame_data_.empty()) {
+ Section eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0);
+ eh_frame.SetBuffer(eh_frame_data_);
+ builder.RegisterRawSection(eh_frame);
+ }
+ builder.Init();
+ builder.Write();
+
+ // Read the elf file back using objdump.
+ std::vector<std::string> lines;
+ std::string cmd = GetObjdumpPath();
+ cmd = cmd + " " + args + " " + file.GetFilename() + " 2>&1";
+ FILE* output = popen(cmd.data(), "r");
+ char buffer[1024];
+ const char* line;
+ while ((line = fgets(buffer, sizeof(buffer), output)) != nullptr) {
+ if (kPrintObjdumpOutput) {
+ printf("%s", line);
+ }
+ if (line[0] != '\0' && line[0] != '\n') {
+ EXPECT_TRUE(strstr(line, "objdump: Error:") == nullptr) << line;
+ EXPECT_TRUE(strstr(line, "objdump: Warning:") == nullptr) << line;
+ std::string str(line);
+ if (str.back() == '\n') {
+ str.pop_back();
+ }
+ lines.push_back(str);
+ }
+ }
+ pclose(output);
+ return lines;
+ }
+
+ std::vector<std::string> Objdump(bool is64bit, const char* args) {
+ if (is64bit) {
+ return Objdump<Elf64_Word, Elf64_Sword, Elf64_Addr, Elf64_Dyn,
+ Elf64_Sym, Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(is64bit, args);
+ } else {
+ return Objdump<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+ Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(is64bit, args);
+ }
+ }
+
+ // Compare objdump output to the recorded checks.
+ void CheckObjdumpOutput(bool is64bit, const char* args) {
+ std::vector<std::string> actual_lines = Objdump(is64bit, args);
+ auto actual_line = actual_lines.begin();
+ for (const ExpectedLine& expected_line : expected_lines_) {
+ const std::string& substring = expected_line.substring;
+ if (actual_line == actual_lines.end()) {
+ ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) <<
+ "Expected '" << substring << "'.\n" <<
+ "Seen end of output.";
+ } else if (expected_line.next) {
+ if (actual_line->find(substring) == std::string::npos) {
+ ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) <<
+ "Expected '" << substring << "'.\n" <<
+ "Seen '" << actual_line->data() << "'.";
+ } else {
+ // printf("Found '%s' in '%s'.\n", substring.data(), actual_line->data());
+ }
+ actual_line++;
+ } else {
+ bool found = false;
+ for (auto it = actual_line; it < actual_lines.end(); it++) {
+ if (it->find(substring) != std::string::npos) {
+ actual_line = it;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) <<
+ "Expected '" << substring << "'.\n" <<
+ "Not found anywhere in the rest of the output.";
+ } else {
+ // printf("Found '%s' in '%s'.\n", substring.data(), actual_line->data());
+ actual_line++;
+ }
+ }
+ }
+ }
+
+ // Buffers which are going to assembled into ELF file and passed to objdump.
+ std::vector<uint8_t> eh_frame_data_;
+ std::vector<uint8_t> debug_info_data_;
+ std::vector<uint8_t> debug_abbrev_data_;
+ std::vector<uint8_t> debug_str_data_;
+ std::vector<uint8_t> debug_line_data_;
+
+ // The expected output of objdump.
+ std::vector<ExpectedLine> expected_lines_;
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DWARF_DWARF_TEST_H_
diff --git a/compiler/dwarf/register.h b/compiler/dwarf/register.h
new file mode 100644
index 0000000..fa666df
--- /dev/null
+++ b/compiler/dwarf/register.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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_DWARF_REGISTER_H_
+#define ART_COMPILER_DWARF_REGISTER_H_
+
+namespace art {
+namespace dwarf {
+
+// Represents DWARF register.
+class Reg {
+ public:
+ explicit Reg(int reg_num) : num_(reg_num) { }
+ int num() const { return num_; }
+
+ // TODO: Arm S0–S31 register mapping is obsolescent.
+ // We should use VFP-v3/Neon D0-D31 mapping instead.
+ // However, D0 is aliased to pair of S0 and S1, so using that
+ // mapping we can not easily say S0 is spilled and S1 is not.
+ // There are ways around this in DWARF but they are complex.
+ // It would be much simpler to always spill whole D registers.
+ // Arm64 mapping is correct since we already do this there.
+
+ static Reg ArmCore(int num) { return Reg(num); }
+ static Reg ArmFp(int num) { return Reg(64 + num); } // S0–S31.
+ static Reg Arm64Core(int num) { return Reg(num); }
+ static Reg Arm64Fp(int num) { return Reg(64 + num); } // V0-V31.
+ static Reg MipsCore(int num) { return Reg(num); }
+ static Reg Mips64Core(int num) { return Reg(num); }
+ static Reg X86Core(int num) { return Reg(num); }
+ static Reg X86Fp(int num) { return Reg(21 + num); }
+ static Reg X86_64Core(int num) {
+ static const int map[8] = {0, 2, 1, 3, 7, 6, 4, 5};
+ return Reg(num < 8 ? map[num] : num);
+ }
+ static Reg X86_64Fp(int num) { return Reg(17 + num); }
+
+ private:
+ int num_;
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DWARF_REGISTER_H_
diff --git a/compiler/dwarf/writer.h b/compiler/dwarf/writer.h
new file mode 100644
index 0000000..d8e29f0
--- /dev/null
+++ b/compiler/dwarf/writer.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 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_DWARF_WRITER_H_
+#define ART_COMPILER_DWARF_WRITER_H_
+
+#include <vector>
+#include "leb128.h"
+#include "base/logging.h"
+#include "utils.h"
+
+namespace art {
+namespace dwarf {
+
+// The base class for all DWARF writers.
+template<typename Allocator = std::allocator<uint8_t>>
+class Writer {
+ public:
+ void PushUint8(int value) {
+ DCHECK_GE(value, 0);
+ DCHECK_LE(value, UINT8_MAX);
+ data_->push_back(value & 0xff);
+ }
+
+ void PushUint16(int value) {
+ DCHECK_GE(value, 0);
+ DCHECK_LE(value, UINT16_MAX);
+ data_->push_back((value >> 0) & 0xff);
+ data_->push_back((value >> 8) & 0xff);
+ }
+
+ void PushUint32(uint32_t value) {
+ data_->push_back((value >> 0) & 0xff);
+ data_->push_back((value >> 8) & 0xff);
+ data_->push_back((value >> 16) & 0xff);
+ data_->push_back((value >> 24) & 0xff);
+ }
+
+ void PushUint32(int value) {
+ DCHECK_GE(value, 0);
+ PushUint32(static_cast<uint32_t>(value));
+ }
+
+ void PushUint32(uint64_t value) {
+ DCHECK_LE(value, UINT32_MAX);
+ PushUint32(static_cast<uint32_t>(value));
+ }
+
+ void PushUint64(uint64_t value) {
+ data_->push_back((value >> 0) & 0xff);
+ data_->push_back((value >> 8) & 0xff);
+ data_->push_back((value >> 16) & 0xff);
+ data_->push_back((value >> 24) & 0xff);
+ data_->push_back((value >> 32) & 0xff);
+ data_->push_back((value >> 40) & 0xff);
+ data_->push_back((value >> 48) & 0xff);
+ data_->push_back((value >> 56) & 0xff);
+ }
+
+ void PushInt8(int value) {
+ DCHECK_GE(value, INT8_MIN);
+ DCHECK_LE(value, INT8_MAX);
+ PushUint8(static_cast<uint8_t>(value));
+ }
+
+ void PushInt16(int value) {
+ DCHECK_GE(value, INT16_MIN);
+ DCHECK_LE(value, INT16_MAX);
+ PushUint16(static_cast<uint16_t>(value));
+ }
+
+ void PushInt32(int value) {
+ PushUint32(static_cast<uint32_t>(value));
+ }
+
+ void PushInt64(int64_t value) {
+ PushUint64(static_cast<uint64_t>(value));
+ }
+
+ // Variable-length encoders.
+
+ void PushUleb128(uint32_t value) {
+ EncodeUnsignedLeb128(data_, value);
+ }
+
+ void PushUleb128(int value) {
+ DCHECK_GE(value, 0);
+ EncodeUnsignedLeb128(data_, value);
+ }
+
+ void PushSleb128(int value) {
+ EncodeSignedLeb128(data_, value);
+ }
+
+ // Miscellaneous functions.
+
+ void PushString(const char* value) {
+ data_->insert(data_->end(), value, value + strlen(value) + 1);
+ }
+
+ void PushData(const void* ptr, size_t size) {
+ const char* p = reinterpret_cast<const char*>(ptr);
+ data_->insert(data_->end(), p, p + size);
+ }
+
+ void UpdateUint32(size_t offset, uint32_t value) {
+ DCHECK_LT(offset + 3, data_->size());
+ (*data_)[offset + 0] = (value >> 0) & 0xFF;
+ (*data_)[offset + 1] = (value >> 8) & 0xFF;
+ (*data_)[offset + 2] = (value >> 16) & 0xFF;
+ (*data_)[offset + 3] = (value >> 24) & 0xFF;
+ }
+
+ void UpdateUint64(size_t offset, uint64_t value) {
+ DCHECK_LT(offset + 7, data_->size());
+ (*data_)[offset + 0] = (value >> 0) & 0xFF;
+ (*data_)[offset + 1] = (value >> 8) & 0xFF;
+ (*data_)[offset + 2] = (value >> 16) & 0xFF;
+ (*data_)[offset + 3] = (value >> 24) & 0xFF;
+ (*data_)[offset + 4] = (value >> 32) & 0xFF;
+ (*data_)[offset + 5] = (value >> 40) & 0xFF;
+ (*data_)[offset + 6] = (value >> 48) & 0xFF;
+ (*data_)[offset + 7] = (value >> 56) & 0xFF;
+ }
+
+ void Pad(int alignment) {
+ DCHECK_NE(alignment, 0);
+ data_->resize(RoundUp(data_->size(), alignment), 0);
+ }
+
+ const std::vector<uint8_t, Allocator>* data() const {
+ return data_;
+ }
+
+ explicit Writer(std::vector<uint8_t, Allocator>* buffer) : data_(buffer) { }
+
+ private:
+ std::vector<uint8_t, Allocator>* data_;
+
+ DISALLOW_COPY_AND_ASSIGN(Writer);
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DWARF_WRITER_H_
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index a822b24..ca5ec66 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -21,7 +21,9 @@
#include "base/logging.h"
#include "base/unix_file/fd_file.h"
#include "buffered_output_stream.h"
+#include "compiled_method.h"
#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "dwarf.h"
#include "elf_builder.h"
#include "elf_file.h"
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index c1555aa..1ede228 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -19,6 +19,7 @@
#include <sys/stat.h>
#include <memory>
+#include <numeric>
#include <vector>
#include "base/logging.h"
@@ -54,8 +55,7 @@
#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "handle_scope-inl.h"
-
-#include <numeric>
+#include "utils/dex_cache_arrays_layout-inl.h"
using ::art::mirror::ArtField;
using ::art::mirror::ArtMethod;
@@ -238,7 +238,7 @@
DCHECK(object != nullptr);
DCHECK_NE(image_objects_offset_begin_, 0u);
- size_t previous_bin_sizes = GetBinSizeSum(bin_slot.GetBin()); // sum sizes in [0..bin#)
+ size_t previous_bin_sizes = bin_slot_previous_sizes_[bin_slot.GetBin()];
size_t new_offset = image_objects_offset_begin_ + previous_bin_sizes + bin_slot.GetIndex();
DCHECK_ALIGNED(new_offset, kObjectAlignment);
@@ -293,6 +293,28 @@
DCHECK(IsImageBinSlotAssigned(object));
}
+void ImageWriter::PrepareDexCacheArraySlots() {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ReaderMutexLock mu(Thread::Current(), *class_linker->DexLock());
+ size_t dex_cache_count = class_linker->GetDexCacheCount();
+ uint32_t size = 0u;
+ for (size_t idx = 0; idx < dex_cache_count; ++idx) {
+ DexCache* dex_cache = class_linker->GetDexCache(idx);
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ dex_cache_array_starts_.Put(dex_file, size);
+ DexCacheArraysLayout layout(dex_file);
+ DCHECK(layout.Valid());
+ dex_cache_array_indexes_.Put(dex_cache->GetResolvedTypes(), size + layout.TypesOffset());
+ dex_cache_array_indexes_.Put(dex_cache->GetResolvedMethods(), size + layout.MethodsOffset());
+ dex_cache_array_indexes_.Put(dex_cache->GetResolvedFields(), size + layout.FieldsOffset());
+ dex_cache_array_indexes_.Put(dex_cache->GetStrings(), size + layout.StringsOffset());
+ size += layout.Size();
+ }
+ // Set the slot size early to avoid DCHECK() failures in IsImageBinSlotAssigned()
+ // when AssignImageBinSlot() assigns their indexes out or order.
+ bin_slot_sizes_[kBinDexCacheArray] = size;
+}
+
void ImageWriter::AssignImageBinSlot(mirror::Object* object) {
DCHECK(object != nullptr);
size_t object_size = object->SizeOf();
@@ -307,6 +329,7 @@
// 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) {
//
@@ -316,6 +339,12 @@
// 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 beginning.
// * 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,
@@ -376,13 +405,21 @@
}
} else if (object->GetClass<kVerifyNone>()->IsStringClass()) {
bin = kBinString; // Strings are almost always immutable (except for object header).
+ } else if (object->IsObjectArray()) {
+ auto it = dex_cache_array_indexes_.find(object);
+ if (it != dex_cache_array_indexes_.end()) {
+ bin = kBinDexCacheArray;
+ current_offset = it->second; // Use prepared offset defined by the DexCacheLayout.
+ } // else bin = kBinRegular
} // else bin = kBinRegular
}
- size_t current_offset = bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned).
- // Move the current bin size up to accomodate the object we just assigned a bin slot.
size_t offset_delta = RoundUp(object_size, kObjectAlignment); // 64-bit alignment
- bin_slot_sizes_[bin] += offset_delta;
+ if (bin != kBinDexCacheArray) {
+ current_offset = bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned).
+ // Move the current bin size up to accomodate the object we just assigned a bin slot.
+ bin_slot_sizes_[bin] += offset_delta;
+ }
BinSlot new_bin_slot(bin, current_offset);
SetImageBinSlot(object, new_bin_slot);
@@ -887,8 +924,17 @@
// TODO: Image spaces only?
DCHECK_LT(image_end_, image_->Size());
image_objects_offset_begin_ = image_end_;
+ // Prepare bin slots for dex cache arrays.
+ PrepareDexCacheArraySlots();
// Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
heap->VisitObjects(WalkFieldsCallback, this);
+ // Calculate cumulative bin slot sizes.
+ size_t previous_sizes = 0u;
+ for (size_t i = 0; i != kBinSize; ++i) {
+ bin_slot_previous_sizes_[i] = previous_sizes;
+ previous_sizes += bin_slot_sizes_[i];
+ }
+ DCHECK_EQ(previous_sizes, GetBinSizeSum());
// Transform each object's bin slot into an offset which will be used to do the final copy.
heap->VisitObjects(UnbinObjectsIntoOffsetCallback, this);
DCHECK(saved_hashes_map_.empty()); // All binslot hashes should've been put into vector by now.
@@ -1187,8 +1233,8 @@
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 == 29, "wrong number of shift");
+ static_assert(kBinBits == 4, "wrong number of bin bits");
+ static_assert(kBinShift == 28, "wrong number of shift");
static_assert(sizeof(BinSlot) == sizeof(LockWord), "BinSlot/LockWord must have equal sizes");
DCHECK_LT(GetBin(), kBinSize);
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 53f5ce4..71044f7 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -52,7 +52,8 @@
quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0),
quick_to_interpreter_bridge_offset_(0), compile_pic_(compile_pic),
target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())),
- bin_slot_sizes_(), bin_slot_count_() {
+ bin_slot_sizes_(), bin_slot_previous_sizes_(), bin_slot_count_(),
+ string_data_array_(nullptr) {
CHECK_NE(image_begin, 0U);
}
@@ -80,6 +81,14 @@
return reinterpret_cast<mirror::Object*>(image_begin_ + GetImageOffset(object));
}
+ mirror::HeapReference<mirror::Object>* GetDexCacheArrayElementImageAddress(
+ const DexFile* dex_file, uint32_t offset) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ auto it = dex_cache_array_starts_.find(dex_file);
+ DCHECK(it != dex_cache_array_starts_.end());
+ return reinterpret_cast<mirror::HeapReference<mirror::Object>*>(
+ image_begin_ + RoundUp(sizeof(ImageHeader), kObjectAlignment) + it->second + offset);
+ }
+
uint8_t* GetOatFileBegin() const {
return image_begin_ + RoundUp(image_end_, kPageSize);
}
@@ -101,6 +110,10 @@
// Classify different kinds of bins that objects end up getting packed into during image writing.
enum Bin {
+ // 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 beginning.
+ kBinDexCacheArray, // Object arrays belonging to dex cache.
// Likely-clean:
kBinString, // [String] Almost always immutable (except for obj header).
kBinArtMethodsManagedInitialized, // [ArtMethod] Not-native, and initialized. Unlikely to dirty
@@ -113,7 +126,6 @@
kBinClassVerified, // Class verified, but initializers haven't been run
kBinArtMethodNative, // Art method that is actually native
kBinArtMethodNotInitialized, // Art method with a declaring class that wasn't initialized
- // Don't care about other art methods since they don't dirty
// Add more bins here if we add more segregation code.
kBinSize,
};
@@ -157,6 +169,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
size_t GetImageOffset(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void PrepareDexCacheArraySlots() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void AssignImageBinSlot(mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetImageBinSlot(mirror::Object* object, BinSlot bin_slot)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -282,6 +295,12 @@
// Memory mapped for generating the image.
std::unique_ptr<MemMap> image_;
+ // Indexes for dex cache arrays (objects are inside of the image so that they don't move).
+ SafeMap<mirror::Object*, size_t> dex_cache_array_indexes_;
+
+ // The start offsets of the dex cache arrays.
+ SafeMap<const DexFile*, size_t> dex_cache_array_starts_;
+
// Saved hashes (objects are inside of the image so that they don't move).
std::vector<std::pair<mirror::Object*, uint32_t>> saved_hashes_;
@@ -309,6 +328,7 @@
// Bin slot tracking for dirty object packing
size_t bin_slot_sizes_[kBinSize]; // Number of bytes in a bin
+ size_t bin_slot_previous_sizes_[kBinSize]; // Number of bytes in previous bins.
size_t bin_slot_count_[kBinSize]; // Number of objects in a bin
void* string_data_array_; // The backing for the interned strings.
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index afd39e8..503068c 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -17,11 +17,14 @@
#include "arch/instruction_set_features.h"
#include "class_linker.h"
#include "common_compiler_test.h"
+#include "compiled_method.h"
#include "compiler.h"
#include "dex/pass_manager.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex/quick_compiler_callbacks.h"
#include "dex/verification_results.h"
+#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index b3bb438..04f0db6 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -18,14 +18,18 @@
#include <zlib.h>
+#include "arch/arm64/instruction_set_features_arm64.h"
#include "base/allocator.h"
#include "base/bit_vector.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
#include "compiled_class.h"
+#include "compiled_method.h"
#include "dex_file-inl.h"
#include "dex/verification_results.h"
+#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "gc/space/space.h"
#include "image_writer.h"
#include "mirror/art_method-inl.h"
@@ -43,12 +47,12 @@
namespace art {
-class OatWriter::RelativeCallPatcher {
+class OatWriter::RelativePatcher {
public:
- virtual ~RelativeCallPatcher() { }
+ virtual ~RelativePatcher() { }
- // Reserve space for relative call thunks if needed, return adjusted offset.
- // After all methods have been processed it's call one last time with compiled_method == nullptr.
+ // Reserve space for relative call thunks if needed, return adjusted offset. After all methods
+ // of a class have been processed it's called one last time with compiled_method == nullptr.
virtual uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) = 0;
// Write relative call thunks if needed, return adjusted offset.
@@ -56,19 +60,23 @@
// Patch method code. The input displacement is relative to the patched location,
// the patcher may need to adjust it if the correct base is different.
- virtual void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
- uint32_t target_offset) = 0;
+ virtual void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
+ uint32_t patch_offset, uint32_t target_offset) = 0;
+
+ // Patch a reference to a dex cache location.
+ virtual void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
+ uint32_t patch_offset, uint32_t target_offset) = 0;
protected:
- RelativeCallPatcher() { }
+ RelativePatcher() { }
private:
- DISALLOW_COPY_AND_ASSIGN(RelativeCallPatcher);
+ DISALLOW_COPY_AND_ASSIGN(RelativePatcher);
};
-class OatWriter::NoRelativeCallPatcher FINAL : public RelativeCallPatcher {
+class OatWriter::NoRelativePatcher FINAL : public RelativePatcher {
public:
- NoRelativeCallPatcher() { }
+ NoRelativePatcher() { }
uint32_t ReserveSpace(uint32_t offset,
const CompiledMethod* compiled_method ATTRIBUTE_UNUSED) OVERRIDE {
@@ -79,19 +87,27 @@
return offset; // No thunks added; no patches expected.
}
- void Patch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, uint32_t literal_offset ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED,
- uint32_t target_offset ATTRIBUTE_UNUSED) OVERRIDE {
- LOG(FATAL) << "Unexpected relative patch.";
+ void PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ uint32_t literal_offset ATTRIBUTE_UNUSED,
+ uint32_t patch_offset ATTRIBUTE_UNUSED,
+ uint32_t target_offset ATTRIBUTE_UNUSED) OVERRIDE {
+ LOG(FATAL) << "Unexpected relative call patch.";
+ }
+
+ virtual void PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ const LinkerPatch& patch ATTRIBUTE_UNUSED,
+ uint32_t patch_offset ATTRIBUTE_UNUSED,
+ uint32_t target_offset ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "Unexpected relative dex cache array patch.";
}
private:
- DISALLOW_COPY_AND_ASSIGN(NoRelativeCallPatcher);
+ DISALLOW_COPY_AND_ASSIGN(NoRelativePatcher);
};
-class OatWriter::X86RelativeCallPatcher FINAL : public RelativeCallPatcher {
+class OatWriter::X86RelativePatcher FINAL : public RelativePatcher {
public:
- X86RelativeCallPatcher() { }
+ X86RelativePatcher() { }
uint32_t ReserveSpace(uint32_t offset,
const CompiledMethod* compiled_method ATTRIBUTE_UNUSED) OVERRIDE {
@@ -102,8 +118,8 @@
return offset; // No thunks added; no limit on relative call distance.
}
- void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE {
+ void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
+ uint32_t patch_offset, uint32_t target_offset) OVERRIDE {
DCHECK_LE(literal_offset + 4u, code->size());
// Unsigned arithmetic with its well-defined overflow behavior is just fine here.
uint32_t displacement = target_offset - patch_offset;
@@ -113,17 +129,24 @@
reinterpret_cast<unaligned_int32_t*>(&(*code)[literal_offset])[0] = displacement;
}
+ virtual void PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ const LinkerPatch& patch ATTRIBUTE_UNUSED,
+ uint32_t patch_offset ATTRIBUTE_UNUSED,
+ uint32_t target_offset ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "Unexpected relative dex cache array patch.";
+ }
+
private:
// PC displacement from patch location; x86 PC for relative calls points to the next
// instruction and the patch location is 4 bytes earlier.
static constexpr int32_t kPcDisplacement = 4;
- DISALLOW_COPY_AND_ASSIGN(X86RelativeCallPatcher);
+ DISALLOW_COPY_AND_ASSIGN(X86RelativePatcher);
};
-class OatWriter::ArmBaseRelativeCallPatcher : public RelativeCallPatcher {
+class OatWriter::ArmBaseRelativePatcher : public RelativePatcher {
public:
- ArmBaseRelativeCallPatcher(OatWriter* writer,
+ ArmBaseRelativePatcher(OatWriter* writer,
InstructionSet instruction_set, std::vector<uint8_t> thunk_code,
uint32_t max_positive_displacement, uint32_t max_negative_displacement)
: writer_(writer), instruction_set_(instruction_set), thunk_code_(thunk_code),
@@ -133,41 +156,7 @@
}
uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) OVERRIDE {
- // NOTE: The final thunk can be reserved from InitCodeMethodVisitor::EndClass() while it
- // may be written early by WriteCodeMethodVisitor::VisitMethod() for a deduplicated chunk
- // of code. To avoid any alignment discrepancies for the final chunk, we always align the
- // offset after reserving of writing any chunk.
- if (UNLIKELY(compiled_method == nullptr)) {
- uint32_t aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
- bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset);
- if (needs_thunk) {
- thunk_locations_.push_back(aligned_offset);
- offset = CompiledMethod::AlignCode(aligned_offset + thunk_code_.size(), instruction_set_);
- }
- return offset;
- }
- DCHECK(compiled_method->GetQuickCode() != nullptr);
- uint32_t quick_code_size = compiled_method->GetQuickCode()->size();
- uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader);
- uint32_t next_aligned_offset = compiled_method->AlignCode(quick_code_offset + quick_code_size);
- if (!unprocessed_patches_.empty() &&
- next_aligned_offset - unprocessed_patches_.front().second > max_positive_displacement_) {
- bool needs_thunk = ReserveSpaceProcessPatches(next_aligned_offset);
- if (needs_thunk) {
- // A single thunk will cover all pending patches.
- unprocessed_patches_.clear();
- uint32_t thunk_location = compiled_method->AlignCode(offset);
- thunk_locations_.push_back(thunk_location);
- offset = CompiledMethod::AlignCode(thunk_location + thunk_code_.size(), instruction_set_);
- }
- }
- for (const LinkerPatch& patch : compiled_method->GetPatches()) {
- if (patch.Type() == kLinkerPatchCallRelative) {
- unprocessed_patches_.emplace_back(patch.TargetMethod(),
- quick_code_offset + patch.LiteralOffset());
- }
- }
- return offset;
+ return ReserveSpaceInternal(offset, compiled_method, 0u);
}
uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
@@ -197,6 +186,47 @@
}
protected:
+ uint32_t ReserveSpaceInternal(uint32_t offset, const CompiledMethod* compiled_method,
+ uint32_t max_extra_space) {
+ // NOTE: The final thunk can be reserved from InitCodeMethodVisitor::EndClass() while it
+ // may be written early by WriteCodeMethodVisitor::VisitMethod() for a deduplicated chunk
+ // of code. To avoid any alignment discrepancies for the final chunk, we always align the
+ // offset after reserving of writing any chunk.
+ if (UNLIKELY(compiled_method == nullptr)) {
+ uint32_t aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
+ bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset);
+ if (needs_thunk) {
+ thunk_locations_.push_back(aligned_offset);
+ offset = CompiledMethod::AlignCode(aligned_offset + thunk_code_.size(), instruction_set_);
+ }
+ return offset;
+ }
+ DCHECK(compiled_method->GetQuickCode() != nullptr);
+ uint32_t quick_code_size = compiled_method->GetQuickCode()->size();
+ uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader);
+ uint32_t next_aligned_offset = compiled_method->AlignCode(quick_code_offset + quick_code_size);
+ // Adjust for extra space required by the subclass.
+ next_aligned_offset = compiled_method->AlignCode(next_aligned_offset + max_extra_space);
+ if (!unprocessed_patches_.empty() &&
+ next_aligned_offset - unprocessed_patches_.front().second > max_positive_displacement_) {
+ bool needs_thunk = ReserveSpaceProcessPatches(next_aligned_offset);
+ if (needs_thunk) {
+ // A single thunk will cover all pending patches.
+ unprocessed_patches_.clear();
+ uint32_t thunk_location = compiled_method->AlignCode(offset);
+ thunk_locations_.push_back(thunk_location);
+ offset = CompiledMethod::AlignCode(thunk_location + thunk_code_.size(), instruction_set_);
+ }
+ }
+ for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+ if (patch.Type() == kLinkerPatchCallRelative) {
+ unprocessed_patches_.emplace_back(patch.TargetMethod(),
+ quick_code_offset + patch.LiteralOffset());
+ }
+ }
+ return offset;
+ }
+
uint32_t CalculateDisplacement(uint32_t patch_offset, uint32_t target_offset) {
// Unsigned arithmetic with its well-defined overflow behavior is just fine here.
uint32_t displacement = target_offset - patch_offset;
@@ -219,6 +249,10 @@
return displacement;
}
+ OatWriter* Writer() const {
+ return writer_;
+ }
+
private:
bool ReserveSpaceProcessPatches(uint32_t next_aligned_offset) {
// Process as many patches as possible, stop only on unresolved targets or calls too far back.
@@ -261,18 +295,18 @@
typedef std::pair<MethodReference, uint32_t> UnprocessedPatch;
std::deque<UnprocessedPatch> unprocessed_patches_;
- DISALLOW_COPY_AND_ASSIGN(ArmBaseRelativeCallPatcher);
+ DISALLOW_COPY_AND_ASSIGN(ArmBaseRelativePatcher);
};
-class OatWriter::Thumb2RelativeCallPatcher FINAL : public ArmBaseRelativeCallPatcher {
+class OatWriter::Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher {
public:
- explicit Thumb2RelativeCallPatcher(OatWriter* writer)
- : ArmBaseRelativeCallPatcher(writer, kThumb2, CompileThunkCode(),
+ explicit Thumb2RelativePatcher(OatWriter* writer)
+ : ArmBaseRelativePatcher(writer, kThumb2, CompileThunkCode(),
kMaxPositiveDisplacement, kMaxNegativeDisplacement) {
}
- void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE {
+ void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
+ uint32_t patch_offset, uint32_t target_offset) OVERRIDE {
DCHECK_LE(literal_offset + 4u, code->size());
DCHECK_EQ(literal_offset & 1u, 0u);
DCHECK_EQ(patch_offset & 1u, 0u);
@@ -302,6 +336,13 @@
addr[3] = (value >> 8) & 0xff;
}
+ virtual void PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ const LinkerPatch& patch ATTRIBUTE_UNUSED,
+ uint32_t patch_offset ATTRIBUTE_UNUSED,
+ uint32_t target_offset ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "Unexpected relative dex cache array patch.";
+ }
+
private:
static std::vector<uint8_t> CompileThunkCode() {
// The thunk just uses the entry point in the ArtMethod. This works even for calls
@@ -326,18 +367,102 @@
static constexpr uint32_t kMaxPositiveDisplacement = (1u << 24) - 2 + kPcDisplacement;
static constexpr uint32_t kMaxNegativeDisplacement = (1u << 24) - kPcDisplacement;
- DISALLOW_COPY_AND_ASSIGN(Thumb2RelativeCallPatcher);
+ DISALLOW_COPY_AND_ASSIGN(Thumb2RelativePatcher);
};
-class OatWriter::Arm64RelativeCallPatcher FINAL : public ArmBaseRelativeCallPatcher {
+class OatWriter::Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher {
public:
- explicit Arm64RelativeCallPatcher(OatWriter* writer)
- : ArmBaseRelativeCallPatcher(writer, kArm64, CompileThunkCode(),
- kMaxPositiveDisplacement, kMaxNegativeDisplacement) {
+ explicit Arm64RelativePatcher(OatWriter* writer)
+ : ArmBaseRelativePatcher(writer, kArm64, CompileThunkCode(),
+ kMaxPositiveDisplacement, kMaxNegativeDisplacement),
+ fix_cortex_a53_843419_(writer->compiler_driver_->GetInstructionSetFeatures()
+ ->AsArm64InstructionSetFeatures()->NeedFixCortexA53_835769()),
+ reserved_adrp_thunks_(0u),
+ processed_adrp_thunks_(0u) {
+ if (fix_cortex_a53_843419_) {
+ adrp_thunk_locations_.reserve(16u);
+ current_method_thunks_.reserve(16u * kAdrpThunkSize);
+ }
}
- void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
- uint32_t target_offset) OVERRIDE {
+ uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) OVERRIDE {
+ if (!fix_cortex_a53_843419_) {
+ DCHECK(adrp_thunk_locations_.empty());
+ return ReserveSpaceInternal(offset, compiled_method, 0u);
+ }
+
+ // Add thunks for previous method if any.
+ if (reserved_adrp_thunks_ != adrp_thunk_locations_.size()) {
+ size_t num_adrp_thunks = adrp_thunk_locations_.size() - reserved_adrp_thunks_;
+ offset = CompiledMethod::AlignCode(offset, kArm64) + kAdrpThunkSize * num_adrp_thunks;
+ reserved_adrp_thunks_ = adrp_thunk_locations_.size();
+ }
+
+ // Count the number of ADRP insns as the upper bound on the number of thunks needed
+ // and use it to reserve space for other linker patches.
+ size_t num_adrp = 0u;
+ if (LIKELY(compiled_method != nullptr)) {
+ for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+ if (patch.Type() == kLinkerPatchDexCacheArray &&
+ patch.LiteralOffset() == patch.PcInsnOffset()) { // ADRP patch
+ ++num_adrp;
+ }
+ }
+ }
+ offset = ReserveSpaceInternal(offset, compiled_method, kAdrpThunkSize * num_adrp);
+ if (num_adrp == 0u) {
+ return offset;
+ }
+
+ // Now that we have the actual offset where the code will be placed, locate the ADRP insns
+ // that actually require the thunk.
+ uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader);
+ ArrayRef<const uint8_t> code(*compiled_method->GetQuickCode());
+ uint32_t thunk_offset = compiled_method->AlignCode(quick_code_offset + code.size());
+ DCHECK(compiled_method != nullptr);
+ for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+ if (patch.Type() == kLinkerPatchDexCacheArray &&
+ patch.LiteralOffset() == patch.PcInsnOffset()) { // ADRP patch
+ uint32_t patch_offset = quick_code_offset + patch.LiteralOffset();
+ if (NeedsErratum843419Thunk(code, patch.LiteralOffset(), patch_offset)) {
+ adrp_thunk_locations_.emplace_back(patch_offset, thunk_offset);
+ thunk_offset += kAdrpThunkSize;
+ }
+ }
+ }
+ return offset;
+ }
+
+ uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
+ if (fix_cortex_a53_843419_) {
+ if (!current_method_thunks_.empty()) {
+ uint32_t aligned_offset = CompiledMethod::AlignCode(offset, kArm64);
+ if (kIsDebugBuild) {
+ CHECK(IsAligned<kAdrpThunkSize>(current_method_thunks_.size()));
+ size_t num_thunks = current_method_thunks_.size() / kAdrpThunkSize;
+ CHECK_LE(num_thunks, processed_adrp_thunks_);
+ for (size_t i = 0u; i != num_thunks; ++i) {
+ const auto& entry = adrp_thunk_locations_[processed_adrp_thunks_ - num_thunks + i];
+ CHECK_EQ(entry.second, aligned_offset + i * kAdrpThunkSize);
+ }
+ }
+ uint32_t aligned_code_delta = aligned_offset - offset;
+ if (aligned_code_delta != 0u && !Writer()->WriteCodeAlignment(out, aligned_code_delta)) {
+ return 0u;
+ }
+ if (!out->WriteFully(¤t_method_thunks_[0], current_method_thunks_.size())) {
+ return 0u;
+ }
+ Writer()->size_misc_thunks_ += current_method_thunks_.size();
+ offset = aligned_offset + current_method_thunks_.size();
+ current_method_thunks_.clear();
+ }
+ }
+ return ArmBaseRelativePatcher::WriteThunks(out, offset);
+ }
+
+ void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
+ uint32_t patch_offset, uint32_t target_offset) OVERRIDE {
DCHECK_LE(literal_offset + 4u, code->size());
DCHECK_EQ(literal_offset & 3u, 0u);
DCHECK_EQ(patch_offset & 3u, 0u);
@@ -345,20 +470,104 @@
uint32_t displacement = CalculateDisplacement(patch_offset, target_offset & ~1u);
DCHECK_EQ(displacement & 3u, 0u);
DCHECK((displacement >> 27) == 0u || (displacement >> 27) == 31u); // 28-bit signed.
- uint32_t value = (displacement & 0x0fffffffu) >> 2;
- value |= 0x94000000; // BL
+ uint32_t insn = (displacement & 0x0fffffffu) >> 2;
+ insn |= 0x94000000; // BL
- uint8_t* addr = &(*code)[literal_offset];
// Check that we're just overwriting an existing BL.
- DCHECK_EQ(addr[3] & 0xfc, 0x94);
+ DCHECK_EQ(GetInsn(code, literal_offset) & 0xfc000000u, 0x94000000u);
// Write the new BL.
- addr[0] = (value >> 0) & 0xff;
- addr[1] = (value >> 8) & 0xff;
- addr[2] = (value >> 16) & 0xff;
- addr[3] = (value >> 24) & 0xff;
+ SetInsn(code, literal_offset, insn);
+ }
+
+ virtual void PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ const LinkerPatch& patch ATTRIBUTE_UNUSED,
+ uint32_t patch_offset ATTRIBUTE_UNUSED,
+ uint32_t target_offset ATTRIBUTE_UNUSED) {
+ DCHECK_EQ(patch_offset & 3u, 0u);
+ DCHECK_EQ(target_offset & 3u, 0u);
+ uint32_t literal_offset = patch.LiteralOffset();
+ uint32_t insn = GetInsn(code, literal_offset);
+ uint32_t pc_insn_offset = patch.PcInsnOffset();
+ uint32_t disp = target_offset - ((patch_offset - literal_offset + pc_insn_offset) & ~0xfffu);
+ if (literal_offset == pc_insn_offset) {
+ // Check it's an ADRP with imm == 0 (unset).
+ DCHECK_EQ((insn & 0xffffffe0u), 0x90000000u)
+ << literal_offset << ", " << pc_insn_offset << ", 0x" << std::hex << insn;
+ if (fix_cortex_a53_843419_ && processed_adrp_thunks_ != adrp_thunk_locations_.size() &&
+ adrp_thunk_locations_[processed_adrp_thunks_].first == patch_offset) {
+ DCHECK(NeedsErratum843419Thunk(ArrayRef<const uint8_t>(*code),
+ literal_offset, patch_offset));
+ uint32_t thunk_offset = adrp_thunk_locations_[processed_adrp_thunks_].second;
+ uint32_t adrp_disp = target_offset - (thunk_offset & ~0xfffu);
+ uint32_t adrp = PatchAdrp(insn, adrp_disp);
+
+ uint32_t out_disp = thunk_offset - patch_offset;
+ DCHECK_EQ(out_disp & 3u, 0u);
+ DCHECK((out_disp >> 27) == 0u || (out_disp >> 27) == 31u); // 28-bit signed.
+ insn = (out_disp & 0x0fffffffu) >> 2;
+ insn |= 0x14000000; // B <thunk>
+
+ uint32_t back_disp = -out_disp;
+ DCHECK_EQ(back_disp & 3u, 0u);
+ DCHECK((back_disp >> 27) == 0u || (back_disp >> 27) == 31u); // 28-bit signed.
+ uint32_t b_back = (back_disp & 0x0fffffffu) >> 2;
+ b_back |= 0x14000000; // B <back>
+ size_t thunks_code_offset = current_method_thunks_.size();
+ current_method_thunks_.resize(thunks_code_offset + kAdrpThunkSize);
+ SetInsn(¤t_method_thunks_, thunks_code_offset, adrp);
+ SetInsn(¤t_method_thunks_, thunks_code_offset + 4u, b_back);
+ static_assert(kAdrpThunkSize == 2 * 4u, "thunk has 2 instructions");
+
+ processed_adrp_thunks_ += 1u;
+ } else {
+ insn = PatchAdrp(insn, disp);
+ }
+ // Write the new ADRP (or B to the erratum 843419 thunk).
+ SetInsn(code, literal_offset, insn);
+ } else {
+ DCHECK_EQ(insn & 0xfffffc00, 0xb9400000); // LDR 32-bit with imm12 == 0 (unset).
+ if (kIsDebugBuild) {
+ uint32_t adrp = GetInsn(code, pc_insn_offset);
+ if ((adrp & 0x9f000000u) != 0x90000000u) {
+ CHECK(fix_cortex_a53_843419_);
+ CHECK_EQ(adrp & 0xfc000000u, 0x14000000u); // B <thunk>
+ CHECK(IsAligned<kAdrpThunkSize>(current_method_thunks_.size()));
+ size_t num_thunks = current_method_thunks_.size() / kAdrpThunkSize;
+ CHECK_LE(num_thunks, processed_adrp_thunks_);
+ uint32_t b_offset = patch_offset - literal_offset + pc_insn_offset;
+ for (size_t i = processed_adrp_thunks_ - num_thunks; ; ++i) {
+ CHECK_NE(i, processed_adrp_thunks_);
+ if (adrp_thunk_locations_[i].first == b_offset) {
+ size_t idx = num_thunks - (processed_adrp_thunks_ - i);
+ adrp = GetInsn(¤t_method_thunks_, idx * kAdrpThunkSize);
+ break;
+ }
+ }
+ }
+ CHECK_EQ(adrp & 0x9f00001fu, // Check that pc_insn_offset points
+ 0x90000000 | ((insn >> 5) & 0x1fu)); // to ADRP with matching register.
+ }
+ uint32_t imm12 = (disp & 0xfffu) >> 2;
+ insn = (insn & ~(0xfffu << 10)) | (imm12 << 10);
+ SetInsn(code, literal_offset, insn);
+ }
}
private:
+ static uint32_t PatchAdrp(uint32_t adrp, uint32_t disp) {
+ return (adrp & 0x9f00001fu) | // Clear offset bits, keep ADRP with destination reg.
+ // Bottom 12 bits are ignored, the next 2 lowest bits are encoded in bits 29-30.
+ ((disp & 0x00003000u) << (29 - 12)) |
+ // The next 16 bits are encoded in bits 5-22.
+ ((disp & 0xffffc000u) >> (12 + 2 - 5)) |
+ // Since the target_offset is based on the beginning of the oat file and the
+ // image space precedes the oat file, the target_offset into image space will
+ // be negative yet passed as uint32_t. Therefore we limit the displacement
+ // to +-2GiB (rather than the maximim +-4GiB) and determine the sign bit from
+ // the highest bit of the displacement. This is encoded in bit 23.
+ ((disp & 0x80000000u) >> (31 - 23));
+ }
+
static std::vector<uint8_t> CompileThunkCode() {
// The thunk just uses the entry point in the ArtMethod. This works even for calls
// to the generic JNI and interpreter trampolines.
@@ -374,13 +583,65 @@
return thunk_code;
}
+ static bool NeedsErratum843419Thunk(ArrayRef<const uint8_t> code, uint32_t literal_offset,
+ uint32_t patch_offset) {
+ DCHECK_EQ(patch_offset & 0x3u, 0u);
+ if ((patch_offset & 0xff8) == 0xff8) { // ...ff8 or ...ffc
+ uint32_t adrp = GetInsn(code, literal_offset);
+ DCHECK_EQ(adrp & 0xff000000, 0x90000000);
+ // TODO: Improve the check. For now, we're just checking if the next insn is
+ // the LDR using the result of the ADRP, otherwise we implement the workaround.
+ uint32_t next_insn = GetInsn(code, literal_offset + 4u);
+ bool ok = (next_insn & 0xffc00000) == 0xb9400000 && // LDR <Wt>, [<Xn>, #pimm]
+ (((next_insn >> 5) ^ adrp) & 0x1f) == 0; // <Xn> == ADRP destination reg
+ return !ok;
+ }
+ return false;
+ }
+
+ static uint32_t GetInsn(ArrayRef<const uint8_t> code, uint32_t offset) {
+ DCHECK_LE(offset + 4u, code.size());
+ DCHECK_EQ(offset & 3u, 0u);
+ const uint8_t* addr = &code[offset];
+ return
+ (static_cast<uint32_t>(addr[0]) << 0) +
+ (static_cast<uint32_t>(addr[1]) << 8) +
+ (static_cast<uint32_t>(addr[2]) << 16)+
+ (static_cast<uint32_t>(addr[3]) << 24);
+ }
+
+ template <typename Alloc>
+ static uint32_t GetInsn(std::vector<uint8_t, Alloc>* code, uint32_t offset) {
+ return GetInsn(ArrayRef<const uint8_t>(*code), offset);
+ }
+
+ void SetInsn(std::vector<uint8_t>* code, uint32_t offset, uint32_t value) {
+ DCHECK_LE(offset + 4u, code->size());
+ DCHECK_EQ(offset & 3u, 0u);
+ uint8_t* addr = &(*code)[offset];
+ addr[0] = (value >> 0) & 0xff;
+ addr[1] = (value >> 8) & 0xff;
+ addr[2] = (value >> 16) & 0xff;
+ addr[3] = (value >> 24) & 0xff;
+ }
+
// Maximum positive and negative displacement measured from the patch location.
// (Signed 28 bit displacement with the last bit 0 has range [-2^27, 2^27-4] measured from
// the ARM64 PC pointing to the BL.)
static constexpr uint32_t kMaxPositiveDisplacement = (1u << 27) - 4u;
static constexpr uint32_t kMaxNegativeDisplacement = (1u << 27);
- DISALLOW_COPY_AND_ASSIGN(Arm64RelativeCallPatcher);
+ // The ADRP thunk for erratum 843419 is 2 instructions, i.e. 8 bytes.
+ static constexpr uint32_t kAdrpThunkSize = 8u;
+
+ const bool fix_cortex_a53_843419_;
+ // Map original patch_offset to thunk offset.
+ std::vector<std::pair<uint32_t, uint32_t>> adrp_thunk_locations_;
+ size_t reserved_adrp_thunks_;
+ size_t processed_adrp_thunks_;
+ std::vector<uint8_t> current_method_thunks_;
+
+ DISALLOW_COPY_AND_ASSIGN(Arm64RelativePatcher);
};
#define DCHECK_OFFSET() \
@@ -427,6 +688,7 @@
size_code_(0),
size_code_alignment_(0),
size_relative_call_thunks_(0),
+ size_misc_thunks_(0),
size_mapping_table_(0),
size_vmap_table_(0),
size_gc_map_(0),
@@ -445,18 +707,18 @@
switch (compiler_driver_->GetInstructionSet()) {
case kX86:
case kX86_64:
- relative_call_patcher_.reset(new X86RelativeCallPatcher);
+ relative_patcher_.reset(new X86RelativePatcher);
break;
case kArm:
// Fall through: we generate Thumb2 code for "arm".
case kThumb2:
- relative_call_patcher_.reset(new Thumb2RelativeCallPatcher(this));
+ relative_patcher_.reset(new Thumb2RelativePatcher(this));
break;
case kArm64:
- relative_call_patcher_.reset(new Arm64RelativeCallPatcher(this));
+ relative_patcher_.reset(new Arm64RelativePatcher(this));
break;
default:
- relative_call_patcher_.reset(new NoRelativeCallPatcher);
+ relative_patcher_.reset(new NoRelativePatcher);
break;
}
@@ -706,7 +968,7 @@
bool EndClass() {
OatDexMethodVisitor::EndClass();
if (oat_class_index_ == writer_->oat_classes_.size()) {
- offset_ = writer_->relative_call_patcher_->ReserveSpace(offset_, nullptr);
+ offset_ = writer_->relative_patcher_->ReserveSpace(offset_, nullptr);
}
return true;
}
@@ -722,23 +984,22 @@
const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
CHECK(quick_code != nullptr);
- offset_ = writer_->relative_call_patcher_->ReserveSpace(offset_, compiled_method);
- offset_ = compiled_method->AlignCode(offset_);
- DCHECK_ALIGNED_PARAM(offset_,
- GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
uint32_t code_size = quick_code->size() * sizeof(uint8_t);
CHECK_NE(code_size, 0U);
uint32_t thumb_offset = compiled_method->CodeDelta();
- quick_code_offset = offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
-
- bool deduped = false;
// Deduplicate code arrays.
+ bool deduped = false;
auto lb = dedupe_map_.lower_bound(compiled_method);
if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(compiled_method, lb->first)) {
quick_code_offset = lb->second;
deduped = true;
} else {
+ offset_ = writer_->relative_patcher_->ReserveSpace(offset_, compiled_method);
+ offset_ = compiled_method->AlignCode(offset_);
+ DCHECK_ALIGNED_PARAM(offset_,
+ GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+ quick_code_offset = offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
dedupe_map_.PutBefore(lb, compiled_method, quick_code_offset);
}
@@ -790,7 +1051,7 @@
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.Type() != kLinkerPatchCallRelative) {
+ if (!patch.IsPcRelative()) {
writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset());
}
}
@@ -851,6 +1112,37 @@
}
private:
+ struct CodeOffsetsKeyComparator {
+ bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const {
+ if (lhs->GetQuickCode() != rhs->GetQuickCode()) {
+ return lhs->GetQuickCode() < rhs->GetQuickCode();
+ }
+ // If the code is the same, all other fields are likely to be the same as well.
+ if (UNLIKELY(lhs->GetMappingTable() != rhs->GetMappingTable())) {
+ return lhs->GetMappingTable() < rhs->GetMappingTable();
+ }
+ if (UNLIKELY(lhs->GetVmapTable() != rhs->GetVmapTable())) {
+ return lhs->GetVmapTable() < rhs->GetVmapTable();
+ }
+ if (UNLIKELY(lhs->GetGcMap() != rhs->GetGcMap())) {
+ return lhs->GetGcMap() < rhs->GetGcMap();
+ }
+ const auto& lhs_patches = lhs->GetPatches();
+ const auto& rhs_patches = rhs->GetPatches();
+ if (UNLIKELY(lhs_patches.size() != rhs_patches.size())) {
+ return lhs_patches.size() < rhs_patches.size();
+ }
+ auto rit = rhs_patches.begin();
+ for (const LinkerPatch& lpatch : lhs_patches) {
+ if (UNLIKELY(!(lpatch == *rit))) {
+ return lpatch < *rit;
+ }
+ ++rit;
+ }
+ return false;
+ }
+ };
+
// 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_;
@@ -978,7 +1270,7 @@
bool result = OatDexMethodVisitor::EndClass();
if (oat_class_index_ == writer_->oat_classes_.size()) {
DCHECK(result); // OatDexMethodVisitor::EndClass() never fails.
- offset_ = writer_->relative_call_patcher_->WriteThunks(out_, offset_);
+ offset_ = writer_->relative_patcher_->WriteThunks(out_, offset_);
if (UNLIKELY(offset_ == 0u)) {
PLOG(ERROR) << "Failed to write final relative call thunks";
result = false;
@@ -1000,33 +1292,32 @@
if (quick_code != nullptr) {
// Need a wrapper if we create a copy for patching.
ArrayRef<const uint8_t> wrapped(*quick_code);
-
- offset_ = writer_->relative_call_patcher_->WriteThunks(out, offset_);
- if (offset_ == 0u) {
- ReportWriteFailure("relative call thunk", it);
- return false;
- }
- uint32_t aligned_offset = compiled_method->AlignCode(offset_);
- uint32_t aligned_code_delta = aligned_offset - offset_;
- if (aligned_code_delta != 0) {
- if (!writer_->WriteCodeAlignment(out, aligned_code_delta)) {
- ReportWriteFailure("code alignment padding", it);
- return false;
- }
- offset_ += aligned_code_delta;
- DCHECK_OFFSET_();
- }
- DCHECK_ALIGNED_PARAM(offset_,
- GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
uint32_t code_size = quick_code->size() * sizeof(uint8_t);
CHECK_NE(code_size, 0U);
// Deduplicate code arrays.
const OatMethodOffsets& method_offsets = oat_class->method_offsets_[method_offsets_index_];
- DCHECK(method_offsets.code_offset_ < offset_ || method_offsets.code_offset_ ==
- offset_ + sizeof(OatQuickMethodHeader) + compiled_method->CodeDelta())
- << PrettyMethod(it.GetMemberIndex(), *dex_file_);
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 aligned_offset = compiled_method->AlignCode(offset_);
+ uint32_t aligned_code_delta = aligned_offset - offset_;
+ if (aligned_code_delta != 0) {
+ if (!writer_->WriteCodeAlignment(out, aligned_code_delta)) {
+ ReportWriteFailure("code alignment padding", it);
+ return false;
+ }
+ offset_ += aligned_code_delta;
+ DCHECK_OFFSET_();
+ }
+ DCHECK_ALIGNED_PARAM(offset_,
+ GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+ DCHECK_EQ(method_offsets.code_offset_,
+ offset_ + sizeof(OatQuickMethodHeader) + compiled_method->CodeDelta())
+ << PrettyMethod(it.GetMemberIndex(), *dex_file_);
const OatQuickMethodHeader& method_header =
oat_class->method_headers_[method_offsets_index_];
writer_->oat_header_->UpdateChecksum(&method_header, sizeof(method_header));
@@ -1039,15 +1330,21 @@
DCHECK_OFFSET_();
if (!compiled_method->GetPatches().empty()) {
- patched_code_ = std::vector<uint8_t>(quick_code->begin(), quick_code->end());
+ patched_code_.assign(quick_code->begin(), quick_code->end());
wrapped = ArrayRef<const uint8_t>(patched_code_);
for (const LinkerPatch& patch : compiled_method->GetPatches()) {
if (patch.Type() == kLinkerPatchCallRelative) {
// NOTE: Relative calls across oat files are not supported.
uint32_t target_offset = GetTargetOffset(patch);
uint32_t literal_offset = patch.LiteralOffset();
- writer_->relative_call_patcher_->Patch(&patched_code_, literal_offset,
+ writer_->relative_patcher_->PatchCall(&patched_code_, literal_offset,
offset_ + literal_offset, target_offset);
+ } else if (patch.Type() == kLinkerPatchDexCacheArray) {
+ uint32_t target_offset = GetDexCacheOffset(patch);
+ uint32_t literal_offset = patch.LiteralOffset();
+ writer_->relative_patcher_->PatchDexCacheReference(&patched_code_, patch,
+ offset_ + literal_offset,
+ target_offset);
} else if (patch.Type() == kLinkerPatchCall) {
uint32_t target_offset = GetTargetOffset(patch);
PatchCodeAddress(&patched_code_, patch.LiteralOffset(), target_offset);
@@ -1134,6 +1431,18 @@
return type;
}
+ uint32_t GetDexCacheOffset(const LinkerPatch& patch) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (writer_->image_writer_ != nullptr) {
+ auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress(
+ patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset());
+ const uint8_t* oat_data = writer_->image_writer_->GetOatFileBegin() + file_offset_;
+ return reinterpret_cast<const uint8_t*>(element) - oat_data;
+ } else {
+ LOG(FATAL) << "Unimplemented.";
+ UNREACHABLE();
+ }
+ }
+
void PatchObjectAddress(std::vector<uint8_t>* code, uint32_t offset, mirror::Object* object)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// NOTE: Direct method pointers across oat files don't use linker patches. However, direct
@@ -1472,6 +1781,7 @@
DO_STAT(size_code_);
DO_STAT(size_code_alignment_);
DO_STAT(size_relative_call_thunks_);
+ DO_STAT(size_misc_thunks_);
DO_STAT(size_mapping_table_);
DO_STAT(size_vmap_table_);
DO_STAT(size_gc_map_);
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index fd2ccae..676d628 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -21,7 +21,6 @@
#include <cstddef>
#include <memory>
-#include "driver/compiler_driver.h"
#include "mem_map.h"
#include "method_reference.h"
#include "oat.h"
@@ -32,8 +31,10 @@
class BitVector;
class CompiledMethod;
+class CompilerDriver;
class ImageWriter;
class OutputStream;
+class TimingLogger;
// OatHeader variable length with count of D OatDexFiles
//
@@ -312,6 +313,7 @@
uint32_t size_code_;
uint32_t size_code_alignment_;
uint32_t size_relative_call_thunks_;
+ uint32_t size_misc_thunks_;
uint32_t size_mapping_table_;
uint32_t size_vmap_table_;
uint32_t size_gc_map_;
@@ -325,51 +327,20 @@
uint32_t size_oat_class_method_bitmaps_;
uint32_t size_oat_class_method_offsets_;
- class RelativeCallPatcher;
- class NoRelativeCallPatcher;
- class X86RelativeCallPatcher;
- class ArmBaseRelativeCallPatcher;
- class Thumb2RelativeCallPatcher;
- class Arm64RelativeCallPatcher;
+ class RelativePatcher;
+ class NoRelativePatcher;
+ class X86RelativePatcher;
+ class ArmBaseRelativePatcher;
+ class Thumb2RelativePatcher;
+ class Arm64RelativePatcher;
- std::unique_ptr<RelativeCallPatcher> relative_call_patcher_;
+ std::unique_ptr<RelativePatcher> relative_patcher_;
// The locations of absolute patches relative to the start of the executable section.
std::vector<uintptr_t> absolute_patch_locations_;
SafeMap<MethodReference, uint32_t, MethodReferenceComparator> method_offset_map_;
- struct CodeOffsetsKeyComparator {
- bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const {
- if (lhs->GetQuickCode() != rhs->GetQuickCode()) {
- return lhs->GetQuickCode() < rhs->GetQuickCode();
- }
- // If the code is the same, all other fields are likely to be the same as well.
- if (UNLIKELY(lhs->GetMappingTable() != rhs->GetMappingTable())) {
- return lhs->GetMappingTable() < rhs->GetMappingTable();
- }
- if (UNLIKELY(lhs->GetVmapTable() != rhs->GetVmapTable())) {
- return lhs->GetVmapTable() < rhs->GetVmapTable();
- }
- if (UNLIKELY(lhs->GetGcMap() != rhs->GetGcMap())) {
- return lhs->GetGcMap() < rhs->GetGcMap();
- }
- const auto& lhs_patches = lhs->GetPatches();
- const auto& rhs_patches = rhs->GetPatches();
- if (UNLIKELY(lhs_patches.size() != rhs_patches.size())) {
- return lhs_patches.size() < rhs_patches.size();
- }
- auto rit = rhs_patches.begin();
- for (const LinkerPatch& lpatch : lhs_patches) {
- if (UNLIKELY(!(lpatch == *rit))) {
- return lpatch < *rit;
- }
- ++rit;
- }
- return false;
- }
- };
-
DISALLOW_COPY_AND_ASSIGN(OatWriter);
};
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 2cdd5af..2da3176 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -23,6 +23,7 @@
#include "dex_instruction.h"
#include "dex_instruction-inl.h"
#include "driver/compiler_driver-inl.h"
+#include "driver/compiler_options.h"
#include "mirror/art_field.h"
#include "mirror/art_field-inl.h"
#include "mirror/class_loader.h"
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 8d0ca0b..0d5fe49 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -3146,7 +3146,7 @@
Location obj = locations->InAt(0);
if (obj.IsRegister()) {
- __ cmpl(obj.AsRegister<Register>(), Immediate(0));
+ __ testl(obj.AsRegister<Register>(), obj.AsRegister<Register>());
} else if (obj.IsStackSlot()) {
__ cmpl(Address(ESP, obj.GetStackIndex()), Immediate(0));
} else {
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index b2f9c65..e474c49 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -26,11 +26,13 @@
#include "bounds_check_elimination.h"
#include "builder.h"
#include "code_generator.h"
+#include "compiled_method.h"
#include "compiler.h"
#include "constant_folding.h"
#include "dead_code_elimination.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "driver/dex_compilation_unit.h"
#include "elf_writer_quick.h"
#include "graph_visualizer.h"
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index 7d0641e..9df8f56 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <iostream>
#include "parallel_move_resolver.h"
#include "nodes.h"
@@ -63,39 +64,42 @@
}
}
+Location LowOf(Location location) {
+ if (location.IsRegisterPair()) {
+ return Location::RegisterLocation(location.low());
+ } else if (location.IsFpuRegisterPair()) {
+ return Location::FpuRegisterLocation(location.low());
+ } else if (location.IsDoubleStackSlot()) {
+ return Location::StackSlot(location.GetStackIndex());
+ } else {
+ return Location::NoLocation();
+ }
+}
+
+Location HighOf(Location location) {
+ if (location.IsRegisterPair()) {
+ return Location::RegisterLocation(location.high());
+ } else if (location.IsFpuRegisterPair()) {
+ return Location::FpuRegisterLocation(location.high());
+ } else if (location.IsDoubleStackSlot()) {
+ return Location::StackSlot(location.GetHighStackIndex(4));
+ } else {
+ return Location::NoLocation();
+ }
+}
+
// Update the source of `move`, knowing that `updated_location` has been swapped
// with `new_source`. Note that `updated_location` can be a pair, therefore if
// `move` is non-pair, we need to extract which register to use.
static void UpdateSourceOf(MoveOperands* move, Location updated_location, Location new_source) {
Location source = move->GetSource();
- if (new_source.GetKind() == source.GetKind()) {
- DCHECK(updated_location.Equals(source));
+ if (LowOf(updated_location).Equals(source)) {
+ move->SetSource(LowOf(new_source));
+ } else if (HighOf(updated_location).Equals(source)) {
+ move->SetSource(HighOf(new_source));
+ } else {
+ DCHECK(updated_location.Equals(source)) << updated_location << " " << source;
move->SetSource(new_source);
- } else if (new_source.IsStackSlot()
- || new_source.IsDoubleStackSlot()
- || source.IsStackSlot()
- || source.IsDoubleStackSlot()) {
- // Stack slots never take part of a pair/non-pair swap.
- DCHECK(updated_location.Equals(source));
- move->SetSource(new_source);
- } else if (source.IsRegister()) {
- DCHECK(new_source.IsRegisterPair()) << new_source;
- DCHECK(updated_location.IsRegisterPair()) << updated_location;
- if (updated_location.low() == source.reg()) {
- move->SetSource(Location::RegisterLocation(new_source.low()));
- } else {
- DCHECK_EQ(updated_location.high(), source.reg());
- move->SetSource(Location::RegisterLocation(new_source.high()));
- }
- } else if (source.IsFpuRegister()) {
- DCHECK(new_source.IsFpuRegisterPair()) << new_source;
- DCHECK(updated_location.IsFpuRegisterPair()) << updated_location;
- if (updated_location.low() == source.reg()) {
- move->SetSource(Location::FpuRegisterLocation(new_source.low()));
- } else {
- DCHECK_EQ(updated_location.high(), source.reg());
- move->SetSource(Location::FpuRegisterLocation(new_source.high()));
- }
}
}
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 817a44b..5c502f7 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -31,8 +31,13 @@
message_ << "C";
} else if (location.IsPair()) {
message_ << location.low() << "," << location.high();
- } else {
+ } else if (location.IsRegister()) {
message_ << location.reg();
+ } else if (location.IsStackSlot()) {
+ message_ << location.GetStackIndex() << "(sp)";
+ } else {
+ message_ << "2x" << location.GetStackIndex() << "(sp)";
+ DCHECK(location.IsDoubleStackSlot()) << location;
}
}
@@ -279,6 +284,26 @@
resolver.EmitNativeCode(moves);
ASSERT_STREQ("(0,1 <-> 2,3)", resolver.GetMessage().c_str());
}
+
+ {
+ // Test involving registers used in single context and pair context.
+ TestParallelMoveResolver resolver(&allocator);
+ HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+ moves->AddMove(
+ Location::RegisterLocation(10),
+ Location::RegisterLocation(5),
+ nullptr);
+ moves->AddMove(
+ Location::RegisterPairLocation(4, 5),
+ Location::DoubleStackSlot(32),
+ nullptr);
+ moves->AddMove(
+ Location::DoubleStackSlot(32),
+ Location::RegisterPairLocation(10, 11),
+ nullptr);
+ resolver.EmitNativeCode(moves);
+ ASSERT_STREQ("(2x32(sp) <-> 10,11) (4,5 <-> 2x32(sp)) (4 -> 5)", resolver.GetMessage().c_str());
+ }
}
} // namespace art
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index cf38bd3..4bca434 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -1408,26 +1408,36 @@
// Walk over all uses covered by this interval, and update the location
// information.
- while (use != nullptr && use->GetPosition() <= current->GetEnd()) {
- LocationSummary* locations = use->GetUser()->GetLocations();
- if (use->GetIsEnvironment()) {
- locations->SetEnvironmentAt(use->GetInputIndex(), source);
- } else {
- Location expected_location = locations->InAt(use->GetInputIndex());
- // The expected (actual) location may be invalid in case the input is unused. Currently
- // this only happens for intrinsics.
- if (expected_location.IsValid()) {
- if (expected_location.IsUnallocated()) {
- locations->SetInAt(use->GetInputIndex(), source);
- } else if (!expected_location.IsConstant()) {
- AddInputMoveFor(interval->GetDefinedBy(), use->GetUser(), source, expected_location);
- }
- } else {
- DCHECK(use->GetUser()->IsInvoke());
- DCHECK(use->GetUser()->AsInvoke()->GetIntrinsic() != Intrinsics::kNone);
- }
+
+ LiveRange* range = current->GetFirstRange();
+ while (range != nullptr) {
+ while (use != nullptr && use->GetPosition() < range->GetStart()) {
+ DCHECK(use->GetIsEnvironment());
+ use = use->GetNext();
}
- use = use->GetNext();
+ while (use != nullptr && use->GetPosition() <= range->GetEnd()) {
+ DCHECK(current->Covers(use->GetPosition()) || (use->GetPosition() == range->GetEnd()));
+ LocationSummary* locations = use->GetUser()->GetLocations();
+ if (use->GetIsEnvironment()) {
+ locations->SetEnvironmentAt(use->GetInputIndex(), source);
+ } else {
+ Location expected_location = locations->InAt(use->GetInputIndex());
+ // The expected (actual) location may be invalid in case the input is unused. Currently
+ // this only happens for intrinsics.
+ if (expected_location.IsValid()) {
+ if (expected_location.IsUnallocated()) {
+ locations->SetInAt(use->GetInputIndex(), source);
+ } else if (!expected_location.IsConstant()) {
+ AddInputMoveFor(interval->GetDefinedBy(), use->GetUser(), source, expected_location);
+ }
+ } else {
+ DCHECK(use->GetUser()->IsInvoke());
+ DCHECK(use->GetUser()->AsInvoke()->GetIntrinsic() != Intrinsics::kNone);
+ }
+ }
+ use = use->GetNext();
+ }
+ range = range->GetNext();
}
// If the next interval starts just after this one, and has a register,
@@ -1503,7 +1513,15 @@
}
current = next_sibling;
} while (current != nullptr);
- DCHECK(use == nullptr);
+
+ if (kIsDebugBuild) {
+ // Following uses can only be environment uses. The location for
+ // these environments will be none.
+ while (use != nullptr) {
+ DCHECK(use->GetIsEnvironment());
+ use = use->GetNext();
+ }
+ }
}
void RegisterAllocator::ConnectSplitSiblings(LiveInterval* interval,
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 0f3973e..95da6ef 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -218,6 +218,26 @@
current->GetLiveInterval()->SetFrom(current->GetLifetimePosition());
}
+ // Process the environment first, because we know their uses come after
+ // or at the same liveness position of inputs.
+ if (current->HasEnvironment()) {
+ // Handle environment uses. See statements (b) and (c) of the
+ // SsaLivenessAnalysis.
+ HEnvironment* environment = current->GetEnvironment();
+ for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+ HInstruction* instruction = environment->GetInstructionAt(i);
+ bool should_be_live = ShouldBeLiveForEnvironment(instruction);
+ if (should_be_live) {
+ DCHECK(instruction->HasSsaIndex());
+ live_in->SetBit(instruction->GetSsaIndex());
+ }
+ if (instruction != nullptr) {
+ instruction->GetLiveInterval()->AddUse(
+ current, i, /* is_environment */ true, should_be_live);
+ }
+ }
+ }
+
// All inputs of an instruction must be live.
for (size_t i = 0, e = current->InputCount(); i < e; ++i) {
HInstruction* input = current->InputAt(i);
@@ -225,21 +245,7 @@
// to be materialized.
if (input->HasSsaIndex()) {
live_in->SetBit(input->GetSsaIndex());
- input->GetLiveInterval()->AddUse(current, i, false);
- }
- }
-
- if (current->HasEnvironment()) {
- // Handle environment uses. See statements (b) and (c) of the
- // SsaLivenessAnalysis.
- HEnvironment* environment = current->GetEnvironment();
- for (size_t i = 0, e = environment->Size(); i < e; ++i) {
- HInstruction* instruction = environment->GetInstructionAt(i);
- if (ShouldBeLiveForEnvironment(instruction)) {
- DCHECK(instruction->HasSsaIndex());
- live_in->SetBit(instruction->GetSsaIndex());
- instruction->GetLiveInterval()->AddUse(current, i, true);
- }
+ input->GetLiveInterval()->AddUse(current, i, /* is_environment */ false);
}
}
}
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index bc78dc2..d2da84c 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -189,7 +189,10 @@
AddRange(position, position + 1);
}
- void AddUse(HInstruction* instruction, size_t input_index, bool is_environment) {
+ void AddUse(HInstruction* instruction,
+ size_t input_index,
+ bool is_environment,
+ bool keep_alive = false) {
// Set the use within the instruction.
size_t position = instruction->GetLifetimePosition() + 1;
LocationSummary* locations = instruction->GetLocations();
@@ -211,6 +214,7 @@
&& (first_use_->GetPosition() < position)) {
// The user uses the instruction multiple times, and one use dies before the other.
// We update the use list so that the latter is first.
+ DCHECK(!is_environment);
UsePosition* cursor = first_use_;
while ((cursor->GetNext() != nullptr) && (cursor->GetNext()->GetPosition() < position)) {
cursor = cursor->GetNext();
@@ -225,6 +229,15 @@
return;
}
+ first_use_ = new (allocator_) UsePosition(
+ instruction, input_index, is_environment, position, first_use_);
+
+ if (is_environment && !keep_alive) {
+ // If this environment use does not keep the instruction live, it does not
+ // affect the live range of that instruction.
+ return;
+ }
+
size_t start_block_position = instruction->GetBlock()->GetLifetimeStart();
if (first_range_ == nullptr) {
// First time we see a use of that interval.
@@ -246,8 +259,6 @@
// and the check line 205 would succeed.
first_range_ = new (allocator_) LiveRange(start_block_position, position, first_range_);
}
- first_use_ = new (allocator_) UsePosition(
- instruction, input_index, is_environment, position, first_use_);
}
void AddPhiUse(HInstruction* instruction, size_t input_index, HBasicBlock* block) {
@@ -425,9 +436,11 @@
UsePosition* use = first_use_;
size_t end = GetEnd();
while (use != nullptr && use->GetPosition() <= end) {
- size_t use_position = use->GetPosition();
- if (use_position > position) {
- return use_position;
+ if (!use->GetIsEnvironment()) {
+ size_t use_position = use->GetPosition();
+ if (use_position > position) {
+ return use_position;
+ }
}
use = use->GetNext();
}
diff --git a/compiler/utils/dex_cache_arrays_layout-inl.h b/compiler/utils/dex_cache_arrays_layout-inl.h
new file mode 100644
index 0000000..7d02ce3
--- /dev/null
+++ b/compiler/utils/dex_cache_arrays_layout-inl.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 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_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
+#define ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
+
+#include "dex_cache_arrays_layout.h"
+
+#include "base/logging.h"
+#include "globals.h"
+#include "mirror/array-inl.h"
+#include "primitive.h"
+#include "utils.h"
+
+namespace mirror {
+class ArtField;
+class ArtMethod;
+class Class;
+class String;
+} // namespace mirror
+
+namespace art {
+
+inline DexCacheArraysLayout::DexCacheArraysLayout(const DexFile* dex_file)
+ : /* types_offset_ is always 0u */
+ methods_offset_(types_offset_ + ArraySize<mirror::Class>(dex_file->NumTypeIds())),
+ strings_offset_(methods_offset_ + ArraySize<mirror::ArtMethod>(dex_file->NumMethodIds())),
+ fields_offset_(strings_offset_ + ArraySize<mirror::String>(dex_file->NumStringIds())),
+ size_(fields_offset_ + ArraySize<mirror::ArtField>(dex_file->NumFieldIds())) {
+}
+
+inline size_t DexCacheArraysLayout::TypeOffset(uint32_t type_idx) const {
+ return types_offset_ + ElementOffset<mirror::Class>(type_idx);
+}
+
+inline size_t DexCacheArraysLayout::MethodOffset(uint32_t method_idx) const {
+ return methods_offset_ + ElementOffset<mirror::ArtMethod>(method_idx);
+}
+
+inline size_t DexCacheArraysLayout::StringOffset(uint32_t string_idx) const {
+ return strings_offset_ + ElementOffset<mirror::String>(string_idx);
+}
+
+inline size_t DexCacheArraysLayout::FieldOffset(uint32_t field_idx) const {
+ return fields_offset_ + ElementOffset<mirror::ArtField>(field_idx);
+}
+
+template <typename MirrorType>
+inline size_t DexCacheArraysLayout::ElementOffset(uint32_t idx) {
+ return mirror::Array::DataOffset(sizeof(mirror::HeapReference<MirrorType>)).Uint32Value() +
+ sizeof(mirror::HeapReference<MirrorType>) * idx;
+}
+
+template <typename MirrorType>
+inline size_t DexCacheArraysLayout::ArraySize(uint32_t num_elements) {
+ size_t array_size = mirror::ComputeArraySize(
+ num_elements, ComponentSizeShiftWidth<sizeof(mirror::HeapReference<MirrorType>)>());
+ DCHECK_NE(array_size, 0u); // No overflow expected for dex cache arrays.
+ return RoundUp(array_size, kObjectAlignment);
+}
+
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
diff --git a/compiler/utils/dex_cache_arrays_layout.h b/compiler/utils/dex_cache_arrays_layout.h
new file mode 100644
index 0000000..b461256
--- /dev/null
+++ b/compiler/utils/dex_cache_arrays_layout.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 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_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
+#define ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
+
+namespace art {
+
+/**
+ * @class DexCacheArraysLayout
+ * @details This class provides the layout information for the type, method, field and
+ * string arrays for a DexCache with a fixed arrays' layout (such as in the boot image),
+ */
+class DexCacheArraysLayout {
+ public:
+ // Construct an invalid layout.
+ DexCacheArraysLayout()
+ : /* types_offset_ is always 0u */
+ methods_offset_(0u),
+ strings_offset_(0u),
+ fields_offset_(0u),
+ size_(0u) {
+ }
+
+ // Construct a layout for a particular dex file.
+ explicit DexCacheArraysLayout(const DexFile* dex_file);
+
+ bool Valid() const {
+ return Size() != 0u;
+ }
+
+ size_t Size() const {
+ return size_;
+ }
+
+ size_t TypesOffset() const {
+ return types_offset_;
+ }
+
+ size_t TypeOffset(uint32_t type_idx) const;
+
+ size_t MethodsOffset() const {
+ return methods_offset_;
+ }
+
+ size_t MethodOffset(uint32_t method_idx) const;
+
+ size_t StringsOffset() const {
+ return strings_offset_;
+ }
+
+ size_t StringOffset(uint32_t string_idx) const;
+
+ size_t FieldsOffset() const {
+ return fields_offset_;
+ }
+
+ size_t FieldOffset(uint32_t field_idx) const;
+
+ private:
+ static constexpr size_t types_offset_ = 0u;
+ const size_t methods_offset_;
+ const size_t strings_offset_;
+ const size_t fields_offset_;
+ const size_t size_;
+
+ template <typename MirrorType>
+ static size_t ElementOffset(uint32_t idx);
+
+ template <typename MirrorType>
+ static size_t ArraySize(uint32_t num_elements);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index 3f15964..321cd75 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -46,8 +46,8 @@
# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
ifeq ($(ART_BUILD_HOST_NDEBUG),true)
- $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart-compiler libziparchive-host,art/compiler,host,ndebug,$(dex2oat_host_arch)))
+ $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler libziparchive-host,art/compiler,host,ndebug,$(dex2oat_host_arch)))
endif
ifeq ($(ART_BUILD_HOST_DEBUG),true)
- $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd-compiler libziparchive-host,art/compiler,host,debug,$(dex2oat_host_arch)))
+ $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libziparchive-host,art/compiler,host,debug,$(dex2oat_host_arch)))
endif
diff --git a/patchoat/Android.mk b/patchoat/Android.mk
index 1e16096..68ca923 100644
--- a/patchoat/Android.mk
+++ b/patchoat/Android.mk
@@ -38,8 +38,8 @@
# We always build patchoat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
ifeq ($(ART_BUILD_HOST_NDEBUG),true)
- $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,ndebug))
+ $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,host,ndebug))
endif
ifeq ($(ART_BUILD_HOST_DEBUG),true)
- $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,debug))
+ $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,host,debug))
endif
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index a1270dc..f8a9f9d 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -48,19 +48,23 @@
return nullptr;
}
}
- return new Arm64InstructionSetFeatures(smp, needs_a53_835769_fix);
+
+ // The variants that need a fix for 843419 are the same that need a fix for 835769.
+ bool needs_a53_843419_fix = needs_a53_835769_fix;
+
+ return new Arm64InstructionSetFeatures(smp, needs_a53_835769_fix, needs_a53_843419_fix);
}
const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromBitmap(uint32_t bitmap) {
bool smp = (bitmap & kSmpBitfield) != 0;
bool is_a53 = (bitmap & kA53Bitfield) != 0;
- return new Arm64InstructionSetFeatures(smp, is_a53);
+ return new Arm64InstructionSetFeatures(smp, is_a53, is_a53);
}
const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromCppDefines() {
const bool smp = true;
const bool is_a53 = true; // Pessimistically assume all ARM64s are A53s.
- return new Arm64InstructionSetFeatures(smp, is_a53);
+ return new Arm64InstructionSetFeatures(smp, is_a53, is_a53);
}
const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromCpuInfo() {
@@ -85,13 +89,13 @@
} else {
LOG(ERROR) << "Failed to open /proc/cpuinfo";
}
- return new Arm64InstructionSetFeatures(smp, is_a53);
+ return new Arm64InstructionSetFeatures(smp, is_a53, is_a53);
}
const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromHwcap() {
bool smp = sysconf(_SC_NPROCESSORS_CONF) > 1;
const bool is_a53 = true; // Pessimistically assume all ARM64s are A53s.
- return new Arm64InstructionSetFeatures(smp, is_a53);
+ return new Arm64InstructionSetFeatures(smp, is_a53, is_a53);
}
const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromAssembly() {
@@ -140,7 +144,7 @@
return nullptr;
}
}
- return new Arm64InstructionSetFeatures(smp, is_a53);
+ return new Arm64InstructionSetFeatures(smp, is_a53, is_a53);
}
} // namespace art
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.h b/runtime/arch/arm64/instruction_set_features_arm64.h
index f6bfee7..3b3e2c9 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.h
+++ b/runtime/arch/arm64/instruction_set_features_arm64.h
@@ -61,6 +61,11 @@
return fix_cortex_a53_835769_;
}
+ // Generate code addressing Cortex-A53 erratum 843419?
+ bool NeedFixCortexA53_843419() const {
+ return fix_cortex_a53_843419_;
+ }
+
// TODO: Tune this on a per CPU basis. For now, we pessimistically assume
// that all ARM64 CPUs prefer explicit memory barriers over acquire-release.
//
@@ -79,8 +84,12 @@
std::string* error_msg) const OVERRIDE;
private:
- explicit Arm64InstructionSetFeatures(bool smp, bool needs_a53_835769_fix)
- : InstructionSetFeatures(smp), fix_cortex_a53_835769_(needs_a53_835769_fix) {
+ explicit Arm64InstructionSetFeatures(bool smp,
+ bool needs_a53_835769_fix,
+ bool needs_a53_843419_fix)
+ : InstructionSetFeatures(smp),
+ fix_cortex_a53_835769_(needs_a53_835769_fix),
+ fix_cortex_a53_843419_(needs_a53_843419_fix) {
}
// Bitmap positions for encoding features as a bitmap.
@@ -90,6 +99,7 @@
};
const bool fix_cortex_a53_835769_;
+ const bool fix_cortex_a53_843419_;
DISALLOW_COPY_AND_ASSIGN(Arm64InstructionSetFeatures);
};
diff --git a/runtime/gc/accounting/mod_union_table_test.cc b/runtime/gc/accounting/mod_union_table_test.cc
index 7780935..94bb3f5 100644
--- a/runtime/gc/accounting/mod_union_table_test.cc
+++ b/runtime/gc/accounting/mod_union_table_test.cc
@@ -47,7 +47,7 @@
Thread* self, space::ContinuousMemMapAllocSpace* space, size_t component_count)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
auto* klass = GetObjectArrayClass(self, space);
- const size_t size = ComputeArraySize(self, klass, component_count, 2);
+ const size_t size = mirror::ComputeArraySize(component_count, 2);
size_t bytes_allocated = 0, bytes_tl_bulk_allocated;
auto* obj = down_cast<mirror::ObjectArray<mirror::Object>*>(
space->Alloc(self, size, &bytes_allocated, nullptr, &bytes_tl_bulk_allocated));
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 51cf558..7e967f9 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2056,7 +2056,11 @@
}
void Heap::PreZygoteFork() {
- CollectGarbageInternal(collector::kGcTypeFull, kGcCauseBackground, false);
+ if (!HasZygoteSpace()) {
+ // We still want to GC in case there is some unreachable non moving objects that could cause a
+ // suboptimal bin packing when we compact the zygote space.
+ CollectGarbageInternal(collector::kGcTypeFull, kGcCauseBackground, false);
+ }
Thread* self = Thread::Current();
MutexLock mu(self, zygote_creation_lock_);
// Try to see if we have any Zygote spaces.
@@ -2125,7 +2129,7 @@
// Update the end and write out image.
non_moving_space_->SetEnd(target_space.End());
non_moving_space_->SetLimit(target_space.Limit());
- VLOG(heap) << "Zygote space size " << non_moving_space_->Size() << " bytes";
+ VLOG(heap) << "Create zygote space with size=" << non_moving_space_->Size() << " bytes";
}
// Change the collector to the post zygote one.
ChangeCollector(foreground_collector_type_);
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index b822613..656569c 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -507,6 +507,7 @@
Env env = { this, output };
runtime->VisitRoots(RootVisitor, &env);
+ runtime->VisitImageRoots(RootVisitor, &env);
runtime->GetHeap()->VisitObjectsPaused(VisitObjectCallback, &env);
output->StartNewRecord(HPROF_TAG_HEAP_DUMP_END, kHprofTime);
diff --git a/runtime/leb128.h b/runtime/leb128.h
index dfb42b8..d36b690 100644
--- a/runtime/leb128.h
+++ b/runtime/leb128.h
@@ -124,6 +124,18 @@
return dest;
}
+template<typename Allocator>
+static inline void EncodeUnsignedLeb128(std::vector<uint8_t, Allocator>* dest, uint32_t value) {
+ uint8_t out = value & 0x7f;
+ value >>= 7;
+ while (value != 0) {
+ dest->push_back(out | 0x80);
+ out = value & 0x7f;
+ value >>= 7;
+ }
+ dest->push_back(out);
+}
+
static inline uint8_t* EncodeSignedLeb128(uint8_t* dest, int32_t value) {
uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6;
uint8_t out = value & 0x7f;
@@ -137,6 +149,19 @@
return dest;
}
+template<typename Allocator>
+static inline void EncodeSignedLeb128(std::vector<uint8_t, Allocator>* dest, int32_t value) {
+ uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6;
+ uint8_t out = value & 0x7f;
+ while (extra_bits != 0u) {
+ dest->push_back(out | 0x80);
+ value >>= 7;
+ out = value & 0x7f;
+ extra_bits >>= 7;
+ }
+ dest->push_back(out);
+}
+
// An encoder that pushed uint32_t data onto the given std::vector.
class Leb128Encoder {
public:
@@ -149,14 +174,7 @@
}
void PushBackUnsigned(uint32_t value) {
- uint8_t out = value & 0x7f;
- value >>= 7;
- while (value != 0) {
- data_->push_back(out | 0x80);
- out = value & 0x7f;
- value >>= 7;
- }
- data_->push_back(out);
+ EncodeUnsignedLeb128(data_, value);
}
template<typename It>
@@ -167,15 +185,7 @@
}
void PushBackSigned(int32_t value) {
- uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6;
- uint8_t out = value & 0x7f;
- while (extra_bits != 0u) {
- data_->push_back(out | 0x80);
- value >>= 7;
- out = value & 0x7f;
- extra_bits >>= 7;
- }
- data_->push_back(out);
+ EncodeSignedLeb128(data_, value);
}
template<typename It>
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 048d8ba..7f04992 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -64,25 +64,20 @@
return true;
}
-static inline size_t ComputeArraySize(Thread* self, Class* array_class, int32_t component_count,
- size_t component_size_shift)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(array_class != NULL);
+static inline size_t ComputeArraySize(int32_t component_count, size_t component_size_shift) {
DCHECK_GE(component_count, 0);
- DCHECK(array_class->IsArrayClass());
size_t component_size = 1U << component_size_shift;
size_t header_size = Array::DataOffset(component_size).SizeValue();
size_t data_size = static_cast<size_t>(component_count) << component_size_shift;
size_t size = header_size + data_size;
- // Check for size_t overflow and throw OutOfMemoryError if this was
- // an unreasonable request.
+ // Check for size_t overflow if this was an unreasonable request
+ // but let the caller throw OutOfMemoryError.
#ifdef __LP64__
// 64-bit. No overflow as component_count is 32-bit and the maximum
// component size is 8.
DCHECK_LE((1U << component_size_shift), 8U);
- UNUSED(self);
#else
// 32-bit.
DCHECK_NE(header_size, 0U);
@@ -90,9 +85,6 @@
// The array length limit (exclusive).
const size_t length_limit = (0U - header_size) >> component_size_shift;
if (UNLIKELY(length_limit <= static_cast<size_t>(component_count))) {
- self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
- PrettyDescriptor(array_class).c_str(),
- component_count).c_str());
return 0; // failure
}
#endif
@@ -159,15 +151,20 @@
inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count,
size_t component_size_shift, gc::AllocatorType allocator_type) {
DCHECK(allocator_type != gc::kAllocatorTypeLOS);
+ DCHECK(array_class != nullptr);
+ DCHECK(array_class->IsArrayClass());
DCHECK_EQ(array_class->GetComponentSizeShift(), component_size_shift);
DCHECK_EQ(array_class->GetComponentSize(), (1U << component_size_shift));
- size_t size = ComputeArraySize(self, array_class, component_count, component_size_shift);
+ size_t size = ComputeArraySize(component_count, component_size_shift);
#ifdef __LP64__
// 64-bit. No size_t overflow.
DCHECK_NE(size, 0U);
#else
// 32-bit.
if (UNLIKELY(size == 0)) {
+ self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
+ PrettyDescriptor(array_class).c_str(),
+ component_count).c_str());
return nullptr;
}
#endif
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 0422fcd..a8b0876 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -210,29 +210,29 @@
// image in case of the GSS collector.
+ 384 * MB;
- std::string error_msg;
std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true));
ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map";
for (BacktraceMap::const_iterator it = map->begin();
reservation_start < reservation_end && it != map->end(); ++it) {
- if (it->end <= reservation_start) {
- continue;
- }
+ ReserveImageSpaceChunk(reservation_start, std::min(it->start, reservation_end));
+ reservation_start = std::max(reservation_start, it->end);
+ }
+ ReserveImageSpaceChunk(reservation_start, reservation_end);
+ }
- if (it->start < reservation_start) {
- reservation_start = std::min(reservation_end, it->end);
- }
-
+ // Reserve a chunk of memory for the image space in the given range.
+ // Only has effect for chunks with a positive number of bytes.
+ void ReserveImageSpaceChunk(uintptr_t start, uintptr_t end) {
+ if (start < end) {
+ std::string error_msg;
image_reservation_.push_back(std::unique_ptr<MemMap>(
MemMap::MapAnonymous("image reservation",
- reinterpret_cast<uint8_t*>(reservation_start),
- std::min(it->start, reservation_end) - reservation_start,
+ reinterpret_cast<uint8_t*>(start), end - start,
PROT_NONE, false, false, &error_msg)));
ASSERT_TRUE(image_reservation_.back().get() != nullptr) << error_msg;
LOG(INFO) << "Reserved space for image " <<
reinterpret_cast<void*>(image_reservation_.back()->Begin()) << "-" <<
reinterpret_cast<void*>(image_reservation_.back()->End());
- reservation_start = it->end;
}
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 23a7db6..b5d2e15 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -62,7 +62,7 @@
#include "gc/accounting/card_table-inl.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
-#include "gc/space/space.h"
+#include "gc/space/space-inl.h"
#include "handle_scope-inl.h"
#include "image.h"
#include "instrumentation.h"
@@ -1355,6 +1355,23 @@
VisitConcurrentRoots(callback, arg, flags);
}
+void Runtime::VisitImageRoots(RootCallback* callback, void* arg) {
+ for (auto* space : GetHeap()->GetContinuousSpaces()) {
+ if (space->IsImageSpace()) {
+ auto* image_space = space->AsImageSpace();
+ const auto& image_header = image_space->GetImageHeader();
+ for (size_t i = 0; i < ImageHeader::kImageRootsMax; ++i) {
+ auto* obj = image_header.GetImageRoot(static_cast<ImageHeader::ImageRoot>(i));
+ if (obj != nullptr) {
+ auto* after_obj = obj;
+ callback(&after_obj, arg, RootInfo(kRootStickyClass));
+ CHECK_EQ(after_obj, obj);
+ }
+ }
+ }
+ }
+}
+
mirror::ObjectArray<mirror::ArtMethod>* Runtime::CreateDefaultImt(ClassLinker* cl) {
Thread* self = Thread::Current();
StackHandleScope<1> hs(self);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 085335f..64b7183 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -298,6 +298,10 @@
void VisitRoots(RootCallback* visitor, void* arg, VisitRootFlags flags = kVisitRootFlagAllRoots)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Visit image roots, only used for hprof since the GC uses the image space mod union table
+ // instead.
+ void VisitImageRoots(RootCallback* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Visit all of the roots we can do safely do concurrently.
void VisitConcurrentRoots(RootCallback* visitor, void* arg,
VisitRootFlags flags = kVisitRootFlagAllRoots)
diff --git a/test/466-get-live-vreg/expected.txt b/test/466-get-live-vreg/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/466-get-live-vreg/expected.txt
diff --git a/test/466-get-live-vreg/get_live_vreg_jni.cc b/test/466-get-live-vreg/get_live_vreg_jni.cc
new file mode 100644
index 0000000..6715ba1
--- /dev/null
+++ b/test/466-get-live-vreg/get_live_vreg_jni.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 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 "arch/context.h"
+#include "jni.h"
+#include "mirror/art_method-inl.h"
+#include "scoped_thread_state_change.h"
+#include "stack.h"
+#include "thread.h"
+
+namespace art {
+
+namespace {
+
+class TestVisitor : public StackVisitor {
+ public:
+ TestVisitor(Thread* thread, Context* context) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : StackVisitor(thread, context) {}
+
+ bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* m = GetMethod();
+ std::string m_name(m->GetName());
+
+ if (m_name.compare("testLiveArgument") == 0) {
+ found_method_ = true;
+ uint32_t value = 0;
+ CHECK(GetVReg(m, 0, kIntVReg, &value));
+ CHECK_EQ(value, 42u);
+ } else if (m_name.compare("testIntervalHole") == 0) {
+ found_method_ = true;
+ uint32_t value = 0;
+ if (m->IsOptimized(sizeof(void*))) {
+ CHECK_EQ(GetVReg(m, 0, kIntVReg, &value), false);
+ } else {
+ CHECK(GetVReg(m, 0, kIntVReg, &value));
+ CHECK_EQ(value, 1u);
+ }
+ }
+
+ return true;
+ }
+
+ // Value returned to Java to ensure the methods testSimpleVReg and testPairVReg
+ // have been found and tested.
+ bool found_method_ = false;
+};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_doStaticNativeCallLiveVreg(JNIEnv*, jclass) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<Context> context(Context::Create());
+ TestVisitor visitor(soa.Self(), context.get());
+ visitor.WalkStack();
+ CHECK(visitor.found_method_);
+}
+
+} // namespace
+
+} // namespace art
diff --git a/test/466-get-live-vreg/info.txt b/test/466-get-live-vreg/info.txt
new file mode 100644
index 0000000..cbe0c9a
--- /dev/null
+++ b/test/466-get-live-vreg/info.txt
@@ -0,0 +1,3 @@
+Tests for inspecting live DEX registers. The test must
+also pass for non-debuggable, as we need to know live DEX registers
+when de-opting.
diff --git a/test/466-get-live-vreg/src/Main.java b/test/466-get-live-vreg/src/Main.java
new file mode 100644
index 0000000..3118085
--- /dev/null
+++ b/test/466-get-live-vreg/src/Main.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public Main() {
+ }
+
+ static int testLiveArgument(int arg) {
+ doStaticNativeCallLiveVreg();
+ return arg;
+ }
+
+ static void moveArgToCalleeSave() {
+ try {
+ Thread.sleep(0);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+ static void testIntervalHole(int arg, boolean test) {
+ // Move the argument to callee save to ensure it is in
+ // a readable register.
+ moveArgToCalleeSave();
+ if (test) {
+ staticField1 = arg;
+ // The environment use of `arg` should not make it live.
+ doStaticNativeCallLiveVreg();
+ } else {
+ staticField2 = arg;
+ // The environment use of `arg` should not make it live.
+ doStaticNativeCallLiveVreg();
+ }
+ }
+
+ static native void doStaticNativeCallLiveVreg();
+
+ static {
+ System.loadLibrary("arttest");
+ }
+
+ public static void main(String[] args) {
+ if (testLiveArgument(42) != 42) {
+ throw new Error("Expected 42");
+ }
+
+ if (testLiveArgument(42) != 42) {
+ throw new Error("Expected 42");
+ }
+
+ testIntervalHole(1, true);
+ testIntervalHole(1, false);
+ }
+
+ static int staticField1;
+ static int staticField2;
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 0cafb06..5e768ee 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -31,7 +31,8 @@
454-get-vreg/get_vreg_jni.cc \
455-set-vreg/set_vreg_jni.cc \
457-regs/regs_jni.cc \
- 461-get-reference-vreg/get_reference_vreg_jni.cc
+ 461-get-reference-vreg/get_reference_vreg_jni.cc \
+ 466-get-live-vreg/get_live_vreg_jni.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
ifdef TARGET_2ND_ARCH
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 28fbc3e..39afc67 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -313,6 +313,7 @@
455-set-vreg \
457-regs \
461-get-reference-vreg \
+ 466-get-live-vreg \
ifneq (,$(filter ndebug,$(RUN_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),ndebug,$(PREBUILD_TYPES), \