1033 lines
24 KiB
LLVM
1033 lines
24 KiB
LLVM
; RUN: opt < %s -simplifycfg -simplifycfg-require-and-preserve-domtree=1 -sink-common-insts -S | FileCheck -enable-var-scope %s
|
|
; RUN: opt < %s -passes='simplify-cfg<sink-common-insts>' -S | FileCheck -enable-var-scope %s
|
|
|
|
define zeroext i1 @test1(i1 zeroext %flag, i32 %blksA, i32 %blksB, i32 %nblks) {
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
; CHECK-LABEL: test1
|
|
; CHECK: add
|
|
; CHECK: select
|
|
; CHECK: icmp
|
|
; CHECK-NOT: br
|
|
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) {
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
; CHECK-LABEL: test2
|
|
; CHECK: add
|
|
; CHECK: select
|
|
; CHECK: icmp
|
|
; CHECK-NOT: br
|
|
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) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test3
|
|
; CHECK: select
|
|
; CHECK: call
|
|
; CHECK: call
|
|
; CHECK: add
|
|
; CHECK-NOT: br
|
|
|
|
define i32 @test4(i1 zeroext %flag, i32 %x, i32* %y) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test4
|
|
; CHECK: select
|
|
; CHECK: store
|
|
; CHECK-NOT: store
|
|
|
|
define i32 @test5(i1 zeroext %flag, i32 %x, i32* %y) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test5
|
|
; CHECK: store volatile
|
|
; CHECK: store
|
|
|
|
define i32 @test6(i1 zeroext %flag, i32 %x, i32* %y) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test6
|
|
; CHECK: select
|
|
; CHECK: store volatile
|
|
; CHECK-NOT: store
|
|
|
|
define i32 @test7(i1 zeroext %flag, i32 %x, i32* %y) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test7
|
|
; CHECK-DAG: select
|
|
; CHECK-DAG: load volatile
|
|
; CHECK: store volatile
|
|
; CHECK-NOT: load
|
|
; CHECK-NOT: store
|
|
|
|
; %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) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test8
|
|
; CHECK: add
|
|
; CHECK: add
|
|
|
|
; 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) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test9
|
|
; CHECK: add
|
|
; CHECK: add
|
|
|
|
%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) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test10
|
|
; CHECK: getelementptr
|
|
; CHECK: getelementptr
|
|
; CHECK: phi
|
|
; CHECK: store volatile
|
|
|
|
; 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) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test11
|
|
; CHECK: shufflevector
|
|
; CHECK: shufflevector
|
|
|
|
; We can't common an intrinsic!
|
|
define i32 @test12(i1 zeroext %flag, i32 %w, i32 %x, i32 %y) {
|
|
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
|
|
|
|
; CHECK-LABEL: test12
|
|
; CHECK: call i32 @llvm.ctlz
|
|
; CHECK: call i32 @llvm.cttz
|
|
|
|
; The TBAA metadata should be properly combined.
|
|
define i32 @test13(i1 zeroext %flag, i32 %x, i32* %y) {
|
|
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 }
|
|
|
|
; CHECK-LABEL: test13
|
|
; CHECK-DAG: select
|
|
; CHECK-DAG: load volatile
|
|
; CHECK: store volatile {{.*}}, !tbaa ![[$TBAA:[0-9]]]
|
|
; CHECK-NOT: load
|
|
; CHECK-NOT: store
|
|
|
|
; The call should be commoned.
|
|
define i32 @test13a(i1 zeroext %flag, i32 %w, i32 %x, i32 %y) {
|
|
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)
|
|
|
|
; CHECK-LABEL: test13a
|
|
; CHECK: %[[x:.*]] = select i1 %flag
|
|
; CHECK: call i32 @bar(i32 %[[x]])
|
|
|
|
; The load should be commoned.
|
|
define i32 @test14(i1 zeroext %flag, i32 %w, i32 %x, i32 %y, %struct.anon* %s) {
|
|
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)
|
|
|
|
; CHECK-LABEL: test14
|
|
; CHECK: getelementptr
|
|
; CHECK: load
|
|
; CHECK-NOT: load
|
|
|
|
; The load should be commoned.
|
|
define i32 @test15(i1 zeroext %flag, i32 %w, i32 %x, i32 %y, %struct.anon* %s) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test15
|
|
; CHECK: getelementptr
|
|
; CHECK: load
|
|
; CHECK-NOT: load
|
|
|
|
define zeroext i1 @test_crash(i1 zeroext %flag, i32* %i4, i32* %m, i32* %n) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test_crash
|
|
; 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) {
|
|
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test16
|
|
; CHECK: zext
|
|
; CHECK: zext
|
|
|
|
define zeroext i1 @test16a(i1 zeroext %flag, i1 zeroext %flag2, i32 %blksA, i32 %blksB, i32 %nblks, i8* %p) {
|
|
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test16a
|
|
; CHECK: zext
|
|
; CHECK-NOT: zext
|
|
|
|
define zeroext i1 @test17(i32 %flag, i32 %blksA, i32 %blksB, i32 %nblks) {
|
|
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)
|
|
|
|
; CHECK-LABEL: test17
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: icmp uge
|
|
; CHECK-NEXT: br label %[[x:.*]]
|
|
|
|
; CHECK: if.then2:
|
|
; CHECK-NEXT: add
|
|
; CHECK-NEXT: icmp ule
|
|
; CHECK-NEXT: br label %[[x]]
|
|
|
|
; CHECK: [[x]]:
|
|
; CHECK-NEXT: %[[y:.*]] = phi i1 [ %cmp
|
|
; CHECK-NEXT: %[[z:.*]] = call i8 @i1toi8(i1 %[[y]])
|
|
; CHECK-NEXT: br label %if.end
|
|
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: phi i8
|
|
; CHECK-DAG: [ %[[z]], %[[x]] ]
|
|
; CHECK-DAG: [ 0, %entry ]
|
|
|
|
define zeroext i1 @test18(i32 %flag, i32 %blksA, i32 %blksB, i32 %nblks) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test18
|
|
; CHECK: if.end:
|
|
; CHECK-NEXT: %[[x:.*]] = phi i1
|
|
; CHECK-DAG: [ %cmp, %if.then ]
|
|
; CHECK-DAG: [ %cmp2, %if.then2 ]
|
|
; CHECK-DAG: [ %cmp3, %if.then3 ]
|
|
; CHECK-NEXT: zext i1 %[[x]] to i8
|
|
|
|
define i32 @test_pr30188(i1 zeroext %flag, i32 %x) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test_pr30188
|
|
; CHECK-NOT: select
|
|
; CHECK: store
|
|
; CHECK: store
|
|
|
|
define i32 @test_pr30188a(i1 zeroext %flag, i32 %x) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test_pr30188a
|
|
; CHECK-NOT: select
|
|
; CHECK: load
|
|
; CHECK: load
|
|
; CHECK: store
|
|
|
|
; 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) {
|
|
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()
|
|
|
|
; CHECK-LABEL: test_pr30292
|
|
; CHECK: phi i32 [ 0, %entry ], [ %add1, %succ ], [ %add2, %two ]
|
|
|
|
define zeroext i1 @test_pr30244(i1 zeroext %flag, i1 zeroext %flag2, i32 %blksA, i32 %blksB, i32 %nblks) {
|
|
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: @test_pr30244
|
|
; CHECK: store
|
|
; CHECK: store
|
|
|
|
define i32 @test_pr30373a(i1 zeroext %flag, i32 %x, i32 %y) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test_pr30373a
|
|
; CHECK: lshr
|
|
; CHECK-NOT: exact
|
|
; CHECK: }
|
|
|
|
define i32 @test_pr30373b(i1 zeroext %flag, i32 %x, i32 %y) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: test_pr30373b
|
|
; CHECK: lshr
|
|
; CHECK-NOT: exact
|
|
; CHECK: }
|
|
|
|
|
|
; FIXME: Should turn into select
|
|
; CHECK-LABEL: @allow_intrinsic_remove_constant(
|
|
; CHECK: %sv1 = call float @llvm.fma.f32(float %dummy, float 2.000000e+00, float 1.000000e+00)
|
|
; CHECK: %sv2 = call float @llvm.fma.f32(float 2.000000e+00, float %dummy1, float 1.000000e+00)
|
|
define float @allow_intrinsic_remove_constant(i1 zeroext %flag, float %w, float %x, float %y) {
|
|
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)
|
|
|
|
; CHECK-LABEL: @no_remove_constant_immarg(
|
|
; CHECK: call i32 @llvm.ctlz.i32(i32 %x, i1 true)
|
|
; CHECK: call i32 @llvm.ctlz.i32(i32 %x, i1 false)
|
|
define i32 @no_remove_constant_immarg(i1 zeroext %flag, i32 %w, i32 %x, i32 %y) {
|
|
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
|
|
; CHECK-LABEL: @no_replace_memcpy_size(
|
|
; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* %dst, i8 addrspace(1)* %src, i64 1024, i1 false)
|
|
; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* %dst, i8 addrspace(1)* %src, i64 4096, i1 false)
|
|
define void @no_replace_memcpy_size(i1 zeroext %flag, i8 addrspace(1)* %dst, i8 addrspace(1)* %src) {
|
|
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
|
|
; CHECK-LABEL: @no_replace_memmove_size(
|
|
; CHECK: call void @llvm.memmove.p1i8.p1i8.i64(i8 addrspace(1)* %dst, i8 addrspace(1)* %src, i64 1024, i1 false)
|
|
; CHECK: call void @llvm.memmove.p1i8.p1i8.i64(i8 addrspace(1)* %dst, i8 addrspace(1)* %src, i64 4096, i1 false)
|
|
define void @no_replace_memmove_size(i1 zeroext %flag, i8 addrspace(1)* %dst, i8 addrspace(1)* %src) {
|
|
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
|
|
; CHECK-LABEL: @no_replace_memset_size(
|
|
; CHECK: call void @llvm.memset.p1i8.i64(i8 addrspace(1)* %dst, i8 0, i64 1024, i1 false)
|
|
; CHECK: call void @llvm.memset.p1i8.i64(i8 addrspace(1)* %dst, i8 0, i64 4096, i1 false)
|
|
define void @no_replace_memset_size(i1 zeroext %flag, i8 addrspace(1)* %dst) {
|
|
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) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: @test_inline_asm1(
|
|
; CHECK: call i32 asm "rorl $2, $0", "=&r,0,n,~{dirflag},~{fpsr},~{flags}"(i32 %r6, i32 8)
|
|
; CHECK: call i32 asm "rorl $2, $0", "=&r,0,n,~{dirflag},~{fpsr},~{flags}"(i32 %r6, i32 6)
|
|
|
|
declare i32 @call_target()
|
|
|
|
define void @test_operand_bundles(i1 %cond, i32* %ptr) {
|
|
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
|
|
}
|
|
|
|
; CHECK-LABEL: @test_operand_bundles(
|
|
; CHECK: left:
|
|
; CHECK-NEXT: %val0 = call i32 @call_target() [ "deopt"(i32 10) ]
|
|
; CHECK: right:
|
|
; CHECK-NEXT: %val1 = call i32 @call_target() [ "deopt"(i32 20) ]
|
|
|
|
%T = type {i32, i32}
|
|
|
|
define i32 @test_insertvalue(i1 zeroext %flag, %T %P) {
|
|
entry:
|
|
br i1 %flag, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
%t1 = insertvalue %T %P, i32 0, 0
|
|
br label %if.end
|
|
|
|
if.else:
|
|
%t2 = insertvalue %T %P, i32 1, 0
|
|
br label %if.end
|
|
|
|
if.end:
|
|
%t = phi %T [%t1, %if.then], [%t2, %if.else]
|
|
ret i32 1
|
|
}
|
|
|
|
; CHECK-LABEL: @test_insertvalue
|
|
; CHECK: select
|
|
; CHECK: insertvalue
|
|
; CHECK-NOT: insertvalue
|
|
|
|
|
|
declare void @baz(i32)
|
|
|
|
define void @test_sink_void_calls(i32 %x) {
|
|
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.
|
|
; CHECK-LABEL: @test_sink_void_calls
|
|
; CHECK-NOT: call
|
|
; CHECK-LABEL: return:
|
|
; CHECK: phi
|
|
; CHECK: call
|
|
; CHECK-NOT: call
|
|
; CHECK: ret
|
|
}
|
|
|
|
; CHECK-LABEL: @test_not_sink_lifetime_marker
|
|
; CHECK-NOT: select
|
|
; CHECK: call void @llvm.lifetime.end
|
|
; CHECK: call void @llvm.lifetime.end
|
|
define i32 @test_not_sink_lifetime_marker(i1 zeroext %flag, i32 %x) {
|
|
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
|
|
}
|
|
|
|
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
|
|
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)
|
|
|
|
; CHECK: ![[$TBAA]] = !{![[TYPE:[0-9]]], ![[TYPE]], i64 0}
|
|
; CHECK: ![[TYPE]] = !{!"float", ![[TEXT:[0-9]]]}
|
|
; CHECK: ![[TEXT]] = !{!"an example type tree"}
|