pysvp64db: fix traversal
[openpower-isa.git] / src / openpower / sv / sv_binutils.py
1 import argparse as _argparse
2 import dataclasses as _dataclasses
3 import enum as _enum
4
5 import os as _os
6 import sys as _sys
7
8 _sys.path.append(_os.path.dirname(_os.path.realpath(__file__)) + "/../../")
9
10 from openpower.decoder.power_enums import (
11 In1Sel as _In1Sel,
12 In2Sel as _In2Sel,
13 In3Sel as _In3Sel,
14 OutSel as _OutSel,
15 CRInSel as _CRInSel,
16 CRIn2Sel as _CRIn2Sel,
17 CROutSel as _CROutSel,
18 SVPType as _SVPType,
19 SVEType as _SVEType,
20 SVExtra as _SVExtra,
21 SVMode as _SVMode,
22 find_wiki_dir as _find_wiki_dir,
23 )
24 from openpower.consts import SVP64MODE as _SVP64MODE
25 from openpower.insndb.core import Database as _Database
26 from openpower.insndb.core import SVP64Instruction as _SVP64Instruction
27
28
29 DISCLAIMER = """\
30 /* {path} -- {desc}
31 Copyright (C) 2022 Free Software Foundation, Inc.
32 Written by Dmitry Selyutin (ghostmansd).
33 Sponsored by NLnet and NGI POINTER under EU Grants 871528 and 957073.
34
35 This file is part of the GNU opcodes library.
36
37 This library is free software; you can redistribute it and/or modify
38 it under the terms of the GNU General Public License as published by
39 the Free Software Foundation; either version 3, or (at your option)
40 any later version.
41
42 It is distributed in the hope that it will be useful, but WITHOUT
43 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
44 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
45 License for more details.
46
47 You should have received a copy of the GNU General Public License
48 along with this file; see the file COPYING. If not, write to the
49 Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
50 MA 02110-1301, USA. */\
51 """
52
53
54 def indent(strings):
55 return map(lambda string: (" " + string), strings)
56
57
58 class ObjectMeta(type):
59 def __new__(metacls, clsname, bases, ns,
60 c_typedef="void", **kwargs):
61 if c_typedef == "void":
62 for base in bases:
63 if (hasattr(base, "c_typedef") and
64 (base.c_typedef != "void")):
65 c_typedef = base.c_typedef
66 break
67
68 ns.setdefault("c_typedef", c_typedef)
69 ns.update(kwargs)
70
71 return super().__new__(metacls, clsname, bases, ns)
72
73
74 class Object(metaclass=ObjectMeta):
75 @classmethod
76 def c_decl(cls):
77 yield from ()
78
79 @classmethod
80 def c_var(cls, name, prefix="", suffix=""):
81 return f"{prefix}{cls.c_typedef} {name}{suffix}"
82
83 def c_value(self, *, prefix="", suffix="", **kwargs):
84 yield from ()
85
86
87 class ArrayMeta(ObjectMeta):
88 def __new__(metacls, clsname, bases, ns,
89 c_base, c_size, **kwargs):
90 if c_size is Ellipsis:
91 clsname = f"{clsname}[]"
92 else:
93 clsname = f"{clsname}[{c_size}]"
94
95 return super().__new__(metacls, clsname, bases, ns,
96 c_typedef=c_base.c_typedef, c_base=c_base, c_size=c_size, **kwargs)
97
98 def __getitem__(cls, key):
99 (c_base, c_size) = key
100 return cls.__class__(cls.__name__, (cls,), {},
101 c_base=c_base, c_size=c_size)
102
103
104 class Array(Object, tuple, metaclass=ArrayMeta,
105 c_base=Object, c_size=...):
106 @classmethod
107 def c_decl(cls):
108 if cls.c_size is Ellipsis:
109 size = ""
110 else:
111 size = f"{len(cls)}"
112 yield f"{cls.c_base.c_typedef}[{size}]"
113
114 @classmethod
115 def c_var(cls, name, prefix="", suffix=""):
116 if cls.c_size is Ellipsis:
117 size = ""
118 else:
119 size = f"{len(cls)}"
120 return f"{prefix}{cls.c_base.c_typedef} {name}[{size}]{suffix}"
121
122 def c_value(self, *, prefix="", suffix="", **kwargs):
123 yield f"{prefix}{{"
124 for item in self:
125 yield from indent(item.c_value(suffix=","))
126 yield f"}}{suffix}"
127
128
129 class Void(Object, c_typedef="void"):
130 def c_var(cls, name, prefix="", suffix=""):
131 raise NotImplementedError
132
133
134 class EnumMeta(_enum.EnumMeta, ObjectMeta):
135 def __call__(cls, clsname, entries,
136 c_tag=None, c_typedef=None, exclude=None):
137 if exclude is None:
138 exclude = frozenset()
139 if isinstance(entries, type) and issubclass(entries, _enum.Enum):
140 # Use __members__, not __iter__, otherwise aliases are lost.
141 entries = dict(entries.__members__)
142 if isinstance(entries, dict):
143 entries = tuple(entries.items())
144 entries = ((key, value) for (key, value) in entries if key not in exclude)
145
146 if c_tag is None:
147 c_tag = f"svp64_{clsname.lower()}"
148 if c_typedef is None:
149 c_typedef = f"enum {c_tag}"
150
151 base = ObjectMeta(cls.__name__, (), {},
152 c_tag=c_tag, c_typedef=c_typedef)
153
154 return super().__call__(value=clsname, names=entries, type=base)
155
156
157 class Enum(Object, _enum.Enum, metaclass=EnumMeta):
158 @property
159 def c_name(self):
160 return f"{self.c_tag.upper()}_{self.name.upper()}"
161
162 @classmethod
163 def c_decl(cls):
164 yield f"{cls.c_typedef} {{"
165 for item in cls:
166 yield from indent(item.c_value(suffix=","))
167 yield f"}};"
168
169 def c_value(self, *, prefix="", suffix="", **kwargs):
170 yield f"{prefix}{self.c_name}{suffix}"
171
172 @classmethod
173 def c_var(cls, name, prefix="", suffix=""):
174 return f"{prefix}{cls.c_typedef} {name}{suffix}"
175
176
177 In1Sel = Enum("In1Sel", _In1Sel, c_tag="svp64_in1_sel")
178 In2Sel = Enum("In2Sel", _In2Sel, c_tag="svp64_in2_sel")
179 In3Sel = Enum("In3Sel", _In3Sel, c_tag="svp64_in3_sel")
180 OutSel = Enum("OutSel", _OutSel, c_tag="svp64_out_sel")
181 CRInSel = Enum("CRInSel", _CRInSel, c_tag="svp64_cr_in_sel")
182 CRIn2Sel = Enum("CRIn2Sel", _CRIn2Sel, c_tag="svp64_cr_in2_sel")
183 CROutSel = Enum("CROutSel", _CROutSel, c_tag="svp64_cr_out_sel")
184 PType = Enum("PType", _SVPType, c_tag="svp64_ptype")
185 EType = Enum("EType", _SVEType, c_tag="svp64_etype", exclude="NONE")
186 Extra = Enum("Extra", _SVExtra, c_tag="svp64_extra", exclude="Idx_1_2")
187 Mode = Enum("Mode", _SVMode, c_tag="svp64_mode")
188
189
190 class Constant(_enum.Enum, metaclass=EnumMeta):
191 @classmethod
192 def c_decl(cls):
193 yield f"/* {cls.c_tag.upper()} constants */"
194 # Use __members__, not __iter__, otherwise aliases are lost.
195 for (key, item) in cls.__members__.items():
196 key = f"{cls.c_tag.upper()}_{key.upper()}"
197 value = f"0x{item.value:08x}U"
198 yield f"#define {key} {value}"
199
200 def c_value(self, *, prefix="", suffix="", **kwargs):
201 yield f"{prefix}{self.c_tag.upper()}_{self.c_name.upper()}{suffix}"
202
203
204 ModeConst = Constant("Mode", _SVP64MODE)
205
206
207 class StructMeta(ObjectMeta):
208 def __new__(metacls, clsname, bases, ns,
209 c_tag=None, c_typedef=None):
210
211 if c_tag is None:
212 c_tag = f"svp64_{clsname.lower()}"
213 if c_typedef is None:
214 c_typedef = f"struct {c_tag}"
215
216 return super().__new__(metacls, clsname, bases, ns,
217 c_typedef=c_typedef, c_tag=c_tag)
218
219
220 @_dataclasses.dataclass(eq=True, frozen=True)
221 class Struct(Object, metaclass=StructMeta):
222 @classmethod
223 def c_decl(cls):
224 def transform(field):
225 return field.type.c_var(name=f"{field.name}", suffix=";")
226
227 yield f"{cls.c_typedef} {{"
228 yield from indent(map(transform, _dataclasses.fields(cls)))
229 yield f"}};"
230
231 def c_value(self, *, prefix="", suffix="", **kwargs):
232 yield f"{prefix}{{"
233 for field in _dataclasses.fields(self):
234 name = field.name
235 attr = getattr(self, name)
236 yield from indent(attr.c_value(prefix=f".{name} = ", suffix=","))
237 yield f"}}{suffix}"
238
239
240 class Integer(Object, str):
241 def c_value(self, *, prefix="", suffix="", **kwargs):
242 yield f"{prefix}{self}{suffix}"
243
244
245 class Byte(Integer, c_typedef="uint8_t"):
246 pass
247
248
249 class Size(Integer, c_typedef="size_t"):
250 pass
251
252
253 class UInt32(Integer, c_typedef="uint32_t"):
254 pass
255
256
257 class UInt64(Integer, c_typedef="uint64_t"):
258 pass
259
260
261 @_dataclasses.dataclass(eq=True, frozen=True)
262 class Instruction(Struct, c_tag="svp64_insn"):
263 value: UInt64
264
265 @classmethod
266 def c_decl(cls):
267 def mangle(path, action):
268 if path.endswith("]"):
269 path = path[:-1]
270 for symbol in (".", "[", "]"):
271 path = path.replace(symbol, "_")
272 if path != "":
273 return f"{cls.c_tag}_{action}_{path}"
274 else:
275 return f"{cls.c_tag}_{action}"
276
277 def getter(path, field):
278 yield "static inline uint64_t"
279 yield f"{mangle(path, 'get')}(const {cls.c_typedef} *insn)"
280 yield "{"
281 yield from indent(["uint64_t value = insn->value;"])
282 yield from indent(["return ("])
283 actions = []
284 for (dst, src) in enumerate(reversed(field)):
285 src = (64 - (src + 1))
286 dst = f"UINT64_C({dst})"
287 src = f"UINT64_C({src})"
288 action = f"(((value >> {src}) & UINT64_C(1)) << {dst})"
289 actions.append(action)
290 for action in indent(indent(actions[:-1])):
291 yield f"{action} |"
292 yield from indent(indent([f"{actions[-1]}"]))
293 yield from indent([");"])
294 yield "}"
295 yield ""
296
297 def setter(path, field):
298 mask = ((2 ** 64) - 1)
299 for bit in field:
300 mask &= ~(1 << (64 - (bit + 1)))
301 action = mangle(path, "set")
302 yield "static inline void"
303 yield f"{mangle(path, 'set')}({cls.c_typedef} *insn, uint64_t value)"
304 yield "{"
305 yield from indent([f"insn->value &= UINT64_C(0x{mask:016x});"])
306 actions = []
307 for (src, dst) in enumerate(reversed(field)):
308 dst = (64 - (dst + 1))
309 dst = f"UINT64_C({dst})"
310 src = f"UINT64_C({src})"
311 action = f"(((value >> {src}) & UINT64_C(1)) << {dst})"
312 actions.append(action)
313 yield from indent(["insn->value |= ("])
314 for action in indent(indent(actions[:-1])):
315 yield f"{action} |"
316 yield from indent(indent([f"{actions[-1]}"]))
317 yield from indent([");"])
318 yield "}"
319 yield ""
320
321 yield from super().c_decl()
322 yield ""
323 for (path, field) in _SVP64Instruction.traverse(path=""):
324 yield from getter(path, field)
325 yield from setter(path, field)
326
327
328 class Name(Object, str, c_typedef="const char *"):
329 def __repr__(self):
330 escaped = self.replace("\"", "\\\"")
331 return f"\"{escaped}\""
332
333 def c_value(self, *, prefix="", suffix="", **kwargs):
334 yield f"{prefix}{self!r}{suffix}"
335
336 @classmethod
337 def c_var(cls, name, prefix="", suffix=""):
338 return f"{prefix}const char *{name}{suffix}"
339
340
341 class BooleanMeta(ObjectMeta):
342 def __len__(cls):
343 return 1
344
345
346 class Boolean(Object, metaclass=BooleanMeta):
347 def __init__(self, value):
348 self.__state = bool(value)
349 return super().__init__()
350
351 def __bool__(self):
352 return self.__state
353
354 def __repr__(self):
355 return self.__state.__repr__()
356
357 @property
358 def name(self):
359 return "true" if self else "false"
360
361 @property
362 def value(self):
363 return "true" if self else "false"
364
365 def c_value(self, *, prefix="", suffix="", **kwargs):
366 yield f"{prefix}{self.value}{suffix}"
367
368 @classmethod
369 def c_var(cls, name, prefix="", suffix=""):
370 return f"{prefix}bool {name}{suffix}"
371
372
373 @_dataclasses.dataclass(eq=True, frozen=True)
374 class Desc(Struct):
375 mode: Mode
376 in1: In1Sel
377 in2: In2Sel
378 in3: In3Sel
379 out: OutSel
380 out2: OutSel
381 cr_in: CRInSel
382 cr_in2: CRIn2Sel
383 cr_out: CROutSel
384 ptype: PType
385 etype: EType
386 extra_idx_in1: Extra
387 extra_idx_in2: Extra
388 extra_idx_in3: Extra
389 extra_idx_out: Extra
390 extra_idx_out2: Extra
391 extra_idx_cr_in: Extra
392 extra_idx_cr_in2: Extra
393 extra_idx_cr_out: Extra
394 Rc: Boolean
395
396 @classmethod
397 def c_decl(cls):
398 bits_all = 0
399 yield f"struct svp64_desc {{"
400 for field in _dataclasses.fields(cls):
401 bits = len(field.type).bit_length()
402 yield from indent([f"uint64_t {field.name} : {bits};"])
403 bits_all += bits
404 bits_rsvd = (64 - (bits_all % 64))
405 if bits_rsvd:
406 yield from indent([f"uint64_t : {bits_rsvd};"])
407 yield f"}};"
408
409
410 @_dataclasses.dataclass(eq=True, frozen=True)
411 class Opcode(Struct):
412 class Value(UInt32):
413 def __new__(cls, value):
414 if isinstance(value, int):
415 value = f"0x{value:08x}"
416 return super().__new__(cls, value)
417
418 class Mask(UInt32):
419 def __new__(cls, value):
420 if isinstance(value, int):
421 value = f"0x{value:08x}"
422 return super().__new__(cls, value)
423
424 value: Value
425 mask: Mask
426
427
428 class Opcodes(Object, c_typedef="const struct svp64_opcode *"):
429 def __init__(self, offset):
430 self.__offset = offset
431 return super().__init__()
432
433 def __repr__(self):
434 return f"{self.__class__.__name__}({self.__offset})"
435
436 @classmethod
437 def c_var(cls, name, prefix="", suffix=""):
438 return f"{prefix}{cls.c_typedef}{name}{suffix}"
439
440 @classmethod
441 def c_decl(cls):
442 yield "const struct svp64_opcode *opcodes;"
443
444 def c_value(self, *, prefix="", suffix="", **kwargs):
445 yield f"{prefix}&svp64_opcodes[{self.__offset}]{suffix}"
446
447
448 @_dataclasses.dataclass(eq=True, frozen=True)
449 class Record(Struct):
450 name: Name
451 desc: Desc
452 opcodes: Opcodes
453 nr_opcodes: Size
454
455 def __lt__(self, other):
456 if not isinstance(other, self.__class__):
457 return NotImplemented
458
459 return self.name < other.name
460
461
462 class Codegen(_enum.Enum):
463 PPC_SVP64_GEN_H = "include/opcode/ppc-svp64-gen.h"
464 PPC_SVP64_OPC_GEN_C = "opcodes/ppc-svp64-opc-gen.c"
465
466 def __str__(self):
467 return self.value
468
469 def generate(self, opcodes, records):
470 def ppc_svp64_h(opcodes, nr_opcodes, records, nr_records):
471 disclaimer = DISCLAIMER.format(path=str(self),
472 desc="Header file for PowerPC opcode table (SVP64 extensions)")
473 yield from disclaimer.splitlines()
474 yield ""
475
476 yield f"#ifndef {self.name}"
477 yield f"#define {self.name}"
478 yield ""
479
480 yield "#include <stdint.h>"
481 yield ""
482
483 yield "#ifdef __cplusplus"
484 yield "extern \"C\" {"
485 yield "#endif"
486 yield ""
487
488 enums = (
489 In1Sel, In2Sel, In3Sel, OutSel,
490 CRInSel, CRIn2Sel, CROutSel,
491 PType, EType, Extra,
492 Mode, ModeConst,
493 )
494 for enum in enums:
495 yield from enum.c_decl()
496 yield ""
497
498 for cls in (Instruction, Desc, Opcode, Record):
499 yield from cls.c_decl()
500 yield ""
501
502 yield opcodes.c_var("svp64_opcodes",
503 prefix="extern const ", suffix=";")
504 yield nr_opcodes.c_var("svp64_nr_opcodes",
505 prefix="extern const ", suffix=";")
506 yield ""
507
508 yield records.c_var("svp64_records",
509 prefix="extern const ", suffix=";")
510 yield nr_records.c_var("svp64_nr_records",
511 prefix="extern const ", suffix=";")
512 yield ""
513
514 yield "extern const struct powerpc_pd_reg svp64_regs[];"
515 yield Size.c_var("svp64_num_regs", prefix="extern const ", suffix=";")
516 yield ""
517
518 yield "#ifdef __cplusplus"
519 yield "}"
520 yield "#endif"
521 yield ""
522
523 yield f"#endif /* {self.name} */"
524
525 def ppc_svp64_opc_c(opcodes, nr_opcodes, records, nr_records):
526 disclaimer = DISCLAIMER.format(path=str(self),
527 desc="PowerPC opcode list (SVP64 extensions)")
528 yield from disclaimer.splitlines()
529 yield ""
530
531 yield "#include <stdbool.h>"
532 yield "#include \"opcode/ppc-svp64.h\""
533 yield ""
534
535 def opindex(enum, name, table):
536 sep = (max(map(len, list(table.values()) + ["UNUSED"])) + 1)
537 c_tag = f"svp64_{enum.__name__.lower()}"
538 yield "static inline ppc_opindex_t"
539 yield f"svp64_desc_{name}_opindex(const struct svp64_desc *desc)"
540 yield "{"
541 yield from indent(["static const ppc_opindex_t table[] = {"])
542 for key in enum:
543 value = table.get(key, "UNUSED")
544 yield from indent(indent([f"{value:{sep}}, /* {key.c_name} */"]))
545 yield from indent(["};"])
546 yield ""
547 yield from indent([f"return table[desc->{name}];"])
548 yield "}"
549 yield ""
550
551 yield from opindex(In1Sel, "in1", {
552 In1Sel.RA: "RA",
553 In1Sel.RA_OR_ZERO: "RA0",
554 In1Sel.SPR: "SPR",
555 In1Sel.RS: "RS",
556 In1Sel.FRA: "FRA",
557 In1Sel.FRS: "FRS",
558 })
559 yield from opindex(In2Sel, "in2", {
560 In2Sel.RB: "RB",
561 In2Sel.SPR: "SPR",
562 In2Sel.RS: "RS",
563 In2Sel.FRB: "FRB",
564 })
565 yield from opindex(In3Sel, "in3", {
566 In3Sel.RS: "RS",
567 In3Sel.RB: "RB",
568 In3Sel.FRS: "FRS",
569 In3Sel.FRC: "FRC",
570 In3Sel.RC: "RC",
571 In3Sel.RT: "RT",
572 })
573 for name in ("out", "out2"):
574 yield from opindex(OutSel, name, {
575 OutSel.RT: "RT",
576 OutSel.RA: "RA",
577 OutSel.SPR: "SPR",
578 OutSel.RT_OR_ZERO: "RT",
579 OutSel.FRT: "FRT",
580 OutSel.FRS: "FRS",
581 OutSel.RS: "RS",
582 })
583 yield from opindex(CRInSel, "cr_in", {
584 CRInSel.BI: "BI",
585 CRInSel.BFA: "BFA",
586 CRInSel.BC: "BC",
587 CRInSel.BA: "BA",
588 CRInSel.WHOLE_REG: "FXM",
589 })
590 yield from opindex(CRIn2Sel, "cr_in2", {
591 CRIn2Sel.BB: "BB",
592 })
593 yield from opindex(CROutSel, "cr_out", {
594 CROutSel.BF: "BF",
595 CROutSel.BT: "BT",
596 CROutSel.WHOLE_REG: "FXM",
597 })
598
599 yield opcodes.c_var("svp64_opcodes",
600 prefix="const ", suffix=" = \\")
601 yield from opcodes.c_value(prefix="", suffix=";")
602 yield ""
603 yield nr_opcodes.c_var("svp64_nr_opcodes",
604 prefix="const ", suffix=" = \\")
605 yield from indent(nr_opcodes.c_value(suffix=";"))
606 yield ""
607
608 yield records.c_var("svp64_records",
609 prefix="const ", suffix=" = \\")
610 yield from records.c_value(prefix="", suffix=";")
611 yield ""
612 yield nr_records.c_var("svp64_nr_records",
613 prefix="const ", suffix=" = \\")
614 yield from indent(nr_records.c_value(suffix=";"))
615 yield ""
616
617 yield "const struct powerpc_pd_reg svp64_regs[] = {"
618 regs = {}
619 for (category, count, flags) in sorted((
620 ("r", 128, "PPC_OPERAND_GPR"),
621 ("f", 128, "PPC_OPERAND_FPR"),
622 ("cr", 128, "PPC_OPERAND_CR_REG"),
623 )):
624 for index in range(count):
625 regs[f"{category}{index}"] = (index, flags)
626 regs[f"{category}.{index}"] = (index, flags)
627 for (name, (index, flags)) in sorted(regs.items()):
628 yield from indent([f"{{\"{name}\", {index}, {flags}}},"])
629 yield "};"
630 yield ""
631
632 num_regs = Size("(sizeof (svp64_regs) / sizeof (svp64_regs[0]))")
633 yield Size.c_var("svp64_num_regs",
634 prefix="const ", suffix=" = \\")
635 yield from indent(num_regs.c_value(suffix=";"))
636
637 opcodes = Array[Opcode, ...](opcodes)
638 nr_opcodes = Size("(sizeof (svp64_opcodes) / sizeof (svp64_opcodes[0]))")
639
640 records = Array[Record, ...](records)
641 nr_records = Size("(sizeof (svp64_records) / sizeof (svp64_records[0]))")
642
643 yield from {
644 Codegen.PPC_SVP64_GEN_H: ppc_svp64_h,
645 Codegen.PPC_SVP64_OPC_GEN_C: ppc_svp64_opc_c,
646 }[self](opcodes, nr_opcodes, records, nr_records)
647
648
649 def collect(db):
650 opcodes = []
651 records = []
652 fields = {field.name:field.type for field in _dataclasses.fields(Desc)}
653
654 for insn in filter(lambda insn: insn.svp64 is not None, db):
655 desc = {}
656
657 for (key, cls) in fields.items():
658 value = getattr(insn, key)
659
660 if (((cls is EType) and (value is _SVEType.NONE)) or
661 ((cls is Extra) and (value is _SVExtra.Idx_1_2))):
662 desc = None
663 break
664
665 if issubclass(cls, Boolean):
666 value = Boolean(value)
667 elif issubclass(cls, _enum.Enum):
668 value = cls[value.name]
669 else:
670 value = cls(value)
671 desc[key] = value
672
673 if desc is None:
674 continue
675
676 name = Name(f"sv.{insn.name}")
677 offset = len(opcodes)
678 for opcode in insn.opcodes:
679 value = Opcode.Value(opcode.value)
680 mask = Opcode.Mask(opcode.mask)
681 opcode = Opcode(value=value, mask=mask)
682 opcodes.append(opcode)
683 desc = Desc(**desc)
684
685 record = Record(name=name, desc=desc,
686 opcodes=Opcodes(offset), nr_opcodes=Size(len(insn.opcodes)))
687 records.append(record)
688
689 return (opcodes, records)
690
691
692 def main(codegen):
693 db = _Database(_find_wiki_dir())
694 (opcodes, records) = collect(db)
695 for line in codegen.generate(opcodes, records):
696 print(line)
697
698
699 if __name__ == "__main__":
700 parser = _argparse.ArgumentParser()
701 parser.add_argument("codegen",
702 type=Codegen, choices=Codegen,
703 help="code generator")
704
705 args = vars(parser.parse_args())
706 main(**args)