| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| ; 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 | ||
|
|
||
| target datalayout = "P1" | ||
|
|
||
| ; Currently don't support expanding these uses. | ||
| @ifunc_used_in_constantinit_as1 = ifunc i32 (double), ptr addrspace(1) @resolver1_in_1 | ||
| @constant_init_user_addrspace1 = global ptr addrspace(1) @ifunc_used_in_constantinit_as1 | ||
|
|
||
| @ifunc_used_in_constantinit_as1_cast = ifunc i32 (double), ptr @resolver1_in_0 | ||
| @constant_init_user_addrspace1_cast = global ptr addrspace(1) addrspacecast (ptr @ifunc_used_in_constantinit_as1_cast to ptr addrspace(1)) | ||
|
|
||
| @ifunc_used_in_constantinit_as0_cast = ifunc i32 (double), ptr addrspace(1) @resolver0_in_1 | ||
| @constant_init_user_addrspace0_cast = global ptr addrspacecast (ptr addrspace(1) @ifunc_used_in_constantinit_as0_cast to ptr) | ||
|
|
||
|
|
||
| @ifunc_as1_resolver_in_0 = ifunc void (), ptr @resolver1_in_0 | ||
| @ifunc_as1_resolver_in_1 = ifunc void (), ptr addrspace(1) @resolver1_in_1 | ||
| @ifunc_as1_resolver_casted_in_1 = ifunc void (), ptr addrspace(1) addrspacecast (ptr @resolver1_in_0 to ptr addrspace(1)) | ||
|
|
||
| define ptr addrspace(1) @resolver1_in_0() addrspace(0) { | ||
| ret ptr addrspace(1) inttoptr (i64 123 to ptr addrspace(1)) | ||
| } | ||
|
|
||
| define ptr addrspace(1) @resolver1_in_1() addrspace(1) { | ||
| ret ptr addrspace(1) inttoptr (i64 456 to ptr addrspace(1)) | ||
| } | ||
|
|
||
| define ptr addrspace(0) @resolver0_in_1() addrspace(1) { | ||
| ret ptr addrspace(0) inttoptr (i64 789 to ptr addrspace(0)) | ||
| } | ||
|
|
||
| define void @call_ifuncs() addrspace(0) { | ||
| call addrspace(0) void @ifunc_as1_resolver_in_0() | ||
| call addrspace(1) void @ifunc_as1_resolver_in_1() | ||
| call addrspace(1) void @ifunc_as1_resolver_casted_in_1() | ||
| ret void | ||
| } | ||
|
|
||
| define void @load_ifuncs() addrspace(0) { | ||
| %load0 = load volatile ptr addrspace(1), ptr @constant_init_user_addrspace1 | ||
| %load1 = load volatile ptr addrspace(1), ptr @constant_init_user_addrspace1_cast | ||
| %load2 = load volatile ptr, ptr @constant_init_user_addrspace0_cast | ||
| ret void | ||
| } | ||
| ;. | ||
| ; CHECK: @[[CONSTANT_INIT_USER_ADDRSPACE1:[a-zA-Z0-9_$"\\.-]+]] = global ptr addrspace(1) @ifunc_used_in_constantinit_as1 | ||
| ; CHECK: @[[CONSTANT_INIT_USER_ADDRSPACE1_CAST:[a-zA-Z0-9_$"\\.-]+]] = global ptr addrspace(1) addrspacecast (ptr @ifunc_used_in_constantinit_as1_cast to ptr addrspace(1)) | ||
| ; CHECK: @[[CONSTANT_INIT_USER_ADDRSPACE0_CAST:[a-zA-Z0-9_$"\\.-]+]] = global ptr addrspacecast (ptr addrspace(1) @ifunc_used_in_constantinit_as0_cast to ptr) | ||
| ; CHECK: @[[GLOB0:[0-9]+]] = internal global [6 x ptr addrspace(1)] poison, align 8 | ||
| ; CHECK: @[[LLVM_GLOBAL_CTORS:[a-zA-Z0-9_$"\\.-]+]] = appending global [1 x { i32, ptr addrspace(1), ptr }] [{ i32, ptr addrspace(1), ptr } { i32 10, ptr addrspace(1) @[[GLOB1:[0-9]+]], ptr null }] | ||
| ; CHECK: @[[IFUNC_USED_IN_CONSTANTINIT_AS1:[a-zA-Z0-9_$"\\.-]+]] = ifunc i32 (double), ptr addrspace(1) @resolver1_in_1 | ||
| ; CHECK: @[[IFUNC_USED_IN_CONSTANTINIT_AS1_CAST:[a-zA-Z0-9_$"\\.-]+]] = ifunc i32 (double), ptr @resolver1_in_0 | ||
| ; CHECK: @[[IFUNC_USED_IN_CONSTANTINIT_AS0_CAST:[a-zA-Z0-9_$"\\.-]+]] = ifunc i32 (double), ptr addrspace(1) @resolver0_in_1 | ||
| ;. | ||
| ; CHECK-LABEL: define {{[^@]+}}@resolver1_in_0( | ||
| ; CHECK-NEXT: ret ptr addrspace(1) inttoptr (i64 123 to ptr addrspace(1)) | ||
| ; | ||
| ; | ||
| ; CHECK-LABEL: define {{[^@]+}}@resolver1_in_1( | ||
| ; CHECK-NEXT: ret ptr addrspace(1) inttoptr (i64 456 to ptr addrspace(1)) | ||
| ; | ||
| ; | ||
| ; CHECK-LABEL: define {{[^@]+}}@resolver0_in_1( | ||
| ; CHECK-NEXT: ret ptr inttoptr (i64 789 to ptr) | ||
| ; | ||
| ; | ||
| ; CHECK-LABEL: define {{[^@]+}}@call_ifuncs( | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = load ptr addrspace(1), ptr getelementptr inbounds ([6 x ptr addrspace(1)], ptr @[[GLOB0]], i32 0, i32 3), align 8 | ||
| ; CHECK-NEXT: [[TMP2:%.*]] = addrspacecast ptr addrspace(1) [[TMP1]] to ptr | ||
| ; CHECK-NEXT: call addrspace(0) void [[TMP2]]() | ||
| ; CHECK-NEXT: [[TMP3:%.*]] = load ptr addrspace(1), ptr getelementptr inbounds ([6 x ptr addrspace(1)], ptr @[[GLOB0]], i32 0, i32 4), align 8 | ||
| ; CHECK-NEXT: call addrspace(1) void [[TMP3]]() | ||
| ; CHECK-NEXT: [[TMP4:%.*]] = load ptr addrspace(1), ptr getelementptr inbounds ([6 x ptr addrspace(1)], ptr @[[GLOB0]], i32 0, i32 5), align 8 | ||
| ; CHECK-NEXT: call addrspace(1) void [[TMP4]]() | ||
| ; CHECK-NEXT: ret void | ||
| ; | ||
| ; | ||
| ; CHECK-LABEL: define {{[^@]+}}@load_ifuncs( | ||
| ; CHECK-NEXT: [[LOAD0:%.*]] = load volatile ptr addrspace(1), ptr @constant_init_user_addrspace1, align 8 | ||
| ; CHECK-NEXT: [[LOAD1:%.*]] = load volatile ptr addrspace(1), ptr @constant_init_user_addrspace1_cast, align 8 | ||
| ; CHECK-NEXT: [[LOAD2:%.*]] = load volatile ptr, ptr @constant_init_user_addrspace0_cast, align 8 | ||
| ; CHECK-NEXT: ret void | ||
| ; | ||
| ; | ||
| ; CHECK-LABEL: define {{[^@]+}}@1( | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = call addrspace(1) ptr addrspace(1) @resolver1_in_1() | ||
| ; CHECK-NEXT: store ptr addrspace(1) [[TMP1]], ptr @[[GLOB0]], align 8 | ||
| ; CHECK-NEXT: [[TMP2:%.*]] = call addrspace(0) ptr addrspace(1) @resolver1_in_0() | ||
| ; CHECK-NEXT: store ptr addrspace(1) [[TMP2]], ptr getelementptr inbounds ([6 x ptr addrspace(1)], ptr @[[GLOB0]], i32 0, i32 1), align 8 | ||
| ; CHECK-NEXT: [[TMP3:%.*]] = call addrspace(1) ptr @resolver0_in_1() | ||
| ; CHECK-NEXT: [[TMP4:%.*]] = addrspacecast ptr [[TMP3]] to ptr addrspace(1) | ||
| ; CHECK-NEXT: store ptr addrspace(1) [[TMP4]], ptr getelementptr inbounds ([6 x ptr addrspace(1)], ptr @[[GLOB0]], i32 0, i32 2), align 8 | ||
| ; CHECK-NEXT: [[TMP5:%.*]] = call addrspace(0) ptr addrspace(1) @resolver1_in_0() | ||
| ; CHECK-NEXT: store ptr addrspace(1) [[TMP5]], ptr getelementptr inbounds ([6 x ptr addrspace(1)], ptr @[[GLOB0]], i32 0, i32 3), align 8 | ||
| ; CHECK-NEXT: [[TMP6:%.*]] = call addrspace(1) ptr addrspace(1) @resolver1_in_1() | ||
| ; CHECK-NEXT: store ptr addrspace(1) [[TMP6]], ptr getelementptr inbounds ([6 x ptr addrspace(1)], ptr @[[GLOB0]], i32 0, i32 4), align 8 | ||
| ; CHECK-NEXT: [[TMP7:%.*]] = call addrspace(0) ptr addrspace(1) @resolver1_in_0() | ||
| ; CHECK-NEXT: store ptr addrspace(1) [[TMP7]], ptr getelementptr inbounds ([6 x ptr addrspace(1)], ptr @[[GLOB0]], i32 0, i32 5), align 8 | ||
| ; CHECK-NEXT: ret void | ||
| ; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| ; 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 | ||
|
|
||
| ; Check that typed pointers still work | ||
|
|
||
| @ifunc0 = ifunc i32(i32), i32(i32)* ()* @func0 | ||
|
|
||
| define i32 (i32)* @func0() { | ||
| ret i32 (i32)* null | ||
| } | ||
|
|
||
| define i32 (i64)* @func1() { | ||
| ret i32 (i64)* null | ||
| } | ||
|
|
||
| @ifunc1 = ifunc float(double), float(double)* ()* bitcast (i32(i64)* ()* @func1 to float(double)* ()*) | ||
|
|
||
| define i32 @call_ifuncs() { | ||
| %call0 = call i32 @ifunc0(i32 123) | ||
| %call1 = call i32 bitcast (float(double)* @ifunc1 to i32()*)() | ||
| %add = add i32 %call0, %call1 | ||
| ret i32 %add | ||
| } | ||
| ;. | ||
| ; CHECK: @[[GLOB0:[0-9]+]] = internal global [2 x i8*] poison, align 8 | ||
| ; CHECK: @[[LLVM_GLOBAL_CTORS:[a-zA-Z0-9_$"\\.-]+]] = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 10, void ()* @[[GLOB1:[0-9]+]], i8* null }] | ||
| ; CHECK: @[[IFUNC1:[a-zA-Z0-9_$"\\.-]+]] = ifunc float (double), bitcast (i32 (i64)* ()* @func1 to float (double)* ()*) | ||
| ;. | ||
| ; CHECK-LABEL: define {{[^@]+}}@func0( | ||
| ; CHECK-NEXT: ret i32 (i32)* null | ||
| ; | ||
| ; | ||
| ; CHECK-LABEL: define {{[^@]+}}@func1( | ||
| ; CHECK-NEXT: ret i32 (i64)* null | ||
| ; | ||
| ; | ||
| ; CHECK-LABEL: define {{[^@]+}}@call_ifuncs( | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = load i8*, i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @[[GLOB0]], i32 0, i32 0), align 8 | ||
| ; CHECK-NEXT: [[TMP2:%.*]] = bitcast i8* [[TMP1]] to i32 (i32)* | ||
| ; CHECK-NEXT: [[CALL0:%.*]] = call i32 [[TMP2]](i32 123) | ||
| ; CHECK-NEXT: [[CALL1:%.*]] = call i32 bitcast (float (double)* @ifunc1 to i32 ()*)() | ||
| ; CHECK-NEXT: [[ADD:%.*]] = add i32 [[CALL0]], [[CALL1]] | ||
| ; CHECK-NEXT: ret i32 [[ADD]] | ||
| ; | ||
| ; | ||
| ; CHECK-LABEL: define {{[^@]+}}@1( | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = call i32 (i32)* @func0() | ||
| ; CHECK-NEXT: [[TMP2:%.*]] = bitcast i32 (i32)* [[TMP1]] to i8* | ||
| ; CHECK-NEXT: store i8* [[TMP2]], i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @[[GLOB0]], i32 0, i32 0), align 8 | ||
| ; CHECK-NEXT: [[TMP3:%.*]] = call i32 (i64)* @func1() | ||
| ; CHECK-NEXT: [[TMP4:%.*]] = bitcast i32 (i64)* [[TMP3]] to i8* | ||
| ; CHECK-NEXT: store i8* [[TMP4]], i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @[[GLOB0]], i32 0, i32 1), align 8 | ||
| ; CHECK-NEXT: ret void | ||
| ; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| ; 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 | ||
| ; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| ; 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.0 | ||
| ; RUN: FileCheck --check-prefixes=CHECK-FINAL-IFUNCS,ALL --input-file=%t.0 %s | ||
|
|
||
| ; RUN: llvm-reduce --abort-on-invalid-reduction --delta-passes=aliases,ifuncs --test FileCheck --test-arg --check-prefixes=CHECK-INTERESTINGNESS --test-arg %s --test-arg --input-file %s -o %t.1 | ||
| ; RUN: FileCheck --check-prefixes=CHECK-FINAL-BOTH,ALL --input-file=%t.1 %s | ||
|
|
||
| ; Check interaction of reductions between aliases and ifuncs | ||
|
|
||
| ; Test ifunc to alias | ||
| ; CHECK-INTERESTINGNESS: @ifunc0_kept = | ||
|
|
||
|
|
||
| ; ALL: [[TABLE:@[0-9]+]] = internal global [2 x ptr] poison | ||
| ; ALL: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 10, ptr [[CONSTRUCTOR:@[0-9]+]], ptr null }] | ||
|
|
||
|
|
||
| ; CHECK-FINAL-IFUNCS: @resolver_alias = alias ptr (), ptr @resolver | ||
| ; CHECK-FINAL-IFUNCS: @ifunc_alias = alias ptr (), ptr @resolver_alias | ||
| ; CHECK-FINAL-IFUNCS: @alias_of_ifunc = alias float (i64), ptr @ifunc_def | ||
|
|
||
| ; CHECK-FINAL-IFUNCS: @ifunc0_kept = ifunc float (i64), ptr @resolver_alias | ||
| ; CHECK-FINAL-IFUNCS: @ifunc_def = ifunc float (i64), ptr @resolver | ||
|
|
||
|
|
||
| ; CHECK-FINAL-BOTH-NOT: _alias | ||
| ; CHECK-FINAL-BOTH-NOT: @ifunc | ||
| ; CHECK-FINAL-BOTH: @ifunc0_kept = ifunc float (i64), ptr @resolver | ||
| ; CHECK-FINAL-BOTH-NOT: _alias | ||
| ; CHECK-FINAL-BOTH-NOT: @ifunc | ||
| define ptr @resolver() { | ||
| ret ptr inttoptr (i64 333 to ptr) | ||
| } | ||
|
|
||
| @resolver_alias = alias ptr (), ptr @resolver | ||
| @ifunc_alias = alias ptr (), ptr @resolver_alias | ||
|
|
||
| @ifunc0_kept = ifunc float (i64), ptr @resolver_alias | ||
| @ifunc1_removed = ifunc float (i64), ptr @resolver_alias | ||
|
|
||
| @ifunc_def = ifunc float (i64), ptr @resolver | ||
| @alias_of_ifunc = alias float (i64), ptr @ifunc_def | ||
|
|
||
| ; ALL-LABEL: define float @call_ifunc_aliasee(i64 %arg) { | ||
| ; ALL: %1 = load ptr, ptr [[TABLE]], align 8 | ||
| ; ALL: %call = call float %1(i64 %arg) | ||
| ; ALL: ret float %call | ||
| define float @call_ifunc_aliasee(i64 %arg) { | ||
| %call = call float @ifunc1_removed(i64 %arg) | ||
| ret float %call | ||
| } | ||
|
|
||
| ; ALL-LABEL: @call_alias_of_ifunc( | ||
| ; CHECK-FINAL-IFUNCS: call float @alias_of_ifunc( | ||
|
|
||
| ; CHECK-FINAL-BOTH-NEXT: %1 = load ptr, ptr getelementptr inbounds ([2 x ptr], ptr [[TABLE]], i32 0, i32 1), align 8 | ||
| ; CHECK-FINAL-BOTH-NEXT: %call = call float %1(i64 %arg) | ||
| ; CHECK-FINAL-BOTH-NEXT: ret float %call | ||
| define float @call_alias_of_ifunc(i64 %arg) { | ||
| %call = call float @alias_of_ifunc(i64 %arg) | ||
| ret float %call | ||
| } | ||
|
|
||
| ; CHECK-FINAL-BOTH: define internal void [[CONSTRUCTOR]]() { | ||
| ; CHECK-FINAL-BOTH-NEXT: %1 = call ptr @resolver() | ||
| ; CHECK-FINAL-BOTH-NEXT: store ptr %1, ptr [[TABLE]], align 8 | ||
| ; CHECK-FINAL-BOTH-NEXT: %2 = call ptr @resolver() | ||
| ; CHECK-FINAL-BOTH-NEXT: store ptr %2, ptr getelementptr inbounds ([2 x ptr], ptr [[TABLE]], i32 0, i32 1), align 8 | ||
| ; CHECK-FINAL-BOTH-NEXT: ret void |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| ; 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 | ||
| ; RUN: FileCheck --check-prefixes=CHECK-FINAL --input-file=%t %s | ||
|
|
||
| ; Check some cases that should probably be invalid IR don't break | ||
| ; anything. | ||
|
|
||
| ; CHECK-FINAL: @ifunc_with_arg = ifunc void (), ptr @resolver_with_arg | ||
| @ifunc_with_arg = ifunc void (), ptr @resolver_with_arg | ||
|
|
||
| define ptr @resolver_with_arg(i64 %arg) { | ||
| %cast = inttoptr i64 %arg to ptr | ||
| ret ptr %cast | ||
| } | ||
|
|
||
| ; CHECK-INTERESTINGNESS: define void @call_with_arg() | ||
| define void @call_with_arg() { | ||
| ; CHECK-FINAL: define void @call_with_arg() { | ||
| ; CHECK-FINAL-NEXT: call void @ifunc_with_arg() | ||
| ; CHECK-FINAL-NEXT: ret void | ||
| call void @ifunc_with_arg() | ||
| ret void | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| ; XFAIL: * | ||
| ; The verifier should xeject this | ||
| ; 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 | ||
| ; RUN: FileCheck --check-prefixes=CHECK-FINAL --input-file=%t %s | ||
|
|
||
| ; CHECK-INTERESTINGNESS: @ifunc_getelementptr | ||
|
|
||
| ; FIXME: Why is this legal? | ||
| @ifunc_getelementptr = ifunc void (), ptr getelementptr (i8, ptr @resolver1, i32 4) | ||
|
|
||
| define ptr @resolver1() { | ||
| ret ptr inttoptr (i64 123 to ptr) | ||
| } | ||
|
|
||
| define void @call_ifunc_getelementptr(ptr %ptr) { | ||
| ; CHECK-FINAL-LABEL: define void @call_ifunc_getelementptr(ptr %ptr) { | ||
| ; CHECK-FINAL-NEXT: call void @ifunc_getelementptr() | ||
| ; CHECK-FINAL-NEXT: store ptr @ifunc_getelementptr, ptr %ptr, align 8 | ||
| ; CHECK-FINAL-NEXT: store ptr %ptr, ptr @ifunc_getelementptr, align 8 | ||
| ; CHECK-FINAL-NEXT: ret void | ||
| call void @ifunc_getelementptr() | ||
| store ptr @ifunc_getelementptr, ptr %ptr | ||
| store ptr %ptr, ptr @ifunc_getelementptr | ||
| ret void | ||
| } | ||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| ; 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 | ||
| ; RUN: FileCheck -implicit-check-not=ifunc_remove --check-prefixes=CHECK-FINAL --input-file=%t %s | ||
|
|
||
| target datalayout = "P1-G2" | ||
|
|
||
| define void @existing_ctor() addrspace(1) { | ||
| ret void | ||
| } | ||
|
|
||
| ; Currently don't support expanding these uses. | ||
| ; CHECK-FINAL: @constant_init_user_addrspace1 = global ptr addrspace(1) @ifunc_remove_used_in_constantinit_as1 | ||
| ; CHECK-FINAL: @constant_init_user_addrspace1_cast = global ptr addrspace(1) addrspacecast (ptr @ifunc_remove_used_in_constantinit_as1_cast to ptr addrspace(1)) | ||
| ; CHECK-FINAL: @constant_init_user_addrspace0_cast = global ptr addrspacecast (ptr addrspace(1) @ifunc_remove_used_in_constantinit_as0_cast to ptr) | ||
|
|
||
|
|
||
| ; CHECK-FINAL: [[TABLE:@[0-9]+]] = internal addrspace(2) global [6 x ptr addrspace(1)] poison, align 8 | ||
|
|
||
| ; CHECK-FINAL: @llvm.global_ctors = appending addrspace(2) global [2 x { i32, ptr addrspace(1), ptr }] [{ i32, ptr addrspace(1), ptr } { i32 0, ptr addrspace(1) @existing_ctor, ptr null }, { i32, ptr addrspace(1), ptr } { i32 10, ptr addrspace(1) [[TABLE_CTOR:@[0-9]+]], ptr null }] | ||
| @llvm.global_ctors = appending global [1 x { i32, ptr addrspace(1), ptr }] [{ i32, ptr addrspace(1), ptr } { i32 0, ptr addrspace(1) @existing_ctor, ptr null }] | ||
|
|
||
|
|
||
|
|
||
| ; CHECK-FINAL: @ifunc_remove_used_in_constantinit_as1 = ifunc i32 (double), ptr addrspace(1) @resolver1_in_1 | ||
| ; CHECK-FINAL: @ifunc_remove_used_in_constantinit_as1_cast = ifunc i32 (double), ptr @resolver1_in_0 | ||
| ; CHECK-FINAL: @ifunc_remove_used_in_constantinit_as0_cast = ifunc i32 (double), ptr addrspace(1) @resolver0_in_1 | ||
|
|
||
| @ifunc_remove_used_in_constantinit_as1 = ifunc i32 (double), ptr addrspace(1) @resolver1_in_1 | ||
| @constant_init_user_addrspace1 = global ptr addrspace(1) @ifunc_remove_used_in_constantinit_as1 | ||
|
|
||
| @ifunc_remove_used_in_constantinit_as1_cast = ifunc i32 (double), ptr @resolver1_in_0 | ||
| @constant_init_user_addrspace1_cast = global ptr addrspace(1) addrspacecast (ptr @ifunc_remove_used_in_constantinit_as1_cast to ptr addrspace(1)) | ||
|
|
||
| @ifunc_remove_used_in_constantinit_as0_cast = ifunc i32 (double), ptr addrspace(1) @resolver0_in_1 | ||
| @constant_init_user_addrspace0_cast = global ptr addrspacecast (ptr addrspace(1) @ifunc_remove_used_in_constantinit_as0_cast to ptr) | ||
|
|
||
|
|
||
| ; CHECK-INTERESTINGNESS: @ifunc_keep_as1_resolver_in_0 | ||
| ; CHECK-FINAL: @ifunc_keep_as1_resolver_in_0 = ifunc void (), ptr @resolver1_in_0 | ||
| @ifunc_keep_as1_resolver_in_0 = ifunc void (), ptr @resolver1_in_0 | ||
| @ifunc_remove_as1_resolver_in_0 = ifunc void (), ptr @resolver1_in_0 | ||
|
|
||
| ; CHECK-INTERESTINGNESS: @ifunc_keep_as1_resolver_in_1 | ||
| ; CHECK-FINAL: @ifunc_keep_as1_resolver_in_1 = ifunc void (), ptr addrspace(1) @resolver1_in_1 | ||
| @ifunc_keep_as1_resolver_in_1 = ifunc void (), ptr addrspace(1) @resolver1_in_1 | ||
| @ifunc_remove_as1_resolver_in_1 = ifunc void (), ptr addrspace(1) @resolver1_in_1 | ||
|
|
||
| ; CHECK-INTERESTINGNESS: @ifunc_keep_as1_resolver_casted_in_1 | ||
| ; CHECK-FINAL: @ifunc_keep_as1_resolver_casted_in_1 = ifunc void (), addrspacecast (ptr @resolver1_in_0 to ptr addrspace(1)) | ||
| @ifunc_keep_as1_resolver_casted_in_1 = ifunc void (), ptr addrspace(1) addrspacecast (ptr @resolver1_in_0 to ptr addrspace(1)) | ||
| @ifunc_remove_as1_resolver_casted_in_1 = ifunc void (), ptr addrspace(1) addrspacecast (ptr @resolver1_in_0 to ptr addrspace(1)) | ||
|
|
||
|
|
||
| define ptr addrspace(1) @resolver1_in_0() addrspace(0) { | ||
| ret ptr addrspace(1) inttoptr (i64 123 to ptr addrspace(1)) | ||
| } | ||
|
|
||
| define ptr addrspace(1) @resolver1_in_1() addrspace(1) { | ||
| ret ptr addrspace(1) inttoptr (i64 456 to ptr addrspace(1)) | ||
| } | ||
|
|
||
| define ptr addrspace(0) @resolver0_in_1() addrspace(1) { | ||
| ret ptr addrspace(0) inttoptr (i64 789 to ptr addrspace(0)) | ||
| } | ||
|
|
||
| define void @call_removed() addrspace(0) { | ||
| ; CHECK-FINAL-LABEL: @call_removed( | ||
| ; CHECK-FINAL-NEXT: %1 = load ptr addrspace(1), ptr addrspace(2) getelementptr inbounds ([6 x ptr addrspace(1)], ptr addrspace(2) [[TABLE]], i32 0, i32 3), align 8 | ||
| ; CHECK-FINAL-NEXT: %2 = addrspacecast ptr addrspace(1) %1 to ptr | ||
| ; CHECK-FINAL-NEXT: call addrspace(0) void %2() | ||
| ; CHECK-FINAL-NEXT: %3 = load ptr addrspace(1), ptr addrspace(2) getelementptr inbounds ([6 x ptr addrspace(1)], ptr addrspace(2) [[TABLE]], i32 0, i32 4), align 8 | ||
| ; CHECK-FINAL-NEXT: call addrspace(1) void %3() | ||
| ; CHECK-FINAL-NEXT: %4 = load ptr addrspace(1), ptr addrspace(2) getelementptr inbounds ([6 x ptr addrspace(1)], ptr addrspace(2) [[TABLE]], i32 0, i32 5), align 8 | ||
| ; CHECK-FINAL-NEXT: call addrspace(1) void %4() | ||
| ; CHECK-FINAL-NEXT: ret void | ||
| call addrspace(0) void @ifunc_remove_as1_resolver_in_0() | ||
| call addrspace(1) void @ifunc_remove_as1_resolver_in_1() | ||
| call addrspace(1) void @ifunc_remove_as1_resolver_casted_in_1() | ||
| ret void | ||
| } | ||
|
|
||
| define void @load_removed() addrspace(0) { | ||
| ; CHECK-FINAL-LABEL: define void @load_removed( | ||
| ; CHECK-FINAL-NEXT: %load0 = load volatile ptr addrspace(1), ptr @constant_init_user_addrspace1, align 8 | ||
| ; CHECK-FINAL-NEXT: %load1 = load volatile ptr addrspace(1), ptr @constant_init_user_addrspace1_cast, align 8 | ||
| ; CHECK-FINAL-NEXT: %load2 = load volatile ptr, ptr @constant_init_user_addrspace0_cast, align 8 | ||
| ; CHECK-FINAL-NEXT: ret void | ||
| %load0 = load volatile ptr addrspace(1), ptr @constant_init_user_addrspace1 | ||
| %load1 = load volatile ptr addrspace(1), ptr @constant_init_user_addrspace1_cast | ||
| %load2 = load volatile ptr, ptr @constant_init_user_addrspace0_cast | ||
| ret void | ||
| } | ||
|
|
||
| ; CHECK-FINAL: define internal void [[TABLE_CTOR]]() addrspace(1) { | ||
| ; CHECK-FINAL-NEXT: %1 = call addrspace(1) ptr addrspace(1) @resolver1_in_1() | ||
| ; CHECK-FINAL-NEXT: store ptr addrspace(1) %1, ptr addrspace(2) [[TABLE]], align 8 | ||
| ; CHECK-FINAL-NEXT: %2 = call addrspace(0) ptr addrspace(1) @resolver1_in_0() | ||
| ; CHECK-FINAL-NEXT: store ptr addrspace(1) %2, ptr addrspace(2) getelementptr inbounds ([6 x ptr addrspace(1)], ptr addrspace(2) [[TABLE]], i32 0, i32 1), align 8 | ||
| ; CHECK-FINAL-NEXT: %3 = call addrspace(1) ptr @resolver0_in_1() | ||
| ; CHECK-FINAL-NEXT: %4 = addrspacecast ptr %3 to ptr addrspace(1) | ||
| ; CHECK-FINAL-NEXT: store ptr addrspace(1) %4, ptr addrspace(2) getelementptr inbounds ([6 x ptr addrspace(1)], ptr addrspace(2) [[TABLE]], i32 0, i32 2), align 8 | ||
| ; CHECK-FINAL-NEXT: %5 = call addrspace(0) ptr addrspace(1) @resolver1_in_0() | ||
| ; CHECK-FINAL-NEXT: store ptr addrspace(1) %5, ptr addrspace(2) getelementptr inbounds ([6 x ptr addrspace(1)], ptr addrspace(2) [[TABLE]], i32 0, i32 3), align 8 | ||
| ; CHECK-FINAL-NEXT: %6 = call addrspace(1) ptr addrspace(1) @resolver1_in_1() | ||
| ; CHECK-FINAL-NEXT: store ptr addrspace(1) %6, ptr addrspace(2) getelementptr inbounds ([6 x ptr addrspace(1)], ptr addrspace(2) [[TABLE]], i32 0, i32 4), align 8 | ||
| ; CHECK-FINAL-NEXT: %7 = call addrspace(0) ptr addrspace(1) @resolver1_in_0() | ||
| ; CHECK-FINAL-NEXT: store ptr addrspace(1) %7, ptr addrspace(2) getelementptr inbounds ([6 x ptr addrspace(1)], ptr addrspace(2) [[TABLE]], i32 0, i32 5), align 8 | ||
| ; CHECK-FINAL-NEXT: ret void |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| ; 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 | ||
| ; RUN: FileCheck --check-prefixes=CHECK-FINAL --input-file=%t %s | ||
|
|
||
|
|
||
| ; CHECK-FINAL: @initialized_with_ifunc = global ptr @ifunc_constant_initializer_user | ||
| @initialized_with_ifunc = global ptr @ifunc_constant_initializer_user | ||
|
|
||
|
|
||
| ; CHECK-FINAL: [[TABLE:@[0-9]+]] = internal global [[[TABLE_SIZE:[0-9]+]] x ptr] poison | ||
| ; CHECK-FINAL: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 10, ptr @1, ptr null }] | ||
|
|
||
|
|
||
| ; CHECK-INTERESTINGNESS: @ifunc_kept1 = ifunc | ||
|
|
||
| ; CHECK-FINAL: @ifunc_kept1 = ifunc void (), ptr @resolver1 | ||
| @ifunc_kept1 = ifunc void (), ptr @resolver1 | ||
| @ifunc_removed2 = ifunc void (), ptr @resolver1 | ||
|
|
||
| ; CHECK-INTERESTINGNESS: @ifunc_kept3 = | ||
| ; CHECK-FINAL: @ifunc_kept3 = ifunc i32 (double), ptr @resolver2 | ||
| @ifunc_kept3 = ifunc i32 (double), ptr @resolver2 | ||
|
|
||
|
|
||
| ; Remove one with no users | ||
| @ifunc4_removed = ifunc float (i64), ptr @resolver2 | ||
|
|
||
| ; Keep one with no users | ||
| ; CHECK-INTERESTINGNESS: @ifunc5_kept = ifunc | ||
| @ifunc5_kept = ifunc float (i64), ptr @resolver2 | ||
|
|
||
|
|
||
| ; Make sure the hidden is preserved | ||
| ; CHECK-INTERESTINGNESS: @ifunc_kept_hidden = | ||
| ; CHECK-FINAL: @ifunc_kept_hidden = hidden ifunc i32 (double), ptr @resolver3 | ||
| @ifunc_kept_hidden = hidden ifunc i32 (double), ptr @resolver3 | ||
| @ifunc7 = ifunc float (i64), ptr @resolver3 | ||
|
|
||
| @ifunc_ptr_arg = ifunc void (ptr), ptr @resolver4 | ||
|
|
||
|
|
||
| ; CHECK-INTERESTINGNESS: @ifunc_nonvoid_kept0 = ifunc | ||
| @ifunc_nonvoid_kept0 = ifunc i32 (double), ptr @resolver5 | ||
| @ifunc_nonvoid_removed0 = ifunc i32 (double), ptr @resolver5 | ||
|
|
||
| ; CHECK-INTERESTINGNESS: @ifunc_nonvoid_kept1 = ifunc | ||
| @ifunc_nonvoid_kept1 = ifunc i32 (double), ptr @resolver5 | ||
| @ifunc_nonvoid_removed1 = ifunc i32 (double), ptr @resolver5 | ||
|
|
||
| ; CHECK-FINAL: @ifunc_constant_initializer_user = 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) | ||
| } | ||
|
|
||
| define void @call_ifunc_kept1() { | ||
| ; CHECK-FINAL-LABEL: define void @call_ifunc_kept1() { | ||
| ; CHECK-FINAL-NEXT: call void @ifunc_kept1() | ||
| ; CHECK-FINAL-NEXT: ret void | ||
| call void @ifunc_kept1() | ||
| ret void | ||
| } | ||
|
|
||
| ; Test call to removed ifunc | ||
| define void @call_ifunc_removed(ptr %ptr) { | ||
| ; CHECK-FINAL-LABEL: define void @call_ifunc_removed(ptr %ptr) | ||
| ; CHECK-FINAL-NEXT: %1 = load ptr, ptr @0, align 8 | ||
| ; CHECK-FINAL-NEXT: call void %1() | ||
| ; CHECK-FINAL-NEXT: ret void | ||
| call void @ifunc_removed2() | ||
| ret void | ||
| } | ||
|
|
||
| ; Test value use of removed ifunc | ||
| define void @store_ifunc_removed2(ptr %ptr) { | ||
| ; CHECK-FINAL-LABEL: define void @store_ifunc_removed2(ptr %ptr) { | ||
| ; CHECK-FINAL-NEXT: %1 = load ptr, ptr [[TABLE]], align 8 | ||
| ; CHECK-FINAL-NEXT: store ptr %1, ptr %ptr, align 8 | ||
| ; CHECK-FINAL-NEXT: %2 = load ptr, ptr @0, align 8 | ||
| ; CHECK-FINAL-NEXT: store ptr %ptr, ptr %2, align 8 | ||
| ; CHECK-FINAL-NEXT: ret void | ||
| store ptr @ifunc_removed2, ptr %ptr | ||
| store ptr %ptr, ptr @ifunc_removed2 | ||
| ret void | ||
| } | ||
|
|
||
| declare void @other_func(ptr) | ||
|
|
||
| ; Check a call user, but not as the call operand | ||
| define void @call_ifunc_removed_is_argument(ptr %ptr) { | ||
| ; CHECK-FINAL-LABEL: define void @call_ifunc_removed_is_argument(ptr %ptr) { | ||
| ; CHECK-FINAL-NEXT: %1 = load ptr, ptr [[TABLE]], align 8 | ||
| ; CHECK-FINAL-NEXT: call void @other_func(ptr %1) | ||
| ; CHECK-FINAL-NEXT: ret void | ||
| call void @other_func(ptr @ifunc_removed2) | ||
| ret void | ||
| } | ||
|
|
||
| ; Check a call user calling the ifunc, and using the ifunc as an argument | ||
| define void @call_ifunc_removed_both_call_argument(ptr %ptr) { | ||
| ; CHECK-FINAL-LABEL: define void @call_ifunc_removed_both_call_argument( | ||
| ; CHECK-FINAL-NEXT: %1 = load ptr, ptr getelementptr inbounds ([[[TABLE_SIZE]] x ptr], ptr [[TABLE]], i32 0, i32 3), align 8 | ||
| ; CHECK-FINAL-NEXT: %2 = load ptr, ptr getelementptr inbounds ([[[TABLE_SIZE]] x ptr], ptr [[TABLE]], i32 0, i32 3), align 8 | ||
| ; CHECK-FINAL-NEXT: call void %1(ptr %1) | ||
| ; CHECK-FINAL-NEXT: ret void | ||
| call void @ifunc_ptr_arg(ptr @ifunc_ptr_arg) | ||
| ret void | ||
| } | ||
|
|
||
| define i32 @call_ifunc_nonvoid(double %arg) { | ||
| ; CHECK-FINAL-LABEL: define i32 @call_ifunc_nonvoid(double %arg) { | ||
| ; CHECK-FINAL-NEXT: %ret0 = call i32 @ifunc_nonvoid_kept0(double %arg) | ||
| ; CHECK-FINAL-NEXT: %1 = load ptr, ptr getelementptr inbounds ([[[TABLE_SIZE]] x ptr], ptr [[TABLE]], i32 0, i32 4), align 8 | ||
| ; CHECK-FINAL-NEXT: %ret1 = call i32 %1(double %arg) | ||
| ; CHECK-FINAL-NEXT: %add = add i32 %ret0, %ret1 | ||
| ; CHECK-FINAL-NEXT: ret i32 %add | ||
| %ret0 = call i32 @ifunc_nonvoid_kept0(double %arg) | ||
| %ret1 = call i32 @ifunc_nonvoid_removed0(double %arg) | ||
| %add = add i32 %ret0, %ret1 | ||
| ret i32 %add | ||
| } | ||
|
|
||
| ; Use site is different than ifunc function type | ||
| define float @call_different_type_ifunc_nonvoid(double %arg) { | ||
| ; CHECK-FINAL-LABEL: define float @call_different_type_ifunc_nonvoid(double %arg) { | ||
| ; CHECK-FINAL-NEXT: %cast.arg = bitcast double %arg to i64 | ||
| ; CHECK-FINAL-NEXT: %ret0 = call float @ifunc_nonvoid_kept0(i64 %cast.arg) | ||
| ; CHECK-FINAL-NEXT: %1 = load ptr, ptr getelementptr inbounds ([[[TABLE_SIZE]] x ptr], ptr [[TABLE]], i32 0, i32 4), align 8 | ||
| ; CHECK-FINAL-NEXT: %ret1 = call float %1(i64 %cast.arg) | ||
| ; CHECK-FINAL-NEXT: %fadd = fadd float %ret0, %ret1 | ||
| ; CHECK-FINAL-NEXT: ret float %fadd | ||
| %cast.arg = bitcast double %arg to i64 | ||
| %ret0 = call float(i64) @ifunc_nonvoid_kept0(i64 %cast.arg) | ||
| %ret1 = call float(i64) @ifunc_nonvoid_removed0(i64 %cast.arg) | ||
| %fadd = fadd float %ret0, %ret1 | ||
| ret float %fadd | ||
| } | ||
|
|
||
| ; 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) { | ||
| ; CHECK-FINAL-LABEL: define i32 @call_addrspacecast_callee_type_ifunc_nonvoid(double %arg) { | ||
| ; CHECK-FINAL-NEXT: %ret0 = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_kept1 to ptr addrspace(1))(double %arg) | ||
| ; CHECK-FINAL-NEXT: %ret1 = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_removed1 to ptr addrspace(1))(double %arg) | ||
| ; CHECK-FINAL-NEXT: %add = add i32 %ret0, %ret1 | ||
| ; CHECK-FINAL-NEXT: ret i32 %add | ||
| %ret0 = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_kept1 to ptr addrspace(1)) (double %arg) | ||
| %ret1 = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_removed1 to ptr addrspace(1)) (double %arg) | ||
| %add = add i32 %ret0, %ret1 | ||
| ret i32 %add | ||
| } | ||
|
|
||
| define i32 @call_used_in_initializer(double %arg) { | ||
| ; CHECK-FINAL-LABEL: define i32 @call_used_in_initializer(double %arg) { | ||
| ; CHECK-FINAL-NEXT: %1 = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 7), align 8 | ||
| ; CHECK-FINAL-NEXT: %ret = call i32 %1(double %arg) | ||
| ; CHECK-FINAL-NEXT: ret i32 %ret | ||
| %ret = call i32 @ifunc_constant_initializer_user(double %arg) | ||
| ret i32 %ret | ||
| } | ||
|
|
||
| ; CHECK-FINAL-LABEL: define internal void @1() { | ||
| ; CHECK-FINAL-NEXT: %1 = call ptr @resolver1() | ||
| ; CHECK-FINAL-NEXT: store ptr %1, ptr @0, align 8 | ||
| ; CHECK-FINAL-NEXT: %2 = call ptr @resolver2() | ||
| ; CHECK-FINAL-NEXT: store ptr %2, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 1), align 8 | ||
| ; CHECK-FINAL-NEXT: %3 = call ptr @resolver3() | ||
| ; CHECK-FINAL-NEXT: store ptr %3, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 2), align 8 | ||
| ; CHECK-FINAL-NEXT: %4 = call ptr @resolver4() | ||
| ; CHECK-FINAL-NEXT: store ptr %4, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 3), align 8 | ||
| ; CHECK-FINAL-NEXT: %5 = call ptr @resolver5() | ||
| ; CHECK-FINAL-NEXT: store ptr %5, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 4), align 8 | ||
| ; CHECK-FINAL-NEXT: %6 = call ptr @resolver5() | ||
| ; CHECK-FINAL-NEXT: store ptr %6, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 5), align 8 | ||
| ; CHECK-FINAL-NEXT: %7 = call ptr @resolver5() | ||
| ; CHECK-FINAL-NEXT: store ptr %7, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 6), align 8 | ||
| ; CHECK-FINAL-NEXT: %8 = call ptr @resolver5() | ||
| ; CHECK-FINAL-NEXT: store ptr %8, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 7), align 8 | ||
| ; CHECK-FINAL-NEXT: ret void | ||
| ; CHECK-FINAL-NEXT: } |