Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/gallium/drivers/r300/compiler/radeon_emulate_branches.c
4574 views
1
/*
2
* Copyright 2009 Nicolai Hähnle <[email protected]>
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
struct branch_info * branch;
61
struct rc_instruction * inst_mov;
62
63
memory_pool_array_reserve(&s->C->Pool, struct branch_info,
64
s->Branches, s->BranchCount, s->BranchReserved, 1);
65
66
DBG("%s\n", __FUNCTION__);
67
68
branch = &s->Branches[s->BranchCount++];
69
memset(branch, 0, sizeof(struct branch_info));
70
branch->If = inst;
71
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];
80
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;
86
}
87
88
static void handle_else(struct emulate_branch_state * s, struct rc_instruction * inst)
89
{
90
struct branch_info * branch;
91
92
if (!s->BranchCount) {
93
rc_error(s->C, "Encountered ELSE outside of branches");
94
return;
95
}
96
97
DBG("%s\n", __FUNCTION__);
98
99
branch = &s->Branches[s->BranchCount - 1];
100
branch->Else = inst;
101
}
102
103
104
struct state_and_proxies {
105
struct emulate_branch_state * S;
106
struct register_proxies * Proxies;
107
};
108
109
static struct proxy_info * get_proxy_info(struct state_and_proxies * sap,
110
rc_register_file file, unsigned int index)
111
{
112
if (file == RC_FILE_TEMPORARY) {
113
return &sap->Proxies->Temporary[index];
114
} else {
115
return 0;
116
}
117
}
118
119
static void scan_write(void * userdata, struct rc_instruction * inst,
120
rc_register_file file, unsigned int index, unsigned int comp)
121
{
122
struct state_and_proxies * sap = userdata;
123
struct proxy_info * proxy = get_proxy_info(sap, file, index);
124
125
if (proxy && !proxy->Proxied) {
126
proxy->Proxied = 1;
127
proxy->Index = rc_find_free_temporary(sap->S->C);
128
}
129
}
130
131
static void remap_proxy_function(void * userdata, struct rc_instruction * inst,
132
rc_register_file * pfile, unsigned int * pindex)
133
{
134
struct state_and_proxies * sap = userdata;
135
struct proxy_info * proxy = get_proxy_info(sap, *pfile, *pindex);
136
137
if (proxy && proxy->Proxied) {
138
*pfile = RC_FILE_TEMPORARY;
139
*pindex = proxy->Index;
140
}
141
}
142
143
/**
144
* Redirect all writes in the instruction range [begin, end) to proxy
145
* temporary registers.
146
*/
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)
151
{
152
struct state_and_proxies sap;
153
154
sap.S = s;
155
sap.Proxies = proxies;
156
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);
160
}
161
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;
171
}
172
}
173
}
174
175
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)
182
{
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;
195
}
196
197
static void handle_endif(struct emulate_branch_state * s, struct rc_instruction * inst)
198
{
199
struct branch_info * branch;
200
struct register_proxies IfProxies;
201
struct register_proxies ElseProxies;
202
203
if (!s->BranchCount) {
204
rc_error(s->C, "Encountered ENDIF outside of branches");
205
return;
206
}
207
208
DBG("%s\n", __FUNCTION__);
209
210
branch = &s->Branches[s->BranchCount - 1];
211
212
memset(&IfProxies, 0, sizeof(IfProxies));
213
memset(&ElseProxies, 0, sizeof(ElseProxies));
214
215
allocate_and_insert_proxies(s, &IfProxies, branch->If->Next, branch->Else ? branch->Else : inst);
216
217
if (branch->Else)
218
allocate_and_insert_proxies(s, &ElseProxies, branch->Else->Next, inst);
219
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]);
225
}
226
}
227
228
/* Remove all traces of the branch instructions */
229
rc_remove_instruction(branch->If);
230
if (branch->Else)
231
rc_remove_instruction(branch->Else);
232
rc_remove_instruction(inst);
233
234
s->BranchCount--;
235
236
if (VERBOSE) {
237
DBG("Program after ENDIF handling:\n");
238
rc_print_program(&s->C->Program);
239
}
240
}
241
242
243
struct remap_output_data {
244
unsigned int Output:RC_REGISTER_INDEX_BITS;
245
unsigned int Temporary:RC_REGISTER_INDEX_BITS;
246
};
247
248
static void remap_output_function(void * userdata, struct rc_instruction * inst,
249
rc_register_file * pfile, unsigned int * pindex)
250
{
251
struct remap_output_data * data = userdata;
252
253
if (*pfile == RC_FILE_OUTPUT && *pindex == data->Output) {
254
*pfile = RC_FILE_TEMPORARY;
255
*pindex = data->Temporary;
256
}
257
}
258
259
260
/**
261
* Output registers cannot be read from and so cannot be dealt with like
262
* temporary registers.
263
*
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
267
* the program.
268
*/
269
static void fix_output_writes(struct emulate_branch_state * s, struct rc_instruction * inst)
270
{
271
const struct rc_opcode_info * opcode;
272
273
if (!s->BranchCount)
274
return;
275
276
opcode = rc_get_opcode_info(inst->U.I.Opcode);
277
278
if (!opcode->HasDstReg)
279
return;
280
281
if (inst->U.I.DstReg.File == RC_FILE_OUTPUT) {
282
struct remap_output_data remap;
283
struct rc_instruction * inst_mov;
284
285
remap.Output = inst->U.I.DstReg.Index;
286
remap.Temporary = rc_find_free_temporary(s->C);
287
288
for(struct rc_instruction * inst = s->C->Program.Instructions.Next;
289
inst != &s->C->Program.Instructions;
290
inst = inst->Next) {
291
rc_remap_registers(inst, &remap_output_function, &remap);
292
}
293
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;
301
}
302
}
303
304
/**
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.
308
*/
309
void rc_emulate_branches(struct radeon_compiler *c, void *user)
310
{
311
struct emulate_branch_state s;
312
struct rc_instruction * ptr;
313
314
memset(&s, 0, sizeof(s));
315
s.C = c;
316
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;
321
ptr = ptr->Next;
322
323
if (inst->Type == RC_INSTRUCTION_NORMAL) {
324
switch(inst->U.I.Opcode) {
325
case RC_OPCODE_IF:
326
handle_if(&s, inst);
327
break;
328
case RC_OPCODE_ELSE:
329
handle_else(&s, inst);
330
break;
331
case RC_OPCODE_ENDIF:
332
handle_endif(&s, inst);
333
break;
334
default:
335
fix_output_writes(&s, inst);
336
break;
337
}
338
} else {
339
rc_error(c, "%s: unhandled instruction type\n", __FUNCTION__);
340
}
341
}
342
}
343
344