Bug 1459893 - Wasm: memory.fill and memory.copy: add validation code and test cases. r=lth.
authorJulian Seward <jseward@acm.org>
Mon, 14 May 2018 22:43:31 +0200
changeset 418304 c81dfed2fa9fbde91af9d33a929461105102b92e
parent 418303 d09dc112f084a70fbd5e999234c1a35f2beeea07
child 418305 cf3ee14023483cbbb57129479537c713e22c1980
child 418353 dd55c2c6b6fa6a6875148dcfce20937dab327803
push id33997
push userncsoregi@mozilla.com
push dateTue, 15 May 2018 09:53:53 +0000
treeherdermozilla-central@cf3ee1402348 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1459893, 1446930
milestone62.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 1459893 - Wasm: memory.fill and memory.copy: add validation code and test cases. r=lth. Bug 1446930 added a baseline implementation of memory.fill/copy, and runtime test cases for them, but missed out validation code and test cases for them. This is a followup, to add those missing elements.
js/src/jit-test/tests/wasm/memory-bulk.js
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmBinaryToAST.cpp
js/src/wasm/WasmIonCompile.cpp
js/src/wasm/WasmOpIter.h
js/src/wasm/WasmValidate.cpp
--- a/js/src/jit-test/tests/wasm/memory-bulk.js
+++ b/js/src/jit-test/tests/wasm/memory-bulk.js
@@ -1,12 +1,127 @@
 
 if (!wasmBulkMemSupported())
     quit(0);
 
+//---------------------------------------------------------------------//
+//---------------------------------------------------------------------//
+// Validation tests
+
+//-----------------------------------------------------------
+// Test helpers.  Copied and simplified from binary.js.
+
+load(libdir + "wasm-binary.js");
+
+function toU8(array) {
+    for (let b of array)
+        assertEq(b < 256, true);
+    return Uint8Array.from(array);
+}
+
+function varU32(u32) {
+    assertEq(u32 >= 0, true);
+    assertEq(u32 < Math.pow(2,32), true);
+    var bytes = [];
+    do {
+        var byte = u32 & 0x7f;
+        u32 >>>= 7;
+        if (u32 != 0)
+            byte |= 0x80;
+        bytes.push(byte);
+    } while (u32 != 0);
+    return bytes;
+}
+
+function moduleHeaderThen(...rest) {
+    return [magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, ...rest];
+}
+
+function moduleWithSections(sectionArray) {
+    var bytes = moduleHeaderThen();
+    for (let section of sectionArray) {
+        bytes.push(section.name);
+        bytes.push(...varU32(section.body.length));
+        bytes.push(...section.body);
+    }
+    return toU8(bytes);
+}
+
+function sigSection(sigs) {
+    var body = [];
+    body.push(...varU32(sigs.length));
+    for (let sig of sigs) {
+        body.push(...varU32(FuncCode));
+        body.push(...varU32(sig.args.length));
+        for (let arg of sig.args)
+            body.push(...varU32(arg));
+        body.push(...varU32(sig.ret == VoidCode ? 0 : 1));
+        if (sig.ret != VoidCode)
+            body.push(...varU32(sig.ret));
+    }
+    return { name: typeId, body };
+}
+
+function declSection(decls) {
+    var body = [];
+    body.push(...varU32(decls.length));
+    for (let decl of decls)
+        body.push(...varU32(decl));
+    return { name: functionId, body };
+}
+
+function funcBody(func) {
+    var body = varU32(func.locals.length);
+    for (let local of func.locals)
+        body.push(...varU32(local));
+    body = body.concat(...func.body);
+    body.push(EndCode);
+    body.splice(0, 0, ...varU32(body.length));
+    return body;
+}
+
+function bodySection(bodies) {
+    var body = varU32(bodies.length).concat(...bodies);
+    return { name: codeId, body };
+}
+
+const v2vSig = {args:[], ret:VoidCode};
+const v2vSigSection = sigSection([v2vSig]);
+
+// Prefixed opcodes
+
+function checkMiscPrefixed(opcode, expect_failure) {
+    let binary = moduleWithSections(
+           [v2vSigSection, declSection([0]),
+            bodySection(
+                [funcBody(
+                    {locals:[],
+                     body:[0x41, 0x0, 0x41, 0x0, 0x41, 0x0, // 3 x const.i32 0
+                           MiscPrefix, opcode]})])]);
+    if (expect_failure) {
+        assertErrorMessage(() => new WebAssembly.Module(binary),
+                           WebAssembly.CompileError, /unrecognized opcode/);
+    } else {
+        assertEq(true, WebAssembly.validate(binary));
+    }
+}
+
+//-----------------------------------------------------------
+// Verification cases for memory.copy/fill
+
+checkMiscPrefixed(0x3f, true);  // unassigned
+checkMiscPrefixed(0x40, false); // memory.copy
+checkMiscPrefixed(0x41, false); // memory.fill
+checkMiscPrefixed(0x42, true);  // unassigned
+
+
+//---------------------------------------------------------------------//
+//---------------------------------------------------------------------//
+// Run tests
+
 //-----------------------------------------------------------
 // Test helpers
 function checkRange(arr, minIx, maxIxPlusOne, expectedValue)
 {
     for (let i = minIx; i < maxIxPlusOne; i++) {
         assertEq(arr[i], expectedValue);
     }
 }
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -9195,17 +9195,17 @@ BaseCompiler::emitWake()
 
 #ifdef ENABLE_WASM_BULKMEM_OPS
 bool
 BaseCompiler::emitMemCopy()
 {
     uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
     Nothing nothing;
-    if (!iter_.readMemCopy(ValType::I32, &nothing, &nothing, &nothing))
+    if (!iter_.readMemCopy(&nothing, &nothing, &nothing))
         return false;
 
     if (deadCode_)
         return true;
 
     emitInstanceCall(lineOrBytecode, SigPIII_, ExprType::Void, SymbolicAddress::MemCopy);
 
     Label ok;
@@ -9217,17 +9217,17 @@ BaseCompiler::emitMemCopy()
 }
 
 bool
 BaseCompiler::emitMemFill()
 {
     uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
     Nothing nothing;
-    if (!iter_.readMemFill(ValType::I32, &nothing, &nothing, &nothing))
+    if (!iter_.readMemFill(&nothing, &nothing, &nothing))
         return false;
 
     if (deadCode_)
         return true;
 
     emitInstanceCall(lineOrBytecode, SigPIII_, ExprType::Void, SymbolicAddress::MemFill);
 
     Label ok;
--- a/js/src/wasm/WasmBinaryToAST.cpp
+++ b/js/src/wasm/WasmBinaryToAST.cpp
@@ -1248,17 +1248,17 @@ AstDecodeWake(AstDecodeContext& c)
 
     return true;
 }
 
 #ifdef ENABLE_WASM_BULKMEM_OPS
 static bool
 AstDecodeMemCopy(AstDecodeContext& c)
 {
-    if (!c.iter().readMemCopy(ValType::I32, nullptr, nullptr, nullptr))
+    if (!c.iter().readMemCopy(nullptr, nullptr, nullptr))
         return false;
 
     AstDecodeStackItem dest = c.popCopy();
     AstDecodeStackItem src  = c.popCopy();
     AstDecodeStackItem len  = c.popCopy();
 
     AstMemCopy* mc = new(c.lifo) AstMemCopy(dest.expr, src.expr, len.expr);
 
@@ -1269,17 +1269,17 @@ AstDecodeMemCopy(AstDecodeContext& c)
         return false;
 
     return true;
 }
 
 static bool
 AstDecodeMemFill(AstDecodeContext& c)
 {
-    if (!c.iter().readMemFill(ValType::I32, nullptr, nullptr, nullptr))
+    if (!c.iter().readMemFill(nullptr, nullptr, nullptr))
         return false;
 
     AstDecodeStackItem len   = c.popCopy();
     AstDecodeStackItem val   = c.popCopy();
     AstDecodeStackItem start = c.popCopy();
 
     AstMemFill* mf = new(c.lifo) AstMemFill(start.expr, val.expr, len.expr);
 
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -3598,17 +3598,17 @@ EmitAtomicXchg(FunctionCompiler& f, ValT
 
 #endif // ENABLE_WASM_THREAD_OPS
 
 #ifdef ENABLE_WASM_BULKMEM_OPS
 static bool
 EmitMemCopy(FunctionCompiler& f)
 {
     MDefinition *dest, *src, *len;
-    if (!f.iter().readMemCopy(ValType::I32, &dest, &src, &len))
+    if (!f.iter().readMemCopy(&dest, &src, &len))
         return false;
 
     if (f.inDeadCode())
         return false;
 
     uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
 
     CallCompileState args(f, lineOrBytecode);
@@ -3637,17 +3637,17 @@ EmitMemCopy(FunctionCompiler& f)
 
     return true;
 }
 
 static bool
 EmitMemFill(FunctionCompiler& f)
 {
     MDefinition *start, *val, *len;
-    if (!f.iter().readMemFill(ValType::I32, &start, &val, &len))
+    if (!f.iter().readMemFill(&start, &val, &len))
         return false;
 
     if (f.inDeadCode())
         return false;
 
     uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
 
     CallCompileState args(f, lineOrBytecode);
--- a/js/src/wasm/WasmOpIter.h
+++ b/js/src/wasm/WasmOpIter.h
@@ -653,18 +653,18 @@ class MOZ_STACK_CLASS OpIter : private P
     MOZ_MUST_USE bool readSwizzle(ValType simdType, uint8_t (* lanes)[16], Value* vector);
     MOZ_MUST_USE bool readShuffle(ValType simdType, uint8_t (* lanes)[16],
                                   Value* lhs, Value* rhs);
     MOZ_MUST_USE bool readSimdSelect(ValType simdType, Value* trueValue,
                                      Value* falseValue,
                                      Value* condition);
     MOZ_MUST_USE bool readSimdCtor(ValType elementType, uint32_t numElements, ValType simdType,
                                    ValueVector* argValues);
-    MOZ_MUST_USE bool readMemCopy(ValType argType, Value* dest, Value* src, Value* len);
-    MOZ_MUST_USE bool readMemFill(ValType argType, Value* start, Value* val, Value* len);
+    MOZ_MUST_USE bool readMemCopy(Value* dest, Value* src, Value* len);
+    MOZ_MUST_USE bool readMemFill(Value* start, Value* val, Value* len);
 
     // At a location where readOp is allowed, peek at the next opcode
     // without consuming it or updating any internal state.
     // Never fails: returns uint16_t(Op::Limit) in op->b0 if it can't read.
     void peekOp(OpBytes* op);
 
     // ------------------------------------------------------------------------
     // Stack management.
@@ -2234,47 +2234,45 @@ OpIter<Policy>::readSimdCtor(ValType ele
 
     infalliblePush(simdType);
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-OpIter<Policy>::readMemCopy(ValType argType,
-                            Value* dest, Value* src, Value* len)
+OpIter<Policy>::readMemCopy(Value* dest, Value* src, Value* len)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::MemCopy);
 
-    if (!popWithType(argType, len))
+    if (!popWithType(ValType::I32, len))
         return false;
 
-    if (!popWithType(argType, src))
+    if (!popWithType(ValType::I32, src))
         return false;
 
-    if (!popWithType(argType, dest))
+    if (!popWithType(ValType::I32, dest))
         return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-OpIter<Policy>::readMemFill(ValType argType,
-                            Value* start, Value* val, Value* len)
+OpIter<Policy>::readMemFill(Value* start, Value* val, Value* len)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::MemFill);
 
-    if (!popWithType(argType, len))
+    if (!popWithType(ValType::I32, len))
         return false;
 
-    if (!popWithType(argType, val))
+    if (!popWithType(ValType::I32, val))
         return false;
 
-    if (!popWithType(argType, start))
+    if (!popWithType(ValType::I32, start))
         return false;
 
     return true;
 }
 
 } // namespace wasm
 } // namespace js
 
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -812,16 +812,22 @@ DecodeFunctionBodyExprs(const ModuleEnvi
                 CHECK(iter.readConversion(ValType::F64, ValType::I32, &nothing));
               case uint16_t(MiscOp::I64TruncSSatF32):
               case uint16_t(MiscOp::I64TruncUSatF32):
                 CHECK(iter.readConversion(ValType::F32, ValType::I64, &nothing));
               case uint16_t(MiscOp::I64TruncSSatF64):
               case uint16_t(MiscOp::I64TruncUSatF64):
                 CHECK(iter.readConversion(ValType::F64, ValType::I64, &nothing));
 #endif
+#ifdef ENABLE_WASM_BULKMEM_OPS
+              case uint16_t(MiscOp::MemCopy):
+                CHECK(iter.readMemCopy(&nothing, &nothing, &nothing));
+              case uint16_t(MiscOp::MemFill):
+                CHECK(iter.readMemFill(&nothing, &nothing, &nothing));
+#endif
               default:
                 return iter.unrecognizedOpcode(&op);
             }
             break;
           }
 #ifdef ENABLE_WASM_GC
           case uint16_t(Op::RefNull): {
             if (env.gcTypesEnabled == HasGcTypes::False)