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