llvmpipe: implement 64 bit mul opcodes in llvmpipe
[mesa.git] / src / glsl / opt_dead_builtin_varyings.cpp
1 /*
2 * Copyright © 2013 Marek Olšák <maraeo@gmail.com>
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 opt_dead_builtin_varyings.cpp
26 *
27 * This eliminates the built-in shader outputs which are either not written
28 * at all or not used by the next stage. It also eliminates unused elements
29 * of gl_TexCoord inputs, which reduces the overall varying usage.
30 * The varyings handled here are the primary and secondary color, the fog,
31 * and the texture coordinates (gl_TexCoord).
32 *
33 * This pass is necessary, because the Mesa GLSL linker cannot eliminate
34 * built-in varyings like it eliminates user-defined varyings, because
35 * the built-in varyings have pre-assigned locations. Also, the elimination
36 * of unused gl_TexCoord elements requires its own lowering pass anyway.
37 *
38 * It's implemented by replacing all occurences of dead varyings with
39 * temporary variables, which creates dead code. It is recommended to run
40 * a dead-code elimination pass after this.
41 *
42 * If any texture coordinate slots can be eliminated, the gl_TexCoord array is
43 * broken down into separate vec4 variables with locations equal to
44 * VARYING_SLOT_TEX0 + i.
45 */
46
47 #include "main/imports.h" /* for snprintf */
48 #include "ir.h"
49 #include "ir_rvalue_visitor.h"
50 #include "ir_optimization.h"
51 #include "ir_print_visitor.h"
52 #include "glsl_types.h"
53 #include "link_varyings.h"
54
55 namespace {
56
57 /**
58 * This obtains detailed information about built-in varyings from shader code.
59 */
60 class varying_info_visitor : public ir_hierarchical_visitor {
61 public:
62 /* "mode" can be either ir_var_shader_in or ir_var_shader_out */
63 varying_info_visitor(ir_variable_mode mode)
64 : lower_texcoord_array(true),
65 texcoord_array(NULL),
66 texcoord_usage(0),
67 color_usage(0),
68 tfeedback_color_usage(0),
69 fog(NULL),
70 has_fog(false),
71 tfeedback_has_fog(false),
72 mode(mode)
73 {
74 memset(color, 0, sizeof(color));
75 memset(backcolor, 0, sizeof(backcolor));
76 }
77
78 virtual ir_visitor_status visit_enter(ir_dereference_array *ir)
79 {
80 ir_variable *var = ir->variable_referenced();
81
82 if (var && var->mode == this->mode &&
83 var->location == VARYING_SLOT_TEX0) {
84 this->texcoord_array = var;
85
86 ir_constant *index = ir->array_index->as_constant();
87 if (index == NULL) {
88 /* There is variable indexing, we can't lower the texcoord array.
89 */
90 this->texcoord_usage |= (1 << var->type->array_size()) - 1;
91 this->lower_texcoord_array = false;
92 }
93 else {
94 this->texcoord_usage |= 1 << index->get_uint_component(0);
95 }
96
97 /* Don't visit the leaves of ir_dereference_array. */
98 return visit_continue_with_parent;
99 }
100
101 return visit_continue;
102 }
103
104 virtual ir_visitor_status visit(ir_dereference_variable *ir)
105 {
106 ir_variable *var = ir->variable_referenced();
107
108 if (var->mode == this->mode && var->type->is_array() &&
109 var->location == VARYING_SLOT_TEX0) {
110 /* This is a whole array dereference like "gl_TexCoord = x;",
111 * there's probably no point in lowering that.
112 */
113 this->texcoord_usage |= (1 << var->type->array_size()) - 1;
114 this->lower_texcoord_array = false;
115 }
116 return visit_continue;
117 }
118
119 virtual ir_visitor_status visit(ir_variable *var)
120 {
121 if (var->mode != this->mode)
122 return visit_continue;
123
124 /* Handle colors and fog. */
125 switch (var->location) {
126 case VARYING_SLOT_COL0:
127 this->color[0] = var;
128 this->color_usage |= 1;
129 break;
130 case VARYING_SLOT_COL1:
131 this->color[1] = var;
132 this->color_usage |= 2;
133 break;
134 case VARYING_SLOT_BFC0:
135 this->backcolor[0] = var;
136 this->color_usage |= 1;
137 break;
138 case VARYING_SLOT_BFC1:
139 this->backcolor[1] = var;
140 this->color_usage |= 2;
141 break;
142 case VARYING_SLOT_FOGC:
143 this->fog = var;
144 this->has_fog = true;
145 break;
146 }
147
148 return visit_continue;
149 }
150
151 void get(exec_list *ir,
152 unsigned num_tfeedback_decls,
153 tfeedback_decl *tfeedback_decls)
154 {
155 /* Handle the transform feedback varyings. */
156 for (unsigned i = 0; i < num_tfeedback_decls; i++) {
157 if (!tfeedback_decls[i].is_varying())
158 continue;
159
160 unsigned location = tfeedback_decls[i].get_location();
161
162 switch (location) {
163 case VARYING_SLOT_COL0:
164 case VARYING_SLOT_BFC0:
165 this->tfeedback_color_usage |= 1;
166 break;
167 case VARYING_SLOT_COL1:
168 case VARYING_SLOT_BFC1:
169 this->tfeedback_color_usage |= 2;
170 break;
171 case VARYING_SLOT_FOGC:
172 this->tfeedback_has_fog = true;
173 break;
174 default:
175 if (location >= VARYING_SLOT_TEX0 &&
176 location <= VARYING_SLOT_TEX7) {
177 this->lower_texcoord_array = false;
178 }
179 }
180 }
181
182 /* Process the shader. */
183 visit_list_elements(this, ir);
184
185 if (!this->texcoord_array) {
186 this->lower_texcoord_array = false;
187 }
188 }
189
190 bool lower_texcoord_array;
191 ir_variable *texcoord_array;
192 unsigned texcoord_usage; /* bitmask */
193
194 ir_variable *color[2];
195 ir_variable *backcolor[2];
196 unsigned color_usage; /* bitmask */
197 unsigned tfeedback_color_usage; /* bitmask */
198
199 ir_variable *fog;
200 bool has_fog;
201 bool tfeedback_has_fog;
202
203 ir_variable_mode mode;
204 };
205
206
207 /**
208 * This replaces unused varyings with temporary variables.
209 *
210 * If "ir" is the producer, the "external" usage should come from
211 * the consumer. It also works the other way around. If either one is
212 * missing, set the "external" usage to a full mask.
213 */
214 class replace_varyings_visitor : public ir_rvalue_visitor {
215 public:
216 replace_varyings_visitor(exec_list *ir,
217 const varying_info_visitor *info,
218 unsigned external_texcoord_usage,
219 unsigned external_color_usage,
220 bool external_has_fog)
221 : info(info), new_fog(NULL)
222 {
223 void *const ctx = ir;
224
225 memset(this->new_texcoord, 0, sizeof(this->new_texcoord));
226 memset(this->new_color, 0, sizeof(this->new_color));
227 memset(this->new_backcolor, 0, sizeof(this->new_backcolor));
228
229 const char *mode_str =
230 info->mode == ir_var_shader_in ? "in" : "out";
231
232 /* Handle texcoord outputs.
233 *
234 * We're going to break down the gl_TexCoord array into separate
235 * variables. First, add declarations of the new variables all
236 * occurences of gl_TexCoord will be replaced with.
237 */
238 if (info->lower_texcoord_array) {
239 for (int i = MAX_TEXTURE_COORD_UNITS-1; i >= 0; i--) {
240 if (info->texcoord_usage & (1 << i)) {
241 char name[32];
242
243 if (!(external_texcoord_usage & (1 << i))) {
244 /* This varying is unused in the next stage. Declare
245 * a temporary instead of an output. */
246 snprintf(name, 32, "gl_%s_TexCoord%i_dummy", mode_str, i);
247 this->new_texcoord[i] =
248 new (ctx) ir_variable(glsl_type::vec4_type, name,
249 ir_var_temporary);
250 }
251 else {
252 snprintf(name, 32, "gl_%s_TexCoord%i", mode_str, i);
253 this->new_texcoord[i] =
254 new(ctx) ir_variable(glsl_type::vec4_type, name,
255 info->mode);
256 this->new_texcoord[i]->location = VARYING_SLOT_TEX0 + i;
257 this->new_texcoord[i]->explicit_location = true;
258 this->new_texcoord[i]->explicit_index = 0;
259 }
260
261 ir->head->insert_before(new_texcoord[i]);
262 }
263 }
264 }
265
266 /* Create dummy variables which will replace set-but-unused color and
267 * fog outputs.
268 */
269 external_color_usage |= info->tfeedback_color_usage;
270
271 for (int i = 0; i < 2; i++) {
272 char name[32];
273
274 if (!(external_color_usage & (1 << i))) {
275 if (info->color[i]) {
276 snprintf(name, 32, "gl_%s_FrontColor%i_dummy", mode_str, i);
277 this->new_color[i] =
278 new (ctx) ir_variable(glsl_type::vec4_type, name,
279 ir_var_temporary);
280 }
281
282 if (info->backcolor[i]) {
283 snprintf(name, 32, "gl_%s_BackColor%i_dummy", mode_str, i);
284 this->new_backcolor[i] =
285 new (ctx) ir_variable(glsl_type::vec4_type, name,
286 ir_var_temporary);
287 }
288 }
289 }
290
291 if (!external_has_fog && !info->tfeedback_has_fog &&
292 info->fog) {
293 char name[32];
294
295 snprintf(name, 32, "gl_%s_FogFragCoord_dummy", mode_str);
296 this->new_fog = new (ctx) ir_variable(glsl_type::float_type, name,
297 ir_var_temporary);
298 }
299
300 /* Now do the replacing. */
301 visit_list_elements(this, ir);
302 }
303
304 virtual ir_visitor_status visit(ir_variable *var)
305 {
306 /* Remove the gl_TexCoord array. */
307 if (this->info->lower_texcoord_array &&
308 var == this->info->texcoord_array) {
309 var->remove();
310 }
311
312 /* Replace set-but-unused color and fog outputs with dummy variables. */
313 for (int i = 0; i < 2; i++) {
314 if (var == this->info->color[i] && this->new_color[i]) {
315 var->replace_with(this->new_color[i]);
316 }
317 if (var == this->info->backcolor[i] &&
318 this->new_backcolor[i]) {
319 var->replace_with(this->new_backcolor[i]);
320 }
321 }
322
323 if (var == this->info->fog && this->new_fog) {
324 var->replace_with(this->new_fog);
325 }
326
327 return visit_continue;
328 }
329
330 virtual void handle_rvalue(ir_rvalue **rvalue)
331 {
332 if (!*rvalue)
333 return;
334
335 void *ctx = ralloc_parent(*rvalue);
336
337 /* Replace an array dereference gl_TexCoord[i] with a single
338 * variable dereference representing gl_TexCoord[i].
339 */
340 if (this->info->lower_texcoord_array) {
341 /* gl_TexCoord[i] occurence */
342 ir_dereference_array *const da = (*rvalue)->as_dereference_array();
343
344 if (da && da->variable_referenced() ==
345 this->info->texcoord_array) {
346 unsigned i = da->array_index->as_constant()->get_uint_component(0);
347
348 *rvalue = new(ctx) ir_dereference_variable(this->new_texcoord[i]);
349 return;
350 }
351 }
352
353 /* Replace set-but-unused color and fog outputs with dummy variables. */
354 ir_dereference_variable *const dv = (*rvalue)->as_dereference_variable();
355 if (!dv)
356 return;
357
358 ir_variable *var = dv->variable_referenced();
359
360 for (int i = 0; i < 2; i++) {
361 if (var == this->info->color[i] && this->new_color[i]) {
362 *rvalue = new(ctx) ir_dereference_variable(this->new_color[i]);
363 return;
364 }
365 if (var == this->info->backcolor[i] &&
366 this->new_backcolor[i]) {
367 *rvalue = new(ctx) ir_dereference_variable(this->new_backcolor[i]);
368 return;
369 }
370 }
371
372 if (var == this->info->fog && this->new_fog) {
373 *rvalue = new(ctx) ir_dereference_variable(this->new_fog);
374 }
375 }
376
377 virtual ir_visitor_status visit_leave(ir_assignment *ir)
378 {
379 handle_rvalue(&ir->rhs);
380 handle_rvalue(&ir->condition);
381
382 /* We have to use set_lhs when changing the LHS of an assignment. */
383 ir_rvalue *lhs = ir->lhs;
384
385 handle_rvalue(&lhs);
386 if (lhs != ir->lhs) {
387 ir->set_lhs(lhs);
388 }
389
390 return visit_continue;
391 }
392
393 private:
394 const varying_info_visitor *info;
395 ir_variable *new_texcoord[MAX_TEXTURE_COORD_UNITS];
396 ir_variable *new_color[2];
397 ir_variable *new_backcolor[2];
398 ir_variable *new_fog;
399 };
400
401 } /* anonymous namespace */
402
403 static void
404 lower_texcoord_array(exec_list *ir, const varying_info_visitor *info)
405 {
406 replace_varyings_visitor(ir, info,
407 (1 << MAX_TEXTURE_COORD_UNITS) - 1,
408 1 | 2, true);
409 }
410
411
412 void
413 do_dead_builtin_varyings(struct gl_context *ctx,
414 gl_shader *producer, gl_shader *consumer,
415 unsigned num_tfeedback_decls,
416 tfeedback_decl *tfeedback_decls)
417 {
418 /* This optimization has no effect with the core context and GLES2, because
419 * the built-in varyings we're eliminating here are not available there.
420 *
421 * EXT_separate_shader_objects doesn't allow this optimization,
422 * because a program object can be bound partially (e.g. only one
423 * stage of a program object can be bound).
424 */
425 if (ctx->API == API_OPENGL_CORE ||
426 ctx->API == API_OPENGLES2 ||
427 ctx->Extensions.EXT_separate_shader_objects) {
428 return;
429 }
430
431 /* Information about built-in varyings. */
432 varying_info_visitor producer_info(ir_var_shader_out);
433 varying_info_visitor consumer_info(ir_var_shader_in);
434
435 if (producer) {
436 producer_info.get(producer->ir, num_tfeedback_decls, tfeedback_decls);
437
438 if (!consumer) {
439 /* At least eliminate unused gl_TexCoord elements. */
440 if (producer_info.lower_texcoord_array) {
441 lower_texcoord_array(producer->ir, &producer_info);
442 }
443 return;
444 }
445 }
446
447 if (consumer) {
448 consumer_info.get(consumer->ir, 0, NULL);
449
450 if (!producer) {
451 /* At least eliminate unused gl_TexCoord elements. */
452 if (consumer_info.lower_texcoord_array) {
453 lower_texcoord_array(consumer->ir, &consumer_info);
454 }
455 return;
456 }
457 }
458
459 /* Eliminate the outputs unused by the consumer. */
460 if (producer_info.lower_texcoord_array ||
461 producer_info.color_usage ||
462 producer_info.has_fog) {
463 replace_varyings_visitor(producer->ir,
464 &producer_info,
465 consumer_info.texcoord_usage,
466 consumer_info.color_usage,
467 consumer_info.has_fog);
468 }
469
470 /* The gl_TexCoord fragment shader inputs can be initialized
471 * by GL_COORD_REPLACE, so we can't eliminate them.
472 *
473 * This doesn't prevent elimination of the gl_TexCoord elements which
474 * are not read by the fragment shader. We want to eliminate those anyway.
475 */
476 if (consumer->Type == GL_FRAGMENT_SHADER) {
477 producer_info.texcoord_usage = (1 << MAX_TEXTURE_COORD_UNITS) - 1;
478 }
479
480 /* Eliminate the inputs uninitialized by the producer. */
481 if (consumer_info.lower_texcoord_array ||
482 consumer_info.color_usage ||
483 consumer_info.has_fog) {
484 replace_varyings_visitor(consumer->ir,
485 &consumer_info,
486 producer_info.texcoord_usage,
487 producer_info.color_usage,
488 producer_info.has_fog);
489 }
490 }