Merge "ART: Fix unchecked register index validity"
diff --git a/Android.mk b/Android.mk
index 15e8308..669939b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -355,7 +355,7 @@
build-art-target: $(ART_TARGET_EXECUTABLES) $(ART_TARGET_GTEST_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so
########################################################################
-# "m art-host" for just building the files needed to run the art script
+# "m art-host" for just building the files needed to run the art script on the host.
.PHONY: art-host
ifeq ($(HOST_PREFER_32_BIT),true)
art-host: $(HOST_OUT_EXECUTABLES)/art $(HOST_OUT)/bin/dalvikvm32 $(HOST_OUT)/lib/libart.so $(HOST_OUT)/bin/dex2oat $(HOST_OUT)/bin/patchoat $(HOST_CORE_IMG_OUT) $(HOST_OUT)/lib/libjavacore.so $(HOST_OUT)/bin/dalvikvm
@@ -363,6 +363,11 @@
art-host: $(HOST_OUT_EXECUTABLES)/art $(HOST_OUT)/bin/dalvikvm64 $(HOST_OUT)/bin/dalvikvm32 $(HOST_OUT)/lib/libart.so $(HOST_OUT)/bin/dex2oat $(HOST_OUT)/bin/patchoat $(HOST_CORE_IMG_OUT) $(HOST_OUT)/lib/libjavacore.so $(HOST_OUT)/lib64/libjavacore.so $(HOST_OUT)/bin/dalvikvm
endif
+# "m art-target" for just building the files needed to run the art script on the target.
+# Note that the script can run a dalvikvm executable that is not necessarily /system/bin/dalvikvm.
+.PHONY: art-target
+art-target: adb build-art-target art libnativehelper libjavacore
+
.PHONY: art-host-debug
art-host-debug: art-host $(HOST_OUT)/lib/libartd.so $(HOST_OUT)/bin/dex2oatd $(HOST_OUT)/bin/patchoatd
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
index f9a78be..ba5bd30 100644
--- a/compiler/compiled_method.cc
+++ b/compiler/compiled_method.cc
@@ -164,6 +164,25 @@
CompiledMethod::CompiledMethod(CompilerDriver* driver,
InstructionSet instruction_set,
+ const std::vector<uint8_t>& quick_code,
+ const size_t frame_size_in_bytes,
+ const uint32_t core_spill_mask,
+ const uint32_t fp_spill_mask,
+ const std::vector<uint8_t>& mapping_table,
+ const std::vector<uint8_t>& stack_map)
+ : CompiledCode(driver, instruction_set, quick_code),
+ frame_size_in_bytes_(frame_size_in_bytes),
+ core_spill_mask_(core_spill_mask),
+ fp_spill_mask_(fp_spill_mask),
+ src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
+ mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
+ vmap_table_(driver->DeduplicateVMapTable(stack_map)),
+ gc_map_(nullptr),
+ cfi_info_(nullptr) {
+}
+
+CompiledMethod::CompiledMethod(CompilerDriver* driver,
+ InstructionSet instruction_set,
const std::vector<uint8_t>& code,
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index 36f4745..3e34144 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -173,7 +173,7 @@
class CompiledMethod FINAL : public CompiledCode {
public:
- // Constructs a CompiledMethod for the non-LLVM compilers.
+ // Constructs a CompiledMethod for Quick.
CompiledMethod(CompilerDriver* driver,
InstructionSet instruction_set,
const std::vector<uint8_t>& quick_code,
@@ -186,6 +186,16 @@
const std::vector<uint8_t>& native_gc_map,
const std::vector<uint8_t>* cfi_info);
+ // Constructs a CompiledMethod for Optimizing.
+ CompiledMethod(CompilerDriver* driver,
+ InstructionSet instruction_set,
+ const std::vector<uint8_t>& quick_code,
+ const size_t frame_size_in_bytes,
+ const uint32_t core_spill_mask,
+ const uint32_t fp_spill_mask,
+ const std::vector<uint8_t>& mapping_table,
+ const std::vector<uint8_t>& vmap_table);
+
// Constructs a CompiledMethod for the QuickJniCompiler.
CompiledMethod(CompilerDriver* driver,
InstructionSet instruction_set,
@@ -232,9 +242,8 @@
return *vmap_table_;
}
- const std::vector<uint8_t>& GetGcMap() const {
- DCHECK(gc_map_ != nullptr);
- return *gc_map_;
+ std::vector<uint8_t> const* GetGcMap() const {
+ return gc_map_;
}
const std::vector<uint8_t>* GetCFIInfo() const {
diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h
index 6272555..b95789e 100644
--- a/compiler/dex/quick/arm/arm_lir.h
+++ b/compiler/dex/quick/arm/arm_lir.h
@@ -556,22 +556,24 @@
// Instruction assembly field_loc kind.
enum ArmEncodingKind {
- kFmtUnused, // Unused field and marks end of formats.
- kFmtBitBlt, // Bit string using end/start.
- kFmtDfp, // Double FP reg.
- kFmtSfp, // Single FP reg.
- kFmtModImm, // Shifted 8-bit immed using [26,14..12,7..0].
- kFmtImm16, // Zero-extended immed using [26,19..16,14..12,7..0].
- kFmtImm6, // Encoded branch target using [9,7..3]0.
- kFmtImm12, // Zero-extended immediate using [26,14..12,7..0].
- kFmtShift, // Shift descriptor, [14..12,7..4].
- kFmtLsb, // least significant bit using [14..12][7..6].
- kFmtBWidth, // bit-field width, encoded as width-1.
- kFmtShift5, // Shift count, [14..12,7..6].
- kFmtBrOffset, // Signed extended [26,11,13,21-16,10-0]:0.
- kFmtFPImm, // Encoded floating point immediate.
- kFmtOff24, // 24-bit Thumb2 unconditional branch encoding.
- kFmtSkip, // Unused field, but continue to next.
+ kFmtUnused, // Unused field and marks end of formats.
+ kFmtBitBlt, // Bit string using end/start.
+ kFmtLdmRegList, // Load multiple register list using [15,14,12..0].
+ kFmtStmRegList, // Store multiple register list using [14,12..0].
+ kFmtDfp, // Double FP reg.
+ kFmtSfp, // Single FP reg.
+ kFmtModImm, // Shifted 8-bit immed using [26,14..12,7..0].
+ kFmtImm16, // Zero-extended immed using [26,19..16,14..12,7..0].
+ kFmtImm6, // Encoded branch target using [9,7..3]0.
+ kFmtImm12, // Zero-extended immediate using [26,14..12,7..0].
+ kFmtShift, // Shift descriptor, [14..12,7..4].
+ kFmtLsb, // least significant bit using [14..12][7..6].
+ kFmtBWidth, // bit-field width, encoded as width-1.
+ kFmtShift5, // Shift count, [14..12,7..6].
+ kFmtBrOffset, // Signed extended [26,11,13,21-16,10-0]:0.
+ kFmtFPImm, // Encoded floating point immediate.
+ kFmtOff24, // 24-bit Thumb2 unconditional branch encoding.
+ kFmtSkip, // Unused field, but continue to next.
};
// Struct used to define the snippet positions for each Thumb opcode.
diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc
index 35c3597..06d9dd5 100644
--- a/compiler/dex/quick/arm/assemble_arm.cc
+++ b/compiler/dex/quick/arm/assemble_arm.cc
@@ -560,12 +560,12 @@
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1 | IS_MOVE,
"vmov.f64 ", " !0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Ldmia, 0xe8900000,
- kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtBitBlt, 19, 16, kFmtLdmRegList, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
"ldmia", "!0C!!, <!1R>", 4, kFixupNone),
ENCODING_MAP(kThumb2Stmia, 0xe8800000,
- kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtBitBlt, 19, 16, kFmtStmRegList, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_USE_LIST1 | IS_STORE,
"stmia", "!0C!!, <!1R>", 4, kFixupNone),
@@ -935,7 +935,7 @@
IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD_OFF,
"ldr", "!0C, [r15pc, -#!1d]", 4, kFixupNone),
ENCODING_MAP(kThumb2Stm, 0xe9000000,
- kFmtBitBlt, 19, 16, kFmtBitBlt, 12, 0, kFmtUnused, -1, -1,
+ kFmtBitBlt, 19, 16, kFmtStmRegList, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_USE0 | REG_USE_LIST1 | IS_STORE,
"stm", "!0C, <!1R>", 4, kFixupNone),
@@ -992,7 +992,7 @@
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0 | NEEDS_FIXUP,
"movt", "!0C, #!1M", 4, kFixupMovImmHST),
ENCODING_MAP(kThumb2LdmiaWB, 0xe8b00000,
- kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtBitBlt, 19, 16, kFmtLdmRegList, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
"ldmia", "!0C!!, <!1R>", 4, kFixupNone),
@@ -1094,6 +1094,19 @@
bits |= value;
} else {
switch (encoder->field_loc[i].kind) {
+ case kFmtLdmRegList:
+ value = (operand << encoder->field_loc[i].start) &
+ ((1 << (encoder->field_loc[i].end + 1)) - 1);
+ bits |= value;
+ DCHECK_EQ((bits & (1 << 13)), 0u);
+ break;
+ case kFmtStmRegList:
+ value = (operand << encoder->field_loc[i].start) &
+ ((1 << (encoder->field_loc[i].end + 1)) - 1);
+ bits |= value;
+ DCHECK_EQ((bits & (1 << 13)), 0u);
+ DCHECK_EQ((bits & (1 << 15)), 0u);
+ break;
case kFmtSkip:
break; // Nothing to do, but continue to next.
case kFmtUnused:
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index e1d3241..78f5c73 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -1107,9 +1107,12 @@
RegLocation Mir2Lir::InlineTarget(CallInfo* info) {
RegLocation res;
if (info->result.location == kLocInvalid) {
- res = GetReturn(LocToRegClass(info->result));
+ // If result is unused, return a sink target based on type of invoke target.
+ res = GetReturn(ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
} else {
res = info->result;
+ DCHECK_EQ(LocToRegClass(res),
+ ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
}
return res;
}
@@ -1117,9 +1120,12 @@
RegLocation Mir2Lir::InlineTargetWide(CallInfo* info) {
RegLocation res;
if (info->result.location == kLocInvalid) {
- res = GetReturnWide(kCoreReg);
+ // If result is unused, return a sink target based on type of invoke target.
+ res = GetReturnWide(ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
} else {
res = info->result;
+ DCHECK_EQ(LocToRegClass(res),
+ ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
}
return res;
}
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index ef2d9a6..3ca85bf 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -692,33 +692,27 @@
Clobber(rs_r2);
LockTemp(rs_r2);
- // Assume that the result will be in EDX.
- rl_result = {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, rs_r2, INVALID_SREG, INVALID_SREG};
+ // Assume that the result will be in EDX for divide, and EAX for remainder.
+ rl_result = {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, is_div ? rs_r2 : rs_r0,
+ INVALID_SREG, INVALID_SREG};
- // Numerator into EAX.
- RegStorage numerator_reg;
- if (!is_div || (imm > 0 && magic < 0) || (imm < 0 && magic > 0)) {
- // We will need the value later.
- rl_src = LoadValue(rl_src, kCoreReg);
- numerator_reg = rl_src.reg;
- OpRegCopy(rs_r0, numerator_reg);
- } else {
- // Only need this once. Just put it into EAX.
- LoadValueDirectFixed(rl_src, rs_r0);
- }
+ // We need the value at least twice. Load into a temp.
+ rl_src = LoadValue(rl_src, kCoreReg);
+ RegStorage numerator_reg = rl_src.reg;
- // Check if numerator is 0
- OpRegImm(kOpCmp, rs_r0, 0);
+ // Check if numerator is 0.
+ OpRegImm(kOpCmp, numerator_reg, 0);
LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
- LoadConstantNoClobber(rs_r2, 0);
+ // Return result 0 if numerator was 0.
+ LoadConstantNoClobber(rl_result.reg, 0);
LIR* done = NewLIR1(kX86Jmp8, 0);
branch->target = NewLIR0(kPseudoTargetLabel);
- // EDX = magic.
- LoadConstantNoClobber(rs_r2, magic);
+ // EAX = magic.
+ LoadConstant(rs_r0, magic);
- // EDX:EAX = magic & dividend.
- NewLIR1(kX86Imul32DaR, rs_r2.GetReg());
+ // EDX:EAX = magic * numerator.
+ NewLIR1(kX86Imul32DaR, numerator_reg.GetReg());
if (imm > 0 && magic < 0) {
// Add numerator to EDX.
@@ -756,11 +750,10 @@
// EAX = numerator * imm.
OpRegRegImm(kOpMul, rs_r2, rs_r2, imm);
- // EDX -= EAX.
+ // EAX -= EDX.
NewLIR2(kX86Sub32RR, rs_r0.GetReg(), rs_r2.GetReg());
// For this case, return the result in EAX.
- rl_result.reg.SetReg(r0);
}
done->target = NewLIR0(kPseudoTargetLabel);
}
@@ -2045,7 +2038,8 @@
Clobber(rs_r2q);
LockTemp(rs_r2q);
- RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, rs_r2q, INVALID_SREG, INVALID_SREG};
+ RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1,
+ is_div ? rs_r2q : rs_r0q, INVALID_SREG, INVALID_SREG};
// Use H.S.Warren's Hacker's Delight Chapter 10 and
// T,Grablund, P.L.Montogomery's Division by invariant integers using multiplication.
@@ -2069,24 +2063,35 @@
* 5. Thus, RDX is the quotient
*/
- // Numerator into RAX.
+ // RAX = magic.
+ LoadConstantWide(rs_r0q, magic);
+
+ // Multiply by numerator.
RegStorage numerator_reg;
if (!is_div || (imm > 0 && magic < 0) || (imm < 0 && magic > 0)) {
// We will need the value later.
rl_src = LoadValueWide(rl_src, kCoreReg);
numerator_reg = rl_src.reg;
- OpRegCopyWide(rs_r0q, numerator_reg);
+
+ // RDX:RAX = magic * numerator.
+ NewLIR1(kX86Imul64DaR, numerator_reg.GetReg());
} else {
- // Only need this once. Just put it into RAX.
- LoadValueDirectWideFixed(rl_src, rs_r0q);
+ // Only need this once. Multiply directly from the value.
+ rl_src = UpdateLocWideTyped(rl_src, kCoreReg);
+ if (rl_src.location != kLocPhysReg) {
+ // Okay, we can do this from memory.
+ ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+ int displacement = SRegOffset(rl_src.s_reg_low);
+ // RDX:RAX = magic * numerator.
+ LIR *m = NewLIR2(kX86Imul64DaM, rs_rX86_SP.GetReg(), displacement);
+ AnnotateDalvikRegAccess(m, displacement >> 2,
+ true /* is_load */, true /* is_64bit */);
+ } else {
+ // RDX:RAX = magic * numerator.
+ NewLIR1(kX86Imul64DaR, rl_src.reg.GetReg());
+ }
}
- // RDX = magic.
- LoadConstantWide(rs_r2q, magic);
-
- // RDX:RAX = magic & dividend.
- NewLIR1(kX86Imul64DaR, rs_r2q.GetReg());
-
if (imm > 0 && magic < 0) {
// Add numerator to RDX.
DCHECK(numerator_reg.Valid());
@@ -2134,14 +2139,12 @@
NewLIR3(kX86Imul64RRI, rs_r2q.GetReg(), rs_r2q.GetReg(), short_imm);
}
- // RDX -= RAX.
+ // RAX -= RDX.
OpRegReg(kOpSub, rs_r0q, rs_r2q);
- // Store result.
- OpRegCopyWide(rl_result.reg, rs_r0q);
+ // Result in RAX.
} else {
- // Store result.
- OpRegCopyWide(rl_result.reg, rs_r2q);
+ // Result in RDX.
}
StoreValueWide(rl_dest, rl_result);
FreeTemp(rs_r0q);
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index 7bf7a65..a2b9bb7 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -276,11 +276,11 @@
if (bb->dominators == NULL) {
bb->dominators = new (arena_) ArenaBitVector(arena_, num_total_blocks,
- false /* expandable */, kBitMapDominators);
+ true /* expandable */, kBitMapDominators);
bb->i_dominated = new (arena_) ArenaBitVector(arena_, num_total_blocks,
- false /* expandable */, kBitMapIDominated);
+ true /* expandable */, kBitMapIDominated);
bb->dom_frontier = new (arena_) ArenaBitVector(arena_, num_total_blocks,
- false /* expandable */, kBitMapDomFrontier);
+ true /* expandable */, kBitMapDomFrontier);
} else {
bb->dominators->ClearAllBits();
bb->i_dominated->ClearAllBits();
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
new file mode 100644
index 0000000..35320f5
--- /dev/null
+++ b/compiler/elf_builder.h
@@ -0,0 +1,1069 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_ELF_BUILDER_H_
+#define ART_COMPILER_ELF_BUILDER_H_
+
+#include "buffered_output_stream.h"
+#include "elf_utils.h"
+#include "file_output_stream.h"
+#include "instruction_set.h"
+
+namespace art {
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfSectionBuilder {
+ public:
+ ElfSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
+ const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *link, Elf_Word info,
+ Elf_Word align, Elf_Word entsize) : name_(sec_name), link_(link) {
+ memset(§ion_, 0, sizeof(section_));
+ section_.sh_type = type;
+ section_.sh_flags = flags;
+ section_.sh_info = info;
+ section_.sh_addralign = align;
+ section_.sh_entsize = entsize;
+ }
+
+ virtual ~ElfSectionBuilder() {}
+
+ Elf_Shdr section_;
+ Elf_Word section_index_ = 0;
+
+ Elf_Word GetLink() {
+ return (link_) ? link_->section_index_ : 0;
+ }
+
+ const std::string name_;
+
+ protected:
+ const ElfSectionBuilder* link_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Dyn, typename Elf_Shdr>
+class ElfDynamicBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+ void AddDynamicTag(Elf_Sword tag, Elf_Word d_un) {
+ if (tag == DT_NULL) {
+ return;
+ }
+ dynamics_.push_back({nullptr, tag, d_un});
+ }
+
+ void AddDynamicTag(Elf_Sword tag, Elf_Word d_un,
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section) {
+ if (tag == DT_NULL) {
+ return;
+ }
+ dynamics_.push_back({section, tag, d_un});
+ }
+
+ ElfDynamicBuilder(const std::string& sec_name,
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *link)
+ : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, SHT_DYNAMIC, SHF_ALLOC | SHF_ALLOC,
+ link, 0, kPageSize, sizeof(Elf_Dyn)) {}
+ ~ElfDynamicBuilder() {}
+
+ Elf_Word GetSize() {
+ // Add 1 for the DT_NULL, 1 for DT_STRSZ, and 1 for DT_SONAME. All of
+ // these must be added when we actually put the file together because
+ // their values are very dependent on state.
+ return dynamics_.size() + 3;
+ }
+
+ // Create the actual dynamic vector. strsz should be the size of the .dynstr
+ // table and soname_off should be the offset of the soname in .dynstr.
+ // Since niether can be found prior to final layout we will wait until here
+ // to add them.
+ std::vector<Elf_Dyn> GetDynamics(Elf_Word strsz, Elf_Word soname) {
+ std::vector<Elf_Dyn> ret;
+ for (auto it = dynamics_.cbegin(); it != dynamics_.cend(); ++it) {
+ if (it->section_) {
+ // We are adding an address relative to a section.
+ ret.push_back(
+ {it->tag_, {it->off_ + it->section_->section_.sh_addr}});
+ } else {
+ ret.push_back({it->tag_, {it->off_}});
+ }
+ }
+ ret.push_back({DT_STRSZ, {strsz}});
+ ret.push_back({DT_SONAME, {soname}});
+ ret.push_back({DT_NULL, {0}});
+ return ret;
+ }
+
+ protected:
+ struct ElfDynamicState {
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section_;
+ Elf_Sword tag_;
+ Elf_Word off_;
+ };
+ std::vector<ElfDynamicState> dynamics_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfRawSectionBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+ ElfRawSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
+ const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* link, Elf_Word info,
+ Elf_Word align, Elf_Word entsize)
+ : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, flags, link, info, align,
+ entsize) {}
+ ~ElfRawSectionBuilder() {}
+ std::vector<uint8_t>* GetBuffer() { return &buf_; }
+ void SetBuffer(std::vector<uint8_t>&& buf) { buf_ = buf; }
+
+ protected:
+ std::vector<uint8_t> buf_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfOatSectionBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+ ElfOatSectionBuilder(const std::string& sec_name, Elf_Word size, Elf_Word offset,
+ Elf_Word type, Elf_Word flags)
+ : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, flags, nullptr, 0, kPageSize,
+ 0), offset_(offset), size_(size) {}
+ ~ElfOatSectionBuilder() {}
+
+ Elf_Word GetOffset() {
+ return offset_;
+ }
+
+ Elf_Word GetSize() {
+ return size_;
+ }
+
+ protected:
+ // Offset of the content within the file.
+ Elf_Word offset_;
+ // Size of the content within the file.
+ Elf_Word size_;
+};
+
+static inline constexpr uint8_t MakeStInfo(uint8_t binding, uint8_t type) {
+ return ((binding) << 4) + ((type) & 0xf);
+}
+
+// from bionic
+static inline unsigned elfhash(const char *_name) {
+ const unsigned char *name = (const unsigned char *) _name;
+ unsigned h = 0, g;
+
+ while (*name) {
+ h = (h << 4) + *name++;
+ g = h & 0xf0000000;
+ h ^= g;
+ h ^= g >> 24;
+ }
+ return h;
+}
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Sym,
+ typename Elf_Shdr>
+class ElfSymtabBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+ // Add a symbol with given name to this symtab. The symbol refers to
+ // 'relative_addr' within the given section and has the given attributes.
+ void AddSymbol(const std::string& name,
+ const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section,
+ Elf_Addr addr,
+ bool is_relative,
+ Elf_Word size,
+ uint8_t binding,
+ uint8_t type,
+ uint8_t other = 0) {
+ CHECK(section);
+ ElfSymtabBuilder::ElfSymbolState state {name, section, addr, size, is_relative,
+ MakeStInfo(binding, type), other, 0};
+ symbols_.push_back(state);
+ }
+
+ ElfSymtabBuilder(const std::string& sec_name, Elf_Word type,
+ const std::string& str_name, Elf_Word str_type, bool alloc)
+ : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, ((alloc) ? SHF_ALLOC : 0U),
+ &strtab_, 0, sizeof(Elf_Word),
+ sizeof(Elf_Sym)), str_name_(str_name),
+ str_type_(str_type),
+ strtab_(str_name,
+ str_type,
+ ((alloc) ? SHF_ALLOC : 0U),
+ nullptr, 0, 1, 1) {}
+ ~ElfSymtabBuilder() {}
+
+ std::vector<Elf_Word> GenerateHashContents() {
+ // Here is how The ELF hash table works.
+ // There are 3 arrays to worry about.
+ // * The symbol table where the symbol information is.
+ // * The bucket array which is an array of indexes into the symtab and chain.
+ // * The chain array which is also an array of indexes into the symtab and chain.
+ //
+ // Lets say the state is something like this.
+ // +--------+ +--------+ +-----------+
+ // | symtab | | bucket | | chain |
+ // | null | | 1 | | STN_UNDEF |
+ // | <sym1> | | 4 | | 2 |
+ // | <sym2> | | | | 5 |
+ // | <sym3> | | | | STN_UNDEF |
+ // | <sym4> | | | | 3 |
+ // | <sym5> | | | | STN_UNDEF |
+ // +--------+ +--------+ +-----------+
+ //
+ // The lookup process (in python psudocode) is
+ //
+ // def GetSym(name):
+ // # NB STN_UNDEF == 0
+ // indx = bucket[elfhash(name) % num_buckets]
+ // while indx != STN_UNDEF:
+ // if GetSymbolName(symtab[indx]) == name:
+ // return symtab[indx]
+ // indx = chain[indx]
+ // return SYMBOL_NOT_FOUND
+ //
+ // Between bucket and chain arrays every symtab index must be present exactly
+ // once (except for STN_UNDEF, which must be present 1 + num_bucket times).
+
+ // Select number of buckets.
+ // This is essentially arbitrary.
+ Elf_Word nbuckets;
+ Elf_Word chain_size = GetSize();
+ if (symbols_.size() < 8) {
+ nbuckets = 2;
+ } else if (symbols_.size() < 32) {
+ nbuckets = 4;
+ } else if (symbols_.size() < 256) {
+ nbuckets = 16;
+ } else {
+ // Have about 32 ids per bucket.
+ nbuckets = RoundUp(symbols_.size()/32, 2);
+ }
+ std::vector<Elf_Word> hash;
+ hash.push_back(nbuckets);
+ hash.push_back(chain_size);
+ uint32_t bucket_offset = hash.size();
+ uint32_t chain_offset = bucket_offset + nbuckets;
+ hash.resize(hash.size() + nbuckets + chain_size, 0);
+
+ Elf_Word* buckets = hash.data() + bucket_offset;
+ Elf_Word* chain = hash.data() + chain_offset;
+
+ // Set up the actual hash table.
+ for (Elf_Word i = 0; i < symbols_.size(); i++) {
+ // Add 1 since we need to have the null symbol that is not in the symbols
+ // list.
+ Elf_Word index = i + 1;
+ Elf_Word hash_val = static_cast<Elf_Word>(elfhash(symbols_[i].name_.c_str())) % nbuckets;
+ if (buckets[hash_val] == 0) {
+ buckets[hash_val] = index;
+ } else {
+ hash_val = buckets[hash_val];
+ CHECK_LT(hash_val, chain_size);
+ while (chain[hash_val] != 0) {
+ hash_val = chain[hash_val];
+ CHECK_LT(hash_val, chain_size);
+ }
+ chain[hash_val] = index;
+ // Check for loops. Works because if this is non-empty then there must be
+ // another cell which already contains the same symbol index as this one,
+ // which means some symbol has more then one name, which isn't allowed.
+ CHECK_EQ(chain[index], static_cast<Elf_Word>(0));
+ }
+ }
+
+ return hash;
+ }
+
+ std::string GenerateStrtab() {
+ std::string tab;
+ tab += '\0';
+ for (auto it = symbols_.begin(); it != symbols_.end(); ++it) {
+ it->name_idx_ = tab.size();
+ tab += it->name_;
+ tab += '\0';
+ }
+ strtab_.section_.sh_size = tab.size();
+ return tab;
+ }
+
+ std::vector<Elf_Sym> GenerateSymtab() {
+ std::vector<Elf_Sym> ret;
+ Elf_Sym undef_sym;
+ memset(&undef_sym, 0, sizeof(undef_sym));
+ undef_sym.st_shndx = SHN_UNDEF;
+ ret.push_back(undef_sym);
+
+ for (auto it = symbols_.cbegin(); it != symbols_.cend(); ++it) {
+ Elf_Sym sym;
+ memset(&sym, 0, sizeof(sym));
+ sym.st_name = it->name_idx_;
+ if (it->is_relative_) {
+ sym.st_value = it->addr_ + it->section_->section_.sh_offset;
+ } else {
+ sym.st_value = it->addr_;
+ }
+ sym.st_size = it->size_;
+ sym.st_other = it->other_;
+ sym.st_shndx = it->section_->section_index_;
+ sym.st_info = it->info_;
+
+ ret.push_back(sym);
+ }
+ return ret;
+ }
+
+ Elf_Word GetSize() {
+ // 1 is for the implicit NULL symbol.
+ return symbols_.size() + 1;
+ }
+
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* GetStrTab() {
+ return &strtab_;
+ }
+
+ protected:
+ struct ElfSymbolState {
+ const std::string name_;
+ const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section_;
+ Elf_Addr addr_;
+ Elf_Word size_;
+ bool is_relative_;
+ uint8_t info_;
+ uint8_t other_;
+ // Used during Write() to temporarially hold name index in the strtab.
+ Elf_Word name_idx_;
+ };
+
+ // Information for the strsym for dynstr sections.
+ const std::string str_name_;
+ Elf_Word str_type_;
+ // The symbols in the same order they will be in the symbol table.
+ std::vector<ElfSymbolState> symbols_;
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> strtab_;
+};
+
+class CodeOutput {
+ public:
+ virtual bool Write(OutputStream* out) = 0;
+ virtual ~CodeOutput() {}
+};
+
+template <typename Elf_Word, typename Elf_Shdr>
+static inline constexpr Elf_Word NextOffset(const Elf_Shdr& cur, const Elf_Shdr& prev) {
+ return RoundUp(prev.sh_size + prev.sh_offset, cur.sh_addralign);
+}
+
+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>
+class ElfBuilder FINAL {
+ public:
+ ElfBuilder(CodeOutput* oat_writer,
+ File* elf_file,
+ InstructionSet isa,
+ Elf_Word rodata_relative_offset,
+ Elf_Word rodata_size,
+ Elf_Word text_relative_offset,
+ Elf_Word text_size,
+ const bool add_symbols,
+ bool debug = false)
+ : oat_writer_(oat_writer),
+ elf_file_(elf_file),
+ add_symbols_(add_symbols),
+ debug_logging_(debug),
+ text_builder_(".text", text_size, text_relative_offset, SHT_PROGBITS,
+ SHF_ALLOC | SHF_EXECINSTR),
+ rodata_builder_(".rodata", rodata_size, rodata_relative_offset, SHT_PROGBITS, SHF_ALLOC),
+ dynsym_builder_(".dynsym", SHT_DYNSYM, ".dynstr", SHT_STRTAB, true),
+ symtab_builder_(".symtab", SHT_SYMTAB, ".strtab", SHT_STRTAB, false),
+ hash_builder_(".hash", SHT_HASH, SHF_ALLOC, &dynsym_builder_, 0, sizeof(Elf_Word),
+ sizeof(Elf_Word)),
+ dynamic_builder_(".dynamic", &dynsym_builder_),
+ shstrtab_builder_(".shstrtab", SHT_STRTAB, 0, NULL, 0, 1, 1) {
+ SetupEhdr();
+ SetupDynamic();
+ SetupRequiredSymbols();
+ SetISA(isa);
+ }
+ ~ElfBuilder() {}
+
+ bool Init() {
+ // The basic layout of the elf file. Order may be different in final output.
+ // +-------------------------+
+ // | Elf_Ehdr |
+ // +-------------------------+
+ // | Elf_Phdr PHDR |
+ // | Elf_Phdr LOAD R | .dynsym .dynstr .hash .rodata
+ // | Elf_Phdr LOAD R X | .text
+ // | Elf_Phdr LOAD RW | .dynamic
+ // | Elf_Phdr DYNAMIC | .dynamic
+ // +-------------------------+
+ // | .dynsym |
+ // | Elf_Sym STN_UNDEF |
+ // | Elf_Sym oatdata |
+ // | Elf_Sym oatexec |
+ // | Elf_Sym oatlastword |
+ // +-------------------------+
+ // | .dynstr |
+ // | \0 |
+ // | oatdata\0 |
+ // | oatexec\0 |
+ // | oatlastword\0 |
+ // | boot.oat\0 |
+ // +-------------------------+
+ // | .hash |
+ // | Elf_Word nbucket = b |
+ // | Elf_Word nchain = c |
+ // | Elf_Word bucket[0] |
+ // | ... |
+ // | Elf_Word bucket[b - 1] |
+ // | Elf_Word chain[0] |
+ // | ... |
+ // | Elf_Word chain[c - 1] |
+ // +-------------------------+
+ // | .rodata |
+ // | oatdata..oatexec-4 |
+ // +-------------------------+
+ // | .text |
+ // | oatexec..oatlastword |
+ // +-------------------------+
+ // | .dynamic |
+ // | Elf_Dyn DT_SONAME |
+ // | Elf_Dyn DT_HASH |
+ // | Elf_Dyn DT_SYMTAB |
+ // | Elf_Dyn DT_SYMENT |
+ // | Elf_Dyn DT_STRTAB |
+ // | Elf_Dyn DT_STRSZ |
+ // | Elf_Dyn DT_NULL |
+ // +-------------------------+ (Optional)
+ // | .strtab | (Optional)
+ // | program symbol names | (Optional)
+ // +-------------------------+ (Optional)
+ // | .symtab | (Optional)
+ // | program symbols | (Optional)
+ // +-------------------------+
+ // | .shstrtab |
+ // | \0 |
+ // | .dynamic\0 |
+ // | .dynsym\0 |
+ // | .dynstr\0 |
+ // | .hash\0 |
+ // | .rodata\0 |
+ // | .text\0 |
+ // | .shstrtab\0 |
+ // | .symtab\0 | (Optional)
+ // | .strtab\0 | (Optional)
+ // | .debug_str\0 | (Optional)
+ // | .debug_info\0 | (Optional)
+ // | .eh_frame\0 | (Optional)
+ // | .debug_line\0 | (Optional)
+ // | .debug_abbrev\0 | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_info | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_abbrev | (Optional)
+ // +-------------------------+ (Optional)
+ // | .eh_frame | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_line | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_str | (Optional)
+ // +-------------------------+ (Optional)
+ // | Elf_Shdr NULL |
+ // | Elf_Shdr .dynsym |
+ // | Elf_Shdr .dynstr |
+ // | Elf_Shdr .hash |
+ // | Elf_Shdr .text |
+ // | Elf_Shdr .rodata |
+ // | Elf_Shdr .dynamic |
+ // | Elf_Shdr .shstrtab |
+ // | Elf_Shdr .debug_info | (Optional)
+ // | Elf_Shdr .debug_abbrev | (Optional)
+ // | Elf_Shdr .eh_frame | (Optional)
+ // | Elf_Shdr .debug_line | (Optional)
+ // | Elf_Shdr .debug_str | (Optional)
+ // +-------------------------+
+
+ if (fatal_error_) {
+ return false;
+ }
+ // Step 1. Figure out all the offsets.
+
+ if (debug_logging_) {
+ LOG(INFO) << "phdr_offset=" << PHDR_OFFSET << std::hex << " " << PHDR_OFFSET;
+ LOG(INFO) << "phdr_size=" << PHDR_SIZE << std::hex << " " << PHDR_SIZE;
+ }
+
+ memset(&program_headers_, 0, sizeof(program_headers_));
+ program_headers_[PH_PHDR].p_type = PT_PHDR;
+ program_headers_[PH_PHDR].p_offset = PHDR_OFFSET;
+ program_headers_[PH_PHDR].p_vaddr = PHDR_OFFSET;
+ program_headers_[PH_PHDR].p_paddr = PHDR_OFFSET;
+ program_headers_[PH_PHDR].p_filesz = sizeof(program_headers_);
+ program_headers_[PH_PHDR].p_memsz = sizeof(program_headers_);
+ program_headers_[PH_PHDR].p_flags = PF_R;
+ program_headers_[PH_PHDR].p_align = sizeof(Elf_Word);
+
+ program_headers_[PH_LOAD_R__].p_type = PT_LOAD;
+ program_headers_[PH_LOAD_R__].p_offset = 0;
+ program_headers_[PH_LOAD_R__].p_vaddr = 0;
+ program_headers_[PH_LOAD_R__].p_paddr = 0;
+ program_headers_[PH_LOAD_R__].p_flags = PF_R;
+
+ program_headers_[PH_LOAD_R_X].p_type = PT_LOAD;
+ program_headers_[PH_LOAD_R_X].p_flags = PF_R | PF_X;
+
+ program_headers_[PH_LOAD_RW_].p_type = PT_LOAD;
+ program_headers_[PH_LOAD_RW_].p_flags = PF_R | PF_W;
+
+ program_headers_[PH_DYNAMIC].p_type = PT_DYNAMIC;
+ program_headers_[PH_DYNAMIC].p_flags = PF_R | PF_W;
+
+ // Get the dynstr string.
+ dynstr_ = dynsym_builder_.GenerateStrtab();
+
+ // Add the SONAME to the dynstr.
+ dynstr_soname_offset_ = dynstr_.size();
+ std::string file_name(elf_file_->GetPath());
+ size_t directory_separator_pos = file_name.rfind('/');
+ if (directory_separator_pos != std::string::npos) {
+ file_name = file_name.substr(directory_separator_pos + 1);
+ }
+ dynstr_ += file_name;
+ dynstr_ += '\0';
+ if (debug_logging_) {
+ LOG(INFO) << "dynstr size (bytes) =" << dynstr_.size()
+ << std::hex << " " << dynstr_.size();
+ LOG(INFO) << "dynsym size (elements)=" << dynsym_builder_.GetSize()
+ << std::hex << " " << dynsym_builder_.GetSize();
+ }
+
+ // Get the section header string table.
+ shstrtab_ += '\0';
+
+ // Setup sym_undef
+ memset(&null_hdr_, 0, sizeof(null_hdr_));
+ null_hdr_.sh_type = SHT_NULL;
+ null_hdr_.sh_link = SHN_UNDEF;
+ section_ptrs_.push_back(&null_hdr_);
+
+ section_index_ = 1;
+
+ // setup .dynsym
+ section_ptrs_.push_back(&dynsym_builder_.section_);
+ AssignSectionStr(&dynsym_builder_, &shstrtab_);
+ dynsym_builder_.section_index_ = section_index_++;
+
+ // Setup .dynstr
+ section_ptrs_.push_back(&dynsym_builder_.GetStrTab()->section_);
+ AssignSectionStr(dynsym_builder_.GetStrTab(), &shstrtab_);
+ dynsym_builder_.GetStrTab()->section_index_ = section_index_++;
+
+ // Setup .hash
+ section_ptrs_.push_back(&hash_builder_.section_);
+ AssignSectionStr(&hash_builder_, &shstrtab_);
+ hash_builder_.section_index_ = section_index_++;
+
+ // Setup .rodata
+ section_ptrs_.push_back(&rodata_builder_.section_);
+ AssignSectionStr(&rodata_builder_, &shstrtab_);
+ rodata_builder_.section_index_ = section_index_++;
+
+ // Setup .text
+ section_ptrs_.push_back(&text_builder_.section_);
+ AssignSectionStr(&text_builder_, &shstrtab_);
+ text_builder_.section_index_ = section_index_++;
+
+ // Setup .dynamic
+ section_ptrs_.push_back(&dynamic_builder_.section_);
+ AssignSectionStr(&dynamic_builder_, &shstrtab_);
+ dynamic_builder_.section_index_ = section_index_++;
+
+ // Fill in the hash section.
+ hash_ = dynsym_builder_.GenerateHashContents();
+
+ if (debug_logging_) {
+ LOG(INFO) << ".hash size (bytes)=" << hash_.size() * sizeof(Elf_Word)
+ << std::hex << " " << hash_.size() * sizeof(Elf_Word);
+ }
+
+ Elf_Word base_offset = sizeof(Elf_Ehdr) + sizeof(program_headers_);
+
+ // Get the layout in the sections.
+ //
+ // Get the layout of the dynsym section.
+ dynsym_builder_.section_.sh_offset = RoundUp(base_offset, dynsym_builder_.section_.sh_addralign);
+ dynsym_builder_.section_.sh_addr = dynsym_builder_.section_.sh_offset;
+ dynsym_builder_.section_.sh_size = dynsym_builder_.GetSize() * sizeof(Elf_Sym);
+ dynsym_builder_.section_.sh_link = dynsym_builder_.GetLink();
+
+ // Get the layout of the dynstr section.
+ dynsym_builder_.GetStrTab()->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (dynsym_builder_.GetStrTab()->section_,
+ dynsym_builder_.section_);
+ dynsym_builder_.GetStrTab()->section_.sh_addr = dynsym_builder_.GetStrTab()->section_.sh_offset;
+ dynsym_builder_.GetStrTab()->section_.sh_size = dynstr_.size();
+ dynsym_builder_.GetStrTab()->section_.sh_link = dynsym_builder_.GetStrTab()->GetLink();
+
+ // Get the layout of the hash section
+ hash_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (hash_builder_.section_,
+ dynsym_builder_.GetStrTab()->section_);
+ hash_builder_.section_.sh_addr = hash_builder_.section_.sh_offset;
+ hash_builder_.section_.sh_size = hash_.size() * sizeof(Elf_Word);
+ hash_builder_.section_.sh_link = hash_builder_.GetLink();
+
+ // Get the layout of the rodata section.
+ rodata_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (rodata_builder_.section_,
+ hash_builder_.section_);
+ rodata_builder_.section_.sh_addr = rodata_builder_.section_.sh_offset;
+ rodata_builder_.section_.sh_size = rodata_builder_.GetSize();
+ rodata_builder_.section_.sh_link = rodata_builder_.GetLink();
+
+ // Get the layout of the text section.
+ text_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (text_builder_.section_, rodata_builder_.section_);
+ text_builder_.section_.sh_addr = text_builder_.section_.sh_offset;
+ text_builder_.section_.sh_size = text_builder_.GetSize();
+ text_builder_.section_.sh_link = text_builder_.GetLink();
+ CHECK_ALIGNED(rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size, kPageSize);
+
+ // Get the layout of the dynamic section.
+ dynamic_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (dynamic_builder_.section_,
+ text_builder_.section_);
+ dynamic_builder_.section_.sh_addr = dynamic_builder_.section_.sh_offset;
+ dynamic_builder_.section_.sh_size = dynamic_builder_.GetSize() * sizeof(Elf_Dyn);
+ dynamic_builder_.section_.sh_link = dynamic_builder_.GetLink();
+
+ if (debug_logging_) {
+ LOG(INFO) << "dynsym off=" << dynsym_builder_.section_.sh_offset
+ << " dynsym size=" << dynsym_builder_.section_.sh_size;
+ LOG(INFO) << "dynstr off=" << dynsym_builder_.GetStrTab()->section_.sh_offset
+ << " dynstr size=" << dynsym_builder_.GetStrTab()->section_.sh_size;
+ LOG(INFO) << "hash off=" << hash_builder_.section_.sh_offset
+ << " hash size=" << hash_builder_.section_.sh_size;
+ LOG(INFO) << "rodata off=" << rodata_builder_.section_.sh_offset
+ << " rodata size=" << rodata_builder_.section_.sh_size;
+ LOG(INFO) << "text off=" << text_builder_.section_.sh_offset
+ << " text size=" << text_builder_.section_.sh_size;
+ LOG(INFO) << "dynamic off=" << dynamic_builder_.section_.sh_offset
+ << " dynamic size=" << dynamic_builder_.section_.sh_size;
+ }
+
+ return true;
+ }
+
+ bool Write() {
+ std::vector<ElfFilePiece> pieces;
+ Elf_Shdr prev = dynamic_builder_.section_;
+ std::string strtab;
+
+ if (IncludingDebugSymbols()) {
+ // Setup .symtab
+ section_ptrs_.push_back(&symtab_builder_.section_);
+ AssignSectionStr(&symtab_builder_, &shstrtab_);
+ symtab_builder_.section_index_ = section_index_++;
+
+ // Setup .strtab
+ section_ptrs_.push_back(&symtab_builder_.GetStrTab()->section_);
+ AssignSectionStr(symtab_builder_.GetStrTab(), &shstrtab_);
+ symtab_builder_.GetStrTab()->section_index_ = section_index_++;
+
+ strtab = symtab_builder_.GenerateStrtab();
+ if (debug_logging_) {
+ LOG(INFO) << "strtab size (bytes) =" << strtab.size()
+ << std::hex << " " << strtab.size();
+ LOG(INFO) << "symtab size (elements) =" << symtab_builder_.GetSize()
+ << std::hex << " " << symtab_builder_.GetSize();
+ }
+ }
+
+ // Setup all the other sections.
+ for (ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *builder = other_builders_.data(),
+ *end = builder + other_builders_.size();
+ builder != end; ++builder) {
+ section_ptrs_.push_back(&builder->section_);
+ AssignSectionStr(builder, &shstrtab_);
+ builder->section_index_ = section_index_++;
+ }
+
+ // Setup shstrtab
+ section_ptrs_.push_back(&shstrtab_builder_.section_);
+ AssignSectionStr(&shstrtab_builder_, &shstrtab_);
+ shstrtab_builder_.section_index_ = section_index_++;
+
+ if (debug_logging_) {
+ LOG(INFO) << ".shstrtab size (bytes) =" << shstrtab_.size()
+ << std::hex << " " << shstrtab_.size();
+ LOG(INFO) << "section list size (elements)=" << section_ptrs_.size()
+ << std::hex << " " << section_ptrs_.size();
+ }
+
+ if (IncludingDebugSymbols()) {
+ // Get the layout of the symtab section.
+ symtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (symtab_builder_.section_,
+ dynamic_builder_.section_);
+ symtab_builder_.section_.sh_addr = 0;
+ // Add to leave space for the null symbol.
+ symtab_builder_.section_.sh_size = symtab_builder_.GetSize() * sizeof(Elf_Sym);
+ symtab_builder_.section_.sh_link = symtab_builder_.GetLink();
+
+ // Get the layout of the dynstr section.
+ symtab_builder_.GetStrTab()->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (symtab_builder_.GetStrTab()->section_,
+ symtab_builder_.section_);
+ symtab_builder_.GetStrTab()->section_.sh_addr = 0;
+ symtab_builder_.GetStrTab()->section_.sh_size = strtab.size();
+ symtab_builder_.GetStrTab()->section_.sh_link = symtab_builder_.GetStrTab()->GetLink();
+
+ prev = symtab_builder_.GetStrTab()->section_;
+ if (debug_logging_) {
+ LOG(INFO) << "symtab off=" << symtab_builder_.section_.sh_offset
+ << " symtab size=" << symtab_builder_.section_.sh_size;
+ LOG(INFO) << "strtab off=" << symtab_builder_.GetStrTab()->section_.sh_offset
+ << " strtab size=" << symtab_builder_.GetStrTab()->section_.sh_size;
+ }
+ }
+
+ // Get the layout of the extra sections. (This will deal with the debug
+ // sections if they are there)
+ for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
+ it->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>(it->section_, prev);
+ it->section_.sh_addr = 0;
+ it->section_.sh_size = it->GetBuffer()->size();
+ it->section_.sh_link = it->GetLink();
+ pieces.push_back(ElfFilePiece(it->name_, it->section_.sh_offset,
+ it->GetBuffer()->data(), it->GetBuffer()->size()));
+ prev = it->section_;
+ if (debug_logging_) {
+ LOG(INFO) << it->name_ << " off=" << it->section_.sh_offset
+ << " size=" << it->section_.sh_size;
+ }
+ }
+
+ // Get the layout of the shstrtab section
+ shstrtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (shstrtab_builder_.section_, prev);
+ shstrtab_builder_.section_.sh_addr = 0;
+ shstrtab_builder_.section_.sh_size = shstrtab_.size();
+ shstrtab_builder_.section_.sh_link = shstrtab_builder_.GetLink();
+ if (debug_logging_) {
+ LOG(INFO) << "shstrtab off=" << shstrtab_builder_.section_.sh_offset
+ << " shstrtab size=" << shstrtab_builder_.section_.sh_size;
+ }
+
+ // The section list comes after come after.
+ Elf_Word sections_offset = RoundUp(
+ shstrtab_builder_.section_.sh_offset + shstrtab_builder_.section_.sh_size,
+ sizeof(Elf_Word));
+
+ // Setup the actual symbol arrays.
+ std::vector<Elf_Sym> dynsym = dynsym_builder_.GenerateSymtab();
+ CHECK_EQ(dynsym.size() * sizeof(Elf_Sym), dynsym_builder_.section_.sh_size);
+ std::vector<Elf_Sym> symtab;
+ if (IncludingDebugSymbols()) {
+ symtab = symtab_builder_.GenerateSymtab();
+ CHECK_EQ(symtab.size() * sizeof(Elf_Sym), symtab_builder_.section_.sh_size);
+ }
+
+ // Setup the dynamic section.
+ // This will add the 2 values we cannot know until now time, namely the size
+ // and the soname_offset.
+ std::vector<Elf_Dyn> dynamic = dynamic_builder_.GetDynamics(dynstr_.size(),
+ dynstr_soname_offset_);
+ CHECK_EQ(dynamic.size() * sizeof(Elf_Dyn), dynamic_builder_.section_.sh_size);
+
+ // Finish setup of the program headers now that we know the layout of the
+ // whole file.
+ Elf_Word load_r_size = rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size;
+ program_headers_[PH_LOAD_R__].p_filesz = load_r_size;
+ program_headers_[PH_LOAD_R__].p_memsz = load_r_size;
+ program_headers_[PH_LOAD_R__].p_align = rodata_builder_.section_.sh_addralign;
+
+ Elf_Word load_rx_size = text_builder_.section_.sh_size;
+ program_headers_[PH_LOAD_R_X].p_offset = text_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_R_X].p_vaddr = text_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_R_X].p_paddr = text_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_R_X].p_filesz = load_rx_size;
+ program_headers_[PH_LOAD_R_X].p_memsz = load_rx_size;
+ program_headers_[PH_LOAD_R_X].p_align = text_builder_.section_.sh_addralign;
+
+ program_headers_[PH_LOAD_RW_].p_offset = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_RW_].p_vaddr = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_RW_].p_paddr = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_RW_].p_filesz = dynamic_builder_.section_.sh_size;
+ program_headers_[PH_LOAD_RW_].p_memsz = dynamic_builder_.section_.sh_size;
+ program_headers_[PH_LOAD_RW_].p_align = dynamic_builder_.section_.sh_addralign;
+
+ program_headers_[PH_DYNAMIC].p_offset = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_DYNAMIC].p_vaddr = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_DYNAMIC].p_paddr = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_DYNAMIC].p_filesz = dynamic_builder_.section_.sh_size;
+ program_headers_[PH_DYNAMIC].p_memsz = dynamic_builder_.section_.sh_size;
+ program_headers_[PH_DYNAMIC].p_align = dynamic_builder_.section_.sh_addralign;
+
+ // Finish setup of the Ehdr values.
+ elf_header_.e_phoff = PHDR_OFFSET;
+ elf_header_.e_shoff = sections_offset;
+ elf_header_.e_phnum = PH_NUM;
+ elf_header_.e_shnum = section_ptrs_.size();
+ elf_header_.e_shstrndx = shstrtab_builder_.section_index_;
+
+ // Add the rest of the pieces to the list.
+ pieces.push_back(ElfFilePiece("Elf Header", 0, &elf_header_, sizeof(elf_header_)));
+ pieces.push_back(ElfFilePiece("Program headers", PHDR_OFFSET,
+ &program_headers_, sizeof(program_headers_)));
+ pieces.push_back(ElfFilePiece(".dynamic", dynamic_builder_.section_.sh_offset,
+ dynamic.data(), dynamic_builder_.section_.sh_size));
+ pieces.push_back(ElfFilePiece(".dynsym", dynsym_builder_.section_.sh_offset,
+ dynsym.data(), dynsym.size() * sizeof(Elf_Sym)));
+ pieces.push_back(ElfFilePiece(".dynstr", dynsym_builder_.GetStrTab()->section_.sh_offset,
+ dynstr_.c_str(), dynstr_.size()));
+ pieces.push_back(ElfFilePiece(".hash", hash_builder_.section_.sh_offset,
+ hash_.data(), hash_.size() * sizeof(Elf_Word)));
+ pieces.push_back(ElfFilePiece(".rodata", rodata_builder_.section_.sh_offset,
+ nullptr, rodata_builder_.section_.sh_size));
+ pieces.push_back(ElfFilePiece(".text", text_builder_.section_.sh_offset,
+ nullptr, text_builder_.section_.sh_size));
+ if (IncludingDebugSymbols()) {
+ pieces.push_back(ElfFilePiece(".symtab", symtab_builder_.section_.sh_offset,
+ symtab.data(), symtab.size() * sizeof(Elf_Sym)));
+ pieces.push_back(ElfFilePiece(".strtab", symtab_builder_.GetStrTab()->section_.sh_offset,
+ strtab.c_str(), strtab.size()));
+ }
+ pieces.push_back(ElfFilePiece(".shstrtab", shstrtab_builder_.section_.sh_offset,
+ &shstrtab_[0], shstrtab_.size()));
+ for (uint32_t i = 0; i < section_ptrs_.size(); ++i) {
+ // Just add all the sections in induvidually since they are all over the
+ // place on the heap/stack.
+ Elf_Word cur_off = sections_offset + i * sizeof(Elf_Shdr);
+ pieces.push_back(ElfFilePiece("section table piece", cur_off,
+ section_ptrs_[i], sizeof(Elf_Shdr)));
+ }
+
+ if (!WriteOutFile(pieces)) {
+ LOG(ERROR) << "Unable to write to file " << elf_file_->GetPath();
+ return false;
+ }
+ // write out the actual oat file data.
+ Elf_Word oat_data_offset = rodata_builder_.section_.sh_offset;
+ if (static_cast<off_t>(oat_data_offset) != lseek(elf_file_->Fd(), oat_data_offset, SEEK_SET)) {
+ PLOG(ERROR) << "Failed to seek to .rodata offset " << oat_data_offset
+ << " for " << elf_file_->GetPath();
+ return false;
+ }
+ std::unique_ptr<BufferedOutputStream> output_stream(
+ new BufferedOutputStream(new FileOutputStream(elf_file_)));
+ if (!oat_writer_->Write(output_stream.get())) {
+ PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file_->GetPath();
+ return false;
+ }
+
+ return true;
+ }
+
+ // Adds the given raw section to the builder. This will copy it. The caller
+ // is responsible for deallocating their copy.
+ void RegisterRawSection(ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> bld) {
+ other_builders_.push_back(bld);
+ }
+
+ private:
+ CodeOutput* oat_writer_;
+ File* elf_file_;
+ const bool add_symbols_;
+ const bool debug_logging_;
+
+ bool fatal_error_ = false;
+
+ // What phdr is.
+ static const uint32_t PHDR_OFFSET = sizeof(Elf_Ehdr);
+ enum : uint8_t {
+ PH_PHDR = 0,
+ PH_LOAD_R__ = 1,
+ PH_LOAD_R_X = 2,
+ PH_LOAD_RW_ = 3,
+ PH_DYNAMIC = 4,
+ PH_NUM = 5,
+ };
+ static const uint32_t PHDR_SIZE = sizeof(Elf_Phdr) * PH_NUM;
+ Elf_Phdr program_headers_[PH_NUM];
+
+ Elf_Ehdr elf_header_;
+
+ Elf_Shdr null_hdr_;
+ std::string shstrtab_;
+ uint32_t section_index_;
+ std::string dynstr_;
+ uint32_t dynstr_soname_offset_;
+ std::vector<Elf_Shdr*> section_ptrs_;
+ std::vector<Elf_Word> hash_;
+
+ public:
+ ElfOatSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> text_builder_;
+ ElfOatSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> rodata_builder_;
+ ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Sym, Elf_Shdr> dynsym_builder_;
+ ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Sym, Elf_Shdr> symtab_builder_;
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> hash_builder_;
+ ElfDynamicBuilder<Elf_Word, Elf_Sword, Elf_Dyn, Elf_Shdr> dynamic_builder_;
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> shstrtab_builder_;
+ std::vector<ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>> other_builders_;
+
+ private:
+ void SetISA(InstructionSet isa) {
+ switch (isa) {
+ case kArm:
+ // Fall through.
+ case kThumb2: {
+ elf_header_.e_machine = EM_ARM;
+ elf_header_.e_flags = EF_ARM_EABI_VER5;
+ break;
+ }
+ case kArm64: {
+ elf_header_.e_machine = EM_AARCH64;
+ elf_header_.e_flags = 0;
+ break;
+ }
+ case kX86: {
+ elf_header_.e_machine = EM_386;
+ elf_header_.e_flags = 0;
+ break;
+ }
+ case kX86_64: {
+ elf_header_.e_machine = EM_X86_64;
+ elf_header_.e_flags = 0;
+ break;
+ }
+ case kMips: {
+ elf_header_.e_machine = EM_MIPS;
+ elf_header_.e_flags = (EF_MIPS_NOREORDER |
+ EF_MIPS_PIC |
+ EF_MIPS_CPIC |
+ EF_MIPS_ABI_O32 |
+ EF_MIPS_ARCH_32R2);
+ break;
+ }
+ default: {
+ fatal_error_ = true;
+ LOG(FATAL) << "Unknown instruction set: " << isa;
+ break;
+ }
+ }
+ }
+
+ void SetupEhdr() {
+ memset(&elf_header_, 0, sizeof(elf_header_));
+ elf_header_.e_ident[EI_MAG0] = ELFMAG0;
+ elf_header_.e_ident[EI_MAG1] = ELFMAG1;
+ elf_header_.e_ident[EI_MAG2] = ELFMAG2;
+ elf_header_.e_ident[EI_MAG3] = ELFMAG3;
+ elf_header_.e_ident[EI_CLASS] = ELFCLASS32;
+ elf_header_.e_ident[EI_DATA] = ELFDATA2LSB;
+ elf_header_.e_ident[EI_VERSION] = EV_CURRENT;
+ elf_header_.e_ident[EI_OSABI] = ELFOSABI_LINUX;
+ elf_header_.e_ident[EI_ABIVERSION] = 0;
+ elf_header_.e_type = ET_DYN;
+ elf_header_.e_version = 1;
+ elf_header_.e_entry = 0;
+ elf_header_.e_ehsize = sizeof(Elf_Ehdr);
+ elf_header_.e_phentsize = sizeof(Elf_Phdr);
+ elf_header_.e_shentsize = sizeof(Elf_Shdr);
+ elf_header_.e_phoff = sizeof(Elf_Ehdr);
+ }
+
+ // Sets up a bunch of the required Dynamic Section entries.
+ // Namely it will initialize all the mandatory ones that it can.
+ // Specifically:
+ // DT_HASH
+ // DT_STRTAB
+ // DT_SYMTAB
+ // DT_SYMENT
+ //
+ // Some such as DT_SONAME, DT_STRSZ and DT_NULL will be put in later.
+ void SetupDynamic() {
+ dynamic_builder_.AddDynamicTag(DT_HASH, 0, &hash_builder_);
+ dynamic_builder_.AddDynamicTag(DT_STRTAB, 0, dynsym_builder_.GetStrTab());
+ dynamic_builder_.AddDynamicTag(DT_SYMTAB, 0, &dynsym_builder_);
+ dynamic_builder_.AddDynamicTag(DT_SYMENT, sizeof(Elf_Sym));
+ }
+
+ // Sets up the basic dynamic symbols that are needed, namely all those we
+ // can know already.
+ //
+ // Specifically adds:
+ // oatdata
+ // oatexec
+ // oatlastword
+ void SetupRequiredSymbols() {
+ dynsym_builder_.AddSymbol("oatdata", &rodata_builder_, 0, true,
+ rodata_builder_.GetSize(), STB_GLOBAL, STT_OBJECT);
+ dynsym_builder_.AddSymbol("oatexec", &text_builder_, 0, true,
+ text_builder_.GetSize(), STB_GLOBAL, STT_OBJECT);
+ dynsym_builder_.AddSymbol("oatlastword", &text_builder_, text_builder_.GetSize() - 4,
+ true, 4, STB_GLOBAL, STT_OBJECT);
+ }
+
+ void AssignSectionStr(ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *builder,
+ std::string* strtab) {
+ builder->section_.sh_name = strtab->size();
+ *strtab += builder->name_;
+ *strtab += '\0';
+ if (debug_logging_) {
+ LOG(INFO) << "adding section name \"" << builder->name_ << "\" "
+ << "to shstrtab at offset " << builder->section_.sh_name;
+ }
+ }
+
+ struct ElfFilePiece {
+ ElfFilePiece(const std::string& name, Elf_Word offset, const void* data, Elf_Word size)
+ : dbg_name_(name), offset_(offset), data_(data), size_(size) {}
+ ~ElfFilePiece() {}
+
+ const std::string& dbg_name_;
+ Elf_Word offset_;
+ const void *data_;
+ Elf_Word size_;
+ static bool Compare(ElfFilePiece a, ElfFilePiece b) {
+ return a.offset_ < b.offset_;
+ }
+ };
+
+ // Write each of the pieces out to the file.
+ bool WriteOutFile(const std::vector<ElfFilePiece>& pieces) {
+ // TODO It would be nice if this checked for overlap.
+ for (auto it = pieces.begin(); it != pieces.end(); ++it) {
+ if (it->data_) {
+ if (static_cast<off_t>(it->offset_) != lseek(elf_file_->Fd(), it->offset_, SEEK_SET)) {
+ PLOG(ERROR) << "Failed to seek to " << it->dbg_name_ << " offset location "
+ << it->offset_ << " for " << elf_file_->GetPath();
+ return false;
+ }
+ if (!elf_file_->WriteFully(it->data_, it->size_)) {
+ PLOG(ERROR) << "Failed to write " << it->dbg_name_ << " for " << elf_file_->GetPath();
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ bool IncludingDebugSymbols() { return add_symbols_ && symtab_builder_.GetSize() > 1; }
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_ELF_BUILDER_H_
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 4c69fc8..dbd3a37 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -23,6 +23,7 @@
#include "buffered_output_stream.h"
#include "driver/compiler_driver.h"
#include "dwarf.h"
+#include "elf_builder.h"
#include "elf_file.h"
#include "elf_utils.h"
#include "file_output_stream.h"
@@ -34,15 +35,6 @@
namespace art {
-template <typename Elf_Word, typename Elf_Shdr>
-static constexpr Elf_Word NextOffset(const Elf_Shdr& cur, const Elf_Shdr& prev) {
- return RoundUp(prev.sh_size + prev.sh_offset, cur.sh_addralign);
-}
-
-static uint8_t MakeStInfo(uint8_t binding, uint8_t type) {
- return ((binding) << 4) + ((type) & 0xf);
-}
-
static void PushByte(std::vector<uint8_t>* buf, int data) {
buf->push_back(data & 0xff);
}
@@ -83,826 +75,6 @@
typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
typename Elf_Phdr, typename Elf_Shdr>
bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::Init() {
- // The basic layout of the elf file. Order may be different in final output.
- // +-------------------------+
- // | Elf_Ehdr |
- // +-------------------------+
- // | Elf_Phdr PHDR |
- // | Elf_Phdr LOAD R | .dynsym .dynstr .hash .rodata
- // | Elf_Phdr LOAD R X | .text
- // | Elf_Phdr LOAD RW | .dynamic
- // | Elf_Phdr DYNAMIC | .dynamic
- // +-------------------------+
- // | .dynsym |
- // | Elf_Sym STN_UNDEF |
- // | Elf_Sym oatdata |
- // | Elf_Sym oatexec |
- // | Elf_Sym oatlastword |
- // +-------------------------+
- // | .dynstr |
- // | \0 |
- // | oatdata\0 |
- // | oatexec\0 |
- // | oatlastword\0 |
- // | boot.oat\0 |
- // +-------------------------+
- // | .hash |
- // | Elf_Word nbucket = b |
- // | Elf_Word nchain = c |
- // | Elf_Word bucket[0] |
- // | ... |
- // | Elf_Word bucket[b - 1] |
- // | Elf_Word chain[0] |
- // | ... |
- // | Elf_Word chain[c - 1] |
- // +-------------------------+
- // | .rodata |
- // | oatdata..oatexec-4 |
- // +-------------------------+
- // | .text |
- // | oatexec..oatlastword |
- // +-------------------------+
- // | .dynamic |
- // | Elf_Dyn DT_SONAME |
- // | Elf_Dyn DT_HASH |
- // | Elf_Dyn DT_SYMTAB |
- // | Elf_Dyn DT_SYMENT |
- // | Elf_Dyn DT_STRTAB |
- // | Elf_Dyn DT_STRSZ |
- // | Elf_Dyn DT_NULL |
- // +-------------------------+ (Optional)
- // | .strtab | (Optional)
- // | program symbol names | (Optional)
- // +-------------------------+ (Optional)
- // | .symtab | (Optional)
- // | program symbols | (Optional)
- // +-------------------------+
- // | .shstrtab |
- // | \0 |
- // | .dynamic\0 |
- // | .dynsym\0 |
- // | .dynstr\0 |
- // | .hash\0 |
- // | .rodata\0 |
- // | .text\0 |
- // | .shstrtab\0 |
- // | .symtab\0 | (Optional)
- // | .strtab\0 | (Optional)
- // | .debug_str\0 | (Optional)
- // | .debug_info\0 | (Optional)
- // | .eh_frame\0 | (Optional)
- // | .debug_line\0 | (Optional)
- // | .debug_abbrev\0 | (Optional)
- // +-------------------------+ (Optional)
- // | .debug_info | (Optional)
- // +-------------------------+ (Optional)
- // | .debug_abbrev | (Optional)
- // +-------------------------+ (Optional)
- // | .eh_frame | (Optional)
- // +-------------------------+ (Optional)
- // | .debug_line | (Optional)
- // +-------------------------+ (Optional)
- // | .debug_str | (Optional)
- // +-------------------------+ (Optional)
- // | Elf_Shdr NULL |
- // | Elf_Shdr .dynsym |
- // | Elf_Shdr .dynstr |
- // | Elf_Shdr .hash |
- // | Elf_Shdr .text |
- // | Elf_Shdr .rodata |
- // | Elf_Shdr .dynamic |
- // | Elf_Shdr .shstrtab |
- // | Elf_Shdr .debug_info | (Optional)
- // | Elf_Shdr .debug_abbrev | (Optional)
- // | Elf_Shdr .eh_frame | (Optional)
- // | Elf_Shdr .debug_line | (Optional)
- // | Elf_Shdr .debug_str | (Optional)
- // +-------------------------+
-
- if (fatal_error_) {
- return false;
- }
- // Step 1. Figure out all the offsets.
-
- if (debug_logging_) {
- LOG(INFO) << "phdr_offset=" << PHDR_OFFSET << std::hex << " " << PHDR_OFFSET;
- LOG(INFO) << "phdr_size=" << PHDR_SIZE << std::hex << " " << PHDR_SIZE;
- }
-
- memset(&program_headers_, 0, sizeof(program_headers_));
- program_headers_[PH_PHDR].p_type = PT_PHDR;
- program_headers_[PH_PHDR].p_offset = PHDR_OFFSET;
- program_headers_[PH_PHDR].p_vaddr = PHDR_OFFSET;
- program_headers_[PH_PHDR].p_paddr = PHDR_OFFSET;
- program_headers_[PH_PHDR].p_filesz = sizeof(program_headers_);
- program_headers_[PH_PHDR].p_memsz = sizeof(program_headers_);
- program_headers_[PH_PHDR].p_flags = PF_R;
- program_headers_[PH_PHDR].p_align = sizeof(Elf_Word);
-
- program_headers_[PH_LOAD_R__].p_type = PT_LOAD;
- program_headers_[PH_LOAD_R__].p_offset = 0;
- program_headers_[PH_LOAD_R__].p_vaddr = 0;
- program_headers_[PH_LOAD_R__].p_paddr = 0;
- program_headers_[PH_LOAD_R__].p_flags = PF_R;
-
- program_headers_[PH_LOAD_R_X].p_type = PT_LOAD;
- program_headers_[PH_LOAD_R_X].p_flags = PF_R | PF_X;
-
- program_headers_[PH_LOAD_RW_].p_type = PT_LOAD;
- program_headers_[PH_LOAD_RW_].p_flags = PF_R | PF_W;
-
- program_headers_[PH_DYNAMIC].p_type = PT_DYNAMIC;
- program_headers_[PH_DYNAMIC].p_flags = PF_R | PF_W;
-
- // Get the dynstr string.
- dynstr_ = dynsym_builder_.GenerateStrtab();
-
- // Add the SONAME to the dynstr.
- dynstr_soname_offset_ = dynstr_.size();
- std::string file_name(elf_file_->GetPath());
- size_t directory_separator_pos = file_name.rfind('/');
- if (directory_separator_pos != std::string::npos) {
- file_name = file_name.substr(directory_separator_pos + 1);
- }
- dynstr_ += file_name;
- dynstr_ += '\0';
- if (debug_logging_) {
- LOG(INFO) << "dynstr size (bytes) =" << dynstr_.size()
- << std::hex << " " << dynstr_.size();
- LOG(INFO) << "dynsym size (elements)=" << dynsym_builder_.GetSize()
- << std::hex << " " << dynsym_builder_.GetSize();
- }
-
- // Get the section header string table.
- shstrtab_ += '\0';
-
- // Setup sym_undef
- memset(&null_hdr_, 0, sizeof(null_hdr_));
- null_hdr_.sh_type = SHT_NULL;
- null_hdr_.sh_link = SHN_UNDEF;
- section_ptrs_.push_back(&null_hdr_);
-
- section_index_ = 1;
-
- // setup .dynsym
- section_ptrs_.push_back(&dynsym_builder_.section_);
- AssignSectionStr(&dynsym_builder_, &shstrtab_);
- dynsym_builder_.section_index_ = section_index_++;
-
- // Setup .dynstr
- section_ptrs_.push_back(&dynsym_builder_.strtab_.section_);
- AssignSectionStr(&dynsym_builder_.strtab_, &shstrtab_);
- dynsym_builder_.strtab_.section_index_ = section_index_++;
-
- // Setup .hash
- section_ptrs_.push_back(&hash_builder_.section_);
- AssignSectionStr(&hash_builder_, &shstrtab_);
- hash_builder_.section_index_ = section_index_++;
-
- // Setup .rodata
- section_ptrs_.push_back(&rodata_builder_.section_);
- AssignSectionStr(&rodata_builder_, &shstrtab_);
- rodata_builder_.section_index_ = section_index_++;
-
- // Setup .text
- section_ptrs_.push_back(&text_builder_.section_);
- AssignSectionStr(&text_builder_, &shstrtab_);
- text_builder_.section_index_ = section_index_++;
-
- // Setup .dynamic
- section_ptrs_.push_back(&dynamic_builder_.section_);
- AssignSectionStr(&dynamic_builder_, &shstrtab_);
- dynamic_builder_.section_index_ = section_index_++;
-
- // Fill in the hash section.
- hash_ = dynsym_builder_.GenerateHashContents();
-
- if (debug_logging_) {
- LOG(INFO) << ".hash size (bytes)=" << hash_.size() * sizeof(Elf_Word)
- << std::hex << " " << hash_.size() * sizeof(Elf_Word);
- }
-
- Elf_Word base_offset = sizeof(Elf_Ehdr) + sizeof(program_headers_);
-
- // Get the layout in the sections.
- //
- // Get the layout of the dynsym section.
- dynsym_builder_.section_.sh_offset = RoundUp(base_offset, dynsym_builder_.section_.sh_addralign);
- dynsym_builder_.section_.sh_addr = dynsym_builder_.section_.sh_offset;
- dynsym_builder_.section_.sh_size = dynsym_builder_.GetSize() * sizeof(Elf_Sym);
- dynsym_builder_.section_.sh_link = dynsym_builder_.GetLink();
-
- // Get the layout of the dynstr section.
- dynsym_builder_.strtab_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (dynsym_builder_.strtab_.section_,
- dynsym_builder_.section_);
- dynsym_builder_.strtab_.section_.sh_addr = dynsym_builder_.strtab_.section_.sh_offset;
- dynsym_builder_.strtab_.section_.sh_size = dynstr_.size();
- dynsym_builder_.strtab_.section_.sh_link = dynsym_builder_.strtab_.GetLink();
-
- // Get the layout of the hash section
- hash_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (hash_builder_.section_,
- dynsym_builder_.strtab_.section_);
- hash_builder_.section_.sh_addr = hash_builder_.section_.sh_offset;
- hash_builder_.section_.sh_size = hash_.size() * sizeof(Elf_Word);
- hash_builder_.section_.sh_link = hash_builder_.GetLink();
-
- // Get the layout of the rodata section.
- rodata_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (rodata_builder_.section_,
- hash_builder_.section_);
- rodata_builder_.section_.sh_addr = rodata_builder_.section_.sh_offset;
- rodata_builder_.section_.sh_size = rodata_builder_.size_;
- rodata_builder_.section_.sh_link = rodata_builder_.GetLink();
-
- // Get the layout of the text section.
- text_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (text_builder_.section_, rodata_builder_.section_);
- text_builder_.section_.sh_addr = text_builder_.section_.sh_offset;
- text_builder_.section_.sh_size = text_builder_.size_;
- text_builder_.section_.sh_link = text_builder_.GetLink();
- CHECK_ALIGNED(rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size, kPageSize);
-
- // Get the layout of the dynamic section.
- dynamic_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (dynamic_builder_.section_,
- text_builder_.section_);
- dynamic_builder_.section_.sh_addr = dynamic_builder_.section_.sh_offset;
- dynamic_builder_.section_.sh_size = dynamic_builder_.GetSize() * sizeof(Elf_Dyn);
- dynamic_builder_.section_.sh_link = dynamic_builder_.GetLink();
-
- if (debug_logging_) {
- LOG(INFO) << "dynsym off=" << dynsym_builder_.section_.sh_offset
- << " dynsym size=" << dynsym_builder_.section_.sh_size;
- LOG(INFO) << "dynstr off=" << dynsym_builder_.strtab_.section_.sh_offset
- << " dynstr size=" << dynsym_builder_.strtab_.section_.sh_size;
- LOG(INFO) << "hash off=" << hash_builder_.section_.sh_offset
- << " hash size=" << hash_builder_.section_.sh_size;
- LOG(INFO) << "rodata off=" << rodata_builder_.section_.sh_offset
- << " rodata size=" << rodata_builder_.section_.sh_size;
- LOG(INFO) << "text off=" << text_builder_.section_.sh_offset
- << " text size=" << text_builder_.section_.sh_size;
- LOG(INFO) << "dynamic off=" << dynamic_builder_.section_.sh_offset
- << " dynamic size=" << dynamic_builder_.section_.sh_size;
- }
-
- return true;
-}
-
-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>
-bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::Write() {
- std::vector<ElfFilePiece> pieces;
- Elf_Shdr prev = dynamic_builder_.section_;
- std::string strtab;
-
- if (IncludingDebugSymbols()) {
- // Setup .symtab
- section_ptrs_.push_back(&symtab_builder_.section_);
- AssignSectionStr(&symtab_builder_, &shstrtab_);
- symtab_builder_.section_index_ = section_index_++;
-
- // Setup .strtab
- section_ptrs_.push_back(&symtab_builder_.strtab_.section_);
- AssignSectionStr(&symtab_builder_.strtab_, &shstrtab_);
- symtab_builder_.strtab_.section_index_ = section_index_++;
-
- strtab = symtab_builder_.GenerateStrtab();
- if (debug_logging_) {
- LOG(INFO) << "strtab size (bytes) =" << strtab.size()
- << std::hex << " " << strtab.size();
- LOG(INFO) << "symtab size (elements) =" << symtab_builder_.GetSize()
- << std::hex << " " << symtab_builder_.GetSize();
- }
- }
-
- // Setup all the other sections.
- for (ElfRawSectionBuilder *builder = other_builders_.data(),
- *end = builder + other_builders_.size();
- builder != end; ++builder) {
- section_ptrs_.push_back(&builder->section_);
- AssignSectionStr(builder, &shstrtab_);
- builder->section_index_ = section_index_++;
- }
-
- // Setup shstrtab
- section_ptrs_.push_back(&shstrtab_builder_.section_);
- AssignSectionStr(&shstrtab_builder_, &shstrtab_);
- shstrtab_builder_.section_index_ = section_index_++;
-
- if (debug_logging_) {
- LOG(INFO) << ".shstrtab size (bytes) =" << shstrtab_.size()
- << std::hex << " " << shstrtab_.size();
- LOG(INFO) << "section list size (elements)=" << section_ptrs_.size()
- << std::hex << " " << section_ptrs_.size();
- }
-
- if (IncludingDebugSymbols()) {
- // Get the layout of the symtab section.
- symtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (symtab_builder_.section_,
- dynamic_builder_.section_);
- symtab_builder_.section_.sh_addr = 0;
- // Add to leave space for the null symbol.
- symtab_builder_.section_.sh_size = symtab_builder_.GetSize() * sizeof(Elf_Sym);
- symtab_builder_.section_.sh_link = symtab_builder_.GetLink();
-
- // Get the layout of the dynstr section.
- symtab_builder_.strtab_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (symtab_builder_.strtab_.section_,
- symtab_builder_.section_);
- symtab_builder_.strtab_.section_.sh_addr = 0;
- symtab_builder_.strtab_.section_.sh_size = strtab.size();
- symtab_builder_.strtab_.section_.sh_link = symtab_builder_.strtab_.GetLink();
-
- prev = symtab_builder_.strtab_.section_;
- if (debug_logging_) {
- LOG(INFO) << "symtab off=" << symtab_builder_.section_.sh_offset
- << " symtab size=" << symtab_builder_.section_.sh_size;
- LOG(INFO) << "strtab off=" << symtab_builder_.strtab_.section_.sh_offset
- << " strtab size=" << symtab_builder_.strtab_.section_.sh_size;
- }
- }
-
- // Get the layout of the extra sections. (This will deal with the debug
- // sections if they are there)
- for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
- it->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>(it->section_, prev);
- it->section_.sh_addr = 0;
- it->section_.sh_size = it->GetBuffer()->size();
- it->section_.sh_link = it->GetLink();
- pieces.push_back(ElfFilePiece(it->name_, it->section_.sh_offset,
- it->GetBuffer()->data(), it->GetBuffer()->size()));
- prev = it->section_;
- if (debug_logging_) {
- LOG(INFO) << it->name_ << " off=" << it->section_.sh_offset
- << " " << it->name_ << " size=" << it->section_.sh_size;
- }
- }
-
- // Get the layout of the shstrtab section
- shstrtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
- (shstrtab_builder_.section_, prev);
- shstrtab_builder_.section_.sh_addr = 0;
- shstrtab_builder_.section_.sh_size = shstrtab_.size();
- shstrtab_builder_.section_.sh_link = shstrtab_builder_.GetLink();
- if (debug_logging_) {
- LOG(INFO) << "shstrtab off=" << shstrtab_builder_.section_.sh_offset
- << " shstrtab size=" << shstrtab_builder_.section_.sh_size;
- }
-
- // The section list comes after come after.
- Elf_Word sections_offset = RoundUp(
- shstrtab_builder_.section_.sh_offset + shstrtab_builder_.section_.sh_size,
- sizeof(Elf_Word));
-
- // Setup the actual symbol arrays.
- std::vector<Elf_Sym> dynsym = dynsym_builder_.GenerateSymtab();
- CHECK_EQ(dynsym.size() * sizeof(Elf_Sym), dynsym_builder_.section_.sh_size);
- std::vector<Elf_Sym> symtab;
- if (IncludingDebugSymbols()) {
- symtab = symtab_builder_.GenerateSymtab();
- CHECK_EQ(symtab.size() * sizeof(Elf_Sym), symtab_builder_.section_.sh_size);
- }
-
- // Setup the dynamic section.
- // This will add the 2 values we cannot know until now time, namely the size
- // and the soname_offset.
- std::vector<Elf_Dyn> dynamic = dynamic_builder_.GetDynamics(dynstr_.size(),
- dynstr_soname_offset_);
- CHECK_EQ(dynamic.size() * sizeof(Elf_Dyn), dynamic_builder_.section_.sh_size);
-
- // Finish setup of the program headers now that we know the layout of the
- // whole file.
- Elf_Word load_r_size = rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size;
- program_headers_[PH_LOAD_R__].p_filesz = load_r_size;
- program_headers_[PH_LOAD_R__].p_memsz = load_r_size;
- program_headers_[PH_LOAD_R__].p_align = rodata_builder_.section_.sh_addralign;
-
- Elf_Word load_rx_size = text_builder_.section_.sh_size;
- program_headers_[PH_LOAD_R_X].p_offset = text_builder_.section_.sh_offset;
- program_headers_[PH_LOAD_R_X].p_vaddr = text_builder_.section_.sh_offset;
- program_headers_[PH_LOAD_R_X].p_paddr = text_builder_.section_.sh_offset;
- program_headers_[PH_LOAD_R_X].p_filesz = load_rx_size;
- program_headers_[PH_LOAD_R_X].p_memsz = load_rx_size;
- program_headers_[PH_LOAD_R_X].p_align = text_builder_.section_.sh_addralign;
-
- program_headers_[PH_LOAD_RW_].p_offset = dynamic_builder_.section_.sh_offset;
- program_headers_[PH_LOAD_RW_].p_vaddr = dynamic_builder_.section_.sh_offset;
- program_headers_[PH_LOAD_RW_].p_paddr = dynamic_builder_.section_.sh_offset;
- program_headers_[PH_LOAD_RW_].p_filesz = dynamic_builder_.section_.sh_size;
- program_headers_[PH_LOAD_RW_].p_memsz = dynamic_builder_.section_.sh_size;
- program_headers_[PH_LOAD_RW_].p_align = dynamic_builder_.section_.sh_addralign;
-
- program_headers_[PH_DYNAMIC].p_offset = dynamic_builder_.section_.sh_offset;
- program_headers_[PH_DYNAMIC].p_vaddr = dynamic_builder_.section_.sh_offset;
- program_headers_[PH_DYNAMIC].p_paddr = dynamic_builder_.section_.sh_offset;
- program_headers_[PH_DYNAMIC].p_filesz = dynamic_builder_.section_.sh_size;
- program_headers_[PH_DYNAMIC].p_memsz = dynamic_builder_.section_.sh_size;
- program_headers_[PH_DYNAMIC].p_align = dynamic_builder_.section_.sh_addralign;
-
- // Finish setup of the Ehdr values.
- elf_header_.e_phoff = PHDR_OFFSET;
- elf_header_.e_shoff = sections_offset;
- elf_header_.e_phnum = PH_NUM;
- elf_header_.e_shnum = section_ptrs_.size();
- elf_header_.e_shstrndx = shstrtab_builder_.section_index_;
-
- // Add the rest of the pieces to the list.
- pieces.push_back(ElfFilePiece("Elf Header", 0, &elf_header_, sizeof(elf_header_)));
- pieces.push_back(ElfFilePiece("Program headers", PHDR_OFFSET,
- &program_headers_, sizeof(program_headers_)));
- pieces.push_back(ElfFilePiece(".dynamic", dynamic_builder_.section_.sh_offset,
- dynamic.data(), dynamic_builder_.section_.sh_size));
- pieces.push_back(ElfFilePiece(".dynsym", dynsym_builder_.section_.sh_offset,
- dynsym.data(), dynsym.size() * sizeof(Elf_Sym)));
- pieces.push_back(ElfFilePiece(".dynstr", dynsym_builder_.strtab_.section_.sh_offset,
- dynstr_.c_str(), dynstr_.size()));
- pieces.push_back(ElfFilePiece(".hash", hash_builder_.section_.sh_offset,
- hash_.data(), hash_.size() * sizeof(Elf_Word)));
- pieces.push_back(ElfFilePiece(".rodata", rodata_builder_.section_.sh_offset,
- nullptr, rodata_builder_.section_.sh_size));
- pieces.push_back(ElfFilePiece(".text", text_builder_.section_.sh_offset,
- nullptr, text_builder_.section_.sh_size));
- if (IncludingDebugSymbols()) {
- pieces.push_back(ElfFilePiece(".symtab", symtab_builder_.section_.sh_offset,
- symtab.data(), symtab.size() * sizeof(Elf_Sym)));
- pieces.push_back(ElfFilePiece(".strtab", symtab_builder_.strtab_.section_.sh_offset,
- strtab.c_str(), strtab.size()));
- }
- pieces.push_back(ElfFilePiece(".shstrtab", shstrtab_builder_.section_.sh_offset,
- &shstrtab_[0], shstrtab_.size()));
- for (uint32_t i = 0; i < section_ptrs_.size(); ++i) {
- // Just add all the sections in induvidually since they are all over the
- // place on the heap/stack.
- Elf_Word cur_off = sections_offset + i * sizeof(Elf_Shdr);
- pieces.push_back(ElfFilePiece("section table piece", cur_off,
- section_ptrs_[i], sizeof(Elf_Shdr)));
- }
-
- if (!WriteOutFile(pieces)) {
- LOG(ERROR) << "Unable to write to file " << elf_file_->GetPath();
- return false;
- }
- // write out the actual oat file data.
- Elf_Word oat_data_offset = rodata_builder_.section_.sh_offset;
- if (static_cast<off_t>(oat_data_offset) != lseek(elf_file_->Fd(), oat_data_offset, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to .rodata offset " << oat_data_offset
- << " for " << elf_file_->GetPath();
- return false;
- }
- std::unique_ptr<BufferedOutputStream> output_stream(
- new BufferedOutputStream(new FileOutputStream(elf_file_)));
- if (!oat_writer_->Write(output_stream.get())) {
- PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file_->GetPath();
- return false;
- }
-
- return true;
-}
-
-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>
-bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::WriteOutFile(const std::vector<ElfFilePiece>& pieces) {
- // TODO It would be nice if this checked for overlap.
- for (auto it = pieces.begin(); it != pieces.end(); ++it) {
- if (it->data_) {
- if (static_cast<off_t>(it->offset_) != lseek(elf_file_->Fd(), it->offset_, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to " << it->dbg_name_ << " offset location "
- << it->offset_ << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(it->data_, it->size_)) {
- PLOG(ERROR) << "Failed to write " << it->dbg_name_ << " for " << elf_file_->GetPath();
- return false;
- }
- }
- }
- return true;
-}
-
-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>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::SetupDynamic() {
- dynamic_builder_.AddDynamicTag(DT_HASH, 0, &hash_builder_);
- dynamic_builder_.AddDynamicTag(DT_STRTAB, 0, &dynsym_builder_.strtab_);
- dynamic_builder_.AddDynamicTag(DT_SYMTAB, 0, &dynsym_builder_);
- dynamic_builder_.AddDynamicTag(DT_SYMENT, sizeof(Elf_Sym));
-}
-
-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>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::SetupRequiredSymbols() {
- dynsym_builder_.AddSymbol("oatdata", &rodata_builder_, 0, true,
- rodata_builder_.size_, STB_GLOBAL, STT_OBJECT);
- dynsym_builder_.AddSymbol("oatexec", &text_builder_, 0, true,
- text_builder_.size_, STB_GLOBAL, STT_OBJECT);
- dynsym_builder_.AddSymbol("oatlastword", &text_builder_, text_builder_.size_ - 4,
- true, 4, STB_GLOBAL, STT_OBJECT);
-}
-
-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>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfDynamicBuilder::AddDynamicTag(Elf_Sword tag, Elf_Word d_un) {
- if (tag == DT_NULL) {
- return;
- }
- dynamics_.push_back({nullptr, tag, d_un});
-}
-
-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>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfDynamicBuilder::AddDynamicTag(Elf_Sword tag, Elf_Word d_un,
- ElfSectionBuilder* section) {
- if (tag == DT_NULL) {
- return;
- }
- dynamics_.push_back({section, tag, d_un});
-}
-
-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<Elf_Dyn> ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfDynamicBuilder::GetDynamics(Elf_Word strsz,
- Elf_Word soname) {
- std::vector<Elf_Dyn> ret;
- for (auto it = dynamics_.cbegin(); it != dynamics_.cend(); ++it) {
- if (it->section_) {
- // We are adding an address relative to a section.
- ret.push_back(
- {it->tag_, {it->off_ + it->section_->section_.sh_addr}});
- } else {
- ret.push_back({it->tag_, {it->off_}});
- }
- }
- ret.push_back({DT_STRSZ, {strsz}});
- ret.push_back({DT_SONAME, {soname}});
- ret.push_back({DT_NULL, {0}});
- return ret;
-}
-
-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<Elf_Sym> ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfSymtabBuilder::GenerateSymtab() {
- std::vector<Elf_Sym> ret;
- Elf_Sym undef_sym;
- memset(&undef_sym, 0, sizeof(undef_sym));
- undef_sym.st_shndx = SHN_UNDEF;
- ret.push_back(undef_sym);
-
- for (auto it = symbols_.cbegin(); it != symbols_.cend(); ++it) {
- Elf_Sym sym;
- memset(&sym, 0, sizeof(sym));
- sym.st_name = it->name_idx_;
- if (it->is_relative_) {
- sym.st_value = it->addr_ + it->section_->section_.sh_offset;
- } else {
- sym.st_value = it->addr_;
- }
- sym.st_size = it->size_;
- sym.st_other = it->other_;
- sym.st_shndx = it->section_->section_index_;
- sym.st_info = it->info_;
-
- ret.push_back(sym);
- }
- return ret;
-}
-
-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::string ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfSymtabBuilder::GenerateStrtab() {
- std::string tab;
- tab += '\0';
- for (auto it = symbols_.begin(); it != symbols_.end(); ++it) {
- it->name_idx_ = tab.size();
- tab += it->name_;
- tab += '\0';
- }
- strtab_.section_.sh_size = tab.size();
- return tab;
-}
-
-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>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::AssignSectionStr(
- ElfSectionBuilder* builder, std::string* strtab) {
- builder->section_.sh_name = strtab->size();
- *strtab += builder->name_;
- *strtab += '\0';
- if (debug_logging_) {
- LOG(INFO) << "adding section name \"" << builder->name_ << "\" "
- << "to shstrtab at offset " << builder->section_.sh_name;
- }
-}
-
-// from bionic
-static unsigned elfhash(const char *_name) {
- const unsigned char *name = (const unsigned char *) _name;
- unsigned h = 0, g;
-
- while (*name) {
- h = (h << 4) + *name++;
- g = h & 0xf0000000;
- h ^= g;
- h ^= g >> 24;
- }
- return h;
-}
-
-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<Elf_Word> ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfSymtabBuilder::GenerateHashContents() {
- // Here is how The ELF hash table works.
- // There are 3 arrays to worry about.
- // * The symbol table where the symbol information is.
- // * The bucket array which is an array of indexes into the symtab and chain.
- // * The chain array which is also an array of indexes into the symtab and chain.
- //
- // Lets say the state is something like this.
- // +--------+ +--------+ +-----------+
- // | symtab | | bucket | | chain |
- // | null | | 1 | | STN_UNDEF |
- // | <sym1> | | 4 | | 2 |
- // | <sym2> | | | | 5 |
- // | <sym3> | | | | STN_UNDEF |
- // | <sym4> | | | | 3 |
- // | <sym5> | | | | STN_UNDEF |
- // +--------+ +--------+ +-----------+
- //
- // The lookup process (in python psudocode) is
- //
- // def GetSym(name):
- // # NB STN_UNDEF == 0
- // indx = bucket[elfhash(name) % num_buckets]
- // while indx != STN_UNDEF:
- // if GetSymbolName(symtab[indx]) == name:
- // return symtab[indx]
- // indx = chain[indx]
- // return SYMBOL_NOT_FOUND
- //
- // Between bucket and chain arrays every symtab index must be present exactly
- // once (except for STN_UNDEF, which must be present 1 + num_bucket times).
-
- // Select number of buckets.
- // This is essentially arbitrary.
- Elf_Word nbuckets;
- Elf_Word chain_size = GetSize();
- if (symbols_.size() < 8) {
- nbuckets = 2;
- } else if (symbols_.size() < 32) {
- nbuckets = 4;
- } else if (symbols_.size() < 256) {
- nbuckets = 16;
- } else {
- // Have about 32 ids per bucket.
- nbuckets = RoundUp(symbols_.size()/32, 2);
- }
- std::vector<Elf_Word> hash;
- hash.push_back(nbuckets);
- hash.push_back(chain_size);
- uint32_t bucket_offset = hash.size();
- uint32_t chain_offset = bucket_offset + nbuckets;
- hash.resize(hash.size() + nbuckets + chain_size, 0);
-
- Elf_Word* buckets = hash.data() + bucket_offset;
- Elf_Word* chain = hash.data() + chain_offset;
-
- // Set up the actual hash table.
- for (Elf_Word i = 0; i < symbols_.size(); i++) {
- // Add 1 since we need to have the null symbol that is not in the symbols
- // list.
- Elf_Word index = i + 1;
- Elf_Word hash_val = static_cast<Elf_Word>(elfhash(symbols_[i].name_.c_str())) % nbuckets;
- if (buckets[hash_val] == 0) {
- buckets[hash_val] = index;
- } else {
- hash_val = buckets[hash_val];
- CHECK_LT(hash_val, chain_size);
- while (chain[hash_val] != 0) {
- hash_val = chain[hash_val];
- CHECK_LT(hash_val, chain_size);
- }
- chain[hash_val] = index;
- // Check for loops. Works because if this is non-empty then there must be
- // another cell which already contains the same symbol index as this one,
- // which means some symbol has more then one name, which isn't allowed.
- CHECK_EQ(chain[index], static_cast<Elf_Word>(0));
- }
- }
-
- return hash;
-}
-
-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>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::SetupEhdr() {
- memset(&elf_header_, 0, sizeof(elf_header_));
- elf_header_.e_ident[EI_MAG0] = ELFMAG0;
- elf_header_.e_ident[EI_MAG1] = ELFMAG1;
- elf_header_.e_ident[EI_MAG2] = ELFMAG2;
- elf_header_.e_ident[EI_MAG3] = ELFMAG3;
- elf_header_.e_ident[EI_CLASS] = ELFCLASS32;
- elf_header_.e_ident[EI_DATA] = ELFDATA2LSB;
- elf_header_.e_ident[EI_VERSION] = EV_CURRENT;
- elf_header_.e_ident[EI_OSABI] = ELFOSABI_LINUX;
- elf_header_.e_ident[EI_ABIVERSION] = 0;
- elf_header_.e_type = ET_DYN;
- elf_header_.e_version = 1;
- elf_header_.e_entry = 0;
- elf_header_.e_ehsize = sizeof(Elf_Ehdr);
- elf_header_.e_phentsize = sizeof(Elf_Phdr);
- elf_header_.e_shentsize = sizeof(Elf_Shdr);
- elf_header_.e_phoff = sizeof(Elf_Ehdr);
-}
-
-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>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfBuilder::SetISA(InstructionSet isa) {
- switch (isa) {
- case kArm:
- // Fall through.
- case kThumb2: {
- elf_header_.e_machine = EM_ARM;
- elf_header_.e_flags = EF_ARM_EABI_VER5;
- break;
- }
- case kArm64: {
- elf_header_.e_machine = EM_AARCH64;
- elf_header_.e_flags = 0;
- break;
- }
- case kX86: {
- elf_header_.e_machine = EM_386;
- elf_header_.e_flags = 0;
- break;
- }
- case kX86_64: {
- elf_header_.e_machine = EM_X86_64;
- elf_header_.e_flags = 0;
- break;
- }
- case kMips: {
- elf_header_.e_machine = EM_MIPS;
- elf_header_.e_flags = (EF_MIPS_NOREORDER |
- EF_MIPS_PIC |
- EF_MIPS_CPIC |
- EF_MIPS_ABI_O32 |
- EF_MIPS_ARCH_32R2);
- break;
- }
- default: {
- fatal_error_ = true;
- LOG(FATAL) << "Unknown instruction set: " << isa;
- break;
- }
- }
-}
-
-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>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ElfSymtabBuilder::AddSymbol(
- const std::string& name, const ElfSectionBuilder* section, Elf_Addr addr,
- bool is_relative, Elf_Word size, uint8_t binding, uint8_t type, uint8_t other) {
- CHECK(section);
- ElfSymtabBuilder::ElfSymbolState state {name, section, addr, size, is_relative,
- MakeStInfo(binding, type), other, 0};
- symbols_.push_back(state);
-}
-
-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>
-bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::Create(File* elf_file,
OatWriter* oat_writer,
const std::vector<const DexFile*>& dex_files,
@@ -913,17 +85,14 @@
return elf_writer.Write(oat_writer, dex_files, android_root, is_host);
}
-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>
// Add patch information to this section. Each patch is a Elf_Word that
// identifies an offset from the start of the text section
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::ReservePatchSpace(std::vector<uint8_t>* buffer, bool debug) {
+static void ReservePatchSpace(const CompilerDriver* compiler_driver, std::vector<uint8_t>* buffer,
+ bool debug) {
size_t size =
- compiler_driver_->GetCodeToPatch().size() +
- compiler_driver_->GetMethodsToPatch().size() +
- compiler_driver_->GetClassesToPatch().size();
+ compiler_driver->GetCodeToPatch().size() +
+ compiler_driver->GetMethodsToPatch().size() +
+ compiler_driver->GetClassesToPatch().size();
if (size == 0) {
if (debug) {
LOG(INFO) << "No patches to record";
@@ -1046,6 +215,25 @@
}
}
+class OatWriterWrapper : public CodeOutput {
+ public:
+ explicit OatWriterWrapper(OatWriter* oat_writer) : oat_writer_(oat_writer) {}
+
+ bool Write(OutputStream* out) OVERRIDE {
+ return oat_writer_->Write(out);
+ }
+ private:
+ OatWriter* oat_writer_;
+};
+
+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>
+static void WriteDebugSymbols(const CompilerDriver* compiler_driver,
+ ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
+ OatWriter* oat_writer);
+
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>
@@ -1059,145 +247,40 @@
Elf_Word oat_data_size = oat_header.GetExecutableOffset();
uint32_t oat_exec_size = oat_writer->GetSize() - oat_data_size;
- std::unique_ptr<ElfBuilder> builder(new ElfBuilder(
- oat_writer,
- elf_file_,
- compiler_driver_->GetInstructionSet(),
- 0,
- oat_data_size,
- oat_data_size,
- oat_exec_size,
- compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols(),
- debug));
+ OatWriterWrapper wrapper(oat_writer);
+
+ std::unique_ptr<ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr> > builder(
+ new ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>(
+ &wrapper,
+ elf_file_,
+ compiler_driver_->GetInstructionSet(),
+ 0,
+ oat_data_size,
+ oat_data_size,
+ oat_exec_size,
+ compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols(),
+ debug));
if (!builder->Init()) {
return false;
}
if (compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
- WriteDebugSymbols(builder.get(), oat_writer);
+ WriteDebugSymbols(compiler_driver_, builder.get(), oat_writer);
}
if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {
- ElfRawSectionBuilder oat_patches(".oat_patches", SHT_OAT_PATCH, 0, NULL, 0,
- sizeof(uintptr_t), sizeof(uintptr_t));
- ReservePatchSpace(oat_patches.GetBuffer(), debug);
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> oat_patches(
+ ".oat_patches", SHT_OAT_PATCH, 0, NULL, 0, sizeof(uintptr_t), sizeof(uintptr_t));
+ ReservePatchSpace(compiler_driver_, oat_patches.GetBuffer(), debug);
builder->RegisterRawSection(oat_patches);
}
return builder->Write();
}
-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>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::WriteDebugSymbols(ElfBuilder* builder, OatWriter* oat_writer) {
- std::unique_ptr<std::vector<uint8_t>> cfi_info(
- ConstructCIEFrame(compiler_driver_->GetInstructionSet()));
-
- Elf_Addr text_section_address = builder->text_builder_.section_.sh_addr;
-
- // Iterate over the compiled methods.
- const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
- ElfSymtabBuilder* symtab = &builder->symtab_builder_;
- for (auto it = method_info.begin(); it != method_info.end(); ++it) {
- symtab->AddSymbol(it->method_name_, &builder->text_builder_, it->low_pc_, true,
- it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC);
-
- // Include CFI for compiled method, if possible.
- if (cfi_info.get() != nullptr) {
- DCHECK(it->compiled_method_ != nullptr);
-
- // Copy in the FDE, if present
- const std::vector<uint8_t>* fde = it->compiled_method_->GetCFIInfo();
- if (fde != nullptr) {
- // Copy the information into cfi_info and then fix the address in the new copy.
- int cur_offset = cfi_info->size();
- cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
-
- bool is_64bit = *(reinterpret_cast<const uint32_t*>(fde->data())) == 0xffffffff;
-
- // Set the 'CIE_pointer' field.
- uint64_t CIE_pointer = cur_offset + (is_64bit ? 12 : 4);
- uint64_t offset_to_update = CIE_pointer;
- if (is_64bit) {
- (*cfi_info)[offset_to_update+0] = CIE_pointer;
- (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
- (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
- (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
- (*cfi_info)[offset_to_update+4] = CIE_pointer >> 32;
- (*cfi_info)[offset_to_update+5] = CIE_pointer >> 40;
- (*cfi_info)[offset_to_update+6] = CIE_pointer >> 48;
- (*cfi_info)[offset_to_update+7] = CIE_pointer >> 56;
- } else {
- (*cfi_info)[offset_to_update+0] = CIE_pointer;
- (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
- (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
- (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
- }
-
- // Set the 'initial_location' field.
- offset_to_update += is_64bit ? 8 : 4;
- if (is_64bit) {
- const uint64_t quick_code_start = it->low_pc_ + text_section_address;
- (*cfi_info)[offset_to_update+0] = quick_code_start;
- (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
- (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
- (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
- (*cfi_info)[offset_to_update+4] = quick_code_start >> 32;
- (*cfi_info)[offset_to_update+5] = quick_code_start >> 40;
- (*cfi_info)[offset_to_update+6] = quick_code_start >> 48;
- (*cfi_info)[offset_to_update+7] = quick_code_start >> 56;
- } else {
- const uint32_t quick_code_start = it->low_pc_ + text_section_address;
- (*cfi_info)[offset_to_update+0] = quick_code_start;
- (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
- (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
- (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
- }
- }
- }
- }
-
- bool hasCFI = (cfi_info.get() != nullptr);
- bool hasLineInfo = false;
- for (auto& dbg_info : oat_writer->GetCFIMethodInfo()) {
- if (dbg_info.dbgstream_ != nullptr &&
- !dbg_info.compiled_method_->GetSrcMappingTable().empty()) {
- hasLineInfo = true;
- break;
- }
- }
-
- if (hasLineInfo || hasCFI) {
- ElfRawSectionBuilder debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- ElfRawSectionBuilder debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- ElfRawSectionBuilder debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- ElfRawSectionBuilder debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
-
- FillInCFIInformation(oat_writer, debug_info.GetBuffer(),
- debug_abbrev.GetBuffer(), debug_str.GetBuffer(),
- hasLineInfo ? debug_line.GetBuffer() : nullptr,
- text_section_address);
-
- builder->RegisterRawSection(debug_info);
- builder->RegisterRawSection(debug_abbrev);
-
- if (hasCFI) {
- ElfRawSectionBuilder eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0);
- eh_frame.SetBuffer(std::move(*cfi_info.get()));
- builder->RegisterRawSection(eh_frame);
- }
-
- if (hasLineInfo) {
- builder->RegisterRawSection(debug_line);
- }
-
- builder->RegisterRawSection(debug_str);
- }
-}
-
class LineTableGenerator FINAL : public Leb128Encoder {
public:
LineTableGenerator(int line_base, int line_range, int opcode_base,
@@ -1353,16 +436,19 @@
}
}
-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>
-void ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
- Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::FillInCFIInformation(OatWriter* oat_writer,
- std::vector<uint8_t>* dbg_info,
- std::vector<uint8_t>* dbg_abbrev,
- std::vector<uint8_t>* dbg_str,
- std::vector<uint8_t>* dbg_line,
- uint32_t text_section_offset) {
+/*
+ * @brief Generate the DWARF debug_info and debug_abbrev sections
+ * @param oat_writer The Oat file Writer.
+ * @param dbg_info Compilation unit information.
+ * @param dbg_abbrev Abbreviations used to generate dbg_info.
+ * @param dbg_str Debug strings.
+ */
+static void FillInCFIInformation(OatWriter* oat_writer,
+ std::vector<uint8_t>* dbg_info,
+ std::vector<uint8_t>* dbg_abbrev,
+ std::vector<uint8_t>* dbg_str,
+ std::vector<uint8_t>* dbg_line,
+ uint32_t text_section_offset) {
const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
uint32_t producer_str_offset = PushStr(dbg_str, "Android dex2oat");
@@ -1591,6 +677,130 @@
UpdateWord(dbg_info, cunit_length, dbg_info->size() - cunit_length - 4);
}
+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>
+static void WriteDebugSymbols(const CompilerDriver* compiler_driver,
+ ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
+ OatWriter* oat_writer) {
+ std::unique_ptr<std::vector<uint8_t>> cfi_info(
+ ConstructCIEFrame(compiler_driver->GetInstructionSet()));
+
+ Elf_Addr text_section_address = builder->text_builder_.section_.sh_addr;
+
+ // Iterate over the compiled methods.
+ const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
+ ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr,
+ Elf_Sym, Elf_Shdr>* symtab = &builder->symtab_builder_;
+ for (auto it = method_info.begin(); it != method_info.end(); ++it) {
+ symtab->AddSymbol(it->method_name_, &builder->text_builder_, it->low_pc_, true,
+ it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC);
+
+ // Include CFI for compiled method, if possible.
+ if (cfi_info.get() != nullptr) {
+ DCHECK(it->compiled_method_ != nullptr);
+
+ // Copy in the FDE, if present
+ const std::vector<uint8_t>* fde = it->compiled_method_->GetCFIInfo();
+ if (fde != nullptr) {
+ // Copy the information into cfi_info and then fix the address in the new copy.
+ int cur_offset = cfi_info->size();
+ cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
+
+ bool is_64bit = *(reinterpret_cast<const uint32_t*>(fde->data())) == 0xffffffff;
+
+ // Set the 'CIE_pointer' field.
+ uint64_t CIE_pointer = cur_offset + (is_64bit ? 12 : 4);
+ uint64_t offset_to_update = CIE_pointer;
+ if (is_64bit) {
+ (*cfi_info)[offset_to_update+0] = CIE_pointer;
+ (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
+ (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
+ (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
+ (*cfi_info)[offset_to_update+4] = CIE_pointer >> 32;
+ (*cfi_info)[offset_to_update+5] = CIE_pointer >> 40;
+ (*cfi_info)[offset_to_update+6] = CIE_pointer >> 48;
+ (*cfi_info)[offset_to_update+7] = CIE_pointer >> 56;
+ } else {
+ (*cfi_info)[offset_to_update+0] = CIE_pointer;
+ (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
+ (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
+ (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
+ }
+
+ // Set the 'initial_location' field.
+ offset_to_update += is_64bit ? 8 : 4;
+ if (is_64bit) {
+ const uint64_t quick_code_start = it->low_pc_ + text_section_address;
+ (*cfi_info)[offset_to_update+0] = quick_code_start;
+ (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
+ (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
+ (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
+ (*cfi_info)[offset_to_update+4] = quick_code_start >> 32;
+ (*cfi_info)[offset_to_update+5] = quick_code_start >> 40;
+ (*cfi_info)[offset_to_update+6] = quick_code_start >> 48;
+ (*cfi_info)[offset_to_update+7] = quick_code_start >> 56;
+ } else {
+ const uint32_t quick_code_start = it->low_pc_ + text_section_address;
+ (*cfi_info)[offset_to_update+0] = quick_code_start;
+ (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
+ (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
+ (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
+ }
+ }
+ }
+ }
+
+ bool hasCFI = (cfi_info.get() != nullptr);
+ bool hasLineInfo = false;
+ for (auto& dbg_info : oat_writer->GetCFIMethodInfo()) {
+ if (dbg_info.dbgstream_ != nullptr &&
+ !dbg_info.compiled_method_->GetSrcMappingTable().empty()) {
+ hasLineInfo = true;
+ break;
+ }
+ }
+
+ if (hasLineInfo || hasCFI) {
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_info(".debug_info",
+ SHT_PROGBITS,
+ 0, nullptr, 0, 1, 0);
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_abbrev(".debug_abbrev",
+ SHT_PROGBITS,
+ 0, nullptr, 0, 1, 0);
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_str(".debug_str",
+ SHT_PROGBITS,
+ 0, nullptr, 0, 1, 0);
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_line(".debug_line",
+ SHT_PROGBITS,
+ 0, nullptr, 0, 1, 0);
+
+ FillInCFIInformation(oat_writer, debug_info.GetBuffer(),
+ debug_abbrev.GetBuffer(), debug_str.GetBuffer(),
+ hasLineInfo ? debug_line.GetBuffer() : nullptr,
+ text_section_address);
+
+ builder->RegisterRawSection(debug_info);
+ builder->RegisterRawSection(debug_abbrev);
+
+ if (hasCFI) {
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> eh_frame(".eh_frame",
+ SHT_PROGBITS,
+ SHF_ALLOC,
+ nullptr, 0, 4, 0);
+ eh_frame.SetBuffer(std::move(*cfi_info.get()));
+ builder->RegisterRawSection(eh_frame);
+ }
+
+ if (hasLineInfo) {
+ builder->RegisterRawSection(debug_line);
+ }
+
+ builder->RegisterRawSection(debug_str);
+ }
+}
+
// Explicit instantiations
template class ElfWriterQuick<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>;
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index 890528e..4990ed0 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -19,7 +19,6 @@
#include "elf_utils.h"
#include "elf_writer.h"
-#include "instruction_set.h"
namespace art {
@@ -50,300 +49,6 @@
: ElfWriter(driver, elf_file) {}
~ElfWriterQuick() {}
- class ElfBuilder;
- void WriteDebugSymbols(ElfBuilder* builder, OatWriter* oat_writer);
- void ReservePatchSpace(std::vector<uint8_t>* buffer, bool debug);
-
- class ElfSectionBuilder {
- public:
- ElfSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
- const ElfSectionBuilder *link, Elf_Word info, Elf_Word align,
- Elf_Word entsize)
- : name_(sec_name), link_(link) {
- memset(§ion_, 0, sizeof(section_));
- section_.sh_type = type;
- section_.sh_flags = flags;
- section_.sh_info = info;
- section_.sh_addralign = align;
- section_.sh_entsize = entsize;
- }
-
- virtual ~ElfSectionBuilder() {}
-
- Elf_Shdr section_;
- Elf_Word section_index_ = 0;
-
- protected:
- const std::string name_;
- const ElfSectionBuilder* link_;
-
- Elf_Word GetLink() {
- return (link_) ? link_->section_index_ : 0;
- }
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfDynamicBuilder : public ElfSectionBuilder {
- public:
- void AddDynamicTag(Elf_Sword tag, Elf_Word d_un);
- void AddDynamicTag(Elf_Sword tag, Elf_Word offset, ElfSectionBuilder* section);
-
- ElfDynamicBuilder(const std::string& sec_name, ElfSectionBuilder *link)
- : ElfSectionBuilder(sec_name, SHT_DYNAMIC, SHF_ALLOC | SHF_ALLOC, link,
- 0, kPageSize, sizeof(Elf_Dyn)) {}
- ~ElfDynamicBuilder() {}
-
- protected:
- struct ElfDynamicState {
- ElfSectionBuilder* section_;
- Elf_Sword tag_;
- Elf_Word off_;
- };
- std::vector<ElfDynamicState> dynamics_;
- Elf_Word GetSize() {
- // Add 1 for the DT_NULL, 1 for DT_STRSZ, and 1 for DT_SONAME. All of
- // these must be added when we actually put the file together because
- // their values are very dependent on state.
- return dynamics_.size() + 3;
- }
-
- // Create the actual dynamic vector. strsz should be the size of the .dynstr
- // table and soname_off should be the offset of the soname in .dynstr.
- // Since niether can be found prior to final layout we will wait until here
- // to add them.
- std::vector<Elf_Dyn> GetDynamics(Elf_Word strsz, Elf_Word soname_off);
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfRawSectionBuilder : public ElfSectionBuilder {
- public:
- ElfRawSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
- const ElfSectionBuilder* link, Elf_Word info, Elf_Word align,
- Elf_Word entsize)
- : ElfSectionBuilder(sec_name, type, flags, link, info, align, entsize) {}
- ~ElfRawSectionBuilder() {}
- std::vector<uint8_t>* GetBuffer() { return &buf_; }
- void SetBuffer(std::vector<uint8_t>&& buf) { buf_ = buf; }
-
- protected:
- std::vector<uint8_t> buf_;
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfOatSectionBuilder : public ElfSectionBuilder {
- public:
- ElfOatSectionBuilder(const std::string& sec_name, Elf_Word size, Elf_Word offset,
- Elf_Word type, Elf_Word flags)
- : ElfSectionBuilder(sec_name, type, flags, NULL, 0, kPageSize, 0),
- offset_(offset), size_(size) {}
- ~ElfOatSectionBuilder() {}
-
- protected:
- // Offset of the content within the file.
- Elf_Word offset_;
- // Size of the content within the file.
- Elf_Word size_;
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfSymtabBuilder : public ElfSectionBuilder {
- public:
- // Add a symbol with given name to this symtab. The symbol refers to
- // 'relative_addr' within the given section and has the given attributes.
- void AddSymbol(const std::string& name,
- const ElfSectionBuilder* section,
- Elf_Addr addr,
- bool is_relative,
- Elf_Word size,
- uint8_t binding,
- uint8_t type,
- uint8_t other = 0);
-
- ElfSymtabBuilder(const std::string& sec_name, Elf_Word type,
- const std::string& str_name, Elf_Word str_type, bool alloc)
- : ElfSectionBuilder(sec_name, type, ((alloc) ? SHF_ALLOC : 0U), &strtab_, 0,
- sizeof(Elf_Word), sizeof(Elf_Sym)),
- str_name_(str_name), str_type_(str_type),
- strtab_(str_name, str_type, ((alloc) ? SHF_ALLOC : 0U), NULL, 0, 1, 1) {}
- ~ElfSymtabBuilder() {}
-
- protected:
- std::vector<Elf_Word> GenerateHashContents();
- std::string GenerateStrtab();
- std::vector<Elf_Sym> GenerateSymtab();
-
- Elf_Word GetSize() {
- // 1 is for the implicit NULL symbol.
- return symbols_.size() + 1;
- }
-
- struct ElfSymbolState {
- const std::string name_;
- const ElfSectionBuilder* section_;
- Elf_Addr addr_;
- Elf_Word size_;
- bool is_relative_;
- uint8_t info_;
- uint8_t other_;
- // Used during Write() to temporarially hold name index in the strtab.
- Elf_Word name_idx_;
- };
-
- // Information for the strsym for dynstr sections.
- const std::string str_name_;
- Elf_Word str_type_;
- // The symbols in the same order they will be in the symbol table.
- std::vector<ElfSymbolState> symbols_;
- ElfSectionBuilder strtab_;
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfBuilder FINAL {
- public:
- ElfBuilder(OatWriter* oat_writer,
- File* elf_file,
- InstructionSet isa,
- Elf_Word rodata_relative_offset,
- Elf_Word rodata_size,
- Elf_Word text_relative_offset,
- Elf_Word text_size,
- const bool add_symbols,
- bool debug = false)
- : oat_writer_(oat_writer),
- elf_file_(elf_file),
- add_symbols_(add_symbols),
- debug_logging_(debug),
- text_builder_(".text", text_size, text_relative_offset, SHT_PROGBITS,
- SHF_ALLOC | SHF_EXECINSTR),
- rodata_builder_(".rodata", rodata_size, rodata_relative_offset,
- SHT_PROGBITS, SHF_ALLOC),
- dynsym_builder_(".dynsym", SHT_DYNSYM, ".dynstr", SHT_STRTAB, true),
- symtab_builder_(".symtab", SHT_SYMTAB, ".strtab", SHT_STRTAB, false),
- hash_builder_(".hash", SHT_HASH, SHF_ALLOC, &dynsym_builder_, 0,
- sizeof(Elf_Word), sizeof(Elf_Word)),
- dynamic_builder_(".dynamic", &dynsym_builder_),
- shstrtab_builder_(".shstrtab", SHT_STRTAB, 0, NULL, 0, 1, 1) {
- SetupEhdr();
- SetupDynamic();
- SetupRequiredSymbols();
- SetISA(isa);
- }
- ~ElfBuilder() {}
-
- bool Init();
- bool Write();
-
- // Adds the given raw section to the builder. This will copy it. The caller
- // is responsible for deallocating their copy.
- void RegisterRawSection(ElfRawSectionBuilder bld) {
- other_builders_.push_back(bld);
- }
-
- private:
- OatWriter* oat_writer_;
- File* elf_file_;
- const bool add_symbols_;
- const bool debug_logging_;
-
- bool fatal_error_ = false;
-
- // What phdr is.
- static const uint32_t PHDR_OFFSET = sizeof(Elf_Ehdr);
- enum : uint8_t {
- PH_PHDR = 0,
- PH_LOAD_R__ = 1,
- PH_LOAD_R_X = 2,
- PH_LOAD_RW_ = 3,
- PH_DYNAMIC = 4,
- PH_NUM = 5,
- };
- static const uint32_t PHDR_SIZE = sizeof(Elf_Phdr) * PH_NUM;
- Elf_Phdr program_headers_[PH_NUM];
-
- Elf_Ehdr elf_header_;
-
- Elf_Shdr null_hdr_;
- std::string shstrtab_;
- uint32_t section_index_;
- std::string dynstr_;
- uint32_t dynstr_soname_offset_;
- std::vector<Elf_Shdr*> section_ptrs_;
- std::vector<Elf_Word> hash_;
-
- public:
- ElfOatSectionBuilder text_builder_;
- ElfOatSectionBuilder rodata_builder_;
- ElfSymtabBuilder dynsym_builder_;
- ElfSymtabBuilder symtab_builder_;
- ElfSectionBuilder hash_builder_;
- ElfDynamicBuilder dynamic_builder_;
- ElfSectionBuilder shstrtab_builder_;
- std::vector<ElfRawSectionBuilder> other_builders_;
-
- private:
- void SetISA(InstructionSet isa);
- void SetupEhdr();
-
- // Sets up a bunch of the required Dynamic Section entries.
- // Namely it will initialize all the mandatory ones that it can.
- // Specifically:
- // DT_HASH
- // DT_STRTAB
- // DT_SYMTAB
- // DT_SYMENT
- //
- // Some such as DT_SONAME, DT_STRSZ and DT_NULL will be put in later.
- void SetupDynamic();
-
- // Sets up the basic dynamic symbols that are needed, namely all those we
- // can know already.
- //
- // Specifically adds:
- // oatdata
- // oatexec
- // oatlastword
- void SetupRequiredSymbols();
- void AssignSectionStr(ElfSectionBuilder *builder, std::string* strtab);
- struct ElfFilePiece {
- ElfFilePiece(const std::string& name, Elf_Word offset, const void* data, Elf_Word size)
- : dbg_name_(name), offset_(offset), data_(data), size_(size) {}
- ~ElfFilePiece() {}
-
- const std::string& dbg_name_;
- Elf_Word offset_;
- const void *data_;
- Elf_Word size_;
- static bool Compare(ElfFilePiece a, ElfFilePiece b) {
- return a.offset_ < b.offset_;
- }
- };
-
- // Write each of the pieces out to the file.
- bool WriteOutFile(const std::vector<ElfFilePiece>& pieces);
- bool IncludingDebugSymbols() { return add_symbols_ && symtab_builder_.GetSize() > 1; }
- };
-
- /*
- * @brief Generate the DWARF debug_info and debug_abbrev sections
- * @param oat_writer The Oat file Writer.
- * @param dbg_info Compilation unit information.
- * @param dbg_abbrev Abbreviations used to generate dbg_info.
- * @param dbg_str Debug strings.
- */
- void FillInCFIInformation(OatWriter* oat_writer, std::vector<uint8_t>* dbg_info,
- std::vector<uint8_t>* dbg_abbrev, std::vector<uint8_t>* dbg_str,
- std::vector<uint8_t>* dbg_line, uint32_t text_section_offset);
-
DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick);
};
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 680ce0a..c5d1478 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -140,7 +140,7 @@
struct OatWriter::GcMapDataAccess {
static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
- return &compiled_method->GetGcMap();
+ return compiled_method->GetGcMap();
}
static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
@@ -434,13 +434,15 @@
} else {
status = mirror::Class::kStatusNotReady;
}
- const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap();
- size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]);
- bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
- CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
- << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
- << (status < mirror::Class::kStatusVerified) << " " << status << " "
- << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+ std::vector<uint8_t> const * gc_map = compiled_method->GetGcMap();
+ if (gc_map != nullptr) {
+ size_t gc_map_size = gc_map->size() * sizeof(gc_map[0]);
+ bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
+ CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
+ << gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
+ << (status < mirror::Class::kStatusVerified) << " " << status << " "
+ << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+ }
}
DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
@@ -475,7 +477,7 @@
DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u);
const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
- uint32_t map_size = map->size() * sizeof((*map)[0]);
+ uint32_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
if (map_size != 0u) {
auto lb = dedupe_map_.lower_bound(map);
if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(map, lb->first)) {
@@ -645,7 +647,7 @@
// Write deduplicated map.
const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
- size_t map_size = map->size() * sizeof((*map)[0]);
+ size_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
DCHECK((map_size == 0u && map_offset == 0u) ||
(map_size != 0u && map_offset != 0u && map_offset <= offset_))
<< PrettyMethod(it.GetMemberIndex(), *dex_file_);
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 43e6b83..ecd6802 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -413,6 +413,7 @@
current_block_->AddInstruction(new (arena_) HInstanceFieldSet(
null_check,
value,
+ field_type,
resolved_field->GetOffset()));
} else {
current_block_->AddInstruction(new (arena_) HInstanceFieldGet(
@@ -453,7 +454,8 @@
if (is_put) {
HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type);
// TODO: Insert a type check node if the type is Object.
- current_block_->AddInstruction(new (arena_) HArraySet(object, index, value, dex_offset));
+ current_block_->AddInstruction(new (arena_) HArraySet(
+ object, index, value, anticipated_type, dex_offset));
} else {
current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type));
UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
@@ -750,6 +752,13 @@
ARRAY_XX(_CHAR, Primitive::kPrimChar);
ARRAY_XX(_SHORT, Primitive::kPrimShort);
+ case Instruction::ARRAY_LENGTH: {
+ HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot);
+ current_block_->AddInstruction(new (arena_) HArrayLength(object));
+ UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction());
+ break;
+ }
+
default:
return false;
}
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 7269fff..7731e6e 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -41,10 +41,11 @@
if (!is_leaf) {
MarkNotLeaf();
}
- ComputeFrameSize(GetGraph()->GetMaximumNumberOfOutVRegs()
- + GetGraph()->GetNumberOfLocalVRegs()
- + GetGraph()->GetNumberOfTemporaries()
- + 1 /* filler */);
+ ComputeFrameSize(GetGraph()->GetNumberOfLocalVRegs()
+ + GetGraph()->GetNumberOfTemporaries()
+ + 1 /* filler */,
+ GetGraph()->GetMaximumNumberOfOutVRegs()
+ + 1 /* current method */);
GenerateFrameEntry();
for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
@@ -110,10 +111,10 @@
return -1;
}
-void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots) {
+void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots, size_t number_of_out_slots) {
SetFrameSize(RoundUp(
number_of_spill_slots * kVRegSize
- + kVRegSize // Art method
+ + number_of_out_slots * kVRegSize
+ FrameEntrySpillSize(),
kStackAlignment));
}
@@ -376,4 +377,95 @@
*data = vmap_encoder.GetData();
}
+void CodeGenerator::BuildStackMaps(std::vector<uint8_t>* data) {
+ uint32_t size = stack_map_stream_.ComputeNeededSize();
+ data->resize(size);
+ MemoryRegion region(data->data(), size);
+ stack_map_stream_.FillIn(region);
+}
+
+void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) {
+ // Collect PC infos for the mapping table.
+ struct PcInfo pc_info;
+ pc_info.dex_pc = dex_pc;
+ pc_info.native_pc = GetAssembler()->CodeSize();
+ pc_infos_.Add(pc_info);
+
+ // Populate stack map information.
+
+ if (instruction == nullptr) {
+ // For stack overflow checks.
+ stack_map_stream_.AddStackMapEntry(dex_pc, pc_info.native_pc, 0, 0, 0, 0);
+ return;
+ }
+
+ LocationSummary* locations = instruction->GetLocations();
+ HEnvironment* environment = instruction->GetEnvironment();
+
+ size_t environment_size = instruction->EnvironmentSize();
+
+ size_t register_mask = 0;
+ size_t inlining_depth = 0;
+ stack_map_stream_.AddStackMapEntry(
+ dex_pc, pc_info.native_pc, register_mask,
+ locations->GetStackMask(), environment_size, inlining_depth);
+
+ // Walk over the environment, and record the location of dex registers.
+ for (size_t i = 0; i < environment_size; ++i) {
+ HInstruction* current = environment->GetInstructionAt(i);
+ if (current == nullptr) {
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kNone, 0);
+ continue;
+ }
+
+ Location location = locations->GetEnvironmentAt(i);
+ switch (location.GetKind()) {
+ case Location::kConstant: {
+ DCHECK(current == location.GetConstant());
+ if (current->IsLongConstant()) {
+ int64_t value = current->AsLongConstant()->GetValue();
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, Low32Bits(value));
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value));
+ ++i;
+ DCHECK_LT(i, environment_size);
+ } else {
+ DCHECK(current->IsIntConstant());
+ int32_t value = current->AsIntConstant()->GetValue();
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value);
+ }
+ break;
+ }
+
+ case Location::kStackSlot: {
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack, location.GetStackIndex());
+ break;
+ }
+
+ case Location::kDoubleStackSlot: {
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack, location.GetStackIndex());
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack,
+ location.GetHighStackIndex(kVRegSize));
+ ++i;
+ DCHECK_LT(i, environment_size);
+ break;
+ }
+
+ case Location::kRegister : {
+ int id = location.reg().RegId();
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id);
+ if (current->GetType() == Primitive::kPrimDouble
+ || current->GetType() == Primitive::kPrimLong) {
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id);
+ ++i;
+ DCHECK_LT(i, environment_size);
+ }
+ break;
+ }
+
+ default:
+ LOG(FATAL) << "Unexpected kind " << location.GetKind();
+ }
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 12337c9..a83d703 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -23,6 +23,7 @@
#include "locations.h"
#include "memory_region.h"
#include "nodes.h"
+#include "stack_map_stream.h"
#include "utils/assembler.h"
namespace art {
@@ -97,7 +98,7 @@
virtual HGraphVisitor* GetInstructionVisitor() = 0;
virtual Assembler* GetAssembler() = 0;
virtual size_t GetWordSize() const = 0;
- void ComputeFrameSize(size_t number_of_spill_slots);
+ void ComputeFrameSize(size_t number_of_spill_slots, size_t number_of_out_slots);
virtual size_t FrameEntrySpillSize() const = 0;
int32_t GetStackSlot(HLocal* local) const;
Location GetTemporaryLocation(HTemporary* temp) const;
@@ -114,12 +115,7 @@
virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const = 0;
virtual InstructionSet GetInstructionSet() const = 0;
- void RecordPcInfo(uint32_t dex_pc) {
- struct PcInfo pc_info;
- pc_info.dex_pc = dex_pc;
- pc_info.native_pc = GetAssembler()->CodeSize();
- pc_infos_.Add(pc_info);
- }
+ void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc);
void AddSlowPath(SlowPathCode* slow_path) {
slow_paths_.Add(slow_path);
@@ -131,6 +127,7 @@
void BuildVMapTable(std::vector<uint8_t>* vector) const;
void BuildNativeGCMap(
std::vector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const;
+ void BuildStackMaps(std::vector<uint8_t>* vector);
bool IsLeafMethod() const {
return is_leaf_;
@@ -149,7 +146,8 @@
pc_infos_(graph->GetArena(), 32),
slow_paths_(graph->GetArena(), 8),
blocked_registers_(graph->GetArena()->AllocArray<bool>(number_of_registers)),
- is_leaf_(true) {}
+ is_leaf_(true),
+ stack_map_stream_(graph->GetArena()) {}
~CodeGenerator() {}
// Register allocation logic.
@@ -184,6 +182,8 @@
bool is_leaf_;
+ StackMapStream stack_map_stream_;
+
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
};
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 2c954a0..e72e39b 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -61,18 +61,18 @@
class NullCheckSlowPathARM : public SlowPathCode {
public:
- explicit NullCheckSlowPathARM(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+ explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowNullPointer).Int32Value();
__ ldr(LR, Address(TR, offset));
__ blx(LR);
- codegen->RecordPcInfo(dex_pc_);
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
}
private:
- const uint32_t dex_pc_;
+ HNullCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
};
@@ -92,10 +92,12 @@
class BoundsCheckSlowPathARM : public SlowPathCode {
public:
- explicit BoundsCheckSlowPathARM(uint32_t dex_pc,
+ explicit BoundsCheckSlowPathARM(HBoundsCheck* instruction,
Location index_location,
Location length_location)
- : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+ : instruction_(instruction),
+ index_location_(index_location),
+ length_location_(length_location) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM* arm_codegen = reinterpret_cast<CodeGeneratorARM*>(codegen);
@@ -106,11 +108,11 @@
int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowArrayBounds).Int32Value();
__ ldr(LR, Address(TR, offset));
__ blx(LR);
- codegen->RecordPcInfo(dex_pc_);
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
}
private:
- const uint32_t dex_pc_;
+ HBoundsCheck* const instruction_;
const Location index_location_;
const Location length_location_;
@@ -277,7 +279,7 @@
} else {
__ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
__ ldr(IP, Address(IP, 0));
- RecordPcInfo(0);
+ RecordPcInfo(nullptr, 0);
}
}
@@ -545,14 +547,14 @@
}
void LocationsBuilderARM::VisitIf(HIf* if_instr) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall);
HInstruction* cond = if_instr->InputAt(0);
DCHECK(cond->IsCondition());
HCondition* condition = cond->AsCondition();
if (condition->NeedsMaterialization()) {
locations->SetInAt(0, Location::Any());
}
- if_instr->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
@@ -595,13 +597,13 @@
void LocationsBuilderARM::VisitCondition(HCondition* comp) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1)));
if (comp->NeedsMaterialization()) {
locations->SetOut(Location::RequiresRegister());
}
- comp->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) {
@@ -695,7 +697,8 @@
}
void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
switch (store->InputAt(1)->GetType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -713,25 +716,24 @@
default:
LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
}
- store->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) {
}
void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
locations->SetOut(Location::ConstantLocation(constant));
- constant->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) {
}
void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
locations->SetOut(Location::ConstantLocation(constant));
- constant->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant) {
@@ -747,7 +749,8 @@
}
void LocationsBuilderARM::VisitReturn(HReturn* ret) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
switch (ret->InputAt(0)->GetType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -766,8 +769,6 @@
default:
LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
}
-
- ret->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) {
@@ -794,8 +795,8 @@
}
void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
- codegen_->MarkNotLeaf();
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
locations->AddTemp(ArmCoreLocation(R0));
InvokeDexCallingConventionVisitor calling_convention_visitor;
@@ -826,8 +827,6 @@
LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
break;
}
-
- invoke->SetLocations(locations);
}
void InstructionCodeGeneratorARM::LoadCurrentMethod(Register reg) {
@@ -859,12 +858,13 @@
// LR()
__ blx(LR);
- codegen_->RecordPcInfo(invoke->GetDexPc());
+ codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
DCHECK(!codegen_->IsLeafMethod());
}
void LocationsBuilderARM::VisitAdd(HAdd* add) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
switch (add->GetResultType()) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
@@ -884,7 +884,6 @@
default:
LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
}
- add->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) {
@@ -924,7 +923,8 @@
}
void LocationsBuilderARM::VisitSub(HSub* sub) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
switch (sub->GetResultType()) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
@@ -944,7 +944,6 @@
default:
LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
}
- sub->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitSub(HSub* sub) {
@@ -985,13 +984,12 @@
}
void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
- codegen_->MarkNotLeaf();
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(0)));
locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(1)));
locations->SetOut(ArmCoreLocation(R0));
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
@@ -1003,12 +1001,13 @@
__ ldr(LR, Address(TR, offset));
__ blx(LR);
- codegen_->RecordPcInfo(instruction->GetDexPc());
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
DCHECK(!codegen_->IsLeafMethod());
}
void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
if (location.IsStackSlot()) {
location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
@@ -1016,7 +1015,6 @@
location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
}
locations->SetOut(location);
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instruction) {
@@ -1024,10 +1022,10 @@
}
void LocationsBuilderARM::VisitNot(HNot* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitNot(HNot* instruction) {
@@ -1037,11 +1035,11 @@
}
void LocationsBuilderARM::VisitCompare(HCompare* compare) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- compare->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) {
@@ -1081,12 +1079,12 @@
}
void LocationsBuilderARM::VisitPhi(HPhi* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
locations->SetInAt(i, Location::Any());
}
locations->SetOut(Location::Any());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) {
@@ -1094,22 +1092,22 @@
}
void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// Temporary registers for the write barrier.
- if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+ if (instruction->GetFieldType() == Primitive::kPrimNot) {
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
}
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
LocationSummary* locations = instruction->GetLocations();
Register obj = locations->InAt(0).AsArm().AsCoreRegister();
uint32_t offset = instruction->GetFieldOffset().Uint32Value();
- Primitive::Type field_type = instruction->InputAt(1)->GetType();
+ Primitive::Type field_type = instruction->GetFieldType();
switch (field_type) {
case Primitive::kPrimBoolean:
@@ -1154,10 +1152,10 @@
}
void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -1214,16 +1212,15 @@
}
void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
// TODO: Have a normalization phase that makes this instruction never used.
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
- SlowPathCode* slow_path =
- new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction->GetDexPc());
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction);
codegen_->AddSlowPath(slow_path);
LocationSummary* locations = instruction->GetLocations();
@@ -1237,11 +1234,11 @@
}
void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
@@ -1340,27 +1337,27 @@
}
void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
- Primitive::Type value_type = instruction->InputAt(2)->GetType();
- if (value_type == Primitive::kPrimNot) {
+ Primitive::Type value_type = instruction->GetComponentType();
+ bool is_object = value_type == Primitive::kPrimNot;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+ instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall);
+ if (is_object) {
InvokeRuntimeCallingConvention calling_convention;
locations->SetInAt(0, ArmCoreLocation(calling_convention.GetRegisterAt(0)));
locations->SetInAt(1, ArmCoreLocation(calling_convention.GetRegisterAt(1)));
locations->SetInAt(2, ArmCoreLocation(calling_convention.GetRegisterAt(2)));
- codegen_->MarkNotLeaf();
} else {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
locations->SetInAt(2, Location::RequiresRegister());
}
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
LocationSummary* locations = instruction->GetLocations();
Register obj = locations->InAt(0).AsArm().AsCoreRegister();
Location index = locations->InAt(1);
- Primitive::Type value_type = instruction->InputAt(2)->GetType();
+ Primitive::Type value_type = instruction->GetComponentType();
switch (value_type) {
case Primitive::kPrimBoolean:
@@ -1408,7 +1405,7 @@
int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAputObject).Int32Value();
__ ldr(LR, Address(TR, offset));
__ blx(LR);
- codegen_->RecordPcInfo(instruction->GetDexPc());
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
DCHECK(!codegen_->IsLeafMethod());
break;
}
@@ -1436,10 +1433,10 @@
}
void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) {
@@ -1451,18 +1448,18 @@
}
void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// TODO: Have a normalization phase that makes this instruction never used.
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) {
LocationSummary* locations = instruction->GetLocations();
SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(
- instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+ instruction, locations->InAt(0), locations->InAt(1));
codegen_->AddSlowPath(slow_path);
Register index = locations->InAt(0).AsArm().AsCoreRegister();
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 35b8116..6602d3f 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -61,16 +61,16 @@
class NullCheckSlowPathX86 : public SlowPathCode {
public:
- explicit NullCheckSlowPathX86(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+ explicit NullCheckSlowPathX86(HNullCheck* instruction) : instruction_(instruction) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
__ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowNullPointer)));
- codegen->RecordPcInfo(dex_pc_);
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
}
private:
- const uint32_t dex_pc_;
+ HNullCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86);
};
@@ -91,10 +91,10 @@
class BoundsCheckSlowPathX86 : public SlowPathCode {
public:
- explicit BoundsCheckSlowPathX86(uint32_t dex_pc,
+ explicit BoundsCheckSlowPathX86(HBoundsCheck* instruction,
Location index_location,
Location length_location)
- : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+ : instruction_(instruction), index_location_(index_location), length_location_(length_location) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorX86* x86_codegen = reinterpret_cast<CodeGeneratorX86*>(codegen);
@@ -103,11 +103,11 @@
x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(0)), index_location_);
x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(1)), length_location_);
__ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowArrayBounds)));
- codegen->RecordPcInfo(dex_pc_);
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
}
private:
- const uint32_t dex_pc_;
+ HBoundsCheck* const instruction_;
const Location index_location_;
const Location length_location_;
@@ -244,7 +244,7 @@
bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86);
if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
__ testl(EAX, Address(ESP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86))));
- RecordPcInfo(0);
+ RecordPcInfo(nullptr, 0);
}
// The return PC has already been pushed on the stack.
@@ -398,6 +398,7 @@
__ popl(Address(ESP, calling_convention.GetStackOffsetOf(argument_index + 1)));
}
} else {
+ DCHECK(destination.IsDoubleStackSlot());
if (source.IsRegister()) {
__ movl(Address(ESP, destination.GetStackIndex()), source.AsX86().AsRegisterPairLow());
__ movl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)),
@@ -503,14 +504,14 @@
}
void LocationsBuilderX86::VisitIf(HIf* if_instr) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall);
HInstruction* cond = if_instr->InputAt(0);
DCHECK(cond->IsCondition());
HCondition* condition = cond->AsCondition();
if (condition->NeedsMaterialization()) {
locations->SetInAt(0, Location::Any());
}
- if_instr->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) {
@@ -564,7 +565,8 @@
}
void LocationsBuilderX86::VisitStoreLocal(HStoreLocal* store) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
switch (store->InputAt(1)->GetType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -589,13 +591,13 @@
}
void LocationsBuilderX86::VisitCondition(HCondition* comp) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
if (comp->NeedsMaterialization()) {
locations->SetOut(Location::RequiresRegister());
}
- comp->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitCondition(HCondition* comp) {
@@ -665,18 +667,18 @@
}
void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
locations->SetOut(Location::ConstantLocation(constant));
- constant->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant) {
}
void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
locations->SetOut(Location::ConstantLocation(constant));
- constant->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitLongConstant(HLongConstant* constant) {
@@ -693,7 +695,8 @@
}
void LocationsBuilderX86::VisitReturn(HReturn* ret) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
switch (ret->InputAt(0)->GetType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -712,7 +715,6 @@
default:
LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
}
- ret->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) {
@@ -740,8 +742,8 @@
}
void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
- codegen_->MarkNotLeaf();
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
locations->AddTemp(X86CpuLocation(EAX));
InvokeDexCallingConventionVisitor calling_convention_visitor;
@@ -799,11 +801,12 @@
__ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
DCHECK(!codegen_->IsLeafMethod());
- codegen_->RecordPcInfo(invoke->GetDexPc());
+ codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
void LocationsBuilderX86::VisitAdd(HAdd* add) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
switch (add->GetResultType()) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
@@ -823,7 +826,6 @@
default:
LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
}
- add->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) {
@@ -876,7 +878,8 @@
}
void LocationsBuilderX86::VisitSub(HSub* sub) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
switch (sub->GetResultType()) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
@@ -896,7 +899,6 @@
default:
LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
}
- sub->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitSub(HSub* sub) {
@@ -949,13 +951,12 @@
}
void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) {
- codegen_->MarkNotLeaf();
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
locations->SetOut(X86CpuLocation(EAX));
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(0)));
locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(1)));
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) {
@@ -966,12 +967,13 @@
__ fs()->call(
Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck)));
- codegen_->RecordPcInfo(instruction->GetDexPc());
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
DCHECK(!codegen_->IsLeafMethod());
}
void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
if (location.IsStackSlot()) {
location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
@@ -979,17 +981,16 @@
location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
}
locations->SetOut(location);
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitParameterValue(HParameterValue* instruction) {
}
void LocationsBuilderX86::VisitNot(HNot* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitNot(HNot* instruction) {
@@ -1000,11 +1001,11 @@
}
void LocationsBuilderX86::VisitCompare(HCompare* compare) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
locations->SetOut(Location::RequiresRegister());
- compare->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) {
@@ -1050,12 +1051,12 @@
}
void LocationsBuilderX86::VisitPhi(HPhi* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
locations->SetInAt(i, Location::Any());
}
locations->SetOut(Location::Any());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitPhi(HPhi* instruction) {
@@ -1063,9 +1064,10 @@
}
void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
- Primitive::Type field_type = instruction->InputAt(1)->GetType();
+ Primitive::Type field_type = instruction->GetFieldType();
if (field_type == Primitive::kPrimBoolean || field_type == Primitive::kPrimByte) {
// Ensure the value is in a byte register.
locations->SetInAt(1, X86CpuLocation(EAX));
@@ -1078,14 +1080,13 @@
// Ensure the card is in a byte register.
locations->AddTemp(X86CpuLocation(ECX));
}
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
LocationSummary* locations = instruction->GetLocations();
Register obj = locations->InAt(0).AsX86().AsCpuRegister();
uint32_t offset = instruction->GetFieldOffset().Uint32Value();
- Primitive::Type field_type = instruction->InputAt(1)->GetType();
+ Primitive::Type field_type = instruction->GetFieldType();
switch (field_type) {
case Primitive::kPrimBoolean:
@@ -1144,10 +1145,10 @@
}
void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -1205,16 +1206,15 @@
}
void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::Any());
// TODO: Have a normalization phase that makes this instruction never used.
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
- SlowPathCode* slow_path =
- new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction->GetDexPc());
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction);
codegen_->AddSlowPath(slow_path);
LocationSummary* locations = instruction->GetLocations();
@@ -1231,11 +1231,11 @@
}
void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
@@ -1331,14 +1331,16 @@
}
void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
- Primitive::Type value_type = instruction->InputAt(2)->GetType();
+ Primitive::Type value_type = instruction->GetComponentType();
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+ instruction,
+ value_type == Primitive::kPrimNot ? LocationSummary::kCall : LocationSummary::kNoCall);
+
if (value_type == Primitive::kPrimNot) {
InvokeRuntimeCallingConvention calling_convention;
locations->SetInAt(0, X86CpuLocation(calling_convention.GetRegisterAt(0)));
locations->SetInAt(1, X86CpuLocation(calling_convention.GetRegisterAt(1)));
locations->SetInAt(2, X86CpuLocation(calling_convention.GetRegisterAt(2)));
- codegen_->MarkNotLeaf();
} else {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
@@ -1349,15 +1351,13 @@
locations->SetInAt(2, Location::RequiresRegister());
}
}
-
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) {
LocationSummary* locations = instruction->GetLocations();
Register obj = locations->InAt(0).AsX86().AsCpuRegister();
Location index = locations->InAt(1);
- Primitive::Type value_type = instruction->InputAt(2)->GetType();
+ Primitive::Type value_type = instruction->GetComponentType();
switch (value_type) {
case Primitive::kPrimBoolean:
@@ -1401,7 +1401,7 @@
case Primitive::kPrimNot: {
DCHECK(!codegen_->IsLeafMethod());
__ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAputObject)));
- codegen_->RecordPcInfo(instruction->GetDexPc());
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
break;
}
@@ -1446,18 +1446,18 @@
}
void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// TODO: Have a normalization phase that makes this instruction never used.
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) {
LocationSummary* locations = instruction->GetLocations();
SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86(
- instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+ instruction, locations->InAt(0), locations->InAt(1));
codegen_->AddSlowPath(slow_path);
Register index = locations->InAt(0).AsX86().AsCpuRegister();
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index c4571ca..b2d81e3 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -65,17 +65,17 @@
class NullCheckSlowPathX86_64 : public SlowPathCode {
public:
- explicit NullCheckSlowPathX86_64(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+ explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : instruction_(instruction) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
__ gs()->call(
Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowNullPointer), true));
- codegen->RecordPcInfo(dex_pc_);
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
}
private:
- const uint32_t dex_pc_;
+ HNullCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64);
};
@@ -97,10 +97,12 @@
class BoundsCheckSlowPathX86_64 : public SlowPathCode {
public:
- explicit BoundsCheckSlowPathX86_64(uint32_t dex_pc,
+ explicit BoundsCheckSlowPathX86_64(HBoundsCheck* instruction,
Location index_location,
Location length_location)
- : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+ : instruction_(instruction),
+ index_location_(index_location),
+ length_location_(length_location) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorX86_64* x64_codegen = reinterpret_cast<CodeGeneratorX86_64*>(codegen);
@@ -110,11 +112,11 @@
x64_codegen->Move(X86_64CpuLocation(calling_convention.GetRegisterAt(1)), length_location_);
__ gs()->call(Address::Absolute(
QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowArrayBounds), true));
- codegen->RecordPcInfo(dex_pc_);
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
}
private:
- const uint32_t dex_pc_;
+ HBoundsCheck* const instruction_;
const Location index_location_;
const Location length_location_;
@@ -214,7 +216,7 @@
if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
__ testq(CpuRegister(RAX), Address(
CpuRegister(RSP), -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86_64))));
- RecordPcInfo(0);
+ RecordPcInfo(nullptr, 0);
}
// The return PC has already been pushed on the stack.
@@ -385,14 +387,14 @@
}
void LocationsBuilderX86_64::VisitIf(HIf* if_instr) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall);
HInstruction* cond = if_instr->InputAt(0);
DCHECK(cond->IsCondition());
HCondition* condition = cond->AsCondition();
if (condition->NeedsMaterialization()) {
locations->SetInAt(0, Location::Any());
}
- if_instr->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitIf(HIf* if_instr) {
@@ -444,7 +446,8 @@
}
void LocationsBuilderX86_64::VisitStoreLocal(HStoreLocal* store) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
switch (store->InputAt(1)->GetType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -462,20 +465,19 @@
default:
LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
}
- store->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store) {
}
void LocationsBuilderX86_64::VisitCondition(HCondition* comp) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
if (comp->NeedsMaterialization()) {
locations->SetOut(Location::RequiresRegister());
}
- comp->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* comp) {
@@ -545,11 +547,11 @@
}
void LocationsBuilderX86_64::VisitCompare(HCompare* compare) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- compare->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) {
@@ -578,18 +580,18 @@
}
void LocationsBuilderX86_64::VisitIntConstant(HIntConstant* constant) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
locations->SetOut(Location::ConstantLocation(constant));
- constant->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitIntConstant(HIntConstant* constant) {
}
void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
locations->SetOut(Location::ConstantLocation(constant));
- constant->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitLongConstant(HLongConstant* constant) {
@@ -605,7 +607,8 @@
}
void LocationsBuilderX86_64::VisitReturn(HReturn* ret) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
switch (ret->InputAt(0)->GetType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -620,7 +623,6 @@
default:
LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
}
- ret->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitReturn(HReturn* ret) {
@@ -686,8 +688,8 @@
}
void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
- codegen_->MarkNotLeaf();
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
locations->AddTemp(X86_64CpuLocation(RDI));
InvokeDexCallingConventionVisitor calling_convention_visitor;
@@ -715,8 +717,6 @@
LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
break;
}
-
- invoke->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
@@ -742,11 +742,12 @@
__ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
DCHECK(!codegen_->IsLeafMethod());
- codegen_->RecordPcInfo(invoke->GetDexPc());
+ codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
void LocationsBuilderX86_64::VisitAdd(HAdd* add) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
switch (add->GetResultType()) {
case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RequiresRegister());
@@ -771,7 +772,6 @@
default:
LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
}
- add->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) {
@@ -812,7 +812,8 @@
}
void LocationsBuilderX86_64::VisitSub(HSub* sub) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
switch (sub->GetResultType()) {
case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RequiresRegister());
@@ -837,7 +838,6 @@
default:
LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
}
- sub->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) {
@@ -878,10 +878,9 @@
}
void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) {
- codegen_->MarkNotLeaf();
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
locations->SetOut(X86_64CpuLocation(RAX));
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) {
@@ -893,11 +892,12 @@
QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocObjectWithAccessCheck), true));
DCHECK(!codegen_->IsLeafMethod());
- codegen_->RecordPcInfo(instruction->GetDexPc());
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
}
void LocationsBuilderX86_64::VisitParameterValue(HParameterValue* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
if (location.IsStackSlot()) {
location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
@@ -905,7 +905,6 @@
location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
}
locations->SetOut(location);
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitParameterValue(HParameterValue* instruction) {
@@ -913,10 +912,10 @@
}
void LocationsBuilderX86_64::VisitNot(HNot* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitNot(HNot* instruction) {
@@ -927,12 +926,12 @@
}
void LocationsBuilderX86_64::VisitPhi(HPhi* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
locations->SetInAt(i, Location::Any());
}
locations->SetOut(Location::Any());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitPhi(HPhi* instruction) {
@@ -940,15 +939,15 @@
}
void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// Temporary registers for the write barrier.
- if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+ if (instruction->GetFieldType() == Primitive::kPrimNot) {
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
}
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
@@ -956,7 +955,7 @@
CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
CpuRegister value = locations->InAt(1).AsX86_64().AsCpuRegister();
size_t offset = instruction->GetFieldOffset().SizeValue();
- Primitive::Type field_type = instruction->InputAt(1)->GetType();
+ Primitive::Type field_type = instruction->GetFieldType();
switch (field_type) {
case Primitive::kPrimBoolean:
@@ -997,10 +996,10 @@
}
void LocationsBuilderX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -1051,16 +1050,15 @@
}
void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::Any());
// TODO: Have a normalization phase that makes this instruction never used.
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
- SlowPathCode* slow_path =
- new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction->GetDexPc());
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction);
codegen_->AddSlowPath(slow_path);
LocationSummary* locations = instruction->GetLocations();
@@ -1077,11 +1075,11 @@
}
void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
@@ -1174,27 +1172,27 @@
}
void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
- Primitive::Type value_type = instruction->InputAt(2)->GetType();
- if (value_type == Primitive::kPrimNot) {
+ Primitive::Type value_type = instruction->GetComponentType();
+ bool is_object = value_type == Primitive::kPrimNot;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+ instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall);
+ if (is_object) {
InvokeRuntimeCallingConvention calling_convention;
locations->SetInAt(0, X86_64CpuLocation(calling_convention.GetRegisterAt(0)));
locations->SetInAt(1, X86_64CpuLocation(calling_convention.GetRegisterAt(1)));
locations->SetInAt(2, X86_64CpuLocation(calling_convention.GetRegisterAt(2)));
- codegen_->MarkNotLeaf();
} else {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
locations->SetInAt(2, Location::RequiresRegister());
}
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
LocationSummary* locations = instruction->GetLocations();
CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
Location index = locations->InAt(1);
- Primitive::Type value_type = instruction->InputAt(2)->GetType();
+ Primitive::Type value_type = instruction->GetComponentType();
switch (value_type) {
case Primitive::kPrimBoolean:
@@ -1238,7 +1236,7 @@
case Primitive::kPrimNot: {
__ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAputObject), true));
DCHECK(!codegen_->IsLeafMethod());
- codegen_->RecordPcInfo(instruction->GetDexPc());
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
break;
}
@@ -1264,10 +1262,10 @@
}
void LocationsBuilderX86_64::VisitArrayLength(HArrayLength* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) {
@@ -1279,18 +1277,18 @@
}
void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// TODO: Have a normalization phase that makes this instruction never used.
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
LocationSummary* locations = instruction->GetLocations();
SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64(
- instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+ instruction, locations->InAt(0), locations->InAt(1));
codegen_->AddSlowPath(slow_path);
CpuRegister index = locations->InAt(0).AsX86_64().AsCpuRegister();
diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc
index 468cfb7..fce97bd 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -20,13 +20,29 @@
namespace art {
-LocationSummary::LocationSummary(HInstruction* instruction)
+LocationSummary::LocationSummary(HInstruction* instruction, CallKind call_kind)
: inputs_(instruction->GetBlock()->GetGraph()->GetArena(), instruction->InputCount()),
- temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0) {
+ temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0),
+ environment_(instruction->GetBlock()->GetGraph()->GetArena(),
+ instruction->EnvironmentSize()),
+ call_kind_(call_kind),
+ stack_mask_(nullptr),
+ register_mask_(0),
+ live_registers_(0) {
inputs_.SetSize(instruction->InputCount());
- for (size_t i = 0; i < instruction->InputCount(); i++) {
+ for (size_t i = 0; i < instruction->InputCount(); ++i) {
inputs_.Put(i, Location());
}
+ environment_.SetSize(instruction->EnvironmentSize());
+ for (size_t i = 0; i < instruction->EnvironmentSize(); ++i) {
+ environment_.Put(i, Location());
+ }
+ instruction->SetLocations(this);
+
+ if (NeedsSafepoint()) {
+ ArenaAllocator* arena = instruction->GetBlock()->GetGraph()->GetArena();
+ stack_mask_ = new (arena) ArenaBitVector(arena, 0, true);
+ }
}
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index aaddb09..041e85b 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_OPTIMIZING_LOCATIONS_H_
#include "base/bit_field.h"
+#include "base/bit_vector.h"
#include "utils/allocation.h"
#include "utils/growable_array.h"
#include "utils/managed_register.h"
@@ -43,13 +44,13 @@
// low bits are in the last parameter register, and the high
// bits are in a stack slot. The kQuickParameter kind is for
// handling this special case.
- kQuickParameter = 5,
+ kQuickParameter = 6,
// Unallocated location represents a location that is not fixed and can be
// allocated by a register allocator. Each unallocated location has
// a policy that specifies what kind of location is suitable. Payload
// contains register allocation policy.
- kUnallocated = 6,
+ kUnallocated = 7,
};
Location() : value_(kInvalid) {
@@ -59,8 +60,8 @@
COMPILE_ASSERT((kStackSlot & kLocationTagMask) != kConstant, TagError);
COMPILE_ASSERT((kDoubleStackSlot & kLocationTagMask) != kConstant, TagError);
COMPILE_ASSERT((kRegister & kLocationTagMask) != kConstant, TagError);
+ COMPILE_ASSERT((kQuickParameter & kLocationTagMask) != kConstant, TagError);
COMPILE_ASSERT((kConstant & kLocationTagMask) == kConstant, TagError);
- COMPILE_ASSERT((kQuickParameter & kLocationTagMask) == kConstant, TagError);
DCHECK(!IsValid());
}
@@ -173,7 +174,7 @@
x86_64::X86_64ManagedRegister AsX86_64() const;
Kind GetKind() const {
- return KindField::Decode(value_);
+ return IsConstant() ? kConstant : KindField::Decode(value_);
}
bool Equals(Location other) const {
@@ -275,7 +276,13 @@
*/
class LocationSummary : public ArenaObject {
public:
- explicit LocationSummary(HInstruction* instruction);
+ enum CallKind {
+ kNoCall,
+ kCallOnSlowPath,
+ kCall
+ };
+
+ explicit LocationSummary(HInstruction* instruction, CallKind call_kind = kNoCall);
void SetInAt(uint32_t at, Location location) {
inputs_.Put(at, location);
@@ -309,12 +316,50 @@
return temps_.Size();
}
+ void SetEnvironmentAt(uint32_t at, Location location) {
+ environment_.Put(at, location);
+ }
+
+ Location GetEnvironmentAt(uint32_t at) const {
+ return environment_.Get(at);
+ }
+
Location Out() const { return output_; }
+ bool CanCall() const { return call_kind_ != kNoCall; }
+ bool NeedsSafepoint() const { return CanCall(); }
+
+ void SetStackBit(uint32_t index) {
+ stack_mask_->SetBit(index);
+ }
+
+ void SetRegisterBit(uint32_t reg_id) {
+ register_mask_ |= (1 << reg_id);
+ }
+
+ void SetLiveRegister(uint32_t reg_id) {
+ live_registers_ |= (1 << reg_id);
+ }
+
+ BitVector* GetStackMask() const {
+ return stack_mask_;
+ }
+
private:
GrowableArray<Location> inputs_;
GrowableArray<Location> temps_;
+ GrowableArray<Location> environment_;
Location output_;
+ const CallKind call_kind_;
+
+ // Mask of objects that live in the stack.
+ BitVector* stack_mask_;
+
+ // Mask of objects that live in register.
+ uint32_t register_mask_;
+
+ // Registers that are in use at this position.
+ uint32_t live_registers_;
DISALLOW_COPY_AND_ASSIGN(LocationSummary);
};
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 490d345..207c605 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -414,6 +414,10 @@
env_uses_ = nullptr;
}
+size_t HInstruction::EnvironmentSize() const {
+ return HasEnvironment() ? environment_->Size() : 0;
+}
+
void HPhi::AddInput(HInstruction* input) {
DCHECK(input->GetBlock() != nullptr);
inputs_.Add(input);
@@ -464,4 +468,16 @@
return false;
}
+bool HInstruction::Equals(HInstruction* other) const {
+ if (!InstructionTypeEquals(other)) return false;
+ if (!InstructionDataEquals(other)) return false;
+ if (GetType() != other->GetType()) return false;
+ if (InputCount() != other->InputCount()) return false;
+
+ for (size_t i = 0, e = InputCount(); i < e; ++i) {
+ if (InputAt(i) != other->InputAt(i)) return false;
+ }
+ return true;
+}
+
} // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index bb699e4..9018fee 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -455,6 +455,9 @@
#define DECLARE_INSTRUCTION(type) \
virtual const char* DebugName() const { return #type; } \
virtual H##type* As##type() { return this; } \
+ virtual bool InstructionTypeEquals(HInstruction* other) const { \
+ return other->Is##type(); \
+ } \
virtual void Accept(HGraphVisitor* visitor) \
template <typename T>
@@ -477,9 +480,42 @@
DISALLOW_COPY_AND_ASSIGN(HUseListNode);
};
+// Represents the side effects an instruction may have.
+class SideEffects : public ValueObject {
+ public:
+ static SideEffects None() {
+ return SideEffects(0);
+ }
+
+ static SideEffects All() {
+ return SideEffects(ChangesSomething().flags_ | DependsOnSomething().flags_);
+ }
+
+ static SideEffects ChangesSomething() {
+ return SideEffects((1 << kFlagChangesCount) - 1);
+ }
+
+ static SideEffects DependsOnSomething() {
+ int count = kFlagDependsOnCount - kFlagChangesCount;
+ return SideEffects(((1 << count) - 1) << kFlagChangesCount);
+ }
+
+ private:
+ static constexpr int kFlagChangesSomething = 0;
+ static constexpr int kFlagChangesCount = kFlagChangesSomething + 1;
+
+ static constexpr int kFlagDependsOnSomething = kFlagChangesCount;
+ static constexpr int kFlagDependsOnCount = kFlagDependsOnSomething + 1;
+
+ private:
+ explicit SideEffects(size_t flags) : flags_(flags) {}
+
+ const size_t flags_;
+};
+
class HInstruction : public ArenaObject {
public:
- HInstruction()
+ explicit HInstruction(SideEffects side_effects)
: previous_(nullptr),
next_(nullptr),
block_(nullptr),
@@ -490,7 +526,8 @@
environment_(nullptr),
locations_(nullptr),
live_interval_(nullptr),
- lifetime_position_(kNoLifetime) {}
+ lifetime_position_(kNoLifetime),
+ side_effects_(side_effects) {}
virtual ~HInstruction() {}
@@ -554,6 +591,10 @@
HEnvironment* GetEnvironment() const { return environment_; }
void SetEnvironment(HEnvironment* environment) { environment_ = environment; }
+ // Returns the number of entries in the environment. Typically, that is the
+ // number of dex registers in a method. It could be more in case of inlining.
+ size_t EnvironmentSize() const;
+
LocationSummary* GetLocations() const { return locations_; }
void SetLocations(LocationSummary* locations) { locations_ = locations; }
@@ -570,6 +611,22 @@
FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
#undef INSTRUCTION_TYPE_CHECK
+ // Returns whether the instruction can be moved within the graph.
+ virtual bool CanBeMoved() const { return false; }
+
+ // Returns whether the two instructions are of the same kind.
+ virtual bool InstructionTypeEquals(HInstruction* other) const { return false; }
+
+ // Returns whether any data encoded in the two instructions is equal.
+ // This method does not look at the inputs. Both instructions must be
+ // of the same type, otherwise the method has undefined behavior.
+ virtual bool InstructionDataEquals(HInstruction* other) const { return false; }
+
+ // Returns whether two instructions are equal, that is:
+ // 1) They have the same type and contain the same data,
+ // 2) Their inputs are identical.
+ bool Equals(HInstruction* other) const;
+
size_t GetLifetimePosition() const { return lifetime_position_; }
void SetLifetimePosition(size_t position) { lifetime_position_ = position; }
LiveInterval* GetLiveInterval() const { return live_interval_; }
@@ -583,7 +640,7 @@
// An instruction gets an id when it is added to the graph.
// It reflects creation order. A negative id means the instruction
- // has not beed added to the graph.
+ // has not been added to the graph.
int id_;
// When doing liveness analysis, instructions that have uses get an SSA index.
@@ -595,6 +652,8 @@
// List of environments that contain this instruction.
HUseListNode<HEnvironment>* env_uses_;
+ // The environment associated with this instruction. Not null if the instruction
+ // might jump out of the method.
HEnvironment* environment_;
// Set by the code generator.
@@ -607,6 +666,8 @@
// order of blocks where this instruction's live interval start.
size_t lifetime_position_;
+ const SideEffects side_effects_;
+
friend class HBasicBlock;
friend class HInstructionList;
@@ -660,10 +721,16 @@
vregs_.Put(index, instruction);
}
+ HInstruction* GetInstructionAt(size_t index) const {
+ return vregs_.Get(index);
+ }
+
GrowableArray<HInstruction*>* GetVRegs() {
return &vregs_;
}
+ size_t Size() const { return vregs_.Size(); }
+
private:
GrowableArray<HInstruction*> vregs_;
@@ -777,7 +844,8 @@
template<intptr_t N>
class HTemplateInstruction: public HInstruction {
public:
- HTemplateInstruction<N>() : inputs_() {}
+ HTemplateInstruction<N>(SideEffects side_effects)
+ : HInstruction(side_effects), inputs_() {}
virtual ~HTemplateInstruction() {}
virtual size_t InputCount() const { return N; }
@@ -795,9 +863,10 @@
};
template<intptr_t N>
-class HExpression: public HTemplateInstruction<N> {
+class HExpression : public HTemplateInstruction<N> {
public:
- explicit HExpression<N>(Primitive::Type type) : type_(type) {}
+ HExpression<N>(Primitive::Type type, SideEffects side_effects)
+ : HTemplateInstruction<N>(side_effects), type_(type) {}
virtual ~HExpression() {}
virtual Primitive::Type GetType() const { return type_; }
@@ -810,7 +879,7 @@
// instruction that branches to the exit block.
class HReturnVoid : public HTemplateInstruction<0> {
public:
- HReturnVoid() {}
+ HReturnVoid() : HTemplateInstruction(SideEffects::None()) {}
virtual bool IsControlFlow() const { return true; }
@@ -824,7 +893,7 @@
// instruction that branches to the exit block.
class HReturn : public HTemplateInstruction<1> {
public:
- explicit HReturn(HInstruction* value) {
+ explicit HReturn(HInstruction* value) : HTemplateInstruction(SideEffects::None()) {
SetRawInputAt(0, value);
}
@@ -841,7 +910,7 @@
// exit block.
class HExit : public HTemplateInstruction<0> {
public:
- HExit() {}
+ HExit() : HTemplateInstruction(SideEffects::None()) {}
virtual bool IsControlFlow() const { return true; }
@@ -854,14 +923,14 @@
// Jumps from one block to another.
class HGoto : public HTemplateInstruction<0> {
public:
- HGoto() {}
+ HGoto() : HTemplateInstruction(SideEffects::None()) {}
+
+ virtual bool IsControlFlow() const { return true; }
HBasicBlock* GetSuccessor() const {
return GetBlock()->GetSuccessors().Get(0);
}
- virtual bool IsControlFlow() const { return true; }
-
DECLARE_INSTRUCTION(Goto);
private:
@@ -873,10 +942,12 @@
// two successors.
class HIf : public HTemplateInstruction<1> {
public:
- explicit HIf(HInstruction* input) {
+ explicit HIf(HInstruction* input) : HTemplateInstruction(SideEffects::None()) {
SetRawInputAt(0, input);
}
+ virtual bool IsControlFlow() const { return true; }
+
HBasicBlock* IfTrueSuccessor() const {
return GetBlock()->GetSuccessors().Get(0);
}
@@ -885,8 +956,6 @@
return GetBlock()->GetSuccessors().Get(1);
}
- virtual bool IsControlFlow() const { return true; }
-
DECLARE_INSTRUCTION(If);
virtual bool IsIfInstruction() const { return true; }
@@ -899,7 +968,7 @@
public:
HBinaryOperation(Primitive::Type result_type,
HInstruction* left,
- HInstruction* right) : HExpression(result_type) {
+ HInstruction* right) : HExpression(result_type, SideEffects::None()) {
SetRawInputAt(0, left);
SetRawInputAt(1, right);
}
@@ -910,6 +979,9 @@
virtual bool IsCommutative() { return false; }
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
private:
DISALLOW_COPY_AND_ASSIGN(HBinaryOperation);
};
@@ -1041,7 +1113,8 @@
// A local in the graph. Corresponds to a Dex register.
class HLocal : public HTemplateInstruction<0> {
public:
- explicit HLocal(uint16_t reg_number) : reg_number_(reg_number) {}
+ explicit HLocal(uint16_t reg_number)
+ : HTemplateInstruction(SideEffects::None()), reg_number_(reg_number) {}
DECLARE_INSTRUCTION(Local);
@@ -1057,7 +1130,8 @@
// Load a given local. The local is an input of this instruction.
class HLoadLocal : public HExpression<1> {
public:
- explicit HLoadLocal(HLocal* local, Primitive::Type type) : HExpression(type) {
+ explicit HLoadLocal(HLocal* local, Primitive::Type type)
+ : HExpression(type, SideEffects::None()) {
SetRawInputAt(0, local);
}
@@ -1073,7 +1147,7 @@
// and the local.
class HStoreLocal : public HTemplateInstruction<2> {
public:
- HStoreLocal(HLocal* local, HInstruction* value) {
+ HStoreLocal(HLocal* local, HInstruction* value) : HTemplateInstruction(SideEffects::None()) {
SetRawInputAt(0, local);
SetRawInputAt(1, value);
}
@@ -1088,7 +1162,9 @@
class HConstant : public HExpression<0> {
public:
- explicit HConstant(Primitive::Type type) : HExpression(type) {}
+ explicit HConstant(Primitive::Type type) : HExpression(type, SideEffects::None()) {}
+
+ virtual bool CanBeMoved() const { return true; }
DECLARE_INSTRUCTION(Constant);
@@ -1104,6 +1180,10 @@
int32_t GetValue() const { return value_; }
+ virtual bool InstructionDataEquals(HInstruction* other) const {
+ return other->AsIntConstant()->value_ == value_;
+ }
+
DECLARE_INSTRUCTION(IntConstant);
private:
@@ -1118,6 +1198,10 @@
int64_t GetValue() const { return value_; }
+ virtual bool InstructionDataEquals(HInstruction* other) const {
+ return other->AsLongConstant()->value_ == value_;
+ }
+
DECLARE_INSTRUCTION(LongConstant);
private:
@@ -1132,7 +1216,8 @@
uint32_t number_of_arguments,
Primitive::Type return_type,
uint32_t dex_pc)
- : inputs_(arena, number_of_arguments),
+ : HInstruction(SideEffects::All()),
+ inputs_(arena, number_of_arguments),
return_type_(return_type),
dex_pc_(dex_pc) {
inputs_.SetSize(number_of_arguments);
@@ -1188,8 +1273,10 @@
class HNewInstance : public HExpression<0> {
public:
- HNewInstance(uint32_t dex_pc, uint16_t type_index) : HExpression(Primitive::kPrimNot),
- dex_pc_(dex_pc), type_index_(type_index) {}
+ HNewInstance(uint32_t dex_pc, uint16_t type_index)
+ : HExpression(Primitive::kPrimNot, SideEffects::None()),
+ dex_pc_(dex_pc),
+ type_index_(type_index) {}
uint32_t GetDexPc() const { return dex_pc_; }
uint16_t GetTypeIndex() const { return type_index_; }
@@ -1237,7 +1324,7 @@
class HParameterValue : public HExpression<0> {
public:
HParameterValue(uint8_t index, Primitive::Type parameter_type)
- : HExpression(parameter_type), index_(index) {}
+ : HExpression(parameter_type, SideEffects::None()), index_(index) {}
uint8_t GetIndex() const { return index_; }
@@ -1253,10 +1340,13 @@
class HNot : public HExpression<1> {
public:
- explicit HNot(HInstruction* input) : HExpression(Primitive::kPrimBoolean) {
+ explicit HNot(HInstruction* input) : HExpression(Primitive::kPrimBoolean, SideEffects::None()) {
SetRawInputAt(0, input);
}
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
DECLARE_INSTRUCTION(Not);
private:
@@ -1266,7 +1356,8 @@
class HPhi : public HInstruction {
public:
HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type)
- : inputs_(arena, number_of_inputs),
+ : HInstruction(SideEffects::None()),
+ inputs_(arena, number_of_inputs),
reg_number_(reg_number),
type_(type),
is_live_(false) {
@@ -1306,10 +1397,13 @@
class HNullCheck : public HExpression<1> {
public:
HNullCheck(HInstruction* value, uint32_t dex_pc)
- : HExpression(value->GetType()), dex_pc_(dex_pc) {
+ : HExpression(value->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
SetRawInputAt(0, value);
}
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
virtual bool NeedsEnvironment() const { return true; }
uint32_t GetDexPc() const { return dex_pc_; }
@@ -1324,13 +1418,15 @@
class FieldInfo : public ValueObject {
public:
- explicit FieldInfo(MemberOffset field_offset)
- : field_offset_(field_offset) {}
+ explicit FieldInfo(MemberOffset field_offset, Primitive::Type field_type)
+ : field_offset_(field_offset), field_type_(field_type) {}
MemberOffset GetFieldOffset() const { return field_offset_; }
+ Primitive::Type GetFieldType() const { return field_type_; }
private:
const MemberOffset field_offset_;
+ const Primitive::Type field_type_;
};
class HInstanceFieldGet : public HExpression<1> {
@@ -1338,11 +1434,19 @@
HInstanceFieldGet(HInstruction* value,
Primitive::Type field_type,
MemberOffset field_offset)
- : HExpression(field_type), field_info_(field_offset) {
+ : HExpression(field_type, SideEffects::DependsOnSomething()),
+ field_info_(field_offset, field_type) {
SetRawInputAt(0, value);
}
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const {
+ size_t other_offset = other->AsInstanceFieldGet()->GetFieldOffset().SizeValue();
+ return other_offset == GetFieldOffset().SizeValue();
+ }
+
MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
+ Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
DECLARE_INSTRUCTION(InstanceFieldGet);
@@ -1356,13 +1460,16 @@
public:
HInstanceFieldSet(HInstruction* object,
HInstruction* value,
+ Primitive::Type field_type,
MemberOffset field_offset)
- : field_info_(field_offset) {
+ : HTemplateInstruction(SideEffects::ChangesSomething()),
+ field_info_(field_offset, field_type) {
SetRawInputAt(0, object);
SetRawInputAt(1, value);
}
MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
+ Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
DECLARE_INSTRUCTION(InstanceFieldSet);
@@ -1375,11 +1482,14 @@
class HArrayGet : public HExpression<2> {
public:
HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type)
- : HExpression(type) {
+ : HExpression(type, SideEffects::DependsOnSomething()) {
SetRawInputAt(0, array);
SetRawInputAt(1, index);
}
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
DECLARE_INSTRUCTION(ArrayGet);
private:
@@ -1391,7 +1501,11 @@
HArraySet(HInstruction* array,
HInstruction* index,
HInstruction* value,
- uint32_t dex_pc) : dex_pc_(dex_pc) {
+ Primitive::Type component_type,
+ uint32_t dex_pc)
+ : HTemplateInstruction(SideEffects::ChangesSomething()),
+ dex_pc_(dex_pc),
+ component_type_(component_type) {
SetRawInputAt(0, array);
SetRawInputAt(1, index);
SetRawInputAt(2, value);
@@ -1405,20 +1519,29 @@
uint32_t GetDexPc() const { return dex_pc_; }
+ Primitive::Type GetComponentType() const { return component_type_; }
+
DECLARE_INSTRUCTION(ArraySet);
private:
const uint32_t dex_pc_;
+ const Primitive::Type component_type_;
DISALLOW_COPY_AND_ASSIGN(HArraySet);
};
class HArrayLength : public HExpression<1> {
public:
- explicit HArrayLength(HInstruction* array) : HExpression(Primitive::kPrimInt) {
+ explicit HArrayLength(HInstruction* array)
+ : HExpression(Primitive::kPrimInt, SideEffects::None()) {
+ // Note that arrays do not change length, so the instruction does not
+ // depend on any write.
SetRawInputAt(0, array);
}
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
DECLARE_INSTRUCTION(ArrayLength);
private:
@@ -1428,12 +1551,15 @@
class HBoundsCheck : public HExpression<2> {
public:
HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
- : HExpression(index->GetType()), dex_pc_(dex_pc) {
+ : HExpression(index->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
DCHECK(index->GetType() == Primitive::kPrimInt);
SetRawInputAt(0, index);
SetRawInputAt(1, length);
}
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
virtual bool NeedsEnvironment() const { return true; }
uint32_t GetDexPc() const { return dex_pc_; }
@@ -1455,7 +1581,7 @@
*/
class HTemporary : public HTemplateInstruction<0> {
public:
- explicit HTemporary(size_t index) : index_(index) {}
+ explicit HTemporary(size_t index) : HTemplateInstruction(SideEffects::None()), index_(index) {}
size_t GetIndex() const { return index_; }
@@ -1529,7 +1655,8 @@
class HParallelMove : public HTemplateInstruction<0> {
public:
- explicit HParallelMove(ArenaAllocator* arena) : moves_(arena, kDefaultNumberOfMoves) {}
+ explicit HParallelMove(ArenaAllocator* arena)
+ : HTemplateInstruction(SideEffects::None()), moves_(arena, kDefaultNumberOfMoves) {}
void AddMove(MoveOperands* move) {
moves_.Add(move);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 3461276..75f4155 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -240,8 +240,27 @@
visualizer.DumpGraph(kRegisterAllocatorPassName);
codegen->CompileOptimized(&allocator);
+
+ std::vector<uint8_t> mapping_table;
+ SrcMap src_mapping_table;
+ codegen->BuildMappingTable(&mapping_table,
+ GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
+ &src_mapping_table : nullptr);
+
+ std::vector<uint8_t> stack_map;
+ codegen->BuildStackMaps(&stack_map);
+
+ return new CompiledMethod(GetCompilerDriver(),
+ instruction_set,
+ allocator.GetMemory(),
+ codegen->GetFrameSize(),
+ codegen->GetCoreSpillMask(),
+ 0, /* FPR spill mask, unused */
+ mapping_table,
+ stack_map);
} else if (shouldOptimize && RegisterAllocator::Supports(instruction_set)) {
LOG(FATAL) << "Could not allocate registers in optimizing compiler";
+ return nullptr;
} else {
codegen->CompileBaseline(&allocator);
@@ -253,29 +272,29 @@
SsaLivenessAnalysis liveness(*graph, codegen);
liveness.Analyze();
visualizer.DumpGraph(kLivenessPassName);
+
+ std::vector<uint8_t> mapping_table;
+ SrcMap src_mapping_table;
+ codegen->BuildMappingTable(&mapping_table,
+ GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
+ &src_mapping_table : nullptr);
+ std::vector<uint8_t> vmap_table;
+ codegen->BuildVMapTable(&vmap_table);
+ std::vector<uint8_t> gc_map;
+ codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
+
+ return new CompiledMethod(GetCompilerDriver(),
+ instruction_set,
+ allocator.GetMemory(),
+ codegen->GetFrameSize(),
+ codegen->GetCoreSpillMask(),
+ 0, /* FPR spill mask, unused */
+ &src_mapping_table,
+ mapping_table,
+ vmap_table,
+ gc_map,
+ nullptr);
}
-
- std::vector<uint8_t> mapping_table;
- SrcMap src_mapping_table;
- codegen->BuildMappingTable(&mapping_table,
- GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
- &src_mapping_table : nullptr);
- std::vector<uint8_t> vmap_table;
- codegen->BuildVMapTable(&vmap_table);
- std::vector<uint8_t> gc_map;
- codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
-
- return new CompiledMethod(GetCompilerDriver(),
- instruction_set,
- allocator.GetMemory(),
- codegen->GetFrameSize(),
- codegen->GetCoreSpillMask(),
- 0, /* FPR spill mask, unused */
- &src_mapping_table,
- mapping_table,
- vmap_table,
- gc_map,
- nullptr);
}
CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index da13b1e..54888ba 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -31,18 +31,26 @@
: allocator_(allocator),
codegen_(codegen),
liveness_(liveness),
- unhandled_(allocator, 0),
+ unhandled_core_intervals_(allocator, 0),
+ unhandled_fp_intervals_(allocator, 0),
+ unhandled_(nullptr),
handled_(allocator, 0),
active_(allocator, 0),
inactive_(allocator, 0),
physical_register_intervals_(allocator, codegen->GetNumberOfRegisters()),
+ temp_intervals_(allocator, 4),
spill_slots_(allocator, kDefaultNumberOfSpillSlots),
+ safepoints_(allocator, 0),
processing_core_registers_(false),
number_of_registers_(-1),
registers_array_(nullptr),
- blocked_registers_(allocator->AllocArray<bool>(codegen->GetNumberOfRegisters())) {
+ blocked_registers_(allocator->AllocArray<bool>(codegen->GetNumberOfRegisters())),
+ reserved_out_slots_(0) {
codegen->SetupBlockedRegisters(blocked_registers_);
physical_register_intervals_.SetSize(codegen->GetNumberOfRegisters());
+ // Always reserve for the current method and the graph's max out registers.
+ // TODO: compute it instead.
+ reserved_out_slots_ = 1 + codegen->GetGraph()->GetMaximumNumberOfOutVRegs();
}
bool RegisterAllocator::CanAllocateRegistersFor(const HGraph& graph,
@@ -55,7 +63,6 @@
!it.Done();
it.Advance()) {
HInstruction* current = it.Current();
- if (current->NeedsEnvironment()) return false;
if (current->GetType() == Primitive::kPrimLong && instruction_set != kX86_64) return false;
if (current->GetType() == Primitive::kPrimFloat) return false;
if (current->GetType() == Primitive::kPrimDouble) return false;
@@ -65,17 +72,14 @@
}
static bool ShouldProcess(bool processing_core_registers, LiveInterval* interval) {
+ if (interval == nullptr) return false;
bool is_core_register = (interval->GetType() != Primitive::kPrimDouble)
&& (interval->GetType() != Primitive::kPrimFloat);
return processing_core_registers == is_core_register;
}
void RegisterAllocator::AllocateRegisters() {
- processing_core_registers_ = true;
AllocateRegistersInternal();
- processing_core_registers_ = false;
- AllocateRegistersInternal();
-
Resolve();
if (kIsDebugBuild) {
@@ -101,78 +105,120 @@
interval->AddRange(start, end);
}
-// TODO: make the register allocator understand instructions like HCondition
-// that may not need to be materialized. It doesn't need to allocate any
-// registers for it.
void RegisterAllocator::AllocateRegistersInternal() {
- number_of_registers_ = processing_core_registers_
- ? codegen_->GetNumberOfCoreRegisters()
- : codegen_->GetNumberOfFloatingPointRegisters();
-
- registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_);
-
// Iterate post-order, to ensure the list is sorted, and the last added interval
// is the one with the lowest start position.
- for (size_t i = liveness_.GetNumberOfSsaValues(); i > 0; --i) {
- HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i - 1);
- LiveInterval* current = instruction->GetLiveInterval();
- if (ShouldProcess(processing_core_registers_, current)) {
- DCHECK(unhandled_.IsEmpty() || current->StartsBefore(unhandled_.Peek()));
-
- LocationSummary* locations = instruction->GetLocations();
- if (locations->GetTempCount() != 0) {
- // Note that we already filtered out instructions requiring temporaries in
- // RegisterAllocator::CanAllocateRegistersFor.
- LOG(FATAL) << "Unimplemented";
- }
-
- // Some instructions define their output in fixed register/stack slot. We need
- // to ensure we know these locations before doing register allocation. For a
- // given register, we create an interval that covers these locations. The register
- // will be unavailable at these locations when trying to allocate one for an
- // interval.
- //
- // The backwards walking ensures the ranges are ordered on increasing start positions.
- Location output = locations->Out();
- size_t position = instruction->GetLifetimePosition();
- if (output.IsRegister()) {
- // Shift the interval's start by one to account for the blocked register.
- current->SetFrom(position + 1);
- current->SetRegister(output.reg().RegId());
- BlockRegister(output, position, position + 1, instruction->GetType());
- } else if (output.IsStackSlot() || output.IsDoubleStackSlot()) {
- current->SetSpillSlot(output.GetStackIndex());
- }
- for (size_t i = 0; i < instruction->InputCount(); ++i) {
- Location input = locations->InAt(i);
- if (input.IsRegister()) {
- BlockRegister(input, position, position + 1, instruction->InputAt(i)->GetType());
- }
- }
-
- // Add the interval to the correct list.
- if (current->HasRegister()) {
- DCHECK(instruction->IsParameterValue());
- inactive_.Add(current);
- } else if (current->HasSpillSlot() || instruction->IsConstant()) {
- // Split before first register use.
- size_t first_register_use = current->FirstRegisterUse();
- if (first_register_use != kNoLifetime) {
- LiveInterval* split = Split(current, first_register_use - 1);
- // Don't add direclty to `unhandled_`, it needs to be sorted and the start
- // of this new interval might be after intervals already in the list.
- AddToUnhandled(split);
- } else {
- // Nothing to do, we won't allocate a register for this value.
- }
- } else {
- DCHECK(unhandled_.IsEmpty() || current->StartsBefore(unhandled_.Peek()));
- unhandled_.Add(current);
- }
+ for (HLinearPostOrderIterator it(liveness_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ ProcessInstruction(it.Current());
+ }
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ ProcessInstruction(it.Current());
}
}
+ number_of_registers_ = codegen_->GetNumberOfCoreRegisters();
+ registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_);
+ processing_core_registers_ = true;
+ unhandled_ = &unhandled_core_intervals_;
LinearScan();
+
+ inactive_.Reset();
+ active_.Reset();
+ handled_.Reset();
+
+ number_of_registers_ = codegen_->GetNumberOfFloatingPointRegisters();
+ registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_);
+ processing_core_registers_ = false;
+ unhandled_ = &unhandled_fp_intervals_;
+ // TODO: Enable FP register allocation.
+ DCHECK(unhandled_->IsEmpty());
+ LinearScan();
+}
+
+void RegisterAllocator::ProcessInstruction(HInstruction* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ size_t position = instruction->GetLifetimePosition();
+
+ if (locations == nullptr) return;
+
+ // Create synthesized intervals for temporaries.
+ for (size_t i = 0; i < locations->GetTempCount(); ++i) {
+ Location temp = locations->GetTemp(i);
+ if (temp.IsRegister()) {
+ BlockRegister(temp, position, position + 1, Primitive::kPrimInt);
+ } else {
+ LiveInterval* interval =
+ LiveInterval::MakeTempInterval(allocator_, instruction, Primitive::kPrimInt);
+ temp_intervals_.Add(interval);
+ interval->AddRange(position, position + 1);
+ unhandled_core_intervals_.Add(interval);
+ }
+ }
+
+ if (locations->CanCall()) {
+ codegen_->MarkNotLeaf();
+ safepoints_.Add(instruction);
+ // Block all registers.
+ for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) {
+ BlockRegister(Location::RegisterLocation(ManagedRegister(i)),
+ position,
+ position + 1,
+ Primitive::kPrimInt);
+ }
+ }
+
+ for (size_t i = 0; i < instruction->InputCount(); ++i) {
+ Location input = locations->InAt(i);
+ if (input.IsRegister()) {
+ BlockRegister(input, position, position + 1, instruction->InputAt(i)->GetType());
+ }
+ }
+
+ bool core_register = (instruction->GetType() != Primitive::kPrimDouble)
+ && (instruction->GetType() != Primitive::kPrimFloat);
+ GrowableArray<LiveInterval*>& unhandled = core_register
+ ? unhandled_core_intervals_
+ : unhandled_fp_intervals_;
+
+ LiveInterval* current = instruction->GetLiveInterval();
+ if (current == nullptr) return;
+
+ DCHECK(unhandled.IsEmpty() || current->StartsBefore(unhandled.Peek()));
+ // Some instructions define their output in fixed register/stack slot. We need
+ // to ensure we know these locations before doing register allocation. For a
+ // given register, we create an interval that covers these locations. The register
+ // will be unavailable at these locations when trying to allocate one for an
+ // interval.
+ //
+ // The backwards walking ensures the ranges are ordered on increasing start positions.
+ Location output = locations->Out();
+ if (output.IsRegister()) {
+ // Shift the interval's start by one to account for the blocked register.
+ current->SetFrom(position + 1);
+ current->SetRegister(output.reg().RegId());
+ BlockRegister(output, position, position + 1, instruction->GetType());
+ } else if (output.IsStackSlot() || output.IsDoubleStackSlot()) {
+ current->SetSpillSlot(output.GetStackIndex());
+ }
+
+ // If needed, add interval to the list of unhandled intervals.
+ if (current->HasSpillSlot() || instruction->IsConstant()) {
+ // Split before first register use.
+ size_t first_register_use = current->FirstRegisterUse();
+ if (first_register_use != kNoLifetime) {
+ LiveInterval* split = Split(current, first_register_use - 1);
+ // Don't add direclty to `unhandled`, it needs to be sorted and the start
+ // of this new interval might be after intervals already in the list.
+ AddSorted(&unhandled, split);
+ } else {
+ // Nothing to do, we won't allocate a register for this value.
+ }
+ } else {
+ DCHECK(unhandled.IsEmpty() || current->StartsBefore(unhandled.Peek()));
+ unhandled.Add(current);
+ }
}
class AllRangesIterator : public ValueObject {
@@ -220,12 +266,20 @@
}
}
- return ValidateIntervals(intervals, spill_slots_.Size(), *codegen_, allocator_,
- processing_core_registers_, log_fatal_on_failure);
+ for (size_t i = 0, e = temp_intervals_.Size(); i < e; ++i) {
+ LiveInterval* temp = temp_intervals_.Get(i);
+ if (ShouldProcess(processing_core_registers_, temp)) {
+ intervals.Add(temp);
+ }
+ }
+
+ return ValidateIntervals(intervals, spill_slots_.Size(), reserved_out_slots_, *codegen_,
+ allocator_, processing_core_registers_, log_fatal_on_failure);
}
bool RegisterAllocator::ValidateIntervals(const GrowableArray<LiveInterval*>& intervals,
size_t number_of_spill_slots,
+ size_t number_of_out_slots,
const CodeGenerator& codegen,
ArenaAllocator* allocator,
bool processing_core_registers,
@@ -249,8 +303,9 @@
if (current->GetParent()->HasSpillSlot()
// Parameters have their own stack slot.
&& !(defined_by != nullptr && defined_by->IsParameterValue())) {
- BitVector* liveness_of_spill_slot = liveness_of_values.Get(
- number_of_registers + current->GetParent()->GetSpillSlot() / kVRegSize);
+ BitVector* liveness_of_spill_slot = liveness_of_values.Get(number_of_registers
+ + current->GetParent()->GetSpillSlot() / kVRegSize
+ - number_of_out_slots);
for (size_t j = it.CurrentRange()->GetStart(); j < it.CurrentRange()->GetEnd(); ++j) {
if (liveness_of_spill_slot->IsBitSet(j)) {
if (log_fatal_on_failure) {
@@ -272,7 +327,11 @@
if (liveness_of_register->IsBitSet(j)) {
if (log_fatal_on_failure) {
std::ostringstream message;
- message << "Register conflict at " << j << " for ";
+ message << "Register conflict at " << j << " ";
+ if (defined_by != nullptr) {
+ message << "(" << defined_by->DebugName() << ")";
+ }
+ message << "for ";
if (processing_core_registers) {
codegen.DumpCoreRegister(message, current->GetRegister());
} else {
@@ -309,10 +368,10 @@
// By the book implementation of a linear scan register allocator.
void RegisterAllocator::LinearScan() {
- while (!unhandled_.IsEmpty()) {
+ while (!unhandled_->IsEmpty()) {
// (1) Remove interval with the lowest start position from unhandled.
- LiveInterval* current = unhandled_.Pop();
- DCHECK(!current->IsFixed() && !current->HasRegister() && !current->HasSpillSlot());
+ LiveInterval* current = unhandled_->Pop();
+ DCHECK(!current->IsFixed() && !current->HasSpillSlot());
size_t position = current->GetStart();
// (2) Remove currently active intervals that are dead at this position.
@@ -392,13 +451,19 @@
free_until[interval->GetRegister()] = 0;
}
- // Pick the register that is free the longest.
int reg = -1;
- for (size_t i = 0; i < number_of_registers_; ++i) {
- if (IsBlocked(i)) continue;
- if (reg == -1 || free_until[i] > free_until[reg]) {
- reg = i;
- if (free_until[i] == kMaxLifetimePosition) break;
+ if (current->HasRegister()) {
+ // Some instructions have a fixed register output.
+ reg = current->GetRegister();
+ DCHECK_NE(free_until[reg], 0u);
+ } else {
+ // Pick the register that is free the longest.
+ for (size_t i = 0; i < number_of_registers_; ++i) {
+ if (IsBlocked(i)) continue;
+ if (reg == -1 || free_until[i] > free_until[reg]) {
+ reg = i;
+ if (free_until[i] == kMaxLifetimePosition) break;
+ }
}
}
@@ -414,7 +479,7 @@
// the register is not available anymore.
LiveInterval* split = Split(current, free_until[reg]);
DCHECK(split != nullptr);
- AddToUnhandled(split);
+ AddSorted(unhandled_, split);
}
return true;
}
@@ -493,7 +558,7 @@
// register, we split this interval just before its first register use.
AllocateSpillSlotFor(current);
LiveInterval* split = Split(current, first_register_use - 1);
- AddToUnhandled(split);
+ AddSorted(unhandled_, split);
return false;
} else {
// Use this register and spill the active and inactives interval that
@@ -507,7 +572,7 @@
LiveInterval* split = Split(active, current->GetStart());
active_.DeleteAt(i);
handled_.Add(active);
- AddToUnhandled(split);
+ AddSorted(unhandled_, split);
break;
}
}
@@ -519,12 +584,12 @@
if (next_intersection != kNoLifetime) {
if (inactive->IsFixed()) {
LiveInterval* split = Split(current, next_intersection);
- AddToUnhandled(split);
+ AddSorted(unhandled_, split);
} else {
LiveInterval* split = Split(inactive, current->GetStart());
inactive_.DeleteAt(i);
handled_.Add(inactive);
- AddToUnhandled(split);
+ AddSorted(unhandled_, split);
--i;
}
}
@@ -535,16 +600,16 @@
}
}
-void RegisterAllocator::AddToUnhandled(LiveInterval* interval) {
+void RegisterAllocator::AddSorted(GrowableArray<LiveInterval*>* array, LiveInterval* interval) {
size_t insert_at = 0;
- for (size_t i = unhandled_.Size(); i > 0; --i) {
- LiveInterval* current = unhandled_.Get(i - 1);
+ for (size_t i = array->Size(); i > 0; --i) {
+ LiveInterval* current = array->Get(i - 1);
if (current->StartsAfter(interval)) {
insert_at = i;
break;
}
}
- unhandled_.InsertAt(insert_at, interval);
+ array->InsertAt(insert_at, interval);
}
LiveInterval* RegisterAllocator::Split(LiveInterval* interval, size_t position) {
@@ -624,7 +689,7 @@
spill_slots_.Put(slot + 1, end);
}
- parent->SetSpillSlot(slot * kVRegSize);
+ parent->SetSpillSlot((slot + reserved_out_slots_) * kVRegSize);
}
void RegisterAllocator::AllocateOneSpillSlot(LiveInterval* parent, size_t end) {
@@ -643,7 +708,7 @@
spill_slots_.Put(slot, end);
}
- parent->SetSpillSlot(slot * kVRegSize);
+ parent->SetSpillSlot((slot + reserved_out_slots_) * kVRegSize);
}
static Location ConvertToLocation(LiveInterval* interval) {
@@ -820,8 +885,10 @@
// Walk over all uses covered by this interval, and update the location
// information.
while (use != nullptr && use->GetPosition() <= current->GetEnd()) {
- if (!use->GetIsEnvironment()) {
- LocationSummary* locations = use->GetUser()->GetLocations();
+ LocationSummary* locations = use->GetUser()->GetLocations();
+ if (use->GetIsEnvironment()) {
+ locations->SetEnvironmentAt(use->GetInputIndex(), source);
+ } else {
Location expected_location = locations->InAt(use->GetInputIndex());
if (expected_location.IsUnallocated()) {
locations->SetInAt(use->GetInputIndex(), source);
@@ -841,6 +908,38 @@
Location destination = ConvertToLocation(next_sibling);
InsertParallelMoveAt(current->GetEnd(), source, destination);
}
+
+ // At each safepoint, we record stack and register information.
+ for (size_t i = 0, e = safepoints_.Size(); i < e; ++i) {
+ HInstruction* safepoint = safepoints_.Get(i);
+ size_t position = safepoint->GetLifetimePosition();
+ LocationSummary* locations = safepoint->GetLocations();
+ if (!current->Covers(position)) continue;
+
+ if (current->GetType() == Primitive::kPrimNot) {
+ DCHECK(current->GetParent()->HasSpillSlot());
+ locations->SetStackBit(current->GetParent()->GetSpillSlot() / kVRegSize);
+ }
+
+ switch (source.GetKind()) {
+ case Location::kRegister: {
+ locations->SetLiveRegister(source.reg().RegId());
+ if (current->GetType() == Primitive::kPrimNot) {
+ locations->SetRegisterBit(source.reg().RegId());
+ }
+ break;
+ }
+ case Location::kStackSlot: // Fall-through
+ case Location::kDoubleStackSlot: // Fall-through
+ case Location::kConstant: {
+ // Nothing to do.
+ break;
+ }
+ default: {
+ LOG(FATAL) << "Unexpected location for object";
+ }
+ }
+ }
current = next_sibling;
} while (current != nullptr);
DCHECK(use == nullptr);
@@ -907,7 +1006,7 @@
}
void RegisterAllocator::Resolve() {
- codegen_->ComputeFrameSize(spill_slots_.Size());
+ codegen_->ComputeFrameSize(spill_slots_.Size(), reserved_out_slots_);
// Adjust the Out Location of instructions.
// TODO: Use pointers of Location inside LiveInterval to avoid doing another iteration.
@@ -978,6 +1077,20 @@
}
}
}
+
+ // Assign temp locations.
+ HInstruction* current = nullptr;
+ size_t temp_index = 0;
+ for (size_t i = 0; i < temp_intervals_.Size(); ++i) {
+ LiveInterval* temp = temp_intervals_.Get(i);
+ if (temp->GetDefinedBy() != current) {
+ temp_index = 0;
+ current = temp->GetDefinedBy();
+ }
+ LocationSummary* locations = current->GetLocations();
+ locations->SetTempAt(
+ temp_index++, Location::RegisterLocation(ManagedRegister(temp->GetRegister())));
+ }
}
} // namespace art
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index be1c7ec..f737491 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -59,6 +59,7 @@
// Helper method for validation. Used by unit testing.
static bool ValidateIntervals(const GrowableArray<LiveInterval*>& intervals,
size_t number_of_spill_slots,
+ size_t number_of_out_slots,
const CodeGenerator& codegen,
ArenaAllocator* allocator,
bool processing_core_registers,
@@ -83,8 +84,8 @@
bool AllocateBlockedReg(LiveInterval* interval);
void Resolve();
- // Add `interval` in the sorted list of unhandled intervals.
- void AddToUnhandled(LiveInterval* interval);
+ // Add `interval` in the given sorted list.
+ static void AddSorted(GrowableArray<LiveInterval*>* array, LiveInterval* interval);
// Split `interval` at the position `at`. The new interval starts at `at`.
LiveInterval* Split(LiveInterval* interval, size_t at);
@@ -115,6 +116,7 @@
// Helper methods.
void AllocateRegistersInternal();
+ void ProcessInstruction(HInstruction* instruction);
bool ValidateInternal(bool log_fatal_on_failure) const;
void DumpInterval(std::ostream& stream, LiveInterval* interval) const;
@@ -122,9 +124,17 @@
CodeGenerator* const codegen_;
const SsaLivenessAnalysis& liveness_;
- // List of intervals that must be processed, ordered by start position. Last entry
- // is the interval that has the lowest start position.
- GrowableArray<LiveInterval*> unhandled_;
+ // List of intervals for core registers that must be processed, ordered by start
+ // position. Last entry is the interval that has the lowest start position.
+ // This list is initially populated before doing the linear scan.
+ GrowableArray<LiveInterval*> unhandled_core_intervals_;
+
+ // List of intervals for floating-point registers. Same comments as above.
+ GrowableArray<LiveInterval*> unhandled_fp_intervals_;
+
+ // Currently processed list of unhandled intervals. Either `unhandled_core_intervals_`
+ // or `unhandled_fp_intervals_`.
+ GrowableArray<LiveInterval*>* unhandled_;
// List of intervals that have been processed.
GrowableArray<LiveInterval*> handled_;
@@ -137,13 +147,20 @@
// That is, they have a lifetime hole that spans the start of the new interval.
GrowableArray<LiveInterval*> inactive_;
- // Fixed intervals for physical registers. Such an interval covers the positions
+ // Fixed intervals for physical registers. Such intervals cover the positions
// where an instruction requires a specific register.
GrowableArray<LiveInterval*> physical_register_intervals_;
+ // Intervals for temporaries. Such intervals cover the positions
+ // where an instruction requires a temporary.
+ GrowableArray<LiveInterval*> temp_intervals_;
+
// The spill slots allocated for live intervals.
GrowableArray<size_t> spill_slots_;
+ // Instructions that need a safepoint.
+ GrowableArray<HInstruction*> safepoints_;
+
// True if processing core registers. False if processing floating
// point registers.
bool processing_core_registers_;
@@ -157,6 +174,9 @@
// Blocked registers, as decided by the code generator.
bool* const blocked_registers_;
+ // Slots reserved for out arguments.
+ size_t reserved_out_slots_;
+
DISALLOW_COPY_AND_ASSIGN(RegisterAllocator);
};
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index bafe577..7539d44 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -66,11 +66,11 @@
intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 0));
intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 1));
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, *codegen, &allocator, true, false));
intervals.Get(1)->SetRegister(0);
ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, *codegen, &allocator, true, false));
intervals.Reset();
}
@@ -81,11 +81,11 @@
static constexpr size_t ranges2[][2] = {{42, 43}};
intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, *codegen, &allocator, true, false));
intervals.Get(1)->SetRegister(0);
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, *codegen, &allocator, true, false));
intervals.Reset();
}
@@ -96,11 +96,11 @@
static constexpr size_t ranges2[][2] = {{42, 43}};
intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, *codegen, &allocator, true, false));
intervals.Get(1)->SetRegister(0);
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, *codegen, &allocator, true, false));
intervals.Reset();
}
@@ -111,11 +111,11 @@
static constexpr size_t ranges2[][2] = {{42, 47}};
intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, *codegen, &allocator, true, false));
intervals.Get(1)->SetRegister(0);
ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, *codegen, &allocator, true, false));
intervals.Reset();
}
@@ -127,16 +127,16 @@
static constexpr size_t ranges2[][2] = {{42, 47}};
intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, *codegen, &allocator, true, false));
intervals.Get(1)->SetRegister(0);
// Sibling of the first interval has no register allocated to it.
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, *codegen, &allocator, true, false));
intervals.Get(0)->GetNextSibling()->SetRegister(0);
ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, *codegen, &allocator, true, false));
}
}
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 83035b5..33b1f1f 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -47,7 +47,7 @@
};
/**
- * A live range contains the start and end of a range where an instruction
+ * A live range contains the start and end of a range where an instruction or a temporary
* is live.
*/
class LiveRange : public ArenaObject {
@@ -76,7 +76,7 @@
private:
size_t start_;
- size_t end_;
+ const size_t end_;
LiveRange* next_;
friend class LiveInterval;
@@ -133,7 +133,12 @@
*/
class LiveInterval : public ArenaObject {
public:
- LiveInterval(ArenaAllocator* allocator, Primitive::Type type, HInstruction* defined_by = nullptr)
+ LiveInterval(ArenaAllocator* allocator,
+ Primitive::Type type,
+ HInstruction* defined_by = nullptr,
+ bool is_fixed = false,
+ int reg = kNoRegister,
+ bool is_temp = false)
: allocator_(allocator),
first_range_(nullptr),
last_range_(nullptr),
@@ -141,16 +146,20 @@
type_(type),
next_sibling_(nullptr),
parent_(this),
- register_(kNoRegister),
+ register_(reg),
spill_slot_(kNoSpillSlot),
- is_fixed_(false),
+ is_fixed_(is_fixed),
+ is_temp_(is_temp),
defined_by_(defined_by) {}
static LiveInterval* MakeFixedInterval(ArenaAllocator* allocator, int reg, Primitive::Type type) {
- LiveInterval* interval = new (allocator) LiveInterval(allocator, type);
- interval->SetRegister(reg);
- interval->is_fixed_ = true;
- return interval;
+ return new (allocator) LiveInterval(allocator, type, nullptr, true, reg, false);
+ }
+
+ static LiveInterval* MakeTempInterval(ArenaAllocator* allocator,
+ HInstruction* defined_by,
+ Primitive::Type type) {
+ return new (allocator) LiveInterval(allocator, type, defined_by, false, kNoRegister, true);
}
bool IsFixed() const { return is_fixed_; }
@@ -192,8 +201,10 @@
} else if (first_range_->GetStart() == end) {
// There is a use in the following block.
first_range_->start_ = start;
+ } else if (first_range_->GetStart() == start && first_range_->GetEnd() == end) {
+ DCHECK(is_fixed_);
} else {
- DCHECK(first_range_->GetStart() > end);
+ DCHECK_GT(first_range_->GetStart(), end);
// There is a hole in the interval. Create a new range.
first_range_ = new (allocator_) LiveRange(start, end, first_range_);
}
@@ -215,7 +226,11 @@
}
bool HasSpillSlot() const { return spill_slot_ != kNoSpillSlot; }
- void SetSpillSlot(int slot) { spill_slot_ = slot; }
+ void SetSpillSlot(int slot) {
+ DCHECK(!is_fixed_);
+ DCHECK(!is_temp_);
+ spill_slot_ = slot;
+ }
int GetSpillSlot() const { return spill_slot_; }
void SetFrom(size_t from) {
@@ -243,6 +258,9 @@
}
bool Covers(size_t position) const {
+ if (IsDeadAt(position)) {
+ return false;
+ }
LiveRange* current = first_range_;
while (current != nullptr) {
if (position >= current->GetStart() && position < current->GetEnd()) {
@@ -288,6 +306,9 @@
}
size_t FirstRegisterUseAfter(size_t position) const {
+ if (is_temp_) {
+ return position == GetStart() ? position : kNoLifetime;
+ }
if (position == GetStart() && defined_by_ != nullptr) {
LocationSummary* locations = defined_by_->GetLocations();
Location location = locations->Out();
@@ -342,6 +363,7 @@
* [position ... end)
*/
LiveInterval* SplitAt(size_t position) {
+ DCHECK(!is_temp_);
DCHECK(!is_fixed_);
DCHECK_GT(position, GetStart());
@@ -453,7 +475,10 @@
int spill_slot_;
// Whether the interval is for a fixed register.
- bool is_fixed_;
+ const bool is_fixed_;
+
+ // Whether the interval is for a temporary.
+ const bool is_temp_;
// The instruction represented by this interval.
HInstruction* const defined_by_;
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index a079954..65675dc 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -26,6 +26,8 @@
HPhi* phi = it.Current()->AsPhi();
if (phi->HasEnvironmentUses()) {
// TODO: Do we want to keep that phi alive?
+ worklist_.Add(phi);
+ phi->SetLive();
continue;
}
for (HUseIterator<HInstruction> it(phi->GetUses()); !it.Done(); it.Advance()) {
@@ -105,7 +107,7 @@
for (size_t i = 1; i < phi->InputCount(); ++i) {
HInstruction* input = phi->InputAt(i);
- // For a loop phi, If the input is the phi, the phi is still candidate for
+ // For a loop phi, if the input is the phi, the phi is still candidate for
// elimination.
if (input != candidate && input != phi) {
candidate = nullptr;
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 5e1329e..0ea11ad 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -26,9 +26,9 @@
namespace art {
/**
- * Collects and builds a CodeInfo for a method.
+ * Collects and builds stack maps for a method. All the stack maps
+ * for a method are placed in a CodeInfo object.
*/
-template<typename T>
class StackMapStream : public ValueObject {
public:
explicit StackMapStream(ArenaAllocator* allocator)
@@ -47,7 +47,7 @@
// See runtime/stack_map.h to know what these fields contain.
struct StackMapEntry {
uint32_t dex_pc;
- T native_pc;
+ uint32_t native_pc_offset;
uint32_t register_mask;
BitVector* sp_mask;
uint32_t num_dex_registers;
@@ -66,14 +66,14 @@
};
void AddStackMapEntry(uint32_t dex_pc,
- T native_pc,
+ uint32_t native_pc_offset,
uint32_t register_mask,
BitVector* sp_mask,
uint32_t num_dex_registers,
uint8_t inlining_depth) {
StackMapEntry entry;
entry.dex_pc = dex_pc;
- entry.native_pc = native_pc;
+ entry.native_pc_offset = native_pc_offset;
entry.register_mask = register_mask;
entry.sp_mask = sp_mask;
entry.num_dex_registers = num_dex_registers;
@@ -82,7 +82,9 @@
entry.inline_infos_start_index = inline_infos_.Size();
stack_maps_.Add(entry);
- stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet());
+ if (sp_mask != nullptr) {
+ stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet());
+ }
if (inlining_depth > 0) {
number_of_stack_maps_with_inline_info_++;
}
@@ -102,14 +104,14 @@
}
size_t ComputeNeededSize() const {
- return CodeInfo<T>::kFixedSize
+ return CodeInfo::kFixedSize
+ ComputeStackMapSize()
+ ComputeDexRegisterMapSize()
+ ComputeInlineInfoSize();
}
size_t ComputeStackMapSize() const {
- return stack_maps_.Size() * (StackMap<T>::kFixedSize + StackMaskEncodingSize(stack_mask_max_));
+ return stack_maps_.Size() * (StackMap::kFixedSize + StackMaskEncodingSize(stack_mask_max_));
}
size_t ComputeDexRegisterMapSize() const {
@@ -130,11 +132,12 @@
}
size_t ComputeDexRegisterMapStart() const {
- return CodeInfo<T>::kFixedSize + ComputeStackMapSize();
+ return CodeInfo::kFixedSize + ComputeStackMapSize();
}
void FillIn(MemoryRegion region) {
- CodeInfo<T> code_info(region);
+ CodeInfo code_info(region);
+ code_info.SetOverallSize(region.size());
size_t stack_mask_size = StackMaskEncodingSize(stack_mask_max_);
uint8_t* memory_start = region.start();
@@ -153,13 +156,15 @@
uintptr_t next_dex_register_map_offset = 0;
uintptr_t next_inline_info_offset = 0;
for (size_t i = 0, e = stack_maps_.Size(); i < e; ++i) {
- StackMap<T> stack_map = code_info.GetStackMapAt(i);
+ StackMap stack_map = code_info.GetStackMapAt(i);
StackMapEntry entry = stack_maps_.Get(i);
stack_map.SetDexPc(entry.dex_pc);
- stack_map.SetNativePc(entry.native_pc);
+ stack_map.SetNativePcOffset(entry.native_pc_offset);
stack_map.SetRegisterMask(entry.register_mask);
- stack_map.SetStackMask(*entry.sp_mask);
+ if (entry.sp_mask != nullptr) {
+ stack_map.SetStackMask(*entry.sp_mask);
+ }
// Set the register map.
MemoryRegion region = dex_register_maps_region.Subregion(
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index a70259e..5ee6ae0 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -34,7 +34,7 @@
TEST(StackMapTest, Test1) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- StackMapStream<size_t> stream(&arena);
+ StackMapStream stream(&arena);
ArenaBitVector sp_mask(&arena, 0, false);
stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, 2, 0);
@@ -46,15 +46,15 @@
MemoryRegion region(memory, size);
stream.FillIn(region);
- CodeInfo<size_t> code_info(region);
+ CodeInfo code_info(region);
ASSERT_EQ(0u, code_info.GetStackMaskSize());
ASSERT_EQ(1u, code_info.GetNumberOfStackMaps());
- StackMap<size_t> stack_map = code_info.GetStackMapAt(0);
+ StackMap stack_map = code_info.GetStackMapAt(0);
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(64)));
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
ASSERT_EQ(0u, stack_map.GetDexPc());
- ASSERT_EQ(64u, stack_map.GetNativePc());
+ ASSERT_EQ(64u, stack_map.GetNativePcOffset());
ASSERT_EQ(0x3u, stack_map.GetRegisterMask());
ASSERT_FALSE(stack_map.HasInlineInfo());
@@ -71,7 +71,7 @@
TEST(StackMapTest, Test2) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- StackMapStream<size_t> stream(&arena);
+ StackMapStream stream(&arena);
ArenaBitVector sp_mask1(&arena, 0, true);
sp_mask1.SetBit(2);
@@ -93,15 +93,15 @@
MemoryRegion region(memory, size);
stream.FillIn(region);
- CodeInfo<size_t> code_info(region);
+ CodeInfo code_info(region);
ASSERT_EQ(1u, code_info.GetStackMaskSize());
ASSERT_EQ(2u, code_info.GetNumberOfStackMaps());
- StackMap<size_t> stack_map = code_info.GetStackMapAt(0);
+ StackMap stack_map = code_info.GetStackMapAt(0);
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(64)));
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
ASSERT_EQ(0u, stack_map.GetDexPc());
- ASSERT_EQ(64u, stack_map.GetNativePc());
+ ASSERT_EQ(64u, stack_map.GetNativePcOffset());
ASSERT_EQ(0x3u, stack_map.GetRegisterMask());
MemoryRegion stack_mask = stack_map.GetStackMask();
@@ -120,9 +120,9 @@
stack_map = code_info.GetStackMapAt(1);
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u)));
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(128u)));
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u)));
ASSERT_EQ(1u, stack_map.GetDexPc());
- ASSERT_EQ(128u, stack_map.GetNativePc());
+ ASSERT_EQ(128u, stack_map.GetNativePcOffset());
ASSERT_EQ(0xFFu, stack_map.GetRegisterMask());
stack_mask = stack_map.GetStackMask();
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index c35ff85..25d10bd 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -22,17 +22,17 @@
oatdump.cc
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler,art/disassembler,target,ndebug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler libart-compiler,art/disassembler art/compiler,target,ndebug))
endif
ifeq ($(ART_BUILD_TARGET_DEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler,art/disassembler,target,debug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler libart-compiler,art/disassembler art/compiler,target,debug))
endif
ifeq ($(ART_BUILD_HOST_NDEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler,art/disassembler,host,ndebug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler libart-compiler,art/disassembler art/compiler,host,ndebug))
endif
ifeq ($(ART_BUILD_HOST_DEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler,art/disassembler,host,debug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler libart-compiler,art/disassembler art/compiler,host,debug))
endif
########################################################################
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 931ee56..4cdf618 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -20,6 +20,7 @@
#include <fstream>
#include <iostream>
#include <string>
+#include <unordered_map>
#include <vector>
#include "base/stringpiece.h"
@@ -29,6 +30,7 @@
#include "dex_file-inl.h"
#include "dex_instruction.h"
#include "disassembler.h"
+#include "elf_builder.h"
#include "field_helper.h"
#include "gc_map.h"
#include "gc/space/image_space.h"
@@ -47,6 +49,7 @@
#include "oat.h"
#include "oat_file-inl.h"
#include "os.h"
+#include "output_stream.h"
#include "runtime.h"
#include "safe_map.h"
#include "scoped_thread_state_change.h"
@@ -105,6 +108,224 @@
"kClassRoots",
};
+class OatSymbolizer : public CodeOutput {
+ public:
+ explicit OatSymbolizer(const OatFile* oat_file, std::string& output_name) :
+ oat_file_(oat_file), builder_(nullptr), elf_output_(nullptr), output_name_(output_name) {}
+
+ bool Init() {
+ Elf32_Word oat_data_size = oat_file_->GetOatHeader().GetExecutableOffset();
+
+ uint32_t diff = static_cast<uint32_t>(oat_file_->End() - oat_file_->Begin());
+ uint32_t oat_exec_size = diff - oat_data_size;
+
+ if (output_name_.empty()) {
+ output_name_ = "symbolized.oat";
+ }
+ elf_output_ = OS::CreateEmptyFile(output_name_.c_str());
+
+ builder_.reset(new ElfBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+ Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(
+ this,
+ elf_output_,
+ oat_file_->GetOatHeader().GetInstructionSet(),
+ 0,
+ oat_data_size,
+ oat_data_size,
+ oat_exec_size,
+ true,
+ false));
+
+ if (!builder_->Init()) {
+ builder_.reset(nullptr);
+ return false;
+ }
+
+ return true;
+ }
+
+ typedef void (OatSymbolizer::*Callback)(const DexFile::ClassDef&,
+ uint32_t,
+ const OatFile::OatMethod&,
+ const DexFile&,
+ uint32_t,
+ const DexFile::CodeItem*,
+ uint32_t);
+
+ bool Symbolize() {
+ if (builder_.get() == nullptr) {
+ return false;
+ }
+
+ Walk(&art::OatSymbolizer::RegisterForDedup);
+
+ NormalizeState();
+
+ Walk(&art::OatSymbolizer::AddSymbol);
+
+ bool result = builder_->Write();
+
+ elf_output_->Flush();
+ elf_output_->Close();
+
+ return result;
+ }
+
+ void Walk(Callback callback) {
+ std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file_->GetOatDexFiles();
+ for (size_t i = 0; i < oat_dex_files.size(); i++) {
+ const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i];
+ CHECK(oat_dex_file != NULL);
+ WalkOatDexFile(oat_dex_file, callback);
+ }
+ }
+
+ void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file, Callback callback) {
+ std::string error_msg;
+ std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(&error_msg));
+ if (dex_file.get() == nullptr) {
+ return;
+ }
+ for (size_t class_def_index = 0;
+ class_def_index < dex_file->NumClassDefs();
+ class_def_index++) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+ const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
+ OatClassType type = oat_class.GetType();
+ switch (type) {
+ case kOatClassAllCompiled:
+ case kOatClassSomeCompiled:
+ WalkOatClass(oat_class, *dex_file.get(), class_def, callback);
+ break;
+
+ case kOatClassNoneCompiled:
+ case kOatClassMax:
+ // Ignore.
+ break;
+ }
+ }
+ }
+
+ void WalkOatClass(const OatFile::OatClass& oat_class, const DexFile& dex_file,
+ const DexFile::ClassDef& class_def, Callback callback) {
+ const byte* class_data = dex_file.GetClassData(class_def);
+ if (class_data == nullptr) { // empty class such as a marker interface?
+ return;
+ }
+ // Note: even if this is an interface or a native class, we still have to walk it, as there
+ // might be a static initializer.
+ ClassDataItemIterator it(dex_file, class_data);
+ SkipAllFields(&it);
+ uint32_t class_method_idx = 0;
+ while (it.HasNextDirectMethod()) {
+ const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+ WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(),
+ it.GetMethodCodeItem(), it.GetMemberAccessFlags(), callback);
+ class_method_idx++;
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+ WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(),
+ it.GetMethodCodeItem(), it.GetMemberAccessFlags(), callback);
+ class_method_idx++;
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
+ }
+
+ void WalkOatMethod(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+ const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+ uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags, Callback callback) {
+ if ((method_access_flags & kAccAbstract) != 0) {
+ // Abstract method, no code.
+ return;
+ }
+ if (oat_method.GetCodeOffset() == 0) {
+ // No code.
+ return;
+ }
+
+ (this->*callback)(class_def, class_method_index, oat_method, dex_file, dex_method_idx, code_item,
+ method_access_flags);
+ }
+
+ void RegisterForDedup(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+ const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+ uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags) {
+ state_[oat_method.GetCodeOffset()]++;
+ }
+
+ void NormalizeState() {
+ for (auto& x : state_) {
+ if (x.second == 1) {
+ state_[x.first] = 0;
+ }
+ }
+ }
+
+ enum class DedupState { // private
+ kNotDeduplicated,
+ kDeduplicatedFirst,
+ kDeduplicatedOther
+ };
+ DedupState IsDuplicated(uint32_t offset) {
+ if (state_[offset] == 0) {
+ return DedupState::kNotDeduplicated;
+ }
+ if (state_[offset] == 1) {
+ return DedupState::kDeduplicatedOther;
+ }
+ state_[offset] = 1;
+ return DedupState::kDeduplicatedFirst;
+ }
+
+ void AddSymbol(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+ const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+ uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags) {
+ DedupState dedup = IsDuplicated(oat_method.GetCodeOffset());
+ if (dedup != DedupState::kDeduplicatedOther) {
+ std::string pretty_name = PrettyMethod(dex_method_idx, dex_file, true);
+
+ if (dedup == DedupState::kDeduplicatedFirst) {
+ pretty_name = "[Dedup]" + pretty_name;
+ }
+
+ ElfSymtabBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr,
+ Elf32_Sym, Elf32_Shdr>* symtab = &builder_->symtab_builder_;
+
+ symtab->AddSymbol(pretty_name, &builder_->text_builder_, oat_method.GetCodeOffset() -
+ oat_file_->GetOatHeader().GetExecutableOffset(), true,
+ oat_method.GetQuickCodeSize(), STB_GLOBAL, STT_FUNC);
+ }
+ }
+
+ // Write oat code. Required by ElfBuilder/CodeOutput.
+ bool Write(OutputStream* out) {
+ return out->WriteFully(oat_file_->Begin(), oat_file_->End() - oat_file_->Begin());
+ }
+
+ private:
+ static void SkipAllFields(ClassDataItemIterator* it) {
+ while (it->HasNextStaticField()) {
+ it->Next();
+ }
+ while (it->HasNextInstanceField()) {
+ it->Next();
+ }
+ }
+
+ const OatFile* oat_file_;
+ std::unique_ptr<ElfBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+ Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr> > builder_;
+ File* elf_output_;
+ std::unordered_map<uint32_t, uint32_t> state_;
+ std::string output_name_;
+};
+
class OatDumper {
public:
explicit OatDumper(const OatFile& oat_file, bool dump_raw_mapping_table, bool dump_raw_gc_map)
@@ -165,7 +386,7 @@
GetQuickToInterpreterBridgeOffset);
#undef DUMP_OAT_HEADER_OFFSET
- os << "IMAGE PATCH DELTA:\n" << oat_header.GetImagePatchDelta();
+ os << "IMAGE PATCH DELTA:\n" << oat_header.GetImagePatchDelta() << "\n\n";
os << "IMAGE FILE LOCATION OAT CHECKSUM:\n";
os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatChecksum());
@@ -395,7 +616,11 @@
DumpSpillMask(*indent2_os, oat_method.GetFpSpillMask(), true);
*indent2_os << StringPrintf("\nvmap_table: %p (offset=0x%08x)\n",
oat_method.GetVmapTable(), oat_method.GetVmapTableOffset());
- DumpVmap(*indent2_os, oat_method);
+
+ if (oat_method.GetNativeGcMap() != nullptr) {
+ // The native GC map is null for methods compiled with the optimizing compiler.
+ DumpVmap(*indent2_os, oat_method);
+ }
DumpVregLocations(*indent2_os, oat_method, code_item);
*indent2_os << StringPrintf("mapping_table: %p (offset=0x%08x)\n",
oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
@@ -1537,8 +1762,10 @@
std::string elf_filename_prefix;
std::ostream* os = &std::cout;
std::unique_ptr<std::ofstream> out;
+ std::string output_name;
bool dump_raw_mapping_table = false;
bool dump_raw_gc_map = false;
+ bool symbolize = false;
for (int i = 0; i < argc; i++) {
const StringPiece option(argv[i]);
@@ -1571,13 +1798,17 @@
usage();
}
} else if (option.starts_with("--output=")) {
- const char* filename = option.substr(strlen("--output=")).data();
+ output_name = option.substr(strlen("--output=")).ToString();
+ const char* filename = output_name.c_str();
out.reset(new std::ofstream(filename));
if (!out->good()) {
fprintf(stderr, "Failed to open output filename %s\n", filename);
usage();
}
os = out.get();
+ } else if (option.starts_with("--symbolize=")) {
+ oat_filename = option.substr(strlen("--symbolize=")).data();
+ symbolize = true;
} else {
fprintf(stderr, "Unknown argument %s\n", option.data());
usage();
@@ -1602,8 +1833,20 @@
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
}
- OatDumper oat_dumper(*oat_file, dump_raw_mapping_table, dump_raw_gc_map);
- oat_dumper.Dump(*os);
+ if (symbolize) {
+ OatSymbolizer oat_symbolizer(oat_file, output_name);
+ if (!oat_symbolizer.Init()) {
+ fprintf(stderr, "Failed to initialize symbolizer\n");
+ return EXIT_FAILURE;
+ }
+ if (!oat_symbolizer.Symbolize()) {
+ fprintf(stderr, "Failed to symbolize\n");
+ return EXIT_FAILURE;
+ }
+ } else {
+ OatDumper oat_dumper(*oat_file, dump_raw_mapping_table, dump_raw_gc_map);
+ oat_dumper.Dump(*os);
+ }
return EXIT_SUCCESS;
}
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 337e5fe..4155b7e 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1255,6 +1255,7 @@
PUSH ecx // Pass receiver.
PUSH eax // Pass Method*.
call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR)
+ SETUP_GOT_NOSAVE
addl LITERAL(28), %esp // Pop arguments upto saved Method*.
movl 28(%esp), %edi // Restore edi.
movl %eax, 28(%esp) // Place code* over edi, just under return pc.
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 23caefc..f01ea0c 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -30,22 +30,24 @@
namespace art {
Mutex* Locks::abort_lock_ = nullptr;
+Mutex* Locks::alloc_tracker_lock_ = nullptr;
Mutex* Locks::allocated_monitor_ids_lock_ = nullptr;
Mutex* Locks::allocated_thread_ids_lock_ = nullptr;
ReaderWriterMutex* Locks::breakpoint_lock_ = nullptr;
ReaderWriterMutex* Locks::classlinker_classes_lock_ = nullptr;
+Mutex* Locks::deoptimization_lock_ = nullptr;
ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr;
Mutex* Locks::jni_libraries_lock_ = nullptr;
Mutex* Locks::logging_lock_ = nullptr;
Mutex* Locks::mem_maps_lock_ = nullptr;
Mutex* Locks::modify_ldt_lock_ = nullptr;
ReaderWriterMutex* Locks::mutator_lock_ = nullptr;
+Mutex* Locks::profiler_lock_ = nullptr;
Mutex* Locks::runtime_shutdown_lock_ = nullptr;
Mutex* Locks::thread_list_lock_ = nullptr;
Mutex* Locks::thread_list_suspend_thread_lock_ = nullptr;
Mutex* Locks::thread_suspend_count_lock_ = nullptr;
Mutex* Locks::trace_lock_ = nullptr;
-Mutex* Locks::profiler_lock_ = nullptr;
Mutex* Locks::unexpected_signal_lock_ = nullptr;
Mutex* Locks::intern_table_lock_ = nullptr;
@@ -830,21 +832,23 @@
DCHECK(modify_ldt_lock_ == nullptr);
}
DCHECK(abort_lock_ != nullptr);
+ DCHECK(alloc_tracker_lock_ != nullptr);
DCHECK(allocated_monitor_ids_lock_ != nullptr);
DCHECK(allocated_thread_ids_lock_ != nullptr);
DCHECK(breakpoint_lock_ != nullptr);
DCHECK(classlinker_classes_lock_ != nullptr);
+ DCHECK(deoptimization_lock_ != nullptr);
DCHECK(heap_bitmap_lock_ != nullptr);
+ DCHECK(intern_table_lock_ != nullptr);
DCHECK(jni_libraries_lock_ != nullptr);
DCHECK(logging_lock_ != nullptr);
DCHECK(mutator_lock_ != nullptr);
+ DCHECK(profiler_lock_ != nullptr);
DCHECK(thread_list_lock_ != nullptr);
DCHECK(thread_list_suspend_thread_lock_ != nullptr);
DCHECK(thread_suspend_count_lock_ != nullptr);
DCHECK(trace_lock_ != nullptr);
- DCHECK(profiler_lock_ != nullptr);
DCHECK(unexpected_signal_lock_ != nullptr);
- DCHECK(intern_table_lock_ != nullptr);
} else {
// Create global locks in level order from highest lock level to lowest.
LockLevel current_lock_level = kThreadListSuspendThreadLock;
@@ -853,7 +857,12 @@
new Mutex("thread list suspend thread by .. lock", current_lock_level);
#define UPDATE_CURRENT_LOCK_LEVEL(new_level) \
- DCHECK_LT(new_level, current_lock_level); \
+ if (new_level >= current_lock_level) { \
+ /* Do not use CHECKs or FATAL here, abort_lock_ is not setup yet. */ \
+ fprintf(stderr, "New local level %d is not less than current level %d\n", \
+ new_level, current_lock_level); \
+ exit(1); \
+ } \
current_lock_level = new_level;
UPDATE_CURRENT_LOCK_LEVEL(kMutatorLock);
@@ -876,6 +885,14 @@
DCHECK(trace_lock_ == nullptr);
trace_lock_ = new Mutex("trace lock", current_lock_level);
+ UPDATE_CURRENT_LOCK_LEVEL(kDeoptimizationLock);
+ DCHECK(deoptimization_lock_ == nullptr);
+ deoptimization_lock_ = new Mutex("Deoptimization lock", current_lock_level);
+
+ UPDATE_CURRENT_LOCK_LEVEL(kAllocTrackerLock);
+ DCHECK(alloc_tracker_lock_ == nullptr);
+ alloc_tracker_lock_ = new Mutex("AllocTracker lock", current_lock_level);
+
UPDATE_CURRENT_LOCK_LEVEL(kThreadListLock);
DCHECK(thread_list_lock_ == nullptr);
thread_list_lock_ = new Mutex("thread list lock", current_lock_level);
@@ -911,7 +928,6 @@
DCHECK(intern_table_lock_ == nullptr);
intern_table_lock_ = new Mutex("InternTable lock", current_lock_level);
-
UPDATE_CURRENT_LOCK_LEVEL(kAbortLock);
DCHECK(abort_lock_ == nullptr);
abort_lock_ = new Mutex("abort lock", current_lock_level, true);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 2a623fd..6642b1e 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -85,6 +85,7 @@
kJniLoadLibraryLock,
kThreadListLock,
kBreakpointInvokeLock,
+ kAllocTrackerLock,
kDeoptimizationLock,
kTraceLock,
kProfilerLock,
@@ -557,9 +558,17 @@
// Guards trace (ie traceview) requests.
static Mutex* trace_lock_ ACQUIRED_AFTER(profiler_lock_);
+ // Guards debugger recent allocation records.
+ static Mutex* alloc_tracker_lock_ ACQUIRED_AFTER(trace_lock_);
+
+ // Guards updates to instrumentation to ensure mutual exclusion of
+ // events like deoptimization requests.
+ // TODO: improve name, perhaps instrumentation_update_lock_.
+ static Mutex* deoptimization_lock_ ACQUIRED_AFTER(alloc_tracker_lock_);
+
// The thread_list_lock_ guards ThreadList::list_. It is also commonly held to stop threads
// attaching and detaching.
- static Mutex* thread_list_lock_ ACQUIRED_AFTER(trace_lock_);
+ static Mutex* thread_list_lock_ ACQUIRED_AFTER(deoptimization_lock_);
// Guards maintaining loading library data structures.
static Mutex* jni_libraries_lock_ ACQUIRED_AFTER(thread_list_lock_);
@@ -586,7 +595,7 @@
static Mutex* intern_table_lock_ ACQUIRED_AFTER(modify_ldt_lock_);
// Have an exclusive aborting thread.
- static Mutex* abort_lock_ ACQUIRED_AFTER(classlinker_classes_lock_);
+ static Mutex* abort_lock_ ACQUIRED_AFTER(intern_table_lock_);
// Allow mutual exclusion when manipulating Thread::suspend_count_.
// TODO: Does the trade-off of a per-thread lock make sense?
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index ad436d0..9c2cc6c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -785,7 +785,19 @@
return NULL;
}
-static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file, const char* dex_location,
+
+// Loads all multi dex files from the given oat file returning true on success.
+//
+// Parameters:
+// oat_file - the oat file to load from
+// dex_location - the dex location used to generate the oat file
+// dex_location_checksum - the checksum of the dex_location (may be null for pre-opted files)
+// generated - whether or not the oat_file existed before or was just (re)generated
+// error_msgs - any error messages will be appended here
+// dex_files - the loaded dex_files will be appended here (only if the loading succeeds)
+static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file,
+ const char* dex_location,
+ const uint32_t* dex_location_checksum,
bool generated,
std::vector<std::string>* error_msgs,
std::vector<const DexFile*>* dex_files) {
@@ -800,12 +812,20 @@
std::string next_name_str = DexFile::GetMultiDexClassesDexName(i, dex_location);
const char* next_name = next_name_str.c_str();
- uint32_t dex_location_checksum;
- uint32_t* dex_location_checksum_pointer = &dex_location_checksum;
+ uint32_t next_location_checksum;
+ uint32_t* next_location_checksum_pointer = &next_location_checksum;
std::string error_msg;
- if (!DexFile::GetChecksum(next_name, dex_location_checksum_pointer, &error_msg)) {
+ if ((i == 0) && (strcmp(next_name, dex_location) == 0)) {
+ // When i=0 the multidex name should be the same as the location name. We already have the
+ // checksum it so we don't need to recompute it.
+ if (dex_location_checksum == nullptr) {
+ next_location_checksum_pointer = nullptr;
+ } else {
+ next_location_checksum = *dex_location_checksum;
+ }
+ } else if (!DexFile::GetChecksum(next_name, next_location_checksum_pointer, &error_msg)) {
DCHECK_EQ(false, i == 0 && generated);
- dex_location_checksum_pointer = nullptr;
+ next_location_checksum_pointer = nullptr;
}
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(next_name, nullptr, false);
@@ -814,7 +834,7 @@
if (i == 0 && generated) {
std::string error_msg;
error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out "
- " file'%s'", dex_location, dex_location_checksum,
+ " file'%s'", dex_location, next_location_checksum,
oat_file->GetLocation().c_str());
error_msgs->push_back(error_msg);
}
@@ -823,8 +843,8 @@
// Checksum test. Test must succeed when generated.
success = !generated;
- if (dex_location_checksum_pointer != nullptr) {
- success = dex_location_checksum == oat_dex_file->GetDexFileLocationChecksum();
+ if (next_location_checksum_pointer != nullptr) {
+ success = next_location_checksum == oat_dex_file->GetDexFileLocationChecksum();
}
if (success) {
@@ -840,7 +860,7 @@
// When we generated the file, we expect success, or something is terribly wrong.
CHECK_EQ(false, generated && !success)
<< "dex_location=" << next_name << " oat_location=" << oat_file->GetLocation().c_str()
- << std::hex << " dex_location_checksum=" << dex_location_checksum
+ << std::hex << " dex_location_checksum=" << next_location_checksum
<< " OatDexFile::GetLocationChecksum()=" << oat_dex_file->GetDexFileLocationChecksum();
}
@@ -877,6 +897,7 @@
bool have_checksum = true;
std::string checksum_error_msg;
if (!DexFile::GetChecksum(dex_location, dex_location_checksum_pointer, &checksum_error_msg)) {
+ // This happens for pre-opted files since the corresponding dex files are no longer on disk.
dex_location_checksum_pointer = nullptr;
have_checksum = false;
}
@@ -942,8 +963,9 @@
// 3) If we have an oat file, check all contained multidex files for our dex_location.
// Note: LoadMultiDexFilesFromOatFile will check for nullptr in the first argument.
- bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, false, error_msgs,
- dex_files);
+ bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
+ dex_location_checksum_pointer,
+ false, error_msgs, dex_files);
if (success) {
const OatFile* oat_file = open_oat_file.release(); // Avoid deleting it.
if (needs_registering) {
@@ -1004,8 +1026,8 @@
}
// Try to load again, but stronger checks.
- success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, true, error_msgs,
- dex_files);
+ success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, dex_location_checksum_pointer,
+ true, error_msgs, dex_files);
if (success) {
RegisterOatFile(open_oat_file.release());
return true;
@@ -1189,24 +1211,18 @@
return false;
}
- if (dex_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) {
- *error_msg = StringPrintf("oat file '%s' mismatch (0x%x) with '%s' (0x%x)",
- oat_file->GetLocation().c_str(),
- oat_dex_file->GetDexFileLocationChecksum(),
- dex_location, dex_location_checksum);
- return false;
- }
+ DCHECK_EQ(dex_location_checksum, oat_dex_file->GetDexFileLocationChecksum());
return true;
}
bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file,
const char* dex_location,
+ const uint32_t* dex_location_checksum,
std::string* error_msg) {
CHECK(oat_file != nullptr);
CHECK(dex_location != nullptr);
std::unique_ptr<const DexFile> dex_file;
- uint32_t dex_location_checksum;
- if (!DexFile::GetChecksum(dex_location, &dex_location_checksum, error_msg)) {
+ if (dex_location_checksum == nullptr) {
// If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is
// up-to-date. This is the common case in user builds for jar's and apk's in the /system
// directory.
@@ -1219,13 +1235,13 @@
}
dex_file.reset(oat_dex_file->OpenDexFile(error_msg));
} else {
- bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, dex_location_checksum,
+ bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, *dex_location_checksum,
kRuntimeISA, error_msg);
if (!verified) {
return false;
}
dex_file.reset(oat_file->GetOatDexFile(dex_location,
- &dex_location_checksum)->OpenDexFile(error_msg));
+ dex_location_checksum)->OpenDexFile(error_msg));
}
return dex_file.get() != nullptr;
}
@@ -1249,7 +1265,8 @@
dex_location));
return nullptr;
} else if (oat_file->IsExecutable() &&
- !VerifyOatWithDexFile(oat_file.get(), dex_location, &error_msg)) {
+ !VerifyOatWithDexFile(oat_file.get(), dex_location,
+ dex_location_checksum, &error_msg)) {
error_msgs->push_back(StringPrintf("Failed to verify oat file '%s' found for dex location "
"'%s': %s", oat_file->GetLocation().c_str(), dex_location,
error_msg.c_str()));
@@ -2076,7 +2093,6 @@
// The boot class loader, search the boot class path.
ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
if (pair.second != nullptr) {
- StackHandleScope<1> hs(self);
return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
} else {
// The boot class loader is searched ahead of the application class loader, failures are
@@ -2115,6 +2131,87 @@
}
} else {
ScopedObjectAccessUnchecked soa(self);
+ if (class_loader->GetClass() ==
+ soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) &&
+ class_loader->GetParent()->GetClass() ==
+ soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) {
+ ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+ // Check if this would be found in the parent boot class loader.
+ if (pair.second != nullptr) {
+ mirror::Class* klass = LookupClass(descriptor, nullptr);
+ if (klass != nullptr) {
+ return klass;
+ }
+ klass = DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
+ *pair.second);
+ if (klass == nullptr) {
+ CHECK(self->IsExceptionPending()) << descriptor;
+ self->ClearException();
+ } else {
+ return klass;
+ }
+ } else {
+ // RegisterDexFile may allocate dex caches (and cause thread suspension).
+ StackHandleScope<3> hs(self);
+ // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
+ // We need to get the DexPathList and loop through it.
+ Handle<mirror::ArtField> cookie_field =
+ hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie));
+ Handle<mirror::ArtField> dex_file_field =
+ hs.NewHandle(
+ soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList$Element_dexFile));
+ mirror::Object* dex_path_list =
+ soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
+ GetObject(class_loader.Get());
+ if (dex_path_list != nullptr && dex_file_field.Get() != nullptr &&
+ cookie_field.Get() != nullptr) {
+ // DexPathList has an array dexElements of Elements[] which each contain a dex file.
+ mirror::Object* dex_elements_obj =
+ soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
+ GetObject(dex_path_list);
+ // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
+ // at the mCookie which is a DexFile vector.
+ if (dex_elements_obj != nullptr) {
+ Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
+ hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
+ for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
+ mirror::Object* element = dex_elements->GetWithoutChecks(i);
+ if (element == nullptr) {
+ // Should never happen, fall back to java code to throw a NPE.
+ break;
+ }
+ mirror::Object* dex_file = dex_file_field->GetObject(element);
+ if (dex_file != nullptr) {
+ const uint64_t cookie = cookie_field->GetLong(dex_file);
+ auto* dex_files =
+ reinterpret_cast<std::vector<const DexFile*>*>(static_cast<uintptr_t>(cookie));
+ if (dex_files == nullptr) {
+ // This should never happen so log a warning.
+ LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
+ break;
+ }
+ for (const DexFile* dex_file : *dex_files) {
+ const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
+ if (dex_class_def != nullptr) {
+ RegisterDexFile(*dex_file);
+ mirror::Class* klass =
+ DefineClass(descriptor, class_loader, *dex_file, *dex_class_def);
+ if (klass == nullptr) {
+ CHECK(self->IsExceptionPending()) << descriptor;
+ self->ClearException();
+ // Exit the loop to make the java code generate an exception.
+ i = dex_elements->GetLength();
+ break;
+ }
+ return klass;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
ScopedLocalRef<jobject> class_loader_object(soa.Env(),
soa.AddLocalReference<jobject>(class_loader.Get()));
std::string class_name_string(DescriptorToDot(descriptor));
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 7750c8e..d1f5aa0 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -613,9 +613,18 @@
bool* obsolete_file_cleanup_failed)
LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
- // verify an oat file with the given dex file. Will return false when the dex file could not be
- // verified. Will return true otherwise.
+ // Verifies:
+ // - that the oat file contains the dex file (with a matching checksum, which may be null if the
+ // file was pre-opted)
+ // - the checksums of the oat file (against the image space)
+ // - the checksum of the dex file against dex_location_checksum
+ // - that the dex file can be opened
+ // Returns true iff all verification succeed.
+ //
+ // The dex_location is the dex location as stored in the oat file header.
+ // (see DexFile::GetDexCanonicalLocation for a description of location conventions)
bool VerifyOatWithDexFile(const OatFile* oat_file, const char* dex_location,
+ const uint32_t* dex_location_checksum,
std::string* error_msg);
mirror::ArtMethod* CreateProxyConstructor(Thread* self, ConstHandle<mirror::Class> klass,
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index b3c887e..488e6e7 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -61,7 +61,15 @@
namespace art {
static const size_t kMaxAllocRecordStackDepth = 16; // Max 255.
-static const size_t kDefaultNumAllocRecords = 64*1024; // Must be a power of 2.
+static const size_t kDefaultNumAllocRecords = 64*1024; // Must be a power of 2. 2BE can hold 64k-1.
+
+// Limit alloc_record_count to the 2BE value that is the limit of the current protocol.
+static uint16_t CappedAllocRecordCount(size_t alloc_record_count) {
+ if (alloc_record_count > 0xffff) {
+ return 0xffff;
+ }
+ return alloc_record_count;
+}
class AllocRecordStackTraceElement {
public:
@@ -116,9 +124,10 @@
}
void Dbg::TypeCache::Clear() {
- ScopedObjectAccess soa(Thread::Current());
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ Thread* self = Thread::Current();
for (const auto& p : objects_) {
- soa.Vm()->DeleteWeakGlobalRef(soa.Self(), p.second);
+ vm->DeleteWeakGlobalRef(self, p.second);
}
objects_.clear();
}
@@ -131,8 +140,9 @@
return down_cast<mirror::Class*>(Thread::Current()->DecodeJObject(type_));
}
- void SetType(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- type_ = Dbg::GetTypeCache()->Add(t);
+ void SetType(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_,
+ Locks::alloc_tracker_lock_) {
+ type_ = Dbg::type_cache_.Add(t);
}
size_t GetDepth() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -304,7 +314,6 @@
static ObjectRegistry* gRegistry = nullptr;
// Recent allocation tracking.
-Mutex* Dbg::alloc_tracker_lock_ = nullptr;
AllocRecord* Dbg::recent_allocation_records_ = nullptr; // TODO: CircularBuffer<AllocRecord>
size_t Dbg::alloc_record_max_ = 0;
size_t Dbg::alloc_record_head_ = 0;
@@ -312,7 +321,6 @@
Dbg::TypeCache Dbg::type_cache_;
// Deoptimization support.
-Mutex* Dbg::deoptimization_lock_ = nullptr;
std::vector<DeoptimizationRequest> Dbg::deoptimization_requests_;
size_t Dbg::full_deoptimization_event_count_ = 0;
size_t Dbg::delayed_full_undeoptimization_count_ = 0;
@@ -642,8 +650,6 @@
CHECK(gRegistry == nullptr);
gRegistry = new ObjectRegistry;
- alloc_tracker_lock_ = new Mutex("AllocTracker lock");
- deoptimization_lock_ = new Mutex("deoptimization lock", kDeoptimizationLock);
// Init JDWP if the debugger is enabled. This may connect out to a
// debugger, passively listen for a debugger, or block waiting for a
// debugger.
@@ -677,10 +683,6 @@
gJdwpState = nullptr;
delete gRegistry;
gRegistry = nullptr;
- delete alloc_tracker_lock_;
- alloc_tracker_lock_ = nullptr;
- delete deoptimization_lock_;
- deoptimization_lock_ = nullptr;
}
void Dbg::GcDidFinish() {
@@ -747,7 +749,7 @@
}
{
- MutexLock mu(Thread::Current(), *deoptimization_lock_);
+ MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
CHECK_EQ(deoptimization_requests_.size(), 0U);
CHECK_EQ(full_deoptimization_event_count_, 0U);
CHECK_EQ(delayed_full_undeoptimization_count_, 0U);
@@ -792,7 +794,7 @@
// Since we're going to disable deoptimization, we clear the deoptimization requests queue.
// This prevents us from having any pending deoptimization request when the debugger attaches
// to us again while no event has been requested yet.
- MutexLock mu(Thread::Current(), *deoptimization_lock_);
+ MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
deoptimization_requests_.clear();
full_deoptimization_event_count_ = 0U;
delayed_full_undeoptimization_count_ = 0U;
@@ -2922,7 +2924,7 @@
}
void Dbg::DelayFullUndeoptimization() {
- MutexLock mu(Thread::Current(), *deoptimization_lock_);
+ MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
++delayed_full_undeoptimization_count_;
DCHECK_LE(delayed_full_undeoptimization_count_, full_deoptimization_event_count_);
}
@@ -2930,7 +2932,7 @@
void Dbg::ProcessDelayedFullUndeoptimizations() {
// TODO: avoid taking the lock twice (once here and once in ManageDeoptimization).
{
- MutexLock mu(Thread::Current(), *deoptimization_lock_);
+ MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
while (delayed_full_undeoptimization_count_ > 0) {
DeoptimizationRequest req;
req.SetKind(DeoptimizationRequest::kFullUndeoptimization);
@@ -2947,7 +2949,7 @@
// Nothing to do.
return;
}
- MutexLock mu(Thread::Current(), *deoptimization_lock_);
+ MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
RequestDeoptimizationLocked(req);
}
@@ -3025,7 +3027,7 @@
Thread* const self = Thread::Current();
{
// Avoid suspend/resume if there is no pending request.
- MutexLock mu(self, *deoptimization_lock_);
+ MutexLock mu(self, *Locks::deoptimization_lock_);
if (deoptimization_requests_.empty()) {
return;
}
@@ -3037,7 +3039,7 @@
runtime->GetThreadList()->SuspendAll();
const ThreadState old_state = self->SetStateUnsafe(kRunnable);
{
- MutexLock mu(self, *deoptimization_lock_);
+ MutexLock mu(self, *Locks::deoptimization_lock_);
size_t req_index = 0;
for (DeoptimizationRequest& request : deoptimization_requests_) {
VLOG(jdwp) << "Process deoptimization request #" << req_index++;
@@ -4318,30 +4320,40 @@
return kDefaultNumAllocRecords;
}
-void Dbg::SetAllocTrackingEnabled(bool enabled) {
- if (enabled) {
+void Dbg::SetAllocTrackingEnabled(bool enable) {
+ Thread* self = Thread::Current();
+ if (enable) {
{
- MutexLock mu(Thread::Current(), *alloc_tracker_lock_);
- if (recent_allocation_records_ == nullptr) {
- alloc_record_max_ = GetAllocTrackerMax();
- LOG(INFO) << "Enabling alloc tracker (" << alloc_record_max_ << " entries of "
- << kMaxAllocRecordStackDepth << " frames, taking "
- << PrettySize(sizeof(AllocRecord) * alloc_record_max_) << ")";
- alloc_record_head_ = alloc_record_count_ = 0;
- recent_allocation_records_ = new AllocRecord[alloc_record_max_];
- CHECK(recent_allocation_records_ != nullptr);
+ MutexLock mu(self, *Locks::alloc_tracker_lock_);
+ if (recent_allocation_records_ != nullptr) {
+ return; // Already enabled, bail.
}
+ alloc_record_max_ = GetAllocTrackerMax();
+ LOG(INFO) << "Enabling alloc tracker (" << alloc_record_max_ << " entries of "
+ << kMaxAllocRecordStackDepth << " frames, taking "
+ << PrettySize(sizeof(AllocRecord) * alloc_record_max_) << ")";
+ DCHECK_EQ(alloc_record_head_, 0U);
+ DCHECK_EQ(alloc_record_count_, 0U);
+ recent_allocation_records_ = new AllocRecord[alloc_record_max_];
+ CHECK(recent_allocation_records_ != nullptr);
}
Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
} else {
- Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
{
- MutexLock mu(Thread::Current(), *alloc_tracker_lock_);
+ ScopedObjectAccess soa(self); // For type_cache_.Clear();
+ MutexLock mu(self, *Locks::alloc_tracker_lock_);
+ if (recent_allocation_records_ == nullptr) {
+ return; // Already disabled, bail.
+ }
LOG(INFO) << "Disabling alloc tracker";
delete[] recent_allocation_records_;
recent_allocation_records_ = nullptr;
+ alloc_record_head_ = 0;
+ alloc_record_count_ = 0;
type_cache_.Clear();
}
+ // If an allocation comes in before we uninstrument, we will safely drop it on the floor.
+ Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
}
}
@@ -4381,8 +4393,9 @@
Thread* self = Thread::Current();
CHECK(self != nullptr);
- MutexLock mu(self, *alloc_tracker_lock_);
+ MutexLock mu(self, *Locks::alloc_tracker_lock_);
if (recent_allocation_records_ == nullptr) {
+ // In the process of shutting down recording, bail.
return;
}
@@ -4408,12 +4421,12 @@
// Returns the index of the head element.
//
-// We point at the most-recently-written record, so if gAllocRecordCount is 1
+// We point at the most-recently-written record, so if alloc_record_count_ is 1
// we want to use the current element. Take "head+1" and subtract count
// from it.
//
// We need to handle underflow in our circular buffer, so we add
-// gAllocRecordMax and then mask it back down.
+// alloc_record_max_ and then mask it back down.
size_t Dbg::HeadIndex() {
return (Dbg::alloc_record_head_ + 1 + Dbg::alloc_record_max_ - Dbg::alloc_record_count_) &
(Dbg::alloc_record_max_ - 1);
@@ -4421,7 +4434,7 @@
void Dbg::DumpRecentAllocations() {
ScopedObjectAccess soa(Thread::Current());
- MutexLock mu(soa.Self(), *alloc_tracker_lock_);
+ MutexLock mu(soa.Self(), *Locks::alloc_tracker_lock_);
if (recent_allocation_records_ == nullptr) {
LOG(INFO) << "Not recording tracked allocations";
return;
@@ -4430,7 +4443,8 @@
// "i" is the head of the list. We want to start at the end of the
// list and move forward to the tail.
size_t i = HeadIndex();
- size_t count = alloc_record_count_;
+ const uint16_t capped_count = CappedAllocRecordCount(Dbg::alloc_record_count_);
+ uint16_t count = capped_count;
LOG(INFO) << "Tracked allocations, (head=" << alloc_record_head_ << " count=" << count << ")";
while (count--) {
@@ -4534,7 +4548,7 @@
* followed by UTF-16 data.
*
* We send up 16-bit unsigned indexes into string tables. In theory there
- * can be (kMaxAllocRecordStackDepth * gAllocRecordMax) unique strings in
+ * can be (kMaxAllocRecordStackDepth * alloc_record_max_) unique strings in
* each table, but in practice there should be far fewer.
*
* The chief reason for using a string table here is to keep the size of
@@ -4554,7 +4568,7 @@
Thread* self = Thread::Current();
std::vector<uint8_t> bytes;
{
- MutexLock mu(self, *alloc_tracker_lock_);
+ MutexLock mu(self, *Locks::alloc_tracker_lock_);
//
// Part 1: generate string tables.
//
@@ -4562,8 +4576,9 @@
StringTable method_names;
StringTable filenames;
- int count = alloc_record_count_;
- int idx = HeadIndex();
+ const uint16_t capped_count = CappedAllocRecordCount(Dbg::alloc_record_count_);
+ uint16_t count = capped_count;
+ size_t idx = HeadIndex();
while (count--) {
AllocRecord* record = &recent_allocation_records_[idx];
std::string temp;
@@ -4580,7 +4595,7 @@
idx = (idx + 1) & (alloc_record_max_ - 1);
}
- LOG(INFO) << "allocation records: " << alloc_record_count_;
+ LOG(INFO) << "allocation records: " << capped_count;
//
// Part 2: Generate the output and store it in the buffer.
@@ -4601,7 +4616,7 @@
// (2b) number of class name strings
// (2b) number of method name strings
// (2b) number of source file name strings
- JDWP::Append2BE(bytes, alloc_record_count_);
+ JDWP::Append2BE(bytes, capped_count);
size_t string_table_offset = bytes.size();
JDWP::Append4BE(bytes, 0); // We'll patch this later...
JDWP::Append2BE(bytes, class_names.Size());
@@ -4610,7 +4625,7 @@
idx = HeadIndex();
std::string temp;
- for (count = alloc_record_count_; count != 0; --count) {
+ for (count = capped_count; count != 0; --count) {
// For each entry:
// (4b) total allocation size
// (2b) thread id
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 52ae7a9..3e16288 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -192,9 +192,11 @@
class TypeCache {
public:
// Returns a weak global for the input type. Deduplicates.
- jobject Add(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ jobject Add(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_,
+ Locks::alloc_tracker_lock_);
// Clears the type cache and deletes all the weak global refs.
- void Clear();
+ void Clear() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_,
+ Locks::alloc_tracker_lock_);
private:
std::multimap<int32_t, jobject> objects_;
@@ -221,8 +223,8 @@
*/
static void Connected();
static void GoActive()
- LOCKS_EXCLUDED(Locks::breakpoint_lock_, deoptimization_lock_, Locks::mutator_lock_);
- static void Disconnected() LOCKS_EXCLUDED(deoptimization_lock_, Locks::mutator_lock_);
+ LOCKS_EXCLUDED(Locks::breakpoint_lock_, Locks::deoptimization_lock_, Locks::mutator_lock_);
+ static void Disconnected() LOCKS_EXCLUDED(Locks::deoptimization_lock_, Locks::mutator_lock_);
static void Disposed();
// Returns true if we're actually debugging with a real debugger, false if it's
@@ -493,20 +495,20 @@
// Records deoptimization request in the queue.
static void RequestDeoptimization(const DeoptimizationRequest& req)
- LOCKS_EXCLUDED(deoptimization_lock_)
+ LOCKS_EXCLUDED(Locks::deoptimization_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Support delayed full undeoptimization requests. This is currently only used for single-step
// events.
- static void DelayFullUndeoptimization() LOCKS_EXCLUDED(deoptimization_lock_);
+ static void DelayFullUndeoptimization() LOCKS_EXCLUDED(Locks::deoptimization_lock_);
static void ProcessDelayedFullUndeoptimizations()
- LOCKS_EXCLUDED(deoptimization_lock_)
+ LOCKS_EXCLUDED(Locks::deoptimization_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Manage deoptimization after updating JDWP events list. Suspends all threads, processes each
// request and finally resumes all threads.
static void ManageDeoptimization()
- LOCKS_EXCLUDED(deoptimization_lock_)
+ LOCKS_EXCLUDED(Locks::deoptimization_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Breakpoints.
@@ -560,17 +562,17 @@
* Recent allocation tracking support.
*/
static void RecordAllocation(mirror::Class* type, size_t byte_count)
- LOCKS_EXCLUDED(alloc_tracker_lock_)
+ LOCKS_EXCLUDED(Locks::alloc_tracker_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void SetAllocTrackingEnabled(bool enabled) LOCKS_EXCLUDED(alloc_tracker_lock_);
+ static void SetAllocTrackingEnabled(bool enabled) LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
static bool IsAllocTrackingEnabled() {
return recent_allocation_records_ != nullptr;
}
static jbyteArray GetRecentAllocations()
- LOCKS_EXCLUDED(alloc_tracker_lock_)
+ LOCKS_EXCLUDED(Locks::alloc_tracker_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static size_t HeadIndex() EXCLUSIVE_LOCKS_REQUIRED(alloc_tracker_lock_);
- static void DumpRecentAllocations() LOCKS_EXCLUDED(alloc_tracker_lock_);
+ static size_t HeadIndex() EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
+ static void DumpRecentAllocations() LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
enum HpifWhen {
HPIF_WHEN_NEVER = 0,
@@ -596,10 +598,6 @@
static void DdmSendHeapSegments(bool native)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static TypeCache* GetTypeCache() {
- return &type_cache_;
- }
-
private:
static void DdmBroadcast(bool connect) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void PostThreadStartOrStop(Thread*, uint32_t)
@@ -617,52 +615,47 @@
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
static void RequestDeoptimizationLocked(const DeoptimizationRequest& req)
- EXCLUSIVE_LOCKS_REQUIRED(deoptimization_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::deoptimization_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static Mutex* alloc_tracker_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-
- static AllocRecord* recent_allocation_records_ PT_GUARDED_BY(alloc_tracker_lock_);
- static size_t alloc_record_max_ GUARDED_BY(alloc_tracker_lock_);
- static size_t alloc_record_head_ GUARDED_BY(alloc_tracker_lock_);
- static size_t alloc_record_count_ GUARDED_BY(alloc_tracker_lock_);
-
- // Guards deoptimization requests.
- // TODO rename to instrumentation_update_lock.
- static Mutex* deoptimization_lock_ ACQUIRED_AFTER(Locks::breakpoint_lock_);
+ static AllocRecord* recent_allocation_records_ PT_GUARDED_BY(Locks::alloc_tracker_lock_);
+ static size_t alloc_record_max_ GUARDED_BY(Locks::alloc_tracker_lock_);
+ static size_t alloc_record_head_ GUARDED_BY(Locks::alloc_tracker_lock_);
+ static size_t alloc_record_count_ GUARDED_BY(Locks::alloc_tracker_lock_);
// Deoptimization requests to be processed each time the event list is updated. This is used when
// registering and unregistering events so we do not deoptimize while holding the event list
// lock.
// TODO rename to instrumentation_requests.
- static std::vector<DeoptimizationRequest> deoptimization_requests_ GUARDED_BY(deoptimization_lock_);
+ static std::vector<DeoptimizationRequest> deoptimization_requests_ GUARDED_BY(Locks::deoptimization_lock_);
// Count the number of events requiring full deoptimization. When the counter is > 0, everything
// is deoptimized, otherwise everything is undeoptimized.
// Note: we fully deoptimize on the first event only (when the counter is set to 1). We fully
// undeoptimize when the last event is unregistered (when the counter is set to 0).
- static size_t full_deoptimization_event_count_ GUARDED_BY(deoptimization_lock_);
+ static size_t full_deoptimization_event_count_ GUARDED_BY(Locks::deoptimization_lock_);
// Count the number of full undeoptimization requests delayed to next resume or end of debug
// session.
- static size_t delayed_full_undeoptimization_count_ GUARDED_BY(deoptimization_lock_);
+ static size_t delayed_full_undeoptimization_count_ GUARDED_BY(Locks::deoptimization_lock_);
static size_t* GetReferenceCounterForEvent(uint32_t instrumentation_event);
// Weak global type cache, TODO improve this.
- static TypeCache type_cache_;
+ static TypeCache type_cache_ GUARDED_BY(Locks::alloc_tracker_lock_);
// Instrumentation event reference counters.
// TODO we could use an array instead of having all these dedicated counters. Instrumentation
// events are bits of a mask so we could convert them to array index.
- static size_t dex_pc_change_event_ref_count_ GUARDED_BY(deoptimization_lock_);
- static size_t method_enter_event_ref_count_ GUARDED_BY(deoptimization_lock_);
- static size_t method_exit_event_ref_count_ GUARDED_BY(deoptimization_lock_);
- static size_t field_read_event_ref_count_ GUARDED_BY(deoptimization_lock_);
- static size_t field_write_event_ref_count_ GUARDED_BY(deoptimization_lock_);
- static size_t exception_catch_event_ref_count_ GUARDED_BY(deoptimization_lock_);
+ static size_t dex_pc_change_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+ static size_t method_enter_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+ static size_t method_exit_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+ static size_t field_read_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+ static size_t field_write_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
+ static size_t exception_catch_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
static uint32_t instrumentation_events_ GUARDED_BY(Locks::mutator_lock_);
+ friend class AllocRecord; // For type_cache_ with proper annotalysis.
DISALLOW_COPY_AND_ASSIGN(Dbg);
};
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 494781a..2048160 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -120,7 +120,7 @@
desired_collector_type_(foreground_collector_type_),
heap_trim_request_lock_(nullptr),
last_trim_time_(0),
- last_heap_transition_time_(0),
+ heap_transition_or_trim_target_time_(0),
heap_trim_request_pending_(false),
parallel_gc_threads_(parallel_gc_threads),
conc_gc_threads_(conc_gc_threads),
@@ -916,6 +916,35 @@
}
void Heap::DoPendingTransitionOrTrim() {
+ Thread* self = Thread::Current();
+ CollectorType desired_collector_type;
+ // Wait until we reach the desired transition time.
+ while (true) {
+ uint64_t wait_time;
+ {
+ MutexLock mu(self, *heap_trim_request_lock_);
+ desired_collector_type = desired_collector_type_;
+ uint64_t current_time = NanoTime();
+ if (current_time >= heap_transition_or_trim_target_time_) {
+ break;
+ }
+ wait_time = heap_transition_or_trim_target_time_ - current_time;
+ }
+ ScopedThreadStateChange tsc(self, kSleeping);
+ usleep(wait_time / 1000); // Usleep takes microseconds.
+ }
+ // Launch homogeneous space compaction if it is desired.
+ if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) {
+ if (!CareAboutPauseTimes()) {
+ PerformHomogeneousSpaceCompact();
+ }
+ // No need to Trim(). Homogeneous space compaction may free more virtual and physical memory.
+ desired_collector_type = collector_type_;
+ return;
+ }
+ // Transition the collector if the desired collector type is not the same as the current
+ // collector type.
+ TransitionCollector(desired_collector_type);
if (!CareAboutPauseTimes()) {
// Deflate the monitors, this can cause a pause but shouldn't matter since we don't care
// about pauses.
@@ -927,23 +956,7 @@
<< PrettyDuration(NanoTime() - start_time);
runtime->GetThreadList()->ResumeAll();
}
- if (NanoTime() - last_heap_transition_time_ > kCollectorTransitionWait) {
- // Launch homogeneous space compaction if it is desired.
- if (desired_collector_type_ == kCollectorTypeHomogeneousSpaceCompact) {
- if (!CareAboutPauseTimes()) {
- PerformHomogeneousSpaceCompact();
- last_heap_transition_time_ = NanoTime();
- }
- desired_collector_type_ = collector_type_;
- } else {
- // Transition the collector if the desired collector type is not the same as the current
- // collector type.
- TransitionCollector(desired_collector_type_);
- last_heap_transition_time_ = NanoTime();
- }
- }
- // Do a heap trim if it is needed. This is good to do even with hspace compaction since it may
- // trim the native heap and dlmalloc spaces.
+ // Do a heap trim if it is needed.
Trim();
}
@@ -2977,6 +2990,8 @@
if (desired_collector_type_ == desired_collector_type) {
return;
}
+ heap_transition_or_trim_target_time_ =
+ std::max(heap_transition_or_trim_target_time_, NanoTime() + delta_time);
desired_collector_type_ = desired_collector_type;
}
SignalHeapTrimDaemon(self);
@@ -2998,7 +3013,10 @@
Thread* self = Thread::Current();
Runtime* runtime = Runtime::Current();
- if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self)) {
+ if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self) ||
+ runtime->IsZygote()) {
+ // Ignore the request if we are the zygote to prevent app launching lag due to sleep in heap
+ // trimmer daemon. b/17310019
// Heap trimming isn't supported without a Java runtime or Daemons (such as at dex2oat time)
// Also: we do not wish to start a heap trim if the runtime is shutting down (a racy check
// as we don't hold the lock while requesting the trim).
@@ -3012,6 +3030,10 @@
return;
}
heap_trim_request_pending_ = true;
+ uint64_t current_time = NanoTime();
+ if (heap_transition_or_trim_target_time_ < current_time) {
+ heap_transition_or_trim_target_time_ = current_time + kHeapTrimWait;
+ }
}
// Notify the daemon thread which will actually do the heap trim.
SignalHeapTrimDaemon(self);
@@ -3064,8 +3086,6 @@
}
env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
WellKnownClasses::java_lang_System_runFinalization);
- env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
- WellKnownClasses::java_lang_System_runFinalization);
}
void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 3bfa748..9742277 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -830,8 +830,8 @@
Mutex* heap_trim_request_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
// When we want to perform the next heap trim (nano seconds).
uint64_t last_trim_time_ GUARDED_BY(heap_trim_request_lock_);
- // When we last performed a heap transition or hspace compact.
- uint64_t last_heap_transition_time_;
+ // When we want to perform the next heap transition (nano seconds) or heap trim.
+ uint64_t heap_transition_or_trim_target_time_ GUARDED_BY(heap_trim_request_lock_);
// If we have a heap trim request pending.
bool heap_trim_request_pending_ GUARDED_BY(heap_trim_request_lock_);
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index c50c703..ae17070 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -271,6 +271,9 @@
}
inline const uint8_t* ArtMethod::GetVmapTable(const void* code_pointer) {
+ if (IsOptimized()) {
+ LOG(FATAL) << "Unimplemented vmap table for optimized compiler";
+ }
DCHECK(code_pointer != nullptr);
DCHECK(code_pointer == GetQuickOatCodePointer());
uint32_t offset =
@@ -281,6 +284,17 @@
return reinterpret_cast<const uint8_t*>(code_pointer) - offset;
}
+inline StackMap ArtMethod::GetStackMap(uint32_t native_pc_offset) {
+ DCHECK(IsOptimized());
+ const void* code_pointer = GetQuickOatCodePointer();
+ DCHECK(code_pointer != nullptr);
+ uint32_t offset =
+ reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].vmap_table_offset_;
+ const void* data = reinterpret_cast<const void*>(reinterpret_cast<const uint8_t*>(code_pointer) - offset);
+ CodeInfo code_info(data);
+ return code_info.GetStackMapForNativePcOffset(native_pc_offset);
+}
+
inline void ArtMethod::SetOatNativeGcMapOffset(uint32_t gc_map_offset) {
DCHECK(!Runtime::Current()->IsStarted());
SetNativeGcMap(reinterpret_cast<uint8_t*>(gc_map_offset));
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index ebd5bd5..d37aa57 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -25,6 +25,7 @@
#include "object_callbacks.h"
#include "quick/quick_method_frame_info.h"
#include "read_barrier_option.h"
+#include "stack_map.h"
namespace art {
@@ -150,6 +151,13 @@
SetAccessFlags(GetAccessFlags() | kAccPreverified);
}
+ bool IsOptimized() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Temporary solution for detecting if a method has been optimized: the compiler
+ // does not create a GC map. Instead, the vmap table contains the stack map
+ // (as in stack_map.h).
+ return (GetEntryPointFromQuickCompiledCode() != nullptr) && (GetNativeGcMap() == nullptr);
+ }
+
bool IsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccPortableCompiled) != 0;
}
@@ -340,6 +348,8 @@
const uint8_t* GetVmapTable(const void* code_pointer)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ StackMap GetStackMap(uint32_t native_pc_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const uint8_t* GetNativeGcMap() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_));
}
diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h
index f3594e4..ff2ad89 100644
--- a/runtime/mirror/class_loader.h
+++ b/runtime/mirror/class_loader.h
@@ -32,6 +32,9 @@
static constexpr uint32_t InstanceSize() {
return sizeof(ClassLoader);
}
+ ClassLoader* GetParent() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetFieldObject<ClassLoader>(OFFSET_OF_OBJECT_MEMBER(ClassLoader, parent_));
+ }
private:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 2d0060e..b8b10d2 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -115,18 +115,22 @@
mirror::Object* StackVisitor::GetThisObject() const {
mirror::ArtMethod* m = GetMethod();
if (m->IsStatic()) {
- return NULL;
+ return nullptr;
} else if (m->IsNative()) {
- if (cur_quick_frame_ != NULL) {
+ if (cur_quick_frame_ != nullptr) {
HandleScope* hs = reinterpret_cast<HandleScope*>(
reinterpret_cast<char*>(cur_quick_frame_) + m->GetHandleScopeOffsetInBytes());
return hs->GetReference(0);
} else {
return cur_shadow_frame_->GetVRegReference(0);
}
+ } else if (m->IsOptimized()) {
+ // TODO: Implement, currently only used for exceptions when jdwp is enabled.
+ LOG(WARNING) << "StackVisitor::GetThisObject is unimplemented with the optimizing compiler";
+ return nullptr;
} else {
const DexFile::CodeItem* code_item = m->GetCodeItem();
- if (code_item == NULL) {
+ if (code_item == nullptr) {
UNIMPLEMENTED(ERROR) << "Failed to determine this object of abstract or proxy method: "
<< PrettyMethod(m);
return nullptr;
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 7d3a48f..9b49d31 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -64,9 +64,9 @@
MemoryRegion region_;
- template<typename T> friend class CodeInfo;
- template<typename T> friend class StackMap;
- template<typename T> friend class StackMapStream;
+ friend class CodeInfo;
+ friend class StackMap;
+ friend class StackMapStream;
};
/**
@@ -77,13 +77,15 @@
* The location_kind for a Dex register can either be:
* - Constant: register_value holds the constant,
* - Stack: register_value holds the stack offset,
- * - Register: register_value holds the register number.
+ * - Register: register_value holds the physical register number.
+ * - None: the register has no location yet, meaning it has not been set.
*/
class DexRegisterMap {
public:
explicit DexRegisterMap(MemoryRegion region) : region_(region) {}
enum LocationKind {
+ kNone,
kInStack,
kInRegister,
kConstant
@@ -114,8 +116,8 @@
MemoryRegion region_;
- template <typename T> friend class CodeInfo;
- template <typename T> friend class StackMapStream;
+ friend class CodeInfo;
+ friend class StackMapStream;
};
/**
@@ -127,12 +129,11 @@
* - Knowing the values of dex registers.
*
* The information is of the form:
- * [dex_pc, native_pc, dex_register_map_offset, inlining_info_offset, register_mask, stack_mask].
+ * [dex_pc, native_pc_offset, dex_register_map_offset, inlining_info_offset, register_mask, stack_mask].
*
* Note that register_mask is fixed size, but stack_mask is variable size, depending on the
* stack size of a method.
*/
-template <typename T>
class StackMap {
public:
explicit StackMap(MemoryRegion region) : region_(region) {}
@@ -145,12 +146,12 @@
region_.Store<uint32_t>(kDexPcOffset, dex_pc);
}
- T GetNativePc() const {
- return region_.Load<T>(kNativePcOffset);
+ uint32_t GetNativePcOffset() const {
+ return region_.Load<uint32_t>(kNativePcOffsetOffset);
}
- void SetNativePc(T native_pc) {
- return region_.Store<T>(kNativePcOffset, native_pc);
+ void SetNativePcOffset(uint32_t native_pc_offset) {
+ return region_.Store<uint32_t>(kNativePcOffsetOffset, native_pc_offset);
}
uint32_t GetDexRegisterMapOffset() const {
@@ -199,8 +200,8 @@
private:
static constexpr int kDexPcOffset = 0;
- static constexpr int kNativePcOffset = kDexPcOffset + sizeof(uint32_t);
- static constexpr int kDexRegisterMapOffsetOffset = kNativePcOffset + sizeof(T);
+ static constexpr int kNativePcOffsetOffset = kDexPcOffset + sizeof(uint32_t);
+ static constexpr int kDexRegisterMapOffsetOffset = kNativePcOffsetOffset + sizeof(uint32_t);
static constexpr int kInlineDescriptorOffsetOffset =
kDexRegisterMapOffsetOffset + sizeof(uint32_t);
static constexpr int kRegisterMaskOffset = kInlineDescriptorOffsetOffset + sizeof(uint32_t);
@@ -211,24 +212,36 @@
MemoryRegion region_;
- template <typename U> friend class CodeInfo;
- template <typename U> friend class StackMapStream;
+ friend class CodeInfo;
+ friend class StackMapStream;
};
/**
* Wrapper around all compiler information collected for a method.
* The information is of the form:
- * [number_of_stack_maps, stack_mask_size, StackMap+, DexRegisterInfo+, InlineInfo*].
+ * [overall_size, number_of_stack_maps, stack_mask_size, StackMap+, DexRegisterInfo+, InlineInfo*].
*/
-template <typename T>
class CodeInfo {
public:
explicit CodeInfo(MemoryRegion region) : region_(region) {}
- StackMap<T> GetStackMapAt(size_t i) const {
+ explicit CodeInfo(const void* data) {
+ uint32_t size = reinterpret_cast<const uint32_t*>(data)[0];
+ region_ = MemoryRegion(const_cast<void*>(data), size);
+ }
+
+ StackMap GetStackMapAt(size_t i) const {
size_t size = StackMapSize();
- return StackMap<T>(GetStackMaps().Subregion(i * size, size));
+ return StackMap(GetStackMaps().Subregion(i * size, size));
+ }
+
+ uint32_t GetOverallSize() const {
+ return region_.Load<uint32_t>(kOverallSizeOffset);
+ }
+
+ void SetOverallSize(uint32_t size) {
+ region_.Store<uint32_t>(kOverallSizeOffset, size);
}
uint32_t GetStackMaskSize() const {
@@ -248,47 +261,48 @@
}
size_t StackMapSize() const {
- return StackMap<T>::kFixedSize + GetStackMaskSize();
+ return StackMap::kFixedSize + GetStackMaskSize();
}
- DexRegisterMap GetDexRegisterMapOf(StackMap<T> stack_map, uint32_t number_of_dex_registers) {
+ DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, uint32_t number_of_dex_registers) {
uint32_t offset = stack_map.GetDexRegisterMapOffset();
return DexRegisterMap(region_.Subregion(offset,
DexRegisterMap::kFixedSize + number_of_dex_registers * DexRegisterMap::SingleEntrySize()));
}
- InlineInfo GetInlineInfoOf(StackMap<T> stack_map) {
+ InlineInfo GetInlineInfoOf(StackMap stack_map) {
uint32_t offset = stack_map.GetInlineDescriptorOffset();
uint8_t depth = region_.Load<uint8_t>(offset);
return InlineInfo(region_.Subregion(offset,
InlineInfo::kFixedSize + depth * InlineInfo::SingleEntrySize()));
}
- StackMap<T> GetStackMapForDexPc(uint32_t dex_pc) {
+ StackMap GetStackMapForDexPc(uint32_t dex_pc) {
for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
- StackMap<T> stack_map = GetStackMapAt(i);
+ StackMap stack_map = GetStackMapAt(i);
if (stack_map.GetDexPc() == dex_pc) {
return stack_map;
}
}
LOG(FATAL) << "Unreachable";
- return StackMap<T>(MemoryRegion());
+ return StackMap(MemoryRegion());
}
- StackMap<T> GetStackMapForNativePc(T native_pc) {
+ StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset) {
// TODO: stack maps are sorted by native pc, we can do a binary search.
for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
- StackMap<T> stack_map = GetStackMapAt(i);
- if (stack_map.GetNativePc() == native_pc) {
+ StackMap stack_map = GetStackMapAt(i);
+ if (stack_map.GetNativePcOffset() == native_pc_offset) {
return stack_map;
}
}
LOG(FATAL) << "Unreachable";
- return StackMap<T>(MemoryRegion());
+ return StackMap(MemoryRegion());
}
private:
- static constexpr int kNumberOfStackMapsOffset = 0;
+ static constexpr int kOverallSizeOffset = 0;
+ static constexpr int kNumberOfStackMapsOffset = kOverallSizeOffset + sizeof(uint32_t);
static constexpr int kStackMaskSizeOffset = kNumberOfStackMapsOffset + sizeof(uint32_t);
static constexpr int kFixedSize = kStackMaskSizeOffset + sizeof(uint32_t);
@@ -299,7 +313,7 @@
}
MemoryRegion region_;
- template<typename U> friend class StackMapStream;
+ friend class StackMapStream;
};
} // namespace art
diff --git a/runtime/thread.cc b/runtime/thread.cc
index e323473..6e3e9c1 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2070,48 +2070,72 @@
// Process register map (which native and runtime methods don't have)
if (!m->IsNative() && !m->IsRuntimeMethod() && !m->IsProxyMethod()) {
- const uint8_t* native_gc_map = m->GetNativeGcMap();
- CHECK(native_gc_map != nullptr) << PrettyMethod(m);
- const DexFile::CodeItem* code_item = m->GetCodeItem();
- DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be nullptr or how would we compile its instructions?
- NativePcOffsetToReferenceMap map(native_gc_map);
- size_t num_regs = std::min(map.RegWidth() * 8,
- static_cast<size_t>(code_item->registers_size_));
- if (num_regs > 0) {
+ if (m->IsOptimized()) {
Runtime* runtime = Runtime::Current();
const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m);
uintptr_t native_pc_offset = m->NativePcOffset(GetCurrentQuickFramePc(), entry_point);
- const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
- DCHECK(reg_bitmap != nullptr);
- const void* code_pointer = mirror::ArtMethod::EntryPointToCodePointer(entry_point);
- const VmapTable vmap_table(m->GetVmapTable(code_pointer));
- QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
- // For all dex registers in the bitmap
- StackReference<mirror::ArtMethod>* cur_quick_frame = GetCurrentQuickFrame();
- DCHECK(cur_quick_frame != nullptr);
- for (size_t reg = 0; reg < num_regs; ++reg) {
- // Does this register hold a reference?
- if (TestBitmap(reg, reg_bitmap)) {
- uint32_t vmap_offset;
- if (vmap_table.IsInContext(reg, kReferenceVReg, &vmap_offset)) {
- int vmap_reg = vmap_table.ComputeRegister(frame_info.CoreSpillMask(), vmap_offset,
- kReferenceVReg);
- // This is sound as spilled GPRs will be word sized (ie 32 or 64bit).
- mirror::Object** ref_addr = reinterpret_cast<mirror::Object**>(GetGPRAddress(vmap_reg));
- if (*ref_addr != nullptr) {
- visitor_(ref_addr, reg, this);
+ StackMap map = m->GetStackMap(native_pc_offset);
+ MemoryRegion mask = map.GetStackMask();
+ for (size_t i = 0; i < mask.size_in_bits(); ++i) {
+ if (mask.LoadBit(i)) {
+ StackReference<mirror::Object>* ref_addr =
+ reinterpret_cast<StackReference<mirror::Object>*>(cur_quick_frame) + i;
+ mirror::Object* ref = ref_addr->AsMirrorPtr();
+ if (ref != nullptr) {
+ mirror::Object* new_ref = ref;
+ visitor_(&new_ref, -1, this);
+ if (ref != new_ref) {
+ ref_addr->Assign(new_ref);
}
- } else {
- StackReference<mirror::Object>* ref_addr =
- reinterpret_cast<StackReference<mirror::Object>*>(
- GetVRegAddr(cur_quick_frame, code_item, frame_info.CoreSpillMask(),
- frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), reg));
- mirror::Object* ref = ref_addr->AsMirrorPtr();
- if (ref != nullptr) {
- mirror::Object* new_ref = ref;
- visitor_(&new_ref, reg, this);
- if (ref != new_ref) {
- ref_addr->Assign(new_ref);
+ }
+ }
+ }
+ } else {
+ const uint8_t* native_gc_map = m->GetNativeGcMap();
+ CHECK(native_gc_map != nullptr) << PrettyMethod(m);
+ const DexFile::CodeItem* code_item = m->GetCodeItem();
+ // Can't be nullptr or how would we compile its instructions?
+ DCHECK(code_item != nullptr) << PrettyMethod(m);
+ NativePcOffsetToReferenceMap map(native_gc_map);
+ size_t num_regs = std::min(map.RegWidth() * 8,
+ static_cast<size_t>(code_item->registers_size_));
+ if (num_regs > 0) {
+ Runtime* runtime = Runtime::Current();
+ const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m);
+ uintptr_t native_pc_offset = m->NativePcOffset(GetCurrentQuickFramePc(), entry_point);
+ const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
+ DCHECK(reg_bitmap != nullptr);
+ const void* code_pointer = mirror::ArtMethod::EntryPointToCodePointer(entry_point);
+ const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+ QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
+ // For all dex registers in the bitmap
+ StackReference<mirror::ArtMethod>* cur_quick_frame = GetCurrentQuickFrame();
+ DCHECK(cur_quick_frame != nullptr);
+ for (size_t reg = 0; reg < num_regs; ++reg) {
+ // Does this register hold a reference?
+ if (TestBitmap(reg, reg_bitmap)) {
+ uint32_t vmap_offset;
+ if (vmap_table.IsInContext(reg, kReferenceVReg, &vmap_offset)) {
+ int vmap_reg = vmap_table.ComputeRegister(frame_info.CoreSpillMask(), vmap_offset,
+ kReferenceVReg);
+ // This is sound as spilled GPRs will be word sized (ie 32 or 64bit).
+ mirror::Object** ref_addr =
+ reinterpret_cast<mirror::Object**>(GetGPRAddress(vmap_reg));
+ if (*ref_addr != nullptr) {
+ visitor_(ref_addr, reg, this);
+ }
+ } else {
+ StackReference<mirror::Object>* ref_addr =
+ reinterpret_cast<StackReference<mirror::Object>*>(
+ GetVRegAddr(cur_quick_frame, code_item, frame_info.CoreSpillMask(),
+ frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), reg));
+ mirror::Object* ref = ref_addr->AsMirrorPtr();
+ if (ref != nullptr) {
+ mirror::Object* new_ref = ref;
+ visitor_(&new_ref, reg, this);
+ if (ref != new_ref) {
+ ref_addr->Assign(new_ref);
+ }
}
}
}
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 3a6a72b..7068a4d 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -26,7 +26,11 @@
namespace art {
jclass WellKnownClasses::com_android_dex_Dex;
+jclass WellKnownClasses::dalvik_system_DexFile;
+jclass WellKnownClasses::dalvik_system_DexPathList;
+jclass WellKnownClasses::dalvik_system_DexPathList$Element;
jclass WellKnownClasses::dalvik_system_PathClassLoader;
+jclass WellKnownClasses::java_lang_BootClassLoader;
jclass WellKnownClasses::java_lang_ClassLoader;
jclass WellKnownClasses::java_lang_ClassNotFoundException;
jclass WellKnownClasses::java_lang_Daemons;
@@ -79,6 +83,10 @@
jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
+jfieldID WellKnownClasses::dalvik_system_DexFile_cookie;
+jfieldID WellKnownClasses::dalvik_system_PathClassLoader_pathList;
+jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
+jfieldID WellKnownClasses::dalvik_system_DexPathList$Element_dexFile;
jfieldID WellKnownClasses::java_lang_Thread_daemon;
jfieldID WellKnownClasses::java_lang_Thread_group;
jfieldID WellKnownClasses::java_lang_Thread_lock;
@@ -131,7 +139,11 @@
void WellKnownClasses::Init(JNIEnv* env) {
com_android_dex_Dex = CacheClass(env, "com/android/dex/Dex");
+ dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
+ dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
+ dalvik_system_DexPathList$Element = CacheClass(env, "dalvik/system/DexPathList$Element");
dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader");
+ java_lang_BootClassLoader = CacheClass(env, "java/lang/BootClassLoader");
java_lang_ClassLoader = CacheClass(env, "java/lang/ClassLoader");
java_lang_ClassNotFoundException = CacheClass(env, "java/lang/ClassNotFoundException");
java_lang_Daemons = CacheClass(env, "java/lang/Daemons");
@@ -179,6 +191,10 @@
org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V");
org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
+ dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "J");
+ dalvik_system_PathClassLoader_pathList = CacheField(env, dalvik_system_PathClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
+ dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
+ dalvik_system_DexPathList$Element_dexFile = CacheField(env, dalvik_system_DexPathList$Element, false, "dexFile", "Ldalvik/system/DexFile;");
java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 7639f50..b10106c 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -40,7 +40,11 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static jclass com_android_dex_Dex;
+ static jclass dalvik_system_DexFile;
+ static jclass dalvik_system_DexPathList;
+ static jclass dalvik_system_DexPathList$Element;
static jclass dalvik_system_PathClassLoader;
+ static jclass java_lang_BootClassLoader;
static jclass java_lang_ClassLoader;
static jclass java_lang_ClassNotFoundException;
static jclass java_lang_Daemons;
@@ -93,6 +97,10 @@
static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
+ static jfieldID dalvik_system_DexFile_cookie;
+ static jfieldID dalvik_system_DexPathList_dexElements;
+ static jfieldID dalvik_system_DexPathList$Element_dexFile;
+ static jfieldID dalvik_system_PathClassLoader_pathList;
static jfieldID java_lang_reflect_AbstractMethod_artMethod;
static jfieldID java_lang_reflect_Field_artField;
static jfieldID java_lang_reflect_Proxy_h;
diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt
index e69de29..49d9cc0 100644
--- a/test/004-JniTest/expected.txt
+++ b/test/004-JniTest/expected.txt
@@ -0,0 +1,29 @@
+Super.<init>
+Super.<init>
+Subclass.<init>
+Super.<init>
+Super.<init>
+Subclass.<init>
+Super.<init>
+RUNNING super object, super class, super nonstatic
+Super.nonstaticMethod
+PASSED super object, super class, super nonstatic
+Super.<init>
+RUNNING super object, sub class, super nonstatic
+Super.nonstaticMethod
+PASSED super object, sub class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, super class, super nonstatic
+Super.nonstaticMethod
+PASSED sub object, super class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, sub class, super nonstatic
+Super.nonstaticMethod
+PASSED sub object, sub class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, sub class, sub nonstatic
+Subclass.nonstaticMethod
+PASSED sub object, sub class, sub nonstatic
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index f5a1d65..6fc4484 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -353,3 +353,198 @@
extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetStackClass2(JNIEnv* env, jclass) {
PthreadHelper(&testShallowGetStackClass2);
}
+
+class JniCallNonvirtualVoidMethodTest {
+ public:
+ explicit JniCallNonvirtualVoidMethodTest(JNIEnv* env)
+ : env_(env),
+ check_jni_ri_(true),
+ check_jni_android_(true),
+ super_(GetClass("JniCallNonvirtualTest")),
+ sub_(GetClass("JniCallNonvirtualTestSubclass")),
+ super_constructor_(GetMethodID(super_, true, "<init>")),
+ super_static_(GetMethodID(super_, false, "staticMethod")),
+ super_nonstatic_(GetMethodID(super_, true, "nonstaticMethod")),
+ sub_constructor_(GetMethodID(sub_, true, "<init>")),
+ sub_static_(GetMethodID(sub_, false, "staticMethod")),
+ sub_nonstatic_(GetMethodID(sub_, true, "nonstaticMethod")),
+ super_field_(GetFieldID(super_, "nonstaticMethodSuperCalled")),
+ sub_field_(GetFieldID(super_, "nonstaticMethodSubCalled")) {}
+
+ void Test() {
+ TestStaticCallNonvirtualMethod();
+ TestNewObject();
+ TestnonstaticCallNonvirtualMethod();
+ }
+
+ JNIEnv* const env_;
+
+ bool const check_jni_ri_;
+ bool const check_jni_android_;
+
+ jclass const super_;
+ jclass const sub_;
+
+ jmethodID const super_constructor_;
+ jmethodID const super_static_;
+ jmethodID const super_nonstatic_;
+ jmethodID const sub_constructor_;
+ jmethodID const sub_static_;
+ jmethodID const sub_nonstatic_;
+
+ jfieldID const super_field_;
+ jfieldID const sub_field_;
+
+ private:
+ jclass GetClass(const char* class_name) {
+ jclass c = env_->FindClass(class_name);
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ assert(!env_->ExceptionCheck());
+ assert(c != nullptr);
+ return c;
+ }
+
+ jmethodID GetMethodID(jclass c, bool nonstatic, const char* method_name) {
+ jmethodID m = ((nonstatic) ?
+ env_->GetMethodID(c, method_name, "()V") :
+ env_->GetStaticMethodID(c, method_name, "()V"));
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ assert(m != nullptr);
+ return m;
+ }
+
+ jobject CallConstructor(jclass c, jmethodID m) {
+ jobject o = env_->NewObject(c, m);
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ assert(o != nullptr);
+ return o;
+ }
+
+ void CallMethod(jobject o, jclass c, jmethodID m, bool nonstatic, const char* test_case) {
+ printf("RUNNING %s\n", test_case);
+ env_->CallNonvirtualVoidMethod(o, c, m);
+ bool exception_check = env_->ExceptionCheck();
+ if (c == nullptr || !nonstatic) {
+ if (!exception_check) {
+ printf("FAILED %s due to missing exception\n", test_case);
+ env_->FatalError("Expected NullPointerException with null jclass");
+ }
+ env_->ExceptionClear();
+ } else if (exception_check) {
+ printf("FAILED %s due to pending exception\n", test_case);
+ env_->ExceptionDescribe();
+ env_->FatalError(test_case);
+ }
+ printf("PASSED %s\n", test_case);
+ }
+
+ jfieldID GetFieldID(jclass c, const char* field_name) {
+ jfieldID m = env_->GetFieldID(c, field_name, "Z");
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ assert(m != nullptr);
+ return m;
+ }
+
+ jboolean GetBooleanField(jobject o, jfieldID f) {
+ jboolean b = env_->GetBooleanField(o, f);
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ return b;
+ }
+
+ void TestStaticCallNonvirtualMethod() {
+ if (!check_jni_ri_&& !check_jni_android_) {
+ CallMethod(nullptr, nullptr, super_static_, false, "null object, null class, super static");
+ }
+ if (!check_jni_android_) {
+ CallMethod(nullptr, super_, super_static_, false, "null object, super class, super static");
+ }
+ if (!check_jni_android_) {
+ CallMethod(nullptr, sub_, super_static_, false, "null object, sub class, super static");
+ }
+
+ if (!check_jni_ri_ && !check_jni_android_) {
+ CallMethod(nullptr, nullptr, sub_static_, false, "null object, null class, sub static");
+ }
+ if (!check_jni_android_) {
+ CallMethod(nullptr, sub_, sub_static_, false, "null object, super class, sub static");
+ }
+ if (!check_jni_android_) {
+ CallMethod(nullptr, super_, sub_static_, false, "null object, super class, sub static");
+ }
+ }
+
+ void TestNewObject() {
+ jobject super_super = CallConstructor(super_, super_constructor_);
+ jobject super_sub = CallConstructor(super_, sub_constructor_);
+ jobject sub_super = CallConstructor(sub_, super_constructor_);
+ jobject sub_sub = CallConstructor(sub_, sub_constructor_);
+
+ assert(env_->IsInstanceOf(super_super, super_));
+ assert(!env_->IsInstanceOf(super_super, sub_));
+
+ // Note that even though we called (and ran) the subclass
+ // constructor, we are not the subclass.
+ assert(env_->IsInstanceOf(super_sub, super_));
+ assert(!env_->IsInstanceOf(super_sub, sub_));
+
+ // Note that even though we called the superclass constructor, we
+ // are still the subclass.
+ assert(env_->IsInstanceOf(sub_super, super_));
+ assert(env_->IsInstanceOf(sub_super, sub_));
+
+ assert(env_->IsInstanceOf(sub_sub, super_));
+ assert(env_->IsInstanceOf(sub_sub, sub_));
+ }
+
+ void TestnonstaticCallNonvirtualMethod(bool super_object, bool super_class, bool super_method, const char* test_case) {
+ if (check_jni_android_) {
+ if (super_object && !super_method) {
+ return; // We don't allow a call with sub class method on the super class instance.
+ }
+ if (super_class && !super_method) {
+ return; // We don't allow a call with the sub class method with the super class argument.
+ }
+ }
+ jobject o = ((super_object) ?
+ CallConstructor(super_, super_constructor_) :
+ CallConstructor(sub_, sub_constructor_));
+ jclass c = (super_class) ? super_ : sub_;
+ jmethodID m = (super_method) ? super_nonstatic_ : sub_nonstatic_;
+ CallMethod(o, c, m, true, test_case);
+ jboolean super_field = GetBooleanField(o, super_field_);
+ jboolean sub_field = GetBooleanField(o, sub_field_);
+ assert(super_field == super_method);
+ assert(sub_field != super_method);
+ }
+
+ void TestnonstaticCallNonvirtualMethod() {
+ TestnonstaticCallNonvirtualMethod(true, true, true, "super object, super class, super nonstatic");
+ TestnonstaticCallNonvirtualMethod(true, false, true, "super object, sub class, super nonstatic");
+ TestnonstaticCallNonvirtualMethod(true, false, false, "super object, sub class, sub nonstatic");
+ TestnonstaticCallNonvirtualMethod(true, true, false, "super object, super class, sub nonstatic");
+
+ TestnonstaticCallNonvirtualMethod(false, true, true, "sub object, super class, super nonstatic");
+ TestnonstaticCallNonvirtualMethod(false, false, true, "sub object, sub class, super nonstatic");
+ TestnonstaticCallNonvirtualMethod(false, false, false, "sub object, sub class, sub nonstatic");
+ TestnonstaticCallNonvirtualMethod(false, true, false, "sub object, super class, sub nonstatic");
+ }
+};
+
+extern "C" void JNICALL Java_Main_testCallNonvirtual(JNIEnv* env, jclass) {
+ JniCallNonvirtualVoidMethodTest(env).Test();
+}
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index 5884bc0..8e92010 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -32,6 +32,7 @@
testIsAssignableFromOnPrimitiveTypes();
testShallowGetCallingClassLoader();
testShallowGetStackClass2();
+ testCallNonvirtual();
}
private static native void testFindClassOnAttachedNativeThread();
@@ -94,7 +95,7 @@
// Test sign-extension for values < 32b
- native static byte byteMethod(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7,
+ static native byte byteMethod(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7,
byte b8, byte b9, byte b10);
private static void testByteMethod() {
@@ -109,7 +110,7 @@
}
}
- native static short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
+ private static native short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
short s8, short s9, short s10);
private static void testShortMethod() {
@@ -126,7 +127,7 @@
// Test zero-extension for values < 32b
- native static boolean booleanMethod(boolean b1, boolean b2, boolean b3, boolean b4, boolean b5, boolean b6, boolean b7,
+ private static native boolean booleanMethod(boolean b1, boolean b2, boolean b3, boolean b4, boolean b5, boolean b6, boolean b7,
boolean b8, boolean b9, boolean b10);
private static void testBooleanMethod() {
@@ -139,7 +140,7 @@
}
}
- native static char charMethod(char c1, char c2, char c3, char c4, char c5, char c6, char c7,
+ private static native char charMethod(char c1, char c2, char c3, char c4, char c5, char c6, char c7,
char c8, char c9, char c10);
private static void testCharMethod() {
@@ -168,17 +169,55 @@
}
}
- native static boolean nativeIsAssignableFrom(Class<?> from, Class<?> to);
+ private static native boolean nativeIsAssignableFrom(Class<?> from, Class<?> to);
- static void testShallowGetCallingClassLoader() {
+ private static void testShallowGetCallingClassLoader() {
nativeTestShallowGetCallingClassLoader();
}
- native static void nativeTestShallowGetCallingClassLoader();
+ private native static void nativeTestShallowGetCallingClassLoader();
- static void testShallowGetStackClass2() {
+ private static void testShallowGetStackClass2() {
nativeTestShallowGetStackClass2();
}
- native static void nativeTestShallowGetStackClass2();
+ private static native void nativeTestShallowGetStackClass2();
+
+ private static native void testCallNonvirtual();
+}
+
+class JniCallNonvirtualTest {
+ public boolean nonstaticMethodSuperCalled = false;
+ public boolean nonstaticMethodSubCalled = false;
+
+ private static native void testCallNonvirtual();
+
+ public JniCallNonvirtualTest() {
+ System.out.println("Super.<init>");
+ }
+
+ public static void staticMethod() {
+ System.out.println("Super.staticMethod");
+ }
+
+ public void nonstaticMethod() {
+ System.out.println("Super.nonstaticMethod");
+ nonstaticMethodSuperCalled = true;
+ }
+}
+
+class JniCallNonvirtualTestSubclass extends JniCallNonvirtualTest {
+
+ public JniCallNonvirtualTestSubclass() {
+ System.out.println("Subclass.<init>");
+ }
+
+ public static void staticMethod() {
+ System.out.println("Subclass.staticMethod");
+ }
+
+ public void nonstaticMethod() {
+ System.out.println("Subclass.nonstaticMethod");
+ nonstaticMethodSubCalled = true;
+ }
}
diff --git a/test/036-finalizer/expected.txt b/test/036-finalizer/expected.txt
index a2a74fc..36fa5f8 100644
--- a/test/036-finalizer/expected.txt
+++ b/test/036-finalizer/expected.txt
@@ -11,3 +11,4 @@
sleep
reborn: [FinalizerTest message=nothing, finalized=false]
wimp: null
+Finalized 1024 / 1024
diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java
index 328425f..390472d 100644
--- a/test/036-finalizer/src/Main.java
+++ b/test/036-finalizer/src/Main.java
@@ -120,6 +120,39 @@
System.out.println("reborn: " + FinalizerTest.mReborn);
System.out.println("wimp: " + wimpString(wimp));
+ // Test runFinalization with multiple objects.
+ runFinalizationTest();
+ }
+
+ static class FinalizeCounter {
+ private static Object finalizeLock = new Object();
+ private static volatile int finalizeCount = 0;
+ private int index;
+ static int getCount() {
+ return finalizeCount;
+ }
+ FinalizeCounter(int index) {
+ this.index = index;
+ }
+ protected void finalize() {
+ synchronized(finalizeLock) {
+ ++finalizeCount;
+ }
+ }
+ }
+
+ private static void runFinalizationTest() {
+ int count = 1024;
+ Object[] objs = new Object[count];
+ for (int i = 0; i < count; ++i) {
+ objs[i] = new FinalizeCounter(i);
+ }
+ for (int i = 0; i < count; ++i) {
+ objs[i] = null;
+ }
+ System.gc();
+ System.runFinalization();
+ System.out.println("Finalized " + FinalizeCounter.getCount() + " / " + count);
}
public static class FinalizerTest {
diff --git a/test/098-ddmc/expected.txt b/test/098-ddmc/expected.txt
new file mode 100644
index 0000000..f8cda4c
--- /dev/null
+++ b/test/098-ddmc/expected.txt
@@ -0,0 +1,23 @@
+Confirm empty
+empty=Allocations[message header len: 15 entry header len: 9 stack frame len: 8 number of entries: 0 offset to string table from start of message: 15 number of class name strings: 0 number of method name strings: 0 number of source file name strings: 0]
+Confirm enable
+status=false
+status=true
+Capture some allocations (note just this causes allocations)
+before > 0=true
+Confirm when we overflow, we don't roll over to zero. b/17392248
+before < overflowAllocations=true
+after > before=true
+after.numberOfEntries=65535
+Disable and confirm back to empty
+status=false
+reset=Allocations[message header len: 15 entry header len: 9 stack frame len: 8 number of entries: 0 offset to string table from start of message: 15 number of class name strings: 0 number of method name strings: 0 number of source file name strings: 0]
+Confirm we can disable twice in a row
+status=false
+status=false
+Confirm we can reenable twice in a row without losing allocations
+status=true
+status=true
+second > first =true
+Goodbye
+goodbye=Allocations[message header len: 15 entry header len: 9 stack frame len: 8 number of entries: 0 offset to string table from start of message: 15 number of class name strings: 0 number of method name strings: 0 number of source file name strings: 0]
diff --git a/test/098-ddmc/info.txt b/test/098-ddmc/info.txt
new file mode 100644
index 0000000..39d26db
--- /dev/null
+++ b/test/098-ddmc/info.txt
@@ -0,0 +1 @@
+Tests of private org.apache.harmony.dalvik.ddmc.* APIs used for ddms support.
diff --git a/test/098-ddmc/src/Main.java b/test/098-ddmc/src/Main.java
new file mode 100644
index 0000000..962bd7f
--- /dev/null
+++ b/test/098-ddmc/src/Main.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ String name = System.getProperty("java.vm.name");
+ if (!"Dalvik".equals(name)) {
+ System.out.println("This test is not supported on " + name);
+ return;
+ }
+ testRecentAllocationTracking();
+ }
+
+ private static void testRecentAllocationTracking() throws Exception {
+ System.out.println("Confirm empty");
+ Allocations empty = new Allocations(DdmVmInternal.getRecentAllocations());
+ System.out.println("empty=" + empty);
+
+ System.out.println("Confirm enable");
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+ DdmVmInternal.enableRecentAllocations(true);
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+
+ System.out.println("Capture some allocations (note just this causes allocations)");
+ Allocations before = new Allocations(DdmVmInternal.getRecentAllocations());
+ System.out.println("before > 0=" + (before.numberOfEntries > 0));
+
+ System.out.println("Confirm when we overflow, we don't roll over to zero. b/17392248");
+ final int overflowAllocations = 64 * 1024; // Won't fit in unsigned 16-bit value.
+ for (int i = 0; i < overflowAllocations; i++) {
+ new String("fnord");
+ }
+ Allocations after = new Allocations(DdmVmInternal.getRecentAllocations());
+ System.out.println("before < overflowAllocations=" + (before.numberOfEntries < overflowAllocations));
+ System.out.println("after > before=" + (after.numberOfEntries > before.numberOfEntries));
+ System.out.println("after.numberOfEntries=" + after.numberOfEntries);
+
+ System.out.println("Disable and confirm back to empty");
+ DdmVmInternal.enableRecentAllocations(false);
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+ Allocations reset = new Allocations(DdmVmInternal.getRecentAllocations());
+ System.out.println("reset=" + reset);
+
+ System.out.println("Confirm we can disable twice in a row");
+ DdmVmInternal.enableRecentAllocations(false);
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+ DdmVmInternal.enableRecentAllocations(false);
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+
+ System.out.println("Confirm we can reenable twice in a row without losing allocations");
+ DdmVmInternal.enableRecentAllocations(true);
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+ for (int i = 0; i < 16 * 1024; i++) {
+ new String("fnord");
+ }
+ Allocations first = new Allocations(DdmVmInternal.getRecentAllocations());
+ DdmVmInternal.enableRecentAllocations(true);
+ System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
+ Allocations second = new Allocations(DdmVmInternal.getRecentAllocations());
+ System.out.println("second > first =" + (second.numberOfEntries > first.numberOfEntries));
+
+ System.out.println("Goodbye");
+ DdmVmInternal.enableRecentAllocations(false);
+ Allocations goodbye = new Allocations(DdmVmInternal.getRecentAllocations());
+ System.out.println("goodbye=" + goodbye);
+ }
+
+ private static class Allocations {
+ final int messageHeaderLen;
+ final int entryHeaderLen;
+ final int stackFrameLen;
+ final int numberOfEntries;
+ final int offsetToStringTableFromStartOfMessage;
+ final int numberOfClassNameStrings;
+ final int numberOfMethodNameStrings;
+ final int numberOfSourceFileNameStrings;
+
+ Allocations(byte[] allocations) {
+ ByteBuffer b = ByteBuffer.wrap(allocations);
+ messageHeaderLen = b.get() & 0xff;
+ if (messageHeaderLen != 15) {
+ throw new IllegalArgumentException("Unexpected messageHeaderLen " + messageHeaderLen);
+ }
+ entryHeaderLen = b.get() & 0xff;
+ if (entryHeaderLen != 9) {
+ throw new IllegalArgumentException("Unexpected entryHeaderLen " + entryHeaderLen);
+ }
+ stackFrameLen = b.get() & 0xff;
+ if (stackFrameLen != 8) {
+ throw new IllegalArgumentException("Unexpected messageHeaderLen " + stackFrameLen);
+ }
+ numberOfEntries = b.getShort() & 0xffff;
+ offsetToStringTableFromStartOfMessage = b.getInt();
+ numberOfClassNameStrings = b.getShort() & 0xffff;
+ numberOfMethodNameStrings = b.getShort() & 0xffff;
+ numberOfSourceFileNameStrings = b.getShort() & 0xffff;
+ }
+
+ public String toString() {
+ return ("Allocations[message header len: " + messageHeaderLen +
+ " entry header len: " + entryHeaderLen +
+ " stack frame len: " + stackFrameLen +
+ " number of entries: " + numberOfEntries +
+ " offset to string table from start of message: " + offsetToStringTableFromStartOfMessage +
+ " number of class name strings: " + numberOfClassNameStrings +
+ " number of method name strings: " + numberOfMethodNameStrings +
+ " number of source file name strings: " + numberOfSourceFileNameStrings +
+ "]");
+ }
+ }
+
+ private static class DdmVmInternal {
+ private static final Method enableRecentAllocationsMethod;
+ private static final Method getRecentAllocationStatusMethod;
+ private static final Method getRecentAllocationsMethod;
+ static {
+ try {
+ Class c = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal");
+ enableRecentAllocationsMethod = c.getDeclaredMethod("enableRecentAllocations",
+ Boolean.TYPE);
+ getRecentAllocationStatusMethod = c.getDeclaredMethod("getRecentAllocationStatus");
+ getRecentAllocationsMethod = c.getDeclaredMethod("getRecentAllocations");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void enableRecentAllocations(boolean enable) throws Exception {
+ enableRecentAllocationsMethod.invoke(null, enable);
+ }
+ public static boolean getRecentAllocationStatus() throws Exception {
+ return (boolean) getRecentAllocationStatusMethod.invoke(null);
+ }
+ public static byte[] getRecentAllocations() throws Exception {
+ return (byte[]) getRecentAllocationsMethod.invoke(null);
+ }
+ }
+}
diff --git a/test/120-hashcode/expected.txt b/test/120-hashcode/expected.txt
new file mode 100644
index 0000000..619c561
--- /dev/null
+++ b/test/120-hashcode/expected.txt
@@ -0,0 +1 @@
+Done.
diff --git a/test/120-hashcode/info.txt b/test/120-hashcode/info.txt
new file mode 100644
index 0000000..80f131d
--- /dev/null
+++ b/test/120-hashcode/info.txt
@@ -0,0 +1 @@
+Check that object hashCode and System.identityHashCode never cause the hash to change.
\ No newline at end of file
diff --git a/test/120-hashcode/src/Main.java b/test/120-hashcode/src/Main.java
new file mode 100644
index 0000000..d2435ce
--- /dev/null
+++ b/test/120-hashcode/src/Main.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ Object o = new Object();
+ // Generate a hashcode and put it in the lock word.
+ int hashOrig = o.hashCode();
+ int hashInflated = 0;
+ int hashSystemOrig = System.identityHashCode(o);
+ int hashSystemInflated = 0;
+ // Inflate the monitor to move the hash from the lock word to the Monitor.
+ synchronized (o) {
+ hashInflated = o.hashCode();
+ hashSystemInflated = System.identityHashCode(o);
+ }
+ // Make sure that all the hashes agree.
+ if (hashOrig != hashInflated || hashOrig != hashSystemOrig ||
+ hashSystemOrig != hashSystemInflated) {
+ System.err.println("hash codes dont match: " + hashOrig + " " + hashInflated + " " +
+ hashSystemOrig + " " + hashSystemInflated);
+ }
+ System.out.println("Done.");
+ }
+}
+
+
diff --git a/test/407-arrays/src/Main.java b/test/407-arrays/src/Main.java
index 5d27e6d..b5e95b0 100644
--- a/test/407-arrays/src/Main.java
+++ b/test/407-arrays/src/Main.java
@@ -57,7 +57,7 @@
int[] ints, Object[] objects, long[] longs, int index) {
bools[0] = true;
assertEquals(true, bools[0]);
- bools[1] = true;
+ bools[index] = true;
assertEquals(true, bools[index]);
bytes[0] = -4;
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 4485590..49aa912 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -182,7 +182,9 @@
# since we are relocating. Get the total size of the /system/framework directory
# in 512 byte blocks and set it as the ulimit. This should be more than enough
# room.
- ulimit -S $(du -c -B512 ${ANDROID_ROOT}/framework | tail -1 | cut -f1) || exit 1
+ if [ ! `uname` = "Darwin" ]; then # TODO: Darwin doesn't support "du -B..."
+ ulimit -S $(du -c -B512 ${ANDROID_ROOT}/framework | tail -1 | cut -f1) || exit 1
+ fi
else
FLAGS="${FLAGS} -Xnorelocate"
COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -Xnorelocate --no-include-patch-information"
diff --git a/test/run-test b/test/run-test
index eed28a5..b140fbf 100755
--- a/test/run-test
+++ b/test/run-test
@@ -223,10 +223,10 @@
#
# Cannot use realpath, as it does not exist on Mac.
# Cannot us a simple "cd", as the path might not be created yet.
-# Use -m option of readlink: canonicalizes, but allows non-existing components.
+# Cannot use readlink -m, as it does not exist on Mac.
+# Fallback to nuclear option:
noncanonical_tmp_dir=$tmp_dir
-tmp_dir="`cd $oldwd ; readlink -m $tmp_dir`"
-
+tmp_dir="`cd $oldwd ; python -c "import os; print os.path.realpath('$tmp_dir')"`"
mkdir -p $tmp_dir
if [ "$basic_verify" = "true" ]; then
diff --git a/tools/Android.mk b/tools/Android.mk
index d3be17f..9a96f7a 100644
--- a/tools/Android.mk
+++ b/tools/Android.mk
@@ -27,3 +27,13 @@
@echo "Copy: $(PRIVATE_MODULE) ($@)"
$(copy-file-to-new-target)
$(hide) chmod 755 $@
+
+# Copy the art shell script to the target's bin directory
+include $(CLEAR_VARS)
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := art
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/art $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-new-target)
+ $(hide) chmod 755 $@
diff --git a/tools/art b/tools/art
index 85517d3..1afcc38 100644
--- a/tools/art
+++ b/tools/art
@@ -14,27 +14,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-lib=-XXlib:libart.so
-invoke_with=
-
-while true; do
- if [ "$1" = "--invoke-with" ]; then
- shift
- invoke_with="$1"
- shift
- elif [ "$1" = "-d" ]; then
- lib="-XXlib:libartd.so"
- shift
- elif expr "$1" : "--" >/dev/null 2>&1; then
- echo "unknown option: $1" 1>&2
- exit 1
- else
- break
- fi
-done
-
function follow_links() {
- file="$1"
+ if [ z"$BASH_SOURCE" != z ]; then
+ file="$BASH_SOURCE"
+ else
+ file="$0"
+ fi
while [ -h "$file" ]; do
# On Mac OS, readlink -f doesn't work.
file="$(readlink "$file")"
@@ -42,13 +27,6 @@
echo "$file"
}
-PROG_NAME="$(follow_links "$BASH_SOURCE")"
-PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
-ANDROID_BUILD_TOP="$(cd "${PROG_DIR}/../../../../" ; pwd -P)/"
-ANDROID_HOST_OUT=$PROG_DIR/..
-ANDROID_DATA=$PWD/android-data$$
-DALVIKVM_EXECUTABLE=$ANDROID_HOST_OUT/bin/dalvikvm
-
function find_libdir() {
if [ "$(readlink "$DALVIKVM_EXECUTABLE")" = "dalvikvm64" ]; then
echo "lib64"
@@ -57,14 +35,39 @@
fi
}
-LD_LIBRARY_PATH=$ANDROID_HOST_OUT/"$(find_libdir)"
+PROG_NAME="$(follow_links)"
+PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
+ANDROID_ROOT=$PROG_DIR/..
+ANDROID_DATA=$PWD/android-data$$
+DALVIKVM_EXECUTABLE=$ANDROID_ROOT/bin/dalvikvm
+LIBDIR=$(find_libdir)
+LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR
-mkdir -p $ANDROID_DATA/dalvik-cache/{x86,x86_64}
+lib=-XXlib:$ANDROID_ROOT/$LIBDIR/libart.so
+invoke_with=
+
+while true; do
+ if [ "$1" = "--invoke-with" ]; then
+ shift
+ invoke_with="$1"
+ shift
+ elif [ "$1" = "-d" ]; then
+ lib="-XXlib:$LIBDIR/libartd.so"
+ shift
+ elif expr "$1" : "--" >/dev/null 2>&1; then
+ echo "unknown option: $1" 1>&2
+ exit 1
+ else
+ break
+ fi
+done
+
+mkdir -p $ANDROID_DATA/dalvik-cache/{arm,arm64,x86,x86_64}
ANDROID_DATA=$ANDROID_DATA \
- ANDROID_ROOT=$ANDROID_HOST_OUT \
+ ANDROID_ROOT=$ANDROID_ROOT \
LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
$invoke_with $DALVIKVM_EXECUTABLE $lib \
- -Ximage:$ANDROID_HOST_OUT/framework/core.art \
+ -Ximage:$ANDROID_ROOT/framework/core.art \
"$@"
EXIT_STATUS=$?
rm -rf $ANDROID_DATA
diff --git a/tools/symbolize.sh b/tools/symbolize.sh
new file mode 100755
index 0000000..b66191f
--- /dev/null
+++ b/tools/symbolize.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# Symbolize oat files from the dalvik cache of a device.
+#
+# By default, pulls everything from the dalvik cache. A simple yes/no/quit prompt for each file can
+# be requested by giving "--interactive" as a parameter.
+
+INTERACTIVE="no"
+if [ "x$1" = "x--interactive" ] ; then
+ INTERACTIVE="yes"
+fi
+
+# Pull the file from the device and symbolize it.
+function one() {
+ echo $1 $2
+ if [ "x$INTERACTIVE" = "xyes" ] ; then
+ echo -n "What to do? [Y/n/q] "
+ read -e input
+ if [ "x$input" = "xn" ] ; then
+ return
+ fi
+ if [ "x$input" = "xq" ] ; then
+ exit 0
+ fi
+ fi
+ adb pull /data/dalvik-cache/$1/$2 /tmp || exit 1
+ mkdir -p $OUT/symbols/data/dalvik-cache/$1
+ oatdump --symbolize=/tmp/$2 --output=$OUT/symbols/data/dalvik-cache/$1/$2
+}
+
+# adb shell ls seems to output in DOS format (CRLF), which messes up scripting
+function adbls() {
+ adb shell ls $@ | sed 's/\r$//'
+}
+
+# Check for all ISA directories on device.
+function all() {
+ DIRS=$(adbls /data/dalvik-cache/)
+ for DIR in $DIRS ; do
+ case $DIR in
+ arm|arm64|mips|x86|x86_64)
+ FILES=$(adbls /data/dalvik-cache/$DIR/*.oat /data/dalvik-cache/$DIR/*.dex)
+ for FILE in $FILES ; do
+ # Cannot use basename as the file doesn't exist.
+ NAME=$(echo $FILE | sed -e 's/.*\///')
+ one $DIR $NAME
+ done
+ ;;
+ esac
+ done
+}
+
+all