-
Notifications
You must be signed in to change notification settings - Fork 12.3k
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
[KnownBits] Remove hasConflict()
assertions
#94568
Conversation
@llvm/pr-subscribers-llvm-selectiondag @llvm/pr-subscribers-llvm-support Author: None (c8ef) Changesclose: #94436 Full diff: https://github.com/llvm/llvm-project/pull/94568.diff 1 Files Affected:
diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index ba4a5f01036ca..c206bd77d5880 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -48,7 +48,6 @@ struct KnownBits {
/// Returns true if we know the value of all bits.
bool isConstant() const {
- assert(!hasConflict() && "KnownBits conflict!");
return Zero.popcount() + One.popcount() == getBitWidth();
}
@@ -74,16 +73,10 @@ struct KnownBits {
}
/// Returns true if value is all zero.
- bool isZero() const {
- assert(!hasConflict() && "KnownBits conflict!");
- return Zero.isAllOnes();
- }
+ bool isZero() const { return Zero.isAllOnes(); }
/// Returns true if value is all one bits.
- bool isAllOnes() const {
- assert(!hasConflict() && "KnownBits conflict!");
- return One.isAllOnes();
- }
+ bool isAllOnes() const { return One.isAllOnes(); }
/// Make all bits known to be zero and discard any previous information.
void setAllZero() {
|
These assertions are not just in KnownBits.h, but also spread across many other places. Basically do a |
I removed more assertions in other files. |
There are more checks in KnownBitsTest, but I guess we can drop those in a second step, when removing the various |
Though one testing-related change we should do is drop this check:
|
After removing this statement, I immediately encounter an error locally. Is this the expected behavior? Since the purpose of this PR is to enable a bit to be both zero and one simultaneously, perhaps we should also remove this assert?
UPD: If this assertion is also removed, there will be numerous failures such as:
|
I expect you would have to make some changes like this, since even the supposedly optimal KnownBits implementations do not always report conflict when they should: diff --git a/llvm/unittests/Support/KnownBitsTest.cpp b/llvm/unittests/Support/KnownBitsTest.cpp
index 824cf7501fd4..acec5dbfb4e6 100644
--- a/llvm/unittests/Support/KnownBitsTest.cpp
+++ b/llvm/unittests/Support/KnownBitsTest.cpp
@@ -95,9 +95,10 @@ static void testBinaryOpExhaustive(StringRef Name, BinaryBitsFn BitsFn,
});
});
- EXPECT_TRUE(!Computed.hasConflict());
- EXPECT_TRUE(checkResult(Name, Exact, Computed, {Known1, Known2},
- CheckOptimality));
+ if (!Exact.hasConflict()) {
+ EXPECT_TRUE(checkResult(Name, Exact, Computed, {Known1, Known2},
+ CheckOptimality));
+ }
// In some cases we choose to return zero if the result is always
// poison.
if (RefinePoisonToZero && Exact.hasConflict()) { |
static KnownBits computeForAddCarry(
const KnownBits &LHS, const KnownBits &RHS,
bool CarryZero, bool CarryOne) {
APInt PossibleSumZero = LHS.getMaxValue() + RHS.getMaxValue() + !CarryZero;
APInt PossibleSumOne = LHS.getMinValue() + RHS.getMinValue() + CarryOne;
// Compute known bits of the carry.
APInt CarryKnownZero = ~(PossibleSumZero ^ LHS.Zero ^ RHS.Zero);
APInt CarryKnownOne = PossibleSumOne ^ LHS.One ^ RHS.One;
// Compute set of known bits (where all three relevant bits are known).
APInt LHSKnownUnion = LHS.Zero | LHS.One;
APInt RHSKnownUnion = RHS.Zero | RHS.One;
APInt CarryKnownUnion = std::move(CarryKnownZero) | CarryKnownOne;
APInt Known = std::move(LHSKnownUnion) & RHSKnownUnion & CarryKnownUnion;
// vvvv This assert also hit in test vvvv
assert((PossibleSumZero & Known) == (PossibleSumOne & Known) &&
"known bits of sum differ");
// ^^^^
// Compute known bits of the result.
KnownBits KnownOut;
KnownOut.Zero = ~std::move(PossibleSumZero) & Known;
KnownOut.One = std::move(PossibleSumOne) & Known;
return KnownOut;
} |
@c8ef Yeah, you should drop that assert. |
If we remove this check, it appears that many tests are failing. Currently, I am skipping values that have conflicts, but I am unsure if this is the correct approach. Please inform me if there are any issues. |
@@ -4212,7 +4212,6 @@ KnownBits SelectionDAG::computeKnownBits(SDValue Op, const APInt &DemandedElts, | |||
break; | |||
} | |||
|
|||
assert(!Known.hasConflict() && "Bits known to be one AND zero?"); |
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.
Maybe just focus on the middle-end initially and then look at removing the DAG/GlobalISel assertions 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.
I feel like it's safer to get rid of it everyone at once.
@@ -726,6 +741,9 @@ TEST(KnownBitsTest, SExtInReg) { | |||
unsigned Bits = 4; | |||
for (unsigned FromBits = 1; FromBits <= Bits; ++FromBits) { | |||
ForeachKnownBits(Bits, [&](const KnownBits &Known) { | |||
if (Known.hasConflict()) | |||
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.
I think it would be slightly better to move this check until after the Known.sextInReg() call (and also something similar for the other tests). Basically still call the KnownBits helper to make sure it doesn't assert, but don't check what the result will be, as it could be anything.
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 are some test failures in pre-commit CI:
I think most of these are due to the various |
✅ With the latest revision this PR passed the C/C++ code formatter. |
I would prefer making this an NFC patch that just drops assertions. |
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 looks mostly good to me, but I think some of the setAllZeros() removals should be undone.
llvm/lib/Support/KnownBits.cpp
Outdated
// Result is either known Zero or UB. Return Zero either way. | ||
// Checking this earlier saves us a lot of special cases later on. | ||
Known.setAllZero(); | ||
// Result is either known Zero or UB. |
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 setAllZero() call should be retained. Otherwise we would now provide a worse result if the LHS is zero. (It would go from result zero to result unknown).
llvm/lib/Support/KnownBits.cpp
Outdated
@@ -403,8 +391,7 @@ KnownBits KnownBits::lshr(const KnownBits &LHS, const KnownBits &RHS, | |||
if (Exact) { | |||
unsigned FirstOne = LHS.countMaxTrailingZeros(); | |||
if (FirstOne < MinShiftAmount) { | |||
// Always poison. Return zero because we don't like returning conflict. | |||
Known.setAllZero(); | |||
// Always poison. |
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.
Similar here and in the ashr implementation. We go from returning zero to returning unknown, which is responsible for the regressions in SCEV tests where we go from range [0, 1)
to full-set
.
The correct replacement would be something like setConflict(), but given that we don't have that yet and wouldn't be able to make use of it for folding I think it's best to leave these cases alone.
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 appears that in the current version(
cfe2878), the testcase llvm/test/Analysis/ScalarEvolution/pr76234.ll
is still experiencing a regression from [0, 1)
to full-set
. Is this the intended behavior?
�_bk;t=1717760175285�# | <stdin>:4:2: note: possible intended match here
�_bk;t=1717760175285�# | --> %B9 U: full-set S: full-set
�_bk;t=1717760175285�# | ^
�_bk;t=1717760175285�# |
�_bk;t=1717760175285�# | Input file: <stdin>
�_bk;t=1717760175285�# | Check file: C:\ws\src\llvm\test\Analysis\ScalarEvolution\pr76234.ll
�_bk;t=1717760175285�# |
�_bk;t=1717760175285�# | -dump-input=help explains the following input dump.
�_bk;t=1717760175285�# |
�_bk;t=1717760175285�# | Input was:
�_bk;t=1717760175285�# | <<<<<<
�_bk;t=1717760175285�# | 1: Printing analysis 'Scalar Evolution Analysis' for function 'PR76234':
�_bk;t=1717760175285�# | 2: Classifying expressions for: @PR76234
�_bk;t=1717760175285�# | 3: %B9 = shl i896 0, -264147265567832623176169892458258303259423663018060761063980354513336951278362429737208627943828593947337197496628564339441173779751342768625269489231469788454193341999502542084365758838213220526512116454105594202074014146375780869419198449383518238244769290448868999168
�_bk;t=1717760175285�# | next:9'0 X error: no match found
�_bk;t=1717760175285�# | 4: --> %B9 U: full-set S: full-set
�_bk;t=1717760175285�# | next:9'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
�_bk;t=1717760175285�# | next:9'1 ? possible intended match
�_bk;t=1717760175285�# | 5: %B39 = ashr i896 %B9, 1
�_bk;t=1717760175285�# | next:9'0 ~~~~~~~~~~~~~~~~~~~~~~~~~
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.
Hm, I see. In that case I think it would be best to just revert all the setAllZero() related changes. We will have to update consuming code first so it can do something useful with conflict values before we start generating them.
llvm/lib/Support/KnownBits.cpp
Outdated
@@ -448,8 +433,7 @@ KnownBits KnownBits::ashr(const KnownBits &LHS, const KnownBits &RHS, | |||
MinShiftAmount = 1; | |||
if (LHS.isUnknown()) { | |||
if (MinShiftAmount == BitWidth) { | |||
// Always poison. Return zero because we don't like returning conflict. | |||
Known.setAllZero(); | |||
// Always poison. |
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 one, and the next one as well.
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
@@ -38,8 +38,10 @@ static void TestBinOpExhaustive(Fn1 PropagateFn, Fn2 EvalFn) { | |||
|
|||
APInt AB1R = PropagateFn(0, AOut, Known1Redacted, Known2Redacted); | |||
APInt AB2R = PropagateFn(1, AOut, Known1Redacted, Known2Redacted); | |||
EXPECT_EQ(AB1, AB1R); | |||
EXPECT_EQ(AB2, AB2R); | |||
if (!Known1Redacted.hasConflict() && !Known2Redacted.hasConflict()) { |
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 change still needed in this version of the patch?
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.
Confirm it is still needed.
}); | ||
} | ||
} | ||
|
||
static void testBinaryOpExhaustive(StringRef Name, BinaryBitsFn BitsFn, | ||
BinaryIntFn IntFn, | ||
bool CheckOptimality = true, | ||
bool RefinePoisonToZero = 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.
Should restore this RefinePoisonToZero stuff, since you have restored all the "if conflict return zero" cases.
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 will handle it in another pr.
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.
@@ -653,8 +656,10 @@ TEST(KnownBitsTest, GetMinMaxVal) { | |||
Min = APIntOps::umin(Min, N); | |||
Max = APIntOps::umax(Max, N); | |||
}); | |||
EXPECT_EQ(Min, Known.getMinValue()); | |||
EXPECT_EQ(Max, Known.getMaxValue()); | |||
if (!Known.hasConflict()) { |
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 there's a conflict you could immediately "continue" at the top of the loop to save some time. Same in the next three functions below.
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.
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.
But in this case you are not calling Known.anything()
before checking for conflict and bailing 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.
Hm, yes, I guess we should store Known.getMinValue() etc in a variable first, before the check. Or do people think there is not much value in checking all the functions with conflict inputs?
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.
Or do people think there is not much value in checking all the functions with conflict inputs?
I'm not sure how much value there is right now.
At some point we should check that KnownBits functions return conflict when they ought to, and part of that would definitely involve testing them on conflict inputs.
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.
These particular APIs don't return KnownBits, so they don't really have any specific behavior they should exhibit for conflict inputs beyond "don't assert".
@@ -752,8 +765,10 @@ TEST(KnownBitsTest, CommonBitsSet) { | |||
HasCommonBitsSet |= N1.intersects(N2); | |||
}); | |||
}); | |||
EXPECT_EQ(!HasCommonBitsSet, | |||
KnownBits::haveNoCommonBitsSet(Known1, Known2)); | |||
if (!Known1.hasConflict() && !Known2.hasConflict()) { |
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.
Same 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.
I believe this is necessary because we permit conflicts in test generation. Therefore, even if we revert the set to zero, these tests will only pass when there are no conflicts.
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 meant that you could save some time here by testing for conflict as the first thing in the loop body, before the nested ForeachNumInKnownBits loops.
close: #94436