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