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