| /* |
| * Copyright 2010-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.h" |
| |
| #include <llvm/Analysis/Passes.h> |
| #include <llvm/CodeGen/RegAllocRegistry.h> |
| #include <llvm/Module.h> |
| #include <llvm/PassManager.h> |
| #include <llvm/Support/TargetRegistry.h> |
| #include <llvm/Support/raw_ostream.h> |
| #include <llvm/Target/TargetData.h> |
| #include <llvm/Target/TargetMachine.h> |
| #include <llvm/Transforms/IPO.h> |
| #include <llvm/Transforms/Scalar.h> |
| |
| #include "CompilerConfig.h" |
| #include "DebugHelper.h" |
| #include "OutputFile.h" |
| #include "Script.h" |
| #include "Source.h" |
| |
| using namespace bcc; |
| |
| const char *Compiler::GetErrorString(enum ErrorCode pErrCode) { |
| static const char *ErrorString[] = { |
| /* kSuccess */ |
| "Successfully compiled.", |
| /* kInvalidConfigNoTarget */ |
| "Invalid compiler config supplied (getTarget() returns NULL.) " |
| "(missing call to CompilerConfig::initialize()?)", |
| /* kErrCreateTargetMachine */ |
| "Failed to create llvm::TargetMachine.", |
| /* kErrSwitchTargetMachine */ |
| "Failed to switch llvm::TargetMachine.", |
| /* kErrNoTargetMachine */ |
| "Failed to compile the script since there's no available TargetMachine." |
| " (missing call to Compiler::config()?)", |
| /* kErrTargetDataNoMemory */ |
| "Out of memory when create TargetData during compilation.", |
| /* kErrMaterialization */ |
| "Failed to materialize the module.", |
| /* kErrInvalidOutputFileState */ |
| "Supplied output file was invalid (in the error state.)", |
| /* kErrPrepareOutput */ |
| "Failed to prepare file for output.", |
| /* kPrepareCodeGenPass */ |
| "Failed to construct pass list for code-generation.", |
| |
| /* kErrHookBeforeAddLTOPasses */ |
| "Error occurred during beforeAddLTOPasses() in subclass.", |
| /* kErrHookAfterAddLTOPasses */ |
| "Error occurred during afterAddLTOPasses() in subclass.", |
| /* kErrHookBeforeExecuteLTOPasses */ |
| "Error occurred during beforeExecuteLTOPasses() in subclass.", |
| /* kErrHookAfterExecuteLTOPasses */ |
| "Error occurred during afterExecuteLTOPasses() in subclass.", |
| |
| /* kErrHookBeforeAddCodeGenPasses */ |
| "Error occurred during beforeAddCodeGenPasses() in subclass.", |
| /* kErrHookAfterAddCodeGenPasses */ |
| "Error occurred during afterAddCodeGenPasses() in subclass.", |
| /* kErrHookBeforeExecuteCodeGenPasses */ |
| "Error occurred during beforeExecuteCodeGenPasses() in subclass.", |
| /* kErrHookAfterExecuteCodeGenPasses */ |
| "Error occurred during afterExecuteCodeGenPasses() in subclass.", |
| |
| /* kMaxErrorCode */ |
| "(Unknown error code)" |
| }; |
| |
| if (pErrCode > kMaxErrorCode) { |
| pErrCode = kMaxErrorCode; |
| } |
| |
| return ErrorString[ static_cast<size_t>(pErrCode) ]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Instance Methods |
| //===----------------------------------------------------------------------===// |
| Compiler::Compiler() : mTarget(NULL), mEnableLTO(true) { |
| return; |
| } |
| |
| Compiler::Compiler(const CompilerConfig &pConfig) : mTarget(NULL), |
| mEnableLTO(true) { |
| const std::string &triple = pConfig.getTriple(); |
| |
| enum ErrorCode err = config(pConfig); |
| if (err != kSuccess) { |
| ALOGE("%s (%s, features: %s)", GetErrorString(err), |
| triple.c_str(), pConfig.getFeatureString().c_str()); |
| return; |
| } |
| |
| return; |
| } |
| |
| enum Compiler::ErrorCode Compiler::config(const CompilerConfig &pConfig) { |
| if (pConfig.getTarget() == NULL) { |
| return kInvalidConfigNoTarget; |
| } |
| |
| llvm::TargetMachine *new_target = |
| (pConfig.getTarget())->createTargetMachine(pConfig.getTriple(), |
| pConfig.getCPU(), |
| pConfig.getFeatureString(), |
| pConfig.getTargetOptions(), |
| pConfig.getRelocationModel(), |
| pConfig.getCodeModel(), |
| pConfig.getOptimizationLevel()); |
| |
| if (new_target == NULL) { |
| return ((mTarget != NULL) ? kErrSwitchTargetMachine : |
| kErrCreateTargetMachine); |
| } |
| |
| // Replace the old TargetMachine. |
| delete mTarget; |
| mTarget = new_target; |
| |
| // Adjust register allocation policy according to the optimization level. |
| // createFastRegisterAllocator: fast but bad quality |
| // createLinearScanRegisterAllocator: not so fast but good quality |
| if ((pConfig.getOptimizationLevel() == llvm::CodeGenOpt::None)) { |
| llvm::RegisterRegAlloc::setDefault(llvm::createFastRegisterAllocator); |
| } else { |
| llvm::RegisterRegAlloc::setDefault(llvm::createGreedyRegisterAllocator); |
| } |
| |
| // Relax all machine instructions. |
| mTarget->setMCRelaxAll(true); |
| |
| return kSuccess; |
| } |
| |
| Compiler::~Compiler() { |
| delete mTarget; |
| } |
| |
| enum Compiler::ErrorCode Compiler::runLTO(Script &pScript) { |
| llvm::TargetData *target_data = NULL; |
| |
| // Pass manager for link-time optimization |
| llvm::PassManager lto_passes; |
| |
| // Prepare TargetData target data from Module |
| target_data = new (std::nothrow) llvm::TargetData(*mTarget->getTargetData()); |
| if (target_data == NULL) { |
| return kErrTargetDataNoMemory; |
| } |
| |
| // Add TargetData to the pass manager. |
| lto_passes.add(target_data); |
| |
| // Invokde "beforeAddLTOPasses" before adding the first pass. |
| if (!beforeAddLTOPasses(pScript, lto_passes)) { |
| return kErrHookBeforeAddLTOPasses; |
| } |
| |
| // We now create passes list performing LTO. These are copied from |
| // (including comments) llvm::PassManagerBuilder::populateLTOPassManager(). |
| // Only a subset of these LTO passes are enabled in optimization level 0 as |
| // they interfere with interactive debugging. |
| // |
| // FIXME: Figure out which passes (if any) makes sense for levels 1 and 2. |
| //if ( != llvm::CodeGenOpt::None) { |
| if (mTarget->getOptLevel() == llvm::CodeGenOpt::None) { |
| lto_passes.add(llvm::createGlobalOptimizerPass()); |
| lto_passes.add(llvm::createConstantMergePass()); |
| } else { |
| // Propagate constants at call sites into the functions they call. This |
| // opens opportunities for globalopt (and inlining) by substituting |
| // function pointers passed as arguments to direct uses of functions. |
| lto_passes.add(llvm::createIPSCCPPass()); |
| |
| // Now that we internalized some globals, see if we can hack on them! |
| lto_passes.add(llvm::createGlobalOptimizerPass()); |
| |
| // Linking modules together can lead to duplicated global constants, only |
| // keep one copy of each constant... |
| lto_passes.add(llvm::createConstantMergePass()); |
| |
| // Remove unused arguments from functions... |
| lto_passes.add(llvm::createDeadArgEliminationPass()); |
| |
| // Reduce the code after globalopt and ipsccp. Both can open up |
| // significant simplification opportunities, and both can propagate |
| // functions through function pointers. When this happens, we often have |
| // to resolve varargs calls, etc, so let instcombine do this. |
| lto_passes.add(llvm::createInstructionCombiningPass()); |
| |
| // Inline small functions |
| lto_passes.add(llvm::createFunctionInliningPass()); |
| |
| // Remove dead EH info. |
| lto_passes.add(llvm::createPruneEHPass()); |
| |
| // Internalize the globals again after inlining |
| lto_passes.add(llvm::createGlobalOptimizerPass()); |
| |
| // Remove dead functions. |
| lto_passes.add(llvm::createGlobalDCEPass()); |
| |
| // If we didn't decide to inline a function, check to see if we can |
| // transform it to pass arguments by value instead of by reference. |
| lto_passes.add(llvm::createArgumentPromotionPass()); |
| |
| // The IPO passes may leave cruft around. Clean up after them. |
| lto_passes.add(llvm::createInstructionCombiningPass()); |
| lto_passes.add(llvm::createJumpThreadingPass()); |
| |
| // Break up allocas |
| lto_passes.add(llvm::createScalarReplAggregatesPass()); |
| |
| // Run a few AA driven optimizations here and now, to cleanup the code. |
| lto_passes.add(llvm::createFunctionAttrsPass()); // Add nocapture. |
| lto_passes.add(llvm::createGlobalsModRefPass()); // IP alias analysis. |
| |
| // Hoist loop invariants. |
| lto_passes.add(llvm::createLICMPass()); |
| |
| // Remove redundancies. |
| lto_passes.add(llvm::createGVNPass()); |
| |
| // Remove dead memcpys. |
| lto_passes.add(llvm::createMemCpyOptPass()); |
| |
| // Nuke dead stores. |
| lto_passes.add(llvm::createDeadStoreEliminationPass()); |
| |
| // Cleanup and simplify the code after the scalar optimizations. |
| lto_passes.add(llvm::createInstructionCombiningPass()); |
| |
| lto_passes.add(llvm::createJumpThreadingPass()); |
| |
| // Delete basic blocks, which optimization passes may have killed. |
| lto_passes.add(llvm::createCFGSimplificationPass()); |
| |
| // Now that we have optimized the program, discard unreachable functions. |
| lto_passes.add(llvm::createGlobalDCEPass()); |
| } |
| |
| // Invokde "afterAddLTOPasses" after pass manager finished its |
| // construction. |
| if (!afterAddLTOPasses(pScript, lto_passes)) { |
| return kErrHookAfterAddLTOPasses; |
| } |
| |
| // Invokde "beforeExecuteLTOPasses" before executing the passes. |
| if (!beforeExecuteLTOPasses(pScript, lto_passes)) { |
| return kErrHookBeforeExecuteLTOPasses; |
| } |
| |
| lto_passes.run(pScript.getSource().getModule()); |
| |
| // Invokde "afterExecuteLTOPasses" before returning. |
| if (!afterExecuteLTOPasses(pScript)) { |
| return kErrHookAfterExecuteLTOPasses; |
| } |
| |
| return kSuccess; |
| } |
| |
| enum Compiler::ErrorCode Compiler::runCodeGen(Script &pScript, |
| llvm::raw_ostream &pResult) { |
| llvm::TargetData *target_data; |
| llvm::MCContext *mc_context = NULL; |
| |
| // Create pass manager for MC code generation. |
| llvm::PassManager codegen_passes; |
| |
| // Prepare TargetData target data from Module |
| target_data = new (std::nothrow) llvm::TargetData(*mTarget->getTargetData()); |
| if (target_data == NULL) { |
| return kErrTargetDataNoMemory; |
| } |
| |
| // Add TargetData to the pass manager. |
| codegen_passes.add(target_data); |
| |
| // Invokde "beforeAddCodeGenPasses" before adding the first pass. |
| if (!beforeAddCodeGenPasses(pScript, codegen_passes)) { |
| return kErrHookBeforeAddCodeGenPasses; |
| } |
| |
| // Add passes to the pass manager to emit machine code through MC layer. |
| if (mTarget->addPassesToEmitMC(codegen_passes, mc_context, pResult, |
| /* DisableVerify */false)) { |
| return kPrepareCodeGenPass; |
| } |
| |
| // Invokde "afterAddCodeGenPasses" after pass manager finished its |
| // construction. |
| if (!afterAddCodeGenPasses(pScript, codegen_passes)) { |
| return kErrHookAfterAddCodeGenPasses; |
| } |
| |
| // Invokde "beforeExecuteCodeGenPasses" before executing the passes. |
| if (!beforeExecuteCodeGenPasses(pScript, codegen_passes)) { |
| return kErrHookBeforeExecuteCodeGenPasses; |
| } |
| |
| // Execute the pass. |
| codegen_passes.run(pScript.getSource().getModule()); |
| |
| // Invokde "afterExecuteCodeGenPasses" before returning. |
| if (!afterExecuteCodeGenPasses(pScript)) { |
| return kErrHookAfterExecuteCodeGenPasses; |
| } |
| |
| return kSuccess; |
| } |
| |
| enum Compiler::ErrorCode Compiler::compile(Script &pScript, |
| llvm::raw_ostream &pResult) { |
| llvm::Module &module = pScript.getSource().getModule(); |
| enum ErrorCode err; |
| |
| if (mTarget == NULL) { |
| return kErrNoTargetMachine; |
| } |
| |
| // Materialize the bitcode module. |
| if (module.getMaterializer() != NULL) { |
| std::string error; |
| // A module with non-null materializer means that it is a lazy-load module. |
| // Materialize it now via invoking MaterializeAllPermanently(). This |
| // function returns false when the materialization is successful. |
| if (module.MaterializeAllPermanently(&error)) { |
| ALOGE("Failed to materialize the module `%s'! (%s)", |
| module.getModuleIdentifier().c_str(), error.c_str()); |
| return kErrMaterialization; |
| } |
| } |
| |
| if (mEnableLTO && ((err = runLTO(pScript)) != kSuccess)) { |
| return err; |
| } |
| |
| if ((err = runCodeGen(pScript, pResult)) != kSuccess) { |
| return err; |
| } |
| |
| return kSuccess; |
| } |
| |
| enum Compiler::ErrorCode Compiler::compile(Script &pScript, |
| OutputFile &pResult) { |
| // Check the state of the specified output file. |
| if (pResult.hasError()) { |
| return kErrInvalidOutputFileState; |
| } |
| |
| // Open the output file decorated in llvm::raw_ostream. |
| llvm::raw_ostream *out = pResult.dup(); |
| if (out == NULL) { |
| return kErrPrepareOutput; |
| } |
| |
| // Delegate the request. |
| enum Compiler::ErrorCode err = compile(pScript, *out); |
| |
| // Close the output before return. |
| delete out; |
| |
| return err; |
| } |