/* * 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 "compiler_llvm.h" #include "backend_options.h" #include "class_linker.h" #include "compilation_unit.h" #include "compiled_method.h" #include "compiler.h" #include "dex_cache.h" #include "elf_image.h" #include "elf_loader.h" #include "ir_builder.h" #include "jni_compiler.h" #include "method_compiler.h" #include "oat_compilation_unit.h" #include "oat_file.h" #include "stl_util.h" #include "stub_compiler.h" #include #include #include #include #include namespace llvm { extern bool TimePassesIsEnabled; } namespace { pthread_once_t llvm_initialized = PTHREAD_ONCE_INIT; void InitializeLLVM() { // Initialize LLVM internal data structure for multithreading llvm::llvm_start_multithreaded(); // NOTE: Uncomment following line to show the time consumption of LLVM passes //llvm::TimePassesIsEnabled = true; // Initialize LLVM target-specific options. art::compiler_llvm::InitialBackendOptions(); // Initialize LLVM target, MC subsystem, asm printer, and asm parser llvm::InitializeAllTargets(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmPrinters(); llvm::InitializeAllAsmParsers(); // TODO: Maybe we don't have to initialize "all" targets. // Initialize LLVM optimization passes llvm::PassRegistry ®istry = *llvm::PassRegistry::getPassRegistry(); llvm::initializeCore(registry); llvm::initializeScalarOpts(registry); llvm::initializeIPO(registry); llvm::initializeAnalysis(registry); llvm::initializeIPA(registry); llvm::initializeTransformUtils(registry); llvm::initializeInstCombine(registry); llvm::initializeInstrumentation(registry); llvm::initializeTarget(registry); } // The Guard to Shutdown LLVM // llvm::llvm_shutdown_obj llvm_guard; // TODO: We are commenting out this line because this will cause SEGV from // time to time. // Two reasons: (1) the order of the destruction of static objects, or // (2) dlopen/dlclose side-effect on static objects. } // anonymous namespace namespace art { namespace compiler_llvm { llvm::Module* makeLLVMModuleContents(llvm::Module* module); CompilerLLVM::CompilerLLVM(Compiler* compiler, InstructionSet insn_set) : compiler_(compiler), compiler_lock_("llvm_compiler_lock"), insn_set_(insn_set), curr_cunit_(NULL) { // Initialize LLVM libraries pthread_once(&llvm_initialized, InitializeLLVM); } CompilerLLVM::~CompilerLLVM() { STLDeleteElements(&cunits_); } void CompilerLLVM::EnsureCompilationUnit() { compiler_lock_.AssertHeld(); if (curr_cunit_ != NULL) { return; } // Allocate compilation unit size_t cunit_idx = cunits_.size(); curr_cunit_ = new CompilationUnit(insn_set_, cunit_idx); // Register compilation unit cunits_.push_back(curr_cunit_); } void CompilerLLVM::MaterializeRemainder() { compiler_lock_.Lock(); // Localize CompilationUnit* cunit = curr_cunit_; // Reset the curr_cuit_ curr_cunit_ = NULL; compiler_lock_.Unlock(); if (cunit != NULL) { Materialize(cunit); } } void CompilerLLVM::MaterializeIfThresholdReached() { compiler_lock_.Lock(); // Localize CompilationUnit* cunit = curr_cunit_; if (curr_cunit_ != NULL && curr_cunit_->IsMaterializeThresholdReached()) { // Delete the compilation unit curr_cunit_ = NULL; } else { // Reset cunit such that Materialize() won't be invoked cunit = NULL; } compiler_lock_.Unlock(); if (cunit != NULL) { Materialize(cunit); } } void CompilerLLVM::Materialize(CompilationUnit* cunit) { DCHECK(cunit != NULL); DCHECK(!cunit->IsMaterialized()); // Write bitcode to file when filename is set if (IsBitcodeFileNameAvailable()) { const size_t cunit_idx = cunits_.size(); cunit->SetBitcodeFileName( StringPrintf("%s-%zu", bitcode_filename_.c_str(), cunit_idx)); } // Materialize the llvm::Module into ELF object file cunit->Materialize(compiler_->GetThreadCount()); // Load ELF image when automatic ELF loading is enabled if (IsAutoElfLoadingEnabled()) { LoadElfFromCompilationUnit(cunit); } } void CompilerLLVM::EnableAutoElfLoading() { MutexLock GUARD(compiler_lock_); if (IsAutoElfLoadingEnabled()) { // If there is an existing ELF loader, then do nothing. // Because the existing ELF loader may have returned some code address // already. If we replace the existing ELF loader with // elf_loader_.reset(...), then it is possible to have some dangling // pointer. return; } // Create ELF loader and load the materialized CompilationUnit elf_loader_.reset(new ElfLoader()); for (size_t i = 0; i < cunits_.size(); ++i) { if (cunits_[i]->IsMaterialized()) { LoadElfFromCompilationUnit(cunits_[i]); } } } void CompilerLLVM::LoadElfFromCompilationUnit(const CompilationUnit* cunit) { MutexLock GUARD(compiler_lock_); DCHECK(cunit->IsMaterialized()) << cunit->GetElfIndex(); if (!elf_loader_->LoadElfAt(cunit->GetElfIndex(), cunit->GetElfImage(), OatFile::kRelocAll)) { LOG(ERROR) << "Failed to load ELF from compilation unit " << cunit->GetElfIndex(); } } const void* CompilerLLVM::GetMethodCodeAddr(const CompiledMethod* cm) const { return elf_loader_->GetMethodCodeAddr(cm->GetElfIndex(), cm->GetElfFuncIndex()); } const Method::InvokeStub* CompilerLLVM:: GetMethodInvokeStubAddr(const CompiledInvokeStub* cm) const { return elf_loader_->GetMethodInvokeStubAddr(cm->GetElfIndex(), cm->GetElfFuncIndex()); } std::vector CompilerLLVM::GetElfImages() const { std::vector result; for (size_t i = 0; i < cunits_.size(); ++i) { result.push_back(cunits_[i]->GetElfImage()); } return result; } CompiledMethod* CompilerLLVM:: CompileDexMethod(OatCompilationUnit* oat_compilation_unit) { MutexLock GUARD(compiler_lock_); EnsureCompilationUnit(); MutexLock GUARD_CUNIT(curr_cunit_->cunit_lock_); UniquePtr method_compiler( new MethodCompiler(curr_cunit_, compiler_, oat_compilation_unit)); return method_compiler->Compile(); } CompiledMethod* CompilerLLVM:: CompileNativeMethod(OatCompilationUnit* oat_compilation_unit) { MutexLock GUARD(compiler_lock_); EnsureCompilationUnit(); MutexLock GUARD_CUNIT(curr_cunit_->cunit_lock_); UniquePtr jni_compiler( new JniCompiler(curr_cunit_, *compiler_, oat_compilation_unit)); return jni_compiler->Compile(); } CompiledInvokeStub* CompilerLLVM::CreateInvokeStub(bool is_static, char const *shorty) { MutexLock GUARD(compiler_lock_); EnsureCompilationUnit(); MutexLock GUARD_CUNIT(curr_cunit_->cunit_lock_); UniquePtr stub_compiler( new StubCompiler(curr_cunit_, *compiler_)); return stub_compiler->CreateInvokeStub(is_static, shorty); } CompiledInvokeStub* CompilerLLVM::CreateProxyStub(char const *shorty) { MutexLock GUARD(compiler_lock_); EnsureCompilationUnit(); MutexLock GUARD_CUNIT(curr_cunit_->cunit_lock_); UniquePtr stub_compiler( new StubCompiler(curr_cunit_, *compiler_)); return stub_compiler->CreateProxyStub(shorty); } } // namespace compiler_llvm } // namespace art inline static art::compiler_llvm::CompilerLLVM* ContextOf(art::Compiler& compiler) { void *compiler_context = compiler.GetCompilerContext(); CHECK(compiler_context != NULL); return reinterpret_cast(compiler_context); } inline static const art::compiler_llvm::CompilerLLVM* ContextOf(const art::Compiler& compiler) { void *compiler_context = compiler.GetCompilerContext(); CHECK(compiler_context != NULL); return reinterpret_cast(compiler_context); } extern "C" void ArtInitCompilerContext(art::Compiler& compiler) { CHECK(compiler.GetCompilerContext() == NULL); art::compiler_llvm::CompilerLLVM* compiler_llvm = new art::compiler_llvm::CompilerLLVM(&compiler, compiler.GetInstructionSet()); compiler.SetCompilerContext(compiler_llvm); } extern "C" art::CompiledMethod* ArtCompileMethod(art::Compiler& compiler, const art::DexFile::CodeItem* code_item, uint32_t access_flags, uint32_t method_idx, const art::ClassLoader* class_loader, const art::DexFile& dex_file) { art::ClassLinker *class_linker = art::Runtime::Current()->GetClassLinker(); art::DexCache *dex_cache = class_linker->FindDexCache(dex_file); art::OatCompilationUnit oat_compilation_unit( class_loader, class_linker, dex_file, *dex_cache, code_item, method_idx, access_flags); art::compiler_llvm::CompilerLLVM* compiler_llvm = ContextOf(compiler); art::CompiledMethod* result = compiler_llvm->CompileDexMethod(&oat_compilation_unit); compiler_llvm->MaterializeIfThresholdReached(); return result; } extern "C" art::CompiledMethod* ArtJniCompileMethod(art::Compiler& compiler, uint32_t access_flags, uint32_t method_idx, const art::DexFile& dex_file) { art::ClassLinker *class_linker = art::Runtime::Current()->GetClassLinker(); art::DexCache *dex_cache = class_linker->FindDexCache(dex_file); art::OatCompilationUnit oat_compilation_unit( NULL, class_linker, dex_file, *dex_cache, NULL, method_idx, access_flags); art::compiler_llvm::CompilerLLVM* compiler_llvm = ContextOf(compiler); art::CompiledMethod* result = compiler_llvm->CompileNativeMethod(&oat_compilation_unit); compiler_llvm->MaterializeIfThresholdReached(); return result; } extern "C" art::CompiledInvokeStub* ArtCreateInvokeStub(art::Compiler& compiler, bool is_static, const char* shorty, uint32_t shorty_len) { art::compiler_llvm::CompilerLLVM* compiler_llvm = ContextOf(compiler); art::CompiledInvokeStub* result = compiler_llvm->CreateInvokeStub(is_static, shorty); compiler_llvm->MaterializeIfThresholdReached(); return result; } extern "C" art::CompiledInvokeStub* ArtCreateProxyStub(art::Compiler& compiler, const char* shorty, uint32_t shorty_len) { art::compiler_llvm::CompilerLLVM* compiler_llvm = ContextOf(compiler); art::CompiledInvokeStub* result = compiler_llvm->CreateProxyStub(shorty); compiler_llvm->MaterializeIfThresholdReached(); return result; } extern "C" void compilerLLVMSetBitcodeFileName(art::Compiler& compiler, std::string const& filename) { ContextOf(compiler)->SetBitcodeFileName(filename); } extern "C" void compilerLLVMMaterializeRemainder(art::Compiler& compiler) { ContextOf(compiler)->MaterializeRemainder(); } extern "C" void compilerLLVMEnableAutoElfLoading(art::Compiler& compiler) { art::compiler_llvm::CompilerLLVM* compiler_llvm = reinterpret_cast(compiler.GetCompilerContext()); return compiler_llvm->EnableAutoElfLoading(); } extern "C" const void* compilerLLVMGetMethodCodeAddr(const art::Compiler& compiler, const art::CompiledMethod* cm, const art::Method*) { const art::compiler_llvm::CompilerLLVM* compiler_llvm = reinterpret_cast(compiler.GetCompilerContext()); return compiler_llvm->GetMethodCodeAddr(cm); } extern "C" const art::Method::InvokeStub* compilerLLVMGetMethodInvokeStubAddr(const art::Compiler& compiler, const art::CompiledInvokeStub* cm, const art::Method*) { const art::compiler_llvm::CompilerLLVM* compiler_llvm = reinterpret_cast(compiler.GetCompilerContext()); return compiler_llvm->GetMethodInvokeStubAddr(cm); } extern "C" std::vector compilerLLVMGetElfImages(const art::Compiler& compiler) { return ContextOf(compiler)->GetElfImages(); } extern "C" void compilerLLVMDispose(art::Compiler& compiler) { delete ContextOf(compiler); }