2 * Copyright 2009 Nicolai Hähnle <nhaehnle@gmail.com>
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE. */
23 #include "radeon_emulate_branches.h"
27 #include "radeon_compiler.h"
28 #include "radeon_dataflow.h"
32 #define DBG(...) do { if (VERBOSE) fprintf(stderr, __VA_ARGS__); } while(0)
36 unsigned int Proxied
:1;
37 unsigned int Index
:RC_REGISTER_INDEX_BITS
;
40 struct register_proxies
{
41 struct proxy_info Temporary
[RC_REGISTER_MAX_INDEX
];
45 struct rc_instruction
* If
;
46 struct rc_instruction
* Else
;
49 struct emulate_branch_state
{
50 struct radeon_compiler
* C
;
52 struct branch_info
* Branches
;
53 unsigned int BranchCount
;
54 unsigned int BranchReserved
;
58 static void handle_if(struct emulate_branch_state
* s
, struct rc_instruction
* inst
)
60 memory_pool_array_reserve(&s
->C
->Pool
, struct branch_info
,
61 s
->Branches
, s
->BranchCount
, s
->BranchReserved
, 1);
63 DBG("%s\n", __FUNCTION__
);
65 struct branch_info
* branch
= &s
->Branches
[s
->BranchCount
++];
66 memset(branch
, 0, sizeof(struct branch_info
));
69 /* Make a safety copy of the decision register, because we will need
70 * it at ENDIF time and it might be overwritten in both branches. */
71 struct rc_instruction
* inst_mov
= rc_insert_new_instruction(s
->C
, inst
->Prev
);
72 inst_mov
->U
.I
.Opcode
= RC_OPCODE_MOV
;
73 inst_mov
->U
.I
.DstReg
.File
= RC_FILE_TEMPORARY
;
74 inst_mov
->U
.I
.DstReg
.Index
= rc_find_free_temporary(s
->C
);
75 inst_mov
->U
.I
.DstReg
.WriteMask
= RC_MASK_X
;
76 inst_mov
->U
.I
.SrcReg
[0] = inst
->U
.I
.SrcReg
[0];
78 inst
->U
.I
.SrcReg
[0].File
= RC_FILE_TEMPORARY
;
79 inst
->U
.I
.SrcReg
[0].Index
= inst_mov
->U
.I
.DstReg
.Index
;
80 inst
->U
.I
.SrcReg
[0].Swizzle
= 0;
81 inst
->U
.I
.SrcReg
[0].Abs
= 0;
82 inst
->U
.I
.SrcReg
[0].Negate
= 0;
85 static void handle_else(struct emulate_branch_state
* s
, struct rc_instruction
* inst
)
87 if (!s
->BranchCount
) {
88 rc_error(s
->C
, "Encountered ELSE outside of branches");
92 DBG("%s\n", __FUNCTION__
);
94 struct branch_info
* branch
= &s
->Branches
[s
->BranchCount
- 1];
99 struct state_and_proxies
{
100 struct emulate_branch_state
* S
;
101 struct register_proxies
* Proxies
;
104 static struct proxy_info
* get_proxy_info(struct state_and_proxies
* sap
,
105 rc_register_file file
, unsigned int index
)
107 if (file
== RC_FILE_TEMPORARY
) {
108 return &sap
->Proxies
->Temporary
[index
];
114 static void scan_write(void * userdata
, struct rc_instruction
* inst
,
115 rc_register_file file
, unsigned int index
, unsigned int comp
)
117 struct state_and_proxies
* sap
= userdata
;
118 struct proxy_info
* proxy
= get_proxy_info(sap
, file
, index
);
120 if (proxy
&& !proxy
->Proxied
) {
122 proxy
->Index
= rc_find_free_temporary(sap
->S
->C
);
126 static void remap_proxy_function(void * userdata
, struct rc_instruction
* inst
,
127 rc_register_file
* pfile
, unsigned int * pindex
)
129 struct state_and_proxies
* sap
= userdata
;
130 struct proxy_info
* proxy
= get_proxy_info(sap
, *pfile
, *pindex
);
132 if (proxy
&& proxy
->Proxied
) {
133 *pfile
= RC_FILE_TEMPORARY
;
134 *pindex
= proxy
->Index
;
139 * Redirect all writes in the instruction range [begin, end) to proxy
140 * temporary registers.
142 static void allocate_and_insert_proxies(struct emulate_branch_state
* s
,
143 struct register_proxies
* proxies
,
144 struct rc_instruction
* begin
,
145 struct rc_instruction
* end
)
147 struct state_and_proxies sap
;
150 sap
.Proxies
= proxies
;
152 for(struct rc_instruction
* inst
= begin
; inst
!= end
; inst
= inst
->Next
) {
153 rc_for_all_writes_mask(inst
, scan_write
, &sap
);
154 rc_remap_registers(inst
, remap_proxy_function
, &sap
);
157 for(unsigned int index
= 0; index
< RC_REGISTER_MAX_INDEX
; ++index
) {
158 if (proxies
->Temporary
[index
].Proxied
) {
159 struct rc_instruction
* inst_mov
= rc_insert_new_instruction(s
->C
, begin
->Prev
);
160 inst_mov
->U
.I
.Opcode
= RC_OPCODE_MOV
;
161 inst_mov
->U
.I
.DstReg
.File
= RC_FILE_TEMPORARY
;
162 inst_mov
->U
.I
.DstReg
.Index
= proxies
->Temporary
[index
].Index
;
163 inst_mov
->U
.I
.DstReg
.WriteMask
= RC_MASK_XYZW
;
164 inst_mov
->U
.I
.SrcReg
[0].File
= RC_FILE_TEMPORARY
;
165 inst_mov
->U
.I
.SrcReg
[0].Index
= index
;
171 static void inject_cmp(struct emulate_branch_state
* s
,
172 struct rc_instruction
* inst_if
,
173 struct rc_instruction
* inst_endif
,
174 rc_register_file file
, unsigned int index
,
175 struct proxy_info ifproxy
,
176 struct proxy_info elseproxy
)
178 struct rc_instruction
* inst_cmp
= rc_insert_new_instruction(s
->C
, inst_endif
);
179 inst_cmp
->U
.I
.Opcode
= RC_OPCODE_CMP
;
180 inst_cmp
->U
.I
.DstReg
.File
= file
;
181 inst_cmp
->U
.I
.DstReg
.Index
= index
;
182 inst_cmp
->U
.I
.DstReg
.WriteMask
= RC_MASK_XYZW
;
183 inst_cmp
->U
.I
.SrcReg
[0] = inst_if
->U
.I
.SrcReg
[0];
184 inst_cmp
->U
.I
.SrcReg
[0].Abs
= 1;
185 inst_cmp
->U
.I
.SrcReg
[0].Negate
= RC_MASK_XYZW
;
186 inst_cmp
->U
.I
.SrcReg
[1].File
= RC_FILE_TEMPORARY
;
187 inst_cmp
->U
.I
.SrcReg
[1].Index
= ifproxy
.Proxied
? ifproxy
.Index
: index
;
188 inst_cmp
->U
.I
.SrcReg
[2].File
= RC_FILE_TEMPORARY
;
189 inst_cmp
->U
.I
.SrcReg
[2].Index
= elseproxy
.Proxied
? elseproxy
.Index
: index
;
192 static void handle_endif(struct emulate_branch_state
* s
, struct rc_instruction
* inst
)
194 if (!s
->BranchCount
) {
195 rc_error(s
->C
, "Encountered ENDIF outside of branches");
199 DBG("%s\n", __FUNCTION__
);
201 struct branch_info
* branch
= &s
->Branches
[s
->BranchCount
- 1];
202 struct register_proxies IfProxies
;
203 struct register_proxies ElseProxies
;
205 memset(&IfProxies
, 0, sizeof(IfProxies
));
206 memset(&ElseProxies
, 0, sizeof(ElseProxies
));
208 allocate_and_insert_proxies(s
, &IfProxies
, branch
->If
->Next
, branch
->Else
? branch
->Else
: inst
);
211 allocate_and_insert_proxies(s
, &ElseProxies
, branch
->Else
->Next
, inst
);
213 /* Insert the CMP instructions at the end. */
214 for(unsigned int index
= 0; index
< RC_REGISTER_MAX_INDEX
; ++index
) {
215 if (IfProxies
.Temporary
[index
].Proxied
|| ElseProxies
.Temporary
[index
].Proxied
) {
216 inject_cmp(s
, branch
->If
, inst
, RC_FILE_TEMPORARY
, index
,
217 IfProxies
.Temporary
[index
], ElseProxies
.Temporary
[index
]);
221 /* Remove all traces of the branch instructions */
222 rc_remove_instruction(branch
->If
);
224 rc_remove_instruction(branch
->Else
);
225 rc_remove_instruction(inst
);
230 DBG("Program after ENDIF handling:\n");
231 rc_print_program(&s
->C
->Program
);
236 struct remap_output_data
{
237 unsigned int Output
:RC_REGISTER_INDEX_BITS
;
238 unsigned int Temporary
:RC_REGISTER_INDEX_BITS
;
241 static void remap_output_function(void * userdata
, struct rc_instruction
* inst
,
242 rc_register_file
* pfile
, unsigned int * pindex
)
244 struct remap_output_data
* data
= userdata
;
246 if (*pfile
== RC_FILE_OUTPUT
&& *pindex
== data
->Output
) {
247 *pfile
= RC_FILE_TEMPORARY
;
248 *pindex
= data
->Temporary
;
254 * Output registers cannot be read from and so cannot be dealt with like
255 * temporary registers.
257 * We do the simplest thing: If an output registers is written within
258 * a branch, then *all* writes to this register are proxied to a
259 * temporary register, and a final MOV is appended to the end of
262 static void fix_output_writes(struct emulate_branch_state
* s
, struct rc_instruction
* inst
)
267 const struct rc_opcode_info
* opcode
= rc_get_opcode_info(inst
->U
.I
.Opcode
);
269 if (!opcode
->HasDstReg
)
272 if (inst
->U
.I
.DstReg
.File
== RC_FILE_OUTPUT
) {
273 struct remap_output_data remap
;
275 remap
.Output
= inst
->U
.I
.DstReg
.Index
;
276 remap
.Temporary
= rc_find_free_temporary(s
->C
);
278 for(struct rc_instruction
* inst
= s
->C
->Program
.Instructions
.Next
;
279 inst
!= &s
->C
->Program
.Instructions
;
281 rc_remap_registers(inst
, &remap_output_function
, &remap
);
284 struct rc_instruction
* inst_mov
= rc_insert_new_instruction(s
->C
, s
->C
->Program
.Instructions
.Prev
);
285 inst_mov
->U
.I
.Opcode
= RC_OPCODE_MOV
;
286 inst_mov
->U
.I
.DstReg
.File
= RC_FILE_OUTPUT
;
287 inst_mov
->U
.I
.DstReg
.Index
= remap
.Output
;
288 inst_mov
->U
.I
.DstReg
.WriteMask
= RC_MASK_XYZW
;
289 inst_mov
->U
.I
.SrcReg
[0].File
= RC_FILE_TEMPORARY
;
290 inst_mov
->U
.I
.SrcReg
[0].Index
= remap
.Temporary
;
295 * Remove branch instructions; instead, execute both branches
296 * on different register sets and choose between their results
297 * using CMP instructions in place of the original ENDIF.
299 void rc_emulate_branches(struct radeon_compiler
*c
, void *user
)
301 struct emulate_branch_state s
;
303 memset(&s
, 0, sizeof(s
));
306 /* Untypical loop because we may remove the current instruction */
307 struct rc_instruction
* ptr
= c
->Program
.Instructions
.Next
;
308 while(ptr
!= &c
->Program
.Instructions
) {
309 struct rc_instruction
* inst
= ptr
;
312 if (inst
->Type
== RC_INSTRUCTION_NORMAL
) {
313 switch(inst
->U
.I
.Opcode
) {
318 handle_else(&s
, inst
);
320 case RC_OPCODE_ENDIF
:
321 handle_endif(&s
, inst
);
324 fix_output_writes(&s
, inst
);
328 rc_error(c
, "%s: unhandled instruction type\n", __FUNCTION__
);