Home | History | Annotate | Download | only in bcc
      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 <iostream>
     18 #include <list>
     19 #include <map>
     20 #include <sstream>
     21 #include <string>
     22 #include <vector>
     23 
     24 #include <dlfcn.h>
     25 #include <stdlib.h>
     26 
     27 #include <llvm/ADT/STLExtras.h>
     28 #include <llvm/ADT/SmallString.h>
     29 #include <llvm/Config/config.h>
     30 #include <llvm/Support/CommandLine.h>
     31 #include <llvm/Support/FileSystem.h>
     32 #include <llvm/Support/ManagedStatic.h>
     33 #include <llvm/Support/MemoryBuffer.h>
     34 #include <llvm/Support/Path.h>
     35 #include <llvm/Support/PluginLoader.h>
     36 #include <llvm/Support/raw_ostream.h>
     37 
     38 #include <bcc/BCCContext.h>
     39 #include <bcc/Compiler.h>
     40 #include <bcc/Config/Config.h>
     41 #include <bcc/Renderscript/RSCompilerDriver.h>
     42 #include <bcc/Script.h>
     43 #include <bcc/Source.h>
     44 #include <bcc/Support/Log.h>
     45 #include <bcc/Support/CompilerConfig.h>
     46 #include <bcc/Support/Initialization.h>
     47 #include <bcc/Support/InputFile.h>
     48 #include <bcc/Support/OutputFile.h>
     49 
     50 using namespace bcc;
     51 
     52 #define STR2(a) #a
     53 #define STR(a) STR2(a)
     54 
     55 //===----------------------------------------------------------------------===//
     56 // General Options
     57 //===----------------------------------------------------------------------===//
     58 namespace {
     59 
     60 llvm::cl::list<std::string>
     61 OptInputFilenames(llvm::cl::Positional, llvm::cl::OneOrMore,
     62                   llvm::cl::desc("<input bitcode files>"));
     63 
     64 llvm::cl::list<std::string>
     65 OptMergePlans("merge", llvm::cl::ZeroOrMore,
     66                llvm::cl::desc("Lists of kernels to merge (as source-and-slot "
     67                               "pairs) and names for the final merged kernels"));
     68 
     69 llvm::cl::list<std::string>
     70 OptInvokes("invoke", llvm::cl::ZeroOrMore,
     71            llvm::cl::desc("Invocable functions"));
     72 
     73 llvm::cl::opt<std::string>
     74 OptOutputFilename("o", llvm::cl::desc("Specify the output filename"),
     75                   llvm::cl::value_desc("filename"),
     76                   llvm::cl::init("bcc_output"));
     77 
     78 llvm::cl::opt<std::string>
     79 OptBCLibFilename("bclib", llvm::cl::desc("Specify the bclib filename"),
     80                  llvm::cl::value_desc("bclib"));
     81 
     82 llvm::cl::opt<std::string>
     83 OptBCLibRelaxedFilename("bclib_relaxed", llvm::cl::desc("Specify the bclib filename optimized for "
     84                                                         "relaxed precision floating point maths"),
     85                         llvm::cl::init(""),
     86                         llvm::cl::value_desc("bclib_relaxed"));
     87 
     88 llvm::cl::opt<std::string>
     89 OptOutputPath("output_path", llvm::cl::desc("Specify the output path"),
     90               llvm::cl::value_desc("output path"),
     91               llvm::cl::init("."));
     92 
     93 llvm::cl::opt<bool>
     94 OptEmitLLVM("emit-llvm",
     95             llvm::cl::desc("Emit an LLVM-IR version of the generated program"));
     96 
     97 llvm::cl::opt<std::string>
     98 OptTargetTriple("mtriple",
     99                 llvm::cl::desc("Specify the target triple (default: "
    100                                DEFAULT_TARGET_TRIPLE_STRING ")"),
    101                 llvm::cl::init(DEFAULT_TARGET_TRIPLE_STRING),
    102                 llvm::cl::value_desc("triple"));
    103 
    104 llvm::cl::alias OptTargetTripleC("C", llvm::cl::NotHidden,
    105                                  llvm::cl::desc("Alias for -mtriple"),
    106                                  llvm::cl::aliasopt(OptTargetTriple));
    107 
    108 llvm::cl::opt<bool>
    109 OptRSDebugContext("rs-debug-ctx",
    110     llvm::cl::desc("Enable build to work with a RenderScript debug context"));
    111 
    112 llvm::cl::opt<bool>
    113 OptRSGlobalInfo("rs-global-info",
    114     llvm::cl::desc("Embed information about global variables in the code"));
    115 
    116 llvm::cl::opt<bool>
    117 OptRSGlobalInfoSkipConstant("rs-global-info-skip-constant",
    118     llvm::cl::desc("Skip embedding information about constant global "
    119                    "variables in the code"));
    120 
    121 llvm::cl::opt<std::string>
    122 OptChecksum("build-checksum",
    123             llvm::cl::desc("Embed a checksum of this compiler invocation for"
    124                            " cache invalidation at a later time"),
    125             llvm::cl::value_desc("checksum"));
    126 
    127 //===----------------------------------------------------------------------===//
    128 // Compiler Options
    129 //===----------------------------------------------------------------------===//
    130 llvm::cl::opt<bool>
    131 OptPIC("fPIC", llvm::cl::desc("Generate fully relocatable, position independent"
    132                               " code"));
    133 
    134 // If set, use buildForCompatLib to embed RS symbol information into the object
    135 // file.  The information is stored in the .rs.info variable.  This option is
    136 // to be used in tandem with -fPIC.
    137 llvm::cl::opt<bool>
    138 OptEmbedRSInfo("embedRSInfo",
    139     llvm::cl::desc("Embed RS Info into the object file instead of generating"
    140                    " a separate .o.info file"));
    141 
    142 // RenderScript uses -O3 by default
    143 llvm::cl::opt<char>
    144 OptOptLevel("O", llvm::cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
    145                                 "(default: -O3)"),
    146             llvm::cl::Prefix, llvm::cl::ZeroOrMore, llvm::cl::init('3'));
    147 
    148 // Override "bcc -version" since the LLVM version information is not correct on
    149 // Android build.
    150 void BCCVersionPrinter() {
    151   llvm::raw_ostream &os = llvm::outs();
    152   os << "libbcc (The Android Open Source Project, http://www.android.com/):\n"
    153      << "  Default target: " << DEFAULT_TARGET_TRIPLE_STRING << "\n\n"
    154      << "LLVM (http://llvm.org/):\n"
    155      << "  Version: " << PACKAGE_VERSION << "\n";
    156   return;
    157 }
    158 
    159 void extractSourcesAndSlots(const llvm::cl::list<std::string>& optList,
    160                             std::list<std::string>* batchNames,
    161                             std::list<std::list<std::pair<int, int>>>* sourcesAndSlots) {
    162   for (unsigned i = 0; i < optList.size(); ++i) {
    163     std::string plan = optList[i];
    164     unsigned found = plan.find(":");
    165 
    166     std::string name = plan.substr(0, found);
    167     std::cerr << "new kernel name: " << name << std::endl;
    168     batchNames->push_back(name);
    169 
    170     std::istringstream iss(plan.substr(found + 1));
    171     std::string s;
    172     std::list<std::pair<int, int>> planList;
    173     while (getline(iss, s, '.')) {
    174       found = s.find(",");
    175       std::string sourceStr = s.substr(0, found);
    176       std::string slotStr = s.substr(found + 1);
    177 
    178       std::cerr << "source " << sourceStr << ", slot " << slotStr << std::endl;
    179 
    180       int source = std::stoi(sourceStr);
    181       int slot = std::stoi(slotStr);
    182       planList.push_back(std::make_pair(source, slot));
    183     }
    184 
    185     sourcesAndSlots->push_back(planList);
    186   }
    187 }
    188 
    189 bool compileScriptGroup(BCCContext& Context, RSCompilerDriver& RSCD) {
    190   std::vector<bcc::Source*> sources;
    191   for (unsigned i = 0; i < OptInputFilenames.size(); ++i) {
    192     bcc::Source* source =
    193         bcc::Source::CreateFromFile(Context, OptInputFilenames[i]);
    194     if (!source) {
    195       llvm::errs() << "Error loading file '" << OptInputFilenames[i]<< "'\n";
    196       return false;
    197     }
    198     sources.push_back(source);
    199   }
    200 
    201   std::list<std::string> fusedKernelNames;
    202   std::list<std::list<std::pair<int, int>>> sourcesAndSlots;
    203   extractSourcesAndSlots(OptMergePlans, &fusedKernelNames, &sourcesAndSlots);
    204 
    205   std::list<std::string> invokeBatchNames;
    206   std::list<std::list<std::pair<int, int>>> invokeSourcesAndSlots;
    207   extractSourcesAndSlots(OptInvokes, &invokeBatchNames, &invokeSourcesAndSlots);
    208 
    209   std::string outputFilepath(OptOutputPath);
    210   outputFilepath.append("/");
    211   outputFilepath.append(OptOutputFilename);
    212 
    213   bool success = RSCD.buildScriptGroup(
    214     Context, outputFilepath.c_str(), OptBCLibFilename.c_str(),
    215     OptBCLibRelaxedFilename.c_str(), OptEmitLLVM, OptChecksum.c_str(),
    216     sources, sourcesAndSlots, fusedKernelNames,
    217     invokeSourcesAndSlots, invokeBatchNames);
    218 
    219   return success;
    220 }
    221 
    222 } // end anonymous namespace
    223 
    224 static inline
    225 bool ConfigCompiler(RSCompilerDriver &pRSCD) {
    226   Compiler *RSC = pRSCD.getCompiler();
    227   CompilerConfig *config = nullptr;
    228 
    229   config = new (std::nothrow) CompilerConfig(OptTargetTriple);
    230   if (config == nullptr) {
    231     llvm::errs() << "Out of memory when create the compiler configuration!\n";
    232     return false;
    233   }
    234 
    235   // llvm3.5 has removed the auto-detect feature for x86 subtarget,
    236   // so set features explicitly in bcc.
    237   if ((config->getTriple().find("i686") != std::string::npos) ||
    238     (config->getTriple().find("x86_64") != std::string::npos)) {
    239     std::vector<std::string> fv;
    240 
    241 #if defined(__SSE3__)
    242     fv.push_back("+sse3");
    243 #endif
    244 #if defined(__SSSE3__)
    245     fv.push_back("+ssse3");
    246 #endif
    247 #if defined(__SSE4_1__)
    248     fv.push_back("+sse4.1");
    249 #endif
    250 #if defined(__SSE4_2__)
    251     fv.push_back("+sse4.2");
    252 #endif
    253 
    254     if (fv.size()) {
    255       config->setFeatureString(fv);
    256     }
    257   }
    258 
    259   if (OptPIC) {
    260     config->setRelocationModel(llvm::Reloc::PIC_);
    261 
    262     // For x86_64, CodeModel needs to be small if PIC_ reloc is used.
    263     // Otherwise, we end up with TEXTRELs in the shared library.
    264     if (config->getTriple().find("x86_64") != std::string::npos) {
    265         config->setCodeModel(llvm::CodeModel::Small);
    266     }
    267   }
    268   switch (OptOptLevel) {
    269     case '0': config->setOptimizationLevel(llvm::CodeGenOpt::None); break;
    270     case '1': config->setOptimizationLevel(llvm::CodeGenOpt::Less); break;
    271     case '2': config->setOptimizationLevel(llvm::CodeGenOpt::Default); break;
    272     case '3':
    273     default: {
    274       config->setOptimizationLevel(llvm::CodeGenOpt::Aggressive);
    275       break;
    276     }
    277   }
    278 
    279   pRSCD.setConfig(config);
    280   Compiler::ErrorCode result = RSC->config(*config);
    281 
    282   if (OptRSDebugContext) {
    283     pRSCD.setDebugContext(true);
    284   }
    285 
    286   if (OptRSGlobalInfo) {
    287     pRSCD.setEmbedGlobalInfo(true);
    288   }
    289 
    290   if (OptRSGlobalInfoSkipConstant) {
    291     pRSCD.setEmbedGlobalInfoSkipConstant(true);
    292   }
    293 
    294   if (result != Compiler::kSuccess) {
    295     llvm::errs() << "Failed to configure the compiler! (detail: "
    296                  << Compiler::GetErrorString(result) << ")\n";
    297     return false;
    298   }
    299 
    300   return true;
    301 }
    302 
    303 int main(int argc, char **argv) {
    304 
    305   llvm::llvm_shutdown_obj Y;
    306   init::Initialize();
    307   llvm::cl::SetVersionPrinter(BCCVersionPrinter);
    308   llvm::cl::ParseCommandLineOptions(argc, argv);
    309 
    310   BCCContext context;
    311   RSCompilerDriver RSCD;
    312 
    313   if (OptBCLibFilename.empty()) {
    314     ALOGE("Failed to compile bitcode, -bclib was not specified");
    315     return EXIT_FAILURE;
    316   }
    317 
    318   if (!ConfigCompiler(RSCD)) {
    319     ALOGE("Failed to configure compiler");
    320     return EXIT_FAILURE;
    321   }
    322 
    323   // Attempt to dynamically initialize the compiler driver if such a function
    324   // is present. It is only present if passed via "-load libFOO.so".
    325   RSCompilerDriverInit_t rscdi = (RSCompilerDriverInit_t)
    326       dlsym(RTLD_DEFAULT, STR(RS_COMPILER_DRIVER_INIT_FN));
    327   if (rscdi != nullptr) {
    328     rscdi(&RSCD);
    329   }
    330 
    331   if (OptMergePlans.size() > 0) {
    332     bool success = compileScriptGroup(context, RSCD);
    333 
    334     if (!success) {
    335       return EXIT_FAILURE;
    336     }
    337 
    338     return EXIT_SUCCESS;
    339   }
    340 
    341   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> mb_or_error =
    342       llvm::MemoryBuffer::getFile(OptInputFilenames[0].c_str());
    343   if (mb_or_error.getError()) {
    344     ALOGE("Failed to load bitcode from path %s! (%s)",
    345           OptInputFilenames[0].c_str(), mb_or_error.getError().message().c_str());
    346     return EXIT_FAILURE;
    347   }
    348   std::unique_ptr<llvm::MemoryBuffer> input_data = std::move(mb_or_error.get());
    349 
    350   const char *bitcode = input_data->getBufferStart();
    351   size_t bitcodeSize = input_data->getBufferSize();
    352 
    353   if (!OptEmbedRSInfo) {
    354     bool built = RSCD.build(context, OptOutputPath.c_str(),
    355                             OptOutputFilename.c_str(),
    356                             bitcode, bitcodeSize,
    357                             OptChecksum.c_str(), OptBCLibFilename.c_str(),
    358                             nullptr, OptEmitLLVM);
    359 
    360     if (!built) {
    361       return EXIT_FAILURE;
    362     }
    363   } else {
    364     // embedRSInfo is set.  Use buildForCompatLib to embed RS symbol information
    365     // into the .rs.info symbol.
    366     Source *source = Source::CreateFromBuffer(context, OptInputFilenames[0].c_str(),
    367                                               bitcode, bitcodeSize);
    368     RSScript *s = new (std::nothrow) RSScript(*source);
    369     if (s == nullptr) {
    370       llvm::errs() << "Out of memory when creating script for file `"
    371                    << OptInputFilenames[0] << "'!\n";
    372       delete source;
    373       return EXIT_FAILURE;
    374     }
    375 
    376     llvm::SmallString<80> output(OptOutputPath);
    377     llvm::sys::path::append(output, "/", OptOutputFilename);
    378     llvm::sys::path::replace_extension(output, ".o");
    379 
    380     if (!RSCD.buildForCompatLib(*s, output.c_str(), OptChecksum.c_str(),
    381                                 OptBCLibFilename.c_str(), OptEmitLLVM)) {
    382       fprintf(stderr, "Failed to compile script!");
    383       return EXIT_FAILURE;
    384     }
    385   }
    386 
    387   return EXIT_SUCCESS;
    388 }
    389