summaryrefslogtreecommitdiffstats
path: root/compiler/llvm/llvm_compilation_unit.cc
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/llvm/llvm_compilation_unit.cc')
-rw-r--r--compiler/llvm/llvm_compilation_unit.cc343
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