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