power_insn: fix Fields methods
[openpower-isa.git] / src / openpower / decoder / power_insn.py
1 import collections as _collections
2 import csv as _csv
3 import dataclasses as _dataclasses
4 import enum as _enum
5 import functools as _functools
6 import os as _os
7 import operator as _operator
8 import pathlib as _pathlib
9 import re as _re
10
11 try:
12 from functools import cached_property
13 except ImportError:
14 from cached_property import cached_property
15
16 from openpower.decoder.power_enums import (
17 Function as _Function,
18 MicrOp as _MicrOp,
19 In1Sel as _In1Sel,
20 In2Sel as _In2Sel,
21 In3Sel as _In3Sel,
22 OutSel as _OutSel,
23 CRInSel as _CRInSel,
24 CROutSel as _CROutSel,
25 LDSTLen as _LDSTLen,
26 LDSTMode as _LDSTMode,
27 RC as _RC,
28 CryIn as _CryIn,
29 Form as _Form,
30 SVEtype as _SVEtype,
31 SVMode as _SVMode,
32 SVPtype as _SVPtype,
33 SVExtra as _SVExtra,
34 SVExtraRegType as _SVExtraRegType,
35 SVExtraReg as _SVExtraReg,
36 )
37 from openpower.decoder.selectable_int import (
38 SelectableInt as _SelectableInt,
39 selectconcat as _selectconcat,
40 )
41 from openpower.decoder.power_fields import (
42 Field as _Field,
43 Mapping as _Mapping,
44 )
45
46
47 def dataclass(cls, record, keymap=None, typemap=None):
48 if keymap is None:
49 keymap = {}
50 if typemap is None:
51 typemap = {field.name:field.type for field in _dataclasses.fields(cls)}
52
53 def transform(key_value):
54 (key, value) = key_value
55 key = keymap.get(key, key)
56 hook = typemap.get(key, lambda value: value)
57 if hook is bool and value in ("", "0"):
58 value = False
59 else:
60 value = hook(value)
61 return (key, value)
62
63 record = dict(map(transform, record.items()))
64 for key in frozenset(record.keys()):
65 if record[key] == "":
66 record.pop(key)
67
68 return cls(**record)
69
70
71 @_functools.total_ordering
72 @_dataclasses.dataclass(eq=True, frozen=True)
73 class Opcode:
74 class Value(int):
75 def __repr__(self):
76 if self.bit_length() <= 32:
77 return f"0x{self:08x}"
78 else:
79 return f"0x{self:016x}"
80
81 class Mask(int):
82 def __repr__(self):
83 if self.bit_length() <= 32:
84 return f"0x{self:08x}"
85 else:
86 return f"0x{self:016x}"
87
88 value: Value
89 mask: Mask = None
90
91 def __lt__(self, other):
92 if not isinstance(other, Opcode):
93 return NotImplemented
94 return ((self.value, self.mask) < (other.value, other.mask))
95
96 def __post_init__(self):
97 (value, mask) = (self.value, self.mask)
98
99 if isinstance(value, Opcode):
100 if mask is not None:
101 raise ValueError(mask)
102 (value, mask) = (value.value, value.mask)
103 elif isinstance(value, str):
104 if mask is not None:
105 raise ValueError(mask)
106 value = int(value, 0)
107
108 if not isinstance(value, int):
109 raise ValueError(value)
110 if mask is None:
111 mask = value
112 if not isinstance(mask, int):
113 raise ValueError(mask)
114
115 object.__setattr__(self, "value", self.__class__.Value(value))
116 object.__setattr__(self, "mask", self.__class__.Mask(mask))
117
118
119 class IntegerOpcode(Opcode):
120 def __init__(self, value):
121 if isinstance(value, str):
122 value = int(value, 0)
123 return super().__init__(value=value, mask=None)
124
125
126 class PatternOpcode(Opcode):
127 def __init__(self, value):
128 (pattern, value, mask) = (value, 0, 0)
129
130 for symbol in pattern:
131 if symbol not in {"0", "1", "-"}:
132 raise ValueError(pattern)
133 value |= (symbol == "1")
134 mask |= (symbol != "-")
135 value <<= 1
136 mask <<= 1
137 value >>= 1
138 mask >>= 1
139
140 return super().__init__(value=value, mask=mask)
141
142
143 class FieldsOpcode(Opcode):
144 def __init__(self, fields):
145 def field(opcode, field):
146 (value, mask) = opcode
147 (field, bits) = field
148 shifts = map(lambda bit: (31 - bit), reversed(tuple(bits)))
149 for (index, shift) in enumerate(shifts):
150 bit = ((field & (1 << index)) != 0)
151 value |= (bit << shift)
152 mask |= (1 << shift)
153 return (value, mask)
154
155 (value, mask) = _functools.reduce(field, fields, (0, 0))
156
157 return super().__init__(value=value, mask=mask)
158
159
160 @_dataclasses.dataclass(eq=True, frozen=True)
161 class PPCRecord:
162 class FlagsMeta(type):
163 def __iter__(cls):
164 yield from (
165 "inv A",
166 "inv out",
167 "cry out",
168 "BR",
169 "sgn ext",
170 "rsrv",
171 "32b",
172 "sgn",
173 "lk",
174 "sgl pipe",
175 )
176
177 class Flags(frozenset, metaclass=FlagsMeta):
178 def __new__(cls, flags=frozenset()):
179 flags = frozenset(flags)
180 diff = (flags - frozenset(cls))
181 if diff:
182 raise ValueError(flags)
183 return super().__new__(cls, flags)
184
185 opcode: Opcode
186 comment: str
187 flags: Flags = Flags()
188 comment2: str = ""
189 function: _Function = _Function.NONE
190 intop: _MicrOp = _MicrOp.OP_ILLEGAL
191 in1: _In1Sel = _In1Sel.RA
192 in2: _In2Sel = _In2Sel.NONE
193 in3: _In3Sel = _In3Sel.NONE
194 out: _OutSel = _OutSel.NONE
195 cr_in: _CRInSel = _CRInSel.NONE
196 cr_out: _CROutSel = _CROutSel.NONE
197 cry_in: _CryIn = _CryIn.ZERO
198 ldst_len: _LDSTLen = _LDSTLen.NONE
199 upd: _LDSTMode = _LDSTMode.NONE
200 rc: _RC = _RC.NONE
201 form: _Form = _Form.NONE
202 conditions: str = ""
203 unofficial: bool = False
204
205 __KEYMAP = {
206 "unit": "function",
207 "internal op": "intop",
208 "CR in": "cr_in",
209 "CR out": "cr_out",
210 "cry in": "cry_in",
211 "ldst len": "ldst_len",
212 "CONDITIONS": "conditions",
213 }
214
215 @classmethod
216 def CSV(cls, record, opcode_cls=Opcode):
217 typemap = {field.name:field.type for field in _dataclasses.fields(cls)}
218 typemap["opcode"] = opcode_cls
219
220 flags = set()
221 for flag in frozenset(PPCRecord.Flags):
222 if bool(record.pop(flag, "")):
223 flags.add(flag)
224 record["flags"] = PPCRecord.Flags(flags)
225
226 return dataclass(cls, record, keymap=PPCRecord.__KEYMAP, typemap=typemap)
227
228 @property
229 def identifier(self):
230 return self.comment
231
232 @cached_property
233 def names(self):
234 return frozenset(self.comment.split("=")[-1].split("/"))
235
236
237 @_dataclasses.dataclass(eq=True, frozen=True)
238 class SVP64Record:
239 class ExtraMap(tuple):
240 class Extra(tuple):
241 @_dataclasses.dataclass(eq=True, frozen=True)
242 class Entry:
243 regtype: _SVExtraRegType = _SVExtraRegType.NONE
244 reg: _SVExtraReg = _SVExtraReg.NONE
245
246 def __repr__(self):
247 return f"{self.regtype.value}:{self.reg.name}"
248
249 def __new__(cls, value="0"):
250 if isinstance(value, str):
251 def transform(value):
252 (regtype, reg) = value.split(":")
253 regtype = _SVExtraRegType(regtype)
254 reg = _SVExtraReg(reg)
255 return cls.Entry(regtype=regtype, reg=reg)
256
257 if value == "0":
258 value = tuple()
259 else:
260 value = map(transform, value.split(";"))
261
262 return super().__new__(cls, value)
263
264 def __repr__(self):
265 return repr(list(self))
266
267 def __new__(cls, value=tuple()):
268 value = tuple(value)
269 if len(value) == 0:
270 value = (("0",) * 4)
271 return super().__new__(cls, map(cls.Extra, value))
272
273 def __repr__(self):
274 return repr({index:self[index] for index in range(0, 4)})
275
276 identifier: str
277 ptype: _SVPtype = _SVPtype.NONE
278 etype: _SVEtype = _SVEtype.NONE
279 in1: _In1Sel = _In1Sel.NONE
280 in2: _In2Sel = _In2Sel.NONE
281 in3: _In3Sel = _In3Sel.NONE
282 out: _OutSel = _OutSel.NONE
283 out2: _OutSel = _OutSel.NONE
284 cr_in: _CRInSel = _CRInSel.NONE
285 cr_out: _CROutSel = _CROutSel.NONE
286 extra: ExtraMap = ExtraMap()
287 pu: bool = False
288 conditions: str = ""
289 mode: _SVMode = _SVMode.NORMAL
290
291 __KEYMAP = {
292 "insn": "identifier",
293 "CONDITIONS": "conditions",
294 "Ptype": "ptype",
295 "Etype": "etype",
296 "CR in": "cr_in",
297 "CR out": "cr_out",
298 "PU": "pu",
299 }
300
301 @classmethod
302 def CSV(cls, record):
303 for key in ("in1", "in2", "in3", "out", "out2", "CR in", "CR out"):
304 value = record[key]
305 if value == "0":
306 record[key] = "NONE"
307
308 record["extra"] = cls.ExtraMap(record.pop(f"{index}") for index in range(0, 4))
309
310 return dataclass(cls, record, keymap=cls.__KEYMAP)
311
312
313 class BitSel:
314 def __init__(self, value=(0, 32)):
315 if isinstance(value, str):
316 (start, end) = map(int, value.split(":"))
317 else:
318 (start, end) = value
319 if start < 0 or end < 0 or start >= end:
320 raise ValueError(value)
321
322 self.__start = start
323 self.__end = end
324
325 return super().__init__()
326
327 def __repr__(self):
328 return f"[{self.__start}:{self.__end}]"
329
330 def __iter__(self):
331 yield from range(self.start, (self.end + 1))
332
333 @property
334 def start(self):
335 return self.__start
336
337 @property
338 def end(self):
339 return self.__end
340
341
342 @_dataclasses.dataclass(eq=True, frozen=True)
343 class Section:
344 class Mode(_enum.Enum):
345 INTEGER = _enum.auto()
346 PATTERN = _enum.auto()
347
348 @classmethod
349 def _missing_(cls, value):
350 if isinstance(value, str):
351 return cls[value.upper()]
352 return super()._missing_(value)
353
354 class Suffix(int):
355 def __new__(cls, value=None):
356 if isinstance(value, str):
357 if value.upper() == "NONE":
358 value = None
359 else:
360 value = int(value, 0)
361 if value is None:
362 value = 0
363
364 return super().__new__(cls, value)
365
366 def __str__(self):
367 return repr(self)
368
369 def __repr__(self):
370 return (bin(self) if self else "None")
371
372 path: _pathlib.Path
373 opcode: Opcode
374 bitsel: BitSel
375 suffix: Suffix
376 mode: Mode
377
378 @classmethod
379 def CSV(cls, record):
380 return dataclass(cls, record)
381
382
383 class Fields:
384 def __init__(self, items):
385 if isinstance(items, dict):
386 items = items.items()
387
388 def transform(item):
389 (name, bitrange) = item
390 return (name, tuple(bitrange.values()))
391
392 self.__mapping = dict(map(transform, items))
393
394 return super().__init__()
395
396 def __repr__(self):
397 return repr(self.__mapping)
398
399 def __iter__(self):
400 yield from self.__mapping.items()
401
402 def __contains__(self, key):
403 return self.__mapping.__contains__(key)
404
405 def __getitem__(self, key):
406 return self.__mapping.get(key, None)
407
408
409 @_functools.total_ordering
410 @_dataclasses.dataclass(eq=True, frozen=True)
411 class Record:
412 name: str
413 rc: bool
414 section: Section
415 ppc: PPCRecord
416 fields: Fields
417 svp64: SVP64Record = None
418
419 __EXTRA = (
420 _SVExtra.Idx0,
421 _SVExtra.Idx1,
422 _SVExtra.Idx2,
423 _SVExtra.Idx3,
424 )
425
426 def __lt__(self, other):
427 if not isinstance(other, Record):
428 return NotImplemented
429 return (self.opcode < other.opcode)
430
431 def __repr__(self):
432 return f"{self.__class__.__name__}(name={self.name!r}, opcode={self.opcode})"
433
434 @cached_property
435 def opcode(self):
436 fields = []
437 if self.section.opcode:
438 fields += [(self.section.opcode.value, BitSel((0, 5)))]
439 fields += [(self.ppc.opcode.value, self.section.bitsel)]
440 else:
441 fields += [(self.ppc.opcode.value, self.section.bitsel)]
442
443 # Some instructions are special regarding Rc handling.
444 # They are marked with Rc.ONE, but don't have Rc field.
445 # At least addic., andi., andis. belong to this list.
446 if self.rc and "Rc" in self.fields:
447 fields += [(1, self.fields["Rc"])]
448
449 return FieldsOpcode(fields)
450
451 @property
452 def function(self):
453 return self.ppc.function
454
455 @property
456 def in1(self):
457 return self.ppc.in1
458
459 @property
460 def in2(self):
461 return self.ppc.in2
462
463 @property
464 def in3(self):
465 return self.ppc.in3
466
467 @property
468 def out(self):
469 return self.ppc.out
470
471 @property
472 def out2(self):
473 if self.svp64 is None:
474 return _OutSel.NONE
475 return self.ppc.out
476
477 @property
478 def cr_in(self):
479 return self.ppc.cr_in
480
481 @property
482 def cr_out(self):
483 return self.ppc.cr_out
484
485 def sv_extra(self, key):
486 if key not in frozenset({
487 "in1", "in2", "in3", "cr_in",
488 "out", "out2", "cr_out",
489 }):
490 raise KeyError(key)
491
492 sel = getattr(self.svp64, key)
493 if sel is _CRInSel.BA_BB:
494 return _SVExtra.Idx_1_2
495 reg = _SVExtraReg(sel)
496 if reg is _SVExtraReg.NONE:
497 return _SVExtra.NONE
498
499 extra_map = {
500 _SVExtraRegType.SRC: {},
501 _SVExtraRegType.DST: {},
502 }
503 for index in range(0, 4):
504 for entry in self.svp64.extra[index]:
505 extra_map[entry.regtype][entry.reg] = Record.__EXTRA[index]
506
507 for regtype in (_SVExtraRegType.SRC, _SVExtraRegType.DST):
508 extra = extra_map[regtype].get(reg, _SVExtra.NONE)
509 if extra is not _SVExtra.NONE:
510 return extra
511
512 return _SVExtra.NONE
513
514 sv_in1 = property(_functools.partial(sv_extra, key="in1"))
515 sv_in2 = property(_functools.partial(sv_extra, key="in2"))
516 sv_in3 = property(_functools.partial(sv_extra, key="in3"))
517 sv_out = property(_functools.partial(sv_extra, key="out"))
518 sv_out2 = property(_functools.partial(sv_extra, key="out2"))
519 sv_cr_in = property(_functools.partial(sv_extra, key="cr_in"))
520 sv_cr_out = property(_functools.partial(sv_extra, key="cr_out"))
521
522 @property
523 def sv_ptype(self):
524 if self.svp64 is None:
525 return _SVPtype.NONE
526 return self.svp64.ptype
527
528 @property
529 def sv_etype(self):
530 if self.svp64 is None:
531 return _SVEtype.NONE
532 return self.svp64.etype
533
534
535 class Instruction(_Mapping):
536 @classmethod
537 def integer(cls, value=0, bits=None, byteorder="little"):
538 if isinstance(value, (int, bytes)) and not isinstance(bits, int):
539 raise ValueError(bits)
540
541 if isinstance(value, bytes):
542 if ((len(value) * 8) != bits):
543 raise ValueError(f"bit length mismatch")
544 value = int.from_bytes(value, byteorder=byteorder)
545
546 if isinstance(value, int):
547 value = _SelectableInt(value=value, bits=bits)
548 elif isinstance(value, Instruction):
549 value = value.storage
550
551 if not isinstance(value, _SelectableInt):
552 raise ValueError(value)
553 if bits is None:
554 bits = len(cls)
555 if len(value) != bits:
556 raise ValueError(value)
557
558 value = _SelectableInt(value=value, bits=bits)
559
560 return cls(storage=value)
561
562 def __hash__(self):
563 return hash(int(self))
564
565 def disassemble(self, db):
566 raise NotImplementedError
567
568
569 class WordInstruction(Instruction):
570 _: _Field = range(0, 32)
571 po: _Field = range(0, 6)
572
573 @classmethod
574 def integer(cls, value, byteorder="little"):
575 return super().integer(bits=32, value=value, byteorder=byteorder)
576
577 def disassemble(self, db):
578 record = db[self]
579 if record is None:
580 yield f".long 0x{int(self):08x}"
581 else:
582 yield f".long 0x{int(self):08x} # {record.name}"
583
584
585 class PrefixedInstruction(Instruction):
586 class Prefix(WordInstruction.remap(range(0, 32))):
587 pass
588
589 class Suffix(WordInstruction.remap(range(32, 64))):
590 pass
591
592 _: _Field = range(64)
593 prefix: Prefix
594 suffix: Suffix
595 po: Suffix.po
596
597 @classmethod
598 def integer(cls, value, byteorder="little"):
599 return super().integer(bits=64, value=value, byteorder=byteorder)
600
601 @classmethod
602 def pair(cls, prefix=0, suffix=0, byteorder="little"):
603 def transform(value):
604 return WordInstruction.integer(value=value,
605 byteorder=byteorder)[0:32]
606
607 (prefix, suffix) = map(transform, (prefix, suffix))
608 value = _selectconcat(prefix, suffix)
609
610 return super().integer(value=value)
611
612 def disassemble(self, db):
613 record = db[self.suffix]
614 if record is None:
615 yield f".llong 0x{int(self):016x}"
616 else:
617 yield f".llong 0x{int(self):016x} # {record.name}"
618
619
620 class SVP64Instruction(PrefixedInstruction):
621 """SVP64 instruction: https://libre-soc.org/openpower/sv/svp64/"""
622 class Prefix(PrefixedInstruction.Prefix):
623 class RM(_Mapping):
624 _: _Field = range(24)
625 mmode: _Field = (0,)
626 mask: _Field = range(1, 4)
627 elwidth: _Field = range(4, 6)
628 ewsrc: _Field = range(6, 8)
629 subvl: _Field = range(8, 10)
630 extra: _Field = range(10, 19)
631 mode: _Field = range(19, 24)
632 extra2: _Field[4] = (
633 range(10, 12),
634 range(12, 14),
635 range(14, 16),
636 range(16, 18),
637 )
638 smask: _Field = range(16, 19)
639 extra3: _Field[3] = (
640 range(10, 13),
641 range(13, 16),
642 range(16, 19),
643 )
644
645 id: _Field = (7, 9)
646 rm: RM = ((6, 8) + tuple(range(10, 32)))
647
648 prefix: Prefix
649
650 def disassemble(self, db):
651 record = db[self.suffix]
652 if record is None:
653 yield f".llong 0x{int(self):016x}"
654 else:
655 yield f".llong 0x{int(self):016x} # sv.{record.name}"
656
657
658 class Database:
659 def __init__(self, root):
660 root = _pathlib.Path(root)
661
662 def parse(stream, factory):
663 lines = filter(lambda line: not line.strip().startswith("#"), stream)
664 entries = _csv.DictReader(lines)
665 entries = filter(lambda entry: "TODO" not in frozenset(entry.values()), entries)
666 return tuple(map(factory, entries))
667
668 def database_ppc(root):
669 db = _collections.defaultdict(set)
670 path = (root / "insndb.csv")
671 with open(path, "r", encoding="UTF-8") as stream:
672 for section in parse(stream, Section.CSV):
673 path = (root / section.path)
674 opcode_cls = {
675 section.Mode.INTEGER: IntegerOpcode,
676 section.Mode.PATTERN: PatternOpcode,
677 }[section.mode]
678 factory = _functools.partial(PPCRecord.CSV, opcode_cls=opcode_cls)
679 with open(path, "r", encoding="UTF-8") as stream:
680 db[section].update(parse(stream, factory))
681 for (section, records) in db.items():
682 db[section] = {record.identifier:record for record in records}
683 return db
684
685 def database_svp64(root):
686 db = set()
687 pattern = _re.compile(r"^(?:LDST)?RM-(1P|2P)-.*?\.csv$")
688 for (prefix, _, names) in _os.walk(root):
689 prefix = _pathlib.Path(prefix)
690 for name in filter(lambda name: pattern.match(name), names):
691 path = (prefix / _pathlib.Path(name))
692 with open(path, "r", encoding="UTF-8") as stream:
693 db.update(parse(stream, SVP64Record.CSV))
694 db = {record.identifier:record for record in db}
695 return db
696
697 def database_forms(root):
698 # This is hack. The whole code there should be moved here.
699 # The fields.text parser should take care of the validation.
700 from openpower.decoder.power_fields import DecodeFields as _DecodeFields
701 db = {}
702 df = _DecodeFields()
703 df.create_specs()
704 for (form, fields) in df.instrs.items():
705 if form in {"DQE", "TX"}:
706 continue
707 if form == "all":
708 form = "NONE"
709 db[_Form[form]] = Fields(fields)
710 return db
711
712 def database(ppcdb, svp64db, formsdb):
713 items = set()
714 for section in ppcdb:
715 for (identifier, ppc) in ppcdb[section].items():
716 fields = formsdb[ppc.form]
717 svp64 = svp64db.get(identifier)
718 if ppc.rc is _RC.ONE:
719 variants = {name:True for name in ppc.names}
720 elif ppc.rc is _RC.RC:
721 variants = {name:False for name in ppc.names}
722 variants.update({f"{name}.":True for name in ppc.names})
723 else:
724 variants = {name:False for name in ppc.names}
725 for (name, rc) in variants.items():
726 items.add(Record(name=name, rc=rc,
727 section=section, ppc=ppc, fields=fields, svp64=svp64))
728
729 items = tuple(sorted(items, key=_operator.attrgetter("opcode")))
730 opcodes = {item.opcode:item for item in items}
731 names = {item.name:item for item in sorted(items, key=_operator.attrgetter("name"))}
732
733 return (items, opcodes, names)
734
735 ppcdb = database_ppc(root)
736 svp64db = database_svp64(root)
737 formsdb = database_forms(root)
738
739 (items, opcodes, names) = database(ppcdb, svp64db, formsdb)
740 self.__items = items
741 self.__opcodes = opcodes
742 self.__names = names
743
744 return super().__init__()
745
746 def __repr__(self):
747 return repr(self.__items)
748
749 def __iter__(self):
750 yield from self.__items
751
752 @_functools.lru_cache(maxsize=None)
753 def __contains__(self, key):
754 return self.__getitem__(key) is not None
755
756 @_functools.lru_cache(maxsize=None)
757 def __getitem__(self, key):
758 if isinstance(key, (int, Instruction)):
759 key = int(key)
760 for (opcode, insn) in self.__opcodes.items():
761 if ((opcode.value & opcode.mask) ==
762 (key & opcode.mask)):
763 return insn
764 return None
765 elif isinstance(key, Opcode):
766 return self.__opcodes.get(key, None)
767 elif isinstance(key, str):
768 return self.__names.get(key, None)
769 else:
770 return None