1 /*
2 * Copyright (c) 2017-2019 Gert Wollny
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
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 #include "sfn_liverange.h"
25 #include "sfn_debug.h"
26 #include "sfn_value.h"
27 #include "sfn_value_gpr.h"
28
29 #include "program/prog_instruction.h"
30 #include "util/bitscan.h"
31 #include "util/u_math.h"
32
33 #include <limits>
34 #include <cstdlib>
35 #include <iomanip>
36
37 /* std::sort is significantly faster than qsort */
38 #include <algorithm>
39
40 /* If <windows.h> is included this is defined and clashes with
41 * std::numeric_limits<>::max()
42 */
43 #ifdef max
44 #undef max
45 #endif
46
47
48 namespace r600 {
49
50 using std::numeric_limits;
51 using std::unique_ptr;
52 using std::setw;
53
prog_scope_storage(int n)54 prog_scope_storage::prog_scope_storage(int n):
55 current_slot(0),
56 storage(n)
57 {
58 }
59
~prog_scope_storage()60 prog_scope_storage::~prog_scope_storage()
61 {
62 }
63
64 prog_scope*
create(prog_scope * p,prog_scope_type type,int id,int lvl,int s_begin)65 prog_scope_storage::create(prog_scope *p, prog_scope_type type, int id,
66 int lvl, int s_begin)
67 {
68 storage[current_slot] = prog_scope(p, type, id, lvl, s_begin);
69 return &storage[current_slot++];
70 }
71
prog_scope(prog_scope * parent,prog_scope_type type,int id,int depth,int scope_begin)72 prog_scope::prog_scope(prog_scope *parent, prog_scope_type type, int id,
73 int depth, int scope_begin):
74 scope_type(type),
75 scope_id(id),
76 scope_nesting_depth(depth),
77 scope_begin(scope_begin),
78 scope_end(-1),
79 break_loop_line(numeric_limits<int>::max()),
80 parent_scope(parent)
81 {
82 }
83
prog_scope()84 prog_scope::prog_scope():
85 prog_scope(nullptr, undefined_scope, -1, -1, -1)
86 {
87 }
88
type() const89 prog_scope_type prog_scope::type() const
90 {
91 return scope_type;
92 }
93
parent() const94 prog_scope *prog_scope::parent() const
95 {
96 return parent_scope;
97 }
98
nesting_depth() const99 int prog_scope::nesting_depth() const
100 {
101 return scope_nesting_depth;
102 }
103
is_loop() const104 bool prog_scope::is_loop() const
105 {
106 return (scope_type == loop_body);
107 }
108
is_in_loop() const109 bool prog_scope::is_in_loop() const
110 {
111 if (scope_type == loop_body)
112 return true;
113
114 if (parent_scope)
115 return parent_scope->is_in_loop();
116
117 return false;
118 }
119
innermost_loop() const120 const prog_scope *prog_scope::innermost_loop() const
121 {
122 if (scope_type == loop_body)
123 return this;
124
125 if (parent_scope)
126 return parent_scope->innermost_loop();
127
128 return nullptr;
129 }
130
outermost_loop() const131 const prog_scope *prog_scope::outermost_loop() const
132 {
133 const prog_scope *loop = nullptr;
134 const prog_scope *p = this;
135
136 do {
137 if (p->type() == loop_body)
138 loop = p;
139 p = p->parent();
140 } while (p);
141
142 return loop;
143 }
144
is_child_of_ifelse_id_sibling(const prog_scope * scope) const145 bool prog_scope::is_child_of_ifelse_id_sibling(const prog_scope *scope) const
146 {
147 const prog_scope *my_parent = in_parent_ifelse_scope();
148 while (my_parent) {
149 /* is a direct child? */
150 if (my_parent == scope)
151 return false;
152 /* is a child of the conditions sibling? */
153 if (my_parent->id() == scope->id())
154 return true;
155 my_parent = my_parent->in_parent_ifelse_scope();
156 }
157 return false;
158 }
159
is_child_of(const prog_scope * scope) const160 bool prog_scope::is_child_of(const prog_scope *scope) const
161 {
162 const prog_scope *my_parent = parent();
163 while (my_parent) {
164 if (my_parent == scope)
165 return true;
166 my_parent = my_parent->parent();
167 }
168 return false;
169 }
170
enclosing_conditional() const171 const prog_scope *prog_scope::enclosing_conditional() const
172 {
173 if (is_conditional())
174 return this;
175
176 if (parent_scope)
177 return parent_scope->enclosing_conditional();
178
179 return nullptr;
180 }
181
contains_range_of(const prog_scope & other) const182 bool prog_scope::contains_range_of(const prog_scope& other) const
183 {
184 return (begin() <= other.begin()) && (end() >= other.end());
185 }
186
is_conditional() const187 bool prog_scope::is_conditional() const
188 {
189 return scope_type == if_branch ||
190 scope_type == else_branch ||
191 scope_type == switch_case_branch ||
192 scope_type == switch_default_branch;
193 }
194
in_else_scope() const195 const prog_scope *prog_scope::in_else_scope() const
196 {
197 if (scope_type == else_branch)
198 return this;
199
200 if (parent_scope)
201 return parent_scope->in_else_scope();
202
203 return nullptr;
204 }
205
in_parent_ifelse_scope() const206 const prog_scope *prog_scope::in_parent_ifelse_scope() const
207 {
208 if (parent_scope)
209 return parent_scope->in_ifelse_scope();
210 else
211 return nullptr;
212 }
213
in_ifelse_scope() const214 const prog_scope *prog_scope::in_ifelse_scope() const
215 {
216 if (scope_type == if_branch ||
217 scope_type == else_branch)
218 return this;
219
220 if (parent_scope)
221 return parent_scope->in_ifelse_scope();
222
223 return nullptr;
224 }
225
is_switchcase_scope_in_loop() const226 bool prog_scope::is_switchcase_scope_in_loop() const
227 {
228 return (scope_type == switch_case_branch ||
229 scope_type == switch_default_branch) &&
230 is_in_loop();
231 }
232
break_is_for_switchcase() const233 bool prog_scope::break_is_for_switchcase() const
234 {
235 if (scope_type == loop_body)
236 return false;
237
238 if (scope_type == switch_case_branch ||
239 scope_type == switch_default_branch ||
240 scope_type == switch_body)
241 return true;
242
243 if (parent_scope)
244 return parent_scope->break_is_for_switchcase();
245
246 return false;
247 }
248
id() const249 int prog_scope::id() const
250 {
251 return scope_id;
252 }
253
begin() const254 int prog_scope::begin() const
255 {
256 return scope_begin;
257 }
258
end() const259 int prog_scope::end() const
260 {
261 return scope_end;
262 }
263
set_end(int end)264 void prog_scope::set_end(int end)
265 {
266 if (scope_end == -1)
267 scope_end = end;
268 }
269
set_loop_break_line(int line)270 void prog_scope::set_loop_break_line(int line)
271 {
272 if (scope_type == loop_body) {
273 break_loop_line = MIN2(break_loop_line, line);
274 } else {
275 if (parent_scope)
276 parent()->set_loop_break_line(line);
277 }
278 }
279
loop_break_line() const280 int prog_scope::loop_break_line() const
281 {
282 return break_loop_line;
283 }
284
temp_access()285 temp_access::temp_access():
286 access_mask(0),
287 needs_component_tracking(false),
288 is_array_element(false)
289 {
290 }
291
update_access_mask(int mask)292 void temp_access::update_access_mask(int mask)
293 {
294 if (access_mask && access_mask != mask)
295 needs_component_tracking = true;
296 access_mask |= mask;
297 }
298
record_write(int line,prog_scope * scope,int writemask,bool is_array_elm)299 void temp_access::record_write(int line, prog_scope *scope, int writemask, bool is_array_elm)
300 {
301
302
303 update_access_mask(writemask);
304 is_array_element |= is_array_elm;
305
306 if (writemask & WRITEMASK_X)
307 comp[0].record_write(line, scope);
308 if (writemask & WRITEMASK_Y)
309 comp[1].record_write(line, scope);
310 if (writemask & WRITEMASK_Z)
311 comp[2].record_write(line, scope);
312 if (writemask & WRITEMASK_W)
313 comp[3].record_write(line, scope);
314 }
315
record_read(int line,prog_scope * scope,int readmask,bool is_array_elm)316 void temp_access::record_read(int line, prog_scope *scope, int readmask, bool is_array_elm)
317 {
318 update_access_mask(readmask);
319 is_array_element |= is_array_elm;
320
321 if (readmask & WRITEMASK_X)
322 comp[0].record_read(line, scope);
323 if (readmask & WRITEMASK_Y)
324 comp[1].record_read(line, scope);
325 if (readmask & WRITEMASK_Z)
326 comp[2].record_read(line, scope);
327 if (readmask & WRITEMASK_W)
328 comp[3].record_read(line, scope);
329 }
330
make_live_range(int b,int e)331 inline static register_live_range make_live_range(int b, int e)
332 {
333 register_live_range lt;
334 lt.begin = b;
335 lt.end = e;
336 lt.is_array_elm = false;
337 return lt;
338 }
339
get_required_live_range()340 register_live_range temp_access::get_required_live_range()
341 {
342 register_live_range result = make_live_range(-1, -1);
343
344 unsigned mask = access_mask;
345 while (mask) {
346 unsigned chan = u_bit_scan(&mask);
347 register_live_range lt = comp[chan].get_required_live_range();
348
349 if (lt.begin >= 0) {
350 if ((result.begin < 0) || (result.begin > lt.begin))
351 result.begin = lt.begin;
352 }
353
354 if (lt.end > result.end)
355 result.end = lt.end;
356
357 if (!needs_component_tracking)
358 break;
359 }
360 result.is_array_elm = is_array_element;
361
362 return result;
363 }
364
365 const int
366 temp_comp_access::conditionality_untouched = std::numeric_limits<int>::max();
367
368 const int
369 temp_comp_access::write_is_unconditional = std::numeric_limits<int>::max() - 1;
370
371
temp_comp_access()372 temp_comp_access::temp_comp_access():
373 last_read_scope(nullptr),
374 first_read_scope(nullptr),
375 first_write_scope(nullptr),
376 first_write(-1),
377 last_read(-1),
378 last_write(-1),
379 first_read(numeric_limits<int>::max()),
380 conditionality_in_loop_id(conditionality_untouched),
381 if_scope_write_flags(0),
382 next_ifelse_nesting_depth(0),
383 current_unpaired_if_write_scope(nullptr),
384 was_written_in_current_else_scope(false)
385 {
386 }
387
record_read(int line,prog_scope * scope)388 void temp_comp_access::record_read(int line, prog_scope *scope)
389 {
390 last_read_scope = scope;
391 if (last_read < line)
392 last_read = line;
393
394 if (first_read > line) {
395 first_read = line;
396 first_read_scope = scope;
397 }
398
399 /* If the conditionality of the first write is already resolved then
400 * no further checks are required.
401 */
402 if (conditionality_in_loop_id == write_is_unconditional ||
403 conditionality_in_loop_id == write_is_conditional)
404 return;
405
406 /* Check whether we are in a condition within a loop */
407 const prog_scope *ifelse_scope = scope->in_ifelse_scope();
408 const prog_scope *enclosing_loop;
409 if (ifelse_scope && (enclosing_loop = ifelse_scope->innermost_loop())) {
410
411 /* If we have either not yet written to this register nor writes are
412 * resolved as unconditional in the enclosing loop then check whether
413 * we read before write in an IF/ELSE branch.
414 */
415 if ((conditionality_in_loop_id != write_is_conditional) &&
416 (conditionality_in_loop_id != enclosing_loop->id())) {
417
418 if (current_unpaired_if_write_scope) {
419
420 /* Has been written in this or a parent scope? - this makes the temporary
421 * unconditionally set at this point.
422 */
423 if (scope->is_child_of(current_unpaired_if_write_scope))
424 return;
425
426 /* Has been written in the same scope before it was read? */
427 if (ifelse_scope->type() == if_branch) {
428 if (current_unpaired_if_write_scope->id() == scope->id())
429 return;
430 } else {
431 if (was_written_in_current_else_scope)
432 return;
433 }
434 }
435
436 /* The temporary was read (conditionally) before it is written, hence
437 * it should survive a loop. This can be signaled like if it were
438 * conditionally written.
439 */
440 conditionality_in_loop_id = write_is_conditional;
441 }
442 }
443 }
444
record_write(int line,prog_scope * scope)445 void temp_comp_access::record_write(int line, prog_scope *scope)
446 {
447 last_write = line;
448
449 if (first_write < 0) {
450 first_write = line;
451 first_write_scope = scope;
452
453 /* If the first write we encounter is not in a conditional branch, or
454 * the conditional write is not within a loop, then this is to be
455 * considered an unconditional dominant write.
456 */
457 const prog_scope *conditional = scope->enclosing_conditional();
458 if (!conditional || !conditional->innermost_loop()) {
459 conditionality_in_loop_id = write_is_unconditional;
460 }
461 }
462
463 /* The conditionality of the first write is already resolved. */
464 if (conditionality_in_loop_id == write_is_unconditional ||
465 conditionality_in_loop_id == write_is_conditional)
466 return;
467
468 /* If the nesting depth is larger than the supported level,
469 * then we assume conditional writes.
470 */
471 if (next_ifelse_nesting_depth >= supported_ifelse_nesting_depth) {
472 conditionality_in_loop_id = write_is_conditional;
473 return;
474 }
475
476 /* If we are in an IF/ELSE scope within a loop and the loop has not
477 * been resolved already, then record this write.
478 */
479 const prog_scope *ifelse_scope = scope->in_ifelse_scope();
480 if (ifelse_scope && ifelse_scope->innermost_loop() &&
481 ifelse_scope->innermost_loop()->id() != conditionality_in_loop_id)
482 record_ifelse_write(*ifelse_scope);
483 }
484
record_ifelse_write(const prog_scope & scope)485 void temp_comp_access::record_ifelse_write(const prog_scope& scope)
486 {
487 if (scope.type() == if_branch) {
488 /* The first write in an IF branch within a loop implies unresolved
489 * conditionality (if it was untouched or unconditional before).
490 */
491 conditionality_in_loop_id = conditionality_unresolved;
492 was_written_in_current_else_scope = false;
493 record_if_write(scope);
494 } else {
495 was_written_in_current_else_scope = true;
496 record_else_write(scope);
497 }
498 }
499
record_if_write(const prog_scope & scope)500 void temp_comp_access::record_if_write(const prog_scope& scope)
501 {
502 /* Don't record write if this IF scope if it ...
503 * - is not the first write in this IF scope,
504 * - has already been written in a parent IF scope.
505 * In both cases this write is a secondary write that doesn't contribute
506 * to resolve conditionality.
507 *
508 * Record the write if it
509 * - is the first one (obviously),
510 * - happens in an IF branch that is a child of the ELSE branch of the
511 * last active IF/ELSE pair. In this case recording this write is used to
512 * established whether the write is (un-)conditional in the scope enclosing
513 * this outer IF/ELSE pair.
514 */
515 if (!current_unpaired_if_write_scope ||
516 (current_unpaired_if_write_scope->id() != scope.id() &&
517 scope.is_child_of_ifelse_id_sibling(current_unpaired_if_write_scope))) {
518 if_scope_write_flags |= 1 << next_ifelse_nesting_depth;
519 current_unpaired_if_write_scope = &scope;
520 next_ifelse_nesting_depth++;
521 }
522 }
523
record_else_write(const prog_scope & scope)524 void temp_comp_access::record_else_write(const prog_scope& scope)
525 {
526 int mask = 1 << (next_ifelse_nesting_depth - 1);
527
528 /* If the temporary was written in an IF branch on the same scope level
529 * and this branch is the sibling of this ELSE branch, then we have a
530 * pair of writes that makes write access to this temporary unconditional
531 * in the enclosing scope.
532 */
533
534 if ((if_scope_write_flags & mask) &&
535 (scope.id() == current_unpaired_if_write_scope->id())) {
536 --next_ifelse_nesting_depth;
537 if_scope_write_flags &= ~mask;
538
539 /* The following code deals with propagating unconditionality from
540 * inner levels of nested IF/ELSE to the outer levels like in
541 *
542 * 1: var t;
543 * 2: if (a) { <- start scope A
544 * 3: if (b)
545 * 4: t = ...
546 * 5: else
547 * 6: t = ...
548 * 7: } else { <- start scope B
549 * 8: if (c)
550 * 9: t = ...
551 * A: else <- start scope C
552 * B: t = ...
553 * C: }
554 *
555 */
556
557 const prog_scope *parent_ifelse = scope.parent()->in_ifelse_scope();
558
559 if (1 << (next_ifelse_nesting_depth - 1) & if_scope_write_flags) {
560 /* We are at the end of scope C and already recorded a write
561 * within an IF scope (A), the sibling of the parent ELSE scope B,
562 * and it is not yet resolved. Mark that as the last relevant
563 * IF scope. Below the write will be resolved for the A/B
564 * scope pair.
565 */
566 current_unpaired_if_write_scope = parent_ifelse;
567 } else {
568 current_unpaired_if_write_scope = nullptr;
569 }
570 /* Promote the first write scope to the enclosing scope because
571 * the current IF/ELSE pair is now irrelevant for the analysis.
572 * This is also required to evaluate the minimum life time for t in
573 * {
574 * var t;
575 * if (a)
576 * t = ...
577 * else
578 * t = ...
579 * x = t;
580 * ...
581 * }
582 */
583 first_write_scope = scope.parent();
584
585 /* If some parent is IF/ELSE and in a loop then propagate the
586 * write to that scope. Otherwise the write is unconditional
587 * because it happens in both corresponding IF/ELSE branches
588 * in this loop, and hence, record the loop id to signal the
589 * resolution.
590 */
591 if (parent_ifelse && parent_ifelse->is_in_loop()) {
592 record_ifelse_write(*parent_ifelse);
593 } else {
594 conditionality_in_loop_id = scope.innermost_loop()->id();
595 }
596 } else {
597 /* The temporary was not written in the IF branch corresponding
598 * to this ELSE branch, hence the write is conditional.
599 */
600 conditionality_in_loop_id = write_is_conditional;
601 }
602 }
603
conditional_ifelse_write_in_loop() const604 bool temp_comp_access::conditional_ifelse_write_in_loop() const
605 {
606 return conditionality_in_loop_id <= conditionality_unresolved;
607 }
608
propagate_live_range_to_dominant_write_scope()609 void temp_comp_access::propagate_live_range_to_dominant_write_scope()
610 {
611 first_write = first_write_scope->begin();
612 int lr = first_write_scope->end();
613
614 if (last_read < lr)
615 last_read = lr;
616 }
617
get_required_live_range()618 register_live_range temp_comp_access::get_required_live_range()
619 {
620 bool keep_for_full_loop = false;
621
622 /* This register component is not used at all, or only read,
623 * mark it as unused and ignore it when renaming.
624 * glsl_to_tgsi_visitor::renumber_registers will take care of
625 * eliminating registers that are not written to.
626 */
627 if (last_write < 0)
628 return make_live_range(-1, -1);
629
630 assert(first_write_scope);
631
632 /* Only written to, just make sure the register component is not
633 * reused in the range it is used to write to
634 */
635 if (!last_read_scope)
636 return make_live_range(first_write, last_write + 1);
637
638 const prog_scope *enclosing_scope_first_read = first_read_scope;
639 const prog_scope *enclosing_scope_first_write = first_write_scope;
640
641 /* We read before writing in a loop
642 * hence the value must survive the loops
643 */
644 if ((first_read <= first_write) &&
645 first_read_scope->is_in_loop()) {
646 keep_for_full_loop = true;
647 enclosing_scope_first_read = first_read_scope->outermost_loop();
648 }
649
650 /* A conditional write within a (nested) loop must survive the outermost
651 * loop if the last read was not within the same scope.
652 */
653 const prog_scope *conditional = enclosing_scope_first_write->enclosing_conditional();
654 if (conditional && !conditional->contains_range_of(*last_read_scope) &&
655 (conditional->is_switchcase_scope_in_loop() ||
656 conditional_ifelse_write_in_loop())) {
657 keep_for_full_loop = true;
658 enclosing_scope_first_write = conditional->outermost_loop();
659 }
660
661 /* Evaluate the scope that is shared by all: required first write scope,
662 * required first read before write scope, and last read scope.
663 */
664 const prog_scope *enclosing_scope = enclosing_scope_first_read;
665 if (enclosing_scope_first_write->contains_range_of(*enclosing_scope))
666 enclosing_scope = enclosing_scope_first_write;
667
668 if (last_read_scope->contains_range_of(*enclosing_scope))
669 enclosing_scope = last_read_scope;
670
671 while (!enclosing_scope->contains_range_of(*enclosing_scope_first_write) ||
672 !enclosing_scope->contains_range_of(*last_read_scope)) {
673 enclosing_scope = enclosing_scope->parent();
674 assert(enclosing_scope);
675 }
676
677 /* Propagate the last read scope to the target scope */
678 while (enclosing_scope->nesting_depth() < last_read_scope->nesting_depth()) {
679 /* If the read is in a loop and we have to move up the scope we need to
680 * extend the live range to the end of this current loop because at this
681 * point we don't know whether the component was written before
682 * un-conditionally in the same loop.
683 */
684 if (last_read_scope->is_loop())
685 last_read = last_read_scope->end();
686
687 last_read_scope = last_read_scope->parent();
688 }
689
690 /* If the variable has to be kept for the whole loop, and we
691 * are currently in a loop, then propagate the live range.
692 */
693 if (keep_for_full_loop && first_write_scope->is_loop())
694 propagate_live_range_to_dominant_write_scope();
695
696 /* Propagate the first_dominant_write scope to the target scope */
697 while (enclosing_scope->nesting_depth() < first_write_scope->nesting_depth()) {
698 /* Propagate live_range if there was a break in a loop and the write was
699 * after the break inside that loop. Note, that this is only needed if
700 * we move up in the scopes.
701 */
702 if (first_write_scope->loop_break_line() < first_write) {
703 keep_for_full_loop = true;
704 propagate_live_range_to_dominant_write_scope();
705 }
706
707 first_write_scope = first_write_scope->parent();
708
709 /* Propagte live_range if we are now in a loop */
710 if (keep_for_full_loop && first_write_scope->is_loop())
711 propagate_live_range_to_dominant_write_scope();
712 }
713
714 /* The last write past the last read is dead code, but we have to
715 * ensure that the component is not reused too early, hence extend the
716 * live_range past the last write.
717 */
718 if (last_write >= last_read)
719 last_read = last_write + 1;
720
721 /* Here we are at the same scope, all is resolved */
722 return make_live_range(first_write, last_read);
723 }
724
725 /* Helper class for sorting and searching the registers based
726 * on live ranges. */
727 class register_merge_record {
728 public:
729 int begin;
730 int end;
731 int reg;
732 bool erase;
733 bool is_array_elm;
734
operator <(const register_merge_record & rhs) const735 bool operator < (const register_merge_record& rhs) const {
736 return begin < rhs.begin;
737 }
738 };
739
LiverangeEvaluator()740 LiverangeEvaluator::LiverangeEvaluator():
741 line(0),
742 loop_id(1),
743 if_id(1),
744 switch_id(0),
745 is_at_end(false),
746 n_scopes(1),
747 cur_scope(nullptr)
748 {
749 }
750
run(const Shader & shader,std::vector<register_live_range> & register_live_ranges)751 void LiverangeEvaluator::run(const Shader& shader,
752 std::vector<register_live_range>& register_live_ranges)
753 {
754 temp_acc.resize(register_live_ranges.size());
755 fill(temp_acc.begin(), temp_acc.end(), temp_access());
756
757 sfn_log << SfnLog::merge << "have " << temp_acc.size() << " temps\n";
758
759 for (const auto& block: shader.m_ir) {
760 for (const auto& ir: block) {
761 switch (ir->type()) {
762 case Instruction::cond_if:
763 case Instruction::cond_else:
764 case Instruction::loop_begin:
765 ++n_scopes;
766 default:
767 ;
768 }
769 }
770 }
771
772 scopes.reset(new prog_scope_storage(n_scopes));
773
774 cur_scope = scopes->create(nullptr, outer_scope, 0, 0, line);
775
776 line = 0;
777
778 for (auto& v: shader.m_temp) {
779 if (v.second->type() == Value::gpr) {
780 sfn_log << SfnLog::merge << "Record " << *v.second << "\n";
781 const auto& g = static_cast<const GPRValue&>(*v.second);
782 if (g.is_input()) {
783 sfn_log << SfnLog::merge << "Record INPUT write for "
784 << g << " in " << temp_acc.size() << " temps\n";
785 temp_acc[g.sel()].record_write(line, cur_scope, 1 << g.chan(), false);
786 temp_acc[g.sel()].record_read(line, cur_scope, 1 << g.chan(), false);
787 }
788 if (g.keep_alive()) {
789 sfn_log << SfnLog::merge << "Record KEEP ALIVE for "
790 << g << " in " << temp_acc.size() << " temps\n";
791 temp_acc[g.sel()].record_read(0x7fffff, cur_scope, 1 << g.chan(), false);
792 }
793 }
794 }
795
796 for (const auto& block: shader.m_ir)
797 for (const auto& ir: block) {
798 ir->evalue_liveness(*this);
799 if (ir->type() != Instruction::alu ||
800 static_cast<const AluInstruction&>(*ir).flag(alu_last_instr))
801 ++line;
802 }
803
804 assert(cur_scope->type() == outer_scope);
805 cur_scope->set_end(line);
806 is_at_end = true;
807
808 get_required_live_ranges(register_live_ranges);
809 }
810
811
record_read(const Value & src,bool is_array_elm)812 void LiverangeEvaluator::record_read(const Value& src, bool is_array_elm)
813 {
814 sfn_log << SfnLog::merge << "Record read l:" << line << " reg:" << src << "\n";
815 if (src.type() == Value::gpr) {
816 const GPRValue& v = static_cast<const GPRValue&>(src);
817 if (v.chan() < 4)
818 temp_acc[v.sel()].record_read(v.keep_alive() ? 0x7fffff: line, cur_scope, 1 << v.chan(), is_array_elm);
819 return;
820 } else if (src.type() == Value::gpr_array_value) {
821 const GPRArrayValue& v = static_cast<const GPRArrayValue&>(src);
822 v.record_read(*this);
823 } else if (src.type() == Value::kconst) {
824 const UniformValue& v = static_cast<const UniformValue&>(src);
825 if (v.addr())
826 record_read(*v.addr(),is_array_elm);
827 }
828 }
829
record_write(const Value & src,bool is_array_elm)830 void LiverangeEvaluator::record_write(const Value& src, bool is_array_elm)
831 {
832 sfn_log << SfnLog::merge << "Record write for "
833 << src << " in " << temp_acc.size() << " temps\n";
834
835 if (src.type() == Value::gpr) {
836 const GPRValue& v = static_cast<const GPRValue&>(src);
837 assert(v.sel() < temp_acc.size());
838 if (v.chan() < 4)
839 temp_acc[v.sel()].record_write(line, cur_scope, 1 << v.chan(), is_array_elm);
840 return;
841 } else if (src.type() == Value::gpr_array_value) {
842 const GPRArrayValue& v = static_cast<const GPRArrayValue&>(src);
843 v.record_write(*this);
844 } else if (src.type() == Value::kconst) {
845 const UniformValue& v = static_cast<const UniformValue&>(src);
846 if (v.addr())
847 record_write(*v.addr(),is_array_elm);
848 }
849 }
850
record_read(const GPRVector & src)851 void LiverangeEvaluator::record_read(const GPRVector& src)
852 {
853 for (int i = 0; i < 4; ++i)
854 if (src.reg_i(i))
855 record_read(*src.reg_i(i));
856 }
857
record_write(const GPRVector & dst)858 void LiverangeEvaluator::record_write(const GPRVector& dst)
859 {
860 for (int i = 0; i < 4; ++i)
861 if (dst.reg_i(i))
862 record_write(*dst.reg_i(i));
863 }
864
get_required_live_ranges(std::vector<register_live_range> & register_live_ranges)865 void LiverangeEvaluator::get_required_live_ranges(std::vector<register_live_range>& register_live_ranges)
866 {
867 sfn_log << SfnLog::merge << "== register live ranges ==========\n";
868 for(unsigned i = 0; i < register_live_ranges.size(); ++i) {
869 sfn_log << SfnLog::merge << setw(4) << i;
870 register_live_ranges[i] = temp_acc[i].get_required_live_range();
871 sfn_log << SfnLog::merge << ": [" << register_live_ranges[i].begin << ", "
872 << register_live_ranges[i].end << "]\n";
873 }
874 sfn_log << SfnLog::merge << "==================================\n\n";
875 }
876
scope_if()877 void LiverangeEvaluator::scope_if()
878 {
879 cur_scope = scopes->create(cur_scope, if_branch, if_id++,
880 cur_scope->nesting_depth() + 1, line + 1);
881 }
882
scope_else()883 void LiverangeEvaluator::scope_else()
884 {
885 assert(cur_scope->type() == if_branch);
886 cur_scope->set_end(line - 1);
887 cur_scope = scopes->create(cur_scope->parent(), else_branch,
888 cur_scope->id(), cur_scope->nesting_depth(),
889 line + 1);
890 }
891
scope_endif()892 void LiverangeEvaluator::scope_endif()
893 {
894 cur_scope->set_end(line - 1);
895 cur_scope = cur_scope->parent();
896 assert(cur_scope);
897 }
898
scope_loop_begin()899 void LiverangeEvaluator::scope_loop_begin()
900 {
901 cur_scope = scopes->create(cur_scope, loop_body, loop_id++,
902 cur_scope->nesting_depth() + 1, line);
903 }
904
scope_loop_end()905 void LiverangeEvaluator::scope_loop_end()
906 {
907 assert(cur_scope->type() == loop_body);
908 cur_scope->set_end(line);
909 cur_scope = cur_scope->parent();
910 assert(cur_scope);
911 }
912
scope_loop_break()913 void LiverangeEvaluator::scope_loop_break()
914 {
915 cur_scope->set_loop_break_line(line);
916 }
917
918 /* This functions evaluates the register merges by using a binary
919 * search to find suitable merge candidates. */
920
921 std::vector<rename_reg_pair>
get_temp_registers_remapping(const std::vector<register_live_range> & live_ranges)922 get_temp_registers_remapping(const std::vector<register_live_range>& live_ranges)
923 {
924
925 std::vector<rename_reg_pair> result(live_ranges.size(), rename_reg_pair{false, false, 0});
926 std::vector<register_merge_record> reg_access;
927
928 for (unsigned i = 0; i < live_ranges.size(); ++i) {
929 if (live_ranges[i].begin >= 0) {
930 register_merge_record r;
931 r.begin = live_ranges[i].begin;
932 r.end = live_ranges[i].end;
933 r.is_array_elm = live_ranges[i].is_array_elm;
934 r.reg = i;
935 r.erase = false;
936 reg_access.push_back(r);
937 }
938 }
939
940 std::sort(reg_access.begin(), reg_access.end());
941
942 for (auto& r : reg_access)
943 sfn_log << SfnLog::merge << "Use Range " <<r.reg << " ["
944 << r.begin << ", " << r.end << "]\n";
945
946 auto trgt = reg_access.begin();
947 auto reg_access_end = reg_access.end();
948 auto first_erase = reg_access_end;
949 auto search_start = trgt + 1;
950
951 while (trgt != reg_access_end) {
952 /* Find the next register that has a live-range starting past the
953 * search start and that is not an array element. Array elements can't
954 * be moved (Moving the whole array could be an option to be implemented later)*/
955
956 sfn_log << SfnLog::merge << "Next target is "
957 << trgt->reg << "[" << trgt->begin << ", " << trgt->end << "]\n";
958
959
960 auto src = upper_bound(search_start, reg_access_end, trgt->end,
961 [](int bound, const register_merge_record& m){
962 return bound < m.begin && !m.is_array_elm;}
963 );
964
965 if (src != reg_access_end) {
966 result[src->reg].new_reg = trgt->reg;
967 result[src->reg].valid = true;
968
969 sfn_log << SfnLog::merge << "Map "
970 << src->reg << "[" << src->begin << ", " << src->end << "] to "
971 << trgt->reg << "[" << trgt->begin << ", " << trgt->end << ":";
972 trgt->end = src->end;
973 sfn_log << SfnLog::merge << trgt->end << "]\n";
974
975 /* Since we only search forward, don't remove the renamed
976 * register just now, only mark it. */
977 src->erase = true;
978
979 if (first_erase == reg_access_end)
980 first_erase = src;
981
982 search_start = src + 1;
983 } else {
984 /* Moving to the next target register it is time to remove
985 * the already merged registers from the search range */
986 if (first_erase != reg_access_end) {
987 auto outp = first_erase;
988 auto inp = first_erase + 1;
989
990 while (inp != reg_access_end) {
991 if (!inp->erase)
992 *outp++ = *inp;
993 ++inp;
994 }
995
996 reg_access_end = outp;
997 first_erase = reg_access_end;
998 }
999 ++trgt;
1000 search_start = trgt + 1;
1001 }
1002 }
1003 return result;
1004 }
1005
1006 } // end ns r600
1007