#include "config.h"
#include "JSToWasm.h"
#if ENABLE(WEBASSEMBLY)
#include "CCallHelpers.h"
#include "JSWebAssemblyInstance.h"
#include "JSWebAssemblyRuntimeError.h"
#include "MaxFrameExtentForSlowPathCall.h"
#include "WasmCallingConvention.h"
#include "WasmContextInlines.h"
#include "WasmSignatureInlines.h"
#include "WasmToJS.h"
namespace JSC { namespace Wasm {
std::unique_ptr<InternalFunction> createJSToWasmWrapper(CompilationContext& compilationContext, const Signature& signature, Vector<UnlinkedWasmToWasmCall>* unlinkedWasmToWasmCalls, const ModuleInformation& info, MemoryMode mode, unsigned functionIndex)
{
CCallHelpers& jit = *compilationContext.embedderEntrypointJIT;
auto result = std::make_unique<InternalFunction>();
jit.emitFunctionPrologue();
jit.store64(CCallHelpers::TrustedImm64(0), CCallHelpers::Address(GPRInfo::callFrameRegister, CallFrameSlot::codeBlock * static_cast<int>(sizeof(Register))));
MacroAssembler::DataLabelPtr calleeMoveLocation = jit.moveWithPatch(MacroAssembler::TrustedImmPtr(nullptr), GPRInfo::nonPreservedNonReturnGPR);
jit.storePtr(GPRInfo::nonPreservedNonReturnGPR, CCallHelpers::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
CodeLocationDataLabelPtr<WasmEntryPtrTag>* linkedCalleeMove = &result->calleeMoveLocation;
jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
*linkedCalleeMove = linkBuffer.locationOf<WasmEntryPtrTag>(calleeMoveLocation);
});
const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
RegisterSet toSave = pinnedRegs.toSave(mode);
#if !ASSERT_DISABLED
unsigned toSaveSize = toSave.numberOfSetGPRs();
toSave.filter(RegisterSet::calleeSaveRegisters());
ASSERT(toSave.numberOfSetGPRs() == toSaveSize);
#endif
RegisterAtOffsetList registersToSpill(toSave, RegisterAtOffsetList::OffsetBaseType::FramePointerBased);
result->entrypoint.calleeSaveRegisters = registersToSpill;
unsigned totalFrameSize = registersToSpill.size() * sizeof(void*);
totalFrameSize += WasmCallingConvention::headerSizeInBytes();
totalFrameSize -= sizeof(CallerFrameAndPC);
unsigned numGPRs = 0;
unsigned numFPRs = 0;
bool argumentsIncludeI64 = false;
for (unsigned i = 0; i < signature.argumentCount(); i++) {
switch (signature.argument(i)) {
case Wasm::I64:
argumentsIncludeI64 = true;
FALLTHROUGH;
case Wasm::I32:
if (numGPRs >= wasmCallingConvention().m_gprArgs.size())
totalFrameSize += sizeof(void*);
++numGPRs;
break;
case Wasm::F32:
case Wasm::F64:
if (numFPRs >= wasmCallingConvention().m_fprArgs.size())
totalFrameSize += sizeof(void*);
++numFPRs;
break;
default:
RELEASE_ASSERT_NOT_REACHED();
}
}
totalFrameSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), totalFrameSize);
jit.subPtr(MacroAssembler::TrustedImm32(totalFrameSize), MacroAssembler::stackPointerRegister);
for (const RegisterAtOffset& regAtOffset : registersToSpill) {
GPRReg reg = regAtOffset.reg().gpr();
ptrdiff_t offset = regAtOffset.offset();
jit.storePtr(reg, CCallHelpers::Address(GPRInfo::callFrameRegister, offset));
}
if (argumentsIncludeI64 || signature.returnType() == Wasm::I64) {
if (Context::useFastTLS())
jit.loadWasmContextInstance(GPRInfo::argumentGPR2);
else {
jit.loadPtr(CCallHelpers::Address(GPRInfo::callFrameRegister, CallFrameSlot::thisArgument * sizeof(EncodedJSValue)), GPRInfo::argumentGPR2);
jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR2, JSWebAssemblyInstance::offsetOfPoisonedInstance()), GPRInfo::argumentGPR2);
jit.move(CCallHelpers::TrustedImm64(JSWebAssemblyInstancePoison::key()), GPRInfo::argumentGPR0);
jit.xor64(GPRInfo::argumentGPR0, GPRInfo::argumentGPR2);
}
jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR2, Instance::offsetOfPointerToTopEntryFrame()), GPRInfo::argumentGPR0);
jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR0), GPRInfo::argumentGPR0);
jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(GPRInfo::argumentGPR0);
jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
jit.move(CCallHelpers::TrustedImm32(static_cast<int32_t>(argumentsIncludeI64 ? ExceptionType::I64ArgumentType : ExceptionType::I64ReturnType)), GPRInfo::argumentGPR1);
CCallHelpers::Call call = jit.call(OperationPtrTag);
jit.jump(GPRInfo::returnValueGPR, ExceptionHandlerPtrTag);
jit.breakpoint();
jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
linkBuffer.link(call, FunctionPtr<OperationPtrTag>(wasmToJSException));
});
return result;
}
GPRReg wasmContextInstanceGPR = pinnedRegs.wasmContextInstancePointer;
{
CCallHelpers::Address calleeFrame = CCallHelpers::Address(MacroAssembler::stackPointerRegister, -static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC)));
numGPRs = 0;
numFPRs = 0;
GPRReg scratchReg = pinnedRegs.baseMemoryPointer;
ptrdiff_t jsOffset = CallFrameSlot::thisArgument * sizeof(EncodedJSValue);
if (!Context::useFastTLS()) {
jit.loadPtr(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmContextInstanceGPR);
jit.loadPtr(CCallHelpers::Address(wasmContextInstanceGPR, JSWebAssemblyInstance::offsetOfPoisonedInstance()), wasmContextInstanceGPR);
jit.move(CCallHelpers::TrustedImm64(JSWebAssemblyInstancePoison::key()), scratchReg);
jit.xor64(scratchReg, wasmContextInstanceGPR);
jsOffset += sizeof(EncodedJSValue);
}
ptrdiff_t wasmOffset = CallFrame::headerSizeInRegisters * sizeof(void*);
for (unsigned i = 0; i < signature.argumentCount(); i++) {
switch (signature.argument(i)) {
case Wasm::I32:
case Wasm::I64:
if (numGPRs >= wasmCallingConvention().m_gprArgs.size()) {
if (signature.argument(i) == Wasm::I32) {
jit.load32(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchReg);
jit.store32(scratchReg, calleeFrame.withOffset(wasmOffset));
} else {
jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchReg);
jit.store64(scratchReg, calleeFrame.withOffset(wasmOffset));
}
wasmOffset += sizeof(void*);
} else {
if (signature.argument(i) == Wasm::I32)
jit.load32(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmCallingConvention().m_gprArgs[numGPRs].gpr());
else
jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmCallingConvention().m_gprArgs[numGPRs].gpr());
}
++numGPRs;
break;
case Wasm::F32:
case Wasm::F64:
if (numFPRs >= wasmCallingConvention().m_fprArgs.size()) {
if (signature.argument(i) == Wasm::F32) {
jit.load32(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchReg);
jit.store32(scratchReg, calleeFrame.withOffset(wasmOffset));
} else {
jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchReg);
jit.store64(scratchReg, calleeFrame.withOffset(wasmOffset));
}
wasmOffset += sizeof(void*);
} else {
if (signature.argument(i) == Wasm::F32)
jit.loadFloat(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmCallingConvention().m_fprArgs[numFPRs].fpr());
else
jit.loadDouble(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmCallingConvention().m_fprArgs[numFPRs].fpr());
}
++numFPRs;
break;
default:
RELEASE_ASSERT_NOT_REACHED();
}
jsOffset += sizeof(EncodedJSValue);
}
}
if (!!info.memory) {
GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
if (Context::useFastTLS())
jit.loadWasmContextInstance(baseMemory);
GPRReg currentInstanceGPR = Context::useFastTLS() ? baseMemory : wasmContextInstanceGPR;
if (mode != MemoryMode::Signaling) {
const auto& sizeRegs = pinnedRegs.sizeRegisters;
ASSERT(sizeRegs.size() >= 1);
ASSERT(!sizeRegs[0].sizeOffset); jit.loadPtr(CCallHelpers::Address(currentInstanceGPR, Wasm::Instance::offsetOfCachedMemorySize()), sizeRegs[0].sizeRegister);
for (unsigned i = 1; i < sizeRegs.size(); ++i)
jit.add64(CCallHelpers::TrustedImm32(-sizeRegs[i].sizeOffset), sizeRegs[0].sizeRegister, sizeRegs[i].sizeRegister);
}
jit.loadPtr(CCallHelpers::Address(currentInstanceGPR, Wasm::Instance::offsetOfCachedMemory()), baseMemory);
}
CCallHelpers::Call call = jit.threadSafePatchableNearCall();
unsigned functionIndexSpace = functionIndex + info.importFunctionCount();
ASSERT(functionIndexSpace < info.functionIndexSpaceSize());
jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndexSpace] (LinkBuffer& linkBuffer) {
unlinkedWasmToWasmCalls->append({ linkBuffer.locationOfNearCall<WasmEntryPtrTag>(call), functionIndexSpace });
});
for (const RegisterAtOffset& regAtOffset : registersToSpill) {
GPRReg reg = regAtOffset.reg().gpr();
ASSERT(reg != GPRInfo::returnValueGPR);
ptrdiff_t offset = regAtOffset.offset();
jit.loadPtr(CCallHelpers::Address(GPRInfo::callFrameRegister, offset), reg);
}
switch (signature.returnType()) {
case Wasm::Void:
jit.moveTrustedValue(jsUndefined(), JSValueRegs { GPRInfo::returnValueGPR });
break;
case Wasm::I32:
jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
jit.boxInt32(GPRInfo::returnValueGPR, JSValueRegs { GPRInfo::returnValueGPR }, DoNotHaveTagRegisters);
break;
case Wasm::F32:
jit.convertFloatToDouble(FPRInfo::returnValueFPR, FPRInfo::returnValueFPR);
FALLTHROUGH;
case Wasm::F64: {
jit.moveTrustedValue(jsNumber(pureNaN()), JSValueRegs { GPRInfo::returnValueGPR });
auto isNaN = jit.branchIfNaN(FPRInfo::returnValueFPR);
jit.boxDouble(FPRInfo::returnValueFPR, JSValueRegs { GPRInfo::returnValueGPR }, DoNotHaveTagRegisters);
isNaN.link(&jit);
break;
}
case Wasm::I64:
case Wasm::Func:
case Wasm::Anyfunc:
jit.breakpoint();
break;
default:
break;
}
jit.emitFunctionEpilogue();
jit.ret();
return result;
}
} }
#endif // ENABLE(WEBASSEMBLY)