d3f51cd5f3a2520c2b7fb5fdec51bfab4cbd26a3
1 # Reads OpenPOWER ISA pages from http://libre-soc.org/openpower/isa
2 """OpenPOWER ISA page parser
4 returns an OrderedDict of namedtuple "Ops" containing details of all
5 instructions listed in markdown files.
7 format must be strictly as follows (no optional sections) including whitespace:
15 if L = 0 then a <- [0]*32 || (RA)[32:63]
16 b <- [0]*32 || (RB)[32:63]
19 if a <u b then c <- 0b100
20 else if a >u b then c <- 0b010
22 CR[4*BF+32:4*BF+35] <- c || XER[SO]
24 Special Registers Altered:
35 * instruction registerlist
36 * instruction registerlist
38 4-space-indented pseudo-code
39 4-space-indented pseudo-code
41 Special Registers Altered:
42 4-space-indented register description
44 blank(s) (optional for convenience at end-of-page)
47 from openpower
.util
import log
48 from openpower
.decoder
.orderedset
import OrderedSet
49 from collections
import namedtuple
, OrderedDict
54 opfields
= ("desc", "form", "opcode", "regs", "pcode", "sregs", "page",
56 Ops
= namedtuple("Ops", opfields
)
60 fdir
= os
.path
.abspath(os
.path
.dirname(__file__
))
61 fdir
= os
.path
.split(fdir
)[0]
62 fdir
= os
.path
.split(fdir
)[0]
63 fdir
= os
.path
.split(fdir
)[0]
64 fdir
= os
.path
.split(fdir
)[0]
66 return os
.path
.join(fdir
, "openpower", "isa")
69 pattern_opcode
= r
"[A-Za-z0-9_\.]+\.?"
70 pattern_dynamic
= r
"[A-Za-z0-9_]+(?:\([A-Za-z0-9_]+\))*"
71 pattern_static
= r
"[A-Za-z0-9]+\=[01]"
72 regex_opcode
= re
.compile(f
"^{pattern_opcode}$")
73 regex_dynamic
= re
.compile(f
"^{pattern_dynamic}(?:,{pattern_dynamic})*$")
74 regex_static
= re
.compile(f
"^\({pattern_static}(?:\s{pattern_static})*\)$")
77 def operands(opcode
, desc
):
80 desc
= desc
.replace("(", "")
81 desc
= desc
.replace(")", "")
82 desc
= desc
.replace(",", " ")
83 for operand
in desc
.split(" "):
84 operand
= operand
.strip()
88 def get_indented_lines(lines
):
91 l
= lines
.pop(0).rstrip()
94 assert l
.startswith(' '), ("4spcs not found in line %s" % l
)
95 l
= l
[4:] # lose 4 spaces
102 self
.instr
= OrderedDict()
106 for pth
in os
.listdir(os
.path
.join(get_isa_dir())):
108 print("examining", get_isa_dir(), pth
)
111 if not pth
.endswith(".mdwn"):
112 if not os
.path
.isdir(os
.path
.join(get_isa_dir(), pth
)):
113 log("warning, file not .mdwn, skipping", pth
)
117 # code which helped add in the keyword "Pseudo-code:" automatically
118 rewrite
= self
.read_file_for_rewrite(pth
)
119 name
= os
.path
.join("/tmp", pth
)
120 with
open(name
, "w") as f
:
121 f
.write('\n'.join(rewrite
) + '\n')
124 yield from self
.instr
.items()
126 def read_file_for_rewrite(self
, fname
):
127 pagename
= fname
.split('.')[0]
128 fname
= os
.path
.join(get_isa_dir(), fname
)
129 with
open(fname
) as f
:
130 lines
= f
.readlines()
133 l
= lines
.pop(0).rstrip() # get first line
138 # look for HTML comment, if starting, skip line.
139 # XXX this is braindead! it doesn't look for the end
140 # so please put ending of comments on one line:
141 # <!-- line 1 comment -->
142 # {some whitespace}<!-- line 2 comment -->
143 if l
.strip().startswith('<!--'):
144 # print ("skipping comment", l)
145 l
= lines
.pop(0).rstrip() # get first line
148 # Ignore blank lines before the first #
149 if len(l
.strip()) == 0:
153 assert l
.startswith('#'), ("# not found in line %s" % l
)
155 # whitespace expected
156 l
= lines
.pop(0).strip()
159 assert len(l
) == 0, ("blank line not found %s" % l
)
163 l
= lines
.pop(0).strip()
164 assert l
.endswith('-Form'), ("line with -Form expected %s" % l
)
167 # whitespace expected
168 l
= lines
.pop(0).strip()
169 assert len(l
) == 0, ("blank line not found %s" % l
)
172 # get list of opcodes
174 l
= lines
.pop(0).strip()
178 assert l
.startswith('*'), ("* not found in line %s" % l
)
180 rewrite
.append("Pseudo-code:")
184 l
= lines
.pop(0).rstrip()
185 if l
.strip().startswith('<!--'):
186 # print ("skipping comment", l)
187 l
= lines
.pop(0).rstrip() # get first line
192 assert l
.startswith(' '), ("4spcs not found in line %s" % l
)
194 # "Special Registers Altered" expected
195 l
= lines
.pop(0).rstrip()
196 assert l
.startswith("Special"), ("special not found %s" % l
)
199 # whitespace expected
200 l
= lines
.pop(0).strip()
201 assert len(l
) == 0, ("blank line not found %s" % l
)
206 l
= lines
.pop(0).rstrip()
210 assert l
.startswith(' '), ("4spcs not found in line %s" % l
)
212 # expect and drop whitespace
214 l
= lines
.pop(0).rstrip()
216 if len(l
) != 0 and not l
.strip().startswith('<!--'):
221 def read_file(self
, fname
):
222 pagename
= fname
.split('.')[0]
223 fname
= os
.path
.join(get_isa_dir(), fname
)
224 with
open(fname
) as f
:
225 lines
= f
.readlines()
227 # set up dict with current page name
228 d
= {'page': pagename
}
230 # line-by-line lexer/parser, quite straightforward: pops one
231 # line off the list and checks it. nothing complicated needed,
232 # all sections are mandatory so no need for a full LALR parser.
234 l
= lines
.pop(0).rstrip() # get first line
238 # look for HTML comment, if starting, skip line.
239 # XXX this is braindead! it doesn't look for the end
240 # so please put ending of comments on one line:
241 # <!-- line 1 comment -->
242 # <!-- line 2 comment -->
243 if l
.strip().startswith('<!--'):
244 # print ("skipping comment", l)
245 l
= lines
.pop(0).rstrip() # get next line
248 # Ignore blank lines before the first #
250 l
= lines
.pop(0).rstrip() # get next line
254 assert l
.startswith('#'), ("# not found in line '%s'" % l
)
255 d
['desc'] = l
[1:].strip()
257 # whitespace expected
258 l
= lines
.pop(0).strip()
261 assert len(l
) == 0, ("blank line not found %s" % l
)
264 l
= lines
.pop(0).strip()
265 assert l
.endswith('-Form'), ("line with -Form expected %s" % l
)
266 d
['form'] = l
.split('-')[0]
268 # whitespace expected
269 l
= lines
.pop(0).strip()
270 assert len(l
) == 0, ("blank line not found %s" % l
)
272 # get list of opcodes
275 l
= lines
.pop(0).strip()
278 assert l
.startswith('*'), ("* not found in line %s" % l
)
281 (opcode
, _
, rest
) = map(str.strip
, rest
.partition(" "))
282 if regex_opcode
.match(opcode
) is None:
283 raise IOError(repr(opcode
))
286 (dynamic
, _
, rest
) = map(str.strip
, rest
.partition(" "))
287 if regex_dynamic
.match(dynamic
) is None and dynamic
:
288 raise IOError(f
"{l!r}: {dynamic!r}")
290 opcode
.append(dynamic
.split(","))
293 if regex_static
.match(static
) is None and static
:
294 raise IOError(f
"{l!r}: {static!r}")
296 opcode
.extend(static
[1:-1].split(" "))
298 opcodes
.append(opcode
)
300 # "Pseudocode" expected
301 l
= lines
.pop(0).rstrip()
302 assert l
.startswith("Pseudo-code:"), ("pseudocode found %s" % l
)
304 # whitespace expected
305 l
= lines
.pop(0).strip()
308 assert len(l
) == 0, ("blank line not found %s" % l
)
310 extra_uninit_regs
= OrderedSet()
315 l
= lines
.pop(0).rstrip()
316 re_match
= re
.fullmatch(r
" *<!-- EXTRA_UNINIT_REGS:(.*)-->", l
)
318 for i
in re_match
[1].split(' '):
320 extra_uninit_regs
.add(i
)
323 if l
.strip().startswith('<!--'):
327 assert l
.startswith(' '), ("4spcs not found in line %s" % l
)
328 l
= l
[4:] # lose 4 spaces
331 d
['extra_uninit_regs'] = extra_uninit_regs
333 # "Special Registers Altered" expected
334 l
= lines
.pop(0).rstrip()
335 assert l
.startswith("Special"), ("special not found %s" % l
)
337 # whitespace expected
338 l
= lines
.pop(0).strip()
339 assert len(l
) == 0, ("blank line not found %s" % l
)
342 li
= get_indented_lines(lines
)
349 # expect and drop whitespace and comments
351 l
= lines
.pop(0).rstrip()
352 if len(l
) != 0 and not l
.strip().startswith('<!--'):
355 def add_op(self
, o
, d
):
356 opcode
, regs
= o
[0], o
[1:]
359 op
['opcode'] = opcode
360 self
.instr
[opcode
] = Ops(**op
)
362 # create list of instructions by form
364 fl
= self
.forms
.get(form
, [])
365 self
.forms
[form
] = fl
+ [opcode
]
367 # create list of instructions by page
369 pl
= self
.page
.get(page
, [])
370 self
.page
[page
] = pl
+ [opcode
]
372 def pprint_ops(self
):
373 for k
, v
in self
.instr
.items():
374 print("# %s %s" % (v
.opcode
, v
.desc
))
375 print("Form: %s Regs: %s" % (v
.form
, v
.regs
))
376 print('\n'.join(map(lambda x
: " %s" % x
, v
.pcode
)))
378 print('\n'.join(map(lambda x
: " %s" % x
, v
.sregs
)))
380 for k
, v
in isa
.forms
.items():
384 if __name__
== '__main__':
387 # example on how to access cmp regs:
388 print ("cmp regs:", isa
.instr
["cmp"].regs
)