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