sync changes from ls006 -> int_fp_mv
[libreriscv.git] / openpower / sv / int_fp_mv.mdwn
1 [[!tag standards]]
2
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>
4
5 # FPR-to-GPR and GPR-to-FPR
6
7 TODO special constants instruction (e, tau/N, ln 2, sqrt 2, etc.) -- exclude any constants available through fmvis
8
9 **Draft Status** under development, for submission as an RFC
10
11 Links:
12
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
22
23 Trademarks:
24
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.
30
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
34 Law.
35
36 Introduction:
37
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.
45
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.
50
51 Prior to the formation of the Compliancy Levels first introduced
52 in v3.0C and v3.1
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
60 competitive edge.
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.
64
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.
68
69 Therefore, we are proposing adding:
70
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
76
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:
80
81 * Int -> Float
82 * **standard IEEE754** - used by most languages and CPUs
83 * Float -> Int
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
88
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)
93
94 # Proposed New Scalar Instructions
95
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*.
97
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.
103
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
109 always be present).*
110
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]]
117
118 # Float load immediate <a name="fmvis"></a>
119
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.
126
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
130 is avoided.
131
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.*
141
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).
148
149 ## Load BF16 Immediate
150
151 `fmvis FRS, D`
152
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
157 instruction.
158
159 Example:
160
161 ```
162 # clearing a FPR
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
173
174 # clearing 128 FPRs with 2 SVP64 instructions
175 # by issuing 32 vec4 (subvector length 4) ops
176 setvli VL=MVL=32
177 sv.fmvis/vec4 f0, 0 # writes +0.0 to f0-f127
178 ```
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.
183
184 `fmvis` fits with DX-Form:
185
186 | 0-5 | 6-10 | 11-15 | 16-25 | 26-30 | 31 | Form |
187 |--------|------|-------|-------|-------|-----|---------|
188 | Major | FRS | d1 | d0 | XO | d2 | DX-Form |
189
190 Pseudocode:
191
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
195
196 Special registers altered:
197
198 None
199
200 ## Float Immediate Second-Half MV <a name="fishmv"></a>
201
202 `fishmv FRS, D`
203
204 DX-Form:
205
206 | 0-5 | 6-10 | 11-15 | 16-25 | 26-30 | 31 | Form |
207 |--------|------|-------|-------|-------|-----|---------|
208 | Major | FRS | d1 | d0 | XO | d2 | DX-Form |
209
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
216 lower 16-bits.
217
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.
223
224 Pseudocode:
225
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
229
230 Special registers altered:
231
232 None
233
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*
236
237 Example:
238
239 ```
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
247 ```
248
249 # Immediate Tables
250
251 Tables that are used by
252 `fmvtg[s][.]`/`fmvfg[s][.]`/`fcvt[s]tg[o][.]`/`fcvtfg[s][.]`:
253
254 ## `IT` -- Integer Type
255
256 | `IT` | Integer Type | Assembly Alias Mnemonic |
257 |------|-----------------|-------------------------|
258 | 0 | Signed 32-bit | `<op>w` |
259 | 1 | Unsigned 32-bit | `<op>uw` |
260 | 2 | Signed 64-bit | `<op>d` |
261 | 3 | Unsigned 64-bit | `<op>ud` |
262
263 ## `CVM` -- Float to Integer Conversion Mode
264
265 | `CVM` | `rounding_mode` | Semantics |
266 |-------|-----------------|----------------------------------|
267 | 000 | from `FPSCR` | [OpenPower semantics] |
268 | 001 | Truncate | [OpenPower semantics] |
269 | 010 | from `FPSCR` | [Java/Saturating semantics] |
270 | 011 | Truncate | [Java/Saturating semantics] |
271 | 100 | from `FPSCR` | [JavaScript semantics] |
272 | 101 | Truncate | [JavaScript semantics] |
273 | rest | -- | illegal instruction trap for now |
274
275 [OpenPower semantics]: #fp-to-int-openpower-conversion-semantics
276 [Java/Saturating semantics]: #fp-to-int-java-saturating-conversion-semantics
277 [JavaScript semantics]: #fp-to-int-javascript-conversion-semantics
278
279 # Moves
280
281 These instructions perform a straight unaltered bit-level copy from one Register
282 File to another.
283
284 ## Floating Move To GPR
285
286 ```
287 fmvtg RT, FRB
288 fmvtg. RT, FRB
289 ```
290
291 | 0-5 | 6-10 | 11-15 | 16-20 | 21-30 | 31 | Form |
292 |-----|------|-------|-------|-------|----|--------|
293 | PO | RT | 0 | FRB | XO | Rc | X-Form |
294
295 ```
296 RT <- (FRB)
297 ```
298
299 Move a 64-bit float from a FPR to a GPR, just copying bits of the IEEE 754
300 representation directly. This is equivalent to `stfd` followed by `ld`.
301 As `fmvtg` is just copying bits, `FPSCR` is not affected in any way.
302
303 Rc=1 tests RT and sets CR0, exactly like all other Scalar Fixed-Point
304 operations.
305
306 Special Registers altered:
307
308 ```
309 CR0 (if Rc=1)
310 ```
311
312 ----------
313
314 ## Floating Move To GPR Single
315
316 ```
317 fmvtgs RT, FRB
318 fmvtgs. RT, FRB
319 ```
320
321 | 0-5 | 6-10 | 11-15 | 16-20 | 21-30 | 31 | Form |
322 |-----|------|-------|-------|-------|----|--------|
323 | PO | RT | 0 | FRB | XO | Rc | X-Form |
324
325 ```
326 RT <- [0] * 32 || SINGLE((FRB)) # SINGLE since that's what stfs uses
327 ```
328
329 Move a 32-bit float from a FPR to a GPR, just copying bits of the IEEE 754
330 representation directly. This is equivalent to `stfs` followed by `lwz`.
331 As `fmvtgs` is just copying bits, `FPSCR` is not affected in any way.
332
333 Rc=1 tests RT and sets CR0, exactly like all other Scalar Fixed-Point
334 operations.
335
336 Special Registers altered:
337
338 ```
339 CR0 (if Rc=1)
340 ```
341
342 ----------
343
344 \newpage{}
345
346 ## Double-Precision Floating Move From GPR
347
348 ```
349 fmvfg FRT, RB
350 fmvfg. FRT, RB
351 ```
352
353 | 0-5 | 6-10 | 11-15 | 16-20 | 21-30 | 31 | Form |
354 |-----|------|-------|-------|-------|----|--------|
355 | PO | FRT | 0 | RB | XO | Rc | X-Form |
356
357 ```
358 FRT <- (RB)
359 ```
360
361 move a 64-bit float from a GPR to a FPR, just copying bits of the IEEE 754
362 representation directly. This is equivalent to `std` followed by `lfd`.
363 As `fmvfg` is just copying bits, `FPSCR` is not affected in any way.
364
365 Rc=1 tests FRT and sets CR1, exactly like all other Scalar Floating-Point
366 operations.
367
368 Special Registers altered:
369
370 ```
371 CR1 (if Rc=1)
372 ```
373
374 ----------
375
376 ## Floating Move From GPR Single
377
378 ```
379 fmvfgs FRT, RB
380 fmvfgs. FRT, RB
381 ```
382
383 | 0-5 | 6-10 | 11-15 | 16-20 | 21-30 | 31 | Form |
384 |-----|------|-------|-------|-------|----|--------|
385 | PO | FRT | 0 | RB | XO | Rc | X-Form |
386
387 ```
388 FRT <- DOUBLE((RB)[32:63]) # DOUBLE since that's what lfs uses
389 ```
390
391 move a 32-bit float from a GPR to a FPR, just copying bits of the IEEE 754
392 representation directly. This is equivalent to `stw` followed by `lfs`.
393 As `fmvfgs` is just copying bits, `FPSCR` is not affected in any way.
394
395 Rc=1 tests FRT and sets CR1, exactly like all other Scalar Floating-Point
396 operations.
397
398 Special Registers altered:
399
400 ```
401 CR1 (if Rc=1)
402 ```
403
404 # Conversions
405
406 Unlike the move instructions
407 these instructions perform conversions between Integer and
408 Floating Point. Truncation can therefore occur, as well
409 as exceptions.
410
411 ## Double-Precision Floating Convert From Integer In GPR
412
413 ```
414 fcvtfg FRT, RB, IT
415 fcvtfg. FRT, RB, IT
416 ```
417
418 | 0-5 | 6-10 | 11-12 | 13-15 | 16-20 | 21-30 | 31 | Form |
419 |-----|------|-------|-------|-------|-------|----|--------|
420 | PO | FRT | IT | 0 | RB | XO | Rc | X-Form |
421
422 ```
423 if IT[0] = 0 then # 32-bit int -> 64-bit float
424 # rounding never necessary, so don't touch FPSCR
425 # based off xvcvsxwdp
426 if IT = 0 then # Signed 32-bit
427 src <- bfp_CONVERT_FROM_SI32((RB)[32:63])
428 else # IT = 1 -- Unsigned 32-bit
429 src <- bfp_CONVERT_FROM_UI32((RB)[32:63])
430 FRT <- bfp64_CONVERT_FROM_BFP(src)
431 else
432 # rounding may be necessary. based off xscvuxdsp
433 reset_xflags()
434 switch(IT)
435 case(0): # Signed 32-bit
436 src <- bfp_CONVERT_FROM_SI32((RB)[32:63])
437 case(1): # Unsigned 32-bit
438 src <- bfp_CONVERT_FROM_UI32((RB)[32:63])
439 case(2): # Signed 64-bit
440 src <- bfp_CONVERT_FROM_SI64((RB))
441 default: # Unsigned 64-bit
442 src <- bfp_CONVERT_FROM_UI64((RB))
443 rnd <- bfp_ROUND_TO_BFP64(FPSCR.RN, src)
444 result <- bfp64_CONVERT_FROM_BFP(rnd)
445 cls <- fprf_CLASS_BFP64(result)
446
447 if xx_flag = 1 then SetFX(FPSCR.XX)
448
449 FRT <- result
450 FPSCR.FPRF <- cls
451 FPSCR.FR <- inc_flag
452 FPSCR.FI <- xx_flag
453 ```
454 <!-- note the PowerISA spec. explicitly has empty lines before/after SetFX,
455 don't remove them -->
456
457 Convert from a unsigned/signed 32/64-bit integer in RB to a 64-bit
458 float in FRT.
459
460 If converting from a unsigned/signed 32-bit integer to a 64-bit float,
461 rounding is never necessary, so `FPSCR` is unmodified and exceptions are
462 never raised. Otherwise, `FPSCR` is modified and exceptions are raised
463 as usual.
464
465 Rc=1 tests FRT and sets CR1, exactly like all other Scalar Floating-Point
466 operations.
467
468 Special Registers altered:
469
470 ```
471 CR1 (if Rc=1)
472 FPCSR (TODO: which bits?) (if IT[0]=1)
473 ```
474
475 ### Assembly Aliases
476
477 | Assembly Alias | Full Instruction |&nbsp;| Assembly Alias | Full Instruction |
478 |----------------------|----------------------|------|----------------------|----------------------|
479 | `fcvtfgw FRT, RB` | `fcvtfg FRT, RB, 0` |&nbsp;| `fcvtfgd FRT, RB` | `fcvtfg FRT, RB, 2` |
480 | `fcvtfgw. FRT, RB` | `fcvtfg. FRT, RB, 0` |&nbsp;| `fcvtfgd. FRT, RB` | `fcvtfg. FRT, RB, 2` |
481 | `fcvtfguw FRT, RB` | `fcvtfg FRT, RB, 1` |&nbsp;| `fcvtfgud FRT, RB` | `fcvtfg FRT, RB, 3` |
482 | `fcvtfguw. FRT, RB` | `fcvtfg. FRT, RB, 1` |&nbsp;| `fcvtfgud. FRT, RB` | `fcvtfg. FRT, RB, 3` |
483
484 ## Floating Convert From Integer In GPR Single
485
486 ```
487 fcvtfgs FRT, RB, IT
488 fcvtfgs. FRT, RB, IT
489 ```
490
491 | 0-5 | 6-10 | 11-12 | 13-15 | 16-20 | 21-30 | 31 | Form |
492 |-----|------|-------|-------|-------|-------|----|--------|
493 | PO | FRT | IT | 0 | RB | XO | Rc | X-Form |
494
495 ```
496 # rounding may be necessary. based off xscvuxdsp
497 reset_xflags()
498 switch(IT)
499 case(0): # Signed 32-bit
500 src <- bfp_CONVERT_FROM_SI32((RB)[32:63])
501 case(1): # Unsigned 32-bit
502 src <- bfp_CONVERT_FROM_UI32((RB)[32:63])
503 case(2): # Signed 64-bit
504 src <- bfp_CONVERT_FROM_SI64((RB))
505 default: # Unsigned 64-bit
506 src <- bfp_CONVERT_FROM_UI64((RB))
507 rnd <- bfp_ROUND_TO_BFP32(FPSCR.RN, src)
508 result32 <- bfp32_CONVERT_FROM_BFP(rnd)
509 cls <- fprf_CLASS_BFP32(result32)
510 result <- DOUBLE(result32)
511
512 if xx_flag = 1 then SetFX(FPSCR.XX)
513
514 FRT <- result
515 FPSCR.FPRF <- cls
516 FPSCR.FR <- inc_flag
517 FPSCR.FI <- xx_flag
518 ```
519 <!-- note the PowerISA spec. explicitly has empty lines before/after SetFX,
520 don't remove them -->
521
522 Convert from a unsigned/signed 32/64-bit integer in RB to a 32-bit
523 float in FRT, following the usual 32-bit float in 64-bit float format.
524 `FPSCR` is modified and exceptions are raised as usual.
525
526 Rc=1 tests FRT and sets CR1, exactly like all other Scalar Floating-Point
527 operations.
528
529 Special Registers altered:
530
531 ```
532 CR1 (if Rc=1)
533 FPCSR (TODO: which bits?)
534 ```
535
536 ### Assembly Aliases
537
538 | Assembly Alias | Full Instruction |&nbsp;| Assembly Alias | Full Instruction |
539 |----------------------|----------------------|------|----------------------|----------------------|
540 | `fcvtfgws FRT, RB` | `fcvtfg FRT, RB, 0` |&nbsp;| `fcvtfgds FRT, RB` | `fcvtfg FRT, RB, 2` |
541 | `fcvtfgws. FRT, RB` | `fcvtfg. FRT, RB, 0` |&nbsp;| `fcvtfgds. FRT, RB` | `fcvtfg. FRT, RB, 2` |
542 | `fcvtfguws FRT, RB` | `fcvtfg FRT, RB, 1` |&nbsp;| `fcvtfguds FRT, RB` | `fcvtfg FRT, RB, 3` |
543 | `fcvtfguws. FRT, RB` | `fcvtfg. FRT, RB, 1` |&nbsp;| `fcvtfguds. FRT, RB` | `fcvtfg. FRT, RB, 3` |
544
545 ## Floating-point to Integer Conversion Overview
546
547 <div id="fpr-to-gpr-conversion-mode"></div>
548
549 IEEE 754 doesn't specify what results are obtained when converting a NaN
550 or out-of-range floating-point value to integer, so different programming
551 languages and ISAs have made different choices. Below is an overview
552 of the different variants, listing the languages and hardware that
553 implements each variant.
554
555 For convenience, we will give those different conversion semantics names
556 based on which common ISA or programming language uses them, since there
557 may not be an established name for them:
558
559 **Standard OpenPower conversion**
560
561 This conversion performs "saturation with NaN converted to minimum
562 valid integer". This is also exactly the same as the x86 ISA conversion
563 semantics. OpenPOWER however has instructions for both:
564
565 * rounding mode read from FPSCR
566 * rounding mode always set to truncate
567
568 **Java/Saturating conversion**
569
570 For the sake of simplicity, the FP -> Integer conversion semantics
571 generalized from those used by Java's semantics (and Rust's `as`
572 operator) will be referred to as [Java/Saturating conversion
573 semantics](#fp-to-int-java-saturating-conversion-semantics).
574
575 Those same semantics are used in some way by all of the following
576 languages (not necessarily for the default conversion method):
577
578 * Java's
579 [FP -> Integer conversion](https://docs.oracle.com/javase/specs/jls/se16/html/jls-5.html#jls-5.1.3)
580 (only for long/int results)
581 * Rust's FP -> Integer conversion using the
582 [`as` operator](https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics)
583 * LLVM's
584 [`llvm.fptosi.sat`](https://llvm.org/docs/LangRef.html#llvm-fptosi-sat-intrinsic) and
585 [`llvm.fptoui.sat`](https://llvm.org/docs/LangRef.html#llvm-fptoui-sat-intrinsic) intrinsics
586 * SPIR-V's OpenCL dialect's
587 [`OpConvertFToU`](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpConvertFToU) and
588 [`OpConvertFToS`](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpConvertFToS)
589 instructions when decorated with
590 [the `SaturatedConversion` decorator](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_a_id_decoration_a_decoration).
591 * WebAssembly has also introduced
592 [trunc_sat_u](ttps://webassembly.github.io/spec/core/exec/numerics.html#op-trunc-sat-u) and
593 [trunc_sat_s](https://webassembly.github.io/spec/core/exec/numerics.html#op-trunc-sat-s)
594
595 **JavaScript conversion**
596
597 For the sake of simplicity, the FP -> Integer conversion
598 semantics generalized from those used by JavaScripts's `ToInt32`
599 abstract operation will be referred to as [JavaScript conversion
600 semantics](#fp-to-int-javascript-conversion-semantics).
601
602 This instruction is present in ARM assembler as FJCVTZS
603 <https://developer.arm.com/documentation/dui0801/g/hko1477562192868>
604
605 **Rc=1 and OE=1**
606
607 All of these instructions have an Rc=1 mode which sets CR0
608 in the normal way for any instructions producing a GPR result.
609 Additionally, when OE=1, if the numerical value of the FP number
610 is not 100% accurately preserved (due to truncation or saturation
611 and including when the FP number was NaN) then this is considered
612 to be an integer Overflow condition, and CR0.SO, XER.SO and XER.OV
613 are all set as normal for any GPR instructions that overflow.
614
615 ### FP to Integer Conversion Simplified Pseudo-code
616
617 Key for pseudo-code:
618
619 | term | result type | definition |
620 |---------------------------|-------------|----------------------------------------------------------------------------------------------------|
621 | `fp` | -- | `f32` or `f64` (or other types from SimpleV) |
622 | `int` | -- | `u32`/`u64`/`i32`/`i64` (or other types from SimpleV) |
623 | `uint` | -- | the unsigned integer of the same bit-width as `int` |
624 | `int::BITS` | `int` | the bit-width of `int` |
625 | `uint::MIN_VALUE` | `uint` | the minimum value `uint` can store: `0` |
626 | `uint::MAX_VALUE` | `uint` | the maximum value `uint` can store: `2^int::BITS - 1` |
627 | `int::MIN_VALUE` | `int` | the minimum value `int` can store : `-2^(int::BITS-1)` |
628 | `int::MAX_VALUE` | `int` | the maximum value `int` can store : `2^(int::BITS-1) - 1` |
629 | `int::VALUE_COUNT` | Integer | the number of different values `int` can store (`2^int::BITS`). too big to fit in `int`. |
630 | `rint(fp, rounding_mode)` | `fp` | rounds the floating-point value `fp` to an integer according to rounding mode `rounding_mode` |
631
632 <div id="fp-to-int-openpower-conversion-semantics"></div>
633 OpenPower conversion semantics (section A.2 page 1009 (page 1035) of
634 Power ISA v3.1B):
635
636 ```
637 def fp_to_int_open_power<fp, int>(v: fp) -> int:
638 if v is NaN:
639 return int::MIN_VALUE
640 if v >= int::MAX_VALUE:
641 return int::MAX_VALUE
642 if v <= int::MIN_VALUE:
643 return int::MIN_VALUE
644 return (int)rint(v, rounding_mode)
645 ```
646
647 <div id="fp-to-int-java-saturating-conversion-semantics"></div>
648 [Java/Saturating conversion semantics](https://docs.oracle.com/javase/specs/jls/se16/html/jls-5.html#jls-5.1.3)
649 (only for long/int results)
650 (with adjustment to add non-truncate rounding modes):
651
652 ```
653 def fp_to_int_java_saturating<fp, int>(v: fp) -> int:
654 if v is NaN:
655 return 0
656 if v >= int::MAX_VALUE:
657 return int::MAX_VALUE
658 if v <= int::MIN_VALUE:
659 return int::MIN_VALUE
660 return (int)rint(v, rounding_mode)
661 ```
662
663 <div id="fp-to-int-javascript-conversion-semantics"></div>
664 Section 7.1 of the ECMAScript / JavaScript
665 [conversion semantics](https://262.ecma-international.org/11.0/#sec-toint32)
666 (with adjustment to add non-truncate rounding modes):
667
668 ```
669 def fp_to_int_java_script<fp, int>(v: fp) -> int:
670 if v is NaN or infinite:
671 return 0
672 v = rint(v, rounding_mode) # assume no loss of precision in result
673 v = v mod int::VALUE_COUNT # 2^32 for i32, 2^64 for i64, result is non-negative
674 bits = (uint)v
675 return (int)bits
676 ```
677
678 ## Double-Precision Floating Convert To Integer In GPR
679
680 ```
681 fcvttg RT, FRB, CVM, IT
682 fcvttg. RT, FRB, CVM, IT
683 fcvttgo RT, FRB, CVM, IT
684 fcvttgo. RT, FRB, CVM, IT
685 ```
686
687 | 0-5 | 6-10 | 11-12 | 13-15 | 16-20 | 21 | 22-30 | 31 | Form |
688 |-----|------|-------|-------|-------|----|-------|----|---------|
689 | PO | RT | IT | CVM | FRB | OE | XO | Rc | XO-Form |
690
691 ```
692 # based on xscvdpuxws
693 reset_xflags()
694 src <- bfp_CONVERT_FROM_BFP64((FRB))
695
696 switch(IT)
697 case(0): # Signed 32-bit
698 range_min <- bfp_CONVERT_FROM_SI32(0x8000_0000)
699 range_max <- bfp_CONVERT_FROM_SI32(0x7FFF_FFFF)
700 js_mask <- 0xFFFF_FFFF
701 case(1): # Unsigned 32-bit
702 range_min <- bfp_CONVERT_FROM_UI32(0)
703 range_max <- bfp_CONVERT_FROM_UI32(0xFFFF_FFFF)
704 js_mask <- 0xFFFF_FFFF
705 case(2): # Signed 64-bit
706 range_min <- bfp_CONVERT_FROM_SI64(-0x8000_0000_0000_0000)
707 range_max <- bfp_CONVERT_FROM_SI64(0x7FFF_FFFF_FFFF_FFFF)
708 js_mask <- 0xFFFF_FFFF_FFFF_FFFF
709 default: # Unsigned 64-bit
710 range_min <- bfp_CONVERT_FROM_UI64(0)
711 range_max <- bfp_CONVERT_FROM_UI64(0xFFFF_FFFF_FFFF_FFFF)
712 js_mask <- 0xFFFF_FFFF_FFFF_FFFF
713
714 if CVM[2] = 1 or FPSCR.RN = 0b01 then
715 rnd <- bfp_ROUND_TO_INTEGER_TRUNC(src)
716 else if FPSCR.RN = 0b00 then
717 rnd <- bfp_ROUND_TO_INTEGER_NEAR_EVEN(src)
718 else if FPSCR.RN = 0b10 then
719 rnd <- bfp_ROUND_TO_INTEGER_CEIL(src)
720 else if FPSCR.RN = 0b11 then
721 rnd <- bfp_ROUND_TO_INTEGER_FLOOR(src)
722
723 switch(CVM)
724 case(0, 1): # OpenPower semantics
725 if IsNaN(rnd) then
726 result <- si64_CONVERT_FROM_BFP(range_min)
727 else if bfp_COMPARE_GT(rnd, range_max) then
728 result <- ui64_CONVERT_FROM_BFP(range_max)
729 else if bfp_COMPARE_LT(rnd, range_min) then
730 result <- si64_CONVERT_FROM_BFP(range_min)
731 else if IT[1] = 1 then # Unsigned 32/64-bit
732 result <- ui64_CONVERT_FROM_BFP(range_max)
733 else # Signed 32/64-bit
734 result <- si64_CONVERT_FROM_BFP(range_max)
735 case(2, 3): # Java/Saturating semantics
736 if IsNaN(rnd) then
737 result <- [0] * 64
738 else if bfp_COMPARE_GT(rnd, range_max) then
739 result <- ui64_CONVERT_FROM_BFP(range_max)
740 else if bfp_COMPARE_LT(rnd, range_min) then
741 result <- si64_CONVERT_FROM_BFP(range_min)
742 else if IT[1] = 1 then # Unsigned 32/64-bit
743 result <- ui64_CONVERT_FROM_BFP(range_max)
744 else # Signed 32/64-bit
745 result <- si64_CONVERT_FROM_BFP(range_max)
746 default: # JavaScript semantics
747 # CVM = 6, 7 are illegal instructions
748 # this works because the largest type we try to convert from has
749 # 53 significand bits, and the largest type we try to convert to
750 # has 64 bits, and the sum of those is strictly less than the 128
751 # bits of the intermediate result.
752 limit <- bfp_CONVERT_FROM_UI128([1] * 128)
753 if IsInf(rnd) or IsNaN(rnd) then
754 result <- [0] * 64
755 else if bfp_COMPARE_GT(bfp_ABSOLUTE(rnd), limit) then
756 result <- [0] * 64
757 else
758 result128 <- si128_CONVERT_FROM_BFP(rnd)
759 result <- result128[64:127] & js_mask
760
761 switch(IT)
762 case(0): # Signed 32-bit
763 result <- EXTS64(result[32:63])
764 result_bfp <- bfp_CONVERT_FROM_SI32(result[32:63])
765 case(1): # Unsigned 32-bit
766 result <- EXTZ64(result[32:63])
767 result_bfp <- bfp_CONVERT_FROM_UI32(result[32:63])
768 case(2): # Signed 64-bit
769 result_bfp <- bfp_CONVERT_FROM_SI64(result)
770 default: # Unsigned 64-bit
771 result_bfp <- bfp_CONVERT_FROM_UI64(result)
772
773 if vxsnan_flag = 1 then SetFX(FPSCR.VXSNAN)
774 if vxcvi_flag = 1 then SetFX(FPSCR.VXCVI)
775 if xx_flag = 1 then SetFX(FPSCR.XX)
776
777 vx_flag <- vxsnan_flag | vxcvi_flag
778 vex_flag <- FPSCR.VE & vx_flag
779
780 if vex_flag = 0 then
781 RT <- result
782 FPSCR.FPRF <- undefined
783 FPSCR.FR <- inc_flag
784 FPSCR.FI <- xx_flag
785 if IsNaN(src) or not bfp_COMPARE_EQ(src, result_bfp) then
786 overflow <- 1 # signals SO only when OE = 1
787 else
788 FPSCR.FR <- 0
789 FPSCR.FI <- 0
790 ```
791
792 Convert from 64-bit float in FRB to a unsigned/signed 32/64-bit integer
793 in RT, with the conversion overflow/rounding semantics following the
794 chosen `CVM` value. `FPSCR` is modified and exceptions are raised as usual.
795
796 These instructions have an Rc=1 mode which sets CR0 in the normal
797 way for any instructions producing a GPR result. Additionally, when OE=1,
798 if the numerical value of the FP number is not 100% accurately preserved
799 (due to truncation or saturation and including when the FP number was
800 NaN) then this is considered to be an Integer Overflow condition, and
801 CR0.SO, XER.SO and XER.OV are all set as normal for any GPR instructions
802 that overflow.
803
804 Special Registers altered:
805
806 ```
807 CR0 (if Rc=1)
808 XER SO, OV, OV32 (if OE=1)
809 FPCSR (TODO: which bits?)
810 ```
811
812 ### Assembly Aliases
813
814 | Assembly Alias | Full Instruction | Assembly Alias | Full Instruction |
815 |---------------------------|----------------------------|---------------------------|----------------------------|
816 | `fcvttgw RT, FRB, CVM` | `fcvttg RT, FRB, CVM, 0` | `fcvttgd RT, FRB, CVM` | `fcvttg RT, FRB, CVM, 2` |
817 | `fcvttgw. RT, FRB, CVM` | `fcvttg. RT, FRB, CVM, 0` | `fcvttgd. RT, FRB, CVM` | `fcvttg. RT, FRB, CVM, 2` |
818 | `fcvttgwo RT, FRB, CVM` | `fcvttgo RT, FRB, CVM, 0` | `fcvttgdo RT, FRB, CVM` | `fcvttgo RT, FRB, CVM, 2` |
819 | `fcvttgwo. RT, FRB, CVM` | `fcvttgo. RT, FRB, CVM, 0` | `fcvttgdo. RT, FRB, CVM` | `fcvttgo. RT, FRB, CVM, 2` |
820 | `fcvttguw RT, FRB, CVM` | `fcvttg RT, FRB, CVM, 1` | `fcvttgud RT, FRB, CVM` | `fcvttg RT, FRB, CVM, 3` |
821 | `fcvttguw. RT, FRB, CVM` | `fcvttg. RT, FRB, CVM, 1` | `fcvttgud. RT, FRB, CVM` | `fcvttg. RT, FRB, CVM, 3` |
822 | `fcvttguwo RT, FRB, CVM` | `fcvttgo RT, FRB, CVM, 1` | `fcvttgudo RT, FRB, CVM` | `fcvttgo RT, FRB, CVM, 3` |
823 | `fcvttguwo. RT, FRB, CVM` | `fcvttgo. RT, FRB, CVM, 1` | `fcvttgudo. RT, FRB, CVM` | `fcvttgo. RT, FRB, CVM, 3` |
824
825 ## Floating Convert Single To Integer In GPR
826
827 ```
828 fcvtstg RT, FRB, CVM, IT
829 fcvtstg. RT, FRB, CVM, IT
830 fcvtstgo RT, FRB, CVM, IT
831 fcvtstgo. RT, FRB, CVM, IT
832 ```
833
834 | 0-5 | 6-10 | 11-12 | 13-15 | 16-20 | 21 | 22-30 | 31 | Form |
835 |-----|------|-------|-------|-------|----|-------|----|---------|
836 | PO | RT | IT | CVM | FRB | OE | XO | Rc | XO-Form |
837
838 ```
839 # based on xscvdpuxws
840 reset_xflags()
841 src <- bfp_CONVERT_FROM_BFP32(SINGLE((FRB)))
842
843 switch(IT)
844 case(0): # Signed 32-bit
845 range_min <- bfp_CONVERT_FROM_SI32(0x8000_0000)
846 range_max <- bfp_CONVERT_FROM_SI32(0x7FFF_FFFF)
847 js_mask <- 0xFFFF_FFFF
848 case(1): # Unsigned 32-bit
849 range_min <- bfp_CONVERT_FROM_UI32(0)
850 range_max <- bfp_CONVERT_FROM_UI32(0xFFFF_FFFF)
851 js_mask <- 0xFFFF_FFFF
852 case(2): # Signed 64-bit
853 range_min <- bfp_CONVERT_FROM_SI64(-0x8000_0000_0000_0000)
854 range_max <- bfp_CONVERT_FROM_SI64(0x7FFF_FFFF_FFFF_FFFF)
855 js_mask <- 0xFFFF_FFFF_FFFF_FFFF
856 default: # Unsigned 64-bit
857 range_min <- bfp_CONVERT_FROM_UI64(0)
858 range_max <- bfp_CONVERT_FROM_UI64(0xFFFF_FFFF_FFFF_FFFF)
859 js_mask <- 0xFFFF_FFFF_FFFF_FFFF
860
861 if CVM[2] = 1 or FPSCR.RN = 0b01 then
862 rnd <- bfp_ROUND_TO_INTEGER_TRUNC(src)
863 else if FPSCR.RN = 0b00 then
864 rnd <- bfp_ROUND_TO_INTEGER_NEAR_EVEN(src)
865 else if FPSCR.RN = 0b10 then
866 rnd <- bfp_ROUND_TO_INTEGER_CEIL(src)
867 else if FPSCR.RN = 0b11 then
868 rnd <- bfp_ROUND_TO_INTEGER_FLOOR(src)
869
870 switch(CVM)
871 case(0, 1): # OpenPower semantics
872 if IsNaN(rnd) then
873 result <- si64_CONVERT_FROM_BFP(range_min)
874 else if bfp_COMPARE_GT(rnd, range_max) then
875 result <- ui64_CONVERT_FROM_BFP(range_max)
876 else if bfp_COMPARE_LT(rnd, range_min) then
877 result <- si64_CONVERT_FROM_BFP(range_min)
878 else if IT[1] = 1 then # Unsigned 32/64-bit
879 result <- ui64_CONVERT_FROM_BFP(range_max)
880 else # Signed 32/64-bit
881 result <- si64_CONVERT_FROM_BFP(range_max)
882 case(2, 3): # Java/Saturating semantics
883 if IsNaN(rnd) then
884 result <- [0] * 64
885 else if bfp_COMPARE_GT(rnd, range_max) then
886 result <- ui64_CONVERT_FROM_BFP(range_max)
887 else if bfp_COMPARE_LT(rnd, range_min) then
888 result <- si64_CONVERT_FROM_BFP(range_min)
889 else if IT[1] = 1 then # Unsigned 32/64-bit
890 result <- ui64_CONVERT_FROM_BFP(range_max)
891 else # Signed 32/64-bit
892 result <- si64_CONVERT_FROM_BFP(range_max)
893 default: # JavaScript semantics
894 # CVM = 6, 7 are illegal instructions
895 # this works because the largest type we try to convert from has
896 # 53 significand bits, and the largest type we try to convert to
897 # has 64 bits, and the sum of those is strictly less than the 128
898 # bits of the intermediate result.
899 limit <- bfp_CONVERT_FROM_UI128([1] * 128)
900 if IsInf(rnd) or IsNaN(rnd) then
901 result <- [0] * 64
902 else if bfp_COMPARE_GT(bfp_ABSOLUTE(rnd), limit) then
903 result <- [0] * 64
904 else
905 result128 <- si128_CONVERT_FROM_BFP(rnd)
906 result <- result128[64:127] & js_mask
907
908 switch(IT)
909 case(0): # Signed 32-bit
910 result <- EXTS64(result[32:63])
911 result_bfp <- bfp_CONVERT_FROM_SI32(result[32:63])
912 case(1): # Unsigned 32-bit
913 result <- EXTZ64(result[32:63])
914 result_bfp <- bfp_CONVERT_FROM_UI32(result[32:63])
915 case(2): # Signed 64-bit
916 result_bfp <- bfp_CONVERT_FROM_SI64(result)
917 default: # Unsigned 64-bit
918 result_bfp <- bfp_CONVERT_FROM_UI64(result)
919
920 if vxsnan_flag = 1 then SetFX(FPSCR.VXSNAN)
921 if vxcvi_flag = 1 then SetFX(FPSCR.VXCVI)
922 if xx_flag = 1 then SetFX(FPSCR.XX)
923
924 vx_flag <- vxsnan_flag | vxcvi_flag
925 vex_flag <- FPSCR.VE & vx_flag
926
927 if vex_flag = 0 then
928 RT <- result
929 FPSCR.FPRF <- undefined
930 FPSCR.FR <- inc_flag
931 FPSCR.FI <- xx_flag
932 if IsNaN(src) or not bfp_COMPARE_EQ(src, result_bfp) then
933 overflow <- 1 # signals SO only when OE = 1
934 else
935 FPSCR.FR <- 0
936 FPSCR.FI <- 0
937 ```
938
939 Convert from 32-bit float in FRB to a unsigned/signed 32/64-bit integer
940 in RT, with the conversion overflow/rounding semantics following the
941 chosen `CVM` value, following the usual 32-bit float in 64-bit float
942 format. `FPSCR` is modified and exceptions are raised as usual.
943
944 These instructions have an Rc=1 mode which sets CR0 in the normal
945 way for any instructions producing a GPR result. Additionally, when OE=1,
946 if the numerical value of the FP number is not 100% accurately preserved
947 (due to truncation or saturation and including when the FP number was
948 NaN) then this is considered to be an Integer Overflow condition, and
949 CR0.SO, XER.SO and XER.OV are all set as normal for any GPR instructions
950 that overflow.
951
952 Special Registers altered:
953
954 ```
955 CR0 (if Rc=1)
956 XER SO, OV, OV32 (if OE=1)
957 FPCSR (TODO: which bits?)
958 ```
959
960 ### Assembly Aliases
961
962 | Assembly Alias | Full Instruction | Assembly Alias | Full Instruction |
963 |----------------------------|-----------------------------|----------------------------|-----------------------------|
964 | `fcvtstgw RT, FRB, CVM` | `fcvtstg RT, FRB, CVM, 0` | `fcvtstgd RT, FRB, CVM` | `fcvtstg RT, FRB, CVM, 2` |
965 | `fcvtstgw. RT, FRB, CVM` | `fcvtstg. RT, FRB, CVM, 0` | `fcvtstgd. RT, FRB, CVM` | `fcvtstg. RT, FRB, CVM, 2` |
966 | `fcvtstgwo RT, FRB, CVM` | `fcvtstgo RT, FRB, CVM, 0` | `fcvtstgdo RT, FRB, CVM` | `fcvtstgo RT, FRB, CVM, 2` |
967 | `fcvtstgwo. RT, FRB, CVM` | `fcvtstgo. RT, FRB, CVM, 0` | `fcvtstgdo. RT, FRB, CVM` | `fcvtstgo. RT, FRB, CVM, 2` |
968 | `fcvtstguw RT, FRB, CVM` | `fcvtstg RT, FRB, CVM, 1` | `fcvtstgud RT, FRB, CVM` | `fcvtstg RT, FRB, CVM, 3` |
969 | `fcvtstguw. RT, FRB, CVM` | `fcvtstg. RT, FRB, CVM, 1` | `fcvtstgud. RT, FRB, CVM` | `fcvtstg. RT, FRB, CVM, 3` |
970 | `fcvtstguwo RT, FRB, CVM` | `fcvtstgo RT, FRB, CVM, 1` | `fcvtstgudo RT, FRB, CVM` | `fcvtstgo RT, FRB, CVM, 3` |
971 | `fcvtstguwo. RT, FRB, CVM` | `fcvtstgo. RT, FRB, CVM, 1` | `fcvtstgudo. RT, FRB, CVM` | `fcvtstgo. RT, FRB, CVM, 3` |