Signed-off-by: Nikolay Sivov nsivov@codeweavers.com
-- v7: vkd3d-shader/tpf: Write 'continue' instruction. vkd3d-shader/hlsl: Handle 'continue' statements. vkd3d-shader/hlsl: Allow 'break' instructions in loops. tests: Add some tests for 'break'/'continue' in loops.
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- tests/hlsl/loop.shader_test | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+)
diff --git a/tests/hlsl/loop.shader_test b/tests/hlsl/loop.shader_test index 75a31f7af..9f8bcb92d 100644 --- a/tests/hlsl/loop.shader_test +++ b/tests/hlsl/loop.shader_test @@ -41,3 +41,62 @@ float4 main() : sv_target uniform 0 float 4.0 draw quad probe all rgba (20.0, 20.0, 20.0, 20.0) + +[pixel shader todo] +float a; + +float4 main() : sv_target +{ + int i = 0; + float res = a; + + while (i < 10) + { + if (i == 5) + { + res += 0.1f; + break; + } + res += 1.0f; + i++; + if (i == 2) continue; + res += 100.f; + } + + return res; +} + +[test] +uniform 0 float 4.0 +todo draw quad +todo probe all rgba (409.1, 409.1, 409.1, 409.1) + +[pixel shader todo] +float a; + +float4 main() : sv_target +{ + int i = 0; + float res = a; + + do + { + res += 1.0f; + if (i == 5) + { + res += 0.1f; + break; + } + i++; + if (i == 2) continue; + res += 100.f; + } + while (i < 10); + + return res; +} + +[test] +uniform 0 float 4.0 +todo draw quad +todo probe all rgba (410.1, 410.1, 410.1, 410.1)
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 | 53 +++++++++++++++++++++++++++++++------- tests/hlsl/for.shader_test | 7 +++++ 3 files changed, 53 insertions(+), 9 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index e45256bce..bf1c1dcb7 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -702,6 +702,8 @@ struct hlsl_scope struct rb_tree types; /* Scope containing this scope. This value is NULL for the global scope. */ struct hlsl_scope *upper; + /* The scope was created for the loop statement. */ + bool loop; };
struct hlsl_profile_info diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 8c327c79e..76ce9dc49 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -4587,6 +4587,14 @@ static void validate_texture_format_type(struct hlsl_ctx *ctx, struct hlsl_type } }
+static struct hlsl_scope *get_loop_scope(struct hlsl_scope *scope) +{ + if (scope->loop) + return scope; + + return scope->upper ? get_loop_scope(scope->upper) : NULL; +} + }
%locations @@ -5292,6 +5300,13 @@ scope_start: hlsl_push_scope(ctx); }
+loop_scope_start: + %empty + { + hlsl_push_scope(ctx); + ctx->cur_scope->loop = true; + } + var_identifier: VAR_IDENTIFIER | NEW_IDENTIFIER @@ -6104,7 +6119,25 @@ statement: | loop_statement
jump_statement: - KW_RETURN expr ';' + KW_BREAK ';' + { + struct hlsl_ir_node *jump; + + /* TODO: allow 'break' in the 'switch' statements. */ + + if (!get_loop_scope(ctx->cur_scope)) + { + hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, + "The 'break' statement must be used inside of a loop."); + } + + if (!($$ = make_empty_block(ctx))) + YYABORT; + if (!(jump = hlsl_new_jump(ctx, HLSL_IR_JUMP_BREAK, NULL, &@1))) + YYABORT; + hlsl_block_add_instr($$, jump); + } + | KW_RETURN expr ';' { $$ = $2; if (!add_return(ctx, $$, node_from_block($$), &@1)) @@ -6193,22 +6226,24 @@ if_body: }
loop_statement: - attribute_list_optional KW_WHILE '(' expr ')' statement + attribute_list_optional loop_scope_start KW_WHILE '(' expr ')' statement { - $$ = create_loop(ctx, LOOP_WHILE, &$1, NULL, $4, NULL, $6, &@2); + $$ = create_loop(ctx, LOOP_WHILE, &$1, NULL, $5, NULL, $7, &@3); + hlsl_pop_scope(ctx); } - | attribute_list_optional KW_DO statement KW_WHILE '(' expr ')' ';' + | attribute_list_optional loop_scope_start KW_DO statement KW_WHILE '(' expr ')' ';' { - $$ = create_loop(ctx, LOOP_DO_WHILE, &$1, NULL, $6, NULL, $3, &@2); + $$ = create_loop(ctx, LOOP_DO_WHILE, &$1, NULL, $7, NULL, $4, &@3); + hlsl_pop_scope(ctx); } - | attribute_list_optional KW_FOR '(' scope_start expr_statement expr_statement expr_optional ')' statement + | attribute_list_optional loop_scope_start KW_FOR '(' expr_statement expr_statement expr_optional ')' statement { - $$ = create_loop(ctx, LOOP_FOR, &$1, $5, $6, $7, $9, &@2); + $$ = create_loop(ctx, LOOP_FOR, &$1, $5, $6, $7, $9, &@3); hlsl_pop_scope(ctx); } - | attribute_list_optional KW_FOR '(' scope_start declaration expr_statement expr_optional ')' statement + | attribute_list_optional loop_scope_start KW_FOR '(' declaration expr_statement expr_optional ')' statement { - $$ = create_loop(ctx, LOOP_FOR, &$1, $5, $6, $7, $9, &@2); + $$ = create_loop(ctx, LOOP_FOR, &$1, $5, $6, $7, $9, &@3); hlsl_pop_scope(ctx); }
diff --git a/tests/hlsl/for.shader_test b/tests/hlsl/for.shader_test index b68969819..beb521e77 100644 --- a/tests/hlsl/for.shader_test +++ b/tests/hlsl/for.shader_test @@ -54,3 +54,10 @@ float4 main(float tex : texcoord) : sv_target } return float4(i, x, 0.0, 0.0); } + +[pixel shader fail] +float4 main() : sv_target +{ + break; + return float4(0.0, 0.0, 0.0, 0.0); +}
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- libs/vkd3d-shader/hlsl.c | 4 +++ libs/vkd3d-shader/hlsl.h | 4 +++ libs/vkd3d-shader/hlsl.y | 64 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c index 0bfba35f4..f74712a92 100644 --- a/libs/vkd3d-shader/hlsl.c +++ b/libs/vkd3d-shader/hlsl.c @@ -2558,6 +2558,10 @@ static void dump_ir_jump(struct vkd3d_string_buffer *buffer, const struct hlsl_i case HLSL_IR_JUMP_RETURN: vkd3d_string_buffer_printf(buffer, "return"); break; + + case HLSL_IR_JUMP_UNRESOLVED_CONTINUE: + vkd3d_string_buffer_printf(buffer, "unresolved_continue"); + break; } }
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h index bf1c1dcb7..798e8dfa8 100644 --- a/libs/vkd3d-shader/hlsl.h +++ b/libs/vkd3d-shader/hlsl.h @@ -577,6 +577,10 @@ enum hlsl_ir_jump_type HLSL_IR_JUMP_DISCARD_NEG, HLSL_IR_JUMP_DISCARD_NZ, HLSL_IR_JUMP_RETURN, + /* UNRESOLVED_CONTINUE type is used by the parser when 'continue' statement is found, + it never reaches code generation, and is resolved to CONTINUE type once iteration + and loop exit logic was properly applied. */ + HLSL_IR_JUMP_UNRESOLVED_CONTINUE, };
struct hlsl_ir_jump diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y index 76ce9dc49..786913922 100644 --- a/libs/vkd3d-shader/hlsl.y +++ b/libs/vkd3d-shader/hlsl.y @@ -464,6 +464,50 @@ static bool attribute_list_has_duplicates(const struct parse_attribute_list *att return false; }
+static void resolve_loop_continue(struct hlsl_ctx *ctx, struct hlsl_block *block, enum loop_type type, + struct hlsl_block *cond, struct hlsl_block *iter) +{ + struct hlsl_ir_node *instr, *next; + + LIST_FOR_EACH_ENTRY_SAFE(instr, next, &block->instrs, struct hlsl_ir_node, entry) + { + if (instr->type == HLSL_IR_IF) + { + struct hlsl_ir_if *iff = hlsl_ir_if(instr); + + resolve_loop_continue(ctx, &iff->then_block, type, cond, iter); + resolve_loop_continue(ctx, &iff->else_block, type, cond, iter); + } + else if (instr->type == HLSL_IR_JUMP) + { + struct hlsl_ir_jump *jump = hlsl_ir_jump(instr); + struct hlsl_block block; + + if (jump->type != HLSL_IR_JUMP_UNRESOLVED_CONTINUE) + continue; + + if (type == LOOP_DO_WHILE) + { + if (!hlsl_clone_block(ctx, &block, cond)) + return; + if (!append_conditional_break(ctx, &block)) + { + hlsl_block_cleanup(&block); + return; + } + list_move_before(&instr->entry, &block.instrs); + } + else if (type == LOOP_FOR) + { + if (!hlsl_clone_block(ctx, &block, iter)) + return; + list_move_before(&instr->entry, &block.instrs); + } + jump->type = HLSL_IR_JUMP_CONTINUE; + } + } +} + static struct hlsl_block *create_loop(struct hlsl_ctx *ctx, enum loop_type type, const struct parse_attribute_list *attributes, struct hlsl_block *init, struct hlsl_block *cond, struct hlsl_block *iter, struct hlsl_block *body, const struct vkd3d_shader_location *loc) @@ -501,6 +545,8 @@ static struct hlsl_block *create_loop(struct hlsl_ctx *ctx, enum loop_type type, } }
+ resolve_loop_continue(ctx, body, type, cond, iter); + if (!init && !(init = make_empty_block(ctx))) goto oom;
@@ -6137,6 +6183,24 @@ jump_statement: YYABORT; hlsl_block_add_instr($$, jump); } + | 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."); + } + + if (!($$ = make_empty_block(ctx))) + YYABORT; + + if (!(jump = hlsl_new_jump(ctx, HLSL_IR_JUMP_UNRESOLVED_CONTINUE, NULL, &@1))) + YYABORT; + hlsl_block_add_instr($$, jump); + } | KW_RETURN expr ';' { $$ = $2;
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- libs/vkd3d-shader/tpf.c | 4 ++++ tests/hlsl/for.shader_test | 4 ++-- tests/hlsl/loop.shader_test | 12 ++++++------ 3 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/libs/vkd3d-shader/tpf.c b/libs/vkd3d-shader/tpf.c index 0fc16b4c7..f92ef7ab8 100644 --- a/libs/vkd3d-shader/tpf.c +++ b/libs/vkd3d-shader/tpf.c @@ -5247,6 +5247,10 @@ static void write_sm4_jump(const struct tpf_writer *tpf, const struct hlsl_ir_ju instr.opcode = VKD3D_SM4_OP_BREAK; break;
+ case HLSL_IR_JUMP_CONTINUE: + instr.opcode = VKD3D_SM4_OP_CONTINUE; + break; + case HLSL_IR_JUMP_DISCARD_NZ: { instr.opcode = VKD3D_SM4_OP_DISCARD | VKD3D_SM4_CONDITIONAL_NZ; diff --git a/tests/hlsl/for.shader_test b/tests/hlsl/for.shader_test index beb521e77..e0b0edc11 100644 --- a/tests/hlsl/for.shader_test +++ b/tests/hlsl/for.shader_test @@ -4,7 +4,7 @@ void main(out float tex : texcoord, inout float4 pos : sv_position) tex = pos.x; }
-[pixel shader todo] +[pixel shader] float4 main(float tex : texcoord) : sv_target { int i; @@ -22,7 +22,7 @@ float4 main(float tex : texcoord) : sv_target }
[test] -todo draw quad +draw quad probe ( 0, 0, 159, 480) rgba (10.0, 35.0, 0.0, 0.0) probe (161, 0, 479, 480) rgba (10.0, 38.0, 0.0, 0.0) probe (481, 0, 640, 480) rgba ( 5.0, 10.0, 0.0, 0.0) diff --git a/tests/hlsl/loop.shader_test b/tests/hlsl/loop.shader_test index 9f8bcb92d..bf26b69cf 100644 --- a/tests/hlsl/loop.shader_test +++ b/tests/hlsl/loop.shader_test @@ -42,7 +42,7 @@ uniform 0 float 4.0 draw quad probe all rgba (20.0, 20.0, 20.0, 20.0)
-[pixel shader todo] +[pixel shader] float a;
float4 main() : sv_target @@ -68,10 +68,10 @@ float4 main() : sv_target
[test] uniform 0 float 4.0 -todo draw quad -todo probe all rgba (409.1, 409.1, 409.1, 409.1) +draw quad +probe all rgba (409.1, 409.1, 409.1, 409.1)
-[pixel shader todo] +[pixel shader] float a;
float4 main() : sv_target @@ -98,5 +98,5 @@ float4 main() : sv_target
[test] uniform 0 float 4.0 -todo draw quad -todo probe all rgba (410.1, 410.1, 410.1, 410.1) +draw quad +probe all rgba (410.1, 410.1, 410.1, 410.1)
On Fri Oct 6 12:50:52 2023 +0000, Nikolay Sivov wrote:
changed this line in [version 7 of the diff](/wine/vkd3d/-/merge_requests/245/diffs?diff_id=74318&start_sha=87c0db74948f40c64d90ec45b0afc2cad5ca208d#b4b2f0df64f7fbb515484cb602382012595ddfb6_577_577)
I renamed it, and added a comment too. Please take a look.
This merge request was approved by Zebediah Figura.
This merge request was approved by Henri Verbeet.