Bug type: miscompilation
Component: ConstraintElimination
Bug location: llvm/lib/Transforms/Scalar/ConstraintElimination.cpp: add
decompose
mul
Issue title: Miscompile with opt -passes="constraint-elimination"
Labels: miscompilation
llvm:transforms
Reported by: mikaelholmen

llvm commit: 5842dfe34d89
Reproduce with: ```opt -passes="constraint-elimination" bbi-90501.ll -S -o -```
[bbi-90501.ll.gz](https://github.com/llvm/llvm-project/files/13810077/bbi-90501.ll.gz)

There are two nested loops in the input that should finish and then we should end up at
```
bb.2:                                             ; preds = %bb.1
  ret i16 0
```
but with constraint-elimination the condition in bb.5 is changed to
```
  %i10 = or i1 true, %i9
  br i1 %i10, label %bb.7, label %bb.6
```
so we will go to
```
bb.7:                                             ; preds = %bb.5
  ret i16 1
```
instead.

Comments: author: mikaelholmen
body: This starts happening with e6a1657fa30c747f4412fc47f567660ebe861a9e
```
[ConstraintElim] Add A < B if A is an increasing phi for A != B.
```
@fhahn : any idea what's up?
author: fhahn
body: Looks like an issue with `sub` decomposition with negative constants, preparing a fix.
author: mikaelholmen
body: Thanks for the quick fix! I've verified it solves the problem I saw.
author: fhahn
body: @mikaelholmen thanks for the reproducer!
author: fhahn
body: (appreciated as always, thanks for your extensive testing)

Fix patch:
commit 3c127e83c07c3791e86413c22a414a030d4302e3
Author: Florian Hahn <flo@fhahn.com>
Date:   Tue Jan 2 22:05:57 2024 +0000

    [ConstraintElim] Replace NUWSub decomp with recursive decomp of ops.
    
    The current patterns for NUWSub decompositions do not handle negative
    constants correctly at the moment (causing #76713).
    
    Replace the incorrect pattern by more general code that recursively
    decomposes the operands and then combines the results. This is already
    done in most other places that handle operators like add/mul.
    
    This means we fall back to the general constant handling code (fixes the
    mis-compile) while also being able to support reasoning about
    decomposable expressions in the SUB operands.
    
    Fixes https://github.com/llvm/llvm-project/issues/76713.

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 49ac1e96e255..06c87bd6dc37 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -366,6 +366,13 @@ struct Decomposition {
     append_range(Vars, Other.Vars);
   }
 
+  void sub(const Decomposition &Other) {
+    Decomposition Tmp = Other;
+    Tmp.mul(-1);
+    add(Tmp.Offset);
+    append_range(Vars, Tmp.Vars);
+  }
+
   void mul(int64_t Factor) {
     Offset = multiplyWithOverflow(Offset, Factor);
     for (auto &Var : Vars)
@@ -569,10 +576,12 @@ static Decomposition decompose(Value *V,
     return Result;
   }
 
-  if (match(V, m_NUWSub(m_Value(Op0), m_ConstantInt(CI))) && canUseSExt(CI))
-    return {-1 * CI->getSExtValue(), {{1, Op0}}};
-  if (match(V, m_NUWSub(m_Value(Op0), m_Value(Op1))))
-    return {0, {{1, Op0}, {-1, Op1}}};
+  if (match(V, m_NUWSub(m_Value(Op0), m_Value(Op1)))) {
+    auto ResA = decompose(Op0, Preconditions, IsSigned, DL);
+    auto ResB = decompose(Op1, Preconditions, IsSigned, DL);
+    ResA.sub(ResB);
+    return ResA;
+  }
 
   return {V, IsKnownNonNegative};
 }


Regression tests:
file: llvm/test/Transforms/ConstraintElimination/sub-nuw.ll
commands: opt -passes=constraint-elimination -S %s
tests: test_name: pr76713
test_body: define i1 @pr76713(i16 %i1, i16 %i3) {
entry:
  %c1 = icmp ult i16 %i1, -1
  %c2 = icmp uge i16 %i1, -3
  %c3 = icmp ult i16 %i3, 2
  %and = and i1 %c1, %c2
  %and.2 = and i1 %and, %c3
  br i1 %and, label %then, label %else

then:                                             ; preds = %entry
  %sub = sub nuw nsw i16 %i1, -3
  %arrayidx.idx = mul nuw nsw i16 %i3, 4
  %i6 = add nuw nsw i16 %arrayidx.idx, %sub
  %c4 = icmp ult i16 12, %i6
  ret i1 %c4

else:                                             ; preds = %entry
  ret i1 false
}

test_name: sub_nuw_chained_negative_constants
test_body: declare void @use(i1)

define void @sub_nuw_chained_negative_constants(i8 %a) {
entry:
  %sub1 = sub nuw i8 %a, 10
  %sub2 = sub nuw i8 %sub1, -126
  %c.1 = icmp ugt i8 %sub2, 20
  br i1 %c.1, label %exit.1, label %exit.2

exit.1:                                           ; preds = %entry
  %c.2 = icmp ugt i8 %a, -96
  call void @use(i1 %c.2)
  %c.3 = icmp ugt i8 %a, -95
  call void @use(i1 %c.3)
  ret void

exit.2:                                           ; preds = %entry
  %c.4 = icmp ugt i8 %a, -96
  call void @use(i1 %c.4)
  %c.5 = icmp ugt i8 %a, -95
  call void @use(i1 %c.5)
  ret void
}

test_name: sub_nuw_neg_i16
test_body: define i1 @sub_nuw_neg_i16(i16 %a) {
entry:
  %neg2 = sub nuw i16 %a, -305
  %c.1 = icmp ugt i16 0, %neg2
  br i1 %c.1, label %exit.1, label %exit.2

exit.1:                                           ; preds = %entry
  %c.2 = icmp ugt i16 %a, 0
  ret i1 %c.2

exit.2:                                           ; preds = %entry
  %c.3 = icmp ugt i16 %a, 0
  ret i1 %c.3
}

test_name: sub_nuw_chained_positive_constants
test_body: declare void @use(i1)

define void @sub_nuw_chained_positive_constants(i16 %a) {
entry:
  %sub1 = sub nuw i16 %a, 10
  %sub2 = sub nuw i16 %sub1, 20
  %c.1 = icmp ugt i16 %sub2, 90
  br i1 %c.1, label %exit.1, label %exit.2

exit.1:                                           ; preds = %entry
  %c.2 = icmp ugt i16 %a, 120
  call void @use(i1 %c.2)
  %c.3 = icmp ugt i16 %a, 121
  call void @use(i1 %c.3)
  ret void

exit.2:                                           ; preds = %entry
  %c.4 = icmp ugt i16 %a, 120
  call void @use(i1 %c.4)
  %c.5 = icmp ugt i16 %a, 121
  call void @use(i1 %c.5)
  ret void
}