Index: include/llvm/MC/MCParser/MCAsmParser.h =================================================================== --- include/llvm/MC/MCParser/MCAsmParser.h +++ include/llvm/MC/MCParser/MCAsmParser.h @@ -11,11 +11,12 @@ #define LLVM_MC_MCPARSER_MCASMPARSER_H #include "llvm/ADT/None.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/Support/SMLoc.h" #include @@ -121,8 +122,12 @@ bool getShowParsedOperands() const { return ShowParsedOperands; } void setShowParsedOperands(bool Value) { ShowParsedOperands = Value; } - /// \brief Run the parser on the input source buffer. - virtual bool Run(bool NoInitialTextSection, bool NoFinalize = false) = 0; + /// \brief Run the parser on the input source buffer. A + /// \p GetSymbolAttr callback can be provided to allow the client to + /// provide a symbol attribute given the symbol name. + virtual bool + Run(bool NoInitialTextSection, bool NoFinalize = false, + std::function *GetSymbolAttr = nullptr) = 0; virtual void setParsingInlineAsm(bool V) = 0; virtual bool isParsingInlineAsm() = 0; Index: include/llvm/MC/MCParser/MCAsmParserExtension.h =================================================================== --- include/llvm/MC/MCParser/MCAsmParserExtension.h +++ include/llvm/MC/MCParser/MCAsmParserExtension.h @@ -12,6 +12,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCAsmParser.h" #include "llvm/Support/SMLoc.h" @@ -113,6 +114,12 @@ bool HasBracketExpressions() const { return BracketExpressionsSupported; } + /// Perform any required symbol binding at the end of parsing. A + /// \p GetSymbolAttr callback can be provided to allow the client to + /// provide a symbol attribute given the symbol name. + virtual void finalizeSymbolBinding( + std::function *GetSymbolAttr) {} + /// @} }; Index: include/llvm/Object/ModuleSymbolTable.h =================================================================== --- include/llvm/Object/ModuleSymbolTable.h +++ include/llvm/Object/ModuleSymbolTable.h @@ -52,7 +52,7 @@ /// For each found symbol, call \p AsmSymbol with the name of the symbol found /// and the associated flags. static void CollectAsmSymbols( - const Triple &TheTriple, StringRef InlineAsm, + const Module &M, function_ref AsmSymbol); }; Index: lib/Analysis/ModuleSummaryAnalysis.cpp =================================================================== --- lib/Analysis/ModuleSummaryAnalysis.cpp +++ lib/Analysis/ModuleSummaryAnalysis.cpp @@ -410,9 +410,8 @@ // be listed on the llvm.used or llvm.compiler.used global and marked as // referenced from there. ModuleSymbolTable::CollectAsmSymbols( - Triple(M.getTargetTriple()), M.getModuleInlineAsm(), - [&M, &Index, &CantBePromoted](StringRef Name, - object::BasicSymbolRef::Flags Flags) { + M, [&M, &Index, &CantBePromoted](StringRef Name, + object::BasicSymbolRef::Flags Flags) { // Symbols not marked as Weak or Global are local definitions. if (Flags & (object::BasicSymbolRef::SF_Weak | object::BasicSymbolRef::SF_Global)) Index: lib/LTO/LTOBackend.cpp =================================================================== --- lib/LTO/LTOBackend.cpp +++ lib/LTO/LTOBackend.cpp @@ -354,7 +354,7 @@ // llvm.compiler.used to prevent optimization to drop these from the output. StringSet<> AsmUndefinedRefs; ModuleSymbolTable::CollectAsmSymbols( - Triple(Mod.getTargetTriple()), Mod.getModuleInlineAsm(), + Mod, [&AsmUndefinedRefs](StringRef Name, object::BasicSymbolRef::Flags Flags) { if (Flags & object::BasicSymbolRef::SF_Undefined) AsmUndefinedRefs.insert(Name); Index: lib/MC/MCParser/AsmParser.cpp =================================================================== --- lib/MC/MCParser/AsmParser.cpp +++ lib/MC/MCParser/AsmParser.cpp @@ -214,7 +214,9 @@ AsmParser &operator=(const AsmParser &) = delete; ~AsmParser() override; - bool Run(bool NoInitialTextSection, bool NoFinalize = false) override; + bool Run(bool NoInitialTextSection, bool NoFinalize = false, + std::function *GetSymbolAttr = + nullptr) override; void addDirectiveHandler(StringRef Directive, ExtensionDirectiveHandler Handler) override { @@ -727,7 +729,9 @@ return *tok; } -bool AsmParser::Run(bool NoInitialTextSection, bool NoFinalize) { +bool AsmParser::Run( + bool NoInitialTextSection, bool NoFinalize, + std::function *GetSymbolAttr) { // Create the initial section, if requested. if (!NoInitialTextSection) Out.InitSections(false); @@ -780,6 +784,8 @@ getTargetParser().flushPendingInstructions(getStreamer()); + PlatformParser->finalizeSymbolBinding(GetSymbolAttr); + if (TheCondState.TheCond != StartingCondState.TheCond || TheCondState.Ignore != StartingCondState.Ignore) printError(getTok().getLoc(), "unmatched .ifs or .elses"); Index: lib/MC/MCParser/ELFAsmParser.cpp =================================================================== --- lib/MC/MCParser/ELFAsmParser.cpp +++ lib/MC/MCParser/ELFAsmParser.cpp @@ -46,6 +46,12 @@ bool ParseSectionSwitch(StringRef Section, unsigned Type, unsigned Flags, SectionKind Kind); + // Map of aliases created by .symver directives + DenseMap SymverAliasMap; + + // Map to keep track of symbol attributes specified in asm + DenseMap SymAttrMap; + public: ELFAsmParser() { BracketExpressionsSupported = true; } @@ -78,6 +84,9 @@ addDirectiveHandler<&ELFAsmParser::ParseDirectiveWeakref>(".weakref"); addDirectiveHandler<&ELFAsmParser::ParseDirectiveSymbolAttribute>(".weak"); addDirectiveHandler<&ELFAsmParser::ParseDirectiveSymbolAttribute>(".local"); + addDirectiveHandler<&ELFAsmParser::ParseDirectiveSymbolAttribute>( + ".global"); + addDirectiveHandler<&ELFAsmParser::ParseDirectiveSymbolAttribute>(".globl"); addDirectiveHandler< &ELFAsmParser::ParseDirectiveSymbolAttribute>(".protected"); addDirectiveHandler< @@ -150,6 +159,12 @@ bool ParseDirectiveSymbolAttribute(StringRef, SMLoc); bool ParseDirectiveSubsection(StringRef, SMLoc); + /// Perform any required symbol binding at the end of parsing. A + /// \p GetSymbolAttr callback can be provided to allow the client to + /// provide a symbol attribute given the symbol name. + void finalizeSymbolBinding( + std::function *GetSymbolAttr) override; + private: bool ParseSectionName(StringRef &SectionName); bool ParseSectionArguments(bool IsPush, SMLoc loc); @@ -169,24 +184,27 @@ MCSymbolAttr Attr = StringSwitch(Directive) .Case(".weak", MCSA_Weak) .Case(".local", MCSA_Local) + .Case(".global", MCSA_Global) + .Case(".globl", MCSA_Global) .Case(".hidden", MCSA_Hidden) .Case(".internal", MCSA_Internal) .Case(".protected", MCSA_Protected) .Default(MCSA_Invalid); assert(Attr != MCSA_Invalid && "unexpected symbol attribute directive!"); + auto parseOp = [&]() -> bool { StringRef Name; if (getParser().parseIdentifier(Name)) return true; MCSymbol *Sym = getContext().getOrCreateSymbol(Name); getStreamer().EmitSymbolAttribute(Sym, Attr); + SymAttrMap[Sym] = Attr; return false; }; if (parseMany(parseOp)) return addErrorSuffix(" in '" + Twine(Directive) + "' directive"); - Lex(); return false; } @@ -682,6 +700,7 @@ Lex(); getStreamer().EmitSymbolAttribute(Sym, Attr); + SymAttrMap[Sym] = Attr; return false; } @@ -735,9 +754,35 @@ const MCExpr *Value = MCSymbolRefExpr::create(Sym, getContext()); getStreamer().EmitAssignment(Alias, Value); + + SymverAliasMap[Alias] = Sym; + return false; } +void ELFAsmParser::finalizeSymbolBinding( + std::function *GetSymbolAttr) { + // Aliases created by .symver directives get the same symbol binding + // as the aliased symbol. The binding is finalized at the end of parsing, + // when all symbol attributes have been parsed. A GetSymbolAttr + // callback can be provided to enable getting the binding for symbols + // defined in IR. + for (const auto &I : SymverAliasMap) { + MCSymbol *Alias = I.first; + const MCSymbol *Sym = I.second; + const auto &SymAttr = SymAttrMap.find(Sym); + // First check if the attribute was specified in the asm. + if (SymAttr != SymAttrMap.end()) + getStreamer().EmitSymbolAttribute(Alias, SymAttr->second); + // If a callback is provided, use it to get the attribute. + else if (GetSymbolAttr) { + auto Attr = (*GetSymbolAttr)(Sym->getName()); + if (Attr != MCSA_Invalid) + getStreamer().EmitSymbolAttribute(Alias, Attr); + } + } +} + /// ParseDirectiveVersion /// ::= .version string bool ELFAsmParser::ParseDirectiveVersion(StringRef, SMLoc) { Index: lib/Object/ModuleSymbolTable.cpp =================================================================== --- lib/Object/ModuleSymbolTable.cpp +++ lib/Object/ModuleSymbolTable.cpp @@ -50,20 +50,20 @@ for (GlobalAlias &GA : M->aliases()) SymTab.push_back(&GA); - CollectAsmSymbols(Triple(M->getTargetTriple()), M->getModuleInlineAsm(), - [this](StringRef Name, BasicSymbolRef::Flags Flags) { - SymTab.push_back(new (AsmSymbols.Allocate()) - AsmSymbol(Name, Flags)); - }); + CollectAsmSymbols(*M, [this](StringRef Name, BasicSymbolRef::Flags Flags) { + SymTab.push_back(new (AsmSymbols.Allocate()) AsmSymbol(Name, Flags)); + }); } void ModuleSymbolTable::CollectAsmSymbols( - const Triple &TT, StringRef InlineAsm, + const Module &M, function_ref AsmSymbol) { + StringRef InlineAsm = M.getModuleInlineAsm(); if (InlineAsm.empty()) return; std::string Err; + const Triple TT(M.getTargetTriple()); const Target *T = TargetRegistry::lookupTarget(TT.str(), Err); assert(T && T->hasMCAsmParser()); @@ -103,7 +103,25 @@ return; Parser->setTargetParser(*TAP); - if (Parser->Run(false)) + + // If a symbol exists for the given Name, map its linkage type to + // a symbol attribute. For use in determing the binding of symbols + // used in assembly but defined in IR. + std::function GetSymbolAttr = + [&](StringRef Name) -> MCSymbolAttr { + auto *GV = M.getNamedValue(Name); + if (!GV) + return MCSA_Invalid; + if (GV->hasExternalLinkage()) + return MCSA_Global; + else if (GV->hasLocalLinkage()) + return MCSA_Local; + else if (GV->isWeakForLinker()) + return MCSA_Weak; + return MCSA_Invalid; + }; + + if (Parser->Run(false, true, &GetSymbolAttr)) return; for (auto &KV : Streamer) { Index: lib/Transforms/IPO/FunctionImport.cpp =================================================================== --- lib/Transforms/IPO/FunctionImport.cpp +++ lib/Transforms/IPO/FunctionImport.cpp @@ -598,7 +598,7 @@ // the current module. StringSet<> AsmUndefinedRefs; ModuleSymbolTable::CollectAsmSymbols( - Triple(TheModule.getTargetTriple()), TheModule.getModuleInlineAsm(), + TheModule, [&AsmUndefinedRefs](StringRef Name, object::BasicSymbolRef::Flags Flags) { if (Flags & object::BasicSymbolRef::SF_Undefined) AsmUndefinedRefs.insert(Name); Index: test/LTO/X86/symver-asm.ll =================================================================== --- test/LTO/X86/symver-asm.ll +++ test/LTO/X86/symver-asm.ll @@ -1,7 +1,7 @@ ; RUN: llvm-as < %s >%t1 ; RUN: llvm-lto -exported-symbol=io_cancel_0_4 -exported-symbol=io_cancel_weak_0_4 -o %t2 %t1 ; RUN: llvm-nm %t2 | FileCheck %s -; RUN: llvm-lto2 -r %t1,io_cancel_0_4,plx -r %t1,io_cancel_0_4,plx -r %t1,io_cancel_local_0_4,plx -o %t3 %t1 -save-temps +; RUN: llvm-lto2 -r %t1,io_cancel_0_4,plx -r %t1,io_cancel_0_4,plx -r %t1,io_cancel_local_0_4,plx -r %t1,io_cancel_weak_0_4,plx -r %t1,io_cancel_weak_0_4,plx -r %t1,io_cancel@@LIBAIO_0.4,plx -r %t1,io_cancel_weak@@LIBAIO_0.4,plx -o %t3 %t1 -save-temps ; RUN: llvm-nm %t3.0 | FileCheck %s ; RUN: llvm-dis %t3.0.2.internalize.bc -o - | FileCheck %s --check-prefix=INTERN @@ -10,6 +10,7 @@ module asm ".symver io_cancel_0_4,io_cancel@@LIBAIO_0.4" module asm ".symver io_cancel_local_0_4,io_cancel_local@@LIBAIO_0.4" +module asm ".symver io_cancel_weak_0_4,io_cancel_weak@@LIBAIO_0.4" ; Even without -exported-symbol, io_cancel_0_4 should be noticed by LTOModule's ; RecordStreamer, so it shouldn't get eliminated. However, the object file will @@ -27,3 +28,9 @@ ; CHECK-DAG: t io_cancel_local_0_4 ret i32 0 } + +define weak i32 @io_cancel_weak_0_4() { +; CHECK-DAG: W io_cancel_weak@@LIBAIO_0.4 +; CHECK-DAG: W io_cancel_weak_0_4 +ret i32 0 +} Index: test/LTO/X86/symver-asm2.ll =================================================================== --- /dev/null +++ test/LTO/X86/symver-asm2.ll @@ -0,0 +1,30 @@ +; Test to ensure symbol binding works correctly for symver directives, +; when the aliased symbols are defined in inline assembly, including +; cases when the symbol attributes are provided after the .symver +; directive. + +; RUN: llvm-as < %s >%t1 +; RUN: llvm-lto -o %t2 %t1 +; RUN: llvm-nm %t2 | FileCheck %s +; RUN: llvm-lto2 -r %t1,_start,plx -r %t1,_start3,plx -r %t1,foo@@SOME_VERSION -r %t1,foo@SOME_VERSION3 -o %t3 %t1 +; RUN: llvm-nm %t3.0 | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +module asm ".global _start" +module asm "_start:" +module asm "_start2:" +module asm "_start3:" +module asm ".symver _start, foo@@SOME_VERSION" +module asm ".symver _start2, foo@SOME_VERSION2" +module asm ".symver _start3, foo@SOME_VERSION3" +module asm ".local _start2" +module asm ".weak _start3" + +; CHECK-DAG: T _start +; CHECK-DAG: t _start2 +; CHECK-DAG: W _start3 +; CHECK-DAG: T foo@@SOME_VERSION +; CHECK-DAG: t foo@SOME_VERSION2 +; CHECK-DAG: W foo@SOME_VERSION3 Index: test/MC/AsmParser/AArch64/directive-parse-err.s =================================================================== --- test/MC/AsmParser/AArch64/directive-parse-err.s +++ test/MC/AsmParser/AArch64/directive-parse-err.s @@ -1,5 +1,5 @@ // RUN: not llvm-mc -triple aarch64-unknown-unknown %s 2>&1 | FileCheck %s -// RUN: not llvm-mc -triple aarch64-unknown-unknown %s 2>&1 | grep "error:" | count 60 +// RUN: not llvm-mc -triple aarch64-unknown-unknown %s 2>&1 | grep "error:" | count 61 // CHECK: [[@LINE+1]]:19: error: unexpected token in '.equ' directive .equ ident1, 0 $ @@ -201,7 +201,7 @@ .globl a1 $ // CHECK-NOT: [[@LINE+1]]:{{[0-9]+}}: error: .globl a1 # EOL COMMENT - // CHECK: [[@LINE+1]]:31: error: unexpected token in directive + // CHECK: [[@LINE+1]]:31: error: unexpected token in '.global' directive .global a2 $ // CHECK-NOT: [[@LINE+1]]:{{[0-9]+}}: error: .global a2 # EOL COMMENT