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