Signed-off-by: Nikolay Sivov nsivov@codeweavers.com
-- v25: vkd3d-shader/tpf: Write out 'switch' statements. vkd3d-shader/hlsl: Add a pass to normalize switch cases blocks. vkd3d-shader/hlsl: Add a pass to remove unreachable code. vkd3d-shader/hlsl: Add copy propagation logic for switches. vkd3d-shader/hlsl: Validate break/continue context. vkd3d-shader/hlsl: Check for duplicate case statements. vkd3d-shader/hlsl: Add initial support for parsing 'switch' statements. tests: Add some tests for the 'switch' statements.
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- Makefile.am | 1 + tests/hlsl/switch.shader_test | 552 ++++++++++++++++++++++++++++++++++ 2 files changed, 553 insertions(+) create mode 100644 tests/hlsl/switch.shader_test
diff --git a/Makefile.am b/Makefile.am index 2821ddc6..fcb84d45 100644 --- a/Makefile.am +++ b/Makefile.am @@ -166,6 +166,7 @@ vkd3d_shader_tests = \ tests/hlsl/struct-array.shader_test \ tests/hlsl/struct-assignment.shader_test \ tests/hlsl/struct-semantics.shader_test \ + tests/hlsl/switch.shader_test \ tests/hlsl/swizzle-constant-prop.shader_test \ tests/hlsl/swizzle-matrix.shader_test \ tests/hlsl/swizzles.shader_test \ diff --git a/tests/hlsl/switch.shader_test b/tests/hlsl/switch.shader_test new file mode 100644 index 00000000..aab12485 --- /dev/null +++ b/tests/hlsl/switch.shader_test @@ -0,0 +1,552 @@ +[require] +shader model >= 4.0 + +[pixel shader todo] +uint4 v; + +float4 main() : sv_target +{ + switch (v.x) + { + case 0: + return 3.0; + case 1: + return 4.0; + default: + return 5.0; + } +} + +[test] +uniform 0 uint4 3 0 0 0 +todo draw quad +probe all rgba (5.0, 5.0, 5.0, 5.0) +uniform 0 uint4 1 0 0 0 +todo draw quad +probe all rgba (4.0, 4.0, 4.0, 4.0) +uniform 0 uint4 0 0 0 0 +todo draw quad +probe all rgba (3.0, 3.0, 3.0, 3.0) + +% falling through is only supported for empty case statements +[pixel shader todo] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0, 2.0, 3.0, 4.0}; + + switch (v.x) + { + case 0: + case 1: + c.x += 0.1f; + break; + } + + return c; +} + +[test] +uniform 0 uint4 2 0 0 0 +todo draw quad +probe all rgba (1.0, 2.0, 3.0, 4.0) +uniform 0 uint4 1 0 0 0 +todo draw quad +probe all rgba (1.1, 2.0, 3.0, 4.0) +uniform 0 uint4 0 0 0 0 +todo draw quad +probe all rgba (1.1, 2.0, 3.0, 4.0) + +% case value evaluation +[pixel shader todo] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0, 2.0, 3.0, 4.0}; + + switch (v.x) + { + case 1+1: + c += 0.1f; + break; + case 0: + c += 0.2f; + break; + } + + return c; +} + +[test] +uniform 0 uint4 2 0 0 0 +todo draw quad +probe all rgba (1.1, 2.1, 3.1, 4.1) +uniform 0 uint4 1 0 0 0 +todo draw quad +probe all rgba (1.0, 2.0, 3.0, 4.0) + +% floats are accepted +[pixel shader todo fail(sm>=6)] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0, 2.0, 3.0, 4.0}; + + switch (v.x) + { + case 2.1f: + c += 0.1f; + break; + case 0.9f: + c += 0.2f; + break; + } + + return c; +} + +[test] +uniform 0 uint4 2 0 0 0 +todo draw quad +probe all rgba (1.1, 2.1, 3.1, 4.1) +uniform 0 uint4 1 0 0 0 +todo draw quad +probe all rgba (1.0, 2.0, 3.0, 4.0) + +[pixel shader todo fail(sm>=6)] +float4 v; + +float4 main() : sv_target +{ + float4 c = {1.0, 2.0, 3.0, 4.0}; + + switch (v.x) + { + case 2.1f: + c += 0.1f; + break; + case 0.9f: + c += 0.2f; + break; + } + + return c; +} + +[test] +uniform 0 float4 2.0 0.0 0.0 0.0 +todo draw quad +probe all rgba (1.1, 2.1, 3.1, 4.1) +uniform 0 float4 1.0 0.0 0.0 0.0 +todo draw quad +probe all rgba (1.0, 2.0, 3.0, 4.0) + +[pixel shader fail] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0, 2.0, 3.0, 4.0}; + uint a = 1; + + switch (v.x) + { + case 1+a: + c += 0.1f; + break; + case 0: + c += 0.2f; + break; + } + + return c; +} + +% duplicate cases +[pixel shader fail] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0, 2.0, 3.0, 4.0}; + + switch (v.x) + { + case 2: + c += 0.1f; + break; + case 1+1: + c += 0.2f; + break; + } + + return c; +} + +% multiple default cases +[pixel shader fail] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0, 2.0, 3.0, 4.0}; + + switch (v.x) + { + default: + case 2: + c += 0.1f; + break; + case 1: + c += 0.2f; + break; + default: + break; + } + + return c; +} + +% unterminated cases +[pixel shader fail(sm<6)] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0, 2.0, 3.0, 4.0}; + + switch (v.x) + { + case 0: + c += 0.1f; + case 1: + c += 0.2f; + break; + } + + return c; +} + +[pixel shader fail] +uint4 v; + +float4 main() : sv_target +{ + switch (v.x) + { + case 0: + return 3.0; + case 1: + return 4.0; + case 2: + } + return 0.0; +} + +[pixel shader fail] +uint4 v; + +float4 main() : sv_target +{ + switch (v.x) + { + case 0: + return 3.0; + case 1: + return 4.0; + default: + } + return 0.0; +} + +[pixel shader fail(sm<6)] +uint4 v; + +float4 main() : sv_target +{ + switch (v.x) + { + case 0: + return 3.0; + case 1: + return 4.0; + default: + discard; + } + return 0.0; +} + +[pixel shader fail(sm<6)] +uint4 v; + +float4 main() : sv_target +{ + switch (v.x) + { + case 0: + return 3.0; + case 1: + return 4.0; + case 2: + discard; + } + return 0.0; +} + +[pixel shader fail(sm<6)] +uint4 v; + +float4 main() : sv_target +{ + switch (v.x) + { + case 0: + discard; + case 1: + return 4.0; + } + return 0.0; +} + +% more complicated breaks +[pixel shader todo] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0, 2.0, 3.0, 4.0}; + + switch (v.x) + { + case 2: + c += 0.1f; + if (true) break; + c = 9.0f; + case 1: + if (false) break; + c += 0.2f; + break; + default: + case 0: + break; + } + + return c; +} + +[test] +uniform 0 uint4 2 0 0 0 +todo draw quad +probe all rgba (1.1, 2.1, 3.1, 4.1) +uniform 0 uint4 1 0 0 0 +todo draw quad +probe all rgba (1.2, 2.2, 3.2, 4.2) +uniform 0 uint4 0 0 0 0 +todo draw quad +probe all rgba (1.0, 2.0, 3.0, 4.0) + +% switch breaks within a loop +[pixel shader todo] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0f, 2.0f, 3.0f, 4.0f}; + + for (int i = 0; i < 4; ++i) + { + switch (v.x) + { + case 2: + c += 1.0f; + break; + case 1: + c -= 1.0f; + break; + default: + case 0: + break; + } + } + + return c; +} + +[test] +uniform 0 uint4 2 0 0 0 +todo draw quad +probe all rgba (5.0, 6.0, 7.0, 8.0) + +% default case placement +[pixel shader todo] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0f, 2.0f, 3.0f, 4.0f}; + + switch (v.x) + { + case 2: + c += 1.0f; + break; + case 1: + c -= 1.0f; + break; + case 3: + default: + case 0: + c += 3.0f; + break; + } + + return c; +} + +[test] +uniform 0 uint4 0 0 0 0 +todo draw quad +probe all rgba (4.0, 5.0, 6.0, 7.0) +uniform 0 uint4 2 0 0 0 +todo draw quad +probe all rgba (2.0, 3.0, 4.0, 5.0) +uniform 0 uint4 3 0 0 0 +todo draw quad +probe all rgba (4.0, 5.0, 6.0, 7.0) + +[pixel shader todo] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0f, 2.0f, 3.0f, 4.0f}; + + switch (v.x) + { + case 2: + c += 1.0f; + break; + case 1: + c -= 1.0f; + break; + case 3: + default: + break; + case 0: + c += 3.0f; + break; + } + + return c; +} + +[test] +uniform 0 uint4 3 0 0 0 +todo draw quad +probe all rgba (1.0, 2.0, 3.0, 4.0) +uniform 0 uint4 0 0 0 0 +todo draw quad +probe all rgba (4.0, 5.0, 6.0, 7.0) +uniform 0 uint4 5 0 0 0 +todo draw quad +probe all rgba (1.0, 2.0, 3.0, 4.0) + +% 'continue' is not supported in switches +[pixel shader fail(sm<6)] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0, 2.0, 3.0, 4.0}; + uint i, j; + + for (i = 0; i < v.z; i++) + { + switch (v.x) + { + case 0: + c += 0.1f; + continue; + break; + case 1: + c += 0.2f; + break; + } + } + + return c; +} + +[pixel shader todo] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0, 2.0, 3.0, 4.0}; + uint i, j; + + for (i = 0; i < v.z; i++) + { + switch (v.x) + { + case 0: + for (j = 0; j < v.z; j++) + { + c += 1.0f; + if (v.w) + continue; + } + break; + case 1: + c += 2.0f; + break; + } + } + + return c; +} + +[test] +uniform 0 uint4 0 0 3 1 +todo draw quad +probe all rgba (10.0, 11.0, 12.0, 13.0) +uniform 0 uint4 1 0 3 1 +todo draw quad +probe all rgba (7.0, 8.0, 9.0, 10.0) + +% return from a switch nested in a loop +[pixel shader todo] +uint4 v; + +float4 main() : sv_target +{ + float4 c = {1.0, 2.0, 3.0, 4.0}; + uint i, j; + + for (i = 0; i < v.z; i++) + { + switch (v.x) + { + case 0: + c += 1.0f; + break; + case 1: + c += 2.0f; + return c; + } + c += 100.0f; + } + + return c; +} + +[test] +uniform 0 uint4 0 0 3 1 +todo draw quad +probe all rgba (304.0, 305.0, 306.0, 307.0) +uniform 0 uint4 1 0 3 1 +todo draw quad +probe all rgba (3.0, 4.0, 5.0, 6.0)
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- libs/vkd3d-shader/hlsl.c | 132 +++++++++++++++++++++++++++++++ libs/vkd3d-shader/hlsl.h | 30 +++++++ libs/vkd3d-shader/hlsl.l | 4 +- libs/vkd3d-shader/hlsl.y | 117 +++++++++++++++++++++++++++ libs/vkd3d-shader/hlsl_codegen.c | 81 +++++++++++++++++++ tests/hlsl/switch.shader_test | 12 +-- 6 files changed, 369 insertions(+), 7 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index 2d383bc2..29461e08 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -1343,6 +1343,40 @@ struct hlsl_ir_node *hlsl_new_if(struct hlsl_ctx *ctx, struct hlsl_ir_node *cond return &iff->node; }
+struct hlsl_ir_switch_case *hlsl_new_switch_case(struct hlsl_ctx *ctx, unsigned int value, + bool is_default, struct hlsl_block *body, const struct vkd3d_shader_location *loc) +{ + struct hlsl_ir_switch_case *c; + + if (!(c = hlsl_alloc(ctx, sizeof(*c)))) + return NULL; + + c->value = value; + c->is_default = is_default; + hlsl_block_init(&c->body); + if (body) + hlsl_block_add_block(&c->body, body); + c->loc = *loc; + + return c; +} + +struct hlsl_ir_node *hlsl_new_switch(struct hlsl_ctx *ctx, struct hlsl_ir_node *selector, + struct list *cases, const struct vkd3d_shader_location *loc) +{ + struct hlsl_ir_switch *s; + + if (!(s = hlsl_alloc(ctx, sizeof(*s)))) + return NULL; + init_node(&s->node, HLSL_IR_SWITCH, NULL, loc); + hlsl_src_from_node(&s->selector, selector); + list_init(&s->cases); + if (cases) + list_move_head(&s->cases, cases); + + return &s->node; +} + struct hlsl_ir_load *hlsl_new_load_index(struct hlsl_ctx *ctx, const struct hlsl_deref *deref, struct hlsl_ir_node *idx, const struct vkd3d_shader_location *loc) { @@ -1805,6 +1839,58 @@ static struct hlsl_ir_node *clone_index(struct hlsl_ctx *ctx, struct clone_instr return dst; }
+void hlsl_free_ir_switch_case(struct hlsl_ir_switch_case *c) +{ + hlsl_block_cleanup(&c->body); + list_remove(&c->entry); + vkd3d_free(c); +} + +void hlsl_cleanup_ir_switch_cases(struct list *cases) +{ + struct hlsl_ir_switch_case *c, *next; + + LIST_FOR_EACH_ENTRY_SAFE(c, next, cases, struct hlsl_ir_switch_case, entry) + { + hlsl_free_ir_switch_case(c); + } +} + +static struct hlsl_ir_node *clone_switch(struct hlsl_ctx *ctx, + struct clone_instr_map *map, struct hlsl_ir_switch *s) +{ + struct hlsl_ir_switch_case *c, *d; + struct hlsl_ir_node *ret; + struct hlsl_block body; + struct list cases; + + list_init(&cases); + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + if (!(clone_block(ctx, &body, &c->body, map))) + { + hlsl_cleanup_ir_switch_cases(&cases); + return NULL; + } + + d = hlsl_new_switch_case(ctx, c->value, c->is_default, &body, &c->loc); + hlsl_block_cleanup(&body); + if (!d) + { + hlsl_cleanup_ir_switch_cases(&cases); + return NULL; + } + + list_add_tail(&cases, &d->entry); + } + + ret = hlsl_new_switch(ctx, map_instr(map, s->selector.node), &cases, &s->node.loc); + hlsl_cleanup_ir_switch_cases(&cases); + + return ret; +} + static struct hlsl_ir_node *clone_instr(struct hlsl_ctx *ctx, struct clone_instr_map *map, const struct hlsl_ir_node *instr) { @@ -1843,6 +1929,9 @@ static struct hlsl_ir_node *clone_instr(struct hlsl_ctx *ctx, case HLSL_IR_STORE: return clone_store(ctx, map, hlsl_ir_store(instr));
+ case HLSL_IR_SWITCH: + return clone_switch(ctx, map, hlsl_ir_switch(instr)); + case HLSL_IR_SWIZZLE: return clone_swizzle(ctx, map, hlsl_ir_swizzle(instr)); } @@ -2261,6 +2350,7 @@ const char *hlsl_node_type_to_string(enum hlsl_ir_node_type type) [HLSL_IR_RESOURCE_LOAD ] = "HLSL_IR_RESOURCE_LOAD", [HLSL_IR_RESOURCE_STORE] = "HLSL_IR_RESOURCE_STORE", [HLSL_IR_STORE ] = "HLSL_IR_STORE", + [HLSL_IR_SWITCH ] = "HLSL_IR_SWITCH", [HLSL_IR_SWIZZLE ] = "HLSL_IR_SWIZZLE", };
@@ -2685,6 +2775,32 @@ static void dump_ir_index(struct vkd3d_string_buffer *buffer, const struct hlsl_ vkd3d_string_buffer_printf(buffer, "]"); }
+static void dump_ir_switch(struct hlsl_ctx *ctx, struct vkd3d_string_buffer *buffer, const struct hlsl_ir_switch *s) +{ + struct hlsl_ir_switch_case *c; + + vkd3d_string_buffer_printf(buffer, "switch ("); + dump_src(buffer, &s->selector); + vkd3d_string_buffer_printf(buffer, ") {\n"); + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + if (c->is_default) + { + vkd3d_string_buffer_printf(buffer, " %10s default: {\n", ""); + } + else + { + vkd3d_string_buffer_printf(buffer, " %10s case %u : {\n", "", c->value); + } + + dump_block(ctx, buffer, &c->body); + vkd3d_string_buffer_printf(buffer, " %10s }\n", ""); + } + + vkd3d_string_buffer_printf(buffer, " %10s }", ""); +} + static void dump_instr(struct hlsl_ctx *ctx, struct vkd3d_string_buffer *buffer, const struct hlsl_ir_node *instr) { if (instr->index) @@ -2740,6 +2856,10 @@ static void dump_instr(struct hlsl_ctx *ctx, struct vkd3d_string_buffer *buffer, dump_ir_store(buffer, hlsl_ir_store(instr)); break;
+ case HLSL_IR_SWITCH: + dump_ir_switch(ctx, buffer, hlsl_ir_switch(instr)); + break; + case HLSL_IR_SWIZZLE: dump_ir_swizzle(buffer, hlsl_ir_swizzle(instr)); break; @@ -2900,6 +3020,14 @@ static void free_ir_swizzle(struct hlsl_ir_swizzle *swizzle) vkd3d_free(swizzle); }
+static void free_ir_switch(struct hlsl_ir_switch *s) +{ + hlsl_src_remove(&s->selector); + hlsl_cleanup_ir_switch_cases(&s->cases); + + vkd3d_free(s); +} + static void free_ir_index(struct hlsl_ir_index *index) { hlsl_src_remove(&index->val); @@ -2960,6 +3088,10 @@ void hlsl_free_instr(struct hlsl_ir_node *node) case HLSL_IR_SWIZZLE: free_ir_swizzle(hlsl_ir_swizzle(node)); break; + + case HLSL_IR_SWITCH: + free_ir_switch(hlsl_ir_switch(node)); + break; } }
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 9e83b8b7..6200d6b9 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -281,6 +281,7 @@ enum hlsl_ir_node_type HLSL_IR_RESOURCE_STORE, HLSL_IR_STORE, HLSL_IR_SWIZZLE, + HLSL_IR_SWITCH, };
/* Common data for every type of IR instruction node. */ @@ -499,6 +500,22 @@ struct hlsl_ir_loop unsigned int next_index; /* liveness index of the end of the loop */ };
+struct hlsl_ir_switch_case +{ + unsigned int value; + bool is_default; + struct hlsl_block body; + struct list entry; + struct vkd3d_shader_location loc; +}; + +struct hlsl_ir_switch +{ + struct hlsl_ir_node node; + struct hlsl_src selector; + struct list cases; +}; + enum hlsl_ir_expr_op { HLSL_OP0_VOID, @@ -947,6 +964,12 @@ static inline struct hlsl_ir_index *hlsl_ir_index(const struct hlsl_ir_node *nod return CONTAINING_RECORD(node, struct hlsl_ir_index, node); }
+static inline struct hlsl_ir_switch *hlsl_ir_switch(const struct hlsl_ir_node *node) +{ + assert(node->type == HLSL_IR_SWITCH); + return CONTAINING_RECORD(node, struct hlsl_ir_switch, node); +} + static inline void hlsl_block_init(struct hlsl_block *block) { list_init(&block->instrs); @@ -1120,6 +1143,9 @@ bool hlsl_copy_deref(struct hlsl_ctx *ctx, struct hlsl_deref *deref, const struc void hlsl_cleanup_deref(struct hlsl_deref *deref); void hlsl_cleanup_semantic(struct hlsl_semantic *semantic);
+void hlsl_cleanup_ir_switch_cases(struct list *cases); +void hlsl_free_ir_switch_case(struct hlsl_ir_switch_case *c); + void hlsl_replace_node(struct hlsl_ir_node *old, struct hlsl_ir_node *new);
void hlsl_free_attribute(struct hlsl_attribute *attr); @@ -1213,6 +1239,10 @@ struct hlsl_ir_node *hlsl_new_unary_expr(struct hlsl_ctx *ctx, enum hlsl_ir_expr struct hlsl_ir_var *hlsl_new_var(struct hlsl_ctx *ctx, const char *name, struct hlsl_type *type, const struct vkd3d_shader_location *loc, const struct hlsl_semantic *semantic, unsigned int modifiers, const struct hlsl_reg_reservation *reg_reservation); +struct hlsl_ir_switch_case *hlsl_new_switch_case(struct hlsl_ctx *ctx, unsigned int value, bool is_default, + struct hlsl_block *body, const struct vkd3d_shader_location *loc); +struct hlsl_ir_node *hlsl_new_switch(struct hlsl_ctx *ctx, struct hlsl_ir_node *selector, + struct list *cases, const struct vkd3d_shader_location *loc);
void hlsl_error(struct hlsl_ctx *ctx, const struct vkd3d_shader_location *loc, enum vkd3d_shader_error error, const char *fmt, ...) VKD3D_PRINTF_FUNC(4, 5); diff --git a/libs/vkd3d-shader/hlsl.l b/libs/vkd3d-shader/hlsl.l index b3d4aeee..527d853a 100644 --- a/libs/vkd3d-shader/hlsl.l +++ b/libs/vkd3d-shader/hlsl.l @@ -46,7 +46,7 @@ static void update_location(struct hlsl_ctx *ctx, YYLTYPE *loc);
%x pp pp_line pp_pragma pp_ignore
-RESERVED1 auto|case|catch|char|class|const_cast|default|delete|dynamic_cast|enum +RESERVED1 auto|catch|char|class|const_cast|delete|dynamic_cast|enum RESERVED2 explicit|friend|goto|long|mutable|new|operator|private|protected|public RESERVED3 reinterpret_cast|short|signed|sizeof|static_cast|template|this|throw|try RESERVED4 typename|union|unsigned|using|virtual @@ -73,6 +73,7 @@ ANY (.) BlendState {return KW_BLENDSTATE; } break {return KW_BREAK; } Buffer {return KW_BUFFER; } +case {return KW_CASE; } cbuffer {return KW_CBUFFER; } centroid {return KW_CENTROID; } compile {return KW_COMPILE; } @@ -80,6 +81,7 @@ const {return KW_CONST; } continue {return KW_CONTINUE; } DepthStencilState {return KW_DEPTHSTENCILSTATE; } DepthStencilView {return KW_DEPTHSTENCILVIEW; } +default {return KW_DEFAULT; } discard {return KW_DISCARD; } do {return KW_DO; } double {return KW_DOUBLE; } diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index e58574f7..3d194a0c 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -162,6 +162,12 @@ static void destroy_block(struct hlsl_block *block) vkd3d_free(block); }
+static void destroy_switch_cases(struct list *cases) +{ + hlsl_cleanup_ir_switch_cases(cases); + vkd3d_free(cases); +} + static bool hlsl_types_are_componentwise_compatible(struct hlsl_ctx *ctx, struct hlsl_type *src, struct hlsl_type *dst) { @@ -1180,6 +1186,7 @@ static unsigned int evaluate_static_expression_as_uint(struct hlsl_ctx *ctx, str case HLSL_IR_RESOURCE_LOAD: case HLSL_IR_RESOURCE_STORE: case HLSL_IR_STORE: + case HLSL_IR_SWITCH: hlsl_error(ctx, &node->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Expected literal expression."); } @@ -4679,17 +4686,20 @@ static struct hlsl_scope *get_loop_scope(struct hlsl_scope *scope) enum hlsl_sampler_dim sampler_dim; struct hlsl_attribute *attr; struct parse_attribute_list attr_list; + struct hlsl_ir_switch_case *switch_case; }
%token KW_BLENDSTATE %token KW_BREAK %token KW_BUFFER +%token KW_CASE %token KW_CBUFFER %token KW_CENTROID %token KW_COLUMN_MAJOR %token KW_COMPILE %token KW_CONST %token KW_CONTINUE +%token KW_DEFAULT %token KW_DEPTHSTENCILSTATE %token KW_DEPTHSTENCILVIEW %token KW_DISCARD @@ -4796,6 +4806,7 @@ static struct hlsl_scope *get_loop_scope(struct hlsl_scope *scope) %type <list> type_specs %type <list> variables_def %type <list> variables_def_typed +%type <list> switch_cases
%token <name> VAR_IDENTIFIER %token <name> NEW_IDENTIFIER @@ -4838,6 +4849,7 @@ static struct hlsl_scope *get_loop_scope(struct hlsl_scope *scope) %type <block> statement %type <block> statement_list %type <block> struct_declaration_without_vars +%type <block> switch_statement %type <block> unary_expr
%type <boolval> boolean @@ -4876,6 +4888,8 @@ static struct hlsl_scope *get_loop_scope(struct hlsl_scope *scope)
%type <semantic> semantic
+%type <switch_case> switch_case + %type <type> field_type %type <type> named_struct_spec %type <type> unnamed_struct_spec @@ -5357,6 +5371,11 @@ loop_scope_start: ctx->cur_scope->loop = true; }
+switch_scope_start: + %empty + { + } + var_identifier: VAR_IDENTIFIER | NEW_IDENTIFIER @@ -6185,6 +6204,7 @@ statement: | jump_statement | selection_statement | loop_statement + | switch_statement
jump_statement: KW_BREAK ';' @@ -6333,6 +6353,103 @@ loop_statement: hlsl_pop_scope(ctx); }
+switch_statement: + attribute_list_optional switch_scope_start KW_SWITCH '(' expr ')' '{' switch_cases '}' + { + struct hlsl_ir_node *selector = node_from_block($5); + struct hlsl_ir_node *s; + + if (!(selector = add_implicit_conversion(ctx, $5, selector, hlsl_get_scalar_type(ctx, HLSL_TYPE_UINT), &@5))) + { + destroy_switch_cases($8); + destroy_block($5); + YYABORT; + } + + s = hlsl_new_switch(ctx, selector, $8, &@3); + + destroy_switch_cases($8); + + if (!s) + { + destroy_block($5); + YYABORT; + } + + $$ = $5; + hlsl_block_add_instr($$, s); + } + +switch_case: + KW_CASE expr ':' statement_list + { + struct hlsl_ir_switch_case *c; + unsigned int value; + + value = evaluate_static_expression_as_uint(ctx, $2, &@2); + + c = hlsl_new_switch_case(ctx, value, false, $4, &@2); + + destroy_block($2); + destroy_block($4); + + if (!c) + YYABORT; + $$ = c; + } + | KW_CASE expr ':' + { + struct hlsl_ir_switch_case *c; + unsigned int value; + + value = evaluate_static_expression_as_uint(ctx, $2, &@2); + + c = hlsl_new_switch_case(ctx, value, false, NULL, &@2); + + destroy_block($2); + + if (!c) + YYABORT; + $$ = c; + } + | KW_DEFAULT ':' statement_list + { + struct hlsl_ir_switch_case *c; + + c = hlsl_new_switch_case(ctx, 0, true, $3, &@1); + + destroy_block($3); + + if (!c) + YYABORT; + $$ = c; + } + | KW_DEFAULT ':' + { + struct hlsl_ir_switch_case *c; + + if (!(c = hlsl_new_switch_case(ctx, 0, true, NULL, &@1))) + YYABORT; + $$ = c; + } + +switch_cases: + switch_case + { + struct hlsl_ir_switch_case *c = LIST_ENTRY($1, struct hlsl_ir_switch_case, entry); + if (!($$ = make_empty_list(ctx))) + { + hlsl_free_ir_switch_case(c); + YYABORT; + } + list_add_head($$, &$1->entry); + } + | switch_cases switch_case + { + $$ = $1; + list_add_tail($$, &$2->entry); + } + expr_optional: %empty { diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 7d17ca8c..d1853067 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -575,7 +575,19 @@ bool hlsl_transform_ir(struct hlsl_ctx *ctx, bool (*func)(struct hlsl_ctx *ctx, progress |= hlsl_transform_ir(ctx, func, &iff->else_block, context); } else if (instr->type == HLSL_IR_LOOP) + { progress |= hlsl_transform_ir(ctx, func, &hlsl_ir_loop(instr)->body, context); + } + else if (instr->type == HLSL_IR_SWITCH) + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + progress |= hlsl_transform_ir(ctx, func, &c->body, context); + } + }
progress |= func(ctx, instr, context); } @@ -835,6 +847,30 @@ static bool lower_return(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *fun } } } + else if (instr->type == HLSL_IR_SWITCH) + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + has_early_return |= lower_return(ctx, func, &c->body, true); + } + + if (has_early_return) + { + if (in_loop) + { + /* For a 'switch' nested in a loop append a break after the 'switch'. */ + insert_early_return_break(ctx, func, instr); + } + else + { + cf_instr = instr; + break; + } + } + } }
if (return_instr) @@ -2929,6 +2965,7 @@ static bool dce(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, void *context) case HLSL_IR_JUMP: case HLSL_IR_LOOP: case HLSL_IR_RESOURCE_STORE: + case HLSL_IR_SWITCH: break; }
@@ -2956,6 +2993,16 @@ static unsigned int index_instructions(struct hlsl_block *block, unsigned int in index = index_instructions(&hlsl_ir_loop(instr)->body, index); hlsl_ir_loop(instr)->next_index = index; } + else if (instr->type == HLSL_IR_SWITCH) + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + index = index_instructions(&c->body, index); + } + } }
return index; @@ -3173,6 +3220,16 @@ static void compute_liveness_recurse(struct hlsl_block *block, unsigned int loop jump->condition.node->last_read = last_read; break; } + case HLSL_IR_SWITCH: + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + compute_liveness_recurse(&c->body, loop_first, loop_last); + s->selector.node->last_read = last_read; + break; + } case HLSL_IR_CONSTANT: break; } @@ -3524,6 +3581,18 @@ static void allocate_temp_registers_recurse(struct hlsl_ctx *ctx, break; }
+ case HLSL_IR_SWITCH: + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + allocate_temp_registers_recurse(ctx, &c->body, allocator); + } + break; + } + default: break; } @@ -3633,6 +3702,18 @@ static void allocate_const_registers_recurse(struct hlsl_ctx *ctx, break; }
+ case HLSL_IR_SWITCH: + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + allocate_const_registers_recurse(ctx, &c->body, allocator); + } + break; + } + default: break; } diff --git a/tests/hlsl/switch.shader_test b/tests/hlsl/switch.shader_test index aab12485..e41834d7 100644 --- a/tests/hlsl/switch.shader_test +++ b/tests/hlsl/switch.shader_test @@ -230,7 +230,7 @@ float4 main() : sv_target return c; }
-[pixel shader fail] +[pixel shader fail todo] uint4 v;
float4 main() : sv_target @@ -246,7 +246,7 @@ float4 main() : sv_target return 0.0; }
-[pixel shader fail] +[pixel shader fail todo] uint4 v;
float4 main() : sv_target @@ -262,7 +262,7 @@ float4 main() : sv_target return 0.0; }
-[pixel shader fail(sm<6)] +[pixel shader fail(sm<6) todo] uint4 v;
float4 main() : sv_target @@ -279,7 +279,7 @@ float4 main() : sv_target return 0.0; }
-[pixel shader fail(sm<6)] +[pixel shader fail(sm<6) todo] uint4 v;
float4 main() : sv_target @@ -296,7 +296,7 @@ float4 main() : sv_target return 0.0; }
-[pixel shader fail(sm<6)] +[pixel shader fail(sm<6) todo] uint4 v;
float4 main() : sv_target @@ -455,7 +455,7 @@ todo draw quad probe all rgba (1.0, 2.0, 3.0, 4.0)
% 'continue' is not supported in switches -[pixel shader fail(sm<6)] +[pixel shader fail(sm<6) todo] uint4 v;
float4 main() : sv_target
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- libs/vkd3d-shader/hlsl.y | 34 ++++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 1 + 2 files changed, 35 insertions(+)
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 3d194a0c..20802eb2 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -4648,6 +4648,39 @@ static struct hlsl_scope *get_loop_scope(struct hlsl_scope *scope) return scope->upper ? get_loop_scope(scope->upper) : NULL; }
+static void check_duplicated_switch_cases(struct hlsl_ctx *ctx, const struct hlsl_ir_switch_case *check, struct list *cases) +{ + struct hlsl_ir_switch_case *c; + bool found_duplicate = false; + + LIST_FOR_EACH_ENTRY(c, cases, struct hlsl_ir_switch_case, entry) + { + if (check->is_default) + { + if ((found_duplicate = c->is_default)) + { + hlsl_error(ctx, &check->loc, VKD3D_SHADER_ERROR_HLSL_DUPLICATE_SWITCH_CASE, + "Found multiple 'default' statements."); + hlsl_note(ctx, &c->loc, VKD3D_SHADER_LOG_ERROR, "The 'default' statement was previously found here."); + } + } + else + { + if (c->is_default) continue; + if ((found_duplicate = (c->value == check->value))) + { + hlsl_error(ctx, &check->loc, VKD3D_SHADER_ERROR_HLSL_DUPLICATE_SWITCH_CASE, + "Found duplicate 'case' statement."); + hlsl_note(ctx, &c->loc, VKD3D_SHADER_LOG_ERROR, "The same 'case %d' statement was previously found here.", + c->value); + } + } + + if (found_duplicate) + break; + } +} + }
%locations @@ -6447,6 +6480,7 @@ switch_cases: | switch_cases switch_case { $$ = $1; + check_duplicated_switch_cases(ctx, $2, $$); list_add_tail($$, &$2->entry); }
diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index c9d2dec8..93154c0f 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -142,6 +142,7 @@ enum vkd3d_shader_error VKD3D_SHADER_ERROR_HLSL_RECURSIVE_CALL = 5025, VKD3D_SHADER_ERROR_HLSL_INCONSISTENT_SAMPLER = 5026, VKD3D_SHADER_ERROR_HLSL_NON_FINITE_RESULT = 5027, + VKD3D_SHADER_ERROR_HLSL_DUPLICATE_SWITCH_CASE = 5028,
VKD3D_SHADER_WARNING_HLSL_IMPLICIT_TRUNCATION = 5300, VKD3D_SHADER_WARNING_HLSL_DIVISION_BY_ZERO = 5301,
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- libs/vkd3d-shader/hlsl.h | 2 ++ libs/vkd3d-shader/hlsl.y | 42 ++++++++++++++++++++++++----------- tests/hlsl/switch.shader_test | 4 ++-- 3 files changed, 33 insertions(+), 15 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index 6200d6b9..bd09e869 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -727,6 +727,8 @@ struct hlsl_scope struct hlsl_scope *upper; /* The scope was created for the loop statement. */ bool loop; + /* The scope was created for the switch statement. */ + bool _switch; };
struct hlsl_profile_info diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 20802eb2..5fd140fb 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -4640,12 +4640,31 @@ static void validate_texture_format_type(struct hlsl_ctx *ctx, struct hlsl_type } }
-static struct hlsl_scope *get_loop_scope(struct hlsl_scope *scope) +static bool check_continue(struct hlsl_ctx *ctx, const struct hlsl_scope *scope, const struct vkd3d_shader_location *loc) { + if (scope->_switch) + { + hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, + "The 'continue' statement is not allowed in 'switch' statements."); + return false; + } + if (scope->loop) - return scope; + return true;
- return scope->upper ? get_loop_scope(scope->upper) : NULL; + if (scope->upper) + return check_continue(ctx, scope->upper, loc); + + hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "The 'continue' statement is only allowed in loops."); + return false; +} + +static bool is_break_allowed(const struct hlsl_scope *scope) +{ + if (scope->loop || scope->_switch) + return true; + + return scope->upper ? is_break_allowed(scope->upper) : false; }
static void check_duplicated_switch_cases(struct hlsl_ctx *ctx, const struct hlsl_ir_switch_case *check, struct list *cases) @@ -5407,6 +5426,8 @@ loop_scope_start: switch_scope_start: %empty { + hlsl_push_scope(ctx); + ctx->cur_scope->_switch = true; }
var_identifier: @@ -6244,12 +6265,10 @@ jump_statement: { struct hlsl_ir_node *jump;
- /* TODO: allow 'break' in the 'switch' statements. */ - - if (!get_loop_scope(ctx->cur_scope)) + if (!is_break_allowed(ctx->cur_scope)) { hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, - "The 'break' statement must be used inside of a loop."); + "The 'break' statement must be used inside of a loop or a switch."); }
if (!($$ = make_empty_block(ctx))) @@ -6261,13 +6280,8 @@ jump_statement: | KW_CONTINUE ';' { struct hlsl_ir_node *jump; - struct hlsl_scope *scope;
- if (!(scope = get_loop_scope(ctx->cur_scope))) - { - hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, - "The 'continue' statement must be used inside of a loop."); - } + check_continue(ctx, ctx->cur_scope, &@1);
if (!($$ = make_empty_block(ctx))) YYABORT; @@ -6411,6 +6425,8 @@ switch_statement:
$$ = $5; hlsl_block_add_instr($$, s); + + hlsl_pop_scope(ctx); }
switch_case: diff --git a/tests/hlsl/switch.shader_test b/tests/hlsl/switch.shader_test index e41834d7..720672a7 100644 --- a/tests/hlsl/switch.shader_test +++ b/tests/hlsl/switch.shader_test @@ -211,7 +211,7 @@ float4 main() : sv_target }
% unterminated cases -[pixel shader fail(sm<6)] +[pixel shader fail(sm<6) todo] uint4 v;
float4 main() : sv_target @@ -455,7 +455,7 @@ todo draw quad probe all rgba (1.0, 2.0, 3.0, 4.0)
% 'continue' is not supported in switches -[pixel shader fail(sm<6) todo] +[pixel shader fail(sm<6)] uint4 v;
float4 main() : sv_target
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- libs/vkd3d-shader/hlsl_codegen.c | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index d1853067..cf7bbc8f 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -1675,6 +1675,19 @@ static void copy_propagation_invalidate_from_block(struct hlsl_ctx *ctx, struct break; }
+ case HLSL_IR_SWITCH: + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + copy_propagation_invalidate_from_block(ctx, state, &c->body); + } + + break; + } + default: break; } @@ -1723,6 +1736,28 @@ static bool copy_propagation_process_loop(struct hlsl_ctx *ctx, struct hlsl_ir_l return progress; }
+static bool copy_propagation_process_switch(struct hlsl_ctx *ctx, struct hlsl_ir_switch *s, + struct copy_propagation_state *state) +{ + struct copy_propagation_state inner_state; + struct hlsl_ir_switch_case *c; + bool progress = false; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + copy_propagation_state_init(ctx, &inner_state, state); + progress |= copy_propagation_transform_block(ctx, &c->body, &inner_state); + copy_propagation_state_destroy(&inner_state); + } + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + copy_propagation_invalidate_from_block(ctx, state, &c->body); + } + + return progress; +} + static bool copy_propagation_transform_block(struct hlsl_ctx *ctx, struct hlsl_block *block, struct copy_propagation_state *state) { @@ -1761,6 +1796,10 @@ static bool copy_propagation_transform_block(struct hlsl_ctx *ctx, struct hlsl_b progress |= copy_propagation_process_loop(ctx, hlsl_ir_loop(instr), state); break;
+ case HLSL_IR_SWITCH: + progress |= copy_propagation_process_switch(ctx, hlsl_ir_switch(instr), state); + break; + default: break; }
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- libs/vkd3d-shader/hlsl_codegen.c | 57 ++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+)
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index cf7bbc8f..013667e2 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -4455,6 +4455,62 @@ static bool type_has_object_components(struct hlsl_type *type) return false; }
+static void remove_unreachable_code(struct hlsl_ctx *ctx, struct hlsl_block *body) +{ + struct hlsl_ir_node *instr, *next; + struct hlsl_block block; + struct list *start; + + LIST_FOR_EACH_ENTRY_SAFE(instr, next, &body->instrs, struct hlsl_ir_node, entry) + { + if (instr->type == HLSL_IR_IF) + { + struct hlsl_ir_if *iff = hlsl_ir_if(instr); + + remove_unreachable_code(ctx, &iff->then_block); + remove_unreachable_code(ctx, &iff->else_block); + } + else if (instr->type == HLSL_IR_LOOP) + { + struct hlsl_ir_loop *loop = hlsl_ir_loop(instr); + + remove_unreachable_code(ctx, &loop->body); + } + else if (instr->type == HLSL_IR_SWITCH) + { + struct hlsl_ir_switch *s = hlsl_ir_switch(instr); + struct hlsl_ir_switch_case *c; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + remove_unreachable_code(ctx, &c->body); + } + } + } + + /* Remove instructions past unconditional jumps. */ + LIST_FOR_EACH_ENTRY(instr, &body->instrs, struct hlsl_ir_node, entry) + { + struct hlsl_ir_jump *jump; + + if (instr->type != HLSL_IR_JUMP) + continue; + + jump = hlsl_ir_jump(instr); + if (jump->type != HLSL_IR_JUMP_BREAK && jump->type != HLSL_IR_JUMP_CONTINUE) + continue; + + if (!(start = list_next(&body->instrs, &instr->entry))) + break; + + hlsl_block_init(&block); + list_move_slice_tail(&block.instrs, start, list_tail(&body->instrs)); + hlsl_block_cleanup(&block); + + break; + } +} + int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry_func, enum vkd3d_shader_target_type target_type, struct vkd3d_shader_code *out) { @@ -4572,6 +4628,7 @@ int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry progress |= hlsl_transform_ir(ctx, remove_trivial_conditional_branches, body, NULL); } while (progress); + remove_unreachable_code(ctx, body);
lower_ir(ctx, lower_nonconstant_vector_derefs, body); lower_ir(ctx, lower_casts_to_bool, body);
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- libs/vkd3d-shader/hlsl_codegen.c | 113 +++++++++++++++++++++++++++++++ tests/hlsl/switch.shader_test | 12 ++-- 2 files changed, 119 insertions(+), 6 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 013667e2..541a45ac 100644 --- a/libs/vkd3d-shader/hlsl_codegen.c +++ b/libs/vkd3d-shader/hlsl_codegen.c @@ -2169,6 +2169,118 @@ static bool remove_trivial_conditional_branches(struct hlsl_ctx *ctx, struct hls return true; }
+static bool normalize_switch_cases(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, void *context) +{ + struct hlsl_ir_switch_case *c, *def = NULL; + bool missing_terminal_break = false; + struct hlsl_ir_node *node; + struct hlsl_ir_jump *jump; + struct hlsl_ir_switch *s; + + if (instr->type != HLSL_IR_SWITCH) + return false; + s = hlsl_ir_switch(instr); + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + bool terminal_break = false; + + if (list_empty(&c->body.instrs)) + { + terminal_break = !!list_next(&s->cases, &c->entry); + } + else + { + node = LIST_ENTRY(list_tail(&c->body.instrs), struct hlsl_ir_node, entry); + if (node->type == HLSL_IR_JUMP) + { + jump = hlsl_ir_jump(node); + terminal_break = jump->type == HLSL_IR_JUMP_BREAK; + } + } + + missing_terminal_break |= !terminal_break; + + if (!terminal_break) + { + if (c->is_default) + { + hlsl_error(ctx, &c->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, + "The 'default' case block is not terminated with 'break' or 'return'."); + } + else + { + hlsl_error(ctx, &c->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, + "Switch case block '%u' is not terminated with 'break' or 'return'.", c->value); + } + } + } + + if (missing_terminal_break) + return true; + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + if (c->is_default) + { + def = c; + + /* Remove preceding empty cases. */ + while (list_prev(&s->cases, &def->entry)) + { + c = LIST_ENTRY(list_prev(&s->cases, &def->entry), struct hlsl_ir_switch_case, entry); + if (!list_empty(&c->body.instrs)) + break; + hlsl_free_ir_switch_case(c); + } + + if (list_empty(&def->body.instrs)) + { + /* Remove following empty cases. */ + while (list_next(&s->cases, &def->entry)) + { + c = LIST_ENTRY(list_next(&s->cases, &def->entry), struct hlsl_ir_switch_case, entry); + if (!list_empty(&c->body.instrs)) + break; + hlsl_free_ir_switch_case(c); + } + + /* Merge with the next case. */ + if (list_next(&s->cases, &def->entry)) + { + c = LIST_ENTRY(list_next(&s->cases, &def->entry), struct hlsl_ir_switch_case, entry); + c->is_default = true; + hlsl_free_ir_switch_case(def); + def = c; + } + } + + break; + } + } + + if (def) + { + list_remove(&def->entry); + } + else + { + struct hlsl_ir_node *jump; + + if (!(def = hlsl_new_switch_case(ctx, 0, true, NULL, &s->node.loc))) + return true; + if (!(jump = hlsl_new_jump(ctx, HLSL_IR_JUMP_BREAK, NULL, &s->node.loc))) + { + hlsl_free_ir_switch_case(def); + return true; + } + hlsl_block_add_instr(&def->body, jump); + } + list_add_tail(&s->cases, &def->entry); + + return true; +} + static bool lower_nonconstant_vector_derefs(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, struct hlsl_block *block) { struct hlsl_ir_node *idx; @@ -4629,6 +4741,7 @@ int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry } while (progress); remove_unreachable_code(ctx, body); + hlsl_transform_ir(ctx, normalize_switch_cases, body, NULL);
lower_ir(ctx, lower_nonconstant_vector_derefs, body); lower_ir(ctx, lower_casts_to_bool, body); diff --git a/tests/hlsl/switch.shader_test b/tests/hlsl/switch.shader_test index 720672a7..aab12485 100644 --- a/tests/hlsl/switch.shader_test +++ b/tests/hlsl/switch.shader_test @@ -211,7 +211,7 @@ float4 main() : sv_target }
% unterminated cases -[pixel shader fail(sm<6) todo] +[pixel shader fail(sm<6)] uint4 v;
float4 main() : sv_target @@ -230,7 +230,7 @@ float4 main() : sv_target return c; }
-[pixel shader fail todo] +[pixel shader fail] uint4 v;
float4 main() : sv_target @@ -246,7 +246,7 @@ float4 main() : sv_target return 0.0; }
-[pixel shader fail todo] +[pixel shader fail] uint4 v;
float4 main() : sv_target @@ -262,7 +262,7 @@ float4 main() : sv_target return 0.0; }
-[pixel shader fail(sm<6) todo] +[pixel shader fail(sm<6)] uint4 v;
float4 main() : sv_target @@ -279,7 +279,7 @@ float4 main() : sv_target return 0.0; }
-[pixel shader fail(sm<6) todo] +[pixel shader fail(sm<6)] uint4 v;
float4 main() : sv_target @@ -296,7 +296,7 @@ float4 main() : sv_target return 0.0; }
-[pixel shader fail(sm<6) todo] +[pixel shader fail(sm<6)] uint4 v;
float4 main() : sv_target
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- libs/vkd3d-shader/tpf.c | 44 +++++++++++++++++++++ tests/hlsl/switch.shader_test | 74 +++++++++++++++++------------------ 2 files changed, 81 insertions(+), 37 deletions(-)
diff --git a/libs/vkd3d-shader/tpf.c b/libs/vkd3d-shader/tpf.c index 0ea5a682..0be0ec4f 100644 --- a/libs/vkd3d-shader/tpf.c +++ b/libs/vkd3d-shader/tpf.c @@ -5467,6 +5467,46 @@ static void write_sm4_store(const struct tpf_writer *tpf, const struct hlsl_ir_s write_sm4_instruction(tpf, &instr); }
+static void write_sm4_switch(const struct tpf_writer *tpf, const struct hlsl_ir_switch *s) +{ + const struct hlsl_ir_node *selector = s->selector.node; + struct hlsl_ir_switch_case *c; + struct sm4_instruction instr; + + memset(&instr, 0, sizeof(instr)); + instr.opcode = VKD3D_SM4_OP_SWITCH; + + sm4_src_from_node(tpf, &instr.srcs[0], selector, VKD3DSP_WRITEMASK_ALL); + instr.src_count = 1; + + write_sm4_instruction(tpf, &instr); + + LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry) + { + memset(&instr, 0, sizeof(instr)); + if (c->is_default) + { + instr.opcode = VKD3D_SM4_OP_DEFAULT; + } + else + { + struct hlsl_constant_value value = { .u[0].u = c->value }; + + instr.opcode = VKD3D_SM4_OP_CASE; + sm4_src_from_constant_value(&instr.srcs[0], &value, 1, VKD3DSP_WRITEMASK_ALL); + instr.src_count = 1; + } + + write_sm4_instruction(tpf, &instr); + write_sm4_block(tpf, &c->body); + } + + memset(&instr, 0, sizeof(instr)); + instr.opcode = VKD3D_SM4_OP_ENDSWITCH; + + write_sm4_instruction(tpf, &instr); +} + static void write_sm4_swizzle(const struct tpf_writer *tpf, const struct hlsl_ir_swizzle *swizzle) { unsigned int hlsl_swizzle; @@ -5554,6 +5594,10 @@ static void write_sm4_block(const struct tpf_writer *tpf, const struct hlsl_bloc write_sm4_store(tpf, hlsl_ir_store(instr)); break;
+ case HLSL_IR_SWITCH: + write_sm4_switch(tpf, hlsl_ir_switch(instr)); + break; + case HLSL_IR_SWIZZLE: write_sm4_swizzle(tpf, hlsl_ir_swizzle(instr)); break; diff --git a/tests/hlsl/switch.shader_test b/tests/hlsl/switch.shader_test index aab12485..243b0b11 100644 --- a/tests/hlsl/switch.shader_test +++ b/tests/hlsl/switch.shader_test @@ -1,7 +1,7 @@ [require] shader model >= 4.0
-[pixel shader todo] +[pixel shader] uint4 v;
float4 main() : sv_target @@ -19,17 +19,17 @@ float4 main() : sv_target
[test] uniform 0 uint4 3 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (5.0, 5.0, 5.0, 5.0) uniform 0 uint4 1 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (4.0, 4.0, 4.0, 4.0) uniform 0 uint4 0 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (3.0, 3.0, 3.0, 3.0)
% falling through is only supported for empty case statements -[pixel shader todo] +[pixel shader] uint4 v;
float4 main() : sv_target @@ -49,17 +49,17 @@ float4 main() : sv_target
[test] uniform 0 uint4 2 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.0, 2.0, 3.0, 4.0) uniform 0 uint4 1 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.1, 2.0, 3.0, 4.0) uniform 0 uint4 0 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.1, 2.0, 3.0, 4.0)
% case value evaluation -[pixel shader todo] +[pixel shader] uint4 v;
float4 main() : sv_target @@ -81,14 +81,14 @@ float4 main() : sv_target
[test] uniform 0 uint4 2 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.1, 2.1, 3.1, 4.1) uniform 0 uint4 1 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.0, 2.0, 3.0, 4.0)
% floats are accepted -[pixel shader todo fail(sm>=6)] +[pixel shader fail(sm>=6)] uint4 v;
float4 main() : sv_target @@ -110,13 +110,13 @@ float4 main() : sv_target
[test] uniform 0 uint4 2 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.1, 2.1, 3.1, 4.1) uniform 0 uint4 1 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.0, 2.0, 3.0, 4.0)
-[pixel shader todo fail(sm>=6)] +[pixel shader fail(sm>=6)] float4 v;
float4 main() : sv_target @@ -138,10 +138,10 @@ float4 main() : sv_target
[test] uniform 0 float4 2.0 0.0 0.0 0.0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.1, 2.1, 3.1, 4.1) uniform 0 float4 1.0 0.0 0.0 0.0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.0, 2.0, 3.0, 4.0)
[pixel shader fail] @@ -312,7 +312,7 @@ float4 main() : sv_target }
% more complicated breaks -[pixel shader todo] +[pixel shader] uint4 v;
float4 main() : sv_target @@ -339,17 +339,17 @@ float4 main() : sv_target
[test] uniform 0 uint4 2 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.1, 2.1, 3.1, 4.1) uniform 0 uint4 1 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.2, 2.2, 3.2, 4.2) uniform 0 uint4 0 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.0, 2.0, 3.0, 4.0)
% switch breaks within a loop -[pixel shader todo] +[pixel shader] uint4 v;
float4 main() : sv_target @@ -377,11 +377,11 @@ float4 main() : sv_target
[test] uniform 0 uint4 2 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (5.0, 6.0, 7.0, 8.0)
% default case placement -[pixel shader todo] +[pixel shader] uint4 v;
float4 main() : sv_target @@ -408,16 +408,16 @@ float4 main() : sv_target
[test] uniform 0 uint4 0 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (4.0, 5.0, 6.0, 7.0) uniform 0 uint4 2 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (2.0, 3.0, 4.0, 5.0) uniform 0 uint4 3 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (4.0, 5.0, 6.0, 7.0)
-[pixel shader todo] +[pixel shader] uint4 v;
float4 main() : sv_target @@ -445,13 +445,13 @@ float4 main() : sv_target
[test] uniform 0 uint4 3 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.0, 2.0, 3.0, 4.0) uniform 0 uint4 0 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (4.0, 5.0, 6.0, 7.0) uniform 0 uint4 5 0 0 0 -todo draw quad +todo(sm>=6) draw quad probe all rgba (1.0, 2.0, 3.0, 4.0)
% 'continue' is not supported in switches @@ -480,7 +480,7 @@ float4 main() : sv_target return c; }
-[pixel shader todo] +[pixel shader] uint4 v;
float4 main() : sv_target @@ -511,14 +511,14 @@ float4 main() : sv_target
[test] uniform 0 uint4 0 0 3 1 -todo draw quad +todo(sm>=6) draw quad probe all rgba (10.0, 11.0, 12.0, 13.0) uniform 0 uint4 1 0 3 1 -todo draw quad +todo(sm>=6) draw quad probe all rgba (7.0, 8.0, 9.0, 10.0)
% return from a switch nested in a loop -[pixel shader todo] +[pixel shader] uint4 v;
float4 main() : sv_target @@ -545,8 +545,8 @@ float4 main() : sv_target
[test] uniform 0 uint4 0 0 3 1 -todo draw quad +todo(sm>=6) draw quad probe all rgba (304.0, 305.0, 306.0, 307.0) uniform 0 uint4 1 0 3 1 -todo draw quad +todo(sm>=6) draw quad probe all rgba (3.0, 4.0, 5.0, 6.0)
On Fri Oct 27 01:07:41 2023 +0000, Nikolay Sivov wrote:
changed this line in [version 25 of the diff](/wine/vkd3d/-/merge_requests/361/diffs?diff_id=78878&start_sha=6295173c30d38b4812d42fc4e5d40c7d09d547be#3cf804f245af47d51595ff932bf817c50967eea2_864_862)
I believe this is addressed now, thanks for spotting it.
This is ready for another iteration.
This merge request was approved by Zebediah Figura.
This merge request was approved by Henri Verbeet.