[llvm] [InstCombine] Fold abs(a * abs(b)) --> abs(a * b) (PR #78110)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Feb 17 10:24:44 PST 2024
https://github.com/elhewaty updated https://github.com/llvm/llvm-project/pull/78110
>From 346d4497850a102e77ca2d607a2b097a48e82e48 Mon Sep 17 00:00:00 2001
From: Mohamed Atef <mohamedatef1698 at gmail.com>
Date: Sun, 14 Jan 2024 18:36:27 +0200
Subject: [PATCH 1/2] [InstCombine] Add test coverage for abs(a * abs(b)) -->
abs(a * b) (NFC)
---
.../Transforms/InstCombine/abs-intrinsic.ll | 138 ++++++++++++++++++
1 file changed, 138 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/abs-intrinsic.ll b/llvm/test/Transforms/InstCombine/abs-intrinsic.ll
index 7fe34d92376485..100b63ea3183d0 100644
--- a/llvm/test/Transforms/InstCombine/abs-intrinsic.ll
+++ b/llvm/test/Transforms/InstCombine/abs-intrinsic.ll
@@ -5,7 +5,145 @@ declare i8 @llvm.abs.i8(i8, i1)
declare i32 @llvm.abs.i32(i32, i1)
declare <4 x i32> @llvm.abs.v4i32(<4 x i32>, i1)
declare <3 x i82> @llvm.abs.v3i82(<3 x i82>, i1)
+declare <2 x i8> @llvm.abs.v2i8(<2 x i8>, i1)
declare void @llvm.assume(i1)
+declare void @use(i32)
+
+define i8 @test_abs_abs_a_mul_b_i8(i8 %a, i8 %b) {
+; CHECK-LABEL: @test_abs_abs_a_mul_b_i8(
+; CHECK-NEXT: [[ABS1:%.*]] = call i8 @llvm.abs.i8(i8 [[A:%.*]], i1 true)
+; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[ABS1]], [[B:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i8 @llvm.abs.i8(i8 [[MUL]], i1 true)
+; CHECK-NEXT: ret i8 [[ABS2]]
+;
+ %abs1 = call i8 @llvm.abs.i8(i8 %a, i1 true)
+ %mul = mul i8 %abs1, %b
+ %abs2 = call i8 @llvm.abs.i8(i8 %mul, i1 true)
+ ret i8 %abs2
+}
+
+define i8 @test_abs_a_mul_abs_b_i8(i8 %a, i8 %b) {
+; CHECK-LABEL: @test_abs_a_mul_abs_b_i8(
+; CHECK-NEXT: [[A1:%.*]] = urem i8 123, [[A:%.*]]
+; CHECK-NEXT: [[ABS1:%.*]] = call i8 @llvm.abs.i8(i8 [[B:%.*]], i1 true)
+; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[A1]], [[ABS1]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i8 @llvm.abs.i8(i8 [[MUL]], i1 true)
+; CHECK-NEXT: ret i8 [[ABS2]]
+;
+ %a1 = urem i8 123, %a
+ %abs1 = call i8 @llvm.abs.i8(i8 %b, i1 true)
+ %mul = mul i8 %a1, %abs1 ; thwart complexity-based canonicalization
+ %abs2 = call i8 @llvm.abs.i8(i8 %mul, i1 true)
+ ret i8 %abs2
+}
+
+define i32 @test_abs_abs_a_mul_b_i32(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_abs_abs_a_mul_b_i32(
+; CHECK-NEXT: [[ABS1:%.*]] = call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 true)
+; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[ABS1]], [[B:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i32 @llvm.abs.i32(i32 [[MUL]], i1 true)
+; CHECK-NEXT: ret i32 [[ABS2]]
+;
+ %abs1 = call i32 @llvm.abs.i32(i32 %a, i1 true)
+ %mul = mul i32 %abs1, %b
+ %abs2 = call i32 @llvm.abs.i32(i32 %mul, i1 true)
+ ret i32 %abs2
+}
+
+define i32 @test_abs_abs_a_mul_b_i32_abs_false_true(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_abs_abs_a_mul_b_i32_abs_false_true(
+; CHECK-NEXT: [[ABS1:%.*]] = call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 false)
+; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[ABS1]], [[B:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i32 @llvm.abs.i32(i32 [[MUL]], i1 true)
+; CHECK-NEXT: ret i32 [[ABS2]]
+;
+ %abs1 = call i32 @llvm.abs.i32(i32 %a, i1 false)
+ %mul = mul i32 %abs1, %b
+ %abs2 = call i32 @llvm.abs.i32(i32 %mul, i1 true)
+ ret i32 %abs2
+}
+
+define i32 @test_abs_abs_a_mul_b_i32_abs_true_false(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_abs_abs_a_mul_b_i32_abs_true_false(
+; CHECK-NEXT: [[ABS1:%.*]] = call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 true)
+; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[ABS1]], [[B:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i32 @llvm.abs.i32(i32 [[MUL]], i1 false)
+; CHECK-NEXT: ret i32 [[ABS2]]
+;
+ %abs1 = call i32 @llvm.abs.i32(i32 %a, i1 true)
+ %mul = mul i32 %abs1, %b
+ %abs2 = call i32 @llvm.abs.i32(i32 %mul, i1 false)
+ ret i32 %abs2
+}
+
+define i32 @test_abs_abs_a_mul_b_i32_abs_false_false(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_abs_abs_a_mul_b_i32_abs_false_false(
+; CHECK-NEXT: [[ABS1:%.*]] = call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 false)
+; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[ABS1]], [[B:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i32 @llvm.abs.i32(i32 [[MUL]], i1 false)
+; CHECK-NEXT: ret i32 [[ABS2]]
+;
+ %abs1 = call i32 @llvm.abs.i32(i32 %a, i1 false)
+ %mul = mul i32 %abs1, %b
+ %abs2 = call i32 @llvm.abs.i32(i32 %mul, i1 false)
+ ret i32 %abs2
+}
+
+; this should work
+define i8 @test_nsw_with_true(i8 %a, i8 %b) {
+; CHECK-LABEL: @test_nsw_with_true(
+; CHECK-NEXT: [[ABS1:%.*]] = call i8 @llvm.abs.i8(i8 [[A:%.*]], i1 false)
+; CHECK-NEXT: [[MUL:%.*]] = mul nsw i8 [[ABS1]], [[B:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i8 @llvm.abs.i8(i8 [[MUL]], i1 true)
+; CHECK-NEXT: ret i8 [[ABS2]]
+;
+ %abs1 = call i8 @llvm.abs.i8(i8 %a, i1 false)
+ %mul = mul nsw i8 %abs1, %b
+ %abs2 = call i8 @llvm.abs.i8(i8 %mul, i1 true)
+ ret i8 %abs2
+}
+
+; this shouldn't work
+define i8 @test_nsw_with_false(i8 %a, i8 %b) {
+; CHECK-LABEL: @test_nsw_with_false(
+; CHECK-NEXT: [[ABS1:%.*]] = call i8 @llvm.abs.i8(i8 [[A:%.*]], i1 false)
+; CHECK-NEXT: [[MUL:%.*]] = mul nsw i8 [[ABS1]], [[B:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i8 @llvm.abs.i8(i8 [[MUL]], i1 false)
+; CHECK-NEXT: ret i8 [[ABS2]]
+;
+ %abs1 = call i8 @llvm.abs.i8(i8 %a, i1 false)
+ %mul = mul nsw i8 %abs1, %b
+ %abs2 = call i8 @llvm.abs.i8(i8 %mul, i1 false)
+ ret i8 %abs2
+}
+
+define i32 @test_abs_abs_a_mul_b_more_one_use(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_abs_abs_a_mul_b_more_one_use(
+; CHECK-NEXT: [[ABS1:%.*]] = call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 true)
+; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[ABS1]], [[B:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i32 @llvm.abs.i32(i32 [[MUL]], i1 false)
+; CHECK-NEXT: call void @use(i32 [[MUL]])
+; CHECK-NEXT: ret i32 [[ABS2]]
+;
+ %abs1 = call i32 @llvm.abs.i32(i32 %a, i1 true)
+ %mul = mul i32 %abs1, %b
+ %abs2 = call i32 @llvm.abs.i32(i32 %mul, i1 false)
+ call void @use(i32 %mul)
+ ret i32 %abs2
+}
+
+define <2 x i8> @test_abs_abs_a_mul_b_vector_i8(<2 x i8> %a, <2 x i8> %b) {
+; CHECK-LABEL: @test_abs_abs_a_mul_b_vector_i8(
+; CHECK-NEXT: [[ABS:%.*]] = call <2 x i8> @llvm.abs.v2i8(<2 x i8> [[A:%.*]], i1 true)
+; CHECK-NEXT: [[MUL:%.*]] = mul <2 x i8> [[ABS]], [[B:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call <2 x i8> @llvm.abs.v2i8(<2 x i8> [[MUL]], i1 true)
+; CHECK-NEXT: ret <2 x i8> [[ABS2]]
+;
+ %abs = call <2 x i8> @llvm.abs.v2i8(<2 x i8> %a, i1 true)
+ %mul = mul <2 x i8> %abs, %b
+ %abs2 = call <2 x i8> @llvm.abs.v2i8(<2 x i8> %mul, i1 true)
+ ret <2 x i8> %abs2
+}
; abs preserves trailing zeros so the second and is unneeded
define i32 @abs_trailing_zeros(i32 %x) {
>From 5397dc6c398807015be64ee2ad157ff3ec5940b3 Mon Sep 17 00:00:00 2001
From: Mohamed Atef <mohamedatef1698 at gmail.com>
Date: Sat, 17 Feb 2024 20:24:05 +0200
Subject: [PATCH 2/2] [InstCombine] Fold abs(a * abs(b)) --> abs(a * b)
---
.../InstCombine/InstCombineCalls.cpp | 12 +++++
.../Transforms/InstCombine/abs-intrinsic.ll | 47 ++++++++-----------
2 files changed, 31 insertions(+), 28 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 64fbd5543a9e20..89abc74f9c301b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -1579,6 +1579,18 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
if (match(IIOperand, m_Select(m_Value(), m_Neg(m_Value(X)), m_Deferred(X))))
return replaceOperand(*II, 0, X);
+ Value *Y;
+ // abs(a * abs(b)) -> abs(a * b)
+ if (match(IIOperand,
+ m_OneUse(m_c_Mul(m_Value(X),
+ m_Intrinsic<Intrinsic::abs>(m_Value(Y)))))) {
+ auto *XY =
+ (cast<Instruction>(IIOperand)->hasNoSignedWrap() && IntMinIsPoison)
+ ? Builder.CreateNSWMul(X, Y)
+ : Builder.CreateMul(X, Y);
+ return replaceOperand(*II, 0, XY);
+ }
+
if (std::optional<bool> Known =
getKnownSignOrZero(IIOperand, II, DL, &AC, &DT)) {
// abs(x) -> x if x >= 0 (include abs(x-y) --> x - y where x >= y)
diff --git a/llvm/test/Transforms/InstCombine/abs-intrinsic.ll b/llvm/test/Transforms/InstCombine/abs-intrinsic.ll
index 100b63ea3183d0..c2f3d8e5001f01 100644
--- a/llvm/test/Transforms/InstCombine/abs-intrinsic.ll
+++ b/llvm/test/Transforms/InstCombine/abs-intrinsic.ll
@@ -11,9 +11,8 @@ declare void @use(i32)
define i8 @test_abs_abs_a_mul_b_i8(i8 %a, i8 %b) {
; CHECK-LABEL: @test_abs_abs_a_mul_b_i8(
-; CHECK-NEXT: [[ABS1:%.*]] = call i8 @llvm.abs.i8(i8 [[A:%.*]], i1 true)
-; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[ABS1]], [[B:%.*]]
-; CHECK-NEXT: [[ABS2:%.*]] = call i8 @llvm.abs.i8(i8 [[MUL]], i1 true)
+; CHECK-NEXT: [[TMP1:%.*]] = mul i8 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i8 @llvm.abs.i8(i8 [[TMP1]], i1 true)
; CHECK-NEXT: ret i8 [[ABS2]]
;
%abs1 = call i8 @llvm.abs.i8(i8 %a, i1 true)
@@ -25,9 +24,8 @@ define i8 @test_abs_abs_a_mul_b_i8(i8 %a, i8 %b) {
define i8 @test_abs_a_mul_abs_b_i8(i8 %a, i8 %b) {
; CHECK-LABEL: @test_abs_a_mul_abs_b_i8(
; CHECK-NEXT: [[A1:%.*]] = urem i8 123, [[A:%.*]]
-; CHECK-NEXT: [[ABS1:%.*]] = call i8 @llvm.abs.i8(i8 [[B:%.*]], i1 true)
-; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[A1]], [[ABS1]]
-; CHECK-NEXT: [[ABS2:%.*]] = call i8 @llvm.abs.i8(i8 [[MUL]], i1 true)
+; CHECK-NEXT: [[TMP1:%.*]] = mul i8 [[A1]], [[B:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i8 @llvm.abs.i8(i8 [[TMP1]], i1 true)
; CHECK-NEXT: ret i8 [[ABS2]]
;
%a1 = urem i8 123, %a
@@ -39,9 +37,8 @@ define i8 @test_abs_a_mul_abs_b_i8(i8 %a, i8 %b) {
define i32 @test_abs_abs_a_mul_b_i32(i32 %a, i32 %b) {
; CHECK-LABEL: @test_abs_abs_a_mul_b_i32(
-; CHECK-NEXT: [[ABS1:%.*]] = call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 true)
-; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[ABS1]], [[B:%.*]]
-; CHECK-NEXT: [[ABS2:%.*]] = call i32 @llvm.abs.i32(i32 [[MUL]], i1 true)
+; CHECK-NEXT: [[TMP1:%.*]] = mul i32 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i32 @llvm.abs.i32(i32 [[TMP1]], i1 true)
; CHECK-NEXT: ret i32 [[ABS2]]
;
%abs1 = call i32 @llvm.abs.i32(i32 %a, i1 true)
@@ -52,9 +49,8 @@ define i32 @test_abs_abs_a_mul_b_i32(i32 %a, i32 %b) {
define i32 @test_abs_abs_a_mul_b_i32_abs_false_true(i32 %a, i32 %b) {
; CHECK-LABEL: @test_abs_abs_a_mul_b_i32_abs_false_true(
-; CHECK-NEXT: [[ABS1:%.*]] = call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 false)
-; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[ABS1]], [[B:%.*]]
-; CHECK-NEXT: [[ABS2:%.*]] = call i32 @llvm.abs.i32(i32 [[MUL]], i1 true)
+; CHECK-NEXT: [[TMP1:%.*]] = mul i32 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i32 @llvm.abs.i32(i32 [[TMP1]], i1 true)
; CHECK-NEXT: ret i32 [[ABS2]]
;
%abs1 = call i32 @llvm.abs.i32(i32 %a, i1 false)
@@ -65,9 +61,8 @@ define i32 @test_abs_abs_a_mul_b_i32_abs_false_true(i32 %a, i32 %b) {
define i32 @test_abs_abs_a_mul_b_i32_abs_true_false(i32 %a, i32 %b) {
; CHECK-LABEL: @test_abs_abs_a_mul_b_i32_abs_true_false(
-; CHECK-NEXT: [[ABS1:%.*]] = call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 true)
-; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[ABS1]], [[B:%.*]]
-; CHECK-NEXT: [[ABS2:%.*]] = call i32 @llvm.abs.i32(i32 [[MUL]], i1 false)
+; CHECK-NEXT: [[TMP1:%.*]] = mul i32 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i32 @llvm.abs.i32(i32 [[TMP1]], i1 false)
; CHECK-NEXT: ret i32 [[ABS2]]
;
%abs1 = call i32 @llvm.abs.i32(i32 %a, i1 true)
@@ -78,9 +73,8 @@ define i32 @test_abs_abs_a_mul_b_i32_abs_true_false(i32 %a, i32 %b) {
define i32 @test_abs_abs_a_mul_b_i32_abs_false_false(i32 %a, i32 %b) {
; CHECK-LABEL: @test_abs_abs_a_mul_b_i32_abs_false_false(
-; CHECK-NEXT: [[ABS1:%.*]] = call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 false)
-; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[ABS1]], [[B:%.*]]
-; CHECK-NEXT: [[ABS2:%.*]] = call i32 @llvm.abs.i32(i32 [[MUL]], i1 false)
+; CHECK-NEXT: [[TMP1:%.*]] = mul i32 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i32 @llvm.abs.i32(i32 [[TMP1]], i1 false)
; CHECK-NEXT: ret i32 [[ABS2]]
;
%abs1 = call i32 @llvm.abs.i32(i32 %a, i1 false)
@@ -92,9 +86,8 @@ define i32 @test_abs_abs_a_mul_b_i32_abs_false_false(i32 %a, i32 %b) {
; this should work
define i8 @test_nsw_with_true(i8 %a, i8 %b) {
; CHECK-LABEL: @test_nsw_with_true(
-; CHECK-NEXT: [[ABS1:%.*]] = call i8 @llvm.abs.i8(i8 [[A:%.*]], i1 false)
-; CHECK-NEXT: [[MUL:%.*]] = mul nsw i8 [[ABS1]], [[B:%.*]]
-; CHECK-NEXT: [[ABS2:%.*]] = call i8 @llvm.abs.i8(i8 [[MUL]], i1 true)
+; CHECK-NEXT: [[TMP1:%.*]] = mul nsw i8 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i8 @llvm.abs.i8(i8 [[TMP1]], i1 true)
; CHECK-NEXT: ret i8 [[ABS2]]
;
%abs1 = call i8 @llvm.abs.i8(i8 %a, i1 false)
@@ -103,12 +96,11 @@ define i8 @test_nsw_with_true(i8 %a, i8 %b) {
ret i8 %abs2
}
-; this shouldn't work
+; this should't propagate the nsw
define i8 @test_nsw_with_false(i8 %a, i8 %b) {
; CHECK-LABEL: @test_nsw_with_false(
-; CHECK-NEXT: [[ABS1:%.*]] = call i8 @llvm.abs.i8(i8 [[A:%.*]], i1 false)
-; CHECK-NEXT: [[MUL:%.*]] = mul nsw i8 [[ABS1]], [[B:%.*]]
-; CHECK-NEXT: [[ABS2:%.*]] = call i8 @llvm.abs.i8(i8 [[MUL]], i1 false)
+; CHECK-NEXT: [[TMP1:%.*]] = mul i8 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call i8 @llvm.abs.i8(i8 [[TMP1]], i1 false)
; CHECK-NEXT: ret i8 [[ABS2]]
;
%abs1 = call i8 @llvm.abs.i8(i8 %a, i1 false)
@@ -134,9 +126,8 @@ define i32 @test_abs_abs_a_mul_b_more_one_use(i32 %a, i32 %b) {
define <2 x i8> @test_abs_abs_a_mul_b_vector_i8(<2 x i8> %a, <2 x i8> %b) {
; CHECK-LABEL: @test_abs_abs_a_mul_b_vector_i8(
-; CHECK-NEXT: [[ABS:%.*]] = call <2 x i8> @llvm.abs.v2i8(<2 x i8> [[A:%.*]], i1 true)
-; CHECK-NEXT: [[MUL:%.*]] = mul <2 x i8> [[ABS]], [[B:%.*]]
-; CHECK-NEXT: [[ABS2:%.*]] = call <2 x i8> @llvm.abs.v2i8(<2 x i8> [[MUL]], i1 true)
+; CHECK-NEXT: [[TMP1:%.*]] = mul <2 x i8> [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT: [[ABS2:%.*]] = call <2 x i8> @llvm.abs.v2i8(<2 x i8> [[TMP1]], i1 true)
; CHECK-NEXT: ret <2 x i8> [[ABS2]]
;
%abs = call <2 x i8> @llvm.abs.v2i8(<2 x i8> %a, i1 true)
More information about the llvm-commits
mailing list