1/*
2 * Copyright (C) 2017-2019 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
39namespace JSC {
40
41namespace GetterSetterAccessCaseInternal {
42static constexpr bool verbose = false;
43}
44
45GetterSetterAccessCase::GetterSetterAccessCase(VM& vm, JSCell* owner, AccessType accessType, const Identifier& identifier, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet, bool viaProxy, WatchpointSet* additionalSet, JSObject* customSlotBase, std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain)
46 : Base(vm, owner, accessType, identifier, offset, structure, conditionSet, viaProxy, additionalSet, WTFMove(prototypeAccessChain))
47{
48 m_customSlotBase.setMayBeNull(vm, owner, customSlotBase);
49}
50
51
52std::unique_ptr<AccessCase> GetterSetterAccessCase::create(
53 VM& vm, JSCell* owner, AccessType type, const Identifier& identifier, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet,
54 bool viaProxy, WatchpointSet* additionalSet, FunctionPtr<OperationPtrTag> customGetter, JSObject* customSlotBase,
55 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, identifier, offset, structure, conditionSet, viaProxy, additionalSet, customSlotBase, WTFMove(prototypeAccessChain)));
67 result->m_domAttribute = domAttribute;
68 result->m_customAccessor = customGetter ? FunctionPtr<OperationPtrTag>(customGetter) : nullptr;
69 return result;
70}
71
72std::unique_ptr<AccessCase> GetterSetterAccessCase::create(VM& vm, JSCell* owner, AccessType type, Structure* structure, const Identifier& identifier, 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, identifier, offset, structure, conditionSet, false, nullptr, customSlotBase, WTFMove(prototypeAccessChain)));
78 result->m_customAccessor = customSetter ? FunctionPtr<OperationPtrTag>(customSetter) : nullptr;
79 return result;
80}
81
82
83GetterSetterAccessCase::~GetterSetterAccessCase()
84{
85}
86
87
88GetterSetterAccessCase::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
96std::unique_ptr<AccessCase> GetterSetterAccessCase::clone() const
97{
98 std::unique_ptr<GetterSetterAccessCase> result(new GetterSetterAccessCase(*this));
99 result->resetState();
100 return result;
101}
102
103bool GetterSetterAccessCase::hasAlternateBase() const
104{
105 if (customSlotBase())
106 return true;
107 return Base::hasAlternateBase();
108}
109
110JSObject* GetterSetterAccessCase::alternateBase() const
111{
112 if (customSlotBase())
113 return customSlotBase();
114 return Base::alternateBase();
115}
116
117void 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
126void 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)
248