5bfe573768b1ebda2095b8eae8e31ca14feea1f8
[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 "WasmOMGPlan.h"
40
41 namespace JSC { namespace Wasm {
42
43 MacroAssemblerCodeRef 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     CCallHelpers::Call call = jit.call();
55     jit.jump(GPRInfo::returnValueGPR);
56     jit.breakpoint(); // We should not reach this.
57
58     ThrowWasmException throwWasmException = Thunks::singleton().throwWasmException();
59     RELEASE_ASSERT(throwWasmException);
60     LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
61     linkBuffer.link(call, FunctionPtr(throwWasmException));
62     return FINALIZE_CODE(linkBuffer, "Throw exception from Wasm");
63 }
64
65 MacroAssemblerCodeRef throwStackOverflowFromWasmThunkGenerator(const AbstractLocker& locker)
66 {
67     CCallHelpers jit;
68
69     int32_t stackSpace = WTF::roundUpToMultipleOf(stackAlignmentBytes(), RegisterSet::calleeSaveRegisters().numberOfSetRegisters() * sizeof(Register));
70     ASSERT(static_cast<unsigned>(stackSpace) < Options::softReservedZoneSize());
71     jit.addPtr(CCallHelpers::TrustedImm32(-stackSpace), GPRInfo::callFrameRegister, MacroAssembler::stackPointerRegister);
72     jit.move(CCallHelpers::TrustedImm32(static_cast<uint32_t>(ExceptionType::StackOverflow)), GPRInfo::argumentGPR1);
73     auto jumpToExceptionHandler = jit.jump();
74     LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
75     linkBuffer.link(jumpToExceptionHandler, CodeLocationLabel(Thunks::singleton().stub(locker, throwExceptionFromWasmThunkGenerator).code()));
76     return FINALIZE_CODE(linkBuffer, "Throw stack overflow from Wasm");
77 }
78
79 MacroAssemblerCodeRef triggerOMGTierUpThunkGenerator(const AbstractLocker&)
80 {
81     // We expect that the user has already put the function index into GPRInfo::argumentGPR1
82     CCallHelpers jit;
83
84     jit.emitFunctionPrologue();
85
86     const unsigned extraPaddingBytes = 0;
87     RegisterSet registersToSpill = RegisterSet::allRegisters();
88     registersToSpill.exclude(RegisterSet::registersToNotSaveForCCall());
89     unsigned numberOfStackBytesUsedForRegisterPreservation = ScratchRegisterAllocator::preserveRegistersToStackForCall(jit, registersToSpill, extraPaddingBytes);
90
91     jit.loadWasmContextInstance(GPRInfo::argumentGPR0);
92     typedef void (*Run)(Instance*, uint32_t);
93     Run run = OMGPlan::runForIndex;
94     jit.move(MacroAssembler::TrustedImmPtr(reinterpret_cast<void*>(run)), GPRInfo::argumentGPR2);
95     jit.call(GPRInfo::argumentGPR2);
96
97     ScratchRegisterAllocator::restoreRegistersFromStackForCall(jit, registersToSpill, RegisterSet(), numberOfStackBytesUsedForRegisterPreservation, extraPaddingBytes);
98
99     jit.emitFunctionEpilogue();
100     jit.ret();
101     LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
102     return FINALIZE_CODE(linkBuffer, "Trigger OMG tier up");
103 }
104
105 static Thunks* thunks;
106 void Thunks::initialize()
107 {
108     thunks = new Thunks;
109 }
110
111 Thunks& Thunks::singleton()
112 {
113     ASSERT(thunks);
114     return *thunks;
115 }
116
117 void Thunks::setThrowWasmException(ThrowWasmException throwWasmException)
118 {
119     auto locker = holdLock(m_lock);
120     // The thunks are unique for the entire process, therefore changing the throwing function changes it for all uses of WebAssembly.
121     RELEASE_ASSERT(!m_throwWasmException || m_throwWasmException == throwWasmException);
122     m_throwWasmException = throwWasmException;
123 }
124
125 ThrowWasmException Thunks::throwWasmException()
126 {
127     return m_throwWasmException;
128 }
129
130 MacroAssemblerCodeRef Thunks::stub(ThunkGenerator generator)
131 {
132     auto locker = holdLock(m_lock);
133     return stub(locker, generator);
134 }
135
136 MacroAssemblerCodeRef Thunks::stub(const AbstractLocker& locker, ThunkGenerator generator)
137 {
138     ASSERT(!!generator);
139     {
140         auto addResult = m_stubs.add(generator, MacroAssemblerCodeRef());
141         if (!addResult.isNewEntry)
142             return addResult.iterator->value;
143     }
144
145     MacroAssemblerCodeRef code = generator(locker);
146     // We specifically don't use the iterator here to allow generator to recursively change m_stubs.
147     m_stubs.set(generator, code);
148     return code;
149 }
150
151 MacroAssemblerCodeRef Thunks::existingStub(ThunkGenerator generator)
152 {
153     auto locker = holdLock(m_lock);
154
155     auto iter = m_stubs.find(generator);
156     if (iter != m_stubs.end())
157         return iter->value;
158
159     return MacroAssemblerCodeRef();
160 }
161
162 } } // namespace JSC::Wasm
163
164 #endif // ENABLE(WEBASSEMBLY)