1/*
2 * Copyright © 2019 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_redundant_flags.c
26 *
27 * This eliminates the APF/MPF flags for redundant flags updates.  These are
28 * often produced by our channel masking in nonuniform control flow.
29 */
30
31#include "v3d_compiler.h"
32
33static bool debug;
34
35static void
36vir_dce_pf(struct v3d_compile *c, struct qinst *inst)
37{
38        if (debug) {
39                fprintf(stderr,
40                        "Removing flags write from: ");
41                vir_dump_inst(c, inst);
42                fprintf(stderr, "\n");
43        }
44
45        assert(inst->qpu.type == V3D_QPU_INSTR_TYPE_ALU);
46
47        inst->qpu.flags.apf = V3D_QPU_PF_NONE;
48        inst->qpu.flags.mpf = V3D_QPU_PF_NONE;
49}
50
51static bool
52vir_sources_modified(struct qinst *srcs, struct qinst *write)
53{
54        for (int i = 0; i < vir_get_nsrc(srcs); i++) {
55                if (write->dst.file == QFILE_TEMP &&
56                    srcs->src[i].file == QFILE_TEMP &&
57                    srcs->src[i].index == write->dst.index) {
58                        return true;
59                }
60
61                /* assume magic regs may be modified by basically anything. */
62                if (srcs->src[i].file != QFILE_TEMP &&
63                    srcs->src[i].file != QFILE_SMALL_IMM)
64                        return true;
65        }
66
67        return false;
68}
69
70static bool
71vir_instr_flags_op_equal(struct qinst *a, struct qinst *b)
72{
73        for (int i = 0; i < vir_get_nsrc(a); i++) {
74                if (a->src[i].file != b->src[i].file ||
75                    a->src[i].index != b->src[i].index) {
76                        return false;
77                }
78        }
79
80        if (a->qpu.flags.apf != b->qpu.flags.apf ||
81            a->qpu.flags.mpf != b->qpu.flags.mpf ||
82            a->qpu.alu.add.op != b->qpu.alu.add.op ||
83            a->qpu.alu.mul.op != b->qpu.alu.mul.op ||
84            a->qpu.alu.add.a_unpack != b->qpu.alu.add.a_unpack ||
85            a->qpu.alu.add.b_unpack != b->qpu.alu.add.b_unpack ||
86            a->qpu.alu.add.output_pack != b->qpu.alu.add.output_pack ||
87            a->qpu.alu.mul.a_unpack != b->qpu.alu.mul.a_unpack ||
88            a->qpu.alu.mul.b_unpack != b->qpu.alu.mul.b_unpack ||
89            a->qpu.alu.mul.output_pack != b->qpu.alu.mul.output_pack) {
90                return false;
91        }
92
93        return true;
94}
95
96static bool
97vir_opt_redundant_flags_block(struct v3d_compile *c, struct qblock *block)
98{
99        struct qinst *last_flags = NULL;
100        bool progress = false;
101
102        c->cur_block = block;
103        vir_for_each_inst(inst, block) {
104                if (inst->qpu.type != V3D_QPU_INSTR_TYPE_ALU ||
105                    inst->qpu.flags.auf != V3D_QPU_UF_NONE ||
106                    inst->qpu.flags.muf != V3D_QPU_UF_NONE) {
107                        last_flags = NULL;
108                        continue;
109                }
110
111                /* Flags aren't preserved across a thrsw.
112                 *
113                 * In V3D 4.2+ flags are preserved across thread switches.
114                 */
115                if (c->devinfo->ver < 42) {
116                        if (inst->qpu.sig.thrsw)
117                                last_flags = NULL;
118                }
119
120                if (inst->qpu.flags.apf != V3D_QPU_PF_NONE ||
121                    inst->qpu.flags.mpf != V3D_QPU_PF_NONE) {
122                        if (last_flags &&
123                            vir_instr_flags_op_equal(inst, last_flags)) {
124                                vir_dce_pf(c, inst);
125                                progress = true;
126                        } else {
127                                last_flags = inst;
128                        }
129                }
130
131                if (last_flags && vir_sources_modified(last_flags, inst)) {
132                        last_flags = NULL;
133                }
134        }
135
136        return progress;
137}
138
139bool
140vir_opt_redundant_flags(struct v3d_compile *c)
141{
142        bool progress = false;
143
144        vir_for_each_block(block, c) {
145                progress = vir_opt_redundant_flags_block(c, block) || progress;
146        }
147
148        return progress;
149}
150