Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/gallium/drivers/lima/ir/pp/node_to_instr.c
4574 views
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_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
}
349
350