power_insn: fix opcode-based lookups
[openpower-isa.git] / src / openpower / decoder / power_insn.py
1 import collections as _collections
2 import copy as _copy
3 import csv as _csv
4 import dataclasses as _dataclasses
5 import enum as _enum
6 import functools as _functools
7 import os as _os
8 import operator as _operator
9 import pathlib as _pathlib
10 import re as _re
11
12 try:
13 from functools import cached_property
14 except ImportError:
15 from cached_property import cached_property
16
17 from openpower.decoder.power_enums import (
18 Function as _Function,
19 MicrOp as _MicrOp,
20 In1Sel as _In1Sel,
21 In2Sel as _In2Sel,
22 In3Sel as _In3Sel,
23 OutSel as _OutSel,
24 CRInSel as _CRInSel,
25 CROutSel as _CROutSel,
26 LDSTLen as _LDSTLen,
27 LDSTMode as _LDSTMode,
28 RC as _RC,
29 CryIn as _CryIn,
30 Form as _Form,
31 SVEtype as _SVEtype,
32 SVMode as _SVMode,
33 SVPtype as _SVPtype,
34 SVExtra as _SVExtra,
35 SVExtraRegType as _SVExtraRegType,
36 SVExtraReg as _SVExtraReg,
37 )
38
39
40 def dataclass(cls, record, keymap=None, typemap=None):
41 if keymap is None:
42 keymap = {}
43 if typemap is None:
44 typemap = {field.name:field.type for field in _dataclasses.fields(cls)}
45
46 def transform(key_value):
47 (key, value) = key_value
48 key = keymap.get(key, key)
49 hook = typemap.get(key, lambda value: value)
50 if hook is bool and value in ("", "0"):
51 value = False
52 else:
53 value = hook(value)
54 return (key, value)
55
56 record = dict(map(transform, record.items()))
57 for key in frozenset(record.keys()):
58 if record[key] == "":
59 record.pop(key)
60
61 return cls(**record)
62
63
64 @_functools.total_ordering
65 @_dataclasses.dataclass(eq=True, frozen=True)
66 class Opcode:
67 class Value(int):
68 def __repr__(self):
69 if self.bit_length() <= 32:
70 return f"0x{self:08x}"
71 else:
72 return f"0x{self:016x}"
73
74 class Mask(int):
75 def __repr__(self):
76 if self.bit_length() <= 32:
77 return f"0x{self:08x}"
78 else:
79 return f"0x{self:016x}"
80
81 value: Value
82 mask: Mask = None
83
84 def __lt__(self, other):
85 if not isinstance(other, Opcode):
86 return NotImplemented
87 return ((self.value, self.mask) < (other.value, other.mask))
88
89 def __post_init__(self):
90 (value, mask) = (self.value, self.mask)
91
92 if isinstance(value, Opcode):
93 if mask is not None:
94 raise ValueError(mask)
95 (value, mask) = (value.value, value.mask)
96 elif isinstance(value, str):
97 if mask is not None:
98 raise ValueError(mask)
99 value = int(value, 0)
100
101 if not isinstance(value, int):
102 raise ValueError(value)
103 if mask is None:
104 mask = value
105 if not isinstance(mask, int):
106 raise ValueError(mask)
107
108 object.__setattr__(self, "value", self.__class__.Value(value))
109 object.__setattr__(self, "mask", self.__class__.Mask(mask))
110
111
112 class IntegerOpcode(Opcode):
113 def __init__(self, value):
114 if isinstance(value, str):
115 value = int(value, 0)
116 return super().__init__(value=value, mask=None)
117
118
119 class PatternOpcode(Opcode):
120 def __init__(self, value):
121 (pattern, value, mask) = (value, 0, 0)
122
123 for symbol in pattern:
124 if symbol not in {"0", "1", "-"}:
125 raise ValueError(pattern)
126 value |= (symbol == "1")
127 mask |= (symbol != "-")
128 value <<= 1
129 mask <<= 1
130 value >>= 1
131 mask >>= 1
132
133 return super().__init__(value=value, mask=mask)
134
135
136 class FieldsOpcode(Opcode):
137 def __init__(self, fields):
138 def field(opcode, field):
139 (value, mask) = opcode
140 (field, bits) = field
141 shifts = map(lambda bit: (31 - bit), reversed(tuple(bits)))
142 for (index, shift) in enumerate(shifts):
143 bit = ((field & (1 << index)) != 0)
144 value |= (bit << shift)
145 mask |= (1 << shift)
146 return (value, mask)
147
148 (value, mask) = _functools.reduce(field, fields, (0, 0))
149
150 return super().__init__(value=value, mask=mask)
151
152
153 @_dataclasses.dataclass(eq=True, frozen=True)
154 class PPCRecord:
155 class FlagsMeta(type):
156 def __iter__(cls):
157 yield from (
158 "inv A",
159 "inv out",
160 "cry out",
161 "BR",
162 "sgn ext",
163 "rsrv",
164 "32b",
165 "sgn",
166 "lk",
167 "sgl pipe",
168 )
169
170 class Flags(frozenset, metaclass=FlagsMeta):
171 def __new__(cls, flags=frozenset()):
172 flags = frozenset(flags)
173 diff = (flags - frozenset(cls))
174 if diff:
175 raise ValueError(flags)
176 return super().__new__(cls, flags)
177
178 opcode: Opcode
179 comment: str
180 flags: Flags = Flags()
181 comment2: str = ""
182 unit: _Function = _Function.NONE
183 intop: _MicrOp = _MicrOp.OP_ILLEGAL
184 in1: _In1Sel = _In1Sel.RA
185 in2: _In2Sel = _In2Sel.NONE
186 in3: _In3Sel = _In3Sel.NONE
187 out: _OutSel = _OutSel.NONE
188 cr_in: _CRInSel = _CRInSel.NONE
189 cr_out: _CROutSel = _CROutSel.NONE
190 cry_in: _CryIn = _CryIn.ZERO
191 ldst_len: _LDSTLen = _LDSTLen.NONE
192 upd: _LDSTMode = _LDSTMode.NONE
193 rc: _RC = _RC.NONE
194 form: _Form = _Form.NONE
195 conditions: str = ""
196 unofficial: bool = False
197
198 __KEYMAP = {
199 "internal op": "intop",
200 "CR in": "cr_in",
201 "CR out": "cr_out",
202 "cry in": "cry_in",
203 "ldst len": "ldst_len",
204 "CONDITIONS": "conditions",
205 }
206
207 @classmethod
208 def CSV(cls, record, opcode_cls=Opcode):
209 typemap = {field.name:field.type for field in _dataclasses.fields(cls)}
210 typemap["opcode"] = opcode_cls
211
212 flags = set()
213 for flag in frozenset(PPCRecord.Flags):
214 if bool(record.pop(flag, "")):
215 flags.add(flag)
216 record["flags"] = PPCRecord.Flags(flags)
217
218 return dataclass(cls, record, keymap=PPCRecord.__KEYMAP, typemap=typemap)
219
220 @property
221 def identifier(self):
222 return self.comment
223
224 @cached_property
225 def names(self):
226 return frozenset(self.comment.split("=")[-1].split("/"))
227
228
229 @_dataclasses.dataclass(eq=True, frozen=True)
230 class SVP64Record:
231 class ExtraMap(tuple):
232 class Extra(tuple):
233 @_dataclasses.dataclass(eq=True, frozen=True)
234 class Entry:
235 regtype: _SVExtraRegType = _SVExtraRegType.NONE
236 reg: _SVExtraReg = _SVExtraReg.NONE
237
238 def __repr__(self):
239 return f"{self.regtype.value}:{self.reg.name}"
240
241 def __new__(cls, value="0"):
242 if isinstance(value, str):
243 def transform(value):
244 (regtype, reg) = value.split(":")
245 regtype = _SVExtraRegType(regtype)
246 reg = _SVExtraReg(reg)
247 return cls.Entry(regtype=regtype, reg=reg)
248
249 if value == "0":
250 value = tuple()
251 else:
252 value = map(transform, value.split(";"))
253
254 return super().__new__(cls, value)
255
256 def __repr__(self):
257 return repr(list(self))
258
259 def __new__(cls, value=tuple()):
260 value = tuple(value)
261 if len(value) == 0:
262 value = (("0",) * 4)
263 return super().__new__(cls, map(cls.Extra, value))
264
265 def __repr__(self):
266 return repr({index:self[index] for index in range(0, 4)})
267
268 identifier: str
269 ptype: _SVPtype = _SVPtype.NONE
270 etype: _SVEtype = _SVEtype.NONE
271 in1: _In1Sel = _In1Sel.NONE
272 in2: _In2Sel = _In2Sel.NONE
273 in3: _In3Sel = _In3Sel.NONE
274 out: _OutSel = _OutSel.NONE
275 out2: _OutSel = _OutSel.NONE
276 cr_in: _CRInSel = _CRInSel.NONE
277 cr_out: _CROutSel = _CROutSel.NONE
278 extra: ExtraMap = ExtraMap()
279 pu: bool = False
280 conditions: str = ""
281 mode: _SVMode = _SVMode.NORMAL
282
283 __KEYMAP = {
284 "insn": "identifier",
285 "CONDITIONS": "conditions",
286 "Ptype": "ptype",
287 "Etype": "etype",
288 "CR in": "cr_in",
289 "CR out": "cr_out",
290 "PU": "pu",
291 }
292
293 @classmethod
294 def CSV(cls, record):
295 for key in ("in1", "in2", "in3", "out", "out2", "CR in", "CR out"):
296 value = record[key]
297 if value == "0":
298 record[key] = "NONE"
299
300 record["extra"] = cls.ExtraMap(record.pop(f"{index}") for index in range(0, 4))
301
302 return dataclass(cls, record, keymap=cls.__KEYMAP)
303
304
305 class BitSel:
306 def __init__(self, value=(0, 32)):
307 if isinstance(value, str):
308 (start, end) = map(int, value.split(":"))
309 else:
310 (start, end) = value
311 if start < 0 or end < 0 or start >= end:
312 raise ValueError(value)
313
314 self.__start = start
315 self.__end = end
316
317 return super().__init__()
318
319 def __repr__(self):
320 return f"[{self.__start}:{self.__end}]"
321
322 def __iter__(self):
323 yield from range(self.start, (self.end + 1))
324
325 @property
326 def start(self):
327 return self.__start
328
329 @property
330 def end(self):
331 return self.__end
332
333
334 @_dataclasses.dataclass(eq=True, frozen=True)
335 class Section:
336 class Mode(_enum.Enum):
337 INTEGER = _enum.auto()
338 PATTERN = _enum.auto()
339
340 @classmethod
341 def _missing_(cls, value):
342 if isinstance(value, str):
343 return cls[value.upper()]
344 return super()._missing_(value)
345
346 class Suffix(int):
347 def __new__(cls, value=None):
348 if isinstance(value, str):
349 if value.upper() == "NONE":
350 value = None
351 else:
352 value = int(value, 0)
353 if value is None:
354 value = 0
355
356 return super().__new__(cls, value)
357
358 def __str__(self):
359 return repr(self)
360
361 def __repr__(self):
362 return (bin(self) if self else "None")
363
364 path: _pathlib.Path
365 opcode: Opcode
366 bitsel: BitSel
367 suffix: Suffix
368 mode: Mode
369
370 @classmethod
371 def CSV(cls, record):
372 return dataclass(cls, record)
373
374
375 class Fields:
376 def __init__(self, items):
377 if isinstance(items, dict):
378 items = items.items()
379
380 def transform(item):
381 (name, bitrange) = item
382 return (name, tuple(bitrange.values()))
383
384 self.__mapping = dict(map(transform, items))
385
386 return super().__init__()
387
388 def __repr__(self):
389 return repr(self.__mapping)
390
391 def __contains__(self, key):
392 return self.__mapping.__contains__(key)
393
394 def __getitem__(self, key):
395 return self.__mapping.__getitem__(key)
396
397 def get(self, key, default):
398 return self.__mapping.get(key, default)
399
400
401 @_functools.total_ordering
402 @_dataclasses.dataclass(eq=True, frozen=True)
403 class Instruction:
404 name: str
405 rc: bool
406 section: Section
407 ppc: PPCRecord
408 fields: Fields
409 svp64: SVP64Record = None
410
411 __EXTRA = (
412 _SVExtra.Idx0,
413 _SVExtra.Idx1,
414 _SVExtra.Idx2,
415 _SVExtra.Idx3,
416 )
417
418 def __lt__(self, other):
419 if not isinstance(other, Instruction):
420 return NotImplemented
421 return (self.opcode < other.opcode)
422
423 def __repr__(self):
424 return f"{self.__class__.__name__}(name={self.name!r}, opcode={self.opcode})"
425
426 @cached_property
427 def opcode(self):
428 fields = []
429 if self.section.opcode:
430 fields += [(self.section.opcode.value, BitSel((0, 5)))]
431 fields += [(self.ppc.opcode.value, self.section.bitsel)]
432 else:
433 fields += [(self.ppc.opcode.value, self.section.bitsel)]
434
435 # Some instructions are special regarding Rc handling.
436 # They are marked with Rc.ONE, but don't have Rc field.
437 # At least addic., andi., andis. belong to this list.
438 if self.rc and "Rc" in self.fields:
439 fields += [(1, self.fields["Rc"])]
440
441 return FieldsOpcode(fields)
442
443 @property
444 def function(self):
445 return self.ppc.unit
446
447 @property
448 def in1(self):
449 return self.ppc.in1
450
451 @property
452 def in2(self):
453 return self.ppc.in2
454
455 @property
456 def in3(self):
457 return self.ppc.in3
458
459 @property
460 def out(self):
461 return self.ppc.out
462
463 @property
464 def out2(self):
465 if self.svp64 is None:
466 return _OutSel.NONE
467 return self.ppc.out
468
469 @property
470 def cr_in(self):
471 return self.ppc.cr_in
472
473 @property
474 def cr_out(self):
475 return self.ppc.cr_out
476
477 def sv_extra(self, key):
478 if key not in frozenset({
479 "in1", "in2", "in3", "cr_in",
480 "out", "out2", "cr_out",
481 }):
482 raise KeyError(key)
483
484 sel = getattr(self.svp64, key)
485 if sel is _CRInSel.BA_BB:
486 return _SVExtra.Idx_1_2
487 reg = _SVExtraReg(sel)
488 if reg is _SVExtraReg.NONE:
489 return _SVExtra.NONE
490
491 extra_map = {
492 _SVExtraRegType.SRC: {},
493 _SVExtraRegType.DST: {},
494 }
495 for index in range(0, 4):
496 for entry in self.svp64.extra[index]:
497 extra_map[entry.regtype][entry.reg] = Instruction.__EXTRA[index]
498
499 for regtype in (_SVExtraRegType.SRC, _SVExtraRegType.DST):
500 extra = extra_map[regtype].get(reg, _SVExtra.NONE)
501 if extra is not _SVExtra.NONE:
502 return extra
503
504 return _SVExtra.NONE
505
506 sv_in1 = property(_functools.partial(sv_extra, key="in1"))
507 sv_in2 = property(_functools.partial(sv_extra, key="in2"))
508 sv_in3 = property(_functools.partial(sv_extra, key="in3"))
509 sv_out = property(_functools.partial(sv_extra, key="out"))
510 sv_out2 = property(_functools.partial(sv_extra, key="out2"))
511 sv_cr_in = property(_functools.partial(sv_extra, key="cr_in"))
512 sv_cr_out = property(_functools.partial(sv_extra, key="cr_out"))
513
514 @property
515 def sv_ptype(self):
516 if self.svp64 is None:
517 return _SVPtype.NONE
518 return self.svp64.ptype
519
520 @property
521 def sv_etype(self):
522 if self.svp64 is None:
523 return _SVEtype.NONE
524 return self.svp64.etype
525
526
527 class Database:
528 def __init__(self, root):
529 root = _pathlib.Path(root)
530
531 def parse(stream, factory):
532 lines = filter(lambda line: not line.strip().startswith("#"), stream)
533 entries = _csv.DictReader(lines)
534 entries = filter(lambda entry: "TODO" not in frozenset(entry.values()), entries)
535 return tuple(map(factory, entries))
536
537 def database_ppc(root):
538 db = _collections.defaultdict(set)
539 path = (root / "insndb.csv")
540 with open(path, "r", encoding="UTF-8") as stream:
541 for section in parse(stream, Section.CSV):
542 path = (root / section.path)
543 opcode_cls = {
544 section.Mode.INTEGER: IntegerOpcode,
545 section.Mode.PATTERN: PatternOpcode,
546 }[section.mode]
547 factory = _functools.partial(PPCRecord.CSV, opcode_cls=opcode_cls)
548 with open(path, "r", encoding="UTF-8") as stream:
549 db[section].update(parse(stream, factory))
550 for (section, records) in db.items():
551 db[section] = {record.identifier:record for record in records}
552 return db
553
554 def database_svp64(root):
555 db = set()
556 pattern = _re.compile(r"^(?:LDST)?RM-(1P|2P)-.*?\.csv$")
557 for (prefix, _, names) in _os.walk(root):
558 prefix = _pathlib.Path(prefix)
559 for name in filter(lambda name: pattern.match(name), names):
560 path = (prefix / _pathlib.Path(name))
561 with open(path, "r", encoding="UTF-8") as stream:
562 db.update(parse(stream, SVP64Record.CSV))
563 db = {record.identifier:record for record in db}
564 return db
565
566 def database_forms(root):
567 # This is hack. The whole code there should be moved here.
568 # The fields.text parser should take care of the validation.
569 from openpower.decoder.power_fields import DecodeFields as _DecodeFields
570 db = {}
571 df = _DecodeFields()
572 df.create_specs()
573 for (form, fields) in df.instrs.items():
574 if form in {"DQE", "TX"}:
575 continue
576 if form == "all":
577 form = "NONE"
578 db[_Form[form]] = Fields(fields)
579 return db
580
581 def database(ppcdb, svp64db, formsdb):
582 items = set()
583 for section in ppcdb:
584 for (identifier, ppc) in ppcdb[section].items():
585 fields = formsdb[ppc.form]
586 svp64 = svp64db.get(identifier)
587 if ppc.rc is _RC.ONE:
588 variants = {name:True for name in ppc.names}
589 elif ppc.rc is _RC.RC:
590 variants = {name:False for name in ppc.names}
591 variants.update({f"{name}.":True for name in ppc.names})
592 else:
593 variants = {name:False for name in ppc.names}
594 for (name, rc) in variants.items():
595 items.add(Instruction(name=name, rc=rc,
596 section=section, ppc=ppc, fields=fields, svp64=svp64))
597
598 items = tuple(sorted(items, key=_operator.attrgetter("opcode")))
599 opcodes = {item.opcode:item for item in items}
600 names = {item.name:item for item in sorted(items, key=_operator.attrgetter("name"))}
601
602 return (items, opcodes, names)
603
604 ppcdb = database_ppc(root)
605 svp64db = database_svp64(root)
606 formsdb = database_forms(root)
607
608 (items, opcodes, names) = database(ppcdb, svp64db, formsdb)
609 self.__items = items
610 self.__opcodes = opcodes
611 self.__names = names
612
613 return super().__init__()
614
615 def __repr__(self):
616 return repr(self.__items)
617
618 def __iter__(self):
619 yield from self.__items
620
621 @_functools.lru_cache(maxsize=None)
622 def __contains__(self, key):
623 if isinstance(key, int):
624 return self.__opcodes.__contains__(key)
625 elif isinstance(key, int):
626 for (opcode, insn) in self.__opcodes.items():
627 if ((opcode.value & opcode.mask) ==
628 (key & opcode.mask)):
629 return True
630 return False
631 elif isinstance(key, str):
632 return self.__names.__contains__(key)
633 else:
634 raise KeyError(key)
635
636 @_functools.lru_cache(maxsize=None)
637 def __getitem__(self, key):
638 if isinstance(key, Opcode):
639 return self.__opcodes.__getitem__(key)
640 elif isinstance(key, int):
641 for (opcode, insn) in self.__opcodes.items():
642 if ((opcode.value & opcode.mask) ==
643 (key & opcode.mask)):
644 return insn
645 raise KeyError(key)
646 elif isinstance(key, str):
647 return self.__names.__getitem__(key)
648 else:
649 raise KeyError(key)