vkd3d-shader/hlsl: Partially defer continue resolution.

We need 'for' iter blocks to be separate for loop unrolling.
This commit is contained in:
Victor Chiletto 2024-10-16 17:19:36 -03:00 committed by Henri Verbeet
parent 5d8448a44e
commit 351d58a95b
Notes: Henri Verbeet 2024-12-12 17:48:02 +01:00
Approved-by: Francisco Casas (@fcasas)
Approved-by: Elizabeth Figura (@zfigura)
Approved-by: Henri Verbeet (@hverbeet)
Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/1053
4 changed files with 139 additions and 36 deletions

View File

@ -2064,7 +2064,7 @@ struct hlsl_ir_node *hlsl_new_jump(struct hlsl_ctx *ctx, enum hlsl_ir_jump_type
return &jump->node; return &jump->node;
} }
struct hlsl_ir_node *hlsl_new_loop(struct hlsl_ctx *ctx, struct hlsl_ir_node *hlsl_new_loop(struct hlsl_ctx *ctx, struct hlsl_block *iter,
struct hlsl_block *block, enum hlsl_ir_loop_unroll_type unroll_type, struct hlsl_block *block, enum hlsl_ir_loop_unroll_type unroll_type,
unsigned int unroll_limit, const struct vkd3d_shader_location *loc) unsigned int unroll_limit, const struct vkd3d_shader_location *loc)
{ {
@ -2076,6 +2076,10 @@ struct hlsl_ir_node *hlsl_new_loop(struct hlsl_ctx *ctx,
hlsl_block_init(&loop->body); hlsl_block_init(&loop->body);
hlsl_block_add_block(&loop->body, block); hlsl_block_add_block(&loop->body, block);
hlsl_block_init(&loop->iter);
if (iter)
hlsl_block_add_block(&loop->iter, iter);
loop->unroll_type = unroll_type; loop->unroll_type = unroll_type;
loop->unroll_limit = unroll_limit; loop->unroll_limit = unroll_limit;
return &loop->node; return &loop->node;
@ -2231,14 +2235,21 @@ static struct hlsl_ir_node *clone_load(struct hlsl_ctx *ctx, struct clone_instr_
static struct hlsl_ir_node *clone_loop(struct hlsl_ctx *ctx, struct clone_instr_map *map, struct hlsl_ir_loop *src) static struct hlsl_ir_node *clone_loop(struct hlsl_ctx *ctx, struct clone_instr_map *map, struct hlsl_ir_loop *src)
{ {
struct hlsl_block iter, body;
struct hlsl_ir_node *dst; struct hlsl_ir_node *dst;
struct hlsl_block body;
if (!clone_block(ctx, &body, &src->body, map)) if (!clone_block(ctx, &iter, &src->iter, map))
return NULL; return NULL;
if (!(dst = hlsl_new_loop(ctx, &body, src->unroll_type, src->unroll_limit, &src->node.loc))) if (!clone_block(ctx, &body, &src->body, map))
{ {
hlsl_block_cleanup(&iter);
return NULL;
}
if (!(dst = hlsl_new_loop(ctx, &iter, &body, src->unroll_type, src->unroll_limit, &src->node.loc)))
{
hlsl_block_cleanup(&iter);
hlsl_block_cleanup(&body); hlsl_block_cleanup(&body);
return NULL; return NULL;
} }
@ -3713,6 +3724,7 @@ static void free_ir_load(struct hlsl_ir_load *load)
static void free_ir_loop(struct hlsl_ir_loop *loop) static void free_ir_loop(struct hlsl_ir_loop *loop)
{ {
hlsl_block_cleanup(&loop->body); hlsl_block_cleanup(&loop->body);
hlsl_block_cleanup(&loop->iter);
vkd3d_free(loop); vkd3d_free(loop);
} }

View File

@ -666,11 +666,20 @@ enum hlsl_ir_loop_unroll_type
HLSL_IR_LOOP_FORCE_LOOP HLSL_IR_LOOP_FORCE_LOOP
}; };
enum hlsl_loop_type
{
HLSL_LOOP_FOR,
HLSL_LOOP_WHILE,
HLSL_LOOP_DO_WHILE
};
struct hlsl_ir_loop struct hlsl_ir_loop
{ {
struct hlsl_ir_node node; struct hlsl_ir_node node;
struct hlsl_block iter;
/* loop condition is stored in the body (as "if (!condition) break;") */ /* loop condition is stored in the body (as "if (!condition) break;") */
struct hlsl_block body; struct hlsl_block body;
enum hlsl_loop_type type;
unsigned int next_index; /* liveness index of the end of the loop */ unsigned int next_index; /* liveness index of the end of the loop */
unsigned int unroll_limit; unsigned int unroll_limit;
enum hlsl_ir_loop_unroll_type unroll_type; enum hlsl_ir_loop_unroll_type unroll_type;
@ -1550,8 +1559,9 @@ struct hlsl_ir_node *hlsl_new_compile(struct hlsl_ctx *ctx, enum hlsl_compile_ty
struct hlsl_block *args_instrs, const struct vkd3d_shader_location *loc); struct hlsl_block *args_instrs, const struct vkd3d_shader_location *loc);
struct hlsl_ir_node *hlsl_new_index(struct hlsl_ctx *ctx, struct hlsl_ir_node *val, struct hlsl_ir_node *hlsl_new_index(struct hlsl_ctx *ctx, struct hlsl_ir_node *val,
struct hlsl_ir_node *idx, const struct vkd3d_shader_location *loc); struct hlsl_ir_node *idx, const struct vkd3d_shader_location *loc);
struct hlsl_ir_node *hlsl_new_loop(struct hlsl_ctx *ctx, struct hlsl_ir_node *hlsl_new_loop(struct hlsl_ctx *ctx, struct hlsl_block *iter,
struct hlsl_block *block, enum hlsl_ir_loop_unroll_type unroll_type, unsigned int unroll_limit, const struct vkd3d_shader_location *loc); struct hlsl_block *block, enum hlsl_ir_loop_unroll_type unroll_type,
unsigned int unroll_limit, const struct vkd3d_shader_location *loc);
struct hlsl_ir_node *hlsl_new_resource_load(struct hlsl_ctx *ctx, struct hlsl_ir_node *hlsl_new_resource_load(struct hlsl_ctx *ctx,
const struct hlsl_resource_load_params *params, const struct vkd3d_shader_location *loc); const struct hlsl_resource_load_params *params, const struct vkd3d_shader_location *loc);
struct hlsl_ir_node *hlsl_new_resource_store(struct hlsl_ctx *ctx, const struct hlsl_deref *resource, struct hlsl_ir_node *hlsl_new_resource_store(struct hlsl_ctx *ctx, const struct hlsl_deref *resource,

View File

@ -555,13 +555,6 @@ static bool append_conditional_break(struct hlsl_ctx *ctx, struct hlsl_block *co
return true; return true;
} }
enum loop_type
{
LOOP_FOR,
LOOP_WHILE,
LOOP_DO_WHILE
};
static void check_attribute_list_for_duplicates(struct hlsl_ctx *ctx, const struct parse_attribute_list *attrs) static void check_attribute_list_for_duplicates(struct hlsl_ctx *ctx, const struct parse_attribute_list *attrs)
{ {
unsigned int i, j; unsigned int i, j;
@ -577,8 +570,8 @@ static void check_attribute_list_for_duplicates(struct hlsl_ctx *ctx, const stru
} }
} }
static void resolve_loop_continue(struct hlsl_ctx *ctx, struct hlsl_block *block, enum loop_type type, static void resolve_loop_continue(struct hlsl_ctx *ctx, struct hlsl_block *block,
struct hlsl_block *cond, struct hlsl_block *iter) enum hlsl_loop_type type, struct hlsl_block *cond)
{ {
struct hlsl_ir_node *instr, *next; struct hlsl_ir_node *instr, *next;
@ -588,8 +581,8 @@ static void resolve_loop_continue(struct hlsl_ctx *ctx, struct hlsl_block *block
{ {
struct hlsl_ir_if *iff = hlsl_ir_if(instr); struct hlsl_ir_if *iff = hlsl_ir_if(instr);
resolve_loop_continue(ctx, &iff->then_block, type, cond, iter); resolve_loop_continue(ctx, &iff->then_block, type, cond);
resolve_loop_continue(ctx, &iff->else_block, type, cond, iter); resolve_loop_continue(ctx, &iff->else_block, type, cond);
} }
else if (instr->type == HLSL_IR_JUMP) else if (instr->type == HLSL_IR_JUMP)
{ {
@ -599,7 +592,7 @@ static void resolve_loop_continue(struct hlsl_ctx *ctx, struct hlsl_block *block
if (jump->type != HLSL_IR_JUMP_UNRESOLVED_CONTINUE) if (jump->type != HLSL_IR_JUMP_UNRESOLVED_CONTINUE)
continue; continue;
if (type == LOOP_DO_WHILE) if (type == HLSL_LOOP_DO_WHILE)
{ {
if (!hlsl_clone_block(ctx, &cond_block, cond)) if (!hlsl_clone_block(ctx, &cond_block, cond))
return; return;
@ -610,13 +603,6 @@ static void resolve_loop_continue(struct hlsl_ctx *ctx, struct hlsl_block *block
} }
list_move_before(&instr->entry, &cond_block.instrs); list_move_before(&instr->entry, &cond_block.instrs);
} }
else if (type == LOOP_FOR)
{
if (!hlsl_clone_block(ctx, &cond_block, iter))
return;
list_move_before(&instr->entry, &cond_block.instrs);
}
jump->type = HLSL_IR_JUMP_CONTINUE;
} }
} }
} }
@ -740,7 +726,7 @@ static unsigned int evaluate_static_expression_as_uint(struct hlsl_ctx *ctx, str
return res.number.u; return res.number.u;
} }
static struct hlsl_block *create_loop(struct hlsl_ctx *ctx, enum loop_type type, static struct hlsl_block *create_loop(struct hlsl_ctx *ctx, enum hlsl_loop_type type,
const struct parse_attribute_list *attributes, struct hlsl_block *init, struct hlsl_block *cond, 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) struct hlsl_block *iter, struct hlsl_block *body, const struct vkd3d_shader_location *loc)
{ {
@ -792,7 +778,7 @@ static struct hlsl_block *create_loop(struct hlsl_ctx *ctx, enum loop_type type,
} }
} }
resolve_loop_continue(ctx, body, type, cond, iter); resolve_loop_continue(ctx, body, type, cond);
if (!init && !(init = make_empty_block(ctx))) if (!init && !(init = make_empty_block(ctx)))
goto oom; goto oom;
@ -800,15 +786,12 @@ static struct hlsl_block *create_loop(struct hlsl_ctx *ctx, enum loop_type type,
if (!append_conditional_break(ctx, cond)) if (!append_conditional_break(ctx, cond))
goto oom; goto oom;
if (iter) if (type == HLSL_LOOP_DO_WHILE)
hlsl_block_add_block(body, iter);
if (type == LOOP_DO_WHILE)
list_move_tail(&body->instrs, &cond->instrs); list_move_tail(&body->instrs, &cond->instrs);
else else
list_move_head(&body->instrs, &cond->instrs); list_move_head(&body->instrs, &cond->instrs);
if (!(loop = hlsl_new_loop(ctx, body, unroll_type, unroll_limit, loc))) if (!(loop = hlsl_new_loop(ctx, iter, body, unroll_type, unroll_limit, loc)))
goto oom; goto oom;
hlsl_block_add_instr(init, loop); hlsl_block_add_instr(init, loop);
@ -8831,25 +8814,25 @@ if_body:
loop_statement: loop_statement:
attribute_list_optional loop_scope_start KW_WHILE '(' expr ')' statement attribute_list_optional loop_scope_start KW_WHILE '(' expr ')' statement
{ {
$$ = create_loop(ctx, LOOP_WHILE, &$1, NULL, $5, NULL, $7, &@3); $$ = create_loop(ctx, HLSL_LOOP_WHILE, &$1, NULL, $5, NULL, $7, &@3);
hlsl_pop_scope(ctx); hlsl_pop_scope(ctx);
cleanup_parse_attribute_list(&$1); cleanup_parse_attribute_list(&$1);
} }
| attribute_list_optional loop_scope_start 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, $7, NULL, $4, &@3); $$ = create_loop(ctx, HLSL_LOOP_DO_WHILE, &$1, NULL, $7, NULL, $4, &@3);
hlsl_pop_scope(ctx); hlsl_pop_scope(ctx);
cleanup_parse_attribute_list(&$1); cleanup_parse_attribute_list(&$1);
} }
| attribute_list_optional loop_scope_start KW_FOR '(' 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, &@3); $$ = create_loop(ctx, HLSL_LOOP_FOR, &$1, $5, $6, $7, $9, &@3);
hlsl_pop_scope(ctx); hlsl_pop_scope(ctx);
cleanup_parse_attribute_list(&$1); cleanup_parse_attribute_list(&$1);
} }
| attribute_list_optional loop_scope_start KW_FOR '(' 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, &@3); $$ = create_loop(ctx, HLSL_LOOP_FOR, &$1, $5, $6, $7, $9, &@3);
hlsl_pop_scope(ctx); hlsl_pop_scope(ctx);
cleanup_parse_attribute_list(&$1); cleanup_parse_attribute_list(&$1);
} }

View File

@ -1954,6 +1954,7 @@ static bool copy_propagation_process_loop(struct hlsl_ctx *ctx, struct hlsl_ir_l
bool progress = false; bool progress = false;
copy_propagation_invalidate_from_block(ctx, state, &loop->body, loop->node.index); copy_propagation_invalidate_from_block(ctx, state, &loop->body, loop->node.index);
copy_propagation_invalidate_from_block(ctx, state, &loop->iter, loop->node.index);
copy_propagation_push_scope(state, ctx); copy_propagation_push_scope(state, ctx);
progress |= copy_propagation_transform_block(ctx, &loop->body, state); progress |= copy_propagation_transform_block(ctx, &loop->body, state);
@ -10177,6 +10178,101 @@ static bool unroll_loops(struct hlsl_ctx *ctx, struct hlsl_ir_node *node, void *
return true; return true;
} }
/* We could handle this at parse time. However, loop unrolling often needs to
* know the value of variables modified in the "iter" block. It is possible to
* detect that all exit paths of a loop body modify such variables in the same
* way, but difficult, and d3dcompiler does not attempt to do so.
* In fact, d3dcompiler is capable of unrolling the following loop:
* for (int i = 0; i < 10; ++i)
* {
* if (some_uniform > 4)
* continue;
* }
* but cannot unroll the same loop with "++i" moved to each exit path:
* for (int i = 0; i < 10;)
* {
* if (some_uniform > 4)
* {
* ++i;
* continue;
* }
* ++i;
* }
*/
static bool resolve_loops(struct hlsl_ctx *ctx, struct hlsl_ir_node *node, void *context)
{
struct hlsl_ir_loop *loop;
if (node->type != HLSL_IR_LOOP)
return true;
loop = hlsl_ir_loop(node);
hlsl_block_add_block(&loop->body, &loop->iter);
return true;
}
static void resolve_continues(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_loop *last_loop)
{
struct hlsl_ir_node *node;
LIST_FOR_EACH_ENTRY(node, &block->instrs, struct hlsl_ir_node, entry)
{
switch (node->type)
{
case HLSL_IR_LOOP:
{
struct hlsl_ir_loop *loop = hlsl_ir_loop(node);
resolve_continues(ctx, &loop->body, loop);
break;
}
case HLSL_IR_IF:
{
struct hlsl_ir_if *iff = hlsl_ir_if(node);
resolve_continues(ctx, &iff->then_block, last_loop);
resolve_continues(ctx, &iff->else_block, last_loop);
break;
}
case HLSL_IR_SWITCH:
{
struct hlsl_ir_switch *s = hlsl_ir_switch(node);
struct hlsl_ir_switch_case *c;
LIST_FOR_EACH_ENTRY(c, &s->cases, struct hlsl_ir_switch_case, entry)
{
resolve_continues(ctx, &c->body, last_loop);
}
break;
}
case HLSL_IR_JUMP:
{
struct hlsl_ir_jump *jump = hlsl_ir_jump(node);
if (jump->type != HLSL_IR_JUMP_UNRESOLVED_CONTINUE)
break;
if (last_loop->type == HLSL_LOOP_FOR)
{
struct hlsl_block draft;
if (!hlsl_clone_block(ctx, &draft, &last_loop->iter))
return;
list_move_before(&node->entry, &draft.instrs);
hlsl_block_cleanup(&draft);
}
jump->type = HLSL_IR_JUMP_CONTINUE;
break;
}
default:
break;
}
}
}
static void loop_unrolling_execute(struct hlsl_ctx *ctx, struct hlsl_block *block) static void loop_unrolling_execute(struct hlsl_ctx *ctx, struct hlsl_block *block)
{ {
bool progress; bool progress;
@ -10190,6 +10286,8 @@ static void loop_unrolling_execute(struct hlsl_ctx *ctx, struct hlsl_block *bloc
} while (progress); } while (progress);
hlsl_transform_ir(ctx, split_matrix_copies, block, NULL); hlsl_transform_ir(ctx, split_matrix_copies, block, NULL);
resolve_continues(ctx, block, NULL);
hlsl_transform_ir(ctx, resolve_loops, block, NULL);
hlsl_transform_ir(ctx, unroll_loops, block, block); hlsl_transform_ir(ctx, unroll_loops, block, block);
} }