1/*
2 * Copyright © 2016 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_peephole_sf.c
26 *
27 * Quick optimization to eliminate unused or identical SF updates.
28 */
29
30#include "vc4_qir.h"
31#include "util/u_math.h"
32
33static bool debug;
34
35static void
36dump_from(struct vc4_compile *c, struct qinst *inst, const char *type)
37{
38        if (!debug)
39                return;
40
41        fprintf(stderr, "optimizing %s: ", type);
42        qir_dump_inst(c, inst);
43        fprintf(stderr, "\n");
44}
45
46static void
47dump_to(struct vc4_compile *c, struct qinst *inst)
48{
49        if (!debug)
50                return;
51
52        fprintf(stderr, "to: ");
53        qir_dump_inst(c, inst);
54        fprintf(stderr, "\n");
55}
56
57static bool
58inst_srcs_updated(struct qinst *inst, struct qinst *writer)
59{
60        /* If the sources get overwritten, stop tracking the
61         * last instruction writing SF.
62         */
63        switch (writer->dst.file) {
64        case QFILE_TEMP:
65                for (int i = 0; i < qir_get_nsrc(inst); i++) {
66                        if (inst->src[i].file == QFILE_TEMP &&
67                            inst->src[i].index == writer->dst.index) {
68                                return true;
69                        }
70                }
71                return false;
72        default:
73                return false;
74        }
75}
76
77static bool
78src_file_varies_on_reread(struct qreg reg)
79{
80        switch (reg.file) {
81        case QFILE_VARY:
82        case QFILE_VPM:
83                return true;
84        default:
85                return false;
86        }
87}
88
89static bool
90inst_result_equals(struct qinst *a, struct qinst *b)
91{
92        if (a->op != b->op ||
93            qir_depends_on_flags(a) ||
94            qir_depends_on_flags(b)) {
95                return false;
96        }
97
98        for (int i = 0; i < qir_get_nsrc(a); i++) {
99                if (!qir_reg_equals(a->src[i], b->src[i]) ||
100                    src_file_varies_on_reread(a->src[i]) ||
101                    src_file_varies_on_reread(b->src[i])) {
102                        return false;
103                }
104        }
105
106        return true;
107}
108
109static bool
110qir_opt_peephole_sf_block(struct vc4_compile *c, struct qblock *block)
111{
112        bool progress = false;
113        /* We don't have liveness dataflow analysis for flags, but we also
114         * never generate a use of flags across control flow, so just treat
115         * them as unused at block exit.
116         */
117        bool sf_live = false;
118        struct qinst *last_sf = NULL;
119
120        /* Walk the block from bottom to top, tracking if the SF is used, and
121         * removing unused or repeated ones.
122         */
123        qir_for_each_inst_rev(inst, block) {
124                if (inst->sf) {
125                        if (!sf_live) {
126                                /* Our instruction's SF isn't read, so drop it.
127                                 */
128                                dump_from(c, inst, "dead SF");
129                                inst->sf = false;
130                                dump_to(c, inst);
131                                progress = true;
132                        } else if (last_sf &&
133                                   inst_result_equals(last_sf, inst)) {
134                                /* The last_sf sets up same value as inst, so
135                                 * just drop the later one.
136                                 */
137                                dump_from(c, last_sf, "repeated SF");
138                                last_sf->sf = false;
139                                dump_to(c, last_sf);
140                                progress = true;
141                                last_sf = inst;
142                        } else {
143                                last_sf = inst;
144                        }
145                        sf_live = false;
146                }
147
148                if (last_sf) {
149                        if (inst_srcs_updated(last_sf, inst))
150                                last_sf = NULL;
151                }
152
153                if (qir_depends_on_flags(inst))
154                        sf_live = true;
155        }
156
157        return progress;
158}
159
160bool
161qir_opt_peephole_sf(struct vc4_compile *c)
162{
163        bool progress = false;
164
165        qir_for_each_block(block, c)
166                progress = qir_opt_peephole_sf_block(c, block) || progress;
167
168        return progress;
169}
170