Skip to content

Commit 408cdd8

Browse files
authored
Explain why NaN bitpatterns are nondeterministic. (WebAssembly#973)
* Explain why NaN bitpatterns are nondeterministic. NaN bits are a surprising thing to see in Nondeterminism.md, given how deterministic WebAssembly is otherwise. This PR adds some explanation to Rationale.md. This is meant to finish WebAssembly#619. * Clarify "should" by saying "(as opposed to shall)". * Mention globals and linear memories as ways that NaN bitpatterns can be observed. And clarify the wording in a few other places.
1 parent d3f76e9 commit 408cdd8

File tree

1 file changed

+56
-4
lines changed

1 file changed

+56
-4
lines changed

Rationale.md

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,56 @@ architectures we may revisit some of the design decisions:
344344
which that language doesn't care about, but which another language may want.
345345

346346

347-
## NaN bit pattern propagation
347+
## NaN bit-pattern nondeterminism.
348+
349+
NaNs produced by floating-point instructions in WebAssembly have
350+
nondeterministic bit patterns in most circumstances. The bit pattern of a NaN
351+
is not usually significant, however there are a few ways that it can be
352+
observed:
353+
- a `reinterpret` conversion to an integer type
354+
- a `store` to linear memory followed by a load with a different type or index
355+
- a NaN stored to an imported or exported global variable or linear memory may
356+
be observed by the outside environment
357+
- a NaN passed to a `call` or `call_indirect` to an imported function may
358+
be observed by the outside environment
359+
- a return value of an exported function may be observed by the outside
360+
environment
361+
- `copysign` can be used to copy the sign bit onto a non-NaN value, where
362+
it then be observed
363+
364+
The motivation for nondeterminism in NaN bit patterns is that popular platforms
365+
have differing behavior. IEEE 754-2008 makes some recommendations, but has few
366+
hard requirements in this area, and in practice there is significant divergence,
367+
for example:
368+
- When an instruction with no NaN inputs produces a NaN output, x86 produces
369+
a NaN with the sign bit set, while ARM and others produce a NaN with it
370+
unset.
371+
- When an instruction has multiple NaN inputs, x86 always returns the first
372+
NaN (converted to a quiet NaN if needed), while ARMv8 returns the first
373+
signaling NaN (converted to a quiet NaN) if one is present, and otherwise
374+
returns the first quiet NaN.
375+
- Some hardware architectures have found that returning one of the input NaNs
376+
has a cost, and prefer to return a NaN with a fixed bit pattern instead.
377+
- LLVM (used in some WebAssembly implementations) doesn't guarantee that it
378+
won't commute `fadd`, `fmul` and other instructions, so it's not possible
379+
to rely on the "first" NaN being preserved as such.
380+
- IEEE 754-2008 itself recommends architectures use NaN bits to provide
381+
architecture-specific debugging facilities.
382+
383+
IEEE 754-2008 6.2 says that instructions returning a NaN *should* return one of
384+
their input NaNs. In WebAssembly, implementations may do this, however they are
385+
not required to. Since IEEE 754-2008 states this as a "should" (as opposed to a
386+
"shall"), it isn't a requirement for IEEE 754-2008 conformance.
387+
388+
An alternative design would be to require engines to always "canonicalize"
389+
NaNs whenever their bits could be observed. This would eliminate the
390+
nondeterminism and provide slightly better portability, since it would hide
391+
hardware-specific NaN propagation behavior. However, it is theorized that this
392+
would add an unacceptable amount of overhead, and that the benefit is marginal
393+
since most programs are unaffected by this issue.
394+
395+
396+
## Support for NaN-boxing.
348397

349398
In general, WebAssembly's floating point instructions provide the guarantee that
350399
if all NaNs passed to an instruction are "canonical", the result is "canonical",
@@ -356,11 +405,14 @@ NaN-boxing, because they don't have to canonicalize the output of an arithmetic
356405
instruction if they know the inputs are canonical.
357406

358407
When one or more of the inputs of an instruction are non-canonical NaNs, the
359-
resulting NaN bit pattern is nondeterministic. This is intended to accomodate
408+
resulting NaN bit pattern is nondeterministic. This is intended to accommodate
360409
the diversity in NaN behavior among popular hardware architectures.
361410

362-
The sign bit of generated NaNs is always nondeterministic since x86 generates
363-
NaNs with it set to 1 while other architectures generate NaNs with it set to 0.
411+
Note that the sign bit is still nondeterministic in a canonical NaN. This is
412+
also to accommodate popular hardware architectures; for example, x86 generates
413+
NaNs with the sign bit set to 1 while other architectures generate NaNs with it
414+
set to 0. And as above, the cost of canonicalizing NaNs is believed to be
415+
greater than the benefit.
364416

365417
NaNs generated by JS or other entities in the external environment are not
366418
required to be canonical, so exported function arguments, imported function

0 commit comments

Comments
 (0)