Index: llvm/include/llvm/Transforms/Utils/ModuleUtils.h =================================================================== --- llvm/include/llvm/Transforms/Utils/ModuleUtils.h +++ llvm/include/llvm/Transforms/Utils/ModuleUtils.h @@ -25,6 +25,7 @@ class Module; class Function; class FunctionCallee; +class GlobalIFunc; class GlobalValue; class Constant; class Value; @@ -114,6 +115,11 @@ void embedBufferInModule(Module &M, MemoryBufferRef Buf, StringRef SectionName, Align Alignment = Align(1)); +/// Lower all calls to an ifunc by emitting a call to the resolver function, and +/// an indirect call to the pointer it returned for each user. Returns false if +/// there were any users which were not lowered (e.g. constant initializers). +bool lowerGlobalIFuncUsers(GlobalIFunc &GI); + class CallInst; namespace VFABI { /// Overwrite the Vector Function ABI variants attribute with the names provide Index: llvm/lib/Transforms/Utils/ModuleUtils.cpp =================================================================== --- llvm/lib/Transforms/Utils/ModuleUtils.cpp +++ llvm/lib/Transforms/Utils/ModuleUtils.cpp @@ -288,3 +288,38 @@ appendToCompilerUsed(M, GV); } + +bool llvm::lowerGlobalIFuncUsers(GlobalIFunc &GI) { + bool UnhandledUsers = false; + for (User *User : make_early_inc_range(GI.users())) { + Instruction *Inst = dyn_cast(User); + if (!Inst) { + // TODO: Should handle constantexpr casts in user instructions. Probably + // can't do much about constant initializers. + LLVM_DEBUG(dbgs() << "Not lowering ifunc non-instruction user " << *User + << '\n'); + UnhandledUsers = true; + continue; + } + + Function *ResolvedFunction = GI.getResolverFunction(); + + // We don't know what to pass to a resolver function taking arguments + // + // FIXME: Is this even valid? clang and gcc don't complain but this probably + // should be invalid IR. We could just pass through undef. + if (!std::empty(ResolvedFunction->getFunctionType()->params())) { + LLVM_DEBUG(dbgs() << "Not lowering ifunc resolver function " + << ResolvedFunction->getName() << " with parameters\n"); + UnhandledUsers = true; + continue; + } + + IRBuilder<> B(Inst); + CallInst *ResolvedTarget = B.CreateCall(ResolvedFunction); + Value *ResolvedCast = B.CreatePointerCast(ResolvedTarget, GI.getType()); + Inst->replaceUsesOfWith(&GI, ResolvedCast); + } + + return UnhandledUsers; +} Index: llvm/test/tools/llvm-reduce/ifunc-alias.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-reduce/ifunc-alias.ll @@ -0,0 +1,56 @@ +; 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 = + +; 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 = call ptr @resolver() +; 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: %1 = call ptr @resolver() +; CHECK-FINAL-BOTH: %call = call float %1(i64 %arg) +; CHECK-FINAL-BOTH: ret float %call +define float @call_alias_of_ifunc(i64 %arg) { + %call = call float @alias_of_ifunc(i64 %arg) + ret float %call +} 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,78 @@ +; 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" + +; 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: @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 = call addrspace(0) ptr addrspace(1) @resolver1_in_0() + ; CHECK-FINAL-NEXT: %2 = addrspacecast ptr addrspace(1) %1 to ptr + ; CHECK-FINAL-NEXT: call addrspace(0) void %2() + ; CHECK-FINAL-NEXT: %3 = call addrspace(1) ptr addrspace(1) @resolver1_in_1() + ; CHECK-FINAL-NEXT: call addrspace(1) void %3() + ; CHECK-FINAL-NEXT: %4 = call addrspace(0) ptr addrspace(1) @resolver1_in_0() + ; 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 +} Index: llvm/test/tools/llvm-reduce/remove-ifunc-typed.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-reduce/remove-ifunc-typed.ll @@ -0,0 +1,30 @@ +; 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 + +@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)* ()*) + + +; CHECK-INTERESTINGNESS: define i32 @call_ifuncs( +define i32 @call_ifuncs() { +; CHECK-FINAL-LABEL: define i32 @call_ifuncs( +; CHECK-FINAL-NEXT: %1 = call i32 (i32)* @func0() +; CHECK-FINAL-NEXT: %call0 = call i32 %1(i32 123) +; CHECK-FINAL-NEXT: %call1 = call i32 bitcast (float (double)* @ifunc1 to i32 ()*)() +; CHECK-FINAL-NEXT: %add = add i32 %call0, %call1 +; CHECK-FINAL-NEXT: ret i32 %add + %call0 = call i32 @ifunc0(i32 123) + %call1 = call i32 bitcast (float(double)* @ifunc1 to i32()*)() + %add = add i32 %call0, %call1 + ret i32 %add +} + Index: llvm/test/tools/llvm-reduce/remove-ifunc.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-reduce/remove-ifunc.ll @@ -0,0 +1,173 @@ +; 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-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 = call ptr @resolver1() + ; 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 = call ptr @resolver1() + ; CHECK-FINAL-NEXT: store ptr %1, ptr %ptr, align 8 + ; CHECK-FINAL-NEXT: %2 = call ptr @resolver1() + ; 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 = call ptr @resolver1() + ; 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 = call ptr @resolver4() + ; CHECK-FINAL-NEXT: %2 = call ptr @resolver4() + ; 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 = call ptr @resolver5() + ; 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 = call ptr @resolver5() + ; 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 = call ptr @resolver5() + ; 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 +} 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,19 @@ } } +static void extractIFuncsFromModule(Oracle &O, Module &Program) { + for (GlobalIFunc &GI : make_early_inc_range(Program.ifuncs())) { + if (!O.shouldKeep()) { + if (!lowerGlobalIFuncUsers(GI)) + GI.eraseFromParent(); + } + } +} + void llvm::reduceAliasesDeltaPass(TestRunner &Test) { runDeltaPass(Test, extractAliasesFromModule, "Reducing Aliases"); } + +void llvm::reduceIFuncsDeltaPass(TestRunner &Test) { + runDeltaPass(Test, extractIFuncsFromModule, "Reducing Ifuncs"); +}