pysvp64db: fix traversal
[openpower-isa.git] / src / openpower / decoder / power_fields.py
1 from collections import namedtuple
2
3 import operator
4 import functools
5
6 from openpower.decoder.power_enums import find_wiki_file
7 from openpower.decoder.selectable_int import (
8 SelectableInt,
9 BitRange,
10 selectconcat,
11 selectltu,
12 )
13
14
15 class RemapError(ValueError):
16 pass
17
18
19 class Descriptor:
20 def __init__(self, cls):
21 self.__cls = cls
22 return super().__init__()
23
24 def __get__(self, instance, owner):
25 if instance is None:
26 return self.__cls
27 return self.__cls(storage=instance.storage)
28
29 def __set__(self, instance, value):
30 if instance is None:
31 raise AttributeError("read-only attribute")
32 self.__cls(storage=instance.storage).assign(value)
33
34
35 @functools.total_ordering
36 class Reference:
37 def __init__(self, storage, *args, **kwargs):
38 if not isinstance(storage, SelectableInt):
39 raise ValueError(storage)
40
41 self.storage = storage
42
43 super().__init__()
44 self.__post_init__()
45
46 def __post_init__(self, *args, **kwargs):
47 _ = (args, kwargs)
48
49 def __binary_operator(self, op, other):
50 span = dict.fromkeys(self.__class__.span).keys()
51 lhs = selectconcat(*(self.storage[bit] for bit in span))
52
53 if isinstance(other, Reference):
54 span = dict.fromkeys(other.__class__.span).keys()
55 rhs = selectconcat(*(other.storage[bit] for bit in span))
56 elif isinstance(other, int):
57 bits = len(self.__class__)
58 if other.bit_length() > bits:
59 raise OverflowError(other)
60 rhs = SelectableInt(value=other, bits=bits)
61 elif isinstance(other, SelectableInt):
62 rhs = other
63 else:
64 raise ValueError(other)
65
66 return op(lhs, rhs)
67
68 def __lt__(self, other):
69 return self.__binary_operator(selectltu, other)
70
71 def __eq__(self, other):
72 return self.__binary_operator(operator.eq, other)
73
74 def __bool__(self):
75 return bool(int(self))
76
77 def __int__(self):
78 span = dict.fromkeys(self.__class__.span).keys()
79 return int(selectconcat(*(self.storage[bit] for bit in span)))
80
81 def __index__(self):
82 return int(self).__index__()
83
84 @property
85 def storage(self):
86 return self.__storage
87
88 @storage.setter
89 def storage(self, storage):
90 if not isinstance(storage, SelectableInt):
91 raise ValueError(storage)
92
93 self.__storage = storage
94
95 def assign(self, value, bits=None):
96 if bits is None:
97 bits = range(len(self.__class__))
98 elif isinstance(bits, int):
99 bits = (bits,)
100 elif isinstance(bits, slice):
101 assert bits.step is None or bits.step == 1
102 bits = range(bits.start, bits.stop)
103 bits = tuple(bits)
104
105 if isinstance(value, (int, self.__class__)):
106 value = int(value)
107 if value.bit_length() > len(bits):
108 raise OverflowError(value)
109 value = SelectableInt(value=value, bits=len(bits))
110 if not isinstance(value, SelectableInt):
111 raise ValueError(value)
112 if value.bits != len(bits):
113 raise OverflowError(value)
114
115 span = tuple(self.__class__.span)
116 mapping = dict(enumerate(span))
117 for (src, bit) in enumerate(bits):
118 if src >= value.bits:
119 raise OverflowError(src)
120 dst = mapping.get(bit)
121 if dst is None:
122 raise OverflowError(bit)
123 self.storage[dst] = value[src]
124
125
126 class FieldMeta(type):
127 def __new__(metacls, clsname, bases, ns, items=()):
128 assert "__members__" not in ns
129
130 members = []
131 for item in items:
132 if not isinstance(item, int):
133 raise ValueError(item)
134 if item < 0:
135 raise ValueError(item)
136 members.append(item)
137
138 ns["__members__"] = tuple(members)
139
140 return super().__new__(metacls, clsname, bases, ns)
141
142 def __repr__(cls):
143 if not cls.__members__:
144 return cls.__name__
145 return f"{cls.__name__}{cls.__members__!r}"
146
147 def __iter__(cls):
148 yield from cls.__members__
149
150 def __len__(cls):
151 return len(cls.__members__)
152
153 def __getitem__(cls, selector):
154 if isinstance(selector, int):
155 selector = (selector,)
156
157 items = []
158 for idx in selector:
159 if not isinstance(idx, int):
160 raise ValueError(selector)
161 item = cls.__members__[idx]
162 items.append(item)
163
164 return cls.__class__(cls.__name__, (Field,), {}, items=items)
165
166 def remap(cls, scheme):
167 if isinstance(scheme, type) and issubclass(scheme, Mapping):
168 scheme = range(len(scheme))
169 scheme = cls.__class__(cls.__name__, (cls,), {}, items=scheme)
170
171 if len(cls) == 0:
172 return scheme
173 elif len(cls) > len(scheme):
174 llen = f"len(scheme)"
175 rlen = f"len({cls.__name__})"
176 raise RemapError(f"{llen} != {rlen}")
177
178 ns = {}
179 ns["__doc__"] = cls.__doc__
180 items = map(lambda item: scheme.__members__[item], cls)
181
182 return cls.__class__(cls.__name__, (cls,), ns, items=items)
183
184 @property
185 def span(cls):
186 return cls.__members__
187
188
189 class Field(Reference, metaclass=FieldMeta):
190 def __repr__(self):
191 return f"[{len(self.__class__)}]0x{int(self):x}"
192
193 def __iter__(self):
194 for bit in self.__class__:
195 yield self.storage[bit]
196
197 def __getitem__(self, key):
198 if isinstance(key, int):
199 bit = self.storage[self.__class__.__members__[key]]
200 return SelectableInt(value=bit, bits=1)
201 if isinstance(key, slice):
202 assert key.step is None or key.step == 1
203 key = range(key.start, key.stop)
204
205 return selectconcat(*(self[bit] for bit in key))
206
207 def __setitem__(self, key, value):
208 return self.assign(value=value, bits=key)
209
210 @classmethod
211 def traverse(cls, path):
212 yield (path, cls.__members__)
213
214
215 class MappingMeta(type):
216 def __new__(metacls, clsname, bases, ns):
217 members = {}
218
219 for cls in bases:
220 if isinstance(cls, metacls):
221 members.update(cls.__members__)
222
223 for (name, cls) in ns.get("__annotations__", {}).items():
224 if not (isinstance(cls, type) and
225 issubclass(cls, (Mapping, Field))):
226 raise ValueError(f"{clsname}.{name}: {cls!r}")
227
228 if name in ns:
229 try:
230 members[name] = cls.remap(ns[name])
231 except RemapError as error:
232 raise RemapError(f"{name}: {error}")
233 else:
234 if cls is Field:
235 raise ValueError(f"{clsname}.{name}: missing initializer")
236 members[name] = cls
237
238 ns["__members__"] = members
239 for (name, cls) in members.items():
240 ns[name] = Descriptor(cls)
241
242 return super().__new__(metacls, clsname, bases, ns)
243
244 def __repr__(cls):
245 return f"{cls.__name__}({cls.__members__!r})"
246
247 def __iter__(cls):
248 yield from cls.__members__.items()
249
250 def __len__(cls):
251 length = 0
252 for field in cls.__members__.values():
253 length = max(length, len(field))
254 return length
255
256 def __getitem__(cls, selector):
257 return cls.__members__["_"][selector]
258
259 def remap(cls, scheme):
260 ns = {}
261 annotations = {}
262
263 for (name, field) in cls:
264 annotations[name] = field.remap(scheme)
265 ns["__annotations__"] = annotations
266 ns["__doc__"] = cls.__doc__
267
268 return cls.__class__(cls.__name__, (cls,), ns)
269
270 @property
271 def span(cls):
272 for field in cls.__members__.values():
273 yield from field.span
274
275
276 class Mapping(Reference, metaclass=MappingMeta):
277 def __init__(self, storage, **kwargs):
278 members = {}
279 for (name, cls) in self.__class__:
280 members[name] = cls(storage)
281
282 self.__members = members
283
284 return super().__init__(storage, **kwargs)
285
286 def __repr__(self):
287 items = tuple(f"{name}={field!r}" for (name, field) in self)
288 return f"{{{', '.join(items)}}}"
289
290 def __iter__(self):
291 yield from self.__members.items()
292
293 def __getitem__(self, key):
294 if isinstance(key, (int, slice, list, tuple, range)):
295 return self["_"].__getitem__(key)
296
297 return self.__members.__getitem__(key)
298
299 def __setitem__(self, key, value):
300 if isinstance(key, (int, slice, list, tuple, range)):
301 return self["_"].assign(value=value, bits=key)
302
303 return self.assign(value=value, bits=key)
304
305 def __getattr__(self, key):
306 raise AttributeError(key)
307
308 @classmethod
309 def traverse(cls, path):
310 for (name, member) in cls.__members__.items():
311 if name == "_":
312 yield from member.traverse(path=path)
313 elif path == "":
314 yield from member.traverse(path=name)
315 else:
316 yield from member.traverse(path=f"{path}.{name}")
317
318
319 def decode_instructions(form):
320 res = {}
321 accum = []
322 for l in form:
323 if l.strip().startswith("Formats"):
324 l = l.strip().split(":")[-1]
325 l = l.replace(" ", "")
326 l = l.split(",")
327 for fmt in l:
328 if fmt not in res:
329 res[fmt] = [accum[0]]
330 else:
331 res[fmt].append(accum[0])
332 accum = []
333 else:
334 accum.append(l.strip())
335 return res
336
337
338 def decode_form_header(hdr):
339 res = {}
340 count = 0
341 hdr = hdr.strip()
342 for f in hdr.split("|"):
343 if not f:
344 continue
345 if f[0].isdigit():
346 idx = int(f.strip().split(' ')[0])
347 res[count] = idx
348 count += len(f) + 1
349 return res
350
351
352 def find_unique(d, key):
353 if key not in d:
354 return key
355 idx = 1
356 while "%s_%d" % (key, idx) in d:
357 idx += 1
358 return "%s_%d" % (key, idx)
359
360
361 def decode_line(header, line):
362 line = line.strip()
363 res = {}
364 count = 0
365 prev_fieldname = None
366 for f in line.split("|"):
367 if not f:
368 continue
369 end = count + len(f) + 1
370 fieldname = f.strip()
371 if not fieldname or fieldname.startswith('/'):
372 if prev_fieldname is not None:
373 res[prev_fieldname] = (res[prev_fieldname], header[count])
374 prev_fieldname = None
375 count = end
376 continue
377 bitstart = header[count]
378 if prev_fieldname is not None:
379 res[prev_fieldname] = (res[prev_fieldname], bitstart)
380 res[fieldname] = bitstart
381 count = end
382 prev_fieldname = fieldname
383 res[prev_fieldname] = (bitstart, 32)
384 return res
385
386
387 def decode_form(form):
388 header = decode_form_header(form[0])
389 res = []
390 for line in form[1:]:
391 dec = decode_line(header, line)
392 if dec:
393 res.append(dec)
394 fields = {}
395 falternate = {}
396 for l in res:
397 for k, (start, end) in l.items():
398 if k in fields:
399 if (start, end) == fields[k]:
400 continue # already in and matching for this Form
401 if k in falternate:
402 alternate = "%s_%d" % (k, falternate[k])
403 if (start, end) == fields[alternate]:
404 continue
405 falternate[k] = fidx = falternate.get(k, 0) + 1
406 fields["%s_%d" % (k, fidx)] = (start, end)
407 else:
408 fields[k] = (start, end)
409 return fields
410
411
412 class DecodeFields:
413
414 def __init__(self, bitkls=BitRange, bitargs=(), fname=None,
415 name_on_wiki=None):
416 self.bitkls = bitkls
417 self.bitargs = bitargs
418 if fname is None:
419 assert name_on_wiki is None
420 fname = "fields.txt"
421 name_on_wiki = "fields.text"
422 self.fname = find_wiki_file(name_on_wiki)
423
424 @property
425 def form_names(self):
426 return self.instrs.keys()
427
428 def create_specs(self):
429 self.forms, self.instrs = self.decode_fields()
430 forms = self.form_names
431 #print ("specs", self.forms, forms)
432 for form in forms:
433 fields = self.instrs[form]
434 fk = fields.keys()
435 Fields = namedtuple("Fields", fk)
436 instr = Fields(**fields)
437 setattr(self, "Form%s" % form, instr)
438 # now add in some commonly-used fields (should be done automatically)
439 # note that these should only be ones which are the same on all Forms
440 # note: these are from microwatt insn_helpers.vhdl
441 self.common_fields = {
442 "PO": self.Formall.PO,
443 "FRS": self.FormX.FRS,
444 "FRT": self.FormX.FRT,
445 "FRA": self.FormX.FRA,
446 "FRB": self.FormX.FRB,
447 "FRC": self.FormA.FRC,
448 "RS": self.FormX.RS,
449 "RT": self.FormX.RT,
450 "RA": self.FormX.RA,
451 "RB": self.FormX.RB,
452 "RC": self.FormVA.RC,
453 "SI": self.FormD.SI,
454 "UI": self.FormD.UI,
455 "L": self.FormD.L,
456 "SH32": self.FormM.SH,
457 "sh": self.FormMD.sh,
458 "MB32": self.FormM.MB,
459 "ME32": self.FormM.ME,
460 "LI": self.FormI.LI,
461 "LK": self.FormI.LK,
462 "AA": self.FormB.AA,
463 "Rc": self.FormX.Rc,
464 "OE": self.FormXO.OE,
465 "BD": self.FormB.BD,
466 "BF": self.FormX.BF,
467 "CR": self.FormXL.XO,
468 "BB": self.FormXL.BB,
469 "BA": self.FormXL.BA,
470 "BT": self.FormXL.BT,
471 "FXM": self.FormXFX.FXM,
472 "BO": self.FormXL.BO,
473 "BI": self.FormXL.BI,
474 "BH": self.FormXL.BH,
475 "D": self.FormD.D,
476 "DS": self.FormDS.DS,
477 "TO": self.FormX.TO,
478 "BC": self.FormA.BC,
479 "SH": self.FormX.SH,
480 "ME": self.FormM.ME,
481 "MB": self.FormM.MB,
482 "SPR": self.FormXFX.SPR}
483 for k, v in self.common_fields.items():
484 setattr(self, k, v)
485
486 def decode_fields(self):
487 with open(self.fname) as f:
488 txt = f.readlines()
489 #print ("decode", txt)
490 forms = {}
491 reading_data = False
492 for lineno, l in enumerate(txt):
493 l = l.strip()
494 if len(l) == 0:
495 continue
496 if reading_data:
497 if l[0] == '#':
498 reading_data = False
499 else:
500 form = forms[heading]
501 form.append(l)
502 if len(form) <= 1:
503 continue
504 # check separators line up with header
505 for i, ch in enumerate(l):
506 if ch != '|':
507 continue
508 if i >= len(form[0]) or form[0][i] != '|':
509 col = len(txt[lineno]) - len(txt[lineno].lstrip())
510 col += i + 1
511 raise SyntaxError("form line field separator ('|') "
512 "with no corresponding separator in header",
513 (self.fname, lineno + 1, col, txt[lineno]))
514 if not reading_data:
515 assert l[0] == '#'
516 heading = l[1:].strip()
517 # if heading.startswith('1.6.28'): # skip instr fields for now
518 # break
519 heading = heading.split(' ')[-1]
520 reading_data = True
521 forms[heading] = []
522
523 res = {}
524 inst = {}
525
526 for hdr, form in forms.items():
527 if heading == 'Fields':
528 i = decode_instructions(form)
529 for form, field in i.items():
530 inst[form] = self.decode_instruction_fields(field)
531 # else:
532 # res[hdr] = decode_form(form)
533 return res, inst
534
535 def decode_instruction_fields(self, fields):
536 res = {}
537 for field in fields:
538 f, spec = field.strip().split(" ")
539 ss = spec[1:-1].split(",")
540 fs = f.split(",")
541 if len(fs) > 1:
542 individualfields = []
543 for f0, s0 in zip(fs, ss):
544 txt = "%s (%s)" % (f0, s0)
545 individualfields.append(txt)
546 if len(fs) > 1:
547 res.update(self.decode_instruction_fields(
548 individualfields))
549 d = self.bitkls(*self.bitargs)
550 idx = 0
551 for s in ss:
552 s = s.split(':')
553 if len(s) == 1:
554 d[idx] = int(s[0])
555 idx += 1
556 else:
557 start = int(s[0])
558 end = int(s[1])
559 while start <= end:
560 d[idx] = start
561 idx += 1
562 start += 1
563 f = f.replace(",", "_")
564 unique = find_unique(res, f)
565 res[unique] = d
566
567 return res
568
569
570 if __name__ == '__main__':
571 dec = DecodeFields()
572 dec.create_specs()
573 forms, instrs = dec.forms, dec.instrs
574 for form, fields in instrs.items():
575 print("Form", form)
576 for field, bits in fields.items():
577 print("\tfield", field, bits)