-
Notifications
You must be signed in to change notification settings - Fork 454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix tests to reflect new NaN semantics #414
Conversation
interpreter/host/js.ml
Outdated
@@ -129,6 +129,14 @@ let harness = | |||
" throw new Error(\"Wasm return value NaN expected, got \" + actual);\n" ^ | |||
" };\n" ^ | |||
"}\n" ^ | |||
"function assert_return_canonical_nan(action) {\n" ^ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Insert one empty line before this
interpreter/host/run.ml
Outdated
@@ -365,6 +365,18 @@ let run_assertion ass = | |||
trace ("Asserting return..."); | |||
let got_vs = run_action act in | |||
let is_nan = | |||
(* Accept any NaN that's one everywhere pos_nan is one *) | |||
match got_vs with | |||
| [Values.F32 got_f32] -> (Int32.logand (F32.to_bits got_f32) (F32.to_bits F32.pos_nan)) = (F32.to_bits F32.pos_nan) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: break line at 80 characters. also, redundant parens around the args of =
interpreter/host/run.ml
Outdated
trace ("Asserting return..."); | ||
let got_vs = run_action act in | ||
let is_nan = | ||
(* Accept only the two canonical NaNs *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: instead of this comment, rename the variable to is_canoncial_nan
interpreter/host/run.ml
Outdated
(* Accept any NaN that's one everywhere pos_nan is one *) | ||
match got_vs with | ||
| [Values.F32 got_f32] -> (Int32.logand (F32.to_bits got_f32) (F32.to_bits F32.pos_nan)) = (F32.to_bits F32.pos_nan) | ||
| [Values.F64 got_f64] -> (Int64.logand (F64.to_bits got_f64) (F64.to_bits F64.pos_nan)) = (F64.to_bits F64.pos_nan) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
interpreter/host/js.ml
Outdated
[ GetLocal (var i) @@ at; | ||
GetLocal (var i) @@ at; | ||
Compare (eq_of t) @@ at; | ||
let test t = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous version was intended to work correctly for multiple return values. I believe that dropping the auxiliary locals will consume them in the wrong order, inconsistent with ts
. (Though instead of using locals you can probably reverse ts
.)
interpreter/host/js.ml
Outdated
BrIf (0l @@ at) @@ at ] | ||
in [], List.flatten (List.map test ts) | ||
|
||
let assert_return_canonical_nan ts at = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only difference between the two functions is the initial mask AFAICS. How about making that a parameter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interpreter changes look pretty good already. @sunfishcode and @gahaas may want to have a look at the test changes.
I like it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test changes look right to me.
interpreter/README.md
Outdated
@@ -276,6 +276,7 @@ action: | |||
assertion: | |||
( assert_return <action> <expr>* ) ;; assert action has expected results | |||
( assert_return_nan <action> ) ;; assert action results in NaN | |||
( assert_return_canonical_nan <action> ) ;; assert action results in NaN in canonical form |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps change "NaN in canonical form" to "NaN in a canonical form" to emphasize that there are multiple canonical forms.
interpreter/host/js.ml
Outdated
"function assert_return_canonical_nan(action) {\n" ^ | ||
" let actual = action();\n" ^ | ||
" // Note that JS can't reliably distinguish different NaN values,\n" ^ | ||
" // so there's no good way to test that it's a canonical NaN.\n" ^ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment would now also be relevant in the assert_return_nan
function above, since JS can't test that the most-significant bit of the fraction field is 1 either.
Regarding naming, I tend to agree that |
The current WebAssembly documentation currently avoids using the terms "quiet" or "signaling", because WebAssembly's "signaling" NaNs don't signal anything, and are only distinguishable from "quiet" NaNs in the semantics by their bit pattern. How about We indeed don't have a use for an assert that accepts any NaN under the current semantics. |
Thanks for the feedback all, I hope to find some more time to iterate this in the next couple of days. |
@sunfishcode, but 'quiet NaN' is the IEEE terminology for the values it is checking for. Does it get clearer if we invent our own name to mean the same thing? |
The current NaN rules are designed to accomodate signaling NaNs that actually signal, but I can't predict when this will be exercised. Until then, "WebAssembly doesn't care about signaling NaNs" is tempting as a sufficient and unambiguous mental model for most people, since there are some widespread misconceptions about what signaling NaN actually means. Also, the use of "arithmetic" in this very specific sense is not my invention. However, I don't feel strongly about it. My main interest here is in being consistent. If we're going to talk about "quiet" NaNs in the spec/design repos, we should probably define what it means somewhere and use it consistently. |
We might want to wait for the resolution of WebAssembly/design#976. Even the naming question may become moot then. |
Sounds like WebAssembly/design#976 was resolved, can this move forward? |
@binji, sgtm. Still needs comments to be addressed. |
I'll try to revisit the PR sometime this week. |
It looks like #417 has introduced the "arithmetic_nan" terminology, so I'm going to go ahead with:
|
OK, I've updated this and added some more tests for the behaviour of min/max. |
It's entirely possible that I mis-understood the terminology here - @sunfishcode can you clarify? |
For For |
OK, sounds good, I'll update to match. |
Updated based on naming feedback above. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Except for a few minor fixes that I just noticed, this PR now looks good to me.
test/core/float_exprs.wast
Outdated
(assert_return (invoke "f64.no_fold_gt_select" (f64.const nan) (f64.const 0.0)) (f64.const 0.0)) | ||
(assert_return (invoke "f64.no_fold_gt_select" (f64.const 0.0) (f64.const -0.0)) (f64.const -0.0)) | ||
(assert_return (invoke "f64.no_fold_gt_select" (f64.const -0.0) (f64.const 0.0)) (f64.const 0.0)) | ||
(assert_return (invoke "f64.no_fold_ge_select" (f64.const 0.0) (f64.const nan)) (f64.const nan)) | ||
(assert_return_canonical_nan (invoke "f64.no_fold_ge_select" (f64.const 0.0) (f64.const nan))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for not noticing this earlier, but all these tests involving select
can actually use plain assert_return
to test for a specific bit-pattern, because select
is defined to return one of its operands verbatim.
test/core/float_exprs.wast
Outdated
(assert_return (invoke "f64.no_fold_gt_if" (f64.const nan) (f64.const 0.0)) (f64.const 0.0)) | ||
(assert_return (invoke "f64.no_fold_gt_if" (f64.const 0.0) (f64.const -0.0)) (f64.const -0.0)) | ||
(assert_return (invoke "f64.no_fold_gt_if" (f64.const -0.0) (f64.const 0.0)) (f64.const 0.0)) | ||
(assert_return (invoke "f64.no_fold_ge_if" (f64.const 0.0) (f64.const nan)) (f64.const nan)) | ||
(assert_return_canonical_nan (invoke "f64.no_fold_ge_if" (f64.const 0.0) (f64.const nan))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly, these tests involving if
can use plain assert_return
because if
returns its value verbatim.
test/core/float_exprs.wast
Outdated
(assert_return (invoke "f64.no_fold_gt_select_to_abs" (f64.const 0.0)) (f64.const -0.0)) | ||
(assert_return (invoke "f64.no_fold_gt_select_to_abs" (f64.const -0.0)) (f64.const 0.0)) | ||
(assert_return (invoke "f64.no_fold_ge_select_to_abs" (f64.const nan:0x4000000000000)) (f64.const -nan:0x4000000000000)) | ||
(assert_return (invoke "f64.no_fold_ge_select_to_abs" (f64.const -nan)) (f64.const nan)) | ||
(assert_return_canonical_nan (invoke "f64.no_fold_ge_select_to_abs" (f64.const -nan))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above for these select
tests.
test/core/float_exprs.wast
Outdated
(assert_return (invoke "f64.no_fold_gt_if_to_abs" (f64.const 0.0)) (f64.const -0.0)) | ||
(assert_return (invoke "f64.no_fold_gt_if_to_abs" (f64.const -0.0)) (f64.const 0.0)) | ||
(assert_return (invoke "f64.no_fold_ge_if_to_abs" (f64.const nan:0x4000000000000)) (f64.const -nan:0x4000000000000)) | ||
(assert_return (invoke "f64.no_fold_ge_if_to_abs" (f64.const -nan)) (f64.const nan)) | ||
(assert_return_canonical_nan (invoke "f64.no_fold_ge_if_to_abs" (f64.const -nan))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above for these if
tests.
Thanks @sunfishcode, I've reverted the testcases you identified. |
Looks good to me, and the main changes have general approval above. Thanks for working on this @rfk! |
@rfk Have you joined the WebAssembly W3C Community Group? https://www.w3.org/community/webassembly/ |
4284e11
to
e92b0a9
Compare
Hmm, I thought I had, but I don't see myself in the list of participants there. I'll try again now... |
OK, I'm now successfully showing up in https://www.w3.org/community/webassembly/participants |
Thanks! |
https://bugs.webkit.org/show_bug.cgi?id=173287 <rdar://problem/32725975> Reviewed by Saam Barati. Import spec tests as of 31c641cc15f2aedbec2fa45a5185f68416df578b, with a few modifications so things work. Fix a bunch of bugs found through this process, and punt a few tests (which I marked as blocked by this bug). Fixes: Fix load / store alignment: r216908 erroneously implemented it as bit alignment instead of byte alignment. It was also missing memory-alignment.js despite it being in the ChangeLog, so add it too. This allows spec-test/align.wast.js to pass. Tables can be imported or in a section. There can be only one, but sections can be empty. An Elements section can exist if there's no Table, as long as it is also empty. Memories can be imported or in a section. There can be only one, but sections can be empty. A Data section can exist if there's no Memory, as long as it is also empty. Prototypes: stringify without .prototype. in the string. WebAssembly.Table.prototype.grow was plain wrong: it takes a delta parameter, not a final size, and throws a RangeError on failure, not a TypeError. Fix compile / instantiate so the reject the promise if given an argument of the wrong type (instead of failing instantly). Fix async on neuter test. Element section shouldn't affect any Table if any of the elements are out of bounds. We need to process it in two passes. Segment section shouldn't affect any Data if any of the segments are out of bounds. We need to process it in two passes. Empty data segments are valid, but only when there is no memory. Their index still gets validated, and has to be zero. Punts: Error messages with context, the test seems overly restrictive but this is minor. compile/instantiate/validate property descriptors. UTF-8 bugs. Temporarily disable NaN tests. We need to go back and implement the following semantics: WebAssembly/spec#414 This doesn't matter as much as getting all the other tests passing. Worth noting for NaNs: f64.no_fold_mul_one (also a NaN test) as well as no_fold_promote_demote (an interesting corner case which we get wrong). mul by one is (assert_return (invoke \"f64.no_fold_mul_one\" (i64.const 0x7ff4000000000000)) (i64.const 0x7ff8000000000000)) which means converting sNaN to qNaN, and promote/demote is (assert_return (invoke \"no_fold_promote_demote\" (i32.const 0x7fa00000)) (i32.const 0x7fc00000)) which is the same. I'm not sure why they're not allowed. JSTests: * wasm.yaml: * wasm/function-tests/i32-load8-s.js: * wasm/function-tests/memory-access-past-4gib.js: (const.op.of.WASM.opcodes): * wasm/function-tests/memory-alignment.js: Added. (const.op.of.WASM.opcodes): * wasm/function-tests/memory-section-and-import.js: * wasm/js-api/Module-compile.js: (async.testPromiseAPI): * wasm/js-api/dont-mmap-zero-byte-memory.js: (testMems): * wasm/js-api/element.js: (assert.throws.new.WebAssembly.Module.builder.WebAssembly): * wasm/js-api/neutered-inputs.js: (const.testFunction): Deleted. (const.testConstructor): Deleted. * wasm/js-api/table.js: (assert.throws.new.WebAssembly.Module.builder.WebAssembly): (new.WebAssembly.Module): (assert.throws): (assertBadTableImport): (assert.throws.WebAssembly.Table.prototype.grow): (assertBadTableInstance): Deleted. * wasm/js-api/test_Data.js: (DataSectionWithoutMemory): * wasm/spec-harness/index.js: (module): (uniqueTest): Deleted. (assert_invalid): Deleted. (assert_soft_invalid): Deleted. (register): Deleted. (call): Deleted. (get instance): Deleted. (exports): Deleted. (run): Deleted. (assert_unlinkable): Deleted. (assert_uninstantiable): Deleted. (assert_trap): Deleted. (try.f): Deleted. (catch): Deleted. (assert_exhaustion): Deleted. (assert_return): Deleted. (assert_return_nan): Deleted. * wasm/spec-harness/testharness.css: Removed. * wasm/spec-harness/testharness.js: Removed. * wasm/spec-harness/testharnessreport.js: Removed. * wasm/spec-harness/wasm-constants.js: (assertTraps): (assertWasmThrows): * wasm/spec-harness/wasm-module-builder.js: (Binary.prototype.emit_section): (Binary): (WasmFunctionBuilder.prototype.addBody): (WasmFunctionBuilder.prototype.end): (WasmFunctionBuilder): (WasmModuleBuilder.prototype.stringToBytes): (WasmModuleBuilder.prototype.addCustomSection): (WasmModuleBuilder.prototype.addFunctionTableInit): (WasmModuleBuilder.prototype.appendToTable): (WasmModuleBuilder.prototype.toArray): (WasmModuleBuilder.prototype.toBuffer): (WasmModuleBuilder.prototype.instantiate): (WasmModuleBuilder): * wasm/spec-tests/address.wast.js: * wasm/spec-tests/align.wast.js: Added. * wasm/spec-tests/binary.wast.js: * wasm/spec-tests/block.wast.js: * wasm/spec-tests/br.wast.js: * wasm/spec-tests/br_if.wast.js: * wasm/spec-tests/br_table.wast.js: * wasm/spec-tests/call.wast.js: * wasm/spec-tests/call_indirect.wast.js: * wasm/spec-tests/comments.wast.js: * wasm/spec-tests/const.wast.js: Added. * wasm/spec-tests/conversions.wast.js: Added. * wasm/spec-tests/custom_section.wast.js: * wasm/spec-tests/exports.wast.js: * wasm/spec-tests/f32.wast.js: Added. * wasm/spec-tests/f64.wast.js: Added. * wasm/spec-tests/fac.wast.js: * wasm/spec-tests/float_exprs.wast.js: Added. * wasm/spec-tests/float_misc.wast.js: Added. * wasm/spec-tests/func.wast.js: * wasm/spec-tests/globals.wast.js: * wasm/spec-tests/if.wast.js: * wasm/spec-tests/imports.wast.js: * wasm/spec-tests/inline-module.wast.js: Added. * wasm/spec-tests/jsapi.js: (testJSAPI.test): (testJSAPI): * wasm/spec-tests/labels.wast.js: * wasm/spec-tests/loop.wast.js: * wasm/spec-tests/memory.wast.js: * wasm/spec-tests/memory_trap.wast.js: Added. * wasm/spec-tests/names.wast.js: * wasm/spec-tests/nop.wast.js: * wasm/spec-tests/return.wast.js: * wasm/spec-tests/stack.wast.js: * wasm/spec-tests/token.wast.js: Added. * wasm/spec-tests/type.wast.js: Added. * wasm/spec-tests/typecheck.wast.js: * wasm/spec-tests/unreachable.wast.js: * wasm/spec-tests/unreached-invalid.wast.js: * wasm/spec-tests/unwind.wast.js: * wasm/spec-tests/utf8-custom-section-id.wast.js: Added. * wasm/spec-tests/utf8-import-field.wast.js: Added. * wasm/spec-tests/utf8-import-module.wast.js: Added. Source/JavaScriptCore: * wasm/WasmB3IRGenerator.cpp: * wasm/WasmFunctionParser.h: * wasm/WasmModuleParser.cpp: * wasm/WasmModuleParser.h: * wasm/WasmParser.h: (JSC::Wasm::Parser<SuccessType>::consumeUTF8String): * wasm/generateWasm.py: (memoryLog2Alignment): * wasm/js/JSWebAssemblyTable.cpp: (JSC::JSWebAssemblyTable::grow): * wasm/js/JSWebAssemblyTable.h: * wasm/js/WebAssemblyCompileErrorPrototype.cpp: * wasm/js/WebAssemblyInstancePrototype.cpp: * wasm/js/WebAssemblyLinkErrorPrototype.cpp: * wasm/js/WebAssemblyMemoryPrototype.cpp: * wasm/js/WebAssemblyModulePrototype.cpp: * wasm/js/WebAssemblyModuleRecord.cpp: (JSC::WebAssemblyModuleRecord::evaluate): * wasm/js/WebAssemblyPrototype.cpp: (JSC::webAssemblyCompileFunc): (JSC::resolve): (JSC::instantiate): (JSC::compileAndInstantiate): (JSC::webAssemblyInstantiateFunc): * wasm/js/WebAssemblyRuntimeErrorPrototype.cpp: * wasm/js/WebAssemblyTablePrototype.cpp: (JSC::webAssemblyTableProtoFuncGrow): git-svn-id: http://svn.webkit.org/repository/webkit/trunk@218216 268f45cc-cd09-0410-ab3c-d52691b4dbfc
Fix various places in the spec text and interpreter where binary encodings were inconsistent with those we decided on in WebAssembly#372.
Here's an initial attempt at #286, let me know if it looks like it's on the right track. AFAICT it works as-is, but I still plan to:
Think more about the naming here. Since asserting a canonical nan is the more common case, I wonder if
assert_return_nan()
should retain its existing semantics, and e.g.assert_return_nondet_nan
could be added that allows any quiet NaN.Add some additional tests such as those suggested in Implement the new NaN semantics #286 (comment)