/* * HLSL parser * * Copyright 2008 Stefan Dösinger * Copyright 2012 Matteo Bruni for CodeWeavers * Copyright 2019-2020 Zebediah Figura for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ %code requires { #include "hlsl.h" #include #define HLSL_YYLTYPE struct vkd3d_shader_location struct parse_fields { struct hlsl_struct_field *fields; size_t count, capacity; }; struct parse_parameter { struct hlsl_type *type; const char *name; struct hlsl_semantic semantic; struct hlsl_reg_reservation reg_reservation; uint32_t modifiers; }; struct parse_colon_attribute { struct hlsl_semantic semantic; struct hlsl_reg_reservation reg_reservation; }; struct parse_initializer { struct hlsl_ir_node **args; unsigned int args_count; struct hlsl_block *instrs; bool braces; }; struct parse_array_sizes { uint32_t *sizes; /* innermost first */ unsigned int count; }; struct parse_variable_def { struct list entry; struct vkd3d_shader_location loc; char *name; struct parse_array_sizes arrays; struct hlsl_semantic semantic; struct hlsl_reg_reservation reg_reservation; struct parse_initializer initializer; struct hlsl_type *basic_type; uint32_t modifiers; struct vkd3d_shader_location modifiers_loc; struct hlsl_state_block **state_blocks; unsigned int state_block_count; size_t state_block_capacity; }; struct parse_function { struct hlsl_ir_function_decl *decl; struct hlsl_func_parameters parameters; struct hlsl_semantic return_semantic; bool first; }; struct parse_if_body { struct hlsl_block *then_block; struct hlsl_block *else_block; }; 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 parse_attribute_list { unsigned int count; const struct hlsl_attribute **attrs; }; struct state_block_index { bool has_index; unsigned int index; }; } %code provides { int yylex(HLSL_YYSTYPE *yylval_param, HLSL_YYLTYPE *yylloc_param, void *yyscanner); } %code { #define YYLLOC_DEFAULT(cur, rhs, n) (cur) = YYRHSLOC(rhs, !!n) static void yyerror(YYLTYPE *loc, void *scanner, struct hlsl_ctx *ctx, const char *s) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "%s", s); } static struct hlsl_ir_node *node_from_block(struct hlsl_block *block) { return LIST_ENTRY(list_tail(&block->instrs), struct hlsl_ir_node, entry); } static struct hlsl_block *make_empty_block(struct hlsl_ctx *ctx) { struct hlsl_block *block; if ((block = hlsl_alloc(ctx, sizeof(*block)))) hlsl_block_init(block); return block; } static struct list *make_empty_list(struct hlsl_ctx *ctx) { struct list *list; if ((list = hlsl_alloc(ctx, sizeof(*list)))) list_init(list); return list; } static void destroy_block(struct hlsl_block *block) { if (!block) return; hlsl_block_cleanup(block); vkd3d_free(block); } static void destroy_switch_cases(struct list *cases) { hlsl_cleanup_ir_switch_cases(cases); vkd3d_free(cases); } static bool hlsl_types_are_componentwise_compatible(struct hlsl_ctx *ctx, struct hlsl_type *src, struct hlsl_type *dst) { unsigned int k, count = hlsl_type_component_count(dst); if (count > hlsl_type_component_count(src)) return false; for (k = 0; k < count; ++k) { struct hlsl_type *src_comp_type, *dst_comp_type; src_comp_type = hlsl_type_get_component_type(ctx, src, k); dst_comp_type = hlsl_type_get_component_type(ctx, dst, k); if ((src_comp_type->class != HLSL_CLASS_SCALAR || dst_comp_type->class != HLSL_CLASS_SCALAR) && !hlsl_types_are_equal(src_comp_type, dst_comp_type)) return false; } return true; } static bool hlsl_types_are_componentwise_equal(struct hlsl_ctx *ctx, struct hlsl_type *src, struct hlsl_type *dst) { unsigned int k, count = hlsl_type_component_count(src); if (count != hlsl_type_component_count(dst)) return false; for (k = 0; k < count; ++k) { struct hlsl_type *src_comp_type, *dst_comp_type; src_comp_type = hlsl_type_get_component_type(ctx, src, k); dst_comp_type = hlsl_type_get_component_type(ctx, dst, k); if (!hlsl_types_are_equal(src_comp_type, dst_comp_type)) return false; } return true; } static bool type_contains_only_numerics(const struct hlsl_type *type) { unsigned int i; if (type->class == HLSL_CLASS_ARRAY) return type_contains_only_numerics(type->e.array.type); if (type->class == HLSL_CLASS_STRUCT) { for (i = 0; i < type->e.record.field_count; ++i) { if (!type_contains_only_numerics(type->e.record.fields[i].type)) return false; } return true; } return hlsl_is_numeric_type(type); } static bool explicit_compatible_data_types(struct hlsl_ctx *ctx, struct hlsl_type *src, struct hlsl_type *dst) { if (hlsl_is_numeric_type(src) && src->dimx == 1 && src->dimy == 1 && type_contains_only_numerics(dst)) return true; if (src->class == HLSL_CLASS_MATRIX && dst->class == HLSL_CLASS_MATRIX && src->dimx >= dst->dimx && src->dimy >= dst->dimy) return true; if ((src->class == HLSL_CLASS_MATRIX && src->dimx > 1 && src->dimy > 1) && hlsl_type_component_count(src) != hlsl_type_component_count(dst)) return false; if ((dst->class == HLSL_CLASS_MATRIX && dst->dimy > 1) && hlsl_type_component_count(src) != hlsl_type_component_count(dst)) return false; return hlsl_types_are_componentwise_compatible(ctx, src, dst); } static bool implicit_compatible_data_types(struct hlsl_ctx *ctx, struct hlsl_type *src, struct hlsl_type *dst) { if (hlsl_is_numeric_type(src) != hlsl_is_numeric_type(dst)) return false; if (hlsl_is_numeric_type(src)) { /* Scalar vars can be converted to any other numeric data type */ if (src->dimx == 1 && src->dimy == 1) return true; /* The other way around is true too */ if (dst->dimx == 1 && dst->dimy == 1) return true; if (src->class == HLSL_CLASS_MATRIX || dst->class == HLSL_CLASS_MATRIX) { if (src->class == HLSL_CLASS_MATRIX && dst->class == HLSL_CLASS_MATRIX) return src->dimx >= dst->dimx && src->dimy >= dst->dimy; /* Matrix-vector conversion is apparently allowed if they have * the same components count, or if the matrix is 1xN or Nx1 * and we are reducing the component count */ if (src->class == HLSL_CLASS_VECTOR || dst->class == HLSL_CLASS_VECTOR) { if (hlsl_type_component_count(src) == hlsl_type_component_count(dst)) return true; if ((src->class == HLSL_CLASS_VECTOR || src->dimx == 1 || src->dimy == 1) && (dst->class == HLSL_CLASS_VECTOR || dst->dimx == 1 || dst->dimy == 1)) return hlsl_type_component_count(src) >= hlsl_type_component_count(dst); } return false; } else { return src->dimx >= dst->dimx; } } return hlsl_types_are_componentwise_equal(ctx, src, dst); } static void check_condition_type(struct hlsl_ctx *ctx, const struct hlsl_ir_node *cond) { const struct hlsl_type *type = cond->data_type; if (type->class > HLSL_CLASS_LAST_NUMERIC || type->dimx > 1 || type->dimy > 1) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, type))) hlsl_error(ctx, &cond->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Condition type '%s' is not a scalar numeric type.", string->buffer); hlsl_release_string_buffer(ctx, string); } } static struct hlsl_ir_node *add_cast(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *node, struct hlsl_type *dst_type, const struct vkd3d_shader_location *loc) { struct hlsl_type *src_type = node->data_type; struct hlsl_ir_node *cast; if (hlsl_types_are_equal(src_type, dst_type)) return node; if (src_type->class > HLSL_CLASS_VECTOR || dst_type->class > HLSL_CLASS_VECTOR) { unsigned int src_comp_count = hlsl_type_component_count(src_type); unsigned int dst_comp_count = hlsl_type_component_count(dst_type); struct hlsl_deref var_deref; bool broadcast, matrix_cast; struct hlsl_ir_load *load; struct hlsl_ir_var *var; unsigned int dst_idx; broadcast = hlsl_is_numeric_type(src_type) && src_type->dimx == 1 && src_type->dimy == 1; matrix_cast = !broadcast && dst_comp_count != src_comp_count && src_type->class == HLSL_CLASS_MATRIX && dst_type->class == HLSL_CLASS_MATRIX; assert(src_comp_count >= dst_comp_count || broadcast); if (matrix_cast) { assert(dst_type->dimx <= src_type->dimx); assert(dst_type->dimy <= src_type->dimy); } if (!(var = hlsl_new_synthetic_var(ctx, "cast", dst_type, loc))) return NULL; hlsl_init_simple_deref_from_var(&var_deref, var); for (dst_idx = 0; dst_idx < dst_comp_count; ++dst_idx) { struct hlsl_ir_node *component_load; struct hlsl_type *dst_comp_type; struct hlsl_block store_block; unsigned int src_idx; if (broadcast) { src_idx = 0; } else if (matrix_cast) { unsigned int x = dst_idx % dst_type->dimx, y = dst_idx / dst_type->dimx; src_idx = y * src_type->dimx + x; } else { src_idx = dst_idx; } dst_comp_type = hlsl_type_get_component_type(ctx, dst_type, dst_idx); if (!(component_load = hlsl_add_load_component(ctx, block, node, src_idx, loc))) return NULL; if (!(cast = hlsl_new_cast(ctx, component_load, dst_comp_type, loc))) return NULL; hlsl_block_add_instr(block, cast); if (!hlsl_new_store_component(ctx, &store_block, &var_deref, dst_idx, cast)) return NULL; hlsl_block_add_block(block, &store_block); } if (!(load = hlsl_new_var_load(ctx, var, loc))) return NULL; hlsl_block_add_instr(block, &load->node); return &load->node; } else { if (!(cast = hlsl_new_cast(ctx, node, dst_type, loc))) return NULL; hlsl_block_add_instr(block, cast); return cast; } } static struct hlsl_ir_node *add_implicit_conversion(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *node, struct hlsl_type *dst_type, const struct vkd3d_shader_location *loc) { struct hlsl_type *src_type = node->data_type; if (hlsl_types_are_equal(src_type, dst_type)) return node; if (!implicit_compatible_data_types(ctx, src_type, dst_type)) { struct vkd3d_string_buffer *src_string, *dst_string; src_string = hlsl_type_to_string(ctx, src_type); dst_string = hlsl_type_to_string(ctx, dst_type); if (src_string && dst_string) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Can't implicitly convert from %s to %s.", src_string->buffer, dst_string->buffer); hlsl_release_string_buffer(ctx, src_string); hlsl_release_string_buffer(ctx, dst_string); return NULL; } if (dst_type->dimx * dst_type->dimy < src_type->dimx * src_type->dimy && ctx->warn_implicit_truncation) hlsl_warning(ctx, loc, VKD3D_SHADER_WARNING_HLSL_IMPLICIT_TRUNCATION, "Implicit truncation of %s type.", src_type->class == HLSL_CLASS_VECTOR ? "vector" : "matrix"); return add_cast(ctx, block, node, dst_type, loc); } static uint32_t add_modifiers(struct hlsl_ctx *ctx, uint32_t modifiers, uint32_t mod, const struct vkd3d_shader_location *loc) { if (modifiers & mod) { struct vkd3d_string_buffer *string; if ((string = hlsl_modifiers_to_string(ctx, mod))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, "Modifier '%s' was already specified.", string->buffer); hlsl_release_string_buffer(ctx, string); return modifiers; } return modifiers | mod; } static bool append_conditional_break(struct hlsl_ctx *ctx, struct hlsl_block *cond_block) { struct hlsl_ir_node *condition, *cast, *not, *iff, *jump; struct hlsl_block then_block; struct hlsl_type *bool_type; /* E.g. "for (i = 0; ; ++i)". */ if (list_empty(&cond_block->instrs)) return true; condition = node_from_block(cond_block); check_condition_type(ctx, condition); bool_type = hlsl_get_scalar_type(ctx, HLSL_TYPE_BOOL); if (!(cast = hlsl_new_cast(ctx, condition, bool_type, &condition->loc))) return false; hlsl_block_add_instr(cond_block, cast); if (!(not = hlsl_new_unary_expr(ctx, HLSL_OP1_LOGIC_NOT, cast, &condition->loc))) return false; hlsl_block_add_instr(cond_block, not); hlsl_block_init(&then_block); if (!(jump = hlsl_new_jump(ctx, HLSL_IR_JUMP_BREAK, NULL, &condition->loc))) return false; hlsl_block_add_instr(&then_block, jump); if (!(iff = hlsl_new_if(ctx, not, &then_block, NULL, &condition->loc))) return false; hlsl_block_add_instr(cond_block, iff); return true; } enum loop_type { LOOP_FOR, LOOP_WHILE, LOOP_DO_WHILE }; static bool attribute_list_has_duplicates(const struct parse_attribute_list *attrs) { unsigned int i, j; for (i = 0; i < attrs->count; ++i) { for (j = i + 1; j < attrs->count; ++j) { if (!strcmp(attrs->attrs[i]->name, attrs->attrs[j]->name)) return true; } } return false; } static void resolve_loop_continue(struct hlsl_ctx *ctx, struct hlsl_block *block, enum loop_type type, struct hlsl_block *cond, struct hlsl_block *iter) { struct hlsl_ir_node *instr, *next; LIST_FOR_EACH_ENTRY_SAFE(instr, next, &block->instrs, struct hlsl_ir_node, entry) { if (instr->type == HLSL_IR_IF) { struct hlsl_ir_if *iff = hlsl_ir_if(instr); resolve_loop_continue(ctx, &iff->then_block, type, cond, iter); resolve_loop_continue(ctx, &iff->else_block, type, cond, iter); } else if (instr->type == HLSL_IR_JUMP) { struct hlsl_ir_jump *jump = hlsl_ir_jump(instr); struct hlsl_block cond_block; if (jump->type != HLSL_IR_JUMP_UNRESOLVED_CONTINUE) continue; if (type == LOOP_DO_WHILE) { if (!hlsl_clone_block(ctx, &cond_block, cond)) return; if (!append_conditional_break(ctx, &cond_block)) { hlsl_block_cleanup(&cond_block); return; } list_move_before(&instr->entry, &cond_block.instrs); } else if (type == LOOP_FOR) { if (!hlsl_clone_block(ctx, &cond_block, iter)) return; list_move_before(&instr->entry, &cond_block.instrs); } jump->type = HLSL_IR_JUMP_CONTINUE; } } } static void check_loop_attributes(struct hlsl_ctx *ctx, const struct parse_attribute_list *attributes, const struct vkd3d_shader_location *loc) { bool has_unroll = false, has_loop = false, has_fastopt = false; unsigned int i; for (i = 0; i < attributes->count; ++i) { const char *name = attributes->attrs[i]->name; has_loop |= !strcmp(name, "loop"); has_unroll |= !strcmp(name, "unroll"); has_fastopt |= !strcmp(name, "fastopt"); } if (has_unroll && has_loop) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Unroll attribute can't be used with 'loop' attribute."); if (has_unroll && has_fastopt) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Unroll attribute can't be used with 'fastopt' attribute."); } static struct hlsl_block *create_loop(struct hlsl_ctx *ctx, enum loop_type type, const struct parse_attribute_list *attributes, struct hlsl_block *init, struct hlsl_block *cond, struct hlsl_block *iter, struct hlsl_block *body, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *loop; unsigned int i; if (attribute_list_has_duplicates(attributes)) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Found duplicate attribute."); check_loop_attributes(ctx, attributes, loc); /* Ignore unroll(0) attribute, and any invalid attribute. */ for (i = 0; i < attributes->count; ++i) { const struct hlsl_attribute *attr = attributes->attrs[i]; if (!strcmp(attr->name, "unroll")) { if (attr->args_count) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_NOT_IMPLEMENTED, "Unroll attribute with iteration count."); } else { hlsl_warning(ctx, loc, VKD3D_SHADER_ERROR_HLSL_NOT_IMPLEMENTED, "Loop unrolling is not implemented."); } } else if (!strcmp(attr->name, "loop")) { /* TODO: this attribute will be used to disable unrolling, once it's implememented. */ } else if (!strcmp(attr->name, "fastopt") || !strcmp(attr->name, "allow_uav_condition")) { hlsl_fixme(ctx, loc, "Unhandled attribute '%s'.", attr->name); } else { hlsl_warning(ctx, loc, VKD3D_SHADER_WARNING_HLSL_UNKNOWN_ATTRIBUTE, "Unrecognized attribute '%s'.", attr->name); } } resolve_loop_continue(ctx, body, type, cond, iter); if (!init && !(init = make_empty_block(ctx))) goto oom; if (!append_conditional_break(ctx, cond)) goto oom; if (iter) hlsl_block_add_block(body, iter); if (type == LOOP_DO_WHILE) list_move_tail(&body->instrs, &cond->instrs); else list_move_head(&body->instrs, &cond->instrs); if (!(loop = hlsl_new_loop(ctx, body, loc))) goto oom; hlsl_block_add_instr(init, loop); destroy_block(cond); destroy_block(body); destroy_block(iter); return init; oom: destroy_block(init); destroy_block(cond); destroy_block(iter); destroy_block(body); return NULL; } static unsigned int initializer_size(const struct parse_initializer *initializer) { unsigned int count = 0, i; for (i = 0; i < initializer->args_count; ++i) { count += hlsl_type_component_count(initializer->args[i]->data_type); } return count; } static void cleanup_parse_attribute_list(struct parse_attribute_list *attr_list) { unsigned int i = 0; assert(attr_list); for (i = 0; i < attr_list->count; ++i) hlsl_free_attribute((struct hlsl_attribute *) attr_list->attrs[i]); vkd3d_free(attr_list->attrs); } static void free_parse_initializer(struct parse_initializer *initializer) { destroy_block(initializer->instrs); vkd3d_free(initializer->args); } static struct hlsl_ir_node *get_swizzle(struct hlsl_ctx *ctx, struct hlsl_ir_node *value, const char *swizzle, struct vkd3d_shader_location *loc) { unsigned int len = strlen(swizzle), component = 0; unsigned int i, set, swiz = 0; bool valid; if (value->data_type->class == HLSL_CLASS_MATRIX) { /* Matrix swizzle */ bool m_swizzle; unsigned int inc, x, y; if (len < 3 || swizzle[0] != '_') return NULL; m_swizzle = swizzle[1] == 'm'; inc = m_swizzle ? 4 : 3; if (len % inc || len > inc * 4) return NULL; for (i = 0; i < len; i += inc) { if (swizzle[i] != '_') return NULL; if (m_swizzle) { if (swizzle[i + 1] != 'm') return NULL; y = swizzle[i + 2] - '0'; x = swizzle[i + 3] - '0'; } else { y = swizzle[i + 1] - '1'; x = swizzle[i + 2] - '1'; } if (x >= value->data_type->dimx || y >= value->data_type->dimy) return NULL; swiz |= (y << 4 | x) << component * 8; component++; } return hlsl_new_swizzle(ctx, swiz, component, value, loc); } /* Vector swizzle */ if (len > 4) return NULL; for (set = 0; set < 2; ++set) { valid = true; component = 0; for (i = 0; i < len; ++i) { char c[2][4] = {{'x', 'y', 'z', 'w'}, {'r', 'g', 'b', 'a'}}; unsigned int s = 0; for (s = 0; s < 4; ++s) { if (swizzle[i] == c[set][s]) break; } if (s == 4) { valid = false; break; } if (s >= value->data_type->dimx) return NULL; swiz |= s << component * 2; component++; } if (valid) return hlsl_new_swizzle(ctx, swiz, component, value, loc); } return NULL; } static bool add_return(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *return_value, const struct vkd3d_shader_location *loc) { struct hlsl_type *return_type = ctx->cur_function->return_type; struct hlsl_ir_node *jump; if (ctx->cur_function->return_var) { if (return_value) { struct hlsl_ir_node *store; if (!(return_value = add_implicit_conversion(ctx, block, return_value, return_type, loc))) return false; if (!(store = hlsl_new_simple_store(ctx, ctx->cur_function->return_var, return_value))) return false; list_add_after(&return_value->entry, &store->entry); } else { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RETURN, "Non-void functions must return a value."); return false; } } else { if (return_value) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RETURN, "Void functions cannot return a value."); } if (!(jump = hlsl_new_jump(ctx, HLSL_IR_JUMP_RETURN, NULL, loc))) return false; hlsl_block_add_instr(block, jump); return true; } struct hlsl_ir_node *hlsl_add_load_component(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *var_instr, unsigned int comp, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *load, *store; struct hlsl_block load_block; struct hlsl_ir_var *var; struct hlsl_deref src; if (!(var = hlsl_new_synthetic_var(ctx, "deref", var_instr->data_type, &var_instr->loc))) return NULL; if (!(store = hlsl_new_simple_store(ctx, var, var_instr))) return NULL; hlsl_block_add_instr(block, store); hlsl_init_simple_deref_from_var(&src, var); if (!(load = hlsl_new_load_component(ctx, &load_block, &src, comp, loc))) return NULL; hlsl_block_add_block(block, &load_block); return load; } static bool add_record_access(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *record, unsigned int idx, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *index, *c; assert(idx < record->data_type->e.record.field_count); if (!(c = hlsl_new_uint_constant(ctx, idx, loc))) return false; hlsl_block_add_instr(block, c); if (!(index = hlsl_new_index(ctx, record, c, loc))) return false; hlsl_block_add_instr(block, index); return true; } static struct hlsl_ir_node *add_binary_arithmetic_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2, const struct vkd3d_shader_location *loc); static bool add_array_access(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *array, struct hlsl_ir_node *index, const struct vkd3d_shader_location *loc) { const struct hlsl_type *expr_type = array->data_type, *index_type = index->data_type; struct hlsl_ir_node *return_index, *cast; if ((expr_type->class == HLSL_CLASS_TEXTURE || expr_type->class == HLSL_CLASS_UAV) && expr_type->sampler_dim != HLSL_SAMPLER_DIM_GENERIC) { unsigned int dim_count = hlsl_sampler_dim_count(expr_type->sampler_dim); if (index_type->class > HLSL_CLASS_VECTOR || index_type->dimx != dim_count) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, expr_type))) hlsl_error(ctx, &index->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Array index of type '%s' must be of type 'uint%u'.", string->buffer, dim_count); hlsl_release_string_buffer(ctx, string); return false; } if (!(index = add_implicit_conversion(ctx, block, index, hlsl_get_vector_type(ctx, HLSL_TYPE_UINT, dim_count), &index->loc))) return false; if (!(return_index = hlsl_new_index(ctx, array, index, loc))) return false; hlsl_block_add_instr(block, return_index); return true; } if (index_type->class != HLSL_CLASS_SCALAR) { hlsl_error(ctx, &index->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Array index is not scalar."); return false; } if (!(cast = hlsl_new_cast(ctx, index, hlsl_get_scalar_type(ctx, HLSL_TYPE_UINT), &index->loc))) return false; hlsl_block_add_instr(block, cast); index = cast; if (expr_type->class != HLSL_CLASS_ARRAY && expr_type->class != HLSL_CLASS_VECTOR && expr_type->class != HLSL_CLASS_MATRIX) { if (expr_type->class == HLSL_CLASS_SCALAR) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_INDEX, "Scalar expressions cannot be array-indexed."); else hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_INDEX, "Expression cannot be array-indexed."); return false; } if (!(return_index = hlsl_new_index(ctx, array, index, loc))) return false; hlsl_block_add_instr(block, return_index); return true; } static const struct hlsl_struct_field *get_struct_field(const struct hlsl_struct_field *fields, size_t count, const char *name) { size_t i; for (i = 0; i < count; ++i) { if (!strcmp(fields[i].name, name)) return &fields[i]; } return NULL; } static struct hlsl_type *apply_type_modifiers(struct hlsl_ctx *ctx, struct hlsl_type *type, uint32_t *modifiers, bool force_majority, const struct vkd3d_shader_location *loc) { unsigned int default_majority = 0; struct hlsl_type *new_type; if (!(*modifiers & HLSL_MODIFIERS_MAJORITY_MASK) && !(type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK) && type->class == HLSL_CLASS_MATRIX) { if (!(default_majority = ctx->matrix_majority) && force_majority) default_majority = HLSL_MODIFIER_COLUMN_MAJOR; } else if (type->class != HLSL_CLASS_MATRIX && (*modifiers & HLSL_MODIFIERS_MAJORITY_MASK)) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, "'row_major' and 'column_major' modifiers are only allowed for matrices."); } if (!default_majority && !(*modifiers & HLSL_TYPE_MODIFIERS_MASK)) return type; if (!(new_type = hlsl_type_clone(ctx, type, default_majority, *modifiers & HLSL_TYPE_MODIFIERS_MASK))) return NULL; *modifiers &= ~HLSL_TYPE_MODIFIERS_MASK; if ((new_type->modifiers & HLSL_MODIFIER_ROW_MAJOR) && (new_type->modifiers & HLSL_MODIFIER_COLUMN_MAJOR)) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, "'row_major' and 'column_major' modifiers are mutually exclusive."); return new_type; } static void free_parse_variable_def(struct parse_variable_def *v) { free_parse_initializer(&v->initializer); vkd3d_free(v->arrays.sizes); vkd3d_free(v->name); hlsl_cleanup_semantic(&v->semantic); assert(!v->state_blocks); vkd3d_free(v); } static bool gen_struct_fields(struct hlsl_ctx *ctx, struct parse_fields *fields, struct hlsl_type *type, uint32_t modifiers, struct list *defs) { struct parse_variable_def *v, *v_next; size_t i = 0; if (type->class == HLSL_CLASS_MATRIX) assert(type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK); memset(fields, 0, sizeof(*fields)); fields->count = list_count(defs); if (!hlsl_array_reserve(ctx, (void **)&fields->fields, &fields->capacity, fields->count, sizeof(*fields->fields))) return false; LIST_FOR_EACH_ENTRY_SAFE(v, v_next, defs, struct parse_variable_def, entry) { struct hlsl_struct_field *field = &fields->fields[i++]; bool unbounded_res_array = false; unsigned int k; field->type = type; if (hlsl_version_ge(ctx, 5, 1) && hlsl_type_is_resource(type)) { for (k = 0; k < v->arrays.count; ++k) unbounded_res_array |= (v->arrays.sizes[k] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT); } if (unbounded_res_array) { if (v->arrays.count == 1) { hlsl_fixme(ctx, &v->loc, "Unbounded resource arrays as struct fields."); free_parse_variable_def(v); vkd3d_free(field); continue; } else { hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Unbounded resource arrays cannot be multi-dimensional."); } } else { for (k = 0; k < v->arrays.count; ++k) { if (v->arrays.sizes[k] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT) { hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Implicit size arrays not allowed in struct fields."); } field->type = hlsl_new_array_type(ctx, field->type, v->arrays.sizes[k]); } } if (hlsl_version_ge(ctx, 5, 1) && field->type->class == HLSL_CLASS_ARRAY && hlsl_type_is_resource(field->type)) hlsl_fixme(ctx, &v->loc, "Shader model 5.1+ resource array."); vkd3d_free(v->arrays.sizes); field->loc = v->loc; field->name = v->name; field->semantic = v->semantic; field->storage_modifiers = modifiers; if (v->initializer.args_count) { hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Illegal initializer on a struct field."); free_parse_initializer(&v->initializer); } if (v->reg_reservation.offset_type) hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "packoffset() is not allowed inside struct definitions."); vkd3d_free(v); } vkd3d_free(defs); return true; } static bool add_typedef(struct hlsl_ctx *ctx, struct hlsl_type *const orig_type, struct list *list) { struct parse_variable_def *v, *v_next; struct hlsl_type *type; unsigned int i; bool ret; LIST_FOR_EACH_ENTRY_SAFE(v, v_next, list, struct parse_variable_def, entry) { if (!v->arrays.count) { if (!(type = hlsl_type_clone(ctx, orig_type, 0, 0))) { free_parse_variable_def(v); continue; } } else { uint32_t var_modifiers = 0; if (!(type = apply_type_modifiers(ctx, orig_type, &var_modifiers, true, &v->loc))) { free_parse_variable_def(v); continue; } } ret = true; for (i = 0; i < v->arrays.count; ++i) { if (v->arrays.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT) { hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Implicit size arrays not allowed in typedefs."); } if (!(type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]))) { free_parse_variable_def(v); ret = false; break; } } if (!ret) continue; vkd3d_free(v->arrays.sizes); vkd3d_free((void *)type->name); type->name = v->name; ret = hlsl_scope_add_type(ctx->cur_scope, type); if (!ret) hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "Type '%s' is already defined.", v->name); free_parse_initializer(&v->initializer); vkd3d_free(v); } vkd3d_free(list); return true; } static bool add_func_parameter(struct hlsl_ctx *ctx, struct hlsl_func_parameters *parameters, struct parse_parameter *param, const struct vkd3d_shader_location *loc) { struct hlsl_ir_var *var; if (param->type->class == HLSL_CLASS_MATRIX) assert(param->type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK); if ((param->modifiers & HLSL_STORAGE_OUT) && (param->modifiers & HLSL_STORAGE_UNIFORM)) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, "Parameter '%s' is declared as both \"out\" and \"uniform\".", param->name); if (param->reg_reservation.offset_type) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "packoffset() is not allowed on function parameters."); if (!(var = hlsl_new_var(ctx, param->name, param->type, loc, ¶m->semantic, param->modifiers, ¶m->reg_reservation))) return false; var->is_param = 1; if (!hlsl_add_var(ctx, var, false)) { hlsl_free_var(var); return false; } if (!hlsl_array_reserve(ctx, (void **)¶meters->vars, ¶meters->capacity, parameters->count + 1, sizeof(*parameters->vars))) return false; parameters->vars[parameters->count++] = var; return true; } static bool add_pass(struct hlsl_ctx *ctx, const char *name, struct hlsl_scope *annotations, struct hlsl_state_block *state_block, const struct vkd3d_shader_location *loc) { struct hlsl_ir_var *var; struct hlsl_type *type; type = hlsl_get_type(ctx->globals, "pass", false, false); if (!(var = hlsl_new_var(ctx, name, type, loc, NULL, 0, NULL))) return false; var->annotations = annotations; var->state_blocks = hlsl_alloc(ctx, sizeof(*var->state_blocks)); var->state_blocks[0] = state_block; var->state_block_count = 1; var->state_block_capacity = 1; if (!hlsl_add_var(ctx, var, false)) { struct hlsl_ir_var *old = hlsl_get_var(ctx->cur_scope, var->name); hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "Identifier \"%s\" was already declared in this scope.", var->name); hlsl_note(ctx, &old->loc, VKD3D_SHADER_LOG_ERROR, "\"%s\" was previously declared here.", old->name); hlsl_free_var(var); return false; } return true; } static bool add_technique(struct hlsl_ctx *ctx, const char *name, struct hlsl_scope *scope, struct hlsl_scope *annotations, const char *typename, const struct vkd3d_shader_location *loc) { struct hlsl_ir_var *var; struct hlsl_type *type; type = hlsl_get_type(ctx->globals, typename, false, false); if (!(var = hlsl_new_var(ctx, name, type, loc, NULL, 0, NULL))) return false; var->scope = scope; var->annotations = annotations; if (!hlsl_add_var(ctx, var, false)) { struct hlsl_ir_var *old = hlsl_get_var(ctx->cur_scope, var->name); hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "Identifier \"%s\" was already declared in this scope.", var->name); hlsl_note(ctx, &old->loc, VKD3D_SHADER_LOG_ERROR, "\"%s\" was previously declared here.", old->name); hlsl_free_var(var); return false; } return true; } static bool add_effect_group(struct hlsl_ctx *ctx, const char *name, struct hlsl_scope *scope, struct hlsl_scope *annotations, const struct vkd3d_shader_location *loc) { struct hlsl_ir_var *var; struct hlsl_type *type; type = hlsl_get_type(ctx->globals, "fxgroup", false, false); if (!(var = hlsl_new_var(ctx, name, type, loc, NULL, 0, NULL))) return false; var->scope = scope; var->annotations = annotations; if (!hlsl_add_var(ctx, var, false)) { struct hlsl_ir_var *old = hlsl_get_var(ctx->cur_scope, var->name); hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "Identifier \"%s\" was already declared in this scope.", var->name); hlsl_note(ctx, &old->loc, VKD3D_SHADER_LOG_ERROR, "\"%s\" was previously declared here.", old->name); hlsl_free_var(var); return false; } return true; } static bool parse_reservation_index(struct hlsl_ctx *ctx, const char *string, unsigned int bracket_offset, struct hlsl_reg_reservation *reservation) { char *endptr; reservation->reg_type = ascii_tolower(string[0]); /* Prior to SM5.1, fxc simply ignored bracket offsets for 'b' types. */ if (reservation->reg_type == 'b' && hlsl_version_lt(ctx, 5, 1)) { bracket_offset = 0; } if (string[1] == '\0') { reservation->reg_index = bracket_offset; return true; } reservation->reg_index = strtoul(string + 1, &endptr, 10) + bracket_offset; if (*endptr) { /* fxc for SM >= 4 treats all parse failures for 'b' types as successes, * setting index to -1. It will later fail while validating slot limits. */ if (reservation->reg_type == 'b' && hlsl_version_ge(ctx, 4, 0)) { reservation->reg_index = -1; return true; } /* All other types tolerate leftover characters. */ if (endptr == string + 1) return false; } return true; } static bool parse_reservation_space(const char *string, uint32_t *space) { return !ascii_strncasecmp(string, "space", 5) && sscanf(string + 5, "%u", space); } static struct hlsl_reg_reservation parse_packoffset(struct hlsl_ctx *ctx, const char *reg_string, const char *swizzle, const struct vkd3d_shader_location *loc) { struct hlsl_reg_reservation reservation = {0}; char *endptr; if (hlsl_version_lt(ctx, 4, 0)) return reservation; reservation.offset_index = strtoul(reg_string + 1, &endptr, 10); if (*endptr) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid packoffset() syntax."); return reservation; } reservation.offset_type = ascii_tolower(reg_string[0]); if (reservation.offset_type != 'c') { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Only 'c' registers are allowed in packoffset()."); return reservation; } reservation.offset_index *= 4; if (swizzle) { if (strlen(swizzle) != 1) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid packoffset() component \"%s\".", swizzle); if (swizzle[0] == 'x' || swizzle[0] == 'r') reservation.offset_index += 0; else if (swizzle[0] == 'y' || swizzle[0] == 'g') reservation.offset_index += 1; else if (swizzle[0] == 'z' || swizzle[0] == 'b') reservation.offset_index += 2; else if (swizzle[0] == 'w' || swizzle[0] == 'a') reservation.offset_index += 3; else hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid packoffset() component \"%s\".", swizzle); } return reservation; } static struct hlsl_block *make_block(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr) { struct hlsl_block *block; if (!(block = make_empty_block(ctx))) { hlsl_free_instr(instr); return NULL; } hlsl_block_add_instr(block, instr); return block; } static union hlsl_constant_value_component evaluate_static_expression(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_type *dst_type, const struct vkd3d_shader_location *loc) { union hlsl_constant_value_component ret = {0}; struct hlsl_ir_constant *constant; struct hlsl_ir_node *node; struct hlsl_block expr; struct hlsl_src src; LIST_FOR_EACH_ENTRY(node, &block->instrs, struct hlsl_ir_node, entry) { switch (node->type) { case HLSL_IR_CONSTANT: case HLSL_IR_EXPR: case HLSL_IR_SWIZZLE: case HLSL_IR_LOAD: case HLSL_IR_INDEX: continue; case HLSL_IR_STORE: if (hlsl_ir_store(node)->lhs.var->is_synthetic) break; /* fall-through */ case HLSL_IR_CALL: case HLSL_IR_IF: case HLSL_IR_LOOP: case HLSL_IR_JUMP: case HLSL_IR_RESOURCE_LOAD: case HLSL_IR_RESOURCE_STORE: case HLSL_IR_SWITCH: case HLSL_IR_STATEBLOCK_CONSTANT: hlsl_error(ctx, &node->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Expected literal expression."); break; } } if (!hlsl_clone_block(ctx, &expr, &ctx->static_initializers)) return ret; hlsl_block_add_block(&expr, block); if (!add_implicit_conversion(ctx, &expr, node_from_block(&expr), dst_type, loc)) { hlsl_block_cleanup(&expr); return ret; } /* Wrap the node into a src to allow the reference to survive the multiple const passes. */ hlsl_src_from_node(&src, node_from_block(&expr)); hlsl_run_const_passes(ctx, &expr); node = src.node; hlsl_src_remove(&src); if (node->type == HLSL_IR_CONSTANT) { constant = hlsl_ir_constant(node); ret = constant->value.u[0]; } else { hlsl_error(ctx, &node->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Failed to evaluate constant expression."); } hlsl_block_cleanup(&expr); return ret; } static unsigned int evaluate_static_expression_as_uint(struct hlsl_ctx *ctx, struct hlsl_block *block, const struct vkd3d_shader_location *loc) { union hlsl_constant_value_component res; res = evaluate_static_expression(ctx, block, hlsl_get_scalar_type(ctx, HLSL_TYPE_UINT), loc); return res.u; } static bool expr_compatible_data_types(struct hlsl_type *t1, struct hlsl_type *t2) { /* 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->class == HLSL_CLASS_VECTOR && t2->class == HLSL_CLASS_VECTOR) return true; if (t1->class == HLSL_CLASS_MATRIX || t2->class == 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->class == HLSL_CLASS_VECTOR || t2->class == HLSL_CLASS_VECTOR) { if (hlsl_type_component_count(t1) == hlsl_type_component_count(t2)) return true; return (t1->class == HLSL_CLASS_MATRIX && (t1->dimx == 1 || t1->dimy == 1)) || (t2->class == HLSL_CLASS_MATRIX && (t2->dimx == 1 || t2->dimy == 1)); } /* Both matrices */ if ((t1->dimx >= t2->dimx && t1->dimy >= t2->dimy) || (t1->dimx <= t2->dimx && t1->dimy <= t2->dimy)) return true; } return false; } static enum hlsl_base_type expr_common_base_type(enum hlsl_base_type t1, enum hlsl_base_type t2) { if (t1 == t2) return t1 == HLSL_TYPE_BOOL ? HLSL_TYPE_INT : t1; if (t1 == HLSL_TYPE_DOUBLE || t2 == HLSL_TYPE_DOUBLE) return HLSL_TYPE_DOUBLE; if (t1 == HLSL_TYPE_FLOAT || t2 == HLSL_TYPE_FLOAT || t1 == HLSL_TYPE_HALF || t2 == HLSL_TYPE_HALF) return HLSL_TYPE_FLOAT; if (t1 == HLSL_TYPE_UINT || t2 == HLSL_TYPE_UINT) return HLSL_TYPE_UINT; return HLSL_TYPE_INT; } static bool expr_common_shape(struct hlsl_ctx *ctx, struct hlsl_type *t1, struct hlsl_type *t2, const struct vkd3d_shader_location *loc, enum hlsl_type_class *type, unsigned int *dimx, unsigned int *dimy) { if (!hlsl_is_numeric_type(t1)) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, t1))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Expression of type \"%s\" cannot be used in a numeric expression.", string->buffer); hlsl_release_string_buffer(ctx, string); return false; } if (!hlsl_is_numeric_type(t2)) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, t2))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Expression of type \"%s\" cannot be used in a numeric expression.", string->buffer); hlsl_release_string_buffer(ctx, string); return false; } if (!expr_compatible_data_types(t1, t2)) { struct vkd3d_string_buffer *t1_string = hlsl_type_to_string(ctx, t1); struct vkd3d_string_buffer *t2_string = hlsl_type_to_string(ctx, t2); if (t1_string && t2_string) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Expression data types \"%s\" and \"%s\" are incompatible.", t1_string->buffer, t2_string->buffer); hlsl_release_string_buffer(ctx, t1_string); hlsl_release_string_buffer(ctx, t2_string); return false; } if (t1->dimx == 1 && t1->dimy == 1) { *type = t2->class; *dimx = t2->dimx; *dimy = t2->dimy; } else if (t2->dimx == 1 && t2->dimy == 1) { *type = t1->class; *dimx = t1->dimx; *dimy = t1->dimy; } else if (t1->class == HLSL_CLASS_MATRIX && t2->class == HLSL_CLASS_MATRIX) { *type = HLSL_CLASS_MATRIX; *dimx = min(t1->dimx, t2->dimx); *dimy = min(t1->dimy, t2->dimy); } else { if (t1->dimx * t1->dimy <= t2->dimx * t2->dimy) { *type = t1->class; *dimx = t1->dimx; *dimy = t1->dimy; } else { *type = t2->class; *dimx = t2->dimx; *dimy = t2->dimy; } } return true; } static struct hlsl_ir_node *add_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, enum hlsl_ir_expr_op op, struct hlsl_ir_node *operands[HLSL_MAX_OPERANDS], struct hlsl_type *type, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *expr; unsigned int i; if (type->class == HLSL_CLASS_MATRIX) { struct hlsl_type *scalar_type; struct hlsl_ir_load *var_load; struct hlsl_deref var_deref; struct hlsl_ir_node *load; struct hlsl_ir_var *var; scalar_type = hlsl_get_scalar_type(ctx, type->e.numeric.type); if (!(var = hlsl_new_synthetic_var(ctx, "split_op", type, loc))) return NULL; hlsl_init_simple_deref_from_var(&var_deref, var); for (i = 0; i < type->dimy * type->dimx; ++i) { struct hlsl_ir_node *value, *cell_operands[HLSL_MAX_OPERANDS] = { NULL }; struct hlsl_block store_block; unsigned int j; for (j = 0; j < HLSL_MAX_OPERANDS; j++) { if (operands[j]) { if (!(load = hlsl_add_load_component(ctx, block, operands[j], i, loc))) return NULL; cell_operands[j] = load; } } if (!(value = add_expr(ctx, block, op, cell_operands, scalar_type, loc))) return NULL; if (!hlsl_new_store_component(ctx, &store_block, &var_deref, i, value)) return NULL; hlsl_block_add_block(block, &store_block); } if (!(var_load = hlsl_new_var_load(ctx, var, loc))) return NULL; hlsl_block_add_instr(block, &var_load->node); return &var_load->node; } if (!(expr = hlsl_new_expr(ctx, op, operands, type, loc))) return NULL; hlsl_block_add_instr(block, expr); return expr; } static void check_integer_type(struct hlsl_ctx *ctx, const struct hlsl_ir_node *instr) { const struct hlsl_type *type = instr->data_type; struct vkd3d_string_buffer *string; switch (type->e.numeric.type) { case HLSL_TYPE_BOOL: case HLSL_TYPE_INT: case HLSL_TYPE_UINT: break; default: if ((string = hlsl_type_to_string(ctx, type))) hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Expression type '%s' is not integer.", string->buffer); hlsl_release_string_buffer(ctx, string); break; } } static struct hlsl_ir_node *add_unary_arithmetic_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {arg}; return add_expr(ctx, block, op, args, arg->data_type, loc); } static struct hlsl_ir_node *add_unary_bitwise_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg, const struct vkd3d_shader_location *loc) { check_integer_type(ctx, arg); return add_unary_arithmetic_expr(ctx, block, op, arg, loc); } static struct hlsl_ir_node *add_unary_logical_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {0}; struct hlsl_type *bool_type; bool_type = hlsl_get_numeric_type(ctx, arg->data_type->class, HLSL_TYPE_BOOL, arg->data_type->dimx, arg->data_type->dimy); if (!(args[0] = add_implicit_conversion(ctx, block, arg, bool_type, loc))) return NULL; return add_expr(ctx, block, op, args, bool_type, loc); } static struct hlsl_type *get_common_numeric_type(struct hlsl_ctx *ctx, const struct hlsl_ir_node *arg1, const struct hlsl_ir_node *arg2, const struct vkd3d_shader_location *loc) { enum hlsl_type_class type; enum hlsl_base_type base; unsigned int dimx, dimy; if (!expr_common_shape(ctx, arg1->data_type, arg2->data_type, loc, &type, &dimx, &dimy)) return NULL; base = expr_common_base_type(arg1->data_type->e.numeric.type, arg2->data_type->e.numeric.type); return hlsl_get_numeric_type(ctx, type, base, dimx, dimy); } static struct hlsl_ir_node *add_binary_arithmetic_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {0}; struct hlsl_type *common_type; common_type = get_common_numeric_type(ctx, arg1, arg2, loc); if (!(args[0] = add_implicit_conversion(ctx, block, arg1, common_type, loc))) return NULL; if (!(args[1] = add_implicit_conversion(ctx, block, arg2, common_type, loc))) return NULL; return add_expr(ctx, block, op, args, common_type, loc); } static struct hlsl_ir_node *add_binary_bitwise_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2, const struct vkd3d_shader_location *loc) { check_integer_type(ctx, arg1); check_integer_type(ctx, arg2); return add_binary_arithmetic_expr(ctx, block, op, arg1, arg2, loc); } static struct hlsl_ir_node *add_binary_comparison_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2, const struct vkd3d_shader_location *loc) { struct hlsl_type *common_type, *return_type; enum hlsl_type_class type; enum hlsl_base_type base; unsigned int dimx, dimy; struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {0}; if (!expr_common_shape(ctx, arg1->data_type, arg2->data_type, loc, &type, &dimx, &dimy)) return NULL; base = expr_common_base_type(arg1->data_type->e.numeric.type, arg2->data_type->e.numeric.type); common_type = hlsl_get_numeric_type(ctx, type, base, dimx, dimy); return_type = hlsl_get_numeric_type(ctx, type, HLSL_TYPE_BOOL, dimx, dimy); if (!(args[0] = add_implicit_conversion(ctx, block, arg1, common_type, loc))) return NULL; if (!(args[1] = add_implicit_conversion(ctx, block, arg2, common_type, loc))) return NULL; return add_expr(ctx, block, op, args, return_type, loc); } static struct hlsl_ir_node *add_binary_logical_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {0}; struct hlsl_type *common_type; enum hlsl_type_class type; unsigned int dimx, dimy; if (!expr_common_shape(ctx, arg1->data_type, arg2->data_type, loc, &type, &dimx, &dimy)) return NULL; common_type = hlsl_get_numeric_type(ctx, type, HLSL_TYPE_BOOL, dimx, dimy); if (!(args[0] = add_implicit_conversion(ctx, block, arg1, common_type, loc))) return NULL; if (!(args[1] = add_implicit_conversion(ctx, block, arg2, common_type, loc))) return NULL; return add_expr(ctx, block, op, args, common_type, loc); } static struct hlsl_ir_node *add_binary_shift_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2, const struct vkd3d_shader_location *loc) { enum hlsl_base_type base = arg1->data_type->e.numeric.type; struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {0}; struct hlsl_type *return_type, *integer_type; enum hlsl_type_class type; unsigned int dimx, dimy; check_integer_type(ctx, arg1); check_integer_type(ctx, arg2); if (base == HLSL_TYPE_BOOL) base = HLSL_TYPE_INT; if (!expr_common_shape(ctx, arg1->data_type, arg2->data_type, loc, &type, &dimx, &dimy)) return NULL; return_type = hlsl_get_numeric_type(ctx, type, base, dimx, dimy); integer_type = hlsl_get_numeric_type(ctx, type, HLSL_TYPE_INT, dimx, dimy); if (!(args[0] = add_implicit_conversion(ctx, block, arg1, return_type, loc))) return NULL; if (!(args[1] = add_implicit_conversion(ctx, block, arg2, integer_type, loc))) return NULL; return add_expr(ctx, block, op, args, return_type, loc); } static struct hlsl_ir_node *add_binary_dot_expr(struct hlsl_ctx *ctx, struct hlsl_block *instrs, struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2, const struct vkd3d_shader_location *loc) { enum hlsl_base_type base = expr_common_base_type(arg1->data_type->e.numeric.type, arg2->data_type->e.numeric.type); struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {0}; struct hlsl_type *common_type, *ret_type; enum hlsl_ir_expr_op op; unsigned dim; if (arg1->data_type->class == HLSL_CLASS_MATRIX) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, arg1->data_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Invalid type %s.", string->buffer); hlsl_release_string_buffer(ctx, string); return NULL; } if (arg2->data_type->class == HLSL_CLASS_MATRIX) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, arg2->data_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Invalid type %s.", string->buffer); hlsl_release_string_buffer(ctx, string); return NULL; } if (arg1->data_type->class == HLSL_CLASS_SCALAR) dim = arg2->data_type->dimx; else if (arg2->data_type->class == HLSL_CLASS_SCALAR) dim = arg1->data_type->dimx; else dim = min(arg1->data_type->dimx, arg2->data_type->dimx); if (dim == 1) op = HLSL_OP2_MUL; else op = HLSL_OP2_DOT; common_type = hlsl_get_vector_type(ctx, base, dim); ret_type = hlsl_get_scalar_type(ctx, base); if (!(args[0] = add_implicit_conversion(ctx, instrs, arg1, common_type, loc))) return NULL; if (!(args[1] = add_implicit_conversion(ctx, instrs, arg2, common_type, loc))) return NULL; return add_expr(ctx, instrs, op, args, ret_type, loc); } static struct hlsl_block *add_binary_expr_merge(struct hlsl_ctx *ctx, struct hlsl_block *block1, struct hlsl_block *block2, enum hlsl_ir_expr_op op, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg1 = node_from_block(block1), *arg2 = node_from_block(block2); hlsl_block_add_block(block1, block2); destroy_block(block2); switch (op) { case HLSL_OP2_ADD: case HLSL_OP2_DIV: case HLSL_OP2_MOD: case HLSL_OP2_MUL: add_binary_arithmetic_expr(ctx, block1, op, arg1, arg2, loc); break; case HLSL_OP2_BIT_AND: case HLSL_OP2_BIT_OR: case HLSL_OP2_BIT_XOR: add_binary_bitwise_expr(ctx, block1, op, arg1, arg2, loc); break; case HLSL_OP2_LESS: case HLSL_OP2_GEQUAL: case HLSL_OP2_EQUAL: case HLSL_OP2_NEQUAL: add_binary_comparison_expr(ctx, block1, op, arg1, arg2, loc); break; case HLSL_OP2_LOGIC_AND: case HLSL_OP2_LOGIC_OR: add_binary_logical_expr(ctx, block1, op, arg1, arg2, loc); break; case HLSL_OP2_LSHIFT: case HLSL_OP2_RSHIFT: add_binary_shift_expr(ctx, block1, op, arg1, arg2, loc); break; default: vkd3d_unreachable(); } return block1; } static enum hlsl_ir_expr_op op_from_assignment(enum parse_assign_op op) { static const enum hlsl_ir_expr_op ops[] = { 0, HLSL_OP2_ADD, 0, HLSL_OP2_MUL, HLSL_OP2_DIV, HLSL_OP2_MOD, HLSL_OP2_LSHIFT, HLSL_OP2_RSHIFT, HLSL_OP2_BIT_AND, HLSL_OP2_BIT_OR, HLSL_OP2_BIT_XOR, }; return ops[op]; } static bool invert_swizzle(uint32_t *swizzle, unsigned int *writemask, unsigned int *ret_width) { unsigned int i, j, bit = 0, inverted = 0, width, new_writemask = 0, new_swizzle = 0; /* Apply the writemask to the swizzle to get a new writemask and swizzle. */ for (i = 0; i < 4; ++i) { if (*writemask & (1 << i)) { unsigned int s = (*swizzle >> (i * 2)) & 3; new_swizzle |= s << (bit++ * 2); if (new_writemask & (1 << s)) return false; new_writemask |= 1 << s; } } width = bit; /* Invert the swizzle. */ bit = 0; for (i = 0; i < 4; ++i) { for (j = 0; j < width; ++j) { unsigned int s = (new_swizzle >> (j * 2)) & 3; if (s == i) inverted |= j << (bit++ * 2); } } *swizzle = inverted; *writemask = new_writemask; *ret_width = width; return true; } static bool invert_swizzle_matrix(uint32_t *swizzle, unsigned int *writemask, unsigned int *ret_width) { /* swizzle is 8 bits per component, each component is (from LSB) 4 bits X, then 4 bits Y. * components are indexed by their sources. i.e. the first component comes from the first * component of the rhs. */ unsigned int i, j, bit = 0, inverted = 0, width, new_writemask = 0, new_swizzle = 0; /* First, we filter the swizzle to remove components that aren't enabled by writemask. */ for (i = 0; i < 4; ++i) { if (*writemask & (1 << i)) { unsigned int s = (*swizzle >> (i * 8)) & 0xff; unsigned int x = s & 0xf, y = (s >> 4) & 0xf; unsigned int idx = x + y * 4; new_swizzle |= s << (bit++ * 8); if (new_writemask & (1 << idx)) return false; new_writemask |= 1 << idx; } } width = bit; /* Then we invert the swizzle. The resulting swizzle has 2 bits per component, because it's for the * incoming vector. */ bit = 0; for (i = 0; i < 16; ++i) { for (j = 0; j < width; ++j) { unsigned int s = (new_swizzle >> (j * 8)) & 0xff; unsigned int x = s & 0xf, y = (s >> 4) & 0xf; unsigned int idx = x + y * 4; if (idx == i) inverted |= j << (bit++ * 2); } } *swizzle = inverted; *writemask = new_writemask; *ret_width = width; return true; } static struct hlsl_ir_node *add_assignment(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *lhs, enum parse_assign_op assign_op, struct hlsl_ir_node *rhs) { struct hlsl_type *lhs_type = lhs->data_type; struct hlsl_ir_node *copy; unsigned int writemask = 0, width = 0; bool matrix_writemask = false; if (assign_op == ASSIGN_OP_SUB) { if (!(rhs = add_unary_arithmetic_expr(ctx, block, HLSL_OP1_NEG, rhs, &rhs->loc))) return NULL; assign_op = ASSIGN_OP_ADD; } if (assign_op != ASSIGN_OP_ASSIGN) { enum hlsl_ir_expr_op op = op_from_assignment(assign_op); assert(op); if (!(rhs = add_binary_arithmetic_expr(ctx, block, op, lhs, rhs, &rhs->loc))) return NULL; } if (hlsl_is_numeric_type(lhs_type)) { writemask = (1 << lhs_type->dimx) - 1; width = lhs_type->dimx; } if (!(rhs = add_implicit_conversion(ctx, block, rhs, lhs_type, &rhs->loc))) return NULL; while (lhs->type != HLSL_IR_LOAD && lhs->type != HLSL_IR_INDEX) { if (lhs->type == HLSL_IR_EXPR && hlsl_ir_expr(lhs)->op == HLSL_OP1_CAST) { hlsl_fixme(ctx, &lhs->loc, "Cast on the LHS."); return NULL; } else if (lhs->type == HLSL_IR_SWIZZLE) { struct hlsl_ir_swizzle *swizzle = hlsl_ir_swizzle(lhs); struct hlsl_ir_node *new_swizzle; uint32_t s = swizzle->swizzle; assert(!matrix_writemask); if (swizzle->val.node->data_type->class == HLSL_CLASS_MATRIX) { if (swizzle->val.node->type != HLSL_IR_LOAD && swizzle->val.node->type != HLSL_IR_INDEX) { hlsl_fixme(ctx, &lhs->loc, "Unhandled source of matrix swizzle."); return NULL; } if (!invert_swizzle_matrix(&s, &writemask, &width)) { hlsl_error(ctx, &lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_WRITEMASK, "Invalid writemask for matrix."); return NULL; } matrix_writemask = true; } else if (!invert_swizzle(&s, &writemask, &width)) { hlsl_error(ctx, &lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_WRITEMASK, "Invalid writemask."); return NULL; } if (!(new_swizzle = hlsl_new_swizzle(ctx, s, width, rhs, &swizzle->node.loc))) { return NULL; } hlsl_block_add_instr(block, new_swizzle); lhs = swizzle->val.node; rhs = new_swizzle; } else { hlsl_error(ctx, &lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_LVALUE, "Invalid lvalue."); return NULL; } } if (lhs->type == HLSL_IR_INDEX && hlsl_index_chain_has_resource_access(hlsl_ir_index(lhs))) { struct hlsl_ir_node *coords = hlsl_ir_index(lhs)->idx.node; struct hlsl_deref resource_deref; struct hlsl_type *resource_type; struct hlsl_ir_node *store; unsigned int dim_count; if (!hlsl_index_is_resource_access(hlsl_ir_index(lhs))) { hlsl_fixme(ctx, &lhs->loc, "Non-direct structured resource store."); return NULL; } if (!hlsl_init_deref_from_index_chain(ctx, &resource_deref, hlsl_ir_index(lhs)->val.node)) return NULL; resource_type = hlsl_deref_get_type(ctx, &resource_deref); assert(resource_type->class == HLSL_CLASS_TEXTURE || resource_type->class == HLSL_CLASS_UAV); if (resource_type->class != HLSL_CLASS_UAV) hlsl_error(ctx, &lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Read-only resources cannot be stored to."); dim_count = hlsl_sampler_dim_count(resource_type->sampler_dim); if (width != resource_type->e.resource.format->dimx * resource_type->e.resource.format->dimy) hlsl_error(ctx, &lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_WRITEMASK, "Resource store expressions must write to all components."); assert(coords->data_type->class == HLSL_CLASS_VECTOR); assert(coords->data_type->e.numeric.type == HLSL_TYPE_UINT); assert(coords->data_type->dimx == dim_count); if (!(store = hlsl_new_resource_store(ctx, &resource_deref, coords, rhs, &lhs->loc))) { hlsl_cleanup_deref(&resource_deref); return NULL; } hlsl_block_add_instr(block, store); hlsl_cleanup_deref(&resource_deref); } else if (matrix_writemask) { struct hlsl_deref deref; unsigned int i, j, k = 0; hlsl_init_deref_from_index_chain(ctx, &deref, lhs); for (i = 0; i < lhs->data_type->dimy; ++i) { for (j = 0; j < lhs->data_type->dimx; ++j) { struct hlsl_ir_node *load; struct hlsl_block store_block; const unsigned int idx = i * 4 + j; const unsigned int component = i * lhs->data_type->dimx + j; if (!(writemask & (1 << idx))) continue; if (!(load = hlsl_add_load_component(ctx, block, rhs, k++, &rhs->loc))) { hlsl_cleanup_deref(&deref); return NULL; } if (!hlsl_new_store_component(ctx, &store_block, &deref, component, load)) { hlsl_cleanup_deref(&deref); return NULL; } hlsl_block_add_block(block, &store_block); } } hlsl_cleanup_deref(&deref); } else if (lhs->type == HLSL_IR_INDEX && hlsl_index_is_noncontiguous(hlsl_ir_index(lhs))) { struct hlsl_ir_index *row = hlsl_ir_index(lhs); struct hlsl_ir_node *mat = row->val.node; unsigned int i, k = 0; assert(!matrix_writemask); for (i = 0; i < mat->data_type->dimx; ++i) { struct hlsl_ir_node *cell, *load, *store, *c; struct hlsl_deref deref; if (!(writemask & (1 << i))) continue; if (!(c = hlsl_new_uint_constant(ctx, i, &lhs->loc))) return NULL; hlsl_block_add_instr(block, c); if (!(cell = hlsl_new_index(ctx, &row->node, c, &lhs->loc))) return NULL; hlsl_block_add_instr(block, cell); if (!(load = hlsl_add_load_component(ctx, block, rhs, k++, &rhs->loc))) return NULL; if (!hlsl_init_deref_from_index_chain(ctx, &deref, cell)) return NULL; if (!(store = hlsl_new_store_index(ctx, &deref, NULL, load, 0, &rhs->loc))) { hlsl_cleanup_deref(&deref); return NULL; } hlsl_block_add_instr(block, store); hlsl_cleanup_deref(&deref); } } else { struct hlsl_ir_node *store; struct hlsl_deref deref; if (!hlsl_init_deref_from_index_chain(ctx, &deref, lhs)) return NULL; if (!(store = hlsl_new_store_index(ctx, &deref, NULL, rhs, writemask, &rhs->loc))) { hlsl_cleanup_deref(&deref); return NULL; } hlsl_block_add_instr(block, store); hlsl_cleanup_deref(&deref); } /* Don't use the instruction itself as a source, as this makes structure * splitting easier. Instead copy it here. Since we retrieve sources from * the last instruction in the list, we do need to copy. */ if (!(copy = hlsl_new_copy(ctx, rhs))) return NULL; hlsl_block_add_instr(block, copy); return copy; } static bool add_increment(struct hlsl_ctx *ctx, struct hlsl_block *block, bool decrement, bool post, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *lhs = node_from_block(block); struct hlsl_ir_node *one; if (lhs->data_type->modifiers & HLSL_MODIFIER_CONST) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_MODIFIES_CONST, "Argument to %s%screment operator is const.", post ? "post" : "pre", decrement ? "de" : "in"); if (!(one = hlsl_new_int_constant(ctx, 1, loc))) return false; hlsl_block_add_instr(block, one); if (!add_assignment(ctx, block, lhs, decrement ? ASSIGN_OP_SUB : ASSIGN_OP_ADD, one)) return false; if (post) { struct hlsl_ir_node *copy; if (!(copy = hlsl_new_copy(ctx, lhs))) return false; hlsl_block_add_instr(block, copy); /* Post increment/decrement expressions are considered const. */ if (!(copy->data_type = hlsl_type_clone(ctx, copy->data_type, 0, HLSL_MODIFIER_CONST))) return false; } return true; } /* For some reason, for matrices, values from default value initializers end up in different * components than from regular initializers. Default value initializers fill the matrix in * vertical reading order (left-to-right top-to-bottom) instead of regular reading order * (top-to-bottom left-to-right), so they have to be adjusted. */ static unsigned int get_component_index_from_default_initializer_index(struct hlsl_ctx *ctx, struct hlsl_type *type, unsigned int index) { unsigned int element_comp_count, element, x, y, i; unsigned int base = 0; if (ctx->profile->major_version < 4) return index; if (ctx->profile->type == VKD3D_SHADER_TYPE_EFFECT) return index; switch (type->class) { case HLSL_CLASS_MATRIX: x = index / type->dimy; y = index % type->dimy; return y * type->dimx + x; case HLSL_CLASS_ARRAY: element_comp_count = hlsl_type_component_count(type->e.array.type); element = index / element_comp_count; base = element * element_comp_count; return base + get_component_index_from_default_initializer_index(ctx, type->e.array.type, index - base); case HLSL_CLASS_STRUCT: for (i = 0; i < type->e.record.field_count; ++i) { struct hlsl_type *field_type = type->e.record.fields[i].type; element_comp_count = hlsl_type_component_count(field_type); if (index - base < element_comp_count) return base + get_component_index_from_default_initializer_index(ctx, field_type, index - base); base += element_comp_count; } break; default: return index; } vkd3d_unreachable(); } static void initialize_var_components(struct hlsl_ctx *ctx, struct hlsl_block *instrs, struct hlsl_ir_var *dst, unsigned int *store_index, struct hlsl_ir_node *src) { unsigned int src_comp_count = hlsl_type_component_count(src->data_type); struct hlsl_deref dst_deref; unsigned int k; hlsl_init_simple_deref_from_var(&dst_deref, dst); for (k = 0; k < src_comp_count; ++k) { struct hlsl_ir_node *conv, *load; struct hlsl_type *dst_comp_type; struct hlsl_block block; if (!(load = hlsl_add_load_component(ctx, instrs, src, k, &src->loc))) return; dst_comp_type = hlsl_type_get_component_type(ctx, dst->data_type, *store_index); if (dst->default_values) { struct hlsl_default_value default_value = {0}; unsigned int dst_index; if (!hlsl_clone_block(ctx, &block, instrs)) return; default_value.value = evaluate_static_expression(ctx, &block, dst_comp_type, &src->loc); dst_index = get_component_index_from_default_initializer_index(ctx, dst->data_type, *store_index); dst->default_values[dst_index] = default_value; hlsl_block_cleanup(&block); } else { if (!(conv = add_implicit_conversion(ctx, instrs, load, dst_comp_type, &src->loc))) return; if (!hlsl_new_store_component(ctx, &block, &dst_deref, *store_index, conv)) return; hlsl_block_add_block(instrs, &block); } ++*store_index; } } static bool type_has_object_components(const struct hlsl_type *type) { if (type->class == HLSL_CLASS_ARRAY) return type_has_object_components(type->e.array.type); if (type->class == HLSL_CLASS_STRUCT) { for (unsigned int i = 0; i < type->e.record.field_count; ++i) { if (type_has_object_components(type->e.record.fields[i].type)) return true; } return false; } return !hlsl_is_numeric_type(type); } static bool type_has_numeric_components(struct hlsl_type *type) { if (hlsl_is_numeric_type(type)) return true; if (type->class == HLSL_CLASS_ARRAY) return type_has_numeric_components(type->e.array.type); if (type->class == HLSL_CLASS_STRUCT) { unsigned int i; for (i = 0; i < type->e.record.field_count; ++i) { if (type_has_numeric_components(type->e.record.fields[i].type)) return true; } } return false; } static void check_invalid_in_out_modifiers(struct hlsl_ctx *ctx, unsigned int modifiers, const struct vkd3d_shader_location *loc) { modifiers &= (HLSL_STORAGE_IN | HLSL_STORAGE_OUT); if (modifiers) { struct vkd3d_string_buffer *string; if ((string = hlsl_modifiers_to_string(ctx, modifiers))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, "Modifiers '%s' are not allowed on non-parameter variables.", string->buffer); hlsl_release_string_buffer(ctx, string); } } static void check_invalid_object_fields(struct hlsl_ctx *ctx, const struct hlsl_ir_var *var) { const struct hlsl_type *type = var->data_type; while (type->class == HLSL_CLASS_ARRAY) type = type->e.array.type; if (type->class == HLSL_CLASS_STRUCT && type_has_object_components(type)) hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Target profile doesn't support objects as struct members in uniform variables."); } static void declare_var(struct hlsl_ctx *ctx, struct parse_variable_def *v) { struct hlsl_type *basic_type = v->basic_type; struct hlsl_ir_function_decl *func; struct hlsl_semantic new_semantic; uint32_t modifiers = v->modifiers; bool unbounded_res_array = false; bool constant_buffer = false; struct hlsl_ir_var *var; struct hlsl_type *type; bool local = true; char *var_name; unsigned int i; assert(basic_type); if (basic_type->class == HLSL_CLASS_MATRIX) assert(basic_type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK); type = basic_type; if (hlsl_version_ge(ctx, 5, 1) && hlsl_type_is_resource(type)) { for (i = 0; i < v->arrays.count; ++i) unbounded_res_array |= (v->arrays.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT); } if (type->class == HLSL_CLASS_CONSTANT_BUFFER) { type = type->e.resource.format; constant_buffer = true; } if (unbounded_res_array) { if (v->arrays.count == 1) { hlsl_fixme(ctx, &v->loc, "Unbounded resource arrays."); return; } else { hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Unbounded resource arrays cannot be multi-dimensional."); } } else { for (i = 0; i < v->arrays.count; ++i) { if (v->arrays.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT) { unsigned int size = initializer_size(&v->initializer); unsigned int elem_components = hlsl_type_component_count(type); if (i < v->arrays.count - 1) { hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Only innermost array size can be implicit."); v->initializer.args_count = 0; } else if (elem_components == 0) { hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Cannot declare an implicit size array of a size 0 type."); v->initializer.args_count = 0; } else if (size == 0) { hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Implicit size arrays need to be initialized."); v->initializer.args_count = 0; } else if (size % elem_components != 0) { hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Cannot initialize implicit size array with %u components, expected a multiple of %u.", size, elem_components); v->initializer.args_count = 0; } else { v->arrays.sizes[i] = size / elem_components; } } type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]); } } if (hlsl_version_ge(ctx, 5, 1) && type->class == HLSL_CLASS_ARRAY && hlsl_type_is_resource(type)) { /* SM 5.1/6.x descriptor arrays act differently from previous versions. * Not only are they treated as a single object in reflection, but they * act as a single component for the purposes of assignment and * initialization. */ hlsl_fixme(ctx, &v->loc, "Shader model 5.1+ resource array."); } if (!(var_name = vkd3d_strdup(v->name))) return; if (!hlsl_clone_semantic(ctx, &new_semantic, &v->semantic)) { vkd3d_free(var_name); return; } if (!(var = hlsl_new_var(ctx, var_name, type, &v->loc, &new_semantic, modifiers, &v->reg_reservation))) { hlsl_cleanup_semantic(&new_semantic); vkd3d_free(var_name); return; } if (constant_buffer && ctx->cur_scope == ctx->globals) { if (!(var_name = vkd3d_strdup(v->name))) return; var->buffer = hlsl_new_buffer(ctx, HLSL_BUFFER_CONSTANT, var_name, modifiers, &v->reg_reservation, NULL, &v->loc); } else { var->buffer = ctx->cur_buffer; } if (var->buffer == ctx->globals_buffer) { if (var->reg_reservation.offset_type) hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "packoffset() is only allowed inside constant buffer declarations."); } if (ctx->cur_scope == ctx->globals) { local = false; if ((modifiers & HLSL_STORAGE_UNIFORM) && (modifiers & HLSL_STORAGE_STATIC)) hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, "Variable '%s' is declared as both \"uniform\" and \"static\".", var->name); /* Mark it as uniform. We need to do this here since synthetic * variables also get put in the global scope, but shouldn't be * considered uniforms, and we have no way of telling otherwise. */ if (!(modifiers & HLSL_STORAGE_STATIC)) var->storage_modifiers |= HLSL_STORAGE_UNIFORM; if ((ctx->profile->major_version < 5 || ctx->profile->type == VKD3D_SHADER_TYPE_EFFECT) && (var->storage_modifiers & HLSL_STORAGE_UNIFORM)) { check_invalid_object_fields(ctx, var); } if ((func = hlsl_get_first_func_decl(ctx, var->name))) { hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "'%s' is already defined as a function.", var->name); hlsl_note(ctx, &func->loc, VKD3D_SHADER_LOG_ERROR, "'%s' was previously defined here.", var->name); } } else { static const unsigned int invalid = HLSL_STORAGE_EXTERN | HLSL_STORAGE_SHARED | HLSL_STORAGE_GROUPSHARED | HLSL_STORAGE_UNIFORM; if (modifiers & invalid) { struct vkd3d_string_buffer *string; if ((string = hlsl_modifiers_to_string(ctx, modifiers & invalid))) hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, "Modifiers '%s' are not allowed on local variables.", string->buffer); hlsl_release_string_buffer(ctx, string); } if (var->semantic.name) hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SEMANTIC, "Semantics are not allowed on local variables."); if ((type->modifiers & HLSL_MODIFIER_CONST) && !v->initializer.args_count && !(modifiers & HLSL_STORAGE_STATIC)) { hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_MISSING_INITIALIZER, "Const variable \"%s\" is missing an initializer.", var->name); } } if ((var->storage_modifiers & HLSL_STORAGE_STATIC) && type_has_numeric_components(var->data_type) && type_has_object_components(var->data_type)) { hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Static variables cannot have both numeric and resource components."); } if (!hlsl_add_var(ctx, var, local)) { struct hlsl_ir_var *old = hlsl_get_var(ctx->cur_scope, var->name); hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "Variable \"%s\" was already declared in this scope.", var->name); hlsl_note(ctx, &old->loc, VKD3D_SHADER_LOG_ERROR, "\"%s\" was previously declared here.", old->name); hlsl_free_var(var); return; } } static struct hlsl_block *initialize_vars(struct hlsl_ctx *ctx, struct list *var_list) { struct parse_variable_def *v, *v_next; struct hlsl_block *initializers; unsigned int component_count; struct hlsl_ir_var *var; struct hlsl_type *type; if (!(initializers = make_empty_block(ctx))) { LIST_FOR_EACH_ENTRY_SAFE(v, v_next, var_list, struct parse_variable_def, entry) { free_parse_variable_def(v); } vkd3d_free(var_list); return NULL; } LIST_FOR_EACH_ENTRY_SAFE(v, v_next, var_list, struct parse_variable_def, entry) { /* If this fails, the variable failed to be declared. */ if (!(var = hlsl_get_var(ctx->cur_scope, v->name))) { free_parse_variable_def(v); continue; } type = var->data_type; component_count = hlsl_type_component_count(type); var->state_blocks = v->state_blocks; var->state_block_count = v->state_block_count; var->state_block_capacity = v->state_block_capacity; v->state_block_count = 0; v->state_block_capacity = 0; v->state_blocks = NULL; if (var->state_blocks && component_count != var->state_block_count) { hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Expected %u state blocks, but got %u.", component_count, var->state_block_count); free_parse_variable_def(v); continue; } if (v->initializer.args_count) { unsigned int store_index = 0; bool is_default_values_initializer; unsigned int size, k; is_default_values_initializer = (ctx->cur_buffer != ctx->globals_buffer) || (var->storage_modifiers & HLSL_STORAGE_UNIFORM) || ctx->cur_scope->annotations; if (is_default_values_initializer) { /* Default values might have been allocated already for another variable of the same name, in the same scope. */ if (var->default_values) { free_parse_variable_def(v); continue; } if (!(var->default_values = hlsl_calloc(ctx, component_count, sizeof(*var->default_values)))) { free_parse_variable_def(v); continue; } } if (!v->initializer.braces) { if (!(add_implicit_conversion(ctx, v->initializer.instrs, v->initializer.args[0], type, &v->loc))) { free_parse_variable_def(v); continue; } v->initializer.args[0] = node_from_block(v->initializer.instrs); } size = initializer_size(&v->initializer); if (component_count != size) { hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Expected %u components in initializer, but got %u.", component_count, size); free_parse_variable_def(v); continue; } for (k = 0; k < v->initializer.args_count; ++k) { initialize_var_components(ctx, v->initializer.instrs, var, &store_index, v->initializer.args[k]); } if (is_default_values_initializer) { hlsl_dump_var_default_values(var); } else if (var->storage_modifiers & HLSL_STORAGE_STATIC) { hlsl_block_add_block(&ctx->static_initializers, v->initializer.instrs); } else { hlsl_block_add_block(initializers, v->initializer.instrs); } } else if (var->storage_modifiers & HLSL_STORAGE_STATIC) { struct hlsl_ir_node *cast, *store, *zero; /* Initialize statics to zero by default. */ if (type_has_object_components(var->data_type)) { free_parse_variable_def(v); continue; } if (!(zero = hlsl_new_uint_constant(ctx, 0, &var->loc))) { free_parse_variable_def(v); continue; } hlsl_block_add_instr(&ctx->static_initializers, zero); if (!(cast = add_cast(ctx, &ctx->static_initializers, zero, var->data_type, &var->loc))) { free_parse_variable_def(v); continue; } if (!(store = hlsl_new_simple_store(ctx, var, cast))) { free_parse_variable_def(v); continue; } hlsl_block_add_instr(&ctx->static_initializers, store); } free_parse_variable_def(v); } vkd3d_free(var_list); return initializers; } static bool func_is_compatible_match(struct hlsl_ctx *ctx, const struct hlsl_ir_function_decl *decl, const struct parse_initializer *args) { unsigned int i; if (decl->parameters.count != args->args_count) return false; for (i = 0; i < decl->parameters.count; ++i) { if (!implicit_compatible_data_types(ctx, args->args[i]->data_type, decl->parameters.vars[i]->data_type)) return false; } return true; } static struct hlsl_ir_function_decl *find_function_call(struct hlsl_ctx *ctx, const char *name, const struct parse_initializer *args, const struct vkd3d_shader_location *loc) { struct hlsl_ir_function_decl *decl, *compatible_match = NULL; struct hlsl_ir_function *func; struct rb_entry *entry; if (!(entry = rb_get(&ctx->functions, name))) return NULL; func = RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry); LIST_FOR_EACH_ENTRY(decl, &func->overloads, struct hlsl_ir_function_decl, entry) { if (func_is_compatible_match(ctx, decl, args)) { if (compatible_match) { hlsl_fixme(ctx, loc, "Prioritize between multiple compatible function overloads."); break; } compatible_match = decl; } } return compatible_match; } static struct hlsl_ir_node *hlsl_new_void_expr(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *operands[HLSL_MAX_OPERANDS] = {0}; return hlsl_new_expr(ctx, HLSL_OP0_VOID, operands, ctx->builtin_types.Void, loc); } static bool add_user_call(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *func, const struct parse_initializer *args, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *call; unsigned int i; assert(args->args_count == func->parameters.count); for (i = 0; i < func->parameters.count; ++i) { struct hlsl_ir_var *param = func->parameters.vars[i]; struct hlsl_ir_node *arg = args->args[i]; if (!hlsl_types_are_equal(arg->data_type, param->data_type)) { struct hlsl_ir_node *cast; if (!(cast = add_cast(ctx, args->instrs, arg, param->data_type, &arg->loc))) return false; args->args[i] = cast; arg = cast; } if (param->storage_modifiers & HLSL_STORAGE_IN) { struct hlsl_ir_node *store; if (!(store = hlsl_new_simple_store(ctx, param, arg))) return false; hlsl_block_add_instr(args->instrs, store); } } if (!(call = hlsl_new_call(ctx, func, loc))) return false; hlsl_block_add_instr(args->instrs, call); for (i = 0; i < func->parameters.count; ++i) { struct hlsl_ir_var *param = func->parameters.vars[i]; struct hlsl_ir_node *arg = args->args[i]; if (param->storage_modifiers & HLSL_STORAGE_OUT) { struct hlsl_ir_load *load; if (arg->data_type->modifiers & HLSL_MODIFIER_CONST) hlsl_error(ctx, &arg->loc, VKD3D_SHADER_ERROR_HLSL_MODIFIES_CONST, "Output argument to \"%s\" is const.", func->func->name); if (!(load = hlsl_new_var_load(ctx, param, &arg->loc))) return false; hlsl_block_add_instr(args->instrs, &load->node); if (!add_assignment(ctx, args->instrs, arg, ASSIGN_OP_ASSIGN, &load->node)) return false; } } if (func->return_var) { struct hlsl_ir_load *load; if (!(load = hlsl_new_var_load(ctx, func->return_var, loc))) return false; hlsl_block_add_instr(args->instrs, &load->node); } else { struct hlsl_ir_node *expr; if (!(expr = hlsl_new_void_expr(ctx, loc))) return false; hlsl_block_add_instr(args->instrs, expr); } return true; } static struct hlsl_ir_node *intrinsic_float_convert_arg(struct hlsl_ctx *ctx, const struct parse_initializer *params, struct hlsl_ir_node *arg, const struct vkd3d_shader_location *loc) { struct hlsl_type *type = arg->data_type; if (type->e.numeric.type == HLSL_TYPE_FLOAT || type->e.numeric.type == HLSL_TYPE_HALF) return arg; type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_FLOAT, type->dimx, type->dimy); return add_implicit_conversion(ctx, params->instrs, arg, type, loc); } static bool convert_args(struct hlsl_ctx *ctx, const struct parse_initializer *params, struct hlsl_type *type, const struct vkd3d_shader_location *loc) { unsigned int i; for (i = 0; i < params->args_count; ++i) { struct hlsl_ir_node *new_arg; if (!(new_arg = add_implicit_conversion(ctx, params->instrs, params->args[i], type, loc))) return false; params->args[i] = new_arg; } return true; } static struct hlsl_type *elementwise_intrinsic_get_common_type(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { enum hlsl_base_type base = params->args[0]->data_type->e.numeric.type; bool vectors = false, matrices = false; unsigned int dimx = 4, dimy = 4; struct hlsl_type *common_type; unsigned int i; for (i = 0; i < params->args_count; ++i) { struct hlsl_type *arg_type = params->args[i]->data_type; base = expr_common_base_type(base, arg_type->e.numeric.type); if (arg_type->class == HLSL_CLASS_VECTOR) { vectors = true; dimx = min(dimx, arg_type->dimx); } else if (arg_type->class == HLSL_CLASS_MATRIX) { matrices = true; dimx = min(dimx, arg_type->dimx); dimy = min(dimy, arg_type->dimy); } } if (matrices && vectors) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Cannot use both matrices and vectors in an elementwise intrinsic."); return NULL; } else if (matrices) { common_type = hlsl_get_matrix_type(ctx, base, dimx, dimy); } else if (vectors) { common_type = hlsl_get_vector_type(ctx, base, dimx); } else { common_type = hlsl_get_scalar_type(ctx, base); } return common_type; } static bool elementwise_intrinsic_convert_args(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_type *common_type; if (!(common_type = elementwise_intrinsic_get_common_type(ctx, params, loc))) return false; return convert_args(ctx, params, common_type, loc); } static bool elementwise_intrinsic_float_convert_args(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { enum hlsl_base_type base_type; struct hlsl_type *type; if (!(type = elementwise_intrinsic_get_common_type(ctx, params, loc))) return false; base_type = type->e.numeric.type == HLSL_TYPE_HALF ? HLSL_TYPE_HALF : HLSL_TYPE_FLOAT; type = hlsl_get_numeric_type(ctx, type->class, base_type, type->dimx, type->dimy); return convert_args(ctx, params, type, loc); } static bool intrinsic_abs(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_ABS, params->args[0], loc); } static bool write_acos_or_asin(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc, bool asin_mode) { struct hlsl_ir_function_decl *func; struct hlsl_type *type; char *body; static const char template[] = "%s %s(%s x)\n" "{\n" " %s abs_arg = abs(x);\n" " %s poly_approx = (((-0.018729\n" " * abs_arg + 0.074261)\n" " * abs_arg - 0.212114)\n" " * abs_arg + 1.570729);\n" " %s correction = sqrt(1.0 - abs_arg);\n" " %s zero_flip = (x < 0.0) * (-2.0 * correction * poly_approx + 3.141593);\n" " %s result = poly_approx * correction + zero_flip;\n" " return %s;\n" "}"; static const char fn_name_acos[] = "acos"; static const char fn_name_asin[] = "asin"; static const char return_stmt_acos[] = "result"; static const char return_stmt_asin[] = "-result + 1.570796"; const char *fn_name = asin_mode ? fn_name_asin : fn_name_acos; type = params->args[0]->data_type; type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_FLOAT, type->dimx, type->dimy); if (!(body = hlsl_sprintf_alloc(ctx, template, type->name, fn_name, type->name, type->name, type->name, type->name, type->name, type->name, (asin_mode ? return_stmt_asin : return_stmt_acos)))) return false; func = hlsl_compile_internal_function(ctx, fn_name, body); vkd3d_free(body); if (!func) return false; return add_user_call(ctx, func, params, loc); } static bool intrinsic_acos(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return write_acos_or_asin(ctx, params, loc, false); } /* Find the type corresponding to the given source type, with the same * dimensions but a different base type. */ static struct hlsl_type *convert_numeric_type(const struct hlsl_ctx *ctx, const struct hlsl_type *type, enum hlsl_base_type base_type) { return hlsl_get_numeric_type(ctx, type->class, base_type, type->dimx, type->dimy); } static bool add_combine_components(struct hlsl_ctx *ctx, const struct parse_initializer *params, struct hlsl_ir_node *arg, enum hlsl_ir_expr_op op, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *res, *load; unsigned int i, count; count = hlsl_type_component_count(arg->data_type); if (!(res = hlsl_add_load_component(ctx, params->instrs, arg, 0, loc))) return false; for (i = 1; i < count; ++i) { if (!(load = hlsl_add_load_component(ctx, params->instrs, arg, i, loc))) return false; if (!(res = hlsl_new_binary_expr(ctx, op, res, load))) return NULL; hlsl_block_add_instr(params->instrs, res); } return true; } static bool intrinsic_all(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg = params->args[0], *cast; struct hlsl_type *bool_type; bool_type = convert_numeric_type(ctx, arg->data_type, HLSL_TYPE_BOOL); if (!(cast = add_cast(ctx, params->instrs, arg, bool_type, loc))) return false; return add_combine_components(ctx, params, cast, HLSL_OP2_LOGIC_AND, loc); } static bool intrinsic_any(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg = params->args[0], *cast; struct hlsl_type *bool_type; bool_type = convert_numeric_type(ctx, arg->data_type, HLSL_TYPE_BOOL); if (!(cast = add_cast(ctx, params->instrs, arg, bool_type, loc))) return false; return add_combine_components(ctx, params, cast, HLSL_OP2_LOGIC_OR, loc); } static bool intrinsic_asin(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return write_acos_or_asin(ctx, params, loc, true); } static bool write_atan_or_atan2(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc, bool atan2_mode) { struct hlsl_ir_function_decl *func; struct hlsl_type *type; struct vkd3d_string_buffer *buf; int ret; static const char atan2_name[] = "atan2"; static const char atan_name[] = "atan"; static const char atan2_header_template[] = "%s atan2(%s y, %s x)\n" "{\n" " %s in_y, in_x;\n" " in_y = y;\n" " in_x = x;\n"; static const char atan_header_template[] = "%s atan(%s y)\n" "{\n" " %s in_y, in_x;\n" " in_y = y;\n" " in_x = 1.0;\n"; static const char body_template[] = " %s recip, input, x2, poly_approx, flipped;" " recip = 1.0 / max(abs(in_y), abs(in_x));\n" " input = recip * min(abs(in_y), abs(in_x));\n" " x2 = input * input;\n" " poly_approx = ((((0.020835\n" " * x2 - 0.085133)\n" " * x2 + 0.180141)\n" " * x2 - 0.330299)\n" " * x2 + 0.999866)\n" " * input;\n" " flipped = poly_approx * -2.0 + 1.570796;\n" " poly_approx += abs(in_x) < abs(in_y) ? flipped : 0.0;\n" " poly_approx += in_x < 0.0 ? -3.1415927 : 0.0;\n" " return (min(in_x, in_y) < 0.0 && max(in_x, in_y) >= 0.0)\n" " ? -poly_approx\n" " : poly_approx;\n" "}"; if (!(type = elementwise_intrinsic_get_common_type(ctx, params, loc))) return false; type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_FLOAT, type->dimx, type->dimy); if (!(buf = hlsl_get_string_buffer(ctx))) return false; if (atan2_mode) ret = vkd3d_string_buffer_printf(buf, atan2_header_template, type->name, type->name, type->name, type->name); else ret = vkd3d_string_buffer_printf(buf, atan_header_template, type->name, type->name, type->name); if (ret < 0) { hlsl_release_string_buffer(ctx, buf); return false; } ret = vkd3d_string_buffer_printf(buf, body_template, type->name); if (ret < 0) { hlsl_release_string_buffer(ctx, buf); return false; } func = hlsl_compile_internal_function(ctx, atan2_mode ? atan2_name : atan_name, buf->buffer); hlsl_release_string_buffer(ctx, buf); if (!func) return false; return add_user_call(ctx, func, params, loc); } static bool intrinsic_atan(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return write_atan_or_atan2(ctx, params, loc, false); } static bool intrinsic_atan2(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return write_atan_or_atan2(ctx, params, loc, true); } static bool intrinsic_asfloat(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *operands[HLSL_MAX_OPERANDS] = {0}; struct hlsl_type *data_type; data_type = params->args[0]->data_type; if (data_type->e.numeric.type == HLSL_TYPE_BOOL || data_type->e.numeric.type == HLSL_TYPE_DOUBLE) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, data_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Wrong argument type of asfloat(): expected 'int', 'uint', 'float', or 'half', but got '%s'.", string->buffer); hlsl_release_string_buffer(ctx, string); } data_type = convert_numeric_type(ctx, data_type, HLSL_TYPE_FLOAT); operands[0] = params->args[0]; return add_expr(ctx, params->instrs, HLSL_OP1_REINTERPRET, operands, data_type, loc); } static bool intrinsic_asuint(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *operands[HLSL_MAX_OPERANDS] = {0}; struct hlsl_type *data_type; if (params->args_count != 1 && params->args_count != 3) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Wrong number of arguments to function 'asuint': expected 1 or 3, but got %u.", params->args_count); return false; } if (params->args_count == 3) { hlsl_fixme(ctx, loc, "Double-to-integer conversion."); return false; } data_type = params->args[0]->data_type; if (data_type->e.numeric.type == HLSL_TYPE_BOOL || data_type->e.numeric.type == HLSL_TYPE_DOUBLE) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, data_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Wrong type for argument 0 of asuint(): expected 'int', 'uint', 'float', or 'half', but got '%s'.", string->buffer); hlsl_release_string_buffer(ctx, string); } data_type = convert_numeric_type(ctx, data_type, HLSL_TYPE_UINT); operands[0] = params->args[0]; return add_expr(ctx, params->instrs, HLSL_OP1_REINTERPRET, operands, data_type, loc); } static bool intrinsic_ceil(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_CEIL, arg, loc); } static bool intrinsic_clamp(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *max; if (!elementwise_intrinsic_convert_args(ctx, params, loc)) return false; if (!(max = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MAX, params->args[0], params->args[1], loc))) return false; return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MIN, max, params->args[2], loc); } static bool intrinsic_clip(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *condition, *jump; if (!elementwise_intrinsic_float_convert_args(ctx, params, loc)) return false; condition = params->args[0]; if (ctx->profile->major_version < 4 && hlsl_type_component_count(condition->data_type) > 4) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, condition->data_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Argument type cannot exceed 4 components, got type \"%s\".", string->buffer); hlsl_release_string_buffer(ctx, string); return false; } if (!(jump = hlsl_new_jump(ctx, HLSL_IR_JUMP_DISCARD_NEG, condition, loc))) return false; hlsl_block_add_instr(params->instrs, jump); return true; } static bool intrinsic_cos(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_COS, arg, loc); } static bool write_cosh_or_sinh(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc, bool sinh_mode) { struct hlsl_ir_function_decl *func; struct hlsl_ir_node *arg; const char *fn_name, *type_name; char *body; static const char template[] = "%s %s(%s x)\n" "{\n" " return (exp(x) %s exp(-x)) / 2;\n" "}\n"; static const char fn_name_sinh[] = "sinh"; static const char fn_name_cosh[] = "cosh"; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; type_name = arg->data_type->name; fn_name = sinh_mode ? fn_name_sinh : fn_name_cosh; if (!(body = hlsl_sprintf_alloc(ctx, template, type_name, fn_name, type_name, sinh_mode ? "-" : "+"))) return false; func = hlsl_compile_internal_function(ctx, fn_name, body); vkd3d_free(body); if (!func) return false; return add_user_call(ctx, func, params, loc); } static bool intrinsic_cosh(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return write_cosh_or_sinh(ctx, params, loc, false); } static bool intrinsic_cross(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg1_swzl1, *arg1_swzl2, *arg2_swzl1, *arg2_swzl2; struct hlsl_ir_node *arg1 = params->args[0], *arg2 = params->args[1]; struct hlsl_ir_node *arg1_cast, *arg2_cast, *mul1_neg, *mul1, *mul2; struct hlsl_type *cast_type; enum hlsl_base_type base; if (arg1->data_type->e.numeric.type == HLSL_TYPE_HALF && arg2->data_type->e.numeric.type == HLSL_TYPE_HALF) base = HLSL_TYPE_HALF; else base = HLSL_TYPE_FLOAT; cast_type = hlsl_get_vector_type(ctx, base, 3); if (!(arg1_cast = add_implicit_conversion(ctx, params->instrs, arg1, cast_type, loc))) return false; if (!(arg2_cast = add_implicit_conversion(ctx, params->instrs, arg2, cast_type, loc))) return false; if (!(arg1_swzl1 = hlsl_new_swizzle(ctx, HLSL_SWIZZLE(Z, X, Y, Z), 3, arg1_cast, loc))) return false; hlsl_block_add_instr(params->instrs, arg1_swzl1); if (!(arg2_swzl1 = hlsl_new_swizzle(ctx, HLSL_SWIZZLE(Y, Z, X, Y), 3, arg2_cast, loc))) return false; hlsl_block_add_instr(params->instrs, arg2_swzl1); if (!(mul1 = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, arg1_swzl1, arg2_swzl1, loc))) return false; if (!(mul1_neg = hlsl_new_unary_expr(ctx, HLSL_OP1_NEG, mul1, loc))) return false; hlsl_block_add_instr(params->instrs, mul1_neg); if (!(arg1_swzl2 = hlsl_new_swizzle(ctx, HLSL_SWIZZLE(Y, Z, X, Y), 3, arg1_cast, loc))) return false; hlsl_block_add_instr(params->instrs, arg1_swzl2); if (!(arg2_swzl2 = hlsl_new_swizzle(ctx, HLSL_SWIZZLE(Z, X, Y, Z), 3, arg2_cast, loc))) return false; hlsl_block_add_instr(params->instrs, arg2_swzl2); if (!(mul2 = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, arg1_swzl2, arg2_swzl2, loc))) return false; return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, mul2, mul1_neg, loc); } static bool intrinsic_ddx(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_DSX, arg, loc); } static bool intrinsic_ddx_coarse(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_DSX_COARSE, arg, loc); } static bool intrinsic_ddx_fine(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_DSX_FINE, arg, loc); } static bool intrinsic_ddy(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_DSY, arg, loc); } static bool intrinsic_ddy_coarse(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_DSY_COARSE, arg, loc); } static bool intrinsic_degrees(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg, *deg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; /* 1 rad = 180/pi degree = 57.2957795 degree */ if (!(deg = hlsl_new_float_constant(ctx, 57.2957795f, loc))) return false; hlsl_block_add_instr(params->instrs, deg); return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, arg, deg, loc); } static bool intrinsic_ddy_fine(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_DSY_FINE, arg, loc); } static bool intrinsic_determinant(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { static const char determinant2x2[] = "%s determinant(%s2x2 m)\n" "{\n" " return m._11 * m._22 - m._12 * m._21;\n" "}"; static const char determinant3x3[] = "%s determinant(%s3x3 m)\n" "{\n" " %s2x2 m1 = { m._22, m._23, m._32, m._33 };\n" " %s2x2 m2 = { m._21, m._23, m._31, m._33 };\n" " %s2x2 m3 = { m._21, m._22, m._31, m._32 };\n" " %s3 v1 = { m._11, -m._12, m._13 };\n" " %s3 v2 = { determinant(m1), determinant(m2), determinant(m3) };\n" " return dot(v1, v2);\n" "}"; static const char determinant4x4[] = "%s determinant(%s4x4 m)\n" "{\n" " %s3x3 m1 = { m._22, m._23, m._24, m._32, m._33, m._34, m._42, m._43, m._44 };\n" " %s3x3 m2 = { m._21, m._23, m._24, m._31, m._33, m._34, m._41, m._43, m._44 };\n" " %s3x3 m3 = { m._21, m._22, m._24, m._31, m._32, m._34, m._41, m._42, m._44 };\n" " %s3x3 m4 = { m._21, m._22, m._23, m._31, m._32, m._33, m._41, m._42, m._43 };\n" " %s4 v1 = { m._11, -m._12, m._13, -m._14 };\n" " %s4 v2 = { determinant(m1), determinant(m2), determinant(m3), determinant(m4) };\n" " return dot(v1, v2);\n" "}"; static const char *templates[] = { [2] = determinant2x2, [3] = determinant3x3, [4] = determinant4x4, }; struct hlsl_ir_node *arg = params->args[0]; const struct hlsl_type *type = arg->data_type; struct hlsl_ir_function_decl *func; const char *typename, *template; unsigned int dim; char *body; if (type->class != HLSL_CLASS_SCALAR && type->class != HLSL_CLASS_MATRIX) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Invalid argument type."); return false; } dim = min(type->dimx, type->dimy); if (dim == 1) { if (!(arg = intrinsic_float_convert_arg(ctx, params, arg, loc))) return false; return hlsl_add_load_component(ctx, params->instrs, arg, 0, loc); } typename = type->e.numeric.type == HLSL_TYPE_HALF ? "half" : "float"; template = templates[dim]; switch (dim) { case 2: body = hlsl_sprintf_alloc(ctx, template, typename, typename); break; case 3: body = hlsl_sprintf_alloc(ctx, template, typename, typename, typename, typename, typename, typename, typename); break; case 4: body = hlsl_sprintf_alloc(ctx, template, typename, typename, typename, typename, typename, typename, typename, typename); break; default: vkd3d_unreachable(); } if (!body) return false; func = hlsl_compile_internal_function(ctx, "determinant", body); vkd3d_free(body); if (!func) return false; return add_user_call(ctx, func, params, loc); } static bool intrinsic_distance(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg1, *arg2, *neg, *add, *dot; if (!(arg1 = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; if (!(arg2 = intrinsic_float_convert_arg(ctx, params, params->args[1], loc))) return false; if (!(neg = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_NEG, arg2, loc))) return false; if (!(add = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, arg1, neg, loc))) return false; if (!(dot = add_binary_dot_expr(ctx, params->instrs, add, add, loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_SQRT, dot, loc); } static bool intrinsic_dot(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return !!add_binary_dot_expr(ctx, params->instrs, params->args[0], params->args[1], loc); } static bool intrinsic_exp(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg, *mul, *coeff; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; /* 1/ln(2) */ if (!(coeff = hlsl_new_float_constant(ctx, 1.442695f, loc))) return false; hlsl_block_add_instr(params->instrs, coeff); if (!(mul = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, coeff, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_EXP2, mul, loc); } static bool intrinsic_exp2(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_EXP2, arg, loc); } static bool intrinsic_floor(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_FLOOR, arg, loc); } static bool intrinsic_fmod(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *x, *y, *div, *abs, *frac, *neg_frac, *ge, *select, *zero; struct hlsl_ir_node *operands[HLSL_MAX_OPERANDS] = { 0 }; static const struct hlsl_constant_value zero_value; if (!(x = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; if (!(y = intrinsic_float_convert_arg(ctx, params, params->args[1], loc))) return false; if (!(div = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_DIV, x, y, loc))) return false; if (!(zero = hlsl_new_constant(ctx, div->data_type, &zero_value, loc))) return false; hlsl_block_add_instr(params->instrs, zero); if (!(abs = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_ABS, div, loc))) return false; if (!(frac = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_FRACT, abs, loc))) return false; if (!(neg_frac = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_NEG, frac, loc))) return false; if (!(ge = add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_GEQUAL, div, zero, loc))) return false; operands[0] = ge; operands[1] = frac; operands[2] = neg_frac; if (!(select = add_expr(ctx, params->instrs, HLSL_OP3_TERNARY, operands, x->data_type, loc))) return false; return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, select, y, loc); } static bool intrinsic_frac(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_FRACT, arg, loc); } static bool intrinsic_fwidth(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_function_decl *func; struct hlsl_type *type; char *body; static const char template[] = "%s fwidth(%s x)\n" "{\n" " return abs(ddx(x)) + abs(ddy(x));\n" "}"; if (!elementwise_intrinsic_float_convert_args(ctx, params, loc)) return false; type = params->args[0]->data_type; if (!(body = hlsl_sprintf_alloc(ctx, template, type->name, type->name))) return false; func = hlsl_compile_internal_function(ctx, "fwidth", body); vkd3d_free(body); if (!func) return false; return add_user_call(ctx, func, params, loc); } static bool intrinsic_ldexp(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!elementwise_intrinsic_float_convert_args(ctx, params, loc)) return false; if (!(arg = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_EXP2, params->args[1], loc))) return false; return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, params->args[0], arg, loc); } static bool intrinsic_length(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_type *type = params->args[0]->data_type; struct hlsl_ir_node *arg, *dot; if (type->class == HLSL_CLASS_MATRIX) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Invalid type %s.", string->buffer); hlsl_release_string_buffer(ctx, string); } if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; if (!(dot = add_binary_dot_expr(ctx, params->instrs, arg, arg, loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_SQRT, dot, loc); } static bool intrinsic_lerp(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *neg, *add, *mul; if (!elementwise_intrinsic_float_convert_args(ctx, params, loc)) return false; if (!(neg = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_NEG, params->args[0], loc))) return false; if (!(add = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, params->args[1], neg, loc))) return false; if (!(mul = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, params->args[2], add, loc))) return false; return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, params->args[0], mul, loc); } static struct hlsl_ir_node * add_pow_expr(struct hlsl_ctx *ctx, struct hlsl_block *instrs, struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *log, *mul; if (!(log = add_unary_arithmetic_expr(ctx, instrs, HLSL_OP1_LOG2, arg1, loc))) return NULL; if (!(mul = add_binary_arithmetic_expr(ctx, instrs, HLSL_OP2_MUL, arg2, log, loc))) return NULL; return add_unary_arithmetic_expr(ctx, instrs, HLSL_OP1_EXP2, mul, loc); } static bool intrinsic_lit(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_function_decl *func; static const char body[] = "float4 lit(float n_l, float n_h, float m)\n" "{\n" " float4 ret;\n" " ret.xw = 1.0;\n" " ret.y = max(n_l, 0);\n" " ret.z = (n_l < 0 || n_h < 0) ? 0 : pow(n_h, m);\n" " return ret;\n" "}"; if (params->args[0]->data_type->class != HLSL_CLASS_SCALAR || params->args[1]->data_type->class != HLSL_CLASS_SCALAR || params->args[2]->data_type->class != HLSL_CLASS_SCALAR) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Invalid argument type."); return false; } if (!(func = hlsl_compile_internal_function(ctx, "lit", body))) return false; return add_user_call(ctx, func, params, loc); } static bool intrinsic_log(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *log, *arg, *coeff; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; if (!(log = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_LOG2, arg, loc))) return false; /* ln(2) */ if (!(coeff = hlsl_new_float_constant(ctx, 0.69314718055f, loc))) return false; hlsl_block_add_instr(params->instrs, coeff); return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, log, coeff, loc); } static bool intrinsic_log10(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *log, *arg, *coeff; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; if (!(log = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_LOG2, arg, loc))) return false; /* 1 / log2(10) */ if (!(coeff = hlsl_new_float_constant(ctx, 0.301029996f, loc))) return false; hlsl_block_add_instr(params->instrs, coeff); return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, log, coeff, loc); } static bool intrinsic_log2(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_LOG2, arg, loc); } static bool intrinsic_max(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { if (!elementwise_intrinsic_convert_args(ctx, params, loc)) return false; return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MAX, params->args[0], params->args[1], loc); } static bool intrinsic_min(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { if (!elementwise_intrinsic_convert_args(ctx, params, loc)) return false; return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MIN, params->args[0], params->args[1], loc); } static bool intrinsic_mul(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg1 = params->args[0], *arg2 = params->args[1], *cast1, *cast2; enum hlsl_base_type base = expr_common_base_type(arg1->data_type->e.numeric.type, arg2->data_type->e.numeric.type); struct hlsl_type *cast_type1 = arg1->data_type, *cast_type2 = arg2->data_type, *matrix_type, *ret_type; unsigned int i, j, k, vect_count = 0; struct hlsl_deref var_deref; struct hlsl_ir_load *load; struct hlsl_ir_var *var; if (arg1->data_type->class == HLSL_CLASS_SCALAR || arg2->data_type->class == HLSL_CLASS_SCALAR) return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, arg1, arg2, loc); if (arg1->data_type->class == HLSL_CLASS_VECTOR) { vect_count++; cast_type1 = hlsl_get_matrix_type(ctx, base, arg1->data_type->dimx, 1); } if (arg2->data_type->class == HLSL_CLASS_VECTOR) { vect_count++; cast_type2 = hlsl_get_matrix_type(ctx, base, 1, arg2->data_type->dimx); } matrix_type = hlsl_get_matrix_type(ctx, base, cast_type2->dimx, cast_type1->dimy); if (vect_count == 0) { ret_type = matrix_type; } else if (vect_count == 1) { assert(matrix_type->dimx == 1 || matrix_type->dimy == 1); ret_type = hlsl_get_vector_type(ctx, base, matrix_type->dimx * matrix_type->dimy); } else { assert(matrix_type->dimx == 1 && matrix_type->dimy == 1); ret_type = hlsl_get_scalar_type(ctx, base); } if (!(cast1 = add_implicit_conversion(ctx, params->instrs, arg1, cast_type1, loc))) return false; if (!(cast2 = add_implicit_conversion(ctx, params->instrs, arg2, cast_type2, loc))) return false; if (!(var = hlsl_new_synthetic_var(ctx, "mul", matrix_type, loc))) return false; hlsl_init_simple_deref_from_var(&var_deref, var); for (i = 0; i < matrix_type->dimx; ++i) { for (j = 0; j < matrix_type->dimy; ++j) { struct hlsl_ir_node *instr = NULL; struct hlsl_block block; for (k = 0; k < cast_type1->dimx && k < cast_type2->dimy; ++k) { struct hlsl_ir_node *value1, *value2, *mul; if (!(value1 = hlsl_add_load_component(ctx, params->instrs, cast1, j * cast1->data_type->dimx + k, loc))) return false; if (!(value2 = hlsl_add_load_component(ctx, params->instrs, cast2, k * cast2->data_type->dimx + i, loc))) return false; if (!(mul = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, value1, value2, loc))) return false; if (instr) { if (!(instr = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, instr, mul, loc))) return false; } else { instr = mul; } } if (!hlsl_new_store_component(ctx, &block, &var_deref, j * matrix_type->dimx + i, instr)) return false; hlsl_block_add_block(params->instrs, &block); } } if (!(load = hlsl_new_var_load(ctx, var, loc))) return false; hlsl_block_add_instr(params->instrs, &load->node); return !!add_implicit_conversion(ctx, params->instrs, &load->node, ret_type, loc); } static bool intrinsic_normalize(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_type *type = params->args[0]->data_type; struct hlsl_ir_node *dot, *rsq, *arg; if (type->class == HLSL_CLASS_MATRIX) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Invalid type %s.", string->buffer); hlsl_release_string_buffer(ctx, string); } if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; if (!(dot = add_binary_dot_expr(ctx, params->instrs, arg, arg, loc))) return false; if (!(rsq = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_RSQ, dot, loc))) return false; return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, rsq, arg, loc); } static bool intrinsic_pow(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { if (!elementwise_intrinsic_float_convert_args(ctx, params, loc)) return false; return !!add_pow_expr(ctx, params->instrs, params->args[0], params->args[1], loc); } static bool intrinsic_radians(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg, *rad; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; /* 1 degree = pi/180 rad = 0.0174532925f rad */ if (!(rad = hlsl_new_float_constant(ctx, 0.0174532925f, loc))) return false; hlsl_block_add_instr(params->instrs, rad); return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, arg, rad, loc); } static bool intrinsic_reflect(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *i = params->args[0], *n = params->args[1]; struct hlsl_ir_node *dot, *mul_n, *two_dot, *neg; if (!(dot = add_binary_dot_expr(ctx, params->instrs, i, n, loc))) return false; if (!(two_dot = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, dot, dot, loc))) return false; if (!(mul_n = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, n, two_dot, loc))) return false; if (!(neg = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_NEG, mul_n, loc))) return false; return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, i, neg, loc); } static bool intrinsic_refract(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_type *r_type = params->args[0]->data_type; struct hlsl_type *n_type = params->args[1]->data_type; struct hlsl_type *i_type = params->args[2]->data_type; struct hlsl_type *res_type, *idx_type, *scal_type; struct parse_initializer mut_params; struct hlsl_ir_function_decl *func; enum hlsl_base_type base; char *body; static const char template[] = "%s refract(%s r, %s n, %s i)\n" "{\n" " %s d, t;\n" " d = dot(r, n);\n" " t = 1 - i.x * i.x * (1 - d * d);\n" " return t >= 0.0 ? i.x * r - (i.x * d + sqrt(t)) * n : 0;\n" "}"; if (r_type->class == HLSL_CLASS_MATRIX || n_type->class == HLSL_CLASS_MATRIX || i_type->class == HLSL_CLASS_MATRIX) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Matrix arguments are not supported."); return false; } assert(params->args_count == 3); mut_params = *params; mut_params.args_count = 2; if (!(res_type = elementwise_intrinsic_get_common_type(ctx, &mut_params, loc))) return false; base = expr_common_base_type(res_type->e.numeric.type, i_type->e.numeric.type); base = base == HLSL_TYPE_HALF ? HLSL_TYPE_HALF : HLSL_TYPE_FLOAT; res_type = convert_numeric_type(ctx, res_type, base); idx_type = convert_numeric_type(ctx, i_type, base); scal_type = hlsl_get_scalar_type(ctx, base); if (!(body = hlsl_sprintf_alloc(ctx, template, res_type->name, res_type->name, res_type->name, idx_type->name, scal_type->name))) return false; func = hlsl_compile_internal_function(ctx, "refract", body); vkd3d_free(body); if (!func) return false; return add_user_call(ctx, func, params, loc); } static bool intrinsic_round(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_ROUND, arg, loc); } static bool intrinsic_rsqrt(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_RSQ, arg, loc); } static bool intrinsic_saturate(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_SAT, arg, loc); } static bool intrinsic_sign(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *lt, *neg, *op1, *op2, *zero, *arg = params->args[0]; static const struct hlsl_constant_value zero_value; struct hlsl_type *int_type = hlsl_get_numeric_type(ctx, arg->data_type->class, HLSL_TYPE_INT, arg->data_type->dimx, arg->data_type->dimy); if (!(zero = hlsl_new_constant(ctx, hlsl_get_scalar_type(ctx, arg->data_type->e.numeric.type), &zero_value, loc))) return false; hlsl_block_add_instr(params->instrs, zero); /* Check if 0 < arg, cast bool to int */ if (!(lt = add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_LESS, zero, arg, loc))) return false; if (!(op1 = add_implicit_conversion(ctx, params->instrs, lt, int_type, loc))) return false; /* Check if arg < 0, cast bool to int and invert (meaning true is -1) */ if (!(lt = add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_LESS, arg, zero, loc))) return false; if (!(op2 = add_implicit_conversion(ctx, params->instrs, lt, int_type, loc))) return false; if (!(neg = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_NEG, op2, loc))) return false; /* Adding these two together will make 1 when > 0, -1 when < 0, and 0 when neither */ return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, neg, op1, loc); } static bool intrinsic_sin(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_SIN, arg, loc); } static bool intrinsic_sinh(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return write_cosh_or_sinh(ctx, params, loc, true); } /* smoothstep(a, b, x) = p^2 (3 - 2p), where p = saturate((x - a)/(b - a)) */ static bool intrinsic_smoothstep(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_function_decl *func; struct hlsl_type *type; char *body; static const char template[] = "%s smoothstep(%s low, %s high, %s x)\n" "{\n" " %s p = saturate((x - low) / (high - low));\n" " return (p * p) * (3 - 2 * p);\n" "}"; if (!(type = elementwise_intrinsic_get_common_type(ctx, params, loc))) return false; type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_FLOAT, type->dimx, type->dimy); if (!(body = hlsl_sprintf_alloc(ctx, template, type->name, type->name, type->name, type->name, type->name))) return false; func = hlsl_compile_internal_function(ctx, "smoothstep", body); vkd3d_free(body); if (!func) return false; return add_user_call(ctx, func, params, loc); } static bool intrinsic_sqrt(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_SQRT, arg, loc); } static bool intrinsic_step(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *ge; struct hlsl_type *type; if (!elementwise_intrinsic_float_convert_args(ctx, params, loc)) return false; if (!(ge = add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_GEQUAL, params->args[1], params->args[0], loc))) return false; type = ge->data_type; type = hlsl_get_numeric_type(ctx, type->class, HLSL_TYPE_FLOAT, type->dimx, type->dimy); return !!add_implicit_conversion(ctx, params->instrs, ge, type, loc); } static bool intrinsic_tan(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg = params->args[0], *sin, *cos; if (!(sin = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_SIN, arg, loc))) return false; if (!(cos = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_COS, arg, loc))) return false; return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_DIV, sin, cos, loc); } static bool intrinsic_tanh(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_function_decl *func; struct hlsl_ir_node *arg; struct hlsl_type *type; char *body; static const char template[] = "%s tanh(%s x)\n" "{\n" " %s exp_pos, exp_neg;\n" " exp_pos = exp(x);\n" " exp_neg = exp(-x);\n" " return (exp_pos - exp_neg) / (exp_pos + exp_neg);\n" "}\n"; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; type = arg->data_type; if (!(body = hlsl_sprintf_alloc(ctx, template, type->name, type->name, type->name))) return false; func = hlsl_compile_internal_function(ctx, "tanh", body); vkd3d_free(body); if (!func) return false; return add_user_call(ctx, func, params, loc); } static bool intrinsic_tex(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc, const char *name, enum hlsl_sampler_dim dim) { unsigned int sampler_dim = hlsl_sampler_dim_count(dim); struct hlsl_resource_load_params load_params = { 0 }; const struct hlsl_type *sampler_type; struct hlsl_ir_node *coords, *sample; if (params->args_count != 2 && params->args_count != 4) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Wrong number of arguments to function '%s': expected 2 or 4, but got %u.", name, params->args_count); return false; } sampler_type = params->args[0]->data_type; if (sampler_type->class != HLSL_CLASS_SAMPLER || (sampler_type->sampler_dim != dim && sampler_type->sampler_dim != HLSL_SAMPLER_DIM_GENERIC)) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, sampler_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Wrong type for argument 1 of '%s': expected 'sampler' or '%s', but got '%s'.", name, ctx->builtin_types.sampler[dim]->name, string->buffer); hlsl_release_string_buffer(ctx, string); } if (!strcmp(name, "tex2Dbias") || !strcmp(name, "tex2Dlod")) { struct hlsl_ir_node *lod, *c; if (!strcmp(name, "tex2Dlod")) load_params.type = HLSL_RESOURCE_SAMPLE_LOD; else load_params.type = HLSL_RESOURCE_SAMPLE_LOD_BIAS; if (!(c = hlsl_new_swizzle(ctx, HLSL_SWIZZLE(X, Y, Z, W), sampler_dim, params->args[1], loc))) return false; hlsl_block_add_instr(params->instrs, c); if (!(coords = add_implicit_conversion(ctx, params->instrs, c, hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc))) { return false; } if (!(lod = hlsl_new_swizzle(ctx, HLSL_SWIZZLE(W, W, W, W), 1, params->args[1], loc))) return false; hlsl_block_add_instr(params->instrs, lod); if (!(load_params.lod = add_implicit_conversion(ctx, params->instrs, lod, hlsl_get_scalar_type(ctx, HLSL_TYPE_FLOAT), loc))) { return false; } } else if (!strcmp(name, "tex2Dproj") || !strcmp(name, "tex3Dproj") || !strcmp(name, "texCUBEproj")) { if (!(coords = add_implicit_conversion(ctx, params->instrs, params->args[1], hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, 4), loc))) { return false; } if (hlsl_version_ge(ctx, 4, 0)) { struct hlsl_ir_node *divisor; if (!(divisor = hlsl_new_swizzle(ctx, HLSL_SWIZZLE(W, W, W, W), sampler_dim, coords, loc))) return false; hlsl_block_add_instr(params->instrs, divisor); if (!(coords = hlsl_new_swizzle(ctx, HLSL_SWIZZLE(X, Y, Z, W), sampler_dim, coords, loc))) return false; hlsl_block_add_instr(params->instrs, coords); if (!(coords = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_DIV, coords, divisor, loc))) return false; load_params.type = HLSL_RESOURCE_SAMPLE; } else { load_params.type = HLSL_RESOURCE_SAMPLE_PROJ; } } else if (params->args_count == 4) /* Gradient sampling. */ { if (!(coords = add_implicit_conversion(ctx, params->instrs, params->args[1], hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc))) { return false; } if (!(load_params.ddx = add_implicit_conversion(ctx, params->instrs, params->args[2], hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc))) { return false; } if (!(load_params.ddy = add_implicit_conversion(ctx, params->instrs, params->args[3], hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc))) { return false; } load_params.type = HLSL_RESOURCE_SAMPLE_GRAD; } else { load_params.type = HLSL_RESOURCE_SAMPLE; if (!(coords = add_implicit_conversion(ctx, params->instrs, params->args[1], hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc))) { return false; } } /* tex1D() functions never produce 1D resource declarations. For newer profiles half offset is used for the second coordinate, while older ones appear to replicate first coordinate.*/ if (dim == HLSL_SAMPLER_DIM_1D) { struct hlsl_ir_load *load; struct hlsl_ir_node *half; struct hlsl_ir_var *var; unsigned int idx = 0; if (!(var = hlsl_new_synthetic_var(ctx, "coords", hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, 2), loc))) return false; initialize_var_components(ctx, params->instrs, var, &idx, coords); if (hlsl_version_ge(ctx, 4, 0)) { if (!(half = hlsl_new_float_constant(ctx, 0.5f, loc))) return false; hlsl_block_add_instr(params->instrs, half); initialize_var_components(ctx, params->instrs, var, &idx, half); } else initialize_var_components(ctx, params->instrs, var, &idx, coords); if (!(load = hlsl_new_var_load(ctx, var, loc))) return false; hlsl_block_add_instr(params->instrs, &load->node); coords = &load->node; dim = HLSL_SAMPLER_DIM_2D; } load_params.coords = coords; load_params.resource = params->args[0]; load_params.format = hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, 4); load_params.sampling_dim = dim; if (!(sample = hlsl_new_resource_load(ctx, &load_params, loc))) return false; hlsl_block_add_instr(params->instrs, sample); return true; } static bool intrinsic_tex1D(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return intrinsic_tex(ctx, params, loc, "tex1D", HLSL_SAMPLER_DIM_1D); } static bool intrinsic_tex1Dgrad(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return intrinsic_tex(ctx, params, loc, "tex1Dgrad", HLSL_SAMPLER_DIM_1D); } static bool intrinsic_tex2D(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return intrinsic_tex(ctx, params, loc, "tex2D", HLSL_SAMPLER_DIM_2D); } static bool intrinsic_tex2Dbias(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return intrinsic_tex(ctx, params, loc, "tex2Dbias", HLSL_SAMPLER_DIM_2D); } static bool intrinsic_tex2Dgrad(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return intrinsic_tex(ctx, params, loc, "tex2Dgrad", HLSL_SAMPLER_DIM_2D); } static bool intrinsic_tex2Dlod(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return intrinsic_tex(ctx, params, loc, "tex2Dlod", HLSL_SAMPLER_DIM_2D); } static bool intrinsic_tex2Dproj(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return intrinsic_tex(ctx, params, loc, "tex2Dproj", HLSL_SAMPLER_DIM_2D); } static bool intrinsic_tex3D(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return intrinsic_tex(ctx, params, loc, "tex3D", HLSL_SAMPLER_DIM_3D); } static bool intrinsic_tex3Dgrad(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return intrinsic_tex(ctx, params, loc, "tex3Dgrad", HLSL_SAMPLER_DIM_3D); } static bool intrinsic_tex3Dproj(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return intrinsic_tex(ctx, params, loc, "tex3Dproj", HLSL_SAMPLER_DIM_3D); } static bool intrinsic_texCUBE(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return intrinsic_tex(ctx, params, loc, "texCUBE", HLSL_SAMPLER_DIM_CUBE); } static bool intrinsic_texCUBEgrad(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return intrinsic_tex(ctx, params, loc, "texCUBEgrad", HLSL_SAMPLER_DIM_CUBE); } static bool intrinsic_texCUBEproj(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { return intrinsic_tex(ctx, params, loc, "texCUBEproj", HLSL_SAMPLER_DIM_CUBE); } static bool intrinsic_transpose(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg = params->args[0]; struct hlsl_type *arg_type = arg->data_type; struct hlsl_ir_load *var_load; struct hlsl_deref var_deref; struct hlsl_type *mat_type; struct hlsl_ir_node *load; struct hlsl_ir_var *var; unsigned int i, j; if (arg_type->class != HLSL_CLASS_SCALAR && arg_type->class != HLSL_CLASS_MATRIX) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, arg_type))) hlsl_error(ctx, &arg->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Wrong type for argument 1 of transpose(): expected a matrix or scalar type, but got '%s'.", string->buffer); hlsl_release_string_buffer(ctx, string); return false; } if (arg_type->class == HLSL_CLASS_SCALAR) { hlsl_block_add_instr(params->instrs, arg); return true; } mat_type = hlsl_get_matrix_type(ctx, arg_type->e.numeric.type, arg_type->dimy, arg_type->dimx); if (!(var = hlsl_new_synthetic_var(ctx, "transpose", mat_type, loc))) return false; hlsl_init_simple_deref_from_var(&var_deref, var); for (i = 0; i < arg_type->dimx; ++i) { for (j = 0; j < arg_type->dimy; ++j) { struct hlsl_block block; if (!(load = hlsl_add_load_component(ctx, params->instrs, arg, j * arg->data_type->dimx + i, loc))) return false; if (!hlsl_new_store_component(ctx, &block, &var_deref, i * var->data_type->dimx + j, load)) return false; hlsl_block_add_block(params->instrs, &block); } } if (!(var_load = hlsl_new_var_load(ctx, var, loc))) return false; hlsl_block_add_instr(params->instrs, &var_load->node); return true; } static bool intrinsic_trunc(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg; if (!(arg = intrinsic_float_convert_arg(ctx, params, params->args[0], loc))) return false; return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_TRUNC, arg, loc); } static bool intrinsic_d3dcolor_to_ubyte4(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *arg = params->args[0], *ret, *c, *swizzle; struct hlsl_type *arg_type = arg->data_type; if (arg_type->class != HLSL_CLASS_SCALAR && !(arg_type->class == HLSL_CLASS_VECTOR && arg_type->dimx == 4)) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, arg_type))) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Wrong argument type '%s'.", string->buffer); hlsl_release_string_buffer(ctx, string); } return false; } if (!(arg = intrinsic_float_convert_arg(ctx, params, arg, loc))) return false; if (!(c = hlsl_new_float_constant(ctx, 255.0f + (0.5f / 256.0f), loc))) return false; hlsl_block_add_instr(params->instrs, c); if (arg_type->class == HLSL_CLASS_VECTOR) { if (!(swizzle = hlsl_new_swizzle(ctx, HLSL_SWIZZLE(Z, Y, X, W), 4, arg, loc))) return false; hlsl_block_add_instr(params->instrs, swizzle); arg = swizzle; } if (!(ret = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, arg, c, loc))) return false; if (hlsl_version_ge(ctx, 4, 0)) return !!add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_TRUNC, ret, loc); return true; } static const struct intrinsic_function { const char *name; int param_count; bool check_numeric; bool (*handler)(struct hlsl_ctx *ctx, const struct parse_initializer *params, const struct vkd3d_shader_location *loc); } intrinsic_functions[] = { /* Note: these entries should be kept in alphabetical order. */ {"D3DCOLORtoUBYTE4", 1, true, intrinsic_d3dcolor_to_ubyte4}, {"abs", 1, true, intrinsic_abs}, {"acos", 1, true, intrinsic_acos}, {"all", 1, true, intrinsic_all}, {"any", 1, true, intrinsic_any}, {"asfloat", 1, true, intrinsic_asfloat}, {"asin", 1, true, intrinsic_asin}, {"asuint", -1, true, intrinsic_asuint}, {"atan", 1, true, intrinsic_atan}, {"atan2", 2, true, intrinsic_atan2}, {"ceil", 1, true, intrinsic_ceil}, {"clamp", 3, true, intrinsic_clamp}, {"clip", 1, true, intrinsic_clip}, {"cos", 1, true, intrinsic_cos}, {"cosh", 1, true, intrinsic_cosh}, {"cross", 2, true, intrinsic_cross}, {"ddx", 1, true, intrinsic_ddx}, {"ddx_coarse", 1, true, intrinsic_ddx_coarse}, {"ddx_fine", 1, true, intrinsic_ddx_fine}, {"ddy", 1, true, intrinsic_ddy}, {"ddy_coarse", 1, true, intrinsic_ddy_coarse}, {"ddy_fine", 1, true, intrinsic_ddy_fine}, {"degrees", 1, true, intrinsic_degrees}, {"determinant", 1, true, intrinsic_determinant}, {"distance", 2, true, intrinsic_distance}, {"dot", 2, true, intrinsic_dot}, {"exp", 1, true, intrinsic_exp}, {"exp2", 1, true, intrinsic_exp2}, {"floor", 1, true, intrinsic_floor}, {"fmod", 2, true, intrinsic_fmod}, {"frac", 1, true, intrinsic_frac}, {"fwidth", 1, true, intrinsic_fwidth}, {"ldexp", 2, true, intrinsic_ldexp}, {"length", 1, true, intrinsic_length}, {"lerp", 3, true, intrinsic_lerp}, {"lit", 3, true, intrinsic_lit}, {"log", 1, true, intrinsic_log}, {"log10", 1, true, intrinsic_log10}, {"log2", 1, true, intrinsic_log2}, {"max", 2, true, intrinsic_max}, {"min", 2, true, intrinsic_min}, {"mul", 2, true, intrinsic_mul}, {"normalize", 1, true, intrinsic_normalize}, {"pow", 2, true, intrinsic_pow}, {"radians", 1, true, intrinsic_radians}, {"reflect", 2, true, intrinsic_reflect}, {"refract", 3, true, intrinsic_refract}, {"round", 1, true, intrinsic_round}, {"rsqrt", 1, true, intrinsic_rsqrt}, {"saturate", 1, true, intrinsic_saturate}, {"sign", 1, true, intrinsic_sign}, {"sin", 1, true, intrinsic_sin}, {"sinh", 1, true, intrinsic_sinh}, {"smoothstep", 3, true, intrinsic_smoothstep}, {"sqrt", 1, true, intrinsic_sqrt}, {"step", 2, true, intrinsic_step}, {"tan", 1, true, intrinsic_tan}, {"tanh", 1, true, intrinsic_tanh}, {"tex1D", -1, false, intrinsic_tex1D}, {"tex1Dgrad", 4, false, intrinsic_tex1Dgrad}, {"tex2D", -1, false, intrinsic_tex2D}, {"tex2Dbias", 2, false, intrinsic_tex2Dbias}, {"tex2Dgrad", 4, false, intrinsic_tex2Dgrad}, {"tex2Dlod", 2, false, intrinsic_tex2Dlod}, {"tex2Dproj", 2, false, intrinsic_tex2Dproj}, {"tex3D", -1, false, intrinsic_tex3D}, {"tex3Dgrad", 4, false, intrinsic_tex3Dgrad}, {"tex3Dproj", 2, false, intrinsic_tex3Dproj}, {"texCUBE", -1, false, intrinsic_texCUBE}, {"texCUBEgrad", 4, false, intrinsic_texCUBEgrad}, {"texCUBEproj", 2, false, intrinsic_texCUBEproj}, {"transpose", 1, true, intrinsic_transpose}, {"trunc", 1, true, intrinsic_trunc}, }; static int intrinsic_function_name_compare(const void *a, const void *b) { const struct intrinsic_function *func = b; return strcmp(a, func->name); } static struct hlsl_block *add_call(struct hlsl_ctx *ctx, const char *name, struct parse_initializer *args, const struct vkd3d_shader_location *loc) { struct intrinsic_function *intrinsic; struct hlsl_ir_function_decl *decl; if ((decl = find_function_call(ctx, name, args, loc))) { if (!add_user_call(ctx, decl, args, loc)) goto fail; } else if ((intrinsic = bsearch(name, intrinsic_functions, ARRAY_SIZE(intrinsic_functions), sizeof(*intrinsic_functions), intrinsic_function_name_compare))) { if (intrinsic->param_count >= 0 && args->args_count != intrinsic->param_count) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Wrong number of arguments to function '%s': expected %u, but got %u.", name, intrinsic->param_count, args->args_count); goto fail; } if (intrinsic->check_numeric) { unsigned int i; for (i = 0; i < args->args_count; ++i) { if (!hlsl_is_numeric_type(args->args[i]->data_type)) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, args->args[i]->data_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Wrong type for argument %u of '%s': expected a numeric type, but got '%s'.", i + 1, name, string->buffer); hlsl_release_string_buffer(ctx, string); goto fail; } } } if (!intrinsic->handler(ctx, args, loc)) goto fail; } else if (rb_get(&ctx->functions, name)) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED, "No compatible %u parameter declaration for \"%s\" found.", args->args_count, name); goto fail; } else { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED, "Function \"%s\" is not defined.", name); goto fail; } vkd3d_free(args->args); return args->instrs; fail: free_parse_initializer(args); return NULL; } static struct hlsl_block *add_constructor(struct hlsl_ctx *ctx, struct hlsl_type *type, struct parse_initializer *params, const struct vkd3d_shader_location *loc) { struct hlsl_ir_load *load; struct hlsl_ir_var *var; unsigned int i, idx = 0; if (!(var = hlsl_new_synthetic_var(ctx, "constructor", type, loc))) return NULL; for (i = 0; i < params->args_count; ++i) initialize_var_components(ctx, params->instrs, var, &idx, params->args[i]); if (!(load = hlsl_new_var_load(ctx, var, loc))) return NULL; hlsl_block_add_instr(params->instrs, &load->node); vkd3d_free(params->args); return params->instrs; } static bool add_ternary(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *cond, struct hlsl_ir_node *first, struct hlsl_ir_node *second) { struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {0}; struct hlsl_type *cond_type = cond->data_type; struct hlsl_type *common_type; if (cond_type->class > HLSL_CLASS_LAST_NUMERIC) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, cond_type))) hlsl_error(ctx, &cond->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Ternary condition type '%s' is not numeric.", string->buffer); hlsl_release_string_buffer(ctx, string); } if (first->data_type->class <= HLSL_CLASS_LAST_NUMERIC && second->data_type->class <= HLSL_CLASS_LAST_NUMERIC) { if (!(common_type = get_common_numeric_type(ctx, first, second, &first->loc))) return false; if (cond_type->dimx == 1 && cond_type->dimy == 1) { cond_type = hlsl_get_numeric_type(ctx, common_type->class, HLSL_TYPE_BOOL, common_type->dimx, common_type->dimy); if (!(cond = add_implicit_conversion(ctx, block, cond, cond_type, &cond->loc))) return false; } else { cond_type = hlsl_get_numeric_type(ctx, cond_type->class, HLSL_TYPE_BOOL, cond_type->dimx, cond_type->dimy); if (!(cond = add_implicit_conversion(ctx, block, cond, cond_type, &cond->loc))) return false; if (common_type->dimx == 1 && common_type->dimy == 1) { common_type = hlsl_get_numeric_type(ctx, cond_type->class, common_type->e.numeric.type, cond_type->dimx, cond_type->dimy); } else if (cond_type->dimx != common_type->dimx || cond_type->dimy != common_type->dimy) { /* This condition looks wrong but is correct. * floatN is compatible with float1xN, but not with floatNx1. */ struct vkd3d_string_buffer *cond_string, *value_string; cond_string = hlsl_type_to_string(ctx, cond_type); value_string = hlsl_type_to_string(ctx, common_type); if (cond_string && value_string) hlsl_error(ctx, &first->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Ternary condition type '%s' is not compatible with value type '%s'.", cond_string->buffer, value_string->buffer); hlsl_release_string_buffer(ctx, cond_string); hlsl_release_string_buffer(ctx, value_string); } } if (!(first = add_implicit_conversion(ctx, block, first, common_type, &first->loc))) return false; if (!(second = add_implicit_conversion(ctx, block, second, common_type, &second->loc))) return false; } else { struct vkd3d_string_buffer *first_string, *second_string; if (!hlsl_types_are_equal(first->data_type, second->data_type)) { first_string = hlsl_type_to_string(ctx, first->data_type); second_string = hlsl_type_to_string(ctx, second->data_type); if (first_string && second_string) hlsl_error(ctx, &first->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Ternary argument types '%s' and '%s' do not match.", first_string->buffer, second_string->buffer); hlsl_release_string_buffer(ctx, first_string); hlsl_release_string_buffer(ctx, second_string); } cond_type = hlsl_get_numeric_type(ctx, cond_type->class, HLSL_TYPE_BOOL, cond_type->dimx, cond_type->dimy); if (!(cond = add_implicit_conversion(ctx, block, cond, cond_type, &cond->loc))) return false; common_type = first->data_type; } assert(cond->data_type->e.numeric.type == HLSL_TYPE_BOOL); args[0] = cond; args[1] = first; args[2] = second; return add_expr(ctx, block, HLSL_OP3_TERNARY, args, common_type, &first->loc); } static unsigned int hlsl_offset_dim_count(enum hlsl_sampler_dim dim) { switch (dim) { case HLSL_SAMPLER_DIM_1D: case HLSL_SAMPLER_DIM_1DARRAY: return 1; case HLSL_SAMPLER_DIM_2D: case HLSL_SAMPLER_DIM_2DMS: case HLSL_SAMPLER_DIM_2DARRAY: case HLSL_SAMPLER_DIM_2DMSARRAY: return 2; case HLSL_SAMPLER_DIM_3D: return 3; case HLSL_SAMPLER_DIM_CUBE: case HLSL_SAMPLER_DIM_CUBEARRAY: case HLSL_SAMPLER_DIM_BUFFER: /* Offset parameters not supported for these types. */ return 0; default: vkd3d_unreachable(); } } static bool raise_invalid_method_object_type(struct hlsl_ctx *ctx, const struct hlsl_type *object_type, const char *method, const struct vkd3d_shader_location *loc) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, object_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED, "Method '%s' is not defined on type '%s'.", method, string->buffer); hlsl_release_string_buffer(ctx, string); return false; } static bool add_load_method_call(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *object, const char *name, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { const struct hlsl_type *object_type = object->data_type; struct hlsl_resource_load_params load_params = {.type = HLSL_RESOURCE_LOAD}; unsigned int sampler_dim, offset_dim; struct hlsl_ir_node *load; bool multisampled; if (object_type->sampler_dim == HLSL_SAMPLER_DIM_STRUCTURED_BUFFER) { hlsl_fixme(ctx, loc, "Method '%s' for structured buffers.", name); return false; } sampler_dim = hlsl_sampler_dim_count(object_type->sampler_dim); offset_dim = hlsl_offset_dim_count(object_type->sampler_dim); multisampled = object_type->sampler_dim == HLSL_SAMPLER_DIM_2DMS || object_type->sampler_dim == HLSL_SAMPLER_DIM_2DMSARRAY; if (params->args_count < 1 + multisampled || params->args_count > 2 + multisampled + !!offset_dim) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Wrong number of arguments to method 'Load': expected between %u and %u, but got %u.", 1 + multisampled, 2 + multisampled + !!offset_dim, params->args_count); return false; } if (multisampled) { if (!(load_params.sample_index = add_implicit_conversion(ctx, block, params->args[1], hlsl_get_scalar_type(ctx, HLSL_TYPE_INT), loc))) return false; } if (!!offset_dim && params->args_count > 1 + multisampled) { if (!(load_params.texel_offset = add_implicit_conversion(ctx, block, params->args[1 + multisampled], hlsl_get_vector_type(ctx, HLSL_TYPE_INT, offset_dim), loc))) return false; } if (params->args_count > 1 + multisampled + !!offset_dim) { hlsl_fixme(ctx, loc, "Tiled resource status argument."); } /* +1 for the mipmap level for non-multisampled textures */ if (!(load_params.coords = add_implicit_conversion(ctx, block, params->args[0], hlsl_get_vector_type(ctx, HLSL_TYPE_INT, sampler_dim + !multisampled), loc))) return false; load_params.format = object_type->e.resource.format; load_params.resource = object; if (!(load = hlsl_new_resource_load(ctx, &load_params, loc))) return false; hlsl_block_add_instr(block, load); return true; } static bool add_sample_method_call(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *object, const char *name, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { const struct hlsl_type *object_type = object->data_type; struct hlsl_resource_load_params load_params = {.type = HLSL_RESOURCE_SAMPLE}; unsigned int sampler_dim, offset_dim; const struct hlsl_type *sampler_type; struct hlsl_ir_node *load; sampler_dim = hlsl_sampler_dim_count(object_type->sampler_dim); offset_dim = hlsl_offset_dim_count(object_type->sampler_dim); if (params->args_count < 2 || params->args_count > 4 + !!offset_dim) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Wrong number of arguments to method 'Sample': expected from 2 to %u, but got %u.", 4 + !!offset_dim, params->args_count); return false; } sampler_type = params->args[0]->data_type; if (sampler_type->class != HLSL_CLASS_SAMPLER || sampler_type->sampler_dim != HLSL_SAMPLER_DIM_GENERIC) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, sampler_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Wrong type for argument 0 of Sample(): expected 'sampler', but got '%s'.", string->buffer); hlsl_release_string_buffer(ctx, string); return false; } if (!(load_params.coords = add_implicit_conversion(ctx, block, params->args[1], hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc))) return false; if (offset_dim && params->args_count > 2) { if (!(load_params.texel_offset = add_implicit_conversion(ctx, block, params->args[2], hlsl_get_vector_type(ctx, HLSL_TYPE_INT, offset_dim), loc))) return false; } if (params->args_count > 2 + !!offset_dim) hlsl_fixme(ctx, loc, "Sample() clamp parameter."); if (params->args_count > 3 + !!offset_dim) hlsl_fixme(ctx, loc, "Tiled resource status argument."); load_params.format = object_type->e.resource.format; load_params.resource = object; load_params.sampler = params->args[0]; if (!(load = hlsl_new_resource_load(ctx, &load_params, loc))) return false; hlsl_block_add_instr(block, load); return true; } static bool add_sample_cmp_method_call(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *object, const char *name, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { const struct hlsl_type *object_type = object->data_type; struct hlsl_resource_load_params load_params = { 0 }; unsigned int sampler_dim, offset_dim; const struct hlsl_type *sampler_type; struct hlsl_ir_node *load; sampler_dim = hlsl_sampler_dim_count(object_type->sampler_dim); offset_dim = hlsl_offset_dim_count(object_type->sampler_dim); if (!strcmp(name, "SampleCmpLevelZero")) load_params.type = HLSL_RESOURCE_SAMPLE_CMP_LZ; else load_params.type = HLSL_RESOURCE_SAMPLE_CMP; if (params->args_count < 3 || params->args_count > 5 + !!offset_dim) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Wrong number of arguments to method '%s': expected from 3 to %u, but got %u.", name, 5 + !!offset_dim, params->args_count); return false; } sampler_type = params->args[0]->data_type; if (sampler_type->class != HLSL_CLASS_SAMPLER || sampler_type->sampler_dim != HLSL_SAMPLER_DIM_COMPARISON) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, sampler_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Wrong type for argument 0 of %s(): expected 'SamplerComparisonState', but got '%s'.", name, string->buffer); hlsl_release_string_buffer(ctx, string); return false; } if (!(load_params.coords = add_implicit_conversion(ctx, block, params->args[1], hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc))) return false; if (!(load_params.cmp = add_implicit_conversion(ctx, block, params->args[2], hlsl_get_scalar_type(ctx, HLSL_TYPE_FLOAT), loc))) load_params.cmp = params->args[2]; if (offset_dim && params->args_count > 3) { if (!(load_params.texel_offset = add_implicit_conversion(ctx, block, params->args[2], hlsl_get_vector_type(ctx, HLSL_TYPE_INT, offset_dim), loc))) return false; } if (params->args_count > 3 + !!offset_dim) hlsl_fixme(ctx, loc, "%s() clamp parameter.", name); if (params->args_count > 4 + !!offset_dim) hlsl_fixme(ctx, loc, "Tiled resource status argument."); load_params.format = object_type->e.resource.format; load_params.resource = object; load_params.sampler = params->args[0]; if (!(load = hlsl_new_resource_load(ctx, &load_params, loc))) return false; hlsl_block_add_instr(block, load); return true; } static bool add_gather_method_call(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *object, const char *name, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { const struct hlsl_type *object_type = object->data_type; struct hlsl_resource_load_params load_params = {0}; unsigned int sampler_dim, offset_dim; const struct hlsl_type *sampler_type; struct hlsl_ir_node *load; unsigned int read_channel; sampler_dim = hlsl_sampler_dim_count(object_type->sampler_dim); offset_dim = hlsl_offset_dim_count(object_type->sampler_dim); if (!strcmp(name, "GatherGreen")) { load_params.type = HLSL_RESOURCE_GATHER_GREEN; read_channel = 1; } else if (!strcmp(name, "GatherBlue")) { load_params.type = HLSL_RESOURCE_GATHER_BLUE; read_channel = 2; } else if (!strcmp(name, "GatherAlpha")) { load_params.type = HLSL_RESOURCE_GATHER_ALPHA; read_channel = 3; } else { load_params.type = HLSL_RESOURCE_GATHER_RED; read_channel = 0; } if (!strcmp(name, "Gather") || !offset_dim) { if (params->args_count < 2 || params->args_count > 3 + !!offset_dim) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Wrong number of arguments to method '%s': expected from 2 to %u, but got %u.", name, 3 + !!offset_dim, params->args_count); return false; } } else if (params->args_count < 2 || params->args_count == 5 || params->args_count > 7) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Wrong number of arguments to method '%s': expected 2, 3, 4, 6 or 7, but got %u.", name, params->args_count); return false; } if (params->args_count == 3 + !!offset_dim || params->args_count == 7) hlsl_fixme(ctx, loc, "Tiled resource status argument."); if (params->args_count == 6 || params->args_count == 7) { hlsl_fixme(ctx, loc, "Multiple %s() offset parameters.", name); } else if (offset_dim && params->args_count > 2) { if (!(load_params.texel_offset = add_implicit_conversion(ctx, block, params->args[2], hlsl_get_vector_type(ctx, HLSL_TYPE_INT, offset_dim), loc))) return false; } sampler_type = params->args[0]->data_type; if (sampler_type->class != HLSL_CLASS_SAMPLER || sampler_type->sampler_dim != HLSL_SAMPLER_DIM_GENERIC) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, sampler_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Wrong type for argument 1 of %s(): expected 'sampler', but got '%s'.", name, string->buffer); hlsl_release_string_buffer(ctx, string); return false; } if (read_channel >= object_type->e.resource.format->dimx) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Method %s() requires at least %u channels.", name, read_channel + 1); return false; } if (!(load_params.coords = add_implicit_conversion(ctx, block, params->args[1], hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc))) return false; load_params.format = hlsl_get_vector_type(ctx, object_type->e.resource.format->e.numeric.type, 4); load_params.resource = object; load_params.sampler = params->args[0]; if (!(load = hlsl_new_resource_load(ctx, &load_params, loc))) return false; hlsl_block_add_instr(block, load); return true; } static bool add_assignment_from_component(struct hlsl_ctx *ctx, struct hlsl_block *instrs, struct hlsl_ir_node *dest, struct hlsl_ir_node *src, unsigned int component, const struct vkd3d_shader_location *loc) { struct hlsl_ir_node *load; if (!dest) return true; if (!(load = hlsl_add_load_component(ctx, instrs, src, component, loc))) return false; if (!add_assignment(ctx, instrs, dest, ASSIGN_OP_ASSIGN, load)) return false; return true; } static bool add_getdimensions_method_call(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *object, const char *name, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { const struct hlsl_type *object_type = object->data_type; bool uint_resinfo, has_uint_arg, has_float_arg; struct hlsl_resource_load_params load_params; struct hlsl_ir_node *sample_info, *res_info; struct hlsl_ir_node *zero = NULL, *void_ret; struct hlsl_type *uint_type, *float_type; unsigned int i, j; enum func_argument { ARG_MIP_LEVEL, ARG_WIDTH, ARG_HEIGHT, ARG_ELEMENT_COUNT, ARG_LEVEL_COUNT, ARG_SAMPLE_COUNT, ARG_MAX_ARGS, }; struct hlsl_ir_node *args[ARG_MAX_ARGS] = { 0 }; static const struct overload { enum hlsl_sampler_dim sampler_dim; unsigned int args_count; enum func_argument args[ARG_MAX_ARGS]; } overloads[] = { { HLSL_SAMPLER_DIM_1D, 1, { ARG_WIDTH } }, { HLSL_SAMPLER_DIM_1D, 3, { ARG_MIP_LEVEL, ARG_WIDTH, ARG_LEVEL_COUNT } }, { HLSL_SAMPLER_DIM_1DARRAY, 2, { ARG_WIDTH, ARG_ELEMENT_COUNT } }, { HLSL_SAMPLER_DIM_1DARRAY, 4, { ARG_MIP_LEVEL, ARG_WIDTH, ARG_ELEMENT_COUNT, ARG_LEVEL_COUNT } }, { HLSL_SAMPLER_DIM_2D, 2, { ARG_WIDTH, ARG_HEIGHT } }, { HLSL_SAMPLER_DIM_2D, 4, { ARG_MIP_LEVEL, ARG_WIDTH, ARG_HEIGHT, ARG_LEVEL_COUNT } }, { HLSL_SAMPLER_DIM_2DARRAY, 3, { ARG_WIDTH, ARG_HEIGHT, ARG_ELEMENT_COUNT } }, { HLSL_SAMPLER_DIM_2DARRAY, 5, { ARG_MIP_LEVEL, ARG_WIDTH, ARG_HEIGHT, ARG_ELEMENT_COUNT, ARG_LEVEL_COUNT } }, { HLSL_SAMPLER_DIM_3D, 3, { ARG_WIDTH, ARG_HEIGHT, ARG_ELEMENT_COUNT } }, { HLSL_SAMPLER_DIM_3D, 5, { ARG_MIP_LEVEL, ARG_WIDTH, ARG_HEIGHT, ARG_ELEMENT_COUNT, ARG_LEVEL_COUNT } }, { HLSL_SAMPLER_DIM_CUBE, 2, { ARG_WIDTH, ARG_HEIGHT } }, { HLSL_SAMPLER_DIM_CUBE, 4, { ARG_MIP_LEVEL, ARG_WIDTH, ARG_HEIGHT, ARG_LEVEL_COUNT } }, { HLSL_SAMPLER_DIM_CUBEARRAY, 3, { ARG_WIDTH, ARG_HEIGHT, ARG_ELEMENT_COUNT } }, { HLSL_SAMPLER_DIM_CUBEARRAY, 5, { ARG_MIP_LEVEL, ARG_WIDTH, ARG_HEIGHT, ARG_ELEMENT_COUNT, ARG_LEVEL_COUNT } }, { HLSL_SAMPLER_DIM_2DMS, 3, { ARG_WIDTH, ARG_HEIGHT, ARG_SAMPLE_COUNT } }, { HLSL_SAMPLER_DIM_2DMSARRAY, 4, { ARG_WIDTH, ARG_HEIGHT, ARG_ELEMENT_COUNT, ARG_SAMPLE_COUNT } }, { HLSL_SAMPLER_DIM_BUFFER, 1, { ARG_WIDTH} }, }; const struct overload *o = NULL; if (object_type->sampler_dim == HLSL_SAMPLER_DIM_STRUCTURED_BUFFER) { hlsl_fixme(ctx, loc, "Method '%s' for structured buffers.", name); return false; } uint_type = hlsl_get_scalar_type(ctx, HLSL_TYPE_UINT); float_type = hlsl_get_scalar_type(ctx, HLSL_TYPE_FLOAT); has_uint_arg = has_float_arg = false; for (i = 0; i < ARRAY_SIZE(overloads); ++i) { const struct overload *iter = &overloads[i]; if (iter->sampler_dim == object_type->sampler_dim && iter->args_count == params->args_count) { for (j = 0; j < params->args_count; ++j) { args[iter->args[j]] = params->args[j]; /* Input parameter. */ if (iter->args[j] == ARG_MIP_LEVEL) { if (!(args[ARG_MIP_LEVEL] = add_implicit_conversion(ctx, block, args[ARG_MIP_LEVEL], hlsl_get_scalar_type(ctx, HLSL_TYPE_UINT), loc))) { return false; } continue; } has_float_arg |= hlsl_types_are_equal(params->args[j]->data_type, float_type); has_uint_arg |= hlsl_types_are_equal(params->args[j]->data_type, uint_type); if (params->args[j]->data_type->class != HLSL_CLASS_SCALAR) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Expected scalar arguments."); break; } } o = iter; break; } } uint_resinfo = !has_float_arg && has_uint_arg; if (!o) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, object_type))) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Unexpected number of arguments %u for %s.%s().", params->args_count, string->buffer, name); hlsl_release_string_buffer(ctx, string); } } if (!args[ARG_MIP_LEVEL]) { if (!(zero = hlsl_new_uint_constant(ctx, 0, loc))) return false; hlsl_block_add_instr(block, zero); args[ARG_MIP_LEVEL] = zero; } memset(&load_params, 0, sizeof(load_params)); load_params.type = HLSL_RESOURCE_RESINFO; load_params.resource = object; load_params.lod = args[ARG_MIP_LEVEL]; load_params.format = hlsl_get_vector_type(ctx, uint_resinfo ? HLSL_TYPE_UINT : HLSL_TYPE_FLOAT, 4); if (!(res_info = hlsl_new_resource_load(ctx, &load_params, loc))) return false; hlsl_block_add_instr(block, res_info); if (!add_assignment_from_component(ctx, block, args[ARG_WIDTH], res_info, 0, loc)) return false; if (!add_assignment_from_component(ctx, block, args[ARG_HEIGHT], res_info, 1, loc)) return false; if (!add_assignment_from_component(ctx, block, args[ARG_ELEMENT_COUNT], res_info, object_type->sampler_dim == HLSL_SAMPLER_DIM_1DARRAY ? 1 : 2, loc)) { return false; } if (!add_assignment_from_component(ctx, block, args[ARG_LEVEL_COUNT], res_info, 3, loc)) return false; if (args[ARG_SAMPLE_COUNT]) { memset(&load_params, 0, sizeof(load_params)); load_params.type = HLSL_RESOURCE_SAMPLE_INFO; load_params.resource = object; load_params.format = args[ARG_SAMPLE_COUNT]->data_type; if (!(sample_info = hlsl_new_resource_load(ctx, &load_params, loc))) return false; hlsl_block_add_instr(block, sample_info); if (!add_assignment(ctx, block, args[ARG_SAMPLE_COUNT], ASSIGN_OP_ASSIGN, sample_info)) return false; } if (!(void_ret = hlsl_new_void_expr(ctx, loc))) return false; hlsl_block_add_instr(block, void_ret); return true; } static bool add_sample_lod_method_call(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *object, const char *name, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { const struct hlsl_type *object_type = object->data_type; struct hlsl_resource_load_params load_params = { 0 }; unsigned int sampler_dim, offset_dim; const struct hlsl_type *sampler_type; struct hlsl_ir_node *load; sampler_dim = hlsl_sampler_dim_count(object_type->sampler_dim); offset_dim = hlsl_offset_dim_count(object_type->sampler_dim); if (!strcmp(name, "SampleLevel")) load_params.type = HLSL_RESOURCE_SAMPLE_LOD; else load_params.type = HLSL_RESOURCE_SAMPLE_LOD_BIAS; if (params->args_count < 3 || params->args_count > 4 + !!offset_dim) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Wrong number of arguments to method '%s': expected from 3 to %u, but got %u.", name, 4 + !!offset_dim, params->args_count); return false; } sampler_type = params->args[0]->data_type; if (sampler_type->class != HLSL_CLASS_SAMPLER || sampler_type->sampler_dim != HLSL_SAMPLER_DIM_GENERIC) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, sampler_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Wrong type for argument 0 of %s(): expected 'sampler', but got '%s'.", name, string->buffer); hlsl_release_string_buffer(ctx, string); return false; } if (!(load_params.coords = add_implicit_conversion(ctx, block, params->args[1], hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc))) load_params.coords = params->args[1]; if (!(load_params.lod = add_implicit_conversion(ctx, block, params->args[2], hlsl_get_scalar_type(ctx, HLSL_TYPE_FLOAT), loc))) load_params.lod = params->args[2]; if (offset_dim && params->args_count > 3) { if (!(load_params.texel_offset = add_implicit_conversion(ctx, block, params->args[3], hlsl_get_vector_type(ctx, HLSL_TYPE_INT, offset_dim), loc))) return false; } if (params->args_count > 3 + !!offset_dim) hlsl_fixme(ctx, loc, "Tiled resource status argument."); load_params.format = object_type->e.resource.format; load_params.resource = object; load_params.sampler = params->args[0]; if (!(load = hlsl_new_resource_load(ctx, &load_params, loc))) return false; hlsl_block_add_instr(block, load); return true; } static bool add_sample_grad_method_call(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *object, const char *name, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { const struct hlsl_type *object_type = object->data_type; struct hlsl_resource_load_params load_params = { 0 }; unsigned int sampler_dim, offset_dim; const struct hlsl_type *sampler_type; struct hlsl_ir_node *load; sampler_dim = hlsl_sampler_dim_count(object_type->sampler_dim); offset_dim = hlsl_offset_dim_count(object_type->sampler_dim); load_params.type = HLSL_RESOURCE_SAMPLE_GRAD; if (params->args_count < 4 || params->args_count > 5 + !!offset_dim) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Wrong number of arguments to method '%s': expected from 4 to %u, but got %u.", name, 5 + !!offset_dim, params->args_count); return false; } sampler_type = params->args[0]->data_type; if (sampler_type->class != HLSL_CLASS_SAMPLER || sampler_type->sampler_dim != HLSL_SAMPLER_DIM_GENERIC) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, sampler_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Wrong type for argument 0 of %s(): expected 'sampler', but got '%s'.", name, string->buffer); hlsl_release_string_buffer(ctx, string); return false; } if (!(load_params.coords = add_implicit_conversion(ctx, block, params->args[1], hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc))) load_params.coords = params->args[1]; if (!(load_params.ddx = add_implicit_conversion(ctx, block, params->args[2], hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc))) load_params.ddx = params->args[2]; if (!(load_params.ddy = add_implicit_conversion(ctx, block, params->args[3], hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, sampler_dim), loc))) load_params.ddy = params->args[3]; if (offset_dim && params->args_count > 4) { if (!(load_params.texel_offset = add_implicit_conversion(ctx, block, params->args[4], hlsl_get_vector_type(ctx, HLSL_TYPE_INT, offset_dim), loc))) return false; } if (params->args_count > 4 + !!offset_dim) hlsl_fixme(ctx, loc, "Tiled resource status argument."); load_params.format = object_type->e.resource.format; load_params.resource = object; load_params.sampler = params->args[0]; if (!(load = hlsl_new_resource_load(ctx, &load_params, loc))) return false; hlsl_block_add_instr(block, load); return true; } static const struct method_function { const char *name; bool (*handler)(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *object, const char *name, const struct parse_initializer *params, const struct vkd3d_shader_location *loc); bool valid_dims[HLSL_SAMPLER_DIM_MAX + 1]; } object_methods[] = { /* g c 1d 2d 3d cube 1darr 2darr 2dms 2dmsarr cubearr buff sbuff*/ { "Gather", add_gather_method_call, {0,0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0}}, { "GatherAlpha", add_gather_method_call, {0,0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0}}, { "GatherBlue", add_gather_method_call, {0,0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0}}, { "GatherGreen", add_gather_method_call, {0,0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0}}, { "GatherRed", add_gather_method_call, {0,0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0}}, { "GetDimensions", add_getdimensions_method_call, {0,0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, { "Load", add_load_method_call, {0,0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1}}, { "Sample", add_sample_method_call, {0,0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0}}, { "SampleBias", add_sample_lod_method_call, {0,0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0}}, { "SampleCmp", add_sample_cmp_method_call, {0,0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0}}, { "SampleCmpLevelZero", add_sample_cmp_method_call, {0,0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0}}, { "SampleGrad", add_sample_grad_method_call, {0,0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0}}, { "SampleLevel", add_sample_lod_method_call, {0,0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0}}, }; static int object_method_function_name_compare(const void *a, const void *b) { const struct method_function *func = b; return strcmp(a, func->name); } static bool add_method_call(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *object, const char *name, const struct parse_initializer *params, const struct vkd3d_shader_location *loc) { const struct hlsl_type *object_type = object->data_type; const struct method_function *method; if (object_type->class != HLSL_CLASS_TEXTURE || object_type->sampler_dim == HLSL_SAMPLER_DIM_GENERIC) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, object_type))) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Type '%s' does not have methods.", string->buffer); hlsl_release_string_buffer(ctx, string); return false; } method = bsearch(name, object_methods, ARRAY_SIZE(object_methods), sizeof(*method), object_method_function_name_compare); if (method && method->valid_dims[object_type->sampler_dim]) { return method->handler(ctx, block, object, name, params, loc); } else { return raise_invalid_method_object_type(ctx, object_type, name, loc); } } static void validate_texture_format_type(struct hlsl_ctx *ctx, struct hlsl_type *format, const struct vkd3d_shader_location *loc) { if (format->class > HLSL_CLASS_VECTOR) { struct vkd3d_string_buffer *string; string = hlsl_type_to_string(ctx, format); if (string) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Texture data type %s is not scalar or vector.", string->buffer); hlsl_release_string_buffer(ctx, string); } } static bool check_continue(struct hlsl_ctx *ctx, const struct hlsl_scope *scope, const struct vkd3d_shader_location *loc) { if (scope->_switch) { hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "The 'continue' statement is not allowed in 'switch' statements."); return false; } if (scope->loop) return true; if (scope->upper) return check_continue(ctx, scope->upper, loc); hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "The 'continue' statement is only allowed in loops."); return false; } static bool is_break_allowed(const struct hlsl_scope *scope) { if (scope->loop || scope->_switch) return true; return scope->upper ? is_break_allowed(scope->upper) : false; } static void check_duplicated_switch_cases(struct hlsl_ctx *ctx, const struct hlsl_ir_switch_case *check, struct list *cases) { struct hlsl_ir_switch_case *c; bool found_duplicate = false; LIST_FOR_EACH_ENTRY(c, cases, struct hlsl_ir_switch_case, entry) { if (check->is_default) { if ((found_duplicate = c->is_default)) { hlsl_error(ctx, &check->loc, VKD3D_SHADER_ERROR_HLSL_DUPLICATE_SWITCH_CASE, "Found multiple 'default' statements."); hlsl_note(ctx, &c->loc, VKD3D_SHADER_LOG_ERROR, "The 'default' statement was previously found here."); } } else { if (c->is_default) continue; if ((found_duplicate = (c->value == check->value))) { hlsl_error(ctx, &check->loc, VKD3D_SHADER_ERROR_HLSL_DUPLICATE_SWITCH_CASE, "Found duplicate 'case' statement."); hlsl_note(ctx, &c->loc, VKD3D_SHADER_LOG_ERROR, "The same 'case %d' statement was previously found here.", c->value); } } if (found_duplicate) break; } } static void validate_uav_type(struct hlsl_ctx *ctx, enum hlsl_sampler_dim dim, struct hlsl_type *format, const struct vkd3d_shader_location* loc) { struct vkd3d_string_buffer *string = hlsl_type_to_string(ctx, format); if (!type_contains_only_numerics(format)) { if (string) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "UAV type %s is not numeric.", string->buffer); } switch (dim) { case HLSL_SAMPLER_DIM_BUFFER: case HLSL_SAMPLER_DIM_1D: case HLSL_SAMPLER_DIM_1DARRAY: case HLSL_SAMPLER_DIM_2D: case HLSL_SAMPLER_DIM_2DARRAY: case HLSL_SAMPLER_DIM_3D: if (format->class == HLSL_CLASS_ARRAY) { if (string) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "This type of UAV does not support array type."); } else if (hlsl_type_component_count(format) > 4) { if (string) hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "UAV data type %s size exceeds maximum size.", string->buffer); } break; case HLSL_SAMPLER_DIM_STRUCTURED_BUFFER: break; default: vkd3d_unreachable(); } hlsl_release_string_buffer(ctx, string); } static bool state_block_add_entry(struct hlsl_state_block *state_block, struct hlsl_state_block_entry *entry) { if (!vkd3d_array_reserve((void **)&state_block->entries, &state_block->capacity, state_block->count + 1, sizeof(*state_block->entries))) return false; state_block->entries[state_block->count++] = entry; return true; } } %locations %define parse.error verbose %define api.prefix {hlsl_yy} %define api.pure full %expect 1 %lex-param {yyscan_t scanner} %parse-param {void *scanner} %parse-param {struct hlsl_ctx *ctx} %union { struct hlsl_type *type; INT intval; FLOAT floatval; bool boolval; char *name; uint32_t modifiers; struct hlsl_ir_node *instr; struct hlsl_block *block; struct list *list; struct parse_fields fields; struct parse_function function; struct parse_parameter parameter; struct hlsl_func_parameters parameters; struct parse_initializer initializer; struct parse_array_sizes arrays; struct parse_variable_def *variable_def; struct parse_if_body if_body; enum parse_assign_op assign_op; struct hlsl_reg_reservation reg_reservation; struct parse_colon_attribute colon_attribute; struct hlsl_semantic semantic; enum hlsl_buffer_type buffer_type; enum hlsl_sampler_dim sampler_dim; struct hlsl_attribute *attr; struct parse_attribute_list attr_list; struct hlsl_ir_switch_case *switch_case; struct hlsl_scope *scope; struct hlsl_state_block *state_block; struct state_block_index state_block_index; } %token KW_BLENDSTATE %token KW_BREAK %token KW_BUFFER %token KW_CASE %token KW_CONSTANTBUFFER %token KW_CBUFFER %token KW_CENTROID %token KW_COLUMN_MAJOR %token KW_COMPILE %token KW_COMPUTESHADER %token KW_CONST %token KW_CONTINUE %token KW_DEFAULT %token KW_DEPTHSTENCILSTATE %token KW_DEPTHSTENCILVIEW %token KW_DISCARD %token KW_DO %token KW_DOMAINSHADER %token KW_DOUBLE %token KW_ELSE %token KW_EXPORT %token KW_EXTERN %token KW_FALSE %token KW_FOR %token KW_FXGROUP %token KW_GEOMETRYSHADER %token KW_GROUPSHARED %token KW_HULLSHADER %token KW_IF %token KW_IN %token KW_INLINE %token KW_INOUT %token KW_LINEAR %token KW_MATRIX %token KW_NAMESPACE %token KW_NOINTERPOLATION %token KW_NOPERSPECTIVE %token KW_OUT %token KW_PACKOFFSET %token KW_PASS %token KW_PIXELSHADER %token KW_RASTERIZERORDEREDBUFFER %token KW_RASTERIZERORDEREDSTRUCTUREDBUFFER %token KW_RASTERIZERORDEREDTEXTURE1D %token KW_RASTERIZERORDEREDTEXTURE1DARRAY %token KW_RASTERIZERORDEREDTEXTURE2D %token KW_RASTERIZERORDEREDTEXTURE2DARRAY %token KW_RASTERIZERORDEREDTEXTURE3D %token KW_RASTERIZERSTATE %token KW_RENDERTARGETVIEW %token KW_RETURN %token KW_REGISTER %token KW_ROW_MAJOR %token KW_RWBUFFER %token KW_RWSTRUCTUREDBUFFER %token KW_RWTEXTURE1D %token KW_RWTEXTURE1DARRAY %token KW_RWTEXTURE2D %token KW_RWTEXTURE2DARRAY %token KW_RWTEXTURE3D %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_TECHNIQUE11 %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_TEXTURECUBE %token KW_TEXTURECUBEARRAY %token KW_TRUE %token KW_TYPEDEF %token KW_UNSIGNED %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_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 C_FLOAT %token C_INTEGER %token C_UNSIGNED %token PRE_LINE %type type_specs %type variables_def %type variables_def_typed %type switch_cases %token VAR_IDENTIFIER %token NEW_IDENTIFIER %token STRING %token TYPE_IDENTIFIER %type annotations_opt %type arrays %type assign_op %type attribute %type attribute_list %type attribute_list_optional %type add_expr %type assignment_expr %type bitand_expr %type bitor_expr %type bitxor_expr %type compound_statement %type conditional_expr %type declaration %type declaration_statement %type equality_expr %type expr %type expr_optional %type expr_statement %type initializer_expr %type jump_statement %type logicand_expr %type logicor_expr %type loop_statement %type mul_expr %type postfix_expr %type primary_expr %type relational_expr %type shift_expr %type selection_statement %type statement %type statement_list %type struct_declaration_without_vars %type switch_statement %type unary_expr %type boolean %type buffer_type %type colon_attribute %type field %type fields_list %type func_prototype %type func_prototype_no_attrs %type complex_initializer %type complex_initializer_list %type func_arguments %type initializer_expr_list %type if_body %type array %type var_modifiers %type any_identifier %type var_identifier %type stateblock_lhs_identifier %type name_opt %type parameter %type param_list %type parameters %type register_reservation %type packoffset_reservation %type texture_type texture_ms_type uav_type rov_type %type semantic %type state_block %type state_block_index_opt %type switch_case %type field_type %type named_struct_spec %type unnamed_struct_spec %type struct_spec %type type %type type_no_void %type typedef_type %type state_block_list %type type_spec %type variable_decl %type variable_def %type variable_def_typed %% hlsl_prog: %empty | hlsl_prog func_declaration | hlsl_prog buffer_declaration buffer_body | hlsl_prog declaration_statement { hlsl_block_add_block(&ctx->static_initializers, $2); destroy_block($2); } | hlsl_prog preproc_directive | hlsl_prog global_technique | hlsl_prog effect_group | hlsl_prog ';' name_opt: %empty { $$ = NULL; } | any_identifier pass: KW_PASS name_opt annotations_opt '{' state_block_start state_block '}' { if (!add_pass(ctx, $2, $3, $6, &@1)) YYABORT; } annotations_list: variables_def_typed ';' { struct hlsl_block *block; block = initialize_vars(ctx, $1); destroy_block(block); } | annotations_list variables_def_typed ';' { struct hlsl_block *block; block = initialize_vars(ctx, $2); destroy_block(block); } annotations_opt: %empty { $$ = NULL; } | '<' annotations_scope_start '>' { hlsl_pop_scope(ctx); $$ = NULL; } | '<' annotations_scope_start annotations_list '>' { struct hlsl_scope *scope = ctx->cur_scope; hlsl_pop_scope(ctx); $$ = scope; } pass_list: pass | pass_list pass passes: scope_start | scope_start pass_list technique9: KW_TECHNIQUE name_opt annotations_opt '{' passes '}' { struct hlsl_scope *scope = ctx->cur_scope; hlsl_pop_scope(ctx); if (!add_technique(ctx, $2, scope, $3, "technique", &@1)) YYABORT; } technique10: KW_TECHNIQUE10 name_opt annotations_opt '{' passes '}' { struct hlsl_scope *scope = ctx->cur_scope; hlsl_pop_scope(ctx); if (!add_technique(ctx, $2, scope, $3, "technique10", &@1)) YYABORT; } technique11: KW_TECHNIQUE11 name_opt annotations_opt '{' passes '}' { struct hlsl_scope *scope = ctx->cur_scope; hlsl_pop_scope(ctx); if (ctx->profile->type == VKD3D_SHADER_TYPE_EFFECT && ctx->profile->major_version == 2) hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "The 'technique11' keyword is invalid for this profile."); if (!add_technique(ctx, $2, scope, $3, "technique11", &@1)) YYABORT; } global_technique: technique9 | technique10 | technique11 group_technique: technique10 | technique11 group_techniques: group_technique | group_techniques group_technique effect_group: KW_FXGROUP any_identifier annotations_opt '{' scope_start group_techniques '}' { struct hlsl_scope *scope = ctx->cur_scope; hlsl_pop_scope(ctx); if (!(add_effect_group(ctx, $2, scope, $3, &@2))) YYABORT; } buffer_declaration: var_modifiers buffer_type any_identifier colon_attribute annotations_opt { if ($4.semantic.name) hlsl_error(ctx, &@4, VKD3D_SHADER_ERROR_HLSL_INVALID_SEMANTIC, "Semantics are not allowed on buffers."); if (!(ctx->cur_buffer = hlsl_new_buffer(ctx, $2, $3, $1, &$4.reg_reservation, $5, &@3))) YYABORT; } buffer_body: '{' declaration_statement_list '}' { ctx->cur_buffer = ctx->globals_buffer; } buffer_type: KW_CBUFFER { $$ = HLSL_BUFFER_CONSTANT; } | KW_TBUFFER { $$ = HLSL_BUFFER_TEXTURE; } declaration_statement_list: %empty | declaration_statement_list declaration_statement preproc_directive: PRE_LINE STRING { const char **new_array = NULL; ctx->location.line = $1; if (strcmp($2, ctx->location.source_name)) new_array = hlsl_realloc(ctx, ctx->source_files, sizeof(*ctx->source_files) * (ctx->source_files_count + 1)); if (new_array) { ctx->source_files = new_array; ctx->source_files[ctx->source_files_count++] = $2; ctx->location.source_name = $2; } else { vkd3d_free($2); } } struct_declaration_without_vars: var_modifiers struct_spec ';' { if (!$2->name) hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Anonymous struct type must declare a variable."); if ($1) hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, "Modifiers are not allowed on struct type declarations."); if (!($$ = make_empty_block(ctx))) YYABORT; } struct_spec: named_struct_spec | unnamed_struct_spec named_struct_spec: KW_STRUCT any_identifier '{' fields_list '}' { bool ret; $$ = hlsl_new_struct_type(ctx, $2, $4.fields, $4.count); if (hlsl_get_var(ctx->cur_scope, $2)) { hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "\"%s\" is already declared as a variable.", $2); YYABORT; } ret = hlsl_scope_add_type(ctx->cur_scope, $$); if (!ret) { hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "Struct \"%s\" is already defined.", $2); YYABORT; } } unnamed_struct_spec: KW_STRUCT '{' fields_list '}' { $$ = hlsl_new_struct_type(ctx, NULL, $3.fields, $3.count); } any_identifier: VAR_IDENTIFIER | TYPE_IDENTIFIER | NEW_IDENTIFIER fields_list: %empty { $$.fields = NULL; $$.count = 0; $$.capacity = 0; } | fields_list field { size_t i; for (i = 0; i < $2.count; ++i) { const struct hlsl_struct_field *field = &$2.fields[i]; const struct hlsl_struct_field *existing; if ((existing = get_struct_field($1.fields, $1.count, field->name))) { hlsl_error(ctx, &field->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "Field \"%s\" is already defined.", field->name); hlsl_note(ctx, &existing->loc, VKD3D_SHADER_LOG_ERROR, "'%s' was previously defined here.", field->name); } } if (!hlsl_array_reserve(ctx, (void **)&$1.fields, &$1.capacity, $1.count + $2.count, sizeof(*$1.fields))) YYABORT; memcpy($1.fields + $1.count, $2.fields, $2.count * sizeof(*$2.fields)); $1.count += $2.count; vkd3d_free($2.fields); $$ = $1; } field_type: type | unnamed_struct_spec field: var_modifiers field_type variables_def ';' { struct hlsl_type *type; uint32_t modifiers = $1; if (!(type = apply_type_modifiers(ctx, $2, &modifiers, true, &@1))) YYABORT; if (modifiers & ~HLSL_INTERPOLATION_MODIFIERS_MASK) { struct vkd3d_string_buffer *string; if ((string = hlsl_modifiers_to_string(ctx, modifiers))) hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, "Modifiers '%s' are not allowed on struct fields.", string->buffer); hlsl_release_string_buffer(ctx, string); } if (!gen_struct_fields(ctx, &$$, type, modifiers, $3)) YYABORT; } attribute: '[' any_identifier ']' { if (!($$ = hlsl_alloc(ctx, offsetof(struct hlsl_attribute, args[0])))) { vkd3d_free($2); YYABORT; } $$->name = $2; hlsl_block_init(&$$->instrs); $$->loc = @$; $$->args_count = 0; } | '[' any_identifier '(' initializer_expr_list ')' ']' { unsigned int i; if (!($$ = hlsl_alloc(ctx, offsetof(struct hlsl_attribute, args[$4.args_count])))) { vkd3d_free($2); free_parse_initializer(&$4); YYABORT; } $$->name = $2; hlsl_block_init(&$$->instrs); hlsl_block_add_block(&$$->instrs, $4.instrs); $$->loc = @$; $$->args_count = $4.args_count; for (i = 0; i < $4.args_count; ++i) hlsl_src_from_node(&$$->args[i], $4.args[i]); free_parse_initializer(&$4); } attribute_list: attribute { $$.count = 1; if (!($$.attrs = hlsl_alloc(ctx, sizeof(*$$.attrs)))) { hlsl_free_attribute($1); YYABORT; } $$.attrs[0] = $1; } | attribute_list attribute { const struct hlsl_attribute **new_array; $$ = $1; if (!(new_array = vkd3d_realloc($$.attrs, ($$.count + 1) * sizeof(*$$.attrs)))) { cleanup_parse_attribute_list(&$$); YYABORT; } $$.attrs = new_array; $$.attrs[$$.count++] = $2; } attribute_list_optional: %empty { $$.count = 0; $$.attrs = NULL; } | attribute_list func_declaration: func_prototype compound_statement { struct hlsl_ir_function_decl *decl = $1.decl; if (decl->has_body) { hlsl_error(ctx, &decl->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "Function \"%s\" is already defined.", decl->func->name); hlsl_note(ctx, &decl->loc, VKD3D_SHADER_LOG_ERROR, "\"%s\" was previously defined here.", decl->func->name); destroy_block($2); } else { size_t i; decl->has_body = true; hlsl_block_add_block(&decl->body, $2); destroy_block($2); /* Semantics are taken from whichever definition has a body. * We can't just replace the hlsl_ir_var pointers, though: if * the function was already declared but not defined, the * callers would have used the old declaration's parameters to * transfer arguments. */ if (!$1.first) { assert(decl->parameters.count == $1.parameters.count); for (i = 0; i < $1.parameters.count; ++i) { struct hlsl_ir_var *dst = decl->parameters.vars[i]; struct hlsl_ir_var *src = $1.parameters.vars[i]; hlsl_cleanup_semantic(&dst->semantic); dst->semantic = src->semantic; memset(&src->semantic, 0, sizeof(src->semantic)); } if (decl->return_var) { hlsl_cleanup_semantic(&decl->return_var->semantic); decl->return_var->semantic = $1.return_semantic; memset(&$1.return_semantic, 0, sizeof($1.return_semantic)); } } } hlsl_pop_scope(ctx); if (!$1.first) { vkd3d_free($1.parameters.vars); hlsl_cleanup_semantic(&$1.return_semantic); } } | func_prototype ';' { hlsl_pop_scope(ctx); } func_prototype_no_attrs: /* var_modifiers is necessary to avoid shift/reduce conflicts. */ var_modifiers type var_identifier '(' parameters ')' colon_attribute { uint32_t modifiers = $1; struct hlsl_ir_var *var; struct hlsl_type *type; /* Functions are unconditionally inlined. */ modifiers &= ~HLSL_MODIFIER_INLINE; if (modifiers & ~(HLSL_MODIFIERS_MAJORITY_MASK | HLSL_MODIFIER_EXPORT)) hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, "Unexpected modifier used on a function."); if (!(type = apply_type_modifiers(ctx, $2, &modifiers, true, &@1))) YYABORT; if ((var = hlsl_get_var(ctx->globals, $3))) { hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "\"%s\" is already declared as a variable.", $3); hlsl_note(ctx, &var->loc, VKD3D_SHADER_LOG_ERROR, "\"%s\" was previously declared here.", $3); } if (hlsl_types_are_equal(type, ctx->builtin_types.Void) && $7.semantic.name) { hlsl_error(ctx, &@7, VKD3D_SHADER_ERROR_HLSL_INVALID_SEMANTIC, "Semantics are not allowed on void functions."); } if ($7.reg_reservation.reg_type) FIXME("Unexpected register reservation for a function.\n"); if ($7.reg_reservation.offset_type) hlsl_error(ctx, &@5, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "packoffset() is not allowed on functions."); if (($$.decl = hlsl_get_func_decl(ctx, $3, &$5))) { const struct hlsl_func_parameters *params = &$$.decl->parameters; size_t i; if (!hlsl_types_are_equal($2, $$.decl->return_type)) { hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "\"%s\" was already declared with a different return type.", $3); hlsl_note(ctx, &$$.decl->loc, VKD3D_SHADER_LOG_ERROR, "\"%s\" was previously declared here.", $3); } vkd3d_free($3); /* We implement function invocation by copying to input * parameters, emitting a HLSL_IR_CALL instruction, then copying * from output parameters. As a result, we need to use the same * parameter variables for every invocation of this function, * which means we use the parameters created by the first * declaration. If we're not the first declaration, the * parameter variables that just got created will end up being * mostly ignored—in particular, they won't be used in actual * IR. * * There is a hitch: if this is the actual definition, the * function body will look up parameter variables by name. We * must return the original parameters, and not the ones we just * created, but we're in the wrong scope, and the parameters * might not even have the same names. * * Therefore we need to shuffle the parameters we just created * into a dummy scope where they'll never be looked up, and * rename the original parameters so they have the expected * names. We actually do this for every prototype: we don't know * whether this is the function definition yet, but it doesn't * really matter. The variables can only be used in the * actual definition, and don't do anything in a declaration. * * This is complex, and it seems tempting to avoid this logic by * putting arguments into the HLSL_IR_CALL instruction, letting * the canonical variables be the ones attached to the function * definition, and resolving the copies when inlining. The * problem with this is output parameters. We would have to use * a lot of parsing logic on already lowered IR, which is * brittle and ugly. */ assert($5.count == params->count); for (i = 0; i < params->count; ++i) { struct hlsl_ir_var *orig_param = params->vars[i]; struct hlsl_ir_var *new_param = $5.vars[i]; char *new_name; list_remove(&orig_param->scope_entry); list_add_tail(&ctx->cur_scope->vars, &orig_param->scope_entry); list_remove(&new_param->scope_entry); list_add_tail(&ctx->dummy_scope->vars, &new_param->scope_entry); if (!(new_name = hlsl_strdup(ctx, new_param->name))) YYABORT; vkd3d_free((void *)orig_param->name); orig_param->name = new_name; } $$.first = false; $$.parameters = $5; $$.return_semantic = $7.semantic; } else { if (!($$.decl = hlsl_new_func_decl(ctx, type, &$5, &$7.semantic, &@3))) YYABORT; hlsl_add_function(ctx, $3, $$.decl); $$.first = true; } ctx->cur_function = $$.decl; } func_prototype: func_prototype_no_attrs | attribute_list func_prototype_no_attrs { if ($2.first) { $2.decl->attr_count = $1.count; $2.decl->attrs = $1.attrs; } else { cleanup_parse_attribute_list(&$1); } $$ = $2; } compound_statement: '{' '}' { if (!($$ = make_empty_block(ctx))) YYABORT; } | '{' scope_start statement_list '}' { hlsl_pop_scope(ctx); $$ = $3; } scope_start: %empty { hlsl_push_scope(ctx); } loop_scope_start: %empty { hlsl_push_scope(ctx); ctx->cur_scope->loop = true; } switch_scope_start: %empty { hlsl_push_scope(ctx); ctx->cur_scope->_switch = true; } annotations_scope_start: %empty { hlsl_push_scope(ctx); ctx->cur_scope->annotations = true; } var_identifier: VAR_IDENTIFIER | NEW_IDENTIFIER colon_attribute: %empty { $$.semantic = (struct hlsl_semantic){0}; $$.reg_reservation.reg_type = 0; $$.reg_reservation.offset_type = 0; } | semantic { $$.semantic = $1; $$.reg_reservation.reg_type = 0; $$.reg_reservation.offset_type = 0; } | register_reservation { $$.semantic = (struct hlsl_semantic){0}; $$.reg_reservation = $1; } | packoffset_reservation { $$.semantic = (struct hlsl_semantic){0}; $$.reg_reservation = $1; } semantic: ':' any_identifier { char *p; if (!($$.raw_name = hlsl_strdup(ctx, $2))) YYABORT; for (p = $2 + strlen($2); p > $2 && isdigit(p[-1]); --p) ; $$.name = $2; $$.index = atoi(p); $$.reported_missing = false; $$.reported_duplicated_output_next_index = 0; $$.reported_duplicated_input_incompatible_next_index = 0; *p = 0; } /* FIXME: Writemasks */ register_reservation: ':' KW_REGISTER '(' any_identifier ')' { memset(&$$, 0, sizeof($$)); if (!parse_reservation_index(ctx, $4, 0, &$$)) hlsl_error(ctx, &@4, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid register reservation '%s'.", $4); vkd3d_free($4); } | ':' KW_REGISTER '(' any_identifier '[' expr ']' ')' { memset(&$$, 0, sizeof($$)); if (!parse_reservation_index(ctx, $4, evaluate_static_expression_as_uint(ctx, $6, &@6), &$$)) { hlsl_error(ctx, &@4, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid register reservation '%s'.", $4); } vkd3d_free($4); vkd3d_free($6); } | ':' KW_REGISTER '(' any_identifier ',' any_identifier ')' { memset(&$$, 0, sizeof($$)); if (parse_reservation_index(ctx, $6, 0, &$$)) { hlsl_fixme(ctx, &@4, "Reservation shader target %s.", $4); } else if (parse_reservation_space($6, &$$.reg_space)) { if (!parse_reservation_index(ctx, $4, 0, &$$)) hlsl_error(ctx, &@4, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid register reservation '%s'.", $4); } else { hlsl_error(ctx, &@6, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid register or space reservation '%s'.", $6); } vkd3d_free($4); vkd3d_free($6); } | ':' KW_REGISTER '(' any_identifier '[' expr ']' ',' any_identifier ')' { memset(&$$, 0, sizeof($$)); if (!parse_reservation_space($9, &$$.reg_space)) hlsl_error(ctx, &@9, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid register space reservation '%s'.", $9); if (!parse_reservation_index(ctx, $4, evaluate_static_expression_as_uint(ctx, $6, &@6), &$$)) { hlsl_error(ctx, &@4, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid register reservation '%s'.", $4); } vkd3d_free($4); vkd3d_free($6); vkd3d_free($9); } | ':' KW_REGISTER '(' any_identifier ',' any_identifier '[' expr ']' ')' { hlsl_fixme(ctx, &@4, "Reservation shader target %s.", $4); memset(&$$, 0, sizeof($$)); if (!parse_reservation_index(ctx, $6, evaluate_static_expression_as_uint(ctx, $8, &@8), &$$)) { hlsl_error(ctx, &@6, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid register reservation '%s'.", $6); } vkd3d_free($4); vkd3d_free($6); vkd3d_free($8); } | ':' KW_REGISTER '(' any_identifier ',' any_identifier ',' any_identifier ')' { hlsl_fixme(ctx, &@4, "Reservation shader target %s.", $4); memset(&$$, 0, sizeof($$)); if (!parse_reservation_index(ctx, $6, 0, &$$)) hlsl_error(ctx, &@6, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid register reservation '%s'.", $6); if (!parse_reservation_space($8, &$$.reg_space)) hlsl_error(ctx, &@8, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid register space reservation '%s'.", $8); vkd3d_free($4); vkd3d_free($6); vkd3d_free($8); } | ':' KW_REGISTER '(' any_identifier ',' any_identifier '[' expr ']' ',' any_identifier ')' { hlsl_fixme(ctx, &@4, "Reservation shader target %s.", $4); memset(&$$, 0, sizeof($$)); if (!parse_reservation_index(ctx, $6, evaluate_static_expression_as_uint(ctx, $8, &@8), &$$)) { hlsl_error(ctx, &@6, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid register reservation '%s'.", $6); } if (!parse_reservation_space($11, &$$.reg_space)) hlsl_error(ctx, &@11, VKD3D_SHADER_ERROR_HLSL_INVALID_RESERVATION, "Invalid register space reservation '%s'.", $11); vkd3d_free($4); vkd3d_free($6); vkd3d_free($8); vkd3d_free($11); } packoffset_reservation: ':' KW_PACKOFFSET '(' any_identifier ')' { $$ = parse_packoffset(ctx, $4, NULL, &@$); vkd3d_free($4); } | ':' KW_PACKOFFSET '(' any_identifier '.' any_identifier ')' { $$ = parse_packoffset(ctx, $4, $6, &@$); vkd3d_free($4); vkd3d_free($6); } parameters: scope_start { memset(&$$, 0, sizeof($$)); } | scope_start KW_VOID { memset(&$$, 0, sizeof($$)); } | scope_start param_list { $$ = $2; } param_list: parameter { memset(&$$, 0, sizeof($$)); if (!add_func_parameter(ctx, &$$, &$1, &@1)) { ERR("Error adding function parameter %s.\n", $1.name); YYABORT; } } | param_list ',' parameter { $$ = $1; if (!add_func_parameter(ctx, &$$, &$3, &@3)) { hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "Parameter \"%s\" is already declared.", $3.name); YYABORT; } } parameter: var_modifiers type_no_void any_identifier arrays colon_attribute { uint32_t modifiers = $1; struct hlsl_type *type; unsigned int i; if (!(type = apply_type_modifiers(ctx, $2, &modifiers, true, &@1))) YYABORT; $$.modifiers = modifiers; if (!($$.modifiers & (HLSL_STORAGE_IN | HLSL_STORAGE_OUT))) $$.modifiers |= HLSL_STORAGE_IN; for (i = 0; i < $4.count; ++i) { if ($4.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT) { hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Implicit size arrays not allowed in function parameters."); } type = hlsl_new_array_type(ctx, type, $4.sizes[i]); } vkd3d_free($4.sizes); $$.type = type; if (hlsl_version_ge(ctx, 5, 1) && type->class == HLSL_CLASS_ARRAY && hlsl_type_is_resource(type)) hlsl_fixme(ctx, &@2, "Shader model 5.1+ resource array."); $$.name = $3; $$.semantic = $5.semantic; $$.reg_reservation = $5.reg_reservation; } texture_type: KW_BUFFER { $$ = HLSL_SAMPLER_DIM_BUFFER; } | KW_TEXTURE1D { $$ = HLSL_SAMPLER_DIM_1D; } | KW_TEXTURE2D { $$ = HLSL_SAMPLER_DIM_2D; } | KW_TEXTURE3D { $$ = HLSL_SAMPLER_DIM_3D; } | KW_TEXTURECUBE { $$ = HLSL_SAMPLER_DIM_CUBE; } | KW_TEXTURE1DARRAY { $$ = HLSL_SAMPLER_DIM_1DARRAY; } | KW_TEXTURE2DARRAY { $$ = HLSL_SAMPLER_DIM_2DARRAY; } | KW_TEXTURECUBEARRAY { $$ = HLSL_SAMPLER_DIM_CUBEARRAY; } texture_ms_type: KW_TEXTURE2DMS { $$ = HLSL_SAMPLER_DIM_2DMS; } | KW_TEXTURE2DMSARRAY { $$ = HLSL_SAMPLER_DIM_2DMSARRAY; } uav_type: KW_RWBUFFER { $$ = HLSL_SAMPLER_DIM_BUFFER; } | KW_RWSTRUCTUREDBUFFER { $$ = HLSL_SAMPLER_DIM_STRUCTURED_BUFFER; } | KW_RWTEXTURE1D { $$ = HLSL_SAMPLER_DIM_1D; } | KW_RWTEXTURE1DARRAY { $$ = HLSL_SAMPLER_DIM_1DARRAY; } | KW_RWTEXTURE2D { $$ = HLSL_SAMPLER_DIM_2D; } | KW_RWTEXTURE2DARRAY { $$ = HLSL_SAMPLER_DIM_2DARRAY; } | KW_RWTEXTURE3D { $$ = HLSL_SAMPLER_DIM_3D; } rov_type: KW_RASTERIZERORDEREDBUFFER { $$ = HLSL_SAMPLER_DIM_BUFFER; } | KW_RASTERIZERORDEREDSTRUCTUREDBUFFER { $$ = HLSL_SAMPLER_DIM_STRUCTURED_BUFFER; } | KW_RASTERIZERORDEREDTEXTURE1D { $$ = HLSL_SAMPLER_DIM_1D; } | KW_RASTERIZERORDEREDTEXTURE1DARRAY { $$ = HLSL_SAMPLER_DIM_1DARRAY; } | KW_RASTERIZERORDEREDTEXTURE2D { $$ = HLSL_SAMPLER_DIM_2D; } | KW_RASTERIZERORDEREDTEXTURE2DARRAY { $$ = HLSL_SAMPLER_DIM_2DARRAY; } | KW_RASTERIZERORDEREDTEXTURE3D { $$ = HLSL_SAMPLER_DIM_3D; } type_no_void: KW_VECTOR '<' type ',' C_INTEGER '>' { if ($3->class != HLSL_CLASS_SCALAR) { struct vkd3d_string_buffer *string; string = hlsl_type_to_string(ctx, $3); if (string) hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Vector base type %s is not scalar.", string->buffer); hlsl_release_string_buffer(ctx, string); YYABORT; } if ($5 < 1 || $5 > 4) { hlsl_error(ctx, &@5, VKD3D_SHADER_ERROR_HLSL_INVALID_SIZE, "Vector size %d is not between 1 and 4.", $5); YYABORT; } $$ = hlsl_type_clone(ctx, hlsl_get_vector_type(ctx, $3->e.numeric.type, $5), 0, 0); $$->is_minimum_precision = $3->is_minimum_precision; } | KW_VECTOR { $$ = hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, 4); } | KW_MATRIX '<' type ',' C_INTEGER ',' C_INTEGER '>' { if ($3->class != HLSL_CLASS_SCALAR) { struct vkd3d_string_buffer *string; string = hlsl_type_to_string(ctx, $3); if (string) hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Matrix base type %s is not scalar.", string->buffer); hlsl_release_string_buffer(ctx, string); YYABORT; } if ($5 < 1 || $5 > 4) { hlsl_error(ctx, &@5, VKD3D_SHADER_ERROR_HLSL_INVALID_SIZE, "Matrix row count %d is not between 1 and 4.", $5); YYABORT; } if ($7 < 1 || $7 > 4) { hlsl_error(ctx, &@7, VKD3D_SHADER_ERROR_HLSL_INVALID_SIZE, "Matrix column count %d is not between 1 and 4.", $7); YYABORT; } $$ = hlsl_type_clone(ctx, hlsl_get_matrix_type(ctx, $3->e.numeric.type, $7, $5), 0, 0); $$->is_minimum_precision = $3->is_minimum_precision; } | KW_MATRIX { $$ = hlsl_get_matrix_type(ctx, HLSL_TYPE_FLOAT, 4, 4); } | KW_SAMPLER { $$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_GENERIC]; } | KW_SAMPLERCOMPARISONSTATE { $$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_COMPARISON]; } | KW_SAMPLER1D { $$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_1D]; } | KW_SAMPLER2D { $$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_2D]; } | KW_SAMPLER3D { $$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_3D]; } | KW_SAMPLERCUBE { $$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_CUBE]; } | KW_TEXTURE { $$ = hlsl_new_texture_type(ctx, HLSL_SAMPLER_DIM_GENERIC, NULL, 0); } | texture_type { $$ = hlsl_new_texture_type(ctx, $1, hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, 4), 0); } | texture_type '<' type '>' { validate_texture_format_type(ctx, $3, &@3); $$ = hlsl_new_texture_type(ctx, $1, $3, 0); } | texture_ms_type '<' type '>' { validate_texture_format_type(ctx, $3, &@3); if (hlsl_version_lt(ctx, 4, 1)) { hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Multisampled texture object declaration needs sample count for profile %s.", ctx->profile->name); } $$ = hlsl_new_texture_type(ctx, $1, $3, 0); } | texture_ms_type '<' type ',' shift_expr '>' { unsigned int sample_count; struct hlsl_block block; hlsl_block_init(&block); hlsl_block_add_block(&block, $5); sample_count = evaluate_static_expression_as_uint(ctx, &block, &@5); hlsl_block_cleanup(&block); vkd3d_free($5); $$ = hlsl_new_texture_type(ctx, $1, $3, sample_count); } | uav_type '<' type '>' { validate_uav_type(ctx, $1, $3, &@3); $$ = hlsl_new_uav_type(ctx, $1, $3, false); } | rov_type '<' type '>' { validate_uav_type(ctx, $1, $3, &@3); $$ = hlsl_new_uav_type(ctx, $1, $3, true); } | TYPE_IDENTIFIER { $$ = hlsl_get_type(ctx->cur_scope, $1, true, true); if ($$->is_minimum_precision) { if (hlsl_version_lt(ctx, 4, 0)) { hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Target profile doesn't support minimum-precision types."); } else { FIXME("Reinterpreting type %s.\n", $$->name); } } vkd3d_free($1); } | KW_UNSIGNED TYPE_IDENTIFIER { struct hlsl_type *type = hlsl_get_type(ctx->cur_scope, $2, true, true); if (hlsl_is_numeric_type(type) && type->e.numeric.type == HLSL_TYPE_INT) { if (!(type = hlsl_type_clone(ctx, type, 0, 0))) YYABORT; vkd3d_free((void *)type->name); type->name = NULL; type->e.numeric.type = HLSL_TYPE_UINT; } else { hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "The 'unsigned' keyword can't be used with type %s.", $2); } $$ = type; } | KW_STRUCT TYPE_IDENTIFIER { $$ = hlsl_get_type(ctx->cur_scope, $2, true, true); if ($$->class != HLSL_CLASS_STRUCT) hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_REDEFINED, "\"%s\" redefined as a structure.", $2); vkd3d_free($2); } | KW_RENDERTARGETVIEW { $$ = hlsl_get_type(ctx->cur_scope, "RenderTargetView", true, true); } | KW_DEPTHSTENCILSTATE { $$ = hlsl_get_type(ctx->cur_scope, "DepthStencilState", true, true); } | KW_DEPTHSTENCILVIEW { $$ = hlsl_get_type(ctx->cur_scope, "DepthStencilView", true, true); } | KW_VERTEXSHADER { $$ = hlsl_get_type(ctx->cur_scope, "VertexShader", true, true); } | KW_PIXELSHADER { $$ = hlsl_get_type(ctx->cur_scope, "PixelShader", true, true); } | KW_CONSTANTBUFFER '<' type '>' { if ($3->class != HLSL_CLASS_STRUCT) hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "ConstantBuffer<...> requires user-defined structure type."); $$ = hlsl_new_cb_type(ctx, $3); } | KW_RASTERIZERSTATE { $$ = hlsl_get_type(ctx->cur_scope, "RasterizerState", true, true); } type: type_no_void | KW_VOID { $$ = ctx->builtin_types.Void; } declaration_statement: declaration | struct_declaration_without_vars | typedef { if (!($$ = make_empty_block(ctx))) YYABORT; } typedef_type: type | struct_spec typedef: KW_TYPEDEF var_modifiers typedef_type type_specs ';' { struct parse_variable_def *v, *v_next; uint32_t modifiers = $2; struct hlsl_type *type; if (!(type = apply_type_modifiers(ctx, $3, &modifiers, false, &@2))) { LIST_FOR_EACH_ENTRY_SAFE(v, v_next, $4, struct parse_variable_def, entry) free_parse_variable_def(v); vkd3d_free($4); YYABORT; } if (modifiers) { hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, "Storage modifiers are not allowed on typedefs."); LIST_FOR_EACH_ENTRY_SAFE(v, v_next, $4, struct parse_variable_def, entry) vkd3d_free(v); vkd3d_free($4); YYABORT; } if (!add_typedef(ctx, type, $4)) YYABORT; } type_specs: type_spec { if (!($$ = make_empty_list(ctx))) YYABORT; list_add_head($$, &$1->entry); } | type_specs ',' type_spec { $$ = $1; list_add_tail($$, &$3->entry); } type_spec: any_identifier arrays { $$ = hlsl_alloc(ctx, sizeof(*$$)); $$->loc = @1; $$->name = $1; $$->arrays = $2; } declaration: variables_def_typed ';' { if (!($$ = initialize_vars(ctx, $1))) YYABORT; } variables_def: variable_def { if (!($$ = make_empty_list(ctx))) YYABORT; list_add_head($$, &$1->entry); } | variables_def ',' variable_def { $$ = $1; list_add_tail($$, &$3->entry); } variables_def_typed: variable_def_typed { if (!($$ = make_empty_list(ctx))) YYABORT; list_add_head($$, &$1->entry); declare_var(ctx, $1); } | variables_def_typed ',' variable_def { struct parse_variable_def *head_def; assert(!list_empty($1)); head_def = LIST_ENTRY(list_head($1), struct parse_variable_def, entry); assert(head_def->basic_type); $3->basic_type = head_def->basic_type; $3->modifiers = head_def->modifiers; $3->modifiers_loc = head_def->modifiers_loc; declare_var(ctx, $3); $$ = $1; list_add_tail($$, &$3->entry); } variable_decl: any_identifier arrays colon_attribute { $$ = hlsl_alloc(ctx, sizeof(*$$)); $$->loc = @1; $$->name = $1; $$->arrays = $2; $$->semantic = $3.semantic; $$->reg_reservation = $3.reg_reservation; } state_block_start: %empty { ctx->in_state_block = 1; } stateblock_lhs_identifier: any_identifier { $$ = $1; } | KW_PIXELSHADER { if (!($$ = hlsl_strdup(ctx, "pixelshader"))) YYABORT; } | KW_VERTEXSHADER { if (!($$ = hlsl_strdup(ctx, "vertexshader"))) YYABORT; } state_block_index_opt: %empty { $$.has_index = false; $$.index = 0; } | '[' C_INTEGER ']' { if ($2 < 0) { hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_INVALID_INDEX, "State block array index is not a positive integer constant."); YYABORT; } $$.has_index = true; $$.index = $2; } state_block: %empty { if (!($$ = hlsl_alloc(ctx, sizeof(*$$)))) YYABORT; } | state_block stateblock_lhs_identifier state_block_index_opt '=' complex_initializer ';' { struct hlsl_state_block_entry *entry; unsigned int i; if (!(entry = hlsl_alloc(ctx, sizeof(*entry)))) YYABORT; entry->name = $2; entry->lhs_has_index = $3.has_index; entry->lhs_index = $3.index; entry->instrs = $5.instrs; entry->args_count = $5.args_count; if (!(entry->args = hlsl_alloc(ctx, sizeof(*entry->args) * entry->args_count))) YYABORT; for (i = 0; i < entry->args_count; ++i) hlsl_src_from_node(&entry->args[i], $5.args[i]); vkd3d_free($5.args); $$ = $1; state_block_add_entry($$, entry); } state_block_list: '{' state_block '}' { if (!($$ = hlsl_alloc(ctx, sizeof(*$$)))) YYABORT; if(!(vkd3d_array_reserve((void **)&$$->state_blocks, &$$->state_block_capacity, $$->state_block_count + 1, sizeof(*$$->state_blocks)))) YYABORT; $$->state_blocks[$$->state_block_count++] = $2; } | state_block_list ',' '{' state_block '}' { $$ = $1; if(!(vkd3d_array_reserve((void **)&$$->state_blocks, &$$->state_block_capacity, $$->state_block_count + 1, sizeof(*$$->state_blocks)))) YYABORT; $$->state_blocks[$$->state_block_count++] = $4; } variable_def: variable_decl | variable_decl '=' complex_initializer { $$ = $1; $$->initializer = $3; } | variable_decl '{' state_block_start state_block '}' { $$ = $1; ctx->in_state_block = 0; if(!(vkd3d_array_reserve((void **)&$$->state_blocks, &$$->state_block_capacity, $$->state_block_count + 1, sizeof(*$$->state_blocks)))) YYABORT; $$->state_blocks[$$->state_block_count++] = $4; } | variable_decl '{' state_block_start state_block_list '}' { $$ = $1; ctx->in_state_block = 0; $$->state_blocks = $4->state_blocks; $$->state_block_count = $4->state_block_count; $$->state_block_capacity = $4->state_block_capacity; $4->state_blocks = NULL; $4->state_block_count = 0; $4->state_block_capacity = 0; free_parse_variable_def($4); } variable_def_typed: var_modifiers struct_spec variable_def { uint32_t modifiers = $1; struct hlsl_type *type; if (!(type = apply_type_modifiers(ctx, $2, &modifiers, true, &@1))) YYABORT; check_invalid_in_out_modifiers(ctx, modifiers, &@1); $$ = $3; $$->basic_type = type; $$->modifiers = modifiers; $$->modifiers_loc = @1; } | var_modifiers type variable_def { uint32_t modifiers = $1; struct hlsl_type *type; if (!(type = apply_type_modifiers(ctx, $2, &modifiers, true, &@1))) YYABORT; check_invalid_in_out_modifiers(ctx, modifiers, &@1); $$ = $3; $$->basic_type = type; $$->modifiers = modifiers; $$->modifiers_loc = @1; } array: '[' ']' { $$ = HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT; } | '[' expr ']' { $$ = evaluate_static_expression_as_uint(ctx, $2, &@2); if (!$$) { hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_INVALID_SIZE, "Array size is not a positive integer constant."); YYABORT; } if ($$ > 65536) { hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_INVALID_SIZE, "Array size %u is not between 1 and 65536.", $$); YYABORT; } destroy_block($2); } arrays: %empty { $$.sizes = NULL; $$.count = 0; } | array arrays { uint32_t *new_array; $$ = $2; if (!(new_array = hlsl_realloc(ctx, $$.sizes, ($$.count + 1) * sizeof(*new_array)))) { vkd3d_free($$.sizes); YYABORT; } $$.sizes = new_array; $$.sizes[$$.count++] = $1; } var_modifiers: %empty { $$ = 0; } | KW_EXTERN var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_STORAGE_EXTERN, &@1); } | KW_NOINTERPOLATION var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_STORAGE_NOINTERPOLATION, &@1); } | KW_CENTROID var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_STORAGE_CENTROID, &@1); } | KW_LINEAR var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_STORAGE_LINEAR, &@1); } | KW_NOPERSPECTIVE var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_STORAGE_NOPERSPECTIVE, &@1); } | KW_SHARED var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_STORAGE_SHARED, &@1); } | KW_GROUPSHARED var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_STORAGE_GROUPSHARED, &@1); } | KW_STATIC var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_STORAGE_STATIC, &@1); } | KW_UNIFORM var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_STORAGE_UNIFORM, &@1); } | KW_VOLATILE var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_MODIFIER_VOLATILE, &@1); } | KW_CONST var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_MODIFIER_CONST, &@1); } | KW_ROW_MAJOR var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_MODIFIER_ROW_MAJOR, &@1); } | KW_COLUMN_MAJOR var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_MODIFIER_COLUMN_MAJOR, &@1); } | KW_IN var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_STORAGE_IN, &@1); } | KW_OUT var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_STORAGE_OUT, &@1); } | KW_INOUT var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_STORAGE_IN | HLSL_STORAGE_OUT, &@1); } | KW_INLINE var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_MODIFIER_INLINE, &@1); } | KW_EXPORT var_modifiers { $$ = add_modifiers(ctx, $2, HLSL_MODIFIER_EXPORT, &@1); } | var_identifier var_modifiers { $$ = $2; if (!strcmp($1, "precise")) $$ = add_modifiers(ctx, $2, HLSL_MODIFIER_PRECISE, &@1); else if (!strcmp($1, "single")) $$ = add_modifiers(ctx, $2, HLSL_MODIFIER_SINGLE, &@1); else hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_UNKNOWN_MODIFIER, "Unknown modifier %s.", debugstr_a($1)); } complex_initializer: initializer_expr { $$.args_count = 1; if (!($$.args = hlsl_alloc(ctx, sizeof(*$$.args)))) { destroy_block($1); YYABORT; } $$.args[0] = node_from_block($1); $$.instrs = $1; $$.braces = false; } | '{' complex_initializer_list '}' { $$ = $2; $$.braces = true; } | '{' complex_initializer_list ',' '}' { $$ = $2; $$.braces = true; } complex_initializer_list: complex_initializer | complex_initializer_list ',' complex_initializer { struct hlsl_ir_node **new_args; unsigned int i; $$ = $1; if (!(new_args = hlsl_realloc(ctx, $$.args, ($$.args_count + $3.args_count) * sizeof(*$$.args)))) { free_parse_initializer(&$$); free_parse_initializer(&$3); YYABORT; } $$.args = new_args; for (i = 0; i < $3.args_count; ++i) $$.args[$$.args_count++] = $3.args[i]; hlsl_block_add_block($$.instrs, $3.instrs); free_parse_initializer(&$3); } initializer_expr: assignment_expr initializer_expr_list: initializer_expr { $$.args_count = 1; if (!($$.args = hlsl_alloc(ctx, sizeof(*$$.args)))) { destroy_block($1); YYABORT; } $$.args[0] = node_from_block($1); $$.instrs = $1; $$.braces = false; } | initializer_expr_list ',' initializer_expr { struct hlsl_ir_node **new_args; $$ = $1; if (!(new_args = hlsl_realloc(ctx, $$.args, ($$.args_count + 1) * sizeof(*$$.args)))) { free_parse_initializer(&$$); destroy_block($3); YYABORT; } $$.args = new_args; $$.args[$$.args_count++] = node_from_block($3); hlsl_block_add_block($$.instrs, $3); destroy_block($3); } boolean: KW_TRUE { $$ = true; } | KW_FALSE { $$ = false; } statement_list: statement | statement_list statement { $$ = $1; hlsl_block_add_block($$, $2); destroy_block($2); } statement: declaration_statement | expr_statement | compound_statement | jump_statement | selection_statement | loop_statement | switch_statement jump_statement: KW_BREAK ';' { struct hlsl_ir_node *jump; if (!is_break_allowed(ctx->cur_scope)) { hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "The 'break' statement must be used inside of a loop or a switch."); } if (!($$ = make_empty_block(ctx))) YYABORT; if (!(jump = hlsl_new_jump(ctx, HLSL_IR_JUMP_BREAK, NULL, &@1))) YYABORT; hlsl_block_add_instr($$, jump); } | KW_CONTINUE ';' { struct hlsl_ir_node *jump; check_continue(ctx, ctx->cur_scope, &@1); if (!($$ = make_empty_block(ctx))) YYABORT; if (!(jump = hlsl_new_jump(ctx, HLSL_IR_JUMP_UNRESOLVED_CONTINUE, NULL, &@1))) YYABORT; hlsl_block_add_instr($$, jump); } | KW_RETURN expr ';' { $$ = $2; if (!add_return(ctx, $$, node_from_block($$), &@1)) YYABORT; } | KW_RETURN ';' { if (!($$ = make_empty_block(ctx))) YYABORT; if (!add_return(ctx, $$, NULL, &@1)) YYABORT; } | KW_DISCARD ';' { struct hlsl_ir_node *discard, *c; if (!($$ = make_empty_block(ctx))) YYABORT; if (!(c = hlsl_new_uint_constant(ctx, ~0u, &@1))) return false; hlsl_block_add_instr($$, c); if (!(discard = hlsl_new_jump(ctx, HLSL_IR_JUMP_DISCARD_NZ, c, &@1))) return false; hlsl_block_add_instr($$, discard); } selection_statement: attribute_list_optional KW_IF '(' expr ')' if_body { struct hlsl_ir_node *condition = node_from_block($4); const struct parse_attribute_list *attributes = &$1; struct hlsl_ir_node *instr; unsigned int i; if (attribute_list_has_duplicates(attributes)) hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Found duplicate attribute."); for (i = 0; i < attributes->count; ++i) { const struct hlsl_attribute *attr = attributes->attrs[i]; if (!strcmp(attr->name, "branch") || !strcmp(attr->name, "flatten")) { hlsl_warning(ctx, &@1, VKD3D_SHADER_WARNING_HLSL_IGNORED_ATTRIBUTE, "Unhandled attribute '%s'.", attr->name); } else { hlsl_warning(ctx, &@1, VKD3D_SHADER_WARNING_HLSL_UNKNOWN_ATTRIBUTE, "Unrecognized attribute '%s'.", attr->name); } } check_condition_type(ctx, condition); if (!(condition = add_cast(ctx, $4, condition, hlsl_get_scalar_type(ctx, HLSL_TYPE_BOOL), &@4))) { destroy_block($6.then_block); destroy_block($6.else_block); cleanup_parse_attribute_list(&$1); YYABORT; } if (!(instr = hlsl_new_if(ctx, condition, $6.then_block, $6.else_block, &@2))) { destroy_block($6.then_block); destroy_block($6.else_block); cleanup_parse_attribute_list(&$1); YYABORT; } destroy_block($6.then_block); destroy_block($6.else_block); cleanup_parse_attribute_list(&$1); $$ = $4; hlsl_block_add_instr($$, instr); } if_body: statement { $$.then_block = $1; $$.else_block = NULL; } | statement KW_ELSE statement { $$.then_block = $1; $$.else_block = $3; } loop_statement: attribute_list_optional loop_scope_start KW_WHILE '(' expr ')' statement { $$ = create_loop(ctx, LOOP_WHILE, &$1, NULL, $5, NULL, $7, &@3); hlsl_pop_scope(ctx); cleanup_parse_attribute_list(&$1); } | attribute_list_optional loop_scope_start KW_DO statement KW_WHILE '(' expr ')' ';' { $$ = create_loop(ctx, LOOP_DO_WHILE, &$1, NULL, $7, NULL, $4, &@3); hlsl_pop_scope(ctx); cleanup_parse_attribute_list(&$1); } | attribute_list_optional loop_scope_start KW_FOR '(' expr_statement expr_statement expr_optional ')' statement { $$ = create_loop(ctx, LOOP_FOR, &$1, $5, $6, $7, $9, &@3); hlsl_pop_scope(ctx); cleanup_parse_attribute_list(&$1); } | attribute_list_optional loop_scope_start KW_FOR '(' declaration expr_statement expr_optional ')' statement { $$ = create_loop(ctx, LOOP_FOR, &$1, $5, $6, $7, $9, &@3); hlsl_pop_scope(ctx); cleanup_parse_attribute_list(&$1); } switch_statement: attribute_list_optional switch_scope_start KW_SWITCH '(' expr ')' '{' switch_cases '}' { struct hlsl_ir_node *selector = node_from_block($5); struct hlsl_ir_node *s; if (!(selector = add_implicit_conversion(ctx, $5, selector, hlsl_get_scalar_type(ctx, HLSL_TYPE_UINT), &@5))) { destroy_switch_cases($8); destroy_block($5); cleanup_parse_attribute_list(&$1); YYABORT; } s = hlsl_new_switch(ctx, selector, $8, &@3); destroy_switch_cases($8); if (!s) { destroy_block($5); cleanup_parse_attribute_list(&$1); YYABORT; } $$ = $5; hlsl_block_add_instr($$, s); hlsl_pop_scope(ctx); cleanup_parse_attribute_list(&$1); } switch_case: KW_CASE expr ':' statement_list { struct hlsl_ir_switch_case *c; unsigned int value; value = evaluate_static_expression_as_uint(ctx, $2, &@2); c = hlsl_new_switch_case(ctx, value, false, $4, &@2); destroy_block($2); destroy_block($4); if (!c) YYABORT; $$ = c; } | KW_CASE expr ':' { struct hlsl_ir_switch_case *c; unsigned int value; value = evaluate_static_expression_as_uint(ctx, $2, &@2); c = hlsl_new_switch_case(ctx, value, false, NULL, &@2); destroy_block($2); if (!c) YYABORT; $$ = c; } | KW_DEFAULT ':' statement_list { struct hlsl_ir_switch_case *c; c = hlsl_new_switch_case(ctx, 0, true, $3, &@1); destroy_block($3); if (!c) YYABORT; $$ = c; } | KW_DEFAULT ':' { struct hlsl_ir_switch_case *c; if (!(c = hlsl_new_switch_case(ctx, 0, true, NULL, &@1))) YYABORT; $$ = c; } switch_cases: switch_case { struct hlsl_ir_switch_case *c = LIST_ENTRY($1, struct hlsl_ir_switch_case, entry); if (!($$ = make_empty_list(ctx))) { hlsl_free_ir_switch_case(c); YYABORT; } list_add_head($$, &$1->entry); } | switch_cases switch_case { $$ = $1; check_duplicated_switch_cases(ctx, $2, $$); list_add_tail($$, &$2->entry); } expr_optional: %empty { if (!($$ = make_empty_block(ctx))) YYABORT; } | expr expr_statement: expr_optional ';' { $$ = $1; } func_arguments: %empty { $$.args = NULL; $$.args_count = 0; if (!($$.instrs = make_empty_block(ctx))) YYABORT; $$.braces = false; } | initializer_expr_list primary_expr: C_FLOAT { struct hlsl_ir_node *c; if (!(c = hlsl_new_float_constant(ctx, $1, &@1))) YYABORT; if (!($$ = make_block(ctx, c))) YYABORT; } | C_INTEGER { struct hlsl_ir_node *c; if (!(c = hlsl_new_int_constant(ctx, $1, &@1))) YYABORT; if (!($$ = make_block(ctx, c))) YYABORT; } | C_UNSIGNED { struct hlsl_ir_node *c; if (!(c = hlsl_new_uint_constant(ctx, $1, &@1))) YYABORT; if (!($$ = make_block(ctx, c))) YYABORT; } | boolean { struct hlsl_ir_node *c; if (!(c = hlsl_new_bool_constant(ctx, $1, &@1))) YYABORT; if (!($$ = make_block(ctx, c))) { hlsl_free_instr(c); YYABORT; } } | VAR_IDENTIFIER { struct hlsl_ir_load *load; struct hlsl_ir_var *var; if (!(var = hlsl_get_var(ctx->cur_scope, $1))) { hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED, "Variable \"%s\" is not defined.", $1); vkd3d_free($1); YYABORT; } vkd3d_free($1); if (!(load = hlsl_new_var_load(ctx, var, &@1))) YYABORT; if (!($$ = make_block(ctx, &load->node))) YYABORT; } | '(' expr ')' { $$ = $2; } | var_identifier '(' func_arguments ')' { if (!($$ = add_call(ctx, $1, &$3, &@1))) { vkd3d_free($1); YYABORT; } vkd3d_free($1); } | NEW_IDENTIFIER { if (ctx->in_state_block) { struct hlsl_ir_node *constant; if (!(constant = hlsl_new_stateblock_constant(ctx, $1, &@1))) YYABORT; vkd3d_free($1); if (!($$ = make_block(ctx, constant))) YYABORT; } else { hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED, "Identifier \"%s\" is not declared.", $1); YYABORT; } } postfix_expr: primary_expr | postfix_expr OP_INC { if (!add_increment(ctx, $1, false, true, &@2)) { destroy_block($1); YYABORT; } $$ = $1; } | postfix_expr OP_DEC { if (!add_increment(ctx, $1, true, true, &@2)) { destroy_block($1); YYABORT; } $$ = $1; } | postfix_expr '.' any_identifier { struct hlsl_ir_node *node = node_from_block($1); if (node->data_type->class == HLSL_CLASS_STRUCT) { struct hlsl_type *type = node->data_type; const struct hlsl_struct_field *field; unsigned int field_idx = 0; if (!(field = get_struct_field(type->e.record.fields, type->e.record.field_count, $3))) { hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED, "Field \"%s\" is not defined.", $3); vkd3d_free($3); YYABORT; } field_idx = field - type->e.record.fields; if (!add_record_access(ctx, $1, node, field_idx, &@2)) { vkd3d_free($3); YYABORT; } vkd3d_free($3); $$ = $1; } else if (hlsl_is_numeric_type(node->data_type)) { struct hlsl_ir_node *swizzle; if (!(swizzle = get_swizzle(ctx, node, $3, &@3))) { hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Invalid swizzle \"%s\".", $3); vkd3d_free($3); YYABORT; } hlsl_block_add_instr($1, swizzle); vkd3d_free($3); $$ = $1; } else { hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Invalid subscript \"%s\".", $3); vkd3d_free($3); YYABORT; } } | postfix_expr '[' expr ']' { struct hlsl_ir_node *array = node_from_block($1), *index = node_from_block($3); hlsl_block_add_block($3, $1); destroy_block($1); if (!add_array_access(ctx, $3, array, index, &@2)) { destroy_block($3); YYABORT; } $$ = $3; } /* var_modifiers is necessary to avoid shift/reduce conflicts. */ | var_modifiers type '(' initializer_expr_list ')' { if ($1) { hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, "Modifiers are not allowed on constructors."); free_parse_initializer(&$4); YYABORT; } if (!hlsl_is_numeric_type($2)) { struct vkd3d_string_buffer *string; if ((string = hlsl_type_to_string(ctx, $2))) hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Constructor data type %s is not numeric.", string->buffer); hlsl_release_string_buffer(ctx, string); free_parse_initializer(&$4); YYABORT; } if ($2->dimx * $2->dimy != initializer_size(&$4)) { hlsl_error(ctx, &@4, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT, "Expected %u components in constructor, but got %u.", $2->dimx * $2->dimy, initializer_size(&$4)); free_parse_initializer(&$4); YYABORT; } if (!($$ = add_constructor(ctx, $2, &$4, &@2))) { free_parse_initializer(&$4); YYABORT; } } | postfix_expr '.' any_identifier '(' func_arguments ')' { struct hlsl_ir_node *object = node_from_block($1); hlsl_block_add_block($1, $5.instrs); vkd3d_free($5.instrs); if (!add_method_call(ctx, $1, object, $3, &$5, &@3)) { destroy_block($1); vkd3d_free($5.args); YYABORT; } vkd3d_free($5.args); $$ = $1; } unary_expr: postfix_expr | OP_INC unary_expr { if (!add_increment(ctx, $2, false, false, &@1)) { destroy_block($2); YYABORT; } $$ = $2; } | OP_DEC unary_expr { if (!add_increment(ctx, $2, true, false, &@1)) { destroy_block($2); YYABORT; } $$ = $2; } | '+' unary_expr { $$ = $2; } | '-' unary_expr { add_unary_arithmetic_expr(ctx, $2, HLSL_OP1_NEG, node_from_block($2), &@1); $$ = $2; } | '~' unary_expr { add_unary_bitwise_expr(ctx, $2, HLSL_OP1_BIT_NOT, node_from_block($2), &@1); $$ = $2; } | '!' unary_expr { add_unary_logical_expr(ctx, $2, HLSL_OP1_LOGIC_NOT, node_from_block($2), &@1); $$ = $2; } /* var_modifiers is necessary to avoid shift/reduce conflicts. */ | '(' var_modifiers type arrays ')' unary_expr { struct hlsl_type *src_type = node_from_block($6)->data_type; struct hlsl_type *dst_type; unsigned int i; if ($2) { hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER, "Modifiers are not allowed on casts."); YYABORT; } dst_type = $3; for (i = 0; i < $4.count; ++i) { if ($4.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT) { hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Implicit size arrays not allowed in casts."); } dst_type = hlsl_new_array_type(ctx, dst_type, $4.sizes[i]); } if (!explicit_compatible_data_types(ctx, src_type, dst_type)) { struct vkd3d_string_buffer *src_string, *dst_string; src_string = hlsl_type_to_string(ctx, src_type); dst_string = hlsl_type_to_string(ctx, dst_type); if (src_string && dst_string) hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE, "Can't cast from %s to %s.", src_string->buffer, dst_string->buffer); hlsl_release_string_buffer(ctx, src_string); hlsl_release_string_buffer(ctx, dst_string); YYABORT; } if (!add_cast(ctx, $6, node_from_block($6), dst_type, &@3)) { destroy_block($6); YYABORT; } $$ = $6; } mul_expr: unary_expr | mul_expr '*' unary_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_MUL, &@2); } | mul_expr '/' unary_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_DIV, &@2); } | mul_expr '%' unary_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_MOD, &@2); } add_expr: mul_expr | add_expr '+' mul_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_ADD, &@2); } | add_expr '-' mul_expr { struct hlsl_ir_node *neg; if (!(neg = add_unary_arithmetic_expr(ctx, $3, HLSL_OP1_NEG, node_from_block($3), &@2))) YYABORT; $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_ADD, &@2); } shift_expr: add_expr | shift_expr OP_LEFTSHIFT add_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_LSHIFT, &@2); } | shift_expr OP_RIGHTSHIFT add_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_RSHIFT, &@2); } relational_expr: shift_expr | relational_expr '<' shift_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_LESS, &@2); } | relational_expr '>' shift_expr { $$ = add_binary_expr_merge(ctx, $3, $1, HLSL_OP2_LESS, &@2); } | relational_expr OP_LE shift_expr { $$ = add_binary_expr_merge(ctx, $3, $1, HLSL_OP2_GEQUAL, &@2); } | relational_expr OP_GE shift_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_GEQUAL, &@2); } equality_expr: relational_expr | equality_expr OP_EQ relational_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_EQUAL, &@2); } | equality_expr OP_NE relational_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_NEQUAL, &@2); } bitand_expr: equality_expr | bitand_expr '&' equality_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_BIT_AND, &@2); } bitxor_expr: bitand_expr | bitxor_expr '^' bitand_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_BIT_XOR, &@2); } bitor_expr: bitxor_expr | bitor_expr '|' bitxor_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_BIT_OR, &@2); } logicand_expr: bitor_expr | logicand_expr OP_AND bitor_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_LOGIC_AND, &@2); } logicor_expr: logicand_expr | logicor_expr OP_OR logicand_expr { $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_LOGIC_OR, &@2); } conditional_expr: logicor_expr | logicor_expr '?' expr ':' assignment_expr { struct hlsl_ir_node *cond = node_from_block($1); struct hlsl_ir_node *first = node_from_block($3); struct hlsl_ir_node *second = node_from_block($5); hlsl_block_add_block($1, $3); hlsl_block_add_block($1, $5); destroy_block($3); destroy_block($5); if (!add_ternary(ctx, $1, cond, first, second)) YYABORT; $$ = $1; } assignment_expr: conditional_expr | unary_expr assign_op assignment_expr { struct hlsl_ir_node *lhs = node_from_block($1), *rhs = node_from_block($3); if (lhs->data_type->modifiers & HLSL_MODIFIER_CONST) { hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_MODIFIES_CONST, "Statement modifies a const expression."); YYABORT; } hlsl_block_add_block($3, $1); destroy_block($1); if (!add_assignment(ctx, $3, lhs, $2, rhs)) YYABORT; $$ = $3; } assign_op: '=' { $$ = ASSIGN_OP_ASSIGN; } | OP_ADDASSIGN { $$ = ASSIGN_OP_ADD; } | OP_SUBASSIGN { $$ = ASSIGN_OP_SUB; } | OP_MULASSIGN { $$ = ASSIGN_OP_MUL; } | OP_DIVASSIGN { $$ = ASSIGN_OP_DIV; } | OP_MODASSIGN { $$ = ASSIGN_OP_MOD; } | OP_LEFTSHIFTASSIGN { $$ = ASSIGN_OP_LSHIFT; } | OP_RIGHTSHIFTASSIGN { $$ = ASSIGN_OP_RSHIFT; } | OP_ANDASSIGN { $$ = ASSIGN_OP_AND; } | OP_ORASSIGN { $$ = ASSIGN_OP_OR; } | OP_XORASSIGN { $$ = ASSIGN_OP_XOR; } expr: assignment_expr | expr ',' assignment_expr { $$ = $1; hlsl_block_add_block($$, $3); destroy_block($3); }