}
}
- // i1 mul -> i1 and.
+ // Fold the following two scenarios:
+ // 1) i1 mul -> i1 and.
+ // 2) X * Y --> X & Y, iff X, Y can be only {0,1}.
+ // Note: We could use known bits to generalize this and related patterns with
+ // shifts/truncs
Type *Ty = I.getType();
- if (Ty->isIntOrIntVectorTy(1))
+ if (Ty->isIntOrIntVectorTy(1) ||
+ (match(Op0, m_And(m_Value(), m_One())) &&
+ match(Op1, m_And(m_Value(), m_One()))))
return BinaryOperator::CreateAnd(Op0, Op1);
// X*(1 << Y) --> X << Y
; Scalar tests
define i64 @scalar_mul_bit_x0_y0(i64 %x, i64 %y) {
; CHECK-LABEL: @scalar_mul_bit_x0_y0(
+; CHECK-NEXT: [[AND2:%.*]] = and i64 [[Y:%.*]], 1
+; CHECK-NEXT: [[MUL:%.*]] = and i64 [[AND2]], [[X:%.*]]
+; CHECK-NEXT: ret i64 [[MUL]]
+;
+ %and1 = and i64 %x, 1
+ %and2 = and i64 %y, 1
+ %mul = mul i64 %and1, %and2
+ ret i64 %mul
+}
+
+declare void @use(i64)
+
+define i64 @scalar_mul_bit_x0_y0_uses(i64 %x, i64 %y) {
+; CHECK-LABEL: @scalar_mul_bit_x0_y0_uses(
; CHECK-NEXT: [[AND1:%.*]] = and i64 [[X:%.*]], 1
+; CHECK-NEXT: call void @use(i64 [[AND1]])
; CHECK-NEXT: [[AND2:%.*]] = and i64 [[Y:%.*]], 1
-; CHECK-NEXT: [[MUL:%.*]] = mul nuw nsw i64 [[AND1]], [[AND2]]
+; CHECK-NEXT: call void @use(i64 [[AND2]])
+; CHECK-NEXT: [[MUL:%.*]] = and i64 [[AND2]], [[X]]
; CHECK-NEXT: ret i64 [[MUL]]
;
%and1 = and i64 %x, 1
+ call void @use(i64 %and1)
%and2 = and i64 %y, 1
+ call void @use(i64 %and2)
%mul = mul i64 %and1, %and2
ret i64 %mul
}
; Vector tests
define <2 x i64> @vector_mul_bit_x0_y0(<2 x i64> %x, <2 x i64> %y) {
; CHECK-LABEL: @vector_mul_bit_x0_y0(
-; CHECK-NEXT: [[AND1:%.*]] = and <2 x i64> [[X:%.*]], <i64 1, i64 1>
; CHECK-NEXT: [[AND2:%.*]] = and <2 x i64> [[Y:%.*]], <i64 1, i64 1>
-; CHECK-NEXT: [[MUL:%.*]] = mul nuw nsw <2 x i64> [[AND1]], [[AND2]]
+; CHECK-NEXT: [[MUL:%.*]] = and <2 x i64> [[AND2]], [[X:%.*]]
; CHECK-NEXT: ret <2 x i64> [[MUL]]
;
%and1 = and <2 x i64> %x, <i64 1, i64 1>