mesa/src/compiler/glsl/ir.cpp
Timothy Arceri 3ceae88642 glsl: set mask via initialisation list rather than in constructor body
Potentially more efficient as it may avoid the struct being initialised
twice.

Also add var to the initialisation list while we are here.

Reviewed-by: Samuel Pitoiset <samuel.pitoiset@gmail.com>
2017-05-22 14:21:55 +10:00

2098 lines
56 KiB
C++

/*
* Copyright © 2010 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 <string.h>
#include "main/core.h" /* for MAX2 */
#include "ir.h"
#include "compiler/glsl_types.h"
#include "glsl_parser_extras.h"
ir_rvalue::ir_rvalue(enum ir_node_type t)
: ir_instruction(t)
{
this->type = glsl_type::error_type;
}
bool ir_rvalue::is_zero() const
{
return false;
}
bool ir_rvalue::is_one() const
{
return false;
}
bool ir_rvalue::is_negative_one() const
{
return false;
}
/**
* 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.");
}
}
void
ir_assignment::set_lhs(ir_rvalue *lhs)
{
void *mem_ctx = this;
bool swizzled = false;
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);
rhs_swiz.num_components = swiz->val->type->vector_elements;
}
this->write_mask = write_mask;
lhs = swiz->val;
this->rhs = new(mem_ctx) ir_swizzle(this->rhs, rhs_swiz);
swizzled = true;
}
if (swizzled) {
/* Now, RHS channels line up with the LHS writemask. Collapse it
* to just the channels that will be written.
*/
ir_swizzle_mask rhs_swiz = { 0, 0, 0, 0, 0, 0 };
int rhs_chan = 0;
for (int i = 0; i < 4; i++) {
if (write_mask & (1 << i))
update_rhs_swizzle(rhs_swiz, i, rhs_chan++);
}
rhs_swiz.num_components = rhs_chan;
this->rhs = new(mem_ctx) 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)
: ir_instruction(ir_type_assignment)
{
this->condition = condition;
this->rhs = rhs;
this->lhs = lhs;
this->write_mask = write_mask;
if (lhs->type->is_scalar() || lhs->type->is_vector()) {
int lhs_components = 0;
for (int i = 0; i < 4; i++) {
if (write_mask & (1 << i))
lhs_components++;
}
assert(lhs_components == this->rhs->type->vector_elements);
}
}
ir_assignment::ir_assignment(ir_rvalue *lhs, ir_rvalue *rhs,
ir_rvalue *condition)
: ir_instruction(ir_type_assignment)
{
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);
}
ir_expression::ir_expression(int op, const struct glsl_type *type,
ir_rvalue *op0, ir_rvalue *op1,
ir_rvalue *op2, ir_rvalue *op3)
: ir_rvalue(ir_type_expression)
{
this->type = type;
this->operation = ir_expression_operation(op);
this->operands[0] = op0;
this->operands[1] = op1;
this->operands[2] = op2;
this->operands[3] = op3;
#ifndef NDEBUG
int num_operands = get_num_operands(this->operation);
for (int i = num_operands; i < 4; i++) {
assert(this->operands[i] == NULL);
}
#endif
}
ir_expression::ir_expression(int op, ir_rvalue *op0)
: ir_rvalue(ir_type_expression)
{
this->operation = ir_expression_operation(op);
this->operands[0] = op0;
this->operands[1] = NULL;
this->operands[2] = NULL;
this->operands[3] = NULL;
assert(op <= ir_last_unop);
switch (this->operation) {
case ir_unop_bit_not:
case ir_unop_logic_not:
case ir_unop_neg:
case ir_unop_abs:
case ir_unop_sign:
case ir_unop_rcp:
case ir_unop_rsq:
case ir_unop_sqrt:
case ir_unop_exp:
case ir_unop_log:
case ir_unop_exp2:
case ir_unop_log2:
case ir_unop_trunc:
case ir_unop_ceil:
case ir_unop_floor:
case ir_unop_fract:
case ir_unop_round_even:
case ir_unop_sin:
case ir_unop_cos:
case ir_unop_dFdx:
case ir_unop_dFdx_coarse:
case ir_unop_dFdx_fine:
case ir_unop_dFdy:
case ir_unop_dFdy_coarse:
case ir_unop_dFdy_fine:
case ir_unop_bitfield_reverse:
case ir_unop_interpolate_at_centroid:
case ir_unop_saturate:
this->type = op0->type;
break;
case ir_unop_f2i:
case ir_unop_b2i:
case ir_unop_u2i:
case ir_unop_d2i:
case ir_unop_bitcast_f2i:
case ir_unop_bit_count:
case ir_unop_find_msb:
case ir_unop_find_lsb:
case ir_unop_subroutine_to_int:
case ir_unop_i642i:
case ir_unop_u642i:
this->type = glsl_type::get_instance(GLSL_TYPE_INT,
op0->type->vector_elements, 1);
break;
case ir_unop_b2f:
case ir_unop_i2f:
case ir_unop_u2f:
case ir_unop_d2f:
case ir_unop_bitcast_i2f:
case ir_unop_bitcast_u2f:
case ir_unop_i642f:
case ir_unop_u642f:
this->type = glsl_type::get_instance(GLSL_TYPE_FLOAT,
op0->type->vector_elements, 1);
break;
case ir_unop_f2b:
case ir_unop_i2b:
case ir_unop_d2b:
case ir_unop_i642b:
this->type = glsl_type::get_instance(GLSL_TYPE_BOOL,
op0->type->vector_elements, 1);
break;
case ir_unop_f2d:
case ir_unop_i2d:
case ir_unop_u2d:
case ir_unop_i642d:
case ir_unop_u642d:
this->type = glsl_type::get_instance(GLSL_TYPE_DOUBLE,
op0->type->vector_elements, 1);
break;
case ir_unop_i2u:
case ir_unop_f2u:
case ir_unop_d2u:
case ir_unop_bitcast_f2u:
case ir_unop_i642u:
case ir_unop_u642u:
this->type = glsl_type::get_instance(GLSL_TYPE_UINT,
op0->type->vector_elements, 1);
break;
case ir_unop_i2i64:
case ir_unop_u2i64:
case ir_unop_b2i64:
case ir_unop_f2i64:
case ir_unop_d2i64:
case ir_unop_u642i64:
this->type = glsl_type::get_instance(GLSL_TYPE_INT64,
op0->type->vector_elements, 1);
break;
case ir_unop_i2u64:
case ir_unop_u2u64:
case ir_unop_f2u64:
case ir_unop_d2u64:
case ir_unop_i642u64:
this->type = glsl_type::get_instance(GLSL_TYPE_UINT64,
op0->type->vector_elements, 1);
break;
case ir_unop_noise:
this->type = glsl_type::float_type;
break;
case ir_unop_unpack_double_2x32:
case ir_unop_unpack_uint_2x32:
this->type = glsl_type::uvec2_type;
break;
case ir_unop_unpack_int_2x32:
this->type = glsl_type::ivec2_type;
break;
case ir_unop_pack_snorm_2x16:
case ir_unop_pack_snorm_4x8:
case ir_unop_pack_unorm_2x16:
case ir_unop_pack_unorm_4x8:
case ir_unop_pack_half_2x16:
this->type = glsl_type::uint_type;
break;
case ir_unop_pack_double_2x32:
this->type = glsl_type::double_type;
break;
case ir_unop_pack_int_2x32:
this->type = glsl_type::int64_t_type;
break;
case ir_unop_pack_uint_2x32:
this->type = glsl_type::uint64_t_type;
break;
case ir_unop_unpack_snorm_2x16:
case ir_unop_unpack_unorm_2x16:
case ir_unop_unpack_half_2x16:
this->type = glsl_type::vec2_type;
break;
case ir_unop_unpack_snorm_4x8:
case ir_unop_unpack_unorm_4x8:
this->type = glsl_type::vec4_type;
break;
case ir_unop_unpack_sampler_2x32:
case ir_unop_unpack_image_2x32:
this->type = glsl_type::uvec2_type;
break;
case ir_unop_pack_sampler_2x32:
case ir_unop_pack_image_2x32:
this->type = op0->type;
break;
case ir_unop_frexp_sig:
this->type = op0->type;
break;
case ir_unop_frexp_exp:
this->type = glsl_type::get_instance(GLSL_TYPE_INT,
op0->type->vector_elements, 1);
break;
case ir_unop_get_buffer_size:
case ir_unop_ssbo_unsized_array_length:
this->type = glsl_type::int_type;
break;
case ir_unop_bitcast_i642d:
case ir_unop_bitcast_u642d:
this->type = glsl_type::get_instance(GLSL_TYPE_DOUBLE,
op0->type->vector_elements, 1);
break;
case ir_unop_bitcast_d2i64:
this->type = glsl_type::get_instance(GLSL_TYPE_INT64,
op0->type->vector_elements, 1);
break;
case ir_unop_bitcast_d2u64:
this->type = glsl_type::get_instance(GLSL_TYPE_UINT64,
op0->type->vector_elements, 1);
break;
default:
assert(!"not reached: missing automatic type setup for ir_expression");
this->type = op0->type;
break;
}
}
ir_expression::ir_expression(int op, ir_rvalue *op0, ir_rvalue *op1)
: ir_rvalue(ir_type_expression)
{
this->operation = ir_expression_operation(op);
this->operands[0] = op0;
this->operands[1] = op1;
this->operands[2] = NULL;
this->operands[3] = NULL;
assert(op > ir_last_unop);
switch (this->operation) {
case ir_binop_all_equal:
case ir_binop_any_nequal:
this->type = glsl_type::bool_type;
break;
case ir_binop_add:
case ir_binop_sub:
case ir_binop_min:
case ir_binop_max:
case ir_binop_pow:
case ir_binop_mul:
case ir_binop_div:
case ir_binop_mod:
if (op0->type->is_scalar()) {
this->type = op1->type;
} else if (op1->type->is_scalar()) {
this->type = op0->type;
} else {
if (this->operation == ir_binop_mul) {
this->type = glsl_type::get_mul_type(op0->type, op1->type);
} else {
assert(op0->type == op1->type);
this->type = op0->type;
}
}
break;
case ir_binop_logic_and:
case ir_binop_logic_xor:
case ir_binop_logic_or:
case ir_binop_bit_and:
case ir_binop_bit_xor:
case ir_binop_bit_or:
assert(!op0->type->is_matrix());
assert(!op1->type->is_matrix());
if (op0->type->is_scalar()) {
this->type = op1->type;
} else if (op1->type->is_scalar()) {
this->type = op0->type;
} else {
assert(op0->type->vector_elements == op1->type->vector_elements);
this->type = op0->type;
}
break;
case ir_binop_equal:
case ir_binop_nequal:
case ir_binop_lequal:
case ir_binop_gequal:
case ir_binop_less:
case ir_binop_greater:
assert(op0->type == op1->type);
this->type = glsl_type::get_instance(GLSL_TYPE_BOOL,
op0->type->vector_elements, 1);
break;
case ir_binop_dot:
this->type = op0->type->get_base_type();
break;
case ir_binop_imul_high:
case ir_binop_carry:
case ir_binop_borrow:
case ir_binop_lshift:
case ir_binop_rshift:
case ir_binop_ldexp:
case ir_binop_interpolate_at_offset:
case ir_binop_interpolate_at_sample:
this->type = op0->type;
break;
case ir_binop_vector_extract:
this->type = op0->type->get_scalar_type();
break;
default:
assert(!"not reached: missing automatic type setup for ir_expression");
this->type = glsl_type::float_type;
}
}
ir_expression::ir_expression(int op, ir_rvalue *op0, ir_rvalue *op1,
ir_rvalue *op2)
: ir_rvalue(ir_type_expression)
{
this->operation = ir_expression_operation(op);
this->operands[0] = op0;
this->operands[1] = op1;
this->operands[2] = op2;
this->operands[3] = NULL;
assert(op > ir_last_binop && op <= ir_last_triop);
switch (this->operation) {
case ir_triop_fma:
case ir_triop_lrp:
case ir_triop_bitfield_extract:
case ir_triop_vector_insert:
this->type = op0->type;
break;
case ir_triop_csel:
this->type = op1->type;
break;
default:
assert(!"not reached: missing automatic type setup for ir_expression");
this->type = glsl_type::float_type;
}
}
unsigned int
ir_expression::get_num_operands(ir_expression_operation op)
{
assert(op <= ir_last_opcode);
if (op <= ir_last_unop)
return 1;
if (op <= ir_last_binop)
return 2;
if (op <= ir_last_triop)
return 3;
if (op <= ir_last_quadop)
return 4;
assert(false);
return 0;
}
#include "ir_expression_operation_strings.h"
const char*
depth_layout_string(ir_depth_layout layout)
{
switch(layout) {
case ir_depth_layout_none: return "";
case ir_depth_layout_any: return "depth_any";
case ir_depth_layout_greater: return "depth_greater";
case ir_depth_layout_less: return "depth_less";
case ir_depth_layout_unchanged: return "depth_unchanged";
default:
assert(0);
return "";
}
}
ir_expression_operation
ir_expression::get_operator(const char *str)
{
for (int op = 0; op <= int(ir_last_opcode); op++) {
if (strcmp(str, ir_expression_operation_strings[op]) == 0)
return (ir_expression_operation) op;
}
return (ir_expression_operation) -1;
}
ir_variable *
ir_expression::variable_referenced() const
{
switch (operation) {
case ir_binop_vector_extract:
case ir_triop_vector_insert:
/* We get these for things like a[0] where a is a vector type. In these
* cases we want variable_referenced() to return the actual vector
* variable this is wrapping.
*/
return operands[0]->variable_referenced();
default:
return ir_rvalue::variable_referenced();
}
}
ir_constant::ir_constant()
: ir_rvalue(ir_type_constant)
{
this->array_elements = NULL;
}
ir_constant::ir_constant(const struct glsl_type *type,
const ir_constant_data *data)
: ir_rvalue(ir_type_constant)
{
this->array_elements = NULL;
assert((type->base_type >= GLSL_TYPE_UINT)
&& (type->base_type <= GLSL_TYPE_IMAGE));
this->type = type;
memcpy(& this->value, data, sizeof(this->value));
}
ir_constant::ir_constant(float f, unsigned vector_elements)
: ir_rvalue(ir_type_constant)
{
assert(vector_elements <= 4);
this->type = glsl_type::get_instance(GLSL_TYPE_FLOAT, vector_elements, 1);
for (unsigned i = 0; i < vector_elements; i++) {
this->value.f[i] = f;
}
for (unsigned i = vector_elements; i < 16; i++) {
this->value.f[i] = 0;
}
}
ir_constant::ir_constant(double d, unsigned vector_elements)
: ir_rvalue(ir_type_constant)
{
assert(vector_elements <= 4);
this->type = glsl_type::get_instance(GLSL_TYPE_DOUBLE, vector_elements, 1);
for (unsigned i = 0; i < vector_elements; i++) {
this->value.d[i] = d;
}
for (unsigned i = vector_elements; i < 16; i++) {
this->value.d[i] = 0.0;
}
}
ir_constant::ir_constant(unsigned int u, unsigned vector_elements)
: ir_rvalue(ir_type_constant)
{
assert(vector_elements <= 4);
this->type = glsl_type::get_instance(GLSL_TYPE_UINT, vector_elements, 1);
for (unsigned i = 0; i < vector_elements; i++) {
this->value.u[i] = u;
}
for (unsigned i = vector_elements; i < 16; i++) {
this->value.u[i] = 0;
}
}
ir_constant::ir_constant(int integer, unsigned vector_elements)
: ir_rvalue(ir_type_constant)
{
assert(vector_elements <= 4);
this->type = glsl_type::get_instance(GLSL_TYPE_INT, vector_elements, 1);
for (unsigned i = 0; i < vector_elements; i++) {
this->value.i[i] = integer;
}
for (unsigned i = vector_elements; i < 16; i++) {
this->value.i[i] = 0;
}
}
ir_constant::ir_constant(uint64_t u64, unsigned vector_elements)
: ir_rvalue(ir_type_constant)
{
assert(vector_elements <= 4);
this->type = glsl_type::get_instance(GLSL_TYPE_UINT64, vector_elements, 1);
for (unsigned i = 0; i < vector_elements; i++) {
this->value.u64[i] = u64;
}
for (unsigned i = vector_elements; i < 16; i++) {
this->value.u64[i] = 0;
}
}
ir_constant::ir_constant(int64_t int64, unsigned vector_elements)
: ir_rvalue(ir_type_constant)
{
assert(vector_elements <= 4);
this->type = glsl_type::get_instance(GLSL_TYPE_INT64, vector_elements, 1);
for (unsigned i = 0; i < vector_elements; i++) {
this->value.i64[i] = int64;
}
for (unsigned i = vector_elements; i < 16; i++) {
this->value.i64[i] = 0;
}
}
ir_constant::ir_constant(bool b, unsigned vector_elements)
: ir_rvalue(ir_type_constant)
{
assert(vector_elements <= 4);
this->type = glsl_type::get_instance(GLSL_TYPE_BOOL, vector_elements, 1);
for (unsigned i = 0; i < vector_elements; i++) {
this->value.b[i] = b;
}
for (unsigned i = vector_elements; i < 16; i++) {
this->value.b[i] = false;
}
}
ir_constant::ir_constant(const ir_constant *c, unsigned i)
: ir_rvalue(ir_type_constant)
{
this->array_elements = NULL;
this->type = c->type->get_base_type();
switch (this->type->base_type) {
case GLSL_TYPE_UINT: this->value.u[0] = c->value.u[i]; break;
case GLSL_TYPE_INT: this->value.i[0] = c->value.i[i]; break;
case GLSL_TYPE_FLOAT: this->value.f[0] = c->value.f[i]; break;
case GLSL_TYPE_BOOL: this->value.b[0] = c->value.b[i]; break;
case GLSL_TYPE_DOUBLE: this->value.d[0] = c->value.d[i]; break;
default: assert(!"Should not get here."); break;
}
}
ir_constant::ir_constant(const struct glsl_type *type, exec_list *value_list)
: ir_rvalue(ir_type_constant)
{
this->array_elements = NULL;
this->type = type;
assert(type->is_scalar() || type->is_vector() || type->is_matrix()
|| type->is_record() || type->is_array());
if (type->is_array()) {
this->array_elements = ralloc_array(this, ir_constant *, type->length);
unsigned i = 0;
foreach_in_list(ir_constant, value, value_list) {
assert(value->as_constant() != NULL);
this->array_elements[i++] = value;
}
return;
}
/* If the constant is a record, the types of each of the entries in
* value_list must be a 1-for-1 match with the structure components. Each
* entry must also be a constant. Just move the nodes from the value_list
* to the list in the ir_constant.
*/
/* FINISHME: Should there be some type checking and / or assertions here? */
/* FINISHME: Should the new constant take ownership of the nodes from
* FINISHME: value_list, or should it make copies?
*/
if (type->is_record()) {
value_list->move_nodes_to(& this->components);
return;
}
for (unsigned i = 0; i < 16; i++) {
this->value.u[i] = 0;
}
ir_constant *value = (ir_constant *) (value_list->get_head_raw());
/* Constructors with exactly one scalar argument are special for vectors
* and matrices. For vectors, the scalar value is replicated to fill all
* the components. For matrices, the scalar fills the components of the
* diagonal while the rest is filled with 0.
*/
if (value->type->is_scalar() && value->next->is_tail_sentinel()) {
if (type->is_matrix()) {
/* Matrix - fill diagonal (rest is already set to 0) */
assert(type->is_float() || type->is_double());
for (unsigned i = 0; i < type->matrix_columns; i++) {
if (type->is_float())
this->value.f[i * type->vector_elements + i] =
value->value.f[0];
else
this->value.d[i * type->vector_elements + i] =
value->value.d[0];
}
} else {
/* Vector or scalar - fill all components */
switch (type->base_type) {
case GLSL_TYPE_UINT:
case GLSL_TYPE_INT:
for (unsigned i = 0; i < type->components(); i++)
this->value.u[i] = value->value.u[0];
break;
case GLSL_TYPE_FLOAT:
for (unsigned i = 0; i < type->components(); i++)
this->value.f[i] = value->value.f[0];
break;
case GLSL_TYPE_DOUBLE:
for (unsigned i = 0; i < type->components(); i++)
this->value.d[i] = value->value.d[0];
break;
case GLSL_TYPE_UINT64:
case GLSL_TYPE_INT64:
for (unsigned i = 0; i < type->components(); i++)
this->value.u64[i] = value->value.u64[0];
break;
case GLSL_TYPE_BOOL:
for (unsigned i = 0; i < type->components(); i++)
this->value.b[i] = value->value.b[0];
break;
default:
assert(!"Should not get here.");
break;
}
}
return;
}
if (type->is_matrix() && value->type->is_matrix()) {
assert(value->next->is_tail_sentinel());
/* From section 5.4.2 of the GLSL 1.20 spec:
* "If a matrix is constructed from a matrix, then each component
* (column i, row j) in the result that has a corresponding component
* (column i, row j) in the argument will be initialized from there."
*/
unsigned cols = MIN2(type->matrix_columns, value->type->matrix_columns);
unsigned rows = MIN2(type->vector_elements, value->type->vector_elements);
for (unsigned i = 0; i < cols; i++) {
for (unsigned j = 0; j < rows; j++) {
const unsigned src = i * value->type->vector_elements + j;
const unsigned dst = i * type->vector_elements + j;
this->value.f[dst] = value->value.f[src];
}
}
/* "All other components will be initialized to the identity matrix." */
for (unsigned i = cols; i < type->matrix_columns; i++)
this->value.f[i * type->vector_elements + i] = 1.0;
return;
}
/* Use each component from each entry in the value_list to initialize one
* component of the constant being constructed.
*/
unsigned i = 0;
for (;;) {
assert(value->as_constant() != NULL);
assert(!value->is_tail_sentinel());
for (unsigned j = 0; j < value->type->components(); j++) {
switch (type->base_type) {
case GLSL_TYPE_UINT:
this->value.u[i] = value->get_uint_component(j);
break;
case GLSL_TYPE_INT:
this->value.i[i] = value->get_int_component(j);
break;
case GLSL_TYPE_FLOAT:
this->value.f[i] = value->get_float_component(j);
break;
case GLSL_TYPE_BOOL:
this->value.b[i] = value->get_bool_component(j);
break;
case GLSL_TYPE_DOUBLE:
this->value.d[i] = value->get_double_component(j);
break;
case GLSL_TYPE_UINT64:
this->value.u64[i] = value->get_uint64_component(j);
break;
case GLSL_TYPE_INT64:
this->value.i64[i] = value->get_int64_component(j);
break;
default:
/* FINISHME: What to do? Exceptions are not the answer.
*/
break;
}
i++;
if (i >= type->components())
break;
}
if (i >= type->components())
break; /* avoid downcasting a list sentinel */
value = (ir_constant *) value->next;
}
}
ir_constant *
ir_constant::zero(void *mem_ctx, const glsl_type *type)
{
assert(type->is_scalar() || type->is_vector() || type->is_matrix()
|| type->is_record() || type->is_array());
ir_constant *c = new(mem_ctx) ir_constant;
c->type = type;
memset(&c->value, 0, sizeof(c->value));
if (type->is_array()) {
c->array_elements = ralloc_array(c, ir_constant *, type->length);
for (unsigned i = 0; i < type->length; i++)
c->array_elements[i] = ir_constant::zero(c, type->fields.array);
}
if (type->is_record()) {
for (unsigned i = 0; i < type->length; i++) {
ir_constant *comp = ir_constant::zero(mem_ctx, type->fields.structure[i].type);
c->components.push_tail(comp);
}
}
return c;
}
bool
ir_constant::get_bool_component(unsigned i) const
{
switch (this->type->base_type) {
case GLSL_TYPE_UINT: return this->value.u[i] != 0;
case GLSL_TYPE_INT: return this->value.i[i] != 0;
case GLSL_TYPE_FLOAT: return ((int)this->value.f[i]) != 0;
case GLSL_TYPE_BOOL: return this->value.b[i];
case GLSL_TYPE_DOUBLE: return this->value.d[i] != 0.0;
case GLSL_TYPE_UINT64: return this->value.u64[i] != 0;
case GLSL_TYPE_INT64: return this->value.i64[i] != 0;
default: assert(!"Should not get here."); break;
}
/* Must return something to make the compiler happy. This is clearly an
* error case.
*/
return false;
}
float
ir_constant::get_float_component(unsigned i) const
{
switch (this->type->base_type) {
case GLSL_TYPE_UINT: return (float) this->value.u[i];
case GLSL_TYPE_INT: return (float) this->value.i[i];
case GLSL_TYPE_FLOAT: return this->value.f[i];
case GLSL_TYPE_BOOL: return this->value.b[i] ? 1.0f : 0.0f;
case GLSL_TYPE_DOUBLE: return (float) this->value.d[i];
case GLSL_TYPE_UINT64: return (float) this->value.u64[i];
case GLSL_TYPE_INT64: return (float) this->value.i64[i];
default: assert(!"Should not get here."); break;
}
/* Must return something to make the compiler happy. This is clearly an
* error case.
*/
return 0.0;
}
double
ir_constant::get_double_component(unsigned i) const
{
switch (this->type->base_type) {
case GLSL_TYPE_UINT: return (double) this->value.u[i];
case GLSL_TYPE_INT: return (double) this->value.i[i];
case GLSL_TYPE_FLOAT: return (double) this->value.f[i];
case GLSL_TYPE_BOOL: return this->value.b[i] ? 1.0 : 0.0;
case GLSL_TYPE_DOUBLE: return this->value.d[i];
case GLSL_TYPE_UINT64: return (double) this->value.u64[i];
case GLSL_TYPE_INT64: return (double) this->value.i64[i];
default: assert(!"Should not get here."); break;
}
/* Must return something to make the compiler happy. This is clearly an
* error case.
*/
return 0.0;
}
int
ir_constant::get_int_component(unsigned i) const
{
switch (this->type->base_type) {
case GLSL_TYPE_UINT: return this->value.u[i];
case GLSL_TYPE_INT: return this->value.i[i];
case GLSL_TYPE_FLOAT: return (int) this->value.f[i];
case GLSL_TYPE_BOOL: return this->value.b[i] ? 1 : 0;
case GLSL_TYPE_DOUBLE: return (int) this->value.d[i];
case GLSL_TYPE_UINT64: return (int) this->value.u64[i];
case GLSL_TYPE_INT64: return (int) this->value.i64[i];
default: assert(!"Should not get here."); break;
}
/* Must return something to make the compiler happy. This is clearly an
* error case.
*/
return 0;
}
unsigned
ir_constant::get_uint_component(unsigned i) const
{
switch (this->type->base_type) {
case GLSL_TYPE_UINT: return this->value.u[i];
case GLSL_TYPE_INT: return this->value.i[i];
case GLSL_TYPE_FLOAT: return (unsigned) this->value.f[i];
case GLSL_TYPE_BOOL: return this->value.b[i] ? 1 : 0;
case GLSL_TYPE_DOUBLE: return (unsigned) this->value.d[i];
case GLSL_TYPE_UINT64: return (unsigned) this->value.u64[i];
case GLSL_TYPE_INT64: return (unsigned) this->value.i64[i];
default: assert(!"Should not get here."); break;
}
/* Must return something to make the compiler happy. This is clearly an
* error case.
*/
return 0;
}
int64_t
ir_constant::get_int64_component(unsigned i) const
{
switch (this->type->base_type) {
case GLSL_TYPE_UINT: return this->value.u[i];
case GLSL_TYPE_INT: return this->value.i[i];
case GLSL_TYPE_FLOAT: return (int64_t) this->value.f[i];
case GLSL_TYPE_BOOL: return this->value.b[i] ? 1 : 0;
case GLSL_TYPE_DOUBLE: return (int64_t) this->value.d[i];
case GLSL_TYPE_UINT64: return (int64_t) this->value.u64[i];
case GLSL_TYPE_INT64: return this->value.i64[i];
default: assert(!"Should not get here."); break;
}
/* Must return something to make the compiler happy. This is clearly an
* error case.
*/
return 0;
}
uint64_t
ir_constant::get_uint64_component(unsigned i) const
{
switch (this->type->base_type) {
case GLSL_TYPE_UINT: return this->value.u[i];
case GLSL_TYPE_INT: return this->value.i[i];
case GLSL_TYPE_FLOAT: return (uint64_t) this->value.f[i];
case GLSL_TYPE_BOOL: return this->value.b[i] ? 1 : 0;
case GLSL_TYPE_DOUBLE: return (uint64_t) this->value.d[i];
case GLSL_TYPE_UINT64: return this->value.u64[i];
case GLSL_TYPE_INT64: return (uint64_t) this->value.i64[i];
default: assert(!"Should not get here."); break;
}
/* Must return something to make the compiler happy. This is clearly an
* error case.
*/
return 0;
}
ir_constant *
ir_constant::get_array_element(unsigned i) const
{
assert(this->type->is_array());
/* From page 35 (page 41 of the PDF) of the GLSL 1.20 spec:
*
* "Behavior is undefined if a shader subscripts an array with an index
* less than 0 or greater than or equal to the size the array was
* declared with."
*
* Most out-of-bounds accesses are removed before things could get this far.
* There are cases where non-constant array index values can get constant
* folded.
*/
if (int(i) < 0)
i = 0;
else if (i >= this->type->length)
i = this->type->length - 1;
return array_elements[i];
}
ir_constant *
ir_constant::get_record_field(const char *name)
{
int idx = this->type->field_index(name);
if (idx < 0)
return NULL;
if (this->components.is_empty())
return NULL;
exec_node *node = this->components.get_head_raw();
for (int i = 0; i < idx; i++) {
node = node->next;
/* If the end of the list is encountered before the element matching the
* requested field is found, return NULL.
*/
if (node->is_tail_sentinel())
return NULL;
}
return (ir_constant *) node;
}
void
ir_constant::copy_offset(ir_constant *src, int offset)
{
switch (this->type->base_type) {
case GLSL_TYPE_UINT:
case GLSL_TYPE_INT:
case GLSL_TYPE_FLOAT:
case GLSL_TYPE_DOUBLE:
case GLSL_TYPE_UINT64:
case GLSL_TYPE_INT64:
case GLSL_TYPE_BOOL: {
unsigned int size = src->type->components();
assert (size <= this->type->components() - offset);
for (unsigned int i=0; i<size; i++) {
switch (this->type->base_type) {
case GLSL_TYPE_UINT:
value.u[i+offset] = src->get_uint_component(i);
break;
case GLSL_TYPE_INT:
value.i[i+offset] = src->get_int_component(i);
break;
case GLSL_TYPE_FLOAT:
value.f[i+offset] = src->get_float_component(i);
break;
case GLSL_TYPE_BOOL:
value.b[i+offset] = src->get_bool_component(i);
break;
case GLSL_TYPE_DOUBLE:
value.d[i+offset] = src->get_double_component(i);
break;
case GLSL_TYPE_UINT64:
value.u64[i+offset] = src->get_uint64_component(i);
break;
case GLSL_TYPE_INT64:
value.i64[i+offset] = src->get_int64_component(i);
break;
default: // Shut up the compiler
break;
}
}
break;
}
case GLSL_TYPE_STRUCT: {
assert (src->type == this->type);
this->components.make_empty();
foreach_in_list(ir_constant, orig, &src->components) {
this->components.push_tail(orig->clone(this, NULL));
}
break;
}
case GLSL_TYPE_ARRAY: {
assert (src->type == this->type);
for (unsigned i = 0; i < this->type->length; i++) {
this->array_elements[i] = src->array_elements[i]->clone(this, NULL);
}
break;
}
default:
assert(!"Should not get here.");
break;
}
}
void
ir_constant::copy_masked_offset(ir_constant *src, int offset, unsigned int mask)
{
assert (!type->is_array() && !type->is_record());
if (!type->is_vector() && !type->is_matrix()) {
offset = 0;
mask = 1;
}
int id = 0;
for (int i=0; i<4; i++) {
if (mask & (1 << i)) {
switch (this->type->base_type) {
case GLSL_TYPE_UINT:
value.u[i+offset] = src->get_uint_component(id++);
break;
case GLSL_TYPE_INT:
value.i[i+offset] = src->get_int_component(id++);
break;
case GLSL_TYPE_FLOAT:
value.f[i+offset] = src->get_float_component(id++);
break;
case GLSL_TYPE_BOOL:
value.b[i+offset] = src->get_bool_component(id++);
break;
case GLSL_TYPE_DOUBLE:
value.d[i+offset] = src->get_double_component(id++);
break;
case GLSL_TYPE_UINT64:
value.u64[i+offset] = src->get_uint64_component(id++);
break;
case GLSL_TYPE_INT64:
value.i64[i+offset] = src->get_int64_component(id++);
break;
default:
assert(!"Should not get here.");
return;
}
}
}
}
bool
ir_constant::has_value(const ir_constant *c) const
{
if (this->type != c->type)
return false;
if (this->type->is_array()) {
for (unsigned i = 0; i < this->type->length; i++) {
if (!this->array_elements[i]->has_value(c->array_elements[i]))
return false;
}
return true;
}
if (this->type->is_record()) {
const exec_node *a_node = this->components.get_head_raw();
const exec_node *b_node = c->components.get_head_raw();
while (!a_node->is_tail_sentinel()) {
assert(!b_node->is_tail_sentinel());
const ir_constant *const a_field = (ir_constant *) a_node;
const ir_constant *const b_field = (ir_constant *) b_node;
if (!a_field->has_value(b_field))
return false;
a_node = a_node->next;
b_node = b_node->next;
}
return true;
}
for (unsigned i = 0; i < this->type->components(); i++) {
switch (this->type->base_type) {
case GLSL_TYPE_UINT:
if (this->value.u[i] != c->value.u[i])
return false;
break;
case GLSL_TYPE_INT:
if (this->value.i[i] != c->value.i[i])
return false;
break;
case GLSL_TYPE_FLOAT:
if (this->value.f[i] != c->value.f[i])
return false;
break;
case GLSL_TYPE_BOOL:
if (this->value.b[i] != c->value.b[i])
return false;
break;
case GLSL_TYPE_DOUBLE:
if (this->value.d[i] != c->value.d[i])
return false;
break;
case GLSL_TYPE_UINT64:
if (this->value.u64[i] != c->value.u64[i])
return false;
break;
case GLSL_TYPE_INT64:
if (this->value.i64[i] != c->value.i64[i])
return false;
break;
default:
assert(!"Should not get here.");
return false;
}
}
return true;
}
bool
ir_constant::is_value(float f, int i) const
{
if (!this->type->is_scalar() && !this->type->is_vector())
return false;
/* Only accept boolean values for 0/1. */
if (int(bool(i)) != i && this->type->is_boolean())
return false;
for (unsigned c = 0; c < this->type->vector_elements; c++) {
switch (this->type->base_type) {
case GLSL_TYPE_FLOAT:
if (this->value.f[c] != f)
return false;
break;
case GLSL_TYPE_INT:
if (this->value.i[c] != i)
return false;
break;
case GLSL_TYPE_UINT:
if (this->value.u[c] != unsigned(i))
return false;
break;
case GLSL_TYPE_BOOL:
if (this->value.b[c] != bool(i))
return false;
break;
case GLSL_TYPE_DOUBLE:
if (this->value.d[c] != double(f))
return false;
break;
case GLSL_TYPE_UINT64:
if (this->value.u64[c] != uint64_t(i))
return false;
break;
case GLSL_TYPE_INT64:
if (this->value.i64[c] != i)
return false;
break;
default:
/* The only other base types are structures, arrays, and samplers.
* Samplers cannot be constants, and the others should have been
* filtered out above.
*/
assert(!"Should not get here.");
return false;
}
}
return true;
}
bool
ir_constant::is_zero() const
{
return is_value(0.0, 0);
}
bool
ir_constant::is_one() const
{
return is_value(1.0, 1);
}
bool
ir_constant::is_negative_one() const
{
return is_value(-1.0, -1);
}
bool
ir_constant::is_uint16_constant() const
{
if (!type->is_integer())
return false;
return value.u[0] < (1 << 16);
}
ir_loop::ir_loop()
: ir_instruction(ir_type_loop)
{
}
ir_dereference_variable::ir_dereference_variable(ir_variable *var)
: ir_dereference(ir_type_dereference_variable)
{
assert(var != NULL);
this->var = var;
this->type = var->type;
}
ir_dereference_array::ir_dereference_array(ir_rvalue *value,
ir_rvalue *array_index)
: ir_dereference(ir_type_dereference_array)
{
this->array_index = array_index;
this->set_array(value);
}
ir_dereference_array::ir_dereference_array(ir_variable *var,
ir_rvalue *array_index)
: ir_dereference(ir_type_dereference_array)
{
void *ctx = ralloc_parent(var);
this->array_index = array_index;
this->set_array(new(ctx) ir_dereference_variable(var));
}
void
ir_dereference_array::set_array(ir_rvalue *value)
{
assert(value != NULL);
this->array = value;
const glsl_type *const vt = this->array->type;
if (vt->is_array()) {
type = vt->fields.array;
} else if (vt->is_matrix()) {
type = vt->column_type();
} else if (vt->is_vector()) {
type = vt->get_base_type();
}
}
ir_dereference_record::ir_dereference_record(ir_rvalue *value,
const char *field)
: ir_dereference(ir_type_dereference_record)
{
assert(value != NULL);
this->record = value;
this->field = ralloc_strdup(this, field);
this->type = this->record->type->field_type(field);
}
ir_dereference_record::ir_dereference_record(ir_variable *var,
const char *field)
: ir_dereference(ir_type_dereference_record)
{
void *ctx = ralloc_parent(var);
this->record = new(ctx) ir_dereference_variable(var);
this->field = ralloc_strdup(this, field);
this->type = this->record->type->field_type(field);
}
bool
ir_dereference::is_lvalue(const struct _mesa_glsl_parse_state *state) const
{
ir_variable *var = this->variable_referenced();
/* Every l-value derference chain eventually ends in a variable.
*/
if ((var == NULL) || var->data.read_only)
return false;
/* From section 4.1.7 of the ARB_bindless_texture spec:
*
* "Samplers can be used as l-values, so can be assigned into and used as
* "out" and "inout" function parameters."
*
* From section 4.1.X of the ARB_bindless_texture spec:
*
* "Images can be used as l-values, so can be assigned into and used as
* "out" and "inout" function parameters."
*/
if ((!state || state->has_bindless()) &&
(this->type->contains_sampler() || this->type->contains_image()))
return true;
/* From section 4.1.7 of the GLSL 4.40 spec:
*
* "Opaque variables cannot be treated as l-values; hence cannot
* be used as out or inout function parameters, nor can they be
* assigned into."
*/
if (this->type->contains_opaque())
return false;
return true;
}
static const char * const tex_opcode_strs[] = { "tex", "txb", "txl", "txd", "txf", "txf_ms", "txs", "lod", "tg4", "query_levels", "texture_samples", "samples_identical" };
const char *ir_texture::opcode_string()
{
assert((unsigned int) op < ARRAY_SIZE(tex_opcode_strs));
return tex_opcode_strs[op];
}
ir_texture_opcode
ir_texture::get_opcode(const char *str)
{
const int count = sizeof(tex_opcode_strs) / sizeof(tex_opcode_strs[0]);
for (int op = 0; op < count; op++) {
if (strcmp(str, tex_opcode_strs[op]) == 0)
return (ir_texture_opcode) op;
}
return (ir_texture_opcode) -1;
}
void
ir_texture::set_sampler(ir_dereference *sampler, const glsl_type *type)
{
assert(sampler != NULL);
assert(type != NULL);
this->sampler = sampler;
this->type = type;
if (this->op == ir_txs || this->op == ir_query_levels ||
this->op == ir_texture_samples) {
assert(type->base_type == GLSL_TYPE_INT);
} else if (this->op == ir_lod) {
assert(type->vector_elements == 2);
assert(type->is_float());
} else if (this->op == ir_samples_identical) {
assert(type == glsl_type::bool_type);
assert(sampler->type->is_sampler());
assert(sampler->type->sampler_dimensionality == GLSL_SAMPLER_DIM_MS);
} else {
assert(sampler->type->sampled_type == (int) type->base_type);
if (sampler->type->sampler_shadow)
assert(type->vector_elements == 4 || type->vector_elements == 1);
else
assert(type->vector_elements == 4);
}
}
void
ir_swizzle::init_mask(const unsigned *comp, unsigned count)
{
assert((count >= 1) && (count <= 4));
memset(&this->mask, 0, sizeof(this->mask));
this->mask.num_components = count;
unsigned dup_mask = 0;
switch (count) {
case 4:
assert(comp[3] <= 3);
dup_mask |= (1U << comp[3])
& ((1U << comp[0]) | (1U << comp[1]) | (1U << comp[2]));
this->mask.w = comp[3];
case 3:
assert(comp[2] <= 3);
dup_mask |= (1U << comp[2])
& ((1U << comp[0]) | (1U << comp[1]));
this->mask.z = comp[2];
case 2:
assert(comp[1] <= 3);
dup_mask |= (1U << comp[1])
& ((1U << comp[0]));
this->mask.y = comp[1];
case 1:
assert(comp[0] <= 3);
this->mask.x = comp[0];
}
this->mask.has_duplicates = dup_mask != 0;
/* Based on the number of elements in the swizzle and the base type
* (i.e., float, int, unsigned, or bool) of the vector being swizzled,
* generate the type of the resulting value.
*/
type = glsl_type::get_instance(val->type->base_type, mask.num_components, 1);
}
ir_swizzle::ir_swizzle(ir_rvalue *val, unsigned x, unsigned y, unsigned z,
unsigned w, unsigned count)
: ir_rvalue(ir_type_swizzle), val(val)
{
const unsigned components[4] = { x, y, z, w };
this->init_mask(components, count);
}
ir_swizzle::ir_swizzle(ir_rvalue *val, const unsigned *comp,
unsigned count)
: ir_rvalue(ir_type_swizzle), val(val)
{
this->init_mask(comp, count);
}
ir_swizzle::ir_swizzle(ir_rvalue *val, ir_swizzle_mask mask)
: ir_rvalue(ir_type_swizzle), mask(mask), val(val)
{
this->type = glsl_type::get_instance(val->type->base_type,
mask.num_components, 1);
}
#define X 1
#define R 5
#define S 9
#define I 13
ir_swizzle *
ir_swizzle::create(ir_rvalue *val, const char *str, unsigned vector_length)
{
void *ctx = ralloc_parent(val);
/* For each possible swizzle character, this table encodes the value in
* \c idx_map that represents the 0th element of the vector. For invalid
* swizzle characters (e.g., 'k'), a special value is used that will allow
* detection of errors.
*/
static const unsigned char base_idx[26] = {
/* a b c d e f g h i j k l m */
R, R, I, I, I, I, R, I, I, I, I, I, I,
/* n o p q r s t u v w x y z */
I, I, S, S, R, S, S, I, I, X, X, X, X
};
/* Each valid swizzle character has an entry in the previous table. This
* table encodes the base index encoded in the previous table plus the actual
* index of the swizzle character. When processing swizzles, the first
* character in the string is indexed in the previous table. Each character
* in the string is indexed in this table, and the value found there has the
* value form the first table subtracted. The result must be on the range
* [0,3].
*
* For example, the string "wzyx" will get X from the first table. Each of
* the charcaters will get X+3, X+2, X+1, and X+0 from this table. After
* subtraction, the swizzle values are { 3, 2, 1, 0 }.
*
* The string "wzrg" will get X from the first table. Each of the characters
* will get X+3, X+2, R+0, and R+1 from this table. After subtraction, the
* swizzle values are { 3, 2, 4, 5 }. Since 4 and 5 are outside the range
* [0,3], the error is detected.
*/
static const unsigned char idx_map[26] = {
/* a b c d e f g h i j k l m */
R+3, R+2, 0, 0, 0, 0, R+1, 0, 0, 0, 0, 0, 0,
/* n o p q r s t u v w x y z */
0, 0, S+2, S+3, R+0, S+0, S+1, 0, 0, X+3, X+0, X+1, X+2
};
int swiz_idx[4] = { 0, 0, 0, 0 };
unsigned i;
/* Validate the first character in the swizzle string and look up the base
* index value as described above.
*/
if ((str[0] < 'a') || (str[0] > 'z'))
return NULL;
const unsigned base = base_idx[str[0] - 'a'];
for (i = 0; (i < 4) && (str[i] != '\0'); i++) {
/* Validate the next character, and, as described above, convert it to a
* swizzle index.
*/
if ((str[i] < 'a') || (str[i] > 'z'))
return NULL;
swiz_idx[i] = idx_map[str[i] - 'a'] - base;
if ((swiz_idx[i] < 0) || (swiz_idx[i] >= (int) vector_length))
return NULL;
}
if (str[i] != '\0')
return NULL;
return new(ctx) ir_swizzle(val, swiz_idx[0], swiz_idx[1], swiz_idx[2],
swiz_idx[3], i);
}
#undef X
#undef R
#undef S
#undef I
ir_variable *
ir_swizzle::variable_referenced() const
{
return this->val->variable_referenced();
}
bool ir_variable::temporaries_allocate_names = false;
const char ir_variable::tmp_name[] = "compiler_temp";
ir_variable::ir_variable(const struct glsl_type *type, const char *name,
ir_variable_mode mode)
: ir_instruction(ir_type_variable)
{
this->type = type;
if (mode == ir_var_temporary && !ir_variable::temporaries_allocate_names)
name = NULL;
/* The ir_variable clone method may call this constructor with name set to
* tmp_name.
*/
assert(name != NULL
|| mode == ir_var_temporary
|| mode == ir_var_function_in
|| mode == ir_var_function_out
|| mode == ir_var_function_inout);
assert(name != ir_variable::tmp_name
|| mode == ir_var_temporary);
if (mode == ir_var_temporary
&& (name == NULL || name == ir_variable::tmp_name)) {
this->name = ir_variable::tmp_name;
} else if (name == NULL ||
strlen(name) < ARRAY_SIZE(this->name_storage)) {
strcpy(this->name_storage, name ? name : "");
this->name = this->name_storage;
} else {
this->name = ralloc_strdup(this, name);
}
this->u.max_ifc_array_access = NULL;
this->data.explicit_location = false;
this->data.has_initializer = false;
this->data.location = -1;
this->data.location_frac = 0;
this->data.binding = 0;
this->data.warn_extension_index = 0;
this->constant_value = NULL;
this->constant_initializer = NULL;
this->data.origin_upper_left = false;
this->data.pixel_center_integer = false;
this->data.depth_layout = ir_depth_layout_none;
this->data.used = false;
this->data.always_active_io = false;
this->data.read_only = false;
this->data.centroid = false;
this->data.sample = false;
this->data.patch = false;
this->data.invariant = false;
this->data.how_declared = ir_var_declared_normally;
this->data.mode = mode;
this->data.interpolation = INTERP_MODE_NONE;
this->data.max_array_access = -1;
this->data.offset = 0;
this->data.precision = GLSL_PRECISION_NONE;
this->data.memory_read_only = false;
this->data.memory_write_only = false;
this->data.memory_coherent = false;
this->data.memory_volatile = false;
this->data.memory_restrict = false;
this->data.from_ssbo_unsized_array = false;
this->data.fb_fetch_output = false;
this->data.bindless = false;
this->data.bound = false;
if (type != NULL) {
if (type->is_interface())
this->init_interface_type(type);
else if (type->without_array()->is_interface())
this->init_interface_type(type->without_array());
}
}
const char *
interpolation_string(unsigned interpolation)
{
switch (interpolation) {
case INTERP_MODE_NONE: return "no";
case INTERP_MODE_SMOOTH: return "smooth";
case INTERP_MODE_FLAT: return "flat";
case INTERP_MODE_NOPERSPECTIVE: return "noperspective";
}
assert(!"Should not get here.");
return "";
}
const char *const ir_variable::warn_extension_table[] = {
"",
"GL_ARB_shader_stencil_export",
"GL_AMD_shader_stencil_export",
};
void
ir_variable::enable_extension_warning(const char *extension)
{
for (unsigned i = 0; i < ARRAY_SIZE(warn_extension_table); i++) {
if (strcmp(warn_extension_table[i], extension) == 0) {
this->data.warn_extension_index = i;
return;
}
}
assert(!"Should not get here.");
this->data.warn_extension_index = 0;
}
const char *
ir_variable::get_extension_warning() const
{
return this->data.warn_extension_index == 0
? NULL : warn_extension_table[this->data.warn_extension_index];
}
ir_function_signature::ir_function_signature(const glsl_type *return_type,
builtin_available_predicate b)
: ir_instruction(ir_type_function_signature),
return_type(return_type), is_defined(false),
intrinsic_id(ir_intrinsic_invalid), builtin_avail(b), _function(NULL)
{
this->origin = NULL;
}
bool
ir_function_signature::is_builtin() const
{
return builtin_avail != NULL;
}
bool
ir_function_signature::is_builtin_available(const _mesa_glsl_parse_state *state) const
{
/* We can't call the predicate without a state pointer, so just say that
* the signature is available. At compile time, we need the filtering,
* but also receive a valid state pointer. At link time, we're resolving
* imported built-in prototypes to their definitions, which will always
* be an exact match. So we can skip the filtering.
*/
if (state == NULL)
return true;
assert(builtin_avail != NULL);
return builtin_avail(state);
}
static bool
modes_match(unsigned a, unsigned b)
{
if (a == b)
return true;
/* Accept "in" vs. "const in" */
if ((a == ir_var_const_in && b == ir_var_function_in) ||
(b == ir_var_const_in && a == ir_var_function_in))
return true;
return false;
}
const char *
ir_function_signature::qualifiers_match(exec_list *params)
{
/* check that the qualifiers match. */
foreach_two_lists(a_node, &this->parameters, b_node, params) {
ir_variable *a = (ir_variable *) a_node;
ir_variable *b = (ir_variable *) b_node;
if (a->data.read_only != b->data.read_only ||
!modes_match(a->data.mode, b->data.mode) ||
a->data.interpolation != b->data.interpolation ||
a->data.centroid != b->data.centroid ||
a->data.sample != b->data.sample ||
a->data.patch != b->data.patch ||
a->data.memory_read_only != b->data.memory_read_only ||
a->data.memory_write_only != b->data.memory_write_only ||
a->data.memory_coherent != b->data.memory_coherent ||
a->data.memory_volatile != b->data.memory_volatile ||
a->data.memory_restrict != b->data.memory_restrict) {
/* parameter a's qualifiers don't match */
return a->name;
}
}
return NULL;
}
void
ir_function_signature::replace_parameters(exec_list *new_params)
{
/* Destroy all of the previous parameter information. If the previous
* parameter information comes from the function prototype, it may either
* specify incorrect parameter names or not have names at all.
*/
new_params->move_nodes_to(&parameters);
}
ir_function::ir_function(const char *name)
: ir_instruction(ir_type_function)
{
this->subroutine_index = -1;
this->name = ralloc_strdup(this, name);
}
bool
ir_function::has_user_signature()
{
foreach_in_list(ir_function_signature, sig, &this->signatures) {
if (!sig->is_builtin())
return true;
}
return false;
}
ir_rvalue *
ir_rvalue::error_value(void *mem_ctx)
{
ir_rvalue *v = new(mem_ctx) ir_rvalue(ir_type_unset);
v->type = glsl_type::error_type;
return v;
}
void
visit_exec_list(exec_list *list, ir_visitor *visitor)
{
foreach_in_list_safe(ir_instruction, node, list) {
node->accept(visitor);
}
}
static void
steal_memory(ir_instruction *ir, void *new_ctx)
{
ir_variable *var = ir->as_variable();
ir_function *fn = ir->as_function();
ir_constant *constant = ir->as_constant();
if (var != NULL && var->constant_value != NULL)
steal_memory(var->constant_value, ir);
if (var != NULL && var->constant_initializer != NULL)
steal_memory(var->constant_initializer, ir);
if (fn != NULL && fn->subroutine_types)
ralloc_steal(new_ctx, fn->subroutine_types);
/* The components of aggregate constants are not visited by the normal
* visitor, so steal their values by hand.
*/
if (constant != NULL) {
if (constant->type->is_record()) {
foreach_in_list(ir_constant, field, &constant->components) {
steal_memory(field, ir);
}
} else if (constant->type->is_array()) {
for (unsigned int i = 0; i < constant->type->length; i++) {
steal_memory(constant->array_elements[i], ir);
}
}
}
ralloc_steal(new_ctx, ir);
}
void
reparent_ir(exec_list *list, void *mem_ctx)
{
foreach_in_list(ir_instruction, node, list) {
visit_tree(node, steal_memory, mem_ctx);
}
}
static ir_rvalue *
try_min_one(ir_rvalue *ir)
{
ir_expression *expr = ir->as_expression();
if (!expr || expr->operation != ir_binop_min)
return NULL;
if (expr->operands[0]->is_one())
return expr->operands[1];
if (expr->operands[1]->is_one())
return expr->operands[0];
return NULL;
}
static ir_rvalue *
try_max_zero(ir_rvalue *ir)
{
ir_expression *expr = ir->as_expression();
if (!expr || expr->operation != ir_binop_max)
return NULL;
if (expr->operands[0]->is_zero())
return expr->operands[1];
if (expr->operands[1]->is_zero())
return expr->operands[0];
return NULL;
}
ir_rvalue *
ir_rvalue::as_rvalue_to_saturate()
{
ir_expression *expr = this->as_expression();
if (!expr)
return NULL;
ir_rvalue *max_zero = try_max_zero(expr);
if (max_zero) {
return try_min_one(max_zero);
} else {
ir_rvalue *min_one = try_min_one(expr);
if (min_one) {
return try_max_zero(min_one);
}
}
return NULL;
}
unsigned
vertices_per_prim(GLenum prim)
{
switch (prim) {
case GL_POINTS:
return 1;
case GL_LINES:
return 2;
case GL_TRIANGLES:
return 3;
case GL_LINES_ADJACENCY:
return 4;
case GL_TRIANGLES_ADJACENCY:
return 6;
default:
assert(!"Bad primitive");
return 3;
}
}
/**
* Generate a string describing the mode of a variable
*/
const char *
mode_string(const ir_variable *var)
{
switch (var->data.mode) {
case ir_var_auto:
return (var->data.read_only) ? "global constant" : "global variable";
case ir_var_uniform:
return "uniform";
case ir_var_shader_storage:
return "buffer";
case ir_var_shader_in:
return "shader input";
case ir_var_shader_out:
return "shader output";
case ir_var_function_in:
case ir_var_const_in:
return "function input";
case ir_var_function_out:
return "function output";
case ir_var_function_inout:
return "function inout";
case ir_var_system_value:
return "shader input";
case ir_var_temporary:
return "compiler temporary";
case ir_var_mode_count:
break;
}
assert(!"Should not get here.");
return "invalid variable";
}