| /* |
| * 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 <list> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #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/raw_ostream.h" |
| #include "llvm/Support/ToolOutputFile.h" |
| #include "llvm/Support/system_error.h" |
| |
| #include "llvm/PassManager.h" |
| #include "llvm/Transforms/IPO.h" |
| #include "llvm/Transforms/IPO/PassManagerBuilder.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<std::string> |
| InputFilenames(llvm::cl::Positional, llvm::cl::OneOrMore, |
| llvm::cl::desc("<input bitcode files>")); |
| |
| static llvm::cl::list<std::string> |
| OutputFilenames("o", llvm::cl::desc("Override output filename"), |
| llvm::cl::value_desc("<output bitcode file>")); |
| |
| static llvm::cl::opt<bool> |
| NoStdLib("nostdlib", llvm::cl::desc("Don't link RS default libraries")); |
| |
| static llvm::cl::list<std::string> |
| AdditionalLibs("l", llvm::cl::Prefix, |
| llvm::cl::desc("Specify additional libraries to link to"), |
| llvm::cl::value_desc("<library bitcode>")); |
| |
| static bool GetExportSymbolNames(llvm::NamedMDNode *N, |
| unsigned NameOpIdx, |
| std::vector<const char *> &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<llvm::MDString>(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<const char *> &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) { |
| llvm::OwningPtr<MemoryBuffer> MB; |
| |
| if (llvm::error_code EC = MemoryBuffer::getFile(F, MB)) { |
| errs() << "Failed to load `" << F << "' (" + EC.message() + ")\n"; |
| } |
| |
| return MB.take(); |
| } |
| |
| 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<std::string> &AdditionalLibs, |
| std::list<MemoryBuffer *> &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<std::string>::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<MemoryBuffer *>& LibBitcode) { |
| for (std::list<MemoryBuffer *>::iterator |
| I = LibBitcode.begin(), E = LibBitcode.end(); |
| I != E; |
| I++) |
| delete *I; |
| LibBitcode.clear(); |
| return; |
| } |
| |
| Module *PerformLinking(const std::string &InputFile, |
| const std::list<MemoryBuffer *> &LibBitcode, |
| LLVMContext &Context) { |
| std::string Err; |
| std::auto_ptr<Module> Composite(LoadBitcodeFile(InputFile, Context)); |
| |
| if (Composite.get() == NULL) |
| return NULL; |
| |
| for (std::list<MemoryBuffer *>::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, |
| llvm::Linker::DestroySource, &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<const char *> ExportList; |
| ExportList.push_back("init"); |
| ExportList.push_back("root"); |
| ExportList.push_back(".rs.dtor"); |
| |
| if (!GetExportSymbols(M, ExportList)) { |
| return false; |
| } |
| |
| Passes.add(llvm::createInternalizePass(ExportList)); |
| |
| // TODO(sliao): Do we need to run all LTO passes? |
| llvm::PassManagerBuilder PMBuilder; |
| PMBuilder.populateLTOPassManager(Passes, |
| /* Internalize = */false, |
| /* RunInliner = */true); |
| 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<MemoryBuffer *> 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<Module> 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.os()); |
| |
| Out.keep(); |
| Linked.reset(); |
| |
| if (i == (InputFilenames.size() - 1)) |
| // This is the last file and no error occured. |
| HasError = false; |
| } |
| |
| UnloadLibraries(LibBitcode); |
| |
| return HasError; |
| } |