Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

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