Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -562,6 +562,8 @@ LLVM allows an explicit section to be specified for globals. If the target supports it, it will emit globals to the section specified. +Additionally, the global can placed in a comdat if the target has the necessary +support. By default, global initializers are optimized by assuming that global variables defined within the module are not modified from their @@ -627,8 +629,9 @@ :ref:`parameter attribute ` for the return type, a function name, a (possibly empty) argument list (each with optional :ref:`parameter attributes `), optional :ref:`function attributes `, -an optional section, an optional alignment, an optional :ref:`garbage -collector name `, an optional :ref:`prefix `, an opening +an optional section, an optional alignment, +an optional :ref:`comdat `, +an optional :ref:`garbage collector name `, an optional :ref:`prefix `, an opening curly brace, a list of basic blocks, and a closing curly brace. LLVM function declarations consist of the "``declare``" keyword, an @@ -658,6 +661,8 @@ LLVM allows an explicit section to be specified for functions. If the target supports it, it will emit functions to the section specified. +Additionally, the function can placed in a comdat if the target has the +necessary support. An explicit alignment may be specified for a function. If not present, or if the alignment is set to zero, the alignment of the function is set @@ -673,8 +678,8 @@ define [linkage] [visibility] [DLLStorageClass] [cconv] [ret attrs] @ ([argument list]) - [unnamed_addr] [fn Attrs] [section "name"] [align N] - [gc] [prefix Constant] { ... } + [unnamed_addr] [fn Attrs] [section "name"] [comdat $] + [align N] [gc] [prefix Constant] { ... } .. _langref_aliases: @@ -716,6 +721,55 @@ * No global value in the expression can be a declaration, since that would require a relocation, which is not possible. +.. _langref_comdats: + +Comdats +------- + +Comdat IR provides access to COFF and ELF object file COMDAT functionality. + +Comdats have a name which represents the COMDAT key. All global objects which +specify this key will only end up in the final object file if the linker chooses that key over some other key. + +Comdats provide a selection kind to provide input on how the linker should +choose between keys in two different object files. + +Syntax:: + + $ = comdat SelectionKind + +The selection kind must be one of the following: + +``any`` + The linker may choose any COMDAT key, the choice is arbitrary. +``exactmatch`` + The linker may choose any COMDAT key but the sections must contain the + same data. +``largest`` + The linker will choose the section containing the largest COMDAT key. +``newest`` + The linker will choose the section containing the newest COMDAT key. +``noduplicates`` + The linker requires that only section with this COMDAT key exist. +``samesize`` + The linker may choose any COMDAT key but the sections must contain the + same amount of data. + +Note that not all targets support COMDATs and some may only support ``any`` as a +selection kind. + +Here is an example of a COMDAT group where a function will only be selected if +the COMDAT key's section is the largest: + +.. code-block:: llvm + + $foo = comdat largest + @foo = global i32 2, comdat $foo + + define void @bar() comdat $foo { + ret void + } + .. _namedmetadatastructure: Named Metadata Index: include/llvm-c/Core.h =================================================================== --- include/llvm-c/Core.h +++ include/llvm-c/Core.h @@ -1733,6 +1733,8 @@ void LLVMSetLinkage(LLVMValueRef Global, LLVMLinkage Linkage); const char *LLVMGetSection(LLVMValueRef Global); void LLVMSetSection(LLVMValueRef Global, const char *Section); +LLVMValueRef LLVMGetComdat(LLVMValueRef Global); +void LLVMSetComdat(LLVMValueRef Global, LLVMValueRef ComdatRef); LLVMVisibility LLVMGetVisibility(LLVMValueRef Global); void LLVMSetVisibility(LLVMValueRef Global, LLVMVisibility Viz); LLVMDLLStorageClass LLVMGetDLLStorageClass(LLVMValueRef Global); Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -71,7 +71,8 @@ // MODULE_CODE_PURGEVALS: [numvals] MODULE_CODE_PURGEVALS = 10, - MODULE_CODE_GCNAME = 11 // GCNAME: [strchr x N] + MODULE_CODE_GCNAME = 11, // GCNAME: [strchr x N] + MODULE_CODE_COMDAT = 12 // COMDAT: [selection_kind, name] }; /// PARAMATTR blocks have code for defining a parameter attribute set. @@ -376,6 +377,15 @@ ATTR_KIND_JUMP_TABLE = 40 }; + enum ComdatSelectionKindCodes { + COMDAT_SELECTION_KIND_ANY = 1, + COMDAT_SELECTION_KIND_EXACT_MATCH = 2, + COMDAT_SELECTION_KIND_LARGEST = 3, + COMDAT_SELECTION_KIND_NEWEST = 4, + COMDAT_SELECTION_KIND_NO_DUPLICATES = 5, + COMDAT_SELECTION_KIND_SAME_SIZE = 6, + }; + } // End bitc namespace } // End llvm namespace Index: include/llvm/IR/Comdat.h =================================================================== --- /dev/null +++ include/llvm/IR/Comdat.h @@ -0,0 +1,58 @@ +//===-- llvm/IR/Comdat.h - Comdat definitions -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// @file +/// This file contains the declaration of the Comdat class, which represents a +/// single COMDAT in LLVM. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_IR_COMDAT_H +#define LLVM_IR_COMDAT_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Compiler.h" + +namespace llvm { + +template class StringMapEntry; + +// This is a Name X SelectionKind pair. The reason for having this be an +// independent object instead of just adding the name and the SelectionKind +// to a GlobalObject is that it is invalid to have two comdats with the same +// name but different SelectionKind. This structure makes that unrepresentable. +class Comdat { +public: + enum SelectionKind { + Any, + ExactMatch, + Largest, + Newest, + NoDuplicates, + SameSize, + }; + + Comdat(Comdat &&C); + SelectionKind getSelectionKind() const { return SK; } + SelectionKind setSelectionKind(SelectionKind Val) { return SK = Val; } + StringRef getName() const; + +private: + friend class Module; + Comdat(); + Comdat(SelectionKind SK, StringMapEntry *Name); + Comdat(const Comdat &) LLVM_DELETED_FUNCTION; + + // Points to the map in Module. + StringMapEntry *Name; + SelectionKind SK; +}; +} // end llvm namespace + +#endif Index: include/llvm/IR/GlobalObject.h =================================================================== --- include/llvm/IR/GlobalObject.h +++ include/llvm/IR/GlobalObject.h @@ -20,7 +20,7 @@ #include "llvm/IR/GlobalValue.h" namespace llvm { - +class Comdat; class Module; class GlobalObject : public GlobalValue { @@ -29,11 +29,12 @@ protected: GlobalObject(Type *Ty, ValueTy VTy, Use *Ops, unsigned NumOps, LinkageTypes Linkage, const Twine &Name) - : GlobalValue(Ty, VTy, Ops, NumOps, Linkage, Name) { + : GlobalValue(Ty, VTy, Ops, NumOps, Linkage, Name), ObjComdat(nullptr) { setGlobalValueSubClassData(0); } std::string Section; // Section to emit this into, empty means default + Comdat *ObjComdat; public: unsigned getAlignment() const { return (1u << getGlobalValueSubClassData()) >> 1; @@ -44,6 +45,10 @@ const char *getSection() const { return Section.c_str(); } void setSection(StringRef S); + bool hasComdat() const { return getComdat() != nullptr; } + const Comdat *getComdat() const { return ObjComdat; } + void setComdat(Comdat *C) { ObjComdat = C; } + void copyAttributesFrom(const GlobalValue *Src) override; // Methods for support type inquiry through isa, cast, and dyn_cast: Index: include/llvm/IR/GlobalValue.h =================================================================== --- include/llvm/IR/GlobalValue.h +++ include/llvm/IR/GlobalValue.h @@ -23,6 +23,7 @@ namespace llvm { +class Comdat; class PointerType; class Module; @@ -110,6 +111,9 @@ bool hasUnnamedAddr() const { return UnnamedAddr; } void setUnnamedAddr(bool Val) { UnnamedAddr = Val; } + bool hasComdat() const { return getComdat() != nullptr; } + const Comdat *getComdat() const; + VisibilityTypes getVisibility() const { return VisibilityTypes(Visibility); } bool hasDefaultVisibility() const { return Visibility == DefaultVisibility; } bool hasHiddenVisibility() const { return Visibility == HiddenVisibility; } Index: include/llvm/IR/Module.h =================================================================== --- include/llvm/IR/Module.h +++ include/llvm/IR/Module.h @@ -16,6 +16,7 @@ #define LLVM_IR_MODULE_H #include "llvm/ADT/iterator_range.h" +#include "llvm/IR/Comdat.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalAlias.h" @@ -122,6 +123,8 @@ typedef iplist AliasListType; /// The type for the list of named metadata. typedef ilist NamedMDListType; + /// The type of the comdat "symbol" table. + typedef StringMap ComdatSymTabType; /// The Global Variable iterator. typedef GlobalListType::iterator global_iterator; @@ -196,6 +199,7 @@ NamedMDListType NamedMDList; ///< The named metadata in the module std::string GlobalScopeAsm; ///< Inline Asm at global scope. ValueSymbolTable *ValSymTab; ///< Symbol table for values + ComdatSymTabType ComdatSymTab; ///< Symbol table for COMDATs std::unique_ptr Materializer; ///< Used to materialize GlobalValues std::string ModuleID; ///< Human readable identifier for the module @@ -396,6 +400,14 @@ void eraseNamedMetadata(NamedMDNode *NMD); /// @} +/// @name Comdat Accessors +/// @{ + + /// Return the Comdat in the module with the specified name. It is created + /// if it didn't already exist. + Comdat *getOrInsertComdat(StringRef Name); + +/// @} /// @name Module Flags Accessors /// @{ @@ -496,6 +508,10 @@ const ValueSymbolTable &getValueSymbolTable() const { return *ValSymTab; } /// Get the Module's symbol table of global variable and function identifiers. ValueSymbolTable &getValueSymbolTable() { return *ValSymTab; } + /// Get the Module's symbol table for COMDATs (constant). + const ComdatSymTabType &getComdatSymbolTable() const { return ComdatSymTab; } + /// Get the Module's symbol table for COMDATs. + ComdatSymTabType &getComdatSymbolTable() { return ComdatSymTab; } /// @} /// @name Global Variable Iteration Index: lib/AsmParser/LLLexer.h =================================================================== --- lib/AsmParser/LLLexer.h +++ lib/AsmParser/LLLexer.h @@ -81,6 +81,7 @@ lltok::Kind LexDigitOrNegative(); lltok::Kind LexPositive(); lltok::Kind LexAt(); + lltok::Kind LexDollar(); lltok::Kind LexExclaim(); lltok::Kind LexPercent(); lltok::Kind LexQuote(); Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -209,6 +209,7 @@ return LexToken(); case '+': return LexPositive(); case '@': return LexAt(); + case '$': return LexDollar(); case '%': return LexPercent(); case '"': return LexQuote(); case '.': @@ -222,13 +223,6 @@ return lltok::dotdotdot; } return lltok::Error; - case '$': - if (const char *Ptr = isLabelTail(CurPtr)) { - CurPtr = Ptr; - StrVal.assign(TokStart, CurPtr-1); - return lltok::LabelStr; - } - return lltok::Error; case ';': SkipLineComment(); return LexToken(); @@ -307,6 +301,43 @@ return lltok::Error; } +lltok::Kind LLLexer::LexDollar() { + if (const char *Ptr = isLabelTail(TokStart)) { + CurPtr = Ptr; + StrVal.assign(TokStart, CurPtr - 1); + return lltok::LabelStr; + } + + // Handle DollarStringConstant: $\"[^\"]*\" + if (CurPtr[0] == '"') { + ++CurPtr; + + while (1) { + int CurChar = getNextChar(); + + if (CurChar == EOF) { + Error("end of file in COMDAT variable name"); + return lltok::Error; + } + if (CurChar == '"') { + StrVal.assign(TokStart + 2, CurPtr - 1); + UnEscapeLexed(StrVal); + if (StringRef(StrVal).find_first_of(0) != StringRef::npos) { + Error("Null bytes are not allowed in names"); + return lltok::Error; + } + return lltok::ComdatVar; + } + } + } + + // Handle ComdatVarName: $[-a-zA-Z$._][-a-zA-Z$._0-9]* + if (ReadVarName()) + return lltok::ComdatVar; + + return lltok::Error; +} + /// ReadString - Read a string until the closing quote. lltok::Kind LLLexer::ReadString(lltok::Kind kind) { const char *Start = CurPtr; @@ -618,6 +649,16 @@ KEYWORD(type); KEYWORD(opaque); + KEYWORD(comdat); + + // Comdat types + KEYWORD(any); + KEYWORD(exactmatch); + KEYWORD(largest); + KEYWORD(newest); + KEYWORD(noduplicates); + KEYWORD(samesize); + KEYWORD(eq); KEYWORD(ne); KEYWORD(slt); KEYWORD(sgt); KEYWORD(sle); KEYWORD(sge); KEYWORD(ult); KEYWORD(ugt); KEYWORD(ule); KEYWORD(uge); KEYWORD(oeq); KEYWORD(one); KEYWORD(olt); KEYWORD(ogt); KEYWORD(ole); Index: lib/AsmParser/LLParser.h =================================================================== --- lib/AsmParser/LLParser.h +++ lib/AsmParser/LLParser.h @@ -34,6 +34,7 @@ class Instruction; class Constant; class GlobalValue; + class Comdat; class MDString; class MDNode; class StructType; @@ -122,6 +123,9 @@ std::map > ForwardRefValIDs; std::vector NumberedVals; + // Comdat forward reference information. + std::map ForwardRefComdats; + // References to blockaddress. The key is the function ValID, the value is // a list of references to blocks in that function. std::map > > @@ -154,6 +158,10 @@ GlobalValue *GetGlobalVal(const std::string &N, Type *Ty, LocTy Loc); GlobalValue *GetGlobalVal(unsigned ID, Type *Ty, LocTy Loc); + /// Get a Comdat with the specified name, creating a forward reference + /// record if needed. + Comdat *getComdat(const std::string &N, LocTy Loc); + // Helper Routines. bool ParseToken(lltok::Kind T, const char *ErrMsg); bool EatIfPresent(lltok::Kind T) { @@ -247,6 +255,7 @@ bool ParseAlias(const std::string &Name, LocTy Loc, unsigned Visibility, unsigned DLLStorageClass, GlobalVariable::ThreadLocalMode TLM, bool UnnamedAddr); + bool parseComdat(); bool ParseStandaloneMetadata(); bool ParseNamedMetadata(); bool ParseMDString(MDString *&Result); @@ -358,6 +367,7 @@ bool ParseGlobalValue(Type *Ty, Constant *&V); bool ParseGlobalTypeAndValue(Constant *&V); bool ParseGlobalValueVector(SmallVectorImpl &Elts); + bool parseOptionalComdat(Comdat *&C); bool ParseMetadataListValue(ValID &ID, PerFunctionState *PFS); bool ParseMetadataValue(ValID &ID, PerFunctionState *PFS); bool ParseMDNodeVector(SmallVectorImpl &, PerFunctionState *PFS); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -163,6 +163,11 @@ return Error(I->second.second, "use of undefined type named '" + I->getKey() + "'"); + if (!ForwardRefComdats.empty()) + return Error(ForwardRefComdats.begin()->second, + "use of undefined comdat '$" + + ForwardRefComdats.begin()->first + "'"); + if (!ForwardRefVals.empty()) return Error(ForwardRefVals.begin()->second.second, "use of undefined value '@" + ForwardRefVals.begin()->first + @@ -238,6 +243,7 @@ case lltok::LocalVar: if (ParseNamedType()) return true; break; case lltok::GlobalID: if (ParseUnnamedGlobal()) return true; break; case lltok::GlobalVar: if (ParseNamedGlobal()) return true; break; + case lltok::ComdatVar: if (parseComdat()) return true; break; case lltok::exclaim: if (ParseStandaloneMetadata()) return true; break; case lltok::MetadataVar:if (ParseNamedMetadata()) return true; break; @@ -513,6 +519,59 @@ UnnamedAddr); } +bool LLParser::parseComdat() { + assert(Lex.getKind() == lltok::ComdatVar); + std::string Name = Lex.getStrVal(); + LocTy NameLoc = Lex.getLoc(); + Lex.Lex(); + + if (ParseToken(lltok::equal, "expected '=' here")) + return true; + + if (ParseToken(lltok::kw_comdat, "expected comdat keyword")) + return TokError("expected comdat type"); + + Comdat::SelectionKind SK; + switch (Lex.getKind()) { + default: + return TokError("unknown selection kind"); + case lltok::kw_any: + SK = Comdat::Any; + break; + case lltok::kw_exactmatch: + SK = Comdat::ExactMatch; + break; + case lltok::kw_largest: + SK = Comdat::Largest; + break; + case lltok::kw_newest: + SK = Comdat::Newest; + break; + case lltok::kw_noduplicates: + SK = Comdat::NoDuplicates; + break; + case lltok::kw_samesize: + SK = Comdat::SameSize; + break; + } + Lex.Lex(); + + // See if the comdat was forward referenced, if so, use the comdat. + auto &ComdatSymTab = M->getComdatSymbolTable(); + auto I = M->getComdatSymbolTable().find(Name); + if (I != ComdatSymTab.end() && !ForwardRefComdats.erase(Name)) + return Error(NameLoc, "redefinition of comdat '$" + Name + "'"); + + Comdat *C; + if (I != ComdatSymTab.end()) + C = &I->second; + else + C = M->getOrInsertComdat(Name); + C->setSelectionKind(SK); + + return false; +} + // MDString: // ::= '!' STRINGCONSTANT bool LLParser::ParseMDString(MDString *&Result) { @@ -837,7 +896,13 @@ if (ParseOptionalAlignment(Alignment)) return true; GV->setAlignment(Alignment); } else { - TokError("unknown global variable property!"); + Comdat *C; + if (parseOptionalComdat(C)) + return true; + if (C) + GV->setComdat(C); + else + return TokError("unknown global variable property!"); } } @@ -1096,6 +1161,24 @@ //===----------------------------------------------------------------------===// +// Comdat Reference/Resolution Routines. +//===----------------------------------------------------------------------===// + +Comdat *LLParser::getComdat(const std::string &Name, LocTy Loc) { + // Look this name up in the comdat symbol table. + auto &ComdatSymTab = M->getComdatSymbolTable(); + auto I = ComdatSymTab.find(Name); + if (I != ComdatSymTab.end()) + return &I->second; + + // Otherwise, create a new forward reference for this value and remember it. + Comdat *C = M->getOrInsertComdat(Name); + ForwardRefComdats[Name] = Loc; + return C; +} + + +//===----------------------------------------------------------------------===// // Helper Routines. //===----------------------------------------------------------------------===// @@ -2789,6 +2872,19 @@ ParseGlobalValue(Ty, V); } +bool LLParser::parseOptionalComdat(Comdat *&C) { + C = nullptr; + if (!EatIfPresent(lltok::kw_comdat)) + return false; + if (Lex.getKind() != lltok::ComdatVar) + return TokError("expected comdat variable"); + LocTy Loc = Lex.getLoc(); + StringRef Name = Lex.getStrVal(); + Lex.Lex(); + C = getComdat(Name, Loc); + return false; +} + /// ParseGlobalValueVector /// ::= /*empty*/ /// ::= TypeAndValue (',' TypeAndValue)* @@ -3089,6 +3185,7 @@ bool UnnamedAddr; LocTy UnnamedAddrLoc; Constant *Prefix = nullptr; + Comdat *C; if (ParseArgumentList(ArgList, isVarArg) || ParseOptionalToken(lltok::kw_unnamed_addr, UnnamedAddr, @@ -3097,6 +3194,7 @@ BuiltinLoc) || (EatIfPresent(lltok::kw_section) && ParseStringConstant(Section)) || + parseOptionalComdat(C) || ParseOptionalAlignment(Alignment) || (EatIfPresent(lltok::kw_gc) && ParseStringConstant(GC)) || @@ -3199,6 +3297,7 @@ Fn->setUnnamedAddr(UnnamedAddr); Fn->setAlignment(Alignment); Fn->setSection(Section); + Fn->setComdat(C); if (!GC.empty()) Fn->setGC(GC.c_str()); Fn->setPrefixData(Prefix); ForwardRefAttrGroups[Fn] = FwdRefAttrGrps; Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -142,6 +142,16 @@ kw_type, kw_opaque, + kw_comdat, + + // Comdat types + kw_any, + kw_exactmatch, + kw_largest, + kw_newest, + kw_noduplicates, + kw_samesize, + kw_eq, kw_ne, kw_slt, kw_sgt, kw_sle, kw_sge, kw_ult, kw_ugt, kw_ule, kw_uge, kw_oeq, kw_one, kw_olt, kw_ogt, kw_ole, kw_oge, kw_ord, kw_uno, kw_ueq, kw_une, @@ -180,6 +190,7 @@ // String valued tokens (StrVal). LabelStr, // foo: GlobalVar, // @foo @"foo" + ComdatVar, // $foo LocalVar, // %foo %"foo" MetadataVar, // !foo StringConstant, // "foo" Index: lib/Bitcode/Reader/BitcodeReader.h =================================================================== --- lib/Bitcode/Reader/BitcodeReader.h +++ lib/Bitcode/Reader/BitcodeReader.h @@ -26,6 +26,7 @@ #include namespace llvm { + class Comdat; class MemoryBuffer; class LLVMContext; @@ -136,6 +137,7 @@ std::vector TypeList; BitcodeReaderValueList ValueList; BitcodeReaderMDValueList MDValueList; + std::vector ComdatList; SmallVector InstructionList; SmallVector, 64> UseListRecords; Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -45,6 +45,7 @@ std::vector().swap(TypeList); ValueList.clear(); MDValueList.clear(); + std::vector().swap(ComdatList); std::vector().swap(MAttributes); std::vector().swap(FunctionBBs); @@ -205,6 +206,25 @@ } } +static Comdat::SelectionKind getDecodedComdatSelectionKind(unsigned Val) { + switch (Val) { + default: // Map unknown selection kinds to any. + //FIXME: Why not return an error? + case bitc::COMDAT_SELECTION_KIND_ANY: + return Comdat::Any; + case bitc::COMDAT_SELECTION_KIND_EXACT_MATCH: + return Comdat::ExactMatch; + case bitc::COMDAT_SELECTION_KIND_LARGEST: + return Comdat::Largest; + case bitc::COMDAT_SELECTION_KIND_NEWEST: + return Comdat::Newest; + case bitc::COMDAT_SELECTION_KIND_NO_DUPLICATES: + return Comdat::NoDuplicates; + case bitc::COMDAT_SELECTION_KIND_SAME_SIZE: + return Comdat::SameSize; + } +} + static void UpgradeDLLImportExportLinkage(llvm::GlobalValue *GV, unsigned Val) { switch (Val) { case 5: GV->setDLLStorageClass(GlobalValue::DLLImportStorageClass); break; @@ -1839,6 +1859,18 @@ GCTable.push_back(S); break; } + case bitc::MODULE_CODE_COMDAT: { // COMDAT: [selection_kind, name] + if (Record.size() < 2) + return Error(InvalidRecord); + Comdat::SelectionKind SK = getDecodedComdatSelectionKind(Record[0]); + std::string ComdatName; + if (ConvertToString(Record, 1, ComdatName)) + return Error(InvalidRecord); + Comdat *C = TheModule->getOrInsertComdat(ComdatName); + C->setSelectionKind(SK); + ComdatList.push_back(C); + break; + } // GLOBALVAR: [pointer type, isconst, initid, // linkage, alignment, section, visibility, threadlocal, // unnamed_addr, dllstorageclass] @@ -1899,6 +1931,12 @@ // Remember which value to use for the global initializer. if (unsigned InitID = Record[2]) GlobalInits.push_back(std::make_pair(NewGV, InitID-1)); + + if (Record.size() > 11) + if (unsigned ComdatID = Record[11]) { + assert(ComdatID <= ComdatList.size()); + NewGV->setComdat(ComdatList[ComdatID - 1]); + } break; } // FUNCTION: [type, callingconv, isproto, linkage, paramattr, @@ -1952,6 +1990,12 @@ else UpgradeDLLImportExportLinkage(Func, Record[3]); + if (Record.size() > 12) + if (unsigned ComdatID = Record[12]) { + assert(ComdatID <= ComdatList.size()); + Func->setComdat(ComdatList[ComdatID - 1]); + } + ValueList.push_back(Func); // If this is a function with a body, remember the prototype we are Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -524,6 +524,36 @@ llvm_unreachable("Invalid TLS model"); } +static unsigned getEncodedComdatSelectionKind(const Comdat &C) { + switch (C.getSelectionKind()) { + case Comdat::Any: + return bitc::COMDAT_SELECTION_KIND_ANY; + case Comdat::ExactMatch: + return bitc::COMDAT_SELECTION_KIND_EXACT_MATCH; + case Comdat::Largest: + return bitc::COMDAT_SELECTION_KIND_LARGEST; + case Comdat::Newest: + return bitc::COMDAT_SELECTION_KIND_NEWEST; + case Comdat::NoDuplicates: + return bitc::COMDAT_SELECTION_KIND_NO_DUPLICATES; + case Comdat::SameSize: + return bitc::COMDAT_SELECTION_KIND_SAME_SIZE; + } + llvm_unreachable("Invalid selection kind"); +} + +static void writeComdats(const ValueEnumerator &VE, BitstreamWriter &Stream) { + SmallVector Vals; + for (const Comdat *C : VE.getComdats()) { + // COMDAT: [selection_kind, name] + Vals.push_back(getEncodedComdatSelectionKind(*C)); + for (char Chr : C->getName()) + Vals.push_back((unsigned char)Chr); + Stream.EmitRecord(bitc::MODULE_CODE_COMDAT, Vals, /*AbbrevToUse*/ 0); + Vals.clear(); + } +} + // Emit top-level description of module, including target triple, inline asm, // descriptors for global variables, and function prototype info. static void WriteModuleInfo(const Module *M, const ValueEnumerator &VE, @@ -625,12 +655,14 @@ if (GV.isThreadLocal() || GV.getVisibility() != GlobalValue::DefaultVisibility || GV.hasUnnamedAddr() || GV.isExternallyInitialized() || - GV.getDLLStorageClass() != GlobalValue::DefaultStorageClass) { + GV.getDLLStorageClass() != GlobalValue::DefaultStorageClass || + GV.hasComdat()) { 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()) + 1) : 0); } else { AbbrevToUse = SimpleGVarAbbrev; } @@ -656,6 +688,7 @@ Vals.push_back(F.hasPrefixData() ? (VE.getValueID(F.getPrefixData()) + 1) : 0); Vals.push_back(getEncodedDLLStorageClass(F)); + Vals.push_back(F.hasComdat() ? (VE.getComdatID(F.getComdat()) + 1) : 0); unsigned AbbrevToUse = 0; Stream.EmitRecord(bitc::MODULE_CODE_FUNCTION, Vals, AbbrevToUse); @@ -1915,6 +1948,8 @@ // Emit information describing all of the types in the module. WriteTypeTable(VE, Stream); + writeComdats(VE, Stream); + // Emit top-level description of module, including target triple, inline asm, // descriptors for global variables, and function prototype info. WriteModuleInfo(M, VE, Stream); Index: lib/Bitcode/Writer/ValueEnumerator.h =================================================================== --- lib/Bitcode/Writer/ValueEnumerator.h +++ lib/Bitcode/Writer/ValueEnumerator.h @@ -15,6 +15,7 @@ #define VALUE_ENUMERATOR_H #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/IR/Attributes.h" #include @@ -25,6 +26,7 @@ class Value; class Instruction; class BasicBlock; +class Comdat; class Function; class Module; class MDNode; @@ -34,6 +36,76 @@ class MDSymbolTable; class raw_ostream; +// This is an somewhat odd map and set hybrid. It can be seen as a set that +// also provides +// * A stable iteration over the elements (like SetVector). +// * A find method that returns the iterator of that element. +// +// It can also be seen as a more efficient and convinient version of +// MapVector where an element is mapped to its position in the +// vector. +// +// It might be possible to generalize this to handle Value and Types too. +template class PositionSet { +public: + typedef DenseMap MapType; + typedef std::vector VectorType; + +private: + MapType Map; + VectorType Vector; + +public: + typedef typename VectorType::iterator iterator; + typedef typename VectorType::const_iterator const_iterator; + typedef typename VectorType::size_type SizeType; + + SizeType size() const { + return Vector.size(); + } + + iterator begin() { + return Vector.begin(); + } + + const_iterator begin() const { + return Vector.begin(); + } + + iterator end() { + return Vector.end(); + } + + const_iterator end() const { + return Vector.end(); + } + + const T &operator[](SizeType I) const { + assert(I < Vector.size() && "Index out of range!"); + return Vector[I]; + } + + iterator find(const T &Key) { + typename MapType::const_iterator Pos = Map.find(Key); + return Pos == Map.end() ? Vector.end() : (Vector.begin() + Pos->second); + } + const_iterator find(const T &Key) const { + return const_cast*>(this)->find(Key); + } + + std::pair insert(const T &K) { + std::pair Pair = std::make_pair(K, 0); + std::pair Result = Map.insert(Pair); + unsigned &I = Result.first->second; + if (Result.second) { + Vector.push_back(K); + I = Vector.size() - 1; + return std::make_pair(std::prev(end()), true); + } + return std::make_pair(begin() + I, false); + } +}; + class ValueEnumerator { public: typedef std::vector TypeList; @@ -48,6 +120,10 @@ typedef DenseMap ValueMapType; ValueMapType ValueMap; ValueList Values; + + typedef PositionSet ComdatSetType; + ComdatSetType Comdats; + ValueList MDValues; SmallVector FunctionLocalMDs; ValueMapType MDValueMap; @@ -139,6 +215,9 @@ return AttributeGroups; } + const ComdatSetType &getComdats() const { return Comdats; } + unsigned getComdatID(const Comdat *C) const; + /// getGlobalBasicBlockID - This returns the function-specific ID for the /// specified basic block. This is relatively expensive information, so it /// should only be used by rare constructs such as address-of-label. Index: lib/Bitcode/Writer/ValueEnumerator.cpp =================================================================== --- lib/Bitcode/Writer/ValueEnumerator.cpp +++ lib/Bitcode/Writer/ValueEnumerator.cpp @@ -117,6 +117,13 @@ return I->second; } +unsigned ValueEnumerator::getComdatID(const Comdat *C) const { + auto I = Comdats.find(C); + assert(I != Comdats.end()); + assert(*I == C); + return I - Comdats.begin(); +} + void ValueEnumerator::setInstructionID(const Instruction *I) { InstructionMap[I] = InstructionCount++; } @@ -307,6 +314,10 @@ return; } + if (auto *GO = dyn_cast(V)) + if (const Comdat *C = GO->getComdat()) + Comdats.insert(C); + // Enumerate the type of this value. EnumerateType(V->getType()); Index: lib/CodeGen/TargetLoweringObjectFileImpl.cpp =================================================================== --- lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -200,14 +200,23 @@ // Infer section flags from the section name if we can. Kind = getELFKindForNamedSection(SectionName, Kind); + StringRef Group = ""; + unsigned Flags = getELFSectionFlags(Kind); + if (const Comdat *C = GV->getComdat()) { + if (C->getSelectionKind() != Comdat::Any) + report_fatal_error("ELF COMDATs only support SelectionKind::Any, '" + + C->getName() + "' cannot be lowered."); + Group = C->getName(); + Flags |= ELF::SHF_GROUP; + } return getContext().getELFSection(SectionName, - getELFSectionType(SectionName, Kind), - getELFSectionFlags(Kind), Kind); + getELFSectionType(SectionName, Kind), Flags, + Kind, /*EntrySize=*/0, Group); } /// getSectionPrefixForGlobal - Return the section prefix name used by options /// FunctionsSections and DataSections. -static const char *getSectionPrefixForGlobal(SectionKind Kind) { +static StringRef getSectionPrefixForGlobal(SectionKind Kind) { if (Kind.isText()) return ".text."; if (Kind.isReadOnly()) return ".rodata."; if (Kind.isBSS()) return ".bss."; @@ -238,18 +247,24 @@ // If this global is linkonce/weak and the target handles this by emitting it // into a 'uniqued' section name, create and return the section now. - if ((GV->isWeakForLinker() || EmitUniquedSection) && + if ((GV->isWeakForLinker() || EmitUniquedSection || GV->hasComdat()) && !Kind.isCommon()) { - const char *Prefix; - Prefix = getSectionPrefixForGlobal(Kind); + StringRef Prefix = getSectionPrefixForGlobal(Kind); - SmallString<128> Name(Prefix, Prefix+strlen(Prefix)); + SmallString<128> Name(Prefix); TM.getNameWithPrefix(Name, GV, Mang, true); StringRef Group = ""; unsigned Flags = getELFSectionFlags(Kind); - if (GV->isWeakForLinker()) { - Group = Name.substr(strlen(Prefix)); + if (GV->isWeakForLinker() || GV->hasComdat()) { + if (const Comdat *C = GV->getComdat()) { + if (C->getSelectionKind() != Comdat::Any) + report_fatal_error("ELF COMDATs only support SelectionKind::Any, '" + + C->getName() + "' cannot be lowered."); + Group = C->getName(); + } else { + Group = Name.substr(Prefix.size()); + } Flags |= ELF::SHF_GROUP; } @@ -490,6 +505,10 @@ StringRef Segment, Section; unsigned TAA = 0, StubSize = 0; bool TAAParsed; + + if (const Comdat *C = GV->getComdat()) + report_fatal_error("MachO doesn't support COMDATs, '" + C->getName() + + "' cannot be lowered."); std::string ErrorCode = MCSectionMachO::ParseSectionSpecifier(GV->getSection(), Segment, Section, TAA, TAAParsed, StubSize); @@ -561,6 +580,10 @@ SelectSectionForGlobal(const GlobalValue *GV, SectionKind Kind, Mangler &Mang, const TargetMachine &TM) const { + if (const Comdat *C = GV->getComdat()) + report_fatal_error("MachO doesn't support COMDATs, '" + C->getName() + + "' cannot be lowered."); + // Handle thread local data. if (Kind.isThreadBSS()) return TLSBSSSection; if (Kind.isThreadData()) return TLSDataSection; @@ -728,6 +751,32 @@ return Flags; } +static int getSelectionForCOFF(const GlobalValue *GV) { + if (const Comdat *C = GV->getComdat()) { + if (C->getName() == GV->getName()) { + switch (C->getSelectionKind()) { + case Comdat::Any: + return COFF::IMAGE_COMDAT_SELECT_ANY; + case Comdat::ExactMatch: + return COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH; + case Comdat::Largest: + return COFF::IMAGE_COMDAT_SELECT_LARGEST; + case Comdat::Newest: + return COFF::IMAGE_COMDAT_SELECT_NEWEST; + case Comdat::NoDuplicates: + return COFF::IMAGE_COMDAT_SELECT_NODUPLICATES; + case Comdat::SameSize: + return COFF::IMAGE_COMDAT_SELECT_SAME_SIZE; + } + } else { + return COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE; + } + } else if (GV->isWeakForLinker()) { + return COFF::IMAGE_COMDAT_SELECT_ANY; + } + return 0; +} + const MCSection *TargetLoweringObjectFileCOFF::getExplicitSectionGlobal( const GlobalValue *GV, SectionKind Kind, Mangler &Mang, const TargetMachine &TM) const { @@ -735,11 +784,26 @@ unsigned Characteristics = getCOFFSectionFlags(Kind); StringRef Name = GV->getSection(); StringRef COMDATSymName = ""; - if (GV->isWeakForLinker()) { - Selection = COFF::IMAGE_COMDAT_SELECT_ANY; - Characteristics |= COFF::IMAGE_SCN_LNK_COMDAT; - MCSymbol *Sym = TM.getSymbol(GV, Mang); - COMDATSymName = Sym->getName(); + if ((GV->isWeakForLinker() || GV->hasComdat()) && !Kind.isCommon()) { + Selection = getSelectionForCOFF(GV); + if (Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE || + !GV->hasPrivateLinkage()) { + const GlobalValue *ComdatGV; + if (Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { + StringRef ComdatGVName = GV->getComdat()->getName(); + ComdatGV = GV->getParent()->getNamedValue(ComdatGVName); + if (!ComdatGV) + report_fatal_error("Associative COMDAT symbol '" + ComdatGVName + + "' does not exist."); + } else { + ComdatGV = GV; + } + MCSymbol *Sym = TM.getSymbol(ComdatGV, Mang); + COMDATSymName = Sym->getName(); + Characteristics |= COFF::IMAGE_SCN_LNK_COMDAT; + } else { + Selection = 0; + } } return getContext().getCOFFSection(Name, Characteristics, @@ -776,17 +840,32 @@ // into a 'uniqued' section name, create and return the section now. // Section names depend on the name of the symbol which is not feasible if the // symbol has private linkage. - if ((GV->isWeakForLinker() || EmitUniquedSection) && - !GV->hasPrivateLinkage() && !Kind.isCommon()) { + if ((GV->isWeakForLinker() || EmitUniquedSection || GV->hasComdat()) && + !Kind.isCommon()) { const char *Name = getCOFFSectionNameForUniqueGlobal(Kind); unsigned Characteristics = getCOFFSectionFlags(Kind); Characteristics |= COFF::IMAGE_SCN_LNK_COMDAT; - MCSymbol *Sym = TM.getSymbol(GV, Mang); - return getContext().getCOFFSection( - Name, Characteristics, Kind, Sym->getName(), - GV->isWeakForLinker() ? COFF::IMAGE_COMDAT_SELECT_ANY - : COFF::IMAGE_COMDAT_SELECT_NODUPLICATES); + int Selection = getSelectionForCOFF(GV); + if (!Selection) + Selection = COFF::IMAGE_COMDAT_SELECT_NODUPLICATES; + if (Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE || + !GV->hasPrivateLinkage()) { + const GlobalValue *ComdatGV; + if (Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { + StringRef ComdatGVName = GV->getComdat()->getName(); + ComdatGV = GV->getParent()->getNamedValue(ComdatGVName); + if (!ComdatGV) + report_fatal_error("Associative COMDAT symbol '" + ComdatGVName + + "' does not exist."); + } else { + ComdatGV = GV; + } + MCSymbol *Sym = TM.getSymbol(ComdatGV, Mang); + StringRef COMDATSymName = Sym->getName(); + return getContext().getCOFFSection(Name, Characteristics, Kind, + COMDATSymName, Selection); + } } if (Kind.isText()) Index: lib/IR/AsmWriter.h =================================================================== --- lib/IR/AsmWriter.h +++ lib/IR/AsmWriter.h @@ -16,6 +16,7 @@ #define LLVM_IR_ASSEMBLYWRITER_H #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SetVector.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/TypeFinder.h" @@ -26,6 +27,7 @@ class BasicBlock; class Function; class GlobalValue; +class Comdat; class Module; class NamedMDNode; class Value; @@ -70,6 +72,7 @@ SlotTracker &Machine; TypePrinting TypePrinter; AssemblyAnnotationWriter *AnnotationWriter; + SetVector Comdats; public: /// Construct an AssemblyWriter with an external SlotTracker @@ -101,6 +104,7 @@ void printTypeIdentities(); void printGlobal(const GlobalVariable *GV); void printAlias(const GlobalAlias *GV); + void printComdat(const Comdat *C); void printFunction(const Function *F); void printArgument(const Argument *FA, AttributeSet Attrs, unsigned Idx); void printBasicBlock(const BasicBlock *BB); Index: lib/IR/AsmWriter.cpp =================================================================== --- lib/IR/AsmWriter.cpp +++ lib/IR/AsmWriter.cpp @@ -106,6 +106,7 @@ enum PrefixType { GlobalPrefix, + ComdatPrefix, LabelPrefix, LocalPrefix, NoPrefix @@ -119,6 +120,7 @@ switch (Prefix) { case NoPrefix: break; case GlobalPrefix: OS << '@'; break; + case ComdatPrefix: OS << '$'; break; case LabelPrefix: break; case LocalPrefix: OS << '%'; break; } @@ -202,6 +204,7 @@ case Type::PPC_FP128TyID: OS << "ppc_fp128"; return; case Type::LabelTyID: OS << "label"; return; case Type::MetadataTyID: OS << "metadata"; return; + case Type::X86_MMXTyID: OS << "x86_mmx"; return; case Type::IntegerTyID: OS << 'i' << cast(Ty)->getBitWidth(); @@ -1165,8 +1168,15 @@ } void AssemblyWriter::init() { - if (TheModule) - TypePrinter.incorporateTypes(*TheModule); + if (!TheModule) + return; + TypePrinter.incorporateTypes(*TheModule); + for (const Function &F : *TheModule) + if (const Comdat *C = F.getComdat()) + Comdats.insert(C); + for (const GlobalVariable &GV : TheModule->globals()) + if (const Comdat *C = GV.getComdat()) + Comdats.insert(C); } @@ -1308,6 +1318,15 @@ printTypeIdentities(); + // Output all comdats. + if (!Comdats.empty()) + Out << '\n'; + for (const Comdat *C : Comdats) { + printComdat(C); + if (C != Comdats.back()) + Out << '\n'; + } + // Output all globals. if (!M->global_empty()) Out << '\n'; for (Module::const_global_iterator I = M->global_begin(), E = M->global_end(); @@ -1470,6 +1489,10 @@ PrintEscapedString(GV->getSection(), Out); Out << '"'; } + if (GV->hasComdat()) { + Out << ", comdat "; + PrintLLVMName(Out, GV->getComdat()->getName(), ComdatPrefix); + } if (GV->getAlignment()) Out << ", align " << GV->getAlignment(); @@ -1506,10 +1529,31 @@ writeOperand(Aliasee, !isa(Aliasee)); } + if (GA->hasComdat()) { + Out << ", comdat "; + PrintLLVMName(Out, GA->getComdat()->getName(), ComdatPrefix); + } + printInfoComment(*GA); Out << '\n'; } +void AssemblyWriter::printComdat(const Comdat *C) { + PrintLLVMName(Out, C->getName(), ComdatPrefix); + Out << " = comdat "; + + switch (C->getSelectionKind()) { + case Comdat::Any: Out << "any"; break; + case Comdat::ExactMatch: Out << "exactmatch"; break; + case Comdat::Largest: Out << "largest"; break; + case Comdat::Newest: Out << "newest"; break; + case Comdat::NoDuplicates: Out << "noduplicates"; break; + case Comdat::SameSize: Out << "samesize"; break; + } + + Out << '\n'; +} + void AssemblyWriter::printTypeIdentities() { if (TypePrinter.NumberedTypes.empty() && TypePrinter.NamedTypes.empty()) @@ -1647,6 +1691,10 @@ PrintEscapedString(F->getSection(), Out); Out << '"'; } + if (F->hasComdat()) { + Out << " comdat "; + PrintLLVMName(Out, F->getComdat()->getName(), ComdatPrefix); + } if (F->getAlignment()) Out << " align " << F->getAlignment(); if (F->hasGC()) Index: lib/IR/CMakeLists.txt =================================================================== --- lib/IR/CMakeLists.txt +++ lib/IR/CMakeLists.txt @@ -3,6 +3,7 @@ Attributes.cpp AutoUpgrade.cpp BasicBlock.cpp + Comdat.cpp ConstantFold.cpp ConstantRange.cpp Constants.cpp Index: lib/IR/Comdat.cpp =================================================================== --- /dev/null +++ lib/IR/Comdat.cpp @@ -0,0 +1,29 @@ +//===-- Comdat.cpp - Implement Metadata classes --------------------------===// +// +// 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 Comdat class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/Comdat.h" +#include "SymbolTableListTraitsImpl.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +using namespace llvm; + +Comdat::Comdat(SelectionKind SK, StringMapEntry *Name) + : Name(Name), SK(SK) {} + +Comdat::Comdat(Comdat &&C) : Name(C.Name), SK(C.SK) {} + +Comdat::Comdat() : Name(nullptr), SK(Comdat::Any) {} + +StringRef Comdat::getName() const { + return Name->first(); +} Index: lib/IR/Globals.cpp =================================================================== --- lib/IR/Globals.cpp +++ lib/IR/Globals.cpp @@ -103,6 +103,16 @@ return cast(this)->getSection(); } +const Comdat *GlobalValue::getComdat() const { + if (auto *GA = dyn_cast(this)) { + // In general we cannot compute this at the IR level, but we try. + if (const GlobalObject *GO = getBaseObject(*GA->getAliasee())) + return GO->getComdat(); + return nullptr; + } + return cast(this)->getComdat(); +} + void GlobalObject::setSection(StringRef S) { Section = S; } bool GlobalValue::isDeclaration() const { Index: lib/IR/Module.cpp =================================================================== --- lib/IR/Module.cpp +++ lib/IR/Module.cpp @@ -437,3 +437,10 @@ return dwarf::DWARF_VERSION; return cast(Val)->getZExtValue(); } + +Comdat *Module::getOrInsertComdat(StringRef Name) { + Comdat C; + auto &Entry = ComdatSymTab.GetOrCreateValue(Name, std::move(C)); + Entry.second.Name = &Entry; + return &Entry.second; +} Index: test/Feature/comdat.ll =================================================================== --- /dev/null +++ test/Feature/comdat.ll @@ -0,0 +1,15 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s + +$f = comdat any +; CHECK: $f = comdat any + +$f2 = comdat any +; CHECK-NOT: f2 + +@v = global i32 0, comdat $f +; CHECK: @v = global i32 0, comdat $f + +define void @f() comdat $f { + ret void +} +; CHECK: define void @f() comdat $f Index: test/Instrumentation/AddressSanitizer/do-not-instrument-llvm-metadata.ll =================================================================== --- test/Instrumentation/AddressSanitizer/do-not-instrument-llvm-metadata.ll +++ test/Instrumentation/AddressSanitizer/do-not-instrument-llvm-metadata.ll @@ -5,7 +5,7 @@ target triple = "x86_64-unknown-linux-gnu" @.str_noinst = private unnamed_addr constant [4 x i8] c"aaa\00", section "llvm.metadata" -@.str_inst = private unnamed_addr constant [4 x i8] c"aaa\00", +@.str_inst = private unnamed_addr constant [4 x i8] c"aaa\00" ; CHECK-NOT: {{asan_gen.*str_noinst}} ; CHECK: {{asan_gen.*str_inst}} Index: test/MC/COFF/comdat.ll =================================================================== --- /dev/null +++ test/MC/COFF/comdat.ll @@ -0,0 +1,62 @@ +; RUN: llc -mtriple i386-pc-win32 < %s | FileCheck %s + +$f1 = comdat any +@v1 = global i32 0, comdat $f1 +define void @f1() comdat $f1 { + ret void +} + +$f2 = comdat exactmatch +@v2 = global i32 0, comdat $f2 +define void @f2() comdat $f2 { + ret void +} + +$f3 = comdat largest +@v3 = global i32 0, comdat $f3 +define void @f3() comdat $f3 { + ret void +} + +$f4 = comdat newest +@v4 = global i32 0, comdat $f4 +define void @f4() comdat $f4 { + ret void +} + +$f5 = comdat noduplicates +@v5 = global i32 0, comdat $f5 +define void @f5() comdat $f5 { + ret void +} + +$f6 = comdat samesize +@v6 = global i32 0, comdat $f6 +define void @f6() comdat $f6 { + ret void +} + +; CHECK: .section .text,"xr",discard,_f1 +; CHECK: .globl _f1 +; CHECK: .section .text,"xr",same_contents,_f2 +; CHECK: .globl _f2 +; CHECK: .section .text,"xr",largest,_f3 +; CHECK: .globl _f3 +; CHECK: .section .text,"xr",newest,_f4 +; CHECK: .globl _f4 +; CHECK: .section .text,"xr",one_only,_f5 +; CHECK: .globl _f5 +; CHECK: .section .text,"xr",same_size,_f6 +; CHECK: .globl _f6 +; CHECK: .section .bss,"bw",associative,_f1 +; CHECK: .globl _v1 +; CHECK: .section .bss,"bw",associative,_f2 +; CHECK: .globl _v2 +; CHECK: .section .bss,"bw",associative,_f3 +; CHECK: .globl _v3 +; CHECK: .section .bss,"bw",associative,_f4 +; CHECK: .globl _v4 +; CHECK: .section .bss,"bw",associative,_f5 +; CHECK: .globl _v5 +; CHECK: .section .bss,"bw",associative,_f6 +; CHECK: .globl _v6 Index: test/MC/ELF/comdat.ll =================================================================== --- /dev/null +++ test/MC/ELF/comdat.ll @@ -0,0 +1,11 @@ +; RUN: llc -mtriple x86_64-pc-linux-gnu < %s | FileCheck %s + +$f = comdat any +@v = global i32 0, comdat $f +define void @f() comdat $f { + ret void +} +; CHECK: .section .text.f,"axG",@progbits,f,comdat +; CHECK: .globl f +; CHECK: .section .bss.v,"aGw",@nobits,f,comdat +; CHECK: .globl v Index: test/MC/MachO/comdat.ll =================================================================== --- /dev/null +++ test/MC/MachO/comdat.ll @@ -0,0 +1,6 @@ +; RUN: not llc -mtriple x86_64-apple-darwin < %s 2> %t +; RUN: FileCheck < %t %s + +$f = comdat any +@v = global i32 0, comdat $f +; CHECK: LLVM ERROR: MachO doesn't support COMDATs, 'f' cannot be lowered.