RSCompilerDriver.cpp revision 6da4e253a513feef3405759fef6d0760828808ca
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 "bcc/BCCContext.h" 29#include "bcc/Compiler.h" 30#include "bcc/Config/Config.h" 31#include "bcc/Renderscript/RSInfo.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/Sha1Util.h" 41#include "bcc/Support/OutputFile.h" 42 43#include <sstream> 44#include <string> 45 46#ifdef HAVE_ANDROID_OS 47#include <cutils/properties.h> 48#endif 49#include <utils/StopWatch.h> 50 51using namespace bcc; 52 53// Get the build fingerprint of the Android device we are running on. 54static std::string getBuildFingerPrint() { 55#ifdef HAVE_ANDROID_OS 56 char fingerprint[PROPERTY_VALUE_MAX]; 57 property_get("ro.build.fingerprint", fingerprint, ""); 58 return fingerprint; 59#else 60 return "HostBuild"; 61#endif 62} 63 64RSCompilerDriver::RSCompilerDriver(bool pUseCompilerRT) : 65 mConfig(nullptr), mCompiler(), mDebugContext(false), 66 mLinkRuntimeCallback(nullptr), mEnableGlobalMerge(true) { 67 init::Initialize(); 68} 69 70RSCompilerDriver::~RSCompilerDriver() { 71 delete mConfig; 72} 73 74 75#if defined(PROVIDE_ARM_CODEGEN) 76extern llvm::cl::opt<bool> EnableGlobalMerge; 77#endif 78 79bool RSCompilerDriver::setupConfig(const RSScript &pScript) { 80 bool changed = false; 81 82 const llvm::CodeGenOpt::Level script_opt_level = 83 static_cast<llvm::CodeGenOpt::Level>(pScript.getOptimizationLevel()); 84 85#if defined(PROVIDE_ARM_CODEGEN) 86 EnableGlobalMerge = mEnableGlobalMerge; 87#endif 88 89 if (mConfig != nullptr) { 90 // Renderscript bitcode may have their optimization flag configuration 91 // different than the previous run of RS compilation. 92 if (mConfig->getOptimizationLevel() != script_opt_level) { 93 mConfig->setOptimizationLevel(script_opt_level); 94 changed = true; 95 } 96 } else { 97 // Haven't run the compiler ever. 98 mConfig = new (std::nothrow) CompilerConfig(DEFAULT_TARGET_TRIPLE_STRING); 99 if (mConfig == nullptr) { 100 // Return false since mConfig remains NULL and out-of-memory. 101 return false; 102 } 103 mConfig->setOptimizationLevel(script_opt_level); 104 changed = true; 105 } 106 107#if defined(PROVIDE_ARM_CODEGEN) 108 assert((pScript.getInfo() != nullptr) && "NULL RS info!"); 109 bool script_full_prec = (pScript.getInfo()->getFloatPrecisionRequirement() == 110 RSInfo::FP_Full); 111 if (mConfig->getFullPrecision() != script_full_prec) { 112 mConfig->setFullPrecision(script_full_prec); 113 changed = true; 114 } 115#endif 116 117 return changed; 118} 119 120Compiler::ErrorCode RSCompilerDriver::compileScript(RSScript& pScript, const char* pScriptName, 121 const char* pOutputPath, 122 const char* pRuntimePath, 123 const RSInfo::DependencyHashTy& pSourceHash, 124 const char* compileCommandLineToEmbed, 125 const char* pBuildChecksum, 126 bool saveInfoFile, bool pDumpIR) { 127 // android::StopWatch compile_time("bcc: RSCompilerDriver::compileScript time"); 128 129 // embed build checksum metadata into the source 130 if (pBuildChecksum != nullptr && strlen(pBuildChecksum) > 0) { 131 pScript.getSource().addBuildChecksumMetadata(pBuildChecksum); 132 } 133 134 RSInfo *info = nullptr; 135 136 //===--------------------------------------------------------------------===// 137 // Extract RS-specific information from source bitcode. 138 //===--------------------------------------------------------------------===// 139 // RS info may contains configuration (such as #optimization_level) to the 140 // compiler therefore it should be extracted before compilation. 141 info = RSInfo::ExtractFromSource(pScript.getSource(), pSourceHash, compileCommandLineToEmbed, 142 getBuildFingerPrint().c_str()); 143 if (info == nullptr) { 144 return Compiler::kErrInvalidSource; 145 } 146 147 //===--------------------------------------------------------------------===// 148 // Associate script with its info 149 //===--------------------------------------------------------------------===// 150 // This is required since RS compiler may need information in the info file 151 // to do some transformation (e.g., expand foreach-able function.) 152 pScript.setInfo(info); 153 154 //===--------------------------------------------------------------------===// 155 // Link RS script with Renderscript runtime. 156 //===--------------------------------------------------------------------===// 157 if (!RSScript::LinkRuntime(pScript, pRuntimePath)) { 158 ALOGE("Failed to link script '%s' with Renderscript runtime %s!", 159 pScriptName, pRuntimePath); 160 return Compiler::kErrInvalidSource; 161 } 162 163 { 164 // FIXME(srhines): Windows compilation can't use locking like this, but 165 // we also don't need to worry about concurrent writers of the same file. 166#ifndef USE_MINGW 167 //===------------------------------------------------------------------===// 168 // Acquire the write lock for writing output object file. 169 //===------------------------------------------------------------------===// 170 FileMutex<FileBase::kWriteLock> write_output_mutex(pOutputPath); 171 172 if (write_output_mutex.hasError() || !write_output_mutex.lock()) { 173 ALOGE("Unable to acquire the lock for writing %s! (%s)", 174 pOutputPath, write_output_mutex.getErrorMessage().c_str()); 175 return Compiler::kErrInvalidSource; 176 } 177#endif 178 179 // Open the output file for write. 180 OutputFile output_file(pOutputPath, 181 FileBase::kTruncate | FileBase::kBinary); 182 183 if (output_file.hasError()) { 184 ALOGE("Unable to open %s for write! (%s)", pOutputPath, 185 output_file.getErrorMessage().c_str()); 186 return Compiler::kErrInvalidSource; 187 } 188 189 // Setup the config to the compiler. 190 bool compiler_need_reconfigure = setupConfig(pScript); 191 192 if (mConfig == nullptr) { 193 ALOGE("Failed to setup config for RS compiler to compile %s!", 194 pOutputPath); 195 return Compiler::kErrInvalidSource; 196 } 197 198 if (compiler_need_reconfigure) { 199 Compiler::ErrorCode err = mCompiler.config(*mConfig); 200 if (err != Compiler::kSuccess) { 201 ALOGE("Failed to config the RS compiler for %s! (%s)",pOutputPath, 202 Compiler::GetErrorString(err)); 203 return Compiler::kErrInvalidSource; 204 } 205 } 206 207 OutputFile *ir_file = nullptr; 208 llvm::raw_fd_ostream *IRStream = nullptr; 209 if (pDumpIR) { 210 std::string path(pOutputPath); 211 path.append(".ll"); 212 ir_file = new OutputFile(path.c_str(), FileBase::kTruncate); 213 IRStream = ir_file->dup(); 214 } 215 216 // Run the compiler. 217 Compiler::ErrorCode compile_result = 218 mCompiler.compile(pScript, output_file, IRStream); 219 220 if (ir_file) { 221 ir_file->close(); 222 delete ir_file; 223 } 224 225 if (compile_result != Compiler::kSuccess) { 226 ALOGE("Unable to compile the source to file %s! (%s)", pOutputPath, 227 Compiler::GetErrorString(compile_result)); 228 return Compiler::kErrInvalidSource; 229 } 230 } 231 232 if (saveInfoFile) { 233 std::string info_path = RSInfo::GetPath(pOutputPath); 234 OutputFile info_file(info_path.c_str(), FileBase::kTruncate); 235 236 if (info_file.hasError()) { 237 ALOGE("Failed to open the info file %s for write! (%s)", 238 info_path.c_str(), info_file.getErrorMessage().c_str()); 239 return Compiler::kErrInvalidSource; 240 } 241 242 FileMutex<FileBase::kWriteLock> write_info_mutex(info_path.c_str()); 243 if (write_info_mutex.hasError() || !write_info_mutex.lock()) { 244 ALOGE("Unable to acquire the lock for writing %s! (%s)", 245 info_path.c_str(), write_info_mutex.getErrorMessage().c_str()); 246 return Compiler::kErrInvalidSource; 247 } 248 249 // Perform the write. 250 if (!info->write(info_file)) { 251 ALOGE("Failed to sync the RS info file %s!", info_path.c_str()); 252 return Compiler::kErrInvalidSource; 253 } 254 } 255 256 return Compiler::kSuccess; 257} 258 259bool RSCompilerDriver::build(BCCContext &pContext, 260 const char *pCacheDir, 261 const char *pResName, 262 const char *pBitcode, 263 size_t pBitcodeSize, 264 const char *commandLine, 265 const char *pBuildChecksum, 266 const char *pRuntimePath, 267 RSLinkRuntimeCallback pLinkRuntimeCallback, 268 bool pDumpIR) { 269 // android::StopWatch build_time("bcc: RSCompilerDriver::build time"); 270 //===--------------------------------------------------------------------===// 271 // Check parameters. 272 //===--------------------------------------------------------------------===// 273 if ((pCacheDir == nullptr) || (pResName == nullptr)) { 274 ALOGE("Invalid parameter passed to RSCompilerDriver::build()! (cache dir: " 275 "%s, resource name: %s)", ((pCacheDir) ? pCacheDir : "(null)"), 276 ((pResName) ? pResName : "(null)")); 277 return false; 278 } 279 280 if ((pBitcode == nullptr) || (pBitcodeSize <= 0)) { 281 ALOGE("No bitcode supplied! (bitcode: %p, size of bitcode: %u)", 282 pBitcode, static_cast<unsigned>(pBitcodeSize)); 283 return false; 284 } 285 286 //===--------------------------------------------------------------------===// 287 // Prepare dependency information. 288 //===--------------------------------------------------------------------===// 289 uint8_t bitcode_sha1[SHA1_DIGEST_LENGTH]; 290 Sha1Util::GetSHA1DigestFromBuffer(bitcode_sha1, pBitcode, pBitcodeSize); 291 292 //===--------------------------------------------------------------------===// 293 // Construct output path. 294 // {pCacheDir}/{pResName}.o 295 //===--------------------------------------------------------------------===// 296 llvm::SmallString<80> output_path(pCacheDir); 297 llvm::sys::path::append(output_path, pResName); 298 llvm::sys::path::replace_extension(output_path, ".o"); 299 300 //===--------------------------------------------------------------------===// 301 // Load the bitcode and create script. 302 //===--------------------------------------------------------------------===// 303 Source *source = Source::CreateFromBuffer(pContext, pResName, 304 pBitcode, pBitcodeSize); 305 if (source == nullptr) { 306 return false; 307 } 308 309 RSScript script(*source); 310 if (pLinkRuntimeCallback) { 311 setLinkRuntimeCallback(pLinkRuntimeCallback); 312 } 313 314 script.setLinkRuntimeCallback(getLinkRuntimeCallback()); 315 316 // Read information from bitcode wrapper. 317 bcinfo::BitcodeWrapper wrapper(pBitcode, pBitcodeSize); 318 script.setCompilerVersion(wrapper.getCompilerVersion()); 319 script.setOptimizationLevel(static_cast<RSScript::OptimizationLevel>( 320 wrapper.getOptimizationLevel())); 321 322 //===--------------------------------------------------------------------===// 323 // Compile the script 324 //===--------------------------------------------------------------------===// 325 Compiler::ErrorCode status = compileScript(script, pResName, 326 output_path.c_str(), 327 pRuntimePath, bitcode_sha1, commandLine, 328 pBuildChecksum, true, pDumpIR); 329 330 return status == Compiler::kSuccess; 331} 332 333bool RSCompilerDriver::buildScriptGroup( 334 BCCContext& Context, const char* pOutputFilepath, const char* pRuntimePath, 335 const char* pRuntimeRelaxedPath, bool dumpIR, 336 const std::vector<Source*>& sources, 337 const std::list<std::list<std::pair<int, int>>>& toFuse, 338 const std::list<std::string>& fused, 339 const std::list<std::list<std::pair<int, int>>>& invokes, 340 const std::list<std::string>& invokeBatchNames) { 341 // --------------------------------------------------------------------------- 342 // Link all input modules into a single module 343 // --------------------------------------------------------------------------- 344 345 llvm::LLVMContext& context = Context.getLLVMContext(); 346 llvm::Module module("Merged Script Group", context); 347 348 llvm::Linker linker(&module); 349 for (Source* source : sources) { 350 if (linker.linkInModule(&source->getModule())) { 351 ALOGE("Linking for module in source failed."); 352 return false; 353 } 354 } 355 356 // --------------------------------------------------------------------------- 357 // Create fused kernels 358 // --------------------------------------------------------------------------- 359 360 auto inputIter = toFuse.begin(); 361 for (const std::string& nameOfFused : fused) { 362 auto inputKernels = *inputIter++; 363 std::vector<Source*> sourcesToFuse; 364 std::vector<int> slots; 365 366 for (auto p : inputKernels) { 367 sourcesToFuse.push_back(sources[p.first]); 368 slots.push_back(p.second); 369 } 370 371 if (!fuseKernels(Context, sourcesToFuse, slots, nameOfFused, &module)) { 372 return false; 373 } 374 } 375 376 // --------------------------------------------------------------------------- 377 // Rename invokes 378 // --------------------------------------------------------------------------- 379 380 auto invokeIter = invokes.begin(); 381 for (const std::string& newName : invokeBatchNames) { 382 auto inputInvoke = *invokeIter++; 383 auto p = inputInvoke.front(); 384 Source* source = sources[p.first]; 385 int slot = p.second; 386 387 if (!renameInvoke(Context, source, slot, newName, &module)) { 388 return false; 389 } 390 } 391 392 // --------------------------------------------------------------------------- 393 // Compile the new module with fused kernels 394 // --------------------------------------------------------------------------- 395 396 const std::unique_ptr<Source> source( 397 Source::CreateFromModule(Context, pOutputFilepath, module, true)); 398 RSScript script(*source); 399 400 uint8_t bitcode_sha1[SHA1_DIGEST_LENGTH]; 401 const char* compileCommandLineToEmbed = ""; 402 const char* buildChecksum = "DummyChecksumForScriptGroup"; 403 const char* buildFingerprintToEmbed = ""; 404 405 RSInfo* info = RSInfo::ExtractFromSource(*source, bitcode_sha1, 406 compileCommandLineToEmbed, buildFingerprintToEmbed); 407 if (info == nullptr) { 408 return false; 409 } 410 script.setInfo(info); 411 412 // Embed the info string directly in the ELF 413 script.setEmbedInfo(true); 414 script.setOptimizationLevel(RSScript::kOptLvl3); 415 416 llvm::SmallString<80> output_path(pOutputFilepath); 417 llvm::sys::path::replace_extension(output_path, ".o"); 418 419 // Pick the right runtime lib 420 const char* coreLibPath = pRuntimePath; 421 if (strcmp(pRuntimeRelaxedPath, "")) { 422 bcinfo::MetadataExtractor me(&module); 423 me.extract(); 424 if (me.getRSFloatPrecision() == bcinfo::RS_FP_Relaxed) { 425 coreLibPath = pRuntimeRelaxedPath; 426 } 427 } 428 429 compileScript(script, pOutputFilepath, output_path.c_str(), coreLibPath, 430 bitcode_sha1, compileCommandLineToEmbed, buildChecksum, 431 true, dumpIR); 432 433 return true; 434} 435 436bool RSCompilerDriver::buildForCompatLib(RSScript &pScript, const char *pOut, 437 const char *pBuildChecksum, 438 const char *pRuntimePath, 439 bool pDumpIR) { 440 // For compat lib, we don't check the RS info file so we don't need the source hash, 441 // compile command, and build fingerprint. 442 // TODO We may want to make them optional or embed real values. 443 uint8_t bitcode_sha1[SHA1_DIGEST_LENGTH] = {0}; 444 const char* compileCommandLineToEmbed = ""; 445 const char* buildFingerprintToEmbed = ""; 446 447 RSInfo* info = RSInfo::ExtractFromSource(pScript.getSource(), bitcode_sha1, 448 compileCommandLineToEmbed, buildFingerprintToEmbed); 449 if (info == nullptr) { 450 return false; 451 } 452 pScript.setInfo(info); 453 454 // Embed the info string directly in the ELF, since this path is for an 455 // offline (host) compilation. 456 pScript.setEmbedInfo(true); 457 458 Compiler::ErrorCode status = compileScript(pScript, pOut, pOut, pRuntimePath, 459 bitcode_sha1, 460 compileCommandLineToEmbed, 461 pBuildChecksum, false, pDumpIR); 462 if (status != Compiler::kSuccess) { 463 return false; 464 } 465 466 return true; 467} 468