/ bytecode / GetterSetterAccessCase.cpp
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)