[Cocoa] Add a WKA extension point
[WebKit-https.git] / Source / JavaScriptCore / wasm / js / WebAssemblyFunction.cpp
1 /*
2  * Copyright (C) 2016-2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WebAssemblyFunction.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "B3Compilation.h"
32 #include "JSCInlines.h"
33 #include "JSFunctionInlines.h"
34 #include "JSObject.h"
35 #include "JSWebAssemblyHelpers.h"
36 #include "JSWebAssemblyInstance.h"
37 #include "JSWebAssemblyMemory.h"
38 #include "JSWebAssemblyRuntimeError.h"
39 #include "LLIntThunks.h"
40 #include "LinkBuffer.h"
41 #include "ProtoCallFrame.h"
42 #include "VM.h"
43 #include "WasmCallee.h"
44 #include "WasmCallingConvention.h"
45 #include "WasmContextInlines.h"
46 #include "WasmFormat.h"
47 #include "WasmMemory.h"
48 #include "WasmMemoryInformation.h"
49 #include "WasmModuleInformation.h"
50 #include "WasmSignatureInlines.h"
51 #include <wtf/FastTLS.h>
52 #include <wtf/StackPointer.h>
53 #include <wtf/SystemTracing.h>
54
55 namespace JSC {
56
57 const ClassInfo WebAssemblyFunction::s_info = { "WebAssemblyFunction", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(WebAssemblyFunction) };
58
59 static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec)
60 {
61     VM& vm = exec->vm();
62     auto scope = DECLARE_THROW_SCOPE(vm);
63     WebAssemblyFunction* wasmFunction = jsCast<WebAssemblyFunction*>(exec->jsCallee());
64     Wasm::SignatureIndex signatureIndex = wasmFunction->signatureIndex();
65     const Wasm::Signature& signature = Wasm::SignatureInformation::get(signatureIndex);
66
67     // Make sure that the memory we think we are going to run with matches the one we expect.
68     ASSERT(wasmFunction->instance()->instance().codeBlock()->isSafeToRun(wasmFunction->instance()->memory()->memory().mode()));
69
70     Optional<TraceScope> traceScope;
71     if (Options::useTracePoints())
72         traceScope.emplace(WebAssemblyExecuteStart, WebAssemblyExecuteEnd);
73
74     Vector<JSValue, MarkedArgumentBuffer::inlineCapacity> boxedArgs;
75     JSWebAssemblyInstance* instance = wasmFunction->instance();
76     Wasm::Instance* wasmInstance = &instance->instance();
77     // When we don't use fast TLS to store the context, the JS
78     // entry wrapper expects a JSWebAssemblyInstance as the first argument.
79     if (!Wasm::Context::useFastTLS())
80         boxedArgs.append(instance);
81
82     for (unsigned argIndex = 0; argIndex < signature.argumentCount(); ++argIndex) {
83         JSValue arg = exec->argument(argIndex);
84         switch (signature.argument(argIndex)) {
85         case Wasm::I32:
86             arg = JSValue::decode(arg.toInt32(exec));
87             break;
88         case Wasm::Funcref: {
89             if (!isWebAssemblyHostFunction(vm, arg) && !arg.isNull())
90                 return JSValue::encode(throwException(exec, scope, createJSWebAssemblyRuntimeError(exec, vm, "Funcref must be an exported wasm function")));
91             break;
92         }
93         case Wasm::Anyref:
94             break;
95         case Wasm::I64:
96             arg = JSValue();
97             break;
98         case Wasm::F32:
99             arg = JSValue::decode(bitwise_cast<uint32_t>(arg.toFloat(exec)));
100             break;
101         case Wasm::F64:
102             arg = JSValue::decode(bitwise_cast<uint64_t>(arg.toNumber(exec)));
103             break;
104         case Wasm::Void:
105         case Wasm::Func:
106             RELEASE_ASSERT_NOT_REACHED();
107         }
108         RETURN_IF_EXCEPTION(scope, encodedJSValue());
109         boxedArgs.append(arg);
110     }
111
112     JSValue firstArgument = JSValue();
113     int argCount = 1;
114     JSValue* remainingArgs = nullptr;
115     if (boxedArgs.size()) {
116         remainingArgs = boxedArgs.data();
117         firstArgument = *remainingArgs;
118         remainingArgs++;
119         argCount = boxedArgs.size();
120     }
121
122     // Note: we specifically use the WebAssemblyFunction as the callee to begin with in the ProtoCallFrame.
123     // The reason for this is that calling into the llint may stack overflow, and the stack overflow
124     // handler might read the global object from the callee.
125     ProtoCallFrame protoCallFrame;
126     protoCallFrame.init(nullptr, wasmFunction, firstArgument, argCount, remainingArgs);
127
128     // FIXME Do away with this entire function, and only use the entrypoint generated by B3. https://bugs.webkit.org/show_bug.cgi?id=166486
129     Wasm::Instance* prevWasmInstance = vm.wasmContext.load();
130     {
131         // We do the stack check here for the wrapper function because we don't
132         // want to emit a stack check inside every wrapper function.
133         const intptr_t sp = bitwise_cast<intptr_t>(currentStackPointer());
134         const intptr_t frameSize = (boxedArgs.size() + CallFrame::headerSizeInRegisters) * sizeof(Register);
135         const intptr_t stackSpaceUsed = 2 * frameSize; // We're making two calls. One to the wrapper, and one to the actual wasm code.
136         if (UNLIKELY((sp < stackSpaceUsed) || ((sp - stackSpaceUsed) < bitwise_cast<intptr_t>(vm.softStackLimit()))))
137             return JSValue::encode(throwException(exec, scope, createStackOverflowError(exec)));
138     }
139     vm.wasmContext.store(wasmInstance, vm.softStackLimit());
140     ASSERT(wasmFunction->instance());
141     ASSERT(&wasmFunction->instance()->instance() == vm.wasmContext.load());
142     EncodedJSValue rawResult = vmEntryToWasm(wasmFunction->jsEntrypoint(MustCheckArity).executableAddress(), &vm, &protoCallFrame);
143     // We need to make sure this is in a register or on the stack since it's stored in Vector<JSValue>.
144     // This probably isn't strictly necessary, since the WebAssemblyFunction* should keep the instance
145     // alive. But it's good hygiene.
146     instance->use();
147     if (prevWasmInstance != wasmInstance) {
148         // This is just for some extra safety instead of leaving a cached
149         // value in there. If we ever forget to set the value to be a real
150         // bounds, this will force every stack overflow check to immediately
151         // fire. The stack limit never changes while executing except when
152         // WebAssembly is used through the JSC API: API users can ask the code
153         // to migrate threads.
154         wasmInstance->setCachedStackLimit(bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()));
155     }
156     vm.wasmContext.store(prevWasmInstance, vm.softStackLimit());
157     RETURN_IF_EXCEPTION(scope, { });
158
159     return rawResult;
160 }
161
162 bool WebAssemblyFunction::useTagRegisters() const
163 {
164     const auto& signature = Wasm::SignatureInformation::get(signatureIndex());
165     return signature.argumentCount() || signature.returnType() != Wasm::Void;
166 }
167
168 RegisterSet WebAssemblyFunction::calleeSaves() const
169 {
170     RegisterSet toSave = Wasm::PinnedRegisterInfo::get().toSave(instance()->memoryMode());
171     if (useTagRegisters()) {
172         RegisterSet tagRegisters = RegisterSet::runtimeTagRegisters();
173         // We rely on these being disjoint sets.
174 #if !ASSERT_DISABLED
175         for (Reg reg : tagRegisters)
176             ASSERT(!toSave.contains(reg));
177 #endif
178         toSave.merge(tagRegisters);
179     }
180     return toSave;
181 }
182
183 RegisterAtOffsetList WebAssemblyFunction::usedCalleeSaveRegisters() const
184 {
185     return RegisterAtOffsetList { calleeSaves(), RegisterAtOffsetList::OffsetBaseType::FramePointerBased };
186 }
187
188 ptrdiff_t WebAssemblyFunction::previousInstanceOffset() const
189 {
190     ptrdiff_t result = calleeSaves().numberOfSetRegisters() * sizeof(CPURegister);
191     result = -result - sizeof(CPURegister);
192 #if !ASSERT_DISABLED
193     ptrdiff_t minOffset = 1;
194     for (const RegisterAtOffset& regAtOffset : usedCalleeSaveRegisters()) {
195         ptrdiff_t offset = regAtOffset.offset();
196         ASSERT(offset < 0);
197         minOffset = std::min(offset, minOffset);
198     }
199     ASSERT(minOffset - static_cast<ptrdiff_t>(sizeof(CPURegister)) == result);
200 #endif
201     return result;
202 }
203
204 Wasm::Instance* WebAssemblyFunction::previousInstance(CallFrame* callFrame)
205 {
206     ASSERT(callFrame->callee().rawPtr() == m_jsToWasmICCallee.get());
207     auto* result = *bitwise_cast<Wasm::Instance**>(bitwise_cast<char*>(callFrame) + previousInstanceOffset());
208     return result;
209 }
210
211 MacroAssemblerCodePtr<JSEntryPtrTag> WebAssemblyFunction::jsCallEntrypointSlow()
212 {
213     VM& vm = this->vm();
214     CCallHelpers jit;
215
216     const auto& signature = Wasm::SignatureInformation::get(signatureIndex());
217     const auto& pinnedRegs = Wasm::PinnedRegisterInfo::get();
218     RegisterAtOffsetList registersToSpill = usedCalleeSaveRegisters();
219
220     auto& moduleInformation = instance()->instance().module().moduleInformation();
221
222     unsigned totalFrameSize = registersToSpill.size() * sizeof(CPURegister);
223     totalFrameSize += sizeof(CPURegister); // Slot for the VM's previous wasm instance.
224     totalFrameSize += Wasm::WasmCallingConvention::headerSizeInBytes();
225     totalFrameSize -= sizeof(CallerFrameAndPC);
226
227     unsigned numGPRs = 0;
228     unsigned numFPRs = 0;
229     bool argumentsIncludeI64 = false;
230     for (unsigned i = 0; i < signature.argumentCount(); i++) {
231         switch (signature.argument(i)) {
232         case Wasm::I64:
233             argumentsIncludeI64 = true;
234             break;
235         case Wasm::Anyref:
236         case Wasm::Funcref:
237         case Wasm::I32:
238             if (numGPRs >= Wasm::wasmCallingConvention().m_gprArgs.size())
239                 totalFrameSize += sizeof(CPURegister);
240             ++numGPRs;
241             break;
242         case Wasm::F32:
243         case Wasm::F64:
244             if (numFPRs >= Wasm::wasmCallingConvention().m_fprArgs.size())
245                 totalFrameSize += sizeof(CPURegister);
246             ++numFPRs;
247             break;
248         default:
249             RELEASE_ASSERT_NOT_REACHED();
250         }
251     }
252
253     if (argumentsIncludeI64)
254         return nullptr;
255
256     totalFrameSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), totalFrameSize);
257
258     jit.emitFunctionPrologue();
259     jit.subPtr(MacroAssembler::TrustedImm32(totalFrameSize), MacroAssembler::stackPointerRegister);
260     jit.store64(CCallHelpers::TrustedImm64(0), CCallHelpers::addressFor(CallFrameSlot::codeBlock));
261
262     for (const RegisterAtOffset& regAtOffset : registersToSpill) {
263         GPRReg reg = regAtOffset.reg().gpr();
264         ptrdiff_t offset = regAtOffset.offset();
265         jit.storePtr(reg, CCallHelpers::Address(GPRInfo::callFrameRegister, offset));
266     }
267
268     GPRReg scratchGPR = Wasm::wasmCallingConventionAir().prologueScratch(1);
269     bool stackLimitGPRIsClobbered = false;
270     GPRReg stackLimitGPR = Wasm::wasmCallingConventionAir().prologueScratch(0);
271     jit.loadPtr(vm.addressOfSoftStackLimit(), stackLimitGPR);
272
273     CCallHelpers::JumpList slowPath;
274     slowPath.append(jit.branchPtr(CCallHelpers::Above, MacroAssembler::stackPointerRegister, GPRInfo::callFrameRegister));
275     slowPath.append(jit.branchPtr(CCallHelpers::Below, MacroAssembler::stackPointerRegister, stackLimitGPR));
276
277     // Ensure:
278     // argCountPlusThis - 1 >= signature.argumentCount()
279     // argCountPlusThis >= signature.argumentCount() + 1
280     // FIXME: We should handle mismatched arity
281     // https://bugs.webkit.org/show_bug.cgi?id=196564
282     slowPath.append(jit.branch32(CCallHelpers::Below,
283         CCallHelpers::payloadFor(CallFrameSlot::argumentCount), CCallHelpers::TrustedImm32(signature.argumentCount() + 1)));
284
285     if (useTagRegisters())
286         jit.emitMaterializeTagCheckRegisters();
287
288     // First we do stack slots for FPRs so we can use FPR argument registers as scratch.
289     // After that, we handle FPR argument registers.
290     // We also handle all GPR types here as we have GPR scratch registers.
291     {
292         CCallHelpers::Address calleeFrame = CCallHelpers::Address(MacroAssembler::stackPointerRegister, -static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC)));
293         numGPRs = 0;
294         numFPRs = 0;
295         FPRReg scratchFPR = Wasm::wasmCallingConvention().m_fprArgs[0].fpr();
296
297         ptrdiff_t jsOffset = CallFrameSlot::firstArgument * sizeof(EncodedJSValue);
298
299         ptrdiff_t wasmOffset = CallFrame::headerSizeInRegisters * sizeof(CPURegister);
300         for (unsigned i = 0; i < signature.argumentCount(); i++) {
301             switch (signature.argument(i)) {
302             case Wasm::I32:
303                 jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchGPR);
304                 slowPath.append(jit.branchIfNotInt32(scratchGPR));
305                 if (numGPRs >= Wasm::wasmCallingConvention().m_gprArgs.size()) {
306                     jit.store32(scratchGPR, calleeFrame.withOffset(wasmOffset));
307                     wasmOffset += sizeof(CPURegister);
308                 } else {
309                     jit.zeroExtend32ToPtr(scratchGPR, Wasm::wasmCallingConvention().m_gprArgs[numGPRs].gpr());
310                     ++numGPRs;
311                 }
312                 break;
313             case Wasm::Funcref: {
314                 // Ensure we have a WASM exported function.
315                 jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchGPR);
316                 auto isNull = jit.branchIfNull(scratchGPR);
317                 slowPath.append(jit.branchIfNotCell(scratchGPR));
318
319                 stackLimitGPRIsClobbered = true;
320                 jit.emitLoadStructure(vm, scratchGPR, scratchGPR, stackLimitGPR);
321                 jit.loadPtr(CCallHelpers::Address(scratchGPR, Structure::classInfoOffset()), scratchGPR);
322
323                 static_assert(std::is_final<WebAssemblyFunction>::value, "We do not check for subtypes below");
324                 static_assert(std::is_final<WebAssemblyWrapperFunction>::value, "We do not check for subtypes below");
325
326                 auto isWasmFunction = jit.branchPtr(CCallHelpers::Equal, scratchGPR, CCallHelpers::TrustedImmPtr(WebAssemblyFunction::info()));
327                 slowPath.append(jit.branchPtr(CCallHelpers::NotEqual, scratchGPR, CCallHelpers::TrustedImmPtr(WebAssemblyWrapperFunction::info())));
328
329                 isWasmFunction.link(&jit);
330                 isNull.link(&jit);
331                 FALLTHROUGH;
332             }
333             case Wasm::Anyref: {
334                 jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchGPR);
335
336                 if (numGPRs >= Wasm::wasmCallingConvention().m_gprArgs.size()) {
337                     jit.store64(scratchGPR, calleeFrame.withOffset(wasmOffset));
338                     wasmOffset += sizeof(CPURegister);
339                 } else {
340                     jit.move(scratchGPR, Wasm::wasmCallingConvention().m_gprArgs[numGPRs].gpr());
341                     ++numGPRs;
342                 }
343                 break;
344             }
345             case Wasm::F32:
346             case Wasm::F64:
347                 if (numFPRs >= Wasm::wasmCallingConvention().m_fprArgs.size()) {
348                     jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchGPR);
349                     slowPath.append(jit.branchIfNotNumber(scratchGPR));
350                     auto isInt32 = jit.branchIfInt32(scratchGPR);
351                     if (signature.argument(i) == Wasm::F32) {
352                         jit.unboxDouble(scratchGPR, scratchGPR, scratchFPR);
353                         jit.convertDoubleToFloat(scratchFPR, scratchFPR);
354                         jit.storeFloat(scratchFPR, calleeFrame.withOffset(wasmOffset));
355                     } else {
356                         jit.add64(GPRInfo::tagTypeNumberRegister, scratchGPR, scratchGPR);
357                         jit.store64(scratchGPR, calleeFrame.withOffset(wasmOffset));
358                     }
359                     auto done = jit.jump();
360
361                     isInt32.link(&jit);
362                     if (signature.argument(i) == Wasm::F32) {
363                         jit.convertInt32ToFloat(scratchGPR, scratchFPR);
364                         jit.storeFloat(scratchFPR, calleeFrame.withOffset(wasmOffset));
365                     } else {
366                         jit.convertInt32ToDouble(scratchGPR, scratchFPR);
367                         jit.storeDouble(scratchFPR, calleeFrame.withOffset(wasmOffset));
368                     }
369                     done.link(&jit);
370
371                     wasmOffset += sizeof(CPURegister);
372                 } else
373                     ++numFPRs;
374                 break;
375             default:
376                 RELEASE_ASSERT_NOT_REACHED();
377             }
378
379             jsOffset += sizeof(EncodedJSValue);
380         }
381     }
382
383     // Now handle FPR arguments in registers.
384     {
385         numFPRs = 0;
386         ptrdiff_t jsOffset = CallFrameSlot::firstArgument * sizeof(EncodedJSValue);
387         for (unsigned i = 0; i < signature.argumentCount(); i++) {
388             switch (signature.argument(i)) {
389             case Wasm::F32:
390             case Wasm::F64:
391                 if (numFPRs < Wasm::wasmCallingConvention().m_fprArgs.size()) {
392                     FPRReg argFPR = Wasm::wasmCallingConvention().m_fprArgs[numFPRs].fpr();
393                     jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchGPR);
394                     slowPath.append(jit.branchIfNotNumber(scratchGPR));
395                     auto isInt32 = jit.branchIfInt32(scratchGPR);
396                     jit.unboxDouble(scratchGPR, scratchGPR, argFPR);
397                     if (signature.argument(i) == Wasm::F32)
398                         jit.convertDoubleToFloat(argFPR, argFPR);
399                     auto done = jit.jump();
400
401                     isInt32.link(&jit);
402                     if (signature.argument(i) == Wasm::F32)
403                         jit.convertInt32ToFloat(scratchGPR, argFPR);
404                     else
405                         jit.convertInt32ToDouble(scratchGPR, argFPR);
406
407                     done.link(&jit);
408                     ++numFPRs;
409                 }
410                 break;
411             default:
412                 break;
413             }
414
415             jsOffset += sizeof(EncodedJSValue);
416         }
417     }
418
419     // At this point, we're committed to doing a fast call.
420
421     if (Wasm::Context::useFastTLS()) 
422         jit.loadWasmContextInstance(scratchGPR);
423     else
424         jit.loadPtr(vm.wasmContext.pointerToInstance(), scratchGPR);
425     ptrdiff_t previousInstanceOffset = this->previousInstanceOffset();
426     jit.storePtr(scratchGPR, CCallHelpers::Address(GPRInfo::callFrameRegister, previousInstanceOffset));
427
428     jit.move(CCallHelpers::TrustedImmPtr(&instance()->instance()), scratchGPR);
429     if (Wasm::Context::useFastTLS()) 
430         jit.storeWasmContextInstance(scratchGPR);
431     else {
432         jit.move(scratchGPR, pinnedRegs.wasmContextInstancePointer);
433         jit.storePtr(scratchGPR, vm.wasmContext.pointerToInstance());
434     }
435     if (stackLimitGPRIsClobbered)
436         jit.loadPtr(vm.addressOfSoftStackLimit(), stackLimitGPR);
437     jit.storePtr(stackLimitGPR, CCallHelpers::Address(scratchGPR, Wasm::Instance::offsetOfCachedStackLimit()));
438
439     if (!!moduleInformation.memory) {
440         GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
441         GPRReg scratchOrSize = stackLimitGPR;
442         auto mode = instance()->memoryMode();
443
444         if (isARM64E()) {
445             if (mode != Wasm::MemoryMode::Signaling)
446                 scratchOrSize = pinnedRegs.sizeRegister;
447             jit.loadPtr(CCallHelpers::Address(scratchGPR, Wasm::Instance::offsetOfCachedMemorySize()), scratchOrSize);
448         } else {
449             if (mode != Wasm::MemoryMode::Signaling)
450                 jit.loadPtr(CCallHelpers::Address(scratchGPR, Wasm::Instance::offsetOfCachedMemorySize()), pinnedRegs.sizeRegister);
451         }
452
453         jit.loadPtr(CCallHelpers::Address(scratchGPR, Wasm::Instance::offsetOfCachedMemory()), baseMemory);
454         jit.cageConditionally(Gigacage::Primitive, baseMemory, scratchOrSize, scratchOrSize);
455     }
456
457     // We use this callee to indicate how to unwind past these types of frames:
458     // 1. We need to know where to get callee saves.
459     // 2. We need to know to restore the previous wasm context.
460     if (!m_jsToWasmICCallee)
461         m_jsToWasmICCallee.set(vm, this, JSToWasmICCallee::create(vm, globalObject(), this));
462     jit.storePtr(CCallHelpers::TrustedImmPtr(m_jsToWasmICCallee.get()), CCallHelpers::addressFor(CallFrameSlot::callee));
463
464     {
465         // FIXME: Currently we just do an indirect jump. But we should teach the Module
466         // how to repatch us:
467         // https://bugs.webkit.org/show_bug.cgi?id=196570
468         jit.loadPtr(boxedCalleeLoadLocation(), scratchGPR);
469         jit.storePtr(scratchGPR, CCallHelpers::Address(CCallHelpers::stackPointerRegister, CallFrameSlot::callee * sizeof(Register) - sizeof(CallerFrameAndPC)));
470         jit.loadPtr(entrypointLoadLocation(), scratchGPR);
471         jit.call(scratchGPR, WasmEntryPtrTag);
472     }
473
474     ASSERT(!RegisterSet::runtimeTagRegisters().contains(GPRInfo::nonPreservedNonReturnGPR));
475     jit.loadPtr(CCallHelpers::Address(GPRInfo::callFrameRegister, previousInstanceOffset), GPRInfo::nonPreservedNonReturnGPR);
476     if (Wasm::Context::useFastTLS())
477         jit.storeWasmContextInstance(GPRInfo::nonPreservedNonReturnGPR);
478     else
479         jit.storePtr(GPRInfo::nonPreservedNonReturnGPR, vm.wasmContext.pointerToInstance());
480
481     switch (signature.returnType()) {
482     case Wasm::Void:
483         jit.moveTrustedValue(jsUndefined(), JSValueRegs { GPRInfo::returnValueGPR });
484         break;
485     case Wasm::I32:
486         jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
487         jit.boxInt32(GPRInfo::returnValueGPR, JSValueRegs { GPRInfo::returnValueGPR });
488         break;
489     case Wasm::F32:
490         jit.convertFloatToDouble(FPRInfo::returnValueFPR, FPRInfo::returnValueFPR);
491         FALLTHROUGH;
492     case Wasm::F64: {
493         jit.moveTrustedValue(jsNumber(pureNaN()), JSValueRegs { GPRInfo::returnValueGPR });
494         auto isNaN = jit.branchIfNaN(FPRInfo::returnValueFPR);
495         jit.boxDouble(FPRInfo::returnValueFPR, JSValueRegs { GPRInfo::returnValueGPR });
496         isNaN.link(&jit);
497         break;
498     }
499     case Wasm::Funcref:
500     case Wasm::Anyref:
501         break;
502     case Wasm::I64:
503     case Wasm::Func:
504         return nullptr;
505     default:
506         break;
507     }
508
509     auto emitRestoreCalleeSaves = [&] {
510         for (const RegisterAtOffset& regAtOffset : registersToSpill) {
511             GPRReg reg = regAtOffset.reg().gpr();
512             ASSERT(reg != GPRInfo::returnValueGPR);
513             ptrdiff_t offset = regAtOffset.offset();
514             jit.loadPtr(CCallHelpers::Address(GPRInfo::callFrameRegister, offset), reg);
515         }
516     };
517
518     emitRestoreCalleeSaves();
519
520     jit.emitFunctionEpilogue();
521     jit.ret();
522
523     slowPath.link(&jit);
524     emitRestoreCalleeSaves();
525     jit.move(CCallHelpers::TrustedImmPtr(this), GPRInfo::regT0);
526     jit.emitFunctionEpilogue();
527 #if CPU(ARM64E)
528     jit.untagReturnAddress();
529 #endif
530     auto jumpToHostCallThunk = jit.jump();
531
532     LinkBuffer linkBuffer(jit, nullptr, JITCompilationCanFail);
533     if (UNLIKELY(linkBuffer.didFailToAllocate()))
534         return nullptr;
535
536     linkBuffer.link(jumpToHostCallThunk, CodeLocationLabel<JSEntryPtrTag>(executable()->entrypointFor(CodeForCall, MustCheckArity).executableAddress()));
537     m_jsCallEntrypoint = FINALIZE_CODE(linkBuffer, WasmEntryPtrTag, "JS->Wasm IC");
538     return m_jsCallEntrypoint.code();
539 }
540
541 WebAssemblyFunction* WebAssemblyFunction::create(VM& vm, JSGlobalObject* globalObject, Structure* structure, unsigned length, const String& name, JSWebAssemblyInstance* instance, Wasm::Callee& jsEntrypoint, Wasm::WasmToWasmImportableFunction::LoadLocation wasmToWasmEntrypointLoadLocation, void** boxedCalleeLoadLocation, Wasm::SignatureIndex signatureIndex)
542 {
543     NativeExecutable* executable = vm.getHostFunction(callWebAssemblyFunction, NoIntrinsic, callHostFunctionAsConstructor, nullptr, name);
544     WebAssemblyFunction* function = new (NotNull, allocateCell<WebAssemblyFunction>(vm.heap)) WebAssemblyFunction(vm, globalObject, structure, jsEntrypoint, wasmToWasmEntrypointLoadLocation, boxedCalleeLoadLocation, signatureIndex);
545     function->finishCreation(vm, executable, length, name, instance);
546     ASSERT_WITH_MESSAGE(!function->isLargeAllocation(), "WebAssemblyFunction should be allocated not in large allocation since it is JSCallee.");
547     return function;
548 }
549
550 Structure* WebAssemblyFunction::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
551 {
552     ASSERT(globalObject);
553     return Structure::create(vm, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), info());
554 }
555
556 WebAssemblyFunction::WebAssemblyFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, Wasm::Callee& jsEntrypoint, Wasm::WasmToWasmImportableFunction::LoadLocation wasmToWasmEntrypointLoadLocation, void** boxedCalleeLoadLocation, Wasm::SignatureIndex signatureIndex)
557     : Base { vm, globalObject, structure }
558     , m_jsEntrypoint { jsEntrypoint.code() }
559     , m_importableFunction { signatureIndex, wasmToWasmEntrypointLoadLocation, boxedCalleeLoadLocation }
560 { }
561
562 void WebAssemblyFunction::visitChildren(JSCell* cell, SlotVisitor& visitor)
563 {
564     WebAssemblyFunction* thisObject = jsCast<WebAssemblyFunction*>(cell);
565     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
566
567     Base::visitChildren(thisObject, visitor);
568     visitor.append(thisObject->m_jsToWasmICCallee);
569 }
570
571 void WebAssemblyFunction::destroy(JSCell* cell)
572 {
573     static_cast<WebAssemblyFunction*>(cell)->WebAssemblyFunction::~WebAssemblyFunction();
574 }
575
576 } // namespace JSC
577
578 #endif // ENABLE(WEBASSEMBLY)