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