sv_binutils: introduce code generator class
[openpower-isa.git] / src / openpower / sv / sv_binutils.py
1 import abc as _abc
2 import argparse as _argparse
3 import codecs as _codecs
4 import dataclasses as _dataclasses
5 import enum as _enum
6 import pathlib as _pathlib
7 import re as _re
8 import sys as _sys
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 CROutSel as _CROutSel,
17 SVPtype as _SVPtype,
18 SVEtype as _SVEtype,
19 SVEXTRA as _SVEXTRA,
20 )
21
22
23 def indent(strings):
24 return map(lambda string: (" " + string), strings)
25
26
27 class Field:
28 @classmethod
29 @_abc.abstractmethod
30 def c_decl(self, name):
31 pass
32
33 @_abc.abstractmethod
34 def c_value(self, prefix="", suffix=""):
35 pass
36
37 @classmethod
38 @_abc.abstractmethod
39 def c_var(self, name):
40 pass
41
42
43 class Enum(Field, _enum.Enum):
44 @classmethod
45 def c_decl(cls):
46 c_tag = f"svp64_{cls.__name__.lower()}"
47 yield f"enum {c_tag} {{"
48 for item in cls:
49 yield from indent(item.c_value(suffix=","))
50 yield f"}};"
51
52 def c_value(self, prefix="", suffix=""):
53 c_tag = f"svp64_{self.__class__.__name__.lower()}"
54 yield f"{prefix}{c_tag.upper()}_{self.name.upper()}{suffix}"
55
56 @classmethod
57 def c_var(cls, name):
58 c_tag = f"svp64_{cls.__name__.lower()}"
59 yield f"enum {c_tag} {name};"
60
61
62 # Python forbids inheriting from enum unless it's empty.
63 In1Sel = Enum("In1Sel", {item.name:item.value for item in _In1Sel})
64 In2Sel = Enum("In2Sel", {item.name:item.value for item in _In2Sel})
65 In3Sel = Enum("In3Sel", {item.name:item.value for item in _In3Sel})
66 OutSel = Enum("OutSel", {item.name:item.value for item in _OutSel})
67 CRInSel = Enum("CRInSel", {item.name:item.value for item in _CRInSel})
68 CROutSel = Enum("CROutSel", {item.name:item.value for item in _CROutSel})
69 SVPType = Enum("SVPType", {item.name:item.value for item in _SVPtype})
70 SVEType = Enum("SVEType", {item.name:item.value for item in _SVEtype})
71 SVEXTRA = Enum("SVEXTRA", {item.name:item.value for item in _SVEXTRA})
72
73
74 class Opcode(Field, str):
75 def c_value(self, prefix="", suffix=""):
76 yield f"{prefix}\"{self}\"{suffix}"
77
78 @classmethod
79 def c_var(cls, name):
80 yield f"const char *{name};"
81
82
83 class Name(Field, str):
84 def __repr__(self):
85 escaped = self.replace("\"", "\\\"")
86 return f"\"{escaped}\""
87
88 def c_value(self, prefix="", suffix=""):
89 yield f"{prefix}{self!r}{suffix}"
90
91 @classmethod
92 def c_var(cls, name):
93 yield f"const char *{name};"
94
95
96 @_dataclasses.dataclass(eq=True, frozen=True)
97 class Entry:
98 name: Name
99 opcode: Opcode
100 in1: In1Sel
101 in2: In2Sel
102 in3: In3Sel
103 out: OutSel
104 out2: OutSel
105 cr_in: CRInSel
106 cr_out: CROutSel
107 ptype: SVPType
108 etype: SVEType
109 sv_in1: SVEXTRA
110 sv_in2: SVEXTRA
111 sv_in3: SVEXTRA
112 sv_out: SVEXTRA
113 sv_out2: SVEXTRA
114 sv_cr_in: SVEXTRA
115 sv_cr_out: SVEXTRA
116
117 @classmethod
118 def c_decl(cls):
119 yield f"struct svp64_entry {{"
120 for field in _dataclasses.fields(cls):
121 if issubclass(field.type, Enum):
122 bits = len(field.type).bit_length()
123 yield from indent([f"uint64_t {field.name} : {bits};"])
124 else:
125 yield from indent(field.type.c_var(name=field.name))
126 yield f"}};"
127
128 def c_value(self, prefix="", suffix=""):
129 yield f"{prefix}{{"
130 for field in _dataclasses.fields(self):
131 name = field.name
132 attr = getattr(self, name)
133 yield from indent(attr.c_value(prefix=f".{name} = ", suffix=","))
134 yield f"}}{suffix}"
135
136 @classmethod
137 def c_var(cls, name):
138 yield f"struct svp64_entry {name};"
139
140
141 class Codegen(_enum.Enum):
142 PPC_OPC_SVP64_H = _enum.auto()
143 PPC_OPC_SVP64_C = _enum.auto()
144
145 @classmethod
146 def _missing_(cls, value):
147 return {
148 "ppc-opc-svp64.h": Codegen.PPC_OPC_SVP64_H,
149 "ppc-opc-svp64.c": Codegen.PPC_OPC_SVP64_C,
150 }[value]
151
152 def __str__(self):
153 return {
154 Codegen.PPC_OPC_SVP64_H: "ppc-opc-svp64.h",
155 Codegen.PPC_OPC_SVP64_C: "ppc-opc-svp64.c",
156 }[self]
157
158 def generate(self, entries):
159 def ppc_opc_svp64_h(entries):
160 yield from ()
161
162 def ppc_opc_svp64_c(entries):
163 yield from ()
164
165 return {
166 Codegen.PPC_OPC_SVP64_H: ppc_opc_svp64_h,
167 Codegen.PPC_OPC_SVP64_C: ppc_opc_svp64_c,
168 }[self](entries)
169
170
171 def regex_enum(enum):
172 assert issubclass(enum, _enum.Enum)
173 return "|".join(item.name for item in enum)
174
175
176 PATTERN_VHDL_BINARY = r"(?:2#[01]+#)"
177 PATTERN_DECIMAL = r"(?:[0-9]+)"
178 PATTERN_PARTIAL_BINARY = r"(?:[01-]+)"
179
180 # Examples of the entries to be caught by the pattern below:
181 # 2 => (P2, EXTRA3, RA_OR_ZERO, NONE, NONE, RT, NONE, NONE, NONE, Idx1, NONE, NONE, Idx0, NONE, NONE, NONE), -- lwz
182 # -----10110 => (P2, EXTRA3, NONE, FRB, NONE, FRT, NONE, NONE, CR1, NONE, Idx1, NONE, Idx0, NONE, NONE, Idx0), -- fsqrts
183 # 2#0000000000# => (P2, EXTRA3, NONE, NONE, NONE, NONE, NONE, BFA, BF, NONE, NONE, NONE, NONE, NONE, Idx1, Idx0), -- mcrf
184 PATTERN = "".join((
185 r"^\s*",
186 rf"(?P<opcode>{PATTERN_VHDL_BINARY}|{PATTERN_DECIMAL}|{PATTERN_PARTIAL_BINARY})",
187 r"\s?=>\s?",
188 r"\(",
189 r",\s".join((
190 rf"(?P<ptype>{regex_enum(_SVPtype)})",
191 rf"(?P<etype>{regex_enum(_SVEtype)})",
192 rf"(?P<in1>{regex_enum(_In1Sel)})",
193 rf"(?P<in2>{regex_enum(_In2Sel)})",
194 rf"(?P<in3>{regex_enum(_In3Sel)})",
195 rf"(?P<out>{regex_enum(_OutSel)})",
196 rf"(?P<out2>{regex_enum(_OutSel)})",
197 rf"(?P<cr_in>{regex_enum(_CRInSel)})",
198 rf"(?P<cr_out>{regex_enum(_CROutSel)})",
199 rf"(?P<sv_in1>{regex_enum(_SVEXTRA)})",
200 rf"(?P<sv_in2>{regex_enum(_SVEXTRA)})",
201 rf"(?P<sv_in3>{regex_enum(_SVEXTRA)})",
202 rf"(?P<sv_out>{regex_enum(_SVEXTRA)})",
203 rf"(?P<sv_out2>{regex_enum(_SVEXTRA)})",
204 rf"(?P<sv_cr_in>{regex_enum(_SVEXTRA)})",
205 rf"(?P<sv_cr_out>{regex_enum(_SVEXTRA)})",
206 )),
207 r"\)",
208 r",",
209 r"\s?--\s?",
210 r"(?P<name>[A-Za-z0-9_\./]+)",
211 r"\s*$",
212 ))
213 REGEX = _re.compile(PATTERN)
214
215
216 def parse(stream):
217 for line in stream:
218 match = REGEX.match(line)
219 if match is not None:
220 entry = match.groupdict()
221 for field in _dataclasses.fields(Entry):
222 key = field.name
223 value = entry[key]
224 if issubclass(field.type, _enum.Enum):
225 value = {item.name:item for item in field.type}[value]
226 else:
227 value = field.type(value)
228 entry[key] = value
229 yield Entry(**entry)
230
231
232 def main(codegen):
233 entries = tuple(parse(_sys.stdin))
234 for line in codegen.generate(entries):
235 print(line)
236
237
238 if __name__ == "__main__":
239 parser = _argparse.ArgumentParser()
240 parser.add_argument("codegen", type=Codegen, choices=Codegen, help="code generator")
241
242 args = vars(parser.parse_args())
243 main(**args)