101 changes: 101 additions & 0 deletions llvm/test/Transforms/LowerIFunc/ifunc-program-addrspace.ll
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
;
54 changes: 54 additions & 0 deletions llvm/test/Transforms/LowerIFunc/ifunc-typed.ll
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
;
191 changes: 191 additions & 0 deletions llvm/test/Transforms/LowerIFunc/lower-ifunc.ll
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
;
68 changes: 68 additions & 0 deletions llvm/test/tools/llvm-reduce/ifunc-alias.ll
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
22 changes: 22 additions & 0 deletions llvm/test/tools/llvm-reduce/ifunc-nonsense-resolvers.ll
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
}
27 changes: 27 additions & 0 deletions llvm/test/tools/llvm-reduce/remove-ifunc-constantexpr.ll
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
}


107 changes: 107 additions & 0 deletions llvm/test/tools/llvm-reduce/remove-ifunc-program-addrspace.ll
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
198 changes: 198 additions & 0 deletions llvm/test/tools/llvm-reduce/remove-ifunc.ll
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: }
1 change: 1 addition & 0 deletions llvm/tools/llvm-reduce/DeltaManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
2 changes: 1 addition & 1 deletion llvm/tools/llvm-reduce/TestRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ void TestRunner::setProgram(std::unique_ptr<ReducerWorkItem> P) {
Program = std::move(P);
}

void writeBitcode(ReducerWorkItem &M, raw_ostream &OutStream) {
void writeBitcode(const ReducerWorkItem &M, raw_ostream &OutStream) {
if (M.LTOInfo && M.LTOInfo->IsThinLTO && M.LTOInfo->EnableSplitLTOUnit) {
PassBuilder PB;
LoopAnalysisManager LAM;
Expand Down
45 changes: 20 additions & 25 deletions llvm/tools/llvm-reduce/deltas/Delta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ static cl::opt<unsigned> NumJobs(
unsigned NumJobs = 1;
#endif

void writeBitcode(ReducerWorkItem &M, raw_ostream &OutStream);
void writeBitcode(const ReducerWorkItem &M, raw_ostream &OutStream);

void readBitcode(ReducerWorkItem &M, MemoryBufferRef Data, LLVMContext &Ctx,
StringRef ToolName);

bool isReduced(ReducerWorkItem &M, const TestRunner &Test) {
bool isReduced(const ReducerWorkItem &M, const TestRunner &Test) {
const bool UseBitcode = Test.inputIsBitcode() || TmpFilesAsBitcode;

SmallString<128> CurrentFilepath;
Expand Down Expand Up @@ -134,7 +134,7 @@ static bool increaseGranularity(std::vector<Chunk> &Chunks) {
// Check if \p ChunkToCheckForUninterestingness is interesting. Returns the
// modified module if the chunk resulted in a reduction.
static std::unique_ptr<ReducerWorkItem>
CheckChunk(const Chunk &ChunkToCheckForUninterestingness,
CheckChunk(const Chunk ChunkToCheckForUninterestingness,
std::unique_ptr<ReducerWorkItem> Clone, const TestRunner &Test,
ReductionFunc ExtractChunksFromModule,
const DenseSet<Chunk> &UninterestingChunks,
Expand Down Expand Up @@ -188,13 +188,14 @@ CheckChunk(const Chunk &ChunkToCheckForUninterestingness,
}

static SmallString<0> ProcessChunkFromSerializedBitcode(
Chunk &ChunkToCheckForUninterestingness, TestRunner &Test,
ReductionFunc ExtractChunksFromModule, DenseSet<Chunk> &UninterestingChunks,
std::vector<Chunk> &ChunksStillConsideredInteresting,
SmallString<0> &OriginalBC, std::atomic<bool> &AnyReduced) {
const Chunk ChunkToCheckForUninterestingness, const TestRunner &Test,
ReductionFunc ExtractChunksFromModule,
const DenseSet<Chunk> &UninterestingChunks,
ArrayRef<Chunk> ChunksStillConsideredInteresting, StringRef OriginalBC,
std::atomic<bool> &AnyReduced) {
LLVMContext Ctx;
auto CloneMMM = std::make_unique<ReducerWorkItem>();
MemoryBufferRef Data(StringRef(OriginalBC), "<bc file>");
MemoryBufferRef Data(OriginalBC, "<bc file>");
readBitcode(*CloneMMM, Data, Ctx, Test.getToolName());

SmallString<0> Result;
Expand Down Expand Up @@ -314,14 +315,12 @@ void llvm::runDeltaPass(TestRunner &Test, ReductionFunc ExtractChunksFromModule,
// LLVMContext object. If a task reduces the input, serialize the result
// back in the corresponding Result element.
for (unsigned J = 0; J < NumInitialTasks; ++J) {
Chunk ChunkToCheck = *(I + J);
TaskQueue.emplace_back(ChunkThreadPool.async(
[J, I, &Test, &ExtractChunksFromModule, &UninterestingChunks,
&ChunksStillConsideredInteresting, &OriginalBC, &AnyReduced]() {
return ProcessChunkFromSerializedBitcode(
*(I + J), Test, ExtractChunksFromModule,
UninterestingChunks, ChunksStillConsideredInteresting,
OriginalBC, AnyReduced);
}));
ProcessChunkFromSerializedBitcode, ChunkToCheck, std::ref(Test),
ExtractChunksFromModule, UninterestingChunks,
ChunksStillConsideredInteresting, OriginalBC,
std::ref(AnyReduced)));
}

// Start processing results of the queued tasks. We wait for the first
Expand All @@ -340,16 +339,12 @@ void llvm::runDeltaPass(TestRunner &Test, ReductionFunc ExtractChunksFromModule,
if (Res.empty()) {
unsigned NumScheduledTasks = NumChunksProcessed + TaskQueue.size();
if (!AnyReduced && I + NumScheduledTasks != E) {
Chunk &ChunkToCheck = *(I + NumScheduledTasks);
Chunk ChunkToCheck = *(I + NumScheduledTasks);
TaskQueue.emplace_back(ChunkThreadPool.async(
[&Test, &ExtractChunksFromModule, &UninterestingChunks,
&ChunksStillConsideredInteresting, &OriginalBC,
&ChunkToCheck, &AnyReduced]() {
return ProcessChunkFromSerializedBitcode(
ChunkToCheck, Test, ExtractChunksFromModule,
UninterestingChunks, ChunksStillConsideredInteresting,
OriginalBC, AnyReduced);
}));
ProcessChunkFromSerializedBitcode, ChunkToCheck,
std::ref(Test), ExtractChunksFromModule, UninterestingChunks,
ChunksStillConsideredInteresting, OriginalBC,
std::ref(AnyReduced)));
}
continue;
}
Expand Down Expand Up @@ -381,7 +376,7 @@ void llvm::runDeltaPass(TestRunner &Test, ReductionFunc ExtractChunksFromModule,
if (!Result)
continue;

Chunk &ChunkToCheckForUninterestingness = *I;
const Chunk ChunkToCheckForUninterestingness = *I;
FoundAtLeastOneNewUninterestingChunkWithCurrentGranularity = true;
UninterestingChunks.insert(ChunkToCheckForUninterestingness);
ReducedProgram = std::move(Result);
Expand Down
17 changes: 17 additions & 0 deletions llvm/tools/llvm-reduce/deltas/ReduceAliases.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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");
}
1 change: 1 addition & 0 deletions llvm/tools/llvm-reduce/deltas/ReduceAliases.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

namespace llvm {
void reduceAliasesDeltaPass(TestRunner &Test);
void reduceIFuncsDeltaPass(TestRunner &Test);
} // namespace llvm

#endif
2 changes: 1 addition & 1 deletion llvm/tools/llvm-reduce/llvm-reduce.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ static cl::opt<int>

static codegen::RegisterCodeGenFlags CGF;

bool isReduced(ReducerWorkItem &M, const TestRunner &Test);
bool isReduced(const ReducerWorkItem &M, const TestRunner &Test);

/// Turn off crash debugging features
///
Expand Down