1/* 2 * Copyright © 2014 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 * IN THE SOFTWARE. 22 * 23 * Authors: 24 * Jason Ekstrand (jason@jlekstrand.net) 25 * 26 */ 27 28#include "nir.h" 29 30/* 31 * This pass lowers the neg, abs, and sat operations to source modifiers on 32 * ALU operations to make things nicer for the backend. It's just much 33 * easier to not have them when we're doing optimizations. 34 */ 35 36static bool 37nir_lower_to_source_mods_block(nir_block *block, 38 nir_lower_to_source_mods_flags options) 39{ 40 bool progress = false; 41 42 nir_foreach_instr(instr, block) { 43 if (instr->type != nir_instr_type_alu) 44 continue; 45 46 nir_alu_instr *alu = nir_instr_as_alu(instr); 47 48 bool lower_abs = (nir_op_infos[alu->op].num_inputs < 3) || 49 (options & nir_lower_triop_abs); 50 51 for (unsigned i = 0; i < nir_op_infos[alu->op].num_inputs; i++) { 52 if (!alu->src[i].src.is_ssa) 53 continue; 54 55 if (alu->src[i].src.ssa->parent_instr->type != nir_instr_type_alu) 56 continue; 57 58 nir_alu_instr *parent = nir_instr_as_alu(alu->src[i].src.ssa->parent_instr); 59 60 if (parent->dest.saturate) 61 continue; 62 63 switch (nir_alu_type_get_base_type(nir_op_infos[alu->op].input_types[i])) { 64 case nir_type_float: 65 if (!(options & nir_lower_float_source_mods)) 66 continue; 67 if (parent->op != nir_op_fmov) 68 continue; 69 break; 70 case nir_type_int: 71 if (!(options & nir_lower_int_source_mods)) 72 continue; 73 if (parent->op != nir_op_imov) 74 continue; 75 break; 76 default: 77 continue; 78 } 79 80 /* We can only do a rewrite if the source we are copying is SSA. 81 * Otherwise, moving the read might invalidly reorder reads/writes 82 * on a register. 83 */ 84 if (!parent->src[0].src.is_ssa) 85 continue; 86 87 if (!lower_abs && parent->src[0].abs) 88 continue; 89 90 nir_instr_rewrite_src(instr, &alu->src[i].src, parent->src[0].src); 91 if (alu->src[i].abs) { 92 /* abs trumps both neg and abs, do nothing */ 93 } else { 94 alu->src[i].negate = (alu->src[i].negate != parent->src[0].negate); 95 alu->src[i].abs |= parent->src[0].abs; 96 } 97 98 for (int j = 0; j < 4; ++j) { 99 if (!nir_alu_instr_channel_used(alu, i, j)) 100 continue; 101 alu->src[i].swizzle[j] = parent->src[0].swizzle[alu->src[i].swizzle[j]]; 102 } 103 104 if (list_empty(&parent->dest.dest.ssa.uses) && 105 list_empty(&parent->dest.dest.ssa.if_uses)) 106 nir_instr_remove(&parent->instr); 107 108 progress = true; 109 } 110 111 if (options & nir_lower_float_source_mods) { 112 switch (alu->op) { 113 case nir_op_fsat: 114 alu->op = nir_op_fmov; 115 alu->dest.saturate = true; 116 break; 117 case nir_op_fneg: 118 alu->op = nir_op_fmov; 119 alu->src[0].negate = !alu->src[0].negate; 120 break; 121 case nir_op_fabs: 122 alu->op = nir_op_fmov; 123 alu->src[0].abs = true; 124 alu->src[0].negate = false; 125 break; 126 default: 127 break; 128 } 129 } 130 131 if (options & nir_lower_int_source_mods) { 132 switch (alu->op) { 133 case nir_op_ineg: 134 alu->op = nir_op_imov; 135 alu->src[0].negate = !alu->src[0].negate; 136 break; 137 case nir_op_iabs: 138 alu->op = nir_op_imov; 139 alu->src[0].abs = true; 140 alu->src[0].negate = false; 141 break; 142 default: 143 break; 144 } 145 } 146 /* We've covered sources. Now we're going to try and saturate the 147 * destination if we can. 148 */ 149 150 if (!alu->dest.dest.is_ssa) 151 continue; 152 153 /* We can only saturate float destinations */ 154 if (nir_alu_type_get_base_type(nir_op_infos[alu->op].output_type) != 155 nir_type_float) 156 continue; 157 158 if (!(options & nir_lower_float_source_mods)) 159 continue; 160 161 if (!list_empty(&alu->dest.dest.ssa.if_uses)) 162 continue; 163 164 bool all_children_are_sat = true; 165 nir_foreach_use(child_src, &alu->dest.dest.ssa) { 166 assert(child_src->is_ssa); 167 nir_instr *child = child_src->parent_instr; 168 if (child->type != nir_instr_type_alu) { 169 all_children_are_sat = false; 170 continue; 171 } 172 173 nir_alu_instr *child_alu = nir_instr_as_alu(child); 174 if (child_alu->src[0].negate || child_alu->src[0].abs) { 175 all_children_are_sat = false; 176 continue; 177 } 178 179 if (child_alu->op != nir_op_fsat && 180 !(child_alu->op == nir_op_fmov && child_alu->dest.saturate)) { 181 all_children_are_sat = false; 182 continue; 183 } 184 } 185 186 if (!all_children_are_sat) 187 continue; 188 189 alu->dest.saturate = true; 190 progress = true; 191 192 nir_foreach_use(child_src, &alu->dest.dest.ssa) { 193 assert(child_src->is_ssa); 194 nir_alu_instr *child_alu = nir_instr_as_alu(child_src->parent_instr); 195 196 child_alu->op = nir_op_fmov; 197 child_alu->dest.saturate = false; 198 /* We could propagate the dest of our instruction to the 199 * destinations of the uses here. However, one quick round of 200 * copy propagation will clean that all up and then we don't have 201 * the complexity. 202 */ 203 } 204 } 205 206 return progress; 207} 208 209static bool 210nir_lower_to_source_mods_impl(nir_function_impl *impl, 211 nir_lower_to_source_mods_flags options) 212{ 213 bool progress = false; 214 215 nir_foreach_block(block, impl) { 216 progress |= nir_lower_to_source_mods_block(block, options); 217 } 218 219 if (progress) 220 nir_metadata_preserve(impl, nir_metadata_block_index | 221 nir_metadata_dominance); 222 223 return progress; 224} 225 226bool 227nir_lower_to_source_mods(nir_shader *shader, 228 nir_lower_to_source_mods_flags options) 229{ 230 bool progress = false; 231 232 nir_foreach_function(function, shader) { 233 if (function->impl) { 234 progress |= nir_lower_to_source_mods_impl(function->impl, options); 235 } 236 } 237 238 return progress; 239} 240