RSCompilerDriver.cpp revision 8e9089377848628813a697b972773e969b942c3b
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/ADT/STLExtras.h" 20#include "llvm/IR/AssemblyAnnotationWriter.h" 21#include <llvm/IR/Module.h> 22#include "llvm/Linker/Linker.h" 23#include <llvm/Support/CommandLine.h> 24#include <llvm/Support/Path.h> 25#include <llvm/Support/raw_ostream.h> 26 27#include "bcinfo/BitcodeWrapper.h" 28#include "bcc/Assert.h" 29#include "bcinfo/MetadataExtractor.h" 30#include "bcc/BCCContext.h" 31#include "bcc/Compiler.h" 32#include "bcc/Config/Config.h" 33#include "bcc/Renderscript/RSScript.h" 34#include "bcc/Renderscript/RSScriptGroupFusion.h" 35#include "bcc/Support/CompilerConfig.h" 36#include "bcc/Source.h" 37#include "bcc/Support/FileMutex.h" 38#include "bcc/Support/Log.h" 39#include "bcc/Support/InputFile.h" 40#include "bcc/Support/Initialization.h" 41#include "bcc/Support/OutputFile.h" 42 43#include <sstream> 44#include <string> 45 46#ifdef __ANDROID__ 47#include <cutils/properties.h> 48#endif 49#include <utils/StopWatch.h> 50 51using namespace bcc; 52 53RSCompilerDriver::RSCompilerDriver(bool pUseCompilerRT) : 54 mConfig(nullptr), mCompiler(), mDebugContext(false), 55 mLinkRuntimeCallback(nullptr), mEnableGlobalMerge(true), 56 mEmbedGlobalInfo(false), mEmbedGlobalInfoSkipConstant(false) { 57 init::Initialize(); 58} 59 60RSCompilerDriver::~RSCompilerDriver() { 61 delete mConfig; 62} 63 64 65#if defined(PROVIDE_ARM_CODEGEN) 66extern llvm::cl::opt<bool> EnableGlobalMerge; 67#endif 68 69bool RSCompilerDriver::setupConfig(const RSScript &pScript) { 70 bool changed = false; 71 72 const llvm::CodeGenOpt::Level script_opt_level = 73 static_cast<llvm::CodeGenOpt::Level>(pScript.getOptimizationLevel()); 74 75#if defined(PROVIDE_ARM_CODEGEN) 76 EnableGlobalMerge = mEnableGlobalMerge; 77#endif 78 79 if (mConfig != nullptr) { 80 // Renderscript bitcode may have their optimization flag configuration 81 // different than the previous run of RS compilation. 82 if (mConfig->getOptimizationLevel() != script_opt_level) { 83 mConfig->setOptimizationLevel(script_opt_level); 84 changed = true; 85 } 86 } else { 87 // Haven't run the compiler ever. 88 mConfig = new (std::nothrow) CompilerConfig(DEFAULT_TARGET_TRIPLE_STRING); 89 if (mConfig == nullptr) { 90 // Return false since mConfig remains NULL and out-of-memory. 91 return false; 92 } 93 mConfig->setOptimizationLevel(script_opt_level); 94 changed = true; 95 } 96 97#if defined(PROVIDE_ARM_CODEGEN) 98 bcinfo::MetadataExtractor me(&pScript.getSource().getModule()); 99 if (!me.extract()) { 100 bccAssert("Could not extract RS pragma metadata for module!"); 101 } 102 103 bool script_full_prec = (me.getRSFloatPrecision() == bcinfo::RS_FP_Full); 104 if (mConfig->getFullPrecision() != script_full_prec) { 105 mConfig->setFullPrecision(script_full_prec); 106 changed = true; 107 } 108#endif 109 110 return changed; 111} 112 113Compiler::ErrorCode RSCompilerDriver::compileScript(RSScript& pScript, const char* pScriptName, 114 const char* pOutputPath, 115 const char* pRuntimePath, 116 const char* pBuildChecksum, 117 bool pDumpIR) { 118 // embed build checksum metadata into the source 119 if (pBuildChecksum != nullptr && strlen(pBuildChecksum) > 0) { 120 pScript.getSource().addBuildChecksumMetadata(pBuildChecksum); 121 } 122 123 // Verify that the only external functions in pScript are Renderscript 124 // functions. Fail if verification returns an error. 125 if (mCompiler.screenGlobalFunctions(pScript) != Compiler::kSuccess) { 126 return Compiler::kErrInvalidSource; 127 } 128 129 //===--------------------------------------------------------------------===// 130 // Link RS script with Renderscript runtime. 131 //===--------------------------------------------------------------------===// 132 if (!RSScript::LinkRuntime(pScript, pRuntimePath)) { 133 ALOGE("Failed to link script '%s' with Renderscript runtime %s!", 134 pScriptName, pRuntimePath); 135 return Compiler::kErrInvalidSource; 136 } 137 138 { 139 // FIXME(srhines): Windows compilation can't use locking like this, but 140 // we also don't need to worry about concurrent writers of the same file. 141#ifndef USE_MINGW 142 //===------------------------------------------------------------------===// 143 // Acquire the write lock for writing output object file. 144 //===------------------------------------------------------------------===// 145 FileMutex<FileBase::kWriteLock> write_output_mutex(pOutputPath); 146 147 if (write_output_mutex.hasError() || !write_output_mutex.lock()) { 148 ALOGE("Unable to acquire the lock for writing %s! (%s)", 149 pOutputPath, write_output_mutex.getErrorMessage().c_str()); 150 return Compiler::kErrInvalidSource; 151 } 152#endif 153 154 // Open the output file for write. 155 OutputFile output_file(pOutputPath, 156 FileBase::kTruncate | FileBase::kBinary); 157 158 if (output_file.hasError()) { 159 ALOGE("Unable to open %s for write! (%s)", pOutputPath, 160 output_file.getErrorMessage().c_str()); 161 return Compiler::kErrInvalidSource; 162 } 163 164 // Setup the config to the compiler. 165 bool compiler_need_reconfigure = setupConfig(pScript); 166 167 if (mConfig == nullptr) { 168 ALOGE("Failed to setup config for RS compiler to compile %s!", 169 pOutputPath); 170 return Compiler::kErrInvalidSource; 171 } 172 173 if (compiler_need_reconfigure) { 174 Compiler::ErrorCode err = mCompiler.config(*mConfig); 175 if (err != Compiler::kSuccess) { 176 ALOGE("Failed to config the RS compiler for %s! (%s)",pOutputPath, 177 Compiler::GetErrorString(err)); 178 return Compiler::kErrInvalidSource; 179 } 180 } 181 182 OutputFile *ir_file = nullptr; 183 llvm::raw_fd_ostream *IRStream = nullptr; 184 if (pDumpIR) { 185 std::string path(pOutputPath); 186 path.append(".ll"); 187 ir_file = new OutputFile(path.c_str(), FileBase::kTruncate); 188 IRStream = ir_file->dup(); 189 } 190 191 // Run the compiler. 192 Compiler::ErrorCode compile_result = 193 mCompiler.compile(pScript, output_file, IRStream); 194 195 if (ir_file) { 196 ir_file->close(); 197 delete ir_file; 198 } 199 200 if (compile_result != Compiler::kSuccess) { 201 ALOGE("Unable to compile the source to file %s! (%s)", pOutputPath, 202 Compiler::GetErrorString(compile_result)); 203 return Compiler::kErrInvalidSource; 204 } 205 } 206 207 return Compiler::kSuccess; 208} 209 210bool RSCompilerDriver::build(BCCContext &pContext, 211 const char *pCacheDir, 212 const char *pResName, 213 const char *pBitcode, 214 size_t pBitcodeSize, 215 const char *pBuildChecksum, 216 const char *pRuntimePath, 217 RSLinkRuntimeCallback pLinkRuntimeCallback, 218 bool pDumpIR) { 219 // android::StopWatch build_time("bcc: RSCompilerDriver::build time"); 220 //===--------------------------------------------------------------------===// 221 // Check parameters. 222 //===--------------------------------------------------------------------===// 223 if ((pCacheDir == nullptr) || (pResName == nullptr)) { 224 ALOGE("Invalid parameter passed to RSCompilerDriver::build()! (cache dir: " 225 "%s, resource name: %s)", ((pCacheDir) ? pCacheDir : "(null)"), 226 ((pResName) ? pResName : "(null)")); 227 return false; 228 } 229 230 if ((pBitcode == nullptr) || (pBitcodeSize <= 0)) { 231 ALOGE("No bitcode supplied! (bitcode: %p, size of bitcode: %u)", 232 pBitcode, static_cast<unsigned>(pBitcodeSize)); 233 return false; 234 } 235 236 //===--------------------------------------------------------------------===// 237 // Construct output path. 238 // {pCacheDir}/{pResName}.o 239 //===--------------------------------------------------------------------===// 240 llvm::SmallString<80> output_path(pCacheDir); 241 llvm::sys::path::append(output_path, pResName); 242 llvm::sys::path::replace_extension(output_path, ".o"); 243 244 //===--------------------------------------------------------------------===// 245 // Load the bitcode and create script. 246 //===--------------------------------------------------------------------===// 247 Source *source = Source::CreateFromBuffer(pContext, pResName, 248 pBitcode, pBitcodeSize); 249 if (source == nullptr) { 250 return false; 251 } 252 253 RSScript script(*source, getConfig()); 254 if (pLinkRuntimeCallback) { 255 setLinkRuntimeCallback(pLinkRuntimeCallback); 256 } 257 258 script.setLinkRuntimeCallback(getLinkRuntimeCallback()); 259 260 script.setEmbedGlobalInfo(mEmbedGlobalInfo); 261 script.setEmbedGlobalInfoSkipConstant(mEmbedGlobalInfoSkipConstant); 262 263 // Read information from bitcode wrapper. 264 bcinfo::BitcodeWrapper wrapper(pBitcode, pBitcodeSize); 265 script.setCompilerVersion(wrapper.getCompilerVersion()); 266 script.setOptimizationLevel(static_cast<RSScript::OptimizationLevel>( 267 wrapper.getOptimizationLevel())); 268 269// Assertion-enabled builds can't compile legacy bitcode (due to the use of 270// getName() with anonymous structure definitions). 271#ifdef FORCE_BUILD_LLVM_DISABLE_NDEBUG 272 static const uint32_t kSlangMinimumFixedStructureNames = 2310; 273 uint32_t version = wrapper.getCompilerVersion(); 274 if (version < kSlangMinimumFixedStructureNames) { 275 ALOGE("Found invalid legacy bitcode compiled with a version %u llvm-rs-cc " 276 "used with an assertion build", version); 277 ALOGE("Please recompile this apk with a more recent llvm-rs-cc " 278 "(at least %u)", kSlangMinimumFixedStructureNames); 279 return false; 280 } 281#endif 282 283 //===--------------------------------------------------------------------===// 284 // Compile the script 285 //===--------------------------------------------------------------------===// 286 Compiler::ErrorCode status = compileScript(script, pResName, 287 output_path.c_str(), 288 pRuntimePath, 289 pBuildChecksum, 290 pDumpIR); 291 292 return status == Compiler::kSuccess; 293} 294 295bool RSCompilerDriver::buildScriptGroup( 296 BCCContext& Context, const char* pOutputFilepath, const char* pRuntimePath, 297 const char* pRuntimeRelaxedPath, bool dumpIR, const char* buildChecksum, 298 const std::vector<Source*>& sources, 299 const std::list<std::list<std::pair<int, int>>>& toFuse, 300 const std::list<std::string>& fused, 301 const std::list<std::list<std::pair<int, int>>>& invokes, 302 const std::list<std::string>& invokeBatchNames) { 303 304 // Read and store metadata before linking the modules together 305 std::vector<bcinfo::MetadataExtractor*> metadata; 306 for (Source* source : sources) { 307 if (!source->extractMetadata()) { 308 ALOGE("Cannot extract metadata from module"); 309 return false; 310 } 311 } 312 313 // --------------------------------------------------------------------------- 314 // Link all input modules into a single module 315 // --------------------------------------------------------------------------- 316 317 llvm::LLVMContext& context = Context.getLLVMContext(); 318 llvm::Module module("Merged Script Group", context); 319 320 llvm::Linker linker(module); 321 for (Source* source : sources) { 322 std::unique_ptr<llvm::Module> sourceModule(&source->getModule()); 323 if (linker.linkInModule(std::move(sourceModule))) { 324 ALOGE("Linking for module in source failed."); 325 return false; 326 } 327 // source->getModule() is destroyed after linking. 328 source->markModuleDestroyed(); 329 } 330 331 // --------------------------------------------------------------------------- 332 // Create fused kernels 333 // --------------------------------------------------------------------------- 334 335 auto inputIter = toFuse.begin(); 336 for (const std::string& nameOfFused : fused) { 337 auto inputKernels = *inputIter++; 338 std::vector<Source*> sourcesToFuse; 339 std::vector<int> slots; 340 341 for (auto p : inputKernels) { 342 sourcesToFuse.push_back(sources[p.first]); 343 slots.push_back(p.second); 344 } 345 346 if (!fuseKernels(Context, sourcesToFuse, slots, nameOfFused, &module)) { 347 return false; 348 } 349 } 350 351 // --------------------------------------------------------------------------- 352 // Rename invokes 353 // --------------------------------------------------------------------------- 354 355 auto invokeIter = invokes.begin(); 356 for (const std::string& newName : invokeBatchNames) { 357 auto inputInvoke = *invokeIter++; 358 auto p = inputInvoke.front(); 359 Source* source = sources[p.first]; 360 int slot = p.second; 361 362 if (!renameInvoke(Context, source, slot, newName, &module)) { 363 return false; 364 } 365 } 366 367 // --------------------------------------------------------------------------- 368 // Compile the new module with fused kernels 369 // --------------------------------------------------------------------------- 370 371 const std::unique_ptr<Source> source( 372 Source::CreateFromModule(Context, pOutputFilepath, module, true)); 373 RSScript script(*source); 374 375 // Embed the info string directly in the ELF 376 script.setEmbedInfo(true); 377 script.setOptimizationLevel(RSScript::kOptLvl3); 378 script.setEmbedGlobalInfo(mEmbedGlobalInfo); 379 script.setEmbedGlobalInfoSkipConstant(mEmbedGlobalInfoSkipConstant); 380 381 llvm::SmallString<80> output_path(pOutputFilepath); 382 llvm::sys::path::replace_extension(output_path, ".o"); 383 384 // Pick the right runtime lib 385 const char* coreLibPath = pRuntimePath; 386 if (strcmp(pRuntimeRelaxedPath, "")) { 387 bcinfo::MetadataExtractor me(&module); 388 me.extract(); 389 if (me.getRSFloatPrecision() == bcinfo::RS_FP_Relaxed) { 390 coreLibPath = pRuntimeRelaxedPath; 391 } 392 } 393 394 compileScript(script, pOutputFilepath, output_path.c_str(), coreLibPath, 395 buildChecksum, dumpIR); 396 397 return true; 398} 399 400bool RSCompilerDriver::buildForCompatLib(RSScript &pScript, const char *pOut, 401 const char *pBuildChecksum, 402 const char *pRuntimePath, 403 bool pDumpIR) { 404 // Embed the info string directly in the ELF, since this path is for an 405 // offline (host) compilation. 406 pScript.setEmbedInfo(true); 407 408 pScript.setEmbedGlobalInfo(mEmbedGlobalInfo); 409 pScript.setEmbedGlobalInfoSkipConstant(mEmbedGlobalInfoSkipConstant); 410 pScript.setLinkRuntimeCallback(getLinkRuntimeCallback()); 411 412 Compiler::ErrorCode status = compileScript(pScript, pOut, pOut, pRuntimePath, 413 pBuildChecksum, pDumpIR); 414 if (status != Compiler::kSuccess) { 415 return false; 416 } 417 418 return true; 419} 420