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 | |
47 | namespace JSC { namespace Wasm { |
48 | |
49 | constexpr unsigned numberOfLLIntCalleeSaveRegisters = 4; |
50 | |
51 | using ArgumentLocation = B3::ValueRep; |
52 | enum class CallRole : uint8_t { |
53 | Caller, |
54 | Callee, |
55 | }; |
56 | |
57 | struct 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 | |
84 | class WasmCallingConvention { |
85 | public: |
86 | static constexpr unsigned = 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 | |
98 | private: |
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 | |
128 | public: |
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 | |
169 | class JSCallingConvention { |
170 | public: |
171 | static constexpr unsigned = 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); |
186 | private: |
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 | |
216 | public: |
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 | |
239 | const JSCallingConvention& jsCallingConvention(); |
240 | const WasmCallingConvention& wasmCallingConvention(); |
241 | |
242 | } } // namespace JSC::Wasm |
243 | |
244 | #endif // ENABLE(WEBASSEMBLY) |
245 | |