Main.cpp revision 0ab50b835805c75ad164466767c2c212f48954cc
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/CompilerConfig.h>
45#include <bcc/Support/Initialization.h>
46#include <bcc/Support/InputFile.h>
47#include <bcc/Support/OutputFile.h>
48
49using namespace bcc;
50
51#define STR2(a) #a
52#define STR(a) STR2(a)
53
54//===----------------------------------------------------------------------===//
55// General Options
56//===----------------------------------------------------------------------===//
57namespace {
58
59llvm::cl::list<std::string>
60OptInputFilenames(llvm::cl::Positional, llvm::cl::OneOrMore,
61                  llvm::cl::desc("<input bitcode files>"));
62
63llvm::cl::list<std::string>
64OptMergePlans("merge", llvm::cl::ZeroOrMore,
65               llvm::cl::desc("Lists of kernels to merge (as source-and-slot "
66                              "pairs) and names for the final merged kernels"));
67
68llvm::cl::list<std::string>
69OptInvokes("invoke", llvm::cl::ZeroOrMore,
70           llvm::cl::desc("Invocable functions"));
71
72llvm::cl::opt<std::string>
73OptOutputFilename("o", llvm::cl::desc("Specify the output filename"),
74                  llvm::cl::value_desc("filename"),
75                  llvm::cl::init("bcc_output"));
76
77llvm::cl::opt<std::string>
78OptBCLibFilename("bclib", llvm::cl::desc("Specify the bclib filename"),
79                 llvm::cl::value_desc("bclib"));
80
81llvm::cl::opt<std::string>
82OptOutputPath("output_path", llvm::cl::desc("Specify the output path"),
83              llvm::cl::value_desc("output path"),
84              llvm::cl::init("."));
85
86llvm::cl::opt<bool>
87OptEmitLLVM("emit-llvm",
88            llvm::cl::desc("Emit an LLVM-IR version of the generated program"));
89
90llvm::cl::opt<std::string>
91OptTargetTriple("mtriple",
92                llvm::cl::desc("Specify the target triple (default: "
93                               DEFAULT_TARGET_TRIPLE_STRING ")"),
94                llvm::cl::init(DEFAULT_TARGET_TRIPLE_STRING),
95                llvm::cl::value_desc("triple"));
96
97llvm::cl::alias OptTargetTripleC("C", llvm::cl::NotHidden,
98                                 llvm::cl::desc("Alias for -mtriple"),
99                                 llvm::cl::aliasopt(OptTargetTriple));
100
101llvm::cl::opt<bool>
102OptRSDebugContext("rs-debug-ctx",
103    llvm::cl::desc("Enable build to work with a RenderScript debug context"));
104
105llvm::cl::opt<std::string>
106OptChecksum("build-checksum",
107            llvm::cl::desc("Embed a checksum of this compiler invocation for"
108                           " cache invalidation at a later time"),
109            llvm::cl::value_desc("checksum"));
110
111//===----------------------------------------------------------------------===//
112// Compiler Options
113//===----------------------------------------------------------------------===//
114llvm::cl::opt<bool>
115OptPIC("fPIC", llvm::cl::desc("Generate fully relocatable, position independent"
116                              " code"));
117
118// If set, use buildForCompatLib to embed RS symbol information into the object
119// file.  The information is stored in the .rs.info variable.  This option is
120// to be used in tandem with -fPIC.
121llvm::cl::opt<bool>
122OptEmbedRSInfo("embedRSInfo",
123    llvm::cl::desc("Embed RS Info into the object file instead of generating"
124                   " a separate .o.info file"));
125
126// RenderScript uses -O3 by default
127llvm::cl::opt<char>
128OptOptLevel("O", llvm::cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
129                                "(default: -O3)"),
130            llvm::cl::Prefix, llvm::cl::ZeroOrMore, llvm::cl::init('3'));
131
132// Override "bcc -version" since the LLVM version information is not correct on
133// Android build.
134void BCCVersionPrinter() {
135  llvm::raw_ostream &os = llvm::outs();
136  os << "libbcc (The Android Open Source Project, http://www.android.com/):\n"
137     << "  Default target: " << DEFAULT_TARGET_TRIPLE_STRING << "\n\n"
138     << "LLVM (http://llvm.org/):\n"
139     << "  Version: " << PACKAGE_VERSION << "\n";
140  return;
141}
142
143void extractSourcesAndSlots(const llvm::cl::list<std::string>& optList,
144                            std::list<std::string>* batchNames,
145                            std::list<std::list<std::pair<int, int>>>* sourcesAndSlots) {
146  for (unsigned i = 0; i < optList.size(); ++i) {
147    std::string plan = optList[i];
148    unsigned found = plan.find(":");
149
150    std::string name = plan.substr(0, found);
151    std::cerr << "new kernel name: " << name << std::endl;
152    batchNames->push_back(name);
153
154    std::istringstream iss(plan.substr(found + 1));
155    std::string s;
156    std::list<std::pair<int, int>> planList;
157    while (getline(iss, s, '.')) {
158      found = s.find(",");
159      std::string sourceStr = s.substr(0, found);
160      std::string slotStr = s.substr(found + 1);
161
162      std::cerr << "source " << sourceStr << ", slot " << slotStr << std::endl;
163
164      int source = std::stoi(sourceStr);
165      int slot = std::stoi(slotStr);
166      planList.push_back(std::make_pair(source, slot));
167    }
168
169    sourcesAndSlots->push_back(planList);
170  }
171}
172
173bool compileScriptGroup(BCCContext& Context, RSCompilerDriver& RSCD) {
174  std::vector<bcc::Source*> sources;
175  for (unsigned i = 0; i < OptInputFilenames.size(); ++i) {
176    bcc::Source* source =
177        bcc::Source::CreateFromFile(Context, OptInputFilenames[i]);
178    if (!source) {
179      llvm::errs() << "Error loading file '" << OptInputFilenames[i]<< "'\n";
180      return false;
181    }
182    sources.push_back(source);
183  }
184
185  std::list<std::string> fusedKernelNames;
186  std::list<std::list<std::pair<int, int>>> sourcesAndSlots;
187  extractSourcesAndSlots(OptMergePlans, &fusedKernelNames, &sourcesAndSlots);
188
189  std::list<std::string> invokeBatchNames;
190  std::list<std::list<std::pair<int, int>>> invokeSourcesAndSlots;
191  extractSourcesAndSlots(OptInvokes, &invokeBatchNames, &invokeSourcesAndSlots);
192
193  std::string outputFilepath(OptOutputPath);
194  outputFilepath.append("/");
195  outputFilepath.append(OptOutputFilename);
196
197  bool success = RSCD.buildScriptGroup(
198    Context, outputFilepath.c_str(), OptBCLibFilename.c_str(), true,
199    sources, sourcesAndSlots, fusedKernelNames,
200    invokeSourcesAndSlots, invokeBatchNames);
201
202  return success;
203}
204
205} // end anonymous namespace
206
207static inline
208bool ConfigCompiler(RSCompilerDriver &pRSCD) {
209  Compiler *RSC = pRSCD.getCompiler();
210  CompilerConfig *config = nullptr;
211
212  config = new (std::nothrow) CompilerConfig(OptTargetTriple);
213  if (config == nullptr) {
214    llvm::errs() << "Out of memory when create the compiler configuration!\n";
215    return false;
216  }
217
218  // llvm3.5 has removed the auto-detect feature for x86 subtarget,
219  // so set features explicitly in bcc.
220  if ((config->getTriple().find("i686") != std::string::npos) ||
221    (config->getTriple().find("x86_64") != std::string::npos)) {
222    std::vector<std::string> fv;
223
224#if defined(__SSE3__)
225    fv.push_back("+sse3");
226#endif
227#if defined(__SSSE3__)
228    fv.push_back("+ssse3");
229#endif
230#if defined(__SSE4_1__)
231    fv.push_back("+sse4.1");
232#endif
233#if defined(__SSE4_2__)
234    fv.push_back("+sse4.2");
235#endif
236
237    if (fv.size()) {
238      config->setFeatureString(fv);
239    }
240  }
241
242  if (OptPIC) {
243    config->setRelocationModel(llvm::Reloc::PIC_);
244
245    // For x86_64, CodeModel needs to be small if PIC_ reloc is used.
246    // Otherwise, we end up with TEXTRELs in the shared library.
247    if (config->getTriple().find("x86_64") != std::string::npos) {
248        config->setCodeModel(llvm::CodeModel::Small);
249    }
250  }
251  switch (OptOptLevel) {
252    case '0': config->setOptimizationLevel(llvm::CodeGenOpt::None); break;
253    case '1': config->setOptimizationLevel(llvm::CodeGenOpt::Less); break;
254    case '2': config->setOptimizationLevel(llvm::CodeGenOpt::Default); break;
255    case '3':
256    default: {
257      config->setOptimizationLevel(llvm::CodeGenOpt::Aggressive);
258      break;
259    }
260  }
261
262  pRSCD.setConfig(config);
263  Compiler::ErrorCode result = RSC->config(*config);
264
265  if (OptRSDebugContext) {
266    pRSCD.setDebugContext(true);
267  }
268
269  if (result != Compiler::kSuccess) {
270    llvm::errs() << "Failed to configure the compiler! (detail: "
271                 << Compiler::GetErrorString(result) << ")\n";
272    return false;
273  }
274
275  return true;
276}
277
278int main(int argc, char **argv) {
279
280  llvm::llvm_shutdown_obj Y;
281  init::Initialize();
282  llvm::cl::SetVersionPrinter(BCCVersionPrinter);
283  llvm::cl::ParseCommandLineOptions(argc, argv);
284  std::string commandLine = bcc::getCommandLine(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(), OptOutputFilename.c_str(), bitcode,
331                            bitcodeSize, commandLine.c_str(), OptChecksum.c_str(), OptBCLibFilename.c_str(), nullptr,
332                            OptEmitLLVM);
333
334    if (!built) {
335      return EXIT_FAILURE;
336    }
337  } else {
338    // embedRSInfo is set.  Use buildForCompatLib to embed RS symbol information
339    // into the .rs.info symbol.
340    Source *source = Source::CreateFromBuffer(context, OptInputFilenames[0].c_str(),
341                                              bitcode, bitcodeSize);
342    RSScript *s = new (std::nothrow) RSScript(*source);
343    if (s == nullptr) {
344      llvm::errs() << "Out of memory when creating script for file `"
345                   << OptInputFilenames[0] << "'!\n";
346      delete source;
347      return EXIT_FAILURE;
348    }
349
350    llvm::SmallString<80> output(OptOutputPath);
351    llvm::sys::path::append(output, "/", OptOutputFilename);
352    llvm::sys::path::replace_extension(output, ".o");
353
354    if (!RSCD.buildForCompatLib(*s, output.c_str(), OptChecksum.c_str(),
355                                OptBCLibFilename.c_str(), OptEmitLLVM)) {
356      fprintf(stderr, "Failed to compile script!");
357      return EXIT_FAILURE;
358    }
359  }
360
361  return EXIT_SUCCESS;
362}
363