1 /*
2 * Copyright © 2024 Imagination Technologies Ltd.
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7 #ifndef PCO_BUILDER_H
8 #define PCO_BUILDER_H
9
10 /**
11 * \\file pco_builder.h
12 *
13 * \\brief PCO builder header.
14 */
15
16 #include "pco.h"
17 #include "pco_internal.h"
18 #include "util/list.h"
19 #include "util/macros.h"
20
21 #include <stdbool.h>
22
23 /** Cursor pointer. */
24 enum pco_cursor_option {
25 PCO_CURSOR_BEFORE_CF_NODE,
26 PCO_CURSOR_AFTER_CF_NODE,
27
28 PCO_CURSOR_BEFORE_INSTR,
29 PCO_CURSOR_AFTER_INSTR,
30
31 PCO_CURSOR_BEFORE_IGRP,
32 PCO_CURSOR_AFTER_IGRP,
33 };
34
35 /** Cursor for PCO instructions/groups and basic blocks. */
36 typedef struct pco_cursor {
37 enum pco_cursor_option option; /** Cursor pointer option. */
38 union {
39 pco_cf_node *cf_node;
40 pco_instr *instr;
41 pco_igrp *igrp;
42 };
43 } pco_cursor;
44
45 /** PCO builder context. */
46 typedef struct pco_builder {
47 pco_func *func; /** Target function. */
48 pco_cursor cursor; /** Current position in the function. */
49 } pco_builder;
50
51 /* Cursor position setters. */
52 /**
53 * \brief Returns a cursor set to before a cf node.
54 *
55 * \param[in] cf_node The cf node.
56 * \return The cursor.
57 */
pco_cursor_before_cf_node(pco_cf_node * cf_node)58 static inline pco_cursor pco_cursor_before_cf_node(pco_cf_node *cf_node)
59 {
60 return (pco_cursor){
61 .option = PCO_CURSOR_BEFORE_CF_NODE,
62 .cf_node = cf_node,
63 };
64 }
65
66 /**
67 * \brief Returns a cursor set to after a cf node.
68 *
69 * \param[in] cf_node The cf node.
70 * \return The cursor.
71 */
pco_cursor_after_cf_node(pco_cf_node * cf_node)72 static inline pco_cursor pco_cursor_after_cf_node(pco_cf_node *cf_node)
73 {
74 return (pco_cursor){
75 .option = PCO_CURSOR_AFTER_CF_NODE,
76 .cf_node = cf_node,
77 };
78 }
79
80 /**
81 * \brief Returns a cursor set to before a block.
82 *
83 * \param[in] block The block.
84 * \return The cursor.
85 */
pco_cursor_before_block(pco_block * block)86 static inline pco_cursor pco_cursor_before_block(pco_block *block)
87 {
88 return pco_cursor_before_cf_node(&block->cf_node);
89 }
90
91 /**
92 * \brief Returns a cursor set to after a block.
93 *
94 * \param[in] block The block.
95 * \return The cursor.
96 */
pco_cursor_after_block(pco_block * block)97 static inline pco_cursor pco_cursor_after_block(pco_block *block)
98 {
99 return pco_cursor_after_cf_node(&block->cf_node);
100 }
101
102 /**
103 * \brief Returns a cursor set to before an instruction.
104 *
105 * \param[in] instr The instruction.
106 * \return The cursor.
107 */
pco_cursor_before_instr(pco_instr * instr)108 static inline pco_cursor pco_cursor_before_instr(pco_instr *instr)
109 {
110 return (pco_cursor){
111 .option = PCO_CURSOR_BEFORE_INSTR,
112 .instr = instr,
113 };
114 }
115
116 /**
117 * \brief Returns a cursor set to after an instruction.
118 *
119 * \param[in] instr The instruction.
120 * \return The cursor.
121 */
pco_cursor_after_instr(pco_instr * instr)122 static inline pco_cursor pco_cursor_after_instr(pco_instr *instr)
123 {
124 return (pco_cursor){
125 .option = PCO_CURSOR_AFTER_INSTR,
126 .instr = instr,
127 };
128 }
129
130 /**
131 * \brief Returns a cursor set to before an instruction group.
132 *
133 * \param[in] igrp The instruction group.
134 * \return The cursor.
135 */
pco_cursor_before_igrp(pco_igrp * igrp)136 static inline pco_cursor pco_cursor_before_igrp(pco_igrp *igrp)
137 {
138 return (pco_cursor){
139 .option = PCO_CURSOR_BEFORE_IGRP,
140 .igrp = igrp,
141 };
142 }
143
144 /**
145 * \brief Returns a cursor set to after an instruction group.
146 *
147 * \param[in] igrp The instruction group.
148 * \return The cursor.
149 */
pco_cursor_after_igrp(pco_igrp * igrp)150 static inline pco_cursor pco_cursor_after_igrp(pco_igrp *igrp)
151 {
152 return (pco_cursor){
153 .option = PCO_CURSOR_AFTER_IGRP,
154 .igrp = igrp,
155 };
156 }
157
158 /**
159 * \brief Returns whether a cursor is set to before a construct.
160 *
161 * \param[in] cursor The cursor.
162 * \return True if the cursor is set to before a construct.
163 */
pco_cursor_is_before(pco_cursor cursor)164 static inline bool pco_cursor_is_before(pco_cursor cursor)
165 {
166 return cursor.option == PCO_CURSOR_BEFORE_CF_NODE ||
167 cursor.option == PCO_CURSOR_BEFORE_INSTR ||
168 cursor.option == PCO_CURSOR_BEFORE_IGRP;
169 }
170
171 /* Cursor get functions. */
172 /**
173 * \brief Returns the function being pointed to by the cursor.
174 *
175 * \param[in] cursor The cursor.
176 * \return The function being pointed to.
177 */
pco_cursor_func(pco_cursor cursor)178 static inline pco_func *pco_cursor_func(pco_cursor cursor)
179 {
180 switch (cursor.option) {
181 case PCO_CURSOR_BEFORE_CF_NODE:
182 case PCO_CURSOR_AFTER_CF_NODE:
183 switch (cursor.cf_node->type) {
184 case PCO_CF_NODE_TYPE_BLOCK:
185 return pco_cf_node_as_block(cursor.cf_node)->parent_func;
186
187 case PCO_CF_NODE_TYPE_IF:
188 return pco_cf_node_as_if(cursor.cf_node)->parent_func;
189
190 case PCO_CF_NODE_TYPE_LOOP:
191 return pco_cf_node_as_loop(cursor.cf_node)->parent_func;
192
193 case PCO_CF_NODE_TYPE_FUNC:
194 return pco_cf_node_as_func(cursor.cf_node);
195
196 default:
197 break;
198 }
199 break;
200
201 case PCO_CURSOR_BEFORE_INSTR:
202 case PCO_CURSOR_AFTER_INSTR:
203 return cursor.instr->parent_func;
204
205 case PCO_CURSOR_BEFORE_IGRP:
206 case PCO_CURSOR_AFTER_IGRP:
207 return cursor.igrp->parent_func;
208
209 default:
210 break;
211 }
212
213 unreachable();
214 }
215
216 /**
217 * \brief Returns the cf node being pointed to by the cursor.
218 *
219 * \param[in] cursor The cursor.
220 * \return The cf node being pointed to.
221 */
pco_cursor_cf_node(pco_cursor cursor)222 static inline pco_cf_node *pco_cursor_cf_node(pco_cursor cursor)
223 {
224 switch (cursor.option) {
225 case PCO_CURSOR_BEFORE_CF_NODE:
226 case PCO_CURSOR_AFTER_CF_NODE:
227 return cursor.cf_node;
228
229 case PCO_CURSOR_BEFORE_INSTR:
230 case PCO_CURSOR_AFTER_INSTR:
231 return &cursor.instr->parent_block->cf_node;
232
233 case PCO_CURSOR_BEFORE_IGRP:
234 case PCO_CURSOR_AFTER_IGRP:
235 return &cursor.igrp->parent_block->cf_node;
236
237 default:
238 break;
239 }
240
241 unreachable();
242 }
243
244 /**
245 * \brief Returns the block being pointed to by the cursor.
246 *
247 * \param[in] cursor The cursor.
248 * \return The block being pointed to.
249 */
pco_cursor_block(pco_cursor cursor)250 static inline pco_block *pco_cursor_block(pco_cursor cursor)
251 {
252 switch (cursor.option) {
253 case PCO_CURSOR_BEFORE_CF_NODE:
254 case PCO_CURSOR_AFTER_CF_NODE:
255 switch (cursor.cf_node->type) {
256 case PCO_CF_NODE_TYPE_BLOCK:
257 return pco_cf_node_as_block(cursor.cf_node);
258
259 /* TODO: other cf node types? */
260 default:
261 break;
262 }
263 break;
264
265 case PCO_CURSOR_BEFORE_INSTR:
266 case PCO_CURSOR_AFTER_INSTR:
267 return cursor.instr->parent_block;
268
269 case PCO_CURSOR_BEFORE_IGRP:
270 case PCO_CURSOR_AFTER_IGRP:
271 return cursor.igrp->parent_block;
272
273 default:
274 break;
275 }
276
277 unreachable();
278 }
279
280 /**
281 * \brief Returns the instruction being pointed to by the cursor.
282 *
283 * \param[in] cursor The cursor.
284 * \return The instruction being pointed to.
285 */
pco_cursor_instr(pco_cursor cursor)286 static inline pco_instr *pco_cursor_instr(pco_cursor cursor)
287 {
288 bool before = pco_cursor_is_before(cursor);
289
290 switch (cursor.option) {
291 case PCO_CURSOR_BEFORE_CF_NODE:
292 case PCO_CURSOR_AFTER_CF_NODE: {
293 pco_block *block = NULL;
294
295 switch (cursor.cf_node->type) {
296 case PCO_CF_NODE_TYPE_BLOCK:
297 block = pco_cf_node_as_block(cursor.cf_node);
298 return before ? pco_first_instr(block) : pco_last_instr(block);
299
300 /* TODO: other cf node types? */
301 default:
302 break;
303 }
304 break;
305 }
306
307 case PCO_CURSOR_BEFORE_INSTR:
308 case PCO_CURSOR_AFTER_INSTR:
309 return cursor.instr;
310
311 default:
312 break;
313 }
314
315 unreachable();
316 }
317
318 /**
319 * \brief Returns the instruction group being pointed to by the cursor.
320 *
321 * \param[in] cursor The cursor.
322 * \return The instruction group being pointed to.
323 */
pco_cursor_igrp(pco_cursor cursor)324 static inline pco_igrp *pco_cursor_igrp(pco_cursor cursor)
325 {
326 bool before = pco_cursor_is_before(cursor);
327
328 switch (cursor.option) {
329 case PCO_CURSOR_BEFORE_CF_NODE:
330 case PCO_CURSOR_AFTER_CF_NODE: {
331 pco_block *block = NULL;
332
333 switch (cursor.cf_node->type) {
334 case PCO_CF_NODE_TYPE_BLOCK:
335 block = pco_cf_node_as_block(cursor.cf_node);
336 /* Special case: we're in pco_group_instrs and want to go from
337 * the start.
338 */
339 if (!block->parent_func->parent_shader->is_grouped)
340 return NULL;
341 return before ? pco_first_igrp(block) : pco_last_igrp(block);
342
343 /* TODO: other cf node types? */
344 default:
345 break;
346 }
347 break;
348 }
349
350 case PCO_CURSOR_BEFORE_IGRP:
351 case PCO_CURSOR_AFTER_IGRP:
352 return cursor.igrp;
353
354 default:
355 break;
356 }
357
358 unreachable();
359 }
360
361 /* Builder functions. */
362 /**
363 * \brief Creates a builder.
364 *
365 * \param[in] func The function being targeted.
366 * \param[in] cursor The cursor.
367 * \return The builder.
368 */
pco_builder_create(pco_func * func,pco_cursor cursor)369 static pco_builder pco_builder_create(pco_func *func, pco_cursor cursor)
370 {
371 return (pco_builder){
372 .func = func,
373 .cursor = cursor,
374 };
375 }
376
377 /**
378 * \brief Inserts a block at a position specified by the builder.
379 *
380 * \param[in] b The builder.
381 * \param[in] block The block.
382 */
383 /* TODO: test with multiple blocks. */
pco_builder_insert_block(pco_builder * b,pco_block * block)384 static inline void pco_builder_insert_block(pco_builder *b, pco_block *block)
385 {
386 struct list_head *list = &pco_cursor_cf_node(b->cursor)->link;
387 bool before = pco_cursor_is_before(b->cursor);
388
389 list_add(&block->cf_node.link, before ? list->prev : list);
390 b->cursor = pco_cursor_after_block(block);
391 }
392
393 /**
394 * \brief Inserts a instruction at a position specified by the builder.
395 *
396 * \param[in] b The builder.
397 * \param[in] instr The instruction.
398 */
pco_builder_insert_instr(pco_builder * b,pco_instr * instr)399 static inline void pco_builder_insert_instr(pco_builder *b, pco_instr *instr)
400 {
401 pco_instr *cursor_instr = pco_cursor_instr(b->cursor);
402 bool before = pco_cursor_is_before(b->cursor);
403 pco_block *block = pco_cursor_block(b->cursor);
404 struct list_head *list = cursor_instr ? &cursor_instr->link : &block->instrs;
405
406 instr->parent_block = block;
407
408 list_add(&instr->link, (before && cursor_instr) ? list->prev : list);
409 b->cursor = pco_cursor_after_instr(instr);
410 }
411
412 /**
413 * \brief Inserts a instruction group at a position specified by the builder.
414 *
415 * \param[in] b The builder.
416 * \param[in] igrp The instruction group.
417 */
pco_builder_insert_igrp(pco_builder * b,pco_igrp * igrp)418 static inline void pco_builder_insert_igrp(pco_builder *b, pco_igrp *igrp)
419 {
420 pco_igrp *cursor_igrp = pco_cursor_igrp(b->cursor);
421 bool before = pco_cursor_is_before(b->cursor);
422 pco_block *block = pco_cursor_block(b->cursor);
423 struct list_head *list = cursor_igrp ? &cursor_igrp->link : &block->instrs;
424
425 igrp->parent_block = block;
426
427 list_add(&igrp->link, (before && cursor_igrp) ? list->prev : list);
428 b->cursor = pco_cursor_after_igrp(igrp);
429 }
430
431 /* Generated op building functions. */
432 #include "pco_builder_ops.h"
433
434 /**
435 * \brief Returns whether the instruction has the default execution condition.
436 *
437 * \param[in] instr The instruction.
438 * \return True if the instruction has the default execution condition.
439 */
pco_instr_default_exec(pco_instr * instr)440 static inline bool pco_instr_default_exec(pco_instr *instr)
441 {
442 if (!pco_instr_has_exec_cnd(instr))
443 return true;
444
445 return pco_instr_get_exec_cnd(instr) == PCO_EXEC_CND_E1_ZX;
446 }
447 #endif /* PCO_BUILDER_H */
448