blob: 8638754e243e88dc3825edb5ad024182052ad4d5 [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
Eric Anholt6b0bcfa2009-11-06 14:06:08 -080041/* Returns the mask of channels read from the given srcreg in this instruction.
42 */
43static GLuint
44get_src_arg_mask(const struct prog_instruction *inst, int arg)
45{
46 int writemask = inst->DstReg.WriteMask;
47
48 if (inst->CondUpdate)
49 writemask = WRITEMASK_XYZW;
50
51 switch (inst->Opcode) {
52 case OPCODE_MOV:
53 case OPCODE_ABS:
54 case OPCODE_ADD:
55 case OPCODE_MUL:
56 case OPCODE_SUB:
57 return writemask;
58 case OPCODE_RCP:
59 case OPCODE_SIN:
60 case OPCODE_COS:
61 case OPCODE_RSQ:
62 case OPCODE_POW:
63 case OPCODE_EX2:
64 return WRITEMASK_X;
65 case OPCODE_DP2:
66 return WRITEMASK_XY;
67 case OPCODE_DP3:
68 return WRITEMASK_XYZ;
69 default:
70 return WRITEMASK_XYZW;
71 }
72}
73
Brian Paul82f1c0b2009-03-06 16:18:22 -070074/**
75 * In 'prog' remove instruction[i] if removeFlags[i] == TRUE.
76 * \return number of instructions removed
77 */
78static GLuint
79remove_instructions(struct gl_program *prog, const GLboolean *removeFlags)
80{
81 GLint i, removeEnd = 0, removeCount = 0;
82 GLuint totalRemoved = 0;
83
84 /* go backward */
85 for (i = prog->NumInstructions - 1; i >= 0; i--) {
86 if (removeFlags[i]) {
87 totalRemoved++;
88 if (removeCount == 0) {
89 /* begin a run of instructions to remove */
90 removeEnd = i;
91 removeCount = 1;
92 }
93 else {
94 /* extend the run of instructions to remove */
95 removeCount++;
96 }
97 }
98 else {
99 /* don't remove this instruction, but check if the preceeding
100 * instructions are to be removed.
101 */
102 if (removeCount > 0) {
103 GLint removeStart = removeEnd - removeCount + 1;
104 _mesa_delete_instructions(prog, removeStart, removeCount);
105 removeStart = removeCount = 0; /* reset removal info */
106 }
107 }
108 }
Eric Anholtf3cacfe2009-11-06 13:04:54 -0800109 /* Finish removing if the first instruction was to be removed. */
110 if (removeCount > 0) {
111 GLint removeStart = removeEnd - removeCount + 1;
112 _mesa_delete_instructions(prog, removeStart, removeCount);
113 removeStart = removeCount = 0; /* reset removal info */
114 }
Brian Paul82f1c0b2009-03-06 16:18:22 -0700115 return totalRemoved;
116}
117
118
119/**
Brian Paul12256fc2009-03-20 17:08:30 -0600120 * Remap register indexes according to map.
121 * \param prog the program to search/replace
122 * \param file the type of register file to search/replace
123 * \param map maps old register indexes to new indexes
124 */
125static void
126replace_regs(struct gl_program *prog, gl_register_file file, const GLint map[])
127{
128 GLuint i;
129
130 for (i = 0; i < prog->NumInstructions; i++) {
131 struct prog_instruction *inst = prog->Instructions + i;
132 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
133 GLuint j;
134 for (j = 0; j < numSrc; j++) {
135 if (inst->SrcReg[j].File == file) {
136 GLuint index = inst->SrcReg[j].Index;
137 ASSERT(map[index] >= 0);
138 inst->SrcReg[j].Index = map[index];
139 }
140 }
141 if (inst->DstReg.File == file) {
142 const GLuint index = inst->DstReg.Index;
143 ASSERT(map[index] >= 0);
144 inst->DstReg.Index = map[index];
145 }
146 }
147}
148
149
150/**
Brian Paul82f1c0b2009-03-06 16:18:22 -0700151 * Consolidate temporary registers to use low numbers. For example, if the
152 * shader only uses temps 4, 5, 8, replace them with 0, 1, 2.
153 */
154static void
155_mesa_consolidate_registers(struct gl_program *prog)
156{
157 GLboolean tempUsed[MAX_PROGRAM_TEMPS];
Brian Paul12256fc2009-03-20 17:08:30 -0600158 GLint tempMap[MAX_PROGRAM_TEMPS];
Brian Paul82f1c0b2009-03-06 16:18:22 -0700159 GLuint tempMax = 0, i;
160
161 if (dbg) {
162 _mesa_printf("Optimize: Begin register consolidation\n");
163 }
164
165 memset(tempUsed, 0, sizeof(tempUsed));
166
Brian Paul12256fc2009-03-20 17:08:30 -0600167 for (i = 0; i < MAX_PROGRAM_TEMPS; i++) {
168 tempMap[i] = -1;
169 }
170
Brian Paul82f1c0b2009-03-06 16:18:22 -0700171 /* set tempUsed[i] if temporary [i] is referenced */
172 for (i = 0; i < prog->NumInstructions; i++) {
173 const struct prog_instruction *inst = prog->Instructions + i;
174 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
175 GLuint j;
176 for (j = 0; j < numSrc; j++) {
177 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) {
178 const GLuint index = inst->SrcReg[j].Index;
179 ASSERT(index < MAX_PROGRAM_TEMPS);
180 tempUsed[index] = GL_TRUE;
181 tempMax = MAX2(tempMax, index);
182 break;
183 }
184 }
185 if (inst->DstReg.File == PROGRAM_TEMPORARY) {
186 const GLuint index = inst->DstReg.Index;
187 ASSERT(index < MAX_PROGRAM_TEMPS);
188 tempUsed[index] = GL_TRUE;
189 tempMax = MAX2(tempMax, index);
190 }
191 }
192
193 /* allocate a new index for each temp that's used */
194 {
195 GLuint freeTemp = 0;
196 for (i = 0; i <= tempMax; i++) {
197 if (tempUsed[i]) {
198 tempMap[i] = freeTemp++;
199 /*_mesa_printf("replace %u with %u\n", i, tempMap[i]);*/
200 }
201 }
202 if (freeTemp == tempMax + 1) {
203 /* no consolidation possible */
204 return;
205 }
206 if (dbg) {
207 _mesa_printf("Replace regs 0..%u with 0..%u\n", tempMax, freeTemp-1);
208 }
209 }
210
Brian Paul12256fc2009-03-20 17:08:30 -0600211 replace_regs(prog, PROGRAM_TEMPORARY, tempMap);
212
Brian Paul82f1c0b2009-03-06 16:18:22 -0700213 if (dbg) {
214 _mesa_printf("Optimize: End register consolidation\n");
215 }
216}
217
218
219/**
220 * Remove dead instructions from the given program.
221 * This is very primitive for now. Basically look for temp registers
222 * that are written to but never read. Remove any instructions that
223 * write to such registers. Be careful with condition code setters.
224 */
225static void
226_mesa_remove_dead_code(struct gl_program *prog)
227{
Eric Anholtee0a9e62009-06-12 12:37:25 -0700228 GLboolean tempRead[MAX_PROGRAM_TEMPS][4];
Brian Paul82f1c0b2009-03-06 16:18:22 -0700229 GLboolean *removeInst; /* per-instruction removal flag */
Eric Anholtee0a9e62009-06-12 12:37:25 -0700230 GLuint i, rem = 0, comp;
Brian Paul82f1c0b2009-03-06 16:18:22 -0700231
Brian Paul82f1c0b2009-03-06 16:18:22 -0700232 memset(tempRead, 0, sizeof(tempRead));
233
234 if (dbg) {
235 _mesa_printf("Optimize: Begin dead code removal\n");
236 /*_mesa_print_program(prog);*/
237 }
238
239 removeInst = (GLboolean *)
240 _mesa_calloc(prog->NumInstructions * sizeof(GLboolean));
241
242 /* Determine which temps are read and written */
243 for (i = 0; i < prog->NumInstructions; i++) {
244 const struct prog_instruction *inst = prog->Instructions + i;
245 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
246 GLuint j;
247
248 /* check src regs */
249 for (j = 0; j < numSrc; j++) {
250 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) {
251 const GLuint index = inst->SrcReg[j].Index;
brian18768392009-11-07 08:18:03 -0700252 GLuint read_mask;
Brian Paul82f1c0b2009-03-06 16:18:22 -0700253 ASSERT(index < MAX_PROGRAM_TEMPS);
brian18768392009-11-07 08:18:03 -0700254 read_mask = get_src_arg_mask(inst, j);
Brian Paul82f1c0b2009-03-06 16:18:22 -0700255
256 if (inst->SrcReg[j].RelAddr) {
257 if (dbg)
258 _mesa_printf("abort remove dead code (indirect temp)\n");
Eric Anholtee0a9e62009-06-12 12:37:25 -0700259 goto done;
Brian Paul82f1c0b2009-03-06 16:18:22 -0700260 }
261
Eric Anholtee0a9e62009-06-12 12:37:25 -0700262 for (comp = 0; comp < 4; comp++) {
263 GLuint swz = (inst->SrcReg[j].Swizzle >> (3 * comp)) & 0x7;
264
Eric Anholt6b0bcfa2009-11-06 14:06:08 -0800265 if ((read_mask & (1 << comp)) == 0)
266 continue;
267
Eric Anholtee0a9e62009-06-12 12:37:25 -0700268 switch (swz) {
269 case SWIZZLE_X:
270 tempRead[index][0] = GL_TRUE;
271 break;
272 case SWIZZLE_Y:
273 tempRead[index][1] = GL_TRUE;
274 break;
275 case SWIZZLE_Z:
276 tempRead[index][2] = GL_TRUE;
277 break;
278 case SWIZZLE_W:
279 tempRead[index][3] = GL_TRUE;
280 break;
281 }
282 }
Brian Paul82f1c0b2009-03-06 16:18:22 -0700283 }
284 }
285
286 /* check dst reg */
287 if (inst->DstReg.File == PROGRAM_TEMPORARY) {
288 const GLuint index = inst->DstReg.Index;
289 ASSERT(index < MAX_PROGRAM_TEMPS);
290
291 if (inst->DstReg.RelAddr) {
292 if (dbg)
293 _mesa_printf("abort remove dead code (indirect temp)\n");
Eric Anholtee0a9e62009-06-12 12:37:25 -0700294 goto done;
Brian Paul82f1c0b2009-03-06 16:18:22 -0700295 }
296
Brian Paul82f1c0b2009-03-06 16:18:22 -0700297 if (inst->CondUpdate) {
298 /* If we're writing to this register and setting condition
299 * codes we cannot remove the instruction. Prevent removal
300 * by setting the 'read' flag.
301 */
Eric Anholtee0a9e62009-06-12 12:37:25 -0700302 tempRead[index][0] = GL_TRUE;
303 tempRead[index][1] = GL_TRUE;
304 tempRead[index][2] = GL_TRUE;
305 tempRead[index][3] = GL_TRUE;
Brian Paul82f1c0b2009-03-06 16:18:22 -0700306 }
307 }
308 }
309
Brian Paul82f1c0b2009-03-06 16:18:22 -0700310 /* find instructions that write to dead registers, flag for removal */
311 for (i = 0; i < prog->NumInstructions; i++) {
Eric Anholtee0a9e62009-06-12 12:37:25 -0700312 struct prog_instruction *inst = prog->Instructions + i;
313 const GLuint numDst = _mesa_num_inst_dst_regs(inst->Opcode);
314
315 if (numDst != 0 && inst->DstReg.File == PROGRAM_TEMPORARY) {
316 GLint chan, index = inst->DstReg.Index;
317
318 for (chan = 0; chan < 4; chan++) {
319 if (!tempRead[index][chan] &&
320 inst->DstReg.WriteMask & (1 << chan)) {
321 if (dbg) {
322 _mesa_printf("Remove writemask on %u.%c\n", i,
323 chan == 3 ? 'w' : 'x' + chan);
324 }
325 inst->DstReg.WriteMask &= ~(1 << chan);
326 rem++;
327 }
328 }
329
330 if (inst->DstReg.WriteMask == 0) {
331 /* If we cleared all writes, the instruction can be removed. */
332 if (dbg)
333 _mesa_printf("Remove instruction %u: \n", i);
334 removeInst[i] = GL_TRUE;
335 }
Brian Paul82f1c0b2009-03-06 16:18:22 -0700336 }
337 }
338
339 /* now remove the instructions which aren't needed */
340 rem = remove_instructions(prog, removeInst);
341
Brian Paul82f1c0b2009-03-06 16:18:22 -0700342 if (dbg) {
Eric Anholtee0a9e62009-06-12 12:37:25 -0700343 _mesa_printf("Optimize: End dead code removal.\n");
344 _mesa_printf(" %u channel writes removed\n", rem);
345 _mesa_printf(" %u instructions removed\n", rem);
Brian Paul82f1c0b2009-03-06 16:18:22 -0700346 /*_mesa_print_program(prog);*/
347 }
Eric Anholtee0a9e62009-06-12 12:37:25 -0700348
349done:
350 _mesa_free(removeInst);
Brian Paul82f1c0b2009-03-06 16:18:22 -0700351}
352
353
354enum temp_use
355{
356 READ,
357 WRITE,
358 FLOW,
359 END
360};
361
362/**
363 * Scan forward in program from 'start' for the next occurance of TEMP[index].
364 * Return READ, WRITE, FLOW or END to indicate the next usage or an indicator
365 * that we can't look further.
366 */
367static enum temp_use
368find_next_temp_use(const struct gl_program *prog, GLuint start, GLuint index)
369{
370 GLuint i;
371
372 for (i = start; i < prog->NumInstructions; i++) {
373 const struct prog_instruction *inst = prog->Instructions + i;
374 switch (inst->Opcode) {
375 case OPCODE_BGNLOOP:
376 case OPCODE_ENDLOOP:
377 case OPCODE_BGNSUB:
378 case OPCODE_ENDSUB:
379 return FLOW;
380 default:
381 {
382 const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
383 GLuint j;
384 for (j = 0; j < numSrc; j++) {
385 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY &&
386 inst->SrcReg[j].Index == index)
387 return READ;
388 }
389 if (inst->DstReg.File == PROGRAM_TEMPORARY &&
390 inst->DstReg.Index == index)
391 return WRITE;
392 }
393 }
394 }
395
396 return END;
397}
398
399
400/**
Eric Anholte4e312d2009-05-16 01:47:44 -0700401 * Try to remove use of extraneous MOV instructions, to free them up for dead
402 * code removal.
403 */
404static void
405_mesa_remove_extra_move_use(struct gl_program *prog)
406{
407 GLuint i;
408
409 if (dbg) {
410 _mesa_printf("Optimize: Begin remove extra move use\n");
411 _mesa_print_program(prog);
412 }
413
414 /*
415 * Look for sequences such as this:
416 * MOV tmpX, arg0;
417 * FOO tmpY, tmpX, arg1;
418 * and convert into:
419 * MOV tmpX, arg0;
420 * FOO tmpY, arg0, arg1;
421 */
422
423 for (i = 0; i < prog->NumInstructions - 1; i++) {
424 const struct prog_instruction *mov = prog->Instructions + i;
425 struct prog_instruction *inst2 = prog->Instructions + i + 1;
426 int arg;
427
428 if (mov->Opcode != OPCODE_MOV ||
429 mov->DstReg.File != PROGRAM_TEMPORARY ||
430 mov->DstReg.RelAddr ||
431 mov->DstReg.CondMask != COND_TR ||
432 mov->SaturateMode != SATURATE_OFF)
433 continue;
434
435 for (arg = 0; arg < _mesa_num_inst_src_regs(inst2->Opcode); arg++) {
436 int comp;
Eric Anholt6b0bcfa2009-11-06 14:06:08 -0800437 int read_mask = get_src_arg_mask(inst2, arg);
Eric Anholte4e312d2009-05-16 01:47:44 -0700438
439 if (inst2->SrcReg[arg].File != mov->DstReg.File ||
440 inst2->SrcReg[arg].Index != mov->DstReg.Index ||
441 inst2->SrcReg[arg].RelAddr ||
442 inst2->SrcReg[arg].Abs)
443 continue;
444
445 /* Check that all the sources for this arg of inst2 come from inst1
446 * or constants.
447 */
448 for (comp = 0; comp < 4; comp++) {
449 int src_swz = GET_SWZ(inst2->SrcReg[arg].Swizzle, comp);
450
451 /* If the MOV didn't write that channel, can't use it. */
Eric Anholt6b0bcfa2009-11-06 14:06:08 -0800452 if ((read_mask & (1 << comp)) &&
453 src_swz <= SWIZZLE_W &&
Eric Anholte4e312d2009-05-16 01:47:44 -0700454 (mov->DstReg.WriteMask & (1 << src_swz)) == 0)
455 break;
456 }
457 if (comp != 4)
458 continue;
459
460 /* Adjust the swizzles of inst2 to point at MOV's source */
461 for (comp = 0; comp < 4; comp++) {
462 int inst2_swz = GET_SWZ(inst2->SrcReg[arg].Swizzle, comp);
463
464 if (inst2_swz <= SWIZZLE_W) {
465 GLuint s = GET_SWZ(mov->SrcReg[0].Swizzle, inst2_swz);
466 inst2->SrcReg[arg].Swizzle &= ~(7 << (3 * comp));
467 inst2->SrcReg[arg].Swizzle |= s << (3 * comp);
468 inst2->SrcReg[arg].Negate ^= (((mov->SrcReg[0].Negate >>
469 inst2_swz) & 0x1) << comp);
470 }
471 }
472 inst2->SrcReg[arg].File = mov->SrcReg[0].File;
473 inst2->SrcReg[arg].Index = mov->SrcReg[0].Index;
474 }
475 }
476
477 if (dbg) {
478 _mesa_printf("Optimize: End remove extra move use.\n");
479 /*_mesa_print_program(prog);*/
480 }
481}
482
483/**
Brian Paul82f1c0b2009-03-06 16:18:22 -0700484 * Try to remove extraneous MOV instructions from the given program.
485 */
486static void
487_mesa_remove_extra_moves(struct gl_program *prog)
488{
489 GLboolean *removeInst; /* per-instruction removal flag */
490 GLuint i, rem, loopNesting = 0, subroutineNesting = 0;
491
492 if (dbg) {
493 _mesa_printf("Optimize: Begin remove extra moves\n");
494 _mesa_print_program(prog);
495 }
496
497 removeInst = (GLboolean *)
498 _mesa_calloc(prog->NumInstructions * sizeof(GLboolean));
499
500 /*
501 * Look for sequences such as this:
502 * FOO tmpX, arg0, arg1;
503 * MOV tmpY, tmpX;
504 * and convert into:
505 * FOO tmpY, arg0, arg1;
506 */
507
508 for (i = 0; i < prog->NumInstructions; i++) {
509 const struct prog_instruction *inst = prog->Instructions + i;
510
511 switch (inst->Opcode) {
512 case OPCODE_BGNLOOP:
513 loopNesting++;
514 break;
515 case OPCODE_ENDLOOP:
516 loopNesting--;
517 break;
518 case OPCODE_BGNSUB:
519 subroutineNesting++;
520 break;
521 case OPCODE_ENDSUB:
522 subroutineNesting--;
523 break;
524 case OPCODE_MOV:
525 if (i > 0 &&
526 loopNesting == 0 &&
527 subroutineNesting == 0 &&
528 inst->SrcReg[0].File == PROGRAM_TEMPORARY &&
529 inst->SrcReg[0].Swizzle == SWIZZLE_XYZW) {
530 /* see if this MOV can be removed */
531 const GLuint tempIndex = inst->SrcReg[0].Index;
532 struct prog_instruction *prevInst;
533 GLuint prevI;
534
535 /* get pointer to previous instruction */
536 prevI = i - 1;
537 while (removeInst[prevI] && prevI > 0)
538 prevI--;
539 prevInst = prog->Instructions + prevI;
540
541 if (prevInst->DstReg.File == PROGRAM_TEMPORARY &&
542 prevInst->DstReg.Index == tempIndex &&
543 prevInst->DstReg.WriteMask == WRITEMASK_XYZW) {
544
545 enum temp_use next_use =
546 find_next_temp_use(prog, i + 1, tempIndex);
547
548 if (next_use == WRITE || next_use == END) {
549 /* OK, we can safely remove this MOV instruction.
550 * Transform:
551 * prevI: FOO tempIndex, x, y;
552 * i: MOV z, tempIndex;
553 * Into:
554 * prevI: FOO z, x, y;
555 */
556
557 /* patch up prev inst */
558 prevInst->DstReg.File = inst->DstReg.File;
559 prevInst->DstReg.Index = inst->DstReg.Index;
560
561 /* flag this instruction for removal */
562 removeInst[i] = GL_TRUE;
563
564 if (dbg) {
565 _mesa_printf("Remove MOV at %u\n", i);
566 _mesa_printf("new prev inst %u: ", prevI);
567 _mesa_print_instruction(prevInst);
568 }
569 }
570 }
571 }
572 break;
573 default:
574 ; /* nothing */
575 }
576 }
577
578 /* now remove the instructions which aren't needed */
579 rem = remove_instructions(prog, removeInst);
580
Brian Paul05749542009-10-01 14:52:28 -0600581 _mesa_free(removeInst);
582
Brian Paul82f1c0b2009-03-06 16:18:22 -0700583 if (dbg) {
584 _mesa_printf("Optimize: End remove extra moves. %u instructions removed\n", rem);
585 /*_mesa_print_program(prog);*/
586 }
587}
588
589
Brian Paul12256fc2009-03-20 17:08:30 -0600590/** A live register interval */
591struct interval
592{
593 GLuint Reg; /** The temporary register index */
594 GLuint Start, End; /** Start/end instruction numbers */
595};
596
597
598/** A list of register intervals */
599struct interval_list
600{
601 GLuint Num;
602 struct interval Intervals[MAX_PROGRAM_TEMPS];
603};
604
605
606static void
607append_interval(struct interval_list *list, const struct interval *inv)
608{
609 list->Intervals[list->Num++] = *inv;
610}
611
612
613/** Insert interval inv into list, sorted by interval end */
614static void
615insert_interval_by_end(struct interval_list *list, const struct interval *inv)
616{
617 /* XXX we could do a binary search insertion here since list is sorted */
618 GLint i = list->Num - 1;
619 while (i >= 0 && list->Intervals[i].End > inv->End) {
620 list->Intervals[i + 1] = list->Intervals[i];
621 i--;
622 }
623 list->Intervals[i + 1] = *inv;
624 list->Num++;
625
626#ifdef DEBUG
627 {
628 GLuint i;
629 for (i = 0; i + 1 < list->Num; i++) {
630 ASSERT(list->Intervals[i].End <= list->Intervals[i + 1].End);
631 }
632 }
633#endif
634}
635
636
637/** Remove the given interval from the interval list */
638static void
639remove_interval(struct interval_list *list, const struct interval *inv)
640{
641 /* XXX we could binary search since list is sorted */
642 GLuint k;
643 for (k = 0; k < list->Num; k++) {
644 if (list->Intervals[k].Reg == inv->Reg) {
645 /* found, remove it */
646 ASSERT(list->Intervals[k].Start == inv->Start);
647 ASSERT(list->Intervals[k].End == inv->End);
648 while (k < list->Num - 1) {
649 list->Intervals[k] = list->Intervals[k + 1];
650 k++;
651 }
652 list->Num--;
653 return;
654 }
655 }
656}
657
658
659/** called by qsort() */
660static int
661compare_start(const void *a, const void *b)
662{
663 const struct interval *ia = (const struct interval *) a;
664 const struct interval *ib = (const struct interval *) b;
665 if (ia->Start < ib->Start)
666 return -1;
667 else if (ia->Start > ib->Start)
668 return +1;
669 else
670 return 0;
671}
672
673/** sort the interval list according to interval starts */
674static void
675sort_interval_list_by_start(struct interval_list *list)
676{
677 qsort(list->Intervals, list->Num, sizeof(struct interval), compare_start);
678#ifdef DEBUG
679 {
680 GLuint i;
681 for (i = 0; i + 1 < list->Num; i++) {
682 ASSERT(list->Intervals[i].Start <= list->Intervals[i + 1].Start);
683 }
684 }
685#endif
686}
687
688
689/**
690 * Update the intermediate interval info for register 'index' and
691 * instruction 'ic'.
692 */
693static void
694update_interval(GLint intBegin[], GLint intEnd[], GLuint index, GLuint ic)
695{
696 ASSERT(index < MAX_PROGRAM_TEMPS);
697 if (intBegin[index] == -1) {
698 ASSERT(intEnd[index] == -1);
699 intBegin[index] = intEnd[index] = ic;
700 }
701 else {
702 intEnd[index] = ic;
703 }
704}
705
706
707/**
Brian Paul7da3f942009-04-24 16:28:36 -0600708 * Find first/last instruction that references each temporary register.
Brian Paul12256fc2009-03-20 17:08:30 -0600709 */
Brian Paul7da3f942009-04-24 16:28:36 -0600710GLboolean
711_mesa_find_temp_intervals(const struct prog_instruction *instructions,
712 GLuint numInstructions,
713 GLint intBegin[MAX_PROGRAM_TEMPS],
714 GLint intEnd[MAX_PROGRAM_TEMPS])
Brian Paul12256fc2009-03-20 17:08:30 -0600715{
716 struct loop_info
717 {
718 GLuint Start, End; /**< Start, end instructions of loop */
719 };
720 struct loop_info loopStack[MAX_LOOP_NESTING];
721 GLuint loopStackDepth = 0;
Brian Paul12256fc2009-03-20 17:08:30 -0600722 GLuint i;
723
Brian Paul12256fc2009-03-20 17:08:30 -0600724 for (i = 0; i < MAX_PROGRAM_TEMPS; i++){
725 intBegin[i] = intEnd[i] = -1;
726 }
727
728 /* Scan instructions looking for temporary registers */
Brian Paul7da3f942009-04-24 16:28:36 -0600729 for (i = 0; i < numInstructions; i++) {
730 const struct prog_instruction *inst = instructions + i;
Brian Paul12256fc2009-03-20 17:08:30 -0600731 if (inst->Opcode == OPCODE_BGNLOOP) {
732 loopStack[loopStackDepth].Start = i;
733 loopStack[loopStackDepth].End = inst->BranchTarget;
734 loopStackDepth++;
735 }
736 else if (inst->Opcode == OPCODE_ENDLOOP) {
737 loopStackDepth--;
738 }
739 else if (inst->Opcode == OPCODE_CAL) {
740 return GL_FALSE;
741 }
742 else {
Brian Paul7da3f942009-04-24 16:28:36 -0600743 const GLuint numSrc = 3;/*_mesa_num_inst_src_regs(inst->Opcode);*/
Brian Paul12256fc2009-03-20 17:08:30 -0600744 GLuint j;
745 for (j = 0; j < numSrc; j++) {
746 if (inst->SrcReg[j].File == PROGRAM_TEMPORARY) {
747 const GLuint index = inst->SrcReg[j].Index;
748 if (inst->SrcReg[j].RelAddr)
749 return GL_FALSE;
750 update_interval(intBegin, intEnd, index, i);
751 if (loopStackDepth > 0) {
752 /* extend temp register's interval to end of loop */
753 GLuint loopEnd = loopStack[loopStackDepth - 1].End;
754 update_interval(intBegin, intEnd, index, loopEnd);
755 }
756 }
757 }
758 if (inst->DstReg.File == PROGRAM_TEMPORARY) {
759 const GLuint index = inst->DstReg.Index;
760 if (inst->DstReg.RelAddr)
761 return GL_FALSE;
762 update_interval(intBegin, intEnd, index, i);
763 if (loopStackDepth > 0) {
764 /* extend temp register's interval to end of loop */
765 GLuint loopEnd = loopStack[loopStackDepth - 1].End;
766 update_interval(intBegin, intEnd, index, loopEnd);
767 }
768 }
769 }
770 }
771
Brian Paul7da3f942009-04-24 16:28:36 -0600772 return GL_TRUE;
773}
774
775
776/**
777 * Find the live intervals for each temporary register in the program.
778 * For register R, the interval [A,B] indicates that R is referenced
779 * from instruction A through instruction B.
780 * Special consideration is needed for loops and subroutines.
781 * \return GL_TRUE if success, GL_FALSE if we cannot proceed for some reason
782 */
783static GLboolean
784find_live_intervals(struct gl_program *prog,
785 struct interval_list *liveIntervals)
786{
787 GLint intBegin[MAX_PROGRAM_TEMPS], intEnd[MAX_PROGRAM_TEMPS];
788 GLuint i;
789
790 /*
791 * Note: we'll return GL_FALSE below if we find relative indexing
792 * into the TEMP register file. We can't handle that yet.
793 * We also give up on subroutines for now.
794 */
795
796 if (dbg) {
797 _mesa_printf("Optimize: Begin find intervals\n");
798 }
799
800 /* build intermediate arrays */
801 if (!_mesa_find_temp_intervals(prog->Instructions, prog->NumInstructions,
802 intBegin, intEnd))
803 return GL_FALSE;
804
Brian Paul12256fc2009-03-20 17:08:30 -0600805 /* Build live intervals list from intermediate arrays */
806 liveIntervals->Num = 0;
807 for (i = 0; i < MAX_PROGRAM_TEMPS; i++) {
808 if (intBegin[i] >= 0) {
809 struct interval inv;
810 inv.Reg = i;
811 inv.Start = intBegin[i];
812 inv.End = intEnd[i];
813 append_interval(liveIntervals, &inv);
814 }
815 }
816
817 /* Sort the list according to interval starts */
818 sort_interval_list_by_start(liveIntervals);
819
820 if (dbg) {
821 /* print interval info */
822 for (i = 0; i < liveIntervals->Num; i++) {
823 const struct interval *inv = liveIntervals->Intervals + i;
824 _mesa_printf("Reg[%d] live [%d, %d]:",
825 inv->Reg, inv->Start, inv->End);
826 if (1) {
827 int j;
828 for (j = 0; j < inv->Start; j++)
829 _mesa_printf(" ");
830 for (j = inv->Start; j <= inv->End; j++)
831 _mesa_printf("x");
832 }
833 _mesa_printf("\n");
834 }
835 }
836
837 return GL_TRUE;
838}
839
840
Brian Paulf4468382009-04-07 11:15:27 -0600841/** Scan the array of used register flags to find free entry */
842static GLint
Brian Paul12256fc2009-03-20 17:08:30 -0600843alloc_register(GLboolean usedRegs[MAX_PROGRAM_TEMPS])
844{
845 GLuint k;
846 for (k = 0; k < MAX_PROGRAM_TEMPS; k++) {
847 if (!usedRegs[k]) {
848 usedRegs[k] = GL_TRUE;
849 return k;
850 }
851 }
Brian Paulf4468382009-04-07 11:15:27 -0600852 return -1;
Brian Paul12256fc2009-03-20 17:08:30 -0600853}
854
855
856/**
857 * This function implements "Linear Scan Register Allocation" to reduce
858 * the number of temporary registers used by the program.
859 *
860 * We compute the "live interval" for all temporary registers then
861 * examine the overlap of the intervals to allocate new registers.
862 * Basically, if two intervals do not overlap, they can use the same register.
863 */
864static void
865_mesa_reallocate_registers(struct gl_program *prog)
866{
867 struct interval_list liveIntervals;
868 GLint registerMap[MAX_PROGRAM_TEMPS];
869 GLboolean usedRegs[MAX_PROGRAM_TEMPS];
870 GLuint i;
Brian Paulf4468382009-04-07 11:15:27 -0600871 GLint maxTemp = -1;
Brian Paul12256fc2009-03-20 17:08:30 -0600872
873 if (dbg) {
874 _mesa_printf("Optimize: Begin live-interval register reallocation\n");
875 _mesa_print_program(prog);
876 }
877
878 for (i = 0; i < MAX_PROGRAM_TEMPS; i++){
879 registerMap[i] = -1;
880 usedRegs[i] = GL_FALSE;
881 }
882
883 if (!find_live_intervals(prog, &liveIntervals)) {
884 if (dbg)
885 _mesa_printf("Aborting register reallocation\n");
886 return;
887 }
888
889 {
890 struct interval_list activeIntervals;
891 activeIntervals.Num = 0;
892
893 /* loop over live intervals, allocating a new register for each */
894 for (i = 0; i < liveIntervals.Num; i++) {
895 const struct interval *live = liveIntervals.Intervals + i;
896
897 if (dbg)
898 _mesa_printf("Consider register %u\n", live->Reg);
899
900 /* Expire old intervals. Intervals which have ended with respect
901 * to the live interval can have their remapped registers freed.
902 */
903 {
904 GLint j;
905 for (j = 0; j < activeIntervals.Num; j++) {
906 const struct interval *inv = activeIntervals.Intervals + j;
907 if (inv->End >= live->Start) {
908 /* Stop now. Since the activeInterval list is sorted
909 * we know we don't have to go further.
910 */
911 break;
912 }
913 else {
914 /* Interval 'inv' has expired */
915 const GLint regNew = registerMap[inv->Reg];
916 ASSERT(regNew >= 0);
917
918 if (dbg)
919 _mesa_printf(" expire interval for reg %u\n", inv->Reg);
920
921 /* remove interval j from active list */
922 remove_interval(&activeIntervals, inv);
923 j--; /* counter-act j++ in for-loop above */
924
925 /* return register regNew to the free pool */
926 if (dbg)
927 _mesa_printf(" free reg %d\n", regNew);
928 ASSERT(usedRegs[regNew] == GL_TRUE);
929 usedRegs[regNew] = GL_FALSE;
930 }
931 }
932 }
933
934 /* find a free register for this live interval */
935 {
Brian Paulf4468382009-04-07 11:15:27 -0600936 const GLint k = alloc_register(usedRegs);
937 if (k < 0) {
Brian Paul12256fc2009-03-20 17:08:30 -0600938 /* out of registers, give up */
939 return;
940 }
941 registerMap[live->Reg] = k;
942 maxTemp = MAX2(maxTemp, k);
943 if (dbg)
Brian Paulf4468382009-04-07 11:15:27 -0600944 _mesa_printf(" remap register %u -> %d\n", live->Reg, k);
Brian Paul12256fc2009-03-20 17:08:30 -0600945 }
946
947 /* Insert this live interval into the active list which is sorted
948 * by increasing end points.
949 */
950 insert_interval_by_end(&activeIntervals, live);
951 }
952 }
953
954 if (maxTemp + 1 < liveIntervals.Num) {
955 /* OK, we've reduced the number of registers needed.
956 * Scan the program and replace all the old temporary register
957 * indexes with the new indexes.
958 */
959 replace_regs(prog, PROGRAM_TEMPORARY, registerMap);
960
961 prog->NumTemporaries = maxTemp + 1;
962 }
963
964 if (dbg) {
965 _mesa_printf("Optimize: End live-interval register reallocation\n");
966 _mesa_printf("Num temp regs before: %u after: %u\n",
967 liveIntervals.Num, maxTemp + 1);
968 _mesa_print_program(prog);
969 }
970}
971
972
Brian Paul82f1c0b2009-03-06 16:18:22 -0700973/**
974 * Apply optimizations to the given program to eliminate unnecessary
975 * instructions, temp regs, etc.
976 */
977void
978_mesa_optimize_program(GLcontext *ctx, struct gl_program *program)
979{
Eric Anholte4e312d2009-05-16 01:47:44 -0700980 _mesa_remove_extra_move_use(program);
981
Brian Paul82f1c0b2009-03-06 16:18:22 -0700982 if (1)
983 _mesa_remove_dead_code(program);
984
Brian Paul0f0e24f2009-04-07 11:10:27 -0600985 if (0) /* not tested much yet */
Brian Paul82f1c0b2009-03-06 16:18:22 -0700986 _mesa_remove_extra_moves(program);
987
Brian Paul0f0e24f2009-04-07 11:10:27 -0600988 if (0)
Brian Paul82f1c0b2009-03-06 16:18:22 -0700989 _mesa_consolidate_registers(program);
Brian Paul0f0e24f2009-04-07 11:10:27 -0600990 else
Brian Paul12256fc2009-03-20 17:08:30 -0600991 _mesa_reallocate_registers(program);
Brian Paul82f1c0b2009-03-06 16:18:22 -0700992}