1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals --include-generated-funcs 2; RUN: opt -S -passes=lower-ifunc < %s | FileCheck %s 3 4@initialized_with_ifunc = global ptr @ifunc_constant_initializer_user 5 6@ifunc_1 = ifunc void (), ptr @resolver1 7@ifunc_2 = ifunc void (), ptr @resolver1 8 9 10; Keep one with no users 11@ifunc_no_users = ifunc float (i64), ptr @resolver2 12 13 14@ifunc7 = ifunc float (i64), ptr @resolver3 15@ifunc_ptr_arg = ifunc void (ptr), ptr @resolver4 16 17@ifunc_nonvoid_0 = ifunc i32 (double), ptr @resolver5 18@ifunc_nonvoid_1 = ifunc i32 (double), ptr @resolver5 19@ifunc_constant_initializer_user = ifunc i32 (double), ptr @resolver5 20 21define ptr @resolver1() { 22 ret ptr inttoptr (i64 123 to ptr) 23} 24 25define ptr @resolver2() { 26 ret ptr inttoptr (i64 456 to ptr) 27} 28 29define ptr @resolver3() { 30 ret ptr inttoptr (i64 789 to ptr) 31} 32 33define ptr @resolver4() { 34 ret ptr inttoptr (i64 999 to ptr) 35} 36 37define ptr @resolver5() { 38 ret ptr inttoptr (i64 420 to ptr) 39} 40 41 42; Test call to ifunc 43define void @call_ifunc(ptr %ptr) { 44 call void @ifunc_1() 45 call void @ifunc_2() 46 ret void 47} 48 49; Test value use of ifunc 50define void @store_ifunc_2(ptr %ptr) { 51 store ptr @ifunc_2, ptr %ptr 52 store ptr %ptr, ptr @ifunc_2 53 ret void 54} 55 56declare void @other_func(ptr) 57 58; Check a call user, but not as the call operand 59define void @call_ifunc_is_argument(ptr %ptr) { 60 call void @other_func(ptr @ifunc_2) 61 ret void 62} 63 64; Check a call user calling the ifunc, and using the ifunc as an argument 65define void @call_ifunc_both_call_argument(ptr %ptr) { 66 call void @ifunc_ptr_arg(ptr @ifunc_ptr_arg) 67 ret void 68} 69 70define i32 @call_ifunc_nonvoid(double %arg) { 71 %ret = call i32 @ifunc_nonvoid_0(double %arg) 72 ret i32 %ret 73} 74 75; Use site is different than ifunc function type 76define float @call_different_type_ifunc_nonvoid(double %arg) { 77 %cast.arg = bitcast double %arg to i64 78 %ret = call float(i64) @ifunc_nonvoid_0(i64 %cast.arg) 79 ret float %ret 80} 81 82; FIXME: Should be able to expand this, but we miss the call 83; instruction in the constexpr cast. 84define i32 @call_addrspacecast_callee_type_ifunc_nonvoid(double %arg) { 85 %ret = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_1 to ptr addrspace(1)) (double %arg) 86 ret i32 %ret 87} 88 89define i32 @call_used_in_initializer(double %arg) { 90 %ret = call i32 @ifunc_constant_initializer_user(double %arg) 91 ret i32 %ret 92} 93;. 94; CHECK: @[[INITIALIZED_WITH_IFUNC:[a-zA-Z0-9_$"\\.-]+]] = global ptr @ifunc_constant_initializer_user 95; CHECK: @[[GLOB0:[0-9]+]] = internal global [8 x ptr] poison, align 8 96; CHECK: @[[LLVM_GLOBAL_CTORS:[a-zA-Z0-9_$"\\.-]+]] = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 10, ptr @[[GLOB1:[0-9]+]], ptr null }] 97; CHECK: @[[IFUNC_NONVOID_1:[a-zA-Z0-9_$"\\.-]+]] = ifunc i32 (double), ptr @resolver5 98; CHECK: @[[IFUNC_CONSTANT_INITIALIZER_USER:[a-zA-Z0-9_$"\\.-]+]] = ifunc i32 (double), ptr @resolver5 99;. 100; CHECK-LABEL: define {{[^@]+}}@resolver1( 101; CHECK-NEXT: ret ptr inttoptr (i64 123 to ptr) 102; 103; 104; CHECK-LABEL: define {{[^@]+}}@resolver2( 105; CHECK-NEXT: ret ptr inttoptr (i64 456 to ptr) 106; 107; 108; CHECK-LABEL: define {{[^@]+}}@resolver3( 109; CHECK-NEXT: ret ptr inttoptr (i64 789 to ptr) 110; 111; 112; CHECK-LABEL: define {{[^@]+}}@resolver4( 113; CHECK-NEXT: ret ptr inttoptr (i64 999 to ptr) 114; 115; 116; CHECK-LABEL: define {{[^@]+}}@resolver5( 117; CHECK-NEXT: ret ptr inttoptr (i64 420 to ptr) 118; 119; 120; CHECK-LABEL: define {{[^@]+}}@call_ifunc( 121; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr @[[GLOB0]], align 8 122; CHECK-NEXT: call void [[TMP1]]() 123; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8 124; CHECK-NEXT: call void [[TMP2]]() 125; CHECK-NEXT: ret void 126; 127; 128; CHECK-LABEL: define {{[^@]+}}@store_ifunc_2( 129; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8 130; CHECK-NEXT: store ptr [[TMP1]], ptr [[PTR:%.*]], align 8 131; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8 132; CHECK-NEXT: store ptr [[PTR]], ptr [[TMP2]], align 8 133; CHECK-NEXT: ret void 134; 135; 136; CHECK-LABEL: define {{[^@]+}}@call_ifunc_is_argument( 137; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8 138; CHECK-NEXT: call void @other_func(ptr [[TMP1]]) 139; CHECK-NEXT: ret void 140; 141; 142; CHECK-LABEL: define {{[^@]+}}@call_ifunc_both_call_argument( 143; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 4), align 8 144; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 4), align 8 145; CHECK-NEXT: call void [[TMP1]](ptr [[TMP1]]) 146; CHECK-NEXT: ret void 147; 148; 149; CHECK-LABEL: define {{[^@]+}}@call_ifunc_nonvoid( 150; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 5), align 8 151; CHECK-NEXT: [[RET:%.*]] = call i32 [[TMP1]](double [[ARG:%.*]]) 152; CHECK-NEXT: ret i32 [[RET]] 153; 154; 155; CHECK-LABEL: define {{[^@]+}}@call_different_type_ifunc_nonvoid( 156; CHECK-NEXT: [[CAST_ARG:%.*]] = bitcast double [[ARG:%.*]] to i64 157; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 5), align 8 158; CHECK-NEXT: [[RET:%.*]] = call float [[TMP1]](i64 [[CAST_ARG]]) 159; CHECK-NEXT: ret float [[RET]] 160; 161; 162; CHECK-LABEL: define {{[^@]+}}@call_addrspacecast_callee_type_ifunc_nonvoid( 163; CHECK-NEXT: [[RET:%.*]] = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_1 to ptr addrspace(1))(double [[ARG:%.*]]) 164; CHECK-NEXT: ret i32 [[RET]] 165; 166; 167; CHECK-LABEL: define {{[^@]+}}@call_used_in_initializer( 168; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 7), align 8 169; CHECK-NEXT: [[RET:%.*]] = call i32 [[TMP1]](double [[ARG:%.*]]) 170; CHECK-NEXT: ret i32 [[RET]] 171; 172; 173; CHECK-LABEL: define {{[^@]+}}@1( 174; CHECK-NEXT: [[TMP1:%.*]] = call ptr @resolver1() 175; CHECK-NEXT: store ptr [[TMP1]], ptr @[[GLOB0]], align 8 176; CHECK-NEXT: [[TMP2:%.*]] = call ptr @resolver1() 177; CHECK-NEXT: store ptr [[TMP2]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8 178; CHECK-NEXT: [[TMP3:%.*]] = call ptr @resolver2() 179; CHECK-NEXT: store ptr [[TMP3]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 2), align 8 180; CHECK-NEXT: [[TMP4:%.*]] = call ptr @resolver3() 181; CHECK-NEXT: store ptr [[TMP4]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 3), align 8 182; CHECK-NEXT: [[TMP5:%.*]] = call ptr @resolver4() 183; CHECK-NEXT: store ptr [[TMP5]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 4), align 8 184; CHECK-NEXT: [[TMP6:%.*]] = call ptr @resolver5() 185; CHECK-NEXT: store ptr [[TMP6]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 5), align 8 186; CHECK-NEXT: [[TMP7:%.*]] = call ptr @resolver5() 187; CHECK-NEXT: store ptr [[TMP7]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 6), align 8 188; CHECK-NEXT: [[TMP8:%.*]] = call ptr @resolver5() 189; CHECK-NEXT: store ptr [[TMP8]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 7), align 8 190; CHECK-NEXT: ret void 191; 192