From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- libs/vkd3d-shader/hlsl.c | 141 +++++++++++++++++++++++++++++++ libs/vkd3d-shader/hlsl.h | 32 +++++++ libs/vkd3d-shader/hlsl.l | 4 +- libs/vkd3d-shader/hlsl.y | 115 ++++++++++++++++++++++++- libs/vkd3d-shader/hlsl_codegen.c | 79 +++++++++++++++++ tests/hlsl/switch.shader_test | 2 +- 6 files changed, 368 insertions(+), 5 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index b42e30888..5591973c0 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -1343,6 +1343,47 @@ 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, struct hlsl_block *value, + 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; + + if (value) + { + c->value = hlsl_evaluate_static_expression_as_uint(ctx, value, loc); + } + else + { + c->is_default = true; + } + + 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 +1846,60 @@ 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, NULL, &body, &c->loc); + hlsl_block_cleanup(&body); + if (!d) + { + hlsl_cleanup_ir_switch_cases(&cases); + return NULL; + } + + d->value = c->value; + d->is_default = c->is_default; + 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 +1938,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 +2359,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 +2784,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 +2865,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 +3029,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 +3097,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 9f1a3fe21..d9b5e0ebf 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, @@ -945,6 +962,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); @@ -1118,6 +1141,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); @@ -1211,6 +1237,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, struct hlsl_block *value, + 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); @@ -1265,6 +1295,8 @@ bool hlsl_fold_constant_exprs(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, 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); +unsigned int hlsl_evaluate_static_expression_as_uint(struct hlsl_ctx *ctx, struct hlsl_block *block, + const struct vkd3d_shader_location *loc);
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.l b/libs/vkd3d-shader/hlsl.l index 90abd64a3..d457c6622 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 ba738473f..dcaca929f 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) { @@ -1154,7 +1160,7 @@ static struct hlsl_block *make_block(struct hlsl_ctx *ctx, struct hlsl_ir_node * return block; }
-static unsigned int evaluate_static_expression_as_uint(struct hlsl_ctx *ctx, struct hlsl_block *block, +unsigned int hlsl_evaluate_static_expression_as_uint(struct hlsl_ctx *ctx, struct hlsl_block *block, const struct vkd3d_shader_location *loc) { struct hlsl_ir_constant *constant; @@ -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."); } @@ -4684,12 +4691,14 @@ static struct hlsl_scope *get_loop_scope(struct hlsl_scope *scope) %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 @@ -4794,6 +4803,8 @@ 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_case +%type <list> switch_cases
%token <name> VAR_IDENTIFIER %token <name> NEW_IDENTIFIER @@ -4836,6 +4847,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 @@ -5355,6 +5367,11 @@ loop_scope_start: ctx->cur_scope->loop = true; }
+switch_scope_start: + %empty + { + } + var_identifier: VAR_IDENTIFIER | NEW_IDENTIFIER @@ -5674,7 +5691,7 @@ type_no_void: hlsl_block_init(&block); hlsl_block_add_block(&block, $5);
- sample_count = evaluate_static_expression_as_uint(ctx, &block, &@5); + sample_count = hlsl_evaluate_static_expression_as_uint(ctx, &block, &@5);
hlsl_block_cleanup(&block);
@@ -5948,7 +5965,7 @@ arrays: uint32_t *new_array; unsigned int size;
- size = evaluate_static_expression_as_uint(ctx, $2, &@2); + size = hlsl_evaluate_static_expression_as_uint(ctx, $2, &@2);
destroy_block($2);
@@ -6173,6 +6190,7 @@ statement: | jump_statement | selection_statement | loop_statement + | switch_statement
jump_statement: KW_BREAK ';' @@ -6321,6 +6339,97 @@ 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; + + c = hlsl_new_switch_case(ctx, $2, $4, &@2); + + destroy_block($2); + destroy_block($4); + + if (!c) + YYABORT; + $$ = &c->entry; + } + | KW_CASE expr ':' + { + struct hlsl_ir_switch_case *c; + + c = hlsl_new_switch_case(ctx, $2, NULL, &@2); + + destroy_block($2); + + if (!c) + YYABORT; + $$ = &c->entry; + } + | KW_DEFAULT ':' statement_list + { + struct hlsl_ir_switch_case *c; + + c = hlsl_new_switch_case(ctx, NULL, $3, &@1); + + destroy_block($3); + + if (!c) + YYABORT; + $$ = &c->entry; + } + | KW_DEFAULT ':' + { + struct hlsl_ir_switch_case *c; + + if (!(c = hlsl_new_switch_case(ctx, NULL, NULL, &@1))) + YYABORT; + $$ = &c->entry; + } + +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); + } + | switch_cases switch_case + { + $$ = $1; + list_add_tail($$, $2); + } + expr_optional: %empty { diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c index 7d17ca8ce..c7cce8238 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,28 @@ 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 we're in a loop, we don't need to do anything here. We + * turned the return into a break, and that will already skip + * anything that comes after this "if" block. */ + if (!in_loop) + { + cf_instr = instr; + break; + } + } + } }
if (return_instr) @@ -2929,6 +2963,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 +2991,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 +3218,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 +3579,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 +3700,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 e0d49ec75..b58665a71 100644 --- a/tests/hlsl/switch.shader_test +++ b/tests/hlsl/switch.shader_test @@ -304,7 +304,7 @@ todo draw quad todo probe all rgba (5.0, 6.0, 7.0, 8.0)
% 'continue' is not supported in switches -[pixel shader fail] +[pixel shader fail todo] uint4 v;
float4 main() : sv_target