From f3d7904d20120fb6e4b33cd391b916ca6701e03e Mon Sep 17 00:00:00 2001 From: Javed Absar Date: Thu, 11 May 2017 12:28:08 +0000 Subject: [PATCH] [IR] Allow attributes with global variables This patch extends llvm-ir to allow attributes to be set on global variables. An RFC was sent out earlier by my colleague James Molloy: http://lists.llvm.org/pipermail/cfe-dev/2017-March/053100.html A key part of that proposal was to extend LLVM-IR to carry attributes on global variables. This generic feature could be useful for multiple purposes. In our present context, it would be useful to carry user specified sections for bss/rodata/data. Reviewed by: Jonathan Roelofs, Reid Kleckner Differential Revision: https://reviews.llvm.org/D32009 llvm-svn: 302794 --- llvm/docs/LangRef.rst | 13 ++++- llvm/include/llvm/IR/Attributes.h | 41 +++++++++++++++ llvm/include/llvm/IR/GlobalVariable.h | 57 ++++++++++++++++++++ llvm/lib/AsmParser/LLParser.cpp | 18 ++++++- llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 7 ++- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 8 ++- llvm/lib/Bitcode/Writer/ValueEnumerator.cpp | 7 ++- llvm/lib/IR/AsmWriter.cpp | 7 +++ llvm/lib/IR/Attributes.cpp | 66 ++++++++++++++++++++++++ llvm/lib/IR/Globals.cpp | 1 + llvm/test/Assembler/globalvariable-attributes.ll | 19 +++++++ llvm/test/Bitcode/globalvariable-attributes.ll | 19 +++++++ 12 files changed, 254 insertions(+), 9 deletions(-) create mode 100644 llvm/test/Assembler/globalvariable-attributes.ll create mode 100644 llvm/test/Bitcode/globalvariable-attributes.ll diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index dc6350c..e7c2888 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -641,8 +641,9 @@ assume that the globals are densely packed in their section and try to 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 `. @@ -1624,6 +1625,14 @@ example: 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: diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h index cbe6816..d4a896c 100644 --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -35,6 +35,7 @@ namespace llvm { class AttrBuilder; class AttributeImpl; class AttributeListImpl; +class AttributeList; class AttributeSetNode; template struct DenseMapInfo; class Function; @@ -227,14 +228,51 @@ public: bool operator==(const AttributeSet &O) { return SetNode == O.SetNode; } bool operator!=(const AttributeSet &O) { return !(*this == O); } + /// Add an argument attribute. Because + /// attribute sets are immutable, this returns a new set. + AttributeSet addAttribute(LLVMContext &C, + Attribute::AttrKind Kind) const; + + /// Add a target-dependent attribute. Because + /// attribute sets are immutable, this returns a new set. + AttributeSet addAttribute(LLVMContext &C, StringRef Kind, + StringRef Value = StringRef()) const; + + /// Add attributes to the attribute set. Because + /// attribute sets are immutable, this returns a new set. + AttributeSet addAttributes(LLVMContext &C, AttributeSet AS) const; + + /// Remove the specified attribute from this set. Because + /// attribute sets are immutable, this returns a new set. + AttributeSet removeAttribute(LLVMContext &C, + Attribute::AttrKind Kind) const; + + /// Remove the specified attribute from this set. Because + /// attribute sets are immutable, this returns a new set. + AttributeSet removeAttribute(LLVMContext &C, + StringRef Kind) const; + + /// Remove the specified attributes from this set. Because + /// attribute sets are immutable, this returns a new set. + AttributeSet removeAttributes(LLVMContext &C, + const AttrBuilder &AttrsToRemove) const; + + /// Return the number of attributes in this set. unsigned getNumAttributes() const; + /// Return true if attributes exists in this set. bool hasAttributes() const { return SetNode != nullptr; } + /// Return true if the attribute exists in this set. bool hasAttribute(Attribute::AttrKind Kind) const; + + /// Return true if the attribute exists in this set. bool hasAttribute(StringRef Kind) const; + /// Return the attribute object. Attribute getAttribute(Attribute::AttrKind Kind) const; + + /// Return the target-dependent attribute object. Attribute getAttribute(StringRef Kind) const; unsigned getAlignment() const; @@ -248,6 +286,9 @@ public: iterator begin() const; iterator end() const; +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const; +#endif }; //===----------------------------------------------------------------------===// diff --git a/llvm/include/llvm/IR/GlobalVariable.h b/llvm/include/llvm/IR/GlobalVariable.h index 3b545d8..c25e080 100644 --- a/llvm/include/llvm/IR/GlobalVariable.h +++ b/llvm/include/llvm/IR/GlobalVariable.h @@ -24,6 +24,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/ADT/ilist_node.h" #include "llvm/IR/GlobalObject.h" +#include "llvm/IR/Attributes.h" #include "llvm/IR/OperandTraits.h" #include "llvm/IR/Value.h" #include @@ -41,6 +42,7 @@ class DIGlobalVariableExpression; class GlobalVariable : public GlobalObject, public ilist_node { friend class SymbolTableListTraits; + AttributeSet Attrs; bool isConstantGlobal : 1; // Is this a global constant? bool isExternallyInitializedConstant : 1; // Is this a global whose value // can change from its initial @@ -178,6 +180,61 @@ public: /// Fill the vector with all debug info attachements. void getDebugInfo(SmallVectorImpl &GVs) const; + /// Add attribute to this global. + void addAttribute(Attribute::AttrKind Kind) { + Attrs = Attrs.addAttribute(getContext(), Kind); + } + + /// Add attribute to this global. + void addAttribute(StringRef Kind, StringRef Val = StringRef()) { + Attrs = Attrs.addAttribute(getContext(), Kind, Val); + } + + /// Return true if the attribute exists. + bool hasAttribute(Attribute::AttrKind Kind) const { + return Attrs.hasAttribute(Kind); + } + + /// Return true if the attribute exists. + bool hasAttribute(StringRef Kind) const { + return Attrs.hasAttribute(Kind); + } + + /// Return true if any attributes exist. + bool hasAttributes() const { + return Attrs.hasAttributes(); + } + + /// Return the attribute object. + Attribute getAttribute(Attribute::AttrKind Kind) const { + return Attrs.getAttribute(Kind); + } + + /// Return the attribute object. + Attribute getAttribute(StringRef Kind) const { + return Attrs.getAttribute(Kind); + } + + /// Return the attribute set for this global + AttributeSet getAttributes() const { + return Attrs; + } + + /// Return attribute set as list with index. + /// FIXME: This may not be required once ValueEnumerators + /// in bitcode-writer can enumerate attribute-set. + AttributeList getAttributesAsList(unsigned index) const { + if (!hasAttributes()) + return AttributeList(); + std::pair AS[1] = {{index, Attrs}}; + return AttributeList::get(getContext(), AS); + } + + /// Set attribute list for this global + void setAttributes(AttributeSet 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; diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 97a5675..d7602c8 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -162,6 +162,10 @@ bool LLParser::ValidateEndOfModule() { AS = AS.addAttributes(Context, AttributeList::FunctionIndex, AttributeSet::get(Context, FnAttrs)); II->setAttributes(AS); + } else if (auto *GV = dyn_cast(V)) { + AttrBuilder Attrs(GV->getAttributes()); + Attrs.merge(B); + GV->setAttributes(AttributeSet::get(Context,Attrs)); } else { llvm_unreachable("invalid object with forward attribute group reference"); } @@ -832,10 +836,10 @@ bool LLParser::parseIndirectSymbol( /// 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. @@ -950,6 +954,16 @@ bool LLParser::ParseGlobal(const std::string &Name, LocTy NameLoc, } } + AttrBuilder Attrs; + LocTy BuiltinLoc; + std::vector FwdRefAttrGrps; + if (ParseFnAttributeValuePairs(Attrs, FwdRefAttrGrps, false, BuiltinLoc)) + return true; + if (Attrs.hasAttributes() || !FwdRefAttrGrps.empty()) { + GV->setAttributes(AttributeSet::get(Context, Attrs)); + ForwardRefAttrGroups[GV] = FwdRefAttrGrps; + } + return false; } diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 580261a..900d718 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -2750,7 +2750,7 @@ Error BitcodeReader::parseComdatRecord(ArrayRef Record) { Error BitcodeReader::parseGlobalVarRecord(ArrayRef Record) { // v1: [pointer type, isconst, initid, linkage, alignment, section, // visibility, threadlocal, unnamed_addr, externally_initialized, - // dllstorageclass, comdat] (name in VST) + // dllstorageclass, comdat, attributes] (name in VST) // v2: [strtab_offset, strtab_size, v1] StringRef Name; std::tie(Name, Record) = readNameFromStrtab(Record); @@ -2830,6 +2830,11 @@ Error BitcodeReader::parseGlobalVarRecord(ArrayRef Record) { } else if (hasImplicitComdat(RawLinkage)) { NewGV->setComdat(reinterpret_cast(1)); } + + if (Record.size() > 12) { + auto AS = getAttributes(Record[12]).getFnAttributes(); + NewGV->setAttributes(AS); + } return Error::success(); } diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index 1b8d81a..1f8b503 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1109,7 +1109,7 @@ void ModuleBitcodeWriter::writeModuleInfo() { // GLOBALVAR: [strtab offset, strtab size, type, isconst, initid, // linkage, alignment, section, visibility, threadlocal, // unnamed_addr, externally_initialized, dllstorageclass, - // comdat] + // comdat, attributes] Vals.push_back(StrtabBuilder.add(GV.getName())); Vals.push_back(GV.getName().size()); Vals.push_back(VE.getTypeID(GV.getValueType())); @@ -1124,13 +1124,17 @@ void ModuleBitcodeWriter::writeModuleInfo() { GV.getUnnamedAddr() != GlobalValue::UnnamedAddr::None || GV.isExternallyInitialized() || GV.getDLLStorageClass() != GlobalValue::DefaultStorageClass || - GV.hasComdat()) { + GV.hasComdat() || + GV.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); + + auto AL = GV.getAttributesAsList(AttributeList::FunctionIndex); + Vals.push_back(VE.getAttributeListID(AL)); } else { AbbrevToUse = SimpleGVarAbbrev; } diff --git a/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp b/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp index 8611507..fd76400 100644 --- a/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp +++ b/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp @@ -314,10 +314,13 @@ ValueEnumerator::ValueEnumerator(const Module &M, // Remember what is the cutoff between globalvalue's and other constants. unsigned FirstConstant = Values.size(); - // Enumerate the global variable initializers. - for (const GlobalVariable &GV : M.globals()) + // Enumerate the global variable initializers and attributes. + for (const GlobalVariable &GV : M.globals()) { if (GV.hasInitializer()) EnumerateValue(GV.getInitializer()); + if (GV.hasAttributes()) + EnumerateAttributes(GV.getAttributesAsList(AttributeList::FunctionIndex)); + } // Enumerate the aliasees. for (const GlobalAlias &GA : M.aliases()) diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index 4c6e3e3..ec46630 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -805,6 +805,9 @@ void SlotTracker::processModule() { if (!Var.hasName()) CreateModuleSlot(&Var); processGlobalObjectMetadata(Var); + auto Attrs = Var.getAttributes(); + if (Attrs.hasAttributes()) + CreateAttributeSetSlot(Attrs); } for (const GlobalAlias &A : TheModule->aliases()) { @@ -2502,6 +2505,10 @@ void AssemblyWriter::printGlobal(const GlobalVariable *GV) { GV->getAllMetadata(MDs); printMetadataAttachments(MDs, ", "); + auto Attrs = GV->getAttributes(); + if (Attrs.hasAttributes()) + Out << " #" << Machine.getAttributeGroupSlot(Attrs); + printInfoComment(*GV); } diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index 3b1140a..b97afb6 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -504,6 +504,64 @@ AttributeSet AttributeSet::get(LLVMContext &C, ArrayRef Attrs) { return AttributeSet(AttributeSetNode::get(C, Attrs)); } +AttributeSet AttributeSet::addAttribute(LLVMContext &C, + Attribute::AttrKind Kind) const { + if (hasAttribute(Kind)) return *this; + AttrBuilder B; + B.addAttribute(Kind); + return addAttributes(C, AttributeSet::get(C, B)); +} + +AttributeSet AttributeSet::addAttribute(LLVMContext &C, StringRef Kind, + StringRef Value) const { + AttrBuilder B; + B.addAttribute(Kind, Value); + return addAttributes(C, AttributeSet::get(C, B)); +} + +AttributeSet AttributeSet::addAttributes(LLVMContext &C, + const AttributeSet AS) const { + if (!hasAttributes()) + return AS; + + if (!AS.hasAttributes()) + return *this; + + AttrBuilder B(AS); + for (Attribute I : *this) + B.addAttribute(I); + + return get(C, B); +} + +AttributeSet AttributeSet::removeAttribute(LLVMContext &C, + Attribute::AttrKind Kind) const { + if (!hasAttribute(Kind)) return *this; + AttrBuilder B; + B.addAttribute(Kind); + return removeAttributes(C, B); +} + +AttributeSet AttributeSet::removeAttribute(LLVMContext &C, + StringRef Kind) const { + if (!hasAttribute(Kind)) return *this; + AttrBuilder B; + B.addAttribute(Kind); + return removeAttributes(C, B); +} + +AttributeSet AttributeSet::removeAttributes(LLVMContext &C, + const AttrBuilder &Attrs) const { + + // FIXME it is not obvious how this should work for alignment. + // For now, say we can't pass in alignment, which no current use does. + assert(!Attrs.hasAlignmentAttr() && "Attempt to change alignment!"); + + AttrBuilder B(*this); + B.remove(Attrs); + return get(C, B); +} + unsigned AttributeSet::getNumAttributes() const { return SetNode ? SetNode->getNumAttributes() : 0; } @@ -557,6 +615,14 @@ AttributeSet::iterator AttributeSet::end() const { return SetNode ? SetNode->end() : nullptr; } +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void AttributeSet::dump() const { + dbgs() << "AS =\n"; + dbgs() << " { "; + dbgs() << getAsString(true) << " }\n"; +} +#endif + //===----------------------------------------------------------------------===// // AttributeSetNode Definition //===----------------------------------------------------------------------===// diff --git a/llvm/lib/IR/Globals.cpp b/llvm/lib/IR/Globals.cpp index 5f338f5..5fbf51e 100644 --- a/llvm/lib/IR/Globals.cpp +++ b/llvm/lib/IR/Globals.cpp @@ -338,6 +338,7 @@ void GlobalVariable::copyAttributesFrom(const GlobalValue *Src) { if (const GlobalVariable *SrcVar = dyn_cast(Src)) { setThreadLocalMode(SrcVar->getThreadLocalMode()); setExternallyInitialized(SrcVar->isExternallyInitialized()); + setAttributes(SrcVar->getAttributes()); } } diff --git a/llvm/test/Assembler/globalvariable-attributes.ll b/llvm/test/Assembler/globalvariable-attributes.ll new file mode 100644 index 0000000..64227a4 --- /dev/null +++ b/llvm/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" } + diff --git a/llvm/test/Bitcode/globalvariable-attributes.ll b/llvm/test/Bitcode/globalvariable-attributes.ll new file mode 100644 index 0000000..cbab3b7 --- /dev/null +++ b/llvm/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" } + -- 2.7.4