# FPR-to-GPR and GPR-to-FPR Introduction: High-performance CPU/GPU software needs to often convert between integers and floating-point, therefore fast conversion/data-movement instructions are needed. Also given that initialisation of floats tends to take up considerable space Geven to just load 0.0) the inclusion of float immediate is up for consideration. Libre-SOC will be compliant with the **Scalar Floating-Point Subset** (SFFS) i.e. is not implementing VMX/VSX. With VMX/VSX not available in the SFFS Compliancy Level, the existing non-VSX conversion/data-movement instructions require load/store instructions (slow and expensive) to transfer data between the FPRs and the GPRs. Also, because SimpleV needs efficient scalar instructions in order to generate efficient vector instructions, adding new instructions for data-transfer/conversion between FPRs and GPRs seems necessary. In addition, the vast majority of GPR <-> FPR data-transfers are as part of a FP <-> Integer conversion sequence, therefore reducing the number of instructions required to the minimum seems necessary. Therefore, we are proposing adding both: * FPR <-> GPR data-transfer instructions that just copy bits without conversion * FPR <-> GPR combined data-transfer/conversion instructions that do Integer <-> FP conversions If we're adding new Integer <-> FP conversion instructions, we may as well take this opportunity to modernise the instructions and make them well suited for common/important conversion sequences: * standard Integer -> FP conversion - rounding mode read from FPSCR * standard OpenPower FP -> Integer conversion -- saturation with NaN converted to minimum valid integer - Matches x86's conversion semantics - Has instructions for both: * rounding mode read from FPSCR * rounding mode is always truncate * Rust FP -> Integer conversion -- saturation with NaN converted to 0 Semantics required by all of: * Rust's FP -> Integer conversion using the [`as` operator](https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics) * Java's [FP -> Integer conversion](https://docs.oracle.com/javase/specs/jls/se16/html/jls-5.html#jls-5.1.3) * LLVM's [`llvm.fptosi.sat`](https://llvm.org/docs/LangRef.html#llvm-fptosi-sat-intrinsic) and [`llvm.fptoui.sat`](https://llvm.org/docs/LangRef.html#llvm-fptoui-sat-intrinsic) intrinsics * SPIR-V's OpenCL dialect's [`OpConvertFToU`](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpConvertFToU) and [`OpConvertFToS`](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpConvertFToS) instructions when decorated with [the `SaturatedConversion` decorator](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_a_id_decoration_a_decoration). * JavaScript FP -> Integer conversion -- modular with Inf/NaN converted to 0 Semantics required by JavaScript TODO: review and investigate other language semantics # Links * * * * # Proposed New Scalar Instructions All of the following instructions use the standard OpenPower conversion to/from 64-bit float format when reading/writing a 32-bit float from/to a FPR. This can be overridden by SimpleV. ## FPR to GPR moves `fmvtg RT, FRA` move a 64-bit float from a FPR to a GPR, just copying bits. `fmvtgs RT, FRA` move a 32-bit float from a FPR to a GPR, just copying bits. Converts the 64-bit float in `FRA` to a 32-bit float, then writes the 32-bit float to `RT`. ## GPR to FPR moves `fmvfg FRT, RA` move a 64-bit float from a GPR to a FPR, just copying bits. `fmvfgs FRT, RA` move a 32-bit float from a GPR to a FPR, just copying bits. Converts the 32-bit float in `RA` to a 64-bit float, then writes the 64-bit float to `FRT`. ### Float load immediate (kinda a variant of `fmvfg`) `fmvis FRT, UI` Reinterprets `UI << 16` as a 32-bit float, which is then converted to a 64-bit float and written to `FRT`. This is equivalent to reinterpreting `UI` as a bf16 and converting to 64-bit float, writing to `FRT`. Example: ``` # clearing a FPR fmvis f4, 0 # writes +0.0 to f4 # loading handy constants fmvis f4, 0x8000 # writes -0.0 to f4 fmvis f4, 0x3F80 # writes +1.0 to f4 fmvis f4, 0xBF80 # writes -1.0 to f4 fmvis f4, 0xBFC0 # writes -1.5 to f4 fmvis f4, 0x7FC0 # writes +qNaN to f4 fmvis f4, 0x7F80 # writes +Infinity to f4 fmvis f4, 0xFF80 # writes -Infinity to f4 fmvis f4, 0x3FFF # writes +1.9921875 to f4 ``` Important: If the float load immediate instruction(s) are left out, change all [GPR to FPR conversion instructions](#GPR-to-FPR-conversions) to instead write `+0.0` if `RA` is register `0`, allowing clearing FPRs. ## FPR to GPR conversions
Mode values: | Mode | `rounding_mode` | Semantics | |------|-----------------|----------------------------------| | 000 | from `FPSCR` | [OpenPower semantics] | | 001 | Truncate | [OpenPower semantics] | | 010 | from `FPSCR` | [Rust semantics] | | 011 | Truncate | [Rust semantics] | | 100 | from `FPSCR` | [JavaScript semantics] | | 101 | Truncate | [JavaScript semantics] | | rest | -- | illegal instruction trap for now | [OpenPower semantics]: #fp-to-int-openpower-conversion-semantics [Rust semantics]: #fp-to-int-rust-conversion-semantics [JavaScript semantics]: #fp-to-int-javascript-conversion-semantics `fcvttgw RT, FRA, Mode` Convert from 64-bit float to 32-bit signed integer, writing the result to the GPR `RT`. Converts using [mode `Mode`] `fcvttguw RT, FRA, Mode` Convert from 64-bit float to 32-bit unsigned integer, writing the result to the GPR `RT`. Converts using [mode `Mode`] `fcvttgd RT, FRA, Mode` Convert from 64-bit float to 64-bit signed integer, writing the result to the GPR `RT`. Converts using [mode `Mode`] `fcvttgud RT, FRA, Mode` Convert from 64-bit float to 64-bit unsigned integer, writing the result to the GPR `RT`. Converts using [mode `Mode`] `fcvtstgw RT, FRA, Mode` Convert from 32-bit float to 32-bit signed integer, writing the result to the GPR `RT`. Converts using [mode `Mode`] `fcvtstguw RT, FRA, Mode` Convert from 32-bit float to 32-bit unsigned integer, writing the result to the GPR `RT`. Converts using [mode `Mode`] `fcvtstgd RT, FRA, Mode` Convert from 32-bit float to 64-bit signed integer, writing the result to the GPR `RT`. Converts using [mode `Mode`] `fcvtstgud RT, FRA, Mode` Convert from 32-bit float to 64-bit unsigned integer, writing the result to the GPR `RT`. Converts using [mode `Mode`] [mode `Mode`]: #fpr-to-gpr-conversion-mode ## GPR to FPR conversions All of the following GPR to FPR conversions use the rounding mode from `FPSCR`. `fcvtfgw FRT, RA` Convert from 32-bit signed integer in the GPR `RA` to 64-bit float in `FRT`. `fcvtfgws FRT, RA` Convert from 32-bit signed integer in the GPR `RA` to 32-bit float in `FRT`. `fcvtfguw FRT, RA` Convert from 32-bit unsigned integer in the GPR `RA` to 64-bit float in `FRT`. `fcvtfguws FRT, RA` Convert from 32-bit unsigned integer in the GPR `RA` to 32-bit float in `FRT`. `fcvtfgd FRT, RA` Convert from 64-bit signed integer in the GPR `RA` to 64-bit float in `FRT`. `fcvtfgds FRT, RA` Convert from 64-bit signed integer in the GPR `RA` to 32-bit float in `FRT`. `fcvtfgud FRT, RA` Convert from 64-bit unsigned integer in the GPR `RA` to 64-bit float in `FRT`. `fcvtfguds FRT, RA` Convert from 64-bit unsigned integer in the GPR `RA` to 32-bit float in `FRT`. # FP to Integer Conversion Pseudo-code Key for pseudo-code: | term | result type | definition | |---------------------------|-------------|----------------------------------------------------------------------------------------------------| | `fp` | -- | `f32` or `f64` (or other types from SimpleV) | | `int` | -- | `u32`/`u64`/`i32`/`i64` (or other types from SimpleV) | | `uint` | -- | the unsigned integer of the same bit-width as `int` | | `int::BITS` | `int` | the bit-width of `int` | | `int::MIN_VALUE` | `int` | the minimum value `int` can store (`0` if unsigned, `-2^(int::BITS-1)` if signed) | | `int::MAX_VALUE` | `int` | the maximum value `int` can store (`2^int::BITS - 1` if unsigned, `2^(int::BITS-1) - 1` if signed) | | `int::VALUE_COUNT` | Integer | the number of different values `int` can store (`2^int::BITS`). too big to fit in `int`. | | `rint(fp, rounding_mode)` | `fp` | rounds the floating-point value `fp` to an integer according to rounding mode `rounding_mode` |
OpenPower conversion semantics (section A.2 page 999 (page 1023) of OpenPower ISA v3.1): ``` def fp_to_int_open_power(v: fp) -> int: if v is NaN: return int::MIN_VALUE if v >= int::MAX_VALUE: return int::MAX_VALUE if v <= int::MIN_VALUE: return int::MIN_VALUE return (int)rint(v, rounding_mode) ```
Rust [conversion semantics](https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics) (with adjustment to add non-truncate rounding modes): ``` def fp_to_int_rust(v: fp) -> int: if v is NaN: return 0 if v >= int::MAX_VALUE: return int::MAX_VALUE if v <= int::MIN_VALUE: return int::MIN_VALUE return (int)rint(v, rounding_mode) ```
JavaScript [conversion semantics](https://262.ecma-international.org/11.0/#sec-toint32) (with adjustment to add non-truncate rounding modes): ``` def fp_to_int_java_script(v: fp) -> int: if v is NaN or infinite: return 0 v = rint(v, rounding_mode) v = v mod int::VALUE_COUNT # 2^32 for i32, 2^64 for i64, result is non-negative bits = (uint)v return (int)bits ```