| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| //===-LTOBackend.h - LLVM Link Time Optimizer Backend ---------------------===// | ||
| // | ||
| // 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 "backend" phase of LTO, i.e. it performs | ||
| // optimization and code generation on a loaded module. It is generally used | ||
| // internally by the LTO class but can also be used independently, for example | ||
| // to implement a standalone ThinLTO backend. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_LTO_LTOBACKEND_H | ||
| #define LLVM_LTO_LTOBACKEND_H | ||
|
|
||
| #include "llvm/ADT/MapVector.h" | ||
| #include "llvm/IR/DiagnosticInfo.h" | ||
| #include "llvm/IR/ModuleSummaryIndex.h" | ||
| #include "llvm/LTO/Config.h" | ||
| #include "llvm/Support/MemoryBuffer.h" | ||
| #include "llvm/Target/TargetOptions.h" | ||
| #include "llvm/Transforms/IPO/FunctionImport.h" | ||
|
|
||
| namespace llvm { | ||
|
|
||
| class Error; | ||
| class Module; | ||
| class Target; | ||
|
|
||
| namespace lto { | ||
|
|
||
| /// Runs a regular LTO backend. | ||
| Error backend(Config &C, AddStreamFn AddStream, | ||
| unsigned ParallelCodeGenParallelismLevel, | ||
| std::unique_ptr<Module> M); | ||
|
|
||
| /// Runs a ThinLTO backend. | ||
| Error thinBackend(Config &C, size_t Task, AddStreamFn AddStream, Module &M, | ||
| ModuleSummaryIndex &CombinedIndex, | ||
| const FunctionImporter::ImportMapTy &ImportList, | ||
| const GVSummaryMapTy &DefinedGlobals, | ||
| MapVector<StringRef, MemoryBufferRef> &ModuleMap); | ||
|
|
||
| } | ||
| } | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,4 +34,4 @@ required_libraries = | |
| Scalar | ||
| Support | ||
| Target | ||
| TransformUtils | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,277 @@ | ||
| //===-LTOBackend.cpp - LLVM Link Time Optimizer Backend -------------------===// | ||
| // | ||
| // 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 "backend" phase of LTO, i.e. it performs | ||
| // optimization and code generation on a loaded module. It is generally used | ||
| // internally by the LTO class but can also be used independently, for example | ||
| // to implement a standalone ThinLTO backend. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "llvm/LTO/LTOBackend.h" | ||
| #include "llvm/Analysis/TargetLibraryInfo.h" | ||
| #include "llvm/Analysis/TargetTransformInfo.h" | ||
| #include "llvm/Bitcode/ReaderWriter.h" | ||
| #include "llvm/IR/LegacyPassManager.h" | ||
| #include "llvm/MC/SubtargetFeature.h" | ||
| #include "llvm/Support/Error.h" | ||
| #include "llvm/Support/FileSystem.h" | ||
| #include "llvm/Support/TargetRegistry.h" | ||
| #include "llvm/Support/ThreadPool.h" | ||
| #include "llvm/Target/TargetMachine.h" | ||
| #include "llvm/Transforms/IPO.h" | ||
| #include "llvm/Transforms/IPO/PassManagerBuilder.h" | ||
| #include "llvm/Transforms/Utils/FunctionImportUtils.h" | ||
| #include "llvm/Transforms/Utils/SplitModule.h" | ||
|
|
||
| using namespace llvm; | ||
| using namespace lto; | ||
|
|
||
| Error Config::addSaveTemps(std::string OutputFileName) { | ||
| ShouldDiscardValueNames = false; | ||
|
|
||
| std::error_code EC; | ||
| ResolutionFile = make_unique<raw_fd_ostream>( | ||
| OutputFileName + ".resolution.txt", EC, sys::fs::OpenFlags::F_Text); | ||
| if (EC) | ||
| return errorCodeToError(EC); | ||
|
|
||
| auto setHook = [&](std::string PathSuffix, ModuleHookFn &Hook) { | ||
| // Keep track of the hook provided by the linker, which also needs to run. | ||
| ModuleHookFn LinkerHook = Hook; | ||
| Hook = [=](size_t Task, Module &M) { | ||
| // If the linker's hook returned false, we need to pass that result | ||
| // through. | ||
| if (LinkerHook && !LinkerHook(Task, M)) | ||
| return false; | ||
|
|
||
| std::string PathPrefix; | ||
| PathPrefix = OutputFileName; | ||
| if (Task != 0) | ||
| PathPrefix += "." + utostr(Task); | ||
| std::string Path = PathPrefix + "." + PathSuffix + ".bc"; | ||
| std::error_code EC; | ||
| raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None); | ||
| if (EC) { | ||
| // Because -save-temps is a debugging feature, we report the error | ||
| // directly and exit. | ||
| llvm::errs() << "failed to open " << Path << ": " << EC.message() | ||
| << '\n'; | ||
| exit(1); | ||
| } | ||
| WriteBitcodeToFile(&M, OS, /*ShouldPreserveUseListOrder=*/false); | ||
| return true; | ||
| }; | ||
| }; | ||
|
|
||
| setHook("0.preopt", PreOptModuleHook); | ||
| setHook("1.promote", PostPromoteModuleHook); | ||
| setHook("2.internalize", PostInternalizeModuleHook); | ||
| setHook("3.import", PostImportModuleHook); | ||
| setHook("4.opt", PostOptModuleHook); | ||
| setHook("5.precodegen", PreCodeGenModuleHook); | ||
|
|
||
| CombinedIndexHook = [=](const ModuleSummaryIndex &Index) { | ||
| std::string Path = OutputFileName + ".index.bc"; | ||
| std::error_code EC; | ||
| raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None); | ||
| if (EC) { | ||
| // Because -save-temps is a debugging feature, we report the error | ||
| // directly and exit. | ||
| llvm::errs() << "failed to open " << Path << ": " << EC.message() << '\n'; | ||
| exit(1); | ||
| } | ||
| WriteIndexToFile(Index, OS); | ||
| return true; | ||
| }; | ||
|
|
||
| return Error(); | ||
| } | ||
|
|
||
| namespace { | ||
|
|
||
| std::unique_ptr<TargetMachine> | ||
| createTargetMachine(Config &C, StringRef TheTriple, const Target *TheTarget) { | ||
| SubtargetFeatures Features; | ||
| Features.getDefaultSubtargetFeatures(Triple(TheTriple)); | ||
| for (const std::string &A : C.MAttrs) | ||
| Features.AddFeature(A); | ||
|
|
||
| return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine( | ||
| TheTriple, C.CPU, Features.getString(), C.Options, C.RelocModel, | ||
| C.CodeModel, C.CGOptLevel)); | ||
| } | ||
|
|
||
| bool opt(Config &C, TargetMachine *TM, size_t Task, Module &M, bool IsThinLto) { | ||
| M.setDataLayout(TM->createDataLayout()); | ||
|
|
||
| legacy::PassManager passes; | ||
| passes.add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); | ||
|
|
||
| PassManagerBuilder PMB; | ||
| PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM->getTargetTriple())); | ||
| PMB.Inliner = createFunctionInliningPass(); | ||
| // Unconditionally verify input since it is not verified before this | ||
| // point and has unknown origin. | ||
| PMB.VerifyInput = true; | ||
| PMB.VerifyOutput = !C.DisableVerify; | ||
| PMB.LoopVectorize = true; | ||
| PMB.SLPVectorize = true; | ||
| PMB.OptLevel = C.OptLevel; | ||
| if (IsThinLto) | ||
| PMB.populateThinLTOPassManager(passes); | ||
| else | ||
| PMB.populateLTOPassManager(passes); | ||
| passes.run(M); | ||
|
|
||
| if (C.PostOptModuleHook && !C.PostOptModuleHook(Task, M)) | ||
| return false; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| void codegen(Config &C, TargetMachine *TM, AddStreamFn AddStream, size_t Task, | ||
| Module &M) { | ||
| if (C.PreCodeGenModuleHook && !C.PreCodeGenModuleHook(Task, M)) | ||
| return; | ||
|
|
||
| std::unique_ptr<raw_pwrite_stream> OS = AddStream(Task); | ||
| legacy::PassManager CodeGenPasses; | ||
| if (TM->addPassesToEmitFile(CodeGenPasses, *OS, | ||
| TargetMachine::CGFT_ObjectFile)) | ||
| report_fatal_error("Failed to setup codegen"); | ||
| CodeGenPasses.run(M); | ||
| } | ||
|
|
||
| void splitCodeGen(Config &C, TargetMachine *TM, AddStreamFn AddStream, | ||
| unsigned ParallelCodeGenParallelismLevel, | ||
| std::unique_ptr<Module> M) { | ||
| ThreadPool CodegenThreadPool(ParallelCodeGenParallelismLevel); | ||
| unsigned ThreadCount = 0; | ||
| const Target *T = &TM->getTarget(); | ||
|
|
||
| SplitModule( | ||
| std::move(M), ParallelCodeGenParallelismLevel, | ||
| [&](std::unique_ptr<Module> MPart) { | ||
| // We want to clone the module in a new context to multi-thread the | ||
| // codegen. We do it by serializing partition modules to bitcode | ||
| // (while still on the main thread, in order to avoid data races) and | ||
| // spinning up new threads which deserialize the partitions into | ||
| // separate contexts. | ||
| // FIXME: Provide a more direct way to do this in LLVM. | ||
| SmallString<0> BC; | ||
| raw_svector_ostream BCOS(BC); | ||
| WriteBitcodeToFile(MPart.get(), BCOS); | ||
|
|
||
| // Enqueue the task | ||
| CodegenThreadPool.async( | ||
| [&](const SmallString<0> &BC, unsigned ThreadId) { | ||
| LTOLLVMContext Ctx(C); | ||
| ErrorOr<std::unique_ptr<Module>> MOrErr = parseBitcodeFile( | ||
| MemoryBufferRef(StringRef(BC.data(), BC.size()), "ld-temp.o"), | ||
| Ctx); | ||
| if (!MOrErr) | ||
| report_fatal_error("Failed to read bitcode"); | ||
| std::unique_ptr<Module> MPartInCtx = std::move(MOrErr.get()); | ||
|
|
||
| std::unique_ptr<TargetMachine> TM = | ||
| createTargetMachine(C, MPartInCtx->getTargetTriple(), T); | ||
| codegen(C, TM.get(), AddStream, ThreadId, *MPartInCtx); | ||
| }, | ||
| // Pass BC using std::move to ensure that it get moved rather than | ||
| // copied into the thread's context. | ||
| std::move(BC), ThreadCount++); | ||
| }, | ||
| false); | ||
| } | ||
|
|
||
| Expected<const Target *> initAndLookupTarget(Config &C, Module &M) { | ||
| if (!C.OverrideTriple.empty()) | ||
| M.setTargetTriple(C.OverrideTriple); | ||
| else if (M.getTargetTriple().empty()) | ||
| M.setTargetTriple(C.DefaultTriple); | ||
|
|
||
| std::string Msg; | ||
| const Target *T = TargetRegistry::lookupTarget(M.getTargetTriple(), Msg); | ||
| if (!T) | ||
| return make_error<StringError>(Msg, inconvertibleErrorCode()); | ||
| return T; | ||
| } | ||
|
|
||
| } | ||
|
|
||
| Error lto::backend(Config &C, AddStreamFn AddStream, | ||
| unsigned ParallelCodeGenParallelismLevel, | ||
| std::unique_ptr<Module> M) { | ||
| Expected<const Target *> TOrErr = initAndLookupTarget(C, *M); | ||
| if (!TOrErr) | ||
| return TOrErr.takeError(); | ||
|
|
||
| std::unique_ptr<TargetMachine> TM = | ||
| createTargetMachine(C, M->getTargetTriple(), *TOrErr); | ||
|
|
||
| if (!opt(C, TM.get(), 0, *M, /*IsThinLto=*/false)) | ||
| return Error(); | ||
|
|
||
| if (ParallelCodeGenParallelismLevel == 1) | ||
| codegen(C, TM.get(), AddStream, 0, *M); | ||
| else | ||
| splitCodeGen(C, TM.get(), AddStream, ParallelCodeGenParallelismLevel, | ||
| std::move(M)); | ||
| return Error(); | ||
| } | ||
|
|
||
| Error lto::thinBackend(Config &C, size_t Task, AddStreamFn AddStream, Module &M, | ||
| ModuleSummaryIndex &CombinedIndex, | ||
| const FunctionImporter::ImportMapTy &ImportList, | ||
| const GVSummaryMapTy &DefinedGlobals, | ||
| MapVector<StringRef, MemoryBufferRef> &ModuleMap) { | ||
| Expected<const Target *> TOrErr = initAndLookupTarget(C, M); | ||
| if (!TOrErr) | ||
| return TOrErr.takeError(); | ||
|
|
||
| std::unique_ptr<TargetMachine> TM = | ||
| createTargetMachine(C, M.getTargetTriple(), *TOrErr); | ||
|
|
||
| if (C.PreOptModuleHook && !C.PreOptModuleHook(Task, M)) | ||
| return Error(); | ||
|
|
||
| thinLTOResolveWeakForLinkerModule(M, DefinedGlobals); | ||
|
|
||
| renameModuleForThinLTO(M, CombinedIndex); | ||
|
|
||
| if (C.PostPromoteModuleHook && !C.PostPromoteModuleHook(Task, M)) | ||
| return Error(); | ||
|
|
||
| if (!DefinedGlobals.empty()) | ||
| thinLTOInternalizeModule(M, DefinedGlobals); | ||
|
|
||
| if (C.PostInternalizeModuleHook && !C.PostInternalizeModuleHook(Task, M)) | ||
| return Error(); | ||
|
|
||
| auto ModuleLoader = [&](StringRef Identifier) { | ||
| return std::move(getLazyBitcodeModule(MemoryBuffer::getMemBuffer( | ||
| ModuleMap[Identifier], false), | ||
| M.getContext(), | ||
| /*ShouldLazyLoadMetadata=*/true) | ||
| .get()); | ||
| }; | ||
|
|
||
| FunctionImporter Importer(CombinedIndex, ModuleLoader); | ||
| Importer.importFunctions(M, ImportList); | ||
|
|
||
| if (C.PostImportModuleHook && !C.PostImportModuleHook(Task, M)) | ||
| return Error(); | ||
|
|
||
| if (!opt(C, TM.get(), Task, M, /*IsThinLto=*/true)) | ||
| return Error(); | ||
|
|
||
| codegen(C, TM.get(), AddStream, Task, M); | ||
| return Error(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,6 +43,7 @@ set(LLVM_TEST_DEPENDS | |
| llvm-extract | ||
| llvm-lib | ||
| llvm-link | ||
| llvm-lto2 | ||
| llvm-mc | ||
| llvm-mcmarkup | ||
| llvm-nm | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | ||
| target triple = "x86_64-unknown-linux-gnu" | ||
|
|
||
| @a = global i32 42 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | ||
| target triple = "x86_64-unknown-linux-gnu" | ||
|
|
||
| $c2 = comdat any | ||
| $c1 = comdat any | ||
|
|
||
| ; This is only present in this file. The linker will keep $c1 from the first | ||
| ; file and this will be undefined. | ||
| @will_be_undefined = global i32 1, comdat($c1) | ||
|
|
||
| @v1 = weak_odr global i32 41, comdat($c2) | ||
| define weak_odr protected i32 @f1(i8* %this) comdat($c2) { | ||
| bb20: | ||
| store i8* %this, i8** null | ||
| br label %bb21 | ||
| bb21: | ||
| ret i32 41 | ||
| } | ||
|
|
||
| @r21 = global i32* @v1 | ||
| @r22 = global i32(i8*)* @f1 | ||
|
|
||
| @a21 = alias i32, i32* @v1 | ||
| @a22 = alias i16, bitcast (i32* @v1 to i16*) | ||
|
|
||
| @a23 = alias i32(i8*), i32(i8*)* @f1 | ||
| @a24 = alias i16, bitcast (i32(i8*)* @f1 to i16*) | ||
| @a25 = alias i16, i16* @a24 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| ; RUN: llvm-as %s -o %t1.o | ||
| ; RUN: llvm-as %p/Inputs/alias-1.ll -o %t2.o | ||
| ; RUN: llvm-lto2 -o %t3.o %t2.o %t1.o -r %t2.o,a,px -r %t1.o,a, -r %t1.o,b,px -save-temps | ||
| ; RUN: llvm-dis < %t3.o.0.preopt.bc -o - | FileCheck %s | ||
| ; RUN: FileCheck --check-prefix=RES %s < %t3.o.resolution.txt | ||
|
|
||
| ; CHECK-NOT: alias | ||
| ; CHECK: @a = global i32 42 | ||
| ; CHECK-NEXT: @b = global i32 1 | ||
| ; CHECK-NOT: alias | ||
|
|
||
| ; RES: 2.o{{$}} | ||
| ; RES: {{^}}-r={{.*}}2.o,a,px{{$}} | ||
| ; RES: 1.o{{$}} | ||
| ; RES: {{^}}-r={{.*}}1.o,b,px{{$}} | ||
| ; RES: {{^}}-r={{.*}}1.o,a,{{$}} | ||
|
|
||
| target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | ||
| target triple = "x86_64-unknown-linux-gnu" | ||
|
|
||
| @a = weak alias i32, i32* @b | ||
| @b = global i32 1 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| ; RUN: llvm-as %s -o %t.o | ||
| ; RUN: llvm-as %p/Inputs/comdat.ll -o %t2.o | ||
| ; RUN: llvm-lto2 -save-temps -o %t3.o %t.o %t2.o \ | ||
| ; RUN: -r=%t.o,f1,plx \ | ||
| ; RUN: -r=%t.o,v1,px \ | ||
| ; RUN: -r=%t.o,r11,px \ | ||
| ; RUN: -r=%t.o,r12,px \ | ||
| ; RUN: -r=%t.o,a11,px \ | ||
| ; RUN: -r=%t.o,a12,px \ | ||
| ; RUN: -r=%t.o,a13,px \ | ||
| ; RUN: -r=%t.o,a14,px \ | ||
| ; RUN: -r=%t.o,a15,px \ | ||
| ; RUN: -r=%t2.o,f1,l \ | ||
| ; RUN: -r=%t2.o,will_be_undefined, \ | ||
| ; RUN: -r=%t2.o,v1, \ | ||
| ; RUN: -r=%t2.o,r21,px \ | ||
| ; RUN: -r=%t2.o,r22,px \ | ||
| ; RUN: -r=%t2.o,a21,px \ | ||
| ; RUN: -r=%t2.o,a22,px \ | ||
| ; RUN: -r=%t2.o,a23,px \ | ||
| ; RUN: -r=%t2.o,a24,px \ | ||
| ; RUN: -r=%t2.o,a25,px | ||
| ; RUN: llvm-dis %t3.o.2.internalize.bc -o - | FileCheck %s | ||
|
|
||
| target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | ||
| target triple = "x86_64-unknown-linux-gnu" | ||
|
|
||
| $c1 = comdat any | ||
|
|
||
| @v1 = weak_odr global i32 42, comdat($c1) | ||
| define weak_odr i32 @f1(i8*) comdat($c1) { | ||
| bb10: | ||
| br label %bb11 | ||
| bb11: | ||
| ret i32 42 | ||
| } | ||
|
|
||
| @r11 = global i32* @v1 | ||
| @r12 = global i32 (i8*)* @f1 | ||
|
|
||
| @a11 = alias i32, i32* @v1 | ||
| @a12 = alias i16, bitcast (i32* @v1 to i16*) | ||
|
|
||
| @a13 = alias i32 (i8*), i32 (i8*)* @f1 | ||
| @a14 = alias i16, bitcast (i32 (i8*)* @f1 to i16*) | ||
| @a15 = alias i16, i16* @a14 | ||
|
|
||
| ; CHECK: $c1 = comdat any | ||
| ; CHECK: $c2 = comdat any | ||
|
|
||
| ; CHECK-DAG: @v1 = weak_odr global i32 42, comdat($c1) | ||
|
|
||
| ; CHECK-DAG: @r11 = global i32* @v1{{$}} | ||
| ; CHECK-DAG: @r12 = global i32 (i8*)* @f1{{$}} | ||
|
|
||
| ; CHECK-DAG: @r21 = global i32* @v1{{$}} | ||
| ; CHECK-DAG: @r22 = global i32 (i8*)* @f1{{$}} | ||
|
|
||
| ; CHECK-DAG: @v1.1 = internal global i32 41, comdat($c2) | ||
|
|
||
| ; CHECK-DAG: @a11 = alias i32, i32* @v1{{$}} | ||
| ; CHECK-DAG: @a12 = alias i16, bitcast (i32* @v1 to i16*) | ||
|
|
||
| ; CHECK-DAG: @a13 = alias i32 (i8*), i32 (i8*)* @f1{{$}} | ||
| ; CHECK-DAG: @a14 = alias i16, bitcast (i32 (i8*)* @f1 to i16*) | ||
|
|
||
| ; CHECK-DAG: @a21 = alias i32, i32* @v1.1{{$}} | ||
| ; CHECK-DAG: @a22 = alias i16, bitcast (i32* @v1.1 to i16*) | ||
|
|
||
| ; CHECK-DAG: @a23 = alias i32 (i8*), i32 (i8*)* @f1.2{{$}} | ||
| ; CHECK-DAG: @a24 = alias i16, bitcast (i32 (i8*)* @f1.2 to i16*) | ||
|
|
||
| ; CHECK: define weak_odr i32 @f1(i8*) comdat($c1) { | ||
| ; CHECK-NEXT: bb10: | ||
| ; CHECK-NEXT: br label %bb11{{$}} | ||
| ; CHECK: bb11: | ||
| ; CHECK-NEXT: ret i32 42 | ||
| ; CHECK-NEXT: } | ||
|
|
||
| ; CHECK: define internal i32 @f1.2(i8* %this) comdat($c2) { | ||
| ; CHECK-NEXT: bb20: | ||
| ; CHECK-NEXT: store i8* %this, i8** null | ||
| ; CHECK-NEXT: br label %bb21 | ||
| ; CHECK: bb21: | ||
| ; CHECK-NEXT: ret i32 41 | ||
| ; CHECK-NEXT: } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| if not 'X86' in config.root.targets: | ||
| config.unsupported = True |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| ; RUN: llvm-as %s -o %t.bc | ||
| ; RUN: not llvm-lto2 -o %t2.o %t.bc 2>&1 | FileCheck --check-prefix=ERR1 %s | ||
| ; RUN: not llvm-lto2 -o %t2.o -r %t.bc,foo,p -r %t.bc,bar,p %t.bc 2>&1 | FileCheck --check-prefix=ERR2 %s | ||
| ; RUN: not llvm-lto2 -o %t2.o -r %t.bc,foo,q %t.bc 2>&1 | FileCheck --check-prefix=ERR3 %s | ||
| ; RUN: not llvm-lto2 -o %t2.o -r foo %t.bc 2>&1 | FileCheck --check-prefix=ERR4 %s | ||
|
|
||
| ; ERR1: missing symbol resolution for {{.*}}.bc,foo | ||
| ; ERR2: unused symbol resolution for {{.*}}.bc,bar | ||
| ; ERR3: invalid character q in resolution: {{.*}}.bc,foo | ||
| ; ERR4: invalid resolution: foo | ||
| @foo = global i32 0 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| set(LLVM_LINK_COMPONENTS | ||
| ${LLVM_TARGETS_TO_BUILD} | ||
| LTO | ||
| Object | ||
| Support | ||
| ) | ||
|
|
||
| add_llvm_tool(llvm-lto2 | ||
| llvm-lto2.cpp | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| ;===- ./tools/llvm-lto2/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 = Tool | ||
| name = llvm-lto2 | ||
| parent = Tools | ||
| required_libraries = LTO Object all-targets |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,168 @@ | ||
| //===-- llvm-lto2: test harness for the resolution-based LTO interface ----===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This program takes in a list of bitcode files, links them and performs | ||
| // link-time optimization according to the provided symbol resolutions using the | ||
| // resolution-based LTO interface, and outputs one or more object files. | ||
| // | ||
| // This program is intended to eventually replace llvm-lto which uses the legacy | ||
| // LTO interface. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "llvm/LTO/LTO.h" | ||
| #include "llvm/Support/CommandLine.h" | ||
| #include "llvm/Support/TargetSelect.h" | ||
|
|
||
| using namespace llvm; | ||
| using namespace lto; | ||
| using namespace object; | ||
|
|
||
| static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore, | ||
| cl::desc("<input bitcode files>")); | ||
|
|
||
| static cl::opt<std::string> OutputFilename("o", cl::Required, | ||
| cl::desc("Output filename"), | ||
| cl::value_desc("filename")); | ||
|
|
||
| static cl::opt<bool> SaveTemps("save-temps", cl::desc("Save temporary files")); | ||
|
|
||
| static cl::list<std::string> SymbolResolutions( | ||
| "r", | ||
| cl::desc("Specify a symbol resolution: filename,symbolname,resolution\n" | ||
| "where \"resolution\" is a sequence (which may be empty) of the\n" | ||
| "following characters:\n" | ||
| " p - prevailing: the linker has chosen this definition of the\n" | ||
| " symbol\n" | ||
| " l - local: the definition of this symbol is unpreemptable at\n" | ||
| " runtime and is known to be in this linkage unit\n" | ||
| " x - externally visible: the definition of this symbol is\n" | ||
| " visible outside of the LTO unit\n" | ||
| "A resolution for each symbol must be specified."), | ||
| cl::ZeroOrMore); | ||
|
|
||
| static void check(Error E, std::string Msg) { | ||
| if (!E) | ||
| return; | ||
| handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) { | ||
| errs() << "llvm-lto: " << Msg << ": " << EIB.message().c_str() << '\n'; | ||
| }); | ||
| exit(1); | ||
| } | ||
|
|
||
| template <typename T> static T check(Expected<T> E, std::string Msg) { | ||
| if (E) | ||
| return std::move(*E); | ||
| check(E.takeError(), Msg); | ||
| return T(); | ||
| } | ||
|
|
||
| static void check(std::error_code EC, std::string Msg) { | ||
| check(errorCodeToError(EC), Msg); | ||
| } | ||
|
|
||
| template <typename T> static T check(ErrorOr<T> E, std::string Msg) { | ||
| if (E) | ||
| return std::move(*E); | ||
| check(E.getError(), Msg); | ||
| return T(); | ||
| } | ||
|
|
||
| int main(int argc, char **argv) { | ||
| InitializeAllTargets(); | ||
| InitializeAllTargetMCs(); | ||
| InitializeAllAsmPrinters(); | ||
| InitializeAllAsmParsers(); | ||
|
|
||
| cl::ParseCommandLineOptions(argc, argv, "Resolution-based LTO test harness"); | ||
|
|
||
| std::map<std::pair<std::string, std::string>, SymbolResolution> | ||
| CommandLineResolutions; | ||
| for (std::string R : SymbolResolutions) { | ||
| StringRef Rest = R; | ||
| StringRef FileName, SymbolName; | ||
| std::tie(FileName, Rest) = Rest.split(','); | ||
| if (Rest.empty()) { | ||
| llvm::errs() << "invalid resolution: " << R << '\n'; | ||
| return 1; | ||
| } | ||
| std::tie(SymbolName, Rest) = Rest.split(','); | ||
| SymbolResolution Res; | ||
| for (char C : Rest) { | ||
| if (C == 'p') | ||
| Res.Prevailing = true; | ||
| else if (C == 'l') | ||
| Res.FinalDefinitionInLinkageUnit = true; | ||
| else if (C == 'x') | ||
| Res.VisibleToRegularObj = true; | ||
| else | ||
| llvm::errs() << "invalid character " << C << " in resolution: " << R | ||
| << '\n'; | ||
| } | ||
| CommandLineResolutions[{FileName, SymbolName}] = Res; | ||
| } | ||
|
|
||
| std::vector<std::unique_ptr<MemoryBuffer>> MBs; | ||
|
|
||
| Config Conf; | ||
| Conf.DiagHandler = [](const DiagnosticInfo &) { | ||
| exit(1); | ||
| }; | ||
|
|
||
| if (SaveTemps) | ||
| check(Conf.addSaveTemps(OutputFilename), "Config::addSaveTemps failed"); | ||
|
|
||
| LTO Lto(std::move(Conf)); | ||
|
|
||
| bool HasErrors = false; | ||
| for (std::string F : InputFilenames) { | ||
| std::unique_ptr<MemoryBuffer> MB = check(MemoryBuffer::getFile(F), F); | ||
| std::unique_ptr<InputFile> Input = | ||
| check(InputFile::create(MB->getMemBufferRef()), F); | ||
|
|
||
| std::vector<SymbolResolution> Res; | ||
| for (const InputFile::Symbol &Sym : Input->symbols()) { | ||
| auto I = CommandLineResolutions.find({F, Sym.getName()}); | ||
| if (I == CommandLineResolutions.end()) { | ||
| llvm::errs() << argv[0] << ": missing symbol resolution for " << F | ||
| << ',' << Sym.getName() << '\n'; | ||
| HasErrors = true; | ||
| } else { | ||
| Res.push_back(I->second); | ||
| CommandLineResolutions.erase(I); | ||
| } | ||
| } | ||
|
|
||
| if (HasErrors) | ||
| continue; | ||
|
|
||
| MBs.push_back(std::move(MB)); | ||
| check(Lto.add(std::move(Input), Res), F); | ||
| } | ||
|
|
||
| if (!CommandLineResolutions.empty()) { | ||
| HasErrors = true; | ||
| for (auto UnusedRes : CommandLineResolutions) | ||
| llvm::errs() << argv[0] << ": unused symbol resolution for " | ||
| << UnusedRes.first.first << ',' << UnusedRes.first.second | ||
| << '\n'; | ||
| } | ||
| if (HasErrors) | ||
| return 1; | ||
|
|
||
| auto AddStream = [&](size_t Task) { | ||
| std::string Path = OutputFilename + "." + utostr(Task); | ||
| std::error_code EC; | ||
| auto S = make_unique<raw_fd_ostream>(Path, EC, sys::fs::F_None); | ||
| check(EC, Path); | ||
| return S; | ||
| }; | ||
|
|
||
| check(Lto.run(AddStream), "LTO::run failed"); | ||
| } |