Bug 1330797 - Update WebAssembly spec tests. r=luke
authorDan Gohman <sunfish@mozilla.com>
Thu, 12 Jan 2017 22:15:00 -0800
changeset 374298 bee517272e14ea1817b7b3f1ecfaa98b022ee242
parent 374252 2b2ae61e73f5d2e47403a4c891d613bf31561914
child 374299 bf6a6ad3296eb520a318fec018ad3e11f861e189
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1330797
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1330797 - Update WebAssembly spec tests. r=luke
js/src/jit-test/tests/wasm/binary.js
js/src/jit-test/tests/wasm/spec/call_indirect.wast
js/src/jit-test/tests/wasm/spec/custom_section.wast
js/src/jit-test/tests/wasm/spec/custom_section.wast.js
js/src/jit-test/tests/wasm/spec/float_misc.wast
js/src/jit-test/tests/wasm/spec/globals.wast
js/src/jit-test/tests/wasm/spec/memory.wast
js/src/jit-test/tests/wasm/spec/start.wast
js/src/wasm/WasmCode.cpp
js/src/wasm/WasmValidate.cpp
--- a/js/src/jit-test/tests/wasm/binary.js
+++ b/js/src/jit-test/tests/wasm/binary.js
@@ -198,16 +198,31 @@ function tableSection(initialSize) {
 function memorySection(initialSize) {
     var body = [];
     body.push(...varU32(1));           // number of memories
     body.push(...varU32(0x0));         // for now, no maximum
     body.push(...varU32(initialSize));
     return { name: memoryId, body };
 }
 
+function dataSection(segmentArrays) {
+    var body = [];
+    body.push(...varU32(segmentArrays.length));
+    for (let array of segmentArrays) {
+        body.push(...varU32(0)); // table index
+        body.push(...varU32(I32ConstCode));
+        body.push(...varS32(array.offset));
+        body.push(...varU32(EndCode));
+        body.push(...varU32(array.elems.length));
+        for (let elem of array.elems)
+            body.push(...varU32(elem));
+    }
+    return { name: dataId, body };
+}
+
 function elemSection(elemArrays) {
     var body = [];
     body.push(...varU32(elemArrays.length));
     for (let array of elemArrays) {
         body.push(...varU32(0)); // table index
         body.push(...varU32(I32ConstCode));
         body.push(...varS32(array.offset));
         body.push(...varU32(EndCode));
@@ -273,46 +288,70 @@ wasmEval(moduleWithSections([sigSection(
 
 wasmEval(moduleWithSections([
     sigSection([v2vSig]),
     importSection([{sigIndex:0, module:"a", func:""}]),
     declSection([0]),
     bodySection([v2vBody])
 ]), {a:{"":()=>{}}});
 
-assertErrorMessage(() => wasmEval(moduleWithSections([ {name: dataId, body: [], } ])), CompileError, /data section requires a memory section/);
+assertErrorMessage(() => wasmEval(moduleWithSections([ dataSection([{offset:1, elems:[]}]) ])), CompileError, /data segment requires a memory section/);
 
 wasmEval(moduleWithSections([tableSection(0)]));
 wasmEval(moduleWithSections([elemSection([])]));
 wasmEval(moduleWithSections([tableSection(0), elemSection([])]));
 wasmEval(moduleWithSections([tableSection(1), elemSection([{offset:1, elems:[]}])]));
 assertErrorMessage(() => wasmEval(moduleWithSections([tableSection(1), elemSection([{offset:0, elems:[0]}])])), CompileError, /table element out of range/);
 wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection(1), elemSection([{offset:0, elems:[0]}]), bodySection([v2vBody])]));
 wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection(2), elemSection([{offset:0, elems:[0,0]}]), bodySection([v2vBody])]));
 assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection(2), elemSection([{offset:0, elems:[0,1]}]), bodySection([v2vBody])])), CompileError, /table element out of range/);
 wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0,0,0]), tableSection(4), elemSection([{offset:0, elems:[0,1,0,2]}]), bodySection([v2vBody, v2vBody, v2vBody])]));
 wasmEval(moduleWithSections([sigSection([v2vSig,i2vSig]), declSection([0,0,1]), tableSection(3), elemSection([{offset:0,elems:[0,1,2]}]), bodySection([v2vBody, v2vBody, v2vBody])]));
 
-function invalidTableSection0() {
+function tableSection0() {
     var body = [];
     body.push(...varU32(0));           // number of tables
     return { name: tableId, body };
 }
 
-assertErrorMessage(() => wasmEval(moduleWithSections([invalidTableSection0()])), CompileError, /number of tables must be exactly one/);
+function invalidTableSection2() {
+    var body = [];
+    body.push(...varU32(2));           // number of tables
+    body.push(...varU32(AnyFuncCode));
+    body.push(...varU32(0x0));
+    body.push(...varU32(0));
+    body.push(...varU32(AnyFuncCode));
+    body.push(...varU32(0x0));
+    body.push(...varU32(0));
+    return { name: tableId, body };
+}
+
+wasmEval(moduleWithSections([tableSection0()]));
+assertErrorMessage(() => wasmEval(moduleWithSections([invalidTableSection2()])), CompileError, /number of tables must be at most one/);
 
 wasmEval(moduleWithSections([memorySection(0)]));
 
-function invalidMemorySection0() {
+function memorySection0() {
     var body = [];
     body.push(...varU32(0));           // number of memories
     return { name: memoryId, body };
 }
 
-assertErrorMessage(() => wasmEval(moduleWithSections([invalidMemorySection0()])), CompileError, /number of memories must be exactly one/);
+function invalidMemorySection2() {
+    var body = [];
+    body.push(...varU32(2));           // number of memories
+    body.push(...varU32(0x0));
+    body.push(...varU32(0));
+    body.push(...varU32(0x0));
+    body.push(...varU32(0));
+    return { name: memoryId, body };
+}
+
+wasmEval(moduleWithSections([memorySection0()]));
+assertErrorMessage(() => wasmEval(moduleWithSections([invalidMemorySection2()])), CompileError, /number of memories must be at most one/);
 
 // Test early 'end'
 const bodyMismatch = /function body length mismatch/;
 assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[EndCode]})])])), CompileError, bodyMismatch);
 assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[UnreachableCode,EndCode]})])])), CompileError, bodyMismatch);
 assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[EndCode,UnreachableCode]})])])), CompileError, bodyMismatch);
 
 // Deep nesting shouldn't crash or even throw.
--- a/js/src/jit-test/tests/wasm/spec/call_indirect.wast
+++ b/js/src/jit-test/tests/wasm/spec/call_indirect.wast
@@ -355,8 +355,14 @@
 )
 (assert_invalid
   (module
     (table 0 anyfunc)
     (func $large-type (call_indirect 1012321300 (i32.const 0)))
   )
   "unknown type"
 )
+
+;; invalid table
+(assert_invalid 
+  (module (table anyfunc (elem 0 0)))
+  "unknown function 0"
+)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/spec/custom_section.wast
@@ -0,0 +1,92 @@
+(module
+  "\00asm" "\0d\00\00\00"
+  "\00\24\10" "a custom section" "this is the payload"
+  "\00\20\10" "a custom section" "this is payload"
+  "\00\11\10" "a custom section" ""
+  "\00\10\00" "" "this is payload"
+  "\00\01\00" "" ""
+  "\00\24\10" "\00\00custom sectio\00" "this is the payload"
+)
+
+(module
+  "\00asm" "\0d\00\00\00"
+  "\00\0e\06" "custom" "payload"
+  "\00\0e\06" "custom" "payload"
+  "\01\01\00"  ;; type section
+  "\00\0e\06" "custom" "payload"
+  "\00\0e\06" "custom" "payload"
+  "\02\01\00"  ;; import section
+  "\00\0e\06" "custom" "payload"
+  "\00\0e\06" "custom" "payload"
+  "\03\01\00"  ;; function section
+  "\00\0e\06" "custom" "payload"
+  "\00\0e\06" "custom" "payload"
+  "\04\01\00"  ;; table section
+  "\00\0e\06" "custom" "payload"
+  "\00\0e\06" "custom" "payload"
+  "\05\01\00"  ;; memory section
+  "\00\0e\06" "custom" "payload"
+  "\00\0e\06" "custom" "payload"
+  "\06\01\00"  ;; global section
+  "\00\0e\06" "custom" "payload"
+  "\00\0e\06" "custom" "payload"
+  "\07\01\00"  ;; export section
+  "\00\0e\06" "custom" "payload"
+  "\00\0e\06" "custom" "payload"
+  "\09\01\00"  ;; element section
+  "\00\0e\06" "custom" "payload"
+  "\00\0e\06" "custom" "payload"
+  "\0a\01\00"  ;; code section
+  "\00\0e\06" "custom" "payload"
+  "\00\0e\06" "custom" "payload"
+  "\0b\01\00"  ;; data section
+  "\00\0e\06" "custom" "payload"
+  "\00\0e\06" "custom" "payload"
+)
+
+(module
+  "\00asm" "\0d\00\00\00"
+  "\01\07\01\60\02\7f\7f\01\7f"                ;; type section
+  "\00\1a\06" "custom" "this is the payload"   ;; custom section
+  "\03\02\01\00"                               ;; function section
+  "\07\0a\01\06\61\64\64\54\77\6f\00\00"       ;; export section
+  "\0a\09\01\07\00\20\00\20\01\6a\0b"          ;; code section
+  "\00\1b\07" "custom2" "this is the payload"  ;; custom section
+)
+
+(assert_malformed
+  (module
+    "\00asm" "\0d\00\00\00"
+    "\00\00"
+  )
+  "unexpected end"
+)
+
+(assert_malformed
+  (module
+    "\00asm" "\0d\00\00\00"
+    "\00\26\10" "a custom section" "this is the payload"
+  )
+  "unexpected end"
+)
+
+(assert_malformed
+  (module
+    "\00asm" "\0d\00\00\00"
+    "\00\25\10" "a custom section" "this is the payload"
+    "\00\24\10" "a custom section" "this is the payload"
+  )
+  "invalid section id"
+)
+
+(assert_malformed
+  (module
+    "\00asm" "\0d\00\00\00"
+    "\01\07\01\60\02\7f\7f\01\7f"                         ;; type section
+    "\00\25\10" "a custom section" "this is the payload"  ;; invalid length!
+    "\03\02\01\00"                                        ;; function section
+    "\0a\09\01\07\00\20\00\20\01\6a\0b"                   ;; code section
+    "\00\1b\07" "custom2" "this is the payload"           ;; custom section
+  )
+  "function and code section have inconsistent lengths"
+)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/spec/custom_section.wast.js
@@ -0,0 +1,1 @@
+var importedArgs = ['custom_section.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/float_misc.wast
+++ b/js/src/jit-test/tests/wasm/spec/float_misc.wast
@@ -626,18 +626,8 @@
 (assert_return (invoke "f64.nearest" (f64.const -4.5)) (f64.const -4.0))
 (assert_return (invoke "f64.nearest" (f64.const -3.5)) (f64.const -4.0))
 
 ;; Test the maximum and minimum value for which nearest is not an identity operator.
 (assert_return (invoke "f32.nearest" (f32.const -0x1.fffffep+22)) (f32.const -0x1p+23))
 (assert_return (invoke "f32.nearest" (f32.const 0x1.fffffep+22)) (f32.const 0x1p+23))
 (assert_return (invoke "f64.nearest" (f64.const -0x1.fffffffffffffp+51)) (f64.const -0x1p+52))
 (assert_return (invoke "f64.nearest" (f64.const 0x1.fffffffffffffp+51)) (f64.const 0x1p+52))
-
-;; Test that min and max behave properly with signaling NaNs.
-(assert_return (invoke "f32.min" (f32.const 0.0) (f32.const nan:0x200000)) (f32.const nan:0x600000))
-(assert_return (invoke "f32.min" (f32.const nan:0x200000) (f32.const 0.0)) (f32.const nan:0x600000))
-(assert_return (invoke "f32.max" (f32.const 0.0) (f32.const nan:0x200000)) (f32.const nan:0x600000))
-(assert_return (invoke "f32.max" (f32.const nan:0x200000) (f32.const 0.0)) (f32.const nan:0x600000))
-(assert_return (invoke "f64.min" (f64.const 0.0) (f64.const nan:0x4000000000000)) (f64.const nan:0xc000000000000))
-(assert_return (invoke "f64.min" (f64.const nan:0x4000000000000) (f64.const 0.0)) (f64.const nan:0xc000000000000))
-(assert_return (invoke "f64.max" (f64.const 0.0) (f64.const nan:0x4000000000000)) (f64.const nan:0xc000000000000))
-(assert_return (invoke "f64.max" (f64.const nan:0x4000000000000) (f64.const 0.0)) (f64.const nan:0xc000000000000))
--- a/js/src/jit-test/tests/wasm/spec/globals.wast
+++ b/js/src/jit-test/tests/wasm/spec/globals.wast
@@ -90,8 +90,20 @@
   (module (global i32 (get_global 0)))
   "unknown global"
 )
 
 (assert_invalid
   (module (global i32 (get_global 1)) (global i32 (i32.const 0)))
   "unknown global"
 )
+
+(module
+  (import "spectest" "global" (global i32))
+)
+(assert_malformed (module "\00asm\0d\00\00\00\02\94\80\80\80\00\01\08\73\70\65\63\74\65\73\74\06\67\6c\6f\62\61\6c\03\7f\02") "invalid mutability")
+
+(module
+  (global i32 (i32.const 0))
+)
+(assert_malformed (module "\00asm\0d\00\00\00\06\86\80\80\80\00\01\7f\ff\41\00\0b") "invalid mutability")
+(assert_malformed (module "\00asm\0d\00\00\00\06\86\80\80\80\00\01\7f\d4\41\00\0b") "invalid mutability")
+(assert_malformed (module "\00asm\0d\00\00\00\06\86\80\80\80\00\01\7f\02\41\00\0b") "invalid mutability")
--- a/js/src/jit-test/tests/wasm/spec/memory.wast
+++ b/js/src/jit-test/tests/wasm/spec/memory.wast
@@ -12,28 +12,56 @@
   (data (i32.const 0) "a") (data (i32.const 1) "b") (data (i32.const 2) "c")
 )
 (module (global (import "spectest" "global") i32) (memory 1) (data (get_global 0) "a"))
 (module (global $g (import "spectest" "global") i32) (memory 1) (data (get_global $g) "a"))
 ;; Use of internal globals in constant expressions is not allowed in MVP.
 ;; (module (memory 1) (data (get_global 0) "a") (global i32 (i32.const 0)))
 ;; (module (memory 1) (data (get_global $g) "a") (global $g i32 (i32.const 0)))
 
+(assert_invalid (module (memory 0) (memory 0)) "multiple memories")
+(assert_invalid (module (memory (import "spectest" "memory") 0) (memory 0)) "multiple memories")
+
 (module (memory (data)) (func (export "memsize") (result i32) (current_memory)))
 (assert_return (invoke "memsize") (i32.const 0))
 (module (memory (data "")) (func (export "memsize") (result i32) (current_memory)))
 (assert_return (invoke "memsize") (i32.const 0))
 (module (memory (data "x")) (func (export "memsize") (result i32) (current_memory)))
 (assert_return (invoke "memsize") (i32.const 1))
 
 (assert_invalid (module (data (i32.const 0))) "unknown memory")
 (assert_invalid (module (data (i32.const 0) "")) "unknown memory")
 (assert_invalid (module (data (i32.const 0) "x")) "unknown memory")
 
 (assert_invalid
+  (module (func (drop (f32.load (i32.const 0)))))
+  "unknown memory"
+)
+(assert_invalid
+  (module (func (f32.store (f32.const 0) (i32.const 0))))
+  "unknown memory"
+)
+(assert_invalid
+  (module (func (drop (i32.load8_s (i32.const 0)))))
+  "unknown memory"
+)
+(assert_invalid
+  (module (func (i32.store8 (i32.const 0) (i32.const 0))))
+  "unknown memory"
+)
+(assert_invalid
+  (module (func (drop (current_memory))))
+  "unknown memory"
+)
+(assert_invalid
+  (module (func (drop (grow_memory (i32.const 0)))))
+  "unknown memory"
+)
+
+(assert_invalid
   (module (memory 1) (data (i64.const 0)))
   "type mismatch"
 )
 (assert_invalid
   (module (memory 1) (data (i32.ctz (i32.const 0))))
   "constant expression required"
 )
 (assert_invalid
--- a/js/src/jit-test/tests/wasm/spec/start.wast
+++ b/js/src/jit-test/tests/wasm/spec/start.wast
@@ -1,26 +1,28 @@
 (assert_invalid
   (module (func) (start 1))
   "unknown function"
 )
+
 (assert_invalid
   (module
     (func $main (result i32) (return (i32.const 0)))
     (start $main)
   )
   "start function"
 )
 (assert_invalid
   (module
     (func $main (param $a i32))
     (start $main)
   )
   "start function"
 )
+
 (module
   (memory (data "A"))
   (func $inc
     (i32.store8
       (i32.const 0)
       (i32.add
         (i32.load8_u (i32.const 0))
         (i32.const 1)
@@ -82,12 +84,17 @@
 )
 
 (module
   (func $print_i32 (import "spectest" "print") (param i32))
   (func $main (call $print_i32 (i32.const 2)))
   (start $main)
 )
 
+(module
+  (func $print (import "spectest" "print"))
+  (start $print)
+)
+
 (assert_trap
   (module (func $main (unreachable)) (start $main))
   "unreachable"
 )
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -172,17 +172,17 @@ SendCodeRangesToProfiler(CodeSegment& cs
         (void)start;
         (void)size;
 
 #ifdef JS_ION_PERF
         if (PerfFuncEnabled()) {
             const char* file = metadata.filename.get();
             unsigned line = codeRange.funcLineOrBytecode();
             unsigned column = 0;
-            writePerfSpewerAsmJSFunctionMap(start, size, file, line, column, name.begin());
+            writePerfSpewerWasmFunctionMap(start, size, file, line, column, name.begin());
         }
 #endif
 #ifdef MOZ_VTUNE
         if (IsVTuneProfilingActive()) {
             unsigned method_id = iJIT_GetNewMethodID();
             if (method_id == 0)
                 return;
             iJIT_Method_Load method;
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -1084,21 +1084,23 @@ DecodeTableSection(Decoder& d, ModuleEnv
         return false;
     if (sectionStart == Decoder::NotStarted)
         return true;
 
     uint32_t numTables;
     if (!d.readVarU32(&numTables))
         return d.fail("failed to read number of tables");
 
-    if (numTables != 1)
-        return d.fail("the number of tables must be exactly one");
+    if (numTables > 1)
+        return d.fail("the number of tables must be at most one");
 
-    if (!DecodeTableLimits(d, &env->tables))
-        return false;
+    for (uint32_t i = 0; i < numTables; ++i) {
+        if (!DecodeTableLimits(d, &env->tables))
+            return false;
+    }
 
     if (!d.finishSection(sectionStart, sectionSize, "table"))
         return false;
 
     return true;
 }
 
 static bool
@@ -1109,21 +1111,23 @@ DecodeMemorySection(Decoder& d, ModuleEn
         return false;
     if (sectionStart == Decoder::NotStarted)
         return true;
 
     uint32_t numMemories;
     if (!d.readVarU32(&numMemories))
         return d.fail("failed to read number of memories");
 
-    if (numMemories != 1)
-        return d.fail("the number of memories must be exactly one");
+    if (numMemories > 1)
+        return d.fail("the number of memories must be at most one");
 
-    if (!DecodeMemoryLimits(d, env))
-        return false;
+    for (uint32_t i = 0; i < numMemories; ++i) {
+        if (!DecodeMemoryLimits(d, env))
+            return false;
+    }
 
     if (!d.finishSection(sectionStart, sectionSize, "memory"))
         return false;
 
     return true;
 }
 
 static bool
@@ -1529,34 +1533,34 @@ static bool
 DecodeDataSection(Decoder& d, ModuleEnvironment* env)
 {
     uint32_t sectionStart, sectionSize;
     if (!d.startSection(SectionId::Data, env, &sectionStart, &sectionSize, "data"))
         return false;
     if (sectionStart == Decoder::NotStarted)
         return true;
 
-    if (!env->usesMemory())
-        return d.fail("data section requires a memory section");
-
     uint32_t numSegments;
     if (!d.readVarU32(&numSegments))
         return d.fail("failed to read number of data segments");
 
     if (numSegments > MaxDataSegments)
         return d.fail("too many data segments");
 
     for (uint32_t i = 0; i < numSegments; i++) {
         uint32_t linearMemoryIndex;
         if (!d.readVarU32(&linearMemoryIndex))
             return d.fail("expected linear memory index");
 
         if (linearMemoryIndex != 0)
             return d.fail("linear memory index must currently be 0");
 
+        if (!env->usesMemory())
+            return d.fail("data segment requires a memory section");
+
         DataSegment seg;
         if (!DecodeInitializerExpression(d, env->globals, ValType::I32, &seg.offset))
             return false;
 
         if (!d.readVarU32(&seg.length))
             return d.fail("expected segment size");
 
         seg.bytecodeOffset = d.currentOffset();