• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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