| ; RUN: opt -S -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(i32* %array) { |
| ; CHECK-LABEL: @can_fold_assume |
| ; CHECK-NOT: call void @llvm.assume |
| ; CHECK-NOT: br |
| ; CHECK: ret void |
| %notnull = icmp ne i32* %array, null |
| call void @llvm.assume(i1 %notnull) |
| br i1 %notnull, label %normal, label %error |
| |
| normal: |
| ret void |
| |
| error: |
| store atomic i32 0, i32* @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(i32* %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 i32* %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, i32* @g unordered, align 4 |
| ret void |
| } |
| |
| declare void @dummy(i1) nounwind argmemonly |
| define void @can_fold_some_use_before_assume(i32* %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 i32* %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, i32* @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(i32* %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 i32* %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, i32* @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(i32* %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 i32* %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, i32* @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(i32* %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 i32* %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, i32* @g unordered, align 4 |
| ret void |
| } |
| |
| |
| ; can fold all uses and remove the cond |
| define void @can_fold_assume4(i32* %array) { |
| ; CHECK-LABEL: can_fold_assume4 |
| ; CHECK-NOT: notnull |
| ; CHECK: dummy(i1 true) |
| ; CHECK-NEXT: ret void |
| %notnull = icmp ne i32* %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, i32* @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 } |
| |