/* * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef WASMFunctionCompiler_h #define WASMFunctionCompiler_h #if ENABLE(WEBASSEMBLY) #include "BinarySwitch.h" #include "CCallHelpers.h" #include "JIT.h" #include "JITOperations.h" #include "JSArrayBuffer.h" #include "LinkBuffer.h" #include "MaxFrameExtentForSlowPathCall.h" #define UNUSED 0 namespace JSC { static int32_t JIT_OPERATION operationConvertJSValueToInt32(ExecState* exec, EncodedJSValue value) { return JSValue::decode(value).toInt32(exec); } static double JIT_OPERATION operationConvertJSValueToDouble(ExecState* exec, EncodedJSValue value) { return JSValue::decode(value).toNumber(exec); } #if !CPU(X86) && !CPU(X86_64) static int32_t JIT_OPERATION operationDiv(int32_t left, int32_t right) { return left / right; } static int32_t JIT_OPERATION operationMod(int32_t left, int32_t right) { return left % right; } static uint32_t JIT_OPERATION operationUnsignedDiv(uint32_t left, uint32_t right) { return left / right; } static uint32_t JIT_OPERATION operationUnsignedMod(uint32_t left, uint32_t right) { return left % right; } #endif #if !USE(JSVALUE64) static double JIT_OPERATION operationConvertUnsignedInt32ToDouble(uint32_t value) { return static_cast(value); } #endif static size_t sizeOfMemoryType(WASMMemoryType memoryType) { switch (memoryType) { case WASMMemoryType::I8: return 1; case WASMMemoryType::I16: return 2; case WASMMemoryType::I32: case WASMMemoryType::F32: return 4; case WASMMemoryType::F64: return 8; default: ASSERT_NOT_REACHED(); } RELEASE_ASSERT_NOT_REACHED(); return 0; } class WASMFunctionCompiler : private CCallHelpers { public: typedef int Expression; typedef int Statement; typedef int ExpressionList; struct MemoryAddress { MemoryAddress(void*) { } MemoryAddress(int, uint32_t offset) : offset(offset) { } uint32_t offset; }; struct JumpTarget { Label label; JumpList jumpList; }; enum class JumpCondition { Zero, NonZero }; WASMFunctionCompiler(VM& vm, CodeBlock* codeBlock, JSWASMModule* module, unsigned stackHeight) : CCallHelpers(&vm, codeBlock) , m_module(module) , m_stackHeight(stackHeight) { } void startFunction(const Vector& arguments, uint32_t numberOfI32LocalVariables, uint32_t numberOfF32LocalVariables, uint32_t numberOfF64LocalVariables) { m_calleeSaveSpace = WTF::roundUpToMultipleOf(sizeof(StackSlot), RegisterSet::webAssemblyCalleeSaveRegisters().numberOfSetRegisters() * sizeof(void*)); m_codeBlock->setCalleeSaveRegisters(RegisterSet::webAssemblyCalleeSaveRegisters()); emitFunctionPrologue(); emitPutToCallFrameHeader(m_codeBlock, JSStack::CodeBlock); m_beginLabel = label(); addPtr(TrustedImm32(-m_calleeSaveSpace - WTF::roundUpToMultipleOf(stackAlignmentRegisters(), m_stackHeight) * sizeof(StackSlot) - maxFrameExtentForSlowPathCall), GPRInfo::callFrameRegister, GPRInfo::regT1); m_stackOverflow = branchPtr(Above, AbsoluteAddress(m_vm->addressOfStackLimit()), GPRInfo::regT1); move(GPRInfo::regT1, stackPointerRegister); checkStackPointerAlignment(); emitSaveCalleeSaves(); emitMaterializeTagCheckRegisters(); m_numberOfLocals = arguments.size() + numberOfI32LocalVariables + numberOfF32LocalVariables + numberOfF64LocalVariables; unsigned localIndex = 0; for (size_t i = 0; i < arguments.size(); ++i) { // FIXME: No need to do type conversion if the caller is a WebAssembly function. // https://bugs.webkit.org/show_bug.cgi?id=149310 Address address(GPRInfo::callFrameRegister, CallFrame::argumentOffset(i) * sizeof(Register)); #if USE(JSVALUE64) JSValueRegs valueRegs(GPRInfo::regT0); #else JSValueRegs valueRegs(GPRInfo::regT1, GPRInfo::regT0); #endif loadValue(address, valueRegs); switch (arguments[i]) { case WASMType::I32: convertValueToInt32(valueRegs, GPRInfo::regT0); store32(GPRInfo::regT0, localAddress(localIndex++)); break; case WASMType::F32: case WASMType::F64: #if USE(JSVALUE64) convertValueToDouble(valueRegs, FPRInfo::fpRegT0, GPRInfo::regT1); #else convertValueToDouble(valueRegs, FPRInfo::fpRegT0, GPRInfo::regT2, FPRInfo::fpRegT1); #endif if (arguments[i] == WASMType::F32) convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); storeDouble(FPRInfo::fpRegT0, localAddress(localIndex++)); break; default: ASSERT_NOT_REACHED(); } } for (uint32_t i = 0; i < numberOfI32LocalVariables; ++i) store32(TrustedImm32(0), localAddress(localIndex++)); for (uint32_t i = 0; i < numberOfF32LocalVariables; ++i) store32(TrustedImm32(0), localAddress(localIndex++)); for (uint32_t i = 0; i < numberOfF64LocalVariables; ++i) { #if USE(JSVALUE64) store64(TrustedImm64(0), localAddress(localIndex++)); #else store32(TrustedImm32(0), localAddress(localIndex)); store32(TrustedImm32(0), localAddress(localIndex).withOffset(4)); localIndex++; #endif } m_codeBlock->setNumParameters(1 + arguments.size()); } void endFunction() { ASSERT(!m_tempStackTop); // FIXME: Remove these if the last statement is a return statement. #if USE(JSVALUE64) JSValueRegs returnValueRegs(GPRInfo::returnValueGPR); #else JSValueRegs returnValueRegs(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR); #endif moveTrustedValue(jsUndefined(), returnValueRegs); emitRestoreCalleeSaves(); emitFunctionEpilogue(); ret(); m_stackOverflow.link(this); if (maxFrameExtentForSlowPathCall) addPtr(TrustedImm32(-maxFrameExtentForSlowPathCall), stackPointerRegister); setupArgumentsWithExecState(TrustedImmPtr(m_codeBlock)); appendCallWithExceptionCheck(operationThrowStackOverflowError); // FIXME: Implement arity check. Label arityCheck = label(); emitFunctionPrologue(); emitPutToCallFrameHeader(m_codeBlock, JSStack::CodeBlock); jump(m_beginLabel); if (!m_divideErrorJumpList.empty()) { m_divideErrorJumpList.link(this); setupArgumentsExecState(); appendCallWithExceptionCheck(operationThrowDivideError); } if (!m_outOfBoundsErrorJumpList.empty()) { m_outOfBoundsErrorJumpList.link(this); setupArgumentsExecState(); appendCallWithExceptionCheck(operationThrowOutOfBoundsAccessError); } if (!m_exceptionChecks.empty()) { m_exceptionChecks.link(this); copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(); // lookupExceptionHandler is passed two arguments, the VM and the exec (the CallFrame*). move(TrustedImmPtr(vm()), GPRInfo::argumentGPR0); move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1); #if CPU(X86) // FIXME: should use the call abstraction, but this is currently in the SpeculativeJIT layer! poke(GPRInfo::argumentGPR0); poke(GPRInfo::argumentGPR1, 1); #endif m_calls.append(std::make_pair(call(), FunctionPtr(lookupExceptionHandlerFromCallerFrame).value())); jumpToExceptionHandler(); } LinkBuffer patchBuffer(*m_vm, *this, m_codeBlock, JITCompilationMustSucceed); for (const auto& iterator : m_calls) patchBuffer.link(iterator.first, FunctionPtr(iterator.second)); for (size_t i = 0; i < m_callCompilationInfo.size(); ++i) { CallCompilationInfo& compilationInfo = m_callCompilationInfo[i]; CallLinkInfo& info = *compilationInfo.callLinkInfo; info.setCallLocations(patchBuffer.locationOfNearCall(compilationInfo.callReturnLocation), patchBuffer.locationOf(compilationInfo.hotPathBegin), patchBuffer.locationOfNearCall(compilationInfo.hotPathOther)); } MacroAssemblerCodePtr withArityCheck = patchBuffer.locationOf(arityCheck); CodeRef result = FINALIZE_CODE(patchBuffer, ("Baseline JIT code for WebAssembly")); m_codeBlock->setJITCode(adoptRef(new DirectJITCode(result, withArityCheck, JITCode::BaselineJIT))); m_codeBlock->capabilityLevel(); } int buildSetLocal(WASMOpKind opKind, uint32_t localIndex, int, WASMType type) { switch (type) { case WASMType::I32: case WASMType::F32: load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0); store32(GPRInfo::regT0, localAddress(localIndex)); break; case WASMType::F64: loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0); storeDouble(FPRInfo::fpRegT0, localAddress(localIndex)); break; default: ASSERT_NOT_REACHED(); } if (opKind == WASMOpKind::Statement) m_tempStackTop--; return UNUSED; } int buildSetGlobal(WASMOpKind opKind, uint32_t globalIndex, int, WASMType type) { move(TrustedImmPtr(&m_module->globalVariables()[globalIndex]), GPRInfo::regT0); switch (type) { case WASMType::I32: case WASMType::F32: load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1); store32(GPRInfo::regT1, GPRInfo::regT0); break; case WASMType::F64: loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0); storeDouble(FPRInfo::fpRegT0, GPRInfo::regT0); break; default: ASSERT_NOT_REACHED(); } if (opKind == WASMOpKind::Statement) m_tempStackTop--; return UNUSED; } void buildReturn(int, WASMExpressionType returnType) { #if USE(JSVALUE64) JSValueRegs returnValueRegs(GPRInfo::returnValueGPR); #else JSValueRegs returnValueRegs(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR); #endif switch (returnType) { case WASMExpressionType::I32: load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::returnValueGPR); #if USE(JSVALUE64) or64(GPRInfo::tagTypeNumberRegister, GPRInfo::returnValueGPR); #else move(TrustedImm32(JSValue::Int32Tag), GPRInfo::returnValueGPR2); #endif m_tempStackTop--; break; case WASMExpressionType::F32: case WASMExpressionType::F64: loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0); if (returnType == WASMExpressionType::F32) convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0); convertDoubleToValue(FPRInfo::fpRegT0, returnValueRegs); m_tempStackTop--; break; case WASMExpressionType::Void: moveTrustedValue(jsUndefined(), returnValueRegs); break; default: ASSERT_NOT_REACHED(); } emitRestoreCalleeSaves(); emitFunctionEpilogue(); ret(); } int buildImmediateI32(uint32_t immediate) { store32(Imm32(immediate), temporaryAddress(m_tempStackTop++)); return UNUSED; } int buildImmediateF32(float immediate) { store32(Imm32(bitwise_cast(immediate)), temporaryAddress(m_tempStackTop++)); return UNUSED; } int buildImmediateF64(double immediate) { #if USE(JSVALUE64) store64(Imm64(bitwise_cast(immediate)), temporaryAddress(m_tempStackTop++)); #else union { double doubleValue; int32_t int32Values[2]; } u = { immediate }; m_tempStackTop++; store32(Imm32(u.int32Values[0]), temporaryAddress(m_tempStackTop - 1)); store32(Imm32(u.int32Values[1]), temporaryAddress(m_tempStackTop - 1).withOffset(4)); #endif return UNUSED; } int buildGetLocal(uint32_t localIndex, WASMType type) { switch (type) { case WASMType::I32: case WASMType::F32: load32(localAddress(localIndex), GPRInfo::regT0); store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop++)); break; case WASMType::F64: loadDouble(localAddress(localIndex), FPRInfo::fpRegT0); storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop++)); break; default: ASSERT_NOT_REACHED(); } return UNUSED; } int buildGetGlobal(uint32_t globalIndex, WASMType type) { move(TrustedImmPtr(&m_module->globalVariables()[globalIndex]), GPRInfo::regT0); switch (type) { case WASMType::I32: case WASMType::F32: load32(GPRInfo::regT0, GPRInfo::regT0); store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop++)); break; case WASMType::F64: loadDouble(GPRInfo::regT0, FPRInfo::fpRegT0); storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop++)); break; default: ASSERT_NOT_REACHED(); } return UNUSED; } int buildConvertType(int, WASMExpressionType fromType, WASMExpressionType toType, WASMTypeConversion conversion) { switch (fromType) { case WASMExpressionType::I32: load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0); ASSERT(toType == WASMExpressionType::F32 || toType == WASMExpressionType::F64); if (conversion == WASMTypeConversion::ConvertSigned) convertInt32ToDouble(GPRInfo::regT0, FPRInfo::fpRegT0); else { ASSERT(conversion == WASMTypeConversion::ConvertUnsigned); #if USE(JSVALUE64) convertInt64ToDouble(GPRInfo::regT0, FPRInfo::fpRegT0); #else callOperation(operationConvertUnsignedInt32ToDouble, GPRInfo::regT0, FPRInfo::fpRegT0); #endif } if (toType == WASMExpressionType::F32) convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); break; case WASMExpressionType::F32: loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0); switch (toType) { case WASMExpressionType::I32: ASSERT(conversion == WASMTypeConversion::ConvertSigned); convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0); truncateDoubleToInt32(FPRInfo::fpRegT0, GPRInfo::regT0); store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); break; case WASMExpressionType::F64: ASSERT(conversion == WASMTypeConversion::Promote); convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0); storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); break; default: ASSERT_NOT_REACHED(); } break; case WASMExpressionType::F64: loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0); switch (toType) { case WASMExpressionType::I32: ASSERT(conversion == WASMTypeConversion::ConvertSigned); truncateDoubleToInt32(FPRInfo::fpRegT0, GPRInfo::regT0); store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); break; case WASMExpressionType::F32: ASSERT(conversion == WASMTypeConversion::Demote); convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); break; default: ASSERT_NOT_REACHED(); } break; default: ASSERT_NOT_REACHED(); } return UNUSED; } int buildLoad(const MemoryAddress& memoryAddress, WASMExpressionType expressionType, WASMMemoryType memoryType, MemoryAccessConversion conversion) { const ArrayBuffer* arrayBuffer = m_module->arrayBuffer()->impl(); move(TrustedImmPtr(arrayBuffer->data()), GPRInfo::regT0); load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1); if (memoryAddress.offset) add32(TrustedImm32(memoryAddress.offset), GPRInfo::regT1, GPRInfo::regT1); and32(TrustedImm32(~(sizeOfMemoryType(memoryType) - 1)), GPRInfo::regT1); ASSERT(arrayBuffer->byteLength() < (unsigned)(1 << 31)); if (arrayBuffer->byteLength() >= sizeOfMemoryType(memoryType)) m_outOfBoundsErrorJumpList.append(branch32(Above, GPRInfo::regT1, TrustedImm32(arrayBuffer->byteLength() - sizeOfMemoryType(memoryType)))); else m_outOfBoundsErrorJumpList.append(jump()); BaseIndex address = BaseIndex(GPRInfo::regT0, GPRInfo::regT1, TimesOne); switch (expressionType) { case WASMExpressionType::I32: switch (memoryType) { case WASMMemoryType::I8: if (conversion == MemoryAccessConversion::SignExtend) load8SignedExtendTo32(address, GPRInfo::regT0); else { ASSERT(conversion == MemoryAccessConversion::ZeroExtend); load8(address, GPRInfo::regT0); } break; case WASMMemoryType::I16: if (conversion == MemoryAccessConversion::SignExtend) load16SignedExtendTo32(address, GPRInfo::regT0); else { ASSERT(conversion == MemoryAccessConversion::ZeroExtend); load16(address, GPRInfo::regT0); } break; case WASMMemoryType::I32: load32(address, GPRInfo::regT0); break; default: ASSERT_NOT_REACHED(); } store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); break; case WASMExpressionType::F32: ASSERT(memoryType == WASMMemoryType::F32 && conversion == MemoryAccessConversion::NoConversion); load32(address, GPRInfo::regT0); store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); break; case WASMExpressionType::F64: ASSERT(memoryType == WASMMemoryType::F64 && conversion == MemoryAccessConversion::NoConversion); loadDouble(address, FPRInfo::fpRegT0); storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); break; default: ASSERT_NOT_REACHED(); } return UNUSED; } int buildStore(WASMOpKind opKind, const MemoryAddress& memoryAddress, WASMExpressionType expressionType, WASMMemoryType memoryType, int) { const ArrayBuffer* arrayBuffer = m_module->arrayBuffer()->impl(); move(TrustedImmPtr(arrayBuffer->data()), GPRInfo::regT0); load32(temporaryAddress(m_tempStackTop - 2), GPRInfo::regT1); if (memoryAddress.offset) add32(TrustedImm32(memoryAddress.offset), GPRInfo::regT1, GPRInfo::regT1); and32(TrustedImm32(~(sizeOfMemoryType(memoryType) - 1)), GPRInfo::regT1); ASSERT(arrayBuffer->byteLength() < (1u << 31)); if (arrayBuffer->byteLength() >= sizeOfMemoryType(memoryType)) m_outOfBoundsErrorJumpList.append(branch32(Above, GPRInfo::regT1, TrustedImm32(arrayBuffer->byteLength() - sizeOfMemoryType(memoryType)))); else m_outOfBoundsErrorJumpList.append(jump()); BaseIndex address = BaseIndex(GPRInfo::regT0, GPRInfo::regT1, TimesOne); switch (expressionType) { case WASMExpressionType::I32: load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT2); switch (memoryType) { case WASMMemoryType::I8: store8(GPRInfo::regT2, address); break; case WASMMemoryType::I16: store16(GPRInfo::regT2, address); break; case WASMMemoryType::I32: store32(GPRInfo::regT2, address); break; default: ASSERT_NOT_REACHED(); } break; case WASMExpressionType::F32: ASSERT(memoryType == WASMMemoryType::F32); load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT2); store32(GPRInfo::regT2, address); break; case WASMExpressionType::F64: ASSERT(memoryType == WASMMemoryType::F64); loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0); storeDouble(FPRInfo::fpRegT0, address); break; default: ASSERT_NOT_REACHED(); } m_tempStackTop -= 2; if (opKind == WASMOpKind::Expression) { switch (expressionType) { case WASMExpressionType::I32: case WASMExpressionType::F32: store32(GPRInfo::regT2, temporaryAddress(m_tempStackTop++)); break; case WASMExpressionType::F64: storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop++)); break; default: ASSERT_NOT_REACHED(); } } return UNUSED; } int buildUnaryI32(int, WASMOpExpressionI32 op) { load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0); switch (op) { case WASMOpExpressionI32::Negate: neg32(GPRInfo::regT0); break; case WASMOpExpressionI32::BitNot: xor32(TrustedImm32(-1), GPRInfo::regT0); break; case WASMOpExpressionI32::CountLeadingZeros: countLeadingZeros32(GPRInfo::regT0, GPRInfo::regT0); break; case WASMOpExpressionI32::LogicalNot: { // FIXME: Don't use branches. Jump zero = branchTest32(Zero, GPRInfo::regT0); move(TrustedImm32(0), GPRInfo::regT0); Jump end = jump(); zero.link(this); move(TrustedImm32(1), GPRInfo::regT0); end.link(this); break; } case WASMOpExpressionI32::Abs: { // FIXME: Don't use branches. Jump end = branchTest32(PositiveOrZero, GPRInfo::regT0); neg32(GPRInfo::regT0); end.link(this); break; } default: ASSERT_NOT_REACHED(); } store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); return UNUSED; } int buildUnaryF32(int, WASMOpExpressionF32 op) { loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); switch (op) { case WASMOpExpressionF32::Negate: convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1); negateDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); break; case WASMOpExpressionF32::Abs: convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1); absDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); break; case WASMOpExpressionF32::Ceil: callOperation(ceilf, FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; case WASMOpExpressionF32::Floor: callOperation(floorf, FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; case WASMOpExpressionF32::Sqrt: convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1); sqrtDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); break; default: ASSERT_NOT_REACHED(); } storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); return UNUSED; } int buildUnaryF64(int, WASMOpExpressionF64 op) { loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); switch (op) { case WASMOpExpressionF64::Negate: negateDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; case WASMOpExpressionF64::Abs: absDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; case WASMOpExpressionF64::Sqrt: sqrtDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; case WASMOpExpressionF64::Ceil: case WASMOpExpressionF64::Floor: case WASMOpExpressionF64::Cos: case WASMOpExpressionF64::Sin: case WASMOpExpressionF64::Tan: case WASMOpExpressionF64::ACos: case WASMOpExpressionF64::ASin: case WASMOpExpressionF64::ATan: case WASMOpExpressionF64::Exp: case WASMOpExpressionF64::Ln: D_JITOperation_D operation; switch (op) { case WASMOpExpressionF64::Ceil: operation = ceil; break; case WASMOpExpressionF64::Floor: operation = floor; break; case WASMOpExpressionF64::Cos: operation = cos; break; case WASMOpExpressionF64::Sin: operation = sin; break; case WASMOpExpressionF64::Tan: operation = tan; break; case WASMOpExpressionF64::ACos: operation = acos; break; case WASMOpExpressionF64::ASin: operation = asin; break; case WASMOpExpressionF64::ATan: operation = atan; break; case WASMOpExpressionF64::Exp: operation = exp; break; case WASMOpExpressionF64::Ln: operation = log; break; default: RELEASE_ASSERT_NOT_REACHED(); } callOperation(operation, FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; default: ASSERT_NOT_REACHED(); } storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); return UNUSED; } int buildBinaryI32(int, int, WASMOpExpressionI32 op) { load32(temporaryAddress(m_tempStackTop - 2), GPRInfo::regT0); load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1); switch (op) { case WASMOpExpressionI32::Add: add32(GPRInfo::regT1, GPRInfo::regT0); break; case WASMOpExpressionI32::Sub: sub32(GPRInfo::regT1, GPRInfo::regT0); break; case WASMOpExpressionI32::Mul: mul32(GPRInfo::regT1, GPRInfo::regT0); break; case WASMOpExpressionI32::SDiv: case WASMOpExpressionI32::UDiv: case WASMOpExpressionI32::SMod: case WASMOpExpressionI32::UMod: { m_divideErrorJumpList.append(branchTest32(Zero, GPRInfo::regT1)); if (op == WASMOpExpressionI32::SDiv || op == WASMOpExpressionI32::SMod) { Jump denominatorNotNeg1 = branch32(NotEqual, GPRInfo::regT1, TrustedImm32(-1)); m_divideErrorJumpList.append(branch32(Equal, GPRInfo::regT0, TrustedImm32(-2147483647-1))); denominatorNotNeg1.link(this); } #if CPU(X86) || CPU(X86_64) ASSERT(GPRInfo::regT0 == X86Registers::eax); move(GPRInfo::regT1, X86Registers::ecx); if (op == WASMOpExpressionI32::SDiv || op == WASMOpExpressionI32::SMod) { x86ConvertToDoubleWord32(); x86Div32(X86Registers::ecx); } else { ASSERT(op == WASMOpExpressionI32::UDiv || op == WASMOpExpressionI32::UMod); xor32(X86Registers::edx, X86Registers::edx); m_assembler.divl_r(X86Registers::ecx); } if (op == WASMOpExpressionI32::SMod || op == WASMOpExpressionI32::UMod) move(X86Registers::edx, GPRInfo::regT0); #else // FIXME: We should be able to do an inline div on ARMv7 and ARM64. switch (op) { case WASMOpExpressionI32::SDiv: callOperation(operationDiv, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0); break; case WASMOpExpressionI32::UDiv: callOperation(operationUnsignedDiv, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0); break; case WASMOpExpressionI32::SMod: callOperation(operationMod, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0); break; case WASMOpExpressionI32::UMod: callOperation(operationUnsignedMod, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0); break; default: ASSERT_NOT_REACHED(); } #endif break; } case WASMOpExpressionI32::BitOr: or32(GPRInfo::regT1, GPRInfo::regT0); break; case WASMOpExpressionI32::BitAnd: and32(GPRInfo::regT1, GPRInfo::regT0); break; case WASMOpExpressionI32::BitXor: xor32(GPRInfo::regT1, GPRInfo::regT0); break; case WASMOpExpressionI32::LeftShift: lshift32(GPRInfo::regT1, GPRInfo::regT0); break; case WASMOpExpressionI32::ArithmeticRightShift: rshift32(GPRInfo::regT1, GPRInfo::regT0); break; case WASMOpExpressionI32::LogicalRightShift: urshift32(GPRInfo::regT1, GPRInfo::regT0); break; default: ASSERT_NOT_REACHED(); } m_tempStackTop--; store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); return UNUSED; } int buildBinaryF32(int, int, WASMOpExpressionF32 op) { loadDouble(temporaryAddress(m_tempStackTop - 2), FPRInfo::fpRegT0); loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0); convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1); switch (op) { case WASMOpExpressionF32::Add: addDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; case WASMOpExpressionF32::Sub: subDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; case WASMOpExpressionF32::Mul: mulDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; case WASMOpExpressionF32::Div: divDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; default: RELEASE_ASSERT_NOT_REACHED(); } convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); m_tempStackTop--; storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); return UNUSED; } int buildBinaryF64(int, int, WASMOpExpressionF64 op) { loadDouble(temporaryAddress(m_tempStackTop - 2), FPRInfo::fpRegT0); loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); switch (op) { case WASMOpExpressionF64::Add: addDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; case WASMOpExpressionF64::Sub: subDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; case WASMOpExpressionF64::Mul: mulDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; case WASMOpExpressionF64::Div: divDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; case WASMOpExpressionF64::Mod: case WASMOpExpressionF64::ATan2: case WASMOpExpressionF64::Pow: D_JITOperation_DD operation; switch (op) { case WASMOpExpressionF64::Mod: operation = fmod; break; case WASMOpExpressionF64::ATan2: operation = atan2; break; case WASMOpExpressionF64::Pow: operation = pow; break; default: RELEASE_ASSERT_NOT_REACHED(); } callOperation(operation, FPRInfo::fpRegT0, FPRInfo::fpRegT1, FPRInfo::fpRegT0); break; default: ASSERT_NOT_REACHED(); } m_tempStackTop--; storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); return UNUSED; } int buildRelationalI32(int, int, WASMOpExpressionI32 op) { load32(temporaryAddress(m_tempStackTop - 2), GPRInfo::regT0); load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1); RelationalCondition condition; switch (op) { case WASMOpExpressionI32::EqualI32: condition = Equal; break; case WASMOpExpressionI32::NotEqualI32: condition = NotEqual; break; case WASMOpExpressionI32::SLessThanI32: condition = LessThan; break; case WASMOpExpressionI32::ULessThanI32: condition = Below; break; case WASMOpExpressionI32::SLessThanOrEqualI32: condition = LessThanOrEqual; break; case WASMOpExpressionI32::ULessThanOrEqualI32: condition = BelowOrEqual; break; case WASMOpExpressionI32::SGreaterThanI32: condition = GreaterThan; break; case WASMOpExpressionI32::UGreaterThanI32: condition = Above; break; case WASMOpExpressionI32::SGreaterThanOrEqualI32: condition = GreaterThanOrEqual; break; case WASMOpExpressionI32::UGreaterThanOrEqualI32: condition = AboveOrEqual; break; default: RELEASE_ASSERT_NOT_REACHED(); } compare32(condition, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0); m_tempStackTop--; store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); return UNUSED; } int buildRelationalF32(int, int, WASMOpExpressionI32 op) { loadDouble(temporaryAddress(m_tempStackTop - 2), FPRInfo::fpRegT0); loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0); convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1); DoubleCondition condition; switch (op) { case WASMOpExpressionI32::EqualF32: condition = DoubleEqual; break; case WASMOpExpressionI32::NotEqualF32: condition = DoubleNotEqual; break; case WASMOpExpressionI32::LessThanF32: condition = DoubleLessThan; break; case WASMOpExpressionI32::LessThanOrEqualF32: condition = DoubleLessThanOrEqual; break; case WASMOpExpressionI32::GreaterThanF32: condition = DoubleGreaterThan; break; case WASMOpExpressionI32::GreaterThanOrEqualF32: condition = DoubleGreaterThanOrEqual; break; default: RELEASE_ASSERT_NOT_REACHED(); } m_tempStackTop--; Jump trueCase = branchDouble(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1); store32(TrustedImm32(0), temporaryAddress(m_tempStackTop - 1)); Jump end = jump(); trueCase.link(this); store32(TrustedImm32(1), temporaryAddress(m_tempStackTop - 1)); end.link(this); return UNUSED; } int buildRelationalF64(int, int, WASMOpExpressionI32 op) { loadDouble(temporaryAddress(m_tempStackTop - 2), FPRInfo::fpRegT0); loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); DoubleCondition condition; switch (op) { case WASMOpExpressionI32::EqualF64: condition = DoubleEqual; break; case WASMOpExpressionI32::NotEqualF64: condition = DoubleNotEqual; break; case WASMOpExpressionI32::LessThanF64: condition = DoubleLessThan; break; case WASMOpExpressionI32::LessThanOrEqualF64: condition = DoubleLessThanOrEqual; break; case WASMOpExpressionI32::GreaterThanF64: condition = DoubleGreaterThan; break; case WASMOpExpressionI32::GreaterThanOrEqualF64: condition = DoubleGreaterThanOrEqual; break; default: RELEASE_ASSERT_NOT_REACHED(); } m_tempStackTop--; Jump trueCase = branchDouble(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1); store32(TrustedImm32(0), temporaryAddress(m_tempStackTop - 1)); Jump end = jump(); trueCase.link(this); store32(TrustedImm32(1), temporaryAddress(m_tempStackTop - 1)); end.link(this); return UNUSED; } int buildMinOrMaxI32(int, int, WASMOpExpressionI32 op) { load32(temporaryAddress(m_tempStackTop - 2), GPRInfo::regT0); load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1); RelationalCondition condition; switch (op) { case WASMOpExpressionI32::SMin: condition = LessThanOrEqual; break; case WASMOpExpressionI32::UMin: condition = BelowOrEqual; break; case WASMOpExpressionI32::SMax: condition = GreaterThanOrEqual; break; case WASMOpExpressionI32::UMax: condition = AboveOrEqual; break; default: RELEASE_ASSERT_NOT_REACHED(); } Jump useLeft = branch32(condition, GPRInfo::regT0, GPRInfo::regT1); store32(GPRInfo::regT1, temporaryAddress(m_tempStackTop - 2)); useLeft.link(this); m_tempStackTop--; return UNUSED; } int buildMinOrMaxF64(int, int, WASMOpExpressionF64 op) { loadDouble(temporaryAddress(m_tempStackTop - 2), FPRInfo::fpRegT0); loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); DoubleCondition condition; switch (op) { case WASMOpExpressionF64::Min: condition = DoubleLessThanOrEqual; break; case WASMOpExpressionF64::Max: condition = DoubleGreaterThanOrEqual; break; default: RELEASE_ASSERT_NOT_REACHED(); } Jump useLeft = branchDouble(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1); storeDouble(FPRInfo::fpRegT1, temporaryAddress(m_tempStackTop - 2)); useLeft.link(this); m_tempStackTop--; return UNUSED; } int buildCallInternal(uint32_t functionIndex, int, const WASMSignature& signature, WASMExpressionType returnType) { boxArgumentsAndAdjustStackPointer(signature.arguments); JSFunction* function = m_module->functions()[functionIndex].get(); move(TrustedImmPtr(function), GPRInfo::regT0); callAndUnboxResult(returnType); return UNUSED; } int buildCallIndirect(uint32_t functionPointerTableIndex, int, int, const WASMSignature& signature, WASMExpressionType returnType) { boxArgumentsAndAdjustStackPointer(signature.arguments); const Vector& functions = m_module->functionPointerTables()[functionPointerTableIndex].functions; move(TrustedImmPtr(functions.data()), GPRInfo::regT0); load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1); m_tempStackTop--; and32(TrustedImm32(functions.size() - 1), GPRInfo::regT1); loadPtr(BaseIndex(GPRInfo::regT0, GPRInfo::regT1, timesPtr()), GPRInfo::regT0); callAndUnboxResult(returnType); return UNUSED; } int buildCallImport(uint32_t functionImportIndex, int, const WASMSignature& signature, WASMExpressionType returnType) { boxArgumentsAndAdjustStackPointer(signature.arguments); JSFunction* function = m_module->importedFunctions()[functionImportIndex].get(); move(TrustedImmPtr(function), GPRInfo::regT0); callAndUnboxResult(returnType); return UNUSED; } void appendExpressionList(int&, int) { } void discard(int) { m_tempStackTop--; } void linkTarget(JumpTarget& target) { target.label = label(); target.jumpList.link(this); } void jumpToTarget(JumpTarget& target) { if (target.label.isSet()) jump(target.label); else target.jumpList.append(jump()); } void jumpToTargetIf(JumpCondition condition, int, JumpTarget& target) { load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0); m_tempStackTop--; Jump taken = branchTest32((condition == JumpCondition::Zero) ? Zero : NonZero, GPRInfo::regT0); if (target.label.isSet()) taken.linkTo(target.label, this); else target.jumpList.append(taken); } void startLoop() { m_breakTargets.append(JumpTarget()); m_continueTargets.append(JumpTarget()); } void endLoop() { m_breakTargets.removeLast(); m_continueTargets.removeLast(); } void startSwitch() { m_breakTargets.append(JumpTarget()); } void endSwitch() { m_breakTargets.removeLast(); } void startLabel() { m_breakLabelTargets.append(JumpTarget()); m_continueLabelTargets.append(JumpTarget()); linkTarget(m_continueLabelTargets.last()); } void endLabel() { linkTarget(m_breakLabelTargets.last()); m_breakLabelTargets.removeLast(); m_continueLabelTargets.removeLast(); } JumpTarget& breakTarget() { return m_breakTargets.last(); } JumpTarget& continueTarget() { return m_continueTargets.last(); } JumpTarget& breakLabelTarget(uint32_t labelIndex) { return m_breakLabelTargets[labelIndex]; } JumpTarget& continueLabelTarget(uint32_t labelIndex) { return m_continueLabelTargets[labelIndex]; } void buildSwitch(int, const Vector& cases, Vector& targets, JumpTarget defaultTarget) { load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0); m_tempStackTop--; BinarySwitch binarySwitch(GPRInfo::regT0, cases, BinarySwitch::Int32); while (binarySwitch.advance(*this)) { unsigned index = binarySwitch.caseIndex(); jump(targets[index].label); } binarySwitch.fallThrough().linkTo(defaultTarget.label, this); } private: union StackSlot { int32_t intValue; float floatValue; double doubleValue; }; enum class FloatingPointPrecision { Single, Double }; Address localAddress(unsigned localIndex) const { ASSERT(localIndex < m_numberOfLocals); return Address(GPRInfo::callFrameRegister, -m_calleeSaveSpace - (localIndex + 1) * sizeof(StackSlot)); } Address temporaryAddress(unsigned temporaryIndex) const { ASSERT(m_numberOfLocals + temporaryIndex < m_stackHeight); return Address(GPRInfo::callFrameRegister, -m_calleeSaveSpace - (m_numberOfLocals + temporaryIndex + 1) * sizeof(StackSlot)); } void appendCall(const FunctionPtr& function) { m_calls.append(std::make_pair(call(), function.value())); } void appendCallWithExceptionCheck(const FunctionPtr& function) { appendCall(function); m_exceptionChecks.append(emitExceptionCheck()); } Call emitNakedCall(CodePtr function) { Call nakedCall = nearCall(); m_calls.append(std::make_pair(nakedCall, function.executableAddress())); return nakedCall; } void appendCallSetResult(const FunctionPtr& function, GPRReg result) { appendCall(function); move(GPRInfo::returnValueGPR, result); } #if CPU(X86) void appendCallSetResult(const FunctionPtr& function, FPRReg result, FloatingPointPrecision precision) { appendCall(function); switch (precision) { case FloatingPointPrecision::Single: m_assembler.fstps(0, stackPointerRegister); break; case FloatingPointPrecision::Double: m_assembler.fstpl(0, stackPointerRegister); break; } loadDouble(stackPointerRegister, result); } #elif CPU(ARM) && !CPU(ARM_HARDFP) void appendCallSetResult(const FunctionPtr& function, FPRReg result, FloatingPointPrecision) { appendCall(function); m_assembler.vmov(result, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2); } #else // CPU(X86_64) || (CPU(ARM) && CPU(ARM_HARDFP)) || CPU(ARM64) || CPU(MIPS) || CPU(SH4) void appendCallSetResult(const FunctionPtr& function, FPRReg result, FloatingPointPrecision) { appendCall(function); moveDouble(FPRInfo::returnValueFPR, result); } #endif #if USE(JSVALUE64) void callOperation(Z_JITOperation_EJ operation, GPRReg src, GPRReg dst) { setupArgumentsWithExecState(src); appendCallSetResult(operation, dst); } void callOperation(D_JITOperation_EJ operation, GPRReg src, FPRReg dst) { setupArgumentsWithExecState(src); appendCallSetResult(operation, dst, FloatingPointPrecision::Double); } #else // EncodedJSValue in JSVALUE32_64 is a 64-bit integer. When being compiled in ARM EABI, it must be aligned on an even-numbered register (r0, r2 or [sp]). // To prevent the assembler from using wrong registers, let's occupy r1 or r3 with a dummy argument when necessary. #if (COMPILER_SUPPORTS(EABI) && CPU(ARM)) || CPU(MIPS) #define EABI_32BIT_DUMMY_ARG TrustedImm32(0), #else #define EABI_32BIT_DUMMY_ARG #endif void callOperation(Z_JITOperation_EJ operation, GPRReg srcTag, GPRReg srcPayload, GPRReg dst) { setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG srcPayload, srcTag); appendCallSetResult(operation, dst); } void callOperation(D_JITOperation_EJ operation, GPRReg srcTag, GPRReg srcPayload, FPRReg dst) { setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG srcPayload, srcTag); appendCallSetResult(operation, dst, FloatingPointPrecision::Double); } #endif void callOperation(float JIT_OPERATION (*operation)(float), FPRegisterID src, FPRegisterID dst) { setupArguments(src); appendCallSetResult(operation, dst, FloatingPointPrecision::Single); } void callOperation(D_JITOperation_D operation, FPRegisterID src, FPRegisterID dst) { setupArguments(src); appendCallSetResult(operation, dst, FloatingPointPrecision::Double); } void callOperation(int32_t JIT_OPERATION (*operation)(int32_t, int32_t), GPRReg src1, GPRReg src2, GPRReg dst) { setupArguments(src1, src2); appendCallSetResult(operation, dst); } void callOperation(uint32_t JIT_OPERATION (*operation)(uint32_t, uint32_t), GPRReg src1, GPRReg src2, GPRReg dst) { setupArguments(src1, src2); appendCallSetResult(operation, dst); } void callOperation(D_JITOperation_DD operation, FPRegisterID src1, FPRegisterID src2, FPRegisterID dst) { setupArguments(src1, src2); appendCallSetResult(operation, dst, FloatingPointPrecision::Double); } void callOperation(double JIT_OPERATION (*operation)(uint32_t), GPRReg src, FPRegisterID dst) { setupArguments(src); appendCallSetResult(operation, dst, FloatingPointPrecision::Double); } void boxArgumentsAndAdjustStackPointer(const Vector& arguments) { size_t argumentCount = arguments.size(); int stackOffset = -m_calleeSaveSpace - WTF::roundUpToMultipleOf(stackAlignmentRegisters(), m_numberOfLocals + m_tempStackTop + argumentCount + 1 + JSStack::CallFrameHeaderSize); storeTrustedValue(jsUndefined(), Address(GPRInfo::callFrameRegister, (stackOffset + CallFrame::thisArgumentOffset()) * sizeof(Register))); for (size_t i = 0; i < argumentCount; ++i) { Address address(GPRInfo::callFrameRegister, (stackOffset + CallFrame::argumentOffset(i)) * sizeof(Register)); #if USE(JSVALUE64) JSValueRegs valueRegs(GPRInfo::regT0); #else JSValueRegs valueRegs(GPRInfo::regT1, GPRInfo::regT0); #endif switch (arguments[i]) { case WASMType::I32: load32(temporaryAddress(m_tempStackTop - argumentCount + i), GPRInfo::regT0); #if USE(JSVALUE64) or64(GPRInfo::tagTypeNumberRegister, GPRInfo::regT0); store64(GPRInfo::regT0, address); #else store32(GPRInfo::regT0, address.withOffset(PayloadOffset)); store32(TrustedImm32(JSValue::Int32Tag), address.withOffset(TagOffset)); #endif break; case WASMType::F32: case WASMType::F64: loadDouble(temporaryAddress(m_tempStackTop - argumentCount + i), FPRInfo::fpRegT0); if (arguments[i] == WASMType::F32) convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0); convertDoubleToValue(FPRInfo::fpRegT0, valueRegs); storeValue(valueRegs, address); break; default: ASSERT_NOT_REACHED(); } } m_tempStackTop -= argumentCount; addPtr(TrustedImm32(stackOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), GPRInfo::callFrameRegister, stackPointerRegister); store32(TrustedImm32(argumentCount + 1), Address(stackPointerRegister, JSStack::ArgumentCount * static_cast(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC))); } void callAndUnboxResult(WASMExpressionType returnType) { // regT0 holds callee. #if USE(JSVALUE64) store64(GPRInfo::regT0, Address(stackPointerRegister, JSStack::Callee * static_cast(sizeof(Register)) - sizeof(CallerFrameAndPC))); #else store32(GPRInfo::regT0, Address(stackPointerRegister, JSStack::Callee * static_cast(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC))); store32(TrustedImm32(JSValue::CellTag), Address(stackPointerRegister, JSStack::Callee * static_cast(sizeof(Register)) + TagOffset - sizeof(CallerFrameAndPC))); #endif DataLabelPtr addressOfLinkedFunctionCheck; Jump slowCase = branchPtrWithPatch(NotEqual, GPRInfo::regT0, addressOfLinkedFunctionCheck, TrustedImmPtr(0)); CallLinkInfo* info = m_codeBlock->addCallLinkInfo(); info->setUpCall(CallLinkInfo::Call, CodeOrigin(), GPRInfo::regT0); m_callCompilationInfo.append(CallCompilationInfo()); m_callCompilationInfo.last().hotPathBegin = addressOfLinkedFunctionCheck; m_callCompilationInfo.last().callLinkInfo = info; m_callCompilationInfo.last().hotPathOther = nearCall(); Jump end = jump(); slowCase.link(this); move(TrustedImmPtr(info), GPRInfo::regT2); m_callCompilationInfo.last().callReturnLocation = emitNakedCall(m_vm->getCTIStub(linkCallThunkGenerator).code()); end.link(this); addPtr(TrustedImm32(-m_calleeSaveSpace - WTF::roundUpToMultipleOf(stackAlignmentRegisters(), m_stackHeight) * sizeof(StackSlot) - maxFrameExtentForSlowPathCall), GPRInfo::callFrameRegister, stackPointerRegister); checkStackPointerAlignment(); // FIXME: No need to do type conversion if the callee is a WebAssembly function. // https://bugs.webkit.org/show_bug.cgi?id=149310 #if USE(JSVALUE64) JSValueRegs valueRegs(GPRInfo::returnValueGPR); #else JSValueRegs valueRegs(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR); #endif switch (returnType) { case WASMExpressionType::I32: convertValueToInt32(valueRegs, GPRInfo::regT0); store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop++)); break; case WASMExpressionType::F32: case WASMExpressionType::F64: #if USE(JSVALUE64) convertValueToDouble(valueRegs, FPRInfo::fpRegT0, GPRInfo::nonPreservedNonReturnGPR); #else convertValueToDouble(valueRegs, FPRInfo::fpRegT0, GPRInfo::nonPreservedNonReturnGPR, FPRInfo::fpRegT1); #endif if (returnType == WASMExpressionType::F32) convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop++)); break; case WASMExpressionType::Void: break; default: ASSERT_NOT_REACHED(); } } #if USE(JSVALUE64) void convertValueToInt32(JSValueRegs valueRegs, GPRReg dst) { Jump checkJSInt32 = branchIfInt32(valueRegs); callOperation(operationConvertJSValueToInt32, valueRegs.gpr(), valueRegs.gpr()); checkJSInt32.link(this); move(valueRegs.gpr(), dst); } void convertValueToDouble(JSValueRegs valueRegs, FPRReg dst, GPRReg scratch) { Jump checkJSInt32 = branchIfInt32(valueRegs); Jump checkJSNumber = branchIfNumber(valueRegs, scratch); JumpList end; callOperation(operationConvertJSValueToDouble, valueRegs.gpr(), dst); end.append(jump()); checkJSInt32.link(this); convertInt32ToDouble(valueRegs.gpr(), dst); end.append(jump()); checkJSNumber.link(this); unboxDoubleWithoutAssertions(valueRegs.gpr(), scratch, dst); end.link(this); } #else void convertValueToInt32(JSValueRegs valueRegs, GPRReg dst) { Jump checkJSInt32 = branchIfInt32(valueRegs); callOperation(operationConvertJSValueToInt32, valueRegs.tagGPR(), valueRegs.payloadGPR(), valueRegs.payloadGPR()); checkJSInt32.link(this); move(valueRegs.payloadGPR(), dst); } void convertValueToDouble(JSValueRegs valueRegs, FPRReg dst, GPRReg scratch, FPRReg fpScratch) { Jump checkJSInt32 = branchIfInt32(valueRegs); Jump checkJSNumber = branchIfNumber(valueRegs, scratch); JumpList end; callOperation(operationConvertJSValueToDouble, valueRegs.tagGPR(), valueRegs.payloadGPR(), dst); end.append(jump()); checkJSInt32.link(this); convertInt32ToDouble(valueRegs.payloadGPR(), dst); end.append(jump()); checkJSNumber.link(this); unboxDouble(valueRegs.tagGPR(), valueRegs.payloadGPR(), dst, fpScratch); end.link(this); } #endif void convertDoubleToValue(FPRReg fpr, JSValueRegs valueRegs) { #if USE(JSVALUE64) boxDouble(fpr, valueRegs.gpr()); #else boxDouble(fpr, valueRegs.tagGPR(), valueRegs.payloadGPR()); #endif } JSWASMModule* m_module; unsigned m_stackHeight; unsigned m_numberOfLocals; unsigned m_tempStackTop { 0 }; unsigned m_calleeSaveSpace; Vector m_breakTargets; Vector m_continueTargets; Vector m_breakLabelTargets; Vector m_continueLabelTargets; Label m_beginLabel; Jump m_stackOverflow; JumpList m_divideErrorJumpList; JumpList m_outOfBoundsErrorJumpList; JumpList m_exceptionChecks; Vector> m_calls; Vector m_callCompilationInfo; }; } // namespace JSC #endif // ENABLE(WEBASSEMBLY) #endif // WASMFunctionCompiler_h