1 import collections
as _collections
3 import dataclasses
as _dataclasses
5 import functools
as _functools
7 import pathlib
as _pathlib
11 from functools
import cached_property
13 from cached_property
import cached_property
15 from openpower
.decoder
.power_enums
import (
16 Function
as _Function
,
23 CROutSel
as _CROutSel
,
25 LDSTMode
as _LDSTMode
,
33 SVExtraRegType
as _SVExtraRegType
,
34 SVExtraReg
as _SVExtraReg
,
36 from openpower
.decoder
.selectable_int
import (
37 SelectableInt
as _SelectableInt
,
38 selectconcat
as _selectconcat
,
40 from openpower
.decoder
.power_fields
import (
43 DecodeFields
as _DecodeFields
,
45 from openpower
.decoder
.pseudo
.pagereader
import ISA
as _ISA
48 def dataclass(cls
, record
, keymap
=None, typemap
=None):
52 typemap
= {field
.name
:field
.type for field
in _dataclasses
.fields(cls
)}
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"):
64 record
= dict(map(transform
, record
.items()))
65 for key
in frozenset(record
.keys()):
72 @_functools.total_ordering
73 @_dataclasses.dataclass(eq
=True, frozen
=True)
77 if self
.bit_length() <= 32:
78 return f
"0x{self:08x}"
80 return f
"0x{self:016x}"
84 if self
.bit_length() <= 32:
85 return f
"0x{self:08x}"
87 return f
"0x{self:016x}"
92 def __lt__(self
, other
):
93 if not isinstance(other
, Opcode
):
95 return ((self
.value
, self
.mask
) < (other
.value
, other
.mask
))
97 def __post_init__(self
):
98 (value
, mask
) = (self
.value
, self
.mask
)
100 if isinstance(value
, Opcode
):
102 raise ValueError(mask
)
103 (value
, mask
) = (value
.value
, value
.mask
)
104 elif isinstance(value
, str):
106 raise ValueError(mask
)
107 value
= int(value
, 0)
109 if not isinstance(value
, int):
110 raise ValueError(value
)
113 if not isinstance(mask
, int):
114 raise ValueError(mask
)
116 object.__setattr
__(self
, "value", self
.__class
__.Value(value
))
117 object.__setattr
__(self
, "mask", self
.__class
__.Mask(mask
))
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)
127 class PatternOpcode(Opcode
):
128 def __init__(self
, value
):
129 (pattern
, value
, mask
) = (value
, 0, 0)
131 for symbol
in pattern
:
132 if symbol
not in {"0", "1", "-"}:
133 raise ValueError(pattern
)
134 value |
= (symbol
== "1")
135 mask |
= (symbol
!= "-")
141 return super().__init
__(value
=value
, mask
=mask
)
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
)
156 (value
, mask
) = _functools
.reduce(field
, fields
, (0, 0))
158 return super().__init
__(value
=value
, mask
=mask
)
161 @_dataclasses.dataclass(eq
=True, frozen
=True)
163 class FlagsMeta(type):
178 class Flags(frozenset, metaclass
=FlagsMeta
):
179 def __new__(cls
, flags
=frozenset()):
180 flags
= frozenset(flags
)
181 diff
= (flags
- frozenset(cls
))
183 raise ValueError(flags
)
184 return super().__new
__(cls
, flags
)
188 flags
: Flags
= Flags()
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
204 unofficial
: bool = False
208 "internal op": "intop",
212 "ldst len": "ldst_len",
213 "CONDITIONS": "conditions",
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
222 for flag
in frozenset(PPCRecord
.Flags
):
223 if bool(record
.pop(flag
, "")):
225 record
["flags"] = PPCRecord
.Flags(flags
)
227 return dataclass(cls
, record
, keymap
=PPCRecord
.__KEYMAP
, typemap
=typemap
)
231 return frozenset(self
.comment
.split("=")[-1].split("/"))
234 @_dataclasses.dataclass(eq
=True, frozen
=True)
236 class ExtraMap(tuple):
238 @_dataclasses.dataclass(eq
=True, frozen
=True)
240 regtype
: _SVExtraRegType
= _SVExtraRegType
.NONE
241 reg
: _SVExtraReg
= _SVExtraReg
.NONE
244 return f
"{self.regtype.value}:{self.reg.name}"
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
)
257 value
= map(transform
, value
.split(";"))
259 return super().__new
__(cls
, value
)
262 return repr(list(self
))
264 def __new__(cls
, value
=tuple()):
268 return super().__new
__(cls
, map(cls
.Extra
, value
))
271 return repr({index
:self
[index
] for index
in range(0, 4)})
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()
286 mode
: _SVMode
= _SVMode
.NORMAL
290 "CONDITIONS": "conditions",
299 def CSV(cls
, record
):
300 for key
in ("in1", "in2", "in3", "out", "out2", "CR in", "CR out"):
305 record
["extra"] = cls
.ExtraMap(record
.pop(f
"{index}") for index
in range(0, 4))
307 return dataclass(cls
, record
, keymap
=cls
.__KEYMAP
)
311 def __init__(self
, value
=(0, 32)):
312 if isinstance(value
, str):
313 (start
, end
) = map(int, value
.split(":"))
316 if start
< 0 or end
< 0 or start
>= end
:
317 raise ValueError(value
)
322 return super().__init
__()
325 return f
"[{self.__start}:{self.__end}]"
328 yield from range(self
.start
, (self
.end
+ 1))
339 @_dataclasses.dataclass(eq
=True, frozen
=True)
341 class Mode(_enum
.Enum
):
342 INTEGER
= _enum
.auto()
343 PATTERN
= _enum
.auto()
346 def _missing_(cls
, value
):
347 if isinstance(value
, str):
348 return cls
[value
.upper()]
349 return super()._missing
_(value
)
352 def __new__(cls
, value
=None):
353 if isinstance(value
, str):
354 if value
.upper() == "NONE":
357 value
= int(value
, 0)
361 return super().__new
__(cls
, value
)
367 return (bin(self
) if self
else "None")
376 def CSV(cls
, record
):
377 return dataclass(cls
, record
)
381 def __init__(self
, items
):
382 if isinstance(items
, dict):
383 items
= items
.items()
386 (name
, bitrange
) = item
387 return (name
, tuple(bitrange
.values()))
389 self
.__mapping
= dict(map(transform
, items
))
391 return super().__init
__()
394 return repr(self
.__mapping
)
397 yield from self
.__mapping
.items()
399 def __contains__(self
, key
):
400 return self
.__mapping
.__contains
__(key
)
402 def __getitem__(self
, key
):
403 return self
.__mapping
.get(key
, None)
406 @_dataclasses.dataclass(eq
=True, frozen
=True)
411 class Operands(tuple):
412 @_dataclasses.dataclass(eq
=True, frozen
=True)
413 class DynamicOperand(Operand
):
416 def disassemble(self
, value
, record
):
417 return str(int(value
[record
.fields
[self
.name
]]))
419 @_dataclasses.dataclass(eq
=True, frozen
=True)
420 class StaticOperand(Operand
):
424 @_dataclasses.dataclass(eq
=True, frozen
=True)
425 class DynamicOperandTargetAddrIForm(DynamicOperand
):
426 def disassemble(self
, value
, record
):
427 return hex(int(_selectconcat(
428 value
[record
.fields
["LI"]],
429 _SelectableInt(value
=0b00, bits
=2))))
431 class DynamicOperandTargetAddrBForm(DynamicOperand
):
432 def disassemble(self
, value
, record
):
433 return hex(int(_selectconcat(
434 value
[record
.fields
["BD"]],
435 _SelectableInt(value
=0b00, bits
=2))))
437 def __new__(cls
, insn
, iterable
):
439 "b": {"target_addr": cls
.DynamicOperandTargetAddrIForm
},
440 "ba": {"target_addr": cls
.DynamicOperandTargetAddrIForm
},
441 "bl": {"target_addr": cls
.DynamicOperandTargetAddrIForm
},
442 "bla": {"target_addr": cls
.DynamicOperandTargetAddrIForm
},
443 "bc": {"target_addr": cls
.DynamicOperandTargetAddrBForm
},
444 "bca": {"target_addr": cls
.DynamicOperandTargetAddrBForm
},
445 "bcl": {"target_addr": cls
.DynamicOperandTargetAddrBForm
},
446 "bcla": {"target_addr": cls
.DynamicOperandTargetAddrBForm
},
450 for operand
in iterable
:
451 dynamic_cls
= cls
.DynamicOperand
452 static_cls
= cls
.StaticOperand
455 (name
, value
) = operand
.split("=")
456 operand
= static_cls(name
=name
, value
=int(value
))
458 if insn
in branches
and operand
in branches
[insn
]:
459 dynamic_cls
= branches
[insn
][operand
]
460 operand
= dynamic_cls(name
=operand
)
462 operands
.append(operand
)
464 return super().__new
__(cls
, operands
)
467 @_functools.total_ordering
468 @_dataclasses.dataclass(eq
=True, frozen
=True)
475 svp64
: SVP64Record
= None
484 def __lt__(self
, other
):
485 if not isinstance(other
, Record
):
486 return NotImplemented
487 return (self
.opcode
< other
.opcode
)
490 return f
"{self.__class__.__name__}(name={self.name!r}, opcode={self.opcode})"
495 if self
.section
.opcode
:
496 fields
+= [(self
.section
.opcode
.value
, BitSel((0, 5)))]
497 fields
+= [(self
.ppc
.opcode
.value
, self
.section
.bitsel
)]
499 fields
+= [(self
.ppc
.opcode
.value
, self
.section
.bitsel
)]
501 for operand
in self
.static_operands
:
502 fields
+= [(operand
.value
, self
.fields
[operand
.name
])]
504 return FieldsOpcode(fields
)
508 return self
.ppc
.function
528 if self
.svp64
is None:
534 return self
.ppc
.cr_in
538 return self
.ppc
.cr_out
540 def sv_extra(self
, key
):
541 if key
not in frozenset({
542 "in1", "in2", "in3", "cr_in",
543 "out", "out2", "cr_out",
547 sel
= getattr(self
.svp64
, key
)
548 if sel
is _CRInSel
.BA_BB
:
549 return _SVExtra
.Idx_1_2
550 reg
= _SVExtraReg(sel
)
551 if reg
is _SVExtraReg
.NONE
:
555 _SVExtraRegType
.SRC
: {},
556 _SVExtraRegType
.DST
: {},
558 for index
in range(0, 4):
559 for entry
in self
.svp64
.extra
[index
]:
560 extra_map
[entry
.regtype
][entry
.reg
] = Record
.__EXTRA
[index
]
562 for regtype
in (_SVExtraRegType
.SRC
, _SVExtraRegType
.DST
):
563 extra
= extra_map
[regtype
].get(reg
, _SVExtra
.NONE
)
564 if extra
is not _SVExtra
.NONE
:
569 sv_in1
= property(_functools
.partial(sv_extra
, key
="in1"))
570 sv_in2
= property(_functools
.partial(sv_extra
, key
="in2"))
571 sv_in3
= property(_functools
.partial(sv_extra
, key
="in3"))
572 sv_out
= property(_functools
.partial(sv_extra
, key
="out"))
573 sv_out2
= property(_functools
.partial(sv_extra
, key
="out2"))
574 sv_cr_in
= property(_functools
.partial(sv_extra
, key
="cr_in"))
575 sv_cr_out
= property(_functools
.partial(sv_extra
, key
="cr_out"))
579 if self
.svp64
is None:
581 return self
.svp64
.ptype
585 if self
.svp64
is None:
587 return self
.svp64
.etype
590 def dynamic_operands(self
):
591 for operand
in self
.operands
:
592 if isinstance(operand
, Operands
.DynamicOperand
):
596 def static_operands(self
):
597 for operand
in self
.operands
:
598 if isinstance(operand
, Operands
.StaticOperand
):
602 class Instruction(_Mapping
):
604 def integer(cls
, value
=0, bits
=None, byteorder
="little"):
605 if isinstance(value
, (int, bytes
)) and not isinstance(bits
, int):
606 raise ValueError(bits
)
608 if isinstance(value
, bytes
):
609 if ((len(value
) * 8) != bits
):
610 raise ValueError(f
"bit length mismatch")
611 value
= int.from_bytes(value
, byteorder
=byteorder
)
613 if isinstance(value
, int):
614 value
= _SelectableInt(value
=value
, bits
=bits
)
615 elif isinstance(value
, Instruction
):
616 value
= value
.storage
618 if not isinstance(value
, _SelectableInt
):
619 raise ValueError(value
)
622 if len(value
) != bits
:
623 raise ValueError(value
)
625 value
= _SelectableInt(value
=value
, bits
=bits
)
627 return cls(storage
=value
)
630 return hash(int(self
))
632 def disassemble(self
, db
):
633 raise NotImplementedError
636 class WordInstruction(Instruction
):
637 _
: _Field
= range(0, 32)
638 po
: _Field
= range(0, 6)
641 def integer(cls
, value
, byteorder
="little"):
642 return super().integer(bits
=32, value
=value
, byteorder
=byteorder
)
644 def disassemble(self
, db
):
647 yield f
".long 0x{int(self):08x}"
650 for operand
in record
.dynamic_operands
:
651 operand
= operand
.disassemble(self
, record
)
652 operands
.append(operand
)
654 operands
= ",".join(operands
)
655 operands
= f
" {operands}"
659 yield f
"{record.name}{operands}"
661 class PrefixedInstruction(Instruction
):
662 class Prefix(WordInstruction
.remap(range(0, 32))):
665 class Suffix(WordInstruction
.remap(range(32, 64))):
668 _
: _Field
= range(64)
674 def integer(cls
, value
, byteorder
="little"):
675 return super().integer(bits
=64, value
=value
, byteorder
=byteorder
)
678 def pair(cls
, prefix
=0, suffix
=0, byteorder
="little"):
679 def transform(value
):
680 return WordInstruction
.integer(value
=value
,
681 byteorder
=byteorder
)[0:32]
683 (prefix
, suffix
) = map(transform
, (prefix
, suffix
))
684 value
= _selectconcat(prefix
, suffix
)
686 return super().integer(value
=value
)
688 def disassemble(self
, db
):
689 record
= db
[self
.suffix
]
691 yield f
".llong 0x{int(self):016x}"
693 yield f
".llong 0x{int(self):016x} # {record.name}"
696 class SVP64Instruction(PrefixedInstruction
):
697 """SVP64 instruction: https://libre-soc.org/openpower/sv/svp64/"""
698 class Prefix(PrefixedInstruction
.Prefix
):
700 _
: _Field
= range(24)
702 mask
: _Field
= range(1, 4)
703 elwidth
: _Field
= range(4, 6)
704 ewsrc
: _Field
= range(6, 8)
705 subvl
: _Field
= range(8, 10)
706 extra
: _Field
= range(10, 19)
707 mode
: _Field
= range(19, 24)
708 extra2
: _Field
[4] = (
714 smask
: _Field
= range(16, 19)
715 extra3
: _Field
[3] = (
722 rm
: RM
= ((6, 8) + tuple(range(10, 32)))
726 def disassemble(self
, db
):
727 record
= db
[self
.suffix
]
729 yield f
".llong 0x{int(self):016x}"
731 yield f
".llong 0x{int(self):016x} # sv.{record.name}"
734 def parse(stream
, factory
):
735 lines
= filter(lambda line
: not line
.strip().startswith("#"), stream
)
736 entries
= _csv
.DictReader(lines
)
737 entries
= filter(lambda entry
: "TODO" not in frozenset(entry
.values()), entries
)
738 return tuple(map(factory
, entries
))
742 def __init__(self
, root
):
743 db
= _collections
.defaultdict(set)
744 path
= (root
/ "insndb.csv")
745 with
open(path
, "r", encoding
="UTF-8") as stream
:
746 for section
in parse(stream
, Section
.CSV
):
747 path
= (root
/ section
.path
)
749 section
.Mode
.INTEGER
: IntegerOpcode
,
750 section
.Mode
.PATTERN
: PatternOpcode
,
752 factory
= _functools
.partial(PPCRecord
.CSV
, opcode_cls
=opcode_cls
)
753 with
open(path
, "r", encoding
="UTF-8") as stream
:
754 db
[section
].update(parse(stream
, factory
))
756 return super().__init
__()
758 def __getitem__(self
, key
):
759 for (section
, records
) in self
.__db
.items():
760 for record
in records
:
761 for name
in record
.names
:
763 ((record
.rc
is _RC
.RC
) and
764 key
.endswith(".") and
766 return (section
, record
)
771 def __init__(self
, root
):
773 pattern
= _re
.compile(r
"^(?:LDST)?RM-(1P|2P)-.*?\.csv$")
774 for (prefix
, _
, names
) in _os
.walk(root
):
775 prefix
= _pathlib
.Path(prefix
)
776 for name
in filter(lambda name
: pattern
.match(name
), names
):
777 path
= (prefix
/ _pathlib
.Path(name
))
778 with
open(path
, "r", encoding
="UTF-8") as stream
:
779 db
.update(parse(stream
, SVP64Record
.CSV
))
780 self
.__db
= {record
.name
:record
for record
in db
}
781 return super().__init
__()
783 def __getitem__(self
, key
):
785 record
= self
.__db
.get(name
, None)
786 if record
is not None:
791 class FieldsDatabase
:
796 for (form
, fields
) in df
.instrs
.items():
797 if form
in {"DQE", "TX"}:
801 db
[_Form
[form
]] = Fields(fields
)
803 return super().__init
__()
805 def __getitem__(self
, key
):
806 return self
.__db
.__getitem
__(key
)
809 class MarkdownDatabase
:
812 for (name
, desc
) in _ISA():
815 (dynamic
, *static
) = desc
.regs
816 operands
.extend(dynamic
)
817 operands
.extend(static
)
818 db
[name
] = Operands(insn
=name
, iterable
=operands
)
820 return super().__init
__()
823 yield from self
.__db
.items()
825 def __getitem__(self
, key
):
826 return self
.__db
.__getitem
__(key
)
830 def __init__(self
, root
):
831 root
= _pathlib
.Path(root
)
833 mdwndb
= MarkdownDatabase()
834 fieldsdb
= FieldsDatabase()
835 svp64db
= SVP64Database(root
)
836 ppcdb
= PPCDatabase(root
)
839 for (name
, operands
) in mdwndb
:
840 (section
, ppc
) = ppcdb
[name
]
843 svp64
= svp64db
[ppc
.names
]
844 fields
= fieldsdb
[ppc
.form
]
845 record
= Record(name
=name
,
846 section
=section
, ppc
=ppc
, svp64
=svp64
,
847 operands
=operands
, fields
=fields
)
850 self
.__db
= tuple(sorted(db
))
852 return super().__init
__()
855 return repr(self
.__db
)
860 @_functools.lru_cache(maxsize
=None)
861 def __contains__(self
, key
):
862 return self
.__getitem
__(key
) is not None
864 @_functools.lru_cache(maxsize
=None)
865 def __getitem__(self
, key
):
866 if isinstance(key
, (int, Instruction
)):
869 opcode
= record
.opcode
870 if ((opcode
.value
& opcode
.mask
) ==
871 (key
& opcode
.mask
)):
874 elif isinstance(key
, Opcode
):
876 if record
.opcode
== key
:
878 elif isinstance(key
, str):
880 if record
.name
== key
: