-- v2: 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 | 123 ++++++++++++------------- libs/vkd3d-shader/hlsl_codegen.c | 2 +- tests/hlsl-array-size-expr.shader_test | 37 ++++++++ tests/hlsl-invalid.shader_test | 2 +- 6 files changed, 102 insertions(+), 65 deletions(-) create mode 100644 tests/hlsl-array-size-expr.shader_test
diff --git a/Makefile.am b/Makefile.am index 549354b4..64a1b914 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 974252d4..1f0fe66c 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -1159,6 +1159,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 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 194d21f4..6a6215f4 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -1141,54 +1141,69 @@ 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 enum hlsl_base_type expr_common_base_type(enum hlsl_base_type t1, enum hlsl_base_type t2) { - if (node->data_type->class != HLSL_CLASS_SCALAR) - return 0; + 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; +}
- 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]; +static unsigned int evaluate_static_expression_as_uint(struct hlsl_ctx *ctx, struct hlsl_ir_node *node) +{ + struct hlsl_ir_constant *constant; + union hlsl_constant_value value; + struct hlsl_block block; + unsigned int ret = 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(); - } - } + hlsl_block_init(&block); + hlsl_block_add_instr(&block, node); + transform_ir(ctx, hlsl_fold_constant_exprs, &block, NULL);
- 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; + node = node_from_list(&block.instrs);
- 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(); + if (node->type == HLSL_IR_CONSTANT) + { + constant = hlsl_ir_constant(node); + value = constant->value[0]; + + switch (constant->node.data_type->base_type) + { + case HLSL_TYPE_DOUBLE: + ret = value.d; + break; + case HLSL_TYPE_FLOAT: + ret = value.f; + break; + case HLSL_TYPE_INT: + ret = value.i; + break; + case HLSL_TYPE_UINT: + ret = value.u; + break; + default: + vkd3d_unreachable(); + } } + else + { + hlsl_error(ctx, &node->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, + "Failed to evaluate constant expression %d.", node->type); + } + + hlsl_block_cleanup(&block);
- vkd3d_unreachable(); + return ret; }
static bool expr_compatible_data_types(struct hlsl_type *t1, struct hlsl_type *t2) @@ -1225,24 +1240,6 @@ static bool expr_compatible_data_types(struct hlsl_type *t1, struct hlsl_type *t 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) { @@ -4917,8 +4914,8 @@ 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 = evaluate_static_expression_as_uint(ctx, node_from_list($5)); + vkd3d_free($5);
$$ = hlsl_new_texture_type(ctx, $1, $3, sample_count); } @@ -5111,10 +5108,10 @@ arrays: } | '[' expr ']' arrays { - unsigned int size = evaluate_static_expression(node_from_list($2)); + unsigned int size = evaluate_static_expression_as_uint(ctx, node_from_list($2)); uint32_t *new_array;
- destroy_instr_list($2); + vkd3d_free($2);
$$ = $4;
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 42f8ab3b..fc115636 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 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; diff --git a/tests/hlsl-array-size-expr.shader_test b/tests/hlsl-array-size-expr.shader_test new file mode 100644 index 00000000..eaa1d2cd --- /dev/null +++ b/tests/hlsl-array-size-expr.shader_test @@ -0,0 +1,37 @@ +[pixel shader] +static const float4 array_st[2 + 1] = { + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34 +}; + +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;
Changed to reuse hlsl_fold_constant_exprs(). I had to mark one existing test as todo now, because [(x = 2)] suddenly started working, when it shouldn't. Probably we could simply scan for non-constants and reject such expressions. I'll deal with that once general approach is approved.
Zebediah Figura (@zfigura) commented about libs/vkd3d-shader/hlsl.h:
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 transform_ir(struct hlsl_ctx *ctx, bool (*func)(struct hlsl_ctx *ctx, struct hlsl_ir_node *, void *),
struct hlsl_block *block, void *context);
If we're going to make that function non-static it should really have a hlsl_ prefix. Honestly it may be prettier just to add a wrapper, though, seeing as we may need to do multiple different passes.
Zebediah Figura (@zfigura) commented about libs/vkd3d-shader/hlsl.y:
- 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;
-}
I don't think you need to move this anymore, right?
Zebediah Figura (@zfigura) commented about libs/vkd3d-shader/hlsl.y:
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();
}
}
- hlsl_block_init(&block);
- hlsl_block_add_instr(&block, node);
This instruction already belongs to a block; this'll corrupt that list.
Separately, this will only fold the one expression.
I believe both problems will be fixed if you just transform the whole block, and I don't see any reason not to?
Zebediah Figura (@zfigura) 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:
ret = value.f;
break;
case HLSL_TYPE_INT:
ret = value.i;
break;
case HLSL_TYPE_UINT:
ret = value.u;
break;
default:
vkd3d_unreachable();
BOOL or HALF are possible, aren't they?