Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -641,8 +641,9 @@ iterate over them as an array, alignment padding would break this iteration. The maximum alignment is ``1 << 29``. -Globals can also have a :ref:`DLL storage class ` and -an optional list of attached :ref:`metadata `, +Globals can also have a :ref:`DLL storage class `, +an optional :ref:`global attributes `, and +an optional list of attached :ref:`metadata `. Variables and aliases can have a :ref:`Thread Local Storage Model `. @@ -1613,6 +1614,14 @@ the ELF x86-64 abi, but it can be disabled for some compilation units. +.. _glattrs: + +Global Attributes +------------------- + +Attributes may be set to communicate additional information about a global variable. +Unlike :ref:`function attributes `, attributes on a global variable +are grouped into a single :ref:`attribute group `. .. _opbundles: Index: include/llvm/IR/Attributes.h =================================================================== --- include/llvm/IR/Attributes.h +++ include/llvm/IR/Attributes.h @@ -543,6 +543,85 @@ //===----------------------------------------------------------------------===// /// \class +/// This is a restricted interface to AttributeList for GlobalVariables. +/// This allows reuse of the underlying AttributeList unification mechanisms +/// but only supports one slot index. +template +class SingleSlotAttributeList { + AttributeList Impl; + +public: + enum AttrIndex : unsigned { + Index = Idx + }; + + static SingleSlotAttributeList get(LLVMContext &C, AttrBuilder &B) { + return AttributeList::get(C, Index, B); + } + + SingleSlotAttributeList() = default; + SingleSlotAttributeList(AttributeList Impl) : Impl(Impl) { + assert(Impl.getNumSlots() <= 1 && "can contain only single slot"); + } + + /// The function attributes are returned. + AttributeList getAttributes() const { return Impl; } + + /// Add an attribute to the attribute set at the given index. Because + /// attribute sets are immutable, this returns a new set. + SingleSlotAttributeList addAttribute(LLVMContext &C, + Attribute::AttrKind Kind) const { + return Impl.addAttribute(C, Idx, Kind); + } + + /// Add an attribute to the attribute set at the given index. Because + /// attribute sets are immutable, this returns a new set. + SingleSlotAttributeList addAttribute(LLVMContext &C, StringRef Kind, + StringRef Value = StringRef()) const { + return Impl.addAttribute(C, Idx, Kind, Value); + } + + /// Add an attribute to the attribute set at the given indices. Because + /// attribute sets are immutable, this returns a new set. + SingleSlotAttributeList addAttribute(LLVMContext &C, Attribute A) const { + return Impl.addAttribute(C, makeArrayRef(Idx), A); + } + + /// Add attributes to the attribute set at the given index. Because + /// attribute sets are immutable, this returns a new set. + SingleSlotAttributeList addAttributes(LLVMContext &C, + AttributeList Attrs) const { + return Impl.addAttributes(C, Idx, Attrs); + } + + /// Return true if the attribute exists. + bool hasAttribute(Attribute::AttrKind Kind) const { + return Impl.hasAttribute(Idx, Kind); + } + + /// Return true if the attribute exists. + bool hasAttribute(StringRef Kind) const { + return Impl.hasAttribute(Idx, Kind); + } + + /// Return true if any attributes exist. + bool hasAttributes() const { + return Impl.hasAttributes(Idx); + } + + /// Return the given attribute object. + Attribute getAttribute(Attribute::AttrKind Kind) const { + return Impl.getAttribute(Idx, Kind); + } + + /// Return the given attribute object. + Attribute getAttribute(StringRef Kind) const { + return Impl.getAttribute(Idx, Kind); + } +}; + +//===----------------------------------------------------------------------===// +/// \class /// \brief This class is used in conjunction with the Attribute::get method to /// create an Attribute object. The object itself is uniquified. The Builder's /// value, however, is not. So this can be used as a quick way to test for Index: include/llvm/IR/GlobalVariable.h =================================================================== --- include/llvm/IR/GlobalVariable.h +++ include/llvm/IR/GlobalVariable.h @@ -23,6 +23,7 @@ #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/Twine.h" #include "llvm/ADT/ilist_node.h" +#include "llvm/IR/Attributes.h" #include "llvm/IR/GlobalObject.h" #include "llvm/IR/OperandTraits.h" #include "llvm/IR/Value.h" @@ -39,14 +40,17 @@ class DIGlobalVariableExpression; class GlobalVariable : public GlobalObject, public ilist_node { +public: + typedef SingleSlotAttributeList AttributeListType; +protected: friend class SymbolTableListTraits; + AttributeListType Attrs; bool isConstantGlobal : 1; // Is this a global constant? bool isExternallyInitializedConstant : 1; // Is this a global whose value // can change from its initial // value before global // initializers are run? - public: /// GlobalVariable ctor - If a parent module is specified, the global is /// automatically inserted into the end of the specified modules global list. @@ -178,6 +182,30 @@ /// Fill the vector with all debug info attachements. void getDebugInfo(SmallVectorImpl &GVs) const; + /// Add attributes to this global. + void addAttribute(Attribute::AttrKind Kind) { + Attrs = Attrs.addAttribute(getContext(), Kind); + } + + /// Add attributes to this global. + void addAttribute(StringRef Kind, StringRef Val = StringRef()) { + Attrs = Attrs.addAttribute(getContext(), Attribute::get(getContext(), Kind, Val)); + } + + Attribute getAttribute(Attribute::AttrKind Kind) const { + return Attrs.getAttribute(Kind); + } + + Attribute getAttribute(StringRef Kind) const { + return Attrs.getAttribute(Kind); + } + + /// Return the attribute list for this global + AttributeListType getAttributes() const { return Attrs; } + + /// Set attribute list for this global + void setAttributes(AttributeListType A) { Attrs = A; } + // Methods for support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const Value *V) { return V->getValueID() == Value::GlobalVariableVal; Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -165,6 +165,16 @@ Context, AttributeList::FunctionIndex, AttributeList::get(Context, AttributeList::FunctionIndex, FnAttrs)); II->setAttributes(AS); + } else if (GlobalVariable *GV = dyn_cast(V)) { + auto OrigAS = GV->getAttributes(); + GlobalVariable::AttributeListType NewAS; + const auto GlobalVariableIndex = GlobalVariable::AttributeListType::Index; + + AttrBuilder Attrs(OrigAS.getAttributes(), GlobalVariableIndex); + Attrs.merge(B); + auto AL = AttributeList::get(Context, GlobalVariableIndex, Attrs); + + GV->setAttributes(NewAS.addAttributes(Context, AL)); } else { llvm_unreachable("invalid object with forward attribute group reference"); } @@ -835,10 +845,10 @@ /// ParseGlobal /// ::= GlobalVar '=' OptionalLinkage OptionalVisibility OptionalDLLStorageClass /// OptionalThreadLocal OptionalUnnamedAddr OptionalAddrSpace -/// OptionalExternallyInitialized GlobalType Type Const +/// OptionalExternallyInitialized GlobalType Type Const OptionalAttrs /// ::= OptionalLinkage OptionalVisibility OptionalDLLStorageClass /// OptionalThreadLocal OptionalUnnamedAddr OptionalAddrSpace -/// OptionalExternallyInitialized GlobalType Type Const +/// OptionalExternallyInitialized GlobalType Type Const OptionalAttrs /// /// Everything up to and including OptionalUnnamedAddr has been parsed /// already. @@ -953,6 +963,18 @@ } } + // Parse attributes + AttrBuilder Attrs; + LocTy BuiltinLoc; + std::vector FwdRefAttrGrps; + if (ParseFnAttributeValuePairs(Attrs, FwdRefAttrGrps, false, BuiltinLoc)) + return true; + + if (Attrs.hasAttributes() || !FwdRefAttrGrps.empty()) { + GV->setAttributes(GlobalVariable::AttributeListType::get(Context, Attrs)); + ForwardRefAttrGroups[GV] = FwdRefAttrGrps; + } + return false; } Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -2835,7 +2835,7 @@ // GLOBALVAR: [pointer type, isconst, initid, // linkage, alignment, section, visibility, threadlocal, // unnamed_addr, externally_initialized, dllstorageclass, - // comdat] + // comdat, attributes] case bitc::MODULE_CODE_GLOBALVAR: { if (Record.size() < 6) return error("Invalid record"); @@ -2912,6 +2912,9 @@ } else if (hasImplicitComdat(RawLinkage)) { NewGV->setComdat(reinterpret_cast(1)); } + if (Record.size() > 12) { + NewGV->setAttributes(getAttributes(Record[12])); + } break; } Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1196,7 +1196,7 @@ // GLOBALVAR: [type, isconst, initid, // linkage, alignment, section, visibility, threadlocal, // unnamed_addr, externally_initialized, dllstorageclass, - // comdat] + // comdat, attributes] Vals.push_back(VE.getTypeID(GV.getValueType())); Vals.push_back(GV.getType()->getAddressSpace() << 2 | 2 | GV.isConstant()); Vals.push_back(GV.isDeclaration() ? 0 : @@ -1209,13 +1209,15 @@ GV.getUnnamedAddr() != GlobalValue::UnnamedAddr::None || GV.isExternallyInitialized() || GV.getDLLStorageClass() != GlobalValue::DefaultStorageClass || - GV.hasComdat()) { + GV.hasComdat() || + GV.getAttributes().hasAttributes()) { Vals.push_back(getEncodedVisibility(GV)); Vals.push_back(getEncodedThreadLocalMode(GV)); Vals.push_back(getEncodedUnnamedAddr(GV)); Vals.push_back(GV.isExternallyInitialized()); Vals.push_back(getEncodedDLLStorageClass(GV)); Vals.push_back(GV.hasComdat() ? VE.getComdatID(GV.getComdat()) : 0); + Vals.push_back(VE.getAttributeID(GV.getAttributes().getAttributes())); } else { AbbrevToUse = SimpleGVarAbbrev; } Index: lib/Bitcode/Writer/ValueEnumerator.cpp =================================================================== --- lib/Bitcode/Writer/ValueEnumerator.cpp +++ lib/Bitcode/Writer/ValueEnumerator.cpp @@ -315,9 +315,11 @@ unsigned FirstConstant = Values.size(); // Enumerate the global variable initializers. - for (const GlobalVariable &GV : M.globals()) + for (const GlobalVariable &GV : M.globals()) { if (GV.hasInitializer()) EnumerateValue(GV.getInitializer()); + EnumerateAttributes(GV.getAttributes().getAttributes()); + } // Enumerate the aliasees. for (const GlobalAlias &GA : M.aliases()) Index: lib/IR/AsmWriter.cpp =================================================================== --- lib/IR/AsmWriter.cpp +++ lib/IR/AsmWriter.cpp @@ -804,6 +804,9 @@ if (!Var.hasName()) CreateModuleSlot(&Var); processGlobalObjectMetadata(Var); + auto Attrs = Var.getAttributes(); + if (Attrs.hasAttributes()) + CreateAttributeSetSlot(Attrs.getAttributes().getFnAttributes()); } for (const GlobalAlias &A : TheModule->aliases()) { @@ -2504,6 +2507,10 @@ GV->getAllMetadata(MDs); printMetadataAttachments(MDs, ", "); + auto Attrs = GV->getAttributes(); + if (Attrs.hasAttributes()) + Out << " #" << Machine.getAttributeGroupSlot(Attrs.getAttributes().getFnAttributes()); + printInfoComment(*GV); } Index: lib/IR/Globals.cpp =================================================================== --- lib/IR/Globals.cpp +++ lib/IR/Globals.cpp @@ -338,6 +338,7 @@ if (const GlobalVariable *SrcVar = dyn_cast(Src)) { setThreadLocalMode(SrcVar->getThreadLocalMode()); setExternallyInitialized(SrcVar->isExternallyInitialized()); + setAttributes(SrcVar->getAttributes()); } } Index: test/Assembler/globalvariable-attributes.ll =================================================================== --- /dev/null +++ test/Assembler/globalvariable-attributes.ll @@ -0,0 +1,19 @@ +; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s + +@g1 = global i32 7 "key" = "value" "key2" = "value2" +@g2 = global i32 2, align 4 "key3" = "value3" +@g3 = global i32 2 #0 +@g4 = global i32 2, align 4 "key5" = "value5" #0 + +attributes #0 = { "string" = "value" nobuiltin norecurse } + +; CHECK: @g1 = global i32 7 #0 +; CHECK: @g2 = global i32 2, align 4 #1 +; CHECK: @g3 = global i32 2 #2 +; CHECK: @g4 = global i32 2, align 4 #3 + +; CHECK: attributes #0 = { "key"="value" "key2"="value2" } +; CHECK: attributes #1 = { "key3"="value3" } +; CHECK: attributes #2 = { nobuiltin norecurse "string"="value" } +; CHECK: attributes #3 = { nobuiltin norecurse "key5"="value5" "string"="value" } + Index: test/Bitcode/globalvariable-attributes.ll =================================================================== --- /dev/null +++ test/Bitcode/globalvariable-attributes.ll @@ -0,0 +1,19 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s + +@g1 = global i32 7 "key" = "value" "key2" = "value2" +@g2 = global i32 2, align 4 "key3" = "value3" +@g3 = global i32 2 #0 +@g4 = global i32 2, align 4 "key5" = "value5" #0 + +attributes #0 = { "string" = "value" nobuiltin norecurse } + +; CHECK: @g1 = global i32 7 #0 +; CHECK: @g2 = global i32 2, align 4 #1 +; CHECK: @g3 = global i32 2 #2 +; CHECK: @g4 = global i32 2, align 4 #3 + +; CHECK: attributes #0 = { "key"="value" "key2"="value2" } +; CHECK: attributes #1 = { "key3"="value3" } +; CHECK: attributes #2 = { nobuiltin norecurse "string"="value" } +; CHECK: attributes #3 = { nobuiltin norecurse "key5"="value5" "string"="value" } +