• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
create_new_instr(ppir_block * block,ppir_node * node)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  */
ppir_do_node_to_instr_try_insert(ppir_block * block,ppir_node * node)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 
ppir_do_one_node_to_instr(ppir_block * block,ppir_node * node)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 *succ = ppir_node_first_succ(node);
93          if (succ->instr_pos == PPIR_INSTR_SLOT_ALU_VEC_ADD) {
94             node->instr_pos = PPIR_INSTR_SLOT_ALU_VEC_MUL;
95             ppir_instr_insert_mul_node(succ, node);
96          }
97          else if (succ->instr_pos == PPIR_INSTR_SLOT_ALU_SCL_ADD &&
98                   alu->dest.ssa.num_components == 1) {
99             node->instr_pos = PPIR_INSTR_SLOT_ALU_SCL_MUL;
100             ppir_instr_insert_mul_node(succ, node);
101          }
102       }
103 
104       /* can't inserted to any existing instr, create one */
105       if (!node->instr && !create_new_instr(block, node))
106          return false;
107 
108       break;
109    }
110    case ppir_node_type_load:
111    case ppir_node_type_load_texture:
112    {
113       if (!create_new_instr(block, node))
114          return false;
115 
116       /* load varying output can be a register, it doesn't need a mov */
117       switch (node->op) {
118       case ppir_op_load_varying:
119       case ppir_op_load_coords:
120       case ppir_op_load_coords_reg:
121       case ppir_op_load_fragcoord:
122       case ppir_op_load_pointcoord:
123       case ppir_op_load_frontface:
124          return true;
125       default:
126          break;
127       }
128 
129       /* Load cannot be pipelined, likely slot is already taken. Create a mov */
130       assert(ppir_node_has_single_src_succ(node));
131       ppir_dest *dest = ppir_node_get_dest(node);
132       assert(dest->type == ppir_target_pipeline);
133       ppir_pipeline pipeline_reg = dest->pipeline;
134 
135       /* Turn dest back to SSA, so we can update predecessors */
136       ppir_node *succ = ppir_node_first_succ(node);
137 
138       /* Single succ can still have multiple references to this node */
139       for (int i = 0; i < ppir_node_get_src_num(succ); i++) {
140          ppir_src *src = ppir_node_get_src(succ, i);
141          if (src && src->node == node) {
142             /* Can consume uniforms directly */
143             dest->type = ppir_target_ssa;
144             dest->ssa.index = -1;
145             ppir_node_target_assign(src, node);
146          }
147       }
148 
149       ppir_node *move = ppir_node_insert_mov(node);
150       if (unlikely(!move))
151          return false;
152 
153       ppir_src *mov_src = ppir_node_get_src(move, 0);
154       mov_src->type = dest->type = ppir_target_pipeline;
155       mov_src->pipeline = dest->pipeline = pipeline_reg;
156 
157       ppir_debug("node_to_instr create move %d for load %d\n",
158                  move->index, node->index);
159 
160       if (!ppir_instr_insert_node(node->instr, move))
161          return false;
162 
163       break;
164    }
165    case ppir_node_type_const: {
166       /* Const cannot be pipelined, too many consts in the instruction.
167        * Create a mov. */
168 
169       ppir_node *move = ppir_node_insert_mov(node);
170       if (!create_new_instr(block, move))
171          return false;
172 
173       ppir_debug("node_to_instr create move %d for const %d\n",
174                  move->index, node->index);
175 
176       ppir_dest *dest = ppir_node_get_dest(node);
177       ppir_src *mov_src = ppir_node_get_src(move, 0);
178 
179       /* update succ from ^const to ssa mov output */
180       ppir_dest *move_dest = ppir_node_get_dest(move);
181       move_dest->type = ppir_target_ssa;
182       ppir_node *succ = ppir_node_first_succ(move);
183       ppir_node_replace_child(succ, node, move);
184 
185       mov_src->type = dest->type = ppir_target_pipeline;
186       mov_src->pipeline = dest->pipeline = ppir_pipeline_reg_const0;
187 
188       if (!ppir_instr_insert_node(move->instr, node))
189          return false;
190 
191       break;
192    }
193    case ppir_node_type_store:
194    {
195       if (node->op == ppir_op_store_temp) {
196          if (!create_new_instr(block, node))
197             return false;
198          break;
199       }
200       break;
201    }
202    case ppir_node_type_discard:
203       if (!create_new_instr(block, node))
204          return false;
205       node->instr->is_end = true;
206       break;
207    case ppir_node_type_branch:
208       if (!create_new_instr(block, node))
209          return false;
210       break;
211    default:
212       return false;
213    }
214 
215    return true;
216 }
217 
ppir_node_score(ppir_node * node)218 static unsigned int ppir_node_score(ppir_node *node)
219 {
220    /* preferentially expand nodes in later instruction slots first, so
221     * nodes for earlier slots (which are more likely pipelineable) get
222     * added to the ready list. */
223    unsigned int late_slot = 0;
224    int *slots = ppir_op_infos[node->op].slots;
225    if (slots)
226       for (int i = 0; slots[i] != PPIR_INSTR_SLOT_END; i++)
227          late_slot = MAX2(late_slot, slots[i]);
228 
229    /* to untie, favour nodes with pipelines for earlier expansion.
230     * increase that for nodes with chained pipelines */
231    unsigned int pipeline = 0;
232    ppir_node *n = node;
233    ppir_dest *dest = ppir_node_get_dest(n);
234    while (dest && dest->type == ppir_target_pipeline) {
235       pipeline++;
236       assert(ppir_node_has_single_src_succ(n));
237       n = ppir_node_first_succ(n);
238       dest = ppir_node_get_dest(n);
239    }
240    assert(pipeline < 4);
241 
242    return (late_slot << 2 | pipeline);
243 }
244 
ppir_ready_list_pick_best(ppir_block * block,struct list_head * ready_list)245 static ppir_node *ppir_ready_list_pick_best(ppir_block *block,
246                                             struct list_head *ready_list)
247 {
248    unsigned int best_score = 0;
249    ppir_node *best = NULL;
250 
251    list_for_each_entry(ppir_node, node, ready_list, sched_list) {
252       unsigned int score = ppir_node_score(node);
253       if (!best || score > best_score) {
254          best = node;
255          best_score = score;
256       }
257    }
258 
259    assert(best);
260    return best;
261 }
262 
ppir_do_node_to_instr(ppir_block * block,ppir_node * root)263 static bool ppir_do_node_to_instr(ppir_block *block, ppir_node *root)
264 {
265    struct list_head ready_list;
266    list_inithead(&ready_list);
267    list_addtail(&root->sched_list, &ready_list);
268 
269    while (!list_is_empty(&ready_list)) {
270       ppir_node *node = ppir_ready_list_pick_best(block, &ready_list);
271       list_del(&node->sched_list);
272 
273       /* first try pipeline sched, if that didn't succeed try normal sched */
274       if (!ppir_do_node_to_instr_try_insert(block, node))
275          if (!ppir_do_one_node_to_instr(block, node))
276             return false;
277 
278       if (node->is_end)
279          node->instr->is_end = true;
280 
281       ppir_node_foreach_pred(node, dep) {
282          ppir_node *pred = dep->pred;
283          bool ready = true;
284 
285          /* pred may already have been processed by a previous node */
286          if (pred->instr)
287             continue;
288 
289          /* insert pred only when all its successors have been inserted */
290          ppir_node_foreach_succ(pred, dep) {
291             ppir_node *succ = dep->succ;
292             if (!succ->instr) {
293                ready = false;
294                break;
295             }
296          }
297 
298          if (ready)
299             list_addtail(&pred->sched_list, &ready_list);
300       }
301    }
302 
303    return true;
304 }
305 
ppir_create_instr_from_node(ppir_compiler * comp)306 static bool ppir_create_instr_from_node(ppir_compiler *comp)
307 {
308    list_for_each_entry(ppir_block, block, &comp->block_list, list) {
309       list_for_each_entry(ppir_node, node, &block->node_list, list) {
310          if (ppir_node_is_root(node)) {
311             if (!ppir_do_node_to_instr(block, node))
312                return false;
313          }
314       }
315    }
316 
317    return true;
318 }
319 
ppir_build_instr_dependency(ppir_compiler * comp)320 static void ppir_build_instr_dependency(ppir_compiler *comp)
321 {
322    list_for_each_entry(ppir_block, block, &comp->block_list, list) {
323       list_for_each_entry(ppir_instr, instr, &block->instr_list, list) {
324          for (int i = 0; i < PPIR_INSTR_SLOT_NUM; i++) {
325             ppir_node *node = instr->slots[i];
326             if (node) {
327                ppir_node_foreach_pred(node, dep) {
328                   ppir_node *pred = dep->pred;
329                   if (pred->instr && pred->instr != instr)
330                      ppir_instr_add_dep(instr, pred->instr);
331                }
332             }
333          }
334       }
335    }
336 }
337 
ppir_node_to_instr(ppir_compiler * comp)338 bool ppir_node_to_instr(ppir_compiler *comp)
339 {
340    if (!ppir_create_instr_from_node(comp))
341       return false;
342    ppir_instr_print_list(comp);
343 
344    ppir_build_instr_dependency(comp);
345    ppir_instr_print_dep(comp);
346 
347    return true;
348 }
349