diff options
Diffstat (limited to 'test/Transforms/SimplifyCFG')
26 files changed, 1647 insertions, 48 deletions
diff --git a/test/Transforms/SimplifyCFG/AArch64/cttz-ctlz.ll b/test/Transforms/SimplifyCFG/AArch64/cttz-ctlz.ll new file mode 100644 index 000000000000..e32d711143dc --- /dev/null +++ b/test/Transforms/SimplifyCFG/AArch64/cttz-ctlz.ll @@ -0,0 +1,43 @@ +; RUN: opt -S -simplifycfg -mtriple=aarch64 < %s | FileCheck %s + +define i32 @ctlz(i32 %A) { +; CHECK-LABEL: @ctlz( +; CHECK: [[ICMP:%[A-Za-z0-9]+]] = icmp eq i32 %A, 0 +; CHECK-NEXT: [[CTZ:%[A-Za-z0-9]+]] = tail call i32 @llvm.ctlz.i32(i32 %A, i1 true) +; CHECK-NEXT: [[SEL:%[A-Za-z0-9.]+]] = select i1 [[ICMP]], i32 32, i32 [[CTZ]] +; CHECK-NEXT: ret i32 [[SEL]] +entry: + %tobool = icmp eq i32 %A, 0 + br i1 %tobool, label %cond.end, label %cond.true + +cond.true: + %0 = tail call i32 @llvm.ctlz.i32(i32 %A, i1 true) + br label %cond.end + +cond.end: + %cond = phi i32 [ %0, %cond.true ], [ 32, %entry ] + ret i32 %cond +} + +define i32 @cttz(i32 %A) { +; CHECK-LABEL: @cttz( +; CHECK: [[ICMP:%[A-Za-z0-9]+]] = icmp eq i32 %A, 0 +; CHECK-NEXT: [[CTZ:%[A-Za-z0-9]+]] = tail call i32 @llvm.cttz.i32(i32 %A, i1 true) +; CHECK-NEXT: [[SEL:%[A-Za-z0-9.]+]] = select i1 [[ICMP]], i32 32, i32 [[CTZ]] +; CHECK-NEXT: ret i32 [[SEL]] +entry: + %tobool = icmp eq i32 %A, 0 + br i1 %tobool, label %cond.end, label %cond.true + +cond.true: + %0 = tail call i32 @llvm.cttz.i32(i32 %A, i1 true) + br label %cond.end + +cond.end: + %cond = phi i32 [ %0, %cond.true ], [ 32, %entry ] + ret i32 %cond +} + +declare i32 @llvm.ctlz.i32(i32, i1) +declare i32 @llvm.cttz.i32(i32, i1) + diff --git a/test/Transforms/SimplifyCFG/ARM/cttz-ctlz.ll b/test/Transforms/SimplifyCFG/ARM/cttz-ctlz.ll new file mode 100644 index 000000000000..ffcf2175091f --- /dev/null +++ b/test/Transforms/SimplifyCFG/ARM/cttz-ctlz.ll @@ -0,0 +1,43 @@ +; RUN: opt -S -simplifycfg -mtriple=arm -mattr=+v6t2 < %s | FileCheck %s + +define i32 @ctlz(i32 %A) { +; CHECK-LABEL: @ctlz( +; CHECK: [[ICMP:%[A-Za-z0-9]+]] = icmp eq i32 %A, 0 +; CHECK-NEXT: [[CTZ:%[A-Za-z0-9]+]] = tail call i32 @llvm.ctlz.i32(i32 %A, i1 true) +; CHECK-NEXT: [[SEL:%[A-Za-z0-9.]+]] = select i1 [[ICMP]], i32 32, i32 [[CTZ]] +; CHECK-NEXT: ret i32 [[SEL]] +entry: + %tobool = icmp eq i32 %A, 0 + br i1 %tobool, label %cond.end, label %cond.true + +cond.true: + %0 = tail call i32 @llvm.ctlz.i32(i32 %A, i1 true) + br label %cond.end + +cond.end: + %cond = phi i32 [ %0, %cond.true ], [ 32, %entry ] + ret i32 %cond +} + +define i32 @cttz(i32 %A) { +; CHECK-LABEL: @cttz( +; CHECK: [[ICMP:%[A-Za-z0-9]+]] = icmp eq i32 %A, 0 +; CHECK-NEXT: [[CTZ:%[A-Za-z0-9]+]] = tail call i32 @llvm.cttz.i32(i32 %A, i1 true) +; CHECK-NEXT: [[SEL:%[A-Za-z0-9.]+]] = select i1 [[ICMP]], i32 32, i32 [[CTZ]] +; CHECK-NEXT: ret i32 [[SEL]] +entry: + %tobool = icmp eq i32 %A, 0 + br i1 %tobool, label %cond.end, label %cond.true + +cond.true: + %0 = tail call i32 @llvm.cttz.i32(i32 %A, i1 true) + br label %cond.end + +cond.end: + %cond = phi i32 [ %0, %cond.true ], [ 32, %entry ] + ret i32 %cond +} + +declare i32 @llvm.ctlz.i32(i32, i1) +declare i32 @llvm.cttz.i32(i32, i1) + diff --git a/test/Transforms/SimplifyCFG/ARM/lit.local.cfg b/test/Transforms/SimplifyCFG/ARM/lit.local.cfg new file mode 100644 index 000000000000..5a3b8565213d --- /dev/null +++ b/test/Transforms/SimplifyCFG/ARM/lit.local.cfg @@ -0,0 +1,5 @@ +config.suffixes = ['.ll'] + +targets = set(config.root.targets_to_build.split()) +if not 'ARM' in targets: + config.unsupported = True diff --git a/test/Transforms/SimplifyCFG/Mips/cttz-ctlz.ll b/test/Transforms/SimplifyCFG/Mips/cttz-ctlz.ll new file mode 100644 index 000000000000..b4bfb51dd142 --- /dev/null +++ b/test/Transforms/SimplifyCFG/Mips/cttz-ctlz.ll @@ -0,0 +1,43 @@ +; RUN: opt -S -simplifycfg -mtriple=mips-linux-gnu < %s | FileCheck %s + +define i32 @ctlz(i32 %A) { +; CHECK-LABEL: @ctlz( +; CHECK: [[ICMP:%[A-Za-z0-9]+]] = icmp eq i32 %A, 0 +; CHECK-NEXT: [[CTZ:%[A-Za-z0-9]+]] = tail call i32 @llvm.ctlz.i32(i32 %A, i1 true) +; CHECK-NEXT: [[SEL:%[A-Za-z0-9.]+]] = select i1 [[ICMP]], i32 32, i32 [[CTZ]] +; CHECK-NEXT: ret i32 [[SEL]] +entry: + %tobool = icmp eq i32 %A, 0 + br i1 %tobool, label %cond.end, label %cond.true + +cond.true: + %0 = tail call i32 @llvm.ctlz.i32(i32 %A, i1 true) + br label %cond.end + +cond.end: + %cond = phi i32 [ %0, %cond.true ], [ 32, %entry ] + ret i32 %cond +} + +define i32 @cttz(i32 %A) { +; CHECK-LABEL: @cttz( +; CHECK: [[ICMP:%[A-Za-z0-9]+]] = icmp eq i32 %A, 0 +; CHECK-NEXT: [[CTZ:%[A-Za-z0-9]+]] = tail call i32 @llvm.cttz.i32(i32 %A, i1 true) +; CHECK-NEXT: [[SEL:%[A-Za-z0-9.]+]] = select i1 [[ICMP]], i32 32, i32 [[CTZ]] +; CHECK-NEXT: ret i32 [[SEL]] +entry: + %tobool = icmp eq i32 %A, 0 + br i1 %tobool, label %cond.end, label %cond.true + +cond.true: + %0 = tail call i32 @llvm.cttz.i32(i32 %A, i1 true) + br label %cond.end + +cond.end: + %cond = phi i32 [ %0, %cond.true ], [ 32, %entry ] + ret i32 %cond +} + +declare i32 @llvm.ctlz.i32(i32, i1) +declare i32 @llvm.cttz.i32(i32, i1) + diff --git a/test/Transforms/SimplifyCFG/Mips/lit.local.cfg b/test/Transforms/SimplifyCFG/Mips/lit.local.cfg new file mode 100644 index 000000000000..683bfdccb742 --- /dev/null +++ b/test/Transforms/SimplifyCFG/Mips/lit.local.cfg @@ -0,0 +1,5 @@ +config.suffixes = ['.ll'] + +targets = set(config.root.targets_to_build.split()) +if not 'Mips' in targets: + config.unsupported = True diff --git a/test/Transforms/SimplifyCFG/PR25267.ll b/test/Transforms/SimplifyCFG/PR25267.ll new file mode 100644 index 000000000000..a13d45a0f271 --- /dev/null +++ b/test/Transforms/SimplifyCFG/PR25267.ll @@ -0,0 +1,24 @@ +; RUN: opt < %s -simplifycfg -S | FileCheck %s + +define void @f() { +entry: + br label %for.cond + +for.cond: + %phi = phi i1 [ false, %entry ], [ true, %for.body ] + %select = select i1 %phi, i32 1, i32 2 + br label %for.body + +for.body: + switch i32 %select, label %for.cond [ + i32 1, label %return + i32 2, label %for.body + ] + +return: + ret void +} + +; CHECK-LABEL: define void @f( +; CHECK: br label %[[LABEL:.*]] +; CHECK: br label %[[LABEL]] diff --git a/test/Transforms/SimplifyCFG/SpeculativeExec.ll b/test/Transforms/SimplifyCFG/SpeculativeExec.ll index c23a96df52ee..73f9a0f88aca 100644 --- a/test/Transforms/SimplifyCFG/SpeculativeExec.ll +++ b/test/Transforms/SimplifyCFG/SpeculativeExec.ll @@ -69,3 +69,29 @@ end: ret i8* %x10 } + +define i32* @test5(i32 %a, i32 %b, i32 %c, i32* dereferenceable(10) %ptr1, + i32* dereferenceable(10) %ptr2, i32** dereferenceable(10) %ptr3) nounwind { +; CHECK-LABEL: @test5( +entry: + %tmp1 = icmp eq i32 %b, 0 + br i1 %tmp1, label %bb1, label %bb3 + +bb1: ; preds = %entry + %tmp2 = icmp sgt i32 %c, 1 + br i1 %tmp2, label %bb2, label %bb3 +; CHECK: bb1: +; CHECK-NEXT: icmp sgt i32 %c, 1 +; CHECK-NEXT: load i32*, i32** %ptr3 +; CHECK-NOT: dereferenceable +; CHECK-NEXT: select i1 %tmp2, i32* %tmp3, i32* %ptr2 +; CHECK-NEXT: ret i32* %tmp3.ptr2 + +bb2: ; preds = bb1 + %tmp3 = load i32*, i32** %ptr3, !dereferenceable !{i64 10} + br label %bb3 + +bb3: ; preds = %bb2, %entry + %tmp4 = phi i32* [ %ptr1, %entry ], [ %ptr2, %bb1 ], [ %tmp3, %bb2 ] + ret i32* %tmp4 +} diff --git a/test/Transforms/SimplifyCFG/X86/speculate-cttz-ctlz.ll b/test/Transforms/SimplifyCFG/X86/speculate-cttz-ctlz.ll index 69f6c69059d4..bee80e6acce0 100644 --- a/test/Transforms/SimplifyCFG/X86/speculate-cttz-ctlz.ll +++ b/test/Transforms/SimplifyCFG/X86/speculate-cttz-ctlz.ll @@ -7,9 +7,7 @@ define i64 @test1(i64 %A) { ; ALL-LABEL: @test1( ; ALL: [[COND:%[A-Za-z0-9]+]] = icmp eq i64 %A, 0 ; ALL: [[CTLZ:%[A-Za-z0-9]+]] = tail call i64 @llvm.ctlz.i64(i64 %A, i1 true) -; LZCNT-NEXT: select i1 [[COND]], i64 64, i64 [[CTLZ]] -; BMI-NOT: select -; GENERIC-NOT: select +; ALL-NEXT: select i1 [[COND]], i64 64, i64 [[CTLZ]] ; ALL: ret entry: %tobool = icmp eq i64 %A, 0 @@ -28,9 +26,7 @@ define i32 @test2(i32 %A) { ; ALL-LABEL: @test2( ; ALL: [[COND:%[A-Za-z0-9]+]] = icmp eq i32 %A, 0 ; ALL: [[CTLZ:%[A-Za-z0-9]+]] = tail call i32 @llvm.ctlz.i32(i32 %A, i1 true) -; LZCNT-NEXT: select i1 [[COND]], i32 32, i32 [[CTLZ]] -; BMI-NOT: select -; GENERIC-NOT: select +; ALL-NEXT: select i1 [[COND]], i32 32, i32 [[CTLZ]] ; ALL: ret entry: %tobool = icmp eq i32 %A, 0 @@ -50,9 +46,7 @@ define signext i16 @test3(i16 signext %A) { ; ALL-LABEL: @test3( ; ALL: [[COND:%[A-Za-z0-9]+]] = icmp eq i16 %A, 0 ; ALL: [[CTLZ:%[A-Za-z0-9]+]] = tail call i16 @llvm.ctlz.i16(i16 %A, i1 true) -; LZCNT-NEXT: select i1 [[COND]], i16 16, i16 [[CTLZ]] -; BMI-NOT: select -; GENERIC-NOT: select +; ALL-NEXT: select i1 [[COND]], i16 16, i16 [[CTLZ]] ; ALL: ret entry: %tobool = icmp eq i16 %A, 0 @@ -72,9 +66,7 @@ define i64 @test1b(i64 %A) { ; ALL-LABEL: @test1b( ; ALL: [[COND:%[A-Za-z0-9]+]] = icmp eq i64 %A, 0 ; ALL: [[CTTZ:%[A-Za-z0-9]+]] = tail call i64 @llvm.cttz.i64(i64 %A, i1 true) -; BMI-NEXT: select i1 [[COND]], i64 64, i64 [[CTTZ]] -; LZCNT-NOT: select -; GENERIC-NOT: select +; ALL-NEXT: select i1 [[COND]], i64 64, i64 [[CTTZ]] ; ALL: ret entry: %tobool = icmp eq i64 %A, 0 @@ -94,9 +86,7 @@ define i32 @test2b(i32 %A) { ; ALL-LABEL: @test2b( ; ALL: [[COND:%[A-Za-z0-9]+]] = icmp eq i32 %A, 0 ; ALL: [[CTTZ:%[A-Za-z0-9]+]] = tail call i32 @llvm.cttz.i32(i32 %A, i1 true) -; BMI-NEXT: select i1 [[COND]], i32 32, i32 [[CTTZ]] -; LZCNT-NOT: select -; GENERIC-NOT: select +; ALL-NEXT: select i1 [[COND]], i32 32, i32 [[CTTZ]] ; ALL: ret entry: %tobool = icmp eq i32 %A, 0 @@ -116,9 +106,7 @@ define signext i16 @test3b(i16 signext %A) { ; ALL-LABEL: @test3b( ; ALL: [[COND:%[A-Za-z0-9]+]] = icmp eq i16 %A, 0 ; ALL: [[CTTZ:%[A-Za-z0-9]+]] = tail call i16 @llvm.cttz.i16(i16 %A, i1 true) -; BMI-NEXT: select i1 [[COND]], i16 16, i16 [[CTTZ]] -; LZCNT-NOT: select -; GENERIC-NOT: select +; ALL-NEXT: select i1 [[COND]], i16 16, i16 [[CTTZ]] ; ALL: ret entry: %tobool = icmp eq i16 %A, 0 diff --git a/test/Transforms/SimplifyCFG/basictest.ll b/test/Transforms/SimplifyCFG/basictest.ll index d228499b2ec5..d4a9c81e506d 100644 --- a/test/Transforms/SimplifyCFG/basictest.ll +++ b/test/Transforms/SimplifyCFG/basictest.ll @@ -50,7 +50,7 @@ define i8 @test6f() { ; CHECK: alloca i8, align 1 ; CHECK-NEXT: call i8 @test6g ; CHECK-NEXT: icmp eq i8 %tmp, 0 -; CHECK-NEXT: load i8, i8* %r, align 1{{$}} +; CHECK-NEXT: load i8, i8* %r, align 1, !dbg !{{[0-9]+$}} bb0: %r = alloca i8, align 1 @@ -58,7 +58,7 @@ bb0: %tmp1 = icmp eq i8 %tmp, 0 br i1 %tmp1, label %bb2, label %bb1 bb1: - %tmp3 = load i8, i8* %r, align 1, !range !2, !tbaa !1 + %tmp3 = load i8, i8* %r, align 1, !range !2, !tbaa !1, !dbg !5 %tmp4 = icmp eq i8 %tmp3, 1 br i1 %tmp4, label %bb2, label %bb3 bb2: @@ -69,6 +69,16 @@ bb3: } declare i8 @test6g(i8*) +!llvm.dbg.cu = !{!3} +!llvm.module.flags = !{!8, !9} + !0 = !{!1, !1, i64 0} !1 = !{!"foo"} !2 = !{i8 0, i8 2} +!3 = distinct !DICompileUnit(language: DW_LANG_C99, file: !7, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !4, subprograms: !4, globals: !4) +!4 = !{} +!5 = !DILocation(line: 23, scope: !6) +!6 = distinct !DISubprogram(name: "foo", scope: !3, file: !7, line: 1, type: !DISubroutineType(types: !4), isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: false, variables: !4) +!7 = !DIFile(filename: "foo.c", directory: "/") +!8 = !{i32 2, !"Dwarf Version", i32 2} +!9 = !{i32 2, !"Debug Info Version", i32 3} diff --git a/test/Transforms/SimplifyCFG/branch-fold-dbg.ll b/test/Transforms/SimplifyCFG/branch-fold-dbg.ll index fac5b186e89d..34871063bbcc 100644 --- a/test/Transforms/SimplifyCFG/branch-fold-dbg.ll +++ b/test/Transforms/SimplifyCFG/branch-fold-dbg.ll @@ -4,7 +4,7 @@ @0 = external hidden constant [5 x %0], align 4 -define void @foo(i32) nounwind ssp { +define void @foo(i32) nounwind ssp !dbg !0 { Entry: %1 = icmp slt i32 %0, 0, !dbg !5 br i1 %1, label %BB5, label %BB1, !dbg !5 @@ -41,14 +41,14 @@ declare void @llvm.dbg.value(metadata, i64, metadata, metadata) nounwind readnon !llvm.dbg.sp = !{!0} -!0 = !DISubprogram(name: "foo", line: 231, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, file: !15, scope: !1, type: !3, function: void (i32)* @foo) +!0 = distinct !DISubprogram(name: "foo", line: 231, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, file: !15, scope: !1, type: !3) !1 = !DIFile(filename: "a.c", directory: "/private/tmp") -!2 = !DICompileUnit(language: DW_LANG_C99, producer: "clang (trunk 129006)", isOptimized: true, emissionKind: 0, file: !15, enums: !4, retainedTypes: !4) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "clang (trunk 129006)", isOptimized: true, emissionKind: 0, file: !15, enums: !4, retainedTypes: !4) !3 = !DISubroutineType(types: !4) !4 = !{null} !5 = !DILocation(line: 131, column: 2, scope: !0) !6 = !DILocation(line: 134, column: 2, scope: !0) -!7 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "bar", line: 232, scope: !8, file: !1, type: !9) +!7 = !DILocalVariable(name: "bar", line: 232, scope: !8, file: !1, type: !9) !8 = distinct !DILexicalBlock(line: 231, column: 1, file: !15, scope: !0) !9 = !DIDerivedType(tag: DW_TAG_pointer_type, size: 32, align: 32, scope: !2, baseType: !10) !10 = !DIDerivedType(tag: DW_TAG_const_type, scope: !2, baseType: !11) diff --git a/test/Transforms/SimplifyCFG/empty-cleanuppad.ll b/test/Transforms/SimplifyCFG/empty-cleanuppad.ll new file mode 100644 index 000000000000..57b362889955 --- /dev/null +++ b/test/Transforms/SimplifyCFG/empty-cleanuppad.ll @@ -0,0 +1,415 @@ +; RUN: opt < %s -simplifycfg -S | FileCheck %s + +; ModuleID = 'cppeh-simplify.cpp' +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc18.0.0" + + +; This case arises when two objects with empty destructors are cleaned up. +; +; void f1() { +; S a; +; S b; +; g(); +; } +; +; In this case, both cleanup pads can be eliminated and the invoke can be +; converted to a call. +; +; CHECK: define void @f1() +; CHECK: entry: +; CHECK: call void @g() +; CHECK: ret void +; CHECK-NOT: cleanuppad +; CHECK: } +; +define void @f1() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + invoke void @g() to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + ret void + +ehcleanup: ; preds = %entry + %0 = cleanuppad within none [] + cleanupret from %0 unwind label %ehcleanup.1 + +ehcleanup.1: ; preds = %ehcleanup + %1 = cleanuppad within none [] + cleanupret from %1 unwind to caller +} + + +; This case arises when an object with an empty destructor must be cleaned up +; outside of a try-block and an object with a non-empty destructor must be +; cleaned up within the try-block. +; +; void f2() { +; S a; +; try { +; S2 b; +; g(); +; } catch (...) {} +; } +; +; In this case, the outermost cleanup pad can be eliminated and the catch block +; should unwind to the caller (that is, exception handling continues with the +; parent frame of the caller). +; +; CHECK: define void @f2() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: ehcleanup: +; CHECK: cleanuppad within none +; CHECK: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %b) +; CHECK: cleanupret from %0 unwind label %catch.dispatch +; CHECK: catch.dispatch: +; CHECK: catchswitch within none [label %catch] unwind to caller +; CHECK: catch: +; CHECK: catchpad +; CHECK: catchret +; CHECK-NOT: cleanuppad +; CHECK: } +; +define void @f2() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + %b = alloca %struct.S2, align 1 + invoke void @g() to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + br label %try.cont + +ehcleanup: ; preds = %entry + %0 = cleanuppad within none [] + call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %b) + cleanupret from %0 unwind label %catch.dispatch + +catch.dispatch: ; preds = %ehcleanup + %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup.1 + +catch: ; preds = %catch.dispatch + %1 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] + catchret from %1 to label %catchret.dest + +catchret.dest: ; preds = %catch + br label %try.cont + +try.cont: ; preds = %catchret.dest, %invoke.cont + ret void + +ehcleanup.1: + %2 = cleanuppad within none [] + cleanupret from %2 unwind to caller +} + + +; This case arises when an object with a non-empty destructor must be cleaned up +; outside of a try-block and an object with an empty destructor must be cleaned +; within the try-block. +; +; void f3() { +; S2 a; +; try { +; S b; +; g(); +; } catch (...) {} +; } +; +; In this case the inner cleanup pad should be eliminated and the invoke of g() +; should unwind directly to the catchpad. +; +; CHECK-LABEL: define void @f3() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: to label %try.cont unwind label %catch.dispatch +; CHECK: catch.dispatch: +; CHECK-NEXT: catchswitch within none [label %catch] unwind label %ehcleanup.1 +; CHECK: catch: +; CHECK: catchpad within %cs1 [i8* null, i32 64, i8* null] +; CHECK: catchret +; CHECK: ehcleanup.1: +; CHECK: cleanuppad +; CHECK: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) +; CHECK: cleanupret from %cp3 unwind to caller +; CHECK: } +; +define void @f3() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + %a = alloca %struct.S2, align 1 + invoke void @g() to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + br label %try.cont + +ehcleanup: ; preds = %entry + %0 = cleanuppad within none [] + cleanupret from %0 unwind label %catch.dispatch + +catch.dispatch: ; preds = %ehcleanup + %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup.1 + +catch: ; preds = %catch.dispatch + %cp2 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] + catchret from %cp2 to label %catchret.dest + +catchret.dest: ; preds = %catch + br label %try.cont + +try.cont: ; preds = %catchret.dest, %invoke.cont + ret void + +ehcleanup.1: + %cp3 = cleanuppad within none [] + call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) + cleanupret from %cp3 unwind to caller +} + + +; This case arises when an object with an empty destructor may require cleanup +; from either inside or outside of a try-block. +; +; void f4() { +; S a; +; g(); +; try { +; g(); +; } catch (...) {} +; } +; +; In this case, the cleanuppad should be eliminated, the invoke outside of the +; catch block should be converted to a call (that is, that is, exception +; handling continues with the parent frame of the caller).) +; +; CHECK-LABEL: define void @f4() +; CHECK: entry: +; CHECK: call void @g +; Note: The cleanuppad simplification will insert an unconditional branch here +; but it will be eliminated, placing the following invoke in the entry BB. +; CHECK: invoke void @g() +; CHECK: to label %try.cont unwind label %catch.dispatch +; CHECK: catch.dispatch: +; CHECK: catchswitch within none [label %catch] unwind to caller +; CHECK: catch: +; CHECK: catchpad +; CHECK: catchret +; CHECK-NOT: cleanuppad +; CHECK: } +; +define void @f4() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + invoke void @g() + to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + invoke void @g() + to label %try.cont unwind label %catch.dispatch + +catch.dispatch: ; preds = %invoke.cont + %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup + +catch: ; preds = %catch.dispatch + %0 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] + catchret from %0 to label %try.cont + +try.cont: ; preds = %catch, %invoke.cont + ret void + +ehcleanup: + %cp2 = cleanuppad within none [] + cleanupret from %cp2 unwind to caller +} + +; This case tests simplification of an otherwise empty cleanup pad that contains +; a PHI node. +; +; int f6() { +; int state = 1; +; try { +; S a; +; g(); +; state = 2; +; g(); +; } catch (...) { +; return state; +; } +; return 0; +; } +; +; In this case, the cleanup pad should be eliminated and the PHI node in the +; cleanup pad should be sunk into the catch dispatch block. +; +; CHECK-LABEL: define i32 @f6() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: invoke.cont: +; CHECK: invoke void @g() +; CHECK-NOT: ehcleanup: +; CHECK-NOT: cleanuppad +; CHECK: catch.dispatch: +; CHECK: %state.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] +; CHECK: } +define i32 @f6() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + invoke void @g() + to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + invoke void @g() + to label %return unwind label %ehcleanup + +ehcleanup: ; preds = %invoke.cont, %entry + %state.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] + %0 = cleanuppad within none [] + cleanupret from %0 unwind label %catch.dispatch + +catch.dispatch: ; preds = %ehcleanup + %cs1 = catchswitch within none [label %catch] unwind to caller + +catch: ; preds = %catch.dispatch + %1 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] + catchret from %1 to label %return + +return: ; preds = %invoke.cont, %catch + %retval.0 = phi i32 [ %state.0, %catch ], [ 0, %invoke.cont ] + ret i32 %retval.0 +} + +; This case tests another variation of simplification of an otherwise empty +; cleanup pad that contains a PHI node. +; +; int f7() { +; int state = 1; +; try { +; g(); +; state = 2; +; S a; +; g(); +; state = 3; +; g(); +; } catch (...) { +; return state; +; } +; return 0; +; } +; +; In this case, the cleanup pad should be eliminated and the PHI node in the +; cleanup pad should be merged with the PHI node in the catch dispatch block. +; +; CHECK-LABEL: define i32 @f7() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: invoke.cont: +; CHECK: invoke void @g() +; CHECK: invoke.cont.1: +; CHECK: invoke void @g() +; CHECK-NOT: ehcleanup: +; CHECK-NOT: cleanuppad +; CHECK: catch.dispatch: +; CHECK: %state.1 = phi i32 [ 1, %entry ], [ 3, %invoke.cont.1 ], [ 2, %invoke.cont ] +; CHECK: } +define i32 @f7() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + invoke void @g() + to label %invoke.cont unwind label %catch.dispatch + +invoke.cont: ; preds = %entry + invoke void @g() + to label %invoke.cont.1 unwind label %ehcleanup + +invoke.cont.1: ; preds = %invoke.cont + invoke void @g() + to label %return unwind label %ehcleanup + +ehcleanup: ; preds = %invoke.cont.1, %invoke.cont + %state.0 = phi i32 [ 3, %invoke.cont.1 ], [ 2, %invoke.cont ] + %0 = cleanuppad within none [] + cleanupret from %0 unwind label %catch.dispatch + +catch.dispatch: ; preds = %ehcleanup, %entry + %state.1 = phi i32 [ %state.0, %ehcleanup ], [ 1, %entry ] + %cs1 = catchswitch within none [label %catch] unwind to caller + +catch: ; preds = %catch.dispatch + %1 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] + catchret from %1 to label %return + +return: ; preds = %invoke.cont.1, %catch + %retval.0 = phi i32 [ %state.1, %catch ], [ 0, %invoke.cont.1 ] + ret i32 %retval.0 +} + +; This case tests a scenario where an empty cleanup pad is not dominated by all +; of the predecessors of its successor, but the successor references a PHI node +; in the empty cleanup pad. +; +; Conceptually, the case being modeled is something like this: +; +; int f8() { +; int x = 1; +; try { +; S a; +; g(); +; x = 2; +; retry: +; g(); +; return +; } catch (...) { +; use_x(x); +; } +; goto retry; +; } +; +; While that C++ syntax isn't legal, the IR below is. +; +; In this case, the PHI node that is sunk from ehcleanup to catch.dispatch +; should have an incoming value entry for path from 'foo' that references the +; PHI node itself. +; +; CHECK-LABEL: define void @f8() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: invoke.cont: +; CHECK: invoke void @g() +; CHECK-NOT: ehcleanup: +; CHECK-NOT: cleanuppad +; CHECK: catch.dispatch: +; CHECK: %x = phi i32 [ 2, %invoke.cont ], [ 1, %entry ], [ %x, %catch.cont ] +; CHECK: } +define void @f8() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + invoke void @g() + to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + invoke void @g() + to label %return unwind label %ehcleanup + +ehcleanup: ; preds = %invoke.cont, %entry + %x = phi i32 [ 2, %invoke.cont ], [ 1, %entry ] + %0 = cleanuppad within none [] + cleanupret from %0 unwind label %catch.dispatch + +catch.dispatch: ; preds = %ehcleanup, %catch.cont + %cs1 = catchswitch within none [label %catch] unwind to caller + +catch: ; preds = %catch.dispatch + %1 = catchpad within %cs1 [i8* null, i32 u0x40, i8* null] + call void @use_x(i32 %x) + catchret from %1 to label %catch.cont + +catch.cont: ; preds = %catch + invoke void @g() + to label %return unwind label %catch.dispatch + +return: ; preds = %invoke.cont, %catch.cont + ret void +} + +%struct.S = type { i8 } +%struct.S2 = type { i8 } +declare void @"\01??1S2@@QEAA@XZ"(%struct.S2*) +declare void @g() +declare void @use_x(i32 %x) + +declare i32 @__CxxFrameHandler3(...) + diff --git a/test/Transforms/SimplifyCFG/hoist-dbgvalue.ll b/test/Transforms/SimplifyCFG/hoist-dbgvalue.ll index 345cf6282e3c..887373a2d3db 100644 --- a/test/Transforms/SimplifyCFG/hoist-dbgvalue.ll +++ b/test/Transforms/SimplifyCFG/hoist-dbgvalue.ll @@ -1,6 +1,6 @@ ; RUN: opt -simplifycfg -S < %s | FileCheck %s -define i32 @foo(i32 %i) nounwind ssp { +define i32 @foo(i32 %i) nounwind ssp !dbg !0 { call void @llvm.dbg.value(metadata i32 %i, i64 0, metadata !6, metadata !DIExpression()), !dbg !7 call void @llvm.dbg.value(metadata i32 0, i64 0, metadata !9, metadata !DIExpression()), !dbg !11 %1 = icmp ne i32 %i, 0, !dbg !12 @@ -32,16 +32,16 @@ declare void @llvm.dbg.value(metadata, i64, metadata, metadata) nounwind readnon !llvm.module.flags = !{!21} !llvm.dbg.sp = !{!0} -!0 = !DISubprogram(name: "foo", line: 2, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, file: !20, scope: !1, type: !3, function: i32 (i32)* @foo) +!0 = distinct !DISubprogram(name: "foo", line: 2, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, file: !20, scope: !1, type: !3) !1 = !DIFile(filename: "b.c", directory: "/private/tmp") -!2 = !DICompileUnit(language: DW_LANG_C99, producer: "clang", isOptimized: true, emissionKind: 0, file: !20, enums: !8, retainedTypes: !8) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "clang", isOptimized: true, emissionKind: 0, file: !20, enums: !8, retainedTypes: !8) !3 = !DISubroutineType(types: !4) !4 = !{!5} !5 = !DIBasicType(tag: DW_TAG_base_type, name: "int", size: 32, align: 32, encoding: DW_ATE_signed) -!6 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "i", line: 2, arg: 1, scope: !0, file: !1, type: !5) +!6 = !DILocalVariable(name: "i", line: 2, arg: 1, scope: !0, file: !1, type: !5) !7 = !DILocation(line: 2, column: 13, scope: !0) !8 = !{i32 0} -!9 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "k", line: 3, scope: !10, file: !1, type: !5) +!9 = !DILocalVariable(name: "k", line: 3, scope: !10, file: !1, type: !5) !10 = distinct !DILexicalBlock(line: 2, column: 16, file: !20, scope: !0) !11 = !DILocation(line: 3, column: 12, scope: !10) !12 = !DILocation(line: 4, column: 3, scope: !10) diff --git a/test/Transforms/SimplifyCFG/implied-cond.ll b/test/Transforms/SimplifyCFG/implied-cond.ll new file mode 100644 index 000000000000..317adc4c3472 --- /dev/null +++ b/test/Transforms/SimplifyCFG/implied-cond.ll @@ -0,0 +1,81 @@ +; RUN: opt %s -S -simplifycfg | FileCheck %s +; Check for when one branch implies the value of a successors conditional and +; it's not simply the same conditional repeated. + +define void @test(i32 %length.i, i32 %i) { +; CHECK-LABEL: @test + %iplus1 = add nsw i32 %i, 1 + %var29 = icmp slt i32 %iplus1, %length.i +; CHECK: br i1 %var29, label %in_bounds, label %out_of_bounds + br i1 %var29, label %next, label %out_of_bounds + +next: +; CHECK-LABEL: in_bounds: +; CHECK-NEXT: ret void + %var30 = icmp slt i32 %i, %length.i + br i1 %var30, label %in_bounds, label %out_of_bounds2 + +in_bounds: + ret void + +out_of_bounds: + call void @foo(i64 0) + unreachable + +out_of_bounds2: + call void @foo(i64 1) + unreachable +} + +; If the add is not nsw, it's not safe to use the fact about i+1 to imply the +; i condition since it could have overflowed. +define void @test_neg(i32 %length.i, i32 %i) { +; CHECK-LABEL: @test_neg + %iplus1 = add i32 %i, 1 + %var29 = icmp slt i32 %iplus1, %length.i +; CHECK: br i1 %var29, label %next, label %out_of_bounds + br i1 %var29, label %next, label %out_of_bounds + +next: + %var30 = icmp slt i32 %i, %length.i +; CHECK: br i1 %var30, label %in_bounds, label %out_of_bounds2 + br i1 %var30, label %in_bounds, label %out_of_bounds2 + +in_bounds: + ret void + +out_of_bounds: + call void @foo(i64 0) + unreachable + +out_of_bounds2: + call void @foo(i64 1) + unreachable +} + + +define void @test2(i32 %length.i, i32 %i) { +; CHECK-LABEL: @test2 + %iplus100 = add nsw i32 %i, 100 + %var29 = icmp slt i32 %iplus100, %length.i +; CHECK: br i1 %var29, label %in_bounds, label %out_of_bounds + br i1 %var29, label %next, label %out_of_bounds + +next: + %var30 = icmp slt i32 %i, %length.i + br i1 %var30, label %in_bounds, label %out_of_bounds2 + +in_bounds: + ret void + +out_of_bounds: + call void @foo(i64 0) + unreachable + +out_of_bounds2: + call void @foo(i64 1) + unreachable +} + +declare void @foo(i64) + diff --git a/test/Transforms/SimplifyCFG/invoke_unwind.ll b/test/Transforms/SimplifyCFG/invoke_unwind.ll index 3b4c09d96f77..100bfd4e9e3e 100644 --- a/test/Transforms/SimplifyCFG/invoke_unwind.ll +++ b/test/Transforms/SimplifyCFG/invoke_unwind.ll @@ -17,4 +17,17 @@ Rethrow: resume { i8*, i32 } %exn } +define i32 @test2() personality i32 (...)* @__gxx_personality_v0 { +; CHECK-LABEL: @test2( +; CHECK-NEXT: call void @bar() [ "foo"(i32 100) ] +; CHECK-NEXT: ret i32 0 + invoke void @bar( ) [ "foo"(i32 100) ] + to label %1 unwind label %Rethrow + ret i32 0 +Rethrow: + %exn = landingpad {i8*, i32} + catch i8* null + resume { i8*, i32 } %exn +} + declare i32 @__gxx_personality_v0(...) diff --git a/test/Transforms/SimplifyCFG/merge-cond-stores-2.ll b/test/Transforms/SimplifyCFG/merge-cond-stores-2.ll new file mode 100644 index 000000000000..fe498b5334e8 --- /dev/null +++ b/test/Transforms/SimplifyCFG/merge-cond-stores-2.ll @@ -0,0 +1,215 @@ +; RUN: opt -S < %s -simplifycfg -simplifycfg-merge-cond-stores=true -simplifycfg-merge-cond-stores-aggressively=false -phi-node-folding-threshold=2 | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "armv7--linux-gnueabihf" + +; This is a bit reversal that has been run through the early optimizer (-mem2reg -gvn -instcombine). +; There should be no additional PHIs created at all. The store should be on its own in a predicated +; block and there should be no PHIs. + +; CHECK-LABEL: @f +; Exactly 15 phis, as there are 15 in the original test case. +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK: select +; CHECK-NOT: select +; CHECK: br i1 {{.*}}, label %[[L:.*]], label %[[R:.*]] +; CHECK: [[L]] ; preds = +; CHECK-NEXT: store +; CHECK-NEXT: br label %[[R]] +; CHECK: [[R]] ; preds = +; CHECK-NEXT: ret i32 0 + +define i32 @f(i32* %b) { +entry: + %0 = load i32, i32* %b, align 4 + %and = and i32 %0, 1 + %tobool = icmp eq i32 %and, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %entry + %or = or i32 %0, -2147483648 + store i32 %or, i32* %b, align 4 + br label %if.end + +if.end: ; preds = %entry, %if.then + %1 = phi i32 [ %0, %entry ], [ %or, %if.then ] + %and1 = and i32 %1, 2 + %tobool2 = icmp eq i32 %and1, 0 + br i1 %tobool2, label %if.end5, label %if.then3 + +if.then3: ; preds = %if.end + %or4 = or i32 %1, 1073741824 + store i32 %or4, i32* %b, align 4 + br label %if.end5 + +if.end5: ; preds = %if.end, %if.then3 + %2 = phi i32 [ %1, %if.end ], [ %or4, %if.then3 ] + %and6 = and i32 %2, 4 + %tobool7 = icmp eq i32 %and6, 0 + br i1 %tobool7, label %if.end10, label %if.then8 + +if.then8: ; preds = %if.end5 + %or9 = or i32 %2, 536870912 + store i32 %or9, i32* %b, align 4 + br label %if.end10 + +if.end10: ; preds = %if.end5, %if.then8 + %3 = phi i32 [ %2, %if.end5 ], [ %or9, %if.then8 ] + %and11 = and i32 %3, 8 + %tobool12 = icmp eq i32 %and11, 0 + br i1 %tobool12, label %if.end15, label %if.then13 + +if.then13: ; preds = %if.end10 + %or14 = or i32 %3, 268435456 + store i32 %or14, i32* %b, align 4 + br label %if.end15 + +if.end15: ; preds = %if.end10, %if.then13 + %4 = phi i32 [ %3, %if.end10 ], [ %or14, %if.then13 ] + %and16 = and i32 %4, 16 + %tobool17 = icmp eq i32 %and16, 0 + br i1 %tobool17, label %if.end20, label %if.then18 + +if.then18: ; preds = %if.end15 + %or19 = or i32 %4, 134217728 + store i32 %or19, i32* %b, align 4 + br label %if.end20 + +if.end20: ; preds = %if.end15, %if.then18 + %5 = phi i32 [ %4, %if.end15 ], [ %or19, %if.then18 ] + %and21 = and i32 %5, 32 + %tobool22 = icmp eq i32 %and21, 0 + br i1 %tobool22, label %if.end25, label %if.then23 + +if.then23: ; preds = %if.end20 + %or24 = or i32 %5, 67108864 + store i32 %or24, i32* %b, align 4 + br label %if.end25 + +if.end25: ; preds = %if.end20, %if.then23 + %6 = phi i32 [ %5, %if.end20 ], [ %or24, %if.then23 ] + %and26 = and i32 %6, 64 + %tobool27 = icmp eq i32 %and26, 0 + br i1 %tobool27, label %if.end30, label %if.then28 + +if.then28: ; preds = %if.end25 + %or29 = or i32 %6, 33554432 + store i32 %or29, i32* %b, align 4 + br label %if.end30 + +if.end30: ; preds = %if.end25, %if.then28 + %7 = phi i32 [ %6, %if.end25 ], [ %or29, %if.then28 ] + %and31 = and i32 %7, 256 + %tobool32 = icmp eq i32 %and31, 0 + br i1 %tobool32, label %if.end35, label %if.then33 + +if.then33: ; preds = %if.end30 + %or34 = or i32 %7, 8388608 + store i32 %or34, i32* %b, align 4 + br label %if.end35 + +if.end35: ; preds = %if.end30, %if.then33 + %8 = phi i32 [ %7, %if.end30 ], [ %or34, %if.then33 ] + %and36 = and i32 %8, 512 + %tobool37 = icmp eq i32 %and36, 0 + br i1 %tobool37, label %if.end40, label %if.then38 + +if.then38: ; preds = %if.end35 + %or39 = or i32 %8, 4194304 + store i32 %or39, i32* %b, align 4 + br label %if.end40 + +if.end40: ; preds = %if.end35, %if.then38 + %9 = phi i32 [ %8, %if.end35 ], [ %or39, %if.then38 ] + %and41 = and i32 %9, 1024 + %tobool42 = icmp eq i32 %and41, 0 + br i1 %tobool42, label %if.end45, label %if.then43 + +if.then43: ; preds = %if.end40 + %or44 = or i32 %9, 2097152 + store i32 %or44, i32* %b, align 4 + br label %if.end45 + +if.end45: ; preds = %if.end40, %if.then43 + %10 = phi i32 [ %9, %if.end40 ], [ %or44, %if.then43 ] + %and46 = and i32 %10, 2048 + %tobool47 = icmp eq i32 %and46, 0 + br i1 %tobool47, label %if.end50, label %if.then48 + +if.then48: ; preds = %if.end45 + %or49 = or i32 %10, 1048576 + store i32 %or49, i32* %b, align 4 + br label %if.end50 + +if.end50: ; preds = %if.end45, %if.then48 + %11 = phi i32 [ %10, %if.end45 ], [ %or49, %if.then48 ] + %and51 = and i32 %11, 4096 + %tobool52 = icmp eq i32 %and51, 0 + br i1 %tobool52, label %if.end55, label %if.then53 + +if.then53: ; preds = %if.end50 + %or54 = or i32 %11, 524288 + store i32 %or54, i32* %b, align 4 + br label %if.end55 + +if.end55: ; preds = %if.end50, %if.then53 + %12 = phi i32 [ %11, %if.end50 ], [ %or54, %if.then53 ] + %and56 = and i32 %12, 8192 + %tobool57 = icmp eq i32 %and56, 0 + br i1 %tobool57, label %if.end60, label %if.then58 + +if.then58: ; preds = %if.end55 + %or59 = or i32 %12, 262144 + store i32 %or59, i32* %b, align 4 + br label %if.end60 + +if.end60: ; preds = %if.end55, %if.then58 + %13 = phi i32 [ %12, %if.end55 ], [ %or59, %if.then58 ] + %and61 = and i32 %13, 16384 + %tobool62 = icmp eq i32 %and61, 0 + br i1 %tobool62, label %if.end65, label %if.then63 + +if.then63: ; preds = %if.end60 + %or64 = or i32 %13, 131072 + store i32 %or64, i32* %b, align 4 + br label %if.end65 + +if.end65: ; preds = %if.end60, %if.then63 + %14 = phi i32 [ %13, %if.end60 ], [ %or64, %if.then63 ] + %and66 = and i32 %14, 32768 + %tobool67 = icmp eq i32 %and66, 0 + br i1 %tobool67, label %if.end70, label %if.then68 + +if.then68: ; preds = %if.end65 + %or69 = or i32 %14, 65536 + store i32 %or69, i32* %b, align 4 + br label %if.end70 + +if.end70: ; preds = %if.end65, %if.then68 + %15 = phi i32 [ %14, %if.end65 ], [ %or69, %if.then68 ] + %and71 = and i32 %15, 128 + %tobool72 = icmp eq i32 %and71, 0 + br i1 %tobool72, label %if.end75, label %if.then73 + +if.then73: ; preds = %if.end70 + %or74 = or i32 %15, 16777216 + store i32 %or74, i32* %b, align 4 + br label %if.end75 + +if.end75: ; preds = %if.end70, %if.then73 + ret i32 0 +} diff --git a/test/Transforms/SimplifyCFG/merge-cond-stores.ll b/test/Transforms/SimplifyCFG/merge-cond-stores.ll new file mode 100644 index 000000000000..77e3158d9bbd --- /dev/null +++ b/test/Transforms/SimplifyCFG/merge-cond-stores.ll @@ -0,0 +1,241 @@ +; RUN: opt -simplifycfg -instcombine < %s -simplifycfg-merge-cond-stores=true -simplifycfg-merge-cond-stores-aggressively=false -phi-node-folding-threshold=2 -S | FileCheck %s + +; CHECK-LABEL: @test_simple +; This test should succeed and end up if-converted. +; CHECK: icmp eq i32 %b, 0 +; CHECK-NEXT: icmp ne i32 %a, 0 +; CHECK-NEXT: xor i1 %x2, true +; CHECK-NEXT: %[[x:.*]] = or i1 %{{.*}}, %{{.*}} +; CHECK-NEXT: br i1 %[[x]] +; CHECK: store +; CHECK-NOT: store +; CHECK: ret +define void @test_simple(i32* %p, i32 %a, i32 %b) { +entry: + %x1 = icmp eq i32 %a, 0 + br i1 %x1, label %fallthrough, label %yes1 + +yes1: + store i32 0, i32* %p + br label %fallthrough + +fallthrough: + %x2 = icmp eq i32 %b, 0 + br i1 %x2, label %end, label %yes2 + +yes2: + store i32 1, i32* %p + br label %end + +end: + ret void +} + +; CHECK-LABEL: @test_recursive +; This test should entirely fold away, leaving one large basic block. +; CHECK: store +; CHECK-NOT: store +; CHECK: ret +define void @test_recursive(i32* %p, i32 %a, i32 %b, i32 %c, i32 %d) { +entry: + %x1 = icmp eq i32 %a, 0 + br i1 %x1, label %fallthrough, label %yes1 + +yes1: + store i32 0, i32* %p + br label %fallthrough + +fallthrough: + %x2 = icmp eq i32 %b, 0 + br i1 %x2, label %next, label %yes2 + +yes2: + store i32 1, i32* %p + br label %next + +next: + %x3 = icmp eq i32 %c, 0 + br i1 %x3, label %fallthrough2, label %yes3 + +yes3: + store i32 2, i32* %p + br label %fallthrough2 + +fallthrough2: + %x4 = icmp eq i32 %d, 0 + br i1 %x4, label %end, label %yes4 + +yes4: + store i32 3, i32* %p + br label %end + + +end: + ret void +} + +; CHECK-LABEL: @test_not_ifconverted +; The code in each diamond is too large - it won't be if-converted so our +; heuristics should say no. +; CHECK: store +; CHECK: store +; CHECK: ret +define void @test_not_ifconverted(i32* %p, i32 %a, i32 %b) { +entry: + %x1 = icmp eq i32 %a, 0 + br i1 %x1, label %fallthrough, label %yes1 + +yes1: + %y1 = or i32 %b, 55 + %y2 = add i32 %y1, 24 + %y3 = and i32 %y2, 67 + store i32 %y3, i32* %p + br label %fallthrough + +fallthrough: + %x2 = icmp eq i32 %b, 0 + br i1 %x2, label %end, label %yes2 + +yes2: + %z1 = or i32 %a, 55 + %z2 = add i32 %z1, 24 + %z3 = and i32 %z2, 67 + store i32 %z3, i32* %p + br label %end + +end: + ret void +} + +; CHECK-LABEL: @test_aliasing1 +; The store to %p clobbers the previous store, so if-converting this would +; be illegal. +; CHECK: store +; CHECK: store +; CHECK: ret +define void @test_aliasing1(i32* %p, i32 %a, i32 %b) { +entry: + %x1 = icmp eq i32 %a, 0 + br i1 %x1, label %fallthrough, label %yes1 + +yes1: + store i32 0, i32* %p + br label %fallthrough + +fallthrough: + %y1 = load i32, i32* %p + %x2 = icmp eq i32 %y1, 0 + br i1 %x2, label %end, label %yes2 + +yes2: + store i32 1, i32* %p + br label %end + +end: + ret void +} + +; CHECK-LABEL: @test_aliasing2 +; The load from %q aliases with %p, so if-converting this would be illegal. +; CHECK: store +; CHECK: store +; CHECK: ret +define void @test_aliasing2(i32* %p, i32* %q, i32 %a, i32 %b) { +entry: + %x1 = icmp eq i32 %a, 0 + br i1 %x1, label %fallthrough, label %yes1 + +yes1: + store i32 0, i32* %p + br label %fallthrough + +fallthrough: + %y1 = load i32, i32* %q + %x2 = icmp eq i32 %y1, 0 + br i1 %x2, label %end, label %yes2 + +yes2: + store i32 1, i32* %p + br label %end + +end: + ret void +} + +declare void @f() + +; CHECK-LABEL: @test_diamond_simple +; This should get if-converted. +; CHECK: store +; CHECK-NOT: store +; CHECK: ret +define i32 @test_diamond_simple(i32* %p, i32* %q, i32 %a, i32 %b) { +entry: + %x1 = icmp eq i32 %a, 0 + br i1 %x1, label %no1, label %yes1 + +yes1: + store i32 0, i32* %p + br label %fallthrough + +no1: + %z1 = add i32 %a, %b + br label %fallthrough + +fallthrough: + %z2 = phi i32 [ %z1, %no1 ], [ 0, %yes1 ] + %x2 = icmp eq i32 %b, 0 + br i1 %x2, label %no2, label %yes2 + +yes2: + store i32 1, i32* %p + br label %end + +no2: + %z3 = sub i32 %z2, %b + br label %end + +end: + %z4 = phi i32 [ %z3, %no2 ], [ 3, %yes2 ] + ret i32 %z4 +} + +; CHECK-LABEL: @test_diamond_alias3 +; Now there is a call to f() in the bottom branch. The store in the first +; branch would now be reordered with respect to the call if we if-converted, +; so we must not. +; CHECK: store +; CHECK: store +; CHECK: ret +define i32 @test_diamond_alias3(i32* %p, i32* %q, i32 %a, i32 %b) { +entry: + %x1 = icmp eq i32 %a, 0 + br i1 %x1, label %no1, label %yes1 + +yes1: + store i32 0, i32* %p + br label %fallthrough + +no1: + call void @f() + %z1 = add i32 %a, %b + br label %fallthrough + +fallthrough: + %z2 = phi i32 [ %z1, %no1 ], [ 0, %yes1 ] + %x2 = icmp eq i32 %b, 0 + br i1 %x2, label %no2, label %yes2 + +yes2: + store i32 1, i32* %p + br label %end + +no2: + call void @f() + %z3 = sub i32 %z2, %b + br label %end + +end: + %z4 = phi i32 [ %z3, %no2 ], [ 3, %yes2 ] + ret i32 %z4 +} diff --git a/test/Transforms/SimplifyCFG/no_speculative_loads_with_asan.ll b/test/Transforms/SimplifyCFG/no_speculative_loads_with_asan.ll new file mode 100644 index 000000000000..063bde83f7b3 --- /dev/null +++ b/test/Transforms/SimplifyCFG/no_speculative_loads_with_asan.ll @@ -0,0 +1,40 @@ +; RUN: opt -simplifycfg -S %s | FileCheck %s +; Make sure we don't speculate loads under AddressSanitizer. +@g = global i32 0, align 4 + +define i32 @TestNoAsan(i32 %cond) nounwind readonly uwtable { +entry: + %tobool = icmp eq i32 %cond, 0 + br i1 %tobool, label %return, label %if.then + +if.then: ; preds = %entry + %0 = load i32, i32* @g, align 4 + br label %return + +return: ; preds = %entry, %if.then + %retval = phi i32 [ %0, %if.then ], [ 0, %entry ] + ret i32 %retval +; CHECK-LABEL: @TestNoAsan +; CHECK: %[[LOAD:[^ ]*]] = load +; CHECK: select{{.*}}[[LOAD]] +; CHECK: ret i32 +} + +define i32 @TestAsan(i32 %cond) nounwind readonly uwtable sanitize_address { +entry: + %tobool = icmp eq i32 %cond, 0 + br i1 %tobool, label %return, label %if.then + +if.then: ; preds = %entry + %0 = load i32, i32* @g, align 4 + br label %return + +return: ; preds = %entry, %if.then + %retval = phi i32 [ %0, %if.then ], [ 0, %entry ] + ret i32 %retval +; CHECK-LABEL: @TestAsan +; CHECK: br i1 +; CHECK: load i32, i32* @g +; CHECK: br label +; CHECK: ret i32 +} diff --git a/test/Transforms/SimplifyCFG/preserve-load-metadata-2.ll b/test/Transforms/SimplifyCFG/preserve-load-metadata-2.ll new file mode 100644 index 000000000000..94d3565ce985 --- /dev/null +++ b/test/Transforms/SimplifyCFG/preserve-load-metadata-2.ll @@ -0,0 +1,32 @@ +; RUN: opt < %s -simplifycfg -S | FileCheck %s + +declare void @bar(i32*) +declare void @baz(i32*) + +; CHECK-LABEL: @test_load_combine_metadata( +; Check that dereferenceable metadata is combined +; CHECK: load i32*, i32** %p +; CHECK-SAME: !dereferenceable ![[DEREF:[0-9]+]] +; CHECK: t: +; CHECK: f: +define void @test_load_combine_metadata(i1 %c, i32** %p) { + br i1 %c, label %t, label %f + +t: + %v1 = load i32*, i32** %p, !dereferenceable !0 + call void @bar(i32* %v1) + br label %cont + +f: + %v2 = load i32*, i32** %p, !dereferenceable !1 + call void @baz(i32* %v2) + br label %cont + +cont: + ret void +} + +; CHECK: ![[DEREF]] = !{i64 8} + +!0 = !{i64 8} +!1 = !{i64 16} diff --git a/test/Transforms/SimplifyCFG/preserve-load-metadata-3.ll b/test/Transforms/SimplifyCFG/preserve-load-metadata-3.ll new file mode 100644 index 000000000000..92bdf6ec5c1a --- /dev/null +++ b/test/Transforms/SimplifyCFG/preserve-load-metadata-3.ll @@ -0,0 +1,32 @@ +; RUN: opt < %s -simplifycfg -S | FileCheck %s + +declare void @bar(i32*) +declare void @baz(i32*) + +; CHECK-LABEL: @test_load_combine_metadata( +; Check that dereferenceable_or_null metadata is combined +; CHECK: load i32*, i32** %p +; CHECK-SAME: !dereferenceable_or_null ![[DEREF:[0-9]+]] +; CHECK: t: +; CHECK: f: +define void @test_load_combine_metadata(i1 %c, i32** %p) { + br i1 %c, label %t, label %f + +t: + %v1 = load i32*, i32** %p, !dereferenceable_or_null !0 + call void @bar(i32* %v1) + br label %cont + +f: + %v2 = load i32*, i32** %p, !dereferenceable_or_null !1 + call void @baz(i32* %v2) + br label %cont + +cont: + ret void +} + +; CHECK: ![[DEREF]] = !{i64 8} + +!0 = !{i64 8} +!1 = !{i64 16} diff --git a/test/Transforms/SimplifyCFG/preserve-load-metadata.ll b/test/Transforms/SimplifyCFG/preserve-load-metadata.ll new file mode 100644 index 000000000000..89815c843152 --- /dev/null +++ b/test/Transforms/SimplifyCFG/preserve-load-metadata.ll @@ -0,0 +1,32 @@ +; RUN: opt < %s -simplifycfg -S | FileCheck %s + +declare void @bar(i32*) +declare void @baz(i32*) + +; CHECK-LABEL: @test_load_combine_metadata( +; Check that align metadata is combined +; CHECK: load i32*, i32** %p +; CHECK-SAME: !align ![[ALIGN:[0-9]+]] +; CHECK: t: +; CHECK: f: +define void @test_load_combine_metadata(i1 %c, i32** %p) { + br i1 %c, label %t, label %f + +t: + %v1 = load i32*, i32** %p, !align !0 + call void @bar(i32* %v1) + br label %cont + +f: + %v2 = load i32*, i32** %p, !align !1 + call void @baz(i32* %v2) + br label %cont + +cont: + ret void +} + +; CHECK: ![[ALIGN]] = !{i64 8} + +!0 = !{i64 8} +!1 = !{i64 16} diff --git a/test/Transforms/SimplifyCFG/preserve-make-implicit-on-switch-to-br.ll b/test/Transforms/SimplifyCFG/preserve-make-implicit-on-switch-to-br.ll new file mode 100644 index 000000000000..0e95336bbc1f --- /dev/null +++ b/test/Transforms/SimplifyCFG/preserve-make-implicit-on-switch-to-br.ll @@ -0,0 +1,30 @@ +; RUN: opt %s -simplifycfg -S | FileCheck %s + +; ConstantFoldTerminator function can convert SwitchInst with one case (and default) to +; a conditional BranchInst. This test checks the converted BranchInst preserve the +; make.implicit metadata. + +declare i32 @consume(i32*) +declare void @trap() + +define i32 @copy-metadata(i32* %x) { + +entry: + %x.int = ptrtoint i32* %x to i64 + +; CHECK: br i1 %cond, label %is_null, label %default, !make.implicit !0 + switch i64 %x.int, label %default [ + i64 0, label %is_null + ], !make.implicit !0 + +default: + %0 = call i32 @consume(i32* %x) + ret i32 %0 + +is_null: + call void @trap() + unreachable +} + +!0 = !{} + diff --git a/test/Transforms/SimplifyCFG/speculate-math.ll b/test/Transforms/SimplifyCFG/speculate-math.ll index 0ba93d29117a..5655d5d78821 100644 --- a/test/Transforms/SimplifyCFG/speculate-math.ll +++ b/test/Transforms/SimplifyCFG/speculate-math.ll @@ -1,4 +1,5 @@ -; RUN: opt -S -simplifycfg -phi-node-folding-threshold=2 < %s | FileCheck %s +; RUN: opt -S -simplifycfg < %s | FileCheck %s --check-prefix=EXPENSIVE --check-prefix=ALL +; RUN: opt -S -simplifycfg -speculate-one-expensive-inst=false < %s | FileCheck %s --check-prefix=CHEAP --check-prefix=ALL declare float @llvm.sqrt.f32(float) nounwind readonly declare float @llvm.fma.f32(float, float, float) nounwind readonly @@ -7,8 +8,26 @@ declare float @llvm.fabs.f32(float) nounwind readonly declare float @llvm.minnum.f32(float, float) nounwind readonly declare float @llvm.maxnum.f32(float, float) nounwind readonly -; CHECK-LABEL: @sqrt_test( -; CHECK: select +; ALL-LABEL: @fdiv_test( +; EXPENSIVE: select i1 %cmp, double %div, double 0.0 +; CHEAP-NOT: select + +define double @fdiv_test(double %a, double %b) { +entry: + %cmp = fcmp ogt double %a, 0.0 + br i1 %cmp, label %cond.true, label %cond.end + +cond.true: + %div = fdiv double %b, %a + br label %cond.end + +cond.end: + %cond = phi double [ %div, %cond.true ], [ 0.0, %entry ] + ret double %cond +} + +; ALL-LABEL: @sqrt_test( +; ALL: select define void @sqrt_test(float addrspace(1)* noalias nocapture %out, float %a) nounwind { entry: %cmp.i = fcmp olt float %a, 0.000000e+00 @@ -24,8 +43,8 @@ test_sqrt.exit: ; preds = %cond.else.i, %entry ret void } -; CHECK-LABEL: @fabs_test( -; CHECK: select +; ALL-LABEL: @fabs_test( +; ALL: select define void @fabs_test(float addrspace(1)* noalias nocapture %out, float %a) nounwind { entry: %cmp.i = fcmp olt float %a, 0.000000e+00 @@ -41,8 +60,8 @@ test_fabs.exit: ; preds = %cond.else.i, %entry ret void } -; CHECK-LABEL: @fma_test( -; CHECK: select +; ALL-LABEL: @fma_test( +; ALL: select define void @fma_test(float addrspace(1)* noalias nocapture %out, float %a, float %b, float %c) nounwind { entry: %cmp.i = fcmp olt float %a, 0.000000e+00 @@ -58,8 +77,8 @@ test_fma.exit: ; preds = %cond.else.i, %entry ret void } -; CHECK-LABEL: @fmuladd_test( -; CHECK: select +; ALL-LABEL: @fmuladd_test( +; ALL: select define void @fmuladd_test(float addrspace(1)* noalias nocapture %out, float %a, float %b, float %c) nounwind { entry: %cmp.i = fcmp olt float %a, 0.000000e+00 @@ -75,8 +94,8 @@ test_fmuladd.exit: ; preds = %cond.else.i, %en ret void } -; CHECK-LABEL: @minnum_test( -; CHECK: select +; ALL-LABEL: @minnum_test( +; ALL: select define void @minnum_test(float addrspace(1)* noalias nocapture %out, float %a, float %b) nounwind { entry: %cmp.i = fcmp olt float %a, 0.000000e+00 @@ -92,8 +111,8 @@ test_minnum.exit: ; preds = %cond.else.i, %ent ret void } -; CHECK-LABEL: @maxnum_test( -; CHECK: select +; ALL-LABEL: @maxnum_test( +; ALL: select define void @maxnum_test(float addrspace(1)* noalias nocapture %out, float %a, float %b) nounwind { entry: %cmp.i = fcmp olt float %a, 0.000000e+00 diff --git a/test/Transforms/SimplifyCFG/statepoint-invoke-unwind.ll b/test/Transforms/SimplifyCFG/statepoint-invoke-unwind.ll index 994e47eb0d64..53daa8292da7 100644 --- a/test/Transforms/SimplifyCFG/statepoint-invoke-unwind.ll +++ b/test/Transforms/SimplifyCFG/statepoint-invoke-unwind.ll @@ -3,15 +3,15 @@ ; not optimized into call declare i64 addrspace(1)* @gc_call() -declare i32 @llvm.experimental.gc.statepoint.p0f_p1i64f(i64, i32, i64 addrspace(1)* ()*, i32, i32, ...) +declare token @llvm.experimental.gc.statepoint.p0f_p1i64f(i64, i32, i64 addrspace(1)* ()*, i32, i32, ...) declare i32* @fake_personality_function() define i32 @test() gc "statepoint-example" personality i32* ()* @fake_personality_function { ; CHECK-LABEL: test entry: ; CHECK-LABEL: entry: - ; CHECK-NEXT: %sp = invoke i32 (i64, i32, i64 addrspace(1)* ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_p1i64f - %sp = invoke i32 (i64, i32, i64 addrspace(1)* ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_p1i64f(i64 0, i32 0, i64 addrspace(1)* ()* @gc_call, i32 0, i32 0, i32 0, i32 0) + ; CHECK-NEXT: %sp = invoke token (i64, i32, i64 addrspace(1)* ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_p1i64f + %sp = invoke token (i64, i32, i64 addrspace(1)* ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_p1i64f(i64 0, i32 0, i64 addrspace(1)* ()* @gc_call, i32 0, i32 0, i32 0, i32 0) to label %normal unwind label %exception exception: diff --git a/test/Transforms/SimplifyCFG/switch-dead-default.ll b/test/Transforms/SimplifyCFG/switch-dead-default.ll new file mode 100644 index 000000000000..e5c2ef65b318 --- /dev/null +++ b/test/Transforms/SimplifyCFG/switch-dead-default.ll @@ -0,0 +1,179 @@ +; RUN: opt %s -S -simplifycfg | FileCheck %s +declare void @foo(i32) + +define void @test(i1 %a) { +; CHECK-LABEL: @test +; CHECK: br i1 [[IGNORE:%.*]], label %true, label %false + switch i1 %a, label %default [i1 1, label %true + i1 0, label %false] +true: + call void @foo(i32 1) + ret void +false: + call void @foo(i32 3) + ret void +default: + call void @foo(i32 2) + ret void +} + +define void @test2(i2 %a) { +; CHECK-LABEL: @test2 + switch i2 %a, label %default [i2 0, label %case0 + i2 1, label %case1 + i2 2, label %case2 + i2 3, label %case3] +case0: + call void @foo(i32 0) + ret void +case1: + call void @foo(i32 1) + ret void +case2: + call void @foo(i32 2) + ret void +case3: + call void @foo(i32 3) + ret void +default: +; CHECK-LABEL: default1: +; CHECK-NEXT: unreachable + call void @foo(i32 4) + ret void +} + +; This one is a negative test - we know the value of the default, +; but that's about it +define void @test3(i2 %a) { +; CHECK-LABEL: @test3 + switch i2 %a, label %default [i2 0, label %case0 + i2 1, label %case1 + i2 2, label %case2] + +case0: + call void @foo(i32 0) + ret void +case1: + call void @foo(i32 1) + ret void +case2: + call void @foo(i32 2) + ret void +default: +; CHECK-LABEL: default: +; CHECK-NEXT: call void @foo + call void @foo(i32 0) + ret void +} + +; Negative test - check for possible overflow when computing +; number of possible cases. +define void @test4(i128 %a) { +; CHECK-LABEL: @test4 + switch i128 %a, label %default [i128 0, label %case0 + i128 1, label %case1] + +case0: + call void @foo(i32 0) + ret void +case1: + call void @foo(i32 1) + ret void +default: +; CHECK-LABEL: default: +; CHECK-NEXT: call void @foo + call void @foo(i32 0) + ret void +} + +; All but one bit known zero +define void @test5(i8 %a) { +; CHECK-LABEL: @test5 +; CHECK: br i1 [[IGNORE:%.*]], label %true, label %false + %cmp = icmp ult i8 %a, 2 + call void @llvm.assume(i1 %cmp) + switch i8 %a, label %default [i8 1, label %true + i8 0, label %false] +true: + call void @foo(i32 1) + ret void +false: + call void @foo(i32 3) + ret void +default: + call void @foo(i32 2) + ret void +} + +;; All but one bit known one +define void @test6(i8 %a) { +; CHECK-LABEL: @test6 +; CHECK: @llvm.assume +; CHECK: br i1 [[IGNORE:%.*]], label %true, label %false + %and = and i8 %a, 254 + %cmp = icmp eq i8 %and, 254 + call void @llvm.assume(i1 %cmp) + switch i8 %a, label %default [i8 255, label %true + i8 254, label %false] +true: + call void @foo(i32 1) + ret void +false: + call void @foo(i32 3) + ret void +default: + call void @foo(i32 2) + ret void +} + +; Check that we can eliminate both dead cases and dead defaults +; within a single run of simplify-cfg +define void @test7(i8 %a) { +; CHECK-LABEL: @test7 +; CHECK: @llvm.assume +; CHECK: br i1 [[IGNORE:%.*]], label %true, label %false + %and = and i8 %a, 254 + %cmp = icmp eq i8 %and, 254 + call void @llvm.assume(i1 %cmp) + switch i8 %a, label %default [i8 255, label %true + i8 254, label %false + i8 0, label %also_dead] +true: + call void @foo(i32 1) + ret void +false: + call void @foo(i32 3) + ret void +also_dead: + call void @foo(i32 5) + ret void +default: + call void @foo(i32 2) + ret void +} + +;; All but one bit known undef +;; Note: This is currently testing an optimization which doesn't trigger. The +;; case this is protecting against is that a bit could be assumed both zero +;; *or* one given we know it's undef. ValueTracking doesn't do this today, +;; but it doesn't hurt to confirm. +define void @test8(i8 %a) { +; CHECK-LABEL: @test8( +; CHECK: switch i8 + %and = and i8 %a, 254 + %cmp = icmp eq i8 %and, undef + call void @llvm.assume(i1 %cmp) + switch i8 %a, label %default [i8 255, label %true + i8 254, label %false] +true: + call void @foo(i32 1) + ret void +false: + call void @foo(i32 3) + ret void +default: + call void @foo(i32 2) + ret void +} + +declare void @llvm.assume(i1) diff --git a/test/Transforms/SimplifyCFG/trap-debugloc.ll b/test/Transforms/SimplifyCFG/trap-debugloc.ll index fedf6b172d75..2887aaf52eee 100644 --- a/test/Transforms/SimplifyCFG/trap-debugloc.ll +++ b/test/Transforms/SimplifyCFG/trap-debugloc.ll @@ -1,7 +1,7 @@ ; RUN: opt -S -simplifycfg < %s | FileCheck %s ; Radar 9342286 ; Assign DebugLoc to trap instruction. -define void @foo() nounwind ssp { +define void @foo() nounwind ssp !dbg !0 { ; CHECK: call void @llvm.trap(), !dbg store i32 42, i32* null, !dbg !5 ret void, !dbg !7 @@ -11,9 +11,9 @@ define void @foo() nounwind ssp { !llvm.module.flags = !{!10} !llvm.dbg.sp = !{!0} -!0 = !DISubprogram(name: "foo", line: 3, isLocal: false, isDefinition: true, virtualIndex: 6, isOptimized: false, file: !8, scope: !1, type: !3, function: void ()* @foo) +!0 = distinct !DISubprogram(name: "foo", line: 3, isLocal: false, isDefinition: true, virtualIndex: 6, isOptimized: false, file: !8, scope: !1, type: !3) !1 = !DIFile(filename: "foo.c", directory: "/private/tmp") -!2 = !DICompileUnit(language: DW_LANG_C99, producer: "Apple clang version 3.0 (tags/Apple/clang-206.1) (based on LLVM 3.0svn)", isOptimized: true, emissionKind: 0, file: !8, enums: !{}, retainedTypes: !{}, subprograms: !9) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, producer: "Apple clang version 3.0 (tags/Apple/clang-206.1) (based on LLVM 3.0svn)", isOptimized: true, emissionKind: 0, file: !8, enums: !{}, retainedTypes: !{}, subprograms: !9) !3 = !DISubroutineType(types: !4) !4 = !{null} !5 = !DILocation(line: 4, column: 2, scope: !6) diff --git a/test/Transforms/SimplifyCFG/wineh-unreachable.ll b/test/Transforms/SimplifyCFG/wineh-unreachable.ll new file mode 100644 index 000000000000..670119467dae --- /dev/null +++ b/test/Transforms/SimplifyCFG/wineh-unreachable.ll @@ -0,0 +1,83 @@ +; RUN: opt -S -simplifycfg < %s | FileCheck %s + +declare void @Personality() +declare void @f() + +; CHECK-LABEL: define void @test1() +define void @test1() personality i8* bitcast (void ()* @Personality to i8*) { +entry: + ; CHECK: call void @f() + invoke void @f() + to label %exit unwind label %unreachable.unwind +exit: + ret void +unreachable.unwind: + cleanuppad within none [] + unreachable +} + +; CHECK-LABEL: define void @test2() +define void @test2() personality i8* bitcast (void ()* @Personality to i8*) { +entry: + invoke void @f() + to label %exit unwind label %catch.pad +catch.pad: + %cs1 = catchswitch within none [label %catch.body] unwind label %unreachable.unwind + ; CHECK: catch.pad: + ; CHECK-NEXT: catchswitch within none [label %catch.body] unwind to caller +catch.body: + ; CHECK: catch.body: + ; CHECK-NEXT: catchpad within %cs1 + ; CHECK-NEXT: call void @f() + ; CHECK-NEXT: unreachable + %catch = catchpad within %cs1 [] + call void @f() + catchret from %catch to label %unreachable +exit: + ret void +unreachable.unwind: + cleanuppad within none [] + unreachable +unreachable: + unreachable +} + +; CHECK-LABEL: define void @test3() +define void @test3() personality i8* bitcast (void ()* @Personality to i8*) { +entry: + invoke void @f() + to label %exit unwind label %cleanup.pad +cleanup.pad: + ; CHECK: %cleanup = cleanuppad within none [] + ; CHECK-NEXT: call void @f() + ; CHECK-NEXT: unreachable + %cleanup = cleanuppad within none [] + invoke void @f() + to label %cleanup.ret unwind label %unreachable.unwind +cleanup.ret: + ; This cleanupret should be rewritten to unreachable, + ; and merged into the pred block. + cleanupret from %cleanup unwind label %unreachable.unwind +exit: + ret void +unreachable.unwind: + cleanuppad within none [] + unreachable +} + +; CHECK-LABEL: define void @test5() +define void @test5() personality i8* bitcast (void ()* @Personality to i8*) { +entry: + invoke void @f() + to label %exit unwind label %catch.pad + +catch.pad: + %cs1 = catchswitch within none [label %catch.body] unwind to caller + +catch.body: + %catch = catchpad within %cs1 [] + catchret from %catch to label %exit + +exit: + unreachable +} |
