Skip to content

Allow empty range attribute and add assert for full range #100601

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

Merged
merged 2 commits into from
Aug 8, 2024

Conversation

andjo403
Copy link
Contributor

alternative to #100465

fix #99619

@andjo403 andjo403 requested a review from nikic as a code owner July 25, 2024 17:09
@llvmbot
Copy link
Member

llvmbot commented Jul 25, 2024

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-llvm-ir

Author: Andreas Jonson (andjo403)

Changes

alternative to #100465

fix #99619


Full diff: https://github.com/llvm/llvm-project/pull/100601.diff

4 Files Affected:

  • (modified) llvm/include/llvm/IR/Attributes.h (+1)
  • (modified) llvm/lib/Bitcode/Reader/BitcodeReader.cpp (+3-1)
  • (modified) llvm/lib/IR/Attributes.cpp (+8-1)
  • (modified) llvm/lib/IR/Core.cpp (+4-4)
diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h
index 5a80a072dbbd2..cefbe1d760588 100644
--- a/llvm/include/llvm/IR/Attributes.h
+++ b/llvm/include/llvm/IR/Attributes.h
@@ -160,6 +160,7 @@ class Attribute {
   static Attribute getWithUWTableKind(LLVMContext &Context, UWTableKind Kind);
   static Attribute getWithMemoryEffects(LLVMContext &Context, MemoryEffects ME);
   static Attribute getWithNoFPClass(LLVMContext &Context, FPClassTest Mask);
+  static Attribute getWithRange(LLVMContext &Context, const ConstantRange &CR);
 
   /// For a typed attribute, return the equivalent attribute with the type
   /// changed to \p ReplacementTy.
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index fd4ae109b4bb8..091065cf6d55f 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2369,7 +2369,9 @@ Error BitcodeReader::parseAttributeGroupBlock() {
             return MaybeCR.takeError();
           i--;
 
-          B.addConstantRangeAttr(Kind, MaybeCR.get());
+          assert(Kind == Attribute::Range &&
+                 "Unhandled ConstantRangeAttribute");
+          B.addRangeAttr(MaybeCR.get());
         } else if (Record[i] == 8) {
           Attribute::AttrKind Kind;
 
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index b2fdd218e5d4b..4558e8081a67b 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -286,6 +286,13 @@ Attribute Attribute::getWithNoFPClass(LLVMContext &Context,
   return get(Context, NoFPClass, ClassMask);
 }
 
+Attribute Attribute::getWithRange(LLVMContext &Context,
+                                  const ConstantRange &CR) {
+  assert(!CR.isEmptySet() && "Range must not be empty!");
+  assert(!CR.isFullSet() && "Range must not be full!");
+  return get(Context, Range, CR);
+}
+
 Attribute
 Attribute::getWithAllocSizeArgs(LLVMContext &Context, unsigned ElemSizeArg,
                                 const std::optional<unsigned> &NumElemsArg) {
@@ -2024,7 +2031,7 @@ AttrBuilder &AttrBuilder::addConstantRangeAttr(Attribute::AttrKind Kind,
 }
 
 AttrBuilder &AttrBuilder::addRangeAttr(const ConstantRange &CR) {
-  return addConstantRangeAttr(Attribute::Range, CR);
+  return addAttribute(Attribute::getWithRange(Ctx, CR));
 }
 
 AttrBuilder &
diff --git a/llvm/lib/IR/Core.cpp b/llvm/lib/IR/Core.cpp
index 17c0bf72ef05d..f3e0ed385ae2b 100644
--- a/llvm/lib/IR/Core.cpp
+++ b/llvm/lib/IR/Core.cpp
@@ -191,11 +191,11 @@ LLVMAttributeRef LLVMCreateConstantRangeAttribute(LLVMContextRef C,
                                                   const uint64_t UpperWords[]) {
   auto &Ctx = *unwrap(C);
   auto AttrKind = (Attribute::AttrKind)KindID;
+  assert(AttrKind == Attribute::Range && "Unhandled ConstantRangeAttribute");
   unsigned NumWords = divideCeil(NumBits, 64);
-  return wrap(Attribute::get(
-      Ctx, AttrKind,
-      ConstantRange(APInt(NumBits, ArrayRef(LowerWords, NumWords)),
-                    APInt(NumBits, ArrayRef(UpperWords, NumWords)))));
+  return wrap(Attribute::getWithRange(
+      Ctx, ConstantRange(APInt(NumBits, ArrayRef(LowerWords, NumWords)),
+                         APInt(NumBits, ArrayRef(UpperWords, NumWords)))));
 }
 
 LLVMAttributeRef LLVMCreateStringAttribute(LLVMContextRef C,

@nikic
Copy link
Contributor

nikic commented Jul 28, 2024

I'm wondering whether we wouldn't be better off allowing empty ranges. I'm pretty sure that patches like #100899 are going to cause failures in edge cases (where ctpop is in dead code and we infer that no values are valid).

Similarly, if we have an existing range attribute and then want to add new information to it and we perform an intersection, we may end up with an empty range. In that case we'd need a special case to leave the original information alone.

I think things will overall be more robust if we make empty ranges valid (it just means that the value will always be poison).

@andjo403
Copy link
Contributor Author

Yes I also started to think about in how may places that needed to have that type of empty/full check to avoid asserts when I looked at that PR, I was not able to think of a case but as you say probably missed some corner case.
Do you think that if we remove the empty case in the LangRef that we also shall remove the full case feels like that is only an hint to not add ranges that do not add any information.
so remove this line The range should not represent the full or empty set. That is, a!=b.

@nikic
Copy link
Contributor

nikic commented Jul 28, 2024

I think it would be somewhat unusual to allow no-op attributes, e.g. we don't do this for some other things like dereferenceable(0) either. Instead, some key places will simply not add the attribute if it would have no effect (e.g.

AttrBuilder &AttrBuilder::addDereferenceableAttr(uint64_t Bytes) {
if (Bytes == 0) return *this;
return addRawIntAttr(Attribute::Dereferenceable, Bytes);
}
).

Though my main concern there would be that in textual IR it's kind of hard to distinguish empty & full range without knowing LLVM implementation details, as they are both represented using lo=hi.

@andjo403
Copy link
Contributor Author

hmm so the ref shall be something like The range should not represent the full set. That is, a!=b, Exception is the empty set where a and b is the min value.

@andjo403
Copy link
Contributor Author

or keep the lang ref as is and do as AttrBuilder::addDereferenceableAttr that we only add the attribute if not empty/full

@nikic
Copy link
Contributor

nikic commented Jul 29, 2024

or keep the lang ref as is and do as AttrBuilder::addDereferenceableAttr that we only add the attribute if not empty/full

Not sure about just silently dropping the empty case. For full, this is equivalent to not having the attribute, so that makes sense. For empty, this will result in some non-monotonic behavior. E.g. if you originally have range(0, 5) and then intersect with range(4, 10) you get range(4, 5), but if you intersect with range(5, 10) you instead keep range(0, 5). That is, inferring a better range results in worse attributes being materialized. That doesn't break anything, but it's probably still undesirable.

@andjo403 andjo403 changed the title Assert range attribute is not empty nor full Allow empty range attribute and add assert for full range Aug 6, 2024
@andjo403
Copy link
Contributor Author

andjo403 commented Aug 6, 2024

Updated the langref to allow empty ranges and asserts for full range but the builder ignores full ranges

Comment on lines 1678 to 1679
- Only for the empty set is ``a`` and ``b`` allowed to be equal and
only for the value 0.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found this a bit hard to understand, maybe like this?

Suggested change
- Only for the empty set is ``a`` and ``b`` allowed to be equal and
only for the value 0.
- The empty range is represented using ``0,0``.
- Otherwise, ``a`` and ``b` are not allowed to be equal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes much better

@@ -3109,8 +3109,8 @@ bool LLParser::parseRangeAttr(AttrBuilder &B) {
if (ParseAPSInt(BitWidth, Lower) ||
parseToken(lltok::comma, "expected ','") || ParseAPSInt(BitWidth, Upper))
return true;
if (Lower == Upper)
return tokError("the range should not represent the full or empty set!");
if (Lower == Upper && !Lower.isMinValue())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (Lower == Upper && !Lower.isMinValue())
if (Lower == Upper && !Lower.isZero())

@@ -286,6 +286,12 @@ Attribute Attribute::getWithNoFPClass(LLVMContext &Context,
return get(Context, NoFPClass, ClassMask);
}

Attribute Attribute::getWithRange(LLVMContext &Context,
const ConstantRange &CR) {
assert(!CR.isFullSet() && "Range must not be full!");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be okay to just add this directly into the generic ctor, rather than specific to range. I would expect that other range-based attributes added in the future will not allow full ranges either. (If they do, we can separate it out at the time.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes that seems resonable.

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@andjo403 andjo403 merged commit 04da773 into llvm:main Aug 8, 2024
8 checks passed
@andjo403 andjo403 deleted the rangeAssert branch August 8, 2024 16:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[IR] Missing assertion for empty range attribute
3 participants