Compile method one-by-one.
Change-Id: Ic56fb397f3bd6dee32372eb875261a3383eaf30c
diff --git a/src/compiler_llvm/compilation_unit.cc b/src/compiler_llvm/compilation_unit.cc
index 38faf61..fdbf575 100644
--- a/src/compiler_llvm/compilation_unit.cc
+++ b/src/compiler_llvm/compilation_unit.cc
@@ -17,6 +17,7 @@
#include "compilation_unit.h"
#include "compiled_method.h"
+#include "compiler_llvm.h"
#include "file.h"
#include "instruction_set.h"
#include "ir_builder.h"
@@ -47,8 +48,10 @@
#include <llvm/DerivedTypes.h>
#include <llvm/LLVMContext.h>
#include <llvm/Module.h>
+#include <llvm/Object/ObjectFile.h>
#include <llvm/PassManager.h>
#include <llvm/Support/Debug.h>
+#include <llvm/Support/ELF.h>
#include <llvm/Support/FormattedStream.h>
#include <llvm/Support/ManagedStatic.h>
#include <llvm/Support/MemoryBuffer.h>
@@ -77,37 +80,6 @@
namespace {
-class UpdateFrameSizePass : public llvm::MachineFunctionPass {
- public:
- static char ID;
-
- UpdateFrameSizePass() : llvm::MachineFunctionPass(ID), cunit_(NULL) {
- LOG(FATAL) << "Unexpected instantiation of UpdateFrameSizePass";
- // NOTE: We have to declare this constructor for llvm::RegisterPass, but
- // this constructor won't work because we have no information on
- // CompilationUnit. Thus, we should place a LOG(FATAL) here.
- }
-
- UpdateFrameSizePass(art::compiler_llvm::CompilationUnit* cunit)
- : llvm::MachineFunctionPass(ID), cunit_(cunit) {
- }
-
- virtual bool runOnMachineFunction(llvm::MachineFunction &MF) {
- cunit_->UpdateFrameSizeInBytes(MF.getFunction(),
- MF.getFrameInfo()->getStackSize());
- return false;
- }
-
- private:
- art::compiler_llvm::CompilationUnit* cunit_;
-};
-
-char UpdateFrameSizePass::ID = 0;
-
-llvm::RegisterPass<UpdateFrameSizePass> reg_update_frame_size_pass_(
- "update-frame-size", "Update frame size pass", false, false);
-
-
// TODO: We may need something to manage these passes.
// TODO: We need high-level IR to analysis and do this at the IRBuilder level.
class AddSuspendCheckToLoopLatchPass : public llvm::LoopPass {
@@ -167,10 +139,10 @@
llvm::Module* makeLLVMModuleContents(llvm::Module* module);
-CompilationUnit::CompilationUnit(InstructionSet insn_set, size_t elf_idx)
-: cunit_lock_("compilation_unit_lock"), insn_set_(insn_set), elf_idx_(elf_idx),
- context_(new llvm::LLVMContext()), compiled_methods_map_(new CompiledMethodMap()),
- mem_usage_(0), num_elf_funcs_(0) {
+CompilationUnit::CompilationUnit(const CompilerLLVM* compiler_llvm,
+ size_t cunit_idx)
+: compiler_llvm_(compiler_llvm), cunit_idx_(cunit_idx),
+ context_(new llvm::LLVMContext()) {
// Create the module and include the runtime function declaration
module_ = new llvm::Module("art", *context_);
@@ -180,7 +152,7 @@
irb_.reset(new IRBuilder(*context_, *module_));
// We always need a switch case, so just use a normal function.
- switch(insn_set_) {
+ switch(GetInstructionSet()) {
default:
runtime_support_.reset(new RuntimeSupportBuilder(*context_, *module_, *irb_));
break;
@@ -205,57 +177,56 @@
}
-bool CompilationUnit::Materialize(size_t thread_count) {
- MutexLock GUARD(cunit_lock_);
-
- // Materialize the bitcode to elf_image_
- llvm::raw_string_ostream str_os(elf_image_);
- bool success = MaterializeToFile(str_os);
- LOG(INFO) << "Compilation Unit: " << elf_idx_ << (success ? " (done)" : " (failed)");
-
- // Free the resources
- context_.reset(NULL);
- irb_.reset(NULL);
- module_ = NULL;
- runtime_support_.reset(NULL);
- compiled_methods_map_.reset(NULL);
-
- return success;
+InstructionSet CompilationUnit::GetInstructionSet() const {
+ return compiler_llvm_->GetInstructionSet();
}
-void CompilationUnit::RegisterCompiledMethod(const llvm::Function* func,
- CompiledMethod* compiled_method) {
- MutexLock GUARD(cunit_lock_);
- compiled_methods_map_->Put(func, compiled_method);
-}
+bool CompilationUnit::Materialize() {
+ std::string elf_image;
-
-void CompilationUnit::UpdateFrameSizeInBytes(const llvm::Function* func,
- size_t frame_size_in_bytes) {
- MutexLock GUARD(cunit_lock_);
- SafeMap<const llvm::Function*, CompiledMethod*>::iterator iter =
- compiled_methods_map_->find(func);
-
- if (iter != compiled_methods_map_->end()) {
- CompiledMethod* compiled_method = iter->second;
- compiled_method->SetFrameSizeInBytes(frame_size_in_bytes);
-
- if (frame_size_in_bytes > 1728u) {
- LOG(WARNING) << "Huge frame size: " << frame_size_in_bytes
- << " elf_idx=" << compiled_method->GetElfIndex()
- << " elf_func_idx=" << compiled_method->GetElfFuncIndex();
- }
+ // Compile and prelink llvm::Module
+ if (!MaterializeToString(elf_image)) {
+ LOG(ERROR) << "Failed to materialize compilation unit " << cunit_idx_;
+ DeleteResources();
+ return false;
}
+
+#if 0
+ // Dump the ELF image for debugging
+ std::string filename(StringPrintf("%s/Art%zu.elf",
+ GetArtCacheOrDie(GetAndroidData()).c_str(),
+ cunit_idx_));
+ UniquePtr<File> output(OS::OpenFile(filename.c_str(), true));
+ output->WriteFully(elf_image.data(), elf_image.size());
+#endif
+
+ // Extract the .text section and prelink the code
+ if (!ExtractCodeAndPrelink(elf_image)) {
+ LOG(ERROR) << "Failed to extract code from compilation unit " << cunit_idx_;
+ DeleteResources();
+ return false;
+ }
+
+ DeleteResources();
+ return true;
}
-bool CompilationUnit::MaterializeToFile(llvm::raw_ostream& out_stream) {
+
+bool CompilationUnit::MaterializeToString(std::string& str_buffer) {
+ llvm::raw_string_ostream str_os(str_buffer);
+ return MaterializeToRawOStream(str_os);
+}
+
+
+bool CompilationUnit::MaterializeToRawOStream(llvm::raw_ostream& out_stream) {
// Lookup the LLVM target
char const* target_triple = NULL;
char const* target_cpu = "";
char const* target_attr = NULL;
- switch (insn_set_) {
+ InstructionSet insn_set = GetInstructionSet();
+ switch (insn_set) {
case kThumb2:
target_triple = "thumb-none-linux-gnueabi";
target_cpu = "cortex-a9";
@@ -281,7 +252,7 @@
break;
default:
- LOG(FATAL) << "Unknown instruction set: " << insn_set_;
+ LOG(FATAL) << "Unknown instruction set: " << insn_set;
}
std::string errmsg;
@@ -375,11 +346,6 @@
return false;
}
- // FIXME: Unable to run the UpdateFrameSizePass pass since it tries to
- // update the value reside in the different address space.
- // Add pass to update the frame_size_in_bytes_
- //pm.add(new ::UpdateFrameSizePass(this));
-
// Run the per-function optimization
fpm.doInitialization();
for (llvm::Module::iterator F = module_->begin(), E = module_->end();
@@ -395,5 +361,130 @@
return true;
}
+
+bool CompilationUnit::ExtractCodeAndPrelink(const std::string& elf_image) {
+ llvm::OwningPtr<llvm::MemoryBuffer> elf_image_buff(
+ llvm::MemoryBuffer::getMemBuffer(llvm::StringRef(elf_image.data(),
+ elf_image.size())));
+
+ llvm::OwningPtr<llvm::object::ObjectFile> elf_file(
+ llvm::object::ObjectFile::createELFObjectFile(elf_image_buff.take()));
+
+ llvm::error_code ec;
+
+ const ProcedureLinkageTable& plt = compiler_llvm_->GetProcedureLinkageTable();
+
+ for (llvm::object::section_iterator
+ sec_iter = elf_file->begin_sections(),
+ sec_end = elf_file->end_sections();
+ sec_iter != sec_end; sec_iter.increment(ec)) {
+
+ CHECK(ec == 0) << "Failed to read section because " << ec.message();
+
+ // Read the section information
+ llvm::StringRef name;
+ uint64_t alignment = 0u;
+ uint64_t size = 0u;
+
+ CHECK(sec_iter->getName(name) == 0);
+ CHECK(sec_iter->getSize(size) == 0);
+ CHECK(sec_iter->getAlignment(alignment) == 0);
+
+ if (name == ".data" || name == ".bss" || name == ".rodata") {
+ if (size > 0) {
+ LOG(FATAL) << "Compilation unit " << cunit_idx_ << " has non-empty "
+ << name.str() << " section";
+ }
+
+ } else if (name == "" || name == ".rel.text" ||
+ name == ".ARM.attributes" || name == ".symtab" ||
+ name == ".strtab" || name == ".shstrtab") {
+ // We can ignore these sections. We don't have to copy them into
+ // the result Oat file.
+
+ } else if (name == ".text") {
+ // Ensure the alignment requirement is less than or equal to
+ // kArchAlignment
+ CheckCodeAlign(alignment);
+
+ // Copy the compiled code
+ llvm::StringRef contents;
+ CHECK(sec_iter->getContents(contents) == 0);
+
+ copy(contents.data(),
+ contents.data() + contents.size(),
+ back_inserter(compiled_code_));
+
+ // Prelink the compiled code
+ for (llvm::object::relocation_iterator
+ rel_iter = sec_iter->begin_relocations(),
+ rel_end = sec_iter->end_relocations(); rel_iter != rel_end;
+ rel_iter.increment(ec)) {
+
+ CHECK(ec == 0) << "Failed to read relocation because " << ec.message();
+
+ // Read the relocation information
+ llvm::object::SymbolRef sym_ref;
+ uint64_t rel_offset = 0;
+ uint64_t rel_type = 0;
+ int64_t rel_addend = 0;
+
+ CHECK(rel_iter->getSymbol(sym_ref) == 0);
+ CHECK(rel_iter->getOffset(rel_offset) == 0);
+ CHECK(rel_iter->getType(rel_type) == 0);
+ CHECK(rel_iter->getAdditionalInfo(rel_addend) == 0);
+
+ // Read the symbol related to this relocation fixup
+ llvm::StringRef sym_name;
+ CHECK(sym_ref.getName(sym_name) == 0);
+
+ // Relocate the fixup.
+ // TODO: Support more relocation type.
+ CHECK(rel_type == llvm::ELF::R_ARM_ABS32);
+ CHECK_LE(rel_offset + 4, compiled_code_.size());
+
+ uintptr_t dest_addr = plt.GetEntryAddress(sym_name.str().c_str());
+ uintptr_t final_addr = dest_addr + rel_addend;
+ compiled_code_[rel_offset] = final_addr & 0xff;
+ compiled_code_[rel_offset + 1] = (final_addr >> 8) & 0xff;
+ compiled_code_[rel_offset + 2] = (final_addr >> 16) & 0xff;
+ compiled_code_[rel_offset + 3] = (final_addr >> 24) & 0xff;
+ }
+
+ } else {
+ LOG(WARNING) << "Unexpected section: " << name.str();
+ }
+ }
+
+ return true;
+}
+
+
+// Check whether the align is less than or equal to the code alignment of
+// that architecture. Since the Oat writer only guarantee that the compiled
+// method being aligned to kArchAlignment, we have no way to align the ELf
+// section if the section alignment is greater than kArchAlignment.
+void CompilationUnit::CheckCodeAlign(uint32_t align) const {
+ InstructionSet insn_set = GetInstructionSet();
+ switch (insn_set) {
+ case kThumb2:
+ case kArm:
+ CHECK_LE(align, static_cast<uint32_t>(kArmAlignment));
+ break;
+
+ case kX86:
+ CHECK_LE(align, static_cast<uint32_t>(kX86Alignment));
+ break;
+
+ case kMips:
+ CHECK_LE(align, static_cast<uint32_t>(kMipsAlignment));
+ break;
+
+ default:
+ LOG(FATAL) << "Unknown instruction set: " << insn_set;
+ }
+}
+
+
} // namespace compiler_llvm
} // namespace art