~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/3rdparty/javascriptcore/JavaScriptCore/jit/JITPropertyAccess.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alessandro Ghersi
  • Date: 2009-11-02 18:30:08 UTC
  • mfrom: (1.2.2 upstream)
  • mto: (15.2.5 experimental)
  • mto: This revision was merged to the branch mainline in revision 88.
  • Revision ID: james.westby@ubuntu.com-20091102183008-b6a4gcs128mvfb3m
Tags: upstream-4.6.0~beta1
ImportĀ upstreamĀ versionĀ 4.6.0~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#include "JIT.h"
 
28
 
 
29
#if ENABLE(JIT)
 
30
 
 
31
#include "CodeBlock.h"
 
32
#include "JITInlineMethods.h"
 
33
#include "JITStubCall.h"
 
34
#include "JSArray.h"
 
35
#include "JSFunction.h"
 
36
#include "Interpreter.h"
 
37
#include "LinkBuffer.h"
 
38
#include "RepatchBuffer.h"
 
39
#include "ResultType.h"
 
40
#include "SamplingTool.h"
 
41
 
 
42
#ifndef NDEBUG
 
43
#include <stdio.h>
 
44
#endif
 
45
 
 
46
using namespace std;
 
47
 
 
48
namespace JSC {
 
49
 
 
50
#if USE(JSVALUE32_64)
 
51
 
 
52
void 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
 
 
65
void 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(ImmPtr(&m_codeBlock->identifier(property)));
 
74
    stubCall.addArgument(function);
 
75
    stubCall.call();
 
76
}
 
77
 
 
78
void 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(ImmPtr(&m_codeBlock->identifier(property)));
 
87
    stubCall.addArgument(function);
 
88
    stubCall.call();
 
89
}
 
90
 
 
91
void 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(ImmPtr(&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.
 
109
void JIT::emit_op_method_check(Instruction*) {}
 
110
void 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
 
 
115
void 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
 
 
127
void JIT::emitSlow_op_get_by_val(Instruction*, Vector<SlowCaseEntry>::iterator&)
 
128
{
 
129
    ASSERT_NOT_REACHED();
 
130
}
 
131
 
 
132
void 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
 
 
145
void JIT::emitSlow_op_put_by_val(Instruction*, Vector<SlowCaseEntry>::iterator&)
 
146
{
 
147
    ASSERT_NOT_REACHED();
 
148
}
 
149
 
 
150
void 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(ImmPtr(&(m_codeBlock->identifier(ident))));
 
159
    stubCall.call(dst);
 
160
 
 
161
    m_propertyAccessInstructionIndex++;
 
162
}
 
163
 
 
164
void JIT::emitSlow_op_get_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&)
 
165
{
 
166
    m_propertyAccessInstructionIndex++;
 
167
    ASSERT_NOT_REACHED();
 
168
}
 
169
 
 
170
void 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(ImmPtr(&(m_codeBlock->identifier(ident))));
 
179
    stubCall.addArgument(value);
 
180
    stubCall.call();
 
181
 
 
182
    m_propertyAccessInstructionIndex++;
 
183
}
 
184
 
 
185
void 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
 
 
197
void 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
    Jump structureCheck = branchPtrWithPatch(NotEqual, Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), info.structureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure)));
 
215
    DataLabelPtr protoStructureToCompare, protoObj = moveWithPatch(ImmPtr(0), regT2);
 
216
    Jump protoStructureCheck = branchPtrWithPatch(NotEqual, Address(regT2, OBJECT_OFFSETOF(JSCell, m_structure)), protoStructureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure)));
 
217
 
 
218
    // This will be relinked to load the function without doing a load.
 
219
    DataLabelPtr putFunction = moveWithPatch(ImmPtr(0), regT0);
 
220
    move(Imm32(JSValue::CellTag), regT1);
 
221
    Jump match = jump();
 
222
 
 
223
    ASSERT(differenceBetween(info.structureToCompare, protoObj) == patchOffsetMethodCheckProtoObj);
 
224
    ASSERT(differenceBetween(info.structureToCompare, protoStructureToCompare) == patchOffsetMethodCheckProtoStruct);
 
225
    ASSERT(differenceBetween(info.structureToCompare, putFunction) == patchOffsetMethodCheckPutFunction);
 
226
 
 
227
    // Link the failure cases here.
 
228
    structureCheck.link(this);
 
229
    protoStructureCheck.link(this);
 
230
 
 
231
    // Do a regular(ish) get_by_id (the slow case will be link to
 
232
    // cti_op_get_by_id_method_check instead of cti_op_get_by_id.
 
233
    compileGetByIdHotPath();
 
234
 
 
235
    match.link(this);
 
236
    emitStore(dst, regT1, regT0);
 
237
    map(m_bytecodeIndex + OPCODE_LENGTH(op_method_check), dst, regT1, regT0);
 
238
 
 
239
    // We've already generated the following get_by_id, so make sure it's skipped over.
 
240
    m_bytecodeIndex += OPCODE_LENGTH(op_get_by_id);
 
241
}
 
242
 
 
243
void JIT::emitSlow_op_method_check(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
 
244
{
 
245
    currentInstruction += OPCODE_LENGTH(op_method_check);
 
246
 
 
247
    int dst = currentInstruction[1].u.operand;
 
248
    int base = currentInstruction[2].u.operand;
 
249
    int ident = currentInstruction[3].u.operand;
 
250
 
 
251
    compileGetByIdSlowCase(dst, base, &(m_codeBlock->identifier(ident)), iter, true);
 
252
 
 
253
    // We've already generated the following get_by_id, so make sure it's skipped over.
 
254
    m_bytecodeIndex += OPCODE_LENGTH(op_get_by_id);
 
255
}
 
256
 
 
257
#else //!ENABLE(JIT_OPTIMIZE_METHOD_CALLS)
 
258
 
 
259
// Treat these as nops - the call will be handed as a regular get_by_id/op_call pair.
 
260
void JIT::emit_op_method_check(Instruction*) {}
 
261
void JIT::emitSlow_op_method_check(Instruction*, Vector<SlowCaseEntry>::iterator&) { ASSERT_NOT_REACHED(); }
 
262
 
 
263
#endif
 
264
 
 
265
void JIT::emit_op_get_by_val(Instruction* currentInstruction)
 
266
{
 
267
    unsigned dst = currentInstruction[1].u.operand;
 
268
    unsigned base = currentInstruction[2].u.operand;
 
269
    unsigned property = currentInstruction[3].u.operand;
 
270
    
 
271
    emitLoad2(base, regT1, regT0, property, regT3, regT2);
 
272
 
 
273
    addSlowCase(branch32(NotEqual, regT3, Imm32(JSValue::Int32Tag)));
 
274
    emitJumpSlowCaseIfNotJSCell(base, regT1);
 
275
    addSlowCase(branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr)));
 
276
 
 
277
    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_storage)), regT3);
 
278
    addSlowCase(branch32(AboveOrEqual, regT2, Address(regT0, OBJECT_OFFSETOF(JSArray, m_vectorLength))));
 
279
 
 
280
    load32(BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + 4), regT1); // tag
 
281
    load32(BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])), regT0); // payload
 
282
    addSlowCase(branch32(Equal, regT1, Imm32(JSValue::EmptyValueTag)));
 
283
 
 
284
    emitStore(dst, regT1, regT0);
 
285
    map(m_bytecodeIndex + OPCODE_LENGTH(op_get_by_val), dst, regT1, regT0);
 
286
}
 
287
 
 
288
void JIT::emitSlow_op_get_by_val(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
 
289
{
 
290
    unsigned dst = currentInstruction[1].u.operand;
 
291
    unsigned base = currentInstruction[2].u.operand;
 
292
    unsigned property = currentInstruction[3].u.operand;
 
293
 
 
294
    linkSlowCase(iter); // property int32 check
 
295
    linkSlowCaseIfNotJSCell(iter, base); // base cell check
 
296
    linkSlowCase(iter); // base array check
 
297
    linkSlowCase(iter); // vector length check
 
298
    linkSlowCase(iter); // empty value
 
299
 
 
300
    JITStubCall stubCall(this, cti_op_get_by_val);
 
301
    stubCall.addArgument(base);
 
302
    stubCall.addArgument(property);
 
303
    stubCall.call(dst);
 
304
}
 
305
 
 
306
void JIT::emit_op_put_by_val(Instruction* currentInstruction)
 
307
{
 
308
    unsigned base = currentInstruction[1].u.operand;
 
309
    unsigned property = currentInstruction[2].u.operand;
 
310
    unsigned value = currentInstruction[3].u.operand;
 
311
 
 
312
    emitLoad2(base, regT1, regT0, property, regT3, regT2);
 
313
 
 
314
    addSlowCase(branch32(NotEqual, regT3, Imm32(JSValue::Int32Tag)));
 
315
    emitJumpSlowCaseIfNotJSCell(base, regT1);
 
316
    addSlowCase(branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr)));
 
317
    addSlowCase(branch32(AboveOrEqual, regT2, Address(regT0, OBJECT_OFFSETOF(JSArray, m_vectorLength))));
 
318
 
 
319
    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_storage)), regT3);
 
320
 
 
321
    Jump empty = branch32(Equal, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + 4), Imm32(JSValue::EmptyValueTag));
 
322
 
 
323
    Label storeResult(this);
 
324
    emitLoad(value, regT1, regT0);
 
325
    store32(regT0, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); // payload
 
326
    store32(regT1, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + 4)); // tag
 
327
    Jump end = jump();
 
328
 
 
329
    empty.link(this);
 
330
    add32(Imm32(1), Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector)));
 
331
    branch32(Below, regT2, Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_length))).linkTo(storeResult, this);
 
332
 
 
333
    add32(Imm32(1), regT2, regT0);
 
334
    store32(regT0, Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_length)));
 
335
    jump().linkTo(storeResult, this);
 
336
 
 
337
    end.link(this);
 
338
}
 
339
 
 
340
void JIT::emitSlow_op_put_by_val(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
 
341
{
 
342
    unsigned base = currentInstruction[1].u.operand;
 
343
    unsigned property = currentInstruction[2].u.operand;
 
344
    unsigned value = currentInstruction[3].u.operand;
 
345
 
 
346
    linkSlowCase(iter); // property int32 check
 
347
    linkSlowCaseIfNotJSCell(iter, base); // base cell check
 
348
    linkSlowCase(iter); // base not array check
 
349
    linkSlowCase(iter); // in vector check
 
350
 
 
351
    JITStubCall stubPutByValCall(this, cti_op_put_by_val);
 
352
    stubPutByValCall.addArgument(base);
 
353
    stubPutByValCall.addArgument(property);
 
354
    stubPutByValCall.addArgument(value);
 
355
    stubPutByValCall.call();
 
356
}
 
357
 
 
358
void JIT::emit_op_get_by_id(Instruction* currentInstruction)
 
359
{
 
360
    int dst = currentInstruction[1].u.operand;
 
361
    int base = currentInstruction[2].u.operand;
 
362
    
 
363
    emitLoad(base, regT1, regT0);
 
364
    emitJumpSlowCaseIfNotJSCell(base, regT1);
 
365
    compileGetByIdHotPath();
 
366
    emitStore(dst, regT1, regT0);
 
367
    map(m_bytecodeIndex + OPCODE_LENGTH(op_get_by_id), dst, regT1, regT0);
 
368
}
 
369
 
 
370
void JIT::compileGetByIdHotPath()
 
371
{
 
372
    // As for put_by_id, get_by_id requires the offset of the Structure and the offset of the access to be patched.
 
373
    // Additionally, for get_by_id we need patch the offset of the branch to the slow case (we patch this to jump
 
374
    // to array-length / prototype access tranpolines, and finally we also the the property-map access offset as a label
 
375
    // to jump back to if one of these trampolies finds a match.
 
376
    Label hotPathBegin(this);
 
377
    m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex].hotPathBegin = hotPathBegin;
 
378
    m_propertyAccessInstructionIndex++;
 
379
 
 
380
    DataLabelPtr structureToCompare;
 
381
    Jump structureCheck = branchPtrWithPatch(NotEqual, Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), structureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure)));
 
382
    addSlowCase(structureCheck);
 
383
    ASSERT(differenceBetween(hotPathBegin, structureToCompare) == patchOffsetGetByIdStructure);
 
384
    ASSERT(differenceBetween(hotPathBegin, structureCheck) == patchOffsetGetByIdBranchToSlowCase);
 
385
 
 
386
    Label externalLoad = loadPtrWithPatchToLEA(Address(regT0, OBJECT_OFFSETOF(JSObject, m_externalStorage)), regT2);
 
387
    Label externalLoadComplete(this);
 
388
    ASSERT(differenceBetween(hotPathBegin, externalLoad) == patchOffsetGetByIdExternalLoad);
 
389
    ASSERT(differenceBetween(externalLoad, externalLoadComplete) == patchLengthGetByIdExternalLoad);
 
390
 
 
391
    DataLabel32 displacementLabel1 = loadPtrWithAddressOffsetPatch(Address(regT2, patchGetByIdDefaultOffset), regT0); // payload
 
392
    ASSERT(differenceBetween(hotPathBegin, displacementLabel1) == patchOffsetGetByIdPropertyMapOffset1);
 
393
    DataLabel32 displacementLabel2 = loadPtrWithAddressOffsetPatch(Address(regT2, patchGetByIdDefaultOffset), regT1); // tag
 
394
    ASSERT(differenceBetween(hotPathBegin, displacementLabel2) == patchOffsetGetByIdPropertyMapOffset2);
 
395
 
 
396
    Label putResult(this);
 
397
    ASSERT(differenceBetween(hotPathBegin, putResult) == patchOffsetGetByIdPutResult);
 
398
}
 
399
 
 
400
void JIT::emitSlow_op_get_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
 
401
{
 
402
    int dst = currentInstruction[1].u.operand;
 
403
    int base = currentInstruction[2].u.operand;
 
404
    int ident = currentInstruction[3].u.operand;
 
405
 
 
406
    compileGetByIdSlowCase(dst, base, &(m_codeBlock->identifier(ident)), iter);
 
407
}
 
408
 
 
409
void JIT::compileGetByIdSlowCase(int dst, int base, Identifier* ident, Vector<SlowCaseEntry>::iterator& iter, bool isMethodCheck)
 
410
{
 
411
    // As for the hot path of get_by_id, above, we ensure that we can use an architecture specific offset
 
412
    // so that we only need track one pointer into the slow case code - we track a pointer to the location
 
413
    // of the call (which we can use to look up the patch information), but should a array-length or
 
414
    // prototype access trampoline fail we want to bail out back to here.  To do so we can subtract back
 
415
    // the distance from the call to the head of the slow case.
 
416
    linkSlowCaseIfNotJSCell(iter, base);
 
417
    linkSlowCase(iter);
 
418
 
 
419
    Label coldPathBegin(this);
 
420
 
 
421
    JITStubCall stubCall(this, isMethodCheck ? cti_op_get_by_id_method_check : cti_op_get_by_id);
 
422
    stubCall.addArgument(regT1, regT0);
 
423
    stubCall.addArgument(ImmPtr(ident));
 
424
    Call call = stubCall.call(dst);
 
425
 
 
426
    ASSERT(differenceBetween(coldPathBegin, call) == patchOffsetGetByIdSlowCaseCall);
 
427
 
 
428
    // Track the location of the call; this will be used to recover patch information.
 
429
    m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex].callReturnLocation = call;
 
430
    m_propertyAccessInstructionIndex++;
 
431
}
 
432
 
 
433
void JIT::emit_op_put_by_id(Instruction* currentInstruction)
 
434
{
 
435
    // In order to be able to patch both the Structure, and the object offset, we store one pointer,
 
436
    // to just after the arguments have been loaded into registers 'hotPathBegin', and we generate code
 
437
    // such that the Structure & offset are always at the same distance from this.
 
438
 
 
439
    int base = currentInstruction[1].u.operand;
 
440
    int value = currentInstruction[3].u.operand;
 
441
 
 
442
    emitLoad2(base, regT1, regT0, value, regT3, regT2);
 
443
 
 
444
    emitJumpSlowCaseIfNotJSCell(base, regT1);
 
445
 
 
446
    Label hotPathBegin(this);
 
447
    m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex].hotPathBegin = hotPathBegin;
 
448
    m_propertyAccessInstructionIndex++;
 
449
 
 
450
    // It is important that the following instruction plants a 32bit immediate, in order that it can be patched over.
 
451
    DataLabelPtr structureToCompare;
 
452
    addSlowCase(branchPtrWithPatch(NotEqual, Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), structureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure))));
 
453
    ASSERT(differenceBetween(hotPathBegin, structureToCompare) == patchOffsetPutByIdStructure);
 
454
 
 
455
    // Plant a load from a bogus ofset in the object's property map; we will patch this later, if it is to be used.
 
456
    Label externalLoad = loadPtrWithPatchToLEA(Address(regT0, OBJECT_OFFSETOF(JSObject, m_externalStorage)), regT0);
 
457
    Label externalLoadComplete(this);
 
458
    ASSERT(differenceBetween(hotPathBegin, externalLoad) == patchOffsetPutByIdExternalLoad);
 
459
    ASSERT(differenceBetween(externalLoad, externalLoadComplete) == patchLengthPutByIdExternalLoad);
 
460
 
 
461
    DataLabel32 displacementLabel1 = storePtrWithAddressOffsetPatch(regT2, Address(regT0, patchGetByIdDefaultOffset)); // payload
 
462
    DataLabel32 displacementLabel2 = storePtrWithAddressOffsetPatch(regT3, Address(regT0, patchGetByIdDefaultOffset)); // tag
 
463
    ASSERT(differenceBetween(hotPathBegin, displacementLabel1) == patchOffsetPutByIdPropertyMapOffset1);
 
464
    ASSERT(differenceBetween(hotPathBegin, displacementLabel2) == patchOffsetPutByIdPropertyMapOffset2);
 
465
}
 
466
 
 
467
void JIT::emitSlow_op_put_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
 
468
{
 
469
    int base = currentInstruction[1].u.operand;
 
470
    int ident = currentInstruction[2].u.operand;
 
471
 
 
472
    linkSlowCaseIfNotJSCell(iter, base);
 
473
    linkSlowCase(iter);
 
474
 
 
475
    JITStubCall stubCall(this, cti_op_put_by_id);
 
476
    stubCall.addArgument(regT1, regT0);
 
477
    stubCall.addArgument(ImmPtr(&(m_codeBlock->identifier(ident))));
 
478
    stubCall.addArgument(regT3, regT2); 
 
479
    Call call = stubCall.call();
 
480
 
 
481
    // Track the location of the call; this will be used to recover patch information.
 
482
    m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex].callReturnLocation = call;
 
483
    m_propertyAccessInstructionIndex++;
 
484
}
 
485
 
 
486
// Compile a store into an object's property storage.  May overwrite base.
 
487
void JIT::compilePutDirectOffset(RegisterID base, RegisterID valueTag, RegisterID valuePayload, Structure* structure, size_t cachedOffset)
 
488
{
 
489
    int offset = cachedOffset;
 
490
    if (structure->isUsingInlineStorage())
 
491
        offset += OBJECT_OFFSETOF(JSObject, m_inlineStorage) /  sizeof(Register);
 
492
    else
 
493
        loadPtr(Address(base, OBJECT_OFFSETOF(JSObject, m_externalStorage)), base);
 
494
    emitStore(offset, valueTag, valuePayload, base);
 
495
}
 
496
 
 
497
// Compile a load from an object's property storage.  May overwrite base.
 
498
void JIT::compileGetDirectOffset(RegisterID base, RegisterID resultTag, RegisterID resultPayload, Structure* structure, size_t cachedOffset)
 
499
{
 
500
    int offset = cachedOffset;
 
501
    if (structure->isUsingInlineStorage())
 
502
        offset += OBJECT_OFFSETOF(JSObject, m_inlineStorage) / sizeof(Register);
 
503
    else
 
504
        loadPtr(Address(base, OBJECT_OFFSETOF(JSObject, m_externalStorage)), base);
 
505
    emitLoad(offset, resultTag, resultPayload, base);
 
506
}
 
507
 
 
508
void JIT::compileGetDirectOffset(JSObject* base, RegisterID temp, RegisterID resultTag, RegisterID resultPayload, size_t cachedOffset)
 
509
{
 
510
    if (base->isUsingInlineStorage()) {
 
511
        load32(reinterpret_cast<char*>(&base->m_inlineStorage[cachedOffset]), resultPayload);
 
512
        load32(reinterpret_cast<char*>(&base->m_inlineStorage[cachedOffset]) + 4, resultTag);
 
513
        return;
 
514
    }
 
515
 
 
516
    size_t offset = cachedOffset * sizeof(JSValue);
 
517
 
 
518
    PropertyStorage* protoPropertyStorage = &base->m_externalStorage;
 
519
    loadPtr(static_cast<void*>(protoPropertyStorage), temp);
 
520
    load32(Address(temp, offset), resultPayload);
 
521
    load32(Address(temp, offset + 4), resultTag);
 
522
}
 
523
 
 
524
void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure* oldStructure, Structure* newStructure, size_t cachedOffset, StructureChain* chain, ReturnAddressPtr returnAddress)
 
525
{
 
526
    // It is assumed that regT0 contains the basePayload and regT1 contains the baseTag.  The value can be found on the stack.
 
527
 
 
528
    JumpList failureCases;
 
529
    failureCases.append(branch32(NotEqual, regT1, Imm32(JSValue::CellTag)));
 
530
 
 
531
    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), regT2);
 
532
    failureCases.append(branchPtr(NotEqual, regT2, ImmPtr(oldStructure)));
 
533
 
 
534
    // Verify that nothing in the prototype chain has a setter for this property. 
 
535
    for (RefPtr<Structure>* it = chain->head(); *it; ++it) {
 
536
        loadPtr(Address(regT2, OBJECT_OFFSETOF(Structure, m_prototype)), regT2);
 
537
        loadPtr(Address(regT2, OBJECT_OFFSETOF(JSCell, m_structure)), regT2);
 
538
        failureCases.append(branchPtr(NotEqual, regT2, ImmPtr(it->get())));
 
539
    }
 
540
 
 
541
    // Reallocate property storage if needed.
 
542
    Call callTarget;
 
543
    bool willNeedStorageRealloc = oldStructure->propertyStorageCapacity() != newStructure->propertyStorageCapacity();
 
544
    if (willNeedStorageRealloc) {
 
545
        // This trampoline was called to like a JIT stub; before we can can call again we need to
 
546
        // remove the return address from the stack, to prevent the stack from becoming misaligned.
 
547
        preserveReturnAddressAfterCall(regT3);
 
548
 
 
549
        JITStubCall stubCall(this, cti_op_put_by_id_transition_realloc);
 
550
        stubCall.skipArgument(); // base
 
551
        stubCall.skipArgument(); // ident
 
552
        stubCall.skipArgument(); // value
 
553
        stubCall.addArgument(Imm32(oldStructure->propertyStorageCapacity()));
 
554
        stubCall.addArgument(Imm32(newStructure->propertyStorageCapacity()));
 
555
        stubCall.call(regT0);
 
556
 
 
557
        restoreReturnAddressBeforeReturn(regT3);
 
558
    }
 
559
 
 
560
    sub32(Imm32(1), AbsoluteAddress(oldStructure->addressOfCount()));
 
561
    add32(Imm32(1), AbsoluteAddress(newStructure->addressOfCount()));
 
562
    storePtr(ImmPtr(newStructure), Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)));
 
563
 
 
564
    load32(Address(stackPointerRegister, offsetof(struct JITStackFrame, args[2]) + sizeof(void*)), regT3);
 
565
    load32(Address(stackPointerRegister, offsetof(struct JITStackFrame, args[2]) + sizeof(void*) + 4), regT2);
 
566
 
 
567
    // Write the value
 
568
    compilePutDirectOffset(regT0, regT2, regT3, newStructure, cachedOffset);
 
569
 
 
570
    ret();
 
571
    
 
572
    ASSERT(!failureCases.empty());
 
573
    failureCases.link(this);
 
574
    restoreArgumentReferenceForTrampoline();
 
575
    Call failureCall = tailRecursiveCall();
 
576
 
 
577
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
578
 
 
579
    patchBuffer.link(failureCall, FunctionPtr(cti_op_put_by_id_fail));
 
580
 
 
581
    if (willNeedStorageRealloc) {
 
582
        ASSERT(m_calls.size() == 1);
 
583
        patchBuffer.link(m_calls[0].from, FunctionPtr(cti_op_put_by_id_transition_realloc));
 
584
    }
 
585
    
 
586
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
587
    stubInfo->stubRoutine = entryLabel;
 
588
    RepatchBuffer repatchBuffer(m_codeBlock);
 
589
    repatchBuffer.relinkCallerToTrampoline(returnAddress, entryLabel);
 
590
}
 
591
 
 
592
void JIT::patchGetByIdSelf(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, ReturnAddressPtr returnAddress)
 
593
{
 
594
    RepatchBuffer repatchBuffer(codeBlock);
 
595
 
 
596
    // We don't want to patch more than once - in future go to cti_op_get_by_id_generic.
 
597
    // Should probably go to JITStubs::cti_op_get_by_id_fail, but that doesn't do anything interesting right now.
 
598
    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_self_fail));
 
599
 
 
600
    int offset = sizeof(JSValue) * cachedOffset;
 
601
 
 
602
    // If we're patching to use inline storage, convert the initial load to a lea; this avoids the extra load
 
603
    // and makes the subsequent load's offset automatically correct
 
604
    if (structure->isUsingInlineStorage())
 
605
        repatchBuffer.repatchLoadPtrToLEA(stubInfo->hotPathBegin.instructionAtOffset(patchOffsetGetByIdExternalLoad));
 
606
 
 
607
    // Patch the offset into the propoerty map to load from, then patch the Structure to look for.
 
608
    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(patchOffsetGetByIdStructure), structure);
 
609
    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetGetByIdPropertyMapOffset1), offset); // payload
 
610
    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetGetByIdPropertyMapOffset2), offset + 4); // tag
 
611
}
 
612
 
 
613
void JIT::patchMethodCallProto(CodeBlock* codeBlock, MethodCallLinkInfo& methodCallLinkInfo, JSFunction* callee, Structure* structure, JSObject* proto, ReturnAddressPtr returnAddress)
 
614
{
 
615
    RepatchBuffer repatchBuffer(codeBlock);
 
616
 
 
617
    ASSERT(!methodCallLinkInfo.cachedStructure);
 
618
    methodCallLinkInfo.cachedStructure = structure;
 
619
    structure->ref();
 
620
 
 
621
    Structure* prototypeStructure = proto->structure();
 
622
    ASSERT(!methodCallLinkInfo.cachedPrototypeStructure);
 
623
    methodCallLinkInfo.cachedPrototypeStructure = prototypeStructure;
 
624
    prototypeStructure->ref();
 
625
 
 
626
    repatchBuffer.repatch(methodCallLinkInfo.structureLabel, structure);
 
627
    repatchBuffer.repatch(methodCallLinkInfo.structureLabel.dataLabelPtrAtOffset(patchOffsetMethodCheckProtoObj), proto);
 
628
    repatchBuffer.repatch(methodCallLinkInfo.structureLabel.dataLabelPtrAtOffset(patchOffsetMethodCheckProtoStruct), prototypeStructure);
 
629
    repatchBuffer.repatch(methodCallLinkInfo.structureLabel.dataLabelPtrAtOffset(patchOffsetMethodCheckPutFunction), callee);
 
630
 
 
631
    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id));
 
632
}
 
633
 
 
634
void JIT::patchPutByIdReplace(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, ReturnAddressPtr returnAddress)
 
635
{
 
636
    RepatchBuffer repatchBuffer(codeBlock);
 
637
 
 
638
    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
 
639
    // Should probably go to cti_op_put_by_id_fail, but that doesn't do anything interesting right now.
 
640
    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_put_by_id_generic));
 
641
 
 
642
    int offset = sizeof(JSValue) * cachedOffset;
 
643
 
 
644
    // If we're patching to use inline storage, convert the initial load to a lea; this avoids the extra load
 
645
    // and makes the subsequent load's offset automatically correct
 
646
    if (structure->isUsingInlineStorage())
 
647
        repatchBuffer.repatchLoadPtrToLEA(stubInfo->hotPathBegin.instructionAtOffset(patchOffsetPutByIdExternalLoad));
 
648
 
 
649
    // Patch the offset into the propoerty map to load from, then patch the Structure to look for.
 
650
    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(patchOffsetPutByIdStructure), structure);
 
651
    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetPutByIdPropertyMapOffset1), offset); // payload
 
652
    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetPutByIdPropertyMapOffset2), offset + 4); // tag
 
653
}
 
654
 
 
655
void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress)
 
656
{
 
657
    StructureStubInfo* stubInfo = &m_codeBlock->getStubInfo(returnAddress);
 
658
    
 
659
    // regT0 holds a JSCell*
 
660
 
 
661
    // Check for array
 
662
    Jump failureCases1 = branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr));
 
663
 
 
664
    // Checks out okay! - get the length from the storage
 
665
    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_storage)), regT2);
 
666
    load32(Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length)), regT2);
 
667
 
 
668
    Jump failureCases2 = branch32(Above, regT2, Imm32(INT_MAX));
 
669
    move(regT2, regT0);
 
670
    move(Imm32(JSValue::Int32Tag), regT1);
 
671
    Jump success = jump();
 
672
 
 
673
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
674
 
 
675
    // Use the patch information to link the failure cases back to the original slow case routine.
 
676
    CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall);
 
677
    patchBuffer.link(failureCases1, slowCaseBegin);
 
678
    patchBuffer.link(failureCases2, slowCaseBegin);
 
679
 
 
680
    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
 
681
    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
 
682
 
 
683
    // Track the stub we have created so that it will be deleted later.
 
684
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
685
    stubInfo->stubRoutine = entryLabel;
 
686
 
 
687
    // Finally patch the jump to slow case back in the hot path to jump here instead.
 
688
    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
 
689
    RepatchBuffer repatchBuffer(m_codeBlock);
 
690
    repatchBuffer.relink(jumpLocation, entryLabel);
 
691
 
 
692
    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
 
693
    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_array_fail));
 
694
}
 
695
 
 
696
void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
 
697
{
 
698
    // regT0 holds a JSCell*
 
699
 
 
700
    // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
 
701
    // referencing the prototype object - let's speculatively load it's table nice and early!)
 
702
    JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame));
 
703
 
 
704
    Jump failureCases1 = checkStructure(regT0, structure);
 
705
 
 
706
    // Check the prototype object's Structure had not changed.
 
707
    Structure** prototypeStructureAddress = &(protoObject->m_structure);
 
708
#if PLATFORM(X86_64)
 
709
    move(ImmPtr(prototypeStructure), regT3);
 
710
    Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3);
 
711
#else
 
712
    Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure));
 
713
#endif
 
714
 
 
715
    // Checks out okay! - getDirectOffset
 
716
    compileGetDirectOffset(protoObject, regT2, regT1, regT0, cachedOffset);
 
717
 
 
718
    Jump success = jump();
 
719
 
 
720
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
721
 
 
722
    // Use the patch information to link the failure cases back to the original slow case routine.
 
723
    CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall);
 
724
    patchBuffer.link(failureCases1, slowCaseBegin);
 
725
    patchBuffer.link(failureCases2, slowCaseBegin);
 
726
 
 
727
    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
 
728
    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
 
729
 
 
730
    // Track the stub we have created so that it will be deleted later.
 
731
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
732
    stubInfo->stubRoutine = entryLabel;
 
733
 
 
734
    // Finally patch the jump to slow case back in the hot path to jump here instead.
 
735
    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
 
736
    RepatchBuffer repatchBuffer(m_codeBlock);
 
737
    repatchBuffer.relink(jumpLocation, entryLabel);
 
738
 
 
739
    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
 
740
    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_proto_list));
 
741
}
 
742
 
 
743
 
 
744
void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, size_t cachedOffset)
 
745
{
 
746
    // regT0 holds a JSCell*
 
747
    
 
748
    Jump failureCase = checkStructure(regT0, structure);
 
749
    compileGetDirectOffset(regT0, regT1, regT0, structure, cachedOffset);
 
750
    Jump success = jump();
 
751
 
 
752
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
753
 
 
754
    // Use the patch information to link the failure cases back to the original slow case routine.
 
755
    CodeLocationLabel lastProtoBegin = polymorphicStructures->list[currentIndex - 1].stubRoutine;
 
756
    if (!lastProtoBegin)
 
757
        lastProtoBegin = stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall);
 
758
 
 
759
    patchBuffer.link(failureCase, lastProtoBegin);
 
760
 
 
761
    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
 
762
    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
 
763
 
 
764
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
765
 
 
766
    structure->ref();
 
767
    polymorphicStructures->list[currentIndex].set(entryLabel, structure);
 
768
 
 
769
    // Finally patch the jump to slow case back in the hot path to jump here instead.
 
770
    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
 
771
    RepatchBuffer repatchBuffer(m_codeBlock);
 
772
    repatchBuffer.relink(jumpLocation, entryLabel);
 
773
}
 
774
 
 
775
void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, CallFrame* callFrame)
 
776
{
 
777
    // regT0 holds a JSCell*
 
778
    
 
779
    // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
 
780
    // referencing the prototype object - let's speculatively load it's table nice and early!)
 
781
    JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame));
 
782
 
 
783
    // Check eax is an object of the right Structure.
 
784
    Jump failureCases1 = checkStructure(regT0, structure);
 
785
 
 
786
    // Check the prototype object's Structure had not changed.
 
787
    Structure** prototypeStructureAddress = &(protoObject->m_structure);
 
788
#if PLATFORM(X86_64)
 
789
    move(ImmPtr(prototypeStructure), regT3);
 
790
    Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3);
 
791
#else
 
792
    Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure));
 
793
#endif
 
794
 
 
795
    compileGetDirectOffset(protoObject, regT2, regT1, regT0, cachedOffset);
 
796
 
 
797
    Jump success = jump();
 
798
 
 
799
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
800
 
 
801
    // Use the patch information to link the failure cases back to the original slow case routine.
 
802
    CodeLocationLabel lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine;
 
803
    patchBuffer.link(failureCases1, lastProtoBegin);
 
804
    patchBuffer.link(failureCases2, lastProtoBegin);
 
805
 
 
806
    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
 
807
    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
 
808
 
 
809
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
810
 
 
811
    structure->ref();
 
812
    prototypeStructure->ref();
 
813
    prototypeStructures->list[currentIndex].set(entryLabel, structure, prototypeStructure);
 
814
 
 
815
    // Finally patch the jump to slow case back in the hot path to jump here instead.
 
816
    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
 
817
    RepatchBuffer repatchBuffer(m_codeBlock);
 
818
    repatchBuffer.relink(jumpLocation, entryLabel);
 
819
}
 
820
 
 
821
void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, CallFrame* callFrame)
 
822
{
 
823
    // regT0 holds a JSCell*
 
824
    
 
825
    ASSERT(count);
 
826
    
 
827
    JumpList bucketsOfFail;
 
828
 
 
829
    // Check eax is an object of the right Structure.
 
830
    bucketsOfFail.append(checkStructure(regT0, structure));
 
831
 
 
832
    Structure* currStructure = structure;
 
833
    RefPtr<Structure>* chainEntries = chain->head();
 
834
    JSObject* protoObject = 0;
 
835
    for (unsigned i = 0; i < count; ++i) {
 
836
        protoObject = asObject(currStructure->prototypeForLookup(callFrame));
 
837
        currStructure = chainEntries[i].get();
 
838
 
 
839
        // Check the prototype object's Structure had not changed.
 
840
        Structure** prototypeStructureAddress = &(protoObject->m_structure);
 
841
#if PLATFORM(X86_64)
 
842
        move(ImmPtr(currStructure), regT3);
 
843
        bucketsOfFail.append(branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3));
 
844
#else
 
845
        bucketsOfFail.append(branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(currStructure)));
 
846
#endif
 
847
    }
 
848
    ASSERT(protoObject);
 
849
 
 
850
    compileGetDirectOffset(protoObject, regT2, regT1, regT0, cachedOffset);
 
851
    Jump success = jump();
 
852
 
 
853
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
854
 
 
855
    // Use the patch information to link the failure cases back to the original slow case routine.
 
856
    CodeLocationLabel lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine;
 
857
 
 
858
    patchBuffer.link(bucketsOfFail, lastProtoBegin);
 
859
 
 
860
    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
 
861
    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
 
862
 
 
863
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
864
 
 
865
    // Track the stub we have created so that it will be deleted later.
 
866
    structure->ref();
 
867
    chain->ref();
 
868
    prototypeStructures->list[currentIndex].set(entryLabel, structure, chain);
 
869
 
 
870
    // Finally patch the jump to slow case back in the hot path to jump here instead.
 
871
    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
 
872
    RepatchBuffer repatchBuffer(m_codeBlock);
 
873
    repatchBuffer.relink(jumpLocation, entryLabel);
 
874
}
 
875
 
 
876
void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
 
877
{
 
878
    // regT0 holds a JSCell*
 
879
    
 
880
    ASSERT(count);
 
881
    
 
882
    JumpList bucketsOfFail;
 
883
 
 
884
    // Check eax is an object of the right Structure.
 
885
    bucketsOfFail.append(checkStructure(regT0, structure));
 
886
 
 
887
    Structure* currStructure = structure;
 
888
    RefPtr<Structure>* chainEntries = chain->head();
 
889
    JSObject* protoObject = 0;
 
890
    for (unsigned i = 0; i < count; ++i) {
 
891
        protoObject = asObject(currStructure->prototypeForLookup(callFrame));
 
892
        currStructure = chainEntries[i].get();
 
893
 
 
894
        // Check the prototype object's Structure had not changed.
 
895
        Structure** prototypeStructureAddress = &(protoObject->m_structure);
 
896
#if PLATFORM(X86_64)
 
897
        move(ImmPtr(currStructure), regT3);
 
898
        bucketsOfFail.append(branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3));
 
899
#else
 
900
        bucketsOfFail.append(branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(currStructure)));
 
901
#endif
 
902
    }
 
903
    ASSERT(protoObject);
 
904
 
 
905
    compileGetDirectOffset(protoObject, regT2, regT1, regT0, cachedOffset);
 
906
    Jump success = jump();
 
907
 
 
908
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
909
 
 
910
    // Use the patch information to link the failure cases back to the original slow case routine.
 
911
    patchBuffer.link(bucketsOfFail, stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall));
 
912
 
 
913
    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
 
914
    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
 
915
 
 
916
    // Track the stub we have created so that it will be deleted later.
 
917
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
918
    stubInfo->stubRoutine = entryLabel;
 
919
 
 
920
    // Finally patch the jump to slow case back in the hot path to jump here instead.
 
921
    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
 
922
    RepatchBuffer repatchBuffer(m_codeBlock);
 
923
    repatchBuffer.relink(jumpLocation, entryLabel);
 
924
 
 
925
    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
 
926
    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_proto_list));
 
927
}
 
928
 
 
929
/* ------------------------------ END: !ENABLE / ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) ------------------------------ */
 
930
 
 
931
#endif // !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
 
932
 
 
933
#else // USE(JSVALUE32_64)
 
934
 
 
935
void JIT::emit_op_get_by_val(Instruction* currentInstruction)
 
936
{
 
937
    unsigned dst = currentInstruction[1].u.operand;
 
938
    unsigned base = currentInstruction[2].u.operand;
 
939
    unsigned property = currentInstruction[3].u.operand;
 
940
 
 
941
    emitGetVirtualRegisters(base, regT0, property, regT1);
 
942
    emitJumpSlowCaseIfNotImmediateInteger(regT1);
 
943
#if USE(JSVALUE64)
 
944
    // This is technically incorrect - we're zero-extending an int32.  On the hot path this doesn't matter.
 
945
    // We check the value as if it was a uint32 against the m_vectorLength - which will always fail if
 
946
    // number was signed since m_vectorLength is always less than intmax (since the total allocation
 
947
    // size is always less than 4Gb).  As such zero extending wil have been correct (and extending the value
 
948
    // to 64-bits is necessary since it's used in the address calculation.  We zero extend rather than sign
 
949
    // extending since it makes it easier to re-tag the value in the slow case.
 
950
    zeroExtend32ToPtr(regT1, regT1);
 
951
#else
 
952
    emitFastArithImmToInt(regT1);
 
953
#endif
 
954
    emitJumpSlowCaseIfNotJSCell(regT0, base);
 
955
    addSlowCase(branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr)));
 
956
 
 
957
    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_storage)), regT2);
 
958
    addSlowCase(branch32(AboveOrEqual, regT1, Address(regT0, OBJECT_OFFSETOF(JSArray, m_vectorLength))));
 
959
 
 
960
    loadPtr(BaseIndex(regT2, regT1, ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])), regT0);
 
961
    addSlowCase(branchTestPtr(Zero, regT0));
 
962
 
 
963
    emitPutVirtualRegister(dst);
 
964
}
 
965
 
 
966
void JIT::emit_op_put_by_val(Instruction* currentInstruction)
 
967
{
 
968
    unsigned base = currentInstruction[1].u.operand;
 
969
    unsigned property = currentInstruction[2].u.operand;
 
970
    unsigned value = currentInstruction[3].u.operand;
 
971
 
 
972
    emitGetVirtualRegisters(base, regT0, property, regT1);
 
973
    emitJumpSlowCaseIfNotImmediateInteger(regT1);
 
974
#if USE(JSVALUE64)
 
975
    // See comment in op_get_by_val.
 
976
    zeroExtend32ToPtr(regT1, regT1);
 
977
#else
 
978
    emitFastArithImmToInt(regT1);
 
979
#endif
 
980
    emitJumpSlowCaseIfNotJSCell(regT0, base);
 
981
    addSlowCase(branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr)));
 
982
    addSlowCase(branch32(AboveOrEqual, regT1, Address(regT0, OBJECT_OFFSETOF(JSArray, m_vectorLength))));
 
983
 
 
984
    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_storage)), regT2);
 
985
 
 
986
    Jump empty = branchTestPtr(Zero, BaseIndex(regT2, regT1, ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])));
 
987
 
 
988
    Label storeResult(this);
 
989
    emitGetVirtualRegister(value, regT0);
 
990
    storePtr(regT0, BaseIndex(regT2, regT1, ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])));
 
991
    Jump end = jump();
 
992
    
 
993
    empty.link(this);
 
994
    add32(Imm32(1), Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector)));
 
995
    branch32(Below, regT1, Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length))).linkTo(storeResult, this);
 
996
 
 
997
    move(regT1, regT0);
 
998
    add32(Imm32(1), regT0);
 
999
    store32(regT0, Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length)));
 
1000
    jump().linkTo(storeResult, this);
 
1001
 
 
1002
    end.link(this);
 
1003
}
 
1004
 
 
1005
void JIT::emit_op_put_by_index(Instruction* currentInstruction)
 
1006
{
 
1007
    JITStubCall stubCall(this, cti_op_put_by_index);
 
1008
    stubCall.addArgument(currentInstruction[1].u.operand, regT2);
 
1009
    stubCall.addArgument(Imm32(currentInstruction[2].u.operand));
 
1010
    stubCall.addArgument(currentInstruction[3].u.operand, regT2);
 
1011
    stubCall.call();
 
1012
}
 
1013
 
 
1014
void JIT::emit_op_put_getter(Instruction* currentInstruction)
 
1015
{
 
1016
    JITStubCall stubCall(this, cti_op_put_getter);
 
1017
    stubCall.addArgument(currentInstruction[1].u.operand, regT2);
 
1018
    stubCall.addArgument(ImmPtr(&m_codeBlock->identifier(currentInstruction[2].u.operand)));
 
1019
    stubCall.addArgument(currentInstruction[3].u.operand, regT2);
 
1020
    stubCall.call();
 
1021
}
 
1022
 
 
1023
void JIT::emit_op_put_setter(Instruction* currentInstruction)
 
1024
{
 
1025
    JITStubCall stubCall(this, cti_op_put_setter);
 
1026
    stubCall.addArgument(currentInstruction[1].u.operand, regT2);
 
1027
    stubCall.addArgument(ImmPtr(&m_codeBlock->identifier(currentInstruction[2].u.operand)));
 
1028
    stubCall.addArgument(currentInstruction[3].u.operand, regT2);
 
1029
    stubCall.call();
 
1030
}
 
1031
 
 
1032
void JIT::emit_op_del_by_id(Instruction* currentInstruction)
 
1033
{
 
1034
    JITStubCall stubCall(this, cti_op_del_by_id);
 
1035
    stubCall.addArgument(currentInstruction[2].u.operand, regT2);
 
1036
    stubCall.addArgument(ImmPtr(&m_codeBlock->identifier(currentInstruction[3].u.operand)));
 
1037
    stubCall.call(currentInstruction[1].u.operand);
 
1038
}
 
1039
 
 
1040
 
 
1041
#if !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
 
1042
 
 
1043
/* ------------------------------ BEGIN: !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) ------------------------------ */
 
1044
 
 
1045
// Treat these as nops - the call will be handed as a regular get_by_id/op_call pair.
 
1046
void JIT::emit_op_method_check(Instruction*) {}
 
1047
void JIT::emitSlow_op_method_check(Instruction*, Vector<SlowCaseEntry>::iterator&) { ASSERT_NOT_REACHED(); }
 
1048
#if ENABLE(JIT_OPTIMIZE_METHOD_CALLS)
 
1049
#error "JIT_OPTIMIZE_METHOD_CALLS requires JIT_OPTIMIZE_PROPERTY_ACCESS"
 
1050
#endif
 
1051
 
 
1052
void JIT::emit_op_get_by_id(Instruction* currentInstruction)
 
1053
{
 
1054
    unsigned resultVReg = currentInstruction[1].u.operand;
 
1055
    unsigned baseVReg = currentInstruction[2].u.operand;
 
1056
    Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
 
1057
 
 
1058
    emitGetVirtualRegister(baseVReg, regT0);
 
1059
    JITStubCall stubCall(this, cti_op_get_by_id_generic);
 
1060
    stubCall.addArgument(regT0);
 
1061
    stubCall.addArgument(ImmPtr(ident));
 
1062
    stubCall.call(resultVReg);
 
1063
 
 
1064
    m_propertyAccessInstructionIndex++;
 
1065
}
 
1066
 
 
1067
void JIT::emitSlow_op_get_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&)
 
1068
{
 
1069
    ASSERT_NOT_REACHED();
 
1070
}
 
1071
 
 
1072
void JIT::emit_op_put_by_id(Instruction* currentInstruction)
 
1073
{
 
1074
    unsigned baseVReg = currentInstruction[1].u.operand;
 
1075
    Identifier* ident = &(m_codeBlock->identifier(currentInstruction[2].u.operand));
 
1076
    unsigned valueVReg = currentInstruction[3].u.operand;
 
1077
 
 
1078
    emitGetVirtualRegisters(baseVReg, regT0, valueVReg, regT1);
 
1079
 
 
1080
    JITStubCall stubCall(this, cti_op_put_by_id_generic);
 
1081
    stubCall.addArgument(regT0);
 
1082
    stubCall.addArgument(ImmPtr(ident));
 
1083
    stubCall.addArgument(regT1);
 
1084
    stubCall.call();
 
1085
 
 
1086
    m_propertyAccessInstructionIndex++;
 
1087
}
 
1088
 
 
1089
void JIT::emitSlow_op_put_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&)
 
1090
{
 
1091
    ASSERT_NOT_REACHED();
 
1092
}
 
1093
 
 
1094
#else // !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
 
1095
 
 
1096
/* ------------------------------ BEGIN: ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) ------------------------------ */
 
1097
 
 
1098
#if ENABLE(JIT_OPTIMIZE_METHOD_CALLS)
 
1099
 
 
1100
void JIT::emit_op_method_check(Instruction* currentInstruction)
 
1101
{
 
1102
    // Assert that the following instruction is a get_by_id.
 
1103
    ASSERT(m_interpreter->getOpcodeID((currentInstruction + OPCODE_LENGTH(op_method_check))->u.opcode) == op_get_by_id);
 
1104
 
 
1105
    currentInstruction += OPCODE_LENGTH(op_method_check);
 
1106
    unsigned resultVReg = currentInstruction[1].u.operand;
 
1107
    unsigned baseVReg = currentInstruction[2].u.operand;
 
1108
    Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
 
1109
 
 
1110
    emitGetVirtualRegister(baseVReg, regT0);
 
1111
 
 
1112
    // Do the method check - check the object & its prototype's structure inline (this is the common case).
 
1113
    m_methodCallCompilationInfo.append(MethodCallCompilationInfo(m_propertyAccessInstructionIndex));
 
1114
    MethodCallCompilationInfo& info = m_methodCallCompilationInfo.last();
 
1115
 
 
1116
    Jump notCell = emitJumpIfNotJSCell(regT0);
 
1117
 
 
1118
    BEGIN_UNINTERRUPTED_SEQUENCE(sequenceMethodCheck);
 
1119
 
 
1120
    Jump structureCheck = branchPtrWithPatch(NotEqual, Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), info.structureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure)));
 
1121
    DataLabelPtr protoStructureToCompare, protoObj = moveWithPatch(ImmPtr(0), regT1);
 
1122
    Jump protoStructureCheck = branchPtrWithPatch(NotEqual, Address(regT1, OBJECT_OFFSETOF(JSCell, m_structure)), protoStructureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure)));
 
1123
 
 
1124
    // This will be relinked to load the function without doing a load.
 
1125
    DataLabelPtr putFunction = moveWithPatch(ImmPtr(0), regT0);
 
1126
 
 
1127
    END_UNINTERRUPTED_SEQUENCE(sequenceMethodCheck);
 
1128
 
 
1129
    Jump match = jump();
 
1130
 
 
1131
    ASSERT(differenceBetween(info.structureToCompare, protoObj) == patchOffsetMethodCheckProtoObj);
 
1132
    ASSERT(differenceBetween(info.structureToCompare, protoStructureToCompare) == patchOffsetMethodCheckProtoStruct);
 
1133
    ASSERT(differenceBetween(info.structureToCompare, putFunction) == patchOffsetMethodCheckPutFunction);
 
1134
 
 
1135
    // Link the failure cases here.
 
1136
    notCell.link(this);
 
1137
    structureCheck.link(this);
 
1138
    protoStructureCheck.link(this);
 
1139
 
 
1140
    // Do a regular(ish) get_by_id (the slow case will be link to
 
1141
    // cti_op_get_by_id_method_check instead of cti_op_get_by_id.
 
1142
    compileGetByIdHotPath(resultVReg, baseVReg, ident, m_propertyAccessInstructionIndex++);
 
1143
 
 
1144
    match.link(this);
 
1145
    emitPutVirtualRegister(resultVReg);
 
1146
 
 
1147
    // We've already generated the following get_by_id, so make sure it's skipped over.
 
1148
    m_bytecodeIndex += OPCODE_LENGTH(op_get_by_id);
 
1149
}
 
1150
 
 
1151
void JIT::emitSlow_op_method_check(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
 
1152
{
 
1153
    currentInstruction += OPCODE_LENGTH(op_method_check);
 
1154
    unsigned resultVReg = currentInstruction[1].u.operand;
 
1155
    unsigned baseVReg = currentInstruction[2].u.operand;
 
1156
    Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
 
1157
 
 
1158
    compileGetByIdSlowCase(resultVReg, baseVReg, ident, iter, true);
 
1159
 
 
1160
    // We've already generated the following get_by_id, so make sure it's skipped over.
 
1161
    m_bytecodeIndex += OPCODE_LENGTH(op_get_by_id);
 
1162
}
 
1163
 
 
1164
#else //!ENABLE(JIT_OPTIMIZE_METHOD_CALLS)
 
1165
 
 
1166
// Treat these as nops - the call will be handed as a regular get_by_id/op_call pair.
 
1167
void JIT::emit_op_method_check(Instruction*) {}
 
1168
void JIT::emitSlow_op_method_check(Instruction*, Vector<SlowCaseEntry>::iterator&) { ASSERT_NOT_REACHED(); }
 
1169
 
 
1170
#endif
 
1171
 
 
1172
void JIT::emit_op_get_by_id(Instruction* currentInstruction)
 
1173
{
 
1174
    unsigned resultVReg = currentInstruction[1].u.operand;
 
1175
    unsigned baseVReg = currentInstruction[2].u.operand;
 
1176
    Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
 
1177
 
 
1178
    emitGetVirtualRegister(baseVReg, regT0);
 
1179
    compileGetByIdHotPath(resultVReg, baseVReg, ident, m_propertyAccessInstructionIndex++);
 
1180
    emitPutVirtualRegister(resultVReg);
 
1181
}
 
1182
 
 
1183
void JIT::compileGetByIdHotPath(int, int baseVReg, Identifier*, unsigned propertyAccessInstructionIndex)
 
1184
{
 
1185
    // As for put_by_id, get_by_id requires the offset of the Structure and the offset of the access to be patched.
 
1186
    // Additionally, for get_by_id we need patch the offset of the branch to the slow case (we patch this to jump
 
1187
    // to array-length / prototype access tranpolines, and finally we also the the property-map access offset as a label
 
1188
    // to jump back to if one of these trampolies finds a match.
 
1189
 
 
1190
    emitJumpSlowCaseIfNotJSCell(regT0, baseVReg);
 
1191
 
 
1192
    BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdHotPath);
 
1193
 
 
1194
    Label hotPathBegin(this);
 
1195
    m_propertyAccessCompilationInfo[propertyAccessInstructionIndex].hotPathBegin = hotPathBegin;
 
1196
 
 
1197
    DataLabelPtr structureToCompare;
 
1198
    Jump structureCheck = branchPtrWithPatch(NotEqual, Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), structureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure)));
 
1199
    addSlowCase(structureCheck);
 
1200
    ASSERT(differenceBetween(hotPathBegin, structureToCompare) == patchOffsetGetByIdStructure);
 
1201
    ASSERT(differenceBetween(hotPathBegin, structureCheck) == patchOffsetGetByIdBranchToSlowCase);
 
1202
 
 
1203
    Label externalLoad = loadPtrWithPatchToLEA(Address(regT0, OBJECT_OFFSETOF(JSObject, m_externalStorage)), regT0);
 
1204
    Label externalLoadComplete(this);
 
1205
    ASSERT(differenceBetween(hotPathBegin, externalLoad) == patchOffsetGetByIdExternalLoad);
 
1206
    ASSERT(differenceBetween(externalLoad, externalLoadComplete) == patchLengthGetByIdExternalLoad);
 
1207
 
 
1208
    DataLabel32 displacementLabel = loadPtrWithAddressOffsetPatch(Address(regT0, patchGetByIdDefaultOffset), regT0);
 
1209
    ASSERT(differenceBetween(hotPathBegin, displacementLabel) == patchOffsetGetByIdPropertyMapOffset);
 
1210
 
 
1211
    Label putResult(this);
 
1212
 
 
1213
    END_UNINTERRUPTED_SEQUENCE(sequenceGetByIdHotPath);
 
1214
 
 
1215
    ASSERT(differenceBetween(hotPathBegin, putResult) == patchOffsetGetByIdPutResult);
 
1216
}
 
1217
 
 
1218
void JIT::emitSlow_op_get_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
 
1219
{
 
1220
    unsigned resultVReg = currentInstruction[1].u.operand;
 
1221
    unsigned baseVReg = currentInstruction[2].u.operand;
 
1222
    Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
 
1223
 
 
1224
    compileGetByIdSlowCase(resultVReg, baseVReg, ident, iter, false);
 
1225
}
 
1226
 
 
1227
void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident, Vector<SlowCaseEntry>::iterator& iter, bool isMethodCheck)
 
1228
{
 
1229
    // As for the hot path of get_by_id, above, we ensure that we can use an architecture specific offset
 
1230
    // so that we only need track one pointer into the slow case code - we track a pointer to the location
 
1231
    // of the call (which we can use to look up the patch information), but should a array-length or
 
1232
    // prototype access trampoline fail we want to bail out back to here.  To do so we can subtract back
 
1233
    // the distance from the call to the head of the slow case.
 
1234
 
 
1235
    linkSlowCaseIfNotJSCell(iter, baseVReg);
 
1236
    linkSlowCase(iter);
 
1237
 
 
1238
    BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdSlowCase);
 
1239
 
 
1240
#ifndef NDEBUG
 
1241
    Label coldPathBegin(this);
 
1242
#endif
 
1243
    JITStubCall stubCall(this, isMethodCheck ? cti_op_get_by_id_method_check : cti_op_get_by_id);
 
1244
    stubCall.addArgument(regT0);
 
1245
    stubCall.addArgument(ImmPtr(ident));
 
1246
    Call call = stubCall.call(resultVReg);
 
1247
 
 
1248
    END_UNINTERRUPTED_SEQUENCE(sequenceGetByIdSlowCase);
 
1249
 
 
1250
    ASSERT(differenceBetween(coldPathBegin, call) == patchOffsetGetByIdSlowCaseCall);
 
1251
 
 
1252
    // Track the location of the call; this will be used to recover patch information.
 
1253
    m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex].callReturnLocation = call;
 
1254
    m_propertyAccessInstructionIndex++;
 
1255
}
 
1256
 
 
1257
void JIT::emit_op_put_by_id(Instruction* currentInstruction)
 
1258
{
 
1259
    unsigned baseVReg = currentInstruction[1].u.operand;
 
1260
    unsigned valueVReg = currentInstruction[3].u.operand;
 
1261
 
 
1262
    unsigned propertyAccessInstructionIndex = m_propertyAccessInstructionIndex++;
 
1263
 
 
1264
    // In order to be able to patch both the Structure, and the object offset, we store one pointer,
 
1265
    // to just after the arguments have been loaded into registers 'hotPathBegin', and we generate code
 
1266
    // such that the Structure & offset are always at the same distance from this.
 
1267
 
 
1268
    emitGetVirtualRegisters(baseVReg, regT0, valueVReg, regT1);
 
1269
 
 
1270
    // Jump to a slow case if either the base object is an immediate, or if the Structure does not match.
 
1271
    emitJumpSlowCaseIfNotJSCell(regT0, baseVReg);
 
1272
 
 
1273
    BEGIN_UNINTERRUPTED_SEQUENCE(sequencePutById);
 
1274
 
 
1275
    Label hotPathBegin(this);
 
1276
    m_propertyAccessCompilationInfo[propertyAccessInstructionIndex].hotPathBegin = hotPathBegin;
 
1277
 
 
1278
    // It is important that the following instruction plants a 32bit immediate, in order that it can be patched over.
 
1279
    DataLabelPtr structureToCompare;
 
1280
    addSlowCase(branchPtrWithPatch(NotEqual, Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), structureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure))));
 
1281
    ASSERT(differenceBetween(hotPathBegin, structureToCompare) == patchOffsetPutByIdStructure);
 
1282
 
 
1283
    // Plant a load from a bogus ofset in the object's property map; we will patch this later, if it is to be used.
 
1284
    Label externalLoad = loadPtrWithPatchToLEA(Address(regT0, OBJECT_OFFSETOF(JSObject, m_externalStorage)), regT0);
 
1285
    Label externalLoadComplete(this);
 
1286
    ASSERT(differenceBetween(hotPathBegin, externalLoad) == patchOffsetPutByIdExternalLoad);
 
1287
    ASSERT(differenceBetween(externalLoad, externalLoadComplete) == patchLengthPutByIdExternalLoad);
 
1288
 
 
1289
    DataLabel32 displacementLabel = storePtrWithAddressOffsetPatch(regT1, Address(regT0, patchGetByIdDefaultOffset));
 
1290
 
 
1291
    END_UNINTERRUPTED_SEQUENCE(sequencePutById);
 
1292
 
 
1293
    ASSERT(differenceBetween(hotPathBegin, displacementLabel) == patchOffsetPutByIdPropertyMapOffset);
 
1294
}
 
1295
 
 
1296
void JIT::emitSlow_op_put_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
 
1297
{
 
1298
    unsigned baseVReg = currentInstruction[1].u.operand;
 
1299
    Identifier* ident = &(m_codeBlock->identifier(currentInstruction[2].u.operand));
 
1300
 
 
1301
    unsigned propertyAccessInstructionIndex = m_propertyAccessInstructionIndex++;
 
1302
 
 
1303
    linkSlowCaseIfNotJSCell(iter, baseVReg);
 
1304
    linkSlowCase(iter);
 
1305
 
 
1306
    JITStubCall stubCall(this, cti_op_put_by_id);
 
1307
    stubCall.addArgument(regT0);
 
1308
    stubCall.addArgument(ImmPtr(ident));
 
1309
    stubCall.addArgument(regT1);
 
1310
    Call call = stubCall.call();
 
1311
 
 
1312
    // Track the location of the call; this will be used to recover patch information.
 
1313
    m_propertyAccessCompilationInfo[propertyAccessInstructionIndex].callReturnLocation = call;
 
1314
}
 
1315
 
 
1316
// Compile a store into an object's property storage.  May overwrite the
 
1317
// value in objectReg.
 
1318
void JIT::compilePutDirectOffset(RegisterID base, RegisterID value, Structure* structure, size_t cachedOffset)
 
1319
{
 
1320
    int offset = cachedOffset * sizeof(JSValue);
 
1321
    if (structure->isUsingInlineStorage())
 
1322
        offset += OBJECT_OFFSETOF(JSObject, m_inlineStorage);
 
1323
    else
 
1324
        loadPtr(Address(base, OBJECT_OFFSETOF(JSObject, m_externalStorage)), base);
 
1325
    storePtr(value, Address(base, offset));
 
1326
}
 
1327
 
 
1328
// Compile a load from an object's property storage.  May overwrite base.
 
1329
void JIT::compileGetDirectOffset(RegisterID base, RegisterID result, Structure* structure, size_t cachedOffset)
 
1330
{
 
1331
    int offset = cachedOffset * sizeof(JSValue);
 
1332
    if (structure->isUsingInlineStorage())
 
1333
        offset += OBJECT_OFFSETOF(JSObject, m_inlineStorage);
 
1334
    else
 
1335
        loadPtr(Address(base, OBJECT_OFFSETOF(JSObject, m_externalStorage)), base);
 
1336
    loadPtr(Address(base, offset), result);
 
1337
}
 
1338
 
 
1339
void JIT::compileGetDirectOffset(JSObject* base, RegisterID temp, RegisterID result, size_t cachedOffset)
 
1340
{
 
1341
    if (base->isUsingInlineStorage())
 
1342
        loadPtr(static_cast<void*>(&base->m_inlineStorage[cachedOffset]), result);
 
1343
    else {
 
1344
        PropertyStorage* protoPropertyStorage = &base->m_externalStorage;
 
1345
        loadPtr(static_cast<void*>(protoPropertyStorage), temp);
 
1346
        loadPtr(Address(temp, cachedOffset * sizeof(JSValue)), result);
 
1347
    } 
 
1348
}
 
1349
 
 
1350
void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure* oldStructure, Structure* newStructure, size_t cachedOffset, StructureChain* chain, ReturnAddressPtr returnAddress)
 
1351
{
 
1352
    JumpList failureCases;
 
1353
    // Check eax is an object of the right Structure.
 
1354
    failureCases.append(emitJumpIfNotJSCell(regT0));
 
1355
    failureCases.append(branchPtr(NotEqual, Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), ImmPtr(oldStructure)));
 
1356
    JumpList successCases;
 
1357
 
 
1358
    // ecx = baseObject
 
1359
    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), regT2);
 
1360
    // proto(ecx) = baseObject->structure()->prototype()
 
1361
    failureCases.append(branch32(NotEqual, Address(regT2, OBJECT_OFFSETOF(Structure, m_typeInfo) + OBJECT_OFFSETOF(TypeInfo, m_type)), Imm32(ObjectType)));
 
1362
 
 
1363
    loadPtr(Address(regT2, OBJECT_OFFSETOF(Structure, m_prototype)), regT2);
 
1364
    
 
1365
    // ecx = baseObject->m_structure
 
1366
    for (RefPtr<Structure>* it = chain->head(); *it; ++it) {
 
1367
        // null check the prototype
 
1368
        successCases.append(branchPtr(Equal, regT2, ImmPtr(JSValue::encode(jsNull()))));
 
1369
 
 
1370
        // Check the structure id
 
1371
        failureCases.append(branchPtr(NotEqual, Address(regT2, OBJECT_OFFSETOF(JSCell, m_structure)), ImmPtr(it->get())));
 
1372
        
 
1373
        loadPtr(Address(regT2, OBJECT_OFFSETOF(JSCell, m_structure)), regT2);
 
1374
        failureCases.append(branch32(NotEqual, Address(regT2, OBJECT_OFFSETOF(Structure, m_typeInfo) + OBJECT_OFFSETOF(TypeInfo, m_type)), Imm32(ObjectType)));
 
1375
        loadPtr(Address(regT2, OBJECT_OFFSETOF(Structure, m_prototype)), regT2);
 
1376
    }
 
1377
 
 
1378
    successCases.link(this);
 
1379
 
 
1380
    Call callTarget;
 
1381
 
 
1382
    // emit a call only if storage realloc is needed
 
1383
    bool willNeedStorageRealloc = oldStructure->propertyStorageCapacity() != newStructure->propertyStorageCapacity();
 
1384
    if (willNeedStorageRealloc) {
 
1385
        // This trampoline was called to like a JIT stub; before we can can call again we need to
 
1386
        // remove the return address from the stack, to prevent the stack from becoming misaligned.
 
1387
        preserveReturnAddressAfterCall(regT3);
 
1388
 
 
1389
        JITStubCall stubCall(this, cti_op_put_by_id_transition_realloc);
 
1390
        stubCall.skipArgument(); // base
 
1391
        stubCall.skipArgument(); // ident
 
1392
        stubCall.skipArgument(); // value
 
1393
        stubCall.addArgument(Imm32(oldStructure->propertyStorageCapacity()));
 
1394
        stubCall.addArgument(Imm32(newStructure->propertyStorageCapacity()));
 
1395
        stubCall.call(regT0);
 
1396
        emitGetJITStubArg(2, regT1);
 
1397
 
 
1398
        restoreReturnAddressBeforeReturn(regT3);
 
1399
    }
 
1400
 
 
1401
    // Assumes m_refCount can be decremented easily, refcount decrement is safe as 
 
1402
    // codeblock should ensure oldStructure->m_refCount > 0
 
1403
    sub32(Imm32(1), AbsoluteAddress(oldStructure->addressOfCount()));
 
1404
    add32(Imm32(1), AbsoluteAddress(newStructure->addressOfCount()));
 
1405
    storePtr(ImmPtr(newStructure), Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)));
 
1406
 
 
1407
    // write the value
 
1408
    compilePutDirectOffset(regT0, regT1, newStructure, cachedOffset);
 
1409
 
 
1410
    ret();
 
1411
    
 
1412
    ASSERT(!failureCases.empty());
 
1413
    failureCases.link(this);
 
1414
    restoreArgumentReferenceForTrampoline();
 
1415
    Call failureCall = tailRecursiveCall();
 
1416
 
 
1417
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
1418
 
 
1419
    patchBuffer.link(failureCall, FunctionPtr(cti_op_put_by_id_fail));
 
1420
 
 
1421
    if (willNeedStorageRealloc) {
 
1422
        ASSERT(m_calls.size() == 1);
 
1423
        patchBuffer.link(m_calls[0].from, FunctionPtr(cti_op_put_by_id_transition_realloc));
 
1424
    }
 
1425
    
 
1426
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
1427
    stubInfo->stubRoutine = entryLabel;
 
1428
    RepatchBuffer repatchBuffer(m_codeBlock);
 
1429
    repatchBuffer.relinkCallerToTrampoline(returnAddress, entryLabel);
 
1430
}
 
1431
 
 
1432
void JIT::patchGetByIdSelf(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, ReturnAddressPtr returnAddress)
 
1433
{
 
1434
    RepatchBuffer repatchBuffer(codeBlock);
 
1435
 
 
1436
    // We don't want to patch more than once - in future go to cti_op_get_by_id_generic.
 
1437
    // Should probably go to cti_op_get_by_id_fail, but that doesn't do anything interesting right now.
 
1438
    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_self_fail));
 
1439
 
 
1440
    int offset = sizeof(JSValue) * cachedOffset;
 
1441
 
 
1442
    // If we're patching to use inline storage, convert the initial load to a lea; this avoids the extra load
 
1443
    // and makes the subsequent load's offset automatically correct
 
1444
    if (structure->isUsingInlineStorage())
 
1445
        repatchBuffer.repatchLoadPtrToLEA(stubInfo->hotPathBegin.instructionAtOffset(patchOffsetGetByIdExternalLoad));
 
1446
 
 
1447
    // Patch the offset into the propoerty map to load from, then patch the Structure to look for.
 
1448
    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(patchOffsetGetByIdStructure), structure);
 
1449
    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetGetByIdPropertyMapOffset), offset);
 
1450
}
 
1451
 
 
1452
void JIT::patchMethodCallProto(CodeBlock* codeBlock, MethodCallLinkInfo& methodCallLinkInfo, JSFunction* callee, Structure* structure, JSObject* proto, ReturnAddressPtr returnAddress)
 
1453
{
 
1454
    RepatchBuffer repatchBuffer(codeBlock);
 
1455
 
 
1456
    ASSERT(!methodCallLinkInfo.cachedStructure);
 
1457
    methodCallLinkInfo.cachedStructure = structure;
 
1458
    structure->ref();
 
1459
 
 
1460
    Structure* prototypeStructure = proto->structure();
 
1461
    ASSERT(!methodCallLinkInfo.cachedPrototypeStructure);
 
1462
    methodCallLinkInfo.cachedPrototypeStructure = prototypeStructure;
 
1463
    prototypeStructure->ref();
 
1464
 
 
1465
    repatchBuffer.repatch(methodCallLinkInfo.structureLabel, structure);
 
1466
    repatchBuffer.repatch(methodCallLinkInfo.structureLabel.dataLabelPtrAtOffset(patchOffsetMethodCheckProtoObj), proto);
 
1467
    repatchBuffer.repatch(methodCallLinkInfo.structureLabel.dataLabelPtrAtOffset(patchOffsetMethodCheckProtoStruct), prototypeStructure);
 
1468
    repatchBuffer.repatch(methodCallLinkInfo.structureLabel.dataLabelPtrAtOffset(patchOffsetMethodCheckPutFunction), callee);
 
1469
 
 
1470
    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id));
 
1471
}
 
1472
 
 
1473
void JIT::patchPutByIdReplace(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, ReturnAddressPtr returnAddress)
 
1474
{
 
1475
    RepatchBuffer repatchBuffer(codeBlock);
 
1476
 
 
1477
    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
 
1478
    // Should probably go to cti_op_put_by_id_fail, but that doesn't do anything interesting right now.
 
1479
    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_put_by_id_generic));
 
1480
 
 
1481
    int offset = sizeof(JSValue) * cachedOffset;
 
1482
 
 
1483
    // If we're patching to use inline storage, convert the initial load to a lea; this avoids the extra load
 
1484
    // and makes the subsequent load's offset automatically correct
 
1485
    if (structure->isUsingInlineStorage())
 
1486
        repatchBuffer.repatchLoadPtrToLEA(stubInfo->hotPathBegin.instructionAtOffset(patchOffsetPutByIdExternalLoad));
 
1487
 
 
1488
    // Patch the offset into the propoerty map to load from, then patch the Structure to look for.
 
1489
    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(patchOffsetPutByIdStructure), structure);
 
1490
    repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetPutByIdPropertyMapOffset), offset);
 
1491
}
 
1492
 
 
1493
void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress)
 
1494
{
 
1495
    StructureStubInfo* stubInfo = &m_codeBlock->getStubInfo(returnAddress);
 
1496
 
 
1497
    // Check eax is an array
 
1498
    Jump failureCases1 = branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr));
 
1499
 
 
1500
    // Checks out okay! - get the length from the storage
 
1501
    loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_storage)), regT2);
 
1502
    load32(Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length)), regT2);
 
1503
 
 
1504
    Jump failureCases2 = branch32(Above, regT2, Imm32(JSImmediate::maxImmediateInt));
 
1505
 
 
1506
    emitFastArithIntToImmNoCheck(regT2, regT0);
 
1507
    Jump success = jump();
 
1508
 
 
1509
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
1510
 
 
1511
    // Use the patch information to link the failure cases back to the original slow case routine.
 
1512
    CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall);
 
1513
    patchBuffer.link(failureCases1, slowCaseBegin);
 
1514
    patchBuffer.link(failureCases2, slowCaseBegin);
 
1515
 
 
1516
    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
 
1517
    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
 
1518
 
 
1519
    // Track the stub we have created so that it will be deleted later.
 
1520
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
1521
    stubInfo->stubRoutine = entryLabel;
 
1522
 
 
1523
    // Finally patch the jump to slow case back in the hot path to jump here instead.
 
1524
    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
 
1525
    RepatchBuffer repatchBuffer(m_codeBlock);
 
1526
    repatchBuffer.relink(jumpLocation, entryLabel);
 
1527
 
 
1528
    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
 
1529
    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_array_fail));
 
1530
}
 
1531
 
 
1532
void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
 
1533
{
 
1534
    // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
 
1535
    // referencing the prototype object - let's speculatively load it's table nice and early!)
 
1536
    JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame));
 
1537
 
 
1538
    // Check eax is an object of the right Structure.
 
1539
    Jump failureCases1 = checkStructure(regT0, structure);
 
1540
 
 
1541
    // Check the prototype object's Structure had not changed.
 
1542
    Structure** prototypeStructureAddress = &(protoObject->m_structure);
 
1543
#if PLATFORM(X86_64)
 
1544
    move(ImmPtr(prototypeStructure), regT3);
 
1545
    Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3);
 
1546
#else
 
1547
    Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure));
 
1548
#endif
 
1549
 
 
1550
    // Checks out okay! - getDirectOffset
 
1551
    compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
 
1552
 
 
1553
    Jump success = jump();
 
1554
 
 
1555
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
1556
 
 
1557
    // Use the patch information to link the failure cases back to the original slow case routine.
 
1558
    CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall);
 
1559
    patchBuffer.link(failureCases1, slowCaseBegin);
 
1560
    patchBuffer.link(failureCases2, slowCaseBegin);
 
1561
 
 
1562
    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
 
1563
    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
 
1564
 
 
1565
    // Track the stub we have created so that it will be deleted later.
 
1566
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
1567
    stubInfo->stubRoutine = entryLabel;
 
1568
 
 
1569
    // Finally patch the jump to slow case back in the hot path to jump here instead.
 
1570
    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
 
1571
    RepatchBuffer repatchBuffer(m_codeBlock);
 
1572
    repatchBuffer.relink(jumpLocation, entryLabel);
 
1573
 
 
1574
    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
 
1575
    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_proto_list));
 
1576
}
 
1577
 
 
1578
void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, size_t cachedOffset)
 
1579
{
 
1580
    Jump failureCase = checkStructure(regT0, structure);
 
1581
    compileGetDirectOffset(regT0, regT0, structure, cachedOffset);
 
1582
    Jump success = jump();
 
1583
 
 
1584
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
1585
 
 
1586
    // Use the patch information to link the failure cases back to the original slow case routine.
 
1587
    CodeLocationLabel lastProtoBegin = polymorphicStructures->list[currentIndex - 1].stubRoutine;
 
1588
    if (!lastProtoBegin)
 
1589
        lastProtoBegin = stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall);
 
1590
 
 
1591
    patchBuffer.link(failureCase, lastProtoBegin);
 
1592
 
 
1593
    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
 
1594
    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
 
1595
 
 
1596
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
1597
 
 
1598
    structure->ref();
 
1599
    polymorphicStructures->list[currentIndex].set(entryLabel, structure);
 
1600
 
 
1601
    // Finally patch the jump to slow case back in the hot path to jump here instead.
 
1602
    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
 
1603
    RepatchBuffer repatchBuffer(m_codeBlock);
 
1604
    repatchBuffer.relink(jumpLocation, entryLabel);
 
1605
}
 
1606
 
 
1607
void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, CallFrame* callFrame)
 
1608
{
 
1609
    // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
 
1610
    // referencing the prototype object - let's speculatively load it's table nice and early!)
 
1611
    JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame));
 
1612
 
 
1613
    // Check eax is an object of the right Structure.
 
1614
    Jump failureCases1 = checkStructure(regT0, structure);
 
1615
 
 
1616
    // Check the prototype object's Structure had not changed.
 
1617
    Structure** prototypeStructureAddress = &(protoObject->m_structure);
 
1618
#if PLATFORM(X86_64)
 
1619
    move(ImmPtr(prototypeStructure), regT3);
 
1620
    Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3);
 
1621
#else
 
1622
    Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure));
 
1623
#endif
 
1624
 
 
1625
    // Checks out okay! - getDirectOffset
 
1626
    compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
 
1627
 
 
1628
    Jump success = jump();
 
1629
 
 
1630
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
1631
 
 
1632
    // Use the patch information to link the failure cases back to the original slow case routine.
 
1633
    CodeLocationLabel lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine;
 
1634
    patchBuffer.link(failureCases1, lastProtoBegin);
 
1635
    patchBuffer.link(failureCases2, lastProtoBegin);
 
1636
 
 
1637
    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
 
1638
    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
 
1639
 
 
1640
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
1641
 
 
1642
    structure->ref();
 
1643
    prototypeStructure->ref();
 
1644
    prototypeStructures->list[currentIndex].set(entryLabel, structure, prototypeStructure);
 
1645
 
 
1646
    // Finally patch the jump to slow case back in the hot path to jump here instead.
 
1647
    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
 
1648
    RepatchBuffer repatchBuffer(m_codeBlock);
 
1649
    repatchBuffer.relink(jumpLocation, entryLabel);
 
1650
}
 
1651
 
 
1652
void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, CallFrame* callFrame)
 
1653
{
 
1654
    ASSERT(count);
 
1655
    
 
1656
    JumpList bucketsOfFail;
 
1657
 
 
1658
    // Check eax is an object of the right Structure.
 
1659
    Jump baseObjectCheck = checkStructure(regT0, structure);
 
1660
    bucketsOfFail.append(baseObjectCheck);
 
1661
 
 
1662
    Structure* currStructure = structure;
 
1663
    RefPtr<Structure>* chainEntries = chain->head();
 
1664
    JSObject* protoObject = 0;
 
1665
    for (unsigned i = 0; i < count; ++i) {
 
1666
        protoObject = asObject(currStructure->prototypeForLookup(callFrame));
 
1667
        currStructure = chainEntries[i].get();
 
1668
 
 
1669
        // Check the prototype object's Structure had not changed.
 
1670
        Structure** prototypeStructureAddress = &(protoObject->m_structure);
 
1671
#if PLATFORM(X86_64)
 
1672
        move(ImmPtr(currStructure), regT3);
 
1673
        bucketsOfFail.append(branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3));
 
1674
#else
 
1675
        bucketsOfFail.append(branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(currStructure)));
 
1676
#endif
 
1677
    }
 
1678
    ASSERT(protoObject);
 
1679
 
 
1680
    compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
 
1681
    Jump success = jump();
 
1682
 
 
1683
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
1684
 
 
1685
    // Use the patch information to link the failure cases back to the original slow case routine.
 
1686
    CodeLocationLabel lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine;
 
1687
 
 
1688
    patchBuffer.link(bucketsOfFail, lastProtoBegin);
 
1689
 
 
1690
    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
 
1691
    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
 
1692
 
 
1693
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
1694
 
 
1695
    // Track the stub we have created so that it will be deleted later.
 
1696
    structure->ref();
 
1697
    chain->ref();
 
1698
    prototypeStructures->list[currentIndex].set(entryLabel, structure, chain);
 
1699
 
 
1700
    // Finally patch the jump to slow case back in the hot path to jump here instead.
 
1701
    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
 
1702
    RepatchBuffer repatchBuffer(m_codeBlock);
 
1703
    repatchBuffer.relink(jumpLocation, entryLabel);
 
1704
}
 
1705
 
 
1706
void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
 
1707
{
 
1708
    ASSERT(count);
 
1709
    
 
1710
    JumpList bucketsOfFail;
 
1711
 
 
1712
    // Check eax is an object of the right Structure.
 
1713
    bucketsOfFail.append(checkStructure(regT0, structure));
 
1714
 
 
1715
    Structure* currStructure = structure;
 
1716
    RefPtr<Structure>* chainEntries = chain->head();
 
1717
    JSObject* protoObject = 0;
 
1718
    for (unsigned i = 0; i < count; ++i) {
 
1719
        protoObject = asObject(currStructure->prototypeForLookup(callFrame));
 
1720
        currStructure = chainEntries[i].get();
 
1721
 
 
1722
        // Check the prototype object's Structure had not changed.
 
1723
        Structure** prototypeStructureAddress = &(protoObject->m_structure);
 
1724
#if PLATFORM(X86_64)
 
1725
        move(ImmPtr(currStructure), regT3);
 
1726
        bucketsOfFail.append(branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), regT3));
 
1727
#else
 
1728
        bucketsOfFail.append(branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(currStructure)));
 
1729
#endif
 
1730
    }
 
1731
    ASSERT(protoObject);
 
1732
 
 
1733
    compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
 
1734
    Jump success = jump();
 
1735
 
 
1736
    LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
 
1737
 
 
1738
    // Use the patch information to link the failure cases back to the original slow case routine.
 
1739
    patchBuffer.link(bucketsOfFail, stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall));
 
1740
 
 
1741
    // On success return back to the hot patch code, at a point it will perform the store to dest for us.
 
1742
    patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
 
1743
 
 
1744
    // Track the stub we have created so that it will be deleted later.
 
1745
    CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
 
1746
    stubInfo->stubRoutine = entryLabel;
 
1747
 
 
1748
    // Finally patch the jump to slow case back in the hot path to jump here instead.
 
1749
    CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
 
1750
    RepatchBuffer repatchBuffer(m_codeBlock);
 
1751
    repatchBuffer.relink(jumpLocation, entryLabel);
 
1752
 
 
1753
    // We don't want to patch more than once - in future go to cti_op_put_by_id_generic.
 
1754
    repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_proto_list));
 
1755
}
 
1756
 
 
1757
/* ------------------------------ END: !ENABLE / ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) ------------------------------ */
 
1758
 
 
1759
#endif // !ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
 
1760
 
 
1761
#endif // USE(JSVALUE32_64)
 
1762
 
 
1763
} // namespace JSC
 
1764
 
 
1765
#endif // ENABLE(JIT)