Skip to content
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

Merged
merged 2 commits into from
Mar 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion interpreter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,8 @@ 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 a canonical form
( assert_return_arithmetic_nan <action> ) ;; assert action results in NaN with 1 in MSB of fraction field
( assert_trap <action> <failure> ) ;; assert action traps with given failure string
( assert_malformed <module> <failure> ) ;; assert module cannot be decoded with given failure string
( assert_invalid <module> <failure> ) ;; assert module is invalid with given failure string
Expand Down
57 changes: 46 additions & 11 deletions interpreter/host/js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,18 @@ let harness =
" };\n" ^
"}\n" ^
"\n" ^
"function assert_return_nan(action) {\n" ^
"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" ^
" if (!Number.isNaN(actual)) {\n" ^
" throw new Error(\"Wasm return value NaN expected, got \" + actual);\n" ^
" };\n" ^
"}\n" ^
"\n" ^
"function assert_return_arithmetic_nan(action) {\n" ^
" // Note that JS can't reliably distinguish different NaN values,\n" ^
" // so there's no good way to test for specific bitpatterns here.\n" ^
" let actual = action();\n" ^
" if (!Number.isNaN(actual)) {\n" ^
" throw new Error(\"Wasm return value NaN expected, got \" + actual);\n" ^
Expand Down Expand Up @@ -168,12 +179,24 @@ let eq_of = function
| F32Type -> Values.F32 F32Op.Eq
| F64Type -> Values.F64 F64Op.Eq

let and_of = function
| I32Type | F32Type -> Values.I32 I32Op.And
| I64Type | F64Type -> Values.I64 I64Op.And

let reinterpret_of = function
| I32Type -> I32Type, Nop
| I64Type -> I64Type, Nop
| F32Type -> I32Type, Convert (Values.I32 I32Op.ReinterpretFloat)
| F64Type -> I64Type, Convert (Values.I64 I64Op.ReinterpretFloat)

let canonical_nan_of = function
| I32Type | F32Type -> Values.I32 (F32.to_bits F32.pos_nan)
| I64Type | F64Type -> Values.I64 (F64.to_bits F64.pos_nan)

let abs_mask_of = function
| I32Type | F32Type -> Values.I32 Int32.max_int
| I64Type | F64Type -> Values.I64 Int64.max_int

let invoke t lits at =
[t], FuncImport (1l @@ at) @@ at,
List.map (fun lit -> Const lit @@ at) lits @ [Call (0l @@ at) @@ at]
Expand All @@ -195,15 +218,25 @@ let assert_return lits ts at =
BrIf (0l @@ at) @@ at ]
in [], List.flatten (List.rev_map test lits)

let assert_return_nan ts at =
let var i = Int32.of_int i @@ at in
let init i t = [SetLocal (var i) @@ at] in
let test i t =
[ GetLocal (var i) @@ at;
GetLocal (var i) @@ at;
Compare (eq_of t) @@ at;
let assert_return_nan_bitpattern nan_bitmask_of ts at =
let test t =
let t', reinterpret = reinterpret_of t in
[ reinterpret @@ at;
Const (nan_bitmask_of t' @@ at) @@ at;
Binary (and_of t') @@ at;
Const (canonical_nan_of t' @@ at) @@ at;
Compare (eq_of t') @@ at;
Test (Values.I32 I32Op.Eqz) @@ at;
BrIf (0l @@ at) @@ at ]
in ts, List.flatten (List.mapi init ts @ List.mapi test ts)
in [], List.flatten (List.rev_map test ts)

let assert_return_canonical_nan =
(* The result may only differ from the canonical NaN in its sign bit *)
assert_return_nan_bitpattern abs_mask_of

let assert_return_arithmetic_nan =
(* The result can be any NaN that's one everywhere the canonical NaN is one *)
assert_return_nan_bitpattern canonical_nan_of

let wrap module_name item_name wrap_action wrap_assertion at =
let itypes, ikind, action = wrap_action at in
Expand Down Expand Up @@ -327,8 +360,10 @@ let of_assertion mods ass =
| AssertReturn (act, lits) ->
of_assertion' mods act "assert_return" (List.map of_literal lits)
(Some (assert_return lits))
| AssertReturnNaN act ->
of_assertion' mods act "assert_return_nan" [] (Some assert_return_nan)
| AssertReturnCanonicalNaN act ->
of_assertion' mods act "assert_return_canonical_nan" [] (Some assert_return_canonical_nan)
| AssertReturnArithmeticNaN act ->
of_assertion' mods act "assert_return_arithmetic_nan" [] (Some assert_return_arithmetic_nan)
| AssertTrap (act, _) ->
of_assertion' mods act "assert_trap" [] None
| AssertExhaustion (act, _) ->
Expand Down
19 changes: 16 additions & 3 deletions interpreter/host/run.ml
Original file line number Diff line number Diff line change
Expand Up @@ -356,15 +356,28 @@ let run_assertion ass =
let expect_vs = List.map (fun v -> v.it) vs in
assert_result ass.at (got_vs = expect_vs) got_vs print_result expect_vs

| AssertReturnNaN act ->
| AssertReturnCanonicalNaN act ->
trace ("Asserting return...");
let got_vs = run_action act in
let is_nan =
let is_canonical_nan =
match got_vs with
| [Values.F32 got_f32] -> got_f32 = F32.pos_nan || got_f32 = F32.neg_nan
| [Values.F64 got_f64] -> got_f64 = F64.pos_nan || got_f64 = F64.neg_nan
| _ -> false
in assert_result ass.at is_nan got_vs print_endline "nan"
in assert_result ass.at is_canonical_nan got_vs print_endline "nan"

| AssertReturnArithmeticNaN act ->
trace ("Asserting return...");
let got_vs = run_action act in
let is_arithmetic_nan =
(* Accept any NaN that's one everywhere pos_nan is one *)
match got_vs with
| [Values.F32 got_f32] -> let pos_nan = F32.to_bits F32.pos_nan in
Int32.logand (F32.to_bits got_f32) pos_nan = pos_nan
| [Values.F64 got_f64] -> let pos_nan = F64.to_bits F64.pos_nan in
Int64.logand (F64.to_bits got_f64) pos_nan = pos_nan
| _ -> false
in assert_result ass.at is_arithmetic_nan got_vs print_endline "nan"

| AssertTrap (act, re) ->
trace ("Asserting trap...");
Expand Down
6 changes: 4 additions & 2 deletions interpreter/text/arrange.ml
Original file line number Diff line number Diff line change
Expand Up @@ -416,8 +416,10 @@ let assertion mode ass =
Node ("assert_trap", [definition mode None def; Atom (string re)])
| AssertReturn (act, lits) ->
Node ("assert_return", action act :: List.map literal lits)
| AssertReturnNaN act ->
Node ("assert_return_nan", [action act])
| AssertReturnCanonicalNaN act ->
Node ("assert_return_canonical_nan", [action act])
| AssertReturnArithmeticNaN act ->
Node ("assert_return_arithmetic_nan", [action act])
| AssertTrap (act, re) ->
Node ("assert_trap", [action act; Atom (string re)])
| AssertExhaustion (act, re) ->
Expand Down
3 changes: 2 additions & 1 deletion interpreter/text/lexer.mll
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,8 @@ rule token = parse
| "assert_invalid" { ASSERT_INVALID }
| "assert_unlinkable" { ASSERT_UNLINKABLE }
| "assert_return" { ASSERT_RETURN }
| "assert_return_nan" { ASSERT_RETURN_NAN }
| "assert_return_canonical_nan" { ASSERT_RETURN_CANONICAL_NAN }
| "assert_return_arithmetic_nan" { ASSERT_RETURN_ARITHMETIC_NAN }
| "assert_trap" { ASSERT_TRAP }
| "assert_exhaustion" { ASSERT_EXHAUSTION }
| "input" { INPUT }
Expand Down
5 changes: 3 additions & 2 deletions interpreter/text/parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ let inline_type (c : context) ty at =
%token MODULE TABLE ELEM MEMORY DATA OFFSET IMPORT EXPORT TABLE
%token SCRIPT REGISTER INVOKE GET
%token ASSERT_MALFORMED ASSERT_INVALID ASSERT_SOFT_INVALID ASSERT_UNLINKABLE
%token ASSERT_RETURN ASSERT_RETURN_NAN ASSERT_TRAP ASSERT_EXHAUSTION
%token ASSERT_RETURN ASSERT_RETURN_CANONICAL_NAN ASSERT_RETURN_ARITHMETIC_NAN ASSERT_TRAP ASSERT_EXHAUSTION
%token INPUT OUTPUT
%token EOF

Expand Down Expand Up @@ -661,7 +661,8 @@ assertion :
| LPAR ASSERT_TRAP module_ TEXT RPAR
{ AssertUninstantiable (snd $3, $4) @@ at () }
| LPAR ASSERT_RETURN action const_list RPAR { AssertReturn ($3, $4) @@ at () }
| LPAR ASSERT_RETURN_NAN action RPAR { AssertReturnNaN $3 @@ at () }
| LPAR ASSERT_RETURN_CANONICAL_NAN action RPAR { AssertReturnCanonicalNaN $3 @@ at () }
| LPAR ASSERT_RETURN_ARITHMETIC_NAN action RPAR { AssertReturnArithmeticNaN $3 @@ at () }
| LPAR ASSERT_TRAP action TEXT RPAR { AssertTrap ($3, $4) @@ at () }
| LPAR ASSERT_EXHAUSTION action TEXT RPAR { AssertExhaustion ($3, $4) @@ at () }

Expand Down
3 changes: 2 additions & 1 deletion interpreter/text/script.ml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ and assertion' =
| AssertUnlinkable of definition * string
| AssertUninstantiable of definition * string
| AssertReturn of action * Ast.literal list
| AssertReturnNaN of action
| AssertReturnCanonicalNaN of action
| AssertReturnArithmeticNaN of action
| AssertTrap of action * string
| AssertExhaustion of action * string

Expand Down
Loading