152 lines · plain
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py2; RUN: opt < %s -passes=gvn -S | FileCheck %s3 4; load may be speculated, address is not null using context search.5; There is a critical edge.6define i32 @loadpre_critical_edge(ptr 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 ptr [[ARG:%.*]], null10; 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, ptr [[ARG]], align 413; 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]], ptr [[ARG]], align 420; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 121; 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 027;28entry:29 %cmp = icmp eq ptr %arg, null30 br i1 %cmp, label %null_exit, label %header31 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) readnone36 %v = load i32, ptr %arg37 %sum = add i32 %new_v, %v38 store i32 %sum, ptr %arg39 %iv.next = add i32 %iv, 140 %cond = icmp eq i32 %iv.next, %N41 br i1 %cond, label %exit, label %header42 43exit:44 ret i32 %sum45 46null_exit:47 ret i32 048}49 50; load may be speculated, address is not null using context search.51define i32 @loadpre_basic(ptr 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 ptr [[ARG:%.*]], null55; CHECK-NEXT: br i1 [[CMP]], label [[NULL_EXIT:%.*]], label [[PREHEADER:%.*]]56; CHECK: preheader:57; CHECK-NEXT: [[V_PRE:%.*]] = load i32, ptr [[ARG]], align 458; 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]], ptr [[ARG]], align 465; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 166; 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 072;73entry:74 %cmp = icmp eq ptr %arg, null75 br i1 %cmp, label %null_exit, label %preheader76 77preheader:78 br label %header79 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) readnone84 %v = load i32, ptr %arg85 %sum = add i32 %new_v, %v86 store i32 %sum, ptr %arg87 %iv.next = add i32 %iv, 188 %cond = icmp eq i32 %iv.next, %N89 br i1 %cond, label %exit, label %header90 91exit:92 ret i32 %sum93 94null_exit:95 ret i32 096}97 98; load cannot be speculated, check "address is not null" does not dominate the loop.99define i32 @loadpre_maybe_null(ptr 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 ptr [[ARG:%.*]], null105; 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, ptr [[ARG]], align 4112; CHECK-NEXT: [[SUM:%.*]] = add i32 [[NEW_V]], [[V]]113; CHECK-NEXT: store i32 [[SUM]], ptr [[ARG]], align 4114; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1115; 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 0121;122entry:123 br i1 %c, label %null_check, label %preheader124 125null_check:126 %cmp = icmp eq ptr %arg, null127 br i1 %cmp, label %null_exit, label %preheader128 129preheader:130 br label %header131 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) readnone136 %v = load i32, ptr %arg137 %sum = add i32 %new_v, %v138 store i32 %sum, ptr %arg139 %iv.next = add i32 %iv, 1140 %cond = icmp eq i32 %iv.next, %N141 br i1 %cond, label %exit, label %header142 143exit:144 ret i32 %sum145 146null_exit:147 ret i32 0148}149 150; Does not guarantee that returns.151declare i32 @ro_foo(i32) readnone152