10 import mdis
.dispatcher
14 from openpower
.decoder
.power_enums
import (
18 import openpower
.insndb
.core
as insndb
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
)
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}) |"
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}) |"
52 for bit
in range(31, -1, -1):
53 yield ((integer
>> bit
) & 1)
58 for (index
, bit
) in enumerate(reversed(bits
)):
64 class Mode(enum
.Enum
):
65 DIS_GEN_C
= "opid-dis-gen.c"
66 OPC_GEN_C
= "opid-opc-gen.c"
68 def __call__(self
, db
, **arguments
):
69 def pairwise(iterable
):
70 (a
, b
) = itertools
.tee(iterable
)
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())
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)
90 class Struct(metaclass
=StructMeta
):
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
103 class Opcode(insndb
.Opcode
):
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
118 bits
= tuple(unwrap(self
.mask
)).count(1)
119 return (self
.PO
, -bits
, self
.mask
)
121 def __lt__(self
, other
):
122 if not isinstance(other
, self
.__class
__):
123 return NotImplemented
124 return self
.weight
< other
.weight
126 class DynamicOperandId(Struct
):
131 class NameId(Struct
):
136 class DynamicOperand(Struct
):
142 class Record(Struct
):
145 dynamic_operand_ids
: DynamicOperandIds
146 static_operands
: StaticOperands
149 class Cache(mdis
.visitor
.ContextVisitor
):
151 self
.__PO
= ([0] * (1 << 6))
153 self
.__static
_operand
= collections
.defaultdict(list)
154 self
.__dynamic
_operand
= collections
.defaultdict(set)
155 self
.__dynamic
_operand
_id
= collections
.defaultdict(list)
157 return super().__init
__()
161 operands_table
= tuple(self
.__dynamic
_operand
.keys())
162 nil
= DynamicOperandId()
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
)
169 def dynamic_operand(item
):
170 ((cls
, span
), names
) = item
171 return DynamicOperand(cls
=cls
, span
=span
, names
=tuple(sorted(names
)))
175 return NameId(name
=name
, index
=index
)
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
]))
184 return Record(opcode
=opcode
, name
=name
,
185 dynamic_operand_ids
=dynamic_operand_ids
,
186 static_operands
=static_operands
)
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
))
193 @mdis.dispatcher
.Hook(insndb
.Record
)
194 @contextlib.contextmanager
195 def dispatch_record(self
, node
):
199 @mdis.dispatcher
.Hook(insndb
.Record
.Opcode
)
200 @contextlib.contextmanager
201 def dispatch_record_opcode(self
, node
):
202 self
.__PO
[self
.__record
.PO
] += 1
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
}
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
):
218 mask
= insndb
.Record
.Opcode
.Mask(wrap(mask
))
219 opcode
= Opcode(node
[0].value
, mask
)
220 self
.__records
.append((opcode
, self
.__record
.name
))
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
)
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
),)
239 class Walker(insndb
.Walker
):
240 @mdis.dispatcher
.Hook(Cache
)
241 def dispatch_cache(self
, node
):
245 class Codegen(mdis
.visitor
.ContextVisitor
):
246 def __init__(self
, cache
, **arguments
):
249 return super().__init
__()
255 def __exit__(self
, exc_type
, exc_value
, exc_traceback
):
258 def emit(self
, message
=""):
259 indent
= ((" " * 4 * self
.__level
) if message
else "")
260 print(f
"{indent}{message}")
262 @mdis.dispatcher
.Hook(Cache
)
263 @contextlib.contextmanager
264 def dispatch_cache(self
, node
):
266 self
.emit(" * Autogenerated by libresoc codegen script")
267 self
.emit(" * DO NOT EDIT: all changes will be lost")
273 class Header(Codegen
):
277 class Source(Codegen
):
278 class Walker(Walker
):
279 @mdis.dispatcher
.Hook(DynamicOperand
, RecordTable
)
280 def dispatch_ignore(self
, node
):
283 @mdis.dispatcher
.Hook(Cache
)
284 def dispatch_cache(self
, node
):
285 (operands
, _
, _
, _
) = node
286 yield from self([operands
])
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
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
301 @mdis.dispatcher
.Hook(Cache
)
302 @contextlib.contextmanager
303 def dispatch_cache(self
, node
):
305 self
.emit(" * Autogenerated by libresoc codegen script")
306 self
.emit(" * DO NOT EDIT: all changes will be lost")
309 self
.emit("#include <stddef.h>")
310 self
.emit("#include <stdint.h>")
312 self
.emit("#include \"opid.h\"")
317 class Record(Struct
):
318 static_operands
: StaticOperands
320 opcode
: insndb
.Record
.Opcode
321 dynamic_operand_ids
: DynamicOperandIds
324 class DisGenSource(Source
):
325 class Walker(Walker
):
326 @mdis.dispatcher
.Hook(DynamicOperand
, RecordTable
)
327 def dispatch_ignore(self
, node
):
330 @mdis.dispatcher
.Hook(Cache
)
331 def dispatch_cache(self
, node
):
332 (operands
, _
, _
, _
) = node
333 yield from self([operands
])
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,")
342 self
.emit("size_t category,")
343 self
.emit("struct opid_operand *operand) {")
345 self
.emit(f
"switch (category) {{")
347 self
.emit("default:")
349 self
.emit("return OPID_ERROR_OPERAND_0_LOOKUP;")
353 self
.emit("return OPID_SUCCESS;")
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 = ("
362 yield from fetch(span
)
364 yield f
"operand->flags = {flags};"
367 def nonzero_handler(span
):
368 yield f
"operand->value = (UINT64_C(1) + ("
370 yield from fetch(span
)
372 yield f
"operand->flags = OPID_OPERAND_NONZERO;"
375 def signed_handler(span
, flags
="OPID_OPERAND_SIGNED"):
376 mask
= f
"(UINT64_C(1) << (UINT64_C({len(span)}) - 1))"
377 yield "operand->value = ("
383 yield from fetch(span
)
391 yield f
"operand->flags = {flags};"
394 def address_handler(span
):
395 yield from signed_handler(span
, "(OPID_OPERAND_ADDRESS | OPID_OPERAND_SIGNED)")
397 def gpr_handler(span
, pair
=False):
399 yield from generic_handler(span
, "OPID_OPERAND_GPR")
401 yield from generic_handler(span
, "(OPID_OPERAND_GPR | OPID_OPERAND_PAIR)")
403 def fpr_handler(span
, pair
=False):
405 yield from generic_handler(span
, "OPID_OPERAND_FPR")
407 yield from generic_handler(span
, "(OPID_OPERAND_FPR | OPID_OPERAND_PAIR)")
409 def cr3_handler(span
):
410 yield from generic_handler(span
, "OPID_OPERAND_CR3")
412 def cr5_handler(span
):
413 yield from generic_handler(span
, "OPID_OPERAND_CR5")
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
,
428 self
.emit(f
"case 0x{(path + 1):02x}: /* {', '.join(node.names)} */")
430 for (cls
, handler
) in handlers
.items():
431 if issubclass(node
.cls
, cls
):
434 raise ValueError("unknown handler")
435 for line
in handler(span
=node
.span
):
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
):
448 @mdis.dispatcher
.Hook(Record
)
449 def dispatch_record(self
, node
):
451 "dynamic_operand_ids": "operands",
454 for field
in dataclasses
.fields(node
):
456 value
= getattr(node
, key
)
457 key
= keys
.get(key
, key
)
458 yield (value
, node
, key
, mdis
.walker
.AttributePath
)
460 @mdis.dispatcher
.Hook(Cache
)
461 def dispatch_cache(self
, node
):
462 (_
, records
, opcodes
, names
) = node
463 yield from self([records
, opcodes
, names
])
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
472 @mdis.dispatcher
.Hook(StaticOperands
)
473 @contextlib.contextmanager
474 def dispatch_static_operands(self
, node
):
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))}]")
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
494 @mdis.dispatcher
.Hook(Opcode
)
495 @contextlib.contextmanager
496 def dispatch_opcode(self
, node
):
497 self
.emit(".opcode = {")
499 self
.emit(f
".value = UINT64_C(0x{node.value:08x}),")
500 self
.emit(f
".mask = UINT64_C(0x{node.mask:08x}),")
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] = {")
515 for index
in range(64):
518 self
.emit(f
"[{index}] = {{{head}, {tail}}},")
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}]}},")
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[] = {{")
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
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",
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
)
557 arguments
= dict(vars(main_parser
.parse_args()))
558 mode
= Mode(arguments
.pop("mode"))
559 db
= insndb
.Database(root
=arguments
.pop("database"))
561 return mode(db
=db
, **arguments
)
564 if __name__
== "__main__":