This is no-externally-visible-functional-difference-intended. That is, the test diffs show identical instructions other than name changes (those are included specifically to verify the logic). The existing transforms created extra instructions and relied on subsequent folds to get to the final result, but that could conflict with other transforms like the proposed D110170 (and caused that patch to be reverted twice so far because of infinite combine loops).
275 lines
7.2 KiB
LLVM
275 lines
7.2 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt < %s -instcombine -S | FileCheck %s
|
|
|
|
declare void @use8(i8)
|
|
declare void @use32(i32)
|
|
|
|
; These would crash if we didn't check for a negative shift.
|
|
|
|
; https://llvm.org/bugs/show_bug.cgi?id=12967
|
|
|
|
define void @pr12967() {
|
|
; CHECK-LABEL: @pr12967(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: br label [[LOOP]]
|
|
;
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%c = phi i32 [ %shl, %loop ], [ undef, %entry ]
|
|
%shr = shl i32 %c, 7
|
|
%shl = lshr i32 %shr, -2
|
|
br label %loop
|
|
}
|
|
|
|
; https://llvm.org/bugs/show_bug.cgi?id=26760
|
|
|
|
define void @pr26760() {
|
|
; CHECK-LABEL: @pr26760(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
|
; CHECK: loop:
|
|
; CHECK-NEXT: br label [[LOOP]]
|
|
;
|
|
entry:
|
|
br label %loop
|
|
|
|
loop:
|
|
%c = phi i32 [ %shl, %loop ], [ undef, %entry ]
|
|
%shr = lshr i32 %c, 7
|
|
%shl = shl i32 %shr, -2
|
|
br label %loop
|
|
}
|
|
|
|
; Converting the 2 shifts to SHL 6 without the AND is wrong.
|
|
; https://llvm.org/bugs/show_bug.cgi?id=8547
|
|
|
|
define i32 @pr8547(i32* %g) {
|
|
; CHECK-LABEL: @pr8547(
|
|
; CHECK-NEXT: codeRepl:
|
|
; CHECK-NEXT: br label [[FOR_COND:%.*]]
|
|
; CHECK: for.cond:
|
|
; CHECK-NEXT: [[STOREMERGE:%.*]] = phi i32 [ 0, [[CODEREPL:%.*]] ], [ 5, [[FOR_COND]] ]
|
|
; CHECK-NEXT: store i32 [[STOREMERGE]], i32* [[G:%.*]], align 4
|
|
; CHECK-NEXT: [[TMP0:%.*]] = shl nuw nsw i32 [[STOREMERGE]], 6
|
|
; CHECK-NEXT: [[CONV2:%.*]] = and i32 [[TMP0]], 64
|
|
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[CONV2]], 0
|
|
; CHECK-NEXT: br i1 [[TOBOOL]], label [[FOR_COND]], label [[CODEREPL2:%.*]]
|
|
; CHECK: codeRepl2:
|
|
; CHECK-NEXT: ret i32 [[CONV2]]
|
|
;
|
|
codeRepl:
|
|
br label %for.cond
|
|
|
|
for.cond:
|
|
%storemerge = phi i32 [ 0, %codeRepl ], [ 5, %for.cond ]
|
|
store i32 %storemerge, i32* %g, align 4
|
|
%shl = shl i32 %storemerge, 30
|
|
%conv2 = lshr i32 %shl, 24
|
|
%tobool = icmp eq i32 %conv2, 0
|
|
br i1 %tobool, label %for.cond, label %codeRepl2
|
|
|
|
codeRepl2:
|
|
ret i32 %conv2
|
|
}
|
|
|
|
; Two same direction shifts that add up to more than the bitwidth should get
|
|
; folded to zero.
|
|
|
|
define i32 @shl_shl(i32 %A) {
|
|
; CHECK-LABEL: @shl_shl(
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
%B = shl i32 %A, 6
|
|
%C = shl i32 %B, 28
|
|
ret i32 %C
|
|
}
|
|
|
|
define <2 x i33> @shl_shl_splat_vec(<2 x i33> %A) {
|
|
; CHECK-LABEL: @shl_shl_splat_vec(
|
|
; CHECK-NEXT: ret <2 x i33> zeroinitializer
|
|
;
|
|
%B = shl <2 x i33> %A, <i33 5, i33 5>
|
|
%C = shl <2 x i33> %B, <i33 28, i33 28>
|
|
ret <2 x i33> %C
|
|
}
|
|
|
|
; FIXME
|
|
|
|
define <2 x i33> @shl_shl_vec(<2 x i33> %A) {
|
|
; CHECK-LABEL: @shl_shl_vec(
|
|
; CHECK-NEXT: [[B:%.*]] = shl <2 x i33> [[A:%.*]], <i33 6, i33 5>
|
|
; CHECK-NEXT: [[C:%.*]] = shl <2 x i33> [[B]], <i33 27, i33 28>
|
|
; CHECK-NEXT: ret <2 x i33> [[C]]
|
|
;
|
|
%B = shl <2 x i33> %A, <i33 6, i33 5>
|
|
%C = shl <2 x i33> %B, <i33 27, i33 28>
|
|
ret <2 x i33> %C
|
|
}
|
|
|
|
define i232 @lshr_lshr(i232 %A) {
|
|
; CHECK-LABEL: @lshr_lshr(
|
|
; CHECK-NEXT: ret i232 0
|
|
;
|
|
%B = lshr i232 %A, 231
|
|
%C = lshr i232 %B, 1
|
|
ret i232 %C
|
|
}
|
|
|
|
define <2 x i32> @lshr_lshr_splat_vec(<2 x i32> %A) {
|
|
; CHECK-LABEL: @lshr_lshr_splat_vec(
|
|
; CHECK-NEXT: ret <2 x i32> zeroinitializer
|
|
;
|
|
%B = lshr <2 x i32> %A, <i32 28, i32 28>
|
|
%C = lshr <2 x i32> %B, <i32 4, i32 4>
|
|
ret <2 x i32> %C
|
|
}
|
|
|
|
define <2 x i32> @lshr_lshr_vec(<2 x i32> %A) {
|
|
; CHECK-LABEL: @lshr_lshr_vec(
|
|
; CHECK-NEXT: ret <2 x i32> zeroinitializer
|
|
;
|
|
%B = lshr <2 x i32> %A, <i32 29, i32 28>
|
|
%C = lshr <2 x i32> %B, <i32 4, i32 5>
|
|
ret <2 x i32> %C
|
|
}
|
|
|
|
define i8 @shl_trunc_bigger_lshr(i32 %x) {
|
|
; CHECK-LABEL: @shl_trunc_bigger_lshr(
|
|
; CHECK-NEXT: [[SH_DIFF:%.*]] = lshr i32 [[X:%.*]], 2
|
|
; CHECK-NEXT: [[TR_SH_DIFF:%.*]] = trunc i32 [[SH_DIFF]] to i8
|
|
; CHECK-NEXT: [[LT:%.*]] = and i8 [[TR_SH_DIFF]], -8
|
|
; CHECK-NEXT: ret i8 [[LT]]
|
|
;
|
|
%rt = lshr i32 %x, 5
|
|
%tr = trunc i32 %rt to i8
|
|
%lt = shl i8 %tr, 3
|
|
ret i8 %lt
|
|
}
|
|
|
|
define i8 @shl_trunc_smaller_lshr(i32 %x) {
|
|
; CHECK-LABEL: @shl_trunc_smaller_lshr(
|
|
; CHECK-NEXT: [[X_TR:%.*]] = trunc i32 [[X:%.*]] to i8
|
|
; CHECK-NEXT: [[TR_SH_DIFF:%.*]] = shl i8 [[X_TR]], 2
|
|
; CHECK-NEXT: [[LT:%.*]] = and i8 [[TR_SH_DIFF]], -32
|
|
; CHECK-NEXT: ret i8 [[LT]]
|
|
;
|
|
%rt = lshr i32 %x, 3
|
|
%tr = trunc i32 %rt to i8
|
|
%lt = shl i8 %tr, 5
|
|
ret i8 %lt
|
|
}
|
|
|
|
define i24 @shl_trunc_bigger_ashr(i32 %x) {
|
|
; CHECK-LABEL: @shl_trunc_bigger_ashr(
|
|
; CHECK-NEXT: [[SH_DIFF:%.*]] = ashr i32 [[X:%.*]], 9
|
|
; CHECK-NEXT: [[TR_SH_DIFF:%.*]] = trunc i32 [[SH_DIFF]] to i24
|
|
; CHECK-NEXT: [[LT:%.*]] = and i24 [[TR_SH_DIFF]], -8
|
|
; CHECK-NEXT: ret i24 [[LT]]
|
|
;
|
|
%rt = ashr i32 %x, 12
|
|
%tr = trunc i32 %rt to i24
|
|
%lt = shl i24 %tr, 3
|
|
ret i24 %lt
|
|
}
|
|
|
|
define i24 @shl_trunc_smaller_ashr(i32 %x) {
|
|
; CHECK-LABEL: @shl_trunc_smaller_ashr(
|
|
; CHECK-NEXT: [[X_TR:%.*]] = trunc i32 [[X:%.*]] to i24
|
|
; CHECK-NEXT: [[TR_SH_DIFF:%.*]] = shl i24 [[X_TR]], 3
|
|
; CHECK-NEXT: [[LT:%.*]] = and i24 [[TR_SH_DIFF]], -8192
|
|
; CHECK-NEXT: ret i24 [[LT]]
|
|
;
|
|
%rt = ashr i32 %x, 10
|
|
%tr = trunc i32 %rt to i24
|
|
%lt = shl i24 %tr, 13
|
|
ret i24 %lt
|
|
}
|
|
|
|
define i8 @shl_trunc_bigger_shl(i32 %x) {
|
|
; CHECK-LABEL: @shl_trunc_bigger_shl(
|
|
; CHECK-NEXT: [[X_TR:%.*]] = trunc i32 [[X:%.*]] to i8
|
|
; CHECK-NEXT: [[TR:%.*]] = shl i8 [[X_TR]], 6
|
|
; CHECK-NEXT: ret i8 [[TR]]
|
|
;
|
|
%rt = shl i32 %x, 4
|
|
%tr = trunc i32 %rt to i8
|
|
%lt = shl i8 %tr, 2
|
|
ret i8 %lt
|
|
}
|
|
|
|
define i8 @shl_trunc_smaller_shl(i32 %x) {
|
|
; CHECK-LABEL: @shl_trunc_smaller_shl(
|
|
; CHECK-NEXT: [[X_TR:%.*]] = trunc i32 [[X:%.*]] to i8
|
|
; CHECK-NEXT: [[TR:%.*]] = shl i8 [[X_TR]], 6
|
|
; CHECK-NEXT: ret i8 [[TR]]
|
|
;
|
|
%rt = shl i32 %x, 2
|
|
%tr = trunc i32 %rt to i8
|
|
%lt = shl i8 %tr, 4
|
|
ret i8 %lt
|
|
}
|
|
|
|
define i8 @shl_trunc_bigger_lshr_use1(i32 %x) {
|
|
; CHECK-LABEL: @shl_trunc_bigger_lshr_use1(
|
|
; CHECK-NEXT: [[RT:%.*]] = lshr i32 [[X:%.*]], 5
|
|
; CHECK-NEXT: call void @use32(i32 [[RT]])
|
|
; CHECK-NEXT: [[TR:%.*]] = trunc i32 [[RT]] to i8
|
|
; CHECK-NEXT: [[LT:%.*]] = shl i8 [[TR]], 3
|
|
; CHECK-NEXT: ret i8 [[LT]]
|
|
;
|
|
%rt = lshr i32 %x, 5
|
|
call void @use32(i32 %rt)
|
|
%tr = trunc i32 %rt to i8
|
|
%lt = shl i8 %tr, 3
|
|
ret i8 %lt
|
|
}
|
|
|
|
define i8 @shl_trunc_smaller_lshr_use1(i32 %x) {
|
|
; CHECK-LABEL: @shl_trunc_smaller_lshr_use1(
|
|
; CHECK-NEXT: [[RT:%.*]] = lshr i32 [[X:%.*]], 3
|
|
; CHECK-NEXT: call void @use32(i32 [[RT]])
|
|
; CHECK-NEXT: [[TR:%.*]] = trunc i32 [[RT]] to i8
|
|
; CHECK-NEXT: [[LT:%.*]] = shl i8 [[TR]], 5
|
|
; CHECK-NEXT: ret i8 [[LT]]
|
|
;
|
|
%rt = lshr i32 %x, 3
|
|
call void @use32(i32 %rt)
|
|
%tr = trunc i32 %rt to i8
|
|
%lt = shl i8 %tr, 5
|
|
ret i8 %lt
|
|
}
|
|
|
|
define i8 @shl_trunc_bigger_lshr_use2(i32 %x) {
|
|
; CHECK-LABEL: @shl_trunc_bigger_lshr_use2(
|
|
; CHECK-NEXT: [[RT:%.*]] = lshr i32 [[X:%.*]], 5
|
|
; CHECK-NEXT: [[TR:%.*]] = trunc i32 [[RT]] to i8
|
|
; CHECK-NEXT: call void @use8(i8 [[TR]])
|
|
; CHECK-NEXT: [[LT:%.*]] = shl i8 [[TR]], 3
|
|
; CHECK-NEXT: ret i8 [[LT]]
|
|
;
|
|
%rt = lshr i32 %x, 5
|
|
%tr = trunc i32 %rt to i8
|
|
call void @use8(i8 %tr)
|
|
%lt = shl i8 %tr, 3
|
|
ret i8 %lt
|
|
}
|
|
|
|
define i8 @shl_trunc_smaller_lshr_use2(i32 %x) {
|
|
; CHECK-LABEL: @shl_trunc_smaller_lshr_use2(
|
|
; CHECK-NEXT: [[RT:%.*]] = lshr i32 [[X:%.*]], 3
|
|
; CHECK-NEXT: [[TR:%.*]] = trunc i32 [[RT]] to i8
|
|
; CHECK-NEXT: call void @use8(i8 [[TR]])
|
|
; CHECK-NEXT: [[LT:%.*]] = shl i8 [[TR]], 5
|
|
; CHECK-NEXT: ret i8 [[LT]]
|
|
;
|
|
%rt = lshr i32 %x, 3
|
|
%tr = trunc i32 %rt to i8
|
|
call void @use8(i8 %tr)
|
|
%lt = shl i8 %tr, 5
|
|
ret i8 %lt
|
|
}
|