160d1180bSTaylor Simpson /*
23fd49e22SMarco Liebel  *  Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
360d1180bSTaylor Simpson  *
460d1180bSTaylor Simpson  *  This program is free software; you can redistribute it and/or modify
560d1180bSTaylor Simpson  *  it under the terms of the GNU General Public License as published by
660d1180bSTaylor Simpson  *  the Free Software Foundation; either version 2 of the License, or
760d1180bSTaylor Simpson  *  (at your option) any later version.
860d1180bSTaylor Simpson  *
960d1180bSTaylor Simpson  *  This program is distributed in the hope that it will be useful,
1060d1180bSTaylor Simpson  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1160d1180bSTaylor Simpson  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1260d1180bSTaylor Simpson  *  GNU General Public License for more details.
1360d1180bSTaylor Simpson  *
1460d1180bSTaylor Simpson  *  You should have received a copy of the GNU General Public License
1560d1180bSTaylor Simpson  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
1660d1180bSTaylor Simpson  */
1760d1180bSTaylor Simpson 
1860d1180bSTaylor Simpson #include "qemu/osdep.h"
1960d1180bSTaylor Simpson #include "decode.h"
2060d1180bSTaylor Simpson #include "opcodes.h"
2160d1180bSTaylor Simpson #include "insn.h"
2260d1180bSTaylor Simpson #include "iclass.h"
2360d1180bSTaylor Simpson #include "mmvec/mmvec.h"
2460d1180bSTaylor Simpson #include "mmvec/decode_ext_mmvec.h"
2560d1180bSTaylor Simpson 
2660d1180bSTaylor Simpson static void
2760d1180bSTaylor Simpson check_new_value(Packet *pkt)
2860d1180bSTaylor Simpson {
2960d1180bSTaylor Simpson     /* .new value for a MMVector store */
3060d1180bSTaylor Simpson     int i, j;
3160d1180bSTaylor Simpson     const char *reginfo;
3260d1180bSTaylor Simpson     const char *destletters;
3360d1180bSTaylor Simpson     const char *dststr = NULL;
3460d1180bSTaylor Simpson     uint16_t def_opcode;
3560d1180bSTaylor Simpson     char letter;
3660d1180bSTaylor Simpson     int def_regnum;
3760d1180bSTaylor Simpson 
3860d1180bSTaylor Simpson     for (i = 1; i < pkt->num_insns; i++) {
3960d1180bSTaylor Simpson         uint16_t use_opcode = pkt->insn[i].opcode;
4060d1180bSTaylor Simpson         if (GET_ATTRIB(use_opcode, A_DOTNEWVALUE) &&
4160d1180bSTaylor Simpson             GET_ATTRIB(use_opcode, A_CVI) &&
4260d1180bSTaylor Simpson             GET_ATTRIB(use_opcode, A_STORE)) {
4360d1180bSTaylor Simpson             int use_regidx = strchr(opcode_reginfo[use_opcode], 's') -
4460d1180bSTaylor Simpson                 opcode_reginfo[use_opcode];
4560d1180bSTaylor Simpson             /*
4660d1180bSTaylor Simpson              * What's encoded at the N-field is the offset to who's producing
4760d1180bSTaylor Simpson              * the value.
4860d1180bSTaylor Simpson              * Shift off the LSB which indicates odd/even register.
4960d1180bSTaylor Simpson              */
5060d1180bSTaylor Simpson             int def_off = ((pkt->insn[i].regno[use_regidx]) >> 1);
5160d1180bSTaylor Simpson             int def_oreg = pkt->insn[i].regno[use_regidx] & 1;
5260d1180bSTaylor Simpson             int def_idx = -1;
5360d1180bSTaylor Simpson             for (j = i - 1; (j >= 0) && (def_off >= 0); j--) {
5460d1180bSTaylor Simpson                 if (!GET_ATTRIB(pkt->insn[j].opcode, A_CVI)) {
5560d1180bSTaylor Simpson                     continue;
5660d1180bSTaylor Simpson                 }
5760d1180bSTaylor Simpson                 def_off--;
5860d1180bSTaylor Simpson                 if (def_off == 0) {
5960d1180bSTaylor Simpson                     def_idx = j;
6060d1180bSTaylor Simpson                     break;
6160d1180bSTaylor Simpson                 }
6260d1180bSTaylor Simpson             }
6360d1180bSTaylor Simpson             /*
6460d1180bSTaylor Simpson              * Check for a badly encoded N-field which points to an instruction
6560d1180bSTaylor Simpson              * out-of-range
6660d1180bSTaylor Simpson              */
6760d1180bSTaylor Simpson             g_assert(!((def_off != 0) || (def_idx < 0) ||
6860d1180bSTaylor Simpson                        (def_idx > (pkt->num_insns - 1))));
6960d1180bSTaylor Simpson 
7060d1180bSTaylor Simpson             /* def_idx is the index of the producer */
7160d1180bSTaylor Simpson             def_opcode = pkt->insn[def_idx].opcode;
7260d1180bSTaylor Simpson             reginfo = opcode_reginfo[def_opcode];
7360d1180bSTaylor Simpson             destletters = "dexy";
7460d1180bSTaylor Simpson             for (j = 0; (letter = destletters[j]) != 0; j++) {
7560d1180bSTaylor Simpson                 dststr = strchr(reginfo, letter);
7660d1180bSTaylor Simpson                 if (dststr != NULL) {
7760d1180bSTaylor Simpson                     break;
7860d1180bSTaylor Simpson                 }
7960d1180bSTaylor Simpson             }
8060d1180bSTaylor Simpson             if ((dststr == NULL)  && GET_ATTRIB(def_opcode, A_CVI_GATHER)) {
8160d1180bSTaylor Simpson                 def_regnum = 0;
8260d1180bSTaylor Simpson                 pkt->insn[i].regno[use_regidx] = def_oreg;
8360d1180bSTaylor Simpson                 pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot;
8460d1180bSTaylor Simpson             } else {
8560d1180bSTaylor Simpson                 if (dststr == NULL) {
8660d1180bSTaylor Simpson                     /* still not there, we have a bad packet */
8760d1180bSTaylor Simpson                     g_assert_not_reached();
8860d1180bSTaylor Simpson                 }
8960d1180bSTaylor Simpson                 def_regnum = pkt->insn[def_idx].regno[dststr - reginfo];
9060d1180bSTaylor Simpson                 /* Now patch up the consumer with the register number */
9160d1180bSTaylor Simpson                 pkt->insn[i].regno[use_regidx] = def_regnum ^ def_oreg;
9260d1180bSTaylor Simpson                 /* special case for (Vx,Vy) */
9360d1180bSTaylor Simpson                 dststr = strchr(reginfo, 'y');
9460d1180bSTaylor Simpson                 if (def_oreg && strchr(reginfo, 'x') && dststr) {
9560d1180bSTaylor Simpson                     def_regnum = pkt->insn[def_idx].regno[dststr - reginfo];
9660d1180bSTaylor Simpson                     pkt->insn[i].regno[use_regidx] = def_regnum;
9760d1180bSTaylor Simpson                 }
9860d1180bSTaylor Simpson                 /*
9960d1180bSTaylor Simpson                  * We need to remember who produces this value to later
10060d1180bSTaylor Simpson                  * check if it was dynamically cancelled
10160d1180bSTaylor Simpson                  */
10260d1180bSTaylor Simpson                 pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot;
10360d1180bSTaylor Simpson             }
10460d1180bSTaylor Simpson         }
10560d1180bSTaylor Simpson     }
10660d1180bSTaylor Simpson }
10760d1180bSTaylor Simpson 
10860d1180bSTaylor Simpson /*
10960d1180bSTaylor Simpson  * We don't want to reorder slot1/slot0 with respect to each other.
11060d1180bSTaylor Simpson  * So in our shuffling, we don't want to move the .cur / .tmp vmem earlier
11160d1180bSTaylor Simpson  * Instead, we should move the producing instruction later
11260d1180bSTaylor Simpson  * But the producing instruction might feed a .new store!
11360d1180bSTaylor Simpson  * So we may need to move that even later.
11460d1180bSTaylor Simpson  */
11560d1180bSTaylor Simpson 
11660d1180bSTaylor Simpson static void
11760d1180bSTaylor Simpson decode_mmvec_move_cvi_to_end(Packet *pkt, int max)
11860d1180bSTaylor Simpson {
11960d1180bSTaylor Simpson     int i;
12060d1180bSTaylor Simpson     for (i = 0; i < max; i++) {
12160d1180bSTaylor Simpson         if (GET_ATTRIB(pkt->insn[i].opcode, A_CVI)) {
12260d1180bSTaylor Simpson             int last_inst = pkt->num_insns - 1;
12360d1180bSTaylor Simpson             uint16_t last_opcode = pkt->insn[last_inst].opcode;
12460d1180bSTaylor Simpson 
12560d1180bSTaylor Simpson             /*
12660d1180bSTaylor Simpson              * If the last instruction is an endloop, move to the one before it
12760d1180bSTaylor Simpson              * Keep endloop as the last thing always
12860d1180bSTaylor Simpson              */
12960d1180bSTaylor Simpson             if ((last_opcode == J2_endloop0) ||
13060d1180bSTaylor Simpson                 (last_opcode == J2_endloop1) ||
13160d1180bSTaylor Simpson                 (last_opcode == J2_endloop01)) {
13260d1180bSTaylor Simpson                 last_inst--;
13360d1180bSTaylor Simpson             }
13460d1180bSTaylor Simpson 
13560d1180bSTaylor Simpson             decode_send_insn_to(pkt, i, last_inst);
13660d1180bSTaylor Simpson             max--;
13760d1180bSTaylor Simpson             i--;    /* Retry this index now that packet has rotated */
13860d1180bSTaylor Simpson         }
13960d1180bSTaylor Simpson     }
14060d1180bSTaylor Simpson }
14160d1180bSTaylor Simpson 
14260d1180bSTaylor Simpson static void
14360d1180bSTaylor Simpson decode_shuffle_for_execution_vops(Packet *pkt)
14460d1180bSTaylor Simpson {
14560d1180bSTaylor Simpson     /*
14660d1180bSTaylor Simpson      * Sort for .new
14760d1180bSTaylor Simpson      */
14860d1180bSTaylor Simpson     int i;
14960d1180bSTaylor Simpson     for (i = 0; i < pkt->num_insns; i++) {
15060d1180bSTaylor Simpson         uint16_t opcode = pkt->insn[i].opcode;
1513fd49e22SMarco Liebel         if ((GET_ATTRIB(opcode, A_LOAD) &&
1523fd49e22SMarco Liebel              GET_ATTRIB(opcode, A_CVI_NEW)) ||
1533fd49e22SMarco Liebel             GET_ATTRIB(opcode, A_CVI_TMP)) {
15460d1180bSTaylor Simpson             /*
15560d1180bSTaylor Simpson              * Find prior consuming vector instructions
15660d1180bSTaylor Simpson              * Move to end of packet
15760d1180bSTaylor Simpson              */
15860d1180bSTaylor Simpson             decode_mmvec_move_cvi_to_end(pkt, i);
15960d1180bSTaylor Simpson             break;
16060d1180bSTaylor Simpson         }
16160d1180bSTaylor Simpson     }
16260d1180bSTaylor Simpson 
16360d1180bSTaylor Simpson     /* Move HVX new value stores to the end of the packet */
16460d1180bSTaylor Simpson     for (i = 0; i < pkt->num_insns - 1; i++) {
16560d1180bSTaylor Simpson         uint16_t opcode = pkt->insn[i].opcode;
16660d1180bSTaylor Simpson         if (GET_ATTRIB(opcode, A_STORE) &&
16760d1180bSTaylor Simpson             GET_ATTRIB(opcode, A_CVI_NEW) &&
16860d1180bSTaylor Simpson             !GET_ATTRIB(opcode, A_CVI_SCATTER_RELEASE)) {
16960d1180bSTaylor Simpson             int last_inst = pkt->num_insns - 1;
17060d1180bSTaylor Simpson             uint16_t last_opcode = pkt->insn[last_inst].opcode;
17160d1180bSTaylor Simpson 
17260d1180bSTaylor Simpson             /*
17360d1180bSTaylor Simpson              * If the last instruction is an endloop, move to the one before it
17460d1180bSTaylor Simpson              * Keep endloop as the last thing always
17560d1180bSTaylor Simpson              */
17660d1180bSTaylor Simpson             if ((last_opcode == J2_endloop0) ||
17760d1180bSTaylor Simpson                 (last_opcode == J2_endloop1) ||
17860d1180bSTaylor Simpson                 (last_opcode == J2_endloop01)) {
17960d1180bSTaylor Simpson                 last_inst--;
18060d1180bSTaylor Simpson             }
18160d1180bSTaylor Simpson 
18260d1180bSTaylor Simpson             decode_send_insn_to(pkt, i, last_inst);
18360d1180bSTaylor Simpson             break;
18460d1180bSTaylor Simpson         }
18560d1180bSTaylor Simpson     }
18660d1180bSTaylor Simpson }
18760d1180bSTaylor Simpson 
18860d1180bSTaylor Simpson static void
18960d1180bSTaylor Simpson check_for_vhist(Packet *pkt)
19060d1180bSTaylor Simpson {
19160d1180bSTaylor Simpson     pkt->vhist_insn = NULL;
19260d1180bSTaylor Simpson     for (int i = 0; i < pkt->num_insns; i++) {
19360d1180bSTaylor Simpson         Insn *insn = &pkt->insn[i];
19460d1180bSTaylor Simpson         int opcode = insn->opcode;
19560d1180bSTaylor Simpson         if (GET_ATTRIB(opcode, A_CVI) && GET_ATTRIB(opcode, A_CVI_4SLOT)) {
19660d1180bSTaylor Simpson                 pkt->vhist_insn = insn;
19760d1180bSTaylor Simpson                 return;
19860d1180bSTaylor Simpson         }
19960d1180bSTaylor Simpson     }
20060d1180bSTaylor Simpson }
20160d1180bSTaylor Simpson 
20260d1180bSTaylor Simpson /*
20360d1180bSTaylor Simpson  * Public Functions
20460d1180bSTaylor Simpson  */
20560d1180bSTaylor Simpson 
20660d1180bSTaylor Simpson SlotMask mmvec_ext_decode_find_iclass_slots(int opcode)
20760d1180bSTaylor Simpson {
20860d1180bSTaylor Simpson     if (GET_ATTRIB(opcode, A_CVI_VM)) {
20960d1180bSTaylor Simpson         /* HVX memory instruction */
21060d1180bSTaylor Simpson         if (GET_ATTRIB(opcode, A_RESTRICT_SLOT0ONLY)) {
21160d1180bSTaylor Simpson             return SLOTS_0;
21260d1180bSTaylor Simpson         } else if (GET_ATTRIB(opcode, A_RESTRICT_SLOT1ONLY)) {
21360d1180bSTaylor Simpson             return SLOTS_1;
21460d1180bSTaylor Simpson         }
21560d1180bSTaylor Simpson         return SLOTS_01;
21660d1180bSTaylor Simpson     } else if (GET_ATTRIB(opcode, A_RESTRICT_SLOT2ONLY)) {
21760d1180bSTaylor Simpson         return SLOTS_2;
21860d1180bSTaylor Simpson     } else if (GET_ATTRIB(opcode, A_CVI_VX)) {
21960d1180bSTaylor Simpson         /* HVX multiply instruction */
22060d1180bSTaylor Simpson         return SLOTS_23;
22160d1180bSTaylor Simpson     } else if (GET_ATTRIB(opcode, A_CVI_VS_VX)) {
22260d1180bSTaylor Simpson         /* HVX permute/shift instruction */
22360d1180bSTaylor Simpson         return SLOTS_23;
22460d1180bSTaylor Simpson     } else {
22560d1180bSTaylor Simpson         return SLOTS_0123;
22660d1180bSTaylor Simpson     }
22760d1180bSTaylor Simpson }
22860d1180bSTaylor Simpson 
22960d1180bSTaylor Simpson void mmvec_ext_decode_checks(Packet *pkt, bool disas_only)
23060d1180bSTaylor Simpson {
23160d1180bSTaylor Simpson     check_new_value(pkt);
23260d1180bSTaylor Simpson     if (!disas_only) {
23360d1180bSTaylor Simpson         decode_shuffle_for_execution_vops(pkt);
23460d1180bSTaylor Simpson     }
23560d1180bSTaylor Simpson     check_for_vhist(pkt);
23660d1180bSTaylor Simpson }
237