3 Note on considered alternative naming schemes: we decided to switch to using the reduced mnemonic naming scheme (over some people's objections) since it would be 5 instructions instead of dozens, though we did consider trying to match PowerISA's existing naming scheme for the instructions rather than only for the instruction aliases. <https://bugs.libre-soc.org/show_bug.cgi?id=1015#c7>
5 # FPR-to-GPR and GPR-to-FPR
7 TODO special constants instruction (e, tau/N, ln 2, sqrt 2, etc.) -- exclude any constants available through fmvis
9 **Draft Status** under development, for submission as an RFC
13 * <https://bugs.libre-soc.org/show_bug.cgi?id=650>
14 * <https://bugs.libre-soc.org/show_bug.cgi?id=230#c71>
15 * <https://bugs.libre-soc.org/show_bug.cgi?id=230#c74>
16 * <https://bugs.libre-soc.org/show_bug.cgi?id=230#c76>
17 * <https://bugs.libre-soc.org/show_bug.cgi?id=887> fmvis
18 * <https://bugs.libre-soc.org/show_bug.cgi?id=1015> int-fp RFC
19 * [[int_fp_mv/appendix]]
20 * [[sv/rfc/ls002]] - `fmvis` and `fishmv` External RFC Formal Submission
21 * [[sv/rfc/ls006]] - int-fp-mv External RFC Formal Submission
25 * Rust is a Trademark of the Rust Foundation
26 * Java and JavaScript are Trademarks of Oracle
27 * LLVM is a Trademark of the LLVM Foundation
28 * SPIR-V is a Trademark of the Khronos Group
29 * OpenCL is a Trademark of Apple, Inc.
31 Referring to these Trademarks within this document
32 is by necessity, in order to put the semantics of each language
33 into context, and is considered "fair use" under Trademark
38 High-performance CPU/GPU software needs to often convert between integers
39 and floating-point, therefore fast conversion/data-movement instructions
40 are needed. Also given that initialisation of floats tends to take up
41 considerable space (even to just load 0.0) the inclusion of two compact
42 format float immediate instructions is up for consideration using 16-bit
43 immediates. BF16 is one of the formats: a second instruction allows a full
44 accuracy FP32 to be constructed.
46 Libre-SOC will be compliant with the
47 **Scalar Floating-Point Subset** (SFFS) i.e. is not implementing VMX/VSX,
48 and with its focus on modern 3D GPU hybrid workloads represents an
49 important new potential use-case for OpenPOWER.
51 Prior to the formation of the Compliancy Levels first introduced
53 the progressive historic development of the Scalar parts of the Power ISA assumed
54 that VSX would always be there to complement it. However With VMX/VSX
55 **not available** in the newly-introduced SFFS Compliancy Level, the
56 existing non-VSX conversion/data-movement instructions require
57 a Vector of load/store
58 instructions (slow and expensive) to transfer data between the FPRs and
59 the GPRs. For a modern 3D GPU this kills any possibility of a
61 Also, because SimpleV needs efficient scalar instructions in
62 order to generate efficient vector instructions, adding new instructions
63 for data-transfer/conversion between FPRs and GPRs multiplies the savings.
65 In addition, the vast majority of GPR <-> FPR data-transfers are as part
66 of a FP <-> Integer conversion sequence, therefore reducing the number
67 of instructions required is a priority.
69 Therefore, we are proposing adding:
71 * FPR load-immediate instructions, one equivalent to `BF16`, the
72 other increasing accuracy to `FP32`
73 * FPR <-> GPR data-transfer instructions that just copy bits without conversion
74 * FPR <-> GPR combined data-transfer/conversion instructions that do
75 Integer <-> FP conversions
77 If adding new Integer <-> FP conversion instructions,
78 the opportunity may be taken to modernise the instructions and make them
79 well-suited for common/important conversion sequences:
82 * **standard IEEE754** - used by most languages and CPUs
84 * **standard OpenPOWER** - saturation with NaN
85 converted to minimum valid integer
86 * **Java/Saturating** - saturation with NaN converted to 0
87 * **JavaScript** - modulo wrapping with Inf/NaN converted to 0
89 The assembly listings in the [[int_fp_mv/appendix]] show how costly
90 some of these language-specific conversions are: JavaScript, the
91 worst case, is 32 scalar instructions including seven branch instructions.
92 (FIXME: disagrees with ls006 and sv.mdwn)
94 # Proposed New Scalar Instructions
96 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. All integers however are sourced/stored in the *GPR*.
98 Integer operands and results being in the GPR is the key differentiator between the proposed instructions
99 (the entire rationale) compared to existing Scalar Power ISA.
100 In all existing Power ISA Scalar conversion instructions, all
101 operands are FPRs, even if the format of the source or destination
102 data is actually a scalar integer.
104 *(The existing Scalar instructions being FP-FP only is based on an assumption
105 that VSX will be implemented, and VSX is not part of the SFFS Compliancy
106 Level. An earlier version of the Power ISA used to have similar
107 FPR<->GPR instructions to these:
108 they were deprecated due to this incorrect assumption that VSX would
111 Note that source and destination widths can be overridden by SimpleV
112 SVP64, and that SVP64 also has Saturation Modes *in addition*
113 to those independently described here. SVP64 Overrides and Saturation
114 work on *both* Fixed *and* Floating Point operands and results.
115 The interactions with SVP64
116 are explained in the [[int_fp_mv/appendix]]
118 # Float load immediate <a name="fmvis"></a>
120 These are like a variant of `fmvfg` and `oris`, combined.
121 Power ISA currently requires a large
122 number of instructions to get Floating Point constants into registers.
123 `fmvis` on its own is equivalent to BF16 to FP32/64 conversion,
124 but if followed up by `fishmv` an additional 16 bits of accuracy in the
125 mantissa may be achieved.
127 These instructions **always** save
128 resources compared to FP-load for exactly the same reason
129 that `li` saves resources: an L1-Data-Cache and memory read
132 *IBM may consider it worthwhile to extend these two instructions to
133 v3.1 Prefixed (`pfmvis` and `pfishmv`: 8RR, imm0 extended).
134 If so it is recommended that
135 `pfmvis` load a full FP32 immediate and `pfishmv` supplies the three high
136 missing exponent bits (numbered 8 to 10) and the lower additional
137 29 mantissa bits (23 to 51) needed to construct a full FP64 immediate.
138 Strictly speaking the sequence `fmvis fishmv pfishmv` achieves the
139 same effect in the same number of bytes as `pfmvis pfishmv`,
140 making `pfmvis` redundant.*
142 Just as Floating-point Load does not set FP Flags neither does fmvis or fishmv.
143 As fishmv is specifically intended to work in conjunction with fmvis
144 to provide additional accuracy, all bits other than those which
145 would have been set by a prior fmvis instruction are deliberately ignored.
146 (If these instructions involved reading from registers rather than immediates
147 it would be a different story).
149 ## Load BF16 Immediate
153 Reinterprets `D << 16` as a 32-bit float, which is then converted to a
154 64-bit float and written to `FRS`. This is equivalent to reinterpreting
155 `D` as a `BF16` and converting to 64-bit float.
156 There is no need for an Rc=1 variant because this is an immediate loading
163 fmvis f4, 0 # writes +0.0 to f4
164 # loading handy constants
165 fmvis f4, 0x8000 # writes -0.0 to f4
166 fmvis f4, 0x3F80 # writes +1.0 to f4
167 fmvis f4, 0xBF80 # writes -1.0 to f4
168 fmvis f4, 0xBFC0 # writes -1.5 to f4
169 fmvis f4, 0x7FC0 # writes +qNaN to f4
170 fmvis f4, 0x7F80 # writes +Infinity to f4
171 fmvis f4, 0xFF80 # writes -Infinity to f4
172 fmvis f4, 0x3FFF # writes +1.9921875 to f4
174 # clearing 128 FPRs with 2 SVP64 instructions
175 # by issuing 32 vec4 (subvector length 4) ops
177 sv.fmvis/vec4 f0, 0 # writes +0.0 to f0-f127
179 Important: If the float load immediate instruction(s) are left out,
180 change all [GPR to FPR conversion instructions](#GPR-to-FPR-conversions)
181 to instead write `+0.0` if `RA` is register `0`, at least
182 allowing clearing FPRs.
184 `fmvis` fits with DX-Form:
186 | 0-5 | 6-10 | 11-15 | 16-25 | 26-30 | 31 | Form |
187 |--------|------|-------|-------|-------|-----|---------|
188 | Major | FRS | d1 | d0 | XO | d2 | DX-Form |
192 bf16 = d0 || d1 || d2 # create BF16 immediate
193 fp32 = bf16 || [0]*16 # convert BF16 to FP32
194 FRS = DOUBLE(fp32) # convert FP32 to FP64
196 Special registers altered:
200 ## Float Immediate Second-Half MV <a name="fishmv"></a>
206 | 0-5 | 6-10 | 11-15 | 16-25 | 26-30 | 31 | Form |
207 |--------|------|-------|-------|-------|-----|---------|
208 | Major | FRS | d1 | d0 | XO | d2 | DX-Form |
210 Strategically similar to how `oris` is used to construct
211 32-bit Integers, an additional 16-bits of immediate is
212 inserted into `FRS` to extend its accuracy to
213 a full FP32 (stored as usual in FP64 Format within the FPR).
214 If a prior `fmvis` instruction had been used to
215 set the upper 16-bits of an FP32 value, `fishmv` contains the
218 The key difference between using `li` and `oris` to construct 32-bit
219 GPR Immediates and `fishmv` is that the `fmvis` will have converted
220 the `BF16` immediate to FP64 (Double) format.
221 This is taken into consideration
222 as can be seen in the pseudocode below.
226 fp32 <- SINGLE((FRS)) # convert to FP32
227 fp32[16:31] <- d0 || d1 || d2 # replace LSB half
228 FRS <- DOUBLE(fp32) # convert back to FP64
230 Special registers altered:
234 **This instruction performs a Read-Modify-Write.** *FRS is read, the additional
235 16 bit immediate inserted, and the result also written to FRS*
240 # these two combined instructions write 0x3f808000
241 # into f4 as an FP32 to be converted to an FP64.
242 # actual contents in f4 after conversion: 0x3ff0_1000_0000_0000
243 # first the upper bits, happens to be +1.0
244 fmvis f4, 0x3F80 # writes +1.0 to f4
245 # now write the lower 16 bits of an FP32
246 fishmv f4, 0x8000 # writes +1.00390625 to f4
251 Tables that are used by
252 `fmvtg[s][.]`/`fmvfg[s][.]`/`fcvttg[s][.]`/`fcvtfg[s][.]`:
254 ## `RCS` -- `Rc` and `s`
256 | `RCS` | `Rc` | FP Single Mode | Assembly Alias Mnemonic |
257 |-------|------|----------------|-------------------------|
258 | 0 | 0 | Double | `<op>` |
259 | 1 | 1 | Double | `<op>.` |
260 | 2 | 0 | Single | `<op>s` |
261 | 3 | 1 | Single | `<op>s.` |
263 ## `IT` -- Integer Type
265 | `IT` | Integer Type | Assembly Alias Mnemonic |
266 |------|-----------------|-------------------------|
267 | 0 | Signed 32-bit | `<op>w` |
268 | 1 | Unsigned 32-bit | `<op>uw` |
269 | 2 | Signed 64-bit | `<op>d` |
270 | 3 | Unsigned 64-bit | `<op>ud` |
272 ## `CVM` -- Float to Integer Conversion Mode
274 | `CVM` | `rounding_mode` | Semantics |
275 |-------|-----------------|----------------------------------|
276 | 000 | from `FPSCR` | [OpenPower semantics] |
277 | 001 | Truncate | [OpenPower semantics] |
278 | 010 | from `FPSCR` | [Java/Saturating semantics] |
279 | 011 | Truncate | [Java/Saturating semantics] |
280 | 100 | from `FPSCR` | [JavaScript semantics] |
281 | 101 | Truncate | [JavaScript semantics] |
282 | rest | -- | illegal instruction trap for now |
284 [OpenPower semantics]: #fp-to-int-openpower-conversion-semantics
285 [Java/Saturating semantics]: #fp-to-int-java-saturating-conversion-semantics
286 [JavaScript semantics]: #fp-to-int-javascript-conversion-semantics
290 These instructions perform a straight unaltered bit-level copy from one Register
298 | 0-5 | 6-10 | 11-15 | 16-20 | 21-30 | 31 | Form |
299 |-----|------|-------|-------|-------|----|--------|
300 | PO | RT | 0 | FRB | XO | Rc | X-Form |
306 Move a 64-bit float from a FPR to a GPR, just copying bits of the IEEE 754
307 representation directly. This is equivalent to `stfd` followed by `ld`.
308 As `fmvtg` is just copying bits, `FPSCR` is not affected in any way.
310 Rc=1 tests RT and sets CR0, exactly like all other Scalar Fixed-Point
313 Special Registers altered:
317 ## FPR to GPR Move Single
322 | 0-5 | 6-10 | 11-15 | 16-20 | 21-30 | 31 | Form |
323 |-----|------|-------|-------|-------|----|--------|
324 | PO | RT | 0 | FRB | XO | Rc | X-Form |
327 RT <- [0] * 32 || SINGLE((FRB)) # SINGLE since that's what stfs uses
330 Move a 32-bit float from a FPR to a GPR, just copying bits of the IEEE 754
331 representation directly. This is equivalent to `stfs` followed by `lwz`.
332 As `fmvtgs` is just copying bits, `FPSCR` is not affected in any way.
334 Rc=1 tests RT and sets CR0, exactly like all other Scalar Fixed-Point
337 Special Registers altered:
346 | 0-5 | 6-10 | 11-15 | 16-20 | 21-30 | 31 | Form |
347 |-----|------|-------|-------|-------|----|--------|
348 | PO | FRT | 0 | RB | XO | Rc | X-Form |
354 move a 64-bit float from a GPR to a FPR, just copying bits of the IEEE 754
355 representation directly. This is equivalent to `std` followed by `lfd`.
356 As `fmvfg` is just copying bits, `FPSCR` is not affected in any way.
358 Rc=1 tests FRT and sets CR1, exactly like all other Scalar Floating-Point
361 Special Registers altered:
365 ## GPR to FPR Move Single
370 | 0-5 | 6-10 | 11-15 | 16-20 | 21-30 | 31 | Form |
371 |-----|------|-------|-------|-------|----|--------|
372 | PO | FRT | 0 | RB | XO | Rc | X-Form |
375 FRT <- DOUBLE((RB)[32:63]) # DOUBLE since that's what lfs uses
378 move a 32-bit float from a GPR to a FPR, just copying bits of the IEEE 754
379 representation directly. This is equivalent to `stw` followed by `lfs`.
380 As `fmvfgs` is just copying bits, `FPSCR` is not affected in any way.
382 Rc=1 tests FRT and sets CR1, exactly like all other Scalar Floating-Point
385 Special Registers altered:
391 Unlike the move instructions
392 these instructions perform conversions between Integer and
393 Floating Point. Truncation can therefore occur, as well
396 ## Floating-point Convert From GPR
398 | 0-5 | 6-10 | 11-12 | 13-15 | 16-20 | 21-29 | 30-31 | Form |
399 |-----|------|-------|-------|-------|-------|-------|--------|
400 | PO | FRT | IT | 0 | RB | XO | RCS | X-Form |
402 `fcvtfg FRT, RB, IT, RCS`
405 if IT[0] = 0 and RCS[0] = 0 then # 32-bit int -> 64-bit float
406 # rounding never necessary, so don't touch FPSCR
407 # based off xvcvsxwdp
408 if IT = 0 then # Signed 32-bit
409 src <- bfp_CONVERT_FROM_SI32((RB)[32:63])
410 else # IT = 1 -- Unsigned 32-bit
411 src <- bfp_CONVERT_FROM_UI32((RB)[32:63])
412 FRT <- bfp64_CONVERT_FROM_BFP(src)
414 # rounding may be necessary. based off xscvuxdsp
417 case(0): # Signed 32-bit
418 src <- bfp_CONVERT_FROM_SI32((RB)[32:63])
419 case(1): # Unsigned 32-bit
420 src <- bfp_CONVERT_FROM_UI32((RB)[32:63])
421 case(2): # Signed 64-bit
422 src <- bfp_CONVERT_FROM_SI64((RB))
423 default: # Unsigned 64-bit
424 src <- bfp_CONVERT_FROM_UI64((RB))
425 if RCS[0] = 1 then # Single
426 rnd <- bfp_ROUND_TO_BFP32(FPSCR.RN, src)
427 result32 <- bfp32_CONVERT_FROM_BFP(rnd)
428 cls <- fprf_CLASS_BFP32(result32)
429 result <- DOUBLE(result32)
431 rnd <- bfp_ROUND_TO_BFP64(FPSCR.RN, src)
432 result <- bfp64_CONVERT_FROM_BFP(rnd)
433 cls <- fprf_CLASS_BFP64(result)
435 if xx_flag = 1 then SetFX(FPSCR.XX)
442 <!-- note the PowerISA spec. explicitly has empty lines before/after SetFX,
443 don't remove them -->
445 Convert from a unsigned/signed 32/64-bit integer in RB to a 32/64-bit
446 float in FRT, following the usual 32-bit float in 64-bit float format.
447 If converting from a unsigned/signed 32-bit integer to a 64-bit float,
448 rounding is never necessary, so `FPSCR` is unmodified and exceptions are
449 never raised. Otherwise, `FPSCR` is modified and exceptions are raised
452 Rc=1 tests FRT and sets CR1, exactly like all other Scalar Floating-Point
455 Special Registers altered:
458 FPCSR (TODO: which bits?) (if IT[0] != 0 or RCS[0] != 0)
462 | Assembly Alias | Full Instruction | | Assembly Alias | Full Instruction |
463 |----------------------|------------------------|------|----------------------|------------------------|
464 | `fcvtfgw FRT, RB` | `fcvtfg FRT, RB, 0, 0` | | `fcvtfgd FRT, RB` | `fcvtfg FRT, RB, 2, 0` |
465 | `fcvtfgw. FRT, RB` | `fcvtfg FRT, RB, 0, 1` | | `fcvtfgd. FRT, RB` | `fcvtfg FRT, RB, 2, 1` |
466 | `fcvtfgws FRT, RB` | `fcvtfg FRT, RB, 0, 2` | | `fcvtfgds FRT, RB` | `fcvtfg FRT, RB, 2, 2` |
467 | `fcvtfgws. FRT, RB` | `fcvtfg FRT, RB, 0, 3` | | `fcvtfgds. FRT, RB` | `fcvtfg FRT, RB, 2, 3` |
468 | `fcvtfguw FRT, RB` | `fcvtfg FRT, RB, 1, 0` | | `fcvtfgud FRT, RB` | `fcvtfg FRT, RB, 3, 0` |
469 | `fcvtfguw. FRT, RB` | `fcvtfg FRT, RB, 1, 1` | | `fcvtfgud. FRT, RB` | `fcvtfg FRT, RB, 3, 1` |
470 | `fcvtfguws FRT, RB` | `fcvtfg FRT, RB, 1, 2` | | `fcvtfguds FRT, RB` | `fcvtfg FRT, RB, 3, 2` |
471 | `fcvtfguws. FRT, RB` | `fcvtfg FRT, RB, 1, 3` | | `fcvtfguds. FRT, RB` | `fcvtfg FRT, RB, 3, 3` |
473 ## Floating-point Convert From GPR
475 | 0-5 | 6-10 | 11-12 | 13-15 | 16-20 | 21-29 | 30-31 | Form |
476 |-----|------|-------|-------|-------|-------|-------|--------|
477 | PO | FRT | IT | 0 | RB | XO | RCS | X-Form |
479 `fcvtfg FRT, RB, IT, RCS`
482 if IT[0] = 0 and RCS[0] = 0 then # 32-bit int -> 64-bit float
483 # rounding never necessary, so don't touch FPSCR
484 # based off xvcvsxwdp
485 if IT = 0 then # Signed 32-bit
486 src <- bfp_CONVERT_FROM_SI32((RB)[32:63])
487 else # IT = 1 -- Unsigned 32-bit
488 src <- bfp_CONVERT_FROM_UI32((RB)[32:63])
489 FRT <- bfp64_CONVERT_FROM_BFP(src)
491 # rounding may be necessary. based off xscvuxdsp
494 case(0): # Signed 32-bit
495 src <- bfp_CONVERT_FROM_SI32((RB)[32:63])
496 case(1): # Unsigned 32-bit
497 src <- bfp_CONVERT_FROM_UI32((RB)[32:63])
498 case(2): # Signed 64-bit
499 src <- bfp_CONVERT_FROM_SI64((RB))
500 default: # Unsigned 64-bit
501 src <- bfp_CONVERT_FROM_UI64((RB))
502 if RCS[0] = 1 then # Single
503 rnd <- bfp_ROUND_TO_BFP32(FPSCR.RN, src)
504 result32 <- bfp32_CONVERT_FROM_BFP(rnd)
505 cls <- fprf_CLASS_BFP32(result32)
506 result <- DOUBLE(result32)
508 rnd <- bfp_ROUND_TO_BFP64(FPSCR.RN, src)
509 result <- bfp64_CONVERT_FROM_BFP(rnd)
510 cls <- fprf_CLASS_BFP64(result)
512 if xx_flag = 1 then SetFX(FPSCR.XX)
519 <!-- note the PowerISA spec. explicitly has empty lines before/after SetFX,
520 don't remove them -->
522 Convert from a unsigned/signed 32/64-bit integer in RB to a 32/64-bit
523 float in FRT, following the usual 32-bit float in 64-bit float format.
524 If converting from a unsigned/signed 32-bit integer to a 64-bit float,
525 rounding is never necessary, so `FPSCR` is unmodified and exceptions are
526 never raised. Otherwise, `FPSCR` is modified and exceptions are raised
529 Rc=1 tests FRT and sets CR1, exactly like all other Scalar Floating-Point
532 Special Registers altered:
535 FPCSR (TODO: which bits?) (if IT[0] != 0 or RCS[0] != 0)
539 | Assembly Alias | Full Instruction | | Assembly Alias | Full Instruction |
540 |----------------------|------------------------|------|----------------------|------------------------|
541 | `fcvtfgw FRT, RB` | `fcvtfg FRT, RB, 0, 0` | | `fcvtfgd FRT, RB` | `fcvtfg FRT, RB, 2, 0` |
542 | `fcvtfgw. FRT, RB` | `fcvtfg FRT, RB, 0, 1` | | `fcvtfgd. FRT, RB` | `fcvtfg FRT, RB, 2, 1` |
543 | `fcvtfgws FRT, RB` | `fcvtfg FRT, RB, 0, 2` | | `fcvtfgds FRT, RB` | `fcvtfg FRT, RB, 2, 2` |
544 | `fcvtfgws. FRT, RB` | `fcvtfg FRT, RB, 0, 3` | | `fcvtfgds. FRT, RB` | `fcvtfg FRT, RB, 2, 3` |
545 | `fcvtfguw FRT, RB` | `fcvtfg FRT, RB, 1, 0` | | `fcvtfgud FRT, RB` | `fcvtfg FRT, RB, 3, 0` |
546 | `fcvtfguw. FRT, RB` | `fcvtfg FRT, RB, 1, 1` | | `fcvtfgud. FRT, RB` | `fcvtfg FRT, RB, 3, 1` |
547 | `fcvtfguws FRT, RB` | `fcvtfg FRT, RB, 1, 2` | | `fcvtfguds FRT, RB` | `fcvtfg FRT, RB, 3, 2` |
548 | `fcvtfguws. FRT, RB` | `fcvtfg FRT, RB, 1, 3` | | `fcvtfguds. FRT, RB` | `fcvtfg FRT, RB, 3, 3` |
550 ## Floating-point to Integer Conversion Overview
552 <div id="fpr-to-gpr-conversion-mode"></div>
554 IEEE 754 doesn't specify what results are obtained when converting a NaN
555 or out-of-range floating-point value to integer, so different programming
556 languages and ISAs have made different choices. Below is an overview
557 of the different variants, listing the languages and hardware that
558 implements each variant.
560 For convenience, we will give those different conversion semantics names
561 based on which common ISA or programming language uses them, since there
562 may not be an established name for them:
564 **Standard OpenPower conversion**
566 This conversion performs "saturation with NaN converted to minimum
567 valid integer". This is also exactly the same as the x86 ISA conversion
568 semantics. OpenPOWER however has instructions for both:
570 * rounding mode read from FPSCR
571 * rounding mode always set to truncate
573 **Java/Saturating conversion**
575 For the sake of simplicity, the FP -> Integer conversion semantics
576 generalized from those used by Java's semantics (and Rust's `as`
577 operator) will be referred to as [Java/Saturating conversion
578 semantics](#fp-to-int-java-saturating-conversion-semantics).
580 Those same semantics are used in some way by all of the following
581 languages (not necessarily for the default conversion method):
584 [FP -> Integer conversion](https://docs.oracle.com/javase/specs/jls/se16/html/jls-5.html#jls-5.1.3)
585 (only for long/int results)
586 * Rust's FP -> Integer conversion using the
587 [`as` operator](https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics)
589 [`llvm.fptosi.sat`](https://llvm.org/docs/LangRef.html#llvm-fptosi-sat-intrinsic) and
590 [`llvm.fptoui.sat`](https://llvm.org/docs/LangRef.html#llvm-fptoui-sat-intrinsic) intrinsics
591 * SPIR-V's OpenCL dialect's
592 [`OpConvertFToU`](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpConvertFToU) and
593 [`OpConvertFToS`](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpConvertFToS)
594 instructions when decorated with
595 [the `SaturatedConversion` decorator](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_a_id_decoration_a_decoration).
596 * WebAssembly has also introduced
597 [trunc_sat_u](ttps://webassembly.github.io/spec/core/exec/numerics.html#op-trunc-sat-u) and
598 [trunc_sat_s](https://webassembly.github.io/spec/core/exec/numerics.html#op-trunc-sat-s)
600 **JavaScript conversion**
602 For the sake of simplicity, the FP -> Integer conversion
603 semantics generalized from those used by JavaScripts's `ToInt32`
604 abstract operation will be referred to as [JavaScript conversion
605 semantics](#fp-to-int-javascript-conversion-semantics).
607 This instruction is present in ARM assembler as FJCVTZS
608 <https://developer.arm.com/documentation/dui0801/g/hko1477562192868>
612 All of these instructions have an Rc=1 mode which sets CR0
613 in the normal way for any instructions producing a GPR result.
614 Additionally, when OE=1, if the numerical value of the FP number
615 is not 100% accurately preserved (due to truncation or saturation
616 and including when the FP number was NaN) then this is considered
617 to be an integer Overflow condition, and CR0.SO, XER.SO and XER.OV
618 are all set as normal for any GPR instructions that overflow.
620 ### FP to Integer Conversion Simplified Pseudo-code
624 | term | result type | definition |
625 |---------------------------|-------------|----------------------------------------------------------------------------------------------------|
626 | `fp` | -- | `f32` or `f64` (or other types from SimpleV) |
627 | `int` | -- | `u32`/`u64`/`i32`/`i64` (or other types from SimpleV) |
628 | `uint` | -- | the unsigned integer of the same bit-width as `int` |
629 | `int::BITS` | `int` | the bit-width of `int` |
630 | `uint::MIN_VALUE` | `uint` | the minimum value `uint` can store: `0` |
631 | `uint::MAX_VALUE` | `uint` | the maximum value `uint` can store: `2^int::BITS - 1` |
632 | `int::MIN_VALUE` | `int` | the minimum value `int` can store : `-2^(int::BITS-1)` |
633 | `int::MAX_VALUE` | `int` | the maximum value `int` can store : `2^(int::BITS-1) - 1` |
634 | `int::VALUE_COUNT` | Integer | the number of different values `int` can store (`2^int::BITS`). too big to fit in `int`. |
635 | `rint(fp, rounding_mode)` | `fp` | rounds the floating-point value `fp` to an integer according to rounding mode `rounding_mode` |
637 <div id="fp-to-int-openpower-conversion-semantics"></div>
638 OpenPower conversion semantics (section A.2 page 1009 (page 1035) of
642 def fp_to_int_open_power<fp, int>(v: fp) -> int:
644 return int::MIN_VALUE
645 if v >= int::MAX_VALUE:
646 return int::MAX_VALUE
647 if v <= int::MIN_VALUE:
648 return int::MIN_VALUE
649 return (int)rint(v, rounding_mode)
652 <div id="fp-to-int-java-saturating-conversion-semantics"></div>
653 [Java/Saturating conversion semantics](https://docs.oracle.com/javase/specs/jls/se16/html/jls-5.html#jls-5.1.3)
654 (only for long/int results)/
655 [Rust semantics](https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics)
656 (with adjustment to add non-truncate rounding modes):
659 def fp_to_int_java_saturating<fp, int>(v: fp) -> int:
662 if v >= int::MAX_VALUE:
663 return int::MAX_VALUE
664 if v <= int::MIN_VALUE:
665 return int::MIN_VALUE
666 return (int)rint(v, rounding_mode)
669 <div id="fp-to-int-javascript-conversion-semantics"></div>
670 Section 7.1 of the ECMAScript / JavaScript
671 [conversion semantics](https://262.ecma-international.org/11.0/#sec-toint32)
672 (with adjustment to add non-truncate rounding modes):
675 def fp_to_int_java_script<fp, int>(v: fp) -> int:
676 if v is NaN or infinite:
678 v = rint(v, rounding_mode) # assume no loss of precision in result
679 v = v mod int::VALUE_COUNT # 2^32 for i32, 2^64 for i64, result is non-negative
684 ## Floating-point Convert To GPR
686 | 0-5 | 6-10 | 11-12 | 13-15 | 16-20 | 21-28 | 29 | 30 | 31 | Form |
687 |-----|------|-------|-------|-------|-------|--------|----|--------|---------|
688 | PO | RT | IT | CVM | FRB | XO | RCS[0] | OE | RCS[1] | XO-Form |
690 `fcvttg RT, FRB, CVM, IT, RCS`
691 `fcvttgo RT, FRB, CVM, IT, RCS`
694 # based on xscvdpuxws
696 if RCS[0] = 1 then # if Single mode
697 src <- bfp_CONVERT_FROM_BFP32(SINGLE((FRB)))
699 src <- bfp_CONVERT_FROM_BFP64((FRB))
702 case(0): # Signed 32-bit
703 range_min <- bfp_CONVERT_FROM_SI32(0x8000_0000)
704 range_max <- bfp_CONVERT_FROM_SI32(0x7FFF_FFFF)
705 js_mask <- 0xFFFF_FFFF
706 case(1): # Unsigned 32-bit
707 range_min <- bfp_CONVERT_FROM_UI32(0)
708 range_max <- bfp_CONVERT_FROM_UI32(0xFFFF_FFFF)
709 js_mask <- 0xFFFF_FFFF
710 case(2): # Signed 64-bit
711 range_min <- bfp_CONVERT_FROM_SI64(-0x8000_0000_0000_0000)
712 range_max <- bfp_CONVERT_FROM_SI64(0x7FFF_FFFF_FFFF_FFFF)
713 js_mask <- 0xFFFF_FFFF_FFFF_FFFF
714 default: # Unsigned 64-bit
715 range_min <- bfp_CONVERT_FROM_UI64(0)
716 range_max <- bfp_CONVERT_FROM_UI64(0xFFFF_FFFF_FFFF_FFFF)
717 js_mask <- 0xFFFF_FFFF_FFFF_FFFF
719 if CVM[2] = 1 or FPSCR.RN = 0b01 then
720 rnd <- bfp_ROUND_TO_INTEGER_TRUNC(src)
721 else if FPSCR.RN = 0b00 then
722 rnd <- bfp_ROUND_TO_INTEGER_NEAR_EVEN(src)
723 else if FPSCR.RN = 0b10 then
724 rnd <- bfp_ROUND_TO_INTEGER_CEIL(src)
725 else if FPSCR.RN = 0b11 then
726 rnd <- bfp_ROUND_TO_INTEGER_FLOOR(src)
729 case(0, 1): # OpenPower semantics
731 result <- si64_CONVERT_FROM_BFP(range_min)
732 else if bfp_COMPARE_GT(rnd, range_max) then
733 result <- ui64_CONVERT_FROM_BFP(range_max)
734 else if bfp_COMPARE_LT(rnd, range_min) then
735 result <- si64_CONVERT_FROM_BFP(range_min)
736 else if IT[1] = 1 then # Unsigned 32/64-bit
737 result <- ui64_CONVERT_FROM_BFP(range_max)
738 else # Signed 32/64-bit
739 result <- si64_CONVERT_FROM_BFP(range_max)
740 case(2, 3): # Java/Saturating semantics
743 else if bfp_COMPARE_GT(rnd, range_max) then
744 result <- ui64_CONVERT_FROM_BFP(range_max)
745 else if bfp_COMPARE_LT(rnd, range_min) then
746 result <- si64_CONVERT_FROM_BFP(range_min)
747 else if IT[1] = 1 then # Unsigned 32/64-bit
748 result <- ui64_CONVERT_FROM_BFP(range_max)
749 else # Signed 32/64-bit
750 result <- si64_CONVERT_FROM_BFP(range_max)
751 default: # JavaScript semantics
752 # CVM = 6, 7 are illegal instructions
753 # this works because the largest type we try to convert from has
754 # 53 significand bits, and the largest type we try to convert to
755 # has 64 bits, and the sum of those is strictly less than the 128
756 # bits of the intermediate result.
757 limit <- bfp_CONVERT_FROM_UI128([1] * 128)
758 if IsInf(rnd) or IsNaN(rnd) then
760 else if bfp_COMPARE_GT(bfp_ABSOLUTE(rnd), limit) then
763 result128 <- si128_CONVERT_FROM_BFP(rnd)
764 result <- result128[64:127] & js_mask
767 case(0): # Signed 32-bit
768 result <- EXTS64(result[32:63])
769 result_bfp <- bfp_CONVERT_FROM_SI32(result[32:63])
770 case(1): # Unsigned 32-bit
771 result <- EXTZ64(result[32:63])
772 result_bfp <- bfp_CONVERT_FROM_UI32(result[32:63])
773 case(2): # Signed 64-bit
774 result_bfp <- bfp_CONVERT_FROM_SI64(result)
775 default: # Unsigned 64-bit
776 result_bfp <- bfp_CONVERT_FROM_UI64(result)
778 if vxsnan_flag = 1 then SetFX(FPSCR.VXSNAN)
779 if vxcvi_flag = 1 then SetFX(FPSCR.VXCVI)
780 if xx_flag = 1 then SetFX(FPSCR.XX)
782 vx_flag <- vxsnan_flag | vxcvi_flag
783 vex_flag <- FPSCR.VE & vx_flag
787 FPSCR.FPRF <- undefined
790 if IsNaN(src) or not bfp_COMPARE_EQ(src, result_bfp) then
791 overflow <- 1 # signals SO only when OE = 1
797 Convert from 32/64-bit float in FRB to a unsigned/signed 32/64-bit integer
798 in RT, with the conversion overflow/rounding semantics following the
799 chosen `CVM` value, following the usual 32-bit float in 64-bit float
800 format. `FPSCR` is modified and exceptions are raised as usual.
802 Both of these instructions have an Rc=1 mode which sets CR0 in the normal
803 way for any instructions producing a GPR result. Additionally, when OE=1,
804 if the numerical value of the FP number is not 100% accurately preserved
805 (due to truncation or saturation and including when the FP number was
806 NaN) then this is considered to be an integer Overflow condition, and
807 CR0.SO, XER.SO and XER.OV are all set as normal for any GPR instructions
810 Special Registers altered:
813 XER SO, OV, OV32 (if OE=1)
814 FPCSR (TODO: which bits?)
818 For brevity, `[o]` is used to mean `o` is optional there.
820 | Assembly Alias | Full Instruction | Assembly Alias | Full Instruction |
821 |------------------------------|--------------------------------|------------------------------|--------------------------------|
822 | `fcvttgw[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 0, 0` | `fcvttgd[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 2, 0` |
823 | `fcvttgw[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 0, 1` | `fcvttgd[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 2, 1` |
824 | `fcvtstgw[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 0, 2` | `fcvtstgd[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 2, 2` |
825 | `fcvtstgw[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 0, 3` | `fcvtstgd[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 2, 3` |
826 | `fcvttguw[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 1, 0` | `fcvttgud[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 3, 0` |
827 | `fcvttguw[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 1, 1` | `fcvttgud[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 3, 1` |
828 | `fcvtstguw[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 1, 2` | `fcvtstgud[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 3, 2` |
829 | `fcvtstguw[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 1, 3` | `fcvtstgud[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 3, 3` |
831 ## Floating-point Convert To GPR
833 | 0-5 | 6-10 | 11-12 | 13-15 | 16-20 | 21-28 | 29 | 30 | 31 | Form |
834 |-----|------|-------|-------|-------|-------|--------|----|--------|---------|
835 | PO | RT | IT | CVM | FRB | XO | RCS[0] | OE | RCS[1] | XO-Form |
837 `fcvttg RT, FRB, CVM, IT, RCS`
838 `fcvttgo RT, FRB, CVM, IT, RCS`
841 # based on xscvdpuxws
843 if RCS[0] = 1 then # if Single mode
844 src <- bfp_CONVERT_FROM_BFP32(SINGLE((FRB)))
846 src <- bfp_CONVERT_FROM_BFP64((FRB))
849 case(0): # Signed 32-bit
850 range_min <- bfp_CONVERT_FROM_SI32(0x8000_0000)
851 range_max <- bfp_CONVERT_FROM_SI32(0x7FFF_FFFF)
852 js_mask <- 0xFFFF_FFFF
853 case(1): # Unsigned 32-bit
854 range_min <- bfp_CONVERT_FROM_UI32(0)
855 range_max <- bfp_CONVERT_FROM_UI32(0xFFFF_FFFF)
856 js_mask <- 0xFFFF_FFFF
857 case(2): # Signed 64-bit
858 range_min <- bfp_CONVERT_FROM_SI64(-0x8000_0000_0000_0000)
859 range_max <- bfp_CONVERT_FROM_SI64(0x7FFF_FFFF_FFFF_FFFF)
860 js_mask <- 0xFFFF_FFFF_FFFF_FFFF
861 default: # Unsigned 64-bit
862 range_min <- bfp_CONVERT_FROM_UI64(0)
863 range_max <- bfp_CONVERT_FROM_UI64(0xFFFF_FFFF_FFFF_FFFF)
864 js_mask <- 0xFFFF_FFFF_FFFF_FFFF
866 if CVM[2] = 1 or FPSCR.RN = 0b01 then
867 rnd <- bfp_ROUND_TO_INTEGER_TRUNC(src)
868 else if FPSCR.RN = 0b00 then
869 rnd <- bfp_ROUND_TO_INTEGER_NEAR_EVEN(src)
870 else if FPSCR.RN = 0b10 then
871 rnd <- bfp_ROUND_TO_INTEGER_CEIL(src)
872 else if FPSCR.RN = 0b11 then
873 rnd <- bfp_ROUND_TO_INTEGER_FLOOR(src)
876 case(0, 1): # OpenPower semantics
878 result <- si64_CONVERT_FROM_BFP(range_min)
879 else if bfp_COMPARE_GT(rnd, range_max) then
880 result <- ui64_CONVERT_FROM_BFP(range_max)
881 else if bfp_COMPARE_LT(rnd, range_min) then
882 result <- si64_CONVERT_FROM_BFP(range_min)
883 else if IT[1] = 1 then # Unsigned 32/64-bit
884 result <- ui64_CONVERT_FROM_BFP(range_max)
885 else # Signed 32/64-bit
886 result <- si64_CONVERT_FROM_BFP(range_max)
887 case(2, 3): # Java/Saturating semantics
890 else if bfp_COMPARE_GT(rnd, range_max) then
891 result <- ui64_CONVERT_FROM_BFP(range_max)
892 else if bfp_COMPARE_LT(rnd, range_min) then
893 result <- si64_CONVERT_FROM_BFP(range_min)
894 else if IT[1] = 1 then # Unsigned 32/64-bit
895 result <- ui64_CONVERT_FROM_BFP(range_max)
896 else # Signed 32/64-bit
897 result <- si64_CONVERT_FROM_BFP(range_max)
898 default: # JavaScript semantics
899 # CVM = 6, 7 are illegal instructions
900 # this works because the largest type we try to convert from has
901 # 53 significand bits, and the largest type we try to convert to
902 # has 64 bits, and the sum of those is strictly less than the 128
903 # bits of the intermediate result.
904 limit <- bfp_CONVERT_FROM_UI128([1] * 128)
905 if IsInf(rnd) or IsNaN(rnd) then
907 else if bfp_COMPARE_GT(bfp_ABSOLUTE(rnd), limit) then
910 result128 <- si128_CONVERT_FROM_BFP(rnd)
911 result <- result128[64:127] & js_mask
914 case(0): # Signed 32-bit
915 result <- EXTS64(result[32:63])
916 result_bfp <- bfp_CONVERT_FROM_SI32(result[32:63])
917 case(1): # Unsigned 32-bit
918 result <- EXTZ64(result[32:63])
919 result_bfp <- bfp_CONVERT_FROM_UI32(result[32:63])
920 case(2): # Signed 64-bit
921 result_bfp <- bfp_CONVERT_FROM_SI64(result)
922 default: # Unsigned 64-bit
923 result_bfp <- bfp_CONVERT_FROM_UI64(result)
925 if vxsnan_flag = 1 then SetFX(FPSCR.VXSNAN)
926 if vxcvi_flag = 1 then SetFX(FPSCR.VXCVI)
927 if xx_flag = 1 then SetFX(FPSCR.XX)
929 vx_flag <- vxsnan_flag | vxcvi_flag
930 vex_flag <- FPSCR.VE & vx_flag
934 FPSCR.FPRF <- undefined
937 if IsNaN(src) or not bfp_COMPARE_EQ(src, result_bfp) then
938 overflow <- 1 # signals SO only when OE = 1
944 Convert from 32/64-bit float in FRB to a unsigned/signed 32/64-bit integer
945 in RT, with the conversion overflow/rounding semantics following the
946 chosen `CVM` value, following the usual 32-bit float in 64-bit float
947 format. `FPSCR` is modified and exceptions are raised as usual.
949 Both of these instructions have an Rc=1 mode which sets CR0 in the normal
950 way for any instructions producing a GPR result. Additionally, when OE=1,
951 if the numerical value of the FP number is not 100% accurately preserved
952 (due to truncation or saturation and including when the FP number was
953 NaN) then this is considered to be an integer Overflow condition, and
954 CR0.SO, XER.SO and XER.OV are all set as normal for any GPR instructions
957 Special Registers altered:
960 XER SO, OV, OV32 (if OE=1)
961 FPCSR (TODO: which bits?)
965 For brevity, `[o]` is used to mean `o` is optional there.
967 | Assembly Alias | Full Instruction | Assembly Alias | Full Instruction |
968 |------------------------------|--------------------------------|------------------------------|--------------------------------|
969 | `fcvttgw[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 0, 0` | `fcvttgd[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 2, 0` |
970 | `fcvttgw[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 0, 1` | `fcvttgd[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 2, 1` |
971 | `fcvtstgw[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 0, 2` | `fcvtstgd[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 2, 2` |
972 | `fcvtstgw[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 0, 3` | `fcvtstgd[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 2, 3` |
973 | `fcvttguw[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 1, 0` | `fcvttgud[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 3, 0` |
974 | `fcvttguw[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 1, 1` | `fcvttgud[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 3, 1` |
975 | `fcvtstguw[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 1, 2` | `fcvtstgud[o] RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 3, 2` |
976 | `fcvtstguw[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 1, 3` | `fcvtstgud[o]. RT, FRB, CVM` | `fcvttg[o] RT, FRB, CVM, 3, 3` |