2010-03-15 20:04:13 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2010-03-19 18:57:24 +00:00
|
|
|
#include "glsl_symbol_table.h"
|
2010-03-15 20:04:13 +00:00
|
|
|
#include "ast.h"
|
2016-01-18 09:35:29 +00:00
|
|
|
#include "compiler/glsl_types.h"
|
2010-03-15 20:04:13 +00:00
|
|
|
#include "ir.h"
|
2022-01-07 04:22:18 +00:00
|
|
|
#include "main/shader_types.h"
|
|
|
|
#include "main/consts_exts.h"
|
2015-06-01 01:55:47 +01:00
|
|
|
#include "main/shaderobj.h"
|
2016-10-14 19:02:18 +01:00
|
|
|
#include "builtin_functions.h"
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2010-07-14 21:22:07 +01:00
|
|
|
static ir_rvalue *
|
|
|
|
convert_component(ir_rvalue *src, const glsl_type *desired_type);
|
|
|
|
|
2010-04-01 00:28:51 +01:00
|
|
|
static unsigned
|
|
|
|
process_parameters(exec_list *instructions, exec_list *actual_parameters,
|
2016-08-02 21:19:28 +01:00
|
|
|
exec_list *parameters,
|
|
|
|
struct _mesa_glsl_parse_state *state)
|
2010-03-15 20:26:02 +00:00
|
|
|
{
|
2017-08-10 11:42:29 +01:00
|
|
|
void *mem_ctx = state;
|
2010-04-01 00:28:51 +01:00
|
|
|
unsigned count = 0;
|
|
|
|
|
2014-06-25 06:02:24 +01:00
|
|
|
foreach_list_typed(ast_node, ast, link, parameters) {
|
2016-04-19 10:17:27 +01:00
|
|
|
/* We need to process the parameters first in order to know if we can
|
|
|
|
* raise or not a unitialized warning. Calling set_is_lhs silence the
|
|
|
|
* warning for now. Raising the warning or not will be checked at
|
|
|
|
* verify_parameter_modes.
|
|
|
|
*/
|
|
|
|
ast->set_is_lhs(true);
|
2010-06-10 01:31:02 +01:00
|
|
|
ir_rvalue *result = ast->hir(instructions, state);
|
|
|
|
|
2020-05-26 03:14:13 +01:00
|
|
|
/* Error happened processing function parameter */
|
|
|
|
if (!result) {
|
|
|
|
actual_parameters->push_tail(ir_rvalue::error_value(mem_ctx));
|
|
|
|
count++;
|
|
|
|
continue;
|
|
|
|
}
|
2020-03-27 07:04:31 +00:00
|
|
|
|
2017-08-10 11:42:29 +01:00
|
|
|
ir_constant *const constant =
|
|
|
|
result->constant_expression_value(mem_ctx);
|
|
|
|
|
2010-06-10 01:31:02 +01:00
|
|
|
if (constant != NULL)
|
2016-08-02 21:19:28 +01:00
|
|
|
result = constant;
|
2010-03-15 20:26:02 +00:00
|
|
|
|
2010-05-10 18:47:14 +01:00
|
|
|
actual_parameters->push_tail(result);
|
|
|
|
count++;
|
2010-03-15 20:26:02 +00:00
|
|
|
}
|
|
|
|
|
2010-04-01 00:28:51 +01:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-06 01:21:39 +01:00
|
|
|
/**
|
|
|
|
* Generate a source prototype for a function signature
|
|
|
|
*
|
|
|
|
* \param return_type Return type of the function. May be \c NULL.
|
|
|
|
* \param name Name of the function.
|
2011-07-08 18:34:38 +01:00
|
|
|
* \param parameters List of \c ir_instruction nodes representing the
|
|
|
|
* parameter list for the function. This may be either a
|
|
|
|
* formal (\c ir_variable) or actual (\c ir_rvalue)
|
|
|
|
* parameter list. Only the type is used.
|
2010-08-06 01:21:39 +01:00
|
|
|
*
|
|
|
|
* \return
|
2011-01-21 22:32:31 +00:00
|
|
|
* A ralloced string representing the prototype of the function.
|
2010-08-06 01:21:39 +01:00
|
|
|
*/
|
|
|
|
char *
|
|
|
|
prototype_string(const glsl_type *return_type, const char *name,
|
2016-08-02 21:19:28 +01:00
|
|
|
exec_list *parameters)
|
2010-08-06 01:21:39 +01:00
|
|
|
{
|
|
|
|
char *str = NULL;
|
|
|
|
|
|
|
|
if (return_type != NULL)
|
2011-02-02 07:31:35 +00:00
|
|
|
str = ralloc_asprintf(NULL, "%s ", return_type->name);
|
2010-08-06 01:21:39 +01:00
|
|
|
|
2011-01-21 22:32:31 +00:00
|
|
|
ralloc_asprintf_append(&str, "%s(", name);
|
2010-08-06 01:21:39 +01:00
|
|
|
|
|
|
|
const char *comma = "";
|
2014-06-25 05:34:05 +01:00
|
|
|
foreach_in_list(const ir_variable, param, parameters) {
|
2011-01-21 22:32:31 +00:00
|
|
|
ralloc_asprintf_append(&str, "%s%s", comma, param->type->name);
|
2010-08-06 01:21:39 +01:00
|
|
|
comma = ", ";
|
|
|
|
}
|
|
|
|
|
2011-01-21 22:32:31 +00:00
|
|
|
ralloc_strcat(&str, ")");
|
2010-08-06 01:21:39 +01:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2013-11-22 23:11:50 +00:00
|
|
|
static bool
|
|
|
|
verify_image_parameter(YYLTYPE *loc, _mesa_glsl_parse_state *state,
|
|
|
|
const ir_variable *formal, const ir_variable *actual)
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* From the ARB_shader_image_load_store specification:
|
|
|
|
*
|
|
|
|
* "The values of image variables qualified with coherent,
|
|
|
|
* volatile, restrict, readonly, or writeonly may not be passed
|
|
|
|
* to functions whose formal parameters lack such
|
|
|
|
* qualifiers. [...] It is legal to have additional qualifiers
|
|
|
|
* on a formal parameter, but not to have fewer."
|
|
|
|
*/
|
2017-05-03 10:16:57 +01:00
|
|
|
if (actual->data.memory_coherent && !formal->data.memory_coherent) {
|
2013-11-22 23:11:50 +00:00
|
|
|
_mesa_glsl_error(loc, state,
|
|
|
|
"function call parameter `%s' drops "
|
|
|
|
"`coherent' qualifier", formal->name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-05-03 10:16:57 +01:00
|
|
|
if (actual->data.memory_volatile && !formal->data.memory_volatile) {
|
2013-11-22 23:11:50 +00:00
|
|
|
_mesa_glsl_error(loc, state,
|
|
|
|
"function call parameter `%s' drops "
|
|
|
|
"`volatile' qualifier", formal->name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-05-03 10:16:57 +01:00
|
|
|
if (actual->data.memory_restrict && !formal->data.memory_restrict) {
|
2013-11-22 23:11:50 +00:00
|
|
|
_mesa_glsl_error(loc, state,
|
|
|
|
"function call parameter `%s' drops "
|
|
|
|
"`restrict' qualifier", formal->name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-05-03 10:16:57 +01:00
|
|
|
if (actual->data.memory_read_only && !formal->data.memory_read_only) {
|
2013-11-22 23:11:50 +00:00
|
|
|
_mesa_glsl_error(loc, state,
|
|
|
|
"function call parameter `%s' drops "
|
|
|
|
"`readonly' qualifier", formal->name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-05-03 10:16:57 +01:00
|
|
|
if (actual->data.memory_write_only && !formal->data.memory_write_only) {
|
2013-11-22 23:11:50 +00:00
|
|
|
_mesa_glsl_error(loc, state,
|
|
|
|
"function call parameter `%s' drops "
|
|
|
|
"`writeonly' qualifier", formal->name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-04-24 11:34:00 +01:00
|
|
|
static bool
|
2015-10-09 23:32:33 +01:00
|
|
|
verify_first_atomic_parameter(YYLTYPE *loc, _mesa_glsl_parse_state *state,
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_variable *var)
|
2015-04-24 11:34:00 +01:00
|
|
|
{
|
2015-10-09 23:32:33 +01:00
|
|
|
if (!var ||
|
|
|
|
(!var->is_in_shader_storage_block() &&
|
|
|
|
var->data.mode != ir_var_shader_shared)) {
|
2015-04-24 11:34:00 +01:00
|
|
|
_mesa_glsl_error(loc, state, "First argument to atomic function "
|
2015-10-09 23:32:33 +01:00
|
|
|
"must be a buffer or shared variable");
|
2015-04-24 11:34:00 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2015-10-09 23:32:33 +01:00
|
|
|
is_atomic_function(const char *func_name)
|
2015-04-24 11:34:00 +01:00
|
|
|
{
|
|
|
|
return !strcmp(func_name, "atomicAdd") ||
|
|
|
|
!strcmp(func_name, "atomicMin") ||
|
|
|
|
!strcmp(func_name, "atomicMax") ||
|
|
|
|
!strcmp(func_name, "atomicAnd") ||
|
|
|
|
!strcmp(func_name, "atomicOr") ||
|
|
|
|
!strcmp(func_name, "atomicXor") ||
|
|
|
|
!strcmp(func_name, "atomicExchange") ||
|
|
|
|
!strcmp(func_name, "atomicCompSwap");
|
|
|
|
}
|
|
|
|
|
2020-06-19 17:37:00 +01:00
|
|
|
static bool
|
|
|
|
verify_atomic_image_parameter_qualifier(YYLTYPE *loc, _mesa_glsl_parse_state *state,
|
|
|
|
ir_variable *var)
|
|
|
|
{
|
|
|
|
if (!var ||
|
|
|
|
(var->data.image_format != PIPE_FORMAT_R32_UINT &&
|
|
|
|
var->data.image_format != PIPE_FORMAT_R32_SINT &&
|
|
|
|
var->data.image_format != PIPE_FORMAT_R32_FLOAT)) {
|
|
|
|
_mesa_glsl_error(loc, state, "Image atomic functions should use r32i/r32ui "
|
|
|
|
"format qualifier");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
is_atomic_image_function(const char *func_name)
|
|
|
|
{
|
|
|
|
return !strcmp(func_name, "imageAtomicAdd") ||
|
|
|
|
!strcmp(func_name, "imageAtomicMin") ||
|
|
|
|
!strcmp(func_name, "imageAtomicMax") ||
|
|
|
|
!strcmp(func_name, "imageAtomicAnd") ||
|
|
|
|
!strcmp(func_name, "imageAtomicOr") ||
|
|
|
|
!strcmp(func_name, "imageAtomicXor") ||
|
|
|
|
!strcmp(func_name, "imageAtomicExchange") ||
|
|
|
|
!strcmp(func_name, "imageAtomicCompSwap") ||
|
|
|
|
!strcmp(func_name, "imageAtomicIncWrap") ||
|
|
|
|
!strcmp(func_name, "imageAtomicDecWrap");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-23 18:58:23 +00:00
|
|
|
/**
|
2012-03-29 03:24:45 +01:00
|
|
|
* Verify that 'out' and 'inout' actual parameters are lvalues. Also, verify
|
|
|
|
* that 'const_in' formal parameters (an extension in our IR) correspond to
|
|
|
|
* ir_constant actual parameters.
|
2011-12-23 18:58:23 +00:00
|
|
|
*/
|
2012-03-29 03:24:45 +01:00
|
|
|
static bool
|
|
|
|
verify_parameter_modes(_mesa_glsl_parse_state *state,
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_function_signature *sig,
|
|
|
|
exec_list &actual_ir_parameters,
|
|
|
|
exec_list &actual_ast_parameters)
|
2011-11-09 11:01:54 +00:00
|
|
|
{
|
2016-06-27 22:42:57 +01:00
|
|
|
exec_node *actual_ir_node = actual_ir_parameters.get_head_raw();
|
|
|
|
exec_node *actual_ast_node = actual_ast_parameters.get_head_raw();
|
2011-11-09 11:01:54 +00:00
|
|
|
|
2014-06-25 05:34:05 +01:00
|
|
|
foreach_in_list(const ir_variable, formal, &sig->parameters) {
|
2012-03-29 03:24:45 +01:00
|
|
|
/* The lists must be the same length. */
|
|
|
|
assert(!actual_ir_node->is_tail_sentinel());
|
|
|
|
assert(!actual_ast_node->is_tail_sentinel());
|
2011-12-23 18:58:23 +00:00
|
|
|
|
2012-03-29 03:24:45 +01:00
|
|
|
const ir_rvalue *const actual = (ir_rvalue *) actual_ir_node;
|
|
|
|
const ast_expression *const actual_ast =
|
2016-08-02 21:19:28 +01:00
|
|
|
exec_node_data(ast_expression, actual_ast_node, link);
|
2011-11-09 11:01:54 +00:00
|
|
|
|
2012-03-29 03:24:45 +01:00
|
|
|
YYLTYPE loc = actual_ast->get_location();
|
2011-11-09 11:01:54 +00:00
|
|
|
|
2012-03-29 03:24:45 +01:00
|
|
|
/* Verify that 'const_in' parameters are ir_constants. */
|
2013-12-12 11:51:01 +00:00
|
|
|
if (formal->data.mode == ir_var_const_in &&
|
2016-08-02 21:19:28 +01:00
|
|
|
actual->ir_type != ir_type_constant) {
|
|
|
|
_mesa_glsl_error(&loc, state,
|
|
|
|
"parameter `in %s' must be a constant expression",
|
|
|
|
formal->name);
|
|
|
|
return false;
|
2011-11-09 11:01:54 +00:00
|
|
|
}
|
|
|
|
|
2013-11-10 08:19:31 +00:00
|
|
|
/* Verify that shader_in parameters are shader inputs */
|
|
|
|
if (formal->data.must_be_shader_input) {
|
2016-05-13 05:26:44 +01:00
|
|
|
const ir_rvalue *val = actual;
|
|
|
|
|
2016-08-02 21:19:28 +01:00
|
|
|
/* GLSL 4.40 allows swizzles, while earlier GLSL versions do not. */
|
2016-05-13 05:26:44 +01:00
|
|
|
if (val->ir_type == ir_type_swizzle) {
|
|
|
|
if (!state->is_version(440, 0)) {
|
|
|
|
_mesa_glsl_error(&loc, state,
|
|
|
|
"parameter `%s` must not be swizzled",
|
|
|
|
formal->name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
val = ((ir_swizzle *)val)->val;
|
2013-11-10 08:19:31 +00:00
|
|
|
}
|
|
|
|
|
2017-06-14 11:43:10 +01:00
|
|
|
for (;;) {
|
|
|
|
if (val->ir_type == ir_type_dereference_array) {
|
|
|
|
val = ((ir_dereference_array *)val)->array;
|
|
|
|
} else if (val->ir_type == ir_type_dereference_record &&
|
|
|
|
!state->es_shader) {
|
|
|
|
val = ((ir_dereference_record *)val)->record;
|
|
|
|
} else
|
|
|
|
break;
|
2016-05-13 05:26:44 +01:00
|
|
|
}
|
|
|
|
|
2017-06-14 11:43:10 +01:00
|
|
|
ir_variable *var = NULL;
|
|
|
|
if (const ir_dereference_variable *deref_var = val->as_dereference_variable())
|
|
|
|
var = deref_var->variable_referenced();
|
|
|
|
|
|
|
|
if (!var || var->data.mode != ir_var_shader_in) {
|
2013-11-10 08:19:31 +00:00
|
|
|
_mesa_glsl_error(&loc, state,
|
2016-05-13 05:26:44 +01:00
|
|
|
"parameter `%s` must be a shader input",
|
2013-11-10 08:19:31 +00:00
|
|
|
formal->name);
|
|
|
|
return false;
|
|
|
|
}
|
2017-04-14 07:25:58 +01:00
|
|
|
|
2017-06-14 11:43:10 +01:00
|
|
|
var->data.must_be_shader_input = 1;
|
2013-11-10 08:19:31 +00:00
|
|
|
}
|
|
|
|
|
2012-03-29 03:24:45 +01:00
|
|
|
/* Verify that 'out' and 'inout' actual parameters are lvalues. */
|
2013-12-12 11:51:01 +00:00
|
|
|
if (formal->data.mode == ir_var_function_out
|
|
|
|
|| formal->data.mode == ir_var_function_inout) {
|
2016-08-02 21:19:28 +01:00
|
|
|
const char *mode = NULL;
|
|
|
|
switch (formal->data.mode) {
|
|
|
|
case ir_var_function_out: mode = "out"; break;
|
|
|
|
case ir_var_function_inout: mode = "inout"; break;
|
|
|
|
default: assert(false); break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This AST-based check catches errors like f(i++). The IR-based
|
|
|
|
* is_lvalue() is insufficient because the actual parameter at the
|
|
|
|
* IR-level is just a temporary value, which is an l-value.
|
|
|
|
*/
|
|
|
|
if (actual_ast->non_lvalue_description != NULL) {
|
|
|
|
_mesa_glsl_error(&loc, state,
|
|
|
|
"function parameter '%s %s' references a %s",
|
|
|
|
mode, formal->name,
|
|
|
|
actual_ast->non_lvalue_description);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ir_variable *var = actual->variable_referenced();
|
2016-04-19 10:17:27 +01:00
|
|
|
|
|
|
|
if (var && formal->data.mode == ir_var_function_inout) {
|
2016-08-02 21:19:28 +01:00
|
|
|
if ((var->data.mode == ir_var_auto ||
|
|
|
|
var->data.mode == ir_var_shader_out) &&
|
2016-04-19 10:17:27 +01:00
|
|
|
!var->data.assigned &&
|
|
|
|
!is_gl_identifier(var->name)) {
|
|
|
|
_mesa_glsl_warning(&loc, state, "`%s' used uninitialized",
|
|
|
|
var->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-02 21:19:28 +01:00
|
|
|
if (var)
|
|
|
|
var->data.assigned = true;
|
|
|
|
|
|
|
|
if (var && var->data.read_only) {
|
|
|
|
_mesa_glsl_error(&loc, state,
|
|
|
|
"function parameter '%s %s' references the "
|
|
|
|
"read-only variable '%s'",
|
|
|
|
mode, formal->name,
|
|
|
|
actual->variable_referenced()->name);
|
|
|
|
return false;
|
2017-04-19 17:08:28 +01:00
|
|
|
} else if (!actual->is_lvalue(state)) {
|
2015-11-04 22:58:54 +00:00
|
|
|
_mesa_glsl_error(&loc, state,
|
|
|
|
"function parameter '%s %s' is not an lvalue",
|
|
|
|
mode, formal->name);
|
|
|
|
return false;
|
2016-08-02 21:19:28 +01:00
|
|
|
}
|
2016-04-19 10:17:27 +01:00
|
|
|
} else {
|
|
|
|
assert(formal->data.mode == ir_var_function_in ||
|
|
|
|
formal->data.mode == ir_var_const_in);
|
|
|
|
ir_variable *var = actual->variable_referenced();
|
|
|
|
if (var) {
|
2016-08-02 21:19:28 +01:00
|
|
|
if ((var->data.mode == ir_var_auto ||
|
|
|
|
var->data.mode == ir_var_shader_out) &&
|
2016-04-19 10:17:27 +01:00
|
|
|
!var->data.assigned &&
|
|
|
|
!is_gl_identifier(var->name)) {
|
|
|
|
_mesa_glsl_warning(&loc, state, "`%s' used uninitialized",
|
|
|
|
var->name);
|
|
|
|
}
|
|
|
|
}
|
2011-11-09 11:01:54 +00:00
|
|
|
}
|
|
|
|
|
2013-11-22 23:11:50 +00:00
|
|
|
if (formal->type->is_image() &&
|
|
|
|
actual->variable_referenced()) {
|
|
|
|
if (!verify_image_parameter(&loc, state, formal,
|
|
|
|
actual->variable_referenced()))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-03-29 03:24:45 +01:00
|
|
|
actual_ir_node = actual_ir_node->next;
|
|
|
|
actual_ast_node = actual_ast_node->next;
|
|
|
|
}
|
2015-04-24 11:34:00 +01:00
|
|
|
|
|
|
|
/* The first parameter of atomic functions must be a buffer variable */
|
|
|
|
const char *func_name = sig->function_name();
|
2015-10-09 23:32:33 +01:00
|
|
|
bool is_atomic = is_atomic_function(func_name);
|
|
|
|
if (is_atomic) {
|
2016-06-27 22:42:57 +01:00
|
|
|
const ir_rvalue *const actual =
|
|
|
|
(ir_rvalue *) actual_ir_parameters.get_head_raw();
|
2015-04-24 11:34:00 +01:00
|
|
|
|
|
|
|
const ast_expression *const actual_ast =
|
2016-06-27 22:42:57 +01:00
|
|
|
exec_node_data(ast_expression,
|
|
|
|
actual_ast_parameters.get_head_raw(), link);
|
2015-04-24 11:34:00 +01:00
|
|
|
YYLTYPE loc = actual_ast->get_location();
|
|
|
|
|
2015-10-09 23:32:33 +01:00
|
|
|
if (!verify_first_atomic_parameter(&loc, state,
|
|
|
|
actual->variable_referenced())) {
|
2015-04-24 11:34:00 +01:00
|
|
|
return false;
|
|
|
|
}
|
2020-06-19 17:37:00 +01:00
|
|
|
} else if (is_atomic_image_function(func_name)) {
|
|
|
|
const ir_rvalue *const actual =
|
|
|
|
(ir_rvalue *) actual_ir_parameters.get_head_raw();
|
|
|
|
|
|
|
|
const ast_expression *const actual_ast =
|
|
|
|
exec_node_data(ast_expression,
|
|
|
|
actual_ast_parameters.get_head_raw(), link);
|
|
|
|
YYLTYPE loc = actual_ast->get_location();
|
|
|
|
|
|
|
|
if (!verify_atomic_image_parameter_qualifier(&loc, state,
|
|
|
|
actual->variable_referenced())) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-04-24 11:34:00 +01:00
|
|
|
}
|
|
|
|
|
2012-03-29 03:24:45 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-21 06:22:24 +01:00
|
|
|
struct copy_index_deref_data {
|
|
|
|
void *mem_ctx;
|
|
|
|
exec_list *before_instructions;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
copy_index_derefs_to_temps(ir_instruction *ir, void *data)
|
|
|
|
{
|
|
|
|
struct copy_index_deref_data *d = (struct copy_index_deref_data *)data;
|
|
|
|
|
|
|
|
if (ir->ir_type == ir_type_dereference_array) {
|
|
|
|
ir_dereference_array *a = (ir_dereference_array *) ir;
|
|
|
|
ir = a->array->as_dereference();
|
|
|
|
|
|
|
|
ir_rvalue *idx = a->array_index;
|
2019-01-10 16:16:59 +00:00
|
|
|
ir_variable *var = idx->variable_referenced();
|
2018-07-21 06:22:24 +01:00
|
|
|
|
2019-01-10 16:16:59 +00:00
|
|
|
/* If the index is read only it cannot change so there is no need
|
|
|
|
* to copy it.
|
|
|
|
*/
|
|
|
|
if (!var || var->data.read_only || var->data.memory_read_only)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ir_variable *tmp = new(d->mem_ctx) ir_variable(idx->type, "idx_tmp",
|
|
|
|
ir_var_temporary);
|
|
|
|
d->before_instructions->push_tail(tmp);
|
|
|
|
|
|
|
|
ir_dereference_variable *const deref_tmp_1 =
|
|
|
|
new(d->mem_ctx) ir_dereference_variable(tmp);
|
|
|
|
ir_assignment *const assignment =
|
|
|
|
new(d->mem_ctx) ir_assignment(deref_tmp_1,
|
|
|
|
idx->clone(d->mem_ctx, NULL));
|
|
|
|
d->before_instructions->push_tail(assignment);
|
|
|
|
|
|
|
|
/* Replace the array index with a dereference of the new temporary */
|
|
|
|
ir_dereference_variable *const deref_tmp_2 =
|
|
|
|
new(d->mem_ctx) ir_dereference_variable(tmp);
|
|
|
|
a->array_index = deref_tmp_2;
|
2018-07-21 06:22:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-23 00:45:52 +00:00
|
|
|
static void
|
|
|
|
fix_parameter(void *mem_ctx, ir_rvalue *actual, const glsl_type *formal_type,
|
|
|
|
exec_list *before_instructions, exec_list *after_instructions,
|
|
|
|
bool parameter_is_inout)
|
|
|
|
{
|
|
|
|
ir_expression *const expr = actual->as_expression();
|
|
|
|
|
|
|
|
/* If the types match exactly and the parameter is not a vector-extract,
|
|
|
|
* nothing needs to be done to fix the parameter.
|
|
|
|
*/
|
|
|
|
if (formal_type == actual->type
|
2018-04-09 05:40:40 +01:00
|
|
|
&& (expr == NULL || expr->operation != ir_binop_vector_extract)
|
|
|
|
&& actual->as_dereference_variable())
|
2013-03-23 00:45:52 +00:00
|
|
|
return;
|
|
|
|
|
2018-07-21 06:22:24 +01:00
|
|
|
/* An array index could also be an out variable so we need to make a copy
|
|
|
|
* of them before the function is called.
|
|
|
|
*/
|
|
|
|
if (!actual->as_dereference_variable()) {
|
|
|
|
struct copy_index_deref_data data;
|
|
|
|
data.mem_ctx = mem_ctx;
|
|
|
|
data.before_instructions = before_instructions;
|
|
|
|
|
|
|
|
visit_tree(actual, copy_index_derefs_to_temps, &data);
|
|
|
|
}
|
|
|
|
|
2013-03-23 00:45:52 +00:00
|
|
|
/* To convert an out parameter, we need to create a temporary variable to
|
|
|
|
* hold the value before conversion, and then perform the conversion after
|
|
|
|
* the function call returns.
|
|
|
|
*
|
|
|
|
* This has the effect of transforming code like this:
|
|
|
|
*
|
|
|
|
* void f(out int x);
|
|
|
|
* float value;
|
|
|
|
* f(value);
|
|
|
|
*
|
|
|
|
* Into IR that's equivalent to this:
|
|
|
|
*
|
|
|
|
* void f(out int x);
|
|
|
|
* float value;
|
|
|
|
* int out_parameter_conversion;
|
|
|
|
* f(out_parameter_conversion);
|
|
|
|
* value = float(out_parameter_conversion);
|
|
|
|
*
|
|
|
|
* If the parameter is an ir_expression of ir_binop_vector_extract,
|
|
|
|
* additional conversion is needed in the post-call re-write.
|
|
|
|
*/
|
|
|
|
ir_variable *tmp =
|
|
|
|
new(mem_ctx) ir_variable(formal_type, "inout_tmp", ir_var_temporary);
|
|
|
|
|
|
|
|
before_instructions->push_tail(tmp);
|
|
|
|
|
|
|
|
/* If the parameter is an inout parameter, copy the value of the actual
|
|
|
|
* parameter to the new temporary. Note that no type conversion is allowed
|
|
|
|
* here because inout parameters must match types exactly.
|
|
|
|
*/
|
|
|
|
if (parameter_is_inout) {
|
|
|
|
/* Inout parameters should never require conversion, since that would
|
|
|
|
* require an implicit conversion to exist both to and from the formal
|
|
|
|
* parameter type, and there are no bidirectional implicit conversions.
|
|
|
|
*/
|
|
|
|
assert (actual->type == formal_type);
|
|
|
|
|
|
|
|
ir_dereference_variable *const deref_tmp_1 =
|
|
|
|
new(mem_ctx) ir_dereference_variable(tmp);
|
|
|
|
ir_assignment *const assignment =
|
2018-04-09 05:40:40 +01:00
|
|
|
new(mem_ctx) ir_assignment(deref_tmp_1, actual->clone(mem_ctx, NULL));
|
2013-03-23 00:45:52 +00:00
|
|
|
before_instructions->push_tail(assignment);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Replace the parameter in the call with a dereference of the new
|
|
|
|
* temporary.
|
|
|
|
*/
|
|
|
|
ir_dereference_variable *const deref_tmp_2 =
|
|
|
|
new(mem_ctx) ir_dereference_variable(tmp);
|
|
|
|
actual->replace_with(deref_tmp_2);
|
|
|
|
|
|
|
|
|
|
|
|
/* Copy the temporary variable to the actual parameter with optional
|
|
|
|
* type conversion applied.
|
|
|
|
*/
|
|
|
|
ir_rvalue *rhs = new(mem_ctx) ir_dereference_variable(tmp);
|
|
|
|
if (actual->type != formal_type)
|
|
|
|
rhs = convert_component(rhs, actual->type);
|
|
|
|
|
|
|
|
ir_rvalue *lhs = actual;
|
|
|
|
if (expr != NULL && expr->operation == ir_binop_vector_extract) {
|
2016-08-02 21:19:28 +01:00
|
|
|
lhs = new(mem_ctx) ir_dereference_array(expr->operands[0]->clone(mem_ctx,
|
|
|
|
NULL),
|
|
|
|
expr->operands[1]->clone(mem_ctx,
|
|
|
|
NULL));
|
2013-03-23 00:45:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ir_assignment *const assignment_2 = new(mem_ctx) ir_assignment(lhs, rhs);
|
|
|
|
after_instructions->push_tail(assignment_2);
|
|
|
|
}
|
|
|
|
|
2012-03-29 03:24:45 +01:00
|
|
|
/**
|
2013-11-23 18:39:34 +00:00
|
|
|
* Generate a function call.
|
|
|
|
*
|
2016-08-02 21:19:28 +01:00
|
|
|
* For non-void functions, this returns a dereference of the temporary
|
|
|
|
* variable which stores the return value for the call. For void functions,
|
|
|
|
* this returns NULL.
|
2012-03-29 03:24:45 +01:00
|
|
|
*/
|
|
|
|
static ir_rvalue *
|
|
|
|
generate_call(exec_list *instructions, ir_function_signature *sig,
|
2016-08-02 21:19:28 +01:00
|
|
|
exec_list *actual_parameters,
|
2015-06-01 01:55:47 +01:00
|
|
|
ir_variable *sub_var,
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_rvalue *array_idx,
|
2017-07-27 07:49:55 +01:00
|
|
|
struct _mesa_glsl_parse_state *state)
|
2012-03-29 03:24:45 +01:00
|
|
|
{
|
|
|
|
void *ctx = state;
|
|
|
|
exec_list post_call_conversions;
|
|
|
|
|
|
|
|
/* Perform implicit conversion of arguments. For out parameters, we need
|
|
|
|
* to place them in a temporary variable and do the conversion after the
|
|
|
|
* call takes place. Since we haven't emitted the call yet, we'll place
|
|
|
|
* the post-call conversions in a temporary exec_list, and emit them later.
|
|
|
|
*/
|
glsl: Use a new foreach_two_lists macro for walking two lists at once.
When handling function calls, we often want to walk through the list of
formal parameters and list of actual parameters at the same time.
(Both are guaranteed to be the same length.)
Previously, we used a pattern of:
exec_list_iterator 1st_iter = <1st list>.iterator();
foreach_iter(exec_list_iterator, 2nd_iter, <2nd list>) {
...
1st_iter.next();
}
This was awkward, since you had to manually iterate through one of
the two lists.
This patch introduces a foreach_two_lists macro which safely walks
through two lists at the same time, so you can simply do:
foreach_two_lists(1st_node, <1st list>, 2nd_node, <2nd list>) {
...
}
v2: Rename macro from foreach_list2 to foreach_two_lists, as suggested
by Ian Romanick.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Matt Turner <mattst88@gmail.com>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2014-01-11 00:39:17 +00:00
|
|
|
foreach_two_lists(formal_node, &sig->parameters,
|
|
|
|
actual_node, actual_parameters) {
|
|
|
|
ir_rvalue *actual = (ir_rvalue *) actual_node;
|
|
|
|
ir_variable *formal = (ir_variable *) formal_node;
|
2012-03-29 03:24:45 +01:00
|
|
|
|
2011-11-09 11:01:54 +00:00
|
|
|
if (formal->type->is_numeric() || formal->type->is_boolean()) {
|
2016-08-02 21:19:28 +01:00
|
|
|
switch (formal->data.mode) {
|
|
|
|
case ir_var_const_in:
|
|
|
|
case ir_var_function_in: {
|
|
|
|
ir_rvalue *converted
|
|
|
|
= convert_component(actual, formal->type);
|
|
|
|
actual->replace_with(converted);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ir_var_function_out:
|
|
|
|
case ir_var_function_inout:
|
2013-03-23 00:45:52 +00:00
|
|
|
fix_parameter(ctx, actual, formal->type,
|
|
|
|
instructions, &post_call_conversions,
|
2013-12-12 11:51:01 +00:00
|
|
|
formal->data.mode == ir_var_function_inout);
|
2016-08-02 21:19:28 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert (!"Illegal formal parameter mode");
|
|
|
|
break;
|
|
|
|
}
|
2011-11-09 11:01:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-09 22:17:32 +01:00
|
|
|
/* Section 4.3.2 (Const) of the GLSL 1.10.59 spec says:
|
|
|
|
*
|
|
|
|
* "Initializers for const declarations must be formed from literal
|
|
|
|
* values, other const variables (not including function call
|
|
|
|
* paramaters), or expressions of these.
|
|
|
|
*
|
|
|
|
* Constructors may be used in such expressions, but function calls may
|
|
|
|
* not."
|
|
|
|
*
|
|
|
|
* Section 4.3.3 (Constant Expressions) of the GLSL 1.20.8 spec says:
|
|
|
|
*
|
|
|
|
* "A constant expression is one of
|
|
|
|
*
|
|
|
|
* ...
|
|
|
|
*
|
|
|
|
* - a built-in function call whose arguments are all constant
|
|
|
|
* expressions, with the exception of the texture lookup
|
|
|
|
* functions, the noise functions, and ftransform. The built-in
|
|
|
|
* functions dFdx, dFdy, and fwidth must return 0 when evaluated
|
|
|
|
* inside an initializer with an argument that is a constant
|
|
|
|
* expression."
|
|
|
|
*
|
|
|
|
* Section 5.10 (Constant Expressions) of the GLSL ES 1.00.17 spec says:
|
2011-11-09 11:01:54 +00:00
|
|
|
*
|
2015-10-09 22:17:32 +01:00
|
|
|
* "A constant expression is one of
|
|
|
|
*
|
|
|
|
* ...
|
|
|
|
*
|
|
|
|
* - a built-in function call whose arguments are all constant
|
|
|
|
* expressions, with the exception of the texture lookup
|
|
|
|
* functions."
|
|
|
|
*
|
|
|
|
* Section 4.3.3 (Constant Expressions) of the GLSL ES 3.00.4 spec says:
|
|
|
|
*
|
|
|
|
* "A constant expression is one of
|
|
|
|
*
|
|
|
|
* ...
|
|
|
|
*
|
|
|
|
* - a built-in function call whose arguments are all constant
|
|
|
|
* expressions, with the exception of the texture lookup
|
|
|
|
* functions. The built-in functions dFdx, dFdy, and fwidth must
|
|
|
|
* return 0 when evaluated inside an initializer with an argument
|
|
|
|
* that is a constant expression."
|
|
|
|
*
|
|
|
|
* If the function call is a constant expression, don't generate any
|
|
|
|
* instructions; just generate an ir_constant.
|
2011-11-09 11:01:54 +00:00
|
|
|
*/
|
2018-06-14 02:00:22 +01:00
|
|
|
if (state->is_version(120, 100) ||
|
2022-01-07 03:05:09 +00:00
|
|
|
state->consts->AllowGLSLBuiltinConstantExpression) {
|
2017-08-10 11:42:29 +01:00
|
|
|
ir_constant *value = sig->constant_expression_value(ctx,
|
|
|
|
actual_parameters,
|
2016-08-02 21:19:28 +01:00
|
|
|
NULL);
|
glsl: Convert ir_call to be a statement rather than a value.
Aside from ir_call, our IR is cleanly split into two classes:
- Statements (typeless; used for side effects, control flow)
- Values (deeply nestable, pure, typed expression trees)
Unfortunately, ir_call confused all this:
- For void functions, we placed ir_call directly in the instruction
stream, treating it as an untyped statement. Yet, it was a subclass
of ir_rvalue, and no other ir_rvalue could be used in this way.
- For functions with a return value, ir_call could be placed in
arbitrary expression trees. While this fit naturally with the source
language, it meant that expressions might not be pure, making it
difficult to transform and optimize them. To combat this, we always
emitted ir_call directly in the RHS of an ir_assignment, only using
a temporary variable in expression trees. Many passes relied on this
assumption; the acos and atan built-ins violated it.
This patch makes ir_call a statement (ir_instruction) rather than a
value (ir_rvalue). Non-void calls now take a ir_dereference of a
variable, and store the return value there---effectively a call and
assignment rolled into one. They cannot be embedded in expressions.
All expression trees are now pure, without exception.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-20 22:56:37 +00:00
|
|
|
if (value != NULL) {
|
2016-08-02 21:19:28 +01:00
|
|
|
return value;
|
2011-11-09 11:01:54 +00:00
|
|
|
}
|
glsl: Convert ir_call to be a statement rather than a value.
Aside from ir_call, our IR is cleanly split into two classes:
- Statements (typeless; used for side effects, control flow)
- Values (deeply nestable, pure, typed expression trees)
Unfortunately, ir_call confused all this:
- For void functions, we placed ir_call directly in the instruction
stream, treating it as an untyped statement. Yet, it was a subclass
of ir_rvalue, and no other ir_rvalue could be used in this way.
- For functions with a return value, ir_call could be placed in
arbitrary expression trees. While this fit naturally with the source
language, it meant that expressions might not be pure, making it
difficult to transform and optimize them. To combat this, we always
emitted ir_call directly in the RHS of an ir_assignment, only using
a temporary variable in expression trees. Many passes relied on this
assumption; the acos and atan built-ins violated it.
This patch makes ir_call a statement (ir_instruction) rather than a
value (ir_rvalue). Non-void calls now take a ir_dereference of a
variable, and store the return value there---effectively a call and
assignment rolled into one. They cannot be embedded in expressions.
All expression trees are now pure, without exception.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-20 22:56:37 +00:00
|
|
|
}
|
2011-11-09 11:01:54 +00:00
|
|
|
|
glsl: Convert ir_call to be a statement rather than a value.
Aside from ir_call, our IR is cleanly split into two classes:
- Statements (typeless; used for side effects, control flow)
- Values (deeply nestable, pure, typed expression trees)
Unfortunately, ir_call confused all this:
- For void functions, we placed ir_call directly in the instruction
stream, treating it as an untyped statement. Yet, it was a subclass
of ir_rvalue, and no other ir_rvalue could be used in this way.
- For functions with a return value, ir_call could be placed in
arbitrary expression trees. While this fit naturally with the source
language, it meant that expressions might not be pure, making it
difficult to transform and optimize them. To combat this, we always
emitted ir_call directly in the RHS of an ir_assignment, only using
a temporary variable in expression trees. Many passes relied on this
assumption; the acos and atan built-ins violated it.
This patch makes ir_call a statement (ir_instruction) rather than a
value (ir_rvalue). Non-void calls now take a ir_dereference of a
variable, and store the return value there---effectively a call and
assignment rolled into one. They cannot be embedded in expressions.
All expression trees are now pure, without exception.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-20 22:56:37 +00:00
|
|
|
ir_dereference_variable *deref = NULL;
|
|
|
|
if (!sig->return_type->is_void()) {
|
|
|
|
/* Create a new temporary to hold the return value. */
|
2014-07-09 03:04:52 +01:00
|
|
|
char *const name = ir_variable::temporaries_allocate_names
|
|
|
|
? ralloc_asprintf(ctx, "%s_retval", sig->function_name())
|
|
|
|
: NULL;
|
|
|
|
|
2011-11-09 11:01:54 +00:00
|
|
|
ir_variable *var;
|
|
|
|
|
2014-07-09 03:04:52 +01:00
|
|
|
var = new(ctx) ir_variable(sig->return_type, name, ir_var_temporary);
|
2011-11-09 11:01:54 +00:00
|
|
|
instructions->push_tail(var);
|
|
|
|
|
2014-07-09 03:04:52 +01:00
|
|
|
ralloc_free(name);
|
|
|
|
|
2011-11-09 11:01:54 +00:00
|
|
|
deref = new(ctx) ir_dereference_variable(var);
|
|
|
|
}
|
2015-06-01 01:55:47 +01:00
|
|
|
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_call *call = new(ctx) ir_call(sig, deref,
|
|
|
|
actual_parameters, sub_var, array_idx);
|
glsl: Convert ir_call to be a statement rather than a value.
Aside from ir_call, our IR is cleanly split into two classes:
- Statements (typeless; used for side effects, control flow)
- Values (deeply nestable, pure, typed expression trees)
Unfortunately, ir_call confused all this:
- For void functions, we placed ir_call directly in the instruction
stream, treating it as an untyped statement. Yet, it was a subclass
of ir_rvalue, and no other ir_rvalue could be used in this way.
- For functions with a return value, ir_call could be placed in
arbitrary expression trees. While this fit naturally with the source
language, it meant that expressions might not be pure, making it
difficult to transform and optimize them. To combat this, we always
emitted ir_call directly in the RHS of an ir_assignment, only using
a temporary variable in expression trees. Many passes relied on this
assumption; the acos and atan built-ins violated it.
This patch makes ir_call a statement (ir_instruction) rather than a
value (ir_rvalue). Non-void calls now take a ir_dereference of a
variable, and store the return value there---effectively a call and
assignment rolled into one. They cannot be embedded in expressions.
All expression trees are now pure, without exception.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-20 22:56:37 +00:00
|
|
|
instructions->push_tail(call);
|
|
|
|
|
|
|
|
/* Also emit any necessary out-parameter conversions. */
|
2011-11-09 11:01:54 +00:00
|
|
|
instructions->append_list(&post_call_conversions);
|
glsl: Convert ir_call to be a statement rather than a value.
Aside from ir_call, our IR is cleanly split into two classes:
- Statements (typeless; used for side effects, control flow)
- Values (deeply nestable, pure, typed expression trees)
Unfortunately, ir_call confused all this:
- For void functions, we placed ir_call directly in the instruction
stream, treating it as an untyped statement. Yet, it was a subclass
of ir_rvalue, and no other ir_rvalue could be used in this way.
- For functions with a return value, ir_call could be placed in
arbitrary expression trees. While this fit naturally with the source
language, it meant that expressions might not be pure, making it
difficult to transform and optimize them. To combat this, we always
emitted ir_call directly in the RHS of an ir_assignment, only using
a temporary variable in expression trees. Many passes relied on this
assumption; the acos and atan built-ins violated it.
This patch makes ir_call a statement (ir_instruction) rather than a
value (ir_rvalue). Non-void calls now take a ir_dereference of a
variable, and store the return value there---effectively a call and
assignment rolled into one. They cannot be embedded in expressions.
All expression trees are now pure, without exception.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-20 22:56:37 +00:00
|
|
|
|
|
|
|
return deref ? deref->clone(ctx, NULL) : NULL;
|
2011-11-09 11:01:54 +00:00
|
|
|
}
|
2010-08-06 01:21:39 +01:00
|
|
|
|
glsl: Split up function matching and call generation a bit more.
We used to have one big function, match_signature_by_name, which found
a matching signature, performed out-parameter conversions, and generated
the ir_call. As the code for matching against built-in functions became
more complicated, I split it internally, creating generate_call().
However, I left the same awkward interface. This patch splits it into
three functions:
1. match_signature_by_name()
This now takes a name, a list of parameters, the symbol table, and
returns an ir_function_signature. Simple and one purpose: matching.
2. no_matching_function_error()
Generate the "no matching function" error and list of prototypes.
This was complex enough that I felt it deserved its own function.
3. generate_call()
Do the out-parameter conversion and generate the ir_call. This
could probably use more splitting.
The caller now has a more natural workflow: find a matching signature,
then either generate an error or a call.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-28 23:57:50 +01:00
|
|
|
/**
|
|
|
|
* Given a function name and parameter list, find the matching signature.
|
|
|
|
*/
|
|
|
|
static ir_function_signature *
|
|
|
|
match_function_by_name(const char *name,
|
2016-08-02 21:19:28 +01:00
|
|
|
exec_list *actual_parameters,
|
|
|
|
struct _mesa_glsl_parse_state *state)
|
2010-04-01 00:28:51 +01:00
|
|
|
{
|
2010-11-11 20:11:27 +00:00
|
|
|
ir_function *f = state->symbols->get_function(name);
|
glsl: Always search for an exact function signature match.
Previously, we would fail to compile the following shader due to a bug
in lazy built-in importing:
#version 130
void main() {
float f = abs(5.0);
int i = abs(5);
}
The first call, abs(5.0), would fail to find a local signature, look
through the built-ins, and import "float abs(float)".
The second call, abs(5), would find the newly imported float signature
in the local shader, and settle for that. Unfortunately, it failed to
search the built-ins for the correct/exact signature, "int abs(int)".
Thus, abs(5) ended up being a float, causing a bizarre type error when
we tried to assign it to an int.
Fixes piglit test builtin-overload-matching.frag.
This is /not/ a candidate for stable branches, as it should only be
possible to trigger this bug using GLSL 1.30's built-in functions that
take integer arguments. Plus, the changes are fairly invasive.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
2011-11-11 08:43:06 +00:00
|
|
|
ir_function_signature *local_sig = NULL;
|
|
|
|
ir_function_signature *sig = NULL;
|
|
|
|
|
|
|
|
/* Is the function hidden by a record type constructor? */
|
|
|
|
if (state->symbols->get_type(name))
|
glsl: Immediately inline built-ins rather than generating calls.
In the past, we imported the prototypes of built-in functions, generated
calls to those, and waited until link time to resolve the calls and
import the actual code for the built-in functions.
This severely limited our compile-time optimization opportunities: even
trivial functions like dot() were represented as function calls. We
also had no way of reasoning about those calls; they could have been
1,000 line functions with side-effects for all we knew.
Practically all built-in functions are trivial translations to
ir_expression opcodes, so it makes sense to just generate those inline.
Since we eventually inline all functions anyway, we may as well just do
it for all built-in functions.
There's only one snag: built-in functions that refer to built-in global
variables need those remapped to the variables in the shader being
compiled, rather than the ones in the built-in shader. Currently,
ftransform() is the only function matching those criteria, so it seemed
easier to just make it a special case.
On Skylake:
total instructions in shared programs: 12023491 -> 12024010 (0.00%)
instructions in affected programs: 77595 -> 78114 (0.67%)
helped: 97
HURT: 309
total cycles in shared programs: 137239044 -> 137295498 (0.04%)
cycles in affected programs: 16714026 -> 16770480 (0.34%)
helped: 4663
HURT: 4923
while these statistics are in the wrong direction, the number of
hurt programs is small (309 / 41282 = 0.75%), and I don't think
anything can be done about it. A change like this significantly
alters the order in which optimizations are performed.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by; Ian Romanick <ian.d.romanick@intel.com>
2014-05-31 07:52:22 +01:00
|
|
|
return sig; /* no match */
|
glsl: Always search for an exact function signature match.
Previously, we would fail to compile the following shader due to a bug
in lazy built-in importing:
#version 130
void main() {
float f = abs(5.0);
int i = abs(5);
}
The first call, abs(5.0), would fail to find a local signature, look
through the built-ins, and import "float abs(float)".
The second call, abs(5), would find the newly imported float signature
in the local shader, and settle for that. Unfortunately, it failed to
search the built-ins for the correct/exact signature, "int abs(int)".
Thus, abs(5) ended up being a float, causing a bizarre type error when
we tried to assign it to an int.
Fixes piglit test builtin-overload-matching.frag.
This is /not/ a candidate for stable branches, as it should only be
possible to trigger this bug using GLSL 1.30's built-in functions that
take integer arguments. Plus, the changes are fairly invasive.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
2011-11-11 08:43:06 +00:00
|
|
|
|
|
|
|
/* Is the function hidden by a variable (impossible in 1.10)? */
|
2012-08-02 01:44:02 +01:00
|
|
|
if (!state->symbols->separate_function_namespace
|
|
|
|
&& state->symbols->get_variable(name))
|
glsl: Immediately inline built-ins rather than generating calls.
In the past, we imported the prototypes of built-in functions, generated
calls to those, and waited until link time to resolve the calls and
import the actual code for the built-in functions.
This severely limited our compile-time optimization opportunities: even
trivial functions like dot() were represented as function calls. We
also had no way of reasoning about those calls; they could have been
1,000 line functions with side-effects for all we knew.
Practically all built-in functions are trivial translations to
ir_expression opcodes, so it makes sense to just generate those inline.
Since we eventually inline all functions anyway, we may as well just do
it for all built-in functions.
There's only one snag: built-in functions that refer to built-in global
variables need those remapped to the variables in the shader being
compiled, rather than the ones in the built-in shader. Currently,
ftransform() is the only function matching those criteria, so it seemed
easier to just make it a special case.
On Skylake:
total instructions in shared programs: 12023491 -> 12024010 (0.00%)
instructions in affected programs: 77595 -> 78114 (0.67%)
helped: 97
HURT: 309
total cycles in shared programs: 137239044 -> 137295498 (0.04%)
cycles in affected programs: 16714026 -> 16770480 (0.34%)
helped: 4663
HURT: 4923
while these statistics are in the wrong direction, the number of
hurt programs is small (309 / 41282 = 0.75%), and I don't think
anything can be done about it. A change like this significantly
alters the order in which optimizations are performed.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by; Ian Romanick <ian.d.romanick@intel.com>
2014-05-31 07:52:22 +01:00
|
|
|
return sig; /* no match */
|
glsl: Always search for an exact function signature match.
Previously, we would fail to compile the following shader due to a bug
in lazy built-in importing:
#version 130
void main() {
float f = abs(5.0);
int i = abs(5);
}
The first call, abs(5.0), would fail to find a local signature, look
through the built-ins, and import "float abs(float)".
The second call, abs(5), would find the newly imported float signature
in the local shader, and settle for that. Unfortunately, it failed to
search the built-ins for the correct/exact signature, "int abs(int)".
Thus, abs(5) ended up being a float, causing a bizarre type error when
we tried to assign it to an int.
Fixes piglit test builtin-overload-matching.frag.
This is /not/ a candidate for stable branches, as it should only be
possible to trigger this bug using GLSL 1.30's built-in functions that
take integer arguments. Plus, the changes are fairly invasive.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
2011-11-11 08:43:06 +00:00
|
|
|
|
|
|
|
if (f != NULL) {
|
2014-07-24 22:05:40 +01:00
|
|
|
/* In desktop GL, the presence of a user-defined signature hides any
|
|
|
|
* built-in signatures, so we must ignore them. In contrast, in ES2
|
|
|
|
* user-defined signatures add new overloads, so we must consider them.
|
|
|
|
*/
|
|
|
|
bool allow_builtins = state->es_shader || !f->has_user_signature();
|
|
|
|
|
glsl: Always search for an exact function signature match.
Previously, we would fail to compile the following shader due to a bug
in lazy built-in importing:
#version 130
void main() {
float f = abs(5.0);
int i = abs(5);
}
The first call, abs(5.0), would fail to find a local signature, look
through the built-ins, and import "float abs(float)".
The second call, abs(5), would find the newly imported float signature
in the local shader, and settle for that. Unfortunately, it failed to
search the built-ins for the correct/exact signature, "int abs(int)".
Thus, abs(5) ended up being a float, causing a bizarre type error when
we tried to assign it to an int.
Fixes piglit test builtin-overload-matching.frag.
This is /not/ a candidate for stable branches, as it should only be
possible to trigger this bug using GLSL 1.30's built-in functions that
take integer arguments. Plus, the changes are fairly invasive.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
2011-11-11 08:43:06 +00:00
|
|
|
/* Look for a match in the local shader. If exact, we're done. */
|
|
|
|
bool is_exact = false;
|
2013-08-31 07:11:55 +01:00
|
|
|
sig = local_sig = f->matching_signature(state, actual_parameters,
|
2014-07-24 22:05:40 +01:00
|
|
|
allow_builtins, &is_exact);
|
glsl: Always search for an exact function signature match.
Previously, we would fail to compile the following shader due to a bug
in lazy built-in importing:
#version 130
void main() {
float f = abs(5.0);
int i = abs(5);
}
The first call, abs(5.0), would fail to find a local signature, look
through the built-ins, and import "float abs(float)".
The second call, abs(5), would find the newly imported float signature
in the local shader, and settle for that. Unfortunately, it failed to
search the built-ins for the correct/exact signature, "int abs(int)".
Thus, abs(5) ended up being a float, causing a bizarre type error when
we tried to assign it to an int.
Fixes piglit test builtin-overload-matching.frag.
This is /not/ a candidate for stable branches, as it should only be
possible to trigger this bug using GLSL 1.30's built-in functions that
take integer arguments. Plus, the changes are fairly invasive.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
2011-11-11 08:43:06 +00:00
|
|
|
if (is_exact)
|
glsl: Immediately inline built-ins rather than generating calls.
In the past, we imported the prototypes of built-in functions, generated
calls to those, and waited until link time to resolve the calls and
import the actual code for the built-in functions.
This severely limited our compile-time optimization opportunities: even
trivial functions like dot() were represented as function calls. We
also had no way of reasoning about those calls; they could have been
1,000 line functions with side-effects for all we knew.
Practically all built-in functions are trivial translations to
ir_expression opcodes, so it makes sense to just generate those inline.
Since we eventually inline all functions anyway, we may as well just do
it for all built-in functions.
There's only one snag: built-in functions that refer to built-in global
variables need those remapped to the variables in the shader being
compiled, rather than the ones in the built-in shader. Currently,
ftransform() is the only function matching those criteria, so it seemed
easier to just make it a special case.
On Skylake:
total instructions in shared programs: 12023491 -> 12024010 (0.00%)
instructions in affected programs: 77595 -> 78114 (0.67%)
helped: 97
HURT: 309
total cycles in shared programs: 137239044 -> 137295498 (0.04%)
cycles in affected programs: 16714026 -> 16770480 (0.34%)
helped: 4663
HURT: 4923
while these statistics are in the wrong direction, the number of
hurt programs is small (309 / 41282 = 0.75%), and I don't think
anything can be done about it. A change like this significantly
alters the order in which optimizations are performed.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by; Ian Romanick <ian.d.romanick@intel.com>
2014-05-31 07:52:22 +01:00
|
|
|
return sig;
|
glsl: Always search for an exact function signature match.
Previously, we would fail to compile the following shader due to a bug
in lazy built-in importing:
#version 130
void main() {
float f = abs(5.0);
int i = abs(5);
}
The first call, abs(5.0), would fail to find a local signature, look
through the built-ins, and import "float abs(float)".
The second call, abs(5), would find the newly imported float signature
in the local shader, and settle for that. Unfortunately, it failed to
search the built-ins for the correct/exact signature, "int abs(int)".
Thus, abs(5) ended up being a float, causing a bizarre type error when
we tried to assign it to an int.
Fixes piglit test builtin-overload-matching.frag.
This is /not/ a candidate for stable branches, as it should only be
possible to trigger this bug using GLSL 1.30's built-in functions that
take integer arguments. Plus, the changes are fairly invasive.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
2011-11-11 08:43:06 +00:00
|
|
|
|
2014-07-24 22:05:40 +01:00
|
|
|
if (!allow_builtins)
|
glsl: Immediately inline built-ins rather than generating calls.
In the past, we imported the prototypes of built-in functions, generated
calls to those, and waited until link time to resolve the calls and
import the actual code for the built-in functions.
This severely limited our compile-time optimization opportunities: even
trivial functions like dot() were represented as function calls. We
also had no way of reasoning about those calls; they could have been
1,000 line functions with side-effects for all we knew.
Practically all built-in functions are trivial translations to
ir_expression opcodes, so it makes sense to just generate those inline.
Since we eventually inline all functions anyway, we may as well just do
it for all built-in functions.
There's only one snag: built-in functions that refer to built-in global
variables need those remapped to the variables in the shader being
compiled, rather than the ones in the built-in shader. Currently,
ftransform() is the only function matching those criteria, so it seemed
easier to just make it a special case.
On Skylake:
total instructions in shared programs: 12023491 -> 12024010 (0.00%)
instructions in affected programs: 77595 -> 78114 (0.67%)
helped: 97
HURT: 309
total cycles in shared programs: 137239044 -> 137295498 (0.04%)
cycles in affected programs: 16714026 -> 16770480 (0.34%)
helped: 4663
HURT: 4923
while these statistics are in the wrong direction, the number of
hurt programs is small (309 / 41282 = 0.75%), and I don't think
anything can be done about it. A change like this significantly
alters the order in which optimizations are performed.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by; Ian Romanick <ian.d.romanick@intel.com>
2014-05-31 07:52:22 +01:00
|
|
|
return sig;
|
glsl: Always search for an exact function signature match.
Previously, we would fail to compile the following shader due to a bug
in lazy built-in importing:
#version 130
void main() {
float f = abs(5.0);
int i = abs(5);
}
The first call, abs(5.0), would fail to find a local signature, look
through the built-ins, and import "float abs(float)".
The second call, abs(5), would find the newly imported float signature
in the local shader, and settle for that. Unfortunately, it failed to
search the built-ins for the correct/exact signature, "int abs(int)".
Thus, abs(5) ended up being a float, causing a bizarre type error when
we tried to assign it to an int.
Fixes piglit test builtin-overload-matching.frag.
This is /not/ a candidate for stable branches, as it should only be
possible to trigger this bug using GLSL 1.30's built-in functions that
take integer arguments. Plus, the changes are fairly invasive.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
2011-11-11 08:43:06 +00:00
|
|
|
}
|
2010-06-24 02:11:51 +01:00
|
|
|
|
glsl: Always search for an exact function signature match.
Previously, we would fail to compile the following shader due to a bug
in lazy built-in importing:
#version 130
void main() {
float f = abs(5.0);
int i = abs(5);
}
The first call, abs(5.0), would fail to find a local signature, look
through the built-ins, and import "float abs(float)".
The second call, abs(5), would find the newly imported float signature
in the local shader, and settle for that. Unfortunately, it failed to
search the built-ins for the correct/exact signature, "int abs(int)".
Thus, abs(5) ended up being a float, causing a bizarre type error when
we tried to assign it to an int.
Fixes piglit test builtin-overload-matching.frag.
This is /not/ a candidate for stable branches, as it should only be
possible to trigger this bug using GLSL 1.30's built-in functions that
take integer arguments. Plus, the changes are fairly invasive.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
2011-11-11 08:43:06 +00:00
|
|
|
/* Local shader has no exact candidates; check the built-ins. */
|
2013-09-02 04:48:45 +01:00
|
|
|
sig = _mesa_glsl_find_builtin_function(state, name, actual_parameters);
|
2018-10-30 14:15:58 +00:00
|
|
|
|
|
|
|
/* if _mesa_glsl_find_builtin_function failed, fall back to the result
|
|
|
|
* of choose_best_inexact_overload() instead. This should only affect
|
|
|
|
* GLES.
|
|
|
|
*/
|
|
|
|
return sig ? sig : local_sig;
|
glsl: Split up function matching and call generation a bit more.
We used to have one big function, match_signature_by_name, which found
a matching signature, performed out-parameter conversions, and generated
the ir_call. As the code for matching against built-in functions became
more complicated, I split it internally, creating generate_call().
However, I left the same awkward interface. This patch splits it into
three functions:
1. match_signature_by_name()
This now takes a name, a list of parameters, the symbol table, and
returns an ir_function_signature. Simple and one purpose: matching.
2. no_matching_function_error()
Generate the "no matching function" error and list of prototypes.
This was complex enough that I felt it deserved its own function.
3. generate_call()
Do the out-parameter conversion and generate the ir_call. This
could probably use more splitting.
The caller now has a more natural workflow: find a matching signature,
then either generate an error or a call.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-28 23:57:50 +01:00
|
|
|
}
|
glsl: Always search for an exact function signature match.
Previously, we would fail to compile the following shader due to a bug
in lazy built-in importing:
#version 130
void main() {
float f = abs(5.0);
int i = abs(5);
}
The first call, abs(5.0), would fail to find a local signature, look
through the built-ins, and import "float abs(float)".
The second call, abs(5), would find the newly imported float signature
in the local shader, and settle for that. Unfortunately, it failed to
search the built-ins for the correct/exact signature, "int abs(int)".
Thus, abs(5) ended up being a float, causing a bizarre type error when
we tried to assign it to an int.
Fixes piglit test builtin-overload-matching.frag.
This is /not/ a candidate for stable branches, as it should only be
possible to trigger this bug using GLSL 1.30's built-in functions that
take integer arguments. Plus, the changes are fairly invasive.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
2011-11-11 08:43:06 +00:00
|
|
|
|
2015-06-01 01:55:47 +01:00
|
|
|
static ir_function_signature *
|
|
|
|
match_subroutine_by_name(const char *name,
|
|
|
|
exec_list *actual_parameters,
|
|
|
|
struct _mesa_glsl_parse_state *state,
|
|
|
|
ir_variable **var_r)
|
|
|
|
{
|
|
|
|
void *ctx = state;
|
|
|
|
ir_function_signature *sig = NULL;
|
|
|
|
ir_function *f, *found = NULL;
|
|
|
|
const char *new_name;
|
|
|
|
ir_variable *var;
|
|
|
|
bool is_exact = false;
|
|
|
|
|
2016-08-02 21:19:28 +01:00
|
|
|
new_name =
|
|
|
|
ralloc_asprintf(ctx, "%s_%s",
|
|
|
|
_mesa_shader_stage_to_subroutine_prefix(state->stage),
|
|
|
|
name);
|
2015-06-01 01:55:47 +01:00
|
|
|
var = state->symbols->get_variable(new_name);
|
|
|
|
if (!var)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (int i = 0; i < state->num_subroutine_types; i++) {
|
|
|
|
f = state->subroutine_types[i];
|
|
|
|
if (strcmp(f->name, var->type->without_array()->name))
|
|
|
|
continue;
|
|
|
|
found = f;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
return NULL;
|
|
|
|
*var_r = var;
|
|
|
|
sig = found->matching_signature(state, actual_parameters,
|
2016-08-02 21:19:28 +01:00
|
|
|
false, &is_exact);
|
2015-06-01 01:55:47 +01:00
|
|
|
return sig;
|
|
|
|
}
|
|
|
|
|
2015-08-05 06:49:22 +01:00
|
|
|
static ir_rvalue *
|
|
|
|
generate_array_index(void *mem_ctx, exec_list *instructions,
|
|
|
|
struct _mesa_glsl_parse_state *state, YYLTYPE loc,
|
|
|
|
const ast_expression *array, ast_expression *idx,
|
|
|
|
const char **function_name, exec_list *actual_parameters)
|
|
|
|
{
|
|
|
|
if (array->oper == ast_array_index) {
|
|
|
|
/* This handles arrays of arrays */
|
|
|
|
ir_rvalue *outer_array = generate_array_index(mem_ctx, instructions,
|
|
|
|
state, loc,
|
|
|
|
array->subexpressions[0],
|
|
|
|
array->subexpressions[1],
|
2016-08-02 21:19:28 +01:00
|
|
|
function_name,
|
|
|
|
actual_parameters);
|
2015-08-05 06:49:22 +01:00
|
|
|
ir_rvalue *outer_array_idx = idx->hir(instructions, state);
|
|
|
|
|
|
|
|
YYLTYPE index_loc = idx->get_location();
|
|
|
|
return _mesa_ast_array_index_to_hir(mem_ctx, state, outer_array,
|
|
|
|
outer_array_idx, loc,
|
|
|
|
index_loc);
|
|
|
|
} else {
|
|
|
|
ir_variable *sub_var = NULL;
|
|
|
|
*function_name = array->primary_expression.identifier;
|
|
|
|
|
2017-11-19 10:55:10 +00:00
|
|
|
if (!match_subroutine_by_name(*function_name, actual_parameters,
|
|
|
|
state, &sub_var)) {
|
|
|
|
_mesa_glsl_error(&loc, state, "Unknown subroutine `%s'",
|
|
|
|
*function_name);
|
|
|
|
*function_name = NULL; /* indicate error condition to caller */
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-08-05 06:49:22 +01:00
|
|
|
|
|
|
|
ir_rvalue *outer_array_idx = idx->hir(instructions, state);
|
|
|
|
return new(mem_ctx) ir_dereference_array(sub_var, outer_array_idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-13 11:03:27 +01:00
|
|
|
static bool
|
|
|
|
function_exists(_mesa_glsl_parse_state *state,
|
|
|
|
struct glsl_symbol_table *symbols, const char *name)
|
|
|
|
{
|
|
|
|
ir_function *f = symbols->get_function(name);
|
|
|
|
if (f != NULL) {
|
|
|
|
foreach_in_list(ir_function_signature, sig, &f->signatures) {
|
|
|
|
if (sig->is_builtin() && !sig->is_builtin_available(state))
|
|
|
|
continue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-11-23 20:06:24 +00:00
|
|
|
static void
|
|
|
|
print_function_prototypes(_mesa_glsl_parse_state *state, YYLTYPE *loc,
|
|
|
|
ir_function *f)
|
|
|
|
{
|
|
|
|
if (f == NULL)
|
|
|
|
return;
|
|
|
|
|
2014-06-25 05:34:05 +01:00
|
|
|
foreach_in_list(ir_function_signature, sig, &f->signatures) {
|
2013-11-23 20:06:24 +00:00
|
|
|
if (sig->is_builtin() && !sig->is_builtin_available(state))
|
|
|
|
continue;
|
|
|
|
|
2016-08-02 21:19:28 +01:00
|
|
|
char *str = prototype_string(sig->return_type, f->name,
|
|
|
|
&sig->parameters);
|
2013-11-23 20:06:24 +00:00
|
|
|
_mesa_glsl_error(loc, state, " %s", str);
|
|
|
|
ralloc_free(str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
glsl: Split up function matching and call generation a bit more.
We used to have one big function, match_signature_by_name, which found
a matching signature, performed out-parameter conversions, and generated
the ir_call. As the code for matching against built-in functions became
more complicated, I split it internally, creating generate_call().
However, I left the same awkward interface. This patch splits it into
three functions:
1. match_signature_by_name()
This now takes a name, a list of parameters, the symbol table, and
returns an ir_function_signature. Simple and one purpose: matching.
2. no_matching_function_error()
Generate the "no matching function" error and list of prototypes.
This was complex enough that I felt it deserved its own function.
3. generate_call()
Do the out-parameter conversion and generate the ir_call. This
could probably use more splitting.
The caller now has a more natural workflow: find a matching signature,
then either generate an error or a call.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-28 23:57:50 +01:00
|
|
|
/**
|
|
|
|
* Raise a "no matching function" error, listing all possible overloads the
|
|
|
|
* compiler considered so developers can figure out what went wrong.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
no_matching_function_error(const char *name,
|
2016-08-02 21:19:28 +01:00
|
|
|
YYLTYPE *loc,
|
|
|
|
exec_list *actual_parameters,
|
|
|
|
_mesa_glsl_parse_state *state)
|
glsl: Split up function matching and call generation a bit more.
We used to have one big function, match_signature_by_name, which found
a matching signature, performed out-parameter conversions, and generated
the ir_call. As the code for matching against built-in functions became
more complicated, I split it internally, creating generate_call().
However, I left the same awkward interface. This patch splits it into
three functions:
1. match_signature_by_name()
This now takes a name, a list of parameters, the symbol table, and
returns an ir_function_signature. Simple and one purpose: matching.
2. no_matching_function_error()
Generate the "no matching function" error and list of prototypes.
This was complex enough that I felt it deserved its own function.
3. generate_call()
Do the out-parameter conversion and generate the ir_call. This
could probably use more splitting.
The caller now has a more natural workflow: find a matching signature,
then either generate an error or a call.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-28 23:57:50 +01:00
|
|
|
{
|
2013-12-09 09:18:26 +00:00
|
|
|
gl_shader *sh = _mesa_glsl_get_builtin_function_shader();
|
|
|
|
|
2019-06-13 11:03:27 +01:00
|
|
|
if (!function_exists(state, state->symbols, name)
|
2016-08-02 21:19:28 +01:00
|
|
|
&& (!state->uses_builtin_functions
|
2019-06-13 11:03:27 +01:00
|
|
|
|| !function_exists(state, sh->symbols, name))) {
|
2013-12-09 09:18:26 +00:00
|
|
|
_mesa_glsl_error(loc, state, "no function with name '%s'", name);
|
|
|
|
} else {
|
|
|
|
char *str = prototype_string(NULL, name, actual_parameters);
|
|
|
|
_mesa_glsl_error(loc, state,
|
2016-08-02 21:19:28 +01:00
|
|
|
"no matching function for call to `%s';"
|
|
|
|
" candidates are:",
|
2013-12-09 09:18:26 +00:00
|
|
|
str);
|
|
|
|
ralloc_free(str);
|
2010-08-06 01:21:39 +01:00
|
|
|
|
2016-08-02 21:19:28 +01:00
|
|
|
print_function_prototypes(state, loc,
|
|
|
|
state->symbols->get_function(name));
|
2010-08-06 01:21:39 +01:00
|
|
|
|
2013-12-09 09:18:26 +00:00
|
|
|
if (state->uses_builtin_functions) {
|
2016-08-02 21:19:28 +01:00
|
|
|
print_function_prototypes(state, loc,
|
|
|
|
sh->symbols->get_function(name));
|
2013-12-09 09:18:26 +00:00
|
|
|
}
|
2010-03-15 20:26:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-27 00:38:58 +00:00
|
|
|
/**
|
|
|
|
* Perform automatic type conversion of constructor parameters
|
2010-07-20 07:45:23 +01:00
|
|
|
*
|
|
|
|
* This implements the rules in the "Conversion and Scalar Constructors"
|
|
|
|
* section (GLSL 1.10 section 5.4.1), not the "Implicit Conversions" rules.
|
2010-03-27 00:38:58 +00:00
|
|
|
*/
|
|
|
|
static ir_rvalue *
|
|
|
|
convert_component(ir_rvalue *src, const glsl_type *desired_type)
|
|
|
|
{
|
2011-01-21 22:32:31 +00:00
|
|
|
void *ctx = ralloc_parent(src);
|
2010-03-27 00:38:58 +00:00
|
|
|
const unsigned a = desired_type->base_type;
|
|
|
|
const unsigned b = src->type->base_type;
|
2010-06-07 23:08:04 +01:00
|
|
|
ir_expression *result = NULL;
|
2010-03-27 00:38:58 +00:00
|
|
|
|
|
|
|
if (src->type->is_error())
|
|
|
|
return src;
|
|
|
|
|
2017-04-21 14:27:15 +01:00
|
|
|
assert(a <= GLSL_TYPE_IMAGE);
|
|
|
|
assert(b <= GLSL_TYPE_IMAGE);
|
2010-03-27 00:38:58 +00:00
|
|
|
|
2011-06-15 07:23:49 +01:00
|
|
|
if (a == b)
|
2010-03-27 00:38:58 +00:00
|
|
|
return src;
|
|
|
|
|
|
|
|
switch (a) {
|
|
|
|
case GLSL_TYPE_UINT:
|
2011-06-15 07:23:49 +01:00
|
|
|
switch (b) {
|
|
|
|
case GLSL_TYPE_INT:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_i2u, src);
|
|
|
|
break;
|
2011-06-15 07:23:49 +01:00
|
|
|
case GLSL_TYPE_FLOAT:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_f2u, src);
|
|
|
|
break;
|
2011-06-15 07:23:49 +01:00
|
|
|
case GLSL_TYPE_BOOL:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_i2u,
|
|
|
|
new(ctx) ir_expression(ir_unop_b2i,
|
|
|
|
src));
|
|
|
|
break;
|
2015-02-05 10:04:58 +00:00
|
|
|
case GLSL_TYPE_DOUBLE:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_d2u, src);
|
|
|
|
break;
|
2016-06-09 01:01:00 +01:00
|
|
|
case GLSL_TYPE_UINT64:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_u642u, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_INT64:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_i642u, src);
|
|
|
|
break;
|
2017-04-21 14:27:15 +01:00
|
|
|
case GLSL_TYPE_SAMPLER:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_unpack_sampler_2x32, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_IMAGE:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_unpack_image_2x32, src);
|
|
|
|
break;
|
2011-06-15 07:23:49 +01:00
|
|
|
}
|
|
|
|
break;
|
2010-03-27 00:38:58 +00:00
|
|
|
case GLSL_TYPE_INT:
|
2011-06-15 07:23:49 +01:00
|
|
|
switch (b) {
|
|
|
|
case GLSL_TYPE_UINT:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_u2i, src);
|
|
|
|
break;
|
2011-06-15 07:23:49 +01:00
|
|
|
case GLSL_TYPE_FLOAT:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_f2i, src);
|
|
|
|
break;
|
2011-06-15 07:23:49 +01:00
|
|
|
case GLSL_TYPE_BOOL:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_b2i, src);
|
|
|
|
break;
|
2015-02-05 10:04:58 +00:00
|
|
|
case GLSL_TYPE_DOUBLE:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_d2i, src);
|
|
|
|
break;
|
2016-06-09 01:01:00 +01:00
|
|
|
case GLSL_TYPE_UINT64:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_u642i, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_INT64:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_i642i, src);
|
|
|
|
break;
|
2010-03-27 00:38:58 +00:00
|
|
|
}
|
2010-06-11 21:49:00 +01:00
|
|
|
break;
|
2010-03-27 00:38:58 +00:00
|
|
|
case GLSL_TYPE_FLOAT:
|
|
|
|
switch (b) {
|
|
|
|
case GLSL_TYPE_UINT:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_u2f, desired_type, src, NULL);
|
|
|
|
break;
|
2010-03-27 00:38:58 +00:00
|
|
|
case GLSL_TYPE_INT:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_i2f, desired_type, src, NULL);
|
|
|
|
break;
|
2010-03-27 00:38:58 +00:00
|
|
|
case GLSL_TYPE_BOOL:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_b2f, desired_type, src, NULL);
|
|
|
|
break;
|
2015-02-05 10:04:58 +00:00
|
|
|
case GLSL_TYPE_DOUBLE:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_d2f, desired_type, src, NULL);
|
|
|
|
break;
|
2016-06-09 01:01:00 +01:00
|
|
|
case GLSL_TYPE_UINT64:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_u642f, desired_type, src, NULL);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_INT64:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_i642f, desired_type, src, NULL);
|
|
|
|
break;
|
2010-03-27 00:38:58 +00:00
|
|
|
}
|
|
|
|
break;
|
2010-06-26 00:19:45 +01:00
|
|
|
case GLSL_TYPE_BOOL:
|
2010-06-12 00:52:09 +01:00
|
|
|
switch (b) {
|
2010-06-26 00:19:45 +01:00
|
|
|
case GLSL_TYPE_UINT:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_i2b,
|
|
|
|
new(ctx) ir_expression(ir_unop_u2i,
|
|
|
|
src));
|
|
|
|
break;
|
2010-06-26 00:19:45 +01:00
|
|
|
case GLSL_TYPE_INT:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_i2b, desired_type, src, NULL);
|
|
|
|
break;
|
2010-06-26 00:19:45 +01:00
|
|
|
case GLSL_TYPE_FLOAT:
|
2016-08-02 21:19:28 +01:00
|
|
|
result = new(ctx) ir_expression(ir_unop_f2b, desired_type, src, NULL);
|
|
|
|
break;
|
2015-02-05 10:04:58 +00:00
|
|
|
case GLSL_TYPE_DOUBLE:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_d2b, desired_type, src, NULL);
|
|
|
|
break;
|
2016-06-09 01:01:00 +01:00
|
|
|
case GLSL_TYPE_UINT64:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_i642b,
|
|
|
|
new(ctx) ir_expression(ir_unop_u642i64,
|
|
|
|
src));
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_INT64:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_i642b, desired_type, src, NULL);
|
|
|
|
break;
|
2010-06-12 00:52:09 +01:00
|
|
|
}
|
2010-06-26 00:19:45 +01:00
|
|
|
break;
|
2015-02-05 10:04:58 +00:00
|
|
|
case GLSL_TYPE_DOUBLE:
|
|
|
|
switch (b) {
|
|
|
|
case GLSL_TYPE_INT:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_i2d, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_UINT:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_u2d, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_BOOL:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_f2d,
|
2016-08-02 21:19:28 +01:00
|
|
|
new(ctx) ir_expression(ir_unop_b2f,
|
|
|
|
src));
|
2015-02-05 10:04:58 +00:00
|
|
|
break;
|
|
|
|
case GLSL_TYPE_FLOAT:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_f2d, desired_type, src, NULL);
|
|
|
|
break;
|
2016-06-09 01:01:00 +01:00
|
|
|
case GLSL_TYPE_UINT64:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_u642d, desired_type, src, NULL);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_INT64:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_i642d, desired_type, src, NULL);
|
|
|
|
break;
|
2015-02-05 10:04:58 +00:00
|
|
|
}
|
2016-06-09 01:01:00 +01:00
|
|
|
break;
|
|
|
|
case GLSL_TYPE_UINT64:
|
|
|
|
switch (b) {
|
|
|
|
case GLSL_TYPE_INT:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_i2u64, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_UINT:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_u2u64, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_BOOL:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_i642u64,
|
|
|
|
new(ctx) ir_expression(ir_unop_b2i64,
|
|
|
|
src));
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_FLOAT:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_f2u64, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_DOUBLE:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_d2u64, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_INT64:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_i642u64, src);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_INT64:
|
|
|
|
switch (b) {
|
|
|
|
case GLSL_TYPE_INT:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_i2i64, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_UINT:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_u2i64, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_BOOL:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_b2i64, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_FLOAT:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_f2i64, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_DOUBLE:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_d2i64, src);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_UINT64:
|
|
|
|
result = new(ctx) ir_expression(ir_unop_u642i64, src);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2017-04-21 14:27:15 +01:00
|
|
|
case GLSL_TYPE_SAMPLER:
|
|
|
|
switch (b) {
|
|
|
|
case GLSL_TYPE_UINT:
|
|
|
|
result = new(ctx)
|
|
|
|
ir_expression(ir_unop_pack_sampler_2x32, desired_type, src);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_IMAGE:
|
|
|
|
switch (b) {
|
|
|
|
case GLSL_TYPE_UINT:
|
|
|
|
result = new(ctx)
|
|
|
|
ir_expression(ir_unop_pack_image_2x32, desired_type, src);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2010-03-27 00:38:58 +00:00
|
|
|
}
|
|
|
|
|
2010-06-07 23:08:04 +01:00
|
|
|
assert(result != NULL);
|
2011-06-15 07:23:49 +01:00
|
|
|
assert(result->type == desired_type);
|
2010-06-07 23:08:04 +01:00
|
|
|
|
2010-07-20 07:45:23 +01:00
|
|
|
/* Try constant folding; it may fold in the conversion we just added. */
|
2017-08-10 11:42:29 +01:00
|
|
|
ir_constant *const constant = result->constant_expression_value(ctx);
|
2010-06-07 23:08:04 +01:00
|
|
|
return (constant != NULL) ? (ir_rvalue *) constant : (ir_rvalue *) result;
|
2010-03-27 00:38:58 +00:00
|
|
|
}
|
|
|
|
|
2016-08-02 14:20:59 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Perform automatic type and constant conversion of constructor parameters
|
|
|
|
*
|
|
|
|
* This implements the rules in the "Implicit Conversions" rules, not the
|
|
|
|
* "Conversion and Scalar Constructors".
|
|
|
|
*
|
|
|
|
* After attempting the implicit conversion, an attempt to convert into a
|
|
|
|
* constant valued expression is also done.
|
|
|
|
*
|
|
|
|
* The \c from \c ir_rvalue is converted "in place".
|
|
|
|
*
|
|
|
|
* \param from Operand that is being converted
|
|
|
|
* \param to Base type the operand will be converted to
|
|
|
|
* \param state GLSL compiler state
|
|
|
|
*
|
|
|
|
* \return
|
|
|
|
* If the attempt to convert into a constant expression succeeds, \c true is
|
|
|
|
* returned. Otherwise \c false is returned.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
implicitly_convert_component(ir_rvalue * &from, const glsl_base_type to,
|
|
|
|
struct _mesa_glsl_parse_state *state)
|
|
|
|
{
|
2017-08-10 11:42:29 +01:00
|
|
|
void *mem_ctx = state;
|
2016-08-02 14:20:59 +01:00
|
|
|
ir_rvalue *result = from;
|
|
|
|
|
|
|
|
if (to != from->type->base_type) {
|
|
|
|
const glsl_type *desired_type =
|
|
|
|
glsl_type::get_instance(to,
|
|
|
|
from->type->vector_elements,
|
|
|
|
from->type->matrix_columns);
|
|
|
|
|
|
|
|
if (from->type->can_implicitly_convert_to(desired_type, state)) {
|
|
|
|
/* Even though convert_component() implements the constructor
|
|
|
|
* conversion rules (not the implicit conversion rules), its safe
|
|
|
|
* to use it here because we already checked that the implicit
|
|
|
|
* conversion is legal.
|
|
|
|
*/
|
|
|
|
result = convert_component(from, desired_type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-10 11:42:29 +01:00
|
|
|
ir_rvalue *const constant = result->constant_expression_value(mem_ctx);
|
2016-08-02 14:20:59 +01:00
|
|
|
|
|
|
|
if (constant != NULL)
|
|
|
|
result = constant;
|
|
|
|
|
|
|
|
if (from != result) {
|
|
|
|
from->replace_with(result);
|
|
|
|
from = result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return constant != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-27 00:38:58 +00:00
|
|
|
/**
|
|
|
|
* Dereference a specific component from a scalar, vector, or matrix
|
|
|
|
*/
|
|
|
|
static ir_rvalue *
|
|
|
|
dereference_component(ir_rvalue *src, unsigned component)
|
|
|
|
{
|
2011-01-21 22:32:31 +00:00
|
|
|
void *ctx = ralloc_parent(src);
|
2010-03-27 00:38:58 +00:00
|
|
|
assert(component < src->type->components());
|
|
|
|
|
2010-06-05 00:20:35 +01:00
|
|
|
/* If the source is a constant, just create a new constant instead of a
|
|
|
|
* dereference of the existing constant.
|
|
|
|
*/
|
|
|
|
ir_constant *constant = src->as_constant();
|
|
|
|
if (constant)
|
2010-06-24 02:11:51 +01:00
|
|
|
return new(ctx) ir_constant(constant, component);
|
2010-06-05 00:20:35 +01:00
|
|
|
|
2010-03-27 00:38:58 +00:00
|
|
|
if (src->type->is_scalar()) {
|
|
|
|
return src;
|
|
|
|
} else if (src->type->is_vector()) {
|
2010-06-24 02:11:51 +01:00
|
|
|
return new(ctx) ir_swizzle(src, component, 0, 0, 0, 1);
|
2010-03-27 00:38:58 +00:00
|
|
|
} else {
|
|
|
|
assert(src->type->is_matrix());
|
|
|
|
|
|
|
|
/* Dereference a row of the matrix, then call this function again to get
|
|
|
|
* a specific element from that row.
|
|
|
|
*/
|
|
|
|
const int c = component / src->type->column_type()->vector_elements;
|
|
|
|
const int r = component % src->type->column_type()->vector_elements;
|
2010-06-24 02:11:51 +01:00
|
|
|
ir_constant *const col_index = new(ctx) ir_constant(c);
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_dereference *const col = new(ctx) ir_dereference_array(src,
|
|
|
|
col_index);
|
2010-03-27 00:38:58 +00:00
|
|
|
|
|
|
|
col->type = src->type->column_type();
|
|
|
|
|
|
|
|
return dereference_component(col, r);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(!"Should not get here.");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-29 23:45:46 +01:00
|
|
|
static ir_rvalue *
|
|
|
|
process_vec_mat_constructor(exec_list *instructions,
|
|
|
|
const glsl_type *constructor_type,
|
|
|
|
YYLTYPE *loc, exec_list *parameters,
|
|
|
|
struct _mesa_glsl_parse_state *state)
|
|
|
|
{
|
|
|
|
void *ctx = state;
|
|
|
|
|
|
|
|
/* The ARB_shading_language_420pack spec says:
|
|
|
|
*
|
|
|
|
* "If an initializer is a list of initializers enclosed in curly braces,
|
|
|
|
* the variable being declared must be a vector, a matrix, an array, or a
|
|
|
|
* structure.
|
|
|
|
*
|
|
|
|
* int i = { 1 }; // illegal, i is not an aggregate"
|
|
|
|
*/
|
|
|
|
if (constructor_type->vector_elements <= 1) {
|
2013-07-26 03:56:43 +01:00
|
|
|
_mesa_glsl_error(loc, state, "aggregates can only initialize vectors, "
|
2013-06-29 23:45:46 +01:00
|
|
|
"matrices, arrays, and structs");
|
|
|
|
return ir_rvalue::error_value(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
exec_list actual_parameters;
|
|
|
|
const unsigned parameter_count =
|
|
|
|
process_parameters(instructions, &actual_parameters, parameters, state);
|
|
|
|
|
|
|
|
if (parameter_count == 0
|
|
|
|
|| (constructor_type->is_vector() &&
|
|
|
|
constructor_type->vector_elements != parameter_count)
|
|
|
|
|| (constructor_type->is_matrix() &&
|
|
|
|
constructor_type->matrix_columns != parameter_count)) {
|
|
|
|
_mesa_glsl_error(loc, state, "%s constructor must have %u parameters",
|
|
|
|
constructor_type->is_vector() ? "vector" : "matrix",
|
|
|
|
constructor_type->vector_elements);
|
|
|
|
return ir_rvalue::error_value(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool all_parameters_are_constant = true;
|
|
|
|
|
|
|
|
/* Type cast each parameter and, if possible, fold constants. */
|
2014-06-25 05:58:35 +01:00
|
|
|
foreach_in_list_safe(ir_rvalue, ir, &actual_parameters) {
|
2016-08-02 14:20:59 +01:00
|
|
|
/* Apply implicit conversions (not the scalar constructor rules, see the
|
|
|
|
* spec quote above!) and attempt to convert the parameter to a constant
|
|
|
|
* valued expression. After doing so, track whether or not all the
|
|
|
|
* parameters to the constructor are trivially constant valued
|
|
|
|
* expressions.
|
|
|
|
*/
|
|
|
|
all_parameters_are_constant &=
|
|
|
|
implicitly_convert_component(ir, constructor_type->base_type, state);
|
2013-06-29 23:45:46 +01:00
|
|
|
|
|
|
|
if (constructor_type->is_matrix()) {
|
2016-08-02 14:20:59 +01:00
|
|
|
if (ir->type != constructor_type->column_type()) {
|
2013-06-29 23:45:46 +01:00
|
|
|
_mesa_glsl_error(loc, state, "type error in matrix constructor: "
|
|
|
|
"expected: %s, found %s",
|
|
|
|
constructor_type->column_type()->name,
|
2016-08-02 14:20:59 +01:00
|
|
|
ir->type->name);
|
2013-06-29 23:45:46 +01:00
|
|
|
return ir_rvalue::error_value(ctx);
|
|
|
|
}
|
2016-08-02 14:20:59 +01:00
|
|
|
} else if (ir->type != constructor_type->get_scalar_type()) {
|
2013-06-29 23:45:46 +01:00
|
|
|
_mesa_glsl_error(loc, state, "type error in vector constructor: "
|
|
|
|
"expected: %s, found %s",
|
|
|
|
constructor_type->get_scalar_type()->name,
|
2016-08-02 14:20:59 +01:00
|
|
|
ir->type->name);
|
2013-06-29 23:45:46 +01:00
|
|
|
return ir_rvalue::error_value(ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (all_parameters_are_constant)
|
|
|
|
return new(ctx) ir_constant(constructor_type, &actual_parameters);
|
|
|
|
|
|
|
|
ir_variable *var = new(ctx) ir_variable(constructor_type, "vec_mat_ctor",
|
|
|
|
ir_var_temporary);
|
|
|
|
instructions->push_tail(var);
|
|
|
|
|
|
|
|
int i = 0;
|
2014-07-10 16:55:31 +01:00
|
|
|
|
2014-06-25 05:34:05 +01:00
|
|
|
foreach_in_list(ir_rvalue, rhs, &actual_parameters) {
|
2014-07-10 16:55:31 +01:00
|
|
|
ir_instruction *assignment = NULL;
|
|
|
|
|
|
|
|
if (var->type->is_matrix()) {
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_rvalue *lhs =
|
|
|
|
new(ctx) ir_dereference_array(var, new(ctx) ir_constant(i));
|
2017-09-18 21:30:51 +01:00
|
|
|
assignment = new(ctx) ir_assignment(lhs, rhs);
|
2014-07-10 16:55:31 +01:00
|
|
|
} else {
|
|
|
|
/* use writemask rather than index for vector */
|
|
|
|
assert(var->type->is_vector());
|
|
|
|
assert(i < 4);
|
|
|
|
ir_dereference *lhs = new(ctx) ir_dereference_variable(var);
|
2016-08-02 21:19:28 +01:00
|
|
|
assignment = new(ctx) ir_assignment(lhs, rhs, NULL,
|
|
|
|
(unsigned)(1 << i));
|
2014-07-10 16:55:31 +01:00
|
|
|
}
|
2013-06-29 23:45:46 +01:00
|
|
|
|
|
|
|
instructions->push_tail(assignment);
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new(ctx) ir_dereference_variable(var);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-01 00:48:48 +01:00
|
|
|
static ir_rvalue *
|
|
|
|
process_array_constructor(exec_list *instructions,
|
2016-08-02 21:19:28 +01:00
|
|
|
const glsl_type *constructor_type,
|
|
|
|
YYLTYPE *loc, exec_list *parameters,
|
|
|
|
struct _mesa_glsl_parse_state *state)
|
2010-04-01 00:48:48 +01:00
|
|
|
{
|
2010-06-25 21:14:37 +01:00
|
|
|
void *ctx = state;
|
2010-04-01 00:48:48 +01:00
|
|
|
/* Array constructors come in two forms: sized and unsized. Sized array
|
|
|
|
* constructors look like 'vec4[2](a, b)', where 'a' and 'b' are vec4
|
|
|
|
* variables. In this case the number of parameters must exactly match the
|
|
|
|
* specified size of the array.
|
|
|
|
*
|
|
|
|
* Unsized array constructors look like 'vec4[](a, b)', where 'a' and 'b'
|
|
|
|
* are vec4 variables. In this case the size of the array being constructed
|
|
|
|
* is determined by the number of parameters.
|
|
|
|
*
|
|
|
|
* From page 52 (page 58 of the PDF) of the GLSL 1.50 spec:
|
|
|
|
*
|
|
|
|
* "There must be exactly the same number of arguments as the size of
|
|
|
|
* the array being constructed. If no size is present in the
|
|
|
|
* constructor, then the array is explicitly sized to the number of
|
|
|
|
* arguments provided. The arguments are assigned in order, starting at
|
|
|
|
* element 0, to the elements of the constructed array. Each argument
|
|
|
|
* must be the same type as the element type of the array, or be a type
|
|
|
|
* that can be converted to the element type of the array according to
|
|
|
|
* Section 4.1.10 "Implicit Conversions.""
|
|
|
|
*/
|
|
|
|
exec_list actual_parameters;
|
|
|
|
const unsigned parameter_count =
|
|
|
|
process_parameters(instructions, &actual_parameters, parameters, state);
|
2013-10-23 11:31:27 +01:00
|
|
|
bool is_unsized_array = constructor_type->is_unsized_array();
|
2010-04-01 00:48:48 +01:00
|
|
|
|
2013-10-23 11:31:27 +01:00
|
|
|
if ((parameter_count == 0) ||
|
|
|
|
(!is_unsized_array && (constructor_type->length != parameter_count))) {
|
|
|
|
const unsigned min_param = is_unsized_array
|
|
|
|
? 1 : constructor_type->length;
|
2010-04-01 00:48:48 +01:00
|
|
|
|
|
|
|
_mesa_glsl_error(loc, state, "array constructor must have %s %u "
|
2016-08-02 21:19:28 +01:00
|
|
|
"parameter%s",
|
|
|
|
is_unsized_array ? "at least" : "exactly",
|
|
|
|
min_param, (min_param <= 1) ? "" : "s");
|
2011-09-22 23:04:56 +01:00
|
|
|
return ir_rvalue::error_value(ctx);
|
2010-04-01 00:48:48 +01:00
|
|
|
}
|
|
|
|
|
2013-10-23 11:31:27 +01:00
|
|
|
if (is_unsized_array) {
|
2010-04-01 00:48:48 +01:00
|
|
|
constructor_type =
|
2016-08-02 21:19:28 +01:00
|
|
|
glsl_type::get_array_instance(constructor_type->fields.array,
|
|
|
|
parameter_count);
|
2010-04-01 00:48:48 +01:00
|
|
|
assert(constructor_type != NULL);
|
|
|
|
assert(constructor_type->length == parameter_count);
|
|
|
|
}
|
|
|
|
|
2010-07-20 07:49:58 +01:00
|
|
|
bool all_parameters_are_constant = true;
|
2015-10-15 04:32:41 +01:00
|
|
|
const glsl_type *element_type = constructor_type->fields.array;
|
2010-04-01 00:48:48 +01:00
|
|
|
|
2010-07-20 07:49:58 +01:00
|
|
|
/* Type cast each parameter and, if possible, fold constants. */
|
2014-06-25 05:58:35 +01:00
|
|
|
foreach_in_list_safe(ir_rvalue, ir, &actual_parameters) {
|
2016-08-02 14:20:59 +01:00
|
|
|
/* Apply implicit conversions (not the scalar constructor rules, see the
|
|
|
|
* spec quote above!) and attempt to convert the parameter to a constant
|
|
|
|
* valued expression. After doing so, track whether or not all the
|
|
|
|
* parameters to the constructor are trivially constant valued
|
|
|
|
* expressions.
|
|
|
|
*/
|
|
|
|
all_parameters_are_constant &=
|
|
|
|
implicitly_convert_component(ir, element_type->base_type, state);
|
2010-07-20 07:49:58 +01:00
|
|
|
|
2015-10-15 04:32:41 +01:00
|
|
|
if (constructor_type->fields.array->is_unsized_array()) {
|
|
|
|
/* As the inner parameters of the constructor are created without
|
|
|
|
* knowledge of each other we need to check to make sure unsized
|
|
|
|
* parameters of unsized constructors all end up with the same size.
|
|
|
|
*
|
|
|
|
* e.g we make sure to fail for a constructor like this:
|
|
|
|
* vec4[][] a = vec4[][](vec4[](vec4(0.0), vec4(1.0)),
|
|
|
|
* vec4[](vec4(0.0), vec4(1.0), vec4(1.0)),
|
|
|
|
* vec4[](vec4(0.0), vec4(1.0)));
|
|
|
|
*/
|
|
|
|
if (element_type->is_unsized_array()) {
|
2016-08-02 14:20:59 +01:00
|
|
|
/* This is the first parameter so just get the type */
|
|
|
|
element_type = ir->type;
|
|
|
|
} else if (element_type != ir->type) {
|
2015-10-15 04:32:41 +01:00
|
|
|
_mesa_glsl_error(loc, state, "type error in array constructor: "
|
|
|
|
"expected: %s, found %s",
|
|
|
|
element_type->name,
|
2016-08-02 14:20:59 +01:00
|
|
|
ir->type->name);
|
2015-10-15 04:32:41 +01:00
|
|
|
return ir_rvalue::error_value(ctx);
|
|
|
|
}
|
2016-08-02 14:20:59 +01:00
|
|
|
} else if (ir->type != constructor_type->fields.array) {
|
|
|
|
_mesa_glsl_error(loc, state, "type error in array constructor: "
|
|
|
|
"expected: %s, found %s",
|
|
|
|
constructor_type->fields.array->name,
|
|
|
|
ir->type->name);
|
2013-06-29 23:32:09 +01:00
|
|
|
return ir_rvalue::error_value(ctx);
|
2015-10-15 04:32:41 +01:00
|
|
|
} else {
|
2016-08-02 14:20:59 +01:00
|
|
|
element_type = ir->type;
|
2010-07-20 07:49:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-15 04:32:41 +01:00
|
|
|
if (constructor_type->fields.array->is_unsized_array()) {
|
|
|
|
constructor_type =
|
2016-08-02 21:19:28 +01:00
|
|
|
glsl_type::get_array_instance(element_type,
|
|
|
|
parameter_count);
|
2015-10-15 04:32:41 +01:00
|
|
|
assert(constructor_type != NULL);
|
|
|
|
assert(constructor_type->length == parameter_count);
|
|
|
|
}
|
|
|
|
|
2010-07-20 09:06:33 +01:00
|
|
|
if (all_parameters_are_constant)
|
|
|
|
return new(ctx) ir_constant(constructor_type, &actual_parameters);
|
2010-04-01 00:48:48 +01:00
|
|
|
|
2010-07-20 07:49:58 +01:00
|
|
|
ir_variable *var = new(ctx) ir_variable(constructor_type, "array_ctor",
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_var_temporary);
|
2010-07-20 07:49:58 +01:00
|
|
|
instructions->push_tail(var);
|
|
|
|
|
|
|
|
int i = 0;
|
2014-06-25 05:34:05 +01:00
|
|
|
foreach_in_list(ir_rvalue, rhs, &actual_parameters) {
|
2010-07-20 07:49:58 +01:00
|
|
|
ir_rvalue *lhs = new(ctx) ir_dereference_array(var,
|
2016-08-02 21:19:28 +01:00
|
|
|
new(ctx) ir_constant(i));
|
2010-04-01 00:48:48 +01:00
|
|
|
|
2017-09-18 21:30:51 +01:00
|
|
|
ir_instruction *assignment = new(ctx) ir_assignment(lhs, rhs);
|
2010-07-20 07:49:58 +01:00
|
|
|
instructions->push_tail(assignment);
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
2010-04-01 00:48:48 +01:00
|
|
|
|
2010-07-20 07:49:58 +01:00
|
|
|
return new(ctx) ir_dereference_variable(var);
|
2010-04-01 00:48:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-23 23:19:40 +01:00
|
|
|
/**
|
|
|
|
* Determine if a list consists of a single scalar r-value
|
|
|
|
*/
|
2017-07-09 22:12:31 +01:00
|
|
|
static bool
|
2010-06-23 23:19:40 +01:00
|
|
|
single_scalar_parameter(exec_list *parameters)
|
|
|
|
{
|
2016-06-27 22:42:57 +01:00
|
|
|
const ir_rvalue *const p = (ir_rvalue *) parameters->get_head_raw();
|
2010-06-23 23:19:40 +01:00
|
|
|
assert(((ir_rvalue *)p)->as_rvalue() != NULL);
|
|
|
|
|
2010-07-29 21:52:25 +01:00
|
|
|
return (p->type->is_scalar() && p->next->is_tail_sentinel());
|
2010-06-23 23:19:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate inline code for a vector constructor
|
|
|
|
*
|
|
|
|
* The generated constructor code will consist of a temporary variable
|
|
|
|
* declaration of the same type as the constructor. A sequence of assignments
|
|
|
|
* from constructor parameters to the temporary will follow.
|
|
|
|
*
|
|
|
|
* \return
|
|
|
|
* An \c ir_dereference_variable of the temprorary generated in the constructor
|
|
|
|
* body.
|
|
|
|
*/
|
2017-07-09 22:12:31 +01:00
|
|
|
static ir_rvalue *
|
2010-06-23 23:19:40 +01:00
|
|
|
emit_inline_vector_constructor(const glsl_type *type,
|
2016-08-02 21:19:28 +01:00
|
|
|
exec_list *instructions,
|
|
|
|
exec_list *parameters,
|
|
|
|
void *ctx)
|
2010-06-23 23:19:40 +01:00
|
|
|
{
|
|
|
|
assert(!parameters->is_empty());
|
|
|
|
|
2010-08-03 19:40:26 +01:00
|
|
|
ir_variable *var = new(ctx) ir_variable(type, "vec_ctor", ir_var_temporary);
|
2010-06-23 23:19:40 +01:00
|
|
|
instructions->push_tail(var);
|
|
|
|
|
2015-05-26 13:32:21 +01:00
|
|
|
/* There are three kinds of vector constructors.
|
2010-06-23 23:19:40 +01:00
|
|
|
*
|
|
|
|
* - Construct a vector from a single scalar by replicating that scalar to
|
|
|
|
* all components of the vector.
|
|
|
|
*
|
2015-05-26 13:32:21 +01:00
|
|
|
* - Construct a vector from at least a matrix. This case should already
|
|
|
|
* have been taken care of in ast_function_expression::hir by breaking
|
|
|
|
* down the matrix into a series of column vectors.
|
|
|
|
*
|
2010-06-23 23:19:40 +01:00
|
|
|
* - Construct a vector from an arbirary combination of vectors and
|
|
|
|
* scalars. The components of the constructor parameters are assigned
|
|
|
|
* to the vector in order until the vector is full.
|
|
|
|
*/
|
|
|
|
const unsigned lhs_components = type->components();
|
|
|
|
if (single_scalar_parameter(parameters)) {
|
2016-06-27 22:42:57 +01:00
|
|
|
ir_rvalue *first_param = (ir_rvalue *)parameters->get_head_raw();
|
2010-06-23 23:19:40 +01:00
|
|
|
ir_rvalue *rhs = new(ctx) ir_swizzle(first_param, 0, 0, 0, 0,
|
2016-08-02 21:19:28 +01:00
|
|
|
lhs_components);
|
2010-06-23 23:19:40 +01:00
|
|
|
ir_dereference_variable *lhs = new(ctx) ir_dereference_variable(var);
|
2010-08-04 00:05:54 +01:00
|
|
|
const unsigned mask = (1U << lhs_components) - 1;
|
2010-06-23 23:19:40 +01:00
|
|
|
|
|
|
|
assert(rhs->type == lhs->type);
|
|
|
|
|
2010-08-04 00:05:54 +01:00
|
|
|
ir_instruction *inst = new(ctx) ir_assignment(lhs, rhs, NULL, mask);
|
2010-06-23 23:19:40 +01:00
|
|
|
instructions->push_tail(inst);
|
|
|
|
} else {
|
|
|
|
unsigned base_component = 0;
|
2010-09-22 19:47:03 +01:00
|
|
|
unsigned base_lhs_component = 0;
|
2010-08-31 22:44:13 +01:00
|
|
|
ir_constant_data data;
|
2010-09-22 19:47:03 +01:00
|
|
|
unsigned constant_mask = 0, constant_components = 0;
|
2010-08-31 22:44:13 +01:00
|
|
|
|
|
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
|
2014-06-25 05:34:05 +01:00
|
|
|
foreach_in_list(ir_rvalue, param, parameters) {
|
2016-08-02 21:19:28 +01:00
|
|
|
unsigned rhs_components = param->type->components();
|
|
|
|
|
|
|
|
/* Do not try to assign more components to the vector than it has! */
|
|
|
|
if ((rhs_components + base_lhs_component) > lhs_components) {
|
|
|
|
rhs_components = lhs_components - base_lhs_component;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ir_constant *const c = param->as_constant();
|
|
|
|
if (c != NULL) {
|
|
|
|
for (unsigned i = 0; i < rhs_components; i++) {
|
|
|
|
switch (c->type->base_type) {
|
|
|
|
case GLSL_TYPE_UINT:
|
|
|
|
data.u[i + base_component] = c->get_uint_component(i);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_INT:
|
|
|
|
data.i[i + base_component] = c->get_int_component(i);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_FLOAT:
|
|
|
|
data.f[i + base_component] = c->get_float_component(i);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_DOUBLE:
|
|
|
|
data.d[i + base_component] = c->get_double_component(i);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_BOOL:
|
|
|
|
data.b[i + base_component] = c->get_bool_component(i);
|
|
|
|
break;
|
2016-06-09 00:39:48 +01:00
|
|
|
case GLSL_TYPE_UINT64:
|
|
|
|
data.u64[i + base_component] = c->get_uint64_component(i);
|
|
|
|
break;
|
|
|
|
case GLSL_TYPE_INT64:
|
|
|
|
data.i64[i + base_component] = c->get_int64_component(i);
|
|
|
|
break;
|
2016-08-02 21:19:28 +01:00
|
|
|
default:
|
|
|
|
assert(!"Should not get here.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mask of fields to be written in the assignment. */
|
2016-06-09 00:39:48 +01:00
|
|
|
constant_mask |= ((1U << rhs_components) - 1) << base_lhs_component;
|
2016-08-02 21:19:28 +01:00
|
|
|
constant_components += rhs_components;
|
|
|
|
|
|
|
|
base_component += rhs_components;
|
|
|
|
}
|
|
|
|
/* Advance the component index by the number of components
|
|
|
|
* that were just assigned.
|
|
|
|
*/
|
|
|
|
base_lhs_component += rhs_components;
|
2010-08-31 22:44:13 +01:00
|
|
|
}
|
2010-06-23 23:19:40 +01:00
|
|
|
|
2010-08-31 22:44:13 +01:00
|
|
|
if (constant_mask != 0) {
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_dereference *lhs = new(ctx) ir_dereference_variable(var);
|
|
|
|
const glsl_type *rhs_type =
|
|
|
|
glsl_type::get_instance(var->type->base_type,
|
|
|
|
constant_components,
|
|
|
|
1);
|
|
|
|
ir_rvalue *rhs = new(ctx) ir_constant(rhs_type, &data);
|
|
|
|
|
|
|
|
ir_instruction *inst =
|
|
|
|
new(ctx) ir_assignment(lhs, rhs, NULL, constant_mask);
|
|
|
|
instructions->push_tail(inst);
|
2010-08-31 22:44:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
base_component = 0;
|
2014-06-25 05:34:05 +01:00
|
|
|
foreach_in_list(ir_rvalue, param, parameters) {
|
2016-08-02 21:19:28 +01:00
|
|
|
unsigned rhs_components = param->type->components();
|
|
|
|
|
|
|
|
/* Do not try to assign more components to the vector than it has! */
|
|
|
|
if ((rhs_components + base_component) > lhs_components) {
|
|
|
|
rhs_components = lhs_components - base_component;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we do not have any components left to copy, break out of the
|
|
|
|
* loop. This can happen when initializing a vec4 with a mat3 as the
|
|
|
|
* mat3 would have been broken into a series of column vectors.
|
|
|
|
*/
|
|
|
|
if (rhs_components == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ir_constant *const c = param->as_constant();
|
|
|
|
if (c == NULL) {
|
|
|
|
/* Mask of fields to be written in the assignment. */
|
|
|
|
const unsigned write_mask = ((1U << rhs_components) - 1)
|
|
|
|
<< base_component;
|
|
|
|
|
|
|
|
ir_dereference *lhs = new(ctx) ir_dereference_variable(var);
|
|
|
|
|
|
|
|
/* Generate a swizzle so that LHS and RHS sizes match. */
|
|
|
|
ir_rvalue *rhs =
|
|
|
|
new(ctx) ir_swizzle(param, 0, 1, 2, 3, rhs_components);
|
|
|
|
|
|
|
|
ir_instruction *inst =
|
|
|
|
new(ctx) ir_assignment(lhs, rhs, NULL, write_mask);
|
|
|
|
instructions->push_tail(inst);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Advance the component index by the number of components that were
|
|
|
|
* just assigned.
|
|
|
|
*/
|
|
|
|
base_component += rhs_components;
|
2010-06-23 23:19:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return new(ctx) ir_dereference_variable(var);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-26 00:10:43 +01:00
|
|
|
/**
|
|
|
|
* Generate assignment of a portion of a vector to a portion of a matrix column
|
|
|
|
*
|
|
|
|
* \param src_base First component of the source to be used in assignment
|
|
|
|
* \param column Column of destination to be assiged
|
|
|
|
* \param row_base First component of the destination column to be assigned
|
|
|
|
* \param count Number of components to be assigned
|
|
|
|
*
|
|
|
|
* \note
|
2016-08-02 21:19:28 +01:00
|
|
|
* \c src_base + \c count must be less than or equal to the number of
|
|
|
|
* components in the source vector.
|
2010-06-26 00:10:43 +01:00
|
|
|
*/
|
2017-07-09 22:12:31 +01:00
|
|
|
static ir_instruction *
|
2010-06-26 00:10:43 +01:00
|
|
|
assign_to_matrix_column(ir_variable *var, unsigned column, unsigned row_base,
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_rvalue *src, unsigned src_base, unsigned count,
|
|
|
|
void *mem_ctx)
|
2010-06-26 00:10:43 +01:00
|
|
|
{
|
2010-08-04 04:05:53 +01:00
|
|
|
ir_constant *col_idx = new(mem_ctx) ir_constant(column);
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_dereference *column_ref = new(mem_ctx) ir_dereference_array(var,
|
|
|
|
col_idx);
|
2010-06-26 00:10:43 +01:00
|
|
|
|
|
|
|
assert(column_ref->type->components() >= (row_base + count));
|
|
|
|
assert(src->type->components() >= (src_base + count));
|
|
|
|
|
2010-11-20 01:16:12 +00:00
|
|
|
/* Generate a swizzle that extracts the number of components from the source
|
|
|
|
* that are to be assigned to the column of the matrix.
|
2010-08-04 00:05:54 +01:00
|
|
|
*/
|
2010-11-20 01:16:12 +00:00
|
|
|
if (count < src->type->vector_elements) {
|
|
|
|
src = new(mem_ctx) ir_swizzle(src,
|
2016-08-02 21:19:28 +01:00
|
|
|
src_base + 0, src_base + 1,
|
|
|
|
src_base + 2, src_base + 3,
|
|
|
|
count);
|
2010-11-20 01:16:12 +00:00
|
|
|
}
|
2010-08-04 00:05:54 +01:00
|
|
|
|
2016-08-02 21:19:28 +01:00
|
|
|
/* Mask of fields to be written in the assignment. */
|
2010-08-04 00:05:54 +01:00
|
|
|
const unsigned write_mask = ((1U << count) - 1) << row_base;
|
|
|
|
|
2010-11-20 01:16:12 +00:00
|
|
|
return new(mem_ctx) ir_assignment(column_ref, src, NULL, write_mask);
|
2010-06-26 00:10:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate inline code for a matrix constructor
|
|
|
|
*
|
|
|
|
* The generated constructor code will consist of a temporary variable
|
|
|
|
* declaration of the same type as the constructor. A sequence of assignments
|
|
|
|
* from constructor parameters to the temporary will follow.
|
|
|
|
*
|
|
|
|
* \return
|
|
|
|
* An \c ir_dereference_variable of the temprorary generated in the constructor
|
|
|
|
* body.
|
|
|
|
*/
|
2017-07-09 22:12:31 +01:00
|
|
|
static ir_rvalue *
|
2010-06-26 00:10:43 +01:00
|
|
|
emit_inline_matrix_constructor(const glsl_type *type,
|
2016-08-02 21:19:28 +01:00
|
|
|
exec_list *instructions,
|
|
|
|
exec_list *parameters,
|
|
|
|
void *ctx)
|
2010-06-26 00:10:43 +01:00
|
|
|
{
|
|
|
|
assert(!parameters->is_empty());
|
|
|
|
|
2010-08-03 19:40:26 +01:00
|
|
|
ir_variable *var = new(ctx) ir_variable(type, "mat_ctor", ir_var_temporary);
|
2010-06-26 00:10:43 +01:00
|
|
|
instructions->push_tail(var);
|
|
|
|
|
|
|
|
/* There are three kinds of matrix constructors.
|
|
|
|
*
|
|
|
|
* - Construct a matrix from a single scalar by replicating that scalar to
|
|
|
|
* along the diagonal of the matrix and setting all other components to
|
|
|
|
* zero.
|
|
|
|
*
|
|
|
|
* - Construct a matrix from an arbirary combination of vectors and
|
|
|
|
* scalars. The components of the constructor parameters are assigned
|
2015-04-22 11:33:17 +01:00
|
|
|
* to the matrix in column-major order until the matrix is full.
|
2010-06-26 00:10:43 +01:00
|
|
|
*
|
|
|
|
* - Construct a matrix from a single matrix. The source matrix is copied
|
|
|
|
* to the upper left portion of the constructed matrix, and the remaining
|
|
|
|
* elements take values from the identity matrix.
|
|
|
|
*/
|
2016-06-27 22:42:57 +01:00
|
|
|
ir_rvalue *const first_param = (ir_rvalue *) parameters->get_head_raw();
|
2010-06-26 00:10:43 +01:00
|
|
|
if (single_scalar_parameter(parameters)) {
|
|
|
|
/* Assign the scalar to the X component of a vec4, and fill the remaining
|
|
|
|
* components with zero.
|
|
|
|
*/
|
2015-02-05 10:04:58 +00:00
|
|
|
glsl_base_type param_base_type = first_param->type->base_type;
|
2017-04-21 10:18:50 +01:00
|
|
|
assert(first_param->type->is_float() || first_param->type->is_double());
|
2010-06-28 21:22:55 +01:00
|
|
|
ir_variable *rhs_var =
|
2015-02-05 10:04:58 +00:00
|
|
|
new(ctx) ir_variable(glsl_type::get_instance(param_base_type, 4, 1),
|
|
|
|
"mat_ctor_vec",
|
|
|
|
ir_var_temporary);
|
2010-06-26 00:10:43 +01:00
|
|
|
instructions->push_tail(rhs_var);
|
|
|
|
|
|
|
|
ir_constant_data zero;
|
2015-02-05 10:04:58 +00:00
|
|
|
for (unsigned i = 0; i < 4; i++)
|
2017-04-21 10:18:50 +01:00
|
|
|
if (first_param->type->is_float())
|
2015-02-05 10:04:58 +00:00
|
|
|
zero.f[i] = 0.0;
|
|
|
|
else
|
|
|
|
zero.d[i] = 0.0;
|
2010-06-26 00:10:43 +01:00
|
|
|
|
|
|
|
ir_instruction *inst =
|
2016-02-19 08:14:19 +00:00
|
|
|
new(ctx) ir_assignment(new(ctx) ir_dereference_variable(rhs_var),
|
2017-09-18 21:30:51 +01:00
|
|
|
new(ctx) ir_constant(rhs_var->type, &zero));
|
2010-06-26 00:10:43 +01:00
|
|
|
instructions->push_tail(inst);
|
|
|
|
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_dereference *const rhs_ref =
|
|
|
|
new(ctx) ir_dereference_variable(rhs_var);
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2010-08-04 00:05:54 +01:00
|
|
|
inst = new(ctx) ir_assignment(rhs_ref, first_param, NULL, 0x01);
|
2010-06-26 00:10:43 +01:00
|
|
|
instructions->push_tail(inst);
|
|
|
|
|
|
|
|
/* Assign the temporary vector to each column of the destination matrix
|
|
|
|
* with a swizzle that puts the X component on the diagonal of the
|
|
|
|
* matrix. In some cases this may mean that the X component does not
|
|
|
|
* get assigned into the column at all (i.e., when the matrix has more
|
|
|
|
* columns than rows).
|
|
|
|
*/
|
|
|
|
static const unsigned rhs_swiz[4][4] = {
|
2016-02-19 08:14:19 +00:00
|
|
|
{ 0, 1, 1, 1 },
|
|
|
|
{ 1, 0, 1, 1 },
|
|
|
|
{ 1, 1, 0, 1 },
|
|
|
|
{ 1, 1, 1, 0 }
|
2010-06-26 00:10:43 +01:00
|
|
|
};
|
|
|
|
|
2010-08-13 02:00:35 +01:00
|
|
|
const unsigned cols_to_init = MIN2(type->matrix_columns,
|
2016-02-19 08:14:19 +00:00
|
|
|
type->vector_elements);
|
2010-06-26 00:10:43 +01:00
|
|
|
for (unsigned i = 0; i < cols_to_init; i++) {
|
2016-02-19 08:14:19 +00:00
|
|
|
ir_constant *const col_idx = new(ctx) ir_constant(i);
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_rvalue *const col_ref = new(ctx) ir_dereference_array(var,
|
|
|
|
col_idx);
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2016-02-19 08:14:19 +00:00
|
|
|
ir_rvalue *const rhs_ref = new(ctx) ir_dereference_variable(rhs_var);
|
|
|
|
ir_rvalue *const rhs = new(ctx) ir_swizzle(rhs_ref, rhs_swiz[i],
|
|
|
|
type->vector_elements);
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2017-09-18 21:30:51 +01:00
|
|
|
inst = new(ctx) ir_assignment(col_ref, rhs);
|
2016-02-19 08:14:19 +00:00
|
|
|
instructions->push_tail(inst);
|
2010-06-26 00:10:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned i = cols_to_init; i < type->matrix_columns; i++) {
|
2016-02-19 08:14:19 +00:00
|
|
|
ir_constant *const col_idx = new(ctx) ir_constant(i);
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_rvalue *const col_ref = new(ctx) ir_dereference_array(var,
|
|
|
|
col_idx);
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2016-02-19 08:14:19 +00:00
|
|
|
ir_rvalue *const rhs_ref = new(ctx) ir_dereference_variable(rhs_var);
|
|
|
|
ir_rvalue *const rhs = new(ctx) ir_swizzle(rhs_ref, 1, 1, 1, 1,
|
|
|
|
type->vector_elements);
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2017-09-18 21:30:51 +01:00
|
|
|
inst = new(ctx) ir_assignment(col_ref, rhs);
|
2016-02-19 08:14:19 +00:00
|
|
|
instructions->push_tail(inst);
|
2010-06-26 00:10:43 +01:00
|
|
|
}
|
|
|
|
} else if (first_param->type->is_matrix()) {
|
|
|
|
/* From page 50 (56 of the PDF) of the GLSL 1.50 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. All other components will be initialized to the
|
|
|
|
* identity matrix. If a matrix argument is given to a matrix
|
|
|
|
* constructor, it is an error to have any other arguments."
|
|
|
|
*/
|
2010-07-29 21:52:25 +01:00
|
|
|
assert(first_param->next->is_tail_sentinel());
|
2010-06-26 00:10:43 +01:00
|
|
|
ir_rvalue *const src_matrix = first_param;
|
|
|
|
|
|
|
|
/* If the source matrix is smaller, pre-initialize the relavent parts of
|
|
|
|
* the destination matrix to the identity matrix.
|
|
|
|
*/
|
2016-02-19 08:14:19 +00:00
|
|
|
if ((src_matrix->type->matrix_columns < var->type->matrix_columns) ||
|
|
|
|
(src_matrix->type->vector_elements < var->type->vector_elements)) {
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2016-08-02 21:19:28 +01:00
|
|
|
/* If the source matrix has fewer rows, every column of the
|
|
|
|
* destination must be initialized. Otherwise only the columns in
|
|
|
|
* the destination that do not exist in the source must be
|
|
|
|
* initialized.
|
2016-02-19 08:14:19 +00:00
|
|
|
*/
|
|
|
|
unsigned col =
|
|
|
|
(src_matrix->type->vector_elements < var->type->vector_elements)
|
|
|
|
? 0 : src_matrix->type->matrix_columns;
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2016-02-19 08:14:19 +00:00
|
|
|
const glsl_type *const col_type = var->type->column_type();
|
|
|
|
for (/* empty */; col < var->type->matrix_columns; col++) {
|
|
|
|
ir_constant_data ident;
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2016-02-19 08:16:14 +00:00
|
|
|
if (!col_type->is_double()) {
|
|
|
|
ident.f[0] = 0.0f;
|
|
|
|
ident.f[1] = 0.0f;
|
|
|
|
ident.f[2] = 0.0f;
|
|
|
|
ident.f[3] = 0.0f;
|
|
|
|
ident.f[col] = 1.0f;
|
|
|
|
} else {
|
|
|
|
ident.d[0] = 0.0;
|
|
|
|
ident.d[1] = 0.0;
|
|
|
|
ident.d[2] = 0.0;
|
|
|
|
ident.d[3] = 0.0;
|
|
|
|
ident.d[col] = 1.0;
|
|
|
|
}
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2016-02-19 08:14:19 +00:00
|
|
|
ir_rvalue *const rhs = new(ctx) ir_constant(col_type, &ident);
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2016-02-19 08:14:19 +00:00
|
|
|
ir_rvalue *const lhs =
|
|
|
|
new(ctx) ir_dereference_array(var, new(ctx) ir_constant(col));
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2017-09-18 21:30:51 +01:00
|
|
|
ir_instruction *inst = new(ctx) ir_assignment(lhs, rhs);
|
2016-02-19 08:14:19 +00:00
|
|
|
instructions->push_tail(inst);
|
|
|
|
}
|
2010-06-26 00:10:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Assign columns from the source matrix to the destination matrix.
|
|
|
|
*
|
|
|
|
* Since the parameter will be used in the RHS of multiple assignments,
|
|
|
|
* generate a temporary and copy the paramter there.
|
|
|
|
*/
|
2010-06-28 21:22:55 +01:00
|
|
|
ir_variable *const rhs_var =
|
2016-02-19 08:14:19 +00:00
|
|
|
new(ctx) ir_variable(first_param->type, "mat_ctor_mat",
|
|
|
|
ir_var_temporary);
|
2010-06-26 00:10:43 +01:00
|
|
|
instructions->push_tail(rhs_var);
|
|
|
|
|
|
|
|
ir_dereference *const rhs_var_ref =
|
2016-02-19 08:14:19 +00:00
|
|
|
new(ctx) ir_dereference_variable(rhs_var);
|
2010-06-26 00:10:43 +01:00
|
|
|
ir_instruction *const inst =
|
2017-09-18 21:30:51 +01:00
|
|
|
new(ctx) ir_assignment(rhs_var_ref, first_param);
|
2010-06-26 00:10:43 +01:00
|
|
|
instructions->push_tail(inst);
|
|
|
|
|
2010-09-02 00:10:01 +01:00
|
|
|
const unsigned last_row = MIN2(src_matrix->type->vector_elements,
|
2016-02-19 08:14:19 +00:00
|
|
|
var->type->vector_elements);
|
2010-09-02 00:10:01 +01:00
|
|
|
const unsigned last_col = MIN2(src_matrix->type->matrix_columns,
|
2016-02-19 08:14:19 +00:00
|
|
|
var->type->matrix_columns);
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2010-08-04 00:05:54 +01:00
|
|
|
unsigned swiz[4] = { 0, 0, 0, 0 };
|
2010-09-22 19:47:03 +01:00
|
|
|
for (unsigned i = 1; i < last_row; i++)
|
2016-02-19 08:14:19 +00:00
|
|
|
swiz[i] = i;
|
2010-08-04 00:05:54 +01:00
|
|
|
|
2016-05-24 00:31:30 +01:00
|
|
|
const unsigned write_mask = (1U << last_row) - 1;
|
2010-08-04 00:05:54 +01:00
|
|
|
|
2010-06-26 00:10:43 +01:00
|
|
|
for (unsigned i = 0; i < last_col; i++) {
|
2016-02-19 08:14:19 +00:00
|
|
|
ir_dereference *const lhs =
|
|
|
|
new(ctx) ir_dereference_array(var, new(ctx) ir_constant(i));
|
|
|
|
ir_rvalue *const rhs_col =
|
|
|
|
new(ctx) ir_dereference_array(rhs_var, new(ctx) ir_constant(i));
|
|
|
|
|
|
|
|
/* If one matrix has columns that are smaller than the columns of the
|
|
|
|
* other matrix, wrap the column access of the larger with a swizzle
|
|
|
|
* so that the LHS and RHS of the assignment have the same size (and
|
|
|
|
* therefore have the same type).
|
|
|
|
*
|
|
|
|
* It would be perfectly valid to unconditionally generate the
|
2016-08-02 21:19:28 +01:00
|
|
|
* swizzles, this this will typically result in a more compact IR
|
|
|
|
* tree.
|
2016-02-19 08:14:19 +00:00
|
|
|
*/
|
|
|
|
ir_rvalue *rhs;
|
|
|
|
if (lhs->type->vector_elements != rhs_col->type->vector_elements) {
|
|
|
|
rhs = new(ctx) ir_swizzle(rhs_col, swiz, last_row);
|
|
|
|
} else {
|
|
|
|
rhs = rhs_col;
|
|
|
|
}
|
2010-06-26 00:10:43 +01:00
|
|
|
|
2016-02-19 08:14:19 +00:00
|
|
|
ir_instruction *inst =
|
|
|
|
new(ctx) ir_assignment(lhs, rhs, NULL, write_mask);
|
|
|
|
instructions->push_tail(inst);
|
2010-06-26 00:10:43 +01:00
|
|
|
}
|
|
|
|
} else {
|
2010-08-17 23:57:48 +01:00
|
|
|
const unsigned cols = type->matrix_columns;
|
|
|
|
const unsigned rows = type->vector_elements;
|
2014-11-27 08:39:05 +00:00
|
|
|
unsigned remaining_slots = rows * cols;
|
2010-06-26 00:10:43 +01:00
|
|
|
unsigned col_idx = 0;
|
|
|
|
unsigned row_idx = 0;
|
|
|
|
|
2014-06-25 05:34:05 +01:00
|
|
|
foreach_in_list(ir_rvalue, rhs, parameters) {
|
2014-11-27 08:39:05 +00:00
|
|
|
unsigned rhs_components = rhs->type->components();
|
|
|
|
unsigned rhs_base = 0;
|
|
|
|
|
|
|
|
if (remaining_slots == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Since the parameter might be used in the RHS of two assignments,
|
|
|
|
* generate a temporary and copy the paramter there.
|
|
|
|
*/
|
|
|
|
ir_variable *rhs_var =
|
|
|
|
new(ctx) ir_variable(rhs->type, "mat_ctor_vec", ir_var_temporary);
|
|
|
|
instructions->push_tail(rhs_var);
|
|
|
|
|
|
|
|
ir_dereference *rhs_var_ref =
|
|
|
|
new(ctx) ir_dereference_variable(rhs_var);
|
2017-09-18 21:30:51 +01:00
|
|
|
ir_instruction *inst = new(ctx) ir_assignment(rhs_var_ref, rhs);
|
2014-11-27 08:39:05 +00:00
|
|
|
instructions->push_tail(inst);
|
|
|
|
|
|
|
|
do {
|
|
|
|
/* Assign the current parameter to as many components of the matrix
|
|
|
|
* as it will fill.
|
|
|
|
*
|
|
|
|
* NOTE: A single vector parameter can span two matrix columns. A
|
|
|
|
* single vec4, for example, can completely fill a mat2.
|
|
|
|
*/
|
|
|
|
unsigned count = MIN2(rows - row_idx,
|
|
|
|
rhs_components - rhs_base);
|
|
|
|
|
|
|
|
rhs_var_ref = new(ctx) ir_dereference_variable(rhs_var);
|
|
|
|
ir_instruction *inst = assign_to_matrix_column(var, col_idx,
|
2016-08-02 21:19:28 +01:00
|
|
|
row_idx,
|
|
|
|
rhs_var_ref,
|
|
|
|
rhs_base,
|
|
|
|
count, ctx);
|
2014-11-27 08:39:05 +00:00
|
|
|
instructions->push_tail(inst);
|
|
|
|
rhs_base += count;
|
|
|
|
row_idx += count;
|
|
|
|
remaining_slots -= count;
|
|
|
|
|
|
|
|
/* Sometimes, there is still data left in the parameters and
|
|
|
|
* components left to be set in the destination but in other
|
|
|
|
* column.
|
|
|
|
*/
|
|
|
|
if (row_idx >= rows) {
|
|
|
|
row_idx = 0;
|
|
|
|
col_idx++;
|
|
|
|
}
|
|
|
|
} while(remaining_slots > 0 && rhs_base < rhs_components);
|
2010-06-26 00:10:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return new(ctx) ir_dereference_variable(var);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-09 22:12:31 +01:00
|
|
|
static ir_rvalue *
|
2010-09-01 21:46:04 +01:00
|
|
|
emit_inline_record_constructor(const glsl_type *type,
|
2016-08-02 21:19:28 +01:00
|
|
|
exec_list *instructions,
|
|
|
|
exec_list *parameters,
|
|
|
|
void *mem_ctx)
|
2010-09-01 21:46:04 +01:00
|
|
|
{
|
|
|
|
ir_variable *const var =
|
|
|
|
new(mem_ctx) ir_variable(type, "record_ctor", ir_var_temporary);
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_dereference_variable *const d =
|
|
|
|
new(mem_ctx) ir_dereference_variable(var);
|
2010-09-01 21:46:04 +01:00
|
|
|
|
|
|
|
instructions->push_tail(var);
|
|
|
|
|
2016-06-27 22:42:57 +01:00
|
|
|
exec_node *node = parameters->get_head_raw();
|
2010-09-01 21:46:04 +01:00
|
|
|
for (unsigned i = 0; i < type->length; i++) {
|
|
|
|
assert(!node->is_tail_sentinel());
|
|
|
|
|
|
|
|
ir_dereference *const lhs =
|
2016-08-02 21:19:28 +01:00
|
|
|
new(mem_ctx) ir_dereference_record(d->clone(mem_ctx, NULL),
|
|
|
|
type->fields.structure[i].name);
|
2010-09-01 21:46:04 +01:00
|
|
|
|
|
|
|
ir_rvalue *const rhs = ((ir_instruction *) node)->as_rvalue();
|
|
|
|
assert(rhs != NULL);
|
|
|
|
|
2017-09-18 21:30:51 +01:00
|
|
|
ir_instruction *const assign = new(mem_ctx) ir_assignment(lhs, rhs);
|
2010-09-01 21:46:04 +01:00
|
|
|
|
|
|
|
instructions->push_tail(assign);
|
|
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-13 20:05:21 +01:00
|
|
|
static ir_rvalue *
|
|
|
|
process_record_constructor(exec_list *instructions,
|
|
|
|
const glsl_type *constructor_type,
|
|
|
|
YYLTYPE *loc, exec_list *parameters,
|
|
|
|
struct _mesa_glsl_parse_state *state)
|
|
|
|
{
|
|
|
|
void *ctx = state;
|
2016-07-30 15:57:54 +01:00
|
|
|
/* From page 32 (page 38 of the PDF) of the GLSL 1.20 spec:
|
|
|
|
*
|
|
|
|
* "The arguments to the constructor will be used to set the structure's
|
|
|
|
* fields, in order, using one argument per field. Each argument must
|
|
|
|
* be the same type as the field it sets, or be a type that can be
|
|
|
|
* converted to the field's type according to Section 4.1.10 “Implicit
|
|
|
|
* Conversions.”"
|
|
|
|
*
|
|
|
|
* From page 35 (page 41 of the PDF) of the GLSL 4.20 spec:
|
|
|
|
*
|
|
|
|
* "In all cases, the innermost initializer (i.e., not a list of
|
|
|
|
* initializers enclosed in curly braces) applied to an object must
|
|
|
|
* have the same type as the object being initialized or be a type that
|
|
|
|
* can be converted to the object's type according to section 4.1.10
|
|
|
|
* "Implicit Conversions". In the latter case, an implicit conversion
|
|
|
|
* will be done on the initializer before the assignment is done."
|
|
|
|
*/
|
2013-06-13 20:05:21 +01:00
|
|
|
exec_list actual_parameters;
|
|
|
|
|
2016-07-30 15:57:54 +01:00
|
|
|
const unsigned parameter_count =
|
|
|
|
process_parameters(instructions, &actual_parameters, parameters,
|
|
|
|
state);
|
2013-06-13 20:05:21 +01:00
|
|
|
|
2016-07-30 15:57:54 +01:00
|
|
|
if (parameter_count != constructor_type->length) {
|
|
|
|
_mesa_glsl_error(loc, state,
|
|
|
|
"%s parameters in constructor for `%s'",
|
|
|
|
parameter_count > constructor_type->length
|
|
|
|
? "too many": "insufficient",
|
|
|
|
constructor_type->name);
|
|
|
|
return ir_rvalue::error_value(ctx);
|
|
|
|
}
|
2013-06-13 20:05:21 +01:00
|
|
|
|
2016-07-30 15:57:54 +01:00
|
|
|
bool all_parameters_are_constant = true;
|
2013-06-13 20:05:21 +01:00
|
|
|
|
2016-07-30 15:57:54 +01:00
|
|
|
int i = 0;
|
|
|
|
/* Type cast each parameter and, if possible, fold constants. */
|
|
|
|
foreach_in_list_safe(ir_rvalue, ir, &actual_parameters) {
|
|
|
|
|
|
|
|
const glsl_struct_field *struct_field =
|
|
|
|
&constructor_type->fields.structure[i];
|
|
|
|
|
|
|
|
/* Apply implicit conversions (not the scalar constructor rules, see the
|
|
|
|
* spec quote above!) and attempt to convert the parameter to a constant
|
|
|
|
* valued expression. After doing so, track whether or not all the
|
|
|
|
* parameters to the constructor are trivially constant valued
|
|
|
|
* expressions.
|
|
|
|
*/
|
|
|
|
all_parameters_are_constant &=
|
|
|
|
implicitly_convert_component(ir, struct_field->type->base_type,
|
|
|
|
state);
|
|
|
|
|
|
|
|
if (ir->type != struct_field->type) {
|
2013-06-13 20:05:21 +01:00
|
|
|
_mesa_glsl_error(loc, state,
|
|
|
|
"parameter type mismatch in constructor for `%s.%s' "
|
|
|
|
"(%s vs %s)",
|
|
|
|
constructor_type->name,
|
2016-07-30 15:57:54 +01:00
|
|
|
struct_field->name,
|
2013-06-13 20:05:21 +01:00
|
|
|
ir->type->name,
|
2016-07-30 15:57:54 +01:00
|
|
|
struct_field->type->name);
|
2016-04-13 17:43:09 +01:00
|
|
|
return ir_rvalue::error_value(ctx);
|
2013-06-13 20:05:21 +01:00
|
|
|
}
|
|
|
|
|
2016-07-30 15:57:54 +01:00
|
|
|
i++;
|
2013-06-13 20:05:21 +01:00
|
|
|
}
|
|
|
|
|
2016-07-30 15:57:54 +01:00
|
|
|
if (all_parameters_are_constant) {
|
|
|
|
return new(ctx) ir_constant(constructor_type, &actual_parameters);
|
|
|
|
} else {
|
|
|
|
return emit_inline_record_constructor(constructor_type, instructions,
|
|
|
|
&actual_parameters, state);
|
2013-06-13 20:05:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-01 01:55:47 +01:00
|
|
|
ir_rvalue *
|
|
|
|
ast_function_expression::handle_method(exec_list *instructions,
|
|
|
|
struct _mesa_glsl_parse_state *state)
|
|
|
|
{
|
|
|
|
const ast_expression *field = subexpressions[0];
|
|
|
|
ir_rvalue *op;
|
|
|
|
ir_rvalue *result;
|
|
|
|
void *ctx = state;
|
|
|
|
/* Handle "method calls" in GLSL 1.20 - namely, array.length() */
|
|
|
|
YYLTYPE loc = get_location();
|
|
|
|
state->check_version(120, 300, &loc, "methods not supported");
|
|
|
|
|
|
|
|
const char *method;
|
|
|
|
method = field->primary_expression.identifier;
|
|
|
|
|
glsl: add is_lhs bool on ast_expression
Useful to know if a expression is the recipient of an assignment
or not, that would be used to (for example) raise warnings of
"use of uninitialized variable" without getting a false positive
when assigning first a variable.
By default the value is false, and it is assigned to true on
the following cases:
* The lhs assignments subexpression
* At ast_array_index, on the array itself.
* While handling the method on an array, to avoid the warning
calling array.length
* When computed the cached test expression at test_to_hir, to
avoid a duplicate warning on the test expression of a switch.
set_is_lhs setter is added, because in some cases (like ast_field_selection)
the value need to be propagated on the expression tree. To avoid doing the
propatagion if not needed, it skips if no primary_expression.identifier is
available.
v2: use a new bool on ast_expression, instead of a new parameter
on ast_expression::hir (Timothy Arceri)
v3: fix style and some typos on comments, initialize is_lhs default value
on constructor, to avoid a c++11 feature (Ian Romanick)
v4: some tweaks on comments (Timothy Arceri)
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=94129
Reviewed-by: Timothy Arceri <timothy.arceri@collabora.com>
2016-02-25 10:11:54 +00:00
|
|
|
/* This would prevent to raise "uninitialized variable" warnings when
|
|
|
|
* calling array.length.
|
|
|
|
*/
|
|
|
|
field->subexpressions[0]->set_is_lhs(true);
|
2015-06-01 01:55:47 +01:00
|
|
|
op = field->subexpressions[0]->hir(instructions, state);
|
|
|
|
if (strcmp(method, "length") == 0) {
|
|
|
|
if (!this->expressions.is_empty()) {
|
|
|
|
_mesa_glsl_error(&loc, state, "length method takes no arguments");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op->type->is_array()) {
|
|
|
|
if (op->type->is_unsized_array()) {
|
2015-04-13 15:17:07 +01:00
|
|
|
if (!state->has_shader_storage_buffer_objects()) {
|
2016-08-02 21:19:28 +01:00
|
|
|
_mesa_glsl_error(&loc, state,
|
|
|
|
"length called on unsized array"
|
|
|
|
" only available with"
|
|
|
|
" ARB_shader_storage_buffer_object");
|
2021-07-14 18:52:56 +01:00
|
|
|
goto fail;
|
|
|
|
} else if (op->variable_referenced()->is_in_shader_storage_block()) {
|
|
|
|
/* Calculate length of an unsized array in run-time */
|
|
|
|
result = new(ctx)
|
|
|
|
ir_expression(ir_unop_ssbo_unsized_array_length, op);
|
|
|
|
} else {
|
|
|
|
/* When actual size is known at link-time, this will be
|
|
|
|
* replaced with a constant expression.
|
|
|
|
*/
|
|
|
|
result = new (ctx)
|
|
|
|
ir_expression(ir_unop_implicitly_sized_array_length, op);
|
2015-04-13 15:17:07 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
result = new(ctx) ir_constant(op->type->array_size());
|
2015-06-01 01:55:47 +01:00
|
|
|
}
|
|
|
|
} else if (op->type->is_vector()) {
|
2015-12-07 22:11:01 +00:00
|
|
|
if (state->has_420pack()) {
|
2015-06-01 01:55:47 +01:00
|
|
|
/* .length() returns int. */
|
|
|
|
result = new(ctx) ir_constant((int) op->type->vector_elements);
|
|
|
|
} else {
|
2016-08-02 21:19:28 +01:00
|
|
|
_mesa_glsl_error(&loc, state, "length method on matrix only"
|
|
|
|
" available with ARB_shading_language_420pack");
|
2015-06-01 01:55:47 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else if (op->type->is_matrix()) {
|
2015-12-07 22:11:01 +00:00
|
|
|
if (state->has_420pack()) {
|
2015-06-01 01:55:47 +01:00
|
|
|
/* .length() returns int. */
|
|
|
|
result = new(ctx) ir_constant((int) op->type->matrix_columns);
|
|
|
|
} else {
|
2016-08-02 21:19:28 +01:00
|
|
|
_mesa_glsl_error(&loc, state, "length method on matrix only"
|
|
|
|
" available with ARB_shading_language_420pack");
|
2015-06-01 01:55:47 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_mesa_glsl_error(&loc, state, "length called on scalar.");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else {
|
2016-08-02 21:19:28 +01:00
|
|
|
_mesa_glsl_error(&loc, state, "unknown method: `%s'", method);
|
|
|
|
goto fail;
|
2015-06-01 01:55:47 +01:00
|
|
|
}
|
|
|
|
return result;
|
2016-08-02 21:19:28 +01:00
|
|
|
fail:
|
2015-06-01 01:55:47 +01:00
|
|
|
return ir_rvalue::error_value(ctx);
|
|
|
|
}
|
2013-06-13 20:05:21 +01:00
|
|
|
|
2017-04-21 15:31:52 +01:00
|
|
|
static inline bool is_valid_constructor(const glsl_type *type,
|
|
|
|
struct _mesa_glsl_parse_state *state)
|
|
|
|
{
|
2017-04-21 14:27:15 +01:00
|
|
|
return type->is_numeric() || type->is_boolean() ||
|
|
|
|
(state->has_bindless() && (type->is_sampler() || type->is_image()));
|
2017-04-21 15:31:52 +01:00
|
|
|
}
|
|
|
|
|
2010-03-26 07:25:36 +00:00
|
|
|
ir_rvalue *
|
2010-03-15 20:04:13 +00:00
|
|
|
ast_function_expression::hir(exec_list *instructions,
|
2016-08-02 21:19:28 +01:00
|
|
|
struct _mesa_glsl_parse_state *state)
|
2010-03-15 20:04:13 +00:00
|
|
|
{
|
2010-06-25 21:14:37 +01:00
|
|
|
void *ctx = state;
|
2010-03-15 20:04:13 +00:00
|
|
|
/* There are three sorts of function calls.
|
|
|
|
*
|
2010-07-09 02:03:28 +01:00
|
|
|
* 1. constructors - The first subexpression is an ast_type_specifier.
|
2010-03-15 20:04:13 +00:00
|
|
|
* 2. methods - Only the .length() method of array types.
|
|
|
|
* 3. functions - Calls to regular old functions.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
if (is_constructor()) {
|
2016-08-02 21:19:28 +01:00
|
|
|
const ast_type_specifier *type =
|
|
|
|
(ast_type_specifier *) subexpressions[0];
|
2010-03-23 22:08:30 +00:00
|
|
|
YYLTYPE loc = type->get_location();
|
2010-04-01 00:22:56 +01:00
|
|
|
const char *name;
|
2010-03-23 22:08:30 +00:00
|
|
|
|
2010-04-01 00:22:56 +01:00
|
|
|
const glsl_type *const constructor_type = type->glsl_type(& name, state);
|
2010-03-23 22:08:30 +00:00
|
|
|
|
2011-01-28 01:52:19 +00:00
|
|
|
/* constructor_type can be NULL if a variable with the same name as the
|
|
|
|
* structure has come into scope.
|
|
|
|
*/
|
|
|
|
if (constructor_type == NULL) {
|
2016-08-02 21:19:28 +01:00
|
|
|
_mesa_glsl_error(& loc, state, "unknown type `%s' (structure name "
|
|
|
|
"may be shadowed by a variable with the same name)",
|
|
|
|
type->type_name);
|
|
|
|
return ir_rvalue::error_value(ctx);
|
2011-01-28 01:52:19 +00:00
|
|
|
}
|
|
|
|
|
2010-03-23 22:08:30 +00:00
|
|
|
|
2014-12-04 08:40:56 +00:00
|
|
|
/* Constructors for opaque types are illegal.
|
2017-04-21 17:45:42 +01:00
|
|
|
*
|
|
|
|
* From section 4.1.7 of the ARB_bindless_texture spec:
|
|
|
|
*
|
|
|
|
* "Samplers are represented using 64-bit integer handles, and may be "
|
|
|
|
* converted to and from 64-bit integers using constructors."
|
|
|
|
*
|
|
|
|
* From section 4.1.X of the ARB_bindless_texture spec:
|
|
|
|
*
|
|
|
|
* "Images are represented using 64-bit integer handles, and may be
|
|
|
|
* converted to and from 64-bit integers using constructors."
|
2010-03-23 22:08:30 +00:00
|
|
|
*/
|
2017-04-21 17:45:42 +01:00
|
|
|
if (constructor_type->contains_atomic() ||
|
|
|
|
(!state->has_bindless() && constructor_type->contains_opaque())) {
|
|
|
|
_mesa_glsl_error(& loc, state, "cannot construct %s type `%s'",
|
|
|
|
state->has_bindless() ? "atomic" : "opaque",
|
2016-08-02 21:19:28 +01:00
|
|
|
constructor_type->name);
|
|
|
|
return ir_rvalue::error_value(ctx);
|
2010-03-23 22:08:30 +00:00
|
|
|
}
|
|
|
|
|
2016-05-03 05:33:16 +01:00
|
|
|
if (constructor_type->is_subroutine()) {
|
2016-08-02 21:19:28 +01:00
|
|
|
_mesa_glsl_error(& loc, state,
|
|
|
|
"subroutine name cannot be a constructor `%s'",
|
2016-05-03 05:33:16 +01:00
|
|
|
constructor_type->name);
|
2016-08-02 21:19:28 +01:00
|
|
|
return ir_rvalue::error_value(ctx);
|
2016-05-03 05:33:16 +01:00
|
|
|
}
|
|
|
|
|
2010-04-01 00:25:21 +01:00
|
|
|
if (constructor_type->is_array()) {
|
2020-06-13 22:18:34 +01:00
|
|
|
if (!state->check_version(state->allow_glsl_120_subset_in_110 ? 110 : 120,
|
|
|
|
300, &loc, "array constructors forbidden")) {
|
2016-08-02 21:19:28 +01:00
|
|
|
return ir_rvalue::error_value(ctx);
|
|
|
|
}
|
2010-04-01 00:25:21 +01:00
|
|
|
|
2016-08-02 21:19:28 +01:00
|
|
|
return process_array_constructor(instructions, constructor_type,
|
|
|
|
& loc, &this->expressions, state);
|
2010-04-01 00:25:21 +01:00
|
|
|
}
|
2010-03-23 22:08:30 +00:00
|
|
|
|
2010-09-01 21:46:04 +01:00
|
|
|
|
2013-06-10 21:28:40 +01:00
|
|
|
/* There are two kinds of constructor calls. Constructors for arrays and
|
|
|
|
* structures must have the exact number of arguments with matching types
|
|
|
|
* in the correct order. These constructors follow essentially the same
|
|
|
|
* type matching rules as functions.
|
|
|
|
*
|
|
|
|
* Constructors for built-in language types, such as mat4 and vec2, are
|
|
|
|
* free form. The only requirements are that the parameters must provide
|
|
|
|
* enough values of the correct scalar type and that no arguments are
|
|
|
|
* given past the last used argument.
|
|
|
|
*
|
|
|
|
* When using the C-style initializer syntax from GLSL 4.20, constructors
|
|
|
|
* must have the exact number of arguments with matching types in the
|
|
|
|
* correct order.
|
2010-03-23 22:08:30 +00:00
|
|
|
*/
|
2019-03-05 04:05:52 +00:00
|
|
|
if (constructor_type->is_struct()) {
|
2013-06-13 20:05:21 +01:00
|
|
|
return process_record_constructor(instructions, constructor_type,
|
|
|
|
&loc, &this->expressions,
|
|
|
|
state);
|
2011-01-28 01:52:19 +00:00
|
|
|
}
|
|
|
|
|
2017-04-21 15:31:52 +01:00
|
|
|
if (!is_valid_constructor(constructor_type, state))
|
2016-08-02 21:19:28 +01:00
|
|
|
return ir_rvalue::error_value(ctx);
|
2010-03-27 00:38:58 +00:00
|
|
|
|
2010-07-09 02:03:28 +01:00
|
|
|
/* Total number of components of the type being constructed. */
|
|
|
|
const unsigned type_components = constructor_type->components();
|
2010-05-10 18:47:14 +01:00
|
|
|
|
2010-07-09 02:03:28 +01:00
|
|
|
/* Number of components from parameters that have actually been
|
|
|
|
* consumed. This is used to perform several kinds of error checking.
|
|
|
|
*/
|
|
|
|
unsigned components_used = 0;
|
2010-05-10 18:47:14 +01:00
|
|
|
|
2010-07-09 02:03:28 +01:00
|
|
|
unsigned matrix_parameters = 0;
|
|
|
|
unsigned nonmatrix_parameters = 0;
|
|
|
|
exec_list actual_parameters;
|
2010-03-27 00:38:58 +00:00
|
|
|
|
2014-06-25 06:02:24 +01:00
|
|
|
foreach_list_typed(ast_node, ast, link, &this->expressions) {
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_rvalue *result = ast->hir(instructions, state);
|
|
|
|
|
|
|
|
/* From page 50 (page 56 of the PDF) of the GLSL 1.50 spec:
|
|
|
|
*
|
|
|
|
* "It is an error to provide extra arguments beyond this
|
|
|
|
* last used argument."
|
|
|
|
*/
|
|
|
|
if (components_used >= type_components) {
|
|
|
|
_mesa_glsl_error(& loc, state, "too many parameters to `%s' "
|
|
|
|
"constructor",
|
|
|
|
constructor_type->name);
|
|
|
|
return ir_rvalue::error_value(ctx);
|
|
|
|
}
|
|
|
|
|
2017-04-21 15:31:52 +01:00
|
|
|
if (!is_valid_constructor(result->type, state)) {
|
2016-08-02 21:19:28 +01:00
|
|
|
_mesa_glsl_error(& loc, state, "cannot construct `%s' from a "
|
|
|
|
"non-numeric data type",
|
|
|
|
constructor_type->name);
|
|
|
|
return ir_rvalue::error_value(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Count the number of matrix and nonmatrix parameters. This
|
|
|
|
* is used below to enforce some of the constructor rules.
|
|
|
|
*/
|
|
|
|
if (result->type->is_matrix())
|
|
|
|
matrix_parameters++;
|
|
|
|
else
|
|
|
|
nonmatrix_parameters++;
|
|
|
|
|
|
|
|
actual_parameters.push_tail(result);
|
|
|
|
components_used += result->type->components();
|
2010-03-27 00:38:58 +00:00
|
|
|
}
|
2010-03-23 22:08:30 +00:00
|
|
|
|
2010-07-09 02:03:28 +01:00
|
|
|
/* From page 28 (page 34 of the PDF) of the GLSL 1.10 spec:
|
|
|
|
*
|
|
|
|
* "It is an error to construct matrices from other matrices. This
|
|
|
|
* is reserved for future use."
|
|
|
|
*/
|
2012-08-05 17:57:01 +01:00
|
|
|
if (matrix_parameters > 0
|
|
|
|
&& constructor_type->is_matrix()
|
|
|
|
&& !state->check_version(120, 100, &loc,
|
|
|
|
"cannot construct `%s' from a matrix",
|
|
|
|
constructor_type->name)) {
|
2016-08-02 21:19:28 +01:00
|
|
|
return ir_rvalue::error_value(ctx);
|
2010-07-09 02:03:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* From page 50 (page 56 of the PDF) of the GLSL 1.50 spec:
|
|
|
|
*
|
|
|
|
* "If a matrix argument is given to a matrix constructor, it is
|
|
|
|
* an error to have any other arguments."
|
|
|
|
*/
|
|
|
|
if ((matrix_parameters > 0)
|
2016-08-02 21:19:28 +01:00
|
|
|
&& ((matrix_parameters + nonmatrix_parameters) > 1)
|
|
|
|
&& constructor_type->is_matrix()) {
|
|
|
|
_mesa_glsl_error(& loc, state, "for matrix `%s' constructor, "
|
|
|
|
"matrix must be only parameter",
|
|
|
|
constructor_type->name);
|
|
|
|
return ir_rvalue::error_value(ctx);
|
2010-07-09 02:03:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* From page 28 (page 34 of the PDF) of the GLSL 1.10 spec:
|
|
|
|
*
|
|
|
|
* "In these cases, there must be enough components provided in the
|
|
|
|
* arguments to provide an initializer for every component in the
|
|
|
|
* constructed value."
|
|
|
|
*/
|
2010-09-01 23:04:57 +01:00
|
|
|
if (components_used < type_components && components_used != 1
|
2016-08-02 21:19:28 +01:00
|
|
|
&& matrix_parameters == 0) {
|
|
|
|
_mesa_glsl_error(& loc, state, "too few components to construct "
|
|
|
|
"`%s'",
|
|
|
|
constructor_type->name);
|
|
|
|
return ir_rvalue::error_value(ctx);
|
2010-07-09 02:03:28 +01:00
|
|
|
}
|
|
|
|
|
2015-05-26 13:32:21 +01:00
|
|
|
/* Matrices can never be consumed as is by any constructor but matrix
|
|
|
|
* constructors. If the constructor type is not matrix, always break the
|
|
|
|
* matrix up into a series of column vectors.
|
2010-07-09 02:15:32 +01:00
|
|
|
*/
|
2015-05-26 13:32:21 +01:00
|
|
|
if (!constructor_type->is_matrix()) {
|
2016-08-02 21:19:28 +01:00
|
|
|
foreach_in_list_safe(ir_rvalue, matrix, &actual_parameters) {
|
|
|
|
if (!matrix->type->is_matrix())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Create a temporary containing the matrix. */
|
|
|
|
ir_variable *var = new(ctx) ir_variable(matrix->type, "matrix_tmp",
|
|
|
|
ir_var_temporary);
|
|
|
|
instructions->push_tail(var);
|
|
|
|
instructions->push_tail(
|
|
|
|
new(ctx) ir_assignment(new(ctx) ir_dereference_variable(var),
|
2017-09-18 21:30:51 +01:00
|
|
|
matrix));
|
2017-08-10 11:42:29 +01:00
|
|
|
var->constant_value = matrix->constant_expression_value(ctx);
|
2016-08-02 21:19:28 +01:00
|
|
|
|
|
|
|
/* Replace the matrix with dereferences of its columns. */
|
|
|
|
for (int i = 0; i < matrix->type->matrix_columns; i++) {
|
|
|
|
matrix->insert_before(
|
|
|
|
new (ctx) ir_dereference_array(var,
|
|
|
|
new(ctx) ir_constant(i)));
|
|
|
|
}
|
|
|
|
matrix->remove();
|
|
|
|
}
|
2010-07-09 02:15:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool all_parameters_are_constant = true;
|
|
|
|
|
|
|
|
/* Type cast each parameter and, if possible, fold constants.*/
|
2014-06-25 05:58:35 +01:00
|
|
|
foreach_in_list_safe(ir_rvalue, ir, &actual_parameters) {
|
2017-04-21 14:27:15 +01:00
|
|
|
const glsl_type *desired_type;
|
|
|
|
|
|
|
|
/* From section 5.4.1 of the ARB_bindless_texture spec:
|
|
|
|
*
|
|
|
|
* "In the following four constructors, the low 32 bits of the sampler
|
|
|
|
* type correspond to the .x component of the uvec2 and the high 32
|
|
|
|
* bits correspond to the .y component."
|
|
|
|
*
|
|
|
|
* uvec2(any sampler type) // Converts a sampler type to a
|
|
|
|
* // pair of 32-bit unsigned integers
|
|
|
|
* any sampler type(uvec2) // Converts a pair of 32-bit unsigned integers to
|
|
|
|
* // a sampler type
|
|
|
|
* uvec2(any image type) // Converts an image type to a
|
|
|
|
* // pair of 32-bit unsigned integers
|
|
|
|
* any image type(uvec2) // Converts a pair of 32-bit unsigned integers to
|
|
|
|
* // an image type
|
|
|
|
*/
|
|
|
|
if (ir->type->is_sampler() || ir->type->is_image()) {
|
|
|
|
/* Convert a sampler/image type to a pair of 32-bit unsigned
|
|
|
|
* integers as defined by ARB_bindless_texture.
|
|
|
|
*/
|
|
|
|
if (constructor_type != glsl_type::uvec2_type) {
|
|
|
|
_mesa_glsl_error(&loc, state, "sampler and image types can only "
|
|
|
|
"be converted to a pair of 32-bit unsigned "
|
|
|
|
"integers");
|
|
|
|
}
|
|
|
|
desired_type = glsl_type::uvec2_type;
|
|
|
|
} else if (constructor_type->is_sampler() ||
|
|
|
|
constructor_type->is_image()) {
|
|
|
|
/* Convert a pair of 32-bit unsigned integers to a sampler or image
|
|
|
|
* type as defined by ARB_bindless_texture.
|
|
|
|
*/
|
|
|
|
if (ir->type != glsl_type::uvec2_type) {
|
|
|
|
_mesa_glsl_error(&loc, state, "sampler and image types can only "
|
|
|
|
"be converted from a pair of 32-bit unsigned "
|
|
|
|
"integers");
|
|
|
|
}
|
|
|
|
desired_type = constructor_type;
|
|
|
|
} else {
|
|
|
|
desired_type =
|
|
|
|
glsl_type::get_instance(constructor_type->base_type,
|
|
|
|
ir->type->vector_elements,
|
|
|
|
ir->type->matrix_columns);
|
|
|
|
}
|
|
|
|
|
2016-08-02 21:19:28 +01:00
|
|
|
ir_rvalue *result = convert_component(ir, desired_type);
|
|
|
|
|
|
|
|
/* Attempt to convert the parameter to a constant valued expression.
|
|
|
|
* After doing so, track whether or not all the parameters to the
|
|
|
|
* constructor are trivially constant valued expressions.
|
|
|
|
*/
|
2017-08-10 11:42:29 +01:00
|
|
|
ir_rvalue *const constant = result->constant_expression_value(ctx);
|
2016-08-02 21:19:28 +01:00
|
|
|
|
|
|
|
if (constant != NULL)
|
|
|
|
result = constant;
|
|
|
|
else
|
|
|
|
all_parameters_are_constant = false;
|
|
|
|
|
|
|
|
if (result != ir) {
|
|
|
|
ir->replace_with(result);
|
|
|
|
}
|
2010-07-09 02:15:32 +01:00
|
|
|
}
|
2010-07-09 02:03:28 +01:00
|
|
|
|
|
|
|
/* If all of the parameters are trivially constant, create a
|
|
|
|
* constant representing the complete collection of parameters.
|
|
|
|
*/
|
|
|
|
if (all_parameters_are_constant) {
|
2016-08-02 21:19:28 +01:00
|
|
|
return new(ctx) ir_constant(constructor_type, &actual_parameters);
|
2010-07-09 02:03:28 +01:00
|
|
|
} else if (constructor_type->is_scalar()) {
|
2016-08-02 21:19:28 +01:00
|
|
|
return dereference_component((ir_rvalue *)
|
|
|
|
actual_parameters.get_head_raw(),
|
|
|
|
0);
|
2010-07-09 02:03:28 +01:00
|
|
|
} else if (constructor_type->is_vector()) {
|
2016-08-02 21:19:28 +01:00
|
|
|
return emit_inline_vector_constructor(constructor_type,
|
|
|
|
instructions,
|
|
|
|
&actual_parameters,
|
|
|
|
ctx);
|
2010-07-09 02:03:28 +01:00
|
|
|
} else {
|
2016-08-02 21:19:28 +01:00
|
|
|
assert(constructor_type->is_matrix());
|
|
|
|
return emit_inline_matrix_constructor(constructor_type,
|
|
|
|
instructions,
|
|
|
|
&actual_parameters,
|
|
|
|
ctx);
|
2010-07-09 02:03:28 +01:00
|
|
|
}
|
2015-06-01 01:55:47 +01:00
|
|
|
} else if (subexpressions[0]->oper == ast_field_selection) {
|
|
|
|
return handle_method(instructions, state);
|
2010-03-15 20:04:13 +00:00
|
|
|
} else {
|
|
|
|
const ast_expression *id = subexpressions[0];
|
2016-11-12 19:55:30 +00:00
|
|
|
const char *func_name = NULL;
|
2013-12-09 08:38:35 +00:00
|
|
|
YYLTYPE loc = get_location();
|
2010-06-10 01:23:26 +01:00
|
|
|
exec_list actual_parameters;
|
2015-06-01 01:55:47 +01:00
|
|
|
ir_variable *sub_var = NULL;
|
|
|
|
ir_rvalue *array_idx = NULL;
|
|
|
|
|
2015-08-05 06:49:22 +01:00
|
|
|
process_parameters(instructions, &actual_parameters, &this->expressions,
|
2016-08-02 21:19:28 +01:00
|
|
|
state);
|
2015-08-05 06:49:22 +01:00
|
|
|
|
2015-06-01 01:55:47 +01:00
|
|
|
if (id->oper == ast_array_index) {
|
2015-08-05 06:49:22 +01:00
|
|
|
array_idx = generate_array_index(ctx, instructions, state, loc,
|
|
|
|
id->subexpressions[0],
|
|
|
|
id->subexpressions[1], &func_name,
|
|
|
|
&actual_parameters);
|
2016-11-12 19:55:30 +00:00
|
|
|
} else if (id->oper == ast_identifier) {
|
2015-06-01 01:55:47 +01:00
|
|
|
func_name = id->primary_expression.identifier;
|
2016-11-12 19:55:30 +00:00
|
|
|
} else {
|
|
|
|
_mesa_glsl_error(&loc, state, "function name is not an identifier");
|
2015-06-01 01:55:47 +01:00
|
|
|
}
|
2010-06-10 01:23:26 +01:00
|
|
|
|
2016-05-03 05:39:06 +01:00
|
|
|
/* an error was emitted earlier */
|
|
|
|
if (!func_name)
|
|
|
|
return ir_rvalue::error_value(ctx);
|
|
|
|
|
glsl: Split up function matching and call generation a bit more.
We used to have one big function, match_signature_by_name, which found
a matching signature, performed out-parameter conversions, and generated
the ir_call. As the code for matching against built-in functions became
more complicated, I split it internally, creating generate_call().
However, I left the same awkward interface. This patch splits it into
three functions:
1. match_signature_by_name()
This now takes a name, a list of parameters, the symbol table, and
returns an ir_function_signature. Simple and one purpose: matching.
2. no_matching_function_error()
Generate the "no matching function" error and list of prototypes.
This was complex enough that I felt it deserved its own function.
3. generate_call()
Do the out-parameter conversion and generate the ir_call. This
could probably use more splitting.
The caller now has a more natural workflow: find a matching signature,
then either generate an error or a call.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-28 23:57:50 +01:00
|
|
|
ir_function_signature *sig =
|
2016-08-02 21:19:28 +01:00
|
|
|
match_function_by_name(func_name, &actual_parameters, state);
|
glsl: Split up function matching and call generation a bit more.
We used to have one big function, match_signature_by_name, which found
a matching signature, performed out-parameter conversions, and generated
the ir_call. As the code for matching against built-in functions became
more complicated, I split it internally, creating generate_call().
However, I left the same awkward interface. This patch splits it into
three functions:
1. match_signature_by_name()
This now takes a name, a list of parameters, the symbol table, and
returns an ir_function_signature. Simple and one purpose: matching.
2. no_matching_function_error()
Generate the "no matching function" error and list of prototypes.
This was complex enough that I felt it deserved its own function.
3. generate_call()
Do the out-parameter conversion and generate the ir_call. This
could probably use more splitting.
The caller now has a more natural workflow: find a matching signature,
then either generate an error or a call.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-28 23:57:50 +01:00
|
|
|
|
|
|
|
ir_rvalue *value = NULL;
|
2015-06-01 01:55:47 +01:00
|
|
|
if (sig == NULL) {
|
2016-08-02 21:19:28 +01:00
|
|
|
sig = match_subroutine_by_name(func_name, &actual_parameters,
|
|
|
|
state, &sub_var);
|
2015-06-01 01:55:47 +01:00
|
|
|
}
|
|
|
|
|
glsl: Split up function matching and call generation a bit more.
We used to have one big function, match_signature_by_name, which found
a matching signature, performed out-parameter conversions, and generated
the ir_call. As the code for matching against built-in functions became
more complicated, I split it internally, creating generate_call().
However, I left the same awkward interface. This patch splits it into
three functions:
1. match_signature_by_name()
This now takes a name, a list of parameters, the symbol table, and
returns an ir_function_signature. Simple and one purpose: matching.
2. no_matching_function_error()
Generate the "no matching function" error and list of prototypes.
This was complex enough that I felt it deserved its own function.
3. generate_call()
Do the out-parameter conversion and generate the ir_call. This
could probably use more splitting.
The caller now has a more natural workflow: find a matching signature,
then either generate an error or a call.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-28 23:57:50 +01:00
|
|
|
if (sig == NULL) {
|
2016-08-02 21:19:28 +01:00
|
|
|
no_matching_function_error(func_name, &loc,
|
|
|
|
&actual_parameters, state);
|
|
|
|
value = ir_rvalue::error_value(ctx);
|
|
|
|
} else if (!verify_parameter_modes(state, sig,
|
|
|
|
actual_parameters,
|
|
|
|
this->expressions)) {
|
|
|
|
/* an error has already been emitted */
|
|
|
|
value = ir_rvalue::error_value(ctx);
|
glsl: Immediately inline built-ins rather than generating calls.
In the past, we imported the prototypes of built-in functions, generated
calls to those, and waited until link time to resolve the calls and
import the actual code for the built-in functions.
This severely limited our compile-time optimization opportunities: even
trivial functions like dot() were represented as function calls. We
also had no way of reasoning about those calls; they could have been
1,000 line functions with side-effects for all we knew.
Practically all built-in functions are trivial translations to
ir_expression opcodes, so it makes sense to just generate those inline.
Since we eventually inline all functions anyway, we may as well just do
it for all built-in functions.
There's only one snag: built-in functions that refer to built-in global
variables need those remapped to the variables in the shader being
compiled, rather than the ones in the built-in shader. Currently,
ftransform() is the only function matching those criteria, so it seemed
easier to just make it a special case.
On Skylake:
total instructions in shared programs: 12023491 -> 12024010 (0.00%)
instructions in affected programs: 77595 -> 78114 (0.67%)
helped: 97
HURT: 309
total cycles in shared programs: 137239044 -> 137295498 (0.04%)
cycles in affected programs: 16714026 -> 16770480 (0.34%)
helped: 4663
HURT: 4923
while these statistics are in the wrong direction, the number of
hurt programs is small (309 / 41282 = 0.75%), and I don't think
anything can be done about it. A change like this significantly
alters the order in which optimizations are performed.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by; Ian Romanick <ian.d.romanick@intel.com>
2014-05-31 07:52:22 +01:00
|
|
|
} else if (sig->is_builtin() && strcmp(func_name, "ftransform") == 0) {
|
|
|
|
/* ftransform refers to global variables, and we don't have any code
|
|
|
|
* for remapping the variable references in the built-in shader.
|
|
|
|
*/
|
|
|
|
ir_variable *mvp =
|
|
|
|
state->symbols->get_variable("gl_ModelViewProjectionMatrix");
|
|
|
|
ir_variable *vtx = state->symbols->get_variable("gl_Vertex");
|
|
|
|
value = new(ctx) ir_expression(ir_binop_mul, glsl_type::vec4_type,
|
|
|
|
new(ctx) ir_dereference_variable(mvp),
|
|
|
|
new(ctx) ir_dereference_variable(vtx));
|
glsl: Split up function matching and call generation a bit more.
We used to have one big function, match_signature_by_name, which found
a matching signature, performed out-parameter conversions, and generated
the ir_call. As the code for matching against built-in functions became
more complicated, I split it internally, creating generate_call().
However, I left the same awkward interface. This patch splits it into
three functions:
1. match_signature_by_name()
This now takes a name, a list of parameters, the symbol table, and
returns an ir_function_signature. Simple and one purpose: matching.
2. no_matching_function_error()
Generate the "no matching function" error and list of prototypes.
This was complex enough that I felt it deserved its own function.
3. generate_call()
Do the out-parameter conversion and generate the ir_call. This
could probably use more splitting.
The caller now has a more natural workflow: find a matching signature,
then either generate an error or a call.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-28 23:57:50 +01:00
|
|
|
} else {
|
2019-06-05 09:25:24 +01:00
|
|
|
bool is_begin_interlock = false;
|
|
|
|
bool is_end_interlock = false;
|
|
|
|
if (sig->is_builtin() &&
|
|
|
|
state->stage == MESA_SHADER_FRAGMENT &&
|
|
|
|
state->ARB_fragment_shader_interlock_enable) {
|
|
|
|
is_begin_interlock = strcmp(func_name, "beginInvocationInterlockARB") == 0;
|
|
|
|
is_end_interlock = strcmp(func_name, "endInvocationInterlockARB") == 0;
|
|
|
|
}
|
|
|
|
|
2019-06-05 08:59:11 +01:00
|
|
|
if (sig->is_builtin() &&
|
|
|
|
((state->stage == MESA_SHADER_TESS_CTRL &&
|
|
|
|
strcmp(func_name, "barrier") == 0) ||
|
2019-06-05 09:25:24 +01:00
|
|
|
is_begin_interlock || is_end_interlock)) {
|
2016-09-21 10:03:10 +01:00
|
|
|
if (state->current_function == NULL ||
|
|
|
|
strcmp(state->current_function->function_name(), "main") != 0) {
|
|
|
|
_mesa_glsl_error(&loc, state,
|
2019-06-05 08:59:11 +01:00
|
|
|
"%s() may only be used in main()", func_name);
|
2016-09-21 10:03:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (state->found_return) {
|
|
|
|
_mesa_glsl_error(&loc, state,
|
2019-06-05 08:59:11 +01:00
|
|
|
"%s() may not be used after return", func_name);
|
2016-09-21 10:03:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (instructions != &state->current_function->body) {
|
|
|
|
_mesa_glsl_error(&loc, state,
|
2019-06-05 08:59:11 +01:00
|
|
|
"%s() may not be used in control flow", func_name);
|
2016-09-21 10:03:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 09:25:24 +01:00
|
|
|
/* There can be only one begin/end interlock pair in the function. */
|
|
|
|
if (is_begin_interlock) {
|
|
|
|
if (state->found_begin_interlock)
|
|
|
|
_mesa_glsl_error(&loc, state,
|
|
|
|
"beginInvocationInterlockARB may not be used twice");
|
|
|
|
state->found_begin_interlock = true;
|
|
|
|
} else if (is_end_interlock) {
|
|
|
|
if (!state->found_begin_interlock)
|
|
|
|
_mesa_glsl_error(&loc, state,
|
|
|
|
"endInvocationInterlockARB may not be used "
|
|
|
|
"before beginInvocationInterlockARB");
|
|
|
|
if (state->found_end_interlock)
|
|
|
|
_mesa_glsl_error(&loc, state,
|
|
|
|
"endInvocationInterlockARB may not be used twice");
|
|
|
|
state->found_end_interlock = true;
|
|
|
|
}
|
|
|
|
|
glsl: Immediately inline built-ins rather than generating calls.
In the past, we imported the prototypes of built-in functions, generated
calls to those, and waited until link time to resolve the calls and
import the actual code for the built-in functions.
This severely limited our compile-time optimization opportunities: even
trivial functions like dot() were represented as function calls. We
also had no way of reasoning about those calls; they could have been
1,000 line functions with side-effects for all we knew.
Practically all built-in functions are trivial translations to
ir_expression opcodes, so it makes sense to just generate those inline.
Since we eventually inline all functions anyway, we may as well just do
it for all built-in functions.
There's only one snag: built-in functions that refer to built-in global
variables need those remapped to the variables in the shader being
compiled, rather than the ones in the built-in shader. Currently,
ftransform() is the only function matching those criteria, so it seemed
easier to just make it a special case.
On Skylake:
total instructions in shared programs: 12023491 -> 12024010 (0.00%)
instructions in affected programs: 77595 -> 78114 (0.67%)
helped: 97
HURT: 309
total cycles in shared programs: 137239044 -> 137295498 (0.04%)
cycles in affected programs: 16714026 -> 16770480 (0.34%)
helped: 4663
HURT: 4923
while these statistics are in the wrong direction, the number of
hurt programs is small (309 / 41282 = 0.75%), and I don't think
anything can be done about it. A change like this significantly
alters the order in which optimizations are performed.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by; Ian Romanick <ian.d.romanick@intel.com>
2014-05-31 07:52:22 +01:00
|
|
|
value = generate_call(instructions, sig, &actual_parameters, sub_var,
|
2017-07-27 07:49:55 +01:00
|
|
|
array_idx, state);
|
2015-07-11 18:38:10 +01:00
|
|
|
if (!value) {
|
|
|
|
ir_variable *const tmp = new(ctx) ir_variable(glsl_type::void_type,
|
|
|
|
"void_var",
|
|
|
|
ir_var_temporary);
|
|
|
|
instructions->push_tail(tmp);
|
|
|
|
value = new(ctx) ir_dereference_variable(tmp);
|
|
|
|
}
|
glsl: Split up function matching and call generation a bit more.
We used to have one big function, match_signature_by_name, which found
a matching signature, performed out-parameter conversions, and generated
the ir_call. As the code for matching against built-in functions became
more complicated, I split it internally, creating generate_call().
However, I left the same awkward interface. This patch splits it into
three functions:
1. match_signature_by_name()
This now takes a name, a list of parameters, the symbol table, and
returns an ir_function_signature. Simple and one purpose: matching.
2. no_matching_function_error()
Generate the "no matching function" error and list of prototypes.
This was complex enough that I felt it deserved its own function.
3. generate_call()
Do the out-parameter conversion and generate the ir_call. This
could probably use more splitting.
The caller now has a more natural workflow: find a matching signature,
then either generate an error or a call.
Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
2012-03-28 23:57:50 +01:00
|
|
|
}
|
2011-12-23 18:58:23 +00:00
|
|
|
|
|
|
|
return value;
|
2010-03-15 20:04:13 +00:00
|
|
|
}
|
|
|
|
|
2015-04-11 18:11:13 +01:00
|
|
|
unreachable("not reached");
|
2010-03-15 20:04:13 +00:00
|
|
|
}
|
2013-06-30 03:27:50 +01:00
|
|
|
|
2015-10-07 21:03:53 +01:00
|
|
|
bool
|
|
|
|
ast_function_expression::has_sequence_subexpression() const
|
|
|
|
{
|
|
|
|
foreach_list_typed(const ast_node, ast, link, &this->expressions) {
|
|
|
|
if (ast->has_sequence_subexpression())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-06-30 03:27:50 +01:00
|
|
|
ir_rvalue *
|
|
|
|
ast_aggregate_initializer::hir(exec_list *instructions,
|
|
|
|
struct _mesa_glsl_parse_state *state)
|
|
|
|
{
|
|
|
|
void *ctx = state;
|
|
|
|
YYLTYPE loc = this->get_location();
|
2013-07-12 19:05:38 +01:00
|
|
|
|
|
|
|
if (!this->constructor_type) {
|
|
|
|
_mesa_glsl_error(&loc, state, "type of C-style initializer unknown");
|
|
|
|
return ir_rvalue::error_value(ctx);
|
|
|
|
}
|
glsl: Simplify aggregate type inference to prepare for ARB_arrays_of_arrays.
Most of the time it is not necessary to perform type inference to
compile GLSL; the type of every expression can be inferred from the
contents of the expression itself (and previous type declarations).
The exception is aggregate initializers: their type is determined by
the LHS of the variable being assigned to. For example, in the
statement:
mat2 foo = { { 1, 2 }, { 3, 4 } };
the type of { 1, 2 } is only known to be vec2 (as opposed to, say,
ivec2, uvec2, int[2], or a struct) because of the fact that the result
is being assigned to a mat2.
Previous to this patch, we handled this situation by doing some type
inference during parsing: when parsing a declaration like the one
above, we would call _mesa_set_aggregate_type(), which would infer the
type of each aggregate initializer and store it in the corresponding
ast_aggregate_initializer::constructor_type field. Since this
happened at parse time, we couldn't do the type inference using
glsl_type objects; we had to use ast_type_specifiers, which are much
more awkward to work with. Things are about to get more complicated
when we add support for ARB_arrays_of_arrays.
This patch simplifies things by postponing the call to
_mesa_set_aggregate_type() until ast-to-hir time, when we have access
to glsl_type objects. As a side benefit, we only need to have one
call to _mesa_set_aggregate_type() now, instead of six.
Reviewed-by: Matt Turner <mattst88@gmail.com>
2014-01-21 23:41:26 +00:00
|
|
|
const glsl_type *const constructor_type = this->constructor_type;
|
2013-06-30 03:27:50 +01:00
|
|
|
|
2015-12-07 22:11:01 +00:00
|
|
|
if (!state->has_420pack()) {
|
2013-06-30 03:27:50 +01:00
|
|
|
_mesa_glsl_error(&loc, state, "C-style initialization requires the "
|
|
|
|
"GL_ARB_shading_language_420pack extension");
|
|
|
|
return ir_rvalue::error_value(ctx);
|
|
|
|
}
|
|
|
|
|
glsl: Simplify aggregate type inference to prepare for ARB_arrays_of_arrays.
Most of the time it is not necessary to perform type inference to
compile GLSL; the type of every expression can be inferred from the
contents of the expression itself (and previous type declarations).
The exception is aggregate initializers: their type is determined by
the LHS of the variable being assigned to. For example, in the
statement:
mat2 foo = { { 1, 2 }, { 3, 4 } };
the type of { 1, 2 } is only known to be vec2 (as opposed to, say,
ivec2, uvec2, int[2], or a struct) because of the fact that the result
is being assigned to a mat2.
Previous to this patch, we handled this situation by doing some type
inference during parsing: when parsing a declaration like the one
above, we would call _mesa_set_aggregate_type(), which would infer the
type of each aggregate initializer and store it in the corresponding
ast_aggregate_initializer::constructor_type field. Since this
happened at parse time, we couldn't do the type inference using
glsl_type objects; we had to use ast_type_specifiers, which are much
more awkward to work with. Things are about to get more complicated
when we add support for ARB_arrays_of_arrays.
This patch simplifies things by postponing the call to
_mesa_set_aggregate_type() until ast-to-hir time, when we have access
to glsl_type objects. As a side benefit, we only need to have one
call to _mesa_set_aggregate_type() now, instead of six.
Reviewed-by: Matt Turner <mattst88@gmail.com>
2014-01-21 23:41:26 +00:00
|
|
|
if (constructor_type->is_array()) {
|
2013-06-30 03:27:50 +01:00
|
|
|
return process_array_constructor(instructions, constructor_type, &loc,
|
|
|
|
&this->expressions, state);
|
|
|
|
}
|
|
|
|
|
2019-03-05 04:05:52 +00:00
|
|
|
if (constructor_type->is_struct()) {
|
2013-06-30 03:27:50 +01:00
|
|
|
return process_record_constructor(instructions, constructor_type, &loc,
|
|
|
|
&this->expressions, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
return process_vec_mat_constructor(instructions, constructor_type, &loc,
|
|
|
|
&this->expressions, state);
|
|
|
|
}
|