1 /* 2 * Copyright 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/Renderscript/RSCompilerDriver.h" 18 19 #include "llvm/IR/AssemblyAnnotationWriter.h" 20 #include <llvm/IR/Module.h> 21 #include "llvm/Linker/Linker.h" 22 #include <llvm/Support/CommandLine.h> 23 #include <llvm/Support/Path.h> 24 #include <llvm/Support/raw_ostream.h> 25 26 #include "bcinfo/BitcodeWrapper.h" 27 #include "bcc/Assert.h" 28 #include "bcinfo/MetadataExtractor.h" 29 #include "bcc/BCCContext.h" 30 #include "bcc/Compiler.h" 31 #include "bcc/Config/Config.h" 32 #include "bcc/Renderscript/RSScript.h" 33 #include "bcc/Renderscript/RSScriptGroupFusion.h" 34 #include "bcc/Support/CompilerConfig.h" 35 #include "bcc/Source.h" 36 #include "bcc/Support/FileMutex.h" 37 #include "bcc/Support/Log.h" 38 #include "bcc/Support/InputFile.h" 39 #include "bcc/Support/Initialization.h" 40 #include "bcc/Support/OutputFile.h" 41 42 #include <sstream> 43 #include <string> 44 45 #ifdef HAVE_ANDROID_OS 46 #include <cutils/properties.h> 47 #endif 48 #include <utils/StopWatch.h> 49 50 using namespace bcc; 51 52 RSCompilerDriver::RSCompilerDriver(bool pUseCompilerRT) : 53 mConfig(nullptr), mCompiler(), mDebugContext(false), 54 mLinkRuntimeCallback(nullptr), mEnableGlobalMerge(true), 55 mEmbedGlobalInfo(false), mEmbedGlobalInfoSkipConstant(false) { 56 init::Initialize(); 57 } 58 59 RSCompilerDriver::~RSCompilerDriver() { 60 delete mConfig; 61 } 62 63 64 #if defined(PROVIDE_ARM_CODEGEN) 65 extern llvm::cl::opt<bool> EnableGlobalMerge; 66 #endif 67 68 bool RSCompilerDriver::setupConfig(const RSScript &pScript) { 69 bool changed = false; 70 71 const llvm::CodeGenOpt::Level script_opt_level = 72 static_cast<llvm::CodeGenOpt::Level>(pScript.getOptimizationLevel()); 73 74 #if defined(PROVIDE_ARM_CODEGEN) 75 EnableGlobalMerge = mEnableGlobalMerge; 76 #endif 77 78 if (mConfig != nullptr) { 79 // Renderscript bitcode may have their optimization flag configuration 80 // different than the previous run of RS compilation. 81 if (mConfig->getOptimizationLevel() != script_opt_level) { 82 mConfig->setOptimizationLevel(script_opt_level); 83 changed = true; 84 } 85 } else { 86 // Haven't run the compiler ever. 87 mConfig = new (std::nothrow) CompilerConfig(DEFAULT_TARGET_TRIPLE_STRING); 88 if (mConfig == nullptr) { 89 // Return false since mConfig remains NULL and out-of-memory. 90 return false; 91 } 92 mConfig->setOptimizationLevel(script_opt_level); 93 changed = true; 94 } 95 96 #if defined(PROVIDE_ARM_CODEGEN) 97 bcinfo::MetadataExtractor me(&pScript.getSource().getModule()); 98 if (!me.extract()) { 99 assert("Could not extract RS pragma metadata for module!"); 100 } 101 102 bool script_full_prec = (me.getRSFloatPrecision() == bcinfo::RS_FP_Full); 103 if (mConfig->getFullPrecision() != script_full_prec) { 104 mConfig->setFullPrecision(script_full_prec); 105 changed = true; 106 } 107 #endif 108 109 return changed; 110 } 111 112 Compiler::ErrorCode RSCompilerDriver::compileScript(RSScript& pScript, const char* pScriptName, 113 const char* pOutputPath, 114 const char* pRuntimePath, 115 const char* pBuildChecksum, 116 bool pDumpIR) { 117 // embed build checksum metadata into the source 118 if (pBuildChecksum != nullptr && strlen(pBuildChecksum) > 0) { 119 pScript.getSource().addBuildChecksumMetadata(pBuildChecksum); 120 } 121 122 // Verify that the only external functions in pScript are Renderscript 123 // functions. Fail if verification returns an error. 124 if (mCompiler.screenGlobalFunctions(pScript) != Compiler::kSuccess) { 125 return Compiler::kErrInvalidSource; 126 } 127 128 //===--------------------------------------------------------------------===// 129 // Link RS script with Renderscript runtime. 130 //===--------------------------------------------------------------------===// 131 if (!RSScript::LinkRuntime(pScript, pRuntimePath)) { 132 ALOGE("Failed to link script '%s' with Renderscript runtime %s!", 133 pScriptName, pRuntimePath); 134 return Compiler::kErrInvalidSource; 135 } 136 137 { 138 // FIXME(srhines): Windows compilation can't use locking like this, but 139 // we also don't need to worry about concurrent writers of the same file. 140 #ifndef USE_MINGW 141 //===------------------------------------------------------------------===// 142 // Acquire the write lock for writing output object file. 143 //===------------------------------------------------------------------===// 144 FileMutex<FileBase::kWriteLock> write_output_mutex(pOutputPath); 145 146 if (write_output_mutex.hasError() || !write_output_mutex.lock()) { 147 ALOGE("Unable to acquire the lock for writing %s! (%s)", 148 pOutputPath, write_output_mutex.getErrorMessage().c_str()); 149 return Compiler::kErrInvalidSource; 150 } 151 #endif 152 153 // Open the output file for write. 154 OutputFile output_file(pOutputPath, 155 FileBase::kTruncate | FileBase::kBinary); 156 157 if (output_file.hasError()) { 158 ALOGE("Unable to open %s for write! (%s)", pOutputPath, 159 output_file.getErrorMessage().c_str()); 160 return Compiler::kErrInvalidSource; 161 } 162 163 // Setup the config to the compiler. 164 bool compiler_need_reconfigure = setupConfig(pScript); 165 166 if (mConfig == nullptr) { 167 ALOGE("Failed to setup config for RS compiler to compile %s!", 168 pOutputPath); 169 return Compiler::kErrInvalidSource; 170 } 171 172 if (compiler_need_reconfigure) { 173 Compiler::ErrorCode err = mCompiler.config(*mConfig); 174 if (err != Compiler::kSuccess) { 175 ALOGE("Failed to config the RS compiler for %s! (%s)",pOutputPath, 176 Compiler::GetErrorString(err)); 177 return Compiler::kErrInvalidSource; 178 } 179 } 180 181 OutputFile *ir_file = nullptr; 182 llvm::raw_fd_ostream *IRStream = nullptr; 183 if (pDumpIR) { 184 std::string path(pOutputPath); 185 path.append(".ll"); 186 ir_file = new OutputFile(path.c_str(), FileBase::kTruncate); 187 IRStream = ir_file->dup(); 188 } 189 190 // Run the compiler. 191 Compiler::ErrorCode compile_result = 192 mCompiler.compile(pScript, output_file, IRStream); 193 194 if (ir_file) { 195 ir_file->close(); 196 delete ir_file; 197 } 198 199 if (compile_result != Compiler::kSuccess) { 200 ALOGE("Unable to compile the source to file %s! (%s)", pOutputPath, 201 Compiler::GetErrorString(compile_result)); 202 return Compiler::kErrInvalidSource; 203 } 204 } 205 206 return Compiler::kSuccess; 207 } 208 209 bool RSCompilerDriver::build(BCCContext &pContext, 210 const char *pCacheDir, 211 const char *pResName, 212 const char *pBitcode, 213 size_t pBitcodeSize, 214 const char *pBuildChecksum, 215 const char *pRuntimePath, 216 RSLinkRuntimeCallback pLinkRuntimeCallback, 217 bool pDumpIR) { 218 // android::StopWatch build_time("bcc: RSCompilerDriver::build time"); 219 //===--------------------------------------------------------------------===// 220 // Check parameters. 221 //===--------------------------------------------------------------------===// 222 if ((pCacheDir == nullptr) || (pResName == nullptr)) { 223 ALOGE("Invalid parameter passed to RSCompilerDriver::build()! (cache dir: " 224 "%s, resource name: %s)", ((pCacheDir) ? pCacheDir : "(null)"), 225 ((pResName) ? pResName : "(null)")); 226 return false; 227 } 228 229 if ((pBitcode == nullptr) || (pBitcodeSize <= 0)) { 230 ALOGE("No bitcode supplied! (bitcode: %p, size of bitcode: %u)", 231 pBitcode, static_cast<unsigned>(pBitcodeSize)); 232 return false; 233 } 234 235 //===--------------------------------------------------------------------===// 236 // Construct output path. 237 // {pCacheDir}/{pResName}.o 238 //===--------------------------------------------------------------------===// 239 llvm::SmallString<80> output_path(pCacheDir); 240 llvm::sys::path::append(output_path, pResName); 241 llvm::sys::path::replace_extension(output_path, ".o"); 242 243 //===--------------------------------------------------------------------===// 244 // Load the bitcode and create script. 245 //===--------------------------------------------------------------------===// 246 Source *source = Source::CreateFromBuffer(pContext, pResName, 247 pBitcode, pBitcodeSize); 248 if (source == nullptr) { 249 return false; 250 } 251 252 RSScript script(*source); 253 if (pLinkRuntimeCallback) { 254 setLinkRuntimeCallback(pLinkRuntimeCallback); 255 } 256 257 script.setLinkRuntimeCallback(getLinkRuntimeCallback()); 258 259 script.setEmbedGlobalInfo(mEmbedGlobalInfo); 260 script.setEmbedGlobalInfoSkipConstant(mEmbedGlobalInfoSkipConstant); 261 262 // Read information from bitcode wrapper. 263 bcinfo::BitcodeWrapper wrapper(pBitcode, pBitcodeSize); 264 script.setCompilerVersion(wrapper.getCompilerVersion()); 265 script.setOptimizationLevel(static_cast<RSScript::OptimizationLevel>( 266 wrapper.getOptimizationLevel())); 267 268 //===--------------------------------------------------------------------===// 269 // Compile the script 270 //===--------------------------------------------------------------------===// 271 Compiler::ErrorCode status = compileScript(script, pResName, 272 output_path.c_str(), 273 pRuntimePath, 274 pBuildChecksum, 275 pDumpIR); 276 277 return status == Compiler::kSuccess; 278 } 279 280 bool RSCompilerDriver::buildScriptGroup( 281 BCCContext& Context, const char* pOutputFilepath, const char* pRuntimePath, 282 const char* pRuntimeRelaxedPath, bool dumpIR, const char* buildChecksum, 283 const std::vector<Source*>& sources, 284 const std::list<std::list<std::pair<int, int>>>& toFuse, 285 const std::list<std::string>& fused, 286 const std::list<std::list<std::pair<int, int>>>& invokes, 287 const std::list<std::string>& invokeBatchNames) { 288 // --------------------------------------------------------------------------- 289 // Link all input modules into a single module 290 // --------------------------------------------------------------------------- 291 292 llvm::LLVMContext& context = Context.getLLVMContext(); 293 llvm::Module module("Merged Script Group", context); 294 295 llvm::Linker linker(&module); 296 for (Source* source : sources) { 297 if (linker.linkInModule(&source->getModule())) { 298 ALOGE("Linking for module in source failed."); 299 return false; 300 } 301 } 302 303 // --------------------------------------------------------------------------- 304 // Create fused kernels 305 // --------------------------------------------------------------------------- 306 307 auto inputIter = toFuse.begin(); 308 for (const std::string& nameOfFused : fused) { 309 auto inputKernels = *inputIter++; 310 std::vector<Source*> sourcesToFuse; 311 std::vector<int> slots; 312 313 for (auto p : inputKernels) { 314 sourcesToFuse.push_back(sources[p.first]); 315 slots.push_back(p.second); 316 } 317 318 if (!fuseKernels(Context, sourcesToFuse, slots, nameOfFused, &module)) { 319 return false; 320 } 321 } 322 323 // --------------------------------------------------------------------------- 324 // Rename invokes 325 // --------------------------------------------------------------------------- 326 327 auto invokeIter = invokes.begin(); 328 for (const std::string& newName : invokeBatchNames) { 329 auto inputInvoke = *invokeIter++; 330 auto p = inputInvoke.front(); 331 Source* source = sources[p.first]; 332 int slot = p.second; 333 334 if (!renameInvoke(Context, source, slot, newName, &module)) { 335 return false; 336 } 337 } 338 339 // --------------------------------------------------------------------------- 340 // Compile the new module with fused kernels 341 // --------------------------------------------------------------------------- 342 343 const std::unique_ptr<Source> source( 344 Source::CreateFromModule(Context, pOutputFilepath, module, true)); 345 RSScript script(*source); 346 347 // Embed the info string directly in the ELF 348 script.setEmbedInfo(true); 349 script.setOptimizationLevel(RSScript::kOptLvl3); 350 script.setEmbedGlobalInfo(mEmbedGlobalInfo); 351 script.setEmbedGlobalInfoSkipConstant(mEmbedGlobalInfoSkipConstant); 352 353 llvm::SmallString<80> output_path(pOutputFilepath); 354 llvm::sys::path::replace_extension(output_path, ".o"); 355 356 // Pick the right runtime lib 357 const char* coreLibPath = pRuntimePath; 358 if (strcmp(pRuntimeRelaxedPath, "")) { 359 bcinfo::MetadataExtractor me(&module); 360 me.extract(); 361 if (me.getRSFloatPrecision() == bcinfo::RS_FP_Relaxed) { 362 coreLibPath = pRuntimeRelaxedPath; 363 } 364 } 365 366 compileScript(script, pOutputFilepath, output_path.c_str(), coreLibPath, 367 buildChecksum, dumpIR); 368 369 return true; 370 } 371 372 bool RSCompilerDriver::buildForCompatLib(RSScript &pScript, const char *pOut, 373 const char *pBuildChecksum, 374 const char *pRuntimePath, 375 bool pDumpIR) { 376 // Embed the info string directly in the ELF, since this path is for an 377 // offline (host) compilation. 378 pScript.setEmbedInfo(true); 379 380 pScript.setEmbedGlobalInfo(mEmbedGlobalInfo); 381 pScript.setEmbedGlobalInfoSkipConstant(mEmbedGlobalInfoSkipConstant); 382 pScript.setLinkRuntimeCallback(getLinkRuntimeCallback()); 383 384 Compiler::ErrorCode status = compileScript(pScript, pOut, pOut, pRuntimePath, 385 pBuildChecksum, pDumpIR); 386 if (status != Compiler::kSuccess) { 387 return false; 388 } 389 390 return true; 391 } 392