Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- libs/vkd3d-shader/preproc.l | 20 ++++++++++++++------ libs/vkd3d-shader/preproc.y | 12 ++++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/libs/vkd3d-shader/preproc.l b/libs/vkd3d-shader/preproc.l index 1bf14ff4..0ce75f0e 100644 --- a/libs/vkd3d-shader/preproc.l +++ b/libs/vkd3d-shader/preproc.l @@ -52,6 +52,7 @@ static void update_location(struct preproc_ctx *ctx);
%s ERROR %s INCLUDE +%s LINE
NEWLINE \r?\n WS [ \t] @@ -91,9 +92,9 @@ IDENTIFIER [A-Za-z_][A-Za-z0-9_]* <INITIAL>[0-9]+.([eE][+-]?[0-9]+)?[hHfF]? {return T_TEXT;} <INITIAL>[0-9]+([eE][+-]?[0-9]+)?[hHfF] {return T_TEXT;} <INITIAL>[0-9]+[eE][+-]?[0-9]+ {return T_TEXT;} -<INITIAL>0[xX][0-9a-fA-f]+[ul]{0,2} {return T_INTEGER;} -<INITIAL>0[0-7]*[ul]{0,2} {return T_INTEGER;} -<INITIAL>[1-9][0-9]*[ul]{0,2} {return T_INTEGER;} +<INITIAL,LINE>0[xX][0-9a-fA-f]+[ul]{0,2} {return T_INTEGER;} +<INITIAL,LINE>0[0-7]*[ul]{0,2} {return T_INTEGER;} +<INITIAL,LINE>[1-9][0-9]*[ul]{0,2} {return T_INTEGER;}
<INITIAL>## {return T_CONCAT;}
@@ -103,7 +104,7 @@ IDENTIFIER [A-Za-z_][A-Za-z0-9_]* <INITIAL>">>"=? {return T_TEXT;} <INITIAL>[-+*/%&|^]= {return T_TEXT;}
-<INCLUDE>"[^"]*" {return T_STRING;} +<INCLUDE,LINE>"[^"]*" {return T_STRING;} <INCLUDE><[^>]*> {return T_STRING;}
/* C strings (including escaped quotes). */ @@ -131,6 +132,12 @@ IDENTIFIER [A-Za-z_][A-Za-z0-9_]* return T_INCLUDE; }
+ if (!strcmp(p, "line")) + { + BEGIN(LINE); + return T_LINE; + } + if (!strcmp(p, "define")) return T_DEFINE; if (!strcmp(p, "elif")) @@ -155,8 +162,8 @@ IDENTIFIER [A-Za-z_][A-Za-z0-9_]* return T_TEXT; }
-<INITIAL,INCLUDE>\{NEWLINE} {} -<INITIAL,INCLUDE,ERROR>{NEWLINE} { +<INITIAL,INCLUDE,LINE>\{NEWLINE} {} +<INITIAL,INCLUDE,ERROR,LINE>{NEWLINE} { BEGIN(INITIAL); return T_NEWLINE; } @@ -371,6 +378,7 @@ int yylex(YYSTYPE *lval, YYLTYPE *lloc, yyscan_t scanner) case T_IFDEF: case T_IFNDEF: case T_INCLUDE: + case T_LINE: case T_PRAGMA: case T_UNDEF: ctx->current_directive = token; diff --git a/libs/vkd3d-shader/preproc.y b/libs/vkd3d-shader/preproc.y index 7a995430..01437b2b 100644 --- a/libs/vkd3d-shader/preproc.y +++ b/libs/vkd3d-shader/preproc.y @@ -327,6 +327,7 @@ static void free_parse_arg_names(struct parse_arg_names *args) %token T_IFDEF "#ifdef" %token T_IFNDEF "#ifndef" %token T_INCLUDE "#include" +%token T_LINE "#line" %token T_PRAGMA "#pragma" %token T_UNDEF "#undef"
@@ -671,6 +672,17 @@ directive } vkd3d_free($2); } + | T_LINE T_INTEGER T_NEWLINE + { + FIXME("#line directive.\n"); + vkd3d_free($2); + } + | T_LINE T_INTEGER T_STRING T_NEWLINE + { + FIXME("#line directive.\n"); + vkd3d_free($2); + vkd3d_free($3); + }
primary_expr : T_INTEGER
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- libs/vkd3d-shader/preproc.h | 2 ++ libs/vkd3d-shader/preproc.l | 31 +++++++++++++++++++++++++------ libs/vkd3d-shader/preproc.y | 4 ++-- tests/hlsl_d3d12.c | 10 +++++----- 4 files changed, 34 insertions(+), 13 deletions(-)
diff --git a/libs/vkd3d-shader/preproc.h b/libs/vkd3d-shader/preproc.h index eff70f69..15d173a4 100644 --- a/libs/vkd3d-shader/preproc.h +++ b/libs/vkd3d-shader/preproc.h @@ -130,6 +130,8 @@ struct preproc_ctx bool error; };
+bool preproc_add_macro(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc, char *name, char **arg_names, + size_t arg_count, const struct vkd3d_shader_location *body_loc, struct vkd3d_string_buffer *body) DECLSPEC_HIDDEN; void preproc_close_include(struct preproc_ctx *ctx, const struct vkd3d_shader_code *code) DECLSPEC_HIDDEN; struct preproc_macro *preproc_find_macro(struct preproc_ctx *ctx, const char *name) DECLSPEC_HIDDEN; void preproc_free_macro(struct preproc_macro *macro) DECLSPEC_HIDDEN; diff --git a/libs/vkd3d-shader/preproc.l b/libs/vkd3d-shader/preproc.l index 0ce75f0e..c17b9d62 100644 --- a/libs/vkd3d-shader/preproc.l +++ b/libs/vkd3d-shader/preproc.l @@ -717,8 +717,9 @@ int preproc_lexer_parse(const struct vkd3d_shader_compile_info *compile_info, { static const struct vkd3d_shader_preprocess_info default_preprocess_info = {0}; struct preproc_ctx ctx = {0}; - char *source_name; + char *source_name = NULL; void *output_code; + unsigned int i;
vkd3d_string_buffer_init(&ctx.buffer); rb_init(&ctx.macros, preproc_macro_compare); @@ -727,18 +728,30 @@ int preproc_lexer_parse(const struct vkd3d_shader_compile_info *compile_info, ctx.message_context = message_context;
if (!(source_name = vkd3d_strdup(compile_info->source_name ? compile_info->source_name : "<anonymous>"))) + goto fail; + + for (i = 0; i < ctx.preprocess_info->macro_count; ++i) { - vkd3d_string_buffer_cleanup(&ctx.buffer); - return VKD3D_ERROR_OUT_OF_MEMORY; + const struct vkd3d_shader_location loc = {.source_name = source_name}; + struct vkd3d_string_buffer body; + char *name; + + vkd3d_string_buffer_init(&body); + vkd3d_string_buffer_printf(&body, "%s", ctx.preprocess_info->macros[i].value); + if (!(name = vkd3d_strdup(ctx.preprocess_info->macros[i].name))) + goto fail; + if (!preproc_add_macro(&ctx, &loc, name, NULL, 0, &loc, &body)) + { + vkd3d_free(name); + goto fail; + } }
yylex_init_extra(&ctx, &ctx.scanner); if (!preproc_push_include(&ctx, source_name, &compile_info->source)) { yylex_destroy(ctx.scanner); - vkd3d_free(source_name); - vkd3d_string_buffer_cleanup(&ctx.buffer); - return VKD3D_ERROR_OUT_OF_MEMORY; + goto fail; }
preproc_yyparse(ctx.scanner, &ctx); @@ -789,4 +802,10 @@ int preproc_lexer_parse(const struct vkd3d_shader_compile_info *compile_info, vkd3d_string_buffer_trace(&ctx.buffer); vkd3d_string_buffer_cleanup(&ctx.buffer); return VKD3D_OK; + +fail: + rb_destroy(&ctx.macros, preproc_macro_rb_free, NULL); + vkd3d_free(source_name); + vkd3d_string_buffer_cleanup(&ctx.buffer); + return VKD3D_ERROR_OUT_OF_MEMORY; } diff --git a/libs/vkd3d-shader/preproc.y b/libs/vkd3d-shader/preproc.y index 01437b2b..ac6f6479 100644 --- a/libs/vkd3d-shader/preproc.y +++ b/libs/vkd3d-shader/preproc.y @@ -83,8 +83,8 @@ struct preproc_macro *preproc_find_macro(struct preproc_ctx *ctx, const char *na return NULL; }
-static bool preproc_add_macro(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc, char *name, - char **arg_names, size_t arg_count, const struct vkd3d_shader_location *body_loc, struct vkd3d_string_buffer *body) +bool preproc_add_macro(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc, char *name, char **arg_names, + size_t arg_count, const struct vkd3d_shader_location *body_loc, struct vkd3d_string_buffer *body) { struct preproc_macro *macro; unsigned int i; diff --git a/tests/hlsl_d3d12.c b/tests/hlsl_d3d12.c index 837476d1..da8e0eac 100644 --- a/tests/hlsl_d3d12.c +++ b/tests/hlsl_d3d12.c @@ -358,7 +358,7 @@ static void test_preprocess(void) macros[0].Definition = "value"; macros[1].Name = NULL; macros[1].Definition = NULL; - todo check_preprocess("KEY", macros, NULL, "value", "KEY"); + check_preprocess("KEY", macros, NULL, "value", "KEY");
check_preprocess("#undef KEY\nKEY", macros, NULL, "KEY", "value");
@@ -367,7 +367,7 @@ static void test_preprocess(void)
macros[0].Name = "KEY"; macros[0].Definition = NULL; - todo check_preprocess("KEY", macros, NULL, NULL, "KEY"); + check_preprocess("KEY", macros, NULL, NULL, "KEY");
macros[0].Name = "0"; macros[0].Definition = "value"; @@ -383,19 +383,19 @@ static void test_preprocess(void) macros[1].Definition = "value2"; macros[2].Name = NULL; macros[2].Definition = NULL; - todo check_preprocess("KEY", macros, NULL, "value2", NULL); + check_preprocess("KEY", macros, NULL, "value2", NULL);
macros[0].Name = "KEY"; macros[0].Definition = "KEY2"; macros[1].Name = "KEY2"; macros[1].Definition = "value"; - todo check_preprocess("KEY", macros, NULL, "value", NULL); + check_preprocess("KEY", macros, NULL, "value", NULL);
macros[0].Name = "KEY2"; macros[0].Definition = "value"; macros[1].Name = "KEY"; macros[1].Definition = "KEY2"; - todo check_preprocess("KEY", macros, NULL, "value", NULL); + check_preprocess("KEY", macros, NULL, "value", NULL);
check_preprocess(test_include_top, NULL, &test_include, "pass", "fail"); ok(!refcount_file1, "Got %d references to file1.\n", refcount_file1);
Signed-off-by: Matteo Bruni mbruni@codeweavers.com
Signed-off-by: Henri Verbeet hverbeet@codeweavers.com
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- libs/vkd3d-shader/preproc.l | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/libs/vkd3d-shader/preproc.l b/libs/vkd3d-shader/preproc.l index c17b9d62..82b64925 100644 --- a/libs/vkd3d-shader/preproc.l +++ b/libs/vkd3d-shader/preproc.l @@ -104,6 +104,10 @@ IDENTIFIER [A-Za-z_][A-Za-z0-9_]* <INITIAL>">>"=? {return T_TEXT;} <INITIAL>[-+*/%&|^]= {return T_TEXT;}
+ /* Native doesn't preserve these tokens when running the preprocessor on its + * own, but there's no good reason to emulate that difference yet. */ +<INITIAL>[pv]s.[123].[0-4x] {return T_TEXT;} + <INCLUDE,LINE>"[^"]*" {return T_STRING;} <INCLUDE><[^>]*> {return T_STRING;}
Signed-off-by: Matteo Bruni mbruni@codeweavers.com
Signed-off-by: Henri Verbeet hverbeet@codeweavers.com
This corresponds to Wine commit 9f9fec18799bf59df6211b7294f8d338caa7f4db.
Note that for the moment, compilation messages have not been ported.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- Makefile.am | 14 +- libs/vkd3d-shader/hlsl.c | 1672 ++++++++++++ libs/vkd3d-shader/hlsl.h | 644 +++++ libs/vkd3d-shader/hlsl.l | 288 +++ libs/vkd3d-shader/hlsl.y | 2961 ++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_main.c | 13 +- libs/vkd3d-shader/vkd3d_shader_private.h | 9 + 7 files changed, 5598 insertions(+), 3 deletions(-) create mode 100644 libs/vkd3d-shader/hlsl.c create mode 100644 libs/vkd3d-shader/hlsl.h create mode 100644 libs/vkd3d-shader/hlsl.l create mode 100644 libs/vkd3d-shader/hlsl.y
diff --git a/Makefile.am b/Makefile.am index 27d933c9..b9e8b16f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -126,9 +126,20 @@ libs/vkd3d-shader/preproc.tab.c libs/vkd3d-shader/preproc.tab.h &: libs/vkd3d-sh @$(MKDIR_P) libs/vkd3d-shader $(VKD3D_V_BISON)$(BISON) $(YFLAGS) -d -o libs/vkd3d-shader/preproc.tab.c $<
-BUILT_SOURCES += libs/vkd3d-shader/preproc.tab.h +libs/vkd3d-shader/hlsl.yy.c: $(srcdir)/libs/vkd3d-shader/hlsl.l + $(VKD3D_V_FLEX)$(FLEX) $(LFLAGS) -o $@ $< + +libs/vkd3d-shader/hlsl.tab.c libs/vkd3d-shader/hlsl.tab.h &: libs/vkd3d-shader/hlsl.y + $(VKD3D_V_BISON)$(BISON) $(YFLAGS) -d -o libs/vkd3d-shader/hlsl.tab.c $< + +BUILT_SOURCES += \ + libs/vkd3d-shader/hlsl.tab.h \ + libs/vkd3d-shader/preproc.tab.h
vkd3d_shader_yyfiles = \ + libs/vkd3d-shader/hlsl.tab.c \ + libs/vkd3d-shader/hlsl.tab.h \ + libs/vkd3d-shader/hlsl.yy.c \ libs/vkd3d-shader/preproc.tab.c \ libs/vkd3d-shader/preproc.tab.h \ libs/vkd3d-shader/preproc.yy.c @@ -146,6 +157,7 @@ libvkd3d_shader_la_SOURCES = \ include/vkd3d_shader.h \ libs/vkd3d-shader/checksum.c \ libs/vkd3d-shader/dxbc.c \ + libs/vkd3d-shader/hlsl.c \ libs/vkd3d-shader/spirv.c \ libs/vkd3d-shader/trace.c \ libs/vkd3d-shader/vkd3d_shader.map \ diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c new file mode 100644 index 00000000..61a9e5c5 --- /dev/null +++ b/libs/vkd3d-shader/hlsl.c @@ -0,0 +1,1672 @@ +/* + * Copyright 2012 Matteo Bruni for CodeWeavers + * Copyright 2019-2020 Zebediah Figura for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "hlsl.h" + +BOOL add_declaration(struct hlsl_scope *scope, struct hlsl_ir_var *decl, BOOL local_var) +{ + struct hlsl_ir_var *var; + + LIST_FOR_EACH_ENTRY(var, &scope->vars, struct hlsl_ir_var, scope_entry) + { + if (!strcmp(decl->name, var->name)) + return FALSE; + } + if (local_var && scope->upper->upper == hlsl_ctx.globals) + { + /* Check whether the variable redefines a function parameter. */ + LIST_FOR_EACH_ENTRY(var, &scope->upper->vars, struct hlsl_ir_var, scope_entry) + { + if (!strcmp(decl->name, var->name)) + return FALSE; + } + } + + list_add_tail(&scope->vars, &decl->scope_entry); + return TRUE; +} + +struct hlsl_ir_var *get_variable(struct hlsl_scope *scope, const char *name) +{ + struct hlsl_ir_var *var; + + LIST_FOR_EACH_ENTRY(var, &scope->vars, struct hlsl_ir_var, scope_entry) + { + if (!strcmp(name, var->name)) + return var; + } + if (!scope->upper) + return NULL; + return get_variable(scope->upper, name); +} + +void free_declaration(struct hlsl_ir_var *decl) +{ + vkd3d_free((void *)decl->name); + vkd3d_free((void *)decl->semantic); + vkd3d_free((void *)decl->reg_reservation); + vkd3d_free(decl); +} + +struct hlsl_type *new_hlsl_type(const char *name, enum hlsl_type_class type_class, + enum hlsl_base_type base_type, unsigned dimx, unsigned dimy) +{ + struct hlsl_type *type; + + if (!(type = vkd3d_calloc(1, sizeof(*type)))) + return NULL; + type->name = name; + type->type = type_class; + type->base_type = base_type; + type->dimx = dimx; + type->dimy = dimy; + if (type_class == HLSL_CLASS_MATRIX) + type->reg_size = is_row_major(type) ? dimy : dimx; + else + type->reg_size = 1; + + list_add_tail(&hlsl_ctx.types, &type->entry); + + return type; +} + +struct hlsl_type *new_array_type(struct hlsl_type *basic_type, unsigned int array_size) +{ + struct hlsl_type *type = new_hlsl_type(NULL, HLSL_CLASS_ARRAY, HLSL_TYPE_FLOAT, 1, 1); + + if (!type) + return NULL; + + type->modifiers = basic_type->modifiers; + type->e.array.elements_count = array_size; + type->e.array.type = basic_type; + type->reg_size = basic_type->reg_size * array_size; + type->dimx = basic_type->dimx; + type->dimy = basic_type->dimy; + return type; +} + +struct hlsl_type *get_type(struct hlsl_scope *scope, const char *name, BOOL recursive) +{ + struct rb_entry *entry = rb_get(&scope->types, name); + + if (entry) + return RB_ENTRY_VALUE(entry, struct hlsl_type, scope_entry); + + if (recursive && scope->upper) + return get_type(scope->upper, name, recursive); + return NULL; +} + +BOOL find_function(const char *name) +{ + return rb_get(&hlsl_ctx.functions, name) != NULL; +} + +unsigned int components_count_type(struct hlsl_type *type) +{ + struct hlsl_struct_field *field; + unsigned int count = 0; + + if (type->type <= HLSL_CLASS_LAST_NUMERIC) + { + return type->dimx * type->dimy; + } + if (type->type == HLSL_CLASS_ARRAY) + { + return components_count_type(type->e.array.type) * type->e.array.elements_count; + } + if (type->type != HLSL_CLASS_STRUCT) + { + ERR("Unexpected data type %s.\n", debug_hlsl_type(type)); + return 0; + } + + LIST_FOR_EACH_ENTRY(field, type->e.elements, struct hlsl_struct_field, entry) + { + count += components_count_type(field->type); + } + return count; +} + +BOOL compare_hlsl_types(const struct hlsl_type *t1, const struct hlsl_type *t2) +{ + if (t1 == t2) + return TRUE; + + if (t1->type != t2->type) + return FALSE; + if (t1->base_type != t2->base_type) + return FALSE; + if (t1->base_type == HLSL_TYPE_SAMPLER && t1->sampler_dim != t2->sampler_dim) + return FALSE; + if ((t1->modifiers & HLSL_MODIFIERS_MAJORITY_MASK) + != (t2->modifiers & HLSL_MODIFIERS_MAJORITY_MASK)) + return FALSE; + if (t1->dimx != t2->dimx) + return FALSE; + if (t1->dimy != t2->dimy) + return FALSE; + if (t1->type == HLSL_CLASS_STRUCT) + { + struct list *t1cur, *t2cur; + struct hlsl_struct_field *t1field, *t2field; + + t1cur = list_head(t1->e.elements); + t2cur = list_head(t2->e.elements); + while (t1cur && t2cur) + { + t1field = LIST_ENTRY(t1cur, struct hlsl_struct_field, entry); + t2field = LIST_ENTRY(t2cur, struct hlsl_struct_field, entry); + if (!compare_hlsl_types(t1field->type, t2field->type)) + return FALSE; + if (strcmp(t1field->name, t2field->name)) + return FALSE; + t1cur = list_next(t1->e.elements, t1cur); + t2cur = list_next(t2->e.elements, t2cur); + } + if (t1cur != t2cur) + return FALSE; + } + if (t1->type == HLSL_CLASS_ARRAY) + return t1->e.array.elements_count == t2->e.array.elements_count + && compare_hlsl_types(t1->e.array.type, t2->e.array.type); + + return TRUE; +} + +struct hlsl_type *clone_hlsl_type(struct hlsl_type *old, unsigned int default_majority) +{ + struct hlsl_struct_field *old_field, *field; + struct hlsl_type *type; + + if (!(type = vkd3d_calloc(1, sizeof(*type)))) + return NULL; + + if (old->name) + { + type->name = vkd3d_strdup(old->name); + if (!type->name) + { + vkd3d_free(type); + return NULL; + } + } + type->type = old->type; + type->base_type = old->base_type; + type->dimx = old->dimx; + type->dimy = old->dimy; + type->modifiers = old->modifiers; + if (!(type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK)) + type->modifiers |= default_majority; + type->sampler_dim = old->sampler_dim; + switch (old->type) + { + case HLSL_CLASS_ARRAY: + type->e.array.type = clone_hlsl_type(old->e.array.type, default_majority); + type->e.array.elements_count = old->e.array.elements_count; + type->reg_size = type->e.array.elements_count * type->e.array.type->reg_size; + break; + + case HLSL_CLASS_STRUCT: + { + unsigned int reg_size = 0; + + if (!(type->e.elements = vkd3d_malloc(sizeof(*type->e.elements)))) + { + vkd3d_free((void *)type->name); + vkd3d_free(type); + return NULL; + } + list_init(type->e.elements); + LIST_FOR_EACH_ENTRY(old_field, old->e.elements, struct hlsl_struct_field, entry) + { + if (!(field = vkd3d_calloc(1, sizeof(*field)))) + { + LIST_FOR_EACH_ENTRY_SAFE(field, old_field, type->e.elements, struct hlsl_struct_field, entry) + { + vkd3d_free((void *)field->semantic); + vkd3d_free((void *)field->name); + vkd3d_free(field); + } + vkd3d_free(type->e.elements); + vkd3d_free((void *)type->name); + vkd3d_free(type); + return NULL; + } + field->type = clone_hlsl_type(old_field->type, default_majority); + field->name = vkd3d_strdup(old_field->name); + if (old_field->semantic) + field->semantic = vkd3d_strdup(old_field->semantic); + field->modifiers = old_field->modifiers; + field->reg_offset = reg_size; + reg_size += field->type->reg_size; + list_add_tail(type->e.elements, &field->entry); + } + type->reg_size = reg_size; + break; + } + + case HLSL_CLASS_MATRIX: + type->reg_size = is_row_major(type) ? type->dimy : type->dimx; + break; + + default: + type->reg_size = 1; + break; + } + + list_add_tail(&hlsl_ctx.types, &type->entry); + return type; +} + +static BOOL convertible_data_type(struct hlsl_type *type) +{ + return type->type != HLSL_CLASS_OBJECT; +} + +BOOL compatible_data_types(struct hlsl_type *t1, struct hlsl_type *t2) +{ + if (!convertible_data_type(t1) || !convertible_data_type(t2)) + return FALSE; + + if (t1->type <= HLSL_CLASS_LAST_NUMERIC) + { + /* Scalar vars can be cast to pretty much everything */ + if (t1->dimx == 1 && t1->dimy == 1) + return TRUE; + + if (t1->type == HLSL_CLASS_VECTOR && t2->type == HLSL_CLASS_VECTOR) + return t1->dimx >= t2->dimx; + } + + /* The other way around is true too i.e. whatever to scalar */ + if (t2->type <= HLSL_CLASS_LAST_NUMERIC && t2->dimx == 1 && t2->dimy == 1) + return TRUE; + + if (t1->type == HLSL_CLASS_ARRAY) + { + if (compare_hlsl_types(t1->e.array.type, t2)) + /* e.g. float4[3] to float4 is allowed */ + return TRUE; + + if (t2->type == HLSL_CLASS_ARRAY || t2->type == HLSL_CLASS_STRUCT) + return components_count_type(t1) >= components_count_type(t2); + else + return components_count_type(t1) == components_count_type(t2); + } + + if (t1->type == HLSL_CLASS_STRUCT) + return components_count_type(t1) >= components_count_type(t2); + + if (t2->type == HLSL_CLASS_ARRAY || t2->type == HLSL_CLASS_STRUCT) + return components_count_type(t1) == components_count_type(t2); + + if (t1->type == HLSL_CLASS_MATRIX || t2->type == HLSL_CLASS_MATRIX) + { + if (t1->type == HLSL_CLASS_MATRIX && t2->type == HLSL_CLASS_MATRIX && t1->dimx >= t2->dimx && t1->dimy >= t2->dimy) + return TRUE; + + /* Matrix-vector conversion is apparently allowed if they have the same components count */ + if ((t1->type == HLSL_CLASS_VECTOR || t2->type == HLSL_CLASS_VECTOR) + && components_count_type(t1) == components_count_type(t2)) + return TRUE; + return FALSE; + } + + if (components_count_type(t1) >= components_count_type(t2)) + return TRUE; + return FALSE; +} + +static BOOL implicit_compatible_data_types(struct hlsl_type *t1, struct hlsl_type *t2) +{ + if (!convertible_data_type(t1) || !convertible_data_type(t2)) + return FALSE; + + if (t1->type <= HLSL_CLASS_LAST_NUMERIC) + { + /* Scalar vars can be converted to any other numeric data type */ + if (t1->dimx == 1 && t1->dimy == 1 && t2->type <= HLSL_CLASS_LAST_NUMERIC) + return TRUE; + /* The other way around is true too */ + if (t2->dimx == 1 && t2->dimy == 1 && t2->type <= HLSL_CLASS_LAST_NUMERIC) + return TRUE; + } + + if (t1->type == HLSL_CLASS_ARRAY && t2->type == HLSL_CLASS_ARRAY) + { + return components_count_type(t1) == components_count_type(t2); + } + + if ((t1->type == HLSL_CLASS_ARRAY && t2->type <= HLSL_CLASS_LAST_NUMERIC) + || (t1->type <= HLSL_CLASS_LAST_NUMERIC && t2->type == HLSL_CLASS_ARRAY)) + { + /* e.g. float4[3] to float4 is allowed */ + if (t1->type == HLSL_CLASS_ARRAY && compare_hlsl_types(t1->e.array.type, t2)) + return TRUE; + if (components_count_type(t1) == components_count_type(t2)) + return TRUE; + return FALSE; + } + + if (t1->type <= HLSL_CLASS_VECTOR && t2->type <= HLSL_CLASS_VECTOR) + { + if (t1->dimx >= t2->dimx) + return TRUE; + return FALSE; + } + + if (t1->type == HLSL_CLASS_MATRIX || t2->type == HLSL_CLASS_MATRIX) + { + if (t1->type == HLSL_CLASS_MATRIX && t2->type == HLSL_CLASS_MATRIX + && t1->dimx >= t2->dimx && t1->dimy >= t2->dimy) + return TRUE; + + /* Matrix-vector conversion is apparently allowed if they have the same components count */ + if ((t1->type == HLSL_CLASS_VECTOR || t2->type == HLSL_CLASS_VECTOR) + && components_count_type(t1) == components_count_type(t2)) + return TRUE; + return FALSE; + } + + if (t1->type == HLSL_CLASS_STRUCT && t2->type == HLSL_CLASS_STRUCT) + return compare_hlsl_types(t1, t2); + + return FALSE; +} + +static BOOL expr_compatible_data_types(struct hlsl_type *t1, struct hlsl_type *t2) +{ + if (t1->base_type > HLSL_TYPE_LAST_SCALAR || t2->base_type > HLSL_TYPE_LAST_SCALAR) + return FALSE; + + /* Scalar vars can be converted to pretty much everything */ + if ((t1->dimx == 1 && t1->dimy == 1) || (t2->dimx == 1 && t2->dimy == 1)) + return TRUE; + + if (t1->type == HLSL_CLASS_VECTOR && t2->type == HLSL_CLASS_VECTOR) + return TRUE; + + if (t1->type == HLSL_CLASS_MATRIX || t2->type == HLSL_CLASS_MATRIX) + { + /* Matrix-vector conversion is apparently allowed if either they have the same components + count or the matrix is nx1 or 1xn */ + if (t1->type == HLSL_CLASS_VECTOR || t2->type == HLSL_CLASS_VECTOR) + { + if (components_count_type(t1) == components_count_type(t2)) + return TRUE; + + return (t1->type == HLSL_CLASS_MATRIX && (t1->dimx == 1 || t1->dimy == 1)) + || (t2->type == HLSL_CLASS_MATRIX && (t2->dimx == 1 || t2->dimy == 1)); + } + + /* Both matrices */ + if ((t1->dimx >= t2->dimx && t1->dimy >= t2->dimy) + || (t1->dimx <= t2->dimx && t1->dimy <= t2->dimy)) + return TRUE; + } + + return FALSE; +} + +static enum hlsl_base_type expr_common_base_type(enum hlsl_base_type t1, enum hlsl_base_type t2) +{ + static const enum hlsl_base_type types[] = + { + HLSL_TYPE_BOOL, + HLSL_TYPE_INT, + HLSL_TYPE_UINT, + HLSL_TYPE_HALF, + HLSL_TYPE_FLOAT, + HLSL_TYPE_DOUBLE, + }; + int t1_idx = -1, t2_idx = -1, i; + + for (i = 0; i < ARRAY_SIZE(types); ++i) + { + /* Always convert away from HLSL_TYPE_HALF */ + if (t1 == types[i]) + t1_idx = t1 == HLSL_TYPE_HALF ? i + 1 : i; + if (t2 == types[i]) + t2_idx = t2 == HLSL_TYPE_HALF ? i + 1 : i; + + if (t1_idx != -1 && t2_idx != -1) + break; + } + if (t1_idx == -1 || t2_idx == -1) + { + FIXME("Unexpected base type.\n"); + return HLSL_TYPE_FLOAT; + } + return t1_idx >= t2_idx ? t1 : t2; +} + +static struct hlsl_type *expr_common_type(struct hlsl_type *t1, struct hlsl_type *t2, + struct source_location *loc) +{ + enum hlsl_type_class type; + enum hlsl_base_type base; + unsigned int dimx, dimy; + + if (t1->type > HLSL_CLASS_LAST_NUMERIC || t2->type > HLSL_CLASS_LAST_NUMERIC) + { + hlsl_report_message(*loc, HLSL_LEVEL_ERROR, "non scalar/vector/matrix data type in expression"); + return NULL; + } + + if (compare_hlsl_types(t1, t2)) + return t1; + + if (!expr_compatible_data_types(t1, t2)) + { + hlsl_report_message(*loc, HLSL_LEVEL_ERROR, "expression data types are incompatible"); + return NULL; + } + + if (t1->base_type == t2->base_type) + base = t1->base_type; + else + base = expr_common_base_type(t1->base_type, t2->base_type); + + if (t1->dimx == 1 && t1->dimy == 1) + { + type = t2->type; + dimx = t2->dimx; + dimy = t2->dimy; + } + else if (t2->dimx == 1 && t2->dimy == 1) + { + type = t1->type; + dimx = t1->dimx; + dimy = t1->dimy; + } + else if (t1->type == HLSL_CLASS_MATRIX && t2->type == HLSL_CLASS_MATRIX) + { + type = HLSL_CLASS_MATRIX; + dimx = min(t1->dimx, t2->dimx); + dimy = min(t1->dimy, t2->dimy); + } + else + { + /* Two vectors or a vector and a matrix (matrix must be 1xn or nx1) */ + unsigned int max_dim_1, max_dim_2; + + max_dim_1 = max(t1->dimx, t1->dimy); + max_dim_2 = max(t2->dimx, t2->dimy); + if (t1->dimx * t1->dimy == t2->dimx * t2->dimy) + { + type = HLSL_CLASS_VECTOR; + dimx = max(t1->dimx, t2->dimx); + dimy = 1; + } + else if (max_dim_1 <= max_dim_2) + { + type = t1->type; + if (type == HLSL_CLASS_VECTOR) + { + dimx = max_dim_1; + dimy = 1; + } + else + { + dimx = t1->dimx; + dimy = t1->dimy; + } + } + else + { + type = t2->type; + if (type == HLSL_CLASS_VECTOR) + { + dimx = max_dim_2; + dimy = 1; + } + else + { + dimx = t2->dimx; + dimy = t2->dimy; + } + } + } + + if (type == HLSL_CLASS_SCALAR) + return hlsl_ctx.builtin_types.scalar[base]; + if (type == HLSL_CLASS_VECTOR) + return hlsl_ctx.builtin_types.vector[base][dimx - 1]; + return new_hlsl_type(NULL, type, base, dimx, dimy); +} + +struct hlsl_ir_node *add_implicit_conversion(struct list *instrs, struct hlsl_ir_node *node, + struct hlsl_type *dst_type, struct source_location *loc) +{ + struct hlsl_type *src_type = node->data_type; + struct hlsl_ir_expr *cast; + + if (compare_hlsl_types(src_type, dst_type)) + return node; + + if (!implicit_compatible_data_types(src_type, dst_type)) + { + hlsl_report_message(*loc, HLSL_LEVEL_ERROR, "can't implicitly convert %s to %s", + debug_hlsl_type(src_type), debug_hlsl_type(dst_type)); + return NULL; + } + + if (dst_type->dimx * dst_type->dimy < src_type->dimx * src_type->dimy) + hlsl_report_message(*loc, HLSL_LEVEL_WARNING, "implicit truncation of vector type"); + + TRACE("Implicit conversion from %s to %s.\n", debug_hlsl_type(src_type), debug_hlsl_type(dst_type)); + + if (!(cast = new_cast(node, dst_type, loc))) + return NULL; + list_add_tail(instrs, &cast->node.entry); + return &cast->node; +} + +struct hlsl_ir_expr *add_expr(struct list *instrs, enum hlsl_ir_expr_op op, struct hlsl_ir_node *operands[3], + struct source_location *loc) +{ + struct hlsl_ir_expr *expr; + struct hlsl_type *type; + unsigned int i; + + type = operands[0]->data_type; + for (i = 1; i <= 2; ++i) + { + if (!operands[i]) + break; + type = expr_common_type(type, operands[i]->data_type, loc); + if (!type) + return NULL; + } + for (i = 0; i <= 2; ++i) + { + struct hlsl_ir_expr *cast; + + if (!operands[i]) + break; + if (compare_hlsl_types(operands[i]->data_type, type)) + continue; + TRACE("Implicitly converting %s into %s in an expression.\n", debug_hlsl_type(operands[i]->data_type), debug_hlsl_type(type)); + if (operands[i]->data_type->dimx * operands[i]->data_type->dimy != 1 + && operands[i]->data_type->dimx * operands[i]->data_type->dimy != type->dimx * type->dimy) + { + hlsl_report_message(operands[i]->loc, HLSL_LEVEL_WARNING, "implicit truncation of vector/matrix type"); + } + + if (!(cast = new_cast(operands[i], type, &operands[i]->loc))) + return NULL; + list_add_after(&operands[i]->entry, &cast->node.entry); + operands[i] = &cast->node; + } + + if (!(expr = vkd3d_calloc(1, sizeof(*expr)))) + return NULL; + init_node(&expr->node, HLSL_IR_EXPR, type, *loc); + expr->op = op; + for (i = 0; i <= 2; ++i) + hlsl_src_from_node(&expr->operands[i], operands[i]); + list_add_tail(instrs, &expr->node.entry); + + return expr; +} + +struct hlsl_ir_expr *new_cast(struct hlsl_ir_node *node, struct hlsl_type *type, + struct source_location *loc) +{ + struct hlsl_ir_node *cast; + + cast = new_unary_expr(HLSL_IR_UNOP_CAST, node, *loc); + if (cast) + cast->data_type = type; + return expr_from_node(cast); +} + +static enum hlsl_ir_expr_op op_from_assignment(enum parse_assign_op op) +{ + static const enum hlsl_ir_expr_op ops[] = + { + 0, + HLSL_IR_BINOP_ADD, + HLSL_IR_BINOP_SUB, + HLSL_IR_BINOP_MUL, + HLSL_IR_BINOP_DIV, + HLSL_IR_BINOP_MOD, + HLSL_IR_BINOP_LSHIFT, + HLSL_IR_BINOP_RSHIFT, + HLSL_IR_BINOP_BIT_AND, + HLSL_IR_BINOP_BIT_OR, + HLSL_IR_BINOP_BIT_XOR, + }; + + return ops[op]; +} + +static BOOL invert_swizzle(unsigned int *swizzle, unsigned int *writemask, unsigned int *ret_width) +{ + unsigned int i, j, bit = 0, inverted = 0, width, new_writemask = 0, new_swizzle = 0; + + /* Apply the writemask to the swizzle to get a new writemask and swizzle. */ + for (i = 0; i < 4; ++i) + { + if (*writemask & (1 << i)) + { + unsigned int s = (*swizzle >> (i * 2)) & 3; + new_swizzle |= s << (bit++ * 2); + if (new_writemask & (1 << s)) + return FALSE; + new_writemask |= 1 << s; + } + } + width = bit; + + /* Invert the swizzle. */ + bit = 0; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < width; ++j) + { + unsigned int s = (new_swizzle >> (j * 2)) & 3; + if (s == i) + inverted |= j << (bit++ * 2); + } + } + + *swizzle = inverted; + *writemask = new_writemask; + *ret_width = width; + return TRUE; +} + +struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lhs, + enum parse_assign_op assign_op, struct hlsl_ir_node *rhs) +{ + struct hlsl_ir_assignment *assign; + struct hlsl_type *lhs_type; + DWORD writemask = 0; + + lhs_type = lhs->data_type; + if (lhs_type->type <= HLSL_CLASS_LAST_NUMERIC) + { + writemask = (1 << lhs_type->dimx) - 1; + + if (!(rhs = add_implicit_conversion(instrs, rhs, lhs_type, &rhs->loc))) + return NULL; + } + + if (!(assign = vkd3d_malloc(sizeof(*assign)))) + return NULL; + + while (lhs->type != HLSL_IR_LOAD) + { + struct hlsl_ir_node *lhs_inner; + + if (lhs->type == HLSL_IR_EXPR && expr_from_node(lhs)->op == HLSL_IR_UNOP_CAST) + { + FIXME("Cast on the lhs.\n"); + vkd3d_free(assign); + return NULL; + } + else if (lhs->type == HLSL_IR_SWIZZLE) + { + struct hlsl_ir_swizzle *swizzle = swizzle_from_node(lhs); + const struct hlsl_type *swizzle_type = swizzle->node.data_type; + unsigned int width; + + if (lhs->data_type->type == HLSL_CLASS_MATRIX) + FIXME("Assignments with writemasks and matrices on lhs are not supported yet.\n"); + + lhs_inner = swizzle->val.node; + hlsl_src_remove(&swizzle->val); + list_remove(&lhs->entry); + + list_add_after(&rhs->entry, &lhs->entry); + hlsl_src_from_node(&swizzle->val, rhs); + if (!invert_swizzle(&swizzle->swizzle, &writemask, &width)) + { + hlsl_report_message(lhs->loc, HLSL_LEVEL_ERROR, "invalid writemask"); + vkd3d_free(assign); + return NULL; + } + assert(swizzle_type->type == HLSL_CLASS_VECTOR); + if (swizzle_type->dimx != width) + swizzle->node.data_type = hlsl_ctx.builtin_types.vector[swizzle_type->base_type][width - 1]; + rhs = &swizzle->node; + } + else + { + hlsl_report_message(lhs->loc, HLSL_LEVEL_ERROR, "invalid lvalue"); + vkd3d_free(assign); + return NULL; + } + + lhs = lhs_inner; + } + + init_node(&assign->node, HLSL_IR_ASSIGNMENT, lhs_type, lhs->loc); + assign->writemask = writemask; + assign->lhs.var = load_from_node(lhs)->src.var; + hlsl_src_from_node(&assign->lhs.offset, load_from_node(lhs)->src.offset.node); + if (assign_op != ASSIGN_OP_ASSIGN) + { + enum hlsl_ir_expr_op op = op_from_assignment(assign_op); + struct hlsl_ir_node *expr; + + TRACE("Adding an expression for the compound assignment.\n"); + expr = new_binary_expr(op, lhs, rhs); + list_add_after(&rhs->entry, &expr->entry); + rhs = expr; + } + hlsl_src_from_node(&assign->rhs, rhs); + list_add_tail(instrs, &assign->node.entry); + + return &assign->node; +} + +static int compare_hlsl_types_rb(const void *key, const struct rb_entry *entry) +{ + const struct hlsl_type *type = RB_ENTRY_VALUE(entry, const struct hlsl_type, scope_entry); + const char *name = key; + + if (name == type->name) + return 0; + + if (!name || !type->name) + { + ERR("hlsl_type without a name in a scope?\n"); + return -1; + } + return strcmp(name, type->name); +} + +void push_scope(struct hlsl_parse_ctx *ctx) +{ + struct hlsl_scope *new_scope; + + if (!(new_scope = vkd3d_malloc(sizeof(*new_scope)))) + return; + TRACE("Pushing a new scope.\n"); + list_init(&new_scope->vars); + rb_init(&new_scope->types, compare_hlsl_types_rb); + new_scope->upper = ctx->cur_scope; + ctx->cur_scope = new_scope; + list_add_tail(&ctx->scopes, &new_scope->entry); +} + +BOOL pop_scope(struct hlsl_parse_ctx *ctx) +{ + struct hlsl_scope *prev_scope = ctx->cur_scope->upper; + + if (!prev_scope) + return FALSE; + TRACE("Popping current scope.\n"); + ctx->cur_scope = prev_scope; + return TRUE; +} + +static int compare_param_hlsl_types(const struct hlsl_type *t1, const struct hlsl_type *t2) +{ + if (t1->type != t2->type) + { + if (!((t1->type == HLSL_CLASS_SCALAR && t2->type == HLSL_CLASS_VECTOR) + || (t1->type == HLSL_CLASS_VECTOR && t2->type == HLSL_CLASS_SCALAR))) + return t1->type - t2->type; + } + if (t1->base_type != t2->base_type) + return t1->base_type - t2->base_type; + if (t1->base_type == HLSL_TYPE_SAMPLER && t1->sampler_dim != t2->sampler_dim) + return t1->sampler_dim - t2->sampler_dim; + if (t1->dimx != t2->dimx) + return t1->dimx - t2->dimx; + if (t1->dimy != t2->dimy) + return t1->dimx - t2->dimx; + if (t1->type == HLSL_CLASS_STRUCT) + { + struct list *t1cur, *t2cur; + struct hlsl_struct_field *t1field, *t2field; + int r; + + t1cur = list_head(t1->e.elements); + t2cur = list_head(t2->e.elements); + while (t1cur && t2cur) + { + t1field = LIST_ENTRY(t1cur, struct hlsl_struct_field, entry); + t2field = LIST_ENTRY(t2cur, struct hlsl_struct_field, entry); + if ((r = compare_param_hlsl_types(t1field->type, t2field->type))) + return r; + if ((r = strcmp(t1field->name, t2field->name))) + return r; + t1cur = list_next(t1->e.elements, t1cur); + t2cur = list_next(t2->e.elements, t2cur); + } + if (t1cur != t2cur) + return t1cur ? 1 : -1; + return 0; + } + if (t1->type == HLSL_CLASS_ARRAY) + { + if (t1->e.array.elements_count != t2->e.array.elements_count) + return t1->e.array.elements_count - t2->e.array.elements_count; + return compare_param_hlsl_types(t1->e.array.type, t2->e.array.type); + } + + return 0; +} + +static int compare_function_decl_rb(const void *key, const struct rb_entry *entry) +{ + const struct list *params = key; + const struct hlsl_ir_function_decl *decl = RB_ENTRY_VALUE(entry, const struct hlsl_ir_function_decl, entry); + int decl_params_count = decl->parameters ? list_count(decl->parameters) : 0; + int params_count = params ? list_count(params) : 0; + struct list *p1cur, *p2cur; + int r; + + if (params_count != decl_params_count) + return params_count - decl_params_count; + + p1cur = params ? list_head(params) : NULL; + p2cur = decl->parameters ? list_head(decl->parameters) : NULL; + while (p1cur && p2cur) + { + struct hlsl_ir_var *p1, *p2; + p1 = LIST_ENTRY(p1cur, struct hlsl_ir_var, param_entry); + p2 = LIST_ENTRY(p2cur, struct hlsl_ir_var, param_entry); + if ((r = compare_param_hlsl_types(p1->data_type, p2->data_type))) + return r; + p1cur = list_next(params, p1cur); + p2cur = list_next(decl->parameters, p2cur); + } + return 0; +} + +static int compare_function_rb(const void *key, const struct rb_entry *entry) +{ + const char *name = key; + const struct hlsl_ir_function *func = RB_ENTRY_VALUE(entry, const struct hlsl_ir_function,entry); + + return strcmp(name, func->name); +} + +void init_functions_tree(struct rb_tree *funcs) +{ + rb_init(&hlsl_ctx.functions, compare_function_rb); +} + +const char *debug_base_type(const struct hlsl_type *type) +{ + const char *name = "(unknown)"; + + switch (type->base_type) + { + case HLSL_TYPE_FLOAT: name = "float"; break; + case HLSL_TYPE_HALF: name = "half"; break; + case HLSL_TYPE_DOUBLE: name = "double"; break; + case HLSL_TYPE_INT: name = "int"; break; + case HLSL_TYPE_UINT: name = "uint"; break; + case HLSL_TYPE_BOOL: name = "bool"; break; + case HLSL_TYPE_SAMPLER: + switch (type->sampler_dim) + { + case HLSL_SAMPLER_DIM_GENERIC: name = "sampler"; break; + case HLSL_SAMPLER_DIM_1D: name = "sampler1D"; break; + case HLSL_SAMPLER_DIM_2D: name = "sampler2D"; break; + case HLSL_SAMPLER_DIM_3D: name = "sampler3D"; break; + case HLSL_SAMPLER_DIM_CUBE: name = "samplerCUBE"; break; + } + break; + default: + FIXME("Unhandled case %u.\n", type->base_type); + } + return name; +} + +const char *debug_hlsl_type(const struct hlsl_type *type) +{ + const char *name; + + if (type->name) + return debugstr_a(type->name); + + if (type->type == HLSL_CLASS_STRUCT) + return "<anonymous struct>"; + + if (type->type == HLSL_CLASS_ARRAY) + { + name = debug_base_type(type->e.array.type); + return vkd3d_dbg_sprintf("%s[%u]", name, type->e.array.elements_count); + } + + name = debug_base_type(type); + + if (type->type == HLSL_CLASS_SCALAR) + return vkd3d_dbg_sprintf("%s", name); + if (type->type == HLSL_CLASS_VECTOR) + return vkd3d_dbg_sprintf("%s%u", name, type->dimx); + if (type->type == HLSL_CLASS_MATRIX) + return vkd3d_dbg_sprintf("%s%ux%u", name, type->dimx, type->dimy); + return "unexpected_type"; +} + +const char *debug_modifiers(DWORD modifiers) +{ + char string[110]; + + string[0] = 0; + if (modifiers & HLSL_STORAGE_EXTERN) + strcat(string, " extern"); /* 7 */ + if (modifiers & HLSL_STORAGE_NOINTERPOLATION) + strcat(string, " nointerpolation"); /* 16 */ + if (modifiers & HLSL_MODIFIER_PRECISE) + strcat(string, " precise"); /* 8 */ + if (modifiers & HLSL_STORAGE_SHARED) + strcat(string, " shared"); /* 7 */ + if (modifiers & HLSL_STORAGE_GROUPSHARED) + strcat(string, " groupshared"); /* 12 */ + if (modifiers & HLSL_STORAGE_STATIC) + strcat(string, " static"); /* 7 */ + if (modifiers & HLSL_STORAGE_UNIFORM) + strcat(string, " uniform"); /* 8 */ + if (modifiers & HLSL_STORAGE_VOLATILE) + strcat(string, " volatile"); /* 9 */ + if (modifiers & HLSL_MODIFIER_CONST) + strcat(string, " const"); /* 6 */ + if (modifiers & HLSL_MODIFIER_ROW_MAJOR) + strcat(string, " row_major"); /* 10 */ + if (modifiers & HLSL_MODIFIER_COLUMN_MAJOR) + strcat(string, " column_major"); /* 13 */ + if ((modifiers & (HLSL_STORAGE_IN | HLSL_STORAGE_OUT)) == (HLSL_STORAGE_IN | HLSL_STORAGE_OUT)) + strcat(string, " inout"); /* 6 */ + else if (modifiers & HLSL_STORAGE_IN) + strcat(string, " in"); /* 3 */ + else if (modifiers & HLSL_STORAGE_OUT) + strcat(string, " out"); /* 4 */ + + return vkd3d_dbg_sprintf("%s", string[0] ? string + 1 : ""); +} + +const char *debug_node_type(enum hlsl_ir_node_type type) +{ + static const char * const names[] = + { + "HLSL_IR_ASSIGNMENT", + "HLSL_IR_CONSTANT", + "HLSL_IR_EXPR", + "HLSL_IR_IF", + "HLSL_IR_LOAD", + "HLSL_IR_LOOP", + "HLSL_IR_JUMP", + "HLSL_IR_SWIZZLE", + }; + + if (type >= ARRAY_SIZE(names)) + return "Unexpected node type"; + return names[type]; +} + +static void debug_dump_instr(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_node *instr); + +static void debug_dump_instr_list(struct vkd3d_string_buffer *buffer, const struct list *list) +{ + struct hlsl_ir_node *instr; + + LIST_FOR_EACH_ENTRY(instr, list, struct hlsl_ir_node, entry) + { + debug_dump_instr(buffer, instr); + vkd3d_string_buffer_printf(buffer, "\n"); + } +} + +static void debug_dump_src(struct vkd3d_string_buffer *buffer, const struct hlsl_src *src) +{ + if (src->node->index) + vkd3d_string_buffer_printf(buffer, "@%u", src->node->index); + else + vkd3d_string_buffer_printf(buffer, "%p", src->node); +} + +static void debug_dump_ir_var(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_var *var) +{ + if (var->modifiers) + vkd3d_string_buffer_printf(buffer, "%s ", debug_modifiers(var->modifiers)); + vkd3d_string_buffer_printf(buffer, "%s %s", debug_hlsl_type(var->data_type), var->name); + if (var->semantic) + vkd3d_string_buffer_printf(buffer, " : %s", var->semantic); +} + +static void debug_dump_deref(struct vkd3d_string_buffer *buffer, const struct hlsl_deref *deref) +{ + if (deref->offset.node) + /* Print the variable's type for convenience. */ + vkd3d_string_buffer_printf(buffer, "(%s %s)", debug_hlsl_type(deref->var->data_type), deref->var->name); + else + vkd3d_string_buffer_printf(buffer, "%s", deref->var->name); + if (deref->offset.node) + { + vkd3d_string_buffer_printf(buffer, "["); + debug_dump_src(buffer, &deref->offset); + vkd3d_string_buffer_printf(buffer, "]"); + } +} + +static const char *debug_writemask(DWORD writemask) +{ + static const char components[] = {'x', 'y', 'z', 'w'}; + char string[5]; + unsigned int i = 0, pos = 0; + + assert(!(writemask & ~VKD3DSP_WRITEMASK_ALL)); + + while (writemask) + { + if (writemask & 1) + string[pos++] = components[i]; + writemask >>= 1; + i++; + } + string[pos] = '\0'; + return vkd3d_dbg_sprintf(".%s", string); +} + +static void debug_dump_ir_assignment(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_assignment *assign) +{ + vkd3d_string_buffer_printf(buffer, "= ("); + debug_dump_deref(buffer, &assign->lhs); + if (assign->writemask != VKD3DSP_WRITEMASK_ALL) + vkd3d_string_buffer_printf(buffer, "%s", debug_writemask(assign->writemask)); + vkd3d_string_buffer_printf(buffer, " "); + debug_dump_src(buffer, &assign->rhs); + vkd3d_string_buffer_printf(buffer, ")"); +} + +static void debug_dump_ir_constant(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_constant *constant) +{ + struct hlsl_type *type = constant->node.data_type; + unsigned int x; + + if (type->dimx != 1) + vkd3d_string_buffer_printf(buffer, "{"); + for (x = 0; x < type->dimx; ++x) + { + switch (type->base_type) + { + case HLSL_TYPE_BOOL: + vkd3d_string_buffer_printf(buffer, "%s ", constant->value.b[x] ? "true" : "false"); + break; + + case HLSL_TYPE_DOUBLE: + vkd3d_string_buffer_printf(buffer, "%.16e ", constant->value.d[x]); + break; + + case HLSL_TYPE_FLOAT: + vkd3d_string_buffer_printf(buffer, "%.8e ", constant->value.f[x]); + break; + + case HLSL_TYPE_INT: + vkd3d_string_buffer_printf(buffer, "%d ", constant->value.i[x]); + break; + + case HLSL_TYPE_UINT: + vkd3d_string_buffer_printf(buffer, "%u ", constant->value.u[x]); + break; + + default: + vkd3d_string_buffer_printf(buffer, "Constants of type %s not supported\n", debug_base_type(type)); + } + } + if (type->dimx != 1) + vkd3d_string_buffer_printf(buffer, "}"); +} + +static const char *debug_expr_op(const struct hlsl_ir_expr *expr) +{ + static const char * const op_names[] = + { + "~", + "!", + "-", + "abs", + "sign", + "rcp", + "rsq", + "sqrt", + "nrm", + "exp2", + "log2", + + "cast", + + "fract", + + "sin", + "cos", + "sin_reduced", + "cos_reduced", + + "dsx", + "dsy", + + "sat", + + "pre++", + "pre--", + "post++", + "post--", + + "+", + "-", + "*", + "/", + + "%", + + "<", + ">", + "<=", + ">=", + "==", + "!=", + + "&&", + "||", + + "<<", + ">>", + "&", + "|", + "^", + + "dot", + "crs", + "min", + "max", + + "pow", + + "lerp", + + ",", + }; + + if (expr->op == HLSL_IR_UNOP_CAST) + return debug_hlsl_type(expr->node.data_type); + + return op_names[expr->op]; +} + +/* Dumps the expression in a prefix "operator (operands)" form */ +static void debug_dump_ir_expr(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_expr *expr) +{ + unsigned int i; + + vkd3d_string_buffer_printf(buffer, "%s (", debug_expr_op(expr)); + for (i = 0; i < 3 && expr->operands[i].node; ++i) + { + debug_dump_src(buffer, &expr->operands[i]); + vkd3d_string_buffer_printf(buffer, " "); + } + vkd3d_string_buffer_printf(buffer, ")"); +} + +static void debug_dump_ir_if(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_if *if_node) +{ + vkd3d_string_buffer_printf(buffer, "if ("); + debug_dump_src(buffer, &if_node->condition); + vkd3d_string_buffer_printf(buffer, ")\n{\n"); + debug_dump_instr_list(buffer, &if_node->then_instrs); + vkd3d_string_buffer_printf(buffer, "}\nelse\n{\n"); + debug_dump_instr_list(buffer, &if_node->else_instrs); + vkd3d_string_buffer_printf(buffer, "}\n"); +} + +static void debug_dump_ir_jump(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_jump *jump) +{ + switch (jump->type) + { + case HLSL_IR_JUMP_BREAK: + vkd3d_string_buffer_printf(buffer, "break"); + break; + + case HLSL_IR_JUMP_CONTINUE: + vkd3d_string_buffer_printf(buffer, "continue"); + break; + + case HLSL_IR_JUMP_DISCARD: + vkd3d_string_buffer_printf(buffer, "discard"); + break; + + case HLSL_IR_JUMP_RETURN: + vkd3d_string_buffer_printf(buffer, "return"); + break; + } +} + +static void debug_dump_ir_loop(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_loop *loop) +{ + vkd3d_string_buffer_printf(buffer, "for (;;)\n{\n"); + debug_dump_instr_list(buffer, &loop->body); + vkd3d_string_buffer_printf(buffer, "}\n"); +} + +static void debug_dump_ir_swizzle(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_swizzle *swizzle) +{ + unsigned int i; + + debug_dump_src(buffer, &swizzle->val); + vkd3d_string_buffer_printf(buffer, "."); + if (swizzle->val.node->data_type->dimy > 1) + { + for (i = 0; i < swizzle->node.data_type->dimx; ++i) + vkd3d_string_buffer_printf(buffer, "_m%u%u", (swizzle->swizzle >> i * 8) & 0xf, (swizzle->swizzle >> (i * 8 + 4)) & 0xf); + } + else + { + static const char c[] = {'x', 'y', 'z', 'w'}; + + for (i = 0; i < swizzle->node.data_type->dimx; ++i) + vkd3d_string_buffer_printf(buffer, "%c", c[(swizzle->swizzle >> i * 2) & 0x3]); + } +} + +static void debug_dump_instr(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_node *instr) +{ + if (instr->index) + vkd3d_string_buffer_printf(buffer, "%4u: ", instr->index); + else + vkd3d_string_buffer_printf(buffer, "%p: ", instr); + + vkd3d_string_buffer_printf(buffer, "%10s | ", instr->data_type ? debug_hlsl_type(instr->data_type) : ""); + + switch (instr->type) + { + case HLSL_IR_ASSIGNMENT: + debug_dump_ir_assignment(buffer, assignment_from_node(instr)); + break; + + case HLSL_IR_CONSTANT: + debug_dump_ir_constant(buffer, constant_from_node(instr)); + break; + + case HLSL_IR_EXPR: + debug_dump_ir_expr(buffer, expr_from_node(instr)); + break; + + case HLSL_IR_IF: + debug_dump_ir_if(buffer, if_from_node(instr)); + break; + + case HLSL_IR_JUMP: + debug_dump_ir_jump(buffer, jump_from_node(instr)); + break; + + case HLSL_IR_LOAD: + debug_dump_deref(buffer, &load_from_node(instr)->src); + break; + + case HLSL_IR_LOOP: + debug_dump_ir_loop(buffer, loop_from_node(instr)); + break; + + case HLSL_IR_SWIZZLE: + debug_dump_ir_swizzle(buffer, swizzle_from_node(instr)); + break; + + default: + vkd3d_string_buffer_printf(buffer, "<No dump function for %s>", debug_node_type(instr->type)); + } +} + +void debug_dump_ir_function_decl(const struct hlsl_ir_function_decl *func) +{ + struct vkd3d_string_buffer buffer; + struct hlsl_ir_var *param; + + vkd3d_string_buffer_init(&buffer); + vkd3d_string_buffer_printf(&buffer, "Dumping function %s.\n", func->func->name); + vkd3d_string_buffer_printf(&buffer, "Function parameters:\n"); + LIST_FOR_EACH_ENTRY(param, func->parameters, struct hlsl_ir_var, param_entry) + { + debug_dump_ir_var(&buffer, param); + vkd3d_string_buffer_printf(&buffer, "\n"); + } + if (func->semantic) + vkd3d_string_buffer_printf(&buffer, "Function semantic: %s\n", func->semantic); + if (func->body) + debug_dump_instr_list(&buffer, func->body); + + vkd3d_string_buffer_trace(&buffer); + vkd3d_string_buffer_cleanup(&buffer); +} + +void free_hlsl_type(struct hlsl_type *type) +{ + struct hlsl_struct_field *field, *next_field; + + vkd3d_free((void *)type->name); + if (type->type == HLSL_CLASS_STRUCT) + { + LIST_FOR_EACH_ENTRY_SAFE(field, next_field, type->e.elements, struct hlsl_struct_field, entry) + { + vkd3d_free((void *)field->name); + vkd3d_free((void *)field->semantic); + vkd3d_free(field); + } + } + vkd3d_free(type); +} + +void free_instr_list(struct list *list) +{ + struct hlsl_ir_node *node, *next_node; + + if (!list) + return; + /* Iterate in reverse, to avoid use-after-free when unlinking sources from + * the "uses" list. */ + LIST_FOR_EACH_ENTRY_SAFE_REV(node, next_node, list, struct hlsl_ir_node, entry) + free_instr(node); + vkd3d_free(list); +} + +static void free_ir_assignment(struct hlsl_ir_assignment *assignment) +{ + hlsl_src_remove(&assignment->rhs); + hlsl_src_remove(&assignment->lhs.offset); + vkd3d_free(assignment); +} + +static void free_ir_constant(struct hlsl_ir_constant *constant) +{ + vkd3d_free(constant); +} + +static void free_ir_expr(struct hlsl_ir_expr *expr) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(expr->operands); ++i) + hlsl_src_remove(&expr->operands[i]); + vkd3d_free(expr); +} + +static void free_ir_if(struct hlsl_ir_if *if_node) +{ + struct hlsl_ir_node *node, *next_node; + + LIST_FOR_EACH_ENTRY_SAFE(node, next_node, &if_node->then_instrs, struct hlsl_ir_node, entry) + free_instr(node); + LIST_FOR_EACH_ENTRY_SAFE(node, next_node, &if_node->else_instrs, struct hlsl_ir_node, entry) + free_instr(node); + hlsl_src_remove(&if_node->condition); + vkd3d_free(if_node); +} + +static void free_ir_jump(struct hlsl_ir_jump *jump) +{ + vkd3d_free(jump); +} + +static void free_ir_load(struct hlsl_ir_load *load) +{ + hlsl_src_remove(&load->src.offset); + vkd3d_free(load); +} + +static void free_ir_loop(struct hlsl_ir_loop *loop) +{ + struct hlsl_ir_node *node, *next_node; + + LIST_FOR_EACH_ENTRY_SAFE(node, next_node, &loop->body, struct hlsl_ir_node, entry) + free_instr(node); + vkd3d_free(loop); +} + +static void free_ir_swizzle(struct hlsl_ir_swizzle *swizzle) +{ + hlsl_src_remove(&swizzle->val); + vkd3d_free(swizzle); +} + +void free_instr(struct hlsl_ir_node *node) +{ + switch (node->type) + { + case HLSL_IR_ASSIGNMENT: + free_ir_assignment(assignment_from_node(node)); + break; + + case HLSL_IR_CONSTANT: + free_ir_constant(constant_from_node(node)); + break; + + case HLSL_IR_EXPR: + free_ir_expr(expr_from_node(node)); + break; + + case HLSL_IR_IF: + free_ir_if(if_from_node(node)); + break; + + case HLSL_IR_JUMP: + free_ir_jump(jump_from_node(node)); + break; + + case HLSL_IR_LOAD: + free_ir_load(load_from_node(node)); + break; + + case HLSL_IR_LOOP: + free_ir_loop(loop_from_node(node)); + break; + + case HLSL_IR_SWIZZLE: + free_ir_swizzle(swizzle_from_node(node)); + break; + + default: + FIXME("Unsupported node type %s.\n", debug_node_type(node->type)); + } +} + +static void free_function_decl(struct hlsl_ir_function_decl *decl) +{ + vkd3d_free((void *)decl->semantic); + vkd3d_free(decl->parameters); + free_instr_list(decl->body); + vkd3d_free(decl); +} + +static void free_function_decl_rb(struct rb_entry *entry, void *context) +{ + free_function_decl(RB_ENTRY_VALUE(entry, struct hlsl_ir_function_decl, entry)); +} + +static void free_function(struct hlsl_ir_function *func) +{ + rb_destroy(&func->overloads, free_function_decl_rb, NULL); + vkd3d_free((void *)func->name); + vkd3d_free(func); +} + +void free_function_rb(struct rb_entry *entry, void *context) +{ + free_function(RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry)); +} + +void add_function_decl(struct rb_tree *funcs, char *name, struct hlsl_ir_function_decl *decl, BOOL intrinsic) +{ + struct hlsl_ir_function *func; + struct rb_entry *func_entry, *old_entry; + + func_entry = rb_get(funcs, name); + if (func_entry) + { + func = RB_ENTRY_VALUE(func_entry, struct hlsl_ir_function, entry); + if (intrinsic != func->intrinsic) + { + if (intrinsic) + { + ERR("Redeclaring a user defined function as an intrinsic.\n"); + return; + } + TRACE("Function %s redeclared as a user defined function.\n", debugstr_a(name)); + func->intrinsic = intrinsic; + rb_destroy(&func->overloads, free_function_decl_rb, NULL); + rb_init(&func->overloads, compare_function_decl_rb); + } + decl->func = func; + if ((old_entry = rb_get(&func->overloads, decl->parameters))) + { + struct hlsl_ir_function_decl *old_decl = + RB_ENTRY_VALUE(old_entry, struct hlsl_ir_function_decl, entry); + + if (!decl->body) + { + free_function_decl(decl); + vkd3d_free(name); + return; + } + rb_remove(&func->overloads, old_entry); + free_function_decl(old_decl); + } + rb_put(&func->overloads, decl->parameters, &decl->entry); + vkd3d_free(name); + return; + } + func = vkd3d_malloc(sizeof(*func)); + func->name = name; + rb_init(&func->overloads, compare_function_decl_rb); + decl->func = func; + rb_put(&func->overloads, decl->parameters, &decl->entry); + func->intrinsic = intrinsic; + rb_put(funcs, func->name, &func->entry); +} + +struct hlsl_profile_info +{ + const char *name; + enum vkd3d_shader_type type; + DWORD sm_major; + DWORD sm_minor; + DWORD level_major; + DWORD level_minor; + BOOL sw; +}; + +static const struct hlsl_profile_info *get_target_info(const char *target) +{ + unsigned int i; + + static const struct hlsl_profile_info profiles[] = + { + {"cs_4_0", VKD3D_SHADER_TYPE_COMPUTE, 4, 0, 0, 0, FALSE}, + {"cs_4_1", VKD3D_SHADER_TYPE_COMPUTE, 4, 1, 0, 0, FALSE}, + {"cs_5_0", VKD3D_SHADER_TYPE_COMPUTE, 5, 0, 0, 0, FALSE}, + {"ds_5_0", VKD3D_SHADER_TYPE_DOMAIN, 5, 0, 0, 0, FALSE}, + {"fx_2_0", VKD3D_SHADER_TYPE_EFFECT, 2, 0, 0, 0, FALSE}, + {"fx_4_0", VKD3D_SHADER_TYPE_EFFECT, 4, 0, 0, 0, FALSE}, + {"fx_4_1", VKD3D_SHADER_TYPE_EFFECT, 4, 1, 0, 0, FALSE}, + {"fx_5_0", VKD3D_SHADER_TYPE_EFFECT, 5, 0, 0, 0, FALSE}, + {"gs_4_0", VKD3D_SHADER_TYPE_GEOMETRY, 4, 0, 0, 0, FALSE}, + {"gs_4_1", VKD3D_SHADER_TYPE_GEOMETRY, 4, 1, 0, 0, FALSE}, + {"gs_5_0", VKD3D_SHADER_TYPE_GEOMETRY, 5, 0, 0, 0, FALSE}, + {"hs_5_0", VKD3D_SHADER_TYPE_HULL, 5, 0, 0, 0, FALSE}, + {"ps.1.0", VKD3D_SHADER_TYPE_PIXEL, 1, 0, 0, 0, FALSE}, + {"ps.1.1", VKD3D_SHADER_TYPE_PIXEL, 1, 1, 0, 0, FALSE}, + {"ps.1.2", VKD3D_SHADER_TYPE_PIXEL, 1, 2, 0, 0, FALSE}, + {"ps.1.3", VKD3D_SHADER_TYPE_PIXEL, 1, 3, 0, 0, FALSE}, + {"ps.1.4", VKD3D_SHADER_TYPE_PIXEL, 1, 4, 0, 0, FALSE}, + {"ps.2.0", VKD3D_SHADER_TYPE_PIXEL, 2, 0, 0, 0, FALSE}, + {"ps.2.a", VKD3D_SHADER_TYPE_PIXEL, 2, 1, 0, 0, FALSE}, + {"ps.2.b", VKD3D_SHADER_TYPE_PIXEL, 2, 2, 0, 0, FALSE}, + {"ps.2.sw", VKD3D_SHADER_TYPE_PIXEL, 2, 0, 0, 0, TRUE}, + {"ps.3.0", VKD3D_SHADER_TYPE_PIXEL, 3, 0, 0, 0, FALSE}, + {"ps_1_0", VKD3D_SHADER_TYPE_PIXEL, 1, 0, 0, 0, FALSE}, + {"ps_1_1", VKD3D_SHADER_TYPE_PIXEL, 1, 1, 0, 0, FALSE}, + {"ps_1_2", VKD3D_SHADER_TYPE_PIXEL, 1, 2, 0, 0, FALSE}, + {"ps_1_3", VKD3D_SHADER_TYPE_PIXEL, 1, 3, 0, 0, FALSE}, + {"ps_1_4", VKD3D_SHADER_TYPE_PIXEL, 1, 4, 0, 0, FALSE}, + {"ps_2_0", VKD3D_SHADER_TYPE_PIXEL, 2, 0, 0, 0, FALSE}, + {"ps_2_a", VKD3D_SHADER_TYPE_PIXEL, 2, 1, 0, 0, FALSE}, + {"ps_2_b", VKD3D_SHADER_TYPE_PIXEL, 2, 2, 0, 0, FALSE}, + {"ps_2_sw", VKD3D_SHADER_TYPE_PIXEL, 2, 0, 0, 0, TRUE}, + {"ps_3_0", VKD3D_SHADER_TYPE_PIXEL, 3, 0, 0, 0, FALSE}, + {"ps_3_sw", VKD3D_SHADER_TYPE_PIXEL, 3, 0, 0, 0, TRUE}, + {"ps_4_0", VKD3D_SHADER_TYPE_PIXEL, 4, 0, 0, 0, FALSE}, + {"ps_4_0_level_9_0", VKD3D_SHADER_TYPE_PIXEL, 4, 0, 9, 0, FALSE}, + {"ps_4_0_level_9_1", VKD3D_SHADER_TYPE_PIXEL, 4, 0, 9, 1, FALSE}, + {"ps_4_0_level_9_3", VKD3D_SHADER_TYPE_PIXEL, 4, 0, 9, 3, FALSE}, + {"ps_4_1", VKD3D_SHADER_TYPE_PIXEL, 4, 1, 0, 0, FALSE}, + {"ps_5_0", VKD3D_SHADER_TYPE_PIXEL, 5, 0, 0, 0, FALSE}, + {"tx_1_0", VKD3D_SHADER_TYPE_TEXTURE, 1, 0, 0, 0, FALSE}, + {"vs.1.0", VKD3D_SHADER_TYPE_VERTEX, 1, 0, 0, 0, FALSE}, + {"vs.1.1", VKD3D_SHADER_TYPE_VERTEX, 1, 1, 0, 0, FALSE}, + {"vs.2.0", VKD3D_SHADER_TYPE_VERTEX, 2, 0, 0, 0, FALSE}, + {"vs.2.a", VKD3D_SHADER_TYPE_VERTEX, 2, 1, 0, 0, FALSE}, + {"vs.2.sw", VKD3D_SHADER_TYPE_VERTEX, 2, 0, 0, 0, TRUE}, + {"vs.3.0", VKD3D_SHADER_TYPE_VERTEX, 3, 0, 0, 0, FALSE}, + {"vs.3.sw", VKD3D_SHADER_TYPE_VERTEX, 3, 0, 0, 0, TRUE}, + {"vs_1_0", VKD3D_SHADER_TYPE_VERTEX, 1, 0, 0, 0, FALSE}, + {"vs_1_1", VKD3D_SHADER_TYPE_VERTEX, 1, 1, 0, 0, FALSE}, + {"vs_2_0", VKD3D_SHADER_TYPE_VERTEX, 2, 0, 0, 0, FALSE}, + {"vs_2_a", VKD3D_SHADER_TYPE_VERTEX, 2, 1, 0, 0, FALSE}, + {"vs_2_sw", VKD3D_SHADER_TYPE_VERTEX, 2, 0, 0, 0, TRUE}, + {"vs_3_0", VKD3D_SHADER_TYPE_VERTEX, 3, 0, 0, 0, FALSE}, + {"vs_3_sw", VKD3D_SHADER_TYPE_VERTEX, 3, 0, 0, 0, TRUE}, + {"vs_4_0", VKD3D_SHADER_TYPE_VERTEX, 4, 0, 0, 0, FALSE}, + {"vs_4_0_level_9_0", VKD3D_SHADER_TYPE_VERTEX, 4, 0, 9, 0, FALSE}, + {"vs_4_0_level_9_1", VKD3D_SHADER_TYPE_VERTEX, 4, 0, 9, 1, FALSE}, + {"vs_4_0_level_9_3", VKD3D_SHADER_TYPE_VERTEX, 4, 0, 9, 3, FALSE}, + {"vs_4_1", VKD3D_SHADER_TYPE_VERTEX, 4, 1, 0, 0, FALSE}, + {"vs_5_0", VKD3D_SHADER_TYPE_VERTEX, 5, 0, 0, 0, FALSE}, + }; + + for (i = 0; i < ARRAY_SIZE(profiles); ++i) + { + if (!strcmp(target, profiles[i].name)) + return &profiles[i]; + } + + return NULL; +} + +int hlsl_compile_shader(const char *text, const struct vkd3d_shader_compile_info *compile_info, + struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) +{ + const struct vkd3d_shader_hlsl_source_info *hlsl_source_info; + const struct hlsl_profile_info *profile; + + if (!(hlsl_source_info = vkd3d_find_struct(compile_info->next, HLSL_SOURCE_INFO))) + { + ERR("No HLSL source info given.\n"); + return VKD3D_ERROR_INVALID_ARGUMENT; + } + + if (!(profile = get_target_info(hlsl_source_info->profile))) + { + FIXME("Unknown compilation target %s.\n", debugstr_a(hlsl_source_info->profile)); + return VKD3D_ERROR_NOT_IMPLEMENTED; + } + + vkd3d_shader_dump_shader(profile->type, &compile_info->source); + + return hlsl_lexer_compile(text, profile->type, profile->sm_major, profile->sm_minor, + hlsl_source_info->entry_point, dxbc, message_context); +} diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h new file mode 100644 index 00000000..128de1d6 --- /dev/null +++ b/libs/vkd3d-shader/hlsl.h @@ -0,0 +1,644 @@ +/* + * Copyright 2012 Matteo Bruni for CodeWeavers + * Copyright 2019-2020 Zebediah Figura for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __VKD3D_SHADER_HLSL_H +#define __VKD3D_SHADER_HLSL_H + +#include "vkd3d_shader_private.h" +#include "rbtree.h" + +enum parse_status +{ + PARSE_SUCCESS = 0, + PARSE_WARN = 1, + PARSE_ERR = 2 +}; + +/* The general IR structure is inspired by Mesa GLSL hir, even though the code + * ends up being quite different in practice. Anyway, here comes the relevant + * licensing information. + * + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#define HLSL_SWIZZLE_X (0u) +#define HLSL_SWIZZLE_Y (1u) +#define HLSL_SWIZZLE_Z (2u) +#define HLSL_SWIZZLE_W (3u) + +#define HLSL_SWIZZLE(x, y, z, w) \ + (((HLSL_SWIZZLE_ ## x) << 0) \ + | ((HLSL_SWIZZLE_ ## y) << 2) \ + | ((HLSL_SWIZZLE_ ## z) << 4) \ + | ((HLSL_SWIZZLE_ ## w) << 6)) + +enum hlsl_type_class +{ + HLSL_CLASS_SCALAR, + HLSL_CLASS_VECTOR, + HLSL_CLASS_MATRIX, + HLSL_CLASS_LAST_NUMERIC = HLSL_CLASS_MATRIX, + HLSL_CLASS_STRUCT, + HLSL_CLASS_ARRAY, + HLSL_CLASS_OBJECT, +}; + +enum hlsl_base_type +{ + HLSL_TYPE_FLOAT, + HLSL_TYPE_HALF, + HLSL_TYPE_DOUBLE, + HLSL_TYPE_INT, + HLSL_TYPE_UINT, + HLSL_TYPE_BOOL, + HLSL_TYPE_LAST_SCALAR = HLSL_TYPE_BOOL, + HLSL_TYPE_SAMPLER, + HLSL_TYPE_TEXTURE, + HLSL_TYPE_PIXELSHADER, + HLSL_TYPE_VERTEXSHADER, + HLSL_TYPE_STRING, + HLSL_TYPE_VOID, +}; + +enum hlsl_sampler_dim +{ + HLSL_SAMPLER_DIM_GENERIC, + HLSL_SAMPLER_DIM_1D, + HLSL_SAMPLER_DIM_2D, + HLSL_SAMPLER_DIM_3D, + HLSL_SAMPLER_DIM_CUBE, + HLSL_SAMPLER_DIM_MAX = HLSL_SAMPLER_DIM_CUBE +}; + +enum hlsl_matrix_majority +{ + HLSL_COLUMN_MAJOR, + HLSL_ROW_MAJOR +}; + +struct hlsl_type +{ + struct list entry; + struct rb_entry scope_entry; + enum hlsl_type_class type; + enum hlsl_base_type base_type; + enum hlsl_sampler_dim sampler_dim; + const char *name; + unsigned int modifiers; + unsigned int dimx; + unsigned int dimy; + unsigned int reg_size; + union + { + struct list *elements; + struct + { + struct hlsl_type *type; + unsigned int elements_count; + } array; + } e; +}; + +struct hlsl_struct_field +{ + struct list entry; + struct hlsl_type *type; + const char *name; + const char *semantic; + DWORD modifiers; + unsigned int reg_offset; +}; + +struct source_location +{ + const char *file; + unsigned int line; + unsigned int col; +}; + +enum hlsl_ir_node_type +{ + HLSL_IR_ASSIGNMENT = 0, + HLSL_IR_CONSTANT, + HLSL_IR_EXPR, + HLSL_IR_IF, + HLSL_IR_LOAD, + HLSL_IR_LOOP, + HLSL_IR_JUMP, + HLSL_IR_SWIZZLE, +}; + +struct hlsl_ir_node +{ + struct list entry; + enum hlsl_ir_node_type type; + struct hlsl_type *data_type; + + struct list uses; + + struct source_location loc; + + /* Liveness ranges. "index" is the index of this instruction. Since this is + * essentially an SSA value, the earliest live point is the index. This is + * true even for loops, since currently we can't have a reference to a + * value generated in an earlier iteration of the loop. */ + unsigned int index, last_read; +}; + +struct hlsl_src +{ + struct hlsl_ir_node *node; + struct list entry; +}; + +#define HLSL_STORAGE_EXTERN 0x00000001 +#define HLSL_STORAGE_NOINTERPOLATION 0x00000002 +#define HLSL_MODIFIER_PRECISE 0x00000004 +#define HLSL_STORAGE_SHARED 0x00000008 +#define HLSL_STORAGE_GROUPSHARED 0x00000010 +#define HLSL_STORAGE_STATIC 0x00000020 +#define HLSL_STORAGE_UNIFORM 0x00000040 +#define HLSL_STORAGE_VOLATILE 0x00000080 +#define HLSL_MODIFIER_CONST 0x00000100 +#define HLSL_MODIFIER_ROW_MAJOR 0x00000200 +#define HLSL_MODIFIER_COLUMN_MAJOR 0x00000400 +#define HLSL_STORAGE_IN 0x00000800 +#define HLSL_STORAGE_OUT 0x00001000 + +#define HLSL_TYPE_MODIFIERS_MASK (HLSL_MODIFIER_PRECISE | HLSL_STORAGE_VOLATILE | \ + HLSL_MODIFIER_CONST | HLSL_MODIFIER_ROW_MAJOR | \ + HLSL_MODIFIER_COLUMN_MAJOR) + +#define HLSL_MODIFIERS_MAJORITY_MASK (HLSL_MODIFIER_ROW_MAJOR | HLSL_MODIFIER_COLUMN_MAJOR) + +struct reg_reservation +{ + enum vkd3d_shader_register_type type; + DWORD regnum; +}; + +struct hlsl_ir_var +{ + struct hlsl_type *data_type; + struct source_location loc; + const char *name; + const char *semantic; + unsigned int modifiers; + const struct reg_reservation *reg_reservation; + struct list scope_entry, param_entry; + + unsigned int first_write, last_read; +}; + +struct hlsl_ir_function +{ + struct rb_entry entry; + const char *name; + struct rb_tree overloads; + BOOL intrinsic; +}; + +struct hlsl_ir_function_decl +{ + struct hlsl_type *return_type; + struct hlsl_ir_var *return_var; + struct source_location loc; + struct rb_entry entry; + struct hlsl_ir_function *func; + const char *semantic; + struct list *parameters; + struct list *body; +}; + +struct hlsl_ir_if +{ + struct hlsl_ir_node node; + struct hlsl_src condition; + struct list then_instrs; + struct list else_instrs; +}; + +struct hlsl_ir_loop +{ + struct hlsl_ir_node node; + /* loop condition is stored in the body (as "if (!condition) break;") */ + struct list body; + unsigned int next_index; /* liveness index of the end of the loop */ +}; + +enum hlsl_ir_expr_op +{ + HLSL_IR_UNOP_BIT_NOT = 0, + HLSL_IR_UNOP_LOGIC_NOT, + HLSL_IR_UNOP_NEG, + HLSL_IR_UNOP_ABS, + HLSL_IR_UNOP_SIGN, + HLSL_IR_UNOP_RCP, + HLSL_IR_UNOP_RSQ, + HLSL_IR_UNOP_SQRT, + HLSL_IR_UNOP_NRM, + HLSL_IR_UNOP_EXP2, + HLSL_IR_UNOP_LOG2, + + HLSL_IR_UNOP_CAST, + + HLSL_IR_UNOP_FRACT, + + HLSL_IR_UNOP_SIN, + HLSL_IR_UNOP_COS, + HLSL_IR_UNOP_SIN_REDUCED, /* Reduced range [-pi, pi] */ + HLSL_IR_UNOP_COS_REDUCED, /* Reduced range [-pi, pi] */ + + HLSL_IR_UNOP_DSX, + HLSL_IR_UNOP_DSY, + + HLSL_IR_UNOP_SAT, + + HLSL_IR_UNOP_PREINC, + HLSL_IR_UNOP_PREDEC, + HLSL_IR_UNOP_POSTINC, + HLSL_IR_UNOP_POSTDEC, + + HLSL_IR_BINOP_ADD, + HLSL_IR_BINOP_SUB, + HLSL_IR_BINOP_MUL, + HLSL_IR_BINOP_DIV, + + HLSL_IR_BINOP_MOD, + + HLSL_IR_BINOP_LESS, + HLSL_IR_BINOP_GREATER, + HLSL_IR_BINOP_LEQUAL, + HLSL_IR_BINOP_GEQUAL, + HLSL_IR_BINOP_EQUAL, + HLSL_IR_BINOP_NEQUAL, + + HLSL_IR_BINOP_LOGIC_AND, + HLSL_IR_BINOP_LOGIC_OR, + + HLSL_IR_BINOP_LSHIFT, + HLSL_IR_BINOP_RSHIFT, + HLSL_IR_BINOP_BIT_AND, + HLSL_IR_BINOP_BIT_OR, + HLSL_IR_BINOP_BIT_XOR, + + HLSL_IR_BINOP_DOT, + HLSL_IR_BINOP_CRS, + HLSL_IR_BINOP_MIN, + HLSL_IR_BINOP_MAX, + + HLSL_IR_BINOP_POW, + + HLSL_IR_TEROP_LERP, + + HLSL_IR_SEQUENCE, +}; + +struct hlsl_ir_expr +{ + struct hlsl_ir_node node; + enum hlsl_ir_expr_op op; + struct hlsl_src operands[3]; +}; + +enum hlsl_ir_jump_type +{ + HLSL_IR_JUMP_BREAK, + HLSL_IR_JUMP_CONTINUE, + HLSL_IR_JUMP_DISCARD, + HLSL_IR_JUMP_RETURN, +}; + +struct hlsl_ir_jump +{ + struct hlsl_ir_node node; + enum hlsl_ir_jump_type type; +}; + +struct hlsl_ir_swizzle +{ + struct hlsl_ir_node node; + struct hlsl_src val; + DWORD swizzle; +}; + +struct hlsl_deref +{ + struct hlsl_ir_var *var; + struct hlsl_src offset; +}; + +struct hlsl_ir_load +{ + struct hlsl_ir_node node; + struct hlsl_deref src; +}; + +struct hlsl_ir_assignment +{ + struct hlsl_ir_node node; + struct hlsl_deref lhs; + struct hlsl_src rhs; + unsigned char writemask; +}; + +struct hlsl_ir_constant +{ + struct hlsl_ir_node node; + union + { + unsigned u[4]; + int i[4]; + float f[4]; + double d[4]; + BOOL b[4]; + } value; +}; + +struct hlsl_scope +{ + struct list entry; + struct list vars; + struct rb_tree types; + struct hlsl_scope *upper; +}; + +/* Structures used only during parsing */ +struct parse_parameter +{ + struct hlsl_type *type; + const char *name; + const char *semantic; + const struct reg_reservation *reg_reservation; + unsigned int modifiers; +}; + +struct parse_colon_attribute +{ + const char *semantic; + struct reg_reservation *reg_reservation; +}; + +struct parse_initializer +{ + struct hlsl_ir_node **args; + unsigned int args_count; + struct list *instrs; +}; + +struct parse_variable_def +{ + struct list entry; + struct source_location loc; + + char *name; + unsigned int array_size; + const char *semantic; + struct reg_reservation *reg_reservation; + struct parse_initializer initializer; +}; + +struct parse_function +{ + char *name; + struct hlsl_ir_function_decl *decl; +}; + +struct parse_if_body +{ + struct list *then_instrs; + struct list *else_instrs; +}; + +enum parse_unary_op +{ + UNARY_OP_PLUS, + UNARY_OP_MINUS, + UNARY_OP_LOGICNOT, + UNARY_OP_BITNOT, +}; + +enum parse_assign_op +{ + ASSIGN_OP_ASSIGN, + ASSIGN_OP_ADD, + ASSIGN_OP_SUB, + ASSIGN_OP_MUL, + ASSIGN_OP_DIV, + ASSIGN_OP_MOD, + ASSIGN_OP_LSHIFT, + ASSIGN_OP_RSHIFT, + ASSIGN_OP_AND, + ASSIGN_OP_OR, + ASSIGN_OP_XOR, +}; + +struct hlsl_parse_ctx +{ + const char **source_files; + unsigned int source_files_count; + const char *source_file; + unsigned int line_no; + unsigned int column; + enum parse_status status; + struct vkd3d_shader_message_context *message_context; + + struct hlsl_scope *cur_scope; + struct hlsl_scope *globals; + struct list scopes; + + struct list types; + struct rb_tree functions; + const struct hlsl_ir_function_decl *cur_function; + + enum hlsl_matrix_majority matrix_majority; + + struct + { + struct hlsl_type *scalar[HLSL_TYPE_LAST_SCALAR + 1]; + struct hlsl_type *vector[HLSL_TYPE_LAST_SCALAR + 1][4]; + struct hlsl_type *sampler[HLSL_SAMPLER_DIM_MAX + 1]; + struct hlsl_type *Void; + } builtin_types; + + struct list static_initializers; +}; + +extern struct hlsl_parse_ctx hlsl_ctx DECLSPEC_HIDDEN; + +enum hlsl_error_level +{ + HLSL_LEVEL_ERROR = 0, + HLSL_LEVEL_WARNING, + HLSL_LEVEL_NOTE, +}; + +void hlsl_message(const char *fmt, ...) VKD3D_PRINTF_FUNC(1,2) DECLSPEC_HIDDEN; +void hlsl_report_message(const struct source_location loc, + enum hlsl_error_level level, const char *fmt, ...) VKD3D_PRINTF_FUNC(3,4) DECLSPEC_HIDDEN; + +static inline struct hlsl_ir_assignment *assignment_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_ASSIGNMENT); + return CONTAINING_RECORD(node, struct hlsl_ir_assignment, node); +} + +static inline struct hlsl_ir_constant *constant_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_CONSTANT); + return CONTAINING_RECORD(node, struct hlsl_ir_constant, node); +} + +static inline struct hlsl_ir_expr *expr_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_EXPR); + return CONTAINING_RECORD(node, struct hlsl_ir_expr, node); +} + +static inline struct hlsl_ir_if *if_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_IF); + return CONTAINING_RECORD(node, struct hlsl_ir_if, node); +} + +static inline struct hlsl_ir_jump *jump_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_JUMP); + return CONTAINING_RECORD(node, struct hlsl_ir_jump, node); +} + +static inline struct hlsl_ir_load *load_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_LOAD); + return CONTAINING_RECORD(node, struct hlsl_ir_load, node); +} + +static inline struct hlsl_ir_loop *loop_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_LOOP); + return CONTAINING_RECORD(node, struct hlsl_ir_loop, node); +} + +static inline struct hlsl_ir_swizzle *swizzle_from_node(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_SWIZZLE); + return CONTAINING_RECORD(node, struct hlsl_ir_swizzle, node); +} + +static inline void init_node(struct hlsl_ir_node *node, enum hlsl_ir_node_type type, + struct hlsl_type *data_type, struct source_location loc) +{ + memset(node, 0, sizeof(*node)); + node->type = type; + node->data_type = data_type; + node->loc = loc; + list_init(&node->uses); +} + +static inline void hlsl_src_from_node(struct hlsl_src *src, struct hlsl_ir_node *node) +{ + src->node = node; + if (node) + list_add_tail(&node->uses, &src->entry); +} + +static inline void hlsl_src_remove(struct hlsl_src *src) +{ + if (src->node) + list_remove(&src->entry); + src->node = NULL; +} + +static inline void set_parse_status(enum parse_status *current, enum parse_status update) +{ + if (update == PARSE_ERR) + *current = PARSE_ERR; + else if (update == PARSE_WARN && *current == PARSE_SUCCESS) + *current = PARSE_WARN; +} + +struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lhs, + enum parse_assign_op assign_op, struct hlsl_ir_node *rhs) DECLSPEC_HIDDEN; +struct hlsl_ir_expr *add_expr(struct list *instrs, enum hlsl_ir_expr_op op, struct hlsl_ir_node *operands[3], + struct source_location *loc) DECLSPEC_HIDDEN; +struct hlsl_ir_node *add_implicit_conversion(struct list *instrs, struct hlsl_ir_node *node, struct hlsl_type *type, + struct source_location *loc) DECLSPEC_HIDDEN; + +struct hlsl_ir_expr *new_cast(struct hlsl_ir_node *node, struct hlsl_type *type, + struct source_location *loc) DECLSPEC_HIDDEN; +struct hlsl_ir_node *new_binary_expr(enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg1, + struct hlsl_ir_node *arg2) DECLSPEC_HIDDEN; +struct hlsl_ir_node *new_unary_expr(enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg, + struct source_location loc) DECLSPEC_HIDDEN; + +BOOL add_declaration(struct hlsl_scope *scope, struct hlsl_ir_var *decl, BOOL local_var) DECLSPEC_HIDDEN; +struct hlsl_ir_var *get_variable(struct hlsl_scope *scope, const char *name) DECLSPEC_HIDDEN; +void free_declaration(struct hlsl_ir_var *decl) DECLSPEC_HIDDEN; +struct hlsl_type *new_hlsl_type(const char *name, enum hlsl_type_class type_class, + enum hlsl_base_type base_type, unsigned dimx, unsigned dimy) DECLSPEC_HIDDEN; +struct hlsl_type *new_array_type(struct hlsl_type *basic_type, unsigned int array_size) DECLSPEC_HIDDEN; +struct hlsl_type *clone_hlsl_type(struct hlsl_type *old, unsigned int default_majority) DECLSPEC_HIDDEN; +struct hlsl_type *get_type(struct hlsl_scope *scope, const char *name, BOOL recursive) DECLSPEC_HIDDEN; +BOOL is_row_major(const struct hlsl_type *type) DECLSPEC_HIDDEN; +BOOL find_function(const char *name) DECLSPEC_HIDDEN; +unsigned int components_count_type(struct hlsl_type *type) DECLSPEC_HIDDEN; +BOOL compare_hlsl_types(const struct hlsl_type *t1, const struct hlsl_type *t2) DECLSPEC_HIDDEN; +BOOL compatible_data_types(struct hlsl_type *s1, struct hlsl_type *s2) DECLSPEC_HIDDEN; +void push_scope(struct hlsl_parse_ctx *ctx) DECLSPEC_HIDDEN; +BOOL pop_scope(struct hlsl_parse_ctx *ctx) DECLSPEC_HIDDEN; +void init_functions_tree(struct rb_tree *funcs) DECLSPEC_HIDDEN; +void add_function_decl(struct rb_tree *funcs, char *name, struct hlsl_ir_function_decl *decl, + BOOL intrinsic) DECLSPEC_HIDDEN; + +int hlsl_lexer_compile(const char *text, enum vkd3d_shader_type type, DWORD major, DWORD minor, const char *entrypoint, + struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) DECLSPEC_HIDDEN; +int hlsl_parser_compile(enum vkd3d_shader_type type, DWORD major, DWORD minor, const char *entrypoint, + struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) DECLSPEC_HIDDEN; + +const char *debug_base_type(const struct hlsl_type *type) DECLSPEC_HIDDEN; +const char *debug_hlsl_type(const struct hlsl_type *type) DECLSPEC_HIDDEN; +const char *debug_modifiers(DWORD modifiers) DECLSPEC_HIDDEN; +const char *debug_node_type(enum hlsl_ir_node_type type) DECLSPEC_HIDDEN; +void debug_dump_ir_function_decl(const struct hlsl_ir_function_decl *func) DECLSPEC_HIDDEN; + +void free_hlsl_type(struct hlsl_type *type) DECLSPEC_HIDDEN; +void free_instr(struct hlsl_ir_node *node) DECLSPEC_HIDDEN; +void free_instr_list(struct list *list) DECLSPEC_HIDDEN; +void free_function_rb(struct rb_entry *entry, void *context) DECLSPEC_HIDDEN; + +#endif diff --git a/libs/vkd3d-shader/hlsl.l b/libs/vkd3d-shader/hlsl.l new file mode 100644 index 00000000..0bb1ee88 --- /dev/null +++ b/libs/vkd3d-shader/hlsl.l @@ -0,0 +1,288 @@ +/* + * HLSL parser + * + * Copyright 2008 Stefan Dösinger + * Copyright 2012 Matteo Bruni for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +%{ +#define YY_NO_UNISTD_H +#include "hlsl.h" +#include "hlsl.tab.h" + +#define YY_USER_ACTION \ + do { \ + hlsl_lloc.first_column = hlsl_ctx.column; \ + hlsl_lloc.first_line = hlsl_ctx.line_no; \ + hlsl_ctx.column += yyleng; \ + } while(0); + +%} + +%option prefix="hlsl_" +%option never-interactive +%option noinput +%option nounput +%option noyywrap + +%x pp pp_line pp_pragma pp_ignore + +RESERVED1 auto|case|catch|char|class|const_cast|default|delete|dynamic_cast|enum +RESERVED2 explicit|friend|goto|long|mutable|new|operator|private|protected|public +RESERVED3 reinterpret_cast|short|signed|sizeof|static_cast|template|this|throw|try +RESERVED4 typename|union|unsigned|using|virtual + +WS [ \t] +NEWLINE (\n)|(\r\n) +DOUBLESLASHCOMMENT "//"[^\n]* +STRING "[^"]*" +IDENTIFIER [A-Za-z_][A-Za-z0-9_]* + +ANY (.) + +%% +{RESERVED1} { + hlsl_message("Line %u: Reserved keyword "%s" used.\n", hlsl_ctx.line_no, yytext); + set_parse_status(&hlsl_ctx.status, PARSE_ERR); + } +{RESERVED2} { + hlsl_message("Line %u: Reserved keyword "%s" used.\n", hlsl_ctx.line_no, yytext); + set_parse_status(&hlsl_ctx.status, PARSE_ERR); + } +{RESERVED3} { + hlsl_message("Line %u: Reserved keyword "%s" used.\n", hlsl_ctx.line_no, yytext); + set_parse_status(&hlsl_ctx.status, PARSE_ERR); + } +{RESERVED4} { + hlsl_message("Line %u: Reserved keyword "%s" used.\n", hlsl_ctx.line_no, yytext); + set_parse_status(&hlsl_ctx.status, PARSE_ERR); + } + +BlendState {return KW_BLENDSTATE; } +break {return KW_BREAK; } +Buffer {return KW_BUFFER; } +cbuffer {return KW_CBUFFER; } +compile {return KW_COMPILE; } +const {return KW_CONST; } +continue {return KW_CONTINUE; } +DepthStencilState {return KW_DEPTHSTENCILSTATE; } +DepthStencilView {return KW_DEPTHSTENCILVIEW; } +discard {return KW_DISCARD; } +do {return KW_DO; } +double {return KW_DOUBLE; } +else {return KW_ELSE; } +extern {return KW_EXTERN; } +false {return KW_FALSE; } +for {return KW_FOR; } +GeometryShader {return KW_GEOMETRYSHADER; } +groupshared {return KW_GROUPSHARED; } +if {return KW_IF; } +in {return KW_IN; } +inline {return KW_INLINE; } +inout {return KW_INOUT; } +matrix {return KW_MATRIX; } +namespace {return KW_NAMESPACE; } +nointerpolation {return KW_NOINTERPOLATION; } +out {return KW_OUT; } +pass {return KW_PASS; } +PixelShader {return KW_PIXELSHADER; } +precise {return KW_PRECISE; } +RasterizerState {return KW_RASTERIZERSTATE; } +RenderTargetView {return KW_RENDERTARGETVIEW; } +return {return KW_RETURN; } +register {return KW_REGISTER; } +sampler {return KW_SAMPLER; } +sampler1D {return KW_SAMPLER1D; } +sampler2D {return KW_SAMPLER2D; } +sampler3D {return KW_SAMPLER3D; } +samplerCUBE {return KW_SAMPLERCUBE; } +sampler_state {return KW_SAMPLER_STATE; } +SamplerComparisonState {return KW_SAMPLERCOMPARISONSTATE;} +shared {return KW_SHARED; } +stateblock {return KW_STATEBLOCK; } +stateblock_state {return KW_STATEBLOCK_STATE; } +static {return KW_STATIC; } +string {return KW_STRING; } +struct {return KW_STRUCT; } +switch {return KW_SWITCH; } +tbuffer {return KW_TBUFFER; } +technique {return KW_TECHNIQUE; } +technique10 {return KW_TECHNIQUE10; } +texture {return KW_TEXTURE; } +texture1D {return KW_TEXTURE1D; } +Texture1DArray {return KW_TEXTURE1DARRAY; } +texture2D {return KW_TEXTURE2D; } +Texture2DArray {return KW_TEXTURE2DARRAY; } +Texture2DMS {return KW_TEXTURE2DMS; } +Texture2DMSArray {return KW_TEXTURE2DMSARRAY; } +texture3D {return KW_TEXTURE3D; } +Texture3DArray {return KW_TEXTURE3DARRAY; } +textureCUBE {return KW_TEXTURECUBE; } +true {return KW_TRUE; } +typedef {return KW_TYPEDEF; } +uniform {return KW_UNIFORM; } +vector {return KW_VECTOR; } +VertexShader {return KW_VERTEXSHADER; } +void {return KW_VOID; } +volatile {return KW_VOLATILE; } +while {return KW_WHILE; } + +++ {return OP_INC; } +-- {return OP_DEC; } +&& {return OP_AND; } +|| {return OP_OR; } +== {return OP_EQ; } +<< {return OP_LEFTSHIFT; } +<<= {return OP_LEFTSHIFTASSIGN; } +>> {return OP_RIGHTSHIFT; } +>>= {return OP_RIGHTSHIFTASSIGN; } +... {return OP_ELLIPSIS; } +<= {return OP_LE; } +>= {return OP_GE; } +!= {return OP_NE; } ++= {return OP_ADDASSIGN; } +-= {return OP_SUBASSIGN; } +*= {return OP_MULASSIGN; } +/= {return OP_DIVASSIGN; } +%= {return OP_MODASSIGN; } +&= {return OP_ANDASSIGN; } +|= {return OP_ORASSIGN; } +^= {return OP_XORASSIGN; } +## {return OP_UNKNOWN1; } +#@ {return OP_UNKNOWN2; } +:: {return OP_UNKNOWN3; } +-> {return OP_UNKNOWN4; } + +column_major {return KW_COLUMN_MAJOR; } +row_major {return KW_ROW_MAJOR; } + +{IDENTIFIER} { + hlsl_lval.name = vkd3d_strdup(yytext); + if (get_variable(hlsl_ctx.cur_scope, yytext) + || find_function(yytext)) + return VAR_IDENTIFIER; + else if (get_type(hlsl_ctx.cur_scope, yytext, TRUE)) + return TYPE_IDENTIFIER; + else + return NEW_IDENTIFIER; + } + +[0-9]*.[0-9]+([eE][+-]?[0-9]+)?[h|H|f|F]? { + hlsl_lval.floatval = atof(yytext); + return C_FLOAT; + } +[0-9]+.([eE][+-]?[0-9]+)?[h|H|f|F]? { + hlsl_lval.floatval = atof(yytext); + return C_FLOAT; + } +[0-9]+([eE][+-]?[0-9]+)?[h|H|f|F] { + hlsl_lval.floatval = atof(yytext); + return C_FLOAT; + } +0x[0-9a-fA-F]+ { + sscanf(yytext, "0x%x", &hlsl_lval.intval); + return C_INTEGER; + } +0[0-7]+ { + sscanf(yytext, "0%o", &hlsl_lval.intval); + return C_INTEGER; + } +[0-9]+ { + hlsl_lval.intval = (atoi(yytext)); + return C_INTEGER; + } + +{DOUBLESLASHCOMMENT} {} + +{WS}+ {} +{NEWLINE} { + hlsl_ctx.line_no++; + hlsl_ctx.column = 1; + } + +^# { + BEGIN(pp); + } + +<pp>pragma{WS}+ { + TRACE("Got a #pragma.\n"); + BEGIN(pp_pragma); + } +<pp_pragma>pack_matrix{WS}*({WS}*row_major{WS}*) { + TRACE("#pragma setting row_major mode.\n"); + hlsl_ctx.matrix_majority = HLSL_ROW_MAJOR; + BEGIN(pp_ignore); + } +<pp_pragma>pack_matrix{WS}*({WS}*column_major{WS}*) { + TRACE("#pragma setting column_major mode.\n"); + hlsl_ctx.matrix_majority = HLSL_COLUMN_MAJOR; + BEGIN(pp_ignore); + } +<pp_pragma>{NEWLINE} { + FIXME("Unsupported preprocessor #pragma directive at line %u.\n", hlsl_ctx.line_no); + BEGIN(INITIAL); + } +<pp_pragma>{ANY} {} +<pp>[0-9]+ { + TRACE("Preprocessor line info.\n"); + BEGIN(pp_line); + hlsl_lval.intval = (atoi(yytext)); + return PRE_LINE; + } +<pp_line>{STRING} { + char *string = vkd3d_strdup(yytext + 1); + + BEGIN(pp_ignore); + string[strlen(string) - 1] = 0; + hlsl_lval.name = string; + return STRING; + } +<pp_line>{WS}+ {} +<pp_line>{NEWLINE} { + FIXME("Malformed preprocessor line directive?\n"); + BEGIN(INITIAL); + } +<pp_ignore>{NEWLINE} { + BEGIN(INITIAL); + } +<pp_ignore>{ANY} {} +<pp>{NEWLINE} { + FIXME("Unexpected preprocessor directive.\n"); + BEGIN(INITIAL); + } +<pp>{ANY} {} + +{ANY} { + return yytext[0]; + } + +%% + +int hlsl_lexer_compile(const char *text, enum vkd3d_shader_type type, DWORD major, DWORD minor, const char *entrypoint, + struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) +{ + YY_BUFFER_STATE buffer; + int ret; + + buffer = hlsl__scan_string(text); + hlsl__switch_to_buffer(buffer); + + ret = hlsl_parser_compile(type, major, minor, entrypoint, dxbc, message_context); + + hlsl__delete_buffer(buffer); + return ret; +} diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y new file mode 100644 index 00000000..4e2218c5 --- /dev/null +++ b/libs/vkd3d-shader/hlsl.y @@ -0,0 +1,2961 @@ +/* + * HLSL parser + * + * Copyright 2008 Stefan Dösinger + * Copyright 2012 Matteo Bruni for CodeWeavers + * Copyright 2019-2020 Zebediah Figura for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +%{ +#include "hlsl.h" +#include <stdio.h> + +int hlsl_lex(void); + +struct hlsl_parse_ctx hlsl_ctx; + +struct YYLTYPE; +static struct source_location get_location(const struct YYLTYPE *l); + +void hlsl_message(const char *fmt, ...) +{ + /* FIXME */ +} + +void hlsl_report_message(const struct source_location loc, + enum hlsl_error_level level, const char *fmt, ...) +{ + /* FIXME */ + + if (level == HLSL_LEVEL_ERROR) + set_parse_status(&hlsl_ctx.status, PARSE_ERR); + else if (level == HLSL_LEVEL_WARNING) + set_parse_status(&hlsl_ctx.status, PARSE_WARN); +} + +static void hlsl_error(const char *s) +{ + const struct source_location loc = + { + .file = hlsl_ctx.source_file, + .line = hlsl_ctx.line_no, + .col = hlsl_ctx.column, + }; + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "%s", s); +} + +static struct hlsl_ir_node *node_from_list(struct list *list) +{ + return LIST_ENTRY(list_tail(list), struct hlsl_ir_node, entry); +} + +static void debug_dump_decl(struct hlsl_type *type, DWORD modifiers, const char *declname, unsigned int line_no) +{ + TRACE("Line %u: ", line_no); + if (modifiers) + TRACE("%s ", debug_modifiers(modifiers)); + TRACE("%s %s;\n", debug_hlsl_type(type), declname); +} + +static void check_invalid_matrix_modifiers(DWORD modifiers, struct source_location loc) +{ + if (modifiers & HLSL_MODIFIERS_MAJORITY_MASK) + hlsl_report_message(loc, HLSL_LEVEL_ERROR, + "'row_major' or 'column_major' modifiers are only allowed for matrices."); +} + +static BOOL type_is_single_reg(const struct hlsl_type *type) +{ + return type->type == HLSL_CLASS_SCALAR || type->type == HLSL_CLASS_VECTOR; +} + +static BOOL declare_variable(struct hlsl_ir_var *decl, BOOL local) +{ + BOOL ret; + + TRACE("Declaring variable %s.\n", decl->name); + if (decl->data_type->type != HLSL_CLASS_MATRIX) + check_invalid_matrix_modifiers(decl->modifiers, decl->loc); + + if (local) + { + DWORD invalid = decl->modifiers & (HLSL_STORAGE_EXTERN | HLSL_STORAGE_SHARED + | HLSL_STORAGE_GROUPSHARED | HLSL_STORAGE_UNIFORM); + + if (invalid) + { + hlsl_report_message(decl->loc, HLSL_LEVEL_ERROR, + "modifier '%s' invalid for local variables", debug_modifiers(invalid)); + } + if (decl->semantic) + { + hlsl_report_message(decl->loc, HLSL_LEVEL_ERROR, + "semantics are not allowed on local variables"); + return FALSE; + } + } + else + { + if (find_function(decl->name)) + { + hlsl_report_message(decl->loc, HLSL_LEVEL_ERROR, "redefinition of '%s'", decl->name); + return FALSE; + } + } + ret = add_declaration(hlsl_ctx.cur_scope, decl, local); + if (!ret) + { + struct hlsl_ir_var *old = get_variable(hlsl_ctx.cur_scope, decl->name); + + hlsl_report_message(decl->loc, HLSL_LEVEL_ERROR, ""%s" already declared", decl->name); + hlsl_report_message(old->loc, HLSL_LEVEL_NOTE, ""%s" was previously declared here", old->name); + return FALSE; + } + return TRUE; +} + +static DWORD add_modifiers(DWORD modifiers, DWORD mod, const struct source_location loc) +{ + if (modifiers & mod) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "modifier '%s' already specified", debug_modifiers(mod)); + return modifiers; + } + if ((mod & HLSL_MODIFIERS_MAJORITY_MASK) && (modifiers & HLSL_MODIFIERS_MAJORITY_MASK)) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "more than one matrix majority keyword"); + return modifiers; + } + return modifiers | mod; +} + +static BOOL add_type_to_scope(struct hlsl_scope *scope, struct hlsl_type *def) +{ + if (get_type(scope, def->name, FALSE)) + return FALSE; + + rb_put(&scope->types, def->name, &def->scope_entry); + return TRUE; +} + +static void declare_predefined_types(struct hlsl_scope *scope) +{ + struct hlsl_type *type; + unsigned int x, y, bt; + static const char * const names[] = + { + "float", + "half", + "double", + "int", + "uint", + "bool", + }; + char name[10]; + + static const char *const sampler_names[] = + { + "sampler", + "sampler1D", + "sampler2D", + "sampler3D", + "samplerCUBE" + }; + + for (bt = 0; bt <= HLSL_TYPE_LAST_SCALAR; ++bt) + { + for (y = 1; y <= 4; ++y) + { + for (x = 1; x <= 4; ++x) + { + sprintf(name, "%s%ux%u", names[bt], y, x); + type = new_hlsl_type(vkd3d_strdup(name), HLSL_CLASS_MATRIX, bt, x, y); + add_type_to_scope(scope, type); + + if (y == 1) + { + sprintf(name, "%s%u", names[bt], x); + type = new_hlsl_type(vkd3d_strdup(name), HLSL_CLASS_VECTOR, bt, x, y); + add_type_to_scope(scope, type); + hlsl_ctx.builtin_types.vector[bt][x - 1] = type; + + if (x == 1) + { + sprintf(name, "%s", names[bt]); + type = new_hlsl_type(vkd3d_strdup(name), HLSL_CLASS_SCALAR, bt, x, y); + add_type_to_scope(scope, type); + hlsl_ctx.builtin_types.scalar[bt] = type; + } + } + } + } + } + + for (bt = 0; bt <= HLSL_SAMPLER_DIM_MAX; ++bt) + { + type = new_hlsl_type(vkd3d_strdup(sampler_names[bt]), HLSL_CLASS_OBJECT, HLSL_TYPE_SAMPLER, 1, 1); + type->sampler_dim = bt; + hlsl_ctx.builtin_types.sampler[bt] = type; + } + + hlsl_ctx.builtin_types.Void = new_hlsl_type(vkd3d_strdup("void"), HLSL_CLASS_OBJECT, HLSL_TYPE_VOID, 1, 1); + + /* DX8 effects predefined types */ + type = new_hlsl_type(vkd3d_strdup("DWORD"), HLSL_CLASS_SCALAR, HLSL_TYPE_INT, 1, 1); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("FLOAT"), HLSL_CLASS_SCALAR, HLSL_TYPE_FLOAT, 1, 1); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("VECTOR"), HLSL_CLASS_VECTOR, HLSL_TYPE_FLOAT, 4, 1); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("MATRIX"), HLSL_CLASS_MATRIX, HLSL_TYPE_FLOAT, 4, 4); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("STRING"), HLSL_CLASS_OBJECT, HLSL_TYPE_STRING, 1, 1); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("TEXTURE"), HLSL_CLASS_OBJECT, HLSL_TYPE_TEXTURE, 1, 1); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("PIXELSHADER"), HLSL_CLASS_OBJECT, HLSL_TYPE_PIXELSHADER, 1, 1); + add_type_to_scope(scope, type); + type = new_hlsl_type(vkd3d_strdup("VERTEXSHADER"), HLSL_CLASS_OBJECT, HLSL_TYPE_VERTEXSHADER, 1, 1); + add_type_to_scope(scope, type); +} + +static BOOL type_is_void(const struct hlsl_type *type) +{ + return type->type == HLSL_CLASS_OBJECT && type->base_type == HLSL_TYPE_VOID; +} + +static struct hlsl_ir_if *new_if(struct hlsl_ir_node *condition, struct source_location loc) +{ + struct hlsl_ir_if *iff; + + if (!(iff = vkd3d_malloc(sizeof(*iff)))) + return NULL; + init_node(&iff->node, HLSL_IR_IF, NULL, loc); + hlsl_src_from_node(&iff->condition, condition); + list_init(&iff->then_instrs); + list_init(&iff->else_instrs); + return iff; +} + +static BOOL append_conditional_break(struct list *cond_list) +{ + struct hlsl_ir_node *condition, *not; + struct hlsl_ir_jump *jump; + struct hlsl_ir_if *iff; + + /* E.g. "for (i = 0; ; ++i)". */ + if (!list_count(cond_list)) + return TRUE; + + condition = node_from_list(cond_list); + if (!(not = new_unary_expr(HLSL_IR_UNOP_LOGIC_NOT, condition, condition->loc))) + return FALSE; + list_add_tail(cond_list, ¬->entry); + + if (!(iff = new_if(not, condition->loc))) + return FALSE; + list_add_tail(cond_list, &iff->node.entry); + + if (!(jump = vkd3d_malloc(sizeof(*jump)))) + return FALSE; + init_node(&jump->node, HLSL_IR_JUMP, NULL, condition->loc); + jump->type = HLSL_IR_JUMP_BREAK; + list_add_head(&iff->then_instrs, &jump->node.entry); + return TRUE; +} + +enum loop_type +{ + LOOP_FOR, + LOOP_WHILE, + LOOP_DO_WHILE +}; + +static struct list *create_loop(enum loop_type type, struct list *init, struct list *cond, + struct list *iter, struct list *body, struct source_location loc) +{ + struct list *list = NULL; + struct hlsl_ir_loop *loop = NULL; + struct hlsl_ir_if *cond_jump = NULL; + + if (!(list = vkd3d_malloc(sizeof(*list)))) + goto oom; + list_init(list); + + if (init) + list_move_head(list, init); + + if (!(loop = vkd3d_calloc(1, sizeof(*loop)))) + goto oom; + init_node(&loop->node, HLSL_IR_LOOP, NULL, loc); + list_add_tail(list, &loop->node.entry); + list_init(&loop->body); + + if (!append_conditional_break(cond)) + goto oom; + + if (type != LOOP_DO_WHILE) + list_move_tail(&loop->body, cond); + + list_move_tail(&loop->body, body); + + if (iter) + list_move_tail(&loop->body, iter); + + if (type == LOOP_DO_WHILE) + list_move_tail(&loop->body, cond); + + vkd3d_free(init); + vkd3d_free(cond); + vkd3d_free(body); + return list; + +oom: + vkd3d_free(loop); + vkd3d_free(cond_jump); + vkd3d_free(list); + free_instr_list(init); + free_instr_list(cond); + free_instr_list(iter); + free_instr_list(body); + return NULL; +} + +static unsigned int initializer_size(const struct parse_initializer *initializer) +{ + unsigned int count = 0, i; + + for (i = 0; i < initializer->args_count; ++i) + { + count += components_count_type(initializer->args[i]->data_type); + } + TRACE("Initializer size = %u.\n", count); + return count; +} + +static void free_parse_initializer(struct parse_initializer *initializer) +{ + free_instr_list(initializer->instrs); + vkd3d_free(initializer->args); +} + +static struct hlsl_ir_swizzle *new_swizzle(DWORD s, unsigned int components, + struct hlsl_ir_node *val, struct source_location *loc) +{ + struct hlsl_ir_swizzle *swizzle; + + if (!(swizzle = vkd3d_malloc(sizeof(*swizzle)))) + return NULL; + init_node(&swizzle->node, HLSL_IR_SWIZZLE, + new_hlsl_type(NULL, HLSL_CLASS_VECTOR, val->data_type->base_type, components, 1), *loc); + hlsl_src_from_node(&swizzle->val, val); + swizzle->swizzle = s; + return swizzle; +} + +static struct hlsl_ir_swizzle *get_swizzle(struct hlsl_ir_node *value, const char *swizzle, + struct source_location *loc) +{ + unsigned int len = strlen(swizzle), component = 0; + unsigned int i, set, swiz = 0; + BOOL valid; + + if (value->data_type->type == HLSL_CLASS_MATRIX) + { + /* Matrix swizzle */ + BOOL m_swizzle; + unsigned int inc, x, y; + + if (len < 3 || swizzle[0] != '_') + return NULL; + m_swizzle = swizzle[1] == 'm'; + inc = m_swizzle ? 4 : 3; + + if (len % inc || len > inc * 4) + return NULL; + + for (i = 0; i < len; i += inc) + { + if (swizzle[i] != '_') + return NULL; + if (m_swizzle) + { + if (swizzle[i + 1] != 'm') + return NULL; + y = swizzle[i + 2] - '0'; + x = swizzle[i + 3] - '0'; + } + else + { + y = swizzle[i + 1] - '1'; + x = swizzle[i + 2] - '1'; + } + + if (x >= value->data_type->dimx || y >= value->data_type->dimy) + return NULL; + swiz |= (y << 4 | x) << component * 8; + component++; + } + return new_swizzle(swiz, component, value, loc); + } + + /* Vector swizzle */ + if (len > 4) + return NULL; + + for (set = 0; set < 2; ++set) + { + valid = TRUE; + component = 0; + for (i = 0; i < len; ++i) + { + char c[2][4] = {{'x', 'y', 'z', 'w'}, {'r', 'g', 'b', 'a'}}; + unsigned int s = 0; + + for (s = 0; s < 4; ++s) + { + if (swizzle[i] == c[set][s]) + break; + } + if (s == 4) + { + valid = FALSE; + break; + } + + if (s >= value->data_type->dimx) + return NULL; + swiz |= s << component * 2; + component++; + } + if (valid) + return new_swizzle(swiz, component, value, loc); + } + + return NULL; +} + +static struct hlsl_ir_var *new_var(const char *name, struct hlsl_type *type, const struct source_location loc, + const char *semantic, unsigned int modifiers, const struct reg_reservation *reg_reservation) +{ + struct hlsl_ir_var *var; + + if (!(var = vkd3d_calloc(1, sizeof(*var)))) + { + hlsl_ctx.status = PARSE_ERR; + return NULL; + } + + var->name = name; + var->data_type = type; + var->loc = loc; + var->semantic = semantic; + var->modifiers = modifiers; + var->reg_reservation = reg_reservation; + return var; +} + +static struct hlsl_ir_var *new_synthetic_var(const char *name, struct hlsl_type *type, + const struct source_location loc) +{ + struct hlsl_ir_var *var = new_var(vkd3d_strdup(name), type, loc, NULL, 0, NULL); + + if (var) + list_add_tail(&hlsl_ctx.globals->vars, &var->scope_entry); + return var; +} + +static struct hlsl_ir_assignment *new_assignment(struct hlsl_ir_var *var, struct hlsl_ir_node *offset, + struct hlsl_ir_node *rhs, unsigned int writemask, struct source_location loc) +{ + struct hlsl_ir_assignment *assign; + + if (!writemask && type_is_single_reg(rhs->data_type)) + writemask = (1 << rhs->data_type->dimx) - 1; + + if (!(assign = vkd3d_malloc(sizeof(*assign)))) + return NULL; + + init_node(&assign->node, HLSL_IR_ASSIGNMENT, NULL, loc); + assign->lhs.var = var; + hlsl_src_from_node(&assign->lhs.offset, offset); + hlsl_src_from_node(&assign->rhs, rhs); + assign->writemask = writemask; + return assign; +} + +static struct hlsl_ir_assignment *new_simple_assignment(struct hlsl_ir_var *lhs, struct hlsl_ir_node *rhs) +{ + return new_assignment(lhs, NULL, rhs, 0, rhs->loc); +} + +static struct hlsl_ir_jump *add_return(struct list *instrs, + struct hlsl_ir_node *return_value, struct source_location loc) +{ + struct hlsl_type *return_type = hlsl_ctx.cur_function->return_type; + struct hlsl_ir_jump *jump; + + if (return_value) + { + struct hlsl_ir_assignment *assignment; + + if (!(return_value = add_implicit_conversion(instrs, return_value, return_type, &loc))) + return NULL; + + if (!(assignment = new_simple_assignment(hlsl_ctx.cur_function->return_var, return_value))) + return NULL; + list_add_after(&return_value->entry, &assignment->node.entry); + } + else if (!type_is_void(return_type)) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "non-void function must return a value"); + return NULL; + } + + if (!(jump = vkd3d_malloc(sizeof(*jump)))) + return NULL; + init_node(&jump->node, HLSL_IR_JUMP, NULL, loc); + jump->type = HLSL_IR_JUMP_RETURN; + list_add_tail(instrs, &jump->node.entry); + + return jump; +} + +static struct hlsl_ir_constant *new_uint_constant(unsigned int n, const struct source_location loc) +{ + struct hlsl_ir_constant *c; + + if (!(c = vkd3d_malloc(sizeof(*c)))) + return NULL; + init_node(&c->node, HLSL_IR_CONSTANT, hlsl_ctx.builtin_types.scalar[HLSL_TYPE_UINT], loc); + c->value.u[0] = n; + return c; +} + +struct hlsl_ir_node *new_unary_expr(enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg, struct source_location loc) +{ + struct hlsl_ir_expr *expr; + + if (!(expr = vkd3d_calloc(1, sizeof(*expr)))) + return NULL; + init_node(&expr->node, HLSL_IR_EXPR, arg->data_type, loc); + expr->op = op; + hlsl_src_from_node(&expr->operands[0], arg); + return &expr->node; +} + +struct hlsl_ir_node *new_binary_expr(enum hlsl_ir_expr_op op, + struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2) +{ + struct hlsl_ir_expr *expr; + + assert(compare_hlsl_types(arg1->data_type, arg2->data_type)); + + if (!(expr = vkd3d_calloc(1, sizeof(*expr)))) + return NULL; + init_node(&expr->node, HLSL_IR_EXPR, arg1->data_type, arg1->loc); + expr->op = op; + hlsl_src_from_node(&expr->operands[0], arg1); + hlsl_src_from_node(&expr->operands[1], arg2); + return &expr->node; +} + +static struct hlsl_ir_load *new_var_load(struct hlsl_ir_var *var, const struct source_location loc) +{ + struct hlsl_ir_load *load; + + if (!(load = vkd3d_calloc(1, sizeof(*load)))) + return NULL; + init_node(&load->node, HLSL_IR_LOAD, var->data_type, loc); + load->src.var = var; + return load; +} + +static struct hlsl_ir_load *add_load(struct list *instrs, struct hlsl_ir_node *var_node, struct hlsl_ir_node *offset, + struct hlsl_type *data_type, const struct source_location loc) +{ + struct hlsl_ir_node *add = NULL; + struct hlsl_ir_load *load; + struct hlsl_ir_var *var; + + if (var_node->type == HLSL_IR_LOAD) + { + const struct hlsl_deref *src = &load_from_node(var_node)->src; + + var = src->var; + if (src->offset.node) + { + if (!(add = new_binary_expr(HLSL_IR_BINOP_ADD, src->offset.node, offset))) + return NULL; + list_add_tail(instrs, &add->entry); + offset = add; + } + } + else + { + struct hlsl_ir_assignment *assign; + char name[27]; + + sprintf(name, "<deref-%p>", var_node); + if (!(var = new_synthetic_var(name, var_node->data_type, var_node->loc))) + return NULL; + + TRACE("Synthesized variable %p for %s node.\n", var, debug_node_type(var_node->type)); + + if (!(assign = new_simple_assignment(var, var_node))) + return NULL; + + list_add_tail(instrs, &assign->node.entry); + } + + if (!(load = vkd3d_malloc(sizeof(*load)))) + return NULL; + init_node(&load->node, HLSL_IR_LOAD, data_type, loc); + load->src.var = var; + hlsl_src_from_node(&load->src.offset, offset); + list_add_tail(instrs, &load->node.entry); + return load; +} + +static struct hlsl_ir_load *add_record_load(struct list *instrs, struct hlsl_ir_node *record, + const struct hlsl_struct_field *field, const struct source_location loc) +{ + struct hlsl_ir_constant *c; + + if (!(c = new_uint_constant(field->reg_offset * 4, loc))) + return NULL; + list_add_tail(instrs, &c->node.entry); + + return add_load(instrs, record, &c->node, field->type, loc); +} + +static struct hlsl_ir_load *add_array_load(struct list *instrs, struct hlsl_ir_node *array, + struct hlsl_ir_node *index, const struct source_location loc) +{ + const struct hlsl_type *expr_type = array->data_type; + struct hlsl_type *data_type; + struct hlsl_ir_constant *c; + struct hlsl_ir_node *mul; + + TRACE("Array load from type %s.\n", debug_hlsl_type(expr_type)); + + if (expr_type->type == HLSL_CLASS_ARRAY) + { + data_type = expr_type->e.array.type; + } + else if (expr_type->type == HLSL_CLASS_MATRIX || expr_type->type == HLSL_CLASS_VECTOR) + { + /* This needs to be lowered now, while we still have type information. */ + FIXME("Index of matrix or vector type.\n"); + return NULL; + } + else + { + if (expr_type->type == HLSL_CLASS_SCALAR) + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "array-indexed expression is scalar"); + else + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "expression is not array-indexable"); + return NULL; + } + + if (!(c = new_uint_constant(data_type->reg_size * 4, loc))) + return NULL; + list_add_tail(instrs, &c->node.entry); + if (!(mul = new_binary_expr(HLSL_IR_BINOP_MUL, index, &c->node))) + return NULL; + list_add_tail(instrs, &mul->entry); + index = mul; + + return add_load(instrs, array, index, data_type, loc); +} + +static BOOL add_struct_field(struct list *fields, struct hlsl_struct_field *field) +{ + struct hlsl_struct_field *f; + + LIST_FOR_EACH_ENTRY(f, fields, struct hlsl_struct_field, entry) + { + if (!strcmp(f->name, field->name)) + return FALSE; + } + list_add_tail(fields, &field->entry); + return TRUE; +} + +BOOL is_row_major(const struct hlsl_type *type) +{ + /* Default to column-major if the majority isn't explicitly set, which can + * happen for anonymous nodes. */ + return !!(type->modifiers & HLSL_MODIFIER_ROW_MAJOR); +} + +static struct hlsl_type *apply_type_modifiers(struct hlsl_type *type, + unsigned int *modifiers, struct source_location loc) +{ + unsigned int default_majority = 0; + struct hlsl_type *new_type; + + /* This function is only used for declarations (i.e. variables and struct + * fields), which should inherit the matrix majority. We only explicitly set + * the default majority for declarations—typedefs depend on this—but we + * want to always set it, so that an hlsl_type object is never used to + * represent two different majorities (and thus can be used to store its + * register size, etc.) */ + if (!(*modifiers & HLSL_MODIFIERS_MAJORITY_MASK) + && !(type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK) + && type->type == HLSL_CLASS_MATRIX) + { + if (hlsl_ctx.matrix_majority == HLSL_COLUMN_MAJOR) + default_majority = HLSL_MODIFIER_COLUMN_MAJOR; + else + default_majority = HLSL_MODIFIER_ROW_MAJOR; + } + + if (!default_majority && !(*modifiers & HLSL_TYPE_MODIFIERS_MASK)) + return type; + + if (!(new_type = clone_hlsl_type(type, default_majority))) + return NULL; + + new_type->modifiers = add_modifiers(new_type->modifiers, *modifiers, loc); + *modifiers &= ~HLSL_TYPE_MODIFIERS_MASK; + + if (new_type->type == HLSL_CLASS_MATRIX) + new_type->reg_size = is_row_major(new_type) ? new_type->dimy : new_type->dimx; + return new_type; +} + +static struct list *gen_struct_fields(struct hlsl_type *type, DWORD modifiers, struct list *fields) +{ + struct parse_variable_def *v, *v_next; + struct hlsl_struct_field *field; + struct list *list; + + if (type->type == HLSL_CLASS_MATRIX) + assert(type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK); + + if (!(list = vkd3d_malloc(sizeof(*list)))) + return NULL; + list_init(list); + LIST_FOR_EACH_ENTRY_SAFE(v, v_next, fields, struct parse_variable_def, entry) + { + debug_dump_decl(type, 0, v->name, v->loc.line); + if (!(field = vkd3d_calloc(1, sizeof(*field)))) + { + vkd3d_free(v); + return list; + } + if (v->array_size) + field->type = new_array_type(type, v->array_size); + else + field->type = type; + field->name = v->name; + field->modifiers = modifiers; + field->semantic = v->semantic; + if (v->initializer.args_count) + { + hlsl_report_message(v->loc, HLSL_LEVEL_ERROR, "struct field with an initializer.\n"); + free_parse_initializer(&v->initializer); + } + list_add_tail(list, &field->entry); + vkd3d_free(v); + } + vkd3d_free(fields); + return list; +} + +static DWORD get_array_size(const struct hlsl_type *type) +{ + if (type->type == HLSL_CLASS_ARRAY) + return get_array_size(type->e.array.type) * type->e.array.elements_count; + return 1; +} + +static struct hlsl_type *new_struct_type(const char *name, struct list *fields) +{ + struct hlsl_struct_field *field; + unsigned int reg_size = 0; + struct hlsl_type *type; + + if (!(type = vkd3d_calloc(1, sizeof(*type)))) + return NULL; + type->type = HLSL_CLASS_STRUCT; + type->base_type = HLSL_TYPE_VOID; + type->name = name; + type->dimx = 0; + type->dimy = 1; + type->e.elements = fields; + + LIST_FOR_EACH_ENTRY(field, fields, struct hlsl_struct_field, entry) + { + field->reg_offset = reg_size; + reg_size += field->type->reg_size; + type->dimx += field->type->dimx * field->type->dimy * get_array_size(field->type); + } + type->reg_size = reg_size; + + list_add_tail(&hlsl_ctx.types, &type->entry); + + return type; +} + +static BOOL add_typedef(DWORD modifiers, struct hlsl_type *orig_type, struct list *list) +{ + struct parse_variable_def *v, *v_next; + struct hlsl_type *type; + BOOL ret; + + LIST_FOR_EACH_ENTRY_SAFE(v, v_next, list, struct parse_variable_def, entry) + { + if (v->array_size) + type = new_array_type(orig_type, v->array_size); + else + type = clone_hlsl_type(orig_type, 0); + if (!type) + return FALSE; + vkd3d_free((void *)type->name); + type->name = v->name; + type->modifiers |= modifiers; + + if (type->type != HLSL_CLASS_MATRIX) + check_invalid_matrix_modifiers(type->modifiers, v->loc); + else + type->reg_size = is_row_major(type) ? type->dimy : type->dimx; + + if ((type->modifiers & HLSL_MODIFIER_COLUMN_MAJOR) + && (type->modifiers & HLSL_MODIFIER_ROW_MAJOR)) + hlsl_report_message(v->loc, HLSL_LEVEL_ERROR, "more than one matrix majority keyword"); + + ret = add_type_to_scope(hlsl_ctx.cur_scope, type); + if (!ret) + hlsl_report_message(v->loc, HLSL_LEVEL_ERROR, + "redefinition of custom type '%s'", v->name); + vkd3d_free(v); + } + vkd3d_free(list); + return TRUE; +} + +static BOOL add_func_parameter(struct list *list, struct parse_parameter *param, const struct source_location loc) +{ + struct hlsl_ir_var *var; + + if (param->type->type == HLSL_CLASS_MATRIX) + assert(param->type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK); + + if (!(var = new_var(param->name, param->type, loc, param->semantic, param->modifiers, param->reg_reservation))) + return FALSE; + + if (!add_declaration(hlsl_ctx.cur_scope, var, FALSE)) + { + free_declaration(var); + return FALSE; + } + list_add_tail(list, &var->param_entry); + return TRUE; +} + +static struct reg_reservation *parse_reg_reservation(const char *reg_string) +{ + enum vkd3d_shader_register_type type; + struct reg_reservation *reg_res; + DWORD regnum = 0; + + switch (reg_string[0]) + { + case 'c': + type = VKD3DSPR_CONST; + break; + case 'i': + type = VKD3DSPR_CONSTINT; + break; + case 'b': + type = VKD3DSPR_CONSTBOOL; + break; + case 's': + type = VKD3DSPR_SAMPLER; + break; + default: + FIXME("Unsupported register type.\n"); + return NULL; + } + + if (!sscanf(reg_string + 1, "%u", ®num)) + { + FIXME("Unsupported register reservation syntax.\n"); + return NULL; + } + + if (!(reg_res = vkd3d_malloc(sizeof(*reg_res)))) + return NULL; + reg_res->type = type; + reg_res->regnum = regnum; + return reg_res; +} + +static const struct hlsl_ir_function_decl *get_overloaded_func(struct rb_tree *funcs, char *name, + struct list *params, BOOL exact_signature) +{ + struct hlsl_ir_function *func; + struct rb_entry *entry; + + entry = rb_get(funcs, name); + if (entry) + { + func = RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry); + + entry = rb_get(&func->overloads, params); + if (!entry) + { + if (!exact_signature) + FIXME("No exact match, search for a compatible overloaded function (if any).\n"); + return NULL; + } + return RB_ENTRY_VALUE(entry, struct hlsl_ir_function_decl, entry); + } + return NULL; +} + +static struct hlsl_ir_function_decl *get_func_entry(const char *name) +{ + struct hlsl_ir_function_decl *decl; + struct hlsl_ir_function *func; + struct rb_entry *entry; + + if ((entry = rb_get(&hlsl_ctx.functions, name))) + { + func = RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry); + RB_FOR_EACH_ENTRY(decl, &func->overloads, struct hlsl_ir_function_decl, entry) + return decl; + } + + return NULL; +} + +static struct list *make_list(struct hlsl_ir_node *node) +{ + struct list *list; + + if (!(list = vkd3d_malloc(sizeof(*list)))) + { + free_instr(node); + return NULL; + } + list_init(list); + list_add_tail(list, &node->entry); + return list; +} + +static unsigned int evaluate_array_dimension(struct hlsl_ir_node *node) +{ + if (node->data_type->type != HLSL_CLASS_SCALAR) + return 0; + + switch (node->type) + { + case HLSL_IR_CONSTANT: + { + struct hlsl_ir_constant *constant = constant_from_node(node); + + switch (constant->node.data_type->base_type) + { + case HLSL_TYPE_UINT: + return constant->value.u[0]; + case HLSL_TYPE_INT: + return constant->value.i[0]; + case HLSL_TYPE_FLOAT: + return constant->value.f[0]; + case HLSL_TYPE_DOUBLE: + return constant->value.d[0]; + case HLSL_TYPE_BOOL: + return constant->value.b[0]; + default: + WARN("Invalid type %s.\n", debug_base_type(constant->node.data_type)); + return 0; + } + } + + case HLSL_IR_EXPR: + case HLSL_IR_LOAD: + case HLSL_IR_SWIZZLE: + FIXME("Unhandled type %s.\n", debug_node_type(node->type)); + return 0; + + case HLSL_IR_ASSIGNMENT: + default: + WARN("Invalid node type %s.\n", debug_node_type(node->type)); + return 0; + } +} + +static struct list *append_unop(struct list *list, struct hlsl_ir_node *node) +{ + list_add_tail(list, &node->entry); + return list; +} + +static struct list *add_binary_expr(struct list *list1, struct list *list2, + enum hlsl_ir_expr_op op, struct source_location loc) +{ + struct hlsl_ir_node *args[3] = {node_from_list(list1), node_from_list(list2)}; + + list_move_tail(list1, list2); + vkd3d_free(list2); + add_expr(list1, op, args, &loc); + return list1; +} + +static void struct_var_initializer(struct list *list, struct hlsl_ir_var *var, + struct parse_initializer *initializer) +{ + struct hlsl_type *type = var->data_type; + struct hlsl_struct_field *field; + unsigned int i = 0; + + if (initializer_size(initializer) != components_count_type(type)) + { + hlsl_report_message(var->loc, HLSL_LEVEL_ERROR, "structure initializer mismatch"); + free_parse_initializer(initializer); + return; + } + + list_move_tail(list, initializer->instrs); + vkd3d_free(initializer->instrs); + + LIST_FOR_EACH_ENTRY(field, type->e.elements, struct hlsl_struct_field, entry) + { + struct hlsl_ir_node *node = initializer->args[i]; + struct hlsl_ir_assignment *assign; + struct hlsl_ir_constant *c; + + if (i++ >= initializer->args_count) + break; + + if (components_count_type(field->type) == components_count_type(node->data_type)) + { + if (!(c = new_uint_constant(field->reg_offset * 4, node->loc))) + break; + list_add_tail(list, &c->node.entry); + + if (!(assign = new_assignment(var, &c->node, node, 0, node->loc))) + break; + list_add_tail(list, &assign->node.entry); + } + else + FIXME("Initializing with "mismatched" fields is not supported yet.\n"); + } + + vkd3d_free(initializer->args); +} + +static void free_parse_variable_def(struct parse_variable_def *v) +{ + free_parse_initializer(&v->initializer); + vkd3d_free(v->name); + vkd3d_free((void *)v->semantic); + vkd3d_free(v->reg_reservation); + vkd3d_free(v); +} + +static struct list *declare_vars(struct hlsl_type *basic_type, DWORD modifiers, struct list *var_list) +{ + struct parse_variable_def *v, *v_next; + struct list *statements_list; + struct hlsl_ir_var *var; + struct hlsl_type *type; + BOOL ret, local = TRUE; + + if (basic_type->type == HLSL_CLASS_MATRIX) + assert(basic_type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK); + + if (!(statements_list = vkd3d_malloc(sizeof(*statements_list)))) + { + LIST_FOR_EACH_ENTRY_SAFE(v, v_next, var_list, struct parse_variable_def, entry) + free_parse_variable_def(v); + vkd3d_free(var_list); + return NULL; + } + list_init(statements_list); + + if (!var_list) + return statements_list; + + LIST_FOR_EACH_ENTRY_SAFE(v, v_next, var_list, struct parse_variable_def, entry) + { + if (v->array_size) + type = new_array_type(basic_type, v->array_size); + else + type = basic_type; + + if (!(var = new_var(v->name, type, v->loc, v->semantic, modifiers, v->reg_reservation))) + { + free_parse_variable_def(v); + continue; + } + debug_dump_decl(type, modifiers, v->name, v->loc.line); + + if (hlsl_ctx.cur_scope == hlsl_ctx.globals) + { + var->modifiers |= HLSL_STORAGE_UNIFORM; + local = FALSE; + } + + if (type->modifiers & HLSL_MODIFIER_CONST && !(var->modifiers & HLSL_STORAGE_UNIFORM) && !v->initializer.args_count) + { + hlsl_report_message(v->loc, HLSL_LEVEL_ERROR, "const variable without initializer"); + free_declaration(var); + vkd3d_free(v); + continue; + } + + ret = declare_variable(var, local); + if (!ret) + { + free_declaration(var); + vkd3d_free(v); + continue; + } + TRACE("Declared variable %s.\n", var->name); + + if (v->initializer.args_count) + { + unsigned int size = initializer_size(&v->initializer); + struct hlsl_ir_load *load; + + TRACE("Variable with initializer.\n"); + if (type->type <= HLSL_CLASS_LAST_NUMERIC + && type->dimx * type->dimy != size && size != 1) + { + if (size < type->dimx * type->dimy) + { + hlsl_report_message(v->loc, HLSL_LEVEL_ERROR, + "'%s' initializer does not match", v->name); + free_parse_initializer(&v->initializer); + vkd3d_free(v); + continue; + } + } + if ((type->type == HLSL_CLASS_STRUCT || type->type == HLSL_CLASS_ARRAY) + && components_count_type(type) != size) + { + hlsl_report_message(v->loc, HLSL_LEVEL_ERROR, + "'%s' initializer does not match", v->name); + free_parse_initializer(&v->initializer); + vkd3d_free(v); + continue; + } + + if (type->type == HLSL_CLASS_STRUCT) + { + struct_var_initializer(statements_list, var, &v->initializer); + vkd3d_free(v); + continue; + } + if (type->type > HLSL_CLASS_LAST_NUMERIC) + { + FIXME("Initializers for non scalar/struct variables not supported yet.\n"); + free_parse_initializer(&v->initializer); + vkd3d_free(v); + continue; + } + if (v->array_size > 0) + { + FIXME("Initializing arrays is not supported yet.\n"); + free_parse_initializer(&v->initializer); + vkd3d_free(v); + continue; + } + if (v->initializer.args_count > 1) + { + FIXME("Complex initializers are not supported yet.\n"); + free_parse_initializer(&v->initializer); + vkd3d_free(v); + continue; + } + + load = new_var_load(var, var->loc); + list_add_tail(v->initializer.instrs, &load->node.entry); + add_assignment(v->initializer.instrs, &load->node, ASSIGN_OP_ASSIGN, v->initializer.args[0]); + vkd3d_free(v->initializer.args); + + if (modifiers & HLSL_STORAGE_STATIC) + list_move_tail(&hlsl_ctx.static_initializers, v->initializer.instrs); + else + list_move_tail(statements_list, v->initializer.instrs); + vkd3d_free(v->initializer.instrs); + } + vkd3d_free(v); + } + vkd3d_free(var_list); + return statements_list; +} + +static struct hlsl_ir_function_decl *new_func_decl(struct hlsl_type *return_type, + struct list *parameters, const char *semantic, struct source_location loc) +{ + struct hlsl_ir_function_decl *decl; + + if (!(decl = vkd3d_calloc(1, sizeof(*decl)))) + return NULL; + decl->return_type = return_type; + decl->parameters = parameters; + decl->semantic = semantic; + decl->loc = loc; + + if (!type_is_void(return_type)) + { + struct hlsl_ir_var *return_var; + char name[28]; + + sprintf(name, "<retval-%p>", decl); + if (!(return_var = new_synthetic_var(name, return_type, loc))) + { + vkd3d_free(decl); + return NULL; + } + decl->return_var = return_var; + } + + return decl; +} + +%} + +%locations +%define parse.error verbose +%define api.prefix {hlsl_} +%expect 1 + +%union +{ + struct hlsl_type *type; + INT intval; + FLOAT floatval; + BOOL boolval; + char *name; + DWORD modifiers; + struct hlsl_ir_node *instr; + struct list *list; + struct parse_function function; + struct parse_parameter parameter; + struct parse_initializer initializer; + struct parse_variable_def *variable_def; + struct parse_if_body if_body; + enum parse_unary_op unary_op; + enum parse_assign_op assign_op; + struct reg_reservation *reg_reservation; + struct parse_colon_attribute colon_attribute; +} + +%token KW_BLENDSTATE +%token KW_BREAK +%token KW_BUFFER +%token KW_CBUFFER +%token KW_COLUMN_MAJOR +%token KW_COMPILE +%token KW_CONST +%token KW_CONTINUE +%token KW_DEPTHSTENCILSTATE +%token KW_DEPTHSTENCILVIEW +%token KW_DISCARD +%token KW_DO +%token KW_DOUBLE +%token KW_ELSE +%token KW_EXTERN +%token KW_FALSE +%token KW_FOR +%token KW_GEOMETRYSHADER +%token KW_GROUPSHARED +%token KW_IF +%token KW_IN +%token KW_INLINE +%token KW_INOUT +%token KW_MATRIX +%token KW_NAMESPACE +%token KW_NOINTERPOLATION +%token KW_OUT +%token KW_PASS +%token KW_PIXELSHADER +%token KW_PRECISE +%token KW_RASTERIZERSTATE +%token KW_RENDERTARGETVIEW +%token KW_RETURN +%token KW_REGISTER +%token KW_ROW_MAJOR +%token KW_SAMPLER +%token KW_SAMPLER1D +%token KW_SAMPLER2D +%token KW_SAMPLER3D +%token KW_SAMPLERCUBE +%token KW_SAMPLER_STATE +%token KW_SAMPLERCOMPARISONSTATE +%token KW_SHARED +%token KW_STATEBLOCK +%token KW_STATEBLOCK_STATE +%token KW_STATIC +%token KW_STRING +%token KW_STRUCT +%token KW_SWITCH +%token KW_TBUFFER +%token KW_TECHNIQUE +%token KW_TECHNIQUE10 +%token KW_TEXTURE +%token KW_TEXTURE1D +%token KW_TEXTURE1DARRAY +%token KW_TEXTURE2D +%token KW_TEXTURE2DARRAY +%token KW_TEXTURE2DMS +%token KW_TEXTURE2DMSARRAY +%token KW_TEXTURE3D +%token KW_TEXTURE3DARRAY +%token KW_TEXTURECUBE +%token KW_TRUE +%token KW_TYPEDEF +%token KW_UNIFORM +%token KW_VECTOR +%token KW_VERTEXSHADER +%token KW_VOID +%token KW_VOLATILE +%token KW_WHILE + +%token OP_INC +%token OP_DEC +%token OP_AND +%token OP_OR +%token OP_EQ +%token OP_LEFTSHIFT +%token OP_LEFTSHIFTASSIGN +%token OP_RIGHTSHIFT +%token OP_RIGHTSHIFTASSIGN +%token OP_ELLIPSIS +%token OP_LE +%token OP_GE +%token OP_NE +%token OP_ADDASSIGN +%token OP_SUBASSIGN +%token OP_MULASSIGN +%token OP_DIVASSIGN +%token OP_MODASSIGN +%token OP_ANDASSIGN +%token OP_ORASSIGN +%token OP_XORASSIGN +%token OP_UNKNOWN1 +%token OP_UNKNOWN2 +%token OP_UNKNOWN3 +%token OP_UNKNOWN4 + +%token <floatval> C_FLOAT + +%token <intval> C_INTEGER +%token <intval> PRE_LINE + +%type <list> add_expr +%type <list> assignment_expr +%type <list> bitand_expr +%type <list> bitor_expr +%type <list> bitxor_expr +%type <list> compound_statement +%type <list> conditional_expr +%type <list> declaration +%type <list> declaration_statement +%type <list> equality_expr +%type <list> expr +%type <list> expr_statement +%type <list> field +%type <list> fields_list +%type <list> initializer_expr +%type <list> jump_statement +%type <list> logicand_expr +%type <list> logicor_expr +%type <list> loop_statement +%type <list> mul_expr +%type <list> param_list +%type <list> parameters +%type <list> postfix_expr +%type <list> primary_expr +%type <list> relational_expr +%type <list> selection_statement +%type <list> shift_expr +%type <list> statement +%type <list> statement_list +%type <list> struct_declaration +%type <list> type_specs +%type <list> unary_expr +%type <list> variables_def +%type <list> variables_def_optional + +%token <name> VAR_IDENTIFIER +%token <name> NEW_IDENTIFIER +%token <name> STRING +%token <name> TYPE_IDENTIFIER + +%type <assign_op> assign_op + +%type <boolval> boolean + +%type <colon_attribute> colon_attribute + +%type <function> func_declaration +%type <function> func_prototype + +%type <initializer> complex_initializer +%type <initializer> initializer_expr_list + +%type <if_body> if_body + +%type <intval> array + +%type <modifiers> input_mod +%type <modifiers> input_mods +%type <modifiers> var_modifiers + +%type <name> any_identifier +%type <name> semantic +%type <name> var_identifier + +%type <parameter> parameter + +%type <reg_reservation> register_opt + +%type <type> base_type +%type <type> field_type +%type <type> named_struct_spec +%type <type> unnamed_struct_spec +%type <type> struct_spec +%type <type> type +%type <type> typedef_type + +%type <unary_op> unary_op + +%type <variable_def> type_spec +%type <variable_def> variable_def + +%% + +hlsl_prog: + /* empty */ + | hlsl_prog func_declaration + { + const struct hlsl_ir_function_decl *decl; + + decl = get_overloaded_func(&hlsl_ctx.functions, $2.name, $2.decl->parameters, TRUE); + if (decl && !decl->func->intrinsic) + { + if (decl->body && $2.decl->body) + { + hlsl_report_message($2.decl->loc, HLSL_LEVEL_ERROR, + "redefinition of function %s", debugstr_a($2.name)); + YYABORT; + } + else if (!compare_hlsl_types(decl->return_type, $2.decl->return_type)) + { + hlsl_report_message($2.decl->loc, HLSL_LEVEL_ERROR, + "redefining function %s with a different return type", + debugstr_a($2.name)); + hlsl_report_message(decl->loc, HLSL_LEVEL_NOTE, + "%s previously declared here", + debugstr_a($2.name)); + YYABORT; + } + } + + if (type_is_void($2.decl->return_type) && $2.decl->semantic) + { + hlsl_report_message($2.decl->loc, HLSL_LEVEL_ERROR, + "void function with a semantic"); + } + + TRACE("Adding function '%s' to the function list.\n", $2.name); + add_function_decl(&hlsl_ctx.functions, $2.name, $2.decl, FALSE); + } + | hlsl_prog declaration_statement + { + TRACE("Declaration statement parsed.\n"); + + if (!list_empty($2)) + FIXME("Uniform initializer.\n"); + free_instr_list($2); + } + | hlsl_prog preproc_directive + | hlsl_prog ';' + { + TRACE("Skipping stray semicolon.\n"); + } + +preproc_directive: + PRE_LINE STRING + { + const char **new_array = NULL; + + TRACE("Updating line information to file %s, line %u.\n", debugstr_a($2), $1); + hlsl_ctx.line_no = $1; + if (strcmp($2, hlsl_ctx.source_file)) + new_array = vkd3d_realloc(hlsl_ctx.source_files, + sizeof(*hlsl_ctx.source_files) * (hlsl_ctx.source_files_count + 1)); + + if (new_array) + { + hlsl_ctx.source_files = new_array; + hlsl_ctx.source_files[hlsl_ctx.source_files_count++] = $2; + hlsl_ctx.source_file = $2; + } + else + { + vkd3d_free($2); + } + } + +struct_declaration: + var_modifiers struct_spec variables_def_optional ';' + { + struct hlsl_type *type; + DWORD modifiers = $1; + + if (!$3) + { + if (!$2->name) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, + "anonymous struct declaration with no variables"); + } + if (modifiers) + { + hlsl_report_message(get_location(&@1), HLSL_LEVEL_ERROR, + "modifier not allowed on struct type declaration"); + } + } + + if (!(type = apply_type_modifiers($2, &modifiers, get_location(&@1)))) + YYABORT; + $$ = declare_vars(type, modifiers, $3); + } + +struct_spec: + named_struct_spec + | unnamed_struct_spec + +named_struct_spec: + KW_STRUCT any_identifier '{' fields_list '}' + { + BOOL ret; + + TRACE("Structure %s declaration.\n", debugstr_a($2)); + $$ = new_struct_type($2, $4); + + if (get_variable(hlsl_ctx.cur_scope, $2)) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, "redefinition of '%s'", $2); + YYABORT; + } + + ret = add_type_to_scope(hlsl_ctx.cur_scope, $$); + if (!ret) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, "redefinition of struct '%s'", $2); + YYABORT; + } + } + +unnamed_struct_spec: + KW_STRUCT '{' fields_list '}' + { + TRACE("Anonymous structure declaration.\n"); + $$ = new_struct_type(NULL, $3); + } + +any_identifier: + VAR_IDENTIFIER + | TYPE_IDENTIFIER + | NEW_IDENTIFIER + +fields_list: + /* empty */ + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + } + | fields_list field + { + BOOL ret; + struct hlsl_struct_field *field, *next; + + $$ = $1; + LIST_FOR_EACH_ENTRY_SAFE(field, next, $2, struct hlsl_struct_field, entry) + { + ret = add_struct_field($$, field); + if (ret == FALSE) + { + hlsl_report_message(get_location(&@2), + HLSL_LEVEL_ERROR, "redefinition of '%s'", field->name); + vkd3d_free(field); + } + } + vkd3d_free($2); + } + +field_type: + type + | unnamed_struct_spec + +field: + var_modifiers field_type variables_def ';' + { + struct hlsl_type *type; + DWORD modifiers = $1; + + if (!(type = apply_type_modifiers($2, &modifiers, get_location(&@1)))) + YYABORT; + $$ = gen_struct_fields(type, modifiers, $3); + } + +func_declaration: + func_prototype compound_statement + { + TRACE("Function %s parsed.\n", $1.name); + $$ = $1; + $$.decl->body = $2; + pop_scope(&hlsl_ctx); + } + | func_prototype ';' + { + TRACE("Function prototype for %s.\n", $1.name); + $$ = $1; + pop_scope(&hlsl_ctx); + } + +func_prototype: + /* var_modifiers is necessary to avoid shift/reduce conflicts. */ + var_modifiers type var_identifier '(' parameters ')' colon_attribute + { + if ($1) + { + hlsl_report_message(get_location(&@1), HLSL_LEVEL_ERROR, + "unexpected modifiers on a function"); + YYABORT; + } + if (get_variable(hlsl_ctx.globals, $3)) + { + hlsl_report_message(get_location(&@3), HLSL_LEVEL_ERROR, "redefinition of '%s'\n", $3); + YYABORT; + } + if (type_is_void($2) && $7.semantic) + { + hlsl_report_message(get_location(&@7), HLSL_LEVEL_ERROR, "void function with a semantic"); + } + + if ($7.reg_reservation) + { + FIXME("Unexpected register reservation for a function.\n"); + vkd3d_free($7.reg_reservation); + } + if (!($$.decl = new_func_decl($2, $5, $7.semantic, get_location(&@3)))) + YYABORT; + $$.name = $3; + hlsl_ctx.cur_function = $$.decl; + } + +compound_statement: + '{' '}' + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + } + | '{' scope_start statement_list '}' + { + pop_scope(&hlsl_ctx); + $$ = $3; + } + +scope_start: + /* empty */ + { + push_scope(&hlsl_ctx); + } + +var_identifier: + VAR_IDENTIFIER + | NEW_IDENTIFIER + +colon_attribute: + /* empty */ + { + $$.semantic = NULL; + $$.reg_reservation = NULL; + } + | semantic + { + $$.semantic = $1; + $$.reg_reservation = NULL; + } + | register_opt + { + $$.semantic = NULL; + $$.reg_reservation = $1; + } + +semantic: + ':' any_identifier + { + $$ = $2; + } + +/* FIXME: Writemasks */ +register_opt: + ':' KW_REGISTER '(' any_identifier ')' + { + $$ = parse_reg_reservation($4); + vkd3d_free($4); + } + | ':' KW_REGISTER '(' any_identifier ',' any_identifier ')' + { + FIXME("Ignoring shader target %s in a register reservation.\n", debugstr_a($4)); + vkd3d_free($4); + + $$ = parse_reg_reservation($6); + vkd3d_free($6); + } + +parameters: + scope_start + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + } + | scope_start param_list + { + $$ = $2; + } + +param_list: + parameter + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + if (!add_func_parameter($$, &$1, get_location(&@1))) + { + ERR("Error adding function parameter %s.\n", $1.name); + set_parse_status(&hlsl_ctx.status, PARSE_ERR); + YYABORT; + } + } + | param_list ',' parameter + { + $$ = $1; + if (!add_func_parameter($$, &$3, get_location(&@3))) + { + hlsl_report_message(get_location(&@3), HLSL_LEVEL_ERROR, "duplicate parameter %s", $3.name); + YYABORT; + } + } + +parameter: + input_mods var_modifiers type any_identifier colon_attribute + { + struct hlsl_type *type; + DWORD modifiers = $2; + + if (!(type = apply_type_modifiers($3, &modifiers, get_location(&@2)))) + YYABORT; + + $$.modifiers = $1 ? $1 : HLSL_STORAGE_IN; + $$.modifiers |= modifiers; + $$.type = type; + $$.name = $4; + $$.semantic = $5.semantic; + $$.reg_reservation = $5.reg_reservation; + } + +input_mods: + /* empty */ + { + $$ = 0; + } + | input_mods input_mod + { + if ($1 & $2) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, + "duplicate input-output modifiers"); + YYABORT; + } + $$ = $1 | $2; + } + +input_mod: + KW_IN + { + $$ = HLSL_STORAGE_IN; + } + | KW_OUT + { + $$ = HLSL_STORAGE_OUT; + } + | KW_INOUT + { + $$ = HLSL_STORAGE_IN | HLSL_STORAGE_OUT; + } + +type: + base_type + { + $$ = $1; + } + | KW_VECTOR '<' base_type ',' C_INTEGER '>' + { + if ($3->type != HLSL_CLASS_SCALAR) + { + hlsl_report_message(get_location(&@3), HLSL_LEVEL_ERROR, + "vectors of non-scalar types are not allowed\n"); + YYABORT; + } + if ($5 < 1 || $5 > 4) + { + hlsl_report_message(get_location(&@5), HLSL_LEVEL_ERROR, + "vector size must be between 1 and 4\n"); + YYABORT; + } + + $$ = new_hlsl_type(NULL, HLSL_CLASS_VECTOR, $3->base_type, $5, 1); + } + | KW_MATRIX '<' base_type ',' C_INTEGER ',' C_INTEGER '>' + { + if ($3->type != HLSL_CLASS_SCALAR) + { + hlsl_report_message(get_location(&@3), HLSL_LEVEL_ERROR, + "matrices of non-scalar types are not allowed\n"); + YYABORT; + } + if ($5 < 1 || $5 > 4) + { + hlsl_report_message(get_location(&@5), HLSL_LEVEL_ERROR, + "matrix row count must be between 1 and 4\n"); + YYABORT; + } + if ($7 < 1 || $7 > 4) + { + hlsl_report_message(get_location(&@7), HLSL_LEVEL_ERROR, + "matrix column count must be between 1 and 4\n"); + YYABORT; + } + + $$ = new_hlsl_type(NULL, HLSL_CLASS_MATRIX, $3->base_type, $7, $5); + } + +base_type: + KW_VOID + { + $$ = hlsl_ctx.builtin_types.Void; + } + | KW_SAMPLER + { + $$ = hlsl_ctx.builtin_types.sampler[HLSL_SAMPLER_DIM_GENERIC]; + } + | KW_SAMPLER1D + { + $$ = hlsl_ctx.builtin_types.sampler[HLSL_SAMPLER_DIM_1D]; + } + | KW_SAMPLER2D + { + $$ = hlsl_ctx.builtin_types.sampler[HLSL_SAMPLER_DIM_2D]; + } + | KW_SAMPLER3D + { + $$ = hlsl_ctx.builtin_types.sampler[HLSL_SAMPLER_DIM_3D]; + } + | KW_SAMPLERCUBE + { + $$ = hlsl_ctx.builtin_types.sampler[HLSL_SAMPLER_DIM_3D]; + } + | TYPE_IDENTIFIER + { + $$ = get_type(hlsl_ctx.cur_scope, $1, TRUE); + vkd3d_free($1); + } + | KW_STRUCT TYPE_IDENTIFIER + { + $$ = get_type(hlsl_ctx.cur_scope, $2, TRUE); + if ($$->type != HLSL_CLASS_STRUCT) + hlsl_report_message(get_location(&@1), HLSL_LEVEL_ERROR, "'%s' redefined as a structure\n", $2); + vkd3d_free($2); + } + +declaration_statement: + declaration + | struct_declaration + | typedef + { + if (!($$ = vkd3d_malloc(sizeof(*$$)))) + YYABORT; + list_init($$); + } + +typedef_type: + type + | struct_spec + +typedef: + KW_TYPEDEF var_modifiers typedef_type type_specs ';' + { + if ($2 & ~HLSL_TYPE_MODIFIERS_MASK) + { + struct parse_variable_def *v, *v_next; + hlsl_report_message(get_location(&@1), HLSL_LEVEL_ERROR, "modifier not allowed on typedefs"); + LIST_FOR_EACH_ENTRY_SAFE(v, v_next, $4, struct parse_variable_def, entry) + vkd3d_free(v); + vkd3d_free($4); + YYABORT; + } + if (!add_typedef($2, $3, $4)) + YYABORT; + } + +type_specs: + type_spec + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + list_add_head($$, &$1->entry); + } + | type_specs ',' type_spec + { + $$ = $1; + list_add_tail($$, &$3->entry); + } + +type_spec: + any_identifier array + { + $$ = vkd3d_calloc(1, sizeof(*$$)); + $$->loc = get_location(&@1); + $$->name = $1; + $$->array_size = $2; + } + +declaration: + var_modifiers type variables_def ';' + { + struct hlsl_type *type; + DWORD modifiers = $1; + + if (!(type = apply_type_modifiers($2, &modifiers, get_location(&@1)))) + YYABORT; + $$ = declare_vars(type, modifiers, $3); + } + +variables_def_optional: + /* empty */ + { + $$ = NULL; + } + | variables_def + +variables_def: + variable_def + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + list_add_head($$, &$1->entry); + } + | variables_def ',' variable_def + { + $$ = $1; + list_add_tail($$, &$3->entry); + } + +variable_def: + any_identifier array colon_attribute + { + $$ = vkd3d_calloc(1, sizeof(*$$)); + $$->loc = get_location(&@1); + $$->name = $1; + $$->array_size = $2; + $$->semantic = $3.semantic; + $$->reg_reservation = $3.reg_reservation; + } + | any_identifier array colon_attribute '=' complex_initializer + { + TRACE("Declaration with initializer.\n"); + $$ = vkd3d_calloc(1, sizeof(*$$)); + $$->loc = get_location(&@1); + $$->name = $1; + $$->array_size = $2; + $$->semantic = $3.semantic; + $$->reg_reservation = $3.reg_reservation; + $$->initializer = $5; + } + +array: + /* empty */ + { + $$ = 0; + } + | '[' expr ']' + { + unsigned int size = evaluate_array_dimension(node_from_list($2)); + + free_instr_list($2); + + if (!size) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, + "array size is not a positive integer constant\n"); + YYABORT; + } + TRACE("Array size %u.\n", size); + + if (size > 65536) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, + "array size must be between 1 and 65536"); + YYABORT; + } + $$ = size; + } + +var_modifiers: + /* empty */ + { + $$ = 0; + } + | KW_EXTERN var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_EXTERN, get_location(&@1)); + } + | KW_NOINTERPOLATION var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_NOINTERPOLATION, get_location(&@1)); + } + | KW_PRECISE var_modifiers + { + $$ = add_modifiers($2, HLSL_MODIFIER_PRECISE, get_location(&@1)); + } + | KW_SHARED var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_SHARED, get_location(&@1)); + } + | KW_GROUPSHARED var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_GROUPSHARED, get_location(&@1)); + } + | KW_STATIC var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_STATIC, get_location(&@1)); + } + | KW_UNIFORM var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_UNIFORM, get_location(&@1)); + } + | KW_VOLATILE var_modifiers + { + $$ = add_modifiers($2, HLSL_STORAGE_VOLATILE, get_location(&@1)); + } + | KW_CONST var_modifiers + { + $$ = add_modifiers($2, HLSL_MODIFIER_CONST, get_location(&@1)); + } + | KW_ROW_MAJOR var_modifiers + { + $$ = add_modifiers($2, HLSL_MODIFIER_ROW_MAJOR, get_location(&@1)); + } + | KW_COLUMN_MAJOR var_modifiers + { + $$ = add_modifiers($2, HLSL_MODIFIER_COLUMN_MAJOR, get_location(&@1)); + } + +complex_initializer: + initializer_expr + { + $$.args_count = 1; + if (!($$.args = vkd3d_malloc(sizeof(*$$.args)))) + YYABORT; + $$.args[0] = node_from_list($1); + $$.instrs = $1; + } + | '{' initializer_expr_list '}' + { + $$ = $2; + } + | '{' initializer_expr_list ',' '}' + { + $$ = $2; + } + +initializer_expr: + assignment_expr + +initializer_expr_list: + initializer_expr + { + $$.args_count = 1; + if (!($$.args = vkd3d_malloc(sizeof(*$$.args)))) + YYABORT; + $$.args[0] = node_from_list($1); + $$.instrs = $1; + } + | initializer_expr_list ',' initializer_expr + { + $$ = $1; + if (!($$.args = vkd3d_realloc($$.args, ($$.args_count + 1) * sizeof(*$$.args)))) + YYABORT; + $$.args[$$.args_count++] = node_from_list($3); + list_move_tail($$.instrs, $3); + vkd3d_free($3); + } + +boolean: + KW_TRUE + { + $$ = TRUE; + } + | KW_FALSE + { + $$ = FALSE; + } + +statement_list: + statement + | statement_list statement + { + $$ = $1; + list_move_tail($$, $2); + vkd3d_free($2); + } + +statement: + declaration_statement + | expr_statement + | compound_statement + | jump_statement + | selection_statement + | loop_statement + +jump_statement: + KW_RETURN expr ';' + { + if (!add_return($2, node_from_list($2), get_location(&@1))) + YYABORT; + $$ = $2; + } + | KW_RETURN ';' + { + if (!($$ = vkd3d_malloc(sizeof(*$$)))) + YYABORT; + list_init($$); + if (!add_return($$, NULL, get_location(&@1))) + YYABORT; + } + +selection_statement: + KW_IF '(' expr ')' if_body + { + struct hlsl_ir_node *condition = node_from_list($3); + struct hlsl_ir_if *instr; + + if (!(instr = new_if(condition, get_location(&@1)))) + YYABORT; + list_move_tail(&instr->then_instrs, $5.then_instrs); + list_move_tail(&instr->else_instrs, $5.else_instrs); + vkd3d_free($5.then_instrs); + vkd3d_free($5.else_instrs); + if (condition->data_type->dimx > 1 || condition->data_type->dimy > 1) + hlsl_report_message(instr->node.loc, HLSL_LEVEL_ERROR, + "if condition requires a scalar"); + $$ = $3; + list_add_tail($$, &instr->node.entry); + } + +if_body: + statement + { + $$.then_instrs = $1; + $$.else_instrs = NULL; + } + | statement KW_ELSE statement + { + $$.then_instrs = $1; + $$.else_instrs = $3; + } + +loop_statement: + KW_WHILE '(' expr ')' statement + { + $$ = create_loop(LOOP_WHILE, NULL, $3, NULL, $5, get_location(&@1)); + } + | KW_DO statement KW_WHILE '(' expr ')' ';' + { + $$ = create_loop(LOOP_DO_WHILE, NULL, $5, NULL, $2, get_location(&@1)); + } + | KW_FOR '(' scope_start expr_statement expr_statement expr ')' statement + { + $$ = create_loop(LOOP_FOR, $4, $5, $6, $8, get_location(&@1)); + pop_scope(&hlsl_ctx); + } + | KW_FOR '(' scope_start declaration expr_statement expr ')' statement + { + if (!$4) + hlsl_report_message(get_location(&@4), HLSL_LEVEL_WARNING, + "no expressions in for loop initializer"); + $$ = create_loop(LOOP_FOR, $4, $5, $6, $8, get_location(&@1)); + pop_scope(&hlsl_ctx); + } + +expr_statement: + ';' + { + $$ = vkd3d_malloc(sizeof(*$$)); + list_init($$); + } + | expr ';' + { + $$ = $1; + } + +primary_expr: + C_FLOAT + { + struct hlsl_ir_constant *c; + + if (!(c = vkd3d_malloc(sizeof(*c)))) + YYABORT; + init_node(&c->node, HLSL_IR_CONSTANT, hlsl_ctx.builtin_types.scalar[HLSL_TYPE_FLOAT], get_location(&@1)); + c->value.f[0] = $1; + if (!($$ = make_list(&c->node))) + YYABORT; + } + | C_INTEGER + { + struct hlsl_ir_constant *c; + + if (!(c = vkd3d_malloc(sizeof(*c)))) + YYABORT; + init_node(&c->node, HLSL_IR_CONSTANT, hlsl_ctx.builtin_types.scalar[HLSL_TYPE_INT], get_location(&@1)); + c->value.i[0] = $1; + if (!($$ = make_list(&c->node))) + YYABORT; + } + | boolean + { + struct hlsl_ir_constant *c; + + if (!(c = vkd3d_malloc(sizeof(*c)))) + YYABORT; + init_node(&c->node, HLSL_IR_CONSTANT, hlsl_ctx.builtin_types.scalar[HLSL_TYPE_BOOL], get_location(&@1)); + c->value.b[0] = $1; + if (!($$ = make_list(&c->node))) + YYABORT; + } + | VAR_IDENTIFIER + { + struct hlsl_ir_load *load; + struct hlsl_ir_var *var; + + if (!(var = get_variable(hlsl_ctx.cur_scope, $1))) + { + hlsl_report_message(get_location(&@1), HLSL_LEVEL_ERROR, "variable '%s' is not declared\n", $1); + YYABORT; + } + if ((load = new_var_load(var, get_location(&@1)))) + { + if (!($$ = make_list(&load->node))) + YYABORT; + } + else + $$ = NULL; + } + | '(' expr ')' + { + $$ = $2; + } + +postfix_expr: + primary_expr + | postfix_expr OP_INC + { + struct source_location loc; + struct hlsl_ir_node *inc; + + loc = get_location(&@2); + if (node_from_list($1)->data_type->modifiers & HLSL_MODIFIER_CONST) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "modifying a const expression"); + YYABORT; + } + inc = new_unary_expr(HLSL_IR_UNOP_POSTINC, node_from_list($1), loc); + /* Post increment/decrement expressions are considered const */ + inc->data_type = clone_hlsl_type(inc->data_type, 0); + inc->data_type->modifiers |= HLSL_MODIFIER_CONST; + $$ = append_unop($1, inc); + } + | postfix_expr OP_DEC + { + struct source_location loc; + struct hlsl_ir_node *inc; + + loc = get_location(&@2); + if (node_from_list($1)->data_type->modifiers & HLSL_MODIFIER_CONST) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "modifying a const expression"); + YYABORT; + } + inc = new_unary_expr(HLSL_IR_UNOP_POSTDEC, node_from_list($1), loc); + /* Post increment/decrement expressions are considered const */ + inc->data_type = clone_hlsl_type(inc->data_type, 0); + inc->data_type->modifiers |= HLSL_MODIFIER_CONST; + $$ = append_unop($1, inc); + } + | postfix_expr '.' any_identifier + { + struct hlsl_ir_node *node = node_from_list($1); + struct source_location loc; + + loc = get_location(&@2); + if (node->data_type->type == HLSL_CLASS_STRUCT) + { + struct hlsl_type *type = node->data_type; + struct hlsl_struct_field *field; + + $$ = NULL; + LIST_FOR_EACH_ENTRY(field, type->e.elements, struct hlsl_struct_field, entry) + { + if (!strcmp($3, field->name)) + { + if (!add_record_load($1, node, field, loc)) + YYABORT; + $$ = $1; + break; + } + } + if (!$$) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "invalid subscript %s", debugstr_a($3)); + YYABORT; + } + } + else if (node->data_type->type <= HLSL_CLASS_LAST_NUMERIC) + { + struct hlsl_ir_swizzle *swizzle; + + swizzle = get_swizzle(node, $3, &loc); + if (!swizzle) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "invalid swizzle %s", debugstr_a($3)); + YYABORT; + } + $$ = append_unop($1, &swizzle->node); + } + else + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "invalid subscript %s", debugstr_a($3)); + YYABORT; + } + } + | postfix_expr '[' expr ']' + { + struct hlsl_ir_node *array = node_from_list($1), *index = node_from_list($3); + + list_move_tail($1, $3); + vkd3d_free($3); + + if (index->data_type->type != HLSL_CLASS_SCALAR) + { + hlsl_report_message(get_location(&@3), HLSL_LEVEL_ERROR, "array index is not scalar"); + free_instr_list($1); + YYABORT; + } + + if (!add_array_load($1, array, index, get_location(&@2))) + { + free_instr_list($1); + YYABORT; + } + $$ = $1; + } + + /* var_modifiers is necessary to avoid shift/reduce conflicts. */ + | var_modifiers type '(' initializer_expr_list ')' + { + struct hlsl_ir_assignment *assignment; + unsigned int i, writemask_offset = 0; + static unsigned int counter; + struct hlsl_ir_load *load; + struct hlsl_ir_var *var; + char name[23]; + + if ($1) + { + hlsl_report_message(get_location(&@1), HLSL_LEVEL_ERROR, + "unexpected modifier on a constructor\n"); + YYABORT; + } + if ($2->type > HLSL_CLASS_LAST_NUMERIC) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, + "constructors may only be used with numeric data types\n"); + YYABORT; + } + if ($2->dimx * $2->dimy != initializer_size(&$4)) + { + hlsl_report_message(get_location(&@4), HLSL_LEVEL_ERROR, + "expected %u components in constructor, but got %u\n", + $2->dimx * $2->dimy, initializer_size(&$4)); + YYABORT; + } + + if ($2->type == HLSL_CLASS_MATRIX) + FIXME("Matrix constructors are not supported yet.\n"); + + sprintf(name, "<constructor-%x>", counter++); + if (!(var = new_synthetic_var(name, $2, get_location(&@2)))) + YYABORT; + for (i = 0; i < $4.args_count; ++i) + { + struct hlsl_ir_node *arg = $4.args[i]; + unsigned int width; + + if (arg->data_type->type == HLSL_CLASS_OBJECT) + { + hlsl_report_message(arg->loc, HLSL_LEVEL_ERROR, "invalid constructor argument"); + continue; + } + width = components_count_type(arg->data_type); + + if (width > 4) + { + FIXME("Constructor argument with %u components.\n", width); + continue; + } + + if (!(arg = add_implicit_conversion($4.instrs, arg, + hlsl_ctx.builtin_types.vector[$2->base_type][width - 1], &arg->loc))) + continue; + + if (!(assignment = new_assignment(var, NULL, arg, + ((1 << width) - 1) << writemask_offset, arg->loc))) + YYABORT; + writemask_offset += width; + list_add_tail($4.instrs, &assignment->node.entry); + } + vkd3d_free($4.args); + if (!(load = new_var_load(var, get_location(&@2)))) + YYABORT; + $$ = append_unop($4.instrs, &load->node); + } + +unary_expr: + postfix_expr + | OP_INC unary_expr + { + struct source_location loc; + + loc = get_location(&@1); + if (node_from_list($2)->data_type->modifiers & HLSL_MODIFIER_CONST) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "modifying a const expression"); + YYABORT; + } + $$ = append_unop($2, new_unary_expr(HLSL_IR_UNOP_PREINC, node_from_list($2), loc)); + } + | OP_DEC unary_expr + { + struct source_location loc; + + loc = get_location(&@1); + if (node_from_list($2)->data_type->modifiers & HLSL_MODIFIER_CONST) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "modifying a const expression"); + YYABORT; + } + $$ = append_unop($2, new_unary_expr(HLSL_IR_UNOP_PREDEC, node_from_list($2), loc)); + } + | unary_op unary_expr + { + enum hlsl_ir_expr_op ops[] = {0, HLSL_IR_UNOP_NEG, + HLSL_IR_UNOP_LOGIC_NOT, HLSL_IR_UNOP_BIT_NOT}; + + if ($1 == UNARY_OP_PLUS) + $$ = $2; + else + $$ = append_unop($2, new_unary_expr(ops[$1], node_from_list($2), get_location(&@1))); + } + + /* var_modifiers is necessary to avoid shift/reduce conflicts. */ + | '(' var_modifiers type array ')' unary_expr + { + struct hlsl_type *src_type = node_from_list($6)->data_type; + struct hlsl_type *dst_type; + struct source_location loc; + + loc = get_location(&@3); + if ($2) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "unexpected modifier in a cast"); + YYABORT; + } + + if ($4) + dst_type = new_array_type($3, $4); + else + dst_type = $3; + + if (!compatible_data_types(src_type, dst_type)) + { + hlsl_report_message(loc, HLSL_LEVEL_ERROR, "can't cast from %s to %s", + debug_hlsl_type(src_type), debug_hlsl_type(dst_type)); + YYABORT; + } + + $$ = append_unop($6, &new_cast(node_from_list($6), dst_type, &loc)->node); + } + +unary_op: + '+' + { + $$ = UNARY_OP_PLUS; + } + | '-' + { + $$ = UNARY_OP_MINUS; + } + | '!' + { + $$ = UNARY_OP_LOGICNOT; + } + | '~' + { + $$ = UNARY_OP_BITNOT; + } + +mul_expr: + unary_expr + | mul_expr '*' unary_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_MUL, get_location(&@2)); + } + | mul_expr '/' unary_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_DIV, get_location(&@2)); + } + | mul_expr '%' unary_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_MOD, get_location(&@2)); + } + +add_expr: + mul_expr + | add_expr '+' mul_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_ADD, get_location(&@2)); + } + | add_expr '-' mul_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_SUB, get_location(&@2)); + } + +shift_expr: + add_expr + | shift_expr OP_LEFTSHIFT add_expr + { + FIXME("Left shift.\n"); + } + | shift_expr OP_RIGHTSHIFT add_expr + { + FIXME("Right shift.\n"); + } + +relational_expr: + shift_expr + | relational_expr '<' shift_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_LESS, get_location(&@2)); + } + | relational_expr '>' shift_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_GREATER, get_location(&@2)); + } + | relational_expr OP_LE shift_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_LEQUAL, get_location(&@2)); + } + | relational_expr OP_GE shift_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_GEQUAL, get_location(&@2)); + } + +equality_expr: + relational_expr + | equality_expr OP_EQ relational_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_EQUAL, get_location(&@2)); + } + | equality_expr OP_NE relational_expr + { + $$ = add_binary_expr($1, $3, HLSL_IR_BINOP_NEQUAL, get_location(&@2)); + } + +bitand_expr: + equality_expr + | bitand_expr '&' equality_expr + { + FIXME("Bitwise AND.\n"); + } + +bitxor_expr: + bitand_expr + | bitxor_expr '^' bitand_expr + { + FIXME("Bitwise XOR.\n"); + } + +bitor_expr: + bitxor_expr + | bitor_expr '|' bitxor_expr + { + FIXME("Bitwise OR.\n"); + } + +logicand_expr: + bitor_expr + | logicand_expr OP_AND bitor_expr + { + FIXME("Logical AND.\n"); + } + +logicor_expr: + logicand_expr + | logicor_expr OP_OR logicand_expr + { + FIXME("Logical OR.\n"); + } + +conditional_expr: + logicor_expr + | logicor_expr '?' expr ':' assignment_expr + { + FIXME("Ternary operator.\n"); + } + +assignment_expr: + + conditional_expr + | unary_expr assign_op assignment_expr + { + struct hlsl_ir_node *lhs = node_from_list($1), *rhs = node_from_list($3); + + if (lhs->data_type->modifiers & HLSL_MODIFIER_CONST) + { + hlsl_report_message(get_location(&@2), HLSL_LEVEL_ERROR, "l-value is const"); + YYABORT; + } + list_move_tail($3, $1); + vkd3d_free($1); + if (!add_assignment($3, lhs, $2, rhs)) + YYABORT; + $$ = $3; + } + +assign_op: + '=' + { + $$ = ASSIGN_OP_ASSIGN; + } + | OP_ADDASSIGN + { + $$ = ASSIGN_OP_ADD; + } + | OP_SUBASSIGN + { + $$ = ASSIGN_OP_SUB; + } + | OP_MULASSIGN + { + $$ = ASSIGN_OP_MUL; + } + | OP_DIVASSIGN + { + $$ = ASSIGN_OP_DIV; + } + | OP_MODASSIGN + { + $$ = ASSIGN_OP_MOD; + } + | OP_LEFTSHIFTASSIGN + { + $$ = ASSIGN_OP_LSHIFT; + } + | OP_RIGHTSHIFTASSIGN + { + $$ = ASSIGN_OP_RSHIFT; + } + | OP_ANDASSIGN + { + $$ = ASSIGN_OP_AND; + } + | OP_ORASSIGN + { + $$ = ASSIGN_OP_OR; + } + | OP_XORASSIGN + { + $$ = ASSIGN_OP_XOR; + } + +expr: + assignment_expr + | expr ',' assignment_expr + { + $$ = $1; + list_move_tail($$, $3); + vkd3d_free($3); + } + +%% + +static struct source_location get_location(const struct YYLTYPE *l) +{ + const struct source_location loc = + { + .file = hlsl_ctx.source_file, + .line = l->first_line, + .col = l->first_column, + }; + return loc; +} + +static void dump_function_decl(struct rb_entry *entry, void *context) +{ + struct hlsl_ir_function_decl *func = RB_ENTRY_VALUE(entry, struct hlsl_ir_function_decl, entry); + + if (func->body) + debug_dump_ir_function_decl(func); +} + +static void dump_function(struct rb_entry *entry, void *context) +{ + struct hlsl_ir_function *func = RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry); + rb_for_each_entry(&func->overloads, dump_function_decl, NULL); +} + +/* Allocate a unique, ordered index to each instruction, which will be used for + * computing liveness ranges. */ +static unsigned int index_instructions(struct list *instrs, unsigned int index) +{ + struct hlsl_ir_node *instr; + + LIST_FOR_EACH_ENTRY(instr, instrs, struct hlsl_ir_node, entry) + { + instr->index = index++; + + if (instr->type == HLSL_IR_IF) + { + struct hlsl_ir_if *iff = if_from_node(instr); + index = index_instructions(&iff->then_instrs, index); + index = index_instructions(&iff->else_instrs, index); + } + else if (instr->type == HLSL_IR_LOOP) + { + index = index_instructions(&loop_from_node(instr)->body, index); + loop_from_node(instr)->next_index = index; + } + } + + return index; +} + +/* Compute the earliest and latest liveness for each variable. In the case that + * a variable is accessed inside of a loop, we promote its liveness to extend + * to at least the range of the entire loop. Note that we don't need to do this + * for anonymous nodes, since there's currently no way to use a node which was + * calculated in an earlier iteration of the loop. */ +static void compute_liveness_recurse(struct list *instrs, unsigned int loop_first, unsigned int loop_last) +{ + struct hlsl_ir_node *instr; + struct hlsl_ir_var *var; + + LIST_FOR_EACH_ENTRY(instr, instrs, struct hlsl_ir_node, entry) + { + switch (instr->type) + { + case HLSL_IR_ASSIGNMENT: + { + struct hlsl_ir_assignment *assignment = assignment_from_node(instr); + + var = assignment->lhs.var; + if (!var->first_write) + var->first_write = loop_first ? min(instr->index, loop_first) : instr->index; + assignment->rhs.node->last_read = instr->index; + if (assignment->lhs.offset.node) + assignment->lhs.offset.node->last_read = instr->index; + break; + } + case HLSL_IR_EXPR: + { + struct hlsl_ir_expr *expr = expr_from_node(instr); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(expr->operands) && expr->operands[i].node; ++i) + expr->operands[i].node->last_read = instr->index; + break; + } + case HLSL_IR_IF: + { + struct hlsl_ir_if *iff = if_from_node(instr); + + compute_liveness_recurse(&iff->then_instrs, loop_first, loop_last); + compute_liveness_recurse(&iff->else_instrs, loop_first, loop_last); + iff->condition.node->last_read = instr->index; + break; + } + case HLSL_IR_LOAD: + { + struct hlsl_ir_load *load = load_from_node(instr); + + var = load->src.var; + var->last_read = loop_last ? max(instr->index, loop_last) : instr->index; + if (load->src.offset.node) + load->src.offset.node->last_read = instr->index; + break; + } + case HLSL_IR_LOOP: + { + struct hlsl_ir_loop *loop = loop_from_node(instr); + + compute_liveness_recurse(&loop->body, loop_first ? loop_first : instr->index, + loop_last ? loop_last : loop->next_index); + break; + } + case HLSL_IR_SWIZZLE: + { + struct hlsl_ir_swizzle *swizzle = swizzle_from_node(instr); + + swizzle->val.node->last_read = instr->index; + break; + } + case HLSL_IR_CONSTANT: + case HLSL_IR_JUMP: + break; + } + } +} + +static void compute_liveness(struct hlsl_ir_function_decl *entry_func) +{ + struct hlsl_ir_var *var; + + LIST_FOR_EACH_ENTRY(var, &hlsl_ctx.globals->vars, struct hlsl_ir_var, scope_entry) + { + var->first_write = 1; + } + + LIST_FOR_EACH_ENTRY(var, entry_func->parameters, struct hlsl_ir_var, param_entry) + { + if (var->modifiers & HLSL_STORAGE_IN) + var->first_write = 1; + if (var->modifiers & HLSL_STORAGE_OUT) + var->last_read = UINT_MAX; + } + + if (entry_func->return_var) + entry_func->return_var->last_read = UINT_MAX; + + compute_liveness_recurse(entry_func->body, 0, 0); +} + +int hlsl_parser_compile(enum vkd3d_shader_type type, DWORD major, DWORD minor, const char *entrypoint, + struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) +{ + struct hlsl_ir_function_decl *entry_func; + struct hlsl_scope *scope, *next_scope; + struct hlsl_type *hlsl_type, *next_type; + struct hlsl_ir_var *var, *next_var; + int ret = VKD3D_ERROR; + unsigned int i; + + hlsl_ctx.status = PARSE_SUCCESS; + hlsl_ctx.message_context = message_context; + hlsl_ctx.line_no = hlsl_ctx.column = 1; + hlsl_ctx.source_file = vkd3d_strdup(""); + hlsl_ctx.source_files = vkd3d_malloc(sizeof(*hlsl_ctx.source_files)); + if (hlsl_ctx.source_files) + hlsl_ctx.source_files[0] = hlsl_ctx.source_file; + hlsl_ctx.source_files_count = 1; + hlsl_ctx.cur_scope = NULL; + hlsl_ctx.matrix_majority = HLSL_COLUMN_MAJOR; + list_init(&hlsl_ctx.scopes); + list_init(&hlsl_ctx.types); + init_functions_tree(&hlsl_ctx.functions); + list_init(&hlsl_ctx.static_initializers); + + push_scope(&hlsl_ctx); + hlsl_ctx.globals = hlsl_ctx.cur_scope; + declare_predefined_types(hlsl_ctx.globals); + + hlsl_parse(); + + if (hlsl_ctx.status == PARSE_ERR) + goto out; + + if (!(entry_func = get_func_entry(entrypoint))) + { + hlsl_message("error: entry point %s is not defined\n", debugstr_a(entrypoint)); + goto out; + } + + if (!type_is_void(entry_func->return_type) + && entry_func->return_type->type != HLSL_CLASS_STRUCT && !entry_func->semantic) + { + hlsl_report_message(entry_func->loc, HLSL_LEVEL_ERROR, + "entry point "%s" is missing a return value semantic", entry_func->func->name); + } + + list_move_head(entry_func->body, &hlsl_ctx.static_initializers); + + /* Index 0 means unused; index 1 means function entry, so start at 2. */ + index_instructions(entry_func->body, 2); + + if (TRACE_ON()) + { + TRACE("IR dump.\n"); + rb_for_each_entry(&hlsl_ctx.functions, dump_function, NULL); + } + + compute_liveness(entry_func); + + if (hlsl_ctx.status != PARSE_ERR) + ret = VKD3D_ERROR_NOT_IMPLEMENTED; + +out: + for (i = 0; i < hlsl_ctx.source_files_count; ++i) + vkd3d_free((void *)hlsl_ctx.source_files[i]); + vkd3d_free(hlsl_ctx.source_files); + + TRACE("Freeing functions IR.\n"); + rb_destroy(&hlsl_ctx.functions, free_function_rb, NULL); + + TRACE("Freeing variables.\n"); + LIST_FOR_EACH_ENTRY_SAFE(scope, next_scope, &hlsl_ctx.scopes, struct hlsl_scope, entry) + { + LIST_FOR_EACH_ENTRY_SAFE(var, next_var, &scope->vars, struct hlsl_ir_var, scope_entry) + free_declaration(var); + rb_destroy(&scope->types, NULL, NULL); + vkd3d_free(scope); + } + + TRACE("Freeing types.\n"); + LIST_FOR_EACH_ENTRY_SAFE(hlsl_type, next_type, &hlsl_ctx.types, struct hlsl_type, entry) + free_hlsl_type(hlsl_type); + + return ret; +} diff --git a/libs/vkd3d-shader/vkd3d_shader_main.c b/libs/vkd3d-shader/vkd3d_shader_main.c index df3b1a57..ff917dbf 100644 --- a/libs/vkd3d-shader/vkd3d_shader_main.c +++ b/libs/vkd3d-shader/vkd3d_shader_main.c @@ -225,7 +225,7 @@ static void vkd3d_shader_dump_blob(const char *path, const char *prefix, const v } }
-static void vkd3d_shader_dump_shader(enum vkd3d_shader_type type, const struct vkd3d_shader_code *shader) +void vkd3d_shader_dump_shader(enum vkd3d_shader_type type, const struct vkd3d_shader_code *shader) { static bool enabled = true; const char *path; @@ -961,7 +961,16 @@ static int compile_dxbc_tpf(const struct vkd3d_shader_compile_info *compile_info static int compile_hlsl(const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_code *out, struct vkd3d_shader_message_context *message_context) { - return VKD3D_ERROR_NOT_IMPLEMENTED; + struct vkd3d_shader_code preprocessed; + int ret; + + if ((ret = preproc_lexer_parse(compile_info, &preprocessed, message_context))) + return ret; + + ret = hlsl_compile_shader(preprocessed.code, compile_info, out, message_context); + + vkd3d_shader_free_shader_code(&preprocessed); + return ret; }
int vkd3d_shader_compile(const struct vkd3d_shader_compile_info *compile_info, diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 12edfae1..52196caf 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -548,6 +548,10 @@ enum vkd3d_shader_type VKD3D_SHADER_TYPE_GRAPHICS_COUNT,
VKD3D_SHADER_TYPE_COMPUTE = VKD3D_SHADER_TYPE_GRAPHICS_COUNT, + + VKD3D_SHADER_TYPE_EFFECT, + VKD3D_SHADER_TYPE_TEXTURE, + VKD3D_SHADER_TYPE_LIBRARY, VKD3D_SHADER_TYPE_COUNT, };
@@ -884,6 +888,8 @@ void vkd3d_shader_verror(struct vkd3d_shader_message_context *context, const str void vkd3d_shader_vwarning(struct vkd3d_shader_message_context *context, const struct vkd3d_shader_location *location, enum vkd3d_shader_error error, const char *format, va_list args) DECLSPEC_HIDDEN;
+void vkd3d_shader_dump_shader(enum vkd3d_shader_type type, const struct vkd3d_shader_code *shader) DECLSPEC_HIDDEN; + int shader_extract_from_dxbc(const void *dxbc, size_t dxbc_length, struct vkd3d_shader_message_context *message_context, const char *source_name, struct vkd3d_shader_desc *desc) DECLSPEC_HIDDEN; @@ -909,6 +915,9 @@ void vkd3d_compute_dxbc_checksum(const void *dxbc, size_t size, uint32_t checksu int preproc_lexer_parse(const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_code *out, struct vkd3d_shader_message_context *message_context) DECLSPEC_HIDDEN;
+int hlsl_compile_shader(const char *text, const struct vkd3d_shader_compile_info *compile_info, + struct vkd3d_shader_code *dxbc, struct vkd3d_shader_message_context *message_context) DECLSPEC_HIDDEN; + static inline enum vkd3d_shader_component_type vkd3d_component_type_from_data_type( enum vkd3d_data_type data_type) {
Signed-off-by: Matteo Bruni mbruni@codeweavers.com --- I haven't reviewed the patch line-by-line, although the contents look pretty familiar (including some questionable comments :D). BTW I suspect some of those implicit type conversions might not be correct anymore in newer shader models.
At any rate, this should be as good a starting point as any. The only high-level point I have is that it's a bit unfortunate to introduce a non-reentrant parser now, but it should be possible to fix that afterwards with somewhat contained modifications (and FWIW, reducing the number of changes compared to the version currently in Wine to the bare minimum is a good enough reason for keeping it IMHO).
I have one specific comment:
+/* 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.
FWIW, this was quite true at the time, but it's becoming less and less relevant. The current IR is much closer to e.g. SPIR-V than GLSL HIR and in particular, with the change to a flat IR, the way of manipulating the IR is also entirely different compared to Mesa GLSL HIR and related code. Probably the only "heritage" is some of the naming.
So I don't think the copyright blurb has much of a reason to be there anymore. That said, I don't think carrying the hunk over is a problem, or a big deal anyway.
On 1/26/21 11:45 AM, Matteo Bruni wrote:
Signed-off-by: Matteo Bruni mbruni@codeweavers.com
I haven't reviewed the patch line-by-line, although the contents look pretty familiar (including some questionable comments :D). BTW I suspect some of those implicit type conversions might not be correct anymore in newer shader models.
At any rate, this should be as good a starting point as any. The only high-level point I have is that it's a bit unfortunate to introduce a non-reentrant parser now, but it should be possible to fix that afterwards with somewhat contained modifications (and FWIW, reducing the number of changes compared to the version currently in Wine to the bare minimum is a good enough reason for keeping it IMHO).
Yes, that was largely my reasoning. I already have patches queued to convert the parser to be reëntrant, and I think they're not that painful.
I can see the advantage of making it prettier before moving it, but I also figured it made more sense to try to have as much of the history as possible on the vkd3d side.
I have one specific comment:
+/* 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.
FWIW, this was quite true at the time, but it's becoming less and less relevant. The current IR is much closer to e.g. SPIR-V than GLSL HIR and in particular, with the change to a flat IR, the way of manipulating the IR is also entirely different compared to Mesa GLSL HIR and related code. Probably the only "heritage" is some of the naming.
So I don't think the copyright blurb has much of a reason to be there anymore. That said, I don't think carrying the hunk over is a problem, or a big deal anyway.
Sure. It probably deserves a separate patch to remove it anyway.
(and I don't exactly want to be the one to touch copyright notices...)
On Mon, 25 Jan 2021 at 18:24, Zebediah Figura zfigura@codeweavers.com wrote:
@@ -126,9 +126,20 @@ libs/vkd3d-shader/preproc.tab.c libs/vkd3d-shader/preproc.tab.h &: libs/vkd3d-sh @$(MKDIR_P) libs/vkd3d-shader $(VKD3D_V_BISON)$(BISON) $(YFLAGS) -d -o libs/vkd3d-shader/preproc.tab.c $<
-BUILT_SOURCES += libs/vkd3d-shader/preproc.tab.h +libs/vkd3d-shader/hlsl.yy.c: $(srcdir)/libs/vkd3d-shader/hlsl.l
$(VKD3D_V_FLEX)$(FLEX) $(LFLAGS) -o $@ $<
+libs/vkd3d-shader/hlsl.tab.c libs/vkd3d-shader/hlsl.tab.h &: libs/vkd3d-shader/hlsl.y
$(VKD3D_V_BISON)$(BISON) $(YFLAGS) -d -o libs/vkd3d-shader/hlsl.tab.c $<
Should those have an @$(MKDIR_P) as well?
+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);
That's fine, but you should probably handle the new shader types introduced by this patch in shader_get_type_prefix().
On 1/26/21 1:17 PM, Henri Verbeet wrote:
On Mon, 25 Jan 2021 at 18:24, Zebediah Figura zfigura@codeweavers.com wrote:
@@ -126,9 +126,20 @@ libs/vkd3d-shader/preproc.tab.c libs/vkd3d-shader/preproc.tab.h &: libs/vkd3d-sh @$(MKDIR_P) libs/vkd3d-shader $(VKD3D_V_BISON)$(BISON) $(YFLAGS) -d -o libs/vkd3d-shader/preproc.tab.c $<
-BUILT_SOURCES += libs/vkd3d-shader/preproc.tab.h +libs/vkd3d-shader/hlsl.yy.c: $(srcdir)/libs/vkd3d-shader/hlsl.l
$(VKD3D_V_FLEX)$(FLEX) $(LFLAGS) -o $@ $<
+libs/vkd3d-shader/hlsl.tab.c libs/vkd3d-shader/hlsl.tab.h &: libs/vkd3d-shader/hlsl.y
$(VKD3D_V_BISON)$(BISON) $(YFLAGS) -d -o libs/vkd3d-shader/hlsl.tab.c $<
Should those have an @$(MKDIR_P) as well?
+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);
That's fine, but you should probably handle the new shader types introduced by this patch in shader_get_type_prefix().
Thanks for catching those; resent with both fixed.