WebAssembly: perform stack checks
[WebKit-https.git] / Source / JavaScriptCore / wasm / WasmBinding.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 "WasmBinding.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 "JSWebAssemblyInstance.h"
36 #include "LinkBuffer.h"
37 #include "NativeErrorConstructor.h"
38 #include "WasmCallingConvention.h"
39 #include "WasmContext.h"
40 #include "WasmExceptionType.h"
41
42 namespace JSC { namespace Wasm {
43
44 using JIT = CCallHelpers;
45
46 static void materializeImportJSCell(JIT& jit, unsigned importIndex, GPRReg result)
47 {
48     // We're calling out of the current WebAssembly.Instance. That Instance has a list of all its import functions.
49     jit.loadWasmContext(result);
50     jit.loadPtr(JIT::Address(result, JSWebAssemblyInstance::offsetOfImportFunction(importIndex)), result);
51 }
52
53 MacroAssemblerCodeRef wasmToJs(VM* vm, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex signatureIndex, unsigned importIndex)
54 {
55     // FIXME: This function doesn't properly abstract away the calling convention.
56     // It'd be super easy to do so: https://bugs.webkit.org/show_bug.cgi?id=169401
57     const WasmCallingConvention& wasmCC = wasmCallingConvention();
58     const JSCCallingConvention& jsCC = jscCallingConvention();
59     const Signature& signature = SignatureInformation::get(signatureIndex);
60     unsigned argCount = signature.argumentCount();
61     JIT jit;
62
63     // Below, we assume that the JS calling convention is always on the stack.
64     ASSERT(!jsCC.m_gprArgs.size());
65     ASSERT(!jsCC.m_fprArgs.size());
66
67     jit.emitFunctionPrologue();
68     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
69
70     {
71         bool hasBadI64Use = false;
72         hasBadI64Use |= signature.returnType() == I64;
73         for (unsigned argNum = 0; argNum < argCount && !hasBadI64Use; ++argNum) {
74             Type argType = signature.argument(argNum);
75             switch (argType) {
76             case Void:
77             case Func:
78             case Anyfunc:
79                 RELEASE_ASSERT_NOT_REACHED();
80
81             case I64: {
82                 hasBadI64Use = true;
83                 break;
84             }
85
86             default:
87                 break;
88             }
89         }
90
91         if (hasBadI64Use) {
92             jit.copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(*vm);
93             jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
94             jit.loadWasmContext(GPRInfo::argumentGPR1);
95
96             // Store Callee.
97             jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR1, JSWebAssemblyInstance::offsetOfCallee()), GPRInfo::argumentGPR2);
98             jit.storePtr(GPRInfo::argumentGPR2, JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
99
100             auto call = jit.call();
101             jit.jumpToExceptionHandler(*vm);
102
103             void (*throwBadI64)(ExecState*, JSWebAssemblyInstance*) = [] (ExecState* exec, JSWebAssemblyInstance* wasmContext) -> void {
104                 VM* vm = &exec->vm();
105                 NativeCallFrameTracer tracer(vm, exec);
106
107                 {
108                     auto throwScope = DECLARE_THROW_SCOPE(*vm);
109                     JSGlobalObject* globalObject = wasmContext->globalObject();
110                     auto* error = ErrorInstance::create(exec, *vm, globalObject->typeErrorConstructor()->errorStructure(), ASCIILiteral("i64 not allowed as return type or argument to an imported function"));
111                     throwException(exec, throwScope, error);
112                 }
113
114                 genericUnwind(vm, exec);
115                 ASSERT(!!vm->callFrameForCatch);
116             };
117
118             LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
119             linkBuffer.link(call, throwBadI64);
120             return FINALIZE_CODE(linkBuffer, ("WebAssembly->JavaScript invalid i64 use in import[%i]", importIndex));
121         }
122     }
123
124     // 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.
125     RegisterSet missingCalleeSaves = wasmCC.m_calleeSaveRegisters;
126     missingCalleeSaves.exclude(jsCC.m_calleeSaveRegisters);
127     ASSERT(missingCalleeSaves.isEmpty());
128
129     if (!Options::useCallICsForWebAssemblyToJSCalls()) {
130         ScratchBuffer* scratchBuffer = vm->scratchBufferForSize(argCount * sizeof(uint64_t));
131         char* buffer = argCount ? static_cast<char*>(scratchBuffer->dataBuffer()) : nullptr;
132         unsigned marshalledGPRs = 0;
133         unsigned marshalledFPRs = 0;
134         unsigned bufferOffset = 0;
135         unsigned frOffset = CallFrame::headerSizeInRegisters * static_cast<int>(sizeof(Register));
136         const GPRReg scratchGPR = GPRInfo::regCS0;
137         jit.subPtr(MacroAssembler::TrustedImm32(WTF::roundUpToMultipleOf(stackAlignmentBytes(), sizeof(Register))), MacroAssembler::stackPointerRegister);
138         jit.storePtr(scratchGPR, MacroAssembler::Address(MacroAssembler::stackPointerRegister));
139
140         for (unsigned argNum = 0; argNum < argCount; ++argNum) {
141             Type argType = signature.argument(argNum);
142             switch (argType) {
143             case Void:
144             case Func:
145             case Anyfunc:
146             case I64:
147                 RELEASE_ASSERT_NOT_REACHED();
148             case I32: {
149                 GPRReg gprReg;
150                 if (marshalledGPRs < wasmCC.m_gprArgs.size())
151                     gprReg = wasmCC.m_gprArgs[marshalledGPRs].gpr();
152                 else {
153                     // We've already spilled all arguments, these registers are available as scratch.
154                     gprReg = GPRInfo::argumentGPR0;
155                     jit.load64(JIT::Address(GPRInfo::callFrameRegister, frOffset), gprReg);
156                     frOffset += sizeof(Register);
157                 }
158                 jit.zeroExtend32ToPtr(gprReg, gprReg);
159                 jit.store64(gprReg, buffer + bufferOffset);
160                 ++marshalledGPRs;
161                 break;
162             }
163             case F32: {
164                 FPRReg fprReg;
165                 if (marshalledFPRs < wasmCC.m_fprArgs.size())
166                     fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
167                 else {
168                     // We've already spilled all arguments, these registers are available as scratch.
169                     fprReg = FPRInfo::argumentFPR0;
170                     jit.loadFloat(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
171                     frOffset += sizeof(Register);
172                 }
173                 jit.convertFloatToDouble(fprReg, fprReg);
174                 jit.moveDoubleTo64(fprReg, scratchGPR);
175                 jit.store64(scratchGPR, buffer + bufferOffset);
176                 ++marshalledFPRs;
177                 break;
178             }
179             case F64: {
180                 FPRReg fprReg;
181                 if (marshalledFPRs < wasmCC.m_fprArgs.size())
182                     fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
183                 else {
184                     // We've already spilled all arguments, these registers are available as scratch.
185                     fprReg = FPRInfo::argumentFPR0;
186                     jit.loadDouble(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
187                     frOffset += sizeof(Register);
188                 }
189                 jit.moveDoubleTo64(fprReg, scratchGPR);
190                 jit.store64(scratchGPR, buffer + bufferOffset);
191                 ++marshalledFPRs;
192                 break;
193             }
194             }
195
196             bufferOffset += sizeof(Register);
197         }
198         jit.loadPtr(MacroAssembler::Address(MacroAssembler::stackPointerRegister), scratchGPR);
199         if (argCount) {
200             // The GC should not look at this buffer at all, these aren't JSValues.
201             jit.move(CCallHelpers::TrustedImmPtr(scratchBuffer->activeLengthPtr()), GPRInfo::argumentGPR0);
202             jit.storePtr(CCallHelpers::TrustedImmPtr(0), GPRInfo::argumentGPR0);
203         }
204
205         uint64_t (*callFunc)(ExecState*, JSObject*, SignatureIndex, uint64_t*) =
206             [] (ExecState* exec, JSObject* callee, SignatureIndex signatureIndex, uint64_t* buffer) -> uint64_t { 
207                 VM* vm = &exec->vm();
208                 NativeCallFrameTracer tracer(vm, exec);
209                 auto throwScope = DECLARE_THROW_SCOPE(*vm);
210                 const Signature& signature = SignatureInformation::get(signatureIndex);
211                 MarkedArgumentBuffer args;
212                 for (unsigned argNum = 0; argNum < signature.argumentCount(); ++argNum) {
213                     Type argType = signature.argument(argNum);
214                     JSValue arg;
215                     switch (argType) {
216                     case Void:
217                     case Func:
218                     case Anyfunc:
219                     case I64:
220                         RELEASE_ASSERT_NOT_REACHED();
221                     case I32:
222                         arg = jsNumber(static_cast<int32_t>(buffer[argNum]));
223                         break;
224                     case F32:
225                     case F64:
226                         arg = jsNumber(bitwise_cast<double>(buffer[argNum]));
227                         break;
228                     }
229                     args.append(arg);
230                 }
231
232                 CallData callData;
233                 CallType callType = callee->methodTable(*vm)->getCallData(callee, callData);
234                 RELEASE_ASSERT(callType != CallType::None);
235                 JSValue result = call(exec, callee, callType, callData, jsUndefined(), args);
236                 RETURN_IF_EXCEPTION(throwScope, 0);
237
238                 uint64_t realResult;
239                 switch (signature.returnType()) {
240                 case Func:
241                 case Anyfunc:
242                 case I64:
243                     RELEASE_ASSERT_NOT_REACHED();
244                     break;
245                 case Void:
246                     break;
247                 case I32: {
248                     realResult = static_cast<uint64_t>(static_cast<uint32_t>(result.toInt32(exec)));
249                     break;
250                 }
251                 case F64:
252                 case F32: {
253                     realResult = bitwise_cast<uint64_t>(result.toNumber(exec));
254                     break;
255                 }
256                 }
257
258                 RETURN_IF_EXCEPTION(throwScope, 0);
259                 return realResult;
260             };
261         
262         jit.loadWasmContext(GPRInfo::argumentGPR0);
263         jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR0, JSWebAssemblyInstance::offsetOfCallee()), GPRInfo::argumentGPR0);
264         jit.storePtr(GPRInfo::argumentGPR0, JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
265         
266         materializeImportJSCell(jit, importIndex, GPRInfo::argumentGPR1);
267         static_assert(GPRInfo::numberOfArgumentRegisters >= 4, "We rely on this with the call below.");
268         jit.setupArgumentsWithExecState(GPRInfo::argumentGPR1, CCallHelpers::TrustedImm32(signatureIndex), CCallHelpers::TrustedImmPtr(buffer));
269         auto call = jit.call();
270         auto noException = jit.emitExceptionCheck(*vm, AssemblyHelpers::InvertedExceptionCheck);
271
272         // exception here.
273         jit.copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(*vm);
274         jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
275         void (*doUnwinding)(ExecState*) = [] (ExecState* exec) -> void {
276             VM* vm = &exec->vm();
277             NativeCallFrameTracer tracer(vm, exec);
278             genericUnwind(vm, exec);
279             ASSERT(!!vm->callFrameForCatch);
280         };
281         auto exceptionCall = jit.call();
282         jit.jumpToExceptionHandler(*vm);
283
284         noException.link(&jit);
285         switch (signature.returnType()) {
286         case F64: {
287             jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
288             break;
289         }
290         case F32: {
291             jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
292             jit.convertDoubleToFloat(FPRInfo::returnValueFPR, FPRInfo::returnValueFPR);
293             break;
294         }
295         default:
296             break;
297         }
298
299         jit.emitFunctionEpilogue();
300         jit.ret();
301
302         LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
303         linkBuffer.link(call, callFunc);
304         linkBuffer.link(exceptionCall, doUnwinding);
305
306         return FINALIZE_CODE(linkBuffer, ("WebAssembly->JavaScript import[%i] %s", importIndex, signature.toString().ascii().data()));
307     }
308
309     // FIXME perform a stack check before updating SP. https://bugs.webkit.org/show_bug.cgi?id=165546
310
311     const unsigned numberOfParameters = argCount + 1; // There is a "this" argument.
312     const unsigned numberOfRegsForCall = CallFrame::headerSizeInRegisters + numberOfParameters;
313     const unsigned numberOfBytesForCall = numberOfRegsForCall * sizeof(Register) - sizeof(CallerFrameAndPC);
314     const unsigned stackOffset = WTF::roundUpToMultipleOf(stackAlignmentBytes(), numberOfBytesForCall);
315     jit.subPtr(MacroAssembler::TrustedImm32(stackOffset), MacroAssembler::stackPointerRegister);
316     JIT::Address calleeFrame = CCallHelpers::Address(MacroAssembler::stackPointerRegister, -static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC)));
317
318     // 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
319     
320     // First go through the integer parameters, freeing up their register for use afterwards.
321     {
322         unsigned marshalledGPRs = 0;
323         unsigned marshalledFPRs = 0;
324         unsigned calleeFrameOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register));
325         unsigned frOffset = CallFrame::headerSizeInRegisters * static_cast<int>(sizeof(Register));
326         for (unsigned argNum = 0; argNum < argCount; ++argNum) {
327             Type argType = signature.argument(argNum);
328             switch (argType) {
329             case Void:
330             case Func:
331             case Anyfunc:
332             case I64:
333                 RELEASE_ASSERT_NOT_REACHED(); // Handled above.
334             case I32: {
335                 GPRReg gprReg;
336                 if (marshalledGPRs < wasmCC.m_gprArgs.size())
337                     gprReg = wasmCC.m_gprArgs[marshalledGPRs].gpr();
338                 else {
339                     // We've already spilled all arguments, these registers are available as scratch.
340                     gprReg = GPRInfo::argumentGPR0;
341                     jit.load64(JIT::Address(GPRInfo::callFrameRegister, frOffset), gprReg);
342                     frOffset += sizeof(Register);
343                 }
344                 ++marshalledGPRs;
345                 jit.zeroExtend32ToPtr(gprReg, gprReg); // Clear non-int32 and non-tag bits.
346                 jit.boxInt32(gprReg, JSValueRegs(gprReg), DoNotHaveTagRegisters);
347                 jit.store64(gprReg, calleeFrame.withOffset(calleeFrameOffset));
348                 calleeFrameOffset += sizeof(Register);
349                 break;
350             }
351             case F32:
352             case F64:
353                 // Skipped: handled below.
354                 if (marshalledFPRs >= wasmCC.m_fprArgs.size())
355                     frOffset += sizeof(Register);
356                 ++marshalledFPRs;
357                 calleeFrameOffset += sizeof(Register);
358                 break;
359             }
360         }
361     }
362     
363     {
364         // Integer registers have already been spilled, these are now available.
365         GPRReg doubleEncodeOffsetGPRReg = GPRInfo::argumentGPR0;
366         GPRReg scratch = GPRInfo::argumentGPR1;
367         bool hasMaterializedDoubleEncodeOffset = false;
368         auto materializeDoubleEncodeOffset = [&hasMaterializedDoubleEncodeOffset, &jit] (GPRReg dest) {
369             if (!hasMaterializedDoubleEncodeOffset) {
370                 static_assert(DoubleEncodeOffset == 1ll << 48, "codegen assumes this below");
371                 jit.move(JIT::TrustedImm32(1), dest);
372                 jit.lshift64(JIT::TrustedImm32(48), dest);
373                 hasMaterializedDoubleEncodeOffset = true;
374             }
375         };
376
377         unsigned marshalledGPRs = 0;
378         unsigned marshalledFPRs = 0;
379         unsigned calleeFrameOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register));
380         unsigned frOffset = CallFrame::headerSizeInRegisters * static_cast<int>(sizeof(Register));
381         for (unsigned argNum = 0; argNum < argCount; ++argNum) {
382             Type argType = signature.argument(argNum);
383             switch (argType) {
384             case Void:
385             case Func:
386             case Anyfunc:
387             case I64:
388                 RELEASE_ASSERT_NOT_REACHED(); // Handled above.
389             case I32:
390                 // Skipped: handled above.
391                 if (marshalledGPRs >= wasmCC.m_gprArgs.size())
392                     frOffset += sizeof(Register);
393                 ++marshalledGPRs;
394                 calleeFrameOffset += sizeof(Register);
395                 break;
396             case F32: {
397                 FPRReg fprReg;
398                 if (marshalledFPRs < wasmCC.m_fprArgs.size())
399                     fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
400                 else {
401                     // We've already spilled all arguments, these registers are available as scratch.
402                     fprReg = FPRInfo::argumentFPR0;
403                     jit.loadFloat(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
404                     frOffset += sizeof(Register);
405                 }
406                 jit.convertFloatToDouble(fprReg, fprReg);
407                 jit.purifyNaN(fprReg);
408                 jit.moveDoubleTo64(fprReg, scratch);
409                 materializeDoubleEncodeOffset(doubleEncodeOffsetGPRReg);
410                 jit.add64(doubleEncodeOffsetGPRReg, scratch);
411                 jit.store64(scratch, calleeFrame.withOffset(calleeFrameOffset));
412                 calleeFrameOffset += sizeof(Register);
413                 ++marshalledFPRs;
414                 break;
415             }
416             case F64: {
417                 FPRReg fprReg;
418                 if (marshalledFPRs < wasmCC.m_fprArgs.size())
419                     fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
420                 else {
421                     // We've already spilled all arguments, these registers are available as scratch.
422                     fprReg = FPRInfo::argumentFPR0;
423                     jit.loadDouble(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
424                     frOffset += sizeof(Register);
425                 }
426                 jit.purifyNaN(fprReg);
427                 jit.moveDoubleTo64(fprReg, scratch);
428                 materializeDoubleEncodeOffset(doubleEncodeOffsetGPRReg);
429                 jit.add64(doubleEncodeOffsetGPRReg, scratch);
430                 jit.store64(scratch, calleeFrame.withOffset(calleeFrameOffset));
431                 calleeFrameOffset += sizeof(Register);
432                 ++marshalledFPRs;
433                 break;
434             }
435             }
436         }
437     }
438
439     jit.loadWasmContext(GPRInfo::argumentGPR0);
440     jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR0, JSWebAssemblyInstance::offsetOfCallee()), GPRInfo::argumentGPR0);
441     jit.storePtr(GPRInfo::argumentGPR0, JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
442
443     GPRReg importJSCellGPRReg = GPRInfo::regT0; // Callee needs to be in regT0 for slow path below.
444     ASSERT(!wasmCC.m_calleeSaveRegisters.get(importJSCellGPRReg));
445
446     materializeImportJSCell(jit, importIndex, importJSCellGPRReg);
447
448     jit.store64(importJSCellGPRReg, calleeFrame.withOffset(CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
449     jit.store32(JIT::TrustedImm32(numberOfParameters), calleeFrame.withOffset(CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset));
450     jit.store64(JIT::TrustedImm64(ValueUndefined), calleeFrame.withOffset(CallFrameSlot::thisArgument * static_cast<int>(sizeof(Register))));
451
452     // FIXME Tail call if the wasm return type is void and no registers were spilled. https://bugs.webkit.org/show_bug.cgi?id=165488
453
454     CallLinkInfo* callLinkInfo = callLinkInfos.add();
455     callLinkInfo->setUpCall(CallLinkInfo::Call, CodeOrigin(), importJSCellGPRReg);
456     JIT::DataLabelPtr targetToCheck;
457     JIT::TrustedImmPtr initialRightValue(0);
458     JIT::Jump slowPath = jit.branchPtrWithPatch(MacroAssembler::NotEqual, importJSCellGPRReg, targetToCheck, initialRightValue);
459     JIT::Call fastCall = jit.nearCall();
460     JIT::Jump done = jit.jump();
461     slowPath.link(&jit);
462     // Callee needs to be in regT0 here.
463     jit.move(MacroAssembler::TrustedImmPtr(callLinkInfo), GPRInfo::regT2); // Link info needs to be in regT2.
464     JIT::Call slowCall = jit.nearCall();
465     done.link(&jit);
466
467     CCallHelpers::JumpList exceptionChecks;
468
469     switch (signature.returnType()) {
470     case Void:
471         // Discard.
472         break;
473     case Func:
474     case Anyfunc:
475         // For the JavaScript embedding, imports with these types in their signature return are a WebAssembly.Module validation error.
476         RELEASE_ASSERT_NOT_REACHED();
477         break;
478     case I64: {
479         RELEASE_ASSERT_NOT_REACHED(); // Handled above.
480     }
481     case I32: {
482         CCallHelpers::JumpList done;
483         CCallHelpers::JumpList slowPath;
484
485         slowPath.append(jit.branchIfNotNumber(GPRInfo::returnValueGPR, DoNotHaveTagRegisters));
486         slowPath.append(jit.branchIfNotInt32(JSValueRegs(GPRInfo::returnValueGPR), DoNotHaveTagRegisters));
487         jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
488         done.append(jit.jump());
489
490         slowPath.link(&jit);
491         jit.setupArgumentsWithExecState(GPRInfo::returnValueGPR);
492         auto call = jit.call();
493         exceptionChecks.append(jit.emitJumpIfException(*vm));
494
495         int32_t (*convertToI32)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -> int32_t { 
496             VM* vm = &exec->vm();
497             NativeCallFrameTracer tracer(vm, exec);
498             return v.toInt32(exec);
499         };
500         jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
501             linkBuffer.link(call, convertToI32);
502         });
503
504         done.link(&jit);
505         break;
506     }
507     case F32: {
508         CCallHelpers::JumpList done;
509         auto notANumber = jit.branchIfNotNumber(GPRInfo::returnValueGPR, DoNotHaveTagRegisters);
510         auto isDouble = jit.branchIfNotInt32(JSValueRegs(GPRInfo::returnValueGPR), DoNotHaveTagRegisters);
511         // We're an int32
512         jit.signExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
513         jit.convertInt64ToFloat(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
514         done.append(jit.jump());
515
516         isDouble.link(&jit);
517         jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
518         jit.add64(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR);
519         jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
520         jit.convertDoubleToFloat(FPRInfo::returnValueFPR, FPRInfo::returnValueFPR);
521         done.append(jit.jump());
522
523         notANumber.link(&jit);
524         jit.setupArgumentsWithExecState(GPRInfo::returnValueGPR);
525         auto call = jit.call();
526         exceptionChecks.append(jit.emitJumpIfException(*vm));
527
528         float (*convertToF32)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -> float { 
529             VM* vm = &exec->vm();
530             NativeCallFrameTracer tracer(vm, exec);
531             return static_cast<float>(v.toNumber(exec));
532         };
533         jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
534             linkBuffer.link(call, convertToF32);
535         });
536
537         done.link(&jit);
538         break;
539     }
540     case F64: {
541         CCallHelpers::JumpList done;
542         auto notANumber = jit.branchIfNotNumber(GPRInfo::returnValueGPR, DoNotHaveTagRegisters);
543         auto isDouble = jit.branchIfNotInt32(JSValueRegs(GPRInfo::returnValueGPR), DoNotHaveTagRegisters);
544         // We're an int32
545         jit.signExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
546         jit.convertInt64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
547         done.append(jit.jump());
548
549         isDouble.link(&jit);
550         jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
551         jit.add64(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR);
552         jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
553         done.append(jit.jump());
554
555         notANumber.link(&jit);
556         jit.setupArgumentsWithExecState(GPRInfo::returnValueGPR);
557         auto call = jit.call();
558         exceptionChecks.append(jit.emitJumpIfException(*vm));
559
560         double (*convertToF64)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -> double { 
561             VM* vm = &exec->vm();
562             NativeCallFrameTracer tracer(vm, exec);
563             return v.toNumber(exec);
564         };
565         jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
566             linkBuffer.link(call, convertToF64);
567         });
568
569         done.link(&jit);
570         break;
571     }
572     }
573
574     jit.emitFunctionEpilogue();
575     jit.ret();
576
577     if (!exceptionChecks.empty()) {
578         exceptionChecks.link(&jit);
579         jit.copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(*vm);
580         jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
581         auto call = jit.call();
582         jit.jumpToExceptionHandler(*vm);
583
584         void (*doUnwinding)(ExecState*) = [] (ExecState* exec) -> void {
585             VM* vm = &exec->vm();
586             NativeCallFrameTracer tracer(vm, exec);
587             genericUnwind(vm, exec);
588             ASSERT(!!vm->callFrameForCatch);
589         };
590
591         jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
592             linkBuffer.link(call, doUnwinding);
593         });
594     }
595
596     LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID);
597     patchBuffer.link(slowCall, FunctionPtr(vm->getCTIStub(linkCallThunkGenerator).code().executableAddress()));
598     CodeLocationLabel callReturnLocation(patchBuffer.locationOfNearCall(slowCall));
599     CodeLocationLabel hotPathBegin(patchBuffer.locationOf(targetToCheck));
600     CodeLocationNearCall hotPathOther = patchBuffer.locationOfNearCall(fastCall);
601     callLinkInfo->setCallLocations(callReturnLocation, hotPathBegin, hotPathOther);
602
603     return FINALIZE_CODE(patchBuffer, ("WebAssembly->JavaScript import[%i] %s", importIndex, signature.toString().ascii().data()));
604 }
605
606 MacroAssemblerCodeRef wasmToWasm(unsigned importIndex)
607 {
608     const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
609     JIT jit;
610
611     GPRReg scratch = GPRInfo::nonPreservedNonArgumentGPR;
612     GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
613     ASSERT(baseMemory != scratch);
614     const auto& sizeRegs = pinnedRegs.sizeRegisters;
615     ASSERT(sizeRegs.size() >= 1);
616     ASSERT(sizeRegs[0].sizeRegister != baseMemory);
617     ASSERT(sizeRegs[0].sizeRegister != scratch);
618     GPRReg sizeRegAsScratch = sizeRegs[0].sizeRegister;
619
620     static_assert(std::is_same<Context, JSWebAssemblyInstance>::value, "This is assumed in the code below.");
621     // B3's call codegen ensures that the JSCell is a WebAssemblyFunction.
622     jit.loadWasmContext(sizeRegAsScratch); // Old Instance*
623     jit.loadPtr(JIT::Address(sizeRegAsScratch, JSWebAssemblyInstance::offsetOfImportFunction(importIndex)), scratch);
624
625     // Get the callee's WebAssembly.Instance and set it as WasmContext. The caller will take care of restoring its own Instance.
626     jit.loadPtr(JIT::Address(scratch, WebAssemblyFunction::offsetOfInstance()), baseMemory); // Instance*.
627     jit.storeWasmContext(baseMemory);
628
629     jit.loadPtr(JIT::Address(sizeRegAsScratch, JSWebAssemblyInstance::offsetOfCachedStackLimit()), sizeRegAsScratch);
630     jit.storePtr(sizeRegAsScratch, JIT::Address(baseMemory, JSWebAssemblyInstance::offsetOfCachedStackLimit()));
631
632     // FIXME the following code assumes that all WebAssembly.Instance have the same pinned registers. https://bugs.webkit.org/show_bug.cgi?id=162952
633     // Set up the callee's baseMemory register as well as the memory size registers.
634     jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyInstance::offsetOfMemory()), baseMemory); // JSWebAssemblyMemory*.
635     ASSERT(!sizeRegs[0].sizeOffset); // The following code assumes we start at 0, and calculates subsequent size registers relative to 0.
636     jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyMemory::offsetOfSize()), sizeRegs[0].sizeRegister); // Memory size.
637     jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyMemory::offsetOfMemory()), baseMemory); // WasmMemory::void*.
638     for (unsigned i = 1; i < sizeRegs.size(); ++i) {
639         ASSERT(sizeRegs[i].sizeRegister != baseMemory);
640         ASSERT(sizeRegs[i].sizeRegister != scratch);
641         jit.add64(JIT::TrustedImm32(-sizeRegs[i].sizeOffset), sizeRegs[0].sizeRegister, sizeRegs[i].sizeRegister);
642     }
643
644     // Tail call into the callee WebAssembly function.
645     jit.loadPtr(JIT::Address(scratch, WebAssemblyFunction::offsetOfWasmEntrypointLoadLocation()), scratch);
646     jit.loadPtr(scratch, scratch);
647     jit.jump(scratch);
648
649     LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID);
650     return FINALIZE_CODE(patchBuffer, ("WebAssembly->WebAssembly import[%i]", importIndex));
651 }
652
653 } } // namespace JSC::Wasm
654
655 #endif // ENABLE(WEBASSEMBLY)