This is useful for replacing one instruction with another of a different type, which is useful for some optimizations such as constant folding.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/d3dcompiler_43/d3dcompiler_private.h | 33 ++++++++++++--- dlls/d3dcompiler_43/hlsl.y | 49 +++++++++++----------- dlls/d3dcompiler_43/utils.c | 50 ++++++++++++++--------- 3 files changed, 83 insertions(+), 49 deletions(-)
diff --git a/dlls/d3dcompiler_43/d3dcompiler_private.h b/dlls/d3dcompiler_43/d3dcompiler_private.h index 50457b2e64e..23eff210940 100644 --- a/dlls/d3dcompiler_43/d3dcompiler_private.h +++ b/dlls/d3dcompiler_43/d3dcompiler_private.h @@ -662,6 +662,8 @@ struct hlsl_ir_node enum hlsl_ir_node_type type; struct hlsl_type *data_type;
+ struct list uses; + struct source_location loc;
/* Liveness ranges. "index" is the index of this instruction. Since this is @@ -671,6 +673,12 @@ struct hlsl_ir_node unsigned int index, last_read; };
+struct hlsl_src +{ + struct hlsl_ir_node *node; + struct list entry; +}; + #define HLSL_STORAGE_EXTERN 0x00000001 #define HLSL_STORAGE_NOINTERPOLATION 0x00000002 #define HLSL_MODIFIER_PRECISE 0x00000004 @@ -733,7 +741,7 @@ struct hlsl_ir_function_decl struct hlsl_ir_if { struct hlsl_ir_node node; - struct hlsl_ir_node *condition; + struct hlsl_src condition; struct list *then_instrs; struct list *else_instrs; }; @@ -817,7 +825,7 @@ struct hlsl_ir_expr { struct hlsl_ir_node node; enum hlsl_ir_expr_op op; - struct hlsl_ir_node *operands[3]; + struct hlsl_src operands[3]; };
enum hlsl_ir_jump_type @@ -837,14 +845,14 @@ struct hlsl_ir_jump struct hlsl_ir_swizzle { struct hlsl_ir_node node; - struct hlsl_ir_node *val; + struct hlsl_src val; DWORD swizzle; };
struct hlsl_deref { struct hlsl_ir_var *var; - struct hlsl_ir_node *offset; + struct hlsl_src offset; };
struct hlsl_ir_load @@ -857,7 +865,7 @@ struct hlsl_ir_assignment { struct hlsl_ir_node node; struct hlsl_deref lhs; - struct hlsl_ir_node *rhs; + struct hlsl_src rhs; unsigned char writemask; };
@@ -1055,6 +1063,21 @@ static inline void init_node(struct hlsl_ir_node *node, enum hlsl_ir_node_type t node->type = type; node->data_type = data_type; node->loc = loc; + list_init(&node->uses); +} + +static inline void hlsl_src_from_node(struct hlsl_src *src, struct hlsl_ir_node *node) +{ + src->node = node; + if (node) + list_add_tail(&node->uses, &src->entry); +} + +static inline void hlsl_src_remove(struct hlsl_src *src) +{ + if (src->node) + list_remove(&src->entry); + src->node = NULL; }
struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lhs, diff --git a/dlls/d3dcompiler_43/hlsl.y b/dlls/d3dcompiler_43/hlsl.y index 21828337939..475fd414fd7 100644 --- a/dlls/d3dcompiler_43/hlsl.y +++ b/dlls/d3dcompiler_43/hlsl.y @@ -308,7 +308,7 @@ static BOOL append_conditional_break(struct list *cond_list) return FALSE; } init_node(&iff->node, HLSL_IR_IF, NULL, condition->loc); - iff->condition = not; + hlsl_src_from_node(&iff->condition, not); list_add_tail(cond_list, &iff->node.entry);
if (!(iff->then_instrs = d3dcompiler_alloc(sizeof(*iff->then_instrs)))) @@ -421,7 +421,7 @@ static struct hlsl_ir_swizzle *new_swizzle(DWORD s, unsigned int components, return NULL; init_node(&swizzle->node, HLSL_IR_SWIZZLE, new_hlsl_type(NULL, HLSL_CLASS_VECTOR, val->data_type->base_type, components, 1), *loc); - swizzle->val = val; + hlsl_src_from_node(&swizzle->val, val); swizzle->swizzle = s; return swizzle; } @@ -551,8 +551,8 @@ static struct hlsl_ir_assignment *new_assignment(struct hlsl_ir_var *var, struct
init_node(&assign->node, HLSL_IR_ASSIGNMENT, NULL, loc); assign->lhs.var = var; - assign->lhs.offset = offset; - assign->rhs = rhs; + hlsl_src_from_node(&assign->lhs.offset, offset); + hlsl_src_from_node(&assign->rhs, rhs); assign->writemask = writemask; return assign; } @@ -616,7 +616,7 @@ struct hlsl_ir_node *new_unary_expr(enum hlsl_ir_expr_op op, struct hlsl_ir_node return NULL; init_node(&expr->node, HLSL_IR_EXPR, arg->data_type, loc); expr->op = op; - expr->operands[0] = arg; + hlsl_src_from_node(&expr->operands[0], arg); return &expr->node; }
@@ -631,8 +631,8 @@ struct hlsl_ir_node *new_binary_expr(enum hlsl_ir_expr_op op, return NULL; init_node(&expr->node, HLSL_IR_EXPR, arg1->data_type, arg1->loc); expr->op = op; - expr->operands[0] = arg1; - expr->operands[1] = arg2; + hlsl_src_from_node(&expr->operands[0], arg1); + hlsl_src_from_node(&expr->operands[1], arg2); return &expr->node; }
@@ -662,9 +662,9 @@ static struct hlsl_ir_load *add_load(struct list *instrs, struct hlsl_ir_node *v const struct hlsl_deref *src = &load_from_node(var_node)->src;
var = src->var; - if (src->offset) + if (src->offset.node) { - if (!(add = new_binary_expr(HLSL_IR_BINOP_ADD, src->offset, offset))) + if (!(add = new_binary_expr(HLSL_IR_BINOP_ADD, src->offset.node, offset))) return NULL; list_add_tail(instrs, &add->entry); offset = add; @@ -691,7 +691,7 @@ static struct hlsl_ir_load *add_load(struct list *instrs, struct hlsl_ir_node *v return NULL; init_node(&load->node, HLSL_IR_LOAD, data_type, loc); load->src.var = var; - load->src.offset = offset; + hlsl_src_from_node(&load->src.offset, offset); list_add_tail(instrs, &load->node.entry); return load; } @@ -2212,16 +2212,18 @@ jump_statement: selection_statement: KW_IF '(' expr ')' if_body { struct hlsl_ir_if *instr = d3dcompiler_alloc(sizeof(*instr)); + struct hlsl_ir_node *condition = node_from_list($3); + if (!instr) { ERR("Out of memory\n"); YYABORT; } init_node(&instr->node, HLSL_IR_IF, NULL, get_location(&@1)); - instr->condition = node_from_list($3); + hlsl_src_from_node(&instr->condition, condition); instr->then_instrs = $5.then_instrs; instr->else_instrs = $5.else_instrs; - if (instr->condition->data_type->dimx > 1 || instr->condition->data_type->dimy > 1) + if (condition->data_type->dimx > 1 || condition->data_type->dimy > 1) { hlsl_report_message(instr->node.loc, HLSL_LEVEL_ERROR, "if condition requires a scalar"); @@ -2883,19 +2885,18 @@ static void compute_liveness_recurse(struct list *instrs, unsigned int loop_firs var = assignment->lhs.var; if (!var->first_write) var->first_write = loop_first ? min(instr->index, loop_first) : instr->index; - assignment->rhs->last_read = instr->index; - if (assignment->lhs.offset) - assignment->lhs.offset->last_read = instr->index; + assignment->rhs.node->last_read = instr->index; + if (assignment->lhs.offset.node) + assignment->lhs.offset.node->last_read = instr->index; break; } case HLSL_IR_EXPR: { struct hlsl_ir_expr *expr = expr_from_node(instr); - expr->operands[0]->last_read = instr->index; - if (expr->operands[1]) - expr->operands[1]->last_read = instr->index; - if (expr->operands[2]) - expr->operands[2]->last_read = instr->index; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(expr->operands) && expr->operands[i].node; ++i) + expr->operands[i].node->last_read = instr->index; break; } case HLSL_IR_IF: @@ -2904,7 +2905,7 @@ static void compute_liveness_recurse(struct list *instrs, unsigned int loop_firs compute_liveness_recurse(iff->then_instrs, loop_first, loop_last); if (iff->else_instrs) compute_liveness_recurse(iff->else_instrs, loop_first, loop_last); - iff->condition->last_read = instr->index; + iff->condition.node->last_read = instr->index; break; } case HLSL_IR_LOAD: @@ -2912,8 +2913,8 @@ static void compute_liveness_recurse(struct list *instrs, unsigned int loop_firs struct hlsl_ir_load *load = load_from_node(instr); var = load->src.var; var->last_read = loop_last ? max(instr->index, loop_last) : instr->index; - if (load->src.offset) - load->src.offset->last_read = instr->index; + if (load->src.offset.node) + load->src.offset.node->last_read = instr->index; break; } case HLSL_IR_LOOP: @@ -2926,7 +2927,7 @@ static void compute_liveness_recurse(struct list *instrs, unsigned int loop_firs case HLSL_IR_SWIZZLE: { struct hlsl_ir_swizzle *swizzle = swizzle_from_node(instr); - swizzle->val->last_read = instr->index; + swizzle->val.node->last_read = instr->index; break; } case HLSL_IR_CONSTANT: diff --git a/dlls/d3dcompiler_43/utils.c b/dlls/d3dcompiler_43/utils.c index 30aa9de1dd4..4bc3b9b0a64 100644 --- a/dlls/d3dcompiler_43/utils.c +++ b/dlls/d3dcompiler_43/utils.c @@ -1369,9 +1369,8 @@ struct hlsl_ir_expr *add_expr(struct list *instrs, enum hlsl_ir_expr_op op, stru return NULL; init_node(&expr->node, HLSL_IR_EXPR, type, *loc); expr->op = op; - expr->operands[0] = operands[0]; - expr->operands[1] = operands[1]; - expr->operands[2] = operands[2]; + for (i = 0; i <= 2; ++i) + hlsl_src_from_node(&expr->operands[i], operands[i]); list_add_tail(instrs, &expr->node.entry);
return expr; @@ -1485,11 +1484,12 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh if (lhs->data_type->type == HLSL_CLASS_MATRIX) FIXME("Assignments with writemasks and matrices on lhs are not supported yet.\n");
- lhs_inner = swizzle->val; + lhs_inner = swizzle->val.node; + hlsl_src_remove(&swizzle->val); list_remove(&lhs->entry);
list_add_after(&rhs->entry, &lhs->entry); - swizzle->val = rhs; + hlsl_src_from_node(&swizzle->val, rhs); if (!invert_swizzle(&swizzle->swizzle, &writemask, &width)) { hlsl_report_message(lhs->loc, HLSL_LEVEL_ERROR, "invalid writemask"); @@ -1513,7 +1513,8 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh
init_node(&assign->node, HLSL_IR_ASSIGNMENT, lhs_type, lhs->loc); assign->writemask = writemask; - assign->lhs = load_from_node(lhs)->src; + assign->lhs.var = load_from_node(lhs)->src.var; + hlsl_src_from_node(&assign->lhs.offset, load_from_node(lhs)->src.offset.node); if (assign_op != ASSIGN_OP_ASSIGN) { enum hlsl_ir_expr_op op = op_from_assignment(assign_op); @@ -1524,7 +1525,7 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh list_add_after(&rhs->entry, &expr->entry); rhs = expr; } - assign->rhs = rhs; + hlsl_src_from_node(&assign->rhs, rhs); list_add_tail(instrs, &assign->node.entry);
return &assign->node; @@ -1787,12 +1788,12 @@ static void debug_dump_instr_list(const struct list *list) } }
-static void debug_dump_src(const struct hlsl_ir_node *node) +static void debug_dump_src(const struct hlsl_src *src) { - if (node->index) - wine_dbg_printf("@%u", node->index); + if (src->node->index) + wine_dbg_printf("@%u", src->node->index); else - wine_dbg_printf("%p", node); + wine_dbg_printf("%p", src->node); }
static void debug_dump_ir_var(const struct hlsl_ir_var *var) @@ -1806,15 +1807,15 @@ static void debug_dump_ir_var(const struct hlsl_ir_var *var)
static void debug_dump_deref(const struct hlsl_deref *deref) { - if (deref->offset) + if (deref->offset.node) /* Print the variable's type for convenience. */ wine_dbg_printf("(%s %s)", debug_hlsl_type(deref->var->data_type), deref->var->name); else wine_dbg_printf("%s", deref->var->name); - if (deref->offset) + if (deref->offset.node) { wine_dbg_printf("["); - debug_dump_src(deref->offset); + debug_dump_src(&deref->offset); wine_dbg_printf("]"); } } @@ -1942,9 +1943,9 @@ static void debug_dump_ir_expr(const struct hlsl_ir_expr *expr) unsigned int i;
wine_dbg_printf("%s (", debug_expr_op(expr)); - for (i = 0; i < 3 && expr->operands[i]; ++i) + for (i = 0; i < 3 && expr->operands[i].node; ++i) { - debug_dump_src(expr->operands[i]); + debug_dump_src(&expr->operands[i]); wine_dbg_printf(" "); } wine_dbg_printf(")"); @@ -1976,7 +1977,7 @@ static void debug_dump_ir_assignment(const struct hlsl_ir_assignment *assign) if (assign->writemask != BWRITERSP_WRITEMASK_ALL) wine_dbg_printf("%s", debug_writemask(assign->writemask)); wine_dbg_printf(" "); - debug_dump_src(assign->rhs); + debug_dump_src(&assign->rhs); wine_dbg_printf(")"); }
@@ -1984,9 +1985,9 @@ static void debug_dump_ir_swizzle(const struct hlsl_ir_swizzle *swizzle) { unsigned int i;
- debug_dump_src(swizzle->val); + debug_dump_src(&swizzle->val); wine_dbg_printf("."); - if (swizzle->val->data_type->dimy > 1) + if (swizzle->val.node->data_type->dimy > 1) { for (i = 0; i < swizzle->node.data_type->dimx; ++i) wine_dbg_printf("_m%u%u", (swizzle->swizzle >> i * 8) & 0xf, (swizzle->swizzle >> (i * 8 + 4)) & 0xf); @@ -2022,7 +2023,7 @@ static void debug_dump_ir_jump(const struct hlsl_ir_jump *jump) static void debug_dump_ir_if(const struct hlsl_ir_if *if_node) { wine_dbg_printf("if ("); - debug_dump_src(if_node->condition); + debug_dump_src(&if_node->condition); wine_dbg_printf(")\n{\n"); debug_dump_instr_list(if_node->then_instrs); wine_dbg_printf("}\n"); @@ -2135,21 +2136,29 @@ static void free_ir_constant(struct hlsl_ir_constant *constant)
static void free_ir_load(struct hlsl_ir_load *load) { + hlsl_src_remove(&load->src.offset); d3dcompiler_free(load); }
static void free_ir_swizzle(struct hlsl_ir_swizzle *swizzle) { + hlsl_src_remove(&swizzle->val); d3dcompiler_free(swizzle); }
static void free_ir_expr(struct hlsl_ir_expr *expr) { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(expr->operands); ++i) + hlsl_src_remove(&expr->operands[i]); d3dcompiler_free(expr); }
static void free_ir_assignment(struct hlsl_ir_assignment *assignment) { + hlsl_src_remove(&assignment->rhs); + hlsl_src_remove(&assignment->lhs.offset); d3dcompiler_free(assignment); }
@@ -2157,6 +2166,7 @@ static void free_ir_if(struct hlsl_ir_if *if_node) { free_instr_list(if_node->then_instrs); free_instr_list(if_node->else_instrs); + hlsl_src_remove(&if_node->condition); d3dcompiler_free(if_node); }
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/d3dcompiler_43/hlsl.y | 101 +++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+)
diff --git a/dlls/d3dcompiler_43/hlsl.y b/dlls/d3dcompiler_43/hlsl.y index 475fd414fd7..15bd0d007b9 100644 --- a/dlls/d3dcompiler_43/hlsl.y +++ b/dlls/d3dcompiler_43/hlsl.y @@ -2838,6 +2838,105 @@ static void dump_function(struct wine_rb_entry *entry, void *context) wine_rb_for_each_entry(&func->overloads, dump_function_decl, NULL); }
+static BOOL transform_ir(BOOL (*func)(struct hlsl_ir_node *), struct list *instrs) +{ + struct hlsl_ir_node *instr, *next; + BOOL progress = 0; + + LIST_FOR_EACH_ENTRY_SAFE(instr, next, instrs, struct hlsl_ir_node, entry) + { + if (instr->type == HLSL_IR_IF) + { + struct hlsl_ir_if *iff = if_from_node(instr); + progress |= transform_ir(func, iff->then_instrs); + if (iff->else_instrs) + progress |= transform_ir(func, iff->else_instrs); + } + else if (instr->type == HLSL_IR_LOOP) + progress |= transform_ir(func, loop_from_node(instr)->body); + + progress |= func(instr); + } + + return progress; +} + +static void replace_node(struct hlsl_ir_node *old, struct hlsl_ir_node *new) +{ + struct hlsl_src *src, *next; + + LIST_FOR_EACH_ENTRY_SAFE(src, next, &old->uses, struct hlsl_src, entry) + { + hlsl_src_remove(src); + hlsl_src_from_node(src, new); + } + list_add_before(&old->entry, &new->entry); + list_remove(&old->entry); + free_instr(old); +} + +static BOOL fold_constants(struct hlsl_ir_node *instr) +{ + struct hlsl_ir_constant *arg1, *arg2 = NULL, *res; + struct hlsl_ir_expr *expr; + unsigned int i; + + if (instr->type != HLSL_IR_EXPR) + return FALSE; + expr = expr_from_node(instr); + + for (i = 0; i < ARRAY_SIZE(expr->operands); ++i) + { + if (expr->operands[i].node && expr->operands[i].node->type != HLSL_IR_CONSTANT) + return FALSE; + } + arg1 = constant_from_node(expr->operands[0].node); + if (expr->operands[1].node) + arg2 = constant_from_node(expr->operands[1].node); + + if (!(res = d3dcompiler_alloc(sizeof(*res)))) + { + hlsl_ctx.status = PARSE_ERR; + return FALSE; + } + init_node(&res->node, HLSL_IR_CONSTANT, instr->data_type, instr->loc); + + switch (instr->data_type->base_type) + { + case HLSL_TYPE_UINT: + { + unsigned int i; + + switch (expr->op) + { + case HLSL_IR_BINOP_ADD: + for (i = 0; i < 16; ++i) + res->value.u[i] = arg1->value.u[i] + arg2->value.u[i]; + break; + + case HLSL_IR_BINOP_MUL: + for (i = 0; i < 16; ++i) + res->value.u[i] = arg1->value.u[i] * arg2->value.u[i]; + break; + + default: + FIXME("Fold uint expr %#x.\n", expr->op); + d3dcompiler_free(res); + return FALSE; + } + break; + } + + default: + FIXME("Fold %s expr %#x.\n", debug_base_type(instr->data_type), expr->op); + d3dcompiler_free(res); + return FALSE; + } + + replace_node(&expr->node, &res->node); + return TRUE; +} + /* Allocate a unique, ordered index to each instruction, which will be used for * computing liveness ranges. */ static unsigned int index_instructions(struct list *instrs, unsigned int index) @@ -3009,6 +3108,8 @@ HRESULT parse_hlsl(enum shader_type type, DWORD major, DWORD minor,
list_move_head(entry_func->body, &hlsl_ctx.static_initializers);
+ while (transform_ir(fold_constants, entry_func->body)); + /* Index 0 means unused; index 1 means function entry, so start at 2. */ index_instructions(entry_func->body, 2);
On Tue, Jun 30, 2020 at 1:56 AM Zebediah Figura z.figura12@gmail.com wrote:
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
dlls/d3dcompiler_43/hlsl.y | 101 +++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+)
diff --git a/dlls/d3dcompiler_43/hlsl.y b/dlls/d3dcompiler_43/hlsl.y index 475fd414fd7..15bd0d007b9 100644 --- a/dlls/d3dcompiler_43/hlsl.y +++ b/dlls/d3dcompiler_43/hlsl.y @@ -2838,6 +2838,105 @@ static void dump_function(struct wine_rb_entry *entry, void *context) wine_rb_for_each_entry(&func->overloads, dump_function_decl, NULL); }
+static BOOL transform_ir(BOOL (*func)(struct hlsl_ir_node *), struct list *instrs)
Since this is going to be a pretty generic helper: we might want to pass some "void *context" to func.
+{
- struct hlsl_ir_node *instr, *next;
- BOOL progress = 0;
- LIST_FOR_EACH_ENTRY_SAFE(instr, next, instrs, struct hlsl_ir_node, entry)
- {
if (instr->type == HLSL_IR_IF)
{
struct hlsl_ir_if *iff = if_from_node(instr);
progress |= transform_ir(func, iff->then_instrs);
if (iff->else_instrs)
progress |= transform_ir(func, iff->else_instrs);
}
I like to have a blank line between declarations and other statements.
else if (instr->type == HLSL_IR_LOOP)
progress |= transform_ir(func, loop_from_node(instr)->body);
Unrelated to the patch: maybe we should always initialize else_instrs? I think that would get rid of a bunch of checks with no drawback.
progress |= func(instr);
- }
- return progress;
+}
+static void replace_node(struct hlsl_ir_node *old, struct hlsl_ir_node *new) +{
- struct hlsl_src *src, *next;
- LIST_FOR_EACH_ENTRY_SAFE(src, next, &old->uses, struct hlsl_src, entry)
- {
hlsl_src_remove(src);
hlsl_src_from_node(src, new);
- }
- list_add_before(&old->entry, &new->entry);
- list_remove(&old->entry);
- free_instr(old);
+}
+static BOOL fold_constants(struct hlsl_ir_node *instr) +{
- struct hlsl_ir_constant *arg1, *arg2 = NULL, *res;
- struct hlsl_ir_expr *expr;
- unsigned int i;
- if (instr->type != HLSL_IR_EXPR)
return FALSE;
- expr = expr_from_node(instr);
- for (i = 0; i < ARRAY_SIZE(expr->operands); ++i)
- {
if (expr->operands[i].node && expr->operands[i].node->type != HLSL_IR_CONSTANT)
return FALSE;
- }
- arg1 = constant_from_node(expr->operands[0].node);
- if (expr->operands[1].node)
arg2 = constant_from_node(expr->operands[1].node);
- if (!(res = d3dcompiler_alloc(sizeof(*res))))
- {
hlsl_ctx.status = PARSE_ERR;
return FALSE;
- }
- init_node(&res->node, HLSL_IR_CONSTANT, instr->data_type, instr->loc);
- switch (instr->data_type->base_type)
- {
case HLSL_TYPE_UINT:
{
unsigned int i;
switch (expr->op)
{
case HLSL_IR_BINOP_ADD:
for (i = 0; i < 16; ++i)
res->value.u[i] = arg1->value.u[i] + arg2->value.u[i];
break;
case HLSL_IR_BINOP_MUL:
for (i = 0; i < 16; ++i)
res->value.u[i] = arg1->value.u[i] * arg2->value.u[i];
break;
I'd prefer if this only operated on the valid components: maybe it's not going to be any faster but I feel it would be less confusing and it also happens to be "partial constants ready" (whether we go ahead with that or not).
default:
FIXME("Fold uint expr %#x.\n", expr->op);
d3dcompiler_free(res);
return FALSE;
}
break;
}
default:
FIXME("Fold %s expr %#x.\n", debug_base_type(instr->data_type), expr->op);
d3dcompiler_free(res);
return FALSE;
- }
- replace_node(&expr->node, &res->node);
- return TRUE;
+}
/* Allocate a unique, ordered index to each instruction, which will be used for
- computing liveness ranges. */
static unsigned int index_instructions(struct list *instrs, unsigned int index) @@ -3009,6 +3108,8 @@ HRESULT parse_hlsl(enum shader_type type, DWORD major, DWORD minor,
list_move_head(entry_func->body, &hlsl_ctx.static_initializers);
- while (transform_ir(fold_constants, entry_func->body));
- /* Index 0 means unused; index 1 means function entry, so start at 2. */ index_instructions(entry_func->body, 2);
As we discussed elsewhere, we probably want to move transform_ir() and its calls out of hlsl.y.
On 7/2/20 11:15 AM, Matteo Bruni wrote:
On Tue, Jun 30, 2020 at 1:56 AM Zebediah Figura z.figura12@gmail.com wrote:
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
dlls/d3dcompiler_43/hlsl.y | 101 +++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+)
diff --git a/dlls/d3dcompiler_43/hlsl.y b/dlls/d3dcompiler_43/hlsl.y index 475fd414fd7..15bd0d007b9 100644 --- a/dlls/d3dcompiler_43/hlsl.y +++ b/dlls/d3dcompiler_43/hlsl.y @@ -2838,6 +2838,105 @@ static void dump_function(struct wine_rb_entry *entry, void *context) wine_rb_for_each_entry(&func->overloads, dump_function_decl, NULL); }
+static BOOL transform_ir(BOOL (*func)(struct hlsl_ir_node *), struct list *instrs)
Since this is going to be a pretty generic helper: we might want to pass some "void *context" to func.
Yes, probably. In that case I guess it'd be also useful for the RA passes I have now.
+{
- struct hlsl_ir_node *instr, *next;
- BOOL progress = 0;
- LIST_FOR_EACH_ENTRY_SAFE(instr, next, instrs, struct hlsl_ir_node, entry)
- {
if (instr->type == HLSL_IR_IF)
{
struct hlsl_ir_if *iff = if_from_node(instr);
progress |= transform_ir(func, iff->then_instrs);
if (iff->else_instrs)
progress |= transform_ir(func, iff->else_instrs);
}
I like to have a blank line between declarations and other statements.
Oops, missed that one :-/
else if (instr->type == HLSL_IR_LOOP)
progress |= transform_ir(func, loop_from_node(instr)->body);
Unrelated to the patch: maybe we should always initialize else_instrs? I think that would get rid of a bunch of checks with no drawback.
Besides another heap allocation (though maybe we could just store those inline instead? I don't think there's any reason why not), but yes, probably.
progress |= func(instr);
- }
- return progress;
+}
+static void replace_node(struct hlsl_ir_node *old, struct hlsl_ir_node *new) +{
- struct hlsl_src *src, *next;
- LIST_FOR_EACH_ENTRY_SAFE(src, next, &old->uses, struct hlsl_src, entry)
- {
hlsl_src_remove(src);
hlsl_src_from_node(src, new);
- }
- list_add_before(&old->entry, &new->entry);
- list_remove(&old->entry);
- free_instr(old);
+}
+static BOOL fold_constants(struct hlsl_ir_node *instr) +{
- struct hlsl_ir_constant *arg1, *arg2 = NULL, *res;
- struct hlsl_ir_expr *expr;
- unsigned int i;
- if (instr->type != HLSL_IR_EXPR)
return FALSE;
- expr = expr_from_node(instr);
- for (i = 0; i < ARRAY_SIZE(expr->operands); ++i)
- {
if (expr->operands[i].node && expr->operands[i].node->type != HLSL_IR_CONSTANT)
return FALSE;
- }
- arg1 = constant_from_node(expr->operands[0].node);
- if (expr->operands[1].node)
arg2 = constant_from_node(expr->operands[1].node);
- if (!(res = d3dcompiler_alloc(sizeof(*res))))
- {
hlsl_ctx.status = PARSE_ERR;
return FALSE;
- }
- init_node(&res->node, HLSL_IR_CONSTANT, instr->data_type, instr->loc);
- switch (instr->data_type->base_type)
- {
case HLSL_TYPE_UINT:
{
unsigned int i;
switch (expr->op)
{
case HLSL_IR_BINOP_ADD:
for (i = 0; i < 16; ++i)
res->value.u[i] = arg1->value.u[i] + arg2->value.u[i];
break;
case HLSL_IR_BINOP_MUL:
for (i = 0; i < 16; ++i)
res->value.u[i] = arg1->value.u[i] * arg2->value.u[i];
break;
I'd prefer if this only operated on the valid components: maybe it's not going to be any faster but I feel it would be less confusing and it also happens to be "partial constants ready" (whether we go ahead with that or not).
Sure.
default:
FIXME("Fold uint expr %#x.\n", expr->op);
d3dcompiler_free(res);
return FALSE;
}
break;
}
default:
FIXME("Fold %s expr %#x.\n", debug_base_type(instr->data_type), expr->op);
d3dcompiler_free(res);
return FALSE;
- }
- replace_node(&expr->node, &res->node);
- return TRUE;
+}
/* Allocate a unique, ordered index to each instruction, which will be used for
- computing liveness ranges. */
static unsigned int index_instructions(struct list *instrs, unsigned int index) @@ -3009,6 +3108,8 @@ HRESULT parse_hlsl(enum shader_type type, DWORD major, DWORD minor,
list_move_head(entry_func->body, &hlsl_ctx.static_initializers);
- while (transform_ir(fold_constants, entry_func->body));
- /* Index 0 means unused; index 1 means function entry, so start at 2. */ index_instructions(entry_func->body, 2);
As we discussed elsewhere, we probably want to move transform_ir() and its calls out of hlsl.y.
Yep, will do...
On Thu, Jul 2, 2020 at 6:47 PM Zebediah Figura zfigura@codeweavers.com wrote:
On 7/2/20 11:15 AM, Matteo Bruni wrote:
On Tue, Jun 30, 2020 at 1:56 AM Zebediah Figura z.figura12@gmail.com wrote:
+{
- struct hlsl_ir_node *instr, *next;
- BOOL progress = 0;
- LIST_FOR_EACH_ENTRY_SAFE(instr, next, instrs, struct hlsl_ir_node, entry)
- {
if (instr->type == HLSL_IR_IF)
{
struct hlsl_ir_if *iff = if_from_node(instr);
progress |= transform_ir(func, iff->then_instrs);
if (iff->else_instrs)
progress |= transform_ir(func, iff->else_instrs);
}
I like to have a blank line between declarations and other statements.
Oops, missed that one :-/
No problem :)
else if (instr->type == HLSL_IR_LOOP)
progress |= transform_ir(func, loop_from_node(instr)->body);
Unrelated to the patch: maybe we should always initialize else_instrs? I think that would get rid of a bunch of checks with no drawback.
Besides another heap allocation (though maybe we could just store those inline instead? I don't think there's any reason why not), but yes, probably.
Yeah, I don't see any problem with inlining them either.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/d3dcompiler_43/hlsl.y | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
diff --git a/dlls/d3dcompiler_43/hlsl.y b/dlls/d3dcompiler_43/hlsl.y index 15bd0d007b9..932a75bb6dd 100644 --- a/dlls/d3dcompiler_43/hlsl.y +++ b/dlls/d3dcompiler_43/hlsl.y @@ -2937,6 +2937,32 @@ static BOOL fold_constants(struct hlsl_ir_node *instr) return TRUE; }
+static BOOL dce(struct hlsl_ir_node *instr) +{ + switch (instr->type) + { + case HLSL_IR_CONSTANT: + case HLSL_IR_EXPR: + case HLSL_IR_LOAD: + case HLSL_IR_SWIZZLE: + if (list_empty(&instr->uses)) + { + list_remove(&instr->entry); + free_instr(instr); + return TRUE; + } + break; + + case HLSL_IR_ASSIGNMENT: + case HLSL_IR_IF: + case HLSL_IR_JUMP: + case HLSL_IR_LOOP: + break; + } + + return FALSE; +} + /* Allocate a unique, ordered index to each instruction, which will be used for * computing liveness ranges. */ static unsigned int index_instructions(struct list *instrs, unsigned int index) @@ -3109,6 +3135,7 @@ HRESULT parse_hlsl(enum shader_type type, DWORD major, DWORD minor, list_move_head(entry_func->body, &hlsl_ctx.static_initializers);
while (transform_ir(fold_constants, entry_func->body)); + while (transform_ir(dce, entry_func->body));
/* Index 0 means unused; index 1 means function entry, so start at 2. */ index_instructions(entry_func->body, 2);
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/d3dcompiler_43/d3dcompiler_private.h | 2 ++ dlls/d3dcompiler_43/utils.c | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/dlls/d3dcompiler_43/d3dcompiler_private.h b/dlls/d3dcompiler_43/d3dcompiler_private.h index 23eff210940..2ff019d45a6 100644 --- a/dlls/d3dcompiler_43/d3dcompiler_private.h +++ b/dlls/d3dcompiler_43/d3dcompiler_private.h @@ -786,6 +786,8 @@ enum hlsl_ir_expr_op { HLSL_IR_UNOP_POSTINC, HLSL_IR_UNOP_POSTDEC,
+ HLSL_IR_UNOP_IDENT, + HLSL_IR_BINOP_ADD, HLSL_IR_BINOP_SUB, HLSL_IR_BINOP_MUL, diff --git a/dlls/d3dcompiler_43/utils.c b/dlls/d3dcompiler_43/utils.c index 4bc3b9b0a64..b5422abd1e8 100644 --- a/dlls/d3dcompiler_43/utils.c +++ b/dlls/d3dcompiler_43/utils.c @@ -1448,6 +1448,7 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh { struct hlsl_ir_assignment *assign = d3dcompiler_alloc(sizeof(*assign)); struct hlsl_type *lhs_type; + struct hlsl_ir_node *copy; DWORD writemask = 0;
lhs_type = lhs->data_type; @@ -1511,7 +1512,7 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh lhs = lhs_inner; }
- init_node(&assign->node, HLSL_IR_ASSIGNMENT, lhs_type, lhs->loc); + init_node(&assign->node, HLSL_IR_ASSIGNMENT, NULL, lhs->loc); assign->writemask = writemask; assign->lhs.var = load_from_node(lhs)->src.var; hlsl_src_from_node(&assign->lhs.offset, load_from_node(lhs)->src.offset.node); @@ -1528,7 +1529,14 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh hlsl_src_from_node(&assign->rhs, rhs); list_add_tail(instrs, &assign->node.entry);
- return &assign->node; + /* Don't use the instruction itself as a source, as this makes structure + * splitting easier. Instead copy it here. Since we retrieve sources from + * the last instruction in the list, we do need to copy. */ + + if (!(copy = new_unary_expr(HLSL_IR_UNOP_IDENT, rhs, lhs->loc))) + return NULL; + list_add_tail(instrs, ©->entry); + return copy; }
static int compare_hlsl_types_rb(const void *key, const struct wine_rb_entry *entry) @@ -1896,6 +1904,8 @@ static const char *debug_expr_op(const struct hlsl_ir_expr *expr) "post++", "post--",
+ "", + "+", "-", "*",
On Tue, Jun 30, 2020 at 2:03 AM Zebediah Figura z.figura12@gmail.com wrote:
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
dlls/d3dcompiler_43/d3dcompiler_private.h | 2 ++ dlls/d3dcompiler_43/utils.c | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/dlls/d3dcompiler_43/d3dcompiler_private.h b/dlls/d3dcompiler_43/d3dcompiler_private.h index 23eff210940..2ff019d45a6 100644 --- a/dlls/d3dcompiler_43/d3dcompiler_private.h +++ b/dlls/d3dcompiler_43/d3dcompiler_private.h @@ -786,6 +786,8 @@ enum hlsl_ir_expr_op { HLSL_IR_UNOP_POSTINC, HLSL_IR_UNOP_POSTDEC,
- HLSL_IR_UNOP_IDENT,
- HLSL_IR_BINOP_ADD, HLSL_IR_BINOP_SUB, HLSL_IR_BINOP_MUL,
diff --git a/dlls/d3dcompiler_43/utils.c b/dlls/d3dcompiler_43/utils.c index 4bc3b9b0a64..b5422abd1e8 100644 --- a/dlls/d3dcompiler_43/utils.c +++ b/dlls/d3dcompiler_43/utils.c @@ -1448,6 +1448,7 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh { struct hlsl_ir_assignment *assign = d3dcompiler_alloc(sizeof(*assign)); struct hlsl_type *lhs_type;
struct hlsl_ir_node *copy; DWORD writemask = 0;
lhs_type = lhs->data_type;
@@ -1511,7 +1512,7 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh lhs = lhs_inner; }
- init_node(&assign->node, HLSL_IR_ASSIGNMENT, lhs_type, lhs->loc);
- init_node(&assign->node, HLSL_IR_ASSIGNMENT, NULL, lhs->loc); assign->writemask = writemask; assign->lhs.var = load_from_node(lhs)->src.var; hlsl_src_from_node(&assign->lhs.offset, load_from_node(lhs)->src.offset.node);
@@ -1528,7 +1529,14 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh hlsl_src_from_node(&assign->rhs, rhs); list_add_tail(instrs, &assign->node.entry);
- return &assign->node;
- /* Don't use the instruction itself as a source, as this makes structure
* splitting easier. Instead copy it here. Since we retrieve sources from
* the last instruction in the list, we do need to copy. */
Not just that, what we now define as an assignment (really a STORE) doesn't really make sense anymore as a source.
The need to copy the instruction is a bit annoying though. What about storing the result into a temporary variable and adding an extra load instead? Does it make struct splitting hard / impossible (e.g. along the lines of: replace this instruction with this other instruction and the copy -> now we need to replace the copy -> back to square one)?
If the temporary var + extra load solution doesn't fly, I guess this could be the rare case where the unary + comes in handy, instead of making up a new operator.
On 7/2/20 11:15 AM, Matteo Bruni wrote:
On Tue, Jun 30, 2020 at 2:03 AM Zebediah Figura z.figura12@gmail.com wrote:
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
dlls/d3dcompiler_43/d3dcompiler_private.h | 2 ++ dlls/d3dcompiler_43/utils.c | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/dlls/d3dcompiler_43/d3dcompiler_private.h b/dlls/d3dcompiler_43/d3dcompiler_private.h index 23eff210940..2ff019d45a6 100644 --- a/dlls/d3dcompiler_43/d3dcompiler_private.h +++ b/dlls/d3dcompiler_43/d3dcompiler_private.h @@ -786,6 +786,8 @@ enum hlsl_ir_expr_op { HLSL_IR_UNOP_POSTINC, HLSL_IR_UNOP_POSTDEC,
- HLSL_IR_UNOP_IDENT,
- HLSL_IR_BINOP_ADD, HLSL_IR_BINOP_SUB, HLSL_IR_BINOP_MUL,
diff --git a/dlls/d3dcompiler_43/utils.c b/dlls/d3dcompiler_43/utils.c index 4bc3b9b0a64..b5422abd1e8 100644 --- a/dlls/d3dcompiler_43/utils.c +++ b/dlls/d3dcompiler_43/utils.c @@ -1448,6 +1448,7 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh { struct hlsl_ir_assignment *assign = d3dcompiler_alloc(sizeof(*assign)); struct hlsl_type *lhs_type;
struct hlsl_ir_node *copy; DWORD writemask = 0;
lhs_type = lhs->data_type;
@@ -1511,7 +1512,7 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh lhs = lhs_inner; }
- init_node(&assign->node, HLSL_IR_ASSIGNMENT, lhs_type, lhs->loc);
- init_node(&assign->node, HLSL_IR_ASSIGNMENT, NULL, lhs->loc); assign->writemask = writemask; assign->lhs.var = load_from_node(lhs)->src.var; hlsl_src_from_node(&assign->lhs.offset, load_from_node(lhs)->src.offset.node);
@@ -1528,7 +1529,14 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh hlsl_src_from_node(&assign->rhs, rhs); list_add_tail(instrs, &assign->node.entry);
- return &assign->node;
- /* Don't use the instruction itself as a source, as this makes structure
* splitting easier. Instead copy it here. Since we retrieve sources from
* the last instruction in the list, we do need to copy. */
Not just that, what we now define as an assignment (really a STORE) doesn't really make sense anymore as a source.
The need to copy the instruction is a bit annoying though. What about storing the result into a temporary variable and adding an extra load instead?
It didn't seem obviously better to me, given the extra allocation, but I think there's no reason it can't work. Certainly it avoids 5/5...
Does it make struct splitting hard / impossible (e.g. along the lines of: replace this instruction with this other instruction and the copy -> now we need to replace the copy -> back to square one)?
The algorithm I have basically generates store + load instructions for each struct field, then removes the original. This breaks if the original is used as a source, so I figured it was best if we just didn't do that.
The other easy solution I see is to iterate through the instruction list in reverse.
If the temporary var + extra load solution doesn't fly, I guess this could be the rare case where the unary + comes in handy, instead of making up a new operator.
On Thu, Jul 2, 2020 at 6:58 PM Zebediah Figura zfigura@codeweavers.com wrote:
On 7/2/20 11:15 AM, Matteo Bruni wrote:
On Tue, Jun 30, 2020 at 2:03 AM Zebediah Figura z.figura12@gmail.com wrote:
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
dlls/d3dcompiler_43/d3dcompiler_private.h | 2 ++ dlls/d3dcompiler_43/utils.c | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/dlls/d3dcompiler_43/d3dcompiler_private.h b/dlls/d3dcompiler_43/d3dcompiler_private.h index 23eff210940..2ff019d45a6 100644 --- a/dlls/d3dcompiler_43/d3dcompiler_private.h +++ b/dlls/d3dcompiler_43/d3dcompiler_private.h @@ -786,6 +786,8 @@ enum hlsl_ir_expr_op { HLSL_IR_UNOP_POSTINC, HLSL_IR_UNOP_POSTDEC,
- HLSL_IR_UNOP_IDENT,
- HLSL_IR_BINOP_ADD, HLSL_IR_BINOP_SUB, HLSL_IR_BINOP_MUL,
diff --git a/dlls/d3dcompiler_43/utils.c b/dlls/d3dcompiler_43/utils.c index 4bc3b9b0a64..b5422abd1e8 100644 --- a/dlls/d3dcompiler_43/utils.c +++ b/dlls/d3dcompiler_43/utils.c @@ -1448,6 +1448,7 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh { struct hlsl_ir_assignment *assign = d3dcompiler_alloc(sizeof(*assign)); struct hlsl_type *lhs_type;
struct hlsl_ir_node *copy; DWORD writemask = 0;
lhs_type = lhs->data_type;
@@ -1511,7 +1512,7 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh lhs = lhs_inner; }
- init_node(&assign->node, HLSL_IR_ASSIGNMENT, lhs_type, lhs->loc);
- init_node(&assign->node, HLSL_IR_ASSIGNMENT, NULL, lhs->loc); assign->writemask = writemask; assign->lhs.var = load_from_node(lhs)->src.var; hlsl_src_from_node(&assign->lhs.offset, load_from_node(lhs)->src.offset.node);
@@ -1528,7 +1529,14 @@ struct hlsl_ir_node *add_assignment(struct list *instrs, struct hlsl_ir_node *lh hlsl_src_from_node(&assign->rhs, rhs); list_add_tail(instrs, &assign->node.entry);
- return &assign->node;
- /* Don't use the instruction itself as a source, as this makes structure
* splitting easier. Instead copy it here. Since we retrieve sources from
* the last instruction in the list, we do need to copy. */
Not just that, what we now define as an assignment (really a STORE) doesn't really make sense anymore as a source.
The need to copy the instruction is a bit annoying though. What about storing the result into a temporary variable and adding an extra load instead?
It didn't seem obviously better to me, given the extra allocation, but I think there's no reason it can't work. Certainly it avoids 5/5...
Yeah, my feeling is that these new loads won't be different from other redundant loads that come up in shaders during compilation (i.e. copy propagation of some kind can scoop up all of them).
Does it make struct splitting hard / impossible (e.g. along the lines of: replace this instruction with this other instruction and the copy -> now we need to replace the copy -> back to square one)?
The algorithm I have basically generates store + load instructions for each struct field, then removes the original. This breaks if the original is used as a source, so I figured it was best if we just didn't do that.
The other easy solution I see is to iterate through the instruction list in reverse.
Right. I'm convinced that never using an assignment / store as a source is just a good idea, regardless.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/d3dcompiler_43/hlsl.y | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/dlls/d3dcompiler_43/hlsl.y b/dlls/d3dcompiler_43/hlsl.y index 932a75bb6dd..92d0da0b64c 100644 --- a/dlls/d3dcompiler_43/hlsl.y +++ b/dlls/d3dcompiler_43/hlsl.y @@ -2870,11 +2870,26 @@ static void replace_node(struct hlsl_ir_node *old, struct hlsl_ir_node *new) hlsl_src_remove(src); hlsl_src_from_node(src, new); } - list_add_before(&old->entry, &new->entry); list_remove(&old->entry); free_instr(old); }
+static BOOL fold_ident(struct hlsl_ir_node *instr) +{ + if (instr->type == HLSL_IR_EXPR) + { + struct hlsl_ir_expr *expr = expr_from_node(instr); + + if (expr->op == HLSL_IR_UNOP_IDENT) + { + replace_node(&expr->node, expr->operands[0].node); + return TRUE; + } + } + + return FALSE; +} + static BOOL fold_constants(struct hlsl_ir_node *instr) { struct hlsl_ir_constant *arg1, *arg2 = NULL, *res; @@ -2933,6 +2948,7 @@ static BOOL fold_constants(struct hlsl_ir_node *instr) return FALSE; }
+ list_add_before(&expr->node.entry, &res->node.entry); replace_node(&expr->node, &res->node); return TRUE; } @@ -3134,6 +3150,7 @@ HRESULT parse_hlsl(enum shader_type type, DWORD major, DWORD minor,
list_move_head(entry_func->body, &hlsl_ctx.static_initializers);
+ transform_ir(fold_ident, entry_func->body); while (transform_ir(fold_constants, entry_func->body)); while (transform_ir(dce, entry_func->body));