Skip to content

Commit

Permalink
[InstCombine] Fix miscompile in negation of select (#89698)
Browse files Browse the repository at this point in the history
Swapping the operands of a select is not valid if one hand is more
poisonous that the other, because the negation zero contains poison
elements.

Fix this by adding an extra parameter to isKnownNegation() to forbid
poison elements.

I've implemented this using manual checks to avoid needing four variants
for the NeedsNSW/AllowPoison combinations. Maybe there is a better way
to do this...

Fixes #89669.
  • Loading branch information
nikic committed Apr 24, 2024
1 parent d97cdd7 commit a1b1c4a
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 12 deletions.
3 changes: 2 additions & 1 deletion llvm/include/llvm/Analysis/ValueTracking.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ bool isKnownNonZero(const Value *V, const SimplifyQuery &Q, unsigned Depth = 0);
/// Currently can recoginze Value pair:
/// 1: <X, Y> if X = sub (0, Y) or Y = sub (0, X)
/// 2: <X, Y> if X = sub (A, B) and Y = sub (B, A)
bool isKnownNegation(const Value *X, const Value *Y, bool NeedNSW = false);
bool isKnownNegation(const Value *X, const Value *Y, bool NeedNSW = false,
bool AllowPoison = true);

/// Returns true if the give value is known to be non-negative.
bool isKnownNonNegative(const Value *V, const SimplifyQuery &SQ,
Expand Down
24 changes: 17 additions & 7 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8042,17 +8042,27 @@ static SelectPatternResult matchMinMax(CmpInst::Predicate Pred,
return {SPF_UNKNOWN, SPNB_NA, false};
}

bool llvm::isKnownNegation(const Value *X, const Value *Y, bool NeedNSW) {
bool llvm::isKnownNegation(const Value *X, const Value *Y, bool NeedNSW,
bool AllowPoison) {
assert(X && Y && "Invalid operand");

// X = sub (0, Y) || X = sub nsw (0, Y)
if ((!NeedNSW && match(X, m_Sub(m_ZeroInt(), m_Specific(Y)))) ||
(NeedNSW && match(X, m_NSWNeg(m_Specific(Y)))))
auto IsNegationOf = [&](const Value *X, const Value *Y) {
if (!match(X, m_Neg(m_Specific(Y))))
return false;

auto *BO = cast<BinaryOperator>(X);
if (NeedNSW && !BO->hasNoSignedWrap())
return false;

auto *Zero = cast<Constant>(BO->getOperand(0));
if (!AllowPoison && !Zero->isNullValue())
return false;

return true;
};

// Y = sub (0, X) || Y = sub nsw (0, X)
if ((!NeedNSW && match(Y, m_Sub(m_ZeroInt(), m_Specific(X)))) ||
(NeedNSW && match(Y, m_NSWNeg(m_Specific(X)))))
// X = -Y or Y = -X
if (IsNegationOf(X, Y) || IsNegationOf(Y, X))
return true;

// X = sub (A, B), Y = sub (B, A) || X = sub nsw (A, B), Y = sub nsw (B, A)
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Transforms/InstCombine/InstCombineNegator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,8 @@ std::array<Value *, 2> Negator::getSortedOperandsOfBinOp(Instruction *I) {
return NegatedPHI;
}
case Instruction::Select: {
if (isKnownNegation(I->getOperand(1), I->getOperand(2))) {
if (isKnownNegation(I->getOperand(1), I->getOperand(2), /*NeedNSW=*/false,
/*AllowPoison=*/false)) {
// Of one hand of select is known to be negation of another hand,
// just swap the hands around.
auto *NewSelect = cast<SelectInst>(I->clone());
Expand Down
6 changes: 3 additions & 3 deletions llvm/test/Transforms/InstCombine/sub-of-negatible.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1385,12 +1385,12 @@ define i8 @dont_negate_ordinary_select(i8 %x, i8 %y, i8 %z, i1 %c) {
ret i8 %t1
}

; FIXME: This is a miscompile.
define <2 x i32> @negate_select_of_negation_poison(<2 x i1> %c, <2 x i32> %x) {
; CHECK-LABEL: @negate_select_of_negation_poison(
; CHECK-NEXT: [[NEG:%.*]] = sub <2 x i32> <i32 0, i32 poison>, [[X:%.*]]
; CHECK-NEXT: [[TMP1:%.*]] = select <2 x i1> [[C:%.*]], <2 x i32> [[X]], <2 x i32> [[NEG]]
; CHECK-NEXT: ret <2 x i32> [[TMP1]]
; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[C:%.*]], <2 x i32> [[NEG]], <2 x i32> [[X]]
; CHECK-NEXT: [[NEG2:%.*]] = sub <2 x i32> zeroinitializer, [[SEL]]
; CHECK-NEXT: ret <2 x i32> [[NEG2]]
;
%neg = sub <2 x i32> <i32 0, i32 poison>, %x
%sel = select <2 x i1> %c, <2 x i32> %neg, <2 x i32> %x
Expand Down

0 comments on commit a1b1c4a

Please sign in to comment.