power_insn: bind records to operands
[openpower-isa.git] / src / openpower / sv / sv_binutils_fptrans.py
1 import argparse as _argparse
2 import dataclasses as _dataclasses
3 import enum as _enum
4 import functools as _functools
5
6
7 from openpower.decoder.power_enums import (
8 FPTRANS_INSNS as _FPTRANS_INSNS,
9 find_wiki_dir as _find_wiki_dir,
10 )
11 from openpower.decoder.power_insn import (
12 Database as _Database,
13 StaticOperand as _StaticOperand,
14 WordInstruction as _WordInstruction,
15 )
16
17
18 @_dataclasses.dataclass(eq=True, frozen=True)
19 class StaticOperand:
20 name: str
21 value: int
22 span: tuple
23
24
25 @_dataclasses.dataclass(eq=True, frozen=True)
26 class DynamicOperand:
27 name: str
28 span: tuple
29
30
31 @_functools.total_ordering
32 @_dataclasses.dataclass(eq=True, frozen=True)
33 class Entry:
34 name: str
35 static_operands: tuple
36 dynamic_operands: tuple
37
38 def __lt__(self, other):
39 if not isinstance(other, self.__class__):
40 return NotImplemented
41 return ((self.PO, self.XO, self.Rc) <
42 (other.PO, other.XO, other.Rc))
43
44 @property
45 def PO(self):
46 for operand in self.static_operands:
47 if operand.name == "PO":
48 return operand.value
49 raise ValueError(self)
50
51 @property
52 def XO(self):
53 for operand in self.static_operands:
54 if operand.name == "XO":
55 return operand.value
56 raise ValueError(self)
57
58 @property
59 def Rc(self):
60 for operand in self.static_operands:
61 if operand.name == "Rc":
62 return operand.value
63 raise ValueError(self)
64
65
66 def collect(db):
67 fptrans = tuple(_FPTRANS_INSNS)
68 fptrans_Rc = tuple(map(lambda name: f"{name}.", fptrans))
69
70 def fptrans_match(insn):
71 return ((insn.name in fptrans) or (insn.name in fptrans_Rc))
72
73 for record in filter(fptrans_match, db):
74 if len(record.opcodes) > 1:
75 raise NotImplementedError(record.opcodes)
76 PO = record.section.opcode
77 if PO is None:
78 PO = tuple(record.ppc)[0].opcode
79 XO = None
80 else:
81 XO = tuple(record.ppc)[0].opcode
82
83 @_dataclasses.dataclass(eq=True, frozen=True)
84 class POStaticOperand(_StaticOperand):
85 def span(self, record):
86 return tuple(range(0, 6))
87
88 @_dataclasses.dataclass(eq=True, frozen=True)
89 class XOStaticOperand(_StaticOperand):
90 def span(self, record):
91 return tuple(record.section.bitsel)
92
93 static_operands = [(POStaticOperand, {
94 "name": "PO",
95 "value": (PO.value & PO.mask)
96 })]
97 if XO is not None:
98 static_operands.append((XOStaticOperand, {
99 "name": "XO",
100 "value": (XO.value & XO.mask)
101 }))
102
103 static_operands.extend(record.mdwn.operands.static)
104 dynamic_operands = record.mdwn.operands.dynamic
105
106 def static_operand(operand):
107 (cls, kwargs) = operand
108 operand = cls(record=record, **kwargs)
109 return StaticOperand(name=operand.name,
110 value=operand.value, span=operand.span(record=record))
111
112 def dynamic_operand(operand):
113 (cls, kwargs) = operand
114 operand = cls(record=record, **kwargs)
115 return DynamicOperand(name=operand.name,
116 span=operand.span(record=record))
117
118 static_operands = tuple(map(static_operand, static_operands))
119 dynamic_operands = tuple(map(dynamic_operand, dynamic_operands))
120
121 yield Entry(name=record.name,
122 static_operands=static_operands,
123 dynamic_operands=dynamic_operands)
124
125
126 def opcodes(entry):
127 operands = entry.dynamic_operands
128 operands = ", ".join(operand.name for operand in operands)
129 string = ",\t".join((
130 f"\"{entry.name}\"",
131 f"XRC({entry.PO},{entry.XO},{entry.Rc})",
132 "X_MASK",
133 "SVP64",
134 "PPCVLE",
135 f"{{{operands}}}",
136 ))
137 return f"{{{string}}},"
138
139
140 def asm(entry, binutils=False, regex=False):
141 operands = tuple(entry.dynamic_operands)
142 for (idx, operand) in enumerate(operands):
143 values = []
144 for each in operands:
145 if binutils and each.name in ("FRT", "FRA", "FRB"):
146 values.append("f0")
147 elif binutils and each.name in ("RT", "RA", "RB"):
148 values.append("r0")
149 else:
150 values.append("0")
151 value = str((1 << len(operand.span)) - 1)
152 if binutils and operand.name in ("FRT", "FRA", "FRB"):
153 value = f"f{value}"
154 elif binutils and operand.name in ("RT", "RA", "RB"):
155 value = f"r{value}"
156 values[idx] = value
157 sep = "\s+" if regex else " "
158 yield f"{entry.name}{sep}{','.join(values)}"
159
160
161 def dis(entry, binutils=True):
162 def objdump(byte):
163 return f"{byte:02x}"
164
165 asm_plain = tuple(asm(entry, binutils=binutils, regex=False))
166 asm_regex = tuple(asm(entry, binutils=binutils, regex=True))
167 for (idx, dynamic_operand) in enumerate(entry.dynamic_operands):
168 insn = _WordInstruction.integer(value=0)
169 for static_operand in entry.static_operands:
170 span = static_operand.span
171 insn[span] = static_operand.value
172 span = dynamic_operand.span
173 insn[span] = ((1 << len(span)) - 1)
174 if binutils:
175 big = " ".join(map(objdump, insn.bytes(byteorder="big")))
176 little = " ".join(map(objdump, insn.bytes(byteorder="little")))
177 yield f".*:\s+({big}|{little})\s+{asm_regex[idx]}"
178 else:
179 yield asm_plain[idx]
180
181
182 class Mode(_enum.Enum):
183 OPCODES = "opcodes"
184 ASM = "asm"
185 DIS = "dis"
186
187 def __str__(self):
188 return self.name.lower()
189
190
191 if __name__ == "__main__":
192 parser = _argparse.ArgumentParser()
193 parser.add_argument("mode", type=Mode, choices=Mode)
194 args = dict(vars(parser.parse_args()))
195
196 mode = args["mode"]
197 db = _Database(_find_wiki_dir())
198 entries = sorted(collect(db))
199
200 generator = {
201 Mode.OPCODES: opcodes,
202 Mode.ASM: asm,
203 Mode.DIS: dis,
204 }[mode]
205 if mode is Mode.DIS:
206 print("#as: -mlibresoc")
207 print("#objdump: -dr -Mlibresoc")
208 print("")
209 print(".*: file format .*")
210 print("")
211 print("")
212 print("Disassembly of section \\.text:")
213 print("0+ <\.text>:")
214
215 if mode in {Mode.ASM, Mode.DIS}:
216 for subgenerator in map(generator, entries):
217 for line in subgenerator:
218 print(line)
219 else:
220 for line in map(generator, entries):
221 print(line)