| /* |
| * Copyright © 2013 Intel Corporation |
| * |
| * 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. |
| */ |
| #include "ir.h" |
| #include "ir_builder.h" |
| #include "ir_rvalue_visitor.h" |
| #include "ir_optimization.h" |
| #include "main/mtypes.h" |
| |
| using namespace ir_builder; |
| |
| namespace { |
| |
| class vector_deref_visitor : public ir_rvalue_enter_visitor { |
| public: |
| vector_deref_visitor(void *mem_ctx, gl_shader_stage shader_stage) |
| : progress(false), shader_stage(shader_stage), |
| factory(&factory_instructions, mem_ctx) |
| { |
| } |
| |
| virtual ~vector_deref_visitor() |
| { |
| } |
| |
| virtual void handle_rvalue(ir_rvalue **rv); |
| virtual ir_visitor_status visit_enter(ir_assignment *ir); |
| |
| bool progress; |
| gl_shader_stage shader_stage; |
| exec_list factory_instructions; |
| ir_factory factory; |
| }; |
| |
| } /* anonymous namespace */ |
| |
| ir_visitor_status |
| vector_deref_visitor::visit_enter(ir_assignment *ir) |
| { |
| if (!ir->lhs || ir->lhs->ir_type != ir_type_dereference_array) |
| return ir_rvalue_enter_visitor::visit_enter(ir); |
| |
| ir_dereference_array *const deref = (ir_dereference_array *) ir->lhs; |
| if (!deref->array->type->is_vector()) |
| return ir_rvalue_enter_visitor::visit_enter(ir); |
| |
| /* SSBOs and shared variables are backed by memory and may be accessed by |
| * multiple threads simultaneously. It's not safe to lower a single |
| * component store to a load-vec-store because it may race with writes to |
| * other components. |
| */ |
| ir_variable *var = deref->variable_referenced(); |
| if (var->data.mode == ir_var_shader_storage || |
| var->data.mode == ir_var_shader_shared) |
| return ir_rvalue_enter_visitor::visit_enter(ir); |
| |
| ir_rvalue *const new_lhs = deref->array; |
| |
| void *mem_ctx = ralloc_parent(ir); |
| ir_constant *old_index_constant = |
| deref->array_index->constant_expression_value(mem_ctx); |
| if (!old_index_constant) { |
| if (shader_stage == MESA_SHADER_TESS_CTRL && |
| deref->variable_referenced()->data.mode == ir_var_shader_out) { |
| /* Tessellation control shader outputs act as if they have memory |
| * backing them and if we have writes from multiple threads |
| * targeting the same vec4 (this can happen for patch outputs), the |
| * load-vec-store pattern of ir_triop_vector_insert doesn't work. |
| * Instead, we have to lower to a series of conditional write-masked |
| * assignments. |
| */ |
| ir_variable *const src_temp = |
| factory.make_temp(ir->rhs->type, "scalar_tmp"); |
| |
| /* The newly created variable declaration goes before the assignment |
| * because we're going to set it as the new LHS. |
| */ |
| ir->insert_before(factory.instructions); |
| ir->set_lhs(new(mem_ctx) ir_dereference_variable(src_temp)); |
| |
| ir_variable *const arr_index = |
| factory.make_temp(deref->array_index->type, "index_tmp"); |
| factory.emit(assign(arr_index, deref->array_index)); |
| |
| for (unsigned i = 0; i < new_lhs->type->vector_elements; i++) { |
| ir_constant *const cmp_index = |
| ir_constant::zero(factory.mem_ctx, deref->array_index->type); |
| cmp_index->value.u[0] = i; |
| |
| ir_rvalue *const lhs_clone = new_lhs->clone(factory.mem_ctx, NULL); |
| ir_dereference_variable *const src_temp_deref = |
| new(mem_ctx) ir_dereference_variable(src_temp); |
| |
| if (new_lhs->ir_type != ir_type_swizzle) { |
| assert(lhs_clone->as_dereference()); |
| ir_assignment *cond_assign = |
| new(mem_ctx) ir_assignment(lhs_clone->as_dereference(), |
| src_temp_deref, |
| equal(arr_index, cmp_index), |
| WRITEMASK_X << i); |
| factory.emit(cond_assign); |
| } else { |
| ir_assignment *cond_assign = |
| new(mem_ctx) ir_assignment(swizzle(lhs_clone, i, 1), |
| src_temp_deref, |
| equal(arr_index, cmp_index)); |
| factory.emit(cond_assign); |
| } |
| } |
| ir->insert_after(factory.instructions); |
| } else { |
| ir->rhs = new(mem_ctx) ir_expression(ir_triop_vector_insert, |
| new_lhs->type, |
| new_lhs->clone(mem_ctx, NULL), |
| ir->rhs, |
| deref->array_index); |
| ir->write_mask = (1 << new_lhs->type->vector_elements) - 1; |
| ir->set_lhs(new_lhs); |
| } |
| } else { |
| unsigned index = old_index_constant->get_uint_component(0); |
| |
| if (index >= new_lhs->type->vector_elements) { |
| /* Section 5.11 (Out-of-Bounds Accesses) of the GLSL 4.60 spec says: |
| * |
| * In the subsections described above for array, vector, matrix and |
| * structure accesses, any out-of-bounds access produced undefined |
| * behavior.... Out-of-bounds writes may be discarded or overwrite |
| * other variables of the active program. |
| */ |
| ir->remove(); |
| return visit_continue; |
| } |
| |
| if (new_lhs->ir_type != ir_type_swizzle) { |
| ir->set_lhs(new_lhs); |
| ir->write_mask = 1 << index; |
| } else { |
| /* If the "new" LHS is a swizzle, use the set_lhs helper to instead |
| * swizzle the RHS. |
| */ |
| unsigned component[1] = { index }; |
| ir->set_lhs(new(mem_ctx) ir_swizzle(new_lhs, component, 1)); |
| } |
| } |
| |
| return ir_rvalue_enter_visitor::visit_enter(ir); |
| } |
| |
| void |
| vector_deref_visitor::handle_rvalue(ir_rvalue **rv) |
| { |
| if (*rv == NULL || (*rv)->ir_type != ir_type_dereference_array) |
| return; |
| |
| ir_dereference_array *const deref = (ir_dereference_array *) *rv; |
| if (!deref->array->type->is_vector()) |
| return; |
| |
| /* Back-ends need to be able to handle derefs on vectors for SSBOs, UBOs, |
| * and shared variables. They have to handle it for writes anyway so we |
| * may as well require it for reads. |
| */ |
| ir_variable *var = deref->variable_referenced(); |
| if (var && (var->data.mode == ir_var_shader_storage || |
| var->data.mode == ir_var_shader_shared || |
| (var->data.mode == ir_var_uniform && |
| var->get_interface_type()))) |
| return; |
| |
| void *mem_ctx = ralloc_parent(deref); |
| *rv = new(mem_ctx) ir_expression(ir_binop_vector_extract, |
| deref->array, |
| deref->array_index); |
| } |
| |
| bool |
| lower_vector_derefs(gl_linked_shader *shader) |
| { |
| vector_deref_visitor v(shader->ir, shader->Stage); |
| |
| visit_list_elements(&v, shader->ir); |
| |
| return v.progress; |
| } |