[PATCH vkd3d v8 1/4] vkd3d-shader/hlsl: Perform a copy propagation pass.

Giovanni Mascellani gmascellani at codeweavers.com
Sat Nov 20 08:37:57 CST 2021


Signed-off-by: Giovanni Mascellani <gmascellani at codeweavers.com>
Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
---
v8: Address comments in https://source.winehq.org/patches/data/219953

 libs/vkd3d-shader/hlsl.c         |  21 ++-
 libs/vkd3d-shader/hlsl.h         |   1 +
 libs/vkd3d-shader/hlsl_codegen.c | 222 ++++++++++++++++++++++++++++++-
 3 files changed, 238 insertions(+), 6 deletions(-)

diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c
index 1eee4278..f59e7403 100644
--- a/libs/vkd3d-shader/hlsl.c
+++ b/libs/vkd3d-shader/hlsl.c
@@ -1097,6 +1097,20 @@ const char *debug_hlsl_writemask(unsigned int writemask)
     return vkd3d_dbg_sprintf(".%s", string);
 }
 
+const char *debug_hlsl_swizzle(unsigned int swizzle, unsigned int count)
+{
+    static const char components[] = {'x', 'y', 'z', 'w'};
+    char string[5];
+    unsigned int i = 0;
+
+    for (i = 0; i < count; ++i)
+    {
+        string[i] = components[(swizzle >> (2 * i)) % 4];
+    }
+    string[count] = '\0';
+    return vkd3d_dbg_sprintf(".%s", string);
+}
+
 static void dump_ir_constant(struct vkd3d_string_buffer *buffer, const struct hlsl_ir_constant *constant)
 {
     struct hlsl_type *type = constant->node.data_type;
@@ -1277,18 +1291,15 @@ static void dump_ir_swizzle(struct vkd3d_string_buffer *buffer, const struct hls
     unsigned int i;
 
     dump_src(buffer, &swizzle->val);
-    vkd3d_string_buffer_printf(buffer, ".");
     if (swizzle->val.node->data_type->dimy > 1)
     {
+        vkd3d_string_buffer_printf(buffer, ".");
         for (i = 0; i < swizzle->node.data_type->dimx; ++i)
             vkd3d_string_buffer_printf(buffer, "_m%u%u", (swizzle->swizzle >> i * 8) & 0xf, (swizzle->swizzle >> (i * 8 + 4)) & 0xf);
     }
     else
     {
-        static const char c[] = {'x', 'y', 'z', 'w'};
-
-        for (i = 0; i < swizzle->node.data_type->dimx; ++i)
-            vkd3d_string_buffer_printf(buffer, "%c", c[(swizzle->swizzle >> i * 2) & 0x3]);
+        vkd3d_string_buffer_printf(buffer, "%s", debug_hlsl_swizzle(swizzle->swizzle, swizzle->node.data_type->dimx));
     }
 }
 
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h
index 365ef980..bca59b65 100644
--- a/libs/vkd3d-shader/hlsl.h
+++ b/libs/vkd3d-shader/hlsl.h
@@ -666,6 +666,7 @@ static inline struct hlsl_type *hlsl_get_numeric_type(const struct hlsl_ctx *ctx
 const char *debug_hlsl_expr_op(enum hlsl_ir_expr_op op);
 const char *debug_hlsl_type(struct hlsl_ctx *ctx, const struct hlsl_type *type);
 const char *debug_hlsl_writemask(unsigned int writemask);
+const char *debug_hlsl_swizzle(unsigned int swizzle, unsigned int count);
 
 struct vkd3d_string_buffer *hlsl_type_to_string(struct hlsl_ctx *ctx, const struct hlsl_type *type);
 struct vkd3d_string_buffer *hlsl_modifiers_to_string(struct hlsl_ctx *ctx, unsigned int modifiers);
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c
index 4a7e003f..4e1397f6 100644
--- a/libs/vkd3d-shader/hlsl_codegen.c
+++ b/libs/vkd3d-shader/hlsl_codegen.c
@@ -237,6 +237,221 @@ static void replace_node(struct hlsl_ir_node *old, struct hlsl_ir_node *new)
     hlsl_free_instr(old);
 }
 
+struct copy_propagation_value
+{
+    struct hlsl_ir_node *node;
+    unsigned int component;
+};
+
+struct copy_propagation_var_def
+{
+    struct rb_entry entry;
+    struct hlsl_ir_var *var;
+    struct copy_propagation_value values[];
+};
+
+struct copy_propagation_state
+{
+    struct rb_tree variables;
+};
+
+static int copy_propagation_variable_compare(const void *key, const struct rb_entry *entry)
+{
+    struct copy_propagation_var_def *variable = RB_ENTRY_VALUE(entry, struct copy_propagation_var_def, entry);
+    uintptr_t key_int = (uintptr_t)key, entry_int = (uintptr_t)variable->var;
+
+    return (key_int > entry_int) - (key_int < entry_int);
+}
+
+static void copy_propagation_variable_destroy(struct rb_entry *entry, void *context)
+{
+    struct copy_propagation_var_def *variable = RB_ENTRY_VALUE(entry, struct copy_propagation_var_def, entry);
+
+    vkd3d_free(variable);
+}
+
+static struct copy_propagation_var_def *copy_propagation_get_variable(struct copy_propagation_state *state,
+        struct hlsl_ir_var *var)
+{
+    struct rb_entry *entry = rb_get(&state->variables, var);
+
+    if (entry)
+        return RB_ENTRY_VALUE(entry, struct copy_propagation_var_def, entry);
+    else
+        return NULL;
+}
+
+static struct copy_propagation_var_def *copy_propagation_create_variable(struct hlsl_ctx *ctx,
+        struct copy_propagation_state *state, struct hlsl_ir_var *var)
+{
+    struct rb_entry *entry = rb_get(&state->variables, var);
+    struct copy_propagation_var_def *variable;
+    int res;
+
+    if (entry)
+        return RB_ENTRY_VALUE(entry, struct copy_propagation_var_def, entry);
+
+    variable = hlsl_alloc(ctx, offsetof(struct copy_propagation_var_def, values[var->data_type->reg_size]));
+    if (!variable)
+        return NULL;
+
+    variable->var = var;
+
+    res = rb_put(&state->variables, var, &variable->entry);
+    assert(!res);
+
+    return variable;
+}
+
+static void copy_propagation_invalidate_whole_variable(struct copy_propagation_var_def *variable)
+{
+    TRACE("Invalidate variable %s.\n", variable->var->name);
+    memset(variable->values, 0, sizeof(*variable->values) * variable->var->data_type->reg_size);
+}
+
+static void copy_propagation_set_value(struct copy_propagation_var_def *variable, unsigned int offset,
+        unsigned char writemask, struct hlsl_ir_node *node)
+{
+    unsigned int i, j = 0;
+
+    for (i = 0; i < 4; ++i)
+    {
+        if (writemask & (1u << i))
+        {
+            TRACE("Variable %s[%u] is written by instruction %p%s.\n",
+                    variable->var->name, offset + i, node, debug_hlsl_writemask(1u << i));
+            variable->values[offset + i].node = node;
+            variable->values[offset + i].component = j++;
+        }
+    }
+}
+
+static struct hlsl_ir_node *copy_propagation_compute_replacement(struct copy_propagation_var_def *variable,
+        unsigned int offset, unsigned int count, unsigned int *swizzle)
+{
+    struct hlsl_ir_node *node = NULL;
+    unsigned int i;
+
+    assert(offset + count <= variable->var->data_type->reg_size);
+
+    *swizzle = 0;
+
+    for (i = 0; i < count; ++i)
+    {
+        if (!node)
+            node = variable->values[offset + i].node;
+        else if (node != variable->values[offset + i].node)
+            return NULL;
+        *swizzle |= variable->values[offset + i].component << (2 * i);
+    }
+
+    return node;
+}
+
+static bool copy_propagation_analyze_load(struct hlsl_ctx *ctx, struct hlsl_ir_load *load,
+        struct copy_propagation_state *state)
+{
+    struct hlsl_ir_node *node = &load->node, *new_node;
+    struct copy_propagation_var_def *variable;
+    struct hlsl_type *type = node->data_type;
+    struct hlsl_ir_swizzle *swizzle_node;
+    struct hlsl_deref *src = &load->src;
+    struct hlsl_ir_var *var = src->var;
+    unsigned int offset, swizzle;
+
+    if (type->type != HLSL_CLASS_SCALAR && type->type != HLSL_CLASS_VECTOR)
+        return false;
+
+    if (!hlsl_offset_from_deref(src, &offset))
+        return false;
+
+    variable = copy_propagation_get_variable(state, var);
+    if (!variable)
+        return false;
+
+    new_node = copy_propagation_compute_replacement(variable, offset, type->dimx, &swizzle);
+
+    TRACE("Load from %s[%d-%d] reconstructed as instruction %p%s.\n",
+            var->name, offset, offset + type->dimx, new_node, debug_hlsl_swizzle(swizzle, 4));
+
+    if (!new_node)
+        return false;
+
+    if (!(swizzle_node = hlsl_new_swizzle(ctx, swizzle, type->dimx, new_node, &node->loc)))
+        return false;
+    list_add_before(&node->entry, &swizzle_node->node.entry);
+
+    replace_node(node, &swizzle_node->node);
+
+    return true;
+}
+
+static void copy_propagation_record_store(struct hlsl_ctx *ctx, struct hlsl_ir_store *store,
+        struct copy_propagation_state *state)
+{
+    struct copy_propagation_var_def *variable;
+    struct hlsl_deref *lhs = &store->lhs;
+    struct hlsl_ir_var *var = lhs->var;
+    unsigned int offset;
+
+    variable = copy_propagation_create_variable(ctx, state, var);
+    if (!variable)
+        return;
+
+    if (hlsl_offset_from_deref(lhs, &offset))
+        copy_propagation_set_value(variable, offset, store->writemask, store->rhs.node);
+    else
+        copy_propagation_invalidate_whole_variable(variable);
+}
+
+static bool copy_propagation_transform_block(struct hlsl_ctx *ctx, struct hlsl_block *block,
+        struct copy_propagation_state *state)
+{
+    struct hlsl_ir_node *instr, *next;
+    bool progress = false;
+
+    LIST_FOR_EACH_ENTRY_SAFE(instr, next, &block->instrs, struct hlsl_ir_node, entry)
+    {
+        switch (instr->type)
+        {
+            case HLSL_IR_LOAD:
+                progress |= copy_propagation_analyze_load(ctx, hlsl_ir_load(instr), state);
+                break;
+
+            case HLSL_IR_STORE:
+                copy_propagation_record_store(ctx, hlsl_ir_store(instr), state);
+                break;
+
+            case HLSL_IR_IF:
+                FIXME("Copy propagation doesn't support conditionals yet, leaving.\n");
+                return progress;
+
+            case HLSL_IR_LOOP:
+                FIXME("Copy propagation doesn't support loops yet, leaving.\n");
+                return progress;
+
+            default:
+                break;
+        }
+    }
+
+    return progress;
+}
+
+static bool copy_propagation_execute(struct hlsl_ctx *ctx, struct hlsl_block *block)
+{
+    struct copy_propagation_state state;
+    bool progress;
+
+    rb_init(&state.variables, copy_propagation_variable_compare);
+
+    progress = copy_propagation_transform_block(ctx, block, &state);
+
+    rb_destroy(&state.variables, copy_propagation_variable_destroy, NULL);
+
+    return progress;
+}
+
 static bool is_vec1(const struct hlsl_type *type)
 {
     return (type->type == HLSL_CLASS_SCALAR) || (type->type == HLSL_CLASS_VECTOR && type->dimx == 1);
@@ -1390,7 +1605,12 @@ int hlsl_emit_dxbc(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry_fun
         progress |= transform_ir(ctx, split_struct_copies, body, NULL);
     }
     while (progress);
-    while (transform_ir(ctx, fold_constants, body, NULL));
+    do
+    {
+        progress = transform_ir(ctx, fold_constants, body, NULL);
+        progress |= copy_propagation_execute(ctx, body);
+    }
+    while (progress);
     transform_ir(ctx, remove_trivial_swizzles, body, NULL);
 
     if (ctx->profile->major_version < 4)
-- 
2.33.1




More information about the wine-devel mailing list