This corresponds to Wine commit 9f9fec18799bf59df6211b7294f8d338caa7f4db.
Note that for the moment, compilation messages have not been ported.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- Makefile.am | 14 +- libs/vkd3d-shader/hlsl.c | 1672 ++++++++++++ libs/vkd3d-shader/hlsl.h | 644 +++++ libs/vkd3d-shader/hlsl.l | 288 +++ libs/vkd3d-shader/hlsl.y | 2961 ++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_main.c | 13 +- libs/vkd3d-shader/vkd3d_shader_private.h | 9 + 7 files changed, 5598 insertions(+), 3 deletions(-) create mode 100644 libs/vkd3d-shader/hlsl.c create mode 100644 libs/vkd3d-shader/hlsl.h create mode 100644 libs/vkd3d-shader/hlsl.l create mode 100644 libs/vkd3d-shader/hlsl.y
diff --git a/Makefile.am b/Makefile.am index 27d933c9..b9e8b16f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -126,9 +126,20 @@ libs/vkd3d-shader/preproc.tab.c libs/vkd3d-shader/preproc.tab.h &: libs/vkd3d-sh @$(MKDIR_P) libs/vkd3d-shader $(VKD3D_V_BISON)$(BISON) $(YFLAGS) -d -o libs/vkd3d-shader/preproc.tab.c $<
-BUILT_SOURCES += libs/vkd3d-shader/preproc.tab.h +libs/vkd3d-shader/hlsl.yy.c: $(srcdir)/libs/vkd3d-shader/hlsl.l + $(VKD3D_V_FLEX)$(FLEX) $(LFLAGS) -o $@ $< + +libs/vkd3d-shader/hlsl.tab.c libs/vkd3d-shader/hlsl.tab.h &: libs/vkd3d-shader/hlsl.y + $(VKD3D_V_BISON)$(BISON) $(YFLAGS) -d -o libs/vkd3d-shader/hlsl.tab.c $< + +BUILT_SOURCES += \ + libs/vkd3d-shader/hlsl.tab.h \ + libs/vkd3d-shader/preproc.tab.h
vkd3d_shader_yyfiles = \ + libs/vkd3d-shader/hlsl.tab.c \ + libs/vkd3d-shader/hlsl.tab.h \ + libs/vkd3d-shader/hlsl.yy.c \ libs/vkd3d-shader/preproc.tab.c \ libs/vkd3d-shader/preproc.tab.h \ libs/vkd3d-shader/preproc.yy.c @@ -146,6 +157,7 @@ libvkd3d_shader_la_SOURCES = \ include/vkd3d_shader.h \ libs/vkd3d-shader/checksum.c \ libs/vkd3d-shader/dxbc.c \ + libs/vkd3d-shader/hlsl.c \ libs/vkd3d-shader/spirv.c \ libs/vkd3d-shader/trace.c \ libs/vkd3d-shader/vkd3d_shader.map \ diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c new file mode 100644 index 00000000..61a9e5c5 --- /dev/null +++ b/libs/vkd3d-shader/hlsl.c @@ -0,0 +1,1672 @@ +/* + * 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 + */ + +#include "hlsl.h" + +BOOL add_declaration(struct hlsl_scope *scope, struct hlsl_ir_var *decl, BOOL local_var) +{ + struct hlsl_ir_var *var; + + LIST_FOR_EACH_ENTRY(var, &scope->vars, struct hlsl_ir_var, scope_entry) + { + if (!strcmp(decl->name, var->name)) + return FALSE; + } + if (local_var && scope->upper->upper == hlsl_ctx.globals) + { + /* Check whether the variable redefines a function parameter. */ + LIST_FOR_EACH_ENTRY(var, &scope->upper->vars, struct hlsl_ir_var, scope_entry) + { + if (!strcmp(decl->name, var->name)) + return FALSE; + } + } + + list_add_tail(&scope->vars, &decl->scope_entry); + return TRUE; +} + +struct hlsl_ir_var *get_variable(struct hlsl_scope *scope, const char *name) +{ + struct hlsl_ir_var *var; + + LIST_FOR_EACH_ENTRY(var, &scope->vars, struct hlsl_ir_var, scope_entry) + { + if (!strcmp(name, var->name)) + return var; + } + if (!scope->upper) + return NULL; + return get_variable(scope->upper, name); +} + +void free_declaration(struct hlsl_ir_var *decl) +{ + vkd3d_free((void *)decl->name); + vkd3d_free((void *)decl->semantic); + vkd3d_free((void *)decl->reg_reservation); + vkd3d_free(decl); +} + +struct hlsl_type *new_hlsl_type(const char *name, enum hlsl_type_class type_class, + enum hlsl_base_type base_type, unsigned dimx, unsigned dimy) +{ + struct hlsl_type *type; + + if (!(type = vkd3d_calloc(1, sizeof(*type)))) + return NULL; + type->name = name; + type->type = type_class; + type->base_type = base_type; + type->dimx = dimx; + type->dimy = dimy; + if (type_class == HLSL_CLASS_MATRIX) + type->reg_size = is_row_major(type) ? dimy : dimx; + else + type->reg_size = 1; + + list_add_tail(&hlsl_ctx.types, &type->entry); + + return type; +} + +struct hlsl_type *new_array_type(struct hlsl_type *basic_type, unsigned int array_size) +{ + struct hlsl_type *type = new_hlsl_type(NULL, HLSL_CLASS_ARRAY, HLSL_TYPE_FLOAT, 1, 1); + + if (!type) + return NULL; + + type->modifiers = basic_type->modifiers; + type->e.array.elements_count = array_size; + type->e.array.type = basic_type; + type->reg_size = basic_type->reg_size * array_size; + type->dimx = basic_type->dimx; + type->dimy = basic_type->dimy; + return type; +} + +struct hlsl_type *get_type(struct hlsl_scope *scope, const char *name, BOOL recursive) +{ + struct rb_entry *entry = rb_get(&scope->types, name); + + if (entry) + return RB_ENTRY_VALUE(entry, struct hlsl_type, scope_entry); + + if (recursive && scope->upper) + return get_type(scope->upper, name, recursive); + return NULL; +} + +BOOL find_function(const char *name) +{ + return rb_get(&hlsl_ctx.functions, name) != NULL; +} + +unsigned int components_count_type(struct hlsl_type *type) +{ + struct hlsl_struct_field *field; + unsigned int count = 0; + + if (type->type <= HLSL_CLASS_LAST_NUMERIC) + { + return type->dimx * type->dimy; + } + if (type->type == HLSL_CLASS_ARRAY) + { + return components_count_type(type->e.array.type) * type->e.array.elements_count; + } + if (type->type != HLSL_CLASS_STRUCT) + { + ERR("Unexpected data type %s.\n", debug_hlsl_type(type)); + return 0; + } + + LIST_FOR_EACH_ENTRY(field, type->e.elements, struct hlsl_struct_field, entry) + { + count += components_count_type(field->type); + } + return count; +} + +BOOL compare_hlsl_types(const struct hlsl_type *t1, const struct hlsl_type *t2) +{ + if (t1 == t2) + return TRUE; + + if (t1->type != t2->type) + return FALSE; + if (t1->base_type != t2->base_type) + return FALSE; + if (t1->base_type == HLSL_TYPE_SAMPLER && t1->sampler_dim != t2->sampler_dim) + return FALSE; + if ((t1->modifiers & HLSL_MODIFIERS_MAJORITY_MASK) + != (t2->modifiers & HLSL_MODIFIERS_MAJORITY_MASK)) + return FALSE; + if (t1->dimx != t2->dimx) + return FALSE; + if (t1->dimy != t2->dimy) + return FALSE; + if (t1->type == HLSL_CLASS_STRUCT) + { + struct list *t1cur, *t2cur; + struct hlsl_struct_field *t1field, *t2field; + + t1cur = list_head(t1->e.elements); + t2cur = list_head(t2->e.elements); + while (t1cur && t2cur) + { + t1field = LIST_ENTRY(t1cur, struct hlsl_struct_field, entry); + t2field = LIST_ENTRY(t2cur, struct hlsl_struct_field, entry); + if (!compare_hlsl_types(t1field->type, t2field->type)) + return FALSE; + if (strcmp(t1field->name, t2field->name)) + return FALSE; + t1cur = list_next(t1->e.elements, t1cur); + t2cur = list_next(t2->e.elements, t2cur); + } + if (t1cur != t2cur) + return FALSE; + } + if (t1->type == HLSL_CLASS_ARRAY) + return t1->e.array.elements_count == t2->e.array.elements_count + && compare_hlsl_types(t1->e.array.type, t2->e.array.type); + + return TRUE; +} + +struct hlsl_type *clone_hlsl_type(struct hlsl_type *old, unsigned int default_majority) +{ + struct hlsl_struct_field *old_field, *field; + struct hlsl_type *type; + + if (!(type = vkd3d_calloc(1, sizeof(*type)))) + return NULL; + + if (old->name) + { + type->name = vkd3d_strdup(old->name); + if (!type->name) + { + vkd3d_free(type); + return NULL; + } + } + type->type = old->type; + type->base_type = old->base_type; + type->dimx = old->dimx; + type->dimy = old->dimy; + type->modifiers = old->modifiers; + if (!(type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK)) + type->modifiers |= default_majority; + type->sampler_dim = old->sampler_dim; + switch (old->type) + { + case HLSL_CLASS_ARRAY: + type->e.array.type = clone_hlsl_type(old->e.array.type, default_majority); + type->e.array.elements_count = old->e.array.elements_count; + type->reg_size = type->e.array.elements_count * type->e.array.type->reg_size; + break; + + case HLSL_CLASS_STRUCT: + { + unsigned int reg_size = 0; + + if (!(type->e.elements = vkd3d_malloc(sizeof(*type->e.elements)))) + { + vkd3d_free((void *)type->name); + vkd3d_free(type); + return NULL; + } + list_init(type->e.elements); + LIST_FOR_EACH_ENTRY(old_field, old->e.elements, struct hlsl_struct_field, entry) + { + if (!(field = vkd3d_calloc(1, sizeof(*field)))) + { + LIST_FOR_EACH_ENTRY_SAFE(field, old_field, type->e.elements, struct hlsl_struct_field, entry) + { + vkd3d_free((void *)field->semantic); + vkd3d_free((void *)field->name); + vkd3d_free(field); + } + vkd3d_free(type->e.elements); + vkd3d_free((void *)type->name); + vkd3d_free(type); + return NULL; + } + field->type = clone_hlsl_type(old_field->type, default_majority); + field->name = vkd3d_strdup(old_field->name); + if (old_field->semantic) + field->semantic = vkd3d_strdup(old_field->semantic); + field->modifiers = old_field->modifiers; + field->reg_offset = reg_size; + reg_size += field->type->reg_size; + list_add_tail(type->e.elements, &field->entry); + } + type->reg_size = reg_size; + break; + } + + case HLSL_CLASS_MATRIX: + type->reg_size = is_row_major(type) ? type->dimy : type->dimx; + break; + + default: + type->reg_size = 1; + break; + } + + list_add_tail(&hlsl_ctx.types, &type->entry); + return type; +} + +static BOOL convertible_data_type(struct hlsl_type *type) +{ + return type->type != HLSL_CLASS_OBJECT; +} + +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 (compare_hlsl_types(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 components_count_type(t1) >= components_count_type(t2); + else + return components_count_type(t1) == components_count_type(t2); + } + + if (t1->type == HLSL_CLASS_STRUCT) + return components_count_type(t1) >= components_count_type(t2); + + if (t2->type == HLSL_CLASS_ARRAY || t2->type == HLSL_CLASS_STRUCT) + return components_count_type(t1) == components_count_type(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) + && components_count_type(t1) == components_count_type(t2)) + return TRUE; + return FALSE; + } + + if (components_count_type(t1) >= components_count_type(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 components_count_type(t1) == components_count_type(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 && compare_hlsl_types(t1->e.array.type, t2)) + return TRUE; + if (components_count_type(t1) == components_count_type(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) + && components_count_type(t1) == components_count_type(t2)) + return TRUE; + return FALSE; + } + + if (t1->type == HLSL_CLASS_STRUCT && t2->type == HLSL_CLASS_STRUCT) + return compare_hlsl_types(t1, t2); + + return FALSE; +} + +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 (components_count_type(t1) == components_count_type(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) +{ + static const enum hlsl_base_type types[] = + { + HLSL_TYPE_BOOL, + HLSL_TYPE_INT, + HLSL_TYPE_UINT, + HLSL_TYPE_HALF, + HLSL_TYPE_FLOAT, + HLSL_TYPE_DOUBLE, + }; + int t1_idx = -1, t2_idx = -1, i; + + for (i = 0; i < ARRAY_SIZE(types); ++i) + { + /* Always convert away from HLSL_TYPE_HALF */ + if (t1 == types[i]) + t1_idx = t1 == HLSL_TYPE_HALF ? i + 1 : i; + if (t2 == types[i]) + t2_idx = t2 == HLSL_TYPE_HALF ? i + 1 : i; + + if (t1_idx != -1 && t2_idx != -1) + break; + } + if (t1_idx == -1 || t2_idx == -1) + { + FIXME("Unexpected base type.\n"); + return HLSL_TYPE_FLOAT; + } + return t1_idx >= t2_idx ? t1 : t2; +} + +static struct hlsl_type *expr_common_type(struct hlsl_type *t1, struct hlsl_type *t2, + struct source_location *loc) +{ + enum hlsl_type_class type; + enum hlsl_base_type base; + unsigned int dimx, dimy; + + if (t1->type > HLSL_CLASS_LAST_NUMERIC || t2->type > HLSL_CLASS_LAST_NUMERIC) + { + hlsl_report_message(*loc, HLSL_LEVEL_ERROR, "non scalar/vector/matrix data type in expression"); + return NULL; + } + + if (compare_hlsl_types(t1, t2)) + return t1; + + if (!expr_compatible_data_types(t1, t2)) + { + hlsl_report_message(*loc, HLSL_LEVEL_ERROR, "expression data types are incompatible"); + return NULL; + } + + if (t1->base_type == t2->base_type) + base = t1->base_type; + else + base = expr_common_base_type(t1->base_type, t2->base_type); + + 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 + { + /* Two vectors or a vector and a matrix (matrix must be 1xn or nx1) */ + unsigned int max_dim_1, max_dim_2; + + max_dim_1 = max(t1->dimx, t1->dimy); + max_dim_2 = max(t2->dimx, t2->dimy); + if (t1->dimx * t1->dimy == t2->dimx * t2->dimy) + { + type = HLSL_CLASS_VECTOR; + dimx = max(t1->dimx, t2->dimx); + dimy = 1; + } + else if (max_dim_1 <= max_dim_2) + { + type = t1->type; + if (type == HLSL_CLASS_VECTOR) + { + dimx = max_dim_1; + dimy = 1; + } + else + { + dimx = t1->dimx; + dimy = t1->dimy; + } + } + else + { + type = t2->type; + if (type == HLSL_CLASS_VECTOR) + { + dimx = max_dim_2; + dimy = 1; + } + else + { + dimx = t2->dimx; + dimy = t2->dimy; + } + } + } + + if (type == HLSL_CLASS_SCALAR) + return hlsl_ctx.builtin_types.scalar[base]; + if (type == HLSL_CLASS_VECTOR) + return hlsl_ctx.builtin_types.vector[base][dimx - 1]; + return new_hlsl_type(NULL, type, base, dimx, dimy); +} + +struct hlsl_ir_node *add_implicit_conversion(struct list *instrs, struct hlsl_ir_node *node, + struct hlsl_type *dst_type, struct source_location *loc) +{ + struct hlsl_type *src_type = node->data_type; + struct hlsl_ir_expr *cast; + + if (compare_hlsl_types(src_type, dst_type)) + return node; + + if (!implicit_compatible_data_types(src_type, dst_type)) + { + hlsl_report_message(*loc, HLSL_LEVEL_ERROR, "can't implicitly convert %s to %s", + debug_hlsl_type(src_type), debug_hlsl_type(dst_type)); + return NULL; + } + + if (dst_type->dimx * dst_type->dimy < src_type->dimx * src_type->dimy) + hlsl_report_message(*loc, HLSL_LEVEL_WARNING, "implicit truncation of vector type"); + + TRACE("Implicit conversion from %s to %s.\n", debug_hlsl_type(src_type), debug_hlsl_type(dst_type)); + + if (!(cast = new_cast(node, dst_type, loc))) + return NULL; + list_add_tail(instrs, &cast->node.entry); + return &cast->node; +} + +struct hlsl_ir_expr *add_expr(struct list *instrs, enum hlsl_ir_expr_op op, struct hlsl_ir_node *operands[3], + struct source_location *loc) +{ + struct hlsl_ir_expr *expr; + struct hlsl_type *type; + unsigned int i; + + type = operands[0]->data_type; + for (i = 1; i <= 2; ++i) + { + if (!operands[i]) + break; + type = expr_common_type(type, operands[i]->data_type, loc); + if (!type) + return NULL; + } + for (i = 0; i <= 2; ++i) + { + struct hlsl_ir_expr *cast; + + if (!operands[i]) + break; + if (compare_hlsl_types(operands[i]->data_type, type)) + continue; + TRACE("Implicitly converting %s into %s in an expression.\n", debug_hlsl_type(operands[i]->data_type), debug_hlsl_type(type)); + 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_report_message(operands[i]->loc, HLSL_LEVEL_WARNING, "implicit truncation of vector/matrix type"); + } + + if (!(cast = new_cast(operands[i], type, &operands[i]->loc))) + return NULL; + list_add_after(&operands[i]->entry, &cast->node.entry); + operands[i] = &cast->node; + } + + if (!(expr = vkd3d_calloc(1, sizeof(*expr)))) + return NULL; + init_node(&expr->node, HLSL_IR_EXPR, type, *loc); + expr->op = op; + for (i = 0; i <= 2; ++i) + hlsl_src_from_node(&expr->operands[i], operands[i]); + list_add_tail(instrs, &expr->node.entry); + + return expr; +} + +struct hlsl_ir_expr *new_cast(struct hlsl_ir_node *node, struct hlsl_type *type, + struct source_location *loc) +{ + struct hlsl_ir_node *cast; + + cast = new_unary_expr(HLSL_IR_UNOP_CAST, node, *loc); + if (cast) + cast->data_type = type; + return expr_from_node(cast); +} + +static enum hlsl_ir_expr_op op_from_assignment(enum parse_assign_op op) +{ + static const enum hlsl_ir_expr_op ops[] = + { + 0, + HLSL_IR_BINOP_ADD, + HLSL_IR_BINOP_SUB, + HLSL_IR_BINOP_MUL, + HLSL_IR_BINOP_DIV, + HLSL_IR_BINOP_MOD, + HLSL_IR_BINOP_LSHIFT, + HLSL_IR_BINOP_RSHIFT, + HLSL_IR_BINOP_BIT_AND, + HLSL_IR_BINOP_BIT_OR, + HLSL_IR_BINOP_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; +} + +struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lhs, + enum parse_assign_op assign_op, struct hlsl_ir_node *rhs) +{ + struct hlsl_ir_assignment *assign; + struct hlsl_type *lhs_type; + DWORD writemask = 0; + + lhs_type = lhs->data_type; + if (lhs_type->type <= HLSL_CLASS_LAST_NUMERIC) + { + writemask = (1 << lhs_type->dimx) - 1; + + if (!(rhs = add_implicit_conversion(instrs, rhs, lhs_type, &rhs->loc))) + return NULL; + } + + if (!(assign = vkd3d_malloc(sizeof(*assign)))) + return NULL; + + while (lhs->type != HLSL_IR_LOAD) + { + struct hlsl_ir_node *lhs_inner; + + if (lhs->type == HLSL_IR_EXPR && expr_from_node(lhs)->op == HLSL_IR_UNOP_CAST) + { + FIXME("Cast on the lhs.\n"); + vkd3d_free(assign); + return NULL; + } + else if (lhs->type == HLSL_IR_SWIZZLE) + { + struct hlsl_ir_swizzle *swizzle = swizzle_from_node(lhs); + const struct hlsl_type *swizzle_type = swizzle->node.data_type; + unsigned int width; + + if (lhs->data_type->type == HLSL_CLASS_MATRIX) + FIXME("Assignments with writemasks and matrices on lhs are not supported yet.\n"); + + lhs_inner = swizzle->val.node; + hlsl_src_remove(&swizzle->val); + list_remove(&lhs->entry); + + list_add_after(&rhs->entry, &lhs->entry); + hlsl_src_from_node(&swizzle->val, rhs); + if (!invert_swizzle(&swizzle->swizzle, &writemask, &width)) + { + hlsl_report_message(lhs->loc, HLSL_LEVEL_ERROR, "invalid writemask"); + vkd3d_free(assign); + return NULL; + } + assert(swizzle_type->type == HLSL_CLASS_VECTOR); + if (swizzle_type->dimx != width) + swizzle->node.data_type = hlsl_ctx.builtin_types.vector[swizzle_type->base_type][width - 1]; + rhs = &swizzle->node; + } + else + { + hlsl_report_message(lhs->loc, HLSL_LEVEL_ERROR, "invalid lvalue"); + vkd3d_free(assign); + return NULL; + } + + lhs = lhs_inner; + } + + init_node(&assign->node, HLSL_IR_ASSIGNMENT, lhs_type, lhs->loc); + assign->writemask = writemask; + assign->lhs.var = load_from_node(lhs)->src.var; + hlsl_src_from_node(&assign->lhs.offset, load_from_node(lhs)->src.offset.node); + if (assign_op != ASSIGN_OP_ASSIGN) + { + enum hlsl_ir_expr_op op = op_from_assignment(assign_op); + struct hlsl_ir_node *expr; + + TRACE("Adding an expression for the compound assignment.\n"); + expr = new_binary_expr(op, lhs, rhs); + list_add_after(&rhs->entry, &expr->entry); + rhs = expr; + } + hlsl_src_from_node(&assign->rhs, rhs); + list_add_tail(instrs, &assign->node.entry); + + return &assign->node; +} + +static int compare_hlsl_types_rb(const void *key, const struct rb_entry *entry) +{ + const struct hlsl_type *type = RB_ENTRY_VALUE(entry, const struct hlsl_type, scope_entry); + const char *name = key; + + if (name == type->name) + return 0; + + if (!name || !type->name) + { + ERR("hlsl_type without a name in a scope?\n"); + return -1; + } + return strcmp(name, type->name); +} + +void push_scope(struct hlsl_parse_ctx *ctx) +{ + struct hlsl_scope *new_scope; + + if (!(new_scope = vkd3d_malloc(sizeof(*new_scope)))) + return; + TRACE("Pushing a new scope.\n"); + list_init(&new_scope->vars); + rb_init(&new_scope->types, compare_hlsl_types_rb); + new_scope->upper = ctx->cur_scope; + ctx->cur_scope = new_scope; + list_add_tail(&ctx->scopes, &new_scope->entry); +} + +BOOL pop_scope(struct hlsl_parse_ctx *ctx) +{ + struct hlsl_scope *prev_scope = ctx->cur_scope->upper; + + if (!prev_scope) + return FALSE; + TRACE("Popping current scope.\n"); + ctx->cur_scope = prev_scope; + return TRUE; +} + +static int compare_param_hlsl_types(const struct hlsl_type *t1, const struct hlsl_type *t2) +{ + if (t1->type != t2->type) + { + if (!((t1->type == HLSL_CLASS_SCALAR && t2->type == HLSL_CLASS_VECTOR) + || (t1->type == HLSL_CLASS_VECTOR && t2->type == HLSL_CLASS_SCALAR))) + return t1->type - t2->type; + } + if (t1->base_type != t2->base_type) + return t1->base_type - t2->base_type; + if (t1->base_type == HLSL_TYPE_SAMPLER && t1->sampler_dim != t2->sampler_dim) + return t1->sampler_dim - t2->sampler_dim; + if (t1->dimx != t2->dimx) + return t1->dimx - t2->dimx; + if (t1->dimy != t2->dimy) + return t1->dimx - t2->dimx; + if (t1->type == HLSL_CLASS_STRUCT) + { + struct list *t1cur, *t2cur; + struct hlsl_struct_field *t1field, *t2field; + int r; + + t1cur = list_head(t1->e.elements); + t2cur = list_head(t2->e.elements); + while (t1cur && t2cur) + { + t1field = LIST_ENTRY(t1cur, struct hlsl_struct_field, entry); + t2field = LIST_ENTRY(t2cur, struct hlsl_struct_field, entry); + if ((r = compare_param_hlsl_types(t1field->type, t2field->type))) + return r; + if ((r = strcmp(t1field->name, t2field->name))) + return r; + t1cur = list_next(t1->e.elements, t1cur); + t2cur = list_next(t2->e.elements, t2cur); + } + if (t1cur != t2cur) + return t1cur ? 1 : -1; + return 0; + } + if (t1->type == HLSL_CLASS_ARRAY) + { + if (t1->e.array.elements_count != t2->e.array.elements_count) + return t1->e.array.elements_count - t2->e.array.elements_count; + return compare_param_hlsl_types(t1->e.array.type, t2->e.array.type); + } + + return 0; +} + +static int compare_function_decl_rb(const void *key, const struct rb_entry *entry) +{ + const struct list *params = key; + const struct hlsl_ir_function_decl *decl = RB_ENTRY_VALUE(entry, const struct hlsl_ir_function_decl, entry); + int decl_params_count = decl->parameters ? list_count(decl->parameters) : 0; + int params_count = params ? list_count(params) : 0; + struct list *p1cur, *p2cur; + int r; + + if (params_count != decl_params_count) + return params_count - decl_params_count; + + p1cur = params ? list_head(params) : NULL; + p2cur = decl->parameters ? list_head(decl->parameters) : NULL; + while (p1cur && p2cur) + { + struct hlsl_ir_var *p1, *p2; + p1 = LIST_ENTRY(p1cur, struct hlsl_ir_var, param_entry); + p2 = LIST_ENTRY(p2cur, struct hlsl_ir_var, param_entry); + if ((r = compare_param_hlsl_types(p1->data_type, p2->data_type))) + return r; + p1cur = list_next(params, p1cur); + p2cur = list_next(decl->parameters, p2cur); + } + return 0; +} + +static int compare_function_rb(const void *key, const struct rb_entry *entry) +{ + const char *name = key; + const struct hlsl_ir_function *func = RB_ENTRY_VALUE(entry, const struct hlsl_ir_function,entry); + + return strcmp(name, func->name); +} + +void init_functions_tree(struct rb_tree *funcs) +{ + rb_init(&hlsl_ctx.functions, compare_function_rb); +} + +const char *debug_base_type(const struct hlsl_type *type) +{ + const char *name = "(unknown)"; + + switch (type->base_type) + { + case HLSL_TYPE_FLOAT: name = "float"; break; + case HLSL_TYPE_HALF: name = "half"; break; + case HLSL_TYPE_DOUBLE: name = "double"; break; + case HLSL_TYPE_INT: name = "int"; break; + case HLSL_TYPE_UINT: name = "uint"; break; + case HLSL_TYPE_BOOL: name = "bool"; break; + case HLSL_TYPE_SAMPLER: + switch (type->sampler_dim) + { + case HLSL_SAMPLER_DIM_GENERIC: name = "sampler"; break; + case HLSL_SAMPLER_DIM_1D: name = "sampler1D"; break; + case HLSL_SAMPLER_DIM_2D: name = "sampler2D"; break; + case HLSL_SAMPLER_DIM_3D: name = "sampler3D"; break; + case HLSL_SAMPLER_DIM_CUBE: name = "samplerCUBE"; break; + } + break; + default: + FIXME("Unhandled case %u.\n", type->base_type); + } + return name; +} + +const char *debug_hlsl_type(const struct hlsl_type *type) +{ + const char *name; + + if (type->name) + return debugstr_a(type->name); + + if (type->type == HLSL_CLASS_STRUCT) + return "<anonymous struct>"; + + if (type->type == HLSL_CLASS_ARRAY) + { + name = debug_base_type(type->e.array.type); + return vkd3d_dbg_sprintf("%s[%u]", name, type->e.array.elements_count); + } + + name = debug_base_type(type); + + if (type->type == HLSL_CLASS_SCALAR) + return vkd3d_dbg_sprintf("%s", name); + if (type->type == HLSL_CLASS_VECTOR) + return vkd3d_dbg_sprintf("%s%u", name, type->dimx); + if (type->type == HLSL_CLASS_MATRIX) + return vkd3d_dbg_sprintf("%s%ux%u", name, type->dimx, type->dimy); + return "unexpected_type"; +} + +const char *debug_modifiers(DWORD modifiers) +{ + char string[110]; + + string[0] = 0; + if (modifiers & HLSL_STORAGE_EXTERN) + strcat(string, " extern"); /* 7 */ + if (modifiers & HLSL_STORAGE_NOINTERPOLATION) + strcat(string, " nointerpolation"); /* 16 */ + if (modifiers & HLSL_MODIFIER_PRECISE) + strcat(string, " precise"); /* 8 */ + if (modifiers & HLSL_STORAGE_SHARED) + strcat(string, " shared"); /* 7 */ + if (modifiers & HLSL_STORAGE_GROUPSHARED) + strcat(string, " groupshared"); /* 12 */ + if (modifiers & HLSL_STORAGE_STATIC) + strcat(string, " static"); /* 7 */ + if (modifiers & HLSL_STORAGE_UNIFORM) + strcat(string, " uniform"); /* 8 */ + if (modifiers & HLSL_STORAGE_VOLATILE) + strcat(string, " volatile"); /* 9 */ + if (modifiers & HLSL_MODIFIER_CONST) + strcat(string, " const"); /* 6 */ + if (modifiers & HLSL_MODIFIER_ROW_MAJOR) + strcat(string, " row_major"); /* 10 */ + if (modifiers & HLSL_MODIFIER_COLUMN_MAJOR) + strcat(string, " column_major"); /* 13 */ + if ((modifiers & (HLSL_STORAGE_IN | HLSL_STORAGE_OUT)) == (HLSL_STORAGE_IN | HLSL_STORAGE_OUT)) + strcat(string, " inout"); /* 6 */ + else if (modifiers & HLSL_STORAGE_IN) + strcat(string, " in"); /* 3 */ + else if (modifiers & HLSL_STORAGE_OUT) + strcat(string, " out"); /* 4 */ + + return vkd3d_dbg_sprintf("%s", string[0] ? string + 1 : ""); +} + +const char *debug_node_type(enum hlsl_ir_node_type type) +{ + static const char * const names[] = + { + "HLSL_IR_ASSIGNMENT", + "HLSL_IR_CONSTANT", + "HLSL_IR_EXPR", + "HLSL_IR_IF", + "HLSL_IR_LOAD", + "HLSL_IR_LOOP", + "HLSL_IR_JUMP", + "HLSL_IR_SWIZZLE", + }; + + if (type >= ARRAY_SIZE(names)) + return "Unexpected node type"; + return names[type]; +} + +static void debug_dump_instr(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_node *instr); + +static void debug_dump_instr_list(struct vkd3d_string_buffer *buffer, const struct list *list) +{ + struct hlsl_ir_node *instr; + + LIST_FOR_EACH_ENTRY(instr, list, struct hlsl_ir_node, entry) + { + debug_dump_instr(buffer, instr); + vkd3d_string_buffer_printf(buffer, "\n"); + } +} + +static void debug_dump_src(struct vkd3d_string_buffer *buffer, const struct hlsl_src *src) +{ + if (src->node->index) + vkd3d_string_buffer_printf(buffer, "@%u", src->node->index); + else + vkd3d_string_buffer_printf(buffer, "%p", src->node); +} + +static void debug_dump_ir_var(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_var *var) +{ + if (var->modifiers) + vkd3d_string_buffer_printf(buffer, "%s ", debug_modifiers(var->modifiers)); + vkd3d_string_buffer_printf(buffer, "%s %s", debug_hlsl_type(var->data_type), var->name); + if (var->semantic) + vkd3d_string_buffer_printf(buffer, " : %s", var->semantic); +} + +static void debug_dump_deref(struct vkd3d_string_buffer *buffer, const struct hlsl_deref *deref) +{ + if (deref->offset.node) + /* Print the variable's type for convenience. */ + vkd3d_string_buffer_printf(buffer, "(%s %s)", debug_hlsl_type(deref->var->data_type), deref->var->name); + else + vkd3d_string_buffer_printf(buffer, "%s", deref->var->name); + if (deref->offset.node) + { + vkd3d_string_buffer_printf(buffer, "["); + debug_dump_src(buffer, &deref->offset); + vkd3d_string_buffer_printf(buffer, "]"); + } +} + +static const char *debug_writemask(DWORD writemask) +{ + static const char components[] = {'x', 'y', 'z', 'w'}; + char string[5]; + unsigned int i = 0, pos = 0; + + assert(!(writemask & ~VKD3DSP_WRITEMASK_ALL)); + + while (writemask) + { + if (writemask & 1) + string[pos++] = components[i]; + writemask >>= 1; + i++; + } + string[pos] = '\0'; + return vkd3d_dbg_sprintf(".%s", string); +} + +static void debug_dump_ir_assignment(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_assignment *assign) +{ + vkd3d_string_buffer_printf(buffer, "= ("); + debug_dump_deref(buffer, &assign->lhs); + if (assign->writemask != VKD3DSP_WRITEMASK_ALL) + vkd3d_string_buffer_printf(buffer, "%s", debug_writemask(assign->writemask)); + vkd3d_string_buffer_printf(buffer, " "); + debug_dump_src(buffer, &assign->rhs); + vkd3d_string_buffer_printf(buffer, ")"); +} + +static void debug_dump_ir_constant(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_constant *constant) +{ + struct hlsl_type *type = constant->node.data_type; + unsigned int x; + + if (type->dimx != 1) + vkd3d_string_buffer_printf(buffer, "{"); + for (x = 0; x < type->dimx; ++x) + { + switch (type->base_type) + { + case HLSL_TYPE_BOOL: + vkd3d_string_buffer_printf(buffer, "%s ", constant->value.b[x] ? "true" : "false"); + break; + + case HLSL_TYPE_DOUBLE: + vkd3d_string_buffer_printf(buffer, "%.16e ", constant->value.d[x]); + break; + + case HLSL_TYPE_FLOAT: + vkd3d_string_buffer_printf(buffer, "%.8e ", constant->value.f[x]); + break; + + case HLSL_TYPE_INT: + vkd3d_string_buffer_printf(buffer, "%d ", constant->value.i[x]); + break; + + case HLSL_TYPE_UINT: + vkd3d_string_buffer_printf(buffer, "%u ", constant->value.u[x]); + break; + + default: + vkd3d_string_buffer_printf(buffer, "Constants of type %s not supported\n", debug_base_type(type)); + } + } + if (type->dimx != 1) + vkd3d_string_buffer_printf(buffer, "}"); +} + +static const char *debug_expr_op(const struct hlsl_ir_expr *expr) +{ + static const char * const op_names[] = + { + "~", + "!", + "-", + "abs", + "sign", + "rcp", + "rsq", + "sqrt", + "nrm", + "exp2", + "log2", + + "cast", + + "fract", + + "sin", + "cos", + "sin_reduced", + "cos_reduced", + + "dsx", + "dsy", + + "sat", + + "pre++", + "pre--", + "post++", + "post--", + + "+", + "-", + "*", + "/", + + "%", + + "<", + ">", + "<=", + ">=", + "==", + "!=", + + "&&", + "||", + + "<<", + ">>", + "&", + "|", + "^", + + "dot", + "crs", + "min", + "max", + + "pow", + + "lerp", + + ",", + }; + + if (expr->op == HLSL_IR_UNOP_CAST) + return debug_hlsl_type(expr->node.data_type); + + return op_names[expr->op]; +} + +/* Dumps the expression in a prefix "operator (operands)" form */ +static void debug_dump_ir_expr(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_expr *expr) +{ + unsigned int i; + + vkd3d_string_buffer_printf(buffer, "%s (", debug_expr_op(expr)); + for (i = 0; i < 3 && expr->operands[i].node; ++i) + { + debug_dump_src(buffer, &expr->operands[i]); + vkd3d_string_buffer_printf(buffer, " "); + } + vkd3d_string_buffer_printf(buffer, ")"); +} + +static void debug_dump_ir_if(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_if *if_node) +{ + vkd3d_string_buffer_printf(buffer, "if ("); + debug_dump_src(buffer, &if_node->condition); + vkd3d_string_buffer_printf(buffer, ")\n{\n"); + debug_dump_instr_list(buffer, &if_node->then_instrs); + vkd3d_string_buffer_printf(buffer, "}\nelse\n{\n"); + debug_dump_instr_list(buffer, &if_node->else_instrs); + vkd3d_string_buffer_printf(buffer, "}\n"); +} + +static void debug_dump_ir_jump(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_jump *jump) +{ + switch (jump->type) + { + case HLSL_IR_JUMP_BREAK: + vkd3d_string_buffer_printf(buffer, "break"); + break; + + case HLSL_IR_JUMP_CONTINUE: + vkd3d_string_buffer_printf(buffer, "continue"); + break; + + case HLSL_IR_JUMP_DISCARD: + vkd3d_string_buffer_printf(buffer, "discard"); + break; + + case HLSL_IR_JUMP_RETURN: + vkd3d_string_buffer_printf(buffer, "return"); + break; + } +} + +static void debug_dump_ir_loop(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_loop *loop) +{ + vkd3d_string_buffer_printf(buffer, "for (;;)\n{\n"); + debug_dump_instr_list(buffer, &loop->body); + vkd3d_string_buffer_printf(buffer, "}\n"); +} + +static void debug_dump_ir_swizzle(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_swizzle *swizzle) +{ + unsigned int i; + + debug_dump_src(buffer, &swizzle->val); + vkd3d_string_buffer_printf(buffer, "."); + if (swizzle->val.node->data_type->dimy > 1) + { + for (i = 0; i < swizzle->node.data_type->dimx; ++i) + vkd3d_string_buffer_printf(buffer, "_m%u%u", (swizzle->swizzle >> i * 8) & 0xf, (swizzle->swizzle >> (i * 8 + 4)) & 0xf); + } + else + { + static const char c[] = {'x', 'y', 'z', 'w'}; + + for (i = 0; i < swizzle->node.data_type->dimx; ++i) + vkd3d_string_buffer_printf(buffer, "%c", c[(swizzle->swizzle >> i * 2) & 0x3]); + } +} + +static void debug_dump_instr(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_node *instr) +{ + if (instr->index) + vkd3d_string_buffer_printf(buffer, "%4u: ", instr->index); + else + vkd3d_string_buffer_printf(buffer, "%p: ", instr); + + vkd3d_string_buffer_printf(buffer, "%10s | ", instr->data_type ? debug_hlsl_type(instr->data_type) : ""); + + switch (instr->type) + { + case HLSL_IR_ASSIGNMENT: + debug_dump_ir_assignment(buffer, assignment_from_node(instr)); + break; + + case HLSL_IR_CONSTANT: + debug_dump_ir_constant(buffer, constant_from_node(instr)); + break; + + case HLSL_IR_EXPR: + debug_dump_ir_expr(buffer, expr_from_node(instr)); + break; + + case HLSL_IR_IF: + debug_dump_ir_if(buffer, if_from_node(instr)); + break; + + case HLSL_IR_JUMP: + debug_dump_ir_jump(buffer, jump_from_node(instr)); + break; + + case HLSL_IR_LOAD: + debug_dump_deref(buffer, &load_from_node(instr)->src); + break; + + case HLSL_IR_LOOP: + debug_dump_ir_loop(buffer, loop_from_node(instr)); + break; + + case HLSL_IR_SWIZZLE: + debug_dump_ir_swizzle(buffer, swizzle_from_node(instr)); + break; + + default: + vkd3d_string_buffer_printf(buffer, "<No dump function for %s>", debug_node_type(instr->type)); + } +} + +void debug_dump_ir_function_decl(const struct hlsl_ir_function_decl *func) +{ + struct vkd3d_string_buffer buffer; + struct hlsl_ir_var *param; + + vkd3d_string_buffer_init(&buffer); + vkd3d_string_buffer_printf(&buffer, "Dumping function %s.\n", func->func->name); + vkd3d_string_buffer_printf(&buffer, "Function parameters:\n"); + LIST_FOR_EACH_ENTRY(param, func->parameters, struct hlsl_ir_var, param_entry) + { + debug_dump_ir_var(&buffer, param); + vkd3d_string_buffer_printf(&buffer, "\n"); + } + if (func->semantic) + vkd3d_string_buffer_printf(&buffer, "Function semantic: %s\n", func->semantic); + if (func->body) + debug_dump_instr_list(&buffer, func->body); + + vkd3d_string_buffer_trace(&buffer); + vkd3d_string_buffer_cleanup(&buffer); +} + +void free_hlsl_type(struct hlsl_type *type) +{ + struct hlsl_struct_field *field, *next_field; + + vkd3d_free((void *)type->name); + if (type->type == HLSL_CLASS_STRUCT) + { + LIST_FOR_EACH_ENTRY_SAFE(field, next_field, type->e.elements, struct hlsl_struct_field, entry) + { + vkd3d_free((void *)field->name); + vkd3d_free((void *)field->semantic); + vkd3d_free(field); + } + } + vkd3d_free(type); +} + +void free_instr_list(struct list *list) +{ + struct hlsl_ir_node *node, *next_node; + + if (!list) + return; + /* Iterate in reverse, to avoid use-after-free when unlinking sources from + * the "uses" list. */ + LIST_FOR_EACH_ENTRY_SAFE_REV(node, next_node, list, struct hlsl_ir_node, entry) + free_instr(node); + vkd3d_free(list); +} + +static void free_ir_assignment(struct hlsl_ir_assignment *assignment) +{ + hlsl_src_remove(&assignment->rhs); + hlsl_src_remove(&assignment->lhs.offset); + vkd3d_free(assignment); +} + +static void free_ir_constant(struct hlsl_ir_constant *constant) +{ + vkd3d_free(constant); +} + +static void free_ir_expr(struct hlsl_ir_expr *expr) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(expr->operands); ++i) + hlsl_src_remove(&expr->operands[i]); + vkd3d_free(expr); +} + +static void free_ir_if(struct hlsl_ir_if *if_node) +{ + struct hlsl_ir_node *node, *next_node; + + LIST_FOR_EACH_ENTRY_SAFE(node, next_node, &if_node->then_instrs, struct hlsl_ir_node, entry) + free_instr(node); + LIST_FOR_EACH_ENTRY_SAFE(node, next_node, &if_node->else_instrs, struct hlsl_ir_node, entry) + free_instr(node); + hlsl_src_remove(&if_node->condition); + vkd3d_free(if_node); +} + +static void free_ir_jump(struct hlsl_ir_jump *jump) +{ + vkd3d_free(jump); +} + +static void free_ir_load(struct hlsl_ir_load *load) +{ + hlsl_src_remove(&load->src.offset); + vkd3d_free(load); +} + +static void free_ir_loop(struct hlsl_ir_loop *loop) +{ + struct hlsl_ir_node *node, *next_node; + + LIST_FOR_EACH_ENTRY_SAFE(node, next_node, &loop->body, struct hlsl_ir_node, entry) + free_instr(node); + vkd3d_free(loop); +} + +static void free_ir_swizzle(struct hlsl_ir_swizzle *swizzle) +{ + hlsl_src_remove(&swizzle->val); + vkd3d_free(swizzle); +} + +void free_instr(struct hlsl_ir_node *node) +{ + switch (node->type) + { + case HLSL_IR_ASSIGNMENT: + free_ir_assignment(assignment_from_node(node)); + break; + + case HLSL_IR_CONSTANT: + free_ir_constant(constant_from_node(node)); + break; + + case HLSL_IR_EXPR: + free_ir_expr(expr_from_node(node)); + break; + + case HLSL_IR_IF: + free_ir_if(if_from_node(node)); + break; + + case HLSL_IR_JUMP: + free_ir_jump(jump_from_node(node)); + break; + + case HLSL_IR_LOAD: + free_ir_load(load_from_node(node)); + break; + + case HLSL_IR_LOOP: + free_ir_loop(loop_from_node(node)); + break; + + case HLSL_IR_SWIZZLE: + free_ir_swizzle(swizzle_from_node(node)); + break; + + default: + FIXME("Unsupported node type %s.\n", debug_node_type(node->type)); + } +} + +static void free_function_decl(struct hlsl_ir_function_decl *decl) +{ + vkd3d_free((void *)decl->semantic); + vkd3d_free(decl->parameters); + free_instr_list(decl->body); + vkd3d_free(decl); +} + +static void free_function_decl_rb(struct rb_entry *entry, void *context) +{ + free_function_decl(RB_ENTRY_VALUE(entry, struct hlsl_ir_function_decl, entry)); +} + +static void free_function(struct hlsl_ir_function *func) +{ + rb_destroy(&func->overloads, free_function_decl_rb, NULL); + vkd3d_free((void *)func->name); + vkd3d_free(func); +} + +void free_function_rb(struct rb_entry *entry, void *context) +{ + free_function(RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry)); +} + +void add_function_decl(struct rb_tree *funcs, char *name, struct hlsl_ir_function_decl *decl, BOOL intrinsic) +{ + struct hlsl_ir_function *func; + struct rb_entry *func_entry, *old_entry; + + func_entry = rb_get(funcs, name); + if (func_entry) + { + func = RB_ENTRY_VALUE(func_entry, struct hlsl_ir_function, entry); + if (intrinsic != func->intrinsic) + { + if (intrinsic) + { + ERR("Redeclaring a user defined function as an intrinsic.\n"); + return; + } + TRACE("Function %s redeclared as a user defined function.\n", debugstr_a(name)); + func->intrinsic = intrinsic; + rb_destroy(&func->overloads, free_function_decl_rb, NULL); + rb_init(&func->overloads, compare_function_decl_rb); + } + decl->func = func; + if ((old_entry = rb_get(&func->overloads, decl->parameters))) + { + struct hlsl_ir_function_decl *old_decl = + RB_ENTRY_VALUE(old_entry, struct hlsl_ir_function_decl, entry); + + if (!decl->body) + { + free_function_decl(decl); + vkd3d_free(name); + return; + } + rb_remove(&func->overloads, old_entry); + free_function_decl(old_decl); + } + rb_put(&func->overloads, decl->parameters, &decl->entry); + vkd3d_free(name); + return; + } + func = vkd3d_malloc(sizeof(*func)); + func->name = name; + rb_init(&func->overloads, compare_function_decl_rb); + decl->func = func; + rb_put(&func->overloads, decl->parameters, &decl->entry); + func->intrinsic = intrinsic; + rb_put(funcs, func->name, &func->entry); +} + +struct hlsl_profile_info +{ + const char *name; + enum vkd3d_shader_type type; + DWORD sm_major; + DWORD sm_minor; + DWORD level_major; + DWORD level_minor; + BOOL sw; +}; + +static const struct hlsl_profile_info *get_target_info(const char *target) +{ + unsigned int i; + + static const struct hlsl_profile_info profiles[] = + { + {"cs_4_0", VKD3D_SHADER_TYPE_COMPUTE, 4, 0, 0, 0, FALSE}, + {"cs_4_1", VKD3D_SHADER_TYPE_COMPUTE, 4, 1, 0, 0, FALSE}, + {"cs_5_0", VKD3D_SHADER_TYPE_COMPUTE, 5, 0, 0, 0, FALSE}, + {"ds_5_0", VKD3D_SHADER_TYPE_DOMAIN, 5, 0, 0, 0, FALSE}, + {"fx_2_0", VKD3D_SHADER_TYPE_EFFECT, 2, 0, 0, 0, FALSE}, + {"fx_4_0", VKD3D_SHADER_TYPE_EFFECT, 4, 0, 0, 0, FALSE}, + {"fx_4_1", VKD3D_SHADER_TYPE_EFFECT, 4, 1, 0, 0, FALSE}, + {"fx_5_0", VKD3D_SHADER_TYPE_EFFECT, 5, 0, 0, 0, FALSE}, + {"gs_4_0", VKD3D_SHADER_TYPE_GEOMETRY, 4, 0, 0, 0, FALSE}, + {"gs_4_1", VKD3D_SHADER_TYPE_GEOMETRY, 4, 1, 0, 0, FALSE}, + {"gs_5_0", VKD3D_SHADER_TYPE_GEOMETRY, 5, 0, 0, 0, FALSE}, + {"hs_5_0", VKD3D_SHADER_TYPE_HULL, 5, 0, 0, 0, FALSE}, + {"ps.1.0", VKD3D_SHADER_TYPE_PIXEL, 1, 0, 0, 0, FALSE}, + {"ps.1.1", VKD3D_SHADER_TYPE_PIXEL, 1, 1, 0, 0, FALSE}, + {"ps.1.2", VKD3D_SHADER_TYPE_PIXEL, 1, 2, 0, 0, FALSE}, + {"ps.1.3", VKD3D_SHADER_TYPE_PIXEL, 1, 3, 0, 0, FALSE}, + {"ps.1.4", VKD3D_SHADER_TYPE_PIXEL, 1, 4, 0, 0, FALSE}, + {"ps.2.0", VKD3D_SHADER_TYPE_PIXEL, 2, 0, 0, 0, FALSE}, + {"ps.2.a", VKD3D_SHADER_TYPE_PIXEL, 2, 1, 0, 0, FALSE}, + {"ps.2.b", VKD3D_SHADER_TYPE_PIXEL, 2, 2, 0, 0, FALSE}, + {"ps.2.sw", VKD3D_SHADER_TYPE_PIXEL, 2, 0, 0, 0, TRUE}, + {"ps.3.0", VKD3D_SHADER_TYPE_PIXEL, 3, 0, 0, 0, FALSE}, + {"ps_1_0", VKD3D_SHADER_TYPE_PIXEL, 1, 0, 0, 0, FALSE}, + {"ps_1_1", VKD3D_SHADER_TYPE_PIXEL, 1, 1, 0, 0, FALSE}, + {"ps_1_2", VKD3D_SHADER_TYPE_PIXEL, 1, 2, 0, 0, FALSE}, + {"ps_1_3", VKD3D_SHADER_TYPE_PIXEL, 1, 3, 0, 0, FALSE}, + {"ps_1_4", VKD3D_SHADER_TYPE_PIXEL, 1, 4, 0, 0, FALSE}, + {"ps_2_0", VKD3D_SHADER_TYPE_PIXEL, 2, 0, 0, 0, FALSE}, + {"ps_2_a", VKD3D_SHADER_TYPE_PIXEL, 2, 1, 0, 0, FALSE}, + {"ps_2_b", VKD3D_SHADER_TYPE_PIXEL, 2, 2, 0, 0, FALSE}, + {"ps_2_sw", VKD3D_SHADER_TYPE_PIXEL, 2, 0, 0, 0, TRUE}, + {"ps_3_0", VKD3D_SHADER_TYPE_PIXEL, 3, 0, 0, 0, FALSE}, + {"ps_3_sw", VKD3D_SHADER_TYPE_PIXEL, 3, 0, 0, 0, TRUE}, + {"ps_4_0", VKD3D_SHADER_TYPE_PIXEL, 4, 0, 0, 0, FALSE}, + {"ps_4_0_level_9_0", VKD3D_SHADER_TYPE_PIXEL, 4, 0, 9, 0, FALSE}, + {"ps_4_0_level_9_1", VKD3D_SHADER_TYPE_PIXEL, 4, 0, 9, 1, FALSE}, + {"ps_4_0_level_9_3", VKD3D_SHADER_TYPE_PIXEL, 4, 0, 9, 3, FALSE}, + {"ps_4_1", VKD3D_SHADER_TYPE_PIXEL, 4, 1, 0, 0, FALSE}, + {"ps_5_0", VKD3D_SHADER_TYPE_PIXEL, 5, 0, 0, 0, FALSE}, + {"tx_1_0", VKD3D_SHADER_TYPE_TEXTURE, 1, 0, 0, 0, FALSE}, + {"vs.1.0", VKD3D_SHADER_TYPE_VERTEX, 1, 0, 0, 0, FALSE}, + {"vs.1.1", VKD3D_SHADER_TYPE_VERTEX, 1, 1, 0, 0, FALSE}, + {"vs.2.0", VKD3D_SHADER_TYPE_VERTEX, 2, 0, 0, 0, FALSE}, + {"vs.2.a", VKD3D_SHADER_TYPE_VERTEX, 2, 1, 0, 0, FALSE}, + {"vs.2.sw", VKD3D_SHADER_TYPE_VERTEX, 2, 0, 0, 0, TRUE}, + {"vs.3.0", VKD3D_SHADER_TYPE_VERTEX, 3, 0, 0, 0, FALSE}, + {"vs.3.sw", VKD3D_SHADER_TYPE_VERTEX, 3, 0, 0, 0, TRUE}, + {"vs_1_0", VKD3D_SHADER_TYPE_VERTEX, 1, 0, 0, 0, FALSE}, + {"vs_1_1", VKD3D_SHADER_TYPE_VERTEX, 1, 1, 0, 0, FALSE}, + {"vs_2_0", VKD3D_SHADER_TYPE_VERTEX, 2, 0, 0, 0, FALSE}, + {"vs_2_a", VKD3D_SHADER_TYPE_VERTEX, 2, 1, 0, 0, FALSE}, + {"vs_2_sw", VKD3D_SHADER_TYPE_VERTEX, 2, 0, 0, 0, TRUE}, + {"vs_3_0", VKD3D_SHADER_TYPE_VERTEX, 3, 0, 0, 0, FALSE}, + {"vs_3_sw", VKD3D_SHADER_TYPE_VERTEX, 3, 0, 0, 0, TRUE}, + {"vs_4_0", VKD3D_SHADER_TYPE_VERTEX, 4, 0, 0, 0, FALSE}, + {"vs_4_0_level_9_0", VKD3D_SHADER_TYPE_VERTEX, 4, 0, 9, 0, FALSE}, + {"vs_4_0_level_9_1", VKD3D_SHADER_TYPE_VERTEX, 4, 0, 9, 1, FALSE}, + {"vs_4_0_level_9_3", VKD3D_SHADER_TYPE_VERTEX, 4, 0, 9, 3, FALSE}, + {"vs_4_1", VKD3D_SHADER_TYPE_VERTEX, 4, 1, 0, 0, FALSE}, + {"vs_5_0", VKD3D_SHADER_TYPE_VERTEX, 5, 0, 0, 0, FALSE}, + }; + + for (i = 0; i < ARRAY_SIZE(profiles); ++i) + { + if (!strcmp(target, profiles[i].name)) + return &profiles[i]; + } + + return NULL; +} + +int hlsl_compile_shader(const char *text, const struct vkd3d_shader_compile_info *compile_info, + struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) +{ + const struct vkd3d_shader_hlsl_source_info *hlsl_source_info; + const struct hlsl_profile_info *profile; + + if (!(hlsl_source_info = vkd3d_find_struct(compile_info->next, HLSL_SOURCE_INFO))) + { + ERR("No HLSL source info given.\n"); + return VKD3D_ERROR_INVALID_ARGUMENT; + } + + if (!(profile = get_target_info(hlsl_source_info->profile))) + { + FIXME("Unknown compilation target %s.\n", debugstr_a(hlsl_source_info->profile)); + return VKD3D_ERROR_NOT_IMPLEMENTED; + } + + vkd3d_shader_dump_shader(profile->type, &compile_info->source); + + return hlsl_lexer_compile(text, profile->type, profile->sm_major, profile->sm_minor, + hlsl_source_info->entry_point, dxbc, message_context); +} diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h new file mode 100644 index 00000000..128de1d6 --- /dev/null +++ b/libs/vkd3d-shader/hlsl.h @@ -0,0 +1,644 @@ +/* + * 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 + */ + +#ifndef __VKD3D_SHADER_HLSL_H +#define __VKD3D_SHADER_HLSL_H + +#include "vkd3d_shader_private.h" +#include "rbtree.h" + +enum parse_status +{ + PARSE_SUCCESS = 0, + PARSE_WARN = 1, + PARSE_ERR = 2 +}; + +/* The general IR structure is inspired by Mesa GLSL hir, even though the code + * ends up being quite different in practice. Anyway, here comes the relevant + * licensing information. + * + * 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. + */ + +#define HLSL_SWIZZLE_X (0u) +#define HLSL_SWIZZLE_Y (1u) +#define HLSL_SWIZZLE_Z (2u) +#define HLSL_SWIZZLE_W (3u) + +#define HLSL_SWIZZLE(x, y, z, w) \ + (((HLSL_SWIZZLE_ ## x) << 0) \ + | ((HLSL_SWIZZLE_ ## y) << 2) \ + | ((HLSL_SWIZZLE_ ## z) << 4) \ + | ((HLSL_SWIZZLE_ ## w) << 6)) + +enum hlsl_type_class +{ + HLSL_CLASS_SCALAR, + HLSL_CLASS_VECTOR, + HLSL_CLASS_MATRIX, + HLSL_CLASS_LAST_NUMERIC = HLSL_CLASS_MATRIX, + HLSL_CLASS_STRUCT, + HLSL_CLASS_ARRAY, + HLSL_CLASS_OBJECT, +}; + +enum hlsl_base_type +{ + HLSL_TYPE_FLOAT, + HLSL_TYPE_HALF, + HLSL_TYPE_DOUBLE, + HLSL_TYPE_INT, + HLSL_TYPE_UINT, + HLSL_TYPE_BOOL, + HLSL_TYPE_LAST_SCALAR = HLSL_TYPE_BOOL, + HLSL_TYPE_SAMPLER, + HLSL_TYPE_TEXTURE, + HLSL_TYPE_PIXELSHADER, + HLSL_TYPE_VERTEXSHADER, + HLSL_TYPE_STRING, + HLSL_TYPE_VOID, +}; + +enum hlsl_sampler_dim +{ + HLSL_SAMPLER_DIM_GENERIC, + HLSL_SAMPLER_DIM_1D, + HLSL_SAMPLER_DIM_2D, + HLSL_SAMPLER_DIM_3D, + HLSL_SAMPLER_DIM_CUBE, + HLSL_SAMPLER_DIM_MAX = HLSL_SAMPLER_DIM_CUBE +}; + +enum hlsl_matrix_majority +{ + HLSL_COLUMN_MAJOR, + HLSL_ROW_MAJOR +}; + +struct hlsl_type +{ + struct list entry; + struct rb_entry scope_entry; + enum hlsl_type_class type; + enum hlsl_base_type base_type; + enum hlsl_sampler_dim sampler_dim; + const char *name; + unsigned int modifiers; + unsigned int dimx; + unsigned int dimy; + unsigned int reg_size; + union + { + struct list *elements; + struct + { + struct hlsl_type *type; + unsigned int elements_count; + } array; + } e; +}; + +struct hlsl_struct_field +{ + struct list entry; + struct hlsl_type *type; + const char *name; + const char *semantic; + DWORD modifiers; + unsigned int reg_offset; +}; + +struct source_location +{ + const char *file; + unsigned int line; + unsigned int col; +}; + +enum hlsl_ir_node_type +{ + HLSL_IR_ASSIGNMENT = 0, + HLSL_IR_CONSTANT, + HLSL_IR_EXPR, + HLSL_IR_IF, + HLSL_IR_LOAD, + HLSL_IR_LOOP, + HLSL_IR_JUMP, + HLSL_IR_SWIZZLE, +}; + +struct hlsl_ir_node +{ + struct list entry; + enum hlsl_ir_node_type type; + struct hlsl_type *data_type; + + struct list uses; + + struct source_location loc; + + /* Liveness ranges. "index" is the index of this instruction. Since this is + * essentially an SSA value, the earliest live point is the index. This is + * true even for loops, since currently we can't have a reference to a + * value generated in an earlier iteration of the loop. */ + unsigned int index, last_read; +}; + +struct hlsl_src +{ + struct hlsl_ir_node *node; + struct list entry; +}; + +#define HLSL_STORAGE_EXTERN 0x00000001 +#define HLSL_STORAGE_NOINTERPOLATION 0x00000002 +#define HLSL_MODIFIER_PRECISE 0x00000004 +#define HLSL_STORAGE_SHARED 0x00000008 +#define HLSL_STORAGE_GROUPSHARED 0x00000010 +#define HLSL_STORAGE_STATIC 0x00000020 +#define HLSL_STORAGE_UNIFORM 0x00000040 +#define HLSL_STORAGE_VOLATILE 0x00000080 +#define HLSL_MODIFIER_CONST 0x00000100 +#define HLSL_MODIFIER_ROW_MAJOR 0x00000200 +#define HLSL_MODIFIER_COLUMN_MAJOR 0x00000400 +#define HLSL_STORAGE_IN 0x00000800 +#define HLSL_STORAGE_OUT 0x00001000 + +#define HLSL_TYPE_MODIFIERS_MASK (HLSL_MODIFIER_PRECISE | HLSL_STORAGE_VOLATILE | \ + HLSL_MODIFIER_CONST | HLSL_MODIFIER_ROW_MAJOR | \ + HLSL_MODIFIER_COLUMN_MAJOR) + +#define HLSL_MODIFIERS_MAJORITY_MASK (HLSL_MODIFIER_ROW_MAJOR | HLSL_MODIFIER_COLUMN_MAJOR) + +struct reg_reservation +{ + enum vkd3d_shader_register_type type; + DWORD regnum; +}; + +struct hlsl_ir_var +{ + struct hlsl_type *data_type; + struct source_location loc; + const char *name; + const char *semantic; + unsigned int modifiers; + const struct reg_reservation *reg_reservation; + struct list scope_entry, param_entry; + + unsigned int first_write, last_read; +}; + +struct hlsl_ir_function +{ + struct rb_entry entry; + const char *name; + struct rb_tree overloads; + BOOL intrinsic; +}; + +struct hlsl_ir_function_decl +{ + struct hlsl_type *return_type; + struct hlsl_ir_var *return_var; + struct source_location loc; + struct rb_entry entry; + struct hlsl_ir_function *func; + const char *semantic; + struct list *parameters; + struct list *body; +}; + +struct hlsl_ir_if +{ + struct hlsl_ir_node node; + struct hlsl_src condition; + struct list then_instrs; + struct list else_instrs; +}; + +struct hlsl_ir_loop +{ + struct hlsl_ir_node node; + /* loop condition is stored in the body (as "if (!condition) break;") */ + struct list body; + unsigned int next_index; /* liveness index of the end of the loop */ +}; + +enum hlsl_ir_expr_op +{ + HLSL_IR_UNOP_BIT_NOT = 0, + HLSL_IR_UNOP_LOGIC_NOT, + HLSL_IR_UNOP_NEG, + HLSL_IR_UNOP_ABS, + HLSL_IR_UNOP_SIGN, + HLSL_IR_UNOP_RCP, + HLSL_IR_UNOP_RSQ, + HLSL_IR_UNOP_SQRT, + HLSL_IR_UNOP_NRM, + HLSL_IR_UNOP_EXP2, + HLSL_IR_UNOP_LOG2, + + HLSL_IR_UNOP_CAST, + + HLSL_IR_UNOP_FRACT, + + HLSL_IR_UNOP_SIN, + HLSL_IR_UNOP_COS, + HLSL_IR_UNOP_SIN_REDUCED, /* Reduced range [-pi, pi] */ + HLSL_IR_UNOP_COS_REDUCED, /* Reduced range [-pi, pi] */ + + HLSL_IR_UNOP_DSX, + HLSL_IR_UNOP_DSY, + + HLSL_IR_UNOP_SAT, + + HLSL_IR_UNOP_PREINC, + HLSL_IR_UNOP_PREDEC, + HLSL_IR_UNOP_POSTINC, + HLSL_IR_UNOP_POSTDEC, + + HLSL_IR_BINOP_ADD, + HLSL_IR_BINOP_SUB, + HLSL_IR_BINOP_MUL, + HLSL_IR_BINOP_DIV, + + HLSL_IR_BINOP_MOD, + + HLSL_IR_BINOP_LESS, + HLSL_IR_BINOP_GREATER, + HLSL_IR_BINOP_LEQUAL, + HLSL_IR_BINOP_GEQUAL, + HLSL_IR_BINOP_EQUAL, + HLSL_IR_BINOP_NEQUAL, + + HLSL_IR_BINOP_LOGIC_AND, + HLSL_IR_BINOP_LOGIC_OR, + + HLSL_IR_BINOP_LSHIFT, + HLSL_IR_BINOP_RSHIFT, + HLSL_IR_BINOP_BIT_AND, + HLSL_IR_BINOP_BIT_OR, + HLSL_IR_BINOP_BIT_XOR, + + HLSL_IR_BINOP_DOT, + HLSL_IR_BINOP_CRS, + HLSL_IR_BINOP_MIN, + HLSL_IR_BINOP_MAX, + + HLSL_IR_BINOP_POW, + + HLSL_IR_TEROP_LERP, + + HLSL_IR_SEQUENCE, +}; + +struct hlsl_ir_expr +{ + struct hlsl_ir_node node; + enum hlsl_ir_expr_op op; + struct hlsl_src operands[3]; +}; + +enum hlsl_ir_jump_type +{ + HLSL_IR_JUMP_BREAK, + HLSL_IR_JUMP_CONTINUE, + HLSL_IR_JUMP_DISCARD, + HLSL_IR_JUMP_RETURN, +}; + +struct hlsl_ir_jump +{ + struct hlsl_ir_node node; + enum hlsl_ir_jump_type type; +}; + +struct hlsl_ir_swizzle +{ + struct hlsl_ir_node node; + struct hlsl_src val; + DWORD swizzle; +}; + +struct hlsl_deref +{ + struct hlsl_ir_var *var; + struct hlsl_src offset; +}; + +struct hlsl_ir_load +{ + struct hlsl_ir_node node; + struct hlsl_deref src; +}; + +struct hlsl_ir_assignment +{ + struct hlsl_ir_node node; + struct hlsl_deref lhs; + struct hlsl_src rhs; + unsigned char writemask; +}; + +struct hlsl_ir_constant +{ + struct hlsl_ir_node node; + union + { + unsigned u[4]; + int i[4]; + float f[4]; + double d[4]; + BOOL b[4]; + } value; +}; + +struct hlsl_scope +{ + struct list entry; + struct list vars; + struct rb_tree types; + struct hlsl_scope *upper; +}; + +/* Structures used only during parsing */ +struct parse_parameter +{ + struct hlsl_type *type; + const char *name; + const char *semantic; + const struct reg_reservation *reg_reservation; + unsigned int modifiers; +}; + +struct parse_colon_attribute +{ + const char *semantic; + struct reg_reservation *reg_reservation; +}; + +struct parse_initializer +{ + struct hlsl_ir_node **args; + unsigned int args_count; + struct list *instrs; +}; + +struct parse_variable_def +{ + struct list entry; + struct source_location loc; + + char *name; + unsigned int array_size; + const char *semantic; + struct 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_unary_op +{ + UNARY_OP_PLUS, + UNARY_OP_MINUS, + UNARY_OP_LOGICNOT, + UNARY_OP_BITNOT, +}; + +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, +}; + +struct hlsl_parse_ctx +{ + const char **source_files; + unsigned int source_files_count; + const char *source_file; + unsigned int line_no; + unsigned int column; + enum parse_status status; + struct vkd3d_shader_message_context *message_context; + + struct hlsl_scope *cur_scope; + struct hlsl_scope *globals; + struct list scopes; + + struct list types; + struct rb_tree functions; + const struct hlsl_ir_function_decl *cur_function; + + enum hlsl_matrix_majority matrix_majority; + + struct + { + struct hlsl_type *scalar[HLSL_TYPE_LAST_SCALAR + 1]; + struct hlsl_type *vector[HLSL_TYPE_LAST_SCALAR + 1][4]; + struct hlsl_type *sampler[HLSL_SAMPLER_DIM_MAX + 1]; + struct hlsl_type *Void; + } builtin_types; + + struct list static_initializers; +}; + +extern struct hlsl_parse_ctx hlsl_ctx DECLSPEC_HIDDEN; + +enum hlsl_error_level +{ + HLSL_LEVEL_ERROR = 0, + HLSL_LEVEL_WARNING, + HLSL_LEVEL_NOTE, +}; + +void hlsl_message(const char *fmt, ...) VKD3D_PRINTF_FUNC(1,2) DECLSPEC_HIDDEN; +void hlsl_report_message(const struct source_location loc, + enum hlsl_error_level level, const char *fmt, ...) VKD3D_PRINTF_FUNC(3,4) DECLSPEC_HIDDEN; + +static inline struct hlsl_ir_assignment *assignment_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_ASSIGNMENT); + return CONTAINING_RECORD(node, struct hlsl_ir_assignment, node); +} + +static inline struct hlsl_ir_constant *constant_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_CONSTANT); + return CONTAINING_RECORD(node, struct hlsl_ir_constant, node); +} + +static inline struct hlsl_ir_expr *expr_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_EXPR); + return CONTAINING_RECORD(node, struct hlsl_ir_expr, node); +} + +static inline struct hlsl_ir_if *if_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_IF); + return CONTAINING_RECORD(node, struct hlsl_ir_if, node); +} + +static inline struct hlsl_ir_jump *jump_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_JUMP); + return CONTAINING_RECORD(node, struct hlsl_ir_jump, node); +} + +static inline struct hlsl_ir_load *load_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_LOAD); + return CONTAINING_RECORD(node, struct hlsl_ir_load, node); +} + +static inline struct hlsl_ir_loop *loop_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_LOOP); + return CONTAINING_RECORD(node, struct hlsl_ir_loop, node); +} + +static inline struct hlsl_ir_swizzle *swizzle_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_SWIZZLE); + return CONTAINING_RECORD(node, struct hlsl_ir_swizzle, node); +} + +static inline void init_node(struct hlsl_ir_node *node, enum hlsl_ir_node_type type, + struct hlsl_type *data_type, struct source_location loc) +{ + memset(node, 0, sizeof(*node)); + node->type = type; + node->data_type = data_type; + node->loc = loc; + list_init(&node->uses); +} + +static inline void hlsl_src_from_node(struct hlsl_src *src, struct hlsl_ir_node *node) +{ + src->node = node; + if (node) + list_add_tail(&node->uses, &src->entry); +} + +static inline void hlsl_src_remove(struct hlsl_src *src) +{ + if (src->node) + list_remove(&src->entry); + src->node = NULL; +} + +static inline void set_parse_status(enum parse_status *current, enum parse_status update) +{ + if (update == PARSE_ERR) + *current = PARSE_ERR; + else if (update == PARSE_WARN && *current == PARSE_SUCCESS) + *current = PARSE_WARN; +} + +struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lhs, + enum parse_assign_op assign_op, struct hlsl_ir_node *rhs) DECLSPEC_HIDDEN; +struct hlsl_ir_expr *add_expr(struct list *instrs, enum hlsl_ir_expr_op op, struct hlsl_ir_node *operands[3], + struct source_location *loc) DECLSPEC_HIDDEN; +struct hlsl_ir_node *add_implicit_conversion(struct list *instrs, struct hlsl_ir_node *node, struct hlsl_type *type, + struct source_location *loc) DECLSPEC_HIDDEN; + +struct hlsl_ir_expr *new_cast(struct hlsl_ir_node *node, struct hlsl_type *type, + struct source_location *loc) DECLSPEC_HIDDEN; +struct hlsl_ir_node *new_binary_expr(enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg1, + struct hlsl_ir_node *arg2) DECLSPEC_HIDDEN; +struct hlsl_ir_node *new_unary_expr(enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg, + struct source_location loc) DECLSPEC_HIDDEN; + +BOOL add_declaration(struct hlsl_scope *scope, struct hlsl_ir_var *decl, BOOL local_var) DECLSPEC_HIDDEN; +struct hlsl_ir_var *get_variable(struct hlsl_scope *scope, const char *name) DECLSPEC_HIDDEN; +void free_declaration(struct hlsl_ir_var *decl) DECLSPEC_HIDDEN; +struct hlsl_type *new_hlsl_type(const char *name, enum hlsl_type_class type_class, + enum hlsl_base_type base_type, unsigned dimx, unsigned dimy) DECLSPEC_HIDDEN; +struct hlsl_type *new_array_type(struct hlsl_type *basic_type, unsigned int array_size) DECLSPEC_HIDDEN; +struct hlsl_type *clone_hlsl_type(struct hlsl_type *old, unsigned int default_majority) DECLSPEC_HIDDEN; +struct hlsl_type *get_type(struct hlsl_scope *scope, const char *name, BOOL recursive) DECLSPEC_HIDDEN; +BOOL is_row_major(const struct hlsl_type *type) DECLSPEC_HIDDEN; +BOOL find_function(const char *name) DECLSPEC_HIDDEN; +unsigned int components_count_type(struct hlsl_type *type) DECLSPEC_HIDDEN; +BOOL compare_hlsl_types(const struct hlsl_type *t1, const struct hlsl_type *t2) DECLSPEC_HIDDEN; +BOOL compatible_data_types(struct hlsl_type *s1, struct hlsl_type *s2) DECLSPEC_HIDDEN; +void push_scope(struct hlsl_parse_ctx *ctx) DECLSPEC_HIDDEN; +BOOL pop_scope(struct hlsl_parse_ctx *ctx) DECLSPEC_HIDDEN; +void init_functions_tree(struct rb_tree *funcs) DECLSPEC_HIDDEN; +void add_function_decl(struct rb_tree *funcs, char *name, struct hlsl_ir_function_decl *decl, + BOOL intrinsic) DECLSPEC_HIDDEN; + +int hlsl_lexer_compile(const char *text, enum vkd3d_shader_type type, DWORD major, DWORD minor, const char *entrypoint, + struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) DECLSPEC_HIDDEN; +int hlsl_parser_compile(enum vkd3d_shader_type type, DWORD major, DWORD minor, const char *entrypoint, + struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) DECLSPEC_HIDDEN; + +const char *debug_base_type(const struct hlsl_type *type) DECLSPEC_HIDDEN; +const char *debug_hlsl_type(const struct hlsl_type *type) DECLSPEC_HIDDEN; +const char *debug_modifiers(DWORD modifiers) DECLSPEC_HIDDEN; +const char *debug_node_type(enum hlsl_ir_node_type type) DECLSPEC_HIDDEN; +void debug_dump_ir_function_decl(const struct hlsl_ir_function_decl *func) DECLSPEC_HIDDEN; + +void free_hlsl_type(struct hlsl_type *type) DECLSPEC_HIDDEN; +void free_instr(struct hlsl_ir_node *node) DECLSPEC_HIDDEN; +void free_instr_list(struct list *list) DECLSPEC_HIDDEN; +void free_function_rb(struct rb_entry *entry, void *context) DECLSPEC_HIDDEN; + +#endif diff --git a/libs/vkd3d-shader/hlsl.l b/libs/vkd3d-shader/hlsl.l new file mode 100644 index 00000000..0bb1ee88 --- /dev/null +++ b/libs/vkd3d-shader/hlsl.l @@ -0,0 +1,288 @@ +/* + * HLSL parser + * + * Copyright 2008 Stefan Dösinger + * Copyright 2012 Matteo Bruni 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 + */ + +%{ +#define YY_NO_UNISTD_H +#include "hlsl.h" +#include "hlsl.tab.h" + +#define YY_USER_ACTION \ + do { \ + hlsl_lloc.first_column = hlsl_ctx.column; \ + hlsl_lloc.first_line = hlsl_ctx.line_no; \ + hlsl_ctx.column += yyleng; \ + } while(0); + +%} + +%option prefix="hlsl_" +%option never-interactive +%option noinput +%option nounput +%option noyywrap + +%x pp pp_line pp_pragma pp_ignore + +RESERVED1 auto|case|catch|char|class|const_cast|default|delete|dynamic_cast|enum +RESERVED2 explicit|friend|goto|long|mutable|new|operator|private|protected|public +RESERVED3 reinterpret_cast|short|signed|sizeof|static_cast|template|this|throw|try +RESERVED4 typename|union|unsigned|using|virtual + +WS [ \t] +NEWLINE (\n)|(\r\n) +DOUBLESLASHCOMMENT "//"[^\n]* +STRING "[^"]*" +IDENTIFIER [A-Za-z_][A-Za-z0-9_]* + +ANY (.) + +%% +{RESERVED1} { + hlsl_message("Line %u: Reserved keyword "%s" used.\n", hlsl_ctx.line_no, yytext); + set_parse_status(&hlsl_ctx.status, PARSE_ERR); + } +{RESERVED2} { + hlsl_message("Line %u: Reserved keyword "%s" used.\n", hlsl_ctx.line_no, yytext); + set_parse_status(&hlsl_ctx.status, PARSE_ERR); + } +{RESERVED3} { + hlsl_message("Line %u: Reserved keyword "%s" used.\n", hlsl_ctx.line_no, yytext); + set_parse_status(&hlsl_ctx.status, PARSE_ERR); + } +{RESERVED4} { + hlsl_message("Line %u: Reserved keyword "%s" used.\n", hlsl_ctx.line_no, yytext); + set_parse_status(&hlsl_ctx.status, PARSE_ERR); + } + +BlendState {return KW_BLENDSTATE; } +break {return KW_BREAK; } +Buffer {return KW_BUFFER; } +cbuffer {return KW_CBUFFER; } +compile {return KW_COMPILE; } +const {return KW_CONST; } +continue {return KW_CONTINUE; } +DepthStencilState {return KW_DEPTHSTENCILSTATE; } +DepthStencilView {return KW_DEPTHSTENCILVIEW; } +discard {return KW_DISCARD; } +do {return KW_DO; } +double {return KW_DOUBLE; } +else {return KW_ELSE; } +extern {return KW_EXTERN; } +false {return KW_FALSE; } +for {return KW_FOR; } +GeometryShader {return KW_GEOMETRYSHADER; } +groupshared {return KW_GROUPSHARED; } +if {return KW_IF; } +in {return KW_IN; } +inline {return KW_INLINE; } +inout {return KW_INOUT; } +matrix {return KW_MATRIX; } +namespace {return KW_NAMESPACE; } +nointerpolation {return KW_NOINTERPOLATION; } +out {return KW_OUT; } +pass {return KW_PASS; } +PixelShader {return KW_PIXELSHADER; } +precise {return KW_PRECISE; } +RasterizerState {return KW_RASTERIZERSTATE; } +RenderTargetView {return KW_RENDERTARGETVIEW; } +return {return KW_RETURN; } +register {return KW_REGISTER; } +sampler {return KW_SAMPLER; } +sampler1D {return KW_SAMPLER1D; } +sampler2D {return KW_SAMPLER2D; } +sampler3D {return KW_SAMPLER3D; } +samplerCUBE {return KW_SAMPLERCUBE; } +sampler_state {return KW_SAMPLER_STATE; } +SamplerComparisonState {return KW_SAMPLERCOMPARISONSTATE;} +shared {return KW_SHARED; } +stateblock {return KW_STATEBLOCK; } +stateblock_state {return KW_STATEBLOCK_STATE; } +static {return KW_STATIC; } +string {return KW_STRING; } +struct {return KW_STRUCT; } +switch {return KW_SWITCH; } +tbuffer {return KW_TBUFFER; } +technique {return KW_TECHNIQUE; } +technique10 {return KW_TECHNIQUE10; } +texture {return KW_TEXTURE; } +texture1D {return KW_TEXTURE1D; } +Texture1DArray {return KW_TEXTURE1DARRAY; } +texture2D {return KW_TEXTURE2D; } +Texture2DArray {return KW_TEXTURE2DARRAY; } +Texture2DMS {return KW_TEXTURE2DMS; } +Texture2DMSArray {return KW_TEXTURE2DMSARRAY; } +texture3D {return KW_TEXTURE3D; } +Texture3DArray {return KW_TEXTURE3DARRAY; } +textureCUBE {return KW_TEXTURECUBE; } +true {return KW_TRUE; } +typedef {return KW_TYPEDEF; } +uniform {return KW_UNIFORM; } +vector {return KW_VECTOR; } +VertexShader {return KW_VERTEXSHADER; } +void {return KW_VOID; } +volatile {return KW_VOLATILE; } +while {return KW_WHILE; } + +++ {return OP_INC; } +-- {return OP_DEC; } +&& {return OP_AND; } +|| {return OP_OR; } +== {return OP_EQ; } +<< {return OP_LEFTSHIFT; } +<<= {return OP_LEFTSHIFTASSIGN; } +>> {return OP_RIGHTSHIFT; } +>>= {return OP_RIGHTSHIFTASSIGN; } +... {return OP_ELLIPSIS; } +<= {return OP_LE; } +>= {return OP_GE; } +!= {return OP_NE; } ++= {return OP_ADDASSIGN; } +-= {return OP_SUBASSIGN; } +*= {return OP_MULASSIGN; } +/= {return OP_DIVASSIGN; } +%= {return OP_MODASSIGN; } +&= {return OP_ANDASSIGN; } +|= {return OP_ORASSIGN; } +^= {return OP_XORASSIGN; } +## {return OP_UNKNOWN1; } +#@ {return OP_UNKNOWN2; } +:: {return OP_UNKNOWN3; } +-> {return OP_UNKNOWN4; } + +column_major {return KW_COLUMN_MAJOR; } +row_major {return KW_ROW_MAJOR; } + +{IDENTIFIER} { + hlsl_lval.name = vkd3d_strdup(yytext); + if (get_variable(hlsl_ctx.cur_scope, yytext) + || find_function(yytext)) + return VAR_IDENTIFIER; + else if (get_type(hlsl_ctx.cur_scope, yytext, TRUE)) + return TYPE_IDENTIFIER; + else + return NEW_IDENTIFIER; + } + +[0-9]*.[0-9]+([eE][+-]?[0-9]+)?[h|H|f|F]? { + hlsl_lval.floatval = atof(yytext); + return C_FLOAT; + } +[0-9]+.([eE][+-]?[0-9]+)?[h|H|f|F]? { + hlsl_lval.floatval = atof(yytext); + return C_FLOAT; + } +[0-9]+([eE][+-]?[0-9]+)?[h|H|f|F] { + hlsl_lval.floatval = atof(yytext); + return C_FLOAT; + } +0x[0-9a-fA-F]+ { + sscanf(yytext, "0x%x", &hlsl_lval.intval); + return C_INTEGER; + } +0[0-7]+ { + sscanf(yytext, "0%o", &hlsl_lval.intval); + return C_INTEGER; + } +[0-9]+ { + hlsl_lval.intval = (atoi(yytext)); + return C_INTEGER; + } + +{DOUBLESLASHCOMMENT} {} + +{WS}+ {} +{NEWLINE} { + hlsl_ctx.line_no++; + hlsl_ctx.column = 1; + } + +^# { + BEGIN(pp); + } + +<pp>pragma{WS}+ { + TRACE("Got a #pragma.\n"); + BEGIN(pp_pragma); + } +<pp_pragma>pack_matrix{WS}*({WS}*row_major{WS}*) { + TRACE("#pragma setting row_major mode.\n"); + hlsl_ctx.matrix_majority = HLSL_ROW_MAJOR; + BEGIN(pp_ignore); + } +<pp_pragma>pack_matrix{WS}*({WS}*column_major{WS}*) { + TRACE("#pragma setting column_major mode.\n"); + hlsl_ctx.matrix_majority = HLSL_COLUMN_MAJOR; + BEGIN(pp_ignore); + } +<pp_pragma>{NEWLINE} { + FIXME("Unsupported preprocessor #pragma directive at line %u.\n", hlsl_ctx.line_no); + BEGIN(INITIAL); + } +<pp_pragma>{ANY} {} +<pp>[0-9]+ { + TRACE("Preprocessor line info.\n"); + BEGIN(pp_line); + hlsl_lval.intval = (atoi(yytext)); + return PRE_LINE; + } +<pp_line>{STRING} { + char *string = vkd3d_strdup(yytext + 1); + + BEGIN(pp_ignore); + string[strlen(string) - 1] = 0; + hlsl_lval.name = string; + return STRING; + } +<pp_line>{WS}+ {} +<pp_line>{NEWLINE} { + FIXME("Malformed preprocessor line directive?\n"); + BEGIN(INITIAL); + } +<pp_ignore>{NEWLINE} { + BEGIN(INITIAL); + } +<pp_ignore>{ANY} {} +<pp>{NEWLINE} { + FIXME("Unexpected preprocessor directive.\n"); + BEGIN(INITIAL); + } +<pp>{ANY} {} + +{ANY} { + return yytext[0]; + } + +%% + +int hlsl_lexer_compile(const char *text, enum vkd3d_shader_type type, DWORD major, DWORD minor, const char *entrypoint, + struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) +{ + YY_BUFFER_STATE buffer; + int ret; + + buffer = hlsl__scan_string(text); + hlsl__switch_to_buffer(buffer); + + ret = hlsl_parser_compile(type, major, minor, entrypoint, dxbc, message_context); + + hlsl__delete_buffer(buffer); + return ret; +} diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y new file mode 100644 index 00000000..4e2218c5 --- /dev/null +++ b/libs/vkd3d-shader/hlsl.y @@ -0,0 +1,2961 @@ +/* + * 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 + */ +%{ +#include "hlsl.h" +#include <stdio.h> + +int hlsl_lex(void); + +struct hlsl_parse_ctx hlsl_ctx; + +struct YYLTYPE; +static struct source_location get_location(const struct YYLTYPE *l); + +void hlsl_message(const char *fmt, ...) +{ + /* FIXME */ +} + +void hlsl_report_message(const struct source_location loc, + enum hlsl_error_level level, const char *fmt, ...) +{ + /* FIXME */ + + if (level == HLSL_LEVEL_ERROR) + set_parse_status(&hlsl_ctx.status, PARSE_ERR); + else if (level == HLSL_LEVEL_WARNING) + set_parse_status(&hlsl_ctx.status, PARSE_WARN); +} + +static void hlsl_error(const char *s) +{ + const struct source_location loc = + { + .file = hlsl_ctx.source_file, + .line = hlsl_ctx.line_no, + .col = hlsl_ctx.column, + }; + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "%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 void debug_dump_decl(struct hlsl_type *type, DWORD modifiers, const char *declname, unsigned int line_no) +{ + TRACE("Line %u: ", line_no); + if (modifiers) + TRACE("%s ", debug_modifiers(modifiers)); + TRACE("%s %s;\n", debug_hlsl_type(type), declname); +} + +static void check_invalid_matrix_modifiers(DWORD modifiers, struct source_location loc) +{ + if (modifiers & HLSL_MODIFIERS_MAJORITY_MASK) + hlsl_report_message(loc, HLSL_LEVEL_ERROR, + "'row_major' or 'column_major' modifiers are only allowed for matrices."); +} + +static BOOL type_is_single_reg(const struct hlsl_type *type) +{ + return type->type == HLSL_CLASS_SCALAR || type->type == HLSL_CLASS_VECTOR; +} + +static BOOL declare_variable(struct hlsl_ir_var *decl, BOOL local) +{ + BOOL ret; + + TRACE("Declaring variable %s.\n", decl->name); + if (decl->data_type->type != HLSL_CLASS_MATRIX) + check_invalid_matrix_modifiers(decl->modifiers, decl->loc); + + if (local) + { + DWORD invalid = decl->modifiers & (HLSL_STORAGE_EXTERN | HLSL_STORAGE_SHARED + | HLSL_STORAGE_GROUPSHARED | HLSL_STORAGE_UNIFORM); + + if (invalid) + { + hlsl_report_message(decl->loc, HLSL_LEVEL_ERROR, + "modifier '%s' invalid for local variables", debug_modifiers(invalid)); + } + if (decl->semantic) + { + hlsl_report_message(decl->loc, HLSL_LEVEL_ERROR, + "semantics are not allowed on local variables"); + return FALSE; + } + } + else + { + if (find_function(decl->name)) + { + hlsl_report_message(decl->loc, HLSL_LEVEL_ERROR, "redefinition of '%s'", decl->name); + return FALSE; + } + } + ret = add_declaration(hlsl_ctx.cur_scope, decl, local); + if (!ret) + { + struct hlsl_ir_var *old = get_variable(hlsl_ctx.cur_scope, decl->name); + + hlsl_report_message(decl->loc, HLSL_LEVEL_ERROR, ""%s" already declared", decl->name); + hlsl_report_message(old->loc, HLSL_LEVEL_NOTE, ""%s" was previously declared here", old->name); + return FALSE; + } + return TRUE; +} + +static DWORD add_modifiers(DWORD modifiers, DWORD mod, const struct source_location loc) +{ + if (modifiers & mod) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "modifier '%s' already specified", debug_modifiers(mod)); + return modifiers; + } + if ((mod & HLSL_MODIFIERS_MAJORITY_MASK) && (modifiers & HLSL_MODIFIERS_MAJORITY_MASK)) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "more than one matrix majority keyword"); + return modifiers; + } + return modifiers | mod; +} + +static BOOL add_type_to_scope(struct hlsl_scope *scope, struct hlsl_type *def) +{ + if (get_type(scope, def->name, FALSE)) + return FALSE; + + rb_put(&scope->types, def->name, &def->scope_entry); + return TRUE; +} + +static void declare_predefined_types(struct hlsl_scope *scope) +{ + struct hlsl_type *type; + unsigned int x, y, bt; + static const char * const names[] = + { + "float", + "half", + "double", + "int", + "uint", + "bool", + }; + char name[10]; + + static const char *const sampler_names[] = + { + "sampler", + "sampler1D", + "sampler2D", + "sampler3D", + "samplerCUBE" + }; + + for (bt = 0; bt <= HLSL_TYPE_LAST_SCALAR; ++bt) + { + for (y = 1; y <= 4; ++y) + { + for (x = 1; x <= 4; ++x) + { + sprintf(name, "%s%ux%u", names[bt], y, x); + type = new_hlsl_type(vkd3d_strdup(name), HLSL_CLASS_MATRIX, bt, x, y); + add_type_to_scope(scope, type); + + if (y == 1) + { + sprintf(name, "%s%u", names[bt], x); + type = new_hlsl_type(vkd3d_strdup(name), HLSL_CLASS_VECTOR, bt, x, y); + add_type_to_scope(scope, type); + hlsl_ctx.builtin_types.vector[bt][x - 1] = type; + + if (x == 1) + { + sprintf(name, "%s", names[bt]); + type = new_hlsl_type(vkd3d_strdup(name), HLSL_CLASS_SCALAR, bt, x, y); + add_type_to_scope(scope, type); + hlsl_ctx.builtin_types.scalar[bt] = type; + } + } + } + } + } + + for (bt = 0; bt <= HLSL_SAMPLER_DIM_MAX; ++bt) + { + type = new_hlsl_type(vkd3d_strdup(sampler_names[bt]), HLSL_CLASS_OBJECT, HLSL_TYPE_SAMPLER, 1, 1); + type->sampler_dim = bt; + hlsl_ctx.builtin_types.sampler[bt] = type; + } + + hlsl_ctx.builtin_types.Void = new_hlsl_type(vkd3d_strdup("void"), HLSL_CLASS_OBJECT, HLSL_TYPE_VOID, 1, 1); + + /* DX8 effects predefined types */ + type = new_hlsl_type(vkd3d_strdup("DWORD"), HLSL_CLASS_SCALAR, HLSL_TYPE_INT, 1, 1); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("FLOAT"), HLSL_CLASS_SCALAR, HLSL_TYPE_FLOAT, 1, 1); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("VECTOR"), HLSL_CLASS_VECTOR, HLSL_TYPE_FLOAT, 4, 1); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("MATRIX"), HLSL_CLASS_MATRIX, HLSL_TYPE_FLOAT, 4, 4); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("STRING"), HLSL_CLASS_OBJECT, HLSL_TYPE_STRING, 1, 1); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("TEXTURE"), HLSL_CLASS_OBJECT, HLSL_TYPE_TEXTURE, 1, 1); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("PIXELSHADER"), HLSL_CLASS_OBJECT, HLSL_TYPE_PIXELSHADER, 1, 1); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("VERTEXSHADER"), HLSL_CLASS_OBJECT, HLSL_TYPE_VERTEXSHADER, 1, 1); + add_type_to_scope(scope, type); +} + +static BOOL type_is_void(const struct hlsl_type *type) +{ + return type->type == HLSL_CLASS_OBJECT && type->base_type == HLSL_TYPE_VOID; +} + +static struct hlsl_ir_if *new_if(struct hlsl_ir_node *condition, struct source_location loc) +{ + struct hlsl_ir_if *iff; + + if (!(iff = vkd3d_malloc(sizeof(*iff)))) + return NULL; + init_node(&iff->node, HLSL_IR_IF, NULL, loc); + hlsl_src_from_node(&iff->condition, condition); + list_init(&iff->then_instrs); + list_init(&iff->else_instrs); + return iff; +} + +static BOOL append_conditional_break(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 = new_unary_expr(HLSL_IR_UNOP_LOGIC_NOT, condition, condition->loc))) + return FALSE; + list_add_tail(cond_list, ¬->entry); + + if (!(iff = new_if(not, condition->loc))) + return FALSE; + list_add_tail(cond_list, &iff->node.entry); + + if (!(jump = vkd3d_malloc(sizeof(*jump)))) + return FALSE; + init_node(&jump->node, HLSL_IR_JUMP, NULL, condition->loc); + jump->type = HLSL_IR_JUMP_BREAK; + 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(enum loop_type type, struct list *init, struct list *cond, + struct list *iter, struct list *body, struct source_location loc) +{ + struct list *list = NULL; + struct hlsl_ir_loop *loop = NULL; + struct hlsl_ir_if *cond_jump = NULL; + + if (!(list = vkd3d_malloc(sizeof(*list)))) + goto oom; + list_init(list); + + if (init) + list_move_head(list, init); + + if (!(loop = vkd3d_calloc(1, sizeof(*loop)))) + goto oom; + init_node(&loop->node, HLSL_IR_LOOP, NULL, loc); + list_add_tail(list, &loop->node.entry); + list_init(&loop->body); + + if (!append_conditional_break(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); + free_instr_list(init); + free_instr_list(cond); + free_instr_list(iter); + free_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 += components_count_type(initializer->args[i]->data_type); + } + TRACE("Initializer size = %u.\n", count); + return count; +} + +static void free_parse_initializer(struct parse_initializer *initializer) +{ + free_instr_list(initializer->instrs); + vkd3d_free(initializer->args); +} + +static struct hlsl_ir_swizzle *new_swizzle(DWORD s, unsigned int components, + struct hlsl_ir_node *val, struct source_location *loc) +{ + struct hlsl_ir_swizzle *swizzle; + + if (!(swizzle = vkd3d_malloc(sizeof(*swizzle)))) + return NULL; + init_node(&swizzle->node, HLSL_IR_SWIZZLE, + new_hlsl_type(NULL, HLSL_CLASS_VECTOR, val->data_type->base_type, components, 1), *loc); + hlsl_src_from_node(&swizzle->val, val); + swizzle->swizzle = s; + return swizzle; +} + +static struct hlsl_ir_swizzle *get_swizzle(struct hlsl_ir_node *value, const char *swizzle, + struct source_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 new_swizzle(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 new_swizzle(swiz, component, value, loc); + } + + return NULL; +} + +static struct hlsl_ir_var *new_var(const char *name, struct hlsl_type *type, const struct source_location loc, + const char *semantic, unsigned int modifiers, const struct reg_reservation *reg_reservation) +{ + struct hlsl_ir_var *var; + + if (!(var = vkd3d_calloc(1, sizeof(*var)))) + { + hlsl_ctx.status = PARSE_ERR; + return NULL; + } + + var->name = name; + var->data_type = type; + var->loc = loc; + var->semantic = semantic; + var->modifiers = modifiers; + var->reg_reservation = reg_reservation; + return var; +} + +static struct hlsl_ir_var *new_synthetic_var(const char *name, struct hlsl_type *type, + const struct source_location loc) +{ + struct hlsl_ir_var *var = new_var(vkd3d_strdup(name), type, loc, NULL, 0, NULL); + + if (var) + list_add_tail(&hlsl_ctx.globals->vars, &var->scope_entry); + return var; +} + +static struct hlsl_ir_assignment *new_assignment(struct hlsl_ir_var *var, struct hlsl_ir_node *offset, + struct hlsl_ir_node *rhs, unsigned int writemask, struct source_location loc) +{ + struct hlsl_ir_assignment *assign; + + if (!writemask && type_is_single_reg(rhs->data_type)) + writemask = (1 << rhs->data_type->dimx) - 1; + + if (!(assign = vkd3d_malloc(sizeof(*assign)))) + return NULL; + + init_node(&assign->node, HLSL_IR_ASSIGNMENT, NULL, loc); + assign->lhs.var = var; + hlsl_src_from_node(&assign->lhs.offset, offset); + hlsl_src_from_node(&assign->rhs, rhs); + assign->writemask = writemask; + return assign; +} + +static struct hlsl_ir_assignment *new_simple_assignment(struct hlsl_ir_var *lhs, struct hlsl_ir_node *rhs) +{ + return new_assignment(lhs, NULL, rhs, 0, rhs->loc); +} + +static struct hlsl_ir_jump *add_return(struct list *instrs, + struct hlsl_ir_node *return_value, struct source_location loc) +{ + struct hlsl_type *return_type = hlsl_ctx.cur_function->return_type; + struct hlsl_ir_jump *jump; + + if (return_value) + { + struct hlsl_ir_assignment *assignment; + + if (!(return_value = add_implicit_conversion(instrs, return_value, return_type, &loc))) + return NULL; + + if (!(assignment = new_simple_assignment(hlsl_ctx.cur_function->return_var, return_value))) + return NULL; + list_add_after(&return_value->entry, &assignment->node.entry); + } + else if (!type_is_void(return_type)) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "non-void function must return a value"); + return NULL; + } + + if (!(jump = vkd3d_malloc(sizeof(*jump)))) + return NULL; + init_node(&jump->node, HLSL_IR_JUMP, NULL, loc); + jump->type = HLSL_IR_JUMP_RETURN; + list_add_tail(instrs, &jump->node.entry); + + return jump; +} + +static struct hlsl_ir_constant *new_uint_constant(unsigned int n, const struct source_location loc) +{ + struct hlsl_ir_constant *c; + + if (!(c = vkd3d_malloc(sizeof(*c)))) + return NULL; + init_node(&c->node, HLSL_IR_CONSTANT, hlsl_ctx.builtin_types.scalar[HLSL_TYPE_UINT], loc); + c->value.u[0] = n; + return c; +} + +struct hlsl_ir_node *new_unary_expr(enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg, struct source_location loc) +{ + struct hlsl_ir_expr *expr; + + if (!(expr = vkd3d_calloc(1, sizeof(*expr)))) + return NULL; + init_node(&expr->node, HLSL_IR_EXPR, arg->data_type, loc); + expr->op = op; + hlsl_src_from_node(&expr->operands[0], arg); + return &expr->node; +} + +struct hlsl_ir_node *new_binary_expr(enum hlsl_ir_expr_op op, + struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2) +{ + struct hlsl_ir_expr *expr; + + assert(compare_hlsl_types(arg1->data_type, arg2->data_type)); + + if (!(expr = vkd3d_calloc(1, sizeof(*expr)))) + return NULL; + init_node(&expr->node, HLSL_IR_EXPR, arg1->data_type, arg1->loc); + expr->op = op; + hlsl_src_from_node(&expr->operands[0], arg1); + hlsl_src_from_node(&expr->operands[1], arg2); + return &expr->node; +} + +static struct hlsl_ir_load *new_var_load(struct hlsl_ir_var *var, const struct source_location loc) +{ + struct hlsl_ir_load *load; + + if (!(load = vkd3d_calloc(1, sizeof(*load)))) + return NULL; + init_node(&load->node, HLSL_IR_LOAD, var->data_type, loc); + load->src.var = var; + return load; +} + +static struct hlsl_ir_load *add_load(struct list *instrs, struct hlsl_ir_node *var_node, struct hlsl_ir_node *offset, + struct hlsl_type *data_type, const struct source_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 = &load_from_node(var_node)->src; + + var = src->var; + if (src->offset.node) + { + if (!(add = new_binary_expr(HLSL_IR_BINOP_ADD, src->offset.node, offset))) + return NULL; + list_add_tail(instrs, &add->entry); + offset = add; + } + } + else + { + struct hlsl_ir_assignment *assign; + char name[27]; + + sprintf(name, "<deref-%p>", var_node); + if (!(var = new_synthetic_var(name, var_node->data_type, var_node->loc))) + return NULL; + + TRACE("Synthesized variable %p for %s node.\n", var, debug_node_type(var_node->type)); + + if (!(assign = new_simple_assignment(var, var_node))) + return NULL; + + list_add_tail(instrs, &assign->node.entry); + } + + if (!(load = vkd3d_malloc(sizeof(*load)))) + return NULL; + init_node(&load->node, HLSL_IR_LOAD, data_type, loc); + load->src.var = var; + hlsl_src_from_node(&load->src.offset, offset); + list_add_tail(instrs, &load->node.entry); + return load; +} + +static struct hlsl_ir_load *add_record_load(struct list *instrs, struct hlsl_ir_node *record, + const struct hlsl_struct_field *field, const struct source_location loc) +{ + struct hlsl_ir_constant *c; + + if (!(c = new_uint_constant(field->reg_offset * 4, loc))) + return NULL; + list_add_tail(instrs, &c->node.entry); + + return add_load(instrs, record, &c->node, field->type, loc); +} + +static struct hlsl_ir_load *add_array_load(struct list *instrs, struct hlsl_ir_node *array, + struct hlsl_ir_node *index, const struct source_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; + + TRACE("Array load from type %s.\n", debug_hlsl_type(expr_type)); + + 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_report_message(loc, HLSL_LEVEL_ERROR, "array-indexed expression is scalar"); + else + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "expression is not array-indexable"); + return NULL; + } + + if (!(c = new_uint_constant(data_type->reg_size * 4, loc))) + return NULL; + list_add_tail(instrs, &c->node.entry); + if (!(mul = new_binary_expr(HLSL_IR_BINOP_MUL, index, &c->node))) + return NULL; + list_add_tail(instrs, &mul->entry); + index = mul; + + return add_load(instrs, array, index, data_type, loc); +} + +static BOOL add_struct_field(struct list *fields, struct hlsl_struct_field *field) +{ + struct hlsl_struct_field *f; + + LIST_FOR_EACH_ENTRY(f, fields, struct hlsl_struct_field, entry) + { + if (!strcmp(f->name, field->name)) + return FALSE; + } + list_add_tail(fields, &field->entry); + return TRUE; +} + +BOOL is_row_major(const struct hlsl_type *type) +{ + /* Default to column-major if the majority isn't explicitly set, which can + * happen for anonymous nodes. */ + return !!(type->modifiers & HLSL_MODIFIER_ROW_MAJOR); +} + +static struct hlsl_type *apply_type_modifiers(struct hlsl_type *type, + unsigned int *modifiers, struct source_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 (hlsl_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 = clone_hlsl_type(type, default_majority))) + return NULL; + + new_type->modifiers = add_modifiers(new_type->modifiers, *modifiers, loc); + *modifiers &= ~HLSL_TYPE_MODIFIERS_MASK; + + if (new_type->type == HLSL_CLASS_MATRIX) + new_type->reg_size = is_row_major(new_type) ? new_type->dimy : new_type->dimx; + return new_type; +} + +static struct list *gen_struct_fields(struct hlsl_type *type, DWORD modifiers, 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 = vkd3d_malloc(sizeof(*list)))) + return NULL; + list_init(list); + LIST_FOR_EACH_ENTRY_SAFE(v, v_next, fields, struct parse_variable_def, entry) + { + debug_dump_decl(type, 0, v->name, v->loc.line); + if (!(field = vkd3d_calloc(1, sizeof(*field)))) + { + vkd3d_free(v); + return list; + } + if (v->array_size) + field->type = new_array_type(type, v->array_size); + else + field->type = type; + field->name = v->name; + field->modifiers = modifiers; + field->semantic = v->semantic; + if (v->initializer.args_count) + { + hlsl_report_message(v->loc, HLSL_LEVEL_ERROR, "struct field with an initializer.\n"); + free_parse_initializer(&v->initializer); + } + list_add_tail(list, &field->entry); + vkd3d_free(v); + } + vkd3d_free(fields); + return list; +} + +static DWORD get_array_size(const struct hlsl_type *type) +{ + if (type->type == HLSL_CLASS_ARRAY) + return get_array_size(type->e.array.type) * type->e.array.elements_count; + return 1; +} + +static struct hlsl_type *new_struct_type(const char *name, struct list *fields) +{ + struct hlsl_struct_field *field; + unsigned int reg_size = 0; + struct hlsl_type *type; + + if (!(type = vkd3d_calloc(1, sizeof(*type)))) + return NULL; + type->type = HLSL_CLASS_STRUCT; + type->base_type = HLSL_TYPE_VOID; + type->name = name; + type->dimx = 0; + type->dimy = 1; + type->e.elements = fields; + + LIST_FOR_EACH_ENTRY(field, fields, struct hlsl_struct_field, entry) + { + field->reg_offset = reg_size; + reg_size += field->type->reg_size; + type->dimx += field->type->dimx * field->type->dimy * get_array_size(field->type); + } + type->reg_size = reg_size; + + list_add_tail(&hlsl_ctx.types, &type->entry); + + return type; +} + +static BOOL add_typedef(DWORD modifiers, struct hlsl_type *orig_type, struct list *list) +{ + struct parse_variable_def *v, *v_next; + struct hlsl_type *type; + BOOL ret; + + LIST_FOR_EACH_ENTRY_SAFE(v, v_next, list, struct parse_variable_def, entry) + { + if (v->array_size) + type = new_array_type(orig_type, v->array_size); + else + type = clone_hlsl_type(orig_type, 0); + if (!type) + return FALSE; + vkd3d_free((void *)type->name); + type->name = v->name; + type->modifiers |= modifiers; + + if (type->type != HLSL_CLASS_MATRIX) + check_invalid_matrix_modifiers(type->modifiers, v->loc); + else + type->reg_size = is_row_major(type) ? type->dimy : type->dimx; + + if ((type->modifiers & HLSL_MODIFIER_COLUMN_MAJOR) + && (type->modifiers & HLSL_MODIFIER_ROW_MAJOR)) + hlsl_report_message(v->loc, HLSL_LEVEL_ERROR, "more than one matrix majority keyword"); + + ret = add_type_to_scope(hlsl_ctx.cur_scope, type); + if (!ret) + hlsl_report_message(v->loc, HLSL_LEVEL_ERROR, + "redefinition of custom type '%s'", v->name); + vkd3d_free(v); + } + vkd3d_free(list); + return TRUE; +} + +static BOOL add_func_parameter(struct list *list, struct parse_parameter *param, const struct source_location loc) +{ + struct hlsl_ir_var *var; + + if (param->type->type == HLSL_CLASS_MATRIX) + assert(param->type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK); + + if (!(var = new_var(param->name, param->type, loc, param->semantic, param->modifiers, param->reg_reservation))) + return FALSE; + + if (!add_declaration(hlsl_ctx.cur_scope, var, FALSE)) + { + free_declaration(var); + return FALSE; + } + list_add_tail(list, &var->param_entry); + return TRUE; +} + +static struct reg_reservation *parse_reg_reservation(const char *reg_string) +{ + enum vkd3d_shader_register_type type; + struct reg_reservation *reg_res; + DWORD regnum = 0; + + switch (reg_string[0]) + { + case 'c': + type = VKD3DSPR_CONST; + break; + case 'i': + type = VKD3DSPR_CONSTINT; + break; + case 'b': + type = VKD3DSPR_CONSTBOOL; + break; + case 's': + type = VKD3DSPR_SAMPLER; + break; + default: + FIXME("Unsupported register type.\n"); + return NULL; + } + + if (!sscanf(reg_string + 1, "%u", ®num)) + { + FIXME("Unsupported register reservation syntax.\n"); + return NULL; + } + + if (!(reg_res = vkd3d_malloc(sizeof(*reg_res)))) + return NULL; + reg_res->type = type; + reg_res->regnum = regnum; + return reg_res; +} + +static const struct hlsl_ir_function_decl *get_overloaded_func(struct rb_tree *funcs, char *name, + struct list *params, BOOL exact_signature) +{ + struct hlsl_ir_function *func; + struct rb_entry *entry; + + entry = rb_get(funcs, name); + if (entry) + { + func = RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry); + + entry = rb_get(&func->overloads, params); + if (!entry) + { + if (!exact_signature) + FIXME("No exact match, search for a compatible overloaded function (if any).\n"); + return NULL; + } + return RB_ENTRY_VALUE(entry, struct hlsl_ir_function_decl, entry); + } + return NULL; +} + +static struct hlsl_ir_function_decl *get_func_entry(const char *name) +{ + struct hlsl_ir_function_decl *decl; + struct hlsl_ir_function *func; + struct rb_entry *entry; + + if ((entry = rb_get(&hlsl_ctx.functions, name))) + { + func = RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry); + RB_FOR_EACH_ENTRY(decl, &func->overloads, struct hlsl_ir_function_decl, entry) + return decl; + } + + return NULL; +} + +static struct list *make_list(struct hlsl_ir_node *node) +{ + struct list *list; + + if (!(list = vkd3d_malloc(sizeof(*list)))) + { + free_instr(node); + return NULL; + } + list_init(list); + 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 = constant_from_node(node); + + switch (constant->node.data_type->base_type) + { + case HLSL_TYPE_UINT: + return constant->value.u[0]; + case HLSL_TYPE_INT: + return constant->value.i[0]; + case HLSL_TYPE_FLOAT: + return constant->value.f[0]; + case HLSL_TYPE_DOUBLE: + return constant->value.d[0]; + case HLSL_TYPE_BOOL: + return constant->value.b[0]; + default: + WARN("Invalid type %s.\n", debug_base_type(constant->node.data_type)); + return 0; + } + } + + case HLSL_IR_EXPR: + case HLSL_IR_LOAD: + case HLSL_IR_SWIZZLE: + FIXME("Unhandled type %s.\n", debug_node_type(node->type)); + return 0; + + case HLSL_IR_ASSIGNMENT: + default: + WARN("Invalid node type %s.\n", debug_node_type(node->type)); + return 0; + } +} + +static struct list *append_unop(struct list *list, struct hlsl_ir_node *node) +{ + list_add_tail(list, &node->entry); + return list; +} + +static struct list *add_binary_expr(struct list *list1, struct list *list2, + enum hlsl_ir_expr_op op, struct source_location loc) +{ + struct hlsl_ir_node *args[3] = {node_from_list(list1), node_from_list(list2)}; + + list_move_tail(list1, list2); + vkd3d_free(list2); + add_expr(list1, op, args, &loc); + return list1; +} + +static void struct_var_initializer(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) != components_count_type(type)) + { + hlsl_report_message(var->loc, HLSL_LEVEL_ERROR, "structure initializer mismatch"); + 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_assignment *assign; + struct hlsl_ir_constant *c; + + if (i++ >= initializer->args_count) + break; + + if (components_count_type(field->type) == components_count_type(node->data_type)) + { + if (!(c = new_uint_constant(field->reg_offset * 4, node->loc))) + break; + list_add_tail(list, &c->node.entry); + + if (!(assign = new_assignment(var, &c->node, node, 0, node->loc))) + break; + list_add_tail(list, &assign->node.entry); + } + else + FIXME("Initializing with "mismatched" fields is not supported yet.\n"); + } + + vkd3d_free(initializer->args); +} + +static void free_parse_variable_def(struct parse_variable_def *v) +{ + free_parse_initializer(&v->initializer); + vkd3d_free(v->name); + vkd3d_free((void *)v->semantic); + vkd3d_free(v->reg_reservation); + vkd3d_free(v); +} + +static struct list *declare_vars(struct hlsl_type *basic_type, DWORD modifiers, struct list *var_list) +{ + struct parse_variable_def *v, *v_next; + struct list *statements_list; + struct hlsl_ir_var *var; + struct hlsl_type *type; + BOOL ret, local = TRUE; + + if (basic_type->type == HLSL_CLASS_MATRIX) + assert(basic_type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK); + + if (!(statements_list = vkd3d_malloc(sizeof(*statements_list)))) + { + 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; + } + list_init(statements_list); + + if (!var_list) + return statements_list; + + LIST_FOR_EACH_ENTRY_SAFE(v, v_next, var_list, struct parse_variable_def, entry) + { + if (v->array_size) + type = new_array_type(basic_type, v->array_size); + else + type = basic_type; + + if (!(var = new_var(v->name, type, v->loc, v->semantic, modifiers, v->reg_reservation))) + { + free_parse_variable_def(v); + continue; + } + debug_dump_decl(type, modifiers, v->name, v->loc.line); + + if (hlsl_ctx.cur_scope == hlsl_ctx.globals) + { + var->modifiers |= HLSL_STORAGE_UNIFORM; + local = FALSE; + } + + if (type->modifiers & HLSL_MODIFIER_CONST && !(var->modifiers & HLSL_STORAGE_UNIFORM) && !v->initializer.args_count) + { + hlsl_report_message(v->loc, HLSL_LEVEL_ERROR, "const variable without initializer"); + free_declaration(var); + vkd3d_free(v); + continue; + } + + ret = declare_variable(var, local); + if (!ret) + { + free_declaration(var); + vkd3d_free(v); + continue; + } + TRACE("Declared variable %s.\n", var->name); + + if (v->initializer.args_count) + { + unsigned int size = initializer_size(&v->initializer); + struct hlsl_ir_load *load; + + TRACE("Variable with initializer.\n"); + if (type->type <= HLSL_CLASS_LAST_NUMERIC + && type->dimx * type->dimy != size && size != 1) + { + if (size < type->dimx * type->dimy) + { + hlsl_report_message(v->loc, HLSL_LEVEL_ERROR, + "'%s' initializer does not match", v->name); + free_parse_initializer(&v->initializer); + vkd3d_free(v); + continue; + } + } + if ((type->type == HLSL_CLASS_STRUCT || type->type == HLSL_CLASS_ARRAY) + && components_count_type(type) != size) + { + hlsl_report_message(v->loc, HLSL_LEVEL_ERROR, + "'%s' initializer does not match", v->name); + free_parse_initializer(&v->initializer); + vkd3d_free(v); + continue; + } + + if (type->type == HLSL_CLASS_STRUCT) + { + struct_var_initializer(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->array_size > 0) + { + FIXME("Initializing arrays is not supported yet.\n"); + free_parse_initializer(&v->initializer); + vkd3d_free(v); + continue; + } + if (v->initializer.args_count > 1) + { + FIXME("Complex initializers are not supported yet.\n"); + free_parse_initializer(&v->initializer); + vkd3d_free(v); + continue; + } + + load = new_var_load(var, var->loc); + list_add_tail(v->initializer.instrs, &load->node.entry); + add_assignment(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(&hlsl_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 struct hlsl_ir_function_decl *new_func_decl(struct hlsl_type *return_type, + struct list *parameters, const char *semantic, struct source_location loc) +{ + struct hlsl_ir_function_decl *decl; + + if (!(decl = vkd3d_calloc(1, sizeof(*decl)))) + return NULL; + decl->return_type = return_type; + decl->parameters = parameters; + decl->semantic = semantic; + decl->loc = loc; + + if (!type_is_void(return_type)) + { + struct hlsl_ir_var *return_var; + char name[28]; + + sprintf(name, "<retval-%p>", decl); + if (!(return_var = new_synthetic_var(name, return_type, loc))) + { + vkd3d_free(decl); + return NULL; + } + decl->return_var = return_var; + } + + return decl; +} + +%} + +%locations +%define parse.error verbose +%define api.prefix {hlsl_} +%expect 1 + +%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_variable_def *variable_def; + struct parse_if_body if_body; + enum parse_unary_op unary_op; + enum parse_assign_op assign_op; + struct reg_reservation *reg_reservation; + struct parse_colon_attribute colon_attribute; +} + +%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 <assign_op> assign_op + +%type <boolval> boolean + +%type <colon_attribute> colon_attribute + +%type <function> func_declaration +%type <function> func_prototype + +%type <initializer> complex_initializer +%type <initializer> initializer_expr_list + +%type <if_body> if_body + +%type <intval> array + +%type <modifiers> input_mod +%type <modifiers> input_mods +%type <modifiers> var_modifiers + +%type <name> any_identifier +%type <name> semantic +%type <name> var_identifier + +%type <parameter> parameter + +%type <reg_reservation> register_opt + +%type <type> base_type +%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 <unary_op> unary_op + +%type <variable_def> type_spec +%type <variable_def> variable_def + +%% + +hlsl_prog: + /* empty */ + | hlsl_prog func_declaration + { + const struct hlsl_ir_function_decl *decl; + + decl = get_overloaded_func(&hlsl_ctx.functions, $2.name, $2.decl->parameters, TRUE); + if (decl && !decl->func->intrinsic) + { + if (decl->body && $2.decl->body) + { + hlsl_report_message($2.decl->loc, HLSL_LEVEL_ERROR, + "redefinition of function %s", debugstr_a($2.name)); + YYABORT; + } + else if (!compare_hlsl_types(decl->return_type, $2.decl->return_type)) + { + hlsl_report_message($2.decl->loc, HLSL_LEVEL_ERROR, + "redefining function %s with a different return type", + debugstr_a($2.name)); + hlsl_report_message(decl->loc, HLSL_LEVEL_NOTE, + "%s previously declared here", + debugstr_a($2.name)); + YYABORT; + } + } + + if (type_is_void($2.decl->return_type) && $2.decl->semantic) + { + hlsl_report_message($2.decl->loc, HLSL_LEVEL_ERROR, + "void function with a semantic"); + } + + TRACE("Adding function '%s' to the function list.\n", $2.name); + add_function_decl(&hlsl_ctx.functions, $2.name, $2.decl, FALSE); + } + | hlsl_prog declaration_statement + { + TRACE("Declaration statement parsed.\n"); + + if (!list_empty($2)) + FIXME("Uniform initializer.\n"); + free_instr_list($2); + } + | hlsl_prog preproc_directive + | hlsl_prog ';' + { + TRACE("Skipping stray semicolon.\n"); + } + +preproc_directive: + PRE_LINE STRING + { + const char **new_array = NULL; + + TRACE("Updating line information to file %s, line %u.\n", debugstr_a($2), $1); + hlsl_ctx.line_no = $1; + if (strcmp($2, hlsl_ctx.source_file)) + new_array = vkd3d_realloc(hlsl_ctx.source_files, + sizeof(*hlsl_ctx.source_files) * (hlsl_ctx.source_files_count + 1)); + + if (new_array) + { + hlsl_ctx.source_files = new_array; + hlsl_ctx.source_files[hlsl_ctx.source_files_count++] = $2; + hlsl_ctx.source_file = $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_report_message(get_location(&@2), HLSL_LEVEL_ERROR, + "anonymous struct declaration with no variables"); + } + if (modifiers) + { + hlsl_report_message(get_location(&@1), HLSL_LEVEL_ERROR, + "modifier not allowed on struct type declaration"); + } + } + + if (!(type = apply_type_modifiers($2, &modifiers, get_location(&@1)))) + YYABORT; + $$ = declare_vars(type, modifiers, $3); + } + +struct_spec: + named_struct_spec + | unnamed_struct_spec + +named_struct_spec: + KW_STRUCT any_identifier '{' fields_list '}' + { + BOOL ret; + + TRACE("Structure %s declaration.\n", debugstr_a($2)); + $$ = new_struct_type($2, $4); + + if (get_variable(hlsl_ctx.cur_scope, $2)) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, "redefinition of '%s'", $2); + YYABORT; + } + + ret = add_type_to_scope(hlsl_ctx.cur_scope, $$); + if (!ret) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, "redefinition of struct '%s'", $2); + YYABORT; + } + } + +unnamed_struct_spec: + KW_STRUCT '{' fields_list '}' + { + TRACE("Anonymous structure declaration.\n"); + $$ = new_struct_type(NULL, $3); + } + +any_identifier: + VAR_IDENTIFIER + | TYPE_IDENTIFIER + | NEW_IDENTIFIER + +fields_list: + /* empty */ + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + } + | fields_list field + { + BOOL ret; + struct hlsl_struct_field *field, *next; + + $$ = $1; + LIST_FOR_EACH_ENTRY_SAFE(field, next, $2, struct hlsl_struct_field, entry) + { + ret = add_struct_field($$, field); + if (ret == FALSE) + { + hlsl_report_message(get_location(&@2), + HLSL_LEVEL_ERROR, "redefinition of '%s'", field->name); + vkd3d_free(field); + } + } + 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($2, &modifiers, get_location(&@1)))) + YYABORT; + $$ = gen_struct_fields(type, modifiers, $3); + } + +func_declaration: + func_prototype compound_statement + { + TRACE("Function %s parsed.\n", $1.name); + $$ = $1; + $$.decl->body = $2; + pop_scope(&hlsl_ctx); + } + | func_prototype ';' + { + TRACE("Function prototype for %s.\n", $1.name); + $$ = $1; + pop_scope(&hlsl_ctx); + } + +func_prototype: + /* var_modifiers is necessary to avoid shift/reduce conflicts. */ + var_modifiers type var_identifier '(' parameters ')' colon_attribute + { + if ($1) + { + hlsl_report_message(get_location(&@1), HLSL_LEVEL_ERROR, + "unexpected modifiers on a function"); + YYABORT; + } + if (get_variable(hlsl_ctx.globals, $3)) + { + hlsl_report_message(get_location(&@3), HLSL_LEVEL_ERROR, "redefinition of '%s'\n", $3); + YYABORT; + } + if (type_is_void($2) && $7.semantic) + { + hlsl_report_message(get_location(&@7), HLSL_LEVEL_ERROR, "void function with a semantic"); + } + + if ($7.reg_reservation) + { + FIXME("Unexpected register reservation for a function.\n"); + vkd3d_free($7.reg_reservation); + } + if (!($$.decl = new_func_decl($2, $5, $7.semantic, get_location(&@3)))) + YYABORT; + $$.name = $3; + hlsl_ctx.cur_function = $$.decl; + } + +compound_statement: + '{' '}' + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + } + | '{' scope_start statement_list '}' + { + pop_scope(&hlsl_ctx); + $$ = $3; + } + +scope_start: + /* empty */ + { + push_scope(&hlsl_ctx); + } + +var_identifier: + VAR_IDENTIFIER + | NEW_IDENTIFIER + +colon_attribute: + /* empty */ + { + $$.semantic = NULL; + $$.reg_reservation = NULL; + } + | semantic + { + $$.semantic = $1; + $$.reg_reservation = NULL; + } + | register_opt + { + $$.semantic = NULL; + $$.reg_reservation = $1; + } + +semantic: + ':' any_identifier + { + $$ = $2; + } + +/* 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 + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + } + | scope_start param_list + { + $$ = $2; + } + +param_list: + parameter + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + if (!add_func_parameter($$, &$1, get_location(&@1))) + { + ERR("Error adding function parameter %s.\n", $1.name); + set_parse_status(&hlsl_ctx.status, PARSE_ERR); + YYABORT; + } + } + | param_list ',' parameter + { + $$ = $1; + if (!add_func_parameter($$, &$3, get_location(&@3))) + { + hlsl_report_message(get_location(&@3), HLSL_LEVEL_ERROR, "duplicate parameter %s", $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($3, &modifiers, get_location(&@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) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, + "duplicate input-output modifiers"); + YYABORT; + } + $$ = $1 | $2; + } + +input_mod: + KW_IN + { + $$ = HLSL_STORAGE_IN; + } + | KW_OUT + { + $$ = HLSL_STORAGE_OUT; + } + | KW_INOUT + { + $$ = HLSL_STORAGE_IN | HLSL_STORAGE_OUT; + } + +type: + base_type + { + $$ = $1; + } + | KW_VECTOR '<' base_type ',' C_INTEGER '>' + { + if ($3->type != HLSL_CLASS_SCALAR) + { + hlsl_report_message(get_location(&@3), HLSL_LEVEL_ERROR, + "vectors of non-scalar types are not allowed\n"); + YYABORT; + } + if ($5 < 1 || $5 > 4) + { + hlsl_report_message(get_location(&@5), HLSL_LEVEL_ERROR, + "vector size must be between 1 and 4\n"); + YYABORT; + } + + $$ = new_hlsl_type(NULL, HLSL_CLASS_VECTOR, $3->base_type, $5, 1); + } + | KW_MATRIX '<' base_type ',' C_INTEGER ',' C_INTEGER '>' + { + if ($3->type != HLSL_CLASS_SCALAR) + { + hlsl_report_message(get_location(&@3), HLSL_LEVEL_ERROR, + "matrices of non-scalar types are not allowed\n"); + YYABORT; + } + if ($5 < 1 || $5 > 4) + { + hlsl_report_message(get_location(&@5), HLSL_LEVEL_ERROR, + "matrix row count must be between 1 and 4\n"); + YYABORT; + } + if ($7 < 1 || $7 > 4) + { + hlsl_report_message(get_location(&@7), HLSL_LEVEL_ERROR, + "matrix column count must be between 1 and 4\n"); + YYABORT; + } + + $$ = new_hlsl_type(NULL, HLSL_CLASS_MATRIX, $3->base_type, $7, $5); + } + +base_type: + KW_VOID + { + $$ = hlsl_ctx.builtin_types.Void; + } + | KW_SAMPLER + { + $$ = hlsl_ctx.builtin_types.sampler[HLSL_SAMPLER_DIM_GENERIC]; + } + | KW_SAMPLER1D + { + $$ = hlsl_ctx.builtin_types.sampler[HLSL_SAMPLER_DIM_1D]; + } + | KW_SAMPLER2D + { + $$ = hlsl_ctx.builtin_types.sampler[HLSL_SAMPLER_DIM_2D]; + } + | KW_SAMPLER3D + { + $$ = hlsl_ctx.builtin_types.sampler[HLSL_SAMPLER_DIM_3D]; + } + | KW_SAMPLERCUBE + { + $$ = hlsl_ctx.builtin_types.sampler[HLSL_SAMPLER_DIM_3D]; + } + | TYPE_IDENTIFIER + { + $$ = get_type(hlsl_ctx.cur_scope, $1, TRUE); + vkd3d_free($1); + } + | KW_STRUCT TYPE_IDENTIFIER + { + $$ = get_type(hlsl_ctx.cur_scope, $2, TRUE); + if ($$->type != HLSL_CLASS_STRUCT) + hlsl_report_message(get_location(&@1), HLSL_LEVEL_ERROR, "'%s' redefined as a structure\n", $2); + vkd3d_free($2); + } + +declaration_statement: + declaration + | struct_declaration + | typedef + { + if (!($$ = vkd3d_malloc(sizeof(*$$)))) + YYABORT; + list_init($$); + } + +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_report_message(get_location(&@1), HLSL_LEVEL_ERROR, "modifier 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($2, $3, $4)) + YYABORT; + } + +type_specs: + type_spec + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + list_add_head($$, &$1->entry); + } + | type_specs ',' type_spec + { + $$ = $1; + list_add_tail($$, &$3->entry); + } + +type_spec: + any_identifier array + { + $$ = vkd3d_calloc(1, sizeof(*$$)); + $$->loc = get_location(&@1); + $$->name = $1; + $$->array_size = $2; + } + +declaration: + var_modifiers type variables_def ';' + { + struct hlsl_type *type; + DWORD modifiers = $1; + + if (!(type = apply_type_modifiers($2, &modifiers, get_location(&@1)))) + YYABORT; + $$ = declare_vars(type, modifiers, $3); + } + +variables_def_optional: + /* empty */ + { + $$ = NULL; + } + | variables_def + +variables_def: + variable_def + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + list_add_head($$, &$1->entry); + } + | variables_def ',' variable_def + { + $$ = $1; + list_add_tail($$, &$3->entry); + } + +variable_def: + any_identifier array colon_attribute + { + $$ = vkd3d_calloc(1, sizeof(*$$)); + $$->loc = get_location(&@1); + $$->name = $1; + $$->array_size = $2; + $$->semantic = $3.semantic; + $$->reg_reservation = $3.reg_reservation; + } + | any_identifier array colon_attribute '=' complex_initializer + { + TRACE("Declaration with initializer.\n"); + $$ = vkd3d_calloc(1, sizeof(*$$)); + $$->loc = get_location(&@1); + $$->name = $1; + $$->array_size = $2; + $$->semantic = $3.semantic; + $$->reg_reservation = $3.reg_reservation; + $$->initializer = $5; + } + +array: + /* empty */ + { + $$ = 0; + } + | '[' expr ']' + { + unsigned int size = evaluate_array_dimension(node_from_list($2)); + + free_instr_list($2); + + if (!size) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, + "array size is not a positive integer constant\n"); + YYABORT; + } + TRACE("Array size %u.\n", size); + + if (size > 65536) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, + "array size must be between 1 and 65536"); + YYABORT; + } + $$ = size; + } + +var_modifiers: + /* empty */ + { + $$ = 0; + } + | KW_EXTERN var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_EXTERN, get_location(&@1)); + } + | KW_NOINTERPOLATION var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_NOINTERPOLATION, get_location(&@1)); + } + | KW_PRECISE var_modifiers + { + $$ = add_modifiers($2, HLSL_MODIFIER_PRECISE, get_location(&@1)); + } + | KW_SHARED var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_SHARED, get_location(&@1)); + } + | KW_GROUPSHARED var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_GROUPSHARED, get_location(&@1)); + } + | KW_STATIC var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_STATIC, get_location(&@1)); + } + | KW_UNIFORM var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_UNIFORM, get_location(&@1)); + } + | KW_VOLATILE var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_VOLATILE, get_location(&@1)); + } + | KW_CONST var_modifiers + { + $$ = add_modifiers($2, HLSL_MODIFIER_CONST, get_location(&@1)); + } + | KW_ROW_MAJOR var_modifiers + { + $$ = add_modifiers($2, HLSL_MODIFIER_ROW_MAJOR, get_location(&@1)); + } + | KW_COLUMN_MAJOR var_modifiers + { + $$ = add_modifiers($2, HLSL_MODIFIER_COLUMN_MAJOR, get_location(&@1)); + } + +complex_initializer: + initializer_expr + { + $$.args_count = 1; + if (!($$.args = vkd3d_malloc(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 = vkd3d_malloc(sizeof(*$$.args)))) + YYABORT; + $$.args[0] = node_from_list($1); + $$.instrs = $1; + } + | initializer_expr_list ',' initializer_expr + { + $$ = $1; + if (!($$.args = vkd3d_realloc($$.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($2, node_from_list($2), get_location(&@1))) + YYABORT; + $$ = $2; + } + | KW_RETURN ';' + { + if (!($$ = vkd3d_malloc(sizeof(*$$)))) + YYABORT; + list_init($$); + if (!add_return($$, NULL, get_location(&@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 = new_if(condition, get_location(&@1)))) + YYABORT; + list_move_tail(&instr->then_instrs, $5.then_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) + hlsl_report_message(instr->node.loc, HLSL_LEVEL_ERROR, + "if condition requires a scalar"); + $$ = $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(LOOP_WHILE, NULL, $3, NULL, $5, get_location(&@1)); + } + | KW_DO statement KW_WHILE '(' expr ')' ';' + { + $$ = create_loop(LOOP_DO_WHILE, NULL, $5, NULL, $2, get_location(&@1)); + } + | KW_FOR '(' scope_start expr_statement expr_statement expr ')' statement + { + $$ = create_loop(LOOP_FOR, $4, $5, $6, $8, get_location(&@1)); + pop_scope(&hlsl_ctx); + } + | KW_FOR '(' scope_start declaration expr_statement expr ')' statement + { + if (!$4) + hlsl_report_message(get_location(&@4), HLSL_LEVEL_WARNING, + "no expressions in for loop initializer"); + $$ = create_loop(LOOP_FOR, $4, $5, $6, $8, get_location(&@1)); + pop_scope(&hlsl_ctx); + } + +expr_statement: + ';' + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + } + | expr ';' + { + $$ = $1; + } + +primary_expr: + C_FLOAT + { + struct hlsl_ir_constant *c; + + if (!(c = vkd3d_malloc(sizeof(*c)))) + YYABORT; + init_node(&c->node, HLSL_IR_CONSTANT, hlsl_ctx.builtin_types.scalar[HLSL_TYPE_FLOAT], get_location(&@1)); + c->value.f[0] = $1; + if (!($$ = make_list(&c->node))) + YYABORT; + } + | C_INTEGER + { + struct hlsl_ir_constant *c; + + if (!(c = vkd3d_malloc(sizeof(*c)))) + YYABORT; + init_node(&c->node, HLSL_IR_CONSTANT, hlsl_ctx.builtin_types.scalar[HLSL_TYPE_INT], get_location(&@1)); + c->value.i[0] = $1; + if (!($$ = make_list(&c->node))) + YYABORT; + } + | boolean + { + struct hlsl_ir_constant *c; + + if (!(c = vkd3d_malloc(sizeof(*c)))) + YYABORT; + init_node(&c->node, HLSL_IR_CONSTANT, hlsl_ctx.builtin_types.scalar[HLSL_TYPE_BOOL], get_location(&@1)); + c->value.b[0] = $1; + if (!($$ = make_list(&c->node))) + YYABORT; + } + | VAR_IDENTIFIER + { + struct hlsl_ir_load *load; + struct hlsl_ir_var *var; + + if (!(var = get_variable(hlsl_ctx.cur_scope, $1))) + { + hlsl_report_message(get_location(&@1), HLSL_LEVEL_ERROR, "variable '%s' is not declared\n", $1); + YYABORT; + } + if ((load = new_var_load(var, get_location(&@1)))) + { + if (!($$ = make_list(&load->node))) + YYABORT; + } + else + $$ = NULL; + } + | '(' expr ')' + { + $$ = $2; + } + +postfix_expr: + primary_expr + | postfix_expr OP_INC + { + struct source_location loc; + struct hlsl_ir_node *inc; + + loc = get_location(&@2); + if (node_from_list($1)->data_type->modifiers & HLSL_MODIFIER_CONST) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "modifying a const expression"); + YYABORT; + } + inc = new_unary_expr(HLSL_IR_UNOP_POSTINC, node_from_list($1), loc); + /* Post increment/decrement expressions are considered const */ + inc->data_type = clone_hlsl_type(inc->data_type, 0); + inc->data_type->modifiers |= HLSL_MODIFIER_CONST; + $$ = append_unop($1, inc); + } + | postfix_expr OP_DEC + { + struct source_location loc; + struct hlsl_ir_node *inc; + + loc = get_location(&@2); + if (node_from_list($1)->data_type->modifiers & HLSL_MODIFIER_CONST) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "modifying a const expression"); + YYABORT; + } + inc = new_unary_expr(HLSL_IR_UNOP_POSTDEC, node_from_list($1), loc); + /* Post increment/decrement expressions are considered const */ + inc->data_type = clone_hlsl_type(inc->data_type, 0); + inc->data_type->modifiers |= HLSL_MODIFIER_CONST; + $$ = append_unop($1, inc); + } + | postfix_expr '.' any_identifier + { + struct hlsl_ir_node *node = node_from_list($1); + struct source_location loc; + + loc = get_location(&@2); + if (node->data_type->type == HLSL_CLASS_STRUCT) + { + struct hlsl_type *type = node->data_type; + struct hlsl_struct_field *field; + + $$ = NULL; + LIST_FOR_EACH_ENTRY(field, type->e.elements, struct hlsl_struct_field, entry) + { + if (!strcmp($3, field->name)) + { + if (!add_record_load($1, node, field, loc)) + YYABORT; + $$ = $1; + break; + } + } + if (!$$) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "invalid subscript %s", debugstr_a($3)); + YYABORT; + } + } + else if (node->data_type->type <= HLSL_CLASS_LAST_NUMERIC) + { + struct hlsl_ir_swizzle *swizzle; + + swizzle = get_swizzle(node, $3, &loc); + if (!swizzle) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "invalid swizzle %s", debugstr_a($3)); + YYABORT; + } + $$ = append_unop($1, &swizzle->node); + } + else + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "invalid subscript %s", debugstr_a($3)); + YYABORT; + } + } + | postfix_expr '[' expr ']' + { + struct hlsl_ir_node *array = node_from_list($1), *index = node_from_list($3); + + list_move_tail($1, $3); + vkd3d_free($3); + + if (index->data_type->type != HLSL_CLASS_SCALAR) + { + hlsl_report_message(get_location(&@3), HLSL_LEVEL_ERROR, "array index is not scalar"); + free_instr_list($1); + YYABORT; + } + + if (!add_array_load($1, array, index, get_location(&@2))) + { + free_instr_list($1); + YYABORT; + } + $$ = $1; + } + + /* var_modifiers is necessary to avoid shift/reduce conflicts. */ + | var_modifiers type '(' initializer_expr_list ')' + { + struct hlsl_ir_assignment *assignment; + unsigned int i, writemask_offset = 0; + static unsigned int counter; + struct hlsl_ir_load *load; + struct hlsl_ir_var *var; + char name[23]; + + if ($1) + { + hlsl_report_message(get_location(&@1), HLSL_LEVEL_ERROR, + "unexpected modifier on a constructor\n"); + YYABORT; + } + if ($2->type > HLSL_CLASS_LAST_NUMERIC) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, + "constructors may only be used with numeric data types\n"); + YYABORT; + } + if ($2->dimx * $2->dimy != initializer_size(&$4)) + { + hlsl_report_message(get_location(&@4), HLSL_LEVEL_ERROR, + "expected %u components in constructor, but got %u\n", + $2->dimx * $2->dimy, initializer_size(&$4)); + YYABORT; + } + + if ($2->type == HLSL_CLASS_MATRIX) + FIXME("Matrix constructors are not supported yet.\n"); + + sprintf(name, "<constructor-%x>", counter++); + if (!(var = new_synthetic_var(name, $2, get_location(&@2)))) + YYABORT; + for (i = 0; i < $4.args_count; ++i) + { + struct hlsl_ir_node *arg = $4.args[i]; + unsigned int width; + + if (arg->data_type->type == HLSL_CLASS_OBJECT) + { + hlsl_report_message(arg->loc, HLSL_LEVEL_ERROR, "invalid constructor argument"); + continue; + } + width = components_count_type(arg->data_type); + + if (width > 4) + { + FIXME("Constructor argument with %u components.\n", width); + continue; + } + + if (!(arg = add_implicit_conversion($4.instrs, arg, + hlsl_ctx.builtin_types.vector[$2->base_type][width - 1], &arg->loc))) + continue; + + if (!(assignment = new_assignment(var, NULL, arg, + ((1 << width) - 1) << writemask_offset, arg->loc))) + YYABORT; + writemask_offset += width; + list_add_tail($4.instrs, &assignment->node.entry); + } + vkd3d_free($4.args); + if (!(load = new_var_load(var, get_location(&@2)))) + YYABORT; + $$ = append_unop($4.instrs, &load->node); + } + +unary_expr: + postfix_expr + | OP_INC unary_expr + { + struct source_location loc; + + loc = get_location(&@1); + if (node_from_list($2)->data_type->modifiers & HLSL_MODIFIER_CONST) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "modifying a const expression"); + YYABORT; + } + $$ = append_unop($2, new_unary_expr(HLSL_IR_UNOP_PREINC, node_from_list($2), loc)); + } + | OP_DEC unary_expr + { + struct source_location loc; + + loc = get_location(&@1); + if (node_from_list($2)->data_type->modifiers & HLSL_MODIFIER_CONST) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "modifying a const expression"); + YYABORT; + } + $$ = append_unop($2, new_unary_expr(HLSL_IR_UNOP_PREDEC, node_from_list($2), loc)); + } + | unary_op unary_expr + { + enum hlsl_ir_expr_op ops[] = {0, HLSL_IR_UNOP_NEG, + HLSL_IR_UNOP_LOGIC_NOT, HLSL_IR_UNOP_BIT_NOT}; + + if ($1 == UNARY_OP_PLUS) + $$ = $2; + else + $$ = append_unop($2, new_unary_expr(ops[$1], node_from_list($2), get_location(&@1))); + } + + /* var_modifiers is necessary to avoid shift/reduce conflicts. */ + | '(' var_modifiers type array ')' unary_expr + { + struct hlsl_type *src_type = node_from_list($6)->data_type; + struct hlsl_type *dst_type; + struct source_location loc; + + loc = get_location(&@3); + if ($2) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "unexpected modifier in a cast"); + YYABORT; + } + + if ($4) + dst_type = new_array_type($3, $4); + else + dst_type = $3; + + if (!compatible_data_types(src_type, dst_type)) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "can't cast from %s to %s", + debug_hlsl_type(src_type), debug_hlsl_type(dst_type)); + YYABORT; + } + + $$ = append_unop($6, &new_cast(node_from_list($6), dst_type, &loc)->node); + } + +unary_op: + '+' + { + $$ = UNARY_OP_PLUS; + } + | '-' + { + $$ = UNARY_OP_MINUS; + } + | '!' + { + $$ = UNARY_OP_LOGICNOT; + } + | '~' + { + $$ = UNARY_OP_BITNOT; + } + +mul_expr: + unary_expr + | mul_expr '*' unary_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_MUL, get_location(&@2)); + } + | mul_expr '/' unary_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_DIV, get_location(&@2)); + } + | mul_expr '%' unary_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_MOD, get_location(&@2)); + } + +add_expr: + mul_expr + | add_expr '+' mul_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_ADD, get_location(&@2)); + } + | add_expr '-' mul_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_SUB, get_location(&@2)); + } + +shift_expr: + add_expr + | shift_expr OP_LEFTSHIFT add_expr + { + FIXME("Left shift.\n"); + } + | shift_expr OP_RIGHTSHIFT add_expr + { + FIXME("Right shift.\n"); + } + +relational_expr: + shift_expr + | relational_expr '<' shift_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_LESS, get_location(&@2)); + } + | relational_expr '>' shift_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_GREATER, get_location(&@2)); + } + | relational_expr OP_LE shift_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_LEQUAL, get_location(&@2)); + } + | relational_expr OP_GE shift_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_GEQUAL, get_location(&@2)); + } + +equality_expr: + relational_expr + | equality_expr OP_EQ relational_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_EQUAL, get_location(&@2)); + } + | equality_expr OP_NE relational_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_NEQUAL, get_location(&@2)); + } + +bitand_expr: + equality_expr + | bitand_expr '&' equality_expr + { + FIXME("Bitwise AND.\n"); + } + +bitxor_expr: + bitand_expr + | bitxor_expr '^' bitand_expr + { + FIXME("Bitwise XOR.\n"); + } + +bitor_expr: + bitxor_expr + | bitor_expr '|' bitxor_expr + { + FIXME("Bitwise OR.\n"); + } + +logicand_expr: + bitor_expr + | logicand_expr OP_AND bitor_expr + { + FIXME("Logical AND.\n"); + } + +logicor_expr: + logicand_expr + | logicor_expr OP_OR logicand_expr + { + FIXME("Logical OR.\n"); + } + +conditional_expr: + logicor_expr + | logicor_expr '?' expr ':' assignment_expr + { + FIXME("Ternary operator.\n"); + } + +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_report_message(get_location(&@2), HLSL_LEVEL_ERROR, "l-value is const"); + YYABORT; + } + list_move_tail($3, $1); + vkd3d_free($1); + if (!add_assignment($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); + } + +%% + +static struct source_location get_location(const struct YYLTYPE *l) +{ + const struct source_location loc = + { + .file = hlsl_ctx.source_file, + .line = l->first_line, + .col = l->first_column, + }; + return loc; +} + +static void dump_function_decl(struct rb_entry *entry, void *context) +{ + struct hlsl_ir_function_decl *func = RB_ENTRY_VALUE(entry, struct hlsl_ir_function_decl, entry); + + if (func->body) + debug_dump_ir_function_decl(func); +} + +static void dump_function(struct rb_entry *entry, void *context) +{ + struct hlsl_ir_function *func = RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry); + rb_for_each_entry(&func->overloads, dump_function_decl, NULL); +} + +/* Allocate a unique, ordered index to each instruction, which will be used for + * computing liveness ranges. */ +static unsigned int index_instructions(struct list *instrs, unsigned int index) +{ + struct hlsl_ir_node *instr; + + LIST_FOR_EACH_ENTRY(instr, instrs, struct hlsl_ir_node, entry) + { + instr->index = index++; + + if (instr->type == HLSL_IR_IF) + { + struct hlsl_ir_if *iff = if_from_node(instr); + index = index_instructions(&iff->then_instrs, index); + index = index_instructions(&iff->else_instrs, index); + } + else if (instr->type == HLSL_IR_LOOP) + { + index = index_instructions(&loop_from_node(instr)->body, index); + loop_from_node(instr)->next_index = index; + } + } + + return index; +} + +/* Compute the earliest and latest liveness for each variable. In the case that + * a variable is accessed inside of a loop, we promote its liveness to extend + * to at least the range of the entire loop. Note that we don't need to do this + * for anonymous nodes, since there's currently no way to use a node which was + * calculated in an earlier iteration of the loop. */ +static void compute_liveness_recurse(struct list *instrs, unsigned int loop_first, unsigned int loop_last) +{ + struct hlsl_ir_node *instr; + struct hlsl_ir_var *var; + + LIST_FOR_EACH_ENTRY(instr, instrs, struct hlsl_ir_node, entry) + { + switch (instr->type) + { + case HLSL_IR_ASSIGNMENT: + { + struct hlsl_ir_assignment *assignment = assignment_from_node(instr); + + var = assignment->lhs.var; + if (!var->first_write) + var->first_write = loop_first ? min(instr->index, loop_first) : instr->index; + assignment->rhs.node->last_read = instr->index; + if (assignment->lhs.offset.node) + assignment->lhs.offset.node->last_read = instr->index; + break; + } + case HLSL_IR_EXPR: + { + struct hlsl_ir_expr *expr = expr_from_node(instr); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(expr->operands) && expr->operands[i].node; ++i) + expr->operands[i].node->last_read = instr->index; + break; + } + case HLSL_IR_IF: + { + struct hlsl_ir_if *iff = if_from_node(instr); + + compute_liveness_recurse(&iff->then_instrs, loop_first, loop_last); + compute_liveness_recurse(&iff->else_instrs, loop_first, loop_last); + iff->condition.node->last_read = instr->index; + break; + } + case HLSL_IR_LOAD: + { + struct hlsl_ir_load *load = load_from_node(instr); + + var = load->src.var; + var->last_read = loop_last ? max(instr->index, loop_last) : instr->index; + if (load->src.offset.node) + load->src.offset.node->last_read = instr->index; + break; + } + case HLSL_IR_LOOP: + { + struct hlsl_ir_loop *loop = loop_from_node(instr); + + compute_liveness_recurse(&loop->body, loop_first ? loop_first : instr->index, + loop_last ? loop_last : loop->next_index); + break; + } + case HLSL_IR_SWIZZLE: + { + struct hlsl_ir_swizzle *swizzle = swizzle_from_node(instr); + + swizzle->val.node->last_read = instr->index; + break; + } + case HLSL_IR_CONSTANT: + case HLSL_IR_JUMP: + break; + } + } +} + +static void compute_liveness(struct hlsl_ir_function_decl *entry_func) +{ + struct hlsl_ir_var *var; + + LIST_FOR_EACH_ENTRY(var, &hlsl_ctx.globals->vars, struct hlsl_ir_var, scope_entry) + { + var->first_write = 1; + } + + LIST_FOR_EACH_ENTRY(var, entry_func->parameters, struct hlsl_ir_var, param_entry) + { + if (var->modifiers & HLSL_STORAGE_IN) + var->first_write = 1; + if (var->modifiers & HLSL_STORAGE_OUT) + var->last_read = UINT_MAX; + } + + if (entry_func->return_var) + entry_func->return_var->last_read = UINT_MAX; + + compute_liveness_recurse(entry_func->body, 0, 0); +} + +int hlsl_parser_compile(enum vkd3d_shader_type type, DWORD major, DWORD minor, const char *entrypoint, + struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) +{ + struct hlsl_ir_function_decl *entry_func; + struct hlsl_scope *scope, *next_scope; + struct hlsl_type *hlsl_type, *next_type; + struct hlsl_ir_var *var, *next_var; + int ret = VKD3D_ERROR; + unsigned int i; + + hlsl_ctx.status = PARSE_SUCCESS; + hlsl_ctx.message_context = message_context; + hlsl_ctx.line_no = hlsl_ctx.column = 1; + hlsl_ctx.source_file = vkd3d_strdup(""); + hlsl_ctx.source_files = vkd3d_malloc(sizeof(*hlsl_ctx.source_files)); + if (hlsl_ctx.source_files) + hlsl_ctx.source_files[0] = hlsl_ctx.source_file; + hlsl_ctx.source_files_count = 1; + hlsl_ctx.cur_scope = NULL; + hlsl_ctx.matrix_majority = HLSL_COLUMN_MAJOR; + list_init(&hlsl_ctx.scopes); + list_init(&hlsl_ctx.types); + init_functions_tree(&hlsl_ctx.functions); + list_init(&hlsl_ctx.static_initializers); + + push_scope(&hlsl_ctx); + hlsl_ctx.globals = hlsl_ctx.cur_scope; + declare_predefined_types(hlsl_ctx.globals); + + hlsl_parse(); + + if (hlsl_ctx.status == PARSE_ERR) + goto out; + + if (!(entry_func = get_func_entry(entrypoint))) + { + hlsl_message("error: entry point %s is not defined\n", debugstr_a(entrypoint)); + goto out; + } + + if (!type_is_void(entry_func->return_type) + && entry_func->return_type->type != HLSL_CLASS_STRUCT && !entry_func->semantic) + { + hlsl_report_message(entry_func->loc, HLSL_LEVEL_ERROR, + "entry point "%s" is missing a return value semantic", entry_func->func->name); + } + + list_move_head(entry_func->body, &hlsl_ctx.static_initializers); + + /* Index 0 means unused; index 1 means function entry, so start at 2. */ + index_instructions(entry_func->body, 2); + + if (TRACE_ON()) + { + TRACE("IR dump.\n"); + rb_for_each_entry(&hlsl_ctx.functions, dump_function, NULL); + } + + compute_liveness(entry_func); + + if (hlsl_ctx.status != PARSE_ERR) + ret = VKD3D_ERROR_NOT_IMPLEMENTED; + +out: + for (i = 0; i < hlsl_ctx.source_files_count; ++i) + vkd3d_free((void *)hlsl_ctx.source_files[i]); + vkd3d_free(hlsl_ctx.source_files); + + TRACE("Freeing functions IR.\n"); + rb_destroy(&hlsl_ctx.functions, free_function_rb, NULL); + + TRACE("Freeing variables.\n"); + LIST_FOR_EACH_ENTRY_SAFE(scope, next_scope, &hlsl_ctx.scopes, struct hlsl_scope, entry) + { + LIST_FOR_EACH_ENTRY_SAFE(var, next_var, &scope->vars, struct hlsl_ir_var, scope_entry) + free_declaration(var); + rb_destroy(&scope->types, NULL, NULL); + vkd3d_free(scope); + } + + TRACE("Freeing types.\n"); + LIST_FOR_EACH_ENTRY_SAFE(hlsl_type, next_type, &hlsl_ctx.types, struct hlsl_type, entry) + free_hlsl_type(hlsl_type); + + return ret; +} diff --git a/libs/vkd3d-shader/vkd3d_shader_main.c b/libs/vkd3d-shader/vkd3d_shader_main.c index df3b1a57..ff917dbf 100644 --- a/libs/vkd3d-shader/vkd3d_shader_main.c +++ b/libs/vkd3d-shader/vkd3d_shader_main.c @@ -225,7 +225,7 @@ static void vkd3d_shader_dump_blob(const char *path, const char *prefix, const v } }
-static void vkd3d_shader_dump_shader(enum vkd3d_shader_type type, const struct vkd3d_shader_code *shader) +void vkd3d_shader_dump_shader(enum vkd3d_shader_type type, const struct vkd3d_shader_code *shader) { static bool enabled = true; const char *path; @@ -961,7 +961,16 @@ static int compile_dxbc_tpf(const struct vkd3d_shader_compile_info *compile_info static int compile_hlsl(const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_code *out, struct vkd3d_shader_message_context *message_context) { - return VKD3D_ERROR_NOT_IMPLEMENTED; + struct vkd3d_shader_code preprocessed; + int ret; + + if ((ret = preproc_lexer_parse(compile_info, &preprocessed, message_context))) + return ret; + + ret = hlsl_compile_shader(preprocessed.code, compile_info, out, message_context); + + vkd3d_shader_free_shader_code(&preprocessed); + return ret; }
int vkd3d_shader_compile(const struct vkd3d_shader_compile_info *compile_info, diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 12edfae1..52196caf 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -548,6 +548,10 @@ enum vkd3d_shader_type VKD3D_SHADER_TYPE_GRAPHICS_COUNT,
VKD3D_SHADER_TYPE_COMPUTE = VKD3D_SHADER_TYPE_GRAPHICS_COUNT, + + VKD3D_SHADER_TYPE_EFFECT, + VKD3D_SHADER_TYPE_TEXTURE, + VKD3D_SHADER_TYPE_LIBRARY, VKD3D_SHADER_TYPE_COUNT, };
@@ -884,6 +888,8 @@ void vkd3d_shader_verror(struct vkd3d_shader_message_context *context, const str void vkd3d_shader_vwarning(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location, enum vkd3d_shader_error error, const char *format, va_list args) DECLSPEC_HIDDEN;
+void vkd3d_shader_dump_shader(enum vkd3d_shader_type type, const struct vkd3d_shader_code *shader) DECLSPEC_HIDDEN; + int shader_extract_from_dxbc(const void *dxbc, size_t dxbc_length, struct vkd3d_shader_message_context *message_context, const char *source_name, struct vkd3d_shader_desc *desc) DECLSPEC_HIDDEN; @@ -909,6 +915,9 @@ void vkd3d_compute_dxbc_checksum(const void *dxbc, size_t size, uint32_t checksu int preproc_lexer_parse(const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_code *out, struct vkd3d_shader_message_context *message_context) DECLSPEC_HIDDEN;
+int hlsl_compile_shader(const char *text, const struct vkd3d_shader_compile_info *compile_info, + struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) DECLSPEC_HIDDEN; + static inline enum vkd3d_shader_component_type vkd3d_component_type_from_data_type( enum vkd3d_data_type data_type) {