1; RUN: llvm-reduce --abort-on-invalid-reduction --delta-passes=ifuncs --test FileCheck --test-arg --check-prefixes=CHECK-INTERESTINGNESS --test-arg %s --test-arg --input-file %s -o %t 2; RUN: FileCheck --check-prefixes=CHECK-FINAL --input-file=%t %s 3 4 5; CHECK-FINAL: @initialized_with_ifunc = global ptr @ifunc_constant_initializer_user 6@initialized_with_ifunc = global ptr @ifunc_constant_initializer_user 7 8 9; CHECK-FINAL: [[TABLE:@[0-9]+]] = internal global [[[TABLE_SIZE:[0-9]+]] x ptr] poison 10; CHECK-FINAL: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 10, ptr @1, ptr null }] 11 12 13; CHECK-INTERESTINGNESS: @ifunc_kept1 = ifunc 14 15; CHECK-FINAL: @ifunc_kept1 = ifunc void (), ptr @resolver1 16@ifunc_kept1 = ifunc void (), ptr @resolver1 17@ifunc_removed2 = ifunc void (), ptr @resolver1 18 19; CHECK-INTERESTINGNESS: @ifunc_kept3 = 20; CHECK-FINAL: @ifunc_kept3 = ifunc i32 (double), ptr @resolver2 21@ifunc_kept3 = ifunc i32 (double), ptr @resolver2 22 23 24; Remove one with no users 25@ifunc4_removed = ifunc float (i64), ptr @resolver2 26 27; Keep one with no users 28; CHECK-INTERESTINGNESS: @ifunc5_kept = ifunc 29@ifunc5_kept = ifunc float (i64), ptr @resolver2 30 31 32; Make sure the hidden is preserved 33; CHECK-INTERESTINGNESS: @ifunc_kept_hidden = 34; CHECK-FINAL: @ifunc_kept_hidden = hidden ifunc i32 (double), ptr @resolver3 35@ifunc_kept_hidden = hidden ifunc i32 (double), ptr @resolver3 36@ifunc7 = ifunc float (i64), ptr @resolver3 37 38@ifunc_ptr_arg = ifunc void (ptr), ptr @resolver4 39 40 41; CHECK-INTERESTINGNESS: @ifunc_nonvoid_kept0 = ifunc 42@ifunc_nonvoid_kept0 = ifunc i32 (double), ptr @resolver5 43@ifunc_nonvoid_removed0 = ifunc i32 (double), ptr @resolver5 44 45; CHECK-INTERESTINGNESS: @ifunc_nonvoid_kept1 = ifunc 46@ifunc_nonvoid_kept1 = ifunc i32 (double), ptr @resolver5 47@ifunc_nonvoid_removed1 = ifunc i32 (double), ptr @resolver5 48 49; CHECK-FINAL: @ifunc_constant_initializer_user = ifunc i32 (double), ptr @resolver5 50@ifunc_constant_initializer_user = ifunc i32 (double), ptr @resolver5 51 52 53 54define ptr @resolver1() { 55 ret ptr inttoptr (i64 123 to ptr) 56} 57 58define ptr @resolver2() { 59 ret ptr inttoptr (i64 456 to ptr) 60} 61 62define ptr @resolver3() { 63 ret ptr inttoptr (i64 789 to ptr) 64} 65 66define ptr @resolver4() { 67 ret ptr inttoptr (i64 999 to ptr) 68} 69 70define ptr @resolver5() { 71 ret ptr inttoptr (i64 420 to ptr) 72} 73 74define void @call_ifunc_kept1() { 75 ; CHECK-FINAL-LABEL: define void @call_ifunc_kept1() { 76 ; CHECK-FINAL-NEXT: call void @ifunc_kept1() 77 ; CHECK-FINAL-NEXT: ret void 78 call void @ifunc_kept1() 79 ret void 80} 81 82; Test call to removed ifunc 83define void @call_ifunc_removed(ptr %ptr) { 84 ; CHECK-FINAL-LABEL: define void @call_ifunc_removed(ptr %ptr) 85 ; CHECK-FINAL-NEXT: %1 = load ptr, ptr @0, align 8 86 ; CHECK-FINAL-NEXT: call void %1() 87 ; CHECK-FINAL-NEXT: ret void 88 call void @ifunc_removed2() 89 ret void 90} 91 92; Test value use of removed ifunc 93define void @store_ifunc_removed2(ptr %ptr) { 94 ; CHECK-FINAL-LABEL: define void @store_ifunc_removed2(ptr %ptr) { 95 ; CHECK-FINAL-NEXT: %1 = load ptr, ptr [[TABLE]], align 8 96 ; CHECK-FINAL-NEXT: store ptr %1, ptr %ptr, align 8 97 ; CHECK-FINAL-NEXT: %2 = load ptr, ptr @0, align 8 98 ; CHECK-FINAL-NEXT: store ptr %ptr, ptr %2, align 8 99 ; CHECK-FINAL-NEXT: ret void 100 store ptr @ifunc_removed2, ptr %ptr 101 store ptr %ptr, ptr @ifunc_removed2 102 ret void 103} 104 105declare void @other_func(ptr) 106 107; Check a call user, but not as the call operand 108define void @call_ifunc_removed_is_argument(ptr %ptr) { 109 ; CHECK-FINAL-LABEL: define void @call_ifunc_removed_is_argument(ptr %ptr) { 110 ; CHECK-FINAL-NEXT: %1 = load ptr, ptr [[TABLE]], align 8 111 ; CHECK-FINAL-NEXT: call void @other_func(ptr %1) 112 ; CHECK-FINAL-NEXT: ret void 113 call void @other_func(ptr @ifunc_removed2) 114 ret void 115} 116 117; Check a call user calling the ifunc, and using the ifunc as an argument 118define void @call_ifunc_removed_both_call_argument(ptr %ptr) { 119 ; CHECK-FINAL-LABEL: define void @call_ifunc_removed_both_call_argument( 120 ; CHECK-FINAL-NEXT: %1 = load ptr, ptr getelementptr inbounds ([[[TABLE_SIZE]] x ptr], ptr [[TABLE]], i32 0, i32 3), align 8 121 ; CHECK-FINAL-NEXT: %2 = load ptr, ptr getelementptr inbounds ([[[TABLE_SIZE]] x ptr], ptr [[TABLE]], i32 0, i32 3), align 8 122 ; CHECK-FINAL-NEXT: call void %1(ptr %1) 123 ; CHECK-FINAL-NEXT: ret void 124 call void @ifunc_ptr_arg(ptr @ifunc_ptr_arg) 125 ret void 126} 127 128define i32 @call_ifunc_nonvoid(double %arg) { 129 ; CHECK-FINAL-LABEL: define i32 @call_ifunc_nonvoid(double %arg) { 130 ; CHECK-FINAL-NEXT: %ret0 = call i32 @ifunc_nonvoid_kept0(double %arg) 131 ; CHECK-FINAL-NEXT: %1 = load ptr, ptr getelementptr inbounds ([[[TABLE_SIZE]] x ptr], ptr [[TABLE]], i32 0, i32 4), align 8 132 ; CHECK-FINAL-NEXT: %ret1 = call i32 %1(double %arg) 133 ; CHECK-FINAL-NEXT: %add = add i32 %ret0, %ret1 134 ; CHECK-FINAL-NEXT: ret i32 %add 135 %ret0 = call i32 @ifunc_nonvoid_kept0(double %arg) 136 %ret1 = call i32 @ifunc_nonvoid_removed0(double %arg) 137 %add = add i32 %ret0, %ret1 138 ret i32 %add 139} 140 141; Use site is different than ifunc function type 142define float @call_different_type_ifunc_nonvoid(double %arg) { 143 ; CHECK-FINAL-LABEL: define float @call_different_type_ifunc_nonvoid(double %arg) { 144 ; CHECK-FINAL-NEXT: %cast.arg = bitcast double %arg to i64 145 ; CHECK-FINAL-NEXT: %ret0 = call float @ifunc_nonvoid_kept0(i64 %cast.arg) 146 ; CHECK-FINAL-NEXT: %1 = load ptr, ptr getelementptr inbounds ([[[TABLE_SIZE]] x ptr], ptr [[TABLE]], i32 0, i32 4), align 8 147 ; CHECK-FINAL-NEXT: %ret1 = call float %1(i64 %cast.arg) 148 ; CHECK-FINAL-NEXT: %fadd = fadd float %ret0, %ret1 149 ; CHECK-FINAL-NEXT: ret float %fadd 150 %cast.arg = bitcast double %arg to i64 151 %ret0 = call float(i64) @ifunc_nonvoid_kept0(i64 %cast.arg) 152 %ret1 = call float(i64) @ifunc_nonvoid_removed0(i64 %cast.arg) 153 %fadd = fadd float %ret0, %ret1 154 ret float %fadd 155} 156 157; FIXME: Should be able to expand this, but we miss the call 158; instruction in the constexpr cast. 159define i32 @call_addrspacecast_callee_type_ifunc_nonvoid(double %arg) { 160 ; CHECK-FINAL-LABEL: define i32 @call_addrspacecast_callee_type_ifunc_nonvoid(double %arg) { 161 ; CHECK-FINAL-NEXT: %ret0 = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_kept1 to ptr addrspace(1))(double %arg) 162 ; CHECK-FINAL-NEXT: %ret1 = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_removed1 to ptr addrspace(1))(double %arg) 163 ; CHECK-FINAL-NEXT: %add = add i32 %ret0, %ret1 164 ; CHECK-FINAL-NEXT: ret i32 %add 165 %ret0 = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_kept1 to ptr addrspace(1)) (double %arg) 166 %ret1 = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_removed1 to ptr addrspace(1)) (double %arg) 167 %add = add i32 %ret0, %ret1 168 ret i32 %add 169} 170 171define i32 @call_used_in_initializer(double %arg) { 172 ; CHECK-FINAL-LABEL: define i32 @call_used_in_initializer(double %arg) { 173 ; CHECK-FINAL-NEXT: %1 = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 7), align 8 174 ; CHECK-FINAL-NEXT: %ret = call i32 %1(double %arg) 175 ; CHECK-FINAL-NEXT: ret i32 %ret 176 %ret = call i32 @ifunc_constant_initializer_user(double %arg) 177 ret i32 %ret 178} 179 180; CHECK-FINAL-LABEL: define internal void @1() { 181; CHECK-FINAL-NEXT: %1 = call ptr @resolver1() 182; CHECK-FINAL-NEXT: store ptr %1, ptr @0, align 8 183; CHECK-FINAL-NEXT: %2 = call ptr @resolver2() 184; CHECK-FINAL-NEXT: store ptr %2, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 1), align 8 185; CHECK-FINAL-NEXT: %3 = call ptr @resolver3() 186; CHECK-FINAL-NEXT: store ptr %3, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 2), align 8 187; CHECK-FINAL-NEXT: %4 = call ptr @resolver4() 188; CHECK-FINAL-NEXT: store ptr %4, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 3), align 8 189; CHECK-FINAL-NEXT: %5 = call ptr @resolver5() 190; CHECK-FINAL-NEXT: store ptr %5, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 4), align 8 191; CHECK-FINAL-NEXT: %6 = call ptr @resolver5() 192; CHECK-FINAL-NEXT: store ptr %6, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 5), align 8 193; CHECK-FINAL-NEXT: %7 = call ptr @resolver5() 194; CHECK-FINAL-NEXT: store ptr %7, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 6), align 8 195; CHECK-FINAL-NEXT: %8 = call ptr @resolver5() 196; CHECK-FINAL-NEXT: store ptr %8, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 7), align 8 197; CHECK-FINAL-NEXT: ret void 198; CHECK-FINAL-NEXT: } 199