
Inside foldICmpUsingKnownBits(), instead of rolling our own logic based on min/max values, make use of ICmpInst::compare() working on KnownBits. This gives better results for the equality predicates. In practice, the improvement is only for pointers, because isKnownNonEqual() handles the non-pointer case. I've adjusted some tests to prevent the new fold from triggering, to retain their original intent of testing constant expressions.
2205 lines
65 KiB
LLVM
2205 lines
65 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
|
|
; RUN: opt < %s -passes=instcombine -use-constant-int-for-fixed-length-splat -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:%.*]], splat (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:%.*]], splat (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 [[Y:%.*]], [[X1]]
|
|
; 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:%.*]] = trunc i32 [[A:%.*]] to i1
|
|
; CHECK-NEXT: [[E:%.*]] = select i1 [[TMP1]], i32 [[B:%.*]], i32 0
|
|
; 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)
|
|
|
|
define i32 @PR57278_or_disjoint_nuw(i32 %a) {
|
|
; CHECK-LABEL: @PR57278_or_disjoint_nuw(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = mul nuw i32 [[A:%.*]], 3
|
|
; CHECK-NEXT: [[MUL:%.*]] = add nuw i32 [[TMP1]], 9
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%add = or disjoint i32 %a, 3
|
|
%mul = mul nuw i32 %add, 3
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @PR57278_or_disjoint_nsw(i32 %a) {
|
|
; CHECK-LABEL: @PR57278_or_disjoint_nsw(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = mul i32 [[A:%.*]], 3
|
|
; CHECK-NEXT: [[MUL:%.*]] = add i32 [[TMP1]], 9
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%add = or disjoint i32 %a, 3
|
|
%mul = mul nsw i32 %add, 3
|
|
ret i32 %mul
|
|
}
|
|
|
|
; 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]], splat (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: [[TMP1:%.*]] = mul nuw <2 x i32> [[SHL]], <i32 3, i32 poison>
|
|
; CHECK-NEXT: [[MUL:%.*]] = add nuw <2 x i32> [[TMP1]], <i32 9, 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:%.*]], splat (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:%.*]], splat (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: [[CMP:%.*]] = icmp ne ptr inttoptr (i64 4 to ptr), @PR22087
|
|
; CHECK-NEXT: [[EXT:%.*]] = zext i1 [[CMP]] to i32
|
|
; CHECK-NEXT: [[MUL1:%.*]] = shl i32 [[V:%.*]], [[EXT]]
|
|
; CHECK-NEXT: ret i32 [[MUL1]]
|
|
;
|
|
%cmp = icmp ne ptr inttoptr (i64 4 to ptr), @PR22087
|
|
%ext = zext i1 %cmp 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:%.*]], splat (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:%.*]], splat (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: [[MUL_NEG:%.*]] = mul i32 [[X:%.*]], [[Y:%.*]]
|
|
; CHECK-NEXT: [[MUL2:%.*]] = mul i32 [[MUL_NEG]], [[X]]
|
|
; CHECK-NEXT: ret i32 [[MUL2]]
|
|
;
|
|
%neg = sub i32 0, %x
|
|
%mul = mul i32 %neg, %y
|
|
%mul2 = mul i32 %mul, %neg
|
|
ret i32 %mul2
|
|
}
|
|
|
|
define i32 @mul_nsw_mul_nsw_neg(i32 %x, i32 %y) {
|
|
; CHECK-LABEL: @mul_nsw_mul_nsw_neg(
|
|
; CHECK-NEXT: [[MUL_NEG:%.*]] = mul i32 [[X:%.*]], [[Y:%.*]]
|
|
; CHECK-NEXT: [[MUL2:%.*]] = mul i32 [[MUL_NEG]], [[X]]
|
|
; CHECK-NEXT: ret i32 [[MUL2]]
|
|
;
|
|
%neg = sub i32 0, %x
|
|
%mul = mul nsw i32 %neg, %y
|
|
%mul2 = mul nsw i32 %mul, %neg
|
|
ret i32 %mul2
|
|
}
|
|
|
|
define i32 @mul_mul_nsw_neg(i32 %x,i32 %y) {
|
|
; CHECK-LABEL: @mul_mul_nsw_neg(
|
|
; CHECK-NEXT: [[MUL_NEG:%.*]] = mul i32 [[X:%.*]], [[Y:%.*]]
|
|
; CHECK-NEXT: [[MUL2:%.*]] = mul i32 [[MUL_NEG]], [[X]]
|
|
; CHECK-NEXT: ret i32 [[MUL2]]
|
|
;
|
|
%neg = sub i32 0, %x
|
|
%mul = mul nsw i32 %neg, %y
|
|
%mul2 = mul i32 %mul, %neg
|
|
ret i32 %mul2
|
|
}
|
|
|
|
define i32 @mul_nsw_mul_neg(i32 %x,i32 %y) {
|
|
; CHECK-LABEL: @mul_nsw_mul_neg(
|
|
; CHECK-NEXT: [[MUL_NEG:%.*]] = mul i32 [[X:%.*]], [[Y:%.*]]
|
|
; CHECK-NEXT: [[MUL2:%.*]] = mul i32 [[MUL_NEG]], [[X]]
|
|
; CHECK-NEXT: ret i32 [[MUL2]]
|
|
;
|
|
%neg = sub i32 0, %x
|
|
%mul = mul i32 %neg, %y
|
|
%mul2 = mul nsw i32 %mul, %neg
|
|
ret i32 %mul2
|
|
}
|
|
|
|
define i32 @mul_nsw_mul_neg_onearg(i32 %x) {
|
|
; CHECK-LABEL: @mul_nsw_mul_neg_onearg(
|
|
; CHECK-NEXT: [[MUL_NEG:%.*]] = mul i32 [[X:%.*]], [[X]]
|
|
; CHECK-NEXT: [[MUL2:%.*]] = mul i32 [[MUL_NEG]], [[X]]
|
|
; CHECK-NEXT: ret i32 [[MUL2]]
|
|
;
|
|
%neg = sub i32 0, %x
|
|
%mul = mul i32 %neg, %x
|
|
%mul2 = mul nsw i32 %mul, %neg
|
|
ret i32 %mul2
|
|
}
|
|
|
|
define i8 @mul_mul_nsw_neg_onearg(i8 %x) {
|
|
; CHECK-LABEL: @mul_mul_nsw_neg_onearg(
|
|
; CHECK-NEXT: [[MUL_NEG:%.*]] = mul i8 [[X:%.*]], [[X]]
|
|
; CHECK-NEXT: [[MUL2:%.*]] = mul i8 [[MUL_NEG]], [[X]]
|
|
; CHECK-NEXT: ret i8 [[MUL2]]
|
|
;
|
|
%neg = sub i8 0, %x
|
|
%mul = mul nsw i8 %neg, %x
|
|
%mul2 = mul i8 %mul, %neg
|
|
ret i8 %mul2
|
|
}
|
|
|
|
define i32 @mul_nsw_mul_nsw_neg_onearg(i32 %x) {
|
|
; CHECK-LABEL: @mul_nsw_mul_nsw_neg_onearg(
|
|
; CHECK-NEXT: [[MUL_NEG:%.*]] = mul i32 [[X:%.*]], [[X]]
|
|
; CHECK-NEXT: [[MUL2:%.*]] = mul i32 [[MUL_NEG]], [[X]]
|
|
; CHECK-NEXT: ret i32 [[MUL2]]
|
|
;
|
|
%neg = sub i32 0, %x
|
|
%mul = mul nsw i32 %neg, %x
|
|
%mul2 = mul nsw i32 %mul, %neg
|
|
ret i32 %mul2
|
|
}
|
|
|
|
define i32 @mul_nsw_shl_nsw_neg(i32 %x, i32 %y) {
|
|
; CHECK-LABEL: @mul_nsw_shl_nsw_neg(
|
|
; CHECK-NEXT: [[SHL_NEG:%.*]] = shl i32 [[X:%.*]], [[Y:%.*]]
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[SHL_NEG]], [[X]]
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%neg = sub i32 0, %x
|
|
%shl = shl nsw i32 %neg, %y
|
|
%mul = mul nsw i32 %shl, %neg
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @mul_shl_nsw_neg(i32 %x,i32 %y) {
|
|
; CHECK-LABEL: @mul_shl_nsw_neg(
|
|
; CHECK-NEXT: [[SHL_NEG:%.*]] = shl i32 [[X:%.*]], [[Y:%.*]]
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[SHL_NEG]], [[X]]
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%neg = sub i32 0, %x
|
|
%shl = shl nsw i32 %neg, %y
|
|
%mul = mul i32 %shl, %neg
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @mul_nsw_shl_neg(i32 %x,i32 %y) {
|
|
; CHECK-LABEL: @mul_nsw_shl_neg(
|
|
; CHECK-NEXT: [[SHL_NEG:%.*]] = shl i32 [[X:%.*]], [[Y:%.*]]
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[SHL_NEG]], [[X]]
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%neg = sub i32 0, %x
|
|
%shl = shl i32 %neg, %y
|
|
%mul = mul nsw i32 %shl, %neg
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @mul_nsw_shl_neg_onearg(i32 %x) {
|
|
; CHECK-LABEL: @mul_nsw_shl_neg_onearg(
|
|
; CHECK-NEXT: [[SHL_NEG:%.*]] = shl i32 [[X:%.*]], [[X]]
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[SHL_NEG]], [[X]]
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%neg = sub i32 0, %x
|
|
%shl = shl i32 %neg, %x
|
|
%mul = mul nsw i32 %shl, %neg
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i8 @mul_shl_nsw_neg_onearg(i8 %x) {
|
|
; CHECK-LABEL: @mul_shl_nsw_neg_onearg(
|
|
; CHECK-NEXT: [[SHL_NEG:%.*]] = shl i8 [[X:%.*]], [[X]]
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[SHL_NEG]], [[X]]
|
|
; CHECK-NEXT: ret i8 [[MUL]]
|
|
;
|
|
%neg = sub i8 0, %x
|
|
%shl = shl nsw i8 %neg, %x
|
|
%mul = mul i8 %shl, %neg
|
|
ret i8 %mul
|
|
}
|
|
|
|
define i32 @mul_nsw_shl_nsw_neg_onearg(i32 %x) {
|
|
; CHECK-LABEL: @mul_nsw_shl_nsw_neg_onearg(
|
|
; CHECK-NEXT: [[SHL_NEG:%.*]] = mul i32 [[X:%.*]], [[X]]
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[SHL_NEG]], [[X]]
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%neg = sub i32 0, %x
|
|
%shl = mul nsw i32 %neg, %x
|
|
%mul = mul nsw i32 %shl, %neg
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @mul_use_mul_neg(i32 %x,i32 %y) {
|
|
; CHECK-LABEL: @mul_use_mul_neg(
|
|
; CHECK-NEXT: [[NEG:%.*]] = sub i32 0, [[X:%.*]]
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[Y:%.*]], [[NEG]]
|
|
; CHECK-NEXT: call void @use32(i32 [[MUL]])
|
|
; CHECK-NEXT: [[MUL2:%.*]] = mul i32 [[MUL]], [[NEG]]
|
|
; CHECK-NEXT: ret i32 [[MUL2]]
|
|
;
|
|
%neg = sub i32 0, %x
|
|
%mul = mul i32 %neg, %y
|
|
call void @use32(i32 %mul)
|
|
%mul2 = mul i32 %mul, %neg
|
|
ret i32 %mul2
|
|
}
|
|
|
|
define i32 @mul_shl_use_mul_neg(i32 %x,i32 %y) {
|
|
; CHECK-LABEL: @mul_shl_use_mul_neg(
|
|
; CHECK-NEXT: [[NEG:%.*]] = sub i32 0, [[X:%.*]]
|
|
; CHECK-NEXT: [[SHL:%.*]] = shl i32 [[NEG]], [[Y:%.*]]
|
|
; CHECK-NEXT: call void @use32(i32 [[SHL]])
|
|
; CHECK-NEXT: [[MUL2:%.*]] = mul i32 [[SHL]], [[NEG]]
|
|
; CHECK-NEXT: ret i32 [[MUL2]]
|
|
;
|
|
%neg = sub i32 0, %x
|
|
%shl = shl i32 %neg, %y
|
|
call void @use32(i32 %shl)
|
|
%mul2 = mul i32 %shl, %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> splat (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 poison>, <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> splat (i8 42), [[PX:%.*]]
|
|
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND:%.*]], <2 x i8> <i8 -1, i8 0>, <2 x i8> splat (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> poison, i64 0, i32 0), <vscale x 2 x i64> poison, <vscale x 2 x i32> zeroinitializer
|
|
%t3 = mul <vscale x 2 x i64> %shuf, %z
|
|
ret <vscale x 2 x i64> %t3
|
|
}
|
|
|
|
; fold mul(abs(x),abs(y)) -> abs(mul(x,y))
|
|
define i32 @combine_mul_abs_x_abs_y(i32 %x, i32 %y) {
|
|
; CHECK-LABEL: @combine_mul_abs_x_abs_y(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = mul nsw i32 [[X:%.*]], [[Y:%.*]]
|
|
; CHECK-NEXT: [[MUL:%.*]] = call i32 @llvm.abs.i32(i32 [[TMP1]], i1 true)
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%abs_x = call i32 @llvm.abs.i32(i32 %x, i1 true)
|
|
%abs_y = call i32 @llvm.abs.i32(i32 %y, i1 true)
|
|
%mul = mul nsw i32 %abs_x, %abs_y
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @combine_mul_abs_x_abs_y_no_nsw(i32 %x, i32 %y) {
|
|
; CHECK-LABEL: @combine_mul_abs_x_abs_y_no_nsw(
|
|
; CHECK-NEXT: [[ABS_X:%.*]] = call i32 @llvm.abs.i32(i32 [[X:%.*]], i1 true)
|
|
; CHECK-NEXT: [[ABS_Y:%.*]] = call i32 @llvm.abs.i32(i32 [[Y:%.*]], i1 true)
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[ABS_X]], [[ABS_Y]]
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%abs_x = call i32 @llvm.abs.i32(i32 %x, i1 true)
|
|
%abs_y = call i32 @llvm.abs.i32(i32 %y, i1 true)
|
|
%mul = mul i32 %abs_x, %abs_y
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @combine_mul_abs_x_abs_y_poison_1(i32 %x, i32 %y) {
|
|
; CHECK-LABEL: @combine_mul_abs_x_abs_y_poison_1(
|
|
; CHECK-NEXT: [[ABS_X:%.*]] = call i32 @llvm.abs.i32(i32 [[X:%.*]], i1 true)
|
|
; CHECK-NEXT: [[ABS_Y:%.*]] = call i32 @llvm.abs.i32(i32 [[Y:%.*]], i1 false)
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 [[ABS_X]], [[ABS_Y]]
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%abs_x = call i32 @llvm.abs.i32(i32 %x, i1 true)
|
|
%abs_y = call i32 @llvm.abs.i32(i32 %y, i1 false)
|
|
%mul = mul nsw i32 %abs_x, %abs_y
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @combine_mul_abs_x_abs_y_poison_2(i32 %x, i32 %y) {
|
|
; CHECK-LABEL: @combine_mul_abs_x_abs_y_poison_2(
|
|
; CHECK-NEXT: [[ABS_X:%.*]] = call i32 @llvm.abs.i32(i32 [[X:%.*]], i1 false)
|
|
; CHECK-NEXT: [[ABS_Y:%.*]] = call i32 @llvm.abs.i32(i32 [[Y:%.*]], i1 false)
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 [[ABS_X]], [[ABS_Y]]
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%abs_x = call i32 @llvm.abs.i32(i32 %x, i1 false)
|
|
%abs_y = call i32 @llvm.abs.i32(i32 %y, i1 false)
|
|
%mul = mul nsw i32 %abs_x, %abs_y
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @combine_mul_abs_x_abs_y_not_oneuse(i32 %x, i32 %y) {
|
|
; CHECK-LABEL: @combine_mul_abs_x_abs_y_not_oneuse(
|
|
; CHECK-NEXT: [[ABS_X:%.*]] = call i32 @llvm.abs.i32(i32 [[X:%.*]], i1 true)
|
|
; CHECK-NEXT: [[ABS_Y:%.*]] = call i32 @llvm.abs.i32(i32 [[Y:%.*]], i1 true)
|
|
; CHECK-NEXT: [[ABS_X1:%.*]] = add nuw i32 [[ABS_Y]], 1
|
|
; CHECK-NEXT: [[RET:%.*]] = mul i32 [[ABS_X]], [[ABS_X1]]
|
|
; CHECK-NEXT: ret i32 [[RET]]
|
|
;
|
|
%abs_x = call i32 @llvm.abs.i32(i32 %x, i1 true)
|
|
%abs_y = call i32 @llvm.abs.i32(i32 %y, i1 true)
|
|
%mul = mul nsw i32 %abs_x, %abs_y
|
|
%ret = add i32 %mul, %abs_x
|
|
ret i32 %ret
|
|
}
|
|
|
|
;
|
|
; 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]], splat (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_poison(<2 x i32> %a0, <2 x i32> %a1) {
|
|
; CHECK-LABEL: @mulsub1_vec_nonuniform_poison(
|
|
; 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 poison>
|
|
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:%.*]], splat (i32 2)
|
|
; CHECK-NEXT: [[MUL:%.*]] = add <2 x i32> [[SUB_NEG]], splat (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_poison(<2 x i32> %a0) {
|
|
; CHECK-LABEL: @mulsub2_vec_nonuniform_poison(
|
|
; 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 poison>
|
|
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 poison with 0.
|
|
define <2 x i8> @mulsub_nsw_poison(<2 x i8> %a1, <2 x i8> %a2) {
|
|
; CHECK-LABEL: @mulsub_nsw_poison(
|
|
; 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 poison>
|
|
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:%.*]], splat (i32 2)
|
|
; CHECK-NEXT: [[MUL:%.*]] = sub <2 x i32> splat (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_poison(<2 x i32> %a0) {
|
|
; CHECK-LABEL: @muladd2_vec_nonuniform_poison(
|
|
; 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 poison>
|
|
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]], splat (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]], splat (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 nsw <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
|
|
}
|
|
|
|
define i32 @mul_sext_icmp_with_zero(i32 %x) {
|
|
; CHECK-LABEL: @mul_sext_icmp_with_zero(
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
%cmp = icmp eq i32 %x, 0
|
|
%sext = sext i1 %cmp to i32
|
|
%mul = mul i32 %sext, %x
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @test_mul_sext_bool(i1 %x, i32 %y) {
|
|
; CHECK-LABEL: @test_mul_sext_bool(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = sub i32 0, [[Y:%.*]]
|
|
; CHECK-NEXT: [[MUL:%.*]] = select i1 [[X:%.*]], i32 [[TMP1]], i32 0
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%sext = sext i1 %x to i32
|
|
%mul = mul i32 %sext, %y
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @test_mul_sext_bool_nuw(i1 %x, i32 %y) {
|
|
; CHECK-LABEL: @test_mul_sext_bool_nuw(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = sub i32 0, [[Y:%.*]]
|
|
; CHECK-NEXT: [[MUL:%.*]] = select i1 [[X:%.*]], i32 [[TMP1]], i32 0
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%sext = sext i1 %x to i32
|
|
%mul = mul nuw i32 %sext, %y
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @test_mul_sext_bool_nsw(i1 %x, i32 %y) {
|
|
; CHECK-LABEL: @test_mul_sext_bool_nsw(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = sub nsw i32 0, [[Y:%.*]]
|
|
; CHECK-NEXT: [[MUL:%.*]] = select i1 [[X:%.*]], i32 [[TMP1]], i32 0
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%sext = sext i1 %x to i32
|
|
%mul = mul nsw i32 %sext, %y
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @test_mul_sext_bool_nuw_nsw(i1 %x, i32 %y) {
|
|
; CHECK-LABEL: @test_mul_sext_bool_nuw_nsw(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = sub nsw i32 0, [[Y:%.*]]
|
|
; CHECK-NEXT: [[MUL:%.*]] = select i1 [[X:%.*]], i32 [[TMP1]], i32 0
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%sext = sext i1 %x to i32
|
|
%mul = mul nuw nsw i32 %sext, %y
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @test_mul_sext_bool_commuted(i1 %x, i32 %y) {
|
|
; CHECK-LABEL: @test_mul_sext_bool_commuted(
|
|
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[Y:%.*]], -2
|
|
; CHECK-NEXT: [[YY_NEG:%.*]] = add i32 [[TMP1]], 1
|
|
; CHECK-NEXT: [[MUL:%.*]] = select i1 [[X:%.*]], i32 [[YY_NEG]], i32 0
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%yy = xor i32 %y, 1
|
|
%sext = sext i1 %x to i32
|
|
%mul = mul i32 %yy, %sext
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @test_mul_sext_nonbool(i2 %x, i32 %y) {
|
|
; CHECK-LABEL: @test_mul_sext_nonbool(
|
|
; CHECK-NEXT: [[SEXT:%.*]] = sext i2 [[X:%.*]] to i32
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[Y:%.*]], [[SEXT]]
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%sext = sext i2 %x to i32
|
|
%mul = mul i32 %sext, %y
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i32 @test_mul_sext_multiuse(i1 %x, i32 %y) {
|
|
; CHECK-LABEL: @test_mul_sext_multiuse(
|
|
; CHECK-NEXT: [[SEXT:%.*]] = sext i1 [[X:%.*]] to i32
|
|
; CHECK-NEXT: tail call void @use(i32 [[SEXT]])
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[Y:%.*]], [[SEXT]]
|
|
; CHECK-NEXT: ret i32 [[MUL]]
|
|
;
|
|
%sext = sext i1 %x to i32
|
|
tail call void @use(i32 %sext)
|
|
%mul = mul i32 %sext, %y
|
|
ret i32 %mul
|
|
}
|
|
|
|
define i8 @mul_nsw_nonneg(i8 %x, i8 %y) {
|
|
; CHECK-LABEL: @mul_nsw_nonneg(
|
|
; CHECK-NEXT: [[X_NNEG:%.*]] = icmp sgt i8 [[X:%.*]], -1
|
|
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NNEG]])
|
|
; CHECK-NEXT: [[Y_NNEG:%.*]] = icmp sgt i8 [[Y:%.*]], -1
|
|
; CHECK-NEXT: call void @llvm.assume(i1 [[Y_NNEG]])
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul nuw nsw i8 [[X]], [[Y]]
|
|
; CHECK-NEXT: ret i8 [[MUL]]
|
|
;
|
|
%x.nneg = icmp sge i8 %x, 0
|
|
call void @llvm.assume(i1 %x.nneg)
|
|
%y.nneg = icmp sge i8 %y, 0
|
|
call void @llvm.assume(i1 %y.nneg)
|
|
%mul = mul nsw i8 %x, %y
|
|
ret i8 %mul
|
|
}
|
|
|
|
define i8 @mul_nsw_not_nonneg1(i8 %x, i8 %y) {
|
|
; CHECK-LABEL: @mul_nsw_not_nonneg1(
|
|
; CHECK-NEXT: [[Y_NNEG:%.*]] = icmp sgt i8 [[Y:%.*]], -1
|
|
; CHECK-NEXT: call void @llvm.assume(i1 [[Y_NNEG]])
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul nsw i8 [[X:%.*]], [[Y]]
|
|
; CHECK-NEXT: ret i8 [[MUL]]
|
|
;
|
|
%y.nneg = icmp sge i8 %y, 0
|
|
call void @llvm.assume(i1 %y.nneg)
|
|
%mul = mul nsw i8 %x, %y
|
|
ret i8 %mul
|
|
}
|
|
|
|
define i8 @mul_nsw_not_nonneg2(i8 %x, i8 %y) {
|
|
; CHECK-LABEL: @mul_nsw_not_nonneg2(
|
|
; CHECK-NEXT: [[X_NNEG:%.*]] = icmp sgt i8 [[X:%.*]], -1
|
|
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NNEG]])
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul nsw i8 [[X]], [[Y:%.*]]
|
|
; CHECK-NEXT: ret i8 [[MUL]]
|
|
;
|
|
%x.nneg = icmp sge i8 %x, 0
|
|
call void @llvm.assume(i1 %x.nneg)
|
|
%mul = mul nsw i8 %x, %y
|
|
ret i8 %mul
|
|
}
|
|
|
|
define i8 @mul_not_nsw_nonneg(i8 %x, i8 %y) {
|
|
; CHECK-LABEL: @mul_not_nsw_nonneg(
|
|
; CHECK-NEXT: [[X_NNEG:%.*]] = icmp sgt i8 [[X:%.*]], -1
|
|
; CHECK-NEXT: call void @llvm.assume(i1 [[X_NNEG]])
|
|
; CHECK-NEXT: [[Y_NNEG:%.*]] = icmp sgt i8 [[Y:%.*]], -1
|
|
; CHECK-NEXT: call void @llvm.assume(i1 [[Y_NNEG]])
|
|
; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[X]], [[Y]]
|
|
; CHECK-NEXT: ret i8 [[MUL]]
|
|
;
|
|
%x.nneg = icmp sge i8 %x, 0
|
|
call void @llvm.assume(i1 %x.nneg)
|
|
%y.nneg = icmp sge i8 %y, 0
|
|
call void @llvm.assume(i1 %y.nneg)
|
|
%mul = mul i8 %x, %y
|
|
ret i8 %mul
|
|
}
|