2 * Mesa 3-D graphics library
5 * Copyright (C) 2009 VMware, Inc. All Rights Reserved.
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:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
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.
27 #include "main/glheader.h"
28 #include "main/context.h"
29 #include "main/macros.h"
31 #include "prog_instruction.h"
32 #include "prog_optimize.h"
33 #include "prog_print.h"
36 static GLboolean dbg
= GL_FALSE
;
40 * In 'prog' remove instruction[i] if removeFlags[i] == TRUE.
41 * \return number of instructions removed
44 remove_instructions(struct gl_program
*prog
, const GLboolean
*removeFlags
)
46 GLint i
, removeEnd
= 0, removeCount
= 0;
47 GLuint totalRemoved
= 0;
50 for (i
= prog
->NumInstructions
- 1; i
>= 0; i
--) {
53 if (removeCount
== 0) {
54 /* begin a run of instructions to remove */
59 /* extend the run of instructions to remove */
64 /* don't remove this instruction, but check if the preceeding
65 * instructions are to be removed.
67 if (removeCount
> 0) {
68 GLint removeStart
= removeEnd
- removeCount
+ 1;
69 _mesa_delete_instructions(prog
, removeStart
, removeCount
);
70 removeStart
= removeCount
= 0; /* reset removal info */
79 * Consolidate temporary registers to use low numbers. For example, if the
80 * shader only uses temps 4, 5, 8, replace them with 0, 1, 2.
83 _mesa_consolidate_registers(struct gl_program
*prog
)
85 GLboolean tempUsed
[MAX_PROGRAM_TEMPS
];
86 GLuint tempMap
[MAX_PROGRAM_TEMPS
];
87 GLuint tempMax
= 0, i
;
90 _mesa_printf("Optimize: Begin register consolidation\n");
93 memset(tempUsed
, 0, sizeof(tempUsed
));
95 /* set tempUsed[i] if temporary [i] is referenced */
96 for (i
= 0; i
< prog
->NumInstructions
; i
++) {
97 const struct prog_instruction
*inst
= prog
->Instructions
+ i
;
98 const GLuint numSrc
= _mesa_num_inst_src_regs(inst
->Opcode
);
100 for (j
= 0; j
< numSrc
; j
++) {
101 if (inst
->SrcReg
[j
].File
== PROGRAM_TEMPORARY
) {
102 const GLuint index
= inst
->SrcReg
[j
].Index
;
103 ASSERT(index
< MAX_PROGRAM_TEMPS
);
104 tempUsed
[index
] = GL_TRUE
;
105 tempMax
= MAX2(tempMax
, index
);
109 if (inst
->DstReg
.File
== PROGRAM_TEMPORARY
) {
110 const GLuint index
= inst
->DstReg
.Index
;
111 ASSERT(index
< MAX_PROGRAM_TEMPS
);
112 tempUsed
[index
] = GL_TRUE
;
113 tempMax
= MAX2(tempMax
, index
);
117 /* allocate a new index for each temp that's used */
120 for (i
= 0; i
<= tempMax
; i
++) {
122 tempMap
[i
] = freeTemp
++;
123 /*_mesa_printf("replace %u with %u\n", i, tempMap[i]);*/
126 if (freeTemp
== tempMax
+ 1) {
127 /* no consolidation possible */
131 _mesa_printf("Replace regs 0..%u with 0..%u\n", tempMax
, freeTemp
-1);
135 /* now replace occurances of old temp indexes with new indexes */
136 for (i
= 0; i
< prog
->NumInstructions
; i
++) {
137 struct prog_instruction
*inst
= prog
->Instructions
+ i
;
138 const GLuint numSrc
= _mesa_num_inst_src_regs(inst
->Opcode
);
140 for (j
= 0; j
< numSrc
; j
++) {
141 if (inst
->SrcReg
[j
].File
== PROGRAM_TEMPORARY
) {
142 GLuint index
= inst
->SrcReg
[j
].Index
;
143 assert(index
<= tempMax
);
144 assert(tempUsed
[index
]);
145 inst
->SrcReg
[j
].Index
= tempMap
[index
];
148 if (inst
->DstReg
.File
== PROGRAM_TEMPORARY
) {
149 const GLuint index
= inst
->DstReg
.Index
;
150 assert(tempUsed
[index
]);
151 assert(index
<= tempMax
);
152 inst
->DstReg
.Index
= tempMap
[index
];
156 _mesa_printf("Optimize: End register consolidation\n");
162 * Remove dead instructions from the given program.
163 * This is very primitive for now. Basically look for temp registers
164 * that are written to but never read. Remove any instructions that
165 * write to such registers. Be careful with condition code setters.
168 _mesa_remove_dead_code(struct gl_program
*prog
)
170 GLboolean tempWritten
[MAX_PROGRAM_TEMPS
], tempRead
[MAX_PROGRAM_TEMPS
];
171 GLboolean
*removeInst
; /* per-instruction removal flag */
174 memset(tempWritten
, 0, sizeof(tempWritten
));
175 memset(tempRead
, 0, sizeof(tempRead
));
178 _mesa_printf("Optimize: Begin dead code removal\n");
179 /*_mesa_print_program(prog);*/
182 removeInst
= (GLboolean
*)
183 _mesa_calloc(prog
->NumInstructions
* sizeof(GLboolean
));
185 /* Determine which temps are read and written */
186 for (i
= 0; i
< prog
->NumInstructions
; i
++) {
187 const struct prog_instruction
*inst
= prog
->Instructions
+ i
;
188 const GLuint numSrc
= _mesa_num_inst_src_regs(inst
->Opcode
);
192 for (j
= 0; j
< numSrc
; j
++) {
193 if (inst
->SrcReg
[j
].File
== PROGRAM_TEMPORARY
) {
194 const GLuint index
= inst
->SrcReg
[j
].Index
;
195 ASSERT(index
< MAX_PROGRAM_TEMPS
);
197 if (inst
->SrcReg
[j
].RelAddr
) {
199 _mesa_printf("abort remove dead code (indirect temp)\n");
203 tempRead
[index
] = GL_TRUE
;
208 if (inst
->DstReg
.File
== PROGRAM_TEMPORARY
) {
209 const GLuint index
= inst
->DstReg
.Index
;
210 ASSERT(index
< MAX_PROGRAM_TEMPS
);
212 if (inst
->DstReg
.RelAddr
) {
214 _mesa_printf("abort remove dead code (indirect temp)\n");
218 tempWritten
[index
] = GL_TRUE
;
219 if (inst
->CondUpdate
) {
220 /* If we're writing to this register and setting condition
221 * codes we cannot remove the instruction. Prevent removal
222 * by setting the 'read' flag.
224 tempRead
[index
] = GL_TRUE
;
230 for (i
= 0; i
< MAX_PROGRAM_TEMPS
; i
++) {
231 if (tempWritten
[i
] && !tempRead
[i
])
232 _mesa_printf("Remove writes to tmp %u\n", i
);
236 /* find instructions that write to dead registers, flag for removal */
237 for (i
= 0; i
< prog
->NumInstructions
; i
++) {
238 const struct prog_instruction
*inst
= prog
->Instructions
+ i
;
239 if (inst
->DstReg
.File
== PROGRAM_TEMPORARY
) {
240 GLint index
= inst
->DstReg
.Index
;
241 removeInst
[i
] = (tempWritten
[index
] && !tempRead
[index
]);
242 if (dbg
&& removeInst
[i
]) {
243 _mesa_printf("Remove inst %u: ", i
);
244 _mesa_print_instruction(inst
);
249 /* now remove the instructions which aren't needed */
250 rem
= remove_instructions(prog
, removeInst
);
252 _mesa_free(removeInst
);
255 _mesa_printf("Optimize: End dead code removal. %u instructions removed\n", rem
);
256 /*_mesa_print_program(prog);*/
270 * Scan forward in program from 'start' for the next occurance of TEMP[index].
271 * Return READ, WRITE, FLOW or END to indicate the next usage or an indicator
272 * that we can't look further.
275 find_next_temp_use(const struct gl_program
*prog
, GLuint start
, GLuint index
)
279 for (i
= start
; i
< prog
->NumInstructions
; i
++) {
280 const struct prog_instruction
*inst
= prog
->Instructions
+ i
;
281 switch (inst
->Opcode
) {
289 const GLuint numSrc
= _mesa_num_inst_src_regs(inst
->Opcode
);
291 for (j
= 0; j
< numSrc
; j
++) {
292 if (inst
->SrcReg
[j
].File
== PROGRAM_TEMPORARY
&&
293 inst
->SrcReg
[j
].Index
== index
)
296 if (inst
->DstReg
.File
== PROGRAM_TEMPORARY
&&
297 inst
->DstReg
.Index
== index
)
308 * Try to remove extraneous MOV instructions from the given program.
311 _mesa_remove_extra_moves(struct gl_program
*prog
)
313 GLboolean
*removeInst
; /* per-instruction removal flag */
314 GLuint i
, rem
, loopNesting
= 0, subroutineNesting
= 0;
317 _mesa_printf("Optimize: Begin remove extra moves\n");
318 _mesa_print_program(prog
);
321 removeInst
= (GLboolean
*)
322 _mesa_calloc(prog
->NumInstructions
* sizeof(GLboolean
));
325 * Look for sequences such as this:
326 * FOO tmpX, arg0, arg1;
329 * FOO tmpY, arg0, arg1;
332 for (i
= 0; i
< prog
->NumInstructions
; i
++) {
333 const struct prog_instruction
*inst
= prog
->Instructions
+ i
;
335 switch (inst
->Opcode
) {
351 subroutineNesting
== 0 &&
352 inst
->SrcReg
[0].File
== PROGRAM_TEMPORARY
&&
353 inst
->SrcReg
[0].Swizzle
== SWIZZLE_XYZW
) {
354 /* see if this MOV can be removed */
355 const GLuint tempIndex
= inst
->SrcReg
[0].Index
;
356 struct prog_instruction
*prevInst
;
359 /* get pointer to previous instruction */
361 while (removeInst
[prevI
] && prevI
> 0)
363 prevInst
= prog
->Instructions
+ prevI
;
365 if (prevInst
->DstReg
.File
== PROGRAM_TEMPORARY
&&
366 prevInst
->DstReg
.Index
== tempIndex
&&
367 prevInst
->DstReg
.WriteMask
== WRITEMASK_XYZW
) {
369 enum temp_use next_use
=
370 find_next_temp_use(prog
, i
+ 1, tempIndex
);
372 if (next_use
== WRITE
|| next_use
== END
) {
373 /* OK, we can safely remove this MOV instruction.
375 * prevI: FOO tempIndex, x, y;
376 * i: MOV z, tempIndex;
378 * prevI: FOO z, x, y;
381 /* patch up prev inst */
382 prevInst
->DstReg
.File
= inst
->DstReg
.File
;
383 prevInst
->DstReg
.Index
= inst
->DstReg
.Index
;
385 /* flag this instruction for removal */
386 removeInst
[i
] = GL_TRUE
;
389 _mesa_printf("Remove MOV at %u\n", i
);
390 _mesa_printf("new prev inst %u: ", prevI
);
391 _mesa_print_instruction(prevInst
);
402 /* now remove the instructions which aren't needed */
403 rem
= remove_instructions(prog
, removeInst
);
406 _mesa_printf("Optimize: End remove extra moves. %u instructions removed\n", rem
);
407 /*_mesa_print_program(prog);*/
413 * Apply optimizations to the given program to eliminate unnecessary
414 * instructions, temp regs, etc.
417 _mesa_optimize_program(GLcontext
*ctx
, struct gl_program
*program
)
420 _mesa_remove_dead_code(program
);
422 if (0) /* not test much yet */
423 _mesa_remove_extra_moves(program
);
426 _mesa_consolidate_registers(program
);