1 /*
2 * Copyright (C) 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io>
3 * Copyright (C) 2020 Collabora Ltd.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 #ifndef __AGX_COMPILER_H
26 #define __AGX_COMPILER_H
27
28 #include "compiler/nir/nir.h"
29 #include "util/u_math.h"
30 #include "util/half_float.h"
31 #include "util/u_dynarray.h"
32 #include "util/u_worklist.h"
33 #include "agx_compile.h"
34 #include "agx_opcodes.h"
35 #include "agx_minifloat.h"
36
37 #ifdef __cplusplus
38 extern "C" {
39 #endif
40
41 enum agx_dbg {
42 AGX_DBG_MSGS = BITFIELD_BIT(0),
43 AGX_DBG_SHADERS = BITFIELD_BIT(1),
44 AGX_DBG_SHADERDB = BITFIELD_BIT(2),
45 AGX_DBG_VERBOSE = BITFIELD_BIT(3),
46 AGX_DBG_INTERNAL = BITFIELD_BIT(4),
47 AGX_DBG_NOVALIDATE = BITFIELD_BIT(5),
48 };
49
50 extern int agx_debug;
51
52 /* r0-r127 inclusive, as pairs of 16-bits, gives 256 registers */
53 #define AGX_NUM_REGS (256)
54
55 enum agx_index_type {
56 AGX_INDEX_NULL = 0,
57 AGX_INDEX_NORMAL = 1,
58 AGX_INDEX_IMMEDIATE = 2,
59 AGX_INDEX_UNIFORM = 3,
60 AGX_INDEX_REGISTER = 4,
61 };
62
63 enum agx_size {
64 AGX_SIZE_16 = 0,
65 AGX_SIZE_32 = 1,
66 AGX_SIZE_64 = 2
67 };
68
69 static inline unsigned
agx_size_align_16(enum agx_size size)70 agx_size_align_16(enum agx_size size)
71 {
72 switch (size) {
73 case AGX_SIZE_16: return 1;
74 case AGX_SIZE_32: return 2;
75 case AGX_SIZE_64: return 4;
76 }
77
78 unreachable("Invalid size");
79 }
80
81 typedef struct {
82 /* Sufficient for as many SSA values as we need. Immediates and uniforms fit in 16-bits */
83 unsigned value : 22;
84
85 /* Indicates that this source kills the referenced value (because it is the
86 * last use in a block and the source is not live after the block). Set by
87 * liveness analysis. */
88 bool kill : 1;
89
90 /* Cache hints */
91 bool cache : 1;
92 bool discard : 1;
93
94 /* src - float modifiers */
95 bool abs : 1;
96 bool neg : 1;
97
98 enum agx_size size : 2;
99 enum agx_index_type type : 3;
100 } agx_index;
101
102 static inline agx_index
agx_get_index(unsigned value,enum agx_size size)103 agx_get_index(unsigned value, enum agx_size size)
104 {
105 return (agx_index) {
106 .value = value,
107 .size = size,
108 .type = AGX_INDEX_NORMAL,
109 };
110 }
111
112 static inline agx_index
agx_immediate(uint16_t imm)113 agx_immediate(uint16_t imm)
114 {
115 return (agx_index) {
116 .value = imm,
117 .size = AGX_SIZE_32,
118 .type = AGX_INDEX_IMMEDIATE,
119 };
120 }
121
122 static inline agx_index
agx_immediate_f(float f)123 agx_immediate_f(float f)
124 {
125 assert(agx_minifloat_exact(f));
126 return agx_immediate(agx_minifloat_encode(f));
127 }
128
129 /* in half-words, specify r0h as 1, r1 as 2... */
130 static inline agx_index
agx_register(uint8_t imm,enum agx_size size)131 agx_register(uint8_t imm, enum agx_size size)
132 {
133 return (agx_index) {
134 .value = imm,
135 .size = size,
136 .type = AGX_INDEX_REGISTER,
137 };
138 }
139
140 /* Also in half-words */
141 static inline agx_index
agx_uniform(uint8_t imm,enum agx_size size)142 agx_uniform(uint8_t imm, enum agx_size size)
143 {
144 return (agx_index) {
145 .value = imm,
146 .size = size,
147 .type = AGX_INDEX_UNIFORM,
148 };
149 }
150
151 static inline agx_index
agx_null()152 agx_null()
153 {
154 return (agx_index) { .type = AGX_INDEX_NULL };
155 }
156
157 static inline agx_index
agx_zero()158 agx_zero()
159 {
160 return agx_immediate(0);
161 }
162
163 /* IEEE 754 additive identity -0.0, stored as an 8-bit AGX minifloat: mantissa
164 * = exponent = 0, sign bit set */
165
166 static inline agx_index
agx_negzero()167 agx_negzero()
168 {
169 return agx_immediate(0x80);
170 }
171
172 static inline agx_index
agx_abs(agx_index idx)173 agx_abs(agx_index idx)
174 {
175 idx.abs = true;
176 idx.neg = false;
177 return idx;
178 }
179
180 static inline agx_index
agx_neg(agx_index idx)181 agx_neg(agx_index idx)
182 {
183 idx.neg ^= true;
184 return idx;
185 }
186
187 /* Replaces an index, preserving any modifiers */
188
189 static inline agx_index
agx_replace_index(agx_index old,agx_index replacement)190 agx_replace_index(agx_index old, agx_index replacement)
191 {
192 replacement.abs = old.abs;
193 replacement.neg = old.neg;
194 return replacement;
195 }
196
197 static inline bool
agx_is_null(agx_index idx)198 agx_is_null(agx_index idx)
199 {
200 return idx.type == AGX_INDEX_NULL;
201 }
202
203 /* Compares equivalence as references */
204
205 static inline bool
agx_is_equiv(agx_index left,agx_index right)206 agx_is_equiv(agx_index left, agx_index right)
207 {
208 return (left.type == right.type) && (left.value == right.value);
209 }
210
211 #define AGX_MAX_DESTS 4
212 #define AGX_MAX_SRCS 5
213
214 enum agx_icond {
215 AGX_ICOND_UEQ = 0,
216 AGX_ICOND_ULT = 1,
217 AGX_ICOND_UGT = 2,
218 /* unknown */
219 AGX_ICOND_SEQ = 4,
220 AGX_ICOND_SLT = 5,
221 AGX_ICOND_SGT = 6,
222 /* unknown */
223 };
224
225 enum agx_fcond {
226 AGX_FCOND_EQ = 0,
227 AGX_FCOND_LT = 1,
228 AGX_FCOND_GT = 2,
229 AGX_FCOND_LTN = 3,
230 /* unknown */
231 AGX_FCOND_GE = 5,
232 AGX_FCOND_LE = 6,
233 AGX_FCOND_GTN = 7,
234 };
235
236 enum agx_round {
237 AGX_ROUND_RTZ = 0,
238 AGX_ROUND_RTE = 1,
239 };
240
241 enum agx_convert {
242 AGX_CONVERT_U8_TO_F = 0,
243 AGX_CONVERT_S8_TO_F = 1,
244 AGX_CONVERT_F_TO_U16 = 4,
245 AGX_CONVERT_F_TO_S16 = 5,
246 AGX_CONVERT_U16_TO_F = 6,
247 AGX_CONVERT_S16_TO_F = 7,
248 AGX_CONVERT_F_TO_U32 = 8,
249 AGX_CONVERT_F_TO_S32 = 9,
250 AGX_CONVERT_U32_TO_F = 10,
251 AGX_CONVERT_S32_TO_F = 11
252 };
253
254 enum agx_lod_mode {
255 AGX_LOD_MODE_AUTO_LOD = 0,
256 AGX_LOD_MODE_AUTO_LOD_BIAS = 5,
257 AGX_LOD_MODE_LOD_MIN = 6,
258 AGX_LOD_GRAD = 8,
259 AGX_LOD_GRAD_MIN = 12
260 };
261
262 enum agx_dim {
263 AGX_DIM_TEX_1D = 0,
264 AGX_DIM_TEX_1D_ARRAY = 1,
265 AGX_DIM_TEX_2D = 2,
266 AGX_DIM_TEX_2D_ARRAY = 3,
267 AGX_DIM_TEX_2D_MS = 4,
268 AGX_DIM_TEX_3D = 5,
269 AGX_DIM_TEX_CUBE = 6,
270 AGX_DIM_TEX_CUBE_ARRAY = 7
271 };
272
273 /* Forward declare for branch target */
274 struct agx_block;
275
276 typedef struct {
277 /* Must be first */
278 struct list_head link;
279
280 /* The sources list.
281 *
282 * As a special case to workaround ordering issues when translating phis, if
283 * nr_srcs == 0 and the opcode is PHI, holds a pointer to the NIR phi node.
284 */
285 union {
286 agx_index *src;
287 nir_phi_instr *phi;
288 };
289
290 enum agx_opcode op;
291
292 /* Data flow */
293 agx_index dest[AGX_MAX_DESTS];
294
295 unsigned nr_srcs;
296
297 union {
298 uint32_t imm;
299 uint32_t writeout;
300 uint32_t truth_table;
301 uint32_t component;
302 uint32_t channels;
303 uint32_t bfi_mask;
304 enum agx_sr sr;
305 enum agx_icond icond;
306 enum agx_fcond fcond;
307 enum agx_format format;
308 enum agx_round round;
309 enum agx_lod_mode lod_mode;
310 struct agx_block *target;
311 };
312
313 /* For load varying */
314 bool perspective : 1;
315
316 /* Invert icond/fcond */
317 bool invert_cond : 1;
318
319 /* TODO: Handle tex ops more efficient */
320 enum agx_dim dim : 3;
321
322 /* Final st_vary op */
323 bool last : 1;
324
325 /* Shift for a bitwise or memory op (conflicts with format for memory ops) */
326 unsigned shift : 4;
327
328 /* Scoreboard index, 0 or 1. Leave as 0 for instructions that do not require
329 * scoreboarding (everything but memory load/store and texturing). */
330 unsigned scoreboard : 1;
331
332 /* Number of nested control flow layers to jump by */
333 unsigned nest : 2;
334
335 /* Output modifiers */
336 bool saturate : 1;
337 unsigned mask : 4;
338 } agx_instr;
339
340 struct agx_block;
341
342 typedef struct agx_block {
343 /* Link to next block. Must be first */
344 struct list_head link;
345
346 /* List of instructions emitted for the current block */
347 struct list_head instructions;
348
349 /* Index of the block in source order */
350 unsigned index;
351
352 /* Control flow graph */
353 struct agx_block *successors[2];
354 struct util_dynarray predecessors;
355 bool unconditional_jumps;
356
357 /* Liveness analysis results */
358 BITSET_WORD *live_in;
359 BITSET_WORD *live_out;
360
361 /* Register allocation */
362 BITSET_DECLARE(regs_out, AGX_NUM_REGS);
363
364 /* Offset of the block in the emitted binary */
365 off_t offset;
366
367 /** Available for passes to use for metadata */
368 uint8_t pass_flags;
369 } agx_block;
370
371 typedef struct {
372 nir_shader *nir;
373 gl_shader_stage stage;
374 struct list_head blocks; /* list of agx_block */
375 struct agx_shader_info *out;
376 struct agx_shader_key *key;
377
378 /* Remapping table for varyings indexed by driver_location */
379 unsigned varyings[AGX_MAX_VARYINGS];
380
381 /* Place to start pushing new values */
382 unsigned push_base;
383
384 /* Maximum block index */
385 unsigned num_blocks;
386
387 /* For creating temporaries */
388 unsigned alloc;
389
390 /* I don't really understand how writeout ops work yet */
391 bool did_writeout;
392
393 /* Has r0l been zeroed yet due to control flow? */
394 bool any_cf;
395
396 /* Number of nested control flow structures within the innermost loop. Since
397 * NIR is just loop and if-else, this is the number of nested if-else
398 * statements in the loop */
399 unsigned loop_nesting;
400
401 /* During instruction selection, for inserting control flow */
402 agx_block *current_block;
403 agx_block *continue_block;
404 agx_block *break_block;
405 agx_block *after_block;
406 agx_block **indexed_nir_blocks;
407
408 /* During instruction selection, map from vector agx_index to its scalar
409 * components, populated by a split. */
410 struct hash_table_u64 *allocated_vec;
411
412 /* Stats for shader-db */
413 unsigned loop_count;
414 unsigned spills;
415 unsigned fills;
416 } agx_context;
417
418 static inline void
agx_remove_instruction(agx_instr * ins)419 agx_remove_instruction(agx_instr *ins)
420 {
421 list_del(&ins->link);
422 }
423
424 static inline agx_index
agx_temp(agx_context * ctx,enum agx_size size)425 agx_temp(agx_context *ctx, enum agx_size size)
426 {
427 return agx_get_index(ctx->alloc++, size);
428 }
429
430 static enum agx_size
agx_size_for_bits(unsigned bits)431 agx_size_for_bits(unsigned bits)
432 {
433 switch (bits) {
434 case 1:
435 case 16: return AGX_SIZE_16;
436 case 32: return AGX_SIZE_32;
437 case 64: return AGX_SIZE_64;
438 default: unreachable("Invalid bitsize");
439 }
440 }
441
442 static inline agx_index
agx_src_index(nir_src * src)443 agx_src_index(nir_src *src)
444 {
445 assert(src->is_ssa);
446
447 return agx_get_index(src->ssa->index,
448 agx_size_for_bits(nir_src_bit_size(*src)));
449 }
450
451 static inline agx_index
agx_dest_index(nir_dest * dst)452 agx_dest_index(nir_dest *dst)
453 {
454 assert(dst->is_ssa);
455
456 return agx_get_index(dst->ssa.index,
457 agx_size_for_bits(nir_dest_bit_size(*dst)));
458 }
459
460 static inline agx_index
agx_vec_for_dest(agx_context * ctx,nir_dest * dest)461 agx_vec_for_dest(agx_context *ctx, nir_dest *dest)
462 {
463 return agx_temp(ctx, agx_size_for_bits(nir_dest_bit_size(*dest)));
464 }
465
466 static inline agx_index
agx_vec_for_intr(agx_context * ctx,nir_intrinsic_instr * instr)467 agx_vec_for_intr(agx_context *ctx, nir_intrinsic_instr *instr)
468 {
469 return agx_vec_for_dest(ctx, &instr->dest);
470 }
471
472 /* Iterators for AGX IR */
473
474 #define agx_foreach_block(ctx, v) \
475 list_for_each_entry(agx_block, v, &ctx->blocks, link)
476
477 #define agx_foreach_block_rev(ctx, v) \
478 list_for_each_entry_rev(agx_block, v, &ctx->blocks, link)
479
480 #define agx_foreach_block_from(ctx, from, v) \
481 list_for_each_entry_from(agx_block, v, from, &ctx->blocks, link)
482
483 #define agx_foreach_block_from_rev(ctx, from, v) \
484 list_for_each_entry_from_rev(agx_block, v, from, &ctx->blocks, link)
485
486 #define agx_foreach_instr_in_block(block, v) \
487 list_for_each_entry(agx_instr, v, &(block)->instructions, link)
488
489 #define agx_foreach_instr_in_block_rev(block, v) \
490 list_for_each_entry_rev(agx_instr, v, &(block)->instructions, link)
491
492 #define agx_foreach_instr_in_block_safe(block, v) \
493 list_for_each_entry_safe(agx_instr, v, &(block)->instructions, link)
494
495 #define agx_foreach_instr_in_block_safe_rev(block, v) \
496 list_for_each_entry_safe_rev(agx_instr, v, &(block)->instructions, link)
497
498 #define agx_foreach_instr_in_block_from(block, v, from) \
499 list_for_each_entry_from(agx_instr, v, from, &(block)->instructions, link)
500
501 #define agx_foreach_instr_in_block_from_rev(block, v, from) \
502 list_for_each_entry_from_rev(agx_instr, v, from, &(block)->instructions, link)
503
504 #define agx_foreach_instr_global(ctx, v) \
505 agx_foreach_block(ctx, v_block) \
506 agx_foreach_instr_in_block(v_block, v)
507
508 #define agx_foreach_instr_global_rev(ctx, v) \
509 agx_foreach_block_rev(ctx, v_block) \
510 agx_foreach_instr_in_block_rev(v_block, v)
511
512 #define agx_foreach_instr_global_safe(ctx, v) \
513 agx_foreach_block(ctx, v_block) \
514 agx_foreach_instr_in_block_safe(v_block, v)
515
516 #define agx_foreach_instr_global_safe_rev(ctx, v) \
517 agx_foreach_block_rev(ctx, v_block) \
518 agx_foreach_instr_in_block_safe_rev(v_block, v)
519
520 /* Based on set_foreach, expanded with automatic type casts */
521
522 #define agx_foreach_successor(blk, v) \
523 agx_block *v; \
524 agx_block **_v; \
525 for (_v = (agx_block **) &blk->successors[0], \
526 v = *_v; \
527 v != NULL && _v < (agx_block **) &blk->successors[2]; \
528 _v++, v = *_v) \
529
530 #define agx_foreach_predecessor(blk, v) \
531 util_dynarray_foreach(&blk->predecessors, agx_block *, v)
532
533 #define agx_foreach_src(ins, v) \
534 for (unsigned v = 0; v < ins->nr_srcs; ++v)
535
536 #define agx_foreach_dest(ins, v) \
537 for (unsigned v = 0; v < ARRAY_SIZE(ins->dest); ++v)
538
539 /*
540 * Find the index of a predecessor, used as the implicit order of phi sources.
541 */
542 static inline unsigned
agx_predecessor_index(agx_block * succ,agx_block * pred)543 agx_predecessor_index(agx_block *succ, agx_block *pred)
544 {
545 unsigned index = 0;
546
547 agx_foreach_predecessor(succ, x) {
548 if (*x == pred) return index;
549
550 index++;
551 }
552
553 unreachable("Invalid predecessor");
554 }
555
556 static inline agx_instr *
agx_prev_op(agx_instr * ins)557 agx_prev_op(agx_instr *ins)
558 {
559 return list_last_entry(&(ins->link), agx_instr, link);
560 }
561
562 static inline agx_instr *
agx_next_op(agx_instr * ins)563 agx_next_op(agx_instr *ins)
564 {
565 return list_first_entry(&(ins->link), agx_instr, link);
566 }
567
568 static inline agx_block *
agx_next_block(agx_block * block)569 agx_next_block(agx_block *block)
570 {
571 return list_first_entry(&(block->link), agx_block, link);
572 }
573
574 static inline agx_block *
agx_exit_block(agx_context * ctx)575 agx_exit_block(agx_context *ctx)
576 {
577 agx_block *last = list_last_entry(&ctx->blocks, agx_block, link);
578 assert(!last->successors[0] && !last->successors[1]);
579 return last;
580 }
581
582 #define agx_worklist_init(ctx, w) u_worklist_init(w, ctx->num_blocks, ctx)
583 #define agx_worklist_push_head(w, block) u_worklist_push_head(w, block, index)
584 #define agx_worklist_push_tail(w, block) u_worklist_push_tail(w, block, index)
585 #define agx_worklist_peek_head(w) u_worklist_peek_head(w, agx_block, index)
586 #define agx_worklist_pop_head(w) u_worklist_pop_head( w, agx_block, index)
587 #define agx_worklist_peek_tail(w) u_worklist_peek_tail(w, agx_block, index)
588 #define agx_worklist_pop_tail(w) u_worklist_pop_tail( w, agx_block, index)
589
590 /* Like in NIR, for use with the builder */
591
592 enum agx_cursor_option {
593 agx_cursor_after_block,
594 agx_cursor_before_instr,
595 agx_cursor_after_instr
596 };
597
598 typedef struct {
599 enum agx_cursor_option option;
600
601 union {
602 agx_block *block;
603 agx_instr *instr;
604 };
605 } agx_cursor;
606
607 static inline agx_cursor
agx_after_block(agx_block * block)608 agx_after_block(agx_block *block)
609 {
610 return (agx_cursor) {
611 .option = agx_cursor_after_block,
612 .block = block
613 };
614 }
615
616 static inline agx_cursor
agx_before_instr(agx_instr * instr)617 agx_before_instr(agx_instr *instr)
618 {
619 return (agx_cursor) {
620 .option = agx_cursor_before_instr,
621 .instr = instr
622 };
623 }
624
625 static inline agx_cursor
agx_after_instr(agx_instr * instr)626 agx_after_instr(agx_instr *instr)
627 {
628 return (agx_cursor) {
629 .option = agx_cursor_after_instr,
630 .instr = instr
631 };
632 }
633
634 /*
635 * Get a cursor inserting at the logical end of the block. In particular, this
636 * is before branches or control flow instructions, which occur after the
637 * logical end but before the physical end.
638 */
639 static inline agx_cursor
agx_after_block_logical(agx_block * block)640 agx_after_block_logical(agx_block *block)
641 {
642 /* Search for a p_logical_end */
643 agx_foreach_instr_in_block_rev(block, I) {
644 if (I->op == AGX_OPCODE_P_LOGICAL_END)
645 return agx_before_instr(I);
646 }
647
648 /* If there's no p_logical_end, use the physical end */
649 return agx_after_block(block);
650 }
651
652 /* IR builder in terms of cursor infrastructure */
653
654 typedef struct {
655 agx_context *shader;
656 agx_cursor cursor;
657 } agx_builder;
658
659 static inline agx_builder
agx_init_builder(agx_context * ctx,agx_cursor cursor)660 agx_init_builder(agx_context *ctx, agx_cursor cursor)
661 {
662 return (agx_builder) {
663 .shader = ctx,
664 .cursor = cursor
665 };
666 }
667
668 /* Insert an instruction at the cursor and move the cursor */
669
670 static inline void
agx_builder_insert(agx_cursor * cursor,agx_instr * I)671 agx_builder_insert(agx_cursor *cursor, agx_instr *I)
672 {
673 switch (cursor->option) {
674 case agx_cursor_after_instr:
675 list_add(&I->link, &cursor->instr->link);
676 cursor->instr = I;
677 return;
678
679 case agx_cursor_after_block:
680 list_addtail(&I->link, &cursor->block->instructions);
681 cursor->option = agx_cursor_after_instr;
682 cursor->instr = I;
683 return;
684
685 case agx_cursor_before_instr:
686 list_addtail(&I->link, &cursor->instr->link);
687 cursor->option = agx_cursor_after_instr;
688 cursor->instr = I;
689 return;
690 }
691
692 unreachable("Invalid cursor option");
693 }
694
695 /* Uniform file management */
696
697 agx_index
698 agx_indexed_sysval(agx_context *ctx, enum agx_push_type type, enum agx_size size,
699 unsigned index, unsigned length);
700
701 /* Routines defined for AIR */
702
703 void agx_print_instr(agx_instr *I, FILE *fp);
704 void agx_print_block(agx_block *block, FILE *fp);
705 void agx_print_shader(agx_context *ctx, FILE *fp);
706 void agx_optimizer(agx_context *ctx);
707 void agx_lower_pseudo(agx_context *ctx);
708 void agx_dce(agx_context *ctx);
709 void agx_ra(agx_context *ctx);
710 void agx_pack_binary(agx_context *ctx, struct util_dynarray *emission);
711
712 #ifndef NDEBUG
713 void agx_validate(agx_context *ctx, const char *after_str);
714 #else
agx_validate(UNUSED agx_context * ctx,UNUSED const char * after_str)715 static inline void agx_validate(UNUSED agx_context *ctx, UNUSED const char *after_str) { return; }
716 #endif
717
718 unsigned agx_write_registers(agx_instr *I, unsigned d);
719
720 struct agx_copy {
721 /* Base register destination of the copy */
722 unsigned dest;
723
724 /* Base register source of the copy */
725 unsigned src;
726
727 /* Size of the copy */
728 enum agx_size size;
729
730 /* Whether the copy has been handled. Callers must leave to false. */
731 bool done;
732 };
733
734 void
735 agx_emit_parallel_copies(agx_builder *b, struct agx_copy *copies, unsigned n);
736
737 void agx_compute_liveness(agx_context *ctx);
738 void agx_liveness_ins_update(BITSET_WORD *live, agx_instr *I);
739
740 #ifdef __cplusplus
741 } /* extern C */
742 #endif
743
744 #endif
745