1 /*
2 * Copyright (C) 2020 Collabora Ltd.
3 *
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:
10 *
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
13 * Software.
14 *
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
21 * SOFTWARE.
22 *
23 * Authors (Collabora):
24 * Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
25 */
26
27 #include "compiler.h"
28
29 bool
bi_has_arg(const bi_instr * ins,bi_index arg)30 bi_has_arg(const bi_instr *ins, bi_index arg)
31 {
32 if (!ins)
33 return false;
34
35 bi_foreach_src(ins, s) {
36 if (bi_is_equiv(ins->src[s], arg))
37 return true;
38 }
39
40 return false;
41 }
42
43 /* Precondition: valid 16-bit or 32-bit register format. Returns whether it is
44 * 32-bit. Note auto reads to 32-bit registers even if the memory format is
45 * 16-bit, so is considered as such here */
46
47 bool
bi_is_regfmt_16(enum bi_register_format fmt)48 bi_is_regfmt_16(enum bi_register_format fmt)
49 {
50 switch (fmt) {
51 case BI_REGISTER_FORMAT_F16:
52 case BI_REGISTER_FORMAT_S16:
53 case BI_REGISTER_FORMAT_U16:
54 return true;
55 case BI_REGISTER_FORMAT_F32:
56 case BI_REGISTER_FORMAT_S32:
57 case BI_REGISTER_FORMAT_U32:
58 case BI_REGISTER_FORMAT_AUTO:
59 return false;
60 default:
61 unreachable("Invalid register format");
62 }
63 }
64
65 static unsigned
bi_count_staging_registers(const bi_instr * ins)66 bi_count_staging_registers(const bi_instr *ins)
67 {
68 enum bi_sr_count count = bi_opcode_props[ins->op].sr_count;
69 unsigned vecsize = ins->vecsize + 1; /* XXX: off-by-one */
70
71 switch (count) {
72 case BI_SR_COUNT_0 ... BI_SR_COUNT_4:
73 return count;
74 case BI_SR_COUNT_FORMAT:
75 return bi_is_regfmt_16(ins->register_format) ?
76 DIV_ROUND_UP(vecsize, 2) : vecsize;
77 case BI_SR_COUNT_VECSIZE:
78 return vecsize;
79 case BI_SR_COUNT_SR_COUNT:
80 return ins->sr_count;
81 }
82
83 unreachable("Invalid sr_count");
84 }
85
86 unsigned
bi_count_read_registers(const bi_instr * ins,unsigned s)87 bi_count_read_registers(const bi_instr *ins, unsigned s)
88 {
89 /* PATOM_C reads 1 but writes 2 */
90 if (s == 0 && ins->op == BI_OPCODE_PATOM_C_I32)
91 return 1;
92 else if (s == 0 && bi_opcode_props[ins->op].sr_read)
93 return bi_count_staging_registers(ins);
94 else
95 return 1;
96 }
97
98 unsigned
bi_count_write_registers(const bi_instr * ins,unsigned d)99 bi_count_write_registers(const bi_instr *ins, unsigned d)
100 {
101 if (d == 0 && bi_opcode_props[ins->op].sr_write) {
102 /* TODO: this special case is even more special, TEXC has a
103 * generic write mask stuffed in the desc... */
104 if (ins->op == BI_OPCODE_TEXC)
105 return 4;
106 else
107 return bi_count_staging_registers(ins);
108 } else if (ins->op == BI_OPCODE_SEG_ADD_I64) {
109 return 2;
110 }
111
112 return 1;
113 }
114
115 unsigned
bi_writemask(const bi_instr * ins,unsigned d)116 bi_writemask(const bi_instr *ins, unsigned d)
117 {
118 unsigned mask = BITFIELD_MASK(bi_count_write_registers(ins, d));
119 unsigned shift = ins->dest[d].offset;
120 return (mask << shift);
121 }
122
123 bi_clause *
bi_next_clause(bi_context * ctx,bi_block * block,bi_clause * clause)124 bi_next_clause(bi_context *ctx, bi_block *block, bi_clause *clause)
125 {
126 if (!block && !clause)
127 return NULL;
128
129 /* Try the first clause in this block if we're starting from scratch */
130 if (!clause && !list_is_empty(&block->clauses))
131 return list_first_entry(&block->clauses, bi_clause, link);
132
133 /* Try the next clause in this block */
134 if (clause && clause->link.next != &block->clauses)
135 return list_first_entry(&(clause->link), bi_clause, link);
136
137 /* Try the next block, or the one after that if it's empty, etc .*/
138 bi_block *next_block = bi_next_block(block);
139
140 bi_foreach_block_from(ctx, next_block, block) {
141 if (!list_is_empty(&block->clauses))
142 return list_first_entry(&block->clauses, bi_clause, link);
143 }
144
145 return NULL;
146 }
147
148 /* Does an instruction have a side effect not captured by its register
149 * destination? Applies to certain message-passing instructions, +DISCARD, and
150 * branching only, used in dead code elimation. Branches are characterized by
151 * `last` which applies to them and some atomics, +BARRIER, +BLEND which
152 * implies no loss of generality */
153
154 bool
bi_side_effects(enum bi_opcode op)155 bi_side_effects(enum bi_opcode op)
156 {
157 if (bi_opcode_props[op].last)
158 return true;
159
160 switch (op) {
161 case BI_OPCODE_DISCARD_F32:
162 case BI_OPCODE_DISCARD_B32:
163 return true;
164 default:
165 break;
166 }
167
168 switch (bi_opcode_props[op].message) {
169 case BIFROST_MESSAGE_NONE:
170 case BIFROST_MESSAGE_VARYING:
171 case BIFROST_MESSAGE_ATTRIBUTE:
172 case BIFROST_MESSAGE_TEX:
173 case BIFROST_MESSAGE_VARTEX:
174 case BIFROST_MESSAGE_LOAD:
175 case BIFROST_MESSAGE_64BIT:
176 return false;
177
178 case BIFROST_MESSAGE_STORE:
179 case BIFROST_MESSAGE_ATOMIC:
180 case BIFROST_MESSAGE_BARRIER:
181 case BIFROST_MESSAGE_BLEND:
182 case BIFROST_MESSAGE_Z_STENCIL:
183 case BIFROST_MESSAGE_ATEST:
184 case BIFROST_MESSAGE_JOB:
185 return true;
186
187 case BIFROST_MESSAGE_TILE:
188 return (op != BI_OPCODE_LD_TILE);
189 }
190
191 unreachable("Invalid message type");
192 }
193
194 /* Branch reconvergence is required when the execution mask may change
195 * between adjacent instructions (clauses). This occurs for conditional
196 * branches and for the last instruction (clause) in a block whose
197 * fallthrough successor has multiple predecessors.
198 */
199
200 bool
bi_reconverge_branches(bi_block * block)201 bi_reconverge_branches(bi_block *block)
202 {
203 /* Last block of a program */
204 if (!block->successors[0]) {
205 assert(!block->successors[1]);
206 return true;
207 }
208
209 /* Multiple successors? We're branching */
210 if (block->successors[1])
211 return true;
212
213 /* Must have at least one successor */
214 struct bi_block *succ = block->successors[0];
215 assert(succ->predecessors);
216
217 /* Reconverge if the successor has multiple predecessors */
218 return (succ->predecessors->entries > 1);
219 }
220