pan/bi: Pack register fields
[mesa.git] / src / panfrost / bifrost / bi_pack.c
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
24 #include "compiler.h"
25
26 /* This file contains the final passes of the compiler. Running after
27 * scheduling and RA, the IR is now finalized, so we need to emit it to actual
28 * bits on the wire (as well as fixup branches) */
29
30 static uint64_t
31 bi_pack_header(bi_clause *clause, bi_clause *next)
32 {
33 struct bifrost_header header = {
34 /* stub */
35 .no_end_of_shader = (next != NULL),
36 };
37
38 uint64_t u = 0;
39 memcpy(&u, &header, sizeof(header));
40 return u;
41 }
42
43 /* Represents the assignment of ports for a given bundle */
44
45 struct bi_registers {
46 /* Register to assign to each port */
47 unsigned port[4];
48
49 /* Read ports can be disabled */
50 bool enabled[2];
51
52 /* Should we write FMA? what about ADD? If only a single port is
53 * enabled it is in port 2, else ADD/FMA is 2/3 respectively */
54 bool write_fma, write_add;
55
56 /* Should we read with port 3? */
57 bool read_port3;
58
59 /* Packed uniform/constant */
60 unsigned uniform_constant;
61
62 /* Whether writes are actually for the last instruction */
63 bool first_instruction;
64 };
65
66 /* Determines the register control field, ignoring the first? flag */
67
68 static enum bifrost_reg_control
69 bi_pack_register_ctrl_lo(struct bi_registers r)
70 {
71 if (r.write_fma) {
72 if (r.write_add) {
73 assert(!r.read_port3);
74 return BIFROST_WRITE_ADD_P2_FMA_P3;
75 } else {
76 if (r.read_port3)
77 return BIFROST_WRITE_FMA_P2_READ_P3;
78 else
79 return BIFROST_WRITE_FMA_P2;
80 }
81 } else if (r.write_add) {
82 if (r.read_port3)
83 return BIFROST_WRITE_ADD_P2_READ_P3;
84 else
85 return BIFROST_WRITE_ADD_P2;
86 } else if (r.read_port3)
87 return BIFROST_READ_P3;
88 else
89 return BIFROST_REG_NONE;
90 }
91
92 /* Ditto but account for the first? flag this time */
93
94 static enum bifrost_reg_control
95 bi_pack_register_ctrl(struct bi_registers r)
96 {
97 enum bifrost_reg_control ctrl = bi_pack_register_ctrl_lo(r);
98
99 if (r.first_instruction) {
100 if (ctrl == BIFROST_REG_NONE)
101 ctrl = BIFROST_FIRST_NONE;
102 else
103 ctrl |= BIFROST_FIRST_NONE;
104 }
105
106 return ctrl;
107 }
108
109 static uint64_t
110 bi_pack_registers(struct bi_registers regs)
111 {
112 enum bifrost_reg_control ctrl = bi_pack_register_ctrl(regs);
113 struct bifrost_regs s;
114 uint64_t packed = 0;
115
116 if (regs.enabled[1]) {
117 /* Gotta save that bit!~ Required by the 63-x trick */
118 assert(regs.port[1] > regs.port[0]);
119 assert(regs.enabled[0]);
120
121 /* Do the 63-x trick, see docs/disasm */
122 if (regs.port[0] > 31) {
123 regs.port[0] = 63 - regs.port[0];
124 regs.port[1] = 63 - regs.port[1];
125 }
126
127 assert(regs.port[0] <= 31);
128 assert(regs.port[1] <= 63);
129
130 s.ctrl = ctrl;
131 s.reg1 = regs.port[1];
132 s.reg0 = regs.port[0];
133 } else {
134 /* Port 1 disabled, so set to zero and use port 1 for ctrl */
135 s.reg1 = ctrl << 2;
136
137 if (regs.enabled[0]) {
138 /* Bit 0 upper bit of port 0 */
139 s.reg1 |= (regs.port[0] >> 5);
140
141 /* Rest of port 0 in usual spot */
142 s.reg0 = (regs.port[0] & 0b11111);
143 } else {
144 /* Bit 1 set if port 0 also disabled */
145 s.reg1 |= (1 << 1);
146 }
147 }
148
149 s.reg3 = regs.port[3];
150 s.reg2 = regs.port[2];
151 s.uniform_const = regs.uniform_constant;
152
153 memcpy(&packed, &s, sizeof(s));
154 return packed;
155 }
156
157 static unsigned
158 bi_pack_fma(bi_clause *clause, bi_bundle bundle)
159 {
160 /* TODO */
161 return BIFROST_FMA_NOP;
162 }
163
164 static unsigned
165 bi_pack_add(bi_clause *clause, bi_bundle bundle)
166 {
167 /* TODO */
168 return BIFROST_ADD_NOP;
169 }
170
171 struct bi_packed_bundle {
172 uint64_t lo;
173 uint64_t hi;
174 };
175
176 static struct bi_packed_bundle
177 bi_pack_bundle(bi_clause *clause, bi_bundle bundle)
178 {
179 struct bi_registers regs = {
180 .enabled = { true, true },
181 .port = { 34, 35, 3, 4 },
182 .write_fma = true,
183 .write_add = true,
184 .first_instruction = true,
185 };
186
187 uint64_t reg = bi_pack_registers(regs);
188 uint64_t fma = bi_pack_fma(clause, bundle);
189 uint64_t add = bi_pack_add(clause, bundle);
190
191 struct bi_packed_bundle packed = {
192 .lo = reg | (fma << 35) | ((add & 0b111111) << 58),
193 .hi = add >> 6
194 };
195
196 return packed;
197 }
198
199 static void
200 bi_pack_clause(bi_context *ctx, bi_clause *clause, bi_clause *next,
201 struct util_dynarray *emission)
202 {
203 struct bi_packed_bundle ins_1 = bi_pack_bundle(clause, clause->bundles[0]);
204 assert(clause->bundle_count == 1);
205
206 struct bifrost_fmt1 quad_1 = {
207 .tag = BIFROST_FMT1_FINAL,
208 .header = bi_pack_header(clause, next),
209 .ins_1 = ins_1.lo,
210 .ins_2 = ins_1.hi & ((1 << 11) - 1),
211 .ins_0 = (ins_1.hi >> 11) & 0b111,
212 };
213
214 util_dynarray_append(emission, struct bifrost_fmt1, quad_1);
215 }
216
217 static bi_clause *
218 bi_next_clause(bi_context *ctx, pan_block *block, bi_clause *clause)
219 {
220 /* Try the next clause in this block */
221 if (clause->link.next != &((bi_block *) block)->clauses)
222 return list_first_entry(&(clause->link), bi_clause, link);
223
224 /* Try the next block, or the one after that if it's empty, etc .*/
225 pan_block *next_block = pan_next_block(block);
226
227 bi_foreach_block_from(ctx, next_block, block) {
228 bi_block *blk = (bi_block *) block;
229
230 if (!list_is_empty(&blk->clauses))
231 return list_first_entry(&(blk->clauses), bi_clause, link);
232 }
233
234 return NULL;
235 }
236
237 void
238 bi_pack(bi_context *ctx, struct util_dynarray *emission)
239 {
240 util_dynarray_init(emission, NULL);
241
242 bi_foreach_block(ctx, _block) {
243 bi_block *block = (bi_block *) _block;
244
245 bi_foreach_clause_in_block(block, clause) {
246 bi_clause *next = bi_next_clause(ctx, _block, clause);
247 bi_pack_clause(ctx, clause, next, emission);
248 }
249 }
250 }