blob: e627715b5a6c06b03fb17df16d45a448a6ac86f7 [file] [log] [blame]
Brian Paul82f1c0b2009-03-06 16:18:22 -07001/*
2 * Mesa 3-D graphics library
3 * Version: 7.5
4 *
5 * Copyright (C) 2009 VMware, Inc. All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * VMWARE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26
27#include "main/glheader.h"
28#include "main/context.h"
29#include "main/macros.h"
30#include "program.h"
31#include "prog_instruction.h"
32#include "prog_optimize.h"
33#include "prog_print.h"
34
35
Brian Paul12256fc2009-03-20 17:08:30 -060036#define MAX_LOOP_NESTING 50
37
38
Brian Paul82f1c0b2009-03-06 16:18:22 -070039static GLboolean dbg = GL_FALSE;
40
41
42/**
43 * In 'prog' remove instruction[i] if removeFlags[i] == TRUE.
44 * \return number of instructions removed
45 */
46static GLuint
47remove_instructions(struct gl_program *prog, const GLboolean *removeFlags)
48{
49 GLint i, removeEnd = 0, removeCount = 0;
50 GLuint totalRemoved = 0;
51
52 /* go backward */
53 for (i = prog->NumInstructions - 1; i >= 0; i--) {
54 if (removeFlags[i]) {
55 totalRemoved++;
56 if (removeCount == 0) {
57 /* begin a run of instructions to remove */
58 removeEnd = i;
59 removeCount = 1;
60 }
61 else {
62 /* extend the run of instructions to remove */
63 removeCount++;
64 }
65 }
66 else {
67 /* don't remove this instruction, but check if the preceeding
68 * instructions are to be removed.
69 */
70 if (removeCount > 0) {
71 GLint removeStart = removeEnd - removeCount + 1;
72 _mesa_delete_instructions(prog, removeStart, removeCount);
73 removeStart = removeCount = 0; /* reset removal info */
74 }
75 }
76 }
77 return totalRemoved;
78}
79
80
81/**
Brian Paul12256fc2009-03-20 17:08:30 -060082 * Remap register indexes according to map.
83 * \param prog the program to search/replace
84 * \param file the type of register file to search/replace
85 * \param map maps old register indexes to new indexes
86 */
87static void
88replace_regs(struct gl_program *prog, gl_register_file file, const GLint map[])
89{
90 GLuint i;
91
92 for (i = 0; i < prog->NumInstructions; i++) {
93 struct prog_instruction *inst = prog->Instructions + i;
94 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
95 GLuint j;
96 for (j = 0; j < numSrc; j++) {
97 if (inst->SrcReg[j].File == file) {
98 GLuint index = inst->SrcReg[j].Index;
99 ASSERT(map[index] >= 0);
100 inst->SrcReg[j].Index = map[index];
101 }
102 }
103 if (inst->DstReg.File == file) {
104 const GLuint index = inst->DstReg.Index;
105 ASSERT(map[index] >= 0);
106 inst->DstReg.Index = map[index];
107 }
108 }
109}
110
111
112/**
Brian Paul82f1c0b2009-03-06 16:18:22 -0700113 * Consolidate temporary registers to use low numbers. For example, if the
114 * shader only uses temps 4, 5, 8, replace them with 0, 1, 2.
115 */
116static void
117_mesa_consolidate_registers(struct gl_program *prog)
118{
119 GLboolean tempUsed[MAX_PROGRAM_TEMPS];
Brian Paul12256fc2009-03-20 17:08:30 -0600120 GLint tempMap[MAX_PROGRAM_TEMPS];
Brian Paul82f1c0b2009-03-06 16:18:22 -0700121 GLuint tempMax = 0, i;
122
123 if (dbg) {
124 _mesa_printf("Optimize: Begin register consolidation\n");
125 }
126
127 memset(tempUsed, 0, sizeof(tempUsed));
128
Brian Paul12256fc2009-03-20 17:08:30 -0600129 for (i = 0; i < MAX_PROGRAM_TEMPS; i++) {
130 tempMap[i] = -1;
131 }
132
Brian Paul82f1c0b2009-03-06 16:18:22 -0700133 /* set tempUsed[i] if temporary [i] is referenced */
134 for (i = 0; i < prog->NumInstructions; i++) {
135 const struct prog_instruction *inst = prog->Instructions + i;
136 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
137 GLuint j;
138 for (j = 0; j < numSrc; j++) {
139 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) {
140 const GLuint index = inst->SrcReg[j].Index;
141 ASSERT(index < MAX_PROGRAM_TEMPS);
142 tempUsed[index] = GL_TRUE;
143 tempMax = MAX2(tempMax, index);
144 break;
145 }
146 }
147 if (inst->DstReg.File == PROGRAM_TEMPORARY) {
148 const GLuint index = inst->DstReg.Index;
149 ASSERT(index < MAX_PROGRAM_TEMPS);
150 tempUsed[index] = GL_TRUE;
151 tempMax = MAX2(tempMax, index);
152 }
153 }
154
155 /* allocate a new index for each temp that's used */
156 {
157 GLuint freeTemp = 0;
158 for (i = 0; i <= tempMax; i++) {
159 if (tempUsed[i]) {
160 tempMap[i] = freeTemp++;
161 /*_mesa_printf("replace %u with %u\n", i, tempMap[i]);*/
162 }
163 }
164 if (freeTemp == tempMax + 1) {
165 /* no consolidation possible */
166 return;
167 }
168 if (dbg) {
169 _mesa_printf("Replace regs 0..%u with 0..%u\n", tempMax, freeTemp-1);
170 }
171 }
172
Brian Paul12256fc2009-03-20 17:08:30 -0600173 replace_regs(prog, PROGRAM_TEMPORARY, tempMap);
174
Brian Paul82f1c0b2009-03-06 16:18:22 -0700175 if (dbg) {
176 _mesa_printf("Optimize: End register consolidation\n");
177 }
178}
179
180
181/**
182 * Remove dead instructions from the given program.
183 * This is very primitive for now. Basically look for temp registers
184 * that are written to but never read. Remove any instructions that
185 * write to such registers. Be careful with condition code setters.
186 */
187static void
188_mesa_remove_dead_code(struct gl_program *prog)
189{
Eric Anholtee0a9e62009-06-12 12:37:25 -0700190 GLboolean tempRead[MAX_PROGRAM_TEMPS][4];
Brian Paul82f1c0b2009-03-06 16:18:22 -0700191 GLboolean *removeInst; /* per-instruction removal flag */
Eric Anholtee0a9e62009-06-12 12:37:25 -0700192 GLuint i, rem = 0, comp;
Brian Paul82f1c0b2009-03-06 16:18:22 -0700193
Brian Paul82f1c0b2009-03-06 16:18:22 -0700194 memset(tempRead, 0, sizeof(tempRead));
195
196 if (dbg) {
197 _mesa_printf("Optimize: Begin dead code removal\n");
198 /*_mesa_print_program(prog);*/
199 }
200
201 removeInst = (GLboolean *)
202 _mesa_calloc(prog->NumInstructions * sizeof(GLboolean));
203
204 /* Determine which temps are read and written */
205 for (i = 0; i < prog->NumInstructions; i++) {
206 const struct prog_instruction *inst = prog->Instructions + i;
207 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
208 GLuint j;
209
210 /* check src regs */
211 for (j = 0; j < numSrc; j++) {
212 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) {
213 const GLuint index = inst->SrcReg[j].Index;
214 ASSERT(index < MAX_PROGRAM_TEMPS);
215
216 if (inst->SrcReg[j].RelAddr) {
217 if (dbg)
218 _mesa_printf("abort remove dead code (indirect temp)\n");
Eric Anholtee0a9e62009-06-12 12:37:25 -0700219 goto done;
Brian Paul82f1c0b2009-03-06 16:18:22 -0700220 }
221
Eric Anholtee0a9e62009-06-12 12:37:25 -0700222 for (comp = 0; comp < 4; comp++) {
223 GLuint swz = (inst->SrcReg[j].Swizzle >> (3 * comp)) & 0x7;
224
225 switch (swz) {
226 case SWIZZLE_X:
227 tempRead[index][0] = GL_TRUE;
228 break;
229 case SWIZZLE_Y:
230 tempRead[index][1] = GL_TRUE;
231 break;
232 case SWIZZLE_Z:
233 tempRead[index][2] = GL_TRUE;
234 break;
235 case SWIZZLE_W:
236 tempRead[index][3] = GL_TRUE;
237 break;
238 }
239 }
Brian Paul82f1c0b2009-03-06 16:18:22 -0700240 }
241 }
242
243 /* check dst reg */
244 if (inst->DstReg.File == PROGRAM_TEMPORARY) {
245 const GLuint index = inst->DstReg.Index;
246 ASSERT(index < MAX_PROGRAM_TEMPS);
247
248 if (inst->DstReg.RelAddr) {
249 if (dbg)
250 _mesa_printf("abort remove dead code (indirect temp)\n");
Eric Anholtee0a9e62009-06-12 12:37:25 -0700251 goto done;
Brian Paul82f1c0b2009-03-06 16:18:22 -0700252 }
253
Brian Paul82f1c0b2009-03-06 16:18:22 -0700254 if (inst->CondUpdate) {
255 /* If we're writing to this register and setting condition
256 * codes we cannot remove the instruction. Prevent removal
257 * by setting the 'read' flag.
258 */
Eric Anholtee0a9e62009-06-12 12:37:25 -0700259 tempRead[index][0] = GL_TRUE;
260 tempRead[index][1] = GL_TRUE;
261 tempRead[index][2] = GL_TRUE;
262 tempRead[index][3] = GL_TRUE;
Brian Paul82f1c0b2009-03-06 16:18:22 -0700263 }
264 }
265 }
266
Brian Paul82f1c0b2009-03-06 16:18:22 -0700267 /* find instructions that write to dead registers, flag for removal */
268 for (i = 0; i < prog->NumInstructions; i++) {
Eric Anholtee0a9e62009-06-12 12:37:25 -0700269 struct prog_instruction *inst = prog->Instructions + i;
270 const GLuint numDst = _mesa_num_inst_dst_regs(inst->Opcode);
271
272 if (numDst != 0 && inst->DstReg.File == PROGRAM_TEMPORARY) {
273 GLint chan, index = inst->DstReg.Index;
274
275 for (chan = 0; chan < 4; chan++) {
276 if (!tempRead[index][chan] &&
277 inst->DstReg.WriteMask & (1 << chan)) {
278 if (dbg) {
279 _mesa_printf("Remove writemask on %u.%c\n", i,
280 chan == 3 ? 'w' : 'x' + chan);
281 }
282 inst->DstReg.WriteMask &= ~(1 << chan);
283 rem++;
284 }
285 }
286
287 if (inst->DstReg.WriteMask == 0) {
288 /* If we cleared all writes, the instruction can be removed. */
289 if (dbg)
290 _mesa_printf("Remove instruction %u: \n", i);
291 removeInst[i] = GL_TRUE;
292 }
Brian Paul82f1c0b2009-03-06 16:18:22 -0700293 }
294 }
295
296 /* now remove the instructions which aren't needed */
297 rem = remove_instructions(prog, removeInst);
298
Brian Paul82f1c0b2009-03-06 16:18:22 -0700299 if (dbg) {
Eric Anholtee0a9e62009-06-12 12:37:25 -0700300 _mesa_printf("Optimize: End dead code removal.\n");
301 _mesa_printf(" %u channel writes removed\n", rem);
302 _mesa_printf(" %u instructions removed\n", rem);
Brian Paul82f1c0b2009-03-06 16:18:22 -0700303 /*_mesa_print_program(prog);*/
304 }
Eric Anholtee0a9e62009-06-12 12:37:25 -0700305
306done:
307 _mesa_free(removeInst);
Brian Paul82f1c0b2009-03-06 16:18:22 -0700308}
309
310
311enum temp_use
312{
313 READ,
314 WRITE,
315 FLOW,
316 END
317};
318
319/**
320 * Scan forward in program from 'start' for the next occurance of TEMP[index].
321 * Return READ, WRITE, FLOW or END to indicate the next usage or an indicator
322 * that we can't look further.
323 */
324static enum temp_use
325find_next_temp_use(const struct gl_program *prog, GLuint start, GLuint index)
326{
327 GLuint i;
328
329 for (i = start; i < prog->NumInstructions; i++) {
330 const struct prog_instruction *inst = prog->Instructions + i;
331 switch (inst->Opcode) {
332 case OPCODE_BGNLOOP:
333 case OPCODE_ENDLOOP:
334 case OPCODE_BGNSUB:
335 case OPCODE_ENDSUB:
336 return FLOW;
337 default:
338 {
339 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
340 GLuint j;
341 for (j = 0; j < numSrc; j++) {
342 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY &&
343 inst->SrcReg[j].Index == index)
344 return READ;
345 }
346 if (inst->DstReg.File == PROGRAM_TEMPORARY &&
347 inst->DstReg.Index == index)
348 return WRITE;
349 }
350 }
351 }
352
353 return END;
354}
355
356
357/**
358 * Try to remove extraneous MOV instructions from the given program.
359 */
360static void
361_mesa_remove_extra_moves(struct gl_program *prog)
362{
363 GLboolean *removeInst; /* per-instruction removal flag */
364 GLuint i, rem, loopNesting = 0, subroutineNesting = 0;
365
366 if (dbg) {
367 _mesa_printf("Optimize: Begin remove extra moves\n");
368 _mesa_print_program(prog);
369 }
370
371 removeInst = (GLboolean *)
372 _mesa_calloc(prog->NumInstructions * sizeof(GLboolean));
373
374 /*
375 * Look for sequences such as this:
376 * FOO tmpX, arg0, arg1;
377 * MOV tmpY, tmpX;
378 * and convert into:
379 * FOO tmpY, arg0, arg1;
380 */
381
382 for (i = 0; i < prog->NumInstructions; i++) {
383 const struct prog_instruction *inst = prog->Instructions + i;
384
385 switch (inst->Opcode) {
386 case OPCODE_BGNLOOP:
387 loopNesting++;
388 break;
389 case OPCODE_ENDLOOP:
390 loopNesting--;
391 break;
392 case OPCODE_BGNSUB:
393 subroutineNesting++;
394 break;
395 case OPCODE_ENDSUB:
396 subroutineNesting--;
397 break;
398 case OPCODE_MOV:
399 if (i > 0 &&
400 loopNesting == 0 &&
401 subroutineNesting == 0 &&
402 inst->SrcReg[0].File == PROGRAM_TEMPORARY &&
403 inst->SrcReg[0].Swizzle == SWIZZLE_XYZW) {
404 /* see if this MOV can be removed */
405 const GLuint tempIndex = inst->SrcReg[0].Index;
406 struct prog_instruction *prevInst;
407 GLuint prevI;
408
409 /* get pointer to previous instruction */
410 prevI = i - 1;
411 while (removeInst[prevI] && prevI > 0)
412 prevI--;
413 prevInst = prog->Instructions + prevI;
414
415 if (prevInst->DstReg.File == PROGRAM_TEMPORARY &&
416 prevInst->DstReg.Index == tempIndex &&
417 prevInst->DstReg.WriteMask == WRITEMASK_XYZW) {
418
419 enum temp_use next_use =
420 find_next_temp_use(prog, i + 1, tempIndex);
421
422 if (next_use == WRITE || next_use == END) {
423 /* OK, we can safely remove this MOV instruction.
424 * Transform:
425 * prevI: FOO tempIndex, x, y;
426 * i: MOV z, tempIndex;
427 * Into:
428 * prevI: FOO z, x, y;
429 */
430
431 /* patch up prev inst */
432 prevInst->DstReg.File = inst->DstReg.File;
433 prevInst->DstReg.Index = inst->DstReg.Index;
434
435 /* flag this instruction for removal */
436 removeInst[i] = GL_TRUE;
437
438 if (dbg) {
439 _mesa_printf("Remove MOV at %u\n", i);
440 _mesa_printf("new prev inst %u: ", prevI);
441 _mesa_print_instruction(prevInst);
442 }
443 }
444 }
445 }
446 break;
447 default:
448 ; /* nothing */
449 }
450 }
451
452 /* now remove the instructions which aren't needed */
453 rem = remove_instructions(prog, removeInst);
454
Brian Paul05749542009-10-01 14:52:28 -0600455 _mesa_free(removeInst);
456
Brian Paul82f1c0b2009-03-06 16:18:22 -0700457 if (dbg) {
458 _mesa_printf("Optimize: End remove extra moves. %u instructions removed\n", rem);
459 /*_mesa_print_program(prog);*/
460 }
461}
462
463
Brian Paul12256fc2009-03-20 17:08:30 -0600464/** A live register interval */
465struct interval
466{
467 GLuint Reg; /** The temporary register index */
468 GLuint Start, End; /** Start/end instruction numbers */
469};
470
471
472/** A list of register intervals */
473struct interval_list
474{
475 GLuint Num;
476 struct interval Intervals[MAX_PROGRAM_TEMPS];
477};
478
479
480static void
481append_interval(struct interval_list *list, const struct interval *inv)
482{
483 list->Intervals[list->Num++] = *inv;
484}
485
486
487/** Insert interval inv into list, sorted by interval end */
488static void
489insert_interval_by_end(struct interval_list *list, const struct interval *inv)
490{
491 /* XXX we could do a binary search insertion here since list is sorted */
492 GLint i = list->Num - 1;
493 while (i >= 0 && list->Intervals[i].End > inv->End) {
494 list->Intervals[i + 1] = list->Intervals[i];
495 i--;
496 }
497 list->Intervals[i + 1] = *inv;
498 list->Num++;
499
500#ifdef DEBUG
501 {
502 GLuint i;
503 for (i = 0; i + 1 < list->Num; i++) {
504 ASSERT(list->Intervals[i].End <= list->Intervals[i + 1].End);
505 }
506 }
507#endif
508}
509
510
511/** Remove the given interval from the interval list */
512static void
513remove_interval(struct interval_list *list, const struct interval *inv)
514{
515 /* XXX we could binary search since list is sorted */
516 GLuint k;
517 for (k = 0; k < list->Num; k++) {
518 if (list->Intervals[k].Reg == inv->Reg) {
519 /* found, remove it */
520 ASSERT(list->Intervals[k].Start == inv->Start);
521 ASSERT(list->Intervals[k].End == inv->End);
522 while (k < list->Num - 1) {
523 list->Intervals[k] = list->Intervals[k + 1];
524 k++;
525 }
526 list->Num--;
527 return;
528 }
529 }
530}
531
532
533/** called by qsort() */
534static int
535compare_start(const void *a, const void *b)
536{
537 const struct interval *ia = (const struct interval *) a;
538 const struct interval *ib = (const struct interval *) b;
539 if (ia->Start < ib->Start)
540 return -1;
541 else if (ia->Start > ib->Start)
542 return +1;
543 else
544 return 0;
545}
546
547/** sort the interval list according to interval starts */
548static void
549sort_interval_list_by_start(struct interval_list *list)
550{
551 qsort(list->Intervals, list->Num, sizeof(struct interval), compare_start);
552#ifdef DEBUG
553 {
554 GLuint i;
555 for (i = 0; i + 1 < list->Num; i++) {
556 ASSERT(list->Intervals[i].Start <= list->Intervals[i + 1].Start);
557 }
558 }
559#endif
560}
561
562
563/**
564 * Update the intermediate interval info for register 'index' and
565 * instruction 'ic'.
566 */
567static void
568update_interval(GLint intBegin[], GLint intEnd[], GLuint index, GLuint ic)
569{
570 ASSERT(index < MAX_PROGRAM_TEMPS);
571 if (intBegin[index] == -1) {
572 ASSERT(intEnd[index] == -1);
573 intBegin[index] = intEnd[index] = ic;
574 }
575 else {
576 intEnd[index] = ic;
577 }
578}
579
580
581/**
Brian Paul7da3f942009-04-24 16:28:36 -0600582 * Find first/last instruction that references each temporary register.
Brian Paul12256fc2009-03-20 17:08:30 -0600583 */
Brian Paul7da3f942009-04-24 16:28:36 -0600584GLboolean
585_mesa_find_temp_intervals(const struct prog_instruction *instructions,
586 GLuint numInstructions,
587 GLint intBegin[MAX_PROGRAM_TEMPS],
588 GLint intEnd[MAX_PROGRAM_TEMPS])
Brian Paul12256fc2009-03-20 17:08:30 -0600589{
590 struct loop_info
591 {
592 GLuint Start, End; /**< Start, end instructions of loop */
593 };
594 struct loop_info loopStack[MAX_LOOP_NESTING];
595 GLuint loopStackDepth = 0;
Brian Paul12256fc2009-03-20 17:08:30 -0600596 GLuint i;
597
Brian Paul12256fc2009-03-20 17:08:30 -0600598 for (i = 0; i < MAX_PROGRAM_TEMPS; i++){
599 intBegin[i] = intEnd[i] = -1;
600 }
601
602 /* Scan instructions looking for temporary registers */
Brian Paul7da3f942009-04-24 16:28:36 -0600603 for (i = 0; i < numInstructions; i++) {
604 const struct prog_instruction *inst = instructions + i;
Brian Paul12256fc2009-03-20 17:08:30 -0600605 if (inst->Opcode == OPCODE_BGNLOOP) {
606 loopStack[loopStackDepth].Start = i;
607 loopStack[loopStackDepth].End = inst->BranchTarget;
608 loopStackDepth++;
609 }
610 else if (inst->Opcode == OPCODE_ENDLOOP) {
611 loopStackDepth--;
612 }
613 else if (inst->Opcode == OPCODE_CAL) {
614 return GL_FALSE;
615 }
616 else {
Brian Paul7da3f942009-04-24 16:28:36 -0600617 const GLuint numSrc = 3;/*_mesa_num_inst_src_regs(inst->Opcode);*/
Brian Paul12256fc2009-03-20 17:08:30 -0600618 GLuint j;
619 for (j = 0; j < numSrc; j++) {
620 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) {
621 const GLuint index = inst->SrcReg[j].Index;
622 if (inst->SrcReg[j].RelAddr)
623 return GL_FALSE;
624 update_interval(intBegin, intEnd, index, i);
625 if (loopStackDepth > 0) {
626 /* extend temp register's interval to end of loop */
627 GLuint loopEnd = loopStack[loopStackDepth - 1].End;
628 update_interval(intBegin, intEnd, index, loopEnd);
629 }
630 }
631 }
632 if (inst->DstReg.File == PROGRAM_TEMPORARY) {
633 const GLuint index = inst->DstReg.Index;
634 if (inst->DstReg.RelAddr)
635 return GL_FALSE;
636 update_interval(intBegin, intEnd, index, i);
637 if (loopStackDepth > 0) {
638 /* extend temp register's interval to end of loop */
639 GLuint loopEnd = loopStack[loopStackDepth - 1].End;
640 update_interval(intBegin, intEnd, index, loopEnd);
641 }
642 }
643 }
644 }
645
Brian Paul7da3f942009-04-24 16:28:36 -0600646 return GL_TRUE;
647}
648
649
650/**
651 * Find the live intervals for each temporary register in the program.
652 * For register R, the interval [A,B] indicates that R is referenced
653 * from instruction A through instruction B.
654 * Special consideration is needed for loops and subroutines.
655 * \return GL_TRUE if success, GL_FALSE if we cannot proceed for some reason
656 */
657static GLboolean
658find_live_intervals(struct gl_program *prog,
659 struct interval_list *liveIntervals)
660{
661 GLint intBegin[MAX_PROGRAM_TEMPS], intEnd[MAX_PROGRAM_TEMPS];
662 GLuint i;
663
664 /*
665 * Note: we'll return GL_FALSE below if we find relative indexing
666 * into the TEMP register file. We can't handle that yet.
667 * We also give up on subroutines for now.
668 */
669
670 if (dbg) {
671 _mesa_printf("Optimize: Begin find intervals\n");
672 }
673
674 /* build intermediate arrays */
675 if (!_mesa_find_temp_intervals(prog->Instructions, prog->NumInstructions,
676 intBegin, intEnd))
677 return GL_FALSE;
678
Brian Paul12256fc2009-03-20 17:08:30 -0600679 /* Build live intervals list from intermediate arrays */
680 liveIntervals->Num = 0;
681 for (i = 0; i < MAX_PROGRAM_TEMPS; i++) {
682 if (intBegin[i] >= 0) {
683 struct interval inv;
684 inv.Reg = i;
685 inv.Start = intBegin[i];
686 inv.End = intEnd[i];
687 append_interval(liveIntervals, &inv);
688 }
689 }
690
691 /* Sort the list according to interval starts */
692 sort_interval_list_by_start(liveIntervals);
693
694 if (dbg) {
695 /* print interval info */
696 for (i = 0; i < liveIntervals->Num; i++) {
697 const struct interval *inv = liveIntervals->Intervals + i;
698 _mesa_printf("Reg[%d] live [%d, %d]:",
699 inv->Reg, inv->Start, inv->End);
700 if (1) {
701 int j;
702 for (j = 0; j < inv->Start; j++)
703 _mesa_printf(" ");
704 for (j = inv->Start; j <= inv->End; j++)
705 _mesa_printf("x");
706 }
707 _mesa_printf("\n");
708 }
709 }
710
711 return GL_TRUE;
712}
713
714
Brian Paulf4468382009-04-07 11:15:27 -0600715/** Scan the array of used register flags to find free entry */
716static GLint
Brian Paul12256fc2009-03-20 17:08:30 -0600717alloc_register(GLboolean usedRegs[MAX_PROGRAM_TEMPS])
718{
719 GLuint k;
720 for (k = 0; k < MAX_PROGRAM_TEMPS; k++) {
721 if (!usedRegs[k]) {
722 usedRegs[k] = GL_TRUE;
723 return k;
724 }
725 }
Brian Paulf4468382009-04-07 11:15:27 -0600726 return -1;
Brian Paul12256fc2009-03-20 17:08:30 -0600727}
728
729
730/**
731 * This function implements "Linear Scan Register Allocation" to reduce
732 * the number of temporary registers used by the program.
733 *
734 * We compute the "live interval" for all temporary registers then
735 * examine the overlap of the intervals to allocate new registers.
736 * Basically, if two intervals do not overlap, they can use the same register.
737 */
738static void
739_mesa_reallocate_registers(struct gl_program *prog)
740{
741 struct interval_list liveIntervals;
742 GLint registerMap[MAX_PROGRAM_TEMPS];
743 GLboolean usedRegs[MAX_PROGRAM_TEMPS];
744 GLuint i;
Brian Paulf4468382009-04-07 11:15:27 -0600745 GLint maxTemp = -1;
Brian Paul12256fc2009-03-20 17:08:30 -0600746
747 if (dbg) {
748 _mesa_printf("Optimize: Begin live-interval register reallocation\n");
749 _mesa_print_program(prog);
750 }
751
752 for (i = 0; i < MAX_PROGRAM_TEMPS; i++){
753 registerMap[i] = -1;
754 usedRegs[i] = GL_FALSE;
755 }
756
757 if (!find_live_intervals(prog, &liveIntervals)) {
758 if (dbg)
759 _mesa_printf("Aborting register reallocation\n");
760 return;
761 }
762
763 {
764 struct interval_list activeIntervals;
765 activeIntervals.Num = 0;
766
767 /* loop over live intervals, allocating a new register for each */
768 for (i = 0; i < liveIntervals.Num; i++) {
769 const struct interval *live = liveIntervals.Intervals + i;
770
771 if (dbg)
772 _mesa_printf("Consider register %u\n", live->Reg);
773
774 /* Expire old intervals. Intervals which have ended with respect
775 * to the live interval can have their remapped registers freed.
776 */
777 {
778 GLint j;
779 for (j = 0; j < activeIntervals.Num; j++) {
780 const struct interval *inv = activeIntervals.Intervals + j;
781 if (inv->End >= live->Start) {
782 /* Stop now. Since the activeInterval list is sorted
783 * we know we don't have to go further.
784 */
785 break;
786 }
787 else {
788 /* Interval 'inv' has expired */
789 const GLint regNew = registerMap[inv->Reg];
790 ASSERT(regNew >= 0);
791
792 if (dbg)
793 _mesa_printf(" expire interval for reg %u\n", inv->Reg);
794
795 /* remove interval j from active list */
796 remove_interval(&activeIntervals, inv);
797 j--; /* counter-act j++ in for-loop above */
798
799 /* return register regNew to the free pool */
800 if (dbg)
801 _mesa_printf(" free reg %d\n", regNew);
802 ASSERT(usedRegs[regNew] == GL_TRUE);
803 usedRegs[regNew] = GL_FALSE;
804 }
805 }
806 }
807
808 /* find a free register for this live interval */
809 {
Brian Paulf4468382009-04-07 11:15:27 -0600810 const GLint k = alloc_register(usedRegs);
811 if (k < 0) {
Brian Paul12256fc2009-03-20 17:08:30 -0600812 /* out of registers, give up */
813 return;
814 }
815 registerMap[live->Reg] = k;
816 maxTemp = MAX2(maxTemp, k);
817 if (dbg)
Brian Paulf4468382009-04-07 11:15:27 -0600818 _mesa_printf(" remap register %u -> %d\n", live->Reg, k);
Brian Paul12256fc2009-03-20 17:08:30 -0600819 }
820
821 /* Insert this live interval into the active list which is sorted
822 * by increasing end points.
823 */
824 insert_interval_by_end(&activeIntervals, live);
825 }
826 }
827
828 if (maxTemp + 1 < liveIntervals.Num) {
829 /* OK, we've reduced the number of registers needed.
830 * Scan the program and replace all the old temporary register
831 * indexes with the new indexes.
832 */
833 replace_regs(prog, PROGRAM_TEMPORARY, registerMap);
834
835 prog->NumTemporaries = maxTemp + 1;
836 }
837
838 if (dbg) {
839 _mesa_printf("Optimize: End live-interval register reallocation\n");
840 _mesa_printf("Num temp regs before: %u after: %u\n",
841 liveIntervals.Num, maxTemp + 1);
842 _mesa_print_program(prog);
843 }
844}
845
846
Brian Paul82f1c0b2009-03-06 16:18:22 -0700847/**
848 * Apply optimizations to the given program to eliminate unnecessary
849 * instructions, temp regs, etc.
850 */
851void
852_mesa_optimize_program(GLcontext *ctx, struct gl_program *program)
853{
854 if (1)
855 _mesa_remove_dead_code(program);
856
Brian Paul0f0e24f2009-04-07 11:10:27 -0600857 if (0) /* not tested much yet */
Brian Paul82f1c0b2009-03-06 16:18:22 -0700858 _mesa_remove_extra_moves(program);
859
Brian Paul0f0e24f2009-04-07 11:10:27 -0600860 if (0)
Brian Paul82f1c0b2009-03-06 16:18:22 -0700861 _mesa_consolidate_registers(program);
Brian Paul0f0e24f2009-04-07 11:10:27 -0600862 else
Brian Paul12256fc2009-03-20 17:08:30 -0600863 _mesa_reallocate_registers(program);
Brian Paul82f1c0b2009-03-06 16:18:22 -0700864}