llvm.org GIT mirror llvm / 3afa698
Enable IPConstantPropagation to work with abstract call sites This modification of the currently unused inter-procedural constant propagation pass (IPConstantPropagation) shows how abstract call sites enable optimization of callback calls alongside direct and indirect calls. Through minimal changes, mostly dealing with the partial mapping of callbacks, inter-procedural constant propagation was enabled for callbacks, e.g., OpenMP runtime calls or pthreads_create. Differential Revision: https://reviews.llvm.org/D56447 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351628 91177308-0d34-0410-b5e6-96231b3b80d8 Johannes Doerfert 8 months ago
5 changed file(s) with 321 addition(s) and 12 deletion(s). Raw diff Collapse all Expand all
6161 // Ignore blockaddress uses.
6262 if (isa(UR)) continue;
6363
64 // Used by a non-instruction, or not the callee of a function, do not
65 // transform.
66 if (!isa(UR) && !isa(UR))
67 return false;
68
69 CallSite CS(cast(UR));
70 if (!CS.isCallee(&U))
64 // If no abstract call site was created we did not understand the use, bail.
65 AbstractCallSite ACS(&U);
66 if (!ACS)
7167 return false;
7268
7369 // Check out all of the potentially constant arguments. Note that we don't
7470 // inspect varargs here.
75 CallSite::arg_iterator AI = CS.arg_begin();
7671 Function::arg_iterator Arg = F.arg_begin();
77 for (unsigned i = 0, e = ArgumentConstants.size(); i != e;
78 ++i, ++AI, ++Arg) {
72 for (unsigned i = 0, e = ArgumentConstants.size(); i != e; ++i, ++Arg) {
7973
8074 // If this argument is known non-constant, ignore it.
8175 if (ArgumentConstants[i].second)
8276 continue;
8377
84 Constant *C = dyn_cast(*AI);
78 Value *V = ACS.getCallArgOperand(i);
79 Constant *C = dyn_cast_or_null(V);
80
81 // We can only propagate thread independent values through callbacks.
82 // This is different to direct/indirect call sites because for them we
83 // know the thread executing the caller and callee is the same. For
84 // callbacks this is not guaranteed, thus a thread dependent value could
85 // be different for the caller and callee, making it invalid to propagate.
86 if (C && ACS.isCallbackCall() && C->isThreadDependent()) {
87 // Argument became non-constant. If all arguments are non-constant now,
88 // give up on this function.
89 if (++NumNonconstant == ArgumentConstants.size())
90 return false;
91
92 ArgumentConstants[i].second = true;
93 continue;
94 }
95
8596 if (C && ArgumentConstants[i].first == nullptr) {
8697 ArgumentConstants[i].first = C; // First constant seen.
8798 } else if (C && ArgumentConstants[i].first == C) {
8899 // Still the constant value we think it is.
89 } else if (*AI == &*Arg) {
100 } else if (V == &*Arg) {
90101 // Ignore recursive calls passing argument down.
91102 } else {
92103 // Argument became non-constant. If all arguments are non-constant now,
0 ; RUN: opt -ipconstprop -S < %s | FileCheck %s
1 ;
2 ;
3 ; /---------------------------------------|
4 ; | /----------------------|----|
5 ; | | /-----| |
6 ; V V V | |
7 ; void broker(int (*cb0)(int), int (*cb1)(int), int (*cb2)(int), int, int);
8 ;
9 ; static int cb0(int zero) {
10 ; return zero;
11 ; }
12 ; static int cb1(int unknown) {
13 ; return unknown;
14 ; }
15 ; static int cb2(int unknown) {
16 ; cb0(0);
17 ; return unknown;
18 ; }
19 ; static int cb3(int unknown) {
20 ; return unknown;
21 ; }
22 ; static int cb4(int unknown) {
23 ; return unknown;
24 ; }
25 ;
26 ; void foo() {
27 ; cb0(0);
28 ; cb3(1);
29 ; broker(cb0, cb1, cb0, 0, 1);
30 ; broker(cb1, cb2, cb2, 0, 1);
31 ; broker(cb3, cb2, cb3, 0, 1);
32 ; broker(cb4, cb4, cb4, 0, 1);
33 ; }
34 ;
35 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
36
37 define internal i32 @cb0(i32 %zero) {
38 entry:
39 ; CHECK: @cb0
40 ; CHECK-NEXT: entry
41 ; CHECK-NEXT: ret i32 0
42 ret i32 %zero
43 }
44
45 define internal i32 @cb1(i32 %unknown) {
46 entry:
47 ; CHECK: ret i32 %unknown
48 ret i32 %unknown
49 }
50
51 define internal i32 @cb2(i32 %unknown) {
52 entry:
53 %call = call i32 @cb0(i32 0)
54 ; CHECK: ret i32 %unknown
55 ret i32 %unknown
56 }
57
58 define internal i32 @cb3(i32 %unknown) {
59 entry:
60 ; CHECK: ret i32 %unknown
61 ret i32 %unknown
62 }
63
64 define internal i32 @cb4(i32 %unknown) {
65 entry:
66 ; CHECK: ret i32 %unknown
67 ret i32 %unknown
68 }
69
70 define void @foo() {
71 entry:
72 %call = call i32 @cb0(i32 0)
73 %call1 = call i32 @cb3(i32 1)
74 call void @broker(i32 (i32)* nonnull @cb0, i32 (i32)* nonnull @cb1, i32 (i32)* nonnull @cb0, i32 0, i32 1)
75 call void @broker(i32 (i32)* nonnull @cb1, i32 (i32)* nonnull @cb2, i32 (i32)* nonnull @cb2, i32 0, i32 1)
76 call void @broker(i32 (i32)* nonnull @cb3, i32 (i32)* nonnull @cb2, i32 (i32)* nonnull @cb3, i32 0, i32 1)
77 call void @broker(i32 (i32)* nonnull @cb4, i32 (i32)* nonnull @cb4, i32 (i32)* nonnull @cb4, i32 0, i32 1)
78 ret void
79 }
80
81 declare !callback !3 void @broker(i32 (i32)*, i32 (i32)*, i32 (i32)*, i32, i32)
82
83 !0 = !{i64 0, i64 3, i1 false}
84 !1 = !{i64 1, i64 4, i1 false}
85 !2 = !{i64 2, i64 3, i1 false}
86 !3 = !{!0, !2, !1}
0 ; RUN: opt -S -ipconstprop < %s | FileCheck %s
1 ;
2 ; void bar(int, float, double);
3 ;
4 ; void foo(int N) {
5 ; float p = 3;
6 ; double q = 5;
7 ; N = 7;
8 ;
9 ; #pragma omp parallel for firstprivate(q)
10 ; for (int i = 2; i < N; i++) {
11 ; bar(i, p, q);
12 ; }
13 ; }
14 ;
15 ; Verify the constant value of q is propagated into the outlined function.
16 ;
17 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
18
19 %struct.ident_t = type { i32, i32, i32, i32, i8* }
20
21 @.str = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1
22 @0 = private unnamed_addr global %struct.ident_t { i32 0, i32 514, i32 0, i32 0, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 0) }, align 8
23 @1 = private unnamed_addr global %struct.ident_t { i32 0, i32 2, i32 0, i32 0, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 0) }, align 8
24
25 define dso_local void @foo(i32 %N) {
26 entry:
27 %N.addr = alloca i32, align 4
28 %p = alloca float, align 4
29 store i32 %N, i32* %N.addr, align 4
30 store float 3.000000e+00, float* %p, align 4
31 store i32 7, i32* %N.addr, align 4
32 call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* nonnull @1, i32 3, void (i32*, i32*, ...)* bitcast (void (i32*, i32*, i32*, float*, i64)* @.omp_outlined. to void (i32*, i32*, ...)*), i32* nonnull %N.addr, float* nonnull %p, i64 4617315517961601024)
33 ret void
34 }
35
36 define internal void @.omp_outlined.(i32* noalias %.global_tid., i32* noalias %.bound_tid., i32* dereferenceable(4) %N, float* dereferenceable(4) %p, i64 %q) {
37 entry:
38 %q.addr = alloca i64, align 8
39 %.omp.lb = alloca i32, align 4
40 %.omp.ub = alloca i32, align 4
41 %.omp.stride = alloca i32, align 4
42 %.omp.is_last = alloca i32, align 4
43 ; CHECK: store i64 4617315517961601024, i64* %q.addr, align 8
44 store i64 %q, i64* %q.addr, align 8
45 %conv = bitcast i64* %q.addr to double*
46 %tmp = load i32, i32* %N, align 4
47 %sub3 = add nsw i32 %tmp, -3
48 %cmp = icmp sgt i32 %tmp, 2
49 br i1 %cmp, label %omp.precond.then, label %omp.precond.end
50
51 omp.precond.then: ; preds = %entry
52 store i32 0, i32* %.omp.lb, align 4
53 store i32 %sub3, i32* %.omp.ub, align 4
54 store i32 1, i32* %.omp.stride, align 4
55 store i32 0, i32* %.omp.is_last, align 4
56 %tmp5 = load i32, i32* %.global_tid., align 4
57 call void @__kmpc_for_static_init_4(%struct.ident_t* nonnull @0, i32 %tmp5, i32 34, i32* nonnull %.omp.is_last, i32* nonnull %.omp.lb, i32* nonnull %.omp.ub, i32* nonnull %.omp.stride, i32 1, i32 1)
58 %tmp6 = load i32, i32* %.omp.ub, align 4
59 %cmp6 = icmp sgt i32 %tmp6, %sub3
60 br i1 %cmp6, label %cond.true, label %cond.false
61
62 cond.true: ; preds = %omp.precond.then
63 br label %cond.end
64
65 cond.false: ; preds = %omp.precond.then
66 %tmp7 = load i32, i32* %.omp.ub, align 4
67 br label %cond.end
68
69 cond.end: ; preds = %cond.false, %cond.true
70 %cond = phi i32 [ %sub3, %cond.true ], [ %tmp7, %cond.false ]
71 store i32 %cond, i32* %.omp.ub, align 4
72 %tmp8 = load i32, i32* %.omp.lb, align 4
73 br label %omp.inner.for.cond
74
75 omp.inner.for.cond: ; preds = %omp.inner.for.inc, %cond.end
76 %.omp.iv.0 = phi i32 [ %tmp8, %cond.end ], [ %add11, %omp.inner.for.inc ]
77 %tmp9 = load i32, i32* %.omp.ub, align 4
78 %cmp8 = icmp sgt i32 %.omp.iv.0, %tmp9
79 br i1 %cmp8, label %omp.inner.for.cond.cleanup, label %omp.inner.for.body
80
81 omp.inner.for.cond.cleanup: ; preds = %omp.inner.for.cond
82 br label %omp.inner.for.end
83
84 omp.inner.for.body: ; preds = %omp.inner.for.cond
85 %add10 = add nsw i32 %.omp.iv.0, 2
86 %tmp10 = load float, float* %p, align 4
87 %tmp11 = load double, double* %conv, align 8
88 call void @bar(i32 %add10, float %tmp10, double %tmp11)
89 br label %omp.body.continue
90
91 omp.body.continue: ; preds = %omp.inner.for.body
92 br label %omp.inner.for.inc
93
94 omp.inner.for.inc: ; preds = %omp.body.continue
95 %add11 = add nsw i32 %.omp.iv.0, 1
96 br label %omp.inner.for.cond
97
98 omp.inner.for.end: ; preds = %omp.inner.for.cond.cleanup
99 br label %omp.loop.exit
100
101 omp.loop.exit: ; preds = %omp.inner.for.end
102 %tmp12 = load i32, i32* %.global_tid., align 4
103 call void @__kmpc_for_static_fini(%struct.ident_t* nonnull @0, i32 %tmp12)
104 br label %omp.precond.end
105
106 omp.precond.end: ; preds = %omp.loop.exit, %entry
107 ret void
108 }
109
110 declare dso_local void @__kmpc_for_static_init_4(%struct.ident_t*, i32, i32, i32*, i32*, i32*, i32*, i32, i32)
111
112 declare dso_local void @bar(i32, float, double)
113
114 declare dso_local void @__kmpc_for_static_fini(%struct.ident_t*, i32)
115
116 declare !callback !0 dso_local void @__kmpc_fork_call(%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...)
117
118 !1 = !{i64 2, i64 -1, i64 -1, i1 true}
119 !0 = !{!1}
0 ; RUN: opt -ipconstprop -S < %s | FileCheck %s
1 ;
2 ; #include
3 ;
4 ; void *GlobalVPtr;
5 ;
6 ; static void *foo(void *arg) { return arg; }
7 ; static void *bar(void *arg) { return arg; }
8 ;
9 ; int main() {
10 ; pthread_t thread;
11 ; pthread_create(&thread, NULL, foo, NULL);
12 ; pthread_create(&thread, NULL, bar, &GlobalVPtr);
13 ; return 0;
14 ; }
15 ;
16 ; Verify the constant values NULL and &GlobalVPtr are propagated into foo and
17 ; bar, respectively.
18 ;
19 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
20
21 %union.pthread_attr_t = type { i64, [48 x i8] }
22
23 @GlobalVPtr = common dso_local global i8* null, align 8
24
25 define dso_local i32 @main() {
26 entry:
27 %thread = alloca i64, align 8
28 %call = call i32 @pthread_create(i64* nonnull %thread, %union.pthread_attr_t* null, i8* (i8*)* nonnull @foo, i8* null)
29 %call1 = call i32 @pthread_create(i64* nonnull %thread, %union.pthread_attr_t* null, i8* (i8*)* nonnull @bar, i8* bitcast (i8** @GlobalVPtr to i8*))
30 ret i32 0
31 }
32
33 declare !callback !0 dso_local i32 @pthread_create(i64*, %union.pthread_attr_t*, i8* (i8*)*, i8*)
34
35 define internal i8* @foo(i8* %arg) {
36 entry:
37 ; CHECK: ret i8* null
38 ret i8* %arg
39 }
40
41 define internal i8* @bar(i8* %arg) {
42 entry:
43 ; CHECK: ret i8* bitcast (i8** @GlobalVPtr to i8*)
44 ret i8* %arg
45 }
46
47 !1 = !{i64 2, i64 3, i1 false}
48 !0 = !{!1}
0 ; RUN: opt -ipconstprop -S < %s | FileCheck %s
1 ;
2 ; #include
3 ; thread_local int gtl = 0;
4 ; int gsh = 0;
5 ;
6 ; static int callee(int *thread_local_ptr, int *shared_ptr) {
7 ; return *thread_local_ptr + *shared_ptr;
8 ; }
9 ;
10 ; void broker(int *, int (*callee)(int *, int *), int *);
11 ;
12 ; void caller() {
13 ; broker(>l, callee, &gsh);
14 ; }
15 ;
16 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
17
18 @gtl = dso_local thread_local global i32 0, align 4
19 @gsh = dso_local global i32 0, align 4
20
21 define internal i32 @callee(i32* %thread_local_ptr, i32* %shared_ptr) {
22 entry:
23 ; CHECK: %tmp = load i32, i32* %thread_local_ptr, align 4
24 ; CHECK: %tmp1 = load i32, i32* @gsh, align 4
25 ; CHECK: %add = add nsw i32 %tmp, %tmp1
26 %tmp = load i32, i32* %thread_local_ptr, align 4
27 %tmp1 = load i32, i32* %shared_ptr, align 4
28 %add = add nsw i32 %tmp, %tmp1
29 ret i32 %add
30 }
31
32 define dso_local void @caller() {
33 entry:
34 call void @broker(i32* nonnull @gtl, i32 (i32*, i32*)* nonnull @callee, i32* nonnull @gsh)
35 ret void
36 }
37
38 declare !callback !0 dso_local void @broker(i32*, i32 (i32*, i32*)*, i32*)
39
40 !1 = !{i64 1, i64 0, i64 2, i1 false}
41 !0 = !{!1}