summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/wasm/WASMFunctionCompiler.h
diff options
context:
space:
mode:
authorKonstantin Tokarev <annulen@yandex.ru>2016-08-25 19:20:41 +0300
committerKonstantin Tokarev <annulen@yandex.ru>2017-02-02 12:30:55 +0000
commit6882a04fb36642862b11efe514251d32070c3d65 (patch)
treeb7959826000b061fd5ccc7512035c7478742f7b0 /Source/JavaScriptCore/wasm/WASMFunctionCompiler.h
parentab6df191029eeeb0b0f16f127d553265659f739e (diff)
downloadqtwebkit-6882a04fb36642862b11efe514251d32070c3d65.tar.gz
Imported QtWebKit TP3 (git b57bc6801f1876c3220d5a4bfea33d620d477443)
Change-Id: I3b1d8a2808782c9f34d50240000e20cb38d3680f Reviewed-by: Konstantin Tokarev <annulen@yandex.ru>
Diffstat (limited to 'Source/JavaScriptCore/wasm/WASMFunctionCompiler.h')
-rw-r--r--Source/JavaScriptCore/wasm/WASMFunctionCompiler.h1541
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