sv_binutils: use metaclass for enumerations
[openpower-isa.git] / src / openpower / sv / sv_binutils.py
1 import abc as _abc
2 import argparse as _argparse
3 import dataclasses as _dataclasses
4 import enum as _enum
5
6 from openpower.decoder.power_enums import (
7 In1Sel as _In1Sel,
8 In2Sel as _In2Sel,
9 In3Sel as _In3Sel,
10 OutSel as _OutSel,
11 CRInSel as _CRInSel,
12 CROutSel as _CROutSel,
13 SVPtype as _SVPtype,
14 SVEtype as _SVEtype,
15 SVEXTRA as _SVEXTRA,
16 RC as _RC,
17 )
18 from openpower.decoder.power_svp64 import SVP64RM as _SVP64RM
19
20
21 DISCLAIMER = (
22 "/*",
23 " * this file is auto-generated, do not edit",
24 " * https://git.libre-soc.org/?p=openpower-isa.git;a=blob;f=src/openpower/sv/sv_binutils.py",
25 " * part of Libre-SOC, sponsored by NLnet",
26 " */",
27 )
28
29
30 def indent(strings):
31 return map(lambda string: (" " + string), strings)
32
33
34 class CType:
35 @classmethod
36 @_abc.abstractmethod
37 def c_decl(self, name):
38 pass
39
40 @_abc.abstractmethod
41 def c_value(self, prefix="", suffix=""):
42 pass
43
44 @classmethod
45 @_abc.abstractmethod
46 def c_var(self, name):
47 pass
48
49
50 class EnumMeta(_enum.EnumMeta):
51 def __call__(metacls, *args, **kwargs):
52 if len(args) > 1:
53 names = args[1]
54 else:
55 names = kwargs.pop("names")
56
57 if isinstance(names, type) and issubclass(names, _enum.Enum):
58 names = dict(names.__members__)
59 if isinstance(names, dict):
60 names = tuple(names.items())
61
62 return super().__call__(*args, names=names, **kwargs)
63
64
65 class Enum(CType, _enum.Enum, metaclass=EnumMeta):
66 @classmethod
67 def c_decl(cls):
68 c_tag = f"svp64_{cls.__name__.lower()}"
69 yield f"enum {c_tag} {{"
70 for item in cls:
71 yield from indent(item.c_value(suffix=","))
72 yield f"}};"
73
74 def c_value(self, prefix="", suffix=""):
75 c_tag = f"svp64_{self.__class__.__name__.lower()}"
76 yield f"{prefix}{c_tag.upper()}_{self.name.upper()}{suffix}"
77
78 @classmethod
79 def c_var(cls, name):
80 c_tag = f"svp64_{cls.__name__.lower()}"
81 yield f"enum {c_tag} {name}"
82
83
84 In1Sel = Enum("In1Sel", names=_In1Sel.__members__.items())
85 In2Sel = Enum("In2Sel", names=_In2Sel.__members__.items())
86 In3Sel = Enum("In3Sel", names=_In3Sel.__members__.items())
87 OutSel = Enum("OutSel", names=_OutSel.__members__.items())
88 CRInSel = Enum("CRInSel", names=_CRInSel.__members__.items())
89 CROutSel = Enum("CROutSel", names=_CROutSel.__members__.items())
90 SVPType = Enum("SVPType", names=_SVPtype.__members__.items())
91 SVEType = Enum("SVEType", names=_SVEtype.__members__.items())
92 SVEXTRA = Enum("SVEXTRA", names=_SVEXTRA.__members__.items())
93
94
95 class Opcode(CType):
96 def __init__(self, value, mask, bits):
97 self.__value = value
98 self.__mask = mask
99 self.__bits = bits
100
101 return super().__init__()
102
103 @property
104 def value(self):
105 return self.__value
106
107 @property
108 def mask(self):
109 return self.__mask
110
111 @property
112 def bits(self):
113 return self.__bits
114
115 def __repr__(self):
116 fmt = f"{{value:0{self.bits}b}}:{{mask:0{self.bits}b}}"
117 return fmt.format(value=self.value, mask=self.mask)
118
119 def __lt__(self, other):
120 if not isinstance(other, self.__class__):
121 return NotImplemented
122
123 return self.__value < other.__value
124
125 @classmethod
126 def c_decl(cls):
127 yield f"struct svp64_opcode {{"
128 yield from indent([
129 "uint32_t value;",
130 "uint32_t mask;",
131 ])
132 yield f"}};"
133
134 def c_value(self, prefix="", suffix=""):
135 yield f"{prefix}{{"
136 yield from indent([
137 f".value = UINT32_C(0x{self.value:08X}),",
138 f".mask = UINT32_C(0x{self.mask:08X}),",
139 ])
140 yield f"}}{suffix}"
141
142 @classmethod
143 def c_var(cls, name):
144 yield f"struct svp64_opcode {name}"
145
146
147 class IntegerOpcode(Opcode):
148 def __init__(self, integer):
149 value = int(integer, 0)
150 bits = max(1, value.bit_length())
151 mask = int(("1" * bits), 2)
152
153 return super().__init__(value=value, mask=mask, bits=bits)
154
155
156 class PatternOpcode(Opcode):
157 def __init__(self, pattern):
158 value = 0
159 mask = 0
160 bits = len(pattern)
161 for bit in pattern:
162 value |= (bit == "1")
163 mask |= (bit != "-")
164 value <<= 1
165 mask <<= 1
166 value >>= 1
167 mask >>= 1
168
169 return super().__init__(value=value, mask=mask, bits=bits)
170
171
172 class Name(CType, str):
173 def __repr__(self):
174 escaped = self.replace("\"", "\\\"")
175 return f"\"{escaped}\""
176
177 def c_value(self, prefix="", suffix=""):
178 yield f"{prefix}{self!r}{suffix}"
179
180 @classmethod
181 def c_var(cls, name):
182 yield f"const char *{name}"
183
184
185 @_dataclasses.dataclass(eq=True, frozen=True)
186 class Record(CType):
187 in1: In1Sel
188 in2: In2Sel
189 in3: In3Sel
190 out: OutSel
191 out2: OutSel
192 cr_in: CRInSel
193 cr_out: CROutSel
194 sv_ptype: SVPType
195 sv_etype: SVEType
196 sv_in1: SVEXTRA
197 sv_in2: SVEXTRA
198 sv_in3: SVEXTRA
199 sv_out: SVEXTRA
200 sv_out2: SVEXTRA
201 sv_cr_in: SVEXTRA
202 sv_cr_out: SVEXTRA
203
204 @classmethod
205 def c_decl(cls):
206 bits_all = 0
207 yield f"struct svp64_record {{"
208 for field in _dataclasses.fields(cls):
209 bits = len(field.type).bit_length()
210 yield from indent([f"uint64_t {field.name} : {bits};"])
211 bits_all += bits
212 bits_rsvd = (64 - (bits_all % 64))
213 if bits_rsvd:
214 yield from indent([f"uint64_t : {bits_rsvd};"])
215 yield f"}};"
216
217 def c_value(self, prefix="", suffix=""):
218 yield f"{prefix}{{"
219 for field in _dataclasses.fields(self):
220 name = field.name
221 attr = getattr(self, name)
222 yield from indent(attr.c_value(prefix=f".{name} = ", suffix=","))
223 yield f"}}{suffix}"
224
225 @classmethod
226 def c_var(cls, name):
227 yield f"struct svp64_record {name}"
228
229
230 @_dataclasses.dataclass(eq=True, frozen=True)
231 class Entry(CType):
232 name: Name
233 record: Record
234
235 def __lt__(self, other):
236 if not isinstance(other, self.__class__):
237 return NotImplemented
238
239 return self.name < other.name
240
241 @classmethod
242 def c_decl(cls):
243 yield f"struct svp64_entry {{"
244 for field in _dataclasses.fields(cls):
245 yield from indent(field.type.c_var(name=f"{field.name};"))
246 yield f"}};"
247
248 def c_value(self, prefix="", suffix=""):
249 yield f"{prefix}{{"
250 for field in _dataclasses.fields(self):
251 name = field.name
252 attr = getattr(self, name)
253 yield from indent(attr.c_value(prefix=f".{name} = ", suffix=","))
254 yield f"}}{suffix}"
255
256 @classmethod
257 def c_var(cls, name):
258 yield f"struct svp64_entry {name}"
259
260
261 class Codegen(_enum.Enum):
262 PPC_SVP64_H = _enum.auto()
263 PPC_SVP64_OPC_C = _enum.auto()
264
265 @classmethod
266 def _missing_(cls, value):
267 return {
268 "ppc-svp64.h": Codegen.PPC_SVP64_H,
269 "ppc-svp64-opc.c": Codegen.PPC_SVP64_OPC_C,
270 }.get(value)
271
272 def __str__(self):
273 return {
274 Codegen.PPC_SVP64_H: "ppc-svp64.h",
275 Codegen.PPC_SVP64_OPC_C: "ppc-svp64-opc.c",
276 }[self]
277
278 def generate(self, entries):
279 def ppc_svp64_h(entries):
280 yield from DISCLAIMER
281 yield ""
282
283 yield f"#ifndef {self.name}"
284 yield f"#define {self.name}"
285 yield ""
286
287 yield "#include <stdint.h>"
288 yield ""
289
290 yield "#ifdef __cplusplus"
291 yield "extern \"C\" {"
292 yield "#endif"
293 yield ""
294
295 enums = (
296 In1Sel, In2Sel, In3Sel, OutSel,
297 CRInSel, CROutSel,
298 SVPType, SVEType, SVEXTRA,
299 )
300 for enum in enums:
301 yield from enum.c_decl()
302 yield ""
303
304 yield from Record.c_decl()
305 yield ""
306
307 yield from Entry.c_decl()
308 yield ""
309
310 yield "extern const struct svp64_entry svp64_entries[];"
311 yield "extern const unsigned int svp64_num_entries;"
312 yield ""
313
314 yield f"#define SVP64_NAME_MAX {max(map(lambda entry: len(entry.name), entries))}"
315 yield ""
316
317 yield "#ifdef __cplusplus"
318 yield "}"
319 yield "#endif"
320 yield ""
321
322 yield f"#endif /* {self.name} */"
323 yield ""
324
325 def ppc_svp64_opc_c(entries):
326 yield from DISCLAIMER
327 yield ""
328
329 yield "#include \"opcode/ppc-svp64.h\""
330 yield ""
331
332 yield "const struct svp64_entry svp64_entries[] = {"
333 for (index, entry) in enumerate(entries):
334 yield from indent(entry.c_value(prefix=f"[{index}] = ", suffix=","))
335 yield f"}};"
336 yield ""
337
338 yield "const unsigned int svp64_num_entries = \\"
339 yield " sizeof (svp64_entries) / sizeof (svp64_entries[0]);"
340 yield ""
341
342 return {
343 Codegen.PPC_SVP64_H: ppc_svp64_h,
344 Codegen.PPC_SVP64_OPC_C: ppc_svp64_opc_c,
345 }[self](entries)
346
347
348 ISA = _SVP64RM()
349 FIELDS = {field.name:field.type for field in _dataclasses.fields(Record)}
350 FIELDS.update({field.name:field.type for field in _dataclasses.fields(Entry)})
351
352 def parse(path, opcode_cls):
353 visited = set()
354
355 def name_filter(name):
356 if name.startswith("l") and name.endswith("br"):
357 return False
358 if name in {"mcrxr", "mcrxrx", "darn"}:
359 return False
360 if name in {"bctar", "bcctr"}:
361 return False
362 if "rfid" in name:
363 return False
364 if name in {"setvl"}:
365 return False
366 if name in visited:
367 return False
368
369 visited.add(name)
370
371 return True
372
373 def item_mapper(item):
374 (key, value) = item
375 key = key.lower().replace(" ", "_")
376 cls = FIELDS.get(key, object)
377 if not isinstance(value, cls):
378 if issubclass(cls, _enum.Enum):
379 value = {item.name:item for item in cls}[value]
380 else:
381 value = cls(value)
382 return (key, value)
383
384 def item_filter(item):
385 (key, _) = item
386 return (key in FIELDS)
387
388 for record in ISA.get_svp64_csv(path):
389 opcode = opcode_cls(record.pop("opcode"))
390 names = record.pop("comment").split("=")[-1].split("/")
391 names = set(filter(name_filter, names))
392 if names:
393 rc = _RC[record["rc"] if record["rc"] else "NONE"]
394 if rc is _RC.RC:
395 names.update({f"{name}." for name in names})
396 record = dict(filter(item_filter, map(item_mapper, record.items())))
397 for name in map(Name, names):
398 yield Entry(name=name, record=Record(**record))
399
400
401 def main(codegen):
402 entries = []
403 table = {
404 "minor_19.csv": IntegerOpcode,
405 "minor_30.csv": IntegerOpcode,
406 "minor_31.csv": IntegerOpcode,
407 "minor_58.csv": IntegerOpcode,
408 "minor_62.csv": IntegerOpcode,
409 "minor_22.csv": IntegerOpcode,
410 "minor_5.csv": PatternOpcode,
411 "minor_63.csv": PatternOpcode,
412 "minor_59.csv": PatternOpcode,
413 "major.csv": IntegerOpcode,
414 "extra.csv": PatternOpcode,
415 }
416 for (path, opcode_cls) in table.items():
417 entries.extend(parse(path, opcode_cls))
418 entries = sorted(frozenset(entries))
419
420 for line in codegen.generate(entries):
421 print(line)
422
423
424 if __name__ == "__main__":
425 parser = _argparse.ArgumentParser()
426 parser.add_argument("codegen", type=Codegen, choices=Codegen, help="code generator")
427
428 args = vars(parser.parse_args())
429 main(**args)