This corresponds to Wine commit 9f9fec18799bf59df6211b7294f8d338caa7f4db.
Note that for the moment, compilation messages have not been ported.
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
---
Makefile.am | 16 +-
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/trace.c | 9 +
libs/vkd3d-shader/vkd3d_shader_main.c | 13 +-
libs/vkd3d-shader/vkd3d_shader_private.h | 9 +
8 files changed, 5609 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..96d6140d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -126,9 +126,22 @@ 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
+ @$(MKDIR_P) libs/vkd3d-shader
+ $(VKD3D_V_FLEX)$(FLEX) $(LFLAGS) -o $@ $<
+
+libs/vkd3d-shader/hlsl.tab.c libs/vkd3d-shader/hlsl.tab.h &: libs/vkd3d-shader/hlsl.y
+ @$(MKDIR_P) libs/vkd3d-shader
+ $(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 +159,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/trace.c b/libs/vkd3d-shader/trace.c
index e9c462e5..cab5b80d 100644
--- a/libs/vkd3d-shader/trace.c
+++ b/libs/vkd3d-shader/trace.c
@@ -1220,6 +1220,15 @@ const char *shader_get_type_prefix(enum vkd3d_shader_type type)
case VKD3D_SHADER_TYPE_COMPUTE:
return "cs";
+ case VKD3D_SHADER_TYPE_EFFECT:
+ return "fx";
+
+ case VKD3D_SHADER_TYPE_TEXTURE:
+ return "tx";
+
+ case VKD3D_SHADER_TYPE_LIBRARY:
+ return "lib";
+
default:
FIXME("Unhandled shader type %#x.\n", type);
return "unknown";
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)
{
--
2.30.0