From cc48854d5d51a2d7557f1040a61f160ad86c9729 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Tue, 24 Sep 2013 23:52:22 +0000 Subject: Move LTO support library to a component, allowing it to be tested more reliably across platforms. Patch by Tom Roeder! git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@191343 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/LTO/CMakeLists.txt | 4 + lib/LTO/LLVMBuild.txt | 22 ++ lib/LTO/LTOCodeGenerator.cpp | 453 ++++++++++++++++++++++ lib/LTO/LTOModule.cpp | 900 +++++++++++++++++++++++++++++++++++++++++++ lib/LTO/Makefile | 15 + 5 files changed, 1394 insertions(+) create mode 100644 lib/LTO/CMakeLists.txt create mode 100644 lib/LTO/LLVMBuild.txt create mode 100644 lib/LTO/LTOCodeGenerator.cpp create mode 100644 lib/LTO/LTOModule.cpp create mode 100644 lib/LTO/Makefile (limited to 'lib/LTO') diff --git a/lib/LTO/CMakeLists.txt b/lib/LTO/CMakeLists.txt new file mode 100644 index 0000000000..8e00bcb655 --- /dev/null +++ b/lib/LTO/CMakeLists.txt @@ -0,0 +1,4 @@ +add_llvm_library(LLVMLTO + LTOModule.cpp + LTOCodeGenerator.cpp + ) diff --git a/lib/LTO/LLVMBuild.txt b/lib/LTO/LLVMBuild.txt new file mode 100644 index 0000000000..38c1170b9e --- /dev/null +++ b/lib/LTO/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./lib/LTO/LLVMBuild.txt ----------------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = LTO +parent = Libraries +required_libraries = Analysis BitReader BitWriter Core IPO Linker MC MCParser Scalar Support Target Vectorize \ No newline at end of file diff --git a/lib/LTO/LTOCodeGenerator.cpp b/lib/LTO/LTOCodeGenerator.cpp new file mode 100644 index 0000000000..22e9754bcf --- /dev/null +++ b/lib/LTO/LTOCodeGenerator.cpp @@ -0,0 +1,453 @@ +//===-LTOCodeGenerator.cpp - LLVM Link Time Optimizer ---------------------===// +// +// 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 Link Time Optimization library. This library is +// intended to be used by linker to optimize code at link time. +// +//===----------------------------------------------------------------------===// + +#include "llvm/LTO/LTOCodeGenerator.h" +#include "llvm/LTO/LTOModule.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Analysis/Passes.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Config/config.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/InitializePasses.h" +#include "llvm/Linker.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/SubtargetFeature.h" +#include "llvm/PassManager.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/system_error.h" +#include "llvm/Target/TargetRegisterInfo.h" +#include "llvm/Target/Mangler.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/ObjCARC.h" +using namespace llvm; + +static cl::opt +DisableOpt("disable-opt", cl::init(false), + cl::desc("Do not run any optimization passes")); + +static cl::opt +DisableInline("disable-inlining", cl::init(false), + cl::desc("Do not run the inliner pass")); + +static cl::opt +DisableGVNLoadPRE("disable-gvn-loadpre", cl::init(false), + cl::desc("Do not run the GVN load PRE pass")); + +const char* LTOCodeGenerator::getVersionString() { +#ifdef LLVM_VERSION_INFO + return PACKAGE_NAME " version " PACKAGE_VERSION ", " LLVM_VERSION_INFO; +#else + return PACKAGE_NAME " version " PACKAGE_VERSION; +#endif +} + +LTOCodeGenerator::LTOCodeGenerator() + : Context(getGlobalContext()), Linker(new Module("ld-temp.o", Context)), + TargetMach(NULL), EmitDwarfDebugInfo(false), ScopeRestrictionsDone(false), + CodeModel(LTO_CODEGEN_PIC_MODEL_DYNAMIC), NativeObjectFile(NULL) { + initializeLTOPasses(); +} + +LTOCodeGenerator::~LTOCodeGenerator() { + delete TargetMach; + delete NativeObjectFile; + delete Linker.getModule(); + + for (std::vector::iterator I = CodegenOptions.begin(), + E = CodegenOptions.end(); + I != E; ++I) + free(*I); +} + +// Initialize LTO passes. Please keep this funciton in sync with +// PassManagerBuilder::populateLTOPassManager(), and make sure all LTO +// passes are initialized. +// +void LTOCodeGenerator::initializeLTOPasses() { + PassRegistry &R = *PassRegistry::getPassRegistry(); + + initializeInternalizePassPass(R); + initializeIPSCCPPass(R); + initializeGlobalOptPass(R); + initializeConstantMergePass(R); + initializeDAHPass(R); + initializeInstCombinerPass(R); + initializeSimpleInlinerPass(R); + initializePruneEHPass(R); + initializeGlobalDCEPass(R); + initializeArgPromotionPass(R); + initializeJumpThreadingPass(R); + initializeSROAPass(R); + initializeSROA_DTPass(R); + initializeSROA_SSAUpPass(R); + initializeFunctionAttrsPass(R); + initializeGlobalsModRefPass(R); + initializeLICMPass(R); + initializeGVNPass(R); + initializeMemCpyOptPass(R); + initializeDCEPass(R); + initializeCFGSimplifyPassPass(R); +} + +bool LTOCodeGenerator::addModule(LTOModule* mod, std::string& errMsg) { + bool ret = Linker.linkInModule(mod->getLLVVMModule(), &errMsg); + + const std::vector &undefs = mod->getAsmUndefinedRefs(); + for (int i = 0, e = undefs.size(); i != e; ++i) + AsmUndefinedRefs[undefs[i]] = 1; + + return !ret; +} + +void LTOCodeGenerator::setDebugInfo(lto_debug_model debug) { + switch (debug) { + case LTO_DEBUG_MODEL_NONE: + EmitDwarfDebugInfo = false; + return; + + case LTO_DEBUG_MODEL_DWARF: + EmitDwarfDebugInfo = true; + return; + } + llvm_unreachable("Unknown debug format!"); +} + +void LTOCodeGenerator::setCodePICModel(lto_codegen_model model) { + switch (model) { + case LTO_CODEGEN_PIC_MODEL_STATIC: + case LTO_CODEGEN_PIC_MODEL_DYNAMIC: + case LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC: + CodeModel = model; + return; + } + llvm_unreachable("Unknown PIC model!"); +} + +bool LTOCodeGenerator::writeMergedModules(const char *path, + std::string &errMsg) { + if (!determineTarget(errMsg)) + return false; + + // mark which symbols can not be internalized + applyScopeRestrictions(); + + // create output file + std::string ErrInfo; + tool_output_file Out(path, ErrInfo, sys::fs::F_Binary); + if (!ErrInfo.empty()) { + errMsg = "could not open bitcode file for writing: "; + errMsg += path; + return false; + } + + // write bitcode to it + WriteBitcodeToFile(Linker.getModule(), Out.os()); + Out.os().close(); + + if (Out.os().has_error()) { + errMsg = "could not write bitcode file: "; + errMsg += path; + Out.os().clear_error(); + return false; + } + + Out.keep(); + return true; +} + +bool LTOCodeGenerator::compile_to_file(const char** name, std::string& errMsg) { + // make unique temp .o file to put generated object file + SmallString<128> Filename; + int FD; + error_code EC = sys::fs::createTemporaryFile("lto-llvm", "o", FD, Filename); + if (EC) { + errMsg = EC.message(); + return false; + } + + // generate object file + tool_output_file objFile(Filename.c_str(), FD); + + bool genResult = generateObjectFile(objFile.os(), errMsg); + objFile.os().close(); + if (objFile.os().has_error()) { + objFile.os().clear_error(); + sys::fs::remove(Twine(Filename)); + return false; + } + + objFile.keep(); + if (!genResult) { + sys::fs::remove(Twine(Filename)); + return false; + } + + NativeObjectPath = Filename.c_str(); + *name = NativeObjectPath.c_str(); + return true; +} + +const void* LTOCodeGenerator::compile(size_t* length, std::string& errMsg) { + const char *name; + if (!compile_to_file(&name, errMsg)) + return NULL; + + // remove old buffer if compile() called twice + delete NativeObjectFile; + + // read .o file into memory buffer + OwningPtr BuffPtr; + if (error_code ec = MemoryBuffer::getFile(name, BuffPtr, -1, false)) { + errMsg = ec.message(); + sys::fs::remove(NativeObjectPath); + return NULL; + } + NativeObjectFile = BuffPtr.take(); + + // remove temp files + sys::fs::remove(NativeObjectPath); + + // return buffer, unless error + if (NativeObjectFile == NULL) + return NULL; + *length = NativeObjectFile->getBufferSize(); + return NativeObjectFile->getBufferStart(); +} + +bool LTOCodeGenerator::determineTarget(std::string &errMsg) { + if (TargetMach != NULL) + return true; + + // if options were requested, set them + if (!CodegenOptions.empty()) + cl::ParseCommandLineOptions(CodegenOptions.size(), + const_cast(&CodegenOptions[0])); + + std::string TripleStr = Linker.getModule()->getTargetTriple(); + if (TripleStr.empty()) + TripleStr = sys::getDefaultTargetTriple(); + llvm::Triple Triple(TripleStr); + + // create target machine from info for merged modules + const Target *march = TargetRegistry::lookupTarget(TripleStr, errMsg); + if (march == NULL) + return false; + + // The relocation model is actually a static member of TargetMachine and + // needs to be set before the TargetMachine is instantiated. + Reloc::Model RelocModel = Reloc::Default; + switch (CodeModel) { + case LTO_CODEGEN_PIC_MODEL_STATIC: + RelocModel = Reloc::Static; + break; + case LTO_CODEGEN_PIC_MODEL_DYNAMIC: + RelocModel = Reloc::PIC_; + break; + case LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC: + RelocModel = Reloc::DynamicNoPIC; + break; + } + + // construct LTOModule, hand over ownership of module and target + SubtargetFeatures Features; + Features.getDefaultSubtargetFeatures(Triple); + std::string FeatureStr = Features.getString(); + // Set a default CPU for Darwin triples. + if (MCpu.empty() && Triple.isOSDarwin()) { + if (Triple.getArch() == llvm::Triple::x86_64) + MCpu = "core2"; + else if (Triple.getArch() == llvm::Triple::x86) + MCpu = "yonah"; + } + TargetOptions Options; + LTOModule::getTargetOptions(Options); + TargetMach = march->createTargetMachine(TripleStr, MCpu, FeatureStr, Options, + RelocModel, CodeModel::Default, + CodeGenOpt::Aggressive); + return true; +} + +void LTOCodeGenerator:: +applyRestriction(GlobalValue &GV, + std::vector &MustPreserveList, + SmallPtrSet &AsmUsed, + Mangler &Mangler) { + SmallString<64> Buffer; + Mangler.getNameWithPrefix(Buffer, &GV, false); + + if (GV.isDeclaration()) + return; + if (MustPreserveSymbols.count(Buffer)) + MustPreserveList.push_back(GV.getName().data()); + if (AsmUndefinedRefs.count(Buffer)) + AsmUsed.insert(&GV); +} + +static void findUsedValues(GlobalVariable *LLVMUsed, + SmallPtrSet &UsedValues) { + if (LLVMUsed == 0) return; + + ConstantArray *Inits = cast(LLVMUsed->getInitializer()); + for (unsigned i = 0, e = Inits->getNumOperands(); i != e; ++i) + if (GlobalValue *GV = + dyn_cast(Inits->getOperand(i)->stripPointerCasts())) + UsedValues.insert(GV); +} + +void LTOCodeGenerator::applyScopeRestrictions() { + if (ScopeRestrictionsDone) + return; + Module *mergedModule = Linker.getModule(); + + // Start off with a verification pass. + PassManager passes; + passes.add(createVerifierPass()); + + // mark which symbols can not be internalized + MCContext MContext(TargetMach->getMCAsmInfo(), TargetMach->getRegisterInfo(), + NULL); + Mangler Mangler(MContext, TargetMach); + std::vector MustPreserveList; + SmallPtrSet AsmUsed; + + for (Module::iterator f = mergedModule->begin(), + e = mergedModule->end(); f != e; ++f) + applyRestriction(*f, MustPreserveList, AsmUsed, Mangler); + for (Module::global_iterator v = mergedModule->global_begin(), + e = mergedModule->global_end(); v != e; ++v) + applyRestriction(*v, MustPreserveList, AsmUsed, Mangler); + for (Module::alias_iterator a = mergedModule->alias_begin(), + e = mergedModule->alias_end(); a != e; ++a) + applyRestriction(*a, MustPreserveList, AsmUsed, Mangler); + + GlobalVariable *LLVMCompilerUsed = + mergedModule->getGlobalVariable("llvm.compiler.used"); + findUsedValues(LLVMCompilerUsed, AsmUsed); + if (LLVMCompilerUsed) + LLVMCompilerUsed->eraseFromParent(); + + if (!AsmUsed.empty()) { + llvm::Type *i8PTy = llvm::Type::getInt8PtrTy(Context); + std::vector asmUsed2; + for (SmallPtrSet::const_iterator i = AsmUsed.begin(), + e = AsmUsed.end(); i !=e; ++i) { + GlobalValue *GV = *i; + Constant *c = ConstantExpr::getBitCast(GV, i8PTy); + asmUsed2.push_back(c); + } + + llvm::ArrayType *ATy = llvm::ArrayType::get(i8PTy, asmUsed2.size()); + LLVMCompilerUsed = + new llvm::GlobalVariable(*mergedModule, ATy, false, + llvm::GlobalValue::AppendingLinkage, + llvm::ConstantArray::get(ATy, asmUsed2), + "llvm.compiler.used"); + + LLVMCompilerUsed->setSection("llvm.metadata"); + } + + passes.add(createInternalizePass(MustPreserveList)); + + // apply scope restrictions + passes.run(*mergedModule); + + ScopeRestrictionsDone = true; +} + +/// Optimize merged modules using various IPO passes +bool LTOCodeGenerator::generateObjectFile(raw_ostream &out, + std::string &errMsg) { + if (!this->determineTarget(errMsg)) + return false; + + Module *mergedModule = Linker.getModule(); + + // Mark which symbols can not be internalized + this->applyScopeRestrictions(); + + // Instantiate the pass manager to organize the passes. + PassManager passes; + + // Start off with a verification pass. + passes.add(createVerifierPass()); + + // Add an appropriate DataLayout instance for this module... + passes.add(new DataLayout(*TargetMach->getDataLayout())); + TargetMach->addAnalysisPasses(passes); + + // Enabling internalize here would use its AllButMain variant. It + // keeps only main if it exists and does nothing for libraries. Instead + // we create the pass ourselves with the symbol list provided by the linker. + if (!DisableOpt) + PassManagerBuilder().populateLTOPassManager(passes, + /*Internalize=*/false, + !DisableInline, + DisableGVNLoadPRE); + + // Make sure everything is still good. + passes.add(createVerifierPass()); + + PassManager codeGenPasses; + + codeGenPasses.add(new DataLayout(*TargetMach->getDataLayout())); + TargetMach->addAnalysisPasses(codeGenPasses); + + formatted_raw_ostream Out(out); + + // If the bitcode files contain ARC code and were compiled with optimization, + // the ObjCARCContractPass must be run, so do it unconditionally here. + codeGenPasses.add(createObjCARCContractPass()); + + if (TargetMach->addPassesToEmitFile(codeGenPasses, Out, + TargetMachine::CGFT_ObjectFile)) { + errMsg = "target file type not supported"; + return false; + } + + // Run our queue of passes all at once now, efficiently. + passes.run(*mergedModule); + + // Run the code generator, and write assembly file + codeGenPasses.run(*mergedModule); + + return true; +} + +/// setCodeGenDebugOptions - Set codegen debugging options to aid in debugging +/// LTO problems. +void LTOCodeGenerator::setCodeGenDebugOptions(const char *options) { + for (std::pair o = getToken(options); + !o.first.empty(); o = getToken(o.second)) { + // ParseCommandLineOptions() expects argv[0] to be program name. Lazily add + // that. + if (CodegenOptions.empty()) + CodegenOptions.push_back(strdup("libLLVMLTO")); + CodegenOptions.push_back(strdup(o.first.str().c_str())); + } +} diff --git a/lib/LTO/LTOModule.cpp b/lib/LTO/LTOModule.cpp new file mode 100644 index 0000000000..1457f2f5a2 --- /dev/null +++ b/lib/LTO/LTOModule.cpp @@ -0,0 +1,900 @@ +//===-- LTOModule.cpp - LLVM Link Time Optimizer --------------------------===// +// +// 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 Link Time Optimization library. This library is +// intended to be used by linker to optimize code at link time. +// +//===----------------------------------------------------------------------===// + +#include "llvm/LTO/LTOModule.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCTargetAsmParser.h" +#include "llvm/MC/SubtargetFeature.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/system_error.h" +#include "llvm/Target/TargetRegisterInfo.h" +using namespace llvm; + +static cl::opt +EnableFPMAD("enable-fp-mad", + cl::desc("Enable less precise MAD instructions to be generated"), + cl::init(false)); + +static cl::opt +DisableFPElim("disable-fp-elim", + cl::desc("Disable frame pointer elimination optimization"), + cl::init(false)); + +static cl::opt +EnableUnsafeFPMath("enable-unsafe-fp-math", + cl::desc("Enable optimizations that may decrease FP precision"), + cl::init(false)); + +static cl::opt +EnableNoInfsFPMath("enable-no-infs-fp-math", + cl::desc("Enable FP math optimizations that assume no +-Infs"), + cl::init(false)); + +static cl::opt +EnableNoNaNsFPMath("enable-no-nans-fp-math", + cl::desc("Enable FP math optimizations that assume no NaNs"), + cl::init(false)); + +static cl::opt +EnableHonorSignDependentRoundingFPMath("enable-sign-dependent-rounding-fp-math", + cl::Hidden, + cl::desc("Force codegen to assume rounding mode can change dynamically"), + cl::init(false)); + +static cl::opt +GenerateSoftFloatCalls("soft-float", + cl::desc("Generate software floating point library calls"), + cl::init(false)); + +static cl::opt +FloatABIForCalls("float-abi", + cl::desc("Choose float ABI type"), + cl::init(FloatABI::Default), + cl::values( + clEnumValN(FloatABI::Default, "default", + "Target default float ABI type"), + clEnumValN(FloatABI::Soft, "soft", + "Soft float ABI (implied by -soft-float)"), + clEnumValN(FloatABI::Hard, "hard", + "Hard float ABI (uses FP registers)"), + clEnumValEnd)); + +static cl::opt +FuseFPOps("fp-contract", + cl::desc("Enable aggresive formation of fused FP ops"), + cl::init(FPOpFusion::Standard), + cl::values( + clEnumValN(FPOpFusion::Fast, "fast", + "Fuse FP ops whenever profitable"), + clEnumValN(FPOpFusion::Standard, "on", + "Only fuse 'blessed' FP ops."), + clEnumValN(FPOpFusion::Strict, "off", + "Only fuse FP ops when the result won't be effected."), + clEnumValEnd)); + +static cl::opt +DontPlaceZerosInBSS("nozero-initialized-in-bss", + cl::desc("Don't place zero-initialized symbols into bss section"), + cl::init(false)); + +static cl::opt +EnableGuaranteedTailCallOpt("tailcallopt", + cl::desc("Turn fastcc calls into tail calls by (potentially) changing ABI."), + cl::init(false)); + +static cl::opt +DisableTailCalls("disable-tail-calls", + cl::desc("Never emit tail calls"), + cl::init(false)); + +static cl::opt +OverrideStackAlignment("stack-alignment", + cl::desc("Override default stack alignment"), + cl::init(0)); + +static cl::opt +TrapFuncName("trap-func", cl::Hidden, + cl::desc("Emit a call to trap function rather than a trap instruction"), + cl::init("")); + +static cl::opt +EnablePIE("enable-pie", + cl::desc("Assume the creation of a position independent executable."), + cl::init(false)); + +static cl::opt +SegmentedStacks("segmented-stacks", + cl::desc("Use segmented stacks if possible."), + cl::init(false)); + +static cl::opt +UseInitArray("use-init-array", + cl::desc("Use .init_array instead of .ctors."), + cl::init(false)); + +LTOModule::LTOModule(llvm::Module *m, llvm::TargetMachine *t) + : _module(m), _target(t), + _context(_target->getMCAsmInfo(), _target->getRegisterInfo(), NULL), + _mangler(_context, t) {} + +/// isBitcodeFile - Returns 'true' if the file (or memory contents) is LLVM +/// bitcode. +bool LTOModule::isBitcodeFile(const void *mem, size_t length) { + return sys::fs::identify_magic(StringRef((const char *)mem, length)) == + sys::fs::file_magic::bitcode; +} + +bool LTOModule::isBitcodeFile(const char *path) { + sys::fs::file_magic type; + if (sys::fs::identify_magic(path, type)) + return false; + return type == sys::fs::file_magic::bitcode; +} + +/// isBitcodeFileForTarget - Returns 'true' if the file (or memory contents) is +/// LLVM bitcode for the specified triple. +bool LTOModule::isBitcodeFileForTarget(const void *mem, size_t length, + const char *triplePrefix) { + MemoryBuffer *buffer = makeBuffer(mem, length); + if (!buffer) + return false; + return isTargetMatch(buffer, triplePrefix); +} + +bool LTOModule::isBitcodeFileForTarget(const char *path, + const char *triplePrefix) { + OwningPtr buffer; + if (MemoryBuffer::getFile(path, buffer)) + return false; + return isTargetMatch(buffer.take(), triplePrefix); +} + +/// isTargetMatch - Returns 'true' if the memory buffer is for the specified +/// target triple. +bool LTOModule::isTargetMatch(MemoryBuffer *buffer, const char *triplePrefix) { + std::string Triple = getBitcodeTargetTriple(buffer, getGlobalContext()); + delete buffer; + return strncmp(Triple.c_str(), triplePrefix, strlen(triplePrefix)) == 0; +} + +/// makeLTOModule - Create an LTOModule. N.B. These methods take ownership of +/// the buffer. +LTOModule *LTOModule::makeLTOModule(const char *path, std::string &errMsg) { + OwningPtr buffer; + if (error_code ec = MemoryBuffer::getFile(path, buffer)) { + errMsg = ec.message(); + return NULL; + } + return makeLTOModule(buffer.take(), errMsg); +} + +LTOModule *LTOModule::makeLTOModule(int fd, const char *path, + size_t size, std::string &errMsg) { + return makeLTOModule(fd, path, size, 0, errMsg); +} + +LTOModule *LTOModule::makeLTOModule(int fd, const char *path, + size_t map_size, + off_t offset, + std::string &errMsg) { + OwningPtr buffer; + if (error_code ec = + MemoryBuffer::getOpenFileSlice(fd, path, buffer, map_size, offset)) { + errMsg = ec.message(); + return NULL; + } + return makeLTOModule(buffer.take(), errMsg); +} + +LTOModule *LTOModule::makeLTOModule(const void *mem, size_t length, + std::string &errMsg) { + OwningPtr buffer(makeBuffer(mem, length)); + if (!buffer) + return NULL; + return makeLTOModule(buffer.take(), errMsg); +} + +void LTOModule::getTargetOptions(TargetOptions &Options) { + Options.LessPreciseFPMADOption = EnableFPMAD; + Options.NoFramePointerElim = DisableFPElim; + Options.AllowFPOpFusion = FuseFPOps; + Options.UnsafeFPMath = EnableUnsafeFPMath; + Options.NoInfsFPMath = EnableNoInfsFPMath; + Options.NoNaNsFPMath = EnableNoNaNsFPMath; + Options.HonorSignDependentRoundingFPMathOption = + EnableHonorSignDependentRoundingFPMath; + Options.UseSoftFloat = GenerateSoftFloatCalls; + if (FloatABIForCalls != FloatABI::Default) + Options.FloatABIType = FloatABIForCalls; + Options.NoZerosInBSS = DontPlaceZerosInBSS; + Options.GuaranteedTailCallOpt = EnableGuaranteedTailCallOpt; + Options.DisableTailCalls = DisableTailCalls; + Options.StackAlignmentOverride = OverrideStackAlignment; + Options.TrapFuncName = TrapFuncName; + Options.PositionIndependentExecutable = EnablePIE; + Options.EnableSegmentedStacks = SegmentedStacks; + Options.UseInitArray = UseInitArray; +} + +LTOModule *LTOModule::makeLTOModule(MemoryBuffer *buffer, + std::string &errMsg) { + // parse bitcode buffer + OwningPtr m(getLazyBitcodeModule(buffer, getGlobalContext(), + &errMsg)); + if (!m) { + delete buffer; + return NULL; + } + + std::string TripleStr = m->getTargetTriple(); + if (TripleStr.empty()) + TripleStr = sys::getDefaultTargetTriple(); + llvm::Triple Triple(TripleStr); + + // find machine architecture for this module + const Target *march = TargetRegistry::lookupTarget(TripleStr, errMsg); + if (!march) + return NULL; + + // construct LTOModule, hand over ownership of module and target + SubtargetFeatures Features; + Features.getDefaultSubtargetFeatures(Triple); + std::string FeatureStr = Features.getString(); + // Set a default CPU for Darwin triples. + std::string CPU; + if (Triple.isOSDarwin()) { + if (Triple.getArch() == llvm::Triple::x86_64) + CPU = "core2"; + else if (Triple.getArch() == llvm::Triple::x86) + CPU = "yonah"; + } + TargetOptions Options; + getTargetOptions(Options); + TargetMachine *target = march->createTargetMachine(TripleStr, CPU, FeatureStr, + Options); + LTOModule *Ret = new LTOModule(m.take(), target); + if (Ret->parseSymbols(errMsg)) { + delete Ret; + return NULL; + } + + return Ret; +} + +/// makeBuffer - Create a MemoryBuffer from a memory range. +MemoryBuffer *LTOModule::makeBuffer(const void *mem, size_t length) { + const char *startPtr = (const char*)mem; + return MemoryBuffer::getMemBuffer(StringRef(startPtr, length), "", false); +} + +/// objcClassNameFromExpression - Get string that the data pointer points to. +bool +LTOModule::objcClassNameFromExpression(const Constant *c, std::string &name) { + if (const ConstantExpr *ce = dyn_cast(c)) { + Constant *op = ce->getOperand(0); + if (GlobalVariable *gvn = dyn_cast(op)) { + Constant *cn = gvn->getInitializer(); + if (ConstantDataArray *ca = dyn_cast(cn)) { + if (ca->isCString()) { + name = ".objc_class_name_" + ca->getAsCString().str(); + return true; + } + } + } + } + return false; +} + +/// addObjCClass - Parse i386/ppc ObjC class data structure. +void LTOModule::addObjCClass(const GlobalVariable *clgv) { + const ConstantStruct *c = dyn_cast(clgv->getInitializer()); + if (!c) return; + + // second slot in __OBJC,__class is pointer to superclass name + std::string superclassName; + if (objcClassNameFromExpression(c->getOperand(1), superclassName)) { + NameAndAttributes info; + StringMap::value_type &entry = + _undefines.GetOrCreateValue(superclassName); + if (!entry.getValue().name) { + const char *symbolName = entry.getKey().data(); + info.name = symbolName; + info.attributes = LTO_SYMBOL_DEFINITION_UNDEFINED; + info.isFunction = false; + info.symbol = clgv; + entry.setValue(info); + } + } + + // third slot in __OBJC,__class is pointer to class name + std::string className; + if (objcClassNameFromExpression(c->getOperand(2), className)) { + StringSet::value_type &entry = _defines.GetOrCreateValue(className); + entry.setValue(1); + + NameAndAttributes info; + info.name = entry.getKey().data(); + info.attributes = LTO_SYMBOL_PERMISSIONS_DATA | + LTO_SYMBOL_DEFINITION_REGULAR | LTO_SYMBOL_SCOPE_DEFAULT; + info.isFunction = false; + info.symbol = clgv; + _symbols.push_back(info); + } +} + +/// addObjCCategory - Parse i386/ppc ObjC category data structure. +void LTOModule::addObjCCategory(const GlobalVariable *clgv) { + const ConstantStruct *c = dyn_cast(clgv->getInitializer()); + if (!c) return; + + // second slot in __OBJC,__category is pointer to target class name + std::string targetclassName; + if (!objcClassNameFromExpression(c->getOperand(1), targetclassName)) + return; + + NameAndAttributes info; + StringMap::value_type &entry = + _undefines.GetOrCreateValue(targetclassName); + + if (entry.getValue().name) + return; + + const char *symbolName = entry.getKey().data(); + info.name = symbolName; + info.attributes = LTO_SYMBOL_DEFINITION_UNDEFINED; + info.isFunction = false; + info.symbol = clgv; + entry.setValue(info); +} + +/// addObjCClassRef - Parse i386/ppc ObjC class list data structure. +void LTOModule::addObjCClassRef(const GlobalVariable *clgv) { + std::string targetclassName; + if (!objcClassNameFromExpression(clgv->getInitializer(), targetclassName)) + return; + + NameAndAttributes info; + StringMap::value_type &entry = + _undefines.GetOrCreateValue(targetclassName); + if (entry.getValue().name) + return; + + const char *symbolName = entry.getKey().data(); + info.name = symbolName; + info.attributes = LTO_SYMBOL_DEFINITION_UNDEFINED; + info.isFunction = false; + info.symbol = clgv; + entry.setValue(info); +} + +/// addDefinedDataSymbol - Add a data symbol as defined to the list. +void LTOModule::addDefinedDataSymbol(const GlobalValue *v) { + // Add to list of defined symbols. + addDefinedSymbol(v, false); + + if (!v->hasSection() /* || !isTargetDarwin */) + return; + + // Special case i386/ppc ObjC data structures in magic sections: + // The issue is that the old ObjC object format did some strange + // contortions to avoid real linker symbols. For instance, the + // ObjC class data structure is allocated statically in the executable + // that defines that class. That data structures contains a pointer to + // its superclass. But instead of just initializing that part of the + // struct to the address of its superclass, and letting the static and + // dynamic linkers do the rest, the runtime works by having that field + // instead point to a C-string that is the name of the superclass. + // At runtime the objc initialization updates that pointer and sets + // it to point to the actual super class. As far as the linker + // knows it is just a pointer to a string. But then someone wanted the + // linker to issue errors at build time if the superclass was not found. + // So they figured out a way in mach-o object format to use an absolute + // symbols (.objc_class_name_Foo = 0) and a floating reference + // (.reference .objc_class_name_Bar) to cause the linker into erroring when + // a class was missing. + // The following synthesizes the implicit .objc_* symbols for the linker + // from the ObjC data structures generated by the front end. + + // special case if this data blob is an ObjC class definition + if (v->getSection().compare(0, 15, "__OBJC,__class,") == 0) { + if (const GlobalVariable *gv = dyn_cast(v)) { + addObjCClass(gv); + } + } + + // special case if this data blob is an ObjC category definition + else if (v->getSection().compare(0, 18, "__OBJC,__category,") == 0) { + if (const GlobalVariable *gv = dyn_cast(v)) { + addObjCCategory(gv); + } + } + + // special case if this data blob is the list of referenced classes + else if (v->getSection().compare(0, 18, "__OBJC,__cls_refs,") == 0) { + if (const GlobalVariable *gv = dyn_cast(v)) { + addObjCClassRef(gv); + } + } +} + +/// addDefinedFunctionSymbol - Add a function symbol as defined to the list. +void LTOModule::addDefinedFunctionSymbol(const Function *f) { + // add to list of defined symbols + addDefinedSymbol(f, true); +} + +/// addDefinedSymbol - Add a defined symbol to the list. +void LTOModule::addDefinedSymbol(const GlobalValue *def, bool isFunction) { + // ignore all llvm.* symbols + if (def->getName().startswith("llvm.")) + return; + + // string is owned by _defines + SmallString<64> Buffer; + _mangler.getNameWithPrefix(Buffer, def, false); + + // set alignment part log2() can have rounding errors + uint32_t align = def->getAlignment(); + uint32_t attr = align ? countTrailingZeros(def->getAlignment()) : 0; + + // set permissions part + if (isFunction) { + attr |= LTO_SYMBOL_PERMISSIONS_CODE; + } else { + const GlobalVariable *gv = dyn_cast(def); + if (gv && gv->isConstant()) + attr |= LTO_SYMBOL_PERMISSIONS_RODATA; + else + attr |= LTO_SYMBOL_PERMISSIONS_DATA; + } + + // set definition part + if (def->hasWeakLinkage() || def->hasLinkOnceLinkage() || + def->hasLinkerPrivateWeakLinkage()) + attr |= LTO_SYMBOL_DEFINITION_WEAK; + else if (def->hasCommonLinkage()) + attr |= LTO_SYMBOL_DEFINITION_TENTATIVE; + else + attr |= LTO_SYMBOL_DEFINITION_REGULAR; + + // set scope part + if (def->hasHiddenVisibility()) + attr |= LTO_SYMBOL_SCOPE_HIDDEN; + else if (def->hasProtectedVisibility()) + attr |= LTO_SYMBOL_SCOPE_PROTECTED; + else if (def->hasExternalLinkage() || def->hasWeakLinkage() || + def->hasLinkOnceLinkage() || def->hasCommonLinkage() || + def->hasLinkerPrivateWeakLinkage()) + attr |= LTO_SYMBOL_SCOPE_DEFAULT; + else if (def->hasLinkOnceODRAutoHideLinkage()) + attr |= LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN; + else + attr |= LTO_SYMBOL_SCOPE_INTERNAL; + + StringSet::value_type &entry = _defines.GetOrCreateValue(Buffer); + entry.setValue(1); + + // fill information structure + NameAndAttributes info; + StringRef Name = entry.getKey(); + info.name = Name.data(); + assert(info.name[Name.size()] == '\0'); + info.attributes = attr; + info.isFunction = isFunction; + info.symbol = def; + + // add to table of symbols + _symbols.push_back(info); +} + +/// addAsmGlobalSymbol - Add a global symbol from module-level ASM to the +/// defined list. +void LTOModule::addAsmGlobalSymbol(const char *name, + lto_symbol_attributes scope) { + StringSet::value_type &entry = _defines.GetOrCreateValue(name); + + // only add new define if not already defined + if (entry.getValue()) + return; + + entry.setValue(1); + + NameAndAttributes &info = _undefines[entry.getKey().data()]; + + if (info.symbol == 0) { + // FIXME: This is trying to take care of module ASM like this: + // + // module asm ".zerofill __FOO, __foo, _bar_baz_qux, 0" + // + // but is gross and its mother dresses it funny. Have the ASM parser give us + // more details for this type of situation so that we're not guessing so + // much. + + // fill information structure + info.name = entry.getKey().data(); + info.attributes = + LTO_SYMBOL_PERMISSIONS_DATA | LTO_SYMBOL_DEFINITION_REGULAR | scope; + info.isFunction = false; + info.symbol = 0; + + // add to table of symbols + _symbols.push_back(info); + return; + } + + if (info.isFunction) + addDefinedFunctionSymbol(cast(info.symbol)); + else + addDefinedDataSymbol(info.symbol); + + _symbols.back().attributes &= ~LTO_SYMBOL_SCOPE_MASK; + _symbols.back().attributes |= scope; +} + +/// addAsmGlobalSymbolUndef - Add a global symbol from module-level ASM to the +/// undefined list. +void LTOModule::addAsmGlobalSymbolUndef(const char *name) { + StringMap::value_type &entry = + _undefines.GetOrCreateValue(name); + + _asm_undefines.push_back(entry.getKey().data()); + + // we already have the symbol + if (entry.getValue().name) + return; + + uint32_t attr = LTO_SYMBOL_DEFINITION_UNDEFINED;; + attr |= LTO_SYMBOL_SCOPE_DEFAULT; + NameAndAttributes info; + info.name = entry.getKey().data(); + info.attributes = attr; + info.isFunction = false; + info.symbol = 0; + + entry.setValue(info); +} + +/// addPotentialUndefinedSymbol - Add a symbol which isn't defined just yet to a +/// list to be resolved later. +void +LTOModule::addPotentialUndefinedSymbol(const GlobalValue *decl, bool isFunc) { + // ignore all llvm.* symbols + if (decl->getName().startswith("llvm.")) + return; + + // ignore all aliases + if (isa(decl)) + return; + + SmallString<64> name; + _mangler.getNameWithPrefix(name, decl, false); + + StringMap::value_type &entry = + _undefines.GetOrCreateValue(name); + + // we already have the symbol + if (entry.getValue().name) + return; + + NameAndAttributes info; + + info.name = entry.getKey().data(); + + if (decl->hasExternalWeakLinkage()) + info.attributes = LTO_SYMBOL_DEFINITION_WEAKUNDEF; + else + info.attributes = LTO_SYMBOL_DEFINITION_UNDEFINED; + + info.isFunction = isFunc; + info.symbol = decl; + + entry.setValue(info); +} + +namespace { + class RecordStreamer : public MCStreamer { + public: + enum State { NeverSeen, Global, Defined, DefinedGlobal, Used }; + + private: + StringMap Symbols; + + void markDefined(const MCSymbol &Symbol) { + State &S = Symbols[Symbol.getName()]; + switch (S) { + case DefinedGlobal: + case Global: + S = DefinedGlobal; + break; + case NeverSeen: + case Defined: + case Used: + S = Defined; + break; + } + } + void markGlobal(const MCSymbol &Symbol) { + State &S = Symbols[Symbol.getName()]; + switch (S) { + case DefinedGlobal: + case Defined: + S = DefinedGlobal; + break; + + case NeverSeen: + case Global: + case Used: + S = Global; + break; + } + } + void markUsed(const MCSymbol &Symbol) { + State &S = Symbols[Symbol.getName()]; + switch (S) { + case DefinedGlobal: + case Defined: + case Global: + break; + + case NeverSeen: + case Used: + S = Used; + break; + } + } + + // FIXME: mostly copied for the obj streamer. + void AddValueSymbols(const MCExpr *Value) { + switch (Value->getKind()) { + case MCExpr::Target: + // FIXME: What should we do in here? + break; + + case MCExpr::Constant: + break; + + case MCExpr::Binary: { + const MCBinaryExpr *BE = cast(Value); + AddValueSymbols(BE->getLHS()); + AddValueSymbols(BE->getRHS()); + break; + } + + case MCExpr::SymbolRef: + markUsed(cast(Value)->getSymbol()); + break; + + case MCExpr::Unary: + AddValueSymbols(cast(Value)->getSubExpr()); + break; + } + } + + public: + typedef StringMap::const_iterator const_iterator; + + const_iterator begin() { + return Symbols.begin(); + } + + const_iterator end() { + return Symbols.end(); + } + + RecordStreamer(MCContext &Context) + : MCStreamer(SK_RecordStreamer, Context) {} + + virtual void EmitInstruction(const MCInst &Inst) { + // Scan for values. + for (unsigned i = Inst.getNumOperands(); i--; ) + if (Inst.getOperand(i).isExpr()) + AddValueSymbols(Inst.getOperand(i).getExpr()); + } + virtual void EmitLabel(MCSymbol *Symbol) { + Symbol->setSection(*getCurrentSection().first); + markDefined(*Symbol); + } + virtual void EmitDebugLabel(MCSymbol *Symbol) { + EmitLabel(Symbol); + } + virtual void EmitAssignment(MCSymbol *Symbol, const MCExpr *Value) { + // FIXME: should we handle aliases? + markDefined(*Symbol); + } + virtual bool EmitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) { + if (Attribute == MCSA_Global) + markGlobal(*Symbol); + return true; + } + virtual void EmitZerofill(const MCSection *Section, MCSymbol *Symbol, + uint64_t Size , unsigned ByteAlignment) { + markDefined(*Symbol); + } + virtual void EmitCommonSymbol(MCSymbol *Symbol, uint64_t Size, + unsigned ByteAlignment) { + markDefined(*Symbol); + } + + virtual void EmitBundleAlignMode(unsigned AlignPow2) {} + virtual void EmitBundleLock(bool AlignToEnd) {} + virtual void EmitBundleUnlock() {} + + // Noop calls. + virtual void ChangeSection(const MCSection *Section, + const MCExpr *Subsection) {} + virtual void InitToTextSection() {} + virtual void InitSections() {} + virtual void EmitAssemblerFlag(MCAssemblerFlag Flag) {} + virtual void EmitThumbFunc(MCSymbol *Func) {} + virtual void EmitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) {} + virtual void EmitWeakReference(MCSymbol *Alias, const MCSymbol *Symbol) {} + virtual void BeginCOFFSymbolDef(const MCSymbol *Symbol) {} + virtual void EmitCOFFSymbolStorageClass(int StorageClass) {} + virtual void EmitCOFFSymbolType(int Type) {} + virtual void EndCOFFSymbolDef() {} + virtual void EmitELFSize(MCSymbol *Symbol, const MCExpr *Value) {} + virtual void EmitLocalCommonSymbol(MCSymbol *Symbol, uint64_t Size, + unsigned ByteAlignment) {} + virtual void EmitTBSSSymbol(const MCSection *Section, MCSymbol *Symbol, + uint64_t Size, unsigned ByteAlignment) {} + virtual void EmitBytes(StringRef Data) {} + virtual void EmitValueImpl(const MCExpr *Value, unsigned Size) {} + virtual void EmitULEB128Value(const MCExpr *Value) {} + virtual void EmitSLEB128Value(const MCExpr *Value) {} + virtual void EmitValueToAlignment(unsigned ByteAlignment, int64_t Value, + unsigned ValueSize, + unsigned MaxBytesToEmit) {} + virtual void EmitCodeAlignment(unsigned ByteAlignment, + unsigned MaxBytesToEmit) {} + virtual bool EmitValueToOffset(const MCExpr *Offset, + unsigned char Value ) { return false; } + virtual void EmitFileDirective(StringRef Filename) {} + virtual void EmitDwarfAdvanceLineAddr(int64_t LineDelta, + const MCSymbol *LastLabel, + const MCSymbol *Label, + unsigned PointerSize) {} + virtual void FinishImpl() {} + virtual void EmitCFIEndProcImpl(MCDwarfFrameInfo &Frame) { + RecordProcEnd(Frame); + } + + static bool classof(const MCStreamer *S) { + return S->getKind() == SK_RecordStreamer; + } + }; +} // end anonymous namespace + +/// addAsmGlobalSymbols - Add global symbols from module-level ASM to the +/// defined or undefined lists. +bool LTOModule::addAsmGlobalSymbols(std::string &errMsg) { + const std::string &inlineAsm = _module->getModuleInlineAsm(); + if (inlineAsm.empty()) + return false; + + OwningPtr Streamer(new RecordStreamer(_context)); + MemoryBuffer *Buffer = MemoryBuffer::getMemBuffer(inlineAsm); + SourceMgr SrcMgr; + SrcMgr.AddNewSourceBuffer(Buffer, SMLoc()); + OwningPtr Parser(createMCAsmParser(SrcMgr, + _context, *Streamer, + *_target->getMCAsmInfo())); + const Target &T = _target->getTarget(); + OwningPtr MCII(T.createMCInstrInfo()); + OwningPtr + STI(T.createMCSubtargetInfo(_target->getTargetTriple(), + _target->getTargetCPU(), + _target->getTargetFeatureString())); + OwningPtr TAP(T.createMCAsmParser(*STI, *Parser.get(), *MCII)); + if (!TAP) { + errMsg = "target " + std::string(T.getName()) + + " does not define AsmParser."; + return true; + } + + Parser->setTargetParser(*TAP); + if (Parser->Run(false)) + return true; + + for (RecordStreamer::const_iterator i = Streamer->begin(), + e = Streamer->end(); i != e; ++i) { + StringRef Key = i->first(); + RecordStreamer::State Value = i->second; + if (Value == RecordStreamer::DefinedGlobal) + addAsmGlobalSymbol(Key.data(), LTO_SYMBOL_SCOPE_DEFAULT); + else if (Value == RecordStreamer::Defined) + addAsmGlobalSymbol(Key.data(), LTO_SYMBOL_SCOPE_INTERNAL); + else if (Value == RecordStreamer::Global || + Value == RecordStreamer::Used) + addAsmGlobalSymbolUndef(Key.data()); + } + + return false; +} + +/// isDeclaration - Return 'true' if the global value is a declaration. +static bool isDeclaration(const GlobalValue &V) { + if (V.hasAvailableExternallyLinkage()) + return true; + + if (V.isMaterializable()) + return false; + + return V.isDeclaration(); +} + +/// parseSymbols - Parse the symbols from the module and model-level ASM and add +/// them to either the defined or undefined lists. +bool LTOModule::parseSymbols(std::string &errMsg) { + // add functions + for (Module::iterator f = _module->begin(), e = _module->end(); f != e; ++f) { + if (isDeclaration(*f)) + addPotentialUndefinedSymbol(f, true); + else + addDefinedFunctionSymbol(f); + } + + // add data + for (Module::global_iterator v = _module->global_begin(), + e = _module->global_end(); v != e; ++v) { + if (isDeclaration(*v)) + addPotentialUndefinedSymbol(v, false); + else + addDefinedDataSymbol(v); + } + + // add asm globals + if (addAsmGlobalSymbols(errMsg)) + return true; + + // add aliases + for (Module::alias_iterator a = _module->alias_begin(), + e = _module->alias_end(); a != e; ++a) { + if (isDeclaration(*a->getAliasedGlobal())) + // Is an alias to a declaration. + addPotentialUndefinedSymbol(a, false); + else + addDefinedDataSymbol(a); + } + + // make symbols for all undefines + for (StringMap::iterator u =_undefines.begin(), + e = _undefines.end(); u != e; ++u) { + // If this symbol also has a definition, then don't make an undefine because + // it is a tentative definition. + if (_defines.count(u->getKey())) continue; + NameAndAttributes info = u->getValue(); + _symbols.push_back(info); + } + + return false; +} diff --git a/lib/LTO/Makefile b/lib/LTO/Makefile new file mode 100644 index 0000000000..7ca125ab14 --- /dev/null +++ b/lib/LTO/Makefile @@ -0,0 +1,15 @@ +##===- lib/Linker/Makefile ---------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../.. +LIBRARYNAME = LLVMLTO +BUILD_ARCHIVE := 1 + +include $(LEVEL)/Makefile.common + -- cgit v1.2.3