/* * Copyright 2010, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "llvm/Analysis/Verifier.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/Linker.h" #include "llvm/LLVMContext.h" #include "llvm/Metadata.h" #include "llvm/Module.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/StandardPasses.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetData.h" #include "slang_rs_metadata.h" using llvm::errs; using llvm::LLVMContext; using llvm::MemoryBuffer; using llvm::Module; static llvm::cl::list InputFilenames(llvm::cl::Positional, llvm::cl::OneOrMore, llvm::cl::desc("")); static llvm::cl::list OutputFilenames("o", llvm::cl::desc("Override output filename"), llvm::cl::value_desc("")); static llvm::cl::opt NoStdLib("nostdlib", llvm::cl::desc("Don't link RS default libraries")); static llvm::cl::list AdditionalLibs("l", llvm::cl::Prefix, llvm::cl::desc("Specify additional libraries to link to"), llvm::cl::value_desc("")); static bool GetExportSymbolNames(llvm::NamedMDNode *N, unsigned NameOpIdx, std::vector &Names) { if (N == NULL) return true; for (unsigned i = 0, e = N->getNumOperands(); i != e; ++i) { llvm::MDNode *V = N->getOperand(i); if (V == NULL) continue; if (V->getNumOperands() < (NameOpIdx + 1)) { errs() << "Invalid metadata spec of " << N->getName() << " in RenderScript executable. (#op)\n"; return false; } llvm::MDString *Name = llvm::dyn_cast(V->getOperand(NameOpIdx)); if (Name == NULL) { errs() << "Invalid metadata spec of " << N->getName() << " in RenderScript executable. (#name)\n"; return false; } Names.push_back(Name->getString().data()); } return true; } static bool GetExportSymbols(Module *M, std::vector &Names) { bool Result = true; // Variables marked as export must be externally visible if (llvm::NamedMDNode *EV = M->getNamedMetadata(RS_EXPORT_VAR_MN)) Result |= GetExportSymbolNames(EV, RS_EXPORT_VAR_NAME, Names); // So are those exported functions if (llvm::NamedMDNode *EF = M->getNamedMetadata(RS_EXPORT_FUNC_MN)) Result |= GetExportSymbolNames(EF, RS_EXPORT_FUNC_NAME, Names); return Result; } static inline MemoryBuffer *LoadFileIntoMemory(const std::string &F) { std::string Err; MemoryBuffer *MB = MemoryBuffer::getFile(F, &Err); if (MB == NULL) errs() << "Failed to load `" << F << "' (" << Err << ")\n"; return MB; } static inline Module *ParseBitcodeFromMemoryBuffer(MemoryBuffer *MB, LLVMContext& Context) { std::string Err; Module *M = ParseBitcodeFile(MB, Context, &Err); if (M == NULL) errs() << "Corrupted bitcode file `" << MB->getBufferIdentifier() << "' (" << Err << ")\n"; return M; } // LoadBitcodeFile - Read the specified bitcode file in and return it. static inline Module *LoadBitcodeFile(const std::string &F, LLVMContext& Context) { MemoryBuffer *MB = LoadFileIntoMemory(F); if (MB == NULL) return NULL; Module *M = ParseBitcodeFromMemoryBuffer(MB, Context); if (M == NULL) delete MB; return M; } extern const char rslib_bc[]; extern unsigned rslib_bc_size; static bool PreloadLibraries(bool NoStdLib, const std::vector &AdditionalLibs, std::list &LibBitcode) { MemoryBuffer *MB; LibBitcode.clear(); if (!NoStdLib) { // rslib.bc MB = MemoryBuffer::getMemBuffer(llvm::StringRef(rslib_bc, rslib_bc_size), "rslib.bc"); if (MB == NULL) { errs() << "Failed to load (in-memory) `rslib.bc'!\n"; return false; } LibBitcode.push_back(MB); } // Load additional libraries for (std::vector::const_iterator I = AdditionalLibs.begin(), E = AdditionalLibs.end(); I != E; I++) { MB = LoadFileIntoMemory(*I); if (MB == NULL) return false; LibBitcode.push_back(MB); } return true; } static void UnloadLibraries(std::list& LibBitcode) { for (std::list::iterator I = LibBitcode.begin(), E = LibBitcode.end(); I != E; I++) delete *I; LibBitcode.clear(); return; } Module *PerformLinking(const std::string &InputFile, const std::list &LibBitcode, LLVMContext &Context) { std::string Err; std::auto_ptr Composite(LoadBitcodeFile(InputFile, Context)); if (Composite.get() == NULL) return NULL; for (std::list::const_iterator I = LibBitcode.begin(), E = LibBitcode.end(); I != E; I++) { Module *Lib = ParseBitcodeFromMemoryBuffer(*I, Context); if (Lib == NULL) return NULL; if (llvm::Linker::LinkModules(Composite.get(), Lib, &Err)) { errs() << "Failed to link `" << InputFile << "' with library bitcode `" << (*I)->getBufferIdentifier() << "' (" << Err << ")\n"; return NULL; } } return Composite.release(); } bool OptimizeModule(Module *M) { llvm::PassManager Passes; const std::string &ModuleDataLayout = M->getDataLayout(); if (!ModuleDataLayout.empty()) if (llvm::TargetData *TD = new llvm::TargetData(ModuleDataLayout)) Passes.add(TD); // Some symbols must not be internalized std::vector ExportList; ExportList.push_back("init"); ExportList.push_back("root"); if (!GetExportSymbols(M, ExportList)) { return false; } Passes.add(llvm::createInternalizePass(ExportList)); // TODO(zonr): Do we need to run all LTO passes? createStandardLTOPasses(&Passes, /* Internalize = */false, /* RunInliner = */true, /* VerifyEach = */false); Passes.run(*M); return true; } int main(int argc, char **argv) { llvm::llvm_shutdown_obj X; // Call llvm_shutdown() on exit. llvm::cl::ParseCommandLineOptions(argc, argv, "llvm-rs-link\n"); std::list LibBitcode; if (!PreloadLibraries(NoStdLib, AdditionalLibs, LibBitcode)) return 1; // No libraries specified to be linked if (LibBitcode.size() == 0) return 0; LLVMContext &Context = llvm::getGlobalContext(); bool HasError = true; std::string Err; for (unsigned i = 0, e = InputFilenames.size(); i != e; i++) { std::auto_ptr Linked( PerformLinking(InputFilenames[i], LibBitcode, Context)); // Failed to link with InputFilenames[i] with LibBitcode if (Linked.get() == NULL) break; // Verify linked module if (verifyModule(*Linked, llvm::ReturnStatusAction, &Err)) { errs() << InputFilenames[i] << " linked, but does not verify as " "correct! (" << Err << ")\n"; break; } if (!OptimizeModule(Linked.get())) break; // Write out the module llvm::tool_output_file Out(InputFilenames[i].c_str(), Err, llvm::raw_fd_ostream::F_Binary); if (!Err.empty()) { errs() << InputFilenames[i] << " linked, but failed to write out! " "(" << Err << ")\n"; break; } WriteBitcodeToFile(Linked.get(), Out); Out.keep(); Linked.reset(); if (i == (InputFilenames.size() - 1)) // This is the last file and no error occured. HasError = false; } UnloadLibraries(LibBitcode); return HasError; }