add svp64 fptrans tests
[openpower-isa.git] / src / openpower / sv / trans / svp64.py
1 # SPDX-License-Identifier: LGPLv3+
2 # Copyright (C) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3 # Funded by NLnet http://nlnet.nl
4
5 """SVP64 OpenPOWER v3.0B assembly translator
6
7 This class takes raw svp64 assembly mnemonics (aliases excluded) and creates
8 an EXT001-encoded "svp64 prefix" (as a .long) followed by a v3.0B opcode.
9
10 It is very simple and straightforward, the only weirdness being the
11 extraction of the register information and conversion to v3.0B numbering.
12
13 Encoding format of svp64: https://libre-soc.org/openpower/sv/svp64/
14 Encoding format of arithmetic: https://libre-soc.org/openpower/sv/normal/
15 Encoding format of LDST: https://libre-soc.org/openpower/sv/ldst/
16 **TODO format of branches: https://libre-soc.org/openpower/sv/branches/**
17 **TODO format of CRs: https://libre-soc.org/openpower/sv/cr_ops/**
18 Bugtracker: https://bugs.libre-soc.org/show_bug.cgi?id=578
19 """
20
21 import functools
22 import os
23 import sys
24 from collections import OrderedDict
25 import inspect
26
27 from openpower.decoder.pseudo.pagereader import ISA
28 from openpower.decoder.power_svp64 import SVP64RM, get_regtype, decode_extra
29 from openpower.decoder.selectable_int import SelectableInt
30 from openpower.consts import SVP64MODE
31 from openpower.decoder.power_insn import SVP64Instruction
32 from openpower.decoder.power_insn import Database
33 from openpower.decoder.power_enums import find_wiki_dir
34
35 # for debug logging
36 from openpower.util import log
37
38
39 def instruction(*fields):
40 def instruction(insn, desc):
41 (value, start, end) = desc
42 bits = ((1,) * ((end + 1) - start))
43 mask = 0
44 for bit in bits:
45 mask = ((mask << 1) | bit)
46 return (insn | ((value & mask) << (31 - end)))
47
48 return functools.reduce(instruction, fields, 0)
49
50
51 CUSTOM_INSNS = {}
52
53
54 def _insn(name, **kwargs):
55 return name, kwargs
56
57
58 def _custom_insns(*insns):
59 """ a decorator that adds the function to `CUSTOM_INSNS` """
60
61 def decorator(fn):
62 FIELDS_ARG = object()
63 if len(insns) == 0:
64 insns_ = (fn.__name__, {}),
65 else:
66 insns_ = insns
67 for name, kwargs in insns_:
68 if not isinstance(name, str):
69 raise TypeError("instruction name must be a str: {name!r}")
70 if name in CUSTOM_INSNS:
71 raise ValueError(f"duplicate instruction mnemonic: {name!r}")
72 # use getcallargs to check that arguments work:
73 inspect.getcallargs(fn, FIELDS_ARG, **kwargs)
74 CUSTOM_INSNS[name] = functools.partial(fn, **kwargs)
75 return fn
76 return decorator
77
78
79 @_custom_insns(
80 _insn("setvl", Rc=0),
81 _insn("setvl.", Rc=1),
82 )
83 def setvl(fields, Rc):
84 """
85 setvl is a *32-bit-only* instruction. It controls SVSTATE.
86 It is *not* a 64-bit-prefixed Vector instruction (no sv.setvl, yet),
87 it is a Vector *control* instruction.
88
89 * setvl RT,RA,SVi,vf,vs,ms
90
91 1.6.28 SVL-FORM - from fields.txt
92 |0 |6 |11 |16 |23 |24 |25 |26 |31 |
93 | PO | RT | RA | SVi |ms |vs |vf | XO |Rc |
94 """
95 PO = 22
96 XO = 0b11011
97 # ARRRGH these are in a non-obvious order in openpower/isa/simplev.mdwn
98 # compared to the SVL-Form above. sigh
99 # setvl RT,RA,SVi,vf,vs,ms
100 (RT, RA, SVi, vf, vs, ms) = fields
101 SVi -= 1
102 return instruction(
103 (PO, 0, 5),
104 (RT, 6, 10),
105 (RA, 11, 15),
106 (SVi, 16, 22),
107 (ms, 23, 23),
108 (vs, 24, 24),
109 (vf, 25, 25),
110 (XO, 26, 30),
111 (Rc, 31, 31),
112 )
113
114
115 @_custom_insns(
116 _insn("svstep", Rc=0),
117 _insn("svstep.", Rc=1),
118 )
119 def svstep(fields, Rc):
120 """
121 svstep is a 32-bit instruction. It updates SVSTATE.
122 It *can* be SVP64-prefixed, to indicate that its registers
123 are Vectorised.
124
125 * svstep RT,SVi,vf
126
127 # 1.6.28 SVL-FORM - from fields.txt
128 # |0 |6 |11 |16 |23 |24 |25 |26 |31 |
129 # | PO | RT | / | SVi |/ |/ |vf | XO |Rc |
130
131 """
132 PO = 22
133 XO = 0b10011
134 (RT, SVi, vf) = fields
135 SVi -= 1
136 return instruction(
137 (PO, 0, 5),
138 (RT, 6, 10),
139 (0, 11, 15),
140 (SVi, 16, 22),
141 (0, 23, 23),
142 (0, 24, 24),
143 (vf, 25, 25),
144 (XO, 26, 30),
145 (Rc, 31, 31),
146 )
147
148
149 @_custom_insns()
150 def svshape(fields):
151 """
152 svshape is a *32-bit-only* instruction. It updates SVSHAPE and SVSTATE.
153 It is *not* a 64-bit-prefixed Vector instruction (no sv.svshape, yet),
154 it is a Vector *control* instruction.
155
156 https://libre-soc.org/openpower/sv/remap/#svshape
157
158 * svshape SVxd,SVyd,SVzd,SVrm,vf
159
160 # 1.6.33 SVM-FORM from fields.txt
161 # |0 |6 |11 |16 |21 |25 |26 |31 |
162 # | PO | SVxd | SVyd | SVzd | SVrm |vf | XO |
163
164 note that SVrm is not permitted to be 0b0111, 0b1000 or 0b1001.
165 0b0111 is reserved and 0b100- is for svshape2
166
167 """
168 PO = 22
169 XO = 0b011001
170 (SVxd, SVyd, SVzd, SVrm, vf) = fields
171 SVxd -= 1
172 SVyd -= 1
173 SVzd -= 1
174
175 # check SVrm for reserved (and svshape2) values
176 assert SVrm not in [0b1000, 0b1001], \
177 "svshape reserved SVrm value %s" % bin(SVrm)
178
179 return instruction(
180 (PO, 0, 5),
181 (SVxd, 6, 10),
182 (SVyd, 11, 15),
183 (SVzd, 16, 20),
184 (SVrm, 21, 24),
185 (vf, 25, 25),
186 (XO, 26, 31),
187 )
188
189
190 @_custom_insns()
191 def svshape2(fields):
192 """
193 svshape2 is a *32-bit-only* instruction. It updates SVSHAPE and SVSTATE.
194 It is *not* a 64-bit-prefixed Vector instruction (no sv.svshape2, yet),
195 it is a Vector *control* instruction, and is a sort-of hybrid of
196 svshape and svindex, with the key important feature being the "offset".
197
198 https://libre-soc.org/openpower/sv/remap/discussion
199
200 * svshape2 SVo,SVM2yx,rmm,SVd,sk,mm
201
202 # 1.6.35.1 SVM2-FORM from fields.txt
203 # |0 |6 |10 |11 |16 |21 |24|25 |26 |31 |
204 # | PO | SVo |SVMyx| rmm | SVd |XO |mm|sk | XO |
205
206 note that this fits into the space of svshape and that XO is
207 split across 2 areas.
208
209 """
210 PO = 22
211 XO = 0b011001
212 XO2 = 0b100 # not really XO2 but hey
213 (offs, yx, rmm, SVd, sk, mm) = fields
214 SVd -= 1 # offset by one
215
216 return instruction(
217 (PO, 0, 5),
218 (offs, 6, 9), # offset (the whole point of adding svshape2)
219 (yx, 10, 10), # like svindex
220 (rmm, 11, 15), # ditto svindex
221 (SVd, 16, 20), # ditto svindex
222 (XO2, 21, 23), # actually XO split across 2 places...
223 (mm, 24, 24), # ditto svindex
224 (sk, 25, 25), # ditto svindex
225 (XO, 26, 31),
226 )
227
228
229 @_custom_insns()
230 def svindex(fields):
231 """
232 svindex is a *32-bit-only* instruction. It is a convenience
233 instruction that reduces instruction count for Indexed REMAP
234 Mode.
235 It is *not* a 64-bit-prefixed Vector instruction (no sv.svindex, yet),
236 it is a Vector *control* instruction.
237
238 1.6.28 SVI-FORM
239 |0 |6 |11 |16 |21 |23|24|25|26 31|
240 | PO | SVG|rmm | SVd |ew |yx|mm|sk| XO |
241 """
242 # note that the dimension field one subtracted
243 PO = 22
244 XO = 0b101001
245 (SVG, rmm, SVd, ew, yx, mm, sk) = fields
246 SVd -= 1
247 return instruction(
248 (PO, 0, 5),
249 (SVG, 6, 10),
250 (rmm, 11, 15),
251 (SVd, 16, 20),
252 (ew, 21, 22),
253 (yx, 23, 23),
254 (mm, 24, 24),
255 (sk, 25, 25),
256 (XO, 26, 31),
257 )
258
259
260 @_custom_insns()
261 def svremap(fields):
262 """
263 this is a *32-bit-only* instruction. It updates the SVSHAPE SPR
264 it is *not* a 64-bit-prefixed Vector instruction (no sv.svremap),
265 it is a Vector *control* instruction.
266
267 * svremap SVme,mi0,mi1,mi2,mo0,mo1,pst
268
269 # 1.6.34 SVRM-FORM
270 |0 |6 |11 |13 |15 |17 |19 |21 |22 |26 |31 |
271 | PO | SVme |mi0 | mi1 | mi2 | mo0 | mo1 |pst |/// | XO |
272
273 """
274 PO = 22
275 XO = 0b111001
276 (SVme, mi0, mi1, mi2, mo0, mo1, pst) = fields
277 return instruction(
278 (PO, 0, 5),
279 (SVme, 6, 10),
280 (mi0, 11, 12),
281 (mi1, 13, 14),
282 (mi2, 15, 16),
283 (mo0, 17, 18),
284 (mo1, 19, 20),
285 (pst, 21, 21),
286 (0, 22, 25),
287 (XO, 26, 31),
288 )
289
290
291 # ok from here-on down these are added as 32-bit instructions
292 # and are here only because binutils (at present) doesn't have
293 # them (that's being fixed!)
294 # they can - if implementations then choose - be Vectorised
295 # because they are general-purpose scalar instructions
296 @_custom_insns()
297 def bmask(fields):
298 """
299 1.6.2.2 BM2-FORM
300 |0 |6 |11 |16 |21 |26 |27 31|
301 | PO | RT | RA | RB |bm |L | XO |
302 """
303 PO = 22
304 XO = 0b010001
305 (RT, RA, RB, bm, L) = fields
306 return instruction(
307 (PO, 0, 5),
308 (RT, 6, 10),
309 (RA, 11, 15),
310 (RB, 16, 20),
311 (bm, 21, 25),
312 (L, 26, 26),
313 (XO, 27, 31),
314 )
315
316
317 def _fptrans_insn(name, XO):
318 return [
319 _insn(name, PO=63, Rc=0, XO=XO),
320 _insn(name + ".", PO=63, Rc=1, XO=XO),
321 _insn(name + "s", PO=59, Rc=0, XO=XO),
322 _insn(name + "s.", PO=59, Rc=1, XO=XO),
323 ]
324
325
326 @_custom_insns(
327 *_fptrans_insn("fatan2", XO=0b1001001110),
328 *_fptrans_insn("fatan2pi", XO=0b1000001110),
329 *_fptrans_insn("fpow", XO=0b1111101101),
330 *_fptrans_insn("fpown", XO=0b1101101100),
331 *_fptrans_insn("fpowr", XO=0b1111101100),
332 *_fptrans_insn("frootn", XO=0b1101101101),
333 *_fptrans_insn("fhypot", XO=0b1010001110),
334 *_fptrans_insn("fminnum08", XO=0b1011001100),
335 *_fptrans_insn("fmaxnum08", XO=0b1011101100),
336 *_fptrans_insn("fmin19", XO=0b1011001101),
337 *_fptrans_insn("fmax19", XO=0b1011101101),
338 *_fptrans_insn("fminnum19", XO=0b1011001110),
339 *_fptrans_insn("fmaxnum19", XO=0b1011101110),
340 *_fptrans_insn("fminc", XO=0b1011001111),
341 *_fptrans_insn("fmaxc", XO=0b1011101111),
342 *_fptrans_insn("fminmagnum08", XO=0b1100001110),
343 *_fptrans_insn("fmaxmagnum08", XO=0b1100001111),
344 *_fptrans_insn("fminmag19", XO=0b1101101110),
345 *_fptrans_insn("fmaxmag19", XO=0b1101101111),
346 *_fptrans_insn("fminmagnum19", XO=0b1110001110),
347 *_fptrans_insn("fmaxmagnum19", XO=0b1110001111),
348 *_fptrans_insn("fminmagc", XO=0b1111101110),
349 *_fptrans_insn("fmaxmagc", XO=0b1111101111),
350 *_fptrans_insn("fmod", XO=0b1101001111),
351 *_fptrans_insn("fremainder", XO=0b1111001111),
352 )
353 def fptrans_binary(fields, PO, Rc, XO):
354 # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
355 # however we are out of space with opcode 22
356 # 1.6.7 X-FORM
357 # |0 |6 |11 |16 |21 |31 |
358 # | PO | FRT | FRA | FRB | XO |Rc |
359 # | PO | FRT | FRA | RB | XO |Rc |
360 (FRT, FRA, FRB) = fields
361 return instruction(
362 (PO, 0, 5),
363 (FRT, 6, 10),
364 (FRA, 11, 15),
365 (FRB, 16, 20),
366 (XO, 21, 30),
367 (Rc, 31, 31),
368 )
369
370
371 @_custom_insns(
372 *_fptrans_insn("frsqrt", XO=0b1001001100),
373 *_fptrans_insn("fcbrt", XO=0b1000001100),
374 *_fptrans_insn("frecip", XO=0b1010001100),
375 *_fptrans_insn("fexp2m1", XO=0b1100001100),
376 *_fptrans_insn("flog2p1", XO=0b1100001101),
377 *_fptrans_insn("fexp2", XO=0b1110001100),
378 *_fptrans_insn("flog2", XO=0b1110001101),
379 *_fptrans_insn("fexpm1", XO=0b1100101100),
380 *_fptrans_insn("flogp1", XO=0b1100101101),
381 *_fptrans_insn("fexp", XO=0b1110101100),
382 *_fptrans_insn("flog", XO=0b1110101101),
383 *_fptrans_insn("fexp10m1", XO=0b1101001100),
384 *_fptrans_insn("flog10p1", XO=0b1101001101),
385 *_fptrans_insn("fexp10", XO=0b1111001100),
386 *_fptrans_insn("flog10", XO=0b1111001101),
387 *_fptrans_insn("fsin", XO=0b1001001101),
388 *_fptrans_insn("fcos", XO=0b1001101100),
389 *_fptrans_insn("ftan", XO=0b1001101101),
390 *_fptrans_insn("fasin", XO=0b1001001111),
391 *_fptrans_insn("facos", XO=0b1001101110),
392 *_fptrans_insn("fatan", XO=0b1001101111),
393 *_fptrans_insn("fsinpi", XO=0b1000001101),
394 *_fptrans_insn("fcospi", XO=0b1000101100),
395 *_fptrans_insn("ftanpi", XO=0b1000101101),
396 *_fptrans_insn("fasinpi", XO=0b1000001111),
397 *_fptrans_insn("facospi", XO=0b1000101110),
398 *_fptrans_insn("fatanpi", XO=0b1000101111),
399 *_fptrans_insn("fsinh", XO=0b1010001101),
400 *_fptrans_insn("fcosh", XO=0b1010101100),
401 *_fptrans_insn("ftanh", XO=0b1010101101),
402 *_fptrans_insn("fasinh", XO=0b1010001111),
403 *_fptrans_insn("facosh", XO=0b1010101110),
404 *_fptrans_insn("fatanh", XO=0b1010101111),
405 )
406 def fptrans_unary(fields, PO, Rc, XO):
407 # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
408 # however we are out of space with opcode 22
409 # 1.6.7 X-FORM
410 # |0 |6 |11 |16 |21 |31 |
411 # | PO | FRT | /// | FRB | XO |Rc |
412 (FRT, FRB) = fields
413 return instruction(
414 (PO, 0, 5),
415 (FRT, 6, 10),
416 (0, 11, 15),
417 (FRB, 16, 20),
418 (XO, 21, 30),
419 (Rc, 31, 31),
420 )
421
422
423 @_custom_insns(
424 _insn("ternlogi", Rc=0),
425 _insn("ternlogi.", Rc=1),
426 )
427 def ternlogi(fields, Rc):
428 # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
429 # however we are out of space with opcode 22
430 # 1.6.34 TLI-FORM
431 # |0 |6 |11 |16 |21 |29 |31 |
432 # | PO | RT | RA | RB | TLI | XO |Rc |
433 PO = 5
434 XO = 0
435 (RT, RA, RB, TLI) = fields
436 return instruction(
437 (PO, 0, 5),
438 (RT, 6, 10),
439 (RA, 11, 15),
440 (RB, 16, 20),
441 (TLI, 21, 28),
442 (XO, 29, 30),
443 (Rc, 31, 31),
444 )
445
446
447 @_custom_insns(
448 _insn("grev", Rc=0, imm=0, word=0),
449 _insn("grevw", Rc=0, imm=0, word=1),
450 _insn("grevi", Rc=0, imm=1, word=0),
451 _insn("grevwi", Rc=0, imm=1, word=1),
452 _insn("grev.", Rc=1, imm=0, word=0),
453 _insn("grevw.", Rc=1, imm=0, word=1),
454 _insn("grevi.", Rc=1, imm=1, word=0),
455 _insn("grevwi.", Rc=1, imm=1, word=1),
456 )
457 def grev(fields, Rc, imm, word):
458 # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
459 # however we are out of space with opcode 22
460 insn = PO = 5
461 # _ matches fields in table at:
462 # https://libre-soc.org/openpower/sv/bitmanip/
463 XO = 0b1_0010_110
464 if word:
465 XO |= 0b100_000
466 if imm:
467 XO |= 0b1000_000
468 (RT, RA, XBI) = fields
469 insn = (insn << 5) | RT
470 insn = (insn << 5) | RA
471 if imm and not word:
472 assert 0 <= XBI < 64
473 insn = (insn << 6) | XBI
474 insn = (insn << 9) | XO
475 else:
476 assert 0 <= XBI < 32
477 insn = (insn << 5) | XBI
478 insn = (insn << 10) | XO
479 insn = (insn << 1) | Rc
480 return insn
481
482
483 @_custom_insns(
484 _insn("maxs", XO=0b0111001110, Rc=0),
485 _insn("maxs.", XO=0b0111001110, Rc=1),
486 _insn("maxu", XO=0b0011001110, Rc=0),
487 _insn("maxu.", XO=0b0011001110, Rc=1),
488 _insn("minu", XO=0b0001001110, Rc=0),
489 _insn("minu.", XO=0b0001001110, Rc=1),
490 _insn("mins", XO=0b0101001110, Rc=0),
491 _insn("mins.", XO=0b0101001110, Rc=1),
492 _insn("absdu", XO=0b1011110110, Rc=0),
493 _insn("absdu.", XO=0b1011110110, Rc=1),
494 _insn("absds", XO=0b1001110110, Rc=0),
495 _insn("absds.", XO=0b1001110110, Rc=1),
496 _insn("avgadd", XO=0b1101001110, Rc=0),
497 _insn("avgadd.", XO=0b1101001110, Rc=1),
498 _insn("absdacu", XO=0b1111110110, Rc=0),
499 _insn("absdacu.", XO=0b1111110110, Rc=1),
500 _insn("absdacs", XO=0b0111110110, Rc=0),
501 _insn("absdacs.", XO=0b0111110110, Rc=1),
502 _insn("cprop", XO=0b0110001110, Rc=0),
503 _insn("cprop.", XO=0b0110001110, Rc=1),
504 )
505 def av(fields, XO, Rc):
506 # 1.6.7 X-FORM
507 # |0 |6 |7|8|9 |10 |11|12|13 |15|16|17 |20|21 |31 |
508 # | PO | RT | RA | RB | XO |Rc |
509 PO = 22
510 (RT, RA, RB) = fields
511 return instruction(
512 (PO, 0, 5),
513 (RT, 6, 10),
514 (RA, 11, 15),
515 (RB, 16, 20),
516 (XO, 21, 30),
517 (Rc, 31, 31),
518 )
519
520
521 @_custom_insns()
522 def fmvis(fields):
523 # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
524 # V3.0B 1.6.6 DX-FORM
525 # |0 |6 |7|8|9 |10 |11|12|13 |15|16|17 |26|27 |31 |
526 # | PO | FRS | d1 | d0 | XO |d2 |
527 PO = 22
528 XO = 0b00011
529 (FRS, imm) = fields
530 # first split imm into d1, d0 and d2. sigh
531 d2 = (imm & 1) # LSB (0)
532 d1 = (imm >> 1) & 0b11111 # bits 1-5
533 d0 = (imm >> 6) # MSBs 6-15
534 return instruction(
535 (PO, 0, 5),
536 (FRS, 6, 10),
537 (d1, 11, 15),
538 (d0, 16, 25),
539 (XO, 26, 30),
540 (d2, 31, 31),
541 )
542
543
544 @_custom_insns()
545 def fishmv(fields):
546 # XXX WARNING THESE ARE NOT APPROVED BY OPF ISA WG
547 # V3.0B 1.6.6 DX-FORM
548 # |0 |6 |7|8|9 |10 |11|12|13 |15|16|17 |26|27 |31 |
549 # | PO | FRS | d1 | d0 | XO |d2 |
550 PO = 22
551 XO = 0b01011
552 (FRS, imm) = fields
553 # first split imm into d1, d0 and d2. sigh
554 d2 = (imm & 1) # LSB (0)
555 d1 = (imm >> 1) & 0b11111 # bits 1-5
556 d0 = (imm >> 6) # MSBs 6-15
557 return instruction(
558 (PO, 0, 5),
559 (FRS, 6, 10),
560 (d1, 11, 15),
561 (d0, 16, 25),
562 (XO, 26, 30),
563 (d2, 31, 31),
564 )
565
566
567 # decode GPR into sv extra
568 def get_extra_gpr(etype, regmode, field):
569 if regmode == 'scalar':
570 # cut into 2-bits 5-bits SS FFFFF
571 sv_extra = field >> 5
572 field = field & 0b11111
573 else:
574 # cut into 5-bits 2-bits FFFFF SS
575 sv_extra = field & 0b11
576 field = field >> 2
577 return sv_extra, field
578
579
580 # decode 3-bit CR into sv extra
581 def get_extra_cr_3bit(etype, regmode, field):
582 if regmode == 'scalar':
583 # cut into 2-bits 3-bits SS FFF
584 sv_extra = field >> 3
585 field = field & 0b111
586 else:
587 # cut into 3-bits 4-bits FFF SSSS but will cut 2 zeros off later
588 sv_extra = field & 0b1111
589 field = field >> 4
590 return sv_extra, field
591
592
593 # decodes SUBVL
594 def decode_subvl(encoding):
595 pmap = {'2': 0b01, '3': 0b10, '4': 0b11}
596 assert encoding in pmap, \
597 "encoding %s for SUBVL not recognised" % encoding
598 return pmap[encoding]
599
600
601 # decodes elwidth
602 def decode_elwidth(encoding):
603 pmap = {'8': 0b11, '16': 0b10, '32': 0b01}
604 assert encoding in pmap, \
605 "encoding %s for elwidth not recognised" % encoding
606 return pmap[encoding]
607
608
609 # decodes predicate register encoding
610 def decode_predicate(encoding):
611 pmap = { # integer
612 '1<<r3': (0, 0b001),
613 'r3': (0, 0b010),
614 '~r3': (0, 0b011),
615 'r10': (0, 0b100),
616 '~r10': (0, 0b101),
617 'r30': (0, 0b110),
618 '~r30': (0, 0b111),
619 # CR
620 'lt': (1, 0b000),
621 'nl': (1, 0b001), 'ge': (1, 0b001), # same value
622 'gt': (1, 0b010),
623 'ng': (1, 0b011), 'le': (1, 0b011), # same value
624 'eq': (1, 0b100),
625 'ne': (1, 0b101),
626 'so': (1, 0b110), 'un': (1, 0b110), # same value
627 'ns': (1, 0b111), 'nu': (1, 0b111), # same value
628 }
629 assert encoding in pmap, \
630 "encoding %s for predicate not recognised" % encoding
631 return pmap[encoding]
632
633
634 # decodes "Mode" in similar way to BO field (supposed to, anyway)
635 def decode_bo(encoding):
636 pmap = { # TODO: double-check that these are the same as Branch BO
637 'lt': 0b000,
638 'nl': 0b001, 'ge': 0b001, # same value
639 'gt': 0b010,
640 'ng': 0b011, 'le': 0b011, # same value
641 'eq': 0b100,
642 'ne': 0b101,
643 'so': 0b110, 'un': 0b110, # same value
644 'ns': 0b111, 'nu': 0b111, # same value
645 }
646 assert encoding in pmap, \
647 "encoding %s for BO Mode not recognised" % encoding
648 return pmap[encoding]
649
650
651 # partial-decode fail-first mode
652 def decode_ffirst(encoding):
653 if encoding in ['RC1', '~RC1']:
654 return encoding
655 return decode_bo(encoding)
656
657
658 def decode_reg(field, macros=None):
659 if macros is None:
660 macros = {}
661 # decode the field number. "5.v" or "3.s" or "9"
662 # and now also "*0", and "*%0". note: *NOT* to add "*%rNNN" etc.
663 # https://bugs.libre-soc.org/show_bug.cgi?id=884#c0
664 if field.startswith(("*%", "*")):
665 if field.startswith("*%"):
666 field = field[2:]
667 else:
668 field = field[1:]
669 while field in macros:
670 field = macros[field]
671 return int(field), "vector" # actual register number
672
673 # try old convention (to be retired)
674 field = field.split(".")
675 regmode = 'scalar' # default
676 if len(field) == 2:
677 if field[1] == 's':
678 regmode = 'scalar'
679 elif field[1] == 'v':
680 regmode = 'vector'
681 field = int(field[0]) # actual register number
682 return field, regmode
683
684
685 def decode_imm(field):
686 ldst_imm = "(" in field and field[-1] == ')'
687 if ldst_imm:
688 return field[:-1].split("(")
689 else:
690 return None, field
691
692
693 def crf_extra(etype, rname, extra_idx, regmode, field, extras):
694 """takes a CR Field number (CR0-CR127), splits into EXTRA2/3 and v3.0
695 the scalar/vector mode (crNN.v or crNN.s) changes both the format
696 of the EXTRA2/3 encoding as well as what range of registers is possible.
697 this function can be used for both BF/BFA and BA/BB/BT by first removing
698 the bottom 2 bits of BA/BB/BT then re-instating them after encoding.
699 see https://libre-soc.org/openpower/sv/svp64/appendix/#cr_extra
700 for specification
701 """
702 sv_extra, field = get_extra_cr_3bit(etype, regmode, field)
703 # now sanity-check (and shrink afterwards)
704 if etype == 'EXTRA2':
705 # 3-bit CR Field (BF, BFA) EXTRA2 encoding
706 if regmode == 'scalar':
707 # range is CR0-CR15 in increments of 1
708 assert (sv_extra >> 1) == 0, \
709 "scalar CR %s cannot fit into EXTRA2 %s" % \
710 (rname, str(extras[extra_idx]))
711 # all good: encode as scalar
712 sv_extra = sv_extra & 0b01
713 else: # vector
714 # range is CR0-CR127 in increments of 16
715 assert sv_extra & 0b111 == 0, \
716 "vector CR %s cannot fit into EXTRA2 %s" % \
717 (rname, str(extras[extra_idx]))
718 # all good: encode as vector (bit 2 set)
719 sv_extra = 0b10 | (sv_extra >> 3)
720 else:
721 # 3-bit CR Field (BF, BFA) EXTRA3 encoding
722 if regmode == 'scalar':
723 # range is CR0-CR31 in increments of 1
724 assert (sv_extra >> 2) == 0, \
725 "scalar CR %s cannot fit into EXTRA3 %s" % \
726 (rname, str(extras[extra_idx]))
727 # all good: encode as scalar
728 sv_extra = sv_extra & 0b11
729 else: # vector
730 # range is CR0-CR127 in increments of 8
731 assert sv_extra & 0b11 == 0, \
732 "vector CR %s cannot fit into EXTRA3 %s" % \
733 (rname, str(extras[extra_idx]))
734 # all good: encode as vector (bit 3 set)
735 sv_extra = 0b100 | (sv_extra >> 2)
736 return sv_extra, field
737
738
739 def to_number(field):
740 if field.startswith("0x"):
741 return eval(field)
742 if field.startswith("0b"):
743 return eval(field)
744 return int(field)
745
746
747 db = Database(find_wiki_dir())
748
749
750 # decodes svp64 assembly listings and creates EXT001 svp64 prefixes
751 class SVP64Asm:
752 def __init__(self, lst, bigendian=False, macros=None):
753 if macros is None:
754 macros = {}
755 self.macros = macros
756 self.lst = lst
757 self.trans = self.translate(lst)
758 self.isa = ISA() # reads the v3.0B pseudo-code markdown files
759 self.svp64 = SVP64RM() # reads the svp64 Remap entries for registers
760 assert bigendian == False, "error, bigendian not supported yet"
761
762 def __iter__(self):
763 yield from self.trans
764
765 def translate_one(self, insn, macros=None):
766 if macros is None:
767 macros = {}
768 macros.update(self.macros)
769 isa = self.isa
770 svp64 = self.svp64
771 insn_no_comments = insn.partition('#')[0]
772 # find first space, to get opcode
773 ls = insn_no_comments.split(' ')
774 opcode = ls[0]
775 # now find opcode fields
776 fields = ''.join(ls[1:]).split(',')
777 mfields = list(map(str.strip, fields))
778 log("opcode, fields", ls, opcode, mfields)
779 fields = []
780 # macro substitution
781 for field in mfields:
782 fields.append(macro_subst(macros, field))
783 log("opcode, fields substed", ls, opcode, fields)
784
785 # identify if it is a special instruction
786 custom_insn_hook = CUSTOM_INSNS.get(opcode)
787 if custom_insn_hook is not None:
788 fields = tuple(map(to_number, fields))
789 insn_num = custom_insn_hook(fields)
790 log(opcode, bin(insn_num))
791 yield ".long 0x%X # %s" % (insn_num, insn)
792 return
793
794 # identify if is a svp64 mnemonic
795 if not opcode.startswith('sv.'):
796 yield insn # unaltered
797 return
798 opcode = opcode[3:] # strip leading "sv"
799
800 # start working on decoding the svp64 op: sv.basev30Bop/vec2/mode
801 opmodes = opcode.split("/") # split at "/"
802 v30b_op_orig = opmodes.pop(0) # first is the v3.0B
803 # check instruction ends with dot
804 rc_mode = v30b_op_orig.endswith('.')
805 if rc_mode:
806 v30b_op = v30b_op_orig[:-1]
807 else:
808 v30b_op = v30b_op_orig
809
810 # look up the 32-bit op (original, with "." if it has it)
811 if v30b_op_orig in isa.instr:
812 isa_instr = isa.instr[v30b_op_orig]
813 else:
814 raise Exception("opcode %s of '%s' not supported" %
815 (v30b_op_orig, insn))
816
817 # look up the svp64 op, first the original (with "." if it has it)
818 if v30b_op_orig in svp64.instrs:
819 rm = svp64.instrs[v30b_op_orig] # one row of the svp64 RM CSV
820 # then without the "." (if there was one)
821 elif v30b_op in svp64.instrs:
822 rm = svp64.instrs[v30b_op] # one row of the svp64 RM CSV
823 else:
824 raise Exception(f"opcode {v30b_op_orig!r} of "
825 f"{insn!r} not an svp64 instruction")
826
827 # get regs info e.g. "RT,RA,RB"
828 v30b_regs = isa_instr.regs[0]
829 log("v3.0B op", v30b_op, "Rc=1" if rc_mode else '')
830 log("v3.0B regs", opcode, v30b_regs)
831 log("RM", rm)
832
833 # right. the first thing to do is identify the ordering of
834 # the registers, by name. the EXTRA2/3 ordering is in
835 # rm['0']..rm['3'] but those fields contain the names RA, BB
836 # etc. we have to read the pseudocode to understand which
837 # reg is which in our instruction. sigh.
838
839 # first turn the svp64 rm into a "by name" dict, recording
840 # which position in the RM EXTRA it goes into
841 # also: record if the src or dest was a CR, for sanity-checking
842 # (elwidth overrides on CRs are banned)
843 decode = decode_extra(rm)
844 dest_reg_cr, src_reg_cr, svp64_src, svp64_dest = decode
845
846 log("EXTRA field index, src", svp64_src)
847 log("EXTRA field index, dest", svp64_dest)
848
849 # okaaay now we identify the field value (opcode N,N,N) with
850 # the pseudo-code info (opcode RT, RA, RB)
851 assert len(fields) == len(v30b_regs), \
852 "length of fields %s must match insn `%s` fields %s" % \
853 (str(v30b_regs), insn, str(fields))
854 opregfields = zip(fields, v30b_regs) # err that was easy
855
856 # now for each of those find its place in the EXTRA encoding
857 # note there is the possibility (for LD/ST-with-update) of
858 # RA occurring **TWICE**. to avoid it getting added to the
859 # v3.0B suffix twice, we spot it as a duplicate, here
860 extras = OrderedDict()
861 for idx, (field, regname) in enumerate(opregfields):
862 imm, regname = decode_imm(regname)
863 rtype = get_regtype(regname)
864 log(" idx find", rtype, idx, field, regname, imm)
865 if rtype is None:
866 # probably an immediate field, append it straight
867 extras[('imm', idx, False)] = (idx, field, None, None, None)
868 continue
869 extra = svp64_src.get(regname, None)
870 if extra is not None:
871 extra = ('s', extra, False) # not a duplicate
872 extras[extra] = (idx, field, regname, rtype, imm)
873 log(" idx src", idx, extra, extras[extra])
874 dextra = svp64_dest.get(regname, None)
875 log("regname in", regname, dextra)
876 if dextra is not None:
877 is_a_duplicate = extra is not None # duplicate spotted
878 dextra = ('d', dextra, is_a_duplicate)
879 extras[dextra] = (idx, field, regname, rtype, imm)
880 log(" idx dst", idx, extra, extras[dextra])
881
882 # great! got the extra fields in their associated positions:
883 # also we know the register type. now to create the EXTRA encodings
884 etype = rm['Etype'] # Extra type: EXTRA3/EXTRA2
885 ptype = rm['Ptype'] # Predication type: Twin / Single
886 extra_bits = 0
887 v30b_newfields = []
888 for extra_idx, (idx, field, rname, rtype, iname) in extras.items():
889 # is it a field we don't alter/examine? if so just put it
890 # into newfields
891 if rtype is None:
892 v30b_newfields.append(field)
893 continue
894
895 # identify if this is a ld/st immediate(reg) thing
896 ldst_imm = "(" in field and field[-1] == ')'
897 if ldst_imm:
898 immed, field = field[:-1].split("(")
899
900 field, regmode = decode_reg(field, macros=macros)
901 log(" ", extra_idx, rname, rtype,
902 regmode, iname, field, end=" ")
903
904 # see Mode field https://libre-soc.org/openpower/sv/svp64/
905 # XXX TODO: the following is a bit of a laborious repeated
906 # mess, which could (and should) easily be parameterised.
907 # XXX also TODO: the LD/ST modes which are different
908 # https://libre-soc.org/openpower/sv/ldst/
909
910 # rright. SVP64 register numbering is from 0 to 127
911 # for GPRs, FPRs *and* CR Fields, where for v3.0 the GPRs and RPFs
912 # are 0-31 and CR Fields are only 0-7. the SVP64 RM "Extra"
913 # area is used to extend the numbering from the 32-bit
914 # instruction, and also to record whether the register
915 # is scalar or vector. on a per-operand basis. this
916 # results in a slightly finnicky encoding: here we go...
917
918 # encode SV-GPR and SV-FPR field into extra, v3.0field
919 if rtype in ['GPR', 'FPR']:
920 sv_extra, field = get_extra_gpr(etype, regmode, field)
921 # now sanity-check. EXTRA3 is ok, EXTRA2 has limits
922 # (and shrink to a single bit if ok)
923 if etype == 'EXTRA2':
924 if regmode == 'scalar':
925 # range is r0-r63 in increments of 1
926 assert (sv_extra >> 1) == 0, \
927 "scalar GPR %s cannot fit into EXTRA2 %s" % \
928 (rname, str(extras[extra_idx]))
929 # all good: encode as scalar
930 sv_extra = sv_extra & 0b01
931 else:
932 # range is r0-r127 in increments of 2 (r0 r2 ... r126)
933 assert sv_extra & 0b01 == 0, \
934 "%s: vector field %s cannot fit " \
935 "into EXTRA2 %s" % \
936 (insn, rname, str(extras[extra_idx]))
937 # all good: encode as vector (bit 2 set)
938 sv_extra = 0b10 | (sv_extra >> 1)
939 elif regmode == 'vector':
940 # EXTRA3 vector bit needs marking
941 sv_extra |= 0b100
942
943 # encode SV-CR 3-bit field into extra, v3.0field.
944 # 3-bit is for things like BF and BFA
945 elif rtype == 'CR_3bit':
946 sv_extra, field = crf_extra(etype, rname, extra_idx,
947 regmode, field, extras)
948
949 # encode SV-CR 5-bit field into extra, v3.0field
950 # 5-bit is for things like BA BB BC BT etc.
951 # *sigh* this is the same as 3-bit except the 2 LSBs of the
952 # 5-bit field are passed through unaltered.
953 elif rtype == 'CR_5bit':
954 cr_subfield = field & 0b11 # record bottom 2 bits for later
955 field = field >> 2 # strip bottom 2 bits
956 # use the exact same 3-bit function for the top 3 bits
957 sv_extra, field = crf_extra(etype, rname, extra_idx,
958 regmode, field, extras)
959 # reconstruct the actual 5-bit CR field (preserving the
960 # bottom 2 bits, unaltered)
961 field = (field << 2) | cr_subfield
962
963 else:
964 raise Exception("no type match: %s" % rtype)
965
966 # capture the extra field info
967 log("=>", "%5s" % bin(sv_extra), field)
968 extras[extra_idx] = sv_extra
969
970 # append altered field value to v3.0b, differs for LDST
971 # note that duplicates are skipped e.g. EXTRA2 contains
972 # *BOTH* s:RA *AND* d:RA which happens on LD/ST-with-update
973 srcdest, idx, duplicate = extra_idx
974 if duplicate: # skip adding to v3.0b fields, already added
975 continue
976 if ldst_imm:
977 v30b_newfields.append(("%s(%s)" % (immed, str(field))))
978 else:
979 v30b_newfields.append(str(field))
980
981 log("new v3.0B fields", v30b_op, v30b_newfields)
982 log("extras", extras)
983
984 # rright. now we have all the info. start creating SVP64 instruction.
985 svp64_insn = SVP64Instruction.pair(prefix=0, suffix=0)
986 svp64_prefix = svp64_insn.prefix
987 svp64_rm = svp64_insn.prefix.rm
988
989 # begin with EXTRA fields
990 for idx, sv_extra in extras.items():
991 log(idx)
992 if idx is None:
993 continue
994 if idx[0] == 'imm':
995 continue
996 srcdest, idx, duplicate = idx
997 if etype == 'EXTRA2':
998 svp64_rm.extra2[idx] = sv_extra
999 else:
1000 svp64_rm.extra3[idx] = sv_extra
1001
1002 # identify if the op is a LD/ST. the "blegh" way. copied
1003 # from power_enums. TODO, split the list _insns down.
1004 is_ld = v30b_op in [
1005 "lbarx", "lbz", "lbzu", "lbzux", "lbzx", # load byte
1006 "ld", "ldarx", "ldbrx", "ldu", "ldux", "ldx", # load double
1007 "lfs", "lfsx", "lfsu", "lfsux", # FP load single
1008 "lfd", "lfdx", "lfdu", "lfdux", "lfiwzx", "lfiwax", # FP load dbl
1009 "lha", "lharx", "lhau", "lhaux", "lhax", # load half
1010 "lhbrx", "lhz", "lhzu", "lhzux", "lhzx", # more load half
1011 "lwa", "lwarx", "lwaux", "lwax", "lwbrx", # load word
1012 "lwz", "lwzcix", "lwzu", "lwzux", "lwzx", # more load word
1013 ]
1014 is_st = v30b_op in [
1015 "stb", "stbcix", "stbcx", "stbu", "stbux", "stbx",
1016 "std", "stdbrx", "stdcx", "stdu", "stdux", "stdx",
1017 "stfs", "stfsx", "stfsu", "stfux", # FP store sgl
1018 "stfd", "stfdx", "stfdu", "stfdux", "stfiwx", # FP store dbl
1019 "sth", "sthbrx", "sthcx", "sthu", "sthux", "sthx",
1020 "stw", "stwbrx", "stwcx", "stwu", "stwux", "stwx",
1021 ]
1022 # use this to determine if the SVP64 RM format is different.
1023 # see https://libre-soc.org/openpower/sv/ldst/
1024 is_ldst = is_ld or is_st
1025
1026 # branch-conditional detection
1027 is_bc = v30b_op in [
1028 "bc", "bclr",
1029 ]
1030
1031 # parts of svp64_rm
1032 mmode = 0 # bit 0
1033 pmask = 0 # bits 1-3
1034 destwid = 0 # bits 4-5
1035 srcwid = 0 # bits 6-7
1036 subvl = 0 # bits 8-9
1037 smask = 0 # bits 16-18 but only for twin-predication
1038 mode = 0 # bits 19-23
1039
1040 mask_m_specified = False
1041 has_pmask = False
1042 has_smask = False
1043
1044 saturation = None
1045 src_zero = 0
1046 dst_zero = 0
1047 sv_mode = None
1048
1049 mapreduce = False
1050 reverse_gear = False
1051 mapreduce_crm = False
1052 mapreduce_svm = False
1053
1054 predresult = False
1055 failfirst = False
1056 ldst_elstride = 0
1057
1058 # branch-conditional bits
1059 bc_all = 0
1060 bc_lru = 0
1061 bc_brc = 0
1062 bc_svstep = 0
1063 bc_vsb = 0
1064 bc_vlset = 0
1065 bc_vli = 0
1066 bc_snz = 0
1067
1068 # ok let's start identifying opcode augmentation fields
1069 for encmode in opmodes:
1070 # predicate mask (src and dest)
1071 if encmode.startswith("m="):
1072 pme = encmode
1073 pmmode, pmask = decode_predicate(encmode[2:])
1074 smmode, smask = pmmode, pmask
1075 mmode = pmmode
1076 mask_m_specified = True
1077 # predicate mask (dest)
1078 elif encmode.startswith("dm="):
1079 pme = encmode
1080 pmmode, pmask = decode_predicate(encmode[3:])
1081 mmode = pmmode
1082 has_pmask = True
1083 # predicate mask (src, twin-pred)
1084 elif encmode.startswith("sm="):
1085 sme = encmode
1086 smmode, smask = decode_predicate(encmode[3:])
1087 mmode = smmode
1088 has_smask = True
1089 # vec2/3/4
1090 elif encmode.startswith("vec"):
1091 subvl = decode_subvl(encmode[3:])
1092 # elwidth
1093 elif encmode.startswith("ew="):
1094 destwid = decode_elwidth(encmode[3:])
1095 elif encmode.startswith("sw="):
1096 srcwid = decode_elwidth(encmode[3:])
1097 # HACK! using destwid for pack/unpack TODO, separate setvl RM
1098 elif encmode == 'pk':
1099 destwid = 0b10
1100 elif encmode == 'up':
1101 destwid = 0b01
1102 elif encmode == 'pu':
1103 destwid = 0b11
1104 # element-strided LD/ST
1105 elif encmode == 'els':
1106 ldst_elstride = 1
1107 # saturation
1108 elif encmode == 'sats':
1109 assert sv_mode is None
1110 saturation = 1
1111 sv_mode = 0b10
1112 elif encmode == 'satu':
1113 assert sv_mode is None
1114 sv_mode = 0b10
1115 saturation = 0
1116 # predicate zeroing
1117 elif encmode == 'sz':
1118 src_zero = 1
1119 elif encmode == 'dz':
1120 dst_zero = 1
1121 # failfirst
1122 elif encmode.startswith("ff="):
1123 assert sv_mode is None
1124 sv_mode = 0b01
1125 failfirst = decode_ffirst(encmode[3:])
1126 # predicate-result, interestingly same as fail-first
1127 elif encmode.startswith("pr="):
1128 assert sv_mode is None
1129 sv_mode = 0b11
1130 predresult = decode_ffirst(encmode[3:])
1131 # map-reduce mode, reverse-gear
1132 elif encmode == 'mrr':
1133 assert sv_mode is None
1134 sv_mode = 0b00
1135 mapreduce = True
1136 reverse_gear = True
1137 # map-reduce mode
1138 elif encmode == 'mr':
1139 assert sv_mode is None
1140 sv_mode = 0b00
1141 mapreduce = True
1142 elif encmode == 'crm': # CR on map-reduce
1143 assert sv_mode is None
1144 sv_mode = 0b00
1145 mapreduce_crm = True
1146 elif encmode == 'svm': # sub-vector mode
1147 mapreduce_svm = True
1148 elif is_bc:
1149 if encmode == 'all':
1150 bc_all = 1
1151 elif encmode == 'st': # svstep mode
1152 bc_step = 1
1153 elif encmode == 'sr': # svstep BRc mode
1154 bc_step = 1
1155 bc_brc = 1
1156 elif encmode == 'vs': # VLSET mode
1157 bc_vlset = 1
1158 elif encmode == 'vsi': # VLSET mode with VLI (VL inclusives)
1159 bc_vlset = 1
1160 bc_vli = 1
1161 elif encmode == 'vsb': # VLSET mode with VSb
1162 bc_vlset = 1
1163 bc_vsb = 1
1164 elif encmode == 'vsbi': # VLSET mode with VLI and VSb
1165 bc_vlset = 1
1166 bc_vli = 1
1167 bc_vsb = 1
1168 elif encmode == 'snz': # sz (only) already set above
1169 src_zero = 1
1170 bc_snz = 1
1171 elif encmode == 'lu': # LR update mode
1172 bc_lru = 1
1173 else:
1174 raise AssertionError("unknown encmode %s" % encmode)
1175 else:
1176 raise AssertionError("unknown encmode %s" % encmode)
1177
1178 if ptype == '2P':
1179 # since m=xx takes precedence (overrides) sm=xx and dm=xx,
1180 # treat them as mutually exclusive
1181 if mask_m_specified:
1182 assert not has_smask,\
1183 "cannot have both source-mask and predicate mask"
1184 assert not has_pmask,\
1185 "cannot have both dest-mask and predicate mask"
1186 # since the default is INT predication (ALWAYS), if you
1187 # specify one CR mask, you must specify both, to avoid
1188 # mixing INT and CR reg types
1189 if has_pmask and pmmode == 1:
1190 assert has_smask, \
1191 "need explicit source-mask in CR twin predication"
1192 if has_smask and smmode == 1:
1193 assert has_pmask, \
1194 "need explicit dest-mask in CR twin predication"
1195 # sanity-check that 2Pred mask is same mode
1196 if has_pmask and has_smask:
1197 assert smmode == pmmode, \
1198 "predicate masks %s and %s must be same reg type" % \
1199 (pme, sme)
1200
1201 # sanity-check that twin-predication mask only specified in 2P mode
1202 if ptype == '1P':
1203 assert not has_smask, \
1204 "source-mask can only be specified on Twin-predicate ops"
1205 assert not has_pmask, \
1206 "dest-mask can only be specified on Twin-predicate ops"
1207
1208 # construct the mode field, doing sanity-checking along the way
1209 if mapreduce_svm:
1210 assert sv_mode == 0b00, "sub-vector mode in mapreduce only"
1211 assert subvl != 0, "sub-vector mode not possible on SUBVL=1"
1212
1213 if src_zero:
1214 assert has_smask or mask_m_specified, \
1215 "src zeroing requires a source predicate"
1216 if dst_zero:
1217 assert has_pmask or mask_m_specified, \
1218 "dest zeroing requires a dest predicate"
1219
1220 # okaaay, so there are 4 different modes, here, which will be
1221 # partly-merged-in: is_ldst is merged in with "normal", but
1222 # is_bc is so different it's done separately. likewise is_cr
1223 # (when it is done). here are the maps:
1224
1225 # for "normal" arithmetic: https://libre-soc.org/openpower/sv/normal/
1226 """
1227 | 0-1 | 2 | 3 4 | description |
1228 | --- | --- |---------|-------------------------- |
1229 | 00 | 0 | dz sz | simple mode |
1230 | 00 | 1 | 0 RG | scalar reduce mode (mapreduce), SUBVL=1 |
1231 | 00 | 1 | SVM 0 | subvector reduce mode, SUBVL>1 |
1232 | 00 | 1 | / 1 | reserved |
1233 | 01 | inv | CR-bit | Rc=1: ffirst CR sel |
1234 | 01 | inv | VLi RC1 | Rc=0: ffirst z/nonz |
1235 | 10 | N | dz sz | sat mode: N=0/1 u/s |
1236 | 11 | inv | CR-bit | Rc=1: pred-result CR sel |
1237 | 11 | inv | zz RC1 | Rc=0: pred-result z/nonz |
1238 """
1239
1240 # https://libre-soc.org/openpower/sv/ldst/
1241 # for LD/ST-immediate:
1242 """
1243 | 0-1 | 2 | 3 4 | description |
1244 | --- | --- |---------|--------------------------- |
1245 | 00 | 0 | dz els | normal mode |
1246 | 00 | 1 | dz shf | shift mode |
1247 | 01 | inv | CR-bit | Rc=1: ffirst CR sel |
1248 | 01 | inv | els RC1 | Rc=0: ffirst z/nonz |
1249 | 10 | N | dz els | sat mode: N=0/1 u/s |
1250 | 11 | inv | CR-bit | Rc=1: pred-result CR sel |
1251 | 11 | inv | els RC1 | Rc=0: pred-result z/nonz |
1252 """
1253
1254 # for LD/ST-indexed (RA+RB):
1255 """
1256 | 0-1 | 2 | 3 4 | description |
1257 | --- | --- |---------|-------------------------- |
1258 | 00 | SEA | dz sz | normal mode |
1259 | 01 | SEA | dz sz | Strided (scalar only source) |
1260 | 10 | N | dz sz | sat mode: N=0/1 u/s |
1261 | 11 | inv | CR-bit | Rc=1: pred-result CR sel |
1262 | 11 | inv | dz RC1 | Rc=0: pred-result z/nonz |
1263 """
1264
1265 # and leaving out branches and cr_ops for now because they're
1266 # under development
1267 """ TODO branches and cr_ops
1268 """
1269
1270 # now create mode and (overridden) src/dst widths
1271 # XXX TODO: sanity-check bc modes
1272 if is_bc:
1273 sv_mode = ((bc_svstep << SVP64MODE.MOD2_MSB) |
1274 (bc_vlset << SVP64MODE.MOD2_LSB) |
1275 (bc_snz << SVP64MODE.BC_SNZ))
1276 srcwid = (bc_vsb << 1) | bc_lru
1277 destwid = (bc_lru << 1) | bc_all
1278
1279 else:
1280
1281 ######################################
1282 # "normal" mode
1283 if sv_mode is None:
1284 mode |= src_zero << SVP64MODE.SZ # predicate zeroing
1285 mode |= dst_zero << SVP64MODE.DZ # predicate zeroing
1286 if is_ldst:
1287 # TODO: for now, LD/ST-indexed is ignored.
1288 mode |= ldst_elstride << SVP64MODE.ELS_NORMAL # el-strided
1289 else:
1290 # TODO, reduce and subvector mode
1291 # 00 1 dz CRM reduce mode (mapreduce), SUBVL=1
1292 # 00 1 SVM CRM subvector reduce mode, SUBVL>1
1293 pass
1294 sv_mode = 0b00
1295
1296 ######################################
1297 # "mapreduce" modes
1298 elif sv_mode == 0b00:
1299 mode |= (0b1 << SVP64MODE.REDUCE) # sets mapreduce
1300 assert dst_zero == 0, "dest-zero not allowed in mapreduce mode"
1301 if reverse_gear:
1302 mode |= (0b1 << SVP64MODE.RG) # sets Reverse-gear mode
1303 if mapreduce_crm:
1304 mode |= (0b1 << SVP64MODE.CRM) # sets CRM mode
1305 assert rc_mode, "CRM only allowed when Rc=1"
1306 # bit of weird encoding to jam zero-pred or SVM mode in.
1307 # SVM mode can be enabled only when SUBVL=2/3/4 (vec2/3/4)
1308 if subvl == 0:
1309 mode |= dst_zero << SVP64MODE.DZ # predicate zeroing
1310 elif mapreduce_svm:
1311 mode |= (0b1 << SVP64MODE.SVM) # sets SVM mode
1312
1313 ######################################
1314 # "failfirst" modes
1315 elif sv_mode == 0b01:
1316 assert src_zero == 0, "dest-zero not allowed in failfirst mode"
1317 if failfirst == 'RC1':
1318 mode |= (0b1 << SVP64MODE.RC1) # sets RC1 mode
1319 mode |= (dst_zero << SVP64MODE.DZ) # predicate dst-zeroing
1320 assert rc_mode == False, "ffirst RC1 only ok when Rc=0"
1321 elif failfirst == '~RC1':
1322 mode |= (0b1 << SVP64MODE.RC1) # sets RC1 mode
1323 mode |= (dst_zero << SVP64MODE.DZ) # predicate dst-zeroing
1324 mode |= (0b1 << SVP64MODE.INV) # ... with inversion
1325 assert rc_mode == False, "ffirst RC1 only ok when Rc=0"
1326 else:
1327 assert dst_zero == 0, "dst-zero not allowed in ffirst BO"
1328 assert rc_mode, "ffirst BO only possible when Rc=1"
1329 mode |= (failfirst << SVP64MODE.BO_LSB) # set BO
1330
1331 ######################################
1332 # "saturation" modes
1333 elif sv_mode == 0b10:
1334 mode |= src_zero << SVP64MODE.SZ # predicate zeroing
1335 mode |= dst_zero << SVP64MODE.DZ # predicate zeroing
1336 mode |= (saturation << SVP64MODE.N) # signed/us saturation
1337
1338 ######################################
1339 # "predicate-result" modes. err... code-duplication from ffirst
1340 elif sv_mode == 0b11:
1341 assert src_zero == 0, "dest-zero not allowed in predresult mode"
1342 if predresult == 'RC1':
1343 mode |= (0b1 << SVP64MODE.RC1) # sets RC1 mode
1344 mode |= (dst_zero << SVP64MODE.DZ) # predicate dst-zeroing
1345 assert rc_mode == False, "pr-mode RC1 only ok when Rc=0"
1346 elif predresult == '~RC1':
1347 mode |= (0b1 << SVP64MODE.RC1) # sets RC1 mode
1348 mode |= (dst_zero << SVP64MODE.DZ) # predicate dst-zeroing
1349 mode |= (0b1 << SVP64MODE.INV) # ... with inversion
1350 assert rc_mode == False, "pr-mode RC1 only ok when Rc=0"
1351 else:
1352 assert dst_zero == 0, "dst-zero not allowed in pr-mode BO"
1353 assert rc_mode, "pr-mode BO only possible when Rc=1"
1354 mode |= (predresult << SVP64MODE.BO_LSB) # set BO
1355
1356 # whewww.... modes all done :)
1357 # now put into svp64_rm
1358 mode |= sv_mode
1359 # mode: bits 19-23
1360 svp64_rm.mode = mode
1361
1362 # put in predicate masks into svp64_rm
1363 if ptype == '2P':
1364 # source pred: bits 16-18
1365 svp64_rm.smask = smask
1366 # mask mode: bit 0
1367 svp64_rm.mmode = mmode
1368 # 1-pred: bits 1-3
1369 svp64_rm.mask = pmask
1370
1371 # and subvl: bits 8-9
1372 svp64_rm.subvl = subvl
1373
1374 # put in elwidths
1375 # srcwid: bits 6-7
1376 svp64_rm.ewsrc = srcwid
1377 # destwid: bits 4-5
1378 svp64_rm.elwidth = destwid
1379
1380 # nice debug printout. (and now for something completely different)
1381 # https://youtu.be/u0WOIwlXE9g?t=146
1382 svp64_rm_value = int(svp64_rm)
1383 log("svp64_rm", hex(svp64_rm_value), bin(svp64_rm_value))
1384 log(" mmode 0 :", bin(mmode))
1385 log(" pmask 1-3 :", bin(pmask))
1386 log(" dstwid 4-5 :", bin(destwid))
1387 log(" srcwid 6-7 :", bin(srcwid))
1388 log(" subvl 8-9 :", bin(subvl))
1389 log(" mode 19-23:", bin(mode))
1390 offs = 2 if etype == 'EXTRA2' else 3 # 2 or 3 bits
1391 for idx, sv_extra in extras.items():
1392 if idx is None:
1393 continue
1394 if idx[0] == 'imm':
1395 continue
1396 srcdest, idx, duplicate = idx
1397 start = (10+idx*offs)
1398 end = start + offs-1
1399 log(" extra%d %2d-%2d:" % (idx, start, end),
1400 bin(sv_extra))
1401 if ptype == '2P':
1402 log(" smask 16-17:", bin(smask))
1403 log()
1404
1405 # update prefix PO and ID (aka PID)
1406 svp64_prefix.po = 0x1
1407 svp64_prefix.id = 0b11
1408
1409 # fiinally yield the svp64 prefix and the thingy. v3.0b opcode
1410 rc = '.' if rc_mode else ''
1411 yield ".long 0x%08x" % int(svp64_prefix)
1412 log(v30b_op, v30b_newfields)
1413
1414 v30b_op_rc = v30b_op
1415 if not v30b_op.endswith('.'):
1416 v30b_op_rc += rc
1417
1418 # svstep is weird
1419 # FIXME(lkcl): should sv.svstep be like svstep?
1420 if v30b_op_rc in ("svstep", "svstep."):
1421 # compensate for `SVi -= 1` in svstep()
1422 v30b_newfields[1] = str(int(v30b_newfields[1]) + 1)
1423
1424 custom_insn_hook = CUSTOM_INSNS.get(v30b_op_rc)
1425 if custom_insn_hook is not None:
1426 fields = tuple(map(to_number, v30b_newfields))
1427 insn_num = custom_insn_hook(fields)
1428 log(opcode, bin(insn_num))
1429 yield ".long 0x%X # %s" % (insn_num, insn)
1430 return
1431 # argh, sv.fmadds etc. need to be done manually
1432 elif v30b_op == 'ffmadds':
1433 opcode = 59 << (32-6) # bits 0..6 (MSB0)
1434 opcode |= int(v30b_newfields[0]) << (32-11) # FRT
1435 opcode |= int(v30b_newfields[1]) << (32-16) # FRA
1436 opcode |= int(v30b_newfields[2]) << (32-21) # FRB
1437 opcode |= int(v30b_newfields[3]) << (32-26) # FRC
1438 opcode |= 0b00101 << (32-31) # bits 26-30
1439 if rc:
1440 opcode |= 1 # Rc, bit 31.
1441 yield ".long 0x%x" % opcode
1442 # argh, sv.fdmadds need to be done manually
1443 elif v30b_op == 'fdmadds':
1444 opcode = 59 << (32-6) # bits 0..6 (MSB0)
1445 opcode |= int(v30b_newfields[0]) << (32-11) # FRT
1446 opcode |= int(v30b_newfields[1]) << (32-16) # FRA
1447 opcode |= int(v30b_newfields[2]) << (32-21) # FRB
1448 opcode |= int(v30b_newfields[3]) << (32-26) # FRC
1449 opcode |= 0b11011 << (32-31) # bits 26-30
1450 if rc:
1451 opcode |= 1 # Rc, bit 31.
1452 yield ".long 0x%x" % opcode
1453 # argh, sv.ffadds etc. need to be done manually
1454 elif v30b_op == 'ffadds':
1455 opcode = 59 << (32-6) # bits 0..6 (MSB0)
1456 opcode |= int(v30b_newfields[0]) << (32-11) # FRT
1457 opcode |= int(v30b_newfields[1]) << (32-16) # FRA
1458 opcode |= int(v30b_newfields[2]) << (32-21) # FRB
1459 opcode |= 0b1111100000 << (32-31) # bits 21-30
1460 if rc:
1461 opcode |= 1 # Rc, bit 31.
1462 yield ".long 0x%x" % opcode
1463 else:
1464 if not v30b_op.endswith('.'):
1465 v30b_op += rc
1466 yield "%s %s" % (v30b_op, ", ".join(v30b_newfields))
1467 log("new v3.0B fields", v30b_op, v30b_newfields)
1468
1469 def translate(self, lst):
1470 for insn in lst:
1471 yield from self.translate_one(insn)
1472
1473
1474 def macro_subst(macros, txt):
1475 again = True
1476 log("subst", txt, macros)
1477 while again:
1478 again = False
1479 for macro, value in macros.items():
1480 if macro == txt:
1481 again = True
1482 replaced = txt.replace(macro, value)
1483 log("macro", txt, "replaced", replaced, macro, value)
1484 txt = replaced
1485 continue
1486 toreplace = '%s.s' % macro
1487 if toreplace == txt:
1488 again = True
1489 replaced = txt.replace(toreplace, "%s.s" % value)
1490 log("macro", txt, "replaced", replaced, toreplace, value)
1491 txt = replaced
1492 continue
1493 toreplace = '%s.v' % macro
1494 if toreplace == txt:
1495 again = True
1496 replaced = txt.replace(toreplace, "%s.v" % value)
1497 log("macro", txt, "replaced", replaced, toreplace, value)
1498 txt = replaced
1499 continue
1500 toreplace = '(%s)' % macro
1501 if toreplace in txt:
1502 again = True
1503 replaced = txt.replace(toreplace, '(%s)' % value)
1504 log("macro", txt, "replaced", replaced, toreplace, value)
1505 txt = replaced
1506 continue
1507 log(" processed", txt)
1508 return txt
1509
1510
1511 def get_ws(line):
1512 # find whitespace
1513 ws = ''
1514 while line:
1515 if not line[0].isspace():
1516 break
1517 ws += line[0]
1518 line = line[1:]
1519 return ws, line
1520
1521
1522 def asm_process():
1523 # get an input file and an output file
1524 args = sys.argv[1:]
1525 if len(args) == 0:
1526 infile = sys.stdin
1527 outfile = sys.stdout
1528 # read the whole lot in advance in case of in-place
1529 lines = list(infile.readlines())
1530 elif len(args) != 2:
1531 print("pysvp64asm [infile | -] [outfile | -]", file=sys.stderr)
1532 exit(0)
1533 else:
1534 if args[0] == '--':
1535 infile = sys.stdin
1536 else:
1537 infile = open(args[0], "r")
1538 # read the whole lot in advance in case of in-place overwrite
1539 lines = list(infile.readlines())
1540
1541 if args[1] == '--':
1542 outfile = sys.stdout
1543 else:
1544 outfile = open(args[1], "w")
1545
1546 # read the line, look for custom insn, process it
1547 macros = {} # macros which start ".set"
1548 isa = SVP64Asm([])
1549 for line in lines:
1550 op = line.split("#")[0].strip()
1551 # identify macros
1552 if op.startswith(".set"):
1553 macro = op[4:].split(",")
1554 (macro, value) = map(str.strip, macro)
1555 macros[macro] = value
1556 if not op.startswith('sv.') and not op.startswith(tuple(CUSTOM_INSNS)):
1557 outfile.write(line)
1558 continue
1559
1560 (ws, line) = get_ws(line)
1561 lst = isa.translate_one(op, macros)
1562 lst = '; '.join(lst)
1563 outfile.write("%s%s # %s\n" % (ws, lst, op))
1564
1565
1566 if __name__ == '__main__':
1567 lst = ['slw 3, 1, 4',
1568 'extsw 5, 3',
1569 'sv.extsw 5, 3',
1570 'sv.cmpi 5, 1, 3, 2',
1571 'sv.setb 5, 31',
1572 'sv.isel 64.v, 3, 2, 65.v',
1573 'sv.setb/dm=r3/sm=1<<r3 5, 31',
1574 'sv.setb/m=r3 5, 31',
1575 'sv.setb/vec2 5, 31',
1576 'sv.setb/sw=8/ew=16 5, 31',
1577 'sv.extsw./ff=eq 5, 31',
1578 'sv.extsw./satu/sz/dz/sm=r3/dm=r3 5, 31',
1579 'sv.extsw./pr=eq 5.v, 31',
1580 'sv.add. 5.v, 2.v, 1.v',
1581 'sv.add./m=r3 5.v, 2.v, 1.v',
1582 ]
1583 lst += [
1584 'sv.stw 5.v, 4(1.v)',
1585 'sv.ld 5.v, 4(1.v)',
1586 'setvl. 2, 3, 4, 0, 1, 1',
1587 'sv.setvl. 2, 3, 4, 0, 1, 1',
1588 ]
1589 lst = [
1590 "sv.stfsu 0.v, 16(4.v)",
1591 ]
1592 lst = [
1593 "sv.stfsu/els 0.v, 16(4)",
1594 ]
1595 lst = [
1596 'sv.add./mr 5.v, 2.v, 1.v',
1597 ]
1598 macros = {'win2': '50', 'win': '60'}
1599 lst = [
1600 'sv.addi win2.v, win.v, -1',
1601 'sv.add./mrr 5.v, 2.v, 1.v',
1602 #'sv.lhzsh 5.v, 11(9.v), 15',
1603 #'sv.lwzsh 5.v, 11(9.v), 15',
1604 'sv.ffmadds 6.v, 2.v, 4.v, 6.v',
1605 ]
1606 lst = [
1607 #'sv.fmadds 0.v, 8.v, 16.v, 4.v',
1608 #'sv.ffadds 0.v, 8.v, 4.v',
1609 'svremap 11, 0, 1, 2, 3, 2, 1',
1610 'svshape 8, 1, 1, 1, 0',
1611 'svshape 8, 1, 1, 1, 1',
1612 ]
1613 lst = [
1614 #'sv.lfssh 4.v, 11(8.v), 15',
1615 #'sv.lwzsh 4.v, 11(8.v), 15',
1616 #'sv.svstep. 2.v, 4, 0',
1617 #'sv.fcfids. 48.v, 64.v',
1618 'sv.fcoss. 80.v, 0.v',
1619 'sv.fcoss. 20.v, 0.v',
1620 ]
1621 lst = [
1622 'sv.bc/all 3,12,192',
1623 'sv.bclr/vsbi 3,81.v,192',
1624 'sv.ld 5.v, 4(1.v)',
1625 'sv.svstep. 2.v, 4, 0',
1626 ]
1627 lst = [
1628 'maxs 3,12,5',
1629 'maxs. 3,12,5',
1630 'avgadd 3,12,5',
1631 'absdu 3,12,5',
1632 'absds 3,12,5',
1633 'absdacu 3,12,5',
1634 'absdacs 3,12,5',
1635 'cprop 3,12,5',
1636 'svindex 0,0,1,0,0,0,0',
1637 ]
1638 lst = [
1639 'sv.svstep./m=r3 2.v, 4, 0',
1640 'ternlogi 0,0,0,0x5',
1641 'fmvis 5,65535',
1642 'fmvis 5,1',
1643 'fmvis 5,2',
1644 'fmvis 5,4',
1645 'fmvis 5,8',
1646 'fmvis 5,16',
1647 'fmvis 5,32',
1648 'fmvis 5,64',
1649 'fmvis 5,32768',
1650 ]
1651 lst = [
1652 'sv.andi. *80, *80, 1',
1653 'sv.ffmadds. 6.v, 2.v, 4.v, 6.v', # incorrectly inserted 32-bit op
1654 'sv.ffmadds 6.v, 2.v, 4.v, 6.v', # correctly converted to .long
1655 'svshape2 8, 1, 31, 7, 1, 1',
1656 ]
1657 lst = [
1658 'sv.setvl 2, 3, 4, 0, 1, 1',
1659 'sv.setvl/pk 2, 3, 4, 0, 1, 1',
1660 'sv.setvl/up 2, 3, 4, 0, 1, 1',
1661 'sv.setvl/pu 2, 3, 4, 0, 1, 1',
1662 ]
1663 isa = SVP64Asm(lst, macros=macros)
1664 log("list:\n", "\n\t".join(list(isa)))
1665 # running svp64.py is designed to test hard-coded lists
1666 # (above) - which strictly speaking should all be unit tests.
1667 # if you need to actually do assembler translation at the
1668 # commandline use "pysvp64asm" - see setup.py
1669 # XXX NO. asm_process()