add function to generate table of instruction properties for unimplemented instructions
authorJacob Lifshay <programmerjake@gmail.com>
Wed, 17 Mar 2021 20:45:00 +0000 (13:45 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Wed, 17 Mar 2021 20:45:00 +0000 (13:45 -0700)
generate_headers.py

index 0163c2fafbfa816b990333806e24ed063ad0a4e5..9e33e93f63d220abceb1a27e6df10bb74ef911c8 100755 (executable)
 # See Notices.txt for copyright information
 
 import sys
-from pprint import pprint
 from io import StringIO
-from typing import List
+from typing import Any, Dict, List
 from soc.decoder.pseudo.pagereader import ISA
 from soc.decoder.power_svp64 import SVP64RM
 from soc.decoder.power_fields import DecodeFields
 from soc.decoder.power_decoder import create_pdecode
 
 
+# generated text should wrap at 80 columns
+OUTPUT_WIDTH = 80
+
+
+def wcwidth(text: str) -> int:
+    """ return the number of columns that `text` takes up if printed to a terminal.
+        returns -1 if any characters are not printable.
+    """
+    # TODO: replace with wcwidth from wcwidth
+    # package when we add pip packaging files
+    if text.isprintable():
+        return len(text)
+    return -1
+
+
+class TableEntry:
+    def __init__(self, key: str, value: Any):
+        self.key = repr(key)
+        self.key_lines = self.key.splitlines()
+        for i in range(len(self.key_lines)):
+            self.key_lines[i] = self.key_lines[i].rstrip()
+        self.value = repr(value)
+        self.value_lines = self.value.splitlines()
+        for i in range(len(self.value_lines)):
+            self.value_lines[i] = self.value_lines[i].rstrip()
+        self.width = max(max(map(wcwidth, self.key_lines)),
+                         max(map(wcwidth, self.value_lines)))
+
+
+def format_dict_as_tables(d: Dict[str, Any], column_width: int) -> List[str]:
+    entries = [TableEntry(k, v) for k, v in d.items()]
+    entries.sort(key=lambda entry: entry.key)
+    entries.reverse()
+    col_sep_start = '| '
+    col_sep_start_top_line = '+-'
+    col_sep_start_mid_line = '+='
+    col_sep_start_bottom_line = '+-'
+    col_sep_start_width = wcwidth(col_sep_start)
+    col_sep_mid = ' | '
+    col_sep_mid_top_line = '-+-'
+    col_sep_mid_mid_line = '=+='
+    col_sep_mid_bottom_line = '-+-'
+    col_sep_mid_width = wcwidth(col_sep_mid)
+    col_sep_end = ' |'
+    col_sep_end_top_line = '-+'
+    col_sep_end_mid_line = '=+'
+    col_sep_end_bottom_line = '-+'
+    col_sep_end_width = wcwidth(col_sep_end)
+    col_top_line_char = '-'
+    col_mid_line_char = '='
+    col_bottom_line_char = '-'
+    retval: List[str] = []
+    while len(entries) != 0:
+        total_width = col_sep_start_width - col_sep_mid_width
+        column_entries: List[TableEntry] = []
+        key_line_count = 0
+        value_line_count = 0
+        while len(entries) != 0:
+            entry = entries.pop()
+            next_total_width = total_width + col_sep_mid_width
+            next_total_width += entry.width
+            if len(column_entries) != 0 and \
+                    next_total_width + col_sep_end_width >= column_width:
+                entries.append(entry)
+                break
+            total_width = next_total_width
+            column_entries.append(entry)
+            key_line_count = max(key_line_count, len(entry.key_lines))
+            value_line_count = max(value_line_count, len(entry.value_lines))
+        top_line = col_sep_start_top_line
+        mid_line = col_sep_start_mid_line
+        bottom_line = col_sep_start_bottom_line
+        key_lines = [col_sep_start] * key_line_count
+        value_lines = [col_sep_start] * value_line_count
+        for i in range(len(column_entries)):
+            last = (i == len(column_entries) - 1)
+            entry = column_entries[i]
+
+            def extend_line(line, entry_text, fill_char,
+                            col_sep_mid, col_sep_end):
+                line += entry_text
+                line += fill_char * (entry.width - wcwidth(entry_text))
+                line += col_sep_end if last else col_sep_mid
+                return line
+
+            top_line = extend_line(line=top_line, entry_text='',
+                                   fill_char=col_top_line_char,
+                                   col_sep_mid=col_sep_mid_top_line,
+                                   col_sep_end=col_sep_end_top_line)
+            mid_line = extend_line(line=mid_line, entry_text='',
+                                   fill_char=col_mid_line_char,
+                                   col_sep_mid=col_sep_mid_mid_line,
+                                   col_sep_end=col_sep_end_mid_line)
+            bottom_line = extend_line(line=bottom_line, entry_text='',
+                                      fill_char=col_bottom_line_char,
+                                      col_sep_mid=col_sep_mid_bottom_line,
+                                      col_sep_end=col_sep_end_bottom_line)
+
+            def extend_lines(lines, entry_lines):
+                for j in range(len(lines)):
+                    entry_text = ''
+                    if j < len(entry_lines):
+                        entry_text = entry_lines[j]
+                    lines[j] = extend_line(line=lines[j],
+                                           entry_text=entry_text,
+                                           fill_char=' ',
+                                           col_sep_mid=col_sep_mid,
+                                           col_sep_end=col_sep_end)
+
+            extend_lines(key_lines, entry.key_lines)
+            extend_lines(value_lines, entry.value_lines)
+        retval.append(top_line)
+        retval.extend(key_lines)
+        retval.append(mid_line)
+        retval.extend(value_lines)
+        retval.append(bottom_line)
+    return retval
+
+
 class InclusiveRange:
     __slots__ = "start", "stop"
 
@@ -149,7 +267,7 @@ def subdecoders():
     yield from visit_subdecoders(decoder.dec)
 
 
-def make_opcodes_dict():
+def make_opcodes_dict() -> Dict[str, Dict[str, Any]]:
     retval = {}
     for subdecoder in subdecoders():
         for opcode in subdecoder.opcodes:
@@ -325,8 +443,14 @@ inline __attribute__((always_inline)) VL<MAX_VL> setvl(std::size_t vl)
         else:
             assert SV_Ptype == 'P1'
             # TODO
-        o.write(f"""/// (not yet implemented)
-template <typename... Args>
+        o.write("/// (not yet implemented)\n")
+        instr_without_subdecoder = instr.copy()
+        del instr_without_subdecoder['subdecoder']
+        comment = "/// "
+        for line in format_dict_as_tables(instr_without_subdecoder,
+                                          OUTPUT_WIDTH - wcwidth(comment)):
+            o.write((comment + line).rstrip() + '\n')
+        o.write(f"""template <typename... Args>
 void {function_name}(Args &&...) = delete;
 """)
     o.write(f"""}} // namespace sv