/* * 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 "Config.h" #include #if USE_DISASSEMBLER #include "Disassembler/Disassembler.h" #endif #include "DebugHelper.h" #include "FileHandle.h" #include "Runtime.h" #include "ScriptCompiled.h" #include "Sha1Helper.h" #include "CompilerOption.h" #include "librsloader.h" #include "Transforms/BCCTransforms.h" #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/Passes.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/RegAllocRegistry.h" #include "llvm/CodeGen/SchedulerRegistry.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/SubtargetFeature.h" #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Target/TargetData.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Constants.h" #include "llvm/GlobalValue.h" #include "llvm/Linker.h" #include "llvm/LLVMContext.h" #include "llvm/Module.h" #include "llvm/PassManager.h" #include "llvm/Type.h" #include "llvm/Value.h" #include #include #include #include #include #include #include #include #include #include extern char* gDebugDumpDirectory; namespace bcc { ////////////////////////////////////////////////////////////////////////////// // BCC Compiler Static Variables ////////////////////////////////////////////////////////////////////////////// bool Compiler::GlobalInitialized = false; #if !defined(__HOST__) #define TARGET_TRIPLE_STRING DEFAULT_TARGET_TRIPLE_STRING #else // In host TARGET_TRIPLE_STRING is a variable to allow cross-compilation. #if defined(__cplusplus) extern "C" { #endif char *TARGET_TRIPLE_STRING = (char*)DEFAULT_TARGET_TRIPLE_STRING; #if defined(__cplusplus) }; #endif #endif // Code generation optimization level for the compiler llvm::CodeGenOpt::Level Compiler::CodeGenOptLevel; std::string Compiler::Triple; llvm::Triple::ArchType Compiler::ArchType; std::string Compiler::CPU; std::vector Compiler::Features; ////////////////////////////////////////////////////////////////////////////// // Compiler ////////////////////////////////////////////////////////////////////////////// void Compiler::GlobalInitialization() { if (GlobalInitialized) { return; } #if defined(PROVIDE_ARM_CODEGEN) LLVMInitializeARMAsmPrinter(); LLVMInitializeARMTargetMC(); LLVMInitializeARMTargetInfo(); LLVMInitializeARMTarget(); #endif #if defined(PROVIDE_MIPS_CODEGEN) LLVMInitializeMipsAsmPrinter(); LLVMInitializeMipsTargetMC(); LLVMInitializeMipsTargetInfo(); LLVMInitializeMipsTarget(); #endif #if defined(PROVIDE_X86_CODEGEN) LLVMInitializeX86AsmPrinter(); LLVMInitializeX86TargetMC(); LLVMInitializeX86TargetInfo(); LLVMInitializeX86Target(); #endif #if USE_DISASSEMBLER InitializeDisassembler(); #endif // if (!llvm::llvm_is_multithreaded()) // llvm::llvm_start_multithreaded(); // Set Triple, CPU and Features here Triple = TARGET_TRIPLE_STRING; // Determine ArchType #if defined(__HOST__) { std::string Err; llvm::Target const *Target = llvm::TargetRegistry::lookupTarget(Triple, Err); if (Target != NULL) { ArchType = llvm::Triple::getArchTypeForLLVMName(Target->getName()); } else { ArchType = llvm::Triple::UnknownArch; ALOGE("%s", Err.c_str()); } } #elif defined(DEFAULT_ARM_CODEGEN) ArchType = llvm::Triple::arm; #elif defined(DEFAULT_MIPS_CODEGEN) ArchType = llvm::Triple::mipsel; #elif defined(DEFAULT_X86_CODEGEN) ArchType = llvm::Triple::x86; #elif defined(DEFAULT_X86_64_CODEGEN) ArchType = llvm::Triple::x86_64; #else ArchType = llvm::Triple::UnknownArch; #endif if ((ArchType == llvm::Triple::arm) || (ArchType == llvm::Triple::thumb)) { # if defined(ARCH_ARM_HAVE_VFP) Features.push_back("+vfp3"); # if !defined(ARCH_ARM_HAVE_VFP_D32) Features.push_back("+d16"); # endif # endif # if defined(ARCH_ARM_HAVE_NEON) && !defined(DISABLE_ARCH_ARM_HAVE_NEON) Features.push_back("+neon"); Features.push_back("+neonfp"); # else Features.push_back("-neon"); Features.push_back("-neonfp"); # endif } // Register the scheduler llvm::RegisterScheduler::setDefault(llvm::createDefaultScheduler); // Read in SHA1 checksum of libbcc and libRS. readSHA1(sha1LibBCC_SHA1, sizeof(sha1LibBCC_SHA1), pathLibBCC_SHA1); calcFileSHA1(sha1LibRS, pathLibRS); GlobalInitialized = true; } void Compiler::LLVMErrorHandler(void *UserData, const std::string &Message) { std::string *Error = static_cast(UserData); Error->assign(Message); ALOGE("%s", Message.c_str()); exit(1); } Compiler::Compiler(ScriptCompiled *result) : mpResult(result), mRSExecutable(NULL), mpSymbolLookupFn(NULL), mpSymbolLookupContext(NULL), mModule(NULL), mHasLinked(false) /* Turn off linker */ { llvm::remove_fatal_error_handler(); llvm::install_fatal_error_handler(LLVMErrorHandler, &mError); return; } int Compiler::linkModule(llvm::Module *moduleWith) { if (llvm::Linker::LinkModules(mModule, moduleWith, llvm::Linker::PreserveSource, &mError) != 0) { return hasError(); } // Everything for linking should be settled down here with no error occurs mHasLinked = true; return hasError(); } int Compiler::compile(const CompilerOption &option) { llvm::Target const *Target = NULL; llvm::TargetData *TD = NULL; llvm::TargetMachine *TM = NULL; std::vector ExtraFeatures; std::string FeaturesStr; if (mModule == NULL) // No module was loaded return 0; bcinfo::MetadataExtractor ME(mModule); ME.extract(); size_t VarCount = ME.getExportVarCount(); size_t FuncCount = ME.getExportFuncCount(); size_t ForEachSigCount = ME.getExportForEachSignatureCount(); size_t ObjectSlotCount = ME.getObjectSlotCount(); size_t PragmaCount = ME.getPragmaCount(); std::vector &VarNameList = mpResult->mExportVarsName; std::vector &FuncNameList = mpResult->mExportFuncsName; std::vector &ForEachExpandList = mpResult->mExportForEachName; std::vector ForEachNameList; std::vector ForEachSigList; std::vector ExportSymbols; // Defaults to maximum optimization level from MetadataExtractor. uint32_t OptimizationLevel = ME.getOptimizationLevel(); if (OptimizationLevel == 0) { CodeGenOptLevel = llvm::CodeGenOpt::None; } else if (OptimizationLevel == 1) { CodeGenOptLevel = llvm::CodeGenOpt::Less; } else if (OptimizationLevel == 2) { CodeGenOptLevel = llvm::CodeGenOpt::Default; } else if (OptimizationLevel == 3) { CodeGenOptLevel = llvm::CodeGenOpt::Aggressive; } // not the best place for this, but we need to set the register allocation // policy after we read the optimization_level metadata from the bitcode // Register allocation policy: // createFastRegisterAllocator: fast but bad quality // createLinearScanRegisterAllocator: not so fast but good quality llvm::RegisterRegAlloc::setDefault ((CodeGenOptLevel == llvm::CodeGenOpt::None) ? llvm::createFastRegisterAllocator : llvm::createGreedyRegisterAllocator); // Find LLVM Target Target = llvm::TargetRegistry::lookupTarget(Triple, mError); if (hasError()) goto on_bcc_compile_error; #if defined(ARCH_ARM_HAVE_NEON) // Full-precision means we have to disable NEON if (ME.getRSFloatPrecision() == bcinfo::RS_FP_Full) { ExtraFeatures.push_back("-neon"); ExtraFeatures.push_back("-neonfp"); } #endif if (!CPU.empty() || !Features.empty() || !ExtraFeatures.empty()) { llvm::SubtargetFeatures F; for (std::vector::const_iterator I = Features.begin(), E = Features.end(); I != E; I++) { F.AddFeature(*I); } for (std::vector::const_iterator I = ExtraFeatures.begin(), E = ExtraFeatures.end(); I != E; I++) { F.AddFeature(*I); } FeaturesStr = F.getString(); } // Create LLVM Target Machine TM = Target->createTargetMachine(Triple, CPU, FeaturesStr, option.TargetOpt, option.RelocModelOpt, option.CodeModelOpt); if (TM == NULL) { setError("Failed to create target machine implementation for the" " specified triple '" + Triple + "'"); goto on_bcc_compile_error; } // Get target data from Module TD = new llvm::TargetData(mModule); // Read pragma information from MetadataExtractor if (PragmaCount) { ScriptCompiled::PragmaList &PragmaPairs = mpResult->mPragmas; const char **PragmaKeys = ME.getPragmaKeyList(); const char **PragmaValues = ME.getPragmaValueList(); for (size_t i = 0; i < PragmaCount; i++) { PragmaPairs.push_back(std::make_pair(PragmaKeys[i], PragmaValues[i])); } } if (VarCount) { const char **VarNames = ME.getExportVarNameList(); for (size_t i = 0; i < VarCount; i++) { VarNameList.push_back(VarNames[i]); ExportSymbols.push_back(VarNames[i]); } } if (FuncCount) { const char **FuncNames = ME.getExportFuncNameList(); for (size_t i = 0; i < FuncCount; i++) { FuncNameList.push_back(FuncNames[i]); ExportSymbols.push_back(FuncNames[i]); } } if (ForEachSigCount) { const char **ForEachNames = ME.getExportForEachNameList(); const uint32_t *ForEachSigs = ME.getExportForEachSignatureList(); for (size_t i = 0; i < ForEachSigCount; i++) { std::string Name(ForEachNames[i]); ForEachNameList.push_back(Name); ForEachExpandList.push_back(Name + ".expand"); ForEachSigList.push_back(ForEachSigs[i]); } // Need to wait until ForEachExpandList is fully populated to fill in // exported symbols. for (size_t i = 0; i < ForEachSigCount; i++) { ExportSymbols.push_back(ForEachExpandList[i].c_str()); } } if (ObjectSlotCount) { ScriptCompiled::ObjectSlotList &objectSlotList = mpResult->mObjectSlots; const uint32_t *ObjectSlots = ME.getObjectSlotList(); for (size_t i = 0; i < ObjectSlotCount; i++) { objectSlotList.push_back(ObjectSlots[i]); } } runInternalPasses(ForEachNameList, ForEachSigList); // Perform link-time optimization if we have multiple modules if (mHasLinked) { runLTO(new llvm::TargetData(*TD), ExportSymbols, CodeGenOptLevel); } // Perform code generation if (runMCCodeGen(new llvm::TargetData(*TD), TM) != 0) { goto on_bcc_compile_error; } if (!option.LoadAfterCompile) return 0; // Load the ELF Object mRSExecutable = rsloaderCreateExec((unsigned char *)&*mEmittedELFExecutable.begin(), mEmittedELFExecutable.size(), &resolveSymbolAdapter, this); if (!mRSExecutable) { setError("Fail to load emitted ELF relocatable file"); goto on_bcc_compile_error; } rsloaderUpdateSectionHeaders(mRSExecutable, (unsigned char*) mEmittedELFExecutable.begin()); // Once the ELF object has been loaded, populate the various slots for RS // with the appropriate relocated addresses. if (VarCount) { ScriptCompiled::ExportVarList &VarList = mpResult->mExportVars; for (size_t i = 0; i < VarCount; i++) { VarList.push_back(rsloaderGetSymbolAddress(mRSExecutable, VarNameList[i].c_str())); } } if (FuncCount) { ScriptCompiled::ExportFuncList &FuncList = mpResult->mExportFuncs; for (size_t i = 0; i < FuncCount; i++) { FuncList.push_back(rsloaderGetSymbolAddress(mRSExecutable, FuncNameList[i].c_str())); } } if (ForEachSigCount) { ScriptCompiled::ExportForEachList &ForEachList = mpResult->mExportForEach; for (size_t i = 0; i < ForEachSigCount; i++) { ForEachList.push_back(rsloaderGetSymbolAddress(mRSExecutable, ForEachExpandList[i].c_str())); } } #if DEBUG_MC_DISASSEMBLER { // Get MC codegen emitted function name list size_t func_list_size = rsloaderGetFuncCount(mRSExecutable); std::vector func_list(func_list_size, NULL); rsloaderGetFuncNameList(mRSExecutable, func_list_size, &*func_list.begin()); // Disassemble each function for (size_t i = 0; i < func_list_size; ++i) { void *func = rsloaderGetSymbolAddress(mRSExecutable, func_list[i]); if (func) { size_t size = rsloaderGetSymbolSize(mRSExecutable, func_list[i]); Disassemble(DEBUG_MC_DISASSEMBLER_FILE, Target, TM, func_list[i], (unsigned char const *)func, size); } } } #endif on_bcc_compile_error: // ALOGE("on_bcc_compiler_error"); if (TD) { delete TD; } if (TM) { delete TM; } if (mError.empty()) { return 0; } // ALOGE(getErrorMessage()); return 1; } int Compiler::runMCCodeGen(llvm::TargetData *TD, llvm::TargetMachine *TM) { // Decorate mEmittedELFExecutable with formatted ostream llvm::raw_svector_ostream OutSVOS(mEmittedELFExecutable); // Relax all machine instructions TM->setMCRelaxAll(/* RelaxAll= */ true); // Create MC code generation pass manager llvm::PassManager MCCodeGenPasses; // Add TargetData to MC code generation pass manager MCCodeGenPasses.add(TD); // Add MC code generation passes to pass manager llvm::MCContext *Ctx = NULL; if (TM->addPassesToEmitMC(MCCodeGenPasses, Ctx, OutSVOS, false)) { setError("Fail to add passes to emit file"); return 1; } MCCodeGenPasses.run(*mModule); OutSVOS.flush(); return 0; } int Compiler::runInternalPasses(std::vector& Names, std::vector& Signatures) { llvm::PassManager BCCPasses; // Expand ForEach on CPU path to reduce launch overhead. BCCPasses.add(createForEachExpandPass(Names, Signatures)); BCCPasses.run(*mModule); return 0; } int Compiler::runLTO(llvm::TargetData *TD, std::vector& ExportSymbols, llvm::CodeGenOpt::Level OptimizationLevel) { // Note: ExportSymbols is a workaround for getting all exported variable, // function, and kernel names. // We should refine it soon. // TODO(logan): Remove this after we have finished the // bccMarkExternalSymbol API. // root(), init(), and .rs.dtor() are born to be exported ExportSymbols.push_back("root"); ExportSymbols.push_back("init"); ExportSymbols.push_back(".rs.dtor"); // User-defined exporting symbols std::vector const &UserDefinedExternalSymbols = mpResult->getUserDefinedExternalSymbols(); std::copy(UserDefinedExternalSymbols.begin(), UserDefinedExternalSymbols.end(), std::back_inserter(ExportSymbols)); llvm::PassManager LTOPasses; // Add TargetData to LTO passes LTOPasses.add(TD); // We now create passes list performing LTO. These are copied from // (including comments) llvm::createStandardLTOPasses(). // 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 (OptimizationLevel != llvm::CodeGenOpt::None) { // Internalize all other symbols not listed in ExportSymbols LTOPasses.add(llvm::createInternalizePass(ExportSymbols)); // 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. LTOPasses.add(llvm::createIPSCCPPass()); // Now that we internalized some globals, see if we can hack on them! LTOPasses.add(llvm::createGlobalOptimizerPass()); // Linking modules together can lead to duplicated global constants, only // keep one copy of each constant... LTOPasses.add(llvm::createConstantMergePass()); // Remove unused arguments from functions... LTOPasses.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. LTOPasses.add(llvm::createInstructionCombiningPass()); // Inline small functions LTOPasses.add(llvm::createFunctionInliningPass()); // Remove dead EH info. LTOPasses.add(llvm::createPruneEHPass()); // Internalize the globals again after inlining LTOPasses.add(llvm::createGlobalOptimizerPass()); // Remove dead functions. LTOPasses.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. LTOPasses.add(llvm::createArgumentPromotionPass()); // The IPO passes may leave cruft around. Clean up after them. LTOPasses.add(llvm::createInstructionCombiningPass()); LTOPasses.add(llvm::createJumpThreadingPass()); // Break up allocas LTOPasses.add(llvm::createScalarReplAggregatesPass()); // Run a few AA driven optimizations here and now, to cleanup the code. LTOPasses.add(llvm::createFunctionAttrsPass()); // Add nocapture. LTOPasses.add(llvm::createGlobalsModRefPass()); // IP alias analysis. // Hoist loop invariants. LTOPasses.add(llvm::createLICMPass()); // Remove redundancies. LTOPasses.add(llvm::createGVNPass()); // Remove dead memcpys. LTOPasses.add(llvm::createMemCpyOptPass()); // Nuke dead stores. LTOPasses.add(llvm::createDeadStoreEliminationPass()); // Cleanup and simplify the code after the scalar optimizations. LTOPasses.add(llvm::createInstructionCombiningPass()); LTOPasses.add(llvm::createJumpThreadingPass()); // Delete basic blocks, which optimization passes may have killed. LTOPasses.add(llvm::createCFGSimplificationPass()); // Now that we have optimized the program, discard unreachable functions. LTOPasses.add(llvm::createGlobalDCEPass()); } else { LTOPasses.add(llvm::createInternalizePass(ExportSymbols)); LTOPasses.add(llvm::createGlobalOptimizerPass()); LTOPasses.add(llvm::createConstantMergePass()); } LTOPasses.run(*mModule); #if ANDROID_ENGINEERING_BUILD if (0 != gDebugDumpDirectory) { std::string errs; std::string Filename(gDebugDumpDirectory); Filename += "/post-lto-module.ll"; llvm::raw_fd_ostream FS(Filename.c_str(), errs); mModule->print(FS, 0); FS.close(); } #endif return 0; } void *Compiler::getSymbolAddress(char const *name) { return rsloaderGetSymbolAddress(mRSExecutable, name); } void *Compiler::resolveSymbolAdapter(void *context, char const *name) { Compiler *self = reinterpret_cast(context); if (void *Addr = FindRuntimeFunction(name)) { return Addr; } if (self->mpSymbolLookupFn) { if (void *Addr = self->mpSymbolLookupFn(self->mpSymbolLookupContext, name)) { return Addr; } } ALOGE("Unable to resolve symbol: %s\n", name); return NULL; } Compiler::~Compiler() { rsloaderDisposeExec(mRSExecutable); // llvm::llvm_shutdown(); } } // namespace bcc