17ec681f3Smrg/*
27ec681f3Smrg * Copyright (C) 2021 Collabora, Ltd.
37ec681f3Smrg * Copyright (C) 2014 Valve Corporation
47ec681f3Smrg *
57ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
67ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
77ec681f3Smrg * to deal in the Software without restriction, including without limitation
87ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
97ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
107ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
117ec681f3Smrg *
127ec681f3Smrg * The above copyright notice and this permission notice (including the next
137ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
147ec681f3Smrg * Software.
157ec681f3Smrg *
167ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
177ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
187ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
197ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
207ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
217ec681f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
227ec681f3Smrg * SOFTWARE.
237ec681f3Smrg */
247ec681f3Smrg
257ec681f3Smrg#include "compiler.h"
267ec681f3Smrg#include "bi_builder.h"
277ec681f3Smrg
287ec681f3Smrg#define XXH_INLINE_ALL
297ec681f3Smrg#include "xxhash.h"
307ec681f3Smrg
317ec681f3Smrg/* This pass handles CSE'ing repeated expressions created in the process of
327ec681f3Smrg * translating from NIR. Also, currently this is intra-block only, to make it
337ec681f3Smrg * work over multiple block we'd need to bring forward dominance calculation.
347ec681f3Smrg */
357ec681f3Smrg
367ec681f3Smrgstatic inline uint32_t
377ec681f3SmrgHASH(uint32_t hash, unsigned data)
387ec681f3Smrg{
397ec681f3Smrg        return XXH32(&data, sizeof(data), hash);
407ec681f3Smrg}
417ec681f3Smrg
427ec681f3Smrgstatic uint32_t
437ec681f3Smrghash_index(uint32_t hash, bi_index index)
447ec681f3Smrg{
457ec681f3Smrg        hash = HASH(hash, index.value);
467ec681f3Smrg        hash = HASH(hash, index.abs);
477ec681f3Smrg        hash = HASH(hash, index.neg);
487ec681f3Smrg        hash = HASH(hash, index.swizzle);
497ec681f3Smrg        hash = HASH(hash, index.offset);
507ec681f3Smrg        hash = HASH(hash, index.reg);
517ec681f3Smrg        hash = HASH(hash, index.type);
527ec681f3Smrg        return hash;
537ec681f3Smrg}
547ec681f3Smrg
557ec681f3Smrg/* Hash an ALU instruction. */
567ec681f3Smrgstatic uint32_t
577ec681f3Smrghash_instr(const void *data)
587ec681f3Smrg{
597ec681f3Smrg        const bi_instr *I = data;
607ec681f3Smrg        uint32_t hash = 0;
617ec681f3Smrg
627ec681f3Smrg        hash = HASH(hash, I->op);
637ec681f3Smrg
647ec681f3Smrg        /* Explcitly skip destinations, except for size details */
657ec681f3Smrg        bi_foreach_dest(I, d) {
667ec681f3Smrg                hash = HASH(hash, I->dest[d].swizzle);
677ec681f3Smrg        }
687ec681f3Smrg
697ec681f3Smrg        bi_foreach_src(I, s) {
707ec681f3Smrg                hash = hash_index(hash, I->src[s]);
717ec681f3Smrg        }
727ec681f3Smrg
737ec681f3Smrg        /* Explicitly skip branch, regfmt, vecsize, no_spill, tdd, table */
747ec681f3Smrg        hash = HASH(hash, I->dest_mod);
757ec681f3Smrg
767ec681f3Smrg        /* Explicitly skip other immediates */
777ec681f3Smrg        hash = HASH(hash, I->shift);
787ec681f3Smrg
797ec681f3Smrg        for (unsigned i = 0; i < ARRAY_SIZE(I->flags); ++i)
807ec681f3Smrg                hash = HASH(hash, I->flags[i]);
817ec681f3Smrg
827ec681f3Smrg        return hash;
837ec681f3Smrg}
847ec681f3Smrg
857ec681f3Smrgstatic bool
867ec681f3Smrginstrs_equal(const void *_i1, const void *_i2)
877ec681f3Smrg{
887ec681f3Smrg        const bi_instr *i1 = _i1, *i2 = _i2;
897ec681f3Smrg
907ec681f3Smrg	if (i1->op != i2->op)
917ec681f3Smrg		return false;
927ec681f3Smrg
937ec681f3Smrg        /* Explicitly skip destinations */
947ec681f3Smrg
957ec681f3Smrg        bi_foreach_src(i1, s) {
967ec681f3Smrg                bi_index s1 = i1->src[s], s2 = i2->src[s];
977ec681f3Smrg
987ec681f3Smrg                if (memcmp(&s1, &s2, sizeof(s1)) != 0)
997ec681f3Smrg                        return false;
1007ec681f3Smrg	}
1017ec681f3Smrg
1027ec681f3Smrg        if (i1->dest_mod != i2->dest_mod)
1037ec681f3Smrg                return false;
1047ec681f3Smrg
1057ec681f3Smrg        if (i1->shift != i2->shift)
1067ec681f3Smrg                return false;
1077ec681f3Smrg
1087ec681f3Smrg        for (unsigned i = 0; i < ARRAY_SIZE(i1->flags); ++i) {
1097ec681f3Smrg                if (i1->flags[i] != i2->flags[i])
1107ec681f3Smrg                        return false;
1117ec681f3Smrg        }
1127ec681f3Smrg
1137ec681f3Smrg	return true;
1147ec681f3Smrg}
1157ec681f3Smrg
1167ec681f3Smrg/* Determines what instructions the above routines have to handle */
1177ec681f3Smrg
1187ec681f3Smrgstatic bool
1197ec681f3Smrginstr_can_cse(const bi_instr *I)
1207ec681f3Smrg{
1217ec681f3Smrg        switch (I->op)  {
1227ec681f3Smrg        case BI_OPCODE_DTSEL_IMM:
1237ec681f3Smrg        case BI_OPCODE_DISCARD_F32:
1247ec681f3Smrg                return false;
1257ec681f3Smrg        default:
1267ec681f3Smrg                break;
1277ec681f3Smrg        }
1287ec681f3Smrg
1297ec681f3Smrg        if (bi_opcode_props[I->op].message)
1307ec681f3Smrg                return false;
1317ec681f3Smrg
1327ec681f3Smrg        if (I->branch_target)
1337ec681f3Smrg                return false;
1347ec681f3Smrg
1357ec681f3Smrg        /* Refuse to CSE non-SSA destinations since the data flow analysis
1367ec681f3Smrg         * required is nontrivial */
1377ec681f3Smrg        bi_foreach_dest(I, d) {
1387ec681f3Smrg                if (!bi_is_null(I->dest[d]) && !bi_is_ssa(I->dest[d]))
1397ec681f3Smrg                        return false;
1407ec681f3Smrg        }
1417ec681f3Smrg
1427ec681f3Smrg        /* Similar refuse to CSE non-SSA sources */
1437ec681f3Smrg        bi_foreach_src(I, s) {
1447ec681f3Smrg                if (I->src[s].reg || I->src[s].type == BI_INDEX_REGISTER)
1457ec681f3Smrg                        return false;
1467ec681f3Smrg        }
1477ec681f3Smrg
1487ec681f3Smrg        return true;
1497ec681f3Smrg}
1507ec681f3Smrg
1517ec681f3Smrgvoid
1527ec681f3Smrgbi_opt_cse(bi_context *ctx)
1537ec681f3Smrg{
1547ec681f3Smrg        struct set *instr_set = _mesa_set_create(NULL, hash_instr, instrs_equal);
1557ec681f3Smrg
1567ec681f3Smrg        bi_foreach_block(ctx, block) {
1577ec681f3Smrg                bi_index *replacement = calloc(sizeof(bi_index), ((ctx->ssa_alloc + 1) << 2));
1587ec681f3Smrg                _mesa_set_clear(instr_set, NULL);
1597ec681f3Smrg
1607ec681f3Smrg                bi_foreach_instr_in_block(block, instr) {
1617ec681f3Smrg                        /* Rewrite before trying to CSE anything so we converge
1627ec681f3Smrg                         * locally in one iteration */
1637ec681f3Smrg                        bi_foreach_src(instr, s) {
1647ec681f3Smrg                                if (s == 0 && bi_opcode_props[instr->op].sr_read)
1657ec681f3Smrg                                        continue;
1667ec681f3Smrg
1677ec681f3Smrg                                if (!bi_is_ssa(instr->src[s]))
1687ec681f3Smrg                                        continue;
1697ec681f3Smrg
1707ec681f3Smrg                                bi_index repl = replacement[bi_word_node(instr->src[s])];
1717ec681f3Smrg                                if (!bi_is_null(repl))
1727ec681f3Smrg                                        instr->src[s] = bi_replace_index(instr->src[s], repl);
1737ec681f3Smrg                        }
1747ec681f3Smrg
1757ec681f3Smrg                        if (!instr_can_cse(instr))
1767ec681f3Smrg                                continue;
1777ec681f3Smrg
1787ec681f3Smrg                        bool found;
1797ec681f3Smrg                        struct set_entry *entry =
1807ec681f3Smrg                                _mesa_set_search_or_add(instr_set, instr, &found);
1817ec681f3Smrg                        if (found) {
1827ec681f3Smrg                                const bi_instr *match = entry->key;
1837ec681f3Smrg
1847ec681f3Smrg                                bi_foreach_dest(instr, d) {
1857ec681f3Smrg                                        if (!bi_is_null(instr->dest[d]))
1867ec681f3Smrg                                                replacement[bi_word_node(instr->dest[d])] = match->dest[d];
1877ec681f3Smrg                                }
1887ec681f3Smrg                        }
1897ec681f3Smrg                }
1907ec681f3Smrg
1917ec681f3Smrg                free(replacement);
1927ec681f3Smrg        }
1937ec681f3Smrg
1947ec681f3Smrg        _mesa_set_destroy(instr_set, NULL);
1957ec681f3Smrg}
196