power_insn: support legacy style
[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_insn import Style
34 from openpower.decoder.power_insn import WordInstruction
35 from openpower.decoder.power_enums import find_wiki_dir
36
37 # for debug logging
38 from openpower.util import log
39
40
41 # decode GPR into sv extra
42 def get_extra_gpr(etype, regmode, field):
43 if regmode == 'scalar':
44 # cut into 2-bits 5-bits SS FFFFF
45 sv_extra = field >> 5
46 field = field & 0b11111
47 else:
48 # cut into 5-bits 2-bits FFFFF SS
49 sv_extra = field & 0b11
50 field = field >> 2
51 return sv_extra, field
52
53
54 # decode 3-bit CR into sv extra
55 def get_extra_cr_3bit(etype, regmode, field):
56 if regmode == 'scalar':
57 # cut into 2-bits 3-bits SS FFF
58 sv_extra = field >> 3
59 field = field & 0b111
60 else:
61 # cut into 3-bits 4-bits FFF SSSS but will cut 2 zeros off later
62 sv_extra = field & 0b1111
63 field = field >> 4
64 return sv_extra, field
65
66
67 # decodes SUBVL
68 def decode_subvl(encoding):
69 pmap = {'2': 0b01, '3': 0b10, '4': 0b11}
70 assert encoding in pmap, \
71 "encoding %s for SUBVL not recognised" % encoding
72 return pmap[encoding]
73
74
75 # decodes elwidth
76 def decode_elwidth(encoding):
77 pmap = {'8': 0b11, '16': 0b10, '32': 0b01}
78 assert encoding in pmap, \
79 "encoding %s for elwidth not recognised" % encoding
80 return pmap[encoding]
81
82
83 # decodes predicate register encoding
84 def decode_predicate(encoding):
85 pmap = { # integer
86 '1<<r3': (0, 0b001),
87 'r3': (0, 0b010),
88 '~r3': (0, 0b011),
89 'r10': (0, 0b100),
90 '~r10': (0, 0b101),
91 'r30': (0, 0b110),
92 '~r30': (0, 0b111),
93 # CR
94 'lt': (1, 0b000),
95 'nl': (1, 0b001), 'ge': (1, 0b001), # same value
96 'gt': (1, 0b010),
97 'ng': (1, 0b011), 'le': (1, 0b011), # same value
98 'eq': (1, 0b100),
99 'ne': (1, 0b101),
100 'so': (1, 0b110), 'un': (1, 0b110), # same value
101 'ns': (1, 0b111), 'nu': (1, 0b111), # same value
102 }
103 assert encoding in pmap, \
104 "encoding %s for predicate not recognised" % encoding
105 return pmap[encoding]
106
107
108 # decodes "Mode" in similar way to BO field (supposed to, anyway)
109 def decode_bo(encoding):
110 pmap = { # TODO: double-check that these are the same as Branch BO
111 'lt': 0b000,
112 'nl': 0b001, 'ge': 0b001, # same value
113 'gt': 0b010,
114 'ng': 0b011, 'le': 0b011, # same value
115 'eq': 0b100,
116 'ne': 0b101,
117 'so': 0b110, 'un': 0b110, # same value
118 'ns': 0b111, 'nu': 0b111, # same value
119 }
120 assert encoding in pmap, \
121 "encoding %s for BO Mode not recognised" % encoding
122 # barse-ackwards MSB0/LSB0. sigh. this would be nice to be the
123 # same as the decode_predicate() CRfield table above, but (inv,CRbit)
124 # is how it is in the spec [decode_predicate is (CRbit,inv)]
125 mapped = pmap[encoding]
126 si = SelectableInt(0, 3)
127 si[0] = mapped & 1 # inv
128 si[1:3] = mapped >> 1 # CR
129 return int(si)
130
131
132 # partial-decode fail-first mode
133 def decode_ffirst(encoding):
134 if encoding in ['RC1', '~RC1']:
135 return encoding
136 return decode_bo(encoding)
137
138
139 def decode_reg(field, macros=None):
140 if macros is None:
141 macros = {}
142 # decode the field number. "5.v" or "3.s" or "9"
143 # and now also "*0", and "*%0". note: *NOT* to add "*%rNNN" etc.
144 # https://bugs.libre-soc.org/show_bug.cgi?id=884#c0
145 if field.startswith(("*%", "*")):
146 if field.startswith("*%"):
147 field = field[2:]
148 else:
149 field = field[1:]
150 while field in macros:
151 field = macros[field]
152 return int(field), "vector" # actual register number
153
154 # try old convention (to be retired)
155 field = field.split(".")
156 regmode = 'scalar' # default
157 if len(field) == 2:
158 if field[1] == 's':
159 regmode = 'scalar'
160 elif field[1] == 'v':
161 regmode = 'vector'
162 field = int(field[0]) # actual register number
163 return field, regmode
164
165
166 def decode_imm(field):
167 ldst_imm = "(" in field and field[-1] == ')'
168 if ldst_imm:
169 return field[:-1].split("(")
170 else:
171 return None, field
172
173
174 def crf_extra(etype, rname, extra_idx, regmode, field, extras):
175 """takes a CR Field number (CR0-CR127), splits into EXTRA2/3 and v3.0
176 the scalar/vector mode (crNN.v or crNN.s) changes both the format
177 of the EXTRA2/3 encoding as well as what range of registers is possible.
178 this function can be used for both BF/BFA and BA/BB/BT by first removing
179 the bottom 2 bits of BA/BB/BT then re-instating them after encoding.
180 see https://libre-soc.org/openpower/sv/svp64/appendix/#cr_extra
181 for specification
182 """
183 sv_extra, field = get_extra_cr_3bit(etype, regmode, field)
184 # now sanity-check (and shrink afterwards)
185 if etype == 'EXTRA2':
186 # 3-bit CR Field (BF, BFA) EXTRA2 encoding
187 if regmode == 'scalar':
188 # range is CR0-CR15 in increments of 1
189 assert (sv_extra >> 1) == 0, \
190 "scalar CR %s cannot fit into EXTRA2 %s" % \
191 (rname, str(extras[extra_idx]))
192 # all good: encode as scalar
193 sv_extra = sv_extra & 0b01
194 else: # vector
195 # range is CR0-CR127 in increments of 16
196 assert sv_extra & 0b111 == 0, \
197 "vector CR %s cannot fit into EXTRA2 %s" % \
198 (rname, str(extras[extra_idx]))
199 # all good: encode as vector (bit 2 set)
200 sv_extra = 0b10 | (sv_extra >> 3)
201 else:
202 # 3-bit CR Field (BF, BFA) EXTRA3 encoding
203 if regmode == 'scalar':
204 # range is CR0-CR31 in increments of 1
205 assert (sv_extra >> 2) == 0, \
206 "scalar CR %s cannot fit into EXTRA3 %s" % \
207 (rname, str(extras[extra_idx]))
208 # all good: encode as scalar
209 sv_extra = sv_extra & 0b11
210 else: # vector
211 # range is CR0-CR127 in increments of 8
212 assert sv_extra & 0b11 == 0, \
213 "vector CR %s cannot fit into EXTRA3 %s" % \
214 (rname, str(extras[extra_idx]))
215 # all good: encode as vector (bit 3 set)
216 sv_extra = 0b100 | (sv_extra >> 2)
217 return sv_extra, field
218
219
220 def to_number(field):
221 if field.startswith("0x"):
222 return eval(field)
223 if field.startswith("0b"):
224 return eval(field)
225 return int(field)
226
227
228 DB = Database(find_wiki_dir())
229
230
231 # decodes svp64 assembly listings and creates EXT001 svp64 prefixes
232 class SVP64Asm:
233 def __init__(self, lst, bigendian=False, macros=None):
234 if macros is None:
235 macros = {}
236 self.macros = macros
237 self.lst = lst
238 self.trans = self.translate(lst)
239 self.isa = ISA() # reads the v3.0B pseudo-code markdown files
240 self.svp64 = SVP64RM() # reads the svp64 Remap entries for registers
241 assert bigendian == False, "error, bigendian not supported yet"
242
243 def __iter__(self):
244 yield from self.trans
245
246 def translate_one(self, insn, macros=None):
247 if macros is None:
248 macros = {}
249 macros.update(self.macros)
250 isa = self.isa
251 svp64 = self.svp64
252 insn_no_comments = insn.partition('#')[0].strip()
253 if not insn_no_comments:
254 return
255
256 # find first space, to get opcode
257 ls = insn_no_comments.split()
258 opcode = ls[0]
259 # now find opcode fields
260 fields = ''.join(ls[1:]).split(',')
261 mfields = list(filter(bool, map(str.strip, fields)))
262 log("opcode, fields", ls, opcode, mfields)
263 fields = []
264 # macro substitution
265 for field in mfields:
266 fields.append(macro_subst(macros, field))
267 log("opcode, fields substed", ls, opcode, fields)
268
269 # identify if it is a word instruction
270 record = None
271 if os.environ.get("INSNDB"):
272 record = DB[opcode]
273 if record is not None:
274 insn = WordInstruction.assemble(db=DB,
275 entry=opcode, arguments=fields)
276 yield from insn.disassemble(db=DB, style=Style.LEGACY)
277 return
278
279 # identify if is a svp64 mnemonic
280 if not opcode.startswith('sv.'):
281 yield insn # unaltered
282 return
283 opcode = opcode[3:] # strip leading "sv"
284
285 # start working on decoding the svp64 op: sv.basev30Bop/vec2/mode
286 opmodes = opcode.split("/") # split at "/"
287 v30b_op_orig = opmodes.pop(0) # first is the v3.0B
288 # check instruction ends with dot
289 rc_mode = v30b_op_orig.endswith('.')
290 if rc_mode:
291 v30b_op = v30b_op_orig[:-1]
292 else:
293 v30b_op = v30b_op_orig
294
295 record = None
296 if os.environ.get("INSNDB"):
297 record = DB[v30b_op]
298 if record is not None:
299 insn = SVP64Instruction.assemble(db=DB,
300 entry=v30b_op_orig,
301 arguments=fields,
302 specifiers=opmodes)
303 yield from insn.disassemble(db=DB, style=Style.LEGACY)
304 return
305
306 # look up the 32-bit op (original, with "." if it has it)
307 if v30b_op_orig in isa.instr:
308 isa_instr = isa.instr[v30b_op_orig]
309 else:
310 raise Exception("opcode %s of '%s' not supported" %
311 (v30b_op_orig, insn))
312
313 # look up the svp64 op, first the original (with "." if it has it)
314 if v30b_op_orig in svp64.instrs:
315 rm = svp64.instrs[v30b_op_orig] # one row of the svp64 RM CSV
316 # then without the "." (if there was one)
317 elif v30b_op in svp64.instrs:
318 rm = svp64.instrs[v30b_op] # one row of the svp64 RM CSV
319 else:
320 raise Exception(f"opcode {v30b_op_orig!r} of "
321 f"{insn!r} not an svp64 instruction")
322
323 # get regs info e.g. "RT,RA,RB"
324 v30b_regs = isa_instr.regs[0]
325 log("v3.0B op", v30b_op, "Rc=1" if rc_mode else '')
326 log("v3.0B regs", opcode, v30b_regs)
327 log("RM", rm)
328
329 # right. the first thing to do is identify the ordering of
330 # the registers, by name. the EXTRA2/3 ordering is in
331 # rm['0']..rm['3'] but those fields contain the names RA, BB
332 # etc. we have to read the pseudocode to understand which
333 # reg is which in our instruction. sigh.
334
335 # first turn the svp64 rm into a "by name" dict, recording
336 # which position in the RM EXTRA it goes into
337 # also: record if the src or dest was a CR, for sanity-checking
338 # (elwidth overrides on CRs are banned)
339 decode = decode_extra(rm)
340 dest_reg_cr, src_reg_cr, svp64_src, svp64_dest = decode
341
342 log("EXTRA field index, src", svp64_src)
343 log("EXTRA field index, dest", svp64_dest)
344
345 # okaaay now we identify the field value (opcode N,N,N) with
346 # the pseudo-code info (opcode RT, RA, RB)
347 assert len(fields) == len(v30b_regs), \
348 "length of fields %s must match insn `%s` fields %s" % \
349 (str(v30b_regs), insn, str(fields))
350 opregfields = zip(fields, v30b_regs) # err that was easy
351
352 # now for each of those find its place in the EXTRA encoding
353 # note there is the possibility (for LD/ST-with-update) of
354 # RA occurring **TWICE**. to avoid it getting added to the
355 # v3.0B suffix twice, we spot it as a duplicate, here
356 extras = OrderedDict()
357 for idx, (field, regname) in enumerate(opregfields):
358 imm, regname = decode_imm(regname)
359 rtype = get_regtype(regname)
360 log(" idx find", rtype, idx, field, regname, imm)
361 if rtype is None:
362 # probably an immediate field, append it straight
363 extras[('imm', idx, False)] = (idx, field, None, None, None)
364 continue
365 extra = svp64_src.get(regname, None)
366 if extra is not None:
367 extra = ('s', extra, False) # not a duplicate
368 extras[extra] = (idx, field, regname, rtype, imm)
369 log(" idx src", idx, extra, extras[extra])
370 dextra = svp64_dest.get(regname, None)
371 log("regname in", regname, dextra)
372 if dextra is not None:
373 is_a_duplicate = extra is not None # duplicate spotted
374 dextra = ('d', dextra, is_a_duplicate)
375 extras[dextra] = (idx, field, regname, rtype, imm)
376 log(" idx dst", idx, extra, extras[dextra])
377
378 # great! got the extra fields in their associated positions:
379 # also we know the register type. now to create the EXTRA encodings
380 etype = rm['Etype'] # Extra type: EXTRA3/EXTRA2
381 ptype = rm['Ptype'] # Predication type: Twin / Single
382 extra_bits = 0
383 v30b_newfields = []
384 for extra_idx, (idx, field, rname, rtype, iname) in extras.items():
385 # is it a field we don't alter/examine? if so just put it
386 # into newfields
387 if rtype is None:
388 v30b_newfields.append(field)
389 continue
390
391 # identify if this is a ld/st immediate(reg) thing
392 ldst_imm = "(" in field and field[-1] == ')'
393 if ldst_imm:
394 immed, field = field[:-1].split("(")
395
396 field, regmode = decode_reg(field, macros=macros)
397 log(" ", extra_idx, rname, rtype,
398 regmode, iname, field, end=" ")
399
400 # see Mode field https://libre-soc.org/openpower/sv/svp64/
401 # XXX TODO: the following is a bit of a laborious repeated
402 # mess, which could (and should) easily be parameterised.
403 # XXX also TODO: the LD/ST modes which are different
404 # https://libre-soc.org/openpower/sv/ldst/
405
406 # rright. SVP64 register numbering is from 0 to 127
407 # for GPRs, FPRs *and* CR Fields, where for v3.0 the GPRs and RPFs
408 # are 0-31 and CR Fields are only 0-7. the SVP64 RM "Extra"
409 # area is used to extend the numbering from the 32-bit
410 # instruction, and also to record whether the register
411 # is scalar or vector. on a per-operand basis. this
412 # results in a slightly finnicky encoding: here we go...
413
414 # encode SV-GPR and SV-FPR field into extra, v3.0field
415 if rtype in ['GPR', 'FPR']:
416 sv_extra, field = get_extra_gpr(etype, regmode, field)
417 # now sanity-check. EXTRA3 is ok, EXTRA2 has limits
418 # (and shrink to a single bit if ok)
419 if etype == 'EXTRA2':
420 if regmode == 'scalar':
421 # range is r0-r63 in increments of 1
422 assert (sv_extra >> 1) == 0, \
423 "scalar GPR %s cannot fit into EXTRA2 %s" % \
424 (rname, str(extras[extra_idx]))
425 # all good: encode as scalar
426 sv_extra = sv_extra & 0b01
427 else:
428 # range is r0-r127 in increments of 2 (r0 r2 ... r126)
429 assert sv_extra & 0b01 == 0, \
430 "%s: vector field %s cannot fit " \
431 "into EXTRA2 %s" % \
432 (insn, rname, str(extras[extra_idx]))
433 # all good: encode as vector (bit 2 set)
434 sv_extra = 0b10 | (sv_extra >> 1)
435 elif regmode == 'vector':
436 # EXTRA3 vector bit needs marking
437 sv_extra |= 0b100
438
439 # encode SV-CR 3-bit field into extra, v3.0field.
440 # 3-bit is for things like BF and BFA
441 elif rtype == 'CR_3bit':
442 sv_extra, field = crf_extra(etype, rname, extra_idx,
443 regmode, field, extras)
444
445 # encode SV-CR 5-bit field into extra, v3.0field
446 # 5-bit is for things like BA BB BC BT etc.
447 # *sigh* this is the same as 3-bit except the 2 LSBs of the
448 # 5-bit field are passed through unaltered.
449 elif rtype == 'CR_5bit':
450 cr_subfield = field & 0b11 # record bottom 2 bits for later
451 field = field >> 2 # strip bottom 2 bits
452 # use the exact same 3-bit function for the top 3 bits
453 sv_extra, field = crf_extra(etype, rname, extra_idx,
454 regmode, field, extras)
455 # reconstruct the actual 5-bit CR field (preserving the
456 # bottom 2 bits, unaltered)
457 field = (field << 2) | cr_subfield
458
459 else:
460 raise Exception("no type match: %s" % rtype)
461
462 # capture the extra field info
463 log("=>", "%5s" % bin(sv_extra), field)
464 extras[extra_idx] = sv_extra
465
466 # append altered field value to v3.0b, differs for LDST
467 # note that duplicates are skipped e.g. EXTRA2 contains
468 # *BOTH* s:RA *AND* d:RA which happens on LD/ST-with-update
469 srcdest, idx, duplicate = extra_idx
470 if duplicate: # skip adding to v3.0b fields, already added
471 continue
472 if ldst_imm:
473 v30b_newfields.append(("%s(%s)" % (immed, str(field))))
474 else:
475 v30b_newfields.append(str(field))
476
477 log("new v3.0B fields", v30b_op, v30b_newfields)
478 log("extras", extras)
479
480 # rright. now we have all the info. start creating SVP64 instruction.
481 svp64_insn = SVP64Instruction.pair(prefix=0, suffix=0)
482 svp64_prefix = svp64_insn.prefix
483 svp64_rm = svp64_insn.prefix.rm
484
485 # begin with EXTRA fields
486 for idx, sv_extra in extras.items():
487 log(idx)
488 if idx is None:
489 continue
490 if idx[0] == 'imm':
491 continue
492 srcdest, idx, duplicate = idx
493 if etype == 'EXTRA2':
494 svp64_rm.extra2[idx] = sv_extra
495 else:
496 svp64_rm.extra3[idx] = sv_extra
497
498 # identify if the op is a LD/ST.
499 # see https://libre-soc.org/openpower/sv/ldst/
500 is_ldst = rm['mode'] in ['LDST_IDX', 'LDST_IMM']
501 is_ldst_idx = rm['mode'] == 'LDST_IDX'
502 is_ldst_imm = rm['mode'] == 'LDST_IMM'
503 is_ld = v30b_op.startswith("l") and is_ldst
504 is_st = v30b_op.startswith("s") and is_ldst
505
506 # branch-conditional or CR detection
507 is_bc = rm['mode'] == 'BRANCH'
508 is_cr = rm['mode'] == 'CROP'
509
510 # parts of svp64_rm
511 mmode = 0 # bit 0
512 pmask = 0 # bits 1-3
513 destwid = 0 # bits 4-5
514 srcwid = 0 # bits 6-7
515 subvl = 0 # bits 8-9
516 smask = 0 # bits 16-18 but only for twin-predication
517 mode = 0 # bits 19-23
518
519 mask_m_specified = False
520 has_pmask = False
521 has_smask = False
522
523 saturation = None
524 src_zero = 0
525 dst_zero = 0
526 sv_mode = None
527
528 mapreduce = False
529 reverse_gear = False
530 mapreduce_crm = False
531
532 predresult = False
533 failfirst = False
534 ldst_elstride = 0
535 ldst_postinc = 0
536 sea = False
537
538 vli = False
539 sea = False
540
541 # ok let's start identifying opcode augmentation fields
542 for encmode in opmodes:
543 # predicate mask (src and dest)
544 if encmode.startswith("m="):
545 pme = encmode
546 pmmode, pmask = decode_predicate(encmode[2:])
547 smmode, smask = pmmode, pmask
548 mmode = pmmode
549 mask_m_specified = True
550 # predicate mask (dest)
551 elif encmode.startswith("dm="):
552 pme = encmode
553 pmmode, pmask = decode_predicate(encmode[3:])
554 mmode = pmmode
555 has_pmask = True
556 # predicate mask (src, twin-pred)
557 elif encmode.startswith("sm="):
558 sme = encmode
559 smmode, smask = decode_predicate(encmode[3:])
560 mmode = smmode
561 has_smask = True
562 # vec2/3/4
563 elif encmode.startswith("vec"):
564 subvl = decode_subvl(encmode[3:])
565 # elwidth (both src and dest, like mask)
566 elif encmode.startswith("w="):
567 destwid = decode_elwidth(encmode[2:])
568 srcwid = decode_elwidth(encmode[2:])
569 # just dest width
570 elif encmode.startswith("dw="):
571 destwid = decode_elwidth(encmode[3:])
572 # just src width
573 elif encmode.startswith("sw="):
574 srcwid = decode_elwidth(encmode[3:])
575 # post-increment
576 elif encmode == 'pi':
577 ldst_postinc = 1
578 # in indexed mode, set sv_mode=0b00
579 assert is_ldst_imm is True
580 sv_mode = 0b00
581 # element-strided LD/ST
582 elif encmode == 'els':
583 ldst_elstride = 1
584 # in indexed mode, set sv_mode=0b01
585 if is_ldst_idx:
586 sv_mode = 0b01
587 # saturation
588 elif encmode == 'sats':
589 assert sv_mode is None
590 saturation = 1
591 sv_mode = 0b10
592 elif encmode == 'satu':
593 assert sv_mode is None
594 sv_mode = 0b10
595 saturation = 0
596 # predicate zeroing
597 elif encmode == 'zz': # TODO, a lot more checking on legality
598 dst_zero = 1 # NOT on cr_ops, that's RM[6]
599 src_zero = 1
600 elif encmode == 'sz':
601 src_zero = 1
602 elif encmode == 'dz':
603 dst_zero = 1
604 # failfirst
605 elif encmode.startswith("ff="):
606 assert sv_mode is None
607 if is_cr: # sigh, CROPs is different
608 sv_mode = 0b10
609 else:
610 sv_mode = 0b01
611 failfirst = decode_ffirst(encmode[3:])
612 assert sea is False, "cannot use failfirst with signed-address"
613 # predicate-result, interestingly same as fail-first
614 elif encmode.startswith("pr="):
615 assert sv_mode is None
616 sv_mode = 0b11
617 predresult = decode_ffirst(encmode[3:])
618 # map-reduce mode, reverse-gear
619 elif encmode == 'mrr':
620 assert sv_mode is None
621 sv_mode = 0b00
622 mapreduce = True
623 reverse_gear = True
624 # map-reduce mode
625 elif encmode == 'mr':
626 assert sv_mode is None
627 sv_mode = 0b00
628 mapreduce = True
629 elif encmode == 'crm': # CR on map-reduce
630 assert sv_mode is None
631 sv_mode = 0b00
632 mapreduce_crm = True
633 elif encmode == 'vli':
634 assert failfirst is not False, "VLi only allowed in failfirst"
635 vli = True
636 elif encmode == 'sea':
637 assert is_ldst_idx
638 sea = True
639 assert failfirst is False, "cannot use ffirst+signed-address"
640 elif is_bc:
641 if encmode == 'all':
642 svp64_rm.branch.ALL = 1
643 elif encmode == 'snz':
644 svp64_rm.branch.sz = 1
645 svp64_rm.branch.SNZ = 1
646 elif encmode == 'sl':
647 svp64_rm.branch.SL = 1
648 elif encmode == 'slu':
649 svp64_rm.branch.SLu = 1
650 elif encmode == 'lru':
651 svp64_rm.branch.LRu = 1
652 elif encmode == 'vs':
653 svp64_rm.branch.VLS = 1
654 elif encmode == 'vsi':
655 svp64_rm.branch.VLS = 1
656 svp64_rm.branch.vls.VLi = 1
657 elif encmode == 'vsb':
658 svp64_rm.branch.VLS = 1
659 svp64_rm.branch.vls.VSb = 1
660 elif encmode == 'vsbi':
661 svp64_rm.branch.VLS = 1
662 svp64_rm.branch.vls.VSb = 1
663 svp64_rm.branch.vls.VLi = 1
664 elif encmode == 'ctr':
665 svp64_rm.branch.CTR = 1
666 elif encmode == 'cti':
667 svp64_rm.branch.CTR = 1
668 svp64_rm.branch.ctr.CTi = 1
669 else:
670 raise AssertionError("unknown encmode %s" % encmode)
671 else:
672 raise AssertionError("unknown encmode %s" % encmode)
673
674 # post-inc only available on ld-with-update
675 if ldst_postinc:
676 assert "u" in opcode, "/pi only available on ld/st-update"
677
678 # sanity check if dz/zz used in branch-mode
679 if is_bc and dst_zero:
680 raise AssertionError("dz/zz not supported in branch, use 'sz'")
681
682 # check sea *after* all qualifiers are evaluated
683 if sea:
684 assert sv_mode in (None, 0b00, 0b01)
685
686 if ptype == '2P':
687 # since m=xx takes precedence (overrides) sm=xx and dm=xx,
688 # treat them as mutually exclusive
689 if mask_m_specified:
690 assert not has_smask,\
691 "cannot have both source-mask and predicate mask"
692 assert not has_pmask,\
693 "cannot have both dest-mask and predicate mask"
694 # since the default is INT predication (ALWAYS), if you
695 # specify one CR mask, you must specify both, to avoid
696 # mixing INT and CR reg types
697 if has_pmask and pmmode == 1:
698 assert has_smask, \
699 "need explicit source-mask in CR twin predication"
700 if has_smask and smmode == 1:
701 assert has_pmask, \
702 "need explicit dest-mask in CR twin predication"
703 # sanity-check that 2Pred mask is same mode
704 if has_pmask and has_smask:
705 assert smmode == pmmode, \
706 "predicate masks %s and %s must be same reg type" % \
707 (pme, sme)
708
709 # sanity-check that twin-predication mask only specified in 2P mode
710 if ptype == '1P':
711 assert not has_smask, \
712 "source-mask can only be specified on Twin-predicate ops"
713 assert not has_pmask, \
714 "dest-mask can only be specified on Twin-predicate ops"
715
716 # construct the mode field, doing sanity-checking along the way
717 if src_zero:
718 assert has_smask or mask_m_specified, \
719 "src zeroing requires a source predicate"
720 if dst_zero:
721 assert has_pmask or mask_m_specified, \
722 "dest zeroing requires a dest predicate"
723
724 # okaaay, so there are 4 different modes, here, which will be
725 # partly-merged-in: is_ldst is merged in with "normal", but
726 # is_bc is so different it's done separately. likewise is_cr
727 # (when it is done). here are the maps:
728
729 # for "normal" arithmetic: https://libre-soc.org/openpower/sv/normal/
730 """
731 | 0-1 | 2 | 3 4 | description |
732 | --- | --- |---------|-------------------------- |
733 | 00 | 0 | dz sz | simple mode |
734 | 00 | 1 | 0 RG | scalar reduce mode (mapreduce) |
735 | 01 | inv | CR-bit | Rc=1: ffirst CR sel |
736 | 01 | inv | VLi RC1 | Rc=0: ffirst z/nonz |
737 | 10 | N | dz sz | sat mode: N=0/1 u/s |
738 | 11 | inv | CR-bit | Rc=1: pred-result CR sel |
739 | 11 | inv | zz RC1 | Rc=0: pred-result z/nonz |
740 """
741
742 # https://libre-soc.org/openpower/sv/ldst/
743 # for LD/ST-immediate:
744 """
745 | 0-1 | 2 | 3 4 | description |
746 | --- | --- |---------|--------------------------- |
747 | 00 | 0 | zz els | normal mode |
748 | 00 | 1 | pi lf | post-inc, LD-fault-first |
749 | 01 | inv | CR-bit | Rc=1: ffirst CR sel |
750 | 01 | inv | els RC1 | Rc=0: ffirst z/nonz |
751 | 10 | N | zz els | sat mode: N=0/1 u/s |
752 | 11 | inv | CR-bit | Rc=1: pred-result CR sel |
753 | 11 | inv | els RC1 | Rc=0: pred-result z/nonz |
754 """
755
756 # for LD/ST-indexed (RA+RB):
757 """
758 | 0-1 | 2 | 3 4 | description |
759 | --- | --- |---------|----------------------------- |
760 | 00 | SEA | dz sz | normal mode |
761 | 01 | SEA | dz sz | strided (scalar only source) |
762 | 10 | N | dz sz | sat mode: N=0/1 u/s |
763 | 11 | inv | CR-bit | Rc=1: pred-result CR sel |
764 | 11 | inv | dz RC1 | Rc=0: pred-result z/nonz |
765 """
766
767 # and leaving out branches and cr_ops for now because they're
768 # under development
769 """ TODO branches and cr_ops
770 """
771
772 if is_bc:
773 sv_mode = int(svp64_rm.mode[0, 1])
774 if src_zero:
775 svp64_rm.branch.sz = 1
776
777 else:
778 ######################################
779 # "element-strided" mode, ldst_idx
780 if sv_mode == 0b01 and is_ldst_idx:
781 mode |= src_zero << SVP64MODE.SZ # predicate zeroing
782 mode |= dst_zero << SVP64MODE.DZ # predicate zeroing
783 mode |= sea << SVP64MODE.SEA # el-strided
784
785 ######################################
786 # "normal" mode
787 elif sv_mode is None:
788 mode |= src_zero << SVP64MODE.SZ # predicate zeroing
789 mode |= dst_zero << SVP64MODE.DZ # predicate zeroing
790 if is_ldst:
791 # TODO: for now, LD/ST-indexed is ignored.
792 mode |= ldst_elstride << SVP64MODE.ELS_NORMAL # el-strided
793 else:
794 # TODO, reduce and subvector mode
795 # 00 1 dz CRM reduce mode (mapreduce), SUBVL=1
796 # 00 1 SVM CRM subvector reduce mode, SUBVL>1
797 pass
798 sv_mode = 0b00
799
800 ######################################
801 # ldst-immediate "post" (and "load-fault-first" modes)
802 elif sv_mode == 0b00 and ldst_postinc == 1: # (or ldst_ld_ffirst)
803 mode |= (0b1 << SVP64MODE.LDI_POST) # sets bit 2
804 mode |= (ldst_postinc << SVP64MODE.LDI_PI) # sets post-inc
805
806 ######################################
807 # "mapreduce" modes
808 elif sv_mode == 0b00:
809 mode |= (0b1 << SVP64MODE.REDUCE) # sets mapreduce
810 assert dst_zero == 0, "dest-zero not allowed in mapreduce mode"
811 if reverse_gear:
812 mode |= (0b1 << SVP64MODE.RG) # sets Reverse-gear mode
813 if mapreduce_crm:
814 mode |= (0b1 << SVP64MODE.CRM) # sets CRM mode
815 assert rc_mode, "CRM only allowed when Rc=1"
816 # bit of weird encoding to jam zero-pred or SVM mode in.
817 # SVM mode can be enabled only when SUBVL=2/3/4 (vec2/3/4)
818 if subvl == 0:
819 mode |= dst_zero << SVP64MODE.DZ # predicate zeroing
820
821 ######################################
822 # "failfirst" modes
823 elif failfirst is not False and not is_cr: # sv_mode == 0b01:
824 assert src_zero == 0, "dest-zero not allowed in failfirst mode"
825 if failfirst == 'RC1':
826 mode |= (0b1 << SVP64MODE.RC1) # sets RC1 mode
827 mode |= (dst_zero << SVP64MODE.DZ) # predicate dst-zeroing
828 assert rc_mode == False, "ffirst RC1 only ok when Rc=0"
829 elif failfirst == '~RC1':
830 mode |= (0b1 << SVP64MODE.RC1) # sets RC1 mode
831 mode |= (dst_zero << SVP64MODE.DZ) # predicate dst-zeroing
832 mode |= (0b1 << SVP64MODE.INV) # ... with inversion
833 assert rc_mode == False, "ffirst RC1 only ok when Rc=0"
834 else:
835 assert dst_zero == 0, "dst-zero not allowed in ffirst BO"
836 assert rc_mode, "ffirst BO only possible when Rc=1"
837 mode |= (failfirst << SVP64MODE.BO_LSB) # set BO
838
839 # (crops is really different)
840 elif failfirst is not False and is_cr:
841 if failfirst in ['RC1', '~RC1']:
842 mode |= (src_zero << SVP64MODE.SZ) # predicate src-zeroing
843 mode |= (dst_zero << SVP64MODE.DZ) # predicate dst-zeroing
844 if failfirst == '~RC1':
845 mode |= (0b1 << SVP64MODE.INV) # ... with inversion
846 else:
847 assert dst_zero == src_zero, "dz must equal sz in ffirst BO"
848 mode |= (failfirst << SVP64MODE.BO_LSB) # set BO
849 svp64_rm.cr_op.zz = dst_zero
850 if vli:
851 sv_mode |= 1 # set VLI in LSB of 2-bit mode
852 #svp64_rm.cr_op.vli = 1
853
854 ######################################
855 # "saturation" modes
856 elif sv_mode == 0b10:
857 mode |= src_zero << SVP64MODE.SZ # predicate zeroing
858 mode |= dst_zero << SVP64MODE.DZ # predicate zeroing
859 mode |= (saturation << SVP64MODE.N) # signed/us saturation
860
861 ######################################
862 # "predicate-result" modes. err... code-duplication from ffirst
863 elif sv_mode == 0b11:
864 assert src_zero == 0, "dest-zero not allowed in predresult mode"
865 if predresult == 'RC1':
866 mode |= (0b1 << SVP64MODE.RC1) # sets RC1 mode
867 mode |= (dst_zero << SVP64MODE.DZ) # predicate dst-zeroing
868 assert rc_mode == False, "pr-mode RC1 only ok when Rc=0"
869 elif predresult == '~RC1':
870 mode |= (0b1 << SVP64MODE.RC1) # sets RC1 mode
871 mode |= (dst_zero << SVP64MODE.DZ) # predicate dst-zeroing
872 mode |= (0b1 << SVP64MODE.INV) # ... with inversion
873 assert rc_mode == False, "pr-mode RC1 only ok when Rc=0"
874 else:
875 assert dst_zero == 0, "dst-zero not allowed in pr-mode BO"
876 assert rc_mode, "pr-mode BO only possible when Rc=1"
877 mode |= (predresult << SVP64MODE.BO_LSB) # set BO
878
879 # whewww.... modes all done :)
880 # now put into svp64_rm, but respect MSB0 order
881 if sv_mode & 1:
882 mode |= (0b1 << SVP64MODE.MOD2_LSB)
883 if sv_mode & 2:
884 mode |= (0b1 << SVP64MODE.MOD2_MSB)
885
886 if sea:
887 mode |= (0b1 << SVP64MODE.SEA)
888
889 # this is a mess. really look forward to replacing it with Insn DB
890 if not is_bc:
891 svp64_rm.mode = mode # mode: bits 19-23
892 if vli and not is_cr:
893 svp64_rm.normal.ffrc0.VLi = 1
894
895 # put in predicate masks into svp64_rm
896 if ptype == '2P':
897 svp64_rm.smask = smask # source pred: bits 16-18
898
899 # put in elwidths unless cr
900 if not is_cr:
901 svp64_rm.ewsrc = srcwid # srcwid: bits 6-7
902 svp64_rm.elwidth = destwid # destwid: bits 4-5
903
904 svp64_rm.mmode = mmode # mask mode: bit 0
905 svp64_rm.mask = pmask # 1-pred: bits 1-3
906 svp64_rm.subvl = subvl # and subvl: bits 8-9
907
908 # nice debug printout. (and now for something completely different)
909 # https://youtu.be/u0WOIwlXE9g?t=146
910 svp64_rm_value = int(svp64_rm)
911 log("svp64_rm", hex(svp64_rm_value), bin(svp64_rm_value))
912 log(" mmode 0 :", bin(mmode))
913 log(" pmask 1-3 :", bin(pmask))
914 log(" dstwid 4-5 :", bin(destwid))
915 log(" srcwid 6-7 :", bin(srcwid))
916 log(" subvl 8-9 :", bin(subvl))
917 log(" mode 19-23:", bin(svp64_rm.mode))
918 offs = 2 if etype == 'EXTRA2' else 3 # 2 or 3 bits
919 for idx, sv_extra in extras.items():
920 if idx is None:
921 continue
922 if idx[0] == 'imm':
923 continue
924 srcdest, idx, duplicate = idx
925 start = (10+idx*offs)
926 end = start + offs-1
927 log(" extra%d %2d-%2d:" % (idx, start, end),
928 bin(sv_extra))
929 if ptype == '2P':
930 log(" smask 16-17:", bin(smask))
931 log()
932
933 # update prefix PO and ID (aka PID)
934 svp64_prefix.PO = 0x1
935 svp64_prefix.id = 0b11
936
937 # fiinally yield the svp64 prefix and the thingy. v3.0b opcode
938 rc = '.' if rc_mode else ''
939 yield ".long 0x%08x" % int(svp64_prefix)
940 log(v30b_op, v30b_newfields)
941
942 v30b_op_rc = v30b_op
943 if not v30b_op.endswith('.'):
944 v30b_op_rc += rc
945
946 record = None
947 if os.environ.get("INSNDB"):
948 record = DB[opcode]
949 if record is not None:
950 insn = WordInstruction.assemble(db=DB,
951 entry=opcode, arguments=fields)
952 yield from insn.disassemble(db=DB, style=Style.LEGACY)
953 else:
954 if not v30b_op.endswith('.'):
955 v30b_op += rc
956 yield "%s %s" % (v30b_op, ", ".join(v30b_newfields))
957 for (name, span) in svp64_insn.traverse("SVP64"):
958 value = svp64_insn.storage[span]
959 log(name, f"{value.value:0{value.bits}b}", span)
960 log("new v3.0B fields", v30b_op, v30b_newfields)
961
962 def translate(self, lst):
963 for insn in lst:
964 yield from self.translate_one(insn)
965
966
967 def macro_subst(macros, txt):
968 again = True
969 log("subst", txt, macros)
970 while again:
971 again = False
972 for macro, value in macros.items():
973 if macro == txt:
974 again = True
975 replaced = txt.replace(macro, value)
976 log("macro", txt, "replaced", replaced, macro, value)
977 txt = replaced
978 continue
979 toreplace = '%s.s' % macro
980 if toreplace == txt:
981 again = True
982 replaced = txt.replace(toreplace, "%s.s" % value)
983 log("macro", txt, "replaced", replaced, toreplace, value)
984 txt = replaced
985 continue
986 toreplace = '%s.v' % macro
987 if toreplace == txt:
988 again = True
989 replaced = txt.replace(toreplace, "%s.v" % value)
990 log("macro", txt, "replaced", replaced, toreplace, value)
991 txt = replaced
992 continue
993 toreplace = '*%s' % macro
994 if toreplace in txt:
995 again = True
996 replaced = txt.replace(toreplace, '*%s' % value)
997 log("macro", txt, "replaced", replaced, toreplace, value)
998 txt = replaced
999 continue
1000 toreplace = '(%s)' % macro
1001 if toreplace in txt:
1002 again = True
1003 replaced = txt.replace(toreplace, '(%s)' % value)
1004 log("macro", txt, "replaced", replaced, toreplace, value)
1005 txt = replaced
1006 continue
1007 log(" processed", txt)
1008 return txt
1009
1010
1011 def get_ws(line):
1012 # find whitespace
1013 ws = ''
1014 while line:
1015 if not line[0].isspace():
1016 break
1017 ws += line[0]
1018 line = line[1:]
1019 return ws, line
1020
1021
1022 def asm_process():
1023 # get an input file and an output file
1024 args = sys.argv[1:]
1025 if len(args) == 0:
1026 infile = sys.stdin
1027 outfile = sys.stdout
1028 # read the whole lot in advance in case of in-place
1029 lines = list(infile.readlines())
1030 elif len(args) != 2:
1031 print("pysvp64asm [infile | -] [outfile | -]", file=sys.stderr)
1032 exit(0)
1033 else:
1034 if args[0] == '--':
1035 infile = sys.stdin
1036 else:
1037 infile = open(args[0], "r")
1038 # read the whole lot in advance in case of in-place overwrite
1039 lines = list(infile.readlines())
1040
1041 if args[1] == '--':
1042 outfile = sys.stdout
1043 else:
1044 outfile = open(args[1], "w")
1045
1046 # read the line, look for custom insn, process it
1047 macros = {} # macros which start ".set"
1048 isa = SVP64Asm([])
1049 for line in lines:
1050 op = line.split("#")[0].strip()
1051 # identify macros
1052 if op.startswith(".set"):
1053 macro = op[4:].split(",")
1054 (macro, value) = map(str.strip, macro)
1055 macros[macro] = value
1056
1057 if not op or op.startswith("#"):
1058 outfile.write(line)
1059 continue
1060 (ws, line) = get_ws(line)
1061 lst = isa.translate_one(op, macros)
1062 lst = '; '.join(lst)
1063 outfile.write("%s%s # %s\n" % (ws, lst, op))
1064
1065
1066 if __name__ == '__main__':
1067 lst = ['slw 3, 1, 4',
1068 'extsw 5, 3',
1069 'sv.extsw 5, 3',
1070 'sv.cmpi 5, 1, 3, 2',
1071 'sv.setb 5, 31',
1072 'sv.isel 64.v, 3, 2, 65.v',
1073 'sv.setb/dm=r3/sm=1<<r3 5, 31',
1074 'sv.setb/m=r3 5, 31',
1075 'sv.setb/vec2 5, 31',
1076 'sv.setb/sw=8/ew=16 5, 31',
1077 'sv.extsw./ff=eq 5, 31',
1078 'sv.extsw./satu/sz/dz/sm=r3/dm=r3 5, 31',
1079 'sv.extsw./pr=eq 5.v, 31',
1080 'sv.add. 5.v, 2.v, 1.v',
1081 'sv.add./m=r3 5.v, 2.v, 1.v',
1082 ]
1083 lst += [
1084 'sv.stw 5.v, 4(1.v)',
1085 'sv.ld 5.v, 4(1.v)',
1086 'setvl. 2, 3, 4, 0, 1, 1',
1087 'sv.setvl. 2, 3, 4, 0, 1, 1',
1088 ]
1089 lst = [
1090 "sv.stfsu 0.v, 16(4.v)",
1091 ]
1092 lst = [
1093 "sv.stfsu/els 0.v, 16(4)",
1094 ]
1095 lst = [
1096 'sv.add./mr 5.v, 2.v, 1.v',
1097 ]
1098 macros = {'win2': '50', 'win': '60'}
1099 lst = [
1100 'sv.addi win2.v, win.v, -1',
1101 'sv.add./mrr 5.v, 2.v, 1.v',
1102 #'sv.lhzsh 5.v, 11(9.v), 15',
1103 #'sv.lwzsh 5.v, 11(9.v), 15',
1104 'sv.ffmadds 6.v, 2.v, 4.v, 6.v',
1105 ]
1106 lst = [
1107 #'sv.fmadds 0.v, 8.v, 16.v, 4.v',
1108 #'sv.ffadds 0.v, 8.v, 4.v',
1109 'svremap 11, 0, 1, 2, 3, 2, 1',
1110 'svshape 8, 1, 1, 1, 0',
1111 'svshape 8, 1, 1, 1, 1',
1112 ]
1113 lst = [
1114 #'sv.lfssh 4.v, 11(8.v), 15',
1115 #'sv.lwzsh 4.v, 11(8.v), 15',
1116 #'sv.svstep. 2.v, 4, 0',
1117 #'sv.fcfids. 48.v, 64.v',
1118 'sv.fcoss. 80.v, 0.v',
1119 'sv.fcoss. 20.v, 0.v',
1120 ]
1121 lst = [
1122 'sv.bc/all 3,12,192',
1123 'sv.bclr/vsbi 3,81.v,192',
1124 'sv.ld 5.v, 4(1.v)',
1125 'sv.svstep. 2.v, 4, 0',
1126 ]
1127 lst = [
1128 'maxs 3,12,5',
1129 'maxs. 3,12,5',
1130 'avgadd 3,12,5',
1131 'absdu 3,12,5',
1132 'absds 3,12,5',
1133 'absdacu 3,12,5',
1134 'absdacs 3,12,5',
1135 'cprop 3,12,5',
1136 'svindex 0,0,1,0,0,0,0',
1137 ]
1138 lst = [
1139 'sv.svstep./m=r3 2.v, 4, 0',
1140 'ternlogi 0,0,0,0x5',
1141 'fmvis 5,65535',
1142 'fmvis 5,1',
1143 'fmvis 5,2',
1144 'fmvis 5,4',
1145 'fmvis 5,8',
1146 'fmvis 5,16',
1147 'fmvis 5,32',
1148 'fmvis 5,64',
1149 'fmvis 5,32768',
1150 ]
1151 lst = [
1152 'sv.andi. *80, *80, 1',
1153 'sv.ffmadds. 6.v, 2.v, 4.v, 6.v', # incorrectly inserted 32-bit op
1154 'sv.ffmadds 6.v, 2.v, 4.v, 6.v', # correctly converted to .long
1155 'svshape2 8, 1, 31, 7, 1, 1',
1156 'sv.ld 5.v, 4(1.v)',
1157 'sv.stw 5.v, 4(1.v)',
1158 'sv.bc/all 3,12,192',
1159 'pcdec. 0,0,0,0',
1160 ]
1161 lst = [
1162 #"sv.cmp/ff=gt *0,*1,*2,0",
1163 "dsld 5,4,5,3",
1164
1165 ]
1166 isa = SVP64Asm(lst, macros=macros)
1167 log("list:\n", "\n\t".join(list(isa)))
1168 # running svp64.py is designed to test hard-coded lists
1169 # (above) - which strictly speaking should all be unit tests.
1170 # if you need to actually do assembler translation at the
1171 # commandline use "pysvp64asm" - see setup.py
1172 # XXX NO. asm_process()