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