Merge remote-tracking branch 'mesa-public/master' into vulkan
[mesa.git] / src / glsl / lower_named_interface_blocks.cpp
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 "program/hash_table.h"
67
68 namespace {
69
70 class flatten_named_interface_blocks_declarations : public ir_rvalue_visitor
71 {
72 public:
73 void * const mem_ctx;
74 hash_table *interface_namespace;
75
76 flatten_named_interface_blocks_declarations(void *mem_ctx)
77 : mem_ctx(mem_ctx),
78 interface_namespace(NULL)
79 {
80 }
81
82 void run(exec_list *instructions);
83
84 virtual ir_visitor_status visit_leave(ir_assignment *);
85 virtual void handle_rvalue(ir_rvalue **rvalue);
86 };
87
88 } /* anonymous namespace */
89
90 void
91 flatten_named_interface_blocks_declarations::run(exec_list *instructions)
92 {
93 interface_namespace = hash_table_ctor(0, hash_table_string_hash,
94 hash_table_string_compare);
95
96 /* First pass: adjust instance block variables with an instance name
97 * to not have an instance name.
98 *
99 * The interface block variables are stored in the interface_namespace
100 * hash table so they can be used in the second pass.
101 */
102 foreach_in_list_safe(ir_instruction, node, instructions) {
103 ir_variable *var = node->as_variable();
104 if (!var || !var->is_interface_instance())
105 continue;
106
107 /* It should be possible to handle uniforms during this pass,
108 * but, this will require changes to the other uniform block
109 * support code.
110 */
111 if (var->data.mode == ir_var_uniform ||
112 var->data.mode == ir_var_shader_storage)
113 continue;
114
115 const glsl_type * iface_t = var->type;
116 const glsl_type * array_t = NULL;
117 exec_node *insert_pos = var;
118
119 if (iface_t->is_array()) {
120 array_t = iface_t;
121 iface_t = array_t->fields.array;
122 }
123
124 assert (iface_t->is_interface());
125
126 for (unsigned i = 0; i < iface_t->length; i++) {
127 const char * field_name = iface_t->fields.structure[i].name;
128 char *iface_field_name =
129 ralloc_asprintf(mem_ctx, "%s %s.%s.%s",
130 var->data.mode == ir_var_shader_in ? "in" : "out",
131 iface_t->name, var->name, field_name);
132
133 ir_variable *found_var =
134 (ir_variable *) hash_table_find(interface_namespace,
135 iface_field_name);
136 if (!found_var) {
137 ir_variable *new_var;
138 char *var_name =
139 ralloc_strdup(mem_ctx, iface_t->fields.structure[i].name);
140 if (array_t == NULL) {
141 new_var =
142 new(mem_ctx) ir_variable(iface_t->fields.structure[i].type,
143 var_name,
144 (ir_variable_mode) var->data.mode);
145 new_var->data.from_named_ifc_block_nonarray = 1;
146 } else {
147 const glsl_type *new_array_type =
148 glsl_type::get_array_instance(
149 iface_t->fields.structure[i].type,
150 array_t->length);
151 new_var =
152 new(mem_ctx) ir_variable(new_array_type,
153 var_name,
154 (ir_variable_mode) var->data.mode);
155 new_var->data.from_named_ifc_block_array = 1;
156 }
157 new_var->data.location = iface_t->fields.structure[i].location;
158 new_var->data.explicit_location = (new_var->data.location >= 0);
159 new_var->data.interpolation =
160 iface_t->fields.structure[i].interpolation;
161 new_var->data.centroid = iface_t->fields.structure[i].centroid;
162 new_var->data.sample = iface_t->fields.structure[i].sample;
163 new_var->data.patch = iface_t->fields.structure[i].patch;
164
165 new_var->init_interface_type(iface_t);
166 hash_table_insert(interface_namespace, new_var,
167 iface_field_name);
168 insert_pos->insert_after(new_var);
169 insert_pos = new_var;
170 }
171 }
172 var->remove();
173 }
174
175 /* Second pass: visit all ir_dereference_record instances, and if they
176 * reference an interface block, then flatten the refererence out.
177 */
178 visit_list_elements(this, instructions);
179 hash_table_dtor(interface_namespace);
180 interface_namespace = NULL;
181 }
182
183 ir_visitor_status
184 flatten_named_interface_blocks_declarations::visit_leave(ir_assignment *ir)
185 {
186 ir_dereference_record *lhs_rec = ir->lhs->as_dereference_record();
187 if (lhs_rec) {
188 ir_rvalue *lhs_rec_tmp = lhs_rec;
189 handle_rvalue(&lhs_rec_tmp);
190 if (lhs_rec_tmp != lhs_rec) {
191 ir->set_lhs(lhs_rec_tmp);
192 }
193 }
194 return rvalue_visit(ir);
195 }
196
197 void
198 flatten_named_interface_blocks_declarations::handle_rvalue(ir_rvalue **rvalue)
199 {
200 if (*rvalue == NULL)
201 return;
202
203 ir_dereference_record *ir = (*rvalue)->as_dereference_record();
204 if (ir == NULL)
205 return;
206
207 ir_variable *var = ir->variable_referenced();
208 if (var == NULL)
209 return;
210
211 if (!var->is_interface_instance())
212 return;
213
214 /* It should be possible to handle uniforms during this pass,
215 * but, this will require changes to the other uniform block
216 * support code.
217 */
218 if (var->data.mode == ir_var_uniform || var->data.mode == ir_var_shader_storage)
219 return;
220
221 if (var->get_interface_type() != NULL) {
222 char *iface_field_name =
223 ralloc_asprintf(mem_ctx, "%s %s.%s.%s",
224 var->data.mode == ir_var_shader_in ? "in" : "out",
225 var->get_interface_type()->name,
226 var->name, ir->field);
227 /* Find the variable in the set of flattened interface blocks */
228 ir_variable *found_var =
229 (ir_variable *) hash_table_find(interface_namespace,
230 iface_field_name);
231 assert(found_var);
232
233 ir_dereference_variable *deref_var =
234 new(mem_ctx) ir_dereference_variable(found_var);
235
236 ir_dereference_array *deref_array =
237 ir->record->as_dereference_array();
238 if (deref_array != NULL) {
239 *rvalue =
240 new(mem_ctx) ir_dereference_array(deref_var,
241 deref_array->array_index);
242 } else {
243 *rvalue = deref_var;
244 }
245 }
246 }
247
248 void
249 lower_named_interface_blocks(void *mem_ctx, gl_shader *shader)
250 {
251 flatten_named_interface_blocks_declarations v_decl(mem_ctx);
252 v_decl.run(shader->ir);
253 }
254