efbde11d05e23baf90d6f5c5d87b807cd1c8cdfe
[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 /*
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_src_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
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
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
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
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
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
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 }