]> git.saurik.com Git - apple/javascriptcore.git/blame - jit/Repatch.cpp
JavaScriptCore-7600.1.4.9.tar.gz
[apple/javascriptcore.git] / jit / Repatch.cpp
CommitLineData
81345200
A
1/*
2 * Copyright (C) 2011, 2012, 2013, 2014 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 "Repatch.h"
28
29#if ENABLE(JIT)
30
31#include "AccessorCallJITStubRoutine.h"
32#include "CCallHelpers.h"
33#include "DFGOperations.h"
34#include "DFGSpeculativeJIT.h"
35#include "FTLThunks.h"
36#include "GCAwareJITStubRoutine.h"
37#include "GetterSetter.h"
38#include "JIT.h"
39#include "JITInlines.h"
40#include "LinkBuffer.h"
41#include "JSCInlines.h"
42#include "PolymorphicGetByIdList.h"
43#include "PolymorphicPutByIdList.h"
44#include "RegExpMatchesArray.h"
45#include "RepatchBuffer.h"
46#include "ScratchRegisterAllocator.h"
47#include "StackAlignment.h"
48#include "StructureRareDataInlines.h"
49#include "StructureStubClearingWatchpoint.h"
50#include "ThunkGenerators.h"
51#include <wtf/StringPrintStream.h>
52
53namespace JSC {
54
55// Beware: in this code, it is not safe to assume anything about the following registers
56// that would ordinarily have well-known values:
57// - tagTypeNumberRegister
58// - tagMaskRegister
59
60static FunctionPtr readCallTarget(RepatchBuffer& repatchBuffer, CodeLocationCall call)
61{
62 FunctionPtr result = MacroAssembler::readCallTarget(call);
63#if ENABLE(FTL_JIT)
64 CodeBlock* codeBlock = repatchBuffer.codeBlock();
65 if (codeBlock->jitType() == JITCode::FTLJIT) {
66 return FunctionPtr(codeBlock->vm()->ftlThunks->keyForSlowPathCallThunk(
67 MacroAssemblerCodePtr::createFromExecutableAddress(
68 result.executableAddress())).callTarget());
69 }
70#else
71 UNUSED_PARAM(repatchBuffer);
72#endif // ENABLE(FTL_JIT)
73 return result;
74}
75
76static void repatchCall(RepatchBuffer& repatchBuffer, CodeLocationCall call, FunctionPtr newCalleeFunction)
77{
78#if ENABLE(FTL_JIT)
79 CodeBlock* codeBlock = repatchBuffer.codeBlock();
80 if (codeBlock->jitType() == JITCode::FTLJIT) {
81 VM& vm = *codeBlock->vm();
82 FTL::Thunks& thunks = *vm.ftlThunks;
83 FTL::SlowPathCallKey key = thunks.keyForSlowPathCallThunk(
84 MacroAssemblerCodePtr::createFromExecutableAddress(
85 MacroAssembler::readCallTarget(call).executableAddress()));
86 key = key.withCallTarget(newCalleeFunction.executableAddress());
87 newCalleeFunction = FunctionPtr(
88 thunks.getSlowPathCallThunk(vm, key).code().executableAddress());
89 }
90#endif // ENABLE(FTL_JIT)
91 repatchBuffer.relink(call, newCalleeFunction);
92}
93
94static void repatchCall(CodeBlock* codeblock, CodeLocationCall call, FunctionPtr newCalleeFunction)
95{
96 RepatchBuffer repatchBuffer(codeblock);
97 repatchCall(repatchBuffer, call, newCalleeFunction);
98}
99
100static void repatchByIdSelfAccess(VM& vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo, Structure* structure, const Identifier& propertyName, PropertyOffset offset,
101 const FunctionPtr &slowPathFunction, bool compact)
102{
103 if (structure->typeInfo().newImpurePropertyFiresWatchpoints())
104 vm.registerWatchpointForImpureProperty(propertyName, stubInfo.addWatchpoint(codeBlock));
105
106 RepatchBuffer repatchBuffer(codeBlock);
107
108 // Only optimize once!
109 repatchCall(repatchBuffer, stubInfo.callReturnLocation, slowPathFunction);
110
111 // Patch the structure check & the offset of the load.
112 repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(-(intptr_t)stubInfo.patch.deltaCheckImmToCall), bitwise_cast<int32_t>(structure->id()));
113 repatchBuffer.setLoadInstructionIsActive(stubInfo.callReturnLocation.convertibleLoadAtOffset(stubInfo.patch.deltaCallToStorageLoad), isOutOfLineOffset(offset));
114#if USE(JSVALUE64)
115 if (compact)
116 repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.deltaCallToLoadOrStore), offsetRelativeToPatchedStorage(offset));
117 else
118 repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.deltaCallToLoadOrStore), offsetRelativeToPatchedStorage(offset));
119#elif USE(JSVALUE32_64)
120 if (compact) {
121 repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.deltaCallToTagLoadOrStore), offsetRelativeToPatchedStorage(offset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag));
122 repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.deltaCallToPayloadLoadOrStore), offsetRelativeToPatchedStorage(offset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload));
123 } else {
124 repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.deltaCallToTagLoadOrStore), offsetRelativeToPatchedStorage(offset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag));
125 repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.deltaCallToPayloadLoadOrStore), offsetRelativeToPatchedStorage(offset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload));
126 }
127#endif
128}
129
130static void addStructureTransitionCheck(
131 JSCell* object, Structure* structure, CodeBlock* codeBlock, StructureStubInfo& stubInfo,
132 MacroAssembler& jit, MacroAssembler::JumpList& failureCases, GPRReg scratchGPR)
133{
134 if (object->structure() == structure && structure->transitionWatchpointSetIsStillValid()) {
135 structure->addTransitionWatchpoint(stubInfo.addWatchpoint(codeBlock));
136 if (!ASSERT_DISABLED) {
137 // If we execute this code, the object must have the structure we expect. Assert
138 // this in debug modes.
139 jit.move(MacroAssembler::TrustedImmPtr(object), scratchGPR);
140 MacroAssembler::Jump ok = branchStructure(
141 jit,
142 MacroAssembler::Equal,
143 MacroAssembler::Address(scratchGPR, JSCell::structureIDOffset()),
144 structure);
145 jit.abortWithReason(RepatchIneffectiveWatchpoint);
146 ok.link(&jit);
147 }
148 return;
149 }
150
151 jit.move(MacroAssembler::TrustedImmPtr(object), scratchGPR);
152 failureCases.append(
153 branchStructure(jit,
154 MacroAssembler::NotEqual,
155 MacroAssembler::Address(scratchGPR, JSCell::structureIDOffset()),
156 structure));
157}
158
159static void addStructureTransitionCheck(
160 JSValue prototype, CodeBlock* codeBlock, StructureStubInfo& stubInfo,
161 MacroAssembler& jit, MacroAssembler::JumpList& failureCases, GPRReg scratchGPR)
162{
163 if (prototype.isNull())
164 return;
165
166 ASSERT(prototype.isCell());
167
168 addStructureTransitionCheck(
169 prototype.asCell(), prototype.asCell()->structure(), codeBlock, stubInfo, jit,
170 failureCases, scratchGPR);
171}
172
173static void replaceWithJump(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo, const MacroAssemblerCodePtr target)
174{
175 if (MacroAssembler::canJumpReplacePatchableBranch32WithPatch()) {
176 repatchBuffer.replaceWithJump(
177 RepatchBuffer::startOfPatchableBranch32WithPatchOnAddress(
178 stubInfo.callReturnLocation.dataLabel32AtOffset(
179 -(intptr_t)stubInfo.patch.deltaCheckImmToCall)),
180 CodeLocationLabel(target));
181 return;
182 }
183
184 repatchBuffer.relink(
185 stubInfo.callReturnLocation.jumpAtOffset(
186 stubInfo.patch.deltaCallToJump),
187 CodeLocationLabel(target));
188}
189
190static void emitRestoreScratch(MacroAssembler& stubJit, bool needToRestoreScratch, GPRReg scratchGPR, MacroAssembler::Jump& success, MacroAssembler::Jump& fail, MacroAssembler::JumpList failureCases)
191{
192 if (needToRestoreScratch) {
193 stubJit.popToRestore(scratchGPR);
194
195 success = stubJit.jump();
196
197 // link failure cases here, so we can pop scratchGPR, and then jump back.
198 failureCases.link(&stubJit);
199
200 stubJit.popToRestore(scratchGPR);
201
202 fail = stubJit.jump();
203 return;
204 }
205
206 success = stubJit.jump();
207}
208
209static void linkRestoreScratch(LinkBuffer& patchBuffer, bool needToRestoreScratch, MacroAssembler::Jump success, MacroAssembler::Jump fail, MacroAssembler::JumpList failureCases, CodeLocationLabel successLabel, CodeLocationLabel slowCaseBegin)
210{
211 patchBuffer.link(success, successLabel);
212
213 if (needToRestoreScratch) {
214 patchBuffer.link(fail, slowCaseBegin);
215 return;
216 }
217
218 // link failure cases directly back to normal path
219 patchBuffer.link(failureCases, slowCaseBegin);
220}
221
222static void linkRestoreScratch(LinkBuffer& patchBuffer, bool needToRestoreScratch, StructureStubInfo& stubInfo, MacroAssembler::Jump success, MacroAssembler::Jump fail, MacroAssembler::JumpList failureCases)
223{
224 linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
225}
226
227enum ByIdStubKind {
228 GetValue,
229 CallGetter,
230 CallCustomGetter,
231 CallSetter,
232 CallCustomSetter
233};
234
235static const char* toString(ByIdStubKind kind)
236{
237 switch (kind) {
238 case GetValue:
239 return "GetValue";
240 case CallGetter:
241 return "CallGetter";
242 case CallCustomGetter:
243 return "CallCustomGetter";
244 case CallSetter:
245 return "CallSetter";
246 case CallCustomSetter:
247 return "CallCustomSetter";
248 default:
249 RELEASE_ASSERT_NOT_REACHED();
250 return nullptr;
251 }
252}
253
254static ByIdStubKind kindFor(const PropertySlot& slot)
255{
256 if (slot.isCacheableValue())
257 return GetValue;
258 if (slot.isCacheableCustom())
259 return CallCustomGetter;
260 RELEASE_ASSERT(slot.isCacheableGetter());
261 return CallGetter;
262}
263
264static FunctionPtr customFor(const PropertySlot& slot)
265{
266 if (!slot.isCacheableCustom())
267 return FunctionPtr();
268 return FunctionPtr(slot.customGetter());
269}
270
271static ByIdStubKind kindFor(const PutPropertySlot& slot)
272{
273 RELEASE_ASSERT(!slot.isCacheablePut());
274 if (slot.isCacheableSetter())
275 return CallSetter;
276 RELEASE_ASSERT(slot.isCacheableCustom());
277 return CallCustomSetter;
278}
279
280static FunctionPtr customFor(const PutPropertySlot& slot)
281{
282 if (!slot.isCacheableCustom())
283 return FunctionPtr();
284 return FunctionPtr(slot.customSetter());
285}
286
287static void generateByIdStub(
288 ExecState* exec, ByIdStubKind kind, const Identifier& propertyName,
289 FunctionPtr custom, StructureStubInfo& stubInfo, StructureChain* chain, size_t count,
290 PropertyOffset offset, Structure* structure, bool loadTargetFromProxy, WatchpointSet* watchpointSet,
291 CodeLocationLabel successLabel, CodeLocationLabel slowCaseLabel, RefPtr<JITStubRoutine>& stubRoutine)
292{
293 VM* vm = &exec->vm();
294 GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
295 JSValueRegs valueRegs = JSValueRegs(
296#if USE(JSVALUE32_64)
297 static_cast<GPRReg>(stubInfo.patch.valueTagGPR),
298#endif
299 static_cast<GPRReg>(stubInfo.patch.valueGPR));
300 GPRReg scratchGPR = TempRegisterSet(stubInfo.patch.usedRegisters).getFreeGPR();
301 bool needToRestoreScratch = scratchGPR == InvalidGPRReg;
302 RELEASE_ASSERT(!needToRestoreScratch || kind == GetValue);
303
304 CCallHelpers stubJit(&exec->vm(), exec->codeBlock());
305 if (needToRestoreScratch) {
306 scratchGPR = AssemblyHelpers::selectScratchGPR(
307 baseGPR, valueRegs.tagGPR(), valueRegs.payloadGPR());
308 stubJit.pushToSave(scratchGPR);
309 needToRestoreScratch = true;
310 }
311
312 MacroAssembler::JumpList failureCases;
313
314 GPRReg baseForGetGPR;
315 if (loadTargetFromProxy) {
316 baseForGetGPR = valueRegs.payloadGPR();
317 failureCases.append(stubJit.branch8(
318 MacroAssembler::NotEqual,
319 MacroAssembler::Address(baseGPR, JSCell::typeInfoTypeOffset()),
320 MacroAssembler::TrustedImm32(PureForwardingProxyType)));
321
322 stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSProxy::targetOffset()), scratchGPR);
323
324 failureCases.append(branchStructure(stubJit,
325 MacroAssembler::NotEqual,
326 MacroAssembler::Address(scratchGPR, JSCell::structureIDOffset()),
327 structure));
328 } else {
329 baseForGetGPR = baseGPR;
330
331 failureCases.append(branchStructure(stubJit,
332 MacroAssembler::NotEqual,
333 MacroAssembler::Address(baseForGetGPR, JSCell::structureIDOffset()),
334 structure));
335 }
336
337 CodeBlock* codeBlock = exec->codeBlock();
338 if (structure->typeInfo().newImpurePropertyFiresWatchpoints())
339 vm->registerWatchpointForImpureProperty(propertyName, stubInfo.addWatchpoint(codeBlock));
340
341 if (watchpointSet)
342 watchpointSet->add(stubInfo.addWatchpoint(codeBlock));
343
344 Structure* currStructure = structure;
345 JSObject* protoObject = 0;
346 if (chain) {
347 WriteBarrier<Structure>* it = chain->head();
348 for (unsigned i = 0; i < count; ++i, ++it) {
349 protoObject = asObject(currStructure->prototypeForLookup(exec));
350 Structure* protoStructure = protoObject->structure();
351 if (protoStructure->typeInfo().newImpurePropertyFiresWatchpoints())
352 vm->registerWatchpointForImpureProperty(propertyName, stubInfo.addWatchpoint(codeBlock));
353 addStructureTransitionCheck(
354 protoObject, protoStructure, codeBlock, stubInfo, stubJit,
355 failureCases, scratchGPR);
356 currStructure = it->get();
357 }
358 }
359
360 GPRReg baseForAccessGPR;
361 if (chain) {
362 // We could have clobbered scratchGPR earlier, so we have to reload from baseGPR to get the target.
363 if (loadTargetFromProxy)
364 stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSProxy::targetOffset()), baseForGetGPR);
365 stubJit.move(MacroAssembler::TrustedImmPtr(protoObject), scratchGPR);
366 baseForAccessGPR = scratchGPR;
367 } else {
368 // For proxy objects, we need to do all the Structure checks before moving the baseGPR into
369 // baseForGetGPR because if we fail any of the checks then we would have the wrong value in baseGPR
370 // on the slow path.
371 if (loadTargetFromProxy)
372 stubJit.move(scratchGPR, baseForGetGPR);
373 baseForAccessGPR = baseForGetGPR;
374 }
375
376 GPRReg loadedValueGPR = InvalidGPRReg;
377 if (kind != CallCustomGetter && kind != CallCustomSetter) {
378 if (kind == GetValue)
379 loadedValueGPR = valueRegs.payloadGPR();
380 else
381 loadedValueGPR = scratchGPR;
382
383 GPRReg storageGPR;
384 if (isInlineOffset(offset))
385 storageGPR = baseForAccessGPR;
386 else {
387 stubJit.loadPtr(MacroAssembler::Address(baseForAccessGPR, JSObject::butterflyOffset()), loadedValueGPR);
388 storageGPR = loadedValueGPR;
389 }
390
391#if USE(JSVALUE64)
392 stubJit.load64(MacroAssembler::Address(storageGPR, offsetRelativeToBase(offset)), loadedValueGPR);
393#else
394 if (kind == GetValue)
395 stubJit.load32(MacroAssembler::Address(storageGPR, offsetRelativeToBase(offset) + TagOffset), valueRegs.tagGPR());
396 stubJit.load32(MacroAssembler::Address(storageGPR, offsetRelativeToBase(offset) + PayloadOffset), loadedValueGPR);
397#endif
398 }
399
400 // Stuff for custom getters.
401 MacroAssembler::Call operationCall;
402 MacroAssembler::Call handlerCall;
403
404 // Stuff for JS getters.
405 MacroAssembler::DataLabelPtr addressOfLinkFunctionCheck;
406 MacroAssembler::Call fastPathCall;
407 MacroAssembler::Call slowPathCall;
408 std::unique_ptr<CallLinkInfo> callLinkInfo;
409
410 MacroAssembler::Jump success, fail;
411 if (kind != GetValue) {
412 // Need to make sure that whenever this call is made in the future, we remember the
413 // place that we made it from. It just so happens to be the place that we are at
414 // right now!
415 stubJit.store32(MacroAssembler::TrustedImm32(exec->locationAsRawBits()),
416 CCallHelpers::tagFor(static_cast<VirtualRegister>(JSStack::ArgumentCount)));
417
418 if (kind == CallGetter || kind == CallSetter) {
419 // Create a JS call using a JS call inline cache. Assume that:
420 //
421 // - SP is aligned and represents the extent of the calling compiler's stack usage.
422 //
423 // - FP is set correctly (i.e. it points to the caller's call frame header).
424 //
425 // - SP - FP is an aligned difference.
426 //
427 // - Any byte between FP (exclusive) and SP (inclusive) could be live in the calling
428 // code.
429 //
430 // Therefore, we temporarily grow the stack for the purpose of the call and then
431 // shrink it after.
432
433 callLinkInfo = std::make_unique<CallLinkInfo>();
434 callLinkInfo->callType = CallLinkInfo::Call;
435 callLinkInfo->codeOrigin = stubInfo.codeOrigin;
436 callLinkInfo->calleeGPR = loadedValueGPR;
437
438 MacroAssembler::JumpList done;
439
440 // There is a 'this' argument but nothing else.
441 unsigned numberOfParameters = 1;
442 // ... unless we're calling a setter.
443 if (kind == CallSetter)
444 numberOfParameters++;
445
446 // Get the accessor; if there ain't one then the result is jsUndefined().
447 if (kind == CallSetter) {
448 stubJit.loadPtr(
449 MacroAssembler::Address(loadedValueGPR, GetterSetter::offsetOfSetter()),
450 loadedValueGPR);
451 } else {
452 stubJit.loadPtr(
453 MacroAssembler::Address(loadedValueGPR, GetterSetter::offsetOfGetter()),
454 loadedValueGPR);
455 }
456 MacroAssembler::Jump returnUndefined = stubJit.branchTestPtr(
457 MacroAssembler::Zero, loadedValueGPR);
458
459 unsigned numberOfRegsForCall =
460 JSStack::CallFrameHeaderSize + numberOfParameters;
461
462 unsigned numberOfBytesForCall =
463 numberOfRegsForCall * sizeof(Register) - sizeof(CallerFrameAndPC);
464
465 unsigned alignedNumberOfBytesForCall =
466 WTF::roundUpToMultipleOf(stackAlignmentBytes(), numberOfBytesForCall);
467
468 stubJit.subPtr(
469 MacroAssembler::TrustedImm32(alignedNumberOfBytesForCall),
470 MacroAssembler::stackPointerRegister);
471
472 MacroAssembler::Address calleeFrame = MacroAssembler::Address(
473 MacroAssembler::stackPointerRegister,
474 -static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC)));
475
476 stubJit.store32(
477 MacroAssembler::TrustedImm32(numberOfParameters),
478 calleeFrame.withOffset(
479 JSStack::ArgumentCount * sizeof(Register) + PayloadOffset));
480
481 stubJit.storeCell(
482 loadedValueGPR, calleeFrame.withOffset(JSStack::Callee * sizeof(Register)));
483
484 stubJit.storeCell(
485 baseForGetGPR,
486 calleeFrame.withOffset(
487 virtualRegisterForArgument(0).offset() * sizeof(Register)));
488
489 if (kind == CallSetter) {
490 stubJit.storeValue(
491 valueRegs,
492 calleeFrame.withOffset(
493 virtualRegisterForArgument(1).offset() * sizeof(Register)));
494 }
495
496 MacroAssembler::Jump slowCase = stubJit.branchPtrWithPatch(
497 MacroAssembler::NotEqual, loadedValueGPR, addressOfLinkFunctionCheck,
498 MacroAssembler::TrustedImmPtr(0));
499
500 // loadedValueGPR is already burned. We can reuse it. From here on we assume that
501 // any volatile register will be clobbered anyway.
502 stubJit.loadPtr(
503 MacroAssembler::Address(loadedValueGPR, JSFunction::offsetOfScopeChain()),
504 loadedValueGPR);
505 stubJit.storeCell(
506 loadedValueGPR, calleeFrame.withOffset(JSStack::ScopeChain * sizeof(Register)));
507 fastPathCall = stubJit.nearCall();
508
509 stubJit.addPtr(
510 MacroAssembler::TrustedImm32(alignedNumberOfBytesForCall),
511 MacroAssembler::stackPointerRegister);
512 if (kind == CallGetter)
513 stubJit.setupResults(valueRegs);
514
515 done.append(stubJit.jump());
516 slowCase.link(&stubJit);
517
518 stubJit.move(loadedValueGPR, GPRInfo::regT0);
519#if USE(JSVALUE32_64)
520 stubJit.move(MacroAssembler::TrustedImm32(JSValue::CellTag), GPRInfo::regT1);
521#endif
522 stubJit.move(MacroAssembler::TrustedImmPtr(callLinkInfo.get()), GPRInfo::regT2);
523 slowPathCall = stubJit.nearCall();
524
525 stubJit.addPtr(
526 MacroAssembler::TrustedImm32(alignedNumberOfBytesForCall),
527 MacroAssembler::stackPointerRegister);
528 if (kind == CallGetter)
529 stubJit.setupResults(valueRegs);
530
531 done.append(stubJit.jump());
532 returnUndefined.link(&stubJit);
533
534 if (kind == CallGetter)
535 stubJit.moveTrustedValue(jsUndefined(), valueRegs);
536
537 done.link(&stubJit);
538 } else {
539 // getter: EncodedJSValue (*GetValueFunc)(ExecState*, JSObject* slotBase, EncodedJSValue thisValue, PropertyName);
540 // setter: void (*PutValueFunc)(ExecState*, JSObject* base, EncodedJSValue thisObject, EncodedJSValue value);
541#if USE(JSVALUE64)
542 if (kind == CallCustomGetter)
543 stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseForGetGPR, MacroAssembler::TrustedImmPtr(propertyName.impl()));
544 else
545 stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseForGetGPR, valueRegs.gpr());
546#else
547 if (kind == CallCustomGetter)
548 stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseForGetGPR, MacroAssembler::TrustedImm32(JSValue::CellTag), MacroAssembler::TrustedImmPtr(propertyName.impl()));
549 else
550 stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseForGetGPR, MacroAssembler::TrustedImm32(JSValue::CellTag), valueRegs.payloadGPR(), valueRegs.tagGPR());
551#endif
552 stubJit.storePtr(GPRInfo::callFrameRegister, &vm->topCallFrame);
553
554 operationCall = stubJit.call();
555 if (kind == CallCustomGetter)
556 stubJit.setupResults(valueRegs);
557 MacroAssembler::Jump noException = stubJit.emitExceptionCheck(CCallHelpers::InvertedExceptionCheck);
558
559 stubJit.setupArguments(CCallHelpers::TrustedImmPtr(vm), GPRInfo::callFrameRegister);
560 handlerCall = stubJit.call();
561 stubJit.jumpToExceptionHandler();
562
563 noException.link(&stubJit);
564 }
565 }
566 emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
567
568 LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock());
569
570 linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, successLabel, slowCaseLabel);
571 if (kind == CallCustomGetter || kind == CallCustomSetter) {
572 patchBuffer.link(operationCall, custom);
573 patchBuffer.link(handlerCall, lookupExceptionHandler);
574 } else if (kind == CallGetter || kind == CallSetter) {
575 callLinkInfo->hotPathOther = patchBuffer.locationOfNearCall(fastPathCall);
576 callLinkInfo->hotPathBegin = patchBuffer.locationOf(addressOfLinkFunctionCheck);
577 callLinkInfo->callReturnLocation = patchBuffer.locationOfNearCall(slowPathCall);
578
579 ThunkGenerator generator = linkThunkGeneratorFor(
580 CodeForCall, RegisterPreservationNotRequired);
581 patchBuffer.link(
582 slowPathCall, CodeLocationLabel(vm->getCTIStub(generator).code()));
583 }
584
585 MacroAssemblerCodeRef code = FINALIZE_CODE_FOR(
586 exec->codeBlock(), patchBuffer,
587 ("%s access stub for %s, return point %p",
588 toString(kind), toCString(*exec->codeBlock()).data(),
589 successLabel.executableAddress()));
590
591 if (kind == CallGetter || kind == CallSetter)
592 stubRoutine = adoptRef(new AccessorCallJITStubRoutine(code, *vm, WTF::move(callLinkInfo)));
593 else
594 stubRoutine = createJITStubRoutine(code, *vm, codeBlock->ownerExecutable(), true);
595}
596
597enum InlineCacheAction {
598 GiveUpOnCache,
599 RetryCacheLater,
600 AttemptToCache
601};
602
603static InlineCacheAction actionForCell(VM& vm, JSCell* cell)
604{
605 Structure* structure = cell->structure(vm);
606
607 TypeInfo typeInfo = structure->typeInfo();
608 if (typeInfo.prohibitsPropertyCaching())
609 return GiveUpOnCache;
610
611 if (structure->isUncacheableDictionary()) {
612 if (structure->hasBeenFlattenedBefore())
613 return GiveUpOnCache;
614 // Flattening could have changed the offset, so return early for another try.
615 asObject(cell)->flattenDictionaryObject(vm);
616 return RetryCacheLater;
617 }
618 ASSERT(!structure->isUncacheableDictionary());
619
620 if (typeInfo.hasImpureGetOwnPropertySlot() && !typeInfo.newImpurePropertyFiresWatchpoints())
621 return GiveUpOnCache;
622
623 return AttemptToCache;
624}
625
626static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
627{
628 if (Options::forceICFailure())
629 return GiveUpOnCache;
630
631 // FIXME: Write a test that proves we need to check for recursion here just
632 // like the interpreter does, then add a check for recursion.
633
634 CodeBlock* codeBlock = exec->codeBlock();
635 VM* vm = &exec->vm();
636
637 if ((isJSArray(baseValue) || isRegExpMatchesArray(baseValue) || isJSString(baseValue)) && propertyName == exec->propertyNames().length) {
638 GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
639#if USE(JSVALUE32_64)
640 GPRReg resultTagGPR = static_cast<GPRReg>(stubInfo.patch.valueTagGPR);
641#endif
642 GPRReg resultGPR = static_cast<GPRReg>(stubInfo.patch.valueGPR);
643
644 MacroAssembler stubJit;
645
646 if (isJSArray(baseValue) || isRegExpMatchesArray(baseValue)) {
647 GPRReg scratchGPR = TempRegisterSet(stubInfo.patch.usedRegisters).getFreeGPR();
648 bool needToRestoreScratch = false;
649
650 if (scratchGPR == InvalidGPRReg) {
651#if USE(JSVALUE64)
652 scratchGPR = AssemblyHelpers::selectScratchGPR(baseGPR, resultGPR);
653#else
654 scratchGPR = AssemblyHelpers::selectScratchGPR(baseGPR, resultGPR, resultTagGPR);
655#endif
656 stubJit.pushToSave(scratchGPR);
657 needToRestoreScratch = true;
658 }
659
660 MacroAssembler::JumpList failureCases;
661
662 stubJit.load8(MacroAssembler::Address(baseGPR, JSCell::indexingTypeOffset()), scratchGPR);
663 failureCases.append(stubJit.branchTest32(MacroAssembler::Zero, scratchGPR, MacroAssembler::TrustedImm32(IsArray)));
664 failureCases.append(stubJit.branchTest32(MacroAssembler::Zero, scratchGPR, MacroAssembler::TrustedImm32(IndexingShapeMask)));
665
666 stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR);
667 stubJit.load32(MacroAssembler::Address(scratchGPR, ArrayStorage::lengthOffset()), scratchGPR);
668 failureCases.append(stubJit.branch32(MacroAssembler::LessThan, scratchGPR, MacroAssembler::TrustedImm32(0)));
669
670 stubJit.move(scratchGPR, resultGPR);
671#if USE(JSVALUE64)
672 stubJit.or64(AssemblyHelpers::TrustedImm64(TagTypeNumber), resultGPR);
673#elif USE(JSVALUE32_64)
674 stubJit.move(AssemblyHelpers::TrustedImm32(0xffffffff), resultTagGPR); // JSValue::Int32Tag
675#endif
676
677 MacroAssembler::Jump success, fail;
678
679 emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
680
681 LinkBuffer patchBuffer(*vm, stubJit, codeBlock);
682
683 linkRestoreScratch(patchBuffer, needToRestoreScratch, stubInfo, success, fail, failureCases);
684
685 stubInfo.stubRoutine = FINALIZE_CODE_FOR_STUB(
686 exec->codeBlock(), patchBuffer,
687 ("GetById array length stub for %s, return point %p",
688 toCString(*exec->codeBlock()).data(), stubInfo.callReturnLocation.labelAtOffset(
689 stubInfo.patch.deltaCallToDone).executableAddress()));
690
691 RepatchBuffer repatchBuffer(codeBlock);
692 replaceWithJump(repatchBuffer, stubInfo, stubInfo.stubRoutine->code().code());
693 repatchCall(repatchBuffer, stubInfo.callReturnLocation, operationGetById);
694
695 return RetryCacheLater;
696 }
697
698 // String.length case
699 MacroAssembler::Jump failure = stubJit.branch8(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::typeInfoTypeOffset()), MacroAssembler::TrustedImm32(StringType));
700
701 stubJit.load32(MacroAssembler::Address(baseGPR, JSString::offsetOfLength()), resultGPR);
702
703#if USE(JSVALUE64)
704 stubJit.or64(AssemblyHelpers::TrustedImm64(TagTypeNumber), resultGPR);
705#elif USE(JSVALUE32_64)
706 stubJit.move(AssemblyHelpers::TrustedImm32(0xffffffff), resultTagGPR); // JSValue::Int32Tag
707#endif
708
709 MacroAssembler::Jump success = stubJit.jump();
710
711 LinkBuffer patchBuffer(*vm, stubJit, codeBlock);
712
713 patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
714 patchBuffer.link(failure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
715
716 stubInfo.stubRoutine = FINALIZE_CODE_FOR_STUB(
717 exec->codeBlock(), patchBuffer,
718 ("GetById string length stub for %s, return point %p",
719 toCString(*exec->codeBlock()).data(), stubInfo.callReturnLocation.labelAtOffset(
720 stubInfo.patch.deltaCallToDone).executableAddress()));
721
722 RepatchBuffer repatchBuffer(codeBlock);
723 replaceWithJump(repatchBuffer, stubInfo, stubInfo.stubRoutine->code().code());
724 repatchCall(repatchBuffer, stubInfo.callReturnLocation, operationGetById);
725
726 return RetryCacheLater;
727 }
728
729 // FIXME: Cache property access for immediates.
730 if (!baseValue.isCell())
731 return GiveUpOnCache;
732 JSCell* baseCell = baseValue.asCell();
733 Structure* structure = baseCell->structure();
734 if (!slot.isCacheable())
735 return GiveUpOnCache;
736
737 InlineCacheAction action = actionForCell(*vm, baseCell);
738 if (action != AttemptToCache)
739 return action;
740
741 // Optimize self access.
742 if (slot.slotBase() == baseValue
743 && slot.isCacheableValue()
744 && !slot.watchpointSet()
745 && MacroAssembler::isCompactPtrAlignedAddressOffset(maxOffsetRelativeToPatchedStorage(slot.cachedOffset()))) {
746 repatchByIdSelfAccess(*vm, codeBlock, stubInfo, structure, propertyName, slot.cachedOffset(), operationGetByIdBuildList, true);
747 stubInfo.initGetByIdSelf(*vm, codeBlock->ownerExecutable(), structure);
748 return RetryCacheLater;
749 }
750
751 repatchCall(codeBlock, stubInfo.callReturnLocation, operationGetByIdBuildList);
752 return RetryCacheLater;
753}
754
755void repatchGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
756{
757 GCSafeConcurrentJITLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
758
759 if (tryCacheGetByID(exec, baseValue, propertyName, slot, stubInfo) == GiveUpOnCache)
760 repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, operationGetById);
761}
762
763static void patchJumpToGetByIdStub(CodeBlock* codeBlock, StructureStubInfo& stubInfo, JITStubRoutine* stubRoutine)
764{
765 RELEASE_ASSERT(stubInfo.accessType == access_get_by_id_list);
766 RepatchBuffer repatchBuffer(codeBlock);
767 if (stubInfo.u.getByIdList.list->didSelfPatching()) {
768 repatchBuffer.relink(
769 stubInfo.callReturnLocation.jumpAtOffset(
770 stubInfo.patch.deltaCallToJump),
771 CodeLocationLabel(stubRoutine->code().code()));
772 return;
773 }
774
775 replaceWithJump(repatchBuffer, stubInfo, stubRoutine->code().code());
776}
777
778static InlineCacheAction tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identifier& ident, const PropertySlot& slot, StructureStubInfo& stubInfo)
779{
780 if (!baseValue.isCell()
781 || !slot.isCacheable())
782 return GiveUpOnCache;
783
784 JSCell* baseCell = baseValue.asCell();
785 bool loadTargetFromProxy = false;
786 if (baseCell->type() == PureForwardingProxyType) {
787 baseValue = jsCast<JSProxy*>(baseCell)->target();
788 baseCell = baseValue.asCell();
789 loadTargetFromProxy = true;
790 }
791
792 VM* vm = &exec->vm();
793 CodeBlock* codeBlock = exec->codeBlock();
794
795 InlineCacheAction action = actionForCell(*vm, baseCell);
796 if (action != AttemptToCache)
797 return action;
798
799 Structure* structure = baseCell->structure(*vm);
800 TypeInfo typeInfo = structure->typeInfo();
801
802 if (stubInfo.patch.spillMode == NeedToSpill) {
803 // We cannot do as much inline caching if the registers were not flushed prior to this GetById. In particular,
804 // non-Value cached properties require planting calls, which requires registers to have been flushed. Thus,
805 // if registers were not flushed, don't do non-Value caching.
806 if (!slot.isCacheableValue())
807 return GiveUpOnCache;
808 }
809
810 PropertyOffset offset = slot.cachedOffset();
811 StructureChain* prototypeChain = 0;
812 size_t count = 0;
813
814 if (slot.slotBase() != baseValue) {
815 if (typeInfo.prohibitsPropertyCaching() || structure->isDictionary())
816 return GiveUpOnCache;
817
818 count = normalizePrototypeChainForChainAccess(
819 exec, baseValue, slot.slotBase(), ident, offset);
820 if (count == InvalidPrototypeChain)
821 return GiveUpOnCache;
822 prototypeChain = structure->prototypeChain(exec);
823 }
824
825 PolymorphicGetByIdList* list = PolymorphicGetByIdList::from(stubInfo);
826 if (list->isFull()) {
827 // We need this extra check because of recursion.
828 return GiveUpOnCache;
829 }
830
831 RefPtr<JITStubRoutine> stubRoutine;
832 generateByIdStub(
833 exec, kindFor(slot), ident, customFor(slot), stubInfo, prototypeChain, count, offset,
834 structure, loadTargetFromProxy, slot.watchpointSet(),
835 stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
836 CodeLocationLabel(list->currentSlowPathTarget(stubInfo)), stubRoutine);
837
838 GetByIdAccess::AccessType accessType;
839 if (slot.isCacheableValue())
840 accessType = slot.watchpointSet() ? GetByIdAccess::WatchedStub : GetByIdAccess::SimpleStub;
841 else if (slot.isCacheableGetter())
842 accessType = GetByIdAccess::Getter;
843 else
844 accessType = GetByIdAccess::CustomGetter;
845
846 list->addAccess(GetByIdAccess(
847 *vm, codeBlock->ownerExecutable(), accessType, stubRoutine, structure,
848 prototypeChain, count));
849
850 patchJumpToGetByIdStub(codeBlock, stubInfo, stubRoutine.get());
851
852 return list->isFull() ? GiveUpOnCache : RetryCacheLater;
853}
854
855void buildGetByIDList(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
856{
857 GCSafeConcurrentJITLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
858
859 if (tryBuildGetByIDList(exec, baseValue, propertyName, slot, stubInfo) == GiveUpOnCache)
860 repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, operationGetById);
861}
862
863static V_JITOperation_ESsiJJI appropriateGenericPutByIdFunction(const PutPropertySlot &slot, PutKind putKind)
864{
865 if (slot.isStrictMode()) {
866 if (putKind == Direct)
867 return operationPutByIdDirectStrict;
868 return operationPutByIdStrict;
869 }
870 if (putKind == Direct)
871 return operationPutByIdDirectNonStrict;
872 return operationPutByIdNonStrict;
873}
874
875static V_JITOperation_ESsiJJI appropriateListBuildingPutByIdFunction(const PutPropertySlot &slot, PutKind putKind)
876{
877 if (slot.isStrictMode()) {
878 if (putKind == Direct)
879 return operationPutByIdDirectStrictBuildList;
880 return operationPutByIdStrictBuildList;
881 }
882 if (putKind == Direct)
883 return operationPutByIdDirectNonStrictBuildList;
884 return operationPutByIdNonStrictBuildList;
885}
886
887static void emitPutReplaceStub(
888 ExecState* exec,
889 JSValue,
890 const Identifier&,
891 const PutPropertySlot& slot,
892 StructureStubInfo& stubInfo,
893 PutKind,
894 Structure* structure,
895 CodeLocationLabel failureLabel,
896 RefPtr<JITStubRoutine>& stubRoutine)
897{
898 VM* vm = &exec->vm();
899 GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
900#if USE(JSVALUE32_64)
901 GPRReg valueTagGPR = static_cast<GPRReg>(stubInfo.patch.valueTagGPR);
902#endif
903 GPRReg valueGPR = static_cast<GPRReg>(stubInfo.patch.valueGPR);
904
905 ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters);
906 allocator.lock(baseGPR);
907#if USE(JSVALUE32_64)
908 allocator.lock(valueTagGPR);
909#endif
910 allocator.lock(valueGPR);
911
912 GPRReg scratchGPR1 = allocator.allocateScratchGPR();
913
914 CCallHelpers stubJit(vm, exec->codeBlock());
915
916 allocator.preserveReusedRegistersByPushing(stubJit);
917
918 MacroAssembler::Jump badStructure = branchStructure(stubJit,
919 MacroAssembler::NotEqual,
920 MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()),
921 structure);
922
923#if USE(JSVALUE64)
924 if (isInlineOffset(slot.cachedOffset()))
925 stubJit.store64(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue)));
926 else {
927 stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR1);
928 stubJit.store64(valueGPR, MacroAssembler::Address(scratchGPR1, offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue)));
929 }
930#elif USE(JSVALUE32_64)
931 if (isInlineOffset(slot.cachedOffset())) {
932 stubJit.store32(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
933 stubJit.store32(valueTagGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
934 } else {
935 stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR1);
936 stubJit.store32(valueGPR, MacroAssembler::Address(scratchGPR1, offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
937 stubJit.store32(valueTagGPR, MacroAssembler::Address(scratchGPR1, offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
938 }
939#endif
940
941 MacroAssembler::Jump success;
942 MacroAssembler::Jump failure;
943
944 if (allocator.didReuseRegisters()) {
945 allocator.restoreReusedRegistersByPopping(stubJit);
946 success = stubJit.jump();
947
948 badStructure.link(&stubJit);
949 allocator.restoreReusedRegistersByPopping(stubJit);
950 failure = stubJit.jump();
951 } else {
952 success = stubJit.jump();
953 failure = badStructure;
954 }
955
956 LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock());
957 patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
958 patchBuffer.link(failure, failureLabel);
959
960 stubRoutine = FINALIZE_CODE_FOR_STUB(
961 exec->codeBlock(), patchBuffer,
962 ("PutById replace stub for %s, return point %p",
963 toCString(*exec->codeBlock()).data(), stubInfo.callReturnLocation.labelAtOffset(
964 stubInfo.patch.deltaCallToDone).executableAddress()));
965}
966
967static void emitPutTransitionStub(
968 ExecState* exec,
969 JSValue,
970 const Identifier&,
971 const PutPropertySlot& slot,
972 StructureStubInfo& stubInfo,
973 PutKind putKind,
974 Structure* structure,
975 Structure* oldStructure,
976 StructureChain* prototypeChain,
977 CodeLocationLabel failureLabel,
978 RefPtr<JITStubRoutine>& stubRoutine)
979{
980 VM* vm = &exec->vm();
981
982 GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
983#if USE(JSVALUE32_64)
984 GPRReg valueTagGPR = static_cast<GPRReg>(stubInfo.patch.valueTagGPR);
985#endif
986 GPRReg valueGPR = static_cast<GPRReg>(stubInfo.patch.valueGPR);
987
988 ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters);
989 allocator.lock(baseGPR);
990#if USE(JSVALUE32_64)
991 allocator.lock(valueTagGPR);
992#endif
993 allocator.lock(valueGPR);
994
995 CCallHelpers stubJit(vm);
996
997 bool needThirdScratch = false;
998 if (structure->outOfLineCapacity() != oldStructure->outOfLineCapacity()
999 && oldStructure->outOfLineCapacity()) {
1000 needThirdScratch = true;
1001 }
1002
1003 GPRReg scratchGPR1 = allocator.allocateScratchGPR();
1004 ASSERT(scratchGPR1 != baseGPR);
1005 ASSERT(scratchGPR1 != valueGPR);
1006
1007 GPRReg scratchGPR2 = allocator.allocateScratchGPR();
1008 ASSERT(scratchGPR2 != baseGPR);
1009 ASSERT(scratchGPR2 != valueGPR);
1010 ASSERT(scratchGPR2 != scratchGPR1);
1011
1012 GPRReg scratchGPR3;
1013 if (needThirdScratch) {
1014 scratchGPR3 = allocator.allocateScratchGPR();
1015 ASSERT(scratchGPR3 != baseGPR);
1016 ASSERT(scratchGPR3 != valueGPR);
1017 ASSERT(scratchGPR3 != scratchGPR1);
1018 ASSERT(scratchGPR3 != scratchGPR2);
1019 } else
1020 scratchGPR3 = InvalidGPRReg;
1021
1022 allocator.preserveReusedRegistersByPushing(stubJit);
1023
1024 MacroAssembler::JumpList failureCases;
1025
1026 ASSERT(oldStructure->transitionWatchpointSetHasBeenInvalidated());
1027
1028 failureCases.append(branchStructure(stubJit,
1029 MacroAssembler::NotEqual,
1030 MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()),
1031 oldStructure));
1032
1033 addStructureTransitionCheck(
1034 oldStructure->storedPrototype(), exec->codeBlock(), stubInfo, stubJit, failureCases,
1035 scratchGPR1);
1036
1037 if (putKind == NotDirect) {
1038 for (WriteBarrier<Structure>* it = prototypeChain->head(); *it; ++it) {
1039 addStructureTransitionCheck(
1040 (*it)->storedPrototype(), exec->codeBlock(), stubInfo, stubJit, failureCases,
1041 scratchGPR1);
1042 }
1043 }
1044
1045 MacroAssembler::JumpList slowPath;
1046
1047 bool scratchGPR1HasStorage = false;
1048
1049 if (structure->outOfLineCapacity() != oldStructure->outOfLineCapacity()) {
1050 size_t newSize = structure->outOfLineCapacity() * sizeof(JSValue);
1051 CopiedAllocator* copiedAllocator = &vm->heap.storageAllocator();
1052
1053 if (!oldStructure->outOfLineCapacity()) {
1054 stubJit.loadPtr(&copiedAllocator->m_currentRemaining, scratchGPR1);
1055 slowPath.append(stubJit.branchSubPtr(MacroAssembler::Signed, MacroAssembler::TrustedImm32(newSize), scratchGPR1));
1056 stubJit.storePtr(scratchGPR1, &copiedAllocator->m_currentRemaining);
1057 stubJit.negPtr(scratchGPR1);
1058 stubJit.addPtr(MacroAssembler::AbsoluteAddress(&copiedAllocator->m_currentPayloadEnd), scratchGPR1);
1059 stubJit.addPtr(MacroAssembler::TrustedImm32(sizeof(JSValue)), scratchGPR1);
1060 } else {
1061 size_t oldSize = oldStructure->outOfLineCapacity() * sizeof(JSValue);
1062 ASSERT(newSize > oldSize);
1063
1064 stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR3);
1065 stubJit.loadPtr(&copiedAllocator->m_currentRemaining, scratchGPR1);
1066 slowPath.append(stubJit.branchSubPtr(MacroAssembler::Signed, MacroAssembler::TrustedImm32(newSize), scratchGPR1));
1067 stubJit.storePtr(scratchGPR1, &copiedAllocator->m_currentRemaining);
1068 stubJit.negPtr(scratchGPR1);
1069 stubJit.addPtr(MacroAssembler::AbsoluteAddress(&copiedAllocator->m_currentPayloadEnd), scratchGPR1);
1070 stubJit.addPtr(MacroAssembler::TrustedImm32(sizeof(JSValue)), scratchGPR1);
1071 // We have scratchGPR1 = new storage, scratchGPR3 = old storage, scratchGPR2 = available
1072 for (size_t offset = 0; offset < oldSize; offset += sizeof(void*)) {
1073 stubJit.loadPtr(MacroAssembler::Address(scratchGPR3, -static_cast<ptrdiff_t>(offset + sizeof(JSValue) + sizeof(void*))), scratchGPR2);
1074 stubJit.storePtr(scratchGPR2, MacroAssembler::Address(scratchGPR1, -static_cast<ptrdiff_t>(offset + sizeof(JSValue) + sizeof(void*))));
1075 }
1076 }
1077
1078 stubJit.storePtr(scratchGPR1, MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()));
1079 scratchGPR1HasStorage = true;
1080 }
1081
1082 ASSERT(oldStructure->typeInfo().type() == structure->typeInfo().type());
1083 ASSERT(oldStructure->typeInfo().inlineTypeFlags() == structure->typeInfo().inlineTypeFlags());
1084 ASSERT(oldStructure->indexingType() == structure->indexingType());
1085#if USE(JSVALUE64)
1086 uint32_t val = structure->id();
1087#else
1088 uint32_t val = reinterpret_cast<uint32_t>(structure->id());
1089#endif
1090 stubJit.store32(MacroAssembler::TrustedImm32(val), MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()));
1091#if USE(JSVALUE64)
1092 if (isInlineOffset(slot.cachedOffset()))
1093 stubJit.store64(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue)));
1094 else {
1095 if (!scratchGPR1HasStorage)
1096 stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR1);
1097 stubJit.store64(valueGPR, MacroAssembler::Address(scratchGPR1, offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue)));
1098 }
1099#elif USE(JSVALUE32_64)
1100 if (isInlineOffset(slot.cachedOffset())) {
1101 stubJit.store32(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
1102 stubJit.store32(valueTagGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
1103 } else {
1104 if (!scratchGPR1HasStorage)
1105 stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR1);
1106 stubJit.store32(valueGPR, MacroAssembler::Address(scratchGPR1, offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
1107 stubJit.store32(valueTagGPR, MacroAssembler::Address(scratchGPR1, offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
1108 }
1109#endif
1110
1111 MacroAssembler::Jump success;
1112 MacroAssembler::Jump failure;
1113
1114 if (allocator.didReuseRegisters()) {
1115 allocator.restoreReusedRegistersByPopping(stubJit);
1116 success = stubJit.jump();
1117
1118 failureCases.link(&stubJit);
1119 allocator.restoreReusedRegistersByPopping(stubJit);
1120 failure = stubJit.jump();
1121 } else
1122 success = stubJit.jump();
1123
1124 MacroAssembler::Call operationCall;
1125 MacroAssembler::Jump successInSlowPath;
1126
1127 if (structure->outOfLineCapacity() != oldStructure->outOfLineCapacity()) {
1128 slowPath.link(&stubJit);
1129
1130 allocator.restoreReusedRegistersByPopping(stubJit);
1131 ScratchBuffer* scratchBuffer = vm->scratchBufferForSize(allocator.desiredScratchBufferSizeForCall());
1132 allocator.preserveUsedRegistersToScratchBufferForCall(stubJit, scratchBuffer, scratchGPR1);
1133#if USE(JSVALUE64)
1134 stubJit.setupArgumentsWithExecState(baseGPR, MacroAssembler::TrustedImmPtr(structure), MacroAssembler::TrustedImm32(slot.cachedOffset()), valueGPR);
1135#else
1136 stubJit.setupArgumentsWithExecState(baseGPR, MacroAssembler::TrustedImmPtr(structure), MacroAssembler::TrustedImm32(slot.cachedOffset()), valueGPR, valueTagGPR);
1137#endif
1138 operationCall = stubJit.call();
1139 allocator.restoreUsedRegistersFromScratchBufferForCall(stubJit, scratchBuffer, scratchGPR1);
1140 successInSlowPath = stubJit.jump();
1141 }
1142
1143 LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock());
1144 patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
1145 if (allocator.didReuseRegisters())
1146 patchBuffer.link(failure, failureLabel);
1147 else
1148 patchBuffer.link(failureCases, failureLabel);
1149 if (structure->outOfLineCapacity() != oldStructure->outOfLineCapacity()) {
1150 patchBuffer.link(operationCall, operationReallocateStorageAndFinishPut);
1151 patchBuffer.link(successInSlowPath, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
1152 }
1153
1154 stubRoutine =
1155 createJITStubRoutine(
1156 FINALIZE_CODE_FOR(
1157 exec->codeBlock(), patchBuffer,
1158 ("PutById %stransition stub (%p -> %p) for %s, return point %p",
1159 structure->outOfLineCapacity() != oldStructure->outOfLineCapacity() ? "reallocating " : "",
1160 oldStructure, structure,
1161 toCString(*exec->codeBlock()).data(), stubInfo.callReturnLocation.labelAtOffset(
1162 stubInfo.patch.deltaCallToDone).executableAddress())),
1163 *vm,
1164 exec->codeBlock()->ownerExecutable(),
1165 structure->outOfLineCapacity() != oldStructure->outOfLineCapacity(),
1166 structure);
1167}
1168
1169static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, const Identifier& ident, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
1170{
1171 if (Options::forceICFailure())
1172 return GiveUpOnCache;
1173
1174 CodeBlock* codeBlock = exec->codeBlock();
1175 VM* vm = &exec->vm();
1176
1177 if (!baseValue.isCell())
1178 return GiveUpOnCache;
1179 JSCell* baseCell = baseValue.asCell();
1180 Structure* structure = baseCell->structure();
1181 Structure* oldStructure = structure->previousID();
1182
1183 if (!slot.isCacheablePut() && !slot.isCacheableCustom() && !slot.isCacheableSetter())
1184 return GiveUpOnCache;
1185 if (!structure->propertyAccessesAreCacheable())
1186 return GiveUpOnCache;
1187
1188 // Optimize self access.
1189 if (slot.base() == baseValue && slot.isCacheablePut()) {
1190 if (slot.type() == PutPropertySlot::NewProperty) {
1191 if (structure->isDictionary())
1192 return GiveUpOnCache;
1193
1194 // Skip optimizing the case where we need a realloc, if we don't have
1195 // enough registers to make it happen.
1196 if (GPRInfo::numberOfRegisters < 6
1197 && oldStructure->outOfLineCapacity() != structure->outOfLineCapacity()
1198 && oldStructure->outOfLineCapacity())
1199 return GiveUpOnCache;
1200
1201 // Skip optimizing the case where we need realloc, and the structure has
1202 // indexing storage.
1203 // FIXME: We shouldn't skip this! Implement it!
1204 // https://bugs.webkit.org/show_bug.cgi?id=130914
1205 if (oldStructure->couldHaveIndexingHeader())
1206 return GiveUpOnCache;
1207
1208 if (normalizePrototypeChain(exec, baseCell) == InvalidPrototypeChain)
1209 return GiveUpOnCache;
1210
1211 StructureChain* prototypeChain = structure->prototypeChain(exec);
1212
1213 emitPutTransitionStub(
1214 exec, baseValue, ident, slot, stubInfo, putKind,
1215 structure, oldStructure, prototypeChain,
1216 stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase),
1217 stubInfo.stubRoutine);
1218
1219 RepatchBuffer repatchBuffer(codeBlock);
1220 repatchBuffer.relink(
1221 stubInfo.callReturnLocation.jumpAtOffset(
1222 stubInfo.patch.deltaCallToJump),
1223 CodeLocationLabel(stubInfo.stubRoutine->code().code()));
1224 repatchCall(repatchBuffer, stubInfo.callReturnLocation, appropriateListBuildingPutByIdFunction(slot, putKind));
1225
1226 stubInfo.initPutByIdTransition(*vm, codeBlock->ownerExecutable(), oldStructure, structure, prototypeChain, putKind == Direct);
1227
1228 return RetryCacheLater;
1229 }
1230
1231 if (!MacroAssembler::isPtrAlignedAddressOffset(offsetRelativeToPatchedStorage(slot.cachedOffset())))
1232 return GiveUpOnCache;
1233
1234 repatchByIdSelfAccess(*vm, codeBlock, stubInfo, structure, ident, slot.cachedOffset(), appropriateListBuildingPutByIdFunction(slot, putKind), false);
1235 stubInfo.initPutByIdReplace(*vm, codeBlock->ownerExecutable(), structure);
1236 return RetryCacheLater;
1237 }
1238 if ((slot.isCacheableCustom() || slot.isCacheableSetter())
1239 && stubInfo.patch.spillMode == DontSpill) {
1240 RefPtr<JITStubRoutine> stubRoutine;
1241
1242 StructureChain* prototypeChain = 0;
1243 PropertyOffset offset = slot.cachedOffset();
1244 size_t count = 0;
1245 if (baseValue != slot.base()) {
1246 count = normalizePrototypeChainForChainAccess(exec, baseCell, slot.base(), ident, offset);
1247 if (count == InvalidPrototypeChain)
1248 return GiveUpOnCache;
1249
1250 prototypeChain = structure->prototypeChain(exec);
1251 }
1252 PolymorphicPutByIdList* list;
1253 list = PolymorphicPutByIdList::from(putKind, stubInfo);
1254
1255 generateByIdStub(
1256 exec, kindFor(slot), ident, customFor(slot), stubInfo, prototypeChain, count,
1257 offset, structure, false, nullptr,
1258 stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
1259 stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase),
1260 stubRoutine);
1261
1262 list->addAccess(PutByIdAccess::setter(
1263 *vm, codeBlock->ownerExecutable(),
1264 slot.isCacheableSetter() ? PutByIdAccess::Setter : PutByIdAccess::CustomSetter,
1265 structure, prototypeChain, slot.customSetter(), stubRoutine));
1266
1267 RepatchBuffer repatchBuffer(codeBlock);
1268 repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), CodeLocationLabel(stubRoutine->code().code()));
1269 repatchCall(repatchBuffer, stubInfo.callReturnLocation, appropriateListBuildingPutByIdFunction(slot, putKind));
1270 RELEASE_ASSERT(!list->isFull());
1271 return RetryCacheLater;
1272 }
1273
1274 return GiveUpOnCache;
1275}
1276
1277void repatchPutByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
1278{
1279 GCSafeConcurrentJITLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
1280
1281 if (tryCachePutByID(exec, baseValue, propertyName, slot, stubInfo, putKind) == GiveUpOnCache)
1282 repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind));
1283}
1284
1285static InlineCacheAction tryBuildPutByIdList(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
1286{
1287 CodeBlock* codeBlock = exec->codeBlock();
1288 VM* vm = &exec->vm();
1289
1290 if (!baseValue.isCell())
1291 return GiveUpOnCache;
1292 JSCell* baseCell = baseValue.asCell();
1293 Structure* structure = baseCell->structure();
1294 Structure* oldStructure = structure->previousID();
1295
1296
1297 if (!slot.isCacheablePut() && !slot.isCacheableCustom() && !slot.isCacheableSetter())
1298 return GiveUpOnCache;
1299
1300 if (!structure->propertyAccessesAreCacheable())
1301 return GiveUpOnCache;
1302
1303 // Optimize self access.
1304 if (slot.base() == baseValue && slot.isCacheablePut()) {
1305 PolymorphicPutByIdList* list;
1306 RefPtr<JITStubRoutine> stubRoutine;
1307
1308 if (slot.type() == PutPropertySlot::NewProperty) {
1309 if (structure->isDictionary())
1310 return GiveUpOnCache;
1311
1312 // Skip optimizing the case where we need a realloc, if we don't have
1313 // enough registers to make it happen.
1314 if (GPRInfo::numberOfRegisters < 6
1315 && oldStructure->outOfLineCapacity() != structure->outOfLineCapacity()
1316 && oldStructure->outOfLineCapacity())
1317 return GiveUpOnCache;
1318
1319 // Skip optimizing the case where we need realloc, and the structure has
1320 // indexing storage.
1321 if (oldStructure->couldHaveIndexingHeader())
1322 return GiveUpOnCache;
1323
1324 if (normalizePrototypeChain(exec, baseCell) == InvalidPrototypeChain)
1325 return GiveUpOnCache;
1326
1327 StructureChain* prototypeChain = structure->prototypeChain(exec);
1328
1329 list = PolymorphicPutByIdList::from(putKind, stubInfo);
1330 if (list->isFull())
1331 return GiveUpOnCache; // Will get here due to recursion.
1332
1333 // We're now committed to creating the stub. Mogrify the meta-data accordingly.
1334 emitPutTransitionStub(
1335 exec, baseValue, propertyName, slot, stubInfo, putKind,
1336 structure, oldStructure, prototypeChain,
1337 CodeLocationLabel(list->currentSlowPathTarget()),
1338 stubRoutine);
1339
1340 list->addAccess(
1341 PutByIdAccess::transition(
1342 *vm, codeBlock->ownerExecutable(),
1343 oldStructure, structure, prototypeChain,
1344 stubRoutine));
1345 } else {
1346 list = PolymorphicPutByIdList::from(putKind, stubInfo);
1347 if (list->isFull())
1348 return GiveUpOnCache; // Will get here due to recursion.
1349
1350 // We're now committed to creating the stub. Mogrify the meta-data accordingly.
1351 emitPutReplaceStub(
1352 exec, baseValue, propertyName, slot, stubInfo, putKind,
1353 structure, CodeLocationLabel(list->currentSlowPathTarget()), stubRoutine);
1354
1355 list->addAccess(
1356 PutByIdAccess::replace(
1357 *vm, codeBlock->ownerExecutable(),
1358 structure, stubRoutine));
1359 }
1360
1361 RepatchBuffer repatchBuffer(codeBlock);
1362 repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), CodeLocationLabel(stubRoutine->code().code()));
1363
1364 if (list->isFull())
1365 repatchCall(repatchBuffer, stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind));
1366
1367 return RetryCacheLater;
1368 }
1369
1370 if ((slot.isCacheableCustom() || slot.isCacheableSetter())
1371 && stubInfo.patch.spillMode == DontSpill) {
1372 RefPtr<JITStubRoutine> stubRoutine;
1373 StructureChain* prototypeChain = 0;
1374 PropertyOffset offset = slot.cachedOffset();
1375 size_t count = 0;
1376 if (baseValue != slot.base()) {
1377 count = normalizePrototypeChainForChainAccess(exec, baseCell, slot.base(), propertyName, offset);
1378 if (count == InvalidPrototypeChain)
1379 return GiveUpOnCache;
1380
1381 prototypeChain = structure->prototypeChain(exec);
1382 }
1383 PolymorphicPutByIdList* list;
1384 list = PolymorphicPutByIdList::from(putKind, stubInfo);
1385
1386 generateByIdStub(
1387 exec, kindFor(slot), propertyName, customFor(slot), stubInfo, prototypeChain, count,
1388 offset, structure, false, nullptr,
1389 stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
1390 CodeLocationLabel(list->currentSlowPathTarget()),
1391 stubRoutine);
1392
1393 list->addAccess(PutByIdAccess::setter(
1394 *vm, codeBlock->ownerExecutable(),
1395 slot.isCacheableSetter() ? PutByIdAccess::Setter : PutByIdAccess::CustomSetter,
1396 structure, prototypeChain, slot.customSetter(), stubRoutine));
1397
1398 RepatchBuffer repatchBuffer(codeBlock);
1399 repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), CodeLocationLabel(stubRoutine->code().code()));
1400 if (list->isFull())
1401 repatchCall(repatchBuffer, stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind));
1402
1403 return RetryCacheLater;
1404 }
1405 return GiveUpOnCache;
1406}
1407
1408void buildPutByIdList(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
1409{
1410 GCSafeConcurrentJITLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
1411
1412 if (tryBuildPutByIdList(exec, baseValue, propertyName, slot, stubInfo, putKind) == GiveUpOnCache)
1413 repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind));
1414}
1415
1416static InlineCacheAction tryRepatchIn(
1417 ExecState* exec, JSCell* base, const Identifier& ident, bool wasFound,
1418 const PropertySlot& slot, StructureStubInfo& stubInfo)
1419{
1420 if (Options::forceICFailure())
1421 return GiveUpOnCache;
1422
1423 if (!base->structure()->propertyAccessesAreCacheable())
1424 return GiveUpOnCache;
1425
1426 if (wasFound) {
1427 if (!slot.isCacheable())
1428 return GiveUpOnCache;
1429 }
1430
1431 CodeBlock* codeBlock = exec->codeBlock();
1432 VM* vm = &exec->vm();
1433 Structure* structure = base->structure();
1434
1435 PropertyOffset offsetIgnored;
1436 size_t count = normalizePrototypeChainForChainAccess(exec, base, wasFound ? slot.slotBase() : JSValue(), ident, offsetIgnored);
1437 if (count == InvalidPrototypeChain)
1438 return GiveUpOnCache;
1439
1440 PolymorphicAccessStructureList* polymorphicStructureList;
1441 int listIndex;
1442
1443 CodeLocationLabel successLabel = stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone);
1444 CodeLocationLabel slowCaseLabel;
1445
1446 if (stubInfo.accessType == access_unset) {
1447 polymorphicStructureList = new PolymorphicAccessStructureList();
1448 stubInfo.initInList(polymorphicStructureList, 0);
1449 slowCaseLabel = stubInfo.callReturnLocation.labelAtOffset(
1450 stubInfo.patch.deltaCallToSlowCase);
1451 listIndex = 0;
1452 } else {
1453 RELEASE_ASSERT(stubInfo.accessType == access_in_list);
1454 polymorphicStructureList = stubInfo.u.inList.structureList;
1455 listIndex = stubInfo.u.inList.listSize;
1456 slowCaseLabel = CodeLocationLabel(polymorphicStructureList->list[listIndex - 1].stubRoutine->code().code());
1457
1458 if (listIndex == POLYMORPHIC_LIST_CACHE_SIZE)
1459 return GiveUpOnCache;
1460 }
1461
1462 StructureChain* chain = structure->prototypeChain(exec);
1463 RefPtr<JITStubRoutine> stubRoutine;
1464
1465 {
1466 GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
1467 GPRReg resultGPR = static_cast<GPRReg>(stubInfo.patch.valueGPR);
1468 GPRReg scratchGPR = TempRegisterSet(stubInfo.patch.usedRegisters).getFreeGPR();
1469
1470 CCallHelpers stubJit(vm);
1471
1472 bool needToRestoreScratch;
1473 if (scratchGPR == InvalidGPRReg) {
1474 scratchGPR = AssemblyHelpers::selectScratchGPR(baseGPR, resultGPR);
1475 stubJit.pushToSave(scratchGPR);
1476 needToRestoreScratch = true;
1477 } else
1478 needToRestoreScratch = false;
1479
1480 MacroAssembler::JumpList failureCases;
1481 failureCases.append(branchStructure(stubJit,
1482 MacroAssembler::NotEqual,
1483 MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()),
1484 structure));
1485
1486 CodeBlock* codeBlock = exec->codeBlock();
1487 if (structure->typeInfo().newImpurePropertyFiresWatchpoints())
1488 vm->registerWatchpointForImpureProperty(ident, stubInfo.addWatchpoint(codeBlock));
1489
1490 if (slot.watchpointSet())
1491 slot.watchpointSet()->add(stubInfo.addWatchpoint(codeBlock));
1492
1493 Structure* currStructure = structure;
1494 WriteBarrier<Structure>* it = chain->head();
1495 for (unsigned i = 0; i < count; ++i, ++it) {
1496 JSObject* prototype = asObject(currStructure->prototypeForLookup(exec));
1497 Structure* protoStructure = prototype->structure();
1498 addStructureTransitionCheck(
1499 prototype, protoStructure, exec->codeBlock(), stubInfo, stubJit,
1500 failureCases, scratchGPR);
1501 if (protoStructure->typeInfo().newImpurePropertyFiresWatchpoints())
1502 vm->registerWatchpointForImpureProperty(ident, stubInfo.addWatchpoint(codeBlock));
1503 currStructure = it->get();
1504 }
1505
1506#if USE(JSVALUE64)
1507 stubJit.move(MacroAssembler::TrustedImm64(JSValue::encode(jsBoolean(wasFound))), resultGPR);
1508#else
1509 stubJit.move(MacroAssembler::TrustedImm32(wasFound), resultGPR);
1510#endif
1511
1512 MacroAssembler::Jump success, fail;
1513
1514 emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
1515
1516 LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock());
1517
1518 linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, successLabel, slowCaseLabel);
1519
1520 stubRoutine = FINALIZE_CODE_FOR_STUB(
1521 exec->codeBlock(), patchBuffer,
1522 ("In (found = %s) stub for %s, return point %p",
1523 wasFound ? "yes" : "no", toCString(*exec->codeBlock()).data(),
1524 successLabel.executableAddress()));
1525 }
1526
1527 polymorphicStructureList->list[listIndex].set(*vm, codeBlock->ownerExecutable(), stubRoutine, structure, true);
1528 stubInfo.u.inList.listSize++;
1529
1530 RepatchBuffer repatchBuffer(codeBlock);
1531 repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), CodeLocationLabel(stubRoutine->code().code()));
1532
1533 return listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1) ? RetryCacheLater : GiveUpOnCache;
1534}
1535
1536void repatchIn(
1537 ExecState* exec, JSCell* base, const Identifier& ident, bool wasFound,
1538 const PropertySlot& slot, StructureStubInfo& stubInfo)
1539{
1540 if (tryRepatchIn(exec, base, ident, wasFound, slot, stubInfo) == GiveUpOnCache)
1541 repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, operationIn);
1542}
1543
1544static void linkSlowFor(
1545 RepatchBuffer& repatchBuffer, VM* vm, CallLinkInfo& callLinkInfo,
1546 CodeSpecializationKind kind, RegisterPreservationMode registers)
1547{
1548 repatchBuffer.relink(
1549 callLinkInfo.callReturnLocation,
1550 vm->getCTIStub(virtualThunkGeneratorFor(kind, registers)).code());
1551}
1552
1553void linkFor(
1554 ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCodeBlock,
1555 JSFunction* callee, MacroAssemblerCodePtr codePtr, CodeSpecializationKind kind,
1556 RegisterPreservationMode registers)
1557{
1558 ASSERT(!callLinkInfo.stub);
1559
1560 CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();
1561
1562 // If you're being call-linked from a DFG caller then you obviously didn't get inlined.
1563 if (calleeCodeBlock && JITCode::isOptimizingJIT(callerCodeBlock->jitType()))
1564 calleeCodeBlock->m_shouldAlwaysBeInlined = false;
1565
1566 VM* vm = callerCodeBlock->vm();
1567
1568 RepatchBuffer repatchBuffer(callerCodeBlock);
1569
1570 ASSERT(!callLinkInfo.isLinked());
1571 callLinkInfo.callee.set(exec->callerFrame()->vm(), callLinkInfo.hotPathBegin, callerCodeBlock->ownerExecutable(), callee);
1572 callLinkInfo.lastSeenCallee.set(exec->callerFrame()->vm(), callerCodeBlock->ownerExecutable(), callee);
1573 if (shouldShowDisassemblyFor(callerCodeBlock))
1574 dataLog("Linking call in ", *callerCodeBlock, " at ", callLinkInfo.codeOrigin, " to ", pointerDump(calleeCodeBlock), ", entrypoint at ", codePtr, "\n");
1575 repatchBuffer.relink(callLinkInfo.hotPathOther, codePtr);
1576
1577 if (calleeCodeBlock)
1578 calleeCodeBlock->linkIncomingCall(exec->callerFrame(), &callLinkInfo);
1579
1580 if (kind == CodeForCall) {
1581 repatchBuffer.relink(callLinkInfo.callReturnLocation, vm->getCTIStub(linkClosureCallThunkGeneratorFor(registers)).code());
1582 return;
1583 }
1584
1585 ASSERT(kind == CodeForConstruct);
1586 linkSlowFor(repatchBuffer, vm, callLinkInfo, CodeForConstruct, registers);
1587}
1588
1589void linkSlowFor(
1590 ExecState* exec, CallLinkInfo& callLinkInfo, CodeSpecializationKind kind,
1591 RegisterPreservationMode registers)
1592{
1593 CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();
1594 VM* vm = callerCodeBlock->vm();
1595
1596 RepatchBuffer repatchBuffer(callerCodeBlock);
1597
1598 linkSlowFor(repatchBuffer, vm, callLinkInfo, kind, registers);
1599}
1600
1601void linkClosureCall(
1602 ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCodeBlock,
1603 Structure* structure, ExecutableBase* executable, MacroAssemblerCodePtr codePtr,
1604 RegisterPreservationMode registers)
1605{
1606 ASSERT(!callLinkInfo.stub);
1607
1608 CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();
1609 VM* vm = callerCodeBlock->vm();
1610
1611 GPRReg calleeGPR = static_cast<GPRReg>(callLinkInfo.calleeGPR);
1612
1613 CCallHelpers stubJit(vm, callerCodeBlock);
1614
1615 CCallHelpers::JumpList slowPath;
1616
1617 ptrdiff_t offsetToFrame = -sizeof(CallerFrameAndPC);
1618
1619 if (!ASSERT_DISABLED) {
1620 CCallHelpers::Jump okArgumentCount = stubJit.branch32(
1621 CCallHelpers::Below, CCallHelpers::Address(CCallHelpers::stackPointerRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ArgumentCount) + offsetToFrame + PayloadOffset), CCallHelpers::TrustedImm32(10000000));
1622 stubJit.abortWithReason(RepatchInsaneArgumentCount);
1623 okArgumentCount.link(&stubJit);
1624 }
1625
1626#if USE(JSVALUE64)
1627 // We can safely clobber everything except the calleeGPR. We can't rely on tagMaskRegister
1628 // being set. So we do this the hard way.
1629 GPRReg scratch = AssemblyHelpers::selectScratchGPR(calleeGPR);
1630 stubJit.move(MacroAssembler::TrustedImm64(TagMask), scratch);
1631 slowPath.append(stubJit.branchTest64(CCallHelpers::NonZero, calleeGPR, scratch));
1632#else
1633 // We would have already checked that the callee is a cell.
1634#endif
1635
1636 slowPath.append(
1637 branchStructure(stubJit,
1638 CCallHelpers::NotEqual,
1639 CCallHelpers::Address(calleeGPR, JSCell::structureIDOffset()),
1640 structure));
1641
1642 slowPath.append(
1643 stubJit.branchPtr(
1644 CCallHelpers::NotEqual,
1645 CCallHelpers::Address(calleeGPR, JSFunction::offsetOfExecutable()),
1646 CCallHelpers::TrustedImmPtr(executable)));
1647
1648 stubJit.loadPtr(
1649 CCallHelpers::Address(calleeGPR, JSFunction::offsetOfScopeChain()),
1650 GPRInfo::returnValueGPR);
1651
1652#if USE(JSVALUE64)
1653 stubJit.store64(
1654 GPRInfo::returnValueGPR,
1655 CCallHelpers::Address(MacroAssembler::stackPointerRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain) + offsetToFrame));
1656#else
1657 stubJit.storePtr(
1658 GPRInfo::returnValueGPR,
1659 CCallHelpers::Address(MacroAssembler::stackPointerRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload) + offsetToFrame));
1660 stubJit.store32(
1661 CCallHelpers::TrustedImm32(JSValue::CellTag),
1662 CCallHelpers::Address(MacroAssembler::stackPointerRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag) + offsetToFrame));
1663#endif
1664
1665 AssemblyHelpers::Call call = stubJit.nearCall();
1666 AssemblyHelpers::Jump done = stubJit.jump();
1667
1668 slowPath.link(&stubJit);
1669 stubJit.move(calleeGPR, GPRInfo::regT0);
1670#if USE(JSVALUE32_64)
1671 stubJit.move(CCallHelpers::TrustedImm32(JSValue::CellTag), GPRInfo::regT1);
1672#endif
1673 stubJit.move(CCallHelpers::TrustedImmPtr(&callLinkInfo), GPRInfo::regT2);
1674 stubJit.move(CCallHelpers::TrustedImmPtr(callLinkInfo.callReturnLocation.executableAddress()), GPRInfo::regT4);
1675
1676 stubJit.restoreReturnAddressBeforeReturn(GPRInfo::regT4);
1677 AssemblyHelpers::Jump slow = stubJit.jump();
1678
1679 LinkBuffer patchBuffer(*vm, stubJit, callerCodeBlock);
1680
1681 patchBuffer.link(call, FunctionPtr(codePtr.executableAddress()));
1682 if (JITCode::isOptimizingJIT(callerCodeBlock->jitType()))
1683 patchBuffer.link(done, callLinkInfo.callReturnLocation.labelAtOffset(0));
1684 else
1685 patchBuffer.link(done, callLinkInfo.hotPathOther.labelAtOffset(0));
1686 patchBuffer.link(slow, CodeLocationLabel(vm->getCTIStub(virtualThunkGeneratorFor(CodeForCall, registers)).code()));
1687
1688 RefPtr<ClosureCallStubRoutine> stubRoutine = adoptRef(new ClosureCallStubRoutine(
1689 FINALIZE_CODE_FOR(
1690 callerCodeBlock, patchBuffer,
1691 ("Closure call stub for %s, return point %p, target %p (%s)",
1692 toCString(*callerCodeBlock).data(), callLinkInfo.callReturnLocation.labelAtOffset(0).executableAddress(),
1693 codePtr.executableAddress(), toCString(pointerDump(calleeCodeBlock)).data())),
1694 *vm, callerCodeBlock->ownerExecutable(), structure, executable, callLinkInfo.codeOrigin));
1695
1696 RepatchBuffer repatchBuffer(callerCodeBlock);
1697
1698 repatchBuffer.replaceWithJump(
1699 RepatchBuffer::startOfBranchPtrWithPatchOnRegister(callLinkInfo.hotPathBegin),
1700 CodeLocationLabel(stubRoutine->code().code()));
1701 linkSlowFor(repatchBuffer, vm, callLinkInfo, CodeForCall, registers);
1702
1703 callLinkInfo.stub = stubRoutine.release();
1704
1705 ASSERT(!calleeCodeBlock || calleeCodeBlock->isIncomingCallAlreadyLinked(&callLinkInfo));
1706}
1707
1708void resetGetByID(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo)
1709{
1710 repatchCall(repatchBuffer, stubInfo.callReturnLocation, operationGetByIdOptimize);
1711 CodeLocationDataLabel32 structureLabel = stubInfo.callReturnLocation.dataLabel32AtOffset(-(intptr_t)stubInfo.patch.deltaCheckImmToCall);
1712 if (MacroAssembler::canJumpReplacePatchableBranch32WithPatch()) {
1713 repatchBuffer.revertJumpReplacementToPatchableBranch32WithPatch(
1714 RepatchBuffer::startOfPatchableBranch32WithPatchOnAddress(structureLabel),
1715 MacroAssembler::Address(
1716 static_cast<MacroAssembler::RegisterID>(stubInfo.patch.baseGPR),
1717 JSCell::structureIDOffset()),
1718 static_cast<int32_t>(unusedPointer));
1719 }
1720 repatchBuffer.repatch(structureLabel, static_cast<int32_t>(unusedPointer));
1721#if USE(JSVALUE64)
1722 repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.deltaCallToLoadOrStore), 0);
1723#else
1724 repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.deltaCallToTagLoadOrStore), 0);
1725 repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.deltaCallToPayloadLoadOrStore), 0);
1726#endif
1727 repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
1728}
1729
1730void resetPutByID(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo)
1731{
1732 V_JITOperation_ESsiJJI unoptimizedFunction = bitwise_cast<V_JITOperation_ESsiJJI>(readCallTarget(repatchBuffer, stubInfo.callReturnLocation).executableAddress());
1733 V_JITOperation_ESsiJJI optimizedFunction;
1734 if (unoptimizedFunction == operationPutByIdStrict || unoptimizedFunction == operationPutByIdStrictBuildList)
1735 optimizedFunction = operationPutByIdStrictOptimize;
1736 else if (unoptimizedFunction == operationPutByIdNonStrict || unoptimizedFunction == operationPutByIdNonStrictBuildList)
1737 optimizedFunction = operationPutByIdNonStrictOptimize;
1738 else if (unoptimizedFunction == operationPutByIdDirectStrict || unoptimizedFunction == operationPutByIdDirectStrictBuildList)
1739 optimizedFunction = operationPutByIdDirectStrictOptimize;
1740 else {
1741 ASSERT(unoptimizedFunction == operationPutByIdDirectNonStrict || unoptimizedFunction == operationPutByIdDirectNonStrictBuildList);
1742 optimizedFunction = operationPutByIdDirectNonStrictOptimize;
1743 }
1744 repatchCall(repatchBuffer, stubInfo.callReturnLocation, optimizedFunction);
1745 CodeLocationDataLabel32 structureLabel = stubInfo.callReturnLocation.dataLabel32AtOffset(-(intptr_t)stubInfo.patch.deltaCheckImmToCall);
1746 if (MacroAssembler::canJumpReplacePatchableBranch32WithPatch()) {
1747 repatchBuffer.revertJumpReplacementToPatchableBranch32WithPatch(
1748 RepatchBuffer::startOfPatchableBranch32WithPatchOnAddress(structureLabel),
1749 MacroAssembler::Address(
1750 static_cast<MacroAssembler::RegisterID>(stubInfo.patch.baseGPR),
1751 JSCell::structureIDOffset()),
1752 static_cast<int32_t>(unusedPointer));
1753 }
1754 repatchBuffer.repatch(structureLabel, static_cast<int32_t>(unusedPointer));
1755#if USE(JSVALUE64)
1756 repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.deltaCallToLoadOrStore), 0);
1757#else
1758 repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.deltaCallToTagLoadOrStore), 0);
1759 repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.deltaCallToPayloadLoadOrStore), 0);
1760#endif
1761 repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
1762}
1763
1764void resetIn(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo)
1765{
1766 repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
1767}
1768
1769} // namespace JSC
1770
1771#endif