1 /*
2 * Copyright (c) 2013 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 lower_named_interface_blocks.cpp
26 *
27 * This lowering pass converts all interface blocks with instance names
28 * into interface blocks without an instance name.
29 *
30 * For example, the following shader:
31 *
32 * out block {
33 * float block_var;
34 * } inst_name;
35 *
36 * main()
37 * {
38 * inst_name.block_var = 0.0;
39 * }
40 *
41 * Is rewritten to:
42 *
43 * out block {
44 * float block_var;
45 * };
46 *
47 * main()
48 * {
49 * block_var = 0.0;
50 * }
51 *
52 * This takes place after the shader code has already been verified with
53 * the interface name in place.
54 *
55 * The linking phase will use the interface block name rather than the
56 * interface's instance name when linking interfaces.
57 *
58 * This modification to the ir allows our currently existing dead code
59 * elimination to work with interface blocks without changes.
60 */
61
62 #include "glsl_symbol_table.h"
63 #include "ir.h"
64 #include "ir_optimization.h"
65 #include "ir_rvalue_visitor.h"
66 #include "util/hash_table.h"
67 #include "main/mtypes.h"
68
69 static const glsl_type *
process_array_type(const glsl_type * type,unsigned idx)70 process_array_type(const glsl_type *type, unsigned idx)
71 {
72 const glsl_type *element_type = type->fields.array;
73 if (element_type->is_array()) {
74 const glsl_type *new_array_type = process_array_type(element_type, idx);
75 return glsl_type::get_array_instance(new_array_type, type->length);
76 } else {
77 return glsl_type::get_array_instance(
78 element_type->fields.structure[idx].type, type->length);
79 }
80 }
81
82 static ir_rvalue *
process_array_ir(void * const mem_ctx,ir_dereference_array * deref_array_prev,ir_rvalue * deref_var)83 process_array_ir(void * const mem_ctx,
84 ir_dereference_array *deref_array_prev,
85 ir_rvalue *deref_var)
86 {
87 ir_dereference_array *deref_array =
88 deref_array_prev->array->as_dereference_array();
89
90 if (deref_array == NULL) {
91 return new(mem_ctx) ir_dereference_array(deref_var,
92 deref_array_prev->array_index);
93 } else {
94 deref_array = (ir_dereference_array *) process_array_ir(mem_ctx,
95 deref_array,
96 deref_var);
97 return new(mem_ctx) ir_dereference_array(deref_array,
98 deref_array_prev->array_index);
99 }
100 }
101
102 namespace {
103
104 class flatten_named_interface_blocks_declarations : public ir_rvalue_visitor
105 {
106 public:
107 void * const mem_ctx;
108 hash_table *interface_namespace;
109
flatten_named_interface_blocks_declarations(void * mem_ctx)110 flatten_named_interface_blocks_declarations(void *mem_ctx)
111 : mem_ctx(mem_ctx),
112 interface_namespace(NULL)
113 {
114 }
115
116 void run(exec_list *instructions);
117
118 virtual ir_visitor_status visit_leave(ir_assignment *);
119 virtual ir_visitor_status visit_leave(ir_expression *);
120 virtual void handle_rvalue(ir_rvalue **rvalue);
121 };
122
123 } /* anonymous namespace */
124
125 void
run(exec_list * instructions)126 flatten_named_interface_blocks_declarations::run(exec_list *instructions)
127 {
128 interface_namespace = _mesa_hash_table_create(NULL, _mesa_hash_string,
129 _mesa_key_string_equal);
130
131 /* First pass: adjust instance block variables with an instance name
132 * to not have an instance name.
133 *
134 * The interface block variables are stored in the interface_namespace
135 * hash table so they can be used in the second pass.
136 */
137 foreach_in_list_safe(ir_instruction, node, instructions) {
138 ir_variable *var = node->as_variable();
139 if (!var || !var->is_interface_instance())
140 continue;
141
142 /* It should be possible to handle uniforms during this pass,
143 * but, this will require changes to the other uniform block
144 * support code.
145 */
146 if (var->data.mode == ir_var_uniform ||
147 var->data.mode == ir_var_shader_storage)
148 continue;
149
150 const glsl_type * iface_t = var->type->without_array();
151 exec_node *insert_pos = var;
152
153 assert (iface_t->is_interface());
154
155 for (unsigned i = 0; i < iface_t->length; i++) {
156 const char * field_name = iface_t->fields.structure[i].name;
157 char *iface_field_name =
158 ralloc_asprintf(mem_ctx, "%s %s.%s.%s",
159 var->data.mode == ir_var_shader_in ? "in" : "out",
160 iface_t->name, var->name, field_name);
161
162 hash_entry *entry = _mesa_hash_table_search(interface_namespace,
163 iface_field_name);
164 ir_variable *found_var = entry ? (ir_variable *) entry->data : NULL;
165 if (!found_var) {
166 ir_variable *new_var;
167 char *var_name =
168 ralloc_strdup(mem_ctx, iface_t->fields.structure[i].name);
169 if (!var->type->is_array()) {
170 new_var =
171 new(mem_ctx) ir_variable(iface_t->fields.structure[i].type,
172 var_name,
173 (ir_variable_mode) var->data.mode);
174 } else {
175 const glsl_type *new_array_type =
176 process_array_type(var->type, i);
177 new_var =
178 new(mem_ctx) ir_variable(new_array_type,
179 var_name,
180 (ir_variable_mode) var->data.mode);
181 }
182 new_var->data.location = iface_t->fields.structure[i].location;
183 new_var->data.explicit_location = (new_var->data.location >= 0);
184 new_var->data.offset = iface_t->fields.structure[i].offset;
185 new_var->data.explicit_xfb_offset =
186 (iface_t->fields.structure[i].offset >= 0);
187 new_var->data.xfb_buffer =
188 iface_t->fields.structure[i].xfb_buffer;
189 new_var->data.explicit_xfb_buffer =
190 iface_t->fields.structure[i].explicit_xfb_buffer;
191 new_var->data.interpolation =
192 iface_t->fields.structure[i].interpolation;
193 new_var->data.centroid = iface_t->fields.structure[i].centroid;
194 new_var->data.sample = iface_t->fields.structure[i].sample;
195 new_var->data.patch = iface_t->fields.structure[i].patch;
196 new_var->data.stream = var->data.stream;
197 new_var->data.how_declared = var->data.how_declared;
198 new_var->data.from_named_ifc_block = 1;
199
200 new_var->init_interface_type(var->type);
201 _mesa_hash_table_insert(interface_namespace, iface_field_name,
202 new_var);
203 insert_pos->insert_after(new_var);
204 insert_pos = new_var;
205 }
206 }
207 var->remove();
208 }
209
210 /* Second pass: visit all ir_dereference_record instances, and if they
211 * reference an interface block, then flatten the refererence out.
212 */
213 visit_list_elements(this, instructions);
214 _mesa_hash_table_destroy(interface_namespace, NULL);
215 interface_namespace = NULL;
216 }
217
218 ir_visitor_status
visit_leave(ir_assignment * ir)219 flatten_named_interface_blocks_declarations::visit_leave(ir_assignment *ir)
220 {
221 ir_dereference_record *lhs_rec = ir->lhs->as_dereference_record();
222
223 ir_variable *lhs_var = ir->lhs->variable_referenced();
224 if (lhs_var && lhs_var->get_interface_type()) {
225 lhs_var->data.assigned = 1;
226 }
227
228 if (lhs_rec) {
229 ir_rvalue *lhs_rec_tmp = lhs_rec;
230 handle_rvalue(&lhs_rec_tmp);
231 if (lhs_rec_tmp != lhs_rec) {
232 ir->set_lhs(lhs_rec_tmp);
233 }
234
235 ir_variable *lhs_var = lhs_rec_tmp->variable_referenced();
236 if (lhs_var) {
237 lhs_var->data.assigned = 1;
238 }
239 }
240 return rvalue_visit(ir);
241 }
242
243 ir_visitor_status
visit_leave(ir_expression * ir)244 flatten_named_interface_blocks_declarations::visit_leave(ir_expression *ir)
245 {
246 ir_visitor_status status = rvalue_visit(ir);
247
248 if (ir->operation == ir_unop_interpolate_at_centroid ||
249 ir->operation == ir_binop_interpolate_at_offset ||
250 ir->operation == ir_binop_interpolate_at_sample) {
251 const ir_rvalue *val = ir->operands[0];
252
253 /* This disables varying packing for this input. */
254 val->variable_referenced()->data.must_be_shader_input = 1;
255 }
256
257 return status;
258 }
259
260 void
handle_rvalue(ir_rvalue ** rvalue)261 flatten_named_interface_blocks_declarations::handle_rvalue(ir_rvalue **rvalue)
262 {
263 if (*rvalue == NULL)
264 return;
265
266 ir_dereference_record *ir = (*rvalue)->as_dereference_record();
267 if (ir == NULL)
268 return;
269
270 ir_variable *var = ir->variable_referenced();
271 if (var == NULL)
272 return;
273
274 if (!var->is_interface_instance())
275 return;
276
277 /* It should be possible to handle uniforms during this pass,
278 * but, this will require changes to the other uniform block
279 * support code.
280 */
281 if (var->data.mode == ir_var_uniform || var->data.mode == ir_var_shader_storage)
282 return;
283
284 if (var->get_interface_type() != NULL) {
285 char *iface_field_name =
286 ralloc_asprintf(mem_ctx, "%s %s.%s.%s",
287 var->data.mode == ir_var_shader_in ? "in" : "out",
288 var->get_interface_type()->name,
289 var->name,
290 ir->record->type->fields.structure[ir->field_idx].name);
291
292 /* Find the variable in the set of flattened interface blocks */
293 hash_entry *entry = _mesa_hash_table_search(interface_namespace,
294 iface_field_name);
295 assert(entry);
296 ir_variable *found_var = (ir_variable *) entry->data;
297
298 ir_dereference_variable *deref_var =
299 new(mem_ctx) ir_dereference_variable(found_var);
300
301 ir_dereference_array *deref_array =
302 ir->record->as_dereference_array();
303 if (deref_array != NULL) {
304 *rvalue = process_array_ir(mem_ctx, deref_array,
305 (ir_rvalue *)deref_var);
306 } else {
307 *rvalue = deref_var;
308 }
309 }
310 }
311
312 void
lower_named_interface_blocks(void * mem_ctx,gl_linked_shader * shader)313 lower_named_interface_blocks(void *mem_ctx, gl_linked_shader *shader)
314 {
315 flatten_named_interface_blocks_declarations v_decl(mem_ctx);
316 v_decl.run(shader->ir);
317 }
318
319