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 v3d_opt_dead_code.c 26 * 27 * This is a simple dead code eliminator for SSA values in VIR. 28 * 29 * It walks all the instructions finding what temps are used, then walks again 30 * to remove instructions writing unused temps. 31 * 32 * This is an inefficient implementation if you have long chains of 33 * instructions where the entire chain is dead, but we expect those to have 34 * been eliminated at the NIR level, and here we're just cleaning up small 35 * problems produced by NIR->VIR. 36 */ 37 38#include "v3d_compiler.h" 39 40static bool debug; 41 42static void 43dce(struct v3d_compile *c, struct qinst *inst) 44{ 45 if (debug) { 46 fprintf(stderr, "Removing: "); 47 vir_dump_inst(c, inst); 48 fprintf(stderr, "\n"); 49 } 50 assert(!v3d_qpu_writes_flags(&inst->qpu)); 51 vir_remove_instruction(c, inst); 52} 53 54static bool 55has_nonremovable_reads(struct v3d_compile *c, struct qinst *inst) 56{ 57 for (int i = 0; i < vir_get_nsrc(inst); i++) { 58 if (inst->src[i].file == QFILE_VPM) 59 return true; 60 } 61 62 return false; 63} 64 65static bool 66can_write_to_null(struct v3d_compile *c, struct qinst *inst) 67{ 68 /* The SFU instructions must write to a physical register. */ 69 if (c->devinfo->ver >= 41 && v3d_qpu_uses_sfu(&inst->qpu)) 70 return false; 71 72 return true; 73} 74 75static void 76vir_dce_flags(struct v3d_compile *c, struct qinst *inst) 77{ 78 if (debug) { 79 fprintf(stderr, 80 "Removing flags write from: "); 81 vir_dump_inst(c, inst); 82 fprintf(stderr, "\n"); 83 } 84 85 assert(inst->qpu.type == V3D_QPU_INSTR_TYPE_ALU); 86 87 inst->qpu.flags.apf = V3D_QPU_PF_NONE; 88 inst->qpu.flags.mpf = V3D_QPU_PF_NONE; 89 inst->qpu.flags.auf = V3D_QPU_UF_NONE; 90 inst->qpu.flags.muf = V3D_QPU_UF_NONE; 91} 92 93bool 94vir_opt_dead_code(struct v3d_compile *c) 95{ 96 bool progress = false; 97 bool *used = calloc(c->num_temps, sizeof(bool)); 98 99 /* Defuse the "are you removing the cursor?" assertion in the core. 100 * You'll need to set up a new cursor for any new instructions after 101 * doing DCE (which we would expect, anyway). 102 */ 103 c->cursor.link = NULL; 104 105 vir_for_each_inst_inorder(inst, c) { 106 for (int i = 0; i < vir_get_nsrc(inst); i++) { 107 if (inst->src[i].file == QFILE_TEMP) 108 used[inst->src[i].index] = true; 109 } 110 } 111 112 vir_for_each_block(block, c) { 113 struct qinst *last_flags_write = NULL; 114 115 vir_for_each_inst_safe(inst, block) { 116 /* If this instruction reads the flags, we can't 117 * remove the flags generation for it. 118 */ 119 if (v3d_qpu_reads_flags(&inst->qpu)) 120 last_flags_write = NULL; 121 122 if (inst->dst.file != QFILE_NULL && 123 !(inst->dst.file == QFILE_TEMP && 124 !used[inst->dst.index])) { 125 continue; 126 } 127 128 if (vir_has_side_effects(c, inst)) 129 continue; 130 131 if (v3d_qpu_writes_flags(&inst->qpu)) { 132 /* If we obscure a previous flags write, 133 * drop it. 134 */ 135 if (last_flags_write && 136 (inst->qpu.flags.apf != V3D_QPU_PF_NONE || 137 inst->qpu.flags.mpf != V3D_QPU_PF_NONE)) { 138 vir_dce_flags(c, last_flags_write); 139 progress = true; 140 } 141 142 last_flags_write = inst; 143 } 144 145 if (v3d_qpu_writes_flags(&inst->qpu) || 146 has_nonremovable_reads(c, inst)) { 147 /* If we can't remove the instruction, but we 148 * don't need its destination value, just 149 * remove the destination. The register 150 * allocator would trivially color it and it 151 * wouldn't cause any register pressure, but 152 * it's nicer to read the VIR code without 153 * unused destination regs. 154 */ 155 if (inst->dst.file == QFILE_TEMP && 156 can_write_to_null(c, inst)) { 157 if (debug) { 158 fprintf(stderr, 159 "Removing dst from: "); 160 vir_dump_inst(c, inst); 161 fprintf(stderr, "\n"); 162 } 163 c->defs[inst->dst.index] = NULL; 164 inst->dst.file = QFILE_NULL; 165 progress = true; 166 } 167 continue; 168 } 169 170 assert(inst != last_flags_write); 171 dce(c, inst); 172 progress = true; 173 continue; 174 } 175 } 176 177 free(used); 178 179 return progress; 180} 181