r300/compiler: refactor vertex shader compilation
[mesa.git] / src / mesa / drivers / dri / r300 / compiler / radeon_emulate_branches.c
1 /*
2 * Copyright 2009 Nicolai Hähnle <nhaehnle@gmail.com>
3 *
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:
10 *
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
13 * Software.
14 *
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. */
22
23 #include "radeon_emulate_branches.h"
24
25 #include <stdio.h>
26
27 #include "radeon_compiler.h"
28 #include "radeon_dataflow.h"
29
30 #define VERBOSE 0
31
32 #define DBG(...) do { if (VERBOSE) fprintf(stderr, __VA_ARGS__); } while(0)
33
34
35 struct proxy_info {
36 unsigned int Proxied:1;
37 unsigned int Index:RC_REGISTER_INDEX_BITS;
38 };
39
40 struct register_proxies {
41 struct proxy_info Temporary[RC_REGISTER_MAX_INDEX];
42 };
43
44 struct branch_info {
45 struct rc_instruction * If;
46 struct rc_instruction * Else;
47 };
48
49 struct emulate_branch_state {
50 struct radeon_compiler * C;
51
52 struct branch_info * Branches;
53 unsigned int BranchCount;
54 unsigned int BranchReserved;
55 };
56
57
58 static void handle_if(struct emulate_branch_state * s, struct rc_instruction * inst)
59 {
60 memory_pool_array_reserve(&s->C->Pool, struct branch_info,
61 s->Branches, s->BranchCount, s->BranchReserved, 1);
62
63 DBG("%s\n", __FUNCTION__);
64
65 struct branch_info * branch = &s->Branches[s->BranchCount++];
66 memset(branch, 0, sizeof(struct branch_info));
67 branch->If = inst;
68
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];
77
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;
83 }
84
85 static void handle_else(struct emulate_branch_state * s, struct rc_instruction * inst)
86 {
87 if (!s->BranchCount) {
88 rc_error(s->C, "Encountered ELSE outside of branches");
89 return;
90 }
91
92 DBG("%s\n", __FUNCTION__);
93
94 struct branch_info * branch = &s->Branches[s->BranchCount - 1];
95 branch->Else = inst;
96 }
97
98
99 struct state_and_proxies {
100 struct emulate_branch_state * S;
101 struct register_proxies * Proxies;
102 };
103
104 static struct proxy_info * get_proxy_info(struct state_and_proxies * sap,
105 rc_register_file file, unsigned int index)
106 {
107 if (file == RC_FILE_TEMPORARY) {
108 return &sap->Proxies->Temporary[index];
109 } else {
110 return 0;
111 }
112 }
113
114 static void scan_write(void * userdata, struct rc_instruction * inst,
115 rc_register_file file, unsigned int index, unsigned int comp)
116 {
117 struct state_and_proxies * sap = userdata;
118 struct proxy_info * proxy = get_proxy_info(sap, file, index);
119
120 if (proxy && !proxy->Proxied) {
121 proxy->Proxied = 1;
122 proxy->Index = rc_find_free_temporary(sap->S->C);
123 }
124 }
125
126 static void remap_proxy_function(void * userdata, struct rc_instruction * inst,
127 rc_register_file * pfile, unsigned int * pindex)
128 {
129 struct state_and_proxies * sap = userdata;
130 struct proxy_info * proxy = get_proxy_info(sap, *pfile, *pindex);
131
132 if (proxy && proxy->Proxied) {
133 *pfile = RC_FILE_TEMPORARY;
134 *pindex = proxy->Index;
135 }
136 }
137
138 /**
139 * Redirect all writes in the instruction range [begin, end) to proxy
140 * temporary registers.
141 */
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)
146 {
147 struct state_and_proxies sap;
148
149 sap.S = s;
150 sap.Proxies = proxies;
151
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);
155 }
156
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;
166 }
167 }
168 }
169
170
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)
177 {
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;
190 }
191
192 static void handle_endif(struct emulate_branch_state * s, struct rc_instruction * inst)
193 {
194 if (!s->BranchCount) {
195 rc_error(s->C, "Encountered ENDIF outside of branches");
196 return;
197 }
198
199 DBG("%s\n", __FUNCTION__);
200
201 struct branch_info * branch = &s->Branches[s->BranchCount - 1];
202 struct register_proxies IfProxies;
203 struct register_proxies ElseProxies;
204
205 memset(&IfProxies, 0, sizeof(IfProxies));
206 memset(&ElseProxies, 0, sizeof(ElseProxies));
207
208 allocate_and_insert_proxies(s, &IfProxies, branch->If->Next, branch->Else ? branch->Else : inst);
209
210 if (branch->Else)
211 allocate_and_insert_proxies(s, &ElseProxies, branch->Else->Next, inst);
212
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]);
218 }
219 }
220
221 /* Remove all traces of the branch instructions */
222 rc_remove_instruction(branch->If);
223 if (branch->Else)
224 rc_remove_instruction(branch->Else);
225 rc_remove_instruction(inst);
226
227 s->BranchCount--;
228
229 if (VERBOSE) {
230 DBG("Program after ENDIF handling:\n");
231 rc_print_program(&s->C->Program);
232 }
233 }
234
235
236 struct remap_output_data {
237 unsigned int Output:RC_REGISTER_INDEX_BITS;
238 unsigned int Temporary:RC_REGISTER_INDEX_BITS;
239 };
240
241 static void remap_output_function(void * userdata, struct rc_instruction * inst,
242 rc_register_file * pfile, unsigned int * pindex)
243 {
244 struct remap_output_data * data = userdata;
245
246 if (*pfile == RC_FILE_OUTPUT && *pindex == data->Output) {
247 *pfile = RC_FILE_TEMPORARY;
248 *pindex = data->Temporary;
249 }
250 }
251
252
253 /**
254 * Output registers cannot be read from and so cannot be dealt with like
255 * temporary registers.
256 *
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
260 * the program.
261 */
262 static void fix_output_writes(struct emulate_branch_state * s, struct rc_instruction * inst)
263 {
264 if (!s->BranchCount)
265 return;
266
267 const struct rc_opcode_info * opcode = rc_get_opcode_info(inst->U.I.Opcode);
268
269 if (!opcode->HasDstReg)
270 return;
271
272 if (inst->U.I.DstReg.File == RC_FILE_OUTPUT) {
273 struct remap_output_data remap;
274
275 remap.Output = inst->U.I.DstReg.Index;
276 remap.Temporary = rc_find_free_temporary(s->C);
277
278 for(struct rc_instruction * inst = s->C->Program.Instructions.Next;
279 inst != &s->C->Program.Instructions;
280 inst = inst->Next) {
281 rc_remap_registers(inst, &remap_output_function, &remap);
282 }
283
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;
291 }
292 }
293
294 /**
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.
298 */
299 void rc_emulate_branches(struct radeon_compiler *c, void *user)
300 {
301 struct emulate_branch_state s;
302
303 memset(&s, 0, sizeof(s));
304 s.C = c;
305
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;
310 ptr = ptr->Next;
311
312 if (inst->Type == RC_INSTRUCTION_NORMAL) {
313 switch(inst->U.I.Opcode) {
314 case RC_OPCODE_IF:
315 handle_if(&s, inst);
316 break;
317 case RC_OPCODE_ELSE:
318 handle_else(&s, inst);
319 break;
320 case RC_OPCODE_ENDIF:
321 handle_endif(&s, inst);
322 break;
323 default:
324 fix_output_writes(&s, inst);
325 break;
326 }
327 } else {
328 rc_error(c, "%s: unhandled instruction type\n", __FUNCTION__);
329 }
330 }
331 }