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#include "JIT.h"
30
31#include "CodeBlock.h"
32#include "GCAwareJITStubRoutine.h"
33#include "GetterSetter.h"
34#include "Interpreter.h"
35#include "JITInlines.h"
36#include "JITStubCall.h"
37#include "JSArray.h"
38#include "JSFunction.h"
39#include "JSPropertyNameIterator.h"
40#include "JSVariableObject.h"
41#include "LinkBuffer.h"
42#include "RepatchBuffer.h"
43#include "ResultType.h"
44#include "SamplingTool.h"
45#include <wtf/StringPrintStream.h>
46
47#ifndef NDEBUG
48#include <stdio.h>
49#endif
50
51using namespace std;
52
53namespace JSC {
54#if USE(JSVALUE64)
55
56JIT::CodeRef JIT::stringGetByValStubGenerator(VM* vm)
57{
58    JSInterfaceJIT jit;
59    JumpList failures;
60    failures.append(jit.branchPtr(NotEqual, Address(regT0, JSCell::structureOffset()), TrustedImmPtr(vm->stringStructure.get())));
61
62    // Load string length to regT2, and start the process of loading the data pointer into regT0
63    jit.load32(Address(regT0, ThunkHelpers::jsStringLengthOffset()), regT2);
64    jit.loadPtr(Address(regT0, ThunkHelpers::jsStringValueOffset()), regT0);
65    failures.append(jit.branchTest32(Zero, regT0));
66
67    // Do an unsigned compare to simultaneously filter negative indices as well as indices that are too large
68    failures.append(jit.branch32(AboveOrEqual, regT1, regT2));
69
70    // Load the character
71    JumpList is16Bit;
72    JumpList cont8Bit;
73    // Load the string flags
74    jit.loadPtr(Address(regT0, StringImpl::flagsOffset()), regT2);
75    jit.loadPtr(Address(regT0, StringImpl::dataOffset()), regT0);
76    is16Bit.append(jit.branchTest32(Zero, regT2, TrustedImm32(StringImpl::flagIs8Bit())));
77    jit.load8(BaseIndex(regT0, regT1, TimesOne, 0), regT0);
78    cont8Bit.append(jit.jump());
79    is16Bit.link(&jit);
80    jit.load16(BaseIndex(regT0, regT1, TimesTwo, 0), regT0);
81    cont8Bit.link(&jit);
82
83    failures.append(jit.branch32(AboveOrEqual, regT0, TrustedImm32(0x100)));
84    jit.move(TrustedImmPtr(vm->smallStrings.singleCharacterStrings()), regT1);
85    jit.loadPtr(BaseIndex(regT1, regT0, ScalePtr, 0), regT0);
86    jit.ret();
87
88    failures.link(&jit);
89    jit.move(TrustedImm32(0), regT0);
90    jit.ret();
91
92    LinkBuffer patchBuffer(*vm, &jit, GLOBAL_THUNK_ID);
93    return FINALIZE_CODE(patchBuffer, ("String get_by_val stub"));
94}
95
96void JIT::emit_op_get_by_val(Instruction* currentInstruction)
97{
98    unsigned dst = currentInstruction[1].u.operand;
99    unsigned base = currentInstruction[2].u.operand;
100    unsigned property = currentInstruction[3].u.operand;
101    ArrayProfile* profile = currentInstruction[4].u.arrayProfile;
102
103    emitGetVirtualRegisters(base, regT0, property, regT1);
104    emitJumpSlowCaseIfNotImmediateInteger(regT1);
105
106    // This is technically incorrect - we're zero-extending an int32.  On the hot path this doesn't matter.
107    // We check the value as if it was a uint32 against the m_vectorLength - which will always fail if
108    // number was signed since m_vectorLength is always less than intmax (since the total allocation
109    // size is always less than 4Gb).  As such zero extending wil have been correct (and extending the value
110    // to 64-bits is necessary since it's used in the address calculation.  We zero extend rather than sign
111    // extending since it makes it easier to re-tag the value in the slow case.
112    zeroExtend32ToPtr(regT1, regT1);
113
114    emitJumpSlowCaseIfNotJSCell(regT0, base);
115    loadPtr(Address(regT0, JSCell::structureOffset()), regT2);
116    emitArrayProfilingSite(regT2, regT3, profile);
117    and32(TrustedImm32(IndexingShapeMask), regT2);
118
119    PatchableJump badType;
120    JumpList slowCases;
121
122    JITArrayMode mode = chooseArrayMode(profile);
123    switch (mode) {
124    case JITInt32:
125        slowCases = emitInt32GetByVal(currentInstruction, badType);
126        break;
127    case JITDouble:
128        slowCases = emitDoubleGetByVal(currentInstruction, badType);
129        break;
130    case JITContiguous:
131        slowCases = emitContiguousGetByVal(currentInstruction, badType);
132        break;
133    case JITArrayStorage:
134        slowCases = emitArrayStorageGetByVal(currentInstruction, badType);
135        break;
136    default:
137        CRASH();
138        break;
139    }
140
141    addSlowCase(badType);
142    addSlowCase(slowCases);
143
144    Label done = label();
145
146#if !ASSERT_DISABLED
147    Jump resultOK = branchTest64(NonZero, regT0);
148    breakpoint();
149    resultOK.link(this);
150#endif
151
152    emitValueProfilingSite();
153    emitPutVirtualRegister(dst);
154
155    m_byValCompilationInfo.append(ByValCompilationInfo(m_bytecodeOffset, badType, mode, done));
156}
157
158JIT::JumpList JIT::emitDoubleGetByVal(Instruction*, PatchableJump& badType)
159{
160    JumpList slowCases;
161
162    badType = patchableBranch32(NotEqual, regT2, TrustedImm32(DoubleShape));
163    loadPtr(Address(regT0, JSObject::butterflyOffset()), regT2);
164    slowCases.append(branch32(AboveOrEqual, regT1, Address(regT2, Butterfly::offsetOfPublicLength())));
165    loadDouble(BaseIndex(regT2, regT1, TimesEight), fpRegT0);
166    slowCases.append(branchDouble(DoubleNotEqualOrUnordered, fpRegT0, fpRegT0));
167    moveDoubleTo64(fpRegT0, regT0);
168    sub64(tagTypeNumberRegister, regT0);
169
170    return slowCases;
171}
172
173JIT::JumpList JIT::emitContiguousGetByVal(Instruction*, PatchableJump& badType, IndexingType expectedShape)
174{
175    JumpList slowCases;
176
177    badType = patchableBranch32(NotEqual, regT2, TrustedImm32(expectedShape));
178    loadPtr(Address(regT0, JSObject::butterflyOffset()), regT2);
179    slowCases.append(branch32(AboveOrEqual, regT1, Address(regT2, Butterfly::offsetOfPublicLength())));
180    load64(BaseIndex(regT2, regT1, TimesEight), regT0);
181    slowCases.append(branchTest64(Zero, regT0));
182
183    return slowCases;
184}
185
186JIT::JumpList JIT::emitArrayStorageGetByVal(Instruction*, PatchableJump& badType)
187{
188    JumpList slowCases;
189
190    add32(TrustedImm32(-ArrayStorageShape), regT2, regT3);
191    badType = patchableBranch32(Above, regT3, TrustedImm32(SlowPutArrayStorageShape - ArrayStorageShape));
192
193    loadPtr(Address(regT0, JSObject::butterflyOffset()), regT2);
194    slowCases.append(branch32(AboveOrEqual, regT1, Address(regT2, ArrayStorage::vectorLengthOffset())));
195
196    load64(BaseIndex(regT2, regT1, TimesEight, ArrayStorage::vectorOffset()), regT0);
197    slowCases.append(branchTest64(Zero, regT0));
198
199    return slowCases;
200}
201
202void JIT::emitSlow_op_get_by_val(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
203{
204    unsigned dst = currentInstruction[1].u.operand;
205    unsigned base = currentInstruction[2].u.operand;
206    unsigned property = currentInstruction[3].u.operand;
207    ArrayProfile* profile = currentInstruction[4].u.arrayProfile;
208
209    linkSlowCase(iter); // property int32 check
210    linkSlowCaseIfNotJSCell(iter, base); // base cell check
211    Jump nonCell = jump();
212    linkSlowCase(iter); // base array check
213    Jump notString = branchPtr(NotEqual, Address(regT0, JSCell::structureOffset()), TrustedImmPtr(m_vm->stringStructure.get()));
214    emitNakedCall(CodeLocationLabel(m_vm->getCTIStub(stringGetByValStubGenerator).code()));
215    Jump failed = branchTest64(Zero, regT0);
216    emitPutVirtualRegister(dst, regT0);
217    emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_get_by_val));
218    failed.link(this);
219    notString.link(this);
220    nonCell.link(this);
221
222    Jump skipProfiling = jump();
223
224    linkSlowCase(iter); // vector length check
225    linkSlowCase(iter); // empty value
226
227    emitArrayProfileOutOfBoundsSpecialCase(profile);
228
229    skipProfiling.link(this);
230
231    Label slowPath = label();
232
233    JITStubCall stubCall(this, cti_op_get_by_val);
234    stubCall.addArgument(base, regT2);
235    stubCall.addArgument(property, regT2);
236    Call call = stubCall.call(dst);
237
238    m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath;
239    m_byValCompilationInfo[m_byValInstructionIndex].returnAddress = call;
240    m_byValInstructionIndex++;
241
242    emitValueProfilingSite();
243}
244
245void JIT::compileGetDirectOffset(RegisterID base, RegisterID result, RegisterID offset, RegisterID scratch, FinalObjectMode finalObjectMode)
246{
247    ASSERT(sizeof(JSValue) == 8);
248
249    if (finalObjectMode == MayBeFinal) {
250        Jump isInline = branch32(LessThan, offset, TrustedImm32(firstOutOfLineOffset));
251        loadPtr(Address(base, JSObject::butterflyOffset()), scratch);
252        neg32(offset);
253        Jump done = jump();
254        isInline.link(this);
255        addPtr(TrustedImm32(JSObject::offsetOfInlineStorage() - (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue)), base, scratch);
256        done.link(this);
257    } else {
258#if !ASSERT_DISABLED
259        Jump isOutOfLine = branch32(GreaterThanOrEqual, offset, TrustedImm32(firstOutOfLineOffset));
260        breakpoint();
261        isOutOfLine.link(this);
262#endif
263        loadPtr(Address(base, JSObject::butterflyOffset()), scratch);
264        neg32(offset);
265    }
266    signExtend32ToPtr(offset, offset);
267    load64(BaseIndex(scratch, offset, TimesEight, (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue)), result);
268}
269
270void JIT::emit_op_get_by_pname(Instruction* currentInstruction)
271{
272    unsigned dst = currentInstruction[1].u.operand;
273    unsigned base = currentInstruction[2].u.operand;
274    unsigned property = currentInstruction[3].u.operand;
275    unsigned expected = currentInstruction[4].u.operand;
276    unsigned iter = currentInstruction[5].u.operand;
277    unsigned i = currentInstruction[6].u.operand;
278
279    emitGetVirtualRegister(property, regT0);
280    addSlowCase(branch64(NotEqual, regT0, addressFor(expected)));
281    emitGetVirtualRegisters(base, regT0, iter, regT1);
282    emitJumpSlowCaseIfNotJSCell(regT0, base);
283
284    // Test base's structure
285    loadPtr(Address(regT0, JSCell::structureOffset()), regT2);
286    addSlowCase(branchPtr(NotEqual, regT2, Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedStructure))));
287    load32(addressFor(i), regT3);
288    sub32(TrustedImm32(1), regT3);
289    addSlowCase(branch32(AboveOrEqual, regT3, Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_numCacheableSlots))));
290    Jump inlineProperty = branch32(Below, regT3, Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedStructureInlineCapacity)));
291    add32(TrustedImm32(firstOutOfLineOffset), regT3);
292    sub32(Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedStructureInlineCapacity)), regT3);
293    inlineProperty.link(this);
294    compileGetDirectOffset(regT0, regT0, regT3, regT1);
295
296    emitPutVirtualRegister(dst, regT0);
297}
298
299void JIT::emitSlow_op_get_by_pname(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
300{
301    unsigned dst = currentInstruction[1].u.operand;
302    unsigned base = currentInstruction[2].u.operand;
303    unsigned property = currentInstruction[3].u.operand;
304
305    linkSlowCase(iter);
306    linkSlowCaseIfNotJSCell(iter, base);
307    linkSlowCase(iter);
308    linkSlowCase(iter);
309
310    JITStubCall stubCall(this, cti_op_get_by_val_generic);
311    stubCall.addArgument(base, regT2);
312    stubCall.addArgument(property, regT2);
313    stubCall.call(dst);
314}
315
316void JIT::emit_op_put_by_val(Instruction* currentInstruction)
317{
318    unsigned base = currentInstruction[1].u.operand;
319    unsigned property = currentInstruction[2].u.operand;
320    ArrayProfile* profile = currentInstruction[4].u.arrayProfile;
321
322    emitGetVirtualRegisters(base, regT0, property, regT1);
323    emitJumpSlowCaseIfNotImmediateInteger(regT1);
324    // See comment in op_get_by_val.
325    zeroExtend32ToPtr(regT1, regT1);
326    emitJumpSlowCaseIfNotJSCell(regT0, base);
327    loadPtr(Address(regT0, JSCell::structureOffset()), regT2);
328    emitArrayProfilingSite(regT2, regT3, profile);
329    and32(TrustedImm32(IndexingShapeMask), regT2);
330
331    PatchableJump badType;
332    JumpList slowCases;
333
334    JITArrayMode mode = chooseArrayMode(profile);
335    switch (mode) {
336    case JITInt32:
337        slowCases = emitInt32PutByVal(currentInstruction, badType);
338        break;
339    case JITDouble:
340        slowCases = emitDoublePutByVal(currentInstruction, badType);
341        break;
342    case JITContiguous:
343        slowCases = emitContiguousPutByVal(currentInstruction, badType);
344        break;
345    case JITArrayStorage:
346        slowCases = emitArrayStoragePutByVal(currentInstruction, badType);
347        break;
348    default:
349        CRASH();
350        break;
351    }
352
353    addSlowCase(badType);
354    addSlowCase(slowCases);
355
356    Label done = label();
357
358    m_byValCompilationInfo.append(ByValCompilationInfo(m_bytecodeOffset, badType, mode, done));
359
360    emitWriteBarrier(regT0, regT3, regT1, regT3, ShouldFilterImmediates, WriteBarrierForPropertyAccess);
361}
362
363JIT::JumpList JIT::emitGenericContiguousPutByVal(Instruction* currentInstruction, PatchableJump& badType, IndexingType indexingShape)
364{
365    unsigned value = currentInstruction[3].u.operand;
366    ArrayProfile* profile = currentInstruction[4].u.arrayProfile;
367
368    JumpList slowCases;
369
370    badType = patchableBranch32(NotEqual, regT2, TrustedImm32(indexingShape));
371
372    loadPtr(Address(regT0, JSObject::butterflyOffset()), regT2);
373    Jump outOfBounds = branch32(AboveOrEqual, regT1, Address(regT2, Butterfly::offsetOfPublicLength()));
374
375    Label storeResult = label();
376    emitGetVirtualRegister(value, regT3);
377    switch (indexingShape) {
378    case Int32Shape:
379        slowCases.append(emitJumpIfNotImmediateInteger(regT3));
380        store64(regT3, BaseIndex(regT2, regT1, TimesEight));
381        break;
382    case DoubleShape: {
383        Jump notInt = emitJumpIfNotImmediateInteger(regT3);
384        convertInt32ToDouble(regT3, fpRegT0);
385        Jump ready = jump();
386        notInt.link(this);
387        add64(tagTypeNumberRegister, regT3);
388        move64ToDouble(regT3, fpRegT0);
389        slowCases.append(branchDouble(DoubleNotEqualOrUnordered, fpRegT0, fpRegT0));
390        ready.link(this);
391        storeDouble(fpRegT0, BaseIndex(regT2, regT1, TimesEight));
392        break;
393    }
394    case ContiguousShape:
395        store64(regT3, BaseIndex(regT2, regT1, TimesEight));
396        break;
397    default:
398        CRASH();
399        break;
400    }
401
402    Jump done = jump();
403    outOfBounds.link(this);
404
405    slowCases.append(branch32(AboveOrEqual, regT1, Address(regT2, Butterfly::offsetOfVectorLength())));
406
407    emitArrayProfileStoreToHoleSpecialCase(profile);
408
409    add32(TrustedImm32(1), regT1, regT3);
410    store32(regT3, Address(regT2, Butterfly::offsetOfPublicLength()));
411    jump().linkTo(storeResult, this);
412
413    done.link(this);
414
415    return slowCases;
416}
417
418JIT::JumpList JIT::emitArrayStoragePutByVal(Instruction* currentInstruction, PatchableJump& badType)
419{
420    unsigned value = currentInstruction[3].u.operand;
421    ArrayProfile* profile = currentInstruction[4].u.arrayProfile;
422
423    JumpList slowCases;
424
425    badType = patchableBranch32(NotEqual, regT2, TrustedImm32(ArrayStorageShape));
426    loadPtr(Address(regT0, JSObject::butterflyOffset()), regT2);
427    slowCases.append(branch32(AboveOrEqual, regT1, Address(regT2, ArrayStorage::vectorLengthOffset())));
428
429    Jump empty = branchTest64(Zero, BaseIndex(regT2, regT1, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])));
430
431    Label storeResult(this);
432    emitGetVirtualRegister(value, regT3);
433    store64(regT3, BaseIndex(regT2, regT1, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])));
434    Jump end = jump();
435
436    empty.link(this);
437    emitArrayProfileStoreToHoleSpecialCase(profile);
438    add32(TrustedImm32(1), Address(regT2, ArrayStorage::numValuesInVectorOffset()));
439    branch32(Below, regT1, Address(regT2, ArrayStorage::lengthOffset())).linkTo(storeResult, this);
440
441    add32(TrustedImm32(1), regT1);
442    store32(regT1, Address(regT2, ArrayStorage::lengthOffset()));
443    sub32(TrustedImm32(1), regT1);
444    jump().linkTo(storeResult, this);
445
446    end.link(this);
447
448    return slowCases;
449}
450
451void JIT::emitSlow_op_put_by_val(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
452{
453    unsigned base = currentInstruction[1].u.operand;
454    unsigned property = currentInstruction[2].u.operand;
455    unsigned value = currentInstruction[3].u.operand;
456    ArrayProfile* profile = currentInstruction[4].u.arrayProfile;
457
458    linkSlowCase(iter); // property int32 check
459    linkSlowCaseIfNotJSCell(iter, base); // base cell check
460    linkSlowCase(iter); // base not array check
461
462    JITArrayMode mode = chooseArrayMode(profile);
463    switch (mode) {
464    case JITInt32:
465    case JITDouble:
466        linkSlowCase(iter); // value type check
467        break;
468    default:
469        break;
470    }
471
472    Jump skipProfiling = jump();
473    linkSlowCase(iter); // out of bounds
474    emitArrayProfileOutOfBoundsSpecialCase(profile);
475    skipProfiling.link(this);
476
477    Label slowPath = label();
478
479    JITStubCall stubPutByValCall(this, cti_op_put_by_val);
480    stubPutByValCall.addArgument(regT0);
481    stubPutByValCall.addArgument(property, regT2);
482    stubPutByValCall.addArgument(value, regT2);
483    Call call = stubPutByValCall.call();
484
485    m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath;
486    m_byValCompilationInfo[m_byValInstructionIndex].returnAddress = call;
487    m_byValInstructionIndex++;
488}
489
490void JIT::emit_op_put_by_index(Instruction* currentInstruction)
491{
492    JITStubCall stubCall(this, cti_op_put_by_index);
493    stubCall.addArgument(currentInstruction[1].u.operand, regT2);
494    stubCall.addArgument(TrustedImm32(currentInstruction[2].u.operand));
495    stubCall.addArgument(currentInstruction[3].u.operand, regT2);
496    stubCall.call();
497}
498
499void JIT::emit_op_put_getter_setter(Instruction* currentInstruction)
500{
501    JITStubCall stubCall(this, cti_op_put_getter_setter);
502    stubCall.addArgument(currentInstruction[1].u.operand, regT2);
503    stubCall.addArgument(TrustedImmPtr(&m_codeBlock->identifier(currentInstruction[2].u.operand)));
504    stubCall.addArgument(currentInstruction[3].u.operand, regT2);
505    stubCall.addArgument(currentInstruction[4].u.operand, regT2);
506    stubCall.call();
507}
508
509void JIT::emit_op_del_by_id(Instruction* currentInstruction)
510{
511    JITStubCall stubCall(this, cti_op_del_by_id);
512    stubCall.addArgument(currentInstruction[2].u.operand, regT2);
513    stubCall.addArgument(TrustedImmPtr(&m_codeBlock->identifier(currentInstruction[3].u.operand)));
514    stubCall.call(currentInstruction[1].u.operand);
515}
516
517void JIT::emit_op_get_by_id(Instruction* currentInstruction)
518{
519    unsigned resultVReg = currentInstruction[1].u.operand;
520    unsigned baseVReg = currentInstruction[2].u.operand;
521    Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
522
523    emitGetVirtualRegister(baseVReg, regT0);
524    compileGetByIdHotPath(baseVReg, ident);
525    emitValueProfilingSite();
526    emitPutVirtualRegister(resultVReg);
527}
528
529void JIT::compileGetByIdHotPath(int baseVReg, Identifier* ident)
530{
531    // As for put_by_id, get_by_id requires the offset of the Structure and the offset of the access to be patched.
532    // Additionally, for get_by_id we need patch the offset of the branch to the slow case (we patch this to jump
533    // to array-length / prototype access tranpolines, and finally we also the the property-map access offset as a label
534    // to jump back to if one of these trampolies finds a match.
535
536    emitJumpSlowCaseIfNotJSCell(regT0, baseVReg);
537
538    if (*ident == m_vm->propertyNames->length && shouldEmitProfiling()) {
539        loadPtr(Address(regT0, JSCell::structureOffset()), regT1);
540        emitArrayProfilingSiteForBytecodeIndex(regT1, regT2, m_bytecodeOffset);
541    }
542
543    BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdHotPath);
544
545    Label hotPathBegin(this);
546
547    DataLabelPtr structureToCompare;
548    PatchableJump structureCheck = patchableBranchPtrWithPatch(NotEqual, Address(regT0, JSCell::structureOffset()), structureToCompare, TrustedImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure)));
549    addSlowCase(structureCheck);
550
551    ConvertibleLoadLabel propertyStorageLoad = convertibleLoadPtr(Address(regT0, JSObject::butterflyOffset()), regT0);
552    DataLabelCompact displacementLabel = load64WithCompactAddressOffsetPatch(Address(regT0, patchGetByIdDefaultOffset), regT0);
553
554    Label putResult(this);
555
556    END_UNINTERRUPTED_SEQUENCE(sequenceGetByIdHotPath);
557
558    m_propertyAccessCompilationInfo.append(PropertyStubCompilationInfo(PropertyStubGetById, m_bytecodeOffset, hotPathBegin, structureToCompare, structureCheck, propertyStorageLoad, displacementLabel, putResult));
559}
560
561void JIT::emitSlow_op_get_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
562{
563    unsigned resultVReg = currentInstruction[1].u.operand;
564    unsigned baseVReg = currentInstruction[2].u.operand;
565    Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
566
567    compileGetByIdSlowCase(resultVReg, baseVReg, ident, iter);
568    emitValueProfilingSite();
569}
570
571void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident, Vector<SlowCaseEntry>::iterator& iter)
572{
573    // As for the hot path of get_by_id, above, we ensure that we can use an architecture specific offset
574    // so that we only need track one pointer into the slow case code - we track a pointer to the location
575    // of the call (which we can use to look up the patch information), but should a array-length or
576    // prototype access trampoline fail we want to bail out back to here.  To do so we can subtract back
577    // the distance from the call to the head of the slow case.
578
579    linkSlowCaseIfNotJSCell(iter, baseVReg);
580    linkSlowCase(iter);
581
582    BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdSlowCase);
583
584    Label coldPathBegin(this);
585    JITStubCall stubCall(this, cti_op_get_by_id);
586    stubCall.addArgument(regT0);
587    stubCall.addArgument(TrustedImmPtr(ident));
588    Call call = stubCall.call(resultVReg);
589
590    END_UNINTERRUPTED_SEQUENCE(sequenceGetByIdSlowCase);
591
592    // Track the location of the call; this will be used to recover patch information.
593    m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex++].slowCaseInfo(PropertyStubGetById, coldPathBegin, call);
594}
595
596void JIT::emit_op_put_by_id(Instruction* currentInstruction)
597{
598    unsigned baseVReg = currentInstruction[1].u.operand;
599    unsigned valueVReg = currentInstruction[3].u.operand;
600
601    // In order to be able to patch both the Structure, and the object offset, we store one pointer,
602    // to just after the arguments have been loaded into registers 'hotPathBegin', and we generate code
603    // such that the Structure & offset are always at the same distance from this.
604
605    emitGetVirtualRegisters(baseVReg, regT0, valueVReg, regT1);
606
607    // Jump to a slow case if either the base object is an immediate, or if the Structure does not match.
608    emitJumpSlowCaseIfNotJSCell(regT0, baseVReg);
609
610    BEGIN_UNINTERRUPTED_SEQUENCE(sequencePutById);
611
612    Label hotPathBegin(this);
613
614    // It is important that the following instruction plants a 32bit immediate, in order that it can be patched over.
615    DataLabelPtr structureToCompare;
616    addSlowCase(branchPtrWithPatch(NotEqual, Address(regT0, JSCell::structureOffset()), structureToCompare, TrustedImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure))));
617
618    ConvertibleLoadLabel propertyStorageLoad = convertibleLoadPtr(Address(regT0, JSObject::butterflyOffset()), regT2);
619    DataLabel32 displacementLabel = store64WithAddressOffsetPatch(regT1, Address(regT2, patchPutByIdDefaultOffset));
620
621    END_UNINTERRUPTED_SEQUENCE(sequencePutById);
622
623    emitWriteBarrier(regT0, regT1, regT2, regT3, ShouldFilterImmediates, WriteBarrierForPropertyAccess);
624
625    m_propertyAccessCompilationInfo.append(PropertyStubCompilationInfo(PropertyStubPutById, m_bytecodeOffset, hotPathBegin, structureToCompare, propertyStorageLoad, displacementLabel));
626}
627
628void JIT::emitSlow_op_put_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
629{
630    unsigned baseVReg = currentInstruction[1].u.operand;
631    Identifier* ident = &(m_codeBlock->identifier(currentInstruction[2].u.operand));
632    unsigned direct = currentInstruction[8].u.operand;
633
634    linkSlowCaseIfNotJSCell(iter, baseVReg);
635    linkSlowCase(iter);
636
637    JITStubCall stubCall(this, direct ? cti_op_put_by_id_direct : cti_op_put_by_id);
638    stubCall.addArgument(regT0);
639    stubCall.addArgument(TrustedImmPtr(ident));
640    stubCall.addArgument(regT1);
641    move(regT0, nonArgGPR1);
642    Call call = stubCall.call();
643
644    // Track the location of the call; this will be used to recover patch information.
645    m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex++].slowCaseInfo(PropertyStubPutById, call);
646}
647
648// Compile a store into an object's property storage.  May overwrite the
649// value in objectReg.
650void JIT::compilePutDirectOffset(RegisterID base, RegisterID value, PropertyOffset cachedOffset)
651{
652    if (isInlineOffset(cachedOffset)) {
653        store64(value, Address(base, JSObject::offsetOfInlineStorage() + sizeof(JSValue) * offsetInInlineStorage(cachedOffset)));
654        return;
655    }
656
657    loadPtr(Address(base, JSObject::butterflyOffset()), base);
658    store64(value, Address(base, sizeof(JSValue) * offsetInButterfly(cachedOffset)));
659}
660
661// Compile a load from an object's property storage.  May overwrite base.
662void JIT::compileGetDirectOffset(RegisterID base, RegisterID result, PropertyOffset cachedOffset)
663{
664    if (isInlineOffset(cachedOffset)) {
665        load64(Address(base, JSObject::offsetOfInlineStorage() + sizeof(JSValue) * offsetInInlineStorage(cachedOffset)), result);
666        return;
667    }
668
669    loadPtr(Address(base, JSObject::butterflyOffset()), result);
670    load64(Address(result, sizeof(JSValue) * offsetInButterfly(cachedOffset)), result);
671}
672
673void JIT::compileGetDirectOffset(JSObject* base, RegisterID result, PropertyOffset cachedOffset)
674{
675    if (isInlineOffset(cachedOffset)) {
676        load64(base->locationForOffset(cachedOffset), result);
677        return;
678    }
679
680    loadPtr(base->butterflyAddress(), result);
681    load64(Address(result, offsetInButterfly(cachedOffset) * sizeof(WriteBarrier<Unknown>)), result);
682}
683
684void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure* oldStructure, Structure* newStructure, PropertyOffset cachedOffset, StructureChain* chain, ReturnAddressPtr returnAddress, bool direct)
685{
686    move(nonArgGPR1, regT0);
687
688    JumpList failureCases;
689    // Check eax is an object of the right Structure.
690    failureCases.append(emitJumpIfNotJSCell(regT0));
691    failureCases.append(branchPtr(NotEqual, Address(regT0, JSCell::structureOffset()), TrustedImmPtr(oldStructure)));
692
693    testPrototype(oldStructure->storedPrototype(), failureCases, stubInfo);
694
695    ASSERT(oldStructure->storedPrototype().isNull() || oldStructure->storedPrototype().asCell()->structure() == chain->head()->get());
696
697    // ecx = baseObject->m_structure
698    if (!direct) {
699        for (WriteBarrier<Structure>* it = chain->head(); *it; ++it) {
700            ASSERT((*it)->storedPrototype().isNull() || (*it)->storedPrototype().asCell()->structure() == it[1].get());
701            testPrototype((*it)->storedPrototype(), failureCases, stubInfo);
702        }
703    }
704
705    // If we succeed in all of our checks, and the code was optimizable, then make sure we
706    // decrement the rare case counter.
707#if ENABLE(VALUE_PROFILER)
708    if (m_codeBlock->canCompileWithDFG() >= DFG::MayInline) {
709        sub32(
710            TrustedImm32(1),
711            AbsoluteAddress(&m_codeBlock->rareCaseProfileForBytecodeOffset(stubInfo->bytecodeIndex)->m_counter));
712    }
713#endif
714
715    // emit a call only if storage realloc is needed
716    bool willNeedStorageRealloc = oldStructure->outOfLineCapacity() != newStructure->outOfLineCapacity();
717    if (willNeedStorageRealloc) {
718        // This trampoline was called to like a JIT stub; before we can can call again we need to
719        // remove the return address from the stack, to prevent the stack from becoming misaligned.
720        preserveReturnAddressAfterCall(regT3);
721
722        JITStubCall stubCall(this, cti_op_put_by_id_transition_realloc);
723        stubCall.skipArgument(); // base
724        stubCall.skipArgument(); // ident
725        stubCall.skipArgument(); // value
726        stubCall.addArgument(TrustedImm32(oldStructure->outOfLineCapacity()));
727        stubCall.addArgument(TrustedImmPtr(newStructure));
728        stubCall.call(regT0);
729        emitGetJITStubArg(2, regT1);
730
731        restoreReturnAddressBeforeReturn(regT3);
732    }
733
734    // Planting the new structure triggers the write barrier so we need
735    // an unconditional barrier here.
736    emitWriteBarrier(regT0, regT1, regT2, regT3, UnconditionalWriteBarrier, WriteBarrierForPropertyAccess);
737
738    ASSERT(newStructure->classInfo() == oldStructure->classInfo());
739    storePtr(TrustedImmPtr(newStructure), Address(regT0, JSCell::structureOffset()));
740    compilePutDirectOffset(regT0, regT1, cachedOffset);
741
742    ret();
743
744    ASSERT(!failureCases.empty());
745    failureCases.link(this);
746    restoreArgumentReferenceForTrampoline();
747    Call failureCall = tailRecursiveCall();
748
749    LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
750
751    patchBuffer.link(failureCall, FunctionPtr(direct ? cti_op_put_by_id_direct_fail : cti_op_put_by_id_fail));
752
753    if (willNeedStorageRealloc) {
754        ASSERT(m_calls.size() == 1);
755        patchBuffer.link(m_calls[0].from, FunctionPtr(cti_op_put_by_id_transition_realloc));
756    }
757
758    stubInfo->stubRoutine = createJITStubRoutine(
759        FINALIZE_CODE(
760            patchBuffer,
761            ("Baseline put_by_id transition for %s, return point %p",
762                toCString(*m_codeBlock).data(), returnAddress.value())),
763        *m_vm,
764        m_codeBlock->ownerExecutable(),
765        willNeedStorageRealloc,
766        newStructure);
767    RepatchBuffer repatchBuffer(m_codeBlock);
768    repatchBuffer.relinkCallerToTrampoline(returnAddress, CodeLocationLabel(stubInfo->stubRoutine->code().code()));
769}
770
771void JIT::patchGetByIdSelf(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, PropertyOffset cachedOffset, ReturnAddressPtr returnAddress)
772{
773    RepatchBuffer repatchBuffer(codeBlock);
774
775    // We don't want to patch more than once - in future go to cti_op_get_by_id_generic.
776    // Should probably go to cti_op_get_by_id_fail, but that doesn't do anything interesting right now.
777    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_self_fail));
778
779    // Patch the offset into the propoerty map to load from, then patch the Structure to look for.
780    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.get.structureToCompare), structure);
781    repatchBuffer.setLoadInstructionIsActive(stubInfo->hotPathBegin.convertibleLoadAtOffset(stubInfo->patch.baseline.u.get.propertyStorageLoad), isOutOfLineOffset(cachedOffset));
782    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelCompactAtOffset(stubInfo->patch.baseline.u.get.displacementLabel), offsetRelativeToPatchedStorage(cachedOffset));
783}
784
785void JIT::patchPutByIdReplace(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, PropertyOffset cachedOffset, ReturnAddressPtr returnAddress, bool direct)
786{
787    RepatchBuffer repatchBuffer(codeBlock);
788
789    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
790    // Should probably go to cti_op_put_by_id_fail, but that doesn't do anything interesting right now.
791    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(direct ? cti_op_put_by_id_direct_generic : cti_op_put_by_id_generic));
792
793    // Patch the offset into the propoerty map to load from, then patch the Structure to look for.
794    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.put.structureToCompare), structure);
795    repatchBuffer.setLoadInstructionIsActive(stubInfo->hotPathBegin.convertibleLoadAtOffset(stubInfo->patch.baseline.u.put.propertyStorageLoad), isOutOfLineOffset(cachedOffset));
796    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(stubInfo->patch.baseline.u.put.displacementLabel), offsetRelativeToPatchedStorage(cachedOffset));
797}
798
799void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress)
800{
801    StructureStubInfo* stubInfo = &m_codeBlock->getStubInfo(returnAddress);
802
803    // Check eax is an array
804    loadPtr(Address(regT0, JSCell::structureOffset()), regT2);
805    Jump failureCases1 = branchTest32(Zero, regT2, TrustedImm32(IsArray));
806    Jump failureCases2 = branchTest32(Zero, regT2, TrustedImm32(IndexingShapeMask));
807
808    // Checks out okay! - get the length from the storage
809    loadPtr(Address(regT0, JSObject::butterflyOffset()), regT3);
810    load32(Address(regT3, ArrayStorage::lengthOffset()), regT2);
811    Jump failureCases3 = branch32(LessThan, regT2, TrustedImm32(0));
812
813    emitFastArithIntToImmNoCheck(regT2, regT0);
814    Jump success = jump();
815
816    LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
817
818    // Use the patch information to link the failure cases back to the original slow case routine.
819    CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin);
820    patchBuffer.link(failureCases1, slowCaseBegin);
821    patchBuffer.link(failureCases2, slowCaseBegin);
822    patchBuffer.link(failureCases3, slowCaseBegin);
823
824    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
825    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult));
826
827    // Track the stub we have created so that it will be deleted later.
828    stubInfo->stubRoutine = FINALIZE_CODE_FOR_STUB(
829        patchBuffer,
830        ("Basline JIT get_by_id array length stub for %s, return point %p",
831            toCString(*m_codeBlock).data(),
832            stubInfo->hotPathBegin.labelAtOffset(
833                stubInfo->patch.baseline.u.get.putResult).executableAddress()));
834
835    // Finally patch the jump to slow case back in the hot path to jump here instead.
836    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck);
837    RepatchBuffer repatchBuffer(m_codeBlock);
838    repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubInfo->stubRoutine->code().code()));
839
840    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
841    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_array_fail));
842}
843
844void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
845{
846    // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
847    // referencing the prototype object - let's speculatively load it's table nice and early!)
848    JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame));
849
850    // Check eax is an object of the right Structure.
851    Jump failureCases1 = checkStructure(regT0, structure);
852
853    // Check the prototype object's Structure had not changed.
854    Jump failureCases2 = addStructureTransitionCheck(protoObject, prototypeStructure, stubInfo, regT3);
855
856    bool needsStubLink = false;
857
858    // Checks out okay!
859    if (slot.cachedPropertyType() == PropertySlot::Getter) {
860        needsStubLink = true;
861        compileGetDirectOffset(protoObject, regT1, cachedOffset);
862        JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
863        stubCall.addArgument(regT1);
864        stubCall.addArgument(regT0);
865        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
866        stubCall.call();
867    } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
868        needsStubLink = true;
869        JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
870        stubCall.addArgument(TrustedImmPtr(protoObject));
871        stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
872        stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
873        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
874        stubCall.call();
875    } else
876        compileGetDirectOffset(protoObject, regT0, cachedOffset);
877    Jump success = jump();
878    LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
879
880    // Use the patch information to link the failure cases back to the original slow case routine.
881    CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin);
882    patchBuffer.link(failureCases1, slowCaseBegin);
883    if (failureCases2.isSet())
884        patchBuffer.link(failureCases2, slowCaseBegin);
885
886    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
887    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult));
888
889    if (needsStubLink) {
890        for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
891            if (iter->to)
892                patchBuffer.link(iter->from, FunctionPtr(iter->to));
893        }
894    }
895    // Track the stub we have created so that it will be deleted later.
896    stubInfo->stubRoutine = createJITStubRoutine(
897        FINALIZE_CODE(
898            patchBuffer,
899            ("Baseline JIT get_by_id proto stub for %s, return point %p",
900                toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset(
901                    stubInfo->patch.baseline.u.get.putResult).executableAddress())),
902        *m_vm,
903        m_codeBlock->ownerExecutable(),
904        needsStubLink);
905
906    // Finally patch the jump to slow case back in the hot path to jump here instead.
907    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck);
908    RepatchBuffer repatchBuffer(m_codeBlock);
909    repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubInfo->stubRoutine->code().code()));
910
911    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
912    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_proto_list));
913}
914
915void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset)
916{
917    Jump failureCase = checkStructure(regT0, structure);
918    bool needsStubLink = false;
919    bool isDirect = false;
920    if (slot.cachedPropertyType() == PropertySlot::Getter) {
921        needsStubLink = true;
922        compileGetDirectOffset(regT0, regT1, cachedOffset);
923        JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
924        stubCall.addArgument(regT1);
925        stubCall.addArgument(regT0);
926        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
927        stubCall.call();
928    } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
929        needsStubLink = true;
930        JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
931        stubCall.addArgument(regT0);
932        stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
933        stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
934        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
935        stubCall.call();
936    } else {
937        isDirect = true;
938        compileGetDirectOffset(regT0, regT0, cachedOffset);
939    }
940    Jump success = jump();
941
942    LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
943
944    if (needsStubLink) {
945        for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
946            if (iter->to)
947                patchBuffer.link(iter->from, FunctionPtr(iter->to));
948        }
949    }
950
951    // Use the patch information to link the failure cases back to the original slow case routine.
952    CodeLocationLabel lastProtoBegin = CodeLocationLabel(JITStubRoutine::asCodePtr(polymorphicStructures->list[currentIndex - 1].stubRoutine));
953    if (!lastProtoBegin)
954        lastProtoBegin = stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin);
955
956    patchBuffer.link(failureCase, lastProtoBegin);
957
958    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
959    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult));
960
961    RefPtr<JITStubRoutine> stubCode = createJITStubRoutine(
962        FINALIZE_CODE(
963            patchBuffer,
964            ("Baseline JIT get_by_id list stub for %s, return point %p",
965                toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset(
966                    stubInfo->patch.baseline.u.get.putResult).executableAddress())),
967        *m_vm,
968        m_codeBlock->ownerExecutable(),
969        needsStubLink);
970
971    polymorphicStructures->list[currentIndex].set(*m_vm, m_codeBlock->ownerExecutable(), stubCode, structure, isDirect);
972
973    // Finally patch the jump to slow case back in the hot path to jump here instead.
974    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck);
975    RepatchBuffer repatchBuffer(m_codeBlock);
976    repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubCode->code().code()));
977}
978
979void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset, CallFrame* callFrame)
980{
981    // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
982    // referencing the prototype object - let's speculatively load it's table nice and early!)
983    JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame));
984
985    // Check eax is an object of the right Structure.
986    Jump failureCases1 = checkStructure(regT0, structure);
987
988    // Check the prototype object's Structure had not changed.
989    Jump failureCases2 = addStructureTransitionCheck(protoObject, prototypeStructure, stubInfo, regT3);
990
991    // Checks out okay!
992    bool needsStubLink = false;
993    bool isDirect = false;
994    if (slot.cachedPropertyType() == PropertySlot::Getter) {
995        needsStubLink = true;
996        compileGetDirectOffset(protoObject, regT1, cachedOffset);
997        JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
998        stubCall.addArgument(regT1);
999        stubCall.addArgument(regT0);
1000        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
1001        stubCall.call();
1002    } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
1003        needsStubLink = true;
1004        JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
1005        stubCall.addArgument(TrustedImmPtr(protoObject));
1006        stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
1007        stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
1008        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
1009        stubCall.call();
1010    } else {
1011        isDirect = true;
1012        compileGetDirectOffset(protoObject, regT0, cachedOffset);
1013    }
1014
1015    Jump success = jump();
1016
1017    LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
1018
1019    if (needsStubLink) {
1020        for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
1021            if (iter->to)
1022                patchBuffer.link(iter->from, FunctionPtr(iter->to));
1023        }
1024    }
1025
1026    // Use the patch information to link the failure cases back to the original slow case routine.
1027    CodeLocationLabel lastProtoBegin = CodeLocationLabel(JITStubRoutine::asCodePtr(prototypeStructures->list[currentIndex - 1].stubRoutine));
1028    patchBuffer.link(failureCases1, lastProtoBegin);
1029    if (failureCases2.isSet())
1030        patchBuffer.link(failureCases2, lastProtoBegin);
1031
1032    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
1033    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult));
1034
1035    RefPtr<JITStubRoutine> stubCode = createJITStubRoutine(
1036        FINALIZE_CODE(
1037            patchBuffer,
1038            ("Baseline JIT get_by_id proto list stub for %s, return point %p",
1039                toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset(
1040                    stubInfo->patch.baseline.u.get.putResult).executableAddress())),
1041        *m_vm,
1042        m_codeBlock->ownerExecutable(),
1043        needsStubLink);
1044    prototypeStructures->list[currentIndex].set(*m_vm, m_codeBlock->ownerExecutable(), stubCode, structure, prototypeStructure, isDirect);
1045
1046    // Finally patch the jump to slow case back in the hot path to jump here instead.
1047    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck);
1048    RepatchBuffer repatchBuffer(m_codeBlock);
1049    repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubCode->code().code()));
1050}
1051
1052void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, StructureChain* chain, size_t count, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset, CallFrame* callFrame)
1053{
1054    ASSERT(count);
1055    JumpList bucketsOfFail;
1056
1057    // Check eax is an object of the right Structure.
1058    Jump baseObjectCheck = checkStructure(regT0, structure);
1059    bucketsOfFail.append(baseObjectCheck);
1060
1061    Structure* currStructure = structure;
1062    WriteBarrier<Structure>* it = chain->head();
1063    JSObject* protoObject = 0;
1064    for (unsigned i = 0; i < count; ++i, ++it) {
1065        protoObject = asObject(currStructure->prototypeForLookup(callFrame));
1066        currStructure = it->get();
1067        testPrototype(protoObject, bucketsOfFail, stubInfo);
1068    }
1069    ASSERT(protoObject);
1070
1071    bool needsStubLink = false;
1072    bool isDirect = false;
1073    if (slot.cachedPropertyType() == PropertySlot::Getter) {
1074        needsStubLink = true;
1075        compileGetDirectOffset(protoObject, regT1, cachedOffset);
1076        JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
1077        stubCall.addArgument(regT1);
1078        stubCall.addArgument(regT0);
1079        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
1080        stubCall.call();
1081    } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
1082        needsStubLink = true;
1083        JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
1084        stubCall.addArgument(TrustedImmPtr(protoObject));
1085        stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
1086        stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
1087        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
1088        stubCall.call();
1089    } else {
1090        isDirect = true;
1091        compileGetDirectOffset(protoObject, regT0, cachedOffset);
1092    }
1093    Jump success = jump();
1094
1095    LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
1096
1097    if (needsStubLink) {
1098        for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
1099            if (iter->to)
1100                patchBuffer.link(iter->from, FunctionPtr(iter->to));
1101        }
1102    }
1103
1104    // Use the patch information to link the failure cases back to the original slow case routine.
1105    CodeLocationLabel lastProtoBegin = CodeLocationLabel(JITStubRoutine::asCodePtr(prototypeStructures->list[currentIndex - 1].stubRoutine));
1106
1107    patchBuffer.link(bucketsOfFail, lastProtoBegin);
1108
1109    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
1110    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult));
1111
1112    RefPtr<JITStubRoutine> stubRoutine = createJITStubRoutine(
1113        FINALIZE_CODE(
1114            patchBuffer,
1115            ("Baseline JIT get_by_id chain list stub for %s, return point %p",
1116                toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset(
1117                    stubInfo->patch.baseline.u.get.putResult).executableAddress())),
1118        *m_vm,
1119        m_codeBlock->ownerExecutable(),
1120        needsStubLink);
1121
1122    // Track the stub we have created so that it will be deleted later.
1123    prototypeStructures->list[currentIndex].set(callFrame->vm(), m_codeBlock->ownerExecutable(), stubRoutine, structure, chain, isDirect);
1124
1125    // Finally patch the jump to slow case back in the hot path to jump here instead.
1126    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck);
1127    RepatchBuffer repatchBuffer(m_codeBlock);
1128    repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code()));
1129}
1130
1131void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
1132{
1133    ASSERT(count);
1134
1135    JumpList bucketsOfFail;
1136
1137    // Check eax is an object of the right Structure.
1138    bucketsOfFail.append(checkStructure(regT0, structure));
1139
1140    Structure* currStructure = structure;
1141    WriteBarrier<Structure>* it = chain->head();
1142    JSObject* protoObject = 0;
1143    for (unsigned i = 0; i < count; ++i, ++it) {
1144        protoObject = asObject(currStructure->prototypeForLookup(callFrame));
1145        currStructure = it->get();
1146        testPrototype(protoObject, bucketsOfFail, stubInfo);
1147    }
1148    ASSERT(protoObject);
1149
1150    bool needsStubLink = false;
1151    if (slot.cachedPropertyType() == PropertySlot::Getter) {
1152        needsStubLink = true;
1153        compileGetDirectOffset(protoObject, regT1, cachedOffset);
1154        JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
1155        stubCall.addArgument(regT1);
1156        stubCall.addArgument(regT0);
1157        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
1158        stubCall.call();
1159    } else if (slot.cachedPropertyType() == PropertySlot::Custom) {
1160        needsStubLink = true;
1161        JITStubCall stubCall(this, cti_op_get_by_id_custom_stub);
1162        stubCall.addArgument(TrustedImmPtr(protoObject));
1163        stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress()));
1164        stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
1165        stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
1166        stubCall.call();
1167    } else
1168        compileGetDirectOffset(protoObject, regT0, cachedOffset);
1169    Jump success = jump();
1170
1171    LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
1172
1173    if (needsStubLink) {
1174        for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
1175            if (iter->to)
1176                patchBuffer.link(iter->from, FunctionPtr(iter->to));
1177        }
1178    }
1179
1180    // Use the patch information to link the failure cases back to the original slow case routine.
1181    patchBuffer.link(bucketsOfFail, stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin));
1182
1183    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
1184    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult));
1185
1186    // Track the stub we have created so that it will be deleted later.
1187    RefPtr<JITStubRoutine> stubRoutine = createJITStubRoutine(
1188        FINALIZE_CODE(
1189            patchBuffer,
1190            ("Baseline JIT get_by_id chain stub for %s, return point %p",
1191                toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset(
1192                    stubInfo->patch.baseline.u.get.putResult).executableAddress())),
1193        *m_vm,
1194        m_codeBlock->ownerExecutable(),
1195        needsStubLink);
1196    stubInfo->stubRoutine = stubRoutine;
1197
1198    // Finally patch the jump to slow case back in the hot path to jump here instead.
1199    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck);
1200    RepatchBuffer repatchBuffer(m_codeBlock);
1201    repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code()));
1202
1203    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
1204    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_proto_list));
1205}
1206
1207void JIT::emit_op_get_scoped_var(Instruction* currentInstruction)
1208{
1209    int skip = currentInstruction[3].u.operand;
1210
1211    emitGetFromCallFrameHeaderPtr(JSStack::ScopeChain, regT0);
1212    bool checkTopLevel = m_codeBlock->codeType() == FunctionCode && m_codeBlock->needsFullScopeChain();
1213    ASSERT(skip || !checkTopLevel);
1214    if (checkTopLevel && skip--) {
1215        Jump activationNotCreated;
1216        if (checkTopLevel)
1217            activationNotCreated = branchTestPtr(Zero, addressFor(m_codeBlock->activationRegister()));
1218        loadPtr(Address(regT0, JSScope::offsetOfNext()), regT0);
1219        activationNotCreated.link(this);
1220    }
1221    while (skip--)
1222        loadPtr(Address(regT0, JSScope::offsetOfNext()), regT0);
1223
1224    loadPtr(Address(regT0, JSVariableObject::offsetOfRegisters()), regT0);
1225    loadPtr(Address(regT0, currentInstruction[2].u.operand * sizeof(Register)), regT0);
1226    emitValueProfilingSite();
1227    emitPutVirtualRegister(currentInstruction[1].u.operand);
1228}
1229
1230void JIT::emit_op_put_scoped_var(Instruction* currentInstruction)
1231{
1232    int skip = currentInstruction[2].u.operand;
1233
1234    emitGetVirtualRegister(currentInstruction[3].u.operand, regT0);
1235
1236    emitGetFromCallFrameHeaderPtr(JSStack::ScopeChain, regT1);
1237    bool checkTopLevel = m_codeBlock->codeType() == FunctionCode && m_codeBlock->needsFullScopeChain();
1238    ASSERT(skip || !checkTopLevel);
1239    if (checkTopLevel && skip--) {
1240        Jump activationNotCreated;
1241        if (checkTopLevel)
1242            activationNotCreated = branchTestPtr(Zero, addressFor(m_codeBlock->activationRegister()));
1243        loadPtr(Address(regT1, JSScope::offsetOfNext()), regT1);
1244        activationNotCreated.link(this);
1245    }
1246    while (skip--)
1247        loadPtr(Address(regT1, JSScope::offsetOfNext()), regT1);
1248
1249    emitWriteBarrier(regT1, regT0, regT2, regT3, ShouldFilterImmediates, WriteBarrierForVariableAccess);
1250
1251    loadPtr(Address(regT1, JSVariableObject::offsetOfRegisters()), regT1);
1252    storePtr(regT0, Address(regT1, currentInstruction[1].u.operand * sizeof(Register)));
1253}
1254
1255void JIT::emit_op_init_global_const(Instruction* currentInstruction)
1256{
1257    JSGlobalObject* globalObject = m_codeBlock->globalObject();
1258
1259    emitGetVirtualRegister(currentInstruction[2].u.operand, regT0);
1260
1261    store64(regT0, currentInstruction[1].u.registerPointer);
1262    if (Heap::isWriteBarrierEnabled())
1263        emitWriteBarrier(globalObject, regT0, regT2, ShouldFilterImmediates, WriteBarrierForVariableAccess);
1264}
1265
1266void JIT::emit_op_init_global_const_check(Instruction* currentInstruction)
1267{
1268    emitGetVirtualRegister(currentInstruction[2].u.operand, regT0);
1269
1270    addSlowCase(branchTest8(NonZero, AbsoluteAddress(currentInstruction[3].u.predicatePointer)));
1271
1272    JSGlobalObject* globalObject = m_codeBlock->globalObject();
1273
1274    store64(regT0, currentInstruction[1].u.registerPointer);
1275    if (Heap::isWriteBarrierEnabled())
1276        emitWriteBarrier(globalObject, regT0, regT2, ShouldFilterImmediates, WriteBarrierForVariableAccess);
1277}
1278
1279void JIT::emitSlow_op_init_global_const_check(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
1280{
1281    linkSlowCase(iter);
1282
1283    JITStubCall stubCall(this, cti_op_init_global_const_check);
1284    stubCall.addArgument(regT0);
1285    stubCall.addArgument(TrustedImm32(currentInstruction[4].u.operand));
1286    stubCall.call();
1287}
1288
1289void JIT::resetPatchGetById(RepatchBuffer& repatchBuffer, StructureStubInfo* stubInfo)
1290{
1291    repatchBuffer.relink(stubInfo->callReturnLocation, cti_op_get_by_id);
1292    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.get.structureToCompare), reinterpret_cast<void*>(unusedPointer));
1293    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelCompactAtOffset(stubInfo->patch.baseline.u.get.displacementLabel), 0);
1294    repatchBuffer.relink(stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck), stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin));
1295}
1296
1297void JIT::resetPatchPutById(RepatchBuffer& repatchBuffer, StructureStubInfo* stubInfo)
1298{
1299    if (isDirectPutById(stubInfo))
1300        repatchBuffer.relink(stubInfo->callReturnLocation, cti_op_put_by_id_direct);
1301    else
1302        repatchBuffer.relink(stubInfo->callReturnLocation, cti_op_put_by_id);
1303    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.put.structureToCompare), reinterpret_cast<void*>(unusedPointer));
1304    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(stubInfo->patch.baseline.u.put.displacementLabel), 0);
1305}
1306
1307#endif // USE(JSVALUE64)
1308
1309void JIT::emitWriteBarrier(RegisterID owner, RegisterID value, RegisterID scratch, RegisterID scratch2, WriteBarrierMode mode, WriteBarrierUseKind useKind)
1310{
1311    UNUSED_PARAM(owner);
1312    UNUSED_PARAM(scratch);
1313    UNUSED_PARAM(scratch2);
1314    UNUSED_PARAM(useKind);
1315    UNUSED_PARAM(value);
1316    UNUSED_PARAM(mode);
1317    ASSERT(owner != scratch);
1318    ASSERT(owner != scratch2);
1319
1320#if ENABLE(WRITE_BARRIER_PROFILING)
1321    emitCount(WriteBarrierCounters::jitCounterFor(useKind));
1322#endif
1323}
1324
1325void JIT::emitWriteBarrier(JSCell* owner, RegisterID value, RegisterID scratch, WriteBarrierMode mode, WriteBarrierUseKind useKind)
1326{
1327    UNUSED_PARAM(owner);
1328    UNUSED_PARAM(scratch);
1329    UNUSED_PARAM(useKind);
1330    UNUSED_PARAM(value);
1331    UNUSED_PARAM(mode);
1332
1333#if ENABLE(WRITE_BARRIER_PROFILING)
1334    emitCount(WriteBarrierCounters::jitCounterFor(useKind));
1335#endif
1336}
1337
1338JIT::Jump JIT::addStructureTransitionCheck(JSCell* object, Structure* structure, StructureStubInfo* stubInfo, RegisterID scratch)
1339{
1340    if (object->structure() == structure && structure->transitionWatchpointSetIsStillValid()) {
1341        structure->addTransitionWatchpoint(stubInfo->addWatchpoint(m_codeBlock));
1342#if !ASSERT_DISABLED
1343        move(TrustedImmPtr(object), scratch);
1344        Jump ok = branchPtr(Equal, Address(scratch, JSCell::structureOffset()), TrustedImmPtr(structure));
1345        breakpoint();
1346        ok.link(this);
1347#endif
1348        Jump result; // Returning an unset jump this way because otherwise VC++ would complain.
1349        return result;
1350    }
1351
1352    move(TrustedImmPtr(object), scratch);
1353    return branchPtr(NotEqual, Address(scratch, JSCell::structureOffset()), TrustedImmPtr(structure));
1354}
1355
1356void JIT::addStructureTransitionCheck(JSCell* object, Structure* structure, StructureStubInfo* stubInfo, JumpList& failureCases, RegisterID scratch)
1357{
1358    Jump failureCase = addStructureTransitionCheck(object, structure, stubInfo, scratch);
1359    if (!failureCase.isSet())
1360        return;
1361
1362    failureCases.append(failureCase);
1363}
1364
1365void JIT::testPrototype(JSValue prototype, JumpList& failureCases, StructureStubInfo* stubInfo)
1366{
1367    if (prototype.isNull())
1368        return;
1369
1370    ASSERT(prototype.isCell());
1371    addStructureTransitionCheck(prototype.asCell(), prototype.asCell()->structure(), stubInfo, failureCases, regT3);
1372}
1373
1374bool JIT::isDirectPutById(StructureStubInfo* stubInfo)
1375{
1376    switch (stubInfo->accessType) {
1377    case access_put_by_id_transition_normal:
1378        return false;
1379    case access_put_by_id_transition_direct:
1380        return true;
1381    case access_put_by_id_replace:
1382    case access_put_by_id_generic: {
1383        void* oldCall = MacroAssembler::readCallTarget(stubInfo->callReturnLocation).executableAddress();
1384        if (oldCall == bitwise_cast<void*>(cti_op_put_by_id_direct)
1385            || oldCall == bitwise_cast<void*>(cti_op_put_by_id_direct_generic)
1386            || oldCall == bitwise_cast<void*>(cti_op_put_by_id_direct_fail))
1387            return true;
1388        ASSERT(oldCall == bitwise_cast<void*>(cti_op_put_by_id)
1389               || oldCall == bitwise_cast<void*>(cti_op_put_by_id_generic)
1390               || oldCall == bitwise_cast<void*>(cti_op_put_by_id_fail));
1391        return false;
1392    }
1393    default:
1394        RELEASE_ASSERT_NOT_REACHED();
1395        return false;
1396    }
1397}
1398
1399void JIT::privateCompileGetByVal(ByValInfo* byValInfo, ReturnAddressPtr returnAddress, JITArrayMode arrayMode)
1400{
1401    Instruction* currentInstruction = m_codeBlock->instructions().begin() + byValInfo->bytecodeIndex;
1402
1403    PatchableJump badType;
1404    JumpList slowCases;
1405
1406    switch (arrayMode) {
1407    case JITInt32:
1408        slowCases = emitInt32GetByVal(currentInstruction, badType);
1409        break;
1410    case JITDouble:
1411        slowCases = emitDoubleGetByVal(currentInstruction, badType);
1412        break;
1413    case JITContiguous:
1414        slowCases = emitContiguousGetByVal(currentInstruction, badType);
1415        break;
1416    case JITArrayStorage:
1417        slowCases = emitArrayStorageGetByVal(currentInstruction, badType);
1418        break;
1419    case JITInt8Array:
1420        slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->int8ArrayDescriptor(), 1, SignedTypedArray);
1421        break;
1422    case JITInt16Array:
1423        slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->int16ArrayDescriptor(), 2, SignedTypedArray);
1424        break;
1425    case JITInt32Array:
1426        slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->int32ArrayDescriptor(), 4, SignedTypedArray);
1427        break;
1428    case JITUint8Array:
1429        slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->uint8ArrayDescriptor(), 1, UnsignedTypedArray);
1430        break;
1431    case JITUint8ClampedArray:
1432        slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->uint8ClampedArrayDescriptor(), 1, UnsignedTypedArray);
1433        break;
1434    case JITUint16Array:
1435        slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->uint16ArrayDescriptor(), 2, UnsignedTypedArray);
1436        break;
1437    case JITUint32Array:
1438        slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->uint32ArrayDescriptor(), 4, UnsignedTypedArray);
1439        break;
1440    case JITFloat32Array:
1441        slowCases = emitFloatTypedArrayGetByVal(currentInstruction, badType, m_vm->float32ArrayDescriptor(), 4);
1442        break;
1443    case JITFloat64Array:
1444        slowCases = emitFloatTypedArrayGetByVal(currentInstruction, badType, m_vm->float64ArrayDescriptor(), 8);
1445        break;
1446    default:
1447        CRASH();
1448    }
1449
1450    Jump done = jump();
1451
1452    LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
1453
1454    patchBuffer.link(badType, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(returnAddress.value())).labelAtOffset(byValInfo->returnAddressToSlowPath));
1455    patchBuffer.link(slowCases, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(returnAddress.value())).labelAtOffset(byValInfo->returnAddressToSlowPath));
1456
1457    patchBuffer.link(done, byValInfo->badTypeJump.labelAtOffset(byValInfo->badTypeJumpToDone));
1458
1459    byValInfo->stubRoutine = FINALIZE_CODE_FOR_STUB(
1460        patchBuffer,
1461        ("Baseline get_by_val stub for %s, return point %p", toCString(*m_codeBlock).data(), returnAddress.value()));
1462
1463    RepatchBuffer repatchBuffer(m_codeBlock);
1464    repatchBuffer.relink(byValInfo->badTypeJump, CodeLocationLabel(byValInfo->stubRoutine->code().code()));
1465    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_val_generic));
1466}
1467
1468void JIT::privateCompilePutByVal(ByValInfo* byValInfo, ReturnAddressPtr returnAddress, JITArrayMode arrayMode)
1469{
1470    Instruction* currentInstruction = m_codeBlock->instructions().begin() + byValInfo->bytecodeIndex;
1471
1472    PatchableJump badType;
1473    JumpList slowCases;
1474
1475    switch (arrayMode) {
1476    case JITInt32:
1477        slowCases = emitInt32PutByVal(currentInstruction, badType);
1478        break;
1479    case JITDouble:
1480        slowCases = emitDoublePutByVal(currentInstruction, badType);
1481        break;
1482    case JITContiguous:
1483        slowCases = emitContiguousPutByVal(currentInstruction, badType);
1484        break;
1485    case JITArrayStorage:
1486        slowCases = emitArrayStoragePutByVal(currentInstruction, badType);
1487        break;
1488    case JITInt8Array:
1489        slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->int8ArrayDescriptor(), 1, SignedTypedArray, TruncateRounding);
1490        break;
1491    case JITInt16Array:
1492        slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->int16ArrayDescriptor(), 2, SignedTypedArray, TruncateRounding);
1493        break;
1494    case JITInt32Array:
1495        slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->int32ArrayDescriptor(), 4, SignedTypedArray, TruncateRounding);
1496        break;
1497    case JITUint8Array:
1498        slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->uint8ArrayDescriptor(), 1, UnsignedTypedArray, TruncateRounding);
1499        break;
1500    case JITUint8ClampedArray:
1501        slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->uint8ClampedArrayDescriptor(), 1, UnsignedTypedArray, ClampRounding);
1502        break;
1503    case JITUint16Array:
1504        slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->uint16ArrayDescriptor(), 2, UnsignedTypedArray, TruncateRounding);
1505        break;
1506    case JITUint32Array:
1507        slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->uint32ArrayDescriptor(), 4, UnsignedTypedArray, TruncateRounding);
1508        break;
1509    case JITFloat32Array:
1510        slowCases = emitFloatTypedArrayPutByVal(currentInstruction, badType, m_vm->float32ArrayDescriptor(), 4);
1511        break;
1512    case JITFloat64Array:
1513        slowCases = emitFloatTypedArrayPutByVal(currentInstruction, badType, m_vm->float64ArrayDescriptor(), 8);
1514        break;
1515    default:
1516        CRASH();
1517        break;
1518    }
1519
1520    Jump done = jump();
1521
1522    LinkBuffer patchBuffer(*m_vm, this, m_codeBlock);
1523
1524    patchBuffer.link(badType, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(returnAddress.value())).labelAtOffset(byValInfo->returnAddressToSlowPath));
1525    patchBuffer.link(slowCases, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(returnAddress.value())).labelAtOffset(byValInfo->returnAddressToSlowPath));
1526
1527    patchBuffer.link(done, byValInfo->badTypeJump.labelAtOffset(byValInfo->badTypeJumpToDone));
1528
1529    byValInfo->stubRoutine = FINALIZE_CODE_FOR_STUB(
1530        patchBuffer,
1531        ("Baseline put_by_val stub for %s, return point %p", toCString(*m_codeBlock).data(), returnAddress.value()));
1532
1533    RepatchBuffer repatchBuffer(m_codeBlock);
1534    repatchBuffer.relink(byValInfo->badTypeJump, CodeLocationLabel(byValInfo->stubRoutine->code().code()));
1535    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_put_by_val_generic));
1536}
1537
1538JIT::JumpList JIT::emitIntTypedArrayGetByVal(Instruction*, PatchableJump& badType, const TypedArrayDescriptor& descriptor, size_t elementSize, TypedArraySignedness signedness)
1539{
1540    // The best way to test the array type is to use the classInfo. We need to do so without
1541    // clobbering the register that holds the indexing type, base, and property.
1542
1543#if USE(JSVALUE64)
1544    RegisterID base = regT0;
1545    RegisterID property = regT1;
1546    RegisterID resultPayload = regT0;
1547    RegisterID scratch = regT3;
1548#else
1549    RegisterID base = regT0;
1550    RegisterID property = regT2;
1551    RegisterID resultPayload = regT0;
1552    RegisterID resultTag = regT1;
1553    RegisterID scratch = regT3;
1554#endif
1555
1556    JumpList slowCases;
1557
1558    loadPtr(Address(base, JSCell::structureOffset()), scratch);
1559    badType = patchableBranchPtr(NotEqual, Address(scratch, Structure::classInfoOffset()), TrustedImmPtr(descriptor.m_classInfo));
1560    slowCases.append(branch32(AboveOrEqual, property, Address(base, descriptor.m_lengthOffset)));
1561    loadPtr(Address(base, descriptor.m_storageOffset), base);
1562
1563    switch (elementSize) {
1564    case 1:
1565        if (signedness == SignedTypedArray)
1566            load8Signed(BaseIndex(base, property, TimesOne), resultPayload);
1567        else
1568            load8(BaseIndex(base, property, TimesOne), resultPayload);
1569        break;
1570    case 2:
1571        if (signedness == SignedTypedArray)
1572            load16Signed(BaseIndex(base, property, TimesTwo), resultPayload);
1573        else
1574            load16(BaseIndex(base, property, TimesTwo), resultPayload);
1575        break;
1576    case 4:
1577        load32(BaseIndex(base, property, TimesFour), resultPayload);
1578        break;
1579    default:
1580        CRASH();
1581    }
1582
1583    Jump done;
1584    if (elementSize == 4 && signedness == UnsignedTypedArray) {
1585        Jump canBeInt = branch32(GreaterThanOrEqual, resultPayload, TrustedImm32(0));
1586
1587        convertInt32ToDouble(resultPayload, fpRegT0);
1588        addDouble(AbsoluteAddress(&twoToThe32), fpRegT0);
1589#if USE(JSVALUE64)
1590        moveDoubleTo64(fpRegT0, resultPayload);
1591        sub64(tagTypeNumberRegister, resultPayload);
1592#else
1593        moveDoubleToInts(fpRegT0, resultPayload, resultTag);
1594#endif
1595
1596        done = jump();
1597        canBeInt.link(this);
1598    }
1599
1600#if USE(JSVALUE64)
1601    or64(tagTypeNumberRegister, resultPayload);
1602#else
1603    move(TrustedImm32(JSValue::Int32Tag), resultTag);
1604#endif
1605    if (done.isSet())
1606        done.link(this);
1607    return slowCases;
1608}
1609
1610JIT::JumpList JIT::emitFloatTypedArrayGetByVal(Instruction*, PatchableJump& badType, const TypedArrayDescriptor& descriptor, size_t elementSize)
1611{
1612#if USE(JSVALUE64)
1613    RegisterID base = regT0;
1614    RegisterID property = regT1;
1615    RegisterID resultPayload = regT0;
1616    RegisterID scratch = regT3;
1617#else
1618    RegisterID base = regT0;
1619    RegisterID property = regT2;
1620    RegisterID resultPayload = regT0;
1621    RegisterID resultTag = regT1;
1622    RegisterID scratch = regT3;
1623#endif
1624
1625    JumpList slowCases;
1626
1627    loadPtr(Address(base, JSCell::structureOffset()), scratch);
1628    badType = patchableBranchPtr(NotEqual, Address(scratch, Structure::classInfoOffset()), TrustedImmPtr(descriptor.m_classInfo));
1629    slowCases.append(branch32(AboveOrEqual, property, Address(base, descriptor.m_lengthOffset)));
1630    loadPtr(Address(base, descriptor.m_storageOffset), base);
1631
1632    switch (elementSize) {
1633    case 4:
1634        loadFloat(BaseIndex(base, property, TimesFour), fpRegT0);
1635        convertFloatToDouble(fpRegT0, fpRegT0);
1636        break;
1637    case 8: {
1638        loadDouble(BaseIndex(base, property, TimesEight), fpRegT0);
1639        break;
1640    }
1641    default:
1642        CRASH();
1643    }
1644
1645    Jump notNaN = branchDouble(DoubleEqual, fpRegT0, fpRegT0);
1646    static const double NaN = QNaN;
1647    loadDouble(&NaN, fpRegT0);
1648    notNaN.link(this);
1649
1650#if USE(JSVALUE64)
1651    moveDoubleTo64(fpRegT0, resultPayload);
1652    sub64(tagTypeNumberRegister, resultPayload);
1653#else
1654    moveDoubleToInts(fpRegT0, resultPayload, resultTag);
1655#endif
1656    return slowCases;
1657}
1658
1659JIT::JumpList JIT::emitIntTypedArrayPutByVal(Instruction* currentInstruction, PatchableJump& badType, const TypedArrayDescriptor& descriptor, size_t elementSize, TypedArraySignedness signedness, TypedArrayRounding rounding)
1660{
1661    unsigned value = currentInstruction[3].u.operand;
1662
1663#if USE(JSVALUE64)
1664    RegisterID base = regT0;
1665    RegisterID property = regT1;
1666    RegisterID earlyScratch = regT3;
1667    RegisterID lateScratch = regT2;
1668#else
1669    RegisterID base = regT0;
1670    RegisterID property = regT2;
1671    RegisterID earlyScratch = regT3;
1672    RegisterID lateScratch = regT1;
1673#endif
1674
1675    JumpList slowCases;
1676
1677    loadPtr(Address(base, JSCell::structureOffset()), earlyScratch);
1678    badType = patchableBranchPtr(NotEqual, Address(earlyScratch, Structure::classInfoOffset()), TrustedImmPtr(descriptor.m_classInfo));
1679    slowCases.append(branch32(AboveOrEqual, property, Address(base, descriptor.m_lengthOffset)));
1680
1681#if USE(JSVALUE64)
1682    emitGetVirtualRegister(value, earlyScratch);
1683    slowCases.append(emitJumpIfNotImmediateInteger(earlyScratch));
1684#else
1685    emitLoad(value, lateScratch, earlyScratch);
1686    slowCases.append(branch32(NotEqual, lateScratch, TrustedImm32(JSValue::Int32Tag)));
1687#endif
1688
1689    // We would be loading this into base as in get_by_val, except that the slow
1690    // path expects the base to be unclobbered.
1691    loadPtr(Address(base, descriptor.m_storageOffset), lateScratch);
1692
1693    if (rounding == ClampRounding) {
1694        ASSERT(elementSize == 1);
1695        ASSERT_UNUSED(signedness, signedness = UnsignedTypedArray);
1696        Jump inBounds = branch32(BelowOrEqual, earlyScratch, TrustedImm32(0xff));
1697        Jump tooBig = branch32(GreaterThan, earlyScratch, TrustedImm32(0xff));
1698        xor32(earlyScratch, earlyScratch);
1699        Jump clamped = jump();
1700        tooBig.link(this);
1701        move(TrustedImm32(0xff), earlyScratch);
1702        clamped.link(this);
1703        inBounds.link(this);
1704    }
1705
1706    switch (elementSize) {
1707    case 1:
1708        store8(earlyScratch, BaseIndex(lateScratch, property, TimesOne));
1709        break;
1710    case 2:
1711        store16(earlyScratch, BaseIndex(lateScratch, property, TimesTwo));
1712        break;
1713    case 4:
1714        store32(earlyScratch, BaseIndex(lateScratch, property, TimesFour));
1715        break;
1716    default:
1717        CRASH();
1718    }
1719
1720    return slowCases;
1721}
1722
1723JIT::JumpList JIT::emitFloatTypedArrayPutByVal(Instruction* currentInstruction, PatchableJump& badType, const TypedArrayDescriptor& descriptor, size_t elementSize)
1724{
1725    unsigned value = currentInstruction[3].u.operand;
1726
1727#if USE(JSVALUE64)
1728    RegisterID base = regT0;
1729    RegisterID property = regT1;
1730    RegisterID earlyScratch = regT3;
1731    RegisterID lateScratch = regT2;
1732#else
1733    RegisterID base = regT0;
1734    RegisterID property = regT2;
1735    RegisterID earlyScratch = regT3;
1736    RegisterID lateScratch = regT1;
1737#endif
1738
1739    JumpList slowCases;
1740
1741    loadPtr(Address(base, JSCell::structureOffset()), earlyScratch);
1742    badType = patchableBranchPtr(NotEqual, Address(earlyScratch, Structure::classInfoOffset()), TrustedImmPtr(descriptor.m_classInfo));
1743    slowCases.append(branch32(AboveOrEqual, property, Address(base, descriptor.m_lengthOffset)));
1744
1745#if USE(JSVALUE64)
1746    emitGetVirtualRegister(value, earlyScratch);
1747    Jump doubleCase = emitJumpIfNotImmediateInteger(earlyScratch);
1748    convertInt32ToDouble(earlyScratch, fpRegT0);
1749    Jump ready = jump();
1750    doubleCase.link(this);
1751    slowCases.append(emitJumpIfNotImmediateNumber(earlyScratch));
1752    add64(tagTypeNumberRegister, earlyScratch);
1753    move64ToDouble(earlyScratch, fpRegT0);
1754    ready.link(this);
1755#else
1756    emitLoad(value, lateScratch, earlyScratch);
1757    Jump doubleCase = branch32(NotEqual, lateScratch, TrustedImm32(JSValue::Int32Tag));
1758    convertInt32ToDouble(earlyScratch, fpRegT0);
1759    Jump ready = jump();
1760    doubleCase.link(this);
1761    slowCases.append(branch32(Above, lateScratch, TrustedImm32(JSValue::LowestTag)));
1762    moveIntsToDouble(earlyScratch, lateScratch, fpRegT0, fpRegT1);
1763    ready.link(this);
1764#endif
1765
1766    // We would be loading this into base as in get_by_val, except that the slow
1767    // path expects the base to be unclobbered.
1768    loadPtr(Address(base, descriptor.m_storageOffset), lateScratch);
1769
1770    switch (elementSize) {
1771    case 4:
1772        convertDoubleToFloat(fpRegT0, fpRegT0);
1773        storeFloat(fpRegT0, BaseIndex(lateScratch, property, TimesFour));
1774        break;
1775    case 8:
1776        storeDouble(fpRegT0, BaseIndex(lateScratch, property, TimesEight));
1777        break;
1778    default:
1779        CRASH();
1780    }
1781
1782    return slowCases;
1783}
1784
1785} // namespace JSC
1786
1787#endif // ENABLE(JIT)
1788