1 /*
2 * Copyright © 2024 Imagination Technologies Ltd.
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7 /**
8 * \file pco_group_instrs.c
9 *
10 * \brief PCO instruction grouping pass.
11 */
12
13 #include "hwdef/rogue_hw_defs.h"
14 #include "pco.h"
15 #include "pco_builder.h"
16 #include "pco_map.h"
17 #include "util/macros.h"
18
19 #include <stdbool.h>
20
21 /**
22 * \brief Calculates the decode-assist value for an instruction group.
23 *
24 * \param[in] igrp PCO instruction group.
25 * \return The decode-assist value.
26 */
calc_da(pco_igrp * igrp)27 static inline unsigned calc_da(pco_igrp *igrp)
28 {
29 unsigned da = igrp->enc.len.hdr;
30 bool no_srcs_dests = !igrp->enc.len.lower_srcs &&
31 !igrp->enc.len.upper_srcs && !igrp->enc.len.dests;
32
33 switch (igrp->hdr.alutype) {
34 case PCO_ALUTYPE_MAIN:
35 case PCO_ALUTYPE_BITWISE: {
36 for (enum pco_op_phase p = _PCO_OP_PHASE_COUNT; p-- > 0;) {
37 if (igrp->hdr.alutype == PCO_ALUTYPE_BITWISE || p > PCO_OP_PHASE_1)
38 da += igrp->enc.len.instrs[p];
39 }
40 break;
41 }
42
43 case PCO_ALUTYPE_CONTROL:
44 if (no_srcs_dests)
45 return 0;
46
47 da += igrp->enc.len.instrs[PCO_OP_PHASE_CTRL];
48 break;
49
50 default:
51 unreachable();
52 }
53
54 return da;
55 }
56
57 /**
58 * \brief Calculates the lengths for an instruction group.
59 *
60 * \param[in,out] igrp PCO instruction group.
61 * \param[in,out] offset_bytes The cumulative shader offset (in bytes).
62 */
calc_lengths(pco_igrp * igrp,unsigned * offset_bytes)63 static inline void calc_lengths(pco_igrp *igrp, unsigned *offset_bytes)
64 {
65 unsigned total_length = 0;
66
67 igrp->enc.len.hdr = pco_igrp_hdr_bytes(igrp->variant.hdr);
68 total_length += igrp->enc.len.hdr;
69
70 igrp->enc.len.lower_srcs = pco_src_bytes(igrp->variant.lower_src);
71 total_length += igrp->enc.len.lower_srcs;
72
73 igrp->enc.len.upper_srcs = pco_src_bytes(igrp->variant.upper_src);
74 total_length += igrp->enc.len.upper_srcs;
75
76 igrp->enc.len.iss = pco_iss_bytes(igrp->variant.iss);
77 total_length += igrp->enc.len.iss;
78
79 igrp->enc.len.dests = pco_dst_bytes(igrp->variant.dest);
80 total_length += igrp->enc.len.dests;
81
82 for (enum pco_op_phase phase = 0; phase < _PCO_OP_PHASE_COUNT; ++phase) {
83 switch (igrp->hdr.alutype) {
84 case PCO_ALUTYPE_MAIN:
85 if (phase == PCO_OP_PHASE_BACKEND) {
86 igrp->enc.len.instrs[phase] =
87 pco_backend_bytes(igrp->variant.instr[phase].backend);
88 } else {
89 igrp->enc.len.instrs[phase] =
90 pco_main_bytes(igrp->variant.instr[phase].main);
91 }
92 break;
93
94 case PCO_ALUTYPE_BITWISE:
95 igrp->enc.len.instrs[phase] =
96 pco_bitwise_bytes(igrp->variant.instr[phase].bitwise);
97 break;
98
99 case PCO_ALUTYPE_CONTROL:
100 igrp->enc.len.instrs[phase] =
101 pco_ctrl_bytes(igrp->variant.instr[phase].ctrl);
102 break;
103
104 default:
105 unreachable();
106 }
107
108 total_length += igrp->enc.len.instrs[phase];
109 }
110
111 igrp->enc.len.word_padding = total_length % 2;
112 total_length += igrp->enc.len.word_padding;
113
114 igrp->enc.len.total = total_length;
115
116 /* Set igrp header length and decode-assist. */
117 igrp->hdr.length = igrp->enc.len.total / 2;
118 igrp->hdr.da = calc_da(igrp);
119
120 /* Set offset and update running offset byte count. */
121 igrp->enc.offset = *offset_bytes;
122 *offset_bytes += igrp->enc.len.total;
123 }
124
125 /**
126 * \brief Calculates the alignment padding to be applied to
127 * the last instruction group in the shader.
128 *
129 * \param[in,out] last_igrp The last instruction group.
130 * \param[in,out] offset_bytes The cumulative shader offset (in bytes).
131 */
calc_align_padding(pco_igrp * last_igrp,unsigned * offset_bytes)132 static inline void calc_align_padding(pco_igrp *last_igrp,
133 unsigned *offset_bytes)
134 {
135 /* We should never end up with a completely empty shader. */
136 assert(last_igrp);
137
138 unsigned total_align = last_igrp->enc.len.total % ROGUE_ICACHE_ALIGN;
139 unsigned offset_align = last_igrp->enc.offset % ROGUE_ICACHE_ALIGN;
140
141 if (total_align) {
142 unsigned padding = ROGUE_ICACHE_ALIGN - total_align;
143 *offset_bytes += padding;
144
145 /* Pad the size of the last igrp. */
146 last_igrp->enc.len.align_padding += padding;
147 last_igrp->enc.len.total += padding;
148
149 /* Update the last igrp header length. */
150 last_igrp->hdr.length = last_igrp->enc.len.total / 2;
151 }
152
153 if (offset_align) {
154 unsigned padding = ROGUE_ICACHE_ALIGN - offset_align;
155 *offset_bytes += padding;
156
157 /* Pad the size of the penultimate igrp. */
158 pco_igrp *penultimate_igrp =
159 list_entry(last_igrp->link.prev, pco_igrp, link);
160
161 penultimate_igrp->enc.len.align_padding += padding;
162 penultimate_igrp->enc.len.total += padding;
163
164 /* Update the penultimate igrp header length. */
165 penultimate_igrp->hdr.length = penultimate_igrp->enc.len.total / 2;
166
167 /* Update the offset of the last igrp. */
168 last_igrp->enc.offset += padding;
169 }
170 }
171
172 /**
173 * \brief Converts a PCO instruction to an instruction group.
174 *
175 * \param[in] b PCO builder.
176 * \param[in] instr PCO instruction.
177 * \param[out] igrp PCO instruction group.
178 * \param[in,out] offset_bytes The cumulative shader offset (in bytes).
179 */
pco_instr_to_igrp(pco_builder * b,pco_instr * instr,pco_igrp * igrp,unsigned * offset_bytes)180 static void pco_instr_to_igrp(pco_builder *b,
181 pco_instr *instr,
182 pco_igrp *igrp,
183 unsigned *offset_bytes)
184 {
185 pco_map_igrp(igrp, instr);
186 calc_lengths(igrp, offset_bytes);
187 pco_builder_insert_igrp(b, igrp);
188 }
189
190 /**
191 * \brief Groups PCO instructions into instruction groups.
192 *
193 * \param[in,out] shader PCO shader.
194 * \return True if the pass made progress.
195 */
pco_group_instrs(pco_shader * shader)196 bool pco_group_instrs(pco_shader *shader)
197 {
198 pco_builder b;
199 pco_igrp *igrp = NULL;
200 unsigned offset_bytes = 0;
201
202 assert(!shader->is_grouped);
203
204 pco_foreach_func_in_shader (func, shader) {
205 /* TODO: double check that *start* alignment is satisfied by
206 * calc_align_padding when having multiple functions?
207 */
208 pco_foreach_block_in_func (block, func) {
209 b = pco_builder_create(func, pco_cursor_before_block(block));
210 pco_foreach_instr_in_block_safe (instr, block) {
211 igrp = pco_igrp_create(func);
212 pco_instr_to_igrp(&b, instr, igrp, &offset_bytes);
213 }
214 }
215
216 /* Ensure the final instruction group has a total size and offset
217 * that are a multiple of the icache alignment.
218 */
219 calc_align_padding(igrp, &offset_bytes);
220 }
221
222 shader->is_grouped = true;
223 return true;
224 }
225