• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
pop_stack(exec_list * list)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 *
link(void * mem_ctx,bblock_t * block)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 
bblock_t(cfg_t * cfg)53 bblock_t::bblock_t(cfg_t *cfg) :
54    cfg(cfg), idom(NULL), start_ip(0), end_ip(0), num(0), cycle_count(0)
55 {
56    instructions.make_empty();
57    parents.make_empty();
58    children.make_empty();
59 }
60 
61 void
add_successor(void * mem_ctx,bblock_t * successor)62 bblock_t::add_successor(void *mem_ctx, bblock_t *successor)
63 {
64    successor->parents.push_tail(::link(mem_ctx, this));
65    children.push_tail(::link(mem_ctx, successor));
66 }
67 
68 bool
is_predecessor_of(const bblock_t * block) const69 bblock_t::is_predecessor_of(const bblock_t *block) const
70 {
71    foreach_list_typed_safe (bblock_link, parent, link, &block->parents) {
72       if (parent->block == this) {
73          return true;
74       }
75    }
76 
77    return false;
78 }
79 
80 bool
is_successor_of(const bblock_t * block) const81 bblock_t::is_successor_of(const bblock_t *block) const
82 {
83    foreach_list_typed_safe (bblock_link, child, link, &block->children) {
84       if (child->block == this) {
85          return true;
86       }
87    }
88 
89    return false;
90 }
91 
92 static bool
ends_block(const backend_instruction * inst)93 ends_block(const backend_instruction *inst)
94 {
95    enum opcode op = inst->opcode;
96 
97    return op == BRW_OPCODE_IF ||
98           op == BRW_OPCODE_ELSE ||
99           op == BRW_OPCODE_CONTINUE ||
100           op == BRW_OPCODE_BREAK ||
101           op == BRW_OPCODE_DO ||
102           op == BRW_OPCODE_WHILE;
103 }
104 
105 static bool
starts_block(const backend_instruction * inst)106 starts_block(const backend_instruction *inst)
107 {
108    enum opcode op = inst->opcode;
109 
110    return op == BRW_OPCODE_DO ||
111           op == BRW_OPCODE_ENDIF;
112 }
113 
114 bool
can_combine_with(const bblock_t * that) const115 bblock_t::can_combine_with(const bblock_t *that) const
116 {
117    if ((const bblock_t *)this->link.next != that)
118       return false;
119 
120    if (ends_block(this->end()) ||
121        starts_block(that->start()))
122       return false;
123 
124    return true;
125 }
126 
127 void
combine_with(bblock_t * that)128 bblock_t::combine_with(bblock_t *that)
129 {
130    assert(this->can_combine_with(that));
131    foreach_list_typed (bblock_link, link, link, &this->children) {
132       assert(link->block == that);
133    }
134    foreach_list_typed (bblock_link, link, link, &that->parents) {
135       assert(link->block == this);
136    }
137 
138    this->end_ip = that->end_ip;
139    this->instructions.append_list(&that->instructions);
140 
141    this->cfg->remove_block(that);
142 }
143 
144 void
dump(backend_shader * s) const145 bblock_t::dump(backend_shader *s) const
146 {
147    int ip = this->start_ip;
148    foreach_inst_in_block(backend_instruction, inst, this) {
149       fprintf(stderr, "%5d: ", ip);
150       s->dump_instruction(inst);
151       ip++;
152    }
153 }
154 
cfg_t(exec_list * instructions)155 cfg_t::cfg_t(exec_list *instructions)
156 {
157    mem_ctx = ralloc_context(NULL);
158    block_list.make_empty();
159    blocks = NULL;
160    num_blocks = 0;
161    idom_dirty = true;
162    cycle_count = 0;
163 
164    bblock_t *cur = NULL;
165    int ip = 0;
166 
167    bblock_t *entry = new_block();
168    bblock_t *cur_if = NULL;    /**< BB ending with IF. */
169    bblock_t *cur_else = NULL;  /**< BB ending with ELSE. */
170    bblock_t *cur_endif = NULL; /**< BB starting with ENDIF. */
171    bblock_t *cur_do = NULL;    /**< BB starting with DO. */
172    bblock_t *cur_while = NULL; /**< BB immediately following WHILE. */
173    exec_list if_stack, else_stack, do_stack, while_stack;
174    bblock_t *next;
175 
176    set_next_block(&cur, entry, ip);
177 
178    foreach_in_list_safe(backend_instruction, inst, instructions) {
179       /* set_next_block wants the post-incremented ip */
180       ip++;
181 
182       inst->exec_node::remove();
183 
184       switch (inst->opcode) {
185       case BRW_OPCODE_IF:
186          cur->instructions.push_tail(inst);
187 
188 	 /* Push our information onto a stack so we can recover from
189 	  * nested ifs.
190 	  */
191 	 if_stack.push_tail(link(mem_ctx, cur_if));
192 	 else_stack.push_tail(link(mem_ctx, cur_else));
193 
194 	 cur_if = cur;
195 	 cur_else = NULL;
196          cur_endif = NULL;
197 
198 	 /* Set up our immediately following block, full of "then"
199 	  * instructions.
200 	  */
201 	 next = new_block();
202 	 cur_if->add_successor(mem_ctx, next);
203 
204 	 set_next_block(&cur, next, ip);
205 	 break;
206 
207       case BRW_OPCODE_ELSE:
208          cur->instructions.push_tail(inst);
209 
210          cur_else = cur;
211 
212 	 next = new_block();
213          assert(cur_if != NULL);
214 	 cur_if->add_successor(mem_ctx, next);
215 
216 	 set_next_block(&cur, next, ip);
217 	 break;
218 
219       case BRW_OPCODE_ENDIF: {
220          if (cur->instructions.is_empty()) {
221             /* New block was just created; use it. */
222             cur_endif = cur;
223          } else {
224             cur_endif = new_block();
225 
226             cur->add_successor(mem_ctx, cur_endif);
227 
228             set_next_block(&cur, cur_endif, ip - 1);
229          }
230 
231          cur->instructions.push_tail(inst);
232 
233          if (cur_else) {
234             cur_else->add_successor(mem_ctx, cur_endif);
235          } else {
236             assert(cur_if != NULL);
237             cur_if->add_successor(mem_ctx, cur_endif);
238          }
239 
240          assert(cur_if->end()->opcode == BRW_OPCODE_IF);
241          assert(!cur_else || cur_else->end()->opcode == BRW_OPCODE_ELSE);
242 
243 	 /* Pop the stack so we're in the previous if/else/endif */
244 	 cur_if = pop_stack(&if_stack);
245 	 cur_else = pop_stack(&else_stack);
246 	 break;
247       }
248       case BRW_OPCODE_DO:
249 	 /* Push our information onto a stack so we can recover from
250 	  * nested loops.
251 	  */
252 	 do_stack.push_tail(link(mem_ctx, cur_do));
253 	 while_stack.push_tail(link(mem_ctx, cur_while));
254 
255 	 /* Set up the block just after the while.  Don't know when exactly
256 	  * it will start, yet.
257 	  */
258 	 cur_while = new_block();
259 
260          if (cur->instructions.is_empty()) {
261             /* New block was just created; use it. */
262             cur_do = cur;
263          } else {
264             cur_do = new_block();
265 
266             cur->add_successor(mem_ctx, cur_do);
267 
268             set_next_block(&cur, cur_do, ip - 1);
269          }
270 
271          cur->instructions.push_tail(inst);
272 
273          /* Represent divergent execution of the loop as a pair of alternative
274           * edges coming out of the DO instruction: For any physical iteration
275           * of the loop a given logical thread can either start off enabled
276           * (which is represented as the "next" successor), or disabled (if it
277           * has reached a non-uniform exit of the loop during a previous
278           * iteration, which is represented as the "cur_while" successor).
279           *
280           * The disabled edge will be taken by the logical thread anytime we
281           * arrive at the DO instruction through a back-edge coming from a
282           * conditional exit of the loop where divergent control flow started.
283           *
284           * This guarantees that there is a control-flow path from any
285           * divergence point of the loop into the convergence point
286           * (immediately past the WHILE instruction) such that it overlaps the
287           * whole IP region of divergent control flow (potentially the whole
288           * loop) *and* doesn't imply the execution of any instructions part
289           * of the loop (since the corresponding execution mask bit will be
290           * disabled for a diverging thread).
291           *
292           * This way we make sure that any variables that are live throughout
293           * the region of divergence for an inactive logical thread are also
294           * considered to interfere with any other variables assigned by
295           * active logical threads within the same physical region of the
296           * program, since otherwise we would risk cross-channel data
297           * corruption.
298           */
299          next = new_block();
300          cur->add_successor(mem_ctx, next);
301          cur->add_successor(mem_ctx, cur_while);
302          set_next_block(&cur, next, ip);
303 	 break;
304 
305       case BRW_OPCODE_CONTINUE:
306          cur->instructions.push_tail(inst);
307 
308          /* A conditional CONTINUE may start a region of divergent control
309           * flow until the start of the next loop iteration (*not* until the
310           * end of the loop which is why the successor is not the top-level
311           * divergence point at cur_do).  The live interval of any variable
312           * extending through a CONTINUE edge is guaranteed to overlap the
313           * whole region of divergent execution, because any variable live-out
314           * at the CONTINUE instruction will also be live-in at the top of the
315           * loop, and therefore also live-out at the bottom-most point of the
316           * loop which is reachable from the top (since a control flow path
317           * exists from a definition of the variable through this CONTINUE
318           * instruction, the top of the loop, the (reachable) bottom of the
319           * loop, the top of the loop again, into a use of the variable).
320           */
321          assert(cur_do != NULL);
322          cur->add_successor(mem_ctx, cur_do->next());
323 
324 	 next = new_block();
325 	 if (inst->predicate)
326 	    cur->add_successor(mem_ctx, next);
327 
328 	 set_next_block(&cur, next, ip);
329 	 break;
330 
331       case BRW_OPCODE_BREAK:
332          cur->instructions.push_tail(inst);
333 
334          /* A conditional BREAK instruction may start a region of divergent
335           * control flow until the end of the loop if the condition is
336           * non-uniform, in which case the loop will execute additional
337           * iterations with the present channel disabled.  We model this as a
338           * control flow path from the divergence point to the convergence
339           * point that overlaps the whole IP range of the loop and skips over
340           * the execution of any other instructions part of the loop.
341           *
342           * See the DO case for additional explanation.
343           */
344          assert(cur_do != NULL);
345          cur->add_successor(mem_ctx, cur_do);
346 
347 	 next = new_block();
348 	 if (inst->predicate)
349 	    cur->add_successor(mem_ctx, next);
350 
351 	 set_next_block(&cur, next, ip);
352 	 break;
353 
354       case BRW_OPCODE_WHILE:
355          cur->instructions.push_tail(inst);
356 
357          assert(cur_do != NULL && cur_while != NULL);
358 
359          /* A conditional WHILE instruction may start a region of divergent
360           * control flow until the end of the loop, just like the BREAK
361           * instruction.  See the BREAK case for more details.  OTOH an
362           * unconditional WHILE instruction is non-divergent (just like an
363           * unconditional CONTINUE), and will necessarily lead to the
364           * execution of an additional iteration of the loop for all enabled
365           * channels, so we may skip over the divergence point at the top of
366           * the loop to keep the CFG as unambiguous as possible.
367           */
368          cur->add_successor(mem_ctx, inst->predicate ? cur_do :
369                                      cur_do->next());
370 
371 	 set_next_block(&cur, cur_while, ip);
372 
373 	 /* Pop the stack so we're in the previous loop */
374 	 cur_do = pop_stack(&do_stack);
375 	 cur_while = pop_stack(&while_stack);
376 	 break;
377 
378       default:
379          cur->instructions.push_tail(inst);
380 	 break;
381       }
382    }
383 
384    cur->end_ip = ip - 1;
385 
386    make_block_array();
387 }
388 
~cfg_t()389 cfg_t::~cfg_t()
390 {
391    ralloc_free(mem_ctx);
392 }
393 
394 void
remove_block(bblock_t * block)395 cfg_t::remove_block(bblock_t *block)
396 {
397    foreach_list_typed_safe (bblock_link, predecessor, link, &block->parents) {
398       /* Remove block from all of its predecessors' successor lists. */
399       foreach_list_typed_safe (bblock_link, successor, link,
400                                &predecessor->block->children) {
401          if (block == successor->block) {
402             successor->link.remove();
403             ralloc_free(successor);
404          }
405       }
406 
407       /* Add removed-block's successors to its predecessors' successor lists. */
408       foreach_list_typed (bblock_link, successor, link, &block->children) {
409          if (!successor->block->is_successor_of(predecessor->block)) {
410             predecessor->block->children.push_tail(link(mem_ctx,
411                                                         successor->block));
412          }
413       }
414    }
415 
416    foreach_list_typed_safe (bblock_link, successor, link, &block->children) {
417       /* Remove block from all of its childrens' parents lists. */
418       foreach_list_typed_safe (bblock_link, predecessor, link,
419                                &successor->block->parents) {
420          if (block == predecessor->block) {
421             predecessor->link.remove();
422             ralloc_free(predecessor);
423          }
424       }
425 
426       /* Add removed-block's predecessors to its successors' predecessor lists. */
427       foreach_list_typed (bblock_link, predecessor, link, &block->parents) {
428          if (!predecessor->block->is_predecessor_of(successor->block)) {
429             successor->block->parents.push_tail(link(mem_ctx,
430                                                      predecessor->block));
431          }
432       }
433    }
434 
435    block->link.remove();
436 
437    for (int b = block->num; b < this->num_blocks - 1; b++) {
438       this->blocks[b] = this->blocks[b + 1];
439       this->blocks[b]->num = b;
440    }
441 
442    this->blocks[this->num_blocks - 1]->num = this->num_blocks - 2;
443    this->num_blocks--;
444    idom_dirty = true;
445 }
446 
447 bblock_t *
new_block()448 cfg_t::new_block()
449 {
450    bblock_t *block = new(mem_ctx) bblock_t(this);
451 
452    return block;
453 }
454 
455 void
set_next_block(bblock_t ** cur,bblock_t * block,int ip)456 cfg_t::set_next_block(bblock_t **cur, bblock_t *block, int ip)
457 {
458    if (*cur) {
459       (*cur)->end_ip = ip - 1;
460    }
461 
462    block->start_ip = ip;
463    block->num = num_blocks++;
464    block_list.push_tail(&block->link);
465    *cur = block;
466 }
467 
468 void
make_block_array()469 cfg_t::make_block_array()
470 {
471    blocks = ralloc_array(mem_ctx, bblock_t *, num_blocks);
472 
473    int i = 0;
474    foreach_block (block, this) {
475       blocks[i++] = block;
476    }
477    assert(i == num_blocks);
478 }
479 
480 void
dump(backend_shader * s)481 cfg_t::dump(backend_shader *s)
482 {
483    if (idom_dirty)
484       calculate_idom();
485 
486    foreach_block (block, this) {
487       if (block->idom)
488          fprintf(stderr, "START B%d IDOM(B%d)", block->num, block->idom->num);
489       else
490          fprintf(stderr, "START B%d IDOM(none)", block->num);
491 
492       foreach_list_typed(bblock_link, link, link, &block->parents) {
493          fprintf(stderr, " <-B%d",
494                  link->block->num);
495       }
496       fprintf(stderr, "\n");
497       if (s != NULL)
498          block->dump(s);
499       fprintf(stderr, "END B%d", block->num);
500       foreach_list_typed(bblock_link, link, link, &block->children) {
501          fprintf(stderr, " ->B%d",
502                  link->block->num);
503       }
504       fprintf(stderr, "\n");
505    }
506 }
507 
508 /* Calculates the immediate dominator of each block, according to "A Simple,
509  * Fast Dominance Algorithm" by Keith D. Cooper, Timothy J. Harvey, and Ken
510  * Kennedy.
511  *
512  * The authors claim that for control flow graphs of sizes normally encountered
513  * (less than 1000 nodes) that this algorithm is significantly faster than
514  * others like Lengauer-Tarjan.
515  */
516 void
calculate_idom()517 cfg_t::calculate_idom()
518 {
519    foreach_block(block, this) {
520       block->idom = NULL;
521    }
522    blocks[0]->idom = blocks[0];
523 
524    bool changed;
525    do {
526       changed = false;
527 
528       foreach_block(block, this) {
529          if (block->num == 0)
530             continue;
531 
532          bblock_t *new_idom = NULL;
533          foreach_list_typed(bblock_link, parent, link, &block->parents) {
534             if (parent->block->idom) {
535                if (new_idom == NULL) {
536                   new_idom = parent->block;
537                } else if (parent->block->idom != NULL) {
538                   new_idom = intersect(parent->block, new_idom);
539                }
540             }
541          }
542 
543          if (block->idom != new_idom) {
544             block->idom = new_idom;
545             changed = true;
546          }
547       }
548    } while (changed);
549 
550    idom_dirty = false;
551 }
552 
553 bblock_t *
intersect(bblock_t * b1,bblock_t * b2)554 cfg_t::intersect(bblock_t *b1, bblock_t *b2)
555 {
556    /* Note, the comparisons here are the opposite of what the paper says
557     * because we index blocks from beginning -> end (i.e. reverse post-order)
558     * instead of post-order like they assume.
559     */
560    while (b1->num != b2->num) {
561       while (b1->num > b2->num)
562          b1 = b1->idom;
563       while (b2->num > b1->num)
564          b2 = b2->idom;
565    }
566    assert(b1);
567    return b1;
568 }
569 
570 void
dump_cfg()571 cfg_t::dump_cfg()
572 {
573    printf("digraph CFG {\n");
574    for (int b = 0; b < num_blocks; b++) {
575       bblock_t *block = this->blocks[b];
576 
577       foreach_list_typed_safe (bblock_link, child, link, &block->children) {
578          printf("\t%d -> %d\n", b, child->block->num);
579       }
580    }
581    printf("}\n");
582 }
583 
584 void
dump_domtree()585 cfg_t::dump_domtree()
586 {
587    printf("digraph DominanceTree {\n");
588    foreach_block(block, this) {
589       if (block->idom) {
590          printf("\t%d -> %d\n", block->idom->num, block->num);
591       }
592    }
593    printf("}\n");
594 }
595