RSCompilerDriver.cpp revision 3d740780dfc4c446d89b9d130d739b4aa90c8298
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/Module.h> 20#include <llvm/Support/CommandLine.h> 21#include <llvm/Support/PathV1.h> 22#include <llvm/Support/raw_ostream.h> 23 24#include "bcinfo/BitcodeWrapper.h" 25 26#include "bcc/Renderscript/RSExecutable.h" 27#include "bcc/Renderscript/RSScript.h" 28#include "bcc/Support/CompilerConfig.h" 29#include "bcc/Support/TargetCompilerConfigs.h" 30#include "bcc/Source.h" 31#include "bcc/Support/FileMutex.h" 32#include "bcc/Support/Log.h" 33#include "bcc/Support/InputFile.h" 34#include "bcc/Support/Initialization.h" 35#include "bcc/Support/Sha1Util.h" 36#include "bcc/Support/OutputFile.h" 37 38#include <cutils/properties.h> 39#include <utils/String8.h> 40#include <utils/StopWatch.h> 41 42using namespace bcc; 43 44namespace { 45 46bool is_force_recompile() { 47 char buf[PROPERTY_VALUE_MAX]; 48 49 // Re-compile if floating point precision has been overridden. 50 property_get("debug.rs.precision", buf, ""); 51 if (buf[0] != '\0') { 52 return true; 53 } 54 55 // Re-compile if debug.rs.forcerecompile is set. 56 property_get("debug.rs.forcerecompile", buf, "0"); 57 if ((::strcmp(buf, "1") == 0) || (::strcmp(buf, "true") == 0)) { 58 return true; 59 } else { 60 return false; 61 } 62} 63 64} // end anonymous namespace 65 66RSCompilerDriver::RSCompilerDriver(bool pUseCompilerRT) : 67 mConfig(NULL), mCompiler(), mCompilerRuntime(NULL), mDebugContext(false), 68 mEnableGlobalMerge(true) { 69 init::Initialize(); 70 // Chain the symbol resolvers for compiler_rt and RS runtimes. 71 if (pUseCompilerRT) { 72 mCompilerRuntime = new CompilerRTSymbolResolver(); 73 mResolver.chainResolver(*mCompilerRuntime); 74 } 75 mResolver.chainResolver(mRSRuntime); 76} 77 78RSCompilerDriver::~RSCompilerDriver() { 79 delete mCompilerRuntime; 80 delete mConfig; 81} 82 83RSExecutable * 84RSCompilerDriver::loadScriptCache(const char *pOutputPath, 85 const RSInfo::DependencyTableTy &pDeps) { 86 //android::StopWatch load_time("bcc: RSCompilerDriver::loadScriptCache time"); 87 RSExecutable *result = NULL; 88 89 if (is_force_recompile()) 90 return NULL; 91 92 //===--------------------------------------------------------------------===// 93 // Acquire the read lock for reading output object file. 94 //===--------------------------------------------------------------------===// 95 FileMutex<FileBase::kReadLock> read_output_mutex(pOutputPath); 96 97 if (read_output_mutex.hasError() || !read_output_mutex.lock()) { 98 ALOGE("Unable to acquire the read lock for %s! (%s)", pOutputPath, 99 read_output_mutex.getErrorMessage().c_str()); 100 return NULL; 101 } 102 103 //===--------------------------------------------------------------------===// 104 // Read the output object file. 105 //===--------------------------------------------------------------------===// 106 InputFile *output_file = new (std::nothrow) InputFile(pOutputPath); 107 108 if ((output_file == NULL) || output_file->hasError()) { 109 // ALOGE("Unable to open the %s for read! (%s)", pOutputPath, 110 // output_file->getErrorMessage().c_str()); 111 delete output_file; 112 return NULL; 113 } 114 115 //===--------------------------------------------------------------------===// 116 // Acquire the read lock on output_file for reading its RS info file. 117 //===--------------------------------------------------------------------===// 118 android::String8 info_path = RSInfo::GetPath(*output_file); 119 120 if (!output_file->lock()) { 121 ALOGE("Unable to acquire the read lock on %s for reading %s! (%s)", 122 pOutputPath, info_path.string(), 123 output_file->getErrorMessage().c_str()); 124 delete output_file; 125 return NULL; 126 } 127 128 //===---------------------------------------------------------------------===// 129 // Open and load the RS info file. 130 //===--------------------------------------------------------------------===// 131 InputFile info_file(info_path.string()); 132 RSInfo *info = RSInfo::ReadFromFile(info_file, pDeps); 133 134 // Release the lock on output_file. 135 output_file->unlock(); 136 137 if (info == NULL) { 138 delete output_file; 139 return NULL; 140 } 141 142 //===--------------------------------------------------------------------===// 143 // Create the RSExecutable. 144 //===--------------------------------------------------------------------===// 145 result = RSExecutable::Create(*info, *output_file, mResolver); 146 if (result == NULL) { 147 delete output_file; 148 delete info; 149 return NULL; 150 } 151 152 return result; 153} 154 155#if defined(DEFAULT_ARM_CODEGEN) 156extern llvm::cl::opt<bool> EnableGlobalMerge; 157#endif 158 159bool RSCompilerDriver::setupConfig(const RSScript &pScript) { 160 bool changed = false; 161 162 const llvm::CodeGenOpt::Level script_opt_level = 163 static_cast<llvm::CodeGenOpt::Level>(pScript.getOptimizationLevel()); 164 165 if (mConfig != NULL) { 166 // Renderscript bitcode may have their optimization flag configuration 167 // different than the previous run of RS compilation. 168 if (mConfig->getOptimizationLevel() != script_opt_level) { 169 mConfig->setOptimizationLevel(script_opt_level); 170 changed = true; 171 } 172 } else { 173 // Haven't run the compiler ever. 174 mConfig = new (std::nothrow) DefaultCompilerConfig(); 175 if (mConfig == NULL) { 176 // Return false since mConfig remains NULL and out-of-memory. 177 return false; 178 } 179 mConfig->setOptimizationLevel(script_opt_level); 180#if defined(DEFAULT_ARM_CODEGEN) 181 EnableGlobalMerge = mEnableGlobalMerge; 182#endif 183 changed = true; 184 } 185 186#if defined(DEFAULT_ARM_CODEGEN) 187 // NEON should be disable when full-precision floating point is required. 188 assert((pScript.getInfo() != NULL) && "NULL RS info!"); 189 if (pScript.getInfo()->getFloatPrecisionRequirement() == RSInfo::FP_Full) { 190 // Must be ARMCompilerConfig. 191 ARMCompilerConfig *arm_config = static_cast<ARMCompilerConfig *>(mConfig); 192 changed |= arm_config->enableNEON(/* pEnable */false); 193 } 194#endif 195 196 return changed; 197} 198 199RSExecutable * 200RSCompilerDriver::compileScript(RSScript &pScript, 201 const char* pScriptName, 202 const char *pOutputPath, 203 const char *pRuntimePath, 204 const RSInfo::DependencyTableTy &pDeps, 205 bool pSkipLoad) { 206 //android::StopWatch compile_time("bcc: RSCompilerDriver::compileScript time"); 207 RSExecutable *result = NULL; 208 RSInfo *info = NULL; 209 210 //===--------------------------------------------------------------------===// 211 // Extract RS-specific information from source bitcode. 212 //===--------------------------------------------------------------------===// 213 // RS info may contains configuration (such as #optimization_level) to the 214 // compiler therefore it should be extracted before compilation. 215 info = RSInfo::ExtractFromSource(pScript.getSource(), pDeps); 216 if (info == NULL) { 217 return NULL; 218 } 219 220 //===--------------------------------------------------------------------===// 221 // Associate script with its info 222 //===--------------------------------------------------------------------===// 223 // This is required since RS compiler may need information in the info file 224 // to do some transformation (e.g., expand foreach-able function.) 225 pScript.setInfo(info); 226 227 //===--------------------------------------------------------------------===// 228 // Link RS script with Renderscript runtime. 229 //===--------------------------------------------------------------------===// 230 if (!RSScript::LinkRuntime(pScript, pRuntimePath)) { 231 ALOGE("Failed to link script '%s' with Renderscript runtime!", pScriptName); 232 return NULL; 233 } 234 235 //===--------------------------------------------------------------------===// 236 // Acquire the write lock for writing output object file. 237 //===--------------------------------------------------------------------===// 238 FileMutex<FileBase::kWriteLock> write_output_mutex(pOutputPath); 239 240 if (write_output_mutex.hasError() || !write_output_mutex.lock()) { 241 ALOGE("Unable to acquire the lock for writing %s! (%s)", 242 pOutputPath, write_output_mutex.getErrorMessage().c_str()); 243 return NULL; 244 } 245 246 //===--------------------------------------------------------------------===// 247 // Open the output file for write. 248 //===--------------------------------------------------------------------===// 249 unsigned flags = FileBase::kTruncate; 250 if (mDebugContext) { 251 // Delete the cache file when we finish up under a debug context. 252 flags |= FileBase::kDeleteOnClose; 253 } 254 OutputFile *output_file = new (std::nothrow) OutputFile(pOutputPath, flags); 255 256 if ((output_file == NULL) || output_file->hasError()) { 257 ALOGE("Unable to open %s for write! (%s)", pOutputPath, 258 output_file->getErrorMessage().c_str()); 259 delete info; 260 delete output_file; 261 return NULL; 262 } 263 264 //===--------------------------------------------------------------------===// 265 // Setup the config to the compiler. 266 //===--------------------------------------------------------------------===// 267 bool compiler_need_reconfigure = setupConfig(pScript); 268 269 if (mConfig == NULL) { 270 ALOGE("Failed to setup config for RS compiler to compile %s!", pOutputPath); 271 delete info; 272 delete output_file; 273 return NULL; 274 } 275 276 // Compiler need to re-config if it's haven't run the config() yet or the 277 // configuration it referenced is changed. 278 if (compiler_need_reconfigure) { 279 Compiler::ErrorCode err = mCompiler.config(*mConfig); 280 if (err != Compiler::kSuccess) { 281 ALOGE("Failed to config the RS compiler for %s! (%s)",pOutputPath, 282 Compiler::GetErrorString(err)); 283 delete info; 284 delete output_file; 285 return NULL; 286 } 287 } 288 289 //===--------------------------------------------------------------------===// 290 // Run the compiler. 291 //===--------------------------------------------------------------------===// 292 Compiler::ErrorCode compile_result = mCompiler.compile(pScript, *output_file); 293 if (compile_result != Compiler::kSuccess) { 294 ALOGE("Unable to compile the source to file %s! (%s)", pOutputPath, 295 Compiler::GetErrorString(compile_result)); 296 delete info; 297 delete output_file; 298 return NULL; 299 } 300 301 // No need to produce an RSExecutable in this case. 302 // TODO: Error handling in this case is nonexistent. 303 if (pSkipLoad) { 304 return NULL; 305 } 306 307 //===--------------------------------------------------------------------===// 308 // Create the RSExecutable. 309 //===--------------------------------------------------------------------===// 310 result = RSExecutable::Create(*info, *output_file, mResolver); 311 if (result == NULL) { 312 delete info; 313 delete output_file; 314 return NULL; 315 } 316 317 //===--------------------------------------------------------------------===// 318 // Dump the disassembly for debug when possible. 319 //===--------------------------------------------------------------------===// 320#if USE_DISASSEMBLER 321 OutputFile *disassembly_output = 322 new (std::nothrow) OutputFile(DEBUG_DISASSEMBLER_FILE, 323 FileBase::kAppend); 324 325 if (disassembly_output != NULL) { 326 result->dumpDisassembly(*disassembly_output); 327 delete disassembly_output; 328 } 329#endif 330 331 //===--------------------------------------------------------------------===// 332 // Write out the RS info file. 333 //===--------------------------------------------------------------------===// 334 // Note that write failure only results in a warning since the source is 335 // successfully compiled and loaded. 336 if (!result->syncInfo(/* pForce */true)) { 337 ALOGW("%s was successfully compiled and loaded but its RS info file failed " 338 "to write out!", pOutputPath); 339 } 340 341 return result; 342} 343 344RSExecutable *RSCompilerDriver::build(BCCContext &pContext, 345 const char *pCacheDir, 346 const char *pResName, 347 const char *pBitcode, 348 size_t pBitcodeSize, 349 const char *pRuntimePath, 350 RSLinkRuntimeCallback pLinkRuntimeCallback) { 351 // android::StopWatch build_time("bcc: RSCompilerDriver::build time"); 352 //===--------------------------------------------------------------------===// 353 // Check parameters. 354 //===--------------------------------------------------------------------===// 355 if ((pCacheDir == NULL) || (pResName == NULL)) { 356 ALOGE("Invalid parameter passed to RSCompilerDriver::build()! (cache dir: " 357 "%s, resource name: %s)", ((pCacheDir) ? pCacheDir : "(null)"), 358 ((pResName) ? pResName : "(null)")); 359 return NULL; 360 } 361 362 if ((pBitcode == NULL) || (pBitcodeSize <= 0)) { 363 ALOGE("No bitcode supplied! (bitcode: %p, size of bitcode: %u)", 364 pBitcode, static_cast<unsigned>(pBitcodeSize)); 365 return NULL; 366 } 367 368 //===--------------------------------------------------------------------===// 369 // Prepare dependency information. 370 //===--------------------------------------------------------------------===// 371 RSInfo::DependencyTableTy dep_info; 372 uint8_t bitcode_sha1[20]; 373 Sha1Util::GetSHA1DigestFromBuffer(bitcode_sha1, pBitcode, pBitcodeSize); 374 dep_info.push(std::make_pair(pResName, bitcode_sha1)); 375 376 //===--------------------------------------------------------------------===// 377 // Construct output path. 378 //===--------------------------------------------------------------------===// 379 llvm::sys::Path output_path(pCacheDir); 380 381 // {pCacheDir}/{pResName} 382 if (!output_path.appendComponent(pResName)) { 383 ALOGE("Failed to construct output path %s/%s!", pCacheDir, pResName); 384 return NULL; 385 } 386 387 // {pCacheDir}/{pResName}.o 388 output_path.appendSuffix("o"); 389 390 //===--------------------------------------------------------------------===// 391 // Load cache. 392 //===--------------------------------------------------------------------===// 393 RSExecutable *result = NULL; 394 395 // Skip loading from the cache if we are using a debug context. 396 if (!mDebugContext) { 397 result = loadScriptCache(output_path.c_str(), dep_info); 398 399 if (result != NULL) { 400 // Cache hit 401 return result; 402 } 403 } 404 405 //===--------------------------------------------------------------------===// 406 // Load the bitcode and create script. 407 //===--------------------------------------------------------------------===// 408 Source *source = Source::CreateFromBuffer(pContext, pResName, 409 pBitcode, pBitcodeSize); 410 if (source == NULL) { 411 return NULL; 412 } 413 414 RSScript *script = new (std::nothrow) RSScript(*source); 415 if (script == NULL) { 416 ALOGE("Out of memory when create Script object for '%s'! (output: %s)", 417 pResName, output_path.c_str()); 418 delete source; 419 return NULL; 420 } 421 422 script->setLinkRuntimeCallback(pLinkRuntimeCallback); 423 424 // Read information from bitcode wrapper. 425 bcinfo::BitcodeWrapper wrapper(pBitcode, pBitcodeSize); 426 script->setCompilerVersion(wrapper.getCompilerVersion()); 427 script->setOptimizationLevel(static_cast<RSScript::OptimizationLevel>( 428 wrapper.getOptimizationLevel())); 429 430 //===--------------------------------------------------------------------===// 431 // Compile the script 432 //===--------------------------------------------------------------------===// 433 result = compileScript(*script, pResName, output_path.c_str(), pRuntimePath, 434 dep_info, false); 435 436 // Script is no longer used. Free it to get more memory. 437 delete script; 438 439 if (result == NULL) { 440 return NULL; 441 } 442 443 return result; 444} 445 446 447RSExecutable *RSCompilerDriver::build(RSScript &pScript, const char *pOut, 448 const char *pRuntimePath) { 449 RSInfo::DependencyTableTy dep_info; 450 RSInfo *info = RSInfo::ExtractFromSource(pScript.getSource(), dep_info); 451 if (info == NULL) { 452 return NULL; 453 } 454 pScript.setInfo(info); 455 456 // Embed the info string directly in the ELF, since this path is for an 457 // offline (host) compilation. 458 pScript.setEmbedInfo(true); 459 460 RSExecutable *result = compileScript(pScript, pOut, pOut, pRuntimePath, 461 dep_info, true); 462 return result; 463} 464 465