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