blob: b4658cb37f904347eb5c2bd96bd43f3f9a4bed66 [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
Brian Paul82f1c0b2009-03-06 16:18:22 -070041/**
42 * In 'prog' remove instruction[i] if removeFlags[i] == TRUE.
43 * \return number of instructions removed
44 */
45static GLuint
46remove_instructions(struct gl_program *prog, const GLboolean *removeFlags)
47{
48 GLint i, removeEnd = 0, removeCount = 0;
49 GLuint totalRemoved = 0;
50
51 /* go backward */
52 for (i = prog->NumInstructions - 1; i >= 0; i--) {
53 if (removeFlags[i]) {
54 totalRemoved++;
55 if (removeCount == 0) {
56 /* begin a run of instructions to remove */
57 removeEnd = i;
58 removeCount = 1;
59 }
60 else {
61 /* extend the run of instructions to remove */
62 removeCount++;
63 }
64 }
65 else {
66 /* don't remove this instruction, but check if the preceeding
67 * instructions are to be removed.
68 */
69 if (removeCount > 0) {
70 GLint removeStart = removeEnd - removeCount + 1;
71 _mesa_delete_instructions(prog, removeStart, removeCount);
72 removeStart = removeCount = 0; /* reset removal info */
73 }
74 }
75 }
Eric Anholtf3cacfe2009-11-06 13:04:54 -080076 /* Finish removing if the first instruction was to be removed. */
77 if (removeCount > 0) {
78 GLint removeStart = removeEnd - removeCount + 1;
79 _mesa_delete_instructions(prog, removeStart, removeCount);
80 removeStart = removeCount = 0; /* reset removal info */
81 }
Brian Paul82f1c0b2009-03-06 16:18:22 -070082 return totalRemoved;
83}
84
85
86/**
Brian Paul12256fc2009-03-20 17:08:30 -060087 * Remap register indexes according to map.
88 * \param prog the program to search/replace
89 * \param file the type of register file to search/replace
90 * \param map maps old register indexes to new indexes
91 */
92static void
93replace_regs(struct gl_program *prog, gl_register_file file, const GLint map[])
94{
95 GLuint i;
96
97 for (i = 0; i < prog->NumInstructions; i++) {
98 struct prog_instruction *inst = prog->Instructions + i;
99 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
100 GLuint j;
101 for (j = 0; j < numSrc; j++) {
102 if (inst->SrcReg[j].File == file) {
103 GLuint index = inst->SrcReg[j].Index;
104 ASSERT(map[index] >= 0);
105 inst->SrcReg[j].Index = map[index];
106 }
107 }
108 if (inst->DstReg.File == file) {
109 const GLuint index = inst->DstReg.Index;
110 ASSERT(map[index] >= 0);
111 inst->DstReg.Index = map[index];
112 }
113 }
114}
115
116
117/**
Brian Paul82f1c0b2009-03-06 16:18:22 -0700118 * Consolidate temporary registers to use low numbers. For example, if the
119 * shader only uses temps 4, 5, 8, replace them with 0, 1, 2.
120 */
121static void
122_mesa_consolidate_registers(struct gl_program *prog)
123{
124 GLboolean tempUsed[MAX_PROGRAM_TEMPS];
Brian Paul12256fc2009-03-20 17:08:30 -0600125 GLint tempMap[MAX_PROGRAM_TEMPS];
Brian Paul82f1c0b2009-03-06 16:18:22 -0700126 GLuint tempMax = 0, i;
127
128 if (dbg) {
129 _mesa_printf("Optimize: Begin register consolidation\n");
130 }
131
132 memset(tempUsed, 0, sizeof(tempUsed));
133
Brian Paul12256fc2009-03-20 17:08:30 -0600134 for (i = 0; i < MAX_PROGRAM_TEMPS; i++) {
135 tempMap[i] = -1;
136 }
137
Brian Paul82f1c0b2009-03-06 16:18:22 -0700138 /* set tempUsed[i] if temporary [i] is referenced */
139 for (i = 0; i < prog->NumInstructions; i++) {
140 const struct prog_instruction *inst = prog->Instructions + i;
141 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
142 GLuint j;
143 for (j = 0; j < numSrc; j++) {
144 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) {
145 const GLuint index = inst->SrcReg[j].Index;
146 ASSERT(index < MAX_PROGRAM_TEMPS);
147 tempUsed[index] = GL_TRUE;
148 tempMax = MAX2(tempMax, index);
149 break;
150 }
151 }
152 if (inst->DstReg.File == PROGRAM_TEMPORARY) {
153 const GLuint index = inst->DstReg.Index;
154 ASSERT(index < MAX_PROGRAM_TEMPS);
155 tempUsed[index] = GL_TRUE;
156 tempMax = MAX2(tempMax, index);
157 }
158 }
159
160 /* allocate a new index for each temp that's used */
161 {
162 GLuint freeTemp = 0;
163 for (i = 0; i <= tempMax; i++) {
164 if (tempUsed[i]) {
165 tempMap[i] = freeTemp++;
166 /*_mesa_printf("replace %u with %u\n", i, tempMap[i]);*/
167 }
168 }
169 if (freeTemp == tempMax + 1) {
170 /* no consolidation possible */
171 return;
172 }
173 if (dbg) {
174 _mesa_printf("Replace regs 0..%u with 0..%u\n", tempMax, freeTemp-1);
175 }
176 }
177
Brian Paul12256fc2009-03-20 17:08:30 -0600178 replace_regs(prog, PROGRAM_TEMPORARY, tempMap);
179
Brian Paul82f1c0b2009-03-06 16:18:22 -0700180 if (dbg) {
181 _mesa_printf("Optimize: End register consolidation\n");
182 }
183}
184
185
186/**
187 * Remove dead instructions from the given program.
188 * This is very primitive for now. Basically look for temp registers
189 * that are written to but never read. Remove any instructions that
190 * write to such registers. Be careful with condition code setters.
191 */
192static void
193_mesa_remove_dead_code(struct gl_program *prog)
194{
Eric Anholtee0a9e62009-06-12 12:37:25 -0700195 GLboolean tempRead[MAX_PROGRAM_TEMPS][4];
Brian Paul82f1c0b2009-03-06 16:18:22 -0700196 GLboolean *removeInst; /* per-instruction removal flag */
Eric Anholtee0a9e62009-06-12 12:37:25 -0700197 GLuint i, rem = 0, comp;
Brian Paul82f1c0b2009-03-06 16:18:22 -0700198
Brian Paul82f1c0b2009-03-06 16:18:22 -0700199 memset(tempRead, 0, sizeof(tempRead));
200
201 if (dbg) {
202 _mesa_printf("Optimize: Begin dead code removal\n");
203 /*_mesa_print_program(prog);*/
204 }
205
206 removeInst = (GLboolean *)
207 _mesa_calloc(prog->NumInstructions * sizeof(GLboolean));
208
209 /* Determine which temps are read and written */
210 for (i = 0; i < prog->NumInstructions; i++) {
211 const struct prog_instruction *inst = prog->Instructions + i;
212 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
213 GLuint j;
214
215 /* check src regs */
216 for (j = 0; j < numSrc; j++) {
217 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) {
218 const GLuint index = inst->SrcReg[j].Index;
219 ASSERT(index < MAX_PROGRAM_TEMPS);
220
221 if (inst->SrcReg[j].RelAddr) {
222 if (dbg)
223 _mesa_printf("abort remove dead code (indirect temp)\n");
Eric Anholtee0a9e62009-06-12 12:37:25 -0700224 goto done;
Brian Paul82f1c0b2009-03-06 16:18:22 -0700225 }
226
Eric Anholtee0a9e62009-06-12 12:37:25 -0700227 for (comp = 0; comp < 4; comp++) {
228 GLuint swz = (inst->SrcReg[j].Swizzle >> (3 * comp)) & 0x7;
229
230 switch (swz) {
231 case SWIZZLE_X:
232 tempRead[index][0] = GL_TRUE;
233 break;
234 case SWIZZLE_Y:
235 tempRead[index][1] = GL_TRUE;
236 break;
237 case SWIZZLE_Z:
238 tempRead[index][2] = GL_TRUE;
239 break;
240 case SWIZZLE_W:
241 tempRead[index][3] = GL_TRUE;
242 break;
243 }
244 }
Brian Paul82f1c0b2009-03-06 16:18:22 -0700245 }
246 }
247
248 /* check dst reg */
249 if (inst->DstReg.File == PROGRAM_TEMPORARY) {
250 const GLuint index = inst->DstReg.Index;
251 ASSERT(index < MAX_PROGRAM_TEMPS);
252
253 if (inst->DstReg.RelAddr) {
254 if (dbg)
255 _mesa_printf("abort remove dead code (indirect temp)\n");
Eric Anholtee0a9e62009-06-12 12:37:25 -0700256 goto done;
Brian Paul82f1c0b2009-03-06 16:18:22 -0700257 }
258
Brian Paul82f1c0b2009-03-06 16:18:22 -0700259 if (inst->CondUpdate) {
260 /* If we're writing to this register and setting condition
261 * codes we cannot remove the instruction. Prevent removal
262 * by setting the 'read' flag.
263 */
Eric Anholtee0a9e62009-06-12 12:37:25 -0700264 tempRead[index][0] = GL_TRUE;
265 tempRead[index][1] = GL_TRUE;
266 tempRead[index][2] = GL_TRUE;
267 tempRead[index][3] = GL_TRUE;
Brian Paul82f1c0b2009-03-06 16:18:22 -0700268 }
269 }
270 }
271
Brian Paul82f1c0b2009-03-06 16:18:22 -0700272 /* find instructions that write to dead registers, flag for removal */
273 for (i = 0; i < prog->NumInstructions; i++) {
Eric Anholtee0a9e62009-06-12 12:37:25 -0700274 struct prog_instruction *inst = prog->Instructions + i;
275 const GLuint numDst = _mesa_num_inst_dst_regs(inst->Opcode);
276
277 if (numDst != 0 && inst->DstReg.File == PROGRAM_TEMPORARY) {
278 GLint chan, index = inst->DstReg.Index;
279
280 for (chan = 0; chan < 4; chan++) {
281 if (!tempRead[index][chan] &&
282 inst->DstReg.WriteMask & (1 << chan)) {
283 if (dbg) {
284 _mesa_printf("Remove writemask on %u.%c\n", i,
285 chan == 3 ? 'w' : 'x' + chan);
286 }
287 inst->DstReg.WriteMask &= ~(1 << chan);
288 rem++;
289 }
290 }
291
292 if (inst->DstReg.WriteMask == 0) {
293 /* If we cleared all writes, the instruction can be removed. */
294 if (dbg)
295 _mesa_printf("Remove instruction %u: \n", i);
296 removeInst[i] = GL_TRUE;
297 }
Brian Paul82f1c0b2009-03-06 16:18:22 -0700298 }
299 }
300
301 /* now remove the instructions which aren't needed */
302 rem = remove_instructions(prog, removeInst);
303
Brian Paul82f1c0b2009-03-06 16:18:22 -0700304 if (dbg) {
Eric Anholtee0a9e62009-06-12 12:37:25 -0700305 _mesa_printf("Optimize: End dead code removal.\n");
306 _mesa_printf(" %u channel writes removed\n", rem);
307 _mesa_printf(" %u instructions removed\n", rem);
Brian Paul82f1c0b2009-03-06 16:18:22 -0700308 /*_mesa_print_program(prog);*/
309 }
Eric Anholtee0a9e62009-06-12 12:37:25 -0700310
311done:
312 _mesa_free(removeInst);
Brian Paul82f1c0b2009-03-06 16:18:22 -0700313}
314
315
316enum temp_use
317{
318 READ,
319 WRITE,
320 FLOW,
321 END
322};
323
324/**
325 * Scan forward in program from 'start' for the next occurance of TEMP[index].
326 * Return READ, WRITE, FLOW or END to indicate the next usage or an indicator
327 * that we can't look further.
328 */
329static enum temp_use
330find_next_temp_use(const struct gl_program *prog, GLuint start, GLuint index)
331{
332 GLuint i;
333
334 for (i = start; i < prog->NumInstructions; i++) {
335 const struct prog_instruction *inst = prog->Instructions + i;
336 switch (inst->Opcode) {
337 case OPCODE_BGNLOOP:
338 case OPCODE_ENDLOOP:
339 case OPCODE_BGNSUB:
340 case OPCODE_ENDSUB:
341 return FLOW;
342 default:
343 {
344 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
345 GLuint j;
346 for (j = 0; j < numSrc; j++) {
347 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY &&
348 inst->SrcReg[j].Index == index)
349 return READ;
350 }
351 if (inst->DstReg.File == PROGRAM_TEMPORARY &&
352 inst->DstReg.Index == index)
353 return WRITE;
354 }
355 }
356 }
357
358 return END;
359}
360
361
362/**
Eric Anholte4e312d2009-05-16 01:47:44 -0700363 * Try to remove use of extraneous MOV instructions, to free them up for dead
364 * code removal.
365 */
366static void
367_mesa_remove_extra_move_use(struct gl_program *prog)
368{
369 GLuint i;
370
371 if (dbg) {
372 _mesa_printf("Optimize: Begin remove extra move use\n");
373 _mesa_print_program(prog);
374 }
375
376 /*
377 * Look for sequences such as this:
378 * MOV tmpX, arg0;
379 * FOO tmpY, tmpX, arg1;
380 * and convert into:
381 * MOV tmpX, arg0;
382 * FOO tmpY, arg0, arg1;
383 */
384
385 for (i = 0; i < prog->NumInstructions - 1; i++) {
386 const struct prog_instruction *mov = prog->Instructions + i;
387 struct prog_instruction *inst2 = prog->Instructions + i + 1;
388 int arg;
389
390 if (mov->Opcode != OPCODE_MOV ||
391 mov->DstReg.File != PROGRAM_TEMPORARY ||
392 mov->DstReg.RelAddr ||
393 mov->DstReg.CondMask != COND_TR ||
394 mov->SaturateMode != SATURATE_OFF)
395 continue;
396
397 for (arg = 0; arg < _mesa_num_inst_src_regs(inst2->Opcode); arg++) {
398 int comp;
399
400 if (inst2->SrcReg[arg].File != mov->DstReg.File ||
401 inst2->SrcReg[arg].Index != mov->DstReg.Index ||
402 inst2->SrcReg[arg].RelAddr ||
403 inst2->SrcReg[arg].Abs)
404 continue;
405
406 /* Check that all the sources for this arg of inst2 come from inst1
407 * or constants.
408 */
409 for (comp = 0; comp < 4; comp++) {
410 int src_swz = GET_SWZ(inst2->SrcReg[arg].Swizzle, comp);
411
412 /* If the MOV didn't write that channel, can't use it. */
413 if (src_swz <= SWIZZLE_W &&
414 (mov->DstReg.WriteMask & (1 << src_swz)) == 0)
415 break;
416 }
417 if (comp != 4)
418 continue;
419
420 /* Adjust the swizzles of inst2 to point at MOV's source */
421 for (comp = 0; comp < 4; comp++) {
422 int inst2_swz = GET_SWZ(inst2->SrcReg[arg].Swizzle, comp);
423
424 if (inst2_swz <= SWIZZLE_W) {
425 GLuint s = GET_SWZ(mov->SrcReg[0].Swizzle, inst2_swz);
426 inst2->SrcReg[arg].Swizzle &= ~(7 << (3 * comp));
427 inst2->SrcReg[arg].Swizzle |= s << (3 * comp);
428 inst2->SrcReg[arg].Negate ^= (((mov->SrcReg[0].Negate >>
429 inst2_swz) & 0x1) << comp);
430 }
431 }
432 inst2->SrcReg[arg].File = mov->SrcReg[0].File;
433 inst2->SrcReg[arg].Index = mov->SrcReg[0].Index;
434 }
435 }
436
437 if (dbg) {
438 _mesa_printf("Optimize: End remove extra move use.\n");
439 /*_mesa_print_program(prog);*/
440 }
441}
442
443/**
Brian Paul82f1c0b2009-03-06 16:18:22 -0700444 * Try to remove extraneous MOV instructions from the given program.
445 */
446static void
447_mesa_remove_extra_moves(struct gl_program *prog)
448{
449 GLboolean *removeInst; /* per-instruction removal flag */
450 GLuint i, rem, loopNesting = 0, subroutineNesting = 0;
451
452 if (dbg) {
453 _mesa_printf("Optimize: Begin remove extra moves\n");
454 _mesa_print_program(prog);
455 }
456
457 removeInst = (GLboolean *)
458 _mesa_calloc(prog->NumInstructions * sizeof(GLboolean));
459
460 /*
461 * Look for sequences such as this:
462 * FOO tmpX, arg0, arg1;
463 * MOV tmpY, tmpX;
464 * and convert into:
465 * FOO tmpY, arg0, arg1;
466 */
467
468 for (i = 0; i < prog->NumInstructions; i++) {
469 const struct prog_instruction *inst = prog->Instructions + i;
470
471 switch (inst->Opcode) {
472 case OPCODE_BGNLOOP:
473 loopNesting++;
474 break;
475 case OPCODE_ENDLOOP:
476 loopNesting--;
477 break;
478 case OPCODE_BGNSUB:
479 subroutineNesting++;
480 break;
481 case OPCODE_ENDSUB:
482 subroutineNesting--;
483 break;
484 case OPCODE_MOV:
485 if (i > 0 &&
486 loopNesting == 0 &&
487 subroutineNesting == 0 &&
488 inst->SrcReg[0].File == PROGRAM_TEMPORARY &&
489 inst->SrcReg[0].Swizzle == SWIZZLE_XYZW) {
490 /* see if this MOV can be removed */
491 const GLuint tempIndex = inst->SrcReg[0].Index;
492 struct prog_instruction *prevInst;
493 GLuint prevI;
494
495 /* get pointer to previous instruction */
496 prevI = i - 1;
497 while (removeInst[prevI] && prevI > 0)
498 prevI--;
499 prevInst = prog->Instructions + prevI;
500
501 if (prevInst->DstReg.File == PROGRAM_TEMPORARY &&
502 prevInst->DstReg.Index == tempIndex &&
503 prevInst->DstReg.WriteMask == WRITEMASK_XYZW) {
504
505 enum temp_use next_use =
506 find_next_temp_use(prog, i + 1, tempIndex);
507
508 if (next_use == WRITE || next_use == END) {
509 /* OK, we can safely remove this MOV instruction.
510 * Transform:
511 * prevI: FOO tempIndex, x, y;
512 * i: MOV z, tempIndex;
513 * Into:
514 * prevI: FOO z, x, y;
515 */
516
517 /* patch up prev inst */
518 prevInst->DstReg.File = inst->DstReg.File;
519 prevInst->DstReg.Index = inst->DstReg.Index;
520
521 /* flag this instruction for removal */
522 removeInst[i] = GL_TRUE;
523
524 if (dbg) {
525 _mesa_printf("Remove MOV at %u\n", i);
526 _mesa_printf("new prev inst %u: ", prevI);
527 _mesa_print_instruction(prevInst);
528 }
529 }
530 }
531 }
532 break;
533 default:
534 ; /* nothing */
535 }
536 }
537
538 /* now remove the instructions which aren't needed */
539 rem = remove_instructions(prog, removeInst);
540
Brian Paul05749542009-10-01 14:52:28 -0600541 _mesa_free(removeInst);
542
Brian Paul82f1c0b2009-03-06 16:18:22 -0700543 if (dbg) {
544 _mesa_printf("Optimize: End remove extra moves. %u instructions removed\n", rem);
545 /*_mesa_print_program(prog);*/
546 }
547}
548
549
Brian Paul12256fc2009-03-20 17:08:30 -0600550/** A live register interval */
551struct interval
552{
553 GLuint Reg; /** The temporary register index */
554 GLuint Start, End; /** Start/end instruction numbers */
555};
556
557
558/** A list of register intervals */
559struct interval_list
560{
561 GLuint Num;
562 struct interval Intervals[MAX_PROGRAM_TEMPS];
563};
564
565
566static void
567append_interval(struct interval_list *list, const struct interval *inv)
568{
569 list->Intervals[list->Num++] = *inv;
570}
571
572
573/** Insert interval inv into list, sorted by interval end */
574static void
575insert_interval_by_end(struct interval_list *list, const struct interval *inv)
576{
577 /* XXX we could do a binary search insertion here since list is sorted */
578 GLint i = list->Num - 1;
579 while (i >= 0 && list->Intervals[i].End > inv->End) {
580 list->Intervals[i + 1] = list->Intervals[i];
581 i--;
582 }
583 list->Intervals[i + 1] = *inv;
584 list->Num++;
585
586#ifdef DEBUG
587 {
588 GLuint i;
589 for (i = 0; i + 1 < list->Num; i++) {
590 ASSERT(list->Intervals[i].End <= list->Intervals[i + 1].End);
591 }
592 }
593#endif
594}
595
596
597/** Remove the given interval from the interval list */
598static void
599remove_interval(struct interval_list *list, const struct interval *inv)
600{
601 /* XXX we could binary search since list is sorted */
602 GLuint k;
603 for (k = 0; k < list->Num; k++) {
604 if (list->Intervals[k].Reg == inv->Reg) {
605 /* found, remove it */
606 ASSERT(list->Intervals[k].Start == inv->Start);
607 ASSERT(list->Intervals[k].End == inv->End);
608 while (k < list->Num - 1) {
609 list->Intervals[k] = list->Intervals[k + 1];
610 k++;
611 }
612 list->Num--;
613 return;
614 }
615 }
616}
617
618
619/** called by qsort() */
620static int
621compare_start(const void *a, const void *b)
622{
623 const struct interval *ia = (const struct interval *) a;
624 const struct interval *ib = (const struct interval *) b;
625 if (ia->Start < ib->Start)
626 return -1;
627 else if (ia->Start > ib->Start)
628 return +1;
629 else
630 return 0;
631}
632
633/** sort the interval list according to interval starts */
634static void
635sort_interval_list_by_start(struct interval_list *list)
636{
637 qsort(list->Intervals, list->Num, sizeof(struct interval), compare_start);
638#ifdef DEBUG
639 {
640 GLuint i;
641 for (i = 0; i + 1 < list->Num; i++) {
642 ASSERT(list->Intervals[i].Start <= list->Intervals[i + 1].Start);
643 }
644 }
645#endif
646}
647
648
649/**
650 * Update the intermediate interval info for register 'index' and
651 * instruction 'ic'.
652 */
653static void
654update_interval(GLint intBegin[], GLint intEnd[], GLuint index, GLuint ic)
655{
656 ASSERT(index < MAX_PROGRAM_TEMPS);
657 if (intBegin[index] == -1) {
658 ASSERT(intEnd[index] == -1);
659 intBegin[index] = intEnd[index] = ic;
660 }
661 else {
662 intEnd[index] = ic;
663 }
664}
665
666
667/**
Brian Paul7da3f942009-04-24 16:28:36 -0600668 * Find first/last instruction that references each temporary register.
Brian Paul12256fc2009-03-20 17:08:30 -0600669 */
Brian Paul7da3f942009-04-24 16:28:36 -0600670GLboolean
671_mesa_find_temp_intervals(const struct prog_instruction *instructions,
672 GLuint numInstructions,
673 GLint intBegin[MAX_PROGRAM_TEMPS],
674 GLint intEnd[MAX_PROGRAM_TEMPS])
Brian Paul12256fc2009-03-20 17:08:30 -0600675{
676 struct loop_info
677 {
678 GLuint Start, End; /**< Start, end instructions of loop */
679 };
680 struct loop_info loopStack[MAX_LOOP_NESTING];
681 GLuint loopStackDepth = 0;
Brian Paul12256fc2009-03-20 17:08:30 -0600682 GLuint i;
683
Brian Paul12256fc2009-03-20 17:08:30 -0600684 for (i = 0; i < MAX_PROGRAM_TEMPS; i++){
685 intBegin[i] = intEnd[i] = -1;
686 }
687
688 /* Scan instructions looking for temporary registers */
Brian Paul7da3f942009-04-24 16:28:36 -0600689 for (i = 0; i < numInstructions; i++) {
690 const struct prog_instruction *inst = instructions + i;
Brian Paul12256fc2009-03-20 17:08:30 -0600691 if (inst->Opcode == OPCODE_BGNLOOP) {
692 loopStack[loopStackDepth].Start = i;
693 loopStack[loopStackDepth].End = inst->BranchTarget;
694 loopStackDepth++;
695 }
696 else if (inst->Opcode == OPCODE_ENDLOOP) {
697 loopStackDepth--;
698 }
699 else if (inst->Opcode == OPCODE_CAL) {
700 return GL_FALSE;
701 }
702 else {
Brian Paul7da3f942009-04-24 16:28:36 -0600703 const GLuint numSrc = 3;/*_mesa_num_inst_src_regs(inst->Opcode);*/
Brian Paul12256fc2009-03-20 17:08:30 -0600704 GLuint j;
705 for (j = 0; j < numSrc; j++) {
706 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) {
707 const GLuint index = inst->SrcReg[j].Index;
708 if (inst->SrcReg[j].RelAddr)
709 return GL_FALSE;
710 update_interval(intBegin, intEnd, index, i);
711 if (loopStackDepth > 0) {
712 /* extend temp register's interval to end of loop */
713 GLuint loopEnd = loopStack[loopStackDepth - 1].End;
714 update_interval(intBegin, intEnd, index, loopEnd);
715 }
716 }
717 }
718 if (inst->DstReg.File == PROGRAM_TEMPORARY) {
719 const GLuint index = inst->DstReg.Index;
720 if (inst->DstReg.RelAddr)
721 return GL_FALSE;
722 update_interval(intBegin, intEnd, index, i);
723 if (loopStackDepth > 0) {
724 /* extend temp register's interval to end of loop */
725 GLuint loopEnd = loopStack[loopStackDepth - 1].End;
726 update_interval(intBegin, intEnd, index, loopEnd);
727 }
728 }
729 }
730 }
731
Brian Paul7da3f942009-04-24 16:28:36 -0600732 return GL_TRUE;
733}
734
735
736/**
737 * Find the live intervals for each temporary register in the program.
738 * For register R, the interval [A,B] indicates that R is referenced
739 * from instruction A through instruction B.
740 * Special consideration is needed for loops and subroutines.
741 * \return GL_TRUE if success, GL_FALSE if we cannot proceed for some reason
742 */
743static GLboolean
744find_live_intervals(struct gl_program *prog,
745 struct interval_list *liveIntervals)
746{
747 GLint intBegin[MAX_PROGRAM_TEMPS], intEnd[MAX_PROGRAM_TEMPS];
748 GLuint i;
749
750 /*
751 * Note: we'll return GL_FALSE below if we find relative indexing
752 * into the TEMP register file. We can't handle that yet.
753 * We also give up on subroutines for now.
754 */
755
756 if (dbg) {
757 _mesa_printf("Optimize: Begin find intervals\n");
758 }
759
760 /* build intermediate arrays */
761 if (!_mesa_find_temp_intervals(prog->Instructions, prog->NumInstructions,
762 intBegin, intEnd))
763 return GL_FALSE;
764
Brian Paul12256fc2009-03-20 17:08:30 -0600765 /* Build live intervals list from intermediate arrays */
766 liveIntervals->Num = 0;
767 for (i = 0; i < MAX_PROGRAM_TEMPS; i++) {
768 if (intBegin[i] >= 0) {
769 struct interval inv;
770 inv.Reg = i;
771 inv.Start = intBegin[i];
772 inv.End = intEnd[i];
773 append_interval(liveIntervals, &inv);
774 }
775 }
776
777 /* Sort the list according to interval starts */
778 sort_interval_list_by_start(liveIntervals);
779
780 if (dbg) {
781 /* print interval info */
782 for (i = 0; i < liveIntervals->Num; i++) {
783 const struct interval *inv = liveIntervals->Intervals + i;
784 _mesa_printf("Reg[%d] live [%d, %d]:",
785 inv->Reg, inv->Start, inv->End);
786 if (1) {
787 int j;
788 for (j = 0; j < inv->Start; j++)
789 _mesa_printf(" ");
790 for (j = inv->Start; j <= inv->End; j++)
791 _mesa_printf("x");
792 }
793 _mesa_printf("\n");
794 }
795 }
796
797 return GL_TRUE;
798}
799
800
Brian Paulf4468382009-04-07 11:15:27 -0600801/** Scan the array of used register flags to find free entry */
802static GLint
Brian Paul12256fc2009-03-20 17:08:30 -0600803alloc_register(GLboolean usedRegs[MAX_PROGRAM_TEMPS])
804{
805 GLuint k;
806 for (k = 0; k < MAX_PROGRAM_TEMPS; k++) {
807 if (!usedRegs[k]) {
808 usedRegs[k] = GL_TRUE;
809 return k;
810 }
811 }
Brian Paulf4468382009-04-07 11:15:27 -0600812 return -1;
Brian Paul12256fc2009-03-20 17:08:30 -0600813}
814
815
816/**
817 * This function implements "Linear Scan Register Allocation" to reduce
818 * the number of temporary registers used by the program.
819 *
820 * We compute the "live interval" for all temporary registers then
821 * examine the overlap of the intervals to allocate new registers.
822 * Basically, if two intervals do not overlap, they can use the same register.
823 */
824static void
825_mesa_reallocate_registers(struct gl_program *prog)
826{
827 struct interval_list liveIntervals;
828 GLint registerMap[MAX_PROGRAM_TEMPS];
829 GLboolean usedRegs[MAX_PROGRAM_TEMPS];
830 GLuint i;
Brian Paulf4468382009-04-07 11:15:27 -0600831 GLint maxTemp = -1;
Brian Paul12256fc2009-03-20 17:08:30 -0600832
833 if (dbg) {
834 _mesa_printf("Optimize: Begin live-interval register reallocation\n");
835 _mesa_print_program(prog);
836 }
837
838 for (i = 0; i < MAX_PROGRAM_TEMPS; i++){
839 registerMap[i] = -1;
840 usedRegs[i] = GL_FALSE;
841 }
842
843 if (!find_live_intervals(prog, &liveIntervals)) {
844 if (dbg)
845 _mesa_printf("Aborting register reallocation\n");
846 return;
847 }
848
849 {
850 struct interval_list activeIntervals;
851 activeIntervals.Num = 0;
852
853 /* loop over live intervals, allocating a new register for each */
854 for (i = 0; i < liveIntervals.Num; i++) {
855 const struct interval *live = liveIntervals.Intervals + i;
856
857 if (dbg)
858 _mesa_printf("Consider register %u\n", live->Reg);
859
860 /* Expire old intervals. Intervals which have ended with respect
861 * to the live interval can have their remapped registers freed.
862 */
863 {
864 GLint j;
865 for (j = 0; j < activeIntervals.Num; j++) {
866 const struct interval *inv = activeIntervals.Intervals + j;
867 if (inv->End >= live->Start) {
868 /* Stop now. Since the activeInterval list is sorted
869 * we know we don't have to go further.
870 */
871 break;
872 }
873 else {
874 /* Interval 'inv' has expired */
875 const GLint regNew = registerMap[inv->Reg];
876 ASSERT(regNew >= 0);
877
878 if (dbg)
879 _mesa_printf(" expire interval for reg %u\n", inv->Reg);
880
881 /* remove interval j from active list */
882 remove_interval(&activeIntervals, inv);
883 j--; /* counter-act j++ in for-loop above */
884
885 /* return register regNew to the free pool */
886 if (dbg)
887 _mesa_printf(" free reg %d\n", regNew);
888 ASSERT(usedRegs[regNew] == GL_TRUE);
889 usedRegs[regNew] = GL_FALSE;
890 }
891 }
892 }
893
894 /* find a free register for this live interval */
895 {
Brian Paulf4468382009-04-07 11:15:27 -0600896 const GLint k = alloc_register(usedRegs);
897 if (k < 0) {
Brian Paul12256fc2009-03-20 17:08:30 -0600898 /* out of registers, give up */
899 return;
900 }
901 registerMap[live->Reg] = k;
902 maxTemp = MAX2(maxTemp, k);
903 if (dbg)
Brian Paulf4468382009-04-07 11:15:27 -0600904 _mesa_printf(" remap register %u -> %d\n", live->Reg, k);
Brian Paul12256fc2009-03-20 17:08:30 -0600905 }
906
907 /* Insert this live interval into the active list which is sorted
908 * by increasing end points.
909 */
910 insert_interval_by_end(&activeIntervals, live);
911 }
912 }
913
914 if (maxTemp + 1 < liveIntervals.Num) {
915 /* OK, we've reduced the number of registers needed.
916 * Scan the program and replace all the old temporary register
917 * indexes with the new indexes.
918 */
919 replace_regs(prog, PROGRAM_TEMPORARY, registerMap);
920
921 prog->NumTemporaries = maxTemp + 1;
922 }
923
924 if (dbg) {
925 _mesa_printf("Optimize: End live-interval register reallocation\n");
926 _mesa_printf("Num temp regs before: %u after: %u\n",
927 liveIntervals.Num, maxTemp + 1);
928 _mesa_print_program(prog);
929 }
930}
931
932
Brian Paul82f1c0b2009-03-06 16:18:22 -0700933/**
934 * Apply optimizations to the given program to eliminate unnecessary
935 * instructions, temp regs, etc.
936 */
937void
938_mesa_optimize_program(GLcontext *ctx, struct gl_program *program)
939{
Eric Anholte4e312d2009-05-16 01:47:44 -0700940 _mesa_remove_extra_move_use(program);
941
Brian Paul82f1c0b2009-03-06 16:18:22 -0700942 if (1)
943 _mesa_remove_dead_code(program);
944
Brian Paul0f0e24f2009-04-07 11:10:27 -0600945 if (0) /* not tested much yet */
Brian Paul82f1c0b2009-03-06 16:18:22 -0700946 _mesa_remove_extra_moves(program);
947
Brian Paul0f0e24f2009-04-07 11:10:27 -0600948 if (0)
Brian Paul82f1c0b2009-03-06 16:18:22 -0700949 _mesa_consolidate_registers(program);
Brian Paul0f0e24f2009-04-07 11:10:27 -0600950 else
Brian Paul12256fc2009-03-20 17:08:30 -0600951 _mesa_reallocate_registers(program);
Brian Paul82f1c0b2009-03-06 16:18:22 -0700952}