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
27 check_new_value(Packet *pkt)
28 {
29     /* .new value for a MMVector store */
30     int i, j;
31     const char *reginfo;
32     const char *destletters;
33     const char *dststr = NULL;
34     uint16_t def_opcode;
35     char letter;
36     int def_regnum;
37 
38     for (i = 1; i < pkt->num_insns; i++) {
39         uint16_t use_opcode = pkt->insn[i].opcode;
40         if (GET_ATTRIB(use_opcode, A_DOTNEWVALUE) &&
41             GET_ATTRIB(use_opcode, A_CVI) &&
42             GET_ATTRIB(use_opcode, A_STORE)) {
43             int use_regidx = strchr(opcode_reginfo[use_opcode], 's') -
44                 opcode_reginfo[use_opcode];
45             /*
46              * What's encoded at the N-field is the offset to who's producing
47              * the value.
48              * Shift off the LSB which indicates odd/even register.
49              */
50             int def_off = ((pkt->insn[i].regno[use_regidx]) >> 1);
51             int def_oreg = pkt->insn[i].regno[use_regidx] & 1;
52             int def_idx = -1;
53             for (j = i - 1; (j >= 0) && (def_off >= 0); j--) {
54                 if (!GET_ATTRIB(pkt->insn[j].opcode, A_CVI)) {
55                     continue;
56                 }
57                 def_off--;
58                 if (def_off == 0) {
59                     def_idx = j;
60                     break;
61                 }
62             }
63             /*
64              * Check for a badly encoded N-field which points to an instruction
65              * out-of-range
66              */
67             g_assert(!((def_off != 0) || (def_idx < 0) ||
68                        (def_idx > (pkt->num_insns - 1))));
69 
70             /* def_idx is the index of the producer */
71             def_opcode = pkt->insn[def_idx].opcode;
72             reginfo = opcode_reginfo[def_opcode];
73             destletters = "dexy";
74             for (j = 0; (letter = destletters[j]) != 0; j++) {
75                 dststr = strchr(reginfo, letter);
76                 if (dststr != NULL) {
77                     break;
78                 }
79             }
80             if ((dststr == NULL)  && GET_ATTRIB(def_opcode, A_CVI_GATHER)) {
81                 def_regnum = 0;
82                 pkt->insn[i].regno[use_regidx] = def_oreg;
83                 pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot;
84             } else {
85                 if (dststr == NULL) {
86                     /* still not there, we have a bad packet */
87                     g_assert_not_reached();
88                 }
89                 def_regnum = pkt->insn[def_idx].regno[dststr - reginfo];
90                 /* Now patch up the consumer with the register number */
91                 pkt->insn[i].regno[use_regidx] = def_regnum ^ def_oreg;
92                 /* special case for (Vx,Vy) */
93                 dststr = strchr(reginfo, 'y');
94                 if (def_oreg && strchr(reginfo, 'x') && dststr) {
95                     def_regnum = pkt->insn[def_idx].regno[dststr - reginfo];
96                     pkt->insn[i].regno[use_regidx] = def_regnum;
97                 }
98                 /*
99                  * We need to remember who produces this value to later
100                  * check if it was dynamically cancelled
101                  */
102                 pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot;
103             }
104         }
105     }
106 }
107 
108 /*
109  * We don't want to reorder slot1/slot0 with respect to each other.
110  * So in our shuffling, we don't want to move the .cur / .tmp vmem earlier
111  * Instead, we should move the producing instruction later
112  * But the producing instruction might feed a .new store!
113  * So we may need to move that even later.
114  */
115 
116 static void
117 decode_mmvec_move_cvi_to_end(Packet *pkt, int max)
118 {
119     int i;
120     for (i = 0; i < max; i++) {
121         if (GET_ATTRIB(pkt->insn[i].opcode, A_CVI)) {
122             int last_inst = pkt->num_insns - 1;
123             uint16_t last_opcode = pkt->insn[last_inst].opcode;
124 
125             /*
126              * If the last instruction is an endloop, move to the one before it
127              * Keep endloop as the last thing always
128              */
129             if ((last_opcode == J2_endloop0) ||
130                 (last_opcode == J2_endloop1) ||
131                 (last_opcode == J2_endloop01)) {
132                 last_inst--;
133             }
134 
135             decode_send_insn_to(pkt, i, last_inst);
136             max--;
137             i--;    /* Retry this index now that packet has rotated */
138         }
139     }
140 }
141 
142 static void
143 decode_shuffle_for_execution_vops(Packet *pkt)
144 {
145     /*
146      * Sort for .new
147      */
148     int i;
149     for (i = 0; i < pkt->num_insns; i++) {
150         uint16_t opcode = pkt->insn[i].opcode;
151         if ((GET_ATTRIB(opcode, A_LOAD) &&
152              GET_ATTRIB(opcode, A_CVI_NEW)) ||
153             GET_ATTRIB(opcode, A_CVI_TMP)) {
154             /*
155              * Find prior consuming vector instructions
156              * Move to end of packet
157              */
158             decode_mmvec_move_cvi_to_end(pkt, i);
159             break;
160         }
161     }
162 
163     /* Move HVX new value stores to the end of the packet */
164     for (i = 0; i < pkt->num_insns - 1; i++) {
165         uint16_t opcode = pkt->insn[i].opcode;
166         if (GET_ATTRIB(opcode, A_STORE) &&
167             GET_ATTRIB(opcode, A_CVI_NEW) &&
168             !GET_ATTRIB(opcode, A_CVI_SCATTER_RELEASE)) {
169             int last_inst = pkt->num_insns - 1;
170             uint16_t last_opcode = pkt->insn[last_inst].opcode;
171 
172             /*
173              * If the last instruction is an endloop, move to the one before it
174              * Keep endloop as the last thing always
175              */
176             if ((last_opcode == J2_endloop0) ||
177                 (last_opcode == J2_endloop1) ||
178                 (last_opcode == J2_endloop01)) {
179                 last_inst--;
180             }
181 
182             decode_send_insn_to(pkt, i, last_inst);
183             break;
184         }
185     }
186 }
187 
188 static void
189 check_for_vhist(Packet *pkt)
190 {
191     pkt->vhist_insn = NULL;
192     for (int i = 0; i < pkt->num_insns; i++) {
193         Insn *insn = &pkt->insn[i];
194         int opcode = insn->opcode;
195         if (GET_ATTRIB(opcode, A_CVI) && GET_ATTRIB(opcode, A_CVI_4SLOT)) {
196                 pkt->vhist_insn = insn;
197                 return;
198         }
199     }
200 }
201 
202 /*
203  * Public Functions
204  */
205 
206 SlotMask mmvec_ext_decode_find_iclass_slots(int opcode)
207 {
208     if (GET_ATTRIB(opcode, A_CVI_VM)) {
209         /* HVX memory instruction */
210         if (GET_ATTRIB(opcode, A_RESTRICT_SLOT0ONLY)) {
211             return SLOTS_0;
212         } else if (GET_ATTRIB(opcode, A_RESTRICT_SLOT1ONLY)) {
213             return SLOTS_1;
214         }
215         return SLOTS_01;
216     } else if (GET_ATTRIB(opcode, A_RESTRICT_SLOT2ONLY)) {
217         return SLOTS_2;
218     } else if (GET_ATTRIB(opcode, A_CVI_VX)) {
219         /* HVX multiply instruction */
220         return SLOTS_23;
221     } else if (GET_ATTRIB(opcode, A_CVI_VS_VX)) {
222         /* HVX permute/shift instruction */
223         return SLOTS_23;
224     } else {
225         return SLOTS_0123;
226     }
227 }
228 
229 void mmvec_ext_decode_checks(Packet *pkt, bool disas_only)
230 {
231     check_new_value(pkt);
232     if (!disas_only) {
233         decode_shuffle_for_execution_vops(pkt);
234     }
235     check_for_vhist(pkt);
236 }
237