8e718ea600d718af5500909437619ad170ee5424
[WebKit-https.git] / Source / JavaScriptCore / wasm / js / WasmToJS.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 "WasmToJS.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "CCallHelpers.h"
32 #include "FrameTracers.h"
33 #include "JITExceptions.h"
34 #include "JSCInlines.h"
35 #include "JSWebAssemblyHelpers.h"
36 #include "JSWebAssemblyInstance.h"
37 #include "JSWebAssemblyRuntimeError.h"
38 #include "LinkBuffer.h"
39 #include "NativeErrorConstructor.h"
40 #include "ThunkGenerators.h"
41 #include "WasmCallingConvention.h"
42 #include "WasmContext.h"
43 #include "WasmExceptionType.h"
44 #include "WasmInstance.h"
45 #include "WasmSignatureInlines.h"
46
47 namespace JSC { namespace Wasm {
48
49 using JIT = CCallHelpers;
50
51 static void materializeImportJSCell(JIT& jit, unsigned importIndex, GPRReg result)
52 {
53     // We're calling out of the current WebAssembly.Instance. That Instance has a list of all its import functions.
54     jit.loadWasmContextInstance(result);
55     jit.loadPtr(JIT::Address(result, Instance::offsetOfImportFunction(importIndex)), result);
56 }
57
58 static Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> handleBadI64Use(VM* vm, JIT& jit, const Signature& signature, unsigned importIndex)
59 {
60     unsigned argCount = signature.argumentCount();
61
62     bool hasBadI64Use = false;
63     hasBadI64Use |= signature.returnType() == I64;
64     for (unsigned argNum = 0; argNum < argCount && !hasBadI64Use; ++argNum) {
65         Type argType = signature.argument(argNum);
66         switch (argType) {
67         case Void:
68         case Func:
69             RELEASE_ASSERT_NOT_REACHED();
70
71         case I64: {
72             hasBadI64Use = true;
73             break;
74         }
75
76         default:
77             break;
78         }
79     }
80
81     if (hasBadI64Use) {
82         jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm->topEntryFrame);
83         jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
84         jit.loadWasmContextInstance(GPRInfo::argumentGPR1);
85
86         // Store Callee.
87         jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR1, Instance::offsetOfOwner()), GPRInfo::argumentGPR1);
88         jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR1, JSWebAssemblyInstance::offsetOfCallee()), GPRInfo::argumentGPR2);
89         jit.storePtr(GPRInfo::argumentGPR2, JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
90
91         auto call = jit.call(OperationPtrTag);
92         jit.jumpToExceptionHandler(*vm);
93
94         void (*throwBadI64)(ExecState*, JSWebAssemblyInstance*) = [] (ExecState* exec, JSWebAssemblyInstance* instance) -> void {
95             VM* vm = &exec->vm();
96             NativeCallFrameTracer tracer(vm, exec);
97
98             {
99                 auto throwScope = DECLARE_THROW_SCOPE(*vm);
100                 JSGlobalObject* globalObject = instance->globalObject(*vm);
101                 auto* error = ErrorInstance::create(exec, *vm, globalObject->errorStructure(ErrorType::TypeError), "i64 not allowed as return type or argument to an imported function"_s);
102                 throwException(exec, throwScope, error);
103             }
104
105             genericUnwind(vm, exec);
106             ASSERT(!!vm->callFrameForCatch);
107         };
108
109         LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID, JITCompilationCanFail);
110         if (UNLIKELY(linkBuffer.didFailToAllocate()))
111             return makeUnexpected(BindingFailure::OutOfMemory);
112
113         linkBuffer.link(call, FunctionPtr<OperationPtrTag>(throwBadI64));
114         return FINALIZE_CODE(linkBuffer, WasmEntryPtrTag, "WebAssembly->JavaScript invalid i64 use in import[%i]", importIndex);
115     }
116     
117     return MacroAssemblerCodeRef<WasmEntryPtrTag>();
118 }
119
120 Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex signatureIndex, unsigned importIndex)
121 {
122     // FIXME: This function doesn't properly abstract away the calling convention.
123     // It'd be super easy to do so: https://bugs.webkit.org/show_bug.cgi?id=169401
124     const WasmCallingConvention& wasmCC = wasmCallingConvention();
125     const JSCCallingConvention& jsCC = jscCallingConvention();
126     const Signature& signature = SignatureInformation::get(signatureIndex);
127     unsigned argCount = signature.argumentCount();
128     JIT jit;
129
130     // Note: WasmB3IRGenerator assumes that this stub treats SP as a callee save.
131     // If we ever change this, we will also need to change WasmB3IRGenerator.
132
133     // Below, we assume that the JS calling convention is always on the stack.
134     ASSERT(!jsCC.m_gprArgs.size());
135     ASSERT(!jsCC.m_fprArgs.size());
136
137     jit.emitFunctionPrologue();
138     jit.store64(JIT::TrustedImm32(0), JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::codeBlock * static_cast<int>(sizeof(Register)))); // FIXME Stop using 0 as codeBlocks. https://bugs.webkit.org/show_bug.cgi?id=165321
139
140     auto badI64 = handleBadI64Use(vm, jit, signature, importIndex);
141     if (!badI64 || badI64.value())
142         return badI64;
143
144     // Here we assume that the JS calling convention saves at least all the wasm callee saved. We therefore don't need to save and restore more registers since the wasm callee already took care of this.
145     RegisterSet missingCalleeSaves = wasmCC.m_calleeSaveRegisters;
146     missingCalleeSaves.exclude(jsCC.m_calleeSaveRegisters);
147     ASSERT(missingCalleeSaves.isEmpty());
148
149     if (!Options::useCallICsForWebAssemblyToJSCalls()) {
150         ScratchBuffer* scratchBuffer = vm->scratchBufferForSize(argCount * sizeof(uint64_t));
151         char* buffer = argCount ? static_cast<char*>(scratchBuffer->dataBuffer()) : nullptr;
152         unsigned marshalledGPRs = 0;
153         unsigned marshalledFPRs = 0;
154         unsigned bufferOffset = 0;
155         unsigned frOffset = CallFrame::headerSizeInRegisters * static_cast<int>(sizeof(Register));
156         const GPRReg scratchGPR = GPRInfo::regCS0;
157         jit.subPtr(MacroAssembler::TrustedImm32(WTF::roundUpToMultipleOf(stackAlignmentBytes(), sizeof(Register))), MacroAssembler::stackPointerRegister);
158         jit.storePtr(scratchGPR, MacroAssembler::Address(MacroAssembler::stackPointerRegister));
159
160         for (unsigned argNum = 0; argNum < argCount; ++argNum) {
161             Type argType = signature.argument(argNum);
162             switch (argType) {
163             case Void:
164             case Func:
165             case I64:
166                 RELEASE_ASSERT_NOT_REACHED();
167             case Anyref:
168             case Anyfunc:
169             case I32: {
170                 GPRReg gprReg;
171                 if (marshalledGPRs < wasmCC.m_gprArgs.size())
172                     gprReg = wasmCC.m_gprArgs[marshalledGPRs].gpr();
173                 else {
174                     // We've already spilled all arguments, these registers are available as scratch.
175                     gprReg = GPRInfo::argumentGPR0;
176                     jit.load64(JIT::Address(GPRInfo::callFrameRegister, frOffset), gprReg);
177                     frOffset += sizeof(Register);
178                 }
179                 if (argType == I32)
180                     jit.zeroExtend32ToPtr(gprReg, gprReg);
181                 jit.store64(gprReg, buffer + bufferOffset);
182                 ++marshalledGPRs;
183                 break;
184             }
185             case F32: {
186                 FPRReg fprReg;
187                 if (marshalledFPRs < wasmCC.m_fprArgs.size())
188                     fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
189                 else {
190                     // We've already spilled all arguments, these registers are available as scratch.
191                     fprReg = FPRInfo::argumentFPR0;
192                     jit.loadFloat(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
193                     frOffset += sizeof(Register);
194                 }
195                 jit.convertFloatToDouble(fprReg, fprReg);
196                 jit.moveDoubleTo64(fprReg, scratchGPR);
197                 jit.store64(scratchGPR, buffer + bufferOffset);
198                 ++marshalledFPRs;
199                 break;
200             }
201             case F64: {
202                 FPRReg fprReg;
203                 if (marshalledFPRs < wasmCC.m_fprArgs.size())
204                     fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
205                 else {
206                     // We've already spilled all arguments, these registers are available as scratch.
207                     fprReg = FPRInfo::argumentFPR0;
208                     jit.loadDouble(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
209                     frOffset += sizeof(Register);
210                 }
211                 jit.moveDoubleTo64(fprReg, scratchGPR);
212                 jit.store64(scratchGPR, buffer + bufferOffset);
213                 ++marshalledFPRs;
214                 break;
215             }
216             }
217
218             bufferOffset += sizeof(Register);
219         }
220         jit.loadPtr(MacroAssembler::Address(MacroAssembler::stackPointerRegister), scratchGPR);
221         if (argCount) {
222             // The GC should not look at this buffer at all, these aren't JSValues.
223             jit.move(CCallHelpers::TrustedImmPtr(scratchBuffer->addressOfActiveLength()), GPRInfo::argumentGPR0);
224             jit.storePtr(CCallHelpers::TrustedImmPtr(nullptr), GPRInfo::argumentGPR0);
225         }
226
227         uint64_t (*callFunc)(ExecState*, JSObject*, SignatureIndex, uint64_t*) =
228             [] (ExecState* exec, JSObject* callee, SignatureIndex signatureIndex, uint64_t* buffer) -> uint64_t { 
229                 VM* vm = &exec->vm();
230                 NativeCallFrameTracer tracer(vm, exec);
231                 auto throwScope = DECLARE_THROW_SCOPE(*vm);
232                 const Signature& signature = SignatureInformation::get(signatureIndex);
233                 MarkedArgumentBuffer args;
234                 for (unsigned argNum = 0; argNum < signature.argumentCount(); ++argNum) {
235                     Type argType = signature.argument(argNum);
236                     JSValue arg;
237                     switch (argType) {
238                     case Void:
239                     case Func:
240                     case I64:
241                         RELEASE_ASSERT_NOT_REACHED();
242                     case I32:
243                         arg = jsNumber(static_cast<int32_t>(buffer[argNum]));
244                         break;
245                     case Anyfunc: {
246                         arg = JSValue::decode(buffer[argNum]);
247                         ASSERT(isWebAssemblyHostFunction(*vm, arg) || arg.isNull());
248                         break;
249                     }
250                     case Anyref:
251                         arg = JSValue::decode(buffer[argNum]);
252                         break;
253                     case F32:
254                     case F64:
255                         arg = jsNumber(purifyNaN(bitwise_cast<double>(buffer[argNum])));
256                         break;
257                     }
258                     args.append(arg);
259                 }
260                 if (UNLIKELY(args.hasOverflowed())) {
261                     throwOutOfMemoryError(exec, throwScope);
262                     return 0;
263                 }
264
265                 CallData callData;
266                 CallType callType = callee->methodTable(*vm)->getCallData(callee, callData);
267                 RELEASE_ASSERT(callType != CallType::None);
268                 JSValue result = call(exec, callee, callType, callData, jsUndefined(), args);
269                 RETURN_IF_EXCEPTION(throwScope, 0);
270
271                 uint64_t realResult;
272                 switch (signature.returnType()) {
273                 case Func:
274                 case I64:
275                     RELEASE_ASSERT_NOT_REACHED();
276                     break;
277                 case Void:
278                     break;
279                 case I32: {
280                     realResult = static_cast<uint64_t>(static_cast<uint32_t>(result.toInt32(exec)));
281                     break;
282                 }
283                 case Anyfunc: {
284                     realResult = JSValue::encode(result);
285                     ASSERT(result.isFunction(*vm) || result.isNull());
286                     break;
287                 }
288                 case Anyref: {
289                     realResult = JSValue::encode(result);
290                     break;
291                 }
292                 case F64:
293                 case F32: {
294                     realResult = bitwise_cast<uint64_t>(result.toNumber(exec));
295                     break;
296                 }
297                 }
298
299                 RETURN_IF_EXCEPTION(throwScope, 0);
300                 return realResult;
301             };
302         
303         jit.loadWasmContextInstance(GPRInfo::argumentGPR0);
304         jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR0, Instance::offsetOfOwner()), GPRInfo::argumentGPR0);
305         jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR0, JSWebAssemblyInstance::offsetOfCallee()), GPRInfo::argumentGPR0);
306         jit.storePtr(GPRInfo::argumentGPR0, JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
307         
308         materializeImportJSCell(jit, importIndex, GPRInfo::argumentGPR1);
309         
310         static_assert(GPRInfo::numberOfArgumentRegisters >= 4, "We rely on this with the call below.");
311         static_assert(sizeof(SignatureIndex) == sizeof(uint64_t), "Following code assumes SignatureIndex is 64bit.");
312         jit.setupArguments<decltype(callFunc)>(GPRInfo::argumentGPR1, CCallHelpers::TrustedImm64(signatureIndex), CCallHelpers::TrustedImmPtr(buffer));
313         auto call = jit.call(OperationPtrTag);
314         auto noException = jit.emitExceptionCheck(*vm, AssemblyHelpers::InvertedExceptionCheck);
315
316         // Exception here.
317         jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm->topEntryFrame);
318         jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
319         void (*doUnwinding)(ExecState*) = [] (ExecState* exec) -> void {
320             VM* vm = &exec->vm();
321             NativeCallFrameTracer tracer(vm, exec);
322             genericUnwind(vm, exec);
323             ASSERT(!!vm->callFrameForCatch);
324         };
325         auto exceptionCall = jit.call(OperationPtrTag);
326         jit.jumpToExceptionHandler(*vm);
327
328         noException.link(&jit);
329         switch (signature.returnType()) {
330         case F64: {
331             jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
332             break;
333         }
334         case F32: {
335             jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
336             jit.convertDoubleToFloat(FPRInfo::returnValueFPR, FPRInfo::returnValueFPR);
337             break;
338         }
339         default:
340             break;
341         }
342
343         jit.emitFunctionEpilogue();
344         jit.ret();
345
346         LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID, JITCompilationCanFail);
347         if (UNLIKELY(linkBuffer.didFailToAllocate()))
348             return makeUnexpected(BindingFailure::OutOfMemory);
349
350         linkBuffer.link(call, FunctionPtr<OperationPtrTag>(callFunc));
351         linkBuffer.link(exceptionCall, FunctionPtr<OperationPtrTag>(doUnwinding));
352
353         return FINALIZE_CODE(linkBuffer, WasmEntryPtrTag, "WebAssembly->JavaScript import[%i] %s", importIndex, signature.toString().ascii().data());
354     }
355
356     // Note: We don't need to perform a stack check here since WasmB3IRGenerator
357     // will do the stack check for us. Whenever it detects that it might make
358     // a call to this thunk, it'll make sure its stack check includes space
359     // for us here.
360
361     const unsigned numberOfParameters = argCount + 1; // There is a "this" argument.
362     const unsigned numberOfRegsForCall = CallFrame::headerSizeInRegisters + numberOfParameters;
363     const unsigned numberOfBytesForCall = numberOfRegsForCall * sizeof(Register) - sizeof(CallerFrameAndPC);
364     const unsigned stackOffset = WTF::roundUpToMultipleOf(stackAlignmentBytes(), numberOfBytesForCall);
365     jit.subPtr(MacroAssembler::TrustedImm32(stackOffset), MacroAssembler::stackPointerRegister);
366     JIT::Address calleeFrame = CCallHelpers::Address(MacroAssembler::stackPointerRegister, -static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC)));
367
368     // FIXME make these loops which switch on Signature if there are many arguments on the stack. It'll otherwise be huge for huge signatures. https://bugs.webkit.org/show_bug.cgi?id=165547
369     
370     // First go through the integer parameters, freeing up their register for use afterwards.
371     {
372         unsigned marshalledGPRs = 0;
373         unsigned marshalledFPRs = 0;
374         unsigned calleeFrameOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register));
375         unsigned frOffset = CallFrame::headerSizeInRegisters * static_cast<int>(sizeof(Register));
376         for (unsigned argNum = 0; argNum < argCount; ++argNum) {
377             Type argType = signature.argument(argNum);
378             switch (argType) {
379             case Void:
380             case Func:
381             case I64:
382                 RELEASE_ASSERT_NOT_REACHED(); // Handled above.
383             case Anyref:
384             case Anyfunc:
385             case I32: {
386                 GPRReg gprReg;
387                 if (marshalledGPRs < wasmCC.m_gprArgs.size())
388                     gprReg = wasmCC.m_gprArgs[marshalledGPRs].gpr();
389                 else {
390                     // We've already spilled all arguments, these registers are available as scratch.
391                     gprReg = GPRInfo::argumentGPR0;
392                     jit.load64(JIT::Address(GPRInfo::callFrameRegister, frOffset), gprReg);
393                     frOffset += sizeof(Register);
394                 }
395                 ++marshalledGPRs;
396                 if (argType == I32) {
397                     jit.zeroExtend32ToPtr(gprReg, gprReg); // Clear non-int32 and non-tag bits.
398                     jit.boxInt32(gprReg, JSValueRegs(gprReg), DoNotHaveTagRegisters);
399                 }
400                 jit.store64(gprReg, calleeFrame.withOffset(calleeFrameOffset));
401                 calleeFrameOffset += sizeof(Register);
402                 break;
403             }
404             case F32:
405             case F64:
406                 // Skipped: handled below.
407                 if (marshalledFPRs >= wasmCC.m_fprArgs.size())
408                     frOffset += sizeof(Register);
409                 ++marshalledFPRs;
410                 calleeFrameOffset += sizeof(Register);
411                 break;
412             }
413         }
414     }
415     
416     {
417         // Integer registers have already been spilled, these are now available.
418         GPRReg doubleEncodeOffsetGPRReg = GPRInfo::argumentGPR0;
419         GPRReg scratch = GPRInfo::argumentGPR1;
420         bool hasMaterializedDoubleEncodeOffset = false;
421         auto materializeDoubleEncodeOffset = [&hasMaterializedDoubleEncodeOffset, &jit] (GPRReg dest) {
422             if (!hasMaterializedDoubleEncodeOffset) {
423                 static_assert(DoubleEncodeOffset == 1ll << 48, "codegen assumes this below");
424                 jit.move(JIT::TrustedImm32(1), dest);
425                 jit.lshift64(JIT::TrustedImm32(48), dest);
426                 hasMaterializedDoubleEncodeOffset = true;
427             }
428         };
429
430         unsigned marshalledGPRs = 0;
431         unsigned marshalledFPRs = 0;
432         unsigned calleeFrameOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register));
433         unsigned frOffset = CallFrame::headerSizeInRegisters * static_cast<int>(sizeof(Register));
434
435         auto marshallFPR = [&] (FPRReg fprReg) {
436             jit.purifyNaN(fprReg);
437             jit.moveDoubleTo64(fprReg, scratch);
438             materializeDoubleEncodeOffset(doubleEncodeOffsetGPRReg);
439             jit.add64(doubleEncodeOffsetGPRReg, scratch);
440             jit.store64(scratch, calleeFrame.withOffset(calleeFrameOffset));
441             calleeFrameOffset += sizeof(Register);
442             ++marshalledFPRs;
443         };
444
445         for (unsigned argNum = 0; argNum < argCount; ++argNum) {
446             Type argType = signature.argument(argNum);
447             switch (argType) {
448             case Void:
449             case Func:
450             case I64:
451                 RELEASE_ASSERT_NOT_REACHED(); // Handled above.
452             case Anyref:
453             case Anyfunc:
454             case I32:
455                 // Skipped: handled above.
456                 if (marshalledGPRs >= wasmCC.m_gprArgs.size())
457                     frOffset += sizeof(Register);
458                 ++marshalledGPRs;
459                 calleeFrameOffset += sizeof(Register);
460                 break;
461             case F32: {
462                 FPRReg fprReg;
463                 if (marshalledFPRs < wasmCC.m_fprArgs.size())
464                     fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
465                 else {
466                     // We've already spilled all arguments, these registers are available as scratch.
467                     fprReg = FPRInfo::argumentFPR0;
468                     jit.loadFloat(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
469                     frOffset += sizeof(Register);
470                 }
471                 jit.convertFloatToDouble(fprReg, fprReg);
472                 marshallFPR(fprReg);
473                 break;
474             }
475             case F64: {
476                 FPRReg fprReg;
477                 if (marshalledFPRs < wasmCC.m_fprArgs.size())
478                     fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
479                 else {
480                     // We've already spilled all arguments, these registers are available as scratch.
481                     fprReg = FPRInfo::argumentFPR0;
482                     jit.loadDouble(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
483                     frOffset += sizeof(Register);
484                 }
485                 marshallFPR(fprReg);
486                 break;
487             }
488             }
489         }
490     }
491
492     jit.loadWasmContextInstance(GPRInfo::argumentGPR0);
493     jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR0, Instance::offsetOfOwner()), GPRInfo::argumentGPR0);
494     jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR0, JSWebAssemblyInstance::offsetOfCallee()), GPRInfo::argumentGPR0);
495     jit.storePtr(GPRInfo::argumentGPR0, JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
496
497     GPRReg importJSCellGPRReg = GPRInfo::regT0; // Callee needs to be in regT0 for slow path below.
498
499     ASSERT(!wasmCC.m_calleeSaveRegisters.get(importJSCellGPRReg));
500     materializeImportJSCell(jit, importIndex, importJSCellGPRReg);
501
502     jit.store64(importJSCellGPRReg, calleeFrame.withOffset(CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
503     jit.store32(JIT::TrustedImm32(numberOfParameters), calleeFrame.withOffset(CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset));
504     jit.store64(JIT::TrustedImm64(ValueUndefined), calleeFrame.withOffset(CallFrameSlot::thisArgument * static_cast<int>(sizeof(Register))));
505
506     // FIXME Tail call if the wasm return type is void and no registers were spilled. https://bugs.webkit.org/show_bug.cgi?id=165488
507
508     CallLinkInfo* callLinkInfo = callLinkInfos.add();
509     callLinkInfo->setUpCall(CallLinkInfo::Call, CodeOrigin(), importJSCellGPRReg);
510     JIT::DataLabelPtr targetToCheck;
511     JIT::TrustedImmPtr initialRightValue(nullptr);
512     JIT::Jump slowPath = jit.branchPtrWithPatch(MacroAssembler::NotEqual, importJSCellGPRReg, targetToCheck, initialRightValue);
513     JIT::Call fastCall = jit.nearCall();
514     JIT::Jump done = jit.jump();
515     slowPath.link(&jit);
516     // Callee needs to be in regT0 here.
517     jit.move(MacroAssembler::TrustedImmPtr(callLinkInfo), GPRInfo::regT2); // Link info needs to be in regT2.
518     JIT::Call slowCall = jit.nearCall();
519     done.link(&jit);
520
521     CCallHelpers::JumpList exceptionChecks;
522
523     switch (signature.returnType()) {
524     case Void:
525         // Discard.
526         break;
527     case Func:
528         // For the JavaScript embedding, imports with these types in their signature return are a WebAssembly.Module validation error.
529         RELEASE_ASSERT_NOT_REACHED();
530         break;
531     case I64: {
532         RELEASE_ASSERT_NOT_REACHED(); // Handled above.
533     }
534     case I32: {
535         CCallHelpers::JumpList done;
536         CCallHelpers::JumpList slowPath;
537
538         int32_t (*convertToI32)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -> int32_t {
539             VM* vm = &exec->vm();
540             NativeCallFrameTracer tracer(vm, exec);
541             return v.toInt32(exec);
542         };
543
544         slowPath.append(jit.branchIfNotNumber(GPRInfo::returnValueGPR, DoNotHaveTagRegisters));
545         slowPath.append(jit.branchIfNotInt32(JSValueRegs(GPRInfo::returnValueGPR), DoNotHaveTagRegisters));
546         jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
547         done.append(jit.jump());
548
549         slowPath.link(&jit);
550         jit.setupArguments<decltype(convertToI32)>(GPRInfo::returnValueGPR);
551         auto call = jit.call(OperationPtrTag);
552         exceptionChecks.append(jit.emitJumpIfException(*vm));
553
554         jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
555             linkBuffer.link(call, FunctionPtr<OperationPtrTag>(convertToI32));
556         });
557
558         done.link(&jit);
559         break;
560     }
561     case Anyfunc:
562     case Anyref:
563         break;
564     case F32: {
565         CCallHelpers::JumpList done;
566
567         float (*convertToF32)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -> float {
568             VM* vm = &exec->vm();
569             NativeCallFrameTracer tracer(vm, exec);
570             return static_cast<float>(v.toNumber(exec));
571         };
572
573         auto notANumber = jit.branchIfNotNumber(GPRInfo::returnValueGPR, DoNotHaveTagRegisters);
574         auto isDouble = jit.branchIfNotInt32(JSValueRegs(GPRInfo::returnValueGPR), DoNotHaveTagRegisters);
575         // We're an int32
576         jit.signExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
577         jit.convertInt64ToFloat(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
578         done.append(jit.jump());
579
580         isDouble.link(&jit);
581         jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
582         jit.add64(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR);
583         jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
584         jit.convertDoubleToFloat(FPRInfo::returnValueFPR, FPRInfo::returnValueFPR);
585         done.append(jit.jump());
586
587         notANumber.link(&jit);
588         jit.setupArguments<decltype(convertToF32)>(GPRInfo::returnValueGPR);
589         auto call = jit.call(OperationPtrTag);
590         exceptionChecks.append(jit.emitJumpIfException(*vm));
591
592         jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
593             linkBuffer.link(call, FunctionPtr<OperationPtrTag>(convertToF32));
594         });
595
596         done.link(&jit);
597         break;
598     }
599     case F64: {
600         CCallHelpers::JumpList done;
601
602         double (*convertToF64)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -> double {
603             VM* vm = &exec->vm();
604             NativeCallFrameTracer tracer(vm, exec);
605             return v.toNumber(exec);
606         };
607
608         auto notANumber = jit.branchIfNotNumber(GPRInfo::returnValueGPR, DoNotHaveTagRegisters);
609         auto isDouble = jit.branchIfNotInt32(JSValueRegs(GPRInfo::returnValueGPR), DoNotHaveTagRegisters);
610         // We're an int32
611         jit.signExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
612         jit.convertInt64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
613         done.append(jit.jump());
614
615         isDouble.link(&jit);
616         jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
617         jit.add64(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR);
618         jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
619         done.append(jit.jump());
620
621         notANumber.link(&jit);
622         jit.setupArguments<decltype(convertToF64)>(GPRInfo::returnValueGPR);
623         auto call = jit.call(OperationPtrTag);
624         exceptionChecks.append(jit.emitJumpIfException(*vm));
625
626         jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
627             linkBuffer.link(call, FunctionPtr<OperationPtrTag>(convertToF64));
628         });
629
630         done.link(&jit);
631         break;
632     }
633     }
634
635     jit.emitFunctionEpilogue();
636     jit.ret();
637
638     if (!exceptionChecks.empty()) {
639         exceptionChecks.link(&jit);
640         jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm->topEntryFrame);
641         jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
642         auto call = jit.call(OperationPtrTag);
643         jit.jumpToExceptionHandler(*vm);
644
645         void (*doUnwinding)(ExecState*) = [] (ExecState* exec) -> void {
646             VM* vm = &exec->vm();
647             NativeCallFrameTracer tracer(vm, exec);
648             genericUnwind(vm, exec);
649             ASSERT(!!vm->callFrameForCatch);
650         };
651
652         jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
653             linkBuffer.link(call, FunctionPtr<OperationPtrTag>(doUnwinding));
654         });
655     }
656
657     LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID, JITCompilationCanFail);
658     if (UNLIKELY(patchBuffer.didFailToAllocate()))
659         return makeUnexpected(BindingFailure::OutOfMemory);
660
661     patchBuffer.link(slowCall, FunctionPtr<JITThunkPtrTag>(vm->getCTIStub(linkCallThunkGenerator).code()));
662     CodeLocationLabel<JSInternalPtrTag> callReturnLocation(patchBuffer.locationOfNearCall<JSInternalPtrTag>(slowCall));
663     CodeLocationLabel<JSInternalPtrTag> hotPathBegin(patchBuffer.locationOf<JSInternalPtrTag>(targetToCheck));
664     CodeLocationNearCall<JSInternalPtrTag> hotPathOther = patchBuffer.locationOfNearCall<JSInternalPtrTag>(fastCall);
665     callLinkInfo->setCallLocations(callReturnLocation, hotPathBegin, hotPathOther);
666
667     return FINALIZE_CODE(patchBuffer, WasmEntryPtrTag, "WebAssembly->JavaScript import[%i] %s", importIndex, signature.toString().ascii().data());
668 }
669
670 void* wasmToJSException(ExecState* exec, Wasm::ExceptionType type, Instance* wasmInstance)
671 {
672     wasmInstance->storeTopCallFrame(exec);
673     JSWebAssemblyInstance* instance = wasmInstance->owner<JSWebAssemblyInstance>();
674     JSGlobalObject* globalObject = instance->globalObject();
675
676     // Do not retrieve VM& from ExecState since ExecState's callee is not a JSCell.
677     VM& vm = globalObject->vm();
678
679     {
680         auto throwScope = DECLARE_THROW_SCOPE(vm);
681
682         JSObject* error;
683         if (type == ExceptionType::StackOverflow)
684             error = createStackOverflowError(exec, globalObject);
685         else
686             error = JSWebAssemblyRuntimeError::create(exec, vm, globalObject->webAssemblyRuntimeErrorStructure(), Wasm::errorMessageForExceptionType(type));
687         throwException(exec, throwScope, error);
688     }
689
690     genericUnwind(&vm, exec);
691     ASSERT(!!vm.callFrameForCatch);
692     ASSERT(!!vm.targetMachinePCForThrow);
693     // FIXME: We could make this better:
694     // This is a total hack, but the llint (both op_catch and handleUncaughtException)
695     // require a cell in the callee field to load the VM. (The baseline JIT does not require
696     // this since it is compiled with a constant VM pointer.) We could make the calling convention
697     // for exceptions first load callFrameForCatch info call frame register before jumping
698     // to the exception handler. If we did this, we could remove this terrible hack.
699     // https://bugs.webkit.org/show_bug.cgi?id=170440
700     bitwise_cast<uint64_t*>(exec)[CallFrameSlot::callee] = bitwise_cast<uint64_t>(instance->webAssemblyToJSCallee());
701     return vm.targetMachinePCForThrow;
702 }
703
704 void emitThrowWasmToJSException(CCallHelpers& jit, GPRReg wasmInstance, Wasm::ExceptionType type)
705 {
706     ASSERT(wasmInstance != GPRInfo::argumentGPR0);
707     jit.loadPtr(CCallHelpers::Address(wasmInstance, Wasm::Instance::offsetOfPointerToTopEntryFrame()), GPRInfo::argumentGPR0);
708     jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR0), GPRInfo::argumentGPR0);
709     jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(GPRInfo::argumentGPR0);
710     jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
711     jit.move(CCallHelpers::TrustedImm32(static_cast<int32_t>(type)), GPRInfo::argumentGPR1);
712
713     CCallHelpers::Call call = jit.call(OperationPtrTag);
714
715     jit.jump(GPRInfo::returnValueGPR, ExceptionHandlerPtrTag);
716     jit.breakpoint(); // We should not reach this.
717
718     jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
719         linkBuffer.link(call, FunctionPtr<OperationPtrTag>(Wasm::wasmToJSException));
720     });
721 }
722
723 } } // namespace JSC::Wasm
724
725 #endif // ENABLE(WEBASSEMBLY)