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 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
main(int argc,char ** argv)279 int 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