blob: 95073ea39bc8b3e8ae5f69f2b96327ea7cb2ccc6 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "compilation_unit.h"
#include "compiled_method.h"
#include "compiler_llvm.h"
#include "file.h"
#include "instruction_set.h"
#include "ir_builder.h"
#include "logging.h"
#include "os.h"
#include "runtime_support_builder_arm.h"
#include "runtime_support_builder_thumb2.h"
#include "runtime_support_builder_x86.h"
#include <llvm/ADT/OwningPtr.h>
#include <llvm/ADT/StringSet.h>
#include <llvm/ADT/Triple.h>
#include <llvm/Analysis/CallGraph.h>
#include <llvm/Analysis/DebugInfo.h>
#include <llvm/Analysis/Dominators.h>
#include <llvm/Analysis/LoopInfo.h>
#include <llvm/Analysis/LoopPass.h>
#include <llvm/Analysis/RegionPass.h>
#include <llvm/Analysis/ScalarEvolution.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Assembly/PrintModulePass.h>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/CallGraphSCCPass.h>
#include <llvm/CodeGen/MachineFrameInfo.h>
#include <llvm/CodeGen/MachineFunction.h>
#include <llvm/CodeGen/MachineFunctionPass.h>
#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>
#include <llvm/Support/PassNameParser.h>
#include <llvm/Support/PluginLoader.h>
#include <llvm/Support/PrettyStackTrace.h>
#include <llvm/Support/Signals.h>
#include <llvm/Support/SystemUtils.h>
#include <llvm/Support/TargetRegistry.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/ToolOutputFile.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Support/system_error.h>
#include <llvm/Target/TargetData.h>
#include <llvm/Target/TargetLibraryInfo.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include <llvm/Transforms/Scalar.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string>
namespace {
// 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 {
public:
static char ID;
AddSuspendCheckToLoopLatchPass() : llvm::LoopPass(ID), irb_(NULL) {
LOG(FATAL) << "Unexpected instantiation of AddSuspendCheckToLoopLatchPass";
// NOTE: We have to declare this constructor for llvm::RegisterPass, but
// this constructor won't work because we have no information on
// IRBuilder. Thus, we should place a LOG(FATAL) here.
}
AddSuspendCheckToLoopLatchPass(art::compiler_llvm::IRBuilder* irb)
: llvm::LoopPass(ID), irb_(irb) {
}
virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const {
AU.addRequired<llvm::LoopInfo>();
AU.addRequiredID(llvm::LoopSimplifyID);
AU.addPreserved<llvm::DominatorTree>();
AU.addPreserved<llvm::LoopInfo>();
AU.addPreservedID(llvm::LoopSimplifyID);
AU.addPreserved<llvm::ScalarEvolution>();
AU.addPreservedID(llvm::BreakCriticalEdgesID);
}
virtual bool runOnLoop(llvm::Loop *loop, llvm::LPPassManager &lpm) {
llvm::LoopInfo* loop_info = &getAnalysis<llvm::LoopInfo>();
CHECK_EQ(loop->getNumBackEdges(), 1U) << "Loop must be simplified!";
llvm::BasicBlock* bb = loop->getLoopLatch();
CHECK_NE(bb, static_cast<void*>(NULL)) << "A single loop latch must exist.";
llvm::BasicBlock* tb = bb->splitBasicBlock(bb->getTerminator(), "suspend_exit");
// Remove unconditional branch which is added by splitBasicBlock.
bb->getTerminator()->eraseFromParent();
irb_->SetInsertPoint(bb);
irb_->Runtime().EmitTestSuspend();
irb_->CreateBr(tb);
loop->addBasicBlockToLoop(tb, loop_info->getBase());
// EmitTestSuspend() creates some basic blocks. We should add them to using
// addBasicBlockToLoop(...) as above.
for (llvm::succ_iterator succ_iter = llvm::succ_begin(bb), succ_end = llvm::succ_end(bb);
succ_iter != succ_end;
succ_iter++) {
loop->addBasicBlockToLoop(*succ_iter, loop_info->getBase());
}
return true;
}
private:
art::compiler_llvm::IRBuilder* irb_;
};
char AddSuspendCheckToLoopLatchPass::ID = 0;
llvm::RegisterPass<AddSuspendCheckToLoopLatchPass> reg_add_suspend_check_to_loop_latch_pass_(
"add-suspend-check-to-loop-latch", "Add suspend check to loop latch pass", false, false);
} // end anonymous namespace
namespace art {
namespace compiler_llvm {
#if defined(ART_USE_DEXLANG_FRONTEND)
llvm::FunctionPass*
CreateGBCExpanderPass(const greenland::IntrinsicHelper& intrinsic_helper,
IRBuilder& irb);
#elif defined(ART_USE_QUICK_COMPILER)
llvm::FunctionPass*
CreateGBCExpanderPass(const greenland::IntrinsicHelper& intrinsic_helper, IRBuilder& irb,
Compiler* compiler, OatCompilationUnit* oat_compilation_unit);
#endif
llvm::Module* makeLLVMModuleContents(llvm::Module* module);
CompilationUnit::CompilationUnit(const CompilerLLVM* compiler_llvm,
size_t cunit_idx)
: compiler_llvm_(compiler_llvm), cunit_idx_(cunit_idx) {
#if !defined(ART_USE_QUICK_COMPILER)
context_.reset(new llvm::LLVMContext());
module_ = new llvm::Module("art", *context_);
#else
compiler_ = NULL;
oat_compilation_unit_ = NULL;
quick_ctx_.reset(new QuickCompiler());
context_.reset(quick_ctx_->GetLLVMContext());
module_ = quick_ctx_->GetLLVMModule();
#endif
// Include the runtime function declaration
makeLLVMModuleContents(module_);
#if defined(ART_USE_DEXLANG_FRONTEND)
dex_lang_ctx_ = new greenland::DexLang::Context(*module_);
#endif
// Create IRBuilder
irb_.reset(new IRBuilder(*context_, *module_));
// We always need a switch case, so just use a normal function.
switch(GetInstructionSet()) {
default:
runtime_support_.reset(new RuntimeSupportBuilder(*context_, *module_, *irb_));
break;
case kArm:
runtime_support_.reset(new RuntimeSupportBuilderARM(*context_, *module_, *irb_));
break;
case kThumb2:
runtime_support_.reset(new RuntimeSupportBuilderThumb2(*context_, *module_, *irb_));
break;
case kX86:
runtime_support_.reset(new RuntimeSupportBuilderX86(*context_, *module_, *irb_));
break;
}
irb_->SetRuntimeSupport(runtime_support_.get());
}
CompilationUnit::~CompilationUnit() {
#if defined(ART_USE_DEXLANG_FRONTEND)
delete dex_lang_ctx_;
#elif defined(ART_USE_QUICK_COMPILER)
llvm::LLVMContext* llvm_context = context_.release(); // Managed by quick_ctx_
CHECK(llvm_context != NULL);
#endif
}
InstructionSet CompilationUnit::GetInstructionSet() const {
return compiler_llvm_->GetInstructionSet();
}
bool CompilationUnit::Materialize() {
std::string elf_image;
// Compile and prelink llvm::Module
if (!MaterializeToString(elf_image)) {
LOG(ERROR) << "Failed to materialize compilation unit " << cunit_idx_;
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_;
return false;
}
return true;
}
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
const char* target_triple = NULL;
const char* target_cpu = "";
const char* target_attr = NULL;
InstructionSet insn_set = GetInstructionSet();
switch (insn_set) {
case kThumb2:
target_triple = "thumb-none-linux-gnueabi";
target_cpu = "cortex-a9";
target_attr = "+thumb2,+neon,+neonfp,+vfp3,+db";
break;
case kArm:
target_triple = "armv7-none-linux-gnueabi";
// TODO: Fix for Nexus S.
target_cpu = "cortex-a9";
// TODO: Fix for Xoom.
target_attr = "+v7,+neon,+neonfp,+vfp3,+db";
break;
case kX86:
target_triple = "i386-pc-linux-gnu";
target_attr = "";
break;
case kMips:
target_triple = "mipsel-unknown-linux";
target_attr = "mips32r2";
break;
default:
LOG(FATAL) << "Unknown instruction set: " << insn_set;
}
std::string errmsg;
const llvm::Target* target =
llvm::TargetRegistry::lookupTarget(target_triple, errmsg);
CHECK(target != NULL) << errmsg;
// Target options
llvm::TargetOptions target_options;
target_options.FloatABIType = llvm::FloatABI::Soft;
target_options.NoFramePointerElim = true;
target_options.NoFramePointerElimNonLeaf = true;
target_options.UseSoftFloat = false;
target_options.EnableFastISel = false;
// Create the llvm::TargetMachine
llvm::OwningPtr<llvm::TargetMachine> target_machine(
target->createTargetMachine(target_triple, target_cpu, target_attr, target_options,
llvm::Reloc::Static, llvm::CodeModel::Small,
llvm::CodeGenOpt::Aggressive));
CHECK(target_machine.get() != NULL) << "Failed to create target machine";
// Add target data
const llvm::TargetData* target_data = target_machine->getTargetData();
// PassManager for code generation passes
llvm::PassManager pm;
pm.add(new llvm::TargetData(*target_data));
// FunctionPassManager for optimization pass
llvm::FunctionPassManager fpm(module_);
fpm.add(new llvm::TargetData(*target_data));
if (bitcode_filename_.empty()) {
// If we don't need write the bitcode to file, add the AddSuspendCheckToLoopLatchPass to the
// regular FunctionPass.
#if defined(ART_USE_DEXLANG_FRONTEND)
fpm.add(CreateGBCExpanderPass(dex_lang_ctx_->GetIntrinsicHelper(), *irb_.get()));
#elif defined(ART_USE_QUICK_COMPILER)
fpm.add(CreateGBCExpanderPass(*quick_ctx_->GetIntrinsicHelper(), *irb_.get(),
compiler_, oat_compilation_unit_));
#endif
fpm.add(new ::AddSuspendCheckToLoopLatchPass(irb_.get()));
} else {
// Run AddSuspendCheckToLoopLatchPass before we write the bitcode to file.
llvm::FunctionPassManager fpm2(module_);
#if defined(ART_USE_DEXLANG_FRONTEND)
fpm2.add(CreateGBCExpanderPass(dex_lang_ctx_->GetIntrinsicHelper(), *irb_.get()));
#elif defined(ART_USE_QUICK_COMPILER)
fpm2.add(CreateGBCExpanderPass(*quick_ctx_->GetIntrinsicHelper(), *irb_.get(),
compiler_, oat_compilation_unit_));
#endif
fpm2.add(new ::AddSuspendCheckToLoopLatchPass(irb_.get()));
fpm2.doInitialization();
for (llvm::Module::iterator F = module_->begin(), E = module_->end();
F != E; ++F) {
fpm2.run(*F);
}
fpm2.doFinalization();
// Write bitcode to file
std::string errmsg;
llvm::OwningPtr<llvm::tool_output_file> out_file(
new llvm::tool_output_file(bitcode_filename_.c_str(), errmsg,
llvm::raw_fd_ostream::F_Binary));
if (!errmsg.empty()) {
LOG(ERROR) << "Failed to create bitcode output file: " << errmsg;
return false;
}
llvm::WriteBitcodeToFile(module_, out_file->os());
out_file->keep();
}
// Add optimization pass
llvm::PassManagerBuilder pm_builder;
// TODO: Use inliner after we can do IPO.
pm_builder.Inliner = NULL;
//pm_builder.Inliner = llvm::createFunctionInliningPass();
//pm_builder.Inliner = llvm::createAlwaysInlinerPass();
//pm_builder.Inliner = llvm::createPartialInliningPass();
pm_builder.OptLevel = 3;
pm_builder.DisableSimplifyLibCalls = 1;
pm_builder.DisableUnitAtATime = 1;
pm_builder.populateFunctionPassManager(fpm);
pm_builder.populateModulePassManager(pm);
pm.add(llvm::createStripDeadPrototypesPass());
// Add passes to emit ELF image
{
llvm::formatted_raw_ostream formatted_os(out_stream, false);
// Ask the target to add backend passes as necessary.
if (target_machine->addPassesToEmitFile(pm,
formatted_os,
llvm::TargetMachine::CGFT_ObjectFile,
true)) {
LOG(FATAL) << "Unable to generate ELF for this target";
return false;
}
// Run the per-function optimization
fpm.doInitialization();
for (llvm::Module::iterator F = module_->begin(), E = module_->end();
F != E; ++F) {
fpm.run(*F);
}
fpm.doFinalization();
// Run the code generation passes
pm.run(*module_);
}
return true;
}
bool CompilationUnit::ExtractCodeAndPrelink(const std::string& elf_image) {
if (GetInstructionSet() == kX86) {
compiled_code_.push_back(0xccU);
compiled_code_.push_back(0xccU);
compiled_code_.push_back(0xccU);
compiled_code_.push_back(0xccU);
return true;
}
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