i965/cfg: Add functions to combine basic blocks.
[mesa.git] / src / mesa / drivers / dri / i965 / brw_cfg.cpp
1 /*
2 * Copyright © 2012 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * Authors:
24 * Eric Anholt <eric@anholt.net>
25 *
26 */
27
28 #include "brw_cfg.h"
29
30 /** @file brw_cfg.cpp
31 *
32 * Walks the shader instructions generated and creates a set of basic
33 * blocks with successor/predecessor edges connecting them.
34 */
35
36 static bblock_t *
37 pop_stack(exec_list *list)
38 {
39 bblock_link *link = (bblock_link *)list->get_tail();
40 bblock_t *block = link->block;
41 link->link.remove();
42
43 return block;
44 }
45
46 static exec_node *
47 link(void *mem_ctx, bblock_t *block)
48 {
49 bblock_link *l = new(mem_ctx) bblock_link(block);
50 return &l->link;
51 }
52
53 bblock_t::bblock_t(cfg_t *cfg) :
54 cfg(cfg), start_ip(0), end_ip(0), num(0),
55 if_block(NULL), else_block(NULL)
56 {
57 start = NULL;
58 end = NULL;
59
60 parents.make_empty();
61 children.make_empty();
62 }
63
64 void
65 bblock_t::add_successor(void *mem_ctx, bblock_t *successor)
66 {
67 successor->parents.push_tail(::link(mem_ctx, this));
68 children.push_tail(::link(mem_ctx, successor));
69 }
70
71 bool
72 bblock_t::is_predecessor_of(const bblock_t *block) const
73 {
74 foreach_list_typed_safe (bblock_link, parent, link, &block->parents) {
75 if (parent->block == this) {
76 return true;
77 }
78 }
79
80 return false;
81 }
82
83 bool
84 bblock_t::is_successor_of(const bblock_t *block) const
85 {
86 foreach_list_typed_safe (bblock_link, child, link, &block->children) {
87 if (child->block == this) {
88 return true;
89 }
90 }
91
92 return false;
93 }
94
95 static bool
96 ends_block(const backend_instruction *inst)
97 {
98 enum opcode op = inst->opcode;
99
100 return op == BRW_OPCODE_IF ||
101 op == BRW_OPCODE_ELSE ||
102 op == BRW_OPCODE_CONTINUE ||
103 op == BRW_OPCODE_BREAK ||
104 op == BRW_OPCODE_WHILE;
105 }
106
107 static bool
108 starts_block(const backend_instruction *inst)
109 {
110 enum opcode op = inst->opcode;
111
112 return op == BRW_OPCODE_DO ||
113 op == BRW_OPCODE_ENDIF;
114 }
115
116 bool
117 bblock_t::can_combine_with(const bblock_t *that) const
118 {
119 if ((const bblock_t *)this->link.next != that)
120 return false;
121
122 if (ends_block(this->end) ||
123 starts_block(that->start))
124 return false;
125
126 return true;
127 }
128
129 void
130 bblock_t::combine_with(bblock_t *that)
131 {
132 assert(this->can_combine_with(that));
133 foreach_list_typed (bblock_link, link, link, &this->children) {
134 assert(link->block == that);
135 }
136 foreach_list_typed (bblock_link, link, link, &that->parents) {
137 assert(link->block == this);
138 }
139
140 this->end_ip = that->end_ip;
141 this->end = that->end;
142 this->else_block = that->else_block;
143
144 this->cfg->remove_block(that);
145 }
146
147 void
148 bblock_t::dump(backend_visitor *v)
149 {
150 int ip = this->start_ip;
151 for (backend_instruction *inst = (backend_instruction *)this->start;
152 inst != this->end->next;
153 inst = (backend_instruction *) inst->next) {
154 fprintf(stderr, "%5d: ", ip);
155 v->dump_instruction(inst);
156 ip++;
157 }
158 }
159
160 cfg_t::cfg_t(exec_list *instructions)
161 {
162 mem_ctx = ralloc_context(NULL);
163 block_list.make_empty();
164 blocks = NULL;
165 num_blocks = 0;
166
167 bblock_t *cur = NULL;
168 int ip = 0;
169
170 bblock_t *entry = new_block();
171 bblock_t *cur_if = NULL; /**< BB ending with IF. */
172 bblock_t *cur_else = NULL; /**< BB ending with ELSE. */
173 bblock_t *cur_endif = NULL; /**< BB starting with ENDIF. */
174 bblock_t *cur_do = NULL; /**< BB starting with DO. */
175 bblock_t *cur_while = NULL; /**< BB immediately following WHILE. */
176 exec_list if_stack, else_stack, do_stack, while_stack;
177 bblock_t *next;
178
179 set_next_block(&cur, entry, ip);
180
181 entry->start = (backend_instruction *) instructions->get_head();
182
183 foreach_in_list(backend_instruction, inst, instructions) {
184 cur->end = inst;
185
186 /* set_next_block wants the post-incremented ip */
187 ip++;
188
189 switch (inst->opcode) {
190 case BRW_OPCODE_IF:
191 /* Push our information onto a stack so we can recover from
192 * nested ifs.
193 */
194 if_stack.push_tail(link(mem_ctx, cur_if));
195 else_stack.push_tail(link(mem_ctx, cur_else));
196
197 cur_if = cur;
198 cur_else = NULL;
199 cur_endif = NULL;
200
201 /* Set up our immediately following block, full of "then"
202 * instructions.
203 */
204 next = new_block();
205 next->start = (backend_instruction *)inst->next;
206 cur_if->add_successor(mem_ctx, next);
207
208 set_next_block(&cur, next, ip);
209 break;
210
211 case BRW_OPCODE_ELSE:
212 cur_else = cur;
213
214 next = new_block();
215 next->start = (backend_instruction *)inst->next;
216 cur_if->add_successor(mem_ctx, next);
217
218 set_next_block(&cur, next, ip);
219 break;
220
221 case BRW_OPCODE_ENDIF: {
222 if (cur->start == inst) {
223 /* New block was just created; use it. */
224 cur_endif = cur;
225 } else {
226 cur_endif = new_block();
227 cur_endif->start = inst;
228
229 cur->end = (backend_instruction *)inst->prev;
230 cur->add_successor(mem_ctx, cur_endif);
231
232 set_next_block(&cur, cur_endif, ip - 1);
233 }
234
235 if (cur_else) {
236 cur_else->add_successor(mem_ctx, cur_endif);
237 } else {
238 cur_if->add_successor(mem_ctx, cur_endif);
239 }
240
241 assert(cur_if->end->opcode == BRW_OPCODE_IF);
242 assert(!cur_else || cur_else->end->opcode == BRW_OPCODE_ELSE);
243
244 cur_if->if_block = cur_if;
245 cur_if->else_block = cur_else;
246
247 if (cur_else) {
248 cur_else->if_block = cur_if;
249 cur_else->else_block = cur_else;
250 }
251
252 cur->if_block = cur_if;
253 cur->else_block = cur_else;
254
255 /* Pop the stack so we're in the previous if/else/endif */
256 cur_if = pop_stack(&if_stack);
257 cur_else = pop_stack(&else_stack);
258 break;
259 }
260 case BRW_OPCODE_DO:
261 /* Push our information onto a stack so we can recover from
262 * nested loops.
263 */
264 do_stack.push_tail(link(mem_ctx, cur_do));
265 while_stack.push_tail(link(mem_ctx, cur_while));
266
267 /* Set up the block just after the while. Don't know when exactly
268 * it will start, yet.
269 */
270 cur_while = new_block();
271
272 if (cur->start == inst) {
273 /* New block was just created; use it. */
274 cur_do = cur;
275 } else {
276 cur_do = new_block();
277 cur_do->start = inst;
278
279 cur->end = (backend_instruction *)inst->prev;
280 cur->add_successor(mem_ctx, cur_do);
281
282 set_next_block(&cur, cur_do, ip - 1);
283 }
284 break;
285
286 case BRW_OPCODE_CONTINUE:
287 cur->add_successor(mem_ctx, cur_do);
288
289 next = new_block();
290 next->start = (backend_instruction *)inst->next;
291 if (inst->predicate)
292 cur->add_successor(mem_ctx, next);
293
294 set_next_block(&cur, next, ip);
295 break;
296
297 case BRW_OPCODE_BREAK:
298 cur->add_successor(mem_ctx, cur_while);
299
300 next = new_block();
301 next->start = (backend_instruction *)inst->next;
302 if (inst->predicate)
303 cur->add_successor(mem_ctx, next);
304
305 set_next_block(&cur, next, ip);
306 break;
307
308 case BRW_OPCODE_WHILE:
309 cur_while->start = (backend_instruction *)inst->next;
310
311 cur->add_successor(mem_ctx, cur_do);
312 set_next_block(&cur, cur_while, ip);
313
314 /* Pop the stack so we're in the previous loop */
315 cur_do = pop_stack(&do_stack);
316 cur_while = pop_stack(&while_stack);
317 break;
318
319 default:
320 break;
321 }
322 }
323
324 assert(cur->end);
325
326 cur->end_ip = ip;
327
328 make_block_array();
329 }
330
331 cfg_t::~cfg_t()
332 {
333 ralloc_free(mem_ctx);
334 }
335
336 void
337 cfg_t::remove_block(bblock_t *block)
338 {
339 foreach_list_typed_safe (bblock_link, predecessor, link, &block->parents) {
340 /* Remove block from all of its predecessors' successor lists. */
341 foreach_list_typed_safe (bblock_link, successor, link,
342 &predecessor->block->children) {
343 if (block == successor->block) {
344 successor->link.remove();
345 ralloc_free(successor);
346 }
347 }
348
349 /* Add removed-block's successors to its predecessors' successor lists. */
350 foreach_list_typed (bblock_link, successor, link, &block->children) {
351 if (!successor->block->is_successor_of(predecessor->block)) {
352 predecessor->block->children.push_tail(link(mem_ctx,
353 successor->block));
354 }
355 }
356 }
357
358 foreach_list_typed_safe (bblock_link, successor, link, &block->children) {
359 /* Remove block from all of its childrens' parents lists. */
360 foreach_list_typed_safe (bblock_link, predecessor, link,
361 &successor->block->parents) {
362 if (block == predecessor->block) {
363 predecessor->link.remove();
364 ralloc_free(predecessor);
365 }
366 }
367
368 /* Add removed-block's predecessors to its successors' predecessor lists. */
369 foreach_list_typed (bblock_link, predecessor, link, &block->parents) {
370 if (!predecessor->block->is_predecessor_of(successor->block)) {
371 successor->block->parents.push_tail(link(mem_ctx,
372 predecessor->block));
373 }
374 }
375 }
376
377 block->link.remove();
378
379 for (int b = block->num; b < this->num_blocks - 1; b++) {
380 this->blocks[b] = this->blocks[b + 1];
381 this->blocks[b]->num = b;
382 }
383
384 this->blocks[this->num_blocks - 1]->num = this->num_blocks - 2;
385 this->num_blocks--;
386 }
387
388 bblock_t *
389 cfg_t::new_block()
390 {
391 bblock_t *block = new(mem_ctx) bblock_t(this);
392
393 return block;
394 }
395
396 void
397 cfg_t::set_next_block(bblock_t **cur, bblock_t *block, int ip)
398 {
399 if (*cur) {
400 assert((*cur)->end->next == block->start);
401 (*cur)->end_ip = ip - 1;
402 }
403
404 block->start_ip = ip;
405 block->num = num_blocks++;
406 block_list.push_tail(&block->link);
407 *cur = block;
408 }
409
410 void
411 cfg_t::make_block_array()
412 {
413 blocks = ralloc_array(mem_ctx, bblock_t *, num_blocks);
414
415 int i = 0;
416 foreach_block (block, this) {
417 blocks[i++] = block;
418 }
419 assert(i == num_blocks);
420 }
421
422 void
423 cfg_t::dump(backend_visitor *v)
424 {
425 foreach_block (block, this) {
426 fprintf(stderr, "START B%d", block->num);
427 foreach_list_typed(bblock_link, link, link, &block->parents) {
428 fprintf(stderr, " <-B%d",
429 link->block->num);
430 }
431 fprintf(stderr, "\n");
432 block->dump(v);
433 fprintf(stderr, "END B%d", block->num);
434 foreach_list_typed(bblock_link, link, link, &block->children) {
435 fprintf(stderr, " ->B%d",
436 link->block->num);
437 }
438 fprintf(stderr, "\n");
439 }
440 }