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