xref: /aosp_15_r20/external/mesa3d/src/gallium/drivers/r300/compiler/radeon_variable.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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