-
Notifications
You must be signed in to change notification settings - Fork 10.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the 'initializes' attribute langref and support #84803
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-llvm-adt @llvm/pr-subscribers-llvm-ir Author: Haopeng Liu (haopliu) ChangesWe propose adding a new LLVM attribute, Will commit the attribute inferring in the follow-up PRs. Full diff: https://github.com/llvm/llvm-project/pull/84803.diff 1 Files Affected:
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index b70220dec92615..39a555edfa80d6 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1621,6 +1621,28 @@ Currently, only the following parameter attributes are defined:
``readonly`` or a ``memory`` attribute that does not contain
``argmem: write``.
+``initialized((Lo1,Hi1),...)``
+ This attribute is a list of const ranges in ascending order with no
+ overlapping or continuous. It indicates that the function initializes the
+ memory through the pointer argument, [%p+LoN, %p+HiN): there are no reads,
+ and no special accesses (such as volatile access or untrackable capture)
+ before the initialization in the function. LoN/HiN are 64-bit ints;
+ negative values are allowed in case a pointer to partway through the
+ allocation is passed to.
+
+ This attribute implies that the function initializes and does not read
+ before initialization through this pointer argument, even though it may
+ read the memory before initialization that the pointer points to, such
+ as through other arguments.
+
+ The ``writable`` or ``dereferenceable`` attribute does not imply
+ ``initialized`` attribute, and ``initialized`` does not imply ``writeonly``
+ since cases that read from the pointer after write, can be ``initialized``
+ but not ``writeonly``.
+
+ Note that this attribute does not apply to the unwind edge: the memory may
+ not actually be written to when unwinding happens.
+
``dead_on_unwind``
At a high level, this attribute indicates that the pointer argument is dead
if the call unwinds, in the sense that the caller will not depend on the
|
llvm/docs/LangRef.rst
Outdated
@@ -1621,6 +1621,28 @@ Currently, only the following parameter attributes are defined: | |||
``readonly`` or a ``memory`` attribute that does not contain | |||
``argmem: write``. | |||
|
|||
``initialized((Lo1,Hi1),...)`` | |||
This attribute is a list of const ranges in ascending order with no | |||
overlapping or continuous. It indicates that the function initializes the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "with no overlapping or adjoining list elements"? or something like that (felt like it was missing a word at the end at least)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should start this off with a one sentence overview of the attribute. So starting with something like "This indicates that the function initializes the ranges of the pointer parameter's memory." Then describe what "initialize" means. Then the random details like non-overlapping/continuous ranges at the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
llvm/docs/LangRef.rst
Outdated
and no special accesses (such as volatile access or untrackable capture) | ||
before the initialization in the function. LoN/HiN are 64-bit ints; | ||
negative values are allowed in case a pointer to partway through the | ||
allocation is passed to. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "in case the argument points partway into an allocation." ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
llvm/docs/LangRef.rst
Outdated
as through other arguments. | ||
|
||
The ``writable`` or ``dereferenceable`` attribute does not imply | ||
``initialized`` attribute, and ``initialized`` does not imply ``writeonly`` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "does not imply [the] initialized
attribute, and ... writeonly
, since initialized
allows reading from the pointer after writing." ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
we should combine this PR with the PR that adds support for this in LLVM, or else it's weird if we're documenting something that LLVM doesn't support yet |
llvm/docs/LangRef.rst
Outdated
since cases that read from the pointer after write, can be ``initialized`` | ||
but not ``writeonly``. | ||
|
||
Note that this attribute does not apply to the unwind edge: the memory may |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as mentioned in the RFC, we should be more accurate here. the part of the attribute where the memory is read from before a write still must apply on the unwind edge. Also, I'd move this up since this is important to the semantics
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, I meant that the memory is not read from before a write
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
unintentional add of FunctionAttrs.cpp change?
specifically just the attribute support (e.g. bitcode) not any of the inference or usage of it |
llvm/docs/LangRef.rst
Outdated
special accesses (such as volatile access or untrackable capture) before | ||
the initialization write in the function. | ||
|
||
This attribute implies that the function initializes and does not read |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't use implies
.
`This attribute only holds for the memory accessed via this pointer parameter. Other arbitrary accesses to the same memory via other pointers are allowed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
llvm/docs/LangRef.rst
Outdated
written to when unwinding happens. | ||
|
||
The ``writable`` or ``dereferenceable`` attribute does not imply the | ||
``initialized`` attribute, and ``initialized`` does not imply ``writeonly`` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd separate this into two sentences.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
llvm/docs/LangRef.rst
Outdated
``initialized`` attribute, and ``initialized`` does not imply ``writeonly`` | ||
since ``initialized`` allows reading from the pointer after writing. | ||
|
||
This attribute is a list of const ranges in ascending order with no |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/const/constant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, done!
llvm/docs/LangRef.rst
Outdated
since ``initialized`` allows reading from the pointer after writing. | ||
|
||
This attribute is a list of const ranges in ascending order with no | ||
overlapping or adjoining list elements. LoN/HiN are 64-bit ints, and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't heard "adjoining" used in this context, I think "consecutive" is more commonly used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
llvm/docs/LangRef.rst
Outdated
since ``initialized`` allows reading from the pointer after writing. | ||
|
||
This attribute is a list of const ranges in ascending order with no | ||
overlapping or adjoining list elements. LoN/HiN are 64-bit ints, and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
put LoN
/HiN
in double backticks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
As a heads up, the inference implementation for this (or at least the available prototype) does not look viable from a compile-time perspective: https://llvm-compile-time-tracker.com/compare.php?from=69b09b43b0a2057918078edb401adab888d1014b&to=0bd68ae2d56783377acc0aa5d7958b47411b8342&stat=instructions:u |
Thanks for this heads up! This PR only adds the attribute support. Will further tune the inferring performance and post the updated compile time tracker in the inference PR. |
Thank you all! Update the LangRef, and add the attribute support. Please take another look :-D |
llvm/include/llvm/IR/Attributes.td
Outdated
@@ -38,6 +38,9 @@ class IntAttr<string S, list<AttrProperty> P> : Attr<S, P>; | |||
/// Type attribute. | |||
class TypeAttr<string S, list<AttrProperty> P> : Attr<S, P>; | |||
|
|||
/// Const range list attribute. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: put closer to the ConstantRangeAttr?
llvm/include/llvm/IR/Attributes.td
Outdated
@@ -318,6 +321,9 @@ def Writable : EnumAttr<"writable", [ParamAttr]>; | |||
/// Function only writes to memory. | |||
def WriteOnly : EnumAttr<"writeonly", [ParamAttr]>; | |||
|
|||
/// Pointer argument memory [%p+LoN, %p+HiN) is initialized. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The list looks almost sorted, so may be better to put it near InAlloca or ?
@@ -307,6 +308,10 @@ namespace llvm { | |||
bool AllowParens = false); | |||
bool parseOptionalCodeModel(CodeModel::Model &model); | |||
bool parseOptionalDerefAttrBytes(lltok::Kind AttrKind, uint64_t &Bytes); | |||
bool parseConstRange(std::pair<int64_t, int64_t> &Range); | |||
bool parseInitializedRanges( | |||
lltok::Kind AttrKind, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can the AttrKind be hardcoded within the function instead of taking as parameter? (unlike DerefAttrBytes, there is only one variation of the attribute, I believe?)
llvm/include/llvm/IR/Attributes.h
Outdated
@@ -94,6 +94,8 @@ class Attribute { | |||
|
|||
static const unsigned NumIntAttrKinds = LastIntAttr - FirstIntAttr + 1; | |||
static const unsigned NumTypeAttrKinds = LastTypeAttr - FirstTypeAttr + 1; | |||
static const unsigned NumConstRangeListAttrKinds = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this used by anything (if not, can remove)? I don't see the ConstantRangeAttrKind version of it, and hard to see "NumTypeAttrKinds" used
llvm/include/llvm/IR/Attributes.h
Outdated
@@ -186,6 +193,9 @@ class Attribute { | |||
/// Return true if the attribute is a type attribute. | |||
bool isTypeAttribute() const; | |||
|
|||
/// Return true if the attribute is a const range list attribute. | |||
bool isConstRangeListAttribute() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: wonder if should s/Const/Constant/ to be consistent with isConstantRangeAttribute, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, no need to abbreviate
llvm/include/llvm/ADT/FoldingSet.h
Outdated
@@ -362,6 +362,21 @@ class FoldingSetNodeID { | |||
} | |||
} | |||
|
|||
void AddRanges(const SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to use ArrayRef or something to not need specific SmallVector inline size of 16 here? "Prefer to use ArrayRef or SmallVectorImpl as a parameter type." under https://llvm.org/docs/ProgrammersManual.html#llvm-adt-smallvector-h
llvm/include/llvm/IR/Attributes.h
Outdated
@@ -226,6 +236,10 @@ class Attribute { | |||
/// attribute to be a ConstantRange attribute. | |||
ConstantRange getValueAsConstantRange() const; | |||
|
|||
/// Return the attribute's value as a const range list. This requires the | |||
/// attribute to be a const range list attribute. | |||
SmallVector<std::pair<int64_t, int64_t>, 16> getValueAsRanges() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to return an ArrayRef instead of a copy? Will the underlying storage lifetime work out?
llvm/lib/AsmParser/LLParser.cpp
Outdated
if (parseInt64(Range.first)) | ||
return true; | ||
|
||
if (EatIfPresent(lltok::comma)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should it be an error to be missing the comma?
@@ -930,11 +932,24 @@ void ModuleBitcodeWriter::writeAttributeGroupTable() { | |||
Record.push_back(getAttrKindEncoding(Attr.getKindAsEnum())); | |||
if (Ty) | |||
Record.push_back(VE.getTypeID(Attr.getValueAsType())); | |||
} else { | |||
} else if (Attr.isConstantRangeAttribute()) { | |||
assert(Attr.isConstantRangeAttribute()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can remove the assert?
llvm/lib/IR/Verifier.cpp
Outdated
Check(!Inits.empty(), "Attribute 'initialized' does not support empty list", | ||
V); | ||
|
||
for (size_t i = 1; i < Inits.size(); i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps check low < high for the same "i"?
llvm/include/llvm/IR/Attributes.h
Outdated
@@ -226,6 +236,10 @@ class Attribute { | |||
/// attribute to be a ConstantRange attribute. | |||
ConstantRange getValueAsConstantRange() const; | |||
|
|||
/// Return the attribute's value as a const range list. This requires the | |||
/// attribute to be a const range list attribute. | |||
SmallVector<std::pair<int64_t, int64_t>, 16> getValueAsRanges() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should use ConstantRange
instead of std::pair
, especially since it already has intersection/union methods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, 16 elements for the small vector optimization seems very high, I feel the vast majority of cases will be at most 4. I'd choose 4 (or even 2)
llvm/lib/IR/Attributes.cpp
Outdated
@@ -616,6 +652,23 @@ std::string Attribute::getAsString(bool InAttrGrp) const { | |||
return Result; | |||
} | |||
|
|||
if (hasAttribute(Attribute::Initialized)) { | |||
auto Ranges = getValueAsRanges(); | |||
if (Ranges.empty()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this allowed? I think we should forbid an empty list
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh I see the verifier check below, so I don't think we need this check
llvm/lib/IR/Verifier.cpp
Outdated
@@ -1993,6 +1993,14 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty, | |||
Attrs.hasAttribute(Attribute::ReadOnly)), | |||
"Attributes writable and readonly are incompatible!", V); | |||
|
|||
Check(!(Attrs.hasAttribute(Attribute::Initialized) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is true. A function that immediately unwinds can be marked as initializing all arguments according to our semantics, but is also readnone
llvm/include/llvm/IR/Attributes.h
Outdated
@@ -186,6 +193,9 @@ class Attribute { | |||
/// Return true if the attribute is a type attribute. | |||
bool isTypeAttribute() const; | |||
|
|||
/// Return true if the attribute is a const range list attribute. | |||
bool isConstRangeListAttribute() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, no need to abbreviate
llvm/docs/LangRef.rst
Outdated
parameter. Other arbitrary accesses to the same memory via other pointers | ||
are allowed. | ||
|
||
Note that this attribute does not apply to the unwind edge: the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is now redundant with the unwind comment above
The main thing I would be concerned about at this point is whether the entire approach is viable or not -- I'd rather not add the attribute (plus a whole new attribute kind) if it later turns out that this is too expensive. Anyway, a couple of high level comments:
|
+1
Makes sense,
Yes the concern was padding holes. @haopliu do you have any data on how often padding prevents whole object initialization? |
Even if we represent padding more precisely with multiple ranges in I think |
godbolt seems to not show metadata (I'm not seeing it on a |
actually I'm unsure if it would be legal to remove stores on padding bytes |
Changed the attribute implementation to Please take another look. Thanks! |
@@ -1565,6 +1566,8 @@ class LLVMContextImpl { | |||
UniqueStringSaver Saver{Alloc}; | |||
SpecificBumpPtrAllocator<ConstantRangeAttributeImpl> | |||
ConstantRangeAttributeAlloc; | |||
SpecificBumpPtrAllocator<ConstantRangeListAttributeImpl> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems this isn't used anymore? Just using Alloc now?
Then the forward declaration of ConstantRangeListAttributeImpl above isn't needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, done!
llvm/lib/IR/AttributeImpl.h
Outdated
private TrailingObjects<ConstantRangeListAttributeImpl, ConstantRange> { | ||
friend TrailingObjects; | ||
|
||
unsigned ValSize; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe just "Size" since there isn't a field named "Val"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
llvm/lib/IR/ConstantRangeList.cpp
Outdated
Ranges.insert(Ranges.begin(), NewRange); | ||
return; | ||
} | ||
if (std::find(Ranges.begin(), Ranges.end(), NewRange) != Ranges.end()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what Arthur meant (correct me if I'm wrong), is remove this std::find. The std::lower_bound below already does a "find" in the list.
Then check if LowerBound == NewRange. If so, then we've found a duplicate and can early return the way that this std::find() had previously done. I think that's what he meant by "check just one element" (the LowerBound element).
So then, also maybe you don't need the special case size() == 1 with overlap (not sure that's common).
Here is an updated compile time regression of the entire stack of patches: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm with one last nit
I'd like some other approvals before landing this
for the followup patches, the inference patch generally lgtm (since I wrote a lot of it :) ), and the DSE patch in principle makes sense (the important thing is that it allows an instruction to be associated with multiple MemoryDefs), although I still need to understand it better. 0.07% compile time regression for interprocedural DSE seems acceptable IMO
} | ||
} | ||
|
||
void ConstantRangeList::print(raw_ostream &OS) const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't really matter, they're the same
llvm/lib/IR/ConstantRangeList.cpp
Outdated
|
||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | ||
LLVM_DUMP_METHOD void ConstantRangeList::dump() const { | ||
print(llvm::dbgs()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove llvm::
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work reducing the compile time from the initial version =)
@@ -267,6 +281,9 @@ class Attribute { | |||
/// Returns the value of the range attribute. | |||
ConstantRange getRange() const; | |||
|
|||
/// Returns the value of the initializes attribute. | |||
ConstantRangeList getInitializes() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Perhaps return ArrayRef to be consistent with getValueAsConstantRangeList(), then the caller can decide to make it a ConstantRangeList or not?
Also ArrayRef allows the caller to avoid a copy if it isn't needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
llvm/docs/LangRef.rst
Outdated
parameter. Other arbitrary accesses to the same memory via other pointers | ||
are allowed. | ||
|
||
The ``writable`` or ``dereferenceable`` attribute does not imply the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ``writable`` or ``dereferenceable`` attribute does not imply the | |
The ``writable`` or ``dereferenceable`` attribute do not imply the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
llvm/docs/LangRef.rst
Outdated
are allowed. | ||
|
||
The ``writable`` or ``dereferenceable`` attribute does not imply the | ||
``initializes`` attribute. The ``initializes`` does not imply ``writeonly`` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
``initializes`` attribute. The ``initializes`` does not imply ``writeonly`` | |
``initializes`` attribute. The ``initializes`` attribute does not imply ``writeonly`` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
llvm/docs/LangRef.rst
Outdated
since ``initializes`` allows reading from the pointer after writing. | ||
|
||
This attribute is a list of constant ranges in ascending order with no | ||
overlapping or consecutive list elements. ``LoN/HiN`` are 64-bit ints, and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overlapping or consecutive list elements. ``LoN/HiN`` are 64-bit ints, and | |
overlapping or consecutive list elements. ``LoN/HiN`` are 64-bit integers, and |
Though I wonder whether the integers shouldn't match the pointer index type rather than being hardcoded to 64-bits...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
@@ -0,0 +1,86 @@ | |||
//===- ConstantRangeList.h - A list of constant range -----------*- C++ -*-===// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//===- ConstantRangeList.h - A list of constant range -----------*- C++ -*-===// | |
//===- ConstantRangeList.h - A list of constant ranges -----------*- C++ -*-===// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
//===----------------------------------------------------------------------===// | ||
// | ||
// Represent a list of signed ConstantRange and do NOT support wrap around the | ||
// end of the numeric range. Ranges in the list are ordered and no overlapping. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// end of the numeric range. Ranges in the list are ordered and no overlapping. | |
// end of the numeric range. Ranges in the list are ordered and not overlapping. |
or "non-overlapping".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
|
||
SmallVector<ConstantRange, 2> Val; | ||
int RangeSize = Record[++i]; | ||
assert(i + 2 * RangeSize < e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can't be an assert, it should be an if with error return.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, changed to an if with error return!
Record.push_back(Val.size()); | ||
for (auto &CR : Val) { | ||
emitSignedInt64(Record, CR.getLower().getSExtValue()); | ||
emitSignedInt64(Record, CR.getUpper().getSExtValue()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This hardcodes the 64-bit ranges in the bitcode format. We shouldn't do that to allow future uses of this attribute type with different width. This should use emitConstantRange() instead, maybe after a small refactoring to allow emitting the bit width separately (as we don't need to emit it for each range...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I wasn't imagining that a future attribute might want to use ConstantRangeList, but that does seem like something we should support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to use emitConstantRange()
.
ConstantRangeListAttributeImpl::totalSizeToAlloc(Val), | ||
alignof(ConstantRangeListAttributeImpl)); | ||
PA = new (Mem) ConstantRangeListAttributeImpl(Kind, Val); | ||
pImpl->AttrsSet.InsertNode(PA, InsertPoint); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this ends up working fine right now where the ConstantRanges have <= 64 bits, but in general I think this may result in a memory leak due to the dtor not running.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How to call the dtor explicitly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm I'm not seeing where the existing ConstantRangeAttributeImpl's destructor gets called
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah this should be using a separate SpecificBumpPtrAllocator
like ConstantRangeAttributeAlloc
so that we make sure to call destructors when the LLVMContext is destroyed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh thanks for this info! Changed to SpecificBumpPtrAllocator
. Please take another look :-)
llvm/lib/IR/ConstantRangeList.cpp
Outdated
// Represent a list of signed ConstantRange and do NOT support wrap around the | ||
// end of the numeric range. Ranges in the list are ordered and no overlapping. | ||
// Ranges should have the same bitwidth. Each range's lower should be less than | ||
// its upper. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't repeat the header comment here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, what I originally had in mind was to actually not have separate ConstantRange and ConstantRangeList attribute kinds, and only have the latter -- the existing ConstantRange kind would just be a list with a single element.
The upside of doing that is that we have less attribute kinds to deal with :) The downside is that the representation for the single-attribute case becomes less efficient (because we have to explicitly store the size 1).
The less efficient single-range seems ok if the single range attributes aren't too common and have too many unique ranges. It's hard to predict the future to know if this will be the case. Supporting a separate ConstantRangeList attribute doesn't seem too bad though, so I'm leaning toward keeping them separate. |
llvm/lib/IR/ConstantRangeList.cpp
Outdated
// Slow insert. | ||
SmallVector<ConstantRange, 2> ExistingTail(LowerBound, Ranges.end()); | ||
Ranges.erase(LowerBound, Ranges.end()); | ||
// "sle" instead of "slt" to merge consecutive ranges. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the "sle" instead of "slt"
portion of the comment isn't necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this portion!
ConstantRangeListAttributeImpl::totalSizeToAlloc(Val), | ||
alignof(ConstantRangeListAttributeImpl)); | ||
PA = new (Mem) ConstantRangeListAttributeImpl(Kind, Val); | ||
pImpl->AttrsSet.InsertNode(PA, InsertPoint); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm I'm not seeing where the existing ConstantRangeAttributeImpl's destructor gets called
if (i + 2 * RangeSize >= e) | ||
return error("Incomplete constant range list"); | ||
for (unsigned Idx = 0; Idx < RangeSize; ++Idx) { | ||
int64_t Start = BitcodeReader::decodeSignRotatedValue(Record[++i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The int64_t
as type would still assume BitWidth <= 64 (if > 64, the writer actually writes more than 2 elements in the record per Idx)
Can you factor out part of readConstantRange() and reuse that (the part after having read the BitWidth from the record)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored readConstantRange
and reused it to avoid the bitwidth assumption. Thanks!
@nikic any more concerns? |
ping @nikic |
bool empty() const { return Ranges.empty(); } | ||
|
||
/// Get the bit width of this ConstantRangeList. | ||
uint32_t getBitWidth() const { return 64; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoding this seems a bit odd for a generically named class like ConstantRangeList, but I guess this can always be relaxed later...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, relaxing later on demand is the plan!
// Represent a list of signed ConstantRange and do NOT support wrap around the | ||
// end of the numeric range. Ranges in the list are ordered and not overlapping. | ||
// Ranges should have the same bitwidth. Each range's lower should be less than | ||
// its upper. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something I noticed is that while ConstantRangeList maintains the "ordered and not overlapping" invariant through manipulations, it does not check that it initially holds. And indeed, LLParser will create such invalid ConstantRangeLists, which will later get rejected by the Verifier. Is this how it is intended to work out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is convenient that the verifier can check for sorted ConstantRangeList
s without asserting on construction, but perhaps it is better to move the checks to both LLParser and bitcode parser
a static method that returns a std::optional<ConstantRangeList>
depending on if the input is sorted or not, and error checking in the LLParser/bitcode parser would be good. then we can assert that the input is sorted in the constructor.
(or just automatically sort ConstantRangeLists on construction, but IMO the checks can be useful for finding bugs)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added static std::optional<ConstantRangeList> getConstantRangeList()
and checked the optional return in LLParse and bitcode parser.
|
||
SmallVector<ConstantRange, 2> Val; | ||
unsigned RangeSize = Record[++i]; | ||
unsigned BitWidth = Record[++i]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing bounds check before these accesses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
if (i + 2 * RangeSize >= e) | ||
return error("Incomplete constant range list"); | ||
for (unsigned Idx = 0; Idx < RangeSize; ++Idx) { | ||
i++; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really get why this i++ and i-- dance here is necessary. I think maybe you want just one ++i before the loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to before the loop and changed the Record access to "i++" rather than "++i", the same way as the "Range" attribute.
Record.push_back(Val[0].getBitWidth()); | ||
for (auto &CR : Val) { | ||
emitConstantRange(Record, CR, /*EmitBitWidth=*/false); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove braces.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
llvm/lib/IR/AttributeImpl.h
Outdated
assert(Size > 0); | ||
unsigned BitWidth = Val.front().getLower().getBitWidth(); | ||
for (unsigned I = 0; I != Size; ++I) { | ||
assert(BitWidth == Val[I].getLower().getBitWidth()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert(BitWidth == Val[I].getLower().getBitWidth()); | |
assert(BitWidth == Val[I].getBitWidth()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this code snippet.
llvm/lib/IR/AttributeImpl.h
Outdated
unsigned BitWidth = Val.front().getLower().getBitWidth(); | ||
for (unsigned I = 0; I != Size; ++I) { | ||
assert(BitWidth == Val[I].getLower().getBitWidth()); | ||
new (&TrailingCR[I]) ConstantRange(BitWidth, false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you use uninitialized_copy
instead of performing a dummy initialization first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this code snippet.
llvm/lib/IR/Attributes.cpp
Outdated
// If we didn't find any existing attributes of the same shape then create a | ||
// new one and insert it. | ||
PA = new (pImpl->ConstantRangeListAttributeAlloc.Allocate( | ||
ConstantRangeListAttributeImpl::totalSizeToAlloc(Val))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heh, this does something completely different from what you think it does. It allocates an array of sizeof(ConstantRangeListAttributeImpl) * totalSizeToAlloc(Val)
.
It's not possible to use SpecificBumpPtrAllocator with a dynamically sized class, it can only be used to allocate objects of fixed size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we have a variable number of ConstantRange
s in a ConstantRangeListAttributeImpl
and they need a destructor, I don't see a nice way of using trailing objects and bump allocators. I think we should go back to ConstantRangeListAttributeImpl
containing a ConstantRangeList
and using SpecificBumpPtrAllocator<ConstantRangeListAttributeImpl>
so that we allocate the right amount of memory and properly call the destructor. most of the time we'll be in the non-allocating version of the SmallVector<ConstantRange>
anyway
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed the attr implementation back to ConstantRangeList.
Thanks for the comments, Nikita and Arthur! @nikic please take another look. |
We propose adding a new LLVM attribute,
initializes((Lo1,Hi1),(Lo2,Hi2),...)
, which expresses the notion of memory space (i.e., intervals, in bytes) that the argument pointing to is initialized in the function.Will commit the attribute inferring in the follow-up PRs.
https://discourse.llvm.org/t/rfc-llvm-new-initialized-parameter-attribute-for-improved-interprocedural-dse/77337