forked from OSchip/llvm-project
[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
This commit is contained in:
parent
a44fc83d9f
commit
f3d7904d20
|
@ -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 <dllstorageclass>` and
|
||||
an optional list of attached :ref:`metadata <metadata>`,
|
||||
Globals can also have a :ref:`DLL storage class <dllstorageclass>`,
|
||||
an optional :ref:`global attributes <glattrs>` and
|
||||
an optional list of attached :ref:`metadata <metadata>`.
|
||||
|
||||
Variables and aliases can have a
|
||||
:ref:`Thread Local Storage Model <tls_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 <fnattrs>`, attributes on a global variable
|
||||
are grouped into a single :ref:`attribute group <attrgrp>`.
|
||||
|
||||
.. _opbundles:
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace llvm {
|
|||
class AttrBuilder;
|
||||
class AttributeImpl;
|
||||
class AttributeListImpl;
|
||||
class AttributeList;
|
||||
class AttributeSetNode;
|
||||
template<typename T> 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
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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 <cassert>
|
||||
|
@ -41,6 +42,7 @@ class DIGlobalVariableExpression;
|
|||
class GlobalVariable : public GlobalObject, public ilist_node<GlobalVariable> {
|
||||
friend class SymbolTableListTraits<GlobalVariable>;
|
||||
|
||||
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<DIGlobalVariableExpression *> &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<unsigned, AttributeSet> 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;
|
||||
|
|
|
@ -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<GlobalVariable>(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<unsigned> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -2750,7 +2750,7 @@ Error BitcodeReader::parseComdatRecord(ArrayRef<uint64_t> Record) {
|
|||
Error BitcodeReader::parseGlobalVarRecord(ArrayRef<uint64_t> 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<uint64_t> Record) {
|
|||
} else if (hasImplicitComdat(RawLinkage)) {
|
||||
NewGV->setComdat(reinterpret_cast<Comdat *>(1));
|
||||
}
|
||||
|
||||
if (Record.size() > 12) {
|
||||
auto AS = getAttributes(Record[12]).getFnAttributes();
|
||||
NewGV->setAttributes(AS);
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -504,6 +504,64 @@ AttributeSet AttributeSet::get(LLVMContext &C, ArrayRef<Attribute> 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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -338,6 +338,7 @@ void GlobalVariable::copyAttributesFrom(const GlobalValue *Src) {
|
|||
if (const GlobalVariable *SrcVar = dyn_cast<GlobalVariable>(Src)) {
|
||||
setThreadLocalMode(SrcVar->getThreadLocalMode());
|
||||
setExternallyInitialized(SrcVar->isExternallyInitialized());
|
||||
setAttributes(SrcVar->getAttributes());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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" }
|
||||
|
|
@ -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" }
|
||||
|
Loading…
Reference in New Issue