2 * Copyright (C) 2008 Nicolai Haehnle.
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial
16 * portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 * "Not-quite SSA" and Dead-Code Elimination.
33 * @note This code uses SWIZZLE_NIL in a source register to indicate that
34 * the corresponding component is ignored by the corresponding instruction.
37 #include "radeon_nqssadce.h"
41 * Return the @ref register_state for the given register (or 0 for untracked
42 * registers, i.e. constants).
44 static struct register_state
*get_reg_state(struct nqssadce_state
* s
, GLuint file
, GLuint index
)
47 case PROGRAM_TEMPORARY
: return &s
->Temps
[index
];
48 case PROGRAM_OUTPUT
: return &s
->Outputs
[index
];
49 case PROGRAM_ADDRESS
: return &s
->Address
;
56 * Left multiplication of a register with a swizzle
58 * @note Works correctly only for X, Y, Z, W swizzles, not for constant swizzles.
60 struct prog_src_register
lmul_swizzle(GLuint swizzle
, struct prog_src_register srcreg
)
62 struct prog_src_register tmp
= srcreg
;
65 tmp
.Negate
= NEGATE_NONE
;
66 for(i
= 0; i
< 4; ++i
) {
67 GLuint swz
= GET_SWZ(swizzle
, i
);
69 tmp
.Swizzle
|= GET_SWZ(srcreg
.Swizzle
, swz
) << (i
*3);
70 tmp
.Negate
|= GET_BIT(srcreg
.Negate
, swz
) << i
;
72 tmp
.Swizzle
|= swz
<< (i
*3);
79 static struct prog_instruction
* track_used_srcreg(struct nqssadce_state
* s
,
80 struct prog_instruction
*inst
, GLint src
, GLuint sourced
)
83 GLuint deswz_source
= 0;
85 for(i
= 0; i
< 4; ++i
) {
86 if (GET_BIT(sourced
, i
)) {
87 GLuint swz
= GET_SWZ(inst
->SrcReg
[src
].Swizzle
, i
);
88 deswz_source
|= 1 << swz
;
90 inst
->SrcReg
[src
].Swizzle
&= ~(7 << (3*i
));
91 inst
->SrcReg
[src
].Swizzle
|= SWIZZLE_NIL
<< (3*i
);
95 if (!s
->Descr
->IsNativeSwizzle(inst
->Opcode
, inst
->SrcReg
[src
])) {
96 struct prog_dst_register dstreg
= inst
->DstReg
;
97 dstreg
.File
= PROGRAM_TEMPORARY
;
98 dstreg
.Index
= _mesa_find_free_register(s
->Program
, PROGRAM_TEMPORARY
);
99 dstreg
.WriteMask
= sourced
;
101 s
->Descr
->BuildSwizzle(s
, dstreg
, inst
->SrcReg
[src
]);
103 inst
= s
->Program
->Instructions
+ s
->IP
;
104 inst
->SrcReg
[src
].File
= PROGRAM_TEMPORARY
;
105 inst
->SrcReg
[src
].Index
= dstreg
.Index
;
106 inst
->SrcReg
[src
].Swizzle
= 0;
107 inst
->SrcReg
[src
].Negate
= NEGATE_NONE
;
108 inst
->SrcReg
[src
].Abs
= 0;
109 for(i
= 0; i
< 4; ++i
) {
110 if (GET_BIT(sourced
, i
))
111 inst
->SrcReg
[src
].Swizzle
|= i
<< (3*i
);
113 inst
->SrcReg
[src
].Swizzle
|= SWIZZLE_NIL
<< (3*i
);
115 deswz_source
= sourced
;
118 struct register_state
*regstate
;
120 if (inst
->SrcReg
[src
].RelAddr
) {
121 regstate
= get_reg_state(s
, PROGRAM_ADDRESS
, 0);
123 regstate
->Sourced
|= WRITEMASK_X
;
125 regstate
= get_reg_state(s
, inst
->SrcReg
[src
].File
, inst
->SrcReg
[src
].Index
);
127 regstate
->Sourced
|= deswz_source
& 0xf;
133 static void unalias_srcregs(struct prog_instruction
*inst
, GLuint oldindex
, GLuint newindex
)
135 int nsrc
= _mesa_num_inst_src_regs(inst
->Opcode
);
137 for(i
= 0; i
< nsrc
; ++i
)
138 if (inst
->SrcReg
[i
].File
== PROGRAM_TEMPORARY
&& inst
->SrcReg
[i
].Index
== oldindex
)
139 inst
->SrcReg
[i
].Index
= newindex
;
142 static void unalias_temporary(struct nqssadce_state
* s
, GLuint oldindex
)
144 GLuint newindex
= _mesa_find_free_register(s
->Program
, PROGRAM_TEMPORARY
);
146 for(ip
= 0; ip
< s
->IP
; ++ip
) {
147 struct prog_instruction
* inst
= s
->Program
->Instructions
+ ip
;
148 if (inst
->DstReg
.File
== PROGRAM_TEMPORARY
&& inst
->DstReg
.Index
== oldindex
)
149 inst
->DstReg
.Index
= newindex
;
150 unalias_srcregs(inst
, oldindex
, newindex
);
152 unalias_srcregs(s
->Program
->Instructions
+ s
->IP
, oldindex
, newindex
);
157 * Handle one instruction.
159 static void process_instruction(struct nqssadce_state
* s
)
161 struct prog_instruction
*inst
= s
->Program
->Instructions
+ s
->IP
;
163 if (inst
->Opcode
== OPCODE_END
)
166 if (inst
->Opcode
!= OPCODE_KIL
) {
167 struct register_state
*regstate
= get_reg_state(s
, inst
->DstReg
.File
, inst
->DstReg
.Index
);
169 _mesa_problem(s
->Ctx
, "NqssaDce: bad destination register (%i[%i])\n",
170 inst
->DstReg
.File
, inst
->DstReg
.Index
);
174 inst
->DstReg
.WriteMask
&= regstate
->Sourced
;
175 regstate
->Sourced
&= ~inst
->DstReg
.WriteMask
;
177 if (inst
->DstReg
.WriteMask
== 0) {
178 _mesa_delete_instructions(s
->Program
, s
->IP
, 1);
182 if (inst
->DstReg
.File
== PROGRAM_TEMPORARY
&& !regstate
->Sourced
)
183 unalias_temporary(s
, inst
->DstReg
.Index
);
186 /* Attention: Due to swizzle emulation code, the following
187 * might change the instruction stream under us, so we have
188 * to be careful with the inst pointer. */
189 switch (inst
->Opcode
) {
195 inst
= track_used_srcreg(s
, inst
, 0, inst
->DstReg
.WriteMask
);
203 inst
= track_used_srcreg(s
, inst
, 0, inst
->DstReg
.WriteMask
);
204 inst
= track_used_srcreg(s
, inst
, 1, inst
->DstReg
.WriteMask
);
208 inst
= track_used_srcreg(s
, inst
, 0, inst
->DstReg
.WriteMask
);
209 inst
= track_used_srcreg(s
, inst
, 1, inst
->DstReg
.WriteMask
);
210 inst
= track_used_srcreg(s
, inst
, 2, inst
->DstReg
.WriteMask
);
218 inst
= track_used_srcreg(s
, inst
, 0, 0x1);
221 inst
= track_used_srcreg(s
, inst
, 0, 0x7);
222 inst
= track_used_srcreg(s
, inst
, 1, 0x7);
225 inst
= track_used_srcreg(s
, inst
, 0, 0xf);
226 inst
= track_used_srcreg(s
, inst
, 1, 0xf);
232 inst
= track_used_srcreg(s
, inst
, 0, 0xf);
235 inst
= track_used_srcreg(s
, inst
, 0, 0x6);
236 inst
= track_used_srcreg(s
, inst
, 1, 0xa);
241 inst
= track_used_srcreg(s
, inst
, 0, 0x3);
244 inst
= track_used_srcreg(s
, inst
, 0, 0xb);
247 _mesa_problem(s
->Ctx
, "NqssaDce: Unknown opcode %d\n", inst
->Opcode
);
252 static void calculateInputsOutputs(struct gl_program
*p
)
254 struct prog_instruction
*inst
;
255 GLuint InputsRead
, OutputsWritten
;
257 inst
= p
->Instructions
;
260 while (inst
->Opcode
!= OPCODE_END
)
264 num_src_regs
= _mesa_num_inst_src_regs(inst
->Opcode
);
265 for (i
= 0; i
< num_src_regs
; ++i
) {
266 if (inst
->SrcReg
[i
].File
== PROGRAM_INPUT
)
267 InputsRead
|= 1 << inst
->SrcReg
[i
].Index
;
270 if (inst
->DstReg
.File
== PROGRAM_OUTPUT
)
271 OutputsWritten
|= 1 << inst
->DstReg
.Index
;
276 p
->InputsRead
= InputsRead
;
277 p
->OutputsWritten
= OutputsWritten
;
280 void radeonNqssaDce(GLcontext
*ctx
, struct gl_program
*p
, struct radeon_nqssadce_descr
* descr
)
282 struct nqssadce_state s
;
284 _mesa_bzero(&s
, sizeof(s
));
289 s
.IP
= p
->NumInstructions
;
293 process_instruction(&s
);
296 calculateInputsOutputs(p
);