mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 15:16:08 +00:00

SimplifyCFG implements basic jump threading, if a branch is performed on a phi node with constant operands. However, InstCombine canonicalizes such phis to the condition value of a previous branch, if possible. SimplifyCFG does support this as well, but only in the very limited case where the same condition is used in a direct predecessor -- notably, this does not include the common diamond pattern (i.e. two consecutive if/elses on the same condition). This patch extends the code to look back a limited number of blocks to find a branch on the same value, rather than only looking at the direct predecessor. Fixes https://github.com/llvm/llvm-project/issues/54980. Differential Revision: https://reviews.llvm.org/D124159
1570 lines
47 KiB
LLVM
1570 lines
47 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt < %s -simplifycfg -simplifycfg-require-and-preserve-domtree=1 -sink-common-insts -S | FileCheck %s
|
|
; RUN: opt < %s -passes='simplifycfg<sink-common-insts>' -S | FileCheck %s
|
|
|
|
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
|
|
target triple = "x86_64-pc-linux-gnu"
|
|
|
|
define zeroext i1 @test1(i1 zeroext %flag, i32 %blksA, i32 %blksB, i32 %nblks) {
|
|
; CHECK-LABEL: @test1(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[NBLKS:%.*]], [[BLKSB:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp ule i32 [[ADD]], [[BLKSA:%.*]]
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i32 [[BLKSA]], [[NBLKS]]
|
|
; CHECK-NEXT: [[CMP2_SINK:%.*]] = select i1 [[FLAG:%.*]], i1 [[CMP]], i1 [[CMP2]]
|
|
; CHECK-NEXT: [[FROMBOOL3:%.*]] = zext i1 [[CMP2_SINK]] to i8
|
|
; CHECK-NEXT: [[TOBOOL4:%.*]] = icmp ne i8 [[FROMBOOL3]], 0
|
|
; CHECK-NEXT: ret i1 [[TOBOOL4]]
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%cmp = icmp uge i32 %blksA, %nblks
|
|
%frombool1 = zext i1 %cmp to i8
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%add = add i32 %nblks, %blksB
|
|
%cmp2 = icmp ule i32 %add, %blksA
|
|
%frombool3 = zext i1 %cmp2 to i8
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%obeys.0 = phi i8 [ %frombool1, %if.then ], [ %frombool3, %if.else ]
|
|
%tobool4 = icmp ne i8 %obeys.0, 0
|
|
ret i1 %tobool4
|
|
}
|
|
|
|
define zeroext i1 @test2(i1 zeroext %flag, i32 %blksA, i32 %blksB, i32 %nblks) {
|
|
; CHECK-LABEL: @test2(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[NBLKS:%.*]], [[BLKSB:%.*]]
|
|
; CHECK-NEXT: [[ADD_SINK:%.*]] = select i1 [[FLAG:%.*]], i32 [[NBLKS]], i32 [[ADD]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp uge i32 [[BLKSA:%.*]], [[ADD_SINK]]
|
|
; CHECK-NEXT: [[FROMBOOL3:%.*]] = zext i1 [[CMP2]] to i8
|
|
; CHECK-NEXT: [[TOBOOL4:%.*]] = icmp ne i8 [[FROMBOOL3]], 0
|
|
; CHECK-NEXT: ret i1 [[TOBOOL4]]
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%cmp = icmp uge i32 %blksA, %nblks
|
|
%frombool1 = zext i1 %cmp to i8
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%add = add i32 %nblks, %blksB
|
|
%cmp2 = icmp uge i32 %blksA, %add
|
|
%frombool3 = zext i1 %cmp2 to i8
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%obeys.0 = phi i8 [ %frombool1, %if.then ], [ %frombool3, %if.else ]
|
|
%tobool4 = icmp ne i8 %obeys.0, 0
|
|
ret i1 %tobool4
|
|
}
|
|
|
|
declare i32 @foo(i32, i32) nounwind readnone
|
|
|
|
define i32 @test3(i1 zeroext %flag, i32 %x, i32 %y) {
|
|
; CHECK-LABEL: @test3(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[X_Y:%.*]] = select i1 [[FLAG:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]]
|
|
; CHECK-NEXT: [[X1:%.*]] = call i32 @foo(i32 [[X_Y]], i32 0) #[[ATTR0:[0-9]+]]
|
|
; CHECK-NEXT: [[Y1:%.*]] = call i32 @foo(i32 [[X_Y]], i32 1) #[[ATTR0]]
|
|
; CHECK-NEXT: [[RET:%.*]] = add i32 [[X1]], [[Y1]]
|
|
; CHECK-NEXT: ret i32 [[RET]]
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%x0 = call i32 @foo(i32 %x, i32 0) nounwind readnone
|
|
%y0 = call i32 @foo(i32 %x, i32 1) nounwind readnone
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%x1 = call i32 @foo(i32 %y, i32 0) nounwind readnone
|
|
%y1 = call i32 @foo(i32 %y, i32 1) nounwind readnone
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%xx = phi i32 [ %x0, %if.then ], [ %x1, %if.else ]
|
|
%yy = phi i32 [ %y0, %if.then ], [ %y1, %if.else ]
|
|
%ret = add i32 %xx, %yy
|
|
ret i32 %ret
|
|
}
|
|
|
|
|
|
define i32 @test4(i1 zeroext %flag, i32 %x, i32* %y) {
|
|
; CHECK-LABEL: @test4(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[DOT:%.*]] = select i1 [[FLAG:%.*]], i32 5, i32 7
|
|
; CHECK-NEXT: [[B:%.*]] = add i32 [[X:%.*]], [[DOT]]
|
|
; CHECK-NEXT: store i32 [[B]], i32* [[Y:%.*]], align 4
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%a = add i32 %x, 5
|
|
store i32 %a, i32* %y
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%b = add i32 %x, 7
|
|
store i32 %b, i32* %y
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i32 1
|
|
}
|
|
|
|
|
|
define i32 @test5(i1 zeroext %flag, i32 %x, i32* %y) {
|
|
; CHECK-LABEL: @test5(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[A:%.*]] = add i32 [[X:%.*]], 5
|
|
; CHECK-NEXT: store volatile i32 [[A]], i32* [[Y:%.*]], align 4
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[B:%.*]] = add i32 [[X]], 7
|
|
; CHECK-NEXT: store i32 [[B]], i32* [[Y]], align 4
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%a = add i32 %x, 5
|
|
store volatile i32 %a, i32* %y
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%b = add i32 %x, 7
|
|
store i32 %b, i32* %y
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i32 1
|
|
}
|
|
|
|
|
|
define i32 @test6(i1 zeroext %flag, i32 %x, i32* %y) {
|
|
; CHECK-LABEL: @test6(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[DOT:%.*]] = select i1 [[FLAG:%.*]], i32 5, i32 7
|
|
; CHECK-NEXT: [[B:%.*]] = add i32 [[X:%.*]], [[DOT]]
|
|
; CHECK-NEXT: store volatile i32 [[B]], i32* [[Y:%.*]], align 4
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%a = add i32 %x, 5
|
|
store volatile i32 %a, i32* %y
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%b = add i32 %x, 7
|
|
store volatile i32 %b, i32* %y
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i32 1
|
|
}
|
|
|
|
|
|
define i32 @test7(i1 zeroext %flag, i32 %x, i32* %y) {
|
|
; CHECK-LABEL: @test7(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[DOT:%.*]] = select i1 [[FLAG:%.*]], i32 5, i32 7
|
|
; CHECK-NEXT: [[W:%.*]] = load volatile i32, i32* [[Y:%.*]], align 4
|
|
; CHECK-NEXT: [[B:%.*]] = add i32 [[W]], [[DOT]]
|
|
; CHECK-NEXT: store volatile i32 [[B]], i32* [[Y]], align 4
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%z = load volatile i32, i32* %y
|
|
%a = add i32 %z, 5
|
|
store volatile i32 %a, i32* %y
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%w = load volatile i32, i32* %y
|
|
%b = add i32 %w, 7
|
|
store volatile i32 %b, i32* %y
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i32 1
|
|
}
|
|
|
|
|
|
; %z and %w are in different blocks. We shouldn't sink the add because
|
|
; there may be intervening memory instructions.
|
|
define i32 @test8(i1 zeroext %flag, i32 %x, i32* %y) {
|
|
; CHECK-LABEL: @test8(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[Z:%.*]] = load volatile i32, i32* [[Y:%.*]], align 4
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[A:%.*]] = add i32 [[Z]], 5
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[W:%.*]] = load volatile i32, i32* [[Y]], align 4
|
|
; CHECK-NEXT: [[B:%.*]] = add i32 [[W]], 7
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[B_SINK:%.*]] = phi i32 [ [[B]], [[IF_ELSE]] ], [ [[A]], [[IF_THEN]] ]
|
|
; CHECK-NEXT: store volatile i32 [[B_SINK]], i32* [[Y]], align 4
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
%z = load volatile i32, i32* %y
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%a = add i32 %z, 5
|
|
store volatile i32 %a, i32* %y
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%w = load volatile i32, i32* %y
|
|
%b = add i32 %w, 7
|
|
store volatile i32 %b, i32* %y
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i32 1
|
|
}
|
|
|
|
|
|
; The extra store in %if.then means %z and %w are not equivalent.
|
|
define i32 @test9(i1 zeroext %flag, i32 %x, i32* %y, i32* %p) {
|
|
; CHECK-LABEL: @test9(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: store i32 7, i32* [[P:%.*]], align 4
|
|
; CHECK-NEXT: [[Z:%.*]] = load volatile i32, i32* [[Y:%.*]], align 4
|
|
; CHECK-NEXT: store i32 6, i32* [[P]], align 4
|
|
; CHECK-NEXT: [[A:%.*]] = add i32 [[Z]], 5
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[W:%.*]] = load volatile i32, i32* [[Y]], align 4
|
|
; CHECK-NEXT: [[B:%.*]] = add i32 [[W]], 7
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[B_SINK:%.*]] = phi i32 [ [[B]], [[IF_ELSE]] ], [ [[A]], [[IF_THEN]] ]
|
|
; CHECK-NEXT: store volatile i32 [[B_SINK]], i32* [[Y]], align 4
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
store i32 7, i32* %p
|
|
%z = load volatile i32, i32* %y
|
|
store i32 6, i32* %p
|
|
%a = add i32 %z, 5
|
|
store volatile i32 %a, i32* %y
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%w = load volatile i32, i32* %y
|
|
%b = add i32 %w, 7
|
|
store volatile i32 %b, i32* %y
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i32 1
|
|
}
|
|
|
|
|
|
%struct.anon = type { i32, i32 }
|
|
|
|
; The GEP indexes a struct type so cannot have a variable last index.
|
|
define i32 @test10(i1 zeroext %flag, i32 %x, i32* %y, %struct.anon* %s) {
|
|
; CHECK-LABEL: @test10(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[DUMMY:%.*]] = add i32 [[X:%.*]], 5
|
|
; CHECK-NEXT: [[GEPA:%.*]] = getelementptr inbounds [[STRUCT_ANON:%.*]], %struct.anon* [[S:%.*]], i32 0, i32 0
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[DUMMY1:%.*]] = add i32 [[X]], 6
|
|
; CHECK-NEXT: [[GEPB:%.*]] = getelementptr inbounds [[STRUCT_ANON]], %struct.anon* [[S]], i32 0, i32 1
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[GEPB_SINK:%.*]] = phi i32* [ [[GEPB]], [[IF_ELSE]] ], [ [[GEPA]], [[IF_THEN]] ]
|
|
; CHECK-NEXT: store volatile i32 [[X]], i32* [[GEPB_SINK]], align 4
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%dummy = add i32 %x, 5
|
|
%gepa = getelementptr inbounds %struct.anon, %struct.anon* %s, i32 0, i32 0
|
|
store volatile i32 %x, i32* %gepa
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%dummy1 = add i32 %x, 6
|
|
%gepb = getelementptr inbounds %struct.anon, %struct.anon* %s, i32 0, i32 1
|
|
store volatile i32 %x, i32* %gepb
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i32 1
|
|
}
|
|
|
|
|
|
; The shufflevector's mask operand cannot be merged in a PHI.
|
|
define i32 @test11(i1 zeroext %flag, i32 %w, <2 x i32> %x, <2 x i32> %y) {
|
|
; CHECK-LABEL: @test11(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[DUMMY:%.*]] = add i32 [[W:%.*]], 5
|
|
; CHECK-NEXT: [[SV1:%.*]] = shufflevector <2 x i32> [[X:%.*]], <2 x i32> [[Y:%.*]], <2 x i32> <i32 0, i32 1>
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[DUMMY1:%.*]] = add i32 [[W]], 6
|
|
; CHECK-NEXT: [[SV2:%.*]] = shufflevector <2 x i32> [[X]], <2 x i32> [[Y]], <2 x i32> <i32 1, i32 0>
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[P:%.*]] = phi <2 x i32> [ [[SV1]], [[IF_THEN]] ], [ [[SV2]], [[IF_ELSE]] ]
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%dummy = add i32 %w, 5
|
|
%sv1 = shufflevector <2 x i32> %x, <2 x i32> %y, <2 x i32> <i32 0, i32 1>
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%dummy1 = add i32 %w, 6
|
|
%sv2 = shufflevector <2 x i32> %x, <2 x i32> %y, <2 x i32> <i32 1, i32 0>
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%p = phi <2 x i32> [ %sv1, %if.then ], [ %sv2, %if.else ]
|
|
ret i32 1
|
|
}
|
|
|
|
|
|
; We can't common an intrinsic!
|
|
define i32 @test12(i1 zeroext %flag, i32 %w, i32 %x, i32 %y) {
|
|
; CHECK-LABEL: @test12(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[DUMMY:%.*]] = add i32 [[W:%.*]], 5
|
|
; CHECK-NEXT: [[SV1:%.*]] = call i32 @llvm.ctlz.i32(i32 [[X:%.*]], i1 false)
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[DUMMY1:%.*]] = add i32 [[W]], 6
|
|
; CHECK-NEXT: [[SV2:%.*]] = call i32 @llvm.cttz.i32(i32 [[X]], i1 false)
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[P:%.*]] = phi i32 [ [[SV1]], [[IF_THEN]] ], [ [[SV2]], [[IF_ELSE]] ]
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%dummy = add i32 %w, 5
|
|
%sv1 = call i32 @llvm.ctlz.i32(i32 %x, i1 false)
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%dummy1 = add i32 %w, 6
|
|
%sv2 = call i32 @llvm.cttz.i32(i32 %x, i1 false)
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%p = phi i32 [ %sv1, %if.then ], [ %sv2, %if.else ]
|
|
ret i32 1
|
|
}
|
|
|
|
declare i32 @llvm.ctlz.i32(i32 %x, i1 immarg) readnone
|
|
declare i32 @llvm.cttz.i32(i32 %x, i1 immarg) readnone
|
|
|
|
|
|
; The TBAA metadata should be properly combined.
|
|
define i32 @test13(i1 zeroext %flag, i32 %x, i32* %y) {
|
|
; CHECK-LABEL: @test13(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[DOT:%.*]] = select i1 [[FLAG:%.*]], i32 5, i32 7
|
|
; CHECK-NEXT: [[W:%.*]] = load volatile i32, i32* [[Y:%.*]], align 4
|
|
; CHECK-NEXT: [[B:%.*]] = add i32 [[W]], [[DOT]]
|
|
; CHECK-NEXT: store volatile i32 [[B]], i32* [[Y]], align 4, !tbaa [[TBAA4:![0-9]+]]
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%z = load volatile i32, i32* %y
|
|
%a = add i32 %z, 5
|
|
store volatile i32 %a, i32* %y, !tbaa !3
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%w = load volatile i32, i32* %y
|
|
%b = add i32 %w, 7
|
|
store volatile i32 %b, i32* %y, !tbaa !4
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i32 1
|
|
}
|
|
|
|
!0 = !{ !"an example type tree" }
|
|
!1 = !{ !"int", !0 }
|
|
!2 = !{ !"float", !0 }
|
|
!3 = !{ !"const float", !2, i64 0 }
|
|
!4 = !{ !"special float", !2, i64 1 }
|
|
|
|
|
|
; The call should be commoned.
|
|
define i32 @test13a(i1 zeroext %flag, i32 %w, i32 %x, i32 %y) {
|
|
; CHECK-LABEL: @test13a(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[X_Y:%.*]] = select i1 [[FLAG:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]]
|
|
; CHECK-NEXT: [[SV2:%.*]] = call i32 @bar(i32 [[X_Y]])
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%sv1 = call i32 @bar(i32 %x)
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%sv2 = call i32 @bar(i32 %y)
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%p = phi i32 [ %sv1, %if.then ], [ %sv2, %if.else ]
|
|
ret i32 1
|
|
}
|
|
declare i32 @bar(i32)
|
|
|
|
|
|
; The load should be commoned.
|
|
define i32 @test14(i1 zeroext %flag, i32 %w, i32 %x, i32 %y, %struct.anon* %s) {
|
|
; CHECK-LABEL: @test14(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[DOT:%.*]] = select i1 [[FLAG:%.*]], i32 1, i32 4
|
|
; CHECK-NEXT: [[DOT2:%.*]] = select i1 [[FLAG]], i32 56, i32 57
|
|
; CHECK-NEXT: [[DUMMY2:%.*]] = add i32 [[X:%.*]], [[DOT]]
|
|
; CHECK-NEXT: [[GEPB:%.*]] = getelementptr inbounds [[STRUCT_ANON:%.*]], %struct.anon* [[S:%.*]], i32 0, i32 1
|
|
; CHECK-NEXT: [[SV2:%.*]] = load i32, i32* [[GEPB]], align 4
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32 [[SV2]], [[DOT2]]
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%dummy = add i32 %x, 1
|
|
%gepa = getelementptr inbounds %struct.anon, %struct.anon* %s, i32 0, i32 1
|
|
%sv1 = load i32, i32* %gepa
|
|
%cmp1 = icmp eq i32 %sv1, 56
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%dummy2 = add i32 %x, 4
|
|
%gepb = getelementptr inbounds %struct.anon, %struct.anon* %s, i32 0, i32 1
|
|
%sv2 = load i32, i32* %gepb
|
|
%cmp2 = icmp eq i32 %sv2, 57
|
|
call void @llvm.dbg.value(metadata i32 0, metadata !9, metadata !DIExpression()), !dbg !11
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%p = phi i1 [ %cmp1, %if.then ], [ %cmp2, %if.else ]
|
|
ret i32 1
|
|
}
|
|
|
|
declare void @llvm.dbg.value(metadata, metadata, metadata)
|
|
!llvm.module.flags = !{!5, !6}
|
|
!llvm.dbg.cu = !{!7}
|
|
|
|
!5 = !{i32 2, !"Dwarf Version", i32 4}
|
|
!6 = !{i32 2, !"Debug Info Version", i32 3}
|
|
!7 = distinct !DICompileUnit(language: DW_LANG_C99, file: !10)
|
|
!8 = distinct !DISubprogram(name: "foo", unit: !7)
|
|
!9 = !DILocalVariable(name: "b", line: 1, arg: 2, scope: !8)
|
|
!10 = !DIFile(filename: "a.c", directory: "a/b")
|
|
!11 = !DILocation(line: 1, column: 14, scope: !8)
|
|
|
|
|
|
; The load should be commoned.
|
|
define i32 @test15(i1 zeroext %flag, i32 %w, i32 %x, i32 %y, %struct.anon* %s) {
|
|
; CHECK-LABEL: @test15(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[DUMMY:%.*]] = add i32 [[X:%.*]], 1
|
|
; CHECK-NEXT: [[GEPA:%.*]] = getelementptr inbounds [[STRUCT_ANON:%.*]], %struct.anon* [[S:%.*]], i32 0, i32 0
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[DUMMY2:%.*]] = add i32 [[X]], 4
|
|
; CHECK-NEXT: [[GEPB:%.*]] = getelementptr inbounds [[STRUCT_ANON]], %struct.anon* [[S]], i32 0, i32 1
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[GEPB_SINK:%.*]] = phi i32* [ [[GEPB]], [[IF_ELSE]] ], [ [[GEPA]], [[IF_THEN]] ]
|
|
; CHECK-NEXT: [[DOTSINK:%.*]] = phi i64 [ 57, [[IF_ELSE]] ], [ 56, [[IF_THEN]] ]
|
|
; CHECK-NEXT: [[SV2:%.*]] = load i32, i32* [[GEPB_SINK]], align 4
|
|
; CHECK-NEXT: [[EXT2:%.*]] = zext i32 [[SV2]] to i64
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i64 [[EXT2]], [[DOTSINK]]
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%dummy = add i32 %x, 1
|
|
%gepa = getelementptr inbounds %struct.anon, %struct.anon* %s, i32 0, i32 0
|
|
%sv1 = load i32, i32* %gepa
|
|
%ext1 = zext i32 %sv1 to i64
|
|
%cmp1 = icmp eq i64 %ext1, 56
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%dummy2 = add i32 %x, 4
|
|
%gepb = getelementptr inbounds %struct.anon, %struct.anon* %s, i32 0, i32 1
|
|
%sv2 = load i32, i32* %gepb
|
|
%ext2 = zext i32 %sv2 to i64
|
|
%cmp2 = icmp eq i64 %ext2, 57
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%p = phi i1 [ %cmp1, %if.then ], [ %cmp2, %if.else ]
|
|
ret i32 1
|
|
}
|
|
|
|
|
|
define zeroext i1 @test_crash(i1 zeroext %flag, i32* %i4, i32* %m, i32* %n) {
|
|
; CHECK-LABEL: @test_crash(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[I4:%.*]], align 4
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[TMP3:%.*]] = load i32, i32* [[M:%.*]], align 4
|
|
; CHECK-NEXT: [[TMP4:%.*]] = load i32, i32* [[N:%.*]], align 4
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[TMP4_SINK:%.*]] = phi i32 [ [[TMP4]], [[IF_ELSE]] ], [ -1, [[IF_THEN]] ]
|
|
; CHECK-NEXT: [[TMP3_SINK:%.*]] = phi i32 [ [[TMP3]], [[IF_ELSE]] ], [ [[TMP1]], [[IF_THEN]] ]
|
|
; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[TMP3_SINK]], [[TMP4_SINK]]
|
|
; CHECK-NEXT: store i32 [[TMP5]], i32* [[I4]], align 4
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%tmp1 = load i32, i32* %i4
|
|
%tmp2 = add i32 %tmp1, -1
|
|
store i32 %tmp2, i32* %i4
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%tmp3 = load i32, i32* %m
|
|
%tmp4 = load i32, i32* %n
|
|
%tmp5 = add i32 %tmp3, %tmp4
|
|
store i32 %tmp5, i32* %i4
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i1 true
|
|
}
|
|
|
|
; No checks for test_crash - just ensure it doesn't crash!
|
|
|
|
define zeroext i1 @test16(i1 zeroext %flag, i1 zeroext %flag2, i32 %blksA, i32 %blksB, i32 %nblks) {
|
|
; CHECK-LABEL: @test16(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i32 [[BLKSA:%.*]], [[NBLKS:%.*]]
|
|
; CHECK-NEXT: [[FROMBOOL1:%.*]] = zext i1 [[CMP]] to i8
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: br i1 [[FLAG2:%.*]], label [[IF_THEN2:%.*]], label [[IF_END]]
|
|
; CHECK: if.then2:
|
|
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[NBLKS]], [[BLKSB:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp ule i32 [[ADD]], [[BLKSA]]
|
|
; CHECK-NEXT: [[FROMBOOL3:%.*]] = zext i1 [[CMP2]] to i8
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[OBEYS_0:%.*]] = phi i8 [ [[FROMBOOL1]], [[IF_THEN]] ], [ [[FROMBOOL3]], [[IF_THEN2]] ], [ 0, [[IF_ELSE]] ]
|
|
; CHECK-NEXT: [[TOBOOL4:%.*]] = icmp ne i8 [[OBEYS_0]], 0
|
|
; CHECK-NEXT: ret i1 [[TOBOOL4]]
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%cmp = icmp uge i32 %blksA, %nblks
|
|
%frombool1 = zext i1 %cmp to i8
|
|
br label %if.end
|
|
|
|
if.else:
|
|
br i1 %flag2, label %if.then2, label %if.end
|
|
|
|
if.then2:
|
|
%add = add i32 %nblks, %blksB
|
|
%cmp2 = icmp ule i32 %add, %blksA
|
|
%frombool3 = zext i1 %cmp2 to i8
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%obeys.0 = phi i8 [ %frombool1, %if.then ], [ %frombool3, %if.then2 ], [ 0, %if.else ]
|
|
%tobool4 = icmp ne i8 %obeys.0, 0
|
|
ret i1 %tobool4
|
|
}
|
|
|
|
|
|
define zeroext i1 @test16a(i1 zeroext %flag, i1 zeroext %flag2, i32 %blksA, i32 %blksB, i32 %nblks, i8* %p) {
|
|
; CHECK-LABEL: @test16a(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i32 [[BLKSA:%.*]], [[NBLKS:%.*]]
|
|
; CHECK-NEXT: br label [[IF_END_SINK_SPLIT:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: br i1 [[FLAG2:%.*]], label [[IF_THEN2:%.*]], label [[IF_END:%.*]]
|
|
; CHECK: if.then2:
|
|
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[NBLKS]], [[BLKSB:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp ule i32 [[ADD]], [[BLKSA]]
|
|
; CHECK-NEXT: br label [[IF_END_SINK_SPLIT]]
|
|
; CHECK: if.end.sink.split:
|
|
; CHECK-NEXT: [[CMP2_SINK:%.*]] = phi i1 [ [[CMP2]], [[IF_THEN2]] ], [ [[CMP]], [[IF_THEN]] ]
|
|
; CHECK-NEXT: [[FROMBOOL3:%.*]] = zext i1 [[CMP2_SINK]] to i8
|
|
; CHECK-NEXT: store i8 [[FROMBOOL3]], i8* [[P:%.*]], align 1
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%cmp = icmp uge i32 %blksA, %nblks
|
|
%frombool1 = zext i1 %cmp to i8
|
|
store i8 %frombool1, i8* %p
|
|
br label %if.end
|
|
|
|
if.else:
|
|
br i1 %flag2, label %if.then2, label %if.end
|
|
|
|
if.then2:
|
|
%add = add i32 %nblks, %blksB
|
|
%cmp2 = icmp ule i32 %add, %blksA
|
|
%frombool3 = zext i1 %cmp2 to i8
|
|
store i8 %frombool3, i8* %p
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i1 true
|
|
}
|
|
|
|
|
|
define zeroext i1 @test17(i32 %flag, i32 %blksA, i32 %blksB, i32 %nblks) {
|
|
; CHECK-LABEL: @test17(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: switch i32 [[FLAG:%.*]], label [[IF_END:%.*]] [
|
|
; CHECK-NEXT: i32 0, label [[IF_THEN:%.*]]
|
|
; CHECK-NEXT: i32 1, label [[IF_THEN2:%.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i32 [[BLKSA:%.*]], [[NBLKS:%.*]]
|
|
; CHECK-NEXT: br label [[IF_END_SINK_SPLIT:%.*]]
|
|
; CHECK: if.then2:
|
|
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[NBLKS]], [[BLKSB:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp ule i32 [[ADD]], [[BLKSA]]
|
|
; CHECK-NEXT: br label [[IF_END_SINK_SPLIT]]
|
|
; CHECK: if.end.sink.split:
|
|
; CHECK-NEXT: [[CMP2_SINK:%.*]] = phi i1 [ [[CMP2]], [[IF_THEN2]] ], [ [[CMP]], [[IF_THEN]] ]
|
|
; CHECK-NEXT: [[FROMBOOL3:%.*]] = call i8 @i1toi8(i1 [[CMP2_SINK]])
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[OBEYS_0:%.*]] = phi i8 [ 0, [[ENTRY:%.*]] ], [ [[FROMBOOL3]], [[IF_END_SINK_SPLIT]] ]
|
|
; CHECK-NEXT: [[TOBOOL4:%.*]] = icmp ne i8 [[OBEYS_0]], 0
|
|
; CHECK-NEXT: ret i1 [[TOBOOL4]]
|
|
;
|
|
entry:
|
|
switch i32 %flag, label %if.end [
|
|
i32 0, label %if.then
|
|
i32 1, label %if.then2
|
|
]
|
|
|
|
if.then:
|
|
%cmp = icmp uge i32 %blksA, %nblks
|
|
%frombool1 = call i8 @i1toi8(i1 %cmp)
|
|
br label %if.end
|
|
|
|
if.then2:
|
|
%add = add i32 %nblks, %blksB
|
|
%cmp2 = icmp ule i32 %add, %blksA
|
|
%frombool3 = call i8 @i1toi8(i1 %cmp2)
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%obeys.0 = phi i8 [ %frombool1, %if.then ], [ %frombool3, %if.then2 ], [ 0, %entry ]
|
|
%tobool4 = icmp ne i8 %obeys.0, 0
|
|
ret i1 %tobool4
|
|
}
|
|
declare i8 @i1toi8(i1)
|
|
|
|
|
|
|
|
|
|
|
|
define zeroext i1 @test18(i32 %flag, i32 %blksA, i32 %blksB, i32 %nblks) {
|
|
; CHECK-LABEL: @test18(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: switch i32 [[FLAG:%.*]], label [[IF_THEN3:%.*]] [
|
|
; CHECK-NEXT: i32 0, label [[IF_THEN:%.*]]
|
|
; CHECK-NEXT: i32 1, label [[IF_THEN2:%.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i32 [[BLKSA:%.*]], [[NBLKS:%.*]]
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.then2:
|
|
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[NBLKS]], [[BLKSB:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp ule i32 [[ADD]], [[BLKSA]]
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.then3:
|
|
; CHECK-NEXT: [[ADD2:%.*]] = add i32 [[NBLKS]], [[BLKSA]]
|
|
; CHECK-NEXT: [[CMP3:%.*]] = icmp ule i32 [[ADD2]], [[BLKSA]]
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[CMP3_SINK:%.*]] = phi i1 [ [[CMP3]], [[IF_THEN3]] ], [ [[CMP2]], [[IF_THEN2]] ], [ [[CMP]], [[IF_THEN]] ]
|
|
; CHECK-NEXT: [[FROMBOOL4:%.*]] = zext i1 [[CMP3_SINK]] to i8
|
|
; CHECK-NEXT: [[TOBOOL4:%.*]] = icmp ne i8 [[FROMBOOL4]], 0
|
|
; CHECK-NEXT: ret i1 [[TOBOOL4]]
|
|
;
|
|
entry:
|
|
switch i32 %flag, label %if.then3 [
|
|
i32 0, label %if.then
|
|
i32 1, label %if.then2
|
|
]
|
|
|
|
if.then:
|
|
%cmp = icmp uge i32 %blksA, %nblks
|
|
%frombool1 = zext i1 %cmp to i8
|
|
br label %if.end
|
|
|
|
if.then2:
|
|
%add = add i32 %nblks, %blksB
|
|
%cmp2 = icmp ule i32 %add, %blksA
|
|
%frombool3 = zext i1 %cmp2 to i8
|
|
br label %if.end
|
|
|
|
if.then3:
|
|
%add2 = add i32 %nblks, %blksA
|
|
%cmp3 = icmp ule i32 %add2, %blksA
|
|
%frombool4 = zext i1 %cmp3 to i8
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%obeys.0 = phi i8 [ %frombool1, %if.then ], [ %frombool3, %if.then2 ], [ %frombool4, %if.then3 ]
|
|
%tobool4 = icmp ne i8 %obeys.0, 0
|
|
ret i1 %tobool4
|
|
}
|
|
|
|
|
|
define i32 @test_pr30188(i1 zeroext %flag, i32 %x) {
|
|
; CHECK-LABEL: @test_pr30188(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[Y:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: [[Z:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: store i32 [[X:%.*]], i32* [[Y]], align 4
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: store i32 [[X]], i32* [[Z]], align 4
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
%y = alloca i32
|
|
%z = alloca i32
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
store i32 %x, i32* %y
|
|
br label %if.end
|
|
|
|
if.else:
|
|
store i32 %x, i32* %z
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i32 1
|
|
}
|
|
|
|
|
|
define i32 @test_pr30188a(i1 zeroext %flag, i32 %x) {
|
|
; CHECK-LABEL: @test_pr30188a(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[Y:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: [[Z:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: call void @g()
|
|
; CHECK-NEXT: [[ONE:%.*]] = load i32, i32* [[Y]], align 4
|
|
; CHECK-NEXT: [[TWO:%.*]] = add i32 [[ONE]], 2
|
|
; CHECK-NEXT: store i32 [[TWO]], i32* [[Y]], align 4
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[THREE:%.*]] = load i32, i32* [[Z]], align 4
|
|
; CHECK-NEXT: [[FOUR:%.*]] = add i32 [[THREE]], 2
|
|
; CHECK-NEXT: store i32 [[FOUR]], i32* [[Y]], align 4
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
%y = alloca i32
|
|
%z = alloca i32
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
call void @g()
|
|
%one = load i32, i32* %y
|
|
%two = add i32 %one, 2
|
|
store i32 %two, i32* %y
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%three = load i32, i32* %z
|
|
%four = add i32 %three, 2
|
|
store i32 %four, i32* %y
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i32 1
|
|
}
|
|
|
|
|
|
; The phi is confusing - both add instructions are used by it, but
|
|
; not on their respective unconditional arcs. It should not be
|
|
; optimized.
|
|
define void @test_pr30292(i1 %cond, i1 %cond2, i32 %a, i32 %b) {
|
|
; CHECK-LABEL: @test_pr30292(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[ADD1:%.*]] = add i32 [[A:%.*]], 1
|
|
; CHECK-NEXT: br label [[SUCC:%.*]]
|
|
; CHECK: two:
|
|
; CHECK-NEXT: call void @g()
|
|
; CHECK-NEXT: [[ADD2:%.*]] = add i32 [[A]], 1
|
|
; CHECK-NEXT: br label [[TWO:%.*]]
|
|
; CHECK: succ:
|
|
; CHECK-NEXT: [[P:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD1]], [[SUCC]] ]
|
|
; CHECK-NEXT: br i1 [[COND:%.*]], label [[TWO]], label [[SUCC]]
|
|
;
|
|
entry:
|
|
%add1 = add i32 %a, 1
|
|
br label %succ
|
|
|
|
one:
|
|
br i1 %cond, label %two, label %succ
|
|
|
|
two:
|
|
call void @g()
|
|
%add2 = add i32 %a, 1
|
|
br label %succ
|
|
|
|
succ:
|
|
%p = phi i32 [ 0, %entry ], [ %add1, %one ], [ %add2, %two ]
|
|
br label %one
|
|
}
|
|
declare void @g()
|
|
|
|
|
|
define zeroext i1 @test_pr30244(i1 zeroext %flag, i1 zeroext %flag2, i32 %blksA, i32 %blksB, i32 %nblks) {
|
|
; CHECK-LABEL: @test_pr30244(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[P:%.*]] = alloca i8, align 1
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i32 [[BLKSA:%.*]], [[NBLKS:%.*]]
|
|
; CHECK-NEXT: [[FROMBOOL1:%.*]] = zext i1 [[CMP]] to i8
|
|
; CHECK-NEXT: store i8 [[FROMBOOL1]], i8* [[P]], align 1
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: br i1 [[FLAG2:%.*]], label [[IF_THEN2:%.*]], label [[IF_END]]
|
|
; CHECK: if.then2:
|
|
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[NBLKS]], [[BLKSB:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp ule i32 [[ADD]], [[BLKSA]]
|
|
; CHECK-NEXT: [[FROMBOOL3:%.*]] = zext i1 [[CMP2]] to i8
|
|
; CHECK-NEXT: store i8 [[FROMBOOL3]], i8* [[P]], align 1
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
entry:
|
|
%p = alloca i8
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%cmp = icmp uge i32 %blksA, %nblks
|
|
%frombool1 = zext i1 %cmp to i8
|
|
store i8 %frombool1, i8* %p
|
|
br label %if.end
|
|
|
|
if.else:
|
|
br i1 %flag2, label %if.then2, label %if.end
|
|
|
|
if.then2:
|
|
%add = add i32 %nblks, %blksB
|
|
%cmp2 = icmp ule i32 %add, %blksA
|
|
%frombool3 = zext i1 %cmp2 to i8
|
|
store i8 %frombool3, i8* %p
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i1 true
|
|
}
|
|
|
|
|
|
define i32 @test_pr30373a(i1 zeroext %flag, i32 %x, i32 %y) {
|
|
; CHECK-LABEL: @test_pr30373a(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[X_Y:%.*]] = select i1 [[FLAG:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]]
|
|
; CHECK-NEXT: [[X1:%.*]] = call i32 @foo(i32 [[X_Y]], i32 0) #[[ATTR0]]
|
|
; CHECK-NEXT: [[Y1:%.*]] = call i32 @foo(i32 [[X_Y]], i32 1) #[[ATTR0]]
|
|
; CHECK-NEXT: [[Z1:%.*]] = lshr i32 [[Y1]], 8
|
|
; CHECK-NEXT: [[RET:%.*]] = add i32 [[X1]], [[Z1]]
|
|
; CHECK-NEXT: ret i32 [[RET]]
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%x0 = call i32 @foo(i32 %x, i32 0) nounwind readnone
|
|
%y0 = call i32 @foo(i32 %x, i32 1) nounwind readnone
|
|
%z0 = lshr i32 %y0, 8
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%x1 = call i32 @foo(i32 %y, i32 0) nounwind readnone
|
|
%y1 = call i32 @foo(i32 %y, i32 1) nounwind readnone
|
|
%z1 = lshr exact i32 %y1, 8
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%xx = phi i32 [ %x0, %if.then ], [ %x1, %if.else ]
|
|
%yy = phi i32 [ %z0, %if.then ], [ %z1, %if.else ]
|
|
%ret = add i32 %xx, %yy
|
|
ret i32 %ret
|
|
}
|
|
|
|
|
|
define i32 @test_pr30373b(i1 zeroext %flag, i32 %x, i32 %y) {
|
|
; CHECK-LABEL: @test_pr30373b(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[X_Y:%.*]] = select i1 [[FLAG:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]]
|
|
; CHECK-NEXT: [[X1:%.*]] = call i32 @foo(i32 [[X_Y]], i32 0) #[[ATTR0]]
|
|
; CHECK-NEXT: [[Y1:%.*]] = call i32 @foo(i32 [[X_Y]], i32 1) #[[ATTR0]]
|
|
; CHECK-NEXT: [[Z1:%.*]] = lshr i32 [[Y1]], 8
|
|
; CHECK-NEXT: [[RET:%.*]] = add i32 [[X1]], [[Z1]]
|
|
; CHECK-NEXT: ret i32 [[RET]]
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%x0 = call i32 @foo(i32 %x, i32 0) nounwind readnone
|
|
%y0 = call i32 @foo(i32 %x, i32 1) nounwind readnone
|
|
%z0 = lshr exact i32 %y0, 8
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%x1 = call i32 @foo(i32 %y, i32 0) nounwind readnone
|
|
%y1 = call i32 @foo(i32 %y, i32 1) nounwind readnone
|
|
%z1 = lshr i32 %y1, 8
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%xx = phi i32 [ %x0, %if.then ], [ %x1, %if.else ]
|
|
%yy = phi i32 [ %z0, %if.then ], [ %z1, %if.else ]
|
|
%ret = add i32 %xx, %yy
|
|
ret i32 %ret
|
|
}
|
|
|
|
|
|
|
|
; FIXME: Should turn into select
|
|
define float @allow_intrinsic_remove_constant(i1 zeroext %flag, float %w, float %x, float %y) {
|
|
; CHECK-LABEL: @allow_intrinsic_remove_constant(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[DUMMY:%.*]] = fadd float [[W:%.*]], 4.000000e+00
|
|
; CHECK-NEXT: [[SV1:%.*]] = call float @llvm.fma.f32(float [[DUMMY]], float 2.000000e+00, float 1.000000e+00)
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[DUMMY1:%.*]] = fadd float [[W]], 8.000000e+00
|
|
; CHECK-NEXT: [[SV2:%.*]] = call float @llvm.fma.f32(float 2.000000e+00, float [[DUMMY1]], float 1.000000e+00)
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[P:%.*]] = phi float [ [[SV1]], [[IF_THEN]] ], [ [[SV2]], [[IF_ELSE]] ]
|
|
; CHECK-NEXT: ret float [[P]]
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%dummy = fadd float %w, 4.0
|
|
%sv1 = call float @llvm.fma.f32(float %dummy, float 2.0, float 1.0)
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%dummy1 = fadd float %w, 8.0
|
|
%sv2 = call float @llvm.fma.f32(float 2.0, float %dummy1, float 1.0)
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%p = phi float [ %sv1, %if.then ], [ %sv2, %if.else ]
|
|
ret float %p
|
|
}
|
|
|
|
declare float @llvm.fma.f32(float, float, float)
|
|
|
|
define i32 @no_remove_constant_immarg(i1 zeroext %flag, i32 %w, i32 %x, i32 %y) {
|
|
; CHECK-LABEL: @no_remove_constant_immarg(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[DUMMY:%.*]] = add i32 [[W:%.*]], 5
|
|
; CHECK-NEXT: [[SV1:%.*]] = call i32 @llvm.ctlz.i32(i32 [[X:%.*]], i1 true)
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[DUMMY1:%.*]] = add i32 [[W]], 6
|
|
; CHECK-NEXT: [[SV2:%.*]] = call i32 @llvm.ctlz.i32(i32 [[X]], i1 false)
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[P:%.*]] = phi i32 [ [[SV1]], [[IF_THEN]] ], [ [[SV2]], [[IF_ELSE]] ]
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%dummy = add i32 %w, 5
|
|
%sv1 = call i32 @llvm.ctlz.i32(i32 %x, i1 true)
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%dummy1 = add i32 %w, 6
|
|
%sv2 = call i32 @llvm.ctlz.i32(i32 %x, i1 false)
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%p = phi i32 [ %sv1, %if.then ], [ %sv2, %if.else ]
|
|
ret i32 1
|
|
}
|
|
|
|
declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* nocapture, i8 addrspace(1)* nocapture readonly, i64, i1)
|
|
|
|
; Make sure a memcpy size isn't replaced with a variable
|
|
define void @no_replace_memcpy_size(i1 zeroext %flag, i8 addrspace(1)* %dst, i8 addrspace(1)* %src) {
|
|
; CHECK-LABEL: @no_replace_memcpy_size(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* [[DST:%.*]], i8 addrspace(1)* [[SRC:%.*]], i64 1024, i1 false)
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* [[DST]], i8 addrspace(1)* [[SRC]], i64 4096, i1 false)
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* %dst, i8 addrspace(1)* %src, i64 1024, i1 false)
|
|
br label %if.end
|
|
|
|
if.else:
|
|
call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* %dst, i8 addrspace(1)* %src, i64 4096, i1 false)
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret void
|
|
}
|
|
|
|
declare void @llvm.memmove.p1i8.p1i8.i64(i8 addrspace(1)* nocapture, i8 addrspace(1)* nocapture readonly, i64, i1)
|
|
|
|
; Make sure a memmove size isn't replaced with a variable
|
|
define void @no_replace_memmove_size(i1 zeroext %flag, i8 addrspace(1)* %dst, i8 addrspace(1)* %src) {
|
|
; CHECK-LABEL: @no_replace_memmove_size(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: call void @llvm.memmove.p1i8.p1i8.i64(i8 addrspace(1)* [[DST:%.*]], i8 addrspace(1)* [[SRC:%.*]], i64 1024, i1 false)
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: call void @llvm.memmove.p1i8.p1i8.i64(i8 addrspace(1)* [[DST]], i8 addrspace(1)* [[SRC]], i64 4096, i1 false)
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
call void @llvm.memmove.p1i8.p1i8.i64(i8 addrspace(1)* %dst, i8 addrspace(1)* %src, i64 1024, i1 false)
|
|
br label %if.end
|
|
|
|
if.else:
|
|
call void @llvm.memmove.p1i8.p1i8.i64(i8 addrspace(1)* %dst, i8 addrspace(1)* %src, i64 4096, i1 false)
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret void
|
|
}
|
|
|
|
declare void @llvm.memset.p1i8.i64(i8 addrspace(1)* nocapture, i8, i64, i1)
|
|
|
|
; Make sure a memset size isn't replaced with a variable
|
|
define void @no_replace_memset_size(i1 zeroext %flag, i8 addrspace(1)* %dst) {
|
|
; CHECK-LABEL: @no_replace_memset_size(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: call void @llvm.memset.p1i8.i64(i8 addrspace(1)* [[DST:%.*]], i8 0, i64 1024, i1 false)
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: call void @llvm.memset.p1i8.i64(i8 addrspace(1)* [[DST]], i8 0, i64 4096, i1 false)
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
call void @llvm.memset.p1i8.i64(i8 addrspace(1)* %dst, i8 0, i64 1024, i1 false)
|
|
br label %if.end
|
|
|
|
if.else:
|
|
call void @llvm.memset.p1i8.i64(i8 addrspace(1)* %dst, i8 0, i64 4096, i1 false)
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret void
|
|
}
|
|
|
|
; Check that simplifycfg doesn't sink and merge inline-asm instructions.
|
|
|
|
define i32 @test_inline_asm1(i32 %c, i32 %r6) {
|
|
; CHECK-LABEL: @test_inline_asm1(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[C:%.*]], 0
|
|
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[TMP0:%.*]] = call i32 asm "rorl $2, $0", "=&r,0,n,~{dirflag},~{fpsr},~{flags}"(i32 [[R6:%.*]], i32 8)
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[TMP1:%.*]] = call i32 asm "rorl $2, $0", "=&r,0,n,~{dirflag},~{fpsr},~{flags}"(i32 [[R6]], i32 6)
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: [[R6_ADDR_0:%.*]] = phi i32 [ [[TMP0]], [[IF_THEN]] ], [ [[TMP1]], [[IF_ELSE]] ]
|
|
; CHECK-NEXT: ret i32 [[R6_ADDR_0]]
|
|
;
|
|
entry:
|
|
%tobool = icmp eq i32 %c, 0
|
|
br i1 %tobool, label %if.else, label %if.then
|
|
|
|
if.then:
|
|
%0 = call i32 asm "rorl $2, $0", "=&r,0,n,~{dirflag},~{fpsr},~{flags}"(i32 %r6, i32 8)
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%1 = call i32 asm "rorl $2, $0", "=&r,0,n,~{dirflag},~{fpsr},~{flags}"(i32 %r6, i32 6)
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%r6.addr.0 = phi i32 [ %0, %if.then ], [ %1, %if.else ]
|
|
ret i32 %r6.addr.0
|
|
}
|
|
|
|
|
|
declare i32 @call_target()
|
|
|
|
define void @test_operand_bundles(i1 %cond, i32* %ptr) {
|
|
; CHECK-LABEL: @test_operand_bundles(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[COND:%.*]], label [[LEFT:%.*]], label [[RIGHT:%.*]]
|
|
; CHECK: left:
|
|
; CHECK-NEXT: [[VAL0:%.*]] = call i32 @call_target() [ "deopt"(i32 10) ]
|
|
; CHECK-NEXT: br label [[MERGE:%.*]]
|
|
; CHECK: right:
|
|
; CHECK-NEXT: [[VAL1:%.*]] = call i32 @call_target() [ "deopt"(i32 20) ]
|
|
; CHECK-NEXT: br label [[MERGE]]
|
|
; CHECK: merge:
|
|
; CHECK-NEXT: [[VAL1_SINK:%.*]] = phi i32 [ [[VAL1]], [[RIGHT]] ], [ [[VAL0]], [[LEFT]] ]
|
|
; CHECK-NEXT: store i32 [[VAL1_SINK]], i32* [[PTR:%.*]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br i1 %cond, label %left, label %right
|
|
|
|
left:
|
|
%val0 = call i32 @call_target() [ "deopt"(i32 10) ]
|
|
store i32 %val0, i32* %ptr
|
|
br label %merge
|
|
|
|
right:
|
|
%val1 = call i32 @call_target() [ "deopt"(i32 20) ]
|
|
store i32 %val1, i32* %ptr
|
|
br label %merge
|
|
|
|
merge:
|
|
ret void
|
|
}
|
|
|
|
|
|
%TP = type {i32, i32}
|
|
|
|
define i32 @test_insertvalue(i1 zeroext %flag, %TP %P) {
|
|
; CHECK-LABEL: @test_insertvalue(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[DOT:%.*]] = select i1 [[FLAG:%.*]], i32 0, i32 1
|
|
; CHECK-NEXT: [[I2:%.*]] = insertvalue [[TP:%.*]] [[P:%.*]], i32 [[DOT]], 0
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%i1 = insertvalue %TP %P, i32 0, 0
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%i2 = insertvalue %TP %P, i32 1, 0
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%i = phi %TP [%i1, %if.then], [%i2, %if.else]
|
|
ret i32 1
|
|
}
|
|
|
|
|
|
|
|
declare void @baz(i32)
|
|
|
|
define void @test_sink_void_calls(i32 %x) {
|
|
; CHECK-LABEL: @test_sink_void_calls(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: switch i32 [[X:%.*]], label [[DEFAULT:%.*]] [
|
|
; CHECK-NEXT: i32 0, label [[RETURN:%.*]]
|
|
; CHECK-NEXT: i32 1, label [[BB1:%.*]]
|
|
; CHECK-NEXT: i32 2, label [[BB2:%.*]]
|
|
; CHECK-NEXT: i32 3, label [[BB3:%.*]]
|
|
; CHECK-NEXT: i32 4, label [[BB4:%.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: br label [[RETURN]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: br label [[RETURN]]
|
|
; CHECK: bb3:
|
|
; CHECK-NEXT: br label [[RETURN]]
|
|
; CHECK: bb4:
|
|
; CHECK-NEXT: br label [[RETURN]]
|
|
; CHECK: default:
|
|
; CHECK-NEXT: unreachable
|
|
; CHECK: return:
|
|
; CHECK-NEXT: [[DOTSINK:%.*]] = phi i32 [ 90, [[BB4]] ], [ 78, [[BB3]] ], [ 56, [[BB2]] ], [ 34, [[BB1]] ], [ 12, [[ENTRY:%.*]] ]
|
|
; CHECK-NEXT: call void @baz(i32 [[DOTSINK]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
switch i32 %x, label %default [
|
|
i32 0, label %bb0
|
|
i32 1, label %bb1
|
|
i32 2, label %bb2
|
|
i32 3, label %bb3
|
|
i32 4, label %bb4
|
|
]
|
|
bb0:
|
|
call void @baz(i32 12)
|
|
br label %return
|
|
bb1:
|
|
call void @baz(i32 34)
|
|
br label %return
|
|
bb2:
|
|
call void @baz(i32 56)
|
|
br label %return
|
|
bb3:
|
|
call void @baz(i32 78)
|
|
br label %return
|
|
bb4:
|
|
call void @baz(i32 90)
|
|
br label %return
|
|
default:
|
|
unreachable
|
|
return:
|
|
ret void
|
|
|
|
; Check that the calls get sunk to the return block.
|
|
; We would previously not sink calls without uses, see PR41259.
|
|
}
|
|
|
|
define i32 @test_not_sink_lifetime_marker(i1 zeroext %flag, i32 %x) {
|
|
; CHECK-LABEL: @test_not_sink_lifetime_marker(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[Y:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: [[Z:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: br i1 [[FLAG:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: [[Y_CAST:%.*]] = bitcast i32* [[Y]] to i8*
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* [[Y_CAST]])
|
|
; CHECK-NEXT: br label [[IF_END:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[Z_CAST:%.*]] = bitcast i32* [[Z]] to i8*
|
|
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* [[Z_CAST]])
|
|
; CHECK-NEXT: br label [[IF_END]]
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: ret i32 1
|
|
;
|
|
entry:
|
|
%y = alloca i32
|
|
%z = alloca i32
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%y.cast = bitcast i32* %y to i8*
|
|
call void @llvm.lifetime.end.p0i8(i64 4, i8* %y.cast)
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%z.cast = bitcast i32* %z to i8*
|
|
call void @llvm.lifetime.end.p0i8(i64 4, i8* %z.cast)
|
|
br label %if.end
|
|
|
|
if.end:
|
|
ret i32 1
|
|
}
|
|
|
|
define void @direct_caller(i1 %c) {
|
|
; CHECK-LABEL: @direct_caller(
|
|
; CHECK-NEXT: br i1 [[C:%.*]], label [[CALL_FOO:%.*]], label [[CALL_BAR:%.*]]
|
|
; CHECK: call_foo:
|
|
; CHECK-NEXT: call void @direct_callee()
|
|
; CHECK-NEXT: br label [[END:%.*]]
|
|
; CHECK: call_bar:
|
|
; CHECK-NEXT: call void @direct_callee2()
|
|
; CHECK-NEXT: br label [[END]]
|
|
; CHECK: end:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
br i1 %c, label %call_foo, label %call_bar
|
|
|
|
call_foo:
|
|
call void @direct_callee()
|
|
br label %end
|
|
|
|
call_bar:
|
|
call void @direct_callee2()
|
|
br label %end
|
|
|
|
end:
|
|
ret void
|
|
}
|
|
|
|
define void @indirect_caller(i1 %c, i32 %v, void (i32)* %foo, void (i32)* %bar) {
|
|
; CHECK-LABEL: @indirect_caller(
|
|
; CHECK-NEXT: end:
|
|
; CHECK-NEXT: [[FOO_BAR:%.*]] = select i1 [[C:%.*]], void (i32)* [[FOO:%.*]], void (i32)* [[BAR:%.*]]
|
|
; CHECK-NEXT: tail call void [[FOO_BAR]](i32 [[V:%.*]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
br i1 %c, label %call_foo, label %call_bar
|
|
|
|
call_foo:
|
|
tail call void %foo(i32 %v)
|
|
br label %end
|
|
|
|
call_bar:
|
|
tail call void %bar(i32 %v)
|
|
br label %end
|
|
|
|
end:
|
|
ret void
|
|
}
|
|
|
|
define void @maybe_indirect_caller(void ()* %fun) {
|
|
; CHECK-LABEL: @maybe_indirect_caller(
|
|
; CHECK-NEXT: [[C:%.*]] = icmp eq void ()* [[FUN:%.*]], @direct_callee
|
|
; CHECK-NEXT: br i1 [[C]], label [[IF_TRUE_DIRECT_TARG:%.*]], label [[IF_FALSE_ORIG_INDIRECT:%.*]]
|
|
; CHECK: if.true.direct_targ:
|
|
; CHECK-NEXT: tail call void @direct_callee()
|
|
; CHECK-NEXT: br label [[IF_END_ICP:%.*]]
|
|
; CHECK: if.false.orig_indirect:
|
|
; CHECK-NEXT: tail call void [[FUN]]()
|
|
; CHECK-NEXT: br label [[IF_END_ICP]]
|
|
; CHECK: if.end.icp:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%c = icmp eq void ()* %fun, @direct_callee
|
|
br i1 %c, label %if.true.direct_targ, label %if.false.orig_indirect
|
|
|
|
if.true.direct_targ:
|
|
tail call void @direct_callee()
|
|
br label %if.end.icp
|
|
|
|
if.false.orig_indirect:
|
|
tail call void %fun()
|
|
br label %if.end.icp
|
|
|
|
if.end.icp:
|
|
ret void
|
|
}
|
|
define void @maybe_indirect_caller2(void ()* %fun) {
|
|
; CHECK-LABEL: @maybe_indirect_caller2(
|
|
; CHECK-NEXT: [[C:%.*]] = icmp eq void ()* [[FUN:%.*]], @direct_callee
|
|
; CHECK-NEXT: br i1 [[C]], label [[IF_TRUE_DIRECT_TARG:%.*]], label [[IF_FALSE_ORIG_INDIRECT:%.*]]
|
|
; CHECK: if.false.orig_indirect:
|
|
; CHECK-NEXT: tail call void [[FUN]]()
|
|
; CHECK-NEXT: br label [[IF_END_ICP:%.*]]
|
|
; CHECK: if.true.direct_targ:
|
|
; CHECK-NEXT: tail call void @direct_callee()
|
|
; CHECK-NEXT: br label [[IF_END_ICP]]
|
|
; CHECK: if.end.icp:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%c = icmp eq void ()* %fun, @direct_callee
|
|
br i1 %c, label %if.true.direct_targ, label %if.false.orig_indirect
|
|
|
|
if.false.orig_indirect:
|
|
tail call void %fun()
|
|
br label %if.end.icp
|
|
|
|
if.true.direct_targ:
|
|
tail call void @direct_callee()
|
|
br label %if.end.icp
|
|
|
|
if.end.icp:
|
|
ret void
|
|
}
|
|
declare void @direct_callee()
|
|
declare void @direct_callee2()
|
|
declare void @direct_callee3()
|
|
|
|
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
|
|
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)
|
|
|
|
define void @creating_too_many_phis(i1 %cond, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h) {
|
|
; CHECK-LABEL: @creating_too_many_phis(
|
|
; CHECK-NEXT: br i1 [[COND:%.*]], label [[BB0:%.*]], label [[BB1:%.*]]
|
|
; CHECK: bb0:
|
|
; CHECK-NEXT: [[V0:%.*]] = add i32 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[V1:%.*]] = add i32 [[V0]], [[C:%.*]]
|
|
; CHECK-NEXT: [[V2:%.*]] = add i32 [[D:%.*]], [[E:%.*]]
|
|
; CHECK-NEXT: [[R3:%.*]] = add i32 [[V1]], [[V2]]
|
|
; CHECK-NEXT: br label [[END:%.*]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: [[V4:%.*]] = add i32 [[A]], [[B]]
|
|
; CHECK-NEXT: [[V5:%.*]] = add i32 [[V4]], [[C]]
|
|
; CHECK-NEXT: [[V6:%.*]] = add i32 [[G:%.*]], [[H:%.*]]
|
|
; CHECK-NEXT: [[R7:%.*]] = add i32 [[V5]], [[V6]]
|
|
; CHECK-NEXT: br label [[END]]
|
|
; CHECK: end:
|
|
; CHECK-NEXT: [[R7_SINK:%.*]] = phi i32 [ [[R7]], [[BB1]] ], [ [[R3]], [[BB0]] ]
|
|
; CHECK-NEXT: call void @use32(i32 [[R7_SINK]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
br i1 %cond, label %bb0, label %bb1
|
|
|
|
bb0:
|
|
%v0 = add i32 %a, %b
|
|
%v1 = add i32 %v0, %c
|
|
%v2 = add i32 %d, %e
|
|
%r3 = add i32 %v1, %v2
|
|
call void @use32(i32 %r3)
|
|
br label %end
|
|
|
|
bb1:
|
|
%v4 = add i32 %a, %b
|
|
%v5 = add i32 %v4, %c
|
|
%v6 = add i32 %g, %h
|
|
%r7 = add i32 %v5, %v6
|
|
call void @use32(i32 %r7)
|
|
br label %end
|
|
|
|
end:
|
|
ret void
|
|
}
|
|
declare void @use32(i32)
|
|
|
|
define void @multiple_cond_preds(i1 %c0, i1 %c1, i1 %c2) {
|
|
; CHECK-LABEL: @multiple_cond_preds(
|
|
; CHECK-NEXT: dispatch0:
|
|
; CHECK-NEXT: br i1 [[C0:%.*]], label [[DISPATCH1:%.*]], label [[DISPATCH2:%.*]]
|
|
; CHECK: dispatch1:
|
|
; CHECK-NEXT: call void @direct_callee2()
|
|
; CHECK-NEXT: br i1 [[C1:%.*]], label [[END_SINK_SPLIT:%.*]], label [[END:%.*]]
|
|
; CHECK: dispatch2:
|
|
; CHECK-NEXT: call void @direct_callee3()
|
|
; CHECK-NEXT: br i1 [[C2:%.*]], label [[END_SINK_SPLIT]], label [[END]]
|
|
; CHECK: end.sink.split:
|
|
; CHECK-NEXT: call void @direct_callee()
|
|
; CHECK-NEXT: br label [[END]]
|
|
; CHECK: end:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
dispatch0:
|
|
br i1 %c0, label %dispatch1, label %dispatch2
|
|
|
|
dispatch1:
|
|
call void @direct_callee2()
|
|
br i1 %c1, label %uncond_pred0, label %end
|
|
|
|
dispatch2:
|
|
call void @direct_callee3()
|
|
br i1 %c2, label %uncond_pred1, label %end
|
|
|
|
uncond_pred0:
|
|
call void @direct_callee()
|
|
br label %end
|
|
|
|
uncond_pred1:
|
|
call void @direct_callee()
|
|
br label %end
|
|
|
|
end:
|
|
ret void
|
|
}
|