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