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 struct branch_info
* branch
;
61 struct rc_instruction
* inst_mov
;
63 memory_pool_array_reserve(&s
->C
->Pool
, struct branch_info
,
64 s
->Branches
, s
->BranchCount
, s
->BranchReserved
, 1);
66 DBG("%s\n", __FUNCTION__
);
68 branch
= &s
->Branches
[s
->BranchCount
++];
69 memset(branch
, 0, sizeof(struct branch_info
));
72 /* Make a safety copy of the decision register, because we will need
73 * it at ENDIF time and it might be overwritten in both branches. */
74 inst_mov
= rc_insert_new_instruction(s
->C
, inst
->Prev
);
75 inst_mov
->U
.I
.Opcode
= RC_OPCODE_MOV
;
76 inst_mov
->U
.I
.DstReg
.File
= RC_FILE_TEMPORARY
;
77 inst_mov
->U
.I
.DstReg
.Index
= rc_find_free_temporary(s
->C
);
78 inst_mov
->U
.I
.DstReg
.WriteMask
= RC_MASK_X
;
79 inst_mov
->U
.I
.SrcReg
[0] = inst
->U
.I
.SrcReg
[0];
81 inst
->U
.I
.SrcReg
[0].File
= RC_FILE_TEMPORARY
;
82 inst
->U
.I
.SrcReg
[0].Index
= inst_mov
->U
.I
.DstReg
.Index
;
83 inst
->U
.I
.SrcReg
[0].Swizzle
= 0;
84 inst
->U
.I
.SrcReg
[0].Abs
= 0;
85 inst
->U
.I
.SrcReg
[0].Negate
= 0;
88 static void handle_else(struct emulate_branch_state
* s
, struct rc_instruction
* inst
)
90 struct branch_info
* branch
;
92 if (!s
->BranchCount
) {
93 rc_error(s
->C
, "Encountered ELSE outside of branches");
97 DBG("%s\n", __FUNCTION__
);
99 branch
= &s
->Branches
[s
->BranchCount
- 1];
104 struct state_and_proxies
{
105 struct emulate_branch_state
* S
;
106 struct register_proxies
* Proxies
;
109 static struct proxy_info
* get_proxy_info(struct state_and_proxies
* sap
,
110 rc_register_file file
, unsigned int index
)
112 if (file
== RC_FILE_TEMPORARY
) {
113 return &sap
->Proxies
->Temporary
[index
];
119 static void scan_write(void * userdata
, struct rc_instruction
* inst
,
120 rc_register_file file
, unsigned int index
, unsigned int comp
)
122 struct state_and_proxies
* sap
= userdata
;
123 struct proxy_info
* proxy
= get_proxy_info(sap
, file
, index
);
125 if (proxy
&& !proxy
->Proxied
) {
127 proxy
->Index
= rc_find_free_temporary(sap
->S
->C
);
131 static void remap_proxy_function(void * userdata
, struct rc_instruction
* inst
,
132 rc_register_file
* pfile
, unsigned int * pindex
)
134 struct state_and_proxies
* sap
= userdata
;
135 struct proxy_info
* proxy
= get_proxy_info(sap
, *pfile
, *pindex
);
137 if (proxy
&& proxy
->Proxied
) {
138 *pfile
= RC_FILE_TEMPORARY
;
139 *pindex
= proxy
->Index
;
144 * Redirect all writes in the instruction range [begin, end) to proxy
145 * temporary registers.
147 static void allocate_and_insert_proxies(struct emulate_branch_state
* s
,
148 struct register_proxies
* proxies
,
149 struct rc_instruction
* begin
,
150 struct rc_instruction
* end
)
152 struct state_and_proxies sap
;
155 sap
.Proxies
= proxies
;
157 for(struct rc_instruction
* inst
= begin
; inst
!= end
; inst
= inst
->Next
) {
158 rc_for_all_writes_mask(inst
, scan_write
, &sap
);
159 rc_remap_registers(inst
, remap_proxy_function
, &sap
);
162 for(unsigned int index
= 0; index
< RC_REGISTER_MAX_INDEX
; ++index
) {
163 if (proxies
->Temporary
[index
].Proxied
) {
164 struct rc_instruction
* inst_mov
= rc_insert_new_instruction(s
->C
, begin
->Prev
);
165 inst_mov
->U
.I
.Opcode
= RC_OPCODE_MOV
;
166 inst_mov
->U
.I
.DstReg
.File
= RC_FILE_TEMPORARY
;
167 inst_mov
->U
.I
.DstReg
.Index
= proxies
->Temporary
[index
].Index
;
168 inst_mov
->U
.I
.DstReg
.WriteMask
= RC_MASK_XYZW
;
169 inst_mov
->U
.I
.SrcReg
[0].File
= RC_FILE_TEMPORARY
;
170 inst_mov
->U
.I
.SrcReg
[0].Index
= index
;
176 static void inject_cmp(struct emulate_branch_state
* s
,
177 struct rc_instruction
* inst_if
,
178 struct rc_instruction
* inst_endif
,
179 rc_register_file file
, unsigned int index
,
180 struct proxy_info ifproxy
,
181 struct proxy_info elseproxy
)
183 struct rc_instruction
* inst_cmp
= rc_insert_new_instruction(s
->C
, inst_endif
);
184 inst_cmp
->U
.I
.Opcode
= RC_OPCODE_CMP
;
185 inst_cmp
->U
.I
.DstReg
.File
= file
;
186 inst_cmp
->U
.I
.DstReg
.Index
= index
;
187 inst_cmp
->U
.I
.DstReg
.WriteMask
= RC_MASK_XYZW
;
188 inst_cmp
->U
.I
.SrcReg
[0] = inst_if
->U
.I
.SrcReg
[0];
189 inst_cmp
->U
.I
.SrcReg
[0].Abs
= 1;
190 inst_cmp
->U
.I
.SrcReg
[0].Negate
= RC_MASK_XYZW
;
191 inst_cmp
->U
.I
.SrcReg
[1].File
= RC_FILE_TEMPORARY
;
192 inst_cmp
->U
.I
.SrcReg
[1].Index
= ifproxy
.Proxied
? ifproxy
.Index
: index
;
193 inst_cmp
->U
.I
.SrcReg
[2].File
= RC_FILE_TEMPORARY
;
194 inst_cmp
->U
.I
.SrcReg
[2].Index
= elseproxy
.Proxied
? elseproxy
.Index
: index
;
197 static void handle_endif(struct emulate_branch_state
* s
, struct rc_instruction
* inst
)
199 struct branch_info
* branch
;
200 struct register_proxies IfProxies
;
201 struct register_proxies ElseProxies
;
203 if (!s
->BranchCount
) {
204 rc_error(s
->C
, "Encountered ENDIF outside of branches");
208 DBG("%s\n", __FUNCTION__
);
210 branch
= &s
->Branches
[s
->BranchCount
- 1];
212 memset(&IfProxies
, 0, sizeof(IfProxies
));
213 memset(&ElseProxies
, 0, sizeof(ElseProxies
));
215 allocate_and_insert_proxies(s
, &IfProxies
, branch
->If
->Next
, branch
->Else
? branch
->Else
: inst
);
218 allocate_and_insert_proxies(s
, &ElseProxies
, branch
->Else
->Next
, inst
);
220 /* Insert the CMP instructions at the end. */
221 for(unsigned int index
= 0; index
< RC_REGISTER_MAX_INDEX
; ++index
) {
222 if (IfProxies
.Temporary
[index
].Proxied
|| ElseProxies
.Temporary
[index
].Proxied
) {
223 inject_cmp(s
, branch
->If
, inst
, RC_FILE_TEMPORARY
, index
,
224 IfProxies
.Temporary
[index
], ElseProxies
.Temporary
[index
]);
228 /* Remove all traces of the branch instructions */
229 rc_remove_instruction(branch
->If
);
231 rc_remove_instruction(branch
->Else
);
232 rc_remove_instruction(inst
);
237 DBG("Program after ENDIF handling:\n");
238 rc_print_program(&s
->C
->Program
);
243 struct remap_output_data
{
244 unsigned int Output
:RC_REGISTER_INDEX_BITS
;
245 unsigned int Temporary
:RC_REGISTER_INDEX_BITS
;
248 static void remap_output_function(void * userdata
, struct rc_instruction
* inst
,
249 rc_register_file
* pfile
, unsigned int * pindex
)
251 struct remap_output_data
* data
= userdata
;
253 if (*pfile
== RC_FILE_OUTPUT
&& *pindex
== data
->Output
) {
254 *pfile
= RC_FILE_TEMPORARY
;
255 *pindex
= data
->Temporary
;
261 * Output registers cannot be read from and so cannot be dealt with like
262 * temporary registers.
264 * We do the simplest thing: If an output registers is written within
265 * a branch, then *all* writes to this register are proxied to a
266 * temporary register, and a final MOV is appended to the end of
269 static void fix_output_writes(struct emulate_branch_state
* s
, struct rc_instruction
* inst
)
271 const struct rc_opcode_info
* opcode
;
276 opcode
= rc_get_opcode_info(inst
->U
.I
.Opcode
);
278 if (!opcode
->HasDstReg
)
281 if (inst
->U
.I
.DstReg
.File
== RC_FILE_OUTPUT
) {
282 struct remap_output_data remap
;
283 struct rc_instruction
* inst_mov
;
285 remap
.Output
= inst
->U
.I
.DstReg
.Index
;
286 remap
.Temporary
= rc_find_free_temporary(s
->C
);
288 for(struct rc_instruction
* inst
= s
->C
->Program
.Instructions
.Next
;
289 inst
!= &s
->C
->Program
.Instructions
;
291 rc_remap_registers(inst
, &remap_output_function
, &remap
);
294 inst_mov
= rc_insert_new_instruction(s
->C
, s
->C
->Program
.Instructions
.Prev
);
295 inst_mov
->U
.I
.Opcode
= RC_OPCODE_MOV
;
296 inst_mov
->U
.I
.DstReg
.File
= RC_FILE_OUTPUT
;
297 inst_mov
->U
.I
.DstReg
.Index
= remap
.Output
;
298 inst_mov
->U
.I
.DstReg
.WriteMask
= RC_MASK_XYZW
;
299 inst_mov
->U
.I
.SrcReg
[0].File
= RC_FILE_TEMPORARY
;
300 inst_mov
->U
.I
.SrcReg
[0].Index
= remap
.Temporary
;
305 * Remove branch instructions; instead, execute both branches
306 * on different register sets and choose between their results
307 * using CMP instructions in place of the original ENDIF.
309 void rc_emulate_branches(struct radeon_compiler
*c
, void *user
)
311 struct emulate_branch_state s
;
312 struct rc_instruction
* ptr
;
314 memset(&s
, 0, sizeof(s
));
317 /* Untypical loop because we may remove the current instruction */
318 ptr
= c
->Program
.Instructions
.Next
;
319 while(ptr
!= &c
->Program
.Instructions
) {
320 struct rc_instruction
* inst
= ptr
;
323 if (inst
->Type
== RC_INSTRUCTION_NORMAL
) {
324 switch(inst
->U
.I
.Opcode
) {
329 handle_else(&s
, inst
);
331 case RC_OPCODE_ENDIF
:
332 handle_endif(&s
, inst
);
335 fix_output_writes(&s
, inst
);
339 rc_error(c
, "%s: unhandled instruction type\n", __FUNCTION__
);