1; RUN: opt < %s -tailcallelim -S | FileCheck %s 2; PR4323 3 4target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 5 6; Several cases where tail call elimination should move the load above the call, 7; then eliminate the tail recursion. 8 9 10@global = external global i32 ; <i32*> [#uses=1] 11@extern_weak_global = extern_weak global i32 ; <i32*> [#uses=1] 12 13 14; This load can be moved above the call because the function won't write to it 15; and the call has no side effects. 16define fastcc i32 @raise_load_1(i32* %a_arg, i32 %a_len_arg, i32 %start_arg) nounwind readonly { 17; CHECK-LABEL: @raise_load_1( 18; CHECK-NOT: call 19; CHECK: load i32, i32* 20; CHECK-NOT: call 21; CHECK: } 22entry: 23 %tmp2 = icmp sge i32 %start_arg, %a_len_arg ; <i1> [#uses=1] 24 br i1 %tmp2, label %if, label %else 25 26if: ; preds = %entry 27 ret i32 0 28 29else: ; preds = %entry 30 %tmp7 = add i32 %start_arg, 1 ; <i32> [#uses=1] 31 %tmp8 = call fastcc i32 @raise_load_1(i32* %a_arg, i32 %a_len_arg, i32 %tmp7) ; <i32> [#uses=1] 32 %tmp9 = load i32, i32* %a_arg ; <i32> [#uses=1] 33 %tmp10 = add i32 %tmp9, %tmp8 ; <i32> [#uses=1] 34 ret i32 %tmp10 35} 36 37 38; This load can be moved above the call because the function won't write to it 39; and the load provably can't trap. 40define fastcc i32 @raise_load_2(i32* %a_arg, i32 %a_len_arg, i32 %start_arg) readonly { 41; CHECK-LABEL: @raise_load_2( 42; CHECK-NOT: call 43; CHECK: load i32, i32* 44; CHECK-NOT: call 45; CHECK: } 46entry: 47 %tmp2 = icmp sge i32 %start_arg, %a_len_arg ; <i1> [#uses=1] 48 br i1 %tmp2, label %if, label %else 49 50if: ; preds = %entry 51 ret i32 0 52 53else: ; preds = %entry 54 %nullcheck = icmp eq i32* %a_arg, null ; <i1> [#uses=1] 55 br i1 %nullcheck, label %unwind, label %recurse 56 57unwind: ; preds = %else 58 unreachable 59 60recurse: ; preds = %else 61 %tmp7 = add i32 %start_arg, 1 ; <i32> [#uses=1] 62 %tmp8 = call fastcc i32 @raise_load_2(i32* %a_arg, i32 %a_len_arg, i32 %tmp7) ; <i32> [#uses=1] 63 %tmp9 = load i32, i32* @global ; <i32> [#uses=1] 64 %tmp10 = add i32 %tmp9, %tmp8 ; <i32> [#uses=1] 65 ret i32 %tmp10 66} 67 68 69; This load can be safely moved above the call (even though it's from an 70; extern_weak global) because the call has no side effects. 71define fastcc i32 @raise_load_3(i32* %a_arg, i32 %a_len_arg, i32 %start_arg) nounwind readonly { 72; CHECK-LABEL: @raise_load_3( 73; CHECK-NOT: call 74; CHECK: load i32, i32* 75; CHECK-NOT: call 76; CHECK: } 77entry: 78 %tmp2 = icmp sge i32 %start_arg, %a_len_arg ; <i1> [#uses=1] 79 br i1 %tmp2, label %if, label %else 80 81if: ; preds = %entry 82 ret i32 0 83 84else: ; preds = %entry 85 %tmp7 = add i32 %start_arg, 1 ; <i32> [#uses=1] 86 %tmp8 = call fastcc i32 @raise_load_3(i32* %a_arg, i32 %a_len_arg, i32 %tmp7) ; <i32> [#uses=1] 87 %tmp9 = load i32, i32* @extern_weak_global ; <i32> [#uses=1] 88 %tmp10 = add i32 %tmp9, %tmp8 ; <i32> [#uses=1] 89 ret i32 %tmp10 90} 91 92 93; The second load can be safely moved above the call even though it's from an 94; unknown pointer (which normally means it might trap) because the first load 95; proves it doesn't trap. 96define fastcc i32 @raise_load_4(i32* %a_arg, i32 %a_len_arg, i32 %start_arg) readonly { 97; CHECK-LABEL: @raise_load_4( 98; CHECK-NOT: call 99; CHECK: load i32, i32* 100; CHECK-NEXT: load i32, i32* 101; CHECK-NOT: call 102; CHECK: } 103entry: 104 %tmp2 = icmp sge i32 %start_arg, %a_len_arg ; <i1> [#uses=1] 105 br i1 %tmp2, label %if, label %else 106 107if: ; preds = %entry 108 ret i32 0 109 110else: ; preds = %entry 111 %nullcheck = icmp eq i32* %a_arg, null ; <i1> [#uses=1] 112 br i1 %nullcheck, label %unwind, label %recurse 113 114unwind: ; preds = %else 115 unreachable 116 117recurse: ; preds = %else 118 %tmp7 = add i32 %start_arg, 1 ; <i32> [#uses=1] 119 %first = load i32, i32* %a_arg ; <i32> [#uses=1] 120 %tmp8 = call fastcc i32 @raise_load_4(i32* %a_arg, i32 %first, i32 %tmp7) ; <i32> [#uses=1] 121 %second = load i32, i32* %a_arg ; <i32> [#uses=1] 122 %tmp10 = add i32 %second, %tmp8 ; <i32> [#uses=1] 123 ret i32 %tmp10 124} 125 126; This load can be moved above the call because the function won't write to it 127; and the a_arg is dereferenceable. 128define fastcc i32 @raise_load_5(i32* dereferenceable(4) %a_arg, i32 %a_len_arg, i32 %start_arg) readonly { 129; CHECK-LABEL: @raise_load_5( 130; CHECK-NOT: call 131; CHECK: load i32, i32* 132; CHECK-NOT: call 133; CHECK: } 134entry: 135 %tmp2 = icmp sge i32 %start_arg, %a_len_arg ; <i1> [#uses=1] 136 br i1 %tmp2, label %if, label %else 137 138if: ; preds = %entry 139 ret i32 0 140 141else: ; preds = %entry 142 %tmp7 = add i32 %start_arg, 1 ; <i32> [#uses=1] 143 %tmp8 = call fastcc i32 @raise_load_5(i32* %a_arg, i32 %a_len_arg, i32 %tmp7) ; <i32> [#uses=1] 144 %tmp9 = load i32, i32* %a_arg ; <i32> [#uses=1] 145 %tmp10 = add i32 %tmp9, %tmp8 ; <i32> [#uses=1] 146 ret i32 %tmp10 147} 148