xref: /aosp_15_r20/external/mesa3d/src/gallium/drivers/r600/sfn/sfn_liverangeevaluator_helpers.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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