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