vkd3d/libs/vkd3d-shader/hlsl.y
Zebediah Figura 8ed7437708 vkd3d-shader/hlsl: Evaluate index before array.
Co-authored-by: Francisco Casas <fcasas@codeweavers.com>
2023-04-13 23:05:43 +02:00

5822 lines
188 KiB
Plaintext

/*
* HLSL parser
*
* Copyright 2008 Stefan Dösinger
* Copyright 2012 Matteo Bruni for CodeWeavers
* Copyright 2019-2020 Zebediah Figura for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
%code requires
{
#include "hlsl.h"
#include <stdio.h>
#define HLSL_YYLTYPE struct vkd3d_shader_location
struct parse_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;
unsigned int modifiers;
};
struct parse_colon_attribute
{
struct hlsl_semantic semantic;
struct hlsl_reg_reservation reg_reservation;
};
struct parse_initializer
{
struct hlsl_ir_node **args;
unsigned int args_count;
struct list *instrs;
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 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 list *then_instrs;
struct list *else_instrs;
};
enum parse_assign_op
{
ASSIGN_OP_ASSIGN,
ASSIGN_OP_ADD,
ASSIGN_OP_SUB,
ASSIGN_OP_MUL,
ASSIGN_OP_DIV,
ASSIGN_OP_MOD,
ASSIGN_OP_LSHIFT,
ASSIGN_OP_RSHIFT,
ASSIGN_OP_AND,
ASSIGN_OP_OR,
ASSIGN_OP_XOR,
};
struct parse_attribute_list
{
unsigned int count;
const struct hlsl_attribute **attrs;
};
}
%code provides
{
int yylex(HLSL_YYSTYPE *yylval_param, HLSL_YYLTYPE *yylloc_param, void *yyscanner);
}
%code
{
#define YYLLOC_DEFAULT(cur, rhs, n) (cur) = YYRHSLOC(rhs, !!n)
static void yyerror(YYLTYPE *loc, void *scanner, struct hlsl_ctx *ctx, const char *s)
{
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "%s", s);
}
static struct hlsl_ir_node *node_from_list(struct list *list)
{
return LIST_ENTRY(list_tail(list), struct hlsl_ir_node, entry);
}
static struct list *make_empty_list(struct hlsl_ctx *ctx)
{
struct list *list;
if ((list = hlsl_alloc(ctx, sizeof(*list))))
list_init(list);
return list;
}
static void destroy_instr_list(struct list *list)
{
hlsl_free_instr_list(list);
vkd3d_free(list);
}
static 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(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 type->class <= HLSL_CLASS_LAST_NUMERIC;
}
static bool explicit_compatible_data_types(struct hlsl_ctx *ctx, struct hlsl_type *src, struct hlsl_type *dst)
{
if (src->class <= HLSL_CLASS_LAST_NUMERIC && 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 ((src->class <= HLSL_CLASS_LAST_NUMERIC) != (dst->class <= HLSL_CLASS_LAST_NUMERIC))
return false;
if (src->class <= HLSL_CLASS_LAST_NUMERIC)
{
/* 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 struct hlsl_ir_load *add_load_component(struct hlsl_ctx *ctx, struct list *instrs, struct hlsl_ir_node *var_instr,
unsigned int comp, const struct vkd3d_shader_location *loc);
static struct hlsl_ir_node *add_cast(struct hlsl_ctx *ctx, struct list *instrs,
struct hlsl_ir_node *node, struct hlsl_type *dst_type, const struct vkd3d_shader_location *loc)
{
struct hlsl_type *src_type = node->data_type;
struct hlsl_ir_expr *cast;
if (hlsl_types_are_equal(src_type, dst_type))
return node;
if (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 = src_type->class <= HLSL_CLASS_LAST_NUMERIC && 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_type *dst_comp_type;
struct hlsl_ir_store *store;
struct hlsl_block 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 (!(load = add_load_component(ctx, instrs, node, src_idx, loc)))
return NULL;
if (!(cast = hlsl_new_cast(ctx, &load->node, dst_comp_type, loc)))
return NULL;
list_add_tail(instrs, &cast->node.entry);
if (!(store = hlsl_new_store_component(ctx, &block, &var_deref, dst_idx, &cast->node)))
return NULL;
list_move_tail(instrs, &block.instrs);
}
if (!(load = hlsl_new_var_load(ctx, var, *loc)))
return NULL;
list_add_tail(instrs, &load->node.entry);
return &load->node;
}
else
{
if (!(cast = hlsl_new_cast(ctx, node, dst_type, loc)))
return NULL;
list_add_tail(instrs, &cast->node.entry);
return &cast->node;
}
}
static struct hlsl_ir_node *add_implicit_conversion(struct hlsl_ctx *ctx, struct list *instrs,
struct hlsl_ir_node *node, struct hlsl_type *dst_type, const struct vkd3d_shader_location *loc)
{
struct hlsl_type *src_type = node->data_type;
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)
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, instrs, node, dst_type, loc);
}
static DWORD add_modifiers(struct hlsl_ctx *ctx, DWORD modifiers, DWORD mod, const struct vkd3d_shader_location loc)
{
if (modifiers & mod)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_modifiers_to_string(ctx, mod)))
hlsl_error(ctx, &loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Modifier '%s' was already specified.", string->buffer);
hlsl_release_string_buffer(ctx, string);
return modifiers;
}
return modifiers | mod;
}
static bool append_conditional_break(struct hlsl_ctx *ctx, struct list *cond_list)
{
struct hlsl_ir_node *condition, *not;
struct hlsl_ir_jump *jump;
struct hlsl_ir_if *iff;
/* E.g. "for (i = 0; ; ++i)". */
if (list_empty(cond_list))
return true;
condition = node_from_list(cond_list);
if (!(not = hlsl_new_unary_expr(ctx, HLSL_OP1_LOGIC_NOT, condition, condition->loc)))
return false;
list_add_tail(cond_list, &not->entry);
if (!(iff = hlsl_new_if(ctx, not, condition->loc)))
return false;
list_add_tail(cond_list, &iff->node.entry);
if (!(jump = hlsl_new_jump(ctx, HLSL_IR_JUMP_BREAK, condition->loc)))
return false;
hlsl_block_add_instr(&iff->then_instrs, &jump->node);
return true;
}
enum loop_type
{
LOOP_FOR,
LOOP_WHILE,
LOOP_DO_WHILE
};
static struct list *create_loop(struct hlsl_ctx *ctx, enum loop_type type, struct list *init, struct list *cond,
struct list *iter, struct list *body, struct vkd3d_shader_location loc)
{
struct list *list = NULL;
struct hlsl_ir_loop *loop = NULL;
struct hlsl_ir_if *cond_jump = NULL;
if (!(list = make_empty_list(ctx)))
goto oom;
if (init)
list_move_head(list, init);
if (!(loop = hlsl_new_loop(ctx, loc)))
goto oom;
list_add_tail(list, &loop->node.entry);
if (!append_conditional_break(ctx, cond))
goto oom;
if (type != LOOP_DO_WHILE)
list_move_tail(&loop->body.instrs, cond);
list_move_tail(&loop->body.instrs, body);
if (iter)
list_move_tail(&loop->body.instrs, iter);
if (type == LOOP_DO_WHILE)
list_move_tail(&loop->body.instrs, cond);
vkd3d_free(init);
vkd3d_free(cond);
vkd3d_free(body);
return list;
oom:
vkd3d_free(loop);
vkd3d_free(cond_jump);
vkd3d_free(list);
destroy_instr_list(init);
destroy_instr_list(cond);
destroy_instr_list(iter);
destroy_instr_list(body);
return NULL;
}
static unsigned int initializer_size(const struct parse_initializer *initializer)
{
unsigned int count = 0, i;
for (i = 0; i < initializer->args_count; ++i)
{
count += hlsl_type_component_count(initializer->args[i]->data_type);
}
return count;
}
static void free_parse_initializer(struct parse_initializer *initializer)
{
destroy_instr_list(initializer->instrs);
vkd3d_free(initializer->args);
}
static struct hlsl_ir_swizzle *get_swizzle(struct hlsl_ctx *ctx, struct hlsl_ir_node *value, const char *swizzle,
struct vkd3d_shader_location *loc)
{
unsigned int len = strlen(swizzle), component = 0;
unsigned int i, set, swiz = 0;
bool valid;
if (value->data_type->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 struct hlsl_ir_jump *add_return(struct hlsl_ctx *ctx, struct list *instrs,
struct hlsl_ir_node *return_value, struct vkd3d_shader_location loc)
{
struct hlsl_type *return_type = ctx->cur_function->return_type;
struct hlsl_ir_jump *jump;
if (ctx->cur_function->return_var)
{
if (return_value)
{
struct hlsl_ir_store *store;
if (!(return_value = add_implicit_conversion(ctx, instrs, return_value, return_type, &loc)))
return NULL;
if (!(store = hlsl_new_simple_store(ctx, ctx->cur_function->return_var, return_value)))
return NULL;
list_add_after(&return_value->entry, &store->node.entry);
}
else
{
hlsl_error(ctx, &loc, VKD3D_SHADER_ERROR_HLSL_INVALID_RETURN, "Non-void functions must return a value.");
return NULL;
}
}
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, loc)))
return NULL;
list_add_tail(instrs, &jump->node.entry);
return jump;
}
static struct hlsl_ir_load *add_load_component(struct hlsl_ctx *ctx, struct list *instrs, struct hlsl_ir_node *var_instr,
unsigned int comp, const struct vkd3d_shader_location *loc)
{
const struct hlsl_deref *src;
struct hlsl_ir_store *store;
struct hlsl_ir_load *load;
struct hlsl_block block;
struct hlsl_ir_var *var;
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;
list_add_tail(instrs, &store->node.entry);
src = &store->lhs;
if (!(load = hlsl_new_load_component(ctx, &block, src, comp, loc)))
return NULL;
list_move_tail(instrs, &block.instrs);
return load;
}
static bool add_record_access(struct hlsl_ctx *ctx, struct list *instrs, struct hlsl_ir_node *record,
unsigned int idx, const struct vkd3d_shader_location loc)
{
struct hlsl_ir_node *index;
struct hlsl_ir_constant *c;
assert(idx < record->data_type->e.record.field_count);
if (!(c = hlsl_new_uint_constant(ctx, idx, &loc)))
return false;
list_add_tail(instrs, &c->node.entry);
if (!(index = hlsl_new_index(ctx, record, &c->node, &loc)))
return false;
list_add_tail(instrs, &index->entry);
return true;
}
static struct hlsl_ir_node *add_binary_arithmetic_expr(struct hlsl_ctx *ctx, struct list *instrs,
enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2,
const struct vkd3d_shader_location *loc);
static struct hlsl_ir_node *add_zero_mipmap_level(struct hlsl_ctx *ctx, struct list *instrs,
struct hlsl_ir_node *index, unsigned int dim_count, const struct vkd3d_shader_location *loc)
{
struct hlsl_ir_load *coords_load;
struct hlsl_deref coords_deref;
struct hlsl_ir_constant *zero;
struct hlsl_ir_store *store;
struct hlsl_ir_var *coords;
if (!(coords = hlsl_new_synthetic_var(ctx, "coords",
hlsl_get_vector_type(ctx, HLSL_TYPE_UINT, dim_count + 1), loc)))
return NULL;
hlsl_init_simple_deref_from_var(&coords_deref, coords);
if (!(store = hlsl_new_store_index(ctx, &coords_deref, NULL, index, (1u << dim_count) - 1, loc)))
return NULL;
list_add_tail(instrs, &store->node.entry);
if (!(zero = hlsl_new_uint_constant(ctx, 0, loc)))
return NULL;
list_add_tail(instrs, &zero->node.entry);
if (!(store = hlsl_new_store_index(ctx, &coords_deref, NULL, &zero->node, 1u << dim_count, loc)))
return NULL;
list_add_tail(instrs, &store->node.entry);
if (!(coords_load = hlsl_new_var_load(ctx, coords, *loc)))
return NULL;
list_add_tail(instrs, &coords_load->node.entry);
return &coords_load->node;
}
static bool add_array_access(struct hlsl_ctx *ctx, struct list *instrs, struct hlsl_ir_node *array,
struct hlsl_ir_node *index, const struct vkd3d_shader_location *loc)
{
const struct hlsl_type *expr_type = array->data_type, *index_type = index->data_type;
struct hlsl_ir_node *return_index;
struct hlsl_ir_expr *cast;
if (expr_type->class == HLSL_CLASS_OBJECT
&& (expr_type->base_type == HLSL_TYPE_TEXTURE || expr_type->base_type == HLSL_TYPE_UAV)
&& expr_type->sampler_dim != HLSL_SAMPLER_DIM_GENERIC)
{
struct hlsl_resource_load_params load_params = {.type = HLSL_RESOURCE_LOAD};
unsigned int dim_count = hlsl_sampler_dim_count(expr_type->sampler_dim);
struct hlsl_ir_resource_load *resource_load;
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, instrs, index,
hlsl_get_vector_type(ctx, HLSL_TYPE_UINT, dim_count), &index->loc)))
return false;
if (!(index = add_zero_mipmap_level(ctx, instrs, index, dim_count, loc)))
return false;
load_params.format = expr_type->e.resource_format;
load_params.resource = array;
load_params.coords = index;
if (!(resource_load = hlsl_new_resource_load(ctx, &load_params, loc)))
return false;
list_add_tail(instrs, &resource_load->node.entry);
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;
list_add_tail(instrs, &cast->node.entry);
index = &cast->node;
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;
list_add_tail(instrs, &return_index->entry);
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,
unsigned int *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);
vkd3d_free(v);
}
static bool shader_is_sm_5_1(const struct hlsl_ctx *ctx)
{
return ctx->profile->major_version == 5 && ctx->profile->minor_version >= 1;
}
static bool gen_struct_fields(struct hlsl_ctx *ctx, struct parse_fields *fields,
struct hlsl_type *type, unsigned int 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 (shader_is_sm_5_1(ctx) && type->class == HLSL_CLASS_OBJECT)
{
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]);
}
}
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
{
unsigned int 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, &param->semantic, param->modifiers,
&param->reg_reservation)))
return false;
var->is_param = 1;
if (!hlsl_add_var(ctx, var, false))
{
hlsl_free_var(var);
return false;
}
if (!hlsl_array_reserve(ctx, (void **)&parameters->vars, &parameters->capacity,
parameters->count + 1, sizeof(*parameters->vars)))
return false;
parameters->vars[parameters->count++] = var;
return true;
}
static struct hlsl_reg_reservation parse_reg_reservation(const char *reg_string)
{
struct hlsl_reg_reservation reservation = {0};
if (!sscanf(reg_string + 1, "%u", &reservation.reg_index))
{
FIXME("Unsupported register reservation syntax.\n");
return reservation;
}
reservation.reg_type = reg_string[0];
return reservation;
}
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 (ctx->profile->major_version < 4)
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 = 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_ir_function_decl *get_func_decl(struct rb_tree *funcs,
const char *name, const struct hlsl_func_parameters *parameters)
{
struct hlsl_ir_function *func;
struct rb_entry *entry;
if ((entry = rb_get(funcs, name)))
{
func = RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry);
if ((entry = rb_get(&func->overloads, parameters)))
return RB_ENTRY_VALUE(entry, struct hlsl_ir_function_decl, entry);
}
return NULL;
}
static struct list *make_list(struct hlsl_ctx *ctx, struct hlsl_ir_node *node)
{
struct list *list;
if (!(list = make_empty_list(ctx)))
{
hlsl_free_instr(node);
return NULL;
}
list_add_tail(list, &node->entry);
return list;
}
static unsigned int evaluate_static_expression(struct hlsl_ir_node *node)
{
if (node->data_type->class != HLSL_CLASS_SCALAR)
return 0;
switch (node->type)
{
case HLSL_IR_CONSTANT:
{
struct hlsl_ir_constant *constant = hlsl_ir_constant(node);
const union hlsl_constant_value *value = &constant->value[0];
switch (constant->node.data_type->base_type)
{
case HLSL_TYPE_UINT:
return value->u;
case HLSL_TYPE_INT:
return value->i;
case HLSL_TYPE_FLOAT:
case HLSL_TYPE_HALF:
return value->f;
case HLSL_TYPE_DOUBLE:
return value->d;
case HLSL_TYPE_BOOL:
return !!value->u;
default:
vkd3d_unreachable();
}
}
case HLSL_IR_EXPR:
case HLSL_IR_INDEX:
case HLSL_IR_LOAD:
case HLSL_IR_RESOURCE_LOAD:
case HLSL_IR_SWIZZLE:
FIXME("Unhandled type %s.\n", hlsl_node_type_to_string(node->type));
return 0;
case HLSL_IR_CALL:
case HLSL_IR_IF:
case HLSL_IR_JUMP:
case HLSL_IR_LOOP:
case HLSL_IR_RESOURCE_STORE:
case HLSL_IR_STORE:
vkd3d_unreachable();
}
vkd3d_unreachable();
}
static bool expr_compatible_data_types(struct hlsl_type *t1, struct hlsl_type *t2)
{
if (t1->base_type > HLSL_TYPE_LAST_SCALAR || t2->base_type > HLSL_TYPE_LAST_SCALAR)
return false;
/* Scalar vars can be converted to pretty much everything */
if ((t1->dimx == 1 && t1->dimy == 1) || (t2->dimx == 1 && t2->dimy == 1))
return true;
if (t1->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 > HLSL_TYPE_LAST_SCALAR || t2 > HLSL_TYPE_LAST_SCALAR) {
FIXME("Unexpected base type.\n");
return HLSL_TYPE_FLOAT;
}
if (t1 == t2)
return t1 == HLSL_TYPE_BOOL ? HLSL_TYPE_INT : t1;
if (t1 == HLSL_TYPE_DOUBLE || t2 == HLSL_TYPE_DOUBLE)
return HLSL_TYPE_DOUBLE;
if (t1 == HLSL_TYPE_FLOAT || t2 == HLSL_TYPE_FLOAT
|| t1 == HLSL_TYPE_HALF || t2 == HLSL_TYPE_HALF)
return HLSL_TYPE_FLOAT;
if (t1 == HLSL_TYPE_UINT || t2 == HLSL_TYPE_UINT)
return HLSL_TYPE_UINT;
return HLSL_TYPE_INT;
}
static bool expr_common_shape(struct hlsl_ctx *ctx, struct hlsl_type *t1, struct hlsl_type *t2,
const struct vkd3d_shader_location *loc, enum hlsl_type_class *type, unsigned int *dimx, unsigned int *dimy)
{
if (t1->class > HLSL_CLASS_LAST_NUMERIC)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, t1)))
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Expression of type \"%s\" cannot be used in a numeric expression.", string->buffer);
hlsl_release_string_buffer(ctx, string);
return false;
}
if (t2->class > HLSL_CLASS_LAST_NUMERIC)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, t2)))
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Expression of type \"%s\" cannot be used in a numeric expression.", string->buffer);
hlsl_release_string_buffer(ctx, string);
return false;
}
if (!expr_compatible_data_types(t1, t2))
{
struct vkd3d_string_buffer *t1_string = hlsl_type_to_string(ctx, t1);
struct vkd3d_string_buffer *t2_string = hlsl_type_to_string(ctx, t2);
if (t1_string && t2_string)
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Expression data types \"%s\" and \"%s\" are incompatible.",
t1_string->buffer, t2_string->buffer);
hlsl_release_string_buffer(ctx, t1_string);
hlsl_release_string_buffer(ctx, t2_string);
return false;
}
if (t1->dimx == 1 && t1->dimy == 1)
{
*type = t2->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 list *instrs,
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_deref var_deref;
struct hlsl_ir_load *load;
struct hlsl_ir_var *var;
scalar_type = hlsl_get_scalar_type(ctx, type->base_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_ir_store *store;
struct hlsl_block block;
unsigned int j;
for (j = 0; j < HLSL_MAX_OPERANDS; j++)
{
if (operands[j])
{
if (!(load = add_load_component(ctx, instrs, operands[j], i, loc)))
return NULL;
cell_operands[j] = &load->node;
}
}
if (!(value = add_expr(ctx, instrs, op, cell_operands, scalar_type, loc)))
return NULL;
if (!(store = hlsl_new_store_component(ctx, &block, &var_deref, i, value)))
return NULL;
list_move_tail(instrs, &block.instrs);
}
if (!(load = hlsl_new_var_load(ctx, var, *loc)))
return NULL;
list_add_tail(instrs, &load->node.entry);
return &load->node;
}
if (!(expr = hlsl_new_expr(ctx, op, operands, type, loc)))
return NULL;
list_add_tail(instrs, &expr->entry);
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->base_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 list *instrs,
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, instrs, op, args, arg->data_type, loc);
}
static struct hlsl_ir_node *add_unary_bitwise_expr(struct hlsl_ctx *ctx, struct list *instrs,
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, instrs, op, arg, loc);
}
static struct hlsl_ir_node *add_unary_logical_expr(struct hlsl_ctx *ctx, struct list *instrs,
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, instrs, arg, bool_type, loc)))
return NULL;
return add_expr(ctx, instrs, op, args, bool_type, loc);
}
static struct hlsl_ir_node *add_binary_arithmetic_expr(struct hlsl_ctx *ctx, struct list *instrs,
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;
enum hlsl_base_type base = expr_common_base_type(arg1->data_type->base_type, arg2->data_type->base_type);
enum hlsl_type_class type;
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;
common_type = hlsl_get_numeric_type(ctx, type, base, dimx, dimy);
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, common_type, loc);
}
static struct list *add_binary_arithmetic_expr_merge(struct hlsl_ctx *ctx, struct list *list1, struct list *list2,
enum hlsl_ir_expr_op op, struct vkd3d_shader_location loc)
{
struct hlsl_ir_node *arg1 = node_from_list(list1), *arg2 = node_from_list(list2);
list_move_tail(list1, list2);
vkd3d_free(list2);
add_binary_arithmetic_expr(ctx, list1, op, arg1, arg2, &loc);
return list1;
}
static struct hlsl_ir_node *add_binary_bitwise_expr(struct hlsl_ctx *ctx, struct list *instrs,
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, instrs, op, arg1, arg2, loc);
}
static struct list *add_binary_bitwise_expr_merge(struct hlsl_ctx *ctx, struct list *list1, struct list *list2,
enum hlsl_ir_expr_op op, const struct vkd3d_shader_location *loc)
{
struct hlsl_ir_node *arg1 = node_from_list(list1), *arg2 = node_from_list(list2);
list_move_tail(list1, list2);
vkd3d_free(list2);
add_binary_bitwise_expr(ctx, list1, op, arg1, arg2, loc);
return list1;
}
static struct hlsl_ir_node *add_binary_comparison_expr(struct hlsl_ctx *ctx, struct list *instrs,
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_base_type base = expr_common_base_type(arg1->data_type->base_type, arg2->data_type->base_type);
enum hlsl_type_class type;
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;
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, 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, return_type, loc);
}
static struct list *add_binary_comparison_expr_merge(struct hlsl_ctx *ctx, struct list *list1, struct list *list2,
enum hlsl_ir_expr_op op, const struct vkd3d_shader_location loc)
{
struct hlsl_ir_node *arg1 = node_from_list(list1), *arg2 = node_from_list(list2);
list_move_tail(list1, list2);
vkd3d_free(list2);
add_binary_comparison_expr(ctx, list1, op, arg1, arg2, &loc);
return list1;
}
static struct hlsl_ir_node *add_binary_logical_expr(struct hlsl_ctx *ctx, struct list *instrs,
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, 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, common_type, loc);
}
static struct list *add_binary_logical_expr_merge(struct hlsl_ctx *ctx, struct list *list1, struct list *list2,
enum hlsl_ir_expr_op op, const struct vkd3d_shader_location *loc)
{
struct hlsl_ir_node *arg1 = node_from_list(list1), *arg2 = node_from_list(list2);
list_move_tail(list1, list2);
vkd3d_free(list2);
add_binary_logical_expr(ctx, list1, op, arg1, arg2, loc);
return list1;
}
static struct hlsl_ir_node *add_binary_shift_expr(struct hlsl_ctx *ctx, struct list *instrs,
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->base_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, instrs, arg1, return_type, loc)))
return NULL;
if (!(args[1] = add_implicit_conversion(ctx, instrs, arg2, integer_type, loc)))
return NULL;
return add_expr(ctx, instrs, op, args, return_type, loc);
}
static struct list *add_binary_shift_expr_merge(struct hlsl_ctx *ctx, struct list *list1, struct list *list2,
enum hlsl_ir_expr_op op, const struct vkd3d_shader_location *loc)
{
struct hlsl_ir_node *arg1 = node_from_list(list1), *arg2 = node_from_list(list2);
list_move_tail(list1, list2);
vkd3d_free(list2);
add_binary_shift_expr(ctx, list1, op, arg1, arg2, loc);
return list1;
}
static struct hlsl_ir_node *add_binary_dot_expr(struct hlsl_ctx *ctx, struct list *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->base_type, arg2->data_type->base_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.\n", 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.\n", 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 enum hlsl_ir_expr_op op_from_assignment(enum parse_assign_op op)
{
static const enum hlsl_ir_expr_op ops[] =
{
0,
HLSL_OP2_ADD,
0,
HLSL_OP2_MUL,
HLSL_OP2_DIV,
HLSL_OP2_MOD,
HLSL_OP2_LSHIFT,
HLSL_OP2_RSHIFT,
HLSL_OP2_BIT_AND,
HLSL_OP2_BIT_OR,
HLSL_OP2_BIT_XOR,
};
return ops[op];
}
static bool invert_swizzle(unsigned int *swizzle, unsigned int *writemask, unsigned int *ret_width)
{
unsigned int i, j, bit = 0, inverted = 0, width, new_writemask = 0, new_swizzle = 0;
/* Apply the writemask to the swizzle to get a new writemask and swizzle. */
for (i = 0; i < 4; ++i)
{
if (*writemask & (1 << i))
{
unsigned int s = (*swizzle >> (i * 2)) & 3;
new_swizzle |= s << (bit++ * 2);
if (new_writemask & (1 << s))
return false;
new_writemask |= 1 << s;
}
}
width = bit;
/* Invert the swizzle. */
bit = 0;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < width; ++j)
{
unsigned int s = (new_swizzle >> (j * 2)) & 3;
if (s == i)
inverted |= j << (bit++ * 2);
}
}
*swizzle = inverted;
*writemask = new_writemask;
*ret_width = width;
return true;
}
static struct hlsl_ir_node *add_assignment(struct hlsl_ctx *ctx, struct list *instrs, struct hlsl_ir_node *lhs,
enum parse_assign_op assign_op, struct hlsl_ir_node *rhs)
{
struct hlsl_type *lhs_type = lhs->data_type;
struct hlsl_ir_expr *copy;
unsigned int writemask = 0;
if (assign_op == ASSIGN_OP_SUB)
{
if (!(rhs = add_unary_arithmetic_expr(ctx, instrs, 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, instrs, op, lhs, rhs, &rhs->loc)))
return NULL;
}
if (lhs_type->class <= HLSL_CLASS_LAST_NUMERIC)
writemask = (1 << lhs_type->dimx) - 1;
if (!(rhs = add_implicit_conversion(ctx, instrs, rhs, lhs_type, &rhs->loc)))
return NULL;
while (lhs->type != HLSL_IR_LOAD && lhs->type != HLSL_IR_RESOURCE_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), *new_swizzle;
unsigned int width, s = swizzle->swizzle;
if (lhs->data_type->class == HLSL_CLASS_MATRIX)
hlsl_fixme(ctx, &lhs->loc, "Matrix assignment with a writemask.");
if (!invert_swizzle(&s, &writemask, &width))
{
hlsl_error(ctx, &lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_WRITEMASK, "Invalid writemask.");
return NULL;
}
if (!(new_swizzle = hlsl_new_swizzle(ctx, s, width, rhs, &swizzle->node.loc)))
{
return NULL;
}
list_add_tail(instrs, &new_swizzle->node.entry);
lhs = swizzle->val.node;
rhs = &new_swizzle->node;
}
else
{
hlsl_error(ctx, &lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_LVALUE, "Invalid lvalue.");
return NULL;
}
}
if (lhs->type == HLSL_IR_RESOURCE_LOAD)
{
struct hlsl_ir_resource_load *load = hlsl_ir_resource_load(lhs);
struct hlsl_ir_resource_store *store;
struct hlsl_type *resource_type;
struct hlsl_ir_swizzle *coords;
unsigned int dim_count;
/* Such an lvalue was produced by an index expression. */
assert(load->load_type == HLSL_RESOURCE_LOAD);
resource_type = hlsl_deref_get_type(ctx, &load->resource);
assert(resource_type->class == HLSL_CLASS_OBJECT);
assert(resource_type->base_type == HLSL_TYPE_TEXTURE || resource_type->base_type == HLSL_TYPE_UAV);
if (resource_type->base_type != HLSL_TYPE_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 (writemask != ((1u << resource_type->e.resource_format->dimx) - 1))
hlsl_error(ctx, &lhs->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_WRITEMASK,
"Resource store expressions must write to all components.");
/* Remove the (implicit) mipmap level from the load expression. */
assert(load->coords.node->data_type->class == HLSL_CLASS_VECTOR);
assert(load->coords.node->data_type->base_type == HLSL_TYPE_UINT);
assert(load->coords.node->data_type->dimx == dim_count + 1);
if (!(coords = hlsl_new_swizzle(ctx, HLSL_SWIZZLE(X, Y, Z, W), dim_count, load->coords.node, &lhs->loc)))
return NULL;
list_add_tail(instrs, &coords->node.entry);
if (!(store = hlsl_new_resource_store(ctx, &load->resource, &coords->node, rhs, &lhs->loc)))
return NULL;
list_add_tail(instrs, &store->node.entry);
}
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;
for (i = 0; i < mat->data_type->dimx; ++i)
{
struct hlsl_ir_store *store;
struct hlsl_ir_constant *c;
struct hlsl_ir_node *cell;
struct hlsl_ir_load *load;
struct hlsl_deref deref;
if (!(writemask & (1 << i)))
continue;
if (!(c = hlsl_new_uint_constant(ctx, i, &lhs->loc)))
return NULL;
list_add_tail(instrs, &c->node.entry);
if (!(cell = hlsl_new_index(ctx, &row->node, &c->node, &lhs->loc)))
return NULL;
list_add_tail(instrs, &cell->entry);
if (!(load = add_load_component(ctx, instrs, 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->node, 0, &rhs->loc)))
{
hlsl_cleanup_deref(&deref);
return NULL;
}
list_add_tail(instrs, &store->node.entry);
hlsl_cleanup_deref(&deref);
}
}
else
{
struct hlsl_ir_store *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;
}
list_add_tail(instrs, &store->node.entry);
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;
list_add_tail(instrs, &copy->node.entry);
return &copy->node;
}
static bool add_increment(struct hlsl_ctx *ctx, struct list *instrs, bool decrement, bool post,
struct vkd3d_shader_location loc)
{
struct hlsl_ir_node *lhs = node_from_list(instrs);
struct hlsl_ir_constant *one;
if (lhs->data_type->modifiers & HLSL_MODIFIER_CONST)
hlsl_error(ctx, &loc, VKD3D_SHADER_ERROR_HLSL_MODIFIES_CONST,
"Argument to %s%screment operator is const.", post ? "post" : "pre", decrement ? "de" : "in");
if (!(one = hlsl_new_int_constant(ctx, 1, &loc)))
return false;
list_add_tail(instrs, &one->node.entry);
if (!add_assignment(ctx, instrs, lhs, decrement ? ASSIGN_OP_SUB : ASSIGN_OP_ADD, &one->node))
return false;
if (post)
{
struct hlsl_ir_expr *copy;
if (!(copy = hlsl_new_copy(ctx, lhs)))
return false;
list_add_tail(instrs, &copy->node.entry);
/* Post increment/decrement expressions are considered const. */
if (!(copy->node.data_type = hlsl_type_clone(ctx, copy->node.data_type, 0, HLSL_MODIFIER_CONST)))
return false;
}
return true;
}
static void initialize_var_components(struct hlsl_ctx *ctx, struct list *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_type *dst_comp_type;
struct hlsl_ir_store *store;
struct hlsl_ir_load *load;
struct hlsl_ir_node *conv;
struct hlsl_block block;
if (!(load = 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 (!(conv = add_implicit_conversion(ctx, instrs, &load->node, dst_comp_type, &src->loc)))
return;
if (!(store = hlsl_new_store_component(ctx, &block, &dst_deref, *store_index, conv)))
return;
list_move_tail(instrs, &block.instrs);
++*store_index;
}
}
static bool type_has_object_components(struct hlsl_type *type, bool must_be_in_struct)
{
if (type->class == HLSL_CLASS_OBJECT)
return !must_be_in_struct;
if (type->class == HLSL_CLASS_ARRAY)
return type_has_object_components(type->e.array.type, must_be_in_struct);
if (type->class == HLSL_CLASS_STRUCT)
{
unsigned int i;
for (i = 0; i < type->e.record.field_count; ++i)
{
if (type_has_object_components(type->e.record.fields[i].type, false))
return true;
}
}
return false;
}
static bool type_has_numeric_components(struct hlsl_type *type)
{
if (type->class <= HLSL_CLASS_LAST_NUMERIC)
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 struct list *declare_vars(struct hlsl_ctx *ctx, struct hlsl_type *basic_type,
unsigned int modifiers, const struct vkd3d_shader_location *modifiers_loc, struct list *var_list)
{
struct parse_variable_def *v, *v_next;
struct hlsl_ir_function_decl *func;
unsigned int invalid_modifiers;
struct list *statements_list;
struct hlsl_ir_var *var;
struct hlsl_type *type;
bool local = true;
if (basic_type->class == HLSL_CLASS_MATRIX)
assert(basic_type->modifiers & HLSL_MODIFIERS_MAJORITY_MASK);
if (!(statements_list = make_empty_list(ctx)))
{
LIST_FOR_EACH_ENTRY_SAFE(v, v_next, var_list, struct parse_variable_def, entry)
free_parse_variable_def(v);
vkd3d_free(var_list);
return NULL;
}
if (!var_list)
return statements_list;
invalid_modifiers = modifiers & (HLSL_STORAGE_IN | HLSL_STORAGE_OUT);
if (invalid_modifiers)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_modifiers_to_string(ctx, invalid_modifiers)))
hlsl_error(ctx, modifiers_loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Modifiers '%s' are not allowed on non-parameter variables.", string->buffer);
hlsl_release_string_buffer(ctx, string);
}
LIST_FOR_EACH_ENTRY_SAFE(v, v_next, var_list, struct parse_variable_def, entry)
{
bool unbounded_res_array = false;
unsigned int i;
type = basic_type;
if (shader_is_sm_5_1(ctx) && type->class == HLSL_CLASS_OBJECT)
{
for (i = 0; i < v->arrays.count; ++i)
unbounded_res_array |= (v->arrays.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT);
}
if (unbounded_res_array)
{
if (v->arrays.count == 1)
{
hlsl_fixme(ctx, &v->loc, "Unbounded resource arrays.");
free_parse_variable_def(v);
continue;
}
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.");
free_parse_initializer(&v->initializer);
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.");
free_parse_initializer(&v->initializer);
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.");
free_parse_initializer(&v->initializer);
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);
free_parse_initializer(&v->initializer);
v->initializer.args_count = 0;
}
else
{
v->arrays.sizes[i] = size / elem_components;
}
}
type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]);
}
}
vkd3d_free(v->arrays.sizes);
if (!(var = hlsl_new_var(ctx, v->name, type, v->loc, &v->semantic, modifiers, &v->reg_reservation)))
{
free_parse_variable_def(v);
continue;
}
var->buffer = ctx->cur_buffer;
if (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 && (var->storage_modifiers & HLSL_STORAGE_UNIFORM) &&
type_has_object_components(var->data_type, true))
{
hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Target profile doesn't support objects as struct members in uniform variables.\n");
}
if ((func = hlsl_get_func_decl(ctx, var->name)))
{
hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED,
"'%s' is already defined as a function.", var->name);
hlsl_note(ctx, &func->loc, VKD3D_SHADER_LOG_ERROR,
"'%s' was previously defined here.", var->name);
}
}
else
{
static const unsigned int invalid = HLSL_STORAGE_EXTERN | HLSL_STORAGE_SHARED
| HLSL_STORAGE_GROUPSHARED | HLSL_STORAGE_UNIFORM;
if (modifiers & invalid)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_modifiers_to_string(ctx, modifiers & invalid)))
hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Modifiers '%s' are not allowed on local variables.", string->buffer);
hlsl_release_string_buffer(ctx, string);
}
if (var->semantic.name)
hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SEMANTIC,
"Semantics are not allowed on local variables.");
}
if ((var->storage_modifiers & HLSL_STORAGE_STATIC) && type_has_numeric_components(var->data_type)
&& type_has_object_components(var->data_type, false))
{
hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Static variables cannot have both numeric and resource components.");
}
if ((type->modifiers & HLSL_MODIFIER_CONST) && !v->initializer.args_count
&& !(modifiers & (HLSL_STORAGE_STATIC | HLSL_STORAGE_UNIFORM)))
{
hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_MISSING_INITIALIZER,
"Const variable \"%s\" is missing an initializer.", var->name);
hlsl_free_var(var);
free_parse_initializer(&v->initializer);
vkd3d_free(v);
continue;
}
if (!hlsl_add_var(ctx, var, local))
{
struct hlsl_ir_var *old = hlsl_get_var(ctx->cur_scope, var->name);
hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_REDEFINED,
"Variable \"%s\" was already declared in this scope.", var->name);
hlsl_note(ctx, &old->loc, VKD3D_SHADER_LOG_ERROR, "\"%s\" was previously declared here.", old->name);
hlsl_free_var(var);
free_parse_initializer(&v->initializer);
vkd3d_free(v);
continue;
}
if (v->initializer.args_count)
{
if (v->initializer.braces)
{
unsigned int size = initializer_size(&v->initializer);
unsigned int store_index = 0;
unsigned int k;
if (hlsl_type_component_count(type) != size)
{
hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
"Expected %u components in initializer, but got %u.",
hlsl_type_component_count(type), size);
free_parse_initializer(&v->initializer);
vkd3d_free(v);
continue;
}
for (k = 0; k < v->initializer.args_count; ++k)
{
initialize_var_components(ctx, v->initializer.instrs, var,
&store_index, v->initializer.args[k]);
}
}
else
{
struct hlsl_ir_load *load = hlsl_new_var_load(ctx, var, var->loc);
assert(v->initializer.args_count == 1);
list_add_tail(v->initializer.instrs, &load->node.entry);
add_assignment(ctx, v->initializer.instrs, &load->node, ASSIGN_OP_ASSIGN, v->initializer.args[0]);
}
if (modifiers & HLSL_STORAGE_STATIC)
list_move_tail(&ctx->static_initializers, v->initializer.instrs);
else
list_move_tail(statements_list, v->initializer.instrs);
vkd3d_free(v->initializer.args);
vkd3d_free(v->initializer.instrs);
}
else if (var->storage_modifiers & HLSL_STORAGE_STATIC)
{
struct hlsl_ir_constant *zero;
struct hlsl_ir_store *store;
struct hlsl_ir_node *cast;
/* Initialize statics to zero by default. */
if (type_has_object_components(var->data_type, false))
{
vkd3d_free(v);
continue;
}
if (!(zero = hlsl_new_uint_constant(ctx, 0, &var->loc)))
{
vkd3d_free(v);
continue;
}
list_add_tail(&ctx->static_initializers, &zero->node.entry);
if (!(cast = add_cast(ctx, &ctx->static_initializers, &zero->node, var->data_type, &var->loc)))
{
vkd3d_free(v);
continue;
}
if (!(store = hlsl_new_simple_store(ctx, var, cast)))
{
vkd3d_free(v);
continue;
}
list_add_tail(&ctx->static_initializers, &store->node.entry);
}
vkd3d_free(v);
}
vkd3d_free(var_list);
return statements_list;
}
struct find_function_call_args
{
struct hlsl_ctx *ctx;
const struct parse_initializer *params;
struct hlsl_ir_function_decl *decl;
unsigned int compatible_overloads_count;
};
static void find_function_call_exact(struct rb_entry *entry, void *context)
{
struct hlsl_ir_function_decl *decl = RB_ENTRY_VALUE(entry, struct hlsl_ir_function_decl, entry);
struct find_function_call_args *args = context;
unsigned int i;
if (decl->parameters.count != args->params->args_count)
return;
for (i = 0; i < decl->parameters.count; ++i)
{
if (!hlsl_types_are_equal(decl->parameters.vars[i]->data_type, args->params->args[i]->data_type))
return;
}
args->decl = decl;
}
static void find_function_call_compatible(struct rb_entry *entry, void *context)
{
struct hlsl_ir_function_decl *decl = RB_ENTRY_VALUE(entry, struct hlsl_ir_function_decl, entry);
struct find_function_call_args *args = context;
unsigned int i;
if (decl->parameters.count != args->params->args_count)
return;
for (i = 0; i < decl->parameters.count; ++i)
{
if (!implicit_compatible_data_types(args->ctx, args->params->args[i]->data_type,
decl->parameters.vars[i]->data_type))
return;
}
args->compatible_overloads_count++;
args->decl = decl;
}
static struct hlsl_ir_function_decl *find_function_call(struct hlsl_ctx *ctx,
const char *name, const struct parse_initializer *params,
const struct vkd3d_shader_location *loc)
{
struct find_function_call_args args = {.ctx = ctx, .params = params};
struct hlsl_ir_function *func;
struct rb_entry *entry;
if (!(entry = rb_get(&ctx->functions, name)))
return NULL;
func = RB_ENTRY_VALUE(entry, struct hlsl_ir_function, entry);
rb_for_each_entry(&func->overloads, find_function_call_exact, &args);
if (!args.decl)
{
rb_for_each_entry(&func->overloads, find_function_call_compatible, &args);
if (args.compatible_overloads_count > 1)
{
hlsl_fixme(ctx, loc, "Prioritize between multiple compatible function overloads.");
}
}
return args.decl;
}
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->base_type == HLSL_TYPE_FLOAT || type->base_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->base_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->base_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)
{
struct hlsl_type *type;
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);
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 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], *mul;
struct hlsl_ir_constant *one, *zero;
struct hlsl_ir_load *load;
unsigned int i, count;
if (!(one = hlsl_new_float_constant(ctx, 1.0f, loc)))
return false;
list_add_tail(params->instrs, &one->node.entry);
if (!(zero = hlsl_new_float_constant(ctx, 0.0f, loc)))
return false;
list_add_tail(params->instrs, &zero->node.entry);
mul = &one->node;
count = hlsl_type_component_count(arg->data_type);
for (i = 0; i < count; ++i)
{
if (!(load = add_load_component(ctx, params->instrs, arg, i, loc)))
return false;
if (!(mul = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, &load->node, mul, loc)))
return false;
}
return !!add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_NEQUAL, mul, &zero->node, loc);
}
/* 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 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->base_type == HLSL_TYPE_BOOL || data_type->base_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_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_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 intrinsic_cross(struct hlsl_ctx *ctx,
const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
{
struct hlsl_ir_swizzle *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->base_type == HLSL_TYPE_HALF && arg2->data_type->base_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;
list_add_tail(params->instrs, &arg1_swzl1->node.entry);
if (!(arg2_swzl1 = hlsl_new_swizzle(ctx, HLSL_SWIZZLE(Y, Z, X, Y), 3, arg2_cast, loc)))
return false;
list_add_tail(params->instrs, &arg2_swzl1->node.entry);
if (!(mul1 = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL,
&arg1_swzl1->node, &arg2_swzl1->node, loc)))
return false;
if (!(mul1_neg = hlsl_new_unary_expr(ctx, HLSL_OP1_NEG, mul1, *loc)))
return false;
list_add_tail(params->instrs, &mul1_neg->entry);
if (!(arg1_swzl2 = hlsl_new_swizzle(ctx, HLSL_SWIZZLE(Y, Z, X, Y), 3, arg1_cast, loc)))
return false;
list_add_tail(params->instrs, &arg1_swzl2->node.entry);
if (!(arg2_swzl2 = hlsl_new_swizzle(ctx, HLSL_SWIZZLE(Z, X, Y, Z), 3, arg2_cast, loc)))
return false;
list_add_tail(params->instrs, &arg2_swzl2->node.entry);
if (!(mul2 = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL,
&arg1_swzl2->node, &arg2_swzl2->node, loc)))
return false;
return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, mul2, mul1_neg, 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_constant *coeff;
struct hlsl_ir_node *arg, *mul;
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;
list_add_tail(params->instrs, &coeff->node.entry);
if (!(mul = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, &coeff->node, 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_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_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 list *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_node *n_l_neg, *n_h_neg, *specular_or, *specular_pow;
struct hlsl_ir_constant *init, *zero;
struct hlsl_ir_node *n_l, *n_h, *m;
struct hlsl_ir_node *diffuse;
struct hlsl_ir_store *store;
struct hlsl_deref var_deref;
struct hlsl_type *ret_type;
struct hlsl_ir_load *load;
struct hlsl_ir_var *var;
struct hlsl_block block;
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 (!(n_l = intrinsic_float_convert_arg(ctx, params, params->args[0], loc)))
return false;
if (!(n_h = intrinsic_float_convert_arg(ctx, params, params->args[1], loc)))
return false;
if (!(m = intrinsic_float_convert_arg(ctx, params, params->args[2], loc)))
return false;
ret_type = hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, 4);
if (!(var = hlsl_new_synthetic_var(ctx, "lit", ret_type, loc)))
return false;
hlsl_init_simple_deref_from_var(&var_deref, var);
if (!(init = hlsl_new_constant(ctx, ret_type, loc)))
return false;
init->value[0].f = 1.0f;
init->value[1].f = 0.0f;
init->value[2].f = 0.0f;
init->value[3].f = 1.0f;
list_add_tail(params->instrs, &init->node.entry);
if (!(store = hlsl_new_simple_store(ctx, var, &init->node)))
return false;
list_add_tail(params->instrs, &store->node.entry);
if (!(zero = hlsl_new_float_constant(ctx, 0.0f, loc)))
return false;
list_add_tail(params->instrs, &zero->node.entry);
/* Diffuse component. */
if (!(diffuse = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MAX, n_l, &zero->node, loc)))
return false;
if (!(store = hlsl_new_store_component(ctx, &block, &var_deref, 1, diffuse)))
return false;
list_move_tail(params->instrs, &block.instrs);
/* Specular component. */
if (!(n_h_neg = add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_LESS,
n_h, &zero->node, loc)))
return false;
if (!(n_l_neg = add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_LESS,
n_l, &zero->node, loc)))
return false;
if (!(specular_or = add_binary_logical_expr(ctx, params->instrs, HLSL_OP2_LOGIC_OR, n_l_neg, n_h_neg, loc)))
return false;
if (!(specular_pow = add_pow_expr(ctx, params->instrs, n_h, m, loc)))
return false;
if (!(load = hlsl_add_conditional(ctx, params->instrs, specular_or, &zero->node, specular_pow)))
return false;
if (!(store = hlsl_new_store_component(ctx, &block, &var_deref, 2, &load->node)))
return false;
list_move_tail(params->instrs, &block.instrs);
if (!(load = hlsl_new_var_load(ctx, var, *loc)))
return false;
list_add_tail(params->instrs, &load->node.entry);
return true;
}
static bool intrinsic_log(struct hlsl_ctx *ctx,
const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
{
struct hlsl_ir_constant *coeff;
struct hlsl_ir_node *log, *arg;
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;
return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, log, &coeff->node, loc);
}
static bool intrinsic_log10(struct hlsl_ctx *ctx,
const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
{
struct hlsl_ir_constant *coeff;
struct hlsl_ir_node *log, *arg;
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;
return !!add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, log, &coeff->node, 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->base_type, arg2->data_type->base_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_ir_store *store;
struct hlsl_block block;
for (k = 0; k < cast_type1->dimx && k < cast_type2->dimy; ++k)
{
struct hlsl_ir_load *value1, *value2;
struct hlsl_ir_node *mul;
if (!(value1 = add_load_component(ctx, params->instrs, cast1, j * cast1->data_type->dimx + k, loc)))
return false;
if (!(value2 = 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->node, &value2->node, 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 (!(store = hlsl_new_store_component(ctx, &block, &var_deref, j * matrix_type->dimx + i, instr)))
return false;
list_move_tail(params->instrs, &block.instrs);
}
}
if (!(load = hlsl_new_var_load(ctx, var, *loc)))
return false;
list_add_tail(params->instrs, &load->node.entry);
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_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_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_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);
}
/* 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_node *min_arg, *max_arg, *x_arg, *p, *p_num, *p_denom, *res;
struct hlsl_ir_constant *one, *minus_two, *three;
if (!elementwise_intrinsic_float_convert_args(ctx, params, loc))
return false;
min_arg = params->args[0];
max_arg = params->args[1];
x_arg = params->args[2];
if (!(min_arg = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_NEG, min_arg, loc)))
return false;
if (!(p_num = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, x_arg, min_arg, loc)))
return false;
if (!(p_denom = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, max_arg, min_arg, loc)))
return false;
if (!(one = hlsl_new_float_constant(ctx, 1.0, loc)))
return false;
list_add_tail(params->instrs, &one->node.entry);
if (!(p_denom = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_DIV, &one->node, p_denom, loc)))
return false;
if (!(p = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, p_num, p_denom, loc)))
return false;
if (!(p = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_SAT, p, loc)))
return false;
if (!(minus_two = hlsl_new_float_constant(ctx, -2.0, loc)))
return false;
list_add_tail(params->instrs, &minus_two->node.entry);
if (!(three = hlsl_new_float_constant(ctx, 3.0, loc)))
return false;
list_add_tail(params->instrs, &three->node.entry);
if (!(res = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, &minus_two->node, p, loc)))
return false;
if (!(res = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_ADD, &three->node, res, loc)))
return false;
if (!(p = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, p, p, loc)))
return false;
if (!(res = add_binary_arithmetic_expr(ctx, params->instrs, HLSL_OP2_MUL, p, res, loc)))
return false;
return true;
}
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_tex(struct hlsl_ctx *ctx, const struct parse_initializer *params,
const struct vkd3d_shader_location *loc, const char *name, enum hlsl_sampler_dim dim)
{
struct hlsl_resource_load_params load_params = {.type = HLSL_RESOURCE_SAMPLE};
const struct hlsl_type *sampler_type;
struct hlsl_ir_resource_load *load;
struct hlsl_ir_node *coords;
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;
}
if (params->args_count == 4)
{
hlsl_fixme(ctx, loc, "Samples with gradients are not implemented.\n");
}
sampler_type = params->args[0]->data_type;
if (sampler_type->class != HLSL_CLASS_OBJECT || sampler_type->base_type != HLSL_TYPE_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);
}
else
{
load_params.resource = params->args[0];
}
if (!(coords = add_implicit_conversion(ctx, params->instrs, params->args[1],
hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, hlsl_sampler_dim_count(dim)), loc)))
coords = params->args[1];
load_params.coords = coords;
load_params.format = hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, 4);
if (!(load = hlsl_new_resource_load(ctx, &load_params, loc)))
return false;
list_add_tail(params->instrs, &load->node.entry);
return true;
}
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_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_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_deref var_deref;
struct hlsl_type *mat_type;
struct hlsl_ir_load *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'.\n",
string->buffer);
hlsl_release_string_buffer(ctx, string);
return false;
}
if (arg_type->class == HLSL_CLASS_SCALAR)
{
list_add_tail(params->instrs, &arg->entry);
return true;
}
mat_type = hlsl_get_matrix_type(ctx, arg_type->base_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_ir_store *store;
struct hlsl_block block;
if (!(load = add_load_component(ctx, params->instrs, arg, j * arg->data_type->dimx + i, loc)))
return false;
if (!(store = hlsl_new_store_component(ctx, &block, &var_deref, i * var->data_type->dimx + j, &load->node)))
return false;
list_move_tail(params->instrs, &block.instrs);
}
}
if (!(load = hlsl_new_var_load(ctx, var, *loc)))
return false;
list_add_tail(params->instrs, &load->node.entry);
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. */
{"abs", 1, true, intrinsic_abs},
{"all", 1, true, intrinsic_all},
{"asuint", -1, true, intrinsic_asuint},
{"clamp", 3, true, intrinsic_clamp},
{"cos", 1, true, intrinsic_cos},
{"cross", 2, true, intrinsic_cross},
{"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},
{"frac", 1, true, intrinsic_frac},
{"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},
{"reflect", 2, true, intrinsic_reflect},
{"round", 1, true, intrinsic_round},
{"rsqrt", 1, true, intrinsic_rsqrt},
{"saturate", 1, true, intrinsic_saturate},
{"sin", 1, true, intrinsic_sin},
{"smoothstep", 3, true, intrinsic_smoothstep},
{"sqrt", 1, true, intrinsic_sqrt},
{"step", 2, true, intrinsic_step},
{"tex2D", -1, false, intrinsic_tex2D},
{"tex3D", -1, false, intrinsic_tex3D},
{"transpose", 1, true, intrinsic_transpose},
};
static int intrinsic_function_name_compare(const void *a, const void *b)
{
const struct intrinsic_function *func = b;
return strcmp(a, func->name);
}
static struct list *add_call(struct hlsl_ctx *ctx, const char *name,
struct parse_initializer *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)))
{
struct hlsl_ir_node *call;
unsigned int i;
assert(args->args_count == decl->parameters.count);
for (i = 0; i < decl->parameters.count; ++i)
{
struct hlsl_ir_var *param = decl->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)))
goto fail;
args->args[i] = cast;
arg = cast;
}
if (param->storage_modifiers & HLSL_STORAGE_IN)
{
struct hlsl_ir_store *store;
if (!(store = hlsl_new_simple_store(ctx, param, arg)))
goto fail;
list_add_tail(args->instrs, &store->node.entry);
}
}
if (!(call = hlsl_new_call(ctx, decl, loc)))
goto fail;
list_add_tail(args->instrs, &call->entry);
for (i = 0; i < decl->parameters.count; ++i)
{
struct hlsl_ir_var *param = decl->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.", decl->func->name);
if (!(load = hlsl_new_var_load(ctx, param, arg->loc)))
goto fail;
list_add_tail(args->instrs, &load->node.entry);
if (!add_assignment(ctx, args->instrs, arg, ASSIGN_OP_ASSIGN, &load->node))
goto fail;
}
}
if (decl->return_var)
{
struct hlsl_ir_load *load;
if (!(load = hlsl_new_var_load(ctx, decl->return_var, *loc)))
goto fail;
list_add_tail(args->instrs, &load->node.entry);
}
else
{
struct hlsl_ir_node *operands[HLSL_MAX_OPERANDS] = {0};
struct hlsl_ir_node *expr;
if (!(expr = hlsl_new_expr(ctx, HLSL_OP0_VOID, operands, ctx->builtin_types.Void, loc)))
goto fail;
list_add_tail(args->instrs, &expr->entry);
}
}
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 (args->args[i]->data_type->class > HLSL_CLASS_LAST_NUMERIC)
{
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 list *add_constructor(struct hlsl_ctx *ctx, struct hlsl_type *type,
struct parse_initializer *params, 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)
{
struct hlsl_ir_node *arg = params->args[i];
if (arg->data_type->class == HLSL_CLASS_OBJECT)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, arg->data_type)))
hlsl_error(ctx, &arg->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Invalid type %s for constructor argument.", string->buffer);
hlsl_release_string_buffer(ctx, string);
continue;
}
initialize_var_components(ctx, params->instrs, var, &idx, arg);
}
if (!(load = hlsl_new_var_load(ctx, var, loc)))
return NULL;
list_add_tail(params->instrs, &load->node.entry);
vkd3d_free(params->args);
return params->instrs;
}
static 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:
/* Offset parameters not supported for these types. */
return 0;
default:
vkd3d_unreachable();
}
}
static bool add_method_call(struct hlsl_ctx *ctx, struct list *instrs, struct hlsl_ir_node *object,
const char *name, const struct parse_initializer *params, const struct vkd3d_shader_location *loc)
{
const struct hlsl_type *object_type = object->data_type;
if (object_type->class != HLSL_CLASS_OBJECT || object_type->base_type != HLSL_TYPE_TEXTURE
|| object_type->sampler_dim == HLSL_SAMPLER_DIM_GENERIC)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, object_type)))
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Type '%s' does not have methods.", string->buffer);
hlsl_release_string_buffer(ctx, string);
return false;
}
if (!strcmp(name, "Load")
&& object_type->sampler_dim != HLSL_SAMPLER_DIM_CUBE
&& object_type->sampler_dim != HLSL_SAMPLER_DIM_CUBEARRAY)
{
const unsigned int sampler_dim = hlsl_sampler_dim_count(object_type->sampler_dim);
const unsigned int offset_dim = hlsl_offset_dim_count(object_type->sampler_dim);
struct hlsl_resource_load_params load_params = {.type = HLSL_RESOURCE_LOAD};
struct hlsl_ir_resource_load *load;
bool multisampled;
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 > 3 + multisampled)
{
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, 3 + multisampled, params->args_count);
return false;
}
if (multisampled)
{
hlsl_fixme(ctx, loc, "Load() sampling index parameter.");
}
assert(offset_dim);
if (params->args_count > 1 + multisampled)
{
if (!(load_params.texel_offset = add_implicit_conversion(ctx, instrs, params->args[1 + multisampled],
hlsl_get_vector_type(ctx, HLSL_TYPE_INT, offset_dim), loc)))
return false;
}
if (params->args_count > 2 + multisampled)
{
hlsl_fixme(ctx, loc, "Tiled resource status argument.");
}
/* +1 for the mipmap level */
if (!(load_params.coords = add_implicit_conversion(ctx, instrs, params->args[0],
hlsl_get_vector_type(ctx, HLSL_TYPE_INT, sampler_dim + 1), 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;
list_add_tail(instrs, &load->node.entry);
return true;
}
else if (!strcmp(name, "Sample")
&& object_type->sampler_dim != HLSL_SAMPLER_DIM_2DMS
&& object_type->sampler_dim != HLSL_SAMPLER_DIM_2DMSARRAY)
{
const unsigned int sampler_dim = hlsl_sampler_dim_count(object_type->sampler_dim);
const unsigned int offset_dim = hlsl_offset_dim_count(object_type->sampler_dim);
struct hlsl_resource_load_params load_params = {.type = HLSL_RESOURCE_SAMPLE};
const struct hlsl_type *sampler_type;
struct hlsl_ir_resource_load *load;
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_OBJECT || sampler_type->base_type != HLSL_TYPE_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, instrs, 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, instrs, 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;
list_add_tail(instrs, &load->node.entry);
return true;
}
else if ((!strcmp(name, "Gather") || !strcmp(name, "GatherRed") || !strcmp(name, "GatherBlue")
|| !strcmp(name, "GatherGreen") || !strcmp(name, "GatherAlpha"))
&& (object_type->sampler_dim == HLSL_SAMPLER_DIM_2D
|| object_type->sampler_dim == HLSL_SAMPLER_DIM_2DARRAY
|| object_type->sampler_dim == HLSL_SAMPLER_DIM_CUBE
|| object_type->sampler_dim == HLSL_SAMPLER_DIM_CUBEARRAY))
{
const unsigned int sampler_dim = hlsl_sampler_dim_count(object_type->sampler_dim);
const unsigned int offset_dim = hlsl_offset_dim_count(object_type->sampler_dim);
struct hlsl_resource_load_params load_params = {0};
const struct hlsl_type *sampler_type;
struct hlsl_ir_resource_load *load;
unsigned int read_channel;
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, instrs, 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_OBJECT || sampler_type->base_type != HLSL_TYPE_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, instrs, 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->base_type, 4);
load_params.resource = object;
load_params.sampler = params->args[0];
if (!(load = hlsl_new_resource_load(ctx, &load_params, loc)))
return false;
list_add_tail(instrs, &load->node.entry);
return true;
}
else if (!strcmp(name, "SampleLevel")
&& object_type->sampler_dim != HLSL_SAMPLER_DIM_2DMS
&& object_type->sampler_dim != HLSL_SAMPLER_DIM_2DMSARRAY)
{
struct hlsl_resource_load_params load_params = {.type = HLSL_RESOURCE_SAMPLE_LOD};
const unsigned int sampler_dim = hlsl_sampler_dim_count(object_type->sampler_dim);
const unsigned int offset_dim = hlsl_offset_dim_count(object_type->sampler_dim);
const struct hlsl_type *sampler_type;
struct hlsl_ir_resource_load *load;
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 'SampleLevel': expected from 3 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_OBJECT || sampler_type->base_type != HLSL_TYPE_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 SampleLevel(): expected 'sampler', but got '%s'.", string->buffer);
hlsl_release_string_buffer(ctx, string);
return false;
}
if (!(load_params.coords = add_implicit_conversion(ctx, instrs, 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, instrs, 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, instrs, 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;
list_add_tail(instrs, &load->node.entry);
return true;
}
else
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, object_type)))
hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED,
"Method '%s' is not defined on type '%s'.", name, string->buffer);
hlsl_release_string_buffer(ctx, string);
return false;
}
}
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);
}
}
}
%locations
%define parse.error verbose
%define api.prefix {hlsl_yy}
%define api.pure full
%expect 1
%lex-param {yyscan_t scanner}
%parse-param {void *scanner}
%parse-param {struct hlsl_ctx *ctx}
%union
{
struct hlsl_type *type;
INT intval;
FLOAT floatval;
bool boolval;
char *name;
DWORD modifiers;
struct hlsl_ir_node *instr;
struct list *list;
struct parse_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;
}
%token KW_BLENDSTATE
%token KW_BREAK
%token KW_BUFFER
%token KW_CBUFFER
%token KW_COLUMN_MAJOR
%token KW_COMPILE
%token KW_CONST
%token KW_CONTINUE
%token KW_DEPTHSTENCILSTATE
%token KW_DEPTHSTENCILVIEW
%token KW_DISCARD
%token KW_DO
%token KW_DOUBLE
%token KW_ELSE
%token KW_EXTERN
%token KW_FALSE
%token KW_FOR
%token KW_GEOMETRYSHADER
%token KW_GROUPSHARED
%token KW_IF
%token KW_IN
%token KW_INLINE
%token KW_INOUT
%token KW_MATRIX
%token KW_NAMESPACE
%token KW_NOINTERPOLATION
%token KW_OUT
%token KW_PACKOFFSET
%token KW_PASS
%token KW_PIXELSHADER
%token KW_PRECISE
%token KW_RASTERIZERSTATE
%token KW_RENDERTARGETVIEW
%token KW_RETURN
%token KW_REGISTER
%token KW_ROW_MAJOR
%token KW_RWTEXTURE1D
%token KW_RWTEXTURE2D
%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_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_UNIFORM
%token KW_VECTOR
%token KW_VERTEXSHADER
%token KW_VOID
%token KW_VOLATILE
%token KW_WHILE
%token OP_INC
%token OP_DEC
%token OP_AND
%token OP_OR
%token OP_EQ
%token OP_LEFTSHIFT
%token OP_LEFTSHIFTASSIGN
%token OP_RIGHTSHIFT
%token OP_RIGHTSHIFTASSIGN
%token OP_ELLIPSIS
%token OP_LE
%token OP_GE
%token OP_NE
%token OP_ADDASSIGN
%token OP_SUBASSIGN
%token OP_MULASSIGN
%token OP_DIVASSIGN
%token OP_MODASSIGN
%token OP_ANDASSIGN
%token OP_ORASSIGN
%token OP_XORASSIGN
%token OP_UNKNOWN1
%token OP_UNKNOWN2
%token OP_UNKNOWN3
%token OP_UNKNOWN4
%token <floatval> C_FLOAT
%token <intval> C_INTEGER
%token <intval> PRE_LINE
%type <list> add_expr
%type <list> assignment_expr
%type <list> bitand_expr
%type <list> bitor_expr
%type <list> bitxor_expr
%type <list> compound_statement
%type <list> conditional_expr
%type <list> declaration
%type <list> declaration_statement
%type <list> equality_expr
%type <list> expr
%type <list> expr_optional
%type <list> expr_statement
%type <list> initializer_expr
%type <list> jump_statement
%type <list> logicand_expr
%type <list> logicor_expr
%type <list> loop_statement
%type <list> mul_expr
%type <list> postfix_expr
%type <list> primary_expr
%type <list> relational_expr
%type <list> selection_statement
%type <list> shift_expr
%type <list> statement
%type <list> statement_list
%type <list> struct_declaration
%type <list> type_specs
%type <list> unary_expr
%type <list> variables_def
%type <list> variables_def_optional
%token <name> VAR_IDENTIFIER
%token <name> NEW_IDENTIFIER
%token <name> STRING
%token <name> TYPE_IDENTIFIER
%type <arrays> arrays
%type <assign_op> assign_op
%type <attr> attribute
%type <attr_list> attribute_list
%type <boolval> boolean
%type <buffer_type> buffer_type
%type <colon_attribute> colon_attribute
%type <fields> field
%type <fields> fields_list
%type <function> func_prototype
%type <function> func_prototype_no_attrs
%type <initializer> complex_initializer
%type <initializer> complex_initializer_list
%type <initializer> func_arguments
%type <initializer> initializer_expr_list
%type <if_body> if_body
%type <modifiers> var_modifiers
%type <name> any_identifier
%type <name> var_identifier
%type <parameter> parameter
%type <parameters> param_list
%type <parameters> parameters
%type <reg_reservation> register_opt
%type <reg_reservation> packoffset_opt
%type <sampler_dim> texture_type texture_ms_type uav_type
%type <semantic> semantic
%type <type> field_type
%type <type> named_struct_spec
%type <type> unnamed_struct_spec
%type <type> struct_spec
%type <type> type
%type <type> type_no_void
%type <type> typedef_type
%type <variable_def> type_spec
%type <variable_def> variable_decl
%type <variable_def> variable_def
%%
hlsl_prog:
%empty
| hlsl_prog func_declaration
| hlsl_prog buffer_declaration buffer_body
| hlsl_prog declaration_statement
{
if (!list_empty($2))
hlsl_fixme(ctx, &@2, "Uniform initializer.");
destroy_instr_list($2);
}
| hlsl_prog preproc_directive
| hlsl_prog ';'
buffer_declaration:
buffer_type any_identifier colon_attribute
{
if ($3.semantic.name)
hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_SEMANTIC, "Semantics are not allowed on buffers.");
if (!(ctx->cur_buffer = hlsl_new_buffer(ctx, $1, $2, &$3.reg_reservation, @2)))
YYABORT;
}
buffer_body:
'{' declaration_statement_list '}'
{
ctx->cur_buffer = ctx->globals_buffer;
}
buffer_type:
KW_CBUFFER
{
$$ = HLSL_BUFFER_CONSTANT;
}
| KW_TBUFFER
{
$$ = HLSL_BUFFER_TEXTURE;
}
declaration_statement_list:
%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:
var_modifiers struct_spec variables_def_optional ';'
{
struct hlsl_type *type;
unsigned int modifiers = $1;
if (!$3)
{
if (!$2->name)
hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX,
"Anonymous struct type must declare a variable.");
if (modifiers)
hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Modifiers are not allowed on struct type declarations.");
}
if (!(type = apply_type_modifiers(ctx, $2, &modifiers, true, &@1)))
YYABORT;
$$ = declare_vars(ctx, type, modifiers, &@1, $3);
}
struct_spec:
named_struct_spec
| unnamed_struct_spec
named_struct_spec:
KW_STRUCT any_identifier '{' fields_list '}'
{
bool ret;
$$ = hlsl_new_struct_type(ctx, $2, $4.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;
unsigned int modifiers = $1;
if (!(type = apply_type_modifiers(ctx, $2, &modifiers, true, &@1)))
YYABORT;
if (modifiers & ~HLSL_STORAGE_NOINTERPOLATION)
{
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;
list_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;
list_init(&$$->instrs);
list_move_tail(&$$->instrs, $4.instrs);
vkd3d_free($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]);
}
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))))
{
unsigned int i;
for (i = 0; i < $$.count; ++i)
hlsl_free_attribute((void *)$$.attrs[i]);
vkd3d_free($$.attrs);
YYABORT;
}
$$.attrs = new_array;
$$.attrs[$$.count++] = $2;
}
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);
hlsl_free_instr_list($2);
}
else
{
size_t i;
decl->has_body = true;
list_move_tail(&decl->body.instrs, $2);
vkd3d_free($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
{
unsigned int modifiers = $1;
struct hlsl_ir_var *var;
struct hlsl_type *type;
if (modifiers & ~HLSL_MODIFIERS_MAJORITY_MASK)
hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Only majority modifiers are allowed on functions.");
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 = get_func_decl(&ctx->functions, $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
{
free($1.attrs);
}
$$ = $2;
}
compound_statement:
'{' '}'
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
}
| '{' scope_start statement_list '}'
{
hlsl_pop_scope(ctx);
$$ = $3;
}
scope_start:
%empty
{
hlsl_push_scope(ctx);
}
var_identifier:
VAR_IDENTIFIER
| NEW_IDENTIFIER
colon_attribute:
%empty
{
$$.semantic.name = NULL;
$$.reg_reservation.reg_type = 0;
$$.reg_reservation.offset_type = 0;
}
| semantic
{
$$.semantic = $1;
$$.reg_reservation.reg_type = 0;
$$.reg_reservation.offset_type = 0;
}
| register_opt
{
$$.semantic.name = NULL;
$$.reg_reservation = $1;
}
| packoffset_opt
{
$$.semantic.name = NULL;
$$.reg_reservation = $1;
}
semantic:
':' any_identifier
{
char *p;
for (p = $2 + strlen($2); p > $2 && isdigit(p[-1]); --p)
;
$$.name = $2;
$$.index = atoi(p);
*p = 0;
}
/* FIXME: Writemasks */
register_opt:
':' KW_REGISTER '(' any_identifier ')'
{
$$ = parse_reg_reservation($4);
vkd3d_free($4);
}
| ':' KW_REGISTER '(' any_identifier ',' any_identifier ')'
{
FIXME("Ignoring shader target %s in a register reservation.\n", debugstr_a($4));
vkd3d_free($4);
$$ = parse_reg_reservation($6);
vkd3d_free($6);
}
packoffset_opt:
':' 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
{
unsigned int 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]);
}
$$.type = type;
$$.name = $3;
$$.semantic = $5.semantic;
$$.reg_reservation = $5.reg_reservation;
}
texture_type:
KW_TEXTURE1D
{
$$ = HLSL_SAMPLER_DIM_1D;
}
| KW_TEXTURE2D
{
$$ = HLSL_SAMPLER_DIM_2D;
}
| KW_TEXTURE3D
{
$$ = HLSL_SAMPLER_DIM_3D;
}
| KW_TEXTURECUBE
{
$$ = HLSL_SAMPLER_DIM_CUBE;
}
| 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_RWTEXTURE1D
{
$$ = HLSL_SAMPLER_DIM_1D;
}
| KW_RWTEXTURE2D
{
$$ = HLSL_SAMPLER_DIM_2D;
}
| KW_RWTEXTURE3D
{
$$ = 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->base_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->base_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_SAMPLER1D
{
$$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_1D];
}
| KW_SAMPLER2D
{
$$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_2D];
}
| KW_SAMPLER3D
{
$$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_3D];
}
| KW_SAMPLERCUBE
{
$$ = ctx->builtin_types.sampler[HLSL_SAMPLER_DIM_3D];
}
| KW_TEXTURE
{
$$ = hlsl_new_texture_type(ctx, HLSL_SAMPLER_DIM_GENERIC, NULL, 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);
/* TODO: unspecified sample count is not allowed for all targets */
$$ = hlsl_new_texture_type(ctx, $1, $3, 0);
}
| texture_ms_type '<' type ',' shift_expr '>'
{
unsigned int sample_count = evaluate_static_expression(node_from_list($5));
destroy_instr_list($5);
$$ = hlsl_new_texture_type(ctx, $1, $3, sample_count);
}
| uav_type '<' type '>'
{
if ($3->class > HLSL_CLASS_VECTOR)
{
struct vkd3d_string_buffer *string;
string = hlsl_type_to_string(ctx, $3);
if (string)
hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"UAV data type %s is not scalar or vector.", string->buffer);
hlsl_release_string_buffer(ctx, string);
}
$$ = hlsl_new_uav_type(ctx, $1, $3);
}
| TYPE_IDENTIFIER
{
$$ = hlsl_get_type(ctx->cur_scope, $1, true, true);
if ($$->is_minimum_precision)
{
if (ctx->profile->major_version < 4)
{
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_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);
}
type:
type_no_void
| KW_VOID
{
$$ = ctx->builtin_types.Void;
}
declaration_statement:
declaration
| struct_declaration
| typedef
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
}
typedef_type:
type
| struct_spec
typedef:
KW_TYPEDEF var_modifiers typedef_type type_specs ';'
{
struct parse_variable_def *v, *v_next;
unsigned int 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:
var_modifiers type variables_def ';'
{
struct hlsl_type *type;
unsigned int modifiers = $1;
if (!(type = apply_type_modifiers(ctx, $2, &modifiers, true, &@1)))
YYABORT;
$$ = declare_vars(ctx, type, modifiers, &@1, $3);
}
variables_def_optional:
%empty
{
$$ = NULL;
}
| variables_def
variables_def:
variable_def
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
list_add_head($$, &$1->entry);
}
| variables_def ',' variable_def
{
$$ = $1;
list_add_tail($$, &$3->entry);
}
variable_decl:
any_identifier arrays colon_attribute
{
$$ = hlsl_alloc(ctx, sizeof(*$$));
$$->loc = @1;
$$->name = $1;
$$->arrays = $2;
$$->semantic = $3.semantic;
$$->reg_reservation = $3.reg_reservation;
}
state:
any_identifier '=' expr ';'
{
vkd3d_free($1);
hlsl_free_instr_list($3);
}
state_block_start:
%empty
{
ctx->in_state_block = 1;
}
state_block:
%empty
| state_block state
variable_def:
variable_decl
| variable_decl '=' complex_initializer
{
$$ = $1;
$$->initializer = $3;
}
| variable_decl '{' state_block_start state_block '}'
{
$$ = $1;
ctx->in_state_block = 0;
}
arrays:
%empty
{
$$.sizes = NULL;
$$.count = 0;
}
| '[' expr ']' arrays
{
unsigned int size = evaluate_static_expression(node_from_list($2));
uint32_t *new_array;
destroy_instr_list($2);
$$ = $4;
if (!size)
{
hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_INVALID_SIZE,
"Array size is not a positive integer constant.");
vkd3d_free($$.sizes);
YYABORT;
}
if (size > 65536)
{
hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_INVALID_SIZE,
"Array size %u is not between 1 and 65536.", size);
vkd3d_free($$.sizes);
YYABORT;
}
if (!(new_array = hlsl_realloc(ctx, $$.sizes, ($$.count + 1) * sizeof(*new_array))))
{
vkd3d_free($$.sizes);
YYABORT;
}
$$.sizes = new_array;
$$.sizes[$$.count++] = size;
}
| '[' ']' arrays
{
uint32_t *new_array;
$$ = $3;
if (!(new_array = hlsl_realloc(ctx, $$.sizes, ($$.count + 1) * sizeof(*new_array))))
{
vkd3d_free($$.sizes);
YYABORT;
}
$$.sizes = new_array;
$$.sizes[$$.count++] = HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT;
}
var_modifiers:
%empty
{
$$ = 0;
}
| KW_EXTERN var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_STORAGE_EXTERN, @1);
}
| KW_NOINTERPOLATION var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_STORAGE_NOINTERPOLATION, @1);
}
| KW_PRECISE var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_MODIFIER_PRECISE, @1);
}
| KW_SHARED var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_STORAGE_SHARED, @1);
}
| KW_GROUPSHARED var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_STORAGE_GROUPSHARED, @1);
}
| KW_STATIC var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_STORAGE_STATIC, @1);
}
| KW_UNIFORM var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_STORAGE_UNIFORM, @1);
}
| KW_VOLATILE var_modifiers
{
$$ = add_modifiers(ctx, $2, HLSL_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);
}
complex_initializer:
initializer_expr
{
$$.args_count = 1;
if (!($$.args = hlsl_alloc(ctx, sizeof(*$$.args))))
{
destroy_instr_list($1);
YYABORT;
}
$$.args[0] = node_from_list($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];
list_move_tail($$.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_instr_list($1);
YYABORT;
}
$$.args[0] = node_from_list($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_instr_list($3);
YYABORT;
}
$$.args = new_args;
$$.args[$$.args_count++] = node_from_list($3);
list_move_tail($$.instrs, $3);
vkd3d_free($3);
}
boolean:
KW_TRUE
{
$$ = true;
}
| KW_FALSE
{
$$ = false;
}
statement_list:
statement
| statement_list statement
{
$$ = $1;
list_move_tail($$, $2);
vkd3d_free($2);
}
statement:
declaration_statement
| expr_statement
| compound_statement
| jump_statement
| selection_statement
| loop_statement
jump_statement:
KW_RETURN expr ';'
{
if (!add_return(ctx, $2, node_from_list($2), @1))
YYABORT;
$$ = $2;
}
| KW_RETURN ';'
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
if (!add_return(ctx, $$, NULL, @1))
YYABORT;
}
selection_statement:
KW_IF '(' expr ')' if_body
{
struct hlsl_ir_node *condition = node_from_list($3);
struct hlsl_ir_if *instr;
if (!(instr = hlsl_new_if(ctx, condition, @1)))
YYABORT;
list_move_tail(&instr->then_instrs.instrs, $5.then_instrs);
if ($5.else_instrs)
list_move_tail(&instr->else_instrs.instrs, $5.else_instrs);
vkd3d_free($5.then_instrs);
vkd3d_free($5.else_instrs);
if (condition->data_type->dimx > 1 || condition->data_type->dimy > 1)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, condition->data_type)))
hlsl_error(ctx, &instr->node.loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"if condition type %s is not scalar.", string->buffer);
hlsl_release_string_buffer(ctx, string);
}
$$ = $3;
list_add_tail($$, &instr->node.entry);
}
if_body:
statement
{
$$.then_instrs = $1;
$$.else_instrs = NULL;
}
| statement KW_ELSE statement
{
$$.then_instrs = $1;
$$.else_instrs = $3;
}
loop_statement:
KW_WHILE '(' expr ')' statement
{
$$ = create_loop(ctx, LOOP_WHILE, NULL, $3, NULL, $5, @1);
}
| KW_DO statement KW_WHILE '(' expr ')' ';'
{
$$ = create_loop(ctx, LOOP_DO_WHILE, NULL, $5, NULL, $2, @1);
}
| KW_FOR '(' scope_start expr_statement expr_statement expr_optional ')' statement
{
$$ = create_loop(ctx, LOOP_FOR, $4, $5, $6, $8, @1);
hlsl_pop_scope(ctx);
}
| KW_FOR '(' scope_start declaration expr_statement expr_optional ')' statement
{
$$ = create_loop(ctx, LOOP_FOR, $4, $5, $6, $8, @1);
hlsl_pop_scope(ctx);
}
expr_optional:
%empty
{
if (!($$ = make_empty_list(ctx)))
YYABORT;
}
| expr
expr_statement:
expr_optional ';'
{
$$ = $1;
}
func_arguments:
%empty
{
$$.args = NULL;
$$.args_count = 0;
if (!($$.instrs = make_empty_list(ctx)))
YYABORT;
$$.braces = false;
}
| initializer_expr_list
primary_expr:
C_FLOAT
{
struct hlsl_ir_constant *c;
if (!(c = hlsl_new_float_constant(ctx, $1, &@1)))
YYABORT;
if (!($$ = make_list(ctx, &c->node)))
YYABORT;
}
| C_INTEGER
{
struct hlsl_ir_constant *c;
if (!(c = hlsl_new_int_constant(ctx, $1, &@1)))
YYABORT;
if (!($$ = make_list(ctx, &c->node)))
YYABORT;
}
| boolean
{
struct hlsl_ir_constant *c;
if (!(c = hlsl_new_bool_constant(ctx, $1, &@1)))
YYABORT;
if (!($$ = make_list(ctx, &c->node)))
{
hlsl_free_instr(&c->node);
YYABORT;
}
}
| VAR_IDENTIFIER
{
struct hlsl_ir_load *load;
struct hlsl_ir_var *var;
if (!(var = hlsl_get_var(ctx->cur_scope, $1)))
{
hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED, "Variable \"%s\" is not defined.", $1);
YYABORT;
}
if (!(load = hlsl_new_var_load(ctx, var, @1)))
YYABORT;
if (!($$ = make_list(ctx, &load->node)))
YYABORT;
}
| '(' expr ')'
{
$$ = $2;
}
| var_identifier '(' func_arguments ')'
{
if (!($$ = add_call(ctx, $1, &$3, &@1)))
{
vkd3d_free($1);
YYABORT;
}
vkd3d_free($1);
}
| NEW_IDENTIFIER
{
if (ctx->in_state_block)
{
struct hlsl_ir_load *load;
struct hlsl_ir_var *var;
if (!(var = hlsl_new_synthetic_var(ctx, "state_block_expr",
hlsl_get_scalar_type(ctx, HLSL_TYPE_INT), &@1)))
YYABORT;
if (!(load = hlsl_new_var_load(ctx, var, @1)))
YYABORT;
if (!($$ = make_list(ctx, &load->node)))
YYABORT;
}
else
{
hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_NOT_DEFINED, "Identifier \"%s\" is not declared.", $1);
YYABORT;
}
}
postfix_expr:
primary_expr
| postfix_expr OP_INC
{
if (!add_increment(ctx, $1, false, true, @2))
{
destroy_instr_list($1);
YYABORT;
}
$$ = $1;
}
| postfix_expr OP_DEC
{
if (!add_increment(ctx, $1, true, true, @2))
{
destroy_instr_list($1);
YYABORT;
}
$$ = $1;
}
| postfix_expr '.' any_identifier
{
struct hlsl_ir_node *node = node_from_list($1);
if (node->data_type->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);
YYABORT;
}
field_idx = field - type->e.record.fields;
if (!add_record_access(ctx, $1, node, field_idx, @2))
YYABORT;
$$ = $1;
}
else if (node->data_type->class <= HLSL_CLASS_LAST_NUMERIC)
{
struct hlsl_ir_swizzle *swizzle;
if (!(swizzle = get_swizzle(ctx, node, $3, &@3)))
{
hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Invalid swizzle \"%s\".", $3);
YYABORT;
}
list_add_tail($1, &swizzle->node.entry);
$$ = $1;
}
else
{
hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Invalid subscript \"%s\".", $3);
YYABORT;
}
}
| postfix_expr '[' expr ']'
{
struct hlsl_ir_node *array = node_from_list($1), *index = node_from_list($3);
list_move_head($1, $3);
vkd3d_free($3);
if (!add_array_access(ctx, $1, array, index, &@2))
{
destroy_instr_list($1);
YYABORT;
}
$$ = $1;
}
/* var_modifiers is necessary to avoid shift/reduce conflicts. */
| var_modifiers type '(' initializer_expr_list ')'
{
if ($1)
{
hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_MODIFIER,
"Modifiers are not allowed on constructors.");
free_parse_initializer(&$4);
YYABORT;
}
if ($2->class > HLSL_CLASS_LAST_NUMERIC)
{
struct vkd3d_string_buffer *string;
if ((string = hlsl_type_to_string(ctx, $2)))
hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
"Constructor data type %s is not numeric.", string->buffer);
hlsl_release_string_buffer(ctx, string);
free_parse_initializer(&$4);
YYABORT;
}
if ($2->dimx * $2->dimy != initializer_size(&$4))
{
hlsl_error(ctx, &@4, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
"Expected %u components in constructor, but got %u.",
$2->dimx * $2->dimy, initializer_size(&$4));
free_parse_initializer(&$4);
YYABORT;
}
if (!($$ = add_constructor(ctx, $2, &$4, @2)))
{
free_parse_initializer(&$4);
YYABORT;
}
}
| postfix_expr '.' any_identifier '(' func_arguments ')'
{
struct hlsl_ir_node *object = node_from_list($1);
list_move_tail($1, $5.instrs);
vkd3d_free($5.instrs);
if (!add_method_call(ctx, $1, object, $3, &$5, &@3))
{
hlsl_free_instr_list($1);
vkd3d_free($5.args);
YYABORT;
}
vkd3d_free($5.args);
$$ = $1;
}
unary_expr:
postfix_expr
| OP_INC unary_expr
{
if (!add_increment(ctx, $2, false, false, @1))
{
destroy_instr_list($2);
YYABORT;
}
$$ = $2;
}
| OP_DEC unary_expr
{
if (!add_increment(ctx, $2, true, false, @1))
{
destroy_instr_list($2);
YYABORT;
}
$$ = $2;
}
| '+' unary_expr
{
$$ = $2;
}
| '-' unary_expr
{
add_unary_arithmetic_expr(ctx, $2, HLSL_OP1_NEG, node_from_list($2), &@1);
$$ = $2;
}
| '~' unary_expr
{
add_unary_bitwise_expr(ctx, $2, HLSL_OP1_BIT_NOT, node_from_list($2), &@1);
$$ = $2;
}
| '!' unary_expr
{
add_unary_logical_expr(ctx, $2, HLSL_OP1_LOGIC_NOT, node_from_list($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_list($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_list($6), dst_type, &@3))
{
hlsl_free_instr_list($6);
YYABORT;
}
$$ = $6;
}
mul_expr:
unary_expr
| mul_expr '*' unary_expr
{
$$ = add_binary_arithmetic_expr_merge(ctx, $1, $3, HLSL_OP2_MUL, @2);
}
| mul_expr '/' unary_expr
{
$$ = add_binary_arithmetic_expr_merge(ctx, $1, $3, HLSL_OP2_DIV, @2);
}
| mul_expr '%' unary_expr
{
$$ = add_binary_arithmetic_expr_merge(ctx, $1, $3, HLSL_OP2_MOD, @2);
}
add_expr:
mul_expr
| add_expr '+' mul_expr
{
$$ = add_binary_arithmetic_expr_merge(ctx, $1, $3, HLSL_OP2_ADD, @2);
}
| add_expr '-' mul_expr
{
struct hlsl_ir_node *neg;
if (!(neg = hlsl_new_unary_expr(ctx, HLSL_OP1_NEG, node_from_list($3), @2)))
YYABORT;
list_add_tail($3, &neg->entry);
$$ = add_binary_arithmetic_expr_merge(ctx, $1, $3, HLSL_OP2_ADD, @2);
}
shift_expr:
add_expr
| shift_expr OP_LEFTSHIFT add_expr
{
$$ = add_binary_shift_expr_merge(ctx, $1, $3, HLSL_OP2_LSHIFT, &@2);
}
| shift_expr OP_RIGHTSHIFT add_expr
{
$$ = add_binary_shift_expr_merge(ctx, $1, $3, HLSL_OP2_RSHIFT, &@2);
}
relational_expr:
shift_expr
| relational_expr '<' shift_expr
{
$$ = add_binary_comparison_expr_merge(ctx, $1, $3, HLSL_OP2_LESS, @2);
}
| relational_expr '>' shift_expr
{
$$ = add_binary_comparison_expr_merge(ctx, $3, $1, HLSL_OP2_LESS, @2);
}
| relational_expr OP_LE shift_expr
{
$$ = add_binary_comparison_expr_merge(ctx, $3, $1, HLSL_OP2_GEQUAL, @2);
}
| relational_expr OP_GE shift_expr
{
$$ = add_binary_comparison_expr_merge(ctx, $1, $3, HLSL_OP2_GEQUAL, @2);
}
equality_expr:
relational_expr
| equality_expr OP_EQ relational_expr
{
$$ = add_binary_comparison_expr_merge(ctx, $1, $3, HLSL_OP2_EQUAL, @2);
}
| equality_expr OP_NE relational_expr
{
$$ = add_binary_comparison_expr_merge(ctx, $1, $3, HLSL_OP2_NEQUAL, @2);
}
bitand_expr:
equality_expr
| bitand_expr '&' equality_expr
{
$$ = add_binary_bitwise_expr_merge(ctx, $1, $3, HLSL_OP2_BIT_AND, &@2);
}
bitxor_expr:
bitand_expr
| bitxor_expr '^' bitand_expr
{
$$ = add_binary_bitwise_expr_merge(ctx, $1, $3, HLSL_OP2_BIT_XOR, &@2);
}
bitor_expr:
bitxor_expr
| bitor_expr '|' bitxor_expr
{
$$ = add_binary_bitwise_expr_merge(ctx, $1, $3, HLSL_OP2_BIT_OR, &@2);
}
logicand_expr:
bitor_expr
| logicand_expr OP_AND bitor_expr
{
$$ = add_binary_logical_expr_merge(ctx, $1, $3, HLSL_OP2_LOGIC_AND, &@2);
}
logicor_expr:
logicand_expr
| logicor_expr OP_OR logicand_expr
{
$$ = add_binary_logical_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_list($1), *first = node_from_list($3), *second = node_from_list($5);
list_move_tail($1, $3);
list_move_tail($1, $5);
vkd3d_free($3);
vkd3d_free($5);
if (!hlsl_add_conditional(ctx, $1, cond, first, second))
YYABORT;
$$ = $1;
}
assignment_expr:
conditional_expr
| unary_expr assign_op assignment_expr
{
struct hlsl_ir_node *lhs = node_from_list($1), *rhs = node_from_list($3);
if (lhs->data_type->modifiers & HLSL_MODIFIER_CONST)
{
hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_MODIFIES_CONST, "Statement modifies a const expression.");
YYABORT;
}
list_move_tail($3, $1);
vkd3d_free($1);
if (!add_assignment(ctx, $3, lhs, $2, rhs))
YYABORT;
$$ = $3;
}
assign_op:
'='
{
$$ = ASSIGN_OP_ASSIGN;
}
| OP_ADDASSIGN
{
$$ = ASSIGN_OP_ADD;
}
| OP_SUBASSIGN
{
$$ = ASSIGN_OP_SUB;
}
| OP_MULASSIGN
{
$$ = ASSIGN_OP_MUL;
}
| OP_DIVASSIGN
{
$$ = ASSIGN_OP_DIV;
}
| OP_MODASSIGN
{
$$ = ASSIGN_OP_MOD;
}
| OP_LEFTSHIFTASSIGN
{
$$ = ASSIGN_OP_LSHIFT;
}
| OP_RIGHTSHIFTASSIGN
{
$$ = ASSIGN_OP_RSHIFT;
}
| OP_ANDASSIGN
{
$$ = ASSIGN_OP_AND;
}
| OP_ORASSIGN
{
$$ = ASSIGN_OP_OR;
}
| OP_XORASSIGN
{
$$ = ASSIGN_OP_XOR;
}
expr:
assignment_expr
| expr ',' assignment_expr
{
$$ = $1;
list_move_tail($$, $3);
vkd3d_free($3);
}