-- v3: vkd3d-shader/hlsl: Partially implement static expressions evaluation.
From: Nikolay Sivov nsivov@codeweavers.com
--- Makefile.am | 1 + libs/vkd3d-shader/hlsl.h | 2 + libs/vkd3d-shader/hlsl.y | 100 ++++++++++++++----------- libs/vkd3d-shader/hlsl_codegen.c | 68 ++++++++--------- tests/hlsl-array-size-expr.shader_test | 40 ++++++++++ tests/hlsl-invalid.shader_test | 2 +- 6 files changed, 133 insertions(+), 80 deletions(-) create mode 100644 tests/hlsl-array-size-expr.shader_test
diff --git a/Makefile.am b/Makefile.am index 3f1ec171..2c1e3db5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,6 +71,7 @@ vkd3d_shader_tests = \ tests/frac.shader_test \ tests/function-return.shader_test \ tests/hlsl-array-dimension.shader_test \ + tests/hlsl-array-size-expr.shader_test \ tests/hlsl-attributes.shader_test \ tests/hlsl-bool-cast.shader_test \ tests/hlsl-clamp.shader_test \ diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 2ff18536..71154ed7 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -1160,6 +1160,8 @@ struct hlsl_reg hlsl_reg_from_deref(struct hlsl_ctx *ctx, const struct hlsl_dere
bool hlsl_fold_constant_exprs(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, void *context); bool hlsl_fold_constant_swizzles(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, void *context); +bool hlsl_transform_ir(struct hlsl_ctx *ctx, bool (*func)(struct hlsl_ctx *ctx, struct hlsl_ir_node *, void *), + struct hlsl_block *block, void *context);
bool hlsl_sm1_register_from_semantic(struct hlsl_ctx *ctx, const struct hlsl_semantic *semantic, bool output, D3DSHADER_PARAM_REGISTER_TYPE *type, unsigned int *reg); diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 10deda75..8691699b 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -1141,54 +1141,48 @@ static struct list *make_list(struct hlsl_ctx *ctx, struct hlsl_ir_node *node) return list; }
-static unsigned int evaluate_static_expression(struct hlsl_ir_node *node) +static unsigned int evaluate_static_expression_as_uint(struct hlsl_ctx *ctx, struct hlsl_block *block) { - if (node->data_type->class != HLSL_CLASS_SCALAR) - return 0; + struct hlsl_ir_constant *constant; + union hlsl_constant_value value; + struct hlsl_ir_node *node; + unsigned int ret = 0;
- switch (node->type) + while (hlsl_transform_ir(ctx, hlsl_fold_constant_exprs, block, NULL)); + + node = node_from_list(&block->instrs); + if (node->type == HLSL_IR_CONSTANT) { - case HLSL_IR_CONSTANT: - { - struct hlsl_ir_constant *constant = hlsl_ir_constant(node); - const union hlsl_constant_value *value = &constant->value[0]; + constant = hlsl_ir_constant(node); + 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(); - } + switch (constant->node.data_type->base_type) + { + case HLSL_TYPE_DOUBLE: + ret = value.d; + break; + case HLSL_TYPE_FLOAT: + case HLSL_TYPE_HALF: + ret = value.f; + break; + case HLSL_TYPE_INT: + ret = value.i; + break; + case HLSL_TYPE_UINT: + case HLSL_TYPE_BOOL: + ret = value.u; + break; + 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(); + } + else + { + hlsl_error(ctx, &node->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, + "Failed to evaluate constant expression %d.", node->type); }
- vkd3d_unreachable(); + return ret; }
static bool expr_compatible_data_types(struct hlsl_type *t1, struct hlsl_type *t2) @@ -4957,8 +4951,17 @@ type_no_void: } | texture_ms_type '<' type ',' shift_expr '>' { - unsigned int sample_count = evaluate_static_expression(node_from_list($5)); - destroy_instr_list($5); + unsigned int sample_count; + struct hlsl_block block; + + hlsl_block_init(&block); + list_move_tail(&block.instrs, $5); + + sample_count = evaluate_static_expression_as_uint(ctx, &block); + + hlsl_block_cleanup(&block); + + vkd3d_free($5);
$$ = hlsl_new_texture_type(ctx, $1, $3, sample_count); } @@ -5151,10 +5154,17 @@ arrays: } | '[' expr ']' arrays { - unsigned int size = evaluate_static_expression(node_from_list($2)); + struct hlsl_block block; uint32_t *new_array; + unsigned int size;
- destroy_instr_list($2); + hlsl_block_init(&block); + list_move_tail(&block.instrs, $2); + + size = evaluate_static_expression_as_uint(ctx, &block); + + hlsl_block_cleanup(&block); + vkd3d_free($2);
$$ = $4;
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 42f8ab3b..85ffbf82 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -447,7 +447,7 @@ static void append_output_var_copy(struct hlsl_ctx *ctx, struct list *instrs, st append_output_copy(ctx, instrs, load, var->storage_modifiers, &var->semantic); }
-static bool transform_ir(struct hlsl_ctx *ctx, bool (*func)(struct hlsl_ctx *ctx, struct hlsl_ir_node *, void *), +bool hlsl_transform_ir(struct hlsl_ctx *ctx, bool (*func)(struct hlsl_ctx *ctx, struct hlsl_ir_node *, void *), struct hlsl_block *block, void *context) { struct hlsl_ir_node *instr, *next; @@ -459,11 +459,11 @@ static bool transform_ir(struct hlsl_ctx *ctx, bool (*func)(struct hlsl_ctx *ctx { struct hlsl_ir_if *iff = hlsl_ir_if(instr);
- progress |= transform_ir(ctx, func, &iff->then_block, context); - progress |= transform_ir(ctx, func, &iff->else_block, context); + progress |= hlsl_transform_ir(ctx, func, &iff->then_block, context); + progress |= hlsl_transform_ir(ctx, func, &iff->else_block, context); } else if (instr->type == HLSL_IR_LOOP) - progress |= transform_ir(ctx, func, &hlsl_ir_loop(instr)->body, context); + progress |= hlsl_transform_ir(ctx, func, &hlsl_ir_loop(instr)->body, context);
progress |= func(ctx, instr, context); } @@ -506,7 +506,7 @@ static bool find_recursive_calls(struct hlsl_ctx *ctx, struct hlsl_ir_node *inst return false; call_ctx->backtrace[call_ctx->count++] = decl;
- transform_ir(ctx, find_recursive_calls, &decl->body, call_ctx); + hlsl_transform_ir(ctx, find_recursive_calls, &decl->body, call_ctx);
--call_ctx->count;
@@ -568,7 +568,7 @@ static bool lower_return(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *fun * the CF instruction, shove it into an if block, and then lower that if * block. * - * (We could return a "did we make progress" boolean like transform_ir() + * (We could return a "did we make progress" boolean like hlsl_transform_ir() * and run this pass multiple times, but we already know the only block * that still needs to be addressed, so there's not much point.) * @@ -3438,7 +3438,7 @@ int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry list_move_head(&body->instrs, &ctx->static_initializers);
memset(&recursive_call_ctx, 0, sizeof(recursive_call_ctx)); - transform_ir(ctx, find_recursive_calls, body, &recursive_call_ctx); + hlsl_transform_ir(ctx, find_recursive_calls, body, &recursive_call_ctx); vkd3d_free(recursive_call_ctx.backtrace);
/* Avoid going into an infinite loop when processing call instructions. @@ -3448,9 +3448,9 @@ int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry
lower_return(ctx, entry_func, body, false);
- while (transform_ir(ctx, lower_calls, body, NULL)); + while (hlsl_transform_ir(ctx, lower_calls, body, NULL));
- transform_ir(ctx, lower_index_loads, body, NULL); + hlsl_transform_ir(ctx, lower_index_loads, body, NULL);
LIST_FOR_EACH_ENTRY(var, &ctx->globals->vars, struct hlsl_ir_var, scope_entry) { @@ -3502,54 +3502,54 @@ int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry hlsl_error(ctx, &entry_func->loc, VKD3D_SHADER_ERROR_HLSL_MISSING_ATTRIBUTE, "Entry point "%s" is missing a [numthreads] attribute.", entry_func->func->name);
- transform_ir(ctx, lower_broadcasts, body, NULL); - while (transform_ir(ctx, fold_redundant_casts, body, NULL)); + hlsl_transform_ir(ctx, lower_broadcasts, body, NULL); + while (hlsl_transform_ir(ctx, fold_redundant_casts, body, NULL)); do { - progress = transform_ir(ctx, split_array_copies, body, NULL); - progress |= transform_ir(ctx, split_struct_copies, body, NULL); + progress = hlsl_transform_ir(ctx, split_array_copies, body, NULL); + progress |= hlsl_transform_ir(ctx, split_struct_copies, body, NULL); } while (progress); - transform_ir(ctx, split_matrix_copies, body, NULL); - - transform_ir(ctx, lower_narrowing_casts, body, NULL); - transform_ir(ctx, lower_casts_to_bool, body, NULL); - transform_ir(ctx, lower_int_division, body, NULL); - transform_ir(ctx, lower_int_modulus, body, NULL); - transform_ir(ctx, lower_int_abs, body, NULL); - transform_ir(ctx, lower_float_modulus, body, NULL); + hlsl_transform_ir(ctx, split_matrix_copies, body, NULL); + + hlsl_transform_ir(ctx, lower_narrowing_casts, body, NULL); + hlsl_transform_ir(ctx, lower_casts_to_bool, body, NULL); + hlsl_transform_ir(ctx, lower_int_division, body, NULL); + hlsl_transform_ir(ctx, lower_int_modulus, body, NULL); + hlsl_transform_ir(ctx, lower_int_abs, body, NULL); + hlsl_transform_ir(ctx, lower_float_modulus, body, NULL); do { - progress = transform_ir(ctx, hlsl_fold_constant_exprs, body, NULL); - progress |= transform_ir(ctx, hlsl_fold_constant_swizzles, body, NULL); + progress = hlsl_transform_ir(ctx, hlsl_fold_constant_exprs, body, NULL); + progress |= hlsl_transform_ir(ctx, hlsl_fold_constant_swizzles, body, NULL); progress |= copy_propagation_execute(ctx, body); - progress |= transform_ir(ctx, fold_swizzle_chains, body, NULL); - progress |= transform_ir(ctx, remove_trivial_swizzles, body, NULL); + progress |= hlsl_transform_ir(ctx, fold_swizzle_chains, body, NULL); + progress |= hlsl_transform_ir(ctx, remove_trivial_swizzles, body, NULL); } while (progress);
if (profile->major_version < 4) { - transform_ir(ctx, lower_division, body, NULL); - transform_ir(ctx, lower_sqrt, body, NULL); - transform_ir(ctx, lower_dot, body, NULL); - transform_ir(ctx, lower_round, body, NULL); + hlsl_transform_ir(ctx, lower_division, body, NULL); + hlsl_transform_ir(ctx, lower_sqrt, body, NULL); + hlsl_transform_ir(ctx, lower_dot, body, NULL); + hlsl_transform_ir(ctx, lower_round, body, NULL); }
if (profile->major_version < 2) { - transform_ir(ctx, lower_abs, body, NULL); + hlsl_transform_ir(ctx, lower_abs, body, NULL); }
- transform_ir(ctx, validate_static_object_references, body, NULL); + hlsl_transform_ir(ctx, validate_static_object_references, body, NULL);
/* TODO: move forward, remove when no longer needed */ - transform_ir(ctx, transform_deref_paths_into_offsets, body, NULL); - while (transform_ir(ctx, hlsl_fold_constant_exprs, body, NULL)); + hlsl_transform_ir(ctx, transform_deref_paths_into_offsets, body, NULL); + while (hlsl_transform_ir(ctx, hlsl_fold_constant_exprs, body, NULL));
do compute_liveness(ctx, entry_func); - while (transform_ir(ctx, dce, body, NULL)); + while (hlsl_transform_ir(ctx, dce, body, NULL));
compute_liveness(ctx, entry_func);
diff --git a/tests/hlsl-array-size-expr.shader_test b/tests/hlsl-array-size-expr.shader_test new file mode 100644 index 00000000..0d64462b --- /dev/null +++ b/tests/hlsl-array-size-expr.shader_test @@ -0,0 +1,40 @@ +[pixel shader] +static const float4 array_st[(2 + 1) * 2] = { + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44, + 51, 52, 53, 54, + 61, 62, 63, 64, +}; + +float4 main() : SV_TARGET +{ + return array_st[1]; +} + +[test] +draw quad +probe all rgba (21, 22, 23, 24) + + +[pixel shader] +static const float4 array_st[2*2][1+1] = { + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44, + 51, 52, 53, 54, + 61, 62, 63, 64, + 71, 72, 73, 74, + 81, 82, 83, 84, +}; + +float4 main() : SV_TARGET +{ + return array_st[2][1]; +} + +[test] +draw quad +probe all rgba (61, 62, 63, 64) diff --git a/tests/hlsl-invalid.shader_test b/tests/hlsl-invalid.shader_test index ad062652..61033811 100644 --- a/tests/hlsl-invalid.shader_test +++ b/tests/hlsl-invalid.shader_test @@ -72,7 +72,7 @@ float4 main() : sv_target return 0; }
-[pixel shader fail] +[pixel shader fail todo] float4 main() : sv_target { int x;
This merge request was approved by Francisco Casas.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/hlsl.y:
}
switch (constant->node.data_type->base_type)
{
case HLSL_TYPE_DOUBLE:
ret = value.d;
break;
case HLSL_TYPE_FLOAT:
case HLSL_TYPE_HALF:
ret = value.f;
break;
case HLSL_TYPE_INT:
ret = value.i;
break;
case HLSL_TYPE_UINT:
case HLSL_TYPE_BOOL:
ret = value.u;
That's wrong, `true` is represented as `0xffffffff`, but must be converted to `1`. Rather than doing "by hand" the final cast, I'd rather use `add_implicit_conversion()` to `uint` and then unconditionally read `value.u`. This resuses the code that we have already, displays errors and warnings as appropriate and maybe even make this function shorter.