diff options
Diffstat (limited to 'compiler/llvm/llvm_compilation_unit.cc')
-rw-r--r-- | compiler/llvm/llvm_compilation_unit.cc | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/compiler/llvm/llvm_compilation_unit.cc b/compiler/llvm/llvm_compilation_unit.cc new file mode 100644 index 0000000..dfb5724 --- /dev/null +++ b/compiler/llvm/llvm_compilation_unit.cc @@ -0,0 +1,343 @@ +/* + * 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. + */ + +// TODO: TargetLibraryInfo is included before sys/... because on Android bionic does #define tricks like: +// +// #define stat64 stat +// #define fstat64 fstat +// #define lstat64 lstat +// +// which causes grief. bionic probably should not do that. +#include <llvm/Target/TargetLibraryInfo.h> + +#include "llvm_compilation_unit.h" + +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <string> + +#include <llvm/ADT/OwningPtr.h> +#include <llvm/ADT/StringSet.h> +#include <llvm/ADT/Triple.h> +#include <llvm/Analysis/CallGraph.h> +#include <llvm/Analysis/CallGraphSCCPass.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/CodeGen/MachineFrameInfo.h> +#include <llvm/CodeGen/MachineFunction.h> +#include <llvm/CodeGen/MachineFunctionPass.h> +#include <llvm/DebugInfo.h> +#include <llvm/IR/DataLayout.h> +#include <llvm/IR/DerivedTypes.h> +#include <llvm/IR/LLVMContext.h> +#include <llvm/IR/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/TargetMachine.h> +#include <llvm/Transforms/IPO.h> +#include <llvm/Transforms/IPO/PassManagerBuilder.h> +#include <llvm/Transforms/Scalar.h> + +#include "base/logging.h" +#include "base/unix_file/fd_file.h" +#include "compiled_method.h" +#include "compiler_llvm.h" +#include "instruction_set.h" +#include "ir_builder.h" +#include "os.h" +#include "runtime_support_builder_arm.h" +#include "runtime_support_builder_thumb2.h" +#include "runtime_support_builder_x86.h" +#include "utils_llvm.h" + +namespace art { +namespace llvm { + +::llvm::FunctionPass* +CreateGBCExpanderPass(const IntrinsicHelper& intrinsic_helper, IRBuilder& irb, + CompilerDriver* compiler, const DexCompilationUnit* dex_compilation_unit); + +::llvm::Module* makeLLVMModuleContents(::llvm::Module* module); + + +LlvmCompilationUnit::LlvmCompilationUnit(const CompilerLLVM* compiler_llvm, size_t cunit_id) + : compiler_llvm_(compiler_llvm), cunit_id_(cunit_id) { + driver_ = NULL; + dex_compilation_unit_ = NULL; + llvm_info_.reset(new LLVMInfo()); + context_.reset(llvm_info_->GetLLVMContext()); + module_ = llvm_info_->GetLLVMModule(); + + // Include the runtime function declaration + makeLLVMModuleContents(module_); + + + intrinsic_helper_.reset(new IntrinsicHelper(*context_, *module_)); + + // Create IRBuilder + irb_.reset(new IRBuilder(*context_, *module_, *intrinsic_helper_)); + + // 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()); +} + + +LlvmCompilationUnit::~LlvmCompilationUnit() { + ::llvm::LLVMContext* llvm_context = context_.release(); // Managed by llvm_info_ + CHECK(llvm_context != NULL); +} + + +InstructionSet LlvmCompilationUnit::GetInstructionSet() const { + return compiler_llvm_->GetInstructionSet(); +} + + +static std::string DumpDirectory() { + if (kIsTargetBuild) { + return GetDalvikCacheOrDie(GetAndroidData()); + } + return "/tmp"; +} + +void LlvmCompilationUnit::DumpBitcodeToFile() { + std::string bitcode; + DumpBitcodeToString(bitcode); + std::string filename(StringPrintf("%s/Art%u.bc", DumpDirectory().c_str(), cunit_id_)); + UniquePtr<File> output(OS::OpenFile(filename.c_str(), true)); + output->WriteFully(bitcode.data(), bitcode.size()); + LOG(INFO) << ".bc file written successfully: " << filename; +} + +void LlvmCompilationUnit::DumpBitcodeToString(std::string& str_buffer) { + ::llvm::raw_string_ostream str_os(str_buffer); + ::llvm::WriteBitcodeToFile(module_, str_os); +} + +bool LlvmCompilationUnit::Materialize() { + + const bool kDumpBitcode = false; + if (kDumpBitcode) { + // Dump the bitcode for debugging + DumpBitcodeToFile(); + } + + // Compile and prelink ::llvm::Module + if (!MaterializeToString(elf_object_)) { + LOG(ERROR) << "Failed to materialize compilation unit " << cunit_id_; + return false; + } + + const bool kDumpELF = false; + if (kDumpELF) { + // Dump the ELF image for debugging + std::string filename(StringPrintf("%s/Art%u.o", DumpDirectory().c_str(), cunit_id_)); + UniquePtr<File> output(OS::OpenFile(filename.c_str(), true)); + output->WriteFully(elf_object_.data(), elf_object_.size()); + LOG(INFO) << ".o file written successfully: " << filename; + } + + return true; +} + + +bool LlvmCompilationUnit::MaterializeToString(std::string& str_buffer) { + ::llvm::raw_string_ostream str_os(str_buffer); + return MaterializeToRawOStream(str_os); +} + + +bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_stream) { + // Lookup the LLVM target + std::string target_triple; + std::string target_cpu; + std::string target_attr; + CompilerDriver::InstructionSetToLLVMTarget(GetInstructionSet(), target_triple, target_cpu, target_attr); + + 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::DataLayout* data_layout = target_machine->getDataLayout(); + + // PassManager for code generation passes + ::llvm::PassManager pm; + pm.add(new ::llvm::DataLayout(*data_layout)); + + // FunctionPassManager for optimization pass + ::llvm::FunctionPassManager fpm(module_); + fpm.add(new ::llvm::DataLayout(*data_layout)); + + if (bitcode_filename_.empty()) { + // If we don't need write the bitcode to file, add the AddSuspendCheckToLoopLatchPass to the + // regular FunctionPass. + fpm.add(CreateGBCExpanderPass(*llvm_info_->GetIntrinsicHelper(), *irb_.get(), + driver_, dex_compilation_unit_)); + } else { + ::llvm::FunctionPassManager fpm2(module_); + fpm2.add(CreateGBCExpanderPass(*llvm_info_->GetIntrinsicHelper(), *irb_.get(), + driver_, dex_compilation_unit_)); + 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; +} + +// 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 LlvmCompilationUnit::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 llvm +} // namespace art |