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