vkd3d/libs/vkd3d-shader/hlsl.y
Giovanni Mascellani ce9a86975f vkd3d-shader/hlsl: Return the shape of the left operand when appropriate.
The assumption about the size of matrices is not correct: it is
legitimate to compose a matrix 2x2 with a vector of length 4, in which
case it appears that the result has the shape of the first (leftmost)
operand. Even for matrices 1xN or Nx1, the result is not always a
vector: in general it has the shape of the first operand again.

Signed-off-by: Giovanni Mascellani <gmascellani@codeweavers.com>
Signed-off-by: Zebediah Figura <zfigura@codeweavers.com>
Signed-off-by: Matteo Bruni <mbruni@codeweavers.com>
Signed-off-by: Henri Verbeet <hverbeet@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2021-11-01 20:50:23 +01:00

3427 lines
101 KiB
Plaintext

/*
* HLSL parser
*
* Copyright 2008 Stefan Dösinger
* Copyright 2012 Matteo Bruni for CodeWeavers
* Copyright 2019-2020 Zebediah Figura for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
%code requires
{
#include "hlsl.h"
#include <stdio.h>
#define HLSL_YYLTYPE struct vkd3d_shader_location
struct parse_parameter
{
struct hlsl_type *type;
const char *name;
struct hlsl_semantic semantic;
struct hlsl_reg_reservation reg_reservation;
unsigned int modifiers;
};
struct parse_colon_attribute
{
struct hlsl_semantic semantic;
struct hlsl_reg_reservation reg_reservation;
};
struct parse_initializer
{
struct hlsl_ir_node **args;
unsigned int args_count;
struct list *instrs;
};
struct parse_array_sizes
{
uint32_t *sizes; /* innermost first */
unsigned int count;
};
struct parse_variable_def
{
struct list entry;
struct vkd3d_shader_location loc;
char *name;
struct parse_array_sizes arrays;
struct hlsl_semantic semantic;
struct hlsl_reg_reservation reg_reservation;
struct parse_initializer initializer;
};
struct parse_function
{
char *name;
struct hlsl_ir_function_decl *decl;
};
struct parse_if_body
{
struct list *then_instrs;
struct list *else_instrs;
};
enum parse_assign_op
{
ASSIGN_OP_ASSIGN,
ASSIGN_OP_ADD,
ASSIGN_OP_SUB,
ASSIGN_OP_MUL,
ASSIGN_OP_DIV,
ASSIGN_OP_MOD,
ASSIGN_OP_LSHIFT,
ASSIGN_OP_RSHIFT,
ASSIGN_OP_AND,
ASSIGN_OP_OR,
ASSIGN_OP_XOR,
};
}
%code provides
{
int yylex(HLSL_YYSTYPE *yylval_param, HLSL_YYLTYPE *yylloc_param, void *yyscanner);
}
%code
{
#define YYLLOC_DEFAULT(cur, rhs, n) (cur) = YYRHSLOC(rhs, !!n)
static void yyerror(YYLTYPE *loc, void *scanner, struct hlsl_ctx *ctx, const char *s)
{
hlsl_error(ctx, *loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "%s", s);
}
static struct hlsl_ir_node *node_from_list(struct list *list)
{
return LIST_ENTRY(list_tail(list), struct hlsl_ir_node, entry);
}
static struct list *make_empty_list(struct hlsl_ctx *ctx)
{
struct list *list;
if ((list = hlsl_alloc(ctx, sizeof(*list))))
list_init(list);
return list;
}
static void destroy_instr_list(struct list *list)
{
hlsl_free_instr_list(list);
vkd3d_free(list);
}
static void check_invalid_matrix_modifiers(struct hlsl_ctx *ctx, DWORD modifiers, struct vkd3d_shader_location loc)
{
if (modifiers & HLSL_MODIFIERS_MAJORITY_MASK)
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"'row_major' and 'column_major' modifiers are only allowed for matrices.");
}
static bool convertible_data_type(struct hlsl_type *type)
{
return type->type != HLSL_CLASS_OBJECT;
}
static bool compatible_data_types(struct hlsl_type *t1, struct hlsl_type *t2)
{
if (!convertible_data_type(t1) || !convertible_data_type(t2))
return false;
if (t1->type <= HLSL_CLASS_LAST_NUMERIC)
{
/* Scalar vars can be cast to pretty much everything */
if (t1->dimx == 1 && t1->dimy == 1)
return true;
if (t1->type == HLSL_CLASS_VECTOR && t2->type == HLSL_CLASS_VECTOR)
return t1->dimx >= t2->dimx;
}
/* The other way around is true too i.e. whatever to scalar */
if (t2->type <= HLSL_CLASS_LAST_NUMERIC && t2->dimx == 1 && t2->dimy == 1)
return true;
if (t1->type == HLSL_CLASS_ARRAY)
{
if (hlsl_types_are_equal(t1->e.array.type, t2))
/* e.g. float4[3] to float4 is allowed */
return true;
if (t2->type == HLSL_CLASS_ARRAY || t2->type == HLSL_CLASS_STRUCT)
return hlsl_type_component_count(t1) >= hlsl_type_component_count(t2);
else
return hlsl_type_component_count(t1) == hlsl_type_component_count(t2);
}
if (t1->type == HLSL_CLASS_STRUCT)
return hlsl_type_component_count(t1) >= hlsl_type_component_count(t2);
if (t2->type == HLSL_CLASS_ARRAY || t2->type == HLSL_CLASS_STRUCT)
return hlsl_type_component_count(t1) == hlsl_type_component_count(t2);
if (t1->type == HLSL_CLASS_MATRIX || t2->type == HLSL_CLASS_MATRIX)
{
if (t1->type == HLSL_CLASS_MATRIX && t2->type == HLSL_CLASS_MATRIX && t1->dimx >= t2->dimx && t1->dimy >= t2->dimy)
return true;
/* Matrix-vector conversion is apparently allowed if they have the same components count */
if ((t1->type == HLSL_CLASS_VECTOR || t2->type == HLSL_CLASS_VECTOR)
&& hlsl_type_component_count(t1) == hlsl_type_component_count(t2))
return true;
return false;
}
if (hlsl_type_component_count(t1) >= hlsl_type_component_count(t2))
return true;
return false;
}
static bool implicit_compatible_data_types(struct hlsl_type *t1, struct hlsl_type *t2)
{
if (!convertible_data_type(t1) || !convertible_data_type(t2))
return false;
if (t1->type <= HLSL_CLASS_LAST_NUMERIC)
{
/* Scalar vars can be converted to any other numeric data type */
if (t1->dimx == 1 && t1->dimy == 1 && t2->type <= HLSL_CLASS_LAST_NUMERIC)
return true;
/* The other way around is true too */
if (t2->dimx == 1 && t2->dimy == 1 && t2->type <= HLSL_CLASS_LAST_NUMERIC)
return true;
}
if (t1->type == HLSL_CLASS_ARRAY && t2->type == HLSL_CLASS_ARRAY)
{
return hlsl_type_component_count(t1) == hlsl_type_component_count(t2);
}
if ((t1->type == HLSL_CLASS_ARRAY && t2->type <= HLSL_CLASS_LAST_NUMERIC)
|| (t1->type <= HLSL_CLASS_LAST_NUMERIC && t2->type == HLSL_CLASS_ARRAY))
{
/* e.g. float4[3] to float4 is allowed */
if (t1->type == HLSL_CLASS_ARRAY && hlsl_types_are_equal(t1->e.array.type, t2))
return true;
if (hlsl_type_component_count(t1) == hlsl_type_component_count(t2))
return true;
return false;
}
if (t1->type <= HLSL_CLASS_VECTOR && t2->type <= HLSL_CLASS_VECTOR)
{
if (t1->dimx >= t2->dimx)
return true;
return false;
}
if (t1->type == HLSL_CLASS_MATRIX || t2->type == HLSL_CLASS_MATRIX)
{
if (t1->type == HLSL_CLASS_MATRIX && t2->type == HLSL_CLASS_MATRIX
&& t1->dimx >= t2->dimx && t1->dimy >= t2->dimy)
return true;
/* Matrix-vector conversion is apparently allowed if they have the same components count */
if ((t1->type == HLSL_CLASS_VECTOR || t2->type == HLSL_CLASS_VECTOR)
&& hlsl_type_component_count(t1) == hlsl_type_component_count(t2))
return true;
return false;
}
if (t1->type == HLSL_CLASS_STRUCT && t2->type == HLSL_CLASS_STRUCT)
return hlsl_types_are_equal(t1, t2);
return false;
}
static struct hlsl_ir_node *add_implicit_conversion(struct hlsl_ctx *ctx, struct list *instrs,
struct hlsl_ir_node *node, struct hlsl_type *dst_type, const struct vkd3d_shader_location *loc)
{
struct hlsl_type *src_type = node->data_type;
struct hlsl_ir_expr *cast;
if (hlsl_types_are_equal(src_type, dst_type))
return node;
if (!implicit_compatible_data_types(src_type, dst_type))
{
struct vkd3d_string_buffer *src_string, *dst_string;
src_string = hlsl_type_to_string(ctx, src_type);
dst_string = hlsl_type_to_string(ctx, dst_type);
if (src_string && dst_string)
hlsl_error(ctx, *loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Can't implicitly convert from %s to %s.", src_string->buffer, dst_string->buffer);
hlsl_release_string_buffer(ctx, src_string);
hlsl_release_string_buffer(ctx, dst_string);
return NULL;
}
if (dst_type->dimx * dst_type->dimy < src_type->dimx * src_type->dimy)
hlsl_warning(ctx, *loc, VKD3D_SHADER_WARNING_HLSL_IMPLICIT_TRUNCATION, "Implicit truncation of %s type.",
src_type->type == HLSL_CLASS_VECTOR ? "vector" : "matrix");
if (!(cast = hlsl_new_cast(ctx, node, dst_type, loc)))
return NULL;
list_add_tail(instrs, &cast->node.entry);
return &cast->node;
}
static DWORD add_modifiers(struct hlsl_ctx *ctx, DWORD modifiers, DWORD mod, const struct vkd3d_shader_location loc)
{
if (modifiers & mod)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_modifiers_to_string(ctx, mod)))
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Modifier '%s' was already specified.", string->buffer);
hlsl_release_string_buffer(ctx, string);
return modifiers;
}
if ((mod & HLSL_MODIFIERS_MAJORITY_MASK) && (modifiers & HLSL_MODIFIERS_MAJORITY_MASK))
{
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"'row_major' and 'column_major' modifiers are mutually exclusive.");
return modifiers;
}
return modifiers | mod;
}
static bool append_conditional_break(struct hlsl_ctx *ctx, struct list *cond_list)
{
struct hlsl_ir_node *condition, *not;
struct hlsl_ir_jump *jump;
struct hlsl_ir_if *iff;
/* E.g. "for (i = 0; ; ++i)". */
if (!list_count(cond_list))
return true;
condition = node_from_list(cond_list);
if (!(not = hlsl_new_unary_expr(ctx, HLSL_OP1_LOGIC_NOT, condition, condition->loc)))
return false;
list_add_tail(cond_list, &not->entry);
if (!(iff = hlsl_new_if(ctx, not, condition->loc)))
return false;
list_add_tail(cond_list, &iff->node.entry);
if (!(jump = hlsl_new_jump(ctx, HLSL_IR_JUMP_BREAK, condition->loc)))
return false;
list_add_head(&iff->then_instrs, &jump->node.entry);
return true;
}
enum loop_type
{
LOOP_FOR,
LOOP_WHILE,
LOOP_DO_WHILE
};
static struct list *create_loop(struct hlsl_ctx *ctx, enum loop_type type, struct list *init, struct list *cond,
struct list *iter, struct list *body, struct vkd3d_shader_location loc)
{
struct list *list = NULL;
struct hlsl_ir_loop *loop = NULL;
struct hlsl_ir_if *cond_jump = NULL;
if (!(list = make_empty_list(ctx)))
goto oom;
if (init)
list_move_head(list, init);
if (!(loop = hlsl_new_loop(ctx, loc)))
goto oom;
list_add_tail(list, &loop->node.entry);
if (!append_conditional_break(ctx, cond))
goto oom;
if (type != LOOP_DO_WHILE)
list_move_tail(&loop->body, cond);
list_move_tail(&loop->body, body);
if (iter)
list_move_tail(&loop->body, iter);
if (type == LOOP_DO_WHILE)
list_move_tail(&loop->body, cond);
vkd3d_free(init);
vkd3d_free(cond);
vkd3d_free(body);
return list;
oom:
vkd3d_free(loop);
vkd3d_free(cond_jump);
vkd3d_free(list);
destroy_instr_list(init);
destroy_instr_list(cond);
destroy_instr_list(iter);
destroy_instr_list(body);
return NULL;
}
static unsigned int initializer_size(const struct parse_initializer *initializer)
{
unsigned int count = 0, i;
for (i = 0; i < initializer->args_count; ++i)
{
count += hlsl_type_component_count(initializer->args[i]->data_type);
}
return count;
}
static void free_parse_initializer(struct parse_initializer *initializer)
{
destroy_instr_list(initializer->instrs);
vkd3d_free(initializer->args);
}
static struct hlsl_ir_swizzle *get_swizzle(struct hlsl_ctx *ctx, struct hlsl_ir_node *value, const char *swizzle,
struct vkd3d_shader_location *loc)
{
unsigned int len = strlen(swizzle), component = 0;
unsigned int i, set, swiz = 0;
bool valid;
if (value->data_type->type == HLSL_CLASS_MATRIX)
{
/* Matrix swizzle */
bool m_swizzle;
unsigned int inc, x, y;
if (len < 3 || swizzle[0] != '_')
return NULL;
m_swizzle = swizzle[1] == 'm';
inc = m_swizzle ? 4 : 3;
if (len % inc || len > inc * 4)
return NULL;
for (i = 0; i < len; i += inc)
{
if (swizzle[i] != '_')
return NULL;
if (m_swizzle)
{
if (swizzle[i + 1] != 'm')
return NULL;
y = swizzle[i + 2] - '0';
x = swizzle[i + 3] - '0';
}
else
{
y = swizzle[i + 1] - '1';
x = swizzle[i + 2] - '1';
}
if (x >= value->data_type->dimx || y >= value->data_type->dimy)
return NULL;
swiz |= (y << 4 | x) << component * 8;
component++;
}
return hlsl_new_swizzle(ctx, swiz, component, value, loc);
}
/* Vector swizzle */
if (len > 4)
return NULL;
for (set = 0; set < 2; ++set)
{
valid = true;
component = 0;
for (i = 0; i < len; ++i)
{
char c[2][4] = {{'x', 'y', 'z', 'w'}, {'r', 'g', 'b', 'a'}};
unsigned int s = 0;
for (s = 0; s < 4; ++s)
{
if (swizzle[i] == c[set][s])
break;
}
if (s == 4)
{
valid = false;
break;
}
if (s >= value->data_type->dimx)
return NULL;
swiz |= s << component * 2;
component++;
}
if (valid)
return hlsl_new_swizzle(ctx, swiz, component, value, loc);
}
return NULL;
}
static struct hlsl_ir_jump *add_return(struct hlsl_ctx *ctx, struct list *instrs,
struct hlsl_ir_node *return_value, struct vkd3d_shader_location loc)
{
struct hlsl_type *return_type = ctx->cur_function->return_type;
struct hlsl_ir_jump *jump;
if (return_value)
{
struct hlsl_ir_store *store;
if (!(return_value = add_implicit_conversion(ctx, instrs, return_value, return_type, &loc)))
return NULL;
if (!(store = hlsl_new_simple_store(ctx, ctx->cur_function->return_var, return_value)))
return NULL;
list_add_after(&return_value->entry, &store->node.entry);
}
else if (ctx->cur_function->return_var)
{
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RETURN, "Non-void function must return a value.");
return NULL;
}
if (!(jump = hlsl_new_jump(ctx, HLSL_IR_JUMP_RETURN, loc)))
return NULL;
list_add_tail(instrs, &jump->node.entry);
return jump;
}
static struct hlsl_ir_load *add_load(struct hlsl_ctx *ctx, struct list *instrs, struct hlsl_ir_node *var_node,
struct hlsl_ir_node *offset, struct hlsl_type *data_type, const struct vkd3d_shader_location loc)
{
struct hlsl_ir_node *add = NULL;
struct hlsl_ir_load *load;
struct hlsl_ir_var *var;
if (var_node->type == HLSL_IR_LOAD)
{
const struct hlsl_deref *src = &hlsl_ir_load(var_node)->src;
var = src->var;
if (src->offset.node)
{
if (!(add = hlsl_new_binary_expr(ctx, HLSL_OP2_ADD, src->offset.node, offset)))
return NULL;
list_add_tail(instrs, &add->entry);
offset = add;
}
}
else
{
struct hlsl_ir_store *store;
char name[27];
sprintf(name, "<deref-%p>", var_node);
if (!(var = hlsl_new_synthetic_var(ctx, name, var_node->data_type, var_node->loc)))
return NULL;
if (!(store = hlsl_new_simple_store(ctx, var, var_node)))
return NULL;
list_add_tail(instrs, &store->node.entry);
}
if (!(load = hlsl_new_load(ctx, var, offset, data_type, loc)))
return NULL;
list_add_tail(instrs, &load->node.entry);
return load;
}
static struct hlsl_ir_load *add_record_load(struct hlsl_ctx *ctx, struct list *instrs, struct hlsl_ir_node *record,
const struct hlsl_struct_field *field, const struct vkd3d_shader_location loc)
{
struct hlsl_ir_constant *c;
if (!(c = hlsl_new_uint_constant(ctx, field->reg_offset, loc)))
return NULL;
list_add_tail(instrs, &c->node.entry);
return add_load(ctx, instrs, record, &c->node, field->type, loc);
}
static struct hlsl_ir_load *add_array_load(struct hlsl_ctx *ctx, struct list *instrs, struct hlsl_ir_node *array,
struct hlsl_ir_node *index, const struct vkd3d_shader_location loc)
{
const struct hlsl_type *expr_type = array->data_type;
struct hlsl_type *data_type;
struct hlsl_ir_constant *c;
struct hlsl_ir_node *mul;
if (expr_type->type == HLSL_CLASS_ARRAY)
{
data_type = expr_type->e.array.type;
}
else if (expr_type->type == HLSL_CLASS_MATRIX || expr_type->type == HLSL_CLASS_VECTOR)
{
/* This needs to be lowered now, while we still have type information. */
FIXME("Index of matrix or vector type.\n");
return NULL;
}
else
{
if (expr_type->type == HLSL_CLASS_SCALAR)
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_INDEX, "Scalar expressions cannot be array-indexed.");
else
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_INDEX, "Expression cannot be array-indexed.");
return NULL;
}
if (!(c = hlsl_new_uint_constant(ctx, data_type->reg_size, loc)))
return NULL;
list_add_tail(instrs, &c->node.entry);
if (!(mul = hlsl_new_binary_expr(ctx, HLSL_OP2_MUL, index, &c->node)))
return NULL;
list_add_tail(instrs, &mul->entry);
index = mul;
return add_load(ctx, instrs, array, index, data_type, loc);
}
static struct hlsl_struct_field *get_struct_field(struct list *fields, const char *name)
{
struct hlsl_struct_field *f;
LIST_FOR_EACH_ENTRY(f, fields, struct hlsl_struct_field, entry)
{
if (!strcmp(f->name, name))
return f;
}
return NULL;
}
static struct hlsl_type *apply_type_modifiers(struct hlsl_ctx *ctx, struct hlsl_type *type,
unsigned int *modifiers, struct vkd3d_shader_location loc)
{
unsigned int default_majority = 0;
struct hlsl_type *new_type;
/* This function is only used for declarations (i.e. variables and struct
* fields), which should inherit the matrix majority. We only explicitly set
* the default majority for declarations—typedefs depend on this—but we
* want to always set it, so that an hlsl_type object is never used to
* represent two different majorities (and thus can be used to store its
* register size, etc.) */
if (!(*modifiers & HLSL_MODIFIERS_MAJORITY_MASK)
&& !(type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK)
&& type->type == HLSL_CLASS_MATRIX)
{
if (ctx->matrix_majority == HLSL_COLUMN_MAJOR)
default_majority = HLSL_MODIFIER_COLUMN_MAJOR;
else
default_majority = HLSL_MODIFIER_ROW_MAJOR;
}
if (!default_majority && !(*modifiers & HLSL_TYPE_MODIFIERS_MASK))
return type;
if (!(new_type = hlsl_type_clone(ctx, type, default_majority, *modifiers & HLSL_TYPE_MODIFIERS_MASK)))
return NULL;
*modifiers &= ~HLSL_TYPE_MODIFIERS_MASK;
if ((new_type->modifiers & HLSL_MODIFIER_ROW_MAJOR) && (new_type->modifiers & HLSL_MODIFIER_COLUMN_MAJOR))
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"'row_major' and 'column_major' modifiers are mutually exclusive.");
return new_type;
}
static struct list *gen_struct_fields(struct hlsl_ctx *ctx, struct hlsl_type *type, struct list *fields)
{
struct parse_variable_def *v, *v_next;
struct hlsl_struct_field *field;
struct list *list;
if (type->type == HLSL_CLASS_MATRIX)
assert(type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK);
if (!(list = make_empty_list(ctx)))
return NULL;
LIST_FOR_EACH_ENTRY_SAFE(v, v_next, fields, struct parse_variable_def, entry)
{
unsigned int i;
if (!(field = hlsl_alloc(ctx, sizeof(*field))))
{
vkd3d_free(v);
return list;
}
field->type = type;
for (i = 0; i < v->arrays.count; ++i)
field->type = hlsl_new_array_type(ctx, field->type, v->arrays.sizes[i]);
field->loc = v->loc;
field->name = v->name;
field->semantic = v->semantic;
if (v->initializer.args_count)
{
hlsl_error(ctx, v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Illegal initializer on a struct field.");
free_parse_initializer(&v->initializer);
}
list_add_tail(list, &field->entry);
vkd3d_free(v);
}
vkd3d_free(fields);
return list;
}
static bool add_typedef(struct hlsl_ctx *ctx, DWORD modifiers, struct hlsl_type *orig_type, struct list *list)
{
struct parse_variable_def *v, *v_next;
struct hlsl_type *type;
unsigned int i;
bool ret;
LIST_FOR_EACH_ENTRY_SAFE(v, v_next, list, struct parse_variable_def, entry)
{
if (!v->arrays.count)
{
if (!(type = hlsl_type_clone(ctx, orig_type, 0, modifiers)))
return false;
}
else
{
type = orig_type;
}
for (i = 0; i < v->arrays.count; ++i)
{
if (!(type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i])))
return false;
}
vkd3d_free((void *)type->name);
type->name = v->name;
if (type->type != HLSL_CLASS_MATRIX)
check_invalid_matrix_modifiers(ctx, type->modifiers, v->loc);
if ((type->modifiers & HLSL_MODIFIER_COLUMN_MAJOR)
&& (type->modifiers & HLSL_MODIFIER_ROW_MAJOR))
hlsl_error(ctx, v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"'row_major' and 'column_major' modifiers are mutually exclusive.");
ret = hlsl_scope_add_type(ctx->cur_scope, type);
if (!ret)
hlsl_error(ctx, v->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED,
"Type '%s' is already defined.", v->name);
vkd3d_free(v);
}
vkd3d_free(list);
return true;
}
static bool add_func_parameter(struct hlsl_ctx *ctx, struct list *list,
struct parse_parameter *param, const struct vkd3d_shader_location loc)
{
struct hlsl_ir_var *var;
if (param->type->type == HLSL_CLASS_MATRIX)
assert(param->type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK);
if ((param->modifiers & HLSL_STORAGE_OUT) && (param->modifiers & HLSL_STORAGE_UNIFORM))
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Parameter '%s' is declared as both \"out\" and \"uniform\".", param->name);
if (!(var = hlsl_new_var(ctx, param->name, param->type, loc, &param->semantic, param->modifiers, &param->reg_reservation)))
return false;
var->is_param = 1;
if (!hlsl_add_var(ctx, var, false))
{
hlsl_free_var(var);
return false;
}
list_add_tail(list, &var->param_entry);
return true;
}
static struct hlsl_reg_reservation parse_reg_reservation(const char *reg_string)
{
struct hlsl_reg_reservation reservation = {0};
if (!sscanf(reg_string + 1, "%u", &reservation.index))
{
FIXME("Unsupported register reservation syntax.\n");
return reservation;
}
reservation.type = reg_string[0];
return reservation;
}
static const struct hlsl_ir_function_decl *get_func_decl(struct rb_tree *funcs, char *name, struct list *params)
{
struct hlsl_ir_function *func;
struct rb_entry *entry;
if ((entry = rb_get(funcs, name)))
{
func = RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry);
if ((entry = rb_get(&func->overloads, params)))
return RB_ENTRY_VALUE(entry, struct hlsl_ir_function_decl, entry);
}
return NULL;
}
static struct list *make_list(struct hlsl_ctx *ctx, struct hlsl_ir_node *node)
{
struct list *list;
if (!(list = make_empty_list(ctx)))
{
hlsl_free_instr(node);
return NULL;
}
list_add_tail(list, &node->entry);
return list;
}
static unsigned int evaluate_array_dimension(struct hlsl_ir_node *node)
{
if (node->data_type->type != HLSL_CLASS_SCALAR)
return 0;
switch (node->type)
{
case HLSL_IR_CONSTANT:
{
struct hlsl_ir_constant *constant = hlsl_ir_constant(node);
const union hlsl_constant_value *value = &constant->value[0];
switch (constant->node.data_type->base_type)
{
case HLSL_TYPE_UINT:
return value->u;
case HLSL_TYPE_INT:
return value->i;
case HLSL_TYPE_FLOAT:
case HLSL_TYPE_HALF:
return value->f;
case HLSL_TYPE_DOUBLE:
return value->d;
case HLSL_TYPE_BOOL:
return value->b;
default:
assert(0);
return 0;
}
}
case HLSL_IR_EXPR:
case HLSL_IR_LOAD:
case HLSL_IR_RESOURCE_LOAD:
case HLSL_IR_SWIZZLE:
FIXME("Unhandled type %s.\n", hlsl_node_type_to_string(node->type));
return 0;
case HLSL_IR_IF:
case HLSL_IR_JUMP:
case HLSL_IR_LOOP:
case HLSL_IR_STORE:
WARN("Invalid node type %s.\n", hlsl_node_type_to_string(node->type));
return 0;
}
assert(0);
return 0;
}
static bool expr_compatible_data_types(struct hlsl_type *t1, struct hlsl_type *t2)
{
if (t1->base_type > HLSL_TYPE_LAST_SCALAR || t2->base_type > HLSL_TYPE_LAST_SCALAR)
return false;
/* Scalar vars can be converted to pretty much everything */
if ((t1->dimx == 1 && t1->dimy == 1) || (t2->dimx == 1 && t2->dimy == 1))
return true;
if (t1->type == HLSL_CLASS_VECTOR && t2->type == HLSL_CLASS_VECTOR)
return true;
if (t1->type == HLSL_CLASS_MATRIX || t2->type == HLSL_CLASS_MATRIX)
{
/* Matrix-vector conversion is apparently allowed if either they have the same components
count or the matrix is nx1 or 1xn */
if (t1->type == HLSL_CLASS_VECTOR || t2->type == HLSL_CLASS_VECTOR)
{
if (hlsl_type_component_count(t1) == hlsl_type_component_count(t2))
return true;
return (t1->type == HLSL_CLASS_MATRIX && (t1->dimx == 1 || t1->dimy == 1))
|| (t2->type == HLSL_CLASS_MATRIX && (t2->dimx == 1 || t2->dimy == 1));
}
/* Both matrices */
if ((t1->dimx >= t2->dimx && t1->dimy >= t2->dimy)
|| (t1->dimx <= t2->dimx && t1->dimy <= t2->dimy))
return true;
}
return false;
}
static enum hlsl_base_type expr_common_base_type(enum hlsl_base_type t1, enum hlsl_base_type t2)
{
if (t1 > HLSL_TYPE_LAST_SCALAR || t2 > HLSL_TYPE_LAST_SCALAR) {
FIXME("Unexpected base type.\n");
return HLSL_TYPE_FLOAT;
}
if (t1 == t2)
return t1 == HLSL_TYPE_BOOL ? HLSL_TYPE_INT : t1;
if (t1 == HLSL_TYPE_DOUBLE || t2 == HLSL_TYPE_DOUBLE)
return HLSL_TYPE_DOUBLE;
if (t1 == HLSL_TYPE_FLOAT || t2 == HLSL_TYPE_FLOAT
|| t1 == HLSL_TYPE_HALF || t2 == HLSL_TYPE_HALF)
return HLSL_TYPE_FLOAT;
if (t1 == HLSL_TYPE_UINT || t2 == HLSL_TYPE_UINT)
return HLSL_TYPE_UINT;
return HLSL_TYPE_INT;
}
static bool expr_common_shape(struct hlsl_ctx *ctx, struct hlsl_type *t1, struct hlsl_type *t2,
struct vkd3d_shader_location *loc, enum hlsl_type_class *type, unsigned int *dimx, unsigned int *dimy)
{
if (t1->type > HLSL_CLASS_LAST_NUMERIC)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, t1)))
hlsl_error(ctx, *loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Expression of type \"%s\" cannot be used in a numeric expression.", string->buffer);
hlsl_release_string_buffer(ctx, string);
return false;
}
if (t2->type > HLSL_CLASS_LAST_NUMERIC)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, t2)))
hlsl_error(ctx, *loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Expression of type \"%s\" cannot be used in a numeric expression.", string->buffer);
hlsl_release_string_buffer(ctx, string);
return false;
}
if (!expr_compatible_data_types(t1, t2))
{
struct vkd3d_string_buffer *t1_string = hlsl_type_to_string(ctx, t1);
struct vkd3d_string_buffer *t2_string = hlsl_type_to_string(ctx, t2);
if (t1_string && t2_string)
hlsl_error(ctx, *loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Expression data types \"%s\" and \"%s\" are incompatible.",
t1_string->buffer, t2_string->buffer);
hlsl_release_string_buffer(ctx, t1_string);
hlsl_release_string_buffer(ctx, t2_string);
return false;
}
if (t1->dimx == 1 && t1->dimy == 1)
{
*type = t2->type;
*dimx = t2->dimx;
*dimy = t2->dimy;
}
else if (t2->dimx == 1 && t2->dimy == 1)
{
*type = t1->type;
*dimx = t1->dimx;
*dimy = t1->dimy;
}
else if (t1->type == HLSL_CLASS_MATRIX && t2->type == HLSL_CLASS_MATRIX)
{
*type = HLSL_CLASS_MATRIX;
*dimx = min(t1->dimx, t2->dimx);
*dimy = min(t1->dimy, t2->dimy);
}
else
{
if (t1->dimx * t1->dimy <= t2->dimx * t2->dimy)
{
*type = t1->type;
*dimx = t1->dimx;
*dimy = t1->dimy;
}
else
{
*type = t2->type;
*dimx = t2->dimx;
*dimy = t2->dimy;
}
}
return true;
}
static struct hlsl_type *expr_common_type(struct hlsl_ctx *ctx, struct hlsl_type *t1, struct hlsl_type *t2,
struct vkd3d_shader_location *loc)
{
enum hlsl_type_class type;
enum hlsl_base_type base;
unsigned int dimx, dimy;
if (!expr_common_shape(ctx, t1, t2, loc, &type, &dimx, &dimy))
return NULL;
base = expr_common_base_type(t1->base_type, t2->base_type);
return hlsl_get_numeric_type(ctx, type, base, dimx, dimy);
}
static struct hlsl_ir_expr *add_expr(struct hlsl_ctx *ctx, struct list *instrs, enum hlsl_ir_expr_op op,
struct hlsl_ir_node *operands[HLSL_MAX_OPERANDS], struct vkd3d_shader_location *loc)
{
struct hlsl_ir_expr *expr;
struct hlsl_type *type;
unsigned int i;
type = operands[0]->data_type;
for (i = 1; i < HLSL_MAX_OPERANDS; ++i)
{
if (!operands[i])
break;
type = expr_common_type(ctx, type, operands[i]->data_type, loc);
if (!type)
return NULL;
}
for (i = 0; i < HLSL_MAX_OPERANDS; ++i)
{
struct hlsl_ir_expr *cast;
if (!operands[i])
break;
if (hlsl_types_are_equal(operands[i]->data_type, type))
continue;
if (operands[i]->data_type->dimx * operands[i]->data_type->dimy != 1
&& operands[i]->data_type->dimx * operands[i]->data_type->dimy != type->dimx * type->dimy)
hlsl_warning(ctx, operands[i]->loc, VKD3D_SHADER_WARNING_HLSL_IMPLICIT_TRUNCATION,
"Implicit truncation of %s type.",
operands[i]->data_type->type == HLSL_CLASS_VECTOR ? "vector" : "matrix");
if (!(cast = hlsl_new_cast(ctx, operands[i], type, &operands[i]->loc)))
return NULL;
list_add_after(&operands[i]->entry, &cast->node.entry);
operands[i] = &cast->node;
}
if (!(expr = hlsl_alloc(ctx, sizeof(*expr))))
return NULL;
init_node(&expr->node, HLSL_IR_EXPR, type, *loc);
expr->op = op;
for (i = 0; i < HLSL_MAX_OPERANDS; ++i)
hlsl_src_from_node(&expr->operands[i], operands[i]);
list_add_tail(instrs, &expr->node.entry);
return expr;
}
static struct list *add_unary_expr(struct hlsl_ctx *ctx, struct list *instrs,
enum hlsl_ir_expr_op op, struct vkd3d_shader_location loc)
{
struct hlsl_ir_node *expr;
if (!(expr = hlsl_new_unary_expr(ctx, op, node_from_list(instrs), loc)))
return NULL;
list_add_tail(instrs, &expr->entry);
return instrs;
}
static struct list *add_binary_expr(struct hlsl_ctx *ctx, struct list *list1, struct list *list2,
enum hlsl_ir_expr_op op, struct vkd3d_shader_location loc)
{
struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {node_from_list(list1), node_from_list(list2)};
list_move_tail(list1, list2);
vkd3d_free(list2);
add_expr(ctx, list1, op, args, &loc);
return list1;
}
static enum hlsl_ir_expr_op op_from_assignment(enum parse_assign_op op)
{
static const enum hlsl_ir_expr_op ops[] =
{
0,
HLSL_OP2_ADD,
0,
HLSL_OP2_MUL,
HLSL_OP2_DIV,
HLSL_OP2_MOD,
HLSL_OP2_LSHIFT,
HLSL_OP2_RSHIFT,
HLSL_OP2_BIT_AND,
HLSL_OP2_BIT_OR,
HLSL_OP2_BIT_XOR,
};
return ops[op];
}
static bool invert_swizzle(unsigned int *swizzle, unsigned int *writemask, unsigned int *ret_width)
{
unsigned int i, j, bit = 0, inverted = 0, width, new_writemask = 0, new_swizzle = 0;
/* Apply the writemask to the swizzle to get a new writemask and swizzle. */
for (i = 0; i < 4; ++i)
{
if (*writemask & (1 << i))
{
unsigned int s = (*swizzle >> (i * 2)) & 3;
new_swizzle |= s << (bit++ * 2);
if (new_writemask & (1 << s))
return false;
new_writemask |= 1 << s;
}
}
width = bit;
/* Invert the swizzle. */
bit = 0;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < width; ++j)
{
unsigned int s = (new_swizzle >> (j * 2)) & 3;
if (s == i)
inverted |= j << (bit++ * 2);
}
}
*swizzle = inverted;
*writemask = new_writemask;
*ret_width = width;
return true;
}
static struct hlsl_ir_node *add_assignment(struct hlsl_ctx *ctx, struct list *instrs, struct hlsl_ir_node *lhs,
enum parse_assign_op assign_op, struct hlsl_ir_node *rhs)
{
struct hlsl_type *lhs_type = lhs->data_type;
struct hlsl_ir_store *store;
struct hlsl_ir_expr *copy;
DWORD writemask = 0;
if (assign_op == ASSIGN_OP_SUB)
{
struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {rhs};
struct hlsl_ir_expr *expr;
if (!(expr = add_expr(ctx, instrs, HLSL_OP1_NEG, args, &rhs->loc)))
return NULL;
rhs = &expr->node;
assign_op = ASSIGN_OP_ADD;
}
if (assign_op != ASSIGN_OP_ASSIGN)
{
struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {lhs, rhs};
enum hlsl_ir_expr_op op = op_from_assignment(assign_op);
struct hlsl_ir_expr *expr;
assert(op);
if (!(expr = add_expr(ctx, instrs, op, args, &rhs->loc)))
return NULL;
rhs = &expr->node;
}
if (lhs_type->type <= HLSL_CLASS_LAST_NUMERIC)
{
writemask = (1 << lhs_type->dimx) - 1;
if (!(rhs = add_implicit_conversion(ctx, instrs, rhs, lhs_type, &rhs->loc)))
return NULL;
}
if (!(store = hlsl_alloc(ctx, sizeof(*store))))
return NULL;
while (lhs->type != HLSL_IR_LOAD)
{
if (lhs->type == HLSL_IR_EXPR && hlsl_ir_expr(lhs)->op == HLSL_OP1_CAST)
{
hlsl_fixme(ctx, lhs->loc, "Cast on the LHS.");
vkd3d_free(store);
return NULL;
}
else if (lhs->type == HLSL_IR_SWIZZLE)
{
struct hlsl_ir_swizzle *swizzle = hlsl_ir_swizzle(lhs), *new_swizzle;
unsigned int width, s = swizzle->swizzle;
if (lhs->data_type->type == HLSL_CLASS_MATRIX)
hlsl_fixme(ctx, lhs->loc, "Matrix assignment with a writemask.");
if (!invert_swizzle(&s, &writemask, &width))
{
hlsl_error(ctx, lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_WRITEMASK, "Invalid writemask.");
vkd3d_free(store);
return NULL;
}
if (!(new_swizzle = hlsl_new_swizzle(ctx, s, width, rhs, &swizzle->node.loc)))
{
vkd3d_free(store);
return NULL;
}
list_add_tail(instrs, &new_swizzle->node.entry);
lhs = swizzle->val.node;
rhs = &new_swizzle->node;
}
else
{
hlsl_error(ctx, lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_LVALUE, "Invalid lvalue.");
vkd3d_free(store);
return NULL;
}
}
init_node(&store->node, HLSL_IR_STORE, NULL, lhs->loc);
store->writemask = writemask;
store->lhs.var = hlsl_ir_load(lhs)->src.var;
hlsl_src_from_node(&store->lhs.offset, hlsl_ir_load(lhs)->src.offset.node);
hlsl_src_from_node(&store->rhs, rhs);
list_add_tail(instrs, &store->node.entry);
/* Don't use the instruction itself as a source, as this makes structure
* splitting easier. Instead copy it here. Since we retrieve sources from
* the last instruction in the list, we do need to copy. */
if (!(copy = hlsl_new_copy(ctx, rhs)))
return NULL;
list_add_tail(instrs, &copy->node.entry);
return &copy->node;
}
static bool add_increment(struct hlsl_ctx *ctx, struct list *instrs, bool decrement, bool post,
struct vkd3d_shader_location loc)
{
struct hlsl_ir_node *lhs = node_from_list(instrs);
struct hlsl_ir_constant *one;
if (lhs->data_type->modifiers & HLSL_MODIFIER_CONST)
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_MODIFIES_CONST,
"Argument to %s%screment operator is const.", post ? "post" : "pre", decrement ? "de" : "in");
if (!(one = hlsl_new_uint_constant(ctx, 1, loc)))
return false;
list_add_tail(instrs, &one->node.entry);
if (!add_assignment(ctx, instrs, lhs, decrement ? ASSIGN_OP_SUB : ASSIGN_OP_ADD, &one->node))
return false;
if (post)
{
struct hlsl_ir_expr *copy;
if (!(copy = hlsl_new_copy(ctx, lhs)))
return false;
list_add_tail(instrs, &copy->node.entry);
/* Post increment/decrement expressions are considered const. */
if (!(copy->node.data_type = hlsl_type_clone(ctx, copy->node.data_type, 0, HLSL_MODIFIER_CONST)))
return false;
}
return true;
}
static void struct_var_initializer(struct hlsl_ctx *ctx, struct list *list, struct hlsl_ir_var *var,
struct parse_initializer *initializer)
{
struct hlsl_type *type = var->data_type;
struct hlsl_struct_field *field;
unsigned int i = 0;
if (initializer_size(initializer) != hlsl_type_component_count(type))
{
hlsl_error(ctx, var->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
"Expected %u components in initializer, but got %u.",
hlsl_type_component_count(type), initializer_size(initializer));
free_parse_initializer(initializer);
return;
}
list_move_tail(list, initializer->instrs);
vkd3d_free(initializer->instrs);
LIST_FOR_EACH_ENTRY(field, type->e.elements, struct hlsl_struct_field, entry)
{
struct hlsl_ir_node *node = initializer->args[i];
struct hlsl_ir_store *store;
struct hlsl_ir_constant *c;
if (i++ >= initializer->args_count)
break;
if (hlsl_type_component_count(field->type) == hlsl_type_component_count(node->data_type))
{
if (!(c = hlsl_new_uint_constant(ctx, field->reg_offset, node->loc)))
break;
list_add_tail(list, &c->node.entry);
if (!(store = hlsl_new_store(ctx, var, &c->node, node, 0, node->loc)))
break;
list_add_tail(list, &store->node.entry);
}
else
{
hlsl_fixme(ctx, node->loc, "Implicit cast in structure initializer.");
}
}
vkd3d_free(initializer->args);
}
static void free_parse_variable_def(struct parse_variable_def *v)
{
free_parse_initializer(&v->initializer);
vkd3d_free(v->arrays.sizes);
vkd3d_free(v->name);
vkd3d_free((void *)v->semantic.name);
vkd3d_free(v);
}
static struct list *declare_vars(struct hlsl_ctx *ctx, struct hlsl_type *basic_type,
DWORD modifiers, struct list *var_list)
{
struct parse_variable_def *v, *v_next;
struct hlsl_ir_function_decl *func;
struct list *statements_list;
struct hlsl_ir_var *var;
struct hlsl_type *type;
bool local = true;
if (basic_type->type == HLSL_CLASS_MATRIX)
assert(basic_type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK);
if (!(statements_list = make_empty_list(ctx)))
{
LIST_FOR_EACH_ENTRY_SAFE(v, v_next, var_list, struct parse_variable_def, entry)
free_parse_variable_def(v);
vkd3d_free(var_list);
return NULL;
}
if (!var_list)
return statements_list;
LIST_FOR_EACH_ENTRY_SAFE(v, v_next, var_list, struct parse_variable_def, entry)
{
unsigned int i;
type = basic_type;
for (i = 0; i < v->arrays.count; ++i)
type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]);
if (type->type != HLSL_CLASS_MATRIX)
check_invalid_matrix_modifiers(ctx, modifiers, v->loc);
if (!(var = hlsl_new_var(ctx, v->name, type, v->loc, &v->semantic, modifiers, &v->reg_reservation)))
{
free_parse_variable_def(v);
continue;
}
var->buffer = ctx->cur_buffer;
if (ctx->cur_scope == ctx->globals)
{
local = false;
if ((modifiers & HLSL_STORAGE_UNIFORM) && (modifiers & HLSL_STORAGE_STATIC))
hlsl_error(ctx, var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Variable '%s' is declared as both \"uniform\" and \"static\".", var->name);
/* Mark it as uniform. We need to do this here since synthetic
* variables also get put in the global scope, but shouldn't be
* considered uniforms, and we have no way of telling otherwise. */
if (!(modifiers & HLSL_STORAGE_STATIC))
var->modifiers |= HLSL_STORAGE_UNIFORM;
if ((func = hlsl_get_func_decl(ctx, var->name)))
{
hlsl_error(ctx, var->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED,
"'%s' is already defined as a function.", var->name);
hlsl_note(ctx, func->loc, VKD3D_SHADER_LOG_ERROR,
"'%s' was previously defined here.", var->name);
}
}
else
{
static const unsigned int invalid = HLSL_STORAGE_EXTERN | HLSL_STORAGE_SHARED
| HLSL_STORAGE_GROUPSHARED | HLSL_STORAGE_UNIFORM;
if (modifiers & invalid)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_modifiers_to_string(ctx, modifiers & invalid)))
hlsl_error(ctx, var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Modifiers '%s' are not allowed on local variables.", string->buffer);
hlsl_release_string_buffer(ctx, string);
}
if (var->semantic.name)
hlsl_error(ctx, var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SEMANTIC,
"Semantics are not allowed on local variables.");
}
if ((type->modifiers & HLSL_MODIFIER_CONST) && !v->initializer.args_count
&& !(modifiers & (HLSL_STORAGE_STATIC | HLSL_STORAGE_UNIFORM)))
{
hlsl_error(ctx, v->loc, VKD3D_SHADER_ERROR_HLSL_MISSING_INITIALIZER,
"Const variable \"%s\" is missing an initializer.", var->name);
hlsl_free_var(var);
vkd3d_free(v);
continue;
}
if (!hlsl_add_var(ctx, var, local))
{
struct hlsl_ir_var *old = hlsl_get_var(ctx->cur_scope, var->name);
hlsl_error(ctx, var->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED,
"Variable \"%s\" was already declared in this scope.", var->name);
hlsl_note(ctx, old->loc, VKD3D_SHADER_LOG_ERROR, "\"%s\" was previously declared here.", old->name);
hlsl_free_var(var);
vkd3d_free(v);
continue;
}
if (v->initializer.args_count)
{
unsigned int size = initializer_size(&v->initializer);
struct hlsl_ir_load *load;
if (type->type <= HLSL_CLASS_LAST_NUMERIC
&& type->dimx * type->dimy != size && size != 1)
{
if (size < type->dimx * type->dimy)
{
hlsl_error(ctx, v->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
"Expected %u components in numeric initializer, but got %u.",
type->dimx * type->dimy, size);
free_parse_initializer(&v->initializer);
vkd3d_free(v);
continue;
}
}
if ((type->type == HLSL_CLASS_STRUCT || type->type == HLSL_CLASS_ARRAY)
&& hlsl_type_component_count(type) != size)
{
hlsl_error(ctx, v->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
"Expected %u components in initializer, but got %u.", hlsl_type_component_count(type), size);
free_parse_initializer(&v->initializer);
vkd3d_free(v);
continue;
}
if (type->type == HLSL_CLASS_STRUCT)
{
struct_var_initializer(ctx, statements_list, var, &v->initializer);
vkd3d_free(v);
continue;
}
if (type->type > HLSL_CLASS_LAST_NUMERIC)
{
FIXME("Initializers for non scalar/struct variables not supported yet.\n");
free_parse_initializer(&v->initializer);
vkd3d_free(v);
continue;
}
if (v->arrays.count)
{
hlsl_fixme(ctx, v->loc, "Array initializer.");
free_parse_initializer(&v->initializer);
vkd3d_free(v);
continue;
}
if (v->initializer.args_count > 1)
{
hlsl_fixme(ctx, v->loc, "Complex initializer.");
free_parse_initializer(&v->initializer);
vkd3d_free(v);
continue;
}
load = hlsl_new_var_load(ctx, var, var->loc);
list_add_tail(v->initializer.instrs, &load->node.entry);
add_assignment(ctx, v->initializer.instrs, &load->node, ASSIGN_OP_ASSIGN, v->initializer.args[0]);
vkd3d_free(v->initializer.args);
if (modifiers & HLSL_STORAGE_STATIC)
list_move_tail(&ctx->static_initializers, v->initializer.instrs);
else
list_move_tail(statements_list, v->initializer.instrs);
vkd3d_free(v->initializer.instrs);
}
vkd3d_free(v);
}
vkd3d_free(var_list);
return statements_list;
}
static unsigned int sampler_dim_count(enum hlsl_sampler_dim dim)
{
switch (dim)
{
case HLSL_SAMPLER_DIM_1D:
return 1;
case HLSL_SAMPLER_DIM_2D:
return 2;
case HLSL_SAMPLER_DIM_3D:
case HLSL_SAMPLER_DIM_CUBE:
return 3;
default:
assert(0);
return 0;
}
}
struct find_function_call_args
{
const struct parse_initializer *params;
const struct hlsl_ir_function_decl *decl;
};
static void find_function_call_exact(struct rb_entry *entry, void *context)
{
const struct hlsl_ir_function_decl *decl = RB_ENTRY_VALUE(entry, const struct hlsl_ir_function_decl, entry);
struct find_function_call_args *args = context;
const struct hlsl_ir_var *param;
unsigned int i = 0;
LIST_FOR_EACH_ENTRY(param, decl->parameters, struct hlsl_ir_var, param_entry)
{
if (i >= args->params->args_count
|| !hlsl_types_are_equal(param->data_type, args->params->args[i++]->data_type))
return;
}
if (i == args->params->args_count)
args->decl = decl;
}
static const struct hlsl_ir_function_decl *find_function_call(struct hlsl_ctx *ctx,
const char *name, const struct parse_initializer *params)
{
struct find_function_call_args args = {.params = params};
struct hlsl_ir_function *func;
struct rb_entry *entry;
if (!(entry = rb_get(&ctx->functions, name)))
return NULL;
func = RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry);
rb_for_each_entry(&func->overloads, find_function_call_exact, &args);
if (!args.decl)
FIXME("Search for compatible overloads.\n");
return args.decl;
}
static bool intrinsic_abs(struct hlsl_ctx *ctx,
const struct parse_initializer *params, struct vkd3d_shader_location loc)
{
struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {params->args[0]};
return !!add_expr(ctx, params->instrs, HLSL_OP1_ABS, args, &loc);
}
static bool intrinsic_clamp(struct hlsl_ctx *ctx,
const struct parse_initializer *params, struct vkd3d_shader_location loc)
{
struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {params->args[0], params->args[1]};
struct hlsl_ir_expr *max;
if (!(max = add_expr(ctx, params->instrs, HLSL_OP2_MAX, args, &loc)))
return false;
args[0] = &max->node;
args[1] = params->args[2];
return !!add_expr(ctx, params->instrs, HLSL_OP2_MIN, args, &loc);
}
static bool intrinsic_max(struct hlsl_ctx *ctx,
const struct parse_initializer *params, struct vkd3d_shader_location loc)
{
struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {params->args[0], params->args[1]};
return !!add_expr(ctx, params->instrs, HLSL_OP2_MAX, args, &loc);
}
static bool intrinsic_pow(struct hlsl_ctx *ctx,
const struct parse_initializer *params, struct vkd3d_shader_location loc)
{
struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {0};
struct hlsl_ir_node *log, *exp;
struct hlsl_ir_expr *mul;
if (!(log = hlsl_new_unary_expr(ctx, HLSL_OP1_LOG2, params->args[0], loc)))
return false;
list_add_tail(params->instrs, &log->entry);
args[0] = params->args[1];
args[1] = log;
if (!(mul = add_expr(ctx, params->instrs, HLSL_OP2_MUL, args, &loc)))
return false;
if (!(exp = hlsl_new_unary_expr(ctx, HLSL_OP1_EXP2, &mul->node, loc)))
return false;
list_add_tail(params->instrs, &exp->entry);
return true;
}
static bool intrinsic_saturate(struct hlsl_ctx *ctx,
const struct parse_initializer *params, struct vkd3d_shader_location loc)
{
struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {params->args[0]};
return !!add_expr(ctx, params->instrs, HLSL_OP1_SAT, args, &loc);
}
static const struct intrinsic_function
{
const char *name;
int param_count;
bool check_numeric;
bool (*handler)(struct hlsl_ctx *ctx, const struct parse_initializer *params, struct vkd3d_shader_location loc);
}
intrinsic_functions[] =
{
{"abs", 1, true, intrinsic_abs},
{"clamp", 3, true, intrinsic_clamp},
{"max", 2, true, intrinsic_max},
{"pow", 2, true, intrinsic_pow},
{"saturate", 1, true, intrinsic_saturate},
};
static int intrinsic_function_name_compare(const void *a, const void *b)
{
const struct intrinsic_function *func = b;
return strcmp(a, func->name);
}
static struct list *add_call(struct hlsl_ctx *ctx, const char *name,
struct parse_initializer *params, struct vkd3d_shader_location loc)
{
const struct hlsl_ir_function_decl *decl;
struct intrinsic_function *intrinsic;
if ((decl = find_function_call(ctx, name, params)))
{
hlsl_fixme(ctx, loc, "Call to user-defined function \"%s\".", name);
free_parse_initializer(params);
return NULL;
}
else if ((intrinsic = bsearch(name, intrinsic_functions, ARRAY_SIZE(intrinsic_functions),
sizeof(*intrinsic_functions), intrinsic_function_name_compare)))
{
if (intrinsic->param_count >= 0 && params->args_count != intrinsic->param_count)
{
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
"Wrong number of arguments to function '%s': expected %u, but got %u.\n",
name, intrinsic->param_count, params->args_count);
free_parse_initializer(params);
return NULL;
}
if (intrinsic->check_numeric)
{
unsigned int i;
for (i = 0; i < params->args_count; ++i)
{
if (params->args[i]->data_type->type > HLSL_CLASS_LAST_NUMERIC)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, params->args[i]->data_type)))
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Wrong type for argument %u of '%s': expected a numeric type, but got '%s'.\n",
i + 1, name, string->buffer);
hlsl_release_string_buffer(ctx, string);
free_parse_initializer(params);
return NULL;
}
}
}
if (!intrinsic->handler(ctx, params, loc))
{
free_parse_initializer(params);
return NULL;
}
}
else
{
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED, "Function \"%s\" is not defined.", name);
free_parse_initializer(params);
return NULL;
}
vkd3d_free(params->args);
return params->instrs;
}
static struct list *add_constructor(struct hlsl_ctx *ctx, struct hlsl_type *type,
struct parse_initializer *params, struct vkd3d_shader_location loc)
{
unsigned int i, writemask_offset = 0;
struct hlsl_ir_store *store;
static unsigned int counter;
struct hlsl_ir_load *load;
struct hlsl_ir_var *var;
char name[23];
if (type->type == HLSL_CLASS_MATRIX)
hlsl_fixme(ctx, loc, "Matrix constructor.");
sprintf(name, "<constructor-%x>", counter++);
if (!(var = hlsl_new_synthetic_var(ctx, name, type, loc)))
return NULL;
for (i = 0; i < params->args_count; ++i)
{
struct hlsl_ir_node *arg = params->args[i];
unsigned int width;
if (arg->data_type->type == HLSL_CLASS_OBJECT)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, arg->data_type)))
hlsl_error(ctx, arg->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Invalid type %s for constructor argument.", string->buffer);
hlsl_release_string_buffer(ctx, string);
continue;
}
width = hlsl_type_component_count(arg->data_type);
if (width > 4)
{
FIXME("Constructor argument with %u components.\n", width);
continue;
}
if (!(arg = add_implicit_conversion(ctx, params->instrs, arg,
hlsl_get_vector_type(ctx, type->base_type, width), &arg->loc)))
continue;
if (!(store = hlsl_new_store(ctx, var, NULL, arg,
((1u << width) - 1) << writemask_offset, arg->loc)))
return NULL;
list_add_tail(params->instrs, &store->node.entry);
writemask_offset += width;
}
if (!(load = hlsl_new_var_load(ctx, var, loc)))
return NULL;
list_add_tail(params->instrs, &load->node.entry);
vkd3d_free(params->args);
return params->instrs;
}
static bool add_method_call(struct hlsl_ctx *ctx, struct list *instrs, struct hlsl_ir_node *object,
const char *name, const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
{
const struct hlsl_type *object_type = object->data_type;
struct hlsl_ir_load *object_load;
if (object_type->type != HLSL_CLASS_OBJECT || object_type->base_type != HLSL_TYPE_TEXTURE
|| object_type->sampler_dim == HLSL_SAMPLER_DIM_GENERIC)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, object_type)))
hlsl_error(ctx, *loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Type '%s' does not have methods.", string->buffer);
hlsl_release_string_buffer(ctx, string);
return false;
}
/* Only HLSL_IR_LOAD can return an object. */
object_load = hlsl_ir_load(object);
if (!strcmp(name, "Load"))
{
const unsigned int sampler_dim = sampler_dim_count(object_type->sampler_dim);
struct hlsl_ir_resource_load *load;
struct hlsl_ir_node *coords;
if (params->args_count < 1 || params->args_count > 3)
{
hlsl_error(ctx, *loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
"Wrong number of arguments to method 'Load': expected 1, 2, or 3, but got %u.", params->args_count);
return false;
}
if (params->args_count >= 2)
FIXME("Ignoring index and/or offset parameter(s).\n");
/* +1 for the mipmap level */
if (!(coords = add_implicit_conversion(ctx, instrs, params->args[0],
hlsl_get_vector_type(ctx, HLSL_TYPE_INT, sampler_dim + 1), loc)))
return false;
if (!(load = hlsl_new_resource_load(ctx, object_type->e.resource_format, HLSL_RESOURCE_LOAD,
object_load->src.var, object_load->src.offset.node, coords, loc)))
return false;
list_add_tail(instrs, &load->node.entry);
return true;
}
else
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, object_type)))
hlsl_error(ctx, *loc, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED,
"Method '%s' is not defined on type '%s'.", name, string->buffer);
hlsl_release_string_buffer(ctx, string);
return false;
}
}
}
%locations
%define parse.error verbose
%define api.prefix {hlsl_yy}
%define api.pure full
%expect 1
%lex-param {yyscan_t scanner}
%parse-param {void *scanner}
%parse-param {struct hlsl_ctx *ctx}
%union
{
struct hlsl_type *type;
INT intval;
FLOAT floatval;
BOOL boolval;
char *name;
DWORD modifiers;
struct hlsl_ir_node *instr;
struct list *list;
struct parse_function function;
struct parse_parameter parameter;
struct parse_initializer initializer;
struct parse_array_sizes arrays;
struct parse_variable_def *variable_def;
struct parse_if_body if_body;
enum parse_assign_op assign_op;
struct hlsl_reg_reservation reg_reservation;
struct parse_colon_attribute colon_attribute;
struct hlsl_semantic semantic;
enum hlsl_buffer_type buffer_type;
enum hlsl_sampler_dim sampler_dim;
}
%token KW_BLENDSTATE
%token KW_BREAK
%token KW_BUFFER
%token KW_CBUFFER
%token KW_COLUMN_MAJOR
%token KW_COMPILE
%token KW_CONST
%token KW_CONTINUE
%token KW_DEPTHSTENCILSTATE
%token KW_DEPTHSTENCILVIEW
%token KW_DISCARD
%token KW_DO
%token KW_DOUBLE
%token KW_ELSE
%token KW_EXTERN
%token KW_FALSE
%token KW_FOR
%token KW_GEOMETRYSHADER
%token KW_GROUPSHARED
%token KW_IF
%token KW_IN
%token KW_INLINE
%token KW_INOUT
%token KW_MATRIX
%token KW_NAMESPACE
%token KW_NOINTERPOLATION
%token KW_OUT
%token KW_PASS
%token KW_PIXELSHADER
%token KW_PRECISE
%token KW_RASTERIZERSTATE
%token KW_RENDERTARGETVIEW
%token KW_RETURN
%token KW_REGISTER
%token KW_ROW_MAJOR
%token KW_SAMPLER
%token KW_SAMPLER1D
%token KW_SAMPLER2D
%token KW_SAMPLER3D
%token KW_SAMPLERCUBE
%token KW_SAMPLER_STATE
%token KW_SAMPLERCOMPARISONSTATE
%token KW_SHARED
%token KW_STATEBLOCK
%token KW_STATEBLOCK_STATE
%token KW_STATIC
%token KW_STRING
%token KW_STRUCT
%token KW_SWITCH
%token KW_TBUFFER
%token KW_TECHNIQUE
%token KW_TECHNIQUE10
%token KW_TEXTURE
%token KW_TEXTURE1D
%token KW_TEXTURE1DARRAY
%token KW_TEXTURE2D
%token KW_TEXTURE2DARRAY
%token KW_TEXTURE2DMS
%token KW_TEXTURE2DMSARRAY
%token KW_TEXTURE3D
%token KW_TEXTURE3DARRAY
%token KW_TEXTURECUBE
%token KW_TRUE
%token KW_TYPEDEF
%token KW_UNIFORM
%token KW_VECTOR
%token KW_VERTEXSHADER
%token KW_VOID
%token KW_VOLATILE
%token KW_WHILE
%token OP_INC
%token OP_DEC
%token OP_AND
%token OP_OR
%token OP_EQ
%token OP_LEFTSHIFT
%token OP_LEFTSHIFTASSIGN
%token OP_RIGHTSHIFT
%token OP_RIGHTSHIFTASSIGN
%token OP_ELLIPSIS
%token OP_LE
%token OP_GE
%token OP_NE
%token OP_ADDASSIGN
%token OP_SUBASSIGN
%token OP_MULASSIGN
%token OP_DIVASSIGN
%token OP_MODASSIGN
%token OP_ANDASSIGN
%token OP_ORASSIGN
%token OP_XORASSIGN
%token OP_UNKNOWN1
%token OP_UNKNOWN2
%token OP_UNKNOWN3
%token OP_UNKNOWN4
%token <floatval> C_FLOAT
%token <intval> C_INTEGER
%token <intval> PRE_LINE
%type <list> add_expr
%type <list> assignment_expr
%type <list> bitand_expr
%type <list> bitor_expr
%type <list> bitxor_expr
%type <list> compound_statement
%type <list> conditional_expr
%type <list> declaration
%type <list> declaration_statement
%type <list> equality_expr
%type <list> expr
%type <list> expr_statement
%type <list> field
%type <list> fields_list
%type <list> initializer_expr
%type <list> jump_statement
%type <list> logicand_expr
%type <list> logicor_expr
%type <list> loop_statement
%type <list> mul_expr
%type <list> param_list
%type <list> parameters
%type <list> postfix_expr
%type <list> primary_expr
%type <list> relational_expr
%type <list> selection_statement
%type <list> shift_expr
%type <list> statement
%type <list> statement_list
%type <list> struct_declaration
%type <list> type_specs
%type <list> unary_expr
%type <list> variables_def
%type <list> variables_def_optional
%token <name> VAR_IDENTIFIER
%token <name> NEW_IDENTIFIER
%token <name> STRING
%token <name> TYPE_IDENTIFIER
%type <arrays> arrays
%type <assign_op> assign_op
%type <boolval> boolean
%type <buffer_type> buffer_type
%type <colon_attribute> colon_attribute
%type <function> func_declaration
%type <function> func_prototype
%type <initializer> complex_initializer
%type <initializer> func_arguments
%type <initializer> initializer_expr_list
%type <if_body> if_body
%type <modifiers> input_mod
%type <modifiers> input_mods
%type <modifiers> var_modifiers
%type <name> any_identifier
%type <name> var_identifier
%type <parameter> parameter
%type <reg_reservation> register_opt
%type <sampler_dim> texture_type
%type <semantic> semantic
%type <type> field_type
%type <type> named_struct_spec
%type <type> unnamed_struct_spec
%type <type> struct_spec
%type <type> type
%type <type> typedef_type
%type <variable_def> type_spec
%type <variable_def> variable_decl
%type <variable_def> variable_def
%%
hlsl_prog:
%empty
| hlsl_prog func_declaration
{
const struct hlsl_ir_function_decl *decl;
decl = get_func_decl(&ctx->functions, $2.name, $2.decl->parameters);
if (decl && !decl->func->intrinsic)
{
if (decl->body && $2.decl->body)
{
hlsl_error(ctx, $2.decl->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED,
"Function \"%s\" is already defined.", $2.name);
hlsl_note(ctx, decl->loc, VKD3D_SHADER_LOG_ERROR, "\"%s\" was previously defined here.", $2.name);
YYABORT;
}
else if (!hlsl_types_are_equal(decl->return_type, $2.decl->return_type))
{
hlsl_error(ctx, $2.decl->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED,
"Function \"%s\" was already declared with a different return type.", $2.name);
hlsl_note(ctx, decl->loc, VKD3D_SHADER_LOG_ERROR, "\"%s\" was previously declared here.", $2.name);
YYABORT;
}
}
hlsl_add_function(ctx, $2.name, $2.decl, false);
}
| hlsl_prog buffer_declaration buffer_body
| hlsl_prog declaration_statement
{
if (!list_empty($2))
hlsl_fixme(ctx, @2, "Uniform initializer.");
destroy_instr_list($2);
}
| hlsl_prog preproc_directive
| hlsl_prog ';'
buffer_declaration:
buffer_type any_identifier colon_attribute
{
if ($3.semantic.name)
hlsl_error(ctx, @3, VKD3D_SHADER_ERROR_HLSL_INVALID_SEMANTIC, "Semantics are not allowed on buffers.");
if (!(ctx->cur_buffer = hlsl_new_buffer(ctx, $1, $2, &$3.reg_reservation, @2)))
YYABORT;
}
buffer_body:
'{' declaration_statement_list '}'
{
ctx->cur_buffer = ctx->globals_buffer;
}
buffer_type:
KW_CBUFFER
{
$$ = HLSL_BUFFER_CONSTANT;
}
| KW_TBUFFER
{
$$ = HLSL_BUFFER_TEXTURE;
}
declaration_statement_list:
declaration_statement
| declaration_statement_list declaration_statement
preproc_directive:
PRE_LINE STRING
{
const char **new_array = NULL;
ctx->location.line = $1;
if (strcmp($2, ctx->location.source_name))
new_array = hlsl_realloc(ctx, ctx->source_files,
sizeof(*ctx->source_files) * (ctx->source_files_count + 1));
if (new_array)
{
ctx->source_files = new_array;
ctx->source_files[ctx->source_files_count++] = $2;
ctx->location.source_name = $2;
}
else
{
vkd3d_free($2);
}
}
struct_declaration:
var_modifiers struct_spec variables_def_optional ';'
{
struct hlsl_type *type;
DWORD modifiers = $1;
if (!$3)
{
if (!$2->name)
hlsl_error(ctx, @2, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX,
"Anonymous struct type must declare a variable.");
if (modifiers)
hlsl_error(ctx, @1, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Modifiers are not allowed on struct type declataions.");
}
if (!(type = apply_type_modifiers(ctx, $2, &modifiers, @1)))
YYABORT;
$$ = declare_vars(ctx, type, modifiers, $3);
}
struct_spec:
named_struct_spec
| unnamed_struct_spec
named_struct_spec:
KW_STRUCT any_identifier '{' fields_list '}'
{
bool ret;
$$ = hlsl_new_struct_type(ctx, $2, $4);
if (hlsl_get_var(ctx->cur_scope, $2))
{
hlsl_error(ctx, @2, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "\"%s\" is already declared as a variable.", $2);
YYABORT;
}
ret = hlsl_scope_add_type(ctx->cur_scope, $$);
if (!ret)
{
hlsl_error(ctx, @2, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "Struct \"%s\" is already defined.", $2);
YYABORT;
}
}
unnamed_struct_spec:
KW_STRUCT '{' fields_list '}'
{
$$ = hlsl_new_struct_type(ctx, NULL, $3);
}
any_identifier:
VAR_IDENTIFIER
| TYPE_IDENTIFIER
| NEW_IDENTIFIER
fields_list:
%empty
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
}
| fields_list field
{
struct hlsl_struct_field *field, *next, *existing;
$$ = $1;
LIST_FOR_EACH_ENTRY_SAFE(field, next, $2, struct hlsl_struct_field, entry)
{
if ((existing = get_struct_field($$, field->name)))
{
hlsl_error(ctx, @2, VKD3D_SHADER_ERROR_HLSL_REDEFINED,
"Field \"%s\" is already defined.", field->name);
hlsl_note(ctx, existing->loc, VKD3D_SHADER_LOG_ERROR,
"'%s' was previously defined here.", field->name);
vkd3d_free(field);
}
else
{
list_add_tail($$, &field->entry);
}
}
vkd3d_free($2);
}
field_type:
type
| unnamed_struct_spec
field:
var_modifiers field_type variables_def ';'
{
struct hlsl_type *type;
DWORD modifiers = $1;
if (!(type = apply_type_modifiers(ctx, $2, &modifiers, @1)))
YYABORT;
if (modifiers)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_modifiers_to_string(ctx, modifiers)))
hlsl_error(ctx, @1, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Modifiers '%s' are not allowed on struct fields.", string->buffer);
hlsl_release_string_buffer(ctx, string);
}
$$ = gen_struct_fields(ctx, type, $3);
}
func_declaration:
func_prototype compound_statement
{
$$ = $1;
$$.decl->body = $2;
hlsl_pop_scope(ctx);
}
| func_prototype ';'
{
$$ = $1;
hlsl_pop_scope(ctx);
}
func_prototype:
/* var_modifiers is necessary to avoid shift/reduce conflicts. */
var_modifiers type var_identifier '(' parameters ')' colon_attribute
{
struct hlsl_ir_var *var;
if ($1)
{
hlsl_error(ctx, @1, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Modifiers are not allowed on functions.");
YYABORT;
}
if ((var = hlsl_get_var(ctx->globals, $3)))
{
hlsl_error(ctx, @3, VKD3D_SHADER_ERROR_HLSL_REDEFINED,
"\"%s\" is already declared as a variable.", $3);
hlsl_note(ctx, var->loc, VKD3D_SHADER_LOG_ERROR,
"\"%s\" was previously declared here.", $3);
YYABORT;
}
if (hlsl_types_are_equal($2, ctx->builtin_types.Void) && $7.semantic.name)
{
hlsl_error(ctx, @7, VKD3D_SHADER_ERROR_HLSL_INVALID_SEMANTIC,
"Semantics are not allowed on void functions.");
}
if ($7.reg_reservation.type)
FIXME("Unexpected register reservation for a function.\n");
if (!($$.decl = hlsl_new_func_decl(ctx, $2, $5, &$7.semantic, @3)))
YYABORT;
$$.name = $3;
ctx->cur_function = $$.decl;
}
compound_statement:
'{' '}'
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
}
| '{' scope_start statement_list '}'
{
hlsl_pop_scope(ctx);
$$ = $3;
}
scope_start:
%empty
{
hlsl_push_scope(ctx);
}
var_identifier:
VAR_IDENTIFIER
| NEW_IDENTIFIER
colon_attribute:
%empty
{
$$.semantic.name = NULL;
$$.reg_reservation.type = 0;
}
| semantic
{
$$.semantic = $1;
$$.reg_reservation.type = 0;
}
| register_opt
{
$$.semantic.name = NULL;
$$.reg_reservation = $1;
}
semantic:
':' any_identifier
{
char *p;
for (p = $2 + strlen($2); p > $2 && isdigit(p[-1]); --p)
;
$$.name = $2;
$$.index = atoi(p);
*p = 0;
}
/* FIXME: Writemasks */
register_opt:
':' KW_REGISTER '(' any_identifier ')'
{
$$ = parse_reg_reservation($4);
vkd3d_free($4);
}
| ':' KW_REGISTER '(' any_identifier ',' any_identifier ')'
{
FIXME("Ignoring shader target %s in a register reservation.\n", debugstr_a($4));
vkd3d_free($4);
$$ = parse_reg_reservation($6);
vkd3d_free($6);
}
parameters:
scope_start
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
}
| scope_start param_list
{
$$ = $2;
}
param_list:
parameter
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
if (!add_func_parameter(ctx, $$, &$1, @1))
{
ERR("Error adding function parameter %s.\n", $1.name);
YYABORT;
}
}
| param_list ',' parameter
{
$$ = $1;
if (!add_func_parameter(ctx, $$, &$3, @3))
{
hlsl_error(ctx, @3, VKD3D_SHADER_ERROR_HLSL_REDEFINED,
"Parameter \"%s\" is already declared.", $3.name);
YYABORT;
}
}
parameter:
input_mods var_modifiers type any_identifier colon_attribute
{
struct hlsl_type *type;
DWORD modifiers = $2;
if (!(type = apply_type_modifiers(ctx, $3, &modifiers, @2)))
YYABORT;
$$.modifiers = $1 ? $1 : HLSL_STORAGE_IN;
$$.modifiers |= modifiers;
$$.type = type;
$$.name = $4;
$$.semantic = $5.semantic;
$$.reg_reservation = $5.reg_reservation;
}
input_mods:
%empty
{
$$ = 0;
}
| input_mods input_mod
{
if ($1 & $2)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_modifiers_to_string(ctx, $2)))
hlsl_error(ctx, @2, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Modifier \"%s\" was already specified.", string->buffer);
hlsl_release_string_buffer(ctx, string);
YYABORT;
}
$$ = $1 | $2;
}
input_mod:
KW_IN
{
$$ = HLSL_STORAGE_IN;
}
| KW_OUT
{
$$ = HLSL_STORAGE_OUT;
}
| KW_INOUT
{
$$ = HLSL_STORAGE_IN | HLSL_STORAGE_OUT;
}
texture_type:
KW_TEXTURE1D
{
$$ = HLSL_SAMPLER_DIM_1D;
}
| KW_TEXTURE2D
{
$$ = HLSL_SAMPLER_DIM_2D;
}
| KW_TEXTURE3D
{
$$ = HLSL_SAMPLER_DIM_3D;
}
| KW_TEXTURECUBE
{
$$ = HLSL_SAMPLER_DIM_CUBE;
}
type:
KW_VECTOR '<' type ',' C_INTEGER '>'
{
if ($3->type != HLSL_CLASS_SCALAR)
{
struct vkd3d_string_buffer *string;
string = hlsl_type_to_string(ctx, $3);
if (string)
hlsl_error(ctx, @3, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Vector base type %s is not scalar.", string->buffer);
hlsl_release_string_buffer(ctx, string);
YYABORT;
}
if ($5 < 1 || $5 > 4)
{
hlsl_error(ctx, @5, VKD3D_SHADER_ERROR_HLSL_INVALID_SIZE,
"Vector size %d is not between 1 and 4.", $5);
YYABORT;
}
$$ = hlsl_get_vector_type(ctx, $3->base_type, $5);
}
| KW_MATRIX '<' type ',' C_INTEGER ',' C_INTEGER '>'
{
if ($3->type != HLSL_CLASS_SCALAR)
{
struct vkd3d_string_buffer *string;
string = hlsl_type_to_string(ctx, $3);
if (string)
hlsl_error(ctx, @3, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Matrix base type %s is not scalar.", string->buffer);
hlsl_release_string_buffer(ctx, string);
YYABORT;
}
if ($5 < 1 || $5 > 4)
{
hlsl_error(ctx, @5, VKD3D_SHADER_ERROR_HLSL_INVALID_SIZE,
"Matrix row count %d is not between 1 and 4.", $5);
YYABORT;
}
if ($7 < 1 || $7 > 4)
{
hlsl_error(ctx, @7, VKD3D_SHADER_ERROR_HLSL_INVALID_SIZE,
"Matrix column count %d is not between 1 and 4.", $7);
YYABORT;
}
$$ = hlsl_get_matrix_type(ctx, $3->base_type, $7, $5);
}
| KW_VOID
{
$$ = ctx->builtin_types.Void;
}
| KW_SAMPLER
{
$$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_GENERIC];
}
| KW_SAMPLER1D
{
$$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_1D];
}
| KW_SAMPLER2D
{
$$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_2D];
}
| KW_SAMPLER3D
{
$$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_3D];
}
| KW_SAMPLERCUBE
{
$$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_3D];
}
| KW_TEXTURE
{
$$ = hlsl_new_texture_type(ctx, HLSL_SAMPLER_DIM_GENERIC, NULL);
}
| texture_type
{
$$ = hlsl_new_texture_type(ctx, $1, hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, 4));
}
| texture_type '<' type '>'
{
if ($3->type > HLSL_CLASS_VECTOR)
{
struct vkd3d_string_buffer *string;
string = hlsl_type_to_string(ctx, $3);
if (string)
hlsl_error(ctx, @3, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Texture data type %s is not scalar or vector.\n", string->buffer);
hlsl_release_string_buffer(ctx, string);
}
$$ = hlsl_new_texture_type(ctx, $1, $3);
}
| TYPE_IDENTIFIER
{
$$ = hlsl_get_type(ctx->cur_scope, $1, true);
vkd3d_free($1);
}
| KW_STRUCT TYPE_IDENTIFIER
{
$$ = hlsl_get_type(ctx->cur_scope, $2, true);
if ($$->type != HLSL_CLASS_STRUCT)
hlsl_error(ctx, @1, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "\"%s\" redefined as a structure.", $2);
vkd3d_free($2);
}
declaration_statement:
declaration
| struct_declaration
| typedef
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
}
typedef_type:
type
| struct_spec
typedef:
KW_TYPEDEF var_modifiers typedef_type type_specs ';'
{
if ($2 & ~HLSL_TYPE_MODIFIERS_MASK)
{
struct parse_variable_def *v, *v_next;
hlsl_error(ctx, @1, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Storage modifiers are not allowed on typedefs.");
LIST_FOR_EACH_ENTRY_SAFE(v, v_next, $4, struct parse_variable_def, entry)
vkd3d_free(v);
vkd3d_free($4);
YYABORT;
}
if (!add_typedef(ctx, $2, $3, $4))
YYABORT;
}
type_specs:
type_spec
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
list_add_head($$, &$1->entry);
}
| type_specs ',' type_spec
{
$$ = $1;
list_add_tail($$, &$3->entry);
}
type_spec:
any_identifier arrays
{
$$ = hlsl_alloc(ctx, sizeof(*$$));
$$->loc = @1;
$$->name = $1;
$$->arrays = $2;
}
declaration:
var_modifiers type variables_def ';'
{
struct hlsl_type *type;
DWORD modifiers = $1;
if (!(type = apply_type_modifiers(ctx, $2, &modifiers, @1)))
YYABORT;
$$ = declare_vars(ctx, type, modifiers, $3);
}
variables_def_optional:
%empty
{
$$ = NULL;
}
| variables_def
variables_def:
variable_def
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
list_add_head($$, &$1->entry);
}
| variables_def ',' variable_def
{
$$ = $1;
list_add_tail($$, &$3->entry);
}
variable_decl:
any_identifier arrays colon_attribute
{
$$ = hlsl_alloc(ctx, sizeof(*$$));
$$->loc = @1;
$$->name = $1;
$$->arrays = $2;
$$->semantic = $3.semantic;
$$->reg_reservation = $3.reg_reservation;
}
state:
any_identifier '=' expr ';'
{
vkd3d_free($1);
hlsl_free_instr_list($3);
}
state_block_start:
%empty
{
ctx->in_state_block = 1;
}
state_block:
%empty
| state_block state
variable_def:
variable_decl
| variable_decl '=' complex_initializer
{
$$ = $1;
$$->initializer = $3;
}
| variable_decl '{' state_block_start state_block '}'
{
$$ = $1;
ctx->in_state_block = 0;
}
arrays:
%empty
{
$$.sizes = NULL;
$$.count = 0;
}
| '[' expr ']' arrays
{
unsigned int size = evaluate_array_dimension(node_from_list($2));
uint32_t *new_array;
destroy_instr_list($2);
$$ = $4;
if (!size)
{
hlsl_error(ctx, @2, VKD3D_SHADER_ERROR_HLSL_INVALID_SIZE,
"Array size is not a positive integer constant.");
vkd3d_free($$.sizes);
YYABORT;
}
if (size > 65536)
{
hlsl_error(ctx, @2, VKD3D_SHADER_ERROR_HLSL_INVALID_SIZE,
"Array size %u is not between 1 and 65536.", size);
vkd3d_free($$.sizes);
YYABORT;
}
if (!(new_array = hlsl_realloc(ctx, $$.sizes, ($$.count + 1) * sizeof(*new_array))))
{
vkd3d_free($$.sizes);
YYABORT;
}
$$.sizes = new_array;
$$.sizes[$$.count++] = size;
}
var_modifiers:
%empty
{
$$ = 0;
}
| KW_EXTERN var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_STORAGE_EXTERN, @1);
}
| KW_NOINTERPOLATION var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_STORAGE_NOINTERPOLATION, @1);
}
| KW_PRECISE var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_MODIFIER_PRECISE, @1);
}
| KW_SHARED var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_STORAGE_SHARED, @1);
}
| KW_GROUPSHARED var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_STORAGE_GROUPSHARED, @1);
}
| KW_STATIC var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_STORAGE_STATIC, @1);
}
| KW_UNIFORM var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_STORAGE_UNIFORM, @1);
}
| KW_VOLATILE var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_STORAGE_VOLATILE, @1);
}
| KW_CONST var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_MODIFIER_CONST, @1);
}
| KW_ROW_MAJOR var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_MODIFIER_ROW_MAJOR, @1);
}
| KW_COLUMN_MAJOR var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_MODIFIER_COLUMN_MAJOR, @1);
}
complex_initializer:
initializer_expr
{
$$.args_count = 1;
if (!($$.args = hlsl_alloc(ctx, sizeof(*$$.args))))
YYABORT;
$$.args[0] = node_from_list($1);
$$.instrs = $1;
}
| '{' initializer_expr_list '}'
{
$$ = $2;
}
| '{' initializer_expr_list ',' '}'
{
$$ = $2;
}
initializer_expr:
assignment_expr
initializer_expr_list:
initializer_expr
{
$$.args_count = 1;
if (!($$.args = hlsl_alloc(ctx, sizeof(*$$.args))))
YYABORT;
$$.args[0] = node_from_list($1);
$$.instrs = $1;
}
| initializer_expr_list ',' initializer_expr
{
$$ = $1;
if (!($$.args = hlsl_realloc(ctx, $$.args, ($$.args_count + 1) * sizeof(*$$.args))))
YYABORT;
$$.args[$$.args_count++] = node_from_list($3);
list_move_tail($$.instrs, $3);
vkd3d_free($3);
}
boolean:
KW_TRUE
{
$$ = TRUE;
}
| KW_FALSE
{
$$ = FALSE;
}
statement_list:
statement
| statement_list statement
{
$$ = $1;
list_move_tail($$, $2);
vkd3d_free($2);
}
statement:
declaration_statement
| expr_statement
| compound_statement
| jump_statement
| selection_statement
| loop_statement
jump_statement:
KW_RETURN expr ';'
{
if (!add_return(ctx, $2, node_from_list($2), @1))
YYABORT;
$$ = $2;
}
| KW_RETURN ';'
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
if (!add_return(ctx, $$, NULL, @1))
YYABORT;
}
selection_statement:
KW_IF '(' expr ')' if_body
{
struct hlsl_ir_node *condition = node_from_list($3);
struct hlsl_ir_if *instr;
if (!(instr = hlsl_new_if(ctx, condition, @1)))
YYABORT;
list_move_tail(&instr->then_instrs, $5.then_instrs);
if ($5.else_instrs)
list_move_tail(&instr->else_instrs, $5.else_instrs);
vkd3d_free($5.then_instrs);
vkd3d_free($5.else_instrs);
if (condition->data_type->dimx > 1 || condition->data_type->dimy > 1)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, condition->data_type)))
hlsl_error(ctx, instr->node.loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"if condition type %s is not scalar.", string->buffer);
hlsl_release_string_buffer(ctx, string);
}
$$ = $3;
list_add_tail($$, &instr->node.entry);
}
if_body:
statement
{
$$.then_instrs = $1;
$$.else_instrs = NULL;
}
| statement KW_ELSE statement
{
$$.then_instrs = $1;
$$.else_instrs = $3;
}
loop_statement:
KW_WHILE '(' expr ')' statement
{
$$ = create_loop(ctx, LOOP_WHILE, NULL, $3, NULL, $5, @1);
}
| KW_DO statement KW_WHILE '(' expr ')' ';'
{
$$ = create_loop(ctx, LOOP_DO_WHILE, NULL, $5, NULL, $2, @1);
}
| KW_FOR '(' scope_start expr_statement expr_statement expr ')' statement
{
$$ = create_loop(ctx, LOOP_FOR, $4, $5, $6, $8, @1);
hlsl_pop_scope(ctx);
}
| KW_FOR '(' scope_start declaration expr_statement expr ')' statement
{
$$ = create_loop(ctx, LOOP_FOR, $4, $5, $6, $8, @1);
hlsl_pop_scope(ctx);
}
expr_statement:
';'
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
}
| expr ';'
{
$$ = $1;
}
func_arguments:
%empty
{
$$.args = NULL;
$$.args_count = 0;
if (!($$.instrs = make_empty_list(ctx)))
YYABORT;
}
| initializer_expr_list
primary_expr:
C_FLOAT
{
struct hlsl_ir_constant *c;
if (!(c = hlsl_alloc(ctx, sizeof(*c))))
YYABORT;
init_node(&c->node, HLSL_IR_CONSTANT, hlsl_get_scalar_type(ctx, HLSL_TYPE_FLOAT), @1);
c->value[0].f = $1;
if (!($$ = make_list(ctx, &c->node)))
YYABORT;
}
| C_INTEGER
{
struct hlsl_ir_constant *c;
if (!(c = hlsl_alloc(ctx, sizeof(*c))))
YYABORT;
init_node(&c->node, HLSL_IR_CONSTANT, hlsl_get_scalar_type(ctx, HLSL_TYPE_INT), @1);
c->value[0].i = $1;
if (!($$ = make_list(ctx, &c->node)))
YYABORT;
}
| boolean
{
struct hlsl_ir_constant *c;
if (!(c = hlsl_alloc(ctx, sizeof(*c))))
YYABORT;
init_node(&c->node, HLSL_IR_CONSTANT, hlsl_get_scalar_type(ctx, HLSL_TYPE_BOOL), @1);
c->value[0].b = $1;
if (!($$ = make_list(ctx, &c->node)))
YYABORT;
}
| VAR_IDENTIFIER
{
struct hlsl_ir_load *load;
struct hlsl_ir_var *var;
if (!(var = hlsl_get_var(ctx->cur_scope, $1)))
{
hlsl_error(ctx, @1, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED, "Variable \"%s\" is not defined.", $1);
YYABORT;
}
if (!(load = hlsl_new_var_load(ctx, var, @1)))
YYABORT;
if (!($$ = make_list(ctx, &load->node)))
YYABORT;
}
| '(' expr ')'
{
$$ = $2;
}
| var_identifier '(' func_arguments ')'
{
if (!($$ = add_call(ctx, $1, &$3, @1)))
YYABORT;
}
| NEW_IDENTIFIER
{
if (ctx->in_state_block)
{
struct hlsl_ir_load *load;
struct hlsl_ir_var *var;
if (!(var = hlsl_new_synthetic_var(ctx, "<state-block-expr>",
hlsl_get_scalar_type(ctx, HLSL_TYPE_INT), @1)))
YYABORT;
if (!(load = hlsl_new_var_load(ctx, var, @1)))
YYABORT;
if (!($$ = make_list(ctx, &load->node)))
YYABORT;
}
else
{
hlsl_error(ctx, @1, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED, "Identifier \"%s\" is not declared.\n", $1);
YYABORT;
}
}
postfix_expr:
primary_expr
| postfix_expr OP_INC
{
if (!add_increment(ctx, $1, false, true, @2))
{
destroy_instr_list($1);
YYABORT;
}
$$ = $1;
}
| postfix_expr OP_DEC
{
if (!add_increment(ctx, $1, true, true, @2))
{
destroy_instr_list($1);
YYABORT;
}
$$ = $1;
}
| postfix_expr '.' any_identifier
{
struct hlsl_ir_node *node = node_from_list($1);
if (node->data_type->type == HLSL_CLASS_STRUCT)
{
struct hlsl_type *type = node->data_type;
struct hlsl_struct_field *field;
if (!(field = get_struct_field(type->e.elements, $3)))
{
hlsl_error(ctx, @3, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED, "Field \"%s\" is not defined.", $3);
YYABORT;
}
if (!add_record_load(ctx, $1, node, field, @2))
YYABORT;
$$ = $1;
}
else if (node->data_type->type <= HLSL_CLASS_LAST_NUMERIC)
{
struct hlsl_ir_swizzle *swizzle;
if (!(swizzle = get_swizzle(ctx, node, $3, &@3)))
{
hlsl_error(ctx, @3, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Invalid swizzle \"%s\".", $3);
YYABORT;
}
list_add_tail($1, &swizzle->node.entry);
$$ = $1;
}
else
{
hlsl_error(ctx, @3, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Invalid subscript \"%s\".", $3);
YYABORT;
}
}
| postfix_expr '[' expr ']'
{
struct hlsl_ir_node *array = node_from_list($1), *index = node_from_list($3);
struct hlsl_ir_expr *cast;
list_move_tail($1, $3);
vkd3d_free($3);
if (index->data_type->type != HLSL_CLASS_SCALAR)
{
hlsl_error(ctx, @3, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Array index is not scalar.");
destroy_instr_list($1);
YYABORT;
}
if (!(cast = hlsl_new_cast(ctx, index, hlsl_get_scalar_type(ctx, HLSL_TYPE_UINT), &index->loc)))
{
destroy_instr_list($1);
YYABORT;
}
list_add_tail($1, &cast->node.entry);
if (!add_array_load(ctx, $1, array, &cast->node, @2))
{
destroy_instr_list($1);
YYABORT;
}
$$ = $1;
}
/* var_modifiers is necessary to avoid shift/reduce conflicts. */
| var_modifiers type '(' initializer_expr_list ')'
{
if ($1)
{
hlsl_error(ctx, @1, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Modifiers are not allowed on constructors.");
free_parse_initializer(&$4);
YYABORT;
}
if ($2->type > HLSL_CLASS_LAST_NUMERIC)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, $2)))
hlsl_error(ctx, @2, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Constructor data type %s is not numeric.", string->buffer);
hlsl_release_string_buffer(ctx, string);
free_parse_initializer(&$4);
YYABORT;
}
if ($2->dimx * $2->dimy != initializer_size(&$4))
{
hlsl_error(ctx, @4, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
"Expected %u components in constructor, but got %u.",
$2->dimx * $2->dimy, initializer_size(&$4));
free_parse_initializer(&$4);
YYABORT;
}
if (!($$ = add_constructor(ctx, $2, &$4, @2)))
{
free_parse_initializer(&$4);
YYABORT;
}
}
| postfix_expr '.' any_identifier '(' func_arguments ')'
{
struct hlsl_ir_node *object = node_from_list($1);
list_move_tail($1, $5.instrs);
vkd3d_free($5.instrs);
if (!add_method_call(ctx, $1, object, $3, &$5, &@3))
{
hlsl_free_instr_list($1);
vkd3d_free($5.args);
YYABORT;
}
vkd3d_free($5.args);
$$ = $1;
}
unary_expr:
postfix_expr
| OP_INC unary_expr
{
if (!add_increment(ctx, $2, false, false, @1))
{
destroy_instr_list($2);
YYABORT;
}
$$ = $2;
}
| OP_DEC unary_expr
{
if (!add_increment(ctx, $2, true, false, @1))
{
destroy_instr_list($2);
YYABORT;
}
$$ = $2;
}
| '+' unary_expr
{
$$ = $2;
}
| '-' unary_expr
{
$$ = add_unary_expr(ctx, $2, HLSL_OP1_NEG, @1);
}
| '~' unary_expr
{
$$ = add_unary_expr(ctx, $2, HLSL_OP1_BIT_NOT, @1);
}
| '!' unary_expr
{
$$ = add_unary_expr(ctx, $2, HLSL_OP1_LOGIC_NOT, @1);
}
/* var_modifiers is necessary to avoid shift/reduce conflicts. */
| '(' var_modifiers type arrays ')' unary_expr
{
struct hlsl_type *src_type = node_from_list($6)->data_type;
struct hlsl_type *dst_type;
struct hlsl_ir_expr *cast;
unsigned int i;
if ($2)
{
hlsl_error(ctx, @2, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Modifiers are not allowed on casts.");
YYABORT;
}
dst_type = $3;
for (i = 0; i < $4.count; ++i)
dst_type = hlsl_new_array_type(ctx, dst_type, $4.sizes[i]);
if (!compatible_data_types(src_type, dst_type))
{
struct vkd3d_string_buffer *src_string, *dst_string;
src_string = hlsl_type_to_string(ctx, src_type);
dst_string = hlsl_type_to_string(ctx, dst_type);
if (src_string && dst_string)
hlsl_error(ctx, @3, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Can't cast from %s to %s.",
src_string->buffer, dst_string->buffer);
hlsl_release_string_buffer(ctx, src_string);
hlsl_release_string_buffer(ctx, dst_string);
YYABORT;
}
if (!(cast = hlsl_new_cast(ctx, node_from_list($6), dst_type, &@3)))
{
hlsl_free_instr_list($6);
YYABORT;
}
list_add_tail($6, &cast->node.entry);
$$ = $6;
}
mul_expr:
unary_expr
| mul_expr '*' unary_expr
{
$$ = add_binary_expr(ctx, $1, $3, HLSL_OP2_MUL, @2);
}
| mul_expr '/' unary_expr
{
$$ = add_binary_expr(ctx, $1, $3, HLSL_OP2_DIV, @2);
}
| mul_expr '%' unary_expr
{
$$ = add_binary_expr(ctx, $1, $3, HLSL_OP2_MOD, @2);
}
add_expr:
mul_expr
| add_expr '+' mul_expr
{
$$ = add_binary_expr(ctx, $1, $3, HLSL_OP2_ADD, @2);
}
| add_expr '-' mul_expr
{
struct hlsl_ir_node *neg;
if (!(neg = hlsl_new_unary_expr(ctx, HLSL_OP1_NEG, node_from_list($3), @2)))
YYABORT;
list_add_tail($3, &neg->entry);
$$ = add_binary_expr(ctx, $1, $3, HLSL_OP2_ADD, @2);
}
shift_expr:
add_expr
| shift_expr OP_LEFTSHIFT add_expr
{
hlsl_fixme(ctx, @$, "Left shift.");
}
| shift_expr OP_RIGHTSHIFT add_expr
{
hlsl_fixme(ctx, @$, "Right shift.");
}
relational_expr:
shift_expr
| relational_expr '<' shift_expr
{
$$ = add_binary_expr(ctx, $1, $3, HLSL_OP2_LESS, @2);
}
| relational_expr '>' shift_expr
{
$$ = add_binary_expr(ctx, $1, $3, HLSL_OP2_GREATER, @2);
}
| relational_expr OP_LE shift_expr
{
$$ = add_binary_expr(ctx, $1, $3, HLSL_OP2_LEQUAL, @2);
}
| relational_expr OP_GE shift_expr
{
$$ = add_binary_expr(ctx, $1, $3, HLSL_OP2_GEQUAL, @2);
}
equality_expr:
relational_expr
| equality_expr OP_EQ relational_expr
{
$$ = add_binary_expr(ctx, $1, $3, HLSL_OP2_EQUAL, @2);
}
| equality_expr OP_NE relational_expr
{
$$ = add_binary_expr(ctx, $1, $3, HLSL_OP2_NEQUAL, @2);
}
bitand_expr:
equality_expr
| bitand_expr '&' equality_expr
{
hlsl_fixme(ctx, @$, "Bitwise AND.");
}
bitxor_expr:
bitand_expr
| bitxor_expr '^' bitand_expr
{
hlsl_fixme(ctx, @$, "Bitwise XOR.");
}
bitor_expr:
bitxor_expr
| bitor_expr '|' bitxor_expr
{
hlsl_fixme(ctx, @$, "Bitwise OR.");
}
logicand_expr:
bitor_expr
| logicand_expr OP_AND bitor_expr
{
hlsl_fixme(ctx, @$, "Logical AND.");
}
logicor_expr:
logicand_expr
| logicor_expr OP_OR logicand_expr
{
hlsl_fixme(ctx, @$, "Logical OR.");
}
conditional_expr:
logicor_expr
| logicor_expr '?' expr ':' assignment_expr
{
hlsl_fixme(ctx, @$, "Ternary operator.");
}
assignment_expr:
conditional_expr
| unary_expr assign_op assignment_expr
{
struct hlsl_ir_node *lhs = node_from_list($1), *rhs = node_from_list($3);
if (lhs->data_type->modifiers & HLSL_MODIFIER_CONST)
{
hlsl_error(ctx, @2, VKD3D_SHADER_ERROR_HLSL_MODIFIES_CONST, "Statement modifies a const expression.");
YYABORT;
}
list_move_tail($3, $1);
vkd3d_free($1);
if (!add_assignment(ctx, $3, lhs, $2, rhs))
YYABORT;
$$ = $3;
}
assign_op:
'='
{
$$ = ASSIGN_OP_ASSIGN;
}
| OP_ADDASSIGN
{
$$ = ASSIGN_OP_ADD;
}
| OP_SUBASSIGN
{
$$ = ASSIGN_OP_SUB;
}
| OP_MULASSIGN
{
$$ = ASSIGN_OP_MUL;
}
| OP_DIVASSIGN
{
$$ = ASSIGN_OP_DIV;
}
| OP_MODASSIGN
{
$$ = ASSIGN_OP_MOD;
}
| OP_LEFTSHIFTASSIGN
{
$$ = ASSIGN_OP_LSHIFT;
}
| OP_RIGHTSHIFTASSIGN
{
$$ = ASSIGN_OP_RSHIFT;
}
| OP_ANDASSIGN
{
$$ = ASSIGN_OP_AND;
}
| OP_ORASSIGN
{
$$ = ASSIGN_OP_OR;
}
| OP_XORASSIGN
{
$$ = ASSIGN_OP_XOR;
}
expr:
assignment_expr
| expr ',' assignment_expr
{
$$ = $1;
list_move_tail($$, $3);
vkd3d_free($3);
}