Title: [216173] trunk
Revision
216173
Author
[email protected]
Date
2017-05-03 22:50:01 -0700 (Wed, 03 May 2017)

Log Message

How we build polymorphic cases is wrong when making a call from Wasm
https://bugs.webkit.org/show_bug.cgi?id=171527

Reviewed by JF Bastien.

Source/_javascript_Core:

This patches fixes a bug when we emit a polymorphic call IC from
Wasm. We were incorrectly assuming that if we made a call *from wasm*,
then the thing we are *calling to* does not have a CodeBlock. This
is obviously wrong. This patch fixes the incorrect assumption.

This patch also does two more things:
1. Add a new option that makes us make calls to JS using a
slow path instead of using a call IC.
2. Fixes a potential GC bug where we didn't populate JSWebAssemblyCodeBlock's
JSWebAssemblyModule pointer.

* jit/Repatch.cpp:
(JSC::linkPolymorphicCall):
* runtime/Options.h:
* wasm/WasmBinding.cpp:
(JSC::Wasm::wasmToJs):
* wasm/js/JSWebAssemblyCodeBlock.cpp:
(JSC::JSWebAssemblyCodeBlock::create):
(JSC::JSWebAssemblyCodeBlock::finishCreation):
* wasm/js/JSWebAssemblyCodeBlock.h:
* wasm/js/JSWebAssemblyInstance.cpp:
(JSC::JSWebAssemblyInstance::finalizeCreation):

Tools:

* Scripts/run-jsc-stress-tests:

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (216172 => 216173)


--- trunk/Source/_javascript_Core/ChangeLog	2017-05-04 04:48:07 UTC (rev 216172)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-05-04 05:50:01 UTC (rev 216173)
@@ -1,3 +1,33 @@
+2017-05-03  Saam Barati  <[email protected]>
+
+        How we build polymorphic cases is wrong when making a call from Wasm
+        https://bugs.webkit.org/show_bug.cgi?id=171527
+
+        Reviewed by JF Bastien.
+
+        This patches fixes a bug when we emit a polymorphic call IC from
+        Wasm. We were incorrectly assuming that if we made a call *from wasm*,
+        then the thing we are *calling to* does not have a CodeBlock. This
+        is obviously wrong. This patch fixes the incorrect assumption.
+        
+        This patch also does two more things:
+        1. Add a new option that makes us make calls to JS using a
+        slow path instead of using a call IC.
+        2. Fixes a potential GC bug where we didn't populate JSWebAssemblyCodeBlock's
+        JSWebAssemblyModule pointer.
+
+        * jit/Repatch.cpp:
+        (JSC::linkPolymorphicCall):
+        * runtime/Options.h:
+        * wasm/WasmBinding.cpp:
+        (JSC::Wasm::wasmToJs):
+        * wasm/js/JSWebAssemblyCodeBlock.cpp:
+        (JSC::JSWebAssemblyCodeBlock::create):
+        (JSC::JSWebAssemblyCodeBlock::finishCreation):
+        * wasm/js/JSWebAssemblyCodeBlock.h:
+        * wasm/js/JSWebAssemblyInstance.cpp:
+        (JSC::JSWebAssemblyInstance::finalizeCreation):
+
 2017-05-03  Keith Miller  <[email protected]>
 
         Array.prototype.sort should also allow a null comparator

Modified: trunk/Source/_javascript_Core/jit/Repatch.cpp (216172 => 216173)


--- trunk/Source/_javascript_Core/jit/Repatch.cpp	2017-05-04 04:48:07 UTC (rev 216172)
+++ trunk/Source/_javascript_Core/jit/Repatch.cpp	2017-05-04 05:50:01 UTC (rev 216173)
@@ -762,7 +762,7 @@
     // Figure out what our cases are.
     for (CallVariant variant : list) {
         CodeBlock* codeBlock;
-        if (isWebAssembly || variant.executable()->isHostFunction())
+        if (variant.executable()->isHostFunction())
             codeBlock = nullptr;
         else {
             ExecutableBase* executable = variant.executable();

Modified: trunk/Source/_javascript_Core/runtime/Options.h (216172 => 216173)


--- trunk/Source/_javascript_Core/runtime/Options.h	2017-05-04 04:48:07 UTC (rev 216172)
+++ trunk/Source/_javascript_Core/runtime/Options.h	2017-05-04 05:50:01 UTC (rev 216173)
@@ -446,7 +446,8 @@
     v(unsigned, webAssemblyFastMemoryRedzonePages, 128, Normal, "WebAssembly fast memories use 4GiB virtual allocations, plus a redzone (counted as multiple of 64KiB WebAssembly pages) at the end to catch reg+imm accesses which exceed 32-bit, anything beyond the redzone is explicitly bounds-checked") \
     v(bool, crashIfWebAssemblyCantFastMemory, false, Normal, "If true, we will crash if we can't obtain fast memory for wasm.") \
     v(unsigned, webAssemblyFastMemoryPreallocateCount, 0, Normal, "WebAssembly fast memories can be pre-allocated at program startup and remain cached to avoid fragmentation leading to bounds-checked memory. This number is an upper bound on initial allocation as well as total count of fast memories. Zero means no pre-allocation, no caching, and no limit to the number of runtime allocations.") \
-    v(bool, useWebAssemblyFastTLS, true, Normal, "If true, we will try to use fast thread-local storage if available on the current platform.")
+    v(bool, useWebAssemblyFastTLS, true, Normal, "If true, we will try to use fast thread-local storage if available on the current platform.") \
+    v(bool, useCallICsForWebAssemblyToJSCalls, true, Normal, "If true, we will use CallLinkInfo to inline cache Wasm to JS calls.")
 
 
 enum OptionEquivalence {

Modified: trunk/Source/_javascript_Core/wasm/WasmBinding.cpp (216172 => 216173)


--- trunk/Source/_javascript_Core/wasm/WasmBinding.cpp	2017-05-04 04:48:07 UTC (rev 216172)
+++ trunk/Source/_javascript_Core/wasm/WasmBinding.cpp	2017-05-04 05:50:01 UTC (rev 216173)
@@ -125,6 +125,186 @@
     missingCalleeSaves.exclude(jsCC.m_calleeSaveRegisters);
     ASSERT(missingCalleeSaves.isEmpty());
 
+    if (!Options::useCallICsForWebAssemblyToJSCalls()) {
+        ScratchBuffer* scratchBuffer = vm->scratchBufferForSize(argCount * sizeof(uint64_t));
+        char* buffer = argCount ? static_cast<char*>(scratchBuffer->dataBuffer()) : nullptr;
+        unsigned marshalledGPRs = 0;
+        unsigned marshalledFPRs = 0;
+        unsigned bufferOffset = 0;
+        unsigned frOffset = CallFrame::headerSizeInRegisters * static_cast<int>(sizeof(Register));
+        const GPRReg scratchGPR = GPRInfo::regCS0;
+        jit.subPtr(MacroAssembler::TrustedImm32(WTF::roundUpToMultipleOf(stackAlignmentBytes(), sizeof(Register))), MacroAssembler::stackPointerRegister);
+        jit.storePtr(scratchGPR, MacroAssembler::Address(MacroAssembler::stackPointerRegister));
+
+        for (unsigned argNum = 0; argNum < argCount; ++argNum) {
+            Type argType = signature.argument(argNum);
+            switch (argType) {
+            case Void:
+            case Func:
+            case Anyfunc:
+            case I64:
+                RELEASE_ASSERT_NOT_REACHED();
+            case I32: {
+                GPRReg gprReg;
+                if (marshalledGPRs < wasmCC.m_gprArgs.size())
+                    gprReg = wasmCC.m_gprArgs[marshalledGPRs].gpr();
+                else {
+                    // We've already spilled all arguments, these registers are available as scratch.
+                    gprReg = GPRInfo::argumentGPR0;
+                    jit.load64(JIT::Address(GPRInfo::callFrameRegister, frOffset), gprReg);
+                    frOffset += sizeof(Register);
+                }
+                jit.zeroExtend32ToPtr(gprReg, gprReg);
+                jit.store64(gprReg, buffer + bufferOffset);
+                ++marshalledGPRs;
+                break;
+            }
+            case F32: {
+                FPRReg fprReg;
+                if (marshalledFPRs < wasmCC.m_fprArgs.size())
+                    fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
+                else {
+                    // We've already spilled all arguments, these registers are available as scratch.
+                    fprReg = FPRInfo::argumentFPR0;
+                    jit.loadFloat(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
+                    frOffset += sizeof(Register);
+                }
+                jit.convertFloatToDouble(fprReg, fprReg);
+                jit.moveDoubleTo64(fprReg, scratchGPR);
+                jit.store64(scratchGPR, buffer + bufferOffset);
+                ++marshalledFPRs;
+                break;
+            }
+            case F64: {
+                FPRReg fprReg;
+                if (marshalledFPRs < wasmCC.m_fprArgs.size())
+                    fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
+                else {
+                    // We've already spilled all arguments, these registers are available as scratch.
+                    fprReg = FPRInfo::argumentFPR0;
+                    jit.loadDouble(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
+                    frOffset += sizeof(Register);
+                }
+                jit.moveDoubleTo64(fprReg, scratchGPR);
+                jit.store64(scratchGPR, buffer + bufferOffset);
+                ++marshalledFPRs;
+                break;
+            }
+            }
+
+            bufferOffset += sizeof(Register);
+        }
+        jit.loadPtr(MacroAssembler::Address(MacroAssembler::stackPointerRegister), scratchGPR);
+        if (argCount) {
+            // The GC should not look at this buffer at all, these aren't JSValues.
+            jit.move(CCallHelpers::TrustedImmPtr(scratchBuffer->activeLengthPtr()), GPRInfo::argumentGPR0);
+            jit.storePtr(CCallHelpers::TrustedImmPtr(0), GPRInfo::argumentGPR0);
+        }
+
+        uint64_t (*callFunc)(ExecState*, JSObject*, SignatureIndex, uint64_t*) =
+            [] (ExecState* exec, JSObject* callee, SignatureIndex signatureIndex, uint64_t* buffer) -> uint64_t { 
+                VM* vm = &exec->vm();
+                NativeCallFrameTracer tracer(vm, exec);
+                auto throwScope = DECLARE_THROW_SCOPE(*vm);
+                const Signature& signature = SignatureInformation::get(signatureIndex);
+                MarkedArgumentBuffer args;
+                for (unsigned argNum = 0; argNum < signature.argumentCount(); ++argNum) {
+                    Type argType = signature.argument(argNum);
+                    JSValue arg;
+                    switch (argType) {
+                    case Void:
+                    case Func:
+                    case Anyfunc:
+                    case I64:
+                        RELEASE_ASSERT_NOT_REACHED();
+                    case I32:
+                        arg = jsNumber(static_cast<int32_t>(buffer[argNum]));
+                        break;
+                    case F32:
+                    case F64:
+                        arg = jsNumber(bitwise_cast<double>(buffer[argNum]));
+                        break;
+                    }
+                    args.append(arg);
+                }
+
+                CallData callData;
+                CallType callType = callee->methodTable(*vm)->getCallData(callee, callData);
+                RELEASE_ASSERT(callType != CallType::None);
+                JSValue result = call(exec, callee, callType, callData, jsUndefined(), args);
+                RETURN_IF_EXCEPTION(throwScope, 0);
+
+                uint64_t realResult;
+                switch (signature.returnType()) {
+                case Func:
+                case Anyfunc:
+                case I64:
+                    RELEASE_ASSERT_NOT_REACHED();
+                    break;
+                case Void:
+                    break;
+                case I32: {
+                    realResult = static_cast<uint64_t>(static_cast<uint32_t>(result.toInt32(exec)));
+                    break;
+                }
+                case F64:
+                case F32: {
+                    realResult = bitwise_cast<uint64_t>(result.toNumber(exec));
+                    break;
+                }
+                }
+
+                RETURN_IF_EXCEPTION(throwScope, 0);
+                return realResult;
+            };
+        
+        jit.loadWasmContext(GPRInfo::argumentGPR0);
+        jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR0, JSWebAssemblyInstance::offsetOfCallee()), GPRInfo::argumentGPR0);
+        jit.storePtr(GPRInfo::argumentGPR0, JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
+        
+        materializeImportJSCell(jit, importIndex, GPRInfo::argumentGPR1);
+        static_assert(GPRInfo::numberOfArgumentRegisters >= 4, "We rely on this with the call below.");
+        jit.setupArgumentsWithExecState(GPRInfo::argumentGPR1, CCallHelpers::TrustedImm32(signatureIndex), CCallHelpers::TrustedImmPtr(buffer));
+        auto call = jit.call();
+        auto noException = jit.emitExceptionCheck(*vm, AssemblyHelpers::InvertedExceptionCheck);
+
+        // exception here.
+        jit.copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(*vm);
+        jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+        void (*doUnwinding)(ExecState*) = [] (ExecState* exec) -> void {
+            VM* vm = &exec->vm();
+            NativeCallFrameTracer tracer(vm, exec);
+            genericUnwind(vm, exec);
+            ASSERT(!!vm->callFrameForCatch);
+        };
+        auto exceptionCall = jit.call();
+        jit.jumpToExceptionHandler(*vm);
+
+        noException.link(&jit);
+        switch (signature.returnType()) {
+        case F64: {
+            jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
+            break;
+        }
+        case F32: {
+            jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
+            jit.convertDoubleToFloat(FPRInfo::returnValueFPR, FPRInfo::returnValueFPR);
+            break;
+        }
+        default:
+            break;
+        }
+
+        jit.emitFunctionEpilogue();
+        jit.ret();
+
+        LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
+        linkBuffer.link(call, callFunc);
+        linkBuffer.link(exceptionCall, doUnwinding);
+
+        return FINALIZE_CODE(linkBuffer, ("WebAssembly->_javascript_ import[%i] %s", importIndex, signature.toString().ascii().data()));
+    }
+
     // FIXME perform a stack check before updating SP. https://bugs.webkit.org/show_bug.cgi?id=165546
 
     const unsigned numberOfParameters = argCount + 1; // There is a "this" argument.

Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyCodeBlock.cpp (216172 => 216173)


--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyCodeBlock.cpp	2017-05-04 04:48:07 UTC (rev 216172)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyCodeBlock.cpp	2017-05-04 05:50:01 UTC (rev 216173)
@@ -41,10 +41,11 @@
 
 const ClassInfo JSWebAssemblyCodeBlock::s_info = { "WebAssemblyCodeBlock", nullptr, 0, CREATE_METHOD_TABLE(JSWebAssemblyCodeBlock) };
 
-JSWebAssemblyCodeBlock* JSWebAssemblyCodeBlock::create(VM& vm, Ref<Wasm::CodeBlock> codeBlock, const Wasm::ModuleInformation& moduleInformation)
+JSWebAssemblyCodeBlock* JSWebAssemblyCodeBlock::create(VM& vm, Ref<Wasm::CodeBlock> codeBlock, JSWebAssemblyModule* module)
 {
+    const Wasm::ModuleInformation& moduleInformation = module->module().moduleInformation();
     auto* result = new (NotNull, allocateCell<JSWebAssemblyCodeBlock>(vm.heap, allocationSize(moduleInformation.importFunctionCount()))) JSWebAssemblyCodeBlock(vm, WTFMove(codeBlock), moduleInformation);
-    result->finishCreation(vm);
+    result->finishCreation(vm, module);
     return result;
 }
 
@@ -62,6 +63,12 @@
     }
 }
 
+void JSWebAssemblyCodeBlock::finishCreation(VM& vm, JSWebAssemblyModule* module)
+{
+    Base::finishCreation(vm);
+    m_module.set(vm, this, module);
+}
+
 void JSWebAssemblyCodeBlock::destroy(JSCell* cell)
 {
     static_cast<JSWebAssemblyCodeBlock*>(cell)->JSWebAssemblyCodeBlock::~JSWebAssemblyCodeBlock();

Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyCodeBlock.h (216172 => 216173)


--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyCodeBlock.h	2017-05-04 04:48:07 UTC (rev 216172)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyCodeBlock.h	2017-05-04 05:50:01 UTC (rev 216173)
@@ -51,7 +51,7 @@
     typedef JSCell Base;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
 
-    static JSWebAssemblyCodeBlock* create(VM&, Ref<Wasm::CodeBlock>, const Wasm::ModuleInformation&);
+    static JSWebAssemblyCodeBlock* create(VM&, Ref<Wasm::CodeBlock>, JSWebAssemblyModule*);
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
     {
         return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
@@ -62,6 +62,8 @@
 
     bool isSafeToRun(JSWebAssemblyMemory*) const;
 
+    void finishCreation(VM&, JSWebAssemblyModule*);
+
     // These two callee getters are only valid once the callees have been populated.
 
     Wasm::Callee& jsEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace)

Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.cpp (216172 => 216173)


--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.cpp	2017-05-04 04:48:07 UTC (rev 216172)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.cpp	2017-05-04 05:50:01 UTC (rev 216173)
@@ -112,7 +112,7 @@
         ASSERT(&codeBlock->codeBlock() == wasmCodeBlock.ptr());
         m_codeBlock.set(vm, this, codeBlock);
     } else {
-        codeBlock = JSWebAssemblyCodeBlock::create(vm, wasmCodeBlock.copyRef(), m_module->module().moduleInformation());
+        codeBlock = JSWebAssemblyCodeBlock::create(vm, wasmCodeBlock.copyRef(), m_module.get());
         m_codeBlock.set(vm, this, codeBlock);
         module()->setCodeBlock(vm, memoryMode(), codeBlock);
     }

Modified: trunk/Tools/ChangeLog (216172 => 216173)


--- trunk/Tools/ChangeLog	2017-05-04 04:48:07 UTC (rev 216172)
+++ trunk/Tools/ChangeLog	2017-05-04 05:50:01 UTC (rev 216173)
@@ -1,3 +1,12 @@
+2017-05-03  Saam Barati  <[email protected]>
+
+        How we build polymorphic cases is wrong when making a call from Wasm
+        https://bugs.webkit.org/show_bug.cgi?id=171527
+
+        Reviewed by JF Bastien.
+
+        * Scripts/run-jsc-stress-tests:
+
 2017-05-03  Eric Carlson  <[email protected]>
 
         [MediaStream] Allow host application to enable/disable media capture

Modified: trunk/Tools/Scripts/run-jsc-stress-tests (216172 => 216173)


--- trunk/Tools/Scripts/run-jsc-stress-tests	2017-05-04 04:48:07 UTC (rev 216172)
+++ trunk/Tools/Scripts/run-jsc-stress-tests	2017-05-04 05:50:01 UTC (rev 216173)
@@ -1200,6 +1200,8 @@
     prepareExtraRelativeFiles(modules.map { |f| "../" + f }, $collection)
     run("default-wasm", "-m", *FTL_OPTIONS)
     run("wasm-no-cjit", "-m", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
+    run("wasm-eager-jettison", "-m", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
+    run("wasm-no-call-ic", "-m", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
 end
 
 def runWebAssemblyEmscripten(mode)
@@ -1213,6 +1215,8 @@
     prepareExtraRelativeFiles([Pathname('..') + wasm], $collection)
     run("default-wasm", *FTL_OPTIONS)
     run("wasm-no-cjit", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
+    run("wasm-eager-jettison", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
+    run("wasm-no-call-ic", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
 end
 
 def runWebAssemblySpecTest(mode)
@@ -1233,6 +1237,8 @@
 
     runWithOutputHandler("default-wasm", noisyOutputHandler, "../spec-harness.js", *FTL_OPTIONS)
     runWithOutputHandler("wasm-no-cjit", noisyOutputHandler, "../spec-harness.js", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
+    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)
 end
 
 def runChakra(mode, exception, baselineFile, extraFiles)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to