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