From d07de16dd70b5d7a8f0cb75c7d510002c16c2628 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 4 Sep 2022 06:44:05 -0700 Subject: [PATCH] working on assigning fptrans XO values --- .gitignore | 2 + openpower/power_trans_ops.mdwn | 134 +++++----- .../power_trans_ops_copy_from_PO59_table.py | 250 ++++++++++++++++++ 3 files changed, 319 insertions(+), 67 deletions(-) create mode 100644 openpower/power_trans_ops_copy_from_PO59_table.py diff --git a/.gitignore b/.gitignore index 3c900299f..61a008fe5 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ __pycache__ tex_out /openpower/simple_v_spec.pdf +/openpower/power_trans_ops.old.mdwn +/openpower/power_trans_ops.new.mdwn diff --git a/openpower/power_trans_ops.mdwn b/openpower/power_trans_ops.mdwn index 5eb04b57a..0d06b0b7e 100644 --- a/openpower/power_trans_ops.mdwn +++ b/openpower/power_trans_ops.mdwn @@ -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. -|  | 01100 | 01101 | 01110 | 01111 | +| XO LSB half →
XO MSB half ↓ | 01100 | 01101 | 01110 | 01111 | | ----- | ----- | ----- | ----- | ----- | -| 10000 | | | | | -| 10001 | | | | | -| 10010 | | | | | -| 10011 | | | | | -| 10100 | | | | | -| 10101 | | | | | -| 10110 | | | | | -| 10111 | | | | | +| 10000 | `10000 01100`
(ffadds) (draft) | `10000 01101`
fsinpis (draft) | `10000 01110`
fatan2pis (draft) | `10000 01111`
fasinpis (draft) | +| 10001 | `10001 01100`
fcospis (draft) | `10001 01101`
ftanpis (draft) | `10001 01110`
facospis (draft) | `10001 01111`
fatanpis (draft) | +| 10010 | `10010 01100`
**TBD** (draft) | `10010 01101`
fsins (draft) | `10010 01110`
fatan2s (draft) | `10010 01111`
fasins (draft) | +| 10011 | `10011 01100`
fcoss (draft) | `10011 01101`
ftans (draft) | `10011 01110`
facoss (draft) | `10011 01111`
fatans (draft) | +| 10100 | `10100 01100`
**TBD** (draft) | `10100 01101`
fsinhs (draft) | `10100 01110`
fhypots (draft) | `10100 01111`
fasinhs (draft) | +| 10101 | `10101 01100`
fcoshs (draft) | `10101 01101`
ftanhs (draft) | `10101 01110`
facoshs (draft) | `10101 01111`
fatanhs (draft) | +| 10110 | `10110 01100`
**TBD** (draft) | `10110 01101`
**TBD** (draft) | `10110 01110`
**TBD** (draft) | `10110 01111`
**TBD** (draft) | +| 10111 | `10111 01100`
**TBD** (draft) | `10111 01101`
**TBD** (draft) | `10111 01110`
**TBD** (draft) | `10111 01111`
**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 -|  | 01100 | 01101 | 01110 | 01111 | +| XO LSB half →
XO MSB half ↓ | 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 / π | 10000 01110 | +| fpow(s) | xy | | +| fpown(s) | xn (n ∈ ℤ) | | +| fpowr(s) | xy (x >= 0) | | +| frootn(s) | n√x (n ∈ ℤ) | | +| fhypot(s) | √(x2 + y2) | 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 / √x | | +| fcbrt(s) | ∛x | | +| frecip(s) | 1 / x | | +| fexp2m1(s) | 2x - 1 | | +| flog2p1(s) | log2 (x + 1) | | +| fexp2(s) | 2x | | +| flog2(s) | log2 x | | +| fexpm1(s) | ex - 1 | | +| flogp1(s) | loge (x + 1) | | +| fexp(s) | ex | | +| flog(s) | loge x | | +| fexp10m1(s)| 10x - 1 | | +| flog10p1(s)| log10 (x + 1) | | +| fexp10(s) | 10x | | +| flog10(s) | log10 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(π * x) | 10000 01101 | +| fcospi(s) | cos(π * x) | 10001 01100 | +| ftanpi(s) | tan(π * x) | 10001 01101 | +| fasinpi(s) | arcsin(x) / π | 10000 01111 | +| facospi(s) | arccos(x) / π | 10001 01110 | +| fatanpi(s) | arctan(x) / π | 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 index 000000000..061b7e5f3 --- /dev/null +++ b/openpower/power_trans_ops_copy_from_PO59_table.py @@ -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 →
XO MSB half ↓ |"), \ + "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" * *` *(?P[01][01 ]*[01]) *` *" + r" *
*(?P[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) -- 2.30.2