[WASM-References] Add support for Anyref in parameters and return types, Ref.null...
authorjustin_michaud@apple.com <justin_michaud@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 18 May 2019 04:58:36 +0000 (04:58 +0000)
committerjustin_michaud@apple.com <justin_michaud@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 18 May 2019 04:58:36 +0000 (04:58 +0000)
https://bugs.webkit.org/show_bug.cgi?id=197969

JSTests:

Reviewed by Keith Miller.

Support the anyref type in Builder.js, plus add some extra error logging.
Add new folder for wasm references tests.

* wasm.yaml:
* wasm/Builder.js:
(const._isValidValue):
* wasm/references/anyref_modules.js: Added.
(Call.3.RefIsNull.End.End.WebAssembly.js.ident):
(Call.3.RefIsNull.End.End.WebAssembly.js.make_null):
(Call.3.RefIsNull.End.End.WebAssembly):
(undefined):
* wasm/references/is_null.js: Added.
* wasm/references/is_null_error.js: Added.
* wasm/spec-harness/index.js:
* wasm/wasm.json:

Source/JavaScriptCore:

Reviewed by Keith Miller.

Add a new runtime option for wasm references.
Add support for Anyref as a value type.
Add support for Anyref in parameters and return types of Wasm functions. JSValues are marshalled into/out of wasm Anyrefs
        as a black box, except null which becomes a Nullref value. Nullref is not expressible in the bytecode or in the js API.
Add Ref.null and Ref.is_null for Anyref values. Support for these functions with funcrefs is out of scope.

* runtime/Options.h:
* wasm/WasmAirIRGenerator.cpp:
(JSC::Wasm::AirIRGenerator::tmpForType):
(JSC::Wasm::AirIRGenerator::AirIRGenerator):
(JSC::Wasm::AirIRGenerator::addConstant):
(JSC::Wasm::AirIRGenerator::addRefIsNull):
(JSC::Wasm::AirIRGenerator::addReturn):
* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::addRefIsNull):
* wasm/WasmCallingConvention.h:
(JSC::Wasm::CallingConventionAir::marshallArgument const):
(JSC::Wasm::CallingConventionAir::setupCall const):
* wasm/WasmFormat.h:
(JSC::Wasm::isValueType):
* wasm/WasmFunctionParser.h:
(JSC::Wasm::FunctionParser<Context>::FunctionParser):
(JSC::Wasm::FunctionParser<Context>::parseExpression):
(JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression):
* wasm/WasmValidate.cpp:
(JSC::Wasm::Validate::addRefIsNull):
* wasm/generateWasmOpsHeader.py:
(bitSet):
* wasm/js/JSToWasm.cpp:
(JSC::Wasm::createJSToWasmWrapper):
* wasm/js/WasmToJS.cpp:
(JSC::Wasm::wasmToJS):
* wasm/js/WebAssemblyFunction.cpp:
(JSC::callWebAssemblyFunction):
(JSC::WebAssemblyFunction::jsCallEntrypointSlow):
* wasm/wasm.json:

Tools:

Run wasm tests additionally with wasmBBQUsesAir=0.

Reviewed by Keith Miller.

* Scripts/run-jsc-stress-tests:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@245496 268f45cc-cd09-0410-ab3c-d52691b4dbfc

23 files changed:
JSTests/ChangeLog
JSTests/wasm.yaml
JSTests/wasm/Builder.js
JSTests/wasm/references/anyref_modules.js [new file with mode: 0644]
JSTests/wasm/references/is_null.js [new file with mode: 0644]
JSTests/wasm/references/is_null_error.js [new file with mode: 0644]
JSTests/wasm/spec-harness/index.js
JSTests/wasm/wasm.json
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/Options.h
Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp
Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Source/JavaScriptCore/wasm/WasmCallingConvention.h
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/WasmFunctionParser.h
Source/JavaScriptCore/wasm/WasmValidate.cpp
Source/JavaScriptCore/wasm/generateWasmOpsHeader.py
Source/JavaScriptCore/wasm/js/JSToWasm.cpp
Source/JavaScriptCore/wasm/js/WasmToJS.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp
Source/JavaScriptCore/wasm/wasm.json
Tools/ChangeLog
Tools/Scripts/run-jsc-stress-tests

index e68c826..0daf3b1 100644 (file)
@@ -1,3 +1,26 @@
+2019-05-17  Justin Michaud  <justin_michaud@apple.com>
+
+        [WASM-References] Add support for Anyref in parameters and return types, Ref.null and Ref.is_null for Anyref values.
+        https://bugs.webkit.org/show_bug.cgi?id=197969
+
+        Reviewed by Keith Miller.
+
+        Support the anyref type in Builder.js, plus add some extra error logging.
+        Add new folder for wasm references tests.
+
+        * wasm.yaml:
+        * wasm/Builder.js:
+        (const._isValidValue):
+        * wasm/references/anyref_modules.js: Added.
+        (Call.3.RefIsNull.End.End.WebAssembly.js.ident):
+        (Call.3.RefIsNull.End.End.WebAssembly.js.make_null):
+        (Call.3.RefIsNull.End.End.WebAssembly):
+        (undefined):
+        * wasm/references/is_null.js: Added.
+        * wasm/references/is_null_error.js: Added.
+        * wasm/spec-harness/index.js:
+        * wasm/wasm.json:
+
 2019-05-16  Ross Kirsling  <ross.kirsling@sony.com>
 
         [JSC] Invalid AssignmentTargetType should be an early error.
index 3c1a5fc..45dbd1a 100644 (file)
@@ -29,6 +29,8 @@
   cmd: runNoJIT unless parseRunCommands
 - path: wasm/function-tests
   cmd: runWebAssemblySuite unless parseRunCommands
+- path: wasm/references
+  cmd: runWebAssemblySuite unless parseRunCommands
 - path: wasm/fuzz
   cmd: runWebAssemblySuite unless parseRunCommands
 - path: wasm/stress
index e94f0a3..f22de1b 100644 (file)
@@ -33,6 +33,7 @@ const _isValidValue = (value, type) => {
     switch (type) {
     // We allow both signed and unsigned numbers.
     case "i32": return Math.round(value) === value && LLB.varint32Min <= value && value <= LLB.varuint32Max;
+    case "anyref": return true;
     case "i64": return true; // FIXME https://bugs.webkit.org/show_bug.cgi?id=163420 64-bit values
     case "f32": return typeof(value) === "number" && isFinite(value);
     case "f64": return typeof(value) === "number" && isFinite(value);
diff --git a/JSTests/wasm/references/anyref_modules.js b/JSTests/wasm/references/anyref_modules.js
new file mode 100644 (file)
index 0000000..ed10096
--- /dev/null
@@ -0,0 +1,132 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+      .Type().End()
+      .Function().End()
+      .Export()
+          .Function("h")
+          .Function("i")
+          .Function("j")
+          .Function("k")
+      .End()
+      .Code()
+        .Function("h", { params: ["anyref"], ret: "anyref" })
+          .GetLocal(0)
+        .End()
+
+        .Function("i", { params: [], ret: "anyref" })
+            .RefNull()
+            .Call(0)
+        .End()
+
+        .Function("j", { params: ["anyref"], ret: "i32" })
+            .GetLocal(0)
+            .RefIsNull()
+        .End()
+
+        .Function("k", { params: [], ret: "i32" })
+            .RefNull()
+            .RefIsNull()
+        .End()
+      .End().WebAssembly().get()));
+
+const $2 = new WebAssembly.Instance(new WebAssembly.Module((new Builder())
+      .Type().End()
+      .Import()
+          .Function("m1", "h", { params: ["anyref"], ret: "anyref" })
+          .Function("m1", "j", { params: ["anyref"], ret: "i32" })
+          .Function("js", "ident", { params: ["anyref"], ret: "anyref" })
+          .Function("js", "make_null", { params: [], ret: "anyref" })
+      .End()
+      .Function().End()
+      .Export()
+          .Function("call_h")
+          .Function("call_j")
+          .Function("call_h_null")
+          .Function("call_j_null")
+          .Function("call_ident")
+          .Function("call_ident_null")
+          .Function("is_makenull_null")
+      .End()
+      .Code()
+        .Function("call_h", { params: ["anyref"], ret: "anyref" })
+          .GetLocal(0)
+          .Call(0)
+        .End()
+
+        .Function("call_j", { params: ["anyref"], ret: "i32" })
+          .GetLocal(0)
+          .Call(1)
+        .End()
+
+        .Function("call_h_null", { params: [], ret: "anyref" })
+          .RefNull()
+          .Call(0)
+        .End()
+
+        .Function("call_j_null", { params: [], ret: "i32" })
+          .RefNull()
+          .Call(1)
+        .End()
+
+        .Function("call_ident", { params: ["anyref"], ret: "anyref" })
+          .I32Const(1)
+          .If("anyref")
+          .Block("anyref", (b) =>
+            b.GetLocal(0)
+          )
+          .Else()
+          .Block("anyref", (b) =>
+            b.GetLocal(0)
+          )
+          .End()
+          .Call(2)
+        .End()
+
+        .Function("call_ident_null", { params: [], ret: "anyref" })
+          .RefNull()
+          .Call(2)
+        .End()
+
+        .Function("is_makenull_null", { params: [], ret: "i32" })
+          .Call(3)
+          .RefIsNull()
+        .End()
+      .End().WebAssembly().get()), { m1: $1.exports, js: {
+        ident: function(x) { return x; },
+        make_null: function() { return null; },
+      } });
+
+assert.eq($2.exports.call_h(null), null)
+
+const obj = { test: "hi" }
+assert.eq($2.exports.call_h(obj), obj)
+assert.eq($2.exports.call_h(5), 5)
+assert.eq($2.exports.call_h("hi"), "hi")
+assert.eq($2.exports.call_h(undefined), undefined)
+
+assert.eq($2.exports.call_j(obj), 0)
+assert.eq($2.exports.call_j(5), 0)
+assert.eq($2.exports.call_j("hi"), 0)
+assert.eq($2.exports.call_j(null), 1)
+assert.eq($2.exports.call_j(undefined), 0)
+
+assert.eq($2.exports.call_h_null(), null)
+assert.eq($2.exports.call_j_null(), 1)
+
+assert.eq($2.exports.call_ident(null), null)
+assert.eq($2.exports.call_ident(obj), obj)
+assert.eq($2.exports.call_ident(5), 5)
+assert.eq($2.exports.call_ident("hi"), "hi")
+assert.eq($2.exports.call_ident(undefined), undefined)
+
+for (let i=0; i<1000; ++i) {
+    // Trigger the ic path
+    assert.eq($2.exports.call_ident(null), null)
+    assert.eq($2.exports.call_ident(7), 7)
+    assert.eq($2.exports.call_ident("bye"), "bye")
+}
+
+assert.eq($2.exports.call_ident_null(), null)
+assert.eq($2.exports.is_makenull_null(), 1)
diff --git a/JSTests/wasm/references/is_null.js b/JSTests/wasm/references/is_null.js
new file mode 100644 (file)
index 0000000..52bcbf3
--- /dev/null
@@ -0,0 +1,60 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const builder = (new Builder())
+      .Type().End()
+      .Function().End()
+      .Export()
+          .Function("h")
+          .Function("i")
+          .Function("j")
+          .Function("k")
+      .End()
+      .Code()
+        .Function("h", { params: ["anyref"], ret: "anyref" })
+          .GetLocal(0)
+        .End()
+
+        .Function("i", { params: [], ret: "anyref" })
+            .RefNull()
+            .Call(0)
+        .End()
+
+        .Function("j", { params: ["anyref"], ret: "i32" })
+            .GetLocal(0)
+            .RefIsNull()
+        .End()
+
+        .Function("k", { params: [], ret: "i32" })
+            .RefNull()
+            .RefIsNull()
+        .End()
+      .End();
+
+const bin = builder.WebAssembly().get();
+const module = new WebAssembly.Module(bin);
+const instance = new WebAssembly.Instance(module);
+assert.eq(instance.exports.h(null), null)
+
+const obj = { test: "hi" }
+assert.eq(instance.exports.h(obj), obj)
+assert.eq(instance.exports.h(5), 5)
+assert.eq(instance.exports.h("hi"), "hi")
+assert.eq(instance.exports.h(undefined), undefined)
+
+assert.eq(instance.exports.i(), null)
+
+assert.eq(instance.exports.j(obj), 0)
+assert.eq(instance.exports.j(5), 0)
+assert.eq(instance.exports.j("hi"), 0)
+assert.eq(instance.exports.j(null), 1)
+assert.eq(instance.exports.j(undefined), 0)
+
+assert.eq(instance.exports.k(), 1)
+
+assert.eq(obj.test, "hi")
+const obj2 = instance.exports.h(obj)
+obj2.test = "bye"
+assert.eq(obj.test, "bye")
+
+for (let i=0; i<1000; ++i) assert.eq(instance.exports.h(null), null)
diff --git a/JSTests/wasm/references/is_null_error.js b/JSTests/wasm/references/is_null_error.js
new file mode 100644 (file)
index 0000000..915b36a
--- /dev/null
@@ -0,0 +1,22 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+{
+    const builder = (new Builder())
+      .Type().End()
+      .Function().End()
+      .Export()
+          .Function("j")
+      .End()
+      .Code()
+        .Function("j", { params: [], ret: "i32" })
+            .I32Const(0)
+            .RefIsNull()
+        .End()
+      .End();
+
+    const bin = builder.WebAssembly();
+    bin.trim();
+
+    assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't validate: ref.is_null to type I32 expected Anyref, in function at index 0 (evaluating 'new WebAssembly.Module(bin.get())')");
+}
index bc298d2..eb00c6d 100644 (file)
@@ -190,6 +190,7 @@ function instance(bytes, imports = registry, valid = true) {
     if (valid) {
         uniqueTest(() => {
             let instantiated = err === null;
+            if (!instantiated) print(err);
             assert_true(instantiated, err);
         }, "module successfully instantiated");
     }
index 31d103f..e61aa74 100644 (file)
         "f32":     { "type": "varint7", "value":  -3, "b3type": "B3::Float" },
         "f64":     { "type": "varint7", "value":  -4, "b3type": "B3::Double" },
         "anyfunc": { "type": "varint7", "value": -16, "b3type": "B3::Void" },
+        "anyref":  { "type": "varint7", "value": -17, "b3type": "B3::Int64" },
         "func":    { "type": "varint7", "value": -32, "b3type": "B3::Void" },
         "void":    { "type": "varint7", "value": -64, "b3type": "B3::Void" }
     },
-    "value_type": ["i32", "i64", "f32", "f64"],
-    "block_type": ["i32", "i64", "f32", "f64", "void"],
+    "value_type": ["i32", "i64", "f32", "f64", "anyref"],
+    "block_type": ["i32", "i64", "f32", "f64", "void", "anyref"],
     "elem_type": ["anyfunc"],
     "external_kind": {
         "Function": { "type": "uint8", "value": 0 },
@@ -58,6 +59,8 @@
         "i64.const":           { "category": "special",    "value":  66, "return": ["i64"],      "parameter": [],                       "immediate": [{"name": "value",          "type": "varint64"}],                                             "description": "a constant value interpreted as i64" },
         "f64.const":           { "category": "special",    "value":  68, "return": ["f64"],      "parameter": [],                       "immediate": [{"name": "value",          "type": "double"}],                                               "description": "a constant value interpreted as f64" },
         "f32.const":           { "category": "special",    "value":  67, "return": ["f32"],      "parameter": [],                       "immediate": [{"name": "value",          "type": "float"}],                                                "description": "a constant value interpreted as f32" },
+        "ref.null":            { "category": "special",    "value": 208, "return": ["anyref"],   "parameter": [],                       "immediate": [],                                                                                           "description": "a constant null reference" },
+        "ref.is_null":         { "category": "special",    "value": 209, "return": ["i32"],      "parameter": ["anyref"],               "immediate": [],                                                                                           "description": "determine if a reference is null" },
         "get_local":           { "category": "special",    "value":  32, "return": ["any"],      "parameter": [],                       "immediate": [{"name": "local_index",    "type": "varuint32"}],                                            "description": "read a local variable or parameter" },
         "set_local":           { "category": "special",    "value":  33, "return": [],           "parameter": ["any"],                  "immediate": [{"name": "local_index",    "type": "varuint32"}],                                            "description": "write a local variable or parameter" },
         "tee_local":           { "category": "special",    "value":  34, "return": ["any"],      "parameter": ["any"],                  "immediate": [{"name": "local_index",    "type": "varuint32"}],                                            "description": "write a local variable or parameter and return the same value" },
index 5511733..1851c98 100644 (file)
@@ -1,3 +1,47 @@
+2019-05-17  Justin Michaud  <justin_michaud@apple.com>
+
+        [WASM-References] Add support for Anyref in parameters and return types, Ref.null and Ref.is_null for Anyref values.
+        https://bugs.webkit.org/show_bug.cgi?id=197969
+
+        Reviewed by Keith Miller.
+
+        Add a new runtime option for wasm references.
+        Add support for Anyref as a value type.
+        Add support for Anyref in parameters and return types of Wasm functions. JSValues are marshalled into/out of wasm Anyrefs
+                as a black box, except null which becomes a Nullref value. Nullref is not expressible in the bytecode or in the js API.
+        Add Ref.null and Ref.is_null for Anyref values. Support for these functions with funcrefs is out of scope.
+
+        * runtime/Options.h:
+        * wasm/WasmAirIRGenerator.cpp:
+        (JSC::Wasm::AirIRGenerator::tmpForType):
+        (JSC::Wasm::AirIRGenerator::AirIRGenerator):
+        (JSC::Wasm::AirIRGenerator::addConstant):
+        (JSC::Wasm::AirIRGenerator::addRefIsNull):
+        (JSC::Wasm::AirIRGenerator::addReturn):
+        * wasm/WasmB3IRGenerator.cpp:
+        (JSC::Wasm::B3IRGenerator::addRefIsNull):
+        * wasm/WasmCallingConvention.h:
+        (JSC::Wasm::CallingConventionAir::marshallArgument const):
+        (JSC::Wasm::CallingConventionAir::setupCall const):
+        * wasm/WasmFormat.h:
+        (JSC::Wasm::isValueType):
+        * wasm/WasmFunctionParser.h:
+        (JSC::Wasm::FunctionParser<Context>::FunctionParser):
+        (JSC::Wasm::FunctionParser<Context>::parseExpression):
+        (JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression):
+        * wasm/WasmValidate.cpp:
+        (JSC::Wasm::Validate::addRefIsNull):
+        * wasm/generateWasmOpsHeader.py:
+        (bitSet):
+        * wasm/js/JSToWasm.cpp:
+        (JSC::Wasm::createJSToWasmWrapper):
+        * wasm/js/WasmToJS.cpp:
+        (JSC::Wasm::wasmToJS):
+        * wasm/js/WebAssemblyFunction.cpp:
+        (JSC::callWebAssemblyFunction):
+        (JSC::WebAssemblyFunction::jsCallEntrypointSlow):
+        * wasm/wasm.json:
+
 2019-05-17  Don Olmstead  <don.olmstead@sony.com>
 
         [CMake] Use builtin FindICU
index 6bc1500..f32fa6e 100644 (file)
@@ -502,6 +502,7 @@ constexpr bool enableWebAssemblyStreamingApi = false;
     v(bool, useWebAssemblyStreamingApi, enableWebAssemblyStreamingApi, Normal, "Allow to run WebAssembly's Streaming API") \
     v(bool, useCallICsForWebAssemblyToJSCalls, true, Normal, "If true, we will use CallLinkInfo to inline cache Wasm to JS calls.") \
     v(bool, useEagerWebAssemblyModuleHashing, false, Normal, "Unnamed WebAssembly modules are identified in backtraces through their hash, if available.") \
+    v(bool, useWebAssemblyReferences, true, Normal, "Allow types from the wasm references spec.") \
     v(bool, useBigInt, false, Normal, "If true, we will enable BigInt support.") \
     v(bool, useIntlNumberFormatToParts, enableIntlNumberFormatToParts, Normal, "If true, we will enable Intl.NumberFormat.prototype.formatToParts") \
     v(bool, useIntlPluralRules, enableIntlPluralRules, Normal, "If true, we will enable Intl.PluralRules.") \
index 475e926..f796073 100644 (file)
@@ -41,6 +41,7 @@
 #include "B3ProcedureInlines.h"
 #include "BinarySwitch.h"
 #include "DisallowMacroScratchRegisterUsage.h"
+#include "JSCInlines.h"
 #include "ScratchRegisterAllocator.h"
 #include "VirtualRegister.h"
 #include "WasmCallingConvention.h"
@@ -230,6 +231,8 @@ public:
     ExpressionType addConstant(Type, uint64_t);
     ExpressionType addConstant(BasicBlock*, Type, uint64_t);
 
+    PartialResult WARN_UNUSED_RETURN addRefIsNull(ExpressionType& value, ExpressionType& result);
+
     // Locals
     PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
     PartialResult WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
@@ -360,6 +363,7 @@ private:
         case Type::I32:
             return g32();
         case Type::I64:
+        case Type::Anyref:
             return g64();
         case Type::F32:
             return f32();
@@ -786,6 +790,7 @@ AirIRGenerator::AirIRGenerator(const ModuleInformation& info, B3::Procedure& pro
             append(Move32, arg, m_locals[i]);
             break;
         case Type::I64:
+        case Type::Anyref:
             append(Move, arg, m_locals[i]);
             break;
         case Type::F32:
@@ -902,6 +907,7 @@ auto AirIRGenerator::addConstant(BasicBlock* block, Type type, uint64_t value) -
     switch (type) {
     case Type::I32:
     case Type::I64:
+    case Type::Anyref:
         append(block, Move, Arg::bigImm(value), result);
         break;
     case Type::F32:
@@ -925,6 +931,18 @@ auto AirIRGenerator::addArguments(const Signature& signature) -> PartialResult
     return { };
 }
 
+auto AirIRGenerator::addRefIsNull(ExpressionType& value, ExpressionType& result) -> PartialResult
+{
+    ASSERT(value.tmp());
+    result = tmpForType(Type::I32);
+    auto tmp = g64();
+
+    append(Move, Arg::bigImm(JSValue::encode(jsNull())), tmp);
+    append(Compare64, Arg::relCond(MacroAssembler::Equal), value, tmp, result);
+
+    return { };
+}
+
 auto AirIRGenerator::getLocal(uint32_t index, ExpressionType& result) -> PartialResult
 {
     ASSERT(m_locals[index].tmp());
@@ -1509,6 +1527,7 @@ auto AirIRGenerator::addReturn(const ControlData& data, const ExpressionList& re
             append(Ret32, returnValueGPR);
             break;
         case Type::I64:
+        case Type::Anyref:
             append(Move, returnValues[0], returnValueGPR);
             append(Ret64, returnValueGPR);
             break;
index 15f464f..bb2471f 100644 (file)
@@ -184,6 +184,8 @@ public:
     PartialResult WARN_UNUSED_RETURN addLocal(Type, uint32_t);
     ExpressionType addConstant(Type, uint64_t);
 
+    PartialResult WARN_UNUSED_RETURN addRefIsNull(ExpressionType& value, ExpressionType& result);
+
     // Locals
     PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
     PartialResult WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
@@ -550,6 +552,12 @@ auto B3IRGenerator::addArguments(const Signature& signature) -> PartialResult
     return { };
 }
 
+auto B3IRGenerator::addRefIsNull(ExpressionType& value, ExpressionType& result) -> PartialResult
+{
+    result = m_currentBlock->appendNew<Value>(m_proc, B3::Equal, origin(), value, m_currentBlock->appendNew<Const64Value>(m_proc, origin(), JSValue::encode(jsNull())));
+    return { };
+}
+
 auto B3IRGenerator::getLocal(uint32_t index, ExpressionType& result) -> PartialResult
 {
     ASSERT(m_locals[index]);
index 31866c2..10eb8cd 100644 (file)
@@ -235,6 +235,7 @@ private:
         switch (type) {
         case Type::I32:
         case Type::I64:
+        case Type::Anyref:
             marshallArgumentImpl(m_gprArgs, gpArgumentCount, stackOffset, regFunc, stackFunc);
             break;
         case Type::F32:
@@ -299,6 +300,7 @@ public:
             break;
         case Type::I32:
         case Type::I64:
+        case Type::Anyref:
             patchpoint->resultConstraint = B3::ValueRep::reg(GPRInfo::returnValueGPR);
             break;
         default:
index fc69ee5..b762264 100644 (file)
@@ -62,6 +62,8 @@ inline bool isValueType(Type type)
     case F32:
     case F64:
         return true;
+    case Anyref:
+        return Options::useWebAssemblyReferences();
     default:
         break;
     }
index 2e028e3..175056b 100644 (file)
@@ -105,7 +105,7 @@ FunctionParser<Context>::FunctionParser(Context& context, const uint8_t* functio
     , m_info(info)
 {
     if (verbose)
-        dataLogLn("Parsing function starting at: ", (uintptr_t)functionStart, " of length: ", functionLength);
+        dataLogLn("Parsing function starting at: ", (uintptr_t)functionStart, " of length: ", functionLength, " with signature: ", signature);
     m_context.setParser(this);
 }
 
@@ -281,6 +281,21 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult
         return { };
     }
 
+    case RefNull: {
+        WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled");
+        m_expressionStack.append(m_context.addConstant(Anyref, JSValue::encode(jsNull())));
+        return { };
+    }
+
+    case RefIsNull: {
+        WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled");
+        ExpressionType result, value;
+        WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "ref.is_null");
+        WASM_TRY_ADD_TO_CONTEXT(addRefIsNull(value, result));
+        m_expressionStack.append(result);
+        return { };
+    }
+
     case GetLocal: {
         uint32_t index;
         ExpressionType result;
@@ -639,6 +654,16 @@ auto FunctionParser<Context>::parseUnreachableExpression() -> PartialResult
         return { };
     }
 
+    case RefNull: {
+        WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled");
+        return { };
+    }
+
+    case RefIsNull: {
+        WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled");
+        return { };
+    }
+
     case GrowMemory:
     case CurrentMemory: {
         uint8_t reserved;
index bcd477b..fd54162 100644 (file)
@@ -100,6 +100,8 @@ public:
     Result WARN_UNUSED_RETURN addLocal(Type, uint32_t);
     ExpressionType addConstant(Type type, uint64_t) { return type; }
 
+    Result WARN_UNUSED_RETURN addRefIsNull(ExpressionType& value, ExpressionType& result);
+
     // Locals
     Result WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
     Result WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
@@ -169,6 +171,14 @@ auto Validate::addArguments(const Signature& signature) -> Result
     return { };
 }
 
+auto Validate::addRefIsNull(ExpressionType& value, ExpressionType& result) -> Result
+{
+    result = Type::I32;
+    WASM_VALIDATOR_FAIL_IF(Type::Anyref != value, "ref.is_null to type ", value, " expected ", Type::Anyref);
+
+    return { };
+}
+
 auto Validate::addLocal(Type type, uint32_t count) -> Result
 {
     size_t size = m_locals.size() + count;
index 0b8d7b0..4e9a665 100755 (executable)
@@ -95,7 +95,7 @@ def ceilDiv(a, b):
 
 def bitSet():
     v = ""
-    for i in range(ceilDiv(maxOpValue, 8)):
+    for i in range(ceilDiv(maxOpValue + 1, 8)):
         entry = 0
         for j in range(8):
             if i * 8 + j in opValueSet:
index 774bce0..657da57 100644 (file)
@@ -82,6 +82,7 @@ std::unique_ptr<InternalFunction> createJSToWasmWrapper(CompilationContext& comp
             argumentsIncludeI64 = true;
             FALLTHROUGH;
         case Wasm::I32:
+        case Wasm::Anyref:
             if (numGPRs >= wasmCallingConvention().m_gprArgs.size())
                 totalFrameSize += sizeof(void*);
             ++numGPRs;
@@ -164,6 +165,7 @@ std::unique_ptr<InternalFunction> createJSToWasmWrapper(CompilationContext& comp
             switch (signature.argument(i)) {
             case Wasm::I32:
             case Wasm::I64:
+            case Wasm::Anyref:
                 if (numGPRs >= wasmCallingConvention().m_gprArgs.size()) {
                     if (signature.argument(i) == Wasm::I32) {
                         jit.load32(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchReg);
@@ -247,6 +249,9 @@ std::unique_ptr<InternalFunction> createJSToWasmWrapper(CompilationContext& comp
     case Wasm::Void:
         jit.moveTrustedValue(jsUndefined(), JSValueRegs { GPRInfo::returnValueGPR });
         break;
+    case Wasm::Anyref:
+        // FIXME: We need to box wasm Funcrefs once they are supported here.
+        break;
     case Wasm::I32:
         jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
         jit.boxInt32(GPRInfo::returnValueGPR, JSValueRegs { GPRInfo::returnValueGPR }, DoNotHaveTagRegisters);
index 572b030..5bff9cc 100644 (file)
@@ -165,6 +165,7 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm
             case Anyfunc:
             case I64:
                 RELEASE_ASSERT_NOT_REACHED();
+            case Anyref:
             case I32: {
                 GPRReg gprReg;
                 if (marshalledGPRs < wasmCC.m_gprArgs.size())
@@ -175,7 +176,8 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm
                     jit.load64(JIT::Address(GPRInfo::callFrameRegister, frOffset), gprReg);
                     frOffset += sizeof(Register);
                 }
-                jit.zeroExtend32ToPtr(gprReg, gprReg);
+                if (argType == I32)
+                    jit.zeroExtend32ToPtr(gprReg, gprReg);
                 jit.store64(gprReg, buffer + bufferOffset);
                 ++marshalledGPRs;
                 break;
@@ -241,6 +243,10 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm
                     case I32:
                         arg = jsNumber(static_cast<int32_t>(buffer[argNum]));
                         break;
+                    case Anyref:
+                        // FIXME: We need to box wasm Funcrefs once they are supported here.
+                        arg = JSValue::decode(buffer[argNum]);
+                        break;
                     case F32:
                     case F64:
                         arg = jsNumber(purifyNaN(bitwise_cast<double>(buffer[argNum])));
@@ -272,6 +278,10 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm
                     realResult = static_cast<uint64_t>(static_cast<uint32_t>(result.toInt32(exec)));
                     break;
                 }
+                case Anyref: {
+                    realResult = JSValue::encode(result);
+                    break;
+                }
                 case F64:
                 case F32: {
                     realResult = bitwise_cast<uint64_t>(result.toNumber(exec));
@@ -364,6 +374,7 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm
             case Anyfunc:
             case I64:
                 RELEASE_ASSERT_NOT_REACHED(); // Handled above.
+            case Anyref:
             case I32: {
                 GPRReg gprReg;
                 if (marshalledGPRs < wasmCC.m_gprArgs.size())
@@ -375,8 +386,11 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm
                     frOffset += sizeof(Register);
                 }
                 ++marshalledGPRs;
-                jit.zeroExtend32ToPtr(gprReg, gprReg); // Clear non-int32 and non-tag bits.
-                jit.boxInt32(gprReg, JSValueRegs(gprReg), DoNotHaveTagRegisters);
+                if (argType == I32) {
+                    jit.zeroExtend32ToPtr(gprReg, gprReg); // Clear non-int32 and non-tag bits.
+                    jit.boxInt32(gprReg, JSValueRegs(gprReg), DoNotHaveTagRegisters);
+                }
+                // FIXME: We need to box wasm Funcrefs once they are supported here.
                 jit.store64(gprReg, calleeFrame.withOffset(calleeFrameOffset));
                 calleeFrameOffset += sizeof(Register);
                 break;
@@ -430,6 +444,7 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm
             case Anyfunc:
             case I64:
                 RELEASE_ASSERT_NOT_REACHED(); // Handled above.
+            case Anyref:
             case I32:
                 // Skipped: handled above.
                 if (marshalledGPRs >= wasmCC.m_gprArgs.size())
@@ -538,6 +553,8 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm
         done.link(&jit);
         break;
     }
+    case Anyref:
+        break;
     case F32: {
         CCallHelpers::JumpList done;
 
index 6dcec80..2ff0e78 100644 (file)
@@ -84,6 +84,8 @@ static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec)
         case Wasm::I32:
             arg = JSValue::decode(arg.toInt32(exec));
             break;
+        case Wasm::Anyref:
+            break;
         case Wasm::I64:
             arg = JSValue();
             break;
@@ -225,6 +227,7 @@ MacroAssemblerCodePtr<JSEntryPtrTag> WebAssemblyFunction::jsCallEntrypointSlow()
         case Wasm::I64:
             argumentsIncludeI64 = true;
             break;
+        case Wasm::Anyref:
         case Wasm::I32:
             if (numGPRs >= Wasm::wasmCallingConvention().m_gprArgs.size())
                 totalFrameSize += sizeof(CPURegister);
@@ -300,6 +303,18 @@ MacroAssemblerCodePtr<JSEntryPtrTag> WebAssemblyFunction::jsCallEntrypointSlow()
                     ++numGPRs;
                 }
                 break;
+            case Wasm::Anyref: {
+                jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchGPR);
+
+                if (numGPRs >= Wasm::wasmCallingConvention().m_gprArgs.size()) {
+                    jit.store64(scratchGPR, calleeFrame.withOffset(wasmOffset));
+                    wasmOffset += sizeof(CPURegister);
+                } else {
+                    jit.move(scratchGPR, Wasm::wasmCallingConvention().m_gprArgs[numGPRs].gpr());
+                    ++numGPRs;
+                }
+                break;
+            }
             case Wasm::F32:
             case Wasm::F64:
                 if (numFPRs >= Wasm::wasmCallingConvention().m_fprArgs.size()) {
@@ -451,6 +466,10 @@ MacroAssemblerCodePtr<JSEntryPtrTag> WebAssemblyFunction::jsCallEntrypointSlow()
         isNaN.link(&jit);
         break;
     }
+    case Wasm::Anyref: {
+        // FIXME: We need to box wasm Funcrefs once they are supported here.
+        break;
+    }
     case Wasm::I64:
     case Wasm::Func:
     case Wasm::Anyfunc:
index 31d103f..e61aa74 100644 (file)
         "f32":     { "type": "varint7", "value":  -3, "b3type": "B3::Float" },
         "f64":     { "type": "varint7", "value":  -4, "b3type": "B3::Double" },
         "anyfunc": { "type": "varint7", "value": -16, "b3type": "B3::Void" },
+        "anyref":  { "type": "varint7", "value": -17, "b3type": "B3::Int64" },
         "func":    { "type": "varint7", "value": -32, "b3type": "B3::Void" },
         "void":    { "type": "varint7", "value": -64, "b3type": "B3::Void" }
     },
-    "value_type": ["i32", "i64", "f32", "f64"],
-    "block_type": ["i32", "i64", "f32", "f64", "void"],
+    "value_type": ["i32", "i64", "f32", "f64", "anyref"],
+    "block_type": ["i32", "i64", "f32", "f64", "void", "anyref"],
     "elem_type": ["anyfunc"],
     "external_kind": {
         "Function": { "type": "uint8", "value": 0 },
@@ -58,6 +59,8 @@
         "i64.const":           { "category": "special",    "value":  66, "return": ["i64"],      "parameter": [],                       "immediate": [{"name": "value",          "type": "varint64"}],                                             "description": "a constant value interpreted as i64" },
         "f64.const":           { "category": "special",    "value":  68, "return": ["f64"],      "parameter": [],                       "immediate": [{"name": "value",          "type": "double"}],                                               "description": "a constant value interpreted as f64" },
         "f32.const":           { "category": "special",    "value":  67, "return": ["f32"],      "parameter": [],                       "immediate": [{"name": "value",          "type": "float"}],                                                "description": "a constant value interpreted as f32" },
+        "ref.null":            { "category": "special",    "value": 208, "return": ["anyref"],   "parameter": [],                       "immediate": [],                                                                                           "description": "a constant null reference" },
+        "ref.is_null":         { "category": "special",    "value": 209, "return": ["i32"],      "parameter": ["anyref"],               "immediate": [],                                                                                           "description": "determine if a reference is null" },
         "get_local":           { "category": "special",    "value":  32, "return": ["any"],      "parameter": [],                       "immediate": [{"name": "local_index",    "type": "varuint32"}],                                            "description": "read a local variable or parameter" },
         "set_local":           { "category": "special",    "value":  33, "return": [],           "parameter": ["any"],                  "immediate": [{"name": "local_index",    "type": "varuint32"}],                                            "description": "write a local variable or parameter" },
         "tee_local":           { "category": "special",    "value":  34, "return": ["any"],      "parameter": ["any"],                  "immediate": [{"name": "local_index",    "type": "varuint32"}],                                            "description": "write a local variable or parameter and return the same value" },
index 7a9ef1c..20f2f23 100644 (file)
@@ -1,3 +1,14 @@
+2019-05-17  Justin Michaud  <justin_michaud@apple.com>
+
+        [WASM-References] Add support for Anyref in parameters and return types, Ref.null and Ref.is_null for Anyref values.
+        https://bugs.webkit.org/show_bug.cgi?id=197969
+
+        Run wasm tests additionally with wasmBBQUsesAir=0.
+
+        Reviewed by Keith Miller.
+
+        * Scripts/run-jsc-stress-tests:
+
 2019-05-17  Don Olmstead  <don.olmstead@sony.com>
 
         [CMake] Use builtin FindICU
index a7be368..2703c40 100755 (executable)
@@ -1080,6 +1080,7 @@ def runWebAssembly
         run("wasm-no-call-ic", "-m", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
         run("wasm-no-tls-context", "-m", "--useFastTLSForWasmContext=false", *FTL_OPTIONS)
         run("wasm-slow-memory", "-m", "--useWebAssemblyFastMemory=false", *FTL_OPTIONS)
+        run("wasm-no-air", "-m", "--wasmBBQUsesAir=false", *FTL_OPTIONS)
     end
 end
 
@@ -1096,6 +1097,7 @@ def runWebAssemblySuite
         run("wasm-no-call-ic", "-m", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
         run("wasm-no-tls-context", "-m", "--useFastTLSForWasmContext=false", *FTL_OPTIONS)
         run("wasm-slow-memory", "-m", "--useWebAssemblyFastMemory=false", *FTL_OPTIONS)
+        run("wasm-no-air", "-m", "--wasmBBQUsesAir=false", *FTL_OPTIONS)
     end
 end
 
@@ -1114,6 +1116,7 @@ def runWebAssemblyEmscripten(mode)
         run("wasm-eager-jettison", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
         run("wasm-no-call-ic", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
         run("wasm-no-tls-context", "--useFastTLSForWasmContext=false", *FTL_OPTIONS)
+        run("wasm-no-air", "--wasmBBQUsesAir=false", *FTL_OPTIONS)
     end
 end
 
@@ -1138,6 +1141,7 @@ def runWebAssemblySpecTest(mode)
       runWithOutputHandler("wasm-eager-jettison", noisyOutputHandler, "../spec-harness.js", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
       runWithOutputHandler("wasm-no-call-ic", noisyOutputHandler, "../spec-harness.js", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
       runWithOutputHandler("wasm-no-tls-context", noisyOutputHandler, "../spec-harness.js", "--useFastTLSForWasmContext=false", *FTL_OPTIONS)
+      runWithOutputHandler("wasm-no-air", noisyOutputHandler, "../spec-harness.js", "--wasmBBQUsesAir=false", *FTL_OPTIONS)
     end
 end