diff --git a/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h --- a/llvm/include/llvm/MC/MCContext.h +++ b/llvm/include/llvm/MC/MCContext.h @@ -396,7 +396,6 @@ void initInlineSourceManager(); SourceMgr *getInlineSourceManager() { - assert(InlineSrcMgr); return InlineSrcMgr.get(); } std::vector &getLocInfos() { return LocInfos; } diff --git a/llvm/include/llvm/MC/MCParser/MCAsmParser.h b/llvm/include/llvm/MC/MCParser/MCAsmParser.h --- a/llvm/include/llvm/MC/MCParser/MCAsmParser.h +++ b/llvm/include/llvm/MC/MCParser/MCAsmParser.h @@ -182,6 +182,8 @@ virtual void setParsingMSInlineAsm(bool V) = 0; virtual bool isParsingMSInlineAsm() = 0; + virtual bool discardLTOSymbol(StringRef) const { return false; } + virtual bool isParsingMasm() const { return false; } virtual bool defineMacro(StringRef Name, StringRef Value) { return true; } diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -12,6 +12,7 @@ #include "llvm/LTO/LTO.h" #include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/StackSafetyAnalysis.h" #include "llvm/Analysis/TargetLibraryInfo.h" @@ -752,6 +753,7 @@ Skip(); std::set NonPrevailingComdats; + std::set NonPrevailingAsmSymbols; for (const InputFile::Symbol &Sym : Syms) { assert(ResI != ResE); SymbolResolution Res = *ResI++; @@ -798,7 +800,13 @@ GV->setDLLStorageClass(GlobalValue::DLLStorageClassTypes:: DefaultStorageClass); } - } + } else if (auto *AS = Msym.dyn_cast()) { + // Collect non-prevailing symbols. + if (!Res.Prevailing) + NonPrevailingAsmSymbols.insert(AS->first); + } else + llvm_unreachable("unknown symbol type"); + // Common resolution: collect the maximum size/alignment over all commons. // We also record if we see an instance of a common as prevailing, so that // if none is prevailing we can ignore it later. @@ -812,11 +820,29 @@ CommonRes.Align = max(*SymAlign, CommonRes.Align); CommonRes.Prevailing |= Res.Prevailing; } - } + if (!M.getComdatSymbolTable().empty()) for (GlobalValue &GV : M.global_values()) handleNonPrevailingComdat(GV, NonPrevailingComdats); + + // Prepend ".module" to each module inline asm block. Also emit a + // ".lto_discard , *" directive if needed. + if (!M.getModuleInlineAsm().empty()) { + std::string NewIA = ".module\n"; + if (!NonPrevailingAsmSymbols.empty()) { + // Don't dicard a symbol if there is a live .symver for it. + ModuleSymbolTable::CollectAsmSymvers( + M, [&](StringRef Name, StringRef Alias) { + if (!NonPrevailingAsmSymbols.count(Alias)) + NonPrevailingAsmSymbols.erase(Name); + }); + NewIA += + ".lto_discard " + llvm::join(NonPrevailingAsmSymbols, ", ") + "\n"; + } + M.setModuleInlineAsm(NewIA + M.getModuleInlineAsm()); + } + assert(MsymI == MsymE); return std::move(Mod); } diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -15,6 +15,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/None.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" @@ -54,6 +55,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SMLoc.h" +#include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" #include @@ -168,6 +170,9 @@ /// List of forward directional labels for diagnosis at the end. SmallVector, 4> DirLabels; + bool EnableLTODiscardSymbols = false; + SmallSet LTODiscardSymbols; + /// AssemblerDialect. ~OU means unset value and use value provided by MAI. unsigned AssemblerDialect = ~0U; @@ -235,6 +240,12 @@ } bool isParsingMSInlineAsm() override { return ParsingMSInlineAsm; } + bool discardLTOSymbol(StringRef Name) const override { + if (!LTODiscardSymbols.empty()) + assert(EnableLTODiscardSymbols); + return LTODiscardSymbols.contains(Name); + } + bool parseMSInlineAsm(void *AsmLoc, std::string &AsmString, unsigned &NumOutputs, unsigned &NumInputs, SmallVectorImpl> &OpDecls, @@ -516,6 +527,8 @@ DK_ADDRSIG, DK_ADDRSIG_SYM, DK_PSEUDO_PROBE, + DK_MOUDLE, + DK_LTO_DISCARD, DK_END }; @@ -682,6 +695,12 @@ // .pseudoprobe bool parseDirectivePseudoProbe(); + // ".module" + bool parseDirectiveModule(); + + // ".lto_discard" + bool parseDirectiveLTODiscard(); + // Directives to support address-significance tables. bool parseDirectiveAddrsig(); bool parseDirectiveAddrsigSym(); @@ -892,6 +911,11 @@ } bool AsmParser::Run(bool NoInitialTextSection, bool NoFinalize) { + // Only enable EnableLTODiscardSymbols for inline assembly. + SaveAndRestore SaveLTODiscard(EnableLTODiscardSymbols, + Ctx.getInlineSourceManager() != nullptr); + LTODiscardSymbols.clear(); + // Create the initial section, if requested. if (!NoInitialTextSection) Out.InitSections(false); @@ -1770,7 +1794,6 @@ StringMap::const_iterator DirKindIt = DirectiveKindMap.find(IDVal.lower()); DirectiveKind DirKind = (DirKindIt == DirectiveKindMap.end()) - ? DK_NO_DIRECTIVE : DirKindIt->getValue(); switch (DirKind) { @@ -1868,6 +1891,9 @@ Lex(); } + if (discardLTOSymbol(IDVal)) + return false; + getTargetParser().doBeforeLabelEmit(Sym); // Emit the label. @@ -2208,6 +2234,10 @@ return parseDirectiveAddrsigSym(); case DK_PSEUDO_PROBE: return parseDirectivePseudoProbe(); + case DK_MOUDLE: + return parseDirectiveModule(); + case DK_LTO_DISCARD: + return parseDirectiveLTODiscard(); } return Error(IDLoc, "unknown directive"); @@ -2852,6 +2882,9 @@ return false; } + if (discardLTOSymbol(Name)) + return false; + // Do the assignment. Out.emitAssignment(Sym, Value); if (NoDeadStrip) @@ -5493,6 +5526,8 @@ DirectiveKindMap[".addrsig"] = DK_ADDRSIG; DirectiveKindMap[".addrsig_sym"] = DK_ADDRSIG_SYM; DirectiveKindMap[".pseudoprobe"] = DK_PSEUDO_PROBE; + DirectiveKindMap[".module"] = DK_MOUDLE; + DirectiveKindMap[".lto_discard"] = DK_LTO_DISCARD; } MCAsmMacro *AsmParser::parseMacroLikeBody(SMLoc DirectiveLoc) { @@ -5806,6 +5841,31 @@ return false; } +bool AsmParser::parseDirectiveModule() { + if (parseEOL()) + return true; + LTODiscardSymbols.clear(); + return false; +} + +/// parseDirectiveLTODiscard +/// ::= ".lto_discard" [ identifier ( , identifier )* ] +bool AsmParser::parseDirectiveLTODiscard() { + auto parseOp = [&]() -> bool { + StringRef Name; + SMLoc Loc = getTok().getLoc(); + if (parseIdentifier(Name)) + return Error(Loc, "expected identifier"); + if (EnableLTODiscardSymbols) + LTODiscardSymbols.insert(Name); + return false; + }; + + if (parseMany(parseOp)) + return addErrorSuffix(" in directive"); + return false; +} + // We are comparing pointers, but the pointers are relative to a single string. // Thus, this should always be deterministic. static int rewritesSort(const AsmRewrite *AsmRewriteA, diff --git a/llvm/lib/MC/MCParser/ELFAsmParser.cpp b/llvm/lib/MC/MCParser/ELFAsmParser.cpp --- a/llvm/lib/MC/MCParser/ELFAsmParser.cpp +++ b/llvm/lib/MC/MCParser/ELFAsmParser.cpp @@ -182,6 +182,12 @@ if (getParser().parseIdentifier(Name)) return TokError("expected identifier in directive"); + if (getParser().discardLTOSymbol(Name)) { + if (getLexer().is(AsmToken::EndOfStatement)) + break; + continue; + } + MCSymbol *Sym = getContext().getOrCreateSymbol(Name); getStreamer().emitSymbolAttribute(Sym, Attr); diff --git a/llvm/test/LTO/X86/weak-asm.ll b/llvm/test/LTO/X86/weak-asm.ll new file mode 100644 --- /dev/null +++ b/llvm/test/LTO/X86/weak-asm.ll @@ -0,0 +1,70 @@ +; Check that non-prevailing symbols in module inline assembly are discarded +; during regular LTO otherwise the final symbol binding could be wrong. + +; RUN: split-file %s %t +; RUN: opt %t/t1.ll -o %t1 +; RUN: opt %t/t2.ll -o %t2 +; RUN: opt %t/t3.ll -o %t3 + +; RUN: llvm-lto2 run -o %to1 -save-temps %t1 %t2 \ +; RUN: -r %t1,foo,px \ +; RUN: -r %t2,foo, \ +; RUN: -r %t2,bar,pl +; RUN: llvm-dis < %to1.0.0.preopt.bc -o - | FileCheck %s --check-prefix=ASM1 +; RUN: llvm-nm %to1.0 | FileCheck %s --check-prefix=SYM +; RUN: llvm-objdump -d --disassemble-symbols=foo %to1.0 \ +; RUN: | FileCheck %s --check-prefix=DEF + +; RUN: llvm-lto2 run -o %to2 -save-temps %t2 %t3 \ +; RUN: -r %t2,foo, \ +; RUN: -r %t2,bar,pl \ +; RUN: -r %t3,foo,px +; RUN: llvm-dis < %to2.0.0.preopt.bc -o - | FileCheck %s --check-prefix=ASM2 +; RUN: llvm-nm %to2.0 | FileCheck %s --check-prefix=SYM +; RUN: llvm-objdump -d --disassemble-symbols=foo %to2.0 \ +; RUN: | FileCheck %s --check-prefix=DEF + +; ASM1: module asm ".module" +; ASM1-NEXT: module asm ".lto_discard foo" +; ASM1-NEXT: module asm ".weak foo" +; ASM1-NEXT: module asm ".equ foo,bar" + +; ASM2: module asm ".module" +; ASM2-NEXT: module asm ".lto_discard foo" +; ASM2-NEXT: module asm ".weak foo" +; ASM2-NEXT: module asm ".equ foo,bar" +; ASM2-NEXT: module asm ".module" +; ASM2-NEXT: module asm " .global foo ; foo: leal 2(%rdi), %eax" + +; SYM: T foo + +; DEF: leal 2(%rdi), %eax + +;--- t1.ll +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define dso_local i32 @foo(i32 %0) { + %2 = add nsw i32 %0, 2 + ret i32 %2 +} + +;--- t2.ll +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +module asm ".weak foo" +module asm ".equ foo,bar" + +@llvm.compiler.used = appending global [1 x i8*] [i8* bitcast (i32 (i32)* @bar to i8*)], section "llvm.metadata" + +define internal i32 @bar(i32 %0) { + %2 = add nsw i32 %0, 1 + ret i32 %2 +} + +;--- t3.ll +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +module asm " .global foo ; foo: leal 2(%rdi), %eax"