17e102996Smaya/*
27e102996Smaya * Copyright © 2016 Intel Corporation
37e102996Smaya *
47e102996Smaya * Permission is hereby granted, free of charge, to any person obtaining a
57e102996Smaya * copy of this software and associated documentation files (the "Software"),
67e102996Smaya * to deal in the Software without restriction, including without limitation
77e102996Smaya * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87e102996Smaya * and/or sell copies of the Software, and to permit persons to whom the
97e102996Smaya * Software is furnished to do so, subject to the following conditions:
107e102996Smaya *
117e102996Smaya * The above copyright notice and this permission notice (including the next
127e102996Smaya * paragraph) shall be included in all copies or substantial portions of the
137e102996Smaya * Software.
147e102996Smaya *
157e102996Smaya * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167e102996Smaya * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177e102996Smaya * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187e102996Smaya * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197e102996Smaya * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
207e102996Smaya * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
217e102996Smaya * IN THE SOFTWARE.
227e102996Smaya *
237e102996Smaya * Authors:
247e102996Smaya *    Jason Ekstrand (jason@jlekstrand.net)
257e102996Smaya *
267e102996Smaya */
277e102996Smaya
287e102996Smaya/*
297e102996Smaya * This lowering pass converts references to variables with loads/stores to
307e102996Smaya * scratch space based on a few configurable parameters.
317e102996Smaya */
327e102996Smaya
337e102996Smaya#include "nir.h"
347e102996Smaya#include "nir_builder.h"
357e102996Smaya#include "nir_deref.h"
367e102996Smaya
377e102996Smayastatic void
387e102996Smayalower_load_store(nir_builder *b,
397e102996Smaya                 nir_intrinsic_instr *intrin,
407e102996Smaya                 glsl_type_size_align_func size_align)
417e102996Smaya{
427e102996Smaya   b->cursor = nir_before_instr(&intrin->instr);
437e102996Smaya
447e102996Smaya   nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);
457e102996Smaya   nir_variable *var = nir_deref_instr_get_variable(deref);
467e102996Smaya
477e102996Smaya   nir_ssa_def *offset =
487e102996Smaya      nir_iadd_imm(b, nir_build_deref_offset(b, deref, size_align),
497e102996Smaya                      var->data.location);
507e102996Smaya
517e102996Smaya   unsigned align, UNUSED size;
527e102996Smaya   size_align(deref->type, &size, &align);
537e102996Smaya
547e102996Smaya   if (intrin->intrinsic == nir_intrinsic_load_deref) {
557ec681f3Smrg      unsigned bit_size = intrin->dest.ssa.bit_size;
567ec681f3Smrg      nir_ssa_def *value = nir_load_scratch(
577ec681f3Smrg         b, intrin->num_components, bit_size == 1 ? 32 : bit_size, offset, .align_mul=align);
587ec681f3Smrg      if (bit_size == 1)
597ec681f3Smrg         value = nir_b2b1(b, value);
607ec681f3Smrg
617ec681f3Smrg      nir_ssa_def_rewrite_uses(&intrin->dest.ssa, value);
627e102996Smaya   } else {
637e102996Smaya      assert(intrin->intrinsic == nir_intrinsic_store_deref);
647e102996Smaya
657e102996Smaya      assert(intrin->src[1].is_ssa);
667e102996Smaya      nir_ssa_def *value = intrin->src[1].ssa;
677ec681f3Smrg      if (value->bit_size == 1)
687ec681f3Smrg         value = nir_b2b32(b, value);
697ec681f3Smrg
707ec681f3Smrg      nir_store_scratch(b, value, offset, .align_mul=align,
717ec681f3Smrg                           .write_mask=nir_intrinsic_write_mask(intrin));
727e102996Smaya   }
737e102996Smaya
747e102996Smaya   nir_instr_remove(&intrin->instr);
757e102996Smaya   nir_deref_instr_remove_if_unused(deref);
767e102996Smaya}
777e102996Smaya
787ec681f3Smrgstatic bool only_used_for_load_store(nir_deref_instr *deref)
797ec681f3Smrg{
807ec681f3Smrg   nir_foreach_use(src, &deref->dest.ssa) {
817ec681f3Smrg      if (!src->parent_instr)
827ec681f3Smrg         return false;
837ec681f3Smrg      if (src->parent_instr->type == nir_instr_type_deref) {
847ec681f3Smrg          if (!only_used_for_load_store(nir_instr_as_deref(src->parent_instr)))
857ec681f3Smrg            return false;
867ec681f3Smrg      } else if (src->parent_instr->type != nir_instr_type_intrinsic) {
877ec681f3Smrg         return false;
887ec681f3Smrg      } else {
897ec681f3Smrg         nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(src->parent_instr);
907ec681f3Smrg         if (intrin->intrinsic != nir_intrinsic_load_deref &&
917ec681f3Smrg             intrin->intrinsic != nir_intrinsic_store_deref)
927ec681f3Smrg            return false;
937ec681f3Smrg      }
947ec681f3Smrg   }
957ec681f3Smrg   return true;
967ec681f3Smrg}
977ec681f3Smrg
987e102996Smayabool
997e102996Smayanir_lower_vars_to_scratch(nir_shader *shader,
1007e102996Smaya                          nir_variable_mode modes,
1017e102996Smaya                          int size_threshold,
1027e102996Smaya                          glsl_type_size_align_func size_align)
1037e102996Smaya{
1047ec681f3Smrg   struct set *set = _mesa_pointer_set_create(NULL);
1057ec681f3Smrg
1067e102996Smaya   /* First, we walk the instructions and flag any variables we want to lower
1077e102996Smaya    * by removing them from their respective list and setting the mode to 0.
1087e102996Smaya    */
1097e102996Smaya   nir_foreach_function(function, shader) {
1107e102996Smaya      nir_foreach_block(block, function->impl) {
1117e102996Smaya         nir_foreach_instr(instr, block) {
1127e102996Smaya            if (instr->type != nir_instr_type_intrinsic)
1137e102996Smaya               continue;
1147e102996Smaya
1157e102996Smaya            nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
1167e102996Smaya            if (intrin->intrinsic != nir_intrinsic_load_deref &&
1177e102996Smaya                intrin->intrinsic != nir_intrinsic_store_deref)
1187e102996Smaya               continue;
1197e102996Smaya
1207e102996Smaya            nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);
1217ec681f3Smrg            if (!nir_deref_mode_is_one_of(deref, modes))
1227e102996Smaya               continue;
1237e102996Smaya
1247ec681f3Smrg            if (!nir_deref_instr_has_indirect(nir_src_as_deref(intrin->src[0])))
1257e102996Smaya               continue;
1267e102996Smaya
1277e102996Smaya            nir_variable *var = nir_deref_instr_get_variable(deref);
1287ec681f3Smrg            if (!var)
1297ec681f3Smrg               continue;
1307e102996Smaya
1317e102996Smaya            /* We set var->mode to 0 to indicate that a variable will be moved
1327e102996Smaya             * to scratch.  Don't assign a scratch location twice.
1337e102996Smaya             */
1347e102996Smaya            if (var->data.mode == 0)
1357e102996Smaya               continue;
1367e102996Smaya
1377e102996Smaya            unsigned var_size, var_align;
1387e102996Smaya            size_align(var->type, &var_size, &var_align);
1397e102996Smaya            if (var_size <= size_threshold)
1407e102996Smaya               continue;
1417e102996Smaya
1427ec681f3Smrg            _mesa_set_add(set, var);
1437ec681f3Smrg         }
1447ec681f3Smrg      }
1457ec681f3Smrg   }
1467ec681f3Smrg
1477ec681f3Smrg   if (set->entries == 0) {
1487ec681f3Smrg      _mesa_set_destroy(set, NULL);
1497ec681f3Smrg      return false;
1507ec681f3Smrg   }
1517ec681f3Smrg
1527ec681f3Smrg   nir_foreach_function(function, shader) {
1537ec681f3Smrg      nir_foreach_block(block, function->impl) {
1547ec681f3Smrg         nir_foreach_instr(instr, block) {
1557ec681f3Smrg            if (instr->type != nir_instr_type_deref)
1567ec681f3Smrg               continue;
1577ec681f3Smrg
1587ec681f3Smrg            nir_deref_instr *deref = nir_instr_as_deref(instr);
1597ec681f3Smrg            if (deref->deref_type != nir_deref_type_var)
1607ec681f3Smrg               continue;
1617ec681f3Smrg
1627ec681f3Smrg            struct set_entry *entry = _mesa_set_search(set, deref->var);
1637ec681f3Smrg            if (!entry)
1647ec681f3Smrg               continue;
1657e102996Smaya
1667ec681f3Smrg            if (!only_used_for_load_store(deref))
1677ec681f3Smrg               _mesa_set_remove(set, entry);
1687e102996Smaya         }
1697e102996Smaya      }
1707e102996Smaya   }
1717e102996Smaya
1727ec681f3Smrg   set_foreach(set, entry) {
1737ec681f3Smrg      nir_variable* var = (void*)entry->key;
1747ec681f3Smrg
1757ec681f3Smrg      /* Remove it from its list */
1767ec681f3Smrg      exec_node_remove(&var->node);
1777ec681f3Smrg      /* Invalid mode used to flag "moving to scratch" */
1787ec681f3Smrg      var->data.mode = 0;
1797ec681f3Smrg
1807ec681f3Smrg      /* We don't allocate space here as iteration in this loop is
1817ec681f3Smrg       * non-deterministic due to the nir_variable pointers. */
1827ec681f3Smrg      var->data.location = INT_MAX;
1837ec681f3Smrg   }
1847ec681f3Smrg
1857e102996Smaya   bool progress = false;
1867e102996Smaya   nir_foreach_function(function, shader) {
1877e102996Smaya      if (!function->impl)
1887e102996Smaya         continue;
1897e102996Smaya
1907e102996Smaya      nir_builder build;
1917e102996Smaya      nir_builder_init(&build, function->impl);
1927e102996Smaya
1937e102996Smaya      bool impl_progress = false;
1947e102996Smaya      nir_foreach_block(block, function->impl) {
1957e102996Smaya         nir_foreach_instr_safe(instr, block) {
1967e102996Smaya            if (instr->type != nir_instr_type_intrinsic)
1977e102996Smaya               continue;
1987e102996Smaya
1997e102996Smaya            nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
2007e102996Smaya            if (intrin->intrinsic != nir_intrinsic_load_deref &&
2017e102996Smaya                intrin->intrinsic != nir_intrinsic_store_deref)
2027e102996Smaya               continue;
2037e102996Smaya
2047e102996Smaya            nir_variable *var = nir_intrinsic_get_var(intrin, 0);
2057e102996Smaya            /* Variables flagged for lowering above have mode == 0 */
2067e102996Smaya            if (!var || var->data.mode)
2077e102996Smaya               continue;
2087e102996Smaya
2097ec681f3Smrg            if (var->data.location == INT_MAX) {
2107ec681f3Smrg               unsigned var_size, var_align;
2117ec681f3Smrg               size_align(var->type, &var_size, &var_align);
2127ec681f3Smrg
2137ec681f3Smrg               var->data.location = ALIGN_POT(shader->scratch_size, var_align);
2147ec681f3Smrg               shader->scratch_size = var->data.location + var_size;
2157ec681f3Smrg            }
2167ec681f3Smrg
2177e102996Smaya            lower_load_store(&build, intrin, size_align);
2187e102996Smaya            impl_progress = true;
2197e102996Smaya         }
2207e102996Smaya      }
2217e102996Smaya
2227e102996Smaya      if (impl_progress) {
2237e102996Smaya         progress = true;
2247e102996Smaya         nir_metadata_preserve(function->impl, nir_metadata_block_index |
2257e102996Smaya                                               nir_metadata_dominance);
2267ec681f3Smrg      } else {
2277ec681f3Smrg         nir_metadata_preserve(function->impl, nir_metadata_all);
2287e102996Smaya      }
2297e102996Smaya   }
2307e102996Smaya
2317ec681f3Smrg   _mesa_set_destroy(set, NULL);
2327ec681f3Smrg
2337e102996Smaya   return progress;
2347e102996Smaya}
235