1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright © 2022 Imagination Technologies Ltd.
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
5bf215546Sopenharmony_ci * of this software and associated documentation files (the "Software"), to deal
6bf215546Sopenharmony_ci * in the Software without restriction, including without limitation the rights
7bf215546Sopenharmony_ci * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8bf215546Sopenharmony_ci * copies of the Software, and to permit persons to whom the Software is
9bf215546Sopenharmony_ci * 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 THE
18bf215546Sopenharmony_ci * 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#include <stddef.h>
25bf215546Sopenharmony_ci#include <stdint.h>
26bf215546Sopenharmony_ci
27bf215546Sopenharmony_ci#include "rogue_operand.h"
28bf215546Sopenharmony_ci#include "rogue_regalloc.h"
29bf215546Sopenharmony_ci#include "rogue_shader.h"
30bf215546Sopenharmony_ci#include "rogue_util.h"
31bf215546Sopenharmony_ci#include "util/hash_table.h"
32bf215546Sopenharmony_ci#include "util/list.h"
33bf215546Sopenharmony_ci#include "util/ralloc.h"
34bf215546Sopenharmony_ci#include "util/register_allocate.h"
35bf215546Sopenharmony_ci#include "util/u_dynarray.h"
36bf215546Sopenharmony_ci
37bf215546Sopenharmony_ci/**
38bf215546Sopenharmony_ci * \file rogue_regalloc.c
39bf215546Sopenharmony_ci *
40bf215546Sopenharmony_ci * \brief Contains register allocation helper functions.
41bf215546Sopenharmony_ci */
42bf215546Sopenharmony_ci
43bf215546Sopenharmony_ci/**
44bf215546Sopenharmony_ci * \brief Sets up the register data with the classes to be used for allocation.
45bf215546Sopenharmony_ci *
46bf215546Sopenharmony_ci * \param[in] data The register data array.
47bf215546Sopenharmony_ci */
48bf215546Sopenharmony_cistatic void
49bf215546Sopenharmony_cirogue_reg_data_init(struct rogue_reg_data data[static ROGUE_REG_CLASS_COUNT])
50bf215546Sopenharmony_ci{
51bf215546Sopenharmony_ci   data[ROGUE_REG_CLASS_TEMP].type = ROGUE_OPERAND_TYPE_REG_TEMP;
52bf215546Sopenharmony_ci   data[ROGUE_REG_CLASS_TEMP].count = ROGUE_MAX_REG_TEMP;
53bf215546Sopenharmony_ci   data[ROGUE_REG_CLASS_TEMP].stride = 1;
54bf215546Sopenharmony_ci
55bf215546Sopenharmony_ci   data[ROGUE_REG_CLASS_VEC4].type = ROGUE_OPERAND_TYPE_REG_INTERNAL;
56bf215546Sopenharmony_ci   data[ROGUE_REG_CLASS_VEC4].count = ROGUE_MAX_REG_INTERNAL;
57bf215546Sopenharmony_ci   data[ROGUE_REG_CLASS_VEC4].stride = 4;
58bf215546Sopenharmony_ci}
59bf215546Sopenharmony_ci
60bf215546Sopenharmony_ci/**
61bf215546Sopenharmony_ci * \brief Initializes the Rogue register allocation context.
62bf215546Sopenharmony_ci *
63bf215546Sopenharmony_ci * \param[in] mem_ctx The memory context for the ra context.
64bf215546Sopenharmony_ci * \return A rogue_ra * if successful, or NULL if unsuccessful.
65bf215546Sopenharmony_ci */
66bf215546Sopenharmony_cistruct rogue_ra *rogue_ra_init(void *mem_ctx)
67bf215546Sopenharmony_ci{
68bf215546Sopenharmony_ci   struct rogue_ra *ra;
69bf215546Sopenharmony_ci   size_t total_regs = 0;
70bf215546Sopenharmony_ci
71bf215546Sopenharmony_ci   ra = rzalloc_size(mem_ctx, sizeof(*ra));
72bf215546Sopenharmony_ci   if (!ra)
73bf215546Sopenharmony_ci      return NULL;
74bf215546Sopenharmony_ci
75bf215546Sopenharmony_ci   /* Initialize the register class data. */
76bf215546Sopenharmony_ci   rogue_reg_data_init(ra->reg_data);
77bf215546Sopenharmony_ci
78bf215546Sopenharmony_ci   /* Count up the registers classes and set up their offsets.
79bf215546Sopenharmony_ci    *
80bf215546Sopenharmony_ci    * The physical register numbers are sequential, even if the
81bf215546Sopenharmony_ci    * registers are from different banks, so keeping track of
82bf215546Sopenharmony_ci    * the offset means we can get the true physical register
83bf215546Sopenharmony_ci    * number back after allocation.
84bf215546Sopenharmony_ci    */
85bf215546Sopenharmony_ci   for (size_t u = 0; u < ARRAY_SIZE(ra->reg_data); ++u) {
86bf215546Sopenharmony_ci      ra->reg_data[u].offset = total_regs;
87bf215546Sopenharmony_ci      total_regs += ra->reg_data[u].count;
88bf215546Sopenharmony_ci   }
89bf215546Sopenharmony_ci
90bf215546Sopenharmony_ci   /* Create a register set for allocation.  */
91bf215546Sopenharmony_ci   ra->regs = ra_alloc_reg_set(ra, total_regs, true);
92bf215546Sopenharmony_ci   if (!ra->regs) {
93bf215546Sopenharmony_ci      ralloc_free(ra);
94bf215546Sopenharmony_ci      return NULL;
95bf215546Sopenharmony_ci   }
96bf215546Sopenharmony_ci
97bf215546Sopenharmony_ci   /* Create the register class for the temps. */
98bf215546Sopenharmony_ci   ra->reg_data[ROGUE_REG_CLASS_TEMP].class =
99bf215546Sopenharmony_ci      ra_alloc_contig_reg_class(ra->regs, 1);
100bf215546Sopenharmony_ci
101bf215546Sopenharmony_ci   /* Create the register class for vec4 registers
102bf215546Sopenharmony_ci    * (using the internal register bank).
103bf215546Sopenharmony_ci    */
104bf215546Sopenharmony_ci   ra->reg_data[ROGUE_REG_CLASS_VEC4].class =
105bf215546Sopenharmony_ci      ra_alloc_contig_reg_class(ra->regs, 4);
106bf215546Sopenharmony_ci
107bf215546Sopenharmony_ci   /* Populate the register classes. */
108bf215546Sopenharmony_ci   for (size_t u = 0; u < ARRAY_SIZE(ra->reg_data); ++u) {
109bf215546Sopenharmony_ci      struct rogue_reg_data *reg_data = &ra->reg_data[u];
110bf215546Sopenharmony_ci      size_t offset = reg_data->offset;
111bf215546Sopenharmony_ci      size_t end = reg_data->offset + reg_data->count;
112bf215546Sopenharmony_ci      size_t stride = reg_data->stride;
113bf215546Sopenharmony_ci
114bf215546Sopenharmony_ci      for (size_t r = offset; r < end; r += stride)
115bf215546Sopenharmony_ci         ra_class_add_reg(reg_data->class, r);
116bf215546Sopenharmony_ci   }
117bf215546Sopenharmony_ci
118bf215546Sopenharmony_ci   /* Finalize the set (no early conflicts passed along for now). */
119bf215546Sopenharmony_ci   ra_set_finalize(ra->regs, NULL);
120bf215546Sopenharmony_ci
121bf215546Sopenharmony_ci   return ra;
122bf215546Sopenharmony_ci}
123bf215546Sopenharmony_ci
124bf215546Sopenharmony_ci/**
125bf215546Sopenharmony_ci * \brief The range for which a (virtual) register is live, and its references.
126bf215546Sopenharmony_ci */
127bf215546Sopenharmony_cistruct live_range {
128bf215546Sopenharmony_ci   size_t start;
129bf215546Sopenharmony_ci   size_t end;
130bf215546Sopenharmony_ci   enum rogue_reg_class class;
131bf215546Sopenharmony_ci   struct util_dynarray operand_refs;
132bf215546Sopenharmony_ci};
133bf215546Sopenharmony_ci
134bf215546Sopenharmony_ci/**
135bf215546Sopenharmony_ci * \brief Performs register allocation.
136bf215546Sopenharmony_ci *
137bf215546Sopenharmony_ci * \param[in] instr_list A linked list of instructions with virtual registers to
138bf215546Sopenharmony_ci * be allocated.
139bf215546Sopenharmony_ci * \param[in] ra The register allocation context.
140bf215546Sopenharmony_ci */
141bf215546Sopenharmony_cibool rogue_ra_alloc(struct list_head *instr_list,
142bf215546Sopenharmony_ci                    struct rogue_ra *ra,
143bf215546Sopenharmony_ci                    size_t *temps_used,
144bf215546Sopenharmony_ci                    size_t *internals_used)
145bf215546Sopenharmony_ci{
146bf215546Sopenharmony_ci   /* Used for ra_alloc_interference_graph() as it doesn't
147bf215546Sopenharmony_ci    * like having gaps (e.g. with v0, v2 count = 3 rather
148bf215546Sopenharmony_ci    * than 2).
149bf215546Sopenharmony_ci    */
150bf215546Sopenharmony_ci   size_t max_vreg = 0;
151bf215546Sopenharmony_ci
152bf215546Sopenharmony_ci   struct hash_table *reg_ht =
153bf215546Sopenharmony_ci      _mesa_hash_table_create(ra, _mesa_hash_uint, _mesa_key_uint_equal);
154bf215546Sopenharmony_ci   if (!reg_ht)
155bf215546Sopenharmony_ci      return false;
156bf215546Sopenharmony_ci
157bf215546Sopenharmony_ci   /* Calculate live ranges for virtual registers. */
158bf215546Sopenharmony_ci   size_t ip = 0U; /* "Instruction pointer". */
159bf215546Sopenharmony_ci   foreach_instr (instr, instr_list) {
160bf215546Sopenharmony_ci      for (size_t u = 0U; u < instr->num_operands; ++u) {
161bf215546Sopenharmony_ci         struct hash_entry *entry;
162bf215546Sopenharmony_ci         struct live_range *range;
163bf215546Sopenharmony_ci
164bf215546Sopenharmony_ci         if (instr->operands[u].type != ROGUE_OPERAND_TYPE_VREG)
165bf215546Sopenharmony_ci            continue;
166bf215546Sopenharmony_ci
167bf215546Sopenharmony_ci         entry =
168bf215546Sopenharmony_ci            _mesa_hash_table_search(reg_ht, &instr->operands[u].vreg.number);
169bf215546Sopenharmony_ci         if (!entry) {
170bf215546Sopenharmony_ci            /* First use of this virtual register: initialize live range. */
171bf215546Sopenharmony_ci            /* TODO: Error handling. */
172bf215546Sopenharmony_ci            range = rzalloc_size(reg_ht, sizeof(*range));
173bf215546Sopenharmony_ci
174bf215546Sopenharmony_ci            range->start = ip;
175bf215546Sopenharmony_ci            range->end = ip;
176bf215546Sopenharmony_ci            range->class = instr->operands[u].vreg.is_vector
177bf215546Sopenharmony_ci                              ? ROGUE_REG_CLASS_VEC4
178bf215546Sopenharmony_ci                              : ROGUE_REG_CLASS_TEMP;
179bf215546Sopenharmony_ci
180bf215546Sopenharmony_ci            entry = _mesa_hash_table_insert(reg_ht,
181bf215546Sopenharmony_ci                                            &instr->operands[u].vreg.number,
182bf215546Sopenharmony_ci                                            range);
183bf215546Sopenharmony_ci
184bf215546Sopenharmony_ci            max_vreg = MAX2(max_vreg, instr->operands[u].vreg.number);
185bf215546Sopenharmony_ci
186bf215546Sopenharmony_ci            util_dynarray_init(&range->operand_refs, range);
187bf215546Sopenharmony_ci         } else {
188bf215546Sopenharmony_ci            /* Subsequent uses: update live range end. */
189bf215546Sopenharmony_ci            range = entry->data;
190bf215546Sopenharmony_ci            range->end = MAX2(range->end, ip);
191bf215546Sopenharmony_ci            assert(range->class == (instr->operands[u].vreg.is_vector
192bf215546Sopenharmony_ci                                       ? ROGUE_REG_CLASS_VEC4
193bf215546Sopenharmony_ci                                       : ROGUE_REG_CLASS_TEMP));
194bf215546Sopenharmony_ci         }
195bf215546Sopenharmony_ci
196bf215546Sopenharmony_ci         /* Save a reference to the operand.  */
197bf215546Sopenharmony_ci         util_dynarray_append(&range->operand_refs,
198bf215546Sopenharmony_ci                              struct rogue_operand *,
199bf215546Sopenharmony_ci                              &instr->operands[u]);
200bf215546Sopenharmony_ci      }
201bf215546Sopenharmony_ci      ++ip;
202bf215546Sopenharmony_ci   }
203bf215546Sopenharmony_ci
204bf215546Sopenharmony_ci   /* Initialize the interference graph. */
205bf215546Sopenharmony_ci   struct ra_graph *g = ra_alloc_interference_graph(ra->regs, max_vreg + 1);
206bf215546Sopenharmony_ci
207bf215546Sopenharmony_ci   /* Set each virtual register to the appropriate class. */
208bf215546Sopenharmony_ci   hash_table_foreach (reg_ht, entry) {
209bf215546Sopenharmony_ci      const uint32_t *vreg = entry->key;
210bf215546Sopenharmony_ci      struct live_range *range = entry->data;
211bf215546Sopenharmony_ci      struct ra_class *class = ra->reg_data[range->class].class;
212bf215546Sopenharmony_ci
213bf215546Sopenharmony_ci      ra_set_node_class(g, *vreg, class);
214bf215546Sopenharmony_ci      /* TODO: ra_set_node_spill_cost(g, *vreg, cost); */
215bf215546Sopenharmony_ci   }
216bf215546Sopenharmony_ci
217bf215546Sopenharmony_ci   /* Build interference graph from overlapping live ranges. */
218bf215546Sopenharmony_ci   hash_table_foreach (reg_ht, entry_first) {
219bf215546Sopenharmony_ci      const uint32_t *vreg_first = entry_first->key;
220bf215546Sopenharmony_ci      struct live_range *range_first = entry_first->data;
221bf215546Sopenharmony_ci
222bf215546Sopenharmony_ci      hash_table_foreach (reg_ht, entry_second) {
223bf215546Sopenharmony_ci         const uint32_t *vreg_second = entry_second->key;
224bf215546Sopenharmony_ci         struct live_range *range_second = entry_second->data;
225bf215546Sopenharmony_ci
226bf215546Sopenharmony_ci         if (*vreg_first == *vreg_second)
227bf215546Sopenharmony_ci            continue;
228bf215546Sopenharmony_ci
229bf215546Sopenharmony_ci         /* If the live ranges overlap, those register nodes interfere. */
230bf215546Sopenharmony_ci         if (!(range_first->start >= range_second->end ||
231bf215546Sopenharmony_ci               range_second->start >= range_first->end)) {
232bf215546Sopenharmony_ci            ra_add_node_interference(g, *vreg_first, *vreg_second);
233bf215546Sopenharmony_ci         }
234bf215546Sopenharmony_ci      }
235bf215546Sopenharmony_ci   }
236bf215546Sopenharmony_ci
237bf215546Sopenharmony_ci   /* Add node interferences such that the same register can't be used for
238bf215546Sopenharmony_ci    * both an instruction's source and destination.
239bf215546Sopenharmony_ci    */
240bf215546Sopenharmony_ci   foreach_instr (instr, instr_list) {
241bf215546Sopenharmony_ci      for (size_t u = 0U; u < instr->num_operands; ++u) {
242bf215546Sopenharmony_ci         if (instr->operands[u].type != ROGUE_OPERAND_TYPE_VREG)
243bf215546Sopenharmony_ci            continue;
244bf215546Sopenharmony_ci
245bf215546Sopenharmony_ci         /* Operand 0 (if it exists and is virtual) is always
246bf215546Sopenharmony_ci          * the destination register.
247bf215546Sopenharmony_ci          */
248bf215546Sopenharmony_ci         if (u > 0 && instr->operands[0].type == ROGUE_OPERAND_TYPE_VREG)
249bf215546Sopenharmony_ci            ra_add_node_interference(g,
250bf215546Sopenharmony_ci                                     instr->operands[0].vreg.number,
251bf215546Sopenharmony_ci                                     instr->operands[u].vreg.number);
252bf215546Sopenharmony_ci      }
253bf215546Sopenharmony_ci   }
254bf215546Sopenharmony_ci
255bf215546Sopenharmony_ci   /* Perform register allocation. */
256bf215546Sopenharmony_ci   /* TODO: Spilling support. */
257bf215546Sopenharmony_ci   assert(ra_allocate(g));
258bf215546Sopenharmony_ci
259bf215546Sopenharmony_ci   /* Replace virtual registers with allocated physical registers.
260bf215546Sopenharmony_ci    * N.B. This is a destructive process as it overwrites the hash table key!
261bf215546Sopenharmony_ci    */
262bf215546Sopenharmony_ci   hash_table_foreach (reg_ht, entry) {
263bf215546Sopenharmony_ci      uint32_t vreg = *(uint32_t *)entry->key;
264bf215546Sopenharmony_ci      unsigned phy_reg = ra_get_node_reg(g, vreg);
265bf215546Sopenharmony_ci      struct live_range *range = entry->data;
266bf215546Sopenharmony_ci
267bf215546Sopenharmony_ci      struct rogue_reg_data *reg_data = &ra->reg_data[range->class];
268bf215546Sopenharmony_ci      enum rogue_operand_type type = reg_data->type;
269bf215546Sopenharmony_ci      size_t reg_offset = reg_data->offset;
270bf215546Sopenharmony_ci      size_t *num_used = &reg_data->num_used;
271bf215546Sopenharmony_ci
272bf215546Sopenharmony_ci      util_dynarray_foreach (&range->operand_refs,
273bf215546Sopenharmony_ci                             struct rogue_operand *,
274bf215546Sopenharmony_ci                             operand_ptr) {
275bf215546Sopenharmony_ci         size_t num = phy_reg - reg_offset;
276bf215546Sopenharmony_ci         struct rogue_operand *operand = *operand_ptr;
277bf215546Sopenharmony_ci
278bf215546Sopenharmony_ci         assert(operand->type == ROGUE_OPERAND_TYPE_VREG);
279bf215546Sopenharmony_ci         assert(operand->vreg.number == vreg);
280bf215546Sopenharmony_ci
281bf215546Sopenharmony_ci         /* Index the component of emulated vec4 registers. */
282bf215546Sopenharmony_ci         if (operand->vreg.is_vector &&
283bf215546Sopenharmony_ci             operand->vreg.component != ROGUE_COMPONENT_ALL)
284bf215546Sopenharmony_ci            num += operand->vreg.component;
285bf215546Sopenharmony_ci
286bf215546Sopenharmony_ci         operand->type = type;
287bf215546Sopenharmony_ci         operand->reg.number = num;
288bf215546Sopenharmony_ci
289bf215546Sopenharmony_ci         *num_used = MAX2(*num_used, operand->reg.number);
290bf215546Sopenharmony_ci      }
291bf215546Sopenharmony_ci
292bf215546Sopenharmony_ci      util_dynarray_fini(&range->operand_refs);
293bf215546Sopenharmony_ci      _mesa_hash_table_remove(reg_ht, entry);
294bf215546Sopenharmony_ci   }
295bf215546Sopenharmony_ci
296bf215546Sopenharmony_ci   /* Registers used = max reg number + 1. */
297bf215546Sopenharmony_ci   for (size_t u = 0; u < ARRAY_SIZE(ra->reg_data); ++u)
298bf215546Sopenharmony_ci      if (ra->reg_data[u].num_used)
299bf215546Sopenharmony_ci         ++ra->reg_data[u].num_used;
300bf215546Sopenharmony_ci
301bf215546Sopenharmony_ci   /* Pass back the registers used. */
302bf215546Sopenharmony_ci   if (temps_used)
303bf215546Sopenharmony_ci      *temps_used = ra->reg_data[ROGUE_REG_CLASS_TEMP].num_used;
304bf215546Sopenharmony_ci
305bf215546Sopenharmony_ci   if (internals_used)
306bf215546Sopenharmony_ci      *internals_used = ra->reg_data[ROGUE_REG_CLASS_VEC4].num_used;
307bf215546Sopenharmony_ci
308bf215546Sopenharmony_ci   ralloc_free(g);
309bf215546Sopenharmony_ci
310bf215546Sopenharmony_ci   _mesa_hash_table_destroy(reg_ht, NULL);
311bf215546Sopenharmony_ci
312bf215546Sopenharmony_ci   return true;
313bf215546Sopenharmony_ci}
314