2 * Copyright © 2020 Google, Inc.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 #include "util/ralloc.h"
25 #include "util/u_dynarray.h"
30 * A bit more extra cleanup after sched pass. In particular, prior to
31 * instruction scheduling, we can't easily eliminate unneeded mov's
32 * from "arrays", because we don't yet know if there is an intervening
33 * array-write scheduled before the use of the array-read.
35 * NOTE array is equivalent to nir "registers".. ie. it can be length of
36 * one. It is basically anything that is not SSA.
41 * Check if any instruction before `use` and after `src` writes to the
42 * specified array. If `offset` is negative, it is a relative (a0.x)
43 * access and we care about all writes to the array (as we don't know
44 * which array element is read). Otherwise in the case of non-relative
45 * access, we only have to care about the write to the specified (>= 0)
49 has_conflicting_write(struct ir3_instruction
*src
,
50 struct ir3_instruction
*use
,
51 unsigned id
, int offset
)
53 assert(src
->block
== use
->block
);
55 /* NOTE that since src and use are in the same block, src by
56 * definition appears in the block's instr_list before use:
58 foreach_instr_rev (instr
, &use
->node
) {
62 /* if we are looking at a RELATIV read, we can't move
63 * it past an a0.x write:
65 if ((offset
< 0) && (dest_regs(instr
) > 0) &&
66 (instr
->regs
[0]->num
== regid(REG_A0
, 0)))
69 if (!writes_gpr(instr
))
72 struct ir3_register
*dst
= instr
->regs
[0];
73 if (!(dst
->flags
& IR3_REG_ARRAY
))
76 if (dst
->array
.id
!= id
)
80 * At this point, we have narrowed down an instruction
81 * that writes to the same array.. check if it the write
82 * is to an array element that we care about:
85 /* is write to an unknown array element? */
86 if (dst
->flags
& IR3_REG_RELATIV
)
89 /* is read from an unknown array element? */
93 /* is write to same array element? */
94 if (dst
->array
.offset
== offset
)
101 /* Can we fold the mov src into use without invalid flags? */
103 valid_flags(struct ir3_instruction
*use
, struct ir3_instruction
*mov
)
105 struct ir3_register
*src
= mov
->regs
[1];
107 foreach_src_n (reg
, n
, use
) {
111 if (!ir3_valid_flags(use
, n
, reg
->flags
| src
->flags
))
119 instr_cp_postsched(struct ir3_instruction
*mov
)
121 struct ir3_register
*src
= mov
->regs
[1];
123 /* only consider mov's from "arrays", other cases we have
124 * already considered already:
126 if (!(src
->flags
& IR3_REG_ARRAY
))
129 int offset
= (src
->flags
& IR3_REG_RELATIV
) ? -1 : src
->array
.offset
;
131 /* Once we move the array read directly into the consuming
132 * instruction(s), we will also need to update instructions
133 * that had a false-dep on the original mov to have deps
134 * on the consuming instructions:
136 struct util_dynarray newdeps
;
137 util_dynarray_init(&newdeps
, mov
->uses
);
139 foreach_ssa_use (use
, mov
) {
140 if (use
->block
!= mov
->block
)
146 if (has_conflicting_write(mov
, use
, src
->array
.id
, offset
))
149 if (conflicts(mov
->address
, use
->address
))
152 if (!valid_flags(use
, mov
))
155 /* Ok, we've established that it is safe to remove this copy: */
157 bool removed
= false;
158 foreach_src_n (reg
, n
, use
) {
162 use
->regs
[n
+ 1] = ir3_reg_clone(mov
->block
->shader
, src
);
164 /* preserve (abs)/etc modifiers: */
165 use
->regs
[n
+ 1]-> flags
|= reg
->flags
;
170 /* the use could have been only a false-dep, only add to
171 * the newdeps array if we've actually updated a real
172 * src reg for the use:
175 util_dynarray_append(&newdeps
, struct ir3_instruction
*, use
);
177 /* Remove the use from the src instruction: */
178 _mesa_set_remove_key(mov
->uses
, use
);
182 /* Once we have the complete set of instruction(s) that are are now
183 * directly reading from the array, update any false-dep uses to
184 * now depend on these instructions. The only remaining uses at
185 * this point should be false-deps:
187 foreach_ssa_use (use
, mov
) {
188 util_dynarray_foreach(&newdeps
, struct ir3_instruction
*, instrp
) {
189 struct ir3_instruction
*newdep
= *instrp
;
190 ir3_instr_add_dep(use
, newdep
);
194 return util_dynarray_num_elements(&newdeps
, struct ir3_instruction
**) > 0;
198 ir3_cp_postsched(struct ir3
*ir
)
200 void *mem_ctx
= ralloc_context(NULL
);
201 bool progress
= false;
203 ir3_find_ssa_uses(ir
, mem_ctx
, false);
205 foreach_block (block
, &ir
->block_list
) {
206 foreach_instr_safe (instr
, &block
->instr_list
) {
207 if (is_same_type_mov(instr
))
208 progress
|= instr_cp_postsched(instr
);
212 ralloc_free(mem_ctx
);