2 * Copyright (C) 2017 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "GetterSetterAccessCase.h"
31 #include "DOMJITAccessCasePatchpointParams.h"
32 #include "DOMJITCallDOMGetterPatchpoint.h"
33 #include "DOMJITGetterSetter.h"
34 #include "HeapInlines.h"
35 #include "JSCJSValueInlines.h"
36 #include "PolymorphicAccess.h"
37 #include "StructureStubInfo.h"
41 static const bool verbose = false;
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)
46 m_customSlotBase.setMayBeNull(vm, owner, customSlotBase);
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)
57 case CustomAccessorGetter:
58 case CustomValueGetter:
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);
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)
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);
81 GetterSetterAccessCase::~GetterSetterAccessCase()
86 GetterSetterAccessCase::GetterSetterAccessCase(const GetterSetterAccessCase& other)
88 , m_customSlotBase(other.m_customSlotBase)
90 m_customAccessor.opaque = other.m_customAccessor.opaque;
91 m_domJIT = other.m_domJIT;
94 std::unique_ptr<AccessCase> GetterSetterAccessCase::clone() const
96 std::unique_ptr<GetterSetterAccessCase> result(new GetterSetterAccessCase(*this));
98 return WTFMove(result);
101 JSObject* GetterSetterAccessCase::alternateBase() const
103 if (customSlotBase())
104 return customSlotBase();
105 return conditionSet().slotBaseCondition().object();
108 void GetterSetterAccessCase::dumpImpl(PrintStream& out, CommaPrinter& comma) const
110 Base::dumpImpl(out, comma);
111 out.print(comma, "customSlotBase = ", RawPointer(customSlotBase()));
113 out.print(comma, "callLinkInfo = ", RawPointer(callLinkInfo()));
114 out.print(comma, "customAccessor = ", RawPointer(m_customAccessor.opaque));
117 void GetterSetterAccessCase::emitDOMJITGetter(AccessGenerationState& state, GPRReg baseForGetGPR)
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;
125 // We construct the environment that can execute the DOMJIT::Patchpoint here.
126 Ref<DOMJIT::CallDOMGetterPatchpoint> patchpoint = domJIT()->callDOMGetter();
128 Vector<GPRReg> gpScratch;
129 Vector<FPRReg> fpScratch;
130 Vector<DOMJIT::Value> regs;
132 ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters);
133 allocator.lock(baseGPR);
134 #if USE(JSVALUE32_64)
135 allocator.lock(static_cast<GPRReg>(stubInfo.patch.baseTagGPR));
137 allocator.lock(valueRegs);
138 allocator.lock(scratchGPR);
140 GPRReg paramBaseGPR = InvalidGPRReg;
141 GPRReg paramGlobalObjectGPR = InvalidGPRReg;
142 JSValueRegs paramValueRegs = valueRegs;
143 GPRReg remainingScratchGPR = InvalidGPRReg;
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 // DOMJIT::Patchpoint, DOMJIT::Patchpoint 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 (!patchpoint->requireGlobalObject)
152 remainingScratchGPR = scratchGPR;
154 paramGlobalObjectGPR = scratchGPR;
156 jit.move(valueRegs.payloadGPR(), scratchGPR);
157 paramBaseGPR = scratchGPR;
158 if (patchpoint->requireGlobalObject)
159 paramGlobalObjectGPR = allocator.allocateScratchGPR();
162 JSGlobalObject* globalObjectForDOMJIT = structure()->globalObject();
164 regs.append(paramValueRegs);
165 regs.append(paramBaseGPR);
166 if (patchpoint->requireGlobalObject) {
167 ASSERT(paramGlobalObjectGPR != InvalidGPRReg);
168 regs.append(DOMJIT::Value(paramGlobalObjectGPR, globalObjectForDOMJIT));
171 if (patchpoint->numGPScratchRegisters) {
173 if (remainingScratchGPR != InvalidGPRReg) {
174 gpScratch.append(remainingScratchGPR);
177 for (; i < patchpoint->numGPScratchRegisters; ++i)
178 gpScratch.append(allocator.allocateScratchGPR());
181 for (unsigned i = 0; i < patchpoint->numFPScratchRegisters; ++i)
182 fpScratch.append(allocator.allocateScratchFPR());
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);
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 < patchpoint->numGPScratchRegisters; ++i)
197 dataLog("gpScratch[", i, "] = ", gpScratch[i], "\n");
200 if (patchpoint->requireGlobalObject)
201 jit.move(CCallHelpers::TrustedImmPtr(globalObjectForDOMJIT), paramGlobalObjectGPR);
203 // We just spill the registers used in DOMJIT::Patchpoint 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 DOMJIT::Reg reg = value.reg();
210 if (reg.isJSValueRegs())
211 registersToSpillForCCall.set(reg.jsValueRegs());
212 else if (reg.isGPR())
213 registersToSpillForCCall.set(reg.gpr());
215 registersToSpillForCCall.set(reg.fpr());
217 for (GPRReg reg : gpScratch)
218 registersToSpillForCCall.set(reg);
219 for (FPRReg reg : fpScratch)
220 registersToSpillForCCall.set(reg);
221 registersToSpillForCCall.exclude(RegisterSet::registersToNotSaveForCCall());
223 DOMJITAccessCasePatchpointParams params(state.m_vm, WTFMove(regs), WTFMove(gpScratch), WTFMove(fpScratch));
224 patchpoint->generator()->run(jit, params);
225 allocator.restoreReusedRegistersByPopping(jit, preservedState);
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();
238 #endif // ENABLE(JIT)