/*
* 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;
}