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