dbc1c9c32cee33e487a83b7b0c3e96c1d537ffd0
[mesa.git] / src / gallium / drivers / lima / ir / pp / node_to_instr.c
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 static bool insert_to_each_succ_instr(ppir_block *block, ppir_node *node)
41 {
42 ppir_dest *dest = ppir_node_get_dest(node);
43 assert(dest->type == ppir_target_ssa);
44
45 ppir_node *move = NULL;
46
47 ppir_node_foreach_succ_safe(node, dep) {
48 ppir_node *succ = dep->succ;
49 assert(succ->type == ppir_node_type_alu ||
50 succ->type == ppir_node_type_branch);
51
52 if (!ppir_instr_insert_node(succ->instr, node)) {
53 /* create a move node to insert for failed node */
54 if (!move) {
55 move = ppir_node_create(block, ppir_op_mov, -1, 0);
56 if (unlikely(!move))
57 return false;
58
59 ppir_debug("node_to_instr create move %d for %d\n",
60 move->index, node->index);
61
62 ppir_alu_node *alu = ppir_node_to_alu(move);
63 alu->dest = *dest;
64 alu->num_src = 1;
65 ppir_node_target_assign(alu->src, dest);
66 for (int i = 0; i < 4; i++)
67 alu->src->swizzle[i] = i;
68 }
69
70 ppir_node_replace_pred(dep, move);
71 ppir_node_replace_child(succ, node, move);
72 }
73 }
74
75 if (move) {
76 if (!create_new_instr(block, move))
77 return false;
78
79 ASSERTED bool insert_result =
80 ppir_instr_insert_node(move->instr, node);
81 assert(insert_result);
82
83 ppir_node_add_dep(move, node);
84 list_addtail(&move->list, &node->list);
85 }
86
87 /* dupliacte node for each successor */
88
89 bool first = true;
90 struct list_head dup_list;
91 list_inithead(&dup_list);
92
93 ppir_node_foreach_succ_safe(node, dep) {
94 ppir_node *succ = dep->succ;
95
96 if (first) {
97 first = false;
98 node->instr = succ->instr;
99 continue;
100 }
101
102 if (succ->instr == node->instr)
103 continue;
104
105 list_for_each_entry(ppir_node, dup, &dup_list, list) {
106 if (succ->instr == dup->instr) {
107 ppir_node_replace_pred(dep, dup);
108 continue;
109 }
110 }
111
112 ppir_node *dup = ppir_node_create(block, node->op, -1, 0);
113 if (unlikely(!dup))
114 return false;
115 list_addtail(&dup->list, &dup_list);
116
117 ppir_debug("node_to_instr duplicate %s %d from %d\n",
118 ppir_op_infos[dup->op].name, dup->index, node->index);
119
120 ppir_instr *instr = succ->instr;
121 dup->instr = instr;
122 dup->instr_pos = node->instr_pos;
123 ppir_node_replace_pred(dep, dup);
124
125 if ((node->op == ppir_op_load_uniform) || (node->op == ppir_op_load_temp)) {
126 ppir_load_node *load = ppir_node_to_load(node);
127 ppir_load_node *dup_load = ppir_node_to_load(dup);
128 dup_load->dest = load->dest;
129 dup_load->index = load->index;
130 dup_load->num_components = load->num_components;
131 instr->slots[node->instr_pos] = dup;
132 }
133 }
134
135 list_splicetail(&dup_list, &node->list);
136
137 return true;
138 }
139
140 /*
141 * If a node has a pipeline dest, schedule it in the same instruction as its
142 * successor.
143 * Since it has a pipeline dest, it must have only one successor and since we
144 * schedule nodes backwards, its successor must have already been scheduled.
145 */
146 static bool ppir_do_node_to_instr_pipeline(ppir_block *block, ppir_node *node)
147 {
148 ppir_dest *dest = ppir_node_get_dest(node);
149
150 if (!dest || dest->type != ppir_target_pipeline)
151 return false;
152
153 assert(ppir_node_has_single_succ(node));
154 ppir_node *succ = ppir_node_first_succ(node);
155 assert(succ);
156 assert(succ->instr);
157
158 if (!ppir_instr_insert_node(succ->instr, node))
159 return false;
160
161 return true;
162 }
163
164 static bool ppir_do_one_node_to_instr(ppir_block *block, ppir_node *node, ppir_node **next)
165 {
166 switch (node->type) {
167 case ppir_node_type_alu:
168 {
169 /* merge pred mul and succ add in the same instr can save a reg
170 * by using pipeline reg ^vmul/^fmul */
171 ppir_alu_node *alu = ppir_node_to_alu(node);
172 if (alu->dest.type == ppir_target_ssa &&
173 ppir_node_has_single_succ(node)) {
174 ppir_node *succ = ppir_node_first_succ(node);
175 if (succ->instr_pos == PPIR_INSTR_SLOT_ALU_VEC_ADD) {
176 node->instr_pos = PPIR_INSTR_SLOT_ALU_VEC_MUL;
177 /* select instr's condition must be inserted to fmul slot */
178 if (succ->op == ppir_op_select &&
179 ppir_node_first_pred(succ) == node) {
180 assert(alu->dest.ssa.num_components == 1);
181 node->instr_pos = PPIR_INSTR_SLOT_ALU_SCL_MUL;
182 }
183 ppir_instr_insert_mul_node(succ, node);
184 }
185 else if (succ->instr_pos == PPIR_INSTR_SLOT_ALU_SCL_ADD &&
186 alu->dest.ssa.num_components == 1) {
187 node->instr_pos = PPIR_INSTR_SLOT_ALU_SCL_MUL;
188 ppir_instr_insert_mul_node(succ, node);
189 }
190 }
191
192 /* can't inserted to any existing instr, create one */
193 if (!node->instr && !create_new_instr(block, node))
194 return false;
195
196 break;
197 }
198 case ppir_node_type_load:
199 if ((node->op == ppir_op_load_uniform) || (node->op == ppir_op_load_temp)) {
200 /* merge pred load_uniform into succ instr can save a reg
201 * by using pipeline reg */
202 if (!insert_to_each_succ_instr(block, node))
203 return false;
204
205 ppir_load_node *load = ppir_node_to_load(node);
206 load->dest.type = ppir_target_pipeline;
207 load->dest.pipeline = ppir_pipeline_reg_uniform;
208 }
209 else if (node->op == ppir_op_load_temp) {
210 /* merge pred load_temp into succ instr can save a reg
211 * by using pipeline reg */
212 if (!insert_to_each_succ_instr(block, node))
213 return false;
214
215 ppir_load_node *load = ppir_node_to_load(node);
216 load->dest.type = ppir_target_pipeline;
217 load->dest.pipeline = ppir_pipeline_reg_uniform;
218 }
219 else if (node->op == ppir_op_load_varying ||
220 node->op == ppir_op_load_fragcoord ||
221 node->op == ppir_op_load_pointcoord ||
222 node->op == ppir_op_load_frontface) {
223 /* delay the load varying dup to scheduler */
224 if (!create_new_instr(block, node))
225 return false;
226 }
227 else {
228 /* not supported yet */
229 assert(0);
230 return false;
231 }
232 break;
233 case ppir_node_type_load_texture:
234 if (!create_new_instr(block, node))
235 return false;
236 break;
237 case ppir_node_type_const:
238 if (!insert_to_each_succ_instr(block, node))
239 return false;
240 break;
241 case ppir_node_type_store:
242 {
243 if (node->op == ppir_op_store_temp) {
244 if (!create_new_instr(block, node))
245 return false;
246 break;
247 }
248
249 /* Only the store color node should appear here.
250 * Currently we always insert a move node as the end instr.
251 * But it should only be done when:
252 * 1. store a const node
253 * 2. store a load node
254 * 3. store a reg assigned in another block like loop/if
255 */
256
257 assert(node->op == ppir_op_store_color);
258
259 ppir_node *move = ppir_node_create(block, ppir_op_mov, -1, 0);
260 if (unlikely(!move))
261 return false;
262
263 ppir_debug("node_to_instr create move %d from store %d\n",
264 move->index, node->index);
265
266 ppir_node_foreach_pred_safe(node, dep) {
267 ppir_node *pred = dep->pred;
268 /* we can't do this in this function except here as this
269 * store is the root of this recursion */
270 ppir_node_remove_dep(dep);
271 ppir_node_add_dep(move, pred);
272 }
273
274 ppir_node_add_dep(node, move);
275 list_addtail(&move->list, &node->list);
276
277 ppir_alu_node *alu = ppir_node_to_alu(move);
278 ppir_store_node *store = ppir_node_to_store(node);
279 alu->src[0] = store->src;
280 alu->num_src = 1;
281
282 alu->dest.type = ppir_target_ssa;
283 alu->dest.ssa.num_components = 4;
284 alu->dest.ssa.live_in = INT_MAX;
285 alu->dest.ssa.live_out = 0;
286 alu->dest.write_mask = 0xf;
287
288 store->src.type = ppir_target_ssa;
289 store->src.ssa = &alu->dest.ssa;
290
291 if (!create_new_instr(block, move))
292 return false;
293
294 move->instr->is_end = true;
295 node->instr = move->instr;
296
297 /* use move for the following recursion */
298 *next = move;
299 break;
300 }
301 case ppir_node_type_discard:
302 if (!create_new_instr(block, node))
303 return false;
304 node->instr->is_end = true;
305 break;
306 case ppir_node_type_branch:
307 if (!create_new_instr(block, node))
308 return false;
309 break;
310 default:
311 return false;
312 }
313
314 return true;
315 }
316
317 static bool ppir_do_node_to_instr(ppir_block *block, ppir_node *node)
318 {
319 ppir_node *next = node;
320
321 /* first try pipeline sched, if that didn't succeed try normal scheduling */
322 if (!ppir_do_node_to_instr_pipeline(block, node))
323 if (!ppir_do_one_node_to_instr(block, node, &next))
324 return false;
325
326 /* next may have been updated in ppir_do_one_node_to_instr */
327 node = next;
328
329 /* we have to make sure the dep not be destroyed (due to
330 * succ change) in ppir_do_node_to_instr, otherwise we can't
331 * do recursion like this */
332 ppir_node_foreach_pred(node, dep) {
333 ppir_node *pred = dep->pred;
334 bool ready = true;
335
336 /* pred may already be processed by the previous pred
337 * (this pred may be both node and previous pred's child) */
338 if (pred->instr)
339 continue;
340
341 /* insert pred only when all its successors have been inserted */
342 ppir_node_foreach_succ(pred, dep) {
343 ppir_node *succ = dep->succ;
344 if (!succ->instr) {
345 ready = false;
346 break;
347 }
348 }
349
350 if (ready) {
351 if (!ppir_do_node_to_instr(block, pred))
352 return false;
353 }
354 }
355
356 return true;
357 }
358
359 static bool ppir_create_instr_from_node(ppir_compiler *comp)
360 {
361 list_for_each_entry(ppir_block, block, &comp->block_list, list) {
362 list_for_each_entry(ppir_node, node, &block->node_list, list) {
363 if (ppir_node_is_root(node)) {
364 if (!ppir_do_node_to_instr(block, node))
365 return false;
366 }
367 }
368 }
369
370 return true;
371 }
372
373 static void ppir_build_instr_dependency(ppir_compiler *comp)
374 {
375 list_for_each_entry(ppir_block, block, &comp->block_list, list) {
376 list_for_each_entry(ppir_instr, instr, &block->instr_list, list) {
377 for (int i = 0; i < PPIR_INSTR_SLOT_NUM; i++) {
378 ppir_node *node = instr->slots[i];
379 if (node) {
380 ppir_node_foreach_pred(node, dep) {
381 ppir_node *pred = dep->pred;
382 if (pred->instr && pred->instr != instr)
383 ppir_instr_add_dep(instr, pred->instr);
384 }
385 }
386 }
387 }
388 }
389 }
390
391 bool ppir_node_to_instr(ppir_compiler *comp)
392 {
393 if (!ppir_create_instr_from_node(comp))
394 return false;
395 ppir_instr_print_list(comp);
396
397 ppir_build_instr_dependency(comp);
398 ppir_instr_print_dep(comp);
399
400 return true;
401 }