1/*
2 * Copyright (c) 2017 Lima Project
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sub license,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the
12 * next paragraph) shall be included in all copies or substantial portions
13 * of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25#include "util/ralloc.h"
26
27#include "ppir.h"
28
29ppir_instr *ppir_instr_create(ppir_block *block)
30{
31   ppir_instr *instr = rzalloc(block, ppir_instr);
32   if (!instr)
33      return NULL;
34
35   list_inithead(&instr->succ_list);
36   list_inithead(&instr->pred_list);
37
38   instr->index = block->comp->cur_instr_index++;
39   instr->reg_pressure = -1;
40
41   list_addtail(&instr->list, &block->instr_list);
42   return instr;
43}
44
45void ppir_instr_add_dep(ppir_instr *succ, ppir_instr *pred)
46{
47   /* don't add duplicated instr */
48   ppir_instr_foreach_pred(succ, dep) {
49      if (pred == dep->pred)
50         return;
51   }
52
53   ppir_dep *dep = ralloc(succ, ppir_dep);
54   dep->pred = pred;
55   dep->succ = succ;
56   list_addtail(&dep->pred_link, &succ->pred_list);
57   list_addtail(&dep->succ_link, &pred->succ_list);
58}
59
60void ppir_instr_insert_mul_node(ppir_node *add, ppir_node *mul)
61{
62   ppir_instr *instr = add->instr;
63   int pos = mul->instr_pos;
64   int *slots = ppir_op_infos[mul->op].slots;
65
66   for (int i = 0; slots[i] != PPIR_INSTR_SLOT_END; i++) {
67      /* possible to insert at required place */
68      if (slots[i] == pos) {
69         if (!instr->slots[pos]) {
70            ppir_alu_node *add_alu = ppir_node_to_alu(add);
71            ppir_alu_node *mul_alu = ppir_node_to_alu(mul);
72            ppir_dest *dest = &mul_alu->dest;
73            int pipeline = pos == PPIR_INSTR_SLOT_ALU_VEC_MUL ?
74               ppir_pipeline_reg_vmul : ppir_pipeline_reg_fmul;
75
76            /* ^vmul/^fmul can't be used as last arg */
77            if (add_alu->num_src > 1) {
78               ppir_src *last_src = add_alu->src + add_alu->num_src - 1;
79               if (ppir_node_target_equal(last_src, dest))
80                  return;
81            }
82
83            /* update add node src to use pipeline reg */
84            ppir_src *src = add_alu->src;
85            if (add_alu->num_src == 3) {
86               if (ppir_node_target_equal(src, dest)) {
87                  src->type = ppir_target_pipeline;
88                  src->pipeline = pipeline;
89               }
90
91               if (ppir_node_target_equal(++src, dest)) {
92                  src->type = ppir_target_pipeline;
93                  src->pipeline = pipeline;
94               }
95            }
96            else {
97               assert(ppir_node_target_equal(src, dest));
98               src->type = ppir_target_pipeline;
99               src->pipeline = pipeline;
100            }
101
102            /* update mul node dest to output to pipeline reg */
103            dest->type = ppir_target_pipeline;
104            dest->pipeline = pipeline;
105
106            instr->slots[pos] = mul;
107            mul->instr = instr;
108         }
109         return;
110      }
111   }
112}
113
114/* check whether a const slot fix into another const slot */
115static bool ppir_instr_insert_const(ppir_const *dst, const ppir_const *src,
116                                    uint8_t *swizzle)
117{
118   int i, j;
119
120   for (i = 0; i < src->num; i++) {
121      for (j = 0; j < dst->num; j++) {
122         if (src->value[i].ui == dst->value[j].ui)
123            break;
124      }
125
126      if (j == dst->num) {
127         if (dst->num == 4)
128            return false;
129         dst->value[dst->num++] = src->value[i];
130      }
131
132      swizzle[i] = j;
133   }
134
135   return true;
136}
137
138static void ppir_update_src_pipeline(ppir_pipeline pipeline, ppir_src *src,
139                                     ppir_dest *dest, uint8_t *swizzle)
140{
141   if (ppir_node_target_equal(src, dest)) {
142      src->type = ppir_target_pipeline;
143      src->pipeline = pipeline;
144
145      if (swizzle) {
146         for (int k = 0; k < 4; k++)
147            src->swizzle[k] = swizzle[src->swizzle[k]];
148      }
149   }
150}
151
152/* make alu node src reflact the pipeline reg */
153static void ppir_instr_update_src_pipeline(ppir_instr *instr, ppir_pipeline pipeline,
154                                           ppir_dest *dest, uint8_t *swizzle)
155{
156   for (int i = PPIR_INSTR_SLOT_ALU_START; i <= PPIR_INSTR_SLOT_ALU_END; i++) {
157      if (!instr->slots[i])
158         continue;
159
160      ppir_alu_node *alu = ppir_node_to_alu(instr->slots[i]);
161      for (int j = 0; j < alu->num_src; j++) {
162         ppir_src *src = alu->src + j;
163         ppir_update_src_pipeline(pipeline, src, dest, swizzle);
164      }
165   }
166
167   ppir_node *branch_node = instr->slots[PPIR_INSTR_SLOT_BRANCH];
168   if (branch_node && (branch_node->type == ppir_node_type_branch)) {
169      ppir_branch_node *branch = ppir_node_to_branch(branch_node);
170      for (int j = 0; j < 2; j++) {
171         ppir_src *src = branch->src + j;
172         ppir_update_src_pipeline(pipeline, src, dest, swizzle);
173      }
174   }
175}
176
177bool ppir_instr_insert_node(ppir_instr *instr, ppir_node *node)
178{
179   if (node->op == ppir_op_const) {
180      int i;
181      ppir_const_node *c = ppir_node_to_const(node);
182      const ppir_const *nc = &c->constant;
183
184      for (i = 0; i < 2; i++) {
185         ppir_const ic = instr->constant[i];
186         uint8_t swizzle[4] = {0};
187
188         if (ppir_instr_insert_const(&ic, nc, swizzle)) {
189            ppir_node *succ = ppir_node_first_succ(node);
190            ppir_src *src = NULL;
191            for (int s = 0; s < ppir_node_get_src_num(succ); s++) {
192               src = ppir_node_get_src(succ, s);
193               if (src->node == node)
194                  break;
195            }
196            assert(src->node == node);
197
198            instr->constant[i] = ic;
199            ppir_update_src_pipeline(ppir_pipeline_reg_const0 + i, src,
200                                     &c->dest, swizzle);
201            break;
202         }
203      }
204
205      /* no const slot can insert */
206      if (i == 2)
207         return false;
208
209      return true;
210   }
211   else {
212      int *slots = ppir_op_infos[node->op].slots;
213      for (int i = 0; slots[i] != PPIR_INSTR_SLOT_END; i++) {
214         int pos = slots[i];
215
216         if (instr->slots[pos]) {
217            /* node already in this instr, i.e. load_uniform */
218            if (instr->slots[pos] == node)
219               return true;
220            else
221               continue;
222         }
223
224         /* ^fmul dests (e.g. condition for select) can only be
225          * scheduled to ALU_SCL_MUL */
226         if (pos == PPIR_INSTR_SLOT_ALU_SCL_ADD) {
227            ppir_dest *dest = ppir_node_get_dest(node);
228            if (dest && dest->type == ppir_target_pipeline &&
229                dest->pipeline == ppir_pipeline_reg_fmul)
230            continue;
231         }
232
233         if (pos == PPIR_INSTR_SLOT_ALU_SCL_MUL ||
234             pos == PPIR_INSTR_SLOT_ALU_SCL_ADD) {
235            ppir_dest *dest = ppir_node_get_dest(node);
236            if (!ppir_target_is_scalar(dest))
237               continue;
238         }
239
240         instr->slots[pos] = node;
241         node->instr = instr;
242         node->instr_pos = pos;
243
244         if ((node->op == ppir_op_load_uniform) || (node->op == ppir_op_load_temp)) {
245            ppir_load_node *l = ppir_node_to_load(node);
246            ppir_instr_update_src_pipeline(
247               instr, ppir_pipeline_reg_uniform, &l->dest, NULL);
248         }
249
250         return true;
251      }
252
253      return false;
254   }
255}
256
257static struct {
258   int len;
259   char *name;
260} ppir_instr_fields[] = {
261   [PPIR_INSTR_SLOT_VARYING] = { 4, "vary" },
262   [PPIR_INSTR_SLOT_TEXLD] = { 4, "texl"},
263   [PPIR_INSTR_SLOT_UNIFORM] = { 4, "unif" },
264   [PPIR_INSTR_SLOT_ALU_VEC_MUL] = { 4, "vmul" },
265   [PPIR_INSTR_SLOT_ALU_SCL_MUL] = { 4, "smul" },
266   [PPIR_INSTR_SLOT_ALU_VEC_ADD] = { 4, "vadd" },
267   [PPIR_INSTR_SLOT_ALU_SCL_ADD] = { 4, "sadd" },
268   [PPIR_INSTR_SLOT_ALU_COMBINE] = { 4, "comb" },
269   [PPIR_INSTR_SLOT_STORE_TEMP] = { 4, "stor" },
270   [PPIR_INSTR_SLOT_BRANCH] = { 4, "brch" },
271};
272
273void ppir_instr_print_list(ppir_compiler *comp)
274{
275   if (!(lima_debug & LIMA_DEBUG_PP))
276      return;
277
278   printf("======ppir instr list======\n");
279   printf("      ");
280   for (int i = 0; i < PPIR_INSTR_SLOT_NUM; i++)
281      printf("%-*s ", ppir_instr_fields[i].len, ppir_instr_fields[i].name);
282   printf("const0|1\n");
283
284   list_for_each_entry(ppir_block, block, &comp->block_list, list) {
285      printf("-------block %3d-------\n", block->index);
286      list_for_each_entry(ppir_instr, instr, &block->instr_list, list) {
287         printf("%c%03d: ", instr->is_end ? '*' : ' ', instr->index);
288         for (int i = 0; i < PPIR_INSTR_SLOT_NUM; i++) {
289            ppir_node *node = instr->slots[i];
290            if (node)
291               printf("%-*d ", ppir_instr_fields[i].len, node->index);
292            else
293               printf("%-*s ", ppir_instr_fields[i].len, "null");
294         }
295         for (int i = 0; i < 2; i++) {
296            if (i)
297               printf("| ");
298
299            for (int j = 0; j < instr->constant[i].num; j++)
300               printf("%f ", instr->constant[i].value[j].f);
301         }
302         printf("\n");
303      }
304   }
305   printf("===========================\n");
306}
307
308static void ppir_instr_print_sub(ppir_instr *instr)
309{
310   printf("[%s%d",
311          instr->printed && !ppir_instr_is_leaf(instr) ? "+" : "",
312          instr->index);
313
314   if (!instr->printed) {
315      ppir_instr_foreach_pred(instr, dep) {
316         ppir_instr_print_sub(dep->pred);
317      }
318
319      instr->printed = true;
320   }
321
322   printf("]");
323}
324
325void ppir_instr_print_dep(ppir_compiler *comp)
326{
327   if (!(lima_debug & LIMA_DEBUG_PP))
328      return;
329
330   list_for_each_entry(ppir_block, block, &comp->block_list, list) {
331      list_for_each_entry(ppir_instr, instr, &block->instr_list, list) {
332         instr->printed = false;
333      }
334   }
335
336   printf("======ppir instr depend======\n");
337   list_for_each_entry(ppir_block, block, &comp->block_list, list) {
338      printf("-------block %3d-------\n", block->index);
339      list_for_each_entry(ppir_instr, instr, &block->instr_list, list) {
340         if (ppir_instr_is_root(instr)) {
341            ppir_instr_print_sub(instr);
342            printf("\n");
343         }
344      }
345   }
346   printf("=============================\n");
347}
348