/* * Copyright ©2019 Collabora Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * \file lower_xfb_varying.cpp * */ #include "ir.h" #include "main/mtypes.h" #include "glsl_symbol_table.h" #include "util/strndup.h" namespace { /** * Visitor that splices varying packing code before every return. */ class lower_xfb_var_splicer : public ir_hierarchical_visitor { public: explicit lower_xfb_var_splicer(void *mem_ctx, const exec_list *instructions); virtual ir_visitor_status visit_leave(ir_return *ret); virtual ir_visitor_status visit_leave(ir_function_signature *sig); private: /** * Memory context used to allocate new instructions for the shader. */ void * const mem_ctx; /** * Instructions that should be spliced into place before each return. */ const exec_list *instructions; }; } /* anonymous namespace */ lower_xfb_var_splicer::lower_xfb_var_splicer(void *mem_ctx, const exec_list *instructions) : mem_ctx(mem_ctx), instructions(instructions) { } ir_visitor_status lower_xfb_var_splicer::visit_leave(ir_return *ret) { foreach_in_list(ir_instruction, ir, this->instructions) { ret->insert_before(ir->clone(this->mem_ctx, NULL)); } return visit_continue; } /** Insert a copy-back assignment at the end of the main() function */ ir_visitor_status lower_xfb_var_splicer::visit_leave(ir_function_signature *sig) { if (strcmp(sig->function_name(), "main") != 0) return visit_continue; if (((ir_instruction*)sig->body.get_tail())->ir_type == ir_type_return) return visit_continue; foreach_in_list(ir_instruction, ir, this->instructions) { sig->body.push_tail(ir->clone(this->mem_ctx, NULL)); } return visit_continue; } static char* get_field_name(const char *name) { const char *first_dot = strchr(name, '.'); const char *first_square_bracket = strchr(name, '['); int name_size = 0; if (!first_square_bracket && !first_dot) name_size = strlen(name); else if ((!first_square_bracket || (first_dot && first_dot < first_square_bracket))) name_size = first_dot - name; else name_size = first_square_bracket - name; return strndup(name, name_size); } /* Generate a new name given the old xfb declaration string by replacing dots * with '_', brackets with '@' and appending "-xfb" */ static char * generate_new_name(void *mem_ctx, const char *name) { char *new_name; unsigned i = 0; new_name = ralloc_strdup(mem_ctx, name); while (new_name[i]) { if (new_name[i] == '.') { new_name[i] = '_'; } else if (new_name[i] == '[' || new_name[i] == ']') { new_name[i] = '@'; } i++; } if (!ralloc_strcat(&new_name, "-xfb")) { ralloc_free(new_name); return NULL; } return new_name; } /* Get the dereference for the given variable name. The method is called * recursively to parse array indices and struct members. */ static bool get_deref(void *ctx, const char *name, struct gl_linked_shader *shader, ir_dereference **deref, const glsl_type **type) { if (name[0] == '\0') { /* End */ return (*deref != NULL); } else if (name[0] == '[') { /* Array index */ char *endptr = NULL; unsigned index; index = strtol(name + 1, &endptr, 10); assert(*type != NULL && (*type)->is_array() && endptr[0] == ']'); *deref = new(ctx) ir_dereference_array(*deref, new(ctx) ir_constant(index)); *type = (*type)->without_array(); return get_deref(ctx, endptr + 1, shader, deref, type); } else if (name[0] == '.') { /* Struct member */ char *field = get_field_name(name + 1); assert(*type != NULL && (*type)->is_struct() && field != NULL); *deref = new(ctx) ir_dereference_record(*deref, field); *type = (*type)->field_type(field); assert(*type != glsl_type::error_type); name += 1 + strlen(field); free(field); return get_deref(ctx, name, shader, deref, type); } else { /* Top level variable */ char *field = get_field_name(name); ir_variable *toplevel_var; toplevel_var = shader->symbols->get_variable(field); name += strlen(field); free(field); if (toplevel_var == NULL) { return false; } *deref = new (ctx) ir_dereference_variable(toplevel_var); *type = toplevel_var->type; return get_deref(ctx, name, shader, deref, type); } } ir_variable * lower_xfb_varying(void *mem_ctx, struct gl_linked_shader *shader, const char *old_var_name) { exec_list new_instructions; char *new_var_name; ir_dereference *deref = NULL; const glsl_type *type = NULL; if (!get_deref(mem_ctx, old_var_name, shader, &deref, &type)) { if (deref) { delete deref; } return NULL; } new_var_name = generate_new_name(mem_ctx, old_var_name); ir_variable *new_variable = new(mem_ctx) ir_variable(type, new_var_name, ir_var_shader_out); new_variable->data.assigned = true; new_variable->data.used = true; shader->ir->push_head(new_variable); ralloc_free(new_var_name); ir_dereference *lhs = new(mem_ctx) ir_dereference_variable(new_variable); ir_assignment *new_assignment = new(mem_ctx) ir_assignment(lhs, deref); new_instructions.push_tail(new_assignment); lower_xfb_var_splicer splicer(mem_ctx, &new_instructions); visit_list_elements(&splicer, shader->ir); return new_variable; }