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
))
201 case ppir_node_type_discard
:
202 if (!create_new_instr(block
, node
))
204 node
->instr
->is_end
= true;
206 case ppir_node_type_branch
:
207 if (!create_new_instr(block
, node
))
217 static bool ppir_do_node_to_instr(ppir_block
*block
, ppir_node
*node
)
219 /* first try pipeline sched, if that didn't succeed try normal scheduling */
220 if (!ppir_do_node_to_instr_try_insert(block
, node
))
221 if (!ppir_do_one_node_to_instr(block
, node
))
225 node
->instr
->is_end
= true;
227 /* we have to make sure the dep not be destroyed (due to
228 * succ change) in ppir_do_node_to_instr, otherwise we can't
229 * do recursion like this */
230 ppir_node_foreach_pred(node
, dep
) {
231 ppir_node
*pred
= dep
->pred
;
234 /* pred may already be processed by the previous pred
235 * (this pred may be both node and previous pred's child) */
239 /* insert pred only when all its successors have been inserted */
240 ppir_node_foreach_succ(pred
, dep
) {
241 ppir_node
*succ
= dep
->succ
;
249 if (!ppir_do_node_to_instr(block
, pred
))
257 static bool ppir_create_instr_from_node(ppir_compiler
*comp
)
259 list_for_each_entry(ppir_block
, block
, &comp
->block_list
, list
) {
260 list_for_each_entry(ppir_node
, node
, &block
->node_list
, list
) {
261 if (ppir_node_is_root(node
)) {
262 if (!ppir_do_node_to_instr(block
, node
))
271 static void ppir_build_instr_dependency(ppir_compiler
*comp
)
273 list_for_each_entry(ppir_block
, block
, &comp
->block_list
, list
) {
274 list_for_each_entry(ppir_instr
, instr
, &block
->instr_list
, list
) {
275 for (int i
= 0; i
< PPIR_INSTR_SLOT_NUM
; i
++) {
276 ppir_node
*node
= instr
->slots
[i
];
278 ppir_node_foreach_pred(node
, dep
) {
279 ppir_node
*pred
= dep
->pred
;
280 if (pred
->instr
&& pred
->instr
!= instr
)
281 ppir_instr_add_dep(instr
, pred
->instr
);
289 bool ppir_node_to_instr(ppir_compiler
*comp
)
291 if (!ppir_create_instr_from_node(comp
))
293 ppir_instr_print_list(comp
);
295 ppir_build_instr_dependency(comp
);
296 ppir_instr_print_dep(comp
);