glsl: Delete the vectorization opt pass.
Nothing uses it, and i965 was the last thing to. Even if I enable it for softpipe or crocus, it quickly causes NIR validation failures in shader-db from swizzles outside the bounds of vectors. Retire it in favor of nir_opt_vectorize(). Reviewed-by: Alyssa Rosenzweig <alyssa@collabora.com>. Reviewed-by: Marek Olšák <marek.olsak@amd.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/14249>
This commit is contained in:
@@ -2388,10 +2388,6 @@ do_common_optimization(exec_list *ir, bool linked,
|
|||||||
if (options->OptimizeForAOS && !linked)
|
if (options->OptimizeForAOS && !linked)
|
||||||
OPT(opt_flip_matrices, ir);
|
OPT(opt_flip_matrices, ir);
|
||||||
|
|
||||||
if (linked && options->OptimizeForAOS) {
|
|
||||||
OPT(do_vectorize, ir);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (linked)
|
if (linked)
|
||||||
OPT(do_dead_code, ir, uniform_locations_assigned);
|
OPT(do_dead_code, ir, uniform_locations_assigned);
|
||||||
else
|
else
|
||||||
|
@@ -127,7 +127,6 @@ bool do_mat_op_to_vec(exec_list *instructions);
|
|||||||
bool do_minmax_prune(exec_list *instructions);
|
bool do_minmax_prune(exec_list *instructions);
|
||||||
bool do_structure_splitting(exec_list *instructions);
|
bool do_structure_splitting(exec_list *instructions);
|
||||||
bool optimize_swizzles(exec_list *instructions);
|
bool optimize_swizzles(exec_list *instructions);
|
||||||
bool do_vectorize(exec_list *instructions);
|
|
||||||
bool do_tree_grafting(exec_list *instructions);
|
bool do_tree_grafting(exec_list *instructions);
|
||||||
bool do_vec_index_to_cond_assign(exec_list *instructions);
|
bool do_vec_index_to_cond_assign(exec_list *instructions);
|
||||||
bool do_vec_index_to_swizzle(exec_list *instructions);
|
bool do_vec_index_to_swizzle(exec_list *instructions);
|
||||||
|
@@ -208,7 +208,6 @@ files_libglsl = files(
|
|||||||
'opt_structure_splitting.cpp',
|
'opt_structure_splitting.cpp',
|
||||||
'opt_swizzle.cpp',
|
'opt_swizzle.cpp',
|
||||||
'opt_tree_grafting.cpp',
|
'opt_tree_grafting.cpp',
|
||||||
'opt_vectorize.cpp',
|
|
||||||
'program.h',
|
'program.h',
|
||||||
'propagate_invariance.cpp',
|
'propagate_invariance.cpp',
|
||||||
's_expression.cpp',
|
's_expression.cpp',
|
||||||
|
@@ -1,407 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \file opt_vectorize.cpp
|
|
||||||
*
|
|
||||||
* Combines scalar assignments of the same expression (modulo swizzle) to
|
|
||||||
* multiple channels of the same variable into a single vectorized expression
|
|
||||||
* and assignment.
|
|
||||||
*
|
|
||||||
* Many generated shaders contain scalarized code. That is, they contain
|
|
||||||
*
|
|
||||||
* r1.x = log2(v0.x);
|
|
||||||
* r1.y = log2(v0.y);
|
|
||||||
* r1.z = log2(v0.z);
|
|
||||||
*
|
|
||||||
* rather than
|
|
||||||
*
|
|
||||||
* r1.xyz = log2(v0.xyz);
|
|
||||||
*
|
|
||||||
* We look for consecutive assignments of the same expression (modulo swizzle)
|
|
||||||
* to each channel of the same variable.
|
|
||||||
*
|
|
||||||
* For instance, we want to convert these three scalar operations
|
|
||||||
*
|
|
||||||
* (assign (x) (var_ref r1) (expression float log2 (swiz x (var_ref v0))))
|
|
||||||
* (assign (y) (var_ref r1) (expression float log2 (swiz y (var_ref v0))))
|
|
||||||
* (assign (z) (var_ref r1) (expression float log2 (swiz z (var_ref v0))))
|
|
||||||
*
|
|
||||||
* into a single vector operation
|
|
||||||
*
|
|
||||||
* (assign (xyz) (var_ref r1) (expression vec3 log2 (swiz xyz (var_ref v0))))
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ir.h"
|
|
||||||
#include "ir_visitor.h"
|
|
||||||
#include "ir_optimization.h"
|
|
||||||
#include "compiler/glsl_types.h"
|
|
||||||
#include "program/prog_instruction.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class ir_vectorize_visitor : public ir_hierarchical_visitor {
|
|
||||||
public:
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
assignment[0] = NULL;
|
|
||||||
assignment[1] = NULL;
|
|
||||||
assignment[2] = NULL;
|
|
||||||
assignment[3] = NULL;
|
|
||||||
current_assignment = NULL;
|
|
||||||
last_assignment = NULL;
|
|
||||||
channels = 0;
|
|
||||||
has_swizzle = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ir_vectorize_visitor()
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
progress = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ir_visitor_status visit_enter(ir_assignment *);
|
|
||||||
virtual ir_visitor_status visit_enter(ir_swizzle *);
|
|
||||||
virtual ir_visitor_status visit_enter(ir_dereference_array *);
|
|
||||||
virtual ir_visitor_status visit_enter(ir_expression *);
|
|
||||||
virtual ir_visitor_status visit_enter(ir_if *);
|
|
||||||
virtual ir_visitor_status visit_enter(ir_loop *);
|
|
||||||
virtual ir_visitor_status visit_enter(ir_texture *);
|
|
||||||
|
|
||||||
virtual ir_visitor_status visit_leave(ir_assignment *);
|
|
||||||
|
|
||||||
void try_vectorize();
|
|
||||||
|
|
||||||
ir_assignment *assignment[4];
|
|
||||||
ir_assignment *current_assignment, *last_assignment;
|
|
||||||
unsigned channels;
|
|
||||||
bool has_swizzle;
|
|
||||||
|
|
||||||
bool progress;
|
|
||||||
};
|
|
||||||
|
|
||||||
} /* unnamed namespace */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rewrites the swizzles and types of a right-hand side of an assignment.
|
|
||||||
*
|
|
||||||
* From the example above, this function would be called (by visit_tree()) on
|
|
||||||
* the nodes of the tree (expression float log2 (swiz z (var_ref v0))),
|
|
||||||
* rewriting it into (expression vec3 log2 (swiz xyz (var_ref v0))).
|
|
||||||
*
|
|
||||||
* The function operates on ir_expressions (and its operands) and ir_swizzles.
|
|
||||||
* For expressions it sets a new type and swizzles any non-expression and non-
|
|
||||||
* swizzle scalar operands into appropriately sized vector arguments. For
|
|
||||||
* example, if combining
|
|
||||||
*
|
|
||||||
* (assign (x) (var_ref r1) (expression float + (swiz x (var_ref v0) (var_ref v1))))
|
|
||||||
* (assign (y) (var_ref r1) (expression float + (swiz y (var_ref v0) (var_ref v1))))
|
|
||||||
*
|
|
||||||
* where v1 is a scalar, rewrite_swizzle() would insert a swizzle on
|
|
||||||
* (var_ref v1) such that the final result was
|
|
||||||
*
|
|
||||||
* (assign (xy) (var_ref r1) (expression vec2 + (swiz xy (var_ref v0))
|
|
||||||
* (swiz xx (var_ref v1))))
|
|
||||||
*
|
|
||||||
* For swizzles, it sets a new type, and if the variable being swizzled is a
|
|
||||||
* vector it overwrites the swizzle mask with the ir_swizzle_mask passed as the
|
|
||||||
* data parameter. If the swizzled variable is scalar, then the swizzle was
|
|
||||||
* added by an earlier call to rewrite_swizzle() on an expression, so the
|
|
||||||
* mask should not be modified.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
rewrite_swizzle(ir_instruction *ir, void *data)
|
|
||||||
{
|
|
||||||
ir_swizzle_mask *mask = (ir_swizzle_mask *)data;
|
|
||||||
|
|
||||||
switch (ir->ir_type) {
|
|
||||||
case ir_type_swizzle: {
|
|
||||||
ir_swizzle *swz = (ir_swizzle *)ir;
|
|
||||||
if (swz->val->type->is_vector()) {
|
|
||||||
swz->mask = *mask;
|
|
||||||
}
|
|
||||||
swz->type = glsl_type::get_instance(swz->type->base_type,
|
|
||||||
mask->num_components, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ir_type_expression: {
|
|
||||||
ir_expression *expr = (ir_expression *)ir;
|
|
||||||
expr->type = glsl_type::get_instance(expr->type->base_type,
|
|
||||||
mask->num_components, 1);
|
|
||||||
for (unsigned i = 0; i < 4; i++) {
|
|
||||||
if (expr->operands[i]) {
|
|
||||||
ir_rvalue *rval = expr->operands[i]->as_rvalue();
|
|
||||||
if (rval && rval->type->is_scalar() &&
|
|
||||||
!rval->as_expression() && !rval->as_swizzle()) {
|
|
||||||
expr->operands[i] = new(ir) ir_swizzle(rval, 0, 0, 0, 0,
|
|
||||||
mask->num_components);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to vectorize the previously saved assignments, and clear them from
|
|
||||||
* consideration.
|
|
||||||
*
|
|
||||||
* If the assignments are able to be combined, it modifies in-place the last
|
|
||||||
* assignment seen to be an equivalent vector form of the scalar assignments.
|
|
||||||
* It then removes the other now obsolete scalar assignments.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
ir_vectorize_visitor::try_vectorize()
|
|
||||||
{
|
|
||||||
if (this->last_assignment && this->channels > 1) {
|
|
||||||
ir_swizzle_mask mask = {0, 0, 0, 0, channels, 0};
|
|
||||||
|
|
||||||
this->last_assignment->write_mask = 0;
|
|
||||||
|
|
||||||
for (unsigned i = 0, j = 0; i < 4; i++) {
|
|
||||||
if (this->assignment[i]) {
|
|
||||||
this->last_assignment->write_mask |= 1 << i;
|
|
||||||
|
|
||||||
if (this->assignment[i] != this->last_assignment) {
|
|
||||||
this->assignment[i]->remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (j) {
|
|
||||||
case 0: mask.x = i; break;
|
|
||||||
case 1: mask.y = i; break;
|
|
||||||
case 2: mask.z = i; break;
|
|
||||||
case 3: mask.w = i; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visit_tree(this->last_assignment->rhs, rewrite_swizzle, &mask);
|
|
||||||
|
|
||||||
this->progress = true;
|
|
||||||
}
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the write mask is a single channel.
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
single_channel_write_mask(unsigned write_mask)
|
|
||||||
{
|
|
||||||
return write_mask != 0 && (write_mask & (write_mask - 1)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Translates single-channeled write mask to single-channeled swizzle.
|
|
||||||
*/
|
|
||||||
static unsigned
|
|
||||||
write_mask_to_swizzle(unsigned write_mask)
|
|
||||||
{
|
|
||||||
switch (write_mask) {
|
|
||||||
case WRITEMASK_X: return SWIZZLE_X;
|
|
||||||
case WRITEMASK_Y: return SWIZZLE_Y;
|
|
||||||
case WRITEMASK_Z: return SWIZZLE_Z;
|
|
||||||
case WRITEMASK_W: return SWIZZLE_W;
|
|
||||||
}
|
|
||||||
unreachable("not reached");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether a single-channeled write mask matches a swizzle.
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
write_mask_matches_swizzle(unsigned write_mask,
|
|
||||||
const ir_swizzle *swz)
|
|
||||||
{
|
|
||||||
return ((write_mask == WRITEMASK_X && swz->mask.x == SWIZZLE_X) ||
|
|
||||||
(write_mask == WRITEMASK_Y && swz->mask.x == SWIZZLE_Y) ||
|
|
||||||
(write_mask == WRITEMASK_Z && swz->mask.x == SWIZZLE_Z) ||
|
|
||||||
(write_mask == WRITEMASK_W && swz->mask.x == SWIZZLE_W));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upon entering an ir_assignment, attempt to vectorize the currently tracked
|
|
||||||
* assignments if the current assignment is not suitable. Keep a pointer to
|
|
||||||
* the current assignment.
|
|
||||||
*/
|
|
||||||
ir_visitor_status
|
|
||||||
ir_vectorize_visitor::visit_enter(ir_assignment *ir)
|
|
||||||
{
|
|
||||||
ir_dereference *lhs = this->last_assignment != NULL ?
|
|
||||||
this->last_assignment->lhs : NULL;
|
|
||||||
ir_rvalue *rhs = this->last_assignment != NULL ?
|
|
||||||
this->last_assignment->rhs : NULL;
|
|
||||||
|
|
||||||
if (ir->condition ||
|
|
||||||
this->channels >= 4 ||
|
|
||||||
!single_channel_write_mask(ir->write_mask) ||
|
|
||||||
this->assignment[write_mask_to_swizzle(ir->write_mask)] != NULL ||
|
|
||||||
(lhs && !ir->lhs->equals(lhs)) ||
|
|
||||||
(rhs && !ir->rhs->equals(rhs, ir_type_swizzle))) {
|
|
||||||
try_vectorize();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->current_assignment = ir;
|
|
||||||
|
|
||||||
return visit_continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upon entering an ir_swizzle, set ::has_swizzle if we're visiting from an
|
|
||||||
* ir_assignment (i.e., that ::current_assignment is set) and the swizzle mask
|
|
||||||
* matches the current assignment's write mask.
|
|
||||||
*
|
|
||||||
* If the write mask doesn't match the swizzle mask, remove the current
|
|
||||||
* assignment from further consideration.
|
|
||||||
*/
|
|
||||||
ir_visitor_status
|
|
||||||
ir_vectorize_visitor::visit_enter(ir_swizzle *ir)
|
|
||||||
{
|
|
||||||
if (this->current_assignment) {
|
|
||||||
if (write_mask_matches_swizzle(this->current_assignment->write_mask, ir)) {
|
|
||||||
this->has_swizzle = true;
|
|
||||||
} else {
|
|
||||||
this->current_assignment = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return visit_continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Upon entering an ir_array_dereference, remove the current assignment from
|
|
||||||
* further consideration. Since the index of an array dereference must scalar,
|
|
||||||
* we are not able to vectorize it.
|
|
||||||
*
|
|
||||||
* FINISHME: If all of scalar indices are identical we could vectorize.
|
|
||||||
*/
|
|
||||||
ir_visitor_status
|
|
||||||
ir_vectorize_visitor::visit_enter(ir_dereference_array *)
|
|
||||||
{
|
|
||||||
this->current_assignment = NULL;
|
|
||||||
return visit_continue_with_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upon entering an ir_expression, remove the current assignment from further
|
|
||||||
* consideration if the expression operates horizontally on vectors.
|
|
||||||
*/
|
|
||||||
ir_visitor_status
|
|
||||||
ir_vectorize_visitor::visit_enter(ir_expression *ir)
|
|
||||||
{
|
|
||||||
if (ir->is_horizontal()) {
|
|
||||||
this->current_assignment = NULL;
|
|
||||||
return visit_continue_with_parent;
|
|
||||||
}
|
|
||||||
return visit_continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Since there is no statement to visit between the "then" and "else"
|
|
||||||
* instructions try to vectorize before, in between, and after them to avoid
|
|
||||||
* combining statements from different basic blocks.
|
|
||||||
*/
|
|
||||||
ir_visitor_status
|
|
||||||
ir_vectorize_visitor::visit_enter(ir_if *ir)
|
|
||||||
{
|
|
||||||
try_vectorize();
|
|
||||||
|
|
||||||
visit_list_elements(this, &ir->then_instructions);
|
|
||||||
try_vectorize();
|
|
||||||
|
|
||||||
visit_list_elements(this, &ir->else_instructions);
|
|
||||||
try_vectorize();
|
|
||||||
|
|
||||||
return visit_continue_with_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Since there is no statement to visit between the instructions in the body of
|
|
||||||
* the loop and the instructions after it try to vectorize before and after the
|
|
||||||
* body to avoid combining statements from different basic blocks.
|
|
||||||
*/
|
|
||||||
ir_visitor_status
|
|
||||||
ir_vectorize_visitor::visit_enter(ir_loop *ir)
|
|
||||||
{
|
|
||||||
try_vectorize();
|
|
||||||
|
|
||||||
visit_list_elements(this, &ir->body_instructions);
|
|
||||||
try_vectorize();
|
|
||||||
|
|
||||||
return visit_continue_with_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upon entering an ir_texture, remove the current assignment from
|
|
||||||
* further consideration. Vectorizing multiple texture lookups into one
|
|
||||||
* is wrong.
|
|
||||||
*/
|
|
||||||
ir_visitor_status
|
|
||||||
ir_vectorize_visitor::visit_enter(ir_texture *)
|
|
||||||
{
|
|
||||||
this->current_assignment = NULL;
|
|
||||||
return visit_continue_with_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upon leaving an ir_assignment, save a pointer to it in ::assignment[] if
|
|
||||||
* the swizzle mask(s) found were appropriate. Also save a pointer in
|
|
||||||
* ::last_assignment so that we can compare future assignments with it.
|
|
||||||
*
|
|
||||||
* Finally, clear ::current_assignment and ::has_swizzle.
|
|
||||||
*/
|
|
||||||
ir_visitor_status
|
|
||||||
ir_vectorize_visitor::visit_leave(ir_assignment *ir)
|
|
||||||
{
|
|
||||||
if (this->has_swizzle && this->current_assignment) {
|
|
||||||
assert(this->current_assignment == ir);
|
|
||||||
|
|
||||||
unsigned channel = write_mask_to_swizzle(this->current_assignment->write_mask);
|
|
||||||
this->assignment[channel] = ir;
|
|
||||||
this->channels++;
|
|
||||||
|
|
||||||
this->last_assignment = this->current_assignment;
|
|
||||||
}
|
|
||||||
this->current_assignment = NULL;
|
|
||||||
this->has_swizzle = false;
|
|
||||||
return visit_continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Combines scalar assignments of the same expression (modulo swizzle) to
|
|
||||||
* multiple channels of the same variable into a single vectorized expression
|
|
||||||
* and assignment.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
do_vectorize(exec_list *instructions)
|
|
||||||
{
|
|
||||||
ir_vectorize_visitor v;
|
|
||||||
|
|
||||||
v.run(instructions);
|
|
||||||
|
|
||||||
/* Try to vectorize the last assignments seen. */
|
|
||||||
v.try_vectorize();
|
|
||||||
|
|
||||||
return v.progress;
|
|
||||||
}
|
|
Reference in New Issue
Block a user