2712e51b77e591b588d678e95cab196116d0414f
[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 pathlib
8 import sys
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 = "UINT32_C(1)"
30 for (dst, origin) in enumerate(span):
31 src = (32 - (origin + 1))
32 dst = (bits - (dst + 1))
33 dst = f"UINT32_C({dst})"
34 src = f"UINT32_C({src})"
35 yield f"/* {origin:<2} */ (((insn >> {src}) & {one}) << {dst}) |"
36 yield f"UINT32_C(0)"
37
38
39 class Mode(enum.Enum):
40 PPC_DIS_GEN_C = "opid-dis-gen.c"
41 PPC_OPC_GEN_C = "opid-opc-gen.c"
42
43 def __call__(self, db, **arguments):
44 def pairwise(iterable):
45 (a, b) = itertools.tee(iterable)
46 next(b, None)
47 return zip(a, b)
48
49 cache = Cache()
50 codegen = {
51 Mode.PPC_DIS_GEN_C: DisGenSource,
52 Mode.PPC_OPC_GEN_C: OpcGenSource,
53 }[self](cache=cache, **arguments)
54 for (root, visitor) in pairwise((db, cache, codegen),):
55 walker_cls = getattr(visitor, "Walker", Walker)
56 traverse(root=root, visitor=visitor, walker=walker_cls())
57
58
59 class StructMeta(type):
60 def __new__(metacls, name, bases, ns):
61 cls = super().__new__(metacls, name, bases, ns)
62 return dataclasses.dataclass(cls, eq=True, frozen=True)
63
64
65 class Struct(metaclass=StructMeta):
66 pass
67
68
69 class DynamicOperandIds(tuple): pass
70 class StaticOperands(tuple): pass
71 class DynamicOperands(tuple): pass
72 class POTable(tuple): pass
73 class RecordTable(tuple): pass
74
75
76 class DynamicOperandId(Struct):
77 name: str = "NIL"
78 index: int = 0
79
80
81 class DynamicOperand(Struct):
82 cls: type
83 span: tuple
84 names: tuple
85
86
87 class Record(Struct):
88 name: str
89 opcode: insndb.Record.Opcode
90 dynamic_operand_ids: DynamicOperandIds
91 static_operands: StaticOperands
92
93
94 class Cache(mdis.visitor.ContextVisitor):
95 def __init__(self):
96 self.__PO = ([0] * (1 << 6))
97 self.__records = collections.defaultdict(list)
98 self.__static_operand = collections.defaultdict(list)
99 self.__dynamic_operand = collections.defaultdict(set)
100 self.__dynamic_operand_id = collections.defaultdict(list)
101
102 return super().__init__()
103
104 def __iter__(self):
105 table = tuple(self.__dynamic_operand.keys())
106 nil = DynamicOperandId()
107
108 def dynamic_operand_id(item):
109 (name, cls, span) = item
110 index = (table.index((cls, span),) + 1)
111 return DynamicOperandId(name=name, index=index)
112
113 def dynamic_operand(item):
114 ((cls, span), names) = item
115 return DynamicOperand(cls=cls, span=span, names=tuple(sorted(names)))
116
117 def record(item):
118 (opcode, name) = item
119 dynamic_operand_ids = map(dynamic_operand_id, self.__dynamic_operand_id[name])
120 dynamic_operand_ids = DynamicOperandIds(tuple(dynamic_operand_ids) + (nil,))
121 static_operands = StaticOperands(self.__static_operand[name])
122
123 return Record(opcode=opcode, name=name,
124 dynamic_operand_ids=dynamic_operand_ids,
125 static_operands=static_operands)
126
127 yield DynamicOperands(map(dynamic_operand, self.__dynamic_operand.items()))
128 yield RecordTable(map(record, sorted(self.__records.items())))
129 yield POTable(self.__PO)
130
131 @mdis.dispatcher.Hook(insndb.Record)
132 @contextlib.contextmanager
133 def dispatch_record(self, node):
134 self.__record = node
135 yield node
136
137 @mdis.dispatcher.Hook(insndb.Record.Opcode)
138 @contextlib.contextmanager
139 def dispatch_record_opcode(self, node):
140 self.__records[node] = self.__record.name
141 self.__PO[self.__record.PO] += 1
142 yield node
143
144 @mdis.dispatcher.Hook(insndb.StaticOperand)
145 @contextlib.contextmanager
146 def dispatch_static_operand(self, node):
147 self.__static_operand[self.__record.name].append(node)
148 yield node
149
150 @mdis.dispatcher.Hook(insndb.DynamicOperand)
151 @contextlib.contextmanager
152 def dispatch_dynamic_operand(self, node):
153 (cls, span) = (node.__class__, node.span)
154 self.__dynamic_operand[cls, span].add(node.name)
155 self.__dynamic_operand_id[self.__record.name].append((node.name, cls, span),)
156 yield node
157
158
159 class Walker(insndb.Walker):
160 @mdis.dispatcher.Hook(Cache)
161 def dispatch_cache(self, node):
162 yield from ()
163
164
165 class Codegen(mdis.visitor.ContextVisitor):
166 def __init__(self, cache, **arguments):
167 self.__level = 0
168
169 return super().__init__()
170
171 def __enter__(self):
172 self.__level += 1
173 return self
174
175 def __exit__(self, exc_type, exc_value, exc_traceback):
176 self.__level -= 1
177
178 def emit(self, message=""):
179 indent = ((" " * 4 * self.__level) if message else "")
180 print(f"{indent}{message}")
181
182 @mdis.dispatcher.Hook(Cache)
183 @contextlib.contextmanager
184 def dispatch_cache(self, node):
185 self.emit("/*")
186 self.emit(" * Autogenerated by libresoc codegen script")
187 self.emit(" * DO NOT EDIT: all changes will be lost")
188 self.emit(" */")
189 self.emit("")
190 yield node
191
192
193 class Header(Codegen):
194 pass
195
196
197 class Source(Codegen):
198 @mdis.dispatcher.Hook(str)
199 @contextlib.contextmanager
200 def dispatch_str(self, node, *, path, pathcls):
201 self.emit(f"{pathcls(path)} = \"{node}\",")
202 with self: yield node
203
204 @mdis.dispatcher.Hook(object)
205 @contextlib.contextmanager
206 def dispatch_object(self, node, *, path, pathcls):
207 self.emit(f"{pathcls(path)} = {{")
208 with self: yield node
209 self.emit("},")
210
211 @mdis.dispatcher.Hook(Cache)
212 @contextlib.contextmanager
213 def dispatch_cache(self, node):
214 self.emit("/*")
215 self.emit(" * Autogenerated by libresoc codegen script")
216 self.emit(" * DO NOT EDIT: all changes will be lost")
217 self.emit(" */")
218 self.emit("")
219 self.emit("#include <stddef.h>")
220 self.emit("#include <stdint.h>")
221 self.emit("")
222 self.emit("#include \"opid.h\"")
223 self.emit("")
224 yield node
225
226
227 class Record(Struct):
228 static_operands: StaticOperands
229 name: str
230 opcode: insndb.Record.Opcode
231 dynamic_operand_ids: DynamicOperandIds
232
233
234 class DisGenSource(Source):
235 class Walker(Walker):
236 @mdis.dispatcher.Hook(DynamicOperand, RecordTable)
237 def dispatch_ignore(self, node):
238 yield from ()
239
240 @mdis.dispatcher.Hook(Cache)
241 def dispatch_cache(self, node):
242 (operands, _, _) = node
243 yield from self([operands])
244
245 @mdis.dispatcher.Hook(DynamicOperands)
246 @contextlib.contextmanager
247 def dispatch_operands(self, node):
248 self.emit("static inline enum opid_state")
249 self.emit("opid_disassemble_operand(struct opid_ctx *ctx, uint32_t insn, size_t id) {")
250 with self:
251 self.emit("uint32_t value;")
252 self.emit("uint32_t flags;")
253 self.emit("")
254 self.emit(f"switch (ctx->record->operands[id]) {{")
255 yield node
256 self.emit("default:")
257 with self:
258 self.emit("return (enum opid_state)((size_t)OPID_ERROR_OPERAND_0 + id);")
259 self.emit("}")
260 self.emit("")
261 with self:
262 self.emit("ctx->operands[id].value = value;")
263 self.emit("ctx->operands[id].flags = flags;")
264 self.emit("")
265 self.emit("return OPID_SUCCESS;")
266 self.emit("}")
267
268 @mdis.dispatcher.Hook(DynamicOperand)
269 @contextlib.contextmanager
270 def dispatch_operand(self, node, *, path, pathcls):
271 def generic_handler(span, flags="UINT32_C(0)"):
272 yield f"value = ("
273 with self:
274 yield from fetch(span)
275 yield f");"
276 yield f"flags = {flags};"
277 self.emit("break;")
278
279 def nonzero_handler(span):
280 yield f"value = (UINT32_C(1) + ("
281 with self:
282 yield from fetch(span)
283 yield f"));"
284 yield f"flags = OPID_OPERAND_NONZERO;"
285 self.emit("break;")
286
287 def signed_handler(span, flags="OPID_OPERAND_SIGNED"):
288 mask = f"(UINT32_C(1) << (UINT32_C({len(span)}) - 1))"
289 yield "value = ("
290 with self:
291 yield "("
292 with self:
293 yield "("
294 with self:
295 yield from fetch(span)
296 yield ")"
297 yield "^"
298 yield f"{mask}"
299 yield ")"
300 yield "-"
301 yield f"{mask}"
302 yield ");"
303 yield f"flags = {flags};"
304 self.emit("break;")
305
306 def address_handler(span):
307 yield from signed_handler(span, "(OPID_OPERAND_ADDRESS | OPID_OPERAND_SIGNED)")
308
309 def gpr_handler(span, pair=False):
310 if not pair:
311 yield from generic_handler(span, "OPID_OPERAND_GPR")
312 else:
313 yield from generic_handler(span, "(OPID_OPERAND_GPR | OPID_OPERAND_PAIR)")
314
315 def fpr_handler(span, pair=False):
316 if not pair:
317 yield from generic_handler(span, "OPID_OPERAND_FPR")
318 else:
319 yield from generic_handler(span, "(OPID_OPERAND_FPR | OPID_OPERAND_PAIR)")
320
321 def cr3_handler(span):
322 yield from generic_handler(span, "OPID_OPERAND_CR3")
323
324 def cr5_handler(span):
325 yield from generic_handler(span, "OPID_OPERAND_CR5")
326
327 handlers = {
328 insndb.GPRPairOperand: lambda span: gpr_handler(span, True),
329 insndb.FPRPairOperand: lambda span: fpr_handler(span, True),
330 insndb.GPROperand: gpr_handler,
331 insndb.FPROperand: fpr_handler,
332 insndb.CR3Operand: cr3_handler,
333 insndb.CR5Operand: cr5_handler,
334 insndb.TargetAddrOperand: address_handler,
335 insndb.SignedOperand: signed_handler,
336 insndb.NonZeroOperand: nonzero_handler,
337 insndb.DynamicOperand: generic_handler,
338 object: None,
339 }
340 self.emit(f"case 0x{(path + 1):02x}: /* {', '.join(node.names)} */")
341 with self:
342 for (cls, handler) in handlers.items():
343 if issubclass(node.cls, cls):
344 break
345 if handler is None:
346 raise ValueError("unknown handler")
347 for line in handler(span=node.span):
348 self.emit(line)
349 self.emit("")
350 yield node
351
352
353 class OpcGenSource(Source):
354 class Walker(Walker):
355 @mdis.dispatcher.Hook(DynamicOperandId, DynamicOperands, insndb.StaticOperand, POTable)
356 def dispatch_ignore(self, node):
357 yield from ()
358
359 @mdis.dispatcher.Hook(Record)
360 def dispatch_record(self, node):
361 keys = {
362 "dynamic_operand_ids": "operands",
363 }
364
365 for field in dataclasses.fields(node):
366 key = field.name
367 value = getattr(node, key)
368 key = keys.get(key, key)
369 yield (value, node, key, mdis.walker.AttributePath)
370
371 @mdis.dispatcher.Hook(Cache)
372 def dispatch_cache(self, node):
373 (_, records, potable) = node
374 yield from self([records, potable])
375
376 @mdis.dispatcher.Hook(DynamicOperandId)
377 @contextlib.contextmanager
378 def dispatch_dynamic_operand_id(self, node, *, path, pathcls):
379 index = f"UINT8_C(0x{node.index:02x})"
380 self.emit(f"{pathcls(path)} = {index}, /* {node.name} */")
381 with self: yield node
382
383 @mdis.dispatcher.Hook(StaticOperands)
384 @contextlib.contextmanager
385 def dispatch_static_operands(self, node):
386 if node:
387 self.emit("/*")
388 yield node
389 self.emit(" */")
390 else:
391 yield node
392
393 @mdis.dispatcher.Hook(insndb.StaticOperand)
394 @contextlib.contextmanager
395 def dispatch_static_operand(self, node):
396 self.emit(f" * {node.name}={node.value} [{', '.join(map(str, node.span))}]")
397 yield node
398
399 @mdis.dispatcher.Hook(insndb.Record.Opcode.Value, insndb.Record.Opcode.Mask)
400 @contextlib.contextmanager
401 def dispatch_opcode_parts(self, node, *, path, pathcls):
402 self.emit(f"{pathcls(path)} = UINT32_C(0x{node:016x}),")
403 with self: yield node
404
405 @mdis.dispatcher.Hook(insndb.Record.Opcode)
406 @contextlib.contextmanager
407 def dispatch_opcode(self, node):
408 self.emit(".opcode = {")
409 with self: yield node
410 self.emit("},")
411
412 @mdis.dispatcher.Hook(POTable)
413 @contextlib.contextmanager
414 def dispatch_potable(self, node):
415 heads = ([0] * (1 << 6))
416 tails = ([0] * (1 << 6))
417 for (index, counter) in enumerate(itertools.accumulate(node)):
418 heads[index] = (counter - node[index])
419 tails[index] = counter
420 heads = [(tail - node[index]) for (index, tail) in enumerate(tails)]
421 self.emit("static uint16_t const opid_opcode_table[64][2] = {")
422 with self:
423 for index in range(64):
424 head = heads[index]
425 tail = tails[index]
426 self.emit(f"[0x{index:02x}] = {{{head}, {tail}}},")
427 self.emit("};")
428 yield node
429
430 @mdis.dispatcher.Hook(RecordTable)
431 @contextlib.contextmanager
432 def dispatch_records(self, node):
433 self.emit("static struct opid_record const opid_record_table[] = {")
434 with self: yield node
435 self.emit("};")
436 self.emit("")
437
438
439 def main():
440 table = {mode:{} for mode in Mode}
441 main_parser = argparse.ArgumentParser("codegen",
442 description="C code generator")
443 main_parser.add_argument("-d", "--database",
444 type=pathlib.Path,
445 default=pathlib.Path(find_wiki_dir()))
446 main_subprarsers = main_parser.add_subparsers(dest="mode", required=True)
447 for (mode, _) in table.items():
448 parser = main_subprarsers.add_parser(mode.value)
449
450 arguments = dict(vars(main_parser.parse_args()))
451 mode = Mode(arguments.pop("mode"))
452 db = insndb.Database(root=arguments.pop("database"))
453
454 return mode(db=db, **arguments)
455
456
457 if __name__ == "__main__":
458 main()