diff options
| author | Dan Gohman <sunfish@mozilla.com> | 2017-02-03 06:34:37 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-02-03 06:34:37 -0800 |
| commit | 408cdd8b8bfabfe158df22969da2b15c1e05afec (patch) | |
| tree | 56ffdecb00811ac20363dac3235194d9a671e7fc | |
| parent | d3f76e97edea4ccb15829ebab03b85d7928e09d9 (diff) | |
| download | nanowasm-design-408cdd8b8bfabfe158df22969da2b15c1e05afec.tar.gz | |
Explain why NaN bitpatterns are nondeterministic. (#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 https://github.com/WebAssembly/design/issues/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.
| -rw-r--r-- | Rationale.md | 60 |
1 files changed, 56 insertions, 4 deletions
diff --git a/Rationale.md b/Rationale.md index 040e712..779da1f 100644 --- a/Rationale.md +++ b/Rationale.md @@ -344,7 +344,56 @@ architectures we may revisit some of the design decisions: which that language doesn't care about, but which another language may want. -## NaN bit pattern propagation +## NaN bit-pattern nondeterminism. + +NaNs produced by floating-point instructions in WebAssembly have +nondeterministic bit patterns in most circumstances. The bit pattern of a NaN +is not usually significant, however there are a few ways that it can be +observed: + - a `reinterpret` conversion to an integer type + - a `store` to linear memory followed by a load with a different type or index + - a NaN stored to an imported or exported global variable or linear memory may + be observed by the outside environment + - a NaN passed to a `call` or `call_indirect` to an imported function may + be observed by the outside environment + - a return value of an exported function may be observed by the outside + environment + - `copysign` can be used to copy the sign bit onto a non-NaN value, where + it then be observed + +The motivation for nondeterminism in NaN bit patterns is that popular platforms +have differing behavior. IEEE 754-2008 makes some recommendations, but has few +hard requirements in this area, and in practice there is significant divergence, +for example: + - When an instruction with no NaN inputs produces a NaN output, x86 produces + a NaN with the sign bit set, while ARM and others produce a NaN with it + unset. + - When an instruction has multiple NaN inputs, x86 always returns the first + NaN (converted to a quiet NaN if needed), while ARMv8 returns the first + signaling NaN (converted to a quiet NaN) if one is present, and otherwise + returns the first quiet NaN. + - Some hardware architectures have found that returning one of the input NaNs + has a cost, and prefer to return a NaN with a fixed bit pattern instead. + - LLVM (used in some WebAssembly implementations) doesn't guarantee that it + won't commute `fadd`, `fmul` and other instructions, so it's not possible + to rely on the "first" NaN being preserved as such. + - IEEE 754-2008 itself recommends architectures use NaN bits to provide + architecture-specific debugging facilities. + +IEEE 754-2008 6.2 says that instructions returning a NaN *should* return one of +their input NaNs. In WebAssembly, implementations may do this, however they are +not required to. Since IEEE 754-2008 states this as a "should" (as opposed to a +"shall"), it isn't a requirement for IEEE 754-2008 conformance. + +An alternative design would be to require engines to always "canonicalize" +NaNs whenever their bits could be observed. This would eliminate the +nondeterminism and provide slightly better portability, since it would hide +hardware-specific NaN propagation behavior. However, it is theorized that this +would add an unacceptable amount of overhead, and that the benefit is marginal +since most programs are unaffected by this issue. + + +## Support for NaN-boxing. In general, WebAssembly's floating point instructions provide the guarantee that 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 instruction if they know the inputs are canonical. When one or more of the inputs of an instruction are non-canonical NaNs, the -resulting NaN bit pattern is nondeterministic. This is intended to accomodate +resulting NaN bit pattern is nondeterministic. This is intended to accommodate the diversity in NaN behavior among popular hardware architectures. -The sign bit of generated NaNs is always nondeterministic since x86 generates -NaNs with it set to 1 while other architectures generate NaNs with it set to 0. +Note that the sign bit is still nondeterministic in a canonical NaN. This is +also to accommodate popular hardware architectures; for example, x86 generates +NaNs with the sign bit set to 1 while other architectures generate NaNs with it +set to 0. And as above, the cost of canonicalizing NaNs is believed to be +greater than the benefit. NaNs generated by JS or other entities in the external environment are not required to be canonical, so exported function arguments, imported function |
