llvm-reduce: Reduce ifuncs
authorMatt Arsenault <Matthew.Arsenault@amd.com>
Tue, 22 Nov 2022 21:55:44 +0000 (16:55 -0500)
committerMatt Arsenault <arsenm2@gmail.com>
Wed, 18 Jan 2023 03:33:57 +0000 (22:33 -0500)
Reduce by calling the resolver function at the use site, and inserting
an indirect call. Try to delete if there are no uses left over.

We should also probably try to do something about constantexpr uses;
perhaps treat them like aliases.

llvm/include/llvm/Transforms/Utils/ModuleUtils.h
llvm/test/tools/llvm-reduce/ifunc-alias.ll [new file with mode: 0644]
llvm/test/tools/llvm-reduce/ifunc-nonsense-resolvers.ll [new file with mode: 0644]
llvm/test/tools/llvm-reduce/remove-ifunc-constantexpr.ll [new file with mode: 0644]
llvm/test/tools/llvm-reduce/remove-ifunc-program-addrspace.ll [new file with mode: 0644]
llvm/test/tools/llvm-reduce/remove-ifunc.ll [new file with mode: 0644]
llvm/tools/llvm-reduce/DeltaManager.cpp
llvm/tools/llvm-reduce/deltas/ReduceAliases.cpp
llvm/tools/llvm-reduce/deltas/ReduceAliases.h

index 0fe96c1..1a4e842 100644 (file)
@@ -27,6 +27,7 @@ template <typename T> class ArrayRef;
 class Module;
 class Function;
 class FunctionCallee;
+class GlobalIFunc;
 class GlobalValue;
 class Constant;
 class Value;
diff --git a/llvm/test/tools/llvm-reduce/ifunc-alias.ll b/llvm/test/tools/llvm-reduce/ifunc-alias.ll
new file mode 100644 (file)
index 0000000..258eb00
--- /dev/null
@@ -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
diff --git a/llvm/test/tools/llvm-reduce/ifunc-nonsense-resolvers.ll b/llvm/test/tools/llvm-reduce/ifunc-nonsense-resolvers.ll
new file mode 100644 (file)
index 0000000..603acc5
--- /dev/null
@@ -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
+}
diff --git a/llvm/test/tools/llvm-reduce/remove-ifunc-constantexpr.ll b/llvm/test/tools/llvm-reduce/remove-ifunc-constantexpr.ll
new file mode 100644 (file)
index 0000000..5e3a7e7
--- /dev/null
@@ -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
+}
+
+
diff --git a/llvm/test/tools/llvm-reduce/remove-ifunc-program-addrspace.ll b/llvm/test/tools/llvm-reduce/remove-ifunc-program-addrspace.ll
new file mode 100644 (file)
index 0000000..e275d61
--- /dev/null
@@ -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
diff --git a/llvm/test/tools/llvm-reduce/remove-ifunc.ll b/llvm/test/tools/llvm-reduce/remove-ifunc.ll
new file mode 100644 (file)
index 0000000..a7853d9
--- /dev/null
@@ -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: }
index 57e8175..6c46767 100644 (file)
@@ -75,6 +75,7 @@ static cl::list<std::string>
     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)                              \
index 8ceae20..db5376f 100644 (file)
 
 #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;
 
@@ -29,6 +31,21 @@ static void extractAliasesFromModule(Oracle &O, Module &Program) {
   }
 }
 
+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");
+}
index 82b4d3e..404677d 100644 (file)
@@ -18,6 +18,7 @@
 
 namespace llvm {
 void reduceAliasesDeltaPass(TestRunner &Test);
+void reduceIFuncsDeltaPass(TestRunner &Test);
 } // namespace llvm
 
 #endif