2 * Copyright (c) 2017 Lima Project
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:
11 * The above copyright notice and this permission notice (including the
12 * next paragraph) shall be included in all copies or substantial portions
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.
28 static bool create_new_instr(ppir_block
*block
, ppir_node
*node
)
30 ppir_instr
*instr
= ppir_instr_create(block
);
34 if (!ppir_instr_insert_node(instr
, node
))
41 * If a node has a pipeline dest, schedule it in the same instruction as its
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.
48 static bool ppir_do_node_to_instr_try_insert(ppir_block
*block
, ppir_node
*node
)
50 ppir_dest
*dest
= ppir_node_get_dest(node
);
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
);
58 return ppir_instr_insert_node(succ
->instr
, node
);
62 case ppir_node_type_load
:
68 if (!ppir_node_has_single_src_succ(node
))
71 ppir_node
*succ
= ppir_node_first_succ(node
);
75 return ppir_instr_insert_node(succ
->instr
, node
);
78 static bool ppir_do_one_node_to_instr(ppir_block
*block
, ppir_node
*node
)
81 case ppir_node_type_alu
:
83 /* don't create an instr for undef node */
84 if (node
->op
== ppir_op_undef
)
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
);
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
);
104 /* can't inserted to any existing instr, create one */
105 if (!node
->instr
&& !create_new_instr(block
, node
))
110 case ppir_node_type_load
:
111 case ppir_node_type_load_texture
:
113 if (!create_new_instr(block
, node
))
116 /* load varying output can be a register, it doesn't need a mov */
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
:
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
;
135 /* Turn dest back to SSA, so we can update predecessors */
136 ppir_node
*succ
= ppir_node_first_succ(node
);
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
);
149 ppir_node
*move
= ppir_node_insert_mov(node
);
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
;
157 ppir_debug("node_to_instr create move %d for load %d\n",
158 move
->index
, node
->index
);
160 if (!ppir_instr_insert_node(node
->instr
, move
))
165 case ppir_node_type_const
: {
166 /* Const cannot be pipelined, too many consts in the instruction.
169 ppir_node
*move
= ppir_node_insert_mov(node
);
170 if (!create_new_instr(block
, move
))
173 ppir_debug("node_to_instr create move %d for const %d\n",
174 move
->index
, node
->index
);
176 ppir_dest
*dest
= ppir_node_get_dest(node
);
177 ppir_src
*mov_src
= ppir_node_get_src(move
, 0);
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
);
185 mov_src
->type
= dest
->type
= ppir_target_pipeline
;
186 mov_src
->pipeline
= dest
->pipeline
= ppir_pipeline_reg_const0
;
188 if (!ppir_instr_insert_node(move
->instr
, node
))
193 case ppir_node_type_store
:
195 if (node
->op
== ppir_op_store_temp
) {
196 if (!create_new_instr(block
, node
))
202 case ppir_node_type_discard
:
203 if (!create_new_instr(block
, node
))
205 node
->instr
->is_end
= true;
207 case ppir_node_type_branch
:
208 if (!create_new_instr(block
, node
))
218 static unsigned int ppir_node_score(ppir_node
*node
)
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
;
226 for (int i
= 0; slots
[i
] != PPIR_INSTR_SLOT_END
; i
++)
227 late_slot
= MAX2(late_slot
, slots
[i
]);
229 /* to untie, favour nodes with pipelines for earlier expansion.
230 * increase that for nodes with chained pipelines */
231 unsigned int pipeline
= 0;
233 ppir_dest
*dest
= ppir_node_get_dest(n
);
234 while (dest
&& dest
->type
== ppir_target_pipeline
) {
236 assert(ppir_node_has_single_src_succ(n
));
237 n
= ppir_node_first_succ(n
);
238 dest
= ppir_node_get_dest(n
);
240 assert(pipeline
< 4);
242 return (late_slot
<< 2 | pipeline
);
245 static ppir_node
*ppir_ready_list_pick_best(ppir_block
*block
,
246 struct list_head
*ready_list
)
248 unsigned int best_score
= 0;
249 ppir_node
*best
= NULL
;
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
) {
263 static bool ppir_do_node_to_instr(ppir_block
*block
, ppir_node
*root
)
265 struct list_head ready_list
;
266 list_inithead(&ready_list
);
267 list_addtail(&root
->sched_list
, &ready_list
);
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
);
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
))
279 node
->instr
->is_end
= true;
281 ppir_node_foreach_pred(node
, dep
) {
282 ppir_node
*pred
= dep
->pred
;
285 /* pred may already have been processed by a previous node */
289 /* insert pred only when all its successors have been inserted */
290 ppir_node_foreach_succ(pred
, dep
) {
291 ppir_node
*succ
= dep
->succ
;
299 list_addtail(&pred
->sched_list
, &ready_list
);
306 static bool ppir_create_instr_from_node(ppir_compiler
*comp
)
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
))
320 static void ppir_build_instr_dependency(ppir_compiler
*comp
)
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
];
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
);
338 bool ppir_node_to_instr(ppir_compiler
*comp
)
340 if (!ppir_create_instr_from_node(comp
))
342 ppir_instr_print_list(comp
);
344 ppir_build_instr_dependency(comp
);
345 ppir_instr_print_dep(comp
);