power_insn.py: introduce instruction database
[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 in1(self):
445 return self.ppc.in1
446
447 @property
448 def in2(self):
449 return self.ppc.in2
450
451 @property
452 def in3(self):
453 return self.ppc.in3
454
455 @property
456 def out(self):
457 return self.ppc.out
458
459 @property
460 def out2(self):
461 if self.svp64 is None:
462 return _OutSel.NONE
463 return self.ppc.out
464
465 @property
466 def cr_in(self):
467 return self.ppc.cr_in
468
469 @property
470 def cr_out(self):
471 return self.ppc.cr_out
472
473 def sv_extra(self, key):
474 if key not in frozenset({
475 "in1", "in2", "in3", "cr_in",
476 "out", "out2", "cr_out",
477 }):
478 raise KeyError(key)
479
480 sel = getattr(self.svp64, key)
481 if sel is _CRInSel.BA_BB:
482 return _SVExtra.Idx_1_2
483
484 extra_map = {
485 _SVExtraRegType.SRC: {},
486 _SVExtraRegType.DST: {},
487 }
488 for index in range(0, 4):
489 for entry in self.svp64.extra[index]:
490 extra_map[entry.regtype][entry.reg] = Instruction.__EXTRA[index]
491
492 for regtype in (_SVExtraRegType.SRC, _SVExtraRegType.DST):
493 extra = extra_map[regtype][sel]
494 if extra is not _SVExtra.NONE:
495 return extra
496
497 return _SVExtra.NONE
498
499 sv_in1 = property(_functools.partial(sv_extra, key="in1"))
500 sv_in2 = property(_functools.partial(sv_extra, key="in2"))
501 sv_in3 = property(_functools.partial(sv_extra, key="in3"))
502 sv_out = property(_functools.partial(sv_extra, key="out"))
503 sv_out2 = property(_functools.partial(sv_extra, key="out2"))
504 sv_cr_in = property(_functools.partial(sv_extra, key="cr_in"))
505 sv_cr_out = property(_functools.partial(sv_extra, key="cr_out"))
506
507 @property
508 def sv_ptype(self):
509 if self.svp64 is None:
510 return _SVPtype.NONE
511 return self.svp64.ptype
512
513 @property
514 def sv_etype(self):
515 if self.svp64 is None:
516 return _SVEtype.NONE
517 return self.svp64.etype
518
519
520 class Database:
521 def __init__(self, root):
522 root = _pathlib.Path(root)
523
524 def parse(stream, factory):
525 lines = filter(lambda line: not line.strip().startswith("#"), stream)
526 entries = _csv.DictReader(lines)
527 entries = filter(lambda entry: "TODO" not in frozenset(entry.values()), entries)
528 return tuple(map(factory, entries))
529
530 def database_ppc(root):
531 db = _collections.defaultdict(set)
532 path = (root / "insndb.csv")
533 with open(path, "r", encoding="UTF-8") as stream:
534 for section in parse(stream, Section.CSV):
535 path = (root / section.path)
536 opcode_cls = {
537 section.Mode.INTEGER: IntegerOpcode,
538 section.Mode.PATTERN: PatternOpcode,
539 }[section.mode]
540 factory = _functools.partial(PPCRecord.CSV, opcode_cls=opcode_cls)
541 with open(path, "r", encoding="UTF-8") as stream:
542 db[section].update(parse(stream, factory))
543 for (section, records) in db.items():
544 db[section] = {record.identifier:record for record in records}
545 return db
546
547 def database_svp64(root):
548 db = set()
549 pattern = _re.compile(r"^(?:LDST)?RM-(1P|2P)-.*?\.csv$")
550 for (prefix, _, names) in _os.walk(root):
551 prefix = _pathlib.Path(prefix)
552 for name in filter(lambda name: pattern.match(name), names):
553 path = (prefix / _pathlib.Path(name))
554 with open(path, "r", encoding="UTF-8") as stream:
555 db.update(parse(stream, SVP64Record.CSV))
556 db = {record.identifier:record for record in db}
557 return db
558
559 def database_forms(root):
560 # This is hack. The whole code there should be moved here.
561 # The fields.text parser should take care of the validation.
562 from openpower.decoder.power_fields import DecodeFields as _DecodeFields
563 db = {}
564 df = _DecodeFields()
565 df.create_specs()
566 for (form, fields) in df.instrs.items():
567 if form in {"DQE", "TX"}:
568 continue
569 if form == "all":
570 form = "NONE"
571 db[_Form[form]] = Fields(fields)
572 return db
573
574 def database(ppcdb, svp64db, formsdb):
575 items = set()
576 for section in ppcdb:
577 for (identifier, ppc) in ppcdb[section].items():
578 fields = formsdb[ppc.form]
579 svp64 = svp64db.get(identifier)
580 if ppc.rc is _RC.ONE:
581 variants = {name:True for name in ppc.names}
582 elif ppc.rc is _RC.RC:
583 variants = {name:False for name in ppc.names}
584 variants.update({f"{name}.":True for name in ppc.names})
585 else:
586 variants = {name:False for name in ppc.names}
587 for (name, rc) in variants.items():
588 items.add(Instruction(name=name, rc=rc,
589 section=section, ppc=ppc, fields=fields, svp64=svp64))
590
591 items = tuple(sorted(items, key=_operator.attrgetter("opcode")))
592 opcodes = {item.opcode:item for item in items}
593 names = {item.name:item for item in sorted(items, key=_operator.attrgetter("name"))}
594
595 return (items, opcodes, names)
596
597 ppcdb = database_ppc(root)
598 svp64db = database_svp64(root)
599 formsdb = database_forms(root)
600
601 (items, opcodes, names) = database(ppcdb, svp64db, formsdb)
602 self.__items = items
603 self.__opcodes = opcodes
604 self.__names = names
605
606 return super().__init__()
607
608 def __repr__(self):
609 return repr(self.__items)
610
611 def __iter__(self):
612 yield from self.__items
613
614 def __contains__(self, key):
615 if isinstance(key, int):
616 return self.__opcodes.__contains__(key)
617 elif isinstance(key, str):
618 return self.__names.__contains__(key)
619 else:
620 raise KeyError(key)
621
622 def __getitem__(self, key):
623 if isinstance(key, int):
624 return self.__opcodes.__getitem__(key)
625 elif isinstance(key, str):
626 return self.__names.__getitem__(key)
627 else:
628 raise KeyError(key)