1 # SPDX-License-Identifier: LGPLv3+
2 # Copyright (C) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3 # Funded by NLnet http://nlnet.nl
5 """SVP64 OpenPOWER v3.0B assembly translator
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.
10 It is very simple and straightforward, the only weirdness being the
11 extraction of the register information and conversion to v3.0B numbering.
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
24 from collections
import OrderedDict
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
38 from openpower
.util
import log
41 DB
= Database(find_wiki_dir())
44 class AssemblerError(ValueError):
48 # decodes svp64 assembly listings and creates EXT001 svp64 prefixes
50 def __init__(self
, lst
, bigendian
=False, macros
=None):
55 self
.trans
= self
.translate(lst
)
56 self
.isa
= ISA() # reads the v3.0B pseudo-code markdown files
57 self
.svp64
= SVP64RM() # reads the svp64 Remap entries for registers
58 assert bigendian
== False, "error, bigendian not supported yet"
63 def translate_one(self
, insn
, macros
=None):
66 macros
.update(self
.macros
)
69 insn_no_comments
= insn
.partition('#')[0].strip()
70 if not insn_no_comments
:
73 # find first space, to get opcode
74 ls
= insn_no_comments
.split()
76 # now find opcode fields
77 fields
= ''.join(ls
[1:]).split(',')
78 mfields
= list(filter(bool, map(str.strip
, fields
)))
79 log("opcode, fields", ls
, opcode
, mfields
)
83 fields
.append(macro_subst(macros
, field
))
84 log("opcode, fields substed", ls
, opcode
, fields
)
86 # identify if it is a word instruction
89 if record
is not None:
90 insn
= WordInstruction
.assemble(db
=DB
,
91 entry
=opcode
, arguments
=fields
)
92 yield from insn
.disassemble(db
=DB
, style
=Style
.LEGACY
)
95 # identify if is a svp64 mnemonic
96 if not opcode
.startswith('sv.'):
97 yield insn
# unaltered
99 opcode
= opcode
[3:] # strip leading "sv"
101 # start working on decoding the svp64 op: sv.basev30Bop/vec2/mode
102 opmodes
= opcode
.split("/") # split at "/"
103 v30b_op_orig
= opmodes
.pop(0) # first is the v3.0B
104 # check instruction ends with dot
105 rc_mode
= v30b_op_orig
.endswith('.')
107 v30b_op
= v30b_op_orig
[:-1]
109 v30b_op
= v30b_op_orig
113 if record
is not None:
114 insn
= SVP64Instruction
.assemble(db
=DB
,
118 yield from insn
.disassemble(db
=DB
, style
=Style
.LEGACY
)
121 raise AssemblerError(insn_no_comments
)
123 def translate(self
, lst
):
125 yield from self
.translate_one(insn
)
128 def macro_subst(macros
, txt
):
130 log("subst", txt
, macros
)
133 for macro
, value
in macros
.items():
136 replaced
= txt
.replace(macro
, value
)
137 log("macro", txt
, "replaced", replaced
, macro
, value
)
140 toreplace
= '%s.s' % macro
143 replaced
= txt
.replace(toreplace
, "%s.s" % value
)
144 log("macro", txt
, "replaced", replaced
, toreplace
, value
)
147 toreplace
= '%s.v' % macro
150 replaced
= txt
.replace(toreplace
, "%s.v" % value
)
151 log("macro", txt
, "replaced", replaced
, toreplace
, value
)
154 toreplace
= '*%s' % macro
157 replaced
= txt
.replace(toreplace
, '*%s' % value
)
158 log("macro", txt
, "replaced", replaced
, toreplace
, value
)
161 toreplace
= '(%s)' % macro
164 replaced
= txt
.replace(toreplace
, '(%s)' % value
)
165 log("macro", txt
, "replaced", replaced
, toreplace
, value
)
168 log(" processed", txt
)
176 if not line
[0].isspace():
184 # get an input file and an output file
189 # read the whole lot in advance in case of in-place
190 lines
= list(infile
.readlines())
192 print("pysvp64asm [infile | -] [outfile | -]", file=sys
.stderr
)
198 infile
= open(args
[0], "r")
199 # read the whole lot in advance in case of in-place overwrite
200 lines
= list(infile
.readlines())
205 outfile
= open(args
[1], "w")
207 # read the line, look for custom insn, process it
208 macros
= {} # macros which start ".set"
211 op
= line
.split("#")[0].strip()
213 if op
.startswith(".set"):
214 macro
= op
[4:].split(",")
215 (macro
, value
) = map(str.strip
, macro
)
216 macros
[macro
] = value
218 if not op
or op
.startswith("#"):
221 (ws
, line
) = get_ws(line
)
222 lst
= isa
.translate_one(op
, macros
)
224 outfile
.write("%s%s # %s\n" % (ws
, lst
, op
))
227 if __name__
== '__main__':
228 lst
= ['slw 3, 1, 4',
231 'sv.cmpi 5, 1, 3, 2',
233 'sv.isel 64.v, 3, 2, 65.v',
234 'sv.setb/dm=r3/sm=1<<r3 5, 31',
235 'sv.setb/m=r3 5, 31',
236 'sv.setb/vec2 5, 31',
237 'sv.setb/sw=8/ew=16 5, 31',
238 'sv.extsw./ff=eq 5, 31',
239 'sv.extsw./satu/sz/dz/sm=r3/dm=r3 5, 31',
240 'sv.extsw./pr=eq 5.v, 31',
241 'sv.add. 5.v, 2.v, 1.v',
242 'sv.add./m=r3 5.v, 2.v, 1.v',
245 'sv.stw 5.v, 4(1.v)',
247 'setvl. 2, 3, 4, 0, 1, 1',
248 'sv.setvl. 2, 3, 4, 0, 1, 1',
251 "sv.stfsu 0.v, 16(4.v)",
254 "sv.stfsu/els 0.v, 16(4)",
257 'sv.add./mr 5.v, 2.v, 1.v',
259 macros
= {'win2': '50', 'win': '60'}
261 'sv.addi win2.v, win.v, -1',
262 'sv.add./mrr 5.v, 2.v, 1.v',
263 #'sv.lhzsh 5.v, 11(9.v), 15',
264 #'sv.lwzsh 5.v, 11(9.v), 15',
265 'sv.ffmadds 6.v, 2.v, 4.v, 6.v',
268 #'sv.fmadds 0.v, 8.v, 16.v, 4.v',
269 #'sv.ffadds 0.v, 8.v, 4.v',
270 'svremap 11, 0, 1, 2, 3, 2, 1',
271 'svshape 8, 1, 1, 1, 0',
272 'svshape 8, 1, 1, 1, 1',
275 #'sv.lfssh 4.v, 11(8.v), 15',
276 #'sv.lwzsh 4.v, 11(8.v), 15',
277 #'sv.svstep. 2.v, 4, 0',
278 #'sv.fcfids. 48.v, 64.v',
279 'sv.fcoss. 80.v, 0.v',
280 'sv.fcoss. 20.v, 0.v',
283 'sv.bc/all 3,12,192',
284 'sv.bclr/vsbi 3,81.v,192',
286 'sv.svstep. 2.v, 4, 0',
297 'svindex 0,0,1,0,0,0,0',
300 'sv.svstep./m=r3 2.v, 4, 0',
301 'ternlogi 0,0,0,0x5',
313 'sv.andi. *80, *80, 1',
314 'sv.ffmadds. 6.v, 2.v, 4.v, 6.v', # incorrectly inserted 32-bit op
315 'sv.ffmadds 6.v, 2.v, 4.v, 6.v', # correctly converted to .long
316 'svshape2 8, 1, 31, 7, 1, 1',
318 'sv.stw 5.v, 4(1.v)',
319 'sv.bc/all 3,12,192',
323 #"sv.cmp/ff=gt *0,*1,*2,0",
327 isa
= SVP64Asm(lst
, macros
=macros
)
328 log("list:\n", "\n\t".join(list(isa
)))
329 # running svp64.py is designed to test hard-coded lists
330 # (above) - which strictly speaking should all be unit tests.
331 # if you need to actually do assembler translation at the
332 # commandline use "pysvp64asm" - see setup.py
333 # XXX NO. asm_process()