Skip to content

Commit 1762f16

Browse files
authored
[InstCombine] Fold umax/umin(nuw_shl(z, x), nuw_shl(z, y)) -> nuw_shl(z, umax/umin(x, y)) and umax/umin(nuw_shl(x, z), nuw_shl(y, z)) -> nuw_shl(umax/umin(x, y), z) (llvm#131076)
- Closes llvm#129947 This PR introduces the following transformations: 1. `umax(nuw_shl(z, x), nuw_shl(z, y)) -> nuw_shl(z, umax(x, y))` 2. `umin(nuw_shl(z, x), nuw_shl(z, y)) -> nuw_shl(z, umin(x, y))` 3. `umax(nuw_shl(x, z), nuw_shl(y, z)) -> nuw_shl(umax(x, y),z)` 4. `umin(nuw_shl(x, z), nuw_shl(y, z)) -> nuw_shl(umin(x, y),z)` Alive2 live proof: - https://alive2.llvm.org/ce/z/6bM-p7 for 1 and 2 - https://alive2.llvm.org/ce/z/aqLRYA and https://alive2.llvm.org/ce/z/twoVhb for 3 and 4 repectively
1 parent 215c47e commit 1762f16

File tree

4 files changed

+716
-77
lines changed

4 files changed

+716
-77
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,7 +1554,11 @@ static bool leftDistributesOverRight(Instruction::BinaryOps LOp, bool HasNUW,
15541554
switch (ROp) {
15551555
case Intrinsic::umax:
15561556
case Intrinsic::umin:
1557-
return HasNUW && LOp == Instruction::Add;
1557+
if (HasNUW && LOp == Instruction::Add)
1558+
return true;
1559+
if (HasNUW && LOp == Instruction::Shl)
1560+
return true;
1561+
return false;
15581562
case Intrinsic::smax:
15591563
case Intrinsic::smin:
15601564
return HasNSW && LOp == Instruction::Add;
@@ -1592,29 +1596,37 @@ foldIntrinsicUsingDistributiveLaws(IntrinsicInst *II,
15921596
if (!leftDistributesOverRight(InnerOpcode, HasNUW, HasNSW, TopLevelOpcode))
15931597
return nullptr;
15941598

1595-
assert(II->isCommutative() && Op0->isCommutative() &&
1596-
"Only inner and outer commutative op codes are supported.");
1597-
15981599
Value *A = Op0->getOperand(0);
15991600
Value *B = Op0->getOperand(1);
16001601
Value *C = Op1->getOperand(0);
16011602
Value *D = Op1->getOperand(1);
16021603

1603-
// Attempts to swap variables such that A always equals C
1604-
if (A != C && A != D)
1605-
std::swap(A, B);
1606-
if (A == C || A == D) {
1607-
if (A != C)
1604+
// Attempts to swap variables such that A equals C or B equals D,
1605+
// if the inner operation is commutative.
1606+
if (Op0->isCommutative() && A != C && B != D) {
1607+
if (A == D || B == C)
16081608
std::swap(C, D);
1609+
else
1610+
return nullptr;
1611+
}
1612+
1613+
BinaryOperator *NewBinop;
1614+
if (A == C) {
16091615
Value *NewIntrinsic = Builder.CreateBinaryIntrinsic(TopLevelOpcode, B, D);
1610-
BinaryOperator *NewBinop =
1611-
cast<BinaryOperator>(Builder.CreateBinOp(InnerOpcode, NewIntrinsic, A));
1612-
NewBinop->setHasNoSignedWrap(HasNSW);
1613-
NewBinop->setHasNoUnsignedWrap(HasNUW);
1614-
return NewBinop;
1616+
NewBinop =
1617+
cast<BinaryOperator>(Builder.CreateBinOp(InnerOpcode, A, NewIntrinsic));
1618+
} else if (B == D) {
1619+
Value *NewIntrinsic = Builder.CreateBinaryIntrinsic(TopLevelOpcode, A, C);
1620+
NewBinop =
1621+
cast<BinaryOperator>(Builder.CreateBinOp(InnerOpcode, NewIntrinsic, B));
1622+
} else {
1623+
return nullptr;
16151624
}
16161625

1617-
return nullptr;
1626+
NewBinop->setHasNoUnsignedWrap(HasNUW);
1627+
NewBinop->setHasNoSignedWrap(HasNSW);
1628+
1629+
return NewBinop;
16181630
}
16191631

16201632
/// CallInst simplification. This mostly only handles folding of intrinsic
@@ -1887,6 +1899,7 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
18871899
if (Instruction *I = foldMaxMulShift(I1, I0))
18881900
return I;
18891901
}
1902+
18901903
// If both operands of unsigned min/max are sign-extended, it is still ok
18911904
// to narrow the operation.
18921905
[[fallthrough]];

llvm/test/Transforms/InstCombine/div-shift.ll

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,9 @@ define i8 @udiv_umin_(i8 %x, i8 %y, i8 %z) {
144144
; Negative test, extra use
145145
define i8 @udiv_umin_extra_use(i8 %x, i8 %y, i8 %z) {
146146
; CHECK-LABEL: @udiv_umin_extra_use(
147-
; CHECK-NEXT: [[Y2:%.*]] = shl nuw i8 1, [[Y:%.*]]
148-
; CHECK-NEXT: [[Z2:%.*]] = shl nuw i8 1, [[Z:%.*]]
149-
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[Y2]], i8 [[Z2]])
147+
; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.umin.i8(i8 [[Y:%.*]], i8 [[Z:%.*]])
148+
; CHECK-NEXT: [[M:%.*]] = shl nuw i8 1, [[TMP1]]
150149
; CHECK-NEXT: call void @use(i8 [[M]])
151-
; CHECK-NEXT: [[TMP1:%.*]] = call range(i8 0, 9) i8 @llvm.cttz.i8(i8 [[M]], i1 true)
152150
; CHECK-NEXT: [[D:%.*]] = lshr i8 [[X:%.*]], [[TMP1]]
153151
; CHECK-NEXT: ret i8 [[D]]
154152
;

0 commit comments

Comments
 (0)