1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright (C) 2021 Valve Corporation
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci *
11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci * Software.
14bf215546Sopenharmony_ci *
15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20bf215546Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21bf215546Sopenharmony_ci * SOFTWARE.
22bf215546Sopenharmony_ci */
23bf215546Sopenharmony_ci
24bf215546Sopenharmony_ci/* This pass lowers array accesses to SSA.
25bf215546Sopenharmony_ci *
26bf215546Sopenharmony_ci * After this pass, instructions writing arrays implicitly read the contents of
27bf215546Sopenharmony_ci * the array defined in instr->dsts[0]->def (possibly a phi node), perform the
28bf215546Sopenharmony_ci * operation, and store to instr->dsts[0].
29bf215546Sopenharmony_ci *
30bf215546Sopenharmony_ci * This makes arrays appear like "normal" SSA values, even if the false
31bf215546Sopenharmony_ci * dependencies mean that they always stay in CSSA form (i.e. able to removed
32bf215546Sopenharmony_ci * out-of-SSA with no copies.) While hopefully they shouldn't induce copies in
33bf215546Sopenharmony_ci * most cases, we can't make that guarantee while also splitting spilling from
34bf215546Sopenharmony_ci * RA and guaranteeing a certain number of registers are used, so we have to
35bf215546Sopenharmony_ci * insert the phi nodes to be able to know when copying should happen.
36bf215546Sopenharmony_ci *
37bf215546Sopenharmony_ci * The implementation is based on the idea in "Simple and Efficient Construction
38bf215546Sopenharmony_ci * of Static Single Assignment Form" of scanning backwards to find the
39bf215546Sopenharmony_ci * definition. However, since we're not doing this on-the-fly we can simplify
40bf215546Sopenharmony_ci * things a little by doing a pre-pass to get the last definition of each array
41bf215546Sopenharmony_ci * in each block. Then we optimize trivial phis in a separate pass, "on the fly"
42bf215546Sopenharmony_ci * so that we don't have to rewrite (and keep track of) users.
43bf215546Sopenharmony_ci */
44bf215546Sopenharmony_ci
45bf215546Sopenharmony_ci#include <stdlib.h>
46bf215546Sopenharmony_ci#include "ir3.h"
47bf215546Sopenharmony_ci
48bf215546Sopenharmony_cistruct array_state {
49bf215546Sopenharmony_ci   struct ir3_register *live_in_definition;
50bf215546Sopenharmony_ci   struct ir3_register *live_out_definition;
51bf215546Sopenharmony_ci   bool constructed;
52bf215546Sopenharmony_ci   bool optimized;
53bf215546Sopenharmony_ci};
54bf215546Sopenharmony_ci
55bf215546Sopenharmony_cistruct array_ctx {
56bf215546Sopenharmony_ci   struct array_state *states;
57bf215546Sopenharmony_ci   struct ir3 *ir;
58bf215546Sopenharmony_ci   unsigned array_count;
59bf215546Sopenharmony_ci};
60bf215546Sopenharmony_ci
61bf215546Sopenharmony_cistatic struct array_state *
62bf215546Sopenharmony_ciget_state(struct array_ctx *ctx, struct ir3_block *block, unsigned id)
63bf215546Sopenharmony_ci{
64bf215546Sopenharmony_ci   return &ctx->states[ctx->array_count * block->index + id];
65bf215546Sopenharmony_ci}
66bf215546Sopenharmony_ci
67bf215546Sopenharmony_cistatic struct ir3_register *read_value_beginning(struct array_ctx *ctx,
68bf215546Sopenharmony_ci                                                 struct ir3_block *block,
69bf215546Sopenharmony_ci                                                 struct ir3_array *arr);
70bf215546Sopenharmony_ci
71bf215546Sopenharmony_cistatic struct ir3_register *
72bf215546Sopenharmony_ciread_value_end(struct array_ctx *ctx, struct ir3_block *block,
73bf215546Sopenharmony_ci               struct ir3_array *arr)
74bf215546Sopenharmony_ci{
75bf215546Sopenharmony_ci   struct array_state *state = get_state(ctx, block, arr->id);
76bf215546Sopenharmony_ci   if (state->live_out_definition)
77bf215546Sopenharmony_ci      return state->live_out_definition;
78bf215546Sopenharmony_ci
79bf215546Sopenharmony_ci   state->live_out_definition = read_value_beginning(ctx, block, arr);
80bf215546Sopenharmony_ci   return state->live_out_definition;
81bf215546Sopenharmony_ci}
82bf215546Sopenharmony_ci
83bf215546Sopenharmony_ci/* Roughly equivalent to readValueRecursive from the paper: */
84bf215546Sopenharmony_cistatic struct ir3_register *
85bf215546Sopenharmony_ciread_value_beginning(struct array_ctx *ctx, struct ir3_block *block,
86bf215546Sopenharmony_ci                     struct ir3_array *arr)
87bf215546Sopenharmony_ci{
88bf215546Sopenharmony_ci   struct array_state *state = get_state(ctx, block, arr->id);
89bf215546Sopenharmony_ci
90bf215546Sopenharmony_ci   if (state->constructed)
91bf215546Sopenharmony_ci      return state->live_in_definition;
92bf215546Sopenharmony_ci
93bf215546Sopenharmony_ci   if (block->predecessors_count == 0) {
94bf215546Sopenharmony_ci      state->constructed = true;
95bf215546Sopenharmony_ci      return NULL;
96bf215546Sopenharmony_ci   }
97bf215546Sopenharmony_ci
98bf215546Sopenharmony_ci   if (block->predecessors_count == 1) {
99bf215546Sopenharmony_ci      state->live_in_definition =
100bf215546Sopenharmony_ci         read_value_end(ctx, block->predecessors[0], arr);
101bf215546Sopenharmony_ci      state->constructed = true;
102bf215546Sopenharmony_ci      return state->live_in_definition;
103bf215546Sopenharmony_ci   }
104bf215546Sopenharmony_ci
105bf215546Sopenharmony_ci   unsigned flags = IR3_REG_ARRAY | (arr->half ? IR3_REG_HALF : 0);
106bf215546Sopenharmony_ci   struct ir3_instruction *phi =
107bf215546Sopenharmony_ci      ir3_instr_create(block, OPC_META_PHI, 1, block->predecessors_count);
108bf215546Sopenharmony_ci   list_del(&phi->node);
109bf215546Sopenharmony_ci   list_add(&phi->node, &block->instr_list);
110bf215546Sopenharmony_ci
111bf215546Sopenharmony_ci   struct ir3_register *dst = __ssa_dst(phi);
112bf215546Sopenharmony_ci   dst->flags |= flags;
113bf215546Sopenharmony_ci   dst->array.id = arr->id;
114bf215546Sopenharmony_ci   dst->size = arr->length;
115bf215546Sopenharmony_ci
116bf215546Sopenharmony_ci   state->live_in_definition = phi->dsts[0];
117bf215546Sopenharmony_ci   state->constructed = true;
118bf215546Sopenharmony_ci
119bf215546Sopenharmony_ci   for (unsigned i = 0; i < block->predecessors_count; i++) {
120bf215546Sopenharmony_ci      struct ir3_register *src =
121bf215546Sopenharmony_ci         read_value_end(ctx, block->predecessors[i], arr);
122bf215546Sopenharmony_ci      struct ir3_register *src_reg;
123bf215546Sopenharmony_ci      if (src) {
124bf215546Sopenharmony_ci         src_reg = __ssa_src(phi, src->instr, flags);
125bf215546Sopenharmony_ci      } else {
126bf215546Sopenharmony_ci         src_reg = ir3_src_create(phi, INVALID_REG, flags | IR3_REG_SSA);
127bf215546Sopenharmony_ci      }
128bf215546Sopenharmony_ci      src_reg->array.id = arr->id;
129bf215546Sopenharmony_ci      src_reg->size = arr->length;
130bf215546Sopenharmony_ci   }
131bf215546Sopenharmony_ci   return phi->dsts[0];
132bf215546Sopenharmony_ci}
133bf215546Sopenharmony_ci
134bf215546Sopenharmony_cistatic struct ir3_register *
135bf215546Sopenharmony_ciremove_trivial_phi(struct ir3_instruction *phi)
136bf215546Sopenharmony_ci{
137bf215546Sopenharmony_ci   /* Break cycles */
138bf215546Sopenharmony_ci   if (phi->data)
139bf215546Sopenharmony_ci      return phi->data;
140bf215546Sopenharmony_ci
141bf215546Sopenharmony_ci   phi->data = phi->dsts[0];
142bf215546Sopenharmony_ci
143bf215546Sopenharmony_ci   struct ir3_register *unique_def = NULL;
144bf215546Sopenharmony_ci   bool unique = true;
145bf215546Sopenharmony_ci   for (unsigned i = 0; i < phi->block->predecessors_count; i++) {
146bf215546Sopenharmony_ci      struct ir3_register *src = phi->srcs[i];
147bf215546Sopenharmony_ci
148bf215546Sopenharmony_ci      /* If there are any undef sources, then the remaining sources may not
149bf215546Sopenharmony_ci       * dominate the phi node, even if they are all equal. So we need to
150bf215546Sopenharmony_ci       * bail out in this case.
151bf215546Sopenharmony_ci       *
152bf215546Sopenharmony_ci       * This seems to be a bug in the original paper.
153bf215546Sopenharmony_ci       */
154bf215546Sopenharmony_ci      if (!src->def) {
155bf215546Sopenharmony_ci         unique = false;
156bf215546Sopenharmony_ci         break;
157bf215546Sopenharmony_ci      }
158bf215546Sopenharmony_ci
159bf215546Sopenharmony_ci      struct ir3_instruction *src_instr = src->def->instr;
160bf215546Sopenharmony_ci
161bf215546Sopenharmony_ci      /* phi sources which point to the phi itself don't count for
162bf215546Sopenharmony_ci       * figuring out if the phi is trivial
163bf215546Sopenharmony_ci       */
164bf215546Sopenharmony_ci      if (src_instr == phi)
165bf215546Sopenharmony_ci         continue;
166bf215546Sopenharmony_ci
167bf215546Sopenharmony_ci      if (src_instr->opc == OPC_META_PHI) {
168bf215546Sopenharmony_ci         src->def = remove_trivial_phi(src->def->instr);
169bf215546Sopenharmony_ci      }
170bf215546Sopenharmony_ci
171bf215546Sopenharmony_ci      if (unique_def) {
172bf215546Sopenharmony_ci         if (unique_def != src->def) {
173bf215546Sopenharmony_ci            unique = false;
174bf215546Sopenharmony_ci            break;
175bf215546Sopenharmony_ci         }
176bf215546Sopenharmony_ci      } else {
177bf215546Sopenharmony_ci         unique_def = src->def;
178bf215546Sopenharmony_ci      }
179bf215546Sopenharmony_ci   }
180bf215546Sopenharmony_ci
181bf215546Sopenharmony_ci   if (unique) {
182bf215546Sopenharmony_ci      phi->data = unique_def;
183bf215546Sopenharmony_ci      return unique_def;
184bf215546Sopenharmony_ci   } else {
185bf215546Sopenharmony_ci      return phi->dsts[0];
186bf215546Sopenharmony_ci   }
187bf215546Sopenharmony_ci}
188bf215546Sopenharmony_ci
189bf215546Sopenharmony_cistatic struct ir3_register *
190bf215546Sopenharmony_cilookup_value(struct ir3_register *reg)
191bf215546Sopenharmony_ci{
192bf215546Sopenharmony_ci   if (reg->instr->opc == OPC_META_PHI)
193bf215546Sopenharmony_ci      return reg->instr->data;
194bf215546Sopenharmony_ci   return reg;
195bf215546Sopenharmony_ci}
196bf215546Sopenharmony_ci
197bf215546Sopenharmony_cistatic struct ir3_register *
198bf215546Sopenharmony_cilookup_live_in(struct array_ctx *ctx, struct ir3_block *block, unsigned id)
199bf215546Sopenharmony_ci{
200bf215546Sopenharmony_ci   struct array_state *state = get_state(ctx, block, id);
201bf215546Sopenharmony_ci   if (state->live_in_definition)
202bf215546Sopenharmony_ci      return lookup_value(state->live_in_definition);
203bf215546Sopenharmony_ci
204bf215546Sopenharmony_ci   return NULL;
205bf215546Sopenharmony_ci}
206bf215546Sopenharmony_ci
207bf215546Sopenharmony_cibool
208bf215546Sopenharmony_ciir3_array_to_ssa(struct ir3 *ir)
209bf215546Sopenharmony_ci{
210bf215546Sopenharmony_ci   struct array_ctx ctx = {};
211bf215546Sopenharmony_ci
212bf215546Sopenharmony_ci   foreach_array (array, &ir->array_list) {
213bf215546Sopenharmony_ci      ctx.array_count = MAX2(ctx.array_count, array->id + 1);
214bf215546Sopenharmony_ci   }
215bf215546Sopenharmony_ci
216bf215546Sopenharmony_ci   if (ctx.array_count == 0)
217bf215546Sopenharmony_ci      return false;
218bf215546Sopenharmony_ci
219bf215546Sopenharmony_ci   unsigned i = 0;
220bf215546Sopenharmony_ci   foreach_block (block, &ir->block_list) {
221bf215546Sopenharmony_ci      block->index = i++;
222bf215546Sopenharmony_ci   }
223bf215546Sopenharmony_ci
224bf215546Sopenharmony_ci   ctx.ir = ir;
225bf215546Sopenharmony_ci   ctx.states = calloc(ctx.array_count * i, sizeof(struct array_state));
226bf215546Sopenharmony_ci
227bf215546Sopenharmony_ci   foreach_block (block, &ir->block_list) {
228bf215546Sopenharmony_ci      foreach_instr (instr, &block->instr_list) {
229bf215546Sopenharmony_ci         foreach_dst (dst, instr) {
230bf215546Sopenharmony_ci            if (dst->flags & IR3_REG_ARRAY) {
231bf215546Sopenharmony_ci               struct array_state *state =
232bf215546Sopenharmony_ci                  get_state(&ctx, block, dst->array.id);
233bf215546Sopenharmony_ci               state->live_out_definition = dst;
234bf215546Sopenharmony_ci            }
235bf215546Sopenharmony_ci         }
236bf215546Sopenharmony_ci      }
237bf215546Sopenharmony_ci   }
238bf215546Sopenharmony_ci
239bf215546Sopenharmony_ci   foreach_block (block, &ir->block_list) {
240bf215546Sopenharmony_ci      foreach_instr (instr, &block->instr_list) {
241bf215546Sopenharmony_ci         if (instr->opc == OPC_META_PHI)
242bf215546Sopenharmony_ci            continue;
243bf215546Sopenharmony_ci
244bf215546Sopenharmony_ci         foreach_dst (reg, instr) {
245bf215546Sopenharmony_ci            if ((reg->flags & IR3_REG_ARRAY) && !reg->tied) {
246bf215546Sopenharmony_ci               struct ir3_array *arr = ir3_lookup_array(ir, reg->array.id);
247bf215546Sopenharmony_ci
248bf215546Sopenharmony_ci               /* Construct any phi nodes necessary to read this value */
249bf215546Sopenharmony_ci               read_value_beginning(&ctx, block, arr);
250bf215546Sopenharmony_ci            }
251bf215546Sopenharmony_ci         }
252bf215546Sopenharmony_ci         foreach_src (reg, instr) {
253bf215546Sopenharmony_ci            if ((reg->flags & IR3_REG_ARRAY) && !reg->def) {
254bf215546Sopenharmony_ci               struct ir3_array *arr = ir3_lookup_array(ir, reg->array.id);
255bf215546Sopenharmony_ci
256bf215546Sopenharmony_ci               /* Construct any phi nodes necessary to read this value */
257bf215546Sopenharmony_ci               read_value_beginning(&ctx, block, arr);
258bf215546Sopenharmony_ci            }
259bf215546Sopenharmony_ci         }
260bf215546Sopenharmony_ci      }
261bf215546Sopenharmony_ci   }
262bf215546Sopenharmony_ci
263bf215546Sopenharmony_ci   foreach_block (block, &ir->block_list) {
264bf215546Sopenharmony_ci      foreach_instr_safe (instr, &block->instr_list) {
265bf215546Sopenharmony_ci         if (instr->opc == OPC_META_PHI)
266bf215546Sopenharmony_ci            remove_trivial_phi(instr);
267bf215546Sopenharmony_ci         else
268bf215546Sopenharmony_ci            break;
269bf215546Sopenharmony_ci      }
270bf215546Sopenharmony_ci   }
271bf215546Sopenharmony_ci
272bf215546Sopenharmony_ci   foreach_block (block, &ir->block_list) {
273bf215546Sopenharmony_ci      foreach_instr_safe (instr, &block->instr_list) {
274bf215546Sopenharmony_ci         if (instr->opc == OPC_META_PHI) {
275bf215546Sopenharmony_ci            if (!(instr->flags & IR3_REG_ARRAY))
276bf215546Sopenharmony_ci               continue;
277bf215546Sopenharmony_ci            if (instr->data != instr->dsts[0]) {
278bf215546Sopenharmony_ci               list_del(&instr->node);
279bf215546Sopenharmony_ci               continue;
280bf215546Sopenharmony_ci            }
281bf215546Sopenharmony_ci            for (unsigned i = 0; i < instr->srcs_count; i++) {
282bf215546Sopenharmony_ci               instr->srcs[i] = lookup_value(instr->srcs[i]);
283bf215546Sopenharmony_ci            }
284bf215546Sopenharmony_ci         } else {
285bf215546Sopenharmony_ci            foreach_dst (reg, instr) {
286bf215546Sopenharmony_ci               if ((reg->flags & IR3_REG_ARRAY)) {
287bf215546Sopenharmony_ci                  if (!reg->tied) {
288bf215546Sopenharmony_ci                     struct ir3_register *def =
289bf215546Sopenharmony_ci                        lookup_live_in(&ctx, block, reg->array.id);
290bf215546Sopenharmony_ci                     if (def)
291bf215546Sopenharmony_ci                        ir3_reg_set_last_array(instr, reg, def);
292bf215546Sopenharmony_ci                  }
293bf215546Sopenharmony_ci                  reg->flags |= IR3_REG_SSA;
294bf215546Sopenharmony_ci               }
295bf215546Sopenharmony_ci            }
296bf215546Sopenharmony_ci            foreach_src (reg, instr) {
297bf215546Sopenharmony_ci               if ((reg->flags & IR3_REG_ARRAY)) {
298bf215546Sopenharmony_ci                  /* It is assumed that before calling
299bf215546Sopenharmony_ci                   * ir3_array_to_ssa(), reg->def was set to the
300bf215546Sopenharmony_ci                   * previous writer of the array within the current
301bf215546Sopenharmony_ci                   * block or NULL if none.
302bf215546Sopenharmony_ci                   */
303bf215546Sopenharmony_ci                  if (!reg->def) {
304bf215546Sopenharmony_ci                     reg->def = lookup_live_in(&ctx, block, reg->array.id);
305bf215546Sopenharmony_ci                  }
306bf215546Sopenharmony_ci                  reg->flags |= IR3_REG_SSA;
307bf215546Sopenharmony_ci               }
308bf215546Sopenharmony_ci            }
309bf215546Sopenharmony_ci         }
310bf215546Sopenharmony_ci      }
311bf215546Sopenharmony_ci   }
312bf215546Sopenharmony_ci
313bf215546Sopenharmony_ci   free(ctx.states);
314bf215546Sopenharmony_ci   return true;
315bf215546Sopenharmony_ci}
316