Index: llvm/include/llvm/Transforms/Utils/ModuleUtils.h =================================================================== --- llvm/include/llvm/Transforms/Utils/ModuleUtils.h +++ llvm/include/llvm/Transforms/Utils/ModuleUtils.h @@ -27,6 +27,7 @@ class Module; class Function; class FunctionCallee; +class GlobalIFunc; class GlobalValue; class Constant; class Value; Index: llvm/test/tools/llvm-reduce/ifunc-alias.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-reduce/ifunc-alias.ll @@ -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 Index: llvm/test/tools/llvm-reduce/ifunc-nonsense-resolvers.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-reduce/ifunc-nonsense-resolvers.ll @@ -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 +} Index: llvm/test/tools/llvm-reduce/remove-ifunc-constantexpr.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-reduce/remove-ifunc-constantexpr.ll @@ -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 +} + + Index: llvm/test/tools/llvm-reduce/remove-ifunc-program-addrspace.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-reduce/remove-ifunc-program-addrspace.ll @@ -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 Index: llvm/test/tools/llvm-reduce/remove-ifunc.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-reduce/remove-ifunc.ll @@ -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: llvm/tools/llvm-reduce/DeltaManager.cpp =================================================================== --- llvm/tools/llvm-reduce/DeltaManager.cpp +++ llvm/tools/llvm-reduce/DeltaManager.cpp @@ -74,6 +74,7 @@ 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("unreachable-basic-blocks", reduceUnreachableBasicBlocksDeltaPass) \ Index: llvm/tools/llvm-reduce/deltas/ReduceAliases.h =================================================================== --- llvm/tools/llvm-reduce/deltas/ReduceAliases.h +++ llvm/tools/llvm-reduce/deltas/ReduceAliases.h @@ -18,6 +18,7 @@ namespace llvm { void reduceAliasesDeltaPass(TestRunner &Test); +void reduceIFuncsDeltaPass(TestRunner &Test); } // namespace llvm #endif Index: llvm/tools/llvm-reduce/deltas/ReduceAliases.cpp =================================================================== --- llvm/tools/llvm-reduce/deltas/ReduceAliases.cpp +++ llvm/tools/llvm-reduce/deltas/ReduceAliases.cpp @@ -13,8 +13,10 @@ #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 extractIFuncsFromModule(Oracle &O, Module &Program) { + std::vector 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"); +}