2ecc2aa8aa2f6ff4f22168c407a795d3ea8b2af5
[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 pathlib as _pathlib
8 import re as _re
9
10 try:
11 from functools import cached_property
12 except ImportError:
13 from cached_property import cached_property
14
15 from openpower.decoder.power_enums import (
16 Function as _Function,
17 MicrOp as _MicrOp,
18 In1Sel as _In1Sel,
19 In2Sel as _In2Sel,
20 In3Sel as _In3Sel,
21 OutSel as _OutSel,
22 CRInSel as _CRInSel,
23 CROutSel as _CROutSel,
24 LDSTLen as _LDSTLen,
25 LDSTMode as _LDSTMode,
26 RCOE as _RCOE,
27 CryIn as _CryIn,
28 Form as _Form,
29 SVEtype as _SVEtype,
30 SVMode as _SVMode,
31 SVPtype as _SVPtype,
32 SVExtra as _SVExtra,
33 RegType as _RegType,
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 Array as _Array,
44 Mapping as _Mapping,
45 DecodeFields as _DecodeFields,
46 )
47 from openpower.decoder.pseudo.pagereader import ISA as _ISA
48
49
50 def dataclass(cls, record, keymap=None, typemap=None):
51 if keymap is None:
52 keymap = {}
53 if typemap is None:
54 typemap = {field.name:field.type for field in _dataclasses.fields(cls)}
55
56 def transform(key_value):
57 (key, value) = key_value
58 key = keymap.get(key, key)
59 hook = typemap.get(key, lambda value: value)
60 if hook is bool and value in ("", "0"):
61 value = False
62 else:
63 value = hook(value)
64 return (key, value)
65
66 record = dict(map(transform, record.items()))
67 for key in frozenset(record.keys()):
68 if record[key] == "":
69 record.pop(key)
70
71 return cls(**record)
72
73
74 @_functools.total_ordering
75 @_dataclasses.dataclass(eq=True, frozen=True)
76 class Opcode:
77 class Value(int):
78 def __repr__(self):
79 if self.bit_length() <= 32:
80 return f"0x{self:08x}"
81 else:
82 return f"0x{self:016x}"
83
84 class Mask(int):
85 def __repr__(self):
86 if self.bit_length() <= 32:
87 return f"0x{self:08x}"
88 else:
89 return f"0x{self:016x}"
90
91 value: Value
92 mask: Mask = None
93
94 def __lt__(self, other):
95 if not isinstance(other, Opcode):
96 return NotImplemented
97 return ((self.value, self.mask) < (other.value, other.mask))
98
99 def __post_init__(self):
100 (value, mask) = (self.value, self.mask)
101
102 if isinstance(value, Opcode):
103 if mask is not None:
104 raise ValueError(mask)
105 (value, mask) = (value.value, value.mask)
106 elif isinstance(value, str):
107 if mask is not None:
108 raise ValueError(mask)
109 value = int(value, 0)
110
111 if not isinstance(value, int):
112 raise ValueError(value)
113 if mask is None:
114 mask = value
115 if not isinstance(mask, int):
116 raise ValueError(mask)
117
118 object.__setattr__(self, "value", self.__class__.Value(value))
119 object.__setattr__(self, "mask", self.__class__.Mask(mask))
120
121
122 class IntegerOpcode(Opcode):
123 def __init__(self, value):
124 if isinstance(value, str):
125 value = int(value, 0)
126 return super().__init__(value=value, mask=None)
127
128
129 class PatternOpcode(Opcode):
130 def __init__(self, value):
131 (pattern, value, mask) = (value, 0, 0)
132
133 for symbol in pattern:
134 if symbol not in {"0", "1", "-"}:
135 raise ValueError(pattern)
136 value |= (symbol == "1")
137 mask |= (symbol != "-")
138 value <<= 1
139 mask <<= 1
140 value >>= 1
141 mask >>= 1
142
143 return super().__init__(value=value, mask=mask)
144
145
146 class FieldsOpcode(Opcode):
147 def __init__(self, fields):
148 def field(opcode, field):
149 (value, mask) = opcode
150 (field, bits) = field
151 shifts = map(lambda bit: (31 - bit), reversed(tuple(bits)))
152 for (index, shift) in enumerate(shifts):
153 bit = ((field & (1 << index)) != 0)
154 value |= (bit << shift)
155 mask |= (1 << shift)
156 return (value, mask)
157
158 (value, mask) = _functools.reduce(field, fields, (0, 0))
159
160 return super().__init__(value=value, mask=mask)
161
162
163 @_dataclasses.dataclass(eq=True, frozen=True)
164 class PPCRecord:
165 class FlagsMeta(type):
166 def __iter__(cls):
167 yield from (
168 "inv A",
169 "inv out",
170 "cry out",
171 "BR",
172 "sgn ext",
173 "rsrv",
174 "32b",
175 "sgn",
176 "lk",
177 "sgl pipe",
178 )
179
180 class Flags(frozenset, metaclass=FlagsMeta):
181 def __new__(cls, flags=frozenset()):
182 flags = frozenset(flags)
183 diff = (flags - frozenset(cls))
184 if diff:
185 raise ValueError(flags)
186 return super().__new__(cls, flags)
187
188 opcode: Opcode
189 comment: str
190 flags: Flags = Flags()
191 comment2: str = ""
192 function: _Function = _Function.NONE
193 intop: _MicrOp = _MicrOp.OP_ILLEGAL
194 in1: _In1Sel = _In1Sel.RA
195 in2: _In2Sel = _In2Sel.NONE
196 in3: _In3Sel = _In3Sel.NONE
197 out: _OutSel = _OutSel.NONE
198 cr_in: _CRInSel = _CRInSel.NONE
199 cr_out: _CROutSel = _CROutSel.NONE
200 cry_in: _CryIn = _CryIn.ZERO
201 ldst_len: _LDSTLen = _LDSTLen.NONE
202 upd: _LDSTMode = _LDSTMode.NONE
203 Rc: _RCOE = _RCOE.NONE
204 form: _Form = _Form.NONE
205 conditions: str = ""
206 unofficial: bool = False
207
208 __KEYMAP = {
209 "unit": "function",
210 "internal op": "intop",
211 "CR in": "cr_in",
212 "CR out": "cr_out",
213 "cry in": "cry_in",
214 "ldst len": "ldst_len",
215 "rc": "Rc",
216 "CONDITIONS": "conditions",
217 }
218
219 @classmethod
220 def CSV(cls, record, opcode_cls=Opcode):
221 typemap = {field.name:field.type for field in _dataclasses.fields(cls)}
222 typemap["opcode"] = opcode_cls
223
224 flags = set()
225 for flag in frozenset(PPCRecord.Flags):
226 if bool(record.pop(flag, "")):
227 flags.add(flag)
228 record["flags"] = PPCRecord.Flags(flags)
229
230 return dataclass(cls, record,
231 keymap=PPCRecord.__KEYMAP,
232 typemap=typemap)
233
234 @cached_property
235 def names(self):
236 return frozenset(self.comment.split("=")[-1].split("/"))
237
238
239 class PPCMultiRecord(frozenset):
240 @cached_property
241 def unified(self):
242 def merge(lhs, rhs):
243 value = 0
244 mask = 0
245 lvalue = lhs.opcode.value
246 rvalue = rhs.opcode.value
247 lmask = lhs.opcode.mask
248 rmask = rhs.opcode.mask
249 bits = max(lmask.bit_length(), rmask.bit_length())
250 for bit in range(bits):
251 lvstate = ((lvalue & (1 << bit)) != 0)
252 rvstate = ((rvalue & (1 << bit)) != 0)
253 lmstate = ((lmask & (1 << bit)) != 0)
254 rmstate = ((rmask & (1 << bit)) != 0)
255 vstate = lvstate
256 mstate = True
257 if (not lmstate or not rmstate) or (lvstate != rvstate):
258 vstate = 0
259 mstate = 0
260 value |= (vstate << bit)
261 mask |= (mstate << bit)
262
263 opcode = opcode=Opcode(value=value, mask=mask)
264
265 return _dataclasses.replace(lhs, opcode=opcode)
266
267 return _functools.reduce(merge, self)
268
269 def __getattr__(self, attr):
270 return getattr(self.unified, attr)
271
272
273 @_dataclasses.dataclass(eq=True, frozen=True)
274 class SVP64Record:
275 class ExtraMap(tuple):
276 class Extra(tuple):
277 @_dataclasses.dataclass(eq=True, frozen=True)
278 class Entry:
279 regtype: _SVExtraRegType = _SVExtraRegType.NONE
280 reg: _SVExtraReg = _SVExtraReg.NONE
281
282 def __repr__(self):
283 return f"{self.regtype.value}:{self.reg.name}"
284
285 def __new__(cls, value="0"):
286 if isinstance(value, str):
287 def transform(value):
288 (regtype, reg) = value.split(":")
289 regtype = _SVExtraRegType(regtype)
290 reg = _SVExtraReg(reg)
291 return cls.Entry(regtype=regtype, reg=reg)
292
293 if value == "0":
294 value = tuple()
295 else:
296 value = map(transform, value.split(";"))
297
298 return super().__new__(cls, value)
299
300 def __repr__(self):
301 return repr(list(self))
302
303 def __new__(cls, value=tuple()):
304 value = tuple(value)
305 if len(value) == 0:
306 value = (("0",) * 4)
307 return super().__new__(cls, map(cls.Extra, value))
308
309 def __repr__(self):
310 return repr({index:self[index] for index in range(0, 4)})
311
312 name: str
313 ptype: _SVPtype = _SVPtype.NONE
314 etype: _SVEtype = _SVEtype.NONE
315 in1: _In1Sel = _In1Sel.NONE
316 in2: _In2Sel = _In2Sel.NONE
317 in3: _In3Sel = _In3Sel.NONE
318 out: _OutSel = _OutSel.NONE
319 out2: _OutSel = _OutSel.NONE
320 cr_in: _CRInSel = _CRInSel.NONE
321 cr_out: _CROutSel = _CROutSel.NONE
322 extra: ExtraMap = ExtraMap()
323 conditions: str = ""
324 mode: _SVMode = _SVMode.NORMAL
325
326 __KEYMAP = {
327 "insn": "name",
328 "CONDITIONS": "conditions",
329 "Ptype": "ptype",
330 "Etype": "etype",
331 "CR in": "cr_in",
332 "CR out": "cr_out",
333 }
334
335 @classmethod
336 def CSV(cls, record):
337 for key in ("in1", "in2", "in3", "out", "out2", "CR in", "CR out"):
338 value = record[key]
339 if value == "0":
340 record[key] = "NONE"
341
342 extra = []
343 for idx in range(0, 4):
344 extra.append(record.pop(f"{idx}"))
345
346 record["extra"] = cls.ExtraMap(extra)
347
348 return dataclass(cls, record, keymap=cls.__KEYMAP)
349
350 @_functools.lru_cache(maxsize=None)
351 def extra_idx(self, key):
352 extra_idx = (
353 _SVExtra.Idx0,
354 _SVExtra.Idx1,
355 _SVExtra.Idx2,
356 _SVExtra.Idx3,
357 )
358
359 if key not in frozenset({
360 "in1", "in2", "in3", "cr_in",
361 "out", "out2", "cr_out",
362 }):
363 raise KeyError(key)
364
365 sel = getattr(self, key)
366 if sel is _CRInSel.BA_BB:
367 return _SVExtra.Idx_1_2
368 reg = _SVExtraReg(sel)
369 if reg is _SVExtraReg.NONE:
370 return _SVExtra.NONE
371
372 extra_map = {
373 _SVExtraRegType.SRC: {},
374 _SVExtraRegType.DST: {},
375 }
376 for index in range(0, 4):
377 for entry in self.extra[index]:
378 extra_map[entry.regtype][entry.reg] = extra_idx[index]
379
380 for regtype in (_SVExtraRegType.SRC, _SVExtraRegType.DST):
381 extra = extra_map[regtype].get(reg, _SVExtra.NONE)
382 if extra is not _SVExtra.NONE:
383 return extra
384
385 return _SVExtra.NONE
386
387 extra_idx_in1 = property(_functools.partial(extra_idx, key="in1"))
388 extra_idx_in2 = property(_functools.partial(extra_idx, key="in2"))
389 extra_idx_in3 = property(_functools.partial(extra_idx, key="in3"))
390 extra_idx_out = property(_functools.partial(extra_idx, key="out"))
391 extra_idx_out2 = property(_functools.partial(extra_idx, key="out2"))
392 extra_idx_cr_in = property(_functools.partial(extra_idx, key="cr_in"))
393 extra_idx_cr_out = property(_functools.partial(extra_idx, key="cr_out"))
394
395 @_functools.lru_cache(maxsize=None)
396 def extra_reg(self, key):
397 return _SVExtraReg(getattr(self, key))
398
399 extra_reg_in1 = property(_functools.partial(extra_reg, key="in1"))
400 extra_reg_in2 = property(_functools.partial(extra_reg, key="in2"))
401 extra_reg_in3 = property(_functools.partial(extra_reg, key="in3"))
402 extra_reg_out = property(_functools.partial(extra_reg, key="out"))
403 extra_reg_out2 = property(_functools.partial(extra_reg, key="out2"))
404 extra_reg_cr_in = property(_functools.partial(extra_reg, key="cr_in"))
405 extra_reg_cr_out = property(_functools.partial(extra_reg, key="cr_out"))
406
407
408 class BitSel:
409 def __init__(self, value=(0, 32)):
410 if isinstance(value, str):
411 (start, end) = map(int, value.split(":"))
412 else:
413 (start, end) = value
414 if start < 0 or end < 0 or start >= end:
415 raise ValueError(value)
416
417 self.__start = start
418 self.__end = end
419
420 return super().__init__()
421
422 def __repr__(self):
423 return f"[{self.__start}:{self.__end}]"
424
425 def __iter__(self):
426 yield from range(self.start, (self.end + 1))
427
428 @property
429 def start(self):
430 return self.__start
431
432 @property
433 def end(self):
434 return self.__end
435
436
437 @_dataclasses.dataclass(eq=True, frozen=True)
438 class Section:
439 class Mode(_enum.Enum):
440 INTEGER = _enum.auto()
441 PATTERN = _enum.auto()
442
443 @classmethod
444 def _missing_(cls, value):
445 if isinstance(value, str):
446 return cls[value.upper()]
447 return super()._missing_(value)
448
449 class Suffix(int):
450 def __new__(cls, value=None):
451 if isinstance(value, str):
452 if value.upper() == "NONE":
453 value = None
454 else:
455 value = int(value, 0)
456 if value is None:
457 value = 0
458
459 return super().__new__(cls, value)
460
461 def __str__(self):
462 return repr(self)
463
464 def __repr__(self):
465 return (bin(self) if self else "None")
466
467 path: _pathlib.Path
468 opcode: Opcode
469 bitsel: BitSel
470 suffix: Suffix
471 mode: Mode
472
473 @classmethod
474 def CSV(cls, record):
475 return dataclass(cls, record)
476
477
478 class Fields:
479 def __init__(self, items):
480 if isinstance(items, dict):
481 items = items.items()
482
483 def transform(item):
484 (name, bitrange) = item
485 return (name, list(bitrange.values()))
486
487 self.__mapping = dict(map(transform, items))
488
489 return super().__init__()
490
491 def __repr__(self):
492 return repr(self.__mapping)
493
494 def __iter__(self):
495 yield from self.__mapping.items()
496
497 def __contains__(self, key):
498 return self.__mapping.__contains__(key)
499
500 def __getitem__(self, key):
501 return self.__mapping.get(key, None)
502
503
504 @_dataclasses.dataclass(eq=True, frozen=True)
505 class Operand:
506 name: str
507
508 def disassemble(self, insn, record, verbose=False):
509 raise NotImplementedError
510
511
512 @_dataclasses.dataclass(eq=True, frozen=True)
513 class DynamicOperand(Operand):
514 def disassemble(self, insn, record, verbose=False):
515 span = record.fields[self.name]
516 value = insn[span]
517 if verbose:
518 yield f"{int(value):0{value.bits}b}"
519 yield repr(span)
520 else:
521 yield str(int(value))
522
523
524 @_dataclasses.dataclass(eq=True, frozen=True)
525 class DynamicOperandReg(DynamicOperand):
526 @property
527 def extra_reg(self):
528 return _SVExtraReg(self.name)
529
530 def extra_idx(self, record):
531 for key in frozenset({
532 "in1", "in2", "in3", "cr_in",
533 "out", "out2", "cr_out",
534 }):
535 if self.extra_reg is record.svp64.extra_reg(key):
536 return record.extra_idx(key)
537
538 return _SVExtra.NONE
539
540
541 @_dataclasses.dataclass(eq=True, frozen=True)
542 class ImmediateOperand(DynamicOperand):
543 pass
544
545
546 @_dataclasses.dataclass(eq=True, frozen=True)
547 class StaticOperand(Operand):
548 value: int
549
550 def disassemble(self, insn, record, verbose=False):
551 span = record.fields[self.name]
552 value = insn[span]
553 if verbose:
554 yield f"{int(value):0{value.bits}b}"
555 yield repr(span)
556 else:
557 yield str(int(value))
558
559
560 @_dataclasses.dataclass(eq=True, frozen=True)
561 class DynamicOperandTargetAddrLI(DynamicOperandReg):
562 @property
563 def name(self):
564 return "LI"
565
566 @name.setter
567 def name(self, _):
568 pass
569
570 def disassemble(self, insn, record, verbose=False):
571 span = record.fields["LI"]
572 value = insn[span]
573 if verbose:
574 yield f"{int(value):0{value.bits}b}"
575 yield repr(span)
576 yield "target_addr = EXTS(LI || 0b00))"
577 else:
578 yield hex(int(_selectconcat(value,
579 _SelectableInt(value=0b00, bits=2))))
580
581
582 class DynamicOperandTargetAddrBD(DynamicOperand):
583 @property
584 def name(self):
585 return "BD"
586
587 @name.setter
588 def name(self, _):
589 pass
590
591 def disassemble(self, insn, record, verbose=False):
592 span = record.fields["BD"]
593 value = insn[span]
594 if verbose:
595 yield f"{int(value):0{value.bits}b}"
596 yield repr(span)
597 yield "target_addr = EXTS(BD || 0b00))"
598 else:
599 yield hex(int(_selectconcat(value,
600 _SelectableInt(value=0b00, bits=2))))
601
602
603 @_dataclasses.dataclass(eq=True, frozen=True)
604 class DynamicOperandGPR(DynamicOperandReg):
605 def disassemble(self, insn, record, verbose=False):
606 span = record.fields[self.name]
607 value = insn[span]
608 if verbose:
609 yield f"{int(value):0{value.bits}b}"
610 yield repr(span)
611 if isinstance(insn, SVP64Instruction):
612 extra_idx = self.extra_idx(record)
613 if record.etype is _SVEtype.NONE:
614 yield f"extra[none]"
615 else:
616 etype = repr(record.etype).lower()
617 yield f"{etype}{extra_idx!r}"
618 else:
619 yield f"r{str(int(value))}"
620
621
622 @_dataclasses.dataclass(eq=True, frozen=True)
623 class DynamicOperandFPR(DynamicOperandReg):
624 def disassemble(self, insn, record, verbose=False):
625 span = record.fields[self.name]
626 value = insn[span]
627 if verbose:
628 yield f"{int(value):0{value.bits}b}"
629 yield repr(span)
630 if isinstance(insn, SVP64Instruction):
631 extra_idx = self.extra_idx(record)
632 if record.etype is _SVEtype.NONE:
633 yield f"extra[none]"
634 else:
635 etype = repr(record.etype).lower()
636 yield f"{etype}{extra_idx!r}"
637 else:
638 yield f"f{str(int(value))}"
639
640
641 class Operands(tuple):
642 def __new__(cls, insn, iterable):
643 branches = {
644 "b": {"target_addr": DynamicOperandTargetAddrLI},
645 "ba": {"target_addr": DynamicOperandTargetAddrLI},
646 "bl": {"target_addr": DynamicOperandTargetAddrLI},
647 "bla": {"target_addr": DynamicOperandTargetAddrLI},
648 "bc": {"target_addr": DynamicOperandTargetAddrBD},
649 "bca": {"target_addr": DynamicOperandTargetAddrBD},
650 "bcl": {"target_addr": DynamicOperandTargetAddrBD},
651 "bcla": {"target_addr": DynamicOperandTargetAddrBD},
652 }
653
654 operands = []
655 for operand in iterable:
656 dynamic_cls = DynamicOperand
657 static_cls = StaticOperand
658
659 if "=" in operand:
660 (name, value) = operand.split("=")
661 operand = static_cls(name=name, value=int(value))
662 operands.append(operand)
663 else:
664 if operand.endswith(")"):
665 operand = operand.replace("(", " ").replace(")", "")
666 (immediate, _, operand) = operand.partition(" ")
667 else:
668 immediate = None
669
670 if immediate is not None:
671 operands.append(ImmediateOperand(name=immediate))
672
673 if insn in branches and operand in branches[insn]:
674 dynamic_cls = branches[insn][operand]
675
676 if operand in _RegType.__members__:
677 regtype = _RegType[operand]
678 if regtype is _RegType.GPR:
679 dynamic_cls = DynamicOperandGPR
680 elif regtype is _RegType.FPR:
681 dynamic_cls = DynamicOperandFPR
682
683 operand = dynamic_cls(name=operand)
684 operands.append(operand)
685
686 return super().__new__(cls, operands)
687
688 def __contains__(self, key):
689 return self.__getitem__(key) is not None
690
691 def __getitem__(self, key):
692 for operand in self:
693 if operand.name == key:
694 return operand
695
696 return None
697
698 @property
699 def dynamic(self):
700 for operand in self:
701 if isinstance(operand, DynamicOperand):
702 yield operand
703
704 @property
705 def static(self):
706 for operand in self:
707 if isinstance(operand, StaticOperand):
708 yield operand
709
710
711 @_functools.total_ordering
712 @_dataclasses.dataclass(eq=True, frozen=True)
713 class Record:
714 name: str
715 section: Section
716 ppc: PPCRecord
717 fields: Fields
718 operands: Operands
719 svp64: SVP64Record = None
720
721 def __lt__(self, other):
722 if not isinstance(other, Record):
723 return NotImplemented
724 return (self.opcode < other.opcode)
725
726 @cached_property
727 def opcode(self):
728 fields = []
729 if self.section.opcode:
730 fields += [(self.section.opcode.value, BitSel((0, 5)))]
731 fields += [(self.ppc.opcode.value, self.section.bitsel)]
732 else:
733 fields += [(self.ppc.opcode.value, self.section.bitsel)]
734
735 for operand in self.operands.static:
736 fields += [(operand.value, self.fields[operand.name])]
737
738 return FieldsOpcode(fields)
739
740 @property
741 def function(self):
742 return self.ppc.function
743
744 @property
745 def in1(self):
746 return self.ppc.in1
747
748 @property
749 def in2(self):
750 return self.ppc.in2
751
752 @property
753 def in3(self):
754 return self.ppc.in3
755
756 @property
757 def out(self):
758 return self.ppc.out
759
760 @property
761 def out2(self):
762 if self.svp64 is None:
763 return _OutSel.NONE
764 return self.ppc.out
765
766 @property
767 def cr_in(self):
768 return self.ppc.cr_in
769
770 @property
771 def cr_out(self):
772 return self.ppc.cr_out
773
774 ptype = property(lambda self: self.svp64.ptype)
775 etype = property(lambda self: self.svp64.etype)
776
777 def extra_idx(self, key):
778 return self.svp64.extra_idx(key)
779
780 extra_idx_in1 = property(lambda self: self.svp64.extra_idx_in1)
781 extra_idx_in2 = property(lambda self: self.svp64.extra_idx_in2)
782 extra_idx_in3 = property(lambda self: self.svp64.extra_idx_in3)
783 extra_idx_out = property(lambda self: self.svp64.extra_idx_out)
784 extra_idx_out2 = property(lambda self: self.svp64.extra_idx_out2)
785 extra_idx_cr_in = property(lambda self: self.svp64.extra_idx_cr_in)
786 extra_idx_cr_out = property(lambda self: self.svp64.extra_idx_cr_out)
787
788
789 class Instruction(_Mapping):
790 @classmethod
791 def integer(cls, value=0, bits=None, byteorder="little"):
792 if isinstance(value, (int, bytes)) and not isinstance(bits, int):
793 raise ValueError(bits)
794
795 if isinstance(value, bytes):
796 if ((len(value) * 8) != bits):
797 raise ValueError(f"bit length mismatch")
798 value = int.from_bytes(value, byteorder=byteorder)
799
800 if isinstance(value, int):
801 value = _SelectableInt(value=value, bits=bits)
802 elif isinstance(value, Instruction):
803 value = value.storage
804
805 if not isinstance(value, _SelectableInt):
806 raise ValueError(value)
807 if bits is None:
808 bits = len(cls)
809 if len(value) != bits:
810 raise ValueError(value)
811
812 value = _SelectableInt(value=value, bits=bits)
813
814 return cls(storage=value)
815
816 def __hash__(self):
817 return hash(int(self))
818
819 def record(self, db):
820 record = db[self]
821 if record is None:
822 raise KeyError(self)
823 return record
824
825 def disassemble(self, db, byteorder="little", verbose=False):
826 raise NotImplementedError
827
828
829 class WordInstruction(Instruction):
830 _: _Field = range(0, 32)
831 po: _Field = range(0, 6)
832
833 @classmethod
834 def integer(cls, value, byteorder="little"):
835 return super().integer(bits=32, value=value, byteorder=byteorder)
836
837 @property
838 def binary(self):
839 bits = []
840 for idx in range(32):
841 bit = int(self[idx])
842 bits.append(bit)
843 return "".join(map(str, bits))
844
845 def spec(self, db):
846 record = self.record(db=db)
847
848 immediate = ""
849 dynamic_operands = []
850 for operand in record.operands.dynamic:
851 name = operand.name
852 if immediate:
853 name = f"{immediate}({name})"
854 immediate = ""
855 if isinstance(operand, ImmediateOperand):
856 immediate = operand.name
857 if not immediate:
858 dynamic_operands.append(name)
859
860 static_operands = []
861 for operand in record.operands.static:
862 static_operands.append(f"{operand.name}={operand.value}")
863
864 operands = ""
865 if dynamic_operands:
866 operands += f" {','.join(dynamic_operands)}"
867 if static_operands:
868 operands += f" ({' '.join(static_operands)})"
869
870 return f"{record.name}{operands}"
871
872 def opcode(self, db):
873 record = self.record(db=db)
874 return f"0x{record.opcode.value:08x}"
875
876 def mask(self, db):
877 record = self.record(db=db)
878 return f"0x{record.opcode.mask:08x}"
879
880 def disassemble(self, db, byteorder="little", verbose=False):
881 integer = int(self)
882 blob = integer.to_bytes(length=4, byteorder=byteorder)
883 blob = " ".join(map(lambda byte: f"{byte:02x}", blob))
884
885 record = self.record(db=db)
886 if record is None:
887 yield f"{blob} .long 0x{integer:08x}"
888 return
889
890 operands = []
891 for operand in record.operands.dynamic:
892 operand = " ".join(operand.disassemble(insn=self,
893 record=record, verbose=False))
894 operands.append(operand)
895 if operands:
896 operands = ",".join(operands)
897 operands = f" {operands}"
898 else:
899 operands = ""
900
901 yield f"{blob} {record.name}{operands}"
902
903 if verbose:
904 indent = (" " * 4)
905 binary = self.binary
906 spec = self.spec(db=db)
907 opcode = self.opcode(db=db)
908 mask = self.mask(db=db)
909 yield f"{indent}spec"
910 yield f"{indent}{indent}{spec}"
911 yield f"{indent}binary"
912 yield f"{indent}{indent}[0:8] {binary[0:8]}"
913 yield f"{indent}{indent}[8:16] {binary[8:16]}"
914 yield f"{indent}{indent}[16:24] {binary[16:24]}"
915 yield f"{indent}{indent}[24:32] {binary[24:32]}"
916 yield f"{indent}opcode"
917 yield f"{indent}{indent}{opcode}"
918 yield f"{indent}mask"
919 yield f"{indent}{indent}{mask}"
920 for operand in record.operands:
921 name = operand.name
922 yield f"{indent}{name}"
923 parts = operand.disassemble(insn=self,
924 record=record, verbose=True)
925 for part in parts:
926 yield f"{indent}{indent}{part}"
927 yield ""
928
929
930 class PrefixedInstruction(Instruction):
931 class Prefix(WordInstruction.remap(range(0, 32))):
932 pass
933
934 class Suffix(WordInstruction.remap(range(32, 64))):
935 pass
936
937 _: _Field = range(64)
938 prefix: Prefix
939 suffix: Suffix
940 po: Suffix.po
941
942 @classmethod
943 def integer(cls, value, byteorder="little"):
944 return super().integer(bits=64, value=value, byteorder=byteorder)
945
946 @classmethod
947 def pair(cls, prefix=0, suffix=0, byteorder="little"):
948 def transform(value):
949 return WordInstruction.integer(value=value,
950 byteorder=byteorder)[0:32]
951
952 (prefix, suffix) = map(transform, (prefix, suffix))
953 value = _selectconcat(prefix, suffix)
954
955 return super().integer(value=value)
956
957
958 class Mode(_Mapping):
959 _: _Field = range(0, 5)
960 sel: _Field = range(0, 2)
961
962
963 class NormalMode(Mode):
964 class simple(Mode):
965 """simple mode"""
966 dz: Mode[3]
967 sz: Mode[4]
968
969 class smr(Mode):
970 """scalar reduce mode (mapreduce), SUBVL=1"""
971 RG: Mode[4]
972
973 class pmr(Mode):
974 """parallel reduce mode (mapreduce), SUBVL=1"""
975 pass
976
977 class svmr(Mode):
978 """subvector reduce mode, SUBVL>1"""
979 SVM: Mode[3]
980
981 class pu(Mode):
982 """Pack/Unpack mode, SUBVL>1"""
983 SVM: Mode[3]
984
985 class ffrc1(Mode):
986 """Rc=1: ffirst CR sel"""
987 inv: Mode[2]
988 CRbit: Mode[3, 4]
989
990 class ffrc0(Mode):
991 """Rc=0: ffirst z/nonz"""
992 inv: Mode[2]
993 VLi: Mode[3]
994 RC1: Mode[4]
995
996 class sat(Mode):
997 """sat mode: N=0/1 u/s, SUBVL=1"""
998 N: Mode[2]
999 dz: Mode[3]
1000 sz: Mode[4]
1001
1002 class satx(Mode):
1003 """sat mode: N=0/1 u/s, SUBVL>1"""
1004 N: Mode[2]
1005 zz: Mode[3]
1006 dz: Mode[3]
1007 sz: Mode[3]
1008
1009 class satpu(Mode):
1010 """Pack/Unpack sat mode: N=0/1 u/s, SUBVL>1"""
1011 N: Mode[2]
1012 zz: Mode[3]
1013 dz: Mode[3]
1014 sz: Mode[3]
1015
1016 class prrc1(Mode):
1017 """Rc=1: pred-result CR sel"""
1018 inv: Mode[2]
1019 CRbit: Mode[3, 4]
1020
1021 class prrc0(Mode):
1022 """Rc=0: pred-result z/nonz"""
1023 inv: Mode[2]
1024 zz: Mode[3]
1025 RC1: Mode[4]
1026 dz: Mode[3]
1027 sz: Mode[3]
1028
1029 simple: simple
1030 smr: smr
1031 pmr: pmr
1032 svmr: svmr
1033 pu: pu
1034 ffrc1: ffrc1
1035 ffrc0: ffrc0
1036 sat: sat
1037 satx: satx
1038 satpu: satpu
1039 prrc1: prrc1
1040 prrc0: prrc0
1041
1042
1043 class LDSTImmMode(Mode):
1044 class simple(Mode):
1045 """simple mode"""
1046 zz: Mode[3]
1047 els: Mode[4]
1048 dz: Mode[3]
1049 sz: Mode[3]
1050
1051 class spu(Mode):
1052 """Structured Pack/Unpack"""
1053 zz: Mode[3]
1054 els: Mode[4]
1055 dz: Mode[3]
1056 sz: Mode[3]
1057
1058 class ffrc1(Mode):
1059 """Rc=1: ffirst CR sel"""
1060 inv: Mode[2]
1061 CRbit: Mode[3, 4]
1062
1063 class ffrc0(Mode):
1064 """Rc=0: ffirst z/nonz"""
1065 inv: Mode[2]
1066 els: Mode[3]
1067 RC1: Mode[4]
1068
1069 class sat(Mode):
1070 """sat mode: N=0/1 u/s"""
1071 N: Mode[2]
1072 zz: Mode[3]
1073 els: Mode[4]
1074 dz: Mode[3]
1075 sz: Mode[3]
1076
1077 class prrc1(Mode):
1078 """Rc=1: pred-result CR sel"""
1079 inv: Mode[2]
1080 CRbit: Mode[3, 4]
1081
1082 class prrc0(Mode):
1083 """Rc=0: pred-result z/nonz"""
1084 inv: Mode[2]
1085 els: Mode[3]
1086 RC1: Mode[4]
1087
1088 simple: simple
1089 spu: spu
1090 ffrc1: ffrc1
1091 ffrc0: ffrc0
1092 sat: sat
1093 prrc1: prrc1
1094 prrc0: prrc0
1095
1096
1097 class LDSTIdxMode(Mode):
1098 class simple(Mode):
1099 """simple mode"""
1100 SEA: Mode[2]
1101 sz: Mode[3]
1102 dz: Mode[3]
1103
1104 class stride(Mode):
1105 """strided (scalar only source)"""
1106 SEA: Mode[2]
1107 dz: Mode[3]
1108 sz: Mode[4]
1109
1110 class sat(Mode):
1111 """sat mode: N=0/1 u/s"""
1112 N: Mode[2]
1113 dz: Mode[3]
1114 sz: Mode[4]
1115
1116 class prrc1(Mode):
1117 """Rc=1: pred-result CR sel"""
1118 inv: Mode[2]
1119 CRbit: Mode[3, 4]
1120
1121 class prrc0(Mode):
1122 """Rc=0: pred-result z/nonz"""
1123 inv: Mode[2]
1124 zz: Mode[3]
1125 RC1: Mode[4]
1126 dz: Mode[3]
1127 sz: Mode[3]
1128
1129 simple: simple
1130 stride: stride
1131 sat: sat
1132 prrc1: prrc1
1133 prrc0: prrc0
1134
1135
1136 class RM(_Mapping):
1137 class Mode(Mode):
1138 normal: NormalMode
1139 ldst_imm: LDSTImmMode
1140 ldst_idx: LDSTIdxMode
1141
1142 _: _Field = range(24)
1143 mmode: _Field = (0,)
1144 mask: _Field = range(1, 4)
1145 elwidth: _Field = range(4, 6)
1146 ewsrc: _Field = range(6, 8)
1147 subvl: _Field = range(8, 10)
1148 extra: _Field = range(10, 19)
1149 mode: Mode.remap(range(19, 24))
1150 extra2: _Array[4] = (
1151 range(10, 12),
1152 range(12, 14),
1153 range(14, 16),
1154 range(16, 18),
1155 )
1156 smask: _Field = range(16, 19)
1157 extra3: _Array[3] = (
1158 range(10, 13),
1159 range(13, 16),
1160 range(16, 19),
1161 )
1162
1163
1164 class SVP64Instruction(PrefixedInstruction):
1165 """SVP64 instruction: https://libre-soc.org/openpower/sv/svp64/"""
1166 class Prefix(PrefixedInstruction.Prefix):
1167 id: _Field = (7, 9)
1168 rm: RM.remap((6, 8) + tuple(range(10, 32)))
1169
1170 prefix: Prefix
1171
1172 @property
1173 def binary(self):
1174 bits = []
1175 for idx in range(64):
1176 bit = int(self[idx])
1177 bits.append(bit)
1178 return "".join(map(str, bits))
1179
1180 def spec(self, db):
1181 return f"sv.{self.suffix.spec(db=db)}"
1182
1183 def opcode(self, db):
1184 return self.suffix.opcode(db=db)
1185
1186 def mask(self, db):
1187 return self.suffix.mask(db=db)
1188
1189 def mode(self, db):
1190 record = self.record(db=db)
1191
1192 Rc = False
1193 if record.operands["Rc"] is not None:
1194 Rc = bool(self[record.fields["Rc"]])
1195
1196 record = self.record(db=db)
1197 subvl = self.prefix.rm.subvl
1198 mode = self.prefix.rm.mode
1199 sel = mode.sel
1200
1201 if record.svp64.mode is _SVMode.NORMAL:
1202 mode = mode.normal
1203 if sel == 0b00:
1204 if mode[2] == 0b0:
1205 mode = mode.simple
1206 else:
1207 if subvl == 0b00:
1208 if mode[3] == 0b0:
1209 mode = mode.smr
1210 else:
1211 mode = mode.pmr
1212 else:
1213 if mode[4] == 0b0:
1214 mode = mode.svmr
1215 else:
1216 mode = mode.pu
1217 elif sel == 0b01:
1218 if Rc:
1219 mode = mode.ffrc1
1220 else:
1221 mode = mode.ffrc0
1222 elif sel == 0b10:
1223 if subvl == 0b00:
1224 mode = mode.sat
1225 else:
1226 if mode[4]:
1227 mode = mode.satx
1228 else:
1229 mode = mode.satpu
1230 elif sel == 0b11:
1231 if Rc:
1232 mode = mode.prrc1
1233 else:
1234 mode = mode.prrc0
1235 elif record.svp64.mode is _SVMode.LDST_IMM:
1236 mode = mode.ldst_imm
1237 if sel == 0b00:
1238 if mode[2] == 0b0:
1239 mode = mode.simple
1240 else:
1241 mode = mode.spu
1242 elif sel == 0b01:
1243 if Rc:
1244 mode = mode.ffrc1
1245 else:
1246 mode = mode.ffrc0
1247 elif sel == 0b10:
1248 mode = mode.sat
1249 elif sel == 0b11:
1250 if Rc:
1251 mode = mode.prrc1
1252 else:
1253 mode = mode.prrc0
1254 elif record.svp64.mode is _SVMode.LDST_IMM:
1255 mode = mode.ldst_idx
1256 if mode.sel == 0b00:
1257 mode = mode.simple
1258 elif mode.sel == 0b01:
1259 mode = mode.stride
1260 elif mode.sel == 0b10:
1261 mode = mode.sat
1262 elif mode.sel == 0b11:
1263 if Rc:
1264 mode = mode.prrc1
1265 else:
1266 mode = mode.prrc0
1267
1268 modes = {
1269 NormalMode.simple: "normal: simple",
1270 NormalMode.smr: "normal: smr",
1271 NormalMode.pmr: "normal: pmr",
1272 NormalMode.svmr: "normal: svmr",
1273 NormalMode.pu: "normal: pu",
1274 NormalMode.ffrc1: "normal: ffrc1",
1275 NormalMode.ffrc0: "normal: ffrc0",
1276 NormalMode.sat: "normal: sat",
1277 NormalMode.satx: "normal: satx",
1278 NormalMode.satpu: "normal: satpu",
1279 NormalMode.prrc1: "normal: prrc1",
1280 NormalMode.prrc0: "normal: prrc0",
1281 LDSTImmMode.simple: "ld/st imm: simple",
1282 LDSTImmMode.spu: "ld/st imm: spu",
1283 LDSTImmMode.ffrc1: "ld/st imm: ffrc1",
1284 LDSTImmMode.ffrc0: "ld/st imm: ffrc0",
1285 LDSTImmMode.sat: "ld/st imm: sat",
1286 LDSTImmMode.prrc1: "ld/st imm: prrc1",
1287 LDSTImmMode.prrc0: "ld/st imm: prrc0",
1288 LDSTIdxMode.simple: "ld/st idx simple",
1289 LDSTIdxMode.stride: "ld/st idx stride",
1290 LDSTIdxMode.sat: "ld/st idx sat",
1291 LDSTIdxMode.prrc1: "ld/st idx prrc1",
1292 LDSTIdxMode.prrc0: "ld/st idx prrc0",
1293 }
1294 for (cls, desc) in modes.items():
1295 if isinstance(mode, cls):
1296 return (mode, desc)
1297
1298 if record.svp64.mode is _SVMode.BRANCH:
1299 return (self.prefix.rm.mode, "branch")
1300
1301 raise ValueError(self)
1302
1303 def disassemble(self, db, byteorder="little", verbose=False):
1304 integer_prefix = int(self.prefix)
1305 blob_prefix = integer_prefix.to_bytes(length=4, byteorder=byteorder)
1306 blob_prefix = " ".join(map(lambda byte: f"{byte:02x}", blob_prefix))
1307
1308 integer_suffix = int(self.suffix)
1309 blob_suffix = integer_suffix.to_bytes(length=4, byteorder=byteorder)
1310 blob_suffix = " ".join(map(lambda byte: f"{byte:02x}", blob_suffix))
1311
1312 record = self.record(db=db)
1313 if record is None or record.svp64 is None:
1314 yield f"{blob_prefix} .long 0x{int(self.prefix):08x}"
1315 yield f"{blob_suffix} .long 0x{int(self.suffix):08x}"
1316 return
1317
1318 yield f"{blob_prefix} sv.{record.name}"
1319 yield f"{blob_suffix}"
1320
1321 (mode, mode_desc) = self.mode(db=db)
1322
1323 if verbose:
1324 indent = (" " * 4)
1325 binary = self.binary
1326 spec = self.spec(db=db)
1327 opcode = self.opcode(db=db)
1328 mask = self.mask(db=db)
1329 yield f"{indent}spec"
1330 yield f"{indent}{indent}{spec}"
1331 yield f"{indent}binary"
1332 yield f"{indent}{indent}[0:8] {binary[0:8]}"
1333 yield f"{indent}{indent}[8:16] {binary[8:16]}"
1334 yield f"{indent}{indent}[16:24] {binary[16:24]}"
1335 yield f"{indent}{indent}[24:32] {binary[24:32]}"
1336 yield f"{indent}{indent}[32:40] {binary[32:40]}"
1337 yield f"{indent}{indent}[40:48] {binary[40:48]}"
1338 yield f"{indent}{indent}[48:56] {binary[48:56]}"
1339 yield f"{indent}{indent}[56:64] {binary[56:64]}"
1340 yield f"{indent}opcode"
1341 yield f"{indent}{indent}{opcode}"
1342 yield f"{indent}mask"
1343 yield f"{indent}{indent}{mask}"
1344 for operand in record.operands:
1345 name = operand.name
1346 yield f"{indent}{name}"
1347 parts = operand.disassemble(insn=self,
1348 record=record, verbose=True)
1349 for part in parts:
1350 yield f"{indent}{indent}{part}"
1351
1352 yield f"{indent}mode"
1353 yield f"{indent}{indent}{mode_desc}"
1354 yield ""
1355
1356
1357 def parse(stream, factory):
1358 def match(entry):
1359 return ("TODO" not in frozenset(entry.values()))
1360
1361 lines = filter(lambda line: not line.strip().startswith("#"), stream)
1362 entries = _csv.DictReader(lines)
1363 entries = filter(match, entries)
1364 return tuple(map(factory, entries))
1365
1366
1367 class MarkdownDatabase:
1368 def __init__(self):
1369 db = {}
1370 for (name, desc) in _ISA():
1371 operands = []
1372 if desc.regs:
1373 (dynamic, *static) = desc.regs
1374 operands.extend(dynamic)
1375 operands.extend(static)
1376 db[name] = Operands(insn=name, iterable=operands)
1377 self.__db = db
1378 return super().__init__()
1379
1380 def __iter__(self):
1381 yield from self.__db.items()
1382
1383 def __getitem__(self, key):
1384 return self.__db.__getitem__(key)
1385
1386
1387 class FieldsDatabase:
1388 def __init__(self):
1389 db = {}
1390 df = _DecodeFields()
1391 df.create_specs()
1392 for (form, fields) in df.instrs.items():
1393 if form in {"DQE", "TX"}:
1394 continue
1395 if form == "all":
1396 form = "NONE"
1397 db[_Form[form]] = Fields(fields)
1398
1399 self.__db = db
1400
1401 return super().__init__()
1402
1403 def __getitem__(self, key):
1404 return self.__db.__getitem__(key)
1405
1406
1407 class PPCDatabase:
1408 def __init__(self, root, mdwndb):
1409 # The code below groups the instructions by section:identifier.
1410 # We use the comment as an identifier, there's nothing better.
1411 # The point is to capture different opcodes for the same instruction.
1412 dd = _collections.defaultdict
1413 records = dd(lambda: dd(set))
1414 path = (root / "insndb.csv")
1415 with open(path, "r", encoding="UTF-8") as stream:
1416 for section in parse(stream, Section.CSV):
1417 path = (root / section.path)
1418 opcode_cls = {
1419 section.Mode.INTEGER: IntegerOpcode,
1420 section.Mode.PATTERN: PatternOpcode,
1421 }[section.mode]
1422 factory = _functools.partial(
1423 PPCRecord.CSV, opcode_cls=opcode_cls)
1424 with open(path, "r", encoding="UTF-8") as stream:
1425 for insn in parse(stream, factory):
1426 records[section][insn.comment].add(insn)
1427
1428 db = dd(set)
1429 for (section, group) in records.items():
1430 for records in group.values():
1431 db[section].add(PPCMultiRecord(records))
1432
1433 self.__db = db
1434 self.__mdwndb = mdwndb
1435
1436 return super().__init__()
1437
1438 def __getitem__(self, key):
1439 def exact_match(key, record):
1440 for name in record.names:
1441 if name == key:
1442 return True
1443
1444 return False
1445
1446 def Rc_match(key, record):
1447 if not key.endswith("."):
1448 return False
1449
1450 if not record.Rc is _RCOE.RC:
1451 return False
1452
1453 return exact_match(key[:-1], record)
1454
1455 def LK_match(key, record):
1456 if not key.endswith("l"):
1457 return False
1458
1459 if "lk" not in record.flags:
1460 return False
1461
1462 return exact_match(key[:-1], record)
1463
1464 def AA_match(key, record):
1465 if not key.endswith("a"):
1466 return False
1467
1468 if record.intop not in {_MicrOp.OP_B, _MicrOp.OP_BC}:
1469 return False
1470
1471 if self.__mdwndb[key]["AA"] is None:
1472 return False
1473
1474 return (exact_match(key[:-1], record) or
1475 LK_match(key[:-1], record))
1476
1477 for (section, records) in self.__db.items():
1478 for record in records:
1479 if exact_match(key, record):
1480 return (section, record)
1481
1482 for record in records:
1483 if (Rc_match(key, record) or
1484 LK_match(key, record) or
1485 AA_match(key, record)):
1486 return (section, record)
1487
1488 return (None, None)
1489
1490
1491 class SVP64Database:
1492 def __init__(self, root, ppcdb):
1493 db = set()
1494 pattern = _re.compile(r"^(?:LDST)?RM-(1P|2P)-.*?\.csv$")
1495 for (prefix, _, names) in _os.walk(root):
1496 prefix = _pathlib.Path(prefix)
1497 for name in filter(lambda name: pattern.match(name), names):
1498 path = (prefix / _pathlib.Path(name))
1499 with open(path, "r", encoding="UTF-8") as stream:
1500 db.update(parse(stream, SVP64Record.CSV))
1501
1502 self.__db = {record.name:record for record in db}
1503 self.__ppcdb = ppcdb
1504
1505 return super().__init__()
1506
1507 def __getitem__(self, key):
1508 (_, record) = self.__ppcdb[key]
1509 if record is None:
1510 return None
1511
1512 for name in record.names:
1513 record = self.__db.get(name, None)
1514 if record is not None:
1515 return record
1516
1517 return None
1518
1519
1520 class Database:
1521 def __init__(self, root):
1522 root = _pathlib.Path(root)
1523
1524 mdwndb = MarkdownDatabase()
1525 fieldsdb = FieldsDatabase()
1526 ppcdb = PPCDatabase(root=root, mdwndb=mdwndb)
1527 svp64db = SVP64Database(root=root, ppcdb=ppcdb)
1528
1529 db = set()
1530 for (name, operands) in mdwndb:
1531 (section, ppc) = ppcdb[name]
1532 if ppc is None:
1533 continue
1534 svp64 = svp64db[name]
1535 fields = fieldsdb[ppc.form]
1536 record = Record(name=name,
1537 section=section, ppc=ppc, svp64=svp64,
1538 operands=operands, fields=fields)
1539 db.add(record)
1540
1541 self.__db = tuple(sorted(db))
1542
1543 return super().__init__()
1544
1545 def __repr__(self):
1546 return repr(self.__db)
1547
1548 def __iter__(self):
1549 yield from self.__db
1550
1551 @_functools.lru_cache(maxsize=None)
1552 def __contains__(self, key):
1553 return self.__getitem__(key) is not None
1554
1555 @_functools.lru_cache(maxsize=None)
1556 def __getitem__(self, key):
1557 if isinstance(key, (int, Instruction)):
1558 key = int(key)
1559 for record in self:
1560 opcode = record.opcode
1561 if ((opcode.value & opcode.mask) ==
1562 (key & opcode.mask)):
1563 return record
1564 return None
1565 elif isinstance(key, Opcode):
1566 for record in self:
1567 if record.opcode == key:
1568 return record
1569 elif isinstance(key, str):
1570 for record in self:
1571 if record.name == key:
1572 return record
1573 return None