-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[InstCombine] Simplify (add/sub (sub/add) (sub/add))
irrelevant of use-count
#105866
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
Conversation
@llvm/pr-subscribers-llvm-transforms Author: None (goldsteinn) Changes
Full diff: https://github.com/llvm/llvm-project/pull/105866.diff 5 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index d7758b5fbf1786..5272bc11865f53 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1520,6 +1520,9 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
if (Instruction *R = combineAddSubWithShlAddSub(Builder, I))
return R;
+ if (Instruction *R = foldAddLike(I))
+ return R;
+
Value *LHS = I.getOperand(0), *RHS = I.getOperand(1);
Type *Ty = I.getType();
if (Ty->isIntOrIntVectorTy(1))
@@ -2286,6 +2289,33 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
}
}
+ {
+ Value *W, *Z;
+ if (match(Op0, m_AddLike(m_Value(W), m_Value(X))) &&
+ match(Op1, m_AddLike(m_Value(Y), m_Value(Z)))) {
+ Instruction *R = nullptr;
+ if (W == Y)
+ R = BinaryOperator::CreateSub(X, Z);
+ if (W == Z)
+ R = BinaryOperator::CreateSub(X, Y);
+ if (X == Y)
+ R = BinaryOperator::CreateSub(W, Z);
+ if (X == Z)
+ R = BinaryOperator::CreateSub(W, Y);
+ if (R) {
+ bool NSW = I.hasNoSignedWrap() &&
+ match(Op0, m_NSWAddLike(m_Value(), m_Value())) &&
+ match(Op1, m_NSWAddLike(m_Value(), m_Value()));
+
+ bool NUW = I.hasNoUnsignedWrap() &&
+ match(Op1, m_NUWAddLike(m_Value(), m_Value()));
+ R->setHasNoSignedWrap(NSW);
+ R->setHasNoUnsignedWrap(NUW);
+ return R;
+ }
+ }
+ }
+
// (~X) - (~Y) --> Y - X
{
// Need to ensure we can consume at least one of the `not` instructions,
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index b703bc7d04db58..8a2998e1de6430 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3576,6 +3576,9 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
if (Instruction *R = tryFoldInstWithCtpopWithNot(&I))
return R;
+ if (Instruction *R = foldAddLike(I))
+ return R;
+
Value *X, *Y;
const APInt *CV;
if (match(&I, m_c_Or(m_OneUse(m_Xor(m_Value(X), m_APInt(CV))), m_Value(Y))) &&
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index a0e846c3b5a566..e94f118ab3808a 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -585,6 +585,9 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
FPClassTest DemandedMask, KnownFPClass &Known,
unsigned Depth = 0);
+ /// Common transforms for add / disjoint or
+ Instruction *foldAddLike(BinaryOperator &I);
+
/// Canonicalize the position of binops relative to shufflevector.
Instruction *foldVectorBinop(BinaryOperator &Inst);
Instruction *foldVectorSelect(SelectInst &Sel);
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index c3f79fe4f901ad..3cdf693b66dfd1 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2008,6 +2008,32 @@ static bool shouldMergeGEPs(GEPOperator &GEP, GEPOperator &Src) {
return true;
}
+Instruction *InstCombinerImpl::foldAddLike(BinaryOperator &I) {
+ Value *LHS = I.getOperand(0);
+ Value *RHS = I.getOperand(1);
+ Value *A, *B, *C, *D;
+ if (match(LHS, m_Sub(m_Value(A), m_Value(B))) &&
+ match(RHS, m_Sub(m_Value(C), m_Value(D)))) {
+ Instruction *R = nullptr;
+ if (A == D)
+ R = BinaryOperator::CreateSub(C, B);
+ if (C == B)
+ R = BinaryOperator::CreateSub(A, D);
+ if (R) {
+ bool NSW = match(&I, m_NSWAddLike(m_Value(), m_Value())) &&
+ match(LHS, m_NSWSub(m_Value(), m_Value())) &&
+ match(RHS, m_NSWSub(m_Value(), m_Value()));
+
+ bool NUW = match(LHS, m_NUWSub(m_Value(), m_Value())) &&
+ match(RHS, m_NUWSub(m_Value(), m_Value()));
+ R->setHasNoSignedWrap(NSW);
+ R->setHasNoUnsignedWrap(NUW);
+ return R;
+ }
+ }
+ return nullptr;
+}
+
Instruction *InstCombinerImpl::foldVectorBinop(BinaryOperator &Inst) {
if (!isa<VectorType>(Inst.getType()))
return nullptr;
diff --git a/llvm/test/Transforms/InstCombine/fold-add-sub.ll b/llvm/test/Transforms/InstCombine/fold-add-sub.ll
new file mode 100644
index 00000000000000..bbb7bb67369e7f
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fold-add-sub.ll
@@ -0,0 +1,207 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @use.i8(i8)
+define i8 @test_add_nsw(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: @test_add_nsw(
+; CHECK-NEXT: [[LHS:%.*]] = add nsw i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[RHS:%.*]] = add nsw i8 [[X]], [[Z:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[LHS]])
+; CHECK-NEXT: call void @use.i8(i8 [[RHS]])
+; CHECK-NEXT: [[R:%.*]] = sub nsw i8 [[Y]], [[Z]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lhs = add nsw i8 %x, %y
+ %rhs = add nsw i8 %x, %z
+ call void @use.i8(i8 %lhs)
+ call void @use.i8(i8 %rhs)
+ %r = sub nsw i8 %lhs, %rhs
+ ret i8 %r
+}
+
+define i8 @test_add_nsw_no_prop(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: @test_add_nsw_no_prop(
+; CHECK-NEXT: [[LHS:%.*]] = add nsw i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[RHS:%.*]] = add nuw i8 [[X]], [[Z:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[LHS]])
+; CHECK-NEXT: call void @use.i8(i8 [[RHS]])
+; CHECK-NEXT: [[R:%.*]] = sub i8 [[Y]], [[Z]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lhs = add nsw i8 %x, %y
+ %rhs = add nuw i8 %x, %z
+ call void @use.i8(i8 %lhs)
+ call void @use.i8(i8 %rhs)
+ %r = sub nsw i8 %lhs, %rhs
+ ret i8 %r
+}
+
+define i8 @test_add(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: @test_add(
+; CHECK-NEXT: [[LHS:%.*]] = add i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[RHS:%.*]] = add i8 [[X]], [[Z:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[LHS]])
+; CHECK-NEXT: call void @use.i8(i8 [[RHS]])
+; CHECK-NEXT: [[R:%.*]] = sub i8 [[Y]], [[Z]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lhs = add i8 %x, %y
+ %rhs = add i8 %x, %z
+ call void @use.i8(i8 %lhs)
+ call void @use.i8(i8 %rhs)
+ %r = sub i8 %lhs, %rhs
+ ret i8 %r
+}
+
+define i8 @test_add_fail(i8 %w, i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: @test_add_fail(
+; CHECK-NEXT: [[LHS:%.*]] = add i8 [[W:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[RHS:%.*]] = add i8 [[X:%.*]], [[Z:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[LHS]])
+; CHECK-NEXT: call void @use.i8(i8 [[RHS]])
+; CHECK-NEXT: [[R:%.*]] = sub i8 [[LHS]], [[RHS]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lhs = add i8 %w, %y
+ %rhs = add i8 %x, %z
+ call void @use.i8(i8 %lhs)
+ call void @use.i8(i8 %rhs)
+ %r = sub i8 %lhs, %rhs
+ ret i8 %r
+}
+
+define i8 @test_add_nuw(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: @test_add_nuw(
+; CHECK-NEXT: [[LHS:%.*]] = add i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[RHS:%.*]] = or disjoint i8 [[X]], [[Z:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[LHS]])
+; CHECK-NEXT: call void @use.i8(i8 [[RHS]])
+; CHECK-NEXT: [[R:%.*]] = sub nuw i8 [[Y]], [[Z]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lhs = add i8 %x, %y
+ %rhs = or disjoint i8 %x, %z
+ call void @use.i8(i8 %lhs)
+ call void @use.i8(i8 %rhs)
+ %r = sub nuw i8 %lhs, %rhs
+ ret i8 %r
+}
+
+define i8 @test_add_nuw_no_prop(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: @test_add_nuw_no_prop(
+; CHECK-NEXT: [[LHS:%.*]] = add i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[RHS:%.*]] = or disjoint i8 [[X]], [[Z:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[LHS]])
+; CHECK-NEXT: call void @use.i8(i8 [[RHS]])
+; CHECK-NEXT: [[R:%.*]] = sub i8 [[Y]], [[Z]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lhs = add i8 %x, %y
+ %rhs = or disjoint i8 %x, %z
+ call void @use.i8(i8 %lhs)
+ call void @use.i8(i8 %rhs)
+ %r = sub i8 %lhs, %rhs
+ ret i8 %r
+}
+
+define i8 @test_sub_nuw(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: @test_sub_nuw(
+; CHECK-NEXT: [[LHS:%.*]] = sub nuw i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[RHS:%.*]] = sub nuw i8 [[Y]], [[Z:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[LHS]])
+; CHECK-NEXT: call void @use.i8(i8 [[RHS]])
+; CHECK-NEXT: [[R:%.*]] = sub nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lhs = sub nuw i8 %x, %y
+ %rhs = sub nuw i8 %y, %z
+ call void @use.i8(i8 %lhs)
+ call void @use.i8(i8 %rhs)
+ %r = add i8 %lhs, %rhs
+ ret i8 %r
+}
+
+define i8 @test_sub_nuw_no_prop(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: @test_sub_nuw_no_prop(
+; CHECK-NEXT: [[LHS:%.*]] = sub nuw i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[RHS:%.*]] = sub i8 [[Y]], [[Z:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[LHS]])
+; CHECK-NEXT: call void @use.i8(i8 [[RHS]])
+; CHECK-NEXT: [[R:%.*]] = sub i8 [[X]], [[Z]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lhs = sub nuw i8 %x, %y
+ %rhs = sub i8 %y, %z
+ call void @use.i8(i8 %lhs)
+ call void @use.i8(i8 %rhs)
+ %r = add nuw i8 %lhs, %rhs
+ ret i8 %r
+}
+
+define i8 @test_sub_nsw(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: @test_sub_nsw(
+; CHECK-NEXT: [[LHS:%.*]] = sub nsw i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[RHS:%.*]] = sub nsw i8 [[Y]], [[Z:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[LHS]])
+; CHECK-NEXT: call void @use.i8(i8 [[RHS]])
+; CHECK-NEXT: [[R:%.*]] = sub nsw i8 [[X]], [[Z]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lhs = sub nsw i8 %x, %y
+ %rhs = sub nsw i8 %y, %z
+ call void @use.i8(i8 %lhs)
+ call void @use.i8(i8 %rhs)
+ %r = or disjoint i8 %lhs, %rhs
+ ret i8 %r
+}
+
+define i8 @test_sub_nsw_no_prop(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: @test_sub_nsw_no_prop(
+; CHECK-NEXT: [[LHS:%.*]] = sub i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[RHS:%.*]] = sub nsw i8 [[Y]], [[Z:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[LHS]])
+; CHECK-NEXT: call void @use.i8(i8 [[RHS]])
+; CHECK-NEXT: [[R:%.*]] = sub i8 [[X]], [[Z]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lhs = sub i8 %x, %y
+ %rhs = sub nsw i8 %y, %z
+ call void @use.i8(i8 %lhs)
+ call void @use.i8(i8 %rhs)
+ %r = or disjoint i8 %lhs, %rhs
+ ret i8 %r
+}
+
+define i8 @test_sub_none(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: @test_sub_none(
+; CHECK-NEXT: [[LHS:%.*]] = sub i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[RHS:%.*]] = sub i8 [[Y]], [[Z:%.*]]
+; CHECK-NEXT: call void @use.i8(i8 [[LHS]])
+; CHECK-NEXT: call void @use.i8(i8 [[RHS]])
+; CHECK-NEXT: [[R:%.*]] = sub i8 [[X]], [[Z]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lhs = sub i8 %x, %y
+ %rhs = sub i8 %y, %z
+ call void @use.i8(i8 %lhs)
+ call void @use.i8(i8 %rhs)
+ %r = add i8 %lhs, %rhs
+ ret i8 %r
+}
+
+define i8 @test_sub_none_fail(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: @test_sub_none_fail(
+; CHECK-NEXT: [[LHS:%.*]] = sub i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[RHS:%.*]] = sub i8 [[Z:%.*]], [[Y]]
+; CHECK-NEXT: call void @use.i8(i8 [[LHS]])
+; CHECK-NEXT: call void @use.i8(i8 [[RHS]])
+; CHECK-NEXT: [[R:%.*]] = add i8 [[LHS]], [[RHS]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %lhs = sub i8 %x, %y
+ %rhs = sub i8 %z, %y
+ call void @use.i8(i8 %lhs)
+ call void @use.i8(i8 %rhs)
+ %r = add i8 %lhs, %rhs
+ ret i8 %r
+}
|
(add/sub (sub/add) (sub/add))
irrelivant of use-count
NB regarding if this patch exists, saw changes in: ./lib/Demangle/MicrosoftDemangle.cpp when bootstrapping LLVM (changes in final output of O3 pipeline). |
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 patch causes clang to miscompile lief:
Reproducer:
static inline unsigned char mbedtls_ct_uchar_in_range_if(unsigned char low,
unsigned char high,
unsigned char c,
unsigned char t)
{
const unsigned char co = (unsigned char) (c);
const unsigned char to = (unsigned char) (t);
/* low_mask is: 0 if low <= c, 0x...ff if low > c */
unsigned low_mask = ((unsigned) co - low) >> 8;
/* high_mask is: 0 if c <= high, 0x...ff if c > high */
unsigned high_mask = ((unsigned) high - co) >> 8;
return (unsigned char) (~(low_mask | high_mask)) & to;
}
unsigned char mbedtls_ct_base64_enc_char(unsigned char value)
{
unsigned char digit = 0;
/* For each range of values, if value is in that range, mask digit with
* the corresponding value. Since value can only be in a single range,
* only at most one masking will change digit. */
digit |= mbedtls_ct_uchar_in_range_if(0, 25, value, 'A' + value);
digit |= mbedtls_ct_uchar_in_range_if(26, 51, value, 'a' + value - 26);
digit |= mbedtls_ct_uchar_in_range_if(52, 61, value, '0' + value - 52);
digit |= mbedtls_ct_uchar_in_range_if(62, 62, value, '+');
digit |= mbedtls_ct_uchar_in_range_if(63, 63, value, '/');
return digit;
}
int main() {
return mbedtls_ct_base64_enc_char(0);
}
It should map 0
to 65(ascii 'A')
, not -1
.
[... snip ...]
Fixed... wasnt guarding the |
d437f5c
to
fee4823
Compare
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.
LG
@@ -2008,6 +2008,30 @@ static bool shouldMergeGEPs(GEPOperator &GEP, GEPOperator &Src) { | |||
return true; | |||
} | |||
|
|||
Instruction *InstCombinerImpl::foldAddLike(Value *LHS, Value *RHS, bool NSW, |
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 also add foldAddLikeCommutative()
which is invoked with both LHS/RHS orders, as most add folds with non-constant RHS will want to check both orders.
Then your pattern here will reduce down to:
if (match(LHS, m_Sub(m_Value(A), m_Value(B))) &&
match(RHS, m_Sub(m_Value(C), m_Specific(A)))) {
…use-count Added folds: - `(add (sub X, Y), (sub Z, X))` -> `(sub Z, Y)` - `(sub (add X, Y), (add X, Z))` -> `(sub Y, Z)` The fold typically is handled in the `Reassosiate` pass, but it fails if the inner `sub`/`add` are multi-use. Less importantly, Reassosiate doesn't propagate flags correctly. This patch adds the fold explicitly the InstCombine Proofs: https://alive2.llvm.org/ce/z/p6JyRP
fee4823
to
6d62821
Compare
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
@@ -2008,6 +2008,24 @@ static bool shouldMergeGEPs(GEPOperator &GEP, GEPOperator &Src) { | |||
return true; | |||
} | |||
|
|||
Instruction *InstCombinerImpl::foldAddLikeCommutative(Value *LHS, Value *RHS, | |||
bool NSW, bool NUW) { |
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.
Please move this into InstCombineAddSub.cpp. It will make it easier to move folds if necessary.
(add/sub (sub/add) (sub/add))
irrelivant of use-count(add/sub (sub/add) (sub/add))
irrelevant of use-count
(add/sub (sub/add) (sub/add))
; NFC(add/sub (sub/add) (sub/add))
irrelivant of use-countAdded folds:
-
(add (sub X, Y), (sub Z, X))
->(sub Z, Y)
-
(sub (add X, Y), (add X, Z))
->(sub Y, Z)
The fold typically is handled in the
Reassosiate
pass, but it failsif the inner
sub
/add
are multi-use. Less importantly, Reassosiatedoesn't propagate flags correctly.
This patch adds the fold explicitly the InstCombine
Proofs: https://alive2.llvm.org/ce/z/p6JyRP