1 /* 2 * Copyright 2014 Broadcom 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 24 /** 25 * @file vc4_opt_copy_propagation.c 26 * 27 * This implements simple copy propagation for QIR without control flow. 28 * 29 * For each temp, it keeps a qreg of which source it was MOVed from, if it 30 * was. If we see that used later, we can just reuse the source value, since 31 * we know we don't have control flow, and we have SSA for our values so 32 * there's no killing to worry about. 33 */ 34 35 #include "vc4_qir.h" 36 37 static bool 38 is_copy_mov(struct qinst *inst) 39 { 40 if (!inst) 41 return false; 42 43 if (inst->op != QOP_MOV && 44 inst->op != QOP_FMOV && 45 inst->op != QOP_MMOV) { 46 return false; 47 } 48 49 if (inst->dst.file != QFILE_TEMP) 50 return false; 51 52 if (inst->src[0].file != QFILE_TEMP && 53 inst->src[0].file != QFILE_UNIF) { 54 return false; 55 } 56 57 if (inst->dst.pack || inst->cond != QPU_COND_ALWAYS) 58 return false; 59 60 return true; 61 62 } 63 64 static bool 65 try_copy_prop(struct vc4_compile *c, struct qinst *inst, struct qinst **movs) 66 { 67 bool debug = false; 68 bool progress = false; 69 70 for (int i = 0; i < qir_get_nsrc(inst); i++) { 71 if (inst->src[i].file != QFILE_TEMP) 72 continue; 73 74 /* We have two ways of finding MOVs we can copy propagate 75 * from. One is if it's an SSA def: then we can reuse it from 76 * any block in the program, as long as its source is also an 77 * SSA def. Alternatively, if it's in the "movs" array 78 * tracked within the block, then we know the sources for it 79 * haven't been changed since we saw the instruction within 80 * our block. 81 */ 82 struct qinst *mov = movs[inst->src[i].index]; 83 if (!mov) { 84 if (!is_copy_mov(c->defs[inst->src[i].index])) 85 continue; 86 mov = c->defs[inst->src[i].index]; 87 88 if (mov->src[0].file == QFILE_TEMP && 89 !c->defs[mov->src[0].index]) 90 continue; 91 } 92 93 /* Mul rotation's source needs to be in an r0-r3 accumulator, 94 * so no uniforms or regfile-a/r4 unpacking allowed. 95 */ 96 if (inst->op == QOP_ROT_MUL && 97 (mov->src[0].file != QFILE_TEMP || 98 mov->src[0].pack)) 99 continue; 100 101 uint8_t unpack; 102 if (mov->src[0].pack) { 103 /* Make sure that the meaning of the unpack 104 * would be the same between the two 105 * instructions. 106 */ 107 if (qir_is_float_input(inst) != 108 qir_is_float_input(mov)) { 109 continue; 110 } 111 112 /* There's only one unpack field, so make sure 113 * this instruction doesn't already use it. 114 */ 115 bool already_has_unpack = false; 116 for (int j = 0; j < qir_get_nsrc(inst); j++) { 117 if (inst->src[j].pack) 118 already_has_unpack = true; 119 } 120 if (already_has_unpack) 121 continue; 122 123 /* A destination pack requires the PM bit to 124 * be set to a specific value already, which 125 * may be different from ours. 126 */ 127 if (inst->dst.pack) 128 continue; 129 130 unpack = mov->src[0].pack; 131 } else { 132 unpack = inst->src[i].pack; 133 } 134 135 if (debug) { 136 fprintf(stderr, "Copy propagate: "); 137 qir_dump_inst(c, inst); 138 fprintf(stderr, "\n"); 139 } 140 141 inst->src[i] = mov->src[0]; 142 inst->src[i].pack = unpack; 143 144 if (debug) { 145 fprintf(stderr, "to: "); 146 qir_dump_inst(c, inst); 147 fprintf(stderr, "\n"); 148 } 149 150 progress = true; 151 } 152 153 return progress; 154 } 155 156 static void 157 apply_kills(struct vc4_compile *c, struct qinst **movs, struct qinst *inst) 158 { 159 if (inst->dst.file != QFILE_TEMP) 160 return; 161 162 for (int i = 0; i < c->num_temps; i++) { 163 if (movs[i] && 164 (movs[i]->dst.index == inst->dst.index || 165 (movs[i]->src[0].file == QFILE_TEMP && 166 movs[i]->src[0].index == inst->dst.index))) { 167 movs[i] = NULL; 168 } 169 } 170 } 171 172 bool 173 qir_opt_copy_propagation(struct vc4_compile *c) 174 { 175 bool progress = false; 176 struct qinst **movs; 177 178 movs = ralloc_array(c, struct qinst *, c->num_temps); 179 if (!movs) 180 return false; 181 182 qir_for_each_block(block, c) { 183 /* The MOVs array tracks only available movs within the 184 * block. 185 */ 186 memset(movs, 0, sizeof(struct qinst *) * c->num_temps); 187 188 qir_for_each_inst(inst, block) { 189 progress = try_copy_prop(c, inst, movs) || progress; 190 191 apply_kills(c, movs, inst); 192 193 if (is_copy_mov(inst)) 194 movs[inst->dst.index] = inst; 195 } 196 } 197 198 ralloc_free(movs); 199 200 return progress; 201 } 202