Changeset 207671 in webkit


Ignore:
Timestamp:
Oct 21, 2016 9:02:39 AM (7 years ago)
Author:
keith_miller@apple.com
Message:

Add support for WASM calls
https://bugs.webkit.org/show_bug.cgi?id=161727

Reviewed by Filip Pizlo and Michael Saboff.

JSTests:

Add members of the Call category to the WASMOps special group.

  • wasm/generate-wasmops-header.js:

Source/JavaScriptCore:

Add support for WASM calls. Since most of the work for this was already done when we added
WASM Memory, this is mostly just cleanup work. The main interesting part of this patch is
how we link calls to other WASM functions in the same module. Since a WASM callee may not
have been compiled by the time the current function has started compilation we don't know
what address we need to call to. For each callsite in the compiling function, WASM
remembers the CodeLocationCall and the target function index. Once all WASM functions are
compiled, each callsite is linked to the appropriate entrypoint.

  • testWASM.cpp:

(runWASMTests):

  • wasm/WASMB3IRGenerator.cpp:

(JSC::WASM::createJSWrapper):
(JSC::WASM::parseAndCompile):

  • wasm/WASMB3IRGenerator.h:
  • wasm/WASMCallingConvention.cpp:

(JSC::WASM::jscCallingConvention):
(JSC::WASM::wasmCallingConvention):

  • wasm/WASMCallingConvention.h:

(JSC::WASM::CallingConvention::CallingConvention):
(JSC::WASM::CallingConvention::marshallArgumentImpl):
(JSC::WASM::CallingConvention::marshallArgument):
(JSC::WASM::CallingConvention::loadArguments):
(JSC::WASM::CallingConvention::setupCall):
(JSC::WASM::CallingConvention::iterate): Deleted.

  • wasm/WASMFormat.h:
  • wasm/WASMFunctionParser.h:

(JSC::WASM::FunctionParser<Context>::FunctionParser):
(JSC::WASM::FunctionParser<Context>::parseBlock):
(JSC::WASM::FunctionParser<Context>::parseExpression):

  • wasm/WASMModuleParser.cpp:

(JSC::WASM::ModuleParser::parse):

  • wasm/WASMOps.h:
  • wasm/WASMParser.h:

(JSC::WASM::Parser::parseVarUInt32):
(JSC::WASM::Parser::parseVarUInt64):

  • wasm/WASMPlan.cpp:

(JSC::WASM::Plan::Plan):

Source/WTF:

Added a new decodeUInt64. Also, added WTF::LEBDecoder namespace.

  • wtf/LEBDecoder.h:

(WTF::LEBDecoder::decodeUInt):
(WTF::LEBDecoder::decodeUInt32):
(WTF::LEBDecoder::decodeUInt64):
(WTF::LEBDecoder::decodeInt32):
(decodeUInt32): Deleted.
(decodeInt32): Deleted.

Location:
trunk
Files:
16 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r207652 r207671  
     12016-10-20  Keith Miller  <keith_miller@apple.com>
     2
     3        Add support for WASM calls
     4        https://bugs.webkit.org/show_bug.cgi?id=161727
     5
     6        Reviewed by Filip Pizlo and Michael Saboff.
     7
     8        Add members of the Call category to the WASMOps special group.
     9
     10        * wasm/generate-wasmops-header.js:
     11
    1122016-10-20  Yusuke Suzuki  <utatane.tea@gmail.com>
    213
  • trunk/JSTests/wasm/generate-wasmops-header.js

    r207453 r207671  
    3030const defines = [
    3131    "#define FOR_EACH_WASM_SPECIAL_OP(macro)",
    32     ...opcodeMacroizer(op => op.category === "special"),
     32    ...opcodeMacroizer(op => op.category === "special" || op.category === "call"),
    3333    "\n\n#define FOR_EACH_WASM_CONTROL_FLOW_OP(macro)",
    3434    ...opcodeMacroizer(op => op.category === "control"),
  • trunk/Source/JavaScriptCore/ChangeLog

    r207670 r207671  
     12016-10-20  Keith Miller  <keith_miller@apple.com>
     2
     3        Add support for WASM calls
     4        https://bugs.webkit.org/show_bug.cgi?id=161727
     5
     6        Reviewed by Filip Pizlo and Michael Saboff.
     7
     8        Add support for WASM calls. Since most of the work for this was already done when we added
     9        WASM Memory, this is mostly just cleanup work.  The main interesting part of this patch is
     10        how we link calls to other WASM functions in the same module. Since a WASM callee may not
     11        have been compiled by the time the current function has started compilation we don't know
     12        what address we need to call to.  For each callsite in the compiling function, WASM
     13        remembers the CodeLocationCall and the target function index. Once all WASM functions are
     14        compiled, each callsite is linked to the appropriate entrypoint.
     15
     16        * testWASM.cpp:
     17        (runWASMTests):
     18        * wasm/WASMB3IRGenerator.cpp:
     19        (JSC::WASM::createJSWrapper):
     20        (JSC::WASM::parseAndCompile):
     21        * wasm/WASMB3IRGenerator.h:
     22        * wasm/WASMCallingConvention.cpp:
     23        (JSC::WASM::jscCallingConvention):
     24        (JSC::WASM::wasmCallingConvention):
     25        * wasm/WASMCallingConvention.h:
     26        (JSC::WASM::CallingConvention::CallingConvention):
     27        (JSC::WASM::CallingConvention::marshallArgumentImpl):
     28        (JSC::WASM::CallingConvention::marshallArgument):
     29        (JSC::WASM::CallingConvention::loadArguments):
     30        (JSC::WASM::CallingConvention::setupCall):
     31        (JSC::WASM::CallingConvention::iterate): Deleted.
     32        * wasm/WASMFormat.h:
     33        * wasm/WASMFunctionParser.h:
     34        (JSC::WASM::FunctionParser<Context>::FunctionParser):
     35        (JSC::WASM::FunctionParser<Context>::parseBlock):
     36        (JSC::WASM::FunctionParser<Context>::parseExpression):
     37        * wasm/WASMModuleParser.cpp:
     38        (JSC::WASM::ModuleParser::parse):
     39        * wasm/WASMOps.h:
     40        * wasm/WASMParser.h:
     41        (JSC::WASM::Parser::parseVarUInt32):
     42        (JSC::WASM::Parser::parseVarUInt64):
     43        * wasm/WASMPlan.cpp:
     44        (JSC::WASM::Plan::Plan):
     45
    1462016-10-21  Wenson Hsieh  <wenson_hsieh@apple.com>
    247
  • trunk/Source/JavaScriptCore/testWASM.cpp

    r207453 r207671  
    136136        size_t offset = startOffset; \
    137137        uint32_t result; \
    138         bool status = decodeUInt32(vector.data(), vector.size(), offset, result); \
     138        bool status = WTF::LEBDecoder::decodeUInt32(vector.data(), vector.size(), offset, result); \
    139139        CHECK_EQ(status, expectedStatus); \
    140140        if (expectedStatus) { \
     
    184184        size_t offset = startOffset; \
    185185        int32_t result; \
    186         bool status = decodeInt32(vector.data(), vector.size(), offset, result); \
     186        bool status = WTF::LEBDecoder::decodeInt32(vector.data(), vector.size(), offset, result); \
    187187        CHECK_EQ(status, expectedStatus); \
    188188        if (expectedStatus) { \
     
    243243static void runWASMTests()
    244244{
     245    {
     246        // Generated from:
     247        //    (module
     248        //     (memory 1)
     249        //     (func $sum12 (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32) (return (i32.add (get_local 0) (i32.add (get_local 1) (i32.add (get_local 2) (i32.add (get_local 3) (i32.add (get_local 4) (i32.add (get_local 5) (i32.add (get_local 6) (i32.add (get_local 7) (i32.add (get_local 8) (i32.add (get_local 9) (i32.add (get_local 10) (get_local 11))))))))))))))
     250        //     (func (export "mult12") (param i32) (result i32) (return (call $sum12 (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0) (get_local 0))))
     251        //     )
     252
     253        Vector<uint8_t> vector = {
     254            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x96, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40,
     255            0x0c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40,
     256            0x01, 0x01, 0x01, 0x01, 0x03, 0x83, 0x80, 0x80, 0x80, 0x00, 0x02, 0x00, 0x01, 0x05, 0x83, 0x80,
     257            0x80, 0x80, 0x00, 0x01, 0x00, 0x01, 0x07, 0x8a, 0x80, 0x80, 0x80, 0x00, 0x01, 0x06, 0x6d, 0x75,
     258            0x6c, 0x74, 0x31, 0x32, 0x00, 0x01, 0x0a, 0xce, 0x80, 0x80, 0x80, 0x00, 0x02, 0xa6, 0x80, 0x80,
     259            0x80, 0x00, 0x00, 0x14, 0x00, 0x14, 0x01, 0x14, 0x02, 0x14, 0x03, 0x14, 0x04, 0x14, 0x05, 0x14,
     260            0x06, 0x14, 0x07, 0x14, 0x08, 0x14, 0x09, 0x14, 0x0a, 0x14, 0x0b, 0x40, 0x40, 0x40, 0x40, 0x40,
     261            0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x09, 0x0f, 0x9d, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x00,
     262            0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00,
     263            0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x16, 0x00, 0x09, 0x0f
     264        };
     265
     266        Plan plan(*vm, vector);
     267        if (plan.result.size() != 2 || !plan.result[0] || !plan.result[1]) {
     268            dataLogLn("Module failed to compile correctly.");
     269            CRASH();
     270        }
     271
     272        // Test this doesn't crash.
     273        CHECK_EQ(invoke<int>(*plan.result[1]->jsEntryPoint, { box(0) }), 0);
     274        CHECK_EQ(invoke<int>(*plan.result[1]->jsEntryPoint, { box(100) }), 1200);
     275        CHECK_EQ(invoke<int>(*plan.result[1]->jsEntryPoint, { box(1) }), 12);
     276        CHECK_EQ(invoke<int>(*plan.result[0]->jsEntryPoint, { box(1), box(2), box(3), box(4), box(5), box(6), box(7), box(8), box(9), box(10), box(11), box(12) }), 78);
     277        CHECK_EQ(invoke<int>(*plan.result[0]->jsEntryPoint, { box(1), box(2), box(3), box(4), box(5), box(6), box(7), box(8), box(9), box(10), box(11), box(100) }), 166);
     278    }
     279
     280    {
     281        // Generated from:
     282        //    (module
     283        //     (memory 1)
     284        //     (func $fac (export "fac") (param i64) (result i64)
     285        //      (if (i64.eqz (get_local 0))
     286        //       (return (i64.const 1))
     287        //       )
     288        //      (return (i64.mul (get_local 0) (call $fac (i64.sub (get_local 0) (i64.const 1)))))
     289        //      )
     290        //     )
     291        Vector<uint8_t> vector = {
     292            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
     293            0x01, 0x02, 0x01, 0x02, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x05, 0x83, 0x80, 0x80,
     294            0x80, 0x00, 0x01, 0x00, 0x01, 0x07, 0x87, 0x80, 0x80, 0x80, 0x00, 0x01, 0x03, 0x66, 0x61, 0x63,
     295            0x00, 0x00, 0x0a, 0x9e, 0x80, 0x80, 0x80, 0x00, 0x01, 0x98, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14,
     296            0x00, 0x11, 0x00, 0x68, 0x03, 0x00, 0x11, 0x01, 0x09, 0x0f, 0x14, 0x00, 0x14, 0x00, 0x11, 0x01,
     297            0x5c, 0x16, 0x00, 0x5d, 0x09, 0x0f
     298        };
     299
     300        Plan plan(*vm, vector);
     301        if (plan.result.size() != 1 || !plan.result[0]) {
     302            dataLogLn("Module failed to compile correctly.");
     303            CRASH();
     304        }
     305
     306        // Test this doesn't crash.
     307        CHECK_EQ(invoke<int>(*plan.result[0]->jsEntryPoint, { box(0) }), 1);
     308        CHECK_EQ(invoke<int>(*plan.result[0]->jsEntryPoint, { box(1) }), 1);
     309        CHECK_EQ(invoke<int>(*plan.result[0]->jsEntryPoint, { box(2) }), 2);
     310        CHECK_EQ(invoke<int>(*plan.result[0]->jsEntryPoint, { box(4) }), 24);
     311    }
     312
     313    {
     314        // Generated from:
     315        //    (module
     316        //     (memory 1)
     317        //     (func (export "double") (param i64) (result i64) (return (call 1 (get_local 0) (get_local 0))))
     318        //     (func $sum (param i64) (param i64) (result i64) (return (i64.add (get_local 0) (get_local 1))))
     319        //     )
     320        Vector<uint8_t> vector = {
     321            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x8c, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40,
     322            0x01, 0x02, 0x01, 0x02, 0x40, 0x02, 0x02, 0x02, 0x01, 0x02, 0x03, 0x83, 0x80, 0x80, 0x80, 0x00,
     323            0x02, 0x00, 0x01, 0x05, 0x83, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x01, 0x07, 0x8a, 0x80, 0x80,
     324            0x80, 0x00, 0x01, 0x06, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x00, 0x00, 0x0a, 0x9c, 0x80, 0x80,
     325            0x80, 0x00, 0x02, 0x89, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x00, 0x14, 0x00, 0x16, 0x01, 0x09,
     326            0x0f, 0x88, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x00, 0x14, 0x01, 0x5b, 0x09, 0x0f
     327        };
     328
     329        Plan plan(*vm, vector);
     330        if (plan.result.size() != 2 || !plan.result[0] || !plan.result[1]) {
     331            dataLogLn("Module failed to compile correctly.");
     332            CRASH();
     333        }
     334
     335        // Test this doesn't crash.
     336        CHECK_EQ(invoke<int>(*plan.result[1]->jsEntryPoint, { box(0), box(0) }), 0);
     337        CHECK_EQ(invoke<int>(*plan.result[1]->jsEntryPoint, { box(100), box(0) }), 100);
     338        CHECK_EQ(invoke<int>(*plan.result[1]->jsEntryPoint, { box(1), box(15) }), 16);
     339        CHECK_EQ(invoke<int>(*plan.result[0]->jsEntryPoint, { box(0) }), 0);
     340        CHECK_EQ(invoke<int>(*plan.result[0]->jsEntryPoint, { box(100) }), 200);
     341        CHECK_EQ(invoke<int>(*plan.result[0]->jsEntryPoint, { box(1) }), 2);
     342    }
     343
     344    {
     345        // Generated from:
     346        //    (module
     347        //     (memory 1)
     348        //     (func $id (param $value i32) (result i32) (return (get_local $value)))
     349        //     (func (export "id-call") (param $value i32) (result i32) (return (call $id (get_local $value))))
     350        //     )
     351        Vector<uint8_t> vector = {
     352            0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
     353            0x01, 0x01, 0x01, 0x01, 0x03, 0x83, 0x80, 0x80, 0x80, 0x00, 0x02, 0x00, 0x00, 0x05, 0x83, 0x80,
     354            0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x07, 0x8b, 0x80, 0x80, 0x80, 0x00, 0x01, 0x07, 0x69, 0x64,
     355            0x2d, 0x63, 0x61, 0x6c, 0x6c, 0x00, 0x01, 0x0a, 0x97, 0x80, 0x80, 0x80, 0x00, 0x02, 0x85, 0x80,
     356            0x80, 0x80, 0x00, 0x00, 0x14, 0x00, 0x09, 0x0f, 0x87, 0x80, 0x80, 0x80, 0x00, 0x00, 0x14, 0x00,
     357            0x16, 0x00, 0x09, 0x0f
     358        };
     359
     360        Plan plan(*vm, vector);
     361        if (plan.result.size() != 2 || !plan.result[0] || !plan.result[1]) {
     362            dataLogLn("Module failed to compile correctly.");
     363            CRASH();
     364        }
     365
     366        // Test this doesn't crash.
     367        CHECK_EQ(invoke<int>(*plan.result[1]->jsEntryPoint, { box(0) }), 0);
     368        CHECK_EQ(invoke<int>(*plan.result[1]->jsEntryPoint, { box(100) }), 100);
     369        CHECK_EQ(invoke<int>(*plan.result[1]->jsEntryPoint, { box(1) }), 1);
     370        CHECK_EQ(invoke<int>(*plan.result[0]->jsEntryPoint, { box(0) }), 0);
     371        CHECK_EQ(invoke<int>(*plan.result[0]->jsEntryPoint, { box(100) }), 100);
     372        CHECK_EQ(invoke<int>(*plan.result[0]->jsEntryPoint, { box(1) }), 1);
     373    }
     374
    245375    {
    246376        // Generated from:
  • trunk/Source/JavaScriptCore/wasm/WASMB3IRGenerator.cpp

    r207453 r207671  
    171171    static constexpr ExpressionType emptyExpression = nullptr;
    172172
    173     B3IRGenerator(Memory*, Procedure&);
     173    B3IRGenerator(Memory*, Procedure&, Vector<UnlinkedCall>& unlinkedCalls);
    174174
    175175    void addArguments(const Vector<Type>&);
     
    199199    bool WARN_UNUSED_RETURN endBlock(ControlData&, ExpressionList& expressionStack);
    200200
     201    bool WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const FunctionInformation&, Vector<ExpressionType>& args, ExpressionType& result);
     202
    201203    bool isContinuationReachable(ControlData&);
    202204
     
    215217    BasicBlock* m_currentBlock;
    216218    Vector<Variable*> m_locals;
     219    // m_unlikedCalls is list of each call site and the function index whose address it should be patched with.
     220    Vector<UnlinkedCall>& m_unlinkedCalls;
    217221    GPRReg m_memoryBaseGPR;
    218222    GPRReg m_memorySizeGPR;
    219223};
    220224
    221 B3IRGenerator::B3IRGenerator(Memory* memory, Procedure& procedure)
     225B3IRGenerator::B3IRGenerator(Memory* memory, Procedure& procedure, Vector<UnlinkedCall>& unlinkedCalls)
    222226    : m_memory(memory)
    223227    , m_proc(procedure)
     228    , m_unlinkedCalls(unlinkedCalls)
    224229{
    225230    m_currentBlock = m_proc.addBlock();
     
    232237        for (const PinnedSizeRegisterInfo& info : m_memory->pinnedRegisters().sizeRegisters)
    233238            m_proc.pinRegister(info.sizeRegister);
    234     }
    235 
    236     m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR, unsigned) {
    237         ASSERT_UNUSED(pinnedGPR, m_memorySizeGPR == pinnedGPR);
    238         // FIXME: This should unwind the stack and throw a JS exception. See: https://bugs.webkit.org/show_bug.cgi?id=163351
    239         jit.breakpoint();
    240     });
     239
     240        m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR, unsigned) {
     241            ASSERT_UNUSED(pinnedGPR, m_memorySizeGPR == pinnedGPR);
     242            // FIXME: This should unwind the stack and throw a JS exception. See: https://bugs.webkit.org/show_bug.cgi?id=163351
     243            jit.breakpoint();
     244        });
     245    }
    241246}
    242247
     
    252257    ASSERT(!m_locals.size());
    253258    m_locals.grow(types.size());
    254     jscCallingConvention().iterate(types, m_proc, m_currentBlock, Origin(),
     259    wasmCallingConvention().loadArguments(types, m_proc, m_currentBlock, Origin(),
    255260        [&] (ExpressionType argument, unsigned i) {
    256261            Variable* argumentVariable = m_proc.addVariable(argument->type());
     
    562567}
    563568
     569bool B3IRGenerator::addCall(unsigned functionIndex, const FunctionInformation& info, Vector<ExpressionType>& args, ExpressionType& result)
     570{
     571    ASSERT(info.signature->arguments.size() == args.size());
     572
     573    Type returnType = info.signature->returnType;
     574
     575    size_t callIndex = m_unlinkedCalls.size();
     576    m_unlinkedCalls.grow(callIndex + 1);
     577    result = wasmCallingConvention().setupCall(m_proc, m_currentBlock, Origin(), args, toB3Type(returnType),
     578        [&] (PatchpointValue* patchpoint) {
     579            patchpoint->effects.writesPinned = true;
     580            patchpoint->effects.readsPinned = true;
     581
     582            patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
     583                AllowMacroScratchRegisterUsage allowScratch(jit);
     584
     585                CCallHelpers::Call call = jit.call();
     586
     587                jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
     588                    m_unlinkedCalls[callIndex] = { linkBuffer.locationOf(call), functionIndex };
     589                });
     590            });
     591        });
     592    return true;
     593}
     594
    564595bool B3IRGenerator::isContinuationReachable(ControlData& data)
    565596{
     
    643674    // Get our arguments.
    644675    Vector<Value*> arguments;
    645     jscCallingConvention().iterate(signature->arguments, proc, block, Origin(), [&] (Value* argument, unsigned) {
     676    jscCallingConvention().loadArguments(signature->arguments, proc, block, Origin(), [&] (Value* argument, unsigned) {
    646677        arguments.append(argument);
    647678    });
    648679
    649680    // Move the arguments into place.
    650     Value* result = jscCallingConvention().setupCall(proc, block, Origin(), mainFunction, arguments, toB3Type(signature->returnType), [&] (PatchpointValue* patchpoint) {
     681    Value* result = wasmCallingConvention().setupCall(proc, block, Origin(), arguments, toB3Type(signature->returnType), [&] (PatchpointValue* patchpoint) {
    651682        if (memory) {
    652683            ASSERT(sizes.size() == memory->pinnedRegisters().sizeRegisters.size());
     
    655686                patchpoint->append(ConstrainedValue(sizes[i], ValueRep::reg(memory->pinnedRegisters().sizeRegisters[i].sizeRegister)));
    656687        }
     688
     689        patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
     690            AllowMacroScratchRegisterUsage allowScratch(jit);
     691
     692            CCallHelpers::Call call = jit.call();
     693            jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
     694                linkBuffer.link(call, FunctionPtr(mainFunction.executableAddress()));
     695            });
     696        });
    657697    });
    658698
     
    666706}
    667707
    668 std::unique_ptr<FunctionCompilation> parseAndCompile(VM& vm, Vector<uint8_t>& source, Memory* memory, FunctionInformation info, unsigned optLevel)
    669 {
     708std::unique_ptr<FunctionCompilation> parseAndCompile(VM& vm, Vector<uint8_t>& source, Memory* memory, FunctionInformation info, const Vector<FunctionInformation>& functions, unsigned optLevel)
     709{
     710    auto result = std::make_unique<FunctionCompilation>();
     711
    670712    Procedure procedure;
    671     B3IRGenerator context(memory, procedure);
    672     FunctionParser<B3IRGenerator> parser(context, source, info);
     713    B3IRGenerator context(memory, procedure, result->unlinkedCalls);
     714    FunctionParser<B3IRGenerator> parser(context, source, info, functions);
    673715    if (!parser.parse())
    674716        RELEASE_ASSERT_NOT_REACHED();
     
    680722    if (verbose)
    681723        dataLog("Post SSA: ", procedure);
    682     auto result = std::make_unique<FunctionCompilation>();
    683724
    684725    result->code = std::make_unique<Compilation>(vm, procedure, optLevel);
  • trunk/Source/JavaScriptCore/wasm/WASMB3IRGenerator.h

    r207453 r207671  
    3838class Memory;
    3939
    40 std::unique_ptr<FunctionCompilation> parseAndCompile(VM&, Vector<uint8_t>&, Memory*, FunctionInformation, unsigned optLevel = 1);
     40std::unique_ptr<FunctionCompilation> parseAndCompile(VM&, Vector<uint8_t>&, Memory*, FunctionInformation, const Vector<FunctionInformation>&, unsigned optLevel = 1);
    4141
    4242} } // namespace JSC::WASM
  • trunk/Source/JavaScriptCore/wasm/WASMCallingConvention.cpp

    r205552 r207671  
    3838    static std::once_flag staticJSCCallingConventionFlag;
    3939    std::call_once(staticJSCCallingConventionFlag, [] () {
    40         staticJSCCallingConvention.construct(Vector<GPRReg>(), RegisterSet::calleeSaveRegisters());
     40        staticJSCCallingConvention.construct(Vector<Reg>(), Vector<Reg>(), RegisterSet::calleeSaveRegisters());
    4141    });
    4242
     
    4444}
    4545
     46const WASMCallingConvention& wasmCallingConvention()
     47{
     48    static LazyNeverDestroyed<JSCCallingConvention> staticWASMCallingConvention;
     49    static std::once_flag staticWASMCallingConventionFlag;
     50    std::call_once(staticWASMCallingConventionFlag, [] () {
     51        Vector<Reg> gprArgumentRegisters(GPRInfo::numberOfArgumentRegisters);
     52        for (unsigned i = 0; i < GPRInfo::numberOfArgumentRegisters; ++i)
     53            gprArgumentRegisters[i] = GPRInfo::toArgumentRegister(i);
     54
     55        Vector<Reg> fprArgumentRegisters(FPRInfo::numberOfArgumentRegisters);
     56        for (unsigned i = 0; i < FPRInfo::numberOfArgumentRegisters; ++i)
     57            fprArgumentRegisters[i] = FPRInfo::toArgumentRegister(i);
     58
     59        staticWASMCallingConvention.construct(WTFMove(gprArgumentRegisters), WTFMove(fprArgumentRegisters), RegisterSet::calleeSaveRegisters());
     60    });
     61
     62    return staticWASMCallingConvention;
     63}
     64
    4665} } // namespace JSC::WASM
    4766
  • trunk/Source/JavaScriptCore/wasm/WASMCallingConvention.h

    r207453 r207671  
    4949class CallingConvention {
    5050public:
    51     CallingConvention(Vector<GPRReg>&& registerArguments, RegisterSet&& calleeSaveRegisters)
    52         : m_registerArguments(registerArguments)
     51    CallingConvention(Vector<Reg>&& gprArgs, Vector<Reg>&& fprArgs, RegisterSet&& calleeSaveRegisters)
     52        : m_gprArgs(gprArgs)
     53        , m_fprArgs(fprArgs)
    5354        , m_calleeSaveRegisters(calleeSaveRegisters)
    5455    {
    5556    }
    5657
     58private:
     59    B3::ValueRep marshallArgumentImpl(Vector<Reg> regArgs, B3::Type type, size_t& count, size_t& stackOffset) const
     60    {
     61        if (count < regArgs.size())
     62            return B3::ValueRep::reg(regArgs[count++]);
     63
     64        count++;
     65        B3::ValueRep result = B3::ValueRep::stackArgument(stackOffset);
     66        stackOffset = updateOffset(stackOffset, type);
     67        return result;
     68    }
     69
     70    B3::ValueRep marshallArgument(B3::Type type, size_t& gpArgumentCount, size_t& fpArgumentCount, size_t& stackOffset) const
     71    {
     72        switch (type) {
     73        case B3::Int32:
     74        case B3::Int64:
     75            return marshallArgumentImpl(m_gprArgs, type, gpArgumentCount, stackOffset);
     76        case B3::Float:
     77        case B3::Double:
     78            return marshallArgumentImpl(m_fprArgs, type, fpArgumentCount, stackOffset);
     79        case Void:
     80            break;
     81        }
     82        RELEASE_ASSERT_NOT_REACHED();
     83    }
     84
     85public:
    5786    template<typename Functor>
    58     void iterate(const Vector<Type>& argumentTypes, B3::Procedure& proc, B3::BasicBlock* block, B3::Origin origin, const Functor& functor) const
     87    void loadArguments(const Vector<Type>& argumentTypes, B3::Procedure& proc, B3::BasicBlock* block, B3::Origin origin, const Functor& functor) const
    5988    {
    60         unsigned currentOffset = headerSize;
    6189        B3::Value* framePointer = block->appendNew<B3::Value>(proc, B3::FramePointer, origin);
    6290
    63         for (unsigned i = 0; i < argumentTypes.size(); ++i) {
     91        size_t gpArgumentCount = 0;
     92        size_t fpArgumentCount = 0;
     93        size_t stackOffset = headerSize;
     94
     95        for (size_t i = 0; i < argumentTypes.size(); ++i) {
     96            B3::Type type = toB3Type(argumentTypes[i]);
    6497            B3::Value* argument;
    65             if (i < m_registerArguments.size())
    66                 argument = block->appendNew<B3::ArgumentRegValue>(proc, origin, m_registerArguments[i]);
    67             else {
     98            B3::ValueRep rep = marshallArgument(type, gpArgumentCount, fpArgumentCount, stackOffset);
     99            if (rep.isReg()) {
     100                argument = block->appendNew<B3::ArgumentRegValue>(proc, origin, rep.reg());
     101                if (type == B3::Int32)
     102                    argument = block->appendNew<B3::Value>(proc, B3::Trunc, origin, argument);
     103                // FIXME: How do I get a float from a FPR? We don't support floating points yet so it's not a big deal... yet.
     104                // see: https://bugs.webkit.org/show_bug.cgi?id=163770
     105            } else {
     106                ASSERT(rep.isStackArgument());
    68107                B3::Value* address = block->appendNew<B3::Value>(proc, B3::Add, origin, framePointer,
    69                     block->appendNew<B3::Const64Value>(proc, origin, currentOffset));
    70                 argument = block->appendNew<B3::MemoryValue>(proc, B3::Load, toB3Type(argumentTypes[i]), origin, address);
    71                 currentOffset = updateOffset(currentOffset, toB3Type(argumentTypes[i]));
     108                    block->appendNew<B3::Const64Value>(proc, origin, rep.offsetFromSP()));
     109                argument = block->appendNew<B3::MemoryValue>(proc, B3::Load, type, origin, address);
    72110            }
    73111            functor(argument, i);
     
    75113    }
    76114
     115    // It's expected that the pachpointFunctor sets the generator for the call operation.
    77116    template<typename Functor>
    78     B3::Value* setupCall(B3::Procedure& proc, B3::BasicBlock* block, B3::Origin origin, MacroAssemblerCodePtr target, const Vector<B3::Value*>& arguments, B3::Type returnType, const Functor& patchpointFunctor) const
     117    B3::Value* setupCall(B3::Procedure& proc, B3::BasicBlock* block, B3::Origin origin, const Vector<B3::Value*>& arguments, B3::Type returnType, const Functor& patchpointFunctor) const
    79118    {
    80         size_t stackArgumentCount = arguments.size() < m_registerArguments.size() ? 0 : arguments.size() - m_registerArguments.size();
    81         unsigned offset = headerSize - sizeof(CallerFrameAndPC);
     119        size_t gpArgumentCount = 0;
     120        size_t fpArgumentCount = 0;
     121        size_t stackOffset = headerSize - sizeof(CallerFrameAndPC);
    82122
    83         proc.requestCallArgAreaSizeInBytes(WTF::roundUpToMultipleOf(stackAlignmentBytes(), headerSize + (stackArgumentCount * sizeof(Register))));
    84123        Vector<B3::ConstrainedValue> constrainedArguments;
    85         for (unsigned i = 0; i < arguments.size(); ++i) {
    86             B3::ValueRep rep;
    87             if (i < m_registerArguments.size())
    88                 rep = B3::ValueRep::reg(m_registerArguments[i]);
    89             else
    90                 rep = B3::ValueRep::stackArgument(offset);
    91             constrainedArguments.append(B3::ConstrainedValue(arguments[i], rep));
    92             offset = updateOffset(offset, arguments[i]->type());
     124        for (B3::Value* argument : arguments) {
     125            B3::ValueRep rep = marshallArgument(argument->type(), gpArgumentCount, fpArgumentCount, stackOffset);
     126            constrainedArguments.append(B3::ConstrainedValue(argument, rep));
    93127        }
    94128
     129        proc.requestCallArgAreaSizeInBytes(WTF::roundUpToMultipleOf(stackAlignmentBytes(), stackOffset));
     130
    95131        B3::PatchpointValue* patchpoint = block->appendNew<B3::PatchpointValue>(proc, returnType, origin);
     132        patchpoint->clobberEarly(RegisterSet::macroScratchRegisters());
     133        patchpoint->clobberLate(RegisterSet::volatileRegistersForJSCall());
    96134        patchpoint->appendVector(constrainedArguments);
    97135        patchpointFunctor(patchpoint);
    98         patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
    99             AllowMacroScratchRegisterUsage allowScratch(jit);
    100 
    101             CCallHelpers::Call call = jit.call();
    102             jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
    103                 linkBuffer.link(call, FunctionPtr(target.executableAddress()));
    104             });
    105         });
    106136
    107137        if (returnType == B3::Void)
     
    112142    }
    113143
    114     const Vector<GPRReg> m_registerArguments;
     144    const Vector<Reg> m_gprArgs;
     145    const Vector<Reg> m_fprArgs;
    115146    const RegisterSet m_calleeSaveRegisters;
    116147    const RegisterSet m_callerSaveRegisters;
     
    125156typedef CallingConvention<jscHeaderSize, nextJSCOffset> JSCCallingConvention;
    126157
     158typedef JSCCallingConvention WASMCallingConvention;
     159
    127160const JSCCallingConvention& jscCallingConvention();
     161const WASMCallingConvention& wasmCallingConvention();
    128162
    129163} } // namespace JSC::WASM
  • trunk/Source/JavaScriptCore/wasm/WASMFormat.h

    r207453 r207671  
    4747#include "B3Compilation.h"
    4848#include "B3Type.h"
     49#include "CodeLocation.h"
    4950#include <wtf/Vector.h>
    5051#include <wtf/text/WTFString.h>
     
    128129};
    129130
     131struct UnlinkedCall {
     132    CodeLocationCall callLocation;
     133    size_t functionIndex;
     134};
     135
    130136struct FunctionCompilation {
     137    Vector<UnlinkedCall> unlinkedCalls;
    131138    std::unique_ptr<B3::Compilation> code;
    132139    std::unique_ptr<B3::Compilation> jsEntryPoint;
  • trunk/Source/JavaScriptCore/wasm/WASMFunctionParser.h

    r207453 r207671  
    4545    typedef typename Context::ControlType ControlType;
    4646
    47     FunctionParser(Context&, const Vector<uint8_t>& sourceBuffer, const FunctionInformation&);
     47    FunctionParser(Context&, const Vector<uint8_t>& sourceBuffer, const FunctionInformation&, const Vector<FunctionInformation>& functions);
    4848
    4949    bool WARN_UNUSED_RETURN parse();
     
    6161    Vector<ControlType> m_controlStack;
    6262    const Signature& m_signature;
     63    const Vector<FunctionInformation>& m_functions;
    6364    unsigned m_unreachableBlocks { 0 };
    6465};
    6566
    6667template<typename Context>
    67 FunctionParser<Context>::FunctionParser(Context& context, const Vector<uint8_t>& sourceBuffer, const FunctionInformation& info)
     68FunctionParser<Context>::FunctionParser(Context& context, const Vector<uint8_t>& sourceBuffer, const FunctionInformation& info, const Vector<FunctionInformation>& functions)
    6869    : Parser(sourceBuffer, info.start, info.end)
    6970    , m_context(context)
    7071    , m_signature(*info.signature)
     72    , m_functions(functions)
    7173{
    7274    if (verbose)
     
    102104    while (true) {
    103105        uint8_t op;
    104         if (!parseUInt7(op) || !isValidOpType(op))
    105             return false;
     106        if (!parseUInt7(op) || !isValidOpType(op)) {
     107            if (verbose)
     108                WTF::dataLogLn("attempted to decode invalid op: ", RawPointer(reinterpret_cast<void*>(op)), " at offset: ", RawPointer(reinterpret_cast<void*>(m_offset)));
     109            return false;
     110        }
    106111
    107112        if (verbose) {
     
    194199    }
    195200
     201    case OpType::I64Const: {
     202        uint64_t constant;
     203        if (!parseVarUInt64(constant))
     204            return false;
     205        m_expressionStack.append(m_context.addConstant(I64, constant));
     206        return true;
     207    }
     208
    196209    case OpType::GetLocal: {
    197210        uint32_t index;
     
    212225        ExpressionType value = m_expressionStack.takeLast();
    213226        return m_context.setLocal(index, value);
     227    }
     228
     229    case OpType::Call: {
     230        uint32_t functionIndex;
     231        if (!parseVarUInt32(functionIndex))
     232            return false;
     233
     234        if (functionIndex >= m_functions.size())
     235            return false;
     236
     237        const FunctionInformation& info = m_functions[functionIndex];
     238
     239        Vector<ExpressionType> args;
     240        for (unsigned i = 0; i < info.signature->arguments.size(); ++i)
     241            args.append(m_expressionStack.takeLast());
     242
     243        ExpressionType result = Context::emptyExpression;
     244        if (!m_context.addCall(functionIndex, info, args, result))
     245            return false;
     246
     247        if (result != Context::emptyExpression)
     248            m_expressionStack.append(result);
     249
     250        return true;
    214251    }
    215252
     
    282319    case OpType::Nop:
    283320    case OpType::Drop:
    284     case OpType::I64Const:
    285321    case OpType::F32Const:
    286322    case OpType::F64Const:
     
    288324    case OpType::GetGlobal:
    289325    case OpType::SetGlobal:
     326    case OpType::CallIndirect:
    290327        // FIXME: Not yet implemented.
    291328        return false;
  • trunk/Source/JavaScriptCore/wasm/WASMModuleParser.cpp

    r207453 r207671  
    8383
    8484            // Make sure we can read up to the section's size.
    85             if (m_offset + sectionNameLength + maxLEBByteLength >= m_sourceLength)
     85            if (m_offset + sectionNameLength + WTF::LEBDecoder::max32BitLEBByteLength >= m_sourceLength)
    8686                return false;
    8787
  • trunk/Source/JavaScriptCore/wasm/WASMOps.h

    r207453 r207671  
    4343    macro(TeeLocal, 0x19, Oops) \
    4444    macro(GetGlobal, 0xbb, Oops) \
    45     macro(SetGlobal, 0xbc, Oops)
     45    macro(SetGlobal, 0xbc, Oops) \
     46    macro(Call, 0x16, Oops) \
     47    macro(CallIndirect, 0x17, Oops)
    4648
    4749#define FOR_EACH_WASM_CONTROL_FLOW_OP(macro) \
  • trunk/Source/JavaScriptCore/wasm/WASMParser.h

    r206794 r207671  
    4747    bool WARN_UNUSED_RETURN parseUInt7(uint8_t& result);
    4848    bool WARN_UNUSED_RETURN parseUInt32(uint32_t& result);
    49     bool WARN_UNUSED_RETURN parseVarUInt32(uint32_t& result) { return decodeUInt32(m_source.data(), m_sourceLength, m_offset, result); }
     49    bool WARN_UNUSED_RETURN parseVarUInt32(uint32_t& result) { return WTF::LEBDecoder::decodeUInt32(m_source.data(), m_sourceLength, m_offset, result); }
     50    bool WARN_UNUSED_RETURN parseVarUInt64(uint64_t& result) { return WTF::LEBDecoder::decodeUInt64(m_source.data(), m_sourceLength, m_offset, result); }
    5051
    5152
  • trunk/Source/JavaScriptCore/wasm/WASMPlan.cpp

    r207453 r207671  
    5555        if (verbose)
    5656            dataLogLn("Processing funcion starting at: ", info.start, " and ending at: ", info.end);
    57         result.append(parseAndCompile(vm, source, moduleParser.memory().get(), info));
     57        result.append(parseAndCompile(vm, source, moduleParser.memory().get(), info, moduleParser.functionInformation()));
    5858    }
     59
     60    // Patch the call sites for each function.
     61    for (std::unique_ptr<FunctionCompilation>& functionPtr : result) {
     62        FunctionCompilation* function = functionPtr.get();
     63        for (auto& call : function->unlinkedCalls)
     64            MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(result[call.functionIndex]->code->code()));
     65    }
     66
    5967    memory = WTFMove(moduleParser.memory());
    6068}
  • trunk/Source/WTF/ChangeLog

    r207653 r207671  
     12016-10-20  Keith Miller  <keith_miller@apple.com>
     2
     3        Add support for WASM calls
     4        https://bugs.webkit.org/show_bug.cgi?id=161727
     5
     6        Reviewed by Filip Pizlo and Michael Saboff.
     7
     8        Added a new decodeUInt64. Also, added WTF::LEBDecoder namespace.
     9
     10        * wtf/LEBDecoder.h:
     11        (WTF::LEBDecoder::decodeUInt):
     12        (WTF::LEBDecoder::decodeUInt32):
     13        (WTF::LEBDecoder::decodeUInt64):
     14        (WTF::LEBDecoder::decodeInt32):
     15        (decodeUInt32): Deleted.
     16        (decodeInt32): Deleted.
     17
    1182016-10-20  Filip Pizlo  <fpizlo@apple.com>
    219
  • trunk/Source/WTF/wtf/LEBDecoder.h

    r204484 r207671  
    3333// LEB format.
    3434
    35 const size_t maxLEBByteLength = 5;
     35namespace WTF { namespace LEBDecoder {
    3636
    37 inline bool WARN_UNUSED_RETURN decodeUInt32(const uint8_t* bytes, size_t length, size_t& offset, uint32_t& result)
     37template<size_t maxByteLength, typename T>
     38inline bool WARN_UNUSED_RETURN decodeUInt(const uint8_t* bytes, size_t length, size_t& offset, T& result)
    3839{
    3940    ASSERT(length > offset);
    4041    result = 0;
    4142    unsigned shift = 0;
    42     size_t last = std::min(maxLEBByteLength, length - offset - 1);
     43    size_t last = std::min(maxByteLength, length - offset - 1);
    4344    for (unsigned i = 0; true; ++i) {
    4445        uint8_t byte = bytes[offset++];
     
    5455}
    5556
     57const size_t max32BitLEBByteLength = 5;
     58const size_t max64BitLEBByteLength = 10;
     59
     60inline bool WARN_UNUSED_RETURN decodeUInt32(const uint8_t* bytes, size_t length, size_t& offset, uint32_t& result)
     61{
     62    return decodeUInt<max32BitLEBByteLength, uint32_t>(bytes, length, offset, result);
     63}
     64
     65inline bool WARN_UNUSED_RETURN decodeUInt64(const uint8_t* bytes, size_t length, size_t& offset, uint64_t& result)
     66{
     67    return decodeUInt<max64BitLEBByteLength, uint64_t>(bytes, length, offset, result);
     68}
     69
    5670inline bool WARN_UNUSED_RETURN decodeInt32(const uint8_t* bytes, size_t length, size_t& offset, int32_t& result)
    5771{
     
    5973    result = 0;
    6074    unsigned shift = 0;
    61     size_t last = std::min(maxLEBByteLength, length - offset - 1);
     75    size_t last = std::min(max32BitLEBByteLength, length - offset - 1);
    6276    uint8_t byte;
    6377    for (unsigned i = 0; true; ++i) {
     
    7589    return true;
    7690}
     91
     92} } // WTF::LEBDecoder
Note: See TracChangeset for help on using the changeset viewer.