1 /*
2  *  Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "qemu/osdep.h"
19 #include "decode.h"
20 #include "opcodes.h"
21 #include "insn.h"
22 #include "iclass.h"
23 #include "mmvec/mmvec.h"
24 #include "mmvec/decode_ext_mmvec.h"
25 
26 static void
check_new_value(Packet * pkt)27 check_new_value(Packet *pkt)
28 {
29     /* .new value for a MMVector store */
30     int i, j;
31     uint16_t def_opcode;
32 
33     for (i = 1; i < pkt->num_insns; i++) {
34         uint16_t use_opcode = pkt->insn[i].opcode;
35         if (GET_ATTRIB(use_opcode, A_DOTNEWVALUE) &&
36             GET_ATTRIB(use_opcode, A_CVI) &&
37             GET_ATTRIB(use_opcode, A_STORE)) {
38             int use_regidx = pkt->insn[i].new_read_idx;
39             g_assert(pkt->insn[i].new_read_idx != -1);
40             /*
41              * What's encoded at the N-field is the offset to who's producing
42              * the value.
43              * Shift off the LSB which indicates odd/even register.
44              */
45             int def_off = ((pkt->insn[i].regno[use_regidx]) >> 1);
46             int def_oreg = pkt->insn[i].regno[use_regidx] & 1;
47             int def_idx = -1;
48             for (j = i - 1; (j >= 0) && (def_off >= 0); j--) {
49                 if (!GET_ATTRIB(pkt->insn[j].opcode, A_CVI)) {
50                     continue;
51                 }
52                 def_off--;
53                 if (def_off == 0) {
54                     def_idx = j;
55                     break;
56                 }
57             }
58             /*
59              * Check for a badly encoded N-field which points to an instruction
60              * out-of-range
61              */
62             g_assert(!((def_off != 0) || (def_idx < 0) ||
63                        (def_idx > (pkt->num_insns - 1))));
64 
65             /* def_idx is the index of the producer */
66             def_opcode = pkt->insn[def_idx].opcode;
67             if ((pkt->insn[def_idx].dest_idx == -1)  &&
68                 GET_ATTRIB(def_opcode, A_CVI_GATHER)) {
69                 pkt->insn[i].regno[use_regidx] = def_oreg;
70                 pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot;
71             } else {
72                 if (pkt->insn[def_idx].dest_idx == -1) {
73                     /* still not there, we have a bad packet */
74                     g_assert_not_reached();
75                 }
76                 int def_regnum =
77                     pkt->insn[def_idx].regno[pkt->insn[def_idx].dest_idx];
78                 /* Now patch up the consumer with the register number */
79                 pkt->insn[i].regno[use_regidx] = def_regnum ^ def_oreg;
80                 /*
81                  * We need to remember who produces this value to later
82                  * check if it was dynamically cancelled
83                  */
84                 pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot;
85             }
86         }
87     }
88 }
89 
90 /*
91  * We don't want to reorder slot1/slot0 with respect to each other.
92  * So in our shuffling, we don't want to move the .cur / .tmp vmem earlier
93  * Instead, we should move the producing instruction later
94  * But the producing instruction might feed a .new store!
95  * So we may need to move that even later.
96  */
97 
98 static void
decode_mmvec_move_cvi_to_end(Packet * pkt,int max)99 decode_mmvec_move_cvi_to_end(Packet *pkt, int max)
100 {
101     int i;
102     for (i = 0; i < max; i++) {
103         if (GET_ATTRIB(pkt->insn[i].opcode, A_CVI)) {
104             int last_inst = pkt->num_insns - 1;
105             uint16_t last_opcode = pkt->insn[last_inst].opcode;
106 
107             /*
108              * If the last instruction is an endloop, move to the one before it
109              * Keep endloop as the last thing always
110              */
111             if ((last_opcode == J2_endloop0) ||
112                 (last_opcode == J2_endloop1) ||
113                 (last_opcode == J2_endloop01)) {
114                 last_inst--;
115             }
116 
117             decode_send_insn_to(pkt, i, last_inst);
118             max--;
119             i--;    /* Retry this index now that packet has rotated */
120         }
121     }
122 }
123 
124 static void
decode_shuffle_for_execution_vops(Packet * pkt)125 decode_shuffle_for_execution_vops(Packet *pkt)
126 {
127     /*
128      * Sort for .new
129      */
130     int i;
131     for (i = 0; i < pkt->num_insns; i++) {
132         uint16_t opcode = pkt->insn[i].opcode;
133         if ((GET_ATTRIB(opcode, A_LOAD) &&
134              GET_ATTRIB(opcode, A_CVI_NEW)) ||
135             GET_ATTRIB(opcode, A_CVI_TMP)) {
136             /*
137              * Find prior consuming vector instructions
138              * Move to end of packet
139              */
140             decode_mmvec_move_cvi_to_end(pkt, i);
141             break;
142         }
143     }
144 
145     /* Move HVX new value stores to the end of the packet */
146     for (i = 0; i < pkt->num_insns - 1; i++) {
147         uint16_t opcode = pkt->insn[i].opcode;
148         if (GET_ATTRIB(opcode, A_STORE) &&
149             GET_ATTRIB(opcode, A_CVI_NEW) &&
150             !GET_ATTRIB(opcode, A_CVI_SCATTER_RELEASE)) {
151             int last_inst = pkt->num_insns - 1;
152             uint16_t last_opcode = pkt->insn[last_inst].opcode;
153 
154             /*
155              * If the last instruction is an endloop, move to the one before it
156              * Keep endloop as the last thing always
157              */
158             if ((last_opcode == J2_endloop0) ||
159                 (last_opcode == J2_endloop1) ||
160                 (last_opcode == J2_endloop01)) {
161                 last_inst--;
162             }
163 
164             decode_send_insn_to(pkt, i, last_inst);
165             break;
166         }
167     }
168 }
169 
170 static void
check_for_vhist(Packet * pkt)171 check_for_vhist(Packet *pkt)
172 {
173     pkt->vhist_insn = NULL;
174     for (int i = 0; i < pkt->num_insns; i++) {
175         Insn *insn = &pkt->insn[i];
176         int opcode = insn->opcode;
177         if (GET_ATTRIB(opcode, A_CVI) && GET_ATTRIB(opcode, A_CVI_4SLOT)) {
178                 pkt->vhist_insn = insn;
179                 return;
180         }
181     }
182 }
183 
184 /*
185  * Public Functions
186  */
187 
mmvec_ext_decode_find_iclass_slots(int opcode)188 SlotMask mmvec_ext_decode_find_iclass_slots(int opcode)
189 {
190     if (GET_ATTRIB(opcode, A_CVI_VM)) {
191         /* HVX memory instruction */
192         if (GET_ATTRIB(opcode, A_RESTRICT_SLOT0ONLY)) {
193             return SLOTS_0;
194         } else if (GET_ATTRIB(opcode, A_RESTRICT_SLOT1ONLY)) {
195             return SLOTS_1;
196         }
197         return SLOTS_01;
198     } else if (GET_ATTRIB(opcode, A_RESTRICT_SLOT2ONLY)) {
199         return SLOTS_2;
200     } else if (GET_ATTRIB(opcode, A_CVI_VX)) {
201         /* HVX multiply instruction */
202         return SLOTS_23;
203     } else if (GET_ATTRIB(opcode, A_CVI_VS_VX)) {
204         /* HVX permute/shift instruction */
205         return SLOTS_23;
206     } else {
207         return SLOTS_0123;
208     }
209 }
210 
mmvec_ext_decode_checks(Packet * pkt,bool disas_only)211 void mmvec_ext_decode_checks(Packet *pkt, bool disas_only)
212 {
213     check_new_value(pkt);
214     if (!disas_only) {
215         decode_shuffle_for_execution_vops(pkt);
216     }
217     check_for_vhist(pkt);
218 }
219