diff --git a/Makefile.am b/Makefile.am
index 60db7794b4b898538cfc0850eac4392551908ca9..962036ff49ea5c7286fba5db2cc9e2980f28a82f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -115,6 +115,7 @@ vkd3d_shader_tests = \
 	tests/matrix-semantics.shader_test \
 	tests/multiple-rt.shader_test \
 	tests/nointerpolation.shader_test \
+	tests/object-references.shader_test
 	tests/pow.shader_test \
 	tests/preproc-if.shader_test \
 	tests/preproc-ifdef.shader_test \
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c
index a47e3d2d1b30a7fcb93717888b0e51a200dfdcdd..2b5222dbaf36ffcb83d1fa37d871b5f221afded7 100644
--- a/libs/vkd3d-shader/hlsl.c
+++ b/libs/vkd3d-shader/hlsl.c
@@ -140,6 +140,23 @@ unsigned int hlsl_type_major_size(const struct hlsl_type *type)
         return type->dimx;
 }
 
+unsigned int hlsl_type_element_count(const struct hlsl_type *type)
+{
+    switch (type->type)
+    {
+        case HLSL_CLASS_VECTOR:
+            return type->dimx;
+        case HLSL_CLASS_MATRIX:
+            return hlsl_type_major_size(type);
+        case HLSL_CLASS_ARRAY:
+            return type->e.array.elements_count;
+        case HLSL_CLASS_STRUCT:
+            return type->e.record.field_count;
+        default:
+            return 0;
+    }
+}
+
 static unsigned int get_array_size(const struct hlsl_type *type)
 {
     if (type->type == HLSL_CLASS_ARRAY)
@@ -181,8 +198,9 @@ static void hlsl_type_calculate_reg_size(struct hlsl_ctx *ctx, struct hlsl_type
         {
             unsigned int element_size = type->e.array.type->reg_size;
 
-            assert(element_size);
-            if (is_sm4)
+            if (type->e.array.elements_count == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
+                type->reg_size = 0;
+            else if (is_sm4)
                 type->reg_size = (type->e.array.elements_count - 1) * align(element_size, 4) + element_size;
             else
                 type->reg_size = type->e.array.elements_count * element_size;
@@ -201,8 +219,6 @@ static void hlsl_type_calculate_reg_size(struct hlsl_ctx *ctx, struct hlsl_type
                 struct hlsl_struct_field *field = &type->e.record.fields[i];
                 unsigned int field_size = field->type->reg_size;
 
-                assert(field_size);
-
                 type->reg_size = hlsl_type_get_sm4_offset(field->type, type->reg_size);
                 field->reg_offset = type->reg_size;
                 type->reg_size += field_size;
@@ -213,8 +229,7 @@ static void hlsl_type_calculate_reg_size(struct hlsl_ctx *ctx, struct hlsl_type
         }
 
         case HLSL_CLASS_OBJECT:
-            /* For convenience when performing copy propagation. */
-            type->reg_size = 1;
+            type->reg_size = 0;
             break;
     }
 }
@@ -370,7 +385,7 @@ static struct hlsl_type *get_type_from_deref(struct hlsl_ctx *ctx, const struct
 
     type = deref->var->data_type;
     for (i = 0; i < deref->path_len; ++i)
-        type = hlsl_get_inner_type_from_path_index(ctx, type, deref->path[i].node);
+        type = hlsl_get_element_type_from_path_index(ctx, type, deref->path[i].node);
     return type;
 }
 
@@ -423,7 +438,7 @@ static bool init_deref_from_component_index(struct hlsl_ctx *ctx, struct hlsl_bl
     return true;
 }
 
-struct hlsl_type *hlsl_get_inner_type_from_path_index(struct hlsl_ctx *ctx, const struct hlsl_type *type,
+struct hlsl_type *hlsl_get_element_type_from_path_index(struct hlsl_ctx *ctx, const struct hlsl_type *type,
         struct hlsl_ir_node *idx)
 {
     assert(idx);
@@ -548,25 +563,32 @@ struct hlsl_ir_function_decl *hlsl_get_func_decl(struct hlsl_ctx *ctx, const cha
 
 unsigned int hlsl_type_component_count(const struct hlsl_type *type)
 {
-    unsigned int count = 0, i;
-
-    if (type->type <= HLSL_CLASS_LAST_NUMERIC)
-    {
-        return type->dimx * type->dimy;
-    }
-    if (type->type == HLSL_CLASS_ARRAY)
-    {
-        return hlsl_type_component_count(type->e.array.type) * type->e.array.elements_count;
-    }
-    if (type->type != HLSL_CLASS_STRUCT)
+    switch (type->type)
     {
-        ERR("Unexpected data type %#x.\n", type->type);
-        return 0;
-    }
+        case HLSL_CLASS_SCALAR:
+        case HLSL_CLASS_VECTOR:
+        case HLSL_CLASS_MATRIX:
+            return type->dimx * type->dimy;
+
+        case HLSL_CLASS_STRUCT:
+        {
+            unsigned int count = 0, i;
+
+            for (i = 0; i < type->e.record.field_count; ++i)
+                count += hlsl_type_component_count(type->e.record.fields[i].type);
+            return count;
+        }
 
-    for (i = 0; i < type->e.record.field_count; ++i)
-        count += hlsl_type_component_count(type->e.record.fields[i].type);
-    return count;
+        case HLSL_CLASS_ARRAY:
+            return hlsl_type_component_count(type->e.array.type) * type->e.array.elements_count;
+
+        case HLSL_CLASS_OBJECT:
+            return 1;
+
+        default:
+            assert(0);
+            return 0;
+    }
 }
 
 bool hlsl_types_are_equal(const struct hlsl_type *t1, const struct hlsl_type *t2)
@@ -763,7 +785,7 @@ static bool type_is_single_reg(const struct hlsl_type *type)
     return type->type == HLSL_CLASS_SCALAR || type->type == HLSL_CLASS_VECTOR;
 }
 
-static bool copy_deref(struct hlsl_ctx *ctx, struct hlsl_deref *deref, struct hlsl_deref *other)
+bool hlsl_copy_deref(struct hlsl_ctx *ctx, struct hlsl_deref *deref, struct hlsl_deref *other)
 {
     unsigned int i;
 
@@ -963,7 +985,7 @@ struct hlsl_ir_load *hlsl_new_load_index(struct hlsl_ctx *ctx, const struct hlsl
 
     type = get_type_from_deref(ctx, deref);
     if (idx)
-        type = hlsl_get_inner_type_from_path_index(ctx, type, idx);
+        type = hlsl_get_element_type_from_path_index(ctx, type, idx);
 
     if (!(load = hlsl_alloc(ctx, sizeof(*load))))
         return NULL;
@@ -1029,8 +1051,8 @@ struct hlsl_ir_resource_load *hlsl_new_resource_load(struct hlsl_ctx *ctx, struc
         return NULL;
     init_node(&load->node, HLSL_IR_RESOURCE_LOAD, data_type, *loc);
     load->load_type = type;
-    copy_deref(ctx, &load->resource, resource);
-    copy_deref(ctx, &load->sampler, sampler);
+    hlsl_copy_deref(ctx, &load->resource, resource);
+    hlsl_copy_deref(ctx, &load->sampler, sampler);
     hlsl_src_from_node(&load->coords, coords);
     hlsl_src_from_node(&load->texel_offset, texel_offset);
     return load;
@@ -1293,7 +1315,12 @@ struct vkd3d_string_buffer *hlsl_type_to_string(struct hlsl_ctx *ctx, const stru
             }
 
             for (t = type; t->type == HLSL_CLASS_ARRAY; t = t->e.array.type)
-                vkd3d_string_buffer_printf(string, "[%u]", t->e.array.elements_count);
+            {
+                if (t->e.array.elements_count == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
+                    vkd3d_string_buffer_printf(string, "[]");
+                else
+                    vkd3d_string_buffer_printf(string, "[%u]", t->e.array.elements_count);
+            }
             return string;
         }
 
@@ -1538,6 +1565,7 @@ static void dump_ir_constant(struct vkd3d_string_buffer *buffer, const struct hl
                 break;
 
             case HLSL_TYPE_FLOAT:
+            case HLSL_TYPE_HALF:
                 vkd3d_string_buffer_printf(buffer, "%.8e ", value->f);
                 break;
 
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h
index 7970665c31343dc49e5305684861ccbebf36d0ac..d3317bb12bfb25ba83a085dda8bdf170ceb0a9bc 100644
--- a/libs/vkd3d-shader/hlsl.h
+++ b/libs/vkd3d-shader/hlsl.h
@@ -230,6 +230,8 @@ struct hlsl_src
 
 #define HLSL_MODIFIERS_MAJORITY_MASK (HLSL_MODIFIER_ROW_MAJOR | HLSL_MODIFIER_COLUMN_MAJOR)
 
+#define HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT 0
+
 struct hlsl_reg_reservation
 {
     char type;
@@ -721,6 +723,7 @@ void hlsl_dump_function(struct hlsl_ctx *ctx, const struct hlsl_ir_function_decl
 int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry_func,
         enum vkd3d_shader_target_type target_type, struct vkd3d_shader_code *out);
 
+bool hlsl_copy_deref(struct hlsl_ctx *ctx, struct hlsl_deref *deref, struct hlsl_deref *other);
 void hlsl_cleanup_deref(struct hlsl_deref *deref);
 
 void hlsl_replace_node(struct hlsl_ir_node *old, struct hlsl_ir_node *new);
@@ -735,7 +738,7 @@ struct hlsl_ir_function_decl *hlsl_get_func_decl(struct hlsl_ctx *ctx, const cha
 struct hlsl_type *hlsl_get_type(struct hlsl_scope *scope, const char *name, bool recursive);
 struct hlsl_ir_var *hlsl_get_var(struct hlsl_scope *scope, const char *name);
 
-struct hlsl_type *hlsl_get_inner_type_from_path_index(struct hlsl_ctx *ctx, const struct hlsl_type *type,
+struct hlsl_type *hlsl_get_element_type_from_path_index(struct hlsl_ctx *ctx, const struct hlsl_type *type,
         struct hlsl_ir_node *idx);
 
 struct hlsl_type *hlsl_new_array_type(struct hlsl_ctx *ctx, struct hlsl_type *basic_type, unsigned int array_size);
@@ -813,6 +816,7 @@ struct hlsl_type *hlsl_type_get_component_type(struct hlsl_ctx *ctx, struct hlsl
 bool hlsl_type_is_row_major(const struct hlsl_type *type);
 unsigned int hlsl_type_minor_size(const struct hlsl_type *type);
 unsigned int hlsl_type_major_size(const struct hlsl_type *type);
+unsigned int hlsl_type_element_count(const struct hlsl_type *type);
 unsigned int hlsl_type_get_sm4_offset(const struct hlsl_type *type, unsigned int offset);
 bool hlsl_types_are_equal(const struct hlsl_type *t1, const struct hlsl_type *t2);
 
@@ -821,6 +825,8 @@ unsigned int hlsl_combine_writemasks(unsigned int first, unsigned int second);
 unsigned int hlsl_map_swizzle(unsigned int swizzle, unsigned int writemask);
 unsigned int hlsl_swizzle_from_writemask(unsigned int writemask);
 
+bool hlsl_component_index_range_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref *deref,
+        unsigned int *start, unsigned int *count);
 bool hlsl_offset_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref *deref, unsigned int *offset);
 unsigned int hlsl_offset_from_deref_safe(struct hlsl_ctx *ctx, const struct hlsl_deref *deref);
 struct hlsl_reg hlsl_reg_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref *deref);
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y
index 7df7afb940ca38541d6235430839f8f27cec544f..91f0ab60a9ddb9376856532b2b72bd26f5a28e3c 100644
--- a/libs/vkd3d-shader/hlsl.y
+++ b/libs/vkd3d-shader/hlsl.y
@@ -370,6 +370,9 @@ static struct hlsl_ir_node *add_implicit_conversion(struct hlsl_ctx *ctx, struct
 {
     struct hlsl_type *src_type = node->data_type;
 
+    if (hlsl_types_are_equal(src_type, dst_type))
+        return node;
+
     if (!implicit_compatible_data_types(src_type, dst_type))
     {
         struct vkd3d_string_buffer *src_string, *dst_string;
@@ -850,6 +853,11 @@ static void free_parse_variable_def(struct parse_variable_def *v)
     vkd3d_free(v);
 }
 
+static bool shader_is_sm_5_1(const struct hlsl_ctx *ctx)
+{
+    return ctx->profile->major_version == 5 && ctx->profile->minor_version >= 1;
+}
+
 static bool gen_struct_fields(struct hlsl_ctx *ctx, struct parse_fields *fields,
         struct hlsl_type *type, unsigned int modifiers, struct list *defs)
 {
@@ -867,11 +875,45 @@ static bool gen_struct_fields(struct hlsl_ctx *ctx, struct parse_fields *fields,
     LIST_FOR_EACH_ENTRY_SAFE(v, v_next, defs, struct parse_variable_def, entry)
     {
         struct hlsl_struct_field *field = &fields->fields[i++];
-        unsigned int j;
+        bool unbounded_res_array = false;
+        unsigned int k;
 
         field->type = type;
-        for (j = 0; j < v->arrays.count; ++j)
-            field->type = hlsl_new_array_type(ctx, field->type, v->arrays.sizes[j]);
+
+        if (shader_is_sm_5_1(ctx) && type->type == HLSL_CLASS_OBJECT)
+        {
+            for (k = 0; k < v->arrays.count; ++k)
+                unbounded_res_array |= (v->arrays.sizes[k] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT);
+        }
+
+        if (unbounded_res_array)
+        {
+            if (v->arrays.count == 1)
+            {
+                hlsl_fixme(ctx, &v->loc, "Unbounded resource arrays as struct fields.");
+                free_parse_variable_def(v);
+                vkd3d_free(field);
+                continue;
+            }
+            else
+            {
+                hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                        "Unbounded resource arrays cannot be multi-dimensional.");
+            }
+        }
+        else
+        {
+            for (k = 0; k < v->arrays.count; ++k)
+            {
+                if (v->arrays.sizes[k] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
+                {
+                    hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                            "Implicit size arrays not allowed in struct fields.");
+                }
+
+                field->type = hlsl_new_array_type(ctx, field->type, v->arrays.sizes[k]);
+            }
+        }
         vkd3d_free(v->arrays.sizes);
         field->loc = v->loc;
         field->name = v->name;
@@ -913,6 +955,12 @@ static bool add_typedef(struct hlsl_ctx *ctx, DWORD modifiers, struct hlsl_type
         ret = true;
         for (i = 0; i < v->arrays.count; ++i)
         {
+            if (v->arrays.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
+            {
+                hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                        "Implicit size arrays not allowed in typedefs.");
+            }
+
             if (!(type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i])))
             {
                 free_parse_variable_def(v);
@@ -1772,11 +1820,78 @@ static struct list *declare_vars(struct hlsl_ctx *ctx, struct hlsl_type *basic_t
 
     LIST_FOR_EACH_ENTRY_SAFE(v, v_next, var_list, struct parse_variable_def, entry)
     {
+        bool unbounded_res_array = false;
         unsigned int i;
 
         type = basic_type;
-        for (i = 0; i < v->arrays.count; ++i)
-            type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]);
+
+        if (shader_is_sm_5_1(ctx) && type->type == HLSL_CLASS_OBJECT)
+        {
+            for (i = 0; i < v->arrays.count; ++i)
+                unbounded_res_array |= (v->arrays.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT);
+        }
+
+        if (unbounded_res_array)
+        {
+            if (v->arrays.count == 1)
+            {
+                hlsl_fixme(ctx, &v->loc, "Unbounded resource arrays.");
+                free_parse_variable_def(v);
+                continue;
+            }
+            else
+            {
+                hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                        "Unbounded resource arrays cannot be multi-dimensional.");
+            }
+        }
+        else
+        {
+            for (i = 0; i < v->arrays.count; ++i)
+            {
+                if (v->arrays.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
+                {
+                    unsigned int size = initializer_size(&v->initializer);
+                    unsigned int elem_components = hlsl_type_component_count(type);
+
+                    if (i < v->arrays.count - 1)
+                    {
+                        hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                                "Only innermost array size can be implicit.");
+                        free_parse_initializer(&v->initializer);
+                        v->initializer.args_count = 0;
+                    }
+                    else if (elem_components == 0)
+                    {
+                        hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                                "Cannot declare an implicit size array of a size 0 type.");
+                        free_parse_initializer(&v->initializer);
+                        v->initializer.args_count = 0;
+                    }
+                    else if (size == 0)
+                    {
+                        hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                                "Implicit size arrays need to be initialized.");
+                        free_parse_initializer(&v->initializer);
+                        v->initializer.args_count = 0;
+
+                    }
+                    else if (size % elem_components != 0)
+                    {
+                        hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
+                                "Cannot initialize implicit size array with %u components, expected a multiple of %u.",
+                                size, elem_components);
+                        free_parse_initializer(&v->initializer);
+                        v->initializer.args_count = 0;
+                    }
+                    else
+                    {
+                        v->arrays.sizes[i] = size / elem_components;
+                    }
+                }
+                type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]);
+            }
+        }
         vkd3d_free(v->arrays.sizes);
 
         if (type->type != HLSL_CLASS_MATRIX)
@@ -3514,6 +3629,21 @@ arrays:
             $$.sizes = new_array;
             $$.sizes[$$.count++] = size;
         }
+    | '[' ']' arrays
+        {
+            uint32_t *new_array;
+
+            $$ = $3;
+
+            if (!(new_array = hlsl_realloc(ctx, $$.sizes, ($$.count + 1) * sizeof(*new_array))))
+            {
+                vkd3d_free($$.sizes);
+                YYABORT;
+            }
+
+            $$.sizes = new_array;
+            $$.sizes[$$.count++] = HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT;
+        }
 
 var_modifiers:
       %empty
@@ -4055,7 +4185,14 @@ unary_expr:
 
             dst_type = $3;
             for (i = 0; i < $4.count; ++i)
+            {
+                if ($4.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
+                {
+                    hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_TYPE,
+                            "Implicit size arrays not allowed in casts.");
+                }
                 dst_type = hlsl_new_array_type(ctx, dst_type, $4.sizes[i]);
+            }
 
             if (!compatible_data_types(src_type, dst_type))
             {
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c
index 36a417e556a945e1f509e3bd439c1e96eb465f9a..6c4a0eec62debaac07a6375a814a0762bd08f348 100644
--- a/libs/vkd3d-shader/hlsl_codegen.c
+++ b/libs/vkd3d-shader/hlsl_codegen.c
@@ -116,7 +116,7 @@ static struct hlsl_ir_node *new_offset_instr_from_deref(struct hlsl_ctx *ctx, st
 
         list_move_tail(&block->instrs, &idx_block.instrs);
 
-        type = hlsl_get_inner_type_from_path_index(ctx, type, deref->path[i].node);
+        type = hlsl_get_element_type_from_path_index(ctx, type, deref->path[i].node);
     }
 
     return offset;
@@ -541,7 +541,7 @@ static void copy_propagation_var_def_destroy(struct rb_entry *entry, void *conte
 }
 
 static struct copy_propagation_value *copy_propagation_get_value(const struct copy_propagation_state *state,
-        const struct hlsl_ir_var *var, unsigned component)
+        const struct hlsl_ir_var *var, unsigned int component)
 {
     for (; state; state = state->parent)
     {
@@ -549,9 +549,11 @@ static struct copy_propagation_value *copy_propagation_get_value(const struct co
         if (entry)
         {
             struct copy_propagation_var_def *var_def = RB_ENTRY_VALUE(entry, struct copy_propagation_var_def, entry);
-            enum copy_propagation_value_state state = var_def->values[component].state;
+            unsigned int component_count = hlsl_type_component_count(var->data_type);
+            enum copy_propagation_value_state state;
 
-            assert(component < var_def->var->data_type->reg_size);
+            assert(component < component_count);
+            state = var_def->values[component].state;
 
             switch (state)
             {
@@ -573,12 +575,13 @@ static struct copy_propagation_var_def *copy_propagation_create_var_def(struct h
 {
     struct rb_entry *entry = rb_get(&state->var_defs, var);
     struct copy_propagation_var_def *var_def;
+    unsigned int component_count = hlsl_type_component_count(var->data_type);
     int res;
 
     if (entry)
         return RB_ENTRY_VALUE(entry, struct copy_propagation_var_def, entry);
 
-    if (!(var_def = hlsl_alloc(ctx, offsetof(struct copy_propagation_var_def, values[var->data_type->reg_size]))))
+    if (!(var_def = hlsl_alloc(ctx, offsetof(struct copy_propagation_var_def, values[component_count]))))
         return NULL;
 
     var_def->var = var;
@@ -590,31 +593,75 @@ static struct copy_propagation_var_def *copy_propagation_create_var_def(struct h
 }
 
 static void copy_propagation_invalidate_variable(struct copy_propagation_var_def *var_def,
-        unsigned offset, unsigned char writemask)
+        unsigned int comp, unsigned char writemask)
 {
     unsigned i;
 
-    TRACE("Invalidate variable %s[%u]%s.\n", var_def->var->name, offset, debug_hlsl_writemask(writemask));
+    TRACE("Invalidate variable %s[%u]%s.\n", var_def->var->name, comp, debug_hlsl_writemask(writemask));
 
     for (i = 0; i < 4; ++i)
     {
         if (writemask & (1u << i))
-            var_def->values[offset + i].state = VALUE_STATE_DYNAMICALLY_WRITTEN;
+            var_def->values[comp + i].state = VALUE_STATE_DYNAMICALLY_WRITTEN;
     }
 }
 
-static void copy_propagation_invalidate_whole_variable(struct copy_propagation_var_def *var_def)
+static void copy_propagation_invalidate_variable_from_deref_recurse(struct hlsl_ctx *ctx,
+        struct copy_propagation_var_def *var_def, const struct hlsl_deref *deref,
+        struct hlsl_type *type, unsigned int depth, unsigned int comp_start, unsigned char writemask)
 {
-    unsigned i;
+    unsigned int i, subtype_comp_count;
+    struct hlsl_ir_node *path_node;
+    struct hlsl_type *subtype;
+
+    if (depth == deref->path_len)
+    {
+        copy_propagation_invalidate_variable(var_def, comp_start, writemask);
+        return;
+    }
+
+    path_node = deref->path[depth].node;
+    subtype = hlsl_get_element_type_from_path_index(ctx, type, path_node);
 
-    TRACE("Invalidate variable %s.\n", var_def->var->name);
+    if (type->type == HLSL_CLASS_STRUCT)
+    {
+        unsigned int idx = hlsl_ir_constant(path_node)->value[0].u;
+
+        for (i = 0; i < idx; ++i)
+            comp_start += hlsl_type_component_count(type->e.record.fields[i].type);
+
+        copy_propagation_invalidate_variable_from_deref_recurse(ctx, var_def, deref, subtype,
+                depth + 1, comp_start, writemask);
+    }
+    else
+    {
+        subtype_comp_count = hlsl_type_component_count(subtype);
 
-    for (i = 0; i < var_def->var->data_type->reg_size; ++i)
-        var_def->values[i].state = VALUE_STATE_DYNAMICALLY_WRITTEN;
+        if (path_node->type == HLSL_IR_CONSTANT)
+        {
+            copy_propagation_invalidate_variable_from_deref_recurse(ctx, var_def, deref, subtype,
+                    depth + 1, hlsl_ir_constant(path_node)->value[0].u * subtype_comp_count, writemask);
+        }
+        else
+        {
+            for (i = 0; i < hlsl_type_element_count(type); ++i)
+            {
+                copy_propagation_invalidate_variable_from_deref_recurse(ctx, var_def, deref, subtype,
+                        depth + 1, i * subtype_comp_count, writemask);
+            }
+        }
+    }
+}
+
+static void copy_propagation_invalidate_variable_from_deref(struct hlsl_ctx *ctx,
+        struct copy_propagation_var_def *var_def, const struct hlsl_deref *deref, unsigned char writemask)
+{
+    copy_propagation_invalidate_variable_from_deref_recurse(ctx, var_def, deref, deref->var->data_type,
+            0, 0, writemask);
 }
 
-static void copy_propagation_set_value(struct copy_propagation_var_def *var_def, unsigned int offset,
-        unsigned char writemask, struct hlsl_ir_node *node)
+static void copy_propagation_set_value(struct copy_propagation_var_def *var_def, unsigned int comp,
+        unsigned char writemask, struct hlsl_ir_node *instr)
 {
     unsigned int i, j = 0;
 
@@ -623,59 +670,56 @@ static void copy_propagation_set_value(struct copy_propagation_var_def *var_def,
         if (writemask & (1u << i))
         {
             TRACE("Variable %s[%u] is written by instruction %p%s.\n",
-                    var_def->var->name, offset + i, node, debug_hlsl_writemask(1u << i));
-            var_def->values[offset + i].state = VALUE_STATE_STATICALLY_WRITTEN;
-            var_def->values[offset + i].node = node;
-            var_def->values[offset + i].component = j++;
+                    var_def->var->name, comp + i, instr, debug_hlsl_writemask(1u << i));
+            var_def->values[comp + i].state = VALUE_STATE_STATICALLY_WRITTEN;
+            var_def->values[comp + i].node = instr;
+            var_def->values[comp + i].component = j++;
         }
     }
 }
 
 static struct hlsl_ir_node *copy_propagation_compute_replacement(struct hlsl_ctx *ctx,
         const struct copy_propagation_state *state, const struct hlsl_deref *deref,
-        unsigned int count, unsigned int *swizzle)
+        unsigned int *swizzle)
 {
     const struct hlsl_ir_var *var = deref->var;
-    struct hlsl_ir_node *node = NULL;
-    unsigned int offset, i;
+    struct hlsl_ir_node *instr = NULL;
+    unsigned int start, count, i;
 
-    if (!hlsl_offset_from_deref(ctx, deref, &offset))
+    if (!hlsl_component_index_range_from_deref(ctx, deref, &start, &count))
         return NULL;
 
-    if (var->data_type->type != HLSL_CLASS_OBJECT)
-        assert(offset + count <= var->data_type->reg_size);
-
     *swizzle = 0;
 
     for (i = 0; i < count; ++i)
     {
-        struct copy_propagation_value *value = copy_propagation_get_value(state, var, offset + i);
+        struct copy_propagation_value *value = copy_propagation_get_value(state, var, start + i);
 
         if (!value)
             return NULL;
 
-        if (!node)
+        if (!instr)
         {
-            node = value->node;
+            instr = value->node;
         }
-        else if (node != value->node)
+        else if (instr != value->node)
         {
-            TRACE("No single source for propagating load from %s[%u-%u].\n", var->name, offset, offset + count);
+            TRACE("No single source for propagating load from %s[%u-%u].\n", var->name, start, start + count);
             return NULL;
         }
         *swizzle |= value->component << i * 2;
     }
 
     TRACE("Load from %s[%u-%u] propagated as instruction %p%s.\n",
-            var->name, offset, offset + count, node, debug_hlsl_swizzle(*swizzle, count));
-    return node;
+            var->name, start, start + count, instr, debug_hlsl_swizzle(*swizzle, count));
+    return instr;
 }
 
 static bool copy_propagation_transform_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 hlsl_type *type = node->data_type;
+    struct hlsl_ir_node *instr = &load->node, *new_instr;
+    struct hlsl_type *type = instr->data_type;
     struct hlsl_ir_swizzle *swizzle_node;
     unsigned int dimx = 0;
     unsigned int swizzle;
@@ -699,17 +743,17 @@ static bool copy_propagation_transform_load(struct hlsl_ctx *ctx,
             return false;
     }
 
-    if (!(new_node = copy_propagation_compute_replacement(ctx, state, &load->src, dimx, &swizzle)))
+    if (!(new_instr = copy_propagation_compute_replacement(ctx, state, &load->src, &swizzle)))
         return false;
 
     if (type->type != HLSL_CLASS_OBJECT)
     {
-        if (!(swizzle_node = hlsl_new_swizzle(ctx, swizzle, dimx, new_node, &node->loc)))
+        if (!(swizzle_node = hlsl_new_swizzle(ctx, swizzle, dimx, new_instr, &instr->loc)))
             return false;
-        list_add_before(&node->entry, &swizzle_node->node.entry);
-        new_node = &swizzle_node->node;
+        list_add_before(&instr->entry, &swizzle_node->node.entry);
+        new_instr = &swizzle_node->node;
     }
-    hlsl_replace_node(node, new_node);
+    hlsl_replace_node(instr, new_instr);
     return true;
 }
 
@@ -717,17 +761,18 @@ static bool copy_propagation_transform_object_load(struct hlsl_ctx *ctx,
         struct hlsl_deref *deref, struct copy_propagation_state *state)
 {
     struct hlsl_ir_load *load;
-    struct hlsl_ir_node *node;
+    struct hlsl_ir_node *instr;
     unsigned int swizzle;
 
-    if (!(node = copy_propagation_compute_replacement(ctx, state, deref, 1, &swizzle)))
+    if (!(instr = copy_propagation_compute_replacement(ctx, state, deref, &swizzle)))
         return false;
 
     /* Only HLSL_IR_LOAD can produce an object. */
-    load = hlsl_ir_load(node);
-    deref->var = load->src.var;
-    hlsl_src_remove(&deref->offset);
-    hlsl_src_from_node(&deref->offset, load->src.offset.node);
+    load = hlsl_ir_load(instr);
+
+    hlsl_cleanup_deref(deref);
+    hlsl_copy_deref(ctx, deref, &load->src);
+
     return true;
 }
 
@@ -748,22 +793,22 @@ static void copy_propagation_record_store(struct hlsl_ctx *ctx, struct hlsl_ir_s
     struct copy_propagation_var_def *var_def;
     struct hlsl_deref *lhs = &store->lhs;
     struct hlsl_ir_var *var = lhs->var;
-    unsigned int offset;
+    unsigned int start, count;
 
     if (!(var_def = copy_propagation_create_var_def(ctx, state, var)))
         return;
 
-    if (hlsl_offset_from_deref(ctx, lhs, &offset))
+    if (hlsl_component_index_range_from_deref(ctx, lhs, &start, &count))
     {
         unsigned int writemask = store->writemask;
 
         if (store->rhs.node->data_type->type == HLSL_CLASS_OBJECT)
             writemask = VKD3DSP_WRITEMASK_0;
-        copy_propagation_set_value(var_def, offset, writemask, store->rhs.node);
+        copy_propagation_set_value(var_def, start, writemask, store->rhs.node);
     }
     else
     {
-        copy_propagation_invalidate_whole_variable(var_def);
+        copy_propagation_invalidate_variable_from_deref(ctx, var_def, lhs, store->writemask);
     }
 }
 
@@ -794,15 +839,11 @@ static void copy_propagation_invalidate_from_block(struct hlsl_ctx *ctx, struct
                 struct copy_propagation_var_def *var_def;
                 struct hlsl_deref *lhs = &store->lhs;
                 struct hlsl_ir_var *var = lhs->var;
-                unsigned int offset;
 
                 if (!(var_def = copy_propagation_create_var_def(ctx, state, var)))
                     continue;
 
-                if (hlsl_offset_from_deref(ctx, lhs, &offset))
-                    copy_propagation_invalidate_variable(var_def, offset, store->writemask);
-                else
-                    copy_propagation_invalidate_whole_variable(var_def);
+                copy_propagation_invalidate_variable_from_deref(ctx, var_def, lhs, store->writemask);
 
                 break;
             }
@@ -926,6 +967,51 @@ static bool copy_propagation_execute(struct hlsl_ctx *ctx, struct hlsl_block *bl
     return progress;
 }
 
+static void note_non_static_deref_expressions(struct hlsl_ctx *ctx, const struct hlsl_deref *deref,
+        const char *usage)
+{
+    unsigned int i;
+
+    for (i = 0; i < deref->path_len; ++i)
+    {
+        struct hlsl_ir_node *path_node = deref->path[i].node;
+
+        assert(path_node);
+        if (path_node->type != HLSL_IR_CONSTANT)
+            hlsl_note(ctx, &path_node->loc, VKD3D_SHADER_LOG_ERROR,
+                    "Expression for %s within \"%s\" cannot be resolved statically.",
+                    usage, deref->var->name);
+    }
+}
+
+static bool validate_static_object_references(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr,
+        void *context)
+{
+    unsigned int start, count;
+
+    if (instr->type == HLSL_IR_RESOURCE_LOAD)
+    {
+        struct hlsl_ir_resource_load *load = hlsl_ir_resource_load(instr);
+
+        if (!hlsl_component_index_range_from_deref(ctx, &load->resource, &start, &count))
+        {
+            hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_NON_STATIC_OBJECT_REF,
+                    "Loaded resource from \"%s\" must be determinable at compile time.",
+                    load->resource.var->name);
+            note_non_static_deref_expressions(ctx, &load->resource, "loaded resource");
+        }
+        if (load->sampler.var && !hlsl_component_index_range_from_deref(ctx, &load->sampler, &start, &count))
+        {
+            hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_NON_STATIC_OBJECT_REF,
+                    "Resource load sampler from \"%s\" must be determinable at compile time.",
+                    load->sampler.var->name);
+            note_non_static_deref_expressions(ctx, &load->sampler, "resource load sampler");
+        }
+    }
+
+    return false;
+}
+
 static bool is_vec1(const struct hlsl_type *type)
 {
     return (type->type == HLSL_CLASS_SCALAR) || (type->type == HLSL_CLASS_VECTOR && type->dimx == 1);
@@ -1996,6 +2082,82 @@ static void allocate_objects(struct hlsl_ctx *ctx, enum hlsl_base_type type)
     }
 }
 
+bool hlsl_component_index_range_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref *deref,
+        unsigned int *start, unsigned int *count)
+{
+    struct hlsl_type *type = deref->var->data_type;
+    unsigned int i, k;
+
+    *start = 0;
+    *count = 0;
+
+    for (i = 0; i < deref->path_len; ++i)
+    {
+        struct hlsl_ir_node *path_node = deref->path[i].node;
+        unsigned int idx = 0;
+
+        assert(path_node);
+        if (path_node->type != HLSL_IR_CONSTANT)
+            return false;
+
+        /* We should always have generated a cast to UINT. */
+        assert(path_node->data_type->type == HLSL_CLASS_SCALAR
+                && path_node->data_type->base_type == HLSL_TYPE_UINT);
+
+        idx = hlsl_ir_constant(path_node)->value[0].u;
+
+        switch (type->type)
+        {
+            case HLSL_CLASS_VECTOR:
+                if (idx >= type->dimx)
+                {
+                    hlsl_error(ctx, &path_node->loc, VKD3D_SHADER_ERROR_HLSL_OFFSET_OUT_OF_BOUNDS,
+                            "Vector index is out of bounds. %u/%u", idx, type->dimx);
+                    return false;
+                }
+                *start += idx;
+                break;
+
+            case HLSL_CLASS_MATRIX:
+                if (idx >= hlsl_type_major_size(type))
+                {
+                    hlsl_error(ctx, &path_node->loc, VKD3D_SHADER_ERROR_HLSL_OFFSET_OUT_OF_BOUNDS,
+                            "Matrix index is out of bounds. %u/%u", idx, hlsl_type_major_size(type));
+                    return false;
+                }
+                if (hlsl_type_is_row_major(type))
+                    *start += idx * type->dimx;
+                else
+                    *start += idx * type->dimy;
+                break;
+
+            case HLSL_CLASS_ARRAY:
+                if (idx >= type->e.array.elements_count)
+                {
+                    hlsl_error(ctx, &path_node->loc, VKD3D_SHADER_ERROR_HLSL_OFFSET_OUT_OF_BOUNDS,
+                            "Array index is out of bounds. %u/%u", idx, type->e.array.elements_count);
+                    return false;
+                }
+                *start += idx * hlsl_type_component_count(type->e.array.type);
+                break;
+
+            case HLSL_CLASS_STRUCT:
+                for (k = 0; k < idx; ++k)
+                    *start += hlsl_type_component_count(type->e.record.fields[k].type);
+                break;
+
+            default:
+                assert(0);
+                break;
+        }
+
+        type = hlsl_get_element_type_from_path_index(ctx, type, path_node);
+    }
+
+    *count = hlsl_type_component_count(type);
+    return true;
+}
+
 bool hlsl_offset_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref *deref, unsigned int *offset)
 {
     struct hlsl_ir_node *offset_node = deref->offset.node;
@@ -2105,8 +2267,6 @@ int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry
     while (progress);
     transform_ir(ctx, split_matrix_copies, body, NULL);
 
-    transform_ir(ctx, transform_deref_paths_into_offsets, body, NULL); /* TODO: move forward, remove when no longer needed */
-
     transform_ir(ctx, lower_narrowing_casts, body, NULL);
     transform_ir(ctx, lower_casts_to_bool, body, NULL);
     do
@@ -2121,6 +2281,12 @@ int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry
     if (ctx->profile->major_version < 4)
         transform_ir(ctx, lower_division, body, NULL);
 
+    transform_ir(ctx, validate_static_object_references, body, NULL);
+
+    /* TODO: move forward, remove when no longer needed */
+    transform_ir(ctx, transform_deref_paths_into_offsets, body, NULL);
+    while (transform_ir(ctx, hlsl_fold_constant_exprs, body, NULL));
+
     do
         compute_liveness(ctx, entry_func);
     while (transform_ir(ctx, dce, body, NULL));
diff --git a/libs/vkd3d-shader/hlsl_sm4.c b/libs/vkd3d-shader/hlsl_sm4.c
index cdd73e571e3eb50a281b69388014d4c0541be7ce..fab2abe2c45bebcfc17bfa18861082dd636dc0e1 100644
--- a/libs/vkd3d-shader/hlsl_sm4.c
+++ b/libs/vkd3d-shader/hlsl_sm4.c
@@ -2082,11 +2082,23 @@ static void write_sm4_resource_load(struct hlsl_ctx *ctx,
     const struct hlsl_ir_node *texel_offset = load->texel_offset.node;
     const struct hlsl_ir_node *coords = load->coords.node;
 
+    if (resource_type->type != HLSL_CLASS_OBJECT)
+    {
+        assert(resource_type->type == HLSL_CLASS_ARRAY || resource_type->type == HLSL_CLASS_STRUCT);
+        hlsl_fixme(ctx, &load->node.loc, "Resource being a component of another variable.");
+        return;
+    }
+
     if (load->sampler.var)
     {
         const struct hlsl_type *sampler_type = load->sampler.var->data_type;
 
-        assert(sampler_type->type == HLSL_CLASS_OBJECT);
+        if (sampler_type->type != HLSL_CLASS_OBJECT)
+        {
+            assert(sampler_type->type == HLSL_CLASS_ARRAY || sampler_type->type == HLSL_CLASS_STRUCT);
+            hlsl_fixme(ctx, &load->node.loc, "Sampler being a component of another variable.");
+            return;
+        }
         assert(sampler_type->base_type == HLSL_TYPE_SAMPLER);
         assert(sampler_type->sampler_dim == HLSL_SAMPLER_DIM_GENERIC);
 
diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h
index 8bde5523425a8b198e84780aab60eeda9b59a87d..aca5606b0dcfd07ec869f5956c8a1307731a505d 100644
--- a/libs/vkd3d-shader/vkd3d_shader_private.h
+++ b/libs/vkd3d-shader/vkd3d_shader_private.h
@@ -118,6 +118,7 @@ enum vkd3d_shader_error
     VKD3D_SHADER_ERROR_HLSL_OFFSET_OUT_OF_BOUNDS        = 5019,
     VKD3D_SHADER_ERROR_HLSL_INCOMPATIBLE_PROFILE        = 5020,
     VKD3D_SHADER_ERROR_HLSL_DIVISION_BY_ZERO            = 5021,
+    VKD3D_SHADER_ERROR_HLSL_NON_STATIC_OBJECT_REF       = 5022,
 
     VKD3D_SHADER_WARNING_HLSL_IMPLICIT_TRUNCATION       = 5300,
     VKD3D_SHADER_WARNING_HLSL_DIVISION_BY_ZERO          = 5301,
diff --git a/tests/hlsl-initializer-implicit-array.shader_test b/tests/hlsl-initializer-implicit-array.shader_test
index 648825eb3d6d0e1e3b85deca5141d0015081501e..38c8234cbb54b7c38e2fa375d73fe5e858fdbd84 100644
--- a/tests/hlsl-initializer-implicit-array.shader_test
+++ b/tests/hlsl-initializer-implicit-array.shader_test
@@ -7,8 +7,8 @@ float4 main() : SV_TARGET
 }
 
 [test]
-todo draw quad
-todo probe all rgba (50, 60, 70, 80)
+draw quad
+probe all rgba (50, 60, 70, 80)
 
 
 [pixel shader]
@@ -21,8 +21,8 @@ float4 main() : sv_target
 }
 
 [test]
-todo draw quad
-todo probe all rgba (5.0, 6.0, 7.0, 8.0)
+draw quad
+probe all rgba (5.0, 6.0, 7.0, 8.0)
 
 
 [pixel shader]
@@ -34,8 +34,8 @@ float4 main() : sv_target
 }
 
 [test]
-todo draw quad
-todo probe all rgba (7.0, 8.0, 9.0, 10.0)
+draw quad
+probe all rgba (7.0, 8.0, 9.0, 10.0)
 
 
 [pixel shader]
@@ -62,8 +62,8 @@ float4 main() : SV_TARGET
 }
 
 [test]
-todo draw quad
-todo probe all rgba (318.0, 320.0, 322.0, 324.0)
+draw quad
+probe all rgba (318.0, 320.0, 322.0, 324.0)
 
 
 [pixel shader fail]
diff --git a/tests/hlsl-initializer-objects.shader_test b/tests/hlsl-initializer-objects.shader_test
index 2306d07f271368f3881d81ab36ed06b1c263fc7e..dcb60029100098bb42770dba74d25e030482bee9 100644
--- a/tests/hlsl-initializer-objects.shader_test
+++ b/tests/hlsl-initializer-objects.shader_test
@@ -25,8 +25,8 @@ float4 main() : sv_target
 }
 
 [test]
-todo draw quad
-todo probe all rgba (0.2, 0.2, 0.2, 0.1)
+draw quad
+probe all rgba (0.2, 0.2, 0.2, 0.1)
 
 
 [pixel shader]
@@ -71,7 +71,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader fail todo]
+[pixel shader fail]
 Texture2D tex;
 
 struct foo
@@ -90,7 +90,7 @@ float4 main() : sv_target
 }
 
 
-[pixel shader fail todo]
+[pixel shader fail]
 Texture2D tex;
 
 struct foo
diff --git a/tests/object-references.shader_test b/tests/object-references.shader_test
new file mode 100644
index 0000000000000000000000000000000000000000..6cff351a5f6413970d559d1f753ca736966ba55c
--- /dev/null
+++ b/tests/object-references.shader_test
@@ -0,0 +1,120 @@
+[require]
+shader model >= 4.0
+
+[texture 0]
+size (2, 2)
+0.1 0.2 0.3 0.4     0.5 0.7 0.6 0.8
+0.6 0.5 0.2 0.1     0.8 0.0 0.7 1.0
+
+[pixel shader]
+Texture2D t;
+
+struct foo {
+    int3 a;
+    Texture2D b;
+};
+
+float4 main(float4 pos : sv_position) : sv_target
+{
+    struct foo q;
+
+    q.a = int3(pos.xy, 0);
+    q.b = t;
+    return q.b.Load(q.a);
+}
+
+[test]
+draw quad
+probe (0, 0) rgba (0.1, 0.2, 0.3, 0.4)
+probe (1, 0) rgba (0.5, 0.7, 0.6, 0.8)
+probe (0, 1) rgba (0.6, 0.5, 0.2, 0.1)
+probe (1, 1) rgba (0.8, 0.0, 0.7, 1.0)
+
+
+[texture 0]
+size (1, 1)
+1.0 1.0 1.0 1.0
+
+[texture 1]
+size (1, 1)
+2.0 2.0 2.0 1.0
+
+[texture 2]
+size (1, 1)
+3.0 3.0 3.0 1.0
+
+[pixel shader]
+Texture2D tex[3];
+
+struct foo {
+    float4 p;
+    Texture2D t;
+};
+
+float4 main() : sv_target
+{
+    struct foo s[3];
+
+    s[0].t = tex[0];
+    s[1].t = tex[1];
+    s[2].t = tex[2];
+    return 100 * s[2].t.Load(0) + 10 * s[0].t.Load(0) + s[1].t.Load(0);
+}
+
+[test]
+todo draw quad
+todo probe all rgba (312, 312, 312, 111)
+
+
+[pixel shader]
+Texture2D tex1;
+Texture2D tex2;
+Texture2D tex3;
+
+float4 main() : sv_target
+{
+    Texture2D t[3][2];
+
+    t[0][0] = tex1; // Note: Only invalid in shader model 5.1, array ref. cannot be used as l-value.
+    t[0][1] = tex2;
+    t[1][0] = tex3;
+    t[1][1] = tex1;
+    t[2][0] = tex2;
+    t[2][1] = tex3;
+    return 1000 * t[2][0].Load(0) + 100 * t[1][1].Load(0) + 10 * t[2][1].Load(0) + t[0][1].Load(0);
+}
+
+[test]
+draw quad
+probe all rgba (2132, 2132, 2132, 1111)
+
+
+[pixel shader fail]
+Texture2D tex[3];
+uniform int n;
+
+struct foo {
+    float4 p;
+    Texture2D t;
+};
+
+float4 main() : sv_target
+{
+    struct foo s[3];
+
+    s[0].t = tex[0];
+    s[1].t = tex[1];
+    s[2].t = tex[2];
+    return s[n].t.Load(0);
+}
+
+
+[pixel shader fail]
+// Note: Only valid in shader model 5.1
+Texture2D tex[3];
+uniform int n;
+
+float4 main() : sv_target
+{
+    return tex[n].Load(0);
+}