Home | History | Annotate | Download | only in i965
      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 (at) 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), 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
     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
     69 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
     81 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
     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_WHILE;
    102 }
    103 
    104 static bool
    105 starts_block(const backend_instruction *inst)
    106 {
    107    enum opcode op = inst->opcode;
    108 
    109    return op == BRW_OPCODE_DO ||
    110           op == BRW_OPCODE_ENDIF;
    111 }
    112 
    113 bool
    114 bblock_t::can_combine_with(const bblock_t *that) const
    115 {
    116    if ((const bblock_t *)this->link.next != that)
    117       return false;
    118 
    119    if (ends_block(this->end()) ||
    120        starts_block(that->start()))
    121       return false;
    122 
    123    return true;
    124 }
    125 
    126 void
    127 bblock_t::combine_with(bblock_t *that)
    128 {
    129    assert(this->can_combine_with(that));
    130    foreach_list_typed (bblock_link, link, link, &this->children) {
    131       assert(link->block == that);
    132    }
    133    foreach_list_typed (bblock_link, link, link, &that->parents) {
    134       assert(link->block == this);
    135    }
    136 
    137    this->end_ip = that->end_ip;
    138    this->instructions.append_list(&that->instructions);
    139 
    140    this->cfg->remove_block(that);
    141 }
    142 
    143 void
    144 bblock_t::dump(backend_shader *s) const
    145 {
    146    int ip = this->start_ip;
    147    foreach_inst_in_block(backend_instruction, inst, this) {
    148       fprintf(stderr, "%5d: ", ip);
    149       s->dump_instruction(inst);
    150       ip++;
    151    }
    152 }
    153 
    154 cfg_t::cfg_t(exec_list *instructions)
    155 {
    156    mem_ctx = ralloc_context(NULL);
    157    block_list.make_empty();
    158    blocks = NULL;
    159    num_blocks = 0;
    160    idom_dirty = true;
    161    cycle_count = 0;
    162 
    163    bblock_t *cur = NULL;
    164    int ip = 0;
    165 
    166    bblock_t *entry = new_block();
    167    bblock_t *cur_if = NULL;    /**< BB ending with IF. */
    168    bblock_t *cur_else = NULL;  /**< BB ending with ELSE. */
    169    bblock_t *cur_endif = NULL; /**< BB starting with ENDIF. */
    170    bblock_t *cur_do = NULL;    /**< BB starting with DO. */
    171    bblock_t *cur_while = NULL; /**< BB immediately following WHILE. */
    172    exec_list if_stack, else_stack, do_stack, while_stack;
    173    bblock_t *next;
    174 
    175    set_next_block(&cur, entry, ip);
    176 
    177    foreach_in_list_safe(backend_instruction, inst, instructions) {
    178       /* set_next_block wants the post-incremented ip */
    179       ip++;
    180 
    181       inst->exec_node::remove();
    182 
    183       switch (inst->opcode) {
    184       case BRW_OPCODE_IF:
    185          cur->instructions.push_tail(inst);
    186 
    187 	 /* Push our information onto a stack so we can recover from
    188 	  * nested ifs.
    189 	  */
    190 	 if_stack.push_tail(link(mem_ctx, cur_if));
    191 	 else_stack.push_tail(link(mem_ctx, cur_else));
    192 
    193 	 cur_if = cur;
    194 	 cur_else = NULL;
    195          cur_endif = NULL;
    196 
    197 	 /* Set up our immediately following block, full of "then"
    198 	  * instructions.
    199 	  */
    200 	 next = new_block();
    201 	 cur_if->add_successor(mem_ctx, next);
    202 
    203 	 set_next_block(&cur, next, ip);
    204 	 break;
    205 
    206       case BRW_OPCODE_ELSE:
    207          cur->instructions.push_tail(inst);
    208 
    209          cur_else = cur;
    210 
    211 	 next = new_block();
    212          assert(cur_if != NULL);
    213 	 cur_if->add_successor(mem_ctx, next);
    214 
    215 	 set_next_block(&cur, next, ip);
    216 	 break;
    217 
    218       case BRW_OPCODE_ENDIF: {
    219          if (cur->instructions.is_empty()) {
    220             /* New block was just created; use it. */
    221             cur_endif = cur;
    222          } else {
    223             cur_endif = new_block();
    224 
    225             cur->add_successor(mem_ctx, cur_endif);
    226 
    227             set_next_block(&cur, cur_endif, ip - 1);
    228          }
    229 
    230          cur->instructions.push_tail(inst);
    231 
    232          if (cur_else) {
    233             cur_else->add_successor(mem_ctx, cur_endif);
    234          } else {
    235             assert(cur_if != NULL);
    236             cur_if->add_successor(mem_ctx, cur_endif);
    237          }
    238 
    239          assert(cur_if->end()->opcode == BRW_OPCODE_IF);
    240          assert(!cur_else || cur_else->end()->opcode == BRW_OPCODE_ELSE);
    241 
    242 	 /* Pop the stack so we're in the previous if/else/endif */
    243 	 cur_if = pop_stack(&if_stack);
    244 	 cur_else = pop_stack(&else_stack);
    245 	 break;
    246       }
    247       case BRW_OPCODE_DO:
    248 	 /* Push our information onto a stack so we can recover from
    249 	  * nested loops.
    250 	  */
    251 	 do_stack.push_tail(link(mem_ctx, cur_do));
    252 	 while_stack.push_tail(link(mem_ctx, cur_while));
    253 
    254 	 /* Set up the block just after the while.  Don't know when exactly
    255 	  * it will start, yet.
    256 	  */
    257 	 cur_while = new_block();
    258 
    259          if (cur->instructions.is_empty()) {
    260             /* New block was just created; use it. */
    261             cur_do = cur;
    262          } else {
    263             cur_do = new_block();
    264 
    265             cur->add_successor(mem_ctx, cur_do);
    266 
    267             set_next_block(&cur, cur_do, ip - 1);
    268          }
    269 
    270          cur->instructions.push_tail(inst);
    271 	 break;
    272 
    273       case BRW_OPCODE_CONTINUE:
    274          cur->instructions.push_tail(inst);
    275 
    276          assert(cur_do != NULL);
    277 	 cur->add_successor(mem_ctx, cur_do);
    278 
    279 	 next = new_block();
    280 	 if (inst->predicate)
    281 	    cur->add_successor(mem_ctx, next);
    282 
    283 	 set_next_block(&cur, next, ip);
    284 	 break;
    285 
    286       case BRW_OPCODE_BREAK:
    287          cur->instructions.push_tail(inst);
    288 
    289          assert(cur_while != NULL);
    290 	 cur->add_successor(mem_ctx, cur_while);
    291 
    292 	 next = new_block();
    293 	 if (inst->predicate)
    294 	    cur->add_successor(mem_ctx, next);
    295 
    296 	 set_next_block(&cur, next, ip);
    297 	 break;
    298 
    299       case BRW_OPCODE_WHILE:
    300          cur->instructions.push_tail(inst);
    301 
    302          assert(cur_do != NULL && cur_while != NULL);
    303 	 cur->add_successor(mem_ctx, cur_do);
    304 
    305          if (inst->predicate)
    306             cur->add_successor(mem_ctx, cur_while);
    307 
    308 	 set_next_block(&cur, cur_while, ip);
    309 
    310 	 /* Pop the stack so we're in the previous loop */
    311 	 cur_do = pop_stack(&do_stack);
    312 	 cur_while = pop_stack(&while_stack);
    313 	 break;
    314 
    315       default:
    316          cur->instructions.push_tail(inst);
    317 	 break;
    318       }
    319    }
    320 
    321    cur->end_ip = ip - 1;
    322 
    323    make_block_array();
    324 }
    325 
    326 cfg_t::~cfg_t()
    327 {
    328    ralloc_free(mem_ctx);
    329 }
    330 
    331 void
    332 cfg_t::remove_block(bblock_t *block)
    333 {
    334    foreach_list_typed_safe (bblock_link, predecessor, link, &block->parents) {
    335       /* Remove block from all of its predecessors' successor lists. */
    336       foreach_list_typed_safe (bblock_link, successor, link,
    337                                &predecessor->block->children) {
    338          if (block == successor->block) {
    339             successor->link.remove();
    340             ralloc_free(successor);
    341          }
    342       }
    343 
    344       /* Add removed-block's successors to its predecessors' successor lists. */
    345       foreach_list_typed (bblock_link, successor, link, &block->children) {
    346          if (!successor->block->is_successor_of(predecessor->block)) {
    347             predecessor->block->children.push_tail(link(mem_ctx,
    348                                                         successor->block));
    349          }
    350       }
    351    }
    352 
    353    foreach_list_typed_safe (bblock_link, successor, link, &block->children) {
    354       /* Remove block from all of its childrens' parents lists. */
    355       foreach_list_typed_safe (bblock_link, predecessor, link,
    356                                &successor->block->parents) {
    357          if (block == predecessor->block) {
    358             predecessor->link.remove();
    359             ralloc_free(predecessor);
    360          }
    361       }
    362 
    363       /* Add removed-block's predecessors to its successors' predecessor lists. */
    364       foreach_list_typed (bblock_link, predecessor, link, &block->parents) {
    365          if (!predecessor->block->is_predecessor_of(successor->block)) {
    366             successor->block->parents.push_tail(link(mem_ctx,
    367                                                      predecessor->block));
    368          }
    369       }
    370    }
    371 
    372    block->link.remove();
    373 
    374    for (int b = block->num; b < this->num_blocks - 1; b++) {
    375       this->blocks[b] = this->blocks[b + 1];
    376       this->blocks[b]->num = b;
    377    }
    378 
    379    this->blocks[this->num_blocks - 1]->num = this->num_blocks - 2;
    380    this->num_blocks--;
    381    idom_dirty = true;
    382 }
    383 
    384 bblock_t *
    385 cfg_t::new_block()
    386 {
    387    bblock_t *block = new(mem_ctx) bblock_t(this);
    388 
    389    return block;
    390 }
    391 
    392 void
    393 cfg_t::set_next_block(bblock_t **cur, bblock_t *block, int ip)
    394 {
    395    if (*cur) {
    396       (*cur)->end_ip = ip - 1;
    397    }
    398 
    399    block->start_ip = ip;
    400    block->num = num_blocks++;
    401    block_list.push_tail(&block->link);
    402    *cur = block;
    403 }
    404 
    405 void
    406 cfg_t::make_block_array()
    407 {
    408    blocks = ralloc_array(mem_ctx, bblock_t *, num_blocks);
    409 
    410    int i = 0;
    411    foreach_block (block, this) {
    412       blocks[i++] = block;
    413    }
    414    assert(i == num_blocks);
    415 }
    416 
    417 void
    418 cfg_t::dump(backend_shader *s)
    419 {
    420    if (idom_dirty)
    421       calculate_idom();
    422 
    423    foreach_block (block, this) {
    424       if (block->idom)
    425          fprintf(stderr, "START B%d IDOM(B%d)", block->num, block->idom->num);
    426       else
    427          fprintf(stderr, "START B%d IDOM(none)", block->num);
    428 
    429       foreach_list_typed(bblock_link, link, link, &block->parents) {
    430          fprintf(stderr, " <-B%d",
    431                  link->block->num);
    432       }
    433       fprintf(stderr, "\n");
    434       if (s != NULL)
    435          block->dump(s);
    436       fprintf(stderr, "END B%d", block->num);
    437       foreach_list_typed(bblock_link, link, link, &block->children) {
    438          fprintf(stderr, " ->B%d",
    439                  link->block->num);
    440       }
    441       fprintf(stderr, "\n");
    442    }
    443 }
    444 
    445 /* Calculates the immediate dominator of each block, according to "A Simple,
    446  * Fast Dominance Algorithm" by Keith D. Cooper, Timothy J. Harvey, and Ken
    447  * Kennedy.
    448  *
    449  * The authors claim that for control flow graphs of sizes normally encountered
    450  * (less than 1000 nodes) that this algorithm is significantly faster than
    451  * others like Lengauer-Tarjan.
    452  */
    453 void
    454 cfg_t::calculate_idom()
    455 {
    456    foreach_block(block, this) {
    457       block->idom = NULL;
    458    }
    459    blocks[0]->idom = blocks[0];
    460 
    461    bool changed;
    462    do {
    463       changed = false;
    464 
    465       foreach_block(block, this) {
    466          if (block->num == 0)
    467             continue;
    468 
    469          bblock_t *new_idom = NULL;
    470          foreach_list_typed(bblock_link, parent, link, &block->parents) {
    471             if (parent->block->idom) {
    472                if (new_idom == NULL) {
    473                   new_idom = parent->block;
    474                } else if (parent->block->idom != NULL) {
    475                   new_idom = intersect(parent->block, new_idom);
    476                }
    477             }
    478          }
    479 
    480          if (block->idom != new_idom) {
    481             block->idom = new_idom;
    482             changed = true;
    483          }
    484       }
    485    } while (changed);
    486 
    487    idom_dirty = false;
    488 }
    489 
    490 bblock_t *
    491 cfg_t::intersect(bblock_t *b1, bblock_t *b2)
    492 {
    493    /* Note, the comparisons here are the opposite of what the paper says
    494     * because we index blocks from beginning -> end (i.e. reverse post-order)
    495     * instead of post-order like they assume.
    496     */
    497    while (b1->num != b2->num) {
    498       while (b1->num > b2->num)
    499          b1 = b1->idom;
    500       while (b2->num > b1->num)
    501          b2 = b2->idom;
    502    }
    503    assert(b1);
    504    return b1;
    505 }
    506 
    507 void
    508 cfg_t::dump_cfg()
    509 {
    510    printf("digraph CFG {\n");
    511    for (int b = 0; b < num_blocks; b++) {
    512       bblock_t *block = this->blocks[b];
    513 
    514       foreach_list_typed_safe (bblock_link, child, link, &block->children) {
    515          printf("\t%d -> %d\n", b, child->block->num);
    516       }
    517    }
    518    printf("}\n");
    519 }
    520 
    521 void
    522 cfg_t::dump_domtree()
    523 {
    524    printf("digraph DominanceTree {\n");
    525    foreach_block(block, this) {
    526       if (block->idom) {
    527          printf("\t%d -> %d\n", block->idom->num, block->num);
    528       }
    529    }
    530    printf("}\n");
    531 }
    532