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