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_dead_code.c
26 *
27 * This is a simple dead code eliminator for SSA values in QIR.
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->QIR.
36 */
37
38#include "vc4_qir.h"
39
40static bool debug;
41
42static void
43dce(struct vc4_compile *c, struct qinst *inst)
44{
45        if (debug) {
46                fprintf(stderr, "Removing: ");
47                qir_dump_inst(c, inst);
48                fprintf(stderr, "\n");
49        }
50        assert(!inst->sf);
51        qir_remove_instruction(c, inst);
52}
53
54static bool
55has_nonremovable_reads(struct vc4_compile *c, struct qinst *inst)
56{
57        for (int i = 0; i < qir_get_nsrc(inst); i++) {
58                if (inst->src[i].file == QFILE_VPM) {
59                        uint32_t attr = inst->src[i].index / 4;
60                        uint32_t offset = (inst->src[i].index % 4) * 4;
61
62                        if (c->vattr_sizes[attr] != offset + 4)
63                                return true;
64
65                        /* Can't get rid of the last VPM read, or the
66                         * simulator (at least) throws an error.
67                         */
68                        uint32_t total_size = 0;
69                        for (uint32_t i = 0; i < ARRAY_SIZE(c->vattr_sizes); i++)
70                                total_size += c->vattr_sizes[i];
71                        if (total_size == 4)
72                                return true;
73                }
74
75                if (inst->src[i].file == QFILE_VARY &&
76                    c->input_slots[inst->src[i].index].slot == 0xff) {
77                        return true;
78                }
79        }
80
81        return false;
82}
83
84bool
85qir_opt_dead_code(struct vc4_compile *c)
86{
87        bool progress = false;
88        bool *used = calloc(c->num_temps, sizeof(bool));
89
90        qir_for_each_inst_inorder(inst, c) {
91                for (int i = 0; i < qir_get_nsrc(inst); i++) {
92                        if (inst->src[i].file == QFILE_TEMP)
93                                used[inst->src[i].index] = true;
94                }
95        }
96
97        qir_for_each_block(block, c) {
98                qir_for_each_inst_safe(inst, block) {
99                        if (inst->dst.file != QFILE_NULL &&
100                            !(inst->dst.file == QFILE_TEMP &&
101                              !used[inst->dst.index])) {
102                                continue;
103                        }
104
105                        if (qir_has_side_effects(c, inst))
106                                continue;
107
108                        if (inst->sf ||
109                            has_nonremovable_reads(c, inst)) {
110                                /* If we can't remove the instruction, but we
111                                 * don't need its destination value, just
112                                 * remove the destination.  The register
113                                 * allocator would trivially color it and it
114                                 * wouldn't cause any register pressure, but
115                                 * it's nicer to read the QIR code without
116                                 * unused destination regs.
117                                 */
118                                if (inst->dst.file == QFILE_TEMP) {
119                                        if (debug) {
120                                                fprintf(stderr,
121                                                        "Removing dst from: ");
122                                                qir_dump_inst(c, inst);
123                                                fprintf(stderr, "\n");
124                                        }
125                                        c->defs[inst->dst.index] = NULL;
126                                        inst->dst.file = QFILE_NULL;
127                                        progress = true;
128                                }
129                                continue;
130                        }
131
132                        for (int i = 0; i < qir_get_nsrc(inst); i++) {
133                                if (inst->src[i].file != QFILE_VPM)
134                                        continue;
135                                uint32_t attr = inst->src[i].index / 4;
136                                uint32_t offset = (inst->src[i].index % 4) * 4;
137
138                                if (c->vattr_sizes[attr] == offset + 4) {
139                                        c->num_inputs--;
140                                        c->vattr_sizes[attr] -= 4;
141                                }
142                        }
143
144                        dce(c, inst);
145                        progress = true;
146                        continue;
147                }
148        }
149
150        free(used);
151
152        return progress;
153}
154