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",
55 "extra_uninit_regs", "english")
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 def operands(opcode
, desc
):
72 desc
= desc
.replace("(", "")
73 desc
= desc
.replace(")", "")
74 desc
= desc
.replace(",", " ")
75 for operand
in desc
.split(" "):
76 operand
= operand
.strip()
81 def get_indented_lines(lines
, pagename
, expect
=None):
82 """gets a set of indented lines, plus a blank line as termination.
83 blank lines are permitted inside the block, but this requires a lookahead
84 at the next line to see if the next section is coming up (expected).
85 but... Description is currently *optional* sigh therefore it is
86 necessary to look for *either* "Special" *or* "Description" sigh
87 TODO: simplify once all Descriptions are added
91 l
= lines
.pop(0).rstrip()
93 if l
.strip().startswith('<!--'):
97 # if not at the end of the block, add the extra 4 spaces
98 if expect
and len(lines
) >= 1:
101 if lines
[0].startswith(e
):
109 assert l
.startswith(' '), ("4spcs not found in line %s" % l
+
110 "page %s" % pagename
)
111 l
= l
[4:] # lose 4 spaces
118 self
.instr
= OrderedDict()
122 for pth
in os
.listdir(os
.path
.join(get_isa_dir())):
124 print("examining", get_isa_dir(), pth
)
127 if not pth
.endswith(".mdwn"):
128 if not os
.path
.isdir(os
.path
.join(get_isa_dir(), pth
)):
129 log("warning, file not .mdwn, skipping", pth
)
133 # code which helped add in the keyword "Pseudo-code:" automatically
134 rewrite
= self
.read_file_for_rewrite(pth
)
135 name
= os
.path
.join("/tmp", pth
)
136 with
open(name
, "w") as f
:
137 f
.write('\n'.join(rewrite
) + '\n')
140 yield from self
.instr
.items()
142 def read_file_for_rewrite(self
, fname
):
143 pagename
= fname
.split('.')[0]
144 fname
= os
.path
.join(get_isa_dir(), fname
)
145 with
open(fname
) as f
:
146 lines
= f
.readlines()
149 l
= lines
.pop(0).rstrip() # get first line
154 # look for HTML comment, if starting, skip line.
155 # XXX this is braindead! it doesn't look for the end
156 # so please put ending of comments on one line:
157 # <!-- line 1 comment -->
158 # {some whitespace}<!-- line 2 comment -->
159 if l
.strip().startswith('<!--'):
160 # print ("skipping comment", l)
161 l
= lines
.pop(0).rstrip() # get first line
164 # Ignore blank lines before the first #
165 if len(l
.strip()) == 0:
169 assert l
.startswith('#'), ("# not found in line %s" % l
)
171 # whitespace expected
172 l
= lines
.pop(0).strip()
175 assert len(l
) == 0, ("blank line not found %s" % l
)
179 l
= lines
.pop(0).strip()
180 assert l
.endswith('-Form'), ("line with -Form expected %s" % l
)
183 # whitespace expected
184 l
= lines
.pop(0).strip()
185 assert len(l
) == 0, ("blank line not found %s" % l
)
188 # get list of opcodes
190 l
= lines
.pop(0).strip()
194 assert l
.startswith('*'), ("* not found in line %s" % l
)
196 rewrite
.append("Pseudo-code:")
200 l
= lines
.pop(0).rstrip()
201 if l
.strip().startswith('<!--'):
202 # print ("skipping comment", l)
203 l
= lines
.pop(0).rstrip() # get first line
208 assert l
.startswith(' '), ("4spcs not found in line %s" % l
)
210 # "Special Registers Altered" expected
211 l
= lines
.pop(0).rstrip()
212 assert l
.startswith("Special"), ("special not found %s" % l
)
215 # whitespace expected
216 l
= lines
.pop(0).strip()
217 assert len(l
) == 0, ("blank line not found %s" % l
)
222 l
= lines
.pop(0).rstrip()
226 assert l
.startswith(' '), ("4spcs not found in line %s" % l
)
228 # expect and drop whitespace
230 l
= lines
.pop(0).rstrip()
232 if len(l
) != 0 and not l
.strip().startswith('<!--'):
237 def read_file(self
, fname
):
238 pagename
= fname
.split('.')[0]
239 fname
= os
.path
.join(get_isa_dir(), fname
)
240 with
open(fname
) as f
:
241 lines
= f
.readlines()
243 # set up dict with current page name and blank english description
244 d
= {'page': pagename
, 'english': None}
246 # line-by-line lexer/parser, quite straightforward: pops one
247 # line off the list and checks it. nothing complicated needed,
248 # all sections are mandatory so no need for a full LALR parser.
250 l
= lines
.pop(0).rstrip() # get first line
254 # look for HTML comment, if starting, skip line.
255 # XXX this is braindead! it doesn't look for the end
256 # so please put ending of comments on one line:
257 # <!-- line 1 comment -->
258 # <!-- line 2 comment -->
259 if l
.strip().startswith('<!--'):
260 # print ("skipping comment", l)
261 l
= lines
.pop(0).rstrip() # get next line
264 # Ignore blank lines before the first #
266 l
= lines
.pop(0).rstrip() # get next line
270 assert l
.startswith('#'), ("# not found in line '%s'" % l
)
271 d
['desc'] = l
[1:].strip()
273 # whitespace expected
274 l
= lines
.pop(0).strip()
277 assert len(l
) == 0, ("blank line not found %s" % l
)
280 l
= lines
.pop(0).strip()
281 assert l
.endswith('-Form'), ("line with -Form expected %s" % l
)
282 d
['form'] = l
.split('-')[0]
284 # whitespace expected
285 l
= lines
.pop(0).strip()
286 assert len(l
) == 0, ("blank line not found %s" % l
)
288 # get list of opcodes
291 l
= lines
.pop(0).strip()
294 assert l
.startswith('*'), ("* not found in line %s" % l
)
297 (opcode
, _
, rest
) = map(str.strip
, rest
.partition(" "))
300 (dynamic
, _
, rest
) = map(str.strip
, rest
.partition(" "))
302 opcode
.append(dynamic
.split(","))
306 opcode
.extend(static
[1:-1].split(" "))
308 opcodes
.append(opcode
)
310 # "Pseudocode" expected
311 l
= lines
.pop(0).rstrip()
312 assert l
.startswith("Pseudo-code:"), ("pseudocode found %s" % l
)
314 # whitespace expected
315 l
= lines
.pop(0).strip()
318 assert len(l
) == 0, ("blank line not found %s" % l
)
320 extra_uninit_regs
= OrderedSet()
324 d
['pcode'] = get_indented_lines(lines
, pagename
,
325 ['Description', 'Special'])
326 d
['extra_uninit_regs'] = extra_uninit_regs
328 # check if (temporarily) optional "Description" exists
329 if lines
[0].startswith("Description:"):
330 l
= lines
.pop(0).rstrip() # skip "Description"
331 d
['english'] = get_indented_lines(lines
, pagename
,
334 # "Special Registers Altered" expected
335 l
= lines
.pop(0).rstrip()
336 assert l
.startswith("Special"), \
337 ("special not found %s (%s)" % (l
, d
['desc']))
339 # whitespace expected
340 l
= lines
.pop(0).strip()
341 assert len(l
) == 0, ("blank line not found %s" % l
)
344 li
= get_indented_lines(lines
, pagename
)
351 # expect and drop whitespace and comments
353 l
= lines
.pop(0).rstrip()
354 if len(l
) != 0 and not l
.strip().startswith('<!--'):
357 def add_op(self
, o
, d
):
358 opcode
, regs
= o
[0], o
[1:]
361 op
['opcode'] = opcode
362 if opcode
in self
.instr
:
363 raise IOError("Duplicate instruction: %s\nin both %s and %s" % (
364 opcode
, self
.instr
[opcode
].page
, op
['page']))
365 self
.instr
[opcode
] = Ops(**op
)
367 # create list of instructions by form
369 fl
= self
.forms
.get(form
, [])
370 self
.forms
[form
] = fl
+ [opcode
]
372 # create list of instructions by page
374 pl
= self
.page
.get(page
, [])
375 self
.page
[page
] = pl
+ [opcode
]
377 def pprint_ops(self
):
378 for k
, v
in self
.instr
.items():
379 print("# %s %s" % (v
.opcode
, v
.desc
))
380 print("Form: %s Regs: %s" % (v
.form
, v
.regs
))
381 print('\n'.join(map(lambda x
: " %s" % x
, v
.pcode
)))
383 print('\n'.join(map(lambda x
: " %s" % x
, v
.sregs
)))
385 for k
, v
in isa
.forms
.items():
389 if __name__
== '__main__':
392 # example on how to access cmp regs:
393 print ("cmp regs:", isa
.instr
["cmp"].regs
)