/*
 * Copyright (C) 2016-2024 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.
 */

#pragma once

#if ENABLE(WEBASSEMBLY)

#include "AllowMacroScratchRegisterUsage.h"
#include "CallFrame.h"
#include "LinkBuffer.h"
#include "RegisterAtOffsetList.h"
#include "RegisterSet.h"
#include "StackAlignment.h"
#include "WasmFormat.h"
#include "WasmTypeDefinition.h"
#include "WasmTypeDefinitionInlines.h"
#include "WasmValueLocation.h"

namespace JSC { namespace Wasm {

constexpr unsigned numberOfLLIntCalleeSaveRegisters = 2;
#if CPU(ARM)
constexpr unsigned numberOfIPIntCalleeSaveRegisters = 2;
#else
constexpr unsigned numberOfIPIntCalleeSaveRegisters = 3;
#endif
constexpr unsigned numberOfLLIntInternalRegisters = 2;
constexpr unsigned numberOfIPIntInternalRegisters = 2;
constexpr ptrdiff_t WasmToJSScratchSpaceSize = 0x8 * 1 + 0x8; // Needs to be aligned to 0x10.
constexpr ptrdiff_t WasmToJSCallableFunctionSlot = -0x8;

struct ArgumentLocation {
#if USE(JSVALUE32_64)
    ArgumentLocation(ValueLocation loc, Width width, Width usedWidth)
        : location(loc)
        , width(width)
        , usedWidth(usedWidth)
    {
    }
    ArgumentLocation(ValueLocation loc, Width width)
        : location(loc)
        , width(width)
        , usedWidth(width)
    {
    }
#else
    ArgumentLocation(ValueLocation loc, Width width)
        : location(loc)
        , width(width)
    {
    }
#endif // USE(JSVALUE32_64)

    ArgumentLocation() {}

    ValueLocation location;
    Width width;
#if USE(JSVALUE32_64)
    Width usedWidth;
#endif
};

enum class CallRole : uint8_t {
    Caller,
    Callee,
};

struct CallInformation {
    CallInformation() = default;
    CallInformation(ArgumentLocation passedThisArgument, Vector<ArgumentLocation, 8>&& parameters, Vector<ArgumentLocation, 1>&& returnValues, size_t stackOffset, size_t stackValues)
        : thisArgument(passedThisArgument)
        , params(WTFMove(parameters))
        , results(WTFMove(returnValues))
        , headerAndArgumentStackSizeInBytes(stackOffset)
        , numberOfStackValues(stackValues)
    { }

    RegisterAtOffsetList computeResultsOffsetList()
    {
        RegisterSet usedResultRegisters;
        for (auto loc : results) {
            if (loc.location.isGPR()) {
                usedResultRegisters.add(loc.location.jsr().payloadGPR(), IgnoreVectors);
#if USE(JSVALUE32_64)
                usedResultRegisters.add(loc.location.jsr().tagGPR(), IgnoreVectors);
#endif
            } else if (loc.location.isFPR())
                usedResultRegisters.add(loc.location.fpr(), loc.width);
        }

        RegisterAtOffsetList savedRegs(usedResultRegisters, RegisterAtOffsetList::ZeroBased);
        return savedRegs;
    }

    ArgumentLocation thisArgument { };
    Vector<ArgumentLocation, 8> params { };
    Vector<ArgumentLocation, 1> results { };
    // As a callee this includes CallerFrameAndPC as a caller it does not.
    size_t headerAndArgumentStackSizeInBytes { 0 };
    size_t numberOfStackValues { 0 };
};

class WasmCallingConvention {
public:
    static constexpr unsigned headerSizeInBytes = CallFrame::headerSizeInRegisters * sizeof(Register);

    WasmCallingConvention(Vector<JSValueRegs>&& jsrs, Vector<FPRReg>&& fprs, Vector<GPRReg>&& scratches, RegisterSetBuilder&& calleeSaves)
        : jsrArgs(WTFMove(jsrs))
        , fprArgs(WTFMove(fprs))
        , prologueScratchGPRs(WTFMove(scratches))
        , calleeSaveRegisters(calleeSaves.buildAndValidate())
    { }

    WTF_MAKE_NONCOPYABLE(WasmCallingConvention);

private:
    template <typename RegType>
    ArgumentLocation marshallRegs(const Vector<RegType>& regArgs, size_t& count, size_t valueSize, Width width) const
    {
        if constexpr (std::is_same<RegType, JSValueRegs>::value) {
            JSValueRegs jsr = regArgs[count++];
#if USE(JSVALUE32_64)
                if (valueSize == 4)
                    return ArgumentLocation { ValueLocation { jsr }, width , Width32 };
#else
                UNUSED_PARAM(valueSize);
#endif
            return ArgumentLocation { ValueLocation { jsr }, width };
        } else
            return ArgumentLocation { ValueLocation { regArgs[count++] }, width };
    }

    template<typename RegType>
    ArgumentLocation marshallLocationImpl(CallRole role, const Vector<RegType>& regArgs, size_t& count, size_t& stackOffset, size_t valueSize) const
    {
        size_t alignedSize = WTF::roundUpToMultipleOf(valueSize, sizeof(Register));
        Width width = widthForBytes(alignedSize);

        if (count < regArgs.size())
            return marshallRegs(regArgs, count, valueSize, width);

        count++;
        ArgumentLocation result = { role == CallRole::Caller ? ValueLocation::stackArgument(stackOffset) : ValueLocation::stack(stackOffset), width };
        stackOffset += alignedSize;
        return result;
    }

    ArgumentLocation marshallLocation(CallRole role, Type valueType, size_t& gpArgumentCount, size_t& fpArgumentCount, size_t& stackOffset) const
    {
        ASSERT(isValueType(valueType));
        unsigned valueSize = bytesForWidth(valueType.width());
        switch (valueType.kind) {
        case TypeKind::I32:
        case TypeKind::I64:
        case TypeKind::Funcref:
        case TypeKind::Exn:
        case TypeKind::Externref:
        case TypeKind::Ref:
        case TypeKind::RefNull:
            return marshallLocationImpl(role, jsrArgs, gpArgumentCount, stackOffset, valueSize);
        case TypeKind::F32:
        case TypeKind::F64:
        case TypeKind::V128:
            return marshallLocationImpl(role, fprArgs, fpArgumentCount, stackOffset, valueSize);
        default:
            break;
        }
        RELEASE_ASSERT_NOT_REACHED();
    }

public:
    uint32_t numberOfStackResults(const FunctionSignature& signature) const
    {
        const uint32_t gprCount = jsrArgs.size();
        const uint32_t fprCount = fprArgs.size();
        uint32_t gprIndex = 0;
        uint32_t fprIndex = 0;
        uint32_t stackCount = 0;
        for (uint32_t i = 0; i < signature.returnCount(); i++) {
            switch (signature.returnType(i).kind) {
            case TypeKind::I32:
            case TypeKind::I64:
            case TypeKind::Exn:
            case TypeKind::Externref:
            case TypeKind::Funcref:
            case TypeKind::RefNull:
            case TypeKind::Ref:
                if (gprIndex < gprCount)
                    ++gprIndex;
                else
                    ++stackCount;
                break;
            case TypeKind::F32:
            case TypeKind::F64:
            case TypeKind::V128:
                if (fprIndex < fprCount)
                    ++fprIndex;
                else
                    ++stackCount;
                break;
            case TypeKind::Void:
            case TypeKind::Func:
            case TypeKind::Struct:
            case TypeKind::Structref:
            case TypeKind::Array:
            case TypeKind::Arrayref:
            case TypeKind::Eqref:
            case TypeKind::Anyref:
            case TypeKind::Nullexn:
            case TypeKind::Nullref:
            case TypeKind::Nullfuncref:
            case TypeKind::Nullexternref:
            case TypeKind::I31ref:
            case TypeKind::Sub:
            case TypeKind::Subfinal:
            case TypeKind::Rec:
                RELEASE_ASSERT_NOT_REACHED();
            }
        }
        return stackCount;
    }

    uint32_t numberOfStackArguments(const FunctionSignature& signature) const
    {
        const uint32_t gprCount = jsrArgs.size();
        const uint32_t fprCount = fprArgs.size();
        uint32_t gprIndex = 0;
        uint32_t fprIndex = 0;
        uint32_t stackCount = 0;
        for (uint32_t i = 0; i < signature.argumentCount(); i++) {
            switch (signature.argumentType(i).kind) {
            case TypeKind::I32:
            case TypeKind::I64:
            case TypeKind::Exn:
            case TypeKind::Externref:
            case TypeKind::Funcref:
            case TypeKind::RefNull:
            case TypeKind::Ref:
                if (gprIndex < gprCount)
                    ++gprIndex;
                else
                    ++stackCount;
                break;
            case TypeKind::F32:
            case TypeKind::F64:
            case TypeKind::V128:
                if (fprIndex < fprCount)
                    ++fprIndex;
                else
                    ++stackCount;
                break;
            case TypeKind::Void:
            case TypeKind::Func:
            case TypeKind::Struct:
            case TypeKind::Structref:
            case TypeKind::Array:
            case TypeKind::Arrayref:
            case TypeKind::Eqref:
            case TypeKind::Anyref:
            case TypeKind::Nullexn:
            case TypeKind::Nullref:
            case TypeKind::Nullfuncref:
            case TypeKind::Nullexternref:
            case TypeKind::I31ref:
            case TypeKind::Sub:
            case TypeKind::Subfinal:
            case TypeKind::Rec:
                RELEASE_ASSERT_NOT_REACHED();
            }
        }
        return stackCount;
    }

    uint32_t numberOfStackValues(const FunctionSignature& signature) const
    {
        return std::max(numberOfStackArguments(signature), numberOfStackResults(signature));
    }

    CallInformation callInformationFor(const TypeDefinition& type, CallRole role = CallRole::Caller) const
    {
        const auto& signature = *type.as<FunctionSignature>();
        return callInformationFor(signature, role);
    }

    CallInformation callInformationFor(const FunctionSignature& signature, CallRole role = CallRole::Caller) const
    {
        size_t gpArgumentCount = 0;
        size_t fpArgumentCount = 0;
        size_t headerSize = headerSizeInBytes;
        if (role == CallRole::Caller)
            headerSize -= sizeof(CallerFrameAndPC);

        ArgumentLocation thisArgument = { role == CallRole::Caller ? ValueLocation::stackArgument(headerSize) : ValueLocation::stack(headerSize), widthForBytes(sizeof(void*)) };
        headerSize += sizeof(Register);

        size_t argStackOffset = headerSize;
        Vector<ArgumentLocation, 8> params(signature.argumentCount(),
            [&](unsigned index) {
                return marshallLocation(role, signature.argumentType(index), gpArgumentCount, fpArgumentCount, argStackOffset);
            });
        uint32_t stackArgs = argStackOffset - headerSize;
        size_t stackArguments = 0;
        if (gpArgumentCount > jsrArgs.size())
            stackArguments += (gpArgumentCount - jsrArgs.size());
        if (fpArgumentCount > fprArgs.size())
            stackArguments += (fpArgumentCount - fprArgs.size());
        ASSERT(stackArguments == numberOfStackArguments(signature));

        gpArgumentCount = 0;
        fpArgumentCount = 0;
        size_t stackResults = numberOfStackResults(signature);
        uint32_t stackResultsInBytes = stackResults * sizeof(Register);
        uint32_t stackCountAligned = WTF::roundUpToMultipleOf<stackAlignmentBytes()>(std::max(stackArgs, stackResultsInBytes));
        size_t resultStackOffset = headerSize + stackCountAligned - stackResultsInBytes;
        Vector<ArgumentLocation, 1> results(signature.returnCount(),
            [&](unsigned index) {
                return marshallLocation(role, signature.returnType(index), gpArgumentCount, fpArgumentCount, resultStackOffset);
            });

        return { thisArgument, WTFMove(params), WTFMove(results), std::max(argStackOffset, resultStackOffset), std::max(stackArguments, stackResults) };
    }

    RegisterSet argumentGPRs() const { return RegisterSetBuilder::argumentGPRs(); }

    const Vector<JSValueRegs> jsrArgs;
    const Vector<FPRReg> fprArgs;
    const Vector<GPRReg> prologueScratchGPRs;
    const RegisterSet calleeSaveRegisters;
};

class JSCallingConvention {
public:
    static constexpr unsigned headerSizeInBytes = CallFrame::headerSizeInRegisters * sizeof(Register);

    JSCallingConvention(Vector<JSValueRegs>&& gprs, Vector<FPRReg>&& fprs, RegisterSetBuilder&& calleeSaves)
        : jsrArgs(WTFMove(gprs))
        , fprArgs(WTFMove(fprs))
        , calleeSaveRegisters(calleeSaves.buildAndValidate())
    { }

    WTF_MAKE_NONCOPYABLE(JSCallingConvention);
private:
    template <typename RegType>
    ArgumentLocation marshallLocationImpl(CallRole role, const Vector<RegType>& regArgs, size_t& count, size_t& stackOffset) const
    {
        if (count < regArgs.size())
            return ArgumentLocation { ValueLocation { regArgs[count++] }, Width64 };

        count++;
        ArgumentLocation result = { role == CallRole::Caller ? ValueLocation::stackArgument(stackOffset) : ValueLocation::stack(stackOffset), Width64 };
        stackOffset += sizeof(Register);
        return result;
    }

    ArgumentLocation marshallLocation(CallRole role, Type valueType, size_t& gpArgumentCount, size_t& fpArgumentCount, size_t& stackOffset) const
    {
        ASSERT(isValueType(valueType));
        switch (valueType.kind) {
        case TypeKind::I32:
        case TypeKind::I64:
        case TypeKind::Funcref:
        case TypeKind::Exn:
        case TypeKind::Externref:
        case TypeKind::Ref:
        case TypeKind::RefNull:
            return marshallLocationImpl(role, jsrArgs, gpArgumentCount, stackOffset);
        case TypeKind::F32:
        case TypeKind::F64:
            return marshallLocationImpl(role, fprArgs, fpArgumentCount, stackOffset);
        default:
            break;
        }
        RELEASE_ASSERT_NOT_REACHED();
    }

public:
    CallInformation callInformationFor(const TypeDefinition& signature, CallRole role = CallRole::Callee) const { return callInformationFor(*signature.as<FunctionSignature>(), role); }
    CallInformation callInformationFor(const FunctionSignature& signature, CallRole role = CallRole::Callee) const
    {
        size_t gpArgumentCount = 0;
        size_t fpArgumentCount = 0;
        size_t stackOffset = headerSizeInBytes;
        if (role == CallRole::Caller)
            stackOffset -= sizeof(CallerFrameAndPC);

        ArgumentLocation thisArgument = { role == CallRole::Caller ? ValueLocation::stackArgument(stackOffset) : ValueLocation::stack(stackOffset), widthForBytes(sizeof(void*)) };
        stackOffset += sizeof(Register);

        Vector<ArgumentLocation, 8> params(signature.argumentCount(),
            [&](unsigned index) {
                return marshallLocation(role, signature.argumentType(index), gpArgumentCount, fpArgumentCount, stackOffset);
            });
        Vector<ArgumentLocation, 1> results { ArgumentLocation { ValueLocation { JSRInfo::returnValueJSR }, Width64 } };
        return { thisArgument, WTFMove(params), WTFMove(results), stackOffset, 0U };
    }

    const Vector<JSValueRegs> jsrArgs;
    const Vector<FPRReg> fprArgs;
    const RegisterSet calleeSaveRegisters;
};

const JSCallingConvention& jsCallingConvention();
const WasmCallingConvention& wasmCallingConvention();

#if CPU(ARM_THUMB2)

class CCallingConventionArmThumb2 {
public:
    static constexpr unsigned headerSizeInBytes = 0;

    CCallingConventionArmThumb2(Vector<GPRReg>&& gprs, Vector<FPRReg>&& fprs, Vector<GPRReg>&& scratches, RegisterSetBuilder&& calleeSaves)
        : gprArgs(WTFMove(gprs))
        , fprArgs(WTFMove(fprs))
        , prologueScratchGPRs(WTFMove(scratches))
        , calleeSaveRegisters(calleeSaves.buildAndValidate())
    { }

    WTF_MAKE_NONCOPYABLE(CCallingConventionArmThumb2);

private:
    ArgumentLocation marshallLocationImplGPReg(CallRole role, const Vector<GPRReg>& regArgs, size_t& count, size_t& stackOffset, size_t valueSize) const
    {
        if (count < regArgs.size())
            return ArgumentLocation { ValueLocation { JSValueRegs::payloadOnly(regArgs[count++]) }, widthForBytes(valueSize) };

        count++;
        ArgumentLocation result = { role == CallRole::Caller ? ValueLocation::stackArgument(stackOffset) : ValueLocation::stack(stackOffset), widthForBytes(valueSize) };
        stackOffset += valueSize;
        return result;
    }
    ArgumentLocation marshallLocationImplGPRegPair(CallRole role, const Vector<GPRReg>& regArgs, size_t& count, size_t& stackOffset, size_t valueSize) const
    {
        count = WTF::roundUpToMultipleOf(2, count);
        if (count+1 < regArgs.size()) {
            auto payloadReg = regArgs[count];
            auto tagReg = regArgs[count+1];
            count += 2;
            return ArgumentLocation { ValueLocation { JSValueRegs::withTwoAvailableRegs(tagReg, payloadReg) }, widthForBytes(valueSize) };
        }

        count += 2;
        stackOffset = WTF::roundUpToMultipleOf(valueSize, stackOffset);
        ArgumentLocation result = { role == CallRole::Caller ? ValueLocation::stackArgument(stackOffset) : ValueLocation::stack(stackOffset), widthForBytes(valueSize) };
        stackOffset += valueSize;
        return result;
    }
    ArgumentLocation marshallLocationImplFPReg(CallRole role, const Vector<FPRReg>& regArgs, size_t& count, size_t& stackOffset, size_t valueSize) const
    {
        if (count < regArgs.size())
            return ArgumentLocation { ValueLocation { regArgs[count++] }, widthForBytes(valueSize) };

        count++;
        stackOffset = WTF::roundUpToMultipleOf(valueSize, stackOffset);
        ArgumentLocation result = { role == CallRole::Caller ? ValueLocation::stackArgument(stackOffset) : ValueLocation::stack(stackOffset), widthForBytes(valueSize) };
        stackOffset += valueSize;
        return result;
    }

    ArgumentLocation marshallLocation(CallRole role, Type valueType, size_t& gpArgumentCount, size_t& fpArgumentCount, size_t& stackOffset) const
    {
        ASSERT(isValueType(valueType));
        unsigned alignedWidth = bytesForWidth(valueType.width());
        switch (valueType.kind) {
        case TypeKind::I64:
        case TypeKind::Funcref:
        case TypeKind::Exn:
        case TypeKind::Externref:
        case TypeKind::RefNull:
        case TypeKind::Ref:
            return marshallLocationImplGPRegPair(role, gprArgs, gpArgumentCount, stackOffset, alignedWidth);
        case TypeKind::I32:
            return marshallLocationImplGPReg(role, gprArgs, gpArgumentCount, stackOffset, alignedWidth);
        case TypeKind::F32:
        case TypeKind::F64:
            return marshallLocationImplFPReg(role, fprArgs, fpArgumentCount, stackOffset, alignedWidth);
        default:
            break;
        }
        RELEASE_ASSERT_NOT_REACHED();
    }

public:
    uint32_t numberOfStackResults(const FunctionSignature& signature) const
    {
        const uint32_t gprCount = gprArgs.size();
        const uint32_t fprCount = fprArgs.size();
        uint32_t gprIndex = 0;
        uint32_t fprIndex = 0;
        uint32_t stackCount = 0;
        for (uint32_t i = 0; i < signature.returnCount(); i++) {
            switch (signature.returnType(i).kind) {
            case TypeKind::I64:
            case TypeKind::Funcref:
            case TypeKind::Exn:
            case TypeKind::Externref:
            case TypeKind::RefNull:
            case TypeKind::Ref:
                if (gprIndex < gprCount)
                    gprIndex += 2;
                else
                    stackCount += 2;
                break;
            case TypeKind::I32:
                if (gprIndex < gprCount)
                    ++gprIndex;
                else
                    ++stackCount;
                break;
            case TypeKind::F32:
            case TypeKind::F64:
                if (fprIndex < fprCount)
                    ++fprIndex;
                else
                    ++stackCount;
                break;
            default:
                RELEASE_ASSERT_NOT_REACHED();
                break;
            }
        }
        return stackCount;
    }

    uint32_t numberOfStackArguments(const FunctionSignature& signature) const
    {
        const uint32_t gprCount = gprArgs.size();
        const uint32_t fprCount = fprArgs.size();
        uint32_t gprIndex = 0;
        uint32_t fprIndex = 0;
        uint32_t stackCount = 0;
        for (uint32_t i = 0; i < signature.argumentCount(); i++) {
            switch (signature.argumentType(i).kind) {
            case TypeKind::I64:
            case TypeKind::Funcref:
            case TypeKind::Exn:
            case TypeKind::Externref:
            case TypeKind::RefNull:
            case TypeKind::Ref:
                gprIndex = WTF::roundUpToMultipleOf(2, gprIndex);
                stackCount = WTF::roundUpToMultipleOf(2, stackCount);
                if (gprIndex < gprCount)
                    gprIndex += 2;
                else
                    stackCount += 2;
                break;
            case TypeKind::I32:
                if (gprIndex < gprCount)
                    ++gprIndex;
                else
                    ++stackCount;
                break;
            case TypeKind::F32:
            case TypeKind::F64:
                if (fprIndex < fprCount)
                    ++fprIndex;
                else
                    ++stackCount;
                break;
            default:
                RELEASE_ASSERT_NOT_REACHED();
                break;
            }
        }
        return stackCount;
    }

    CallInformation callInformationFor(const TypeDefinition& type, CallRole role = CallRole::Caller) const
    {
        const auto& signature = *type.as<FunctionSignature>();
        size_t gpArgumentCount = 0;
        size_t fpArgumentCount = 0;
        size_t headerSize = headerSizeInBytes;
        if (role == CallRole::Caller)
            headerSize -= sizeof(CallerFrameAndPC);

        ArgumentLocation thisArgument = { role == CallRole::Caller ? ValueLocation::stackArgument(headerSize) : ValueLocation::stack(headerSize), widthForBytes(sizeof(void*)) };
        headerSize += sizeof(Register);

        size_t argStackOffset = headerSize;
        Vector<ArgumentLocation, 8> params(signature.argumentCount(),
            [&](unsigned index) {
                auto argumentType = signature.argumentType(index);
                ASSERT(!argumentType.isV128());
                return marshallLocation(role, argumentType, gpArgumentCount, fpArgumentCount, argStackOffset);
            });
        uint32_t stackArgs = argStackOffset - headerSize;
        size_t stackArgsCount = numberOfStackArguments(signature);

        gpArgumentCount = 0;
        fpArgumentCount = 0;
        size_t stackResultsCount = numberOfStackResults(signature);
        uint32_t stackResults = stackResultsCount * sizeof(Register);
        uint32_t stackCountAligned = WTF::roundUpToMultipleOf<stackAlignmentBytes()>(std::max(stackArgs, stackResults));
        size_t resultStackOffset = headerSize + stackCountAligned - stackResults;
        Vector<ArgumentLocation, 1> results(signature.returnCount(),
            [&](unsigned index) {
                ASSERT(!signature.returnType(index).isV128());
                return marshallLocation(role, signature.returnType(index), gpArgumentCount, fpArgumentCount, resultStackOffset);
            });
        return { thisArgument, WTFMove(params), WTFMove(results), std::max(argStackOffset, resultStackOffset), std::max(stackArgsCount, stackResultsCount) };
    }

    const Vector<GPRReg> gprArgs;
    const Vector<FPRReg> fprArgs;
    const Vector<GPRReg> prologueScratchGPRs;
    const RegisterSet calleeSaveRegisters;
};

const CCallingConventionArmThumb2& cCallingConventionArmThumb2();
#endif

} } // namespace JSC::Wasm

namespace WTF {

template<>
struct VectorTraits<JSC::Wasm::ArgumentLocation> : VectorTraitsBase<false, JSC::Wasm::ValueLocation> {
    static constexpr bool canInitializeWithMemset = true;
    static constexpr bool canMoveWithMemcpy = true;
    static constexpr bool canCopyWithMemcpy = true;
};

} // namespace WTF

#endif // ENABLE(WEBASSEMBLY)
