1 /*
2 * Copyright © 2010 Intel Corporation
3 * Copyright © 2014-2015 Broadcom
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 /**
26 * @file vc4_qir_schedule.c
27 *
28 * The basic model of the list scheduler is to take a basic block, compute a
29 * DAG of the dependencies from the bottom up, and make a list of the DAG
30 * heads. Heuristically pick a DAG head and schedule (remove) it, then put
31 * all the parents that are now DAG heads into the list of things to
32 * schedule.
33 *
34 * The goal of scheduling here, before register allocation and conversion to
35 * QPU instructions, is to reduce register pressure by reordering instructions
36 * to consume values when possible.
37 */
38
39 #include "vc4_qir.h"
40
41 static bool debug;
42
43 struct schedule_node {
44 struct list_head link;
45 struct qinst *inst;
46
47 struct schedule_node **children;
48 uint32_t child_count;
49 uint32_t child_array_size;
50 uint32_t parent_count;
51
52 /* Length of the longest (latency) chain from a DAG head to the this
53 * instruction.
54 */
55 uint32_t delay;
56
57 /* Longest time + latency_between(parent, this) of any parent of this
58 * node.
59 */
60 uint32_t unblocked_time;
61 };
62
63 struct schedule_state {
64 /* List of struct schedule_node *. This starts out with all
65 * instructions, and after dependency updates it's trimmed to be just
66 * the DAG heads.
67 */
68 struct list_head worklist;
69
70 uint32_t time;
71
72 uint32_t *temp_writes;
73
74 BITSET_WORD *temp_live;
75 };
76
77 /* When walking the instructions in reverse, we need to swap before/after in
78 * add_dep().
79 */
80 enum direction { F, R };
81
82 /**
83 * Marks a dependency between two intructions, that \p after must appear after
84 * \p before.
85 *
86 * Our dependencies are tracked as a DAG. Since we're scheduling bottom-up,
87 * the latest instructions with nothing left to schedule are the DAG heads,
88 * and their inputs are their children.
89 */
90 static void
add_dep(enum direction dir,struct schedule_node * before,struct schedule_node * after)91 add_dep(enum direction dir,
92 struct schedule_node *before,
93 struct schedule_node *after)
94 {
95 if (!before || !after)
96 return;
97
98 assert(before != after);
99
100 if (dir == R) {
101 struct schedule_node *t = before;
102 before = after;
103 after = t;
104 }
105
106 for (int i = 0; i < after->child_count; i++) {
107 if (after->children[i] == after)
108 return;
109 }
110
111 if (after->child_array_size <= after->child_count) {
112 after->child_array_size = MAX2(after->child_array_size * 2, 16);
113 after->children = reralloc(after, after->children,
114 struct schedule_node *,
115 after->child_array_size);
116 }
117
118 after->children[after->child_count] = before;
119 after->child_count++;
120 before->parent_count++;
121 }
122
123 static void
add_write_dep(enum direction dir,struct schedule_node ** before,struct schedule_node * after)124 add_write_dep(enum direction dir,
125 struct schedule_node **before,
126 struct schedule_node *after)
127 {
128 add_dep(dir, *before, after);
129 *before = after;
130 }
131
132 struct schedule_setup_state {
133 struct schedule_node **last_temp_write;
134 struct schedule_node *last_sf;
135 struct schedule_node *last_vary_read;
136 struct schedule_node *last_vpm_read;
137 struct schedule_node *last_vpm_write;
138 struct schedule_node *last_tex_coord;
139 struct schedule_node *last_tex_result;
140 struct schedule_node *last_tlb;
141 struct schedule_node *last_uniforms_reset;
142 enum direction dir;
143
144 /**
145 * Texture FIFO tracking. This is done top-to-bottom, and is used to
146 * track the QOP_TEX_RESULTs and add dependencies on previous ones
147 * when trying to submit texture coords with TFREQ full or new texture
148 * fetches with TXRCV full.
149 */
150 struct {
151 struct schedule_node *node;
152 int coords;
153 } tex_fifo[8];
154 int tfreq_count; /**< Number of texture coords outstanding. */
155 int tfrcv_count; /**< Number of texture results outstanding. */
156 int tex_fifo_pos;
157 };
158
159 static void
block_until_tex_result(struct schedule_setup_state * state,struct schedule_node * n)160 block_until_tex_result(struct schedule_setup_state *state, struct schedule_node *n)
161 {
162 add_dep(state->dir, state->tex_fifo[0].node, n);
163
164 state->tfreq_count -= state->tex_fifo[0].coords;
165 state->tfrcv_count--;
166
167 memmove(&state->tex_fifo[0],
168 &state->tex_fifo[1],
169 state->tex_fifo_pos * sizeof(state->tex_fifo[0]));
170 state->tex_fifo_pos--;
171 }
172
173 /**
174 * Common code for dependencies that need to be tracked both forward and
175 * backward.
176 *
177 * This is for things like "all VPM reads have to happen in order."
178 */
179 static void
calculate_deps(struct schedule_setup_state * state,struct schedule_node * n)180 calculate_deps(struct schedule_setup_state *state, struct schedule_node *n)
181 {
182 struct qinst *inst = n->inst;
183 enum direction dir = state->dir;
184
185
186 /* Add deps for temp registers and varyings accesses. Note that we
187 * ignore uniforms accesses, because qir_reorder_uniforms() happens
188 * after this.
189 */
190 for (int i = 0; i < qir_get_nsrc(inst); i++) {
191 switch (inst->src[i].file) {
192 case QFILE_TEMP:
193 add_dep(dir,
194 state->last_temp_write[inst->src[i].index], n);
195 break;
196
197 case QFILE_VARY:
198 add_write_dep(dir, &state->last_vary_read, n);
199 break;
200
201 case QFILE_VPM:
202 add_write_dep(dir, &state->last_vpm_read, n);
203 break;
204
205 default:
206 break;
207 }
208 }
209
210 switch (inst->op) {
211 case QOP_VARY_ADD_C:
212 add_dep(dir, state->last_vary_read, n);
213 break;
214
215 case QOP_TEX_RESULT:
216 /* Results have to be fetched in order. */
217 add_write_dep(dir, &state->last_tex_result, n);
218 break;
219
220 case QOP_THRSW:
221 /* After a new THRSW, one must collect all texture samples
222 * queued since the previous THRSW/program start. For now, we
223 * have one THRSW in between each texture setup and its
224 * results collection as our input, and we just make sure that
225 * that ordering is maintained.
226 */
227 add_write_dep(dir, &state->last_tex_coord, n);
228 add_write_dep(dir, &state->last_tex_result, n);
229
230 /* accumulators and flags are lost across thread switches. */
231 add_write_dep(dir, &state->last_sf, n);
232
233 /* Setup, like the varyings, will need to be drained before we
234 * thread switch.
235 */
236 add_write_dep(dir, &state->last_vary_read, n);
237
238 /* The TLB-locking operations have to stay after the last
239 * thread switch.
240 */
241 add_write_dep(dir, &state->last_tlb, n);
242 break;
243
244 case QOP_TLB_COLOR_READ:
245 case QOP_MS_MASK:
246 add_write_dep(dir, &state->last_tlb, n);
247 break;
248
249 default:
250 break;
251 }
252
253 switch (inst->dst.file) {
254 case QFILE_VPM:
255 add_write_dep(dir, &state->last_vpm_write, n);
256 break;
257
258 case QFILE_TEMP:
259 add_write_dep(dir, &state->last_temp_write[inst->dst.index], n);
260 break;
261
262 case QFILE_TLB_COLOR_WRITE:
263 case QFILE_TLB_COLOR_WRITE_MS:
264 case QFILE_TLB_Z_WRITE:
265 case QFILE_TLB_STENCIL_SETUP:
266 add_write_dep(dir, &state->last_tlb, n);
267 break;
268
269 case QFILE_TEX_S_DIRECT:
270 case QFILE_TEX_S:
271 case QFILE_TEX_T:
272 case QFILE_TEX_R:
273 case QFILE_TEX_B:
274 /* Texturing setup gets scheduled in order, because
275 * the uniforms referenced by them have to land in a
276 * specific order.
277 */
278 add_write_dep(dir, &state->last_tex_coord, n);
279 break;
280
281 default:
282 break;
283 }
284
285 if (qir_depends_on_flags(inst))
286 add_dep(dir, state->last_sf, n);
287
288 if (inst->sf)
289 add_write_dep(dir, &state->last_sf, n);
290 }
291
292 static void
calculate_forward_deps(struct vc4_compile * c,void * mem_ctx,struct list_head * schedule_list)293 calculate_forward_deps(struct vc4_compile *c, void *mem_ctx,
294 struct list_head *schedule_list)
295 {
296 struct schedule_setup_state state;
297
298 memset(&state, 0, sizeof(state));
299 state.last_temp_write = rzalloc_array(mem_ctx, struct schedule_node *,
300 c->num_temps);
301 state.dir = F;
302
303 list_for_each_entry(struct schedule_node, n, schedule_list, link) {
304 struct qinst *inst = n->inst;
305
306 calculate_deps(&state, n);
307
308 for (int i = 0; i < qir_get_nsrc(inst); i++) {
309 switch (inst->src[i].file) {
310 case QFILE_UNIF:
311 add_dep(state.dir, state.last_uniforms_reset, n);
312 break;
313 default:
314 break;
315 }
316 }
317
318 switch (inst->dst.file) {
319 case QFILE_TEX_S_DIRECT:
320 case QFILE_TEX_S:
321 case QFILE_TEX_T:
322 case QFILE_TEX_R:
323 case QFILE_TEX_B:
324 /* From the VC4 spec:
325 *
326 * "The TFREQ input FIFO holds two full lots of s,
327 * t, r, b data, plus associated setup data, per
328 * QPU, that is, there are eight data slots. For
329 * each texture request, slots are only consumed
330 * for the components of s, t, r, and b actually
331 * written. Thus the FIFO can hold four requests
332 * of just (s, t) data, or eight requests of just
333 * s data (for direct addressed data lookups).
334 *
335 * Note that there is one FIFO per QPU, and the
336 * FIFO has no concept of threads - that is,
337 * multi-threaded shaders must be careful to use
338 * only 1/2 the FIFO depth before reading
339 * back. Multi-threaded programs must also
340 * therefore always thread switch on texture
341 * fetch as the other thread may have data
342 * waiting in the FIFO."
343 *
344 * If the texture coordinate fifo is full, block this
345 * on the last QOP_TEX_RESULT.
346 */
347 if (state.tfreq_count == (c->fs_threaded ? 4 : 8)) {
348 block_until_tex_result(&state, n);
349 }
350
351 /* From the VC4 spec:
352 *
353 * "Since the maximum number of texture requests
354 * in the input (TFREQ) FIFO is four lots of (s,
355 * t) data, the output (TFRCV) FIFO is sized to
356 * holds four lots of max-size color data per
357 * QPU. For non-float color, reads are packed
358 * RGBA8888 data (one read per pixel). For 16-bit
359 * float color, two reads are necessary per
360 * pixel, with reads packed as RG1616 then
361 * BA1616. So per QPU there are eight color slots
362 * in the TFRCV FIFO."
363 *
364 * If the texture result fifo is full, block adding
365 * any more to it until the last QOP_TEX_RESULT.
366 */
367 if (inst->dst.file == QFILE_TEX_S ||
368 inst->dst.file == QFILE_TEX_S_DIRECT) {
369 if (state.tfrcv_count ==
370 (c->fs_threaded ? 2 : 4))
371 block_until_tex_result(&state, n);
372 state.tfrcv_count++;
373 }
374
375 state.tex_fifo[state.tex_fifo_pos].coords++;
376 state.tfreq_count++;
377 break;
378
379 default:
380 break;
381 }
382
383 switch (inst->op) {
384 case QOP_TEX_RESULT:
385 /* Results have to be fetched after the
386 * coordinate setup. Note that we're assuming
387 * here that our input shader has the texture
388 * coord setup and result fetch in order,
389 * which is true initially but not of our
390 * instruction stream after this pass.
391 */
392 add_dep(state.dir, state.last_tex_coord, n);
393
394 state.tex_fifo[state.tex_fifo_pos].node = n;
395
396 state.tex_fifo_pos++;
397 memset(&state.tex_fifo[state.tex_fifo_pos], 0,
398 sizeof(state.tex_fifo[0]));
399 break;
400
401 case QOP_UNIFORMS_RESET:
402 add_write_dep(state.dir, &state.last_uniforms_reset, n);
403 break;
404
405 default:
406 break;
407 }
408 }
409 }
410
411 static void
calculate_reverse_deps(struct vc4_compile * c,void * mem_ctx,struct list_head * schedule_list)412 calculate_reverse_deps(struct vc4_compile *c, void *mem_ctx,
413 struct list_head *schedule_list)
414 {
415 struct schedule_setup_state state;
416
417 memset(&state, 0, sizeof(state));
418 state.dir = R;
419 state.last_temp_write = rzalloc_array(mem_ctx, struct schedule_node *,
420 c->num_temps);
421
422 list_for_each_entry_rev(struct schedule_node, n, schedule_list, link) {
423 calculate_deps(&state, n);
424 }
425 }
426
427 static int
get_register_pressure_cost(struct schedule_state * state,struct qinst * inst)428 get_register_pressure_cost(struct schedule_state *state, struct qinst *inst)
429 {
430 int cost = 0;
431
432 if (inst->dst.file == QFILE_TEMP &&
433 state->temp_writes[inst->dst.index] == 1)
434 cost--;
435
436 for (int i = 0; i < qir_get_nsrc(inst); i++) {
437 if (inst->src[i].file != QFILE_TEMP ||
438 BITSET_TEST(state->temp_live, inst->src[i].index)) {
439 continue;
440 }
441
442 bool already_counted = false;
443 for (int j = 0; j < i; j++) {
444 if (inst->src[i].file == inst->src[j].file &&
445 inst->src[i].index == inst->src[j].index) {
446 already_counted = true;
447 }
448 }
449 if (!already_counted)
450 cost++;
451 }
452
453 return cost;
454 }
455
456 static bool
locks_scoreboard(struct qinst * inst)457 locks_scoreboard(struct qinst *inst)
458 {
459 if (inst->op == QOP_TLB_COLOR_READ)
460 return true;
461
462 switch (inst->dst.file) {
463 case QFILE_TLB_Z_WRITE:
464 case QFILE_TLB_COLOR_WRITE:
465 case QFILE_TLB_COLOR_WRITE_MS:
466 return true;
467 default:
468 return false;
469 }
470 }
471
472 static struct schedule_node *
choose_instruction(struct schedule_state * state)473 choose_instruction(struct schedule_state *state)
474 {
475 struct schedule_node *chosen = NULL;
476
477 list_for_each_entry(struct schedule_node, n, &state->worklist, link) {
478 /* The branches aren't being tracked as dependencies. Make
479 * sure that they stay scheduled as the last instruction of
480 * the block, which is to say the first one we choose to
481 * schedule.
482 */
483 if (n->inst->op == QOP_BRANCH)
484 return n;
485
486 if (!chosen) {
487 chosen = n;
488 continue;
489 }
490
491 /* Prefer scheduling things that lock the scoreboard, so that
492 * they appear late in the program and we get more parallelism
493 * between shaders on multiple QPUs hitting the same fragment.
494 */
495 if (locks_scoreboard(n->inst) &&
496 !locks_scoreboard(chosen->inst)) {
497 chosen = n;
498 continue;
499 } else if (!locks_scoreboard(n->inst) &&
500 locks_scoreboard(chosen->inst)) {
501 continue;
502 }
503
504 /* If we would block on the previously chosen node, but would
505 * block less on this one, then prefer it.
506 */
507 if (chosen->unblocked_time > state->time &&
508 n->unblocked_time < chosen->unblocked_time) {
509 chosen = n;
510 continue;
511 } else if (n->unblocked_time > state->time &&
512 n->unblocked_time > chosen->unblocked_time) {
513 continue;
514 }
515
516 /* If we can definitely reduce register pressure, do so
517 * immediately.
518 */
519 int register_pressure_cost =
520 get_register_pressure_cost(state, n->inst);
521 int chosen_register_pressure_cost =
522 get_register_pressure_cost(state, chosen->inst);
523
524 if (register_pressure_cost < chosen_register_pressure_cost) {
525 chosen = n;
526 continue;
527 } else if (register_pressure_cost >
528 chosen_register_pressure_cost) {
529 continue;
530 }
531
532 /* Otherwise, prefer instructions with the deepest chain to
533 * the end of the program. This avoids the problem of
534 * "everything generates a temp, nothing finishes freeing one,
535 * guess I'll just keep emitting varying mul/adds".
536 */
537 if (n->delay > chosen->delay) {
538 chosen = n;
539 continue;
540 } else if (n->delay < chosen->delay) {
541 continue;
542 }
543 }
544
545 return chosen;
546 }
547
548 static void
dump_state(struct vc4_compile * c,struct schedule_state * state)549 dump_state(struct vc4_compile *c, struct schedule_state *state)
550 {
551 uint32_t i = 0;
552 list_for_each_entry(struct schedule_node, n, &state->worklist, link) {
553 fprintf(stderr, "%3d: ", i++);
554 qir_dump_inst(c, n->inst);
555 fprintf(stderr, " (%d cost)\n",
556 get_register_pressure_cost(state, n->inst));
557
558 for (int i = 0; i < n->child_count; i++) {
559 struct schedule_node *child = n->children[i];
560 fprintf(stderr, " - ");
561 qir_dump_inst(c, child->inst);
562 fprintf(stderr, " (%d parents)\n", child->parent_count);
563 }
564 }
565 }
566
567 /* Estimate of how many instructions we should schedule between operations.
568 *
569 * These aren't in real cycle counts, because we're just estimating cycle
570 * times anyway. QIR instructions will get paired up when turned into QPU
571 * instructions, or extra NOP delays will have to be added due to register
572 * allocation choices.
573 */
574 static uint32_t
latency_between(struct schedule_node * before,struct schedule_node * after)575 latency_between(struct schedule_node *before, struct schedule_node *after)
576 {
577 if ((before->inst->dst.file == QFILE_TEX_S ||
578 before->inst->dst.file == QFILE_TEX_S_DIRECT) &&
579 after->inst->op == QOP_TEX_RESULT)
580 return 100;
581
582 switch (before->inst->op) {
583 case QOP_RCP:
584 case QOP_RSQ:
585 case QOP_EXP2:
586 case QOP_LOG2:
587 for (int i = 0; i < qir_get_nsrc(after->inst); i++) {
588 if (after->inst->src[i].file ==
589 before->inst->dst.file &&
590 after->inst->src[i].index ==
591 before->inst->dst.index) {
592 /* There are two QPU delay slots before we can
593 * read a math result, which could be up to 4
594 * QIR instructions if they packed well.
595 */
596 return 4;
597 }
598 }
599 break;
600 default:
601 break;
602 }
603
604 return 1;
605 }
606
607 /** Recursive computation of the delay member of a node. */
608 static void
compute_delay(struct schedule_node * n)609 compute_delay(struct schedule_node *n)
610 {
611 if (!n->child_count) {
612 /* The color read needs to be scheduled late, to avoid locking
613 * the scoreboard early. This is our best tool for
614 * encouraging that. The other scoreboard locking ops will
615 * have this happen by default, since they are generally the
616 * DAG heads or close to them.
617 */
618 if (n->inst->op == QOP_TLB_COLOR_READ)
619 n->delay = 1000;
620 else
621 n->delay = 1;
622 } else {
623 for (int i = 0; i < n->child_count; i++) {
624 if (!n->children[i]->delay)
625 compute_delay(n->children[i]);
626 n->delay = MAX2(n->delay,
627 n->children[i]->delay +
628 latency_between(n->children[i], n));
629 }
630 }
631 }
632
633 static void
schedule_instructions(struct vc4_compile * c,struct qblock * block,struct schedule_state * state)634 schedule_instructions(struct vc4_compile *c,
635 struct qblock *block, struct schedule_state *state)
636 {
637 if (debug) {
638 fprintf(stderr, "initial deps:\n");
639 dump_state(c, state);
640 }
641
642 /* Remove non-DAG heads from the list. */
643 list_for_each_entry_safe(struct schedule_node, n,
644 &state->worklist, link) {
645 if (n->parent_count != 0)
646 list_del(&n->link);
647 }
648
649 state->time = 0;
650 while (!list_empty(&state->worklist)) {
651 struct schedule_node *chosen = choose_instruction(state);
652 struct qinst *inst = chosen->inst;
653
654 if (debug) {
655 fprintf(stderr, "current list:\n");
656 dump_state(c, state);
657 fprintf(stderr, "chose: ");
658 qir_dump_inst(c, inst);
659 fprintf(stderr, " (%d cost)\n",
660 get_register_pressure_cost(state, inst));
661 }
662
663 state->time = MAX2(state->time, chosen->unblocked_time);
664
665 /* Schedule this instruction back onto the QIR list. */
666 list_del(&chosen->link);
667 list_add(&inst->link, &block->instructions);
668
669 /* Now that we've scheduled a new instruction, some of its
670 * children can be promoted to the list of instructions ready to
671 * be scheduled. Update the children's unblocked time for this
672 * DAG edge as we do so.
673 */
674 for (int i = chosen->child_count - 1; i >= 0; i--) {
675 struct schedule_node *child = chosen->children[i];
676
677 child->unblocked_time = MAX2(child->unblocked_time,
678 state->time +
679 latency_between(child,
680 chosen));
681 child->parent_count--;
682 if (child->parent_count == 0)
683 list_add(&child->link, &state->worklist);
684 }
685
686 /* Update our tracking of register pressure. */
687 for (int i = 0; i < qir_get_nsrc(inst); i++) {
688 if (inst->src[i].file == QFILE_TEMP)
689 BITSET_SET(state->temp_live, inst->src[i].index);
690 }
691 if (inst->dst.file == QFILE_TEMP) {
692 state->temp_writes[inst->dst.index]--;
693 if (state->temp_writes[inst->dst.index] == 0)
694 BITSET_CLEAR(state->temp_live, inst->dst.index);
695 }
696
697 state->time++;
698 }
699 }
700
701 static void
qir_schedule_instructions_block(struct vc4_compile * c,struct qblock * block)702 qir_schedule_instructions_block(struct vc4_compile *c,
703 struct qblock *block)
704 {
705 void *mem_ctx = ralloc_context(NULL);
706 struct schedule_state state = { { 0 } };
707
708 state.temp_writes = rzalloc_array(mem_ctx, uint32_t, c->num_temps);
709 state.temp_live = rzalloc_array(mem_ctx, BITSET_WORD,
710 BITSET_WORDS(c->num_temps));
711 list_inithead(&state.worklist);
712
713 /* Wrap each instruction in a scheduler structure. */
714 qir_for_each_inst_safe(inst, block) {
715 struct schedule_node *n = rzalloc(mem_ctx, struct schedule_node);
716
717 n->inst = inst;
718 list_del(&inst->link);
719 list_addtail(&n->link, &state.worklist);
720
721 if (inst->dst.file == QFILE_TEMP)
722 state.temp_writes[inst->dst.index]++;
723 }
724
725 /* Dependencies tracked top-to-bottom. */
726 calculate_forward_deps(c, mem_ctx, &state.worklist);
727 /* Dependencies tracked bottom-to-top. */
728 calculate_reverse_deps(c, mem_ctx, &state.worklist);
729
730 list_for_each_entry(struct schedule_node, n, &state.worklist, link)
731 compute_delay(n);
732
733 schedule_instructions(c, block, &state);
734
735 ralloc_free(mem_ctx);
736 }
737
738 void
qir_schedule_instructions(struct vc4_compile * c)739 qir_schedule_instructions(struct vc4_compile *c)
740 {
741
742 if (debug) {
743 fprintf(stderr, "Pre-schedule instructions\n");
744 qir_dump(c);
745 }
746
747 qir_for_each_block(block, c)
748 qir_schedule_instructions_block(c, block);
749
750 if (debug) {
751 fprintf(stderr, "Post-schedule instructions\n");
752 qir_dump(c);
753 }
754 }
755