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