1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 2; RUN: opt < %s -gvn --basic-aa -S | FileCheck %s 3 4; load may be speculated, address is not null using context search. 5; There is a critical edge. 6define i32 @loadpre_critical_edge(i32* align 8 dereferenceable_or_null(48) %arg, i32 %N) nofree nosync { 7; CHECK-LABEL: @loadpre_critical_edge( 8; CHECK-NEXT: entry: 9; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32* [[ARG:%.*]], null 10; CHECK-NEXT: br i1 [[CMP]], label [[NULL_EXIT:%.*]], label [[ENTRY_HEADER_CRIT_EDGE:%.*]] 11; CHECK: entry.header_crit_edge: 12; CHECK-NEXT: [[V_PRE:%.*]] = load i32, i32* [[ARG]], align 4 13; CHECK-NEXT: br label [[HEADER:%.*]] 14; CHECK: header: 15; CHECK-NEXT: [[V:%.*]] = phi i32 [ [[V_PRE]], [[ENTRY_HEADER_CRIT_EDGE]] ], [ [[SUM:%.*]], [[HEADER]] ] 16; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY_HEADER_CRIT_EDGE]] ], [ [[IV_NEXT:%.*]], [[HEADER]] ] 17; CHECK-NEXT: [[NEW_V:%.*]] = call i32 @ro_foo(i32 [[IV]]) #[[ATTR0:[0-9]+]] 18; CHECK-NEXT: [[SUM]] = add i32 [[NEW_V]], [[V]] 19; CHECK-NEXT: store i32 [[SUM]], i32* [[ARG]], align 4 20; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 21; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[IV_NEXT]], [[N:%.*]] 22; CHECK-NEXT: br i1 [[COND]], label [[EXIT:%.*]], label [[HEADER]] 23; CHECK: exit: 24; CHECK-NEXT: ret i32 [[SUM]] 25; CHECK: null_exit: 26; CHECK-NEXT: ret i32 0 27; 28entry: 29 %cmp = icmp eq i32* %arg, null 30 br i1 %cmp, label %null_exit, label %header 31 32header: 33 %iv = phi i32 [0, %entry], [%iv.next, %header] 34; Call prevents to move load over due to it does not guarantee to return. 35 %new_v = call i32 @ro_foo(i32 %iv) readnone 36 %v = load i32, i32* %arg 37 %sum = add i32 %new_v, %v 38 store i32 %sum, i32* %arg 39 %iv.next = add i32 %iv, 1 40 %cond = icmp eq i32 %iv.next, %N 41 br i1 %cond, label %exit, label %header 42 43exit: 44 ret i32 %sum 45 46null_exit: 47 ret i32 0 48} 49 50; load may be speculated, address is not null using context search. 51define i32 @loadpre_basic(i32* align 8 dereferenceable_or_null(48) %arg, i32 %N) nofree nosync { 52; CHECK-LABEL: @loadpre_basic( 53; CHECK-NEXT: entry: 54; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32* [[ARG:%.*]], null 55; CHECK-NEXT: br i1 [[CMP]], label [[NULL_EXIT:%.*]], label [[PREHEADER:%.*]] 56; CHECK: preheader: 57; CHECK-NEXT: [[V_PRE:%.*]] = load i32, i32* [[ARG]], align 4 58; CHECK-NEXT: br label [[HEADER:%.*]] 59; CHECK: header: 60; CHECK-NEXT: [[V:%.*]] = phi i32 [ [[V_PRE]], [[PREHEADER]] ], [ [[SUM:%.*]], [[HEADER]] ] 61; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[PREHEADER]] ], [ [[IV_NEXT:%.*]], [[HEADER]] ] 62; CHECK-NEXT: [[NEW_V:%.*]] = call i32 @ro_foo(i32 [[IV]]) #[[ATTR0]] 63; CHECK-NEXT: [[SUM]] = add i32 [[NEW_V]], [[V]] 64; CHECK-NEXT: store i32 [[SUM]], i32* [[ARG]], align 4 65; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 66; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[IV_NEXT]], [[N:%.*]] 67; CHECK-NEXT: br i1 [[COND]], label [[EXIT:%.*]], label [[HEADER]] 68; CHECK: exit: 69; CHECK-NEXT: ret i32 [[SUM]] 70; CHECK: null_exit: 71; CHECK-NEXT: ret i32 0 72; 73entry: 74 %cmp = icmp eq i32* %arg, null 75 br i1 %cmp, label %null_exit, label %preheader 76 77preheader: 78 br label %header 79 80header: 81 %iv = phi i32 [0, %preheader], [%iv.next, %header] 82; Call prevents to move load over due to it does not guarantee to return. 83 %new_v = call i32 @ro_foo(i32 %iv) readnone 84 %v = load i32, i32* %arg 85 %sum = add i32 %new_v, %v 86 store i32 %sum, i32* %arg 87 %iv.next = add i32 %iv, 1 88 %cond = icmp eq i32 %iv.next, %N 89 br i1 %cond, label %exit, label %header 90 91exit: 92 ret i32 %sum 93 94null_exit: 95 ret i32 0 96} 97 98; load cannot be speculated, check "address is not null" does not dominate the loop. 99define i32 @loadpre_maybe_null(i32* align 8 dereferenceable_or_null(48) %arg, i32 %N, i1 %c) nofree nosync { 100; CHECK-LABEL: @loadpre_maybe_null( 101; CHECK-NEXT: entry: 102; CHECK-NEXT: br i1 [[C:%.*]], label [[NULL_CHECK:%.*]], label [[PREHEADER:%.*]] 103; CHECK: null_check: 104; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32* [[ARG:%.*]], null 105; CHECK-NEXT: br i1 [[CMP]], label [[NULL_EXIT:%.*]], label [[PREHEADER]] 106; CHECK: preheader: 107; CHECK-NEXT: br label [[HEADER:%.*]] 108; CHECK: header: 109; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[PREHEADER]] ], [ [[IV_NEXT:%.*]], [[HEADER]] ] 110; CHECK-NEXT: [[NEW_V:%.*]] = call i32 @ro_foo(i32 [[IV]]) #[[ATTR0]] 111; CHECK-NEXT: [[V:%.*]] = load i32, i32* [[ARG]], align 4 112; CHECK-NEXT: [[SUM:%.*]] = add i32 [[NEW_V]], [[V]] 113; CHECK-NEXT: store i32 [[SUM]], i32* [[ARG]], align 4 114; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 115; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[IV_NEXT]], [[N:%.*]] 116; CHECK-NEXT: br i1 [[COND]], label [[EXIT:%.*]], label [[HEADER]] 117; CHECK: exit: 118; CHECK-NEXT: ret i32 [[SUM]] 119; CHECK: null_exit: 120; CHECK-NEXT: ret i32 0 121; 122entry: 123 br i1 %c, label %null_check, label %preheader 124 125null_check: 126 %cmp = icmp eq i32* %arg, null 127 br i1 %cmp, label %null_exit, label %preheader 128 129preheader: 130 br label %header 131 132header: 133 %iv = phi i32 [0, %preheader], [%iv.next, %header] 134; Call prevents to move load over due to it does not guarantee to return. 135 %new_v = call i32 @ro_foo(i32 %iv) readnone 136 %v = load i32, i32* %arg 137 %sum = add i32 %new_v, %v 138 store i32 %sum, i32* %arg 139 %iv.next = add i32 %iv, 1 140 %cond = icmp eq i32 %iv.next, %N 141 br i1 %cond, label %exit, label %header 142 143exit: 144 ret i32 %sum 145 146null_exit: 147 ret i32 0 148} 149 150; Does not guarantee that returns. 151declare i32 @ro_foo(i32) readnone 152