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