Index: docs/BitCodeFormat.rst =================================================================== --- docs/BitCodeFormat.rst +++ docs/BitCodeFormat.rst @@ -673,7 +673,7 @@ MODULE_CODE_GLOBALVAR Record ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -``[GLOBALVAR, pointer type, isconst, initid, linkage, alignment, section, visibility, threadlocal, unnamed_addr, externally_initialized, dllstorageclass, comdat]`` +``[GLOBALVAR, pointer type, isconst, initid, linkage, alignment, section, visibility, threadlocal, unnamed_addr, externally_initialized, dllstorageclass, comdat, can_omit_from_dynsym]`` The ``GLOBALVAR`` record (code 7) marks the declaration or definition of a global variable. The operand fields are: @@ -718,6 +718,8 @@ * ``hidden``: code 1 * ``protected``: code 2 +.. _bcthreadlocal: + * *threadlocal*: If present, an encoding of the thread local storage mode of the variable: * ``not thread local``: code 0 @@ -737,12 +739,17 @@ * ``dllimport``: code 1 * ``dllexport``: code 2 +* *comdat*: An encoding of the COMDAT of this function + +* *can_omit_from_dynsym*: If present and non-zero, indicates that the variable has + ``can_omit_from_dynsym``. + .. _FUNCTION: MODULE_CODE_FUNCTION Record ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -``[FUNCTION, type, callingconv, isproto, linkage, paramattr, alignment, section, visibility, gc, prologuedata, dllstorageclass, comdat, prefixdata, personalityfn]`` +``[FUNCTION, type, callingconv, isproto, linkage, paramattr, alignment, section, visibility, gc, prologuedata, dllstorageclass, comdat, prefixdata, personalityfn, can_omit_from_dynsym]`` The ``FUNCTION`` record (code 8) marks the declaration or definition of a function. The operand fields are: @@ -801,10 +808,13 @@ * *personalityfn*: If non-zero, the value index of the personality function for this function, plus 1. +* *can_omit_from_dynsym*: If present and non-zero, indicates that the function has + ``can_omit_from_dynsym``. + MODULE_CODE_ALIAS Record ^^^^^^^^^^^^^^^^^^^^^^^^ -``[ALIAS, alias type, aliasee val#, linkage, visibility, dllstorageclass]`` +``[ALIAS, alias type, aliasee val#, linkage, visibility, dllstorageclass, threadlocal, unnamed_addr]`` The ``ALIAS`` record (code 9) marks the definition of an alias. The operand fields are @@ -820,6 +830,15 @@ * *dllstorageclass*: If present, an encoding of the :ref:`dllstorageclass` of the alias +* *threadlocal*: If present, an encoding of the + :ref:`thread local property` of the alias + +* *unnamed_addr*: If present and non-zero, indicates that the alias has + ``unnamed_addr`` + +* *can_omit_from_dynsym*: If present and non-zero, indicates that the alias has + ``can_omit_from_dynsym``. + MODULE_CODE_PURGEVALS Record ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -584,6 +584,13 @@ merged with a ``unnamed_addr`` constant, the result being a constant whose address is significant. +If the ``can_omit_from_dynsym`` attribute is given and all modules in the +linkage unit have this attribute, this value may be omitted from the dynamic +symbol table. This is set on constant linkonce_odr globals whose addresses +are insignificant within the module. Because such globals must be defined +in each linkage unit that uses them, a valid program would not be able to +observe the absence of the symbol unless its address is significant. + A global variable may be declared to reside in a target-specific numbered address space. For targets that support them, address spaces may affect how optimizations are performed and/or what target @@ -707,6 +714,13 @@ If the ``unnamed_addr`` attribute is given, the address is known to not be significant and two identical functions can be merged. +If the ``can_omit_from_dynsym`` attribute is given and all modules in the +linkage unit have this attribute, this value may be omitted from the dynamic +symbol table. This is set on constant linkonce_odr globals whose addresses +are insignificant within the module. Because such globals must be defined +in each linkage unit that uses them, a valid program would not be able to +observe the absence of the symbol unless its address is significant. + Syntax:: define [linkage] [visibility] [DLLStorageClass] Index: include/llvm/CodeGen/Analysis.h =================================================================== --- include/llvm/CodeGen/Analysis.h +++ include/llvm/CodeGen/Analysis.h @@ -111,13 +111,6 @@ const ReturnInst *Ret, const TargetLoweringBase &TLI); -// True if GV can be left out of the object symbol table. This is the case -// for linkonce_odr values whose address is not significant. While legal, it is -// not normally profitable to omit them from the .o symbol table. Using this -// analysis makes sense when the information can be passed down to the linker -// or we are in LTO. -bool canBeOmittedFromSymbolTable(const GlobalValue *GV); - DenseMap getFuncletMembership(const MachineFunction &MF); Index: include/llvm/IR/GlobalValue.h =================================================================== --- include/llvm/IR/GlobalValue.h +++ include/llvm/IR/GlobalValue.h @@ -70,8 +70,9 @@ LinkageTypes Linkage, const Twine &Name, unsigned AddressSpace) : Constant(PointerType::get(Ty, AddressSpace), VTy, Ops, NumOps), ValueType(Ty), Linkage(Linkage), Visibility(DefaultVisibility), - UnnamedAddr(0), DllStorageClass(DefaultStorageClass), - ThreadLocal(NotThreadLocal), IntID((Intrinsic::ID)0U), Parent(nullptr) { + UnnamedAddr(0), CanOmitFromDynSym(0), + DllStorageClass(DefaultStorageClass), ThreadLocal(NotThreadLocal), + IntID((Intrinsic::ID)0U), Parent(nullptr) { setName(Name); } @@ -81,6 +82,8 @@ unsigned Linkage : 4; // The linkage of this global unsigned Visibility : 2; // The visibility style of this global unsigned UnnamedAddr : 1; // This value's address is not significant + unsigned CanOmitFromDynSym : 1; // This value can be omitted from the dynamic + // symbol table unsigned DllStorageClass : 2; // DLL storage class unsigned ThreadLocal : 3; // Is this symbol "Thread Local", if so, what is @@ -89,7 +92,7 @@ private: // Give subclasses access to what otherwise would be wasted padding. - // (19 + 3 + 2 + 1 + 2 + 5) == 32. + // (19 + 4 + 2 + 1 + 1 + 2 + 3) == 32. unsigned SubClassData : GlobalValueSubClassDataBits; friend class Constant; @@ -156,6 +159,20 @@ bool hasUnnamedAddr() const { return UnnamedAddr; } void setUnnamedAddr(bool Val) { UnnamedAddr = Val; } + /// If this attribute is true and all modules in the linkage unit have this + /// attribute, this value may be omitted from the dynamic symbol table. This + /// is set on linkonce_odr globals whose addresses are insignificant within + /// the module. Because linkonce_odr globals must be defined in each linkage + /// unit that uses them, a valid program would not be able to observe the + /// absence of the symbol unless its address is significant. + /// + /// This attribute is intended to be used only by the code generator and LTO. + /// It should probably not be used by optimizer passes, as the value's address + /// may be used by other modules. Optimizers should use hasUnnamedAddr() + /// instead. + bool canOmitFromDynSym() const { return CanOmitFromDynSym; } + void setCanOmitFromDynSym(bool Val) { CanOmitFromDynSym = Val; } + bool hasComdat() const { return getComdat() != nullptr; } Comdat *getComdat(); const Comdat *getComdat() const { Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -513,6 +513,7 @@ KEYWORD(hidden); KEYWORD(protected); KEYWORD(unnamed_addr); + KEYWORD(can_omit_from_dynsym); KEYWORD(externally_initialized); KEYWORD(extern_weak); KEYWORD(external); Index: lib/AsmParser/LLParser.h =================================================================== --- lib/AsmParser/LLParser.h +++ lib/AsmParser/LLParser.h @@ -229,6 +229,10 @@ bool parseOptionalUnnamedAddr(bool &UnnamedAddr) { return ParseOptionalToken(lltok::kw_unnamed_addr, UnnamedAddr); } + bool parseOptionalCanOmitFromDynSym(bool &CanOmitFromDynSym) { + return ParseOptionalToken(lltok::kw_can_omit_from_dynsym, + CanOmitFromDynSym); + } bool ParseOptionalAddrSpace(unsigned &AddrSpace); bool ParseOptionalParamAttrs(AttrBuilder &B); bool ParseOptionalReturnAttrs(AttrBuilder &B); @@ -275,12 +279,13 @@ bool ParseGlobal(const std::string &Name, LocTy Loc, unsigned Linkage, bool HasLinkage, unsigned Visibility, unsigned DLLStorageClass, - GlobalVariable::ThreadLocalMode TLM, bool UnnamedAddr); + GlobalVariable::ThreadLocalMode TLM, + bool UnnamedAddr, bool CanOmitFromDynSym); bool parseIndirectSymbol(const std::string &Name, LocTy Loc, unsigned Linkage, unsigned Visibility, unsigned DLLStorageClass, GlobalVariable::ThreadLocalMode TLM, - bool UnnamedAddr); + bool UnnamedAddr, bool CanOmitFromDynSym); bool parseComdat(); bool ParseStandaloneMetadata(); bool ParseNamedMetadata(); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -453,17 +453,18 @@ bool HasLinkage; unsigned Linkage, Visibility, DLLStorageClass; GlobalVariable::ThreadLocalMode TLM; - bool UnnamedAddr; + bool UnnamedAddr, CanOmitFromDynSym; if (ParseOptionalLinkage(Linkage, HasLinkage, Visibility, DLLStorageClass) || - ParseOptionalThreadLocal(TLM) || parseOptionalUnnamedAddr(UnnamedAddr)) + ParseOptionalThreadLocal(TLM) || parseOptionalUnnamedAddr(UnnamedAddr) || + parseOptionalCanOmitFromDynSym(CanOmitFromDynSym)) return true; if (Lex.getKind() != lltok::kw_alias && Lex.getKind() != lltok::kw_ifunc) return ParseGlobal(Name, NameLoc, Linkage, HasLinkage, Visibility, - DLLStorageClass, TLM, UnnamedAddr); + DLLStorageClass, TLM, UnnamedAddr, CanOmitFromDynSym); return parseIndirectSymbol(Name, NameLoc, Linkage, Visibility, - DLLStorageClass, TLM, UnnamedAddr); + DLLStorageClass, TLM, UnnamedAddr, CanOmitFromDynSym); } /// ParseNamedGlobal: @@ -479,18 +480,20 @@ bool HasLinkage; unsigned Linkage, Visibility, DLLStorageClass; GlobalVariable::ThreadLocalMode TLM; - bool UnnamedAddr; + bool UnnamedAddr, CanOmitFromDynSym; if (ParseToken(lltok::equal, "expected '=' in global variable") || ParseOptionalLinkage(Linkage, HasLinkage, Visibility, DLLStorageClass) || - ParseOptionalThreadLocal(TLM) || parseOptionalUnnamedAddr(UnnamedAddr)) + ParseOptionalThreadLocal(TLM) || parseOptionalUnnamedAddr(UnnamedAddr) || + parseOptionalCanOmitFromDynSym(CanOmitFromDynSym)) return true; if (Lex.getKind() != lltok::kw_alias && Lex.getKind() != lltok::kw_ifunc) return ParseGlobal(Name, NameLoc, Linkage, HasLinkage, Visibility, - DLLStorageClass, TLM, UnnamedAddr); + DLLStorageClass, TLM, UnnamedAddr, CanOmitFromDynSym); return parseIndirectSymbol(Name, NameLoc, Linkage, Visibility, - DLLStorageClass, TLM, UnnamedAddr); + DLLStorageClass, TLM, UnnamedAddr, + CanOmitFromDynSym); } bool LLParser::parseComdat() { @@ -657,11 +660,10 @@ /// /// Everything through OptionalUnnamedAddr has already been parsed. /// -bool LLParser::parseIndirectSymbol(const std::string &Name, LocTy NameLoc, - unsigned L, unsigned Visibility, - unsigned DLLStorageClass, - GlobalVariable::ThreadLocalMode TLM, - bool UnnamedAddr) { +bool LLParser::parseIndirectSymbol( + const std::string &Name, LocTy NameLoc, unsigned L, unsigned Visibility, + unsigned DLLStorageClass, GlobalVariable::ThreadLocalMode TLM, + bool UnnamedAddr, bool CanOmitFromDynSym) { bool IsAlias; if (Lex.getKind() == lltok::kw_alias) IsAlias = true; @@ -752,6 +754,7 @@ GA->setVisibility((GlobalValue::VisibilityTypes)Visibility); GA->setDLLStorageClass((GlobalValue::DLLStorageClassTypes)DLLStorageClass); GA->setUnnamedAddr(UnnamedAddr); + GA->setCanOmitFromDynSym(CanOmitFromDynSym); if (Name.empty()) NumberedVals.push_back(GA.get()); @@ -797,7 +800,7 @@ unsigned Linkage, bool HasLinkage, unsigned Visibility, unsigned DLLStorageClass, GlobalVariable::ThreadLocalMode TLM, - bool UnnamedAddr) { + bool UnnamedAddr, bool CanOmitFromDynSym) { if (!isValidVisibilityForLinkage(Visibility, Linkage)) return Error(NameLoc, "symbol with local linkage must have default visibility"); @@ -875,6 +878,7 @@ GV->setExternallyInitialized(IsExternallyInitialized); GV->setThreadLocalMode(TLM); GV->setUnnamedAddr(UnnamedAddr); + GV->setCanOmitFromDynSym(CanOmitFromDynSym); // Parse attributes on the global. while (Lex.getKind() == lltok::comma) { @@ -4545,7 +4549,8 @@ std::string Section; unsigned Alignment; std::string GC; - bool UnnamedAddr; + bool UnnamedAddr = false; + bool CanOmitFromDynSym = false; LocTy UnnamedAddrLoc; Constant *Prefix = nullptr; Constant *Prologue = nullptr; @@ -4553,8 +4558,8 @@ Comdat *C; if (ParseArgumentList(ArgList, isVarArg) || - ParseOptionalToken(lltok::kw_unnamed_addr, UnnamedAddr, - &UnnamedAddrLoc) || + parseOptionalUnnamedAddr(UnnamedAddr) || + parseOptionalCanOmitFromDynSym(CanOmitFromDynSym) || ParseFnAttributeValuePairs(FuncAttrs, FwdRefAttrGrps, false, BuiltinLoc) || (EatIfPresent(lltok::kw_section) && @@ -4662,6 +4667,7 @@ Fn->setCallingConv(CC); Fn->setAttributes(PAL); Fn->setUnnamedAddr(UnnamedAddr); + Fn->setCanOmitFromDynSym(CanOmitFromDynSym); Fn->setAlignment(Alignment); Fn->setSection(Section); Fn->setComdat(C); Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -60,6 +60,7 @@ kw_hidden, kw_protected, kw_unnamed_addr, + kw_can_omit_from_dynsym, kw_externally_initialized, kw_extern_weak, kw_external, Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -3740,7 +3740,7 @@ // GLOBALVAR: [pointer type, isconst, initid, // linkage, alignment, section, visibility, threadlocal, // unnamed_addr, externally_initialized, dllstorageclass, - // comdat] + // comdat, can_omit_from_dynsym] case bitc::MODULE_CODE_GLOBALVAR: { if (Record.size() < 6) return error("Invalid record"); @@ -3817,11 +3817,16 @@ } else if (hasImplicitComdat(RawLinkage)) { NewGV->setComdat(reinterpret_cast(1)); } + + if (Record.size() > 12) + NewGV->setCanOmitFromDynSym(Record[12]); + break; } // FUNCTION: [type, callingconv, isproto, linkage, paramattr, // alignment, section, visibility, gc, unnamed_addr, - // prologuedata, dllstorageclass, comdat, prefixdata] + // prologuedata, dllstorageclass, comdat, prefixdata, + // personality, can_omit_from_dynsym] case bitc::MODULE_CODE_FUNCTION: { if (Record.size() < 8) return error("Invalid record"); @@ -3892,6 +3897,9 @@ if (Record.size() > 14 && Record[14] != 0) FunctionPersonalityFns.push_back(std::make_pair(Func, Record[14] - 1)); + if (Record.size() > 15) + Func->setCanOmitFromDynSym(Record[15]); + ValueList.push_back(Func); // If this is a function with a body, remember the prototype we are @@ -3904,8 +3912,10 @@ break; } // ALIAS: [alias type, addrspace, aliasee val#, linkage] - // ALIAS: [alias type, addrspace, aliasee val#, linkage, visibility, dllstorageclass] - // IFUNC: [alias type, addrspace, aliasee val#, linkage, visibility, dllstorageclass] + // ALIAS: [alias type, addrspace, aliasee val#, linkage, visibility, dllstorageclass, + // threadlocal, unnamed_addr, can_omit_from_dynsym] + // IFUNC: [alias type, addrspace, aliasee val#, linkage, visibility, dllstorageclass, + // threadlocal, unnamed_addr, can_omit_from_dynsym] case bitc::MODULE_CODE_IFUNC: case bitc::MODULE_CODE_ALIAS: case bitc::MODULE_CODE_ALIAS_OLD: { @@ -3954,6 +3964,8 @@ NewGA->setThreadLocalMode(getDecodedThreadLocalMode(Record[OpNum++])); if (OpNum != Record.size()) NewGA->setUnnamedAddr(Record[OpNum++]); + if (OpNum != Record.size()) + NewGA->setCanOmitFromDynSym(Record[OpNum++]); ValueList.push_back(NewGA); IndirectSymbolInits.push_back(std::make_pair(NewGA, Val)); break; Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1142,7 +1142,7 @@ // GLOBALVAR: [type, isconst, initid, // linkage, alignment, section, visibility, threadlocal, // unnamed_addr, externally_initialized, dllstorageclass, - // comdat] + // comdat, can_omit_from_dynsym] Vals.push_back(VE.getTypeID(GV.getValueType())); Vals.push_back(GV.getType()->getAddressSpace() << 2 | 2 | GV.isConstant()); Vals.push_back(GV.isDeclaration() ? 0 : @@ -1154,13 +1154,14 @@ GV.getVisibility() != GlobalValue::DefaultVisibility || GV.hasUnnamedAddr() || GV.isExternallyInitialized() || GV.getDLLStorageClass() != GlobalValue::DefaultStorageClass || - GV.hasComdat()) { + GV.hasComdat() || GV.canOmitFromDynSym()) { Vals.push_back(getEncodedVisibility(GV)); Vals.push_back(getEncodedThreadLocalMode(GV)); Vals.push_back(GV.hasUnnamedAddr()); Vals.push_back(GV.isExternallyInitialized()); Vals.push_back(getEncodedDLLStorageClass(GV)); Vals.push_back(GV.hasComdat() ? VE.getComdatID(GV.getComdat()) : 0); + Vals.push_back(GV.canOmitFromDynSym()); } else { AbbrevToUse = SimpleGVarAbbrev; } @@ -1173,7 +1174,8 @@ for (const Function &F : M) { // FUNCTION: [type, callingconv, isproto, linkage, paramattrs, alignment, // section, visibility, gc, unnamed_addr, prologuedata, - // dllstorageclass, comdat, prefixdata, personalityfn] + // dllstorageclass, comdat, prefixdata, personalityfn, + // can_omit_from_dynsym] Vals.push_back(VE.getTypeID(F.getFunctionType())); Vals.push_back(F.getCallingConv()); Vals.push_back(F.isDeclaration()); @@ -1192,6 +1194,7 @@ : 0); Vals.push_back( F.hasPersonalityFn() ? (VE.getValueID(F.getPersonalityFn()) + 1) : 0); + Vals.push_back(F.canOmitFromDynSym()); unsigned AbbrevToUse = 0; Stream.EmitRecord(bitc::MODULE_CODE_FUNCTION, Vals, AbbrevToUse); @@ -1200,7 +1203,8 @@ // Emit the alias information. for (const GlobalAlias &A : M.aliases()) { - // ALIAS: [alias type, aliasee val#, linkage, visibility] + // ALIAS: [alias type, aliasee val#, linkage, visibility, dllstorageclass, + // threadlocal, unnamed_addr, can_omit_from_dynsym] Vals.push_back(VE.getTypeID(A.getValueType())); Vals.push_back(A.getType()->getAddressSpace()); Vals.push_back(VE.getValueID(A.getAliasee())); @@ -1209,6 +1213,7 @@ Vals.push_back(getEncodedDLLStorageClass(A)); Vals.push_back(getEncodedThreadLocalMode(A)); Vals.push_back(A.hasUnnamedAddr()); + Vals.push_back(A.canOmitFromDynSym()); unsigned AbbrevToUse = 0; Stream.EmitRecord(bitc::MODULE_CODE_ALIAS, Vals, AbbrevToUse); Vals.clear(); Index: lib/CodeGen/Analysis.cpp =================================================================== --- lib/CodeGen/Analysis.cpp +++ lib/CodeGen/Analysis.cpp @@ -619,37 +619,6 @@ return true; } -bool llvm::canBeOmittedFromSymbolTable(const GlobalValue *GV) { - if (!GV->hasLinkOnceODRLinkage()) - return false; - - if (GV->hasUnnamedAddr()) - return true; - - // If it is a non constant variable, it needs to be uniqued across shared - // objects. - if (const GlobalVariable *Var = dyn_cast(GV)) { - if (!Var->isConstant()) - return false; - } - - // An alias can point to a variable. We could try to resolve the alias to - // decide, but for now just don't hide them. - if (isa(GV)) - return false; - - // If we don't see every use, we have to be conservative and assume the value - // address is significant. - if (GV->getParent()->getMaterializer()) - return false; - - GlobalStatus GS; - if (GlobalStatus::analyzeGlobal(GV, GS)) - return false; - - return !GS.IsCompared; -} - static void collectFuncletMembers( DenseMap &FuncletMembership, int Funclet, const MachineBasicBlock *MBB) { Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -289,7 +289,7 @@ if (!MAI.hasWeakDefCanBeHiddenDirective()) return false; - return canBeOmittedFromSymbolTable(GV); + return GV->canOmitFromDynSym(); } void AsmPrinter::EmitLinkage(const GlobalValue *GV, MCSymbol *GVSym) const { Index: lib/IR/AsmWriter.cpp =================================================================== --- lib/IR/AsmWriter.cpp +++ lib/IR/AsmWriter.cpp @@ -2452,6 +2452,8 @@ PrintThreadLocalModel(GV->getThreadLocalMode(), Out); if (GV->hasUnnamedAddr()) Out << "unnamed_addr "; + if (GV->canOmitFromDynSym()) + Out << "can_omit_from_dynsym "; if (unsigned AddressSpace = GV->getType()->getAddressSpace()) Out << "addrspace(" << AddressSpace << ") "; @@ -2489,6 +2491,8 @@ PrintThreadLocalModel(GIS->getThreadLocalMode(), Out); if (GIS->hasUnnamedAddr()) Out << "unnamed_addr "; + if (GIS->canOmitFromDynSym()) + Out << "can_omit_from_dynsym "; if (isa(GIS)) Out << "alias "; @@ -2646,6 +2650,8 @@ Out << ')'; if (F->hasUnnamedAddr()) Out << " unnamed_addr"; + if (F->canOmitFromDynSym()) + Out << " can_omit_from_dynsym"; if (Attrs.hasAttributes(AttributeSet::FunctionIndex)) Out << " #" << Machine.getAttributeGroupSlot(Attrs.getFnAttributes()); if (F->hasSection()) { Index: lib/IR/Globals.cpp =================================================================== --- lib/IR/Globals.cpp +++ lib/IR/Globals.cpp @@ -52,6 +52,7 @@ void GlobalValue::copyAttributesFrom(const GlobalValue *Src) { setVisibility(Src->getVisibility()); setUnnamedAddr(Src->hasUnnamedAddr()); + setCanOmitFromDynSym(Src->canOmitFromDynSym()); setDLLStorageClass(Src->getDLLStorageClass()); } Index: lib/LTO/LTOModule.cpp =================================================================== --- lib/LTO/LTOModule.cpp +++ lib/LTO/LTOModule.cpp @@ -456,7 +456,7 @@ attr |= LTO_SYMBOL_SCOPE_HIDDEN; else if (def->hasProtectedVisibility()) attr |= LTO_SYMBOL_SCOPE_PROTECTED; - else if (canBeOmittedFromSymbolTable(def)) + else if (def->canOmitFromDynSym()) attr |= LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN; else attr |= LTO_SYMBOL_SCOPE_DEFAULT; Index: lib/Linker/LinkModules.cpp =================================================================== --- lib/Linker/LinkModules.cpp +++ lib/Linker/LinkModules.cpp @@ -379,6 +379,10 @@ bool HasUnnamedAddr = GV.hasUnnamedAddr() && DGV->hasUnnamedAddr(); DGV->setUnnamedAddr(HasUnnamedAddr); GV.setUnnamedAddr(HasUnnamedAddr); + + bool CanOmitFromDynSym = GV.canOmitFromDynSym() && DGV->canOmitFromDynSym(); + DGV->setCanOmitFromDynSym(CanOmitFromDynSym); + GV.setCanOmitFromDynSym(CanOmitFromDynSym); } // Don't want to append to global_ctors list, for example, when we Index: lib/Transforms/IPO/GlobalOpt.cpp =================================================================== --- lib/Transforms/IPO/GlobalOpt.cpp +++ lib/Transforms/IPO/GlobalOpt.cpp @@ -1937,27 +1937,62 @@ return false; } +// Returns true if the GlobalValue could be omitted from the dynamic symbol +// table if its address is not taken. +static bool isCandidateForOmissionFromDynSym(GlobalValue &GV) { + if (!GV.hasLinkOnceODRLinkage()) + return false; + + if (auto *GVar = dyn_cast(&GV)) + return GVar->isConstant(); + + return isa(GV); +} + /// Analyze the specified global variable and optimize it if possible. If we /// make a change, return true. static bool processGlobal(GlobalValue &GV, TargetLibraryInfo *TLI, function_ref LookupDomTree) { - // Do more involved optimizations if the global is internal. - if (!GV.hasLocalLinkage()) + // Do more involved optimizations if the global is internal or could + // potentially be omitted from the dynamic symbol table. + if (!GV.hasLocalLinkage() && !isCandidateForOmissionFromDynSym(GV)) return false; + if (!GV.hasLocalLinkage()) { + // For omission candidates, unnamed_addr implies can_omit_from_dynsym. + if (GV.hasUnnamedAddr() && !GV.canOmitFromDynSym()) { + GV.setCanOmitFromDynSym(true); + return true; + } + + // If we already know we can omit, there's nothing else to do. + if (GV.canOmitFromDynSym()) + return false; + } + GlobalStatus GS; if (GlobalStatus::analyzeGlobal(&GV, GS)) return false; bool Changed = false; - if (!GS.IsCompared && !GV.hasUnnamedAddr()) { - GV.setUnnamedAddr(true); - NumUnnamed++; - Changed = true; + if (!GS.IsCompared) { + if (GV.hasLocalLinkage()) { + if (!GV.hasUnnamedAddr()) { + GV.setUnnamedAddr(true); + NumUnnamed++; + Changed = true; + } + } else if (!GV.canOmitFromDynSym()) { + GV.setCanOmitFromDynSym(true); + Changed = true; + } } + if (!GV.hasLocalLinkage()) + return Changed; + auto *GVar = dyn_cast(&GV); if (!GVar) return Changed; Index: test/Assembler/can-omit-from-dynsym.ll =================================================================== --- /dev/null +++ test/Assembler/can-omit-from-dynsym.ll @@ -0,0 +1,13 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s +; RUN: verify-uselistorder %s + +; CHECK: @c = can_omit_from_dynsym constant i32 0 +@c = can_omit_from_dynsym constant i32 0 + +; CHECK: @a = can_omit_from_dynsym alias i32, i32* @c +@a = can_omit_from_dynsym alias i32, i32* @c + +; CHECK: define void @f() can_omit_from_dynsym { +define void @f() can_omit_from_dynsym { + ret void +} Index: test/CodeGen/PowerPC/weak_def_can_be_hidden.ll =================================================================== --- test/CodeGen/PowerPC/weak_def_can_be_hidden.ll +++ test/CodeGen/PowerPC/weak_def_can_be_hidden.ll @@ -3,48 +3,40 @@ ; RUN: llc -mtriple=powerpc-apple-darwin9 -O0 < %s | FileCheck --check-prefix=CHECK-D89 %s ; RUN: llc -mtriple=powerpc-apple-darwin8 -O0 < %s | FileCheck --check-prefix=CHECK-D89 %s -@v1 = linkonce_odr constant i32 32 +define linkonce_odr void @f1() can_omit_from_dynsym { + %x = load i32, i32 * @v1 + %y = load i32, i32 * @v2 + call void @f2() + ret void +} +; CHECK: .globl _f1 +; CHECK: .weak_def_can_be_hidden _f1 + +; CHECK-D89: .globl _f1 +; CHECK-D89: .weak_definition _f1 + +define linkonce_odr void @f2() { + %x = load i32, i32 * @v1 + %y = load i32, i32 * @v2 + call void @f1() + ret void +} +; CHECK: .globl _f2 +; CHECK: .weak_definition _f2 + +; CHECK-D89: .globl _f2 +; CHECK-D89: .weak_definition _f2 + +@v1 = linkonce_odr can_omit_from_dynsym constant i32 32 ; CHECK: .globl _v1 ; CHECK: .weak_def_can_be_hidden _v1 ; CHECK-D89: .globl _v1 ; CHECK-D89: .weak_definition _v1 -define i32 @f1() { - %x = load i32 , i32 * @v1 - ret i32 %x -} - @v2 = linkonce_odr constant i32 32 ; CHECK: .globl _v2 ; CHECK: .weak_definition _v2 ; CHECK-D89: .globl _v2 ; CHECK-D89: .weak_definition _v2 - -define i32* @f2() { - ret i32* @v2 -} - -@v3 = linkonce_odr unnamed_addr global i32 32 -; CHECK: .globl _v3 -; CHECK: .weak_def_can_be_hidden _v3 - -; CHECK-D89: .globl _v3 -; CHECK-D89: .weak_definition _v3 - -define i32* @f3() { - ret i32* @v3 -} - -@v4 = linkonce_odr global i32 32 -; CHECK: .globl _v4 -; CHECK: .weak_definition _v4 - -; CHECK-D89: .globl _v4 -; CHECK-D89: .weak_definition _v4 - -define i32 @f4() { - %x = load i32 , i32 * @v4 - ret i32 %x -} Index: test/CodeGen/X86/weak_def_can_be_hidden.ll =================================================================== --- test/CodeGen/X86/weak_def_can_be_hidden.ll +++ test/CodeGen/X86/weak_def_can_be_hidden.ll @@ -4,18 +4,37 @@ ; RUN: llc -mtriple=i686-apple-darwin9 -O0 < %s | FileCheck --check-prefix=CHECK-D89 %s ; RUN: llc -mtriple=i686-apple-darwin8 -O0 < %s | FileCheck --check-prefix=CHECK-D89 %s -@v1 = linkonce_odr constant i32 32 +define linkonce_odr void @f1() can_omit_from_dynsym { + %x = load i32, i32 * @v1 + %y = load i32, i32 * @v2 + call void @f2() + ret void +} +; CHECK: .globl _f1 +; CHECK: .weak_def_can_be_hidden _f1 + +; CHECK-D89: .globl _f1 +; CHECK-D89: .weak_definition _f1 + +define linkonce_odr void @f2() { + %x = load i32, i32 * @v1 + %y = load i32, i32 * @v2 + call void @f1() + ret void +} +; CHECK: .globl _f2 +; CHECK: .weak_definition _f2 + +; CHECK-D89: .globl _f2 +; CHECK-D89: .weak_definition _f2 + +@v1 = linkonce_odr can_omit_from_dynsym constant i32 32 ; CHECK: .globl _v1 ; CHECK: .weak_def_can_be_hidden _v1 ; CHECK-D89: .globl _v1 ; CHECK-D89: .weak_definition _v1 -define i32 @f1() { - %x = load i32 , i32 * @v1 - ret i32 %x -} - @v2 = linkonce_odr constant i32 32 ; CHECK: .globl _v2 ; CHECK: .weak_definition _v2 @@ -23,29 +42,3 @@ ; CHECK-D89: .globl _v2 ; CHECK-D89: .weak_definition _v2 -define i32* @f2() { - ret i32* @v2 -} - -@v3 = linkonce_odr unnamed_addr global i32 32 -; CHECK: .globl _v3 -; CHECK: .weak_def_can_be_hidden _v3 - -; CHECK-D89: .globl _v3 -; CHECK-D89: .weak_definition _v3 - -define i32* @f3() { - ret i32* @v3 -} - -@v4 = linkonce_odr global i32 32 -; CHECK: .globl _v4 -; CHECK: .weak_definition _v4 - -; CHECK-D89: .globl _v4 -; CHECK-D89: .weak_definition _v4 - -define i32 @f4() { - %x = load i32 , i32 * @v4 - ret i32 %x -} Index: test/Feature/OperandBundles/pr26510.ll =================================================================== --- test/Feature/OperandBundles/pr26510.ll +++ test/Feature/OperandBundles/pr26510.ll @@ -10,7 +10,7 @@ declare void @foo() readnone -; CHECK-LABEL: define i8* @test(i8* %p) { +; CHECK-LABEL: define i8* @test(i8* %p) ; CHECK: %a = alloca i8*, align 8 ; CHECK: store i8* %p, i8** %a, align 8 ; CHECK: call void @foo() [ "abc"(i8** %a) ] Index: test/LTO/X86/cfi_endproc.ll =================================================================== --- test/LTO/X86/cfi_endproc.ll +++ test/LTO/X86/cfi_endproc.ll @@ -33,8 +33,8 @@ ret i32* @zed1 } -; ZED1_AND_ZED2: d zed2 -@zed2 = linkonce_odr unnamed_addr global i32 42 +; ZED1_AND_ZED2: r zed2 +@zed2 = linkonce_odr can_omit_from_dynsym constant i32 42 define i32 @useZed2() { %x = load i32, i32* @zed2 Index: test/LTO/X86/linkonce_odr_func.ll =================================================================== --- test/LTO/X86/linkonce_odr_func.ll +++ test/LTO/X86/linkonce_odr_func.ll @@ -1,61 +1,31 @@ ; RUN: llvm-as < %s >%t1 -; RUN: llvm-lto -o %t2 -dso-symbol=foo1 -dso-symbol=foo2 -dso-symbol=foo3 \ -; RUN: -dso-symbol=foo4 -dso-symbol=v1 -dso-symbol=v2 %t1 -O0 +; RUN: llvm-lto -o %t2 -dso-symbol=foo1 -dso-symbol=foo2 \ +; RUN: -dso-symbol=v1 -dso-symbol=v2 %t1 -O0 ; RUN: llvm-nm %t2 | FileCheck %s target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" -; CHECK: t foo1 +; CHECK: W foo1 define linkonce_odr void @foo1() noinline { ret void } -; CHECK: W foo2 -define linkonce_odr void @foo2() noinline { +; CHECK: t foo2 +define linkonce_odr void @foo2() can_omit_from_dynsym noinline { ret void } -; CHECK: t foo3 -define linkonce_odr void @foo3() noinline { - ret void -} - -; CHECK: W foo4 -define linkonce_odr void @foo4() noinline { - ret void -} - -; CHECK: r v1 +; CHECK: V v1 @v1 = linkonce_odr constant i32 32 -define i32 @useV1() { - %x = load i32, i32* @v1 - ret i32 %x -} +; CHECK: r v2 +@v2 = linkonce_odr can_omit_from_dynsym constant i32 32 -; CHECK: V v2 -@v2 = linkonce_odr global i32 32 - -define i32 @useV2() { - %x = load i32, i32* @v2 - ret i32 %x -} - -declare void @f(void()*) - -declare void @p() - -define void @bar() personality void()* @p { -bb0: +define void @use() { call void @foo1() - call void @f(void()* @foo2) - invoke void @foo3() to label %bb1 unwind label %clean -bb1: - invoke void @f(void()* @foo4) to label %bb2 unwind label %clean -bb2: - ret void -clean: - landingpad {i32, i32} cleanup + call void @foo2() + %x1 = load i32, i32* @v1 + %x2 = load i32, i32* @v2 ret void } Index: test/Transforms/GlobalOpt/pr21191.ll =================================================================== --- test/Transforms/GlobalOpt/pr21191.ll +++ test/Transforms/GlobalOpt/pr21191.ll @@ -6,12 +6,12 @@ define linkonce_odr void @foo() comdat($c) { ret void } -; CHECK: define linkonce_odr void @foo() comdat($c) +; CHECK: define linkonce_odr void @foo() can_omit_from_dynsym comdat($c) define linkonce_odr void @bar() comdat($c) { ret void } -; CHECK: define linkonce_odr void @bar() comdat($c) +; CHECK: define linkonce_odr void @bar() can_omit_from_dynsym comdat($c) define void @zed() { call void @foo() Index: test/Transforms/GlobalOpt/unnamed-addr.ll =================================================================== --- test/Transforms/GlobalOpt/unnamed-addr.ll +++ test/Transforms/GlobalOpt/unnamed-addr.ll @@ -5,12 +5,14 @@ @c = internal global i32 0, align 4 @d = internal constant [4 x i8] c"foo\00", align 1 @e = linkonce_odr global i32 0 +@f = linkonce_odr unnamed_addr constant i32 0 ; CHECK: @a = internal global i32 0, align 4 ; CHECK: @b = internal global i32 0, align 4 ; CHECK: @c = internal unnamed_addr global i32 0, align 4 ; CHECK: @d = internal unnamed_addr constant [4 x i8] c"foo\00", align 1 ; CHECK: @e = linkonce_odr global i32 0 +; CHECK: @f = linkonce_odr unnamed_addr can_omit_from_dynsym constant i32 0 ; CHECK: define internal fastcc void @used_internal() unnamed_addr { define internal void @used_internal() { @@ -50,6 +52,7 @@ entry: %cmp = icmp eq i32* %x, @a %conv = zext i1 %cmp to i32 + %cmp2 = icmp eq i32* %x, @f ret i32 %conv } Index: test/tools/gold/X86/coff.ll =================================================================== --- test/tools/gold/X86/coff.ll +++ test/tools/gold/X86/coff.ll @@ -16,7 +16,7 @@ ret void } -; CHECK: define internal void @h() { -define linkonce_odr void @h() { +; CHECK: define internal void @h() can_omit_from_dynsym { +define linkonce_odr void @h() can_omit_from_dynsym { ret void } Index: test/tools/gold/X86/emit-llvm.ll =================================================================== --- test/tools/gold/X86/emit-llvm.ll +++ test/tools/gold/X86/emit-llvm.ll @@ -25,10 +25,16 @@ target triple = "x86_64-unknown-linux-gnu" -@g7 = extern_weak global i32 -; CHECK-DAG: @g7 = extern_weak global i32 +; CHECK-DAG: @g1 = linkonce_odr constant i32 32 +@g1 = linkonce_odr constant i32 32 -@g8 = external global i32 +; CHECK-DAG: @g2 = internal can_omit_from_dynsym constant i32 32 +@g2 = linkonce_odr can_omit_from_dynsym constant i32 32 + +@g6 = extern_weak global i32 +; CHECK-DAG: @g6 = extern_weak global i32 + +@g7 = external global i32 ; CHECK-DAG: define internal void @f1() ; OPT2-NOT: @f1 @@ -53,7 +59,7 @@ ; CHECK-DAG: define internal void @f4() ; OPT2-NOT: @f4 -define linkonce_odr void @f4() { +define linkonce_odr void @f4() can_omit_from_dynsym { ret void } @@ -62,23 +68,16 @@ define linkonce_odr void @f5() { ret void } -@g5 = global void()* @f5 +@g8 = global void()* @f5 -; CHECK-DAG: define internal void @f6() unnamed_addr -; OPT-DAG: define internal void @f6() unnamed_addr -define linkonce_odr void @f6() unnamed_addr { - ret void +define i32* @f6() { + ret i32* @g6 } -@g6 = global void()* @f6 define i32* @f7() { ret i32* @g7 } -define i32* @f8() { - ret i32* @g8 -} - ; API: f1 PREVAILING_DEF_IRONLY ; API: f2 PREVAILING_DEF_IRONLY ; API: f3 PREVAILING_DEF_IRONLY_EXP @@ -86,8 +85,8 @@ ; API: f5 PREVAILING_DEF_IRONLY_EXP ; API: f6 PREVAILING_DEF_IRONLY_EXP ; API: f7 PREVAILING_DEF_IRONLY_EXP -; API: f8 PREVAILING_DEF_IRONLY_EXP +; API: g1 PREVAILING_DEF_IRONLY_EXP +; API: g2 PREVAILING_DEF_IRONLY_EXP +; API: g6 UNDEF ; API: g7 UNDEF -; API: g8 UNDEF -; API: g5 PREVAILING_DEF_IRONLY_EXP -; API: g6 PREVAILING_DEF_IRONLY_EXP +; API: g8 PREVAILING_DEF_IRONLY_EXP Index: tools/gold/gold-plugin.cpp =================================================================== --- tools/gold/gold-plugin.cpp +++ tools/gold/gold-plugin.cpp @@ -104,6 +104,7 @@ unsigned CommonAlign = 0; bool IsLinkonceOdr = true; bool UnnamedAddr = true; + bool CanOmitFromDynSym = true; GlobalValue::VisibilityTypes Visibility = GlobalValue::DefaultVisibility; bool CommonInternal = false; bool UseCommon = false; @@ -520,6 +521,7 @@ sym.visibility = LDPV_DEFAULT; if (GV) { Res.UnnamedAddr &= GV->hasUnnamedAddr(); + Res.CanOmitFromDynSym &= GV->canOmitFromDynSym(); Res.IsLinkonceOdr &= GV->hasLinkOnceLinkage(); Res.Visibility = getMinVisibility(Res.Visibility, GV->getVisibility()); switch (GV->getVisibility()) { @@ -662,8 +664,7 @@ static std::unique_ptr getModuleForFile(LLVMContext &Context, claimed_file &F, const void *View, ld_plugin_input_file &Info, raw_fd_ostream *ApiFile, - StringSet<> &Internalize, StringSet<> &Maybe, - std::vector &Keep, + StringSet<> &Internalize, std::vector &Keep, StringMap &Realign) { MemoryBufferRef BufferRef(StringRef((const char *)View, Info.filesize), Info.name); @@ -799,12 +800,9 @@ break; case LDPR_PREVAILING_DEF_IRONLY_EXP: { - // We can only check for address uses after we merge the modules. The - // reason is that this GV might have a copy in another module - // and in that module the address might be significant, but that - // copy will be LDPR_PREEMPTED_IR. - Maybe.insert(GV->getName()); Keep.push_back(GV); + if (Res.CanOmitFromDynSym) + Internalize.insert(GV->getName()); break; } } @@ -1085,12 +1083,11 @@ /// saved in the IRMover \p L. Returns true on error, false on success. static bool linkInModule(LLVMContext &Context, IRMover &L, claimed_file &F, const void *View, ld_plugin_input_file &File, - raw_fd_ostream *ApiFile, StringSet<> &Internalize, - StringSet<> &Maybe) { + raw_fd_ostream *ApiFile, StringSet<> &Internalize) { std::vector Keep; StringMap Realign; std::unique_ptr M = getModuleForFile( - Context, F, View, File, ApiFile, Internalize, Maybe, Keep, Realign); + Context, F, View, File, ApiFile, Internalize, Keep, Realign); if (!M.get()) return false; if (!options::triple.empty()) @@ -1130,7 +1127,7 @@ IRMover L(*NewModule.get()); StringSet<> Dummy; - if (linkInModule(Context, L, F, View, File, ApiFile, Dummy, Dummy)) + if (linkInModule(Context, L, F, View, File, ApiFile, Dummy)) message(LDPL_FATAL, "Failed to rename module for ThinLTO"); if (renameModuleForThinLTO(*NewModule, CombinedIndex)) message(LDPL_FATAL, "Failed to rename module for ThinLTO"); @@ -1308,14 +1305,13 @@ IRMover L(*Combined); StringSet<> Internalize; - StringSet<> Maybe; for (claimed_file &F : Modules) { PluginInputFile InputFile(F.handle); const void *View = getSymbolsAndView(F); if (!View) continue; if (linkInModule(Context, L, F, View, InputFile.file(), ApiFile, - Internalize, Maybe)) + Internalize)) message(LDPL_FATAL, "Failed to link module"); } @@ -1325,15 +1321,6 @@ internalize(*GV); } - for (const auto &Name : Maybe) { - GlobalValue *GV = Combined->getNamedValue(Name.first()); - if (!GV) - continue; - GV->setLinkage(GlobalValue::LinkOnceODRLinkage); - if (canBeOmittedFromSymbolTable(GV)) - internalize(*GV); - } - if (options::TheOutputType == options::OT_DISABLE) return LDPS_OK;