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