libopid: refactor opcodes; update naming
[openpower-isa.git] / src / libopid / codegen.py
1 import argparse
2 import collections
3 import contextlib
4 import dataclasses
5 import enum
6 import itertools
7 import operator
8 import pathlib
9
10 import mdis.dispatcher
11 import mdis.visitor
12 import mdis.walker
13
14 from openpower.decoder.power_enums import (
15 find_wiki_dir,
16 )
17
18 import openpower.insndb.core as insndb
19
20
21 def traverse(root, visitor, walker, **kwargs):
22 with visitor(root, **kwargs):
23 for (node, *_, path, pathcls) in walker(root):
24 traverse(node, visitor, walker, path=path, pathcls=pathcls)
25
26
27 def fetch(span):
28 bits = len(span)
29 one = "UINT64_C(1)"
30 for (dst, origin) in enumerate(span):
31 src = (32 - (origin + 1))
32 dst = (bits - (dst + 1))
33 dst = f"UINT64_C({dst})"
34 src = f"UINT64_C({src})"
35 yield f"/* {origin:<2} */ (((insn >> {src}) & {one}) << {dst}) |"
36 yield f"UINT64_C(0)"
37
38
39 def store(span):
40 bits = len(span)
41 one = "UINT64_C(1)"
42 for (dst, origin) in enumerate(span):
43 src = (32 - (origin + 1))
44 dst = (bits - (dst + 1))
45 dst = f"UINT64_C({dst})"
46 src = f"UINT64_C({src})"
47 yield f"/* {origin:<2} */ (((operand->value >> {dst}) & {one}) << {src}) |"
48 yield f"UINT64_C(0)"
49
50
51 def unwrap(integer):
52 for bit in range(31, -1, -1):
53 yield ((integer >> bit) & 1)
54
55
56 def wrap(bits):
57 value = 0
58 for (index, bit) in enumerate(reversed(bits)):
59 if bit:
60 value |= (1 << index)
61 return value
62
63
64 class Mode(enum.Enum):
65 DIS_GEN_C = "opid-dis-gen.c"
66 OPC_GEN_C = "opid-opc-gen.c"
67
68 def __call__(self, db, **arguments):
69 def pairwise(iterable):
70 (a, b) = itertools.tee(iterable)
71 next(b, None)
72 return zip(a, b)
73
74 cache = Cache()
75 codegen = {
76 Mode.DIS_GEN_C: DisGenSource,
77 Mode.OPC_GEN_C: OpcGenSource,
78 }[self](cache=cache, **arguments)
79 for (root, visitor) in pairwise((db, cache, codegen),):
80 walker_cls = getattr(visitor, "Walker", Walker)
81 traverse(root=root, visitor=visitor, walker=walker_cls())
82
83
84 class StructMeta(type):
85 def __new__(metacls, name, bases, ns):
86 cls = super().__new__(metacls, name, bases, ns)
87 return dataclasses.dataclass(cls, eq=True, frozen=True)
88
89
90 class Struct(metaclass=StructMeta):
91 pass
92
93
94 class DynamicOperandIds(tuple): pass
95 class StaticOperands(tuple): pass
96 class DynamicOperands(tuple): pass
97 class OpcodeIds(tuple): pass
98 class NameTable(tuple): pass
99 class OpcodeTable(tuple): pass
100 class RecordTable(tuple): pass
101
102
103 class Opcode(insndb.Opcode):
104 @property
105 def PO(self):
106 (*_, result) = itertools.accumulate((
107 (((self.value >> 31) & 1) << 5), # 0
108 (((self.value >> 30) & 1) << 4), # 1
109 (((self.value >> 29) & 1) << 3), # 2
110 (((self.value >> 28) & 1) << 2), # 3
111 (((self.value >> 27) & 1) << 1), # 4
112 (((self.value >> 26) & 1) << 0), # 5
113 ), operator.or_)
114 return result
115
116 @property
117 def weight(self):
118 bits = tuple(unwrap(self.mask)).count(1)
119 return (self.PO, -bits, self.mask)
120
121 def __lt__(self, other):
122 if not isinstance(other, self.__class__):
123 return NotImplemented
124 return self.weight < other.weight
125
126 class DynamicOperandId(Struct):
127 name: str = "NIL"
128 index: int = 0
129
130
131 class NameId(Struct):
132 name: str
133 index: int
134
135
136 class DynamicOperand(Struct):
137 cls: type
138 span: tuple
139 names: tuple
140
141
142 class Record(Struct):
143 name: str
144 opcode: Opcode
145 dynamic_operand_ids: DynamicOperandIds
146 static_operands: StaticOperands
147
148
149 class Cache(mdis.visitor.ContextVisitor):
150 def __init__(self):
151 self.__PO = ([0] * (1 << 6))
152 self.__records = []
153 self.__static_operand = collections.defaultdict(list)
154 self.__dynamic_operand = collections.defaultdict(set)
155 self.__dynamic_operand_id = collections.defaultdict(list)
156
157 return super().__init__()
158
159 def __iter__(self):
160 name_table = {}
161 operands_table = tuple(self.__dynamic_operand.keys())
162 nil = DynamicOperandId()
163
164 def dynamic_operand_id(item):
165 (name, cls, span) = item
166 index = (operands_table.index((cls, span),) + 1)
167 return DynamicOperandId(name=name, index=index)
168
169 def dynamic_operand(item):
170 ((cls, span), names) = item
171 return DynamicOperand(cls=cls, span=span, names=tuple(sorted(names)))
172
173 def name_id(item):
174 (name, index) = item
175 return NameId(name=name, index=index)
176
177 def record(item):
178 (index, (opcode, name)) = item
179 name_table[name] = index
180 dynamic_operand_ids = map(dynamic_operand_id, self.__dynamic_operand_id[name])
181 dynamic_operand_ids = DynamicOperandIds(tuple(dynamic_operand_ids) + (nil,))
182 static_operands = StaticOperands(dict.fromkeys(self.__static_operand[name]))
183
184 return Record(opcode=opcode, name=name,
185 dynamic_operand_ids=dynamic_operand_ids,
186 static_operands=static_operands)
187
188 yield DynamicOperands(map(dynamic_operand, self.__dynamic_operand.items()))
189 yield RecordTable(map(record, enumerate(sorted(self.__records))))
190 yield OpcodeTable(self.__PO)
191 yield NameTable(sorted(map(name_id, name_table.items()), key=lambda item: item.name))
192
193 @mdis.dispatcher.Hook(insndb.Record)
194 @contextlib.contextmanager
195 def dispatch_record(self, node):
196 self.__record = node
197 yield node
198
199 @mdis.dispatcher.Hook(insndb.Record.Opcode)
200 @contextlib.contextmanager
201 def dispatch_record_opcode(self, node):
202 self.__PO[self.__record.PO] += 1
203 yield node
204
205 @mdis.dispatcher.Hook(insndb.Record.Opcodes)
206 @contextlib.contextmanager
207 def dispatch_record_opcodes(self, node):
208 masks = {subnode.mask for subnode in node}
209 if len(masks) != 1:
210 raise ValueError(masks)
211 mask = list(unwrap(masks.pop()))
212 states = tuple(unwrap(node[0].value))
213 for subnode in node[1:]:
214 for (index, bit) in enumerate(unwrap(subnode.value)):
215 if mask[index] and (states[index] != bit):
216 mask[index] = 0
217
218 mask = insndb.Record.Opcode.Mask(wrap(mask))
219 opcode = Opcode(node[0].value, mask)
220 self.__records.append((opcode, self.__record.name))
221
222 yield node
223
224 @mdis.dispatcher.Hook(insndb.StaticOperand)
225 @contextlib.contextmanager
226 def dispatch_static_operand(self, node):
227 self.__static_operand[self.__record.name].append(node)
228 yield node
229
230 @mdis.dispatcher.Hook(insndb.DynamicOperand)
231 @contextlib.contextmanager
232 def dispatch_dynamic_operand(self, node):
233 (cls, span) = (node.__class__, node.span)
234 self.__dynamic_operand[cls, span].add(node.name)
235 self.__dynamic_operand_id[self.__record.name].append((node.name, cls, span),)
236 yield node
237
238
239 class Walker(insndb.Walker):
240 @mdis.dispatcher.Hook(Cache)
241 def dispatch_cache(self, node):
242 yield from ()
243
244
245 class Codegen(mdis.visitor.ContextVisitor):
246 def __init__(self, cache, **arguments):
247 self.__level = 0
248
249 return super().__init__()
250
251 def __enter__(self):
252 self.__level += 1
253 return self
254
255 def __exit__(self, exc_type, exc_value, exc_traceback):
256 self.__level -= 1
257
258 def emit(self, message=""):
259 indent = ((" " * 4 * self.__level) if message else "")
260 print(f"{indent}{message}")
261
262 @mdis.dispatcher.Hook(Cache)
263 @contextlib.contextmanager
264 def dispatch_cache(self, node):
265 self.emit("/*")
266 self.emit(" * Autogenerated by libresoc codegen script")
267 self.emit(" * DO NOT EDIT: all changes will be lost")
268 self.emit(" */")
269 self.emit("")
270 yield node
271
272
273 class Header(Codegen):
274 pass
275
276
277 class Source(Codegen):
278 class Walker(Walker):
279 @mdis.dispatcher.Hook(DynamicOperand, RecordTable)
280 def dispatch_ignore(self, node):
281 yield from ()
282
283 @mdis.dispatcher.Hook(Cache)
284 def dispatch_cache(self, node):
285 (operands, _, _, _) = node
286 yield from self([operands])
287
288 @mdis.dispatcher.Hook(str)
289 @contextlib.contextmanager
290 def dispatch_str(self, node, *, path, pathcls):
291 self.emit(f"{pathcls(path)} = \"{node}\",")
292 with self: yield node
293
294 @mdis.dispatcher.Hook(object)
295 @contextlib.contextmanager
296 def dispatch_object(self, node, *, path, pathcls):
297 self.emit(f"{pathcls(path)} = {{")
298 with self: yield node
299 self.emit("},")
300
301 @mdis.dispatcher.Hook(Cache)
302 @contextlib.contextmanager
303 def dispatch_cache(self, node):
304 self.emit("/*")
305 self.emit(" * Autogenerated by libresoc codegen script")
306 self.emit(" * DO NOT EDIT: all changes will be lost")
307 self.emit(" */")
308 self.emit("")
309 self.emit("#include <stddef.h>")
310 self.emit("#include <stdint.h>")
311 self.emit("")
312 self.emit("#include \"opid.h\"")
313 self.emit("")
314 yield node
315
316
317 class Record(Struct):
318 static_operands: StaticOperands
319 name: str
320 opcode: insndb.Record.Opcode
321 dynamic_operand_ids: DynamicOperandIds
322
323
324 class DisGenSource(Source):
325 class Walker(Walker):
326 @mdis.dispatcher.Hook(DynamicOperand, RecordTable)
327 def dispatch_ignore(self, node):
328 yield from ()
329
330 @mdis.dispatcher.Hook(Cache)
331 def dispatch_cache(self, node):
332 (operands, _, _, _) = node
333 yield from self([operands])
334
335 @mdis.dispatcher.Hook(DynamicOperands)
336 @contextlib.contextmanager
337 def dispatch_operands(self, node):
338 self.emit("static inline enum opid_state")
339 self.emit("opid_disassemble_operand(uint64_t insn,")
340 with self:
341 with self:
342 self.emit("size_t category,")
343 self.emit("struct opid_operand *operand) {")
344 with self:
345 self.emit(f"switch (category) {{")
346 yield node
347 self.emit("default:")
348 with self:
349 self.emit("return OPID_ERROR_OPERAND_0_LOOKUP;")
350 self.emit("}")
351 self.emit("")
352 with self:
353 self.emit("return OPID_SUCCESS;")
354 self.emit("}")
355
356 @mdis.dispatcher.Hook(DynamicOperand)
357 @contextlib.contextmanager
358 def dispatch_operand(self, node, *, path, pathcls):
359 def generic_handler(span, flags="UINT64_C(0)"):
360 yield f"operand->value = ("
361 with self:
362 yield from fetch(span)
363 yield f");"
364 yield f"operand->flags = {flags};"
365 self.emit("break;")
366
367 def nonzero_handler(span):
368 yield f"operand->value = (UINT64_C(1) + ("
369 with self:
370 yield from fetch(span)
371 yield f"));"
372 yield f"operand->flags = OPID_OPERAND_NONZERO;"
373 self.emit("break;")
374
375 def signed_handler(span, flags="OPID_OPERAND_SIGNED"):
376 mask = f"(UINT64_C(1) << (UINT64_C({len(span)}) - 1))"
377 yield "operand->value = ("
378 with self:
379 yield "("
380 with self:
381 yield "("
382 with self:
383 yield from fetch(span)
384 yield ")"
385 yield "^"
386 yield f"{mask}"
387 yield ")"
388 yield "-"
389 yield f"{mask}"
390 yield ");"
391 yield f"operand->flags = {flags};"
392 self.emit("break;")
393
394 def address_handler(span):
395 yield from signed_handler(span, "(OPID_OPERAND_ADDRESS | OPID_OPERAND_SIGNED)")
396
397 def gpr_handler(span, pair=False):
398 if not pair:
399 yield from generic_handler(span, "OPID_OPERAND_GPR")
400 else:
401 yield from generic_handler(span, "(OPID_OPERAND_GPR | OPID_OPERAND_PAIR)")
402
403 def fpr_handler(span, pair=False):
404 if not pair:
405 yield from generic_handler(span, "OPID_OPERAND_FPR")
406 else:
407 yield from generic_handler(span, "(OPID_OPERAND_FPR | OPID_OPERAND_PAIR)")
408
409 def cr3_handler(span):
410 yield from generic_handler(span, "OPID_OPERAND_CR3")
411
412 def cr5_handler(span):
413 yield from generic_handler(span, "OPID_OPERAND_CR5")
414
415 handlers = {
416 insndb.GPRPairOperand: lambda span: gpr_handler(span, True),
417 insndb.FPRPairOperand: lambda span: fpr_handler(span, True),
418 insndb.GPROperand: gpr_handler,
419 insndb.FPROperand: fpr_handler,
420 insndb.CR3Operand: cr3_handler,
421 insndb.CR5Operand: cr5_handler,
422 insndb.TargetAddrOperand: address_handler,
423 insndb.SignedOperand: signed_handler,
424 insndb.NonZeroOperand: nonzero_handler,
425 insndb.DynamicOperand: generic_handler,
426 object: None,
427 }
428 self.emit(f"case 0x{(path + 1):02x}: /* {', '.join(node.names)} */")
429 with self:
430 for (cls, handler) in handlers.items():
431 if issubclass(node.cls, cls):
432 break
433 if handler is None:
434 raise ValueError("unknown handler")
435 for line in handler(span=node.span):
436 self.emit(line)
437 self.emit("")
438 yield node
439
440
441 class OpcGenSource(Source):
442 class Walker(Walker):
443 @mdis.dispatcher.Hook(DynamicOperandId, NameId, Opcode,
444 DynamicOperands, insndb.StaticOperand, OpcodeTable)
445 def dispatch_ignore(self, node):
446 yield from ()
447
448 @mdis.dispatcher.Hook(Record)
449 def dispatch_record(self, node):
450 keys = {
451 "dynamic_operand_ids": "operands",
452 }
453
454 for field in dataclasses.fields(node):
455 key = field.name
456 value = getattr(node, key)
457 key = keys.get(key, key)
458 yield (value, node, key, mdis.walker.AttributePath)
459
460 @mdis.dispatcher.Hook(Cache)
461 def dispatch_cache(self, node):
462 (_, records, opcodes, names) = node
463 yield from self([records, opcodes, names])
464
465 @mdis.dispatcher.Hook(DynamicOperandId)
466 @contextlib.contextmanager
467 def dispatch_dynamic_operand_id(self, node, *, path, pathcls):
468 index = f"UINT8_C(0x{node.index:02x})"
469 self.emit(f"{pathcls(path)} = {index}, /* {node.name} */")
470 with self: yield node
471
472 @mdis.dispatcher.Hook(StaticOperands)
473 @contextlib.contextmanager
474 def dispatch_static_operands(self, node):
475 if node:
476 self.emit("/*")
477 yield node
478 self.emit(" */")
479 else:
480 yield node
481
482 @mdis.dispatcher.Hook(insndb.StaticOperand)
483 @contextlib.contextmanager
484 def dispatch_static_operand(self, node):
485 self.emit(f" * {node.name}={node.value} [{', '.join(map(str, node.span))}]")
486 yield node
487
488 @mdis.dispatcher.Hook(insndb.Record.Opcode.Value, insndb.Record.Opcode.Mask)
489 @contextlib.contextmanager
490 def dispatch_opcode_parts(self, node, *, path, pathcls):
491 self.emit(f"{pathcls(path)} = UINT64_C(0x{node:016x}),")
492 with self: yield node
493
494 @mdis.dispatcher.Hook(Opcode)
495 @contextlib.contextmanager
496 def dispatch_opcode(self, node):
497 self.emit(".opcode = {")
498 with self:
499 self.emit(f".value = UINT64_C(0x{node.value:08x}),")
500 self.emit(f".mask = UINT64_C(0x{node.mask:08x}),")
501 self.emit("},")
502 yield node
503
504 @mdis.dispatcher.Hook(OpcodeTable)
505 @contextlib.contextmanager
506 def dispatch_potable(self, node):
507 heads = ([0] * (1 << 6))
508 tails = ([0] * (1 << 6))
509 for (index, counter) in enumerate(itertools.accumulate(node)):
510 heads[index] = (counter - node[index])
511 tails[index] = counter
512 heads = [(tail - node[index]) for (index, tail) in enumerate(tails)]
513 self.emit("static uint16_t const opid_opcode_table[64][2] = {")
514 with self:
515 for index in range(64):
516 head = heads[index]
517 tail = tails[index]
518 self.emit(f"[{index}] = {{{head}, {tail}}},")
519 self.emit("};")
520 self.emit("")
521 yield node
522
523 @mdis.dispatcher.Hook(NameId)
524 @contextlib.contextmanager
525 def dispatch_name_id(self, node):
526 self.emit(f"{{\"{node.name}\", &opid_record_table[{node.index}]}},")
527 yield node
528
529 @mdis.dispatcher.Hook(NameTable)
530 @contextlib.contextmanager
531 def dispatch_name_table(self, node):
532 self.emit(f"static struct opid_name_id const opid_name_id_table[] = {{")
533 with self:
534 yield node
535 self.emit("};")
536
537 @mdis.dispatcher.Hook(RecordTable)
538 @contextlib.contextmanager
539 def dispatch_records(self, node):
540 self.emit("static struct opid_record const opid_record_table[] = {")
541 with self: yield node
542 self.emit("};")
543 self.emit("")
544
545
546 def main():
547 table = {mode:{} for mode in Mode}
548 main_parser = argparse.ArgumentParser("codegen",
549 description="C code generator")
550 main_parser.add_argument("-d", "--database",
551 type=pathlib.Path,
552 default=pathlib.Path(find_wiki_dir()))
553 main_subprarsers = main_parser.add_subparsers(dest="mode", required=True)
554 for (mode, _) in table.items():
555 parser = main_subprarsers.add_parser(mode.value)
556
557 arguments = dict(vars(main_parser.parse_args()))
558 mode = Mode(arguments.pop("mode"))
559 db = insndb.Database(root=arguments.pop("database"))
560
561 return mode(db=db, **arguments)
562
563
564 if __name__ == "__main__":
565 main()