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