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