Add support for the Wasm multi-value proposal
[WebKit-https.git] / Source / JavaScriptCore / wasm / WasmThunks.cpp
1 /*
2  * Copyright (C) 2017-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 #include "config.h"
27 #include "WasmThunks.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "CCallHelpers.h"
32 #include "HeapCellInlines.h"
33 #include "JITExceptions.h"
34 #include "LinkBuffer.h"
35 #include "ScratchRegisterAllocator.h"
36 #include "WasmContext.h"
37 #include "WasmExceptionType.h"
38 #include "WasmInstance.h"
39 #include "WasmOperations.h"
40
41 namespace JSC { namespace Wasm {
42
43 MacroAssemblerCodeRef<JITThunkPtrTag> throwExceptionFromWasmThunkGenerator(const AbstractLocker&)
44 {
45     CCallHelpers jit;
46
47     // The thing that jumps here must move ExceptionType into the argumentGPR1 before jumping here.
48     // We're allowed to use temp registers here. We are not allowed to use callee saves.
49     jit.loadWasmContextInstance(GPRInfo::argumentGPR2);
50     jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR2, Instance::offsetOfPointerToTopEntryFrame()), GPRInfo::argumentGPR0);
51     jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR0), GPRInfo::argumentGPR0);
52     jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(GPRInfo::argumentGPR0);
53     jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
54
55     CCallHelpers::Call call = jit.call(OperationPtrTag);
56     jit.farJump(GPRInfo::returnValueGPR, ExceptionHandlerPtrTag);
57     jit.breakpoint(); // We should not reach this.
58
59     ThrowWasmException throwWasmException = Thunks::singleton().throwWasmException();
60     RELEASE_ASSERT(throwWasmException);
61     LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
62     linkBuffer.link(call, FunctionPtr<OperationPtrTag>(throwWasmException));
63     return FINALIZE_WASM_CODE(linkBuffer, JITThunkPtrTag, "Throw exception from Wasm");
64 }
65
66 MacroAssemblerCodeRef<JITThunkPtrTag> throwStackOverflowFromWasmThunkGenerator(const AbstractLocker& locker)
67 {
68     CCallHelpers jit;
69
70     int32_t stackSpace = WTF::roundUpToMultipleOf(stackAlignmentBytes(), RegisterSet::calleeSaveRegisters().numberOfSetRegisters() * sizeof(Register));
71     ASSERT(static_cast<unsigned>(stackSpace) < Options::softReservedZoneSize());
72     jit.addPtr(CCallHelpers::TrustedImm32(-stackSpace), GPRInfo::callFrameRegister, MacroAssembler::stackPointerRegister);
73     jit.move(CCallHelpers::TrustedImm32(static_cast<uint32_t>(ExceptionType::StackOverflow)), GPRInfo::argumentGPR1);
74     auto jumpToExceptionHandler = jit.jump();
75     LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
76     linkBuffer.link(jumpToExceptionHandler, CodeLocationLabel<JITThunkPtrTag>(Thunks::singleton().stub(locker, throwExceptionFromWasmThunkGenerator).code()));
77     return FINALIZE_WASM_CODE(linkBuffer, JITThunkPtrTag, "Throw stack overflow from Wasm");
78 }
79
80 MacroAssemblerCodeRef<JITThunkPtrTag> triggerOMGEntryTierUpThunkGenerator(const AbstractLocker&)
81 {
82     // We expect that the user has already put the function index into GPRInfo::argumentGPR1
83     CCallHelpers jit;
84
85     jit.emitFunctionPrologue();
86
87     const unsigned extraPaddingBytes = 0;
88     RegisterSet registersToSpill = RegisterSet::allRegisters();
89     registersToSpill.exclude(RegisterSet::registersToNotSaveForCCall());
90     unsigned numberOfStackBytesUsedForRegisterPreservation = ScratchRegisterAllocator::preserveRegistersToStackForCall(jit, registersToSpill, extraPaddingBytes);
91
92     jit.loadWasmContextInstance(GPRInfo::argumentGPR0);
93     jit.move(MacroAssembler::TrustedImmPtr(tagCFunctionPtr<OperationPtrTag>(triggerTierUpNow)), GPRInfo::argumentGPR2);
94     jit.call(GPRInfo::argumentGPR2, OperationPtrTag);
95
96     ScratchRegisterAllocator::restoreRegistersFromStackForCall(jit, registersToSpill, RegisterSet(), numberOfStackBytesUsedForRegisterPreservation, extraPaddingBytes);
97
98     jit.emitFunctionEpilogue();
99     jit.ret();
100     LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
101     return FINALIZE_WASM_CODE(linkBuffer, JITThunkPtrTag, "Trigger OMG entry tier up");
102 }
103
104 static Thunks* thunks;
105 void Thunks::initialize()
106 {
107     thunks = new Thunks;
108 }
109
110 Thunks& Thunks::singleton()
111 {
112     ASSERT(thunks);
113     return *thunks;
114 }
115
116 void Thunks::setThrowWasmException(ThrowWasmException throwWasmException)
117 {
118     auto locker = holdLock(m_lock);
119     // The thunks are unique for the entire process, therefore changing the throwing function changes it for all uses of WebAssembly.
120     RELEASE_ASSERT(!m_throwWasmException || m_throwWasmException == throwWasmException);
121     m_throwWasmException = throwWasmException;
122 }
123
124 ThrowWasmException Thunks::throwWasmException()
125 {
126     return m_throwWasmException;
127 }
128
129 MacroAssemblerCodeRef<JITThunkPtrTag> Thunks::stub(ThunkGenerator generator)
130 {
131     auto locker = holdLock(m_lock);
132     return stub(locker, generator);
133 }
134
135 MacroAssemblerCodeRef<JITThunkPtrTag> Thunks::stub(const AbstractLocker& locker, ThunkGenerator generator)
136 {
137     ASSERT(!!generator);
138     {
139         auto addResult = m_stubs.add(generator, MacroAssemblerCodeRef<JITThunkPtrTag>());
140         if (!addResult.isNewEntry)
141             return addResult.iterator->value;
142     }
143
144     MacroAssemblerCodeRef<JITThunkPtrTag> code = generator(locker);
145     // We specifically don't use the iterator here to allow generator to recursively change m_stubs.
146     m_stubs.set(generator, code);
147     return code;
148 }
149
150 MacroAssemblerCodeRef<JITThunkPtrTag> Thunks::existingStub(ThunkGenerator generator)
151 {
152     auto locker = holdLock(m_lock);
153
154     auto iter = m_stubs.find(generator);
155     if (iter != m_stubs.end())
156         return iter->value;
157
158     return MacroAssemblerCodeRef<JITThunkPtrTag>();
159 }
160
161 } } // namespace JSC::Wasm
162
163 #endif // ENABLE(WEBASSEMBLY)