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