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