#include "config.h"
#include "WasmBinding.h"
#if ENABLE(WEBASSEMBLY)
#include "AssemblyHelpers.h"
#include "JSCJSValueInlines.h"
#include "JSWebAssemblyInstance.h"
#include "LinkBuffer.h"
#include "WasmCallingConvention.h"
namespace JSC { namespace Wasm {
WasmToJSStub importStubGenerator(VM* vm, Bag<CallLinkInfo>& callLinkInfos, Signature* signature, unsigned importIndex)
{
const WasmCallingConvention& wasmCC = wasmCallingConvention();
const JSCCallingConvention& jsCC = jscCallingConvention();
unsigned argCount = signature->arguments.size();
typedef AssemblyHelpers JIT;
JIT jit(vm, nullptr);
ASSERT(!jsCC.m_gprArgs.size());
ASSERT(!jsCC.m_fprArgs.size());
jit.emitFunctionPrologue();
jit.store64(JIT::TrustedImm32(0), JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::codeBlock * static_cast<int>(sizeof(Register)))); jit.storePtr(JIT::TrustedImmPtr(vm->webAssemblyToJSCallee.get()), JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
RegisterSet missingCalleeSaves = wasmCC.m_calleeSaveRegisters;
missingCalleeSaves.exclude(jsCC.m_calleeSaveRegisters);
ASSERT(missingCalleeSaves.isEmpty());
unsigned numberOfParameters = argCount + 1; unsigned numberOfRegsForCall = CallFrame::headerSizeInRegisters + numberOfParameters;
unsigned numberOfBytesForCall = numberOfRegsForCall * sizeof(Register) - sizeof(CallerFrameAndPC);
const unsigned stackOffset = WTF::roundUpToMultipleOf(stackAlignmentBytes(), numberOfBytesForCall);
jit.subPtr(MacroAssembler::TrustedImm32(stackOffset), MacroAssembler::stackPointerRegister);
JIT::Address calleeFrame = CCallHelpers::Address(MacroAssembler::stackPointerRegister, -static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC)));
unsigned marshalledGPRs = 0;
unsigned marshalledFPRs = 0;
unsigned calleeFrameOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register));
unsigned frOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register));
for (unsigned argNum = 0; argNum < argCount; ++argNum) {
Type argType = signature->arguments[argNum];
switch (argType) {
case Void:
case Func:
case Anyfunc:
case I64:
jit.breakpoint();
break;
case I32: {
GPRReg gprReg;
if (marshalledGPRs < wasmCC.m_gprArgs.size())
gprReg = wasmCC.m_gprArgs[marshalledGPRs].gpr();
else {
gprReg = GPRInfo::argumentGPR0;
jit.load64(JIT::Address(GPRInfo::callFrameRegister, frOffset), gprReg);
frOffset += sizeof(Register);
}
++marshalledGPRs;
jit.boxInt32(gprReg, JSValueRegs(gprReg), DoNotHaveTagRegisters);
jit.store64(gprReg, calleeFrame.withOffset(calleeFrameOffset));
calleeFrameOffset += sizeof(Register);
break;
}
case F32: {
FPRReg fprReg;
if (marshalledFPRs < wasmCC.m_fprArgs.size())
fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
else {
fprReg = FPRInfo::argumentFPR0;
jit.loadFloat(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
frOffset += sizeof(Register);
}
jit.convertFloatToDouble(fprReg, fprReg);
jit.purifyNaN(fprReg);
jit.storeDouble(fprReg, calleeFrame.withOffset(calleeFrameOffset));
calleeFrameOffset += sizeof(Register);
++marshalledFPRs;
break;
}
case F64: {
FPRReg fprReg;
if (marshalledFPRs < wasmCC.m_fprArgs.size())
fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
else {
fprReg = FPRInfo::argumentFPR0;
jit.loadDouble(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
frOffset += sizeof(Register);
}
jit.purifyNaN(fprReg);
jit.storeDouble(fprReg, calleeFrame.withOffset(calleeFrameOffset));
calleeFrameOffset += sizeof(Register);
++marshalledFPRs;
break;
}
}
}
GPRReg importJSCellGPRReg = GPRInfo::regT0; ASSERT(!wasmCC.m_calleeSaveRegisters.get(importJSCellGPRReg));
jit.loadPtr(&vm->topJSWebAssemblyInstance, importJSCellGPRReg);
jit.loadPtr(JIT::Address(importJSCellGPRReg, JSWebAssemblyInstance::offsetOfImportFunction(importIndex)), importJSCellGPRReg);
uint64_t thisArgument = ValueUndefined; jit.store64(importJSCellGPRReg, calleeFrame.withOffset(CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
jit.store32(JIT::TrustedImm32(numberOfParameters), calleeFrame.withOffset(CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset));
jit.store64(JIT::TrustedImm64(thisArgument), calleeFrame.withOffset(CallFrameSlot::thisArgument * static_cast<int>(sizeof(Register))));
CallLinkInfo* callLinkInfo = callLinkInfos.add();
callLinkInfo->setUpCall(CallLinkInfo::Call, CodeOrigin(), importJSCellGPRReg);
JIT::DataLabelPtr targetToCheck;
JIT::TrustedImmPtr initialRightValue(0);
JIT::Jump slowPath = jit.branchPtrWithPatch(MacroAssembler::NotEqual, importJSCellGPRReg, targetToCheck, initialRightValue);
JIT::Call fastCall = jit.nearCall();
JIT::Jump done = jit.jump();
slowPath.link(&jit);
jit.move(MacroAssembler::TrustedImmPtr(callLinkInfo), GPRInfo::regT2); JIT::Call slowCall = jit.nearCall();
done.link(&jit);
switch (signature->returnType) {
case Void:
break;
case Func:
case Anyfunc:
RELEASE_ASSERT_NOT_REACHED();
break;
case I32: {
jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
JIT::Jump checkJSInt32 = jit.branch64(JIT::AboveOrEqual, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
jit.truncateDoubleToInt32(FPRInfo::returnValueFPR, GPRInfo::returnValueGPR);
JIT::Jump checkJSNumber = jit.branchTest64(JIT::NonZero, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
jit.abortWithReason(AHIsNotJSNumber); checkJSInt32.link(&jit);
jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
checkJSNumber.link(&jit);
break;
}
case I64: {
jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
JIT::Jump checkJSInt32 = jit.branch64(JIT::AboveOrEqual, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
jit.truncateDoubleToInt64(FPRInfo::returnValueFPR, GPRInfo::returnValueGPR);
JIT::Jump checkJSNumber = jit.branchTest64(JIT::NonZero, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
jit.abortWithReason(AHIsNotJSNumber); checkJSInt32.link(&jit);
jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
checkJSNumber.link(&jit);
break;
}
case F32: {
jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
jit.convertDoubleToFloat(FPRInfo::returnValueFPR, FPRInfo::returnValueFPR);
JIT::Jump checkJSInt32 = jit.branch64(JIT::AboveOrEqual, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
JIT::Jump checkJSNumber = jit.branchTest64(JIT::NonZero, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
jit.abortWithReason(AHIsNotJSNumber); checkJSInt32.link(&jit);
jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
jit.convertInt64ToFloat(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
checkJSNumber.link(&jit);
break;
}
case F64: {
jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2);
jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
JIT::Jump checkJSInt32 = jit.branch64(JIT::AboveOrEqual, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
JIT::Jump checkJSNumber = jit.branchTest64(JIT::NonZero, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
jit.abortWithReason(AHIsNotJSNumber); checkJSInt32.link(&jit);
jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
jit.convertInt64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR);
checkJSNumber.link(&jit);
break;
}
}
jit.emitFunctionEpilogue();
jit.ret();
LinkBuffer patchBuffer(*vm, jit, GLOBAL_THUNK_ID);
patchBuffer.link(slowCall, FunctionPtr(vm->getCTIStub(linkCallThunkGenerator).code().executableAddress()));
CodeLocationLabel callReturnLocation(patchBuffer.locationOfNearCall(slowCall));
CodeLocationLabel hotPathBegin(patchBuffer.locationOf(targetToCheck));
CodeLocationNearCall hotPathOther = patchBuffer.locationOfNearCall(fastCall);
callLinkInfo->setCallLocations(callReturnLocation, hotPathBegin, hotPathOther);
return FINALIZE_CODE(patchBuffer, ("WebAssembly import[%i] stub for signature %p", importIndex, signature));
}
} }
#endif // ENABLE(WEBASSEMBLY)