blob: 6c8188ef6f1cf0fc73d4541f6dc99560168c67bf [file] [log] [blame]
/*
* 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 <llvm/LinkAllPasses.h>
#include <llvm/LinkAllVMCore.h>
#include <llvm/Support/ManagedStatic.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Threading.h>
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 &registry = *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<ElfImage> CompilerLLVM::GetElfImages() const {
std::vector<ElfImage> 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<MethodCompiler> 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<JniCompiler> 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<StubCompiler> 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<StubCompiler> 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<art::compiler_llvm::CompilerLLVM*>(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<const art::compiler_llvm::CompilerLLVM*>(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<art::compiler_llvm::CompilerLLVM*>(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<const art::compiler_llvm::CompilerLLVM*>(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<const art::compiler_llvm::CompilerLLVM*>(compiler.GetCompilerContext());
return compiler_llvm->GetMethodInvokeStubAddr(cm);
}
extern "C" std::vector<art::ElfImage> compilerLLVMGetElfImages(const art::Compiler& compiler) {
return ContextOf(compiler)->GetElfImages();
}
extern "C" void compilerLLVMDispose(art::Compiler& compiler) {
delete ContextOf(compiler);
}