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