1/*
2 * Copyright (C) 2008, 2009 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
28#if ENABLE(JIT)
29#if USE(JSVALUE32_64)
30#include "JIT.h"
31
32#include "CodeBlock.h"
33#include "JITInlineMethods.h"
34#include "JITStubCall.h"
35#include "JSArray.h"
36#include "JSFunction.h"
37#include "JSPropertyNameIterator.h"
38#include "Interpreter.h"
39#include "LinkBuffer.h"
40#include "RepatchBuffer.h"
41#include "ResultType.h"
42#include "SamplingTool.h"
43
44#ifndef NDEBUG
45#include <stdio.h>
46#endif
47
48using namespace std;
49
50namespace JSC {
51
52void JIT::emit_op_put_by_index(Instruction* currentInstruction)
53{
54    unsigned base = currentInstruction[1].u.operand;
55    unsigned property = currentInstruction[2].u.operand;
56    unsigned value = currentInstruction[3].u.operand;
57
58    JITStubCall stubCall(this, cti_op_put_by_index);
59    stubCall.addArgument(base);
60    stubCall.addArgument(Imm32(property));
61    stubCall.addArgument(value);
62    stubCall.call();
63}
64
65void JIT::emit_op_put_getter(Instruction* currentInstruction)
66{
67    unsigned base = currentInstruction[1].u.operand;
68    unsigned property = currentInstruction[2].u.operand;
69    unsigned function = currentInstruction[3].u.operand;
70
71    JITStubCall stubCall(this, cti_op_put_getter);
72    stubCall.addArgument(base);
73    stubCall.addArgument(TrustedImmPtr(&m_codeBlock->identifier(property)));
74    stubCall.addArgument(function);
75    stubCall.call();
76}
77
78void JIT::emit_op_put_setter(Instruction* currentInstruction)
79{
80    unsigned base = currentInstruction[1].u.operand;
81    unsigned property = currentInstruction[2].u.operand;
82    unsigned function = currentInstruction[3].u.operand;
83
84    JITStubCall stubCall(this, cti_op_put_setter);
85    stubCall.addArgument(base);
86    stubCall.addArgument(TrustedImmPtr(&m_codeBlock->identifier(property)));
87    stubCall.addArgument(function);
88    stubCall.call();
89}
90
91void JIT::emit_op_del_by_id(Instruction* currentInstruction)
92{
93    unsigned dst = currentInstruction[1].u.operand;
94    unsigned base = currentInstruction[2].u.operand;
95    unsigned property = currentInstruction[3].u.operand;
96
97    JITStubCall stubCall(this, cti_op_del_by_id);
98    stubCall.addArgument(base);
99    stubCall.addArgument(TrustedImmPtr(&m_codeBlock->identifier(property)));
100    stubCall.call(dst);
101}
102
103
104#if !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
105
106/* ------------------------------ BEGIN: !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) ------------------------------ */
107
108// Treat these as nops - the call will be handed as a regular get_by_id/op_call pair.
109void JIT::emit_op_method_check(Instruction*) {}
110void JIT::emitSlow_op_method_check(Instruction*, Vector<SlowCaseEntry>::iterator&) { ASSERT_NOT_REACHED(); }
111#if ENABLE(JIT_OPTIMIZE_METHOD_CALLS)
112#error "JIT_OPTIMIZE_METHOD_CALLS requires JIT_OPTIMIZE_PROPERTY_ACCESS"
113#endif
114
115void JIT::emit_op_get_by_val(Instruction* currentInstruction)
116{
117    unsigned dst = currentInstruction[1].u.operand;
118    unsigned base = currentInstruction[2].u.operand;
119    unsigned property = currentInstruction[3].u.operand;
120
121    JITStubCall stubCall(this, cti_op_get_by_val);
122    stubCall.addArgument(base);
123    stubCall.addArgument(property);
124    stubCall.call(dst);
125}
126
127void JIT::emitSlow_op_get_by_val(Instruction*, Vector<SlowCaseEntry>::iterator&)
128{
129    ASSERT_NOT_REACHED();
130}
131
132void JIT::emit_op_put_by_val(Instruction* currentInstruction)
133{
134    unsigned base = currentInstruction[1].u.operand;
135    unsigned property = currentInstruction[2].u.operand;
136    unsigned value = currentInstruction[3].u.operand;
137
138    JITStubCall stubCall(this, cti_op_put_by_val);
139    stubCall.addArgument(base);
140    stubCall.addArgument(property);
141    stubCall.addArgument(value);
142    stubCall.call();
143}
144
145void JIT::emitSlow_op_put_by_val(Instruction*, Vector<SlowCaseEntry>::iterator&)
146{
147    ASSERT_NOT_REACHED();
148}
149
150void JIT::emit_op_get_by_id(Instruction* currentInstruction)
151{
152    int dst = currentInstruction[1].u.operand;
153    int base = currentInstruction[2].u.operand;
154    int ident = currentInstruction[3].u.operand;
155
156    JITStubCall stubCall(this, cti_op_get_by_id_generic);
157    stubCall.addArgument(base);
158    stubCall.addArgument(TrustedImmPtr(&(m_codeBlock->identifier(ident))));
159    stubCall.call(dst);
160
161    m_propertyAccessInstructionIndex++;
162}
163
164void JIT::emitSlow_op_get_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&)
165{
166    m_propertyAccessInstructionIndex++;
167    ASSERT_NOT_REACHED();
168}
169
170void JIT::emit_op_put_by_id(Instruction* currentInstruction)
171{
172    int base = currentInstruction[1].u.operand;
173    int ident = currentInstruction[2].u.operand;
174    int value = currentInstruction[3].u.operand;
175
176    JITStubCall stubCall(this, cti_op_put_by_id_generic);
177    stubCall.addArgument(base);
178    stubCall.addArgument(TrustedImmPtr(&(m_codeBlock->identifier(ident))));
179    stubCall.addArgument(value);
180    stubCall.call();
181
182    m_propertyAccessInstructionIndex++;
183}
184
185void JIT::emitSlow_op_put_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&)
186{
187    m_propertyAccessInstructionIndex++;
188    ASSERT_NOT_REACHED();
189}
190
191#else // !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
192
193/* ------------------------------ BEGIN: ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) ------------------------------ */
194
195#if ENABLE(JIT_OPTIMIZE_METHOD_CALLS)
196
197void JIT::emit_op_method_check(Instruction* currentInstruction)
198{
199    // Assert that the following instruction is a get_by_id.
200    ASSERT(m_interpreter->getOpcodeID((currentInstruction + OPCODE_LENGTH(op_method_check))->u.opcode) == op_get_by_id);
201
202    currentInstruction += OPCODE_LENGTH(op_method_check);
203
204    // Do the method check - check the object & its prototype's structure inline (this is the common case).
205    m_methodCallCompilationInfo.append(MethodCallCompilationInfo(m_propertyAccessInstructionIndex));
206    MethodCallCompilationInfo& info = m_methodCallCompilationInfo.last();
207
208    int dst = currentInstruction[1].u.operand;
209    int base = currentInstruction[2].u.operand;
210
211    emitLoad(base, regT1, regT0);
212    emitJumpSlowCaseIfNotJSCell(base, regT1);
213
214    BEGIN_UNINTERRUPTED_SEQUENCE(sequenceMethodCheck);
215
216    Jump structureCheck = branchPtrWithPatch(NotEqual, Address(regT0, JSCell::structureOffset()), info.structureToCompare, TrustedImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure)));
217    DataLabelPtr protoStructureToCompare, protoObj = moveWithPatch(TrustedImmPtr(0), regT2);
218    Jump protoStructureCheck = branchPtrWithPatch(NotEqual, Address(regT2, JSCell::structureOffset()), protoStructureToCompare, TrustedImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure)));
219
220    // This will be relinked to load the function without doing a load.
221    DataLabelPtr putFunction = moveWithPatch(TrustedImmPtr(0), regT0);
222
223    END_UNINTERRUPTED_SEQUENCE(sequenceMethodCheck);
224
225    move(TrustedImm32(JSValue::CellTag), regT1);
226    Jump match = jump();
227
228    ASSERT_JIT_OFFSET(differenceBetween(info.structureToCompare, protoObj), patchOffsetMethodCheckProtoObj);
229    ASSERT_JIT_OFFSET(differenceBetween(info.structureToCompare, protoStructureToCompare), patchOffsetMethodCheckProtoStruct);
230    ASSERT_JIT_OFFSET(differenceBetween(info.structureToCompare, putFunction), patchOffsetMethodCheckPutFunction);
231
232    // Link the failure cases here.
233    structureCheck.link(this);
234    protoStructureCheck.link(this);
235
236    // Do a regular(ish) get_by_id (the slow case will be link to
237    // cti_op_get_by_id_method_check instead of cti_op_get_by_id.
238    compileGetByIdHotPath();
239
240    match.link(this);
241    emitStore(dst, regT1, regT0);
242    map(m_bytecodeOffset + OPCODE_LENGTH(op_method_check), dst, regT1, regT0);
243
244    // We've already generated the following get_by_id, so make sure it's skipped over.
245    m_bytecodeOffset += OPCODE_LENGTH(op_get_by_id);
246}
247
248void JIT::emitSlow_op_method_check(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
249{
250    currentInstruction += OPCODE_LENGTH(op_method_check);
251
252    int dst = currentInstruction[1].u.operand;
253    int base = currentInstruction[2].u.operand;
254    int ident = currentInstruction[3].u.operand;
255
256    compileGetByIdSlowCase(dst, base, &(m_codeBlock->identifier(ident)), iter, true);
257
258    // We've already generated the following get_by_id, so make sure it's skipped over.
259    m_bytecodeOffset += OPCODE_LENGTH(op_get_by_id);
260}
261
262#else //!ENABLE(JIT_OPTIMIZE_METHOD_CALLS)
263
264// Treat these as nops - the call will be handed as a regular get_by_id/op_call pair.
265void JIT::emit_op_method_check(Instruction*) {}
266void JIT::emitSlow_op_method_check(Instruction*, Vector<SlowCaseEntry>::iterator&) { ASSERT_NOT_REACHED(); }
267
268#endif
269
270JIT::CodePtr JIT::stringGetByValStubGenerator(JSGlobalData* globalData, ExecutablePool* pool)
271{
272    JSInterfaceJIT jit;
273    JumpList failures;
274    failures.append(jit.branchPtr(NotEqual, Address(regT0), TrustedImmPtr(globalData->jsStringVPtr)));
275    failures.append(jit.branchTest32(NonZero, Address(regT0, OBJECT_OFFSETOF(JSString, m_fiberCount))));
276
277    // Load string length to regT1, and start the process of loading the data pointer into regT0
278    jit.load32(Address(regT0, ThunkHelpers::jsStringLengthOffset()), regT1);
279    jit.loadPtr(Address(regT0, ThunkHelpers::jsStringValueOffset()), regT0);
280    jit.loadPtr(Address(regT0, ThunkHelpers::stringImplDataOffset()), regT0);
281
282    // Do an unsigned compare to simultaneously filter negative indices as well as indices that are too large
283    failures.append(jit.branch32(AboveOrEqual, regT2, regT1));
284
285    // Load the character
286    jit.load16(BaseIndex(regT0, regT2, TimesTwo, 0), regT0);
287
288    failures.append(jit.branch32(AboveOrEqual, regT0, TrustedImm32(0x100)));
289    jit.move(TrustedImmPtr(globalData->smallStrings.singleCharacterStrings()), regT1);
290    jit.loadPtr(BaseIndex(regT1, regT0, ScalePtr, 0), regT0);
291    jit.move(TrustedImm32(JSValue::CellTag), regT1); // We null check regT0 on return so this is safe
292    jit.ret();
293
294    failures.link(&jit);
295    jit.move(TrustedImm32(0), regT0);
296    jit.ret();
297
298    LinkBuffer patchBuffer(&jit, pool, 0);
299    return patchBuffer.finalizeCode().m_code;
300}
301
302void JIT::emit_op_get_by_val(Instruction* currentInstruction)
303{
304    unsigned dst = currentInstruction[1].u.operand;
305    unsigned base = currentInstruction[2].u.operand;
306    unsigned property = currentInstruction[3].u.operand;
307
308    emitLoad2(base, regT1, regT0, property, regT3, regT2);
309
310    addSlowCase(branch32(NotEqual, regT3, TrustedImm32(JSValue::Int32Tag)));
311    emitJumpSlowCaseIfNotJSCell(base, regT1);
312    addSlowCase(branchPtr(NotEqual, Address(regT0), TrustedImmPtr(m_globalData->jsArrayVPtr)));
313
314    loadPtr(Address(regT0, JSArray::storageOffset()), regT3);
315    addSlowCase(branch32(AboveOrEqual, regT2, Address(regT0, JSArray::vectorLengthOffset())));
316
317    load32(BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), regT1); // tag
318    load32(BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT0); // payload
319    addSlowCase(branch32(Equal, regT1, TrustedImm32(JSValue::EmptyValueTag)));
320
321    emitStore(dst, regT1, regT0);
322    map(m_bytecodeOffset + OPCODE_LENGTH(op_get_by_val), dst, regT1, regT0);
323}
324
325void JIT::emitSlow_op_get_by_val(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
326{
327    unsigned dst = currentInstruction[1].u.operand;
328    unsigned base = currentInstruction[2].u.operand;
329    unsigned property = currentInstruction[3].u.operand;
330
331    linkSlowCase(iter); // property int32 check
332    linkSlowCaseIfNotJSCell(iter, base); // base cell check
333
334    Jump nonCell = jump();
335    linkSlowCase(iter); // base array check
336    Jump notString = branchPtr(NotEqual, Address(regT0), TrustedImmPtr(m_globalData->jsStringVPtr));
337    emitNakedCall(m_globalData->getCTIStub(stringGetByValStubGenerator));
338    Jump failed = branchTestPtr(Zero, regT0);
339    emitStore(dst, regT1, regT0);
340    emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_get_by_val));
341    failed.link(this);
342    notString.link(this);
343    nonCell.link(this);
344
345    linkSlowCase(iter); // vector length check
346    linkSlowCase(iter); // empty value
347
348    JITStubCall stubCall(this, cti_op_get_by_val);
349    stubCall.addArgument(base);
350    stubCall.addArgument(property);
351    stubCall.call(dst);
352}
353
354void JIT::emit_op_put_by_val(Instruction* currentInstruction)
355{
356    unsigned base = currentInstruction[1].u.operand;
357    unsigned property = currentInstruction[2].u.operand;
358    unsigned value = currentInstruction[3].u.operand;
359
360    emitLoad2(base, regT1, regT0, property, regT3, regT2);
361
362    addSlowCase(branch32(NotEqual, regT3, TrustedImm32(JSValue::Int32Tag)));
363    emitJumpSlowCaseIfNotJSCell(base, regT1);
364    addSlowCase(branchPtr(NotEqual, Address(regT0), TrustedImmPtr(m_globalData->jsArrayVPtr)));
365    addSlowCase(branch32(AboveOrEqual, regT2, Address(regT0, JSArray::vectorLengthOffset())));
366
367    loadPtr(Address(regT0, JSArray::storageOffset()), regT3);
368
369    Jump empty = branch32(Equal, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), TrustedImm32(JSValue::EmptyValueTag));
370
371    Label storeResult(this);
372    emitLoad(value, regT1, regT0);
373    store32(regT0, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); // payload
374    store32(regT1, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); // tag
375    Jump end = jump();
376
377    empty.link(this);
378    add32(TrustedImm32(1), Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector)));
379    branch32(Below, regT2, Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_length))).linkTo(storeResult, this);
380
381    add32(TrustedImm32(1), regT2, regT0);
382    store32(regT0, Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_length)));
383    jump().linkTo(storeResult, this);
384
385    end.link(this);
386}
387
388void JIT::emitSlow_op_put_by_val(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
389{
390    unsigned base = currentInstruction[1].u.operand;
391    unsigned property = currentInstruction[2].u.operand;
392    unsigned value = currentInstruction[3].u.operand;
393
394    linkSlowCase(iter); // property int32 check
395    linkSlowCaseIfNotJSCell(iter, base); // base cell check
396    linkSlowCase(iter); // base not array check
397    linkSlowCase(iter); // in vector check
398
399    JITStubCall stubPutByValCall(this, cti_op_put_by_val);
400    stubPutByValCall.addArgument(base);
401    stubPutByValCall.addArgument(property);
402    stubPutByValCall.addArgument(value);
403    stubPutByValCall.call();
404}
405
406void JIT::emit_op_get_by_id(Instruction* currentInstruction)
407{
408    int dst = currentInstruction[1].u.operand;
409    int base = currentInstruction[2].u.operand;
410
411    emitLoad(base, regT1, regT0);
412    emitJumpSlowCaseIfNotJSCell(base, regT1);
413    compileGetByIdHotPath();
414    emitStore(dst, regT1, regT0);
415    map(m_bytecodeOffset + OPCODE_LENGTH(op_get_by_id), dst, regT1, regT0);
416}
417
418void JIT::compileGetByIdHotPath()
419{
420    // As for put_by_id, get_by_id requires the offset of the Structure and the offset of the access to be patched.
421    // Additionally, for get_by_id we need patch the offset of the branch to the slow case (we patch this to jump
422    // to array-length / prototype access tranpolines, and finally we also the the property-map access offset as a label
423    // to jump back to if one of these trampolies finds a match.
424
425    BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdHotPath);
426
427    Label hotPathBegin(this);
428    m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex].hotPathBegin = hotPathBegin;
429    m_propertyAccessInstructionIndex++;
430
431    DataLabelPtr structureToCompare;
432    Jump structureCheck = branchPtrWithPatch(NotEqual, Address(regT0, JSCell::structureOffset()), structureToCompare, TrustedImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure)));
433    addSlowCase(structureCheck);
434    ASSERT_JIT_OFFSET(differenceBetween(hotPathBegin, structureToCompare), patchOffsetGetByIdStructure);
435    ASSERT_JIT_OFFSET(differenceBetween(hotPathBegin, structureCheck), patchOffsetGetByIdBranchToSlowCase);
436
437    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSObject, m_propertyStorage)), regT2);
438    DataLabel32 displacementLabel1 = loadPtrWithAddressOffsetPatch(Address(regT2, patchGetByIdDefaultOffset), regT0); // payload
439    ASSERT_JIT_OFFSET(differenceBetween(hotPathBegin, displacementLabel1), patchOffsetGetByIdPropertyMapOffset1);
440    DataLabel32 displacementLabel2 = loadPtrWithAddressOffsetPatch(Address(regT2, patchGetByIdDefaultOffset), regT1); // tag
441    ASSERT_JIT_OFFSET(differenceBetween(hotPathBegin, displacementLabel2), patchOffsetGetByIdPropertyMapOffset2);
442
443    Label putResult(this);
444    ASSERT_JIT_OFFSET(differenceBetween(hotPathBegin, putResult), patchOffsetGetByIdPutResult);
445
446    END_UNINTERRUPTED_SEQUENCE(sequenceGetByIdHotPath);
447}
448
449void JIT::emitSlow_op_get_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
450{
451    int dst = currentInstruction[1].u.operand;
452    int base = currentInstruction[2].u.operand;
453    int ident = currentInstruction[3].u.operand;
454
455    compileGetByIdSlowCase(dst, base, &(m_codeBlock->identifier(ident)), iter);
456}
457
458void JIT::compileGetByIdSlowCase(int dst, int base, Identifier* ident, Vector<SlowCaseEntry>::iterator& iter, bool isMethodCheck)
459{
460    // As for the hot path of get_by_id, above, we ensure that we can use an architecture specific offset
461    // so that we only need track one pointer into the slow case code - we track a pointer to the location
462    // of the call (which we can use to look up the patch information), but should a array-length or
463    // prototype access trampoline fail we want to bail out back to here.  To do so we can subtract back
464    // the distance from the call to the head of the slow case.
465    linkSlowCaseIfNotJSCell(iter, base);
466    linkSlowCase(iter);
467
468    BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdSlowCase);
469
470#ifndef NDEBUG
471    Label coldPathBegin(this);
472#endif
473    JITStubCall stubCall(this, isMethodCheck ? cti_op_get_by_id_method_check : cti_op_get_by_id);
474    stubCall.addArgument(regT1, regT0);
475    stubCall.addArgument(TrustedImmPtr(ident));
476    Call call = stubCall.call(dst);
477
478    END_UNINTERRUPTED_SEQUENCE_FOR_PUT(sequenceGetByIdSlowCase, dst);
479
480    ASSERT_JIT_OFFSET(differenceBetween(coldPathBegin, call), patchOffsetGetByIdSlowCaseCall);
481
482    // Track the location of the call; this will be used to recover patch information.
483    m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex].callReturnLocation = call;
484    m_propertyAccessInstructionIndex++;
485}
486
487void JIT::emit_op_put_by_id(Instruction* currentInstruction)
488{
489    // In order to be able to patch both the Structure, and the object offset, we store one pointer,
490    // to just after the arguments have been loaded into registers 'hotPathBegin', and we generate code
491    // such that the Structure & offset are always at the same distance from this.
492
493    int base = currentInstruction[1].u.operand;
494    int value = currentInstruction[3].u.operand;
495
496    emitLoad2(base, regT1, regT0, value, regT3, regT2);
497
498    emitJumpSlowCaseIfNotJSCell(base, regT1);
499
500    BEGIN_UNINTERRUPTED_SEQUENCE(sequencePutById);
501
502    Label hotPathBegin(this);
503    m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex].hotPathBegin = hotPathBegin;
504    m_propertyAccessInstructionIndex++;
505
506    // It is important that the following instruction plants a 32bit immediate, in order that it can be patched over.
507    DataLabelPtr structureToCompare;
508    addSlowCase(branchPtrWithPatch(NotEqual, Address(regT0, JSCell::structureOffset()), structureToCompare, TrustedImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure))));
509    ASSERT_JIT_OFFSET(differenceBetween(hotPathBegin, structureToCompare), patchOffsetPutByIdStructure);
510
511    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSObject, m_propertyStorage)), regT0);
512    DataLabel32 displacementLabel1 = storePtrWithAddressOffsetPatch(regT2, Address(regT0, patchGetByIdDefaultOffset)); // payload
513    DataLabel32 displacementLabel2 = storePtrWithAddressOffsetPatch(regT3, Address(regT0, patchGetByIdDefaultOffset)); // tag
514
515    END_UNINTERRUPTED_SEQUENCE(sequencePutById);
516
517    ASSERT_JIT_OFFSET(differenceBetween(hotPathBegin, displacementLabel1), patchOffsetPutByIdPropertyMapOffset1);
518    ASSERT_JIT_OFFSET(differenceBetween(hotPathBegin, displacementLabel2), patchOffsetPutByIdPropertyMapOffset2);
519}
520
521void JIT::emitSlow_op_put_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
522{
523    int base = currentInstruction[1].u.operand;
524    int ident = currentInstruction[2].u.operand;
525    int direct = currentInstruction[8].u.operand;
526
527    linkSlowCaseIfNotJSCell(iter, base);
528    linkSlowCase(iter);
529
530    JITStubCall stubCall(this, direct ? cti_op_put_by_id_direct : cti_op_put_by_id);
531    stubCall.addArgument(regT1, regT0);
532    stubCall.addArgument(TrustedImmPtr(&(m_codeBlock->identifier(ident))));
533    stubCall.addArgument(regT3, regT2);
534    Call call = stubCall.call();
535
536    // Track the location of the call; this will be used to recover patch information.
537    m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex].callReturnLocation = call;
538    m_propertyAccessInstructionIndex++;
539}
540
541// Compile a store into an object's property storage.  May overwrite base.
542void JIT::compilePutDirectOffset(RegisterID base, RegisterID valueTag, RegisterID valuePayload, Structure* structure, size_t cachedOffset)
543{
544    int offset = cachedOffset;
545    if (structure->isUsingInlineStorage())
546        offset += JSObject::offsetOfInlineStorage() /  sizeof(Register);
547    else
548        loadPtr(Address(base, OBJECT_OFFSETOF(JSObject, m_propertyStorage)), base);
549    emitStore(offset, valueTag, valuePayload, base);
550}
551
552// Compile a load from an object's property storage.  May overwrite base.
553void JIT::compileGetDirectOffset(RegisterID base, RegisterID resultTag, RegisterID resultPayload, Structure* structure, size_t cachedOffset)
554{
555    int offset = cachedOffset;
556    if (structure->isUsingInlineStorage()) {
557        offset += JSObject::offsetOfInlineStorage() / sizeof(Register);
558        emitLoad(offset, resultTag, resultPayload, base);
559    } else {
560        RegisterID temp = resultPayload;
561        loadPtr(Address(base, OBJECT_OFFSETOF(JSObject, m_propertyStorage)), temp);
562        emitLoad(offset, resultTag, resultPayload, temp);
563    }
564}
565
566void JIT::compileGetDirectOffset(JSObject* base, RegisterID resultTag, RegisterID resultPayload, size_t cachedOffset)
567{
568    load32(reinterpret_cast<char*>(&base->m_propertyStorage[cachedOffset]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload), resultPayload);
569    load32(reinterpret_cast<char*>(&base->m_propertyStorage[cachedOffset]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag), resultTag);
570}
571
572void JIT::testPrototype(JSValue prototype, JumpList& failureCases)
573{
574    if (prototype.isNull())
575        return;
576
577    // We have a special case for X86_64 here because X86 instructions that take immediate values
578    // only take 32 bit immediate values, wheras the pointer constants we are using here are 64 bit
579    // values.  In the non X86_64 case, the generated code is slightly more efficient because it uses
580    // two less instructions and doesn't require any scratch registers.
581#if CPU(X86_64)
582    move(TrustedImmPtr(prototype.asCell()->structure()), regT3);
583    failureCases.append(branchPtr(NotEqual, AbsoluteAddress(prototype.asCell()->addressOfStructure()), regT3));
584#else
585    failureCases.append(branchPtr(NotEqual, AbsoluteAddress(prototype.asCell()->addressOfStructure()), TrustedImmPtr(prototype.asCell()->structure())));
586#endif
587}
588
589void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure* oldStructure, Structure* newStructure, size_t cachedOffset, StructureChain* chain, ReturnAddressPtr returnAddress, bool direct)
590{
591    // It is assumed that regT0 contains the basePayload and regT1 contains the baseTag.  The value can be found on the stack.
592
593    JumpList failureCases;
594    failureCases.append(branch32(NotEqual, regT1, TrustedImm32(JSValue::CellTag)));
595    failureCases.append(branchPtr(NotEqual, Address(regT0, JSCell::structureOffset()), TrustedImmPtr(oldStructure)));
596    testPrototype(oldStructure->storedPrototype(), failureCases);
597
598    if (!direct) {
599        // Verify that nothing in the prototype chain has a setter for this property.
600        for (WriteBarrier<Structure>* it = chain->head(); *it; ++it)
601            testPrototype((*it)->storedPrototype(), failureCases);
602    }
603
604    // Reallocate property storage if needed.
605    Call callTarget;
606    bool willNeedStorageRealloc = oldStructure->propertyStorageCapacity() != newStructure->propertyStorageCapacity();
607    if (willNeedStorageRealloc) {
608        // This trampoline was called to like a JIT stub; before we can can call again we need to
609        // remove the return address from the stack, to prevent the stack from becoming misaligned.
610        preserveReturnAddressAfterCall(regT3);
611
612        JITStubCall stubCall(this, cti_op_put_by_id_transition_realloc);
613        stubCall.skipArgument(); // base
614        stubCall.skipArgument(); // ident
615        stubCall.skipArgument(); // value
616        stubCall.addArgument(TrustedImm32(oldStructure->propertyStorageCapacity()));
617        stubCall.addArgument(TrustedImm32(newStructure->propertyStorageCapacity()));
618        stubCall.call(regT0);
619
620        restoreReturnAddressBeforeReturn(regT3);
621    }
622
623    storePtrWithWriteBarrier(TrustedImmPtr(newStructure), regT0, Address(regT0, JSCell::structureOffset()));
624
625#if CPU(MIPS) || CPU(SH4)
626    // For MIPS, we don't add sizeof(void*) to the stack offset.
627    load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[2]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT3);
628    load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[2]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), regT2);
629#else
630    load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[2]) + sizeof(void*) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT3);
631    load32(Address(stackPointerRegister, OBJECT_OFFSETOF(JITStackFrame, args[2]) + sizeof(void*) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), regT2);
632#endif
633
634    // Write the value
635    compilePutDirectOffset(regT0, regT2, regT3, newStructure, cachedOffset);
636
637    ret();
638
639    ASSERT(!failureCases.empty());
640    failureCases.link(this);
641    restoreArgumentReferenceForTrampoline();
642    Call failureCall = tailRecursiveCall();
643
644    LinkBuffer patchBuffer(this, m_codeBlock->executablePool(), 0);
645
646    patchBuffer.link(failureCall, FunctionPtr(direct ? cti_op_put_by_id_direct_fail : cti_op_put_by_id_fail));
647
648    if (willNeedStorageRealloc) {
649        ASSERT(m_calls.size() == 1);
650        patchBuffer.link(m_calls[0].from, FunctionPtr(cti_op_put_by_id_transition_realloc));
651    }
652
653    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
654    stubInfo->stubRoutine = entryLabel;
655    RepatchBuffer repatchBuffer(m_codeBlock);
656    repatchBuffer.relinkCallerToTrampoline(returnAddress, entryLabel);
657}
658
659void JIT::patchGetByIdSelf(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, ReturnAddressPtr returnAddress)
660{
661    RepatchBuffer repatchBuffer(codeBlock);
662
663    // We don't want to patch more than once - in future go to cti_op_get_by_id_generic.
664    // Should probably go to JITStubs::cti_op_get_by_id_fail, but that doesn't do anything interesting right now.
665    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_self_fail));
666
667    int offset = sizeof(JSValue) * cachedOffset;
668
669    // Patch the offset into the propoerty map to load from, then patch the Structure to look for.
670    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(patchOffsetGetByIdStructure), structure);
671    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetGetByIdPropertyMapOffset1), offset + OBJECT_OFFSETOF(JSValue, u.asBits.payload)); // payload
672    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetGetByIdPropertyMapOffset2), offset + OBJECT_OFFSETOF(JSValue, u.asBits.tag)); // tag
673}
674
675void JIT::patchMethodCallProto(JSGlobalData& globalData, CodeBlock* codeBlock, MethodCallLinkInfo& methodCallLinkInfo, JSFunction* callee, Structure* structure, JSObject* proto, ReturnAddressPtr returnAddress)
676{
677    RepatchBuffer repatchBuffer(codeBlock);
678
679    ASSERT(!methodCallLinkInfo.cachedStructure);
680    methodCallLinkInfo.cachedStructure.set(globalData, codeBlock->ownerExecutable(), structure);
681    Structure* prototypeStructure = proto->structure();
682    methodCallLinkInfo.cachedPrototypeStructure.set(globalData, codeBlock->ownerExecutable(), prototypeStructure);
683
684    repatchBuffer.repatch(methodCallLinkInfo.structureLabel, structure);
685    repatchBuffer.repatch(methodCallLinkInfo.structureLabel.dataLabelPtrAtOffset(patchOffsetMethodCheckProtoObj), proto);
686    repatchBuffer.repatch(methodCallLinkInfo.structureLabel.dataLabelPtrAtOffset(patchOffsetMethodCheckProtoStruct), prototypeStructure);
687    repatchBuffer.repatch(methodCallLinkInfo.structureLabel.dataLabelPtrAtOffset(patchOffsetMethodCheckPutFunction), callee);
688
689    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id));
690}
691
692void JIT::patchPutByIdReplace(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, ReturnAddressPtr returnAddress, bool direct)
693{
694    RepatchBuffer repatchBuffer(codeBlock);
695
696    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
697    // Should probably go to cti_op_put_by_id_fail, but that doesn't do anything interesting right now.
698    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(direct ? cti_op_put_by_id_direct_generic : cti_op_put_by_id_generic));
699
700    int offset = sizeof(JSValue) * cachedOffset;
701
702    // Patch the offset into the propoerty map to load from, then patch the Structure to look for.
703    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(patchOffsetPutByIdStructure), structure);
704    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetPutByIdPropertyMapOffset1), offset + OBJECT_OFFSETOF(JSValue, u.asBits.payload)); // payload
705    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetPutByIdPropertyMapOffset2), offset + OBJECT_OFFSETOF(JSValue, u.asBits.tag)); // tag
706}
707
708void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress)
709{
710    StructureStubInfo* stubInfo = &m_codeBlock->getStubInfo(returnAddress);
711
712    // regT0 holds a JSCell*
713
714    // Check for array
715    Jump failureCases1 = branchPtr(NotEqual, Address(regT0), TrustedImmPtr(m_globalData->jsArrayVPtr));
716
717    // Checks out okay! - get the length from the storage
718    loadPtr(Address(regT0, JSArray::storageOffset()), regT2);
719    load32(Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length)), regT2);
720
721    Jump failureCases2 = branch32(Above, regT2, TrustedImm32(INT_MAX));
722    move(regT2, regT0);
723    move(TrustedImm32(JSValue::Int32Tag), regT1);
724    Jump success = jump();
725
726    LinkBuffer patchBuffer(this, m_codeBlock->executablePool(), 0);
727
728    // Use the patch information to link the failure cases back to the original slow case routine.
729    CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall);
730    patchBuffer.link(failureCases1, slowCaseBegin);
731    patchBuffer.link(failureCases2, slowCaseBegin);
732
733    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
734    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
735
736    // Track the stub we have created so that it will be deleted later.
737    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
738    stubInfo->stubRoutine = entryLabel;
739
740    // Finally patch the jump to slow case back in the hot path to jump here instead.
741    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
742    RepatchBuffer repatchBuffer(m_codeBlock);
743    repatchBuffer.relink(jumpLocation, entryLabel);
744
745    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
746    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_array_fail));
747}
748
749void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, const Identifier& ident, const PropertySlot& slot, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
750{
751    // regT0 holds a JSCell*
752
753    // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
754    // referencing the prototype object - let's speculatively load it's table nice and early!)
755    JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame));
756
757    Jump failureCases1 = checkStructure(regT0, structure);
758
759    // Check the prototype object's Structure had not changed.
760    const void* prototypeStructureAddress = protoObject->addressOfStructure();
761#if CPU(X86_64)
762    move(TrustedImmPtr(prototypeStructure), regT3);
763    Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3);
764#else
765    Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), TrustedImmPtr(prototypeStructure));
766#endif
767    bool needsStubLink = false;
768    // Checks out okay!
769    if (slot.cachedPropertyType() == PropertySlot::Getter) {
770        needsStubLink = true;
771        compileGetDirectOffset(protoObject, regT2, regT1, cachedOffset);
772        JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
773        stubCall.addArgument(regT1);
774        stubCall.addArgument(regT0);
775        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
776        stubCall.call();
777    } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
778        needsStubLink = true;
779        JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
780        stubCall.addArgument(TrustedImmPtr(protoObject));
781        stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
782        stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
783        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
784        stubCall.call();
785    } else
786        compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
787
788    Jump success = jump();
789
790    LinkBuffer patchBuffer(this, m_codeBlock->executablePool(), 0);
791
792    // Use the patch information to link the failure cases back to the original slow case routine.
793    CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall);
794    patchBuffer.link(failureCases1, slowCaseBegin);
795    patchBuffer.link(failureCases2, slowCaseBegin);
796
797    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
798    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
799
800    if (needsStubLink) {
801        for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
802            if (iter->to)
803                patchBuffer.link(iter->from, FunctionPtr(iter->to));
804        }
805    }
806
807    // Track the stub we have created so that it will be deleted later.
808    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
809    stubInfo->stubRoutine = entryLabel;
810
811    // Finally patch the jump to slow case back in the hot path to jump here instead.
812    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
813    RepatchBuffer repatchBuffer(m_codeBlock);
814    repatchBuffer.relink(jumpLocation, entryLabel);
815
816    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
817    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_proto_list));
818}
819
820
821void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, const Identifier& ident, const PropertySlot& slot, size_t cachedOffset)
822{
823    // regT0 holds a JSCell*
824    Jump failureCase = checkStructure(regT0, structure);
825    bool needsStubLink = false;
826    if (slot.cachedPropertyType() == PropertySlot::Getter) {
827        needsStubLink = true;
828        compileGetDirectOffset(regT0, regT2, regT1, structure, cachedOffset);
829        JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
830        stubCall.addArgument(regT1);
831        stubCall.addArgument(regT0);
832        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
833        stubCall.call();
834    } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
835        needsStubLink = true;
836        JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
837        stubCall.addArgument(regT0);
838        stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
839        stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
840        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
841        stubCall.call();
842    } else
843        compileGetDirectOffset(regT0, regT1, regT0, structure, cachedOffset);
844
845    Jump success = jump();
846
847    LinkBuffer patchBuffer(this, m_codeBlock->executablePool(), 0);
848    if (needsStubLink) {
849        for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
850            if (iter->to)
851                patchBuffer.link(iter->from, FunctionPtr(iter->to));
852        }
853    }
854    // Use the patch information to link the failure cases back to the original slow case routine.
855    CodeLocationLabel lastProtoBegin = polymorphicStructures->list[currentIndex - 1].stubRoutine;
856    if (!lastProtoBegin)
857        lastProtoBegin = stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall);
858
859    patchBuffer.link(failureCase, lastProtoBegin);
860
861    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
862    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
863
864    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
865
866    polymorphicStructures->list[currentIndex].set(*m_globalData, m_codeBlock->ownerExecutable(), entryLabel, structure);
867
868    // Finally patch the jump to slow case back in the hot path to jump here instead.
869    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
870    RepatchBuffer repatchBuffer(m_codeBlock);
871    repatchBuffer.relink(jumpLocation, entryLabel);
872}
873
874void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, const Identifier& ident, const PropertySlot& slot, size_t cachedOffset, CallFrame* callFrame)
875{
876    // regT0 holds a JSCell*
877
878    // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
879    // referencing the prototype object - let's speculatively load it's table nice and early!)
880    JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame));
881
882    // Check eax is an object of the right Structure.
883    Jump failureCases1 = checkStructure(regT0, structure);
884
885    // Check the prototype object's Structure had not changed.
886    const void* prototypeStructureAddress = protoObject->addressOfStructure();
887#if CPU(X86_64)
888    move(TrustedImmPtr(prototypeStructure), regT3);
889    Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3);
890#else
891    Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), TrustedImmPtr(prototypeStructure));
892#endif
893
894    bool needsStubLink = false;
895    if (slot.cachedPropertyType() == PropertySlot::Getter) {
896        needsStubLink = true;
897        compileGetDirectOffset(protoObject, regT2, regT1, cachedOffset);
898        JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
899        stubCall.addArgument(regT1);
900        stubCall.addArgument(regT0);
901        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
902        stubCall.call();
903    } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
904        needsStubLink = true;
905        JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
906        stubCall.addArgument(TrustedImmPtr(protoObject));
907        stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
908        stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
909        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
910        stubCall.call();
911    } else
912        compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
913
914    Jump success = jump();
915
916    LinkBuffer patchBuffer(this, m_codeBlock->executablePool(), 0);
917    if (needsStubLink) {
918        for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
919            if (iter->to)
920                patchBuffer.link(iter->from, FunctionPtr(iter->to));
921        }
922    }
923    // Use the patch information to link the failure cases back to the original slow case routine.
924    CodeLocationLabel lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine;
925    patchBuffer.link(failureCases1, lastProtoBegin);
926    patchBuffer.link(failureCases2, lastProtoBegin);
927
928    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
929    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
930
931    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
932
933    prototypeStructures->list[currentIndex].set(callFrame->globalData(), m_codeBlock->ownerExecutable(), entryLabel, structure, prototypeStructure);
934
935    // Finally patch the jump to slow case back in the hot path to jump here instead.
936    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
937    RepatchBuffer repatchBuffer(m_codeBlock);
938    repatchBuffer.relink(jumpLocation, entryLabel);
939}
940
941void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, StructureChain* chain, size_t count, const Identifier& ident, const PropertySlot& slot, size_t cachedOffset, CallFrame* callFrame)
942{
943    // regT0 holds a JSCell*
944    ASSERT(count);
945
946    JumpList bucketsOfFail;
947
948    // Check eax is an object of the right Structure.
949    bucketsOfFail.append(checkStructure(regT0, structure));
950
951    Structure* currStructure = structure;
952    WriteBarrier<Structure>* it = chain->head();
953    JSObject* protoObject = 0;
954    for (unsigned i = 0; i < count; ++i, ++it) {
955        protoObject = asObject(currStructure->prototypeForLookup(callFrame));
956        currStructure = it->get();
957        testPrototype(protoObject, bucketsOfFail);
958    }
959    ASSERT(protoObject);
960
961    bool needsStubLink = false;
962    if (slot.cachedPropertyType() == PropertySlot::Getter) {
963        needsStubLink = true;
964        compileGetDirectOffset(protoObject, regT2, regT1, cachedOffset);
965        JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
966        stubCall.addArgument(regT1);
967        stubCall.addArgument(regT0);
968        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
969        stubCall.call();
970    } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
971        needsStubLink = true;
972        JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
973        stubCall.addArgument(TrustedImmPtr(protoObject));
974        stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
975        stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
976        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
977        stubCall.call();
978    } else
979        compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
980
981    Jump success = jump();
982
983    LinkBuffer patchBuffer(this, m_codeBlock->executablePool(), 0);
984    if (needsStubLink) {
985        for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
986            if (iter->to)
987                patchBuffer.link(iter->from, FunctionPtr(iter->to));
988        }
989    }
990    // Use the patch information to link the failure cases back to the original slow case routine.
991    CodeLocationLabel lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine;
992
993    patchBuffer.link(bucketsOfFail, lastProtoBegin);
994
995    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
996    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
997
998    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
999
1000    // Track the stub we have created so that it will be deleted later.
1001    prototypeStructures->list[currentIndex].set(callFrame->globalData(), m_codeBlock->ownerExecutable(), entryLabel, structure, chain);
1002
1003    // Finally patch the jump to slow case back in the hot path to jump here instead.
1004    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
1005    RepatchBuffer repatchBuffer(m_codeBlock);
1006    repatchBuffer.relink(jumpLocation, entryLabel);
1007}
1008
1009void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, const Identifier& ident, const PropertySlot& slot, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
1010{
1011    // regT0 holds a JSCell*
1012    ASSERT(count);
1013
1014    JumpList bucketsOfFail;
1015
1016    // Check eax is an object of the right Structure.
1017    bucketsOfFail.append(checkStructure(regT0, structure));
1018
1019    Structure* currStructure = structure;
1020    WriteBarrier<Structure>* it = chain->head();
1021    JSObject* protoObject = 0;
1022    for (unsigned i = 0; i < count; ++i, ++it) {
1023        protoObject = asObject(currStructure->prototypeForLookup(callFrame));
1024        currStructure = it->get();
1025        testPrototype(protoObject, bucketsOfFail);
1026    }
1027    ASSERT(protoObject);
1028
1029    bool needsStubLink = false;
1030    if (slot.cachedPropertyType() == PropertySlot::Getter) {
1031        needsStubLink = true;
1032        compileGetDirectOffset(protoObject, regT2, regT1, cachedOffset);
1033        JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
1034        stubCall.addArgument(regT1);
1035        stubCall.addArgument(regT0);
1036        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
1037        stubCall.call();
1038    } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
1039        needsStubLink = true;
1040        JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
1041        stubCall.addArgument(TrustedImmPtr(protoObject));
1042        stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
1043        stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
1044        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
1045        stubCall.call();
1046    } else
1047        compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
1048    Jump success = jump();
1049
1050    LinkBuffer patchBuffer(this, m_codeBlock->executablePool(), 0);
1051    if (needsStubLink) {
1052        for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
1053            if (iter->to)
1054                patchBuffer.link(iter->from, FunctionPtr(iter->to));
1055        }
1056    }
1057    // Use the patch information to link the failure cases back to the original slow case routine.
1058    patchBuffer.link(bucketsOfFail, stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall));
1059
1060    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
1061    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
1062
1063    // Track the stub we have created so that it will be deleted later.
1064    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
1065    stubInfo->stubRoutine = entryLabel;
1066
1067    // Finally patch the jump to slow case back in the hot path to jump here instead.
1068    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
1069    RepatchBuffer repatchBuffer(m_codeBlock);
1070    repatchBuffer.relink(jumpLocation, entryLabel);
1071
1072    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
1073    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_proto_list));
1074}
1075
1076/* ------------------------------ END: !ENABLE / ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) ------------------------------ */
1077
1078#endif // !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
1079
1080void JIT::compileGetDirectOffset(RegisterID base, RegisterID resultTag, RegisterID resultPayload, RegisterID offset)
1081{
1082    ASSERT(sizeof(JSValue) == 8);
1083
1084    loadPtr(Address(base, OBJECT_OFFSETOF(JSObject, m_propertyStorage)), base);
1085    loadPtr(BaseIndex(base, offset, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload);
1086    loadPtr(BaseIndex(base, offset, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag);
1087}
1088
1089void JIT::emit_op_get_by_pname(Instruction* currentInstruction)
1090{
1091    unsigned dst = currentInstruction[1].u.operand;
1092    unsigned base = currentInstruction[2].u.operand;
1093    unsigned property = currentInstruction[3].u.operand;
1094    unsigned expected = currentInstruction[4].u.operand;
1095    unsigned iter = currentInstruction[5].u.operand;
1096    unsigned i = currentInstruction[6].u.operand;
1097
1098    emitLoad2(property, regT1, regT0, base, regT3, regT2);
1099    emitJumpSlowCaseIfNotJSCell(property, regT1);
1100    addSlowCase(branchPtr(NotEqual, regT0, payloadFor(expected)));
1101    // Property registers are now available as the property is known
1102    emitJumpSlowCaseIfNotJSCell(base, regT3);
1103    emitLoadPayload(iter, regT1);
1104
1105    // Test base's structure
1106    loadPtr(Address(regT2, JSCell::structureOffset()), regT0);
1107    addSlowCase(branchPtr(NotEqual, regT0, Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedStructure))));
1108    load32(addressFor(i), regT3);
1109    sub32(TrustedImm32(1), regT3);
1110    addSlowCase(branch32(AboveOrEqual, regT3, Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_numCacheableSlots))));
1111    compileGetDirectOffset(regT2, regT1, regT0, regT3);
1112
1113    emitStore(dst, regT1, regT0);
1114    map(m_bytecodeOffset + OPCODE_LENGTH(op_get_by_pname), dst, regT1, regT0);
1115}
1116
1117void JIT::emitSlow_op_get_by_pname(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
1118{
1119    unsigned dst = currentInstruction[1].u.operand;
1120    unsigned base = currentInstruction[2].u.operand;
1121    unsigned property = currentInstruction[3].u.operand;
1122
1123    linkSlowCaseIfNotJSCell(iter, property);
1124    linkSlowCase(iter);
1125    linkSlowCaseIfNotJSCell(iter, base);
1126    linkSlowCase(iter);
1127    linkSlowCase(iter);
1128
1129    JITStubCall stubCall(this, cti_op_get_by_val);
1130    stubCall.addArgument(base);
1131    stubCall.addArgument(property);
1132    stubCall.call(dst);
1133}
1134
1135} // namespace JSC
1136
1137#endif // USE(JSVALUE32_64)
1138#endif // ENABLE(JIT)
1139