~mmach/netext73/mesa-haswell

« back to all changes in this revision

Viewing changes to src/gallium/drivers/lima/ir/pp/node_to_instr.c

  • Committer: mmach
  • Date: 2022-09-22 19:56:13 UTC
  • Revision ID: netbit73@gmail.com-20220922195613-wtik9mmy20tmor0i
2022-09-22 21:17:09

Show diffs side-by-side

added added

removed removed

Lines of Context:
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 "ppir.h"
26
 
 
27
 
 
28
 
static bool create_new_instr(ppir_block *block, ppir_node *node)
29
 
{
30
 
   ppir_instr *instr = ppir_instr_create(block);
31
 
   if (unlikely(!instr))
32
 
      return false;
33
 
 
34
 
   if (!ppir_instr_insert_node(instr, node))
35
 
      return false;
36
 
 
37
 
   return true;
38
 
}
39
 
 
40
 
/*
41
 
 * If a node has a pipeline dest, schedule it in the same instruction as its
42
 
 * successor.
43
 
 * Since it has a pipeline dest, it must have only one successor and since we
44
 
 * schedule nodes backwards, its successor must have already been scheduled.
45
 
 * Load varyings can't output to a pipeline register but are also potentially
46
 
 * trivial to insert and save an instruction if they have a single successor.
47
 
 */
48
 
static bool ppir_do_node_to_instr_try_insert(ppir_block *block, ppir_node *node)
49
 
{
50
 
   ppir_dest *dest = ppir_node_get_dest(node);
51
 
 
52
 
   if (dest && dest->type == ppir_target_pipeline) {
53
 
      assert(ppir_node_has_single_src_succ(node));
54
 
      ppir_node *succ = ppir_node_first_succ(node);
55
 
      assert(succ);
56
 
      assert(succ->instr);
57
 
 
58
 
      return ppir_instr_insert_node(succ->instr, node);
59
 
   }
60
 
 
61
 
   switch (node->type) {
62
 
      case ppir_node_type_load:
63
 
         break;
64
 
      default:
65
 
         return false;
66
 
   }
67
 
 
68
 
   if (!ppir_node_has_single_src_succ(node))
69
 
      return false;
70
 
 
71
 
   ppir_node *succ = ppir_node_first_succ(node);
72
 
   assert(succ);
73
 
   assert(succ->instr);
74
 
 
75
 
   return ppir_instr_insert_node(succ->instr, node);
76
 
}
77
 
 
78
 
static bool ppir_do_one_node_to_instr(ppir_block *block, ppir_node *node)
79
 
{
80
 
   switch (node->type) {
81
 
   case ppir_node_type_alu:
82
 
   {
83
 
      /* don't create an instr for undef node */
84
 
      if (node->op == ppir_op_undef)
85
 
         break;
86
 
 
87
 
      /* merge pred mul and succ add in the same instr can save a reg
88
 
       * by using pipeline reg ^vmul/^fmul */
89
 
      ppir_alu_node *alu = ppir_node_to_alu(node);
90
 
      if (alu->dest.type == ppir_target_ssa &&
91
 
          ppir_node_has_single_succ(node) &&
92
 
          ppir_node_has_single_src_succ(node)) {
93
 
         ppir_node *succ = ppir_node_first_succ(node);
94
 
         if (succ->instr_pos == PPIR_INSTR_SLOT_ALU_VEC_ADD) {
95
 
            node->instr_pos = PPIR_INSTR_SLOT_ALU_VEC_MUL;
96
 
            ppir_instr_insert_mul_node(succ, node);
97
 
         }
98
 
         else if (succ->instr_pos == PPIR_INSTR_SLOT_ALU_SCL_ADD &&
99
 
                  alu->dest.ssa.num_components == 1) {
100
 
            node->instr_pos = PPIR_INSTR_SLOT_ALU_SCL_MUL;
101
 
            ppir_instr_insert_mul_node(succ, node);
102
 
         }
103
 
      }
104
 
 
105
 
      /* can't inserted to any existing instr, create one */
106
 
      if (!node->instr && !create_new_instr(block, node))
107
 
         return false;
108
 
 
109
 
      break;
110
 
   }
111
 
   case ppir_node_type_load:
112
 
   case ppir_node_type_load_texture:
113
 
   {
114
 
      if (!create_new_instr(block, node))
115
 
         return false;
116
 
 
117
 
      /* load varying output can be a register, it doesn't need a mov */
118
 
      switch (node->op) {
119
 
      case ppir_op_load_varying:
120
 
      case ppir_op_load_coords:
121
 
      case ppir_op_load_coords_reg:
122
 
      case ppir_op_load_fragcoord:
123
 
      case ppir_op_load_pointcoord:
124
 
      case ppir_op_load_frontface:
125
 
         return true;
126
 
      default:
127
 
         break;
128
 
      }
129
 
 
130
 
      /* Load cannot be pipelined, likely slot is already taken. Create a mov */
131
 
      assert(ppir_node_has_single_src_succ(node));
132
 
      ppir_dest *dest = ppir_node_get_dest(node);
133
 
      assert(dest->type == ppir_target_pipeline);
134
 
      ppir_pipeline pipeline_reg = dest->pipeline;
135
 
 
136
 
      /* Turn dest back to SSA, so we can update predecessors */
137
 
      ppir_node *succ = ppir_node_first_succ(node);
138
 
 
139
 
      /* Single succ can still have multiple references to this node */
140
 
      for (int i = 0; i < ppir_node_get_src_num(succ); i++) {
141
 
         ppir_src *src = ppir_node_get_src(succ, i);
142
 
         if (src && src->node == node) {
143
 
            /* Can consume uniforms directly */
144
 
            dest->type = ppir_target_ssa;
145
 
            dest->ssa.index = -1;
146
 
            ppir_node_target_assign(src, node);
147
 
         }
148
 
      }
149
 
 
150
 
      ppir_node *move = ppir_node_insert_mov(node);
151
 
      if (unlikely(!move))
152
 
         return false;
153
 
 
154
 
      ppir_src *mov_src = ppir_node_get_src(move, 0);
155
 
      mov_src->type = dest->type = ppir_target_pipeline;
156
 
      mov_src->pipeline = dest->pipeline = pipeline_reg;
157
 
 
158
 
      ppir_debug("node_to_instr create move %d for load %d\n",
159
 
                 move->index, node->index);
160
 
 
161
 
      if (!ppir_instr_insert_node(node->instr, move))
162
 
         return false;
163
 
 
164
 
      break;
165
 
   }
166
 
   case ppir_node_type_const: {
167
 
      /* Const cannot be pipelined, too many consts in the instruction.
168
 
       * Create a mov. */
169
 
 
170
 
      ppir_node *move = ppir_node_insert_mov(node);
171
 
      if (!create_new_instr(block, move))
172
 
         return false;
173
 
 
174
 
      ppir_debug("node_to_instr create move %d for const %d\n",
175
 
                 move->index, node->index);
176
 
 
177
 
      ppir_dest *dest = ppir_node_get_dest(node);
178
 
      ppir_src *mov_src = ppir_node_get_src(move, 0);
179
 
 
180
 
      /* update succ from ^const to ssa mov output */
181
 
      ppir_dest *move_dest = ppir_node_get_dest(move);
182
 
      move_dest->type = ppir_target_ssa;
183
 
      ppir_node *succ = ppir_node_first_succ(move);
184
 
      ppir_node_replace_child(succ, node, move);
185
 
 
186
 
      mov_src->type = dest->type = ppir_target_pipeline;
187
 
      mov_src->pipeline = dest->pipeline = ppir_pipeline_reg_const0;
188
 
 
189
 
      if (!ppir_instr_insert_node(move->instr, node))
190
 
         return false;
191
 
 
192
 
      break;
193
 
   }
194
 
   case ppir_node_type_store:
195
 
   {
196
 
      if (node->op == ppir_op_store_temp) {
197
 
         if (!create_new_instr(block, node))
198
 
            return false;
199
 
         break;
200
 
      }
201
 
      break;
202
 
   }
203
 
   case ppir_node_type_discard:
204
 
      if (!create_new_instr(block, node))
205
 
         return false;
206
 
      block->stop = true;
207
 
      break;
208
 
   case ppir_node_type_branch:
209
 
      if (!create_new_instr(block, node))
210
 
         return false;
211
 
      break;
212
 
   default:
213
 
      return false;
214
 
   }
215
 
 
216
 
   return true;
217
 
}
218
 
 
219
 
static unsigned int ppir_node_score(ppir_node *node)
220
 
{
221
 
   /* preferentially expand nodes in later instruction slots first, so
222
 
    * nodes for earlier slots (which are more likely pipelineable) get
223
 
    * added to the ready list. */
224
 
   unsigned int late_slot = 0;
225
 
   int *slots = ppir_op_infos[node->op].slots;
226
 
   if (slots)
227
 
      for (int i = 0; slots[i] != PPIR_INSTR_SLOT_END; i++)
228
 
         late_slot = MAX2(late_slot, slots[i]);
229
 
 
230
 
   /* to untie, favour nodes with pipelines for earlier expansion.
231
 
    * increase that for nodes with chained pipelines */
232
 
   unsigned int pipeline = 0;
233
 
   ppir_node *n = node;
234
 
   ppir_dest *dest = ppir_node_get_dest(n);
235
 
   while (dest && dest->type == ppir_target_pipeline) {
236
 
      pipeline++;
237
 
      assert(ppir_node_has_single_src_succ(n));
238
 
      n = ppir_node_first_succ(n);
239
 
      dest = ppir_node_get_dest(n);
240
 
   }
241
 
   assert(pipeline < 4);
242
 
 
243
 
   return (late_slot << 2 | pipeline);
244
 
}
245
 
 
246
 
static ppir_node *ppir_ready_list_pick_best(ppir_block *block,
247
 
                                            struct list_head *ready_list)
248
 
{
249
 
   unsigned int best_score = 0;
250
 
   ppir_node *best = NULL;
251
 
 
252
 
   list_for_each_entry(ppir_node, node, ready_list, sched_list) {
253
 
      unsigned int score = ppir_node_score(node);
254
 
      if (!best || score > best_score) {
255
 
         best = node;
256
 
         best_score = score;
257
 
      }
258
 
   }
259
 
 
260
 
   assert(best);
261
 
   return best;
262
 
}
263
 
 
264
 
static bool ppir_do_node_to_instr(ppir_block *block, ppir_node *root)
265
 
{
266
 
   struct list_head ready_list;
267
 
   list_inithead(&ready_list);
268
 
   list_addtail(&root->sched_list, &ready_list);
269
 
 
270
 
   while (!list_is_empty(&ready_list)) {
271
 
      ppir_node *node = ppir_ready_list_pick_best(block, &ready_list);
272
 
      list_del(&node->sched_list);
273
 
 
274
 
      /* first try pipeline sched, if that didn't succeed try normal sched */
275
 
      if (!ppir_do_node_to_instr_try_insert(block, node))
276
 
         if (!ppir_do_one_node_to_instr(block, node))
277
 
            return false;
278
 
 
279
 
      /* The node writes output register. We can't stop at this exact
280
 
       * instruction because there may be another node that writes another
281
 
       * output, so set stop flag for the block. We will set stop flag on
282
 
       * the last instruction of the block during codegen
283
 
       */
284
 
      if (node->is_out)
285
 
         block->stop = true;
286
 
 
287
 
      ppir_node_foreach_pred(node, dep) {
288
 
         ppir_node *pred = dep->pred;
289
 
         bool ready = true;
290
 
 
291
 
         /* pred may already have been processed by a previous node */
292
 
         if (pred->instr)
293
 
            continue;
294
 
 
295
 
         /* insert pred only when all its successors have been inserted */
296
 
         ppir_node_foreach_succ(pred, dep) {
297
 
            ppir_node *succ = dep->succ;
298
 
            if (!succ->instr) {
299
 
               ready = false;
300
 
               break;
301
 
            }
302
 
         }
303
 
 
304
 
         if (ready)
305
 
            list_addtail(&pred->sched_list, &ready_list);
306
 
      }
307
 
   }
308
 
 
309
 
   return true;
310
 
}
311
 
 
312
 
static bool ppir_create_instr_from_node(ppir_compiler *comp)
313
 
{
314
 
   list_for_each_entry(ppir_block, block, &comp->block_list, list) {
315
 
      list_for_each_entry(ppir_node, node, &block->node_list, list) {
316
 
         if (ppir_node_is_root(node)) {
317
 
            if (!ppir_do_node_to_instr(block, node))
318
 
               return false;
319
 
         }
320
 
      }
321
 
   }
322
 
 
323
 
   return true;
324
 
}
325
 
 
326
 
static void ppir_build_instr_dependency(ppir_compiler *comp)
327
 
{
328
 
   list_for_each_entry(ppir_block, block, &comp->block_list, list) {
329
 
      list_for_each_entry(ppir_instr, instr, &block->instr_list, list) {
330
 
         for (int i = 0; i < PPIR_INSTR_SLOT_NUM; i++) {
331
 
            ppir_node *node = instr->slots[i];
332
 
            if (node) {
333
 
               ppir_node_foreach_pred(node, dep) {
334
 
                  ppir_node *pred = dep->pred;
335
 
                  if (pred->instr && pred->instr != instr)
336
 
                     ppir_instr_add_dep(instr, pred->instr);
337
 
               }
338
 
            }
339
 
         }
340
 
      }
341
 
   }
342
 
}
343
 
 
344
 
bool ppir_node_to_instr(ppir_compiler *comp)
345
 
{
346
 
   if (!ppir_create_instr_from_node(comp))
347
 
      return false;
348
 
   ppir_instr_print_list(comp);
349
 
 
350
 
   ppir_build_instr_dependency(comp);
351
 
   ppir_instr_print_dep(comp);
352
 
 
353
 
   return true;
354
 
}