glsl2: Add ir_assignment::write_mask and associated methods
Replace swizzles on the LHS with additional swizzles on the RHS and a write mask in the assignment instruction. As part of this add ir_assignment::set_lhs. Ideally we'd make ir_assignment::lhs private to prevent erroneous writes, but that would require a lot of code butchery at this point. Add ir_assignment constructor that takes an explicit write mask. This is required for ir_assignment::clone, but it can also be used in other places. Without this, ir_assignment clones lose their write masks, and incorrect IR is generated in optimization passes. Add ir_assignment::whole_variable_written method. This method gets the variable on the LHS if the whole variable is written or NULL otherwise. This is different from ir->lhs->whole_variable_referenced() because the latter has no knowledge of the write mask stored in the ir_assignment. Gut all code from ir_to_mesa that handled swizzles on the LHS of assignments. There is probably some other refactoring that could be done here, but that can be left for another day.
This commit is contained in:
113
src/glsl/ir.cpp
113
src/glsl/ir.cpp
@@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "main/imports.h"
|
#include "main/imports.h"
|
||||||
|
#include "main/macros.h"
|
||||||
#include "ir.h"
|
#include "ir.h"
|
||||||
#include "ir_visitor.h"
|
#include "ir_visitor.h"
|
||||||
#include "glsl_types.h"
|
#include "glsl_types.h"
|
||||||
@@ -31,13 +32,121 @@ ir_rvalue::ir_rvalue()
|
|||||||
this->type = glsl_type::error_type;
|
this->type = glsl_type::error_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify the swizzle make to move one component to another
|
||||||
|
*
|
||||||
|
* \param m IR swizzle to be modified
|
||||||
|
* \param from Component in the RHS that is to be swizzled
|
||||||
|
* \param to Desired swizzle location of \c from
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
update_rhs_swizzle(ir_swizzle_mask &m, unsigned from, unsigned to)
|
||||||
|
{
|
||||||
|
switch (to) {
|
||||||
|
case 0: m.x = from; break;
|
||||||
|
case 1: m.y = from; break;
|
||||||
|
case 2: m.z = from; break;
|
||||||
|
case 3: m.w = from; break;
|
||||||
|
default: assert(!"Should not get here.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m.num_components = MAX2(m.num_components, (to + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ir_assignment::set_lhs(ir_rvalue *lhs)
|
||||||
|
{
|
||||||
|
while (lhs != NULL) {
|
||||||
|
ir_swizzle *swiz = lhs->as_swizzle();
|
||||||
|
|
||||||
|
if (swiz == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
unsigned write_mask = 0;
|
||||||
|
ir_swizzle_mask rhs_swiz = { 0, 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < swiz->mask.num_components; i++) {
|
||||||
|
unsigned c = 0;
|
||||||
|
|
||||||
|
switch (i) {
|
||||||
|
case 0: c = swiz->mask.x; break;
|
||||||
|
case 1: c = swiz->mask.y; break;
|
||||||
|
case 2: c = swiz->mask.z; break;
|
||||||
|
case 3: c = swiz->mask.w; break;
|
||||||
|
default: assert(!"Should not get here.");
|
||||||
|
}
|
||||||
|
|
||||||
|
write_mask |= (((this->write_mask >> i) & 1) << c);
|
||||||
|
update_rhs_swizzle(rhs_swiz, i, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->write_mask = write_mask;
|
||||||
|
lhs = swiz->val;
|
||||||
|
|
||||||
|
this->rhs = new(this) ir_swizzle(this->rhs, rhs_swiz);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert((lhs == NULL) || lhs->as_dereference());
|
||||||
|
|
||||||
|
this->lhs = (ir_dereference *) lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ir_variable *
|
||||||
|
ir_assignment::whole_variable_written()
|
||||||
|
{
|
||||||
|
ir_variable *v = this->lhs->whole_variable_referenced();
|
||||||
|
|
||||||
|
if (v == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (v->type->is_scalar())
|
||||||
|
return v;
|
||||||
|
|
||||||
|
if (v->type->is_vector()) {
|
||||||
|
const unsigned mask = (1U << v->type->vector_elements) - 1;
|
||||||
|
|
||||||
|
if (mask != this->write_mask)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Either all the vector components are assigned or the variable is some
|
||||||
|
* composite type (and the whole thing is assigned.
|
||||||
|
*/
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
ir_assignment::ir_assignment(ir_dereference *lhs, ir_rvalue *rhs,
|
||||||
|
ir_rvalue *condition, unsigned write_mask)
|
||||||
|
{
|
||||||
|
this->ir_type = ir_type_assignment;
|
||||||
|
this->condition = condition;
|
||||||
|
this->rhs = rhs;
|
||||||
|
this->lhs = lhs;
|
||||||
|
this->write_mask = write_mask;
|
||||||
|
}
|
||||||
|
|
||||||
ir_assignment::ir_assignment(ir_rvalue *lhs, ir_rvalue *rhs,
|
ir_assignment::ir_assignment(ir_rvalue *lhs, ir_rvalue *rhs,
|
||||||
ir_rvalue *condition)
|
ir_rvalue *condition)
|
||||||
{
|
{
|
||||||
this->ir_type = ir_type_assignment;
|
this->ir_type = ir_type_assignment;
|
||||||
this->lhs = lhs;
|
|
||||||
this->rhs = rhs;
|
|
||||||
this->condition = condition;
|
this->condition = condition;
|
||||||
|
this->rhs = rhs;
|
||||||
|
|
||||||
|
/* If the RHS is a vector type, assume that all components of the vector
|
||||||
|
* type are being written to the LHS. The write mask comes from the RHS
|
||||||
|
* because we can have a case where the LHS is a vec4 and the RHS is a
|
||||||
|
* vec3. In that case, the assignment is:
|
||||||
|
*
|
||||||
|
* (assign (...) (xyz) (var_ref lhs) (var_ref rhs))
|
||||||
|
*/
|
||||||
|
if (rhs->type->is_vector())
|
||||||
|
this->write_mask = (1U << rhs->type->vector_elements) - 1;
|
||||||
|
else if (rhs->type->is_scalar())
|
||||||
|
this->write_mask = 1;
|
||||||
|
else
|
||||||
|
this->write_mask = 0;
|
||||||
|
|
||||||
|
this->set_lhs(lhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -514,6 +514,16 @@ class ir_assignment : public ir_instruction {
|
|||||||
public:
|
public:
|
||||||
ir_assignment(ir_rvalue *lhs, ir_rvalue *rhs, ir_rvalue *condition);
|
ir_assignment(ir_rvalue *lhs, ir_rvalue *rhs, ir_rvalue *condition);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an assignment with an explicit write mask
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* Since a write mask is supplied, the LHS must already be a bare
|
||||||
|
* \c ir_dereference. The cannot be any swizzles in the LHS.
|
||||||
|
*/
|
||||||
|
ir_assignment(ir_dereference *lhs, ir_rvalue *rhs, ir_rvalue *condition,
|
||||||
|
unsigned write_mask);
|
||||||
|
|
||||||
virtual ir_assignment *clone(void *mem_ctx, struct hash_table *ht) const;
|
virtual ir_assignment *clone(void *mem_ctx, struct hash_table *ht) const;
|
||||||
|
|
||||||
virtual ir_constant *constant_expression_value();
|
virtual ir_constant *constant_expression_value();
|
||||||
@@ -531,9 +541,31 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Left-hand side of the assignment.
|
* Get a whole variable written by an assignment
|
||||||
|
*
|
||||||
|
* If the LHS of the assignment writes a whole variable, the variable is
|
||||||
|
* returned. Otherwise \c NULL is returned. Examples of whole-variable
|
||||||
|
* assignment are:
|
||||||
|
*
|
||||||
|
* - Assigning to a scalar
|
||||||
|
* - Assigning to all components of a vector
|
||||||
|
* - Whole array (or matrix) assignment
|
||||||
|
* - Whole structure assignment
|
||||||
*/
|
*/
|
||||||
ir_rvalue *lhs;
|
ir_variable *whole_variable_written();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the LHS of an assignment
|
||||||
|
*/
|
||||||
|
void set_lhs(ir_rvalue *lhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Left-hand side of the assignment.
|
||||||
|
*
|
||||||
|
* This should be treated as read only. If you need to set the LHS of an
|
||||||
|
* assignment, use \c ir_assignment::set_lhs.
|
||||||
|
*/
|
||||||
|
ir_dereference *lhs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value being assigned
|
* Value being assigned
|
||||||
@@ -544,6 +576,16 @@ public:
|
|||||||
* Optional condition for the assignment.
|
* Optional condition for the assignment.
|
||||||
*/
|
*/
|
||||||
ir_rvalue *condition;
|
ir_rvalue *condition;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component mask written
|
||||||
|
*
|
||||||
|
* For non-vector types in the LHS, this field will be zero. For vector
|
||||||
|
* types, a bit will be set for each component that is written. Note that
|
||||||
|
* for \c vec2 and \c vec3 types only the lower bits will ever be set.
|
||||||
|
*/
|
||||||
|
unsigned write_mask:4;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Update ir_expression::num_operands() and operator_strs when
|
/* Update ir_expression::num_operands() and operator_strs when
|
||||||
|
@@ -242,7 +242,8 @@ ir_assignment::clone(void *mem_ctx, struct hash_table *ht) const
|
|||||||
|
|
||||||
return new(mem_ctx) ir_assignment(this->lhs->clone(mem_ctx, ht),
|
return new(mem_ctx) ir_assignment(this->lhs->clone(mem_ctx, ht),
|
||||||
this->rhs->clone(mem_ctx, ht),
|
this->rhs->clone(mem_ctx, ht),
|
||||||
new_condition);
|
new_condition,
|
||||||
|
this->write_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
ir_function *
|
ir_function *
|
||||||
|
@@ -110,7 +110,7 @@ ir_constant_variable_visitor::visit_enter(ir_assignment *ir)
|
|||||||
return visit_continue;
|
return visit_continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ir_variable *var = ir->lhs->whole_variable_referenced();
|
ir_variable *var = ir->whole_variable_written();
|
||||||
if (!var)
|
if (!var)
|
||||||
return visit_continue;
|
return visit_continue;
|
||||||
|
|
||||||
|
@@ -224,7 +224,7 @@ add_copy(void *ctx, ir_assignment *ir, exec_list *acp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ir_variable *lhs_var = ir->lhs->whole_variable_referenced();
|
ir_variable *lhs_var = ir->whole_variable_written();
|
||||||
ir_variable *rhs_var = ir->rhs->whole_variable_referenced();
|
ir_variable *rhs_var = ir->rhs->whole_variable_referenced();
|
||||||
|
|
||||||
if ((lhs_var != NULL) && (rhs_var != NULL)) {
|
if ((lhs_var != NULL) && (rhs_var != NULL)) {
|
||||||
|
@@ -137,7 +137,7 @@ process_assignment(void *ctx, ir_assignment *ir, exec_list *assignments)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Now, check if we did a whole-variable assignment. */
|
/* Now, check if we did a whole-variable assignment. */
|
||||||
if (always_assign && (ir->lhs->whole_variable_referenced() != NULL)) {
|
if (always_assign && (ir->whole_variable_written() != NULL)) {
|
||||||
/* We did a whole-variable assignment. So, any instruction in
|
/* We did a whole-variable assignment. So, any instruction in
|
||||||
* the assignment list with the same LHS is dead.
|
* the assignment list with the same LHS is dead.
|
||||||
*/
|
*/
|
||||||
|
@@ -296,7 +296,19 @@ void ir_print_visitor::visit(ir_assignment *ir)
|
|||||||
else
|
else
|
||||||
printf("(constant bool (1))");
|
printf("(constant bool (1))");
|
||||||
|
|
||||||
printf(" ");
|
|
||||||
|
char mask[5];
|
||||||
|
unsigned j = 0;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 4; i++) {
|
||||||
|
if ((ir->write_mask & (1 << i)) != 0) {
|
||||||
|
mask[j] = "xyzw"[i];
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mask[j] = '\0';
|
||||||
|
|
||||||
|
printf(" (%s) ", mask);
|
||||||
|
|
||||||
ir->lhs->accept(this);
|
ir->lhs->accept(this);
|
||||||
|
|
||||||
|
@@ -315,7 +315,7 @@ tree_grafting_basic_block(ir_instruction *bb_first,
|
|||||||
if (!assign)
|
if (!assign)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ir_variable *lhs_var = assign->lhs->whole_variable_referenced();
|
ir_variable *lhs_var = assign->whole_variable_written();
|
||||||
if (!lhs_var)
|
if (!lhs_var)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@@ -107,7 +107,7 @@ ir_vec_index_to_swizzle_visitor::visit_enter(ir_swizzle *ir)
|
|||||||
ir_visitor_status
|
ir_visitor_status
|
||||||
ir_vec_index_to_swizzle_visitor::visit_enter(ir_assignment *ir)
|
ir_vec_index_to_swizzle_visitor::visit_enter(ir_assignment *ir)
|
||||||
{
|
{
|
||||||
ir->lhs = convert_vec_index_to_swizzle(ir->lhs);
|
ir->set_lhs(convert_vec_index_to_swizzle(ir->lhs));
|
||||||
ir->rhs = convert_vec_index_to_swizzle(ir->rhs);
|
ir->rhs = convert_vec_index_to_swizzle(ir->rhs);
|
||||||
|
|
||||||
return visit_continue;
|
return visit_continue;
|
||||||
|
@@ -356,6 +356,7 @@ ir_to_mesa_visitor::ir_to_mesa_emit_op1(ir_instruction *ir,
|
|||||||
ir_to_mesa_dst_reg dst,
|
ir_to_mesa_dst_reg dst,
|
||||||
ir_to_mesa_src_reg src0)
|
ir_to_mesa_src_reg src0)
|
||||||
{
|
{
|
||||||
|
assert(dst.writemask != 0);
|
||||||
return ir_to_mesa_emit_op3(ir, op, dst,
|
return ir_to_mesa_emit_op3(ir, op, dst,
|
||||||
src0, ir_to_mesa_undef, ir_to_mesa_undef);
|
src0, ir_to_mesa_undef, ir_to_mesa_undef);
|
||||||
}
|
}
|
||||||
@@ -1615,21 +1616,17 @@ ir_to_mesa_visitor::visit(ir_dereference_record *ir)
|
|||||||
* We want to be careful in assignment setup to hit the actual storage
|
* We want to be careful in assignment setup to hit the actual storage
|
||||||
* instead of potentially using a temporary like we might with the
|
* instead of potentially using a temporary like we might with the
|
||||||
* ir_dereference handler.
|
* ir_dereference handler.
|
||||||
*
|
|
||||||
* Thanks to ir_swizzle_swizzle, and ir_vec_index_to_swizzle, we
|
|
||||||
* should only see potentially one variable array index of a vector,
|
|
||||||
* and one swizzle, before getting to actual vec4 storage. So handle
|
|
||||||
* those, then go use ir_dereference to handle the rest.
|
|
||||||
*/
|
*/
|
||||||
static struct ir_to_mesa_dst_reg
|
static struct ir_to_mesa_dst_reg
|
||||||
get_assignment_lhs(ir_instruction *ir, ir_to_mesa_visitor *v,
|
get_assignment_lhs(ir_dereference *ir, ir_to_mesa_visitor *v,
|
||||||
ir_to_mesa_src_reg *r)
|
ir_to_mesa_src_reg *r)
|
||||||
{
|
{
|
||||||
struct ir_to_mesa_dst_reg dst_reg;
|
/* The LHS must be a dereference. If the LHS is a variable indexed array
|
||||||
ir_swizzle *swiz;
|
* access of a vector, it must be separated into a series conditional moves
|
||||||
|
* before reaching this point (see ir_vec_index_to_cond_assign).
|
||||||
|
*/
|
||||||
|
assert(ir->as_dereference());
|
||||||
ir_dereference_array *deref_array = ir->as_dereference_array();
|
ir_dereference_array *deref_array = ir->as_dereference_array();
|
||||||
/* This should have been handled by ir_vec_index_to_cond_assign */
|
|
||||||
if (deref_array) {
|
if (deref_array) {
|
||||||
assert(!deref_array->array->type->is_vector());
|
assert(!deref_array->array->type->is_vector());
|
||||||
}
|
}
|
||||||
@@ -1638,38 +1635,7 @@ get_assignment_lhs(ir_instruction *ir, ir_to_mesa_visitor *v,
|
|||||||
* swizzles in it and write swizzles using writemask, though.
|
* swizzles in it and write swizzles using writemask, though.
|
||||||
*/
|
*/
|
||||||
ir->accept(v);
|
ir->accept(v);
|
||||||
dst_reg = ir_to_mesa_dst_reg_from_src(v->result);
|
return ir_to_mesa_dst_reg_from_src(v->result);
|
||||||
|
|
||||||
if ((swiz = ir->as_swizzle())) {
|
|
||||||
int swizzles[4] = {
|
|
||||||
swiz->mask.x,
|
|
||||||
swiz->mask.y,
|
|
||||||
swiz->mask.z,
|
|
||||||
swiz->mask.w
|
|
||||||
};
|
|
||||||
int new_r_swizzle[4];
|
|
||||||
int orig_r_swizzle = r->swizzle;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < 4; i++) {
|
|
||||||
new_r_swizzle[i] = GET_SWZ(orig_r_swizzle, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
dst_reg.writemask = 0;
|
|
||||||
for (i = 0; i < 4; i++) {
|
|
||||||
if (i < swiz->mask.num_components) {
|
|
||||||
dst_reg.writemask |= 1 << swizzles[i];
|
|
||||||
new_r_swizzle[swizzles[i]] = GET_SWZ(orig_r_swizzle, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r->swizzle = MAKE_SWIZZLE4(new_r_swizzle[0],
|
|
||||||
new_r_swizzle[1],
|
|
||||||
new_r_swizzle[2],
|
|
||||||
new_r_swizzle[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dst_reg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1684,6 +1650,23 @@ ir_to_mesa_visitor::visit(ir_assignment *ir)
|
|||||||
|
|
||||||
l = get_assignment_lhs(ir->lhs, this, &r);
|
l = get_assignment_lhs(ir->lhs, this, &r);
|
||||||
|
|
||||||
|
/* FINISHME: This should really set to the correct maximal writemask for each
|
||||||
|
* FINISHME: component written (in the loops below). This case can only
|
||||||
|
* FINISHME: occur for matrices, arrays, and structures.
|
||||||
|
*/
|
||||||
|
if (ir->write_mask == 0) {
|
||||||
|
assert(!ir->lhs->type->is_scalar() && !ir->lhs->type->is_vector());
|
||||||
|
l.writemask = WRITEMASK_XYZW;
|
||||||
|
} else if (ir->lhs->type->is_scalar()) {
|
||||||
|
/* FINISHME: This hack makes writing to gl_FragData, which lives in the
|
||||||
|
* FINISHME: W component of fragment shader output zero, work correctly.
|
||||||
|
*/
|
||||||
|
l.writemask = WRITEMASK_XYZW;
|
||||||
|
} else {
|
||||||
|
assert(ir->lhs->type->is_vector());
|
||||||
|
l.writemask = ir->write_mask;
|
||||||
|
}
|
||||||
|
|
||||||
assert(l.file != PROGRAM_UNDEFINED);
|
assert(l.file != PROGRAM_UNDEFINED);
|
||||||
assert(r.file != PROGRAM_UNDEFINED);
|
assert(r.file != PROGRAM_UNDEFINED);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user