]>
Commit | Line | Data |
---|---|---|
6fe7ccc8 A |
1 | # Copyright (C) 2011, 2012 Apple Inc. All rights reserved. |
2 | # | |
3 | # Redistribution and use in source and binary forms, with or without | |
4 | # modification, are permitted provided that the following conditions | |
5 | # are met: | |
6 | # 1. Redistributions of source code must retain the above copyright | |
7 | # notice, this list of conditions and the following disclaimer. | |
8 | # 2. Redistributions in binary form must reproduce the above copyright | |
9 | # notice, this list of conditions and the following disclaimer in the | |
10 | # documentation and/or other materials provided with the distribution. | |
11 | # | |
12 | # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' | |
13 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | |
14 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
15 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS | |
16 | # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
17 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
18 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
19 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
20 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
21 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
22 | # THE POSSIBILITY OF SUCH DAMAGE. | |
23 | ||
24 | ||
25 | # Crash course on the language that this is written in (which I just call | |
26 | # "assembly" even though it's more than that): | |
27 | # | |
28 | # - Mostly gas-style operand ordering. The last operand tends to be the | |
29 | # destination. So "a := b" is written as "mov b, a". But unlike gas, | |
30 | # comparisons are in-order, so "if (a < b)" is written as | |
31 | # "bilt a, b, ...". | |
32 | # | |
33 | # - "b" = byte, "h" = 16-bit word, "i" = 32-bit word, "p" = pointer. | |
34 | # Currently this is just 32-bit so "i" and "p" are interchangeable | |
35 | # except when an op supports one but not the other. | |
36 | # | |
37 | # - In general, valid operands for macro invocations and instructions are | |
38 | # registers (eg "t0"), addresses (eg "4[t0]"), base-index addresses | |
39 | # (eg "7[t0, t1, 2]"), absolute addresses (eg "0xa0000000[]"), or labels | |
40 | # (eg "_foo" or ".foo"). Macro invocations can also take anonymous | |
41 | # macros as operands. Instructions cannot take anonymous macros. | |
42 | # | |
43 | # - Labels must have names that begin with either "_" or ".". A "." label | |
44 | # is local and gets renamed before code gen to minimize namespace | |
45 | # pollution. A "_" label is an extern symbol (i.e. ".globl"). The "_" | |
46 | # may or may not be removed during code gen depending on whether the asm | |
47 | # conventions for C name mangling on the target platform mandate a "_" | |
48 | # prefix. | |
49 | # | |
50 | # - A "macro" is a lambda expression, which may be either anonymous or | |
51 | # named. But this has caveats. "macro" can take zero or more arguments, | |
52 | # which may be macros or any valid operands, but it can only return | |
53 | # code. But you can do Turing-complete things via continuation passing | |
54 | # style: "macro foo (a, b) b(a) end foo(foo, foo)". Actually, don't do | |
55 | # that, since you'll just crash the assembler. | |
56 | # | |
57 | # - An "if" is a conditional on settings. Any identifier supplied in the | |
58 | # predicate of an "if" is assumed to be a #define that is available | |
59 | # during code gen. So you can't use "if" for computation in a macro, but | |
60 | # you can use it to select different pieces of code for different | |
61 | # platforms. | |
62 | # | |
63 | # - Arguments to macros follow lexical scoping rather than dynamic scoping. | |
64 | # Const's also follow lexical scoping and may override (hide) arguments | |
65 | # or other consts. All variables (arguments and constants) can be bound | |
66 | # to operands. Additionally, arguments (but not constants) can be bound | |
67 | # to macros. | |
68 | ||
69 | ||
70 | # Below we have a bunch of constant declarations. Each constant must have | |
71 | # a corresponding ASSERT() in LLIntData.cpp. | |
72 | ||
73 | ||
74 | # Value representation constants. | |
75 | const Int32Tag = -1 | |
76 | const BooleanTag = -2 | |
77 | const NullTag = -3 | |
78 | const UndefinedTag = -4 | |
79 | const CellTag = -5 | |
80 | const EmptyValueTag = -6 | |
81 | const DeletedValueTag = -7 | |
82 | const LowestTag = DeletedValueTag | |
83 | ||
84 | ||
85 | # Utilities | |
86 | macro dispatch(advance) | |
87 | addp advance * 4, PC | |
88 | jmp [PC] | |
89 | end | |
90 | ||
91 | macro dispatchBranchWithOffset(pcOffset) | |
92 | lshifti 2, pcOffset | |
93 | addp pcOffset, PC | |
94 | jmp [PC] | |
95 | end | |
96 | ||
97 | macro dispatchBranch(pcOffset) | |
98 | loadi pcOffset, t0 | |
99 | dispatchBranchWithOffset(t0) | |
100 | end | |
101 | ||
102 | macro dispatchAfterCall() | |
103 | loadi ArgumentCount + TagOffset[cfr], PC | |
104 | jmp [PC] | |
105 | end | |
106 | ||
107 | macro cCall2(function, arg1, arg2) | |
108 | if ARMv7 | |
109 | move arg1, t0 | |
110 | move arg2, t1 | |
111 | elsif X86 | |
112 | poke arg1, 0 | |
113 | poke arg2, 1 | |
114 | else | |
115 | error | |
116 | end | |
117 | call function | |
118 | end | |
119 | ||
120 | # This barely works. arg3 and arg4 should probably be immediates. | |
121 | macro cCall4(function, arg1, arg2, arg3, arg4) | |
122 | if ARMv7 | |
123 | move arg1, t0 | |
124 | move arg2, t1 | |
125 | move arg3, t2 | |
126 | move arg4, t3 | |
127 | elsif X86 | |
128 | poke arg1, 0 | |
129 | poke arg2, 1 | |
130 | poke arg3, 2 | |
131 | poke arg4, 3 | |
132 | else | |
133 | error | |
134 | end | |
135 | call function | |
136 | end | |
137 | ||
138 | macro callSlowPath(slowPath) | |
139 | cCall2(slowPath, cfr, PC) | |
140 | move t0, PC | |
141 | move t1, cfr | |
142 | end | |
143 | ||
144 | # Debugging operation if you'd like to print an operand in the instruction stream. fromWhere | |
145 | # should be an immediate integer - any integer you like; use it to identify the place you're | |
146 | # debugging from. operand should likewise be an immediate, and should identify the operand | |
147 | # in the instruction stream you'd like to print out. | |
148 | macro traceOperand(fromWhere, operand) | |
149 | cCall4(_llint_trace_operand, cfr, PC, fromWhere, operand) | |
150 | move t0, PC | |
151 | move t1, cfr | |
152 | end | |
153 | ||
154 | # Debugging operation if you'd like to print the value of an operand in the instruction | |
155 | # stream. Same as traceOperand(), but assumes that the operand is a register, and prints its | |
156 | # value. | |
157 | macro traceValue(fromWhere, operand) | |
158 | cCall4(_llint_trace_value, cfr, PC, fromWhere, operand) | |
159 | move t0, PC | |
160 | move t1, cfr | |
161 | end | |
162 | ||
163 | # Call a slowPath for call opcodes. | |
164 | macro callCallSlowPath(advance, slowPath, action) | |
165 | addp advance * 4, PC, t0 | |
166 | storep t0, ArgumentCount + TagOffset[cfr] | |
167 | cCall2(slowPath, cfr, PC) | |
168 | move t1, cfr | |
169 | action(t0) | |
170 | end | |
171 | ||
172 | macro checkSwitchToJITForLoop() | |
173 | checkSwitchToJIT( | |
174 | 1, | |
175 | macro () | |
176 | storei PC, ArgumentCount + TagOffset[cfr] | |
177 | cCall2(_llint_loop_osr, cfr, PC) | |
178 | move t1, cfr | |
179 | btpz t0, .recover | |
180 | jmp t0 | |
181 | .recover: | |
182 | loadi ArgumentCount + TagOffset[cfr], PC | |
183 | end) | |
184 | end | |
185 | ||
186 | # Index, tag, and payload must be different registers. Index is not | |
187 | # changed. | |
188 | macro loadConstantOrVariable(index, tag, payload) | |
189 | bigteq index, FirstConstantRegisterIndex, .constant | |
190 | loadi TagOffset[cfr, index, 8], tag | |
191 | loadi PayloadOffset[cfr, index, 8], payload | |
192 | jmp .done | |
193 | .constant: | |
194 | loadp CodeBlock[cfr], payload | |
195 | loadp CodeBlock::m_constantRegisters + VectorBufferOffset[payload], payload | |
196 | # There is a bit of evil here: if the index contains a value >= FirstConstantRegisterIndex, | |
197 | # then value << 3 will be equal to (value - FirstConstantRegisterIndex) << 3. | |
198 | loadp TagOffset[payload, index, 8], tag | |
199 | loadp PayloadOffset[payload, index, 8], payload | |
200 | .done: | |
201 | end | |
202 | ||
203 | macro loadConstantOrVariableTag(index, tag) | |
204 | bigteq index, FirstConstantRegisterIndex, .constant | |
205 | loadi TagOffset[cfr, index, 8], tag | |
206 | jmp .done | |
207 | .constant: | |
208 | loadp CodeBlock[cfr], tag | |
209 | loadp CodeBlock::m_constantRegisters + VectorBufferOffset[tag], tag | |
210 | # There is a bit of evil here: if the index contains a value >= FirstConstantRegisterIndex, | |
211 | # then value << 3 will be equal to (value - FirstConstantRegisterIndex) << 3. | |
212 | loadp TagOffset[tag, index, 8], tag | |
213 | .done: | |
214 | end | |
215 | ||
216 | # Index and payload may be the same register. Index may be clobbered. | |
217 | macro loadConstantOrVariable2Reg(index, tag, payload) | |
218 | bigteq index, FirstConstantRegisterIndex, .constant | |
219 | loadi TagOffset[cfr, index, 8], tag | |
220 | loadi PayloadOffset[cfr, index, 8], payload | |
221 | jmp .done | |
222 | .constant: | |
223 | loadp CodeBlock[cfr], tag | |
224 | loadp CodeBlock::m_constantRegisters + VectorBufferOffset[tag], tag | |
225 | # There is a bit of evil here: if the index contains a value >= FirstConstantRegisterIndex, | |
226 | # then value << 3 will be equal to (value - FirstConstantRegisterIndex) << 3. | |
227 | lshifti 3, index | |
228 | addp index, tag | |
229 | loadp PayloadOffset[tag], payload | |
230 | loadp TagOffset[tag], tag | |
231 | .done: | |
232 | end | |
233 | ||
234 | macro loadConstantOrVariablePayloadTagCustom(index, tagCheck, payload) | |
235 | bigteq index, FirstConstantRegisterIndex, .constant | |
236 | tagCheck(TagOffset[cfr, index, 8]) | |
237 | loadi PayloadOffset[cfr, index, 8], payload | |
238 | jmp .done | |
239 | .constant: | |
240 | loadp CodeBlock[cfr], payload | |
241 | loadp CodeBlock::m_constantRegisters + VectorBufferOffset[payload], payload | |
242 | # There is a bit of evil here: if the index contains a value >= FirstConstantRegisterIndex, | |
243 | # then value << 3 will be equal to (value - FirstConstantRegisterIndex) << 3. | |
244 | tagCheck(TagOffset[payload, index, 8]) | |
245 | loadp PayloadOffset[payload, index, 8], payload | |
246 | .done: | |
247 | end | |
248 | ||
249 | # Index and payload must be different registers. Index is not mutated. Use | |
250 | # this if you know what the tag of the variable should be. Doing the tag | |
251 | # test as part of loading the variable reduces register use, but may not | |
252 | # be faster than doing loadConstantOrVariable followed by a branch on the | |
253 | # tag. | |
254 | macro loadConstantOrVariablePayload(index, expectedTag, payload, slow) | |
255 | loadConstantOrVariablePayloadTagCustom( | |
256 | index, | |
257 | macro (actualTag) bineq actualTag, expectedTag, slow end, | |
258 | payload) | |
259 | end | |
260 | ||
261 | macro loadConstantOrVariablePayloadUnchecked(index, payload) | |
262 | loadConstantOrVariablePayloadTagCustom( | |
263 | index, | |
264 | macro (actualTag) end, | |
265 | payload) | |
266 | end | |
267 | ||
268 | macro writeBarrier(tag, payload) | |
269 | # Nothing to do, since we don't have a generational or incremental collector. | |
270 | end | |
271 | ||
272 | macro valueProfile(tag, payload, profile) | |
273 | if VALUE_PROFILER | |
274 | storei tag, ValueProfile::m_buckets + TagOffset[profile] | |
275 | storei payload, ValueProfile::m_buckets + PayloadOffset[profile] | |
276 | end | |
277 | end | |
278 | ||
279 | ||
280 | # Entrypoints into the interpreter | |
281 | ||
282 | # Expects that CodeBlock is in t1, which is what prologue() leaves behind. | |
283 | macro functionArityCheck(doneLabel, slow_path) | |
284 | loadi PayloadOffset + ArgumentCount[cfr], t0 | |
285 | biaeq t0, CodeBlock::m_numParameters[t1], doneLabel | |
286 | cCall2(slow_path, cfr, PC) # This slow_path has a simple protocol: t0 = 0 => no error, t0 != 0 => error | |
287 | move t1, cfr | |
288 | btiz t0, .continue | |
289 | loadp JITStackFrame::globalData[sp], t1 | |
290 | loadp JSGlobalData::callFrameForThrow[t1], t0 | |
291 | jmp JSGlobalData::targetMachinePCForThrow[t1] | |
292 | .continue: | |
293 | # Reload CodeBlock and PC, since the slow_path clobbered it. | |
294 | loadp CodeBlock[cfr], t1 | |
295 | loadp CodeBlock::m_instructions[t1], PC | |
296 | jmp doneLabel | |
297 | end | |
298 | ||
299 | ||
300 | # Instruction implementations | |
301 | ||
302 | _llint_op_enter: | |
303 | traceExecution() | |
304 | loadp CodeBlock[cfr], t2 | |
305 | loadi CodeBlock::m_numVars[t2], t2 | |
306 | btiz t2, .opEnterDone | |
307 | move UndefinedTag, t0 | |
308 | move 0, t1 | |
309 | .opEnterLoop: | |
310 | subi 1, t2 | |
311 | storei t0, TagOffset[cfr, t2, 8] | |
312 | storei t1, PayloadOffset[cfr, t2, 8] | |
313 | btinz t2, .opEnterLoop | |
314 | .opEnterDone: | |
315 | dispatch(1) | |
316 | ||
317 | ||
318 | _llint_op_create_activation: | |
319 | traceExecution() | |
320 | loadi 4[PC], t0 | |
321 | bineq TagOffset[cfr, t0, 8], EmptyValueTag, .opCreateActivationDone | |
322 | callSlowPath(_llint_slow_path_create_activation) | |
323 | .opCreateActivationDone: | |
324 | dispatch(2) | |
325 | ||
326 | ||
327 | _llint_op_init_lazy_reg: | |
328 | traceExecution() | |
329 | loadi 4[PC], t0 | |
330 | storei EmptyValueTag, TagOffset[cfr, t0, 8] | |
331 | storei 0, PayloadOffset[cfr, t0, 8] | |
332 | dispatch(2) | |
333 | ||
334 | ||
335 | _llint_op_create_arguments: | |
336 | traceExecution() | |
337 | loadi 4[PC], t0 | |
338 | bineq TagOffset[cfr, t0, 8], EmptyValueTag, .opCreateArgumentsDone | |
339 | callSlowPath(_llint_slow_path_create_arguments) | |
340 | .opCreateArgumentsDone: | |
341 | dispatch(2) | |
342 | ||
343 | ||
344 | _llint_op_create_this: | |
345 | traceExecution() | |
346 | loadi 8[PC], t0 | |
347 | assertNotConstant(t0) | |
348 | bineq TagOffset[cfr, t0, 8], CellTag, .opCreateThisSlow | |
349 | loadi PayloadOffset[cfr, t0, 8], t0 | |
350 | loadp JSCell::m_structure[t0], t1 | |
351 | bbb Structure::m_typeInfo + TypeInfo::m_type[t1], ObjectType, .opCreateThisSlow | |
352 | loadp JSObject::m_inheritorID[t0], t2 | |
353 | btpz t2, .opCreateThisSlow | |
354 | allocateBasicJSObject(JSFinalObjectSizeClassIndex, JSGlobalData::jsFinalObjectClassInfo, t2, t0, t1, t3, .opCreateThisSlow) | |
355 | loadi 4[PC], t1 | |
356 | storei CellTag, TagOffset[cfr, t1, 8] | |
357 | storei t0, PayloadOffset[cfr, t1, 8] | |
358 | dispatch(3) | |
359 | ||
360 | .opCreateThisSlow: | |
361 | callSlowPath(_llint_slow_path_create_this) | |
362 | dispatch(3) | |
363 | ||
364 | ||
365 | _llint_op_get_callee: | |
366 | traceExecution() | |
367 | loadi 4[PC], t0 | |
368 | loadp PayloadOffset + Callee[cfr], t1 | |
369 | storei CellTag, TagOffset[cfr, t0, 8] | |
370 | storei t1, PayloadOffset[cfr, t0, 8] | |
371 | dispatch(2) | |
372 | ||
373 | ||
374 | _llint_op_convert_this: | |
375 | traceExecution() | |
376 | loadi 4[PC], t0 | |
377 | bineq TagOffset[cfr, t0, 8], CellTag, .opConvertThisSlow | |
378 | loadi PayloadOffset[cfr, t0, 8], t0 | |
379 | loadp JSCell::m_structure[t0], t0 | |
380 | bbb Structure::m_typeInfo + TypeInfo::m_type[t0], ObjectType, .opConvertThisSlow | |
381 | dispatch(2) | |
382 | ||
383 | .opConvertThisSlow: | |
384 | callSlowPath(_llint_slow_path_convert_this) | |
385 | dispatch(2) | |
386 | ||
387 | ||
388 | _llint_op_new_object: | |
389 | traceExecution() | |
390 | loadp CodeBlock[cfr], t0 | |
391 | loadp CodeBlock::m_globalObject[t0], t0 | |
392 | loadp JSGlobalObject::m_emptyObjectStructure[t0], t1 | |
393 | allocateBasicJSObject(JSFinalObjectSizeClassIndex, JSGlobalData::jsFinalObjectClassInfo, t1, t0, t2, t3, .opNewObjectSlow) | |
394 | loadi 4[PC], t1 | |
395 | storei CellTag, TagOffset[cfr, t1, 8] | |
396 | storei t0, PayloadOffset[cfr, t1, 8] | |
397 | dispatch(2) | |
398 | ||
399 | .opNewObjectSlow: | |
400 | callSlowPath(_llint_slow_path_new_object) | |
401 | dispatch(2) | |
402 | ||
403 | ||
404 | _llint_op_mov: | |
405 | traceExecution() | |
406 | loadi 8[PC], t1 | |
407 | loadi 4[PC], t0 | |
408 | loadConstantOrVariable(t1, t2, t3) | |
409 | storei t2, TagOffset[cfr, t0, 8] | |
410 | storei t3, PayloadOffset[cfr, t0, 8] | |
411 | dispatch(3) | |
412 | ||
413 | ||
414 | _llint_op_not: | |
415 | traceExecution() | |
416 | loadi 8[PC], t0 | |
417 | loadi 4[PC], t1 | |
418 | loadConstantOrVariable(t0, t2, t3) | |
419 | bineq t2, BooleanTag, .opNotSlow | |
420 | xori 1, t3 | |
421 | storei t2, TagOffset[cfr, t1, 8] | |
422 | storei t3, PayloadOffset[cfr, t1, 8] | |
423 | dispatch(3) | |
424 | ||
425 | .opNotSlow: | |
426 | callSlowPath(_llint_slow_path_not) | |
427 | dispatch(3) | |
428 | ||
429 | ||
430 | _llint_op_eq: | |
431 | traceExecution() | |
432 | loadi 12[PC], t2 | |
433 | loadi 8[PC], t0 | |
434 | loadConstantOrVariable(t2, t3, t1) | |
435 | loadConstantOrVariable2Reg(t0, t2, t0) | |
436 | bineq t2, t3, .opEqSlow | |
437 | bieq t2, CellTag, .opEqSlow | |
438 | bib t2, LowestTag, .opEqSlow | |
439 | loadi 4[PC], t2 | |
440 | cieq t0, t1, t0 | |
441 | storei BooleanTag, TagOffset[cfr, t2, 8] | |
442 | storei t0, PayloadOffset[cfr, t2, 8] | |
443 | dispatch(4) | |
444 | ||
445 | .opEqSlow: | |
446 | callSlowPath(_llint_slow_path_eq) | |
447 | dispatch(4) | |
448 | ||
449 | ||
450 | _llint_op_eq_null: | |
451 | traceExecution() | |
452 | loadi 8[PC], t0 | |
453 | loadi 4[PC], t3 | |
454 | assertNotConstant(t0) | |
455 | loadi TagOffset[cfr, t0, 8], t1 | |
456 | loadi PayloadOffset[cfr, t0, 8], t0 | |
457 | bineq t1, CellTag, .opEqNullImmediate | |
458 | loadp JSCell::m_structure[t0], t1 | |
459 | tbnz Structure::m_typeInfo + TypeInfo::m_flags[t1], MasqueradesAsUndefined, t1 | |
460 | jmp .opEqNullNotImmediate | |
461 | .opEqNullImmediate: | |
462 | cieq t1, NullTag, t2 | |
463 | cieq t1, UndefinedTag, t1 | |
464 | ori t2, t1 | |
465 | .opEqNullNotImmediate: | |
466 | storei BooleanTag, TagOffset[cfr, t3, 8] | |
467 | storei t1, PayloadOffset[cfr, t3, 8] | |
468 | dispatch(3) | |
469 | ||
470 | ||
471 | _llint_op_neq: | |
472 | traceExecution() | |
473 | loadi 12[PC], t2 | |
474 | loadi 8[PC], t0 | |
475 | loadConstantOrVariable(t2, t3, t1) | |
476 | loadConstantOrVariable2Reg(t0, t2, t0) | |
477 | bineq t2, t3, .opNeqSlow | |
478 | bieq t2, CellTag, .opNeqSlow | |
479 | bib t2, LowestTag, .opNeqSlow | |
480 | loadi 4[PC], t2 | |
481 | cineq t0, t1, t0 | |
482 | storei BooleanTag, TagOffset[cfr, t2, 8] | |
483 | storei t0, PayloadOffset[cfr, t2, 8] | |
484 | dispatch(4) | |
485 | ||
486 | .opNeqSlow: | |
487 | callSlowPath(_llint_slow_path_neq) | |
488 | dispatch(4) | |
489 | ||
490 | ||
491 | _llint_op_neq_null: | |
492 | traceExecution() | |
493 | loadi 8[PC], t0 | |
494 | loadi 4[PC], t3 | |
495 | assertNotConstant(t0) | |
496 | loadi TagOffset[cfr, t0, 8], t1 | |
497 | loadi PayloadOffset[cfr, t0, 8], t0 | |
498 | bineq t1, CellTag, .opNeqNullImmediate | |
499 | loadp JSCell::m_structure[t0], t1 | |
500 | tbz Structure::m_typeInfo + TypeInfo::m_flags[t1], MasqueradesAsUndefined, t1 | |
501 | jmp .opNeqNullNotImmediate | |
502 | .opNeqNullImmediate: | |
503 | cineq t1, NullTag, t2 | |
504 | cineq t1, UndefinedTag, t1 | |
505 | andi t2, t1 | |
506 | .opNeqNullNotImmediate: | |
507 | storei BooleanTag, TagOffset[cfr, t3, 8] | |
508 | storei t1, PayloadOffset[cfr, t3, 8] | |
509 | dispatch(3) | |
510 | ||
511 | ||
512 | macro strictEq(equalityOperation, slowPath) | |
513 | loadi 12[PC], t2 | |
514 | loadi 8[PC], t0 | |
515 | loadConstantOrVariable(t2, t3, t1) | |
516 | loadConstantOrVariable2Reg(t0, t2, t0) | |
517 | bineq t2, t3, .slow | |
518 | bib t2, LowestTag, .slow | |
519 | bineq t2, CellTag, .notString | |
520 | loadp JSCell::m_structure[t0], t2 | |
521 | loadp JSCell::m_structure[t1], t3 | |
522 | bbneq Structure::m_typeInfo + TypeInfo::m_type[t2], StringType, .notString | |
523 | bbeq Structure::m_typeInfo + TypeInfo::m_type[t3], StringType, .slow | |
524 | .notString: | |
525 | loadi 4[PC], t2 | |
526 | equalityOperation(t0, t1, t0) | |
527 | storei BooleanTag, TagOffset[cfr, t2, 8] | |
528 | storei t0, PayloadOffset[cfr, t2, 8] | |
529 | dispatch(4) | |
530 | ||
531 | .slow: | |
532 | callSlowPath(slowPath) | |
533 | dispatch(4) | |
534 | end | |
535 | ||
536 | _llint_op_stricteq: | |
537 | traceExecution() | |
538 | strictEq(macro (left, right, result) cieq left, right, result end, _llint_slow_path_stricteq) | |
539 | ||
540 | ||
541 | _llint_op_nstricteq: | |
542 | traceExecution() | |
543 | strictEq(macro (left, right, result) cineq left, right, result end, _llint_slow_path_nstricteq) | |
544 | ||
545 | ||
546 | _llint_op_pre_inc: | |
547 | traceExecution() | |
548 | loadi 4[PC], t0 | |
549 | bineq TagOffset[cfr, t0, 8], Int32Tag, .opPreIncSlow | |
550 | loadi PayloadOffset[cfr, t0, 8], t1 | |
551 | baddio 1, t1, .opPreIncSlow | |
552 | storei t1, PayloadOffset[cfr, t0, 8] | |
553 | dispatch(2) | |
554 | ||
555 | .opPreIncSlow: | |
556 | callSlowPath(_llint_slow_path_pre_inc) | |
557 | dispatch(2) | |
558 | ||
559 | ||
560 | _llint_op_pre_dec: | |
561 | traceExecution() | |
562 | loadi 4[PC], t0 | |
563 | bineq TagOffset[cfr, t0, 8], Int32Tag, .opPreDecSlow | |
564 | loadi PayloadOffset[cfr, t0, 8], t1 | |
565 | bsubio 1, t1, .opPreDecSlow | |
566 | storei t1, PayloadOffset[cfr, t0, 8] | |
567 | dispatch(2) | |
568 | ||
569 | .opPreDecSlow: | |
570 | callSlowPath(_llint_slow_path_pre_dec) | |
571 | dispatch(2) | |
572 | ||
573 | ||
574 | _llint_op_post_inc: | |
575 | traceExecution() | |
576 | loadi 8[PC], t0 | |
577 | loadi 4[PC], t1 | |
578 | bineq TagOffset[cfr, t0, 8], Int32Tag, .opPostIncSlow | |
579 | bieq t0, t1, .opPostIncDone | |
580 | loadi PayloadOffset[cfr, t0, 8], t2 | |
581 | move t2, t3 | |
582 | baddio 1, t3, .opPostIncSlow | |
583 | storei Int32Tag, TagOffset[cfr, t1, 8] | |
584 | storei t2, PayloadOffset[cfr, t1, 8] | |
585 | storei t3, PayloadOffset[cfr, t0, 8] | |
586 | .opPostIncDone: | |
587 | dispatch(3) | |
588 | ||
589 | .opPostIncSlow: | |
590 | callSlowPath(_llint_slow_path_post_inc) | |
591 | dispatch(3) | |
592 | ||
593 | ||
594 | _llint_op_post_dec: | |
595 | traceExecution() | |
596 | loadi 8[PC], t0 | |
597 | loadi 4[PC], t1 | |
598 | bineq TagOffset[cfr, t0, 8], Int32Tag, .opPostDecSlow | |
599 | bieq t0, t1, .opPostDecDone | |
600 | loadi PayloadOffset[cfr, t0, 8], t2 | |
601 | move t2, t3 | |
602 | bsubio 1, t3, .opPostDecSlow | |
603 | storei Int32Tag, TagOffset[cfr, t1, 8] | |
604 | storei t2, PayloadOffset[cfr, t1, 8] | |
605 | storei t3, PayloadOffset[cfr, t0, 8] | |
606 | .opPostDecDone: | |
607 | dispatch(3) | |
608 | ||
609 | .opPostDecSlow: | |
610 | callSlowPath(_llint_slow_path_post_dec) | |
611 | dispatch(3) | |
612 | ||
613 | ||
614 | _llint_op_to_jsnumber: | |
615 | traceExecution() | |
616 | loadi 8[PC], t0 | |
617 | loadi 4[PC], t1 | |
618 | loadConstantOrVariable(t0, t2, t3) | |
619 | bieq t2, Int32Tag, .opToJsnumberIsInt | |
620 | biaeq t2, EmptyValueTag, .opToJsnumberSlow | |
621 | .opToJsnumberIsInt: | |
622 | storei t2, TagOffset[cfr, t1, 8] | |
623 | storei t3, PayloadOffset[cfr, t1, 8] | |
624 | dispatch(3) | |
625 | ||
626 | .opToJsnumberSlow: | |
627 | callSlowPath(_llint_slow_path_to_jsnumber) | |
628 | dispatch(3) | |
629 | ||
630 | ||
631 | _llint_op_negate: | |
632 | traceExecution() | |
633 | loadi 8[PC], t0 | |
634 | loadi 4[PC], t3 | |
635 | loadConstantOrVariable(t0, t1, t2) | |
636 | bineq t1, Int32Tag, .opNegateSrcNotInt | |
637 | btiz t2, 0x7fffffff, .opNegateSlow | |
638 | negi t2 | |
639 | storei Int32Tag, TagOffset[cfr, t3, 8] | |
640 | storei t2, PayloadOffset[cfr, t3, 8] | |
641 | dispatch(3) | |
642 | .opNegateSrcNotInt: | |
643 | bia t1, LowestTag, .opNegateSlow | |
644 | xori 0x80000000, t1 | |
645 | storei t1, TagOffset[cfr, t3, 8] | |
646 | storei t2, PayloadOffset[cfr, t3, 8] | |
647 | dispatch(3) | |
648 | ||
649 | .opNegateSlow: | |
650 | callSlowPath(_llint_slow_path_negate) | |
651 | dispatch(3) | |
652 | ||
653 | ||
654 | macro binaryOpCustomStore(integerOperationAndStore, doubleOperation, slowPath) | |
655 | loadi 12[PC], t2 | |
656 | loadi 8[PC], t0 | |
657 | loadConstantOrVariable(t2, t3, t1) | |
658 | loadConstantOrVariable2Reg(t0, t2, t0) | |
659 | bineq t2, Int32Tag, .op1NotInt | |
660 | bineq t3, Int32Tag, .op2NotInt | |
661 | loadi 4[PC], t2 | |
662 | integerOperationAndStore(t3, t1, t0, .slow, t2) | |
663 | dispatch(5) | |
664 | ||
665 | .op1NotInt: | |
666 | # First operand is definitely not an int, the second operand could be anything. | |
667 | bia t2, LowestTag, .slow | |
668 | bib t3, LowestTag, .op1NotIntOp2Double | |
669 | bineq t3, Int32Tag, .slow | |
670 | ci2d t1, ft1 | |
671 | jmp .op1NotIntReady | |
672 | .op1NotIntOp2Double: | |
673 | fii2d t1, t3, ft1 | |
674 | .op1NotIntReady: | |
675 | loadi 4[PC], t1 | |
676 | fii2d t0, t2, ft0 | |
677 | doubleOperation(ft1, ft0) | |
678 | stored ft0, [cfr, t1, 8] | |
679 | dispatch(5) | |
680 | ||
681 | .op2NotInt: | |
682 | # First operand is definitely an int, the second operand is definitely not. | |
683 | loadi 4[PC], t2 | |
684 | bia t3, LowestTag, .slow | |
685 | ci2d t0, ft0 | |
686 | fii2d t1, t3, ft1 | |
687 | doubleOperation(ft1, ft0) | |
688 | stored ft0, [cfr, t2, 8] | |
689 | dispatch(5) | |
690 | ||
691 | .slow: | |
692 | callSlowPath(slowPath) | |
693 | dispatch(5) | |
694 | end | |
695 | ||
696 | macro binaryOp(integerOperation, doubleOperation, slowPath) | |
697 | binaryOpCustomStore( | |
698 | macro (int32Tag, left, right, slow, index) | |
699 | integerOperation(left, right, slow) | |
700 | storei int32Tag, TagOffset[cfr, index, 8] | |
701 | storei right, PayloadOffset[cfr, index, 8] | |
702 | end, | |
703 | doubleOperation, slowPath) | |
704 | end | |
705 | ||
706 | _llint_op_add: | |
707 | traceExecution() | |
708 | binaryOp( | |
709 | macro (left, right, slow) baddio left, right, slow end, | |
710 | macro (left, right) addd left, right end, | |
711 | _llint_slow_path_add) | |
712 | ||
713 | ||
714 | _llint_op_mul: | |
715 | traceExecution() | |
716 | binaryOpCustomStore( | |
717 | macro (int32Tag, left, right, slow, index) | |
718 | const scratch = int32Tag # We know that we can reuse the int32Tag register since it has a constant. | |
719 | move right, scratch | |
720 | bmulio left, scratch, slow | |
721 | btinz scratch, .done | |
722 | bilt left, 0, slow | |
723 | bilt right, 0, slow | |
724 | .done: | |
725 | storei Int32Tag, TagOffset[cfr, index, 8] | |
726 | storei scratch, PayloadOffset[cfr, index, 8] | |
727 | end, | |
728 | macro (left, right) muld left, right end, | |
729 | _llint_slow_path_mul) | |
730 | ||
731 | ||
732 | _llint_op_sub: | |
733 | traceExecution() | |
734 | binaryOp( | |
735 | macro (left, right, slow) bsubio left, right, slow end, | |
736 | macro (left, right) subd left, right end, | |
737 | _llint_slow_path_sub) | |
738 | ||
739 | ||
740 | _llint_op_div: | |
741 | traceExecution() | |
742 | binaryOpCustomStore( | |
743 | macro (int32Tag, left, right, slow, index) | |
744 | ci2d left, ft0 | |
745 | ci2d right, ft1 | |
746 | divd ft0, ft1 | |
747 | bcd2i ft1, right, .notInt | |
748 | storei int32Tag, TagOffset[cfr, index, 8] | |
749 | storei right, PayloadOffset[cfr, index, 8] | |
750 | jmp .done | |
751 | .notInt: | |
752 | stored ft1, [cfr, index, 8] | |
753 | .done: | |
754 | end, | |
755 | macro (left, right) divd left, right end, | |
756 | _llint_slow_path_div) | |
757 | ||
758 | ||
759 | macro bitOp(operation, slowPath, advance) | |
760 | loadi 12[PC], t2 | |
761 | loadi 8[PC], t0 | |
762 | loadConstantOrVariable(t2, t3, t1) | |
763 | loadConstantOrVariable2Reg(t0, t2, t0) | |
764 | bineq t3, Int32Tag, .slow | |
765 | bineq t2, Int32Tag, .slow | |
766 | loadi 4[PC], t2 | |
767 | operation(t1, t0, .slow) | |
768 | storei t3, TagOffset[cfr, t2, 8] | |
769 | storei t0, PayloadOffset[cfr, t2, 8] | |
770 | dispatch(advance) | |
771 | ||
772 | .slow: | |
773 | callSlowPath(slowPath) | |
774 | dispatch(advance) | |
775 | end | |
776 | ||
777 | _llint_op_lshift: | |
778 | traceExecution() | |
779 | bitOp( | |
780 | macro (left, right, slow) lshifti left, right end, | |
781 | _llint_slow_path_lshift, | |
782 | 4) | |
783 | ||
784 | ||
785 | _llint_op_rshift: | |
786 | traceExecution() | |
787 | bitOp( | |
788 | macro (left, right, slow) rshifti left, right end, | |
789 | _llint_slow_path_rshift, | |
790 | 4) | |
791 | ||
792 | ||
793 | _llint_op_urshift: | |
794 | traceExecution() | |
795 | bitOp( | |
796 | macro (left, right, slow) | |
797 | urshifti left, right | |
798 | bilt right, 0, slow | |
799 | end, | |
800 | _llint_slow_path_urshift, | |
801 | 4) | |
802 | ||
803 | ||
804 | _llint_op_bitand: | |
805 | traceExecution() | |
806 | bitOp( | |
807 | macro (left, right, slow) andi left, right end, | |
808 | _llint_slow_path_bitand, | |
809 | 5) | |
810 | ||
811 | ||
812 | _llint_op_bitxor: | |
813 | traceExecution() | |
814 | bitOp( | |
815 | macro (left, right, slow) xori left, right end, | |
816 | _llint_slow_path_bitxor, | |
817 | 5) | |
818 | ||
819 | ||
820 | _llint_op_bitor: | |
821 | traceExecution() | |
822 | bitOp( | |
823 | macro (left, right, slow) ori left, right end, | |
824 | _llint_slow_path_bitor, | |
825 | 5) | |
826 | ||
827 | ||
828 | _llint_op_check_has_instance: | |
829 | traceExecution() | |
830 | loadi 4[PC], t1 | |
831 | loadConstantOrVariablePayload(t1, CellTag, t0, .opCheckHasInstanceSlow) | |
832 | loadp JSCell::m_structure[t0], t0 | |
833 | btbz Structure::m_typeInfo + TypeInfo::m_flags[t0], ImplementsHasInstance, .opCheckHasInstanceSlow | |
834 | dispatch(2) | |
835 | ||
836 | .opCheckHasInstanceSlow: | |
837 | callSlowPath(_llint_slow_path_check_has_instance) | |
838 | dispatch(2) | |
839 | ||
840 | ||
841 | _llint_op_instanceof: | |
842 | traceExecution() | |
843 | # Check that baseVal implements the default HasInstance behavior. | |
844 | # FIXME: This should be deprecated. | |
845 | loadi 12[PC], t1 | |
846 | loadConstantOrVariablePayloadUnchecked(t1, t0) | |
847 | loadp JSCell::m_structure[t0], t0 | |
848 | btbz Structure::m_typeInfo + TypeInfo::m_flags[t0], ImplementsDefaultHasInstance, .opInstanceofSlow | |
849 | ||
850 | # Actually do the work. | |
851 | loadi 16[PC], t0 | |
852 | loadi 4[PC], t3 | |
853 | loadConstantOrVariablePayload(t0, CellTag, t1, .opInstanceofSlow) | |
854 | loadp JSCell::m_structure[t1], t2 | |
855 | bbb Structure::m_typeInfo + TypeInfo::m_type[t2], ObjectType, .opInstanceofSlow | |
856 | loadi 8[PC], t0 | |
857 | loadConstantOrVariablePayload(t0, CellTag, t2, .opInstanceofSlow) | |
858 | ||
859 | # Register state: t1 = prototype, t2 = value | |
860 | move 1, t0 | |
861 | .opInstanceofLoop: | |
862 | loadp JSCell::m_structure[t2], t2 | |
863 | loadi Structure::m_prototype + PayloadOffset[t2], t2 | |
864 | bpeq t2, t1, .opInstanceofDone | |
865 | btinz t2, .opInstanceofLoop | |
866 | ||
867 | move 0, t0 | |
868 | .opInstanceofDone: | |
869 | storei BooleanTag, TagOffset[cfr, t3, 8] | |
870 | storei t0, PayloadOffset[cfr, t3, 8] | |
871 | dispatch(5) | |
872 | ||
873 | .opInstanceofSlow: | |
874 | callSlowPath(_llint_slow_path_instanceof) | |
875 | dispatch(5) | |
876 | ||
877 | ||
878 | _llint_op_is_undefined: | |
879 | traceExecution() | |
880 | loadi 8[PC], t1 | |
881 | loadi 4[PC], t0 | |
882 | loadConstantOrVariable(t1, t2, t3) | |
883 | storei BooleanTag, TagOffset[cfr, t0, 8] | |
884 | bieq t2, CellTag, .opIsUndefinedCell | |
885 | cieq t2, UndefinedTag, t3 | |
886 | storei t3, PayloadOffset[cfr, t0, 8] | |
887 | dispatch(3) | |
888 | .opIsUndefinedCell: | |
889 | loadp JSCell::m_structure[t3], t1 | |
890 | tbnz Structure::m_typeInfo + TypeInfo::m_flags[t1], MasqueradesAsUndefined, t1 | |
891 | storei t1, PayloadOffset[cfr, t0, 8] | |
892 | dispatch(3) | |
893 | ||
894 | ||
895 | _llint_op_is_boolean: | |
896 | traceExecution() | |
897 | loadi 8[PC], t1 | |
898 | loadi 4[PC], t2 | |
899 | loadConstantOrVariableTag(t1, t0) | |
900 | cieq t0, BooleanTag, t0 | |
901 | storei BooleanTag, TagOffset[cfr, t2, 8] | |
902 | storei t0, PayloadOffset[cfr, t2, 8] | |
903 | dispatch(3) | |
904 | ||
905 | ||
906 | _llint_op_is_number: | |
907 | traceExecution() | |
908 | loadi 8[PC], t1 | |
909 | loadi 4[PC], t2 | |
910 | loadConstantOrVariableTag(t1, t0) | |
911 | storei BooleanTag, TagOffset[cfr, t2, 8] | |
912 | addi 1, t0 | |
913 | cib t0, LowestTag + 1, t1 | |
914 | storei t1, PayloadOffset[cfr, t2, 8] | |
915 | dispatch(3) | |
916 | ||
917 | ||
918 | _llint_op_is_string: | |
919 | traceExecution() | |
920 | loadi 8[PC], t1 | |
921 | loadi 4[PC], t2 | |
922 | loadConstantOrVariable(t1, t0, t3) | |
923 | storei BooleanTag, TagOffset[cfr, t2, 8] | |
924 | bineq t0, CellTag, .opIsStringNotCell | |
925 | loadp JSCell::m_structure[t3], t0 | |
926 | cbeq Structure::m_typeInfo + TypeInfo::m_type[t0], StringType, t1 | |
927 | storei t1, PayloadOffset[cfr, t2, 8] | |
928 | dispatch(3) | |
929 | .opIsStringNotCell: | |
930 | storep 0, PayloadOffset[cfr, t2, 8] | |
931 | dispatch(3) | |
932 | ||
933 | ||
934 | macro resolveGlobal(size, slow) | |
935 | # Operands are as follows: | |
936 | # 4[PC] Destination for the load. | |
937 | # 8[PC] Property identifier index in the code block. | |
938 | # 12[PC] Structure pointer, initialized to 0 by bytecode generator. | |
939 | # 16[PC] Offset in global object, initialized to 0 by bytecode generator. | |
940 | loadp CodeBlock[cfr], t0 | |
941 | loadp CodeBlock::m_globalObject[t0], t0 | |
942 | loadp JSCell::m_structure[t0], t1 | |
943 | bpneq t1, 12[PC], slow | |
944 | loadi 16[PC], t1 | |
945 | loadp JSObject::m_propertyStorage[t0], t0 | |
946 | loadi TagOffset[t0, t1, 8], t2 | |
947 | loadi PayloadOffset[t0, t1, 8], t3 | |
948 | loadi 4[PC], t0 | |
949 | storei t2, TagOffset[cfr, t0, 8] | |
950 | storei t3, PayloadOffset[cfr, t0, 8] | |
951 | loadi (size - 1) * 4[PC], t0 | |
952 | valueProfile(t2, t3, t0) | |
953 | end | |
954 | ||
955 | _llint_op_resolve_global: | |
956 | traceExecution() | |
957 | resolveGlobal(6, .opResolveGlobalSlow) | |
958 | dispatch(6) | |
959 | ||
960 | .opResolveGlobalSlow: | |
961 | callSlowPath(_llint_slow_path_resolve_global) | |
962 | dispatch(6) | |
963 | ||
964 | ||
965 | # Gives you the scope in t0, while allowing you to optionally perform additional checks on the | |
966 | # scopes as they are traversed. scopeCheck() is called with two arguments: the register | |
967 | # holding the scope, and a register that can be used for scratch. Note that this does not | |
968 | # use t3, so you can hold stuff in t3 if need be. | |
969 | macro getScope(deBruijinIndexOperand, scopeCheck) | |
970 | loadp ScopeChain + PayloadOffset[cfr], t0 | |
971 | loadi deBruijinIndexOperand, t2 | |
972 | ||
973 | btiz t2, .done | |
974 | ||
975 | loadp CodeBlock[cfr], t1 | |
976 | bineq CodeBlock::m_codeType[t1], FunctionCode, .loop | |
977 | btbz CodeBlock::m_needsFullScopeChain[t1], .loop | |
978 | ||
979 | loadi CodeBlock::m_activationRegister[t1], t1 | |
980 | ||
981 | # Need to conditionally skip over one scope. | |
982 | bieq TagOffset[cfr, t1, 8], EmptyValueTag, .noActivation | |
983 | scopeCheck(t0, t1) | |
984 | loadp ScopeChainNode::next[t0], t0 | |
985 | .noActivation: | |
986 | subi 1, t2 | |
987 | ||
988 | btiz t2, .done | |
989 | .loop: | |
990 | scopeCheck(t0, t1) | |
991 | loadp ScopeChainNode::next[t0], t0 | |
992 | subi 1, t2 | |
993 | btinz t2, .loop | |
994 | ||
995 | .done: | |
996 | end | |
997 | ||
998 | _llint_op_resolve_global_dynamic: | |
999 | traceExecution() | |
1000 | loadp JITStackFrame::globalData[sp], t3 | |
1001 | loadp JSGlobalData::activationStructure[t3], t3 | |
1002 | getScope( | |
1003 | 20[PC], | |
1004 | macro (scope, scratch) | |
1005 | loadp ScopeChainNode::object[scope], scratch | |
1006 | bpneq JSCell::m_structure[scratch], t3, .opResolveGlobalDynamicSuperSlow | |
1007 | end) | |
1008 | resolveGlobal(7, .opResolveGlobalDynamicSlow) | |
1009 | dispatch(7) | |
1010 | ||
1011 | .opResolveGlobalDynamicSuperSlow: | |
1012 | callSlowPath(_llint_slow_path_resolve_for_resolve_global_dynamic) | |
1013 | dispatch(7) | |
1014 | ||
1015 | .opResolveGlobalDynamicSlow: | |
1016 | callSlowPath(_llint_slow_path_resolve_global_dynamic) | |
1017 | dispatch(7) | |
1018 | ||
1019 | ||
1020 | _llint_op_get_scoped_var: | |
1021 | traceExecution() | |
1022 | # Operands are as follows: | |
1023 | # 4[PC] Destination for the load. | |
1024 | # 8[PC] Index of register in the scope. | |
1025 | # 12[PC] De Bruijin index. | |
1026 | getScope(12[PC], macro (scope, scratch) end) | |
1027 | loadi 4[PC], t1 | |
1028 | loadi 8[PC], t2 | |
1029 | loadp ScopeChainNode::object[t0], t0 | |
1030 | loadp JSVariableObject::m_registers[t0], t0 | |
1031 | loadi TagOffset[t0, t2, 8], t3 | |
1032 | loadi PayloadOffset[t0, t2, 8], t0 | |
1033 | storei t3, TagOffset[cfr, t1, 8] | |
1034 | storei t0, PayloadOffset[cfr, t1, 8] | |
1035 | loadi 16[PC], t1 | |
1036 | valueProfile(t3, t0, t1) | |
1037 | dispatch(5) | |
1038 | ||
1039 | ||
1040 | _llint_op_put_scoped_var: | |
1041 | traceExecution() | |
1042 | getScope(8[PC], macro (scope, scratch) end) | |
1043 | loadi 12[PC], t1 | |
1044 | loadConstantOrVariable(t1, t3, t2) | |
1045 | loadi 4[PC], t1 | |
1046 | writeBarrier(t3, t2) | |
1047 | loadp ScopeChainNode::object[t0], t0 | |
1048 | loadp JSVariableObject::m_registers[t0], t0 | |
1049 | storei t3, TagOffset[t0, t1, 8] | |
1050 | storei t2, PayloadOffset[t0, t1, 8] | |
1051 | dispatch(4) | |
1052 | ||
1053 | ||
1054 | _llint_op_get_global_var: | |
1055 | traceExecution() | |
1056 | loadi 8[PC], t1 | |
1057 | loadi 4[PC], t3 | |
1058 | loadp CodeBlock[cfr], t0 | |
1059 | loadp CodeBlock::m_globalObject[t0], t0 | |
1060 | loadp JSGlobalObject::m_registers[t0], t0 | |
1061 | loadi TagOffset[t0, t1, 8], t2 | |
1062 | loadi PayloadOffset[t0, t1, 8], t1 | |
1063 | storei t2, TagOffset[cfr, t3, 8] | |
1064 | storei t1, PayloadOffset[cfr, t3, 8] | |
1065 | loadi 12[PC], t3 | |
1066 | valueProfile(t2, t1, t3) | |
1067 | dispatch(4) | |
1068 | ||
1069 | ||
1070 | _llint_op_put_global_var: | |
1071 | traceExecution() | |
1072 | loadi 8[PC], t1 | |
1073 | loadp CodeBlock[cfr], t0 | |
1074 | loadp CodeBlock::m_globalObject[t0], t0 | |
1075 | loadp JSGlobalObject::m_registers[t0], t0 | |
1076 | loadConstantOrVariable(t1, t2, t3) | |
1077 | loadi 4[PC], t1 | |
1078 | writeBarrier(t2, t3) | |
1079 | storei t2, TagOffset[t0, t1, 8] | |
1080 | storei t3, PayloadOffset[t0, t1, 8] | |
1081 | dispatch(3) | |
1082 | ||
1083 | ||
1084 | _llint_op_get_by_id: | |
1085 | traceExecution() | |
1086 | # We only do monomorphic get_by_id caching for now, and we do not modify the | |
1087 | # opcode. We do, however, allow for the cache to change anytime if fails, since | |
1088 | # ping-ponging is free. At best we get lucky and the get_by_id will continue | |
1089 | # to take fast path on the new cache. At worst we take slow path, which is what | |
1090 | # we would have been doing anyway. | |
1091 | loadi 8[PC], t0 | |
1092 | loadi 16[PC], t1 | |
1093 | loadConstantOrVariablePayload(t0, CellTag, t3, .opGetByIdSlow) | |
1094 | loadi 20[PC], t2 | |
1095 | loadp JSObject::m_propertyStorage[t3], t0 | |
1096 | bpneq JSCell::m_structure[t3], t1, .opGetByIdSlow | |
1097 | loadi 4[PC], t1 | |
1098 | loadi TagOffset[t0, t2], t3 | |
1099 | loadi PayloadOffset[t0, t2], t2 | |
1100 | storei t3, TagOffset[cfr, t1, 8] | |
1101 | storei t2, PayloadOffset[cfr, t1, 8] | |
1102 | loadi 32[PC], t1 | |
1103 | valueProfile(t3, t2, t1) | |
1104 | dispatch(9) | |
1105 | ||
1106 | .opGetByIdSlow: | |
1107 | callSlowPath(_llint_slow_path_get_by_id) | |
1108 | dispatch(9) | |
1109 | ||
1110 | ||
1111 | _llint_op_get_arguments_length: | |
1112 | traceExecution() | |
1113 | loadi 8[PC], t0 | |
1114 | loadi 4[PC], t1 | |
1115 | bineq TagOffset[cfr, t0, 8], EmptyValueTag, .opGetArgumentsLengthSlow | |
1116 | loadi ArgumentCount + PayloadOffset[cfr], t2 | |
1117 | subi 1, t2 | |
1118 | storei Int32Tag, TagOffset[cfr, t1, 8] | |
1119 | storei t2, PayloadOffset[cfr, t1, 8] | |
1120 | dispatch(4) | |
1121 | ||
1122 | .opGetArgumentsLengthSlow: | |
1123 | callSlowPath(_llint_slow_path_get_arguments_length) | |
1124 | dispatch(4) | |
1125 | ||
1126 | ||
1127 | _llint_op_put_by_id: | |
1128 | traceExecution() | |
1129 | loadi 4[PC], t3 | |
1130 | loadi 16[PC], t1 | |
1131 | loadConstantOrVariablePayload(t3, CellTag, t0, .opPutByIdSlow) | |
1132 | loadi 12[PC], t2 | |
1133 | loadp JSObject::m_propertyStorage[t0], t3 | |
1134 | bpneq JSCell::m_structure[t0], t1, .opPutByIdSlow | |
1135 | loadi 20[PC], t1 | |
1136 | loadConstantOrVariable2Reg(t2, t0, t2) | |
1137 | writeBarrier(t0, t2) | |
1138 | storei t0, TagOffset[t3, t1] | |
1139 | storei t2, PayloadOffset[t3, t1] | |
1140 | dispatch(9) | |
1141 | ||
1142 | .opPutByIdSlow: | |
1143 | callSlowPath(_llint_slow_path_put_by_id) | |
1144 | dispatch(9) | |
1145 | ||
1146 | ||
1147 | macro putByIdTransition(additionalChecks) | |
1148 | traceExecution() | |
1149 | loadi 4[PC], t3 | |
1150 | loadi 16[PC], t1 | |
1151 | loadConstantOrVariablePayload(t3, CellTag, t0, .opPutByIdSlow) | |
1152 | loadi 12[PC], t2 | |
1153 | bpneq JSCell::m_structure[t0], t1, .opPutByIdSlow | |
1154 | additionalChecks(t1, t3, .opPutByIdSlow) | |
1155 | loadi 20[PC], t1 | |
1156 | loadp JSObject::m_propertyStorage[t0], t3 | |
1157 | addp t1, t3 | |
1158 | loadConstantOrVariable2Reg(t2, t1, t2) | |
1159 | writeBarrier(t1, t2) | |
1160 | storei t1, TagOffset[t3] | |
1161 | loadi 24[PC], t1 | |
1162 | storei t2, PayloadOffset[t3] | |
1163 | storep t1, JSCell::m_structure[t0] | |
1164 | dispatch(9) | |
1165 | end | |
1166 | ||
1167 | _llint_op_put_by_id_transition_direct: | |
1168 | putByIdTransition(macro (oldStructure, scratch, slow) end) | |
1169 | ||
1170 | ||
1171 | _llint_op_put_by_id_transition_normal: | |
1172 | putByIdTransition( | |
1173 | macro (oldStructure, scratch, slow) | |
1174 | const protoCell = oldStructure # Reusing the oldStructure register for the proto | |
1175 | ||
1176 | loadp 28[PC], scratch | |
1177 | assert(macro (ok) btpnz scratch, ok end) | |
1178 | loadp StructureChain::m_vector[scratch], scratch | |
1179 | assert(macro (ok) btpnz scratch, ok end) | |
1180 | bieq Structure::m_prototype + TagOffset[oldStructure], NullTag, .done | |
1181 | .loop: | |
1182 | loadi Structure::m_prototype + PayloadOffset[oldStructure], protoCell | |
1183 | loadp JSCell::m_structure[protoCell], oldStructure | |
1184 | bpneq oldStructure, [scratch], slow | |
1185 | addp 4, scratch | |
1186 | bineq Structure::m_prototype + TagOffset[oldStructure], NullTag, .loop | |
1187 | .done: | |
1188 | end) | |
1189 | ||
1190 | ||
1191 | _llint_op_get_by_val: | |
1192 | traceExecution() | |
1193 | loadp CodeBlock[cfr], t1 | |
1194 | loadi 8[PC], t2 | |
1195 | loadi 12[PC], t3 | |
1196 | loadp CodeBlock::m_globalData[t1], t1 | |
1197 | loadConstantOrVariablePayload(t2, CellTag, t0, .opGetByValSlow) | |
1198 | loadp JSGlobalData::jsArrayClassInfo[t1], t2 | |
1199 | loadConstantOrVariablePayload(t3, Int32Tag, t1, .opGetByValSlow) | |
1200 | bpneq [t0], t2, .opGetByValSlow | |
1201 | loadp JSArray::m_storage[t0], t3 | |
1202 | biaeq t1, JSArray::m_vectorLength[t0], .opGetByValSlow | |
1203 | loadi 4[PC], t0 | |
1204 | loadi ArrayStorage::m_vector + TagOffset[t3, t1, 8], t2 | |
1205 | loadi ArrayStorage::m_vector + PayloadOffset[t3, t1, 8], t1 | |
1206 | bieq t2, EmptyValueTag, .opGetByValSlow | |
1207 | storei t2, TagOffset[cfr, t0, 8] | |
1208 | storei t1, PayloadOffset[cfr, t0, 8] | |
1209 | loadi 16[PC], t0 | |
1210 | valueProfile(t2, t1, t0) | |
1211 | dispatch(5) | |
1212 | ||
1213 | .opGetByValSlow: | |
1214 | callSlowPath(_llint_slow_path_get_by_val) | |
1215 | dispatch(5) | |
1216 | ||
1217 | ||
1218 | _llint_op_get_argument_by_val: | |
1219 | traceExecution() | |
1220 | loadi 8[PC], t0 | |
1221 | loadi 12[PC], t1 | |
1222 | bineq TagOffset[cfr, t0, 8], EmptyValueTag, .opGetArgumentByValSlow | |
1223 | loadConstantOrVariablePayload(t1, Int32Tag, t2, .opGetArgumentByValSlow) | |
1224 | addi 1, t2 | |
1225 | loadi ArgumentCount + PayloadOffset[cfr], t1 | |
1226 | biaeq t2, t1, .opGetArgumentByValSlow | |
1227 | negi t2 | |
1228 | loadi 4[PC], t3 | |
1229 | loadi ThisArgumentOffset + TagOffset[cfr, t2, 8], t0 | |
1230 | loadi ThisArgumentOffset + PayloadOffset[cfr, t2, 8], t1 | |
1231 | storei t0, TagOffset[cfr, t3, 8] | |
1232 | storei t1, PayloadOffset[cfr, t3, 8] | |
1233 | dispatch(5) | |
1234 | ||
1235 | .opGetArgumentByValSlow: | |
1236 | callSlowPath(_llint_slow_path_get_argument_by_val) | |
1237 | dispatch(5) | |
1238 | ||
1239 | ||
1240 | _llint_op_get_by_pname: | |
1241 | traceExecution() | |
1242 | loadi 12[PC], t0 | |
1243 | loadConstantOrVariablePayload(t0, CellTag, t1, .opGetByPnameSlow) | |
1244 | loadi 16[PC], t0 | |
1245 | bpneq t1, PayloadOffset[cfr, t0, 8], .opGetByPnameSlow | |
1246 | loadi 8[PC], t0 | |
1247 | loadConstantOrVariablePayload(t0, CellTag, t2, .opGetByPnameSlow) | |
1248 | loadi 20[PC], t0 | |
1249 | loadi PayloadOffset[cfr, t0, 8], t3 | |
1250 | loadp JSCell::m_structure[t2], t0 | |
1251 | bpneq t0, JSPropertyNameIterator::m_cachedStructure[t3], .opGetByPnameSlow | |
1252 | loadi 24[PC], t0 | |
1253 | loadi [cfr, t0, 8], t0 | |
1254 | subi 1, t0 | |
1255 | biaeq t0, JSPropertyNameIterator::m_numCacheableSlots[t3], .opGetByPnameSlow | |
1256 | loadp JSObject::m_propertyStorage[t2], t2 | |
1257 | loadi TagOffset[t2, t0, 8], t1 | |
1258 | loadi PayloadOffset[t2, t0, 8], t3 | |
1259 | loadi 4[PC], t0 | |
1260 | storei t1, TagOffset[cfr, t0, 8] | |
1261 | storei t3, PayloadOffset[cfr, t0, 8] | |
1262 | dispatch(7) | |
1263 | ||
1264 | .opGetByPnameSlow: | |
1265 | callSlowPath(_llint_slow_path_get_by_pname) | |
1266 | dispatch(7) | |
1267 | ||
1268 | ||
1269 | _llint_op_put_by_val: | |
1270 | traceExecution() | |
1271 | loadi 4[PC], t0 | |
1272 | loadConstantOrVariablePayload(t0, CellTag, t1, .opPutByValSlow) | |
1273 | loadi 8[PC], t0 | |
1274 | loadConstantOrVariablePayload(t0, Int32Tag, t2, .opPutByValSlow) | |
1275 | loadp CodeBlock[cfr], t0 | |
1276 | loadp CodeBlock::m_globalData[t0], t0 | |
1277 | loadp JSGlobalData::jsArrayClassInfo[t0], t0 | |
1278 | bpneq [t1], t0, .opPutByValSlow | |
1279 | biaeq t2, JSArray::m_vectorLength[t1], .opPutByValSlow | |
1280 | loadp JSArray::m_storage[t1], t0 | |
1281 | bieq ArrayStorage::m_vector + TagOffset[t0, t2, 8], EmptyValueTag, .opPutByValEmpty | |
1282 | .opPutByValStoreResult: | |
1283 | loadi 12[PC], t3 | |
1284 | loadConstantOrVariable2Reg(t3, t1, t3) | |
1285 | writeBarrier(t1, t3) | |
1286 | storei t1, ArrayStorage::m_vector + TagOffset[t0, t2, 8] | |
1287 | storei t3, ArrayStorage::m_vector + PayloadOffset[t0, t2, 8] | |
1288 | dispatch(4) | |
1289 | ||
1290 | .opPutByValEmpty: | |
1291 | addi 1, ArrayStorage::m_numValuesInVector[t0] | |
1292 | bib t2, ArrayStorage::m_length[t0], .opPutByValStoreResult | |
1293 | addi 1, t2, t1 | |
1294 | storei t1, ArrayStorage::m_length[t0] | |
1295 | jmp .opPutByValStoreResult | |
1296 | ||
1297 | .opPutByValSlow: | |
1298 | callSlowPath(_llint_slow_path_put_by_val) | |
1299 | dispatch(4) | |
1300 | ||
1301 | ||
1302 | _llint_op_loop: | |
1303 | jmp _llint_op_jmp | |
1304 | _llint_op_jmp: | |
1305 | traceExecution() | |
1306 | dispatchBranch(4[PC]) | |
1307 | ||
1308 | ||
1309 | macro jumpTrueOrFalse(conditionOp, slow) | |
1310 | loadi 4[PC], t1 | |
1311 | loadConstantOrVariablePayload(t1, BooleanTag, t0, .slow) | |
1312 | conditionOp(t0, .target) | |
1313 | dispatch(3) | |
1314 | ||
1315 | .target: | |
1316 | dispatchBranch(8[PC]) | |
1317 | ||
1318 | .slow: | |
1319 | callSlowPath(slow) | |
1320 | dispatch(0) | |
1321 | end | |
1322 | ||
1323 | ||
1324 | macro equalNull(cellHandler, immediateHandler) | |
1325 | loadi 4[PC], t0 | |
1326 | assertNotConstant(t0) | |
1327 | loadi TagOffset[cfr, t0, 8], t1 | |
1328 | loadi PayloadOffset[cfr, t0, 8], t0 | |
1329 | bineq t1, CellTag, .immediate | |
1330 | loadp JSCell::m_structure[t0], t2 | |
1331 | cellHandler(Structure::m_typeInfo + TypeInfo::m_flags[t2], .target) | |
1332 | dispatch(3) | |
1333 | ||
1334 | .target: | |
1335 | dispatchBranch(8[PC]) | |
1336 | ||
1337 | .immediate: | |
1338 | ori 1, t1 | |
1339 | immediateHandler(t1, .target) | |
1340 | dispatch(3) | |
1341 | end | |
1342 | ||
1343 | _llint_op_jeq_null: | |
1344 | traceExecution() | |
1345 | equalNull( | |
1346 | macro (value, target) btbnz value, MasqueradesAsUndefined, target end, | |
1347 | macro (value, target) bieq value, NullTag, target end) | |
1348 | ||
1349 | ||
1350 | _llint_op_jneq_null: | |
1351 | traceExecution() | |
1352 | equalNull( | |
1353 | macro (value, target) btbz value, MasqueradesAsUndefined, target end, | |
1354 | macro (value, target) bineq value, NullTag, target end) | |
1355 | ||
1356 | ||
1357 | _llint_op_jneq_ptr: | |
1358 | traceExecution() | |
1359 | loadi 4[PC], t0 | |
1360 | loadi 8[PC], t1 | |
1361 | bineq TagOffset[cfr, t0, 8], CellTag, .opJneqPtrBranch | |
1362 | bpeq PayloadOffset[cfr, t0, 8], t1, .opJneqPtrFallThrough | |
1363 | .opJneqPtrBranch: | |
1364 | dispatchBranch(12[PC]) | |
1365 | .opJneqPtrFallThrough: | |
1366 | dispatch(4) | |
1367 | ||
1368 | ||
1369 | macro compare(integerCompare, doubleCompare, slowPath) | |
1370 | loadi 4[PC], t2 | |
1371 | loadi 8[PC], t3 | |
1372 | loadConstantOrVariable(t2, t0, t1) | |
1373 | loadConstantOrVariable2Reg(t3, t2, t3) | |
1374 | bineq t0, Int32Tag, .op1NotInt | |
1375 | bineq t2, Int32Tag, .op2NotInt | |
1376 | integerCompare(t1, t3, .jumpTarget) | |
1377 | dispatch(4) | |
1378 | ||
1379 | .op1NotInt: | |
1380 | bia t0, LowestTag, .slow | |
1381 | bib t2, LowestTag, .op1NotIntOp2Double | |
1382 | bineq t2, Int32Tag, .slow | |
1383 | ci2d t3, ft1 | |
1384 | jmp .op1NotIntReady | |
1385 | .op1NotIntOp2Double: | |
1386 | fii2d t3, t2, ft1 | |
1387 | .op1NotIntReady: | |
1388 | fii2d t1, t0, ft0 | |
1389 | doubleCompare(ft0, ft1, .jumpTarget) | |
1390 | dispatch(4) | |
1391 | ||
1392 | .op2NotInt: | |
1393 | ci2d t1, ft0 | |
1394 | bia t2, LowestTag, .slow | |
1395 | fii2d t3, t2, ft1 | |
1396 | doubleCompare(ft0, ft1, .jumpTarget) | |
1397 | dispatch(4) | |
1398 | ||
1399 | .jumpTarget: | |
1400 | dispatchBranch(12[PC]) | |
1401 | ||
1402 | .slow: | |
1403 | callSlowPath(slowPath) | |
1404 | dispatch(0) | |
1405 | end | |
1406 | ||
1407 | ||
1408 | _llint_op_switch_imm: | |
1409 | traceExecution() | |
1410 | loadi 12[PC], t2 | |
1411 | loadi 4[PC], t3 | |
1412 | loadConstantOrVariable(t2, t1, t0) | |
1413 | loadp CodeBlock[cfr], t2 | |
1414 | loadp CodeBlock::m_rareData[t2], t2 | |
1415 | muli sizeof SimpleJumpTable, t3 # FIXME: would be nice to peephole this! | |
1416 | loadp CodeBlock::RareData::m_immediateSwitchJumpTables + VectorBufferOffset[t2], t2 | |
1417 | addp t3, t2 | |
1418 | bineq t1, Int32Tag, .opSwitchImmNotInt | |
1419 | subi SimpleJumpTable::min[t2], t0 | |
1420 | biaeq t0, SimpleJumpTable::branchOffsets + VectorSizeOffset[t2], .opSwitchImmFallThrough | |
1421 | loadp SimpleJumpTable::branchOffsets + VectorBufferOffset[t2], t3 | |
1422 | loadi [t3, t0, 4], t1 | |
1423 | btiz t1, .opSwitchImmFallThrough | |
1424 | dispatchBranchWithOffset(t1) | |
1425 | ||
1426 | .opSwitchImmNotInt: | |
1427 | bib t1, LowestTag, .opSwitchImmSlow # Go to slow path if it's a double. | |
1428 | .opSwitchImmFallThrough: | |
1429 | dispatchBranch(8[PC]) | |
1430 | ||
1431 | .opSwitchImmSlow: | |
1432 | callSlowPath(_llint_slow_path_switch_imm) | |
1433 | dispatch(0) | |
1434 | ||
1435 | ||
1436 | _llint_op_switch_char: | |
1437 | traceExecution() | |
1438 | loadi 12[PC], t2 | |
1439 | loadi 4[PC], t3 | |
1440 | loadConstantOrVariable(t2, t1, t0) | |
1441 | loadp CodeBlock[cfr], t2 | |
1442 | loadp CodeBlock::m_rareData[t2], t2 | |
1443 | muli sizeof SimpleJumpTable, t3 | |
1444 | loadp CodeBlock::RareData::m_characterSwitchJumpTables + VectorBufferOffset[t2], t2 | |
1445 | addp t3, t2 | |
1446 | bineq t1, CellTag, .opSwitchCharFallThrough | |
1447 | loadp JSCell::m_structure[t0], t1 | |
1448 | bbneq Structure::m_typeInfo + TypeInfo::m_type[t1], StringType, .opSwitchCharFallThrough | |
1449 | bineq JSString::m_length[t0], 1, .opSwitchCharFallThrough | |
1450 | loadp JSString::m_value[t0], t0 | |
1451 | btpz t0, .opSwitchOnRope | |
1452 | loadp StringImpl::m_data8[t0], t1 | |
1453 | btinz StringImpl::m_hashAndFlags[t0], HashFlags8BitBuffer, .opSwitchChar8Bit | |
1454 | loadh [t1], t0 | |
1455 | jmp .opSwitchCharReady | |
1456 | .opSwitchChar8Bit: | |
1457 | loadb [t1], t0 | |
1458 | .opSwitchCharReady: | |
1459 | subi SimpleJumpTable::min[t2], t0 | |
1460 | biaeq t0, SimpleJumpTable::branchOffsets + VectorSizeOffset[t2], .opSwitchCharFallThrough | |
1461 | loadp SimpleJumpTable::branchOffsets + VectorBufferOffset[t2], t2 | |
1462 | loadi [t2, t0, 4], t1 | |
1463 | btiz t1, .opSwitchCharFallThrough | |
1464 | dispatchBranchWithOffset(t1) | |
1465 | ||
1466 | .opSwitchCharFallThrough: | |
1467 | dispatchBranch(8[PC]) | |
1468 | ||
1469 | .opSwitchOnRope: | |
1470 | callSlowPath(_llint_slow_path_switch_char) | |
1471 | dispatch(0) | |
1472 | ||
1473 | ||
1474 | _llint_op_new_func: | |
1475 | traceExecution() | |
1476 | btiz 12[PC], .opNewFuncUnchecked | |
1477 | loadi 4[PC], t1 | |
1478 | bineq TagOffset[cfr, t1, 8], EmptyValueTag, .opNewFuncDone | |
1479 | .opNewFuncUnchecked: | |
1480 | callSlowPath(_llint_slow_path_new_func) | |
1481 | .opNewFuncDone: | |
1482 | dispatch(4) | |
1483 | ||
1484 | ||
1485 | macro doCall(slowPath) | |
1486 | loadi 4[PC], t0 | |
1487 | loadi 16[PC], t1 | |
1488 | loadp LLIntCallLinkInfo::callee[t1], t2 | |
1489 | loadConstantOrVariablePayload(t0, CellTag, t3, .opCallSlow) | |
1490 | bineq t3, t2, .opCallSlow | |
1491 | loadi 12[PC], t3 | |
1492 | addp 24, PC | |
1493 | lshifti 3, t3 | |
1494 | addp cfr, t3 # t3 contains the new value of cfr | |
1495 | loadp JSFunction::m_scopeChain[t2], t0 | |
1496 | storei t2, Callee + PayloadOffset[t3] | |
1497 | storei t0, ScopeChain + PayloadOffset[t3] | |
1498 | loadi 8 - 24[PC], t2 | |
1499 | storei PC, ArgumentCount + TagOffset[cfr] | |
1500 | storep cfr, CallerFrame[t3] | |
1501 | storei t2, ArgumentCount + PayloadOffset[t3] | |
1502 | storei CellTag, Callee + TagOffset[t3] | |
1503 | storei CellTag, ScopeChain + TagOffset[t3] | |
1504 | move t3, cfr | |
1505 | call LLIntCallLinkInfo::machineCodeTarget[t1] | |
1506 | dispatchAfterCall() | |
1507 | ||
1508 | .opCallSlow: | |
1509 | slowPathForCall(6, slowPath) | |
1510 | end | |
1511 | ||
1512 | ||
1513 | _llint_op_tear_off_activation: | |
1514 | traceExecution() | |
1515 | loadi 4[PC], t0 | |
1516 | loadi 8[PC], t1 | |
1517 | bineq TagOffset[cfr, t0, 8], EmptyValueTag, .opTearOffActivationCreated | |
1518 | bieq TagOffset[cfr, t1, 8], EmptyValueTag, .opTearOffActivationNotCreated | |
1519 | .opTearOffActivationCreated: | |
1520 | callSlowPath(_llint_slow_path_tear_off_activation) | |
1521 | .opTearOffActivationNotCreated: | |
1522 | dispatch(3) | |
1523 | ||
1524 | ||
1525 | _llint_op_tear_off_arguments: | |
1526 | traceExecution() | |
1527 | loadi 4[PC], t0 | |
1528 | subi 1, t0 # Get the unmodifiedArgumentsRegister | |
1529 | bieq TagOffset[cfr, t0, 8], EmptyValueTag, .opTearOffArgumentsNotCreated | |
1530 | callSlowPath(_llint_slow_path_tear_off_arguments) | |
1531 | .opTearOffArgumentsNotCreated: | |
1532 | dispatch(2) | |
1533 | ||
1534 | ||
1535 | _llint_op_ret: | |
1536 | traceExecution() | |
1537 | checkSwitchToJITForEpilogue() | |
1538 | loadi 4[PC], t2 | |
1539 | loadConstantOrVariable(t2, t1, t0) | |
1540 | doReturn() | |
1541 | ||
1542 | ||
1543 | _llint_op_call_put_result: | |
1544 | loadi 4[PC], t2 | |
1545 | loadi 8[PC], t3 | |
1546 | storei t1, TagOffset[cfr, t2, 8] | |
1547 | storei t0, PayloadOffset[cfr, t2, 8] | |
1548 | valueProfile(t1, t0, t3) | |
1549 | traceExecution() # Needs to be here because it would clobber t1, t0 | |
1550 | dispatch(3) | |
1551 | ||
1552 | ||
1553 | _llint_op_ret_object_or_this: | |
1554 | traceExecution() | |
1555 | checkSwitchToJITForEpilogue() | |
1556 | loadi 4[PC], t2 | |
1557 | loadConstantOrVariable(t2, t1, t0) | |
1558 | bineq t1, CellTag, .opRetObjectOrThisNotObject | |
1559 | loadp JSCell::m_structure[t0], t2 | |
1560 | bbb Structure::m_typeInfo + TypeInfo::m_type[t2], ObjectType, .opRetObjectOrThisNotObject | |
1561 | doReturn() | |
1562 | ||
1563 | .opRetObjectOrThisNotObject: | |
1564 | loadi 8[PC], t2 | |
1565 | loadConstantOrVariable(t2, t1, t0) | |
1566 | doReturn() | |
1567 | ||
1568 | ||
1569 | _llint_op_to_primitive: | |
1570 | traceExecution() | |
1571 | loadi 8[PC], t2 | |
1572 | loadi 4[PC], t3 | |
1573 | loadConstantOrVariable(t2, t1, t0) | |
1574 | bineq t1, CellTag, .opToPrimitiveIsImm | |
1575 | loadp JSCell::m_structure[t0], t2 | |
1576 | bbneq Structure::m_typeInfo + TypeInfo::m_type[t2], StringType, .opToPrimitiveSlowCase | |
1577 | .opToPrimitiveIsImm: | |
1578 | storei t1, TagOffset[cfr, t3, 8] | |
1579 | storei t0, PayloadOffset[cfr, t3, 8] | |
1580 | dispatch(3) | |
1581 | ||
1582 | .opToPrimitiveSlowCase: | |
1583 | callSlowPath(_llint_slow_path_to_primitive) | |
1584 | dispatch(3) | |
1585 | ||
1586 | ||
1587 | _llint_op_next_pname: | |
1588 | traceExecution() | |
1589 | loadi 12[PC], t1 | |
1590 | loadi 16[PC], t2 | |
1591 | loadi PayloadOffset[cfr, t1, 8], t0 | |
1592 | bieq t0, PayloadOffset[cfr, t2, 8], .opNextPnameEnd | |
1593 | loadi 20[PC], t2 | |
1594 | loadi PayloadOffset[cfr, t2, 8], t2 | |
1595 | loadp JSPropertyNameIterator::m_jsStrings[t2], t3 | |
1596 | loadi [t3, t0, 8], t3 | |
1597 | addi 1, t0 | |
1598 | storei t0, PayloadOffset[cfr, t1, 8] | |
1599 | loadi 4[PC], t1 | |
1600 | storei CellTag, TagOffset[cfr, t1, 8] | |
1601 | storei t3, PayloadOffset[cfr, t1, 8] | |
1602 | loadi 8[PC], t3 | |
1603 | loadi PayloadOffset[cfr, t3, 8], t3 | |
1604 | loadp JSCell::m_structure[t3], t1 | |
1605 | bpneq t1, JSPropertyNameIterator::m_cachedStructure[t2], .opNextPnameSlow | |
1606 | loadp JSPropertyNameIterator::m_cachedPrototypeChain[t2], t0 | |
1607 | loadp StructureChain::m_vector[t0], t0 | |
1608 | btpz [t0], .opNextPnameTarget | |
1609 | .opNextPnameCheckPrototypeLoop: | |
1610 | bieq Structure::m_prototype + TagOffset[t1], NullTag, .opNextPnameSlow | |
1611 | loadp Structure::m_prototype + PayloadOffset[t1], t2 | |
1612 | loadp JSCell::m_structure[t2], t1 | |
1613 | bpneq t1, [t0], .opNextPnameSlow | |
1614 | addp 4, t0 | |
1615 | btpnz [t0], .opNextPnameCheckPrototypeLoop | |
1616 | .opNextPnameTarget: | |
1617 | dispatchBranch(24[PC]) | |
1618 | ||
1619 | .opNextPnameEnd: | |
1620 | dispatch(7) | |
1621 | ||
1622 | .opNextPnameSlow: | |
1623 | callSlowPath(_llint_slow_path_next_pname) # This either keeps the PC where it was (causing us to loop) or sets it to target. | |
1624 | dispatch(0) | |
1625 | ||
1626 | ||
1627 | _llint_op_catch: | |
1628 | # This is where we end up from the JIT's throw trampoline (because the | |
1629 | # machine code return address will be set to _llint_op_catch), and from | |
1630 | # the interpreter's throw trampoline (see _llint_throw_trampoline). | |
1631 | # The JIT throwing protocol calls for the cfr to be in t0. The throwing | |
1632 | # code must have known that we were throwing to the interpreter, and have | |
1633 | # set JSGlobalData::targetInterpreterPCForThrow. | |
1634 | move t0, cfr | |
1635 | loadp JITStackFrame::globalData[sp], t3 | |
1636 | loadi JSGlobalData::targetInterpreterPCForThrow[t3], PC | |
1637 | loadi JSGlobalData::exception + PayloadOffset[t3], t0 | |
1638 | loadi JSGlobalData::exception + TagOffset[t3], t1 | |
1639 | storei 0, JSGlobalData::exception + PayloadOffset[t3] | |
1640 | storei EmptyValueTag, JSGlobalData::exception + TagOffset[t3] | |
1641 | loadi 4[PC], t2 | |
1642 | storei t0, PayloadOffset[cfr, t2, 8] | |
1643 | storei t1, TagOffset[cfr, t2, 8] | |
1644 | traceExecution() # This needs to be here because we don't want to clobber t0, t1, t2, t3 above. | |
1645 | dispatch(2) | |
1646 | ||
1647 | ||
1648 | _llint_op_end: | |
1649 | traceExecution() | |
1650 | checkSwitchToJITForEpilogue() | |
1651 | loadi 4[PC], t0 | |
1652 | assertNotConstant(t0) | |
1653 | loadi TagOffset[cfr, t0, 8], t1 | |
1654 | loadi PayloadOffset[cfr, t0, 8], t0 | |
1655 | doReturn() | |
1656 | ||
1657 | ||
1658 | _llint_throw_from_slow_path_trampoline: | |
1659 | # When throwing from the interpreter (i.e. throwing from LLIntSlowPaths), so | |
1660 | # the throw target is not necessarily interpreted code, we come to here. | |
1661 | # This essentially emulates the JIT's throwing protocol. | |
1662 | loadp JITStackFrame::globalData[sp], t1 | |
1663 | loadp JSGlobalData::callFrameForThrow[t1], t0 | |
1664 | jmp JSGlobalData::targetMachinePCForThrow[t1] | |
1665 | ||
1666 | ||
1667 | _llint_throw_during_call_trampoline: | |
1668 | preserveReturnAddressAfterCall(t2) | |
1669 | loadp JITStackFrame::globalData[sp], t1 | |
1670 | loadp JSGlobalData::callFrameForThrow[t1], t0 | |
1671 | jmp JSGlobalData::targetMachinePCForThrow[t1] | |
1672 | ||
1673 | ||
1674 | macro nativeCallTrampoline(executableOffsetToFunction) | |
1675 | storep 0, CodeBlock[cfr] | |
1676 | loadp CallerFrame[cfr], t0 | |
1677 | loadi ScopeChain + PayloadOffset[t0], t1 | |
1678 | storei CellTag, ScopeChain + TagOffset[cfr] | |
1679 | storei t1, ScopeChain + PayloadOffset[cfr] | |
1680 | if X86 | |
1681 | loadp JITStackFrame::globalData + 4[sp], t3 # Additional offset for return address | |
1682 | storep cfr, JSGlobalData::topCallFrame[t3] | |
1683 | peek 0, t1 | |
1684 | storep t1, ReturnPC[cfr] | |
1685 | move cfr, t2 # t2 = ecx | |
1686 | subp 16 - 4, sp | |
1687 | loadi Callee + PayloadOffset[cfr], t1 | |
1688 | loadp JSFunction::m_executable[t1], t1 | |
1689 | move t0, cfr | |
1690 | call executableOffsetToFunction[t1] | |
1691 | addp 16 - 4, sp | |
1692 | loadp JITStackFrame::globalData + 4[sp], t3 | |
1693 | elsif ARMv7 | |
1694 | loadp JITStackFrame::globalData[sp], t3 | |
1695 | storep cfr, JSGlobalData::topCallFrame[t3] | |
1696 | move t0, t2 | |
1697 | preserveReturnAddressAfterCall(t3) | |
1698 | storep t3, ReturnPC[cfr] | |
1699 | move cfr, t0 | |
1700 | loadi Callee + PayloadOffset[cfr], t1 | |
1701 | loadp JSFunction::m_executable[t1], t1 | |
1702 | move t2, cfr | |
1703 | call executableOffsetToFunction[t1] | |
1704 | restoreReturnAddressBeforeReturn(t3) | |
1705 | loadp JITStackFrame::globalData[sp], t3 | |
1706 | else | |
1707 | error | |
1708 | end | |
1709 | bineq JSGlobalData::exception + TagOffset[t3], EmptyValueTag, .exception | |
1710 | ret | |
1711 | .exception: | |
1712 | preserveReturnAddressAfterCall(t1) # This is really only needed on X86 | |
1713 | loadi ArgumentCount + TagOffset[cfr], PC | |
1714 | callSlowPath(_llint_throw_from_native_call) | |
1715 | jmp _llint_throw_from_slow_path_trampoline | |
1716 | end | |
1717 |