Fix call mechanism and recompilation (bug 609222, r=dmandelin,adrake,m_kato).
[mozilla-central.git] / js / src / methodjit / FastOps.cpp
blobf91d68700beb7c5d58919441dc2af0a7391ba1cf
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
15 * License.
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 * May 28, 2008.
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
23 * Contributor(s):
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 ***** */
40 #include "jsbool.h"
41 #include "jslibmath.h"
42 #include "jsnum.h"
43 #include "jsscope.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"
52 using namespace js;
53 using namespace js::mjit;
55 typedef JSC::MacroAssembler::RegisterID RegisterID;
57 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);
69 return reg;
70 #else
71 if (rhs->isConstant())
72 return frame.allocReg();
73 return frame.copyDataIntoReg(rhs);
74 #endif
77 void
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);
86 frame.popn(2);
87 frame.pushTypedPayload(JSVAL_TYPE_INT32, result);
90 void
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);
97 frame.popn(2);
98 frame.pushTypedPayload(JSVAL_TYPE_INT32, lhsData);
101 void
102 mjit::Compiler::jsop_rsh_int_const(FrameEntry *lhs, FrameEntry *rhs)
104 int32 shiftAmount = rhs->getValue().toInt32();
106 if (!shiftAmount) {
107 frame.pop();
108 return;
111 RegisterID result = frame.copyDataIntoReg(lhs);
112 masm.rshift32(Imm32(shiftAmount), result);
113 frame.popn(2);
114 frame.pushTypedPayload(JSVAL_TYPE_INT32, result);
117 void
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);
141 if (shiftAmount)
142 masm.rshift32(Imm32(shiftAmount), lhsData);
144 frame.popn(2);
145 frame.pushTypedPayload(JSVAL_TYPE_INT32, lhsData);
147 stubcc.rejoin(Changes(1));
150 void
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));
161 stubcc.leave();
162 OOL_STUBCALL(stubs::Rsh);
163 masm.move(Imm32(lhs->getValue().toInt32()), result);
164 masm.rshift32(rhsData, result);
165 frame.freeReg(rhsData);
167 frame.popn(2);
168 frame.pushTypedPayload(JSVAL_TYPE_INT32, result);
169 stubcc.rejoin(Changes(1));
172 void
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));
183 stubcc.leave();
184 OOL_STUBCALL(stubs::Rsh);
186 masm.rshift32(rhsData, lhsData);
187 frame.freeReg(rhsData);
188 frame.popn(2);
189 frame.pushTypedPayload(JSVAL_TYPE_INT32, lhsData);
191 stubcc.rejoin(Changes(1));
194 void
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))
212 lhsType = rhsType;
213 else
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);
244 frame.popn(2);
245 frame.pushTypedPayload(JSVAL_TYPE_INT32, lhsData);
247 stubcc.rejoin(Changes(1));
250 void
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))
257 return;
259 if (lhs->isNotType(JSVAL_TYPE_INT32) || rhs->isNotType(JSVAL_TYPE_INT32)) {
260 prepareStubCall(Uses(2));
261 INLINE_STUBCALL(stubs::Rsh);
262 frame.popn(2);
263 frame.pushSyncedType(JSVAL_TYPE_INT32);
264 return;
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);
271 else
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);
276 else
277 jsop_rsh_unknown_const(lhs, rhs);
278 } else {
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);
283 else
284 jsop_rsh_unknown_any(lhs, rhs);
288 void
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);
297 frame.pop();
298 frame.pushSyncedType(JSVAL_TYPE_INT32);
299 return;
302 /* Test the type. */
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);
308 stubNeeded = true;
311 if (stubNeeded) {
312 stubcc.leave();
313 OOL_STUBCALL(stubs::BitNot);
316 RegisterID reg = frame.ownRegForData(top);
317 masm.not32(reg);
318 frame.pop();
319 frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
321 if (stubNeeded)
322 stubcc.rejoin(Changes(1));
325 void
326 mjit::Compiler::jsop_bitop(JSOp op)
328 FrameEntry *rhs = frame.peek(-1);
329 FrameEntry *lhs = frame.peek(-2);
331 VoidStub stub;
332 switch (op) {
333 case JSOP_BITOR:
334 stub = stubs::BitOr;
335 break;
336 case JSOP_BITAND:
337 stub = stubs::BitAnd;
338 break;
339 case JSOP_BITXOR:
340 stub = stubs::BitXor;
341 break;
342 case JSOP_LSH:
343 stub = stubs::Lsh;
344 break;
345 case JSOP_URSH:
346 stub = stubs::Ursh;
347 break;
348 default:
349 JS_NOT_REACHED("wat");
350 return;
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)) {
362 frame.popn(2);
363 frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
364 return;
366 MaybeJump isInt;
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));
377 stubcc.leave();
378 OOL_STUBCALL(stub);
380 if (isInt.isSet())
381 isInt.get().linkTo(masm.label(), &masm);
382 frame.popn(2);
383 frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
384 stubcc.rejoin(Changes(1));
385 return;
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);
393 frame.popn(2);
394 if (op == JSOP_URSH)
395 frame.pushSynced();
396 else
397 frame.pushSyncedType(JSVAL_TYPE_INT32);
398 return;
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);
407 stubNeeded = true;
409 if (!lhs->isTypeKnown() && !frame.haveSameBacking(lhs, rhs)) {
410 Jump lhsFail = frame.testInt32(Assembler::NotEqual, lhs);
411 stubcc.linkExit(lhsFail, Uses(2));
412 stubNeeded = true;
415 if (lhs->isConstant() && rhs->isConstant()) {
416 int32 L = lhs->getValue().toInt32();
417 int32 R = rhs->getValue().toInt32();
419 frame.popn(2);
420 switch (op) {
421 case JSOP_BITOR:
422 frame.push(Int32Value(L | R));
423 return;
424 case JSOP_BITXOR:
425 frame.push(Int32Value(L ^ R));
426 return;
427 case JSOP_BITAND:
428 frame.push(Int32Value(L & R));
429 return;
430 case JSOP_LSH:
431 frame.push(Int32Value(L << R));
432 return;
433 case JSOP_URSH:
435 uint32 unsignedL;
436 if (ValueToECMAUint32(cx, lhs->getValue(), (uint32_t*)&unsignedL)) {
437 frame.push(NumberValue(uint32(unsignedL >> (R & 31))));
438 return;
440 break;
442 default:
443 JS_NOT_REACHED("say wat");
447 RegisterID reg;
449 switch (op) {
450 case JSOP_BITOR:
451 case JSOP_BITXOR:
452 case JSOP_BITAND:
454 /* Commutative, and we're guaranteed both are ints. */
455 if (lhs->isConstant()) {
456 JS_ASSERT(!rhs->isConstant());
457 FrameEntry *temp = rhs;
458 rhs = lhs;
459 lhs = temp;
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);
468 else
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);
475 else
476 masm.or32(masm.payloadOf(frame.addressOf(rhs)), reg);
477 } else {
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);
483 else
484 masm.or32(rhsReg, reg);
487 break;
490 case JSOP_LSH:
491 case JSOP_URSH:
493 /* Not commutative. */
494 if (rhs->isConstant()) {
495 RegisterID reg = frame.ownRegForData(lhs);
496 int shift = rhs->getValue().toInt32() & 0x1F;
498 if (shift) {
499 if (op == JSOP_LSH)
500 masm.lshift32(Imm32(shift), reg);
501 else
502 masm.urshift32(Imm32(shift), reg);
504 if (stubNeeded) {
505 stubcc.leave();
506 OOL_STUBCALL(stub);
508 frame.popn(2);
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);
514 if (stubNeeded)
515 stubcc.rejoin(Changes(1));
517 return;
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));
523 #else
524 RegisterID rr = frame.tempRegForData(rhs);
525 #endif
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();
531 if (rr != reg)
532 masm.move(rr, reg);
533 } else {
534 frame.pinReg(rr);
535 if (lhs->isConstant()) {
536 reg = frame.allocReg();
537 masm.move(Imm32(lhs->getValue().toInt32()), reg);
538 } else {
539 reg = frame.copyDataIntoReg(lhs);
541 frame.unpinReg(rr);
544 if (op == JSOP_LSH) {
545 masm.lshift32(rr, reg);
546 } else {
547 masm.urshift32(rr, reg);
549 Jump isNegative = masm.branch32(Assembler::LessThan, reg, Imm32(0));
550 stubcc.linkExit(isNegative, Uses(2));
551 stubNeeded = true;
553 break;
556 default:
557 JS_NOT_REACHED("NYI");
558 return;
561 if (stubNeeded) {
562 stubcc.leave();
563 OOL_STUBCALL(stub);
566 frame.pop();
567 frame.pop();
569 if (op == JSOP_URSH)
570 frame.pushNumber(reg, true);
571 else
572 frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
574 if (stubNeeded)
575 stubcc.rejoin(Changes(1));
578 void
579 mjit::Compiler::jsop_globalinc(JSOp op, uint32 index)
581 uint32 slot = script->getGlobalSlot(index);
583 bool popped = false;
584 PC += JSOP_GLOBALINC_LENGTH;
585 if (JSOp(*PC) == JSOP_POP && !analysis->jumpTarget(PC)) {
586 popped = true;
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);
593 RegisterID data;
594 RegisterID reg = frame.allocReg();
595 Address addr = masm.objSlotRef(globalObj, reg, slot);
596 uint32 depth = frame.stackDepth();
598 if (post && !popped) {
599 frame.push(addr);
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);
604 } else {
605 Jump notInt = masm.testInt32(Assembler::NotEqual, addr);
606 stubcc.linkExit(notInt, Uses(0));
607 data = frame.allocReg();
608 masm.loadPayload(addr, data);
611 Jump ovf;
612 if (amt > 0)
613 ovf = masm.branchAdd32(Assembler::Overflow, Imm32(1), data);
614 else
615 ovf = masm.branchSub32(Assembler::Overflow, Imm32(1), data);
616 stubcc.linkExit(ovf, Uses(0));
618 stubcc.leave();
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);
626 #endif
628 if (!post && !popped)
629 frame.pushInt32(data);
630 else
631 frame.freeReg(data);
633 frame.freeReg(reg);
635 stubcc.rejoin(Changes((!post && !popped) ? 1 : 0));
638 static inline bool
639 CheckNullOrUndefined(FrameEntry *fe)
641 if (!fe->isTypeKnown())
642 return false;
643 JSValueType type = fe->getKnownType();
644 return type == JSVAL_TYPE_NULL || type == JSVAL_TYPE_UNDEFINED;
647 bool
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()));
656 bool lhsTest;
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);
666 frame.pop();
667 frame.pop();
670 * :FIXME: Easier test for undefined || null?
671 * Maybe put them next to each other, subtract, do a single compare?
674 if (target) {
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
684 * jumpAndTrace.
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))
693 return false;
694 j1.linkTo(masm.label(), &masm);
695 } else {
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))
699 return false;
700 j.linkTo(masm.label(), &masm);
702 } else {
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);
713 return true;
716 return emitStubCmpOp(stub, target, fused);
719 bool
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);
746 } else {
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);
757 } else {
758 return jsop_relational_full(op, stub, target, fused);
762 void
763 mjit::Compiler::jsop_not()
765 FrameEntry *top = frame.peek(-1);
767 if (top->isConstant()) {
768 const Value &v = top->getValue();
769 frame.pop();
770 frame.push(BooleanValue(!js_ValueToBoolean(v)));
771 return;
774 if (top->isTypeKnown()) {
775 JSValueType type = top->getKnownType();
776 switch (type) {
777 case JSVAL_TYPE_INT32:
779 RegisterID data = frame.allocReg(Registers::SingleByteRegs);
780 if (frame.shouldAvoidDataRemat(top))
781 masm.loadPayload(frame.addressOf(top), data);
782 else
783 masm.move(frame.tempRegForData(top), data);
785 masm.set32(Assembler::Equal, data, Imm32(0), data);
787 frame.pop();
788 frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, data);
789 break;
792 case JSVAL_TYPE_BOOLEAN:
794 RegisterID reg = frame.ownRegForData(top);
796 masm.xor32(Imm32(1), reg);
798 frame.pop();
799 frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg);
800 break;
803 case JSVAL_TYPE_OBJECT:
805 frame.pop();
806 frame.push(BooleanValue(false));
807 break;
810 default:
812 prepareStubCall(Uses(1));
813 INLINE_STUBCALL(stubs::ValueToBoolean);
815 RegisterID reg = Registers::ReturnReg;
816 frame.takeReg(reg);
817 masm.xor32(Imm32(1), reg);
819 frame.pop();
820 frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg);
821 break;
825 return;
828 RegisterID data = frame.allocReg(Registers::SingleByteRegs);
829 if (frame.shouldAvoidDataRemat(top))
830 masm.loadPayload(frame.addressOf(top), data);
831 else
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);
868 /* Leave. */
869 stubcc.leave();
870 OOL_STUBCALL(stubs::Not);
872 frame.pop();
873 frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, data);
875 stubcc.rejoin(Changes(1));
878 void
879 mjit::Compiler::jsop_typeof()
881 FrameEntry *fe = frame.peek(-1);
883 if (fe->isTypeKnown()) {
884 JSRuntime *rt = cx->runtime;
886 JSAtom *atom = NULL;
887 switch (fe->getKnownType()) {
888 case JSVAL_TYPE_STRING:
889 atom = rt->atomState.typeAtoms[JSTYPE_STRING];
890 break;
891 case JSVAL_TYPE_UNDEFINED:
892 atom = rt->atomState.typeAtoms[JSTYPE_VOID];
893 break;
894 case JSVAL_TYPE_NULL:
895 atom = rt->atomState.typeAtoms[JSTYPE_OBJECT];
896 break;
897 case JSVAL_TYPE_OBJECT:
898 atom = NULL;
899 break;
900 case JSVAL_TYPE_BOOLEAN:
901 atom = rt->atomState.typeAtoms[JSTYPE_BOOLEAN];
902 break;
903 default:
904 atom = rt->atomState.typeAtoms[JSTYPE_NUMBER];
905 break;
908 if (atom) {
909 frame.pop();
910 frame.push(StringValue(ATOM_TO_STRING(atom)));
911 return;
915 prepareStubCall(Uses(1));
916 INLINE_STUBCALL(stubs::TypeOf);
917 frame.pop();
918 frame.takeReg(Registers::ReturnReg);
919 frame.pushTypedPayload(JSVAL_TYPE_STRING, Registers::ReturnReg);
922 bool
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)
937 ? Assembler::NonZero
938 : Assembler::Zero;
939 Assembler::Condition ncond = (op == JSOP_IFNE || op == JSOP_OR)
940 ? Assembler::Zero
941 : Assembler::NonZero;
943 /* Inline path: Boolean guard + call script. */
944 MaybeJump jmpNotBool;
945 MaybeJump jmpNotExecScript;
946 if (type.isSet()) {
947 jmpNotBool.setJump(masm.testBoolean(Assembler::NotEqual, type.reg()));
948 } else {
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),
975 frame.frameDepth());
977 jmpCvtExecScript.setJump(stubcc.masm.branchTest32(cond, Registers::ReturnReg,
978 Registers::ReturnReg));
979 jmpCvtRejoin.setJump(stubcc.masm.jump());
982 /* Rejoin tag. */
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);
996 frame.pop();
998 return jumpAndTrace(j, target);
1001 bool
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());
1009 frame.pop();
1011 if (op == JSOP_IFEQ)
1012 b = !b;
1013 if (b) {
1014 frame.syncAndForgetEverything();
1015 if (!jumpAndTrace(masm.jump(), target))
1016 return false;
1018 return true;
1021 return booleanJumpScript(op, target);
1024 bool
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))
1037 return false;
1040 frame.pop();
1041 return true;
1044 return booleanJumpScript(op, target);
1047 void
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();
1059 double d;
1060 ValueToNumber(cx, v, &d);
1061 if (post) {
1062 frame.push(NumberValue(d + amt));
1063 frame.storeLocal(slot);
1064 frame.pop();
1065 } else {
1066 frame.pop();
1067 frame.push(NumberValue(d + amt));
1068 frame.storeLocal(slot);
1070 if (popped)
1071 frame.pop();
1072 return;
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
1078 * opcodes.
1080 if (fe->isNotType(JSVAL_TYPE_INT32) || (post && !popped)) {
1081 /* V */
1082 jsop_pos();
1083 /* N */
1085 if (post && !popped) {
1086 frame.dup();
1087 /* N N */
1090 frame.push(Int32Value(1));
1091 /* N? N 1 */
1093 if (amt == 1)
1094 jsop_binary(JSOP_ADD, stubs::Add);
1095 else
1096 jsop_binary(JSOP_SUB, stubs::Sub);
1097 /* N? N+1 */
1099 frame.storeLocal(slot, post || popped);
1100 /* N? N+1 */
1102 if (post || popped)
1103 frame.pop();
1105 return;
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);
1116 Jump ovf;
1117 if (amt > 0)
1118 ovf = masm.branchAdd32(Assembler::Overflow, Imm32(1), reg);
1119 else
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. */
1124 stubcc.leave();
1126 stubcc.masm.move(Imm32(slot), Registers::ArgReg1);
1127 if (op == JSOP_LOCALINC || op == JSOP_INCLOCAL)
1128 OOL_STUBCALL(stubs::IncLocal);
1129 else
1130 OOL_STUBCALL(stubs::DecLocal);
1132 frame.pop();
1133 frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
1134 frame.storeLocal(slot, popped, false);
1136 if (popped)
1137 frame.pop();
1138 else
1139 frame.forgetType(frame.peek(-1));
1141 stubcc.rejoin(Changes(0));
1144 void
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();
1151 jsop_getarg(slot);
1152 if (post && !popped)
1153 frame.dup();
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);
1160 frame.pop();
1162 Jump ovf;
1163 if (amt > 0)
1164 ovf = masm.branchAdd32(Assembler::Overflow, Imm32(1), reg);
1165 else
1166 ovf = masm.branchSub32(Assembler::Overflow, Imm32(1), reg);
1167 stubcc.linkExit(ovf, Uses(0));
1169 stubcc.leave();
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);
1180 if (post || popped)
1181 frame.pop();
1182 else
1183 frame.forgetType(fe);
1185 stubcc.rejoin(Changes((post || popped) ? 0 : 1));
1188 static inline bool
1189 IsCacheableSetElem(FrameEntry *obj, FrameEntry *id, FrameEntry *value)
1191 if (obj->isNotType(JSVAL_TYPE_OBJECT))
1192 return false;
1193 if (id->isNotType(JSVAL_TYPE_INT32))
1194 return false;
1195 if (id->isConstant() && id->getValue().toInt32() < 0)
1196 return false;
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))
1202 return false;
1204 return true;
1207 bool
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();
1216 return true;
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
1241 // already existed.
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);
1264 } else {
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());
1282 else
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);
1315 } else {
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);
1322 stubcc.leave();
1323 #ifdef JS_POLYIC
1324 passICAddress(&ic);
1325 ic.slowPathCall = OOL_STUBCALL(STRICT_VARIANT(ic::SetElement));
1326 #else
1327 OOL_STUBCALL(STRICT_VARIANT(stubs::SetElem));
1328 #endif
1330 ic.fastPathRejoin = masm.label();
1332 frame.freeReg(ic.objReg);
1333 frame.shimmy(2);
1334 stubcc.rejoin(Changes(2));
1336 #ifdef JS_POLYIC
1337 if (!setElemICs.append(ic))
1338 return false;
1339 #endif
1341 return true;
1344 static inline bool
1345 IsCacheableGetElem(FrameEntry *obj, FrameEntry *id)
1347 if (obj->isTypeKnown() && obj->getKnownType() != JSVAL_TYPE_OBJECT)
1348 return false;
1349 if (id->isTypeKnown() &&
1350 !(id->getKnownType() == JSVAL_TYPE_INT32
1351 #ifdef JS_POLYIC
1352 || id->getKnownType() == JSVAL_TYPE_STRING
1353 #endif
1354 )) {
1355 return false;
1358 if (id->isTypeKnown() && id->getKnownType() == JSVAL_TYPE_INT32 && id->isConstant() &&
1359 id->getValue().toInt32() < 0) {
1360 return false;
1363 // obj[obj] is not allowed, since it will never optimize.
1364 if (obj->hasSameBacking(id))
1365 return false;
1367 return true;
1370 bool
1371 mjit::Compiler::jsop_getelem(bool isCall)
1373 FrameEntry *obj = frame.peek(-2);
1374 FrameEntry *id = frame.peek(-1);
1376 if (!IsCacheableGetElem(obj, id)) {
1377 if (isCall)
1378 jsop_callelem_slow();
1379 else
1380 jsop_getelem_slow();
1381 return true;
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
1409 // outgoing object.
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();
1422 else
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());
1429 } else {
1430 RegisterID dataReg = frame.tempRegForData(id);
1431 if (id->isTypeKnown())
1432 ic.id = ValueRemat::FromKnownType(id->getKnownType(), dataReg);
1433 else
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.
1463 if (isCall) {
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);
1471 } else {
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);
1478 stubcc.leave();
1479 if (objTypeGuard.isSet())
1480 objTypeGuard.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1481 #ifdef JS_POLYIC
1482 passICAddress(&ic);
1483 if (isCall)
1484 ic.slowPathCall = OOL_STUBCALL(ic::CallElement);
1485 else
1486 ic.slowPathCall = OOL_STUBCALL(ic::GetElement);
1487 #else
1488 if (isCall)
1489 ic.slowPathCall = OOL_STUBCALL(stubs::CallElem);
1490 else
1491 ic.slowPathCall = OOL_STUBCALL(stubs::GetElem);
1492 #endif
1494 ic.fastPathRejoin = masm.label();
1496 frame.popn(2);
1497 frame.pushRegs(ic.typeReg, ic.objReg);
1498 if (isCall)
1499 frame.pushSynced();
1501 stubcc.rejoin(Changes(2));
1503 #ifdef JS_POLYIC
1504 if (!getElemICs.append(ic))
1505 return false;
1506 #endif
1508 return true;
1511 static inline bool
1512 ReallySimpleStrictTest(FrameEntry *fe)
1514 if (!fe->isTypeKnown())
1515 return false;
1516 JSValueType type = fe->getKnownType();
1517 return type == JSVAL_TYPE_NULL || type == JSVAL_TYPE_UNDEFINED;
1520 static inline bool
1521 BooleanStrictTest(FrameEntry *fe)
1523 return fe->isConstant() && fe->getKnownType() == JSVAL_TYPE_BOOLEAN;
1526 void
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());
1542 frame.popn(2);
1543 frame.push(BooleanValue((op == JSOP_STRICTEQ) ? b : !b));
1544 return;
1547 if (frame.haveSameBacking(lhs, rhs)) {
1548 /* False iff NaN. */
1549 if (lhs->isTypeKnown() && lhs->isNotType(JSVAL_TYPE_DOUBLE)) {
1550 frame.popn(2);
1551 frame.push(BooleanValue(op == JSOP_STRICTEQ));
1552 return;
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);
1568 #endif
1570 frame.popn(2);
1571 frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
1572 return;
1575 /* Comparison against undefined or null is super easy. */
1576 bool lhsTest;
1577 if ((lhsTest = ReallySimpleStrictTest(lhs)) || ReallySimpleStrictTest(rhs)) {
1578 FrameEntry *test = lhsTest ? rhs : lhs;
1579 FrameEntry *known = lhsTest ? lhs : rhs;
1581 if (test->isTypeKnown()) {
1582 frame.popn(2);
1583 frame.push(BooleanValue((test->getKnownType() == known->getKnownType()) ==
1584 (op == JSOP_STRICTEQ)));
1585 return;
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);
1594 else
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);
1604 #endif
1605 frame.popn(2);
1606 frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
1607 return;
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)) {
1615 frame.popn(2);
1616 frame.push(BooleanValue(op == JSOP_STRICTNE));
1617 return;
1620 if (test->isConstant()) {
1621 frame.popn(2);
1622 const Value &L = lhs->getValue();
1623 const Value &R = rhs->getValue();
1624 frame.push(BooleanValue((L.toBoolean() == R.toBoolean()) == (op == JSOP_STRICTEQ)));
1625 return;
1628 RegisterID result = frame.allocReg(Registers::SingleByteRegs);
1630 /* Is the other side boolean? */
1631 Jump notBoolean;
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);
1640 else
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);
1645 #endif
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);
1654 frame.popn(2);
1655 frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
1656 return;
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);
1666 else
1667 INLINE_STUBCALL(stubs::StrictNe);
1669 frame.popn(2);
1670 frame.pushSyncedType(JSVAL_TYPE_BOOLEAN);
1671 return;
1674 #ifndef JS_CPU_ARM
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));
1680 needStub = true;
1683 if (!rhs->isTypeKnown() && !frame.haveSameBacking(lhs, rhs)) {
1684 Jump j = frame.testInt32(Assembler::NotEqual, rhs);
1685 stubcc.linkExit(j, Uses(2));
1686 needStub = true;
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);
1705 } else {
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);
1716 if (needStub) {
1717 stubcc.leave();
1718 if (op == JSOP_STRICTEQ)
1719 OOL_STUBCALL(stubs::StrictEq);
1720 else
1721 OOL_STUBCALL(stubs::StrictNe);
1724 frame.popn(2);
1725 frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, resultReg);
1727 if (needStub)
1728 stubcc.rejoin(Changes(1));
1729 #else
1730 /* TODO: Port set32() logic to ARM. */
1731 prepareStubCall(Uses(2));
1733 if (op == JSOP_STRICTEQ)
1734 INLINE_STUBCALL(stubs::StrictEq);
1735 else
1736 INLINE_STUBCALL(stubs::StrictNe);
1738 frame.popn(2);
1739 frame.pushSyncedType(JSVAL_TYPE_BOOLEAN);
1740 return;
1741 #endif
1744 void
1745 mjit::Compiler::jsop_pos()
1747 FrameEntry *top = frame.peek(-1);
1749 if (top->isTypeKnown()) {
1750 if (top->getKnownType() <= JSVAL_TYPE_INT32)
1751 return;
1752 prepareStubCall(Uses(1));
1753 INLINE_STUBCALL(stubs::Pos);
1754 frame.pop();
1755 frame.pushSynced();
1756 return;
1759 frame.giveOwnRegs(top);
1761 Jump j;
1762 if (frame.shouldAvoidTypeRemat(top))
1763 j = masm.testNumber(Assembler::NotEqual, frame.addressOf(top));
1764 else
1765 j = masm.testNumber(Assembler::NotEqual, frame.tempRegForType(top));
1766 stubcc.linkExit(j, Uses(1));
1768 stubcc.leave();
1769 OOL_STUBCALL(stubs::Pos);
1771 stubcc.rejoin(Changes(1));