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
50using namespace bcc;
51
52#define STR2(a) #a
53#define STR(a) STR2(a)
54
55//===----------------------------------------------------------------------===//
56// General Options
57//===----------------------------------------------------------------------===//
58namespace {
59
60llvm::cl::list<std::string>
61OptInputFilenames(llvm::cl::Positional, llvm::cl::OneOrMore,
62                  llvm::cl::desc("<input bitcode files>"));
63
64llvm::cl::list<std::string>
65OptMergePlans("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
69llvm::cl::list<std::string>
70OptInvokes("invoke", llvm::cl::ZeroOrMore,
71           llvm::cl::desc("Invocable functions"));
72
73llvm::cl::opt<std::string>
74OptOutputFilename("o", llvm::cl::desc("Specify the output filename"),
75                  llvm::cl::value_desc("filename"),
76                  llvm::cl::init("bcc_output"));
77
78llvm::cl::opt<std::string>
79OptBCLibFilename("bclib", llvm::cl::desc("Specify the bclib filename"),
80                 llvm::cl::value_desc("bclib"));
81
82llvm::cl::opt<std::string>
83OptBCLibRelaxedFilename("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
88llvm::cl::opt<std::string>
89OptOutputPath("output_path", llvm::cl::desc("Specify the output path"),
90              llvm::cl::value_desc("output path"),
91              llvm::cl::init("."));
92
93llvm::cl::opt<bool>
94OptEmitLLVM("emit-llvm",
95            llvm::cl::desc("Emit an LLVM-IR version of the generated program"));
96
97llvm::cl::opt<std::string>
98OptTargetTriple("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
104llvm::cl::alias OptTargetTripleC("C", llvm::cl::NotHidden,
105                                 llvm::cl::desc("Alias for -mtriple"),
106                                 llvm::cl::aliasopt(OptTargetTriple));
107
108llvm::cl::opt<bool>
109OptRSDebugContext("rs-debug-ctx",
110    llvm::cl::desc("Enable build to work with a RenderScript debug context"));
111
112llvm::cl::opt<bool>
113OptRSGlobalInfo("rs-global-info",
114    llvm::cl::desc("Embed information about global variables in the code"));
115
116llvm::cl::opt<bool>
117OptRSGlobalInfoSkipConstant("rs-global-info-skip-constant",
118    llvm::cl::desc("Skip embedding information about constant global "
119                   "variables in the code"));
120
121llvm::cl::opt<std::string>
122OptChecksum("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//===----------------------------------------------------------------------===//
130llvm::cl::opt<bool>
131OptPIC("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.
137llvm::cl::opt<bool>
138OptEmbedRSInfo("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
143llvm::cl::opt<char>
144OptOptLevel("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.
150void 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
159void 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
189bool 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
224static inline
225bool 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  if (OptPIC) {
236    config->setRelocationModel(llvm::Reloc::PIC_);
237
238    // For x86_64, CodeModel needs to be small if PIC_ reloc is used.
239    // Otherwise, we end up with TEXTRELs in the shared library.
240    if (config->getTriple().find("x86_64") != std::string::npos) {
241        config->setCodeModel(llvm::CodeModel::Small);
242    }
243  }
244  switch (OptOptLevel) {
245    case '0': config->setOptimizationLevel(llvm::CodeGenOpt::None); break;
246    case '1': config->setOptimizationLevel(llvm::CodeGenOpt::Less); break;
247    case '2': config->setOptimizationLevel(llvm::CodeGenOpt::Default); break;
248    case '3':
249    default: {
250      config->setOptimizationLevel(llvm::CodeGenOpt::Aggressive);
251      break;
252    }
253  }
254
255  pRSCD.setConfig(config);
256  Compiler::ErrorCode result = RSC->config(*config);
257
258  if (OptRSDebugContext) {
259    pRSCD.setDebugContext(true);
260  }
261
262  if (OptRSGlobalInfo) {
263    pRSCD.setEmbedGlobalInfo(true);
264  }
265
266  if (OptRSGlobalInfoSkipConstant) {
267    pRSCD.setEmbedGlobalInfoSkipConstant(true);
268  }
269
270  if (result != Compiler::kSuccess) {
271    llvm::errs() << "Failed to configure the compiler! (detail: "
272                 << Compiler::GetErrorString(result) << ")\n";
273    return false;
274  }
275
276  return true;
277}
278
279int main(int argc, char **argv) {
280
281  llvm::llvm_shutdown_obj Y;
282  init::Initialize();
283  llvm::cl::SetVersionPrinter(BCCVersionPrinter);
284  llvm::cl::ParseCommandLineOptions(argc, argv);
285
286  BCCContext context;
287  RSCompilerDriver RSCD;
288
289  if (OptBCLibFilename.empty()) {
290    ALOGE("Failed to compile bitcode, -bclib was not specified");
291    return EXIT_FAILURE;
292  }
293
294  if (!ConfigCompiler(RSCD)) {
295    ALOGE("Failed to configure compiler");
296    return EXIT_FAILURE;
297  }
298
299  // Attempt to dynamically initialize the compiler driver if such a function
300  // is present. It is only present if passed via "-load libFOO.so".
301  RSCompilerDriverInit_t rscdi = (RSCompilerDriverInit_t)
302      dlsym(RTLD_DEFAULT, STR(RS_COMPILER_DRIVER_INIT_FN));
303  if (rscdi != nullptr) {
304    rscdi(&RSCD);
305  }
306
307  if (OptMergePlans.size() > 0) {
308    bool success = compileScriptGroup(context, RSCD);
309
310    if (!success) {
311      return EXIT_FAILURE;
312    }
313
314    return EXIT_SUCCESS;
315  }
316
317  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> mb_or_error =
318      llvm::MemoryBuffer::getFile(OptInputFilenames[0].c_str());
319  if (mb_or_error.getError()) {
320    ALOGE("Failed to load bitcode from path %s! (%s)",
321          OptInputFilenames[0].c_str(), mb_or_error.getError().message().c_str());
322    return EXIT_FAILURE;
323  }
324  std::unique_ptr<llvm::MemoryBuffer> input_data = std::move(mb_or_error.get());
325
326  const char *bitcode = input_data->getBufferStart();
327  size_t bitcodeSize = input_data->getBufferSize();
328
329  if (!OptEmbedRSInfo) {
330    bool built = RSCD.build(context, OptOutputPath.c_str(),
331                            OptOutputFilename.c_str(),
332                            bitcode, bitcodeSize,
333                            OptChecksum.c_str(), OptBCLibFilename.c_str(),
334                            nullptr, OptEmitLLVM);
335
336    if (!built) {
337      return EXIT_FAILURE;
338    }
339  } else {
340    // embedRSInfo is set.  Use buildForCompatLib to embed RS symbol information
341    // into the .rs.info symbol.
342    Source *source = Source::CreateFromBuffer(context, OptInputFilenames[0].c_str(),
343                                              bitcode, bitcodeSize);
344
345    // If the bitcode fails verification in the bitcode loader, the returned Source is set to NULL.
346    if (!source) {
347      ALOGE("Failed to load source from file %s", OptInputFilenames[0].c_str());
348      return EXIT_FAILURE;
349    }
350
351    std::unique_ptr<RSScript> s(new (std::nothrow) RSScript(*source, RSCD.getConfig()));
352    if (s == nullptr) {
353      llvm::errs() << "Out of memory when creating script for file `"
354                   << OptInputFilenames[0] << "'!\n";
355      delete source;
356      return EXIT_FAILURE;
357    }
358
359    llvm::SmallString<80> output(OptOutputPath);
360    llvm::sys::path::append(output, "/", OptOutputFilename);
361    llvm::sys::path::replace_extension(output, ".o");
362
363    if (!RSCD.buildForCompatLib(*s, output.c_str(), OptChecksum.c_str(),
364                                OptBCLibFilename.c_str(), OptEmitLLVM)) {
365      fprintf(stderr, "Failed to compile script!");
366      return EXIT_FAILURE;
367    }
368  }
369
370  return EXIT_SUCCESS;
371}
372