[DOMJIT] Move DOMJIT patchpoint infrastructure out of domjit
[WebKit-https.git] / Source / JavaScriptCore / bytecode / GetterSetterAccessCase.cpp
1 /*
2  * Copyright (C) 2017 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 "GetterSetterAccessCase.h"
28
29 #if ENABLE(JIT)
30
31 #include "AccessCaseSnippetParams.h"
32 #include "DOMJITCallDOMGetterSnippet.h"
33 #include "DOMJITGetterSetter.h"
34 #include "HeapInlines.h"
35 #include "JSCJSValueInlines.h"
36 #include "PolymorphicAccess.h"
37 #include "StructureStubInfo.h"
38
39 namespace JSC {
40
41 static const bool verbose = false;
42
43 GetterSetterAccessCase::GetterSetterAccessCase(VM& vm, JSCell* owner, AccessType accessType, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet, bool viaProxy, WatchpointSet* additionalSet, JSObject* customSlotBase)
44     : Base(vm, owner, accessType, offset, structure, conditionSet, viaProxy, additionalSet)
45 {
46     m_customSlotBase.setMayBeNull(vm, owner, customSlotBase);
47 }
48
49
50 std::unique_ptr<AccessCase> GetterSetterAccessCase::create(
51     VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure,
52     const ObjectPropertyConditionSet& conditionSet, bool viaProxy, WatchpointSet* additionalSet,
53     PropertySlot::GetValueFunc customGetter, JSObject* customSlotBase, DOMJIT::GetterSetter* domJIT)
54 {
55     switch (type) {
56     case Getter:
57     case CustomAccessorGetter:
58     case CustomValueGetter:
59         break;
60     default:
61         ASSERT_NOT_REACHED();
62     };
63
64     std::unique_ptr<GetterSetterAccessCase> result(new GetterSetterAccessCase(vm, owner, type, offset, structure, conditionSet, viaProxy, additionalSet, customSlotBase));
65     result->m_domJIT = domJIT;
66     result->m_customAccessor.getter = customGetter;
67     return WTFMove(result);
68 }
69
70 std::unique_ptr<AccessCase> GetterSetterAccessCase::create(VM& vm, JSCell* owner, AccessType type, Structure* structure, PropertyOffset offset,
71     const ObjectPropertyConditionSet& conditionSet, PutPropertySlot::PutValueFunc customSetter,
72     JSObject* customSlotBase)
73 {
74     ASSERT(type == Setter || type == CustomValueSetter || type == CustomAccessorSetter);
75     std::unique_ptr<GetterSetterAccessCase> result(new GetterSetterAccessCase(vm, owner, type, offset, structure, conditionSet, false, nullptr, customSlotBase));
76     result->m_customAccessor.setter = customSetter;
77     return WTFMove(result);
78 }
79
80
81 GetterSetterAccessCase::~GetterSetterAccessCase()
82 {
83 }
84
85
86 GetterSetterAccessCase::GetterSetterAccessCase(const GetterSetterAccessCase& other)
87     : Base(other)
88     , m_customSlotBase(other.m_customSlotBase)
89 {
90     m_customAccessor.opaque = other.m_customAccessor.opaque;
91     m_domJIT = other.m_domJIT;
92 }
93
94 std::unique_ptr<AccessCase> GetterSetterAccessCase::clone() const
95 {
96     std::unique_ptr<GetterSetterAccessCase> result(new GetterSetterAccessCase(*this));
97     result->resetState();
98     return WTFMove(result);
99 }
100
101 JSObject* GetterSetterAccessCase::alternateBase() const
102 {
103     if (customSlotBase())
104         return customSlotBase();
105     return conditionSet().slotBaseCondition().object();
106 }
107
108 void GetterSetterAccessCase::dumpImpl(PrintStream& out, CommaPrinter& comma) const
109 {
110     Base::dumpImpl(out, comma);
111     out.print(comma, "customSlotBase = ", RawPointer(customSlotBase()));
112     if (callLinkInfo())
113         out.print(comma, "callLinkInfo = ", RawPointer(callLinkInfo()));
114     out.print(comma, "customAccessor = ", RawPointer(m_customAccessor.opaque));
115 }
116
117 void GetterSetterAccessCase::emitDOMJITGetter(AccessGenerationState& state, GPRReg baseForGetGPR)
118 {
119     CCallHelpers& jit = *state.jit;
120     StructureStubInfo& stubInfo = *state.stubInfo;
121     JSValueRegs valueRegs = state.valueRegs;
122     GPRReg baseGPR = state.baseGPR;
123     GPRReg scratchGPR = state.scratchGPR;
124
125     // We construct the environment that can execute the DOMJIT::Snippet here.
126     Ref<DOMJIT::CallDOMGetterSnippet> snippet = domJIT()->callDOMGetter();
127
128     Vector<GPRReg> gpScratch;
129     Vector<FPRReg> fpScratch;
130     Vector<SnippetParams::Value> regs;
131
132     ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters);
133     allocator.lock(baseGPR);
134 #if USE(JSVALUE32_64)
135     allocator.lock(static_cast<GPRReg>(stubInfo.patch.baseTagGPR));
136 #endif
137     allocator.lock(valueRegs);
138     allocator.lock(scratchGPR);
139
140     GPRReg paramBaseGPR = InvalidGPRReg;
141     GPRReg paramGlobalObjectGPR = InvalidGPRReg;
142     JSValueRegs paramValueRegs = valueRegs;
143     GPRReg remainingScratchGPR = InvalidGPRReg;
144
145     // valueRegs and baseForGetGPR may be the same. For example, in Baseline JIT, we pass the same regT0 for baseGPR and valueRegs.
146     // In FTL, there is no constraint that the baseForGetGPR interferes with the result. To make implementation simple in
147     // Snippet, Snippet assumes that result registers always early interfere with input registers, in this case,
148     // baseForGetGPR. So we move baseForGetGPR to the other register if baseForGetGPR == valueRegs.
149     if (baseForGetGPR != valueRegs.payloadGPR()) {
150         paramBaseGPR = baseForGetGPR;
151         if (!snippet->requireGlobalObject)
152             remainingScratchGPR = scratchGPR;
153         else
154             paramGlobalObjectGPR = scratchGPR;
155     } else {
156         jit.move(valueRegs.payloadGPR(), scratchGPR);
157         paramBaseGPR = scratchGPR;
158         if (snippet->requireGlobalObject)
159             paramGlobalObjectGPR = allocator.allocateScratchGPR();
160     }
161
162     JSGlobalObject* globalObjectForDOMJIT = structure()->globalObject();
163
164     regs.append(paramValueRegs);
165     regs.append(paramBaseGPR);
166     if (snippet->requireGlobalObject) {
167         ASSERT(paramGlobalObjectGPR != InvalidGPRReg);
168         regs.append(SnippetParams::Value(paramGlobalObjectGPR, globalObjectForDOMJIT));
169     }
170
171     if (snippet->numGPScratchRegisters) {
172         unsigned i = 0;
173         if (remainingScratchGPR != InvalidGPRReg) {
174             gpScratch.append(remainingScratchGPR);
175             ++i;
176         }
177         for (; i < snippet->numGPScratchRegisters; ++i)
178             gpScratch.append(allocator.allocateScratchGPR());
179     }
180
181     for (unsigned i = 0; i < snippet->numFPScratchRegisters; ++i)
182         fpScratch.append(allocator.allocateScratchFPR());
183
184     // Let's store the reused registers to the stack. After that, we can use allocated scratch registers.
185     ScratchRegisterAllocator::PreservedState preservedState =
186     allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::SpaceForCCall);
187
188     if (verbose) {
189         dataLog("baseGPR = ", baseGPR, "\n");
190         dataLog("valueRegs = ", valueRegs, "\n");
191         dataLog("scratchGPR = ", scratchGPR, "\n");
192         dataLog("paramBaseGPR = ", paramBaseGPR, "\n");
193         if (paramGlobalObjectGPR != InvalidGPRReg)
194             dataLog("paramGlobalObjectGPR = ", paramGlobalObjectGPR, "\n");
195         dataLog("paramValueRegs = ", paramValueRegs, "\n");
196         for (unsigned i = 0; i < snippet->numGPScratchRegisters; ++i)
197             dataLog("gpScratch[", i, "] = ", gpScratch[i], "\n");
198     }
199
200     if (snippet->requireGlobalObject)
201         jit.move(CCallHelpers::TrustedImmPtr(globalObjectForDOMJIT), paramGlobalObjectGPR);
202
203     // We just spill the registers used in Snippet here. For not spilled registers here explicitly,
204     // they must be in the used register set passed by the callers (Baseline, DFG, and FTL) if they need to be kept.
205     // Some registers can be locked, but not in the used register set. For example, the caller could make baseGPR
206     // same to valueRegs, and not include it in the used registers since it will be changed.
207     RegisterSet registersToSpillForCCall;
208     for (auto& value : regs) {
209         SnippetReg reg = value.reg();
210         if (reg.isJSValueRegs())
211             registersToSpillForCCall.set(reg.jsValueRegs());
212         else if (reg.isGPR())
213             registersToSpillForCCall.set(reg.gpr());
214         else
215             registersToSpillForCCall.set(reg.fpr());
216     }
217     for (GPRReg reg : gpScratch)
218         registersToSpillForCCall.set(reg);
219     for (FPRReg reg : fpScratch)
220         registersToSpillForCCall.set(reg);
221     registersToSpillForCCall.exclude(RegisterSet::registersToNotSaveForCCall());
222
223     AccessCaseSnippetParams params(state.m_vm, WTFMove(regs), WTFMove(gpScratch), WTFMove(fpScratch));
224     snippet->generator()->run(jit, params);
225     allocator.restoreReusedRegistersByPopping(jit, preservedState);
226     state.succeed();
227     
228     CCallHelpers::JumpList exceptions = params.emitSlowPathCalls(state, registersToSpillForCCall, jit);
229     if (!exceptions.empty()) {
230         exceptions.link(&jit);
231         allocator.restoreReusedRegistersByPopping(jit, preservedState);
232         state.emitExplicitExceptionHandler();
233     }
234 }
235
236 } // namespace JSC
237
238 #endif // ENABLE(JIT)