diff options
Diffstat (limited to 'Source/JavaScriptCore/wasm/WASMFunctionCompiler.h')
-rw-r--r-- | Source/JavaScriptCore/wasm/WASMFunctionCompiler.h | 1541 |
1 files changed, 1541 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/wasm/WASMFunctionCompiler.h b/Source/JavaScriptCore/wasm/WASMFunctionCompiler.h new file mode 100644 index 000000000..de439ba07 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WASMFunctionCompiler.h @@ -0,0 +1,1541 @@ +/* + * 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<double>(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<WASMType>& 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); + + copyCalleeSavesToVMCalleeSavesBuffer(); + + // 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<int32_t>(immediate)), temporaryAddress(m_tempStackTop++)); + return UNUSED; + } + + int buildImmediateF64(double immediate) + { +#if USE(JSVALUE64) + store64(Imm64(bitwise_cast<int64_t>(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<JSFunction*>& 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<int64_t>& cases, Vector<JumpTarget>& 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<WASMType>& 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<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC))); + } + + void callAndUnboxResult(WASMExpressionType returnType) + { + // regT0 holds callee. +#if USE(JSVALUE64) + store64(GPRInfo::regT0, Address(stackPointerRegister, JSStack::Callee * static_cast<int>(sizeof(Register)) - sizeof(CallerFrameAndPC))); +#else + store32(GPRInfo::regT0, Address(stackPointerRegister, JSStack::Callee * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC))); + store32(TrustedImm32(JSValue::CellTag), Address(stackPointerRegister, JSStack::Callee * static_cast<int>(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(), 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<JumpTarget> m_breakTargets; + Vector<JumpTarget> m_continueTargets; + Vector<JumpTarget> m_breakLabelTargets; + Vector<JumpTarget> m_continueLabelTargets; + + Label m_beginLabel; + Jump m_stackOverflow; + JumpList m_divideErrorJumpList; + JumpList m_outOfBoundsErrorJumpList; + JumpList m_exceptionChecks; + + Vector<std::pair<Call, void*>> m_calls; + Vector<CallCompilationInfo> m_callCompilationInfo; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + +#endif // WASMFunctionCompiler_h |