1 /*
2  * Copyright 2011 Tom Stellard <tstellar@gmail.com>
3  *
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial
16  * portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
22  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  */
27 
28 #include <stdio.h>
29 #include "radeon_variable.h"
30 
31 #include "memory_pool.h"
32 #include "radeon_compiler_util.h"
33 #include "radeon_dataflow.h"
34 #include "radeon_list.h"
35 #include "radeon_opcodes.h"
36 #include "radeon_program.h"
37 
38 /**
39  * Rewrite the index and writemask for the destination register of var
40  * and its friends to new_index and new_writemask.  This function also takes
41  * care of rewriting the swizzles for the sources of var.
42  */
rc_variable_change_dst(struct rc_variable * var,unsigned int new_index,unsigned int new_writemask)43 void rc_variable_change_dst(
44 	struct rc_variable * var,
45 	unsigned int new_index,
46 	unsigned int new_writemask)
47 {
48 	struct rc_variable * var_ptr;
49 	struct rc_list * readers;
50 	unsigned int old_mask = rc_variable_writemask_sum(var);
51 	unsigned int conversion_swizzle =
52 			rc_make_conversion_swizzle(old_mask, new_writemask);
53 
54 	for (var_ptr = var; var_ptr; var_ptr = var_ptr->Friend) {
55 		if (var_ptr->Inst->Type == RC_INSTRUCTION_NORMAL) {
56 			rc_normal_rewrite_writemask(var_ptr->Inst,
57 							conversion_swizzle);
58 			var_ptr->Inst->U.I.DstReg.Index = new_index;
59 		} else {
60 			struct rc_pair_sub_instruction * sub;
61 			if (var_ptr->Dst.WriteMask == RC_MASK_W) {
62 				assert(new_writemask & RC_MASK_W);
63 				sub = &var_ptr->Inst->U.P.Alpha;
64 			} else {
65 				sub = &var_ptr->Inst->U.P.RGB;
66 				rc_pair_rewrite_writemask(sub,
67 							conversion_swizzle);
68 			}
69 			sub->DestIndex = new_index;
70 		}
71 	}
72 
73 	readers = rc_variable_readers_union(var);
74 
75 	for ( ; readers; readers = readers->Next) {
76 		struct rc_reader * reader = readers->Item;
77 		if (reader->Inst->Type == RC_INSTRUCTION_NORMAL) {
78 			reader->U.I.Src->Index = new_index;
79 			reader->U.I.Src->Swizzle = rc_rewrite_swizzle(
80 				reader->U.I.Src->Swizzle, conversion_swizzle);
81 		} else {
82 			struct rc_pair_instruction * pair_inst =
83 							&reader->Inst->U.P;
84 			unsigned int src_type = rc_source_type_swz(
85 							reader->U.P.Arg->Swizzle);
86 
87 			int src_index = reader->U.P.Arg->Source;
88 			if (src_index == RC_PAIR_PRESUB_SRC) {
89 				src_index = rc_pair_get_src_index(
90 						pair_inst, reader->U.P.Src);
91 			}
92 			/* Try to delete the old src, it is OK if this fails,
93 			 * because rc_pair_alloc_source might be able to
94 			 * find a source the ca be reused.
95 			 */
96 			if (rc_pair_remove_src(reader->Inst, src_type,
97 							src_index, old_mask)) {
98 				/* Reuse the source index of the source that
99 				 * was just deleted and set its register
100 				 * index.  We can't use rc_pair_alloc_source
101 				 * for this because it might return a source
102 				 * index that is already being used. */
103 				if (src_type & RC_SOURCE_RGB) {
104 					pair_inst->RGB.Src[src_index]
105 						.Used =	1;
106 					pair_inst->RGB.Src[src_index]
107 						.Index = new_index;
108 					pair_inst->RGB.Src[src_index]
109 						.File = RC_FILE_TEMPORARY;
110 				}
111 				if (src_type & RC_SOURCE_ALPHA) {
112 					pair_inst->Alpha.Src[src_index]
113 						.Used = 1;
114 					pair_inst->Alpha.Src[src_index]
115 						.Index = new_index;
116 					pair_inst->Alpha.Src[src_index]
117 						.File = RC_FILE_TEMPORARY;
118 				}
119 			} else {
120 				src_index = rc_pair_alloc_source(
121 						&reader->Inst->U.P,
122 						src_type & RC_SOURCE_RGB,
123 						src_type & RC_SOURCE_ALPHA,
124 						RC_FILE_TEMPORARY,
125 						new_index);
126 				if (src_index < 0) {
127 					rc_error(var->C, "Rewrite of inst %u failed "
128 						"Can't allocate source for "
129 						"Inst %u src_type=%x "
130 						"new_index=%u new_mask=%u\n",
131 						var->Inst->IP, reader->Inst->IP, src_type, new_index, new_writemask);
132 						continue;
133 				}
134 			}
135 			reader->U.P.Arg->Swizzle = rc_rewrite_swizzle(
136 				reader->U.P.Arg->Swizzle, conversion_swizzle);
137 			if (reader->U.P.Arg->Source != RC_PAIR_PRESUB_SRC) {
138 				reader->U.P.Arg->Source = src_index;
139 			}
140 		}
141 	}
142 }
143 
144 /**
145  * Compute the live intervals for var and its friends.
146  */
rc_variable_compute_live_intervals(struct rc_variable * var)147 void rc_variable_compute_live_intervals(struct rc_variable * var)
148 {
149 	while(var) {
150 		unsigned int i;
151 		unsigned int start = var->Inst->IP;
152 
153 		for (i = 0; i < var->ReaderCount; i++) {
154 			unsigned int chan;
155 			unsigned int chan_start = start;
156 			unsigned int chan_end = var->Readers[i].Inst->IP;
157 			unsigned int mask = var->Readers[i].WriteMask;
158 			struct rc_instruction * inst;
159 
160 			/* Extend the live interval of T0 to the start of the
161 			 * loop for sequences like:
162 			 * BGNLOOP
163 			 * read T0
164 			 * ...
165 			 * write T0
166 			 * ENDLOOP
167 			 */
168 			if (var->Readers[i].Inst->IP < start) {
169 				struct rc_instruction * bgnloop =
170 					rc_match_endloop(var->Readers[i].Inst);
171 				chan_start = bgnloop->IP;
172 			}
173 
174 			/* Extend the live interval of T0 to the start of the
175 			 * loop in case there is a BRK instruction in the loop
176 			 * (we don't actually check for a BRK instruction we
177 			 * assume there is one somewhere in the loop, which
178 			 * there usually is) for sequences like:
179 			 * BGNLOOP
180 			 * ...
181 			 * conditional BRK
182 			 * ...
183 			 * write T0
184 			 * ENDLOOP
185 			 * read T0
186 			 ***************************************************
187 			 * Extend the live interval of T0 to the end of the
188 			 * loop for sequences like:
189 			 * write T0
190 			 * BGNLOOP
191 			 * ...
192 			 * read T0
193 			 * ENDLOOP
194 			 */
195 			for (inst = var->Inst; inst != var->Readers[i].Inst;
196 							inst = inst->Next) {
197 				rc_opcode op = rc_get_flow_control_inst(inst);
198 				if (op == RC_OPCODE_ENDLOOP) {
199 					struct rc_instruction * bgnloop =
200 						rc_match_endloop(inst);
201 					if (bgnloop->IP < chan_start) {
202 						chan_start = bgnloop->IP;
203 					}
204 				} else if (op == RC_OPCODE_BGNLOOP) {
205 					struct rc_instruction * endloop =
206 						rc_match_bgnloop(inst);
207 					if (endloop->IP > chan_end) {
208 						chan_end = endloop->IP;
209 					}
210 				}
211 			}
212 
213 			for (chan = 0; chan < 4; chan++) {
214 				if ((mask >> chan) & 0x1) {
215 					if (!var->Live[chan].Used
216 					|| chan_start < var->Live[chan].Start) {
217 						var->Live[chan].Start =
218 								chan_start;
219 					}
220 					if (!var->Live[chan].Used
221 					|| chan_end > var->Live[chan].End) {
222 						var->Live[chan].End = chan_end;
223 					}
224 					var->Live[chan].Used = 1;
225 				}
226 			}
227 		}
228 		var = var->Friend;
229 	}
230 }
231 
232 /**
233  * @return 1 if a and b share a reader
234  * @return 0 if they do not
235  */
readers_intersect(struct rc_variable * a,struct rc_variable * b)236 static unsigned int readers_intersect(
237 	struct rc_variable * a,
238 	struct rc_variable * b)
239 {
240 	unsigned int a_index, b_index;
241 	for (a_index = 0; a_index < a->ReaderCount; a_index++) {
242 		struct rc_reader reader_a = a->Readers[a_index];
243 		for (b_index = 0; b_index < b->ReaderCount; b_index++) {
244 			struct rc_reader reader_b = b->Readers[b_index];
245 			if (reader_a.Inst->Type == RC_INSTRUCTION_NORMAL
246 				&& reader_b.Inst->Type == RC_INSTRUCTION_NORMAL
247 				&& reader_a.U.I.Src == reader_b.U.I.Src) {
248 
249 				return 1;
250 			}
251 			if (reader_a.Inst->Type == RC_INSTRUCTION_PAIR
252 				&& reader_b.Inst->Type == RC_INSTRUCTION_PAIR
253 				&& reader_a.U.P.Src == reader_b.U.P.Src) {
254 
255 				return 1;
256 			}
257 		}
258 	}
259 	return 0;
260 }
261 
rc_variable_add_friend(struct rc_variable * var,struct rc_variable * friend)262 void rc_variable_add_friend(
263 	struct rc_variable * var,
264 	struct rc_variable * friend)
265 {
266 	assert(var->Dst.Index == friend->Dst.Index);
267 	while(var->Friend) {
268 		var = var->Friend;
269 	}
270 	var->Friend = friend;
271 }
272 
rc_variable(struct radeon_compiler * c,unsigned int DstFile,unsigned int DstIndex,unsigned int DstWriteMask,struct rc_reader_data * reader_data)273 struct rc_variable * rc_variable(
274 	struct radeon_compiler * c,
275 	unsigned int DstFile,
276 	unsigned int DstIndex,
277 	unsigned int DstWriteMask,
278 	struct rc_reader_data * reader_data)
279 {
280 	struct rc_variable * new =
281 			memory_pool_malloc(&c->Pool, sizeof(struct rc_variable));
282 	memset(new, 0, sizeof(struct rc_variable));
283 	new->C = c;
284 	new->Dst.File = DstFile;
285 	new->Dst.Index = DstIndex;
286 	new->Dst.WriteMask = DstWriteMask;
287 	if (reader_data) {
288 		new->Inst = reader_data->Writer;
289 		new->ReaderCount = reader_data->ReaderCount;
290 		new->Readers = reader_data->Readers;
291 	}
292 	return new;
293 }
294 
get_variable_helper(struct rc_list ** variable_list,struct rc_variable * variable)295 static void get_variable_helper(
296 	struct rc_list ** variable_list,
297 	struct rc_variable * variable)
298 {
299 	struct rc_list * list_ptr;
300 	for (list_ptr = *variable_list; list_ptr; list_ptr = list_ptr->Next) {
301 		struct rc_variable * var;
302 		for (var = list_ptr->Item; var; var = var->Friend) {
303 			if (readers_intersect(var, variable)) {
304 				rc_variable_add_friend(var, variable);
305 				return;
306 			}
307 		}
308 	}
309 	rc_list_add(variable_list, rc_list(&variable->C->Pool, variable));
310 }
311 
get_variable_pair_helper(struct rc_list ** variable_list,struct radeon_compiler * c,struct rc_instruction * inst,struct rc_pair_sub_instruction * sub_inst)312 static void get_variable_pair_helper(
313 	struct rc_list ** variable_list,
314 	struct radeon_compiler * c,
315 	struct rc_instruction * inst,
316 	struct rc_pair_sub_instruction * sub_inst)
317 {
318 	struct rc_reader_data reader_data;
319 	struct rc_variable * new_var;
320 	rc_register_file file;
321 	unsigned int writemask;
322 
323 	if (sub_inst->Opcode == RC_OPCODE_NOP) {
324 		return;
325 	}
326 	memset(&reader_data, 0, sizeof(struct rc_reader_data));
327 	rc_get_readers_sub(c, inst, sub_inst, &reader_data, NULL, NULL, NULL);
328 
329 	if (reader_data.ReaderCount == 0) {
330 		return;
331 	}
332 
333 	if (sub_inst->WriteMask) {
334 		file = RC_FILE_TEMPORARY;
335 		writemask = sub_inst->WriteMask;
336 	} else if (sub_inst->OutputWriteMask) {
337 		file = RC_FILE_OUTPUT;
338 		writemask = sub_inst->OutputWriteMask;
339 	} else {
340 		writemask = 0;
341 		file = RC_FILE_NONE;
342 	}
343 	new_var = rc_variable(c, file, sub_inst->DestIndex, writemask,
344 								&reader_data);
345 	get_variable_helper(variable_list, new_var);
346 }
347 
348 /**
349  * Generate a list of variables used by the shader program.  Each instruction
350  * that writes to a register is considered a variable.  The struct rc_variable
351  * data structure includes a list of readers and is essentially a
352  * definition-use chain.  Any two variables that share a reader are considered
353  * "friends" and they are linked together via the Friend attribute.
354  */
rc_get_variables(struct radeon_compiler * c)355 struct rc_list * rc_get_variables(struct radeon_compiler * c)
356 {
357 	struct rc_instruction * inst;
358 	struct rc_list * variable_list = NULL;
359 
360 	for (inst = c->Program.Instructions.Next;
361 					inst != &c->Program.Instructions;
362 					inst = inst->Next) {
363 		struct rc_reader_data reader_data;
364 		struct rc_variable * new_var;
365 		memset(&reader_data, 0, sizeof(reader_data));
366 
367 		if (inst->Type == RC_INSTRUCTION_NORMAL) {
368 			rc_get_readers(c, inst, &reader_data, NULL, NULL, NULL);
369 			if (reader_data.ReaderCount == 0) {
370 				continue;
371 			}
372 			new_var = rc_variable(c, inst->U.I.DstReg.File,
373 				inst->U.I.DstReg.Index,
374 				inst->U.I.DstReg.WriteMask, &reader_data);
375 			get_variable_helper(&variable_list, new_var);
376 		} else {
377 			get_variable_pair_helper(&variable_list, c, inst,
378 							&inst->U.P.RGB);
379 			get_variable_pair_helper(&variable_list, c, inst,
380 							&inst->U.P.Alpha);
381 		}
382 	}
383 
384 	return variable_list;
385 }
386 
387 /**
388  * @return The bitwise or of the writemasks of a variable and all of its
389  * friends.
390  */
rc_variable_writemask_sum(struct rc_variable * var)391 unsigned int rc_variable_writemask_sum(struct rc_variable * var)
392 {
393 	unsigned int writemask = 0;
394 	while(var) {
395 		writemask |= var->Dst.WriteMask;
396 		var = var->Friend;
397 	}
398 	return writemask;
399 }
400 
401 /*
402  * @return A list of readers for a variable and its friends.  Readers
403  * that read from two different variable friends are only included once in
404  * this list.
405  */
rc_variable_readers_union(struct rc_variable * var)406 struct rc_list * rc_variable_readers_union(struct rc_variable * var)
407 {
408 	struct rc_list * list = NULL;
409 	while (var) {
410 		unsigned int i;
411 		for (i = 0; i < var->ReaderCount; i++) {
412 			struct rc_list * temp;
413 			struct rc_reader * a = &var->Readers[i];
414 			unsigned int match = 0;
415 			for (temp = list; temp; temp = temp->Next) {
416 				struct rc_reader * b = temp->Item;
417 				if (a->Inst->Type != b->Inst->Type) {
418 					continue;
419 				}
420 				if (a->Inst->Type == RC_INSTRUCTION_NORMAL) {
421 					if (a->U.I.Src == b->U.I.Src) {
422 						match = 1;
423 						break;
424 					}
425 				}
426 				if (a->Inst->Type == RC_INSTRUCTION_PAIR) {
427 					if (a->U.P.Arg == b->U.P.Arg
428 					    && a->U.P.Src == b->U.P.Src) {
429 						match = 1;
430 						break;
431 					}
432 				}
433 			}
434 			if (match) {
435 				continue;
436 			}
437 			rc_list_add(&list, rc_list(&var->C->Pool, a));
438 		}
439 		var = var->Friend;
440 	}
441 	return list;
442 }
443 
reader_equals_src(struct rc_reader reader,unsigned int src_type,void * src)444 static unsigned int reader_equals_src(
445 	struct rc_reader reader,
446 	unsigned int src_type,
447 	void * src)
448 {
449 	if (reader.Inst->Type != src_type) {
450 		return 0;
451 	}
452 	if (src_type == RC_INSTRUCTION_NORMAL) {
453 		return reader.U.I.Src == src;
454 	} else {
455 		return reader.U.P.Src == src;
456 	}
457 }
458 
variable_writes_src(struct rc_variable * var,unsigned int src_type,void * src)459 static unsigned int variable_writes_src(
460 	struct rc_variable * var,
461 	unsigned int src_type,
462 	void * src)
463 {
464 	unsigned int i;
465 	for (i = 0; i < var->ReaderCount; i++) {
466 		if (reader_equals_src(var->Readers[i], src_type, src)) {
467 			return 1;
468 		}
469 	}
470 	return 0;
471 }
472 
473 
rc_variable_list_get_writers(struct rc_list * var_list,unsigned int src_type,void * src)474 struct rc_list * rc_variable_list_get_writers(
475 	struct rc_list * var_list,
476 	unsigned int src_type,
477 	void * src)
478 {
479 	struct rc_list * list_ptr;
480 	struct rc_list * writer_list = NULL;
481 	for (list_ptr = var_list; list_ptr; list_ptr = list_ptr->Next) {
482 		struct rc_variable * var = list_ptr->Item;
483 		if (variable_writes_src(var, src_type, src)) {
484 			struct rc_variable * friend;
485 			rc_list_add(&writer_list, rc_list(&var->C->Pool, var));
486 			for (friend = var->Friend; friend;
487 						friend = friend->Friend) {
488 				if (variable_writes_src(friend, src_type, src)) {
489 					rc_list_add(&writer_list,
490 						rc_list(&var->C->Pool, friend));
491 				}
492 			}
493 			/* Once we have identified the variable and its
494 			 * friends that write this source, we can stop
495 			 * stop searching, because we know none of the
496 			 * other variables in the list will write this source.
497 			 * If they did they would be friends of var.
498 			 */
499 			break;
500 		}
501 	}
502 	return writer_list;
503 }
504 
rc_variable_list_get_writers_one_reader(struct rc_list * var_list,unsigned int src_type,void * src)505 struct rc_list * rc_variable_list_get_writers_one_reader(
506 	struct rc_list * var_list,
507 	unsigned int src_type,
508 	void * src)
509 {
510 	struct rc_list * writer_list =
511 		rc_variable_list_get_writers(var_list, src_type, src);
512 	struct rc_list * reader_list =
513 		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 
rc_variable_print(struct rc_variable * var)521 void rc_variable_print(struct rc_variable * var)
522 {
523 	unsigned int i;
524 	while (var) {
525 		fprintf(stderr, "%u: TEMP[%u].%u: ",
526 			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,
529 					var->Live[i].Start, var->Live[i].End);
530 		}
531 		fprintf(stderr, "%u readers\n", var->ReaderCount);
532 		if (var->Friend) {
533 			fprintf(stderr, "Friend: \n\t");
534 		}
535 		var = var->Friend;
536 	}
537 }
538