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