glsl/linker: handle array/struct members for DisableXfbPacking
[mesa.git] / src / compiler / glsl / lower_xfb_varying.cpp
1 /*
2 * Copyright ©2019 Collabora Ltd.
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_xfb_varying.cpp
26 *
27 */
28
29 #include "ir.h"
30 #include "main/mtypes.h"
31 #include "glsl_symbol_table.h"
32 #include "util/strndup.h"
33
34 namespace {
35
36 /**
37 * Visitor that splices varying packing code before every return.
38 */
39 class lower_xfb_var_splicer : public ir_hierarchical_visitor
40 {
41 public:
42 explicit lower_xfb_var_splicer(void *mem_ctx,
43 const exec_list *instructions);
44
45 virtual ir_visitor_status visit_leave(ir_return *ret);
46 virtual ir_visitor_status visit_leave(ir_function_signature *sig);
47
48 private:
49 /**
50 * Memory context used to allocate new instructions for the shader.
51 */
52 void * const mem_ctx;
53
54 /**
55 * Instructions that should be spliced into place before each return.
56 */
57 const exec_list *instructions;
58 };
59
60 } /* anonymous namespace */
61
62
63 lower_xfb_var_splicer::lower_xfb_var_splicer(void *mem_ctx, const exec_list *instructions)
64 : mem_ctx(mem_ctx), instructions(instructions)
65 {
66 }
67
68 ir_visitor_status
69 lower_xfb_var_splicer::visit_leave(ir_return *ret)
70 {
71 foreach_in_list(ir_instruction, ir, this->instructions) {
72 ret->insert_before(ir->clone(this->mem_ctx, NULL));
73 }
74 return visit_continue;
75 }
76
77 /** Insert a copy-back assignment at the end of the main() function */
78 ir_visitor_status
79 lower_xfb_var_splicer::visit_leave(ir_function_signature *sig)
80 {
81 if (strcmp(sig->function_name(), "main") != 0)
82 return visit_continue;
83
84 if (((ir_instruction*)sig->body.get_tail())->ir_type == ir_type_return)
85 return visit_continue;
86
87 foreach_in_list(ir_instruction, ir, this->instructions) {
88 sig->body.push_tail(ir->clone(this->mem_ctx, NULL));
89 }
90
91 return visit_continue;
92 }
93
94 static char*
95 get_field_name(const char *name)
96 {
97 const char *first_dot = strchr(name, '.');
98 const char *first_square_bracket = strchr(name, '[');
99 int name_size = 0;
100
101 if (!first_square_bracket && !first_dot)
102 name_size = strlen(name);
103 else if ((!first_square_bracket ||
104 (first_dot && first_dot < first_square_bracket)))
105 name_size = first_dot - name;
106 else
107 name_size = first_square_bracket - name;
108
109 return strndup(name, name_size);
110 }
111
112 /* Generate a new name given the old xfb declaration string by replacing dots
113 * with '_', brackets with '@' and appending "-xfb" */
114 static char *
115 generate_new_name(void *mem_ctx, const char *name)
116 {
117 char *new_name;
118 unsigned i = 0;
119
120 new_name = ralloc_strdup(mem_ctx, name);
121 while (new_name[i]) {
122 if (new_name[i] == '.') {
123 new_name[i] = '_';
124 } else if (new_name[i] == '[' || new_name[i] == ']') {
125 new_name[i] = '@';
126 }
127 i++;
128 }
129
130 if (!ralloc_strcat(&new_name, "-xfb")) {
131 ralloc_free(new_name);
132 return NULL;
133 }
134
135 return new_name;
136 }
137
138 /* Get the dereference for the given variable name. The method is called
139 * recursively to parse array indices and struct members. */
140 static bool
141 get_deref(void *ctx,
142 const char *name,
143 struct gl_linked_shader *shader,
144 ir_dereference **deref,
145 const glsl_type **type)
146 {
147 if (name[0] == '\0') {
148 /* End */
149 return (*deref != NULL);
150 } else if (name[0] == '[') {
151 /* Array index */
152 char *endptr = NULL;
153 unsigned index;
154
155 index = strtol(name + 1, &endptr, 10);
156 assert(*type != NULL && (*type)->is_array() && endptr[0] == ']');
157 *deref = new(ctx) ir_dereference_array(*deref, new(ctx) ir_constant(index));
158 *type = (*type)->without_array();
159 return get_deref(ctx, endptr + 1, shader, deref, type);
160 } else if (name[0] == '.') {
161 /* Struct member */
162 char *field = get_field_name(name + 1);
163
164 assert(*type != NULL && (*type)->is_struct() && field != NULL);
165 *deref = new(ctx) ir_dereference_record(*deref, field);
166 *type = (*type)->field_type(field);
167 assert(*type != glsl_type::error_type);
168 name += 1 + strlen(field);
169 free(field);
170 return get_deref(ctx, name, shader, deref, type);
171 } else {
172 /* Top level variable */
173 char *field = get_field_name(name);
174 ir_variable *toplevel_var;
175
176 toplevel_var = shader->symbols->get_variable(field);
177 name += strlen(field);
178 free(field);
179 if (toplevel_var == NULL) {
180 return false;
181 }
182
183 *deref = new (ctx) ir_dereference_variable(toplevel_var);
184 *type = toplevel_var->type;
185 return get_deref(ctx, name, shader, deref, type);
186 }
187 }
188
189 ir_variable *
190 lower_xfb_varying(void *mem_ctx,
191 struct gl_linked_shader *shader,
192 const char *old_var_name)
193 {
194 exec_list new_instructions;
195 char *new_var_name;
196 ir_dereference *deref = NULL;
197 const glsl_type *type = NULL;
198
199 if (!get_deref(mem_ctx, old_var_name, shader, &deref, &type)) {
200 if (deref) {
201 delete deref;
202 }
203 return NULL;
204 }
205
206 new_var_name = generate_new_name(mem_ctx, old_var_name);
207 ir_variable *new_variable
208 = new(mem_ctx) ir_variable(type, new_var_name, ir_var_shader_out);
209 new_variable->data.assigned = true;
210 new_variable->data.used = true;
211 shader->ir->push_head(new_variable);
212 ralloc_free(new_var_name);
213
214 ir_dereference *lhs = new(mem_ctx) ir_dereference_variable(new_variable);
215 ir_assignment *new_assignment = new(mem_ctx) ir_assignment(lhs, deref);
216 new_instructions.push_tail(new_assignment);
217
218 lower_xfb_var_splicer splicer(mem_ctx, &new_instructions);
219 visit_list_elements(&splicer, shader->ir);
220
221 return new_variable;
222 }