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.
BCCVersionPrinter()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
extractSourcesAndSlots(const llvm::cl::list<std::string> & optList,std::list<std::string> * batchNames,std::list<std::list<std::pair<int,int>>> * sourcesAndSlots)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
compileScriptGroup(BCCContext & Context,RSCompilerDriver & RSCD)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
ConfigCompiler(RSCompilerDriver & pRSCD)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
main(int argc,char ** argv)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