[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / JavaScriptCore / wasm / js / JSToWasm.cpp
1 /*
2  * Copyright (C) 2016-2019 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 #include "config.h"
27 #include "JSToWasm.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "CCallHelpers.h"
32 #include "DisallowMacroScratchRegisterUsage.h"
33 #include "JSCInlines.h"
34 #include "JSWebAssemblyHelpers.h"
35 #include "JSWebAssemblyInstance.h"
36 #include "JSWebAssemblyRuntimeError.h"
37 #include "MaxFrameExtentForSlowPathCall.h"
38 #include "WasmCallingConvention.h"
39 #include "WasmContextInlines.h"
40 #include "WasmSignatureInlines.h"
41 #include "WasmToJS.h"
42
43 namespace JSC { namespace Wasm {
44
45 std::unique_ptr<InternalFunction> createJSToWasmWrapper(CompilationContext& compilationContext, const Signature& signature, Vector<UnlinkedWasmToWasmCall>* unlinkedWasmToWasmCalls, const ModuleInformation& info, MemoryMode mode, unsigned functionIndex)
46 {
47     CCallHelpers& jit = *compilationContext.embedderEntrypointJIT;
48
49     auto result = makeUnique<InternalFunction>();
50     jit.emitFunctionPrologue();
51
52     // FIXME Stop using 0 as codeBlocks. https://bugs.webkit.org/show_bug.cgi?id=165321
53     jit.store64(CCallHelpers::TrustedImm64(0), CCallHelpers::Address(GPRInfo::callFrameRegister, CallFrameSlot::codeBlock * static_cast<int>(sizeof(Register))));
54     MacroAssembler::DataLabelPtr calleeMoveLocation = jit.moveWithPatch(MacroAssembler::TrustedImmPtr(nullptr), GPRInfo::nonPreservedNonReturnGPR);
55     jit.storePtr(GPRInfo::nonPreservedNonReturnGPR, CCallHelpers::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
56     CodeLocationDataLabelPtr<WasmEntryPtrTag>* linkedCalleeMove = &result->calleeMoveLocation;
57     jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
58         *linkedCalleeMove = linkBuffer.locationOf<WasmEntryPtrTag>(calleeMoveLocation);
59     });
60
61     const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
62     RegisterSet toSave = pinnedRegs.toSave(mode);
63
64 #if !ASSERT_DISABLED
65     unsigned toSaveSize = toSave.numberOfSetGPRs();
66     // They should all be callee saves.
67     toSave.filter(RegisterSet::calleeSaveRegisters());
68     ASSERT(toSave.numberOfSetGPRs() == toSaveSize);
69 #endif
70
71     RegisterAtOffsetList registersToSpill(toSave, RegisterAtOffsetList::OffsetBaseType::FramePointerBased);
72     result->entrypoint.calleeSaveRegisters = registersToSpill;
73
74     unsigned totalFrameSize = registersToSpill.size() * sizeof(void*);
75     totalFrameSize += WasmCallingConvention::headerSizeInBytes();
76     totalFrameSize -= sizeof(CallerFrameAndPC);
77     unsigned numGPRs = 0;
78     unsigned numFPRs = 0;
79     bool argumentsIncludeI64 = false;
80     for (unsigned i = 0; i < signature.argumentCount(); i++) {
81         switch (signature.argument(i)) {
82         case Wasm::I64:
83             argumentsIncludeI64 = true;
84             FALLTHROUGH;
85         case Wasm::I32:
86         case Wasm::Anyref:
87         case Wasm::Funcref:
88             if (numGPRs >= wasmCallingConvention().m_gprArgs.size())
89                 totalFrameSize += sizeof(void*);
90             ++numGPRs;
91             break;
92         case Wasm::F32:
93         case Wasm::F64:
94             if (numFPRs >= wasmCallingConvention().m_fprArgs.size())
95                 totalFrameSize += sizeof(void*);
96             ++numFPRs;
97             break;
98         default:
99             RELEASE_ASSERT_NOT_REACHED();
100         }
101     }
102
103     totalFrameSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), totalFrameSize);
104     jit.subPtr(MacroAssembler::TrustedImm32(totalFrameSize), MacroAssembler::stackPointerRegister);
105
106     // We save all these registers regardless of having a memory or not.
107     // The reason is that we use one of these as a scratch. That said,
108     // almost all real wasm programs use memory, so it's not really
109     // worth optimizing for the case that they don't.
110     for (const RegisterAtOffset& regAtOffset : registersToSpill) {
111         GPRReg reg = regAtOffset.reg().gpr();
112         ptrdiff_t offset = regAtOffset.offset();
113         jit.storePtr(reg, CCallHelpers::Address(GPRInfo::callFrameRegister, offset));
114     }
115
116     if (argumentsIncludeI64 || signature.returnType() == Wasm::I64) {
117         if (Context::useFastTLS())
118             jit.loadWasmContextInstance(GPRInfo::argumentGPR2);
119         else {
120             // vmEntryToWasm passes the JSWebAssemblyInstance corresponding to Wasm::Context*'s
121             // instance as the first JS argument when we're not using fast TLS to hold the
122             // Wasm::Context*'s instance.
123             jit.loadPtr(CCallHelpers::Address(GPRInfo::callFrameRegister, CallFrameSlot::thisArgument * sizeof(EncodedJSValue)), GPRInfo::argumentGPR2);
124             jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR2, JSWebAssemblyInstance::offsetOfInstance()), GPRInfo::argumentGPR2);
125         }
126
127         emitThrowWasmToJSException(jit, GPRInfo::argumentGPR2, argumentsIncludeI64 ? ExceptionType::I64ArgumentType : ExceptionType::I64ReturnType);
128         return result;
129     }
130
131     GPRReg wasmContextInstanceGPR = pinnedRegs.wasmContextInstancePointer;
132
133     {
134         CCallHelpers::Address calleeFrame = CCallHelpers::Address(MacroAssembler::stackPointerRegister, -static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC)));
135         numGPRs = 0;
136         numFPRs = 0;
137         // We're going to set the pinned registers after this. So
138         // we can use this as a scratch for now since we saved it above.
139         GPRReg scratchReg = pinnedRegs.baseMemoryPointer;
140
141         ptrdiff_t jsOffset = CallFrameSlot::thisArgument * sizeof(EncodedJSValue);
142
143         // vmEntryToWasm passes the JSWebAssemblyInstance corresponding to Wasm::Context*'s
144         // instance as the first JS argument when we're not using fast TLS to hold the
145         // Wasm::Context*'s instance.
146         if (!Context::useFastTLS()) {
147             jit.loadPtr(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmContextInstanceGPR);
148             jit.loadPtr(CCallHelpers::Address(wasmContextInstanceGPR, JSWebAssemblyInstance::offsetOfInstance()), wasmContextInstanceGPR);
149             jsOffset += sizeof(EncodedJSValue);
150         }
151
152         ptrdiff_t wasmOffset = CallFrame::headerSizeInRegisters * sizeof(void*);
153         for (unsigned i = 0; i < signature.argumentCount(); i++) {
154             switch (signature.argument(i)) {
155             case Wasm::I32:
156             case Wasm::I64:
157             case Wasm::Funcref:
158             case Wasm::Anyref:
159                 if (numGPRs >= wasmCallingConvention().m_gprArgs.size()) {
160                     if (signature.argument(i) == Wasm::I32) {
161                         jit.load32(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchReg);
162                         jit.store32(scratchReg, calleeFrame.withOffset(wasmOffset));
163                     } else {
164                         jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchReg);
165                         jit.store64(scratchReg, calleeFrame.withOffset(wasmOffset));
166                     }
167                     wasmOffset += sizeof(void*);
168                 } else {
169                     if (signature.argument(i) == Wasm::I32)
170                         jit.load32(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmCallingConvention().m_gprArgs[numGPRs].gpr());
171                     else
172                         jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmCallingConvention().m_gprArgs[numGPRs].gpr());
173                 }
174                 ++numGPRs;
175                 break;
176             case Wasm::F32:
177             case Wasm::F64:
178                 if (numFPRs >= wasmCallingConvention().m_fprArgs.size()) {
179                     if (signature.argument(i) == Wasm::F32) {
180                         jit.load32(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchReg);
181                         jit.store32(scratchReg, calleeFrame.withOffset(wasmOffset));
182                     } else {
183                         jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchReg);
184                         jit.store64(scratchReg, calleeFrame.withOffset(wasmOffset));
185                     }
186                     wasmOffset += sizeof(void*);
187                 } else {
188                     if (signature.argument(i) == Wasm::F32)
189                         jit.loadFloat(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmCallingConvention().m_fprArgs[numFPRs].fpr());
190                     else
191                         jit.loadDouble(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmCallingConvention().m_fprArgs[numFPRs].fpr());
192                 }
193                 ++numFPRs;
194                 break;
195             default:
196                 RELEASE_ASSERT_NOT_REACHED();
197             }
198
199             jsOffset += sizeof(EncodedJSValue);
200         }
201     }
202
203     if (!!info.memory) {
204         GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
205         GPRReg scratchOrSize = wasmCallingConventionAir().prologueScratch(0);
206
207         if (Context::useFastTLS())
208             jit.loadWasmContextInstance(baseMemory);
209
210         GPRReg currentInstanceGPR = Context::useFastTLS() ? baseMemory : wasmContextInstanceGPR;
211         if (isARM64E()) {
212             if (mode != Wasm::MemoryMode::Signaling)
213                 scratchOrSize = pinnedRegs.sizeRegister;
214             jit.loadPtr(CCallHelpers::Address(currentInstanceGPR, Wasm::Instance::offsetOfCachedMemorySize()), scratchOrSize);
215         } else {
216             if (mode != Wasm::MemoryMode::Signaling)
217                 jit.loadPtr(CCallHelpers::Address(currentInstanceGPR, Wasm::Instance::offsetOfCachedMemorySize()), pinnedRegs.sizeRegister);
218         }
219
220         jit.loadPtr(CCallHelpers::Address(currentInstanceGPR, Wasm::Instance::offsetOfCachedMemory()), baseMemory);
221         jit.cageConditionally(Gigacage::Primitive, baseMemory, scratchOrSize, scratchOrSize);
222     }
223
224     CCallHelpers::Call call = jit.threadSafePatchableNearCall();
225     unsigned functionIndexSpace = functionIndex + info.importFunctionCount();
226     ASSERT(functionIndexSpace < info.functionIndexSpaceSize());
227     jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndexSpace] (LinkBuffer& linkBuffer) {
228         unlinkedWasmToWasmCalls->append({ linkBuffer.locationOfNearCall<WasmEntryPtrTag>(call), functionIndexSpace });
229     });
230
231     for (const RegisterAtOffset& regAtOffset : registersToSpill) {
232         GPRReg reg = regAtOffset.reg().gpr();
233         ASSERT(reg != GPRInfo::returnValueGPR);
234         ptrdiff_t offset = regAtOffset.offset();
235         jit.loadPtr(CCallHelpers::Address(GPRInfo::callFrameRegister, offset), reg);
236     }
237
238     switch (signature.returnType()) {
239     case Wasm::Void:
240         jit.moveTrustedValue(jsUndefined(), JSValueRegs { GPRInfo::returnValueGPR });
241         break;
242     case Wasm::Anyref:
243     case Wasm::Funcref:
244         break;
245     case Wasm::I32:
246         jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR);
247         jit.boxInt32(GPRInfo::returnValueGPR, JSValueRegs { GPRInfo::returnValueGPR }, DoNotHaveTagRegisters);
248         break;
249     case Wasm::F32:
250         jit.convertFloatToDouble(FPRInfo::returnValueFPR, FPRInfo::returnValueFPR);
251         FALLTHROUGH;
252     case Wasm::F64: {
253         jit.moveTrustedValue(jsNumber(pureNaN()), JSValueRegs { GPRInfo::returnValueGPR });
254         auto isNaN = jit.branchIfNaN(FPRInfo::returnValueFPR);
255         jit.boxDouble(FPRInfo::returnValueFPR, JSValueRegs { GPRInfo::returnValueGPR }, DoNotHaveTagRegisters);
256         isNaN.link(&jit);
257         break;
258     }
259     case Wasm::I64:
260     case Wasm::Func:
261         jit.breakpoint();
262         break;
263     default:
264         break;
265     }
266
267     jit.emitFunctionEpilogue();
268     jit.ret();
269
270     return result;
271 }
272
273 } } // namespace JSC::Wasm
274
275 #endif // ENABLE(WEBASSEMBLY)