Switch int8_t to GPRReg in StructureStubInfo because sizeof(GPRReg) == sizeof(int8_t)
[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 bool GetterSetterAccessCase::hasAlternateBase() const
104 {
105     if (customSlotBase())
106         return true;
107     return Base::hasAlternateBase();
108 }
109
110 JSObject* GetterSetterAccessCase::alternateBase() const
111 {
112     if (customSlotBase())
113         return customSlotBase();
114     return Base::alternateBase();
115 }
116
117 void GetterSetterAccessCase::dumpImpl(PrintStream& out, CommaPrinter& comma) const
118 {
119     Base::dumpImpl(out, comma);
120     out.print(comma, "customSlotBase = ", RawPointer(customSlotBase()));
121     if (callLinkInfo())
122         out.print(comma, "callLinkInfo = ", RawPointer(callLinkInfo()));
123     out.print(comma, "customAccessor = ", RawPointer(m_customAccessor.executableAddress()));
124 }
125
126 void GetterSetterAccessCase::emitDOMJITGetter(AccessGenerationState& state, const DOMJIT::GetterSetter* domJIT, GPRReg baseForGetGPR)
127 {
128     CCallHelpers& jit = *state.jit;
129     StructureStubInfo& stubInfo = *state.stubInfo;
130     JSValueRegs valueRegs = state.valueRegs;
131     GPRReg baseGPR = state.baseGPR;
132     GPRReg scratchGPR = state.scratchGPR;
133
134     // We construct the environment that can execute the DOMJIT::Snippet here.
135     Ref<DOMJIT::CallDOMGetterSnippet> snippet = domJIT->compiler()();
136
137     Vector<GPRReg> gpScratch;
138     Vector<FPRReg> fpScratch;
139     Vector<SnippetParams::Value> regs;
140
141     ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters);
142     allocator.lock(baseGPR);
143 #if USE(JSVALUE32_64)
144     allocator.lock(stubInfo.patch.baseTagGPR);
145 #endif
146     allocator.lock(valueRegs);
147     allocator.lock(scratchGPR);
148
149     GPRReg paramBaseGPR = InvalidGPRReg;
150     GPRReg paramGlobalObjectGPR = InvalidGPRReg;
151     JSValueRegs paramValueRegs = valueRegs;
152     GPRReg remainingScratchGPR = InvalidGPRReg;
153
154     // valueRegs and baseForGetGPR may be the same. For example, in Baseline JIT, we pass the same regT0 for baseGPR and valueRegs.
155     // In FTL, there is no constraint that the baseForGetGPR interferes with the result. To make implementation simple in
156     // Snippet, Snippet assumes that result registers always early interfere with input registers, in this case,
157     // baseForGetGPR. So we move baseForGetGPR to the other register if baseForGetGPR == valueRegs.
158     if (baseForGetGPR != valueRegs.payloadGPR()) {
159         paramBaseGPR = baseForGetGPR;
160         if (!snippet->requireGlobalObject)
161             remainingScratchGPR = scratchGPR;
162         else
163             paramGlobalObjectGPR = scratchGPR;
164     } else {
165         jit.move(valueRegs.payloadGPR(), scratchGPR);
166         paramBaseGPR = scratchGPR;
167         if (snippet->requireGlobalObject)
168             paramGlobalObjectGPR = allocator.allocateScratchGPR();
169     }
170
171     JSGlobalObject* globalObjectForDOMJIT = structure()->globalObject();
172
173     regs.append(paramValueRegs);
174     regs.append(paramBaseGPR);
175     if (snippet->requireGlobalObject) {
176         ASSERT(paramGlobalObjectGPR != InvalidGPRReg);
177         regs.append(SnippetParams::Value(paramGlobalObjectGPR, globalObjectForDOMJIT));
178     }
179
180     if (snippet->numGPScratchRegisters) {
181         unsigned i = 0;
182         if (remainingScratchGPR != InvalidGPRReg) {
183             gpScratch.append(remainingScratchGPR);
184             ++i;
185         }
186         for (; i < snippet->numGPScratchRegisters; ++i)
187             gpScratch.append(allocator.allocateScratchGPR());
188     }
189
190     for (unsigned i = 0; i < snippet->numFPScratchRegisters; ++i)
191         fpScratch.append(allocator.allocateScratchFPR());
192
193     // Let's store the reused registers to the stack. After that, we can use allocated scratch registers.
194     ScratchRegisterAllocator::PreservedState preservedState =
195     allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::SpaceForCCall);
196
197     if (GetterSetterAccessCaseInternal::verbose) {
198         dataLog("baseGPR = ", baseGPR, "\n");
199         dataLog("valueRegs = ", valueRegs, "\n");
200         dataLog("scratchGPR = ", scratchGPR, "\n");
201         dataLog("paramBaseGPR = ", paramBaseGPR, "\n");
202         if (paramGlobalObjectGPR != InvalidGPRReg)
203             dataLog("paramGlobalObjectGPR = ", paramGlobalObjectGPR, "\n");
204         dataLog("paramValueRegs = ", paramValueRegs, "\n");
205         for (unsigned i = 0; i < snippet->numGPScratchRegisters; ++i)
206             dataLog("gpScratch[", i, "] = ", gpScratch[i], "\n");
207     }
208
209     if (snippet->requireGlobalObject)
210         jit.move(CCallHelpers::TrustedImmPtr(globalObjectForDOMJIT), paramGlobalObjectGPR);
211
212     // We just spill the registers used in Snippet here. For not spilled registers here explicitly,
213     // they must be in the used register set passed by the callers (Baseline, DFG, and FTL) if they need to be kept.
214     // Some registers can be locked, but not in the used register set. For example, the caller could make baseGPR
215     // same to valueRegs, and not include it in the used registers since it will be changed.
216     RegisterSet registersToSpillForCCall;
217     for (auto& value : regs) {
218         SnippetReg reg = value.reg();
219         if (reg.isJSValueRegs())
220             registersToSpillForCCall.set(reg.jsValueRegs());
221         else if (reg.isGPR())
222             registersToSpillForCCall.set(reg.gpr());
223         else
224             registersToSpillForCCall.set(reg.fpr());
225     }
226     for (GPRReg reg : gpScratch)
227         registersToSpillForCCall.set(reg);
228     for (FPRReg reg : fpScratch)
229         registersToSpillForCCall.set(reg);
230     registersToSpillForCCall.exclude(RegisterSet::registersToNotSaveForCCall());
231
232     AccessCaseSnippetParams params(state.m_vm, WTFMove(regs), WTFMove(gpScratch), WTFMove(fpScratch));
233     snippet->generator()->run(jit, params);
234     allocator.restoreReusedRegistersByPopping(jit, preservedState);
235     state.succeed();
236     
237     CCallHelpers::JumpList exceptions = params.emitSlowPathCalls(state, registersToSpillForCCall, jit);
238     if (!exceptions.empty()) {
239         exceptions.link(&jit);
240         allocator.restoreReusedRegistersByPopping(jit, preservedState);
241         state.emitExplicitExceptionHandler();
242     }
243 }
244
245 } // namespace JSC
246
247 #endif // ENABLE(JIT)