-# 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 → <br/> XO MSB half ↓ | 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
-| | 01100 | 01101 | 01110 | 01111 |
+| XO LSB half → <br/> XO MSB half ↓ | 01100 | 01101 | 01110 | 01111 |
| ----- | ----- | ----- | ----- | ----- |
| 10000 | | | | |
| 10001 | | | | |
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,
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) | x<sup>y</sup> | |
+| fpown(s) | x<sup>n</sup> (n ∈ ℤ) | |
+| fpowr(s) | x<sup>y</sup> (x >= 0) | |
+| frootn(s) | <sup>n</sup>√x (n ∈ ℤ) | |
+| fhypot(s) | √(x<sup>2</sup> + y<sup>2</sup>) | 10100 01110 |
+
# DRAFT List of 1-arg transcendental opcodes
FIXME(programmerjake): reallocate
| 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) | 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`.
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 |
--- /dev/null
+#!/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 → <br/> 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" *<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)