[llvm] [InstCombine] Reducing rust i128::midpoint instructions (PR #99614)
Julius Alexandre via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 19 01:58:55 PDT 2024
https://github.com/medievalghoul updated https://github.com/llvm/llvm-project/pull/99614
>From 9cc66aeb0e039d4e78f626cb27c4333eceecb1fb Mon Sep 17 00:00:00 2001
From: medievalghoul <61852278+medievalghoul at users.noreply.github.com>
Date: Fri, 19 Jul 2024 03:30:01 -0400
Subject: [PATCH 1/2] [InstCombine] Reducing rust i128::midpoint instructions
---
.../InstCombine/xor_lshr_and_i128.ll | 142 ++++++++++++++++++
1 file changed, 142 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/xor_lshr_and_i128.ll
diff --git a/llvm/test/Transforms/InstCombine/xor_lshr_and_i128.ll b/llvm/test/Transforms/InstCombine/xor_lshr_and_i128.ll
new file mode 100644
index 0000000000000..a219a945722d7
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/xor_lshr_and_i128.ll
@@ -0,0 +1,142 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define noundef i128 @xor_lshr_and(i128 noundef %x, i128 noundef %y) unnamed_addr #0 {
+; check-label: define noundef i128 @xor_lshr_and(
+; check-same: i128 noundef [[x:%.*]], i128 noundef [[y:%.*]]) unnamed_addr {
+; check-next: [[start:.*:]]
+; check-next: [[xor:%.*]] = xor i128 [[y]], [[x]]
+; check-next: [[lshr:%.*]] = lshr i128 [[xor]], 1
+; check-next: [[and:%.*]] = and i128 [[y]], [[x]]
+; check-next: [[add:%.*]] = add i128 [[lshr]], [[and]]
+; check-next: ret i128 [[add]]
+;
+; CHECK-LABEL: define noundef i128 @xor_lshr_and(
+; CHECK-SAME: i128 noundef [[X:%.*]], i128 noundef [[Y:%.*]]) unnamed_addr {
+; CHECK-NEXT: [[START:.*:]]
+; CHECK-NEXT: [[XOR:%.*]] = xor i128 [[Y]], [[X]]
+; CHECK-NEXT: [[LSHR:%.*]] = lshr i128 [[XOR]], 1
+; CHECK-NEXT: [[AND:%.*]] = and i128 [[Y]], [[X]]
+; CHECK-NEXT: [[ADD:%.*]] = add i128 [[LSHR]], [[AND]]
+; CHECK-NEXT: ret i128 [[ADD]]
+;
+start:
+ %xor = xor i128 %y, %x
+ %lshr = lshr i128 %xor, 1
+ %and = and i128 %y, %x
+ %add = add i128 %lshr, %and
+ ret i128 %add
+}
+
+define noundef i128 @xor_lshr_and_commuted1(i128 noundef %x, i128 noundef %y) unnamed_addr #0 {
+; CHECK-LABEL: define noundef i128 @xor_lshr_and_commuted1(
+; CHECK-SAME: i128 noundef [[X:%.*]], i128 noundef [[Y:%.*]]) unnamed_addr {
+; CHECK-NEXT: [[START:.*:]]
+; CHECK-NEXT: [[AND:%.*]] = and i128 [[Y]], [[X]]
+; CHECK-NEXT: [[XOR:%.*]] = xor i128 [[Y]], [[X]]
+; CHECK-NEXT: [[LSHR:%.*]] = lshr i128 [[XOR]], 1
+; CHECK-NEXT: [[ADD:%.*]] = add i128 [[LSHR]], [[AND]]
+; CHECK-NEXT: ret i128 [[ADD]]
+;
+start:
+ %and = and i128 %y, %x
+ %xor = xor i128 %y, %x
+ %lshr = lshr i128 %xor, 1
+ %add = add i128 %lshr, %and
+ ret i128 %add
+}
+
+define noundef i128 @xor_lshr_and_commuted2(i128 noundef %x, i128 noundef %y) unnamed_addr #0 {
+; CHECK-LABEL: define noundef i128 @xor_lshr_and_commuted2(
+; CHECK-SAME: i128 noundef [[X:%.*]], i128 noundef [[Y:%.*]]) unnamed_addr {
+; CHECK-NEXT: [[START:.*:]]
+; CHECK-NEXT: [[AND:%.*]] = and i128 [[Y]], [[X]]
+; CHECK-NEXT: [[XOR:%.*]] = xor i128 [[Y]], [[X]]
+; CHECK-NEXT: [[LSHR:%.*]] = lshr i128 [[XOR]], 1
+; CHECK-NEXT: [[ADD:%.*]] = add i128 [[LSHR]], [[AND]]
+; CHECK-NEXT: ret i128 [[ADD]]
+;
+start:
+ %and = and i128 %y, %x
+ %xor = xor i128 %y, %x
+ %lshr = lshr i128 %xor, 1
+ %add = add i128 %lshr, %and
+ ret i128 %add
+}
+
+declare void @use(i8)
+
+define noundef i128 @xor_lshr_and_multi_use(i128 noundef %x, i128 noundef %y) unnamed_addr #0 {
+; CHECK-LABEL: define noundef i128 @xor_lshr_and_multi_use(
+; CHECK-SAME: i128 noundef [[X:%.*]], i128 noundef [[Y:%.*]]) unnamed_addr {
+; CHECK-NEXT: [[START:.*:]]
+; CHECK-NEXT: [[XOR:%.*]] = xor i128 [[Y]], [[X]]
+; CHECK-NEXT: call void @use(i128 [[XOR]])
+; CHECK-NEXT: [[LSHR:%.*]] = lshr i128 [[XOR]], 1
+; CHECK-NEXT: call void @use(i128 [[LSHR]])
+; CHECK-NEXT: [[AND:%.*]] = and i128 [[Y]], [[X]]
+; CHECK-NEXT: [[ADD:%.*]] = add i128 [[LSHR]], [[AND]]
+; CHECK-NEXT: ret i128 [[ADD]]
+;
+start:
+ %xor = xor i128 %y, %x
+ call void @use(i128 %xor)
+ %lshr = lshr i128 %xor, 1
+ call void @use(i128 %lshr)
+ %and = and i128 %y, %x
+ %add = add i128 %lshr, %and
+ ret i128 %add
+}
+
+define noundef i128 @xor_lshr_and_negative(i128 noundef %x, i128 noundef %y) unnamed_addr #0 {
+; CHECK-LABEL: define noundef i128 @xor_lshr_and_negative(
+; CHECK-SAME: i128 noundef [[X:%.*]], i128 noundef [[Y:%.*]]) unnamed_addr {
+; CHECK-NEXT: [[START:.*:]]
+; CHECK-NEXT: [[XOR:%.*]] = xor i128 [[X]], -1
+; CHECK-NEXT: [[AND:%.*]] = and i128 [[Y]], [[X]]
+; CHECK-NEXT: [[ADD:%.*]] = add i128 [[AND]], [[XOR]]
+; CHECK-NEXT: ret i128 [[ADD]]
+;
+start:
+ %xor = xor i128 %x, -1
+ %and = and i128 %y, %x
+ %add = add i128 %xor, %and
+ ret i128 %add
+}
+
+define noundef i32 @xor_lshr_and_negative2(i32 noundef %x, i32 noundef %y) unnamed_addr #0 {
+; CHECK-LABEL: define noundef i32 @xor_lshr_and_negative2(
+; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) unnamed_addr {
+; CHECK-NEXT: [[START:.*:]]
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[Y]], [[X]]
+; CHECK-NEXT: [[LSHR:%.*]] = lshr i32 [[XOR]], 1
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[Y]], [[X]]
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[LSHR]], [[AND]]
+; CHECK-NEXT: ret i32 [[ADD]]
+;
+start:
+ %xor = xor i32 %y, %x
+ %lshr = lshr i32 %xor, 1
+ %and = and i32 %y, %x
+ %add = add i32 %lshr, %and
+ ret i32 %add
+}
+
+define noundef <2 x i128> @xor_lshr_and_vec(<2 x i128> noundef %x, <2 x i128> noundef %y) unnamed_addr #0 {
+; CHECK-LABEL: define noundef <2 x i128> @xor_lshr_and_vec(
+; CHECK-SAME: <2 x i128> noundef [[X:%.*]], <2 x i128> noundef [[Y:%.*]]) unnamed_addr {
+; CHECK-NEXT: [[START:.*:]]
+; CHECK-NEXT: [[XOR:%.*]] = xor <2 x i128> [[Y]], [[X]]
+; CHECK-NEXT: [[LSHR:%.*]] = lshr <2 x i128> [[XOR]], <i128 1, i128 1>
+; CHECK-NEXT: [[AND:%.*]] = and <2 x i128> [[Y]], [[X]]
+; CHECK-NEXT: [[ADD:%.*]] = add <2 x i128> [[LSHR]], [[AND]]
+; CHECK-NEXT: ret <2 x i128> [[ADD]]
+;
+start:
+ %xor = xor <2 x i128> %y, %x
+ %lshr = lshr <2 x i128> %xor, <i128 1, i128 1>
+ %and = and <2 x i128> %y, %x
+ %add = add <2 x i128> %lshr, %and
+ ret <2 x i128> %add
+}
+
>From 87a34e6a2b143b822177422c1853a885371b60fa Mon Sep 17 00:00:00 2001
From: medievalghoul <61852278+medievalghoul at users.noreply.github.com>
Date: Fri, 19 Jul 2024 04:51:50 -0400
Subject: [PATCH 2/2] added the transformations
---
.../InstCombine/InstCombineAddSub.cpp | 44 +++++++++++++++++++
.../InstCombine/xor_lshr_and_i128.ll | 32 +++++++++-----
2 files changed, 65 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 0a55f4762fdf0..b857150fec2a3 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1483,6 +1483,47 @@ static Instruction *foldBoxMultiply(BinaryOperator &I) {
return nullptr;
}
+// Relating to the i128::midpoint in rust nightly
+// we're finding this expression: ((Y ^ X) >> 1 + (Y & X))
+// with the i128 data type and reducing the amount of asm instructions
+// generated.
+static Instruction *foldMidpointExpression(BinaryOperator &I) {
+ Value *X, *Y;
+
+ if (!I.getType()->isIntegerTy(128))
+ return nullptr;
+
+ if (!match(&I, m_Add(m_LShr(m_Xor(m_Value(X), m_Value(Y)), m_ConstantInt()),
+ m_And(m_Value(X), m_Value(Y)))))
+ return nullptr;
+
+ IRBuilder<> Builder(&I);
+ Module *Mod = I.getModule();
+
+ // Create the call llvm.uadd.with.overflow.i128
+ Function *UAddWithOverflow = Intrinsic::getDeclaration(Mod, Intrinsic::uadd_with_overflow,
+ Type::getInt128Ty(Mod->getContext()));
+ CallInst *UAddCall = Builder.CreateCall(UAddWithOverflow, {Y, X});
+ UAddCall->setTailCall(); // Mark the call as a tail call
+
+ // Extract the sum and the
+ Value *Sum = Builder.CreateExtractValue(UAddCall, 0);
+ Value *Overflow = Builder.CreateExtractValue(UAddCall, 1);
+
+ // Create the right shift for the sum element of
+ // overflow
+ Value *LShrVal = Builder.CreateLShr(Sum, ConstantInt::get(Type::getInt128Ty(I.getContext()), 1));
+
+ // Create the select instruction
+ Value *SelectVal = Builder.CreateSelect(Overflow,
+ ConstantInt::get(Type::getInt128Ty(I.getContext()), APInt(128, 1).shl(127)),
+ ConstantInt::get(Type::getInt128Ty(I.getContext()), 0));
+
+ Instruction *OrVal = BinaryOperator::CreateOr(LShrVal, SelectVal);
+
+ return OrVal;
+}
+
Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
if (Value *V = simplifyAddInst(I.getOperand(0), I.getOperand(1),
I.hasNoSignedWrap(), I.hasNoUnsignedWrap(),
@@ -1505,6 +1546,9 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
if (Instruction *R = foldBoxMultiply(I))
return R;
+ if (Instruction *R = foldMidpointExpression(I))
+ return R;
+
if (Instruction *R = factorizeMathWithShlOps(I, Builder))
return R;
diff --git a/llvm/test/Transforms/InstCombine/xor_lshr_and_i128.ll b/llvm/test/Transforms/InstCombine/xor_lshr_and_i128.ll
index a219a945722d7..7f3da1ddad427 100644
--- a/llvm/test/Transforms/InstCombine/xor_lshr_and_i128.ll
+++ b/llvm/test/Transforms/InstCombine/xor_lshr_and_i128.ll
@@ -14,10 +14,12 @@ define noundef i128 @xor_lshr_and(i128 noundef %x, i128 noundef %y) unnamed_addr
; CHECK-LABEL: define noundef i128 @xor_lshr_and(
; CHECK-SAME: i128 noundef [[X:%.*]], i128 noundef [[Y:%.*]]) unnamed_addr {
; CHECK-NEXT: [[START:.*:]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i128 [[Y]], [[X]]
+; CHECK-NEXT: [[TMP0:%.*]] = tail call { i128, i1 } @llvm.uadd.with.overflow.i128(i128 [[X]], i128 [[Y]])
+; CHECK-NEXT: [[XOR:%.*]] = extractvalue { i128, i1 } [[TMP0]], 0
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { i128, i1 } [[TMP0]], 1
; CHECK-NEXT: [[LSHR:%.*]] = lshr i128 [[XOR]], 1
-; CHECK-NEXT: [[AND:%.*]] = and i128 [[Y]], [[X]]
-; CHECK-NEXT: [[ADD:%.*]] = add i128 [[LSHR]], [[AND]]
+; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP2]], i128 -170141183460469231731687303715884105728, i128 0
+; CHECK-NEXT: [[ADD:%.*]] = or disjoint i128 [[LSHR]], [[TMP4]]
; CHECK-NEXT: ret i128 [[ADD]]
;
start:
@@ -32,10 +34,12 @@ define noundef i128 @xor_lshr_and_commuted1(i128 noundef %x, i128 noundef %y) un
; CHECK-LABEL: define noundef i128 @xor_lshr_and_commuted1(
; CHECK-SAME: i128 noundef [[X:%.*]], i128 noundef [[Y:%.*]]) unnamed_addr {
; CHECK-NEXT: [[START:.*:]]
-; CHECK-NEXT: [[AND:%.*]] = and i128 [[Y]], [[X]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i128 [[Y]], [[X]]
+; CHECK-NEXT: [[TMP0:%.*]] = tail call { i128, i1 } @llvm.uadd.with.overflow.i128(i128 [[X]], i128 [[Y]])
+; CHECK-NEXT: [[XOR:%.*]] = extractvalue { i128, i1 } [[TMP0]], 0
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { i128, i1 } [[TMP0]], 1
; CHECK-NEXT: [[LSHR:%.*]] = lshr i128 [[XOR]], 1
-; CHECK-NEXT: [[ADD:%.*]] = add i128 [[LSHR]], [[AND]]
+; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP2]], i128 -170141183460469231731687303715884105728, i128 0
+; CHECK-NEXT: [[ADD:%.*]] = or disjoint i128 [[LSHR]], [[TMP4]]
; CHECK-NEXT: ret i128 [[ADD]]
;
start:
@@ -50,10 +54,12 @@ define noundef i128 @xor_lshr_and_commuted2(i128 noundef %x, i128 noundef %y) un
; CHECK-LABEL: define noundef i128 @xor_lshr_and_commuted2(
; CHECK-SAME: i128 noundef [[X:%.*]], i128 noundef [[Y:%.*]]) unnamed_addr {
; CHECK-NEXT: [[START:.*:]]
-; CHECK-NEXT: [[AND:%.*]] = and i128 [[Y]], [[X]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i128 [[Y]], [[X]]
+; CHECK-NEXT: [[TMP0:%.*]] = tail call { i128, i1 } @llvm.uadd.with.overflow.i128(i128 [[X]], i128 [[Y]])
+; CHECK-NEXT: [[XOR:%.*]] = extractvalue { i128, i1 } [[TMP0]], 0
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { i128, i1 } [[TMP0]], 1
; CHECK-NEXT: [[LSHR:%.*]] = lshr i128 [[XOR]], 1
-; CHECK-NEXT: [[ADD:%.*]] = add i128 [[LSHR]], [[AND]]
+; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP2]], i128 -170141183460469231731687303715884105728, i128 0
+; CHECK-NEXT: [[ADD:%.*]] = or disjoint i128 [[LSHR]], [[TMP4]]
; CHECK-NEXT: ret i128 [[ADD]]
;
start:
@@ -74,8 +80,12 @@ define noundef i128 @xor_lshr_and_multi_use(i128 noundef %x, i128 noundef %y) un
; CHECK-NEXT: call void @use(i128 [[XOR]])
; CHECK-NEXT: [[LSHR:%.*]] = lshr i128 [[XOR]], 1
; CHECK-NEXT: call void @use(i128 [[LSHR]])
-; CHECK-NEXT: [[AND:%.*]] = and i128 [[Y]], [[X]]
-; CHECK-NEXT: [[ADD:%.*]] = add i128 [[LSHR]], [[AND]]
+; CHECK-NEXT: [[TMP0:%.*]] = tail call { i128, i1 } @llvm.uadd.with.overflow.i128(i128 [[X]], i128 [[Y]])
+; CHECK-NEXT: [[TMP1:%.*]] = extractvalue { i128, i1 } [[TMP0]], 0
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { i128, i1 } [[TMP0]], 1
+; CHECK-NEXT: [[TMP3:%.*]] = lshr i128 [[TMP1]], 1
+; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP2]], i128 -170141183460469231731687303715884105728, i128 0
+; CHECK-NEXT: [[ADD:%.*]] = or disjoint i128 [[TMP3]], [[TMP4]]
; CHECK-NEXT: ret i128 [[ADD]]
;
start:
More information about the llvm-commits
mailing list