1 /*
2 * Copyright 2022 Alyssa Rosenzweig
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "agx_builder.h"
7 #include "agx_compiler.h"
8 #include "agx_opcodes.h"
9
10 /* Lower pseudo instructions created during optimization. */
11
12 static agx_instr *
while_for_break_if(agx_builder * b,agx_instr * I)13 while_for_break_if(agx_builder *b, agx_instr *I)
14 {
15 if (I->op == AGX_OPCODE_BREAK_IF_FCMP) {
16 return agx_while_fcmp(b, I->src[0], I->src[1], I->nest, I->fcond,
17 !I->invert_cond, NULL);
18 } else {
19 return agx_while_icmp(b, I->src[0], I->src[1], I->nest, I->icond,
20 !I->invert_cond, NULL);
21 }
22 }
23
24 static agx_instr *
cmpsel_for_break_if(agx_builder * b,agx_instr * I)25 cmpsel_for_break_if(agx_builder *b, agx_instr *I)
26 {
27 agx_index r0l = agx_register(0, AGX_SIZE_16);
28
29 /* If the condition is true, set r0l to nest to break */
30 agx_index t = agx_immediate(I->nest);
31 agx_index f = r0l;
32
33 if (I->invert_cond) {
34 agx_index temp = t;
35 t = f;
36 f = temp;
37 }
38
39 if (I->op == AGX_OPCODE_BREAK_IF_FCMP)
40 agx_fcmpsel_to(b, r0l, I->src[0], I->src[1], t, f, I->fcond);
41 else
42 agx_icmpsel_to(b, r0l, I->src[0], I->src[1], t, f, I->icond);
43
44 return agx_push_exec(b, 0);
45 }
46
47 static void
swap(agx_builder * b,agx_index x,agx_index y)48 swap(agx_builder *b, agx_index x, agx_index y)
49 {
50 assert(!x.memory && "already lowered");
51 assert(!y.memory && "already lowered");
52
53 /* We can swap lo/hi halves of a 32-bit register with a 32-bit extr */
54 if (x.size == AGX_SIZE_16 && (x.value >> 1) == (y.value >> 1)) {
55
56 assert(((x.value & 1) == (1 - (y.value & 1))) &&
57 "no trivial swaps, and only 2 halves of a register");
58
59 /* r0 = extr r0, r0, #16
60 * = (((r0 << 32) | r0) >> 16) & 0xFFFFFFFF
61 * = (((r0 << 32) >> 16) & 0xFFFFFFFF) | (r0 >> 16)
62 * = (r0l << 16) | r0h
63 */
64 agx_index reg32 = agx_register(x.value & ~1, AGX_SIZE_32);
65 agx_extr_to(b, reg32, reg32, reg32, agx_immediate(16), 0);
66 } else {
67 /* Otherwise, we're swapping GPRs and fallback on a XOR swap. */
68 agx_xor_to(b, x, x, y);
69 agx_xor_to(b, y, x, y);
70 agx_xor_to(b, x, x, y);
71 }
72 }
73
74 static agx_instr *
lower(agx_builder * b,agx_instr * I)75 lower(agx_builder *b, agx_instr *I)
76 {
77 switch (I->op) {
78
79 /* Various instructions are implemented as bitwise truth tables */
80 case AGX_OPCODE_MOV:
81 return agx_bitop_to(b, I->dest[0], I->src[0], agx_zero(), AGX_BITOP_MOV);
82
83 case AGX_OPCODE_NOT:
84 return agx_bitop_to(b, I->dest[0], I->src[0], agx_zero(), AGX_BITOP_NOT);
85
86 /* We can sign-extend with an add */
87 case AGX_OPCODE_SIGNEXT:
88 return agx_iadd_to(b, I->dest[0], I->src[0], agx_zero(), 0);
89
90 /* Unfused comparisons are fused with a 0/1 select */
91 case AGX_OPCODE_ICMP:
92 return agx_icmpsel_to(b, I->dest[0], I->src[0], I->src[1],
93 agx_immediate(I->invert_cond ? 0 : 1),
94 agx_immediate(I->invert_cond ? 1 : 0), I->icond);
95
96 case AGX_OPCODE_FCMP:
97 return agx_fcmpsel_to(b, I->dest[0], I->src[0], I->src[1],
98 agx_immediate(I->invert_cond ? 0 : 1),
99 agx_immediate(I->invert_cond ? 1 : 0), I->fcond);
100
101 case AGX_OPCODE_BALLOT:
102 return agx_icmp_ballot_to(b, I->dest[0], I->src[0], agx_zero(),
103 AGX_ICOND_UEQ, true /* invert */);
104
105 case AGX_OPCODE_QUAD_BALLOT:
106 return agx_icmp_quad_ballot_to(b, I->dest[0], I->src[0], agx_zero(),
107 AGX_ICOND_UEQ, true /* invert */);
108
109 /* Writes to the nesting counter lowered to the real register */
110 case AGX_OPCODE_BEGIN_CF:
111 return agx_mov_imm_to(b, agx_register(0, AGX_SIZE_16), 0);
112
113 case AGX_OPCODE_BREAK:
114 agx_mov_imm_to(b, agx_register(0, AGX_SIZE_16), I->nest);
115 return agx_pop_exec(b, 0);
116
117 case AGX_OPCODE_BREAK_IF_ICMP:
118 case AGX_OPCODE_BREAK_IF_FCMP: {
119 if (I->nest == 1)
120 return while_for_break_if(b, I);
121 else
122 return cmpsel_for_break_if(b, I);
123 }
124
125 case AGX_OPCODE_SWAP:
126 swap(b, I->src[0], I->src[1]);
127 return (void *)true;
128
129 case AGX_OPCODE_EXPORT:
130 /* We already lowered exports during RA, we just need to remove them late
131 * after inserting waits.
132 */
133 return (void *)true;
134
135 default:
136 return NULL;
137 }
138 }
139
140 void
agx_lower_pseudo(agx_context * ctx)141 agx_lower_pseudo(agx_context *ctx)
142 {
143 agx_foreach_instr_global_safe(ctx, I) {
144 agx_builder b = agx_init_builder(ctx, agx_before_instr(I));
145
146 if (lower(&b, I))
147 agx_remove_instruction(I);
148 }
149 }
150