1 /*
2 * Copyright 2011 Tom Stellard <[email protected]>
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include "radeon_variable.h"
9
10 #include "memory_pool.h"
11 #include "radeon_compiler_util.h"
12 #include "radeon_dataflow.h"
13 #include "radeon_list.h"
14 #include "radeon_opcodes.h"
15 #include "radeon_program.h"
16
17 /**
18 * Rewrite the index and writemask for the destination register of var
19 * and its friends to new_index and new_writemask. This function also takes
20 * care of rewriting the swizzles for the sources of var.
21 */
rc_variable_change_dst(struct rc_variable * var,unsigned int new_index,unsigned int new_writemask)22 void rc_variable_change_dst(
23 struct rc_variable * var,
24 unsigned int new_index,
25 unsigned int new_writemask)
26 {
27 struct rc_variable * var_ptr;
28 struct rc_list * readers;
29 unsigned int old_mask = rc_variable_writemask_sum(var);
30 unsigned int conversion_swizzle =
31 rc_make_conversion_swizzle(old_mask, new_writemask);
32
33 for (var_ptr = var; var_ptr; var_ptr = var_ptr->Friend) {
34 if (var_ptr->Inst->Type == RC_INSTRUCTION_NORMAL) {
35 rc_normal_rewrite_writemask(var_ptr->Inst,
36 conversion_swizzle);
37 var_ptr->Inst->U.I.DstReg.Index = new_index;
38 } else {
39 struct rc_pair_sub_instruction * sub;
40 if (var_ptr->Dst.WriteMask == RC_MASK_W) {
41 assert(new_writemask & RC_MASK_W);
42 sub = &var_ptr->Inst->U.P.Alpha;
43 } else {
44 sub = &var_ptr->Inst->U.P.RGB;
45 rc_pair_rewrite_writemask(sub,
46 conversion_swizzle);
47 }
48 sub->DestIndex = new_index;
49 }
50 }
51
52 readers = rc_variable_readers_union(var);
53
54 for ( ; readers; readers = readers->Next) {
55 struct rc_reader * reader = readers->Item;
56 if (reader->Inst->Type == RC_INSTRUCTION_NORMAL) {
57 reader->U.I.Src->Index = new_index;
58 reader->U.I.Src->Swizzle = rc_rewrite_swizzle(
59 reader->U.I.Src->Swizzle, conversion_swizzle);
60 } else {
61 struct rc_pair_instruction * pair_inst =
62 &reader->Inst->U.P;
63 unsigned int src_type = rc_source_type_swz(
64 reader->U.P.Arg->Swizzle);
65
66 int src_index = reader->U.P.Arg->Source;
67 if (src_index == RC_PAIR_PRESUB_SRC) {
68 src_index = rc_pair_get_src_index(
69 pair_inst, reader->U.P.Src);
70 }
71 rc_pair_remove_src(reader->Inst, src_type,
72 src_index);
73 /* Reuse the source index of the source that
74 * was just deleted and set its register
75 * index. We can't use rc_pair_alloc_source
76 * for this because it might return a source
77 * index that is already being used. */
78 if (src_type & RC_SOURCE_RGB) {
79 pair_inst->RGB.Src[src_index]
80 .Used = 1;
81 pair_inst->RGB.Src[src_index]
82 .Index = new_index;
83 pair_inst->RGB.Src[src_index]
84 .File = RC_FILE_TEMPORARY;
85 }
86 if (src_type & RC_SOURCE_ALPHA) {
87 pair_inst->Alpha.Src[src_index]
88 .Used = 1;
89 pair_inst->Alpha.Src[src_index]
90 .Index = new_index;
91 pair_inst->Alpha.Src[src_index]
92 .File = RC_FILE_TEMPORARY;
93 }
94 reader->U.P.Arg->Swizzle = rc_rewrite_swizzle(
95 reader->U.P.Arg->Swizzle, conversion_swizzle);
96 if (reader->U.P.Arg->Source != RC_PAIR_PRESUB_SRC) {
97 reader->U.P.Arg->Source = src_index;
98 }
99 }
100 }
101 }
102
103 /**
104 * Compute the live intervals for var and its friends.
105 */
rc_variable_compute_live_intervals(struct rc_variable * var)106 void rc_variable_compute_live_intervals(struct rc_variable * var)
107 {
108 while(var) {
109 unsigned int i;
110 unsigned int start = var->Inst->IP;
111
112 for (i = 0; i < var->ReaderCount; i++) {
113 unsigned int chan;
114 unsigned int chan_start = start;
115 unsigned int chan_end = var->Readers[i].Inst->IP;
116 unsigned int mask = var->Readers[i].WriteMask;
117 struct rc_instruction * inst;
118
119 /* Extend the live interval of T0 to the start of the
120 * loop for sequences like:
121 * BGNLOOP
122 * read T0
123 * ...
124 * write T0
125 * ENDLOOP
126 */
127 if (var->Readers[i].Inst->IP < start) {
128 struct rc_instruction * bgnloop =
129 rc_match_endloop(var->Readers[i].Inst);
130 chan_start = bgnloop->IP;
131 }
132
133 /* Extend the live interval of T0 to the start of the
134 * loop in case there is a BRK instruction in the loop
135 * (we don't actually check for a BRK instruction we
136 * assume there is one somewhere in the loop, which
137 * there usually is) for sequences like:
138 * BGNLOOP
139 * ...
140 * conditional BRK
141 * ...
142 * write T0
143 * ENDLOOP
144 * read T0
145 ***************************************************
146 * Extend the live interval of T0 to the end of the
147 * loop for sequences like:
148 * write T0
149 * BGNLOOP
150 * ...
151 * read T0
152 * ENDLOOP
153 */
154 for (inst = var->Inst; inst != var->Readers[i].Inst;
155 inst = inst->Next) {
156 rc_opcode op = rc_get_flow_control_inst(inst);
157 if (op == RC_OPCODE_ENDLOOP) {
158 struct rc_instruction * bgnloop =
159 rc_match_endloop(inst);
160 if (bgnloop->IP < chan_start) {
161 chan_start = bgnloop->IP;
162 }
163 } else if (op == RC_OPCODE_BGNLOOP) {
164 struct rc_instruction * endloop =
165 rc_match_bgnloop(inst);
166 if (endloop->IP > chan_end) {
167 chan_end = endloop->IP;
168 }
169 }
170 }
171
172 for (chan = 0; chan < 4; chan++) {
173 if ((mask >> chan) & 0x1) {
174 if (!var->Live[chan].Used
175 || chan_start < var->Live[chan].Start) {
176 var->Live[chan].Start =
177 chan_start;
178 }
179 if (!var->Live[chan].Used
180 || chan_end > var->Live[chan].End) {
181 var->Live[chan].End = chan_end;
182 }
183 var->Live[chan].Used = 1;
184 }
185 }
186 }
187 var = var->Friend;
188 }
189 }
190
191 /**
192 * @return 1 if a and b share a reader
193 * @return 0 if they do not
194 */
readers_intersect(struct rc_variable * a,struct rc_variable * b)195 static unsigned int readers_intersect(
196 struct rc_variable * a,
197 struct rc_variable * b)
198 {
199 unsigned int a_index, b_index;
200 for (a_index = 0; a_index < a->ReaderCount; a_index++) {
201 struct rc_reader reader_a = a->Readers[a_index];
202 for (b_index = 0; b_index < b->ReaderCount; b_index++) {
203 struct rc_reader reader_b = b->Readers[b_index];
204 if (reader_a.Inst->Type == RC_INSTRUCTION_NORMAL
205 && reader_b.Inst->Type == RC_INSTRUCTION_NORMAL
206 && reader_a.U.I.Src == reader_b.U.I.Src) {
207
208 return 1;
209 }
210 if (reader_a.Inst->Type == RC_INSTRUCTION_PAIR
211 && reader_b.Inst->Type == RC_INSTRUCTION_PAIR
212 && reader_a.U.P.Src == reader_b.U.P.Src) {
213
214 return 1;
215 }
216 }
217 }
218 return 0;
219 }
220
rc_variable_add_friend(struct rc_variable * var,struct rc_variable * friend)221 void rc_variable_add_friend(
222 struct rc_variable * var,
223 struct rc_variable * friend)
224 {
225 assert(var->Dst.Index == friend->Dst.Index);
226 while(var->Friend) {
227 var = var->Friend;
228 }
229 var->Friend = friend;
230 }
231
rc_variable(struct radeon_compiler * c,unsigned int DstFile,unsigned int DstIndex,unsigned int DstWriteMask,struct rc_reader_data * reader_data)232 struct rc_variable * rc_variable(
233 struct radeon_compiler * c,
234 unsigned int DstFile,
235 unsigned int DstIndex,
236 unsigned int DstWriteMask,
237 struct rc_reader_data * reader_data)
238 {
239 struct rc_variable * new =
240 memory_pool_malloc(&c->Pool, sizeof(struct rc_variable));
241 memset(new, 0, sizeof(struct rc_variable));
242 new->C = c;
243 new->Dst.File = DstFile;
244 new->Dst.Index = DstIndex;
245 new->Dst.WriteMask = DstWriteMask;
246 if (reader_data) {
247 new->Inst = reader_data->Writer;
248 new->ReaderCount = reader_data->ReaderCount;
249 new->Readers = reader_data->Readers;
250 }
251 return new;
252 }
253
get_variable_helper(struct rc_list ** variable_list,struct rc_variable * variable)254 static void get_variable_helper(
255 struct rc_list ** variable_list,
256 struct rc_variable * variable)
257 {
258 struct rc_list * list_ptr;
259 for (list_ptr = *variable_list; list_ptr; list_ptr = list_ptr->Next) {
260 struct rc_variable * var;
261 for (var = list_ptr->Item; var; var = var->Friend) {
262 if (readers_intersect(var, variable)) {
263 rc_variable_add_friend(var, variable);
264 return;
265 }
266 }
267 }
268 rc_list_add(variable_list, rc_list(&variable->C->Pool, variable));
269 }
270
get_variable_pair_helper(struct rc_list ** variable_list,struct radeon_compiler * c,struct rc_instruction * inst,struct rc_pair_sub_instruction * sub_inst)271 static void get_variable_pair_helper(
272 struct rc_list ** variable_list,
273 struct radeon_compiler * c,
274 struct rc_instruction * inst,
275 struct rc_pair_sub_instruction * sub_inst)
276 {
277 struct rc_reader_data reader_data;
278 struct rc_variable * new_var;
279 rc_register_file file;
280 unsigned int writemask;
281
282 if (sub_inst->Opcode == RC_OPCODE_NOP) {
283 return;
284 }
285 memset(&reader_data, 0, sizeof(struct rc_reader_data));
286 rc_get_readers_sub(c, inst, sub_inst, &reader_data, NULL, NULL, NULL);
287
288 if (reader_data.ReaderCount == 0) {
289 return;
290 }
291
292 if (sub_inst->WriteMask) {
293 file = RC_FILE_TEMPORARY;
294 writemask = sub_inst->WriteMask;
295 } else if (sub_inst->OutputWriteMask) {
296 file = RC_FILE_OUTPUT;
297 writemask = sub_inst->OutputWriteMask;
298 } else {
299 writemask = 0;
300 file = RC_FILE_NONE;
301 }
302 new_var = rc_variable(c, file, sub_inst->DestIndex, writemask,
303 &reader_data);
304 get_variable_helper(variable_list, new_var);
305 }
306
307 /**
308 * Compare function for sorting variable pointers by the lowest instruction
309 * IP from it and its friends.
310 */
cmpfunc_variable_by_ip(const void * a,const void * b)311 static int cmpfunc_variable_by_ip (const void * a, const void * b) {
312 struct rc_variable * var_a = *(struct rc_variable **)a;
313 struct rc_variable * var_b = *(struct rc_variable **)b;
314 unsigned int min_ip_a = var_a->Inst->IP;
315 unsigned int min_ip_b = var_b->Inst->IP;
316
317 /* Find the minimal IP of a variable and its friends */
318 while (var_a->Friend) {
319 var_a = var_a->Friend;
320 if (var_a->Inst->IP < min_ip_a)
321 min_ip_a = var_a->Inst->IP;
322 }
323 while (var_b->Friend) {
324 var_b = var_b->Friend;
325 if (var_b->Inst->IP < min_ip_b)
326 min_ip_b = var_b->Inst->IP;
327 }
328
329 return (int)min_ip_a - (int)min_ip_b;
330 }
331
332 /**
333 * Generate a list of variables used by the shader program. Each instruction
334 * that writes to a register is considered a variable. The struct rc_variable
335 * data structure includes a list of readers and is essentially a
336 * definition-use chain. Any two variables that share a reader are considered
337 * "friends" and they are linked together via the Friend attribute.
338 */
rc_get_variables(struct radeon_compiler * c)339 struct rc_list * rc_get_variables(struct radeon_compiler * c)
340 {
341 struct rc_instruction * inst;
342 struct rc_list * variable_list = NULL;
343
344 /* We search for the variables in two loops in order to get it right in
345 * the following specific case
346 *
347 * IF aluresult.x___;
348 * ...
349 * MAD temp[0].xyz, src0.000, src0.111, src0.000
350 * MAD temp[0].w, src0.0, src0.1, src0.0
351 * ELSE;
352 * ...
353 * TXB temp[0], temp[1].xy_w, 2D[0] SEM_WAIT SEM_ACQUIRE;
354 * ENDIF;
355 * src0.xyz = input[0], src0.w = input[0], src1.xyz = temp[0], src1.w = temp[0] SEM_WAIT
356 * MAD temp[1].xyz, src0.xyz, src1.xyz, src0.000
357 * MAD temp[1].w, src0.w, src1.w, src0.0
358 *
359 * If we go just in one loop, we will first create two variables for the
360 * temp[0].xyz and temp[0].w. This happens because they don't share a reader
361 * as the src1.xyz and src1.w of the instruction where the value is used are
362 * in theory independent. They are not because the same register is written
363 * also by the texture instruction in the other branch and TEX can't write xyz
364 * and w separately.
365 *
366 * Therefore first search for RC_INSTRUCTION_NORMAL to create variables from
367 * the texture instruction and than the pair instructions will be properly
368 * marked as friends. So we will end with only one variable here as we should.
369 *
370 * This doesn't matter before the pair translation, because everything is
371 * RC_INSTRUCTION_NORMAL.
372 */
373 for (inst = c->Program.Instructions.Next;
374 inst != &c->Program.Instructions;
375 inst = inst->Next) {
376 if (inst->Type == RC_INSTRUCTION_NORMAL) {
377 struct rc_reader_data reader_data;
378 struct rc_variable * new_var;
379 memset(&reader_data, 0, sizeof(reader_data));
380 rc_get_readers(c, inst, &reader_data, NULL, NULL, NULL);
381 if (reader_data.ReaderCount == 0) {
382 /* Variable is only returned if there is both writer
383 * and reader. This means dead writes will not get
384 * register allocated as a result and can overwrite random
385 * registers. Assert on dead writes instead so we can improve
386 * the DCE.
387 */
388 const struct rc_opcode_info *opcode =
389 rc_get_opcode_info(inst->U.I.Opcode);
390 assert(c->type == RC_FRAGMENT_PROGRAM ||
391 !opcode->HasDstReg ||
392 inst->U.I.DstReg.File == RC_FILE_OUTPUT ||
393 inst->U.I.DstReg.File == RC_FILE_ADDRESS);
394 continue;
395 }
396 new_var = rc_variable(c, inst->U.I.DstReg.File,
397 inst->U.I.DstReg.Index,
398 inst->U.I.DstReg.WriteMask, &reader_data);
399 get_variable_helper(&variable_list, new_var);
400 }
401 }
402
403 bool needs_sorting = false;
404 for (inst = c->Program.Instructions.Next;
405 inst != &c->Program.Instructions;
406 inst = inst->Next) {
407 if (inst->Type != RC_INSTRUCTION_NORMAL) {
408 needs_sorting = true;
409 get_variable_pair_helper(&variable_list, c, inst,
410 &inst->U.P.RGB);
411 get_variable_pair_helper(&variable_list, c, inst,
412 &inst->U.P.Alpha);
413 }
414 }
415
416 if (variable_list && needs_sorting) {
417 unsigned int count = rc_list_count(variable_list);
418 struct rc_variable **variables = memory_pool_malloc(&c->Pool,
419 sizeof(struct rc_variable *) * count);
420
421 struct rc_list * current = variable_list;
422 for(unsigned int i = 0; current; i++, current = current->Next) {
423 struct rc_variable * var = current->Item;
424 variables[i] = var;
425 }
426
427 qsort(variables, count, sizeof(struct rc_variable *), cmpfunc_variable_by_ip);
428
429 current = variable_list;
430 for(unsigned int i = 0; current; i++, current = current->Next) {
431 current->Item = variables[i];
432 }
433 }
434
435 return variable_list;
436 }
437
438 /**
439 * @return The bitwise or of the writemasks of a variable and all of its
440 * friends.
441 */
rc_variable_writemask_sum(struct rc_variable * var)442 unsigned int rc_variable_writemask_sum(struct rc_variable * var)
443 {
444 unsigned int writemask = 0;
445 while(var) {
446 writemask |= var->Dst.WriteMask;
447 var = var->Friend;
448 }
449 return writemask;
450 }
451
452 /*
453 * @return A list of readers for a variable and its friends. Readers
454 * that read from two different variable friends are only included once in
455 * this list.
456 */
rc_variable_readers_union(struct rc_variable * var)457 struct rc_list * rc_variable_readers_union(struct rc_variable * var)
458 {
459 struct rc_list * list = NULL;
460 while (var) {
461 unsigned int i;
462 for (i = 0; i < var->ReaderCount; i++) {
463 struct rc_list * temp;
464 struct rc_reader * a = &var->Readers[i];
465 unsigned int match = 0;
466 for (temp = list; temp; temp = temp->Next) {
467 struct rc_reader * b = temp->Item;
468 if (a->Inst->Type != b->Inst->Type) {
469 continue;
470 }
471 if (a->Inst->Type == RC_INSTRUCTION_NORMAL) {
472 if (a->U.I.Src == b->U.I.Src) {
473 match = 1;
474 break;
475 }
476 }
477 if (a->Inst->Type == RC_INSTRUCTION_PAIR) {
478 if (a->U.P.Arg == b->U.P.Arg
479 && a->U.P.Src == b->U.P.Src) {
480 match = 1;
481 break;
482 }
483 }
484 }
485 if (match) {
486 continue;
487 }
488 rc_list_add(&list, rc_list(&var->C->Pool, a));
489 }
490 var = var->Friend;
491 }
492 return list;
493 }
494
reader_equals_src(struct rc_reader reader,unsigned int src_type,void * src)495 static unsigned int reader_equals_src(
496 struct rc_reader reader,
497 unsigned int src_type,
498 void * src)
499 {
500 if (reader.Inst->Type != src_type) {
501 return 0;
502 }
503 if (src_type == RC_INSTRUCTION_NORMAL) {
504 return reader.U.I.Src == src;
505 } else {
506 return reader.U.P.Src == src;
507 }
508 }
509
variable_writes_src(struct rc_variable * var,unsigned int src_type,void * src)510 static unsigned int variable_writes_src(
511 struct rc_variable * var,
512 unsigned int src_type,
513 void * src)
514 {
515 unsigned int i;
516 for (i = 0; i < var->ReaderCount; i++) {
517 if (reader_equals_src(var->Readers[i], src_type, src)) {
518 return 1;
519 }
520 }
521 return 0;
522 }
523
524
rc_variable_list_get_writers(struct rc_list * var_list,unsigned int src_type,void * src)525 struct rc_list * rc_variable_list_get_writers(
526 struct rc_list * var_list,
527 unsigned int src_type,
528 void * src)
529 {
530 struct rc_list * list_ptr;
531 struct rc_list * writer_list = NULL;
532 for (list_ptr = var_list; list_ptr; list_ptr = list_ptr->Next) {
533 struct rc_variable * var = list_ptr->Item;
534 if (variable_writes_src(var, src_type, src)) {
535 struct rc_variable * friend;
536 rc_list_add(&writer_list, rc_list(&var->C->Pool, var));
537 for (friend = var->Friend; friend;
538 friend = friend->Friend) {
539 if (variable_writes_src(friend, src_type, src)) {
540 rc_list_add(&writer_list,
541 rc_list(&var->C->Pool, friend));
542 }
543 }
544 /* Once we have identified the variable and its
545 * friends that write this source, we can stop
546 * stop searching, because we know none of the
547 * other variables in the list will write this source.
548 * If they did they would be friends of var.
549 */
550 break;
551 }
552 }
553 return writer_list;
554 }
555
rc_variable_list_get_writers_one_reader(struct rc_list * var_list,unsigned int src_type,void * src)556 struct rc_list * rc_variable_list_get_writers_one_reader(
557 struct rc_list * var_list,
558 unsigned int src_type,
559 void * src)
560 {
561 struct rc_list * writer_list =
562 rc_variable_list_get_writers(var_list, src_type, src);
563 struct rc_list * reader_list =
564 rc_variable_readers_union(writer_list->Item);
565 if (rc_list_count(reader_list) > 1) {
566 return NULL;
567 } else {
568 return writer_list;
569 }
570 }
571
rc_variable_print(struct rc_variable * var)572 void rc_variable_print(struct rc_variable * var)
573 {
574 unsigned int i;
575 while (var) {
576 fprintf(stderr, "%u: TEMP[%u].%u: ",
577 var->Inst->IP, var->Dst.Index, var->Dst.WriteMask);
578 for (i = 0; i < 4; i++) {
579 fprintf(stderr, "chan %u: start=%u end=%u ", i,
580 var->Live[i].Start, var->Live[i].End);
581 }
582 fprintf(stderr, "%u readers\n", var->ReaderCount);
583 if (var->Friend) {
584 fprintf(stderr, "Friend: \n\t");
585 }
586 var = var->Friend;
587 }
588 }
589