initial import
[vuplus_webkit] / Source / JavaScriptCore / dfg / DFGRepatch.cpp
1 /*
2  * Copyright (C) 2011 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 "DFGRepatch.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "DFGJITCodeGenerator.h"
32 #include "LinkBuffer.h"
33 #include "Operations.h"
34 #include "RepatchBuffer.h"
35
36 namespace JSC { namespace DFG {
37
38 static void dfgRepatchCall(CodeBlock* codeblock, CodeLocationCall call, FunctionPtr newCalleeFunction)
39 {
40     RepatchBuffer repatchBuffer(codeblock);
41     repatchBuffer.relink(call, newCalleeFunction);
42 }
43
44 static void dfgRepatchByIdSelfAccess(CodeBlock* codeBlock, StructureStubInfo& stubInfo, Structure* structure, size_t offset, const FunctionPtr &slowPathFunction, bool compact)
45 {
46     RepatchBuffer repatchBuffer(codeBlock);
47
48     // Only optimize once!
49     repatchBuffer.relink(stubInfo.callReturnLocation, slowPathFunction);
50
51     // Patch the structure check & the offset of the load.
52     repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.u.unset.deltaCheckImmToCall), structure);
53     if (compact)
54         repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.u.unset.deltaCallToLoadOrStore), sizeof(JSValue) * offset);
55     else
56         repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.u.unset.deltaCallToLoadOrStore), sizeof(JSValue) * offset);
57 }
58
59 static void emitRestoreScratch(MacroAssembler& stubJit, bool needToRestoreScratch, GPRReg scratchGPR, MacroAssembler::Jump& success, MacroAssembler::Jump& fail, MacroAssembler::JumpList failureCases)
60 {
61     if (needToRestoreScratch) {
62         stubJit.pop(scratchGPR);
63         
64         success = stubJit.jump();
65         
66         // link failure cases here, so we can pop scratchGPR, and then jump back.
67         failureCases.link(&stubJit);
68         
69         stubJit.pop(scratchGPR);
70         
71         fail = stubJit.jump();
72         return;
73     }
74     
75     success = stubJit.jump();
76 }
77
78 static void linkRestoreScratch(LinkBuffer& patchBuffer, bool needToRestoreScratch, MacroAssembler::Jump success, MacroAssembler::Jump fail, MacroAssembler::JumpList failureCases, CodeLocationLabel successLabel, CodeLocationLabel slowCaseBegin)
79 {
80     patchBuffer.link(success, successLabel);
81         
82     if (needToRestoreScratch) {
83         patchBuffer.link(fail, slowCaseBegin);
84         return;
85     }
86     
87     // link failure cases directly back to normal path
88     patchBuffer.link(failureCases, slowCaseBegin);
89 }
90
91 static void linkRestoreScratch(LinkBuffer& patchBuffer, bool needToRestoreScratch, StructureStubInfo& stubInfo, MacroAssembler::Jump success, MacroAssembler::Jump fail, MacroAssembler::JumpList failureCases)
92 {
93     linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToDone), stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToSlowCase));
94 }
95
96 static void generateProtoChainAccessStub(ExecState* exec, StructureStubInfo& stubInfo, StructureChain* chain, size_t count, size_t offset, Structure* structure, CodeLocationLabel successLabel, CodeLocationLabel slowCaseLabel, MacroAssemblerCodeRef& stubRoutine)
97 {
98     JSGlobalData* globalData = &exec->globalData();
99
100     MacroAssembler stubJit;
101         
102     GPRReg baseGPR = static_cast<GPRReg>(stubInfo.baseGPR);
103     GPRReg resultGPR = static_cast<GPRReg>(stubInfo.valueGPR);
104     GPRReg scratchGPR = static_cast<GPRReg>(stubInfo.scratchGPR);
105     bool needToRestoreScratch = false;
106     
107     if (scratchGPR == InvalidGPRReg) {
108         scratchGPR = JITCodeGenerator::selectScratchGPR(baseGPR, resultGPR);
109         stubJit.push(scratchGPR);
110         needToRestoreScratch = true;
111     }
112     
113     MacroAssembler::JumpList failureCases;
114     
115     failureCases.append(stubJit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(structure)));
116     
117     Structure* currStructure = structure;
118     WriteBarrier<Structure>* it = chain->head();
119     JSObject* protoObject = 0;
120     for (unsigned i = 0; i < count; ++i, ++it) {
121         protoObject = asObject(currStructure->prototypeForLookup(exec));
122         stubJit.move(MacroAssembler::TrustedImmPtr(protoObject), scratchGPR);
123         failureCases.append(stubJit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(scratchGPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(protoObject->structure())));
124         currStructure = it->get();
125     }
126     
127     if (protoObject->structure()->isUsingInlineStorage())
128         stubJit.loadPtr(MacroAssembler::Address(scratchGPR, JSObject::offsetOfInlineStorage() + offset * sizeof(JSValue)), resultGPR);
129     else
130         stubJit.loadPtr(protoObject->addressOfPropertyAtOffset(offset), resultGPR);
131         
132     MacroAssembler::Jump success, fail;
133     
134     emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
135     
136     LinkBuffer patchBuffer(*globalData, &stubJit);
137     
138     linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, successLabel, slowCaseLabel);
139     
140     stubRoutine = patchBuffer.finalizeCode();
141 }
142
143 static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
144 {
145     // FIXME: Write a test that proves we need to check for recursion here just
146     // like the interpreter does, then add a check for recursion.
147
148     CodeBlock* codeBlock = exec->codeBlock();
149     JSGlobalData* globalData = &exec->globalData();
150     
151     if (isJSArray(globalData, baseValue) && propertyName == exec->propertyNames().length) {
152         GPRReg baseGPR = static_cast<GPRReg>(stubInfo.baseGPR);
153         GPRReg resultGPR = static_cast<GPRReg>(stubInfo.valueGPR);
154         GPRReg scratchGPR = static_cast<GPRReg>(stubInfo.scratchGPR);
155         bool needToRestoreScratch = false;
156         
157         MacroAssembler stubJit;
158         
159         if (scratchGPR == InvalidGPRReg) {
160             scratchGPR = JITCodeGenerator::selectScratchGPR(baseGPR, resultGPR);
161             stubJit.push(scratchGPR);
162             needToRestoreScratch = true;
163         }
164         
165         MacroAssembler::JumpList failureCases;
166         
167         failureCases.append(stubJit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR), MacroAssembler::TrustedImmPtr(globalData->jsArrayVPtr)));
168         
169         stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), scratchGPR);
170         stubJit.load32(MacroAssembler::Address(scratchGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), scratchGPR);
171         failureCases.append(stubJit.branch32(MacroAssembler::LessThan, scratchGPR, MacroAssembler::TrustedImm32(0)));
172         
173         stubJit.orPtr(GPRInfo::tagTypeNumberRegister, scratchGPR, resultGPR);
174
175         MacroAssembler::Jump success, fail;
176         
177         emitRestoreScratch(stubJit, needToRestoreScratch, scratchGPR, success, fail, failureCases);
178         
179         LinkBuffer patchBuffer(*globalData, &stubJit);
180         
181         linkRestoreScratch(patchBuffer, needToRestoreScratch, stubInfo, success, fail, failureCases);
182         
183         stubInfo.stubRoutine = patchBuffer.finalizeCode();
184         
185         RepatchBuffer repatchBuffer(codeBlock);
186         repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine.code()));
187         repatchBuffer.relink(stubInfo.callReturnLocation, operationGetById);
188         
189         return true;
190     }
191     
192     // FIXME: should support length access for String.
193
194     // FIXME: Cache property access for immediates.
195     if (!baseValue.isCell())
196         return false;
197     JSCell* baseCell = baseValue.asCell();
198     Structure* structure = baseCell->structure();
199     if (!slot.isCacheable())
200         return false;
201     if (structure->isUncacheableDictionary() || structure->typeInfo().prohibitsPropertyCaching())
202         return false;
203
204     // Optimize self access.
205     if (slot.slotBase() == baseValue) {
206         if ((slot.cachedPropertyType() != PropertySlot::Value) || ((slot.cachedOffset() * sizeof(JSValue)) > (unsigned)MacroAssembler::MaximumCompactPtrAlignedAddressOffset))
207             return false;
208
209         dfgRepatchByIdSelfAccess(codeBlock, stubInfo, structure, slot.cachedOffset(), operationGetByIdBuildList, true);
210         stubInfo.initGetByIdSelf(*globalData, codeBlock->ownerExecutable(), structure);
211         return true;
212     }
213     
214     if (structure->isDictionary())
215         return false;
216     
217     // FIXME: optimize getters and setters
218     if (slot.cachedPropertyType() != PropertySlot::Value)
219         return false;
220     
221     size_t offset = slot.cachedOffset();
222     size_t count = normalizePrototypeChain(exec, baseValue, slot.slotBase(), propertyName, offset);
223     if (!count)
224         return false;
225
226     StructureChain* prototypeChain = structure->prototypeChain(exec);
227     
228     ASSERT(slot.slotBase().isObject());
229     
230     generateProtoChainAccessStub(exec, stubInfo, prototypeChain, count, offset, structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToDone), stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToSlowCase), stubInfo.stubRoutine);
231     
232     RepatchBuffer repatchBuffer(codeBlock);
233     repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine.code()));
234     repatchBuffer.relink(stubInfo.callReturnLocation, operationGetByIdProtoBuildList);
235     
236     stubInfo.initGetByIdChain(*globalData, codeBlock->ownerExecutable(), structure, prototypeChain);
237     return true;
238 }
239
240 void dfgRepatchGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
241 {
242     bool cached = tryCacheGetByID(exec, baseValue, propertyName, slot, stubInfo);
243     if (!cached)
244         dfgRepatchCall(exec->codeBlock(), stubInfo.callReturnLocation, operationGetById);
245 }
246
247 static void dfgRepatchGetMethodFast(JSGlobalData* globalData, CodeBlock* codeBlock, MethodCallLinkInfo& methodInfo, JSObject* callee, Structure* structure, JSObject* slotBaseObject)
248 {
249     ScriptExecutable* owner = codeBlock->ownerExecutable();
250     
251     RepatchBuffer repatchBuffer(codeBlock);
252
253     // Only optimize once!
254     repatchBuffer.relink(methodInfo.callReturnLocation, operationGetByIdOptimize);
255
256     methodInfo.cachedStructure.set(*globalData, owner, structure);
257     methodInfo.cachedPrototypeStructure.set(*globalData, owner, slotBaseObject->structure());
258     methodInfo.cachedPrototype.set(*globalData, owner, slotBaseObject);
259     methodInfo.cachedFunction.set(*globalData, owner, callee);
260 }
261
262 static bool tryCacheGetMethod(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, MethodCallLinkInfo& methodInfo)
263 {
264     CodeBlock* codeBlock = exec->codeBlock();
265     JSGlobalData* globalData = &exec->globalData();
266     
267     Structure* structure;
268     JSCell* specific;
269     JSObject* slotBaseObject;
270     if (baseValue.isCell()
271         && slot.isCacheableValue()
272         && !(structure = baseValue.asCell()->structure())->isUncacheableDictionary()
273         && (slotBaseObject = asObject(slot.slotBase()))->getPropertySpecificValue(exec, propertyName, specific)
274         && specific) {
275         
276         JSObject* callee = asObject(specific);
277         
278         // Since we're accessing a prototype in a loop, it's a good bet that it
279         // should not be treated as a dictionary.
280         if (slotBaseObject->structure()->isDictionary())
281             slotBaseObject->flattenDictionaryObject(exec->globalData());
282         
283         if (slot.slotBase() == structure->prototypeForLookup(exec)) {
284             dfgRepatchGetMethodFast(globalData, codeBlock, methodInfo, callee, structure, slotBaseObject);
285             return true;
286         }
287         
288         if (slot.slotBase() == baseValue) {
289             dfgRepatchGetMethodFast(globalData, codeBlock, methodInfo, callee, structure, exec->scopeChain()->globalObject->methodCallDummy());
290             return true;
291         }
292     }
293     
294     return false;
295 }
296
297 void dfgRepatchGetMethod(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, MethodCallLinkInfo& methodInfo)
298 {
299     bool cached = tryCacheGetMethod(exec, baseValue, propertyName, slot, methodInfo);
300     if (!cached)
301         dfgRepatchCall(exec->codeBlock(), methodInfo.callReturnLocation, operationGetByIdOptimize);
302 }
303
304 static bool tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identifier&, const PropertySlot& slot, StructureStubInfo& stubInfo)
305 {
306     if (!baseValue.isCell()
307         || !slot.isCacheable()
308         || baseValue.asCell()->structure()->isUncacheableDictionary()
309         || slot.slotBase() != baseValue
310         || slot.cachedPropertyType() != PropertySlot::Value
311         || (slot.cachedOffset() * sizeof(JSValue)) > (unsigned)MacroAssembler::MaximumCompactPtrAlignedAddressOffset)
312         return false;
313     
314     CodeBlock* codeBlock = exec->codeBlock();
315     JSCell* baseCell = baseValue.asCell();
316     Structure* structure = baseCell->structure();
317     JSGlobalData* globalData = &exec->globalData();
318     
319     ASSERT(slot.slotBase().isObject());
320     
321     PolymorphicAccessStructureList* polymorphicStructureList;
322     int listIndex = 1;
323     
324     if (stubInfo.accessType == access_get_by_id_self) {
325         ASSERT(!stubInfo.stubRoutine);
326         polymorphicStructureList = new PolymorphicAccessStructureList(*globalData, codeBlock->ownerExecutable(), MacroAssemblerCodeRef::createSelfManagedCodeRef(stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToSlowCase)), stubInfo.u.getByIdSelf.baseObjectStructure.get());
327         stubInfo.initGetByIdSelfList(polymorphicStructureList, 1);
328     } else {
329         polymorphicStructureList = stubInfo.u.getByIdSelfList.structureList;
330         listIndex = stubInfo.u.getByIdSelfList.listSize;
331     }
332     
333     if (listIndex < POLYMORPHIC_LIST_CACHE_SIZE) {
334         stubInfo.u.getByIdSelfList.listSize++;
335         
336         GPRReg baseGPR = static_cast<GPRReg>(stubInfo.baseGPR);
337         GPRReg resultGPR = static_cast<GPRReg>(stubInfo.valueGPR);
338         
339         MacroAssembler stubJit;
340         
341         MacroAssembler::Jump wrongStruct = stubJit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(structure));
342         
343         if (structure->isUsingInlineStorage())
344             stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + slot.cachedOffset() * sizeof(JSValue)), resultGPR);
345         else {
346             stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), resultGPR);
347             stubJit.loadPtr(MacroAssembler::Address(resultGPR, slot.cachedOffset() * sizeof(JSValue)), resultGPR);
348         }
349         
350         MacroAssembler::Jump success = stubJit.jump();
351         
352         LinkBuffer patchBuffer(*globalData, &stubJit);
353         
354         CodeLocationLabel lastProtoBegin = CodeLocationLabel(polymorphicStructureList->list[listIndex - 1].stubRoutine.code());
355         ASSERT(!!lastProtoBegin);
356         
357         patchBuffer.link(wrongStruct, lastProtoBegin);
358         patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToDone));
359         
360         MacroAssemblerCodeRef stubRoutine = patchBuffer.finalizeCode();
361         
362         polymorphicStructureList->list[listIndex].set(*globalData, codeBlock->ownerExecutable(), stubRoutine, structure);
363         
364         CodeLocationJump jumpLocation = stubInfo.callReturnLocation.jumpAtOffset(stubInfo.deltaCallToStructCheck);
365         RepatchBuffer repatchBuffer(codeBlock);
366         repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine.code()));
367         
368         if (listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1))
369             return true;
370     }
371     
372     return false;
373 }
374
375 void dfgBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
376 {
377     bool dontChangeCall = tryBuildGetByIDList(exec, baseValue, propertyName, slot, stubInfo);
378     if (!dontChangeCall)
379         dfgRepatchCall(exec->codeBlock(), stubInfo.callReturnLocation, operationGetById);
380 }
381
382 static bool tryBuildGetByIDProtoList(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
383 {
384     if (!baseValue.isCell()
385         || !slot.isCacheable()
386         || baseValue.asCell()->structure()->isDictionary()
387         || baseValue.asCell()->structure()->typeInfo().prohibitsPropertyCaching()
388         || slot.slotBase() == baseValue
389         || slot.cachedPropertyType() != PropertySlot::Value)
390         return false;
391     
392     ASSERT(slot.slotBase().isObject());
393     
394     size_t offset = slot.cachedOffset();
395     size_t count = normalizePrototypeChain(exec, baseValue, slot.slotBase(), propertyName, offset);
396     if (!count)
397         return false;
398
399     Structure* structure = baseValue.asCell()->structure();
400     StructureChain* prototypeChain = structure->prototypeChain(exec);
401     CodeBlock* codeBlock = exec->codeBlock();
402     JSGlobalData* globalData = &exec->globalData();
403     
404     PolymorphicAccessStructureList* polymorphicStructureList;
405     int listIndex = 1;
406     
407     if (stubInfo.accessType == access_get_by_id_chain) {
408         ASSERT(!!stubInfo.stubRoutine);
409         polymorphicStructureList = new PolymorphicAccessStructureList(*globalData, codeBlock->ownerExecutable(), stubInfo.stubRoutine, stubInfo.u.getByIdChain.baseObjectStructure.get(), stubInfo.u.getByIdChain.chain.get());
410         stubInfo.stubRoutine = MacroAssemblerCodeRef();
411         stubInfo.initGetByIdProtoList(polymorphicStructureList, 1);
412     } else {
413         ASSERT(stubInfo.accessType = access_get_by_id_proto_list);
414         polymorphicStructureList = stubInfo.u.getByIdProtoList.structureList;
415         listIndex = stubInfo.u.getByIdProtoList.listSize;
416     }
417     
418     if (listIndex < POLYMORPHIC_LIST_CACHE_SIZE) {
419         stubInfo.u.getByIdProtoList.listSize++;
420         
421         CodeLocationLabel lastProtoBegin = CodeLocationLabel(polymorphicStructureList->list[listIndex - 1].stubRoutine.code());
422         ASSERT(!!lastProtoBegin);
423
424         MacroAssemblerCodeRef stubRoutine;
425         
426         generateProtoChainAccessStub(exec, stubInfo, prototypeChain, count, offset, structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToDone), lastProtoBegin, stubRoutine);
427         
428         polymorphicStructureList->list[listIndex].set(*globalData, codeBlock->ownerExecutable(), stubRoutine, structure);
429         
430         CodeLocationJump jumpLocation = stubInfo.callReturnLocation.jumpAtOffset(stubInfo.deltaCallToStructCheck);
431         RepatchBuffer repatchBuffer(codeBlock);
432         repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine.code()));
433         
434         if (listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1))
435             return true;
436     }
437     
438     return false;
439 }
440
441 void dfgBuildGetByIDProtoList(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
442 {
443     bool dontChangeCall = tryBuildGetByIDProtoList(exec, baseValue, propertyName, slot, stubInfo);
444     if (!dontChangeCall)
445         dfgRepatchCall(exec->codeBlock(), stubInfo.callReturnLocation, operationGetById);
446 }
447
448 static V_DFGOperation_EJJI appropriatePutByIdFunction(const PutPropertySlot &slot, PutKind putKind)
449 {
450     if (slot.isStrictMode()) {
451         if (putKind == Direct)
452             return operationPutByIdDirectStrict;
453         return operationPutByIdStrict;
454     }
455     if (putKind == Direct)
456         return operationPutByIdDirectNonStrict;
457     return operationPutByIdNonStrict;
458 }
459
460 static void testPrototype(MacroAssembler &stubJit, GPRReg scratchGPR, JSValue prototype, MacroAssembler::JumpList& failureCases)
461 {
462     if (prototype.isNull())
463         return;
464     
465     ASSERT(prototype.isCell());
466     
467     stubJit.move(MacroAssembler::TrustedImmPtr(prototype.asCell()), scratchGPR);
468     failureCases.append(stubJit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(scratchGPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(prototype.asCell()->structure())));
469 }
470
471 static bool tryCachePutByID(ExecState* exec, JSValue baseValue, const Identifier&, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
472 {
473     CodeBlock* codeBlock = exec->codeBlock();
474     JSGlobalData* globalData = &exec->globalData();
475
476     if (!baseValue.isCell())
477         return false;
478     JSCell* baseCell = baseValue.asCell();
479     Structure* structure = baseCell->structure();
480     Structure* oldStructure = structure->previousID();
481     
482     if (!slot.isCacheable())
483         return false;
484     if (structure->isUncacheableDictionary())
485         return false;
486
487     // Optimize self access.
488     if (slot.base() == baseValue) {
489         if (slot.type() == PutPropertySlot::NewProperty) {
490             if (structure->isDictionary())
491                 return false;
492             
493             // skip optimizing the case where we need a realloc
494             if (oldStructure->propertyStorageCapacity() != structure->propertyStorageCapacity())
495                 return false;
496             
497             normalizePrototypeChain(exec, baseCell);
498             
499             StructureChain* prototypeChain = structure->prototypeChain(exec);
500             
501             GPRReg baseGPR = static_cast<GPRReg>(stubInfo.baseGPR);
502             GPRReg valueGPR = static_cast<GPRReg>(stubInfo.valueGPR);
503             GPRReg scratchGPR = static_cast<GPRReg>(stubInfo.scratchGPR);
504             bool needToRestoreScratch = false;
505             
506             ASSERT(scratchGPR != baseGPR);
507             
508             MacroAssembler stubJit;
509             
510             MacroAssembler::JumpList failureCases;
511             
512             if (scratchGPR == InvalidGPRReg) {
513                 scratchGPR = JITCodeGenerator::selectScratchGPR(baseGPR, valueGPR);
514                 stubJit.push(scratchGPR);
515                 needToRestoreScratch = true;
516             }
517             
518             failureCases.append(stubJit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(oldStructure)));
519             
520             testPrototype(stubJit, scratchGPR, oldStructure->storedPrototype(), failureCases);
521             
522             if (putKind == NotDirect) {
523                 for (WriteBarrier<Structure>* it = prototypeChain->head(); *it; ++it)
524                     testPrototype(stubJit, scratchGPR, (*it)->storedPrototype(), failureCases);
525             }
526             
527             JITCodeGenerator::writeBarrier(stubJit, baseGPR, scratchGPR, WriteBarrierForPropertyAccess);
528             
529             stubJit.storePtr(MacroAssembler::TrustedImmPtr(structure), MacroAssembler::Address(baseGPR, JSCell::structureOffset()));
530             if (structure->isUsingInlineStorage())
531                 stubJit.storePtr(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + slot.cachedOffset() * sizeof(JSValue)));
532             else {
533                 stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), scratchGPR);
534                 stubJit.storePtr(valueGPR, MacroAssembler::Address(scratchGPR, slot.cachedOffset() * sizeof(JSValue)));
535             }
536             
537             MacroAssembler::Jump success;
538             MacroAssembler::Jump failure;
539             
540             if (needToRestoreScratch) {
541                 stubJit.pop(scratchGPR);
542                 success = stubJit.jump();
543
544                 failureCases.link(&stubJit);
545                 stubJit.pop(scratchGPR);
546                 failure = stubJit.jump();
547             } else
548                 success = stubJit.jump();
549             
550             LinkBuffer patchBuffer(*globalData, &stubJit);
551             patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToDone));
552             if (needToRestoreScratch)
553                 patchBuffer.link(failure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToSlowCase));
554             else
555                 patchBuffer.link(failureCases, stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToSlowCase));
556             
557             stubInfo.stubRoutine = patchBuffer.finalizeCode();
558             
559             RepatchBuffer repatchBuffer(codeBlock);
560             repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine.code()));
561             repatchBuffer.relink(stubInfo.callReturnLocation, appropriatePutByIdFunction(slot, putKind));
562             
563             stubInfo.initPutByIdTransition(*globalData, codeBlock->ownerExecutable(), oldStructure, structure, prototypeChain);
564             
565             return true;
566         }
567
568         dfgRepatchByIdSelfAccess(codeBlock, stubInfo, structure, slot.cachedOffset(), appropriatePutByIdFunction(slot, putKind), false);
569         stubInfo.initPutByIdReplace(*globalData, codeBlock->ownerExecutable(), structure);
570         return true;
571     }
572
573     // FIXME: should support the transition case!
574     return false;
575 }
576
577 void dfgRepatchPutByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
578 {
579     bool cached = tryCachePutByID(exec, baseValue, propertyName, slot, stubInfo, putKind);
580     if (!cached)
581         dfgRepatchCall(exec->codeBlock(), stubInfo.callReturnLocation, appropriatePutByIdFunction(slot, putKind));
582 }
583
584 void dfgLinkFor(ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCodeBlock, JSFunction* callee, MacroAssemblerCodePtr codePtr, CodeSpecializationKind kind)
585 {
586     CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();
587     
588     RepatchBuffer repatchBuffer(callerCodeBlock);
589     
590     if (!calleeCodeBlock || static_cast<int>(exec->argumentCountIncludingThis()) == calleeCodeBlock->m_numParameters) {
591         ASSERT(!callLinkInfo.isLinked());
592         callLinkInfo.callee.set(exec->callerFrame()->globalData(), callLinkInfo.hotPathBegin, callerCodeBlock->ownerExecutable(), callee);
593         repatchBuffer.relink(callLinkInfo.hotPathOther, codePtr);
594         
595         if (calleeCodeBlock)
596             calleeCodeBlock->linkIncomingCall(&callLinkInfo);
597     }
598     
599     if (kind == CodeForCall) {
600         repatchBuffer.relink(CodeLocationCall(callLinkInfo.callReturnLocation), operationVirtualCall);
601         return;
602     }
603     ASSERT(kind == CodeForConstruct);
604     repatchBuffer.relink(CodeLocationCall(callLinkInfo.callReturnLocation), operationVirtualConstruct);
605 }
606
607 } } // namespace JSC::DFG
608
609 #endif