whoops, use cache of pseudocode rather than attempt to truncate
[openpower-isa.git] / src / openpower / decoder / pseudo / pagereader.py
1 # Reads OpenPOWER ISA pages from http://libre-soc.org/openpower/isa
2 """OpenPOWER ISA page parser
3
4 returns an OrderedDict of namedtuple "Ops" containing details of all
5 instructions listed in markdown files.
6
7 format must be strictly as follows (no optional sections) including whitespace:
8
9 # Compare Logical
10
11 X-Form
12
13 * cmpl BF,L,RA,RB
14
15 if L = 0 then a <- [0]*32 || (RA)[32:63]
16 b <- [0]*32 || (RB)[32:63]
17 else a <- (RA)
18 b <- (RB)
19 if a <u b then c <- 0b100
20 else if a >u b then c <- 0b010
21 else c <- 0b001
22 CR[4*BF+32:4*BF+35] <- c || XER[SO]
23
24 Special Registers Altered:
25
26 CR field BF
27 Another field
28
29 this translates to:
30
31 # heading
32 blank
33 Some-Form
34 blank
35 * instruction registerlist
36 * instruction registerlist
37 blank
38 4-space-indented pseudo-code
39 4-space-indented pseudo-code
40 blank
41 Special Registers Altered:
42 4-space-indented register description
43 blank
44 blank(s) (optional for convenience at end-of-page)
45 """
46
47 from collections import namedtuple, OrderedDict
48 from copy import copy
49 import os
50
51 opfields = ("desc", "form", "opcode", "regs", "pcode", "sregs", "page")
52 Ops = namedtuple("Ops", opfields)
53
54
55 def get_isa_dir():
56 fdir = os.path.abspath(os.path.dirname(__file__))
57 fdir = os.path.split(fdir)[0]
58 fdir = os.path.split(fdir)[0]
59 fdir = os.path.split(fdir)[0]
60 fdir = os.path.split(fdir)[0]
61 # print (fdir)
62 return os.path.join(fdir, "openpower", "isa")
63
64
65 class ISA:
66
67 def __init__(self):
68 self.instr = OrderedDict()
69 self.forms = {}
70 self.page = {}
71 self.verbose = False
72 for pth in os.listdir(os.path.join(get_isa_dir())):
73 if self.verbose:
74 print("examining", get_isa_dir(), pth)
75 if "swp" in pth:
76 continue
77 if not pth.endswith(".mdwn"):
78 print ("warning, file not .mdwn, skipping", pth)
79 continue
80 self.read_file(pth)
81 continue
82 # code which helped add in the keyword "Pseudo-code:" automatically
83 rewrite = self.read_file_for_rewrite(pth)
84 name = os.path.join("/tmp", pth)
85 with open(name, "w") as f:
86 f.write('\n'.join(rewrite) + '\n')
87
88 def read_file_for_rewrite(self, fname):
89 pagename = fname.split('.')[0]
90 fname = os.path.join(get_isa_dir(), fname)
91 with open(fname) as f:
92 lines = f.readlines()
93 rewrite = []
94
95 l = lines.pop(0).rstrip() # get first line
96 rewrite.append(l)
97 while lines:
98 if self.verbose:
99 print(l)
100 # look for HTML comment, if starting, skip line.
101 # XXX this is braindead! it doesn't look for the end
102 # so please put ending of comments on one line:
103 # <!-- line 1 comment -->
104 # <!-- line 2 comment -->
105 if l.startswith('<!--'):
106 # print ("skipping comment", l)
107 l = lines.pop(0).rstrip() # get first line
108 continue
109
110 # Ignore blank lines before the first #
111 if len(l.strip()) == 0:
112 continue
113
114 # expect get heading
115 assert l.startswith('#'), ("# not found in line %s" % l)
116
117 # whitespace expected
118 l = lines.pop(0).strip()
119 if self.verbose:
120 print(repr(l))
121 assert len(l) == 0, ("blank line not found %s" % l)
122 rewrite.append(l)
123
124 # Form expected
125 l = lines.pop(0).strip()
126 assert l.endswith('-Form'), ("line with -Form expected %s" % l)
127 rewrite.append(l)
128
129 # whitespace expected
130 l = lines.pop(0).strip()
131 assert len(l) == 0, ("blank line not found %s" % l)
132 rewrite.append(l)
133
134 # get list of opcodes
135 while True:
136 l = lines.pop(0).strip()
137 rewrite.append(l)
138 if len(l) == 0:
139 break
140 assert l.startswith('*'), ("* not found in line %s" % l)
141
142 rewrite.append("Pseudo-code:")
143 rewrite.append("")
144 # get pseudocode
145 while True:
146 l = lines.pop(0).rstrip()
147 rewrite.append(l)
148 if len(l) == 0:
149 break
150 assert l.startswith(' '), ("4spcs not found in line %s" % l)
151
152 # "Special Registers Altered" expected
153 l = lines.pop(0).rstrip()
154 assert l.startswith("Special"), ("special not found %s" % l)
155 rewrite.append(l)
156
157 # whitespace expected
158 l = lines.pop(0).strip()
159 assert len(l) == 0, ("blank line not found %s" % l)
160 rewrite.append(l)
161
162 # get special regs
163 while lines:
164 l = lines.pop(0).rstrip()
165 rewrite.append(l)
166 if len(l) == 0:
167 break
168 assert l.startswith(' '), ("4spcs not found in line %s" % l)
169
170 # expect and drop whitespace
171 while lines:
172 l = lines.pop(0).rstrip()
173 rewrite.append(l)
174 if len(l) != 0 and not l.startswith('<!--'):
175 break
176
177 return rewrite
178
179 def read_file(self, fname):
180 pagename = fname.split('.')[0]
181 fname = os.path.join(get_isa_dir(), fname)
182 with open(fname) as f:
183 lines = f.readlines()
184
185 # set up dict with current page name
186 d = {'page': pagename}
187
188 # line-by-line lexer/parser, quite straightforward: pops one
189 # line off the list and checks it. nothing complicated needed,
190 # all sections are mandatory so no need for a full LALR parser.
191
192 l = lines.pop(0).rstrip() # get first line
193 while lines:
194 if self.verbose:
195 print(l)
196 # look for HTML comment, if starting, skip line.
197 # XXX this is braindead! it doesn't look for the end
198 # so please put ending of comments on one line:
199 # <!-- line 1 comment -->
200 # <!-- line 2 comment -->
201 if l.startswith('<!--'):
202 # print ("skipping comment", l)
203 l = lines.pop(0).rstrip() # get next line
204 continue
205
206 # Ignore blank lines before the first #
207 if len(l) == 0:
208 l = lines.pop(0).rstrip() # get next line
209 continue
210
211 # expect get heading
212 assert l.startswith('#'), ("# not found in line '%s'" % l)
213 d['desc'] = l[1:].strip()
214
215 # whitespace expected
216 l = lines.pop(0).strip()
217 if self.verbose:
218 print(repr(l))
219 assert len(l) == 0, ("blank line not found %s" % l)
220
221 # Form expected
222 l = lines.pop(0).strip()
223 assert l.endswith('-Form'), ("line with -Form expected %s" % l)
224 d['form'] = l.split('-')[0]
225
226 # whitespace expected
227 l = lines.pop(0).strip()
228 assert len(l) == 0, ("blank line not found %s" % l)
229
230 # get list of opcodes
231 li = []
232 while True:
233 l = lines.pop(0).strip()
234 if len(l) == 0:
235 break
236 assert l.startswith('*'), ("* not found in line %s" % l)
237 l = l[1:].split(' ') # lose star
238 l = filter(lambda x: len(x) != 0, l) # strip blanks
239 li.append(list(l))
240 opcodes = li
241
242 # "Pseudocode" expected
243 l = lines.pop(0).rstrip()
244 assert l.startswith("Pseudo-code:"), ("pseudocode found %s" % l)
245
246 # whitespace expected
247 l = lines.pop(0).strip()
248 if self.verbose:
249 print(repr(l))
250 assert len(l) == 0, ("blank line not found %s" % l)
251
252 # get pseudocode
253 li = []
254 while True:
255 l = lines.pop(0).rstrip()
256 if len(l) == 0:
257 break
258 assert l.startswith(' '), ("4spcs not found in line %s" % l)
259 l = l[4:] # lose 4 spaces
260 li.append(l)
261 d['pcode'] = li
262
263 # "Special Registers Altered" expected
264 l = lines.pop(0).rstrip()
265 assert l.startswith("Special"), ("special not found %s" % l)
266
267 # whitespace expected
268 l = lines.pop(0).strip()
269 assert len(l) == 0, ("blank line not found %s" % l)
270
271 # get special regs
272 li = []
273 while lines:
274 l = lines.pop(0).rstrip()
275 if len(l) == 0:
276 break
277 assert l.startswith(' '), ("4spcs not found in line %s" % l)
278 l = l[4:] # lose 4 spaces
279 li.append(l)
280 d['sregs'] = li
281
282 # add in opcode
283 for o in opcodes:
284 self.add_op(o, d)
285
286 # expect and drop whitespace
287 while lines:
288 l = lines.pop(0).rstrip()
289 if len(l) != 0 and not l.startswith('<!--'):
290 break
291
292 def add_op(self, o, d):
293 opcode, regs = o[0], o[1:]
294 op = copy(d)
295 op['regs'] = regs
296 if len(regs) != 0:
297 regs[0] = regs[0].split(",")
298 op['opcode'] = opcode
299 self.instr[opcode] = Ops(**op)
300
301 # create list of instructions by form
302 form = op['form']
303 fl = self.forms.get(form, [])
304 self.forms[form] = fl + [opcode]
305
306 # create list of instructions by page
307 page = op['page']
308 pl = self.page.get(page, [])
309 self.page[page] = pl + [opcode]
310
311 def pprint_ops(self):
312 for k, v in self.instr.items():
313 print("# %s %s" % (v.opcode, v.desc))
314 print("Form: %s Regs: %s" % (v.form, v.regs))
315 print('\n'.join(map(lambda x: " %s" % x, v.pcode)))
316 print("Specials")
317 print('\n'.join(map(lambda x: " %s" % x, v.sregs)))
318 print()
319 for k, v in isa.forms.items():
320 print(k, v)
321
322
323 if __name__ == '__main__':
324 isa = ISA()
325 isa.pprint_ops()
326 # example on how to access cmp regs:
327 print ("cmp regs:", isa.instr["cmp"].regs)