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
, ppir_node
**next
)
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
))
108 if (node
->op
== ppir_op_store_color
)
109 node
->instr
->is_end
= true;
113 case ppir_node_type_load
:
114 case ppir_node_type_load_texture
:
116 if (!create_new_instr(block
, node
))
119 /* load varying output can be a register, it doesn't need a mov */
121 case ppir_op_load_varying
:
122 case ppir_op_load_coords
:
123 case ppir_op_load_coords_reg
:
124 case ppir_op_load_fragcoord
:
125 case ppir_op_load_pointcoord
:
126 case ppir_op_load_frontface
:
132 /* Load cannot be pipelined, likely slot is already taken. Create a mov */
133 assert(ppir_node_has_single_src_succ(node
));
134 ppir_dest
*dest
= ppir_node_get_dest(node
);
135 assert(dest
->type
== ppir_target_pipeline
);
136 ppir_pipeline pipeline_reg
= dest
->pipeline
;
138 /* Turn dest back to SSA, so we can update predecessors */
139 ppir_node
*succ
= ppir_node_first_succ(node
);
141 /* Single succ can still have multiple references to this node */
142 for (int i
= 0; i
< ppir_node_get_src_num(succ
); i
++) {
143 ppir_src
*src
= ppir_node_get_src(succ
, i
);
144 if (src
&& src
->node
== node
) {
145 /* Can consume uniforms directly */
146 dest
->type
= ppir_target_ssa
;
147 dest
->ssa
.index
= -1;
148 ppir_node_target_assign(src
, node
);
152 ppir_node
*move
= ppir_node_insert_mov(node
);
156 ppir_src
*mov_src
= ppir_node_get_src(move
, 0);
157 mov_src
->type
= dest
->type
= ppir_target_pipeline
;
158 mov_src
->pipeline
= dest
->pipeline
= pipeline_reg
;
160 ppir_debug("node_to_instr create move %d for load %d\n",
161 move
->index
, node
->index
);
163 if (!ppir_instr_insert_node(node
->instr
, move
))
168 case ppir_node_type_const
:
169 /* Const nodes are supposed to go through do_node_to_instr_pipeline() */
172 case ppir_node_type_store
:
174 if (node
->op
== ppir_op_store_temp
) {
175 if (!create_new_instr(block
, node
))
180 case ppir_node_type_discard
:
181 if (!create_new_instr(block
, node
))
183 node
->instr
->is_end
= true;
185 case ppir_node_type_branch
:
186 if (!create_new_instr(block
, node
))
196 static bool ppir_do_node_to_instr(ppir_block
*block
, ppir_node
*node
)
198 ppir_node
*next
= node
;
200 /* first try pipeline sched, if that didn't succeed try normal scheduling */
201 if (!ppir_do_node_to_instr_try_insert(block
, node
))
202 if (!ppir_do_one_node_to_instr(block
, node
, &next
))
205 /* next may have been updated in ppir_do_one_node_to_instr */
208 /* we have to make sure the dep not be destroyed (due to
209 * succ change) in ppir_do_node_to_instr, otherwise we can't
210 * do recursion like this */
211 ppir_node_foreach_pred(node
, dep
) {
212 ppir_node
*pred
= dep
->pred
;
215 /* pred may already be processed by the previous pred
216 * (this pred may be both node and previous pred's child) */
220 /* insert pred only when all its successors have been inserted */
221 ppir_node_foreach_succ(pred
, dep
) {
222 ppir_node
*succ
= dep
->succ
;
230 if (!ppir_do_node_to_instr(block
, pred
))
238 static bool ppir_create_instr_from_node(ppir_compiler
*comp
)
240 list_for_each_entry(ppir_block
, block
, &comp
->block_list
, list
) {
241 list_for_each_entry(ppir_node
, node
, &block
->node_list
, list
) {
242 if (ppir_node_is_root(node
)) {
243 if (!ppir_do_node_to_instr(block
, node
))
252 static void ppir_build_instr_dependency(ppir_compiler
*comp
)
254 list_for_each_entry(ppir_block
, block
, &comp
->block_list
, list
) {
255 list_for_each_entry(ppir_instr
, instr
, &block
->instr_list
, list
) {
256 for (int i
= 0; i
< PPIR_INSTR_SLOT_NUM
; i
++) {
257 ppir_node
*node
= instr
->slots
[i
];
259 ppir_node_foreach_pred(node
, dep
) {
260 ppir_node
*pred
= dep
->pred
;
261 if (pred
->instr
&& pred
->instr
!= instr
)
262 ppir_instr_add_dep(instr
, pred
->instr
);
270 bool ppir_node_to_instr(ppir_compiler
*comp
)
272 if (!ppir_create_instr_from_node(comp
))
274 ppir_instr_print_list(comp
);
276 ppir_build_instr_dependency(comp
);
277 ppir_instr_print_dep(comp
);