| /* |
| * 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 |