1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
24 * David Anderson <danderson@mozilla.com>
25 * David Mandelin <dmandelin@mozilla.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "jslibmath.h"
44 #include "jsobjinlines.h"
45 #include "methodjit/MethodJIT.h"
46 #include "methodjit/Compiler.h"
47 #include "methodjit/StubCalls.h"
48 #include "methodjit/FrameState-inl.h"
50 #include "jsautooplen.h"
53 using namespace js::mjit
;
55 typedef JSC::MacroAssembler::RegisterID RegisterID
;
58 mjit::Compiler::rightRegForShift(FrameEntry
*rhs
)
60 #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
62 * Gross: RHS _must_ be in ECX, on x86.
63 * Note that we take this first so that we can't up with other register
64 * allocations (below) owning ecx before rhs.
66 RegisterID reg
= JSC::X86Registers::ecx
;
67 if (!rhs
->isConstant())
68 frame
.copyDataIntoReg(rhs
, reg
);
71 if (rhs
->isConstant())
72 return frame
.allocReg();
73 return frame
.copyDataIntoReg(rhs
);
78 mjit::Compiler::jsop_rsh_const_int(FrameEntry
*lhs
, FrameEntry
*rhs
)
80 RegisterID rhsData
= rightRegForShift(rhs
);
81 RegisterID result
= frame
.allocReg();
82 masm
.move(Imm32(lhs
->getValue().toInt32()), result
);
83 masm
.rshift32(rhsData
, result
);
85 frame
.freeReg(rhsData
);
87 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, result
);
91 mjit::Compiler::jsop_rsh_int_int(FrameEntry
*lhs
, FrameEntry
*rhs
)
93 RegisterID rhsData
= rightRegForShift(rhs
);
94 RegisterID lhsData
= frame
.copyDataIntoReg(lhs
);
95 masm
.rshift32(rhsData
, lhsData
);
96 frame
.freeReg(rhsData
);
98 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, lhsData
);
102 mjit::Compiler::jsop_rsh_int_const(FrameEntry
*lhs
, FrameEntry
*rhs
)
104 int32 shiftAmount
= rhs
->getValue().toInt32();
111 RegisterID result
= frame
.copyDataIntoReg(lhs
);
112 masm
.rshift32(Imm32(shiftAmount
), result
);
114 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, result
);
118 mjit::Compiler::jsop_rsh_unknown_const(FrameEntry
*lhs
, FrameEntry
*rhs
)
120 int32 shiftAmount
= rhs
->getValue().toInt32();
122 RegisterID lhsType
= frame
.tempRegForType(lhs
);
123 frame
.pinReg(lhsType
);
124 RegisterID lhsData
= frame
.copyDataIntoReg(lhs
);
125 frame
.unpinReg(lhsType
);
127 Jump lhsIntGuard
= masm
.testInt32(Assembler::NotEqual
, lhsType
);
128 stubcc
.linkExitDirect(lhsIntGuard
, stubcc
.masm
.label());
130 Jump lhsDoubleGuard
= stubcc
.masm
.testDouble(Assembler::NotEqual
, lhsType
);
131 frame
.loadDouble(lhs
, FPRegisters::First
, stubcc
.masm
);
132 Jump lhsTruncateGuard
= stubcc
.masm
.branchTruncateDoubleToInt32(FPRegisters::First
, lhsData
);
133 stubcc
.crossJump(stubcc
.masm
.jump(), masm
.label());
135 lhsDoubleGuard
.linkTo(stubcc
.masm
.label(), &stubcc
.masm
);
136 lhsTruncateGuard
.linkTo(stubcc
.masm
.label(), &stubcc
.masm
);
138 frame
.sync(stubcc
.masm
, Uses(2));
139 OOL_STUBCALL(stubs::Rsh
);
142 masm
.rshift32(Imm32(shiftAmount
), lhsData
);
145 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, lhsData
);
147 stubcc
.rejoin(Changes(1));
151 mjit::Compiler::jsop_rsh_const_unknown(FrameEntry
*lhs
, FrameEntry
*rhs
)
153 RegisterID rhsData
= rightRegForShift(rhs
);
154 RegisterID rhsType
= frame
.tempRegForType(rhs
);
155 frame
.pinReg(rhsType
);
156 RegisterID result
= frame
.allocReg();
157 frame
.unpinReg(rhsType
);
159 Jump rhsIntGuard
= masm
.testInt32(Assembler::NotEqual
, rhsType
);
160 stubcc
.linkExit(rhsIntGuard
, Uses(2));
162 OOL_STUBCALL(stubs::Rsh
);
163 masm
.move(Imm32(lhs
->getValue().toInt32()), result
);
164 masm
.rshift32(rhsData
, result
);
165 frame
.freeReg(rhsData
);
168 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, result
);
169 stubcc
.rejoin(Changes(1));
173 mjit::Compiler::jsop_rsh_int_unknown(FrameEntry
*lhs
, FrameEntry
*rhs
)
175 RegisterID rhsData
= rightRegForShift(rhs
);
176 RegisterID rhsType
= frame
.tempRegForType(rhs
);
177 frame
.pinReg(rhsType
);
178 RegisterID lhsData
= frame
.copyDataIntoReg(lhs
);
179 frame
.unpinReg(rhsType
);
181 Jump rhsIntGuard
= masm
.testInt32(Assembler::NotEqual
, rhsType
);
182 stubcc
.linkExit(rhsIntGuard
, Uses(2));
184 OOL_STUBCALL(stubs::Rsh
);
186 masm
.rshift32(rhsData
, lhsData
);
187 frame
.freeReg(rhsData
);
189 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, lhsData
);
191 stubcc
.rejoin(Changes(1));
195 mjit::Compiler::jsop_rsh_unknown_any(FrameEntry
*lhs
, FrameEntry
*rhs
)
197 JS_ASSERT(!lhs
->isTypeKnown());
198 JS_ASSERT(!rhs
->isNotType(JSVAL_TYPE_INT32
));
200 /* Allocate registers. */
201 RegisterID rhsData
= rightRegForShift(rhs
);
203 MaybeRegisterID rhsType
;
204 if (!rhs
->isTypeKnown()) {
205 rhsType
.setReg(frame
.tempRegForType(rhs
));
206 frame
.pinReg(rhsType
.reg());
209 RegisterID lhsData
= frame
.copyDataIntoReg(lhs
);
210 MaybeRegisterID lhsType
;
211 if (rhsType
.isSet() && frame
.haveSameBacking(lhs
, rhs
))
214 lhsType
= frame
.tempRegForType(lhs
);
216 /* Non-integer rhs jumps to stub. */
217 MaybeJump rhsIntGuard
;
218 if (rhsType
.isSet()) {
219 rhsIntGuard
.setJump(masm
.testInt32(Assembler::NotEqual
, rhsType
.reg()));
220 frame
.unpinReg(rhsType
.reg());
223 /* Non-integer lhs jumps to double guard. */
224 Jump lhsIntGuard
= masm
.testInt32(Assembler::NotEqual
, lhsType
.reg());
225 stubcc
.linkExitDirect(lhsIntGuard
, stubcc
.masm
.label());
227 /* Attempt to convert lhs double to int32. */
228 Jump lhsDoubleGuard
= stubcc
.masm
.testDouble(Assembler::NotEqual
, lhsType
.reg());
229 frame
.loadDouble(lhs
, FPRegisters::First
, stubcc
.masm
);
230 Jump lhsTruncateGuard
= stubcc
.masm
.branchTruncateDoubleToInt32(FPRegisters::First
, lhsData
);
231 stubcc
.crossJump(stubcc
.masm
.jump(), masm
.label());
233 lhsDoubleGuard
.linkTo(stubcc
.masm
.label(), &stubcc
.masm
);
234 lhsTruncateGuard
.linkTo(stubcc
.masm
.label(), &stubcc
.masm
);
236 if (rhsIntGuard
.isSet())
237 stubcc
.linkExitDirect(rhsIntGuard
.getJump(), stubcc
.masm
.label());
238 frame
.sync(stubcc
.masm
, Uses(2));
239 OOL_STUBCALL(stubs::Rsh
);
241 masm
.rshift32(rhsData
, lhsData
);
243 frame
.freeReg(rhsData
);
245 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, lhsData
);
247 stubcc
.rejoin(Changes(1));
251 mjit::Compiler::jsop_rsh()
253 FrameEntry
*rhs
= frame
.peek(-1);
254 FrameEntry
*lhs
= frame
.peek(-2);
256 if (tryBinaryConstantFold(cx
, frame
, JSOP_RSH
, lhs
, rhs
))
259 if (lhs
->isNotType(JSVAL_TYPE_INT32
) || rhs
->isNotType(JSVAL_TYPE_INT32
)) {
260 prepareStubCall(Uses(2));
261 INLINE_STUBCALL(stubs::Rsh
);
263 frame
.pushSyncedType(JSVAL_TYPE_INT32
);
267 JS_ASSERT(!(lhs
->isConstant() && rhs
->isConstant()));
268 if (lhs
->isConstant()) {
269 if (rhs
->isType(JSVAL_TYPE_INT32
))
270 jsop_rsh_const_int(lhs
, rhs
);
272 jsop_rsh_const_unknown(lhs
, rhs
);
273 } else if (rhs
->isConstant()) {
274 if (lhs
->isType(JSVAL_TYPE_INT32
))
275 jsop_rsh_int_const(lhs
, rhs
);
277 jsop_rsh_unknown_const(lhs
, rhs
);
279 if (lhs
->isType(JSVAL_TYPE_INT32
) && rhs
->isType(JSVAL_TYPE_INT32
))
280 jsop_rsh_int_int(lhs
, rhs
);
281 else if (lhs
->isType(JSVAL_TYPE_INT32
))
282 jsop_rsh_int_unknown(lhs
, rhs
);
284 jsop_rsh_unknown_any(lhs
, rhs
);
289 mjit::Compiler::jsop_bitnot()
291 FrameEntry
*top
= frame
.peek(-1);
293 /* We only want to handle integers here. */
294 if (top
->isTypeKnown() && top
->getKnownType() != JSVAL_TYPE_INT32
) {
295 prepareStubCall(Uses(1));
296 INLINE_STUBCALL(stubs::BitNot
);
298 frame
.pushSyncedType(JSVAL_TYPE_INT32
);
303 bool stubNeeded
= false;
304 if (!top
->isTypeKnown()) {
305 Jump intFail
= frame
.testInt32(Assembler::NotEqual
, top
);
306 stubcc
.linkExit(intFail
, Uses(1));
307 frame
.learnType(top
, JSVAL_TYPE_INT32
);
313 OOL_STUBCALL(stubs::BitNot
);
316 RegisterID reg
= frame
.ownRegForData(top
);
319 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, reg
);
322 stubcc
.rejoin(Changes(1));
326 mjit::Compiler::jsop_bitop(JSOp op
)
328 FrameEntry
*rhs
= frame
.peek(-1);
329 FrameEntry
*lhs
= frame
.peek(-2);
337 stub
= stubs::BitAnd
;
340 stub
= stubs::BitXor
;
349 JS_NOT_REACHED("wat");
353 bool lhsIntOrDouble
= !(lhs
->isNotType(JSVAL_TYPE_DOUBLE
) &&
354 lhs
->isNotType(JSVAL_TYPE_INT32
));
356 /* Fast-path double to int conversion. */
357 if (!lhs
->isConstant() && rhs
->isConstant() && lhsIntOrDouble
&&
358 rhs
->isType(JSVAL_TYPE_INT32
) && rhs
->getValue().toInt32() == 0 &&
359 (op
== JSOP_BITOR
|| op
== JSOP_LSH
)) {
360 RegisterID reg
= frame
.copyDataIntoReg(lhs
);
361 if (lhs
->isType(JSVAL_TYPE_INT32
)) {
363 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, reg
);
367 if (!lhs
->isType(JSVAL_TYPE_DOUBLE
)) {
368 RegisterID typeReg
= frame
.tempRegForType(lhs
);
369 isInt
= masm
.testInt32(Assembler::Equal
, typeReg
);
370 Jump notDouble
= masm
.testDouble(Assembler::NotEqual
, typeReg
);
371 stubcc
.linkExit(notDouble
, Uses(2));
373 frame
.loadDouble(lhs
, FPRegisters::First
, masm
);
375 Jump truncateGuard
= masm
.branchTruncateDoubleToInt32(FPRegisters::First
, reg
);
376 stubcc
.linkExit(truncateGuard
, Uses(2));
381 isInt
.get().linkTo(masm
.label(), &masm
);
383 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, reg
);
384 stubcc
.rejoin(Changes(1));
388 /* We only want to handle integers here. */
389 if (rhs
->isNotType(JSVAL_TYPE_INT32
) || lhs
->isNotType(JSVAL_TYPE_INT32
) ||
390 (op
== JSOP_URSH
&& rhs
->isConstant() && rhs
->getValue().toInt32() % 32 == 0)) {
391 prepareStubCall(Uses(2));
392 INLINE_STUBCALL(stub
);
397 frame
.pushSyncedType(JSVAL_TYPE_INT32
);
401 /* Test the types. */
402 bool stubNeeded
= false;
403 if (!rhs
->isTypeKnown()) {
404 Jump rhsFail
= frame
.testInt32(Assembler::NotEqual
, rhs
);
405 stubcc
.linkExit(rhsFail
, Uses(2));
406 frame
.learnType(rhs
, JSVAL_TYPE_INT32
);
409 if (!lhs
->isTypeKnown() && !frame
.haveSameBacking(lhs
, rhs
)) {
410 Jump lhsFail
= frame
.testInt32(Assembler::NotEqual
, lhs
);
411 stubcc
.linkExit(lhsFail
, Uses(2));
415 if (lhs
->isConstant() && rhs
->isConstant()) {
416 int32 L
= lhs
->getValue().toInt32();
417 int32 R
= rhs
->getValue().toInt32();
422 frame
.push(Int32Value(L
| R
));
425 frame
.push(Int32Value(L
^ R
));
428 frame
.push(Int32Value(L
& R
));
431 frame
.push(Int32Value(L
<< R
));
436 if (ValueToECMAUint32(cx
, lhs
->getValue(), (uint32_t*)&unsignedL
)) {
437 frame
.push(NumberValue(uint32(unsignedL
>> (R
& 31))));
443 JS_NOT_REACHED("say wat");
454 /* Commutative, and we're guaranteed both are ints. */
455 if (lhs
->isConstant()) {
456 JS_ASSERT(!rhs
->isConstant());
457 FrameEntry
*temp
= rhs
;
462 reg
= frame
.ownRegForData(lhs
);
463 if (rhs
->isConstant()) {
464 if (op
== JSOP_BITAND
)
465 masm
.and32(Imm32(rhs
->getValue().toInt32()), reg
);
466 else if (op
== JSOP_BITXOR
)
467 masm
.xor32(Imm32(rhs
->getValue().toInt32()), reg
);
469 masm
.or32(Imm32(rhs
->getValue().toInt32()), reg
);
470 } else if (frame
.shouldAvoidDataRemat(rhs
)) {
471 if (op
== JSOP_BITAND
)
472 masm
.and32(masm
.payloadOf(frame
.addressOf(rhs
)), reg
);
473 else if (op
== JSOP_BITXOR
)
474 masm
.xor32(masm
.payloadOf(frame
.addressOf(rhs
)), reg
);
476 masm
.or32(masm
.payloadOf(frame
.addressOf(rhs
)), reg
);
478 RegisterID rhsReg
= frame
.tempRegForData(rhs
);
479 if (op
== JSOP_BITAND
)
480 masm
.and32(rhsReg
, reg
);
481 else if (op
== JSOP_BITXOR
)
482 masm
.xor32(rhsReg
, reg
);
484 masm
.or32(rhsReg
, reg
);
493 /* Not commutative. */
494 if (rhs
->isConstant()) {
495 RegisterID reg
= frame
.ownRegForData(lhs
);
496 int shift
= rhs
->getValue().toInt32() & 0x1F;
500 masm
.lshift32(Imm32(shift
), reg
);
502 masm
.urshift32(Imm32(shift
), reg
);
510 /* x >>> 0 may result in a double, handled above. */
511 JS_ASSERT_IF(op
== JSOP_URSH
, shift
>= 1);
512 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, reg
);
515 stubcc
.rejoin(Changes(1));
519 #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
520 /* Grosssssss! RHS _must_ be in ECX, on x86 */
521 RegisterID rr
= frame
.tempRegInMaskForData(rhs
,
522 Registers::maskReg(JSC::X86Registers::ecx
));
524 RegisterID rr
= frame
.tempRegForData(rhs
);
527 if (frame
.haveSameBacking(lhs
, rhs
)) {
528 // It's okay to allocReg(). If |rr| is evicted, it won't result in
529 // a load, and |rr == reg| is fine since this is (x << x).
530 reg
= frame
.allocReg();
535 if (lhs
->isConstant()) {
536 reg
= frame
.allocReg();
537 masm
.move(Imm32(lhs
->getValue().toInt32()), reg
);
539 reg
= frame
.copyDataIntoReg(lhs
);
544 if (op
== JSOP_LSH
) {
545 masm
.lshift32(rr
, reg
);
547 masm
.urshift32(rr
, reg
);
549 Jump isNegative
= masm
.branch32(Assembler::LessThan
, reg
, Imm32(0));
550 stubcc
.linkExit(isNegative
, Uses(2));
557 JS_NOT_REACHED("NYI");
570 frame
.pushNumber(reg
, true);
572 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, reg
);
575 stubcc
.rejoin(Changes(1));
579 mjit::Compiler::jsop_globalinc(JSOp op
, uint32 index
)
581 uint32 slot
= script
->getGlobalSlot(index
);
584 PC
+= JSOP_GLOBALINC_LENGTH
;
585 if (JSOp(*PC
) == JSOP_POP
&& !analysis
->jumpTarget(PC
)) {
587 PC
+= JSOP_POP_LENGTH
;
590 int amt
= (js_CodeSpec
[op
].format
& JOF_INC
) ? 1 : -1;
591 bool post
= !!(js_CodeSpec
[op
].format
& JOF_POST
);
594 RegisterID reg
= frame
.allocReg();
595 Address addr
= masm
.objSlotRef(globalObj
, reg
, slot
);
596 uint32 depth
= frame
.stackDepth();
598 if (post
&& !popped
) {
600 FrameEntry
*fe
= frame
.peek(-1);
601 Jump notInt
= frame
.testInt32(Assembler::NotEqual
, fe
);
602 stubcc
.linkExit(notInt
, Uses(0));
603 data
= frame
.copyDataIntoReg(fe
);
605 Jump notInt
= masm
.testInt32(Assembler::NotEqual
, addr
);
606 stubcc
.linkExit(notInt
, Uses(0));
607 data
= frame
.allocReg();
608 masm
.loadPayload(addr
, data
);
613 ovf
= masm
.branchAdd32(Assembler::Overflow
, Imm32(1), data
);
615 ovf
= masm
.branchSub32(Assembler::Overflow
, Imm32(1), data
);
616 stubcc
.linkExit(ovf
, Uses(0));
619 stubcc
.masm
.lea(addr
, Registers::ArgReg1
);
620 stubcc
.vpInc(op
, depth
);
622 #if defined JS_NUNBOX32
623 masm
.storePayload(data
, addr
);
624 #elif defined JS_PUNBOX64
625 masm
.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32
), data
, addr
);
628 if (!post
&& !popped
)
629 frame
.pushInt32(data
);
635 stubcc
.rejoin(Changes((!post
&& !popped
) ? 1 : 0));
639 CheckNullOrUndefined(FrameEntry
*fe
)
641 if (!fe
->isTypeKnown())
643 JSValueType type
= fe
->getKnownType();
644 return type
== JSVAL_TYPE_NULL
|| type
== JSVAL_TYPE_UNDEFINED
;
648 mjit::Compiler::jsop_equality(JSOp op
, BoolStub stub
, jsbytecode
*target
, JSOp fused
)
650 FrameEntry
*rhs
= frame
.peek(-1);
651 FrameEntry
*lhs
= frame
.peek(-2);
653 /* The compiler should have handled constant folding. */
654 JS_ASSERT(!(rhs
->isConstant() && lhs
->isConstant()));
657 if ((lhsTest
= CheckNullOrUndefined(lhs
)) || CheckNullOrUndefined(rhs
)) {
658 /* What's the other mask? */
659 FrameEntry
*test
= lhsTest
? rhs
: lhs
;
661 if (test
->isTypeKnown())
662 return emitStubCmpOp(stub
, target
, fused
);
664 /* The other side must be null or undefined. */
665 RegisterID reg
= frame
.ownRegForType(test
);
670 * :FIXME: Easier test for undefined || null?
671 * Maybe put them next to each other, subtract, do a single compare?
675 frame
.syncAndForgetEverything();
677 if ((op
== JSOP_EQ
&& fused
== JSOP_IFNE
) ||
678 (op
== JSOP_NE
&& fused
== JSOP_IFEQ
)) {
680 * It would be easier to just have two jumpAndTrace calls here, but since
681 * each jumpAndTrace creates a TRACE IC, and since we want the bytecode
682 * to have a reference to the TRACE IC at the top of the loop, it's much
683 * better to have only one TRACE IC per loop, and hence at most one
686 Jump b1
= masm
.branchPtr(Assembler::Equal
, reg
, ImmType(JSVAL_TYPE_UNDEFINED
));
687 Jump b2
= masm
.branchPtr(Assembler::Equal
, reg
, ImmType(JSVAL_TYPE_NULL
));
688 Jump j1
= masm
.jump();
689 b1
.linkTo(masm
.label(), &masm
);
690 b2
.linkTo(masm
.label(), &masm
);
691 Jump j2
= masm
.jump();
692 if (!jumpAndTrace(j2
, target
))
694 j1
.linkTo(masm
.label(), &masm
);
696 Jump j
= masm
.branchPtr(Assembler::Equal
, reg
, ImmType(JSVAL_TYPE_UNDEFINED
));
697 Jump j2
= masm
.branchPtr(Assembler::NotEqual
, reg
, ImmType(JSVAL_TYPE_NULL
));
698 if (!jumpAndTrace(j2
, target
))
700 j
.linkTo(masm
.label(), &masm
);
703 Jump j
= masm
.branchPtr(Assembler::Equal
, reg
, ImmType(JSVAL_TYPE_UNDEFINED
));
704 Jump j2
= masm
.branchPtr(Assembler::Equal
, reg
, ImmType(JSVAL_TYPE_NULL
));
705 masm
.move(Imm32(op
== JSOP_NE
), reg
);
706 Jump j3
= masm
.jump();
707 j2
.linkTo(masm
.label(), &masm
);
708 j
.linkTo(masm
.label(), &masm
);
709 masm
.move(Imm32(op
== JSOP_EQ
), reg
);
710 j3
.linkTo(masm
.label(), &masm
);
711 frame
.pushTypedPayload(JSVAL_TYPE_BOOLEAN
, reg
);
716 return emitStubCmpOp(stub
, target
, fused
);
720 mjit::Compiler::jsop_relational(JSOp op
, BoolStub stub
, jsbytecode
*target
, JSOp fused
)
722 FrameEntry
*rhs
= frame
.peek(-1);
723 FrameEntry
*lhs
= frame
.peek(-2);
725 /* The compiler should have handled constant folding. */
726 JS_ASSERT(!(rhs
->isConstant() && lhs
->isConstant()));
728 /* Always slow path... */
729 if ((lhs
->isNotType(JSVAL_TYPE_INT32
) && lhs
->isNotType(JSVAL_TYPE_DOUBLE
) &&
730 lhs
->isNotType(JSVAL_TYPE_STRING
)) ||
731 (rhs
->isNotType(JSVAL_TYPE_INT32
) && rhs
->isNotType(JSVAL_TYPE_DOUBLE
) &&
732 rhs
->isNotType(JSVAL_TYPE_STRING
))) {
733 if (op
== JSOP_EQ
|| op
== JSOP_NE
)
734 return jsop_equality(op
, stub
, target
, fused
);
735 return emitStubCmpOp(stub
, target
, fused
);
738 if (op
== JSOP_EQ
|| op
== JSOP_NE
) {
739 if ((lhs
->isNotType(JSVAL_TYPE_INT32
) && lhs
->isNotType(JSVAL_TYPE_STRING
)) ||
740 (rhs
->isNotType(JSVAL_TYPE_INT32
) && rhs
->isNotType(JSVAL_TYPE_STRING
))) {
741 return emitStubCmpOp(stub
, target
, fused
);
742 } else if (!target
&& (lhs
->isType(JSVAL_TYPE_STRING
) || rhs
->isType(JSVAL_TYPE_STRING
))) {
743 return emitStubCmpOp(stub
, target
, fused
);
744 } else if (frame
.haveSameBacking(lhs
, rhs
)) {
745 return emitStubCmpOp(stub
, target
, fused
);
747 return jsop_equality_int_string(op
, stub
, target
, fused
);
751 if (frame
.haveSameBacking(lhs
, rhs
)) {
752 return jsop_relational_self(op
, stub
, target
, fused
);
753 } else if (lhs
->isType(JSVAL_TYPE_STRING
) || rhs
->isType(JSVAL_TYPE_STRING
)) {
754 return emitStubCmpOp(stub
, target
, fused
);
755 } else if (lhs
->isType(JSVAL_TYPE_DOUBLE
) || rhs
->isType(JSVAL_TYPE_DOUBLE
)) {
756 return jsop_relational_double(op
, stub
, target
, fused
);
758 return jsop_relational_full(op
, stub
, target
, fused
);
763 mjit::Compiler::jsop_not()
765 FrameEntry
*top
= frame
.peek(-1);
767 if (top
->isConstant()) {
768 const Value
&v
= top
->getValue();
770 frame
.push(BooleanValue(!js_ValueToBoolean(v
)));
774 if (top
->isTypeKnown()) {
775 JSValueType type
= top
->getKnownType();
777 case JSVAL_TYPE_INT32
:
779 RegisterID data
= frame
.allocReg(Registers::SingleByteRegs
);
780 if (frame
.shouldAvoidDataRemat(top
))
781 masm
.loadPayload(frame
.addressOf(top
), data
);
783 masm
.move(frame
.tempRegForData(top
), data
);
785 masm
.set32(Assembler::Equal
, data
, Imm32(0), data
);
788 frame
.pushTypedPayload(JSVAL_TYPE_BOOLEAN
, data
);
792 case JSVAL_TYPE_BOOLEAN
:
794 RegisterID reg
= frame
.ownRegForData(top
);
796 masm
.xor32(Imm32(1), reg
);
799 frame
.pushTypedPayload(JSVAL_TYPE_BOOLEAN
, reg
);
803 case JSVAL_TYPE_OBJECT
:
806 frame
.push(BooleanValue(false));
812 prepareStubCall(Uses(1));
813 INLINE_STUBCALL(stubs::ValueToBoolean
);
815 RegisterID reg
= Registers::ReturnReg
;
817 masm
.xor32(Imm32(1), reg
);
820 frame
.pushTypedPayload(JSVAL_TYPE_BOOLEAN
, reg
);
828 RegisterID data
= frame
.allocReg(Registers::SingleByteRegs
);
829 if (frame
.shouldAvoidDataRemat(top
))
830 masm
.loadPayload(frame
.addressOf(top
), data
);
832 masm
.move(frame
.tempRegForData(top
), data
);
833 RegisterID type
= frame
.tempRegForType(top
);
834 Label syncTarget
= stubcc
.syncExitAndJump(Uses(1));
837 /* Inline path is for booleans. */
838 Jump jmpNotBool
= masm
.testBoolean(Assembler::NotEqual
, type
);
839 masm
.xor32(Imm32(1), data
);
842 /* OOL path is for int + object. */
843 Label lblMaybeInt32
= stubcc
.masm
.label();
845 Jump jmpNotInt32
= stubcc
.masm
.testInt32(Assembler::NotEqual
, type
);
846 stubcc
.masm
.set32(Assembler::Equal
, data
, Imm32(0), data
);
847 Jump jmpInt32Exit
= stubcc
.masm
.jump();
849 Label lblMaybeObject
= stubcc
.masm
.label();
850 Jump jmpNotObject
= stubcc
.masm
.testPrimitive(Assembler::Equal
, type
);
851 stubcc
.masm
.move(Imm32(0), data
);
852 Jump jmpObjectExit
= stubcc
.masm
.jump();
855 /* Rejoin location. */
856 Label lblRejoin
= masm
.label();
858 /* Patch up jumps. */
859 stubcc
.linkExitDirect(jmpNotBool
, lblMaybeInt32
);
861 jmpNotInt32
.linkTo(lblMaybeObject
, &stubcc
.masm
);
862 stubcc
.crossJump(jmpInt32Exit
, lblRejoin
);
864 jmpNotObject
.linkTo(syncTarget
, &stubcc
.masm
);
865 stubcc
.crossJump(jmpObjectExit
, lblRejoin
);
870 OOL_STUBCALL(stubs::Not
);
873 frame
.pushTypedPayload(JSVAL_TYPE_BOOLEAN
, data
);
875 stubcc
.rejoin(Changes(1));
879 mjit::Compiler::jsop_typeof()
881 FrameEntry
*fe
= frame
.peek(-1);
883 if (fe
->isTypeKnown()) {
884 JSRuntime
*rt
= cx
->runtime
;
887 switch (fe
->getKnownType()) {
888 case JSVAL_TYPE_STRING
:
889 atom
= rt
->atomState
.typeAtoms
[JSTYPE_STRING
];
891 case JSVAL_TYPE_UNDEFINED
:
892 atom
= rt
->atomState
.typeAtoms
[JSTYPE_VOID
];
894 case JSVAL_TYPE_NULL
:
895 atom
= rt
->atomState
.typeAtoms
[JSTYPE_OBJECT
];
897 case JSVAL_TYPE_OBJECT
:
900 case JSVAL_TYPE_BOOLEAN
:
901 atom
= rt
->atomState
.typeAtoms
[JSTYPE_BOOLEAN
];
904 atom
= rt
->atomState
.typeAtoms
[JSTYPE_NUMBER
];
910 frame
.push(StringValue(ATOM_TO_STRING(atom
)));
915 prepareStubCall(Uses(1));
916 INLINE_STUBCALL(stubs::TypeOf
);
918 frame
.takeReg(Registers::ReturnReg
);
919 frame
.pushTypedPayload(JSVAL_TYPE_STRING
, Registers::ReturnReg
);
923 mjit::Compiler::booleanJumpScript(JSOp op
, jsbytecode
*target
)
925 FrameEntry
*fe
= frame
.peek(-1);
927 MaybeRegisterID type
;
928 MaybeRegisterID data
;
930 if (!fe
->isTypeKnown() && !frame
.shouldAvoidTypeRemat(fe
))
931 type
.setReg(frame
.copyTypeIntoReg(fe
));
932 data
.setReg(frame
.copyDataIntoReg(fe
));
934 frame
.syncAndForgetEverything();
936 Assembler::Condition cond
= (op
== JSOP_IFNE
|| op
== JSOP_OR
)
939 Assembler::Condition ncond
= (op
== JSOP_IFNE
|| op
== JSOP_OR
)
941 : Assembler::NonZero
;
943 /* Inline path: Boolean guard + call script. */
944 MaybeJump jmpNotBool
;
945 MaybeJump jmpNotExecScript
;
947 jmpNotBool
.setJump(masm
.testBoolean(Assembler::NotEqual
, type
.reg()));
949 if (!fe
->isTypeKnown()) {
950 jmpNotBool
.setJump(masm
.testBoolean(Assembler::NotEqual
,
951 frame
.addressOf(fe
)));
952 } else if (fe
->isNotType(JSVAL_TYPE_BOOLEAN
) &&
953 fe
->isNotType(JSVAL_TYPE_INT32
)) {
954 jmpNotBool
.setJump(masm
.jump());
959 * TODO: We don't need the second jump if
960 * jumpInScript() can go from ool path to inline path.
962 jmpNotExecScript
.setJump(masm
.branchTest32(ncond
, data
.reg(), data
.reg()));
963 Label lblExecScript
= masm
.label();
964 Jump j
= masm
.jump();
967 /* OOL path: Conversion to boolean. */
968 MaybeJump jmpCvtExecScript
;
969 MaybeJump jmpCvtRejoin
;
970 Label lblCvtPath
= stubcc
.masm
.label();
972 if (!fe
->isTypeKnown() ||
973 !(fe
->isType(JSVAL_TYPE_BOOLEAN
) || fe
->isType(JSVAL_TYPE_INT32
))) {
974 stubcc
.masm
.infallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, stubs::ValueToBoolean
),
977 jmpCvtExecScript
.setJump(stubcc
.masm
.branchTest32(cond
, Registers::ReturnReg
,
978 Registers::ReturnReg
));
979 jmpCvtRejoin
.setJump(stubcc
.masm
.jump());
983 Label lblAfterScript
= masm
.label();
985 /* Patch up jumps. */
986 if (jmpNotBool
.isSet())
987 stubcc
.linkExitDirect(jmpNotBool
.getJump(), lblCvtPath
);
988 if (jmpNotExecScript
.isSet())
989 jmpNotExecScript
.getJump().linkTo(lblAfterScript
, &masm
);
991 if (jmpCvtExecScript
.isSet())
992 stubcc
.crossJump(jmpCvtExecScript
.getJump(), lblExecScript
);
993 if (jmpCvtRejoin
.isSet())
994 stubcc
.crossJump(jmpCvtRejoin
.getJump(), lblAfterScript
);
998 return jumpAndTrace(j
, target
);
1002 mjit::Compiler::jsop_ifneq(JSOp op
, jsbytecode
*target
)
1004 FrameEntry
*fe
= frame
.peek(-1);
1006 if (fe
->isConstant()) {
1007 JSBool b
= js_ValueToBoolean(fe
->getValue());
1011 if (op
== JSOP_IFEQ
)
1014 frame
.syncAndForgetEverything();
1015 if (!jumpAndTrace(masm
.jump(), target
))
1021 return booleanJumpScript(op
, target
);
1025 mjit::Compiler::jsop_andor(JSOp op
, jsbytecode
*target
)
1027 FrameEntry
*fe
= frame
.peek(-1);
1029 if (fe
->isConstant()) {
1030 JSBool b
= js_ValueToBoolean(fe
->getValue());
1032 /* Short-circuit. */
1033 if ((op
== JSOP_OR
&& b
== JS_TRUE
) ||
1034 (op
== JSOP_AND
&& b
== JS_FALSE
)) {
1035 frame
.syncAndForgetEverything();
1036 if (!jumpAndTrace(masm
.jump(), target
))
1044 return booleanJumpScript(op
, target
);
1048 mjit::Compiler::jsop_localinc(JSOp op
, uint32 slot
, bool popped
)
1050 bool post
= (op
== JSOP_LOCALINC
|| op
== JSOP_LOCALDEC
);
1051 int32 amt
= (op
== JSOP_INCLOCAL
|| op
== JSOP_LOCALINC
) ? 1 : -1;
1053 frame
.pushLocal(slot
);
1055 FrameEntry
*fe
= frame
.peek(-1);
1057 if (fe
->isConstant() && fe
->getValue().isPrimitive()) {
1058 Value v
= fe
->getValue();
1060 ValueToNumber(cx
, v
, &d
);
1062 frame
.push(NumberValue(d
+ amt
));
1063 frame
.storeLocal(slot
);
1067 frame
.push(NumberValue(d
+ amt
));
1068 frame
.storeLocal(slot
);
1076 * If the local variable is not known to be an int32, or the pre-value
1077 * is observed, then do the simple thing and decompose x++ into simpler
1080 if (fe
->isNotType(JSVAL_TYPE_INT32
) || (post
&& !popped
)) {
1085 if (post
&& !popped
) {
1090 frame
.push(Int32Value(1));
1094 jsop_binary(JSOP_ADD
, stubs::Add
);
1096 jsop_binary(JSOP_SUB
, stubs::Sub
);
1099 frame
.storeLocal(slot
, post
|| popped
);
1108 /* If the pre value is not observed, we can emit better code. */
1109 if (!fe
->isTypeKnown()) {
1110 Jump intFail
= frame
.testInt32(Assembler::NotEqual
, fe
);
1111 stubcc
.linkExit(intFail
, Uses(1));
1114 RegisterID reg
= frame
.copyDataIntoReg(fe
);
1118 ovf
= masm
.branchAdd32(Assembler::Overflow
, Imm32(1), reg
);
1120 ovf
= masm
.branchSub32(Assembler::Overflow
, Imm32(1), reg
);
1121 stubcc
.linkExit(ovf
, Uses(1));
1123 /* Note, stub call will push the original value again no matter what. */
1126 stubcc
.masm
.move(Imm32(slot
), Registers::ArgReg1
);
1127 if (op
== JSOP_LOCALINC
|| op
== JSOP_INCLOCAL
)
1128 OOL_STUBCALL(stubs::IncLocal
);
1130 OOL_STUBCALL(stubs::DecLocal
);
1133 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, reg
);
1134 frame
.storeLocal(slot
, popped
, false);
1139 frame
.forgetType(frame
.peek(-1));
1141 stubcc
.rejoin(Changes(0));
1145 mjit::Compiler::jsop_arginc(JSOp op
, uint32 slot
, bool popped
)
1147 int amt
= (js_CodeSpec
[op
].format
& JOF_INC
) ? 1 : -1;
1148 bool post
= !!(js_CodeSpec
[op
].format
& JOF_POST
);
1149 uint32 depth
= frame
.stackDepth();
1152 if (post
&& !popped
)
1155 FrameEntry
*fe
= frame
.peek(-1);
1156 Jump notInt
= frame
.testInt32(Assembler::NotEqual
, fe
);
1157 stubcc
.linkExit(notInt
, Uses(0));
1159 RegisterID reg
= frame
.ownRegForData(fe
);
1164 ovf
= masm
.branchAdd32(Assembler::Overflow
, Imm32(1), reg
);
1166 ovf
= masm
.branchSub32(Assembler::Overflow
, Imm32(1), reg
);
1167 stubcc
.linkExit(ovf
, Uses(0));
1170 stubcc
.masm
.addPtr(Imm32(JSStackFrame::offsetOfFormalArg(fun
, slot
)),
1171 JSFrameReg
, Registers::ArgReg1
);
1172 stubcc
.vpInc(op
, depth
);
1174 frame
.pushTypedPayload(JSVAL_TYPE_INT32
, reg
);
1175 fe
= frame
.peek(-1);
1177 Address address
= Address(JSFrameReg
, JSStackFrame::offsetOfFormalArg(fun
, slot
));
1178 frame
.storeTo(fe
, address
, popped
);
1183 frame
.forgetType(fe
);
1185 stubcc
.rejoin(Changes((post
|| popped
) ? 0 : 1));
1189 IsCacheableSetElem(FrameEntry
*obj
, FrameEntry
*id
, FrameEntry
*value
)
1191 if (obj
->isNotType(JSVAL_TYPE_OBJECT
))
1193 if (id
->isNotType(JSVAL_TYPE_INT32
))
1195 if (id
->isConstant() && id
->getValue().toInt32() < 0)
1198 // obj[obj] * is not allowed, since it will never optimize.
1199 // obj[id] = id is allowed.
1200 // obj[id] = obj is allowed.
1201 if (obj
->hasSameBacking(id
))
1208 mjit::Compiler::jsop_setelem()
1210 FrameEntry
*obj
= frame
.peek(-3);
1211 FrameEntry
*id
= frame
.peek(-2);
1212 FrameEntry
*value
= frame
.peek(-1);
1214 if (!IsCacheableSetElem(obj
, id
, value
)) {
1215 jsop_setelem_slow();
1219 SetElementICInfo ic
= SetElementICInfo(JSOp(*PC
));
1221 // One by one, check if the most important stack entries have registers,
1222 // and if so, pin them. This is to avoid spilling and reloading from the
1223 // stack as we incrementally allocate other registers.
1224 MaybeRegisterID pinnedValueType
= frame
.maybePinType(value
);
1225 MaybeRegisterID pinnedValueData
= frame
.maybePinData(value
);
1227 // Pin |obj| if it doesn't share a backing with |value|.
1228 MaybeRegisterID pinnedObjData
;
1229 if (!obj
->hasSameBacking(value
))
1230 pinnedObjData
= frame
.maybePinData(obj
);
1232 // Pin |id| if it doesn't share a backing with |value|.
1233 MaybeRegisterID pinnedIdData
;
1234 if (!id
->hasSameBacking(value
))
1235 pinnedIdData
= frame
.maybePinData(id
);
1237 // Note: The fact that |obj| and |value|, or |id| and |value| can be
1238 // copies, is a little complicated, but it is safe. Explanations
1239 // follow at each point. Keep in mind two points:
1240 // 1) maybePin() never allocates a register, it only pins if a register
1242 // 2) tempRegForData() will work fine on a pinned register.
1244 // Guard that the object is an object.
1245 if (!obj
->isTypeKnown()) {
1246 Jump j
= frame
.testObject(Assembler::NotEqual
, obj
);
1247 stubcc
.linkExit(j
, Uses(3));
1250 // Guard that the id is int32.
1251 if (!id
->isTypeKnown()) {
1252 Jump j
= frame
.testInt32(Assembler::NotEqual
, id
);
1253 stubcc
.linkExit(j
, Uses(3));
1256 // Grab a register for the object. It's safe to unpin |obj| because it
1257 // won't have been pinned if it shares a backing with |value|. However,
1258 // it would not be safe to copyDataIntoReg() if the value was pinned,
1259 // since this could evict the register. So we special case.
1260 frame
.maybeUnpinReg(pinnedObjData
);
1261 if (obj
->hasSameBacking(value
) && pinnedValueData
.isSet()) {
1262 ic
.objReg
= frame
.allocReg();
1263 masm
.move(pinnedValueData
.reg(), ic
.objReg
);
1265 ic
.objReg
= frame
.copyDataIntoReg(obj
);
1268 // pinEntry() will ensure pinned registers for |value|. To avoid a
1269 // double-pin assert, first unpin any registers that |value| had.
1270 frame
.maybeUnpinReg(pinnedValueType
);
1271 frame
.maybeUnpinReg(pinnedValueData
);
1272 frame
.pinEntry(value
, ic
.vr
);
1274 // Store rematerialization information about the key. This is the final
1275 // register we allocate, and thus it can use tempRegForData() without
1276 // the worry of being spilled. Once again, this is safe even if |id|
1277 // shares a backing with |value|, because tempRegForData() will work on
1278 // the pinned register, and |pinnedIdData| will not double-pin.
1279 frame
.maybeUnpinReg(pinnedIdData
);
1280 if (id
->isConstant())
1281 ic
.key
= Int32Key::FromConstant(id
->getValue().toInt32());
1283 ic
.key
= Int32Key::FromRegister(frame
.tempRegForData(id
));
1285 // Unpin the value since register allocation is complete.
1286 frame
.unpinEntry(ic
.vr
);
1288 // Now it's also safe to grab remat info for obj (all exits that can
1289 // generate stubs must have the same register state).
1290 ic
.objRemat
= frame
.dataRematInfo(obj
);
1292 // All patchable guards must occur after this point.
1293 ic
.fastPathStart
= masm
.label();
1295 // Create the common out-of-line sync block, taking care to link previous
1296 // guards here after.
1297 ic
.slowPathStart
= stubcc
.syncExit(Uses(3));
1299 // Guard obj is a dense array.
1300 ic
.claspGuard
= masm
.testObjClass(Assembler::NotEqual
, ic
.objReg
, &js_ArrayClass
);
1301 stubcc
.linkExitDirect(ic
.claspGuard
, ic
.slowPathStart
);
1303 // Guard capacity in range.
1304 Jump capacityGuard
= masm
.guardArrayCapacity(ic
.objReg
, ic
.key
);
1305 stubcc
.linkExitDirect(capacityGuard
, ic
.slowPathStart
);
1307 // Load the dynamic slots vector.
1308 masm
.loadPtr(Address(ic
.objReg
, offsetof(JSObject
, slots
)), ic
.objReg
);
1310 // Guard there's no hole, then store directly to the slot.
1311 if (ic
.key
.isConstant()) {
1312 Address
slot(ic
.objReg
, ic
.key
.index() * sizeof(Value
));
1313 ic
.holeGuard
= masm
.guardNotHole(slot
);
1314 masm
.storeValue(ic
.vr
, slot
);
1316 BaseIndex
slot(ic
.objReg
, ic
.key
.reg(), Assembler::JSVAL_SCALE
);
1317 ic
.holeGuard
= masm
.guardNotHole(slot
);
1318 masm
.storeValue(ic
.vr
, slot
);
1320 stubcc
.linkExitDirect(ic
.holeGuard
, ic
.slowPathStart
);
1325 ic
.slowPathCall
= OOL_STUBCALL(STRICT_VARIANT(ic::SetElement
));
1327 OOL_STUBCALL(STRICT_VARIANT(stubs::SetElem
));
1330 ic
.fastPathRejoin
= masm
.label();
1332 frame
.freeReg(ic
.objReg
);
1334 stubcc
.rejoin(Changes(2));
1337 if (!setElemICs
.append(ic
))
1345 IsCacheableGetElem(FrameEntry
*obj
, FrameEntry
*id
)
1347 if (obj
->isTypeKnown() && obj
->getKnownType() != JSVAL_TYPE_OBJECT
)
1349 if (id
->isTypeKnown() &&
1350 !(id
->getKnownType() == JSVAL_TYPE_INT32
1352 || id
->getKnownType() == JSVAL_TYPE_STRING
1358 if (id
->isTypeKnown() && id
->getKnownType() == JSVAL_TYPE_INT32
&& id
->isConstant() &&
1359 id
->getValue().toInt32() < 0) {
1363 // obj[obj] is not allowed, since it will never optimize.
1364 if (obj
->hasSameBacking(id
))
1371 mjit::Compiler::jsop_getelem(bool isCall
)
1373 FrameEntry
*obj
= frame
.peek(-2);
1374 FrameEntry
*id
= frame
.peek(-1);
1376 if (!IsCacheableGetElem(obj
, id
)) {
1378 jsop_callelem_slow();
1380 jsop_getelem_slow();
1384 GetElementICInfo ic
= GetElementICInfo(JSOp(*PC
));
1386 // Pin the top of the stack to avoid spills, before allocating registers.
1387 MaybeRegisterID pinnedIdData
= frame
.maybePinData(id
);
1388 MaybeRegisterID pinnedIdType
= frame
.maybePinType(id
);
1390 MaybeJump objTypeGuard
;
1391 if (!obj
->isTypeKnown()) {
1392 // Test the type of the object without spilling the payload.
1393 MaybeRegisterID pinnedObjData
= frame
.maybePinData(obj
);
1394 Jump guard
= frame
.testObject(Assembler::NotEqual
, obj
);
1395 frame
.maybeUnpinReg(pinnedObjData
);
1397 // Create a sync path, which we'll rejoin manually later. This is safe
1398 // as long as the IC does not build a stub; it won't, because |obj|
1399 // won't be an object. If we extend this IC to support strings, all
1400 // that needs to change is a little code movement.
1401 stubcc
.linkExit(guard
, Uses(2));
1402 objTypeGuard
= stubcc
.masm
.jump();
1405 // Get a mutable register for the object. This will be the data reg.
1406 ic
.objReg
= frame
.copyDataIntoReg(obj
);
1408 // For potential dense array calls, grab an extra reg to save the
1410 MaybeRegisterID thisReg
;
1411 if (isCall
&& id
->mightBeType(JSVAL_TYPE_INT32
)) {
1412 thisReg
= frame
.allocReg();
1413 masm
.move(ic
.objReg
, thisReg
.reg());
1416 // Get a mutable register for pushing the result type. We kill two birds
1417 // with one stone by making sure, if the key type is not known, to be loaded
1418 // into this register. In this case it is both an input and an output.
1419 frame
.maybeUnpinReg(pinnedIdType
);
1420 if (id
->isConstant() || id
->isTypeKnown())
1421 ic
.typeReg
= frame
.allocReg();
1423 ic
.typeReg
= frame
.copyTypeIntoReg(id
);
1425 // Fill in the id value.
1426 frame
.maybeUnpinReg(pinnedIdData
);
1427 if (id
->isConstant()) {
1428 ic
.id
= ValueRemat::FromConstant(id
->getValue());
1430 RegisterID dataReg
= frame
.tempRegForData(id
);
1431 if (id
->isTypeKnown())
1432 ic
.id
= ValueRemat::FromKnownType(id
->getKnownType(), dataReg
);
1434 ic
.id
= ValueRemat::FromRegisters(ic
.typeReg
, dataReg
);
1437 ic
.fastPathStart
= masm
.label();
1439 // Note: slow path here is safe, since the frame will not be modified.
1440 ic
.slowPathStart
= stubcc
.masm
.label();
1441 frame
.sync(stubcc
.masm
, Uses(2));
1443 if (id
->mightBeType(JSVAL_TYPE_INT32
)) {
1444 // Always test the type first (see comment in PolyIC.h).
1445 if (!id
->isTypeKnown()) {
1446 ic
.typeGuard
= masm
.testInt32(Assembler::NotEqual
, ic
.typeReg
);
1447 stubcc
.linkExitDirect(ic
.typeGuard
.get(), ic
.slowPathStart
);
1450 // Guard on the clasp.
1451 ic
.claspGuard
= masm
.testObjClass(Assembler::NotEqual
, ic
.objReg
, &js_ArrayClass
);
1452 stubcc
.linkExitDirect(ic
.claspGuard
, ic
.slowPathStart
);
1454 Int32Key key
= id
->isConstant()
1455 ? Int32Key::FromConstant(id
->getValue().toInt32())
1456 : Int32Key::FromRegister(ic
.id
.dataReg());
1458 Assembler::FastArrayLoadFails fails
=
1459 masm
.fastArrayLoad(ic
.objReg
, key
, ic
.typeReg
, ic
.objReg
);
1461 // Store the object back to sp[-1] for calls. This must occur after
1462 // all guards because otherwise sp[-1] will be clobbered.
1464 Address thisSlot
= frame
.addressOf(id
);
1465 masm
.storeValueFromComponents(ImmType(JSVAL_TYPE_OBJECT
), thisReg
.reg(), thisSlot
);
1466 frame
.freeReg(thisReg
.reg());
1469 stubcc
.linkExitDirect(fails
.rangeCheck
, ic
.slowPathStart
);
1470 stubcc
.linkExitDirect(fails
.holeCheck
, ic
.slowPathStart
);
1472 // The type is known to not be dense-friendly ahead of time, so always
1473 // fall back to a slow path.
1474 ic
.claspGuard
= masm
.jump();
1475 stubcc
.linkExitDirect(ic
.claspGuard
, ic
.slowPathStart
);
1479 if (objTypeGuard
.isSet())
1480 objTypeGuard
.get().linkTo(stubcc
.masm
.label(), &stubcc
.masm
);
1484 ic
.slowPathCall
= OOL_STUBCALL(ic::CallElement
);
1486 ic
.slowPathCall
= OOL_STUBCALL(ic::GetElement
);
1489 ic
.slowPathCall
= OOL_STUBCALL(stubs::CallElem
);
1491 ic
.slowPathCall
= OOL_STUBCALL(stubs::GetElem
);
1494 ic
.fastPathRejoin
= masm
.label();
1497 frame
.pushRegs(ic
.typeReg
, ic
.objReg
);
1501 stubcc
.rejoin(Changes(2));
1504 if (!getElemICs
.append(ic
))
1512 ReallySimpleStrictTest(FrameEntry
*fe
)
1514 if (!fe
->isTypeKnown())
1516 JSValueType type
= fe
->getKnownType();
1517 return type
== JSVAL_TYPE_NULL
|| type
== JSVAL_TYPE_UNDEFINED
;
1521 BooleanStrictTest(FrameEntry
*fe
)
1523 return fe
->isConstant() && fe
->getKnownType() == JSVAL_TYPE_BOOLEAN
;
1527 mjit::Compiler::jsop_stricteq(JSOp op
)
1529 FrameEntry
*rhs
= frame
.peek(-1);
1530 FrameEntry
*lhs
= frame
.peek(-2);
1532 Assembler::Condition cond
= (op
== JSOP_STRICTEQ
) ? Assembler::Equal
: Assembler::NotEqual
;
1535 * NB: x64 can do full-Value comparisons. This is beneficial
1536 * to do if the payload/type are not yet in registers.
1539 /* Constant-fold. */
1540 if (lhs
->isConstant() && rhs
->isConstant()) {
1541 bool b
= StrictlyEqual(cx
, lhs
->getValue(), rhs
->getValue());
1543 frame
.push(BooleanValue((op
== JSOP_STRICTEQ
) ? b
: !b
));
1547 if (frame
.haveSameBacking(lhs
, rhs
)) {
1548 /* False iff NaN. */
1549 if (lhs
->isTypeKnown() && lhs
->isNotType(JSVAL_TYPE_DOUBLE
)) {
1551 frame
.push(BooleanValue(op
== JSOP_STRICTEQ
));
1555 /* Assume NaN is in canonical form. */
1556 RegisterID result
= frame
.allocReg(Registers::SingleByteRegs
);
1557 RegisterID treg
= frame
.tempRegForType(lhs
);
1559 Assembler::Condition oppositeCond
= (op
== JSOP_STRICTEQ
) ? Assembler::NotEqual
: Assembler::Equal
;
1561 #if defined JS_CPU_X86 || defined JS_CPU_ARM
1562 static const int CanonicalNaNType
= 0x7FF80000;
1563 masm
.setPtr(oppositeCond
, treg
, Imm32(CanonicalNaNType
), result
);
1564 #elif defined JS_CPU_X64
1565 static const void *CanonicalNaNType
= (void *)0x7FF8000000000000;
1566 masm
.move(ImmPtr(CanonicalNaNType
), JSC::X86Registers::r11
);
1567 masm
.setPtr(oppositeCond
, treg
, JSC::X86Registers::r11
, result
);
1571 frame
.pushTypedPayload(JSVAL_TYPE_BOOLEAN
, result
);
1575 /* Comparison against undefined or null is super easy. */
1577 if ((lhsTest
= ReallySimpleStrictTest(lhs
)) || ReallySimpleStrictTest(rhs
)) {
1578 FrameEntry
*test
= lhsTest
? rhs
: lhs
;
1579 FrameEntry
*known
= lhsTest
? lhs
: rhs
;
1581 if (test
->isTypeKnown()) {
1583 frame
.push(BooleanValue((test
->getKnownType() == known
->getKnownType()) ==
1584 (op
== JSOP_STRICTEQ
)));
1588 /* This is only true if the other side is |null|. */
1589 RegisterID result
= frame
.allocReg(Registers::SingleByteRegs
);
1590 #if defined JS_CPU_X86 || defined JS_CPU_ARM
1591 JSValueTag mask
= known
->getKnownTag();
1592 if (frame
.shouldAvoidTypeRemat(test
))
1593 masm
.set32(cond
, masm
.tagOf(frame
.addressOf(test
)), Imm32(mask
), result
);
1595 masm
.set32(cond
, frame
.tempRegForType(test
), Imm32(mask
), result
);
1596 #elif defined JS_CPU_X64
1597 RegisterID maskReg
= frame
.allocReg();
1598 masm
.move(ImmTag(known
->getKnownTag()), maskReg
);
1600 RegisterID r
= frame
.tempRegForType(test
);
1601 masm
.setPtr(cond
, r
, maskReg
, result
);
1603 frame
.freeReg(maskReg
);
1606 frame
.pushTypedPayload(JSVAL_TYPE_BOOLEAN
, result
);
1610 /* Hardcoded booleans are easy too. */
1611 if ((lhsTest
= BooleanStrictTest(lhs
)) || BooleanStrictTest(rhs
)) {
1612 FrameEntry
*test
= lhsTest
? rhs
: lhs
;
1614 if (test
->isTypeKnown() && test
->isNotType(JSVAL_TYPE_BOOLEAN
)) {
1616 frame
.push(BooleanValue(op
== JSOP_STRICTNE
));
1620 if (test
->isConstant()) {
1622 const Value
&L
= lhs
->getValue();
1623 const Value
&R
= rhs
->getValue();
1624 frame
.push(BooleanValue((L
.toBoolean() == R
.toBoolean()) == (op
== JSOP_STRICTEQ
)));
1628 RegisterID result
= frame
.allocReg(Registers::SingleByteRegs
);
1630 /* Is the other side boolean? */
1632 if (!test
->isTypeKnown())
1633 notBoolean
= frame
.testBoolean(Assembler::NotEqual
, test
);
1635 /* Do a dynamic test. */
1636 bool val
= lhsTest
? lhs
->getValue().toBoolean() : rhs
->getValue().toBoolean();
1637 #if defined JS_CPU_X86 || defined JS_CPU_ARM
1638 if (frame
.shouldAvoidDataRemat(test
))
1639 masm
.set32(cond
, masm
.payloadOf(frame
.addressOf(test
)), Imm32(val
), result
);
1641 masm
.set32(cond
, frame
.tempRegForData(test
), Imm32(val
), result
);
1642 #elif defined JS_CPU_X64
1643 RegisterID r
= frame
.tempRegForData(test
);
1644 masm
.set32(cond
, r
, Imm32(val
), result
);
1647 if (!test
->isTypeKnown()) {
1648 Jump done
= masm
.jump();
1649 notBoolean
.linkTo(masm
.label(), &masm
);
1650 masm
.move(Imm32((op
== JSOP_STRICTNE
)), result
);
1651 done
.linkTo(masm
.label(), &masm
);
1655 frame
.pushTypedPayload(JSVAL_TYPE_BOOLEAN
, result
);
1659 /* Is it impossible that both Values are ints? */
1660 if ((lhs
->isTypeKnown() && lhs
->isNotType(JSVAL_TYPE_INT32
)) ||
1661 (rhs
->isTypeKnown() && rhs
->isNotType(JSVAL_TYPE_INT32
))) {
1662 prepareStubCall(Uses(2));
1664 if (op
== JSOP_STRICTEQ
)
1665 INLINE_STUBCALL(stubs::StrictEq
);
1667 INLINE_STUBCALL(stubs::StrictNe
);
1670 frame
.pushSyncedType(JSVAL_TYPE_BOOLEAN
);
1675 /* Try an integer fast-path. */
1676 bool needStub
= false;
1677 if (!lhs
->isTypeKnown()) {
1678 Jump j
= frame
.testInt32(Assembler::NotEqual
, lhs
);
1679 stubcc
.linkExit(j
, Uses(2));
1683 if (!rhs
->isTypeKnown() && !frame
.haveSameBacking(lhs
, rhs
)) {
1684 Jump j
= frame
.testInt32(Assembler::NotEqual
, rhs
);
1685 stubcc
.linkExit(j
, Uses(2));
1689 FrameEntry
*test
= lhs
->isConstant() ? rhs
: lhs
;
1690 FrameEntry
*other
= lhs
->isConstant() ? lhs
: rhs
;
1692 /* ReturnReg is safely usable with set32, since %ah can be accessed. */
1693 RegisterID resultReg
= Registers::ReturnReg
;
1694 frame
.takeReg(resultReg
);
1695 RegisterID testReg
= frame
.tempRegForData(test
);
1696 frame
.pinReg(testReg
);
1698 JS_ASSERT(resultReg
!= testReg
);
1700 /* Set boolean in resultReg. */
1701 if (other
->isConstant()) {
1702 masm
.set32(cond
, testReg
, Imm32(other
->getValue().toInt32()), resultReg
);
1703 } else if (frame
.shouldAvoidDataRemat(other
)) {
1704 masm
.set32(cond
, testReg
, frame
.addressOf(other
), resultReg
);
1706 RegisterID otherReg
= frame
.tempRegForData(other
);
1708 JS_ASSERT(otherReg
!= resultReg
);
1709 JS_ASSERT(otherReg
!= testReg
);
1711 masm
.set32(cond
, testReg
, otherReg
, resultReg
);
1714 frame
.unpinReg(testReg
);
1718 if (op
== JSOP_STRICTEQ
)
1719 OOL_STUBCALL(stubs::StrictEq
);
1721 OOL_STUBCALL(stubs::StrictNe
);
1725 frame
.pushTypedPayload(JSVAL_TYPE_BOOLEAN
, resultReg
);
1728 stubcc
.rejoin(Changes(1));
1730 /* TODO: Port set32() logic to ARM. */
1731 prepareStubCall(Uses(2));
1733 if (op
== JSOP_STRICTEQ
)
1734 INLINE_STUBCALL(stubs::StrictEq
);
1736 INLINE_STUBCALL(stubs::StrictNe
);
1739 frame
.pushSyncedType(JSVAL_TYPE_BOOLEAN
);
1745 mjit::Compiler::jsop_pos()
1747 FrameEntry
*top
= frame
.peek(-1);
1749 if (top
->isTypeKnown()) {
1750 if (top
->getKnownType() <= JSVAL_TYPE_INT32
)
1752 prepareStubCall(Uses(1));
1753 INLINE_STUBCALL(stubs::Pos
);
1759 frame
.giveOwnRegs(top
);
1762 if (frame
.shouldAvoidTypeRemat(top
))
1763 j
= masm
.testNumber(Assembler::NotEqual
, frame
.addressOf(top
));
1765 j
= masm
.testNumber(Assembler::NotEqual
, frame
.tempRegForType(top
));
1766 stubcc
.linkExit(j
, Uses(1));
1769 OOL_STUBCALL(stubs::Pos
);
1771 stubcc
.rejoin(Changes(1));