blob: 2076b62acb8d3bf89b1dfc411556c00d98d3138a [file] [log] [blame]
Rob Clark554f1ac2014-01-29 17:18:49 -05001/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
2
3/*
4 * Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 * Authors:
26 * Rob Clark <robclark@freedesktop.org>
27 */
28
Rob Clark13862812014-10-24 09:27:37 -040029#include "freedreno_util.h"
30
Rob Clark554f1ac2014-01-29 17:18:49 -050031#include "ir3.h"
32
33/*
34 * Copy Propagate:
35 *
Rob Clark554f1ac2014-01-29 17:18:49 -050036 */
37
38static void block_cp(struct ir3_block *block);
39static struct ir3_instruction * instr_cp(struct ir3_instruction *instr, bool keep);
40
Rob Clark13862812014-10-24 09:27:37 -040041/* XXX move this somewhere useful (and rename?) */
42static struct ir3_instruction *ssa(struct ir3_register *reg)
43{
44 if (reg->flags & IR3_REG_SSA)
45 return reg->instr;
46 return NULL;
47}
48
49static bool conflicts(struct ir3_instruction *a, struct ir3_instruction *b)
50{
51 return (a && b) && (a != b);
52}
53
54static void set_neighbors(struct ir3_instruction *instr,
55 struct ir3_instruction *left, struct ir3_instruction *right)
56{
57 debug_assert(!conflicts(instr->cp.left, left));
58 if (left) {
59 instr->cp.left_cnt++;
60 instr->cp.left = left;
61 }
62 debug_assert(!conflicts(instr->cp.right, right));
63 if (right) {
64 instr->cp.right_cnt++;
65 instr->cp.right = right;
66 }
67}
68
69/* remove neighbor reference, clearing left/right neighbor ptrs when
70 * there are no more references:
71 */
72static void remove_neighbors(struct ir3_instruction *instr)
73{
74 if (instr->cp.left) {
75 if (--instr->cp.left_cnt == 0)
76 instr->cp.left = NULL;
77 }
78 if (instr->cp.right) {
79 if (--instr->cp.right_cnt == 0)
80 instr->cp.right = NULL;
81 }
82}
83
84/* stop condition for iteration: */
85static bool check_stop(struct ir3_instruction *instr)
86{
87 if (ir3_instr_check_mark(instr))
88 return true;
89
90 /* stay within the block.. don't try to operate across
91 * basic block boundaries or we'll have problems when
92 * dealing with multiple basic blocks:
93 */
94 if (is_meta(instr) && (instr->opc == OPC_META_INPUT))
95 return true;
96
97 return false;
98}
99
Rob Clark554f1ac2014-01-29 17:18:49 -0500100static bool is_eligible_mov(struct ir3_instruction *instr)
101{
102 if ((instr->category == 1) &&
103 (instr->cat1.src_type == instr->cat1.dst_type)) {
Rob Clarka5ac36a2014-07-21 15:24:30 -0400104 struct ir3_register *dst = instr->regs[0];
Rob Clark554f1ac2014-01-29 17:18:49 -0500105 struct ir3_register *src = instr->regs[1];
Rob Clark13862812014-10-24 09:27:37 -0400106 struct ir3_instruction *src_instr = ssa(src);
Rob Clarka5ac36a2014-07-21 15:24:30 -0400107 if (dst->flags & IR3_REG_ADDR)
108 return false;
Rob Clark13862812014-10-24 09:27:37 -0400109 /* TODO: propagate abs/neg modifiers if possible */
110 if (src->flags & (IR3_REG_ABS | IR3_REG_NEGATE | IR3_REG_RELATIV))
111 return false;
112 if (src_instr) {
113 /* check that eliminating the move won't result in
114 * a neighbor conflict, ie. if an instruction feeds
115 * into multiple fanins it can still only have at
116 * most one left and one right neighbor:
117 */
118 if (conflicts(instr->cp.left, src_instr->cp.left))
119 return false;
120 if (conflicts(instr->cp.right, src_instr->cp.right))
121 return false;
Rob Clark554f1ac2014-01-29 17:18:49 -0500122 return true;
Rob Clark13862812014-10-24 09:27:37 -0400123 }
Rob Clark554f1ac2014-01-29 17:18:49 -0500124 }
125 return false;
126}
127
128static void walk_children(struct ir3_instruction *instr, bool keep)
129{
130 unsigned i;
131
132 /* walk down the graph from each src: */
133 for (i = 1; i < instr->regs_count; i++) {
134 struct ir3_register *src = instr->regs[i];
135 if (src->flags & IR3_REG_SSA)
136 src->instr = instr_cp(src->instr, keep);
137 }
138}
139
140static struct ir3_instruction *
141instr_cp_fanin(struct ir3_instruction *instr)
142{
Ilia Mirkin3dd9a0d2014-09-30 23:27:25 -0400143 unsigned i, j;
Rob Clark554f1ac2014-01-29 17:18:49 -0500144
145 /* we need to handle fanin specially, to detect cases
146 * when we need to keep a mov
147 */
148
149 for (i = 1; i < instr->regs_count; i++) {
150 struct ir3_register *src = instr->regs[i];
151 if (src->flags & IR3_REG_SSA) {
152 struct ir3_instruction *cand =
153 instr_cp(src->instr, false);
154
155 /* if the candidate is a fanout, then keep
156 * the move.
157 *
158 * This is a bit, um, fragile, but it should
159 * catch the extra mov's that the front-end
160 * puts in for us already in these cases.
161 */
162 if (is_meta(cand) && (cand->opc == OPC_META_FO))
163 cand = instr_cp(src->instr, true);
164
Ilia Mirkin3dd9a0d2014-09-30 23:27:25 -0400165 /* we can't have 2 registers referring to the same instruction, so
166 * go through and check if any already refer to the candidate
167 * instruction. if so, don't do the propagation.
Rob Clark13862812014-10-24 09:27:37 -0400168 *
169 * NOTE: we need to keep this, despite the neighbor
170 * conflict checks, to avoid A<->B<->A..
Ilia Mirkin3dd9a0d2014-09-30 23:27:25 -0400171 */
172 for (j = 1; j < instr->regs_count; j++)
173 if (instr->regs[j]->instr == cand)
174 break;
175 if (j == instr->regs_count)
176 src->instr = cand;
Rob Clark554f1ac2014-01-29 17:18:49 -0500177 }
178 }
179
180 walk_children(instr, false);
181
182 return instr;
Rob Clark554f1ac2014-01-29 17:18:49 -0500183}
184
185static struct ir3_instruction *
186instr_cp(struct ir3_instruction *instr, bool keep)
187{
188 /* if we've already visited this instruction, bail now: */
Rob Clark13862812014-10-24 09:27:37 -0400189 if (check_stop(instr))
Rob Clark554f1ac2014-01-29 17:18:49 -0500190 return instr;
191
192 if (is_meta(instr) && (instr->opc == OPC_META_FI))
193 return instr_cp_fanin(instr);
194
Rob Clark13862812014-10-24 09:27:37 -0400195 if (!keep && is_eligible_mov(instr)) {
196 struct ir3_instruction *src_instr = ssa(instr->regs[1]);
197 set_neighbors(src_instr, instr->cp.left, instr->cp.right);
198 remove_neighbors(instr);
199 return instr_cp(src_instr, false);
Rob Clark554f1ac2014-01-29 17:18:49 -0500200 }
201
202 walk_children(instr, false);
203
204 return instr;
205}
206
207static void block_cp(struct ir3_block *block)
208{
209 unsigned i, j;
210
211 for (i = 0; i < block->noutputs; i++) {
212 if (block->outputs[i]) {
213 struct ir3_instruction *out =
214 instr_cp(block->outputs[i], false);
215
216 /* To deal with things like this:
217 *
218 * 43: MOV OUT[2], TEMP[5]
219 * 44: MOV OUT[0], TEMP[5]
220 *
221 * we need to ensure that no two outputs point to
222 * the same instruction
223 */
224 for (j = 0; j < i; j++) {
225 if (block->outputs[j] == out) {
226 out = instr_cp(block->outputs[i], true);
227 break;
228 }
229 }
230
231 block->outputs[i] = out;
232 }
233 }
234}
235
Rob Clark13862812014-10-24 09:27:37 -0400236/*
237 * Find instruction neighbors:
238 */
239
240static void instr_find_neighbors(struct ir3_instruction *instr)
241{
242 unsigned i;
243
244 if (check_stop(instr))
245 return;
246
247 if (is_meta(instr) && (instr->opc == OPC_META_FI)) {
248 unsigned n = instr->regs_count;
249 for (i = 1; i < n; i++) {
250 struct ir3_instruction *src_instr = ssa(instr->regs[i]);
251 if (src_instr) {
252 struct ir3_instruction *left = (i > 1) ?
253 ssa(instr->regs[i-1]) : NULL;
254 struct ir3_instruction *right = (i < (n - 1)) ?
255 ssa(instr->regs[i+1]) : NULL;
256 set_neighbors(src_instr, left, right);
257 instr_find_neighbors(src_instr);
258 }
259 }
260 } else {
261 for (i = 1; i < instr->regs_count; i++) {
262 struct ir3_instruction *src_instr = ssa(instr->regs[i]);
263 if (src_instr)
264 instr_find_neighbors(src_instr);
265 }
266 }
267}
268
269static void block_find_neighbors(struct ir3_block *block)
270{
271 unsigned i;
272
273 for (i = 0; i < block->noutputs; i++) {
274 if (block->outputs[i]) {
275 struct ir3_instruction *instr = block->outputs[i];
276 instr_find_neighbors(instr);
277 }
278 }
279}
280
281static void instr_clear_neighbors(struct ir3_instruction *instr)
282{
283 unsigned i;
284
285 if (check_stop(instr))
286 return;
287
288 instr->cp.left_cnt = 0;
289 instr->cp.left = NULL;
290 instr->cp.right_cnt = 0;
291 instr->cp.right = NULL;
292
293 for (i = 1; i < instr->regs_count; i++) {
294 struct ir3_instruction *src_instr = ssa(instr->regs[i]);
295 if (src_instr)
296 instr_clear_neighbors(src_instr);
297 }
298}
299
300static void block_clear_neighbors(struct ir3_block *block)
301{
302 unsigned i;
303
304 for (i = 0; i < block->noutputs; i++) {
305 if (block->outputs[i]) {
306 struct ir3_instruction *instr = block->outputs[i];
307 instr_clear_neighbors(instr);
308 }
309 }
310}
311
Rob Clark554f1ac2014-01-29 17:18:49 -0500312void ir3_block_cp(struct ir3_block *block)
313{
Rob Clark7d7e6ae2014-07-25 10:56:23 -0400314 ir3_clear_mark(block->shader);
Rob Clark13862812014-10-24 09:27:37 -0400315 block_clear_neighbors(block);
316 ir3_clear_mark(block->shader);
317 block_find_neighbors(block);
318 ir3_clear_mark(block->shader);
Rob Clark554f1ac2014-01-29 17:18:49 -0500319 block_cp(block);
320}