1/*
2 * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#if ENABLE(WEBASSEMBLY)
29
30#include "AirCode.h"
31#include "AllowMacroScratchRegisterUsage.h"
32#include "B3ArgumentRegValue.h"
33#include "B3BasicBlock.h"
34#include "B3Const64Value.h"
35#include "B3ConstrainedValue.h"
36#include "B3MemoryValue.h"
37#include "B3PatchpointValue.h"
38#include "B3Procedure.h"
39#include "B3StackmapGenerationParams.h"
40#include "CallFrame.h"
41#include "LinkBuffer.h"
42#include "RegisterAtOffsetList.h"
43#include "RegisterSet.h"
44#include "WasmFormat.h"
45#include "WasmSignature.h"
46
47namespace JSC { namespace Wasm {
48
49constexpr unsigned numberOfLLIntCalleeSaveRegisters = 4;
50
51using ArgumentLocation = B3::ValueRep;
52enum class CallRole : uint8_t {
53 Caller,
54 Callee,
55};
56
57struct CallInformation {
58 CallInformation(Vector<ArgumentLocation>&& parameters, Vector<ArgumentLocation, 1>&& returnValues, size_t stackOffset)
59 : params(WTFMove(parameters))
60 , results(WTFMove(returnValues))
61 , headerAndArgumentStackSizeInBytes(stackOffset)
62 { }
63
64 RegisterAtOffsetList computeResultsOffsetList()
65 {
66 RegisterSet usedResultRegisters;
67 for (B3::ValueRep rep : results) {
68 if (rep.isReg())
69 usedResultRegisters.set(rep.reg());
70 }
71
72 RegisterAtOffsetList savedRegs(usedResultRegisters, RegisterAtOffsetList::ZeroBased);
73 return savedRegs;
74 }
75
76 bool argumentsIncludeI64 { false };
77 bool resultsIncludeI64 { false };
78 Vector<ArgumentLocation> params;
79 Vector<ArgumentLocation, 1> results;
80 // As a callee this includes CallerFrameAndPC as a caller it does not.
81 size_t headerAndArgumentStackSizeInBytes;
82};
83
84class WasmCallingConvention {
85public:
86 static constexpr unsigned headerSizeInBytes = CallFrame::headerSizeInRegisters * sizeof(Register);
87
88 WasmCallingConvention(Vector<Reg>&& gprs, Vector<Reg>&& fprs, Vector<GPRReg>&& scratches, RegisterSet&& calleeSaves, RegisterSet&& callerSaves)
89 : gprArgs(WTFMove(gprs))
90 , fprArgs(WTFMove(fprs))
91 , prologueScratchGPRs(WTFMove(scratches))
92 , calleeSaveRegisters(WTFMove(calleeSaves))
93 , callerSaveRegisters(WTFMove(callerSaves))
94 { }
95
96 WTF_MAKE_NONCOPYABLE(WasmCallingConvention);
97
98private:
99 ArgumentLocation marshallLocationImpl(CallRole role, const Vector<Reg>& regArgs, size_t& count, size_t& stackOffset) const
100 {
101 if (count < regArgs.size())
102 return ArgumentLocation::reg(regArgs[count++]);
103
104 count++;
105 ArgumentLocation result = role == CallRole::Caller ? ArgumentLocation::stackArgument(stackOffset) : ArgumentLocation::stack(stackOffset);
106 stackOffset += sizeof(Register);
107 return result;
108 }
109
110 ArgumentLocation marshallLocation(CallRole role, Type valueType, size_t& gpArgumentCount, size_t& fpArgumentCount, size_t& stackOffset) const
111 {
112 ASSERT(isValueType(valueType));
113 switch (valueType) {
114 case I32:
115 case I64:
116 case Funcref:
117 case Anyref:
118 return marshallLocationImpl(role, gprArgs, gpArgumentCount, stackOffset);
119 case F32:
120 case F64:
121 return marshallLocationImpl(role, fprArgs, fpArgumentCount, stackOffset);
122 default:
123 break;
124 }
125 RELEASE_ASSERT_NOT_REACHED();
126 }
127
128public:
129 CallInformation callInformationFor(const Signature& signature, CallRole role = CallRole::Caller) const
130 {
131 bool argumentsIncludeI64 = false;
132 bool resultsIncludeI64 = false;
133 size_t gpArgumentCount = 0;
134 size_t fpArgumentCount = 0;
135 size_t argStackOffset = headerSizeInBytes;
136 if (role == CallRole::Caller)
137 argStackOffset -= sizeof(CallerFrameAndPC);
138
139 Vector<ArgumentLocation> params(signature.argumentCount());
140 for (size_t i = 0; i < signature.argumentCount(); ++i) {
141 argumentsIncludeI64 |= signature.argument(i) == I64;
142 params[i] = marshallLocation(role, signature.argument(i), gpArgumentCount, fpArgumentCount, argStackOffset);
143 }
144 gpArgumentCount = 0;
145 fpArgumentCount = 0;
146 size_t resultStackOffset = headerSizeInBytes;
147 if (role == CallRole::Caller)
148 resultStackOffset -= sizeof(CallerFrameAndPC);
149
150 Vector<ArgumentLocation, 1> results(signature.returnCount());
151 for (size_t i = 0; i < signature.returnCount(); ++i) {
152 resultsIncludeI64 |= signature.returnType(i) == I64;
153 results[i] = marshallLocation(role, signature.returnType(i), gpArgumentCount, fpArgumentCount, resultStackOffset);
154 }
155
156 CallInformation result(WTFMove(params), WTFMove(results), std::max(argStackOffset, resultStackOffset));
157 result.argumentsIncludeI64 = argumentsIncludeI64;
158 result.resultsIncludeI64 = resultsIncludeI64;
159 return result;
160 }
161
162 const Vector<Reg> gprArgs;
163 const Vector<Reg> fprArgs;
164 const Vector<GPRReg> prologueScratchGPRs;
165 const RegisterSet calleeSaveRegisters;
166 const RegisterSet callerSaveRegisters;
167};
168
169class JSCallingConvention {
170public:
171 static constexpr unsigned headerSizeInBytes = CallFrame::headerSizeInRegisters * sizeof(Register);
172
173 // vmEntryToWasm passes the JSWebAssemblyInstance corresponding to Wasm::Context*'s
174 // instance as the first JS argument when we're not using fast TLS to hold the
175 // Wasm::Context*'s instance.
176 static constexpr ptrdiff_t instanceStackOffset = CallFrameSlot::thisArgument * sizeof(EncodedJSValue);
177
178 JSCallingConvention(Vector<Reg>&& gprs, Vector<Reg>&& fprs, RegisterSet&& calleeSaves, RegisterSet&& callerSaves)
179 : gprArgs(WTFMove(gprs))
180 , fprArgs(WTFMove(fprs))
181 , calleeSaveRegisters(WTFMove(calleeSaves))
182 , callerSaveRegisters(WTFMove(callerSaves))
183 { }
184
185 WTF_MAKE_NONCOPYABLE(JSCallingConvention);
186private:
187 ArgumentLocation marshallLocationImpl(CallRole role, const Vector<Reg>& regArgs, size_t& count, size_t& stackOffset) const
188 {
189 if (count < regArgs.size())
190 return ArgumentLocation::reg(regArgs[count++]);
191
192 count++;
193 ArgumentLocation result = role == CallRole::Caller ? ArgumentLocation::stackArgument(stackOffset) : ArgumentLocation::stack(stackOffset);
194 stackOffset += sizeof(Register);
195 return result;
196 }
197
198 ArgumentLocation marshallLocation(CallRole role, Type valueType, size_t& gpArgumentCount, size_t& fpArgumentCount, size_t& stackOffset) const
199 {
200 ASSERT(isValueType(valueType));
201 switch (valueType) {
202 case I32:
203 case I64:
204 case Funcref:
205 case Anyref:
206 return marshallLocationImpl(role, gprArgs, gpArgumentCount, stackOffset);
207 case F32:
208 case F64:
209 return marshallLocationImpl(role, fprArgs, fpArgumentCount, stackOffset);
210 default:
211 break;
212 }
213 RELEASE_ASSERT_NOT_REACHED();
214 }
215
216public:
217 CallInformation callInformationFor(const Signature& signature, CallRole role = CallRole::Callee) const
218 {
219 size_t gpArgumentCount = 0;
220 size_t fpArgumentCount = 0;
221 size_t stackOffset = headerSizeInBytes + sizeof(Register); // Skip the this value since wasm doesn't use it and we sometimes put the context there.
222 if (role == CallRole::Caller)
223 stackOffset -= sizeof(CallerFrameAndPC);
224
225 Vector<ArgumentLocation> params;
226 for (size_t i = 0; i < signature.argumentCount(); ++i)
227 params.append(marshallLocation(role, signature.argument(i), gpArgumentCount, fpArgumentCount, stackOffset));
228
229 Vector<ArgumentLocation, 1> results { ArgumentLocation::reg(GPRInfo::returnValueGPR) };
230 return CallInformation(WTFMove(params), WTFMove(results), stackOffset);
231 }
232
233 const Vector<Reg> gprArgs;
234 const Vector<Reg> fprArgs;
235 const RegisterSet calleeSaveRegisters;
236 const RegisterSet callerSaveRegisters;
237};
238
239const JSCallingConvention& jsCallingConvention();
240const WasmCallingConvention& wasmCallingConvention();
241
242} } // namespace JSC::Wasm
243
244#endif // ENABLE(WEBASSEMBLY)
245