detect duplicate comment fields
[simplev-cpp.git] / generate_headers.py
1 #!/usr/bin/env python3
2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # See Notices.txt for copyright information
4
5 import sys
6 from io import StringIO
7 from typing import Any, Dict, List
8 from soc.decoder.pseudo.pagereader import ISA
9 from soc.decoder.power_svp64 import SVP64RM
10 from soc.decoder.power_fields import DecodeFields
11 from soc.decoder.power_decoder import create_pdecode
12
13
14 # generated text should wrap at 80 columns
15 OUTPUT_WIDTH = 80
16
17
18 def wcwidth(text: str) -> int:
19 """ return the number of columns that `text` takes up if printed to a terminal.
20 returns -1 if any characters are not printable.
21 """
22 # TODO: replace with wcwidth from wcwidth
23 # package when we add pip packaging files
24 if text.isprintable():
25 return len(text)
26 return -1
27
28
29 class TableEntry:
30 def __init__(self, key: str, value: Any):
31 self.key = repr(key)
32 self.key_lines = self.key.splitlines()
33 for i in range(len(self.key_lines)):
34 self.key_lines[i] = self.key_lines[i].rstrip()
35 self.value = repr(value)
36 self.value_lines = self.value.splitlines()
37 for i in range(len(self.value_lines)):
38 self.value_lines[i] = self.value_lines[i].rstrip()
39 self.width = max(max(map(wcwidth, self.key_lines)),
40 max(map(wcwidth, self.value_lines)))
41
42
43 def format_dict_as_tables(d: Dict[str, Any], column_width: int) -> List[str]:
44 entries = [TableEntry(k, v) for k, v in d.items()]
45 entries.sort(key=lambda entry: entry.key)
46 entries.reverse()
47 col_sep_start = '| '
48 col_sep_start_top_line = '+-'
49 col_sep_start_mid_line = '+='
50 col_sep_start_bottom_line = '+-'
51 col_sep_start_width = wcwidth(col_sep_start)
52 col_sep_mid = ' | '
53 col_sep_mid_top_line = '-+-'
54 col_sep_mid_mid_line = '=+='
55 col_sep_mid_bottom_line = '-+-'
56 col_sep_mid_width = wcwidth(col_sep_mid)
57 col_sep_end = ' |'
58 col_sep_end_top_line = '-+'
59 col_sep_end_mid_line = '=+'
60 col_sep_end_bottom_line = '-+'
61 col_sep_end_width = wcwidth(col_sep_end)
62 col_top_line_char = '-'
63 col_mid_line_char = '='
64 col_bottom_line_char = '-'
65 retval: List[str] = []
66 while len(entries) != 0:
67 total_width = col_sep_start_width - col_sep_mid_width
68 column_entries: List[TableEntry] = []
69 key_line_count = 0
70 value_line_count = 0
71 while len(entries) != 0:
72 entry = entries.pop()
73 next_total_width = total_width + col_sep_mid_width
74 next_total_width += entry.width
75 if len(column_entries) != 0 and \
76 next_total_width + col_sep_end_width >= column_width:
77 entries.append(entry)
78 break
79 total_width = next_total_width
80 column_entries.append(entry)
81 key_line_count = max(key_line_count, len(entry.key_lines))
82 value_line_count = max(value_line_count, len(entry.value_lines))
83 top_line = col_sep_start_top_line
84 mid_line = col_sep_start_mid_line
85 bottom_line = col_sep_start_bottom_line
86 key_lines = [col_sep_start] * key_line_count
87 value_lines = [col_sep_start] * value_line_count
88 for i in range(len(column_entries)):
89 last = (i == len(column_entries) - 1)
90 entry = column_entries[i]
91
92 def extend_line(line, entry_text, fill_char,
93 col_sep_mid, col_sep_end):
94 line += entry_text
95 line += fill_char * (entry.width - wcwidth(entry_text))
96 line += col_sep_end if last else col_sep_mid
97 return line
98
99 top_line = extend_line(line=top_line, entry_text='',
100 fill_char=col_top_line_char,
101 col_sep_mid=col_sep_mid_top_line,
102 col_sep_end=col_sep_end_top_line)
103 mid_line = extend_line(line=mid_line, entry_text='',
104 fill_char=col_mid_line_char,
105 col_sep_mid=col_sep_mid_mid_line,
106 col_sep_end=col_sep_end_mid_line)
107 bottom_line = extend_line(line=bottom_line, entry_text='',
108 fill_char=col_bottom_line_char,
109 col_sep_mid=col_sep_mid_bottom_line,
110 col_sep_end=col_sep_end_bottom_line)
111
112 def extend_lines(lines, entry_lines):
113 for j in range(len(lines)):
114 entry_text = ''
115 if j < len(entry_lines):
116 entry_text = entry_lines[j]
117 lines[j] = extend_line(line=lines[j],
118 entry_text=entry_text,
119 fill_char=' ',
120 col_sep_mid=col_sep_mid,
121 col_sep_end=col_sep_end)
122
123 extend_lines(key_lines, entry.key_lines)
124 extend_lines(value_lines, entry.value_lines)
125 retval.append(top_line)
126 retval.extend(key_lines)
127 retval.append(mid_line)
128 retval.extend(value_lines)
129 retval.append(bottom_line)
130 return retval
131
132
133 class InclusiveRange:
134 __slots__ = "start", "stop"
135
136 def __init__(self, start, stop=None):
137 if stop is None:
138 stop = start
139 self.start = start
140 self.stop = stop
141
142 def __len__(self) -> int:
143 return self.width
144
145 @property
146 def width(self) -> int:
147 return self.stop - self.start + 1
148
149 def __repr__(self):
150 if self.start == self.stop:
151 return f"InclusiveRange({self.start})"
152 return f"InclusiveRange({self.start}, {self.stop})"
153
154 def __str__(self):
155 if self.start == self.stop:
156 return f"{self.start}"
157 return f"{self.start}:{self.stop}"
158
159 def append(self, v):
160 if v == self.stop + 1:
161 self.stop = v
162 else:
163 raise ValueError()
164
165 def endian_reversed(self, word_length) -> "InclusiveRange":
166 # note reversed stop/start
167 return InclusiveRange(word_length - 1 - self.stop,
168 word_length - 1 - self.start)
169
170
171 class RangesField:
172 __slots__ = "ranges",
173 ranges: List[InclusiveRange]
174
175 def __init__(self, ranges=None):
176 if ranges is None:
177 ranges = []
178 if isinstance(ranges, InclusiveRange):
179 ranges = [ranges]
180 self.ranges = ranges
181
182 def __len__(self):
183 retval = 0
184 for i in self.ranges:
185 retval += len(i)
186 return retval
187
188 def __repr__(self):
189 if len(self.ranges) == 0:
190 return "RangesField()"
191 if len(self.ranges) == 1:
192 return f"RangesField({self.ranges[0]})"
193 return f"RangesField({self.ranges})"
194
195 def __str__(self):
196 if len(self.ranges) == 0:
197 return "none"
198 return ",".join(map(str, self.ranges))
199
200 def __setitem__(self, key, value):
201 assert isinstance(key, int)
202 assert isinstance(value, int)
203 assert key == len(self)
204 try:
205 self.ranges[-1].append(value)
206 return
207 except IndexError:
208 pass
209 except ValueError:
210 pass
211 self.ranges.append(InclusiveRange(value))
212
213 def endian_reversed(self, word_length) -> "RangesField":
214 return RangesField([i.endian_reversed(word_length)
215 for i in reversed(self.ranges)])
216
217 @property
218 def contiguous(self) -> bool:
219 return len(self.ranges) == 1
220
221 @property
222 def start(self) -> int:
223 assert self.contiguous
224 return self.ranges[0].start
225
226 @property
227 def stop(self) -> int:
228 assert self.contiguous
229 return self.ranges[0].stop
230
231 @property
232 def width(self) -> int:
233 assert self.contiguous
234 return self.ranges[0].width
235
236
237 try:
238 # shut-up excessive printing
239 old_stdout = sys.stdout
240 new_stdout = StringIO()
241 sys.stdout = new_stdout
242 isa = ISA()
243 svp64rm = SVP64RM()
244 decode_fields = DecodeFields(bitkls=RangesField)
245 decode_fields.create_specs()
246 decoder = create_pdecode()
247 sys.stdout = old_stdout
248 except:
249 sys.stdout = old_stdout
250 print(new_stdout.getvalue())
251 raise
252
253
254 def flatten(v):
255 if isinstance(v, list):
256 for i in v:
257 yield from flatten(i)
258 else:
259 yield v
260
261
262 def subdecoders():
263 def visit_subdecoders(subdecoders):
264 for subdecoder in flatten(subdecoders):
265 yield subdecoder
266 yield from visit_subdecoders(subdecoder.subdecoders)
267 yield from visit_subdecoders(decoder.dec)
268
269
270 def make_opcodes_dict() -> Dict[str, Dict[str, Any]]:
271 retval = {}
272 for subdecoder in subdecoders():
273 for opcode in subdecoder.opcodes:
274 opcode = dict(opcode)
275 opcode['subdecoder'] = subdecoder
276 comment = opcode['comment']
277 if comment not in retval:
278 retval[comment] = opcode
279 else:
280 print(f"duplicate comment field: {comment!r}"
281 f" subdecoder.pattern={subdecoder.pattern}")
282 return retval
283
284
285 OPCODES_DICT = make_opcodes_dict()
286
287
288 def find_opcode(comment):
289 opcode = OPCODES_DICT[comment]
290 return opcode['subdecoder'].pattern, int(opcode['opcode'], base=0)
291
292
293 SETVL_PO, SETVL_XO = find_opcode("setvl")
294 PO_FIELD: RangesField = decode_fields.PO
295 RT_FIELD: RangesField = decode_fields.RT
296 RA_FIELD: RangesField = decode_fields.RA
297 SVL_SVi_FIELD: RangesField = decode_fields.FormSVL.SVi
298 SVL_vs_FIELD: RangesField = decode_fields.FormSVL.vs
299 SVL_ms_FIELD: RangesField = decode_fields.FormSVL.ms
300 SVL_XO_FIELD: RangesField = decode_fields.FormSVL.XO
301 SVL_Rc_FIELD: RangesField = decode_fields.FormSVL.Rc
302 print(f"SETVL_PO={SETVL_PO}")
303 print(f"SETVL_XO={SETVL_XO}")
304 print(f"PO_FIELD={PO_FIELD}")
305 print(f"RT_FIELD={RT_FIELD}")
306 print(f"RA_FIELD={RA_FIELD}")
307 print(f"SVL_SVi_FIELD={SVL_SVi_FIELD}")
308 print(f"SVL_vs_FIELD={SVL_vs_FIELD}")
309 print(f"SVL_ms_FIELD={SVL_ms_FIELD}")
310 print(f"SVL_XO_FIELD={SVL_XO_FIELD}")
311 print(f"SVL_Rc_FIELD={SVL_Rc_FIELD}")
312
313 WORD_LENGTH = 32
314 PO_SHIFT = PO_FIELD.endian_reversed(WORD_LENGTH).start
315 RT_SHIFT = RT_FIELD.endian_reversed(WORD_LENGTH).start
316 RA_SHIFT = RA_FIELD.endian_reversed(WORD_LENGTH).start
317 SVL_SVi_SHIFT = SVL_SVi_FIELD.endian_reversed(WORD_LENGTH).start
318 SVL_vs_SHIFT = SVL_vs_FIELD.endian_reversed(WORD_LENGTH).start
319 SVL_ms_SHIFT = SVL_ms_FIELD.endian_reversed(WORD_LENGTH).start
320 SVL_XO_SHIFT = SVL_XO_FIELD.endian_reversed(WORD_LENGTH).start
321 SVL_Rc_SHIFT = SVL_Rc_FIELD.endian_reversed(WORD_LENGTH).start
322
323 print(f"RT_SHIFT={RT_SHIFT}")
324 print(f"RA_SHIFT={RA_SHIFT}")
325 print(f"PO_SHIFT={PO_SHIFT}")
326 print(f"SVL_SVi_SHIFT={SVL_SVi_SHIFT}")
327 print(f"SVL_vs_SHIFT={SVL_vs_SHIFT}")
328 print(f"SVL_ms_SHIFT={SVL_ms_SHIFT}")
329 print(f"SVL_XO_SHIFT={SVL_XO_SHIFT}")
330 print(f"SVL_Rc_SHIFT={SVL_Rc_SHIFT}")
331
332
333 def is_power_of_2(i):
334 assert isinstance(i, int)
335 return (i & (i - 1)) == 0 and i != 0
336
337
338 with open("include/simplev_cpp_generated.h", mode="w", encoding="utf-8") as o:
339 o.write("""// SPDX-License-Identifier: LGPL-2.1-or-later
340 // See Notices.txt for copyright information
341 // This file is automatically generated by generate_headers.py,
342 // do not edit by hand
343 #pragma once
344 #include <cstddef>
345 #include <cstdint>
346 #include <type_traits>
347
348 #ifdef __clang__
349 #define SIMPLEV_USE_NONPOT_VECTORS
350 #endif
351
352 // #undef SIMPLEV_USE_BIGGER_THAN_8_BYTE_VECTORS
353
354 namespace sv
355 {
356 template <typename ElementType, std::size_t SUB_VL, std::size_t MAX_VL, typename = void>
357 struct VecTypeStruct;
358
359 template <typename ElementType, std::size_t SUB_VL, std::size_t MAX_VL>
360 using VecType = typename VecTypeStruct<ElementType, SUB_VL, MAX_VL>::Type;
361
362 #define SIMPLEV_MAKE_VEC_TYPE(size, underlying_size) \\
363 template <typename ElementType, std::size_t SUB_VL, std::size_t MAX_VL> \\
364 struct VecTypeStruct<ElementType, \\
365 SUB_VL, \\
366 MAX_VL, \\
367 std::enable_if_t<sizeof(ElementType) * SUB_VL * MAX_VL == (size)>> \\
368 final \\
369 { \\
370 typedef ElementType Type __attribute__((vector_size(underlying_size))); \\
371 };
372
373 template <typename ElementType, std::size_t SUB_VL, std::size_t MAX_VL>
374 struct Vec final
375 {
376 static_assert(MAX_VL > 0 && MAX_VL <= 64);
377 static_assert(SUB_VL >= 1 && SUB_VL <= 4);
378 using Type = VecType<ElementType, SUB_VL, MAX_VL>;
379 Type value;
380 };
381
382 // power-of-2
383 #define SIMPLEV_MAKE_VEC_TYPE_POT(size) SIMPLEV_MAKE_VEC_TYPE(size, size)
384
385 // non-power-of-2
386 #ifdef SIMPLEV_USE_NONPOT_VECTORS
387 #define SIMPLEV_MAKE_VEC_TYPE_NONPOT(size, rounded_up_size) SIMPLEV_MAKE_VEC_TYPE(size)
388 #else
389 #define SIMPLEV_MAKE_VEC_TYPE_NONPOT(size, rounded_up_size) \\
390 SIMPLEV_MAKE_VEC_TYPE(size, rounded_up_size)
391 #endif
392
393 """)
394 for i in range(64 * 4):
395 i += 1
396 if is_power_of_2(i):
397 o.write(f"SIMPLEV_MAKE_VEC_TYPE_POT({i})\n")
398 else:
399 rounded_up_size = 1 << i.bit_length()
400 o.write(f"SIMPLEV_MAKE_VEC_TYPE_NONPOT({i}, {rounded_up_size})\n")
401 if i == 8:
402 o.write("#ifdef SIMPLEV_USE_BIGGER_THAN_8_BYTE_VECTORS\n")
403 o.write(f"""#endif // SIMPLEV_USE_BIGGER_THAN_8_BYTE_VECTORS
404
405 template <std::size_t MAX_VL>
406 struct VL final
407 {{
408 static_assert(MAX_VL > 0 && MAX_VL <= 64);
409 std::size_t value;
410 }};
411
412 template <std::size_t MAX_VL>
413 inline __attribute__((always_inline)) VL<MAX_VL> setvl(std::size_t vl)
414 {{
415 VL<MAX_VL> retval;
416 asm volatile(
417 "# setvl %[retval], %[vl], MVL=%[max_vl]\\n\\t"
418 ".long %[instr] | (%[retval] << %[rt_shift]) | (%[vl] << %[ra_shift])"
419 : [retval] "=b"(retval.value)
420 : [vl] "b"(vl),
421 [max_vl] "n"(MAX_VL),
422 [instr] "n"(((MAX_VL - 1) << {SVL_SVi_SHIFT}) | {hex(
423 (1 << SVL_vs_SHIFT)
424 | (1 << SVL_ms_SHIFT)
425 | (SETVL_XO << SVL_XO_SHIFT)
426 | (SETVL_PO << PO_SHIFT))}),
427 [rt_shift] "n"({RT_SHIFT}),
428 [ra_shift] "n"({RA_SHIFT})
429 : "memory");
430 return retval;
431 }}
432 """)
433 for opcode in {i: None for i in svp64rm.instrs}:
434 try:
435 instr = OPCODES_DICT[opcode]
436 except KeyError as e:
437 print(repr(e), file=sys.stderr)
438 o.write(f"\n// skipped invalid opcode: {opcode!r}\n")
439 continue
440 o.write(f"\n/// {opcode}\n")
441 function_name = "sv_" + opcode.split('/')[0].replace('.', '_')
442 SV_Ptype = instr['SV_Ptype']
443 if SV_Ptype == 'P2':
444 pass
445 # TODO
446 else:
447 assert SV_Ptype == 'P1'
448 # TODO
449 o.write("/// (not yet implemented)\n")
450 instr_without_subdecoder = instr.copy()
451 del instr_without_subdecoder['subdecoder']
452 comment = "/// "
453 for line in format_dict_as_tables(instr_without_subdecoder,
454 OUTPUT_WIDTH - wcwidth(comment)):
455 o.write((comment + line).rstrip() + '\n')
456 o.write(f"""template <typename... Args>
457 void {function_name}(Args &&...) = delete;
458 """)
459 o.write(f"""}} // namespace sv
460
461 #undef SIMPLEV_MAKE_VEC_TYPE
462 #undef SIMPLEV_MAKE_VEC_TYPE_NONPOT
463 #undef SIMPLEV_MAKE_VEC_TYPE_POT
464 #undef SIMPLEV_USE_NONPOT_VECTORS
465 #undef SIMPLEV_USE_BIGGER_THAN_8_BYTE_VECTORS
466 """)