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