Compiler.cpp revision b7bce7436876884dfd78ec41d147ddbe47e37cbd
1/* 2 * Copyright 2010-2012, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "bcc/Compiler.h" 18 19#include <llvm/Analysis/Passes.h> 20#include <llvm/CodeGen/RegAllocRegistry.h> 21#include <llvm/IR/Module.h> 22#include <llvm/PassManager.h> 23#include <llvm/Support/TargetRegistry.h> 24#include <llvm/Support/raw_ostream.h> 25#include <llvm/IR/DataLayout.h> 26#include <llvm/Target/TargetMachine.h> 27#include <llvm/Transforms/IPO.h> 28#include <llvm/Transforms/IPO/PassManagerBuilder.h> 29#include <llvm/Transforms/Scalar.h> 30 31#include "bcc/Assert.h" 32#include "bcc/Renderscript/RSExecutable.h" 33#include "bcc/Renderscript/RSScript.h" 34#include "bcc/Renderscript/RSTransforms.h" 35#include "bcc/Script.h" 36#include "bcc/Source.h" 37#include "bcc/Support/CompilerConfig.h" 38#include "bcc/Support/Log.h" 39#include "bcc/Support/OutputFile.h" 40#include "bcinfo/MetadataExtractor.h" 41 42#include <string> 43 44using namespace bcc; 45 46const char *Compiler::GetErrorString(enum ErrorCode pErrCode) { 47 switch (pErrCode) { 48 case kSuccess: 49 return "Successfully compiled."; 50 case kInvalidConfigNoTarget: 51 return "Invalid compiler config supplied (getTarget() returns nullptr.) " 52 "(missing call to CompilerConfig::initialize()?)"; 53 case kErrCreateTargetMachine: 54 return "Failed to create llvm::TargetMachine."; 55 case kErrSwitchTargetMachine: 56 return "Failed to switch llvm::TargetMachine."; 57 case kErrNoTargetMachine: 58 return "Failed to compile the script since there's no available " 59 "TargetMachine. (missing call to Compiler::config()?)"; 60 case kErrDataLayoutNoMemory: 61 return "Out of memory when create DataLayout during compilation."; 62 case kErrMaterialization: 63 return "Failed to materialize the module."; 64 case kErrInvalidOutputFileState: 65 return "Supplied output file was invalid (in the error state.)"; 66 case kErrPrepareOutput: 67 return "Failed to prepare file for output."; 68 case kPrepareCodeGenPass: 69 return "Failed to construct pass list for code-generation."; 70 case kErrCustomPasses: 71 return "Error occurred while adding custom passes."; 72 case kErrInvalidSource: 73 return "Error loading input bitcode"; 74 } 75 76 // This assert should never be reached as the compiler verifies that the 77 // above switch coveres all enum values. 78 assert(false && "Unknown error code encountered"); 79 return ""; 80} 81 82//===----------------------------------------------------------------------===// 83// Instance Methods 84//===----------------------------------------------------------------------===// 85Compiler::Compiler() : mTarget(nullptr), mEnableOpt(true) { 86 return; 87} 88 89Compiler::Compiler(const CompilerConfig &pConfig) : mTarget(nullptr), 90 mEnableOpt(true) { 91 const std::string &triple = pConfig.getTriple(); 92 93 enum ErrorCode err = config(pConfig); 94 if (err != kSuccess) { 95 ALOGE("%s (%s, features: %s)", GetErrorString(err), 96 triple.c_str(), pConfig.getFeatureString().c_str()); 97 return; 98 } 99 100 return; 101} 102 103enum Compiler::ErrorCode Compiler::config(const CompilerConfig &pConfig) { 104 if (pConfig.getTarget() == nullptr) { 105 return kInvalidConfigNoTarget; 106 } 107 108 llvm::TargetMachine *new_target = 109 (pConfig.getTarget())->createTargetMachine(pConfig.getTriple(), 110 pConfig.getCPU(), 111 pConfig.getFeatureString(), 112 pConfig.getTargetOptions(), 113 pConfig.getRelocationModel(), 114 pConfig.getCodeModel(), 115 pConfig.getOptimizationLevel()); 116 117 if (new_target == nullptr) { 118 return ((mTarget != nullptr) ? kErrSwitchTargetMachine : 119 kErrCreateTargetMachine); 120 } 121 122 // Replace the old TargetMachine. 123 delete mTarget; 124 mTarget = new_target; 125 126 // Adjust register allocation policy according to the optimization level. 127 // createFastRegisterAllocator: fast but bad quality 128 // createLinearScanRegisterAllocator: not so fast but good quality 129 if ((pConfig.getOptimizationLevel() == llvm::CodeGenOpt::None)) { 130 llvm::RegisterRegAlloc::setDefault(llvm::createFastRegisterAllocator); 131 } else { 132 llvm::RegisterRegAlloc::setDefault(llvm::createGreedyRegisterAllocator); 133 } 134 135 return kSuccess; 136} 137 138Compiler::~Compiler() { 139 delete mTarget; 140} 141 142enum Compiler::ErrorCode Compiler::runPasses(Script &pScript, 143 llvm::raw_ostream &pResult) { 144 // Pass manager for link-time optimization 145 llvm::PassManager passes; 146 147 // Empty MCContext. 148 llvm::MCContext *mc_context = nullptr; 149 150 // Prepare DataLayout target data from Module 151 llvm::DataLayoutPass *data_layout_pass = 152 new (std::nothrow) llvm::DataLayoutPass(*mTarget->getDataLayout()); 153 154 if (data_layout_pass == nullptr) { 155 return kErrDataLayoutNoMemory; 156 } 157 158 // Add DataLayout to the pass manager. 159 passes.add(data_layout_pass); 160 161 // Add our custom passes. 162 if (!addCustomPasses(pScript, passes)) { 163 return kErrCustomPasses; 164 } 165 166 if (mTarget->getOptLevel() == llvm::CodeGenOpt::None) { 167 passes.add(llvm::createGlobalOptimizerPass()); 168 passes.add(llvm::createConstantMergePass()); 169 170 } else { 171 // FIXME: Figure out which passes should be executed. 172 llvm::PassManagerBuilder Builder; 173 Builder.populateLTOPassManager(passes, 174 /*Internalize*/ false, 175 /*RunInliner*/ true); 176 } 177 178 // Add passes to the pass manager to emit machine code through MC layer. 179 if (mTarget->addPassesToEmitMC(passes, mc_context, pResult, 180 /* DisableVerify */false)) { 181 return kPrepareCodeGenPass; 182 } 183 184 // Execute the passes. 185 passes.run(pScript.getSource().getModule()); 186 187 return kSuccess; 188} 189 190enum Compiler::ErrorCode Compiler::compile(Script &pScript, 191 llvm::raw_ostream &pResult, 192 llvm::raw_ostream *IRStream) { 193 llvm::Module &module = pScript.getSource().getModule(); 194 enum ErrorCode err; 195 196 if (mTarget == nullptr) { 197 return kErrNoTargetMachine; 198 } 199 200 const std::string &triple = module.getTargetTriple(); 201 const llvm::DataLayout *dl = getTargetMachine().getDataLayout(); 202 unsigned int pointerSize = dl->getPointerSizeInBits(); 203 if (triple == "armv7-none-linux-gnueabi") { 204 if (pointerSize != 32) { 205 return kErrInvalidSource; 206 } 207 } else if (triple == "aarch64-none-linux-gnueabi") { 208 if (pointerSize != 64) { 209 return kErrInvalidSource; 210 } 211 } else { 212 return kErrInvalidSource; 213 } 214 215 // Materialize the bitcode module. 216 if (module.getMaterializer() != nullptr) { 217 // A module with non-null materializer means that it is a lazy-load module. 218 // Materialize it now via invoking MaterializeAllPermanently(). This 219 // function returns false when the materialization is successful. 220 std::error_code ec = module.materializeAllPermanently(); 221 if (ec) { 222 ALOGE("Failed to materialize the module `%s'! (%s)", 223 module.getModuleIdentifier().c_str(), ec.message().c_str()); 224 return kErrMaterialization; 225 } 226 } 227 228 if ((err = runPasses(pScript, pResult)) != kSuccess) { 229 return err; 230 } 231 232 if (IRStream) { 233 *IRStream << module; 234 } 235 236 return kSuccess; 237} 238 239enum Compiler::ErrorCode Compiler::compile(Script &pScript, 240 OutputFile &pResult, 241 llvm::raw_ostream *IRStream) { 242 // Check the state of the specified output file. 243 if (pResult.hasError()) { 244 return kErrInvalidOutputFileState; 245 } 246 247 // Open the output file decorated in llvm::raw_ostream. 248 llvm::raw_ostream *out = pResult.dup(); 249 if (out == nullptr) { 250 return kErrPrepareOutput; 251 } 252 253 // Delegate the request. 254 enum Compiler::ErrorCode err = compile(pScript, *out, IRStream); 255 256 // Close the output before return. 257 delete out; 258 259 return err; 260} 261 262bool Compiler::addInternalizeSymbolsPass(Script &pScript, llvm::PassManager &pPM) { 263 // Add a pass to internalize the symbols that don't need to have global 264 // visibility. 265 RSScript &script = static_cast<RSScript &>(pScript); 266 llvm::Module &module = script.getSource().getModule(); 267 bcinfo::MetadataExtractor me(&module); 268 if (!me.extract()) { 269 bccAssert(false && "Could not extract metadata for module!"); 270 return false; 271 } 272 273 // The vector contains the symbols that should not be internalized. 274 std::vector<const char *> export_symbols; 275 276 // Special RS functions should always be global symbols. 277 const char **special_functions = RSExecutable::SpecialFunctionNames; 278 while (*special_functions != nullptr) { 279 export_symbols.push_back(*special_functions); 280 special_functions++; 281 } 282 283 // Visibility of symbols appeared in rs_export_var and rs_export_func should 284 // also be preserved. 285 size_t exportVarCount = me.getExportVarCount(); 286 size_t exportFuncCount = me.getExportFuncCount(); 287 size_t exportForEachCount = me.getExportForEachSignatureCount(); 288 const char **exportVarNameList = me.getExportVarNameList(); 289 const char **exportFuncNameList = me.getExportFuncNameList(); 290 const char **exportForEachNameList = me.getExportForEachNameList(); 291 size_t i; 292 293 for (i = 0; i < exportVarCount; ++i) { 294 export_symbols.push_back(exportVarNameList[i]); 295 } 296 297 for (i = 0; i < exportFuncCount; ++i) { 298 export_symbols.push_back(exportFuncNameList[i]); 299 } 300 301 // Expanded foreach functions should not be internalized, too. 302 // expanded_foreach_funcs keeps the .expand version of the kernel names 303 // around until createInternalizePass() is finished making its own 304 // copy of the visible symbols. 305 std::vector<std::string> expanded_foreach_funcs; 306 for (i = 0; i < exportForEachCount; ++i) { 307 expanded_foreach_funcs.push_back( 308 std::string(exportForEachNameList[i]) + ".expand"); 309 } 310 311 for (i = 0; i < exportForEachCount; i++) { 312 export_symbols.push_back(expanded_foreach_funcs[i].c_str()); 313 } 314 315 pPM.add(llvm::createInternalizePass(export_symbols)); 316 317 return true; 318} 319 320bool Compiler::addInvokeHelperPass(llvm::PassManager &pPM) { 321 llvm::Triple arch(getTargetMachine().getTargetTriple()); 322 if (arch.isArch64Bit()) { 323 pPM.add(createRSInvokeHelperPass()); 324 } 325 return true; 326} 327 328bool Compiler::addExpandForEachPass(Script &pScript, llvm::PassManager &pPM) { 329 // Script passed to RSCompiler must be a RSScript. 330 RSScript &script = static_cast<RSScript &>(pScript); 331 332 // Expand ForEach on CPU path to reduce launch overhead. 333 bool pEnableStepOpt = true; 334 pPM.add(createRSForEachExpandPass(pEnableStepOpt)); 335 if (script.getEmbedInfo()) 336 pPM.add(createRSEmbedInfoPass()); 337 338 return true; 339} 340 341bool Compiler::addCustomPasses(Script &pScript, llvm::PassManager &pPM) { 342 if (!addInvokeHelperPass(pPM)) 343 return false; 344 345 if (!addExpandForEachPass(pScript, pPM)) 346 return false; 347 348 if (!addInternalizeSymbolsPass(pScript, pPM)) 349 return false; 350 351 return true; 352} 353