nir/loop_unroll: Prepare loop for unrolling in wrapper_unroll
[mesa.git] / src / compiler / nir / nir_deref.c
index f1e6eee7745a7a5e69958732a7691250418bcd6c..f7a07c6b465d9a66a4d3d1a844b385cfb16f68c8 100644 (file)
@@ -58,7 +58,7 @@ nir_deref_path_init(nir_deref_path *path,
 #ifndef NDEBUG
    /* Just in case someone uses short_path by accident */
    for (unsigned i = 0; i < ARRAY_SIZE(path->_short_path); i++)
-      path->_short_path[i] = (void *)0xdeadbeef;
+      path->_short_path[i] = (void *)(uintptr_t)0xdeadbeef;
 #endif
 
    path->path = ralloc_array(mem_ctx, nir_deref_instr *, count + 1);
@@ -121,20 +121,110 @@ nir_deref_instr_has_indirect(nir_deref_instr *instr)
    return false;
 }
 
+bool
+nir_deref_instr_is_known_out_of_bounds(nir_deref_instr *instr)
+{
+   for (; instr; instr = nir_deref_instr_parent(instr)) {
+      if (instr->deref_type == nir_deref_type_array &&
+          nir_src_is_const(instr->arr.index) &&
+           nir_src_as_uint(instr->arr.index) >=
+           glsl_get_length(nir_deref_instr_parent(instr)->type))
+         return true;
+   }
+
+   return false;
+}
+
+bool
+nir_deref_instr_has_complex_use(nir_deref_instr *deref)
+{
+   nir_foreach_use(use_src, &deref->dest.ssa) {
+      nir_instr *use_instr = use_src->parent_instr;
+
+      switch (use_instr->type) {
+      case nir_instr_type_deref: {
+         nir_deref_instr *use_deref = nir_instr_as_deref(use_instr);
+
+         /* A var deref has no sources */
+         assert(use_deref->deref_type != nir_deref_type_var);
+
+         /* If a deref shows up in an array index or something like that, it's
+          * a complex use.
+          */
+         if (use_src != &use_deref->parent)
+            return true;
+
+         /* Anything that isn't a basic struct or array deref is considered to
+          * be a "complex" use.  In particular, we don't allow ptr_as_array
+          * because we assume that opt_deref will turn any non-complex
+          * ptr_as_array derefs into regular array derefs eventually so passes
+          * which only want to handle simple derefs will pick them up in a
+          * later pass.
+          */
+         if (use_deref->deref_type != nir_deref_type_struct &&
+             use_deref->deref_type != nir_deref_type_array_wildcard &&
+             use_deref->deref_type != nir_deref_type_array)
+            return true;
+
+         if (nir_deref_instr_has_complex_use(use_deref))
+            return true;
+
+         continue;
+      }
+
+      case nir_instr_type_intrinsic: {
+         nir_intrinsic_instr *use_intrin = nir_instr_as_intrinsic(use_instr);
+         switch (use_intrin->intrinsic) {
+         case nir_intrinsic_load_deref:
+            assert(use_src == &use_intrin->src[0]);
+            continue;
+
+         case nir_intrinsic_copy_deref:
+            assert(use_src == &use_intrin->src[0] ||
+                   use_src == &use_intrin->src[1]);
+            continue;
+
+         case nir_intrinsic_store_deref:
+            /* A use in src[1] of a store means we're taking that pointer and
+             * writing it to a variable.  Because we have no idea who will
+             * read that variable and what they will do with the pointer, it's
+             * considered a "complex" use.  A use in src[0], on the other
+             * hand, is a simple use because we're just going to dereference
+             * it and write a value there.
+             */
+            if (use_src == &use_intrin->src[0])
+               continue;
+            return true;
+
+         default:
+            return true;
+         }
+         unreachable("Switch default failed");
+      }
+
+      default:
+         return true;
+      }
+   }
+
+   nir_foreach_if_use(use, &deref->dest.ssa)
+      return true;
+
+   return false;
+}
+
 unsigned
 nir_deref_instr_ptr_as_array_stride(nir_deref_instr *deref)
 {
-   assert(deref->deref_type == nir_deref_type_ptr_as_array);
-   nir_deref_instr *parent = nir_deref_instr_parent(deref);
-   switch (parent->deref_type) {
+   switch (deref->deref_type) {
    case nir_deref_type_array:
-      return glsl_get_explicit_stride(nir_deref_instr_parent(parent)->type);
+      return glsl_get_explicit_stride(nir_deref_instr_parent(deref)->type);
    case nir_deref_type_ptr_as_array:
-      return nir_deref_instr_ptr_as_array_stride(parent);
+      return nir_deref_instr_ptr_as_array_stride(nir_deref_instr_parent(deref));
    case nir_deref_type_cast:
-      return parent->cast.ptr_stride;
+      return deref->cast.ptr_stride;
    default:
-      unreachable("Invalid parent for ptr_as_array deref");
+      return 0;
    }
 }
 
@@ -601,6 +691,8 @@ rematerialize_deref_src(nir_src *src, void *_state)
  * used.  After this pass has been run, every use of a deref will be of a
  * deref in the same block as the use.  Also, all unused derefs will be
  * deleted as a side-effect.
+ *
+ * Derefs used as sources of phi instructions are not rematerialized.
  */
 bool
 nir_rematerialize_derefs_in_use_blocks_impl(nir_function_impl *impl)
@@ -620,6 +712,12 @@ nir_rematerialize_derefs_in_use_blocks_impl(nir_function_impl *impl)
              nir_deref_instr_remove_if_unused(nir_instr_as_deref(instr)))
             continue;
 
+         /* If a deref is used in a phi, we can't rematerialize it, as the new
+          * derefs would appear before the phi, which is not valid.
+          */
+         if (instr->type == nir_instr_type_phi)
+            continue;
+
          state.builder.cursor = nir_before_instr(instr);
          nir_foreach_src(instr, rematerialize_deref_src, &state);
       }