Changeset 209642 in webkit


Ignore:
Timestamp:
Dec 9, 2016 6:34:02 PM (7 years ago)
Author:
jfbastien@apple.com
Message:

WebAssembly JS API: implement start function
https://bugs.webkit.org/show_bug.cgi?id=165150

Reviewed by Saam Barati.

JSTests:

  • wasm/Builder.js: allow building a .Start()
  • wasm/Builder_WebAssemblyBinary.js:
  • wasm/js-api/test_Start.js: Added.

(const.emitters.Start): serialize a start section

  • wasm/self-test/test_BuilderJSON.js: validate the start section's content

Source/JavaScriptCore:

  • wasm/WasmFormat.h: pass the start function around
  • wasm/WasmModuleParser.cpp:

(JSC::Wasm::ModuleParser::parseTable): mark unreachable code
(JSC::Wasm::ModuleParser::parseGlobal): mark unreachable code
(JSC::Wasm::ModuleParser::parseStart): mark unreachable code
(JSC::Wasm::ModuleParser::parseElement): mark unreachable code
(JSC::Wasm::ModuleParser::parseData): mark unreachable code

  • wasm/js/WebAssemblyFunction.cpp:

(JSC::callWebAssemblyFunction): NFC: call the new function below
(JSC::WebAssemblyFunction::call): separate this out so that the start function can use it

  • wasm/js/WebAssemblyFunction.h:
  • wasm/js/WebAssemblyModuleRecord.cpp:

(JSC::WebAssemblyModuleRecord::visitChildren): visit the start function
(JSC::WebAssemblyModuleRecord::link): handle start function
(JSC::WebAssemblyModuleRecord::evaluate): call the start function, if present

  • wasm/js/WebAssemblyModuleRecord.h:
Location:
trunk
Files:
1 added
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r209630 r209642  
     12016-12-09  JF Bastien  <jfbastien@apple.com>
     2
     3        WebAssembly JS API: implement start function
     4        https://bugs.webkit.org/show_bug.cgi?id=165150
     5
     6        Reviewed by Saam Barati.
     7
     8        * wasm/Builder.js: allow building a .Start()
     9        * wasm/Builder_WebAssemblyBinary.js:
     10        * wasm/js-api/test_Start.js: Added.
     11        (const.emitters.Start): serialize a start section
     12        * wasm/self-test/test_BuilderJSON.js: validate the start section's content
     13
    1142016-12-09  Saam Barati  <sbarati@apple.com>
    215
  • trunk/JSTests/wasm/Builder.js

    r209630 r209642  
    438438
    439439            case "Start":
    440                 // FIXME implement start https://bugs.webkit.org/show_bug.cgi?id=161709
    441                 this[section] = () => { throw new Error(`Unimplemented: section type "${section}"`); };
     440                this[section] = function(functionIndexOrName) {
     441                    const s = this._addSection(section);
     442                    const startBuilder = {
     443                        End: () => this,
     444                    };
     445                    if (typeof(functionIndexOrName) !== "number" && typeof(functionIndexOrName) !== "string")
     446                        throw new Error(`Start section's function index  must either be a number or a string`);
     447                    s.data.push(functionIndexOrName);
     448                    return startBuilder;
     449                };
    442450                break;
    443451
     
    457465                            const importSection = builder._getSection("Import");
    458466                            const exportSection = builder._getSection("Export");
     467                            const startSection = builder._getSection("Start");
    459468                            const codeSection = s;
    460469                            if (exportSection) {
     
    489498                                }
    490499                            }
     500                            if (startSection) {
     501                                const start = startSection.data[0];
     502                                let mapped = builder._getFunctionFromIndexSpace(start);
     503                                if (!builder._checked) {
     504                                    if (typeof(mapped) === "undefined")
     505                                        mapped = start; // In unchecked mode, simply use what was provided if it's nonsensical.
     506                                    assert.isA(start, "number"); // It can't be too nonsensical, otherwise we can't create a binary.
     507                                    startSection.data[0] = start;
     508                                } else {
     509                                    if (typeof(mapped) === "undefined")
     510                                        throw new Error(`Start section refers to non-existant function '${start}'`);
     511                                    if (typeof(start) === "string" || typeof(start) === "object")
     512                                        startSection.data[0] = mapped;
     513                                    // FIXME in checked mode, test that the type is acceptable for start function. We probably want _registerFunctionToIndexSpace to also register types per index. https://bugs.webkit.org/show_bug.cgi?id=165658
     514                                }
     515                            }
    491516                            return builder;
    492517                        },
  • trunk/JSTests/wasm/Builder_WebAssemblyBinary.js

    r209630 r209642  
    116116        }
    117117    },
    118     Start: (section, bin) => { throw new Error(`Not yet implemented`); },
     118    Start: (section, bin) => {
     119        put(bin, "varuint32", section.data[0]);
     120    },
    119121    Element: (section, bin) => { throw new Error(`Not yet implemented`); },
    120122
  • trunk/JSTests/wasm/self-test/test_BuilderJSON.js

    r209630 r209642  
    241241    assert.throws(() => e.Function("foo", 0, { params: ["i32"] }), Error, `Not the same: "1" and "0": Re-exporting import "bar" as "foo" has mismatching type`);
    242242})();
     243
     244(function StartInvalidNumberedFunction() {
     245    const b = (new Builder())
     246        .Type().End()
     247        .Function().End()
     248        .Start(0).End()
     249    assert.throws(() => b.Code().End(), Error, `Start section refers to non-existant function '0'`);
     250})();
     251
     252(function StartInvalidNamedFunction() {
     253    const b = (new Builder())
     254        .Type().End()
     255        .Function().End()
     256        .Start("foo").End();
     257    assert.throws(() => b.Code().End(), Error, `Start section refers to non-existant function 'foo'`);
     258})();
     259
     260(function StartNamedFunction() {
     261    const b = (new Builder())
     262        .Type().End()
     263        .Function().End()
     264        .Start("foo").End()
     265        .Code()
     266            .Function("foo", { params: [] }).End()
     267        .End();
     268    const j = JSON.parse(b.json());
     269    assert.eq(j.section.length, 4);
     270    assert.eq(j.section[2].name, "Start");
     271    assert.eq(j.section[2].data.length, 1);
     272    assert.eq(j.section[2].data[0], 0);
     273})();
     274
     275/* FIXME implement checking of signature https://bugs.webkit.org/show_bug.cgi?id=165658
     276(function StartInvalidTypeArg() {
     277    const b = (new Builder())
     278        .Type().End()
     279        .Function().End()
     280        .Start("foo").End()
     281    assert.throws(() => b.Code().Function("foo", { params: ["i32"] }).End(), Error, `???`);
     282})();
     283
     284(function StartInvalidTypeReturn() {
     285    const b = (new Builder())
     286        .Type().End()
     287        .Function().End()
     288        .Start("foo").End()
     289    assert.throws(() => b.Code().Function("foo", { params: [], ret: "i32" }).I32Const(42).Ret().End(), Error, `???`);
     290})();
     291*/
     292
     293// FIXME test start of import or table. https://bugs.webkit.org/show_bug.cgi?id=165658
    243294
    244295(function EmptyCodeSection() {
  • trunk/Source/JavaScriptCore/ChangeLog

    r209638 r209642  
     12016-12-09  JF Bastien  <jfbastien@apple.com>
     2
     3        WebAssembly JS API: implement start function
     4        https://bugs.webkit.org/show_bug.cgi?id=165150
     5
     6        Reviewed by Saam Barati.
     7
     8        * wasm/WasmFormat.h: pass the start function around
     9        * wasm/WasmModuleParser.cpp:
     10        (JSC::Wasm::ModuleParser::parseTable): mark unreachable code
     11        (JSC::Wasm::ModuleParser::parseGlobal): mark unreachable code
     12        (JSC::Wasm::ModuleParser::parseStart): mark unreachable code
     13        (JSC::Wasm::ModuleParser::parseElement): mark unreachable code
     14        (JSC::Wasm::ModuleParser::parseData): mark unreachable code
     15        * wasm/js/WebAssemblyFunction.cpp:
     16        (JSC::callWebAssemblyFunction): NFC: call the new function below
     17        (JSC::WebAssemblyFunction::call): separate this out so that the start function can use it
     18        * wasm/js/WebAssemblyFunction.h:
     19        * wasm/js/WebAssemblyModuleRecord.cpp:
     20        (JSC::WebAssemblyModuleRecord::visitChildren): visit the start function
     21        (JSC::WebAssemblyModuleRecord::link): handle start function
     22        (JSC::WebAssemblyModuleRecord::evaluate): call the start function, if present
     23        * wasm/js/WebAssemblyModuleRecord.h:
     24
    1252016-12-09  Filip Pizlo  <fpizlo@apple.com>
    226
  • trunk/Source/JavaScriptCore/wasm/WasmFormat.cpp

    r209630 r209642  
    3434namespace JSC { namespace Wasm {
    3535
     36JS_EXPORT_PRIVATE ModuleInformation::~ModuleInformation() { }
     37
    3638} } // namespace JSC::Wasm
    3739
  • trunk/Source/JavaScriptCore/wasm/WasmFormat.h

    r209630 r209642  
    3636#include "WasmOps.h"
    3737#include "WasmPageCount.h"
     38#include <wtf/Optional.h>
    3839#include <wtf/Vector.h>
    3940
     
    124125    MemoryInformation memory;
    125126    Vector<Export> exports;
     127    std::optional<uint32_t> startFunctionIndexSpace;
     128
     129    ~ModuleInformation();
    126130};
    127131
  • trunk/Source/JavaScriptCore/wasm/WasmModuleParser.cpp

    r209630 r209642  
    304304bool ModuleParser::parseTable()
    305305{
    306     // FIXME
     306    // FIXME implement table https://bugs.webkit.org/show_bug.cgi?id=164135
     307    RELEASE_ASSERT_NOT_REACHED();
    307308    return true;
    308309}
     
    366367{
    367368    // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
     369    RELEASE_ASSERT_NOT_REACHED();
    368370    return true;
    369371}
     
    416418bool ModuleParser::parseStart()
    417419{
     420    uint32_t startFunctionIndex;
     421    if (!parseVarUInt32(startFunctionIndex)
     422        || startFunctionIndex >= m_functionIndexSpace.size())
     423        return false;
     424    Signature* signature = m_functionIndexSpace[startFunctionIndex].signature;
     425    if (signature->arguments.size() != 0
     426        || signature->returnType != Void)
     427        return false;
     428    m_module->startFunctionIndexSpace = startFunctionIndex;
     429    return true;
     430}
     431
     432bool ModuleParser::parseElement()
     433{
    418434    // FIXME https://bugs.webkit.org/show_bug.cgi?id=161709
    419     return true;
    420 }
    421 
    422 bool ModuleParser::parseElement()
    423 {
    424     // FIXME https://bugs.webkit.org/show_bug.cgi?id=161709
     435    RELEASE_ASSERT_NOT_REACHED();
    425436    return true;
    426437}
     
    451462{
    452463    // FIXME https://bugs.webkit.org/show_bug.cgi?id=161709
     464    RELEASE_ASSERT_NOT_REACHED();
    453465    return true;
    454466}
  • trunk/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp

    r209630 r209642  
    9393    }
    9494
     95    // Note: we specifically use the WebAssemblyFunction as the callee to begin with in the ProtoCallFrame.
     96    // The reason for this is that calling into the llint may stack overflow, and the stack overflow
     97    // handler might read the global object from the callee. The JSWebAssemblyCallee doesn't have a
     98    // global object, but the WebAssemblyFunction does.
     99    ProtoCallFrame protoCallFrame;
     100    protoCallFrame.init(nullptr, wasmFunction, firstArgument, argCount, remainingArgs);
     101
     102    return wasmFunction->call(vm, &protoCallFrame);
     103}
     104
     105EncodedJSValue WebAssemblyFunction::call(VM& vm, ProtoCallFrame* protoCallFrame)
     106{
    95107    // Setup the memory that the entrance loads.
    96     if (JSWebAssemblyMemory* memory = wasmFunction->instance()->memory()) {
     108    if (JSWebAssemblyMemory* memory = instance()->memory()) {
    97109        Wasm::Memory* wasmMemory = memory->memory();
    98110        vm.topWasmMemoryPointer = wasmMemory->memory();
     
    103115    }
    104116
    105     // Note: we specifically use the WebAsseblyFunction as the callee to begin with in the ProtoCallFrame.
    106     // The reason for this is that calling into the llint may stack overflow, and the stack overflow
    107     // handler might read the global object from the callee. The JSWebAssemblyCallee doesn't have a
    108     // global object, but the WebAssemblyFunction does.
    109     ProtoCallFrame protoCallFrame;
    110     protoCallFrame.init(nullptr, wasmFunction, firstArgument, argCount, remainingArgs);
    111    
    112117    JSWebAssemblyInstance* prevJSWebAssemblyInstance = vm.topJSWebAssemblyInstance;
    113     vm.topJSWebAssemblyInstance = wasmFunction->instance();
    114     EncodedJSValue rawResult = vmEntryToWasm(wasmFunction->webAssemblyCallee()->jsToWasmEntryPoint(), &vm, &protoCallFrame);
     118    vm.topJSWebAssemblyInstance = instance();
     119    EncodedJSValue rawResult = vmEntryToWasm(webAssemblyCallee()->jsToWasmEntryPoint(), &vm, protoCallFrame);
    115120    vm.topJSWebAssemblyInstance = prevJSWebAssemblyInstance;
    116121
    117122    // FIXME is this correct? https://bugs.webkit.org/show_bug.cgi?id=164876
    118     switch (signature->returnType) {
     123    switch (signature()->returnType) {
    119124    case Wasm::Void:
    120125        return JSValue::encode(jsUndefined());
  • trunk/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.h

    r209560 r209642  
    3535class JSGlobalObject;
    3636class JSWebAssemblyCallee;
     37struct ProtoCallFrame;
    3738class WebAssemblyInstance;
    3839
     
    6364        return m_signature;
    6465    }
     66    EncodedJSValue call(VM&, ProtoCallFrame*);
    6567
    6668protected:
  • trunk/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp

    r209630 r209642  
    3535#include "JSWebAssemblyInstance.h"
    3636#include "JSWebAssemblyModule.h"
     37#include "ProtoCallFrame.h"
    3738#include "WasmFormat.h"
    3839#include "WebAssemblyFunction.h"
     40#include <limits>
    3941
    4042namespace JSC {
     
    9799    Base::visitChildren(thisObject, visitor);
    98100    visitor.append(&thisObject->m_instance);
     101    visitor.append(&thisObject->m_startFunction);
    99102}
    100103
     
    108111    JSWebAssemblyModule* module = instance->module();
    109112    const Wasm::ModuleInformation& moduleInformation = module->moduleInformation();
     113
     114    bool hasStart = !!moduleInformation.startFunctionIndexSpace;
     115    auto startFunctionIndexSpace = moduleInformation.startFunctionIndexSpace.value_or(0);
     116
    110117    SymbolTable* exportSymbolTable = module->exportSymbolTable();
    111118    unsigned importCount = module->importCount();
     
    134141            WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->arguments.size(), exp.field.string(), instance, wasmCallee, signature);
    135142            exportedValue = function;
     143            if (hasStart && startFunctionIndexSpace == exp.functionIndex)
     144                m_startFunction.set(vm, this, function);
    136145            break;
    137146        }
     
    158167    }
    159168
     169    if (hasStart) {
     170        Wasm::Signature* signature = module->signatureForFunctionIndexSpace(startFunctionIndexSpace);
     171        // The start function must not take any arguments or return anything. This is enforced by the parser.
     172        ASSERT(!signature->arguments.size());
     173        ASSERT(signature->returnType == Wasm::Void);
     174        // FIXME can start call imports / tables? This assumes not. https://github.com/WebAssembly/design/issues/896
     175        if (!m_startFunction.get()) {
     176            // The start function wasn't added above. It must be a purely internal function.
     177            JSWebAssemblyCallee* wasmCallee = module->calleeFromFunctionIndexSpace(startFunctionIndexSpace);
     178            WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->arguments.size(), "start", instance, wasmCallee, signature);
     179            m_startFunction.set(vm, this, function);
     180        }
     181    }
     182
    160183    RELEASE_ASSERT(!m_instance);
    161184    m_instance.set(vm, this, instance);
     
    165188JSValue WebAssemblyModuleRecord::evaluate(ExecState* state)
    166189{
    167     // FIXME this should call the module's `start` function, if any. https://bugs.webkit.org/show_bug.cgi?id=165150
    168     // https://github.com/WebAssembly/design/blob/master/Modules.md#module-start-function
    169     // The start function must not take any arguments or return anything
    170     UNUSED_PARAM(state);
     190    if (WebAssemblyFunction* startFunction = m_startFunction.get()) {
     191        VM& vm = state->vm();
     192        auto scope = DECLARE_THROW_SCOPE(vm);
     193        ProtoCallFrame protoCallFrame;
     194        protoCallFrame.init(nullptr, startFunction, JSValue(), 1, nullptr);
     195        startFunction->call(vm, &protoCallFrame);
     196        RETURN_IF_EXCEPTION(scope, { });
     197    }
    171198    return jsUndefined();
    172199}
  • trunk/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.h

    r209171 r209642  
    3434
    3535class JSWebAssemblyInstance;
     36class WebAssemblyFunction;
    3637
    3738// Based on the WebAssembly.Instance specification
     
    5960
    6061    WriteBarrier<JSWebAssemblyInstance> m_instance;
     62    WriteBarrier<WebAssemblyFunction> m_startFunction;
    6163};
    6264
Note: See TracChangeset for help on using the changeset viewer.