| ; RUN: opt -S -passes=jump-threading,dce < %s | FileCheck %s | |
| target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | |
| target triple = "x86_64-unknown-linux-gnu" | |
| ; Function Attrs: nounwind uwtable | |
| define i32 @test1(i32 %a, i32 %b) #0 { | |
| entry: | |
| %cmp = icmp sgt i32 %a, 5 | |
| tail call void @llvm.assume(i1 %cmp) | |
| %cmp1 = icmp sgt i32 %b, 1234 | |
| br i1 %cmp1, label %if.then, label %if.else | |
| ; CHECK-LABEL: @test1 | |
| ; CHECK: icmp sgt i32 %a, 5 | |
| ; CHECK: call void @llvm.assume | |
| ; CHECK-NOT: icmp sgt i32 %a, 3 | |
| ; CHECK: ret i32 | |
| if.then: ; preds = %entry | |
| %cmp2 = icmp sgt i32 %a, 3 | |
| br i1 %cmp2, label %if.then3, label %return | |
| if.then3: ; preds = %if.then | |
| tail call void (...) @bar() #1 | |
| br label %return | |
| if.else: ; preds = %entry | |
| tail call void (...) @car() #1 | |
| br label %return | |
| return: ; preds = %if.else, %if.then, %if.then3 | |
| %retval.0 = phi i32 [ 1, %if.then3 ], [ 0, %if.then ], [ 0, %if.else ] | |
| ret i32 %retval.0 | |
| } | |
| define i32 @test2(i32 %a) #0 { | |
| entry: | |
| %cmp = icmp sgt i32 %a, 5 | |
| tail call void @llvm.assume(i1 %cmp) | |
| %cmp1 = icmp sgt i32 %a, 3 | |
| br i1 %cmp1, label %if.then, label %return | |
| ; CHECK-LABEL: @test2 | |
| ; CHECK: icmp sgt i32 %a, 5 | |
| ; CHECK: tail call void @llvm.assume | |
| ; CHECK: tail call void (...) @bar() | |
| ; CHECK: ret i32 1 | |
| if.then: ; preds = %entry | |
| tail call void (...) @bar() #1 | |
| br label %return | |
| return: ; preds = %entry, %if.then | |
| %retval.0 = phi i32 [ 1, %if.then ], [ 0, %entry ] | |
| ret i32 %retval.0 | |
| } | |
| @g = external global i32 | |
| ; Check that we do prove a fact using an assume within the block. | |
| ; We can fold the assume based on the semantics of assume. | |
| define void @can_fold_assume(ptr %array) { | |
| ; CHECK-LABEL: @can_fold_assume | |
| ; CHECK-NOT: call void @llvm.assume | |
| ; CHECK-NOT: br | |
| ; CHECK: ret void | |
| %notnull = icmp ne ptr %array, null | |
| call void @llvm.assume(i1 %notnull) | |
| br i1 %notnull, label %normal, label %error | |
| normal: | |
| ret void | |
| error: | |
| store atomic i32 0, ptr @g unordered, align 4 | |
| ret void | |
| } | |
| declare void @f(i1) | |
| declare void @exit() | |
| ; We can fold the assume but not the uses before the assume. | |
| define void @cannot_fold_use_before_assume(ptr %array) { | |
| ; CHECK-LABEL:@cannot_fold_use_before_assume | |
| ; CHECK: @f(i1 %notnull) | |
| ; CHECK-NEXT: exit() | |
| ; CHECK-NOT: assume | |
| ; CHECK-NEXT: ret void | |
| %notnull = icmp ne ptr %array, null | |
| call void @f(i1 %notnull) | |
| call void @exit() | |
| call void @llvm.assume(i1 %notnull) | |
| br i1 %notnull, label %normal, label %error | |
| normal: | |
| ret void | |
| error: | |
| store atomic i32 0, ptr @g unordered, align 4 | |
| ret void | |
| } | |
| declare void @dummy(i1) nounwind willreturn | |
| define void @can_fold_some_use_before_assume(ptr %array) { | |
| ; CHECK-LABEL:@can_fold_some_use_before_assume | |
| ; CHECK: @f(i1 %notnull) | |
| ; CHECK-NEXT: @dummy(i1 true) | |
| ; CHECK-NOT: assume | |
| ; CHECK-NEXT: ret void | |
| %notnull = icmp ne ptr %array, null | |
| call void @f(i1 %notnull) | |
| call void @dummy(i1 %notnull) | |
| call void @llvm.assume(i1 %notnull) | |
| br i1 %notnull, label %normal, label %error | |
| normal: | |
| ret void | |
| error: | |
| store atomic i32 0, ptr @g unordered, align 4 | |
| ret void | |
| } | |
| ; FIXME: can fold assume and all uses before/after assume. | |
| ; because the trapping exit call is after the assume. | |
| define void @can_fold_assume_and_all_uses(ptr %array) { | |
| ; CHECK-LABEL:@can_fold_assume_and_all_uses | |
| ; CHECK: @dummy(i1 %notnull) | |
| ; CHECK-NEXT: assume(i1 %notnull) | |
| ; CHECK-NEXT: exit() | |
| ; CHECK-NEXT: %notnull2 = or i1 true, false | |
| ; CHECK-NEXT: @f(i1 %notnull2) | |
| ; CHECK-NEXT: ret void | |
| %notnull = icmp ne ptr %array, null | |
| call void @dummy(i1 %notnull) | |
| call void @llvm.assume(i1 %notnull) | |
| call void @exit() | |
| br i1 %notnull, label %normal, label %error | |
| normal: | |
| %notnull2 = or i1 %notnull, false | |
| call void @f(i1 %notnull2) | |
| ret void | |
| error: | |
| store atomic i32 0, ptr @g unordered, align 4 | |
| ret void | |
| } | |
| declare void @fz(i8) | |
| ; FIXME: We can fold assume to true, and the use after assume, but we do not do so | |
| ; currently, because of the function call after the assume. | |
| define void @can_fold_assume2(ptr %array) { | |
| ; CHECK-LABEL:@can_fold_assume2 | |
| ; CHECK: @f(i1 %notnull) | |
| ; CHECK-NEXT: assume(i1 %notnull) | |
| ; CHECK-NEXT: znotnull = zext i1 %notnull to i8 | |
| ; CHECK-NEXT: @f(i1 %notnull) | |
| ; CHECK-NEXT: @f(i1 true) | |
| ; CHECK-NEXT: @fz(i8 %znotnull) | |
| ; CHECK-NEXT: ret void | |
| %notnull = icmp ne ptr %array, null | |
| call void @f(i1 %notnull) | |
| call void @llvm.assume(i1 %notnull) | |
| %znotnull = zext i1 %notnull to i8 | |
| call void @f(i1 %notnull) | |
| br i1 %notnull, label %normal, label %error | |
| normal: | |
| call void @f(i1 %notnull) | |
| call void @fz(i8 %znotnull) | |
| ret void | |
| error: | |
| store atomic i32 0, ptr @g unordered, align 4 | |
| ret void | |
| } | |
| declare void @llvm.experimental.guard(i1, ...) | |
| ; FIXME: We can fold assume to true, but we do not do so | |
| ; because of the guard following the assume. | |
| define void @can_fold_assume3(ptr %array){ | |
| ; CHECK-LABEL:@can_fold_assume3 | |
| ; CHECK: @f(i1 %notnull) | |
| ; CHECK-NEXT: assume(i1 %notnull) | |
| ; CHECK-NEXT: guard(i1 %notnull) | |
| ; CHECK-NEXT: znotnull = zext i1 true to i8 | |
| ; CHECK-NEXT: @f(i1 true) | |
| ; CHECK-NEXT: @fz(i8 %znotnull) | |
| ; CHECK-NEXT: ret void | |
| %notnull = icmp ne ptr %array, null | |
| call void @f(i1 %notnull) | |
| call void @llvm.assume(i1 %notnull) | |
| call void(i1, ...) @llvm.experimental.guard(i1 %notnull) [ "deopt"() ] | |
| %znotnull = zext i1 %notnull to i8 | |
| br i1 %notnull, label %normal, label %error | |
| normal: | |
| call void @f(i1 %notnull) | |
| call void @fz(i8 %znotnull) | |
| ret void | |
| error: | |
| store atomic i32 0, ptr @g unordered, align 4 | |
| ret void | |
| } | |
| ; can fold all uses and remove the cond | |
| define void @can_fold_assume4(ptr %array) { | |
| ; CHECK-LABEL: can_fold_assume4 | |
| ; CHECK-NOT: notnull | |
| ; CHECK: dummy(i1 true) | |
| ; CHECK-NEXT: ret void | |
| %notnull = icmp ne ptr %array, null | |
| call void @exit() | |
| call void @dummy(i1 %notnull) | |
| call void @llvm.assume(i1 %notnull) | |
| br i1 %notnull, label %normal, label %error | |
| normal: | |
| ret void | |
| error: | |
| store atomic i32 0, ptr @g unordered, align 4 | |
| ret void | |
| } | |
| ; Function Attrs: nounwind | |
| declare void @llvm.assume(i1) #1 | |
| declare void @bar(...) | |
| declare void @car(...) | |
| attributes #0 = { nounwind uwtable } | |
| attributes #1 = { nounwind } | |