| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals --include-generated-funcs |
| ; RUN: opt -S -passes=lower-ifunc < %s | FileCheck %s |
| |
| @initialized_with_ifunc = global ptr @ifunc_constant_initializer_user |
| |
| @ifunc_1 = ifunc void (), ptr @resolver1 |
| @ifunc_2 = ifunc void (), ptr @resolver1 |
| |
| |
| ; Keep one with no users |
| @ifunc_no_users = ifunc float (i64), ptr @resolver2 |
| |
| |
| @ifunc7 = ifunc float (i64), ptr @resolver3 |
| @ifunc_ptr_arg = ifunc void (ptr), ptr @resolver4 |
| |
| @ifunc_nonvoid_0 = ifunc i32 (double), ptr @resolver5 |
| @ifunc_nonvoid_1 = ifunc i32 (double), ptr @resolver5 |
| @ifunc_constant_initializer_user = ifunc i32 (double), ptr @resolver5 |
| |
| define ptr @resolver1() { |
| ret ptr inttoptr (i64 123 to ptr) |
| } |
| |
| define ptr @resolver2() { |
| ret ptr inttoptr (i64 456 to ptr) |
| } |
| |
| define ptr @resolver3() { |
| ret ptr inttoptr (i64 789 to ptr) |
| } |
| |
| define ptr @resolver4() { |
| ret ptr inttoptr (i64 999 to ptr) |
| } |
| |
| define ptr @resolver5() { |
| ret ptr inttoptr (i64 420 to ptr) |
| } |
| |
| |
| ; Test call to ifunc |
| define void @call_ifunc(ptr %ptr) { |
| call void @ifunc_1() |
| call void @ifunc_2() |
| ret void |
| } |
| |
| ; Test value use of ifunc |
| define void @store_ifunc_2(ptr %ptr) { |
| store ptr @ifunc_2, ptr %ptr |
| store ptr %ptr, ptr @ifunc_2 |
| ret void |
| } |
| |
| declare void @other_func(ptr) |
| |
| ; Check a call user, but not as the call operand |
| define void @call_ifunc_is_argument(ptr %ptr) { |
| call void @other_func(ptr @ifunc_2) |
| ret void |
| } |
| |
| ; Check a call user calling the ifunc, and using the ifunc as an argument |
| define void @call_ifunc_both_call_argument(ptr %ptr) { |
| call void @ifunc_ptr_arg(ptr @ifunc_ptr_arg) |
| ret void |
| } |
| |
| define i32 @call_ifunc_nonvoid(double %arg) { |
| %ret = call i32 @ifunc_nonvoid_0(double %arg) |
| ret i32 %ret |
| } |
| |
| ; Use site is different than ifunc function type |
| define float @call_different_type_ifunc_nonvoid(double %arg) { |
| %cast.arg = bitcast double %arg to i64 |
| %ret = call float(i64) @ifunc_nonvoid_0(i64 %cast.arg) |
| ret float %ret |
| } |
| |
| ; FIXME: Should be able to expand this, but we miss the call |
| ; instruction in the constexpr cast. |
| define i32 @call_addrspacecast_callee_type_ifunc_nonvoid(double %arg) { |
| %ret = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_1 to ptr addrspace(1)) (double %arg) |
| ret i32 %ret |
| } |
| |
| define i32 @call_used_in_initializer(double %arg) { |
| %ret = call i32 @ifunc_constant_initializer_user(double %arg) |
| ret i32 %ret |
| } |
| ;. |
| ; CHECK: @[[INITIALIZED_WITH_IFUNC:[a-zA-Z0-9_$"\\.-]+]] = global ptr @ifunc_constant_initializer_user |
| ; CHECK: @[[GLOB0:[0-9]+]] = internal global [8 x ptr] poison, align 8 |
| ; 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 }] |
| ; CHECK: @[[IFUNC_NONVOID_1:[a-zA-Z0-9_$"\\.-]+]] = ifunc i32 (double), ptr @resolver5 |
| ; CHECK: @[[IFUNC_CONSTANT_INITIALIZER_USER:[a-zA-Z0-9_$"\\.-]+]] = ifunc i32 (double), ptr @resolver5 |
| ;. |
| ; CHECK-LABEL: define {{[^@]+}}@resolver1( |
| ; CHECK-NEXT: ret ptr inttoptr (i64 123 to ptr) |
| ; |
| ; |
| ; CHECK-LABEL: define {{[^@]+}}@resolver2( |
| ; CHECK-NEXT: ret ptr inttoptr (i64 456 to ptr) |
| ; |
| ; |
| ; CHECK-LABEL: define {{[^@]+}}@resolver3( |
| ; CHECK-NEXT: ret ptr inttoptr (i64 789 to ptr) |
| ; |
| ; |
| ; CHECK-LABEL: define {{[^@]+}}@resolver4( |
| ; CHECK-NEXT: ret ptr inttoptr (i64 999 to ptr) |
| ; |
| ; |
| ; CHECK-LABEL: define {{[^@]+}}@resolver5( |
| ; CHECK-NEXT: ret ptr inttoptr (i64 420 to ptr) |
| ; |
| ; |
| ; CHECK-LABEL: define {{[^@]+}}@call_ifunc( |
| ; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr @[[GLOB0]], align 8 |
| ; CHECK-NEXT: call void [[TMP1]]() |
| ; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8 |
| ; CHECK-NEXT: call void [[TMP2]]() |
| ; CHECK-NEXT: ret void |
| ; |
| ; |
| ; CHECK-LABEL: define {{[^@]+}}@store_ifunc_2( |
| ; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8 |
| ; CHECK-NEXT: store ptr [[TMP1]], ptr [[PTR:%.*]], align 8 |
| ; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8 |
| ; CHECK-NEXT: store ptr [[PTR]], ptr [[TMP2]], align 8 |
| ; CHECK-NEXT: ret void |
| ; |
| ; |
| ; CHECK-LABEL: define {{[^@]+}}@call_ifunc_is_argument( |
| ; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8 |
| ; CHECK-NEXT: call void @other_func(ptr [[TMP1]]) |
| ; CHECK-NEXT: ret void |
| ; |
| ; |
| ; CHECK-LABEL: define {{[^@]+}}@call_ifunc_both_call_argument( |
| ; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 4), align 8 |
| ; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 4), align 8 |
| ; CHECK-NEXT: call void [[TMP1]](ptr [[TMP1]]) |
| ; CHECK-NEXT: ret void |
| ; |
| ; |
| ; CHECK-LABEL: define {{[^@]+}}@call_ifunc_nonvoid( |
| ; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 5), align 8 |
| ; CHECK-NEXT: [[RET:%.*]] = call i32 [[TMP1]](double [[ARG:%.*]]) |
| ; CHECK-NEXT: ret i32 [[RET]] |
| ; |
| ; |
| ; CHECK-LABEL: define {{[^@]+}}@call_different_type_ifunc_nonvoid( |
| ; CHECK-NEXT: [[CAST_ARG:%.*]] = bitcast double [[ARG:%.*]] to i64 |
| ; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 5), align 8 |
| ; CHECK-NEXT: [[RET:%.*]] = call float [[TMP1]](i64 [[CAST_ARG]]) |
| ; CHECK-NEXT: ret float [[RET]] |
| ; |
| ; |
| ; CHECK-LABEL: define {{[^@]+}}@call_addrspacecast_callee_type_ifunc_nonvoid( |
| ; CHECK-NEXT: [[RET:%.*]] = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_1 to ptr addrspace(1))(double [[ARG:%.*]]) |
| ; CHECK-NEXT: ret i32 [[RET]] |
| ; |
| ; |
| ; CHECK-LABEL: define {{[^@]+}}@call_used_in_initializer( |
| ; CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 7), align 8 |
| ; CHECK-NEXT: [[RET:%.*]] = call i32 [[TMP1]](double [[ARG:%.*]]) |
| ; CHECK-NEXT: ret i32 [[RET]] |
| ; |
| ; |
| ; CHECK-LABEL: define {{[^@]+}}@1( |
| ; CHECK-NEXT: [[TMP1:%.*]] = call ptr @resolver1() |
| ; CHECK-NEXT: store ptr [[TMP1]], ptr @[[GLOB0]], align 8 |
| ; CHECK-NEXT: [[TMP2:%.*]] = call ptr @resolver1() |
| ; CHECK-NEXT: store ptr [[TMP2]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8 |
| ; CHECK-NEXT: [[TMP3:%.*]] = call ptr @resolver2() |
| ; CHECK-NEXT: store ptr [[TMP3]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 2), align 8 |
| ; CHECK-NEXT: [[TMP4:%.*]] = call ptr @resolver3() |
| ; CHECK-NEXT: store ptr [[TMP4]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 3), align 8 |
| ; CHECK-NEXT: [[TMP5:%.*]] = call ptr @resolver4() |
| ; CHECK-NEXT: store ptr [[TMP5]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 4), align 8 |
| ; CHECK-NEXT: [[TMP6:%.*]] = call ptr @resolver5() |
| ; CHECK-NEXT: store ptr [[TMP6]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 5), align 8 |
| ; CHECK-NEXT: [[TMP7:%.*]] = call ptr @resolver5() |
| ; CHECK-NEXT: store ptr [[TMP7]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 6), align 8 |
| ; CHECK-NEXT: [[TMP8:%.*]] = call ptr @resolver5() |
| ; CHECK-NEXT: store ptr [[TMP8]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 7), align 8 |
| ; CHECK-NEXT: ret void |
| ; |