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