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
.insndb
.core
import SVP64Instruction
32 from openpower
.insndb
.core
import Database
33 from openpower
.insndb
.core
import Style
34 from openpower
.insndb
.core
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
88 if record
is not None:
89 insn
= WordInstruction
.assemble(record
=record
, arguments
=fields
)
90 yield from insn
.disassemble(record
=record
, style
=Style
.LEGACY
)
93 # identify if is a svp64 mnemonic
94 if not opcode
.startswith('sv.'):
95 yield insn
# unaltered
97 opcode
= opcode
[3:] # strip leading "sv"
99 # start working on decoding the svp64 op: sv.basev30Bop/vec2/mode
100 opmodes
= opcode
.split("/") # split at "/"
101 v30b_op
= opmodes
.pop(0) # first is the v3.0B
104 if record
is not None:
105 insn
= SVP64Instruction
.assemble(record
=record
,
106 arguments
=fields
, specifiers
=opmodes
)
107 yield from insn
.disassemble(record
=record
, style
=Style
.LEGACY
)
110 raise AssemblerError(insn_no_comments
)
112 def translate(self
, lst
):
114 yield from self
.translate_one(insn
)
117 def macro_subst(macros
, txt
):
119 log("subst", txt
, macros
)
122 for macro
, value
in macros
.items():
125 replaced
= txt
.replace(macro
, value
)
126 log("macro", txt
, "replaced", replaced
, macro
, value
)
129 toreplace
= '%s.s' % macro
132 replaced
= txt
.replace(toreplace
, "%s.s" % value
)
133 log("macro", txt
, "replaced", replaced
, toreplace
, value
)
136 toreplace
= '%s.v' % macro
139 replaced
= txt
.replace(toreplace
, "%s.v" % value
)
140 log("macro", txt
, "replaced", replaced
, toreplace
, value
)
143 toreplace
= '*%s' % macro
146 replaced
= txt
.replace(toreplace
, '*%s' % value
)
147 log("macro", txt
, "replaced", replaced
, toreplace
, value
)
150 toreplace
= '(%s)' % macro
153 replaced
= txt
.replace(toreplace
, '(%s)' % value
)
154 log("macro", txt
, "replaced", replaced
, toreplace
, value
)
157 log(" processed", txt
)
165 if not line
[0].isspace():
173 # get an input file and an output file
178 # read the whole lot in advance in case of in-place
179 lines
= list(infile
.readlines())
181 print("pysvp64asm [infile | -] [outfile | -]", file=sys
.stderr
)
187 infile
= open(args
[0], "r")
188 # read the whole lot in advance in case of in-place overwrite
189 lines
= list(infile
.readlines())
194 outfile
= open(args
[1], "w")
196 # read the line, look for custom insn, process it
197 macros
= {} # macros which start ".set"
200 op
= line
.split("#")[0].strip()
202 if op
.startswith(".set"):
203 macro
= op
[4:].split(",")
204 (macro
, value
) = map(str.strip
, macro
)
205 macros
[macro
] = value
207 if not op
or op
.startswith("#"):
210 (ws
, line
) = get_ws(line
)
211 lst
= isa
.translate_one(op
, macros
)
213 outfile
.write("%s%s # %s\n" % (ws
, lst
, op
))
216 if __name__
== '__main__':
217 lst
= ['slw 3, 1, 4',
220 'sv.cmpi 5, 1, 3, 2',
222 'sv.isel 64.v, 3, 2, 65.v',
223 'sv.setb/dm=r3/sm=1<<r3 5, 31',
224 'sv.setb/m=r3 5, 31',
225 'sv.setb/vec2 5, 31',
226 'sv.setb/sw=8/ew=16 5, 31',
227 'sv.extsw./ff=eq 5, 31',
228 'sv.extsw./satu/sz/dz/sm=r3/dm=r3 5, 31',
229 'sv.add. 5.v, 2.v, 1.v',
230 'sv.add./m=r3 5.v, 2.v, 1.v',
233 'sv.stw 5.v, 4(1.v)',
235 'setvl. 2, 3, 4, 0, 1, 1',
236 'sv.setvl. 2, 3, 4, 0, 1, 1',
239 "sv.stfsu 0.v, 16(4.v)",
242 "sv.stfsu/els 0.v, 16(4)",
245 'sv.add./mr 5.v, 2.v, 1.v',
247 macros
= {'win2': '50', 'win': '60'}
249 'sv.addi win2.v, win.v, -1',
250 'sv.add./mrr 5.v, 2.v, 1.v',
251 #'sv.lhzsh 5.v, 11(9.v), 15',
252 #'sv.lwzsh 5.v, 11(9.v), 15',
253 'sv.ffmadds 6.v, 2.v, 4.v, 6.v',
256 #'sv.fmadds 0.v, 8.v, 16.v, 4.v',
257 #'sv.ffadds 0.v, 8.v, 4.v',
258 'svremap 11, 0, 1, 2, 3, 2, 1',
259 'svshape 8, 1, 1, 1, 0',
260 'svshape 8, 1, 1, 1, 1',
263 #'sv.lfssh 4.v, 11(8.v), 15',
264 #'sv.lwzsh 4.v, 11(8.v), 15',
265 #'sv.svstep. 2.v, 4, 0',
266 #'sv.fcfids. 48.v, 64.v',
267 'sv.fcoss. 80.v, 0.v',
268 'sv.fcoss. 20.v, 0.v',
271 'sv.bc/all 3,12,192',
272 'sv.bclr/vsbi 3,81.v,192',
274 'sv.svstep. 2.v, 4, 0',
285 'svindex 0,0,1,0,0,0,0',
288 'sv.svstep./m=r3 2.v, 4, 0',
289 'ternlogi 0,0,0,0x5',
301 'sv.andi. *80, *80, 1',
302 'sv.ffmadds. 6.v, 2.v, 4.v, 6.v', # incorrectly inserted 32-bit op
303 'sv.ffmadds 6.v, 2.v, 4.v, 6.v', # correctly converted to .long
304 'svshape2 8, 1, 31, 7, 1, 1',
306 'sv.stw 5.v, 4(1.v)',
307 'sv.bc/all 3,12,192',
311 #"sv.cmp/ff=gt *0,*1,*2,0",
315 isa
= SVP64Asm(lst
, macros
=macros
)
316 log("list:\n", "\n\t".join(list(isa
)))
317 # running svp64.py is designed to test hard-coded lists
318 # (above) - which strictly speaking should all be unit tests.
319 # if you need to actually do assembler translation at the
320 # commandline use "pysvp64asm" - see setup.py
321 # XXX NO. asm_process()