blob: f57ae44aae4f49d9c4149b4fbfa520fbbf1917db [file] [log] [blame]
Eric Anholt7f7eaf02010-08-05 11:01:09 -07001/*
2 * Copyright © 2010 Intel Corporation
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 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * 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 NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24/**
25 * \file ir_structure_splitting.cpp
26 *
27 * If a structure is only ever referenced by its components, then
28 * split those components out to individual variables so they can be
29 * handled normally by other optimization passes.
30 *
31 * This skips structures like uniforms, which need to be accessible as
32 * structures for their access by the GL.
33 */
34
35#include "ir.h"
36#include "ir_visitor.h"
37#include "glsl_types.h"
38
39class variable_entry : public exec_node
40{
41public:
42 variable_entry(ir_variable *var)
43 {
44 this->var = var;
45 this->whole_structure_access = 0;
46 this->declaration = false;
47 this->components = NULL;
48 this->mem_ctx = NULL;
49 }
50
51 ir_variable *var; /* The key: the variable's pointer. */
52
53 /** Number of times the variable is referenced, including assignments. */
54 unsigned whole_structure_access;
55
56 bool declaration; /* If the variable had a decl in the instruction stream */
57
58 ir_variable **components;
59
60 /** talloc_parent(this->var) -- the shader's talloc context. */
61 void *mem_ctx;
62};
63
64class ir_structure_reference_visitor : public ir_hierarchical_visitor {
65public:
66 ir_structure_reference_visitor(void)
67 {
68 this->mem_ctx = talloc_new(NULL);
69 this->variable_list.make_empty();
70 }
71
72 ~ir_structure_reference_visitor(void)
73 {
74 talloc_free(mem_ctx);
75 }
76
77 virtual ir_visitor_status visit(ir_variable *);
78 virtual ir_visitor_status visit(ir_dereference_variable *);
79 virtual ir_visitor_status visit(ir_dereference_record *);
80
81 virtual ir_visitor_status visit_enter(ir_function_signature *);
82
83 variable_entry *get_variable_entry(ir_variable *var);
84
85 /* List of variable_entry */
86 exec_list variable_list;
87
88 void *mem_ctx;
89};
90
91variable_entry *
92ir_structure_reference_visitor::get_variable_entry(ir_variable *var)
93{
94 assert(var);
95
96 if (!var->type->is_record())
97 return NULL;
98
99 foreach_iter(exec_list_iterator, iter, this->variable_list) {
100 variable_entry *entry = (variable_entry *)iter.get();
101 if (entry->var == var)
102 return entry;
103 }
104
105 variable_entry *entry = new(mem_ctx) variable_entry(var);
106 this->variable_list.push_tail(entry);
107 return entry;
108}
109
110
111ir_visitor_status
112ir_structure_reference_visitor::visit(ir_variable *ir)
113{
114 variable_entry *entry = this->get_variable_entry(ir);
115
116 if (entry)
117 entry->declaration = true;
118
119 return visit_continue;
120}
121
122ir_visitor_status
123ir_structure_reference_visitor::visit(ir_dereference_variable *ir)
124{
125 ir_variable *const var = ir->variable_referenced();
126 variable_entry *entry = this->get_variable_entry(var);
127
128 if (entry)
129 entry->whole_structure_access++;
130
131 return visit_continue;
132}
133
134ir_visitor_status
135ir_structure_reference_visitor::visit(ir_dereference_record *ir)
136{
137 /* Don't descend into the ir_dereference_variable below. */
138 return visit_continue;
139}
140
141ir_visitor_status
142ir_structure_reference_visitor::visit_enter(ir_function_signature *ir)
143{
144 /* We don't want to descend into the function parameters and
145 * dead-code eliminate them, so just accept the body here.
146 */
147 visit_list_elements(this, &ir->body);
148 return visit_continue_with_parent;
149}
150
151class ir_structure_splitting_visitor : public ir_hierarchical_visitor {
152public:
153 ir_structure_splitting_visitor(exec_list *vars)
154 {
155 this->variable_list = vars;
156 }
157
158 virtual ~ir_structure_splitting_visitor()
159 {
160 }
161
162 virtual ir_visitor_status visit_leave(ir_assignment *);
163 virtual ir_visitor_status visit_leave(ir_call *);
164 virtual ir_visitor_status visit_leave(ir_dereference_array *);
165 virtual ir_visitor_status visit_leave(ir_expression *);
166 virtual ir_visitor_status visit_leave(ir_if *);
167 virtual ir_visitor_status visit_leave(ir_return *);
168 virtual ir_visitor_status visit_leave(ir_swizzle *);
169 virtual ir_visitor_status visit_leave(ir_texture *);
170
171 void split_deref(ir_dereference **deref);
172 void split_rvalue(ir_rvalue **rvalue);
173 struct variable_entry *get_splitting_entry(ir_variable *var);
174
175 exec_list *variable_list;
176 void *mem_ctx;
177};
178
179struct variable_entry *
180ir_structure_splitting_visitor::get_splitting_entry(ir_variable *var)
181{
182 assert(var);
183
184 if (!var->type->is_record())
185 return NULL;
186
187 foreach_iter(exec_list_iterator, iter, *this->variable_list) {
188 variable_entry *entry = (variable_entry *)iter.get();
189 if (entry->var == var) {
190 return entry;
191 }
192 }
193
194 return NULL;
195}
196
197void
198ir_structure_splitting_visitor::split_deref(ir_dereference **deref)
199{
200 if ((*deref)->ir_type != ir_type_dereference_record)
201 return;
202
203 ir_dereference_record *deref_record = (ir_dereference_record *)deref;
204 ir_dereference_variable *deref_var = deref_record->as_dereference_variable();
205 if (!deref_var)
206 return;
207
208 variable_entry *entry = get_splitting_entry(deref_var->var);
209 if (entry)
210 return;
211
212 unsigned int i;
213 for (i = 0; i < entry->var->type->length; i++) {
214 if (strcmp(deref_record->field,
215 entry->var->type->fields.structure[i].name) == 0)
216 break;
217 }
218 assert(i != entry->var->type->length);
219
220 *deref = new(entry->mem_ctx) ir_dereference_variable(entry->components[i]);
221}
222
223void
224ir_structure_splitting_visitor::split_rvalue(ir_rvalue **rvalue)
225{
226 ir_dereference *deref = (*rvalue)->as_dereference();
227
228 if (!deref)
229 return;
230
231 split_deref(&deref);
232 *rvalue = deref;
233}
234
235ir_visitor_status
236ir_structure_splitting_visitor::visit_leave(ir_expression *ir)
237{
238 unsigned int operand;
239
240 for (operand = 0; operand < ir->get_num_operands(); operand++) {
241 split_rvalue(&ir->operands[operand]);
242 }
243
244 return visit_continue;
245}
246
247ir_visitor_status
248ir_structure_splitting_visitor::visit_leave(ir_texture *ir)
249{
250 split_rvalue(&ir->coordinate);
251 split_rvalue(&ir->projector);
252 split_rvalue(&ir->shadow_comparitor);
253
254 switch (ir->op) {
255 case ir_tex:
256 break;
257 case ir_txb:
258 split_rvalue(&ir->lod_info.bias);
259 break;
260 case ir_txf:
261 case ir_txl:
262 split_rvalue(&ir->lod_info.lod);
263 break;
264 case ir_txd:
265 split_rvalue(&ir->lod_info.grad.dPdx);
266 split_rvalue(&ir->lod_info.grad.dPdy);
267 break;
268 }
269
270 return visit_continue;
271}
272
273ir_visitor_status
274ir_structure_splitting_visitor::visit_leave(ir_swizzle *ir)
275{
276 split_rvalue(&ir->val);
277 return visit_continue;
278}
279
280ir_visitor_status
281ir_structure_splitting_visitor::visit_leave(ir_dereference_array *ir)
282{
283 split_rvalue(&ir->array_index);
284 return visit_continue;
285}
286
287ir_visitor_status
288ir_structure_splitting_visitor::visit_leave(ir_assignment *ir)
289{
290 split_rvalue(&ir->rhs);
291 split_rvalue(&ir->condition);
292 split_deref(&ir->lhs);
293
294 return visit_continue;
295}
296
297ir_visitor_status
298ir_structure_splitting_visitor::visit_leave(ir_call *ir)
299{
300 foreach_iter(exec_list_iterator, iter, *ir) {
301 ir_rvalue *param = (ir_rvalue *)iter.get();
302 ir_rvalue *new_param = param;
303 split_rvalue(&new_param);
304
305 if (new_param != param) {
306 param->replace_with(new_param);
307 }
308 }
309 return visit_continue;
310}
311
312ir_visitor_status
313ir_structure_splitting_visitor::visit_leave(ir_return *ir)
314{
315 split_rvalue(&ir->value);;
316 return visit_continue;
317}
318
319ir_visitor_status
320ir_structure_splitting_visitor::visit_leave(ir_if *ir)
321{
322 split_rvalue(&ir->condition);
323 return visit_continue;
324}
325
326
327bool
328do_structure_splitting(exec_list *instructions)
329{
330 ir_structure_reference_visitor refs;
331 void *mem_ctx = talloc_new(NULL);
332
333 /* Trim out variables we can't split. */
334 foreach_iter(exec_list_iterator, iter, refs.variable_list) {
335 variable_entry *entry = (variable_entry *)iter.get();
336 if (!entry->declaration || entry->whole_structure_access) {
337 entry->remove();
338 }
339 }
340
341 if (refs.variable_list.is_empty())
342 return false;
343
344 /* Replace the decls of the structures to be split with their split
345 * components.
346 */
347 foreach_iter(exec_list_iterator, iter, refs.variable_list) {
348 variable_entry *entry = (variable_entry *)iter.get();
349 const struct glsl_type *type = entry->var->type;
350
351 entry->mem_ctx = talloc_parent(entry->var);
352
353 entry->components = talloc_array(mem_ctx,
354 ir_variable *,
355 type->length);
356
357 for (unsigned int i = 0; i < entry->var->type->length; i++) {
358 const char *name = talloc_asprintf(mem_ctx, "%s_%s",
359 type->name,
360 type->fields.structure[i].name);
361
362 entry->components[i] =
363 new(entry->mem_ctx) ir_variable(type->fields.structure[i].type,
364 name,
365 ir_var_temporary);
366 entry->var->insert_before(entry->components[i]);
367 }
368
369 entry->var->remove();
370 }
371
372 ir_structure_splitting_visitor split(&refs.variable_list);
373 visit_list_elements(&split, instructions);
374
375 talloc_free(mem_ctx);
376
377 return true;
378}