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