Files
clang-p2996/llvm/test/Transforms/InstCombine/mul.ll
Nikita Popov e4a4122eb6 [IR] Remove zext and sext constant expressions (#71040)
Remove support for zext and sext constant expressions. All places
creating them have been removed beforehand, so this just removes the
APIs and uses of these constant expressions in tests.

There is some additional cleanup that can be done on top of this, e.g.
we can remove the ZExtInst vs ZExtOperator footgun.

This is part of
https://discourse.llvm.org/t/rfc-remove-most-constant-expressions/63179.
2023-11-03 10:46:07 +01:00

1790 lines
52 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
declare i32 @llvm.abs.i32(i32, i1)
define i32 @pow2_multiplier(i32 %A) {
; CHECK-LABEL: @pow2_multiplier(
; CHECK-NEXT: [[B:%.*]] = shl i32 [[A:%.*]], 1
; CHECK-NEXT: ret i32 [[B]]
;
%B = mul i32 %A, 2
ret i32 %B
}
define <2 x i32> @pow2_multiplier_vec(<2 x i32> %A) {
; CHECK-LABEL: @pow2_multiplier_vec(
; CHECK-NEXT: [[B:%.*]] = shl <2 x i32> [[A:%.*]], <i32 3, i32 3>
; CHECK-NEXT: ret <2 x i32> [[B]]
;
%B = mul <2 x i32> %A, <i32 8, i32 8>
ret <2 x i32> %B
}
define i8 @combine_shl(i8 %A) {
; CHECK-LABEL: @combine_shl(
; CHECK-NEXT: [[C:%.*]] = shl i8 [[A:%.*]], 6
; CHECK-NEXT: ret i8 [[C]]
;
%B = mul i8 %A, 8
%C = mul i8 %B, 8
ret i8 %C
}
define i32 @neg(i32 %i) {
; CHECK-LABEL: @neg(
; CHECK-NEXT: [[T:%.*]] = sub i32 0, [[I:%.*]]
; CHECK-NEXT: ret i32 [[T]]
;
%t = mul i32 %i, -1
ret i32 %t
}
; Use the sign-bit as a mask:
; (zext (A < 0)) * B --> (A >> 31) & B
define i32 @test10(i32 %a, i32 %b) {
; CHECK-LABEL: @test10(
; CHECK-NEXT: [[ISNEG:%.*]] = icmp slt i32 [[A:%.*]], 0
; CHECK-NEXT: [[E:%.*]] = select i1 [[ISNEG]], i32 [[B:%.*]], i32 0
; CHECK-NEXT: ret i32 [[E]]
;
%c = icmp slt i32 %a, 0
%d = zext i1 %c to i32
%e = mul i32 %d, %b
ret i32 %e
}
define i32 @test11(i32 %a, i32 %b) {
; CHECK-LABEL: @test11(
; CHECK-NEXT: [[ISNEG:%.*]] = icmp slt i32 [[A:%.*]], 0
; CHECK-NEXT: [[E:%.*]] = select i1 [[ISNEG]], i32 [[B:%.*]], i32 0
; CHECK-NEXT: ret i32 [[E]]
;
%c = icmp sle i32 %a, -1
%d = zext i1 %c to i32
%e = mul i32 %d, %b
ret i32 %e
}
declare void @use32(i32)
define i32 @test12(i32 %a, i32 %b) {
; CHECK-LABEL: @test12(
; CHECK-NEXT: [[A_LOBIT:%.*]] = lshr i32 [[A:%.*]], 31
; CHECK-NEXT: [[ISNEG:%.*]] = icmp slt i32 [[A]], 0
; CHECK-NEXT: [[E:%.*]] = select i1 [[ISNEG]], i32 [[B:%.*]], i32 0
; CHECK-NEXT: call void @use32(i32 [[A_LOBIT]])
; CHECK-NEXT: ret i32 [[E]]
;
%c = icmp ugt i32 %a, 2147483647
%d = zext i1 %c to i32
%e = mul i32 %d, %b
call void @use32(i32 %d)
ret i32 %e
}
; rdar://7293527
define i32 @shl1(i32 %a, i32 %b) {
; CHECK-LABEL: @shl1(
; CHECK-NEXT: [[M1:%.*]] = shl i32 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: ret i32 [[M1]]
;
%shl = shl i32 1, %b
%m = mul i32 %shl, %a
ret i32 %m
}
define i32 @shl1_nsw_nsw(i32 %A, i32 %B) {
; CHECK-LABEL: @shl1_nsw_nsw(
; CHECK-NEXT: [[D1:%.*]] = shl nsw i32 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: ret i32 [[D1]]
;
%shl = shl nsw i32 1, %B
%D = mul nsw i32 %A, %shl
ret i32 %D
}
define <2 x i32> @shl1_nsw_nsw_commute(<2 x i32> %A, <2 x i32> %B) {
; CHECK-LABEL: @shl1_nsw_nsw_commute(
; CHECK-NEXT: [[D1:%.*]] = shl nsw <2 x i32> [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: ret <2 x i32> [[D1]]
;
%shl = shl nsw <2 x i32> <i32 1, i32 poison>, %B
%D = mul nsw <2 x i32> %shl, %A
ret <2 x i32> %D
}
define i32 @shl1_nuw(i32 %A, i32 %B) {
; CHECK-LABEL: @shl1_nuw(
; CHECK-NEXT: [[D1:%.*]] = shl nuw i32 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: ret i32 [[D1]]
;
%shl = shl i32 1, %B
%D = mul nuw i32 %A, %shl
ret i32 %D
}
define i32 @shl1_nuw_commute(i32 %A, i32 %B) {
; CHECK-LABEL: @shl1_nuw_commute(
; CHECK-NEXT: [[D1:%.*]] = shl i32 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: ret i32 [[D1]]
;
%shl = shl nuw i32 1, %B
%D = mul i32 %shl, %A
ret i32 %D
}
define i32 @shl1_nsw(i32 %A) {
; CHECK-LABEL: @shl1_nsw(
; CHECK-NEXT: [[SHL:%.*]] = shl nuw i32 1, [[A:%.*]]
; CHECK-NEXT: [[C1:%.*]] = shl i32 [[SHL]], [[A]]
; CHECK-NEXT: ret i32 [[C1]]
;
%shl = shl i32 1, %A
%C = mul nsw i32 %shl, %shl
ret i32 %C
}
define i5 @shl1_increment(i5 %x, i5 %y) {
; CHECK-LABEL: @shl1_increment(
; CHECK-NEXT: [[Y_FR:%.*]] = freeze i5 [[Y:%.*]]
; CHECK-NEXT: [[MULSHL:%.*]] = shl i5 [[Y_FR]], [[X:%.*]]
; CHECK-NEXT: [[M1:%.*]] = add i5 [[MULSHL]], [[Y_FR]]
; CHECK-NEXT: ret i5 [[M1]]
;
%pow2x = shl i5 1, %x
%x1 = add i5 %pow2x, 1
%m = mul i5 %x1, %y
ret i5 %m
}
define <3 x i5> @shl1_nuw_increment_commute(<3 x i5> %x, <3 x i5> noundef %p) {
; CHECK-LABEL: @shl1_nuw_increment_commute(
; CHECK-NEXT: [[Y:%.*]] = ashr <3 x i5> [[P:%.*]], <i5 1, i5 1, i5 1>
; CHECK-NEXT: [[MULSHL:%.*]] = shl nuw <3 x i5> [[Y]], [[X:%.*]]
; CHECK-NEXT: [[M1:%.*]] = add nuw <3 x i5> [[MULSHL]], [[Y]]
; CHECK-NEXT: ret <3 x i5> [[M1]]
;
%y = ashr <3 x i5> %p, <i5 1, i5 1, i5 1> ; thwart complexity-based canonicalization
%pow2x = shl <3 x i5> <i5 1, i5 poison, i5 1>, %x
%x1 = add <3 x i5> %pow2x, <i5 1, i5 poison, i5 1>
%m = mul nuw <3 x i5> %y, %x1
ret <3 x i5> %m
}
define i5 @shl1_nsw_increment(i5 %x, i5 %y) {
; CHECK-LABEL: @shl1_nsw_increment(
; CHECK-NEXT: [[Y_FR:%.*]] = freeze i5 [[Y:%.*]]
; CHECK-NEXT: [[MULSHL:%.*]] = shl i5 [[Y_FR]], [[X:%.*]]
; CHECK-NEXT: [[M1:%.*]] = add i5 [[MULSHL]], [[Y_FR]]
; CHECK-NEXT: ret i5 [[M1]]
;
%pow2x = shl i5 1, %x
%x1 = add i5 %pow2x, 1
%m = mul nsw i5 %x1, %y
ret i5 %m
}
define i5 @shl1_nsw_nsw_increment(i5 %x, i5 %y) {
; CHECK-LABEL: @shl1_nsw_nsw_increment(
; CHECK-NEXT: [[Y_FR:%.*]] = freeze i5 [[Y:%.*]]
; CHECK-NEXT: [[MULSHL:%.*]] = shl nsw i5 [[Y_FR]], [[X:%.*]]
; CHECK-NEXT: [[M1:%.*]] = add nsw i5 [[MULSHL]], [[Y_FR]]
; CHECK-NEXT: ret i5 [[M1]]
;
%pow2x = shl nsw i5 1, %x
%x1 = add i5 %pow2x, 1
%m = mul nsw i5 %y, %x1
ret i5 %m
}
define i5 @shl1_nsw_nsw_increment_commute(i5 %x, i5 %y) {
; CHECK-LABEL: @shl1_nsw_nsw_increment_commute(
; CHECK-NEXT: [[Y_FR:%.*]] = freeze i5 [[Y:%.*]]
; CHECK-NEXT: [[MULSHL:%.*]] = shl i5 [[Y_FR]], [[X:%.*]]
; CHECK-NEXT: [[M1:%.*]] = add i5 [[MULSHL]], [[Y_FR]]
; CHECK-NEXT: ret i5 [[M1]]
;
%pow2x = shl nsw i5 1, %x
%x1 = add nsw i5 %pow2x, 1
%m = mul i5 %x1, %y
ret i5 %m
}
define i32 @shl1_increment_use(i32 %x, i32 %y) {
; CHECK-LABEL: @shl1_increment_use(
; CHECK-NEXT: [[POW2X:%.*]] = shl nuw i32 1, [[X:%.*]]
; CHECK-NEXT: call void @use32(i32 [[POW2X]])
; CHECK-NEXT: [[X1:%.*]] = add nuw i32 [[POW2X]], 1
; CHECK-NEXT: [[M:%.*]] = mul i32 [[X1]], [[Y:%.*]]
; CHECK-NEXT: ret i32 [[M]]
;
%pow2x = shl i32 1, %x
call void @use32(i32 %pow2x)
%x1 = add i32 %pow2x, 1
%m = mul i32 %x1, %y
ret i32 %m
}
; ((-1 << x) ^ -1) * y --> (y << x) - y
define i8 @shl1_decrement(i8 %x, i8 %y) {
; CHECK-LABEL: @shl1_decrement(
; CHECK-NEXT: [[Y_FR:%.*]] = freeze i8 [[Y:%.*]]
; CHECK-NEXT: [[MULSHL:%.*]] = shl i8 [[Y_FR]], [[X:%.*]]
; CHECK-NEXT: [[M1:%.*]] = sub i8 [[MULSHL]], [[Y_FR]]
; CHECK-NEXT: ret i8 [[M1]]
;
%pow2x = shl i8 -1, %x
%x1 = xor i8 %pow2x, -1
%m = mul i8 %x1, %y
ret i8 %m
}
define i8 @shl1_decrement_commute(i8 %x, i8 noundef %p) {
; CHECK-LABEL: @shl1_decrement_commute(
; CHECK-NEXT: [[Y:%.*]] = ashr i8 [[P:%.*]], 1
; CHECK-NEXT: [[MULSHL:%.*]] = shl i8 [[Y]], [[X:%.*]]
; CHECK-NEXT: [[M1:%.*]] = sub i8 [[MULSHL]], [[Y]]
; CHECK-NEXT: ret i8 [[M1]]
;
%y = ashr i8 %p, 1 ; thwart complexity-based canonicalization
%pow2x = shl i8 1, %x
%x1 = add i8 %pow2x, -1
%m = mul i8 %y, %x1
ret i8 %m
}
define i8 @shl1_nuw_decrement(i8 %x, i8 %y) {
; CHECK-LABEL: @shl1_nuw_decrement(
; CHECK-NEXT: [[Y_FR:%.*]] = freeze i8 [[Y:%.*]]
; CHECK-NEXT: [[MULSHL:%.*]] = shl i8 [[Y_FR]], [[X:%.*]]
; CHECK-NEXT: [[M1:%.*]] = sub i8 [[MULSHL]], [[Y_FR]]
; CHECK-NEXT: ret i8 [[M1]]
;
%pow2x = shl i8 -1, %x
%x1 = xor i8 %pow2x, -1
%m = mul nuw i8 %x1, %y
ret i8 %m
}
define i8 @shl1_nsw_decrement(i8 %x, i8 %y) {
; CHECK-LABEL: @shl1_nsw_decrement(
; CHECK-NEXT: [[Y_FR:%.*]] = freeze i8 [[Y:%.*]]
; CHECK-NEXT: [[MULSHL:%.*]] = shl i8 [[Y_FR]], [[X:%.*]]
; CHECK-NEXT: [[M1:%.*]] = sub i8 [[MULSHL]], [[Y_FR]]
; CHECK-NEXT: ret i8 [[M1]]
;
%pow2x = shl nsw i8 -1, %x
%x1 = xor i8 %pow2x, -1
%m = mul nsw i8 %x1, %y
ret i8 %m
}
; negative test - extra use would require more instructions
define i32 @shl1_decrement_use(i32 %x, i32 %y) {
; CHECK-LABEL: @shl1_decrement_use(
; CHECK-NEXT: [[NOTMASK:%.*]] = shl nsw i32 -1, [[X:%.*]]
; CHECK-NEXT: [[X1:%.*]] = xor i32 [[NOTMASK]], -1
; CHECK-NEXT: call void @use32(i32 [[X1]])
; CHECK-NEXT: [[M:%.*]] = mul i32 [[X1]], [[Y:%.*]]
; CHECK-NEXT: ret i32 [[M]]
;
%pow2x = shl i32 1, %x
%x1 = add i32 %pow2x, -1
call void @use32(i32 %x1)
%m = mul i32 %x1, %y
ret i32 %m
}
; the fold works for vectors too and if 'y' is a constant, sub becomes add
define <2 x i8> @shl1_decrement_vec(<2 x i8> %x) {
; CHECK-LABEL: @shl1_decrement_vec(
; CHECK-NEXT: [[MULSHL:%.*]] = shl <2 x i8> <i8 42, i8 -3>, [[X:%.*]]
; CHECK-NEXT: [[M1:%.*]] = add <2 x i8> [[MULSHL]], <i8 -42, i8 3>
; CHECK-NEXT: ret <2 x i8> [[M1]]
;
%pow2x = shl <2 x i8> <i8 -1, i8 -1>, %x
%x1 = xor <2 x i8> %pow2x, <i8 -1, i8 -1>
%m = mul <2 x i8> %x1, <i8 42, i8 -3>
ret <2 x i8> %m
}
; X * Y (when Y is a boolean) --> Y ? X : 0
define i32 @mul_bool(i32 %x, i1 %y) {
; CHECK-LABEL: @mul_bool(
; CHECK-NEXT: [[M:%.*]] = select i1 [[Y:%.*]], i32 [[X:%.*]], i32 0
; CHECK-NEXT: ret i32 [[M]]
;
%z = zext i1 %y to i32
%m = mul i32 %z, %x
ret i32 %m
}
define <2 x i32> @mul_bool_vec(<2 x i32> %x, <2 x i1> %y) {
; CHECK-LABEL: @mul_bool_vec(
; CHECK-NEXT: [[M:%.*]] = select <2 x i1> [[Y:%.*]], <2 x i32> [[X:%.*]], <2 x i32> zeroinitializer
; CHECK-NEXT: ret <2 x i32> [[M]]
;
%z = zext <2 x i1> %y to <2 x i32>
%m = mul <2 x i32> %z, %x
ret <2 x i32> %m
}
define <2 x i32> @mul_bool_vec_commute(<2 x i32> %px, <2 x i1> %y) {
; CHECK-LABEL: @mul_bool_vec_commute(
; CHECK-NEXT: [[X:%.*]] = mul <2 x i32> [[PX:%.*]], [[PX]]
; CHECK-NEXT: [[M:%.*]] = select <2 x i1> [[Y:%.*]], <2 x i32> [[X]], <2 x i32> zeroinitializer
; CHECK-NEXT: ret <2 x i32> [[M]]
;
%x = mul <2 x i32> %px, %px ; thwart complexity-based canonicalization
%z = zext <2 x i1> %y to <2 x i32>
%m = mul <2 x i32> %x, %z
ret <2 x i32> %m
}
; X * C (when X is a sext boolean) --> X ? -C : 0
define i32 @mul_sext_bool(i1 %x) {
; CHECK-LABEL: @mul_sext_bool(
; CHECK-NEXT: [[M:%.*]] = select i1 [[X:%.*]], i32 -42, i32 0
; CHECK-NEXT: ret i32 [[M]]
;
%s = sext i1 %x to i32
%m = mul i32 %s, 42
ret i32 %m
}
define i32 @mul_sext_bool_use(i1 %x) {
; CHECK-LABEL: @mul_sext_bool_use(
; CHECK-NEXT: [[S:%.*]] = sext i1 [[X:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[S]])
; CHECK-NEXT: [[M:%.*]] = select i1 [[X]], i32 -42, i32 0
; CHECK-NEXT: ret i32 [[M]]
;
%s = sext i1 %x to i32
call void @use32(i32 %s)
%m = mul i32 %s, 42
ret i32 %m
}
define <2 x i8> @mul_sext_bool_vec(<2 x i1> %x) {
; CHECK-LABEL: @mul_sext_bool_vec(
; CHECK-NEXT: [[M:%.*]] = select <2 x i1> [[X:%.*]], <2 x i8> <i8 -42, i8 -128>, <2 x i8> zeroinitializer
; CHECK-NEXT: ret <2 x i8> [[M]]
;
%s = sext <2 x i1> %x to <2 x i8>
%m = mul <2 x i8> %s, <i8 42, i8 -128>
ret <2 x i8> %m
}
define <3 x i7> @mul_bools(<3 x i1> %x, <3 x i1> %y) {
; CHECK-LABEL: @mul_bools(
; CHECK-NEXT: [[MULBOOL:%.*]] = and <3 x i1> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[R:%.*]] = zext <3 x i1> [[MULBOOL]] to <3 x i7>
; CHECK-NEXT: ret <3 x i7> [[R]]
;
%zx = zext <3 x i1> %x to <3 x i7>
%zy = zext <3 x i1> %y to <3 x i7>
%r = mul <3 x i7> %zx, %zy
ret <3 x i7> %r
}
define i32 @mul_bools_use1(i1 %x, i1 %y) {
; CHECK-LABEL: @mul_bools_use1(
; CHECK-NEXT: [[ZY:%.*]] = zext i1 [[Y:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[ZY]])
; CHECK-NEXT: [[MULBOOL:%.*]] = and i1 [[X:%.*]], [[Y]]
; CHECK-NEXT: [[R:%.*]] = zext i1 [[MULBOOL]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%zx = zext i1 %x to i32
%zy = zext i1 %y to i32
call void @use32(i32 %zy)
%r = mul i32 %zx, %zy
ret i32 %r
}
define i32 @mul_bools_use2(i1 %x, i1 %y) {
; CHECK-LABEL: @mul_bools_use2(
; CHECK-NEXT: [[ZY:%.*]] = zext i1 [[Y:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[ZY]])
; CHECK-NEXT: [[MULBOOL:%.*]] = and i1 [[Y]], [[X:%.*]]
; CHECK-NEXT: [[R:%.*]] = zext i1 [[MULBOOL]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%zx = zext i1 %x to i32
%zy = zext i1 %y to i32
call void @use32(i32 %zy)
%r = mul i32 %zy, %zx
ret i32 %r
}
define i32 @mul_bools_use3(i1 %x, i1 %y) {
; CHECK-LABEL: @mul_bools_use3(
; CHECK-NEXT: [[ZX:%.*]] = zext i1 [[X:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[ZX]])
; CHECK-NEXT: [[ZY:%.*]] = zext i1 [[Y:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[ZY]])
; CHECK-NEXT: [[R:%.*]] = select i1 [[X]], i32 [[ZY]], i32 0
; CHECK-NEXT: ret i32 [[R]]
;
%zx = zext i1 %x to i32
call void @use32(i32 %zx)
%zy = zext i1 %y to i32
call void @use32(i32 %zy)
%r = mul i32 %zx, %zy
ret i32 %r
}
define <3 x i32> @mul_bools_sext(<3 x i1> %x, <3 x i1> %y) {
; CHECK-LABEL: @mul_bools_sext(
; CHECK-NEXT: [[MULBOOL:%.*]] = and <3 x i1> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[R:%.*]] = zext <3 x i1> [[MULBOOL]] to <3 x i32>
; CHECK-NEXT: ret <3 x i32> [[R]]
;
%sx = sext <3 x i1> %x to <3 x i32>
%sy = sext <3 x i1> %y to <3 x i32>
%r = mul <3 x i32> %sx, %sy
ret <3 x i32> %r
}
define i32 @mul_bools_sext_use1(i1 %x, i1 %y) {
; CHECK-LABEL: @mul_bools_sext_use1(
; CHECK-NEXT: [[SY:%.*]] = sext i1 [[Y:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[SY]])
; CHECK-NEXT: [[MULBOOL:%.*]] = and i1 [[X:%.*]], [[Y]]
; CHECK-NEXT: [[R:%.*]] = zext i1 [[MULBOOL]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%sx = sext i1 %x to i32
%sy = sext i1 %y to i32
call void @use32(i32 %sy)
%r = mul i32 %sx, %sy
ret i32 %r
}
define i32 @mul_bools_sext_use2(i1 %x, i1 %y) {
; CHECK-LABEL: @mul_bools_sext_use2(
; CHECK-NEXT: [[SY:%.*]] = sext i1 [[Y:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[SY]])
; CHECK-NEXT: [[MULBOOL:%.*]] = and i1 [[Y]], [[X:%.*]]
; CHECK-NEXT: [[R:%.*]] = zext i1 [[MULBOOL]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%sx = sext i1 %x to i32
%sy = sext i1 %y to i32
call void @use32(i32 %sy)
%r = mul i32 %sy, %sx
ret i32 %r
}
define i32 @mul_bools_sext_use3(i1 %x, i1 %y) {
; CHECK-LABEL: @mul_bools_sext_use3(
; CHECK-NEXT: [[SX:%.*]] = sext i1 [[X:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[SX]])
; CHECK-NEXT: [[SY:%.*]] = sext i1 [[Y:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[SY]])
; CHECK-NEXT: [[R:%.*]] = mul nsw i32 [[SY]], [[SX]]
; CHECK-NEXT: ret i32 [[R]]
;
%sx = sext i1 %x to i32
call void @use32(i32 %sx)
%sy = sext i1 %y to i32
call void @use32(i32 %sy)
%r = mul i32 %sy, %sx
ret i32 %r
}
define i32 @mul_bools_sext_one_use_per_op(i1 %x, i1 %y) {
; CHECK-LABEL: @mul_bools_sext_one_use_per_op(
; CHECK-NEXT: [[MULBOOL:%.*]] = and i1 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[R:%.*]] = zext i1 [[MULBOOL]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%sx = sext i1 %x to i32
%sy = sext i1 %y to i32
%r = mul i32 %sx, %sy
ret i32 %r
}
define i32 @mul_bool_sext_one_user(i1 %x) {
; CHECK-LABEL: @mul_bool_sext_one_user(
; CHECK-NEXT: [[R:%.*]] = zext i1 [[X:%.*]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%sx = sext i1 %x to i32
%r = mul i32 %sx, %sx
ret i32 %r
}
define i32 @mul_bools_zext_one_use_per_op(i1 %x, i1 %y) {
; CHECK-LABEL: @mul_bools_zext_one_use_per_op(
; CHECK-NEXT: [[MULBOOL:%.*]] = and i1 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[R:%.*]] = zext i1 [[MULBOOL]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%zx = zext i1 %x to i32
%zy = zext i1 %y to i32
%r = mul i32 %zx, %zy
ret i32 %r
}
define i32 @mul_bool_zext_one_user(i1 %x) {
; CHECK-LABEL: @mul_bool_zext_one_user(
; CHECK-NEXT: [[R:%.*]] = zext i1 [[X:%.*]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%sx = zext i1 %x to i32
%r = mul i32 %sx, %sx
ret i32 %r
}
define i32 @mul_bool_sext_one_extra_user(i1 %x) {
; CHECK-LABEL: @mul_bool_sext_one_extra_user(
; CHECK-NEXT: [[SX:%.*]] = sext i1 [[X:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[SX]])
; CHECK-NEXT: [[R:%.*]] = zext i1 [[X]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%sx = sext i1 %x to i32
call void @use32(i32 %sx)
%r = mul i32 %sx, %sx
ret i32 %r
}
define i32 @mul_bool_zext_one_extra_user(i1 %x) {
; CHECK-LABEL: @mul_bool_zext_one_extra_user(
; CHECK-NEXT: [[SX:%.*]] = zext i1 [[X:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[SX]])
; CHECK-NEXT: [[R:%.*]] = zext i1 [[X]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%sx = zext i1 %x to i32
call void @use32(i32 %sx)
%r = mul i32 %sx, %sx
ret i32 %r
}
define <3 x i32> @mul_bools_mixed_ext(<3 x i1> %x, <3 x i1> %y) {
; CHECK-LABEL: @mul_bools_mixed_ext(
; CHECK-NEXT: [[MULBOOL:%.*]] = and <3 x i1> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[R:%.*]] = sext <3 x i1> [[MULBOOL]] to <3 x i32>
; CHECK-NEXT: ret <3 x i32> [[R]]
;
%zx = zext <3 x i1> %x to <3 x i32>
%sy = sext <3 x i1> %y to <3 x i32>
%r = mul <3 x i32> %zx, %sy
ret <3 x i32> %r
}
define i32 @mul_bools_mixed_ext_use1(i1 %x, i1 %y) {
; CHECK-LABEL: @mul_bools_mixed_ext_use1(
; CHECK-NEXT: [[ZY:%.*]] = zext i1 [[Y:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[ZY]])
; CHECK-NEXT: [[MULBOOL:%.*]] = and i1 [[X:%.*]], [[Y]]
; CHECK-NEXT: [[R:%.*]] = sext i1 [[MULBOOL]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%sx = sext i1 %x to i32
%zy = zext i1 %y to i32
call void @use32(i32 %zy)
%r = mul i32 %sx, %zy
ret i32 %r
}
define i32 @mul_bools_mixed_ext_use2(i1 %x, i1 %y) {
; CHECK-LABEL: @mul_bools_mixed_ext_use2(
; CHECK-NEXT: [[SY:%.*]] = sext i1 [[Y:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[SY]])
; CHECK-NEXT: [[MULBOOL:%.*]] = and i1 [[Y]], [[X:%.*]]
; CHECK-NEXT: [[R:%.*]] = sext i1 [[MULBOOL]] to i32
; CHECK-NEXT: ret i32 [[R]]
;
%zx = zext i1 %x to i32
%sy = sext i1 %y to i32
call void @use32(i32 %sy)
%r = mul i32 %sy, %zx
ret i32 %r
}
define i32 @mul_bools_mixed_ext_use3(i1 %x, i1 %y) {
; CHECK-LABEL: @mul_bools_mixed_ext_use3(
; CHECK-NEXT: [[SX:%.*]] = sext i1 [[X:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[SX]])
; CHECK-NEXT: [[ZY:%.*]] = zext i1 [[Y:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[ZY]])
; CHECK-NEXT: [[R:%.*]] = select i1 [[Y]], i32 [[SX]], i32 0
; CHECK-NEXT: ret i32 [[R]]
;
%sx = sext i1 %x to i32
call void @use32(i32 %sx)
%zy = zext i1 %y to i32
call void @use32(i32 %zy)
%r = mul i32 %zy, %sx
ret i32 %r
}
; (A >>u 31) * B --> (A >>s 31) & B --> A < 0 ? B : 0
define i32 @signbit_mul(i32 %a, i32 %b) {
; CHECK-LABEL: @signbit_mul(
; CHECK-NEXT: [[ISNEG:%.*]] = icmp slt i32 [[A:%.*]], 0
; CHECK-NEXT: [[E:%.*]] = select i1 [[ISNEG]], i32 [[B:%.*]], i32 0
; CHECK-NEXT: ret i32 [[E]]
;
%d = lshr i32 %a, 31
%e = mul i32 %d, %b
ret i32 %e
}
define i32 @signbit_mul_commute_extra_use(i32 %a, i32 %b) {
; CHECK-LABEL: @signbit_mul_commute_extra_use(
; CHECK-NEXT: [[D:%.*]] = lshr i32 [[A:%.*]], 31
; CHECK-NEXT: [[ISNEG:%.*]] = icmp slt i32 [[A]], 0
; CHECK-NEXT: [[E:%.*]] = select i1 [[ISNEG]], i32 [[B:%.*]], i32 0
; CHECK-NEXT: call void @use32(i32 [[D]])
; CHECK-NEXT: ret i32 [[E]]
;
%d = lshr i32 %a, 31
%e = mul i32 %b, %d
call void @use32(i32 %d)
ret i32 %e
}
; (A >>u 31)) * B --> (A >>s 31) & B --> A < 0 ? B : 0
define <2 x i32> @signbit_mul_vec(<2 x i32> %a, <2 x i32> %b) {
; CHECK-LABEL: @signbit_mul_vec(
; CHECK-NEXT: [[ISNEG:%.*]] = icmp slt <2 x i32> [[A:%.*]], zeroinitializer
; CHECK-NEXT: [[E:%.*]] = select <2 x i1> [[ISNEG]], <2 x i32> [[B:%.*]], <2 x i32> zeroinitializer
; CHECK-NEXT: ret <2 x i32> [[E]]
;
%d = lshr <2 x i32> %a, <i32 31, i32 31>
%e = mul <2 x i32> %d, %b
ret <2 x i32> %e
}
define <2 x i32> @signbit_mul_vec_commute(<2 x i32> %a, <2 x i32> %b) {
; CHECK-LABEL: @signbit_mul_vec_commute(
; CHECK-NEXT: [[ISNEG:%.*]] = icmp slt <2 x i32> [[A:%.*]], zeroinitializer
; CHECK-NEXT: [[E:%.*]] = select <2 x i1> [[ISNEG]], <2 x i32> [[B:%.*]], <2 x i32> zeroinitializer
; CHECK-NEXT: ret <2 x i32> [[E]]
;
%d = lshr <2 x i32> %a, <i32 31, i32 31>
%e = mul <2 x i32> %b, %d
ret <2 x i32> %e
}
; (A & 1) * B --> (lowbit A) ? B : 0
define i32 @lowbit_mul(i32 %a, i32 %b) {
; CHECK-LABEL: @lowbit_mul(
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[A:%.*]], 1
; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP1]], 0
; CHECK-NEXT: [[E:%.*]] = select i1 [[DOTNOT]], i32 0, i32 [[B:%.*]]
; CHECK-NEXT: ret i32 [[E]]
;
%d = and i32 %a, 1
%e = mul i32 %d, %b
ret i32 %e
}
; (A & 1) * B --> (lowbit A) ? B : 0
define <2 x i17> @lowbit_mul_commute(<2 x i17> %a, <2 x i17> %p) {
; CHECK-LABEL: @lowbit_mul_commute(
; CHECK-NEXT: [[B:%.*]] = xor <2 x i17> [[P:%.*]], <i17 42, i17 43>
; CHECK-NEXT: [[TMP1:%.*]] = trunc <2 x i17> [[A:%.*]] to <2 x i1>
; CHECK-NEXT: [[E:%.*]] = select <2 x i1> [[TMP1]], <2 x i17> [[B]], <2 x i17> zeroinitializer
; CHECK-NEXT: ret <2 x i17> [[E]]
;
%b = xor <2 x i17> %p, <i17 42, i17 43> ; thwart complexity-based canonicalization
%d = and <2 x i17> %a, <i17 1, i17 1>
%e = mul <2 x i17> %b, %d
ret <2 x i17> %e
}
; negative test - extra use
define i32 @lowbit_mul_use(i32 %a, i32 %b) {
; CHECK-LABEL: @lowbit_mul_use(
; CHECK-NEXT: [[D:%.*]] = and i32 [[A:%.*]], 1
; CHECK-NEXT: call void @use32(i32 [[D]])
; CHECK-NEXT: [[E:%.*]] = mul nuw i32 [[D]], [[B:%.*]]
; CHECK-NEXT: ret i32 [[E]]
;
%d = and i32 %a, 1
call void @use32(i32 %d)
%e = mul i32 %d, %b
ret i32 %e
}
; negative test - wrong mask
define i32 @not_lowbit_mul(i32 %a, i32 %b) {
; CHECK-LABEL: @not_lowbit_mul(
; CHECK-NEXT: [[D:%.*]] = and i32 [[A:%.*]], 2
; CHECK-NEXT: [[E:%.*]] = mul i32 [[D]], [[B:%.*]]
; CHECK-NEXT: ret i32 [[E]]
;
%d = and i32 %a, 2
%e = mul i32 %d, %b
ret i32 %e
}
define i32 @signsplat_mul(i32 %x) {
; CHECK-LABEL: @signsplat_mul(
; CHECK-NEXT: [[ISNEG:%.*]] = icmp slt i32 [[X:%.*]], 0
; CHECK-NEXT: [[MUL:%.*]] = select i1 [[ISNEG]], i32 -42, i32 0
; CHECK-NEXT: ret i32 [[MUL]]
;
%ash = ashr i32 %x, 31
%mul = mul i32 %ash, 42
ret i32 %mul
}
define <2 x i32> @signsplat_mul_vec(<2 x i32> %x) {
; CHECK-LABEL: @signsplat_mul_vec(
; CHECK-NEXT: [[ISNEG:%.*]] = icmp slt <2 x i32> [[X:%.*]], zeroinitializer
; CHECK-NEXT: [[MUL:%.*]] = select <2 x i1> [[ISNEG]], <2 x i32> <i32 -42, i32 3>, <2 x i32> zeroinitializer
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%ash = ashr <2 x i32> %x, <i32 31, i32 31>
%mul = mul <2 x i32> %ash, <i32 42, i32 -3>
ret <2 x i32> %mul
}
; negative test - wrong shift amount
define i32 @not_signsplat_mul(i32 %x) {
; CHECK-LABEL: @not_signsplat_mul(
; CHECK-NEXT: [[ASH:%.*]] = ashr i32 [[X:%.*]], 30
; CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 [[ASH]], 42
; CHECK-NEXT: ret i32 [[MUL]]
;
%ash = ashr i32 %x, 30
%mul = mul i32 %ash, 42
ret i32 %mul
}
; negative test - extra use
define i32 @signsplat_mul_use(i32 %x) {
; CHECK-LABEL: @signsplat_mul_use(
; CHECK-NEXT: [[ASH:%.*]] = ashr i32 [[X:%.*]], 31
; CHECK-NEXT: call void @use32(i32 [[ASH]])
; CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 [[ASH]], -42
; CHECK-NEXT: ret i32 [[MUL]]
;
%ash = ashr i32 %x, 31
call void @use32(i32 %ash)
%mul = mul i32 %ash, -42
ret i32 %mul
}
define i32 @test18(i32 %A, i32 %B) {
; CHECK-LABEL: @test18(
; CHECK-NEXT: ret i32 0
;
%C = and i32 %A, 1
%D = and i32 %B, 1
%E = mul i32 %C, %D
%F = and i32 %E, 16
ret i32 %F
}
declare {i32, i1} @llvm.smul.with.overflow.i32(i32, i32)
declare void @use(i1)
define i32 @test19(i32 %A, i32 %B) {
; CHECK-LABEL: @test19(
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: ret i32 0
;
%C = and i32 %A, 1
%D = and i32 %B, 1
; It would be nice if we also started proving that this doesn't overflow.
%E = call {i32, i1} @llvm.smul.with.overflow.i32(i32 %C, i32 %D)
%F = extractvalue {i32, i1} %E, 0
%G = extractvalue {i32, i1} %E, 1
call void @use(i1 %G)
%H = and i32 %F, 16
ret i32 %H
}
define <2 x i64> @test20(<2 x i64> %A) {
; CHECK-LABEL: @test20(
; CHECK-NEXT: [[TMP1:%.*]] = mul <2 x i64> [[A:%.*]], <i64 3, i64 2>
; CHECK-NEXT: [[C:%.*]] = add <2 x i64> [[TMP1]], <i64 36, i64 28>
; CHECK-NEXT: ret <2 x i64> [[C]]
;
%B = add <2 x i64> %A, <i64 12, i64 14>
%C = mul <2 x i64> %B, <i64 3, i64 2>
ret <2 x i64> %C
}
@g = internal global i32 0, align 4
define i32 @PR20079(i32 %a) {
; CHECK-LABEL: @PR20079(
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A:%.*]], -1
; CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 [[ADD]], ptrtoint (ptr @g to i32)
; CHECK-NEXT: ret i32 [[MUL]]
;
%add = add i32 %a, -1
%mul = mul nsw i32 %add, ptrtoint (ptr @g to i32)
ret i32 %mul
}
; Keep nuw flag in this change, https://alive2.llvm.org/ce/z/-Wowpk
define i32 @add_mul_nuw(i32 %a) {
; CHECK-LABEL: @add_mul_nuw(
; CHECK-NEXT: [[TMP1:%.*]] = mul nuw i32 [[A:%.*]], 3
; CHECK-NEXT: [[MUL:%.*]] = add nuw i32 [[TMP1]], 9
; CHECK-NEXT: ret i32 [[MUL]]
;
%add = add nuw i32 %a, 3
%mul = mul nuw i32 %add, 3
ret i32 %mul
}
; Don't propagate nsw flag in this change
define i32 @add_mul_nsw(i32 %a) {
; CHECK-LABEL: @add_mul_nsw(
; CHECK-NEXT: [[TMP1:%.*]] = mul i32 [[A:%.*]], 3
; CHECK-NEXT: [[MUL:%.*]] = add i32 [[TMP1]], 9
; CHECK-NEXT: ret i32 [[MUL]]
;
%add = add nsw i32 %a, 3
%mul = mul nsw i32 %add, 3
ret i32 %mul
}
; Only the add or only the mul has nuw, https://alive2.llvm.org/ce/z/vPwbEa
define i32 @only_add_nuw(i32 %a) {
; CHECK-LABEL: @only_add_nuw(
; CHECK-NEXT: [[TMP1:%.*]] = mul i32 [[A:%.*]], 3
; CHECK-NEXT: [[MUL:%.*]] = add i32 [[TMP1]], 9
; CHECK-NEXT: ret i32 [[MUL]]
;
%add = add nuw i32 %a, 3
%mul = mul i32 %add, 3
ret i32 %mul
}
define i32 @only_mul_nuw(i32 %a) {
; CHECK-LABEL: @only_mul_nuw(
; CHECK-NEXT: [[TMP1:%.*]] = mul i32 [[A:%.*]], 3
; CHECK-NEXT: [[MUL:%.*]] = add i32 [[TMP1]], 9
; CHECK-NEXT: ret i32 [[MUL]]
;
%add = add i32 %a, 3
%mul = mul nuw i32 %add, 3
ret i32 %mul
}
; Don't propagate nsw flag in this change, https://alive2.llvm.org/ce/z/jJ8rZd
define i32 @PR57278_shl(i32 %a) {
; CHECK-LABEL: @PR57278_shl(
; CHECK-NEXT: [[TMP1:%.*]] = mul i32 [[A:%.*]], 12
; CHECK-NEXT: [[MUL:%.*]] = add i32 [[TMP1]], 9
; CHECK-NEXT: ret i32 [[MUL]]
;
%shl = shl nsw i32 %a, 2
%add = or i32 %shl, 3
%mul = mul nsw i32 %add, 3
ret i32 %mul
}
; Negative test: Have common bits set
define i32 @PR57278_shl_1(i32 %a) {
; CHECK-LABEL: @PR57278_shl_1(
; CHECK-NEXT: [[SHL:%.*]] = shl nsw i32 [[A:%.*]], 2
; CHECK-NEXT: [[ADD:%.*]] = or i32 [[SHL]], 4
; CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 [[ADD]], 3
; CHECK-NEXT: ret i32 [[MUL]]
;
%shl = shl nsw i32 %a, 2
%add = or i32 %shl, 4
%mul = mul nsw i32 %add, 3
ret i32 %mul
}
; Keep nuw flag in this change, https://alive2.llvm.org/ce/z/awsQrx
define i32 @PR57278_mul(i32 %a) {
; CHECK-LABEL: @PR57278_mul(
; CHECK-NEXT: [[TMP1:%.*]] = mul nuw i32 [[A:%.*]], 36
; CHECK-NEXT: [[MUL:%.*]] = add nuw i32 [[TMP1]], 9
; CHECK-NEXT: ret i32 [[MUL]]
;
%mul0 = mul nuw i32 %a, 12
%add = or i32 %mul0, 3
%mul = mul nuw i32 %add, 3
ret i32 %mul
}
; Negative test: Have common bits set, https://alive2.llvm.org/ce/z/bHZRh5
define i32 @PR57278_mul_1(i32 %a) {
; CHECK-LABEL: @PR57278_mul_1(
; CHECK-NEXT: [[MUL0:%.*]] = mul nuw i32 [[A:%.*]], 12
; CHECK-NEXT: [[ADD:%.*]] = or i32 [[MUL0]], 4
; CHECK-NEXT: [[MUL:%.*]] = mul nuw i32 [[ADD]], 3
; CHECK-NEXT: ret i32 [[MUL]]
;
%mul0 = mul nuw i32 %a, 12
%add = or i32 %mul0, 4
%mul = mul nuw i32 %add, 3
ret i32 %mul
}
; Test the haveNoCommonBitsSet with assume, https://alive2.llvm.org/ce/z/AXKBjK
define i32 @PR57278_mul_assume(i32 %a) {
; CHECK-LABEL: @PR57278_mul_assume(
; CHECK-NEXT: [[COMBITS:%.*]] = and i32 [[A:%.*]], 3
; CHECK-NEXT: [[NOCOMBITS:%.*]] = icmp eq i32 [[COMBITS]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[NOCOMBITS]])
; CHECK-NEXT: [[TMP1:%.*]] = mul i32 [[A]], 5
; CHECK-NEXT: [[MUL:%.*]] = add i32 [[TMP1]], 15
; CHECK-NEXT: ret i32 [[MUL]]
;
%combits = and i32 %a , 3
%nocombits = icmp eq i32 %combits, 0
call void @llvm.assume(i1 %nocombits)
%add = or i32 %a, 3
%mul = mul i32 %add, 5
ret i32 %mul
}
declare void @llvm.assume(i1)
; https://alive2.llvm.org/ce/z/XYpv9q
define <2 x i32> @PR57278_shl_vec(<2 x i32> %v1) {
; CHECK-LABEL: @PR57278_shl_vec(
; CHECK-NEXT: [[TMP1:%.*]] = mul nuw <2 x i32> [[V1:%.*]], <i32 12, i32 24>
; CHECK-NEXT: [[MUL:%.*]] = add nuw <2 x i32> [[TMP1]], <i32 9, i32 9>
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%shl = shl nuw <2 x i32> %v1, <i32 2, i32 3>
%add = or <2 x i32> %shl, <i32 3, i32 3>
%mul = mul nuw <2 x i32> %add, <i32 3, i32 3>
ret <2 x i32> %mul
}
; TODO: vector with poison should also be supported, https://alive2.llvm.org/ce/z/XYpv9q
define <2 x i32> @PR57278_shl_vec_poison(<2 x i32> %v1) {
; CHECK-LABEL: @PR57278_shl_vec_poison(
; CHECK-NEXT: [[SHL:%.*]] = shl nuw <2 x i32> [[V1:%.*]], <i32 2, i32 poison>
; CHECK-NEXT: [[ADD:%.*]] = or <2 x i32> [[SHL]], <i32 3, i32 poison>
; CHECK-NEXT: [[MUL:%.*]] = mul nuw <2 x i32> [[ADD]], <i32 3, i32 poison>
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%shl = shl nuw <2 x i32> %v1, <i32 2, i32 poison>
%add = or <2 x i32> %shl, <i32 3, i32 poison>
%mul = mul nuw <2 x i32> %add, <i32 3, i32 poison>
ret <2 x i32> %mul
}
define <2 x i1> @test21(<2 x i1> %A, <2 x i1> %B) {
; CHECK-LABEL: @test21(
; CHECK-NEXT: [[C:%.*]] = and <2 x i1> [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: ret <2 x i1> [[C]]
;
%C = mul <2 x i1> %A, %B
ret <2 x i1> %C
}
define i32 @test22(i32 %A) {
; CHECK-LABEL: @test22(
; CHECK-NEXT: [[B:%.*]] = sub nsw i32 0, [[A:%.*]]
; CHECK-NEXT: ret i32 [[B]]
;
%B = mul nsw i32 %A, -1
ret i32 %B
}
define i32 @test23(i32 %A) {
; CHECK-LABEL: @test23(
; CHECK-NEXT: [[C:%.*]] = mul nuw i32 [[A:%.*]], 6
; CHECK-NEXT: ret i32 [[C]]
;
%B = shl nuw i32 %A, 1
%C = mul nuw i32 %B, 3
ret i32 %C
}
define i32 @test24(i32 %A) {
; CHECK-LABEL: @test24(
; CHECK-NEXT: [[C:%.*]] = mul nsw i32 [[A:%.*]], 6
; CHECK-NEXT: ret i32 [[C]]
;
%B = shl nsw i32 %A, 1
%C = mul nsw i32 %B, 3
ret i32 %C
}
define i32 @neg_neg_mul(i32 %A, i32 %B) {
; CHECK-LABEL: @neg_neg_mul(
; CHECK-NEXT: [[E:%.*]] = mul i32 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: ret i32 [[E]]
;
%C = sub i32 0, %A
%D = sub i32 0, %B
%E = mul i32 %C, %D
ret i32 %E
}
define i32 @neg_neg_mul_nsw(i32 %A, i32 %B) {
; CHECK-LABEL: @neg_neg_mul_nsw(
; CHECK-NEXT: [[E:%.*]] = mul nsw i32 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: ret i32 [[E]]
;
%C = sub nsw i32 0, %A
%D = sub nsw i32 0, %B
%E = mul nsw i32 %C, %D
ret i32 %E
}
define i124 @neg_neg_mul_apint(i124 %A, i124 %B) {
; CHECK-LABEL: @neg_neg_mul_apint(
; CHECK-NEXT: [[E:%.*]] = mul i124 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: ret i124 [[E]]
;
%C = sub i124 0, %A
%D = sub i124 0, %B
%E = mul i124 %C, %D
ret i124 %E
}
define i32 @neg_mul_constant(i32 %A) {
; CHECK-LABEL: @neg_mul_constant(
; CHECK-NEXT: [[E:%.*]] = mul i32 [[A:%.*]], -7
; CHECK-NEXT: ret i32 [[E]]
;
%C = sub i32 0, %A
%E = mul i32 %C, 7
ret i32 %E
}
define i55 @neg_mul_constant_apint(i55 %A) {
; CHECK-LABEL: @neg_mul_constant_apint(
; CHECK-NEXT: [[E:%.*]] = mul i55 [[A:%.*]], -7
; CHECK-NEXT: ret i55 [[E]]
;
%C = sub i55 0, %A
%E = mul i55 %C, 7
ret i55 %E
}
define <3 x i8> @neg_mul_constant_vec(<3 x i8> %a) {
; CHECK-LABEL: @neg_mul_constant_vec(
; CHECK-NEXT: [[B:%.*]] = mul <3 x i8> [[A:%.*]], <i8 -5, i8 -5, i8 -5>
; CHECK-NEXT: ret <3 x i8> [[B]]
;
%A = sub <3 x i8> zeroinitializer, %a
%B = mul <3 x i8> %A, <i8 5, i8 5, i8 5>
ret <3 x i8> %B
}
define <3 x i4> @neg_mul_constant_vec_weird(<3 x i4> %a) {
; CHECK-LABEL: @neg_mul_constant_vec_weird(
; CHECK-NEXT: [[B:%.*]] = mul <3 x i4> [[A:%.*]], <i4 -5, i4 -5, i4 -5>
; CHECK-NEXT: ret <3 x i4> [[B]]
;
%A = sub <3 x i4> zeroinitializer, %a
%B = mul <3 x i4> %A, <i4 5, i4 5, i4 5>
ret <3 x i4> %B
}
define i64 @test29(i31 %A, i31 %B) {
; CHECK-LABEL: @test29(
; CHECK-NEXT: [[C:%.*]] = sext i31 [[A:%.*]] to i64
; CHECK-NEXT: [[D:%.*]] = sext i31 [[B:%.*]] to i64
; CHECK-NEXT: [[E:%.*]] = mul nsw i64 [[C]], [[D]]
; CHECK-NEXT: ret i64 [[E]]
;
%C = sext i31 %A to i64
%D = sext i31 %B to i64
%E = mul i64 %C, %D
ret i64 %E
}
define i64 @test30(i32 %A, i32 %B) {
; CHECK-LABEL: @test30(
; CHECK-NEXT: [[C:%.*]] = zext i32 [[A:%.*]] to i64
; CHECK-NEXT: [[D:%.*]] = zext i32 [[B:%.*]] to i64
; CHECK-NEXT: [[E:%.*]] = mul nuw i64 [[C]], [[D]]
; CHECK-NEXT: ret i64 [[E]]
;
%C = zext i32 %A to i64
%D = zext i32 %B to i64
%E = mul i64 %C, %D
ret i64 %E
}
@PR22087 = external global i32
define i32 @test31(i32 %V) {
; CHECK-LABEL: @test31(
; CHECK-NEXT: [[EXT:%.*]] = zext i1 icmp ne (ptr inttoptr (i64 1 to ptr), ptr @PR22087) to i32
; CHECK-NEXT: [[MUL1:%.*]] = shl i32 [[V:%.*]], [[EXT]]
; CHECK-NEXT: ret i32 [[MUL1]]
;
%ext = zext i1 icmp ne (ptr inttoptr (i64 1 to ptr), ptr @PR22087) to i32
%shl = shl i32 1, %ext
%mul = mul i32 %V, %shl
ret i32 %mul
}
define i32 @test32(i32 %X) {
; CHECK-LABEL: @test32(
; CHECK-NEXT: [[MUL:%.*]] = shl i32 [[X:%.*]], 31
; CHECK-NEXT: ret i32 [[MUL]]
;
%mul = mul nsw i32 %X, -2147483648
ret i32 %mul
}
define <2 x i32> @test32vec(<2 x i32> %X) {
; CHECK-LABEL: @test32vec(
; CHECK-NEXT: [[MUL:%.*]] = shl <2 x i32> [[X:%.*]], <i32 31, i32 31>
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%mul = mul nsw <2 x i32> %X, <i32 -2147483648, i32 -2147483648>
ret <2 x i32> %mul
}
define i32 @test33(i32 %X) {
; CHECK-LABEL: @test33(
; CHECK-NEXT: [[MUL:%.*]] = shl nsw i32 [[X:%.*]], 30
; CHECK-NEXT: ret i32 [[MUL]]
;
%mul = mul nsw i32 %X, 1073741824
ret i32 %mul
}
define <2 x i32> @test33vec(<2 x i32> %X) {
; CHECK-LABEL: @test33vec(
; CHECK-NEXT: [[MUL:%.*]] = shl nsw <2 x i32> [[X:%.*]], <i32 30, i32 30>
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%mul = mul nsw <2 x i32> %X, <i32 1073741824, i32 1073741824>
ret <2 x i32> %mul
}
define i128 @test34(i128 %X) {
; CHECK-LABEL: @test34(
; CHECK-NEXT: [[MUL:%.*]] = shl nsw i128 [[X:%.*]], 1
; CHECK-NEXT: ret i128 [[MUL]]
;
%mul = mul nsw i128 %X, 2
ret i128 %mul
}
define i32 @test_mul_canonicalize_op0(i32 %x, i32 %y) {
; CHECK-LABEL: @test_mul_canonicalize_op0(
; CHECK-NEXT: [[TMP1:%.*]] = mul i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[MUL:%.*]] = sub i32 0, [[TMP1]]
; CHECK-NEXT: ret i32 [[MUL]]
;
%neg = sub i32 0, %x
%mul = mul i32 %neg, %y
ret i32 %mul
}
define i32 @test_mul_canonicalize_op1(i32 %x, i32 %z) {
; CHECK-LABEL: @test_mul_canonicalize_op1(
; CHECK-NEXT: [[Y_NEG:%.*]] = mul i32 [[Z:%.*]], -3
; CHECK-NEXT: [[DOTNEG:%.*]] = mul i32 [[Y_NEG]], [[X:%.*]]
; CHECK-NEXT: ret i32 [[DOTNEG]]
;
%y = mul i32 %z, 3
%neg = sub i32 0, %x
%mul = mul i32 %y, %neg
ret i32 %mul
}
define i32 @test_mul_canonicalize_nsw(i32 %x, i32 %y) {
; CHECK-LABEL: @test_mul_canonicalize_nsw(
; CHECK-NEXT: [[TMP1:%.*]] = mul i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[MUL:%.*]] = sub i32 0, [[TMP1]]
; CHECK-NEXT: ret i32 [[MUL]]
;
%neg = sub nsw i32 0, %x
%mul = mul nsw i32 %neg, %y
ret i32 %mul
}
define <2 x i32> @test_mul_canonicalize_vec(<2 x i32> %x, <2 x i32> %y) {
; CHECK-LABEL: @test_mul_canonicalize_vec(
; CHECK-NEXT: [[TMP1:%.*]] = mul <2 x i32> [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[MUL:%.*]] = sub <2 x i32> zeroinitializer, [[TMP1]]
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%neg = sub <2 x i32> <i32 0, i32 0>, %x
%mul = mul <2 x i32> %neg, %y
ret <2 x i32> %mul
}
define i32 @test_mul_canonicalize_multiple_uses(i32 %x, i32 %y) {
; CHECK-LABEL: @test_mul_canonicalize_multiple_uses(
; CHECK-NEXT: [[NEG:%.*]] = sub i32 0, [[X:%.*]]
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[NEG]], [[Y:%.*]]
; CHECK-NEXT: [[MUL2:%.*]] = mul i32 [[MUL]], [[NEG]]
; CHECK-NEXT: ret i32 [[MUL2]]
;
%neg = sub i32 0, %x
%mul = mul i32 %neg, %y
%mul2 = mul i32 %mul, %neg
ret i32 %mul2
}
@X = global i32 5
define i64 @test_mul_canonicalize_neg_is_not_undone(i64 %L1) {
; Check we do not undo the canonicalization of 0 - (X * Y), if Y is a constant
; expr.
; CHECK-LABEL: @test_mul_canonicalize_neg_is_not_undone(
; CHECK-NEXT: [[B4:%.*]] = mul i64 [[L1:%.*]], sub (i64 0, i64 ptrtoint (ptr @X to i64))
; CHECK-NEXT: ret i64 [[B4]]
;
%v1 = ptrtoint ptr @X to i64
%B8 = sub i64 0, %v1
%B4 = mul i64 %B8, %L1
ret i64 %B4
}
define i32 @negate_if_true(i32 %x, i1 %cond) {
; CHECK-LABEL: @negate_if_true(
; CHECK-NEXT: [[TMP1:%.*]] = sub i32 0, [[X:%.*]]
; CHECK-NEXT: [[R:%.*]] = select i1 [[COND:%.*]], i32 [[TMP1]], i32 [[X]]
; CHECK-NEXT: ret i32 [[R]]
;
%sel = select i1 %cond, i32 -1, i32 1
%r = mul i32 %sel, %x
ret i32 %r
}
define i32 @negate_if_false(i32 %x, i1 %cond) {
; CHECK-LABEL: @negate_if_false(
; CHECK-NEXT: [[TMP1:%.*]] = sub i32 0, [[X:%.*]]
; CHECK-NEXT: [[R:%.*]] = select i1 [[COND:%.*]], i32 [[X]], i32 [[TMP1]]
; CHECK-NEXT: ret i32 [[R]]
;
%sel = select i1 %cond, i32 1, i32 -1
%r = mul i32 %sel, %x
ret i32 %r
}
define <2 x i8> @negate_if_true_commute(<2 x i8> %px, i1 %cond) {
; CHECK-LABEL: @negate_if_true_commute(
; CHECK-NEXT: [[X:%.*]] = sdiv <2 x i8> <i8 42, i8 42>, [[PX:%.*]]
; CHECK-NEXT: [[TMP1:%.*]] = sub nsw <2 x i8> zeroinitializer, [[X]]
; CHECK-NEXT: [[R:%.*]] = select i1 [[COND:%.*]], <2 x i8> [[TMP1]], <2 x i8> [[X]]
; CHECK-NEXT: ret <2 x i8> [[R]]
;
%x = sdiv <2 x i8> <i8 42, i8 42>, %px ; thwart complexity-based canonicalization
%sel = select i1 %cond, <2 x i8> <i8 -1, i8 -1>, <2 x i8> <i8 1, i8 1>
%r = mul <2 x i8> %x, %sel
ret <2 x i8> %r
}
define <2 x i8> @negate_if_false_commute(<2 x i8> %px, <2 x i1> %cond) {
; CHECK-LABEL: @negate_if_false_commute(
; CHECK-NEXT: [[X:%.*]] = sdiv <2 x i8> <i8 42, i8 5>, [[PX:%.*]]
; CHECK-NEXT: [[TMP1:%.*]] = sub <2 x i8> zeroinitializer, [[X]]
; CHECK-NEXT: [[R:%.*]] = select <2 x i1> [[COND:%.*]], <2 x i8> [[X]], <2 x i8> [[TMP1]]
; CHECK-NEXT: ret <2 x i8> [[R]]
;
%x = sdiv <2 x i8> <i8 42, i8 5>, %px ; thwart complexity-based canonicalization
%sel = select <2 x i1> %cond, <2 x i8> <i8 1, i8 undef>, <2 x i8> <i8 -1, i8 -1>
%r = mul <2 x i8> %x, %sel
ret <2 x i8> %r
}
; Negative test
define i32 @negate_if_true_extra_use(i32 %x, i1 %cond) {
; CHECK-LABEL: @negate_if_true_extra_use(
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], i32 -1, i32 1
; CHECK-NEXT: call void @use32(i32 [[SEL]])
; CHECK-NEXT: [[R:%.*]] = mul i32 [[SEL]], [[X:%.*]]
; CHECK-NEXT: ret i32 [[R]]
;
%sel = select i1 %cond, i32 -1, i32 1
call void @use32(i32 %sel)
%r = mul i32 %sel, %x
ret i32 %r
}
; Negative test
define <2 x i8> @negate_if_true_wrong_constant(<2 x i8> %px, i1 %cond) {
; CHECK-LABEL: @negate_if_true_wrong_constant(
; CHECK-NEXT: [[X:%.*]] = sdiv <2 x i8> <i8 42, i8 42>, [[PX:%.*]]
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], <2 x i8> <i8 -1, i8 0>, <2 x i8> <i8 1, i8 1>
; CHECK-NEXT: [[R:%.*]] = mul <2 x i8> [[X]], [[SEL]]
; CHECK-NEXT: ret <2 x i8> [[R]]
;
%x = sdiv <2 x i8> <i8 42, i8 42>, %px ; thwart complexity-based canonicalization
%sel = select i1 %cond, <2 x i8> <i8 -1, i8 0>, <2 x i8> <i8 1, i8 1>
%r = mul <2 x i8> %x, %sel
ret <2 x i8> %r
}
; (C ? (X /exact Y) : 1) * Y -> C ? X : Y
define i32 @mul_div_select(i32 %x, i32 %y, i1 %c) {
; CHECK-LABEL: @mul_div_select(
; CHECK-NEXT: [[MUL:%.*]] = select i1 [[C:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]]
; CHECK-NEXT: ret i32 [[MUL]]
;
%div = udiv exact i32 %x, %y
%sel = select i1 %c, i32 %div, i32 1
%mul = mul i32 %sel, %y
ret i32 %mul
}
; fold mul(abs(x),abs(x)) -> mul(x,x)
define i31 @combine_mul_abs_i31(i31 %0) {
; CHECK-LABEL: @combine_mul_abs_i31(
; CHECK-NEXT: [[M:%.*]] = mul i31 [[TMP0:%.*]], [[TMP0]]
; CHECK-NEXT: ret i31 [[M]]
;
%c = icmp slt i31 %0, 0
%s = sub nsw i31 0, %0
%r = select i1 %c, i31 %s, i31 %0
%m = mul i31 %r, %r
ret i31 %m
}
define i32 @combine_mul_abs_i32(i32 %0) {
; CHECK-LABEL: @combine_mul_abs_i32(
; CHECK-NEXT: [[M:%.*]] = mul i32 [[TMP0:%.*]], [[TMP0]]
; CHECK-NEXT: ret i32 [[M]]
;
%c = icmp slt i32 %0, 0
%s = sub nsw i32 0, %0
%r = select i1 %c, i32 %s, i32 %0
%m = mul i32 %r, %r
ret i32 %m
}
define <4 x i32> @combine_mul_abs_v4i32(<4 x i32> %0) {
; CHECK-LABEL: @combine_mul_abs_v4i32(
; CHECK-NEXT: [[M:%.*]] = mul <4 x i32> [[TMP0:%.*]], [[TMP0]]
; CHECK-NEXT: ret <4 x i32> [[M]]
;
%c = icmp slt <4 x i32> %0, zeroinitializer
%s = sub nsw <4 x i32> zeroinitializer, %0
%r = select <4 x i1> %c, <4 x i32> %s, <4 x i32> %0
%m = mul <4 x i32> %r, %r
ret <4 x i32> %m
}
; fold mul(nabs(x),nabs(x)) -> mul(x,x)
define i31 @combine_mul_nabs_i31(i31 %0) {
; CHECK-LABEL: @combine_mul_nabs_i31(
; CHECK-NEXT: [[M:%.*]] = mul i31 [[TMP0:%.*]], [[TMP0]]
; CHECK-NEXT: ret i31 [[M]]
;
%c = icmp slt i31 %0, 0
%s = sub nsw i31 0, %0
%r = select i1 %c, i31 %0, i31 %s
%m = mul i31 %r, %r
ret i31 %m
}
define i32 @combine_mul_nabs_i32(i32 %0) {
; CHECK-LABEL: @combine_mul_nabs_i32(
; CHECK-NEXT: [[M:%.*]] = mul i32 [[TMP0:%.*]], [[TMP0]]
; CHECK-NEXT: ret i32 [[M]]
;
%c = icmp slt i32 %0, 0
%s = sub nsw i32 0, %0
%r = select i1 %c, i32 %0, i32 %s
%m = mul i32 %r, %r
ret i32 %m
}
define <4 x i32> @combine_mul_nabs_v4i32(<4 x i32> %0) {
; CHECK-LABEL: @combine_mul_nabs_v4i32(
; CHECK-NEXT: [[M:%.*]] = mul <4 x i32> [[TMP0:%.*]], [[TMP0]]
; CHECK-NEXT: ret <4 x i32> [[M]]
;
%c = icmp slt <4 x i32> %0, zeroinitializer
%s = sub nsw <4 x i32> zeroinitializer, %0
%r = select <4 x i1> %c, <4 x i32> %0, <4 x i32> %s
%m = mul <4 x i32> %r, %r
ret <4 x i32> %m
}
define i32 @combine_mul_abs_intrin(i32 %x) {
; CHECK-LABEL: @combine_mul_abs_intrin(
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[X:%.*]], [[X]]
; CHECK-NEXT: ret i32 [[MUL]]
;
%abs = call i32 @llvm.abs.i32(i32 %x, i1 false)
%mul = mul i32 %abs, %abs
ret i32 %mul
}
define i32 @combine_mul_nabs_intrin(i32 %x) {
; CHECK-LABEL: @combine_mul_nabs_intrin(
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[X:%.*]], [[X]]
; CHECK-NEXT: ret i32 [[MUL]]
;
%abs = call i32 @llvm.abs.i32(i32 %x, i1 false)
%neg = sub i32 0, %abs
%mul = mul i32 %neg, %neg
ret i32 %mul
}
; z * splat(0) = splat(0), even for scalable vectors
define <vscale x 2 x i64> @mul_scalable_splat_zero(<vscale x 2 x i64> %z) {
; CHECK-LABEL: @mul_scalable_splat_zero(
; CHECK-NEXT: ret <vscale x 2 x i64> zeroinitializer
;
%shuf = shufflevector <vscale x 2 x i64> insertelement (<vscale x 2 x i64> undef, i64 0, i32 0), <vscale x 2 x i64> undef, <vscale x 2 x i32> zeroinitializer
%t3 = mul <vscale x 2 x i64> %shuf, %z
ret <vscale x 2 x i64> %t3
}
;
; fold mul(sub(x,y),negpow2) -> shl(sub(y,x),log2(pow2))
;
define i32 @mulsub1(i32 %a0, i32 %a1) {
; CHECK-LABEL: @mulsub1(
; CHECK-NEXT: [[SUB_NEG:%.*]] = sub i32 [[A0:%.*]], [[A1:%.*]]
; CHECK-NEXT: [[MUL:%.*]] = shl i32 [[SUB_NEG]], 2
; CHECK-NEXT: ret i32 [[MUL]]
;
%sub = sub i32 %a1, %a0
%mul = mul i32 %sub, -4
ret i32 %mul
}
define <2 x i32> @mulsub1_vec(<2 x i32> %a0, <2 x i32> %a1) {
; CHECK-LABEL: @mulsub1_vec(
; CHECK-NEXT: [[SUB_NEG:%.*]] = sub <2 x i32> [[A0:%.*]], [[A1:%.*]]
; CHECK-NEXT: [[MUL:%.*]] = shl <2 x i32> [[SUB_NEG]], <i32 2, i32 2>
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%sub = sub <2 x i32> %a1, %a0
%mul = mul <2 x i32> %sub, <i32 -4, i32 -4>
ret <2 x i32> %mul
}
define <2 x i32> @mulsub1_vec_nonuniform(<2 x i32> %a0, <2 x i32> %a1) {
; CHECK-LABEL: @mulsub1_vec_nonuniform(
; CHECK-NEXT: [[SUB_NEG:%.*]] = sub <2 x i32> [[A0:%.*]], [[A1:%.*]]
; CHECK-NEXT: [[MUL:%.*]] = shl <2 x i32> [[SUB_NEG]], <i32 2, i32 3>
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%sub = sub <2 x i32> %a1, %a0
%mul = mul <2 x i32> %sub, <i32 -4, i32 -8>
ret <2 x i32> %mul
}
define <2 x i32> @mulsub1_vec_nonuniform_undef(<2 x i32> %a0, <2 x i32> %a1) {
; CHECK-LABEL: @mulsub1_vec_nonuniform_undef(
; CHECK-NEXT: [[SUB_NEG:%.*]] = sub <2 x i32> [[A0:%.*]], [[A1:%.*]]
; CHECK-NEXT: [[MUL:%.*]] = shl <2 x i32> [[SUB_NEG]], <i32 2, i32 0>
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%sub = sub <2 x i32> %a1, %a0
%mul = mul <2 x i32> %sub, <i32 -4, i32 undef>
ret <2 x i32> %mul
}
define i32 @mulsub2(i32 %a0) {
; CHECK-LABEL: @mulsub2(
; CHECK-NEXT: [[SUB_NEG:%.*]] = shl i32 [[A0:%.*]], 2
; CHECK-NEXT: [[MUL:%.*]] = add i32 [[SUB_NEG]], -64
; CHECK-NEXT: ret i32 [[MUL]]
;
%sub = sub i32 16, %a0
%mul = mul i32 %sub, -4
ret i32 %mul
}
define <2 x i32> @mulsub2_vec(<2 x i32> %a0) {
; CHECK-LABEL: @mulsub2_vec(
; CHECK-NEXT: [[SUB_NEG:%.*]] = shl <2 x i32> [[A0:%.*]], <i32 2, i32 2>
; CHECK-NEXT: [[MUL:%.*]] = add <2 x i32> [[SUB_NEG]], <i32 -64, i32 -64>
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%sub = sub <2 x i32> <i32 16, i32 16>, %a0
%mul = mul <2 x i32> %sub, <i32 -4, i32 -4>
ret <2 x i32> %mul
}
define <2 x i32> @mulsub2_vec_nonuniform(<2 x i32> %a0) {
; CHECK-LABEL: @mulsub2_vec_nonuniform(
; CHECK-NEXT: [[SUB_NEG:%.*]] = add <2 x i32> [[A0:%.*]], <i32 -16, i32 -32>
; CHECK-NEXT: [[MUL:%.*]] = shl <2 x i32> [[SUB_NEG]], <i32 2, i32 3>
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%sub = sub <2 x i32> <i32 16, i32 32>, %a0
%mul = mul <2 x i32> %sub, <i32 -4, i32 -8>
ret <2 x i32> %mul
}
define <2 x i32> @mulsub2_vec_nonuniform_undef(<2 x i32> %a0) {
; CHECK-LABEL: @mulsub2_vec_nonuniform_undef(
; CHECK-NEXT: [[SUB_NEG:%.*]] = add <2 x i32> [[A0:%.*]], <i32 -16, i32 -32>
; CHECK-NEXT: [[MUL:%.*]] = shl <2 x i32> [[SUB_NEG]], <i32 2, i32 0>
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%sub = sub <2 x i32> <i32 16, i32 32>, %a0
%mul = mul <2 x i32> %sub, <i32 -4, i32 undef>
ret <2 x i32> %mul
}
define i8 @mulsub_nsw(i8 %a1, i8 %a2) {
; CHECK-LABEL: @mulsub_nsw(
; CHECK-NEXT: [[A_NEG:%.*]] = sub nsw i8 [[A2:%.*]], [[A1:%.*]]
; CHECK-NEXT: [[MUL:%.*]] = shl nsw i8 [[A_NEG]], 1
; CHECK-NEXT: ret i8 [[MUL]]
;
%a = sub nsw i8 %a1, %a2
%mul = mul nsw i8 %a, -2
ret i8 %mul
}
; It would be safe to keep the nsw on the shl here, but only because the mul
; to shl transform happens to replace undef with 0.
define <2 x i8> @mulsub_nsw_undef(<2 x i8> %a1, <2 x i8> %a2) {
; CHECK-LABEL: @mulsub_nsw_undef(
; CHECK-NEXT: [[A_NEG:%.*]] = sub nsw <2 x i8> [[A2:%.*]], [[A1:%.*]]
; CHECK-NEXT: [[MUL:%.*]] = shl <2 x i8> [[A_NEG]], <i8 1, i8 0>
; CHECK-NEXT: ret <2 x i8> [[MUL]]
;
%a = sub nsw <2 x i8> %a1, %a2
%mul = mul nsw <2 x i8> %a, <i8 -2, i8 undef>
ret <2 x i8> %mul
}
define i32 @muladd2(i32 %a0) {
; CHECK-LABEL: @muladd2(
; CHECK-NEXT: [[TMP1:%.*]] = shl i32 [[A0:%.*]], 2
; CHECK-NEXT: [[MUL:%.*]] = sub i32 -64, [[TMP1]]
; CHECK-NEXT: ret i32 [[MUL]]
;
%add = add i32 %a0, 16
%mul = mul i32 %add, -4
ret i32 %mul
}
define <2 x i32> @muladd2_vec(<2 x i32> %a0) {
; CHECK-LABEL: @muladd2_vec(
; CHECK-NEXT: [[TMP1:%.*]] = shl <2 x i32> [[A0:%.*]], <i32 2, i32 2>
; CHECK-NEXT: [[MUL:%.*]] = sub <2 x i32> <i32 -64, i32 -64>, [[TMP1]]
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%add = add <2 x i32> %a0, <i32 16, i32 16>
%mul = mul <2 x i32> %add, <i32 -4, i32 -4>
ret <2 x i32> %mul
}
define <2 x i32> @muladd2_vec_nonuniform(<2 x i32> %a0) {
; CHECK-LABEL: @muladd2_vec_nonuniform(
; CHECK-NEXT: [[ADD_NEG:%.*]] = sub <2 x i32> <i32 -16, i32 -32>, [[A0:%.*]]
; CHECK-NEXT: [[MUL:%.*]] = shl <2 x i32> [[ADD_NEG]], <i32 2, i32 3>
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%add = add <2 x i32> %a0, <i32 16, i32 32>
%mul = mul <2 x i32> %add, <i32 -4, i32 -8>
ret <2 x i32> %mul
}
define <2 x i32> @muladd2_vec_nonuniform_undef(<2 x i32> %a0) {
; CHECK-LABEL: @muladd2_vec_nonuniform_undef(
; CHECK-NEXT: [[ADD_NEG:%.*]] = sub <2 x i32> <i32 -16, i32 -32>, [[A0:%.*]]
; CHECK-NEXT: [[MUL:%.*]] = shl <2 x i32> [[ADD_NEG]], <i32 2, i32 0>
; CHECK-NEXT: ret <2 x i32> [[MUL]]
;
%add = add <2 x i32> %a0, <i32 16, i32 32>
%mul = mul <2 x i32> %add, <i32 -4, i32 undef>
ret <2 x i32> %mul
}
define i32 @mulmuladd2(i32 %a0, i32 %a1) {
; CHECK-LABEL: @mulmuladd2(
; CHECK-NEXT: [[ADD_NEG:%.*]] = sub i32 1073741808, [[A0:%.*]]
; CHECK-NEXT: [[MUL1_NEG:%.*]] = mul i32 [[ADD_NEG]], [[A1:%.*]]
; CHECK-NEXT: [[MUL2:%.*]] = shl i32 [[MUL1_NEG]], 2
; CHECK-NEXT: ret i32 [[MUL2]]
;
%add = add i32 %a0, 16
%mul1 = mul i32 %add, %a1
%mul2 = mul i32 %mul1, -4
ret i32 %mul2
}
define i32 @mulmuladd2_extrause0(i32 %a0, i32 %a1) {
; CHECK-LABEL: @mulmuladd2_extrause0(
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A0:%.*]], 16
; CHECK-NEXT: [[MUL1:%.*]] = mul i32 [[ADD]], [[A1:%.*]]
; CHECK-NEXT: call void @use32(i32 [[MUL1]])
; CHECK-NEXT: [[MUL2:%.*]] = mul i32 [[MUL1]], -4
; CHECK-NEXT: ret i32 [[MUL2]]
;
%add = add i32 %a0, 16
%mul1 = mul i32 %add, %a1
call void @use32(i32 %mul1)
%mul2 = mul i32 %mul1, -4
ret i32 %mul2
}
define i32 @mulmuladd2_extrause1(i32 %a0, i32 %a1) {
; CHECK-LABEL: @mulmuladd2_extrause1(
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A0:%.*]], 16
; CHECK-NEXT: call void @use32(i32 [[ADD]])
; CHECK-NEXT: [[MUL1:%.*]] = mul i32 [[ADD]], [[A1:%.*]]
; CHECK-NEXT: [[MUL2:%.*]] = mul i32 [[MUL1]], -4
; CHECK-NEXT: ret i32 [[MUL2]]
;
%add = add i32 %a0, 16
call void @use32(i32 %add)
%mul1 = mul i32 %add, %a1
%mul2 = mul i32 %mul1, -4
ret i32 %mul2
}
define i32 @mulmuladd2_extrause2(i32 %a0, i32 %a1) {
; CHECK-LABEL: @mulmuladd2_extrause2(
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[A0:%.*]], 16
; CHECK-NEXT: call void @use32(i32 [[ADD]])
; CHECK-NEXT: [[MUL1:%.*]] = mul i32 [[ADD]], [[A1:%.*]]
; CHECK-NEXT: call void @use32(i32 [[MUL1]])
; CHECK-NEXT: [[MUL2:%.*]] = mul i32 [[MUL1]], -4
; CHECK-NEXT: ret i32 [[MUL2]]
;
%add = add i32 %a0, 16
call void @use32(i32 %add)
%mul1 = mul i32 %add, %a1
call void @use32(i32 %mul1)
%mul2 = mul i32 %mul1, -4
ret i32 %mul2
}
define i32 @mulnot(i32 %a0) {
; CHECK-LABEL: @mulnot(
; CHECK-NEXT: [[ADD_NEG:%.*]] = shl i32 [[A0:%.*]], 2
; CHECK-NEXT: [[MUL:%.*]] = add i32 [[ADD_NEG]], 4
; CHECK-NEXT: ret i32 [[MUL]]
;
%add = xor i32 %a0, -1
%mul = mul i32 %add, -4
ret i32 %mul
}
define i32 @mulnot_extrause(i32 %a0) {
; CHECK-LABEL: @mulnot_extrause(
; CHECK-NEXT: [[NOT:%.*]] = xor i32 [[A0:%.*]], -1
; CHECK-NEXT: call void @use32(i32 [[NOT]])
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[NOT]], -4
; CHECK-NEXT: ret i32 [[MUL]]
;
%not = xor i32 %a0, -1
call void @use32(i32 %not)
%mul = mul i32 %not, -4
ret i32 %mul
}
define i32 @zext_negpow2(i8 %x) {
; CHECK-LABEL: @zext_negpow2(
; CHECK-NEXT: [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]
; CHECK-NEXT: [[X_NEG_Z:%.*]] = zext i8 [[X_NEG]] to i32
; CHECK-NEXT: [[R:%.*]] = shl nuw i32 [[X_NEG_Z]], 24
; CHECK-NEXT: ret i32 [[R]]
;
%zx = zext i8 %x to i32
%r = mul i32 %zx, -16777216 ; -1 << 24
ret i32 %r
}
; splat constant
define <2 x i14> @zext_negpow2_vec(<2 x i5> %x) {
; CHECK-LABEL: @zext_negpow2_vec(
; CHECK-NEXT: [[X_NEG:%.*]] = sub <2 x i5> zeroinitializer, [[X:%.*]]
; CHECK-NEXT: [[X_NEG_Z:%.*]] = zext <2 x i5> [[X_NEG]] to <2 x i14>
; CHECK-NEXT: [[R:%.*]] = shl <2 x i14> [[X_NEG_Z]], <i14 11, i14 11>
; CHECK-NEXT: ret <2 x i14> [[R]]
;
%zx = zext <2 x i5> %x to <2 x i14>
%r = mul <2 x i14> %zx, <i14 -2048, i14 -2048> ; -1 << 11
ret <2 x i14> %r
}
; negative test - mul must be big enough to cover bitwidth diff
define i32 @zext_negpow2_too_small(i8 %x) {
; CHECK-LABEL: @zext_negpow2_too_small(
; CHECK-NEXT: [[ZX:%.*]] = zext i8 [[X:%.*]] to i32
; CHECK-NEXT: [[R:%.*]] = mul nsw i32 [[ZX]], -8388608
; CHECK-NEXT: ret i32 [[R]]
;
%zx = zext i8 %x to i32
%r = mul i32 %zx, -8388608 ; -1 << 23
ret i32 %r
}
define i16 @sext_negpow2(i9 %x) {
; CHECK-LABEL: @sext_negpow2(
; CHECK-NEXT: [[X_NEG:%.*]] = sub i9 0, [[X:%.*]]
; CHECK-NEXT: [[X_NEG_Z:%.*]] = zext i9 [[X_NEG]] to i16
; CHECK-NEXT: [[R:%.*]] = shl i16 [[X_NEG_Z]], 10
; CHECK-NEXT: ret i16 [[R]]
;
%sx = sext i9 %x to i16
%r = mul i16 %sx, -1024 ; -1 << 10
ret i16 %r
}
; splat constant with poison element(s)
define <2 x i16> @sext_negpow2_vec(<2 x i8> %x) {
; CHECK-LABEL: @sext_negpow2_vec(
; CHECK-NEXT: [[X_NEG:%.*]] = sub <2 x i8> zeroinitializer, [[X:%.*]]
; CHECK-NEXT: [[X_NEG_Z:%.*]] = zext <2 x i8> [[X_NEG]] to <2 x i16>
; CHECK-NEXT: [[R:%.*]] = shl nuw <2 x i16> [[X_NEG_Z]], <i16 8, i16 8>
; CHECK-NEXT: ret <2 x i16> [[R]]
;
%sx = sext <2 x i8> %x to <2 x i16>
%r = mul <2 x i16> %sx, <i16 -256, i16 poison> ; -1 << 8
ret <2 x i16> %r
}
; negative test - mul must be big enough to cover bitwidth diff
define <2 x i16> @sext_negpow2_too_small_vec(<2 x i8> %x) {
; CHECK-LABEL: @sext_negpow2_too_small_vec(
; CHECK-NEXT: [[SX:%.*]] = sext <2 x i8> [[X:%.*]] to <2 x i16>
; CHECK-NEXT: [[R:%.*]] = mul <2 x i16> [[SX]], <i16 -128, i16 poison>
; CHECK-NEXT: ret <2 x i16> [[R]]
;
%sx = sext <2 x i8> %x to <2 x i16>
%r = mul <2 x i16> %sx, <i16 -128, i16 poison> ; -1 << 7
ret <2 x i16> %r
}
; negative test - too many uses
define i32 @zext_negpow2_use(i8 %x) {
; CHECK-LABEL: @zext_negpow2_use(
; CHECK-NEXT: [[ZX:%.*]] = zext i8 [[X:%.*]] to i32
; CHECK-NEXT: call void @use32(i32 [[ZX]])
; CHECK-NEXT: [[R:%.*]] = mul i32 [[ZX]], -16777216
; CHECK-NEXT: ret i32 [[R]]
;
%zx = zext i8 %x to i32
call void @use32(i32 %zx)
%r = mul i32 %zx, -16777216 ; -1 << 24
ret i32 %r
}