| /* | 
 |  * Copyright (C) 2016-2018 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 "WasmFormat.h" | 
 | #include "WasmTypeDefinition.h" | 
 | #include "WasmValueLocation.h" | 
 |  | 
 | namespace JSC { namespace Wasm { | 
 |  | 
 | constexpr unsigned numberOfLLIntCalleeSaveRegisters = 2; | 
 |  | 
 | using ArgumentLocation = ValueLocation; | 
 | enum class CallRole : uint8_t { | 
 |     Caller, | 
 |     Callee, | 
 | }; | 
 |  | 
 | struct CallInformation { | 
 |     CallInformation(Vector<ArgumentLocation>&& parameters, Vector<ArgumentLocation, 1>&& returnValues, size_t stackOffset) | 
 |         : params(WTFMove(parameters)) | 
 |         , results(WTFMove(returnValues)) | 
 |         , headerAndArgumentStackSizeInBytes(stackOffset) | 
 |     { } | 
 |  | 
 |     RegisterAtOffsetList computeResultsOffsetList() | 
 |     { | 
 |         RegisterSet usedResultRegisters; | 
 |         for (ValueLocation loc : results) { | 
 |             if (loc.isGPR()) { | 
 |                 usedResultRegisters.set(loc.jsr().payloadGPR()); | 
 | #if USE(JSVALUE32_64) | 
 |                 usedResultRegisters.set(loc.jsr().tagGPR()); | 
 | #endif | 
 |             } else if (loc.isFPR()) | 
 |                 usedResultRegisters.set(loc.fpr()); | 
 |         } | 
 |  | 
 |         RegisterAtOffsetList savedRegs(usedResultRegisters, RegisterAtOffsetList::ZeroBased); | 
 |         return savedRegs; | 
 |     } | 
 |  | 
 |     bool argumentsIncludeI64 { false }; | 
 |     bool resultsIncludeI64 { false }; | 
 |     Vector<ArgumentLocation> params; | 
 |     Vector<ArgumentLocation, 1> results; | 
 |     // As a callee this includes CallerFrameAndPC as a caller it does not. | 
 |     size_t headerAndArgumentStackSizeInBytes; | 
 | }; | 
 |  | 
 | class WasmCallingConvention { | 
 | public: | 
 |     static constexpr unsigned headerSizeInBytes = CallFrame::headerSizeInRegisters * sizeof(Register); | 
 |  | 
 |     WasmCallingConvention(Vector<JSValueRegs>&& jsrs, Vector<FPRReg>&& fprs, Vector<GPRReg>&& scratches, RegisterSet&& calleeSaves, RegisterSet&& callerSaves) | 
 |         : jsrArgs(WTFMove(jsrs)) | 
 |         , fprArgs(WTFMove(fprs)) | 
 |         , prologueScratchGPRs(WTFMove(scratches)) | 
 |         , calleeSaveRegisters(WTFMove(calleeSaves)) | 
 |         , callerSaveRegisters(WTFMove(callerSaves)) | 
 |     { } | 
 |  | 
 |     WTF_MAKE_NONCOPYABLE(WasmCallingConvention); | 
 |  | 
 | 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 { regArgs[count++] }; | 
 |  | 
 |         count++; | 
 |         ArgumentLocation result = role == CallRole::Caller ? ArgumentLocation::stackArgument(stackOffset) : ArgumentLocation::stack(stackOffset); | 
 |         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::Externref: | 
 |         case TypeKind::Ref: | 
 |         case TypeKind::RefNull: | 
 |         case TypeKind::Rtt: | 
 |             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& type, CallRole role = CallRole::Caller) const | 
 |     { | 
 |         const auto& signature = *type.as<FunctionSignature>(); | 
 |         bool argumentsIncludeI64 = false; | 
 |         bool resultsIncludeI64 = false; | 
 |         size_t gpArgumentCount = 0; | 
 |         size_t fpArgumentCount = 0; | 
 |         size_t argStackOffset = headerSizeInBytes + sizeof(Register); | 
 |         if (role == CallRole::Caller) | 
 |             argStackOffset -= sizeof(CallerFrameAndPC); | 
 |  | 
 |         Vector<ArgumentLocation> params(signature.argumentCount()); | 
 |         for (size_t i = 0; i < signature.argumentCount(); ++i) { | 
 |             argumentsIncludeI64 |= signature.argumentType(i).isI64(); | 
 |             params[i] = marshallLocation(role, signature.argumentType(i), gpArgumentCount, fpArgumentCount, argStackOffset); | 
 |         } | 
 |         gpArgumentCount = 0; | 
 |         fpArgumentCount = 0; | 
 |         size_t resultStackOffset = headerSizeInBytes + sizeof(Register); | 
 |         if (role == CallRole::Caller) | 
 |             resultStackOffset -= sizeof(CallerFrameAndPC); | 
 |  | 
 |         Vector<ArgumentLocation, 1> results(signature.returnCount()); | 
 |         for (size_t i = 0; i < signature.returnCount(); ++i) { | 
 |             resultsIncludeI64 |= signature.returnType(i).isI64(); | 
 |             results[i] = marshallLocation(role, signature.returnType(i), gpArgumentCount, fpArgumentCount, resultStackOffset); | 
 |         } | 
 |  | 
 |         CallInformation result(WTFMove(params), WTFMove(results), std::max(argStackOffset, resultStackOffset)); | 
 |         result.argumentsIncludeI64 = argumentsIncludeI64; | 
 |         result.resultsIncludeI64 = resultsIncludeI64; | 
 |         return result; | 
 |     } | 
 |  | 
 |     const Vector<JSValueRegs> jsrArgs; | 
 |     const Vector<FPRReg> fprArgs; | 
 |     const Vector<GPRReg> prologueScratchGPRs; | 
 |     const RegisterSet calleeSaveRegisters; | 
 |     const RegisterSet callerSaveRegisters; | 
 | }; | 
 |  | 
 | class JSCallingConvention { | 
 | public: | 
 |     static constexpr unsigned headerSizeInBytes = CallFrame::headerSizeInRegisters * sizeof(Register); | 
 |  | 
 |     // vmEntryToWasm passes the JSWebAssemblyInstance corresponding to Wasm::Context*'s | 
 |     // instance as the first JS argument when we're not using fast TLS to hold the | 
 |     // Wasm::Context*'s instance. | 
 |     static constexpr ptrdiff_t instanceStackOffset = CallFrameSlot::thisArgument * sizeof(EncodedJSValue); | 
 |  | 
 |     JSCallingConvention(Vector<JSValueRegs>&& gprs, Vector<FPRReg>&& fprs, RegisterSet&& calleeSaves, RegisterSet&& callerSaves) | 
 |         : jsrArgs(WTFMove(gprs)) | 
 |         , fprArgs(WTFMove(fprs)) | 
 |         , calleeSaveRegisters(WTFMove(calleeSaves)) | 
 |         , callerSaveRegisters(WTFMove(callerSaves)) | 
 |     { } | 
 |  | 
 |     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 { regArgs[count++] }; | 
 |  | 
 |         count++; | 
 |         ArgumentLocation result = role == CallRole::Caller ? ArgumentLocation::stackArgument(stackOffset) : ArgumentLocation::stack(stackOffset); | 
 |         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::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 | 
 |     { | 
 |         size_t gpArgumentCount = 0; | 
 |         size_t fpArgumentCount = 0; | 
 |         size_t stackOffset = headerSizeInBytes + sizeof(Register); // Skip the this value since wasm doesn't use it and we sometimes put the context there. | 
 |         if (role == CallRole::Caller) | 
 |             stackOffset -= sizeof(CallerFrameAndPC); | 
 |  | 
 |         Vector<ArgumentLocation> params; | 
 |         for (size_t i = 0; i < signature.as<FunctionSignature>()->argumentCount(); ++i) | 
 |             params.append(marshallLocation(role, signature.as<FunctionSignature>()->argumentType(i), gpArgumentCount, fpArgumentCount, stackOffset)); | 
 |  | 
 |         Vector<ArgumentLocation, 1> results { ArgumentLocation { JSRInfo::returnValueJSR } }; | 
 |         return CallInformation(WTFMove(params), WTFMove(results), stackOffset); | 
 |     } | 
 |  | 
 |     const Vector<JSValueRegs> jsrArgs; | 
 |     const Vector<FPRReg> fprArgs; | 
 |     const RegisterSet calleeSaveRegisters; | 
 |     const RegisterSet callerSaveRegisters; | 
 | }; | 
 |  | 
 | const JSCallingConvention& jsCallingConvention(); | 
 | const WasmCallingConvention& wasmCallingConvention(); | 
 |  | 
 | } } // namespace JSC::Wasm | 
 |  | 
 | #endif // ENABLE(WEBASSEMBLY) |