working on assigning fptrans XO values
authorJacob Lifshay <programmerjake@gmail.com>
Sun, 4 Sep 2022 13:44:05 +0000 (06:44 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Sun, 4 Sep 2022 13:56:13 +0000 (06:56 -0700)
.gitignore
openpower/power_trans_ops.mdwn
openpower/power_trans_ops_copy_from_PO59_table.py [new file with mode: 0644]

index 3c900299f343d392757e851eab69187bbe603de0..61a008fe576fe611c2fe186649b324ff13e5f3ca 100644 (file)
@@ -20,3 +20,5 @@ __pycache__
 
 tex_out
 /openpower/simple_v_spec.pdf
+/openpower/power_trans_ops.old.mdwn
+/openpower/power_trans_ops.new.mdwn
index 5eb04b57a064b0ae27fdd5f6b5944fff8efe233d..0d06b0b7e20971cc45356e5d6a96b6e2cdc84bdc 100644 (file)
@@ -1,27 +1,27 @@
-# pre-existing opcodes in Power ISA v3.1B
+# Opcode Tables
 
 ## PO=59 XO=10--011--
 
-TODO: move fdmadds to not conflict, ffadds can fit in a slot fptrans doesn't use.
+Power ISA v3.1B opcodes extracted from Power ISA v3.1B Appendix D Table 23 sheet 2 of 4 page 1391
 
-Extracted from Power ISA v3.1B Appendix D Table 23 sheet 2 of 4 page 1391
+Parenthesized entries are not part of fptrans.
 
-| &nbsp;| 01100 | 01101 | 01110 | 01111 |
+| XO LSB half &#x2192; <br/> XO MSB half &#x2193; | 01100 | 01101 | 01110 | 01111 |
 | ----- | ----- | ----- | ----- | ----- |
-| 10000 |       |       |       |       |
-| 10001 |       |       |       |       |
-| 10010 |       |       |       |       |
-| 10011 |       |       |       |       |
-| 10100 |       |       |       |       |
-| 10101 |       |       |       |       |
-| 10110 |       |       |       |       |
-| 10111 |       |       |       |       |
+| 10000 | <small>`10000 01100`</small><br/>(ffadds) (draft) | <small>`10000 01101`</small><br/>fsinpis (draft) | <small>`10000 01110`</small><br/>fatan2pis (draft) | <small>`10000 01111`</small><br/>fasinpis (draft) |
+| 10001 | <small>`10001 01100`</small><br/>fcospis (draft) | <small>`10001 01101`</small><br/>ftanpis (draft) | <small>`10001 01110`</small><br/>facospis (draft) | <small>`10001 01111`</small><br/>fatanpis (draft) |
+| 10010 | <small>`10010 01100`</small><br/>**TBD** (draft) | <small>`10010 01101`</small><br/>fsins (draft) | <small>`10010 01110`</small><br/>fatan2s (draft) | <small>`10010 01111`</small><br/>fasins (draft) |
+| 10011 | <small>`10011 01100`</small><br/>fcoss (draft) | <small>`10011 01101`</small><br/>ftans (draft) | <small>`10011 01110`</small><br/>facoss (draft) | <small>`10011 01111`</small><br/>fatans (draft) |
+| 10100 | <small>`10100 01100`</small><br/>**TBD** (draft) | <small>`10100 01101`</small><br/>fsinhs (draft) | <small>`10100 01110`</small><br/>fhypots (draft) | <small>`10100 01111`</small><br/>fasinhs (draft) |
+| 10101 | <small>`10101 01100`</small><br/>fcoshs (draft) | <small>`10101 01101`</small><br/>ftanhs (draft) | <small>`10101 01110`</small><br/>facoshs (draft) | <small>`10101 01111`</small><br/>fatanhs (draft) |
+| 10110 | <small>`10110 01100`</small><br/>**TBD** (draft) | <small>`10110 01101`</small><br/>**TBD** (draft) | <small>`10110 01110`</small><br/>**TBD** (draft) | <small>`10110 01111`</small><br/>**TBD** (draft) |
+| 10111 | <small>`10111 01100`</small><br/>**TBD** (draft) | <small>`10111 01101`</small><br/>**TBD** (draft) | <small>`10111 01110`</small><br/>**TBD** (draft) | <small>`10111 01111`</small><br/>**TBD** (draft) |
 
 ## PO=63 XO=10--011--
 
-Extracted from Power ISA v3.1B Appendix D Table 25 sheet 2 of 4 page 1399
+Power ISA v3.1B opcodes extracted from Power ISA v3.1B Appendix D Table 25 sheet 2 of 4 page 1399
 
-| &nbsp;| 01100 | 01101 | 01110 | 01111 |
+| XO LSB half &#x2192; <br/> XO MSB half &#x2193; | 01100 | 01101 | 01110 | 01111 |
 | ----- | ----- | ----- | ----- | ----- |
 | 10000 |       |       |       |       |
 | 10001 |       |       |       |       |
@@ -36,12 +36,12 @@ Extracted from Power ISA v3.1B Appendix D Table 25 sheet 2 of 4 page 1399
 
 FIXME(programmerjake): reallocate and convert to X-FORM
 
-These are A-Form, recommended Major Opcode 63 for full-width
+These are X-Form, recommended Major Opcode 63 for full-width
 and 59 for half-width (ending in s).
 
-| 0.5|6.10|11.15|16.20| 21.25 | 26.30 |31| name          |  Form   |
-| -- | -- | --- | --- | ----- | ----- |--| ----          | ------- |
-| NN |FRT | FRA | FRB  | ///  | xxxxx |Rc| trigonometric | A-Form  |
+| 0.5|6.10|11.15|16.20| 21..30      |31| name           |  Form   |
+| -- | -- | --- | --- | ----------  |--| ----           | ------- |
+| NN |FRT | FRA | FRB | 10xxx011xx  |Rc| transcendental | X-Form  |
 
 As shown in Power ISA 3.1 Book III Appendix D Table 28 p1190
 and Table 23 p1368,
@@ -51,16 +51,16 @@ Major Opcode 59 has 11 5-bit XO instructions,
 with some overlap leading to a partially orthogonal
 set across full and half width.
 
-| opcode    | Description            | Major 63         | Major 59    |
-| ------    | ----------------       | ---------------- | ----------- |
-| fatan2(s) | atan2 arc tangent      | 00001            | 00001       |
-| fatan2pi(s)|atan2 arc tangent / pi | 01001            | 00111       |
-| fpow(s)   | x power of y           | 10000            | 10000       |
-| fpown(s)  | x power of n (n int)   | 10001            | 10001       |
-| fpowr(s)  | x power of y (x +ve)   | 10011            | 10011       |
-| frootn(s) | x power 1/n (n integer)| 11011            | 11011       |
-| fhypot(s) | hypotenuse             | 01101            | 10111       |
+| opcode     | Description                             | Major 59 and 63 |
+| ------     | ----------------                        | --------------- |
+| fatan2(s)  | atan2 arc tangent                       | 10010 01110     |
+| fatan2pi(s)| atan2 arc tangent / &pi;                | 10000 01110     |
+| fpow(s)    | x<sup>y</sup>                           |      |
+| fpown(s)   | x<sup>n</sup> (n &in; &#x2124;)         |      |
+| fpowr(s)   | x<sup>y</sup> (x >= 0)                  |      |
+| frootn(s)  | <sup>n</sup>&#x221A;x (n &in; &#x2124;) |      |
+| fhypot(s)  | &#x221A;(x<sup>2</sup> + y<sup>2</sup>) | 10100 01110     |
+
 # DRAFT List of 1-arg transcendental opcodes
 
 FIXME(programmerjake): reallocate
@@ -76,30 +76,30 @@ Special Registers Altered:
 
 | 0.5|6.10|11.15|16.20| 21..30      |31| name      |  Form   |
 | -- | -- | --- | --- | ----------  |--| ----      | ------- |
-| NN |FRT | /// | FRB  | 1xxxx0111x |Rc| transcendental | X-Form  |
+| NN |FRT | /// | FRB  | 10xxx011xx |Rc| transcendental | X-Form  |
 
 Recommended 10-bit XO with the low 5 LSBs 01100:
 
-| opcode   | Description              | Major 59 and 63 |
-| ------    | ----------------       | ---------------- |
-| frsqrt(s)| Reciprocal Square-root   | 10000 01110     |
-| fcbrt(s) | Cube Root                | 10001 01110     |
-| frecip(s)| Reciprocal               | 10010 01111     |
-| fexp2m1(s)| power-2 minus 1      | 10100 01110     |
-| flog2p1(s)| log2 plus 1               | 10101 01110     |
-| fexp2(s) | power-of-2               | 10110 01110     |
-| flog2(s) | log2                     | 10111 01111     |
-| fexpm1(s)| exponential minus 1      | 11000 01110     |
-| flogp1(s)| log plus 1               | 11001 01110     |
-| fexp(s)  | exponential              | 11010 01110     |
-| flog(s)  | natural log (base e)     | 11011 01111     |
-| fexp10m1(s)| power-10 minus 1      | 11100 01110     |
-| flog10p1(s)| log10 plus 1               | 11101 01110     |
-| fexp10(s)| power-of-10              | 11110 01110     |
-| flog10(s)| log base 10              |11111 01111     |
+| opcode     | Description              | Major 59 and 63 |
+| ------     | ----------------         | --------------- |
+| frsqrt(s)  | 1 / &#x221A;x            |      |
+| fcbrt(s)   | &#x221B;x                |      |
+| frecip(s)  | 1 / x                    |      |
+| fexp2m1(s) | 2<sup>x</sup> - 1        |      |
+| flog2p1(s) | log<sub>2</sub> (x + 1)  |      |
+| fexp2(s)   | 2<sup>x</sup>            |      |
+| flog2(s)   | log<sub>2</sub> x        |      |
+| fexpm1(s)  | e<sup>x</sup> - 1        |      |
+| flogp1(s)  | log<sub>e</sub> (x + 1)  |      |
+| fexp(s)    | e<sup>x</sup>            |      |
+| flog(s)    | log<sub>e</sub> x        |      |
+| fexp10m1(s)| 10<sup>x</sup> - 1       |      |
+| flog10p1(s)| log<sub>10</sub> (x + 1) |      |
+| fexp10(s)  | 10<sup>x</sup>           |      |
+| flog10(s)  | log<sub>10</sub> x       |      |
 
 # DRAFT List of 1-arg trigonometric opcodes
+
 FIXME(programmerjake): reallocate
 
 These are X-Form, and are identical in Special Registers Altered to `fsqrt`.
@@ -117,23 +117,23 @@ Special Registers Altered:
 
 Recommended 10-bit XO with the low 5 LSBs 01101 to 01111:
 
-| opcode      | Description              | Major 59 and 63 |
-| ------    | ----------------       | ----------------   |
-| fsin(s)     | sin (radians)            |  10000 01101   |
-| fcos(s)     | cos (radians)            |  10000 01110    |
-| ftan(s)     | tan (radians)            |  10000 01111   |
-| fasin(s)    | arcsin (radians)         |  10001 01101    |
-| facos(s)    | arccos (radians)         |  10001 01110    |
-| fatan(s)    | arctan (radians)         |  10001 01111    |
-| fsinpi(s)   | sin times pi             |  10010 01101    |
-| fcospi(s)   | cos times pi             |  10010 01110    |
-| ftanpi(s)   | tan times pi             |  10010 01111    |
-| fasinpi(s)  | arcsin / pi              |  10011 01101    |
-| facospi(s)  | arccos / pi              |  10011 01110    |
-| fatanpi(s)  | arctan / pi              |  10011 01111    |
-| fsinh(s)    | hyperbolic sin (radians) |  10100 01101    |
-| fcosh(s)    | hyperbolic cos (radians) |  10100 01110    |
-| ftanh(s)    | hyperbolic tan (radians) |  10100 01111    |
-| fasinh(s)   | inverse hyperbolic sin   |  10101 01111    |
-| facosh(s)   | inverse hyperbolic cos   |  10101 01111    |
-| fatanh(s)   | inverse hyperbolic tan   |  10101 01111    |
+| opcode      | Description              | Major 59 and 63  |
+| ------      | ----------------         | ---------------- |
+| fsin(s)     | sin (radians)            | 10010 01101      |
+| fcos(s)     | cos (radians)            | 10011 01100      |
+| ftan(s)     | tan (radians)            | 10011 01101      |
+| fasin(s)    | arcsin (radians)         | 10010 01111      |
+| facos(s)    | arccos (radians)         | 10011 01110      |
+| fatan(s)    | arctan (radians)         | 10011 01111      |
+| fsinpi(s)   | sin(&pi; * x)            | 10000 01101      |
+| fcospi(s)   | cos(&pi; * x)            | 10001 01100      |
+| ftanpi(s)   | tan(&pi; * x)            | 10001 01101      |
+| fasinpi(s)  | arcsin(x) / &pi;         | 10000 01111      |
+| facospi(s)  | arccos(x) / &pi;         | 10001 01110      |
+| fatanpi(s)  | arctan(x) / &pi;         | 10001 01111      |
+| fsinh(s)    | hyperbolic sin           | 10100 01101      |
+| fcosh(s)    | hyperbolic cos           | 10101 01100      |
+| ftanh(s)    | hyperbolic tan           | 10101 01101      |
+| fasinh(s)   | inverse hyperbolic sin   | 10100 01111      |
+| facosh(s)   | inverse hyperbolic cos   | 10101 01110      |
+| fatanh(s)   | inverse hyperbolic tan   | 10101 01111      |
diff --git a/openpower/power_trans_ops_copy_from_PO59_table.py b/openpower/power_trans_ops_copy_from_PO59_table.py
new file mode 100644 (file)
index 0000000..061b7e5
--- /dev/null
@@ -0,0 +1,250 @@
+#!/usr/bin/env python3
+from pathlib import Path
+import re
+from typing import Iterable
+
+FILENAME = Path("openpower/power_trans_ops.mdwn")
+NEW_FILENAME = FILENAME.with_suffix(".new.mdwn")
+OLD_FILENAME = FILENAME.with_suffix(".old.mdwn")
+
+PO_59_HEADER = "## PO=59 XO=10--011--"
+MNEMONIC_COLUMN_NAME = "opcode"
+XO_COLUMN_NAME = "Major 59 and 63"
+
+# TODO: copy PO=59 table to PO=63 table
+
+class LineReader:
+    def __init__(self, lines):
+        # type: (list[str]) -> None
+        self.__next_line_index = 0
+        self.__lines = lines
+
+    def read(self):
+        if self.__next_line_index == len(self.__lines):
+            self.__next_line_index += 1
+            return None, self.__next_line_index
+        assert self.__next_line_index < len(self.__lines), \
+            "read past end-of-file"
+        line = self.__lines[self.__next_line_index].rstrip()
+        self.__next_line_index += 1
+        return line, self.__next_line_index
+
+
+def process(lr):
+    # type: (LineReader) -> Iterable[str]
+
+    line, lineno = lr.read()
+
+    mnemonic_to_xo_map = {}  # type: dict[str, str]
+
+    def parse_table_separator_line(len_line_parts):
+        # type: (int) -> Iterable[str]
+        nonlocal line, lineno
+
+        assert line is not None \
+            and line.startswith('|') \
+            and line.endswith('|') \
+            and len(line.split('|')) == len_line_parts \
+            and line.strip(" |-") == "", "invalid table separator line"
+
+        yield line
+        line, lineno = lr.read()
+
+        assert line is not None and line != "", "empty table"
+
+    def parse_mnemonic_to_opcode_map():
+        # type: () -> Iterable[str]
+        nonlocal line, lineno, mnemonic_to_xo_map
+
+        while line != PO_59_HEADER:
+            assert line is not None, "missing PO=59 header"
+            yield line
+            line, lineno = lr.read()
+
+        yield line
+        line, lineno = lr.read()
+
+        while line is not None and not line.startswith(("#", "|")):
+            yield line
+            line, lineno = lr.read()
+
+        assert line is not None and line.startswith(
+            "| XO LSB half &#x2192; <br/> XO MSB half &#x2193; |"), \
+            "can't find PO=59 table"
+        line_parts = line.split('|')
+        len_line_parts = len(line_parts)
+        assert line_parts[-1] == "", "invalid PO=59 table top row"
+        columns = []  # type: list[str]
+        columns_range = range(2, len_line_parts - 1)
+        for i in columns_range:
+            column = line_parts[i].strip()
+            if column.startswith('`') and column.endswith('`'):
+                column = column[1:-1].strip()
+            assert column.lstrip(" 01") == "", (f"invalid table top row "
+                                                f"contents -- must be a "
+                                                f"binary string: {column}")
+            columns.append(column)
+
+        yield line
+        line, lineno = lr.read()
+
+        yield from parse_table_separator_line(len_line_parts)
+
+        while line is not None and line != "":
+            line_parts = line.split('|')
+            assert line.startswith('|') and line.endswith('|'), \
+                "invalid table line, must start and end with |"
+            assert len(line_parts) == len_line_parts, (
+                f"invalid table line, wrong part count: found "
+                f"{len(line_parts)} expected {len_line_parts}")
+            row = line_parts[1].strip()
+            if row.startswith('`') and row.endswith('`'):
+                row = row[1:-1].strip()
+            assert row.lstrip(" 01") == "", (f"invalid table line header-cell "
+                                             f"contents -- must be a "
+                                             f"binary string: {row}")
+            for i, column in zip(columns_range, columns):
+                cell = line_parts[i]
+                match = re.fullmatch(
+                    r" *<small> *` *(?P<xo>[01][01 ]*[01]) *` *</small>"
+                    r" *<br/> *(?P<mnemonic>[a-zA-Z0-9_.]+)?"
+                    r"(?(mnemonic)|(?:\([a-zA-Z0-9_.]+\)|\*\*TBD\*\*|))"
+                    r"(?: *\(draft\))? *", cell)
+                assert match is not None, f"invalid table cell: {cell!r}"
+                xo, mnemonic = match.group("xo", "mnemonic")
+                shrunk_xo = xo.replace(" ", "")
+                expected_xo = (row + column).replace(" ", "")
+                assert shrunk_xo == expected_xo, \
+                    f"incorrect XO: found {shrunk_xo} expected {expected_xo}"
+                if mnemonic is None:
+                    continue
+                assert mnemonic.endswith('s'), \
+                    f"PO=59 fptrans mnemonic must end in `s`: {mnemonic}"
+                assert mnemonic not in mnemonic_to_xo_map, (
+                    f"duplicate mnemonic: {mnemonic} -- has opcode "
+                    f"{xo} and {mnemonic_to_xo_map[mnemonic]}")
+
+                mnemonic_to_xo_map[mnemonic] = xo
+
+            yield line
+            line, lineno = lr.read()
+
+    def skip_table():
+        # type: () -> Iterable[str]
+        nonlocal line, lineno
+
+        assert line is not None \
+            and line.startswith("|") and line.endswith('|'), \
+            "invalid table header"
+        line_parts = line.split("|")
+        len_line_parts = len(line_parts)
+        assert len_line_parts >= 3, "invalid table header"
+
+        yield line
+        line, lineno = lr.read()
+
+        yield from parse_table_separator_line(len_line_parts)
+        while line is not None and line != "":
+            line_parts = line.split('|')
+            assert line.startswith('|') and line.endswith('|'), \
+                "invalid table line, must start and end with |"
+            assert len(line_parts) == len_line_parts, (
+                f"invalid table line, wrong part count: found "
+                f"{len(line_parts)} expected {len_line_parts}")
+
+            yield line
+            line, lineno = lr.read()
+
+    def handle_table():
+        # type: () -> Iterable[str]
+        nonlocal line, lineno
+
+        assert line is not None \
+            and line.startswith("|") and line.endswith('|'), \
+            "invalid table header"
+        line_parts = line.split("|")
+        len_line_parts = len(line_parts)
+        assert len_line_parts >= 3, "invalid table header"
+        mnemonic_index = None
+        xo_index = None
+        xo_column_width = 0
+        for i, column in enumerate(line_parts):
+            column_width = len(column)  # should use wcswidth here
+            column = column.strip()
+            if column == MNEMONIC_COLUMN_NAME:
+                assert mnemonic_index is None, \
+                    f"two {MNEMONIC_COLUMN_NAME!r} columns in table " \
+                    f"-- can't handle that"
+                mnemonic_index = i
+            if column == XO_COLUMN_NAME:
+                assert xo_index is None, \
+                    f"two {XO_COLUMN_NAME!r} columns in table " \
+                    f"-- can't handle that"
+                xo_index = i
+                xo_column_width = column_width
+        if mnemonic_index is None and xo_index is None:
+            # not an opcode table -- skip it
+            yield from skip_table()
+            return
+
+        assert mnemonic_index is not None, \
+            f"missing {MNEMONIC_COLUMN_NAME} column"
+        assert xo_index is not None, f"missing {XO_COLUMN_NAME} column"
+
+        yield line
+        line, lineno = lr.read()
+
+        yield from parse_table_separator_line(len_line_parts)
+        while line is not None and line != "":
+            line_parts = line.split('|')
+            assert line.startswith('|') and line.endswith('|'), \
+                "invalid table line, must start and end with |"
+            assert len(line_parts) == len_line_parts, (
+                f"invalid table line, wrong part count: found "
+                f"{len(line_parts)} expected {len_line_parts}")
+
+            mnemonic = line_parts[mnemonic_index].strip()
+            xo = line_parts[xo_index].strip()
+            if mnemonic.endswith("(s)") or mnemonic.endswith("[s]"):
+                mnemonic = mnemonic[:-3] + "s"
+            if mnemonic not in mnemonic_to_xo_map:
+                print(f"mnemonic not assigned an XO value: {mnemonic!r}")
+            elif xo == "":
+                xo = mnemonic_to_xo_map[mnemonic]
+                xo_width = len(xo)  # should use wcswidth here
+                if xo_width < xo_column_width:
+                    # should use wc_ljust here
+                    xo = (" " + xo).ljust(xo_column_width)
+                line_parts[xo_index] = xo
+            else:
+                expected_xo = mnemonic_to_xo_map[mnemonic].replace(" ", "")
+                assert xo.replace(" ", "") == expected_xo, (
+                    f"mismatch in {XO_COLUMN_NAME} column: expected "
+                    f"{mnemonic_to_xo_map[mnemonic]} found {xo!r}")
+
+            yield '|'.join(line_parts)
+            line, lineno = lr.read()
+
+    try:
+        yield from parse_mnemonic_to_opcode_map()
+
+        print(mnemonic_to_xo_map)
+
+        while line is not None:
+            if line.startswith('|'):
+                yield from handle_table()
+            else:
+                yield line
+                line, lineno = lr.read()
+
+    except AssertionError as e:
+        raise AssertionError(f"\n{FILENAME}:{lineno}: error: {e}")
+
+
+inp = FILENAME.read_text(encoding="utf-8")
+output_lines = list(process(LineReader(inp.splitlines())))
+if output_lines[-1] != "":
+    output_lines.append("")  # ensure file ends with newline
+NEW_FILENAME.write_text("\n".join(output_lines), encoding="utf-8")
+FILENAME.replace(OLD_FILENAME)
+NEW_FILENAME.rename(FILENAME)