/* * 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 "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_x86.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 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 { 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.addRequiredID(llvm::LoopSimplifyID); AU.addPreserved(); AU.addPreserved(); AU.addPreservedID(llvm::LoopSimplifyID); AU.addPreserved(); AU.addPreservedID(llvm::BreakCriticalEdgesID); } virtual bool runOnLoop(llvm::Loop *loop, llvm::LPPassManager &lpm) { CHECK_EQ(loop->getNumBackEdges(), 1U) << "Loop must be simplified!"; llvm::BasicBlock* bb = loop->getLoopLatch(); CHECK_NE(bb, static_cast(NULL)) << "A single loop latch must exist."; irb_->SetInsertPoint(bb->getTerminator()); using namespace art::compiler_llvm::runtime_support; llvm::Value* runtime_func = irb_->GetRuntime(TestSuspend); llvm::Value* thread_object_addr = irb_->CreateCall(irb_->GetRuntime(GetCurrentThread)); irb_->CreateCall(runtime_func, thread_object_addr); return true; } private: art::compiler_llvm::IRBuilder* irb_; }; char AddSuspendCheckToLoopLatchPass::ID = 0; llvm::RegisterPass 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 { 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()), mem_usage_(0), num_elf_funcs_(0) { // Create the module and include the runtime function declaration module_ = new llvm::Module("art", *context_); makeLLVMModuleContents(module_); // Create IRBuilder irb_.reset(new IRBuilder(*context_, *module_)); // We always need a switch case, so just use a normal function. switch(insn_set_) { default: runtime_support_.reset(new RuntimeSupportBuilder(*context_, *module_, *irb_)); break; case kArm: case kThumb2: runtime_support_.reset(new RuntimeSupportBuilderARM(*context_, *module_, *irb_)); break; case kX86: runtime_support_.reset(new RuntimeSupportBuilderX86(*context_, *module_, *irb_)); break; } runtime_support_->OptimizeRuntimeSupport(); irb_->SetRuntimeSupport(runtime_support_.get()); } CompilationUnit::~CompilationUnit() { } bool CompilationUnit::Materialize(size_t thread_count) { MutexLock GUARD(cunit_lock_); if (thread_count == 1) { llvm::raw_string_ostream str_os(elf_image_); bool success = MaterializeToFile(str_os, insn_set_); LOG(INFO) << "Compilation Unit: " << elf_idx_ << (success ? " (done)" : " (failed)"); // Free the resources context_.reset(NULL); irb_.reset(NULL); module_ = NULL; return success; } // Prepare the pipe between parent process and child process int pipe_fd[2]; if (pipe(pipe_fd) == -1) { PLOG(FATAL) << "Failed to create pipe for CompilerWorker"; return false; } // Fork a process to do the compilation pid_t pid = fork(); if (pid < 0) { close(pipe_fd[0]); close(pipe_fd[1]); PLOG(FATAL) << "Failed to fork a process to do the compilation"; return false; } else if (pid == 0) { // Child process // Close the unused pipe read end close(pipe_fd[0]); // Change process groups, so we don't get ripped by ProcessManager setpgid(0, 0); llvm::raw_fd_ostream fd_os(pipe_fd[1], /* shouldClose */true); // TODO: Should use exec* family instead of invoking a function. // Forward our compilation request to bcc. exit(static_cast(!MaterializeToFile(fd_os, insn_set_))); } else { // Parent process // Close the unused pipe write end close(pipe_fd[1]); // Free the resources context_.reset(NULL); irb_.reset(NULL); module_ = NULL; // Read the result out from the pipe read end (until failure) const size_t buf_size = 1024; std::vector buf(buf_size); while (true) { // Read from the pipe ssize_t nread = read(pipe_fd[0], &*buf.begin(), buf_size); if (nread < 0) { if (errno == EAGAIN || errno == EINTR) { continue; } else { LOG(ERROR) << "Unexpected error during IPC: " << strerror(errno); } } // Append to the end of the elf_image_ elf_image_.append(buf.begin(), buf.begin() + nread); if (nread < static_cast(buf_size)) { // EOF reached! break; } } close(pipe_fd[0]); // Wait for child to finish int status; pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); if (got_pid != pid) { PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid; elf_image_.clear(); return false; } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { LOG(ERROR) << "Failed to compile the bitcode: " << WEXITSTATUS(status); elf_image_.clear(); return false; } LOG(INFO) << "Compilation Unit: " << elf_idx_ << " (done)"; return true; } } void CompilationUnit::RegisterCompiledMethod(const llvm::Function* func, CompiledMethod* compiled_method) { MutexLock GUARD(cunit_lock_); compiled_methods_map_.Put(func, compiled_method); } void CompilationUnit::UpdateFrameSizeInBytes(const llvm::Function* func, size_t frame_size_in_bytes) { MutexLock GUARD(cunit_lock_); SafeMap::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(); } } } bool CompilationUnit::MaterializeToFile(llvm::raw_ostream& out_stream, InstructionSet insn_set) { // Initialize the LLVM first llvm::InitializeAllTargets(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmPrinters(); llvm::InitializeAllAsmParsers(); // Lookup the LLVM target char const* target_triple = NULL; char const* target_attr = NULL; switch (insn_set) { case kThumb2: target_triple = "thumb-none-linux-gnueabi"; target_attr = "+thumb2,+neon,+neonfp,+vfp3"; break; case kArm: target_triple = "armv7-none-linux-gnueabi"; // TODO: Fix for Xoom. target_attr = "+v7,+neon,+neonfp,+vfp3"; 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; llvm::Target const* 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 = true; // Create the llvm::TargetMachine llvm::OwningPtr target_machine( target->createTargetMachine(target_triple, "", target_attr, target_options, llvm::Reloc::Static, llvm::CodeModel::Small, llvm::CodeGenOpt::Less)); CHECK(target_machine.get() != NULL) << "Failed to create target machine"; // Add target data llvm::TargetData const* 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. fpm.add(new ::AddSuspendCheckToLoopLatchPass(irb_.get())); } else { // Run AddSuspendCheckToLoopLatchPass before we write the bitcode to file. llvm::FunctionPassManager fpm2(module_); 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 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; //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.populateModulePassManager(pm); pm_builder.populateFunctionPassManager(fpm); // 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; } // 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(); F != E; ++F) { fpm.run(*F); } fpm.doFinalization(); // Run the code generation passes pm.run(*module_); } return true; } } // namespace compiler_llvm } // namespace art