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/shader_types.h"
68
69static const glsl_type *
70process_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
82static ir_rvalue *
83process_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
102namespace {
103
104class flatten_named_interface_blocks_declarations : public ir_rvalue_visitor
105{
106public:
107   void * const mem_ctx;
108   hash_table *interface_namespace;
109
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
125void
126flatten_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.location_frac =
184               iface_t->fields.structure[i].component >= 0 ?
185                  iface_t->fields.structure[i].component : 0;
186            new_var->data.explicit_location = (new_var->data.location >= 0);
187            new_var->data.explicit_component =
188               (iface_t->fields.structure[i].component >= 0);
189            new_var->data.offset = iface_t->fields.structure[i].offset;
190            new_var->data.explicit_xfb_offset =
191               (iface_t->fields.structure[i].offset >= 0);
192            new_var->data.xfb_buffer =
193               iface_t->fields.structure[i].xfb_buffer;
194            new_var->data.explicit_xfb_buffer =
195               iface_t->fields.structure[i].explicit_xfb_buffer;
196            new_var->data.interpolation =
197               iface_t->fields.structure[i].interpolation;
198            new_var->data.centroid = iface_t->fields.structure[i].centroid;
199            new_var->data.sample = iface_t->fields.structure[i].sample;
200            new_var->data.patch = iface_t->fields.structure[i].patch;
201            new_var->data.stream = var->data.stream;
202            new_var->data.how_declared = var->data.how_declared;
203            new_var->data.from_named_ifc_block = 1;
204
205            new_var->init_interface_type(var->type);
206            _mesa_hash_table_insert(interface_namespace, iface_field_name,
207                                    new_var);
208            insert_pos->insert_after(new_var);
209            insert_pos = new_var;
210         }
211      }
212      var->remove();
213   }
214
215   /* Second pass: visit all ir_dereference_record instances, and if they
216    * reference an interface block, then flatten the refererence out.
217    */
218   visit_list_elements(this, instructions);
219   _mesa_hash_table_destroy(interface_namespace, NULL);
220   interface_namespace = NULL;
221}
222
223ir_visitor_status
224flatten_named_interface_blocks_declarations::visit_leave(ir_assignment *ir)
225{
226   ir_dereference_record *lhs_rec = ir->lhs->as_dereference_record();
227
228   ir_variable *lhs_var =  ir->lhs->variable_referenced();
229   if (lhs_var && lhs_var->get_interface_type()) {
230      lhs_var->data.assigned = 1;
231   }
232
233   if (lhs_rec) {
234      ir_rvalue *lhs_rec_tmp = lhs_rec;
235      handle_rvalue(&lhs_rec_tmp);
236      if (lhs_rec_tmp != lhs_rec) {
237         ir->set_lhs(lhs_rec_tmp);
238      }
239
240      ir_variable *lhs_var =  lhs_rec_tmp->variable_referenced();
241      if (lhs_var) {
242         lhs_var->data.assigned = 1;
243      }
244   }
245   return rvalue_visit(ir);
246}
247
248ir_visitor_status
249flatten_named_interface_blocks_declarations::visit_leave(ir_expression *ir)
250{
251   ir_visitor_status status = rvalue_visit(ir);
252
253   if (ir->operation == ir_unop_interpolate_at_centroid ||
254       ir->operation == ir_binop_interpolate_at_offset ||
255       ir->operation == ir_binop_interpolate_at_sample) {
256      const ir_rvalue *val = ir->operands[0];
257
258      /* This disables varying packing for this input. */
259      val->variable_referenced()->data.must_be_shader_input = 1;
260   }
261
262   return status;
263}
264
265void
266flatten_named_interface_blocks_declarations::handle_rvalue(ir_rvalue **rvalue)
267{
268   if (*rvalue == NULL)
269      return;
270
271   ir_dereference_record *ir = (*rvalue)->as_dereference_record();
272   if (ir == NULL)
273      return;
274
275   ir_variable *var = ir->variable_referenced();
276   if (var == NULL)
277      return;
278
279   if (!var->is_interface_instance())
280      return;
281
282   /* It should be possible to handle uniforms during this pass,
283    * but, this will require changes to the other uniform block
284    * support code.
285    */
286   if (var->data.mode == ir_var_uniform || var->data.mode == ir_var_shader_storage)
287      return;
288
289   if (var->get_interface_type() != NULL) {
290      char *iface_field_name =
291         ralloc_asprintf(mem_ctx, "%s %s.%s.%s",
292                         var->data.mode == ir_var_shader_in ? "in" : "out",
293                         var->get_interface_type()->name,
294                         var->name,
295                         ir->record->type->fields.structure[ir->field_idx].name);
296
297      /* Find the variable in the set of flattened interface blocks */
298      hash_entry *entry = _mesa_hash_table_search(interface_namespace,
299                                                  iface_field_name);
300      assert(entry);
301      ir_variable *found_var = (ir_variable *) entry->data;
302
303      ir_dereference_variable *deref_var =
304         new(mem_ctx) ir_dereference_variable(found_var);
305
306      ir_dereference_array *deref_array =
307         ir->record->as_dereference_array();
308      if (deref_array != NULL) {
309         *rvalue = process_array_ir(mem_ctx, deref_array,
310                                    (ir_rvalue *)deref_var);
311      } else {
312         *rvalue = deref_var;
313      }
314   }
315}
316
317void
318lower_named_interface_blocks(void *mem_ctx, gl_linked_shader *shader)
319{
320   flatten_named_interface_blocks_declarations v_decl(mem_ctx);
321   v_decl.run(shader->ir);
322}
323
324