Merge "Add D/CHECK_CONSTEXPR macros for use in constexpr context."
diff --git a/build/Android.common.mk b/build/Android.common.mk
index a14b951..09f34b3 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -34,8 +34,8 @@
#
ART_BUILD_TARGET_NDEBUG ?= true
ART_BUILD_TARGET_DEBUG ?= true
-ART_BUILD_HOST_NDEBUG ?= $(WITH_HOST_DALVIK)
-ART_BUILD_HOST_DEBUG ?= $(WITH_HOST_DALVIK)
+ART_BUILD_HOST_NDEBUG ?= true
+ART_BUILD_HOST_DEBUG ?= true
ifeq ($(HOST_PREFER_32_BIT),true)
ART_HOST_ARCH := $(HOST_2ND_ARCH)
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 407269b..1bb1d56 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -250,11 +250,9 @@
$(foreach file,$(RUNTIME_GTEST_TARGET_SRC_FILES), $(eval $(call build-art-test,target,$(file),,)))
$(foreach file,$(COMPILER_GTEST_TARGET_SRC_FILES), $(eval $(call build-art-test,target,$(file),art/compiler,libartd-compiler)))
endif
-ifeq ($(WITH_HOST_DALVIK),true)
- ifeq ($(ART_BUILD_HOST),true)
- $(foreach file,$(RUNTIME_GTEST_HOST_SRC_FILES), $(eval $(call build-art-test,host,$(file),,)))
- $(foreach file,$(COMPILER_GTEST_HOST_SRC_FILES), $(eval $(call build-art-test,host,$(file),art/compiler,libartd-compiler)))
- endif
+ifeq ($(ART_BUILD_HOST),true)
+ $(foreach file,$(RUNTIME_GTEST_HOST_SRC_FILES), $(eval $(call build-art-test,host,$(file),,)))
+ $(foreach file,$(COMPILER_GTEST_HOST_SRC_FILES), $(eval $(call build-art-test,host,$(file),art/compiler,libartd-compiler)))
endif
# Used outside the art project to get a list of the current tests
diff --git a/build/Android.libarttest.mk b/build/Android.libarttest.mk
index b4c99b5..76e5af0 100644
--- a/build/Android.libarttest.mk
+++ b/build/Android.libarttest.mk
@@ -74,8 +74,6 @@
ifeq ($(ART_BUILD_TARGET),true)
$(eval $(call build-libarttest,target))
endif
-ifeq ($(WITH_HOST_DALVIK),true)
- ifeq ($(ART_BUILD_HOST),true)
- $(eval $(call build-libarttest,host))
- endif
+ifeq ($(ART_BUILD_HOST),true)
+ $(eval $(call build-libarttest,host))
endif
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index bf07ecc..fbb7eb3 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -74,7 +74,6 @@
endif # ART_BUILD_HOST
# If we aren't building the host toolchain, skip building the target core.art.
-ifeq ($(WITH_HOST_DALVIK),true)
ifeq ($(ART_BUILD_TARGET),true)
include $(CLEAR_VARS)
LOCAL_MODULE := core.art
@@ -84,4 +83,3 @@
LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_CORE_IMG_OUT)
include $(BUILD_PHONY_PACKAGE)
endif # ART_BUILD_TARGET
-endif # WITH_HOST_DALVIK
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 3cf7368..9a868fc 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -273,14 +273,12 @@
endef
-ifeq ($(WITH_HOST_DALVIK),true)
- # We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
- ifeq ($(ART_BUILD_NDEBUG),true)
- $(eval $(call build-libart-compiler,host,ndebug))
- endif
- ifeq ($(ART_BUILD_DEBUG),true)
- $(eval $(call build-libart-compiler,host,debug))
- endif
+# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
+ifeq ($(ART_BUILD_NDEBUG),true)
+ $(eval $(call build-libart-compiler,host,ndebug))
+endif
+ifeq ($(ART_BUILD_DEBUG),true)
+ $(eval $(call build-libart-compiler,host,debug))
endif
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
$(eval $(call build-libart-compiler,target,ndebug))
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 1460ce6..4b2bc4a 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -346,7 +346,7 @@
if (mir->next != NULL) {
MIR* mir_next = mir->next;
// Make sure result of cmp is used by next insn and nowhere else
- if (IsInstructionIfCcZ(mir->next->dalvikInsn.opcode) &&
+ if (IsInstructionIfCcZ(mir_next->dalvikInsn.opcode) &&
(mir->ssa_rep->defs[0] == mir_next->ssa_rep->uses[0]) &&
(GetSSAUseCount(mir->ssa_rep->defs[0]) == 1)) {
mir_next->meta.ccode = ConditionCodeForIfCcZ(mir_next->dalvikInsn.opcode);
@@ -374,12 +374,16 @@
default: LOG(ERROR) << "Unexpected opcode: " << opcode;
}
mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+ // Copy the SSA information that is relevant.
mir_next->ssa_rep->num_uses = mir->ssa_rep->num_uses;
mir_next->ssa_rep->uses = mir->ssa_rep->uses;
mir_next->ssa_rep->fp_use = mir->ssa_rep->fp_use;
mir_next->ssa_rep->num_defs = 0;
mir->ssa_rep->num_uses = 0;
mir->ssa_rep->num_defs = 0;
+ // Copy in the decoded instruction information for potential SSA re-creation.
+ mir_next->dalvikInsn.vA = mir->dalvikInsn.vB;
+ mir_next->dalvikInsn.vB = mir->dalvikInsn.vC;
}
}
break;
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 8093fd7..a050a05 100644
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -1095,6 +1095,55 @@
void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2) {
+ if (Gen64Bit()) {
+ if (rl_src1.is_const) {
+ std::swap(rl_src1, rl_src2);
+ }
+ // Are we multiplying by a constant?
+ if (rl_src2.is_const) {
+ int64_t val = mir_graph_->ConstantValueWide(rl_src2);
+ if (val == 0) {
+ RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
+ OpRegReg(kOpXor, rl_result.reg, rl_result.reg);
+ StoreValueWide(rl_dest, rl_result);
+ return;
+ } else if (val == 1) {
+ StoreValueWide(rl_dest, rl_src1);
+ return;
+ } else if (val == 2) {
+ GenAddLong(Instruction::ADD_LONG, rl_dest, rl_src1, rl_src1);
+ return;
+ } else if (IsPowerOfTwo(val)) {
+ int shift_amount = LowestSetBit(val);
+ if (!BadOverlap(rl_src1, rl_dest)) {
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ RegLocation rl_result = GenShiftImmOpLong(Instruction::SHL_LONG, rl_dest,
+ rl_src1, shift_amount);
+ StoreValueWide(rl_dest, rl_result);
+ return;
+ }
+ }
+ }
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+ RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
+ if (rl_result.reg.GetReg() == rl_src1.reg.GetReg() &&
+ rl_result.reg.GetReg() == rl_src2.reg.GetReg()) {
+ NewLIR2(kX86Imul64RR, rl_result.reg.GetReg(), rl_result.reg.GetReg());
+ } else if (rl_result.reg.GetReg() != rl_src1.reg.GetReg() &&
+ rl_result.reg.GetReg() == rl_src2.reg.GetReg()) {
+ NewLIR2(kX86Imul64RR, rl_result.reg.GetReg(), rl_src1.reg.GetReg());
+ } else if (rl_result.reg.GetReg() == rl_src1.reg.GetReg() &&
+ rl_result.reg.GetReg() != rl_src2.reg.GetReg()) {
+ NewLIR2(kX86Imul64RR, rl_result.reg.GetReg(), rl_src2.reg.GetReg());
+ } else {
+ OpRegCopy(rl_result.reg, rl_src1.reg);
+ NewLIR2(kX86Imul64RR, rl_result.reg.GetReg(), rl_src2.reg.GetReg());
+ }
+ StoreValueWide(rl_dest, rl_result);
+ return;
+ }
+
if (rl_src1.is_const) {
std::swap(rl_src1, rl_src2);
}
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 09f2eae..cb66e48 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -29,21 +29,16 @@
namespace art {
-bool ElfWriterQuick::Create(File* elf_file,
- OatWriter* oat_writer,
- const std::vector<const DexFile*>& dex_files,
- const std::string& android_root,
- bool is_host,
- const CompilerDriver& driver) {
- ElfWriterQuick elf_writer(driver, elf_file);
- return elf_writer.Write(oat_writer, dex_files, android_root, is_host);
+static constexpr Elf32_Word NextOffset(const Elf32_Shdr& cur, const Elf32_Shdr& prev) {
+ return RoundUp(prev.sh_size + prev.sh_offset, cur.sh_addralign);
}
-bool ElfWriterQuick::Write(OatWriter* oat_writer,
- const std::vector<const DexFile*>& dex_files_unused,
- const std::string& android_root_unused,
- bool is_host_unused) {
- const bool debug = false;
+static uint8_t MakeStInfo(uint8_t binding, uint8_t type) {
+ return ((binding) << 4) + ((type) & 0xf);
+}
+
+bool ElfWriterQuick::ElfBuilder::Write() {
+ // The basic layout of the elf file. Order may be different in final output.
// +-------------------------+
// | Elf32_Ehdr |
// +-------------------------+
@@ -67,12 +62,14 @@
// | boot.oat\0 |
// +-------------------------+
// | .hash |
- // | Elf32_Word nbucket = 1 |
- // | Elf32_Word nchain = 3 |
- // | Elf32_Word bucket[0] = 0|
- // | Elf32_Word chain[0] = 1|
- // | Elf32_Word chain[1] = 2|
- // | Elf32_Word chain[2] = 3|
+ // | Elf32_Word nbucket = b |
+ // | Elf32_Word nchain = c |
+ // | Elf32_Word bucket[0] |
+ // | ... |
+ // | Elf32_Word bucket[b - 1]|
+ // | Elf32_Word chain[0] |
+ // | ... |
+ // | Elf32_Word chain[c - 1] |
// +-------------------------+
// | .rodata |
// | oatdata..oatexec-4 |
@@ -88,6 +85,12 @@
// | Elf32_Dyn DT_STRTAB |
// | Elf32_Dyn DT_STRSZ |
// | Elf32_Dyn DT_NULL |
+ // +-------------------------+ (Optional)
+ // | .strtab | (Optional)
+ // | program symbol names | (Optional)
+ // +-------------------------+ (Optional)
+ // | .symtab | (Optional)
+ // | program symbols | (Optional)
// +-------------------------+
// | .shstrtab |
// | \0 |
@@ -98,7 +101,20 @@
// | .rodata\0 |
// | .text\0 |
// | .shstrtab\0 |
- // | .debug_frame\0 |
+ // | .symtab\0 | (Optional)
+ // | .strtab\0 | (Optional)
+ // | .debug_str\0 | (Optional)
+ // | .debug_info\0 | (Optional)
+ // | .debug_frame\0 | (Optional)
+ // | .debug_abbrev\0 | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_str | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_info | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_frame | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_abbrev | (Optional)
// +-------------------------+
// | Elf32_Shdr NULL |
// | Elf32_Shdr .dynsym |
@@ -108,20 +124,20 @@
// | Elf32_Shdr .rodata |
// | Elf32_Shdr .dynamic |
// | Elf32_Shdr .shstrtab |
+ // | Elf32_Shdr .debug_str | (Optional)
// | Elf32_Shdr .debug_info | (Optional)
- // | Elf32_Shdr .debug_abbrev| (Optional)
// | Elf32_Shdr .debug_frame | (Optional)
+ // | Elf32_Shdr .debug_abbrev| (Optional)
// +-------------------------+
- // phase 1: computing offsets
- uint32_t expected_offset = 0;
- // Elf32_Ehdr
- expected_offset += sizeof(Elf32_Ehdr);
+ if (fatal_error_) {
+ return false;
+ }
+ // Step 1. Figure out all the offsets.
- // PHDR
- uint32_t phdr_alignment = sizeof(Elf32_Word);
- uint32_t phdr_offset = expected_offset;
+ // What phdr is.
+ uint32_t phdr_offset = sizeof(Elf32_Ehdr);
const uint8_t PH_PHDR = 0;
const uint8_t PH_LOAD_R__ = 1;
const uint8_t PH_LOAD_R_X = 2;
@@ -129,41 +145,40 @@
const uint8_t PH_DYNAMIC = 4;
const uint8_t PH_NUM = 5;
uint32_t phdr_size = sizeof(Elf32_Phdr) * PH_NUM;
- expected_offset += phdr_size;
- if (debug) {
+ if (debug_logging_) {
LOG(INFO) << "phdr_offset=" << phdr_offset << std::hex << " " << phdr_offset;
LOG(INFO) << "phdr_size=" << phdr_size << std::hex << " " << phdr_size;
}
+ Elf32_Phdr program_headers[PH_NUM];
+ 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(Elf32_Word);
- // .dynsym
- uint32_t dynsym_alignment = sizeof(Elf32_Word);
- uint32_t dynsym_offset = expected_offset = RoundUp(expected_offset, dynsym_alignment);
- const uint8_t SYM_UNDEF = 0; // aka STN_UNDEF
- const uint8_t SYM_OATDATA = 1;
- const uint8_t SYM_OATEXEC = 2;
- const uint8_t SYM_OATLASTWORD = 3;
- const uint8_t SYM_NUM = 4;
- uint32_t dynsym_size = sizeof(Elf32_Sym) * SYM_NUM;
- expected_offset += dynsym_size;
- if (debug) {
- LOG(INFO) << "dynsym_offset=" << dynsym_offset << std::hex << " " << dynsym_offset;
- LOG(INFO) << "dynsym_size=" << dynsym_size << std::hex << " " << dynsym_size;
- }
+ 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;
- // .dynstr
- uint32_t dynstr_alignment = 1;
- uint32_t dynstr_offset = expected_offset = RoundUp(expected_offset, dynstr_alignment);
- std::string dynstr;
- dynstr += '\0';
- uint32_t dynstr_oatdata_offset = dynstr.size();
- dynstr += "oatdata";
- dynstr += '\0';
- uint32_t dynstr_oatexec_offset = dynstr.size();
- dynstr += "oatexec";
- dynstr += '\0';
- uint32_t dynstr_oatlastword_offset = dynstr.size();
- dynstr += "oatlastword";
- dynstr += '\0';
+ 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.
+ std::string dynstr(dynsym_builder_.GenerateStrtab());
+
+ // Add the SONAME to the dynstr.
uint32_t dynstr_soname_offset = dynstr.size();
std::string file_name(elf_file_->GetPath());
size_t directory_separator_pos = file_name.rfind('/');
@@ -172,672 +187,651 @@
}
dynstr += file_name;
dynstr += '\0';
- uint32_t dynstr_size = dynstr.size();
- expected_offset += dynstr_size;
- if (debug) {
- LOG(INFO) << "dynstr_offset=" << dynstr_offset << std::hex << " " << dynstr_offset;
- LOG(INFO) << "dynstr_size=" << dynstr_size << std::hex << " " << dynstr_size;
+ 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();
}
- // .hash
- uint32_t hash_alignment = sizeof(Elf32_Word); // Even for 64-bit
- uint32_t hash_offset = expected_offset = RoundUp(expected_offset, hash_alignment);
- const uint8_t HASH_NBUCKET = 0;
- const uint8_t HASH_NCHAIN = 1;
- const uint8_t HASH_BUCKET0 = 2;
- const uint8_t HASH_NUM = HASH_BUCKET0 + 1 + SYM_NUM;
- uint32_t hash_size = sizeof(Elf32_Word) * HASH_NUM;
- expected_offset += hash_size;
- if (debug) {
- LOG(INFO) << "hash_offset=" << hash_offset << std::hex << " " << hash_offset;
- LOG(INFO) << "hash_size=" << hash_size << std::hex << " " << hash_size;
+ // get the strtab
+ std::string strtab;
+ if (IncludingDebugSymbols()) {
+ 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();
+ }
}
- // .rodata
- uint32_t oat_data_alignment = kPageSize;
- uint32_t oat_data_offset = expected_offset = RoundUp(expected_offset, oat_data_alignment);
- const OatHeader& oat_header = oat_writer->GetOatHeader();
- CHECK(oat_header.IsValid());
- uint32_t oat_data_size = oat_header.GetExecutableOffset();
- expected_offset += oat_data_size;
- if (debug) {
- LOG(INFO) << "oat_data_offset=" << oat_data_offset << std::hex << " " << oat_data_offset;
- LOG(INFO) << "oat_data_size=" << oat_data_size << std::hex << " " << oat_data_size;
- }
-
- // .text
- uint32_t oat_exec_alignment = kPageSize;
- CHECK_ALIGNED(expected_offset, kPageSize);
- uint32_t oat_exec_offset = expected_offset = RoundUp(expected_offset, oat_exec_alignment);
- uint32_t oat_exec_size = oat_writer->GetSize() - oat_data_size;
- expected_offset += oat_exec_size;
- CHECK_EQ(oat_data_offset + oat_writer->GetSize(), expected_offset);
- if (debug) {
- LOG(INFO) << "oat_exec_offset=" << oat_exec_offset << std::hex << " " << oat_exec_offset;
- LOG(INFO) << "oat_exec_size=" << oat_exec_size << std::hex << " " << oat_exec_size;
- }
-
- // .dynamic
- // alignment would naturally be sizeof(Elf32_Word), but we want this in a new segment
- uint32_t dynamic_alignment = kPageSize;
- uint32_t dynamic_offset = expected_offset = RoundUp(expected_offset, dynamic_alignment);
- const uint8_t DH_SONAME = 0;
- const uint8_t DH_HASH = 1;
- const uint8_t DH_SYMTAB = 2;
- const uint8_t DH_SYMENT = 3;
- const uint8_t DH_STRTAB = 4;
- const uint8_t DH_STRSZ = 5;
- const uint8_t DH_NULL = 6;
- const uint8_t DH_NUM = 7;
- uint32_t dynamic_size = sizeof(Elf32_Dyn) * DH_NUM;
- expected_offset += dynamic_size;
- if (debug) {
- LOG(INFO) << "dynamic_offset=" << dynamic_offset << std::hex << " " << dynamic_offset;
- LOG(INFO) << "dynamic_size=" << dynamic_size << std::hex << " " << dynamic_size;
- }
-
- // .shstrtab
- uint32_t shstrtab_alignment = 1;
- uint32_t shstrtab_offset = expected_offset = RoundUp(expected_offset, shstrtab_alignment);
+ // Get the section header string table.
+ std::vector<Elf32_Shdr*> section_ptrs;
std::string shstrtab;
shstrtab += '\0';
- uint32_t shstrtab_dynamic_offset = shstrtab.size();
- CHECK_EQ(1U, shstrtab_dynamic_offset);
- shstrtab += ".dynamic";
- shstrtab += '\0';
- uint32_t shstrtab_dynsym_offset = shstrtab.size();
- shstrtab += ".dynsym";
- shstrtab += '\0';
- uint32_t shstrtab_dynstr_offset = shstrtab.size();
- shstrtab += ".dynstr";
- shstrtab += '\0';
- uint32_t shstrtab_hash_offset = shstrtab.size();
- shstrtab += ".hash";
- shstrtab += '\0';
- uint32_t shstrtab_rodata_offset = shstrtab.size();
- shstrtab += ".rodata";
- shstrtab += '\0';
- uint32_t shstrtab_text_offset = shstrtab.size();
- shstrtab += ".text";
- shstrtab += '\0';
- uint32_t shstrtab_shstrtab_offset = shstrtab.size();
- shstrtab += ".shstrtab";
- shstrtab += '\0';
- uint32_t shstrtab_debug_info_offset = shstrtab.size();
- shstrtab += ".debug_info";
- shstrtab += '\0';
- uint32_t shstrtab_debug_abbrev_offset = shstrtab.size();
- shstrtab += ".debug_abbrev";
- shstrtab += '\0';
- uint32_t shstrtab_debug_str_offset = shstrtab.size();
- shstrtab += ".debug_str";
- shstrtab += '\0';
- uint32_t shstrtab_debug_frame_offset = shstrtab.size();
- shstrtab += ".debug_frame";
- shstrtab += '\0';
- uint32_t shstrtab_size = shstrtab.size();
- expected_offset += shstrtab_size;
- if (debug) {
- LOG(INFO) << "shstrtab_offset=" << shstrtab_offset << std::hex << " " << shstrtab_offset;
- LOG(INFO) << "shstrtab_size=" << shstrtab_size << std::hex << " " << shstrtab_size;
+
+ // Setup sym_undef
+ Elf32_Shdr null_hdr;
+ 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);
+
+ uint32_t 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++;
+
+ 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++;
+ }
+ ElfRawSectionBuilder* it = other_builders_.data();
+ for (uint32_t cnt = 0; cnt < other_builders_.size(); ++it, ++cnt) {
+ // Setup all the other sections.
+ section_ptrs.push_back(&it->section_);
+ AssignSectionStr(it, &shstrtab);
+ it->section_index_ = section_index++;
}
- // Create debug informatin, if we have it.
- bool generateDebugInformation = compiler_driver_->GetCallFrameInformation() != nullptr;
- std::vector<uint8_t> dbg_info;
- std::vector<uint8_t> dbg_abbrev;
- std::vector<uint8_t> dbg_str;
- if (generateDebugInformation) {
- FillInCFIInformation(oat_writer, &dbg_info, &dbg_abbrev, &dbg_str);
+ // 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();
}
- uint32_t shdbg_info_alignment = 1;
- uint32_t shdbg_info_offset = expected_offset;
- uint32_t shdbg_info_size = dbg_info.size();
- expected_offset += shdbg_info_size;
- if (debug) {
- LOG(INFO) << "shdbg_info_offset=" << shdbg_info_offset << std::hex << " " << shdbg_info_offset;
- LOG(INFO) << "shdbg_info_size=" << shdbg_info_size << std::hex << " " << shdbg_info_size;
+ // Fill in the hash section.
+ std::vector<Elf32_Word> hash = dynsym_builder_.GenerateHashContents();
+
+ if (debug_logging_) {
+ LOG(INFO) << ".hash size (bytes)=" << hash.size() * sizeof(Elf32_Word)
+ << std::hex << " " << hash.size() * sizeof(Elf32_Word);
}
- uint32_t shdbg_abbrev_alignment = 1;
- uint32_t shdbg_abbrev_offset = expected_offset;
- uint32_t shdbg_abbrev_size = dbg_abbrev.size();
- expected_offset += shdbg_abbrev_size;
- if (debug) {
- LOG(INFO) << "shdbg_abbrev_offset=" << shdbg_abbrev_offset << std::hex << " " << shdbg_abbrev_offset;
- LOG(INFO) << "shdbg_abbrev_size=" << shdbg_abbrev_size << std::hex << " " << shdbg_abbrev_size;
+ Elf32_Word base_offset = sizeof(Elf32_Ehdr) + sizeof(program_headers);
+ std::vector<ElfFilePiece> pieces;
+
+ // 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(Elf32_Sym);
+ dynsym_builder_.section_.sh_link = dynsym_builder_.GetLink();
+
+ // Get the layout of the dynstr section.
+ dynsym_builder_.strtab_.section_.sh_offset = NextOffset(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(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(Elf32_Word);
+ hash_builder_.section_.sh_link = hash_builder_.GetLink();
+
+ // Get the layout of the rodata section.
+ rodata_builder_.section_.sh_offset = NextOffset(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(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(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(Elf32_Dyn);
+ dynamic_builder_.section_.sh_link = dynamic_builder_.GetLink();
+
+ Elf32_Shdr prev = dynamic_builder_.section_;
+ if (IncludingDebugSymbols()) {
+ // Get the layout of the symtab section.
+ symtab_builder_.section_.sh_offset = NextOffset(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(Elf32_Sym);
+ symtab_builder_.section_.sh_link = symtab_builder_.GetLink();
+
+ // Get the layout of the dynstr section.
+ symtab_builder_.strtab_.section_.sh_offset = NextOffset(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_;
}
-
- uint32_t shdbg_frm_alignment = 4;
- uint32_t shdbg_frm_offset = expected_offset = RoundUp(expected_offset, shdbg_frm_alignment);
- uint32_t shdbg_frm_size =
- generateDebugInformation ? compiler_driver_->GetCallFrameInformation()->size() : 0;
- expected_offset += shdbg_frm_size;
- if (debug) {
- LOG(INFO) << "shdbg_frm_offset=" << shdbg_frm_offset << std::hex << " " << shdbg_frm_offset;
- LOG(INFO) << "shdbg_frm_size=" << shdbg_frm_size << std::hex << " " << shdbg_frm_size;
- }
-
- uint32_t shdbg_str_alignment = 1;
- uint32_t shdbg_str_offset = expected_offset;
- uint32_t shdbg_str_size = dbg_str.size();
- expected_offset += shdbg_str_size;
- if (debug) {
- LOG(INFO) << "shdbg_str_offset=" << shdbg_str_offset << std::hex << " " << shdbg_str_offset;
- LOG(INFO) << "shdbg_str_size=" << shdbg_str_size << std::hex << " " << shdbg_str_size;
- }
-
- // section headers (after all sections)
- uint32_t shdr_alignment = sizeof(Elf32_Word);
- uint32_t shdr_offset = expected_offset = RoundUp(expected_offset, shdr_alignment);
- const uint8_t SH_NULL = 0;
- const uint8_t SH_DYNSYM = 1;
- const uint8_t SH_DYNSTR = 2;
- const uint8_t SH_HASH = 3;
- const uint8_t SH_RODATA = 4;
- const uint8_t SH_TEXT = 5;
- const uint8_t SH_DYNAMIC = 6;
- const uint8_t SH_SHSTRTAB = 7;
- const uint8_t SH_DBG_INFO = 8;
- const uint8_t SH_DBG_ABRV = 9;
- const uint8_t SH_DBG_FRM = 10;
- const uint8_t SH_DBG_STR = 11;
- const uint8_t SH_NUM = generateDebugInformation ? 12 : 8;
- uint32_t shdr_size = sizeof(Elf32_Shdr) * SH_NUM;
- expected_offset += shdr_size;
- if (debug) {
- LOG(INFO) << "shdr_offset=" << shdr_offset << std::hex << " " << shdr_offset;
- LOG(INFO) << "shdr_size=" << shdr_size << std::hex << " " << shdr_size;
- }
-
- // phase 2: initializing data
-
- // Elf32_Ehdr
- Elf32_Ehdr elf_header;
- 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;
- switch (compiler_driver_->GetInstructionSet()) {
- 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: {
- LOG(FATAL) << "Unknown instruction set: " << compiler_driver_->GetInstructionSet();
- break;
+ 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;
+ if (IncludingDebugSymbols()) {
+ 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;
}
}
- elf_header.e_version = 1;
- elf_header.e_entry = 0;
- elf_header.e_phoff = phdr_offset;
- elf_header.e_shoff = shdr_offset;
- elf_header.e_ehsize = sizeof(Elf32_Ehdr);
- elf_header.e_phentsize = sizeof(Elf32_Phdr);
- elf_header.e_phnum = PH_NUM;
- elf_header.e_shentsize = sizeof(Elf32_Shdr);
- elf_header.e_shnum = SH_NUM;
- elf_header.e_shstrndx = SH_SHSTRTAB;
-
- // PHDR
- Elf32_Phdr program_headers[PH_NUM];
- 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 = phdr_alignment;
-
- 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_filesz = oat_data_offset + oat_data_size;
- program_headers[PH_LOAD_R__].p_memsz = oat_data_offset + oat_data_size;
- program_headers[PH_LOAD_R__].p_flags = PF_R;
- program_headers[PH_LOAD_R__].p_align = oat_data_alignment;
-
- program_headers[PH_LOAD_R_X].p_type = PT_LOAD;
- program_headers[PH_LOAD_R_X].p_offset = oat_exec_offset;
- program_headers[PH_LOAD_R_X].p_vaddr = oat_exec_offset;
- program_headers[PH_LOAD_R_X].p_paddr = oat_exec_offset;
- program_headers[PH_LOAD_R_X].p_filesz = oat_exec_size;
- program_headers[PH_LOAD_R_X].p_memsz = oat_exec_size;
- program_headers[PH_LOAD_R_X].p_flags = PF_R | PF_X;
- program_headers[PH_LOAD_R_X].p_align = oat_exec_alignment;
-
- // TODO: PF_W for DYNAMIC is considered processor specific, do we need it?
- program_headers[PH_LOAD_RW_].p_type = PT_LOAD;
- program_headers[PH_LOAD_RW_].p_offset = dynamic_offset;
- program_headers[PH_LOAD_RW_].p_vaddr = dynamic_offset;
- program_headers[PH_LOAD_RW_].p_paddr = dynamic_offset;
- program_headers[PH_LOAD_RW_].p_filesz = dynamic_size;
- program_headers[PH_LOAD_RW_].p_memsz = dynamic_size;
- program_headers[PH_LOAD_RW_].p_flags = PF_R | PF_W;
- program_headers[PH_LOAD_RW_].p_align = dynamic_alignment;
-
- // TODO: PF_W for DYNAMIC is considered processor specific, do we need it?
- program_headers[PH_DYNAMIC].p_type = PT_DYNAMIC;
- program_headers[PH_DYNAMIC].p_offset = dynamic_offset;
- program_headers[PH_DYNAMIC].p_vaddr = dynamic_offset;
- program_headers[PH_DYNAMIC].p_paddr = dynamic_offset;
- program_headers[PH_DYNAMIC].p_filesz = dynamic_size;
- program_headers[PH_DYNAMIC].p_memsz = dynamic_size;
- program_headers[PH_DYNAMIC].p_flags = PF_R | PF_W;
- program_headers[PH_DYNAMIC].p_align = dynamic_alignment;
-
- // .dynsym
- Elf32_Sym dynsym[SYM_NUM];
- memset(&dynsym, 0, sizeof(dynsym));
-
- dynsym[SYM_UNDEF].st_name = 0;
- dynsym[SYM_UNDEF].st_value = 0;
- dynsym[SYM_UNDEF].st_size = 0;
- dynsym[SYM_UNDEF].st_info = 0;
- dynsym[SYM_UNDEF].st_other = 0;
- dynsym[SYM_UNDEF].st_shndx = 0;
-
- dynsym[SYM_OATDATA].st_name = dynstr_oatdata_offset;
- dynsym[SYM_OATDATA].st_value = oat_data_offset;
- dynsym[SYM_OATDATA].st_size = oat_data_size;
- SetBindingAndType(&dynsym[SYM_OATDATA], STB_GLOBAL, STT_OBJECT);
- dynsym[SYM_OATDATA].st_other = STV_DEFAULT;
- dynsym[SYM_OATDATA].st_shndx = SH_RODATA;
-
- dynsym[SYM_OATEXEC].st_name = dynstr_oatexec_offset;
- dynsym[SYM_OATEXEC].st_value = oat_exec_offset;
- dynsym[SYM_OATEXEC].st_size = oat_exec_size;
- SetBindingAndType(&dynsym[SYM_OATEXEC], STB_GLOBAL, STT_OBJECT);
- dynsym[SYM_OATEXEC].st_other = STV_DEFAULT;
- dynsym[SYM_OATEXEC].st_shndx = SH_TEXT;
-
- dynsym[SYM_OATLASTWORD].st_name = dynstr_oatlastword_offset;
- dynsym[SYM_OATLASTWORD].st_value = oat_exec_offset + oat_exec_size - 4;
- dynsym[SYM_OATLASTWORD].st_size = 4;
- SetBindingAndType(&dynsym[SYM_OATLASTWORD], STB_GLOBAL, STT_OBJECT);
- dynsym[SYM_OATLASTWORD].st_other = STV_DEFAULT;
- dynsym[SYM_OATLASTWORD].st_shndx = SH_TEXT;
-
- // .dynstr initialized above as dynstr
-
- // .hash
- Elf32_Word hash[HASH_NUM]; // Note this is Elf32_Word even on 64-bit
- hash[HASH_NBUCKET] = 1;
- hash[HASH_NCHAIN] = SYM_NUM;
- hash[HASH_BUCKET0] = SYM_OATDATA;
- hash[HASH_BUCKET0 + 1 + SYM_UNDEF] = SYM_UNDEF;
- hash[HASH_BUCKET0 + 1 + SYM_OATDATA] = SYM_OATEXEC;
- hash[HASH_BUCKET0 + 1 + SYM_OATEXEC] = SYM_OATLASTWORD;
- hash[HASH_BUCKET0 + 1 + SYM_OATLASTWORD] = SYM_UNDEF;
-
- // .rodata and .text content come from oat_contents
-
- // .dynamic
- Elf32_Dyn dynamic_headers[DH_NUM];
- memset(&dynamic_headers, 0, sizeof(dynamic_headers));
-
- dynamic_headers[DH_SONAME].d_tag = DT_SONAME;
- dynamic_headers[DH_SONAME].d_un.d_val = dynstr_soname_offset;
-
- dynamic_headers[DH_HASH].d_tag = DT_HASH;
- dynamic_headers[DH_HASH].d_un.d_ptr = hash_offset;
-
- dynamic_headers[DH_SYMTAB].d_tag = DT_SYMTAB;
- dynamic_headers[DH_SYMTAB].d_un.d_ptr = dynsym_offset;
-
- dynamic_headers[DH_SYMENT].d_tag = DT_SYMENT;
- dynamic_headers[DH_SYMENT].d_un.d_val = sizeof(Elf32_Sym);
-
- dynamic_headers[DH_STRTAB].d_tag = DT_STRTAB;
- dynamic_headers[DH_STRTAB].d_un.d_ptr = dynstr_offset;
-
- dynamic_headers[DH_STRSZ].d_tag = DT_STRSZ;
- dynamic_headers[DH_STRSZ].d_un.d_val = dynstr_size;
-
- dynamic_headers[DH_NULL].d_tag = DT_NULL;
- dynamic_headers[DH_NULL].d_un.d_val = 0;
-
- // .shstrtab initialized above as shstrtab
-
- // section headers (after all sections)
- Elf32_Shdr section_headers[SH_NUM];
- memset(§ion_headers, 0, sizeof(section_headers));
-
- section_headers[SH_NULL].sh_name = 0;
- section_headers[SH_NULL].sh_type = SHT_NULL;
- section_headers[SH_NULL].sh_flags = 0;
- section_headers[SH_NULL].sh_addr = 0;
- section_headers[SH_NULL].sh_offset = 0;
- section_headers[SH_NULL].sh_size = 0;
- section_headers[SH_NULL].sh_link = 0;
- section_headers[SH_NULL].sh_info = 0;
- section_headers[SH_NULL].sh_addralign = 0;
- section_headers[SH_NULL].sh_entsize = 0;
-
- section_headers[SH_DYNSYM].sh_name = shstrtab_dynsym_offset;
- section_headers[SH_DYNSYM].sh_type = SHT_DYNSYM;
- section_headers[SH_DYNSYM].sh_flags = SHF_ALLOC;
- section_headers[SH_DYNSYM].sh_addr = dynsym_offset;
- section_headers[SH_DYNSYM].sh_offset = dynsym_offset;
- section_headers[SH_DYNSYM].sh_size = dynsym_size;
- section_headers[SH_DYNSYM].sh_link = SH_DYNSTR;
- section_headers[SH_DYNSYM].sh_info = 1; // 1 because we have not STB_LOCAL symbols
- section_headers[SH_DYNSYM].sh_addralign = dynsym_alignment;
- section_headers[SH_DYNSYM].sh_entsize = sizeof(Elf32_Sym);
-
- section_headers[SH_DYNSTR].sh_name = shstrtab_dynstr_offset;
- section_headers[SH_DYNSTR].sh_type = SHT_STRTAB;
- section_headers[SH_DYNSTR].sh_flags = SHF_ALLOC;
- section_headers[SH_DYNSTR].sh_addr = dynstr_offset;
- section_headers[SH_DYNSTR].sh_offset = dynstr_offset;
- section_headers[SH_DYNSTR].sh_size = dynstr_size;
- section_headers[SH_DYNSTR].sh_link = 0;
- section_headers[SH_DYNSTR].sh_info = 0;
- section_headers[SH_DYNSTR].sh_addralign = dynstr_alignment;
- section_headers[SH_DYNSTR].sh_entsize = 0;
-
- section_headers[SH_HASH].sh_name = shstrtab_hash_offset;
- section_headers[SH_HASH].sh_type = SHT_HASH;
- section_headers[SH_HASH].sh_flags = SHF_ALLOC;
- section_headers[SH_HASH].sh_addr = hash_offset;
- section_headers[SH_HASH].sh_offset = hash_offset;
- section_headers[SH_HASH].sh_size = hash_size;
- section_headers[SH_HASH].sh_link = SH_DYNSYM;
- section_headers[SH_HASH].sh_info = 0;
- section_headers[SH_HASH].sh_addralign = hash_alignment;
- section_headers[SH_HASH].sh_entsize = sizeof(Elf32_Word); // This is Elf32_Word even on 64-bit
-
- section_headers[SH_RODATA].sh_name = shstrtab_rodata_offset;
- section_headers[SH_RODATA].sh_type = SHT_PROGBITS;
- section_headers[SH_RODATA].sh_flags = SHF_ALLOC;
- section_headers[SH_RODATA].sh_addr = oat_data_offset;
- section_headers[SH_RODATA].sh_offset = oat_data_offset;
- section_headers[SH_RODATA].sh_size = oat_data_size;
- section_headers[SH_RODATA].sh_link = 0;
- section_headers[SH_RODATA].sh_info = 0;
- section_headers[SH_RODATA].sh_addralign = oat_data_alignment;
- section_headers[SH_RODATA].sh_entsize = 0;
-
- section_headers[SH_TEXT].sh_name = shstrtab_text_offset;
- section_headers[SH_TEXT].sh_type = SHT_PROGBITS;
- section_headers[SH_TEXT].sh_flags = SHF_ALLOC | SHF_EXECINSTR;
- section_headers[SH_TEXT].sh_addr = oat_exec_offset;
- section_headers[SH_TEXT].sh_offset = oat_exec_offset;
- section_headers[SH_TEXT].sh_size = oat_exec_size;
- section_headers[SH_TEXT].sh_link = 0;
- section_headers[SH_TEXT].sh_info = 0;
- section_headers[SH_TEXT].sh_addralign = oat_exec_alignment;
- section_headers[SH_TEXT].sh_entsize = 0;
-
- // TODO: SHF_WRITE for .dynamic is considered processor specific, do we need it?
- section_headers[SH_DYNAMIC].sh_name = shstrtab_dynamic_offset;
- section_headers[SH_DYNAMIC].sh_type = SHT_DYNAMIC;
- section_headers[SH_DYNAMIC].sh_flags = SHF_WRITE | SHF_ALLOC;
- section_headers[SH_DYNAMIC].sh_addr = dynamic_offset;
- section_headers[SH_DYNAMIC].sh_offset = dynamic_offset;
- section_headers[SH_DYNAMIC].sh_size = dynamic_size;
- section_headers[SH_DYNAMIC].sh_link = SH_DYNSTR;
- section_headers[SH_DYNAMIC].sh_info = 0;
- section_headers[SH_DYNAMIC].sh_addralign = dynamic_alignment;
- section_headers[SH_DYNAMIC].sh_entsize = sizeof(Elf32_Dyn);
-
- section_headers[SH_SHSTRTAB].sh_name = shstrtab_shstrtab_offset;
- section_headers[SH_SHSTRTAB].sh_type = SHT_STRTAB;
- section_headers[SH_SHSTRTAB].sh_flags = 0;
- section_headers[SH_SHSTRTAB].sh_addr = shstrtab_offset;
- section_headers[SH_SHSTRTAB].sh_offset = shstrtab_offset;
- section_headers[SH_SHSTRTAB].sh_size = shstrtab_size;
- section_headers[SH_SHSTRTAB].sh_link = 0;
- section_headers[SH_SHSTRTAB].sh_info = 0;
- section_headers[SH_SHSTRTAB].sh_addralign = shstrtab_alignment;
- section_headers[SH_SHSTRTAB].sh_entsize = 0;
-
- if (generateDebugInformation) {
- section_headers[SH_DBG_INFO].sh_name = shstrtab_debug_info_offset;
- section_headers[SH_DBG_INFO].sh_type = SHT_PROGBITS;
- section_headers[SH_DBG_INFO].sh_flags = 0;
- section_headers[SH_DBG_INFO].sh_addr = 0;
- section_headers[SH_DBG_INFO].sh_offset = shdbg_info_offset;
- section_headers[SH_DBG_INFO].sh_size = shdbg_info_size;
- section_headers[SH_DBG_INFO].sh_link = 0;
- section_headers[SH_DBG_INFO].sh_info = 0;
- section_headers[SH_DBG_INFO].sh_addralign = shdbg_info_alignment;
- section_headers[SH_DBG_INFO].sh_entsize = 0;
-
- section_headers[SH_DBG_ABRV].sh_name = shstrtab_debug_abbrev_offset;
- section_headers[SH_DBG_ABRV].sh_type = SHT_PROGBITS;
- section_headers[SH_DBG_ABRV].sh_flags = 0;
- section_headers[SH_DBG_ABRV].sh_addr = 0;
- section_headers[SH_DBG_ABRV].sh_offset = shdbg_abbrev_offset;
- section_headers[SH_DBG_ABRV].sh_size = shdbg_abbrev_size;
- section_headers[SH_DBG_ABRV].sh_link = 0;
- section_headers[SH_DBG_ABRV].sh_info = 0;
- section_headers[SH_DBG_ABRV].sh_addralign = shdbg_abbrev_alignment;
- section_headers[SH_DBG_ABRV].sh_entsize = 0;
-
- section_headers[SH_DBG_FRM].sh_name = shstrtab_debug_frame_offset;
- section_headers[SH_DBG_FRM].sh_type = SHT_PROGBITS;
- section_headers[SH_DBG_FRM].sh_flags = 0;
- section_headers[SH_DBG_FRM].sh_addr = 0;
- section_headers[SH_DBG_FRM].sh_offset = shdbg_frm_offset;
- section_headers[SH_DBG_FRM].sh_size = shdbg_frm_size;
- section_headers[SH_DBG_FRM].sh_link = 0;
- section_headers[SH_DBG_FRM].sh_info = 0;
- section_headers[SH_DBG_FRM].sh_addralign = shdbg_frm_alignment;
- section_headers[SH_DBG_FRM].sh_entsize = 0;
-
- section_headers[SH_DBG_STR].sh_name = shstrtab_debug_str_offset;
- section_headers[SH_DBG_STR].sh_type = SHT_PROGBITS;
- section_headers[SH_DBG_STR].sh_flags = 0;
- section_headers[SH_DBG_STR].sh_addr = 0;
- section_headers[SH_DBG_STR].sh_offset = shdbg_str_offset;
- section_headers[SH_DBG_STR].sh_size = shdbg_str_size;
- section_headers[SH_DBG_STR].sh_link = 0;
- section_headers[SH_DBG_STR].sh_info = 0;
- section_headers[SH_DBG_STR].sh_addralign = shdbg_str_alignment;
- section_headers[SH_DBG_STR].sh_entsize = 0;
+ // 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(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(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;
}
- // phase 3: writing file
+ // The section list comes after come after.
+ Elf32_Word sections_offset = RoundUp(
+ shstrtab_builder_.section_.sh_offset + shstrtab_builder_.section_.sh_size,
+ sizeof(Elf32_Word));
- // Elf32_Ehdr
- if (!elf_file_->WriteFully(&elf_header, sizeof(elf_header))) {
- PLOG(ERROR) << "Failed to write ELF header for " << elf_file_->GetPath();
+ // Setup the actual symbol arrays.
+ std::vector<Elf32_Sym> dynsym = dynsym_builder_.GenerateSymtab();
+ CHECK_EQ(dynsym.size() * sizeof(Elf32_Sym), dynsym_builder_.section_.sh_size);
+ std::vector<Elf32_Sym> symtab;
+ if (IncludingDebugSymbols()) {
+ symtab = symtab_builder_.GenerateSymtab();
+ CHECK_EQ(symtab.size() * sizeof(Elf32_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<Elf32_Dyn> dynamic = dynamic_builder_.GetDynamics(dynstr.size(),
+ dynstr_soname_offset);
+ CHECK_EQ(dynamic.size() * sizeof(Elf32_Dyn), dynamic_builder_.section_.sh_size);
+
+ // Finish setup of the program headers now that we know the layout of the
+ // whole file.
+ Elf32_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;
+
+ Elf32_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(Elf32_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(Elf32_Word)));
+ pieces.push_back(ElfFilePiece(".rodata", rodata_builder_.section_.sh_offset,
+ NULL, rodata_builder_.section_.sh_size));
+ pieces.push_back(ElfFilePiece(".text", text_builder_.section_.sh_offset,
+ NULL, text_builder_.section_.sh_size));
+ if (IncludingDebugSymbols()) {
+ pieces.push_back(ElfFilePiece(".symtab", symtab_builder_.section_.sh_offset,
+ symtab.data(), symtab.size() * sizeof(Elf32_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.
+ Elf32_Word cur_off = sections_offset + i * sizeof(Elf32_Shdr);
+ pieces.push_back(ElfFilePiece("section table piece", cur_off,
+ section_ptrs[i], sizeof(Elf32_Shdr)));
+ }
+
+ if (!WriteOutFile(pieces)) {
+ LOG(ERROR) << "Unable to write to file " << elf_file_->GetPath();
return false;
}
-
- // PHDR
- if (static_cast<off_t>(phdr_offset) != lseek(elf_file_->Fd(), 0, SEEK_CUR)) {
- PLOG(ERROR) << "Failed to be at expected ELF program header offset phdr_offset "
- << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(program_headers, sizeof(program_headers))) {
- PLOG(ERROR) << "Failed to write ELF program headers for " << elf_file_->GetPath();
- return false;
- }
-
- // .dynsym
- DCHECK_LE(phdr_offset + phdr_size, dynsym_offset);
- if (static_cast<off_t>(dynsym_offset) != lseek(elf_file_->Fd(), dynsym_offset, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to .dynsym offset location " << dynsym_offset
- << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(dynsym, sizeof(dynsym))) {
- PLOG(ERROR) << "Failed to write .dynsym for " << elf_file_->GetPath();
- return false;
- }
-
- // .dynstr
- DCHECK_LE(dynsym_offset + dynsym_size, dynstr_offset);
- if (static_cast<off_t>(dynstr_offset) != lseek(elf_file_->Fd(), dynstr_offset, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to .dynstr offset " << dynstr_offset
- << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(&dynstr[0], dynstr_size)) {
- PLOG(ERROR) << "Failed to write .dynsym for " << elf_file_->GetPath();
- return false;
- }
-
- // .hash
- DCHECK_LE(dynstr_offset + dynstr_size, hash_offset);
- if (static_cast<off_t>(hash_offset) != lseek(elf_file_->Fd(), hash_offset, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to .hash offset " << hash_offset
- << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(hash, sizeof(hash))) {
- PLOG(ERROR) << "Failed to write .dynsym for " << elf_file_->GetPath();
- return false;
- }
-
- // .rodata .text
- DCHECK_LE(hash_offset + hash_size, oat_data_offset);
+ // write out the actual oat file data.
+ Elf32_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())) {
+ 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;
}
- // .dynamic
- DCHECK_LE(oat_data_offset + oat_writer->GetSize(), dynamic_offset);
- if (static_cast<off_t>(dynamic_offset) != lseek(elf_file_->Fd(), dynamic_offset, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to .dynamic offset " << dynamic_offset
- << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(&dynamic_headers[0], dynamic_size)) {
- PLOG(ERROR) << "Failed to write .dynamic for " << elf_file_->GetPath();
- return false;
- }
-
- // .shstrtab
- DCHECK_LE(dynamic_offset + dynamic_size, shstrtab_offset);
- if (static_cast<off_t>(shstrtab_offset) != lseek(elf_file_->Fd(), shstrtab_offset, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to .shstrtab offset " << shstrtab_offset
- << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(&shstrtab[0], shstrtab_size)) {
- PLOG(ERROR) << "Failed to write .shstrtab for " << elf_file_->GetPath();
- return false;
- }
-
- if (generateDebugInformation) {
- // .debug_info
- DCHECK_LE(shstrtab_offset + shstrtab_size, shdbg_info_offset);
- if (static_cast<off_t>(shdbg_info_offset) != lseek(elf_file_->Fd(), shdbg_info_offset, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to .shdbg_info offset " << shdbg_info_offset
- << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(&dbg_info[0], shdbg_info_size)) {
- PLOG(ERROR) << "Failed to write .debug_info for " << elf_file_->GetPath();
- return false;
- }
-
- // .debug_abbrev
- DCHECK_LE(shdbg_info_offset + shdbg_info_size, shdbg_abbrev_offset);
- if (static_cast<off_t>(shdbg_abbrev_offset) != lseek(elf_file_->Fd(), shdbg_abbrev_offset, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to .shdbg_abbrev offset " << shdbg_abbrev_offset
- << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(&dbg_abbrev[0], shdbg_abbrev_size)) {
- PLOG(ERROR) << "Failed to write .debug_abbrev for " << elf_file_->GetPath();
- return false;
- }
-
- // .debug_frame
- DCHECK_LE(shdbg_abbrev_offset + shdbg_abbrev_size, shdbg_frm_offset);
- if (static_cast<off_t>(shdbg_frm_offset) != lseek(elf_file_->Fd(), shdbg_frm_offset, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to .shdbg_frm offset " << shdbg_frm_offset
- << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(&((*compiler_driver_->GetCallFrameInformation())[0]), shdbg_frm_size)) {
- PLOG(ERROR) << "Failed to write .debug_frame for " << elf_file_->GetPath();
- return false;
- }
-
- // .debug_str
- DCHECK_LE(shdbg_frm_offset + shdbg_frm_size, shdbg_str_offset);
- if (static_cast<off_t>(shdbg_str_offset) != lseek(elf_file_->Fd(), shdbg_str_offset, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to .shdbg_str offset " << shdbg_str_offset
- << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(&dbg_str[0], shdbg_str_size)) {
- PLOG(ERROR) << "Failed to write .debug_frame for " << elf_file_->GetPath();
- return false;
- }
- }
-
- // section headers (after all sections)
- if (generateDebugInformation) {
- DCHECK_LE(shdbg_str_offset + shdbg_str_size, shdr_offset);
- } else {
- DCHECK_LE(shstrtab_offset + shstrtab_size, shdr_offset);
- }
- if (static_cast<off_t>(shdr_offset) != lseek(elf_file_->Fd(), shdr_offset, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to ELF section headers offset " << shdr_offset
- << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(section_headers, sizeof(section_headers))) {
- PLOG(ERROR) << "Failed to write ELF section headers for " << elf_file_->GetPath();
- return false;
- }
-
- VLOG(compiler) << "ELF file written successfully: " << elf_file_->GetPath();
return true;
-} // NOLINT(readability/fn_size)
+}
+
+bool ElfWriterQuick::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;
+}
+
+void ElfWriterQuick::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(Elf32_Sym));
+}
+
+void ElfWriterQuick::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);
+}
+
+void ElfWriterQuick::ElfDynamicBuilder::AddDynamicTag(Elf32_Sword tag, Elf32_Word d_un) {
+ if (tag == DT_NULL) {
+ return;
+ }
+ dynamics_.push_back({NULL, tag, d_un});
+}
+
+void ElfWriterQuick::ElfDynamicBuilder::AddDynamicTag(Elf32_Sword tag, Elf32_Word d_un,
+ ElfSectionBuilder* section) {
+ if (tag == DT_NULL) {
+ return;
+ }
+ dynamics_.push_back({section, tag, d_un});
+}
+
+std::vector<Elf32_Dyn> ElfWriterQuick::ElfDynamicBuilder::GetDynamics(Elf32_Word strsz,
+ Elf32_Word soname) {
+ std::vector<Elf32_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;
+}
+
+std::vector<Elf32_Sym> ElfWriterQuick::ElfSymtabBuilder::GenerateSymtab() {
+ std::vector<Elf32_Sym> ret;
+ Elf32_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) {
+ Elf32_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;
+}
+
+std::string ElfWriterQuick::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;
+}
+
+void ElfWriterQuick::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;
+}
+
+
+std::vector<Elf32_Word> ElfWriterQuick::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.
+ Elf32_Word nbuckets;
+ Elf32_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<Elf32_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);
+
+ Elf32_Word* buckets = hash.data() + bucket_offset;
+ Elf32_Word* chain = hash.data() + chain_offset;
+
+ // Set up the actual hash table.
+ for (Elf32_Word i = 0; i < symbols_.size(); i++) {
+ // Add 1 since we need to have the null symbol that is not in the symbols
+ // list.
+ Elf32_Word index = i + 1;
+ Elf32_Word hash_val = static_cast<Elf32_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<Elf32_Word>(0));
+ }
+ }
+
+ return hash;
+}
+
+void ElfWriterQuick::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(Elf32_Ehdr);
+ elf_header_.e_phentsize = sizeof(Elf32_Phdr);
+ elf_header_.e_shentsize = sizeof(Elf32_Shdr);
+ elf_header_.e_phoff = sizeof(Elf32_Ehdr);
+}
+
+void ElfWriterQuick::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;
+ }
+ }
+}
+
+void ElfWriterQuick::ElfSymtabBuilder::AddSymbol(
+ const std::string& name, const ElfSectionBuilder* section, Elf32_Addr addr,
+ bool is_relative, Elf32_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);
+}
+
+bool ElfWriterQuick::Create(File* elf_file,
+ OatWriter* oat_writer,
+ const std::vector<const DexFile*>& dex_files,
+ const std::string& android_root,
+ bool is_host,
+ const CompilerDriver& driver) {
+ ElfWriterQuick elf_writer(driver, elf_file);
+ return elf_writer.Write(oat_writer, dex_files, android_root, is_host);
+}
+
+bool ElfWriterQuick::Write(OatWriter* oat_writer,
+ const std::vector<const DexFile*>& dex_files_unused,
+ const std::string& android_root_unused,
+ bool is_host_unused) {
+ const bool debug = false;
+ const OatHeader& oat_header = oat_writer->GetOatHeader();
+ Elf32_Word oat_data_size = oat_header.GetExecutableOffset();
+ uint32_t oat_exec_size = oat_writer->GetSize() - oat_data_size;
+
+ ElfBuilder builder(oat_writer, elf_file_, compiler_driver_->GetInstructionSet(), 0,
+ oat_data_size, oat_data_size, oat_exec_size, false, debug);
+
+ bool generateDebugInformation = compiler_driver_->GetCallFrameInformation() != nullptr;
+ if (generateDebugInformation) {
+ ElfRawSectionBuilder debug_info(".debug_info", SHT_PROGBITS, 0, NULL, 0, 1, 0);
+ ElfRawSectionBuilder debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, NULL, 0, 1, 0);
+ ElfRawSectionBuilder debug_str(".debug_str", SHT_PROGBITS, 0, NULL, 0, 1, 0);
+ ElfRawSectionBuilder debug_frame(".debug_frame", SHT_PROGBITS, 0, NULL, 0, 4, 0);
+ debug_frame.SetBuffer(*compiler_driver_->GetCallFrameInformation());
+
+ FillInCFIInformation(oat_writer, debug_info.GetBuffer(),
+ debug_abbrev.GetBuffer(), debug_str.GetBuffer());
+ builder.RegisterRawSection(debug_info);
+ builder.RegisterRawSection(debug_abbrev);
+ builder.RegisterRawSection(debug_frame);
+ builder.RegisterRawSection(debug_str);
+ }
+
+ return builder.Write();
+}
static void UpdateWord(std::vector<uint8_t>*buf, int offset, int data) {
(*buf)[offset+0] = data;
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index dec75dc..f687d2e 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -17,7 +17,9 @@
#ifndef ART_COMPILER_ELF_WRITER_QUICK_H_
#define ART_COMPILER_ELF_WRITER_QUICK_H_
+#include "elf_utils.h"
#include "elf_writer.h"
+#include "instruction_set.h"
namespace art {
@@ -45,6 +47,265 @@
: ElfWriter(driver, elf_file) {}
~ElfWriterQuick() {}
+ class ElfBuilder;
+ class ElfSectionBuilder {
+ public:
+ ElfSectionBuilder(const std::string& sec_name, Elf32_Word type, Elf32_Word flags,
+ const ElfSectionBuilder *link, Elf32_Word info, Elf32_Word align,
+ Elf32_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() {}
+
+ Elf32_Shdr section_;
+ Elf32_Word section_index_ = 0;
+
+ protected:
+ const std::string name_;
+ const ElfSectionBuilder* link_;
+
+ Elf32_Word GetLink() {
+ return (link_) ? link_->section_index_ : 0;
+ }
+
+ private:
+ friend class ElfBuilder;
+ };
+
+ class ElfDynamicBuilder : public ElfSectionBuilder {
+ public:
+ void AddDynamicTag(Elf32_Sword tag, Elf32_Word d_un);
+ void AddDynamicTag(Elf32_Sword tag, Elf32_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(Elf32_Dyn)) {}
+ ~ElfDynamicBuilder() {}
+
+ protected:
+ struct ElfDynamicState {
+ ElfSectionBuilder* section_;
+ Elf32_Sword tag_;
+ Elf32_Word off_;
+ };
+ std::vector<ElfDynamicState> dynamics_;
+ Elf32_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<Elf32_Dyn> GetDynamics(Elf32_Word strsz, Elf32_Word soname_off);
+
+ private:
+ friend class ElfBuilder;
+ };
+
+ class ElfRawSectionBuilder : public ElfSectionBuilder {
+ public:
+ ElfRawSectionBuilder(const std::string& sec_name, Elf32_Word type, Elf32_Word flags,
+ const ElfSectionBuilder* link, Elf32_Word info, Elf32_Word align,
+ Elf32_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, Elf32_Word size, Elf32_Word offset,
+ Elf32_Word type, Elf32_Word flags)
+ : ElfSectionBuilder(sec_name, type, flags, NULL, 0, kPageSize, 0),
+ offset_(offset), size_(size) {}
+ ~ElfOatSectionBuilder() {}
+
+ protected:
+ // Offset of the content within the file.
+ Elf32_Word offset_;
+ // Size of the content within the file.
+ Elf32_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,
+ Elf32_Addr addr,
+ bool is_relative,
+ Elf32_Word size,
+ uint8_t binding,
+ uint8_t type,
+ uint8_t other = 0);
+
+ ElfSymtabBuilder(const std::string& sec_name, Elf32_Word type,
+ const std::string& str_name, Elf32_Word str_type, bool alloc)
+ : ElfSectionBuilder(sec_name, type, ((alloc)?SHF_ALLOC:0), &strtab_, 0,
+ sizeof(Elf32_Word), sizeof(Elf32_Sym)),
+ str_name_(str_name), str_type_(str_type),
+ strtab_(str_name, str_type, ((alloc) ? SHF_ALLOC : 0), NULL, 0, 1, 1) {}
+ ~ElfSymtabBuilder() {}
+
+ protected:
+ std::vector<Elf32_Word> GenerateHashContents();
+ std::string GenerateStrtab();
+ std::vector<Elf32_Sym> GenerateSymtab();
+
+ Elf32_Word GetSize() {
+ // 1 is for the implicit NULL symbol.
+ return symbols_.size() + 1;
+ }
+
+ struct ElfSymbolState {
+ const std::string name_;
+ const ElfSectionBuilder* section_;
+ Elf32_Addr addr_;
+ Elf32_Word size_;
+ bool is_relative_;
+ uint8_t info_;
+ uint8_t other_;
+ // Used during Write() to temporarially hold name index in the strtab.
+ Elf32_Word name_idx_;
+ };
+
+ // Information for the strsym for dynstr sections.
+ const std::string str_name_;
+ Elf32_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,
+ Elf32_Word rodata_relative_offset,
+ Elf32_Word rodata_size,
+ Elf32_Word text_relative_offset,
+ Elf32_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(Elf32_Word), sizeof(Elf32_Word)),
+ dynamic_builder_(".dynamic", &dynsym_builder_),
+ shstrtab_builder_(".shstrtab", SHT_STRTAB, 0, NULL, 0, 1, 1) {
+ SetupEhdr();
+ SetupDynamic();
+ SetupRequiredSymbols();
+ SetISA(isa);
+ }
+ ~ElfBuilder() {}
+
+ bool Write();
+ ElfSymtabBuilder* GetDefaultDynsymBuilder() { return &dynsym_builder_; }
+
+ // 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;
+
+ Elf32_Ehdr elf_header_;
+
+ 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, Elf32_Word offset, const void* data, Elf32_Word size)
+ : dbg_name_(name), offset_(offset), data_(data), size_(size) {}
+ ~ElfFilePiece() {}
+
+ const std::string& dbg_name_;
+ Elf32_Word offset_;
+ const void *data_;
+ Elf32_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.
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index beafbcc..f05cb66 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -29,30 +29,60 @@
namespace art {
-void CodeGenerator::Compile(CodeAllocator* allocator) {
+void CodeGenerator::CompileBaseline(CodeAllocator* allocator) {
const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
+ block_labels_.SetSize(blocks.Size());
+
+ DCHECK_EQ(frame_size_, kUninitializedFrameSize);
+ ComputeFrameSize(GetGraph()->GetMaximumNumberOfOutVRegs()
+ + GetGraph()->GetNumberOfVRegs()
+ + 1 /* filler */);
GenerateFrameEntry();
+
for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
- CompileBlock(blocks.Get(i));
+ HBasicBlock* block = blocks.Get(i);
+ Bind(GetLabelOf(block));
+ HGraphVisitor* location_builder = GetLocationBuilder();
+ HGraphVisitor* instruction_visitor = GetInstructionVisitor();
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* current = it.Current();
+ current->Accept(location_builder);
+ InitLocations(current);
+ current->Accept(instruction_visitor);
+ }
}
+
size_t code_size = GetAssembler()->CodeSize();
uint8_t* buffer = allocator->Allocate(code_size);
MemoryRegion code(buffer, code_size);
GetAssembler()->FinalizeInstructions(code);
}
-void CodeGenerator::CompileBlock(HBasicBlock* block) {
- Bind(GetLabelOf(block));
- HGraphVisitor* location_builder = GetLocationBuilder();
- HGraphVisitor* instruction_visitor = GetInstructionVisitor();
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- HInstruction* current = it.Current();
- current->Accept(location_builder);
- InitLocations(current);
- current->Accept(instruction_visitor);
+void CodeGenerator::CompileOptimized(CodeAllocator* allocator) {
+ // The frame size has already been computed during register allocation.
+ DCHECK_NE(frame_size_, kUninitializedFrameSize);
+ const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
+ DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
+ DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
+ block_labels_.SetSize(blocks.Size());
+
+ GenerateFrameEntry();
+ for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
+ HBasicBlock* block = blocks.Get(i);
+ Bind(GetLabelOf(block));
+ HGraphVisitor* instruction_visitor = GetInstructionVisitor();
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* current = it.Current();
+ current->Accept(instruction_visitor);
+ }
}
+
+ size_t code_size = GetAssembler()->CodeSize();
+ uint8_t* buffer = allocator->Allocate(code_size);
+ MemoryRegion code(buffer, code_size);
+ GetAssembler()->FinalizeInstructions(code);
}
size_t CodeGenerator::AllocateFreeRegisterInternal(
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index e197ccd..82fa639 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -28,6 +28,7 @@
namespace art {
static size_t constexpr kVRegSize = 4;
+static size_t constexpr kUninitializedFrameSize = 0;
class DexCompilationUnit;
@@ -51,7 +52,8 @@
public:
// Compiles the graph to executable instructions. Returns whether the compilation
// succeeded.
- void Compile(CodeAllocator* allocator);
+ void CompileBaseline(CodeAllocator* allocator);
+ void CompileOptimized(CodeAllocator* allocator);
static CodeGenerator* Create(ArenaAllocator* allocator,
HGraph* graph,
InstructionSet instruction_set);
@@ -61,6 +63,14 @@
Label* GetLabelOf(HBasicBlock* block) const;
bool GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const;
+ size_t GetStackSlotOfParameter(HParameterValue* parameter) const {
+ // Note that this follows the current calling convention.
+ return GetFrameSize()
+ + kVRegSize // Art method
+ + (parameter->GetIndex() - graph_->GetNumberOfVRegs() + graph_->GetNumberOfInVRegs())
+ * kVRegSize;
+ }
+
virtual void GenerateFrameEntry() = 0;
virtual void GenerateFrameExit() = 0;
virtual void Bind(Label* label) = 0;
@@ -69,6 +79,7 @@
virtual HGraphVisitor* GetInstructionVisitor() = 0;
virtual Assembler* GetAssembler() = 0;
virtual size_t GetWordSize() const = 0;
+ virtual void ComputeFrameSize(size_t number_of_spill_slots) = 0;
uint32_t GetFrameSize() const { return frame_size_; }
void SetFrameSize(uint32_t size) { frame_size_ = size; }
@@ -95,14 +106,12 @@
protected:
CodeGenerator(HGraph* graph, size_t number_of_registers)
- : frame_size_(0),
+ : frame_size_(kUninitializedFrameSize),
graph_(graph),
block_labels_(graph->GetArena(), 0),
pc_infos_(graph->GetArena(), 32),
- blocked_registers_(graph->GetArena()->AllocArray<bool>(number_of_registers)) {
- block_labels_.SetSize(graph->GetBlocks().Size());
- }
- ~CodeGenerator() { }
+ blocked_registers_(graph->GetArena()->AllocArray<bool>(number_of_registers)) {}
+ ~CodeGenerator() {}
// Register allocation logic.
void AllocateRegistersLocally(HInstruction* instruction) const;
@@ -123,7 +132,6 @@
private:
void InitLocations(HInstruction* instruction);
- void CompileBlock(HBasicBlock* block);
HGraph* const graph_;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index e888cc1..d61df36 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -129,16 +129,18 @@
assembler_(codegen->GetAssembler()),
codegen_(codegen) {}
+void CodeGeneratorARM::ComputeFrameSize(size_t number_of_spill_slots) {
+ SetFrameSize(RoundUp(
+ number_of_spill_slots * kVRegSize
+ + kVRegSize // Art method
+ + kNumberOfPushedRegistersAtEntry * kArmWordSize,
+ kStackAlignment));
+}
+
void CodeGeneratorARM::GenerateFrameEntry() {
core_spill_mask_ |= (1 << LR);
__ PushList((1 << LR));
- SetFrameSize(RoundUp(
- (GetGraph()->GetMaximumNumberOfOutVRegs() + GetGraph()->GetNumberOfVRegs()) * kVRegSize
- + kVRegSize // filler
- + kArmWordSize // Art method
- + kNumberOfPushedRegistersAtEntry * kArmWordSize,
- kStackAlignment));
// The return PC has already been pushed on the stack.
__ AddConstant(SP, -(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize));
__ str(R0, Address(SP, 0));
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index c945a06..ac5ef21 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -104,6 +104,7 @@
explicit CodeGeneratorARM(HGraph* graph);
virtual ~CodeGeneratorARM() { }
+ virtual void ComputeFrameSize(size_t number_of_spill_slots) OVERRIDE;
virtual void GenerateFrameEntry() OVERRIDE;
virtual void GenerateFrameExit() OVERRIDE;
virtual void Bind(Label* label) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 72c697f..c7dca86 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -48,7 +48,16 @@
CodeGeneratorX86::CodeGeneratorX86(HGraph* graph)
: CodeGenerator(graph, kNumberOfRegIds),
location_builder_(graph, this),
- instruction_visitor_(graph, this) {}
+ instruction_visitor_(graph, this),
+ move_resolver_(graph->GetArena(), this) {}
+
+void CodeGeneratorX86::ComputeFrameSize(size_t number_of_spill_slots) {
+ SetFrameSize(RoundUp(
+ number_of_spill_slots * kVRegSize
+ + kVRegSize // Art method
+ + kNumberOfPushedRegistersAtEntry * kX86WordSize,
+ kStackAlignment));
+}
static bool* GetBlockedRegisterPairs(bool* blocked_registers) {
return blocked_registers + kNumberOfAllocIds;
@@ -125,13 +134,6 @@
static const int kFakeReturnRegister = 8;
core_spill_mask_ |= (1 << kFakeReturnRegister);
- SetFrameSize(RoundUp(
- (GetGraph()->GetMaximumNumberOfOutVRegs() + GetGraph()->GetNumberOfVRegs()) * kVRegSize
- + kVRegSize // filler
- + kX86WordSize // Art method
- + kNumberOfPushedRegistersAtEntry * kX86WordSize,
- kStackAlignment));
-
// The return PC has already been pushed on the stack.
__ subl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
__ movl(Address(ESP, kCurrentMethodStackOffset), EAX);
@@ -264,8 +266,8 @@
__ movl(Address(ESP, destination.GetStackIndex()), source.AsX86().AsCpuRegister());
} else {
DCHECK(source.IsStackSlot());
- __ movl(EAX, Address(ESP, source.GetStackIndex()));
- __ movl(Address(ESP, destination.GetStackIndex()), EAX);
+ __ pushl(Address(ESP, source.GetStackIndex()));
+ __ popl(Address(ESP, destination.GetStackIndex()));
}
}
}
@@ -302,8 +304,8 @@
DCHECK(source.IsDoubleStackSlot());
__ movl(calling_convention.GetRegisterAt(argument_index),
Address(ESP, source.GetStackIndex()));
- __ movl(EAX, Address(ESP, source.GetHighStackIndex(kX86WordSize)));
- __ movl(Address(ESP, calling_convention.GetStackOffsetOf(argument_index + 1, kX86WordSize)), EAX);
+ __ pushl(Address(ESP, source.GetHighStackIndex(kX86WordSize)));
+ __ popl(Address(ESP, calling_convention.GetStackOffsetOf(argument_index + 1, kX86WordSize)));
}
} else {
if (source.IsRegister()) {
@@ -315,15 +317,15 @@
uint32_t argument_index = source.GetQuickParameterIndex();
__ movl(Address(ESP, destination.GetStackIndex()),
calling_convention.GetRegisterAt(argument_index));
- __ movl(EAX, Address(ESP,
+ __ pushl(Address(ESP,
calling_convention.GetStackOffsetOf(argument_index + 1, kX86WordSize) + GetFrameSize()));
- __ movl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)), EAX);
+ __ popl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)));
} else {
DCHECK(source.IsDoubleStackSlot());
- __ movl(EAX, Address(ESP, source.GetStackIndex()));
- __ movl(Address(ESP, destination.GetStackIndex()), EAX);
- __ movl(EAX, Address(ESP, source.GetHighStackIndex(kX86WordSize)));
- __ movl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)), EAX);
+ __ pushl(Address(ESP, source.GetStackIndex()));
+ __ popl(Address(ESP, destination.GetStackIndex()));
+ __ pushl(Address(ESP, source.GetHighStackIndex(kX86WordSize)));
+ __ popl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)));
}
}
}
@@ -501,7 +503,7 @@
}
void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant) {
- // Will be generated at use site.
+ codegen_->Move(constant, constant->GetLocations()->Out(), nullptr);
}
void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) {
@@ -573,7 +575,7 @@
void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
- locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(X86CpuLocation(EAX));
InvokeDexCallingConventionVisitor calling_convention_visitor;
for (size_t i = 0; i < invoke->InputCount(); i++) {
@@ -802,7 +804,6 @@
}
void InstructionCodeGeneratorX86::VisitParameterValue(HParameterValue* instruction) {
- // Nothing to do, the parameter is already at its location.
}
void LocationsBuilderX86::VisitNot(HNot* instruction) {
@@ -829,15 +830,100 @@
}
void InstructionCodeGeneratorX86::VisitPhi(HPhi* instruction) {
- LOG(FATAL) << "Unimplemented";
+ LOG(FATAL) << "Unreachable";
}
void LocationsBuilderX86::VisitParallelMove(HParallelMove* instruction) {
- LOG(FATAL) << "Unimplemented";
+ LOG(FATAL) << "Unreachable";
}
void InstructionCodeGeneratorX86::VisitParallelMove(HParallelMove* instruction) {
- LOG(FATAL) << "Unimplemented";
+ codegen_->GetMoveResolver()->EmitNativeCode(instruction);
+}
+
+X86Assembler* ParallelMoveResolverX86::GetAssembler() const {
+ return codegen_->GetAssembler();
+}
+
+void ParallelMoveResolverX86::MoveMemoryToMemory(int dst, int src) {
+ ScratchRegisterScope ensure_scratch(
+ this, kNoRegister, codegen_->GetNumberOfCoreRegisters());
+ int stack_offset = ensure_scratch.IsSpilled() ? kX86WordSize : 0;
+ __ movl(static_cast<Register>(ensure_scratch.GetRegister()), Address(ESP, src + stack_offset));
+ __ movl(Address(ESP, dst + stack_offset), static_cast<Register>(ensure_scratch.GetRegister()));
+}
+
+void ParallelMoveResolverX86::EmitMove(size_t index) {
+ MoveOperands* move = moves_.Get(index);
+ Location source = move->GetSource();
+ Location destination = move->GetDestination();
+
+ if (source.IsRegister()) {
+ if (destination.IsRegister()) {
+ __ movl(destination.AsX86().AsCpuRegister(), source.AsX86().AsCpuRegister());
+ } else {
+ DCHECK(destination.IsStackSlot());
+ __ movl(Address(ESP, destination.GetStackIndex()), source.AsX86().AsCpuRegister());
+ }
+ } else if (source.IsStackSlot()) {
+ if (destination.IsRegister()) {
+ __ movl(destination.AsX86().AsCpuRegister(), Address(ESP, source.GetStackIndex()));
+ } else {
+ DCHECK(destination.IsStackSlot());
+ MoveMemoryToMemory(destination.GetStackIndex(),
+ source.GetStackIndex());
+ }
+ } else {
+ LOG(FATAL) << "Unimplemented";
+ }
+}
+
+void ParallelMoveResolverX86::Exchange(Register reg, int mem) {
+ ScratchRegisterScope ensure_scratch(this, reg, codegen_->GetNumberOfCoreRegisters());
+ int stack_offset = ensure_scratch.IsSpilled() ? kX86WordSize : 0;
+ __ movl(static_cast<Register>(ensure_scratch.GetRegister()), Address(ESP, mem + stack_offset));
+ __ movl(Address(ESP, mem + stack_offset), reg);
+ __ movl(reg, static_cast<Register>(ensure_scratch.GetRegister()));
+}
+
+
+void ParallelMoveResolverX86::Exchange(int mem1, int mem2) {
+ ScratchRegisterScope ensure_scratch1(
+ this, kNoRegister, codegen_->GetNumberOfCoreRegisters());
+ ScratchRegisterScope ensure_scratch2(
+ this, ensure_scratch1.GetRegister(), codegen_->GetNumberOfCoreRegisters());
+ int stack_offset = ensure_scratch1.IsSpilled() ? kX86WordSize : 0;
+ stack_offset += ensure_scratch2.IsSpilled() ? kX86WordSize : 0;
+ __ movl(static_cast<Register>(ensure_scratch1.GetRegister()), Address(ESP, mem1 + stack_offset));
+ __ movl(static_cast<Register>(ensure_scratch2.GetRegister()), Address(ESP, mem2 + stack_offset));
+ __ movl(Address(ESP, mem2 + stack_offset), static_cast<Register>(ensure_scratch1.GetRegister()));
+ __ movl(Address(ESP, mem1 + stack_offset), static_cast<Register>(ensure_scratch2.GetRegister()));
+}
+
+void ParallelMoveResolverX86::EmitSwap(size_t index) {
+ MoveOperands* move = moves_.Get(index);
+ Location source = move->GetSource();
+ Location destination = move->GetDestination();
+
+ if (source.IsRegister() && destination.IsRegister()) {
+ __ xchgl(destination.AsX86().AsCpuRegister(), source.AsX86().AsCpuRegister());
+ } else if (source.IsRegister() && destination.IsStackSlot()) {
+ Exchange(source.AsX86().AsCpuRegister(), destination.GetStackIndex());
+ } else if (source.IsStackSlot() && destination.IsRegister()) {
+ Exchange(destination.AsX86().AsCpuRegister(), source.GetStackIndex());
+ } else if (source.IsStackSlot() && destination.IsStackSlot()) {
+ Exchange(destination.GetStackIndex(), source.GetStackIndex());
+ } else {
+ LOG(FATAL) << "Unimplemented";
+ }
+}
+
+void ParallelMoveResolverX86::SpillScratch(int reg) {
+ __ pushl(static_cast<Register>(reg));
+}
+
+void ParallelMoveResolverX86::RestoreScratch(int reg) {
+ __ popl(static_cast<Register>(reg));
}
} // namespace x86
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 4a70636..acc670e 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -19,6 +19,7 @@
#include "code_generator.h"
#include "nodes.h"
+#include "parallel_move_resolver.h"
#include "utils/x86/assembler_x86.h"
namespace art {
@@ -59,6 +60,28 @@
DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitor);
};
+class ParallelMoveResolverX86 : public ParallelMoveResolver {
+ public:
+ ParallelMoveResolverX86(ArenaAllocator* allocator, CodeGeneratorX86* codegen)
+ : ParallelMoveResolver(allocator), codegen_(codegen) {}
+
+ virtual void EmitMove(size_t index) OVERRIDE;
+ virtual void EmitSwap(size_t index) OVERRIDE;
+ virtual void SpillScratch(int reg) OVERRIDE;
+ virtual void RestoreScratch(int reg) OVERRIDE;
+
+ X86Assembler* GetAssembler() const;
+
+ private:
+ void Exchange(Register reg, int mem);
+ void Exchange(int mem1, int mem2);
+ void MoveMemoryToMemory(int dst, int src);
+
+ CodeGeneratorX86* const codegen_;
+
+ DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolverX86);
+};
+
class LocationsBuilderX86 : public HGraphVisitor {
public:
LocationsBuilderX86(HGraph* graph, CodeGeneratorX86* codegen)
@@ -105,6 +128,7 @@
explicit CodeGeneratorX86(HGraph* graph);
virtual ~CodeGeneratorX86() { }
+ virtual void ComputeFrameSize(size_t number_of_spill_slots) OVERRIDE;
virtual void GenerateFrameEntry() OVERRIDE;
virtual void GenerateFrameExit() OVERRIDE;
virtual void Bind(Label* label) OVERRIDE;
@@ -145,6 +169,10 @@
virtual void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
+ ParallelMoveResolverX86* GetMoveResolver() {
+ return &move_resolver_;
+ }
+
private:
// Helper method to move a 32bits value between two locations.
void Move32(Location destination, Location source);
@@ -153,6 +181,7 @@
LocationsBuilderX86 location_builder_;
InstructionCodeGeneratorX86 instruction_visitor_;
+ ParallelMoveResolverX86 move_resolver_;
X86Assembler assembler_;
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86);
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index 7684bb1..8ee775c 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -56,7 +56,7 @@
ASSERT_NE(graph, nullptr);
InternalCodeAllocator allocator;
CodeGenerator* codegen = CodeGenerator::Create(&arena, graph, kX86);
- codegen->Compile(&allocator);
+ codegen->CompileBaseline(&allocator);
typedef int32_t (*fptr)();
#if defined(__i386__)
CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize());
@@ -66,7 +66,7 @@
}
#endif
codegen = CodeGenerator::Create(&arena, graph, kArm);
- codegen->Compile(&allocator);
+ codegen->CompileBaseline(&allocator);
#if defined(__arm__)
CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize());
int32_t result = reinterpret_cast<fptr>(allocator.GetMemory())();
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 5c5042e..a49ce64 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -28,8 +28,15 @@
*/
class HGraphVisualizerPrinter : public HGraphVisitor {
public:
- HGraphVisualizerPrinter(HGraph* graph, std::ostream& output, const CodeGenerator& codegen)
- : HGraphVisitor(graph), output_(output), codegen_(codegen), indent_(0) {}
+ HGraphVisualizerPrinter(HGraph* graph,
+ std::ostream& output,
+ const char* pass_name,
+ const CodeGenerator& codegen)
+ : HGraphVisitor(graph),
+ output_(output),
+ pass_name_(pass_name),
+ codegen_(codegen),
+ indent_(0) {}
void StartTag(const char* name) {
AddIndent();
@@ -94,6 +101,33 @@
output_<< std::endl;
}
+ void DumpLocation(Location location, Primitive::Type type) {
+ if (location.IsRegister()) {
+ if (type == Primitive::kPrimDouble || type == Primitive::kPrimFloat) {
+ codegen_.DumpFloatingPointRegister(output_, location.reg().RegId());
+ } else {
+ codegen_.DumpCoreRegister(output_, location.reg().RegId());
+ }
+ } else {
+ DCHECK(location.IsStackSlot());
+ output_ << location.GetStackIndex() << "(sp)";
+ }
+ }
+
+ void VisitParallelMove(HParallelMove* instruction) {
+ output_ << instruction->DebugName();
+ output_ << " (";
+ for (size_t i = 0, e = instruction->NumMoves(); i < e; ++i) {
+ MoveOperands* move = instruction->MoveOperandsAt(i);
+ DumpLocation(move->GetSource(), Primitive::kPrimInt);
+ output_ << " -> ";
+ DumpLocation(move->GetDestination(), Primitive::kPrimInt);
+ if (i + 1 != e) {
+ output_ << ", ";
+ }
+ }
+ output_ << ")";
+ }
void VisitInstruction(HInstruction* instruction) {
output_ << instruction->DebugName();
@@ -104,24 +138,28 @@
}
output_ << "]";
}
- if (instruction->GetLifetimePosition() != kNoLifetime) {
+ if (pass_name_ == kLivenessPassName && instruction->GetLifetimePosition() != kNoLifetime) {
output_ << " (liveness: " << instruction->GetLifetimePosition();
if (instruction->HasLiveInterval()) {
output_ << " ";
const LiveInterval& interval = *instruction->GetLiveInterval();
interval.Dump(output_);
- if (interval.HasRegister()) {
- int reg = interval.GetRegister();
- output_ << " ";
- if (instruction->GetType() == Primitive::kPrimFloat
- || instruction->GetType() == Primitive::kPrimDouble) {
- codegen_.DumpFloatingPointRegister(output_, reg);
- } else {
- codegen_.DumpCoreRegister(output_, reg);
- }
- }
}
output_ << ")";
+ } else if (pass_name_ == kRegisterAllocatorPassName) {
+ LocationSummary* locations = instruction->GetLocations();
+ if (locations != nullptr) {
+ output_ << " ( ";
+ for (size_t i = 0; i < instruction->InputCount(); ++i) {
+ DumpLocation(locations->InAt(i), instruction->InputAt(i)->GetType());
+ output_ << " ";
+ }
+ output_ << ")";
+ if (locations->Out().IsValid()) {
+ output_ << " -> ";
+ DumpLocation(locations->Out(), instruction->GetType());
+ }
+ }
}
}
@@ -137,9 +175,9 @@
}
}
- void Run(const char* pass_name) {
+ void Run() {
StartTag("cfg");
- PrintProperty("name", pass_name);
+ PrintProperty("name", pass_name_);
VisitInsertionOrder();
EndTag("cfg");
}
@@ -188,6 +226,7 @@
private:
std::ostream& output_;
+ const char* pass_name_;
const CodeGenerator& codegen_;
size_t indent_;
@@ -209,7 +248,7 @@
}
is_enabled_ = true;
- HGraphVisualizerPrinter printer(graph, *output_, codegen_);
+ HGraphVisualizerPrinter printer(graph, *output_, "", codegen_);
printer.StartTag("compilation");
printer.PrintProperty("name", pretty_name.c_str());
printer.PrintProperty("method", pretty_name.c_str());
@@ -227,7 +266,7 @@
}
is_enabled_ = true;
- HGraphVisualizerPrinter printer(graph, *output_, codegen_);
+ HGraphVisualizerPrinter printer(graph, *output_, "", codegen_);
printer.StartTag("compilation");
printer.PrintProperty("name", name);
printer.PrintProperty("method", name);
@@ -239,8 +278,8 @@
if (!is_enabled_) {
return;
}
- HGraphVisualizerPrinter printer(graph_, *output_, codegen_);
- printer.Run(pass_name);
+ HGraphVisualizerPrinter printer(graph_, *output_, pass_name, codegen_);
+ printer.Run();
}
} // namespace art
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index 2638cf5..7cd74e9 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -25,6 +25,9 @@
class DexCompilationUnit;
class HGraph;
+static const char* kLivenessPassName = "liveness";
+static const char* kRegisterAllocatorPassName = "register";
+
/**
* If enabled, emits compilation information suitable for the c1visualizer tool
* and IRHydra.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 68848de..143d5c9 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -508,6 +508,7 @@
void ReplaceWith(HInstruction* instruction);
#define INSTRUCTION_TYPE_CHECK(type) \
+ bool Is##type() { return (As##type() != nullptr); } \
virtual H##type* As##type() { return nullptr; }
FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 3dc0928..ccacbef 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -85,6 +85,8 @@
// For testing purposes, we put a special marker on method names that should be compiled
// with this compiler. This makes sure we're not regressing.
bool shouldCompile = dex_compilation_unit.GetSymbol().find("00024opt_00024") != std::string::npos;
+ bool shouldOptimize =
+ dex_compilation_unit.GetSymbol().find("00024reg_00024") != std::string::npos;
ArenaPool pool;
ArenaAllocator arena(&pool);
@@ -116,7 +118,36 @@
visualizer.DumpGraph("builder");
CodeVectorAllocator allocator;
- codegen->Compile(&allocator);
+
+ if (RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set)) {
+ graph->BuildDominatorTree();
+ graph->TransformToSSA();
+ visualizer.DumpGraph("ssa");
+
+ graph->FindNaturalLoops();
+ SsaLivenessAnalysis liveness(*graph, codegen);
+ liveness.Analyze();
+ visualizer.DumpGraph(kLivenessPassName);
+
+ RegisterAllocator register_allocator(graph->GetArena(), codegen, liveness);
+ register_allocator.AllocateRegisters();
+
+ visualizer.DumpGraph(kRegisterAllocatorPassName);
+ codegen->CompileOptimized(&allocator);
+ } else if (shouldOptimize && RegisterAllocator::Supports(instruction_set)) {
+ LOG(FATAL) << "Could not allocate registers in optimizing compiler";
+ } else {
+ codegen->CompileBaseline(&allocator);
+
+ // Run these phases to get some test coverage.
+ graph->BuildDominatorTree();
+ graph->TransformToSSA();
+ visualizer.DumpGraph("ssa");
+ graph->FindNaturalLoops();
+ SsaLivenessAnalysis liveness(*graph, codegen);
+ liveness.Analyze();
+ visualizer.DumpGraph(kLivenessPassName);
+ }
std::vector<uint8_t> mapping_table;
codegen->BuildMappingTable(&mapping_table);
@@ -125,19 +156,6 @@
std::vector<uint8_t> gc_map;
codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
- // Run these phases to get some test coverage.
- graph->BuildDominatorTree();
- graph->TransformToSSA();
- visualizer.DumpGraph("ssa");
-
- graph->FindNaturalLoops();
- SsaLivenessAnalysis liveness(*graph, codegen);
- liveness.Analyze();
- visualizer.DumpGraph("liveness");
-
- RegisterAllocator(graph->GetArena(), *codegen).AllocateRegisters(liveness);
- visualizer.DumpGraph("register");
-
return new CompiledMethod(GetCompilerDriver(),
instruction_set,
allocator.GetMemory(),
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index 3d2d136..4a1b6ce 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -147,4 +147,64 @@
}
}
+bool ParallelMoveResolver::IsScratchLocation(Location loc) {
+ for (size_t i = 0; i < moves_.Size(); ++i) {
+ if (moves_.Get(i)->Blocks(loc)) {
+ return false;
+ }
+ }
+
+ for (size_t i = 0; i < moves_.Size(); ++i) {
+ if (moves_.Get(i)->GetDestination().Equals(loc)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int ParallelMoveResolver::AllocateScratchRegister(int blocked, int register_count, bool* spilled) {
+ int scratch = -1;
+ for (int reg = 0; reg < register_count; ++reg) {
+ if ((blocked != reg) &&
+ IsScratchLocation(Location::RegisterLocation(ManagedRegister(reg)))) {
+ scratch = reg;
+ break;
+ }
+ }
+
+ if (scratch == -1) {
+ *spilled = true;
+ for (int reg = 0; reg < register_count; ++reg) {
+ if (blocked != reg) {
+ scratch = reg;
+ }
+ }
+ } else {
+ *spilled = false;
+ }
+
+ return scratch;
+}
+
+
+ParallelMoveResolver::ScratchRegisterScope::ScratchRegisterScope(
+ ParallelMoveResolver* resolver, int blocked, int number_of_registers)
+ : resolver_(resolver),
+ reg_(kNoRegister),
+ spilled_(false) {
+ reg_ = resolver_->AllocateScratchRegister(blocked, number_of_registers, &spilled_);
+
+ if (spilled_) {
+ resolver->SpillScratch(reg_);
+ }
+}
+
+
+ParallelMoveResolver::ScratchRegisterScope::~ScratchRegisterScope() {
+ if (spilled_) {
+ resolver_->RestoreScratch(reg_);
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/parallel_move_resolver.h b/compiler/optimizing/parallel_move_resolver.h
index ff20cb0..e1189d8 100644
--- a/compiler/optimizing/parallel_move_resolver.h
+++ b/compiler/optimizing/parallel_move_resolver.h
@@ -23,6 +23,7 @@
namespace art {
class HParallelMove;
+class Location;
class MoveOperands;
/**
@@ -39,15 +40,37 @@
void EmitNativeCode(HParallelMove* parallel_move);
protected:
+ class ScratchRegisterScope : public ValueObject {
+ public:
+ ScratchRegisterScope(ParallelMoveResolver* resolver, int blocked, int number_of_registers);
+ ~ScratchRegisterScope();
+
+ int GetRegister() const { return reg_; }
+ bool IsSpilled() const { return spilled_; }
+
+ private:
+ ParallelMoveResolver* resolver_;
+ int reg_;
+ bool spilled_;
+ };
+
+ bool IsScratchLocation(Location loc);
+ int AllocateScratchRegister(int blocked, int register_count, bool* spilled);
+
// Emit a move.
virtual void EmitMove(size_t index) = 0;
// Execute a move by emitting a swap of two operands.
virtual void EmitSwap(size_t index) = 0;
+ virtual void SpillScratch(int reg) = 0;
+ virtual void RestoreScratch(int reg) = 0;
+
// List of moves not yet resolved.
GrowableArray<MoveOperands*> moves_;
+ static constexpr int kNoRegister = -1;
+
private:
// Build the initial list of moves.
void BuildInitialMoveList(HParallelMove* parallel_move);
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 88df24d..093856d 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -50,6 +50,9 @@
<< ")";
}
+ virtual void SpillScratch(int reg) {}
+ virtual void RestoreScratch(int reg) {}
+
std::string GetMessage() const {
return message_.str();
}
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 8c6eb2a..c2a4769 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -24,64 +24,151 @@
static constexpr size_t kMaxLifetimePosition = -1;
static constexpr size_t kDefaultNumberOfSpillSlots = 4;
-RegisterAllocator::RegisterAllocator(ArenaAllocator* allocator, const CodeGenerator& codegen)
+RegisterAllocator::RegisterAllocator(ArenaAllocator* allocator,
+ CodeGenerator* codegen,
+ const SsaLivenessAnalysis& liveness)
: allocator_(allocator),
codegen_(codegen),
+ liveness_(liveness),
unhandled_(allocator, 0),
handled_(allocator, 0),
active_(allocator, 0),
inactive_(allocator, 0),
+ physical_register_intervals_(allocator, codegen->GetNumberOfRegisters()),
spill_slots_(allocator, kDefaultNumberOfSpillSlots),
processing_core_registers_(false),
number_of_registers_(-1),
registers_array_(nullptr),
- blocked_registers_(allocator->AllocArray<bool>(codegen.GetNumberOfRegisters())) {
- codegen.SetupBlockedRegisters(blocked_registers_);
+ blocked_registers_(allocator->AllocArray<bool>(codegen->GetNumberOfRegisters())) {
+ codegen->SetupBlockedRegisters(blocked_registers_);
+ physical_register_intervals_.SetSize(codegen->GetNumberOfRegisters());
}
-static bool ShouldProcess(bool processing_core_registers, HInstruction* instruction) {
- bool is_core_register = (instruction->GetType() != Primitive::kPrimDouble)
- && (instruction->GetType() != Primitive::kPrimFloat);
+bool RegisterAllocator::CanAllocateRegistersFor(const HGraph& graph,
+ InstructionSet instruction_set) {
+ if (!Supports(instruction_set)) {
+ return false;
+ }
+ for (size_t i = 0, e = graph.GetBlocks().Size(); i < e; ++i) {
+ for (HInstructionIterator it(graph.GetBlocks().Get(i)->GetInstructions());
+ !it.Done();
+ it.Advance()) {
+ HInstruction* current = it.Current();
+ if (current->NeedsEnvironment()) return false;
+ if (current->GetType() == Primitive::kPrimLong) return false;
+ if (current->GetType() == Primitive::kPrimFloat) return false;
+ if (current->GetType() == Primitive::kPrimDouble) return false;
+ }
+ }
+ return true;
+}
+
+static bool ShouldProcess(bool processing_core_registers, LiveInterval* interval) {
+ bool is_core_register = (interval->GetType() != Primitive::kPrimDouble)
+ && (interval->GetType() != Primitive::kPrimFloat);
return processing_core_registers == is_core_register;
}
-void RegisterAllocator::AllocateRegistersInternal(const SsaLivenessAnalysis& liveness) {
+void RegisterAllocator::AllocateRegisters() {
+ processing_core_registers_ = true;
+ AllocateRegistersInternal();
+ processing_core_registers_ = false;
+ AllocateRegistersInternal();
+
+ Resolve();
+
+ if (kIsDebugBuild) {
+ processing_core_registers_ = true;
+ ValidateInternal(true);
+ processing_core_registers_ = false;
+ ValidateInternal(true);
+ }
+}
+
+void RegisterAllocator::BlockRegister(Location location,
+ size_t start,
+ size_t end,
+ Primitive::Type type) {
+ int reg = location.reg().RegId();
+ LiveInterval* interval = physical_register_intervals_.Get(reg);
+ if (interval == nullptr) {
+ interval = LiveInterval::MakeFixedInterval(allocator_, reg, type);
+ physical_register_intervals_.Put(reg, interval);
+ inactive_.Add(interval);
+ }
+ DCHECK(interval->GetRegister() == reg);
+ interval->AddRange(start, end);
+}
+
+void RegisterAllocator::AllocateRegistersInternal() {
number_of_registers_ = processing_core_registers_
- ? codegen_.GetNumberOfCoreRegisters()
- : codegen_.GetNumberOfFloatingPointRegisters();
+ ? 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);
- if (ShouldProcess(processing_core_registers_, instruction)) {
- LiveInterval* current = instruction->GetLiveInterval();
+ 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()));
- unhandled_.Add(current);
+
+ 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()) {
+ 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()) {
+ DCHECK(instruction->IsParameterValue());
+ // 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);
+ // The new interval may start at a late
+ 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);
+ }
}
}
LinearScan();
- if (kIsDebugBuild) {
- ValidateInternal(liveness, true);
- }
-}
-
-bool RegisterAllocator::ValidateInternal(const SsaLivenessAnalysis& liveness,
- bool log_fatal_on_failure) const {
- // To simplify unit testing, we eagerly create the array of intervals, and
- // call the helper method.
- GrowableArray<LiveInterval*> intervals(allocator_, 0);
- for (size_t i = 0; i < liveness.GetNumberOfSsaValues(); ++i) {
- HInstruction* instruction = liveness.GetInstructionFromSsaIndex(i);
- if (ShouldProcess(processing_core_registers_, instruction)) {
- intervals.Add(instruction->GetLiveInterval());
- }
- }
- return ValidateIntervals(intervals, spill_slots_.Size(), codegen_, allocator_,
- processing_core_registers_, log_fatal_on_failure);
}
class AllRangesIterator : public ValueObject {
@@ -111,6 +198,28 @@
DISALLOW_COPY_AND_ASSIGN(AllRangesIterator);
};
+bool RegisterAllocator::ValidateInternal(bool log_fatal_on_failure) const {
+ // To simplify unit testing, we eagerly create the array of intervals, and
+ // call the helper method.
+ GrowableArray<LiveInterval*> intervals(allocator_, 0);
+ for (size_t i = 0; i < liveness_.GetNumberOfSsaValues(); ++i) {
+ HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i);
+ if (ShouldProcess(processing_core_registers_, instruction->GetLiveInterval())) {
+ intervals.Add(instruction->GetLiveInterval());
+ }
+ }
+
+ for (size_t i = 0, e = physical_register_intervals_.Size(); i < e; ++i) {
+ LiveInterval* fixed = physical_register_intervals_.Get(i);
+ if (fixed != nullptr && ShouldProcess(processing_core_registers_, fixed)) {
+ intervals.Add(fixed);
+ }
+ }
+
+ return ValidateIntervals(intervals, spill_slots_.Size(), *codegen_, allocator_,
+ processing_core_registers_, log_fatal_on_failure);
+}
+
bool RegisterAllocator::ValidateIntervals(const GrowableArray<LiveInterval*>& intervals,
size_t number_of_spill_slots,
const CodeGenerator& codegen,
@@ -132,7 +241,10 @@
for (size_t i = 0, e = intervals.Size(); i < e; ++i) {
for (AllRangesIterator it(intervals.Get(i)); !it.Done(); it.Advance()) {
LiveInterval* current = it.CurrentInterval();
- if (current->GetParent()->HasSpillSlot()) {
+ HInstruction* defined_by = current->GetParent()->GetDefinedBy();
+ 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);
for (size_t j = it.CurrentRange()->GetStart(); j < it.CurrentRange()->GetEnd(); ++j) {
@@ -176,14 +288,14 @@
return true;
}
-void RegisterAllocator::DumpInterval(std::ostream& stream, LiveInterval* interval) {
+void RegisterAllocator::DumpInterval(std::ostream& stream, LiveInterval* interval) const {
interval->Dump(stream);
stream << ": ";
if (interval->HasRegister()) {
if (processing_core_registers_) {
- codegen_.DumpCoreRegister(stream, interval->GetRegister());
+ codegen_->DumpCoreRegister(stream, interval->GetRegister());
} else {
- codegen_.DumpFloatingPointRegister(stream, interval->GetRegister());
+ codegen_->DumpFloatingPointRegister(stream, interval->GetRegister());
}
} else {
stream << "spilled";
@@ -196,6 +308,7 @@
while (!unhandled_.IsEmpty()) {
// (1) Remove interval with the lowest start position from unhandled.
LiveInterval* current = unhandled_.Pop();
+ DCHECK(!current->IsFixed() && !current->HasRegister() && !current->HasSpillSlot());
size_t position = current->GetStart();
// (2) Remove currently active intervals that are dead at this position.
@@ -255,13 +368,6 @@
free_until[i] = kMaxLifetimePosition;
}
- // For each active interval, set its register to not free.
- for (size_t i = 0, e = active_.Size(); i < e; ++i) {
- LiveInterval* interval = active_.Get(i);
- DCHECK(interval->HasRegister());
- free_until[interval->GetRegister()] = 0;
- }
-
// For each inactive interval, set its register to be free until
// the next intersection with `current`.
// Thanks to SSA, this should only be needed for intervals
@@ -275,6 +381,13 @@
}
}
+ // For each active interval, set its register to not free.
+ for (size_t i = 0, e = active_.Size(); i < e; ++i) {
+ LiveInterval* interval = active_.Get(i);
+ DCHECK(interval->HasRegister());
+ 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) {
@@ -330,9 +443,13 @@
for (size_t i = 0, e = active_.Size(); i < e; ++i) {
LiveInterval* active = active_.Get(i);
DCHECK(active->HasRegister());
- size_t use = active->FirstRegisterUseAfter(current->GetStart());
- if (use != kNoLifetime) {
- next_use[active->GetRegister()] = use;
+ if (active->IsFixed()) {
+ next_use[active->GetRegister()] = current->GetStart();
+ } else {
+ size_t use = active->FirstRegisterUseAfter(current->GetStart());
+ if (use != kNoLifetime) {
+ next_use[active->GetRegister()] = use;
+ }
}
}
@@ -343,9 +460,17 @@
for (size_t i = 0, e = inactive_.Size(); i < e; ++i) {
LiveInterval* inactive = inactive_.Get(i);
DCHECK(inactive->HasRegister());
- size_t use = inactive->FirstRegisterUseAfter(current->GetStart());
- if (use != kNoLifetime) {
- next_use[inactive->GetRegister()] = use;
+ size_t next_intersection = inactive->FirstIntersectionWith(current);
+ if (next_intersection != kNoLifetime) {
+ if (inactive->IsFixed()) {
+ next_use[inactive->GetRegister()] =
+ std::min(next_intersection, next_use[inactive->GetRegister()]);
+ } else {
+ size_t use = inactive->FirstRegisterUseAfter(current->GetStart());
+ if (use != kNoLifetime) {
+ next_use[inactive->GetRegister()] = std::min(use, next_use[inactive->GetRegister()]);
+ }
+ }
}
}
@@ -374,6 +499,7 @@
for (size_t i = 0, e = active_.Size(); i < e; ++i) {
LiveInterval* active = active_.Get(i);
if (active->GetRegister() == reg) {
+ DCHECK(!active->IsFixed());
LiveInterval* split = Split(active, current->GetStart());
active_.DeleteAt(i);
handled_.Add(active);
@@ -385,11 +511,19 @@
for (size_t i = 0; i < inactive_.Size(); ++i) {
LiveInterval* inactive = inactive_.Get(i);
if (inactive->GetRegister() == reg) {
- LiveInterval* split = Split(inactive, current->GetStart());
- inactive_.DeleteAt(i);
- handled_.Add(inactive);
- AddToUnhandled(split);
- --i;
+ size_t next_intersection = inactive->FirstIntersectionWith(current);
+ if (next_intersection != kNoLifetime) {
+ if (inactive->IsFixed()) {
+ LiveInterval* split = Split(current, next_intersection);
+ AddToUnhandled(split);
+ } else {
+ LiveInterval* split = Split(inactive, current->GetStart());
+ inactive_.DeleteAt(i);
+ handled_.Add(inactive);
+ AddToUnhandled(split);
+ --i;
+ }
+ }
}
}
@@ -398,13 +532,15 @@
}
void RegisterAllocator::AddToUnhandled(LiveInterval* interval) {
+ size_t insert_at = 0;
for (size_t i = unhandled_.Size(); i > 0; --i) {
LiveInterval* current = unhandled_.Get(i - 1);
if (current->StartsAfter(interval)) {
- unhandled_.InsertAt(i, interval);
+ insert_at = i;
break;
}
}
+ unhandled_.InsertAt(insert_at, interval);
}
LiveInterval* RegisterAllocator::Split(LiveInterval* interval, size_t position) {
@@ -429,7 +565,13 @@
return;
}
- // Find when this instruction dies.
+ HInstruction* defined_by = parent->GetDefinedBy();
+ if (defined_by->IsParameterValue()) {
+ // Parameters have their own stack slot.
+ parent->SetSpillSlot(codegen_->GetStackSlotOfParameter(defined_by->AsParameterValue()));
+ return;
+ }
+
LiveInterval* last_sibling = interval;
while (last_sibling->GetNextSibling() != nullptr) {
last_sibling = last_sibling->GetNextSibling();
@@ -451,7 +593,315 @@
spill_slots_.Put(slot, end);
}
- interval->GetParent()->SetSpillSlot(slot * kVRegSize);
+ parent->SetSpillSlot(slot * kVRegSize);
+}
+
+static Location ConvertToLocation(LiveInterval* interval) {
+ if (interval->HasRegister()) {
+ return Location::RegisterLocation(ManagedRegister(interval->GetRegister()));
+ } else {
+ DCHECK(interval->GetParent()->HasSpillSlot());
+ return Location::StackSlot(interval->GetParent()->GetSpillSlot());
+ }
+}
+
+// We create a special marker for inputs moves to differentiate them from
+// moves created during resolution. They must be different instructions
+// because the input moves work on the assumption that the interval moves
+// have been executed.
+static constexpr size_t kInputMoveLifetimePosition = 0;
+static bool IsInputMove(HInstruction* instruction) {
+ return instruction->GetLifetimePosition() == kInputMoveLifetimePosition;
+}
+
+void RegisterAllocator::AddInputMoveFor(HInstruction* instruction,
+ Location source,
+ Location destination) const {
+ if (source.Equals(destination)) return;
+
+ DCHECK(instruction->AsPhi() == nullptr);
+
+ HInstruction* previous = instruction->GetPrevious();
+ HParallelMove* move = nullptr;
+ if (previous == nullptr
+ || previous->AsParallelMove() == nullptr
+ || !IsInputMove(previous)) {
+ move = new (allocator_) HParallelMove(allocator_);
+ move->SetLifetimePosition(kInputMoveLifetimePosition);
+ instruction->GetBlock()->InsertInstructionBefore(move, instruction);
+ } else {
+ move = previous->AsParallelMove();
+ }
+ DCHECK(IsInputMove(move));
+ move->AddMove(new (allocator_) MoveOperands(source, destination));
+}
+
+void RegisterAllocator::InsertParallelMoveAt(size_t position,
+ Location source,
+ Location destination) const {
+ if (source.Equals(destination)) return;
+
+ HInstruction* at = liveness_.GetInstructionFromPosition(position / 2);
+ if (at == nullptr) {
+ // Block boundary, don't no anything the connection of split siblings will handle it.
+ return;
+ }
+ HParallelMove* move;
+ if ((position & 1) == 1) {
+ // Move must happen after the instruction.
+ DCHECK(!at->IsControlFlow());
+ move = at->GetNext()->AsParallelMove();
+ if (move == nullptr || IsInputMove(move)) {
+ move = new (allocator_) HParallelMove(allocator_);
+ move->SetLifetimePosition(position);
+ at->GetBlock()->InsertInstructionBefore(move, at->GetNext());
+ }
+ } else {
+ // Move must happen before the instruction.
+ HInstruction* previous = at->GetPrevious();
+ if (previous != nullptr && previous->AsParallelMove() != nullptr) {
+ if (IsInputMove(previous)) {
+ previous = previous->GetPrevious();
+ }
+ }
+ if (previous == nullptr || previous->AsParallelMove() == nullptr) {
+ move = new (allocator_) HParallelMove(allocator_);
+ move->SetLifetimePosition(position);
+ at->GetBlock()->InsertInstructionBefore(move, at);
+ } else {
+ move = previous->AsParallelMove();
+ }
+ }
+ move->AddMove(new (allocator_) MoveOperands(source, destination));
+}
+
+void RegisterAllocator::InsertParallelMoveAtExitOf(HBasicBlock* block,
+ Location source,
+ Location destination) const {
+ if (source.Equals(destination)) return;
+
+ DCHECK_EQ(block->GetSuccessors().Size(), 1u);
+ HInstruction* last = block->GetLastInstruction();
+ HInstruction* previous = last->GetPrevious();
+ HParallelMove* move;
+ if (previous == nullptr || previous->AsParallelMove() == nullptr) {
+ move = new (allocator_) HParallelMove(allocator_);
+ block->InsertInstructionBefore(move, last);
+ } else {
+ move = previous->AsParallelMove();
+ }
+ move->AddMove(new (allocator_) MoveOperands(source, destination));
+}
+
+void RegisterAllocator::InsertParallelMoveAtEntryOf(HBasicBlock* block,
+ Location source,
+ Location destination) const {
+ if (source.Equals(destination)) return;
+
+ HInstruction* first = block->GetFirstInstruction();
+ HParallelMove* move = first->AsParallelMove();
+ if (move == nullptr || IsInputMove(move)) {
+ move = new (allocator_) HParallelMove(allocator_);
+ move->SetLifetimePosition(block->GetLifetimeStart());
+ block->InsertInstructionBefore(move, first);
+ }
+ move->AddMove(new (allocator_) MoveOperands(source, destination));
+}
+
+void RegisterAllocator::InsertMoveAfter(HInstruction* instruction,
+ Location source,
+ Location destination) const {
+ if (source.Equals(destination)) return;
+
+ if (instruction->AsPhi() != nullptr) {
+ InsertParallelMoveAtEntryOf(instruction->GetBlock(), source, destination);
+ return;
+ }
+
+ HParallelMove* move = instruction->GetNext()->AsParallelMove();
+ if (move == nullptr || IsInputMove(move)) {
+ move = new (allocator_) HParallelMove(allocator_);
+ instruction->GetBlock()->InsertInstructionBefore(move, instruction->GetNext());
+ }
+ move->AddMove(new (allocator_) MoveOperands(source, destination));
+}
+
+void RegisterAllocator::ConnectSiblings(LiveInterval* interval) {
+ LiveInterval* current = interval;
+ if (current->HasSpillSlot() && current->HasRegister()) {
+ // We spill eagerly, so move must be at definition.
+ InsertMoveAfter(interval->GetDefinedBy(),
+ Location::RegisterLocation(ManagedRegister(interval->GetRegister())),
+ Location::StackSlot(interval->GetParent()->GetSpillSlot()));
+ }
+ UsePosition* use = current->GetFirstUse();
+
+ // Walk over all siblings, updating locations of use positions, and
+ // connecting them when they are adjacent.
+ do {
+ Location source = ConvertToLocation(current);
+
+ // 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();
+ Location expected_location = locations->InAt(use->GetInputIndex());
+ if (expected_location.IsUnallocated()) {
+ locations->SetInAt(use->GetInputIndex(), source);
+ } else {
+ AddInputMoveFor(use->GetUser(), source, expected_location);
+ }
+ }
+ use = use->GetNext();
+ }
+
+ // If the next interval starts just after this one, and has a register,
+ // insert a move.
+ LiveInterval* next_sibling = current->GetNextSibling();
+ if (next_sibling != nullptr
+ && next_sibling->HasRegister()
+ && current->GetEnd() == next_sibling->GetStart()) {
+ Location destination = ConvertToLocation(next_sibling);
+ InsertParallelMoveAt(current->GetEnd(), source, destination);
+ }
+ current = next_sibling;
+ } while (current != nullptr);
+ DCHECK(use == nullptr);
+}
+
+void RegisterAllocator::ConnectSplitSiblings(LiveInterval* interval,
+ HBasicBlock* from,
+ HBasicBlock* to) const {
+ if (interval->GetNextSibling() == nullptr) {
+ // Nothing to connect. The whole range was allocated to the same location.
+ return;
+ }
+
+ size_t from_position = from->GetLifetimeEnd() - 1;
+ size_t to_position = to->GetLifetimeStart();
+
+ LiveInterval* destination = nullptr;
+ LiveInterval* source = nullptr;
+
+ LiveInterval* current = interval;
+
+ // Check the intervals that cover `from` and `to`.
+ while ((current != nullptr) && (source == nullptr || destination == nullptr)) {
+ if (current->Covers(from_position)) {
+ DCHECK(source == nullptr);
+ source = current;
+ }
+ if (current->Covers(to_position)) {
+ DCHECK(destination == nullptr);
+ destination = current;
+ }
+
+ current = current->GetNextSibling();
+ }
+
+ if (destination == source) {
+ // Interval was not split.
+ return;
+ }
+
+ if (!destination->HasRegister()) {
+ // Values are eagerly spilled. Spill slot already contains appropriate value.
+ return;
+ }
+
+ // If `from` has only one successor, we can put the moves at the exit of it. Otherwise
+ // we need to put the moves at the entry of `to`.
+ if (from->GetSuccessors().Size() == 1) {
+ InsertParallelMoveAtExitOf(from, ConvertToLocation(source), ConvertToLocation(destination));
+ } else {
+ DCHECK_EQ(to->GetPredecessors().Size(), 1u);
+ InsertParallelMoveAtEntryOf(to, ConvertToLocation(source), ConvertToLocation(destination));
+ }
+}
+
+// Returns the location of `interval`, or siblings of `interval`, at `position`.
+static Location FindLocationAt(LiveInterval* interval, size_t position) {
+ LiveInterval* current = interval;
+ while (!current->Covers(position)) {
+ current = current->GetNextSibling();
+ DCHECK(current != nullptr);
+ }
+ return ConvertToLocation(current);
+}
+
+void RegisterAllocator::Resolve() {
+ codegen_->ComputeFrameSize(spill_slots_.Size());
+
+ // Adjust the Out Location of instructions.
+ // TODO: Use pointers of Location inside LiveInterval to avoid doing another iteration.
+ for (size_t i = 0, e = liveness_.GetNumberOfSsaValues(); i < e; ++i) {
+ HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i);
+ LiveInterval* current = instruction->GetLiveInterval();
+ LocationSummary* locations = instruction->GetLocations();
+ Location location = locations->Out();
+ if (instruction->AsParameterValue() != nullptr) {
+ // Now that we know the frame size, adjust the parameter's location.
+ if (location.IsStackSlot()) {
+ location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
+ current->SetSpillSlot(location.GetStackIndex());
+ locations->SetOut(location);
+ } else if (location.IsDoubleStackSlot()) {
+ location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
+ current->SetSpillSlot(location.GetStackIndex());
+ locations->SetOut(location);
+ } else if (current->HasSpillSlot()) {
+ current->SetSpillSlot(current->GetSpillSlot() + codegen_->GetFrameSize());
+ }
+ }
+
+ Location source = ConvertToLocation(current);
+
+ if (location.IsUnallocated()) {
+ if (location.GetPolicy() == Location::kSameAsFirstInput) {
+ locations->SetInAt(0, source);
+ }
+ locations->SetOut(source);
+ } else {
+ DCHECK(source.Equals(location));
+ }
+ }
+
+ // Connect siblings.
+ for (size_t i = 0, e = liveness_.GetNumberOfSsaValues(); i < e; ++i) {
+ HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i);
+ ConnectSiblings(instruction->GetLiveInterval());
+ }
+
+ // Resolve non-linear control flow across branches. Order does not matter.
+ for (HLinearOrderIterator it(liveness_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ BitVector* live = liveness_.GetLiveInSet(*block);
+ for (uint32_t idx : live->Indexes()) {
+ HInstruction* current = liveness_.GetInstructionFromSsaIndex(idx);
+ LiveInterval* interval = current->GetLiveInterval();
+ for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) {
+ ConnectSplitSiblings(interval, block->GetPredecessors().Get(i), block);
+ }
+ }
+ }
+
+ // Resolve phi inputs. Order does not matter.
+ for (HLinearOrderIterator it(liveness_); !it.Done(); it.Advance()) {
+ HBasicBlock* current = it.Current();
+ for (HInstructionIterator it(current->GetPhis()); !it.Done(); it.Advance()) {
+ HInstruction* phi = it.Current();
+ for (size_t i = 0, e = current->GetPredecessors().Size(); i < e; ++i) {
+ HBasicBlock* predecessor = current->GetPredecessors().Get(i);
+ DCHECK_EQ(predecessor->GetSuccessors().Size(), 1u);
+ HInstruction* input = phi->InputAt(i);
+ Location source = FindLocationAt(input->GetLiveInterval(),
+ predecessor->GetLastInstruction()->GetLifetimePosition());
+ Location destination = ConvertToLocation(phi->GetLiveInterval());
+ InsertParallelMoveAtExitOf(predecessor, source, destination);
+ }
+ }
+ }
}
} // namespace art
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index 3393a04..1b5585f 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -23,7 +23,12 @@
namespace art {
class CodeGenerator;
+class HBasicBlock;
+class HGraph;
+class HInstruction;
+class HParallelMove;
class LiveInterval;
+class Location;
class SsaLivenessAnalysis;
/**
@@ -31,26 +36,23 @@
*/
class RegisterAllocator {
public:
- RegisterAllocator(ArenaAllocator* allocator, const CodeGenerator& codegen);
+ RegisterAllocator(ArenaAllocator* allocator,
+ CodeGenerator* codegen,
+ const SsaLivenessAnalysis& analysis);
// Main entry point for the register allocator. Given the liveness analysis,
// allocates registers to live intervals.
- void AllocateRegisters(const SsaLivenessAnalysis& liveness) {
- processing_core_registers_ = true;
- AllocateRegistersInternal(liveness);
- processing_core_registers_ = false;
- AllocateRegistersInternal(liveness);
- }
+ void AllocateRegisters();
// Validate that the register allocator did not allocate the same register to
// intervals that intersect each other. Returns false if it did not.
- bool Validate(const SsaLivenessAnalysis& liveness, bool log_fatal_on_failure) {
+ bool Validate(bool log_fatal_on_failure) {
processing_core_registers_ = true;
- if (!ValidateInternal(liveness, log_fatal_on_failure)) {
+ if (!ValidateInternal(log_fatal_on_failure)) {
return false;
}
processing_core_registers_ = false;
- return ValidateInternal(liveness, log_fatal_on_failure);
+ return ValidateInternal(log_fatal_on_failure);
}
// Helper method for validation. Used by unit testing.
@@ -61,11 +63,21 @@
bool processing_core_registers,
bool log_fatal_on_failure);
+ static bool CanAllocateRegistersFor(const HGraph& graph, InstructionSet instruction_set);
+ static bool Supports(InstructionSet instruction_set) {
+ return instruction_set == kX86;
+ }
+
+ size_t GetNumberOfSpillSlots() const {
+ return spill_slots_.Size();
+ }
+
private:
// Main methods of the allocator.
void LinearScan();
bool TryAllocateFreeReg(LiveInterval* interval);
bool AllocateBlockedReg(LiveInterval* interval);
+ void Resolve();
// Add `interval` in the sorted list of unhandled intervals.
void AddToUnhandled(LiveInterval* interval);
@@ -76,16 +88,33 @@
// Returns whether `reg` is blocked by the code generator.
bool IsBlocked(int reg) const;
+ // Update the interval for the register in `location` to cover [start, end).
+ void BlockRegister(Location location, size_t start, size_t end, Primitive::Type type);
+
// Allocate a spill slot for the given interval.
void AllocateSpillSlotFor(LiveInterval* interval);
+ // Connect adjacent siblings within blocks.
+ void ConnectSiblings(LiveInterval* interval);
+
+ // Connect siblings between block entries and exits.
+ void ConnectSplitSiblings(LiveInterval* interval, HBasicBlock* from, HBasicBlock* to) const;
+
+ // Helper methods to insert parallel moves in the graph.
+ void InsertParallelMoveAtExitOf(HBasicBlock* block, Location source, Location destination) const;
+ void InsertParallelMoveAtEntryOf(HBasicBlock* block, Location source, Location destination) const;
+ void InsertMoveAfter(HInstruction* instruction, Location source, Location destination) const;
+ void AddInputMoveFor(HInstruction* instruction, Location source, Location destination) const;
+ void InsertParallelMoveAt(size_t position, Location source, Location destination) const;
+
// Helper methods.
- void AllocateRegistersInternal(const SsaLivenessAnalysis& liveness);
- bool ValidateInternal(const SsaLivenessAnalysis& liveness, bool log_fatal_on_failure) const;
- void DumpInterval(std::ostream& stream, LiveInterval* interval);
+ void AllocateRegistersInternal();
+ bool ValidateInternal(bool log_fatal_on_failure) const;
+ void DumpInterval(std::ostream& stream, LiveInterval* interval) const;
ArenaAllocator* const allocator_;
- const CodeGenerator& codegen_;
+ 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.
@@ -102,6 +131,10 @@
// 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
+ // where an instruction requires a specific register.
+ GrowableArray<LiveInterval*> physical_register_intervals_;
+
// The spill slots allocated for live intervals.
GrowableArray<size_t> spill_slots_;
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index ff9b9be..bfabc5a 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -43,9 +43,9 @@
CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86);
SsaLivenessAnalysis liveness(*graph, codegen);
liveness.Analyze();
- RegisterAllocator register_allocator(&allocator, *codegen);
- register_allocator.AllocateRegisters(liveness);
- return register_allocator.Validate(liveness, false);
+ RegisterAllocator register_allocator(&allocator, codegen, liveness);
+ register_allocator.AllocateRegisters();
+ return register_allocator.Validate(false);
}
/**
@@ -300,9 +300,9 @@
CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86);
SsaLivenessAnalysis liveness(*graph, codegen);
liveness.Analyze();
- RegisterAllocator register_allocator(&allocator, *codegen);
- register_allocator.AllocateRegisters(liveness);
- ASSERT_TRUE(register_allocator.Validate(liveness, false));
+ RegisterAllocator register_allocator(&allocator, codegen, liveness);
+ register_allocator.AllocateRegisters();
+ ASSERT_TRUE(register_allocator.Validate(false));
HBasicBlock* loop_header = graph->GetBlocks().Get(2);
HPhi* phi = loop_header->GetFirstPhi()->AsPhi();
@@ -314,7 +314,7 @@
ASSERT_NE(phi_interval->GetRegister(), loop_update->GetRegister());
HBasicBlock* return_block = graph->GetBlocks().Get(3);
- HReturn* ret = return_block->GetFirstInstruction()->AsReturn();
+ HReturn* ret = return_block->GetLastInstruction()->AsReturn();
ASSERT_EQ(phi_interval->GetRegister(), ret->InputAt(0)->GetLiveInterval()->GetRegister());
}
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 7903ad6..fc3eb66 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -172,6 +172,7 @@
// Last use is in the following block.
first_range_->start_ = start_block_position;
} else {
+ DCHECK(first_range_->GetStart() > position);
// There is a hole in the interval. Create a new range.
first_range_ = new (allocator_) LiveRange(start_block_position, position, first_range_);
}
@@ -192,6 +193,7 @@
// There is a use in the following block.
first_range_->start_ = start;
} else {
+ DCHECK(first_range_->GetStart() > end);
// There is a hole in the interval. Create a new range.
first_range_ = new (allocator_) LiveRange(start, end, first_range_);
}
diff --git a/compiler/utils/arena_allocator.cc b/compiler/utils/arena_allocator.cc
index 6a39641..925d4a2 100644
--- a/compiler/utils/arena_allocator.cc
+++ b/compiler/utils/arena_allocator.cc
@@ -139,7 +139,7 @@
if (kUseMemSet || !kUseMemMap) {
memset(Begin(), 0, bytes_allocated_);
} else {
- madvise(Begin(), bytes_allocated_, MADV_DONTNEED);
+ map_->MadviseDontNeedAndZero();
}
bytes_allocated_ = 0;
}
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index a14551c..b07eed3 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -1650,7 +1650,7 @@
pushq(spill_regs.at(i).AsX86_64().AsCpuRegister());
}
// return address then method on stack
- addq(CpuRegister(RSP), Immediate(-frame_size + (spill_regs.size() * kFramePointerSize) +
+ addq(CpuRegister(RSP), Immediate(-static_cast<int64_t>(frame_size) + (spill_regs.size() * kFramePointerSize) +
sizeof(StackReference<mirror::ArtMethod>) /*method*/ +
kFramePointerSize /*return address*/));
@@ -1682,7 +1682,7 @@
void X86_64Assembler::RemoveFrame(size_t frame_size,
const std::vector<ManagedRegister>& spill_regs) {
CHECK_ALIGNED(frame_size, kStackAlignment);
- addq(CpuRegister(RSP), Immediate(frame_size - (spill_regs.size() * kFramePointerSize) - kFramePointerSize));
+ addq(CpuRegister(RSP), Immediate(static_cast<int64_t>(frame_size) - (spill_regs.size() * kFramePointerSize) - kFramePointerSize));
for (size_t i = 0; i < spill_regs.size(); ++i) {
popq(spill_regs.at(i).AsX86_64().AsCpuRegister());
}
@@ -1691,7 +1691,7 @@
void X86_64Assembler::IncreaseFrameSize(size_t adjust) {
CHECK_ALIGNED(adjust, kStackAlignment);
- addq(CpuRegister(RSP), Immediate(-adjust));
+ addq(CpuRegister(RSP), Immediate(-static_cast<int64_t>(adjust)));
}
void X86_64Assembler::DecreaseFrameSize(size_t adjust) {
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 548d379..6276603 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -29,6 +29,13 @@
namespace art {
namespace x86_64 {
+// Encodes an immediate value for operands.
+//
+// Note: Immediates can be 64b on x86-64 for certain instructions, but are often restricted
+// to 32b.
+//
+// Note: As we support cross-compilation, the value type must be int64_t. Please be aware of
+// conversion rules in expressions regarding negation, especially size_t on 32b.
class Immediate {
public:
explicit Immediate(int64_t value) : value_(value) {}
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 7201d04..799db9f 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -200,4 +200,122 @@
DriverFn(&setcc_test_fn, "setcc");
}
+static x86_64::X86_64ManagedRegister ManagedFromCpu(x86_64::Register r) {
+ return x86_64::X86_64ManagedRegister::FromCpuRegister(r);
+}
+
+static x86_64::X86_64ManagedRegister ManagedFromFpu(x86_64::FloatRegister r) {
+ return x86_64::X86_64ManagedRegister::FromXmmRegister(r);
+}
+
+std::string buildframe_test_fn(x86_64::X86_64Assembler* assembler) {
+ // TODO: more interesting spill registers / entry spills.
+
+ // Two random spill regs.
+ std::vector<ManagedRegister> spill_regs;
+ spill_regs.push_back(ManagedFromCpu(x86_64::R10));
+ spill_regs.push_back(ManagedFromCpu(x86_64::RSI));
+
+ // Three random entry spills.
+ ManagedRegisterEntrySpills entry_spills;
+ ManagedRegisterSpill spill(ManagedFromCpu(x86_64::RAX), 8, 0);
+ entry_spills.push_back(spill);
+ ManagedRegisterSpill spill2(ManagedFromCpu(x86_64::RBX), 8, 8);
+ entry_spills.push_back(spill2);
+ ManagedRegisterSpill spill3(ManagedFromFpu(x86_64::XMM1), 8, 16);
+ entry_spills.push_back(spill3);
+
+ x86_64::X86_64ManagedRegister method_reg = ManagedFromCpu(x86_64::RDI);
+
+ size_t frame_size = 10 * kStackAlignment;
+ assembler->BuildFrame(10 * kStackAlignment, method_reg, spill_regs, entry_spills);
+
+ // Construct assembly text counterpart.
+ std::ostringstream str;
+ // 1) Push the spill_regs.
+ str << "pushq %rsi\n";
+ str << "pushq %r10\n";
+ // 2) Move down the stack pointer.
+ ssize_t displacement = -static_cast<ssize_t>(frame_size) + spill_regs.size() * 8 +
+ sizeof(StackReference<mirror::ArtMethod>) + 8;
+ str << "addq $" << displacement << ", %rsp\n";
+ // 3) Make space for method reference, and store it.
+ str << "subq $4, %rsp\n";
+ str << "movl %edi, (%rsp)\n";
+ // 4) Entry spills.
+ str << "movq %rax, " << frame_size + 0 << "(%rsp)\n";
+ str << "movq %rbx, " << frame_size + 8 << "(%rsp)\n";
+ str << "movsd %xmm1, " << frame_size + 16 << "(%rsp)\n";
+
+ return str.str();
+}
+
+TEST_F(AssemblerX86_64Test, BuildFrame) {
+ DriverFn(&buildframe_test_fn, "BuildFrame");
+}
+
+std::string removeframe_test_fn(x86_64::X86_64Assembler* assembler) {
+ // TODO: more interesting spill registers / entry spills.
+
+ // Two random spill regs.
+ std::vector<ManagedRegister> spill_regs;
+ spill_regs.push_back(ManagedFromCpu(x86_64::R10));
+ spill_regs.push_back(ManagedFromCpu(x86_64::RSI));
+
+ size_t frame_size = 10 * kStackAlignment;
+ assembler->RemoveFrame(10 * kStackAlignment, spill_regs);
+
+ // Construct assembly text counterpart.
+ std::ostringstream str;
+ // 1) Move up the stack pointer.
+ ssize_t displacement = static_cast<ssize_t>(frame_size) - spill_regs.size() * 8 - 8;
+ str << "addq $" << displacement << ", %rsp\n";
+ // 2) Pop spill regs.
+ str << "popq %r10\n";
+ str << "popq %rsi\n";
+ str << "ret\n";
+
+ return str.str();
+}
+
+TEST_F(AssemblerX86_64Test, RemoveFrame) {
+ DriverFn(&removeframe_test_fn, "RemoveFrame");
+}
+
+std::string increaseframe_test_fn(x86_64::X86_64Assembler* assembler) {
+ assembler->IncreaseFrameSize(0U);
+ assembler->IncreaseFrameSize(kStackAlignment);
+ assembler->IncreaseFrameSize(10 * kStackAlignment);
+
+ // Construct assembly text counterpart.
+ std::ostringstream str;
+ str << "addq $0, %rsp\n";
+ str << "addq $-" << kStackAlignment << ", %rsp\n";
+ str << "addq $-" << 10 * kStackAlignment << ", %rsp\n";
+
+ return str.str();
+}
+
+TEST_F(AssemblerX86_64Test, IncreaseFrame) {
+ DriverFn(&increaseframe_test_fn, "IncreaseFrame");
+}
+
+std::string decreaseframe_test_fn(x86_64::X86_64Assembler* assembler) {
+ assembler->DecreaseFrameSize(0U);
+ assembler->DecreaseFrameSize(kStackAlignment);
+ assembler->DecreaseFrameSize(10 * kStackAlignment);
+
+ // Construct assembly text counterpart.
+ std::ostringstream str;
+ str << "addq $0, %rsp\n";
+ str << "addq $" << kStackAlignment << ", %rsp\n";
+ str << "addq $" << 10 * kStackAlignment << ", %rsp\n";
+
+ return str.str();
+}
+
+TEST_F(AssemblerX86_64Test, DecreaseFrame) {
+ DriverFn(&decreaseframe_test_fn, "DecreaseFrame");
+}
+
} // namespace art
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
index 03d32f0..31fcd17 100644
--- a/dalvikvm/Android.mk
+++ b/dalvikvm/Android.mk
@@ -38,7 +38,6 @@
ART_TARGET_EXECUTABLES += $(TARGET_OUT_EXECUTABLES)/$(LOCAL_MODULE)
-ifeq ($(WITH_HOST_DALVIK),true)
include $(CLEAR_VARS)
LOCAL_MODULE := dalvikvm
LOCAL_MODULE_TAGS := optional
@@ -54,4 +53,3 @@
include external/libcxx/libcxx.mk
include $(BUILD_HOST_EXECUTABLE)
ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)
-endif
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index c17788e..28db711 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -36,12 +36,10 @@
$(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler,art/compiler,target,debug,$(dex2oat_arch)))
endif
-ifeq ($(WITH_HOST_DALVIK),true)
- # We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
- ifeq ($(ART_BUILD_NDEBUG),true)
- $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart-compiler,art/compiler,host,ndebug))
- endif
- ifeq ($(ART_BUILD_DEBUG),true)
- $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd-compiler,art/compiler,host,debug))
- endif
+# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
+ifeq ($(ART_BUILD_NDEBUG),true)
+ $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart-compiler,art/compiler,host,ndebug))
+endif
+ifeq ($(ART_BUILD_DEBUG),true)
+ $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd-compiler,art/compiler,host,debug))
endif
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
index b4b194d..feacbde 100644
--- a/disassembler/Android.mk
+++ b/disassembler/Android.mk
@@ -98,12 +98,10 @@
ifeq ($(ART_BUILD_TARGET_DEBUG),true)
$(eval $(call build-libart-disassembler,target,debug))
endif
-ifeq ($(WITH_HOST_DALVIK),true)
- # We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
- ifeq ($(ART_BUILD_NDEBUG),true)
- $(eval $(call build-libart-disassembler,host,ndebug))
- endif
- ifeq ($(ART_BUILD_DEBUG),true)
- $(eval $(call build-libart-disassembler,host,debug))
- endif
+# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
+ifeq ($(ART_BUILD_NDEBUG),true)
+ $(eval $(call build-libart-disassembler,host,ndebug))
+endif
+ifeq ($(ART_BUILD_DEBUG),true)
+ $(eval $(call build-libart-disassembler,host,debug))
endif
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index 7cee00e..ecf6a0b 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -28,11 +28,9 @@
$(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler,art/disassembler,target,debug))
endif
-ifeq ($(WITH_HOST_DALVIK),true)
- ifeq ($(ART_BUILD_HOST_NDEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler,art/disassembler,host,ndebug))
- endif
- ifeq ($(ART_BUILD_HOST_DEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler,art/disassembler,host,debug))
- endif
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler,art/disassembler,host,ndebug))
+endif
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler,art/disassembler,host,debug))
endif
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 8f8eeba..c40ae7a8 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -196,7 +196,8 @@
LIBART_GCC_ONLY_SRC_FILES := \
interpreter/interpreter_goto_table_impl.cc
-LIBART_LDFLAGS := -Wl,--no-fatal-warnings
+LIBART_TARGET_LDFLAGS := -Wl,--no-fatal-warnings
+LIBART_HOST_LDFLAGS :=
LIBART_TARGET_SRC_FILES := \
$(LIBART_COMMON_SRC_FILES) \
@@ -368,6 +369,11 @@
LOCAL_CFLAGS := $(LIBART_CFLAGS)
LOCAL_LDFLAGS := $(LIBART_LDFLAGS)
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_LDFLAGS += $(LIBART_TARGET_LDFLAGS)
+ else
+ LOCAL_LDFLAGS += $(LIBART_HOST_LDFLAGS)
+ endif
$(foreach arch,$(ART_SUPPORTED_ARCH),
LOCAL_LDFLAGS_$(arch) := $$(LIBART_TARGET_LDFLAGS_$(arch)))
@@ -442,13 +448,11 @@
# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since
# they are used to cross compile for the target.
-ifeq ($(WITH_HOST_DALVIK),true)
- ifeq ($(ART_BUILD_NDEBUG),true)
- $(eval $(call build-libart,host,ndebug))
- endif
- ifeq ($(ART_BUILD_DEBUG),true)
- $(eval $(call build-libart,host,debug))
- endif
+ifeq ($(ART_BUILD_NDEBUG),true)
+ $(eval $(call build-libart,host,ndebug))
+endif
+ifeq ($(ART_BUILD_DEBUG),true)
+ $(eval $(call build-libart,host,debug))
endif
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 3be0faf..59311bc 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -77,9 +77,10 @@
#if defined(__i386__)
// TODO: Set the thread?
__asm__ __volatile__(
- "pushl %[referrer]\n\t" // Store referrer
+ "subl $12, %%esp\n\t" // Align stack.
+ "pushl %[referrer]\n\t" // Store referrer.
"call *%%edi\n\t" // Call the stub
- "addl $4, %%esp" // Pop referrer
+ "addl $16, %%esp" // Pop referrer
: "=a" (result)
// Use the result from eax
: "a"(arg0), "c"(arg1), "d"(arg2), "D"(code), [referrer]"r"(referrer)
@@ -300,9 +301,10 @@
// TODO: Set the thread?
__asm__ __volatile__(
"movd %[hidden], %%xmm0\n\t"
+ "subl $12, %%esp\n\t" // Align stack.
"pushl %[referrer]\n\t" // Store referrer
"call *%%edi\n\t" // Call the stub
- "addl $4, %%esp" // Pop referrer
+ "addl $16, %%esp" // Pop referrer
: "=a" (result)
// Use the result from eax
: "a"(arg0), "c"(arg1), "d"(arg2), "D"(code), [referrer]"m"(referrer), [hidden]"r"(hidden)
diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S
index f1d0746..ae39be1 100644
--- a/runtime/arch/x86/asm_support_x86.S
+++ b/runtime/arch/x86/asm_support_x86.S
@@ -28,6 +28,7 @@
#define END_MACRO .endmacro
// Clang's as(1) uses $0, $1, and so on for macro arguments.
+ #define RAW_VAR(name,index) $index
#define VAR(name,index) SYMBOL($index)
#define PLT_VAR(name, index) SYMBOL($index)
#define REG_VAR(name,index) %$index
@@ -50,6 +51,7 @@
// no special meaning to $, so literals are still just $x. The use of altmacro means % is a
// special character meaning care needs to be taken when passing registers as macro arguments.
.altmacro
+ #define RAW_VAR(name,index) name&
#define VAR(name,index) name&
#define PLT_VAR(name, index) name&@PLT
#define REG_VAR(name,index) %name
@@ -94,7 +96,7 @@
#if !defined(__APPLE__)
#define SYMBOL(name) name
#if defined(__clang__) && (__clang_major__ < 4) && (__clang_minor__ < 5)
- // TODO: Disabled for old clang 3.3, this leads to text reolocations and there should be a
+ // TODO: Disabled for old clang 3.3, this leads to text relocations and there should be a
// better fix.
#define PLT_SYMBOL(name) name // ## @PLT
#else
@@ -151,8 +153,10 @@
END_MACRO
MACRO0(SETUP_GOT_NOSAVE)
+#ifndef __APPLE__
call __x86.get_pc_thunk.bx
addl $_GLOBAL_OFFSET_TABLE_, %ebx
+#endif
END_MACRO
MACRO0(SETUP_GOT)
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 989ecf9..28e4dd6 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -111,7 +111,7 @@
END_MACRO
MACRO2(NO_ARG_RUNTIME_EXCEPTION, c_name, cxx_name)
- DEFINE_FUNCTION VAR(c_name, 0)
+ DEFINE_FUNCTION RAW_VAR(c_name, 0)
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
mov %esp, %ecx
// Outgoing argument set up
@@ -123,11 +123,11 @@
SETUP_GOT_NOSAVE // clobbers ebx (harmless here)
call PLT_VAR(cxx_name, 1) // cxx_name(Thread*, SP)
int3 // unreached
- END_FUNCTION VAR(c_name, 0)
+ END_FUNCTION RAW_VAR(c_name, 0)
END_MACRO
MACRO2(ONE_ARG_RUNTIME_EXCEPTION, c_name, cxx_name)
- DEFINE_FUNCTION VAR(c_name, 0)
+ DEFINE_FUNCTION RAW_VAR(c_name, 0)
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
mov %esp, %ecx
// Outgoing argument set up
@@ -139,11 +139,11 @@
SETUP_GOT_NOSAVE // clobbers ebx (harmless here)
call PLT_VAR(cxx_name, 1) // cxx_name(arg1, Thread*, SP)
int3 // unreached
- END_FUNCTION VAR(c_name, 0)
+ END_FUNCTION RAW_VAR(c_name, 0)
END_MACRO
MACRO2(TWO_ARG_RUNTIME_EXCEPTION, c_name, cxx_name)
- DEFINE_FUNCTION VAR(c_name, 0)
+ DEFINE_FUNCTION RAW_VAR(c_name, 0)
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
mov %esp, %edx
// Outgoing argument set up
@@ -155,7 +155,7 @@
SETUP_GOT_NOSAVE // clobbers ebx (harmless here)
call PLT_VAR(cxx_name, 1) // cxx_name(arg1, arg2, Thread*, SP)
int3 // unreached
- END_FUNCTION VAR(c_name, 0)
+ END_FUNCTION RAW_VAR(c_name, 0)
END_MACRO
/*
@@ -207,7 +207,7 @@
* pointing back to the original caller.
*/
MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name)
- DEFINE_FUNCTION VAR(c_name, 0)
+ DEFINE_FUNCTION RAW_VAR(c_name, 0)
// Set up the callee save frame to conform with Runtime::CreateCalleeSaveMethod(kRefsAndArgs)
// return address
PUSH edi
@@ -248,7 +248,7 @@
addl MACRO_LITERAL(4), %esp // Pop code pointer off stack
CFI_ADJUST_CFA_OFFSET(-4)
DELIVER_PENDING_EXCEPTION
- END_FUNCTION VAR(c_name, 0)
+ END_FUNCTION RAW_VAR(c_name, 0)
END_MACRO
INVOKE_TRAMPOLINE art_quick_invoke_interface_trampoline, artInvokeInterfaceTrampoline
@@ -315,7 +315,7 @@
END_FUNCTION art_quick_invoke_stub
MACRO3(NO_ARG_DOWNCALL, c_name, cxx_name, return_macro)
- DEFINE_FUNCTION VAR(c_name, 0)
+ DEFINE_FUNCTION RAW_VAR(c_name, 0)
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
mov %esp, %edx // remember SP
SETUP_GOT_NOSAVE // clobbers ebx (harmless here)
@@ -330,11 +330,11 @@
CFI_ADJUST_CFA_OFFSET(-16)
RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
CALL_MACRO(return_macro, 2) // return or deliver exception
- END_FUNCTION VAR(c_name, 0)
+ END_FUNCTION RAW_VAR(c_name, 0)
END_MACRO
MACRO3(ONE_ARG_DOWNCALL, c_name, cxx_name, return_macro)
- DEFINE_FUNCTION VAR(c_name, 0)
+ DEFINE_FUNCTION RAW_VAR(c_name, 0)
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
mov %esp, %edx // remember SP
SETUP_GOT_NOSAVE // clobbers EBX
@@ -349,11 +349,11 @@
CFI_ADJUST_CFA_OFFSET(-16)
RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
CALL_MACRO(return_macro, 2) // return or deliver exception
- END_FUNCTION VAR(c_name, 0)
+ END_FUNCTION RAW_VAR(c_name, 0)
END_MACRO
MACRO3(TWO_ARG_DOWNCALL, c_name, cxx_name, return_macro)
- DEFINE_FUNCTION VAR(c_name, 0)
+ DEFINE_FUNCTION RAW_VAR(c_name, 0)
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
mov %esp, %edx // remember SP
SETUP_GOT_NOSAVE // clobbers EBX
@@ -368,11 +368,11 @@
CFI_ADJUST_CFA_OFFSET(-16)
RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
CALL_MACRO(return_macro, 2) // return or deliver exception
- END_FUNCTION VAR(c_name, 0)
+ END_FUNCTION RAW_VAR(c_name, 0)
END_MACRO
MACRO3(THREE_ARG_DOWNCALL, c_name, cxx_name, return_macro)
- DEFINE_FUNCTION VAR(c_name, 0)
+ DEFINE_FUNCTION RAW_VAR(c_name, 0)
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
mov %esp, %ebx // remember SP
// Outgoing argument set up
@@ -390,7 +390,7 @@
CFI_ADJUST_CFA_OFFSET(-32)
RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
CALL_MACRO(return_macro, 2) // return or deliver exception
- END_FUNCTION VAR(c_name, 0)
+ END_FUNCTION RAW_VAR(c_name, 0)
END_MACRO
MACRO0(RETURN_IF_RESULT_IS_NON_ZERO)
@@ -653,17 +653,17 @@
*/
DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check
testl %eax, %eax
- jnz art_quick_aput_obj_with_bound_check
- jmp art_quick_throw_null_pointer_exception
+ jnz SYMBOL(art_quick_aput_obj_with_bound_check)
+ jmp SYMBOL(art_quick_throw_null_pointer_exception)
END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
DEFINE_FUNCTION art_quick_aput_obj_with_bound_check
movl ARRAY_LENGTH_OFFSET(%eax), %ebx
cmpl %ebx, %ecx
- jb art_quick_aput_obj
+ jb SYMBOL(art_quick_aput_obj)
mov %ecx, %eax
mov %ebx, %ecx
- jmp art_quick_throw_array_bounds
+ jmp SYMBOL(art_quick_throw_array_bounds)
END_FUNCTION art_quick_aput_obj_with_bound_check
DEFINE_FUNCTION art_quick_aput_obj
@@ -1122,7 +1122,7 @@
movd %xmm0, %ecx // get target method index stored in xmm0
movl OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4), %eax // load the target method
POP ecx
- jmp art_quick_invoke_interface_trampoline
+ jmp SYMBOL(art_quick_invoke_interface_trampoline)
END_FUNCTION art_quick_imt_conflict_trampoline
DEFINE_FUNCTION art_quick_resolution_trampoline
@@ -1218,10 +1218,10 @@
jnz .Lexception_in_native
// Tear down the callee-save frame.
- addl MACRO_LITERAL(4), %esp // Remove padding
+ addl LITERAL(4), %esp // Remove padding
CFI_ADJUST_CFA_OFFSET(-4)
POP ecx
- addl MACRO_LITERAL(4), %esp // Avoid edx, as it may be part of the result.
+ addl LITERAL(4), %esp // Avoid edx, as it may be part of the result.
CFI_ADJUST_CFA_OFFSET(-4)
POP ebx
POP ebp // Restore callee saves
diff --git a/runtime/arch/x86/thread_x86.cc b/runtime/arch/x86/thread_x86.cc
index 9f36927..b97c143 100644
--- a/runtime/arch/x86/thread_x86.cc
+++ b/runtime/arch/x86/thread_x86.cc
@@ -156,7 +156,11 @@
// Free LDT entry.
#if defined(__APPLE__)
- i386_set_ldt(selector >> 3, 0, 1);
+ // TODO: release selectors on OS/X this is a leak which will cause ldt entries to be exhausted
+ // after enough threads are created. However, the following code results in kernel panics in OS/X
+ // 10.9.
+ UNUSED(selector);
+ // i386_set_ldt(selector >> 3, 0, 1);
#else
user_desc ldt_entry;
memset(&ldt_entry, 0, sizeof(ldt_entry));
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
index d20eb17..1890181 100644
--- a/runtime/base/mutex-inl.h
+++ b/runtime/base/mutex-inl.h
@@ -45,54 +45,6 @@
}
#endif // ART_USE_FUTEXES
-#if defined(__APPLE__)
-
-// This works on Mac OS 10.6 but hasn't been tested on older releases.
-struct __attribute__((__may_alias__)) darwin_pthread_mutex_t {
- long padding0; // NOLINT(runtime/int) exact match to darwin type
- int padding1;
- uint32_t padding2;
- int16_t padding3;
- int16_t padding4;
- uint32_t padding5;
- pthread_t darwin_pthread_mutex_owner;
- // ...other stuff we don't care about.
-};
-
-struct __attribute__((__may_alias__)) darwin_pthread_rwlock_t {
- long padding0; // NOLINT(runtime/int) exact match to darwin type
- pthread_mutex_t padding1;
- int padding2;
- pthread_cond_t padding3;
- pthread_cond_t padding4;
- int padding5;
- int padding6;
- pthread_t darwin_pthread_rwlock_owner;
- // ...other stuff we don't care about.
-};
-
-#endif // __APPLE__
-
-#if defined(__GLIBC__)
-
-struct __attribute__((__may_alias__)) glibc_pthread_mutex_t {
- int32_t padding0[2];
- int owner;
- // ...other stuff we don't care about.
-};
-
-struct __attribute__((__may_alias__)) glibc_pthread_rwlock_t {
-#ifdef __LP64__
- int32_t padding0[6];
-#else
- int32_t padding0[7];
-#endif
- int writer;
- // ...other stuff we don't care about.
-};
-
-#endif // __GLIBC__
-
class ScopedContentionRecorder {
public:
ScopedContentionRecorder(BaseMutex* mutex, uint64_t blocked_tid, uint64_t owner_tid)
@@ -219,12 +171,14 @@
#else
CHECK_MUTEX_CALL(pthread_rwlock_rdlock, (&rwlock_));
#endif
+ DCHECK(exclusive_owner_ == 0U || exclusive_owner_ == -1U);
RegisterAsLocked(self);
AssertSharedHeld(self);
}
inline void ReaderWriterMutex::SharedUnlock(Thread* self) {
DCHECK(self == NULL || self == Thread::Current());
+ DCHECK(exclusive_owner_ == 0U || exclusive_owner_ == -1U);
AssertSharedHeld(self);
RegisterAsUnlocked(self);
#if ART_USE_FUTEXES
@@ -262,26 +216,7 @@
}
inline uint64_t Mutex::GetExclusiveOwnerTid() const {
-#if ART_USE_FUTEXES
return exclusive_owner_;
-#elif defined(__BIONIC__)
- return static_cast<uint64_t>((mutex_.value >> 16) & 0xffff);
-#elif defined(__GLIBC__)
- return reinterpret_cast<const glibc_pthread_mutex_t*>(&mutex_)->owner;
-#elif defined(__APPLE__)
- const darwin_pthread_mutex_t* dpmutex = reinterpret_cast<const darwin_pthread_mutex_t*>(&mutex_);
- pthread_t owner = dpmutex->darwin_pthread_mutex_owner;
- // 0 for unowned, -1 for PTHREAD_MTX_TID_SWITCHING
- // TODO: should we make darwin_pthread_mutex_owner volatile and recheck until not -1?
- if ((owner == (pthread_t)0) || (owner == (pthread_t)-1)) {
- return 0;
- }
- uint64_t tid;
- CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6
- return tid;
-#else
-#error unsupported C library
-#endif
}
inline bool ReaderWriterMutex::IsExclusiveHeld(const Thread* self) const {
@@ -307,23 +242,7 @@
return exclusive_owner_;
}
#else
-#if defined(__BIONIC__)
- return rwlock_.writerThreadId;
-#elif defined(__GLIBC__)
- return reinterpret_cast<const glibc_pthread_rwlock_t*>(&rwlock_)->writer;
-#elif defined(__APPLE__)
- const darwin_pthread_rwlock_t*
- dprwlock = reinterpret_cast<const darwin_pthread_rwlock_t*>(&rwlock_);
- pthread_t owner = dprwlock->darwin_pthread_rwlock_owner;
- if (owner == (pthread_t)0) {
- return 0;
- }
- uint64_t tid;
- CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6
- return tid;
-#else
-#error unsupported C library
-#endif
+ return exclusive_owner_;
#endif
}
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index aeece74..fd1eb12 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -263,19 +263,11 @@
: BaseMutex(name, level), recursive_(recursive), recursion_count_(0) {
#if ART_USE_FUTEXES
state_ = 0;
- exclusive_owner_ = 0;
DCHECK_EQ(0, num_contenders_.LoadRelaxed());
-#elif defined(__BIONIC__) || defined(__APPLE__)
- // Use recursive mutexes for bionic and Apple otherwise the
- // non-recursive mutexes don't have TIDs to check lock ownership of.
- pthread_mutexattr_t attributes;
- CHECK_MUTEX_CALL(pthread_mutexattr_init, (&attributes));
- CHECK_MUTEX_CALL(pthread_mutexattr_settype, (&attributes, PTHREAD_MUTEX_RECURSIVE));
- CHECK_MUTEX_CALL(pthread_mutex_init, (&mutex_, &attributes));
- CHECK_MUTEX_CALL(pthread_mutexattr_destroy, (&attributes));
#else
- CHECK_MUTEX_CALL(pthread_mutex_init, (&mutex_, NULL));
+ CHECK_MUTEX_CALL(pthread_mutex_init, (&mutex_, nullptr));
#endif
+ exclusive_owner_ = 0;
}
Mutex::~Mutex() {
@@ -336,10 +328,11 @@
// TODO: Change state_ to be a art::Atomic and use an intention revealing CAS operation
// that exposes the ordering semantics.
DCHECK_EQ(state_, 1);
- exclusive_owner_ = SafeGetTid(self);
#else
CHECK_MUTEX_CALL(pthread_mutex_lock, (&mutex_));
#endif
+ DCHECK_EQ(exclusive_owner_, 0U);
+ exclusive_owner_ = SafeGetTid(self);
RegisterAsLocked(self);
}
recursion_count_++;
@@ -369,7 +362,6 @@
} while (!done);
// We again assert no memory fence is needed.
DCHECK_EQ(state_, 1);
- exclusive_owner_ = SafeGetTid(self);
#else
int result = pthread_mutex_trylock(&mutex_);
if (result == EBUSY) {
@@ -380,6 +372,8 @@
PLOG(FATAL) << "pthread_mutex_trylock failed for " << name_;
}
#endif
+ DCHECK_EQ(exclusive_owner_, 0U);
+ exclusive_owner_ = SafeGetTid(self);
RegisterAsLocked(self);
}
recursion_count_++;
@@ -394,6 +388,7 @@
void Mutex::ExclusiveUnlock(Thread* self) {
DCHECK(self == NULL || self == Thread::Current());
AssertHeld(self);
+ DCHECK_NE(exclusive_owner_, 0U);
recursion_count_--;
if (!recursive_ || recursion_count_ == 0) {
if (kDebugLocking) {
@@ -402,34 +397,35 @@
}
RegisterAsUnlocked(self);
#if ART_USE_FUTEXES
- bool done = false;
- do {
- int32_t cur_state = state_;
- if (LIKELY(cur_state == 1)) {
- // The __sync_bool_compare_and_swap enforces the necessary memory ordering.
- // We're no longer the owner.
- exclusive_owner_ = 0;
- // Change state to 0.
- done = __sync_bool_compare_and_swap(&state_, cur_state, 0 /* new state */);
- if (LIKELY(done)) { // Spurious fail?
- // Wake a contender
- if (UNLIKELY(num_contenders_.LoadRelaxed() > 0)) {
- futex(&state_, FUTEX_WAKE, 1, NULL, NULL, 0);
+ bool done = false;
+ do {
+ int32_t cur_state = state_;
+ if (LIKELY(cur_state == 1)) {
+ // The __sync_bool_compare_and_swap enforces the necessary memory ordering.
+ // We're no longer the owner.
+ exclusive_owner_ = 0;
+ // Change state to 0.
+ done = __sync_bool_compare_and_swap(&state_, cur_state, 0 /* new state */);
+ if (LIKELY(done)) { // Spurious fail?
+ // Wake a contender
+ if (UNLIKELY(num_contenders_.LoadRelaxed() > 0)) {
+ futex(&state_, FUTEX_WAKE, 1, NULL, NULL, 0);
+ }
+ }
+ } else {
+ // Logging acquires the logging lock, avoid infinite recursion in that case.
+ if (this != Locks::logging_lock_) {
+ LOG(FATAL) << "Unexpected state_ in unlock " << cur_state << " for " << name_;
+ } else {
+ LogMessageData data(__FILE__, __LINE__, INTERNAL_FATAL, -1);
+ LogMessage::LogLine(data, StringPrintf("Unexpected state_ %d in unlock for %s",
+ cur_state, name_).c_str());
+ _exit(1);
}
}
- } else {
- // Logging acquires the logging lock, avoid infinite recursion in that case.
- if (this != Locks::logging_lock_) {
- LOG(FATAL) << "Unexpected state_ in unlock " << cur_state << " for " << name_;
- } else {
- LogMessageData data(__FILE__, __LINE__, INTERNAL_FATAL, -1);
- LogMessage::LogLine(data, StringPrintf("Unexpected state_ %d in unlock for %s",
- cur_state, name_).c_str());
- _exit(1);
- }
- }
- } while (!done);
+ } while (!done);
#else
+ exclusive_owner_ = 0;
CHECK_MUTEX_CALL(pthread_mutex_unlock, (&mutex_));
#endif
}
@@ -452,12 +448,13 @@
ReaderWriterMutex::ReaderWriterMutex(const char* name, LockLevel level)
: BaseMutex(name, level)
#if ART_USE_FUTEXES
- , state_(0), exclusive_owner_(0), num_pending_readers_(0), num_pending_writers_(0)
+ , state_(0), num_pending_readers_(0), num_pending_writers_(0)
#endif
{ // NOLINT(whitespace/braces)
#if !ART_USE_FUTEXES
- CHECK_MUTEX_CALL(pthread_rwlock_init, (&rwlock_, NULL));
+ CHECK_MUTEX_CALL(pthread_rwlock_init, (&rwlock_, nullptr));
#endif
+ exclusive_owner_ = 0;
}
ReaderWriterMutex::~ReaderWriterMutex() {
@@ -506,10 +503,11 @@
}
} while (!done);
DCHECK_EQ(state_, -1);
- exclusive_owner_ = SafeGetTid(self);
#else
CHECK_MUTEX_CALL(pthread_rwlock_wrlock, (&rwlock_));
#endif
+ DCHECK_EQ(exclusive_owner_, 0U);
+ exclusive_owner_ = SafeGetTid(self);
RegisterAsLocked(self);
AssertExclusiveHeld(self);
}
@@ -518,6 +516,7 @@
DCHECK(self == NULL || self == Thread::Current());
AssertExclusiveHeld(self);
RegisterAsUnlocked(self);
+ DCHECK_NE(exclusive_owner_, 0U);
#if ART_USE_FUTEXES
bool done = false;
do {
@@ -538,6 +537,7 @@
}
} while (!done);
#else
+ exclusive_owner_ = 0;
CHECK_MUTEX_CALL(pthread_rwlock_unlock, (&rwlock_));
#endif
}
@@ -578,7 +578,6 @@
num_pending_writers_--;
}
} while (!done);
- exclusive_owner_ = SafeGetTid(self);
#else
timespec ts;
InitTimeSpec(true, CLOCK_REALTIME, ms, ns, &ts);
@@ -591,6 +590,7 @@
PLOG(FATAL) << "pthread_rwlock_timedwrlock failed for " << name_;
}
#endif
+ exclusive_owner_ = SafeGetTid(self);
RegisterAsLocked(self);
AssertSharedHeld(self);
return true;
@@ -656,7 +656,7 @@
num_waiters_ = 0;
#else
pthread_condattr_t cond_attrs;
- CHECK_MUTEX_CALL(pthread_condattr_init(&cond_attrs));
+ CHECK_MUTEX_CALL(pthread_condattr_init, (&cond_attrs));
#if !defined(__APPLE__)
// Apple doesn't have CLOCK_MONOTONIC or pthread_condattr_setclock.
CHECK_MUTEX_CALL(pthread_condattr_setclock(&cond_attrs, CLOCK_MONOTONIC));
@@ -763,8 +763,11 @@
CHECK_GE(guard_.num_contenders_.LoadRelaxed(), 0);
guard_.num_contenders_--;
#else
+ uint64_t old_owner = guard_.exclusive_owner_;
+ guard_.exclusive_owner_ = 0;
guard_.recursion_count_ = 0;
CHECK_MUTEX_CALL(pthread_cond_wait, (&cond_, &guard_.mutex_));
+ guard_.exclusive_owner_ = old_owner;
#endif
guard_.recursion_count_ = old_recursion_count;
}
@@ -804,6 +807,8 @@
#else
int clock = CLOCK_REALTIME;
#endif
+ uint64_t old_owner = guard_.exclusive_owner_;
+ guard_.exclusive_owner_ = 0;
guard_.recursion_count_ = 0;
timespec ts;
InitTimeSpec(true, clock, ms, ns, &ts);
@@ -812,6 +817,7 @@
errno = rc;
PLOG(FATAL) << "TimedWait failed for " << name_;
}
+ guard_.exclusive_owner_ = old_owner;
#endif
guard_.recursion_count_ = old_recursion_count;
}
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 68b450a..1ba6180 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -245,6 +245,7 @@
AtomicInteger num_contenders_;
#else
pthread_mutex_t mutex_;
+ volatile uint64_t exclusive_owner_; // Guarded by mutex_.
#endif
const bool recursive_; // Can the lock be recursively held?
unsigned int recursion_count_;
@@ -358,6 +359,7 @@
AtomicInteger num_pending_writers_;
#else
pthread_rwlock_t rwlock_;
+ volatile uint64_t exclusive_owner_; // Guarded by rwlock_.
#endif
DISALLOW_COPY_AND_ASSIGN(ReaderWriterMutex);
};
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 87d1c06..6d5b59c 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -69,17 +69,29 @@
}
int FdFile::Flush() {
+#ifdef __linux__
int rc = TEMP_FAILURE_RETRY(fdatasync(fd_));
+#else
+ int rc = TEMP_FAILURE_RETRY(fsync(fd_));
+#endif
return (rc == -1) ? -errno : rc;
}
int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
+#ifdef __linux__
int rc = TEMP_FAILURE_RETRY(pread64(fd_, buf, byte_count, offset));
+#else
+ int rc = TEMP_FAILURE_RETRY(pread(fd_, buf, byte_count, offset));
+#endif
return (rc == -1) ? -errno : rc;
}
int FdFile::SetLength(int64_t new_length) {
+#ifdef __linux__
int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length));
+#else
+ int rc = TEMP_FAILURE_RETRY(ftruncate(fd_, new_length));
+#endif
return (rc == -1) ? -errno : rc;
}
@@ -90,7 +102,11 @@
}
int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
+#ifdef __linux__
int rc = TEMP_FAILURE_RETRY(pwrite64(fd_, buf, byte_count, offset));
+#else
+ int rc = TEMP_FAILURE_RETRY(pwrite(fd_, buf, byte_count, offset));
+#endif
return (rc == -1) ? -errno : rc;
}
diff --git a/runtime/base/unix_file/mapped_file.cc b/runtime/base/unix_file/mapped_file.cc
index bc23a74..63927b1 100644
--- a/runtime/base/unix_file/mapped_file.cc
+++ b/runtime/base/unix_file/mapped_file.cc
@@ -61,7 +61,11 @@
bool MappedFile::MapReadWrite(int64_t file_size) {
CHECK(IsOpened());
CHECK(!IsMapped());
+#ifdef __linux__
int result = TEMP_FAILURE_RETRY(ftruncate64(Fd(), file_size));
+#else
+ int result = TEMP_FAILURE_RETRY(ftruncate(Fd(), file_size));
+#endif
if (result == -1) {
PLOG(ERROR) << "Failed to truncate file '" << GetPath()
<< "' to size " << file_size;
diff --git a/runtime/base/unix_file/mapped_file.h b/runtime/base/unix_file/mapped_file.h
index 28cc551..73056e9 100644
--- a/runtime/base/unix_file/mapped_file.h
+++ b/runtime/base/unix_file/mapped_file.h
@@ -32,8 +32,13 @@
public:
// File modes used in Open().
enum FileMode {
+#ifdef __linux__
kReadOnlyMode = O_RDONLY | O_LARGEFILE,
kReadWriteMode = O_CREAT | O_RDWR | O_LARGEFILE,
+#else
+ kReadOnlyMode = O_RDONLY,
+ kReadWriteMode = O_CREAT | O_RDWR,
+#endif
};
MappedFile() : FdFile(), file_size_(-1), mapped_file_(NULL) {
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index a0cecb0..349700a 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -62,45 +62,98 @@
static const size_t kMaxAllocRecordStackDepth = 16; // Max 255.
static const size_t kDefaultNumAllocRecords = 64*1024; // Must be a power of 2.
-struct AllocRecordStackTraceElement {
- mirror::ArtMethod* method;
- uint32_t dex_pc;
-
- AllocRecordStackTraceElement() : method(nullptr), dex_pc(0) {
+class AllocRecordStackTraceElement {
+ public:
+ AllocRecordStackTraceElement() : method_(nullptr), dex_pc_(0) {
}
- int32_t LineNumber() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return method->GetLineNumFromDexPC(dex_pc);
+ int32_t LineNumber() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* method = Method();
+ DCHECK(method != nullptr);
+ return method->GetLineNumFromDexPC(DexPc());
}
+
+ mirror::ArtMethod* Method() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* method = reinterpret_cast<mirror::ArtMethod*>(
+ Thread::Current()->DecodeJObject(method_));
+ return method;
+ }
+
+ void SetMethod(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ JNIEnv* env = soa.Env();
+ if (method_ != nullptr) {
+ env->DeleteWeakGlobalRef(method_);
+ }
+ method_ = env->NewWeakGlobalRef(soa.AddLocalReference<jobject>(m));
+ }
+
+ uint32_t DexPc() const {
+ return dex_pc_;
+ }
+
+ void SetDexPc(uint32_t pc) {
+ dex_pc_ = pc;
+ }
+
+ private:
+ jobject method_; // This is a weak global.
+ uint32_t dex_pc_;
};
-struct AllocRecord {
- mirror::Class* type;
- size_t byte_count;
- uint16_t thin_lock_id;
- AllocRecordStackTraceElement stack[kMaxAllocRecordStackDepth]; // Unused entries have NULL method.
+class AllocRecord {
+ public:
+ AllocRecord() : type_(nullptr), byte_count_(0), thin_lock_id_(0) {}
- size_t GetDepth() {
+ mirror::Class* Type() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Class* type = reinterpret_cast<mirror::Class*>(
+ Thread::Current()->DecodeJObject(type_));
+ return type;
+ }
+
+ void SetType(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ JNIEnv* env = soa.Env();
+ if (type_ != nullptr) {
+ env->DeleteWeakGlobalRef(type_);
+ }
+ type_ = env->NewWeakGlobalRef(soa.AddLocalReference<jobject>(t));
+ }
+
+ size_t GetDepth() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
size_t depth = 0;
- while (depth < kMaxAllocRecordStackDepth && stack[depth].method != NULL) {
+ while (depth < kMaxAllocRecordStackDepth && stack_[depth].Method() != NULL) {
++depth;
}
return depth;
}
- void UpdateObjectPointers(IsMarkedCallback* callback, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (type != nullptr) {
- type = down_cast<mirror::Class*>(callback(type, arg));
- }
- for (size_t stack_frame = 0; stack_frame < kMaxAllocRecordStackDepth; ++stack_frame) {
- mirror::ArtMethod*& m = stack[stack_frame].method;
- if (m == nullptr) {
- break;
- }
- m = down_cast<mirror::ArtMethod*>(callback(m, arg));
- }
+ size_t ByteCount() const {
+ return byte_count_;
}
+
+ void SetByteCount(size_t count) {
+ byte_count_ = count;
+ }
+
+ uint16_t ThinLockId() const {
+ return thin_lock_id_;
+ }
+
+ void SetThinLockId(uint16_t id) {
+ thin_lock_id_ = id;
+ }
+
+ AllocRecordStackTraceElement* StackElement(size_t index) {
+ DCHECK_LT(index, kMaxAllocRecordStackDepth);
+ return &stack_[index];
+ }
+
+ private:
+ jobject type_; // This is a weak global.
+ size_t byte_count_;
+ uint16_t thin_lock_id_;
+ AllocRecordStackTraceElement stack_[kMaxAllocRecordStackDepth]; // Unused entries have NULL method.
};
struct Breakpoint {
@@ -848,21 +901,13 @@
JDWP::JdwpError Dbg::GetOwnedMonitors(JDWP::ObjectId thread_id,
std::vector<JDWP::ObjectId>& monitors,
std::vector<uint32_t>& stack_depths) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
- MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- Thread* thread;
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
- if (error != JDWP::ERR_NONE) {
- return error;
- }
- if (!IsSuspendedForDebugger(soa, thread)) {
- return JDWP::ERR_THREAD_NOT_SUSPENDED;
- }
-
struct OwnedMonitorVisitor : public StackVisitor {
- OwnedMonitorVisitor(Thread* thread, Context* context)
+ OwnedMonitorVisitor(Thread* thread, Context* context,
+ std::vector<mirror::Object*>* monitor_vector,
+ std::vector<uint32_t>* stack_depth_vector)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, context), current_stack_depth(0) {}
+ : StackVisitor(thread, context), current_stack_depth(0),
+ monitors(monitor_vector), stack_depths(stack_depth_vector) {}
// TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
// annotalysis.
@@ -876,21 +921,38 @@
static void AppendOwnedMonitors(mirror::Object* owned_monitor, void* arg) {
OwnedMonitorVisitor* visitor = reinterpret_cast<OwnedMonitorVisitor*>(arg);
- visitor->monitors.push_back(owned_monitor);
- visitor->stack_depths.push_back(visitor->current_stack_depth);
+ visitor->monitors->push_back(owned_monitor);
+ visitor->stack_depths->push_back(visitor->current_stack_depth);
}
size_t current_stack_depth;
- std::vector<mirror::Object*> monitors;
- std::vector<uint32_t> stack_depths;
+ std::vector<mirror::Object*>* monitors;
+ std::vector<uint32_t>* stack_depths;
};
- std::unique_ptr<Context> context(Context::Create());
- OwnedMonitorVisitor visitor(thread, context.get());
- visitor.WalkStack();
- for (size_t i = 0; i < visitor.monitors.size(); ++i) {
- monitors.push_back(gRegistry->Add(visitor.monitors[i]));
- stack_depths.push_back(visitor.stack_depths[i]);
+ std::vector<mirror::Object*> monitor_vector;
+ std::vector<uint32_t> stack_depth_vector;
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ {
+ MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
+ Thread* thread;
+ JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+ if (error != JDWP::ERR_NONE) {
+ return error;
+ }
+ if (!IsSuspendedForDebugger(soa, thread)) {
+ return JDWP::ERR_THREAD_NOT_SUSPENDED;
+ }
+ std::unique_ptr<Context> context(Context::Create());
+ OwnedMonitorVisitor visitor(thread, context.get(), &monitor_vector, &stack_depth_vector);
+ visitor.WalkStack();
+ }
+
+ // Add() requires the thread_list_lock_ not held to avoid the lock
+ // level violation.
+ for (size_t i = 0; i < monitor_vector.size(); ++i) {
+ monitors.push_back(gRegistry->Add(monitor_vector[i]));
+ stack_depths.push_back(stack_depth_vector[i]);
}
return JDWP::ERR_NONE;
@@ -898,19 +960,23 @@
JDWP::JdwpError Dbg::GetContendedMonitor(JDWP::ObjectId thread_id,
JDWP::ObjectId& contended_monitor) {
+ mirror::Object* contended_monitor_obj;
ScopedObjectAccessUnchecked soa(Thread::Current());
- MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- Thread* thread;
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
- if (error != JDWP::ERR_NONE) {
- return error;
+ {
+ MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
+ Thread* thread;
+ JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+ if (error != JDWP::ERR_NONE) {
+ return error;
+ }
+ if (!IsSuspendedForDebugger(soa, thread)) {
+ return JDWP::ERR_THREAD_NOT_SUSPENDED;
+ }
+ contended_monitor_obj = Monitor::GetContendedMonitor(thread);
}
- if (!IsSuspendedForDebugger(soa, thread)) {
- return JDWP::ERR_THREAD_NOT_SUSPENDED;
- }
-
- contended_monitor = gRegistry->Add(Monitor::GetContendedMonitor(thread));
-
+ // Add() requires the thread_list_lock_ not held to avoid the lock
+ // level violation.
+ contended_monitor = gRegistry->Add(contended_monitor_obj);
return JDWP::ERR_NONE;
}
@@ -1845,9 +1911,12 @@
}
const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroup");
// Okay, so it's an object, but is it actually a thread?
- MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- Thread* thread;
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+ JDWP::JdwpError error;
+ {
+ MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
+ Thread* thread;
+ error = DecodeThread(soa, thread_id, thread);
+ }
if (error == JDWP::ERR_THREAD_NOT_ALIVE) {
// Zombie threads are in the null group.
expandBufAddObjectId(pReply, JDWP::ObjectId(0));
@@ -1856,9 +1925,9 @@
mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread);
CHECK(c != nullptr);
mirror::ArtField* f = c->FindInstanceField("group", "Ljava/lang/ThreadGroup;");
- CHECK(f != NULL);
+ CHECK(f != nullptr);
mirror::Object* group = f->GetObject(thread_object);
- CHECK(group != NULL);
+ CHECK(group != nullptr);
JDWP::ObjectId thread_group_id = gRegistry->Add(group);
expandBufAddObjectId(pReply, thread_group_id);
}
@@ -4146,8 +4215,8 @@
}
mirror::ArtMethod* m = GetMethod();
if (!m->IsRuntimeMethod()) {
- record->stack[depth].method = m;
- record->stack[depth].dex_pc = GetDexPc();
+ record->StackElement(depth)->SetMethod(m);
+ record->StackElement(depth)->SetDexPc(GetDexPc());
++depth;
}
return true;
@@ -4156,8 +4225,8 @@
~AllocRecordStackVisitor() {
// Clear out any unused stack trace elements.
for (; depth < kMaxAllocRecordStackDepth; ++depth) {
- record->stack[depth].method = NULL;
- record->stack[depth].dex_pc = 0;
+ record->StackElement(depth)->SetMethod(nullptr);
+ record->StackElement(depth)->SetDexPc(0);
}
}
@@ -4181,9 +4250,9 @@
// Fill in the basics.
AllocRecord* record = &recent_allocation_records_[alloc_record_head_];
- record->type = type;
- record->byte_count = byte_count;
- record->thin_lock_id = self->GetThreadId();
+ record->SetType(type);
+ record->SetByteCount(byte_count);
+ record->SetThinLockId(self->GetThreadId());
// Fill in the stack trace.
AllocRecordStackVisitor visitor(self, record);
@@ -4224,15 +4293,16 @@
while (count--) {
AllocRecord* record = &recent_allocation_records_[i];
- LOG(INFO) << StringPrintf(" Thread %-2d %6zd bytes ", record->thin_lock_id, record->byte_count)
- << PrettyClass(record->type);
+ LOG(INFO) << StringPrintf(" Thread %-2d %6zd bytes ", record->ThinLockId(), record->ByteCount())
+ << PrettyClass(record->Type());
for (size_t stack_frame = 0; stack_frame < kMaxAllocRecordStackDepth; ++stack_frame) {
- mirror::ArtMethod* m = record->stack[stack_frame].method;
+ AllocRecordStackTraceElement* stack_element = record->StackElement(stack_frame);
+ mirror::ArtMethod* m = stack_element->Method();
if (m == NULL) {
break;
}
- LOG(INFO) << " " << PrettyMethod(m) << " line " << record->stack[stack_frame].LineNumber();
+ LOG(INFO) << " " << PrettyMethod(m) << " line " << stack_element->LineNumber();
}
// pause periodically to help logcat catch up
@@ -4244,35 +4314,6 @@
}
}
-void Dbg::UpdateObjectPointers(IsMarkedCallback* callback, void* arg) {
- if (recent_allocation_records_ != nullptr) {
- MutexLock mu(Thread::Current(), *alloc_tracker_lock_);
- size_t i = HeadIndex();
- size_t count = alloc_record_count_;
- while (count--) {
- AllocRecord* record = &recent_allocation_records_[i];
- DCHECK(record != nullptr);
- record->UpdateObjectPointers(callback, arg);
- i = (i + 1) & (alloc_record_max_ - 1);
- }
- }
- if (gRegistry != nullptr) {
- gRegistry->UpdateObjectPointers(callback, arg);
- }
-}
-
-void Dbg::AllowNewObjectRegistryObjects() {
- if (gRegistry != nullptr) {
- gRegistry->AllowNewObjects();
- }
-}
-
-void Dbg::DisallowNewObjectRegistryObjects() {
- if (gRegistry != nullptr) {
- gRegistry->DisallowNewObjects();
- }
-}
-
class StringTable {
public:
StringTable() {
@@ -4379,10 +4420,10 @@
while (count--) {
AllocRecord* record = &recent_allocation_records_[idx];
- class_names.Add(record->type->GetDescriptor().c_str());
+ class_names.Add(record->Type()->GetDescriptor().c_str());
for (size_t i = 0; i < kMaxAllocRecordStackDepth; i++) {
- mirror::ArtMethod* m = record->stack[i].method;
+ mirror::ArtMethod* m = record->StackElement(i)->Method();
if (m != NULL) {
class_names.Add(m->GetDeclaringClassDescriptor());
method_names.Add(m->GetName());
@@ -4432,9 +4473,9 @@
AllocRecord* record = &recent_allocation_records_[idx];
size_t stack_depth = record->GetDepth();
size_t allocated_object_class_name_index =
- class_names.IndexOf(record->type->GetDescriptor().c_str());
- JDWP::Append4BE(bytes, record->byte_count);
- JDWP::Append2BE(bytes, record->thin_lock_id);
+ class_names.IndexOf(record->Type()->GetDescriptor().c_str());
+ JDWP::Append4BE(bytes, record->ByteCount());
+ JDWP::Append2BE(bytes, record->ThinLockId());
JDWP::Append2BE(bytes, allocated_object_class_name_index);
JDWP::Append1BE(bytes, stack_depth);
@@ -4444,14 +4485,14 @@
// (2b) method name
// (2b) method source file
// (2b) line number, clipped to 32767; -2 if native; -1 if no source
- mirror::ArtMethod* m = record->stack[stack_frame].method;
+ mirror::ArtMethod* m = record->StackElement(stack_frame)->Method();
size_t class_name_index = class_names.IndexOf(m->GetDeclaringClassDescriptor());
size_t method_name_index = method_names.IndexOf(m->GetName());
size_t file_name_index = filenames.IndexOf(GetMethodSourceFile(m));
JDWP::Append2BE(bytes, class_name_index);
JDWP::Append2BE(bytes, method_name_index);
JDWP::Append2BE(bytes, file_name_index);
- JDWP::Append2BE(bytes, record->stack[stack_frame].LineNumber());
+ JDWP::Append2BE(bytes, record->StackElement(stack_frame)->LineNumber());
}
idx = (idx + 1) & (alloc_record_max_ - 1);
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 31ffd6e..1cf0b0c 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -41,7 +41,7 @@
class Object;
class Throwable;
} // namespace mirror
-struct AllocRecord;
+class AllocRecord;
class Thread;
class ThrowLocation;
@@ -531,11 +531,6 @@
static size_t HeadIndex() EXCLUSIVE_LOCKS_REQUIRED(alloc_tracker_lock_);
static void DumpRecentAllocations() LOCKS_EXCLUDED(alloc_tracker_lock_);
- // Updates the stored direct object pointers (called from SweepSystemWeaks).
- static void UpdateObjectPointers(IsMarkedCallback* callback, void* arg)
- LOCKS_EXCLUDED(alloc_tracker_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
enum HpifWhen {
HPIF_WHEN_NEVER = 0,
HPIF_WHEN_NOW = 1,
@@ -560,9 +555,6 @@
static void DdmSendHeapSegments(bool native)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void AllowNewObjectRegistryObjects() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void DisallowNewObjectRegistryObjects() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
private:
static void DdmBroadcast(bool connect) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void PostThreadStartOrStop(Thread*, uint32_t)
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 17d1ffc..a27dfad 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -66,6 +66,64 @@
return true;
}
+const char* DexFileVerifier::CheckLoadStringByIdx(uint32_t idx, const char* error_string) {
+ if (!CheckIndex(idx, dex_file_->NumStringIds(), error_string)) {
+ return nullptr;
+ }
+ return dex_file_->StringDataByIdx(idx);
+}
+
+const char* DexFileVerifier::CheckLoadStringByTypeIdx(uint32_t type_idx, const char* error_string) {
+ if (!CheckIndex(type_idx, dex_file_->NumTypeIds(), error_string)) {
+ return nullptr;
+ }
+ const DexFile::TypeId& type_id = dex_file_->GetTypeId(type_idx);
+ uint32_t idx = type_id.descriptor_idx_;
+ return CheckLoadStringByIdx(idx, error_string);
+}
+
+const DexFile::FieldId* DexFileVerifier::CheckLoadFieldId(uint32_t idx, const char* error_string) {
+ if (!CheckIndex(idx, dex_file_->NumFieldIds(), error_string)) {
+ return nullptr;
+ }
+ return &dex_file_->GetFieldId(idx);
+}
+
+const DexFile::MethodId* DexFileVerifier::CheckLoadMethodId(uint32_t idx, const char* err_string) {
+ if (!CheckIndex(idx, dex_file_->NumMethodIds(), err_string)) {
+ return nullptr;
+ }
+ return &dex_file_->GetMethodId(idx);
+}
+
+// Helper macro to load string and return false on error.
+#define LOAD_STRING(var, idx, error) \
+ const char* var = CheckLoadStringByIdx(idx, error); \
+ if (var == nullptr) { \
+ return false; \
+ }
+
+// Helper macro to load string by type idx and return false on error.
+#define LOAD_STRING_BY_TYPE(var, type_idx, error) \
+ const char* var = CheckLoadStringByTypeIdx(type_idx, error); \
+ if (var == nullptr) { \
+ return false; \
+ }
+
+// Helper macro to load method id. Return last parameter on error.
+#define LOAD_METHOD(var, idx, error_string, error_val) \
+ const DexFile::MethodId* var = CheckLoadMethodId(idx, error_string); \
+ if (var == nullptr) { \
+ return error_val; \
+ }
+
+// Helper macro to load method id. Return last parameter on error.
+#define LOAD_FIELD(var, idx, fmt, error_val) \
+ const DexFile::FieldId* var = CheckLoadFieldId(idx, fmt); \
+ if (var == nullptr) { \
+ return error_val; \
+ }
+
bool DexFileVerifier::Verify(const DexFile* dex_file, const byte* begin, size_t size,
const char* location, std::string* error_msg) {
std::unique_ptr<DexFileVerifier> verifier(new DexFileVerifier(dex_file, begin, size, location));
@@ -120,7 +178,8 @@
if (UNLIKELY((range_start < file_start) || (range_start > file_end) ||
(range_end < file_start) || (range_end > file_end))) {
ErrorStringPrintf("Bad range for %s: %zx to %zx", label,
- range_start - file_start, range_end - file_start);
+ static_cast<size_t>(range_start - file_start),
+ static_cast<size_t>(range_end - file_start));
return false;
}
return true;
@@ -1319,41 +1378,43 @@
return true;
}
-uint16_t DexFileVerifier::FindFirstClassDataDefiner(const byte* ptr) const {
+uint32_t DexFileVerifier::FindFirstClassDataDefiner(const byte* ptr) {
ClassDataItemIterator it(*dex_file_, ptr);
if (it.HasNextStaticField() || it.HasNextInstanceField()) {
- const DexFile::FieldId& field = dex_file_->GetFieldId(it.GetMemberIndex());
- return field.class_idx_;
+ LOAD_FIELD(field, it.GetMemberIndex(), "first_class_data_definer field_id", 0x10000U)
+ return field->class_idx_;
}
if (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) {
- const DexFile::MethodId& method = dex_file_->GetMethodId(it.GetMemberIndex());
- return method.class_idx_;
+ LOAD_METHOD(method, it.GetMemberIndex(), "first_class_data_definer method_id", 0x10000U)
+ return method->class_idx_;
}
return DexFile::kDexNoIndex16;
}
-uint16_t DexFileVerifier::FindFirstAnnotationsDirectoryDefiner(const byte* ptr) const {
+uint32_t DexFileVerifier::FindFirstAnnotationsDirectoryDefiner(const byte* ptr) {
const DexFile::AnnotationsDirectoryItem* item =
reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr);
if (item->fields_size_ != 0) {
DexFile::FieldAnnotationsItem* field_items = (DexFile::FieldAnnotationsItem*) (item + 1);
- const DexFile::FieldId& field = dex_file_->GetFieldId(field_items[0].field_idx_);
- return field.class_idx_;
+ LOAD_FIELD(field, field_items[0].field_idx_, "first_annotations_dir_definer field_id", 0x10000U)
+ return field->class_idx_;
}
if (item->methods_size_ != 0) {
DexFile::MethodAnnotationsItem* method_items = (DexFile::MethodAnnotationsItem*) (item + 1);
- const DexFile::MethodId& method = dex_file_->GetMethodId(method_items[0].method_idx_);
- return method.class_idx_;
+ LOAD_METHOD(method, method_items[0].method_idx_, "first_annotations_dir_definer method id",
+ 0x10000U)
+ return method->class_idx_;
}
if (item->parameters_size_ != 0) {
DexFile::ParameterAnnotationsItem* parameter_items = (DexFile::ParameterAnnotationsItem*) (item + 1);
- const DexFile::MethodId& method = dex_file_->GetMethodId(parameter_items[0].method_idx_);
- return method.class_idx_;
+ LOAD_METHOD(method, parameter_items[0].method_idx_, "first_annotations_dir_definer method id",
+ 0x10000U)
+ return method->class_idx_;
}
return DexFile::kDexNoIndex16;
@@ -1384,7 +1445,8 @@
bool DexFileVerifier::CheckInterTypeIdItem() {
const DexFile::TypeId* item = reinterpret_cast<const DexFile::TypeId*>(ptr_);
- const char* descriptor = dex_file_->StringDataByIdx(item->descriptor_idx_);
+
+ LOAD_STRING(descriptor, item->descriptor_idx_, "inter_type_id_item descriptor_idx")
// Check that the descriptor is a valid type.
if (UNLIKELY(!IsValidDescriptor(descriptor))) {
@@ -1408,14 +1470,17 @@
bool DexFileVerifier::CheckInterProtoIdItem() {
const DexFile::ProtoId* item = reinterpret_cast<const DexFile::ProtoId*>(ptr_);
- const char* shorty = dex_file_->StringDataByIdx(item->shorty_idx_);
+
+ LOAD_STRING(shorty, item->shorty_idx_, "inter_proto_id_item shorty_idx")
+
if (item->parameters_off_ != 0 &&
!CheckOffsetToTypeMap(item->parameters_off_, DexFile::kDexTypeTypeList)) {
return false;
}
// Check the return type and advance the shorty.
- if (!CheckShortyDescriptorMatch(*shorty, dex_file_->StringByTypeIdx(item->return_type_idx_), true)) {
+ LOAD_STRING_BY_TYPE(return_type, item->return_type_idx_, "inter_proto_id_item return_type_idx")
+ if (!CheckShortyDescriptorMatch(*shorty, return_type, true)) {
return false;
}
shorty++;
@@ -1476,21 +1541,21 @@
const DexFile::FieldId* item = reinterpret_cast<const DexFile::FieldId*>(ptr_);
// Check that the class descriptor is valid.
- const char* descriptor = dex_file_->StringByTypeIdx(item->class_idx_);
- if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) {
- ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", descriptor);
+ LOAD_STRING_BY_TYPE(class_descriptor, item->class_idx_, "inter_field_id_item class_idx")
+ if (UNLIKELY(!IsValidDescriptor(class_descriptor) || class_descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", class_descriptor);
return false;
}
// Check that the type descriptor is a valid field name.
- descriptor = dex_file_->StringByTypeIdx(item->type_idx_);
- if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] == 'V')) {
- ErrorStringPrintf("Invalid descriptor for type_idx: '%s'", descriptor);
+ LOAD_STRING_BY_TYPE(type_descriptor, item->type_idx_, "inter_field_id_item type_idx")
+ if (UNLIKELY(!IsValidDescriptor(type_descriptor) || type_descriptor[0] == 'V')) {
+ ErrorStringPrintf("Invalid descriptor for type_idx: '%s'", type_descriptor);
return false;
}
// Check that the name is valid.
- descriptor = dex_file_->StringDataByIdx(item->name_idx_);
+ LOAD_STRING(descriptor, item->name_idx_, "inter_field_id_item name_idx")
if (UNLIKELY(!IsValidMemberName(descriptor))) {
ErrorStringPrintf("Invalid field name: '%s'", descriptor);
return false;
@@ -1523,14 +1588,15 @@
const DexFile::MethodId* item = reinterpret_cast<const DexFile::MethodId*>(ptr_);
// Check that the class descriptor is a valid reference name.
- const char* descriptor = dex_file_->StringByTypeIdx(item->class_idx_);
- if (UNLIKELY(!IsValidDescriptor(descriptor) || (descriptor[0] != 'L' && descriptor[0] != '['))) {
- ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", descriptor);
+ LOAD_STRING_BY_TYPE(class_descriptor, item->class_idx_, "inter_method_id_item class_idx")
+ if (UNLIKELY(!IsValidDescriptor(class_descriptor) || (class_descriptor[0] != 'L' &&
+ class_descriptor[0] != '['))) {
+ ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", class_descriptor);
return false;
}
// Check that the name is valid.
- descriptor = dex_file_->StringDataByIdx(item->name_idx_);
+ LOAD_STRING(descriptor, item->name_idx_, "inter_method_id_item class_idx")
if (UNLIKELY(!IsValidMemberName(descriptor))) {
ErrorStringPrintf("Invalid method name: '%s'", descriptor);
return false;
@@ -1561,11 +1627,10 @@
bool DexFileVerifier::CheckInterClassDefItem() {
const DexFile::ClassDef* item = reinterpret_cast<const DexFile::ClassDef*>(ptr_);
- uint32_t class_idx = item->class_idx_;
- const char* descriptor = dex_file_->StringByTypeIdx(class_idx);
- if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) {
- ErrorStringPrintf("Invalid class descriptor: '%s'", descriptor);
+ LOAD_STRING_BY_TYPE(class_descriptor, item->class_idx_, "inter_class_def_item class_idx")
+ if (UNLIKELY(!IsValidDescriptor(class_descriptor) || class_descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid class descriptor: '%s'", class_descriptor);
return false;
}
@@ -1587,9 +1652,10 @@
}
if (item->superclass_idx_ != DexFile::kDexNoIndex16) {
- descriptor = dex_file_->StringByTypeIdx(item->superclass_idx_);
- if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) {
- ErrorStringPrintf("Invalid superclass: '%s'", descriptor);
+ LOAD_STRING_BY_TYPE(superclass_descriptor, item->superclass_idx_,
+ "inter_class_def_item superclass_idx")
+ if (UNLIKELY(!IsValidDescriptor(superclass_descriptor) || superclass_descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid superclass: '%s'", superclass_descriptor);
return false;
}
}
@@ -1600,9 +1666,10 @@
// Ensure that all interfaces refer to classes (not arrays or primitives).
for (uint32_t i = 0; i < size; i++) {
- descriptor = dex_file_->StringByTypeIdx(interfaces->GetTypeItem(i).type_idx_);
- if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) {
- ErrorStringPrintf("Invalid interface: '%s'", descriptor);
+ LOAD_STRING_BY_TYPE(inf_descriptor, interfaces->GetTypeItem(i).type_idx_,
+ "inter_class_def_item interface type_idx")
+ if (UNLIKELY(!IsValidDescriptor(inf_descriptor) || inf_descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid interface: '%s'", inf_descriptor);
return false;
}
}
@@ -1626,7 +1693,10 @@
// Check that references in class_data_item are to the right class.
if (item->class_data_off_ != 0) {
const byte* data = begin_ + item->class_data_off_;
- uint16_t data_definer = FindFirstClassDataDefiner(data);
+ uint32_t data_definer = FindFirstClassDataDefiner(data);
+ if (data_definer >= 0x10000U) {
+ return false;
+ }
if (UNLIKELY((data_definer != item->class_idx_) && (data_definer != DexFile::kDexNoIndex16))) {
ErrorStringPrintf("Invalid class_data_item");
return false;
@@ -1636,7 +1706,10 @@
// Check that references in annotations_directory_item are to right class.
if (item->annotations_off_ != 0) {
const byte* data = begin_ + item->annotations_off_;
- uint16_t annotations_definer = FindFirstAnnotationsDirectoryDefiner(data);
+ uint32_t annotations_definer = FindFirstAnnotationsDirectoryDefiner(data);
+ if (annotations_definer >= 0x10000U) {
+ return false;
+ }
if (UNLIKELY((annotations_definer != item->class_idx_) &&
(annotations_definer != DexFile::kDexNoIndex16))) {
ErrorStringPrintf("Invalid annotations_directory_item");
@@ -1698,11 +1771,14 @@
bool DexFileVerifier::CheckInterClassDataItem() {
ClassDataItemIterator it(*dex_file_, ptr_);
- uint16_t defining_class = FindFirstClassDataDefiner(ptr_);
+ uint32_t defining_class = FindFirstClassDataDefiner(ptr_);
+ if (defining_class >= 0x10000U) {
+ return false;
+ }
for (; it.HasNextStaticField() || it.HasNextInstanceField(); it.Next()) {
- const DexFile::FieldId& field = dex_file_->GetFieldId(it.GetMemberIndex());
- if (UNLIKELY(field.class_idx_ != defining_class)) {
+ LOAD_FIELD(field, it.GetMemberIndex(), "inter_class_data_item field_id", false)
+ if (UNLIKELY(field->class_idx_ != defining_class)) {
ErrorStringPrintf("Mismatched defining class for class_data_item field");
return false;
}
@@ -1712,8 +1788,8 @@
if (code_off != 0 && !CheckOffsetToTypeMap(code_off, DexFile::kDexTypeCodeItem)) {
return false;
}
- const DexFile::MethodId& method = dex_file_->GetMethodId(it.GetMemberIndex());
- if (UNLIKELY(method.class_idx_ != defining_class)) {
+ LOAD_METHOD(method, it.GetMemberIndex(), "inter_class_data_item method_id", false)
+ if (UNLIKELY(method->class_idx_ != defining_class)) {
ErrorStringPrintf("Mismatched defining class for class_data_item method");
return false;
}
@@ -1726,7 +1802,10 @@
bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() {
const DexFile::AnnotationsDirectoryItem* item =
reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr_);
- uint16_t defining_class = FindFirstAnnotationsDirectoryDefiner(ptr_);
+ uint32_t defining_class = FindFirstAnnotationsDirectoryDefiner(ptr_);
+ if (defining_class >= 0x10000U) {
+ return false;
+ }
if (item->class_annotations_off_ != 0 &&
!CheckOffsetToTypeMap(item->class_annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
@@ -1738,8 +1817,8 @@
reinterpret_cast<const DexFile::FieldAnnotationsItem*>(item + 1);
uint32_t field_count = item->fields_size_;
for (uint32_t i = 0; i < field_count; i++) {
- const DexFile::FieldId& field = dex_file_->GetFieldId(field_item->field_idx_);
- if (UNLIKELY(field.class_idx_ != defining_class)) {
+ LOAD_FIELD(field, field_item->field_idx_, "inter_annotations_directory_item field_id", false)
+ if (UNLIKELY(field->class_idx_ != defining_class)) {
ErrorStringPrintf("Mismatched defining class for field_annotation");
return false;
}
@@ -1754,8 +1833,9 @@
reinterpret_cast<const DexFile::MethodAnnotationsItem*>(field_item);
uint32_t method_count = item->methods_size_;
for (uint32_t i = 0; i < method_count; i++) {
- const DexFile::MethodId& method = dex_file_->GetMethodId(method_item->method_idx_);
- if (UNLIKELY(method.class_idx_ != defining_class)) {
+ LOAD_METHOD(method, method_item->method_idx_, "inter_annotations_directory_item method_id",
+ false)
+ if (UNLIKELY(method->class_idx_ != defining_class)) {
ErrorStringPrintf("Mismatched defining class for method_annotation");
return false;
}
@@ -1770,8 +1850,9 @@
reinterpret_cast<const DexFile::ParameterAnnotationsItem*>(method_item);
uint32_t parameter_count = item->parameters_size_;
for (uint32_t i = 0; i < parameter_count; i++) {
- const DexFile::MethodId& parameter_method = dex_file_->GetMethodId(parameter_item->method_idx_);
- if (UNLIKELY(parameter_method.class_idx_ != defining_class)) {
+ LOAD_METHOD(parameter_method, parameter_item->method_idx_,
+ "inter_annotations_directory_item parameter method_id", false)
+ if (UNLIKELY(parameter_method->class_idx_ != defining_class)) {
ErrorStringPrintf("Mismatched defining class for parameter_annotation");
return false;
}
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index 3337785..7489dcd 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -71,8 +71,11 @@
bool CheckIntraSection();
bool CheckOffsetToTypeMap(size_t offset, uint16_t type);
- uint16_t FindFirstClassDataDefiner(const byte* ptr) const;
- uint16_t FindFirstAnnotationsDirectoryDefiner(const byte* ptr) const;
+
+ // Note: the result type of the following methods is wider than that of the underlying index
+ // (16b vs 32b). This is so that we can define an error value (anything >= 2^16).
+ uint32_t FindFirstClassDataDefiner(const byte* ptr);
+ uint32_t FindFirstAnnotationsDirectoryDefiner(const byte* ptr);
bool CheckInterStringIdItem();
bool CheckInterTypeIdItem();
@@ -88,6 +91,16 @@
bool CheckInterSectionIterate(size_t offset, uint32_t count, uint16_t type);
bool CheckInterSection();
+ // Load a string by (type) index. Checks whether the index is in bounds, printing the error if
+ // not. If there is an error, nullptr is returned.
+ const char* CheckLoadStringByIdx(uint32_t idx, const char* error_fmt);
+ const char* CheckLoadStringByTypeIdx(uint32_t type_idx, const char* error_fmt);
+
+ // Load a field/method Id by index. Checks whether the index is in bounds, printing the error if
+ // not. If there is an error, nullptr is returned.
+ const DexFile::FieldId* CheckLoadFieldId(uint32_t idx, const char* error_fmt);
+ const DexFile::MethodId* CheckLoadMethodId(uint32_t idx, const char* error_fmt);
+
void ErrorStringPrintf(const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
diff --git a/runtime/elf_utils.h b/runtime/elf_utils.h
index f3ec713..ce8587b 100644
--- a/runtime/elf_utils.h
+++ b/runtime/elf_utils.h
@@ -17,9 +17,10 @@
#ifndef ART_RUNTIME_ELF_UTILS_H_
#define ART_RUNTIME_ELF_UTILS_H_
-// Include the micro-API to avoid potential macro conflicts with the
-// compiler's own elf.h file.
-#include "../../bionic/libc/kernel/uapi/linux/elf.h"
+#include <sys/cdefs.h>
+
+// Explicitly include elf.h from elfutils to avoid Linux and other dependencies.
+#include "../../external/elfutils/0.153/libelf/elf.h"
// Architecture dependent flags for the ELF header.
#define EF_ARM_EABI_VER5 0x05000000
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 6b216c7..3112bc0 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -67,7 +67,7 @@
action.sa_sigaction = art_fault_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
-#if !defined(__mips__)
+#if !defined(__APPLE__) && !defined(__mips__)
action.sa_restorer = nullptr;
#endif
diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h
index bd04473..2c72ba1 100644
--- a/runtime/gc/accounting/atomic_stack.h
+++ b/runtime/gc/accounting/atomic_stack.h
@@ -49,10 +49,7 @@
front_index_.StoreRelaxed(0);
back_index_.StoreRelaxed(0);
debug_is_sorted_ = true;
- int result = madvise(begin_, sizeof(T) * capacity_, MADV_DONTNEED);
- if (result == -1) {
- PLOG(WARNING) << "madvise failed";
- }
+ mem_map_->MadviseDontNeedAndZero();
}
// Beware: Mixing atomic pushes and atomic pops will cause ABA problem.
diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc
index 43a173e..a95c003 100644
--- a/runtime/gc/accounting/card_table.cc
+++ b/runtime/gc/accounting/card_table.cc
@@ -96,7 +96,7 @@
void CardTable::ClearCardTable() {
COMPILE_ASSERT(kCardClean == 0, clean_card_must_be_0);
- madvise(mem_map_->Begin(), mem_map_->Size(), MADV_DONTNEED);
+ mem_map_->MadviseDontNeedAndZero();
}
bool CardTable::AddrIsInCardTable(const void* addr) const {
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index c294bae..224b33e 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -79,12 +79,8 @@
template<size_t kAlignment>
void SpaceBitmap<kAlignment>::Clear() {
- if (bitmap_begin_ != NULL) {
- // This returns the memory to the system. Successive page faults will return zeroed memory.
- int result = madvise(bitmap_begin_, bitmap_size_, MADV_DONTNEED);
- if (result == -1) {
- PLOG(FATAL) << "madvise failed";
- }
+ if (bitmap_begin_ != nullptr) {
+ mem_map_->MadviseDontNeedAndZero();
}
}
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 10b88b3..55262f2 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -1507,6 +1507,9 @@
if (madvise_size > 0) {
DCHECK_ALIGNED(madvise_begin, kPageSize);
DCHECK_EQ(RoundUp(madvise_size, kPageSize), madvise_size);
+ if (!kMadviseZeroes) {
+ memset(madvise_begin, 0, madvise_size);
+ }
CHECK_EQ(madvise(madvise_begin, madvise_size, MADV_DONTNEED), 0);
}
if (madvise_begin - zero_begin) {
@@ -2117,6 +2120,9 @@
start = reinterpret_cast<byte*>(fpr) + kPageSize;
}
byte* end = reinterpret_cast<byte*>(fpr) + fpr_size;
+ if (!kMadviseZeroes) {
+ memset(start, 0, end - start);
+ }
CHECK_EQ(madvise(start, end - start, MADV_DONTNEED), 0);
reclaimed_bytes += fpr_size;
size_t num_pages = fpr_size / kPageSize;
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index 9464331..a439188 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -110,11 +110,17 @@
byte_size -= kPageSize;
if (byte_size > 0) {
if (release_pages) {
+ if (!kMadviseZeroes) {
+ memset(start, 0, byte_size);
+ }
madvise(start, byte_size, MADV_DONTNEED);
}
}
} else {
if (release_pages) {
+ if (!kMadviseZeroes) {
+ memset(start, 0, byte_size);
+ }
madvise(start, byte_size, MADV_DONTNEED);
}
}
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index c062706..890036b 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -1130,9 +1130,7 @@
allocations->Reset();
timings_.EndSplit();
- int success = madvise(sweep_array_free_buffer_mem_map_->BaseBegin(),
- sweep_array_free_buffer_mem_map_->BaseSize(), MADV_DONTNEED);
- DCHECK_EQ(success, 0) << "Failed to madvise the sweep array free buffer pages.";
+ sweep_array_free_buffer_mem_map_->MadviseDontNeedAndZero();
}
void MarkSweep::Sweep(bool swap_bitmaps) {
diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc
index fd0a92d..8b35692 100644
--- a/runtime/gc/space/bump_pointer_space.cc
+++ b/runtime/gc/space/bump_pointer_space.cc
@@ -64,6 +64,9 @@
void BumpPointerSpace::Clear() {
// Release the pages back to the operating system.
+ if (!kMadviseZeroes) {
+ memset(Begin(), 0, Limit() - Begin());
+ }
CHECK_NE(madvise(Begin(), Limit() - Begin(), MADV_DONTNEED), -1) << "madvise failed";
// Reset the end of the space back to the beginning, we move the end forward as we allocate
// objects.
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index d18f9f9..5277330 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -38,6 +38,7 @@
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
+#include "mirror/string-inl.h"
#include "object_utils.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc
index 49dceb2..d637a94 100644
--- a/runtime/jdwp/object_registry.cc
+++ b/runtime/jdwp/object_registry.cc
@@ -31,8 +31,7 @@
}
ObjectRegistry::ObjectRegistry()
- : lock_("ObjectRegistry lock", kJdwpObjectRegistryLock), allow_new_objects_(true),
- condition_("object registry condition", lock_), next_id_(1) {
+ : lock_("ObjectRegistry lock", kJdwpObjectRegistryLock), next_id_(1) {
}
JDWP::RefTypeId ObjectRegistry::AddRefType(mirror::Class* c) {
@@ -44,20 +43,17 @@
}
JDWP::ObjectId ObjectRegistry::InternalAdd(mirror::Object* o) {
- if (o == NULL) {
+ if (o == nullptr) {
return 0;
}
+ // Call IdentityHashCode here to avoid a lock level violation between lock_ and monitor_lock.
+ int32_t identity_hash_code = o->IdentityHashCode();
ScopedObjectAccessUnchecked soa(Thread::Current());
MutexLock mu(soa.Self(), lock_);
- while (UNLIKELY(!allow_new_objects_)) {
- condition_.WaitHoldingLocks(soa.Self());
- }
- ObjectRegistryEntry* entry;
- auto it = object_to_entry_.find(o);
- if (it != object_to_entry_.end()) {
+ ObjectRegistryEntry* entry = nullptr;
+ if (ContainsLocked(soa.Self(), o, identity_hash_code, &entry)) {
// This object was already in our map.
- entry = it->second;
++entry->reference_count;
} else {
entry = new ObjectRegistryEntry;
@@ -65,7 +61,8 @@
entry->jni_reference = nullptr;
entry->reference_count = 0;
entry->id = 0;
- object_to_entry_.insert(std::make_pair(o, entry));
+ entry->identity_hash_code = identity_hash_code;
+ object_to_entry_.insert(std::make_pair(identity_hash_code, entry));
// This object isn't in the registry yet, so add it.
JNIEnv* env = soa.Env();
@@ -84,9 +81,31 @@
return entry->id;
}
-bool ObjectRegistry::Contains(mirror::Object* o) {
- MutexLock mu(Thread::Current(), lock_);
- return object_to_entry_.find(o) != object_to_entry_.end();
+bool ObjectRegistry::Contains(mirror::Object* o, ObjectRegistryEntry** out_entry) {
+ if (o == nullptr) {
+ return false;
+ }
+ // Call IdentityHashCode here to avoid a lock level violation between lock_ and monitor_lock.
+ int32_t identity_hash_code = o->IdentityHashCode();
+ Thread* self = Thread::Current();
+ MutexLock mu(self, lock_);
+ return ContainsLocked(self, o, identity_hash_code, out_entry);
+}
+
+bool ObjectRegistry::ContainsLocked(Thread* self, mirror::Object* o, int32_t identity_hash_code,
+ ObjectRegistryEntry** out_entry) {
+ DCHECK(o != nullptr);
+ for (auto it = object_to_entry_.lower_bound(identity_hash_code), end = object_to_entry_.end();
+ it != end && it->first == identity_hash_code; ++it) {
+ ObjectRegistryEntry* entry = it->second;
+ if (o == self->DecodeJObject(entry->jni_reference)) {
+ if (out_entry != nullptr) {
+ *out_entry = entry;
+ }
+ return true;
+ }
+ }
+ return false;
}
void ObjectRegistry::Clear() {
@@ -194,47 +213,24 @@
entry->reference_count -= reference_count;
if (entry->reference_count <= 0) {
JNIEnv* env = self->GetJniEnv();
- mirror::Object* object = self->DecodeJObject(entry->jni_reference);
+ // Erase the object from the maps. Note object may be null if it's
+ // a weak ref and the GC has cleared it.
+ int32_t hash_code = entry->identity_hash_code;
+ for (auto it = object_to_entry_.lower_bound(hash_code), end = object_to_entry_.end();
+ it != end && it->first == hash_code; ++it) {
+ if (entry == it->second) {
+ object_to_entry_.erase(it);
+ break;
+ }
+ }
if (entry->jni_reference_type == JNIWeakGlobalRefType) {
env->DeleteWeakGlobalRef(entry->jni_reference);
} else {
env->DeleteGlobalRef(entry->jni_reference);
}
- object_to_entry_.erase(object);
id_to_entry_.erase(id);
delete entry;
}
}
-void ObjectRegistry::UpdateObjectPointers(IsMarkedCallback* callback, void* arg) {
- MutexLock mu(Thread::Current(), lock_);
- if (object_to_entry_.empty()) {
- return;
- }
- std::map<mirror::Object*, ObjectRegistryEntry*> new_object_to_entry;
- for (auto& pair : object_to_entry_) {
- mirror::Object* new_obj;
- if (pair.first != nullptr) {
- new_obj = callback(pair.first, arg);
- if (new_obj != nullptr) {
- new_object_to_entry.insert(std::make_pair(new_obj, pair.second));
- }
- }
- }
- object_to_entry_ = new_object_to_entry;
-}
-
-void ObjectRegistry::AllowNewObjects() {
- Thread* self = Thread::Current();
- MutexLock mu(self, lock_);
- allow_new_objects_ = true;
- condition_.Broadcast(self);
-}
-
-void ObjectRegistry::DisallowNewObjects() {
- Thread* self = Thread::Current();
- MutexLock mu(self, lock_);
- allow_new_objects_ = false;
-}
-
} // namespace art
diff --git a/runtime/jdwp/object_registry.h b/runtime/jdwp/object_registry.h
index 3c6cb15..e1a6875 100644
--- a/runtime/jdwp/object_registry.h
+++ b/runtime/jdwp/object_registry.h
@@ -43,6 +43,10 @@
// The corresponding id, so we only need one map lookup in Add.
JDWP::ObjectId id;
+
+ // The identity hash code of the object. This is the same as the key
+ // for object_to_entry_. Store this for DisposeObject().
+ int32_t identity_hash_code;
};
std::ostream& operator<<(std::ostream& os, const ObjectRegistryEntry& rhs);
@@ -55,7 +59,8 @@
public:
ObjectRegistry();
- JDWP::ObjectId Add(mirror::Object* o) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ JDWP::ObjectId Add(mirror::Object* o)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::thread_list_lock_);
JDWP::RefTypeId AddRefType(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<typename T> T Get(JDWP::ObjectId id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -65,7 +70,9 @@
return reinterpret_cast<T>(InternalGet(id));
}
- bool Contains(mirror::Object* o) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool Contains(mirror::Object* o) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return Contains(o, nullptr);
+ }
void Clear() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -84,26 +91,20 @@
// Avoid using this and use standard Get when possible.
jobject GetJObject(JDWP::ObjectId id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Visit, objects are treated as system weaks.
- void UpdateObjectPointers(IsMarkedCallback* callback, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- // We have allow / disallow functionality since we use system weak sweeping logic to update moved
- // objects inside of the object_to_entry_ map.
- void AllowNewObjects() LOCKS_EXCLUDED(lock_);
- void DisallowNewObjects() LOCKS_EXCLUDED(lock_);
-
private:
- JDWP::ObjectId InternalAdd(mirror::Object* o) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ JDWP::ObjectId InternalAdd(mirror::Object* o)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::thread_list_lock_);
mirror::Object* InternalGet(JDWP::ObjectId id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void Demote(ObjectRegistryEntry& entry) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, lock_);
void Promote(ObjectRegistryEntry& entry) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, lock_);
+ bool Contains(mirror::Object* o, ObjectRegistryEntry** out_entry)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool ContainsLocked(Thread* self, mirror::Object* o, int32_t identity_hash_code,
+ ObjectRegistryEntry** out_entry)
+ EXCLUSIVE_LOCKS_REQUIRED(lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- bool allow_new_objects_ GUARDED_BY(lock_);
- ConditionVariable condition_ GUARDED_BY(lock_);
-
- std::map<mirror::Object*, ObjectRegistryEntry*> object_to_entry_ GUARDED_BY(lock_);
+ std::multimap<int32_t, ObjectRegistryEntry*> object_to_entry_ GUARDED_BY(lock_);
SafeMap<JDWP::ObjectId, ObjectRegistryEntry*> id_to_entry_ GUARDED_BY(lock_);
size_t next_id_ GUARDED_BY(lock_);
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 22a61a2..81a8623 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -473,6 +473,18 @@
return new MemMap(tail_name, actual, tail_size, actual, tail_base_size, tail_prot);
}
+void MemMap::MadviseDontNeedAndZero() {
+ if (base_begin_ != nullptr || base_size_ != 0) {
+ if (!kMadviseZeroes) {
+ memset(base_begin_, 0, base_size_);
+ }
+ int result = madvise(base_begin_, base_size_, MADV_DONTNEED);
+ if (result == -1) {
+ PLOG(WARNING) << "madvise failed";
+ }
+ }
+}
+
bool MemMap::Protect(int prot) {
if (base_begin_ == nullptr && base_size_ == 0) {
prot_ = prot;
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index dc5909b..e42251c 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -30,6 +30,12 @@
namespace art {
+#ifdef __linux__
+static constexpr bool kMadviseZeroes = true;
+#else
+static constexpr bool kMadviseZeroes = false;
+#endif
+
// Used to keep track of mmap segments.
//
// On 64b systems not supporting MAP_32BIT, the implementation of MemMap will do a linear scan
@@ -77,6 +83,8 @@
bool Protect(int prot);
+ void MadviseDontNeedAndZero();
+
int GetProtect() const {
return prot_;
}
diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc
index fe76c92..69f618c 100644
--- a/runtime/mem_map_test.cc
+++ b/runtime/mem_map_test.cc
@@ -198,16 +198,17 @@
#endif
TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) {
+ uintptr_t start_addr = ART_BASE_ADDRESS + 0x1000000;
std::string error_msg;
std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr",
- reinterpret_cast<byte*>(0x71000000),
+ reinterpret_cast<byte*>(start_addr),
0x21000000,
PROT_READ | PROT_WRITE,
true,
&error_msg));
ASSERT_TRUE(map.get() != nullptr) << error_msg;
ASSERT_TRUE(error_msg.empty());
- ASSERT_EQ(reinterpret_cast<uintptr_t>(BaseBegin(map.get())), 0x71000000U);
+ ASSERT_EQ(reinterpret_cast<uintptr_t>(BaseBegin(map.get())), start_addr);
}
TEST_F(MemMapTest, MapAnonymousOverflow) {
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 2c24e33..7e3810c 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -17,7 +17,11 @@
#include <algorithm>
#include <set>
#include <fcntl.h>
+#ifdef __linux__
#include <sys/sendfile.h>
+#else
+#include <sys/socket.h>
+#endif
#include <sys/stat.h>
#include <unistd.h>
@@ -241,7 +245,12 @@
return;
}
+#ifdef __linux__
if (sendfile(dst.get(), src.get(), nullptr, stat_src.st_size) == -1) {
+#else
+ off_t len;
+ if (sendfile(dst.get(), src.get(), 0, &len, nullptr, 0) == -1) {
+#endif
PLOG(ERROR) << "Failed to copy profile file " << oldfile << " to " << newfile
<< ". My uid:gid is " << getuid() << ":" << getgid();
}
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 7490e6a..820bd04 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -32,9 +32,11 @@
static void EnableDebugger() {
// To let a non-privileged gdbserver attach to this
// process, we must set our dumpable flag.
+#if defined(HAVE_PRCTL)
if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
PLOG(ERROR) << "prctl(PR_SET_DUMPABLE) failed for pid " << getpid();
}
+#endif
// We don't want core dumps, though, so set the core dump size to 0.
rlimit rl;
rl.rlim_cur = 0;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 89058c8..ccf478c 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -18,7 +18,9 @@
// sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
#include <sys/mount.h>
+#ifdef __linux__
#include <linux/fs.h>
+#endif
#include <signal.h>
#include <sys/syscall.h>
@@ -307,7 +309,6 @@
GetInternTable()->SweepInternTableWeaks(visitor, arg);
GetMonitorList()->SweepMonitorList(visitor, arg);
GetJavaVM()->SweepJniWeakGlobals(visitor, arg);
- Dbg::UpdateObjectPointers(visitor, arg);
}
bool Runtime::Create(const Options& options, bool ignore_unrecognized) {
@@ -437,6 +438,7 @@
// Do zygote-mode-only initialization.
bool Runtime::InitZygote() {
+#ifdef __linux__
// zygote goes into its own process group
setpgid(0, 0);
@@ -467,6 +469,10 @@
}
return true;
+#else
+ UNIMPLEMENTED(FATAL);
+ return false;
+#endif
}
void Runtime::DidForkFromZygote() {
@@ -1036,14 +1042,12 @@
monitor_list_->DisallowNewMonitors();
intern_table_->DisallowNewInterns();
java_vm_->DisallowNewWeakGlobals();
- Dbg::DisallowNewObjectRegistryObjects();
}
void Runtime::AllowNewSystemWeaks() {
monitor_list_->AllowNewMonitors();
intern_table_->AllowNewInterns();
java_vm_->AllowNewWeakGlobals();
- Dbg::AllowNewObjectRegistryObjects();
}
void Runtime::SetInstructionSet(InstructionSet instruction_set) {
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index 960d332..46ee274 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -327,7 +327,7 @@
while (true) {
}
}
-
+#ifdef __linux__
// Remove our signal handler for this signal...
struct sigaction action;
memset(&action, 0, sizeof(action));
@@ -336,6 +336,9 @@
sigaction(signal_number, &action, NULL);
// ...and re-raise so we die with the appropriate status.
kill(getpid(), signal_number);
+#else
+ exit(EXIT_FAILURE);
+#endif
}
void Runtime::InitPlatformSignalHandlers() {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 021c7c1..6980530 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -495,7 +495,9 @@
}
// TODO: move this into the Linux GetThreadStack implementation.
-#if !defined(__APPLE__)
+#if defined(__APPLE__)
+ bool is_main_thread = false;
+#else
// If we're the main thread, check whether we were run with an unlimited stack. In that case,
// glibc will have reported a 2GB stack for our 32-bit process, and our stack overflow detection
// will be broken because we'll die long before we get close to 2GB.
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 7700658..f60f795 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1054,6 +1054,7 @@
if (current_method != nullptr) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
+#ifdef __linux__
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
if (!backtrace->Unwind(0)) {
os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n";
@@ -1095,6 +1096,7 @@
}
os << "\n";
}
+#endif
}
#if defined(__APPLE__)
diff --git a/test/404-optimizing-allocator/expected.txt b/test/404-optimizing-allocator/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/404-optimizing-allocator/expected.txt
diff --git a/test/404-optimizing-allocator/info.txt b/test/404-optimizing-allocator/info.txt
new file mode 100644
index 0000000..930d42f
--- /dev/null
+++ b/test/404-optimizing-allocator/info.txt
@@ -0,0 +1 @@
+Initial tests for testing the optimizing compiler's register allocator.
diff --git a/test/404-optimizing-allocator/src/Main.java b/test/404-optimizing-allocator/src/Main.java
new file mode 100644
index 0000000..60477f9
--- /dev/null
+++ b/test/404-optimizing-allocator/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.
+ */
+
+// Note that $opt$reg$ is a marker for the optimizing compiler to ensure
+// it does use its register allocator.
+
+public class Main {
+ public static void main(String[] args) {
+
+ expectEquals(4, $opt$reg$TestLostCopy());
+ expectEquals(-10, $opt$reg$TestTwoLive());
+ expectEquals(-20, $opt$reg$TestThreeLive());
+ expectEquals(5, $opt$reg$TestFourLive());
+ expectEquals(10, $opt$reg$TestMultipleLive());
+ expectEquals(1, $opt$reg$TestWithBreakAndContinue());
+ expectEquals(-15, $opt$reg$testSpillInIf(5, 6, 7));
+ expectEquals(-567, $opt$reg$TestAgressiveLive(1, 2, 3, 4, 5, 6, 7));
+ }
+
+ public static int $opt$reg$TestLostCopy() {
+ int a = 0;
+ int b = 0;
+ do {
+ b = a;
+ a++;
+ } while (a != 5);
+ return b;
+ }
+
+ public static int $opt$reg$TestTwoLive() {
+ int a = 0;
+ int b = 0;
+ do {
+ a++;
+ b += 3;
+ } while (a != 5);
+ return a - b;
+ }
+
+ public static int $opt$reg$TestThreeLive() {
+ int a = 0;
+ int b = 0;
+ int c = 0;
+ do {
+ a++;
+ b += 3;
+ c += 2;
+ } while (a != 5);
+ return a - b - c;
+ }
+
+ public static int $opt$reg$TestFourLive() {
+ int a = 0;
+ int b = 0;
+ int c = 0;
+ int d = 0;
+ do {
+ a++;
+ b += 3;
+ c += 2;
+ d++;
+ } while (a != 5);
+ return d;
+ }
+
+ public static int $opt$reg$TestMultipleLive() {
+ int a = 0;
+ int b = 0;
+ int c = 0;
+ int d = 0;
+ int e = 0;
+ int f = 0;
+ int g = 0;
+ do {
+ a++;
+ b++;
+ c++;
+ d++;
+ e += 3;
+ f += 2;
+ g += 2;
+ } while (a != 5);
+ return f;
+ }
+
+ public static int $opt$reg$TestWithBreakAndContinue() {
+ int a = 0;
+ int b = 0;
+ do {
+ a++;
+ if (a == 2) {
+ continue;
+ }
+ b++;
+ if (a == 5) {
+ break;
+ }
+ } while (true);
+ return a - b;
+ }
+
+ public static int $opt$reg$testSpillInIf(int a, int b, int c) {
+ int d = 0;
+ int e = 0;
+ if (a == 5) {
+ b++;
+ c++;
+ d += 2;
+ e += 3;
+ }
+
+ return a - b - c - d - e;
+ }
+
+ public static int $opt$reg$TestAgressiveLive(int a, int b, int c, int d, int e, int f, int g) {
+ int h = a - b;
+ int i = c - d;
+ int j = e - f;
+ int k = 42 + g - a;
+ do {
+ b++;
+ while (k != 1) {
+ --k;
+ ++i;
+ if (i == 9) {
+ ++i;
+ }
+ j += 5;
+ }
+ k = 9;
+ h++;
+ } while (h != 5);
+ return a - b - c - d - e - f - g - h - i - j - k;
+ }
+
+ public static void expectEquals(int expected, int value) {
+ if (expected != value) {
+ throw new Error("Expected: " + expected + ", got: " + value);
+ }
+ }
+}
diff --git a/test/Android.mk b/test/Android.mk
index 109382d..7897449 100644
--- a/test/Android.mk
+++ b/test/Android.mk
@@ -144,7 +144,7 @@
ANDROID_DATA=/tmp/android-data/test-art-host-oat-default-$(1) \
ANDROID_ROOT=$(HOST_OUT) \
LD_LIBRARY_PATH=$(HOST_LIBRARY_PATH) \
- $(HOST_OUT_EXECUTABLES)/dalvikvm $(DALVIKVM_FLAGS) -XXlib:libartd.so -Ximage:$(HOST_CORE_IMG_LOCATION) -classpath $(HOST_OUT_JAVA_LIBRARIES)/oat-test-dex-$(1).jar -Djava.library.path=$(HOST_LIBRARY_PATH) $(1) $(2) \
+ $(HOST_OUT_EXECUTABLES)/dalvikvm $(DALVIKVM_FLAGS) -XXlib:libartd$(HOST_SHLIB_SUFFIX) -Ximage:$(HOST_CORE_IMG_LOCATION) -classpath $(HOST_OUT_JAVA_LIBRARIES)/oat-test-dex-$(1).jar -Djava.library.path=$(HOST_LIBRARY_PATH) $(1) $(2) \
&& echo test-art-host-oat-default-$(1) PASSED || (echo test-art-host-oat-default-$(1) FAILED && exit 1)
$(hide) rm -r /tmp/android-data/test-art-host-oat-default-$(1)
@@ -154,7 +154,7 @@
ANDROID_DATA=/tmp/android-data/test-art-host-oat-interpreter-$(1) \
ANDROID_ROOT=$(HOST_OUT) \
LD_LIBRARY_PATH=$(HOST_LIBRARY_PATH) \
- $(HOST_OUT_EXECUTABLES)/dalvikvm -XXlib:libartd.so -Ximage:$(HOST_CORE_IMG_LOCATION) $(DALVIKVM_FLAGS) -Xint -classpath $(HOST_OUT_JAVA_LIBRARIES)/oat-test-dex-$(1).jar -Djava.library.path=$(HOST_LIBRARY_PATH) $(1) $(2) \
+ $(HOST_OUT_EXECUTABLES)/dalvikvm -XXlib:libartd$(HOST_SHLIB_SUFFIX) -Ximage:$(HOST_CORE_IMG_LOCATION) $(DALVIKVM_FLAGS) -Xint -classpath $(HOST_OUT_JAVA_LIBRARIES)/oat-test-dex-$(1).jar -Djava.library.path=$(HOST_LIBRARY_PATH) $(1) $(2) \
&& echo test-art-host-oat-interpreter-$(1) PASSED || (echo test-art-host-oat-interpreter-$(1) FAILED && exit 1)
$(hide) rm -r /tmp/android-data/test-art-host-oat-interpreter-$(1)
diff --git a/test/SignalTest/signaltest.cc b/test/SignalTest/signaltest.cc
index b84e395..dfe3197 100644
--- a/test/SignalTest/signaltest.cc
+++ b/test/SignalTest/signaltest.cc
@@ -46,7 +46,7 @@
action.sa_sigaction = signalhandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
-#if !defined(__mips__)
+#if !defined(__APPLE__) && !defined(__mips__)
action.sa_restorer = nullptr;
#endif
diff --git a/tools/Android.mk b/tools/Android.mk
index 6c385dc..d3be17f 100644
--- a/tools/Android.mk
+++ b/tools/Android.mk
@@ -16,7 +16,6 @@
LOCAL_PATH := $(call my-dir)
-ifeq ($(WITH_HOST_DALVIK),true)
# Copy the art shell script to the host's bin directory
include $(CLEAR_VARS)
LOCAL_IS_HOST_MODULE := true
@@ -28,5 +27,3 @@
@echo "Copy: $(PRIVATE_MODULE) ($@)"
$(copy-file-to-new-target)
$(hide) chmod 755 $@
-
-endif