99 changes: 99 additions & 0 deletions llvm/include/llvm/Object/FunctionIndexObjectFile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//===- FunctionIndexObjectFile.h - Function index file implementation -----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares the FunctionIndexObjectFile template class.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_OBJECT_FUNCTIONINDEXOBJECTFILE_H
#define LLVM_OBJECT_FUNCTIONINDEXOBJECTFILE_H

#include "llvm/Object/SymbolicFile.h"

namespace llvm {
class FunctionInfoIndex;

namespace object {
class ObjectFile;

/// This class is used to read just the function summary index related
/// sections out of the given object (which may contain a single module's
/// bitcode or be a combined index bitcode file). It builds a FunctionInfoIndex
/// object.
class FunctionIndexObjectFile : public SymbolicFile {
std::unique_ptr<FunctionInfoIndex> Index;

public:
FunctionIndexObjectFile(MemoryBufferRef Object,
std::unique_ptr<FunctionInfoIndex> I);
~FunctionIndexObjectFile() override;

// TODO: Walk through FunctionMap entries for function symbols.
// However, currently these interfaces are not used by any consumers.
void moveSymbolNext(DataRefImpl &Symb) const override {
llvm_unreachable("not implemented");
}
std::error_code printSymbolName(raw_ostream &OS,
DataRefImpl Symb) const override {
llvm_unreachable("not implemented");
return std::error_code();
}
uint32_t getSymbolFlags(DataRefImpl Symb) const override {
llvm_unreachable("not implemented");
return 0;
}
basic_symbol_iterator symbol_begin_impl() const override {
llvm_unreachable("not implemented");
return basic_symbol_iterator(BasicSymbolRef());
}
basic_symbol_iterator symbol_end_impl() const override {
llvm_unreachable("not implemented");
return basic_symbol_iterator(BasicSymbolRef());
}

const FunctionInfoIndex &getIndex() const {
return const_cast<FunctionIndexObjectFile *>(this)->getIndex();
}
FunctionInfoIndex &getIndex() { return *Index; }
std::unique_ptr<FunctionInfoIndex> takeIndex();

static inline bool classof(const Binary *v) { return v->isFunctionIndex(); }

/// \brief Finds and returns bitcode embedded in the given object file, or an
/// error code if not found.
static ErrorOr<MemoryBufferRef> findBitcodeInObject(const ObjectFile &Obj);

/// \brief Finds and returns bitcode in the given memory buffer (which may
/// be either a bitcode file or a native object file with embedded bitcode),
/// or an error code if not found.
static ErrorOr<MemoryBufferRef> findBitcodeInMemBuffer(
MemoryBufferRef Object);

/// \brief Looks for function summary in the given memory buffer,
/// returns true if found, else false.
static bool hasFunctionSummaryInMemBuffer(MemoryBufferRef Object,
LLVMContext &Context);

/// \brief Parse function index in the given memory buffer.
/// Return new FunctionIndexObjectFile instance containing parsed function
/// summary/index.
static ErrorOr<std::unique_ptr<FunctionIndexObjectFile>> create(
MemoryBufferRef Object, LLVMContext &Context, bool IsLazy = false);

/// \brief Parse the function summary information for function with the
/// given name out of the given buffer. Parsed information is
/// stored on the index object saved in this object.
std::error_code findFunctionSummaryInMemBuffer(MemoryBufferRef Object,
LLVMContext &Context,
StringRef FunctionName);
};
}
}

#endif
597 changes: 583 additions & 14 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp

Large diffs are not rendered by default.

324 changes: 303 additions & 21 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp

Large diffs are not rendered by default.

18 changes: 12 additions & 6 deletions llvm/lib/Bitcode/Writer/BitcodeWriterPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,29 @@
using namespace llvm;

PreservedAnalyses BitcodeWriterPass::run(Module &M) {
WriteBitcodeToFile(&M, OS, ShouldPreserveUseListOrder);
WriteBitcodeToFile(&M, OS, ShouldPreserveUseListOrder, EmitFunctionSummary);
return PreservedAnalyses::all();
}

namespace {
class WriteBitcodePass : public ModulePass {
raw_ostream &OS; // raw_ostream to print on
bool ShouldPreserveUseListOrder;
bool EmitFunctionSummary;

public:
static char ID; // Pass identification, replacement for typeid
explicit WriteBitcodePass(raw_ostream &o, bool ShouldPreserveUseListOrder)
explicit WriteBitcodePass(raw_ostream &o, bool ShouldPreserveUseListOrder,
bool EmitFunctionSummary)
: ModulePass(ID), OS(o),
ShouldPreserveUseListOrder(ShouldPreserveUseListOrder) {}
ShouldPreserveUseListOrder(ShouldPreserveUseListOrder),
EmitFunctionSummary(EmitFunctionSummary) {}

const char *getPassName() const override { return "Bitcode Writer"; }

bool runOnModule(Module &M) override {
WriteBitcodeToFile(&M, OS, ShouldPreserveUseListOrder);
WriteBitcodeToFile(&M, OS, ShouldPreserveUseListOrder,
EmitFunctionSummary);
return false;
}
};
Expand All @@ -46,6 +50,8 @@ namespace {
char WriteBitcodePass::ID = 0;

ModulePass *llvm::createBitcodeWriterPass(raw_ostream &Str,
bool ShouldPreserveUseListOrder) {
return new WriteBitcodePass(Str, ShouldPreserveUseListOrder);
bool ShouldPreserveUseListOrder,
bool EmitFunctionSummary) {
return new WriteBitcodePass(Str, ShouldPreserveUseListOrder,
EmitFunctionSummary);
}
1 change: 1 addition & 0 deletions llvm/lib/IR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ add_llvm_library(LLVMCore
PassManager.cpp
PassRegistry.cpp
Statepoint.cpp
FunctionInfo.cpp
Type.cpp
TypeFinder.cpp
Use.cpp
Expand Down
63 changes: 63 additions & 0 deletions llvm/lib/IR/FunctionInfo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//===-- FunctionInfo.cpp - Function Info Index ----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the function info index and summary classes for the
// IR library.
//
//===----------------------------------------------------------------------===//

#include "llvm/IR/FunctionInfo.h"
#include "llvm/ADT/StringMap.h"
using namespace llvm;

// Create the combined function index/summary from multiple
// per-module instances.
void FunctionInfoIndex::mergeFrom(std::unique_ptr<FunctionInfoIndex> Other,
uint64_t NextModuleId) {

StringRef ModPath;
for (auto &OtherFuncInfoLists : *Other) {
StringRef FuncName = OtherFuncInfoLists.getKey();
FunctionInfoList &List = OtherFuncInfoLists.second;

// Assert that the func info list only has one entry, since we shouldn't
// have duplicate names within a single per-module index.
assert(List.size() == 1);
std::unique_ptr<FunctionInfo> Info = std::move(List.front());

// Add the module path string ref for this module if we haven't already
// saved a reference to it.
if (ModPath.empty())
ModPath =
addModulePath(Info->functionSummary()->modulePath(), NextModuleId);
else
assert(ModPath == Info->functionSummary()->modulePath() &&
"Each module in the combined map should have a unique ID");

// Note the module path string ref was copied above and is still owned by
// the original per-module index. Reset it to the new module path
// string reference owned by the combined index.
Info->functionSummary()->setModulePath(ModPath);

// If it is a local function, rename it.
if (Info->functionSummary()->isLocalFunction()) {
// Any local functions are virtually renamed when being added to the
// combined index map, to disambiguate from other functions with
// the same name. The symbol table created for the combined index
// file should contain the renamed symbols.
FuncName =
FunctionInfoIndex::getGlobalNameForLocal(FuncName, NextModuleId);
}

// Add new function info to existing list. There may be duplicates when
// combining FunctionMap entries, due to COMDAT functions. Any local
// functions were virtually renamed above.
addFunctionInfo(FuncName, std::move(Info));
}
}
1 change: 1 addition & 0 deletions llvm/lib/Object/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ add_llvm_library(LLVMObject
RecordStreamer.cpp
SymbolicFile.cpp
SymbolSize.cpp
FunctionIndexObjectFile.cpp

ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/Object
Expand Down
114 changes: 114 additions & 0 deletions llvm/lib/Object/FunctionIndexObjectFile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//===- FunctionIndexObjectFile.cpp - Function index file implementation ----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Part of the FunctionIndexObjectFile class implementation.
//
//===----------------------------------------------------------------------===//

#include "llvm/Object/FunctionIndexObjectFile.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/IR/FunctionInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace object;

FunctionIndexObjectFile::FunctionIndexObjectFile(
MemoryBufferRef Object, std::unique_ptr<FunctionInfoIndex> I)
: SymbolicFile(Binary::ID_FunctionIndex, Object), Index(std::move(I)) {}

FunctionIndexObjectFile::~FunctionIndexObjectFile() {}

std::unique_ptr<FunctionInfoIndex> FunctionIndexObjectFile::takeIndex() {
return std::move(Index);
}

ErrorOr<MemoryBufferRef> FunctionIndexObjectFile::findBitcodeInObject(
const ObjectFile &Obj) {
for (const SectionRef &Sec : Obj.sections()) {
StringRef SecName;
if (std::error_code EC = Sec.getName(SecName)) return EC;
if (SecName == ".llvmbc") {
StringRef SecContents;
if (std::error_code EC = Sec.getContents(SecContents)) return EC;
return MemoryBufferRef(SecContents, Obj.getFileName());
}
}

return object_error::bitcode_section_not_found;
}

ErrorOr<MemoryBufferRef> FunctionIndexObjectFile::findBitcodeInMemBuffer(
MemoryBufferRef Object) {
sys::fs::file_magic Type = sys::fs::identify_magic(Object.getBuffer());
switch (Type) {
case sys::fs::file_magic::bitcode:
return Object;
case sys::fs::file_magic::elf_relocatable:
case sys::fs::file_magic::macho_object:
case sys::fs::file_magic::coff_object: {
ErrorOr<std::unique_ptr<ObjectFile>> ObjFile =
ObjectFile::createObjectFile(Object, Type);
if (!ObjFile) return ObjFile.getError();
return findBitcodeInObject(*ObjFile->get());
}
default:
return object_error::invalid_file_type;
}
}

// Looks for function index in the given memory buffer.
// returns true if found, else false.
bool FunctionIndexObjectFile::hasFunctionSummaryInMemBuffer(
MemoryBufferRef Object, LLVMContext &Context) {
ErrorOr<MemoryBufferRef> BCOrErr = findBitcodeInMemBuffer(Object);
if (!BCOrErr) return false;

return hasFunctionSummary(BCOrErr.get(), Context, nullptr);
}

// Parse function index in the given memory buffer.
// Return new FunctionIndexObjectFile instance containing parsed
// function summary/index.
ErrorOr<std::unique_ptr<FunctionIndexObjectFile>>
FunctionIndexObjectFile::create(MemoryBufferRef Object, LLVMContext &Context,
bool IsLazy) {
std::unique_ptr<FunctionInfoIndex> Index;

ErrorOr<MemoryBufferRef> BCOrErr = findBitcodeInMemBuffer(Object);
if (!BCOrErr) return BCOrErr.getError();

ErrorOr<std::unique_ptr<FunctionInfoIndex>> IOrErr =
getFunctionInfoIndex(BCOrErr.get(), Context, nullptr, IsLazy);

if (std::error_code EC = IOrErr.getError()) return EC;

Index = std::move(IOrErr.get());

return llvm::make_unique<FunctionIndexObjectFile>(Object, std::move(Index));
}

// Parse the function summary information for function with the
// given name out of the given buffer. Parsed information is
// stored on the index object saved in this object.
std::error_code FunctionIndexObjectFile::findFunctionSummaryInMemBuffer(
MemoryBufferRef Object, LLVMContext &Context, StringRef FunctionName) {
sys::fs::file_magic Type = sys::fs::identify_magic(Object.getBuffer());
switch (Type) {
case sys::fs::file_magic::bitcode: {
return readFunctionSummary(Object, Context, nullptr, FunctionName,
std::move(Index));
}
default:
return object_error::invalid_file_type;
}
}
45 changes: 45 additions & 0 deletions llvm/test/Bitcode/thinlto-function-summary.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
; RUN: llvm-as -function-summary < %s | llvm-bcanalyzer -dump | FileCheck %s -check-prefix=BC
; Check for function summary block/records.

; BC: <FUNCTION_SUMMARY_BLOCK
; BC-NEXT: <PERMODULE_ENTRY
; BC-NEXT: <PERMODULE_ENTRY
; BC-NEXT: <PERMODULE_ENTRY
; BC-NEXT: </FUNCTION_SUMMARY_BLOCK

; RUN: llvm-as -function-summary < %s | llvm-dis | FileCheck %s
; Check that this round-trips correctly.

; ModuleID = '<stdin>'
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; CHECK: define i32 @foo()

; Function Attrs: nounwind uwtable
define i32 @foo() #0 {
entry:
ret i32 1
}

; CHECK: define i32 @bar(i32 %x)

; Function Attrs: nounwind uwtable
define i32 @bar(i32 %x) #0 {
entry:
ret i32 %x
}

; Check an anonymous function as well, since in that case only the alias
; ends up in the value symbol table and having a summary.
@f = alias void (), void ()* @0 ; <void ()*> [#uses=0]
@h = external global void ()* ; <void ()*> [#uses=0]

define internal void @0() nounwind {
entry:
store void()* @0, void()** @h
br label %return

return: ; preds = %entry
ret void
}
4 changes: 4 additions & 0 deletions llvm/test/tools/gold/X86/Inputs/thinlto.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
define void @g() {
entry:
ret void
}
22 changes: 22 additions & 0 deletions llvm/test/tools/gold/X86/thinlto.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
; RUN: llvm-as -function-summary %s -o %t.o
; RUN: llvm-as -function-summary %p/Inputs/thinlto.ll -o %t2.o

; RUN: %gold -plugin %llvmshlibdir/LLVMgold.so \
; RUN: --plugin-opt=thinlto \
; RUN: -shared %t.o %t2.o -o %t3
; RUN: llvm-bcanalyzer -dump %t3.thinlto.bc | FileCheck %s --check-prefix=COMBINED
; RUN: not test -e %t3

; COMBINED: <MODULE_STRTAB_BLOCK
; COMBINED-NEXT: <ENTRY
; COMBINED-NEXT: <ENTRY
; COMBINED-NEXT: </MODULE_STRTAB_BLOCK
; COMBINED-NEXT: <FUNCTION_SUMMARY_BLOCK
; COMBINED-NEXT: <COMBINED_ENTRY
; COMBINED-NEXT: <COMBINED_ENTRY
; COMBINED-NEXT: </FUNCTION_SUMMARY_BLOCK

define void @f() {
entry:
ret void
}
64 changes: 64 additions & 0 deletions llvm/tools/gold/gold-plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "llvm/Linker/Linker.h"
#include "llvm/MC/SubtargetFeature.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Object/FunctionIndexObjectFile.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/ManagedStatic.h"
Expand Down Expand Up @@ -103,6 +104,10 @@ namespace options {
static std::string extra_library_path;
static std::string triple;
static std::string mcpu;
// When the thinlto plugin option is specified, only read the function
// the information from intermediate files and write a combined
// global index for the ThinLTO backends.
static bool thinlto = false;
// Additional options to pass into the code generator.
// Note: This array will contain all plugin options which are not claimed
// as plugin exclusive to pass to the code generator.
Expand Down Expand Up @@ -132,6 +137,8 @@ namespace options {
TheOutputType = OT_SAVE_TEMPS;
} else if (opt == "disable-output") {
TheOutputType = OT_DISABLE;
} else if (opt == "thinlto") {
thinlto = true;
} else if (opt.size() == 2 && opt[0] == 'O') {
if (opt[1] < '0' || opt[1] > '3')
message(LDPL_FATAL, "Optimization level must be between 0 and 3");
Expand Down Expand Up @@ -376,6 +383,10 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,

cf.handle = file->handle;

// If we are doing ThinLTO compilation, don't need to process the symbols.
// Later we simply build a combined index file after all files are claimed.
if (options::thinlto) return LDPS_OK;

for (auto &Sym : Obj->symbols()) {
uint32_t Symflags = Sym.getFlags();
if (shouldSkip(Symflags))
Expand Down Expand Up @@ -591,6 +602,30 @@ static void freeSymName(ld_plugin_symbol &Sym) {
Sym.comdat_key = nullptr;
}

static std::unique_ptr<FunctionInfoIndex> getFunctionIndexForFile(
LLVMContext &Context, claimed_file &F, ld_plugin_input_file &Info) {

if (get_symbols(F.handle, F.syms.size(), &F.syms[0]) != LDPS_OK)
message(LDPL_FATAL, "Failed to get symbol information");

const void *View;
if (get_view(F.handle, &View) != LDPS_OK)
message(LDPL_FATAL, "Failed to get a view of file");

MemoryBufferRef BufferRef(StringRef((const char *)View, Info.filesize),
Info.name);
ErrorOr<std::unique_ptr<object::FunctionIndexObjectFile>> ObjOrErr =
object::FunctionIndexObjectFile::create(BufferRef, Context);

if (std::error_code EC = ObjOrErr.getError())
message(LDPL_FATAL, "Could not read function index bitcode from file : %s",
EC.message().c_str());

object::FunctionIndexObjectFile &Obj = **ObjOrErr;

return Obj.takeIndex();
}

static std::unique_ptr<Module>
getModuleForFile(LLVMContext &Context, claimed_file &F,
ld_plugin_input_file &Info, raw_fd_ostream *ApiFile,
Expand Down Expand Up @@ -857,6 +892,35 @@ static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) {
LLVMContext Context;
Context.setDiagnosticHandler(diagnosticHandler, nullptr, true);

// If we are doing ThinLTO compilation, simply build the combined
// function index/summary and emit it. We don't need to parse the modules
// and link them in this case.
if (options::thinlto) {
std::unique_ptr<FunctionInfoIndex> CombinedIndex(new FunctionInfoIndex());
uint64_t NextModuleId = 0;
for (claimed_file &F : Modules) {
ld_plugin_input_file File;
if (get_input_file(F.handle, &File) != LDPS_OK)
message(LDPL_FATAL, "Failed to get file information");

std::unique_ptr<FunctionInfoIndex> Index =
getFunctionIndexForFile(Context, F, File);
CombinedIndex->mergeFrom(std::move(Index), ++NextModuleId);
}

std::error_code EC;
raw_fd_ostream OS(output_name + ".thinlto.bc", EC,
sys::fs::OpenFlags::F_None);
if (EC)
message(LDPL_FATAL, "Unable to open %s.thinlto.bc for writing: %s",
output_name.data(), EC.message().c_str());
WriteFunctionSummaryToFile(CombinedIndex.get(), OS);
OS.close();

cleanup_hook();
exit(0);
}

std::unique_ptr<Module> Combined(new Module("ld-temp.o", Context));
Linker L(Combined.get());

Expand Down
7 changes: 6 additions & 1 deletion llvm/tools/llvm-as/llvm-as.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ Force("f", cl::desc("Enable binary output on terminals"));
static cl::opt<bool>
DisableOutput("disable-output", cl::desc("Disable output"), cl::init(false));

static cl::opt<bool>
EmitFunctionSummary("function-summary", cl::desc("Emit function summary index"),
cl::init(false));

static cl::opt<bool>
DumpAsm("d", cl::desc("Print assembly as parsed"), cl::Hidden);

Expand Down Expand Up @@ -77,7 +81,8 @@ static void WriteOutputFile(const Module *M) {
}

if (Force || !CheckBitcodeOutputToConsole(Out->os(), true))
WriteBitcodeToFile(M, Out->os(), PreserveBitcodeUseListOrder);
WriteBitcodeToFile(M, Out->os(), PreserveBitcodeUseListOrder,
EmitFunctionSummary);

// Declare success.
Out->keep();
Expand Down
15 changes: 15 additions & 0 deletions llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ static const char *GetBlockName(unsigned BlockID,
case bitc::METADATA_BLOCK_ID: return "METADATA_BLOCK";
case bitc::METADATA_ATTACHMENT_ID: return "METADATA_ATTACHMENT_BLOCK";
case bitc::USELIST_BLOCK_ID: return "USELIST_BLOCK_ID";
case bitc::FUNCTION_SUMMARY_BLOCK_ID:
return "FUNCTION_SUMMARY_BLOCK";
case bitc::MODULE_STRTAB_BLOCK_ID: return "MODULE_STRTAB_BLOCK";
}
}

Expand Down Expand Up @@ -268,6 +271,18 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(VST_CODE, ENTRY)
STRINGIFY_CODE(VST_CODE, BBENTRY)
STRINGIFY_CODE(VST_CODE, FNENTRY)
STRINGIFY_CODE(VST_CODE, COMBINED_FNENTRY)
}
case bitc::MODULE_STRTAB_BLOCK_ID:
switch (CodeID) {
default: return nullptr;
STRINGIFY_CODE(MST_CODE, ENTRY)
}
case bitc::FUNCTION_SUMMARY_BLOCK_ID:
switch (CodeID) {
default: return nullptr;
STRINGIFY_CODE(FS_CODE, PERMODULE_ENTRY)
STRINGIFY_CODE(FS_CODE, COMBINED_ENTRY)
}
case bitc::METADATA_ATTACHMENT_ID:
switch(CodeID) {
Expand Down