Skip to content

Commit ffd2633

Browse files
authored
[InstCombine] Fold mul (shr exact (X, N)), 2^N + 1 -> add (X , shr exact (X, N)) (#112407)
Alive2 Proofs: https://alive2.llvm.org/ce/z/aJnxyp https://alive2.llvm.org/ce/z/dyeGEv
1 parent 7050e7d commit ffd2633

File tree

2 files changed

+171
-0
lines changed

2 files changed

+171
-0
lines changed

llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,33 @@ Instruction *InstCombinerImpl::visitMul(BinaryOperator &I) {
255255
}
256256
}
257257

258+
// mul (shr exact X, N), (2^N + 1) -> add (X, shr exact (X, N))
259+
{
260+
Value *NewOp;
261+
const APInt *ShiftC;
262+
const APInt *MulAP;
263+
if (BitWidth > 2 &&
264+
match(&I, m_Mul(m_Exact(m_Shr(m_Value(NewOp), m_APInt(ShiftC))),
265+
m_APInt(MulAP))) &&
266+
(*MulAP - 1).isPowerOf2() && *ShiftC == MulAP->logBase2()) {
267+
Value *BinOp = Op0;
268+
BinaryOperator *OpBO = cast<BinaryOperator>(Op0);
269+
270+
// mul nuw (ashr exact X, N) -> add nuw (X, lshr exact (X, N))
271+
if (HasNUW && OpBO->getOpcode() == Instruction::AShr && OpBO->hasOneUse())
272+
BinOp = Builder.CreateLShr(NewOp, ConstantInt::get(Ty, *ShiftC), "",
273+
/*isExact=*/true);
274+
275+
auto *NewAdd = BinaryOperator::CreateAdd(NewOp, BinOp);
276+
if (HasNSW && (HasNUW || OpBO->getOpcode() == Instruction::LShr ||
277+
ShiftC->getZExtValue() < BitWidth - 1))
278+
NewAdd->setHasNoSignedWrap(true);
279+
280+
NewAdd->setHasNoUnsignedWrap(HasNUW);
281+
return NewAdd;
282+
}
283+
}
284+
258285
if (Op0->hasOneUse() && match(Op1, m_NegatedPower2())) {
259286
// Interpret X * (-1<<C) as (-X) * (1<<C) and try to sink the negation.
260287
// The "* (1<<C)" thus becomes a potential shifting opportunity.

llvm/test/Transforms/InstCombine/ashr-lshr.ll

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,4 +1077,148 @@ entry:
10771077
ret i32 %shr
10781078
}
10791079

1080+
define i32 @ashr_shift_mul(i32 %x) {
1081+
; CHECK-LABEL: @ashr_shift_mul(
1082+
; CHECK-NEXT: [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
1083+
; CHECK-NEXT: [[RES:%.*]] = add i32 [[X]], [[A]]
1084+
; CHECK-NEXT: ret i32 [[RES]]
1085+
;
1086+
%a = ashr exact i32 %x, 3
1087+
%res = mul i32 %a, 9
1088+
ret i32 %res
1089+
}
1090+
1091+
define i32 @ashr_shift_mul_nuw(i32 %x) {
1092+
; CHECK-LABEL: @ashr_shift_mul_nuw(
1093+
; CHECK-NEXT: [[TMP1:%.*]] = lshr exact i32 [[X:%.*]], 3
1094+
; CHECK-NEXT: [[RES:%.*]] = add nuw i32 [[X]], [[TMP1]]
1095+
; CHECK-NEXT: ret i32 [[RES]]
1096+
;
1097+
%a = ashr exact i32 %x, 3
1098+
%res = mul nuw i32 %a, 9
1099+
ret i32 %res
1100+
}
1101+
1102+
define i32 @ashr_shift_mul_nsw(i32 %x) {
1103+
; CHECK-LABEL: @ashr_shift_mul_nsw(
1104+
; CHECK-NEXT: [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
1105+
; CHECK-NEXT: [[RES:%.*]] = add nsw i32 [[X]], [[A]]
1106+
; CHECK-NEXT: ret i32 [[RES]]
1107+
;
1108+
%a = ashr exact i32 %x, 3
1109+
%res = mul nsw i32 %a, 9
1110+
ret i32 %res
1111+
}
1112+
1113+
define i32 @lshr_shift_mul_nuw(i32 %x) {
1114+
; CHECK-LABEL: @lshr_shift_mul_nuw(
1115+
; CHECK-NEXT: [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
1116+
; CHECK-NEXT: [[RES:%.*]] = add nuw i32 [[X]], [[A]]
1117+
; CHECK-NEXT: ret i32 [[RES]]
1118+
;
1119+
%a = lshr exact i32 %x, 3
1120+
%res = mul nuw i32 %a, 9
1121+
ret i32 %res
1122+
}
1123+
1124+
define i32 @lshr_shift_mul(i32 %x) {
1125+
; CHECK-LABEL: @lshr_shift_mul(
1126+
; CHECK-NEXT: [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
1127+
; CHECK-NEXT: [[RES:%.*]] = add i32 [[X]], [[A]]
1128+
; CHECK-NEXT: ret i32 [[RES]]
1129+
;
1130+
%a = lshr exact i32 %x, 3
1131+
%res = mul i32 %a, 9
1132+
ret i32 %res
1133+
}
1134+
1135+
define i32 @lshr_shift_mul_nsw(i32 %x) {
1136+
; CHECK-LABEL: @lshr_shift_mul_nsw(
1137+
; CHECK-NEXT: [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
1138+
; CHECK-NEXT: [[RES:%.*]] = add nsw i32 [[X]], [[A]]
1139+
; CHECK-NEXT: ret i32 [[RES]]
1140+
;
1141+
%a = lshr exact i32 %x, 3
1142+
%res = mul nsw i32 %a, 9
1143+
ret i32 %res
1144+
}
1145+
1146+
; Negative test
1147+
1148+
define i32 @lshr_no_exact(i32 %x) {
1149+
; CHECK-LABEL: @lshr_no_exact(
1150+
; CHECK-NEXT: [[A:%.*]] = lshr i32 [[X:%.*]], 3
1151+
; CHECK-NEXT: [[RES:%.*]] = mul nuw nsw i32 [[A]], 9
1152+
; CHECK-NEXT: ret i32 [[RES]]
1153+
;
1154+
%a = lshr i32 %x, 3
1155+
%res = mul nsw i32 %a, 9
1156+
ret i32 %res
1157+
}
1158+
1159+
; Negative test
1160+
1161+
define i32 @ashr_no_exact(i32 %x) {
1162+
; CHECK-LABEL: @ashr_no_exact(
1163+
; CHECK-NEXT: [[A:%.*]] = ashr i32 [[X:%.*]], 3
1164+
; CHECK-NEXT: [[RES:%.*]] = mul nsw i32 [[A]], 9
1165+
; CHECK-NEXT: ret i32 [[RES]]
1166+
;
1167+
%a = ashr i32 %x, 3
1168+
%res = mul nsw i32 %a, 9
1169+
ret i32 %res
1170+
}
1171+
1172+
define i32 @lshr_multiuse(i32 %x) {
1173+
; CHECK-LABEL: @lshr_multiuse(
1174+
; CHECK-NEXT: [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
1175+
; CHECK-NEXT: call void @use(i32 [[A]])
1176+
; CHECK-NEXT: [[RES:%.*]] = add nsw i32 [[X]], [[A]]
1177+
; CHECK-NEXT: ret i32 [[RES]]
1178+
;
1179+
%a = lshr exact i32 %x, 3
1180+
call void @use(i32 %a)
1181+
%res = mul nsw i32 %a, 9
1182+
ret i32 %res
1183+
}
1184+
1185+
define i32 @lshr_multiuse_no_flags(i32 %x) {
1186+
; CHECK-LABEL: @lshr_multiuse_no_flags(
1187+
; CHECK-NEXT: [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
1188+
; CHECK-NEXT: call void @use(i32 [[A]])
1189+
; CHECK-NEXT: [[RES:%.*]] = add i32 [[X]], [[A]]
1190+
; CHECK-NEXT: ret i32 [[RES]]
1191+
;
1192+
%a = lshr exact i32 %x, 3
1193+
call void @use(i32 %a)
1194+
%res = mul i32 %a, 9
1195+
ret i32 %res
1196+
}
1197+
1198+
define i32 @ashr_multiuse_no_flags(i32 %x) {
1199+
; CHECK-LABEL: @ashr_multiuse_no_flags(
1200+
; CHECK-NEXT: [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
1201+
; CHECK-NEXT: call void @use(i32 [[A]])
1202+
; CHECK-NEXT: [[RES:%.*]] = add i32 [[X]], [[A]]
1203+
; CHECK-NEXT: ret i32 [[RES]]
1204+
;
1205+
%a = ashr exact i32 %x, 3
1206+
call void @use(i32 %a)
1207+
%res = mul i32 %a, 9
1208+
ret i32 %res
1209+
}
1210+
1211+
define i32 @ashr_multiuse(i32 %x) {
1212+
; CHECK-LABEL: @ashr_multiuse(
1213+
; CHECK-NEXT: [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
1214+
; CHECK-NEXT: call void @use(i32 [[A]])
1215+
; CHECK-NEXT: [[RES:%.*]] = add nsw i32 [[X]], [[A]]
1216+
; CHECK-NEXT: ret i32 [[RES]]
1217+
;
1218+
%a = ashr exact i32 %x, 3
1219+
call void @use(i32 %a)
1220+
%res = mul nsw i32 %a, 9
1221+
ret i32 %res
1222+
}
1223+
10801224
declare void @use(i32)

0 commit comments

Comments
 (0)