class Module;
class Function;
class FunctionCallee;
+class GlobalIFunc;
class GlobalValue;
class Constant;
class Value;
--- /dev/null
+; 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
--- /dev/null
+; 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
+}
--- /dev/null
+; 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
+}
+
+
--- /dev/null
+; 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
--- /dev/null
+; 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: }
DELTA_PASS("function-bodies", reduceFunctionBodiesDeltaPass) \
DELTA_PASS("special-globals", reduceSpecialGlobalsDeltaPass) \
DELTA_PASS("aliases", reduceAliasesDeltaPass) \
+ DELTA_PASS("ifuncs", reduceIFuncsDeltaPass) \
DELTA_PASS("simplify-conditionals-true", reduceConditionalsTrueDeltaPass) \
DELTA_PASS("simplify-conditionals-false", reduceConditionalsFalseDeltaPass)\
DELTA_PASS("invokes", reduceInvokesDeltaPass) \
#include "ReduceAliases.h"
#include "Delta.h"
+#include "Utils.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/GlobalValue.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
using namespace llvm;
}
}
+static void extractIFuncsFromModule(Oracle &O, Module &Program) {
+ std::vector<GlobalIFunc *> IFuncs;
+ for (GlobalIFunc &GI : Program.ifuncs()) {
+ if (!O.shouldKeep())
+ IFuncs.push_back(&GI);
+ }
+
+ if (!IFuncs.empty())
+ lowerGlobalIFuncUsersAsGlobalCtor(Program, IFuncs);
+}
+
void llvm::reduceAliasesDeltaPass(TestRunner &Test) {
runDeltaPass(Test, extractAliasesFromModule, "Reducing Aliases");
}
+
+void llvm::reduceIFuncsDeltaPass(TestRunner &Test) {
+ runDeltaPass(Test, extractIFuncsFromModule, "Reducing Ifuncs");
+}
namespace llvm {
void reduceAliasesDeltaPass(TestRunner &Test);
+void reduceIFuncsDeltaPass(TestRunner &Test);
} // namespace llvm
#endif