Home | History | Annotate | Download | only in lib
      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