-
Notifications
You must be signed in to change notification settings - Fork 44
Expand file tree
/
Copy pathouter-loop-pipelining-ptr-lifting.ll
More file actions
178 lines (153 loc) · 7.17 KB
/
Copy pathouter-loop-pipelining-ptr-lifting.ll
File metadata and controls
178 lines (153 loc) · 7.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
; This file is licensed under the Apache License v2.0 with LLVM Exceptions.
; See https://llvm.org/LICENSE.txt for license information.
; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
;
; (c) Copyright 2026 Advanced Micro Devices, Inc. or its affiliates
;
; RUN: llc -mtriple=aie2p -O2 -aie-enable-outer-loop-pipelining \
; RUN: -stop-after=aie-outer-loop-pipeliner \
; RUN: -o - %s 2>&1 | FileCheck %s
; RUN: llc -mtriple=aie2p -O2 -aie-enable-outer-loop-pipelining \
; RUN: -stop-after=aie-outer-loop-pipeliner -o - %s \
; RUN: | llc -mtriple=aie2p -x mir -run-pass=none -o /dev/null
; Test for pointer update lifting in the AIE Outer Loop Pipeliner pass.
;
; When pointer update instructions (GEPs, addrspacecasts, add.2d, add.3d, etc.)
; are in the epilogue but feed outer header PHIs, they should be lifted to
; the prologue IF they have no uses in the epilogue (outside the chain).
;
; Key behaviors tested:
; 1. Pointer-type PHI chains ARE lifted if no epilogue uses
; 2. Integer-type PHI chains (loop counters) are NOT lifted - they're used by icmp
; 3. Chains with inner loop dependencies cannot be lifted
;
; ============================================================================
; Test 1: Basic pointer update lifting with loop counter preservation
;
; The GEPs (a.ptr.next, c.ptr.next) are lifted because:
; - They have NO uses in the epilogue (stores use c.ptr, not c.ptr.next)
;
; The loop counter (iv.next) is NOT lifted because:
; - It's used by the icmp in the epilogue (epilogue use)
; ============================================================================
; CHECK-LABEL: define void @ptr_lifting_basic
; Warm-up block: loads only, plus hardware loop setup
; CHECK: outer.header.peel.pro:
; CHECK: %v0.peel = load i32, ptr %a, align 4
; CHECK: %outer.jnzd.tc = sub i32 %N, 1
; CHECK: %outer.ctr.init = call i32 @llvm.start.loop.iterations.i32(i32 %outer.jnzd.tc)
; CHECK: br label %outer.header
; Outer header: GEPs are lifted here (no epilogue uses)
; CHECK: outer.header:
; CHECK: %a.ptr = phi ptr
; CHECK: %c.ptr = phi ptr
; CHECK: %v0.phi = phi i32 [ %v0.peel, %outer.header.peel.pro ], [ %v0.epi, %outer.latch ]
; Hardware loop counter PHI (replaces software %iv)
; CHECK: %outer.ctr = phi i32
; GEPs lifted from epilogue (no epilogue uses - stores use c.ptr, not c.ptr.next)
; CHECK: %a.ptr.next = getelementptr inbounds i8, ptr %a.ptr, i64 128
; CHECK: %c.ptr.next = getelementptr inbounds i8, ptr %c.ptr, i64 128
; CHECK: br label %inner.header
; Outer latch: GEPs are NOT here (lifted), hardware loop counter replaces software iv
; CHECK: outer.latch:
; CHECK: store i32
; GEPs are NOT here (they were lifted)
; CHECK-NOT: getelementptr{{.*}}128
; Hardware loop counter update (replaces software %iv.next and icmp eq)
; CHECK: %v0.epi = load i32, ptr %a.ptr.next, align 4
; CHECK: %outer.ctr.next = call i32 @llvm.loop.decrement.reg.i32
; CHECK: %outer.loop.cond = icmp ne i32 %outer.ctr.next, 0
define void @ptr_lifting_basic(ptr noalias %a, ptr noalias %c, i32 %N, i32 %M) {
entry:
%cmp.outer = icmp sgt i32 %N, 1
br i1 %cmp.outer, label %outer.header, label %exit
outer.header:
%iv = phi i32 [ %N, %entry ], [ %iv.next, %outer.latch ]
%a.ptr = phi ptr [ %a, %entry ], [ %a.ptr.next, %outer.latch ]
%c.ptr = phi ptr [ %c, %entry ], [ %c.ptr.next, %outer.latch ]
%v0 = load i32, ptr %a.ptr, align 4
call void @llvm.set.loop.iterations.i32(i32 %M)
br label %inner.header
inner.header:
%acc = phi i32 [ 0, %outer.header ], [ %acc.next, %inner.header ]
%acc.next = add i32 %acc, %v0
%inner.cond = call i1 @llvm.loop.decrement.i32(i32 1)
br i1 %inner.cond, label %inner.header, label %outer.latch, !llvm.loop !1
outer.latch:
store i32 %acc.next, ptr %c.ptr, align 4
; Pointer updates - lifted to prologue (no epilogue uses)
%a.ptr.next = getelementptr inbounds i8, ptr %a.ptr, i64 128
%c.ptr.next = getelementptr inbounds i8, ptr %c.ptr, i64 128
; Loop counter update - NOT lifted (used by icmp below)
%iv.next = add i32 %iv, -1
%outer.cond = icmp eq i32 %iv.next, 0
br i1 %outer.cond, label %exit, label %outer.header, !llvm.loop !0
exit:
ret void
}
declare void @llvm.set.loop.iterations.i32(i32)
declare i1 @llvm.loop.decrement.i32(i32)
!0 = distinct !{!0, !2, !3}
!1 = distinct !{!1, !2}
!2 = !{!"llvm.loop.mustprogress"}
!3 = !{!"llvm.loop.itercount.range", i32 2}
; ============================================================================
; Test 2: Inner loop dependency prevents lifting
;
; When a pointer update chain depends on values produced by the inner loop,
; it cannot be lifted to the prologue. The lifting should be skipped for
; such chains while other independent chains can still be lifted.
; ============================================================================
; CHECK-LABEL: define void @ptr_lifting_inner_dep
; Warm-up: load only, plus hardware loop setup
; CHECK: outer.header.peel.pro:
; CHECK: %v0.peel = load i32, ptr %a, align 4
; CHECK: %outer.jnzd.tc = sub i32 %N, 1
; CHECK: %outer.ctr.init = call i32 @llvm.start.loop.iterations.i32(i32 %outer.jnzd.tc)
; CHECK: br label %outer.header
; Outer header: c.ptr.next is lifted (no inner loop dependency, no epilogue uses)
; CHECK: outer.header:
; The a.ptr update has inner loop dependency, so NOT lifted
; CHECK: %a.ptr = phi ptr [ %a.ptr.next, %outer.latch ], [ %a, %outer.header.peel.pro ]
; CHECK: %c.ptr.next = getelementptr inbounds i8, ptr %c.ptr, i64 64
; CHECK: br label %inner.header
; Outer latch: a.ptr.next stays here (inner loop dependency)
; CHECK: outer.latch:
; CHECK: %offset = sext i32 %acc.next to i64
; CHECK: %a.ptr.next = getelementptr inbounds i8, ptr %a.ptr, i64 %offset
define void @ptr_lifting_inner_dep(ptr noalias %a, ptr noalias %b, ptr noalias %c,
i32 %N, i32 %M) {
entry:
%cmp.outer = icmp sgt i32 %N, 1
br i1 %cmp.outer, label %outer.header, label %exit
outer.header:
%iv = phi i32 [ %N, %entry ], [ %iv.next, %outer.latch ]
%a.ptr = phi ptr [ %a, %entry ], [ %a.ptr.next, %outer.latch ]
%b.ptr = phi ptr [ %b, %entry ], [ %b.ptr.next, %outer.latch ]
%c.ptr = phi ptr [ %c, %entry ], [ %c.ptr.next, %outer.latch ]
%v0 = load i32, ptr %a.ptr, align 4
call void @llvm.set.loop.iterations.i32(i32 %M)
br label %inner.header
inner.header:
%acc = phi i32 [ 0, %outer.header ], [ %acc.next, %inner.header ]
%acc.next = add i32 %acc, %v0
%inner.cond = call i1 @llvm.loop.decrement.i32(i32 1)
br i1 %inner.cond, label %inner.header, label %outer.latch, !llvm.loop !5
outer.latch:
store i32 %acc.next, ptr %c.ptr, align 4
; This pointer update depends on inner loop result - CANNOT be lifted
%offset = sext i32 %acc.next to i64
%a.ptr.next = getelementptr inbounds i8, ptr %a.ptr, i64 %offset
; This pointer update has no inner loop dependency - CAN be lifted
%b.ptr.next = getelementptr inbounds i8, ptr %b.ptr, i64 64
%c.ptr.next = getelementptr inbounds i8, ptr %c.ptr, i64 64
%iv.next = add i32 %iv, -1
%outer.cond = icmp eq i32 %iv.next, 0
br i1 %outer.cond, label %exit, label %outer.header, !llvm.loop !4
exit:
ret void
}
!4 = distinct !{!4, !6, !7}
!5 = distinct !{!5, !6}
!6 = !{!"llvm.loop.mustprogress"}
!7 = !{!"llvm.loop.itercount.range", i32 2}