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
check_new_value(Packet * pkt)2760d1180bSTaylor Simpson check_new_value(Packet *pkt)
2860d1180bSTaylor Simpson {
2960d1180bSTaylor Simpson     /* .new value for a MMVector store */
3060d1180bSTaylor Simpson     int i, j;
3160d1180bSTaylor Simpson     uint16_t def_opcode;
3260d1180bSTaylor Simpson 
3360d1180bSTaylor Simpson     for (i = 1; i < pkt->num_insns; i++) {
3460d1180bSTaylor Simpson         uint16_t use_opcode = pkt->insn[i].opcode;
3560d1180bSTaylor Simpson         if (GET_ATTRIB(use_opcode, A_DOTNEWVALUE) &&
3660d1180bSTaylor Simpson             GET_ATTRIB(use_opcode, A_CVI) &&
3760d1180bSTaylor Simpson             GET_ATTRIB(use_opcode, A_STORE)) {
3809a7e7dbSTaylor Simpson             int use_regidx = pkt->insn[i].new_read_idx;
3909a7e7dbSTaylor Simpson             g_assert(pkt->insn[i].new_read_idx != -1);
4060d1180bSTaylor Simpson             /*
4160d1180bSTaylor Simpson              * What's encoded at the N-field is the offset to who's producing
4260d1180bSTaylor Simpson              * the value.
4360d1180bSTaylor Simpson              * Shift off the LSB which indicates odd/even register.
4460d1180bSTaylor Simpson              */
4560d1180bSTaylor Simpson             int def_off = ((pkt->insn[i].regno[use_regidx]) >> 1);
4660d1180bSTaylor Simpson             int def_oreg = pkt->insn[i].regno[use_regidx] & 1;
4760d1180bSTaylor Simpson             int def_idx = -1;
4860d1180bSTaylor Simpson             for (j = i - 1; (j >= 0) && (def_off >= 0); j--) {
4960d1180bSTaylor Simpson                 if (!GET_ATTRIB(pkt->insn[j].opcode, A_CVI)) {
5060d1180bSTaylor Simpson                     continue;
5160d1180bSTaylor Simpson                 }
5260d1180bSTaylor Simpson                 def_off--;
5360d1180bSTaylor Simpson                 if (def_off == 0) {
5460d1180bSTaylor Simpson                     def_idx = j;
5560d1180bSTaylor Simpson                     break;
5660d1180bSTaylor Simpson                 }
5760d1180bSTaylor Simpson             }
5860d1180bSTaylor Simpson             /*
5960d1180bSTaylor Simpson              * Check for a badly encoded N-field which points to an instruction
6060d1180bSTaylor Simpson              * out-of-range
6160d1180bSTaylor Simpson              */
6260d1180bSTaylor Simpson             g_assert(!((def_off != 0) || (def_idx < 0) ||
6360d1180bSTaylor Simpson                        (def_idx > (pkt->num_insns - 1))));
6460d1180bSTaylor Simpson 
6560d1180bSTaylor Simpson             /* def_idx is the index of the producer */
6660d1180bSTaylor Simpson             def_opcode = pkt->insn[def_idx].opcode;
6709a7e7dbSTaylor Simpson             if ((pkt->insn[def_idx].dest_idx == -1)  &&
6809a7e7dbSTaylor Simpson                 GET_ATTRIB(def_opcode, A_CVI_GATHER)) {
6960d1180bSTaylor Simpson                 pkt->insn[i].regno[use_regidx] = def_oreg;
7060d1180bSTaylor Simpson                 pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot;
7160d1180bSTaylor Simpson             } else {
7209a7e7dbSTaylor Simpson                 if (pkt->insn[def_idx].dest_idx == -1) {
7360d1180bSTaylor Simpson                     /* still not there, we have a bad packet */
7460d1180bSTaylor Simpson                     g_assert_not_reached();
7560d1180bSTaylor Simpson                 }
7609a7e7dbSTaylor Simpson                 int def_regnum =
7709a7e7dbSTaylor Simpson                     pkt->insn[def_idx].regno[pkt->insn[def_idx].dest_idx];
7860d1180bSTaylor Simpson                 /* Now patch up the consumer with the register number */
7960d1180bSTaylor Simpson                 pkt->insn[i].regno[use_regidx] = def_regnum ^ def_oreg;
8060d1180bSTaylor Simpson                 /*
8160d1180bSTaylor Simpson                  * We need to remember who produces this value to later
8260d1180bSTaylor Simpson                  * check if it was dynamically cancelled
8360d1180bSTaylor Simpson                  */
8460d1180bSTaylor Simpson                 pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot;
8560d1180bSTaylor Simpson             }
8660d1180bSTaylor Simpson         }
8760d1180bSTaylor Simpson     }
8860d1180bSTaylor Simpson }
8960d1180bSTaylor Simpson 
9060d1180bSTaylor Simpson /*
9160d1180bSTaylor Simpson  * We don't want to reorder slot1/slot0 with respect to each other.
9260d1180bSTaylor Simpson  * So in our shuffling, we don't want to move the .cur / .tmp vmem earlier
9360d1180bSTaylor Simpson  * Instead, we should move the producing instruction later
9460d1180bSTaylor Simpson  * But the producing instruction might feed a .new store!
9560d1180bSTaylor Simpson  * So we may need to move that even later.
9660d1180bSTaylor Simpson  */
9760d1180bSTaylor Simpson 
9860d1180bSTaylor Simpson static void
decode_mmvec_move_cvi_to_end(Packet * pkt,int max)9960d1180bSTaylor Simpson decode_mmvec_move_cvi_to_end(Packet *pkt, int max)
10060d1180bSTaylor Simpson {
10160d1180bSTaylor Simpson     int i;
10260d1180bSTaylor Simpson     for (i = 0; i < max; i++) {
10360d1180bSTaylor Simpson         if (GET_ATTRIB(pkt->insn[i].opcode, A_CVI)) {
10460d1180bSTaylor Simpson             int last_inst = pkt->num_insns - 1;
10560d1180bSTaylor Simpson             uint16_t last_opcode = pkt->insn[last_inst].opcode;
10660d1180bSTaylor Simpson 
10760d1180bSTaylor Simpson             /*
10860d1180bSTaylor Simpson              * If the last instruction is an endloop, move to the one before it
10960d1180bSTaylor Simpson              * Keep endloop as the last thing always
11060d1180bSTaylor Simpson              */
11160d1180bSTaylor Simpson             if ((last_opcode == J2_endloop0) ||
11260d1180bSTaylor Simpson                 (last_opcode == J2_endloop1) ||
11360d1180bSTaylor Simpson                 (last_opcode == J2_endloop01)) {
11460d1180bSTaylor Simpson                 last_inst--;
11560d1180bSTaylor Simpson             }
11660d1180bSTaylor Simpson 
11760d1180bSTaylor Simpson             decode_send_insn_to(pkt, i, last_inst);
11860d1180bSTaylor Simpson             max--;
11960d1180bSTaylor Simpson             i--;    /* Retry this index now that packet has rotated */
12060d1180bSTaylor Simpson         }
12160d1180bSTaylor Simpson     }
12260d1180bSTaylor Simpson }
12360d1180bSTaylor Simpson 
12460d1180bSTaylor Simpson static void
decode_shuffle_for_execution_vops(Packet * pkt)12560d1180bSTaylor Simpson decode_shuffle_for_execution_vops(Packet *pkt)
12660d1180bSTaylor Simpson {
12760d1180bSTaylor Simpson     /*
12860d1180bSTaylor Simpson      * Sort for .new
12960d1180bSTaylor Simpson      */
13060d1180bSTaylor Simpson     int i;
13160d1180bSTaylor Simpson     for (i = 0; i < pkt->num_insns; i++) {
13260d1180bSTaylor Simpson         uint16_t opcode = pkt->insn[i].opcode;
1333fd49e22SMarco Liebel         if ((GET_ATTRIB(opcode, A_LOAD) &&
1343fd49e22SMarco Liebel              GET_ATTRIB(opcode, A_CVI_NEW)) ||
1353fd49e22SMarco Liebel             GET_ATTRIB(opcode, A_CVI_TMP)) {
13660d1180bSTaylor Simpson             /*
13760d1180bSTaylor Simpson              * Find prior consuming vector instructions
13860d1180bSTaylor Simpson              * Move to end of packet
13960d1180bSTaylor Simpson              */
14060d1180bSTaylor Simpson             decode_mmvec_move_cvi_to_end(pkt, i);
14160d1180bSTaylor Simpson             break;
14260d1180bSTaylor Simpson         }
14360d1180bSTaylor Simpson     }
14460d1180bSTaylor Simpson 
14560d1180bSTaylor Simpson     /* Move HVX new value stores to the end of the packet */
14660d1180bSTaylor Simpson     for (i = 0; i < pkt->num_insns - 1; i++) {
14760d1180bSTaylor Simpson         uint16_t opcode = pkt->insn[i].opcode;
14860d1180bSTaylor Simpson         if (GET_ATTRIB(opcode, A_STORE) &&
14960d1180bSTaylor Simpson             GET_ATTRIB(opcode, A_CVI_NEW) &&
15060d1180bSTaylor Simpson             !GET_ATTRIB(opcode, A_CVI_SCATTER_RELEASE)) {
15160d1180bSTaylor Simpson             int last_inst = pkt->num_insns - 1;
15260d1180bSTaylor Simpson             uint16_t last_opcode = pkt->insn[last_inst].opcode;
15360d1180bSTaylor Simpson 
15460d1180bSTaylor Simpson             /*
15560d1180bSTaylor Simpson              * If the last instruction is an endloop, move to the one before it
15660d1180bSTaylor Simpson              * Keep endloop as the last thing always
15760d1180bSTaylor Simpson              */
15860d1180bSTaylor Simpson             if ((last_opcode == J2_endloop0) ||
15960d1180bSTaylor Simpson                 (last_opcode == J2_endloop1) ||
16060d1180bSTaylor Simpson                 (last_opcode == J2_endloop01)) {
16160d1180bSTaylor Simpson                 last_inst--;
16260d1180bSTaylor Simpson             }
16360d1180bSTaylor Simpson 
16460d1180bSTaylor Simpson             decode_send_insn_to(pkt, i, last_inst);
16560d1180bSTaylor Simpson             break;
16660d1180bSTaylor Simpson         }
16760d1180bSTaylor Simpson     }
16860d1180bSTaylor Simpson }
16960d1180bSTaylor Simpson 
17060d1180bSTaylor Simpson static void
check_for_vhist(Packet * pkt)17160d1180bSTaylor Simpson check_for_vhist(Packet *pkt)
17260d1180bSTaylor Simpson {
17360d1180bSTaylor Simpson     pkt->vhist_insn = NULL;
17460d1180bSTaylor Simpson     for (int i = 0; i < pkt->num_insns; i++) {
17560d1180bSTaylor Simpson         Insn *insn = &pkt->insn[i];
17660d1180bSTaylor Simpson         int opcode = insn->opcode;
17760d1180bSTaylor Simpson         if (GET_ATTRIB(opcode, A_CVI) && GET_ATTRIB(opcode, A_CVI_4SLOT)) {
17860d1180bSTaylor Simpson                 pkt->vhist_insn = insn;
17960d1180bSTaylor Simpson                 return;
18060d1180bSTaylor Simpson         }
18160d1180bSTaylor Simpson     }
18260d1180bSTaylor Simpson }
18360d1180bSTaylor Simpson 
18460d1180bSTaylor Simpson /*
18560d1180bSTaylor Simpson  * Public Functions
18660d1180bSTaylor Simpson  */
18760d1180bSTaylor Simpson 
mmvec_ext_decode_find_iclass_slots(int opcode)18860d1180bSTaylor Simpson SlotMask mmvec_ext_decode_find_iclass_slots(int opcode)
18960d1180bSTaylor Simpson {
19060d1180bSTaylor Simpson     if (GET_ATTRIB(opcode, A_CVI_VM)) {
19160d1180bSTaylor Simpson         /* HVX memory instruction */
19260d1180bSTaylor Simpson         if (GET_ATTRIB(opcode, A_RESTRICT_SLOT0ONLY)) {
19360d1180bSTaylor Simpson             return SLOTS_0;
19460d1180bSTaylor Simpson         } else if (GET_ATTRIB(opcode, A_RESTRICT_SLOT1ONLY)) {
19560d1180bSTaylor Simpson             return SLOTS_1;
19660d1180bSTaylor Simpson         }
19760d1180bSTaylor Simpson         return SLOTS_01;
19860d1180bSTaylor Simpson     } else if (GET_ATTRIB(opcode, A_RESTRICT_SLOT2ONLY)) {
19960d1180bSTaylor Simpson         return SLOTS_2;
20060d1180bSTaylor Simpson     } else if (GET_ATTRIB(opcode, A_CVI_VX)) {
20160d1180bSTaylor Simpson         /* HVX multiply instruction */
20260d1180bSTaylor Simpson         return SLOTS_23;
20360d1180bSTaylor Simpson     } else if (GET_ATTRIB(opcode, A_CVI_VS_VX)) {
20460d1180bSTaylor Simpson         /* HVX permute/shift instruction */
20560d1180bSTaylor Simpson         return SLOTS_23;
20660d1180bSTaylor Simpson     } else {
20760d1180bSTaylor Simpson         return SLOTS_0123;
20860d1180bSTaylor Simpson     }
20960d1180bSTaylor Simpson }
21060d1180bSTaylor Simpson 
mmvec_ext_decode_checks(Packet * pkt,bool disas_only)21160d1180bSTaylor Simpson void mmvec_ext_decode_checks(Packet *pkt, bool disas_only)
21260d1180bSTaylor Simpson {
21360d1180bSTaylor Simpson     check_new_value(pkt);
21460d1180bSTaylor Simpson     if (!disas_only) {
21560d1180bSTaylor Simpson         decode_shuffle_for_execution_vops(pkt);
21660d1180bSTaylor Simpson     }
21760d1180bSTaylor Simpson     check_for_vhist(pkt);
21860d1180bSTaylor Simpson }
219