Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4358 Serge 1
/*
2
 * Copyright 2009 Nicolai Hähnle 
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 
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
}