Path: blob/21.2-virgl/src/broadcom/compiler/vir_opt_dead_code.c
4564 views
/*1* Copyright © 2014 Broadcom2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS20* IN THE SOFTWARE.21*/2223/**24* @file v3d_opt_dead_code.c25*26* This is a simple dead code eliminator for SSA values in VIR.27*28* It walks all the instructions finding what temps are used, then walks again29* to remove instructions writing unused temps.30*31* This is an inefficient implementation if you have long chains of32* instructions where the entire chain is dead, but we expect those to have33* been eliminated at the NIR level, and here we're just cleaning up small34* problems produced by NIR->VIR.35*/3637#include "v3d_compiler.h"3839static bool debug;4041static void42dce(struct v3d_compile *c, struct qinst *inst)43{44if (debug) {45fprintf(stderr, "Removing: ");46vir_dump_inst(c, inst);47fprintf(stderr, "\n");48}49assert(!v3d_qpu_writes_flags(&inst->qpu));50vir_remove_instruction(c, inst);51}5253static bool54has_nonremovable_reads(struct v3d_compile *c, struct qinst *inst)55{56for (int i = 0; i < vir_get_nsrc(inst); i++) {57if (inst->src[i].file == QFILE_VPM)58return true;59}6061return false;62}6364static bool65can_write_to_null(struct v3d_compile *c, struct qinst *inst)66{67/* The SFU instructions must write to a physical register. */68if (c->devinfo->ver >= 41 && v3d_qpu_uses_sfu(&inst->qpu))69return false;7071return true;72}7374static void75vir_dce_flags(struct v3d_compile *c, struct qinst *inst)76{77if (debug) {78fprintf(stderr,79"Removing flags write from: ");80vir_dump_inst(c, inst);81fprintf(stderr, "\n");82}8384assert(inst->qpu.type == V3D_QPU_INSTR_TYPE_ALU);8586inst->qpu.flags.apf = V3D_QPU_PF_NONE;87inst->qpu.flags.mpf = V3D_QPU_PF_NONE;88inst->qpu.flags.auf = V3D_QPU_UF_NONE;89inst->qpu.flags.muf = V3D_QPU_UF_NONE;90}9192static bool93check_last_ldunifa(struct v3d_compile *c,94struct qinst *inst,95struct qblock *block)96{97if (!inst->qpu.sig.ldunifa && !inst->qpu.sig.ldunifarf)98return false;99100list_for_each_entry_from(struct qinst, scan_inst, inst->link.next,101&block->instructions, link) {102/* If we find a new write to unifa, then this was the last103* ldunifa in its sequence and is safe to remove.104*/105if (scan_inst->dst.file == QFILE_MAGIC &&106scan_inst->dst.index == V3D_QPU_WADDR_UNIFA) {107return true;108}109110/* If we find another ldunifa in the same sequence then we111* can't remove it.112*/113if (scan_inst->qpu.sig.ldunifa || scan_inst->qpu.sig.ldunifarf)114return false;115}116117return true;118}119120static bool121check_first_ldunifa(struct v3d_compile *c,122struct qinst *inst,123struct qblock *block,124struct qinst **unifa)125{126if (!inst->qpu.sig.ldunifa && !inst->qpu.sig.ldunifarf)127return false;128129list_for_each_entry_from_rev(struct qinst, scan_inst, inst->link.prev,130&block->instructions, link) {131/* If we find a write to unifa, then this was the first132* ldunifa in its sequence and is safe to remove.133*/134if (scan_inst->dst.file == QFILE_MAGIC &&135scan_inst->dst.index == V3D_QPU_WADDR_UNIFA) {136*unifa = scan_inst;137return true;138}139140/* If we find another ldunifa in the same sequence then we141* can't remove it.142*/143if (scan_inst->qpu.sig.ldunifa || scan_inst->qpu.sig.ldunifarf)144return false;145}146147unreachable("could not find starting unifa for ldunifa sequence");148}149150static bool151increment_unifa_address(struct v3d_compile *c, struct qinst *unifa)152{153if (unifa->qpu.type == V3D_QPU_INSTR_TYPE_ALU &&154unifa->qpu.alu.mul.op == V3D_QPU_M_MOV) {155c->cursor = vir_after_inst(unifa);156struct qreg unifa_reg = vir_reg(QFILE_MAGIC, V3D_QPU_WADDR_UNIFA);157vir_ADD_dest(c, unifa_reg, unifa->src[0], vir_uniform_ui(c, 4u));158vir_remove_instruction(c, unifa);159return true;160}161162if (unifa->qpu.type == V3D_QPU_INSTR_TYPE_ALU &&163unifa->qpu.alu.add.op == V3D_QPU_A_ADD) {164c->cursor = vir_after_inst(unifa);165struct qreg unifa_reg = vir_reg(QFILE_MAGIC, V3D_QPU_WADDR_UNIFA);166struct qreg tmp =167vir_ADD(c, unifa->src[1], vir_uniform_ui(c, 4u));168vir_ADD_dest(c, unifa_reg, unifa->src[0], tmp);169vir_remove_instruction(c, unifa);170return true;171}172173return false;174}175176bool177vir_opt_dead_code(struct v3d_compile *c)178{179bool progress = false;180bool *used = calloc(c->num_temps, sizeof(bool));181182/* Defuse the "are you removing the cursor?" assertion in the core.183* You'll need to set up a new cursor for any new instructions after184* doing DCE (which we would expect, anyway).185*/186c->cursor.link = NULL;187188vir_for_each_inst_inorder(inst, c) {189for (int i = 0; i < vir_get_nsrc(inst); i++) {190if (inst->src[i].file == QFILE_TEMP)191used[inst->src[i].index] = true;192}193}194195vir_for_each_block(block, c) {196struct qinst *last_flags_write = NULL;197198vir_for_each_inst_safe(inst, block) {199/* If this instruction reads the flags, we can't200* remove the flags generation for it.201*/202if (v3d_qpu_reads_flags(&inst->qpu))203last_flags_write = NULL;204205if (inst->dst.file != QFILE_NULL &&206!(inst->dst.file == QFILE_TEMP &&207!used[inst->dst.index])) {208continue;209}210211const bool is_ldunifa = inst->qpu.sig.ldunifa ||212inst->qpu.sig.ldunifarf;213214if (vir_has_side_effects(c, inst) && !is_ldunifa)215continue;216217bool is_first_ldunifa = false;218bool is_last_ldunifa = false;219struct qinst *unifa = NULL;220if (is_ldunifa) {221is_last_ldunifa =222check_last_ldunifa(c, inst, block);223224is_first_ldunifa =225check_first_ldunifa(c, inst, block, &unifa);226}227228if (v3d_qpu_writes_flags(&inst->qpu)) {229/* If we obscure a previous flags write,230* drop it.231*/232if (last_flags_write &&233(inst->qpu.flags.apf != V3D_QPU_PF_NONE ||234inst->qpu.flags.mpf != V3D_QPU_PF_NONE)) {235vir_dce_flags(c, last_flags_write);236progress = true;237}238239last_flags_write = inst;240}241242if (v3d_qpu_writes_flags(&inst->qpu) ||243has_nonremovable_reads(c, inst) ||244(is_ldunifa && !is_first_ldunifa && !is_last_ldunifa)) {245/* If we can't remove the instruction, but we246* don't need its destination value, just247* remove the destination. The register248* allocator would trivially color it and it249* wouldn't cause any register pressure, but250* it's nicer to read the VIR code without251* unused destination regs.252*/253if (inst->dst.file == QFILE_TEMP &&254can_write_to_null(c, inst)) {255if (debug) {256fprintf(stderr,257"Removing dst from: ");258vir_dump_inst(c, inst);259fprintf(stderr, "\n");260}261c->defs[inst->dst.index] = NULL;262inst->dst.file = QFILE_NULL;263progress = true;264}265continue;266}267268/* If we are removing the first ldunifa in a sequence269* we need to update the unifa address.270*/271if (is_first_ldunifa) {272assert(unifa);273if (!increment_unifa_address(c, unifa))274continue;275}276277assert(inst != last_flags_write);278dce(c, inst);279progress = true;280continue;281}282}283284free(used);285286return progress;287}288289290