1 /* -*- mesa-c++ -*-
2 * Copyright 2022 Collabora LTD
3 * Author: Gert Wollny <[email protected]>
4 * SPDX-License-Identifier: MIT
5 */
6
7 #include "sfn_liverangeevaluator_helpers.h"
8
9 #include "sfn_virtualvalues.h"
10 #include "util/u_math.h"
11
12 #include <cassert>
13 #include <iostream>
14 #include <limits>
15
16 namespace r600 {
17
ProgramScope(ProgramScope * parent,ProgramScopeType type,int id,int depth,int scope_begin)18 ProgramScope::ProgramScope(
19 ProgramScope *parent, ProgramScopeType type, int id, int depth, int scope_begin):
20 scope_type(type),
21 scope_id(id),
22 scope_nesting_depth(depth),
23 scope_begin(scope_begin),
24 scope_end(-1),
25 break_loop_line(std::numeric_limits<int>::max()),
26 parent_scope(parent)
27 {
28 }
29
ProgramScope()30 ProgramScope::ProgramScope():
31 ProgramScope(nullptr, undefined_scope, -1, -1, -1)
32 {
33 }
34
35 ProgramScopeType
type() const36 ProgramScope::type() const
37 {
38 return scope_type;
39 }
40
41 ProgramScope *
parent() const42 ProgramScope::parent() const
43 {
44 return parent_scope;
45 }
46
47 int
nesting_depth() const48 ProgramScope::nesting_depth() const
49 {
50 return scope_nesting_depth;
51 }
52
53 bool
is_loop() const54 ProgramScope::is_loop() const
55 {
56 return (scope_type == loop_body);
57 }
58
59 bool
is_in_loop() const60 ProgramScope::is_in_loop() const
61 {
62 if (scope_type == loop_body)
63 return true;
64
65 if (parent_scope)
66 return parent_scope->is_in_loop();
67
68 return false;
69 }
70
71 const ProgramScope *
innermost_loop() const72 ProgramScope::innermost_loop() const
73 {
74 if (scope_type == loop_body)
75 return this;
76
77 if (parent_scope)
78 return parent_scope->innermost_loop();
79
80 return nullptr;
81 }
82
83 const ProgramScope *
outermost_loop() const84 ProgramScope::outermost_loop() const
85 {
86 const ProgramScope *loop = nullptr;
87 const ProgramScope *p = this;
88
89 do {
90 if (p->type() == loop_body)
91 loop = p;
92 p = p->parent();
93 } while (p);
94
95 return loop;
96 }
97
98 bool
is_child_of_ifelse_id_sibling(const ProgramScope * scope) const99 ProgramScope::is_child_of_ifelse_id_sibling(const ProgramScope *scope) const
100 {
101 const ProgramScope *my_parent = in_parent_ifelse_scope();
102 while (my_parent) {
103 /* is a direct child? */
104 if (my_parent == scope)
105 return false;
106 /* is a child of the conditions sibling? */
107 if (my_parent->id() == scope->id())
108 return true;
109 my_parent = my_parent->in_parent_ifelse_scope();
110 }
111 return false;
112 }
113
114 bool
is_child_of(const ProgramScope * scope) const115 ProgramScope::is_child_of(const ProgramScope *scope) const
116 {
117 const ProgramScope *my_parent = parent();
118 while (my_parent) {
119 if (my_parent == scope)
120 return true;
121 my_parent = my_parent->parent();
122 }
123 return false;
124 }
125
126 const ProgramScope *
enclosing_conditional() const127 ProgramScope::enclosing_conditional() const
128 {
129 if (is_conditional())
130 return this;
131
132 if (parent_scope)
133 return parent_scope->enclosing_conditional();
134
135 return nullptr;
136 }
137
138 bool
contains_range_of(const ProgramScope & other) const139 ProgramScope::contains_range_of(const ProgramScope& other) const
140 {
141 return (begin() <= other.begin()) && (end() >= other.end());
142 }
143
144 bool
is_conditional() const145 ProgramScope::is_conditional() const
146 {
147 return scope_type == if_branch || scope_type == else_branch ||
148 scope_type == switch_case_branch || scope_type == switch_default_branch;
149 }
150
151 const ProgramScope *
in_else_scope() const152 ProgramScope::in_else_scope() const
153 {
154 if (scope_type == else_branch)
155 return this;
156
157 if (parent_scope)
158 return parent_scope->in_else_scope();
159
160 return nullptr;
161 }
162
163 const ProgramScope *
in_parent_ifelse_scope() const164 ProgramScope::in_parent_ifelse_scope() const
165 {
166 if (parent_scope)
167 return parent_scope->in_ifelse_scope();
168 else
169 return nullptr;
170 }
171
172 const ProgramScope *
in_ifelse_scope() const173 ProgramScope::in_ifelse_scope() const
174 {
175 if (scope_type == if_branch || scope_type == else_branch)
176 return this;
177
178 if (parent_scope)
179 return parent_scope->in_ifelse_scope();
180
181 return nullptr;
182 }
183
184 bool
is_switchcase_scope_in_loop() const185 ProgramScope::is_switchcase_scope_in_loop() const
186 {
187 return (scope_type == switch_case_branch || scope_type == switch_default_branch) &&
188 is_in_loop();
189 }
190
191 bool
break_is_for_switchcase() const192 ProgramScope::break_is_for_switchcase() const
193 {
194 if (scope_type == loop_body)
195 return false;
196
197 if (scope_type == switch_case_branch || scope_type == switch_default_branch ||
198 scope_type == switch_body)
199 return true;
200
201 if (parent_scope)
202 return parent_scope->break_is_for_switchcase();
203
204 return false;
205 }
206
207 int
id() const208 ProgramScope::id() const
209 {
210 return scope_id;
211 }
212
213 int
begin() const214 ProgramScope::begin() const
215 {
216 return scope_begin;
217 }
218
219 int
end() const220 ProgramScope::end() const
221 {
222 return scope_end;
223 }
224
225 void
set_end(int end)226 ProgramScope::set_end(int end)
227 {
228 if (scope_end == -1)
229 scope_end = end;
230 }
231
232 void
set_loop_break_line(int line)233 ProgramScope::set_loop_break_line(int line)
234 {
235 if (scope_type == loop_body) {
236 break_loop_line = MIN2(break_loop_line, line);
237 } else {
238 if (parent_scope)
239 parent()->set_loop_break_line(line);
240 }
241 }
242
243 int
loop_break_line() const244 ProgramScope::loop_break_line() const
245 {
246 return break_loop_line;
247 }
248
RegisterCompAccess(LiveRange range)249 RegisterCompAccess::RegisterCompAccess(LiveRange range):
250 last_read_scope(nullptr),
251 first_read_scope(nullptr),
252 first_write_scope(nullptr),
253 first_write(range.start),
254 last_read(range.end),
255 last_write(range.start),
256 first_read(std::numeric_limits<int>::max()),
257 conditionality_in_loop_id(conditionality_untouched),
258 if_scope_write_flags(0),
259 next_ifelse_nesting_depth(0),
260 current_unpaired_if_write_scope(nullptr),
261 was_written_in_current_else_scope(false),
262 m_range(range)
263 {
264 }
265
RegisterCompAccess()266 RegisterCompAccess::RegisterCompAccess():
267 RegisterCompAccess(LiveRange(-1, -1))
268 {
269 }
270
271 void
record_read(int block,int line,ProgramScope * scope,LiveRangeEntry::EUse use)272 RegisterCompAccess::record_read(int block, int line, ProgramScope *scope, LiveRangeEntry::EUse use)
273 {
274 last_read_scope = scope;
275
276 if (alu_block_id == block_id_uninitalized) {
277 alu_block_id = block;
278 } else if (alu_block_id != block) {
279 alu_block_id = block_id_not_unique;
280 }
281
282 if (use != LiveRangeEntry::use_unspecified)
283 m_use_type.set(use);
284 if (last_read < line)
285 last_read = line;
286
287 if (first_read > line) {
288 first_read = line;
289 first_read_scope = scope;
290 }
291
292 /* If the conditionality of the first write is already resolved then
293 * no further checks are required.
294 */
295 if (conditionality_in_loop_id == write_is_unconditional ||
296 conditionality_in_loop_id == write_is_conditional)
297 return;
298
299 /* Check whether we are in a condition within a loop */
300 const ProgramScope *ifelse_scope = scope->in_ifelse_scope();
301 const ProgramScope *enclosing_loop;
302 if (ifelse_scope && (enclosing_loop = ifelse_scope->innermost_loop())) {
303
304 /* If we have either not yet written to this register nor writes are
305 * resolved as unconditional in the enclosing loop then check whether
306 * we read before write in an IF/ELSE branch.
307 */
308 if ((conditionality_in_loop_id != write_is_conditional) &&
309 (conditionality_in_loop_id != enclosing_loop->id())) {
310
311 if (current_unpaired_if_write_scope) {
312
313 /* Has been written in this or a parent scope? - this makes the
314 * temporary unconditionally set at this point.
315 */
316 if (scope->is_child_of(current_unpaired_if_write_scope))
317 return;
318
319 /* Has been written in the same scope before it was read? */
320 if (ifelse_scope->type() == if_branch) {
321 if (current_unpaired_if_write_scope->id() == scope->id())
322 return;
323 } else {
324 if (was_written_in_current_else_scope)
325 return;
326 }
327 }
328
329 /* The temporary was read (conditionally) before it is written, hence
330 * it should survive a loop. This can be signaled like if it were
331 * conditionally written.
332 */
333 conditionality_in_loop_id = write_is_conditional;
334 }
335 }
336 }
337
338 void
record_write(int block,int line,ProgramScope * scope)339 RegisterCompAccess::record_write(int block, int line, ProgramScope *scope)
340 {
341 last_write = line;
342 if (alu_block_id == block_id_uninitalized) {
343 alu_block_id = block;
344 } else if (alu_block_id != block) {
345 alu_block_id = block_id_not_unique;
346 }
347
348 if (first_write < 0) {
349 first_write = line;
350 first_write_scope = scope;
351
352 /* If the first write we encounter is not in a conditional branch, or
353 * the conditional write is not within a loop, then this is to be
354 * considered an unconditional dominant write.
355 */
356 const ProgramScope *conditional = scope->enclosing_conditional();
357 if (!conditional || !conditional->innermost_loop()) {
358 conditionality_in_loop_id = write_is_unconditional;
359 }
360 }
361
362 /* The conditionality of the first write is already resolved. */
363 if (conditionality_in_loop_id == write_is_unconditional ||
364 conditionality_in_loop_id == write_is_conditional)
365 return;
366
367 /* If the nesting depth is larger than the supported level,
368 * then we assume conditional writes.
369 */
370 if (next_ifelse_nesting_depth >= supported_ifelse_nesting_depth) {
371 conditionality_in_loop_id = write_is_conditional;
372 return;
373 }
374
375 /* If we are in an IF/ELSE scope within a loop and the loop has not
376 * been resolved already, then record this write.
377 */
378 const ProgramScope *ifelse_scope = scope->in_ifelse_scope();
379 if (ifelse_scope && ifelse_scope->innermost_loop() &&
380 ifelse_scope->innermost_loop()->id() != conditionality_in_loop_id)
381 record_ifelse_write(*ifelse_scope);
382 }
383
384 void
record_ifelse_write(const ProgramScope & scope)385 RegisterCompAccess::record_ifelse_write(const ProgramScope& scope)
386 {
387 if (scope.type() == if_branch) {
388 /* The first write in an IF branch within a loop implies unresolved
389 * conditionality (if it was untouched or unconditional before).
390 */
391 conditionality_in_loop_id = conditionality_unresolved;
392 was_written_in_current_else_scope = false;
393 record_if_write(scope);
394 } else {
395 was_written_in_current_else_scope = true;
396 record_else_write(scope);
397 }
398 }
399
400 void
record_if_write(const ProgramScope & scope)401 RegisterCompAccess::record_if_write(const ProgramScope& scope)
402 {
403 /* Don't record write if this IF scope if it ...
404 * - is not the first write in this IF scope,
405 * - has already been written in a parent IF scope.
406 * In both cases this write is a secondary write that doesn't contribute
407 * to resolve conditionality.
408 *
409 * Record the write if it
410 * - is the first one (obviously),
411 * - happens in an IF branch that is a child of the ELSE branch of the
412 * last active IF/ELSE pair. In this case recording this write is used to
413 * established whether the write is (un-)conditional in the scope
414 * enclosing this outer IF/ELSE pair.
415 */
416 if (!current_unpaired_if_write_scope ||
417 (current_unpaired_if_write_scope->id() != scope.id() &&
418 scope.is_child_of_ifelse_id_sibling(current_unpaired_if_write_scope))) {
419 if_scope_write_flags |= 1 << next_ifelse_nesting_depth;
420 current_unpaired_if_write_scope = &scope;
421 next_ifelse_nesting_depth++;
422 }
423 }
424
425 void
record_else_write(const ProgramScope & scope)426 RegisterCompAccess::record_else_write(const ProgramScope& scope)
427 {
428 int mask = 1 << (next_ifelse_nesting_depth - 1);
429
430 /* If the temporary was written in an IF branch on the same scope level
431 * and this branch is the sibling of this ELSE branch, then we have a
432 * pair of writes that makes write access to this temporary unconditional
433 * in the enclosing scope.
434 */
435
436 if ((if_scope_write_flags & mask) &&
437 (scope.id() == current_unpaired_if_write_scope->id())) {
438 --next_ifelse_nesting_depth;
439 if_scope_write_flags &= ~mask;
440
441 /* The following code deals with propagating unconditionality from
442 * inner levels of nested IF/ELSE to the outer levels like in
443 *
444 * 1: var t;
445 * 2: if (a) { <- start scope A
446 * 3: if (b)
447 * 4: t = ...
448 * 5: else
449 * 6: t = ...
450 * 7: } else { <- start scope B
451 * 8: if (c)
452 * 9: t = ...
453 * A: else <- start scope C
454 * B: t = ...
455 * C: }
456 *
457 */
458
459 const ProgramScope *parent_ifelse = scope.parent()->in_ifelse_scope();
460
461 if (1 << (next_ifelse_nesting_depth - 1) & if_scope_write_flags) {
462 /* We are at the end of scope C and already recorded a write
463 * within an IF scope (A), the sibling of the parent ELSE scope B,
464 * and it is not yet resolved. Mark that as the last relevant
465 * IF scope. Below the write will be resolved for the A/B
466 * scope pair.
467 */
468 current_unpaired_if_write_scope = parent_ifelse;
469 } else {
470 current_unpaired_if_write_scope = nullptr;
471 }
472 /* Promote the first write scope to the enclosing scope because
473 * the current IF/ELSE pair is now irrelevant for the analysis.
474 * This is also required to evaluate the minimum life time for t in
475 * {
476 * var t;
477 * if (a)
478 * t = ...
479 * else
480 * t = ...
481 * x = t;
482 * ...
483 * }
484 */
485 first_write_scope = scope.parent();
486
487 /* If some parent is IF/ELSE and in a loop then propagate the
488 * write to that scope. Otherwise the write is unconditional
489 * because it happens in both corresponding IF/ELSE branches
490 * in this loop, and hence, record the loop id to signal the
491 * resolution.
492 */
493 if (parent_ifelse && parent_ifelse->is_in_loop()) {
494 record_ifelse_write(*parent_ifelse);
495 } else {
496 conditionality_in_loop_id = scope.innermost_loop()->id();
497 }
498 } else {
499 /* The temporary was not written in the IF branch corresponding
500 * to this ELSE branch, hence the write is conditional.
501 */
502 conditionality_in_loop_id = write_is_conditional;
503 }
504 }
505
506 bool
conditional_ifelse_write_in_loop() const507 RegisterCompAccess::conditional_ifelse_write_in_loop() const
508 {
509 return conditionality_in_loop_id <= conditionality_unresolved;
510 }
511
512 void
propagate_live_range_to_dominant_write_scope()513 RegisterCompAccess::propagate_live_range_to_dominant_write_scope()
514 {
515 first_write = first_write_scope->begin();
516 int lr = first_write_scope->end();
517
518 if (last_read < lr)
519 last_read = lr;
520 }
521
522 void
update_required_live_range()523 RegisterCompAccess::update_required_live_range()
524 {
525 bool keep_for_full_loop = false;
526
527 /* This register component is not used at all, or only read,
528 * mark it as unused and ignore it when renaming.
529 * glsl_to_tgsi_visitor::renumber_registers will take care of
530 * eliminating registers that are not written to.
531 */
532 if (last_write < 0) {
533 m_range.start = -1;
534 m_range.end = -1;
535 return;
536 }
537
538 /* Only written to, just make sure the register component is not
539 * reused in the range it is used to write to
540 */
541 if (!last_read_scope) {
542 m_range.start = first_write;
543 m_range.end = last_write + 1;
544 return;
545 }
546
547 assert(first_write_scope || m_range.start >= 0);
548
549 /* The register was pre-defines, so th first write scope is the outerpost
550 * scopw */
551 if (!first_write_scope) {
552 first_write_scope = first_read_scope;
553 while (first_write_scope->parent())
554 first_write_scope = first_write_scope->parent();
555 }
556
557 const ProgramScope *enclosing_scope_first_read = first_read_scope;
558 const ProgramScope *enclosing_scope_first_write = first_write_scope;
559
560 /* We read before writing in a loop
561 * hence the value must survive the loops
562 */
563 if ((first_read <= first_write) && first_read_scope->is_in_loop()) {
564 keep_for_full_loop = true;
565 enclosing_scope_first_read = first_read_scope->outermost_loop();
566 }
567
568 /* A conditional write within a (nested) loop must survive the outermost
569 * loop if the last read was not within the same scope.
570 */
571 const ProgramScope *conditional = enclosing_scope_first_write->enclosing_conditional();
572 if (conditional && !conditional->contains_range_of(*last_read_scope) &&
573 (conditional->is_switchcase_scope_in_loop() ||
574 conditional_ifelse_write_in_loop())) {
575 keep_for_full_loop = true;
576 enclosing_scope_first_write = conditional->outermost_loop();
577 }
578
579 /* Evaluate the scope that is shared by all: required first write scope,
580 * required first read before write scope, and last read scope.
581 */
582 const ProgramScope *enclosing_scope = enclosing_scope_first_read;
583 if (enclosing_scope_first_write->contains_range_of(*enclosing_scope))
584 enclosing_scope = enclosing_scope_first_write;
585
586 if (last_read_scope->contains_range_of(*enclosing_scope))
587 enclosing_scope = last_read_scope;
588
589 while (!enclosing_scope->contains_range_of(*enclosing_scope_first_write) ||
590 !enclosing_scope->contains_range_of(*last_read_scope)) {
591 enclosing_scope = enclosing_scope->parent();
592 assert(enclosing_scope);
593 }
594
595 /* Propagate the last read scope to the target scope */
596 while (enclosing_scope->nesting_depth() < last_read_scope->nesting_depth()) {
597 /* If the read is in a loop and we have to move up the scope we need to
598 * extend the live range to the end of this current loop because at this
599 * point we don't know whether the component was written before
600 * un-conditionally in the same loop.
601 */
602 if (last_read_scope->is_loop())
603 last_read = last_read_scope->end();
604
605 last_read_scope = last_read_scope->parent();
606 }
607
608 /* If the variable has to be kept for the whole loop, and we
609 * are currently in a loop, then propagate the live range.
610 */
611 if (keep_for_full_loop && first_write_scope->is_loop())
612 propagate_live_range_to_dominant_write_scope();
613
614 /* Propagate the first_dominant_write scope to the target scope */
615 while (enclosing_scope->nesting_depth() < first_write_scope->nesting_depth()) {
616 /* Propagate live_range if there was a break in a loop and the write was
617 * after the break inside that loop. Note, that this is only needed if
618 * we move up in the scopes.
619 */
620 if (first_write_scope->loop_break_line() < first_write) {
621 keep_for_full_loop = true;
622 propagate_live_range_to_dominant_write_scope();
623 }
624
625 first_write_scope = first_write_scope->parent();
626
627 /* Propagate live_range if we are now in a loop */
628 if (keep_for_full_loop && first_write_scope->is_loop())
629 propagate_live_range_to_dominant_write_scope();
630 }
631
632 /* The last write past the last read is dead code, but we have to
633 * ensure that the component is not reused too early, hence extend the
634 * live_range past the last write.
635 */
636 if (last_write >= last_read)
637 last_read = last_write + 1;
638
639 /* Here we are at the same scope, all is resolved */
640 m_range.start = first_write;
641 m_range.end = last_read;
642 }
643
644 const int RegisterCompAccess::conditionality_untouched = std::numeric_limits<int>::max();
645
646 const int RegisterCompAccess::write_is_unconditional =
647 std::numeric_limits<int>::max() - 1;
648
RegisterAccess(const std::array<size_t,4> & sizes)649 RegisterAccess::RegisterAccess(const std::array<size_t, 4>& sizes)
650 {
651 for (int i = 0; i < 4; ++i)
652 m_access_record[i].resize(sizes[i]);
653 }
654
655 RegisterCompAccess&
operator ()(const Register & reg)656 RegisterAccess::operator()(const Register& reg)
657 {
658 assert(reg.chan() < 4);
659 assert(m_access_record[reg.chan()].size() > (size_t)reg.index());
660 return m_access_record[reg.chan()][reg.index()];
661 }
662
663 } // namespace r600
664