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
47namespace JSC { namespace Wasm {
48
49using JIT = CCallHelpers;
50
51static 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
58static 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
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 I64:
166 RELEASE_ASSERT_NOT_REACHED();
167 case Anyref:
168 case Funcref:
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 Funcref: {
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 Funcref: {
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 Funcref:
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 Funcref:
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 Funcref:
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
670void* 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
704void 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)
726