[llvm-dev] [RFC] Attribute overhaul 2
Reid Kleckner via llvm-dev
llvm-dev at lists.llvm.org
Mon Mar 20 09:48:32 PDT 2017
LLVM's Attribute APIs need an overhaul.
First, testing for an attribute on an Argument is slow.
llvm::AttributeSet::getAttributes(int) consumed 2% of cycles while
llc during LTO. Our mid-level optimizations are constantly asking if a given
argument has some attribute (nonnull, dereferencable, etc), and this is
currently linear in the size of the function prototype. This should be
Adding and removing individual attributes is also inefficient. Every single
attribute addition or removal convenience method, for example, inserts a new
AttributeSet into a FoldingSet. AttributeFuncs::mergeAttributesForInlining
written in terms of these APIs, and is therefore quite inefficient. This
shows up during LTO. I don't think it's practical to remove these inherently
expensive APIs, but if we make AttributeBuilder easier to use, it will be a
easier to fix these problems as they come up.
Lastly, the Attribute APIs are hard to use because they do too much
hiding. In particular, the choice to make AttributeSetNode an internal
implementation detail of lib/IR is problematic. This type describes all of
attributes on an individual argument, return value, or function, which IPO
transforms often want. Today the getFnAttributes, getRetAttributes, and
getParamAttributes APIs find the relevant AttributeSetNode* and wrap it in a
new, uniqued AttributeListImpl. This requires callers to keep around the
of the extracted attributes so they can look through the wrapper list. If we
make AttributeSetNode public, we can simplify a lot of IPO transform code.
The naming of today's APIs is confusing. I'll try to explain what the
important classes are.
- AttributeSet: This is a uniqued, ordered list of sets of attributes, and
associated with a function or call prototype. It is stored on Function,
CallInst, and InvokeInst. It is a smart pointer value type that wraps
AttributeSetImpl, which contains the actual storage.
- AttributeSetImpl: The private implementation of AttributeSet. Owned by the
LLVMContext. Today this is a vector of pairs of attribute indices and
- AttributeSetNode: This is an ordered, uniqued set of Attributes that might
apply to a single function, callee, return value, parameter, or
uses TrailingObjects to store the attributes, and until Jan 2016, tested
attribute presence by a linear scan. Matthias Braum added a bitset to
tests in r259251.
- AttributeBuilder: A mutable representation of an AttributeSetNode. Used
efficiently building a collection of attributes before freezing it into an
- Attribute: Pointer wrapping an AttributeImpl.
- AttributeImpl: Polymorphic base class of StringAttributeImpl,
EnumAttributeImpl, and IntAttributeImpl. Enums have the attribute kind,
integers have a uint64_t value, and strings have two StringRefs for the
AttributeSet doesn't seem like a good name to me. In the past it was called
AttrListPtr and PAListPtr. Today's AttributeSetImpl was called
"ParameterAttributeList", which is why we have "PAL" local variables. I'd
to rename AttributeSet to just "AttributeList". It's a list of sets of
attributes that is parallel to some function prototype. I already have a
out for this here: https://reviews.llvm.org/D31102
The natural second thing to do is to rename AttributeSetNode to
create a pointer wrapper type called AttributeSet and rename today's
AttributeSetNode to AttributeSetImpl. It is inherently an ordered set of
attributes. I also propose to make this type public, as described earlier.
Testing for presence of an attribute on a parameter should be fast. Today
linear in the size of the prototype. I propose that we change AttributeList
make attributes randomly accessible by slot index, so that it stores a
object array of AttributeSetNode*, or some equivalent type that makes it
efficient to test for attribute presence. I'll do some benchmarking to show
this change doesn't measurably impact LTO memory usage.
I also want to reduce the number of loads needed to test a single attribute.
One potential optimization is to move the bitset of enum attributes up out
AttributeSetNode* into a wrapper type, perhaps called AttributeSet. The
type could be a bitset and pointer pair, or we could try to union those
in the usual way to save memory. I suspect the memory savings of the union
unimportant, but I will measure. If we can always store the enumerated
attributes that have no associated size locally, that seems ideal, since it
allows modifying them without copying the immutable data stored in the LLVM
Another idea is to eliminate the use of AttributeList completely from
and instead store the attributes of an Argument directly *in* the Argument.
function and return attributes would live on the Function. At the end of the
day, `Arg->hasNonNullAttr()` should compile to a load and a bittest. This
more invasive change that would require more API migration, but again it
avoid more FoldingSet usage.
The last two optimizations taken together are particularly nice, because it
means that changing enum attributes on functions and arguments won't trigger
copies of immutable objects. It will only involve flipping bits in a locally
Separately, I want to collapse dereferenceable(N), nonnull, and
dereferenceable_or_null(N) for efficiency and orthogonality. I will probably
raise this in a separate RFC, but I think it was a mistake to have to
imply nonnull. I'm mostly looking at this from an efficiency and redundancy
perspective, though, not a semantic one.
Does this all seem reasonable? Speak up now if you think any of this looks
a step in the wrong direction.
RFC Overhauling Attributes, Bill Wending Sept 2012
dereferenceable vs dereferenceable_or_null split
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the llvm-dev