1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
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/
11 : *
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.
16 : *
17 : * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 : * May 28, 2008.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Brendan Eich <brendan@mozilla.org>
22 : *
23 : * Contributor(s):
24 : * David Anderson <danderson@mozilla.com>
25 : * David Mandelin <dmandelin@mozilla.com>
26 : * Sean Stangl <sstangl@mozilla.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either of the GNU General Public License Version 2 or later (the "GPL"),
30 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 : #include "jsbool.h"
42 : #include "jslibmath.h"
43 : #include "jsnum.h"
44 : #include "methodjit/MethodJIT.h"
45 : #include "methodjit/Compiler.h"
46 : #include "methodjit/StubCalls.h"
47 : #include "methodjit/FrameState-inl.h"
48 :
49 : using namespace js;
50 : using namespace js::mjit;
51 : using namespace js::analyze;
52 : using namespace JSC;
53 :
54 : typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
55 :
56 : bool
57 675197 : mjit::Compiler::tryBinaryConstantFold(JSContext *cx, FrameState &frame, JSOp op,
58 : FrameEntry *lhs, FrameEntry *rhs, Value *vp)
59 : {
60 675197 : if (!lhs->isConstant() || !rhs->isConstant())
61 674276 : return false;
62 :
63 921 : const Value &L = lhs->getValue();
64 921 : const Value &R = rhs->getValue();
65 :
66 1659 : if (!L.isPrimitive() || !R.isPrimitive() ||
67 738 : (op == JSOP_ADD && (L.isString() || R.isString()))) {
68 192 : return false;
69 : }
70 :
71 : bool needInt;
72 729 : switch (op) {
73 : case JSOP_ADD:
74 : case JSOP_SUB:
75 : case JSOP_MUL:
76 : case JSOP_DIV:
77 721 : needInt = false;
78 721 : break;
79 :
80 : case JSOP_MOD:
81 14 : needInt = (L.isInt32() && R.isInt32() &&
82 14 : L.toInt32() >= 0 && R.toInt32() > 0);
83 8 : break;
84 :
85 : default:
86 0 : JS_NOT_REACHED("NYI");
87 : needInt = false; /* Silence compiler warning. */
88 : break;
89 : }
90 :
91 729 : double dL = 0, dR = 0;
92 729 : int32_t nL = 0, nR = 0;
93 : /*
94 : * We don't need to check for conversion failure, since primitive conversion
95 : * is infallible.
96 : */
97 729 : if (needInt) {
98 2 : JS_ALWAYS_TRUE(ToInt32(cx, L, &nL));
99 2 : JS_ALWAYS_TRUE(ToInt32(cx, R, &nR));
100 : } else {
101 727 : JS_ALWAYS_TRUE(ToNumber(cx, L, &dL));
102 727 : JS_ALWAYS_TRUE(ToNumber(cx, R, &dR));
103 : }
104 :
105 729 : switch (op) {
106 : case JSOP_ADD:
107 278 : dL += dR;
108 278 : break;
109 : case JSOP_SUB:
110 124 : dL -= dR;
111 124 : break;
112 : case JSOP_MUL:
113 60 : dL *= dR;
114 60 : break;
115 : case JSOP_DIV:
116 259 : dL = js::NumberDiv(dL, dR);
117 259 : break;
118 : case JSOP_MOD:
119 8 : if (needInt)
120 2 : nL %= nR;
121 6 : else if (dR == 0)
122 2 : dL = js_NaN;
123 : else
124 4 : dL = js_fmod(dL, dR);
125 8 : break;
126 :
127 : default:
128 0 : JS_NOT_REACHED("NYI");
129 : break;
130 : }
131 :
132 729 : if (needInt)
133 2 : vp->setInt32(nL);
134 : else
135 727 : vp->setNumber(dL);
136 :
137 729 : return true;
138 : }
139 :
140 : void
141 276171 : mjit::Compiler::slowLoadConstantDouble(Assembler &masm,
142 : FrameEntry *fe, FPRegisterID fpreg)
143 : {
144 276171 : if (fe->getValue().isInt32())
145 275216 : masm.slowLoadConstantDouble((double) fe->getValue().toInt32(), fpreg);
146 : else
147 955 : masm.slowLoadConstantDouble(fe->getValue().toDouble(), fpreg);
148 276171 : }
149 :
150 : void
151 2385 : mjit::Compiler::maybeJumpIfNotInt32(Assembler &masm, MaybeJump &mj, FrameEntry *fe,
152 : MaybeRegisterID &mreg)
153 : {
154 2385 : if (!fe->isTypeKnown()) {
155 2382 : if (mreg.isSet())
156 2354 : mj.setJump(masm.testInt32(Assembler::NotEqual, mreg.reg()));
157 : else
158 28 : mj.setJump(masm.testInt32(Assembler::NotEqual, frame.addressOf(fe)));
159 3 : } else if (fe->getKnownType() != JSVAL_TYPE_INT32) {
160 0 : mj.setJump(masm.jump());
161 : }
162 2385 : }
163 :
164 : void
165 2385 : mjit::Compiler::maybeJumpIfNotDouble(Assembler &masm, MaybeJump &mj, FrameEntry *fe,
166 : MaybeRegisterID &mreg)
167 : {
168 2385 : if (!fe->isTypeKnown()) {
169 2382 : if (mreg.isSet())
170 2354 : mj.setJump(masm.testDouble(Assembler::NotEqual, mreg.reg()));
171 : else
172 28 : mj.setJump(masm.testDouble(Assembler::NotEqual, frame.addressOf(fe)));
173 3 : } else if (fe->getKnownType() != JSVAL_TYPE_DOUBLE) {
174 3 : mj.setJump(masm.jump());
175 : }
176 2385 : }
177 :
178 : bool
179 68703 : mjit::Compiler::jsop_binary_slow(JSOp op, VoidStub stub, JSValueType type,
180 : FrameEntry *lhs, FrameEntry *rhs)
181 : {
182 : bool isStringResult = (op == JSOP_ADD) &&
183 68703 : (lhs->isType(JSVAL_TYPE_STRING) || rhs->isType(JSVAL_TYPE_STRING));
184 68703 : JS_ASSERT_IF(isStringResult && type != JSVAL_TYPE_UNKNOWN, type == JSVAL_TYPE_STRING);
185 :
186 68703 : prepareStubCall(Uses(2));
187 68703 : INLINE_STUBCALL(stub, REJOIN_BINARY);
188 68703 : frame.popn(2);
189 68703 : frame.pushSynced(isStringResult ? JSVAL_TYPE_STRING : type);
190 68703 : return true;
191 : }
192 :
193 : bool
194 671302 : mjit::Compiler::jsop_binary(JSOp op, VoidStub stub, JSValueType type, types::TypeSet *typeSet)
195 : {
196 671302 : FrameEntry *rhs = frame.peek(-1);
197 671302 : FrameEntry *lhs = frame.peek(-2);
198 :
199 : Value v;
200 671302 : if (tryBinaryConstantFold(cx, frame, op, lhs, rhs, &v)) {
201 721 : if (!v.isInt32() && typeSet && !typeSet->hasType(types::Type::DoubleType())) {
202 : /*
203 : * OK to ignore failure here, we aren't performing the operation
204 : * itself. Note that monitorOverflow will propagate the type as
205 : * necessary if a *INC operation overflowed.
206 : */
207 8 : types::TypeScript::MonitorOverflow(cx, script, PC);
208 8 : return false;
209 : }
210 713 : frame.popn(2);
211 713 : frame.push(v);
212 713 : return true;
213 : }
214 :
215 : /*
216 : * Bail out if there are unhandled types or ops.
217 : * This is temporary while ops are still being implemented.
218 : */
219 2346418 : if ((lhs->isConstant() && rhs->isConstant()) ||
220 749052 : (lhs->isTypeKnown() && (lhs->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET)) ||
221 926785 : (rhs->isTypeKnown() && (rhs->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET)))
222 : {
223 68703 : return jsop_binary_slow(op, stub, type, lhs, rhs);
224 : }
225 :
226 : /*
227 : * If this is an operation on which integer overflows can be ignored, treat
228 : * the result as an integer even if it has been marked as overflowing by
229 : * the interpreter. Doing this changes the values we maintain on the stack
230 : * from those the interpreter would maintain; this is OK as values derived
231 : * from ignored overflows are not live across points where the interpreter
232 : * can join into JIT code (loop heads and safe points).
233 : */
234 601878 : CrossSSAValue pushv(a->inlineIndex, SSAValue::PushedValue(PC - script->code, 0));
235 601878 : bool cannotOverflow = loop && loop->cannotIntegerOverflow(pushv);
236 601878 : bool ignoreOverflow = loop && loop->ignoreIntegerOverflow(pushv);
237 :
238 601878 : if (rhs->isType(JSVAL_TYPE_INT32) && lhs->isType(JSVAL_TYPE_INT32) &&
239 : op == JSOP_ADD && ignoreOverflow) {
240 14 : type = JSVAL_TYPE_INT32;
241 : }
242 :
243 : /* Can do int math iff there is no double constant and the op is not division. */
244 : bool canDoIntMath = op != JSOP_DIV && type != JSVAL_TYPE_DOUBLE &&
245 601878 : !(rhs->isType(JSVAL_TYPE_DOUBLE) || lhs->isType(JSVAL_TYPE_DOUBLE));
246 :
247 601878 : if (!masm.supportsFloatingPoint() && (!canDoIntMath || frame.haveSameBacking(lhs, rhs)))
248 0 : return jsop_binary_slow(op, stub, type, lhs, rhs);
249 :
250 601878 : if (canDoIntMath)
251 588997 : jsop_binary_full(lhs, rhs, op, stub, type, cannotOverflow, ignoreOverflow);
252 : else
253 12881 : jsop_binary_double(lhs, rhs, op, stub, type);
254 :
255 601878 : return true;
256 : }
257 :
258 : static void
259 576208 : EmitDoubleOp(JSOp op, FPRegisterID fpRight, FPRegisterID fpLeft, Assembler &masm)
260 : {
261 576208 : switch (op) {
262 : case JSOP_ADD:
263 528861 : masm.addDouble(fpRight, fpLeft);
264 528861 : break;
265 :
266 : case JSOP_SUB:
267 34440 : masm.subDouble(fpRight, fpLeft);
268 34440 : break;
269 :
270 : case JSOP_MUL:
271 4933 : masm.mulDouble(fpRight, fpLeft);
272 4933 : break;
273 :
274 : case JSOP_DIV:
275 7974 : masm.divDouble(fpRight, fpLeft);
276 7974 : break;
277 :
278 : default:
279 0 : JS_NOT_REACHED("unrecognized binary op");
280 : }
281 576208 : }
282 :
283 : mjit::MaybeJump
284 29947 : mjit::Compiler::loadDouble(FrameEntry *fe, FPRegisterID *fpReg, bool *allocated)
285 : {
286 29947 : MaybeJump notNumber;
287 :
288 29947 : if (!fe->isConstant() && fe->isType(JSVAL_TYPE_DOUBLE)) {
289 9698 : *fpReg = frame.tempFPRegForData(fe);
290 9698 : *allocated = false;
291 9698 : return notNumber;
292 : }
293 :
294 20249 : *fpReg = frame.allocFPReg();
295 20249 : *allocated = true;
296 :
297 20249 : if (fe->isConstant()) {
298 8904 : slowLoadConstantDouble(masm, fe, *fpReg);
299 11345 : } else if (!fe->isTypeKnown()) {
300 8942 : frame.tempRegForType(fe);
301 8942 : Jump j = frame.testDouble(Assembler::Equal, fe);
302 8942 : notNumber = frame.testInt32(Assembler::NotEqual, fe);
303 8942 : frame.convertInt32ToDouble(masm, fe, *fpReg);
304 8942 : Jump converted = masm.jump();
305 8942 : j.linkTo(masm.label(), &masm);
306 : // CANDIDATE
307 8942 : frame.loadDouble(fe, *fpReg, masm);
308 8942 : converted.linkTo(masm.label(), &masm);
309 : } else {
310 2403 : JS_ASSERT(fe->isType(JSVAL_TYPE_INT32));
311 2403 : frame.tempRegForData(fe);
312 2403 : frame.convertInt32ToDouble(masm, fe, *fpReg);
313 : }
314 :
315 20249 : return notNumber;
316 : }
317 :
318 : /*
319 : * This function emits a single fast-path for handling numerical arithmetic.
320 : * Unlike jsop_binary_full(), all integers are converted to doubles.
321 : */
322 : void
323 12881 : mjit::Compiler::jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op,
324 : VoidStub stub, JSValueType type)
325 : {
326 : FPRegisterID fpLeft, fpRight;
327 : bool allocateLeft, allocateRight;
328 :
329 12881 : MaybeJump lhsNotNumber = loadDouble(lhs, &fpLeft, &allocateLeft);
330 12881 : if (lhsNotNumber.isSet())
331 7072 : stubcc.linkExit(lhsNotNumber.get(), Uses(2));
332 :
333 : /* The left register holds the result, and needs to be mutable. */
334 12881 : if (!allocateLeft) {
335 3614 : FPRegisterID res = frame.allocFPReg();
336 3614 : masm.moveDouble(fpLeft, res);
337 3614 : fpLeft = res;
338 3614 : allocateLeft = true;
339 : }
340 :
341 12881 : MaybeJump rhsNotNumber;
342 12881 : if (frame.haveSameBacking(lhs, rhs)) {
343 41 : fpRight = fpLeft;
344 41 : allocateRight = false;
345 : } else {
346 12840 : rhsNotNumber = loadDouble(rhs, &fpRight, &allocateRight);
347 12840 : if (rhsNotNumber.isSet())
348 1639 : stubcc.linkExit(rhsNotNumber.get(), Uses(2));
349 : }
350 :
351 12881 : EmitDoubleOp(op, fpRight, fpLeft, masm);
352 :
353 12881 : MaybeJump done;
354 :
355 : /*
356 : * Try to convert result to integer, if the result has unknown or integer type.
357 : * Skip this for 1/x or -1/x, as the result is unlikely to fit in an int.
358 : */
359 26084 : if (op == JSOP_DIV &&
360 : (type == JSVAL_TYPE_INT32 ||
361 : (type == JSVAL_TYPE_UNKNOWN &&
362 6610 : !(lhs->isConstant() && lhs->isType(JSVAL_TYPE_INT32) &&
363 252 : abs(lhs->getValue().toInt32()) == 1)))) {
364 6845 : RegisterID reg = frame.allocReg();
365 6845 : FPRegisterID fpReg = frame.allocFPReg();
366 13690 : JumpList isDouble;
367 6845 : masm.branchConvertDoubleToInt32(fpLeft, reg, isDouble, fpReg);
368 :
369 : masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), reg,
370 6845 : frame.addressOf(lhs));
371 :
372 6845 : frame.freeReg(reg);
373 6845 : frame.freeReg(fpReg);
374 6845 : done.setJump(masm.jump());
375 :
376 6845 : isDouble.linkTo(masm.label(), &masm);
377 : }
378 :
379 : /*
380 : * Inference needs to know about any operation on integers that produces a
381 : * double result. Unless the pushed type set already contains the double
382 : * type, we need to call a stub rather than push. Note that looking at
383 : * the pushed type tag is not sufficient, as it will be UNKNOWN if
384 : * we do not yet know the possible types of the division's operands.
385 : */
386 12881 : types::TypeSet *resultTypes = pushedTypeSet(0);
387 12881 : if (resultTypes && !resultTypes->hasType(types::Type::DoubleType())) {
388 : /*
389 : * Call a stub and try harder to convert to int32, failing that trigger
390 : * recompilation of this script.
391 : */
392 950 : stubcc.linkExit(masm.jump(), Uses(2));
393 : } else {
394 11931 : JS_ASSERT(type != JSVAL_TYPE_INT32);
395 11931 : if (type != JSVAL_TYPE_DOUBLE)
396 6334 : masm.storeDouble(fpLeft, frame.addressOf(lhs));
397 : }
398 :
399 12881 : if (done.isSet())
400 6845 : done.getJump().linkTo(masm.label(), &masm);
401 :
402 12881 : stubcc.leave();
403 12881 : OOL_STUBCALL(stub, REJOIN_BINARY);
404 :
405 12881 : if (allocateRight)
406 9556 : frame.freeReg(fpRight);
407 :
408 12881 : frame.popn(2);
409 :
410 12881 : if (type == JSVAL_TYPE_DOUBLE) {
411 5597 : frame.pushDouble(fpLeft);
412 : } else {
413 7284 : frame.freeReg(fpLeft);
414 7284 : frame.pushSynced(type);
415 : }
416 :
417 12881 : stubcc.rejoin(Changes(1));
418 12881 : }
419 :
420 : /*
421 : * Simpler version of jsop_binary_full() for when lhs == rhs.
422 : */
423 : void
424 89 : mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub, JSValueType type)
425 : {
426 89 : FrameEntry *lhs = frame.peek(-2);
427 :
428 : /* Easiest case: known double. Don't bother conversion back yet? */
429 89 : if (fe->isType(JSVAL_TYPE_DOUBLE)) {
430 0 : FPRegisterID fpreg = frame.allocFPReg();
431 0 : FPRegisterID lhs = frame.tempFPRegForData(fe);
432 0 : masm.moveDouble(lhs, fpreg);
433 0 : EmitDoubleOp(op, fpreg, fpreg, masm);
434 0 : frame.popn(2);
435 :
436 0 : JS_ASSERT(type == JSVAL_TYPE_DOUBLE); /* :XXX: can fail */
437 0 : frame.pushDouble(fpreg);
438 0 : return;
439 : }
440 :
441 : /* Allocate all registers up-front. */
442 89 : FrameState::BinaryAlloc regs;
443 89 : frame.allocForSameBinary(fe, op, regs);
444 :
445 89 : MaybeJump notNumber;
446 89 : MaybeJump doublePathDone;
447 89 : if (!fe->isTypeKnown()) {
448 61 : Jump notInt = masm.testInt32(Assembler::NotEqual, regs.lhsType.reg());
449 61 : stubcc.linkExitDirect(notInt, stubcc.masm.label());
450 :
451 61 : notNumber = stubcc.masm.testDouble(Assembler::NotEqual, regs.lhsType.reg());
452 61 : frame.loadDouble(fe, regs.lhsFP, stubcc.masm);
453 61 : EmitDoubleOp(op, regs.lhsFP, regs.lhsFP, stubcc.masm);
454 :
455 : /* Force the double back to memory. */
456 61 : Address result = frame.addressOf(lhs);
457 61 : stubcc.masm.storeDouble(regs.lhsFP, result);
458 :
459 : /* Load the payload into the result reg so the rejoin is safe. */
460 61 : stubcc.masm.loadPayload(result, regs.result);
461 :
462 61 : doublePathDone = stubcc.masm.jump();
463 : }
464 :
465 : /* Okay - good to emit the integer fast-path. */
466 89 : MaybeJump overflow;
467 89 : switch (op) {
468 : case JSOP_ADD:
469 35 : overflow = masm.branchAdd32(Assembler::Overflow, regs.result, regs.result);
470 35 : break;
471 :
472 : case JSOP_SUB:
473 0 : overflow = masm.branchSub32(Assembler::Overflow, regs.result, regs.result);
474 0 : break;
475 :
476 : case JSOP_MUL:
477 54 : overflow = masm.branchMul32(Assembler::Overflow, regs.result, regs.result);
478 54 : break;
479 :
480 : default:
481 0 : JS_NOT_REACHED("unrecognized op");
482 : }
483 :
484 89 : JS_ASSERT(overflow.isSet());
485 :
486 : /*
487 : * Integer overflow path. Restore the original values and make a stub call,
488 : * which could trigger recompilation.
489 : */
490 89 : stubcc.linkExitDirect(overflow.get(), stubcc.masm.label());
491 89 : frame.rematBinary(fe, NULL, regs, stubcc.masm);
492 89 : stubcc.syncExitAndJump(Uses(2));
493 :
494 : /* Slow paths funnel here. */
495 89 : if (notNumber.isSet())
496 61 : notNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
497 :
498 : /* Slow call - use frame.sync to avoid erroneous jump repatching in stubcc. */
499 89 : frame.sync(stubcc.masm, Uses(2));
500 89 : stubcc.leave();
501 89 : OOL_STUBCALL(stub, REJOIN_BINARY);
502 :
503 : /* Finish up stack operations. */
504 89 : frame.popn(2);
505 :
506 89 : if (type == JSVAL_TYPE_INT32)
507 28 : frame.pushTypedPayload(type, regs.result);
508 : else
509 61 : frame.pushNumber(regs.result, true);
510 :
511 89 : frame.freeReg(regs.lhsFP);
512 :
513 : /* Merge back OOL double path. */
514 89 : if (doublePathDone.isSet())
515 61 : stubcc.linkRejoin(doublePathDone.get());
516 :
517 89 : stubcc.rejoin(Changes(1));
518 : }
519 :
520 : /*
521 : * This function emits multiple fast-paths for handling numerical arithmetic.
522 : * Currently, it handles only ADD, SUB, and MUL, where both LHS and RHS are
523 : * known not to be doubles.
524 : *
525 : * The control flow of the emitted code depends on which types are known.
526 : * Given both types are unknown, the full spread looks like:
527 : *
528 : * Inline OOL
529 : * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
530 : * Is LHS Int32? ------ No --------> Is LHS Double? ----- No -------,
531 : * Sync LHS |
532 : * Load LHS into XMM1 |
533 : * Is RHS Double? ---- Yes --, |
534 : * Is RHS Int32? ---- No --|-----|
535 : * Convert RHS into XMM0 | |
536 : * Else <-------------------' |
537 : * Sync RHS |
538 : * Load RHS into XMM0 |
539 : * [Add,Sub,Mul] XMM0,XMM1 |
540 : * Jump ---------------------, |
541 : * | |
542 : * Is RHS Int32? ------ No -------> Is RHS Double? ----- No --|-----|
543 : * Sync RHS | |
544 : * Load RHS into XMM0 | |
545 : * Convert LHS into XMM1 | |
546 : * [Add,Sub,Mul] XMM0,XMM1 | |
547 : * Jump ---------------------| Slow Call
548 : * |
549 : * [Add,Sub,Mul] RHS, LHS |
550 : * Overflow ------ Yes -------> Convert RHS into XMM0 |
551 : * Convert LHS into XMM1 |
552 : * [Add,Sub,Mul] XMM0,XMM1 |
553 : * Sync XMM1 to stack <---'
554 : * <--------------------------------- Rejoin
555 : */
556 : void
557 588997 : mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op,
558 : VoidStub stub, JSValueType type,
559 : bool cannotOverflow, bool ignoreOverflow)
560 : {
561 588997 : if (frame.haveSameBacking(lhs, rhs)) {
562 89 : jsop_binary_full_simple(lhs, op, stub, type);
563 89 : return;
564 : }
565 :
566 : /* Allocate all registers up-front. */
567 588908 : FrameState::BinaryAlloc regs;
568 588908 : frame.allocForBinary(lhs, rhs, op, regs);
569 :
570 : /* Quick-test some invariants. */
571 588908 : JS_ASSERT_IF(lhs->isTypeKnown(), lhs->getKnownType() == JSVAL_TYPE_INT32);
572 588908 : JS_ASSERT_IF(rhs->isTypeKnown(), rhs->getKnownType() == JSVAL_TYPE_INT32);
573 :
574 588908 : MaybeJump lhsNotDouble, rhsNotNumber, lhsUnknownDone;
575 588908 : if (!lhs->isTypeKnown())
576 561916 : emitLeftDoublePath(lhs, rhs, regs, lhsNotDouble, rhsNotNumber, lhsUnknownDone);
577 :
578 588908 : MaybeJump rhsNotNumber2;
579 588908 : if (!rhs->isTypeKnown())
580 321793 : emitRightDoublePath(lhs, rhs, regs, rhsNotNumber2);
581 :
582 : /* Perform the double addition. */
583 588908 : MaybeJump doublePathDone;
584 588908 : if (masm.supportsFloatingPoint() && (!rhs->isTypeKnown() || !lhs->isTypeKnown())) {
585 : /* If the LHS type was not known, link its path here. */
586 563266 : if (lhsUnknownDone.isSet())
587 561916 : lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
588 :
589 : /* Perform the double operation. */
590 563266 : EmitDoubleOp(op, regs.rhsFP, regs.lhsFP, stubcc.masm);
591 :
592 : /* Force the double back to memory. */
593 563266 : Address result = frame.addressOf(lhs);
594 563266 : stubcc.masm.storeDouble(regs.lhsFP, result);
595 :
596 : /* Load the payload into the result reg so the rejoin is safe. */
597 563266 : stubcc.masm.loadPayload(result, regs.result);
598 :
599 : /* We'll link this back up later, at the bottom of the op. */
600 563266 : doublePathDone = stubcc.masm.jump();
601 : }
602 :
603 : /* Time to do the integer path. Figure out the immutable side. */
604 588908 : int32_t value = 0;
605 588908 : JSOp origOp = op;
606 588908 : MaybeRegisterID reg;
607 588908 : MaybeJump preOverflow;
608 588908 : if (!regs.resultHasRhs) {
609 586730 : if (!regs.rhsData.isSet())
610 262482 : value = rhs->getValue().toInt32();
611 : else
612 324248 : reg = regs.rhsData.reg();
613 : } else {
614 2178 : if (!regs.lhsData.isSet())
615 1426 : value = lhs->getValue().toInt32();
616 : else
617 752 : reg = regs.lhsData.reg();
618 2178 : if (op == JSOP_SUB) {
619 : // If the RHS is 0x80000000, the smallest negative value, neg does
620 : // not work. Guard against this and treat it as an overflow.
621 598 : preOverflow = masm.branch32(Assembler::Equal, regs.result, Imm32(0x80000000));
622 598 : masm.neg32(regs.result);
623 598 : op = JSOP_ADD;
624 : }
625 : }
626 :
627 : /* Okay - good to emit the integer fast-path. */
628 588908 : MaybeJump overflow;
629 588908 : switch (op) {
630 : case JSOP_ADD:
631 537928 : if (cannotOverflow || ignoreOverflow) {
632 460 : if (reg.isSet())
633 118 : masm.add32(reg.reg(), regs.result);
634 : else
635 112 : masm.add32(Imm32(value), regs.result);
636 : } else {
637 537698 : if (reg.isSet())
638 319018 : overflow = masm.branchAdd32(Assembler::Overflow, reg.reg(), regs.result);
639 : else
640 218680 : overflow = masm.branchAdd32(Assembler::Overflow, Imm32(value), regs.result);
641 : }
642 537928 : break;
643 :
644 : case JSOP_SUB:
645 47214 : if (cannotOverflow) {
646 1118 : if (reg.isSet())
647 0 : masm.sub32(reg.reg(), regs.result);
648 : else
649 1118 : masm.sub32(Imm32(value), regs.result);
650 : } else {
651 46096 : if (reg.isSet())
652 3979 : overflow = masm.branchSub32(Assembler::Overflow, reg.reg(), regs.result);
653 : else
654 42117 : overflow = masm.branchSub32(Assembler::Overflow, Imm32(value), regs.result);
655 : }
656 47214 : break;
657 :
658 : case JSOP_MUL:
659 : {
660 3766 : MaybeJump storeNegZero;
661 3766 : bool maybeNegZero = !ignoreOverflow;
662 3766 : bool hasConstant = (lhs->isConstant() || rhs->isConstant());
663 :
664 3766 : if (hasConstant && maybeNegZero) {
665 1878 : value = (lhs->isConstant() ? lhs : rhs)->getValue().toInt32();
666 1878 : RegisterID nonConstReg = lhs->isConstant() ? regs.rhsData.reg() : regs.lhsData.reg();
667 :
668 1878 : if (value > 0)
669 1813 : maybeNegZero = false;
670 65 : else if (value < 0)
671 57 : storeNegZero = masm.branchTest32(Assembler::Zero, nonConstReg);
672 : else
673 8 : storeNegZero = masm.branch32(Assembler::LessThan, nonConstReg, Imm32(0));
674 : }
675 :
676 3766 : if (cannotOverflow) {
677 12 : if (reg.isSet())
678 12 : masm.mul32(reg.reg(), regs.result);
679 : else
680 0 : masm.mul32(Imm32(value), regs.result, regs.result);
681 : } else {
682 3754 : if (reg.isSet()) {
683 1873 : overflow = masm.branchMul32(Assembler::Overflow, reg.reg(), regs.result);
684 : } else {
685 : overflow = masm.branchMul32(Assembler::Overflow, Imm32(value), regs.result,
686 1881 : regs.result);
687 : }
688 : }
689 :
690 3766 : if (maybeNegZero) {
691 1942 : if (hasConstant) {
692 65 : stubcc.linkExit(storeNegZero.get(), Uses(2));
693 : } else {
694 1877 : Jump isZero = masm.branchTest32(Assembler::Zero, regs.result);
695 1877 : stubcc.linkExitDirect(isZero, stubcc.masm.label());
696 :
697 : /* Restore original value. */
698 1877 : if (regs.resultHasRhs) {
699 233 : if (regs.rhsNeedsRemat)
700 233 : stubcc.masm.loadPayload(frame.addressForDataRemat(rhs), regs.result);
701 : else
702 0 : stubcc.masm.move(regs.rhsData.reg(), regs.result);
703 : } else {
704 1644 : if (regs.lhsNeedsRemat)
705 481 : stubcc.masm.loadPayload(frame.addressForDataRemat(lhs), regs.result);
706 : else
707 1163 : stubcc.masm.move(regs.lhsData.reg(), regs.result);
708 : }
709 1877 : storeNegZero = stubcc.masm.branchOr32(Assembler::Signed, reg.reg(), regs.result);
710 1877 : stubcc.masm.xor32(regs.result, regs.result);
711 1877 : stubcc.crossJump(stubcc.masm.jump(), masm.label());
712 1877 : storeNegZero.getJump().linkTo(stubcc.masm.label(), &stubcc.masm);
713 1877 : frame.rematBinary(lhs, rhs, regs, stubcc.masm);
714 : }
715 1942 : stubcc.syncExitAndJump(Uses(2));
716 : }
717 3766 : break;
718 : }
719 :
720 : default:
721 0 : JS_NOT_REACHED("unrecognized op");
722 : }
723 588908 : op = origOp;
724 :
725 : /*
726 : * Integer overflow path. Restore the original values and make a stub call,
727 : * which could trigger recompilation.
728 : */
729 588908 : MaybeJump overflowDone;
730 588908 : if (preOverflow.isSet())
731 598 : stubcc.linkExitDirect(preOverflow.get(), stubcc.masm.label());
732 588908 : if (overflow.isSet())
733 587548 : stubcc.linkExitDirect(overflow.get(), stubcc.masm.label());
734 :
735 : /* Restore the original operand registers for ADD. */
736 588908 : if (regs.undoResult) {
737 40964 : if (reg.isSet()) {
738 8639 : JS_ASSERT(op == JSOP_ADD);
739 8639 : stubcc.masm.neg32(reg.reg());
740 8639 : stubcc.masm.add32(reg.reg(), regs.result);
741 8639 : stubcc.masm.neg32(reg.reg());
742 : } else {
743 32325 : JS_ASSERT(op == JSOP_ADD || op == JSOP_SUB);
744 32325 : int32_t fixValue = (op == JSOP_ADD) ? -value : value;
745 32325 : stubcc.masm.add32(Imm32(fixValue), regs.result);
746 : }
747 : }
748 :
749 588908 : frame.rematBinary(lhs, rhs, regs, stubcc.masm);
750 588908 : stubcc.syncExitAndJump(Uses(2));
751 :
752 : /* The register allocator creates at most one temporary. */
753 588908 : if (regs.extraFree.isSet())
754 0 : frame.freeReg(regs.extraFree.reg());
755 :
756 : /* Slow paths funnel here. */
757 588908 : if (lhsNotDouble.isSet()) {
758 561916 : lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
759 561916 : if (rhsNotNumber.isSet())
760 320443 : rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
761 : }
762 588908 : if (rhsNotNumber2.isSet())
763 321793 : rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm);
764 :
765 : /* Slow call - use frame.sync to avoid erroneous jump repatching in stubcc. */
766 588908 : frame.sync(stubcc.masm, Uses(2));
767 588908 : stubcc.leave();
768 588908 : OOL_STUBCALL(stub, REJOIN_BINARY);
769 :
770 : /* Finish up stack operations. */
771 588908 : frame.popn(2);
772 :
773 : /*
774 : * Steal the result register if we remat the LHS/RHS by undoing the operation.
775 : * In this case the result register was still assigned to the corresponding
776 : * frame entry (so it is synced properly in OOL paths), so steal it back.
777 : */
778 588908 : if (regs.undoResult)
779 40964 : frame.takeReg(regs.result);
780 :
781 588908 : if (type == JSVAL_TYPE_INT32)
782 29740 : frame.pushTypedPayload(type, regs.result);
783 : else
784 559168 : frame.pushNumber(regs.result, true);
785 :
786 588908 : frame.freeReg(regs.lhsFP);
787 588908 : frame.freeReg(regs.rhsFP);
788 :
789 : /* Merge back OOL double path. */
790 588908 : if (doublePathDone.isSet())
791 563266 : stubcc.linkRejoin(doublePathDone.get());
792 :
793 588908 : stubcc.rejoin(Changes(1));
794 : }
795 :
796 : void
797 3786 : mjit::Compiler::jsop_neg()
798 : {
799 3786 : FrameEntry *fe = frame.peek(-1);
800 3786 : JSValueType type = knownPushedType(0);
801 :
802 7468 : if ((fe->isTypeKnown() && fe->getKnownType() > JSVAL_UPPER_INCL_TYPE_OF_NUMBER_SET) ||
803 3682 : !masm.supportsFloatingPoint())
804 : {
805 104 : prepareStubCall(Uses(1));
806 104 : INLINE_STUBCALL(stubs::Neg, REJOIN_FALLTHROUGH);
807 104 : frame.pop();
808 104 : frame.pushSynced(type);
809 104 : return;
810 : }
811 :
812 3682 : JS_ASSERT(!fe->isConstant());
813 :
814 : /* Handle negation of a known double, or of a known integer which has previously overflowed. */
815 6560 : if (fe->isType(JSVAL_TYPE_DOUBLE) ||
816 2878 : (fe->isType(JSVAL_TYPE_INT32) && type == JSVAL_TYPE_DOUBLE))
817 : {
818 : FPRegisterID fpreg;
819 865 : if (fe->isType(JSVAL_TYPE_DOUBLE)) {
820 804 : fpreg = frame.tempFPRegForData(fe);
821 : } else {
822 61 : fpreg = frame.allocFPReg();
823 61 : frame.convertInt32ToDouble(masm, fe, fpreg);
824 : }
825 :
826 865 : FPRegisterID res = frame.allocFPReg();
827 865 : masm.moveDouble(fpreg, res);
828 865 : masm.negateDouble(res);
829 :
830 865 : if (!fe->isType(JSVAL_TYPE_DOUBLE))
831 61 : frame.freeReg(fpreg);
832 :
833 865 : frame.pop();
834 865 : frame.pushDouble(res);
835 :
836 865 : return;
837 : }
838 :
839 : /* Inline integer path for known integers. */
840 2817 : if (fe->isType(JSVAL_TYPE_INT32) && type == JSVAL_TYPE_INT32) {
841 432 : RegisterID reg = frame.copyDataIntoReg(fe);
842 :
843 : /* Test for 0 and -2147483648 (both result in a double). */
844 432 : Jump zeroOrMinInt = masm.branchTest32(Assembler::Zero, reg, Imm32(0x7fffffff));
845 432 : stubcc.linkExit(zeroOrMinInt, Uses(1));
846 :
847 432 : masm.neg32(reg);
848 :
849 432 : stubcc.leave();
850 432 : OOL_STUBCALL(stubs::Neg, REJOIN_FALLTHROUGH);
851 :
852 432 : frame.pop();
853 432 : frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
854 :
855 432 : stubcc.rejoin(Changes(1));
856 432 : return;
857 : }
858 :
859 : /* Load type information into register. */
860 2385 : MaybeRegisterID feTypeReg;
861 2385 : if (!fe->isTypeKnown() && !frame.shouldAvoidTypeRemat(fe)) {
862 : /* Safe because only one type is loaded. */
863 2354 : feTypeReg.setReg(frame.tempRegForType(fe));
864 :
865 : /* Don't get clobbered by copyDataIntoReg(). */
866 2354 : frame.pinReg(feTypeReg.reg());
867 : }
868 :
869 2385 : RegisterID reg = frame.copyDataIntoReg(masm, fe);
870 2385 : Label feSyncTarget = stubcc.syncExitAndJump(Uses(1));
871 :
872 : /* Try a double path (inline). */
873 2385 : MaybeJump jmpNotDbl;
874 : {
875 2385 : maybeJumpIfNotDouble(masm, jmpNotDbl, fe, feTypeReg);
876 :
877 2385 : FPRegisterID fpreg = frame.allocFPReg();
878 2385 : frame.loadDouble(fe, fpreg, masm);
879 2385 : masm.negateDouble(fpreg);
880 :
881 : /* Overwrite pushed frame's memory (before push). */
882 2385 : masm.storeDouble(fpreg, frame.addressOf(fe));
883 2385 : frame.freeReg(fpreg);
884 : }
885 :
886 : /* Try an integer path (out-of-line). */
887 2385 : MaybeJump jmpNotInt;
888 2385 : MaybeJump jmpMinIntOrIntZero;
889 2385 : MaybeJump jmpIntRejoin;
890 2385 : Label lblIntPath = stubcc.masm.label();
891 : {
892 2385 : maybeJumpIfNotInt32(stubcc.masm, jmpNotInt, fe, feTypeReg);
893 :
894 : /* Test for 0 and -2147483648 (both result in a double). */
895 2385 : jmpMinIntOrIntZero = stubcc.masm.branchTest32(Assembler::Zero, reg, Imm32(0x7fffffff));
896 :
897 2385 : stubcc.masm.neg32(reg);
898 :
899 : /* Sync back with double path. */
900 2385 : if (type == JSVAL_TYPE_DOUBLE) {
901 41 : stubcc.masm.convertInt32ToDouble(reg, Registers::FPConversionTemp);
902 41 : stubcc.masm.storeDouble(Registers::FPConversionTemp, frame.addressOf(fe));
903 : } else {
904 : stubcc.masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), reg,
905 2344 : frame.addressOf(fe));
906 : }
907 :
908 2385 : jmpIntRejoin.setJump(stubcc.masm.jump());
909 : }
910 :
911 2385 : frame.freeReg(reg);
912 2385 : if (feTypeReg.isSet())
913 2354 : frame.unpinReg(feTypeReg.reg());
914 :
915 2385 : stubcc.leave();
916 2385 : OOL_STUBCALL(stubs::Neg, REJOIN_FALLTHROUGH);
917 :
918 2385 : frame.pop();
919 2385 : frame.pushSynced(type);
920 :
921 : /* Link jumps. */
922 2385 : if (jmpNotDbl.isSet())
923 2385 : stubcc.linkExitDirect(jmpNotDbl.getJump(), lblIntPath);
924 :
925 2385 : if (jmpNotInt.isSet())
926 2382 : jmpNotInt.getJump().linkTo(feSyncTarget, &stubcc.masm);
927 2385 : if (jmpMinIntOrIntZero.isSet())
928 2385 : jmpMinIntOrIntZero.getJump().linkTo(feSyncTarget, &stubcc.masm);
929 2385 : if (jmpIntRejoin.isSet())
930 2385 : stubcc.crossJump(jmpIntRejoin.getJump(), masm.label());
931 :
932 2385 : stubcc.rejoin(Changes(1));
933 : }
934 :
935 : bool
936 3895 : mjit::Compiler::jsop_mod()
937 : {
938 : #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
939 3895 : JSValueType type = knownPushedType(0);
940 3895 : FrameEntry *lhs = frame.peek(-2);
941 3895 : FrameEntry *rhs = frame.peek(-1);
942 :
943 : Value v;
944 3895 : if (tryBinaryConstantFold(cx, frame, JSOP_MOD, lhs, rhs, &v)) {
945 8 : types::TypeSet *pushed = pushedTypeSet(0);
946 8 : if (!v.isInt32() && pushed && !pushed->hasType(types::Type::DoubleType())) {
947 2 : types::TypeScript::MonitorOverflow(cx, script, PC);
948 2 : return false;
949 : }
950 6 : frame.popn(2);
951 6 : frame.push(v);
952 6 : return true;
953 : }
954 :
955 15895 : if ((lhs->isConstant() && rhs->isConstant()) ||
956 4487 : (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_INT32) ||
957 7521 : (rhs->isTypeKnown() && rhs->getKnownType() != JSVAL_TYPE_INT32) ||
958 : (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_UNKNOWN))
959 : #endif
960 : {
961 74 : prepareStubCall(Uses(2));
962 74 : INLINE_STUBCALL(stubs::Mod, REJOIN_FALLTHROUGH);
963 74 : frame.popn(2);
964 74 : frame.pushSynced(knownPushedType(0));
965 74 : return true;
966 : }
967 :
968 : #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
969 3813 : if (!lhs->isTypeKnown()) {
970 3271 : Jump j = frame.testInt32(Assembler::NotEqual, lhs);
971 3271 : stubcc.linkExit(j, Uses(2));
972 : }
973 3813 : if (!rhs->isTypeKnown()) {
974 158 : Jump j = frame.testInt32(Assembler::NotEqual, rhs);
975 158 : stubcc.linkExit(j, Uses(2));
976 : }
977 :
978 : /* LHS must be in EAX:EDX */
979 3813 : if (!lhs->isConstant()) {
980 3805 : frame.copyDataIntoReg(lhs, X86Registers::eax);
981 : } else {
982 8 : frame.takeReg(X86Registers::eax);
983 8 : masm.move(Imm32(lhs->getValue().toInt32()), X86Registers::eax);
984 : }
985 :
986 : /* Get RHS into anything but EDX - could avoid more spilling? */
987 3813 : MaybeRegisterID temp;
988 : RegisterID rhsReg;
989 3813 : uint32_t mask = Registers::AvailRegs & ~Registers::maskReg(X86Registers::edx);
990 3813 : if (!rhs->isConstant()) {
991 303 : rhsReg = frame.tempRegInMaskForData(rhs, mask).reg();
992 303 : JS_ASSERT(rhsReg != X86Registers::edx);
993 : } else {
994 3510 : rhsReg = frame.allocReg(mask).reg();
995 3510 : JS_ASSERT(rhsReg != X86Registers::edx);
996 3510 : masm.move(Imm32(rhs->getValue().toInt32()), rhsReg);
997 3510 : temp = rhsReg;
998 : }
999 3813 : frame.takeReg(X86Registers::edx);
1000 3813 : frame.freeReg(X86Registers::eax);
1001 :
1002 3813 : if (temp.isSet())
1003 3510 : frame.freeReg(temp.reg());
1004 :
1005 3813 : bool slowPath = !(lhs->isTypeKnown() && rhs->isTypeKnown());
1006 3813 : if (rhs->isConstant() && rhs->getValue().toInt32() != 0) {
1007 3504 : if (rhs->getValue().toInt32() == -1) {
1008 : /* Guard against -1 / INT_MIN which throws a hardware exception. */
1009 : Jump checkDivExc = masm.branch32(Assembler::Equal, X86Registers::eax,
1010 4 : Imm32(0x80000000));
1011 4 : stubcc.linkExit(checkDivExc, Uses(2));
1012 4 : slowPath = true;
1013 : }
1014 : } else {
1015 309 : Jump checkDivExc = masm.branch32(Assembler::Equal, X86Registers::eax, Imm32(0x80000000));
1016 309 : stubcc.linkExit(checkDivExc, Uses(2));
1017 309 : Jump checkZero = masm.branchTest32(Assembler::Zero, rhsReg, rhsReg);
1018 309 : stubcc.linkExit(checkZero, Uses(2));
1019 309 : slowPath = true;
1020 : }
1021 :
1022 : /* Perform division. */
1023 3813 : masm.idiv(rhsReg);
1024 :
1025 : /* ECMA-262 11.5.3 requires the result to have the same sign as the lhs.
1026 : * Thus, if the remainder of the div instruction is zero and the lhs is
1027 : * negative, we must return negative 0. */
1028 :
1029 3813 : bool lhsMaybeNeg = true;
1030 3813 : bool lhsIsNeg = false;
1031 3813 : if (lhs->isConstant()) {
1032 : /* This condition is established at the top of this function. */
1033 8 : JS_ASSERT(lhs->getValue().isInt32());
1034 8 : lhsMaybeNeg = lhsIsNeg = (lhs->getValue().toInt32() < 0);
1035 : }
1036 :
1037 3813 : MaybeJump gotNegZero;
1038 3813 : MaybeJump done;
1039 3813 : if (lhsMaybeNeg) {
1040 3805 : MaybeRegisterID lhsData;
1041 3805 : if (!lhsIsNeg)
1042 3805 : lhsData = frame.tempRegForData(lhs);
1043 3805 : Jump negZero1 = masm.branchTest32(Assembler::NonZero, X86Registers::edx);
1044 3805 : MaybeJump negZero2;
1045 3805 : if (!lhsIsNeg)
1046 3805 : negZero2 = masm.branchTest32(Assembler::Zero, lhsData.reg(), Imm32(0x80000000));
1047 : /*
1048 : * Darn, negative 0. This goes to a stub call (after our in progress call)
1049 : * which triggers recompilation if necessary.
1050 : */
1051 3805 : gotNegZero = masm.jump();
1052 :
1053 : /* :TODO: This is wrong, must load into EDX as well. */
1054 :
1055 3805 : done = masm.jump();
1056 3805 : negZero1.linkTo(masm.label(), &masm);
1057 3805 : if (negZero2.isSet())
1058 3805 : negZero2.getJump().linkTo(masm.label(), &masm);
1059 : }
1060 :
1061 : /* Better - integer. */
1062 3813 : masm.storeTypeTag(ImmType(JSVAL_TYPE_INT32), frame.addressOf(lhs));
1063 :
1064 3813 : if (done.isSet())
1065 3805 : done.getJump().linkTo(masm.label(), &masm);
1066 :
1067 3813 : if (slowPath) {
1068 3472 : stubcc.leave();
1069 3472 : OOL_STUBCALL(stubs::Mod, REJOIN_FALLTHROUGH);
1070 : }
1071 :
1072 3813 : frame.popn(2);
1073 :
1074 3813 : if (type == JSVAL_TYPE_INT32)
1075 749 : frame.pushTypedPayload(type, X86Registers::edx);
1076 : else
1077 3064 : frame.pushNumber(X86Registers::edx);
1078 :
1079 3813 : if (slowPath)
1080 3472 : stubcc.rejoin(Changes(1));
1081 :
1082 3813 : if (gotNegZero.isSet()) {
1083 3805 : stubcc.linkExit(gotNegZero.getJump(), Uses(2));
1084 3805 : stubcc.leave();
1085 3805 : OOL_STUBCALL(stubs::NegZeroHelper, REJOIN_FALLTHROUGH);
1086 3805 : stubcc.rejoin(Changes(1));
1087 : }
1088 : #endif
1089 :
1090 3813 : return true;
1091 : }
1092 :
1093 : bool
1094 19019 : mjit::Compiler::jsop_equality_int_string(JSOp op, BoolStub stub,
1095 : jsbytecode *target, JSOp fused)
1096 : {
1097 19019 : FrameEntry *rhs = frame.peek(-1);
1098 19019 : FrameEntry *lhs = frame.peek(-2);
1099 :
1100 : /* Swap the LHS and RHS if it makes register allocation better... or possible. */
1101 40489 : if (lhs->isConstant() ||
1102 21470 : (frame.shouldAvoidDataRemat(lhs) && !rhs->isConstant())) {
1103 2433 : FrameEntry *temp = rhs;
1104 2433 : rhs = lhs;
1105 2433 : lhs = temp;
1106 : }
1107 :
1108 19019 : bool lhsInt = lhs->isType(JSVAL_TYPE_INT32);
1109 19019 : bool rhsInt = rhs->isType(JSVAL_TYPE_INT32);
1110 :
1111 : /* Invert the condition if fusing with an IFEQ branch. */
1112 19019 : bool flipCondition = (target && fused == JSOP_IFEQ);
1113 :
1114 : /* Get the condition being tested. */
1115 : Assembler::Condition cond;
1116 19019 : switch (op) {
1117 : case JSOP_EQ:
1118 14442 : cond = flipCondition ? Assembler::NotEqual : Assembler::Equal;
1119 14442 : break;
1120 : case JSOP_NE:
1121 4577 : cond = flipCondition ? Assembler::Equal : Assembler::NotEqual;
1122 4577 : break;
1123 : default:
1124 0 : JS_NOT_REACHED("wat");
1125 : return false;
1126 : }
1127 :
1128 19019 : if (target) {
1129 13071 : Value rval = UndefinedValue(); /* quiet gcc warning */
1130 13071 : bool rhsConst = false;
1131 13071 : if (rhs->isConstant()) {
1132 6869 : rhsConst = true;
1133 6869 : rval = rhs->getValue();
1134 : }
1135 :
1136 : ValueRemat lvr, rvr;
1137 13071 : frame.pinEntry(lhs, lvr);
1138 13071 : frame.pinEntry(rhs, rvr);
1139 :
1140 : /*
1141 : * Sync everything except the top two entries.
1142 : * We will handle the lhs/rhs in the stub call path.
1143 : */
1144 13071 : frame.syncAndKill(Registers(Registers::AvailRegs), Uses(frame.frameSlots()), Uses(2));
1145 :
1146 13071 : RegisterID tempReg = frame.allocReg();
1147 :
1148 13071 : JaegerSpew(JSpew_Insns, " ---- BEGIN STUB CALL CODE ---- \n");
1149 :
1150 : RESERVE_OOL_SPACE(stubcc.masm);
1151 :
1152 : /* Start of the slow path for equality stub call. */
1153 13071 : Label stubEntry = stubcc.masm.label();
1154 :
1155 : /* The lhs/rhs need to be synced in the stub call path. */
1156 13071 : frame.ensureValueSynced(stubcc.masm, lhs, lvr);
1157 13071 : frame.ensureValueSynced(stubcc.masm, rhs, rvr);
1158 :
1159 13071 : bool needIntPath = (!lhs->isTypeKnown() || lhsInt) && (!rhs->isTypeKnown() || rhsInt);
1160 :
1161 13071 : frame.pop();
1162 13071 : frame.pop();
1163 13071 : frame.discardFrame();
1164 :
1165 13071 : bool needStub = true;
1166 :
1167 : #ifdef JS_MONOIC
1168 13071 : EqualityGenInfo ic;
1169 :
1170 13071 : ic.cond = cond;
1171 13071 : ic.tempReg = tempReg;
1172 13071 : ic.lvr = lvr;
1173 13071 : ic.rvr = rvr;
1174 13071 : ic.stubEntry = stubEntry;
1175 13071 : ic.stub = stub;
1176 :
1177 13071 : bool useIC = !a->parent && bytecodeInChunk(target);
1178 :
1179 : /* Call the IC stub, which may generate a fast path. */
1180 13071 : if (useIC) {
1181 : /* Adjust for the two values just pushed. */
1182 12842 : ic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1);
1183 12842 : ic.stubCall = OOL_STUBCALL_LOCAL_SLOTS(ic::Equality, REJOIN_BRANCH,
1184 12842 : frame.totalDepth() + 2);
1185 12842 : needStub = false;
1186 : }
1187 : #endif
1188 :
1189 13071 : if (needStub)
1190 229 : OOL_STUBCALL_LOCAL_SLOTS(stub, REJOIN_BRANCH, frame.totalDepth() + 2);
1191 :
1192 : /*
1193 : * The stub call has no need to rejoin, since state is synced.
1194 : * Instead, we can just test the return value.
1195 : */
1196 : Jump stubBranch = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
1197 13071 : Registers::ReturnReg, Registers::ReturnReg);
1198 13071 : Jump stubFallthrough = stubcc.masm.jump();
1199 :
1200 13071 : JaegerSpew(JSpew_Insns, " ---- END STUB CALL CODE ---- \n");
1201 : CHECK_OOL_SPACE();
1202 :
1203 13071 : Jump fast;
1204 13071 : MaybeJump firstStubJump;
1205 :
1206 13071 : if (needIntPath) {
1207 9012 : if (!lhsInt) {
1208 7026 : Jump lhsFail = masm.testInt32(Assembler::NotEqual, lvr.typeReg());
1209 7026 : stubcc.linkExitDirect(lhsFail, stubEntry);
1210 7026 : firstStubJump = lhsFail;
1211 : }
1212 9012 : if (!rhsInt) {
1213 5142 : Jump rhsFail = masm.testInt32(Assembler::NotEqual, rvr.typeReg());
1214 5142 : stubcc.linkExitDirect(rhsFail, stubEntry);
1215 5142 : if (!firstStubJump.isSet())
1216 23 : firstStubJump = rhsFail;
1217 : }
1218 :
1219 9012 : if (rhsConst)
1220 2856 : fast = masm.branch32(cond, lvr.dataReg(), Imm32(rval.toInt32()));
1221 : else
1222 6156 : fast = masm.branch32(cond, lvr.dataReg(), rvr.dataReg());
1223 : } else {
1224 4059 : Jump j = masm.jump();
1225 4059 : stubcc.linkExitDirect(j, stubEntry);
1226 4059 : firstStubJump = j;
1227 :
1228 : /* This is just a dummy jump. */
1229 4059 : fast = masm.jump();
1230 : }
1231 :
1232 : /* Jump from the stub call fallthrough to here. */
1233 13071 : stubcc.crossJump(stubFallthrough, masm.label());
1234 :
1235 13071 : bool *ptrampoline = NULL;
1236 : #ifdef JS_MONOIC
1237 : /* Remember the stub label in case there is a trampoline for the IC. */
1238 13071 : ic.trampoline = false;
1239 13071 : ic.trampolineStart = stubcc.masm.label();
1240 13071 : if (useIC)
1241 12842 : ptrampoline = &ic.trampoline;
1242 : #endif
1243 :
1244 : /*
1245 : * NB: jumpAndRun emits to the OOL path, so make sure not to use it
1246 : * in the middle of an in-progress slow path.
1247 : */
1248 13071 : if (!jumpAndRun(fast, target, &stubBranch, ptrampoline))
1249 0 : return false;
1250 :
1251 : #ifdef JS_MONOIC
1252 13071 : if (useIC) {
1253 12842 : ic.jumpToStub = firstStubJump;
1254 12842 : ic.fallThrough = masm.label();
1255 12842 : ic.jumpTarget = target;
1256 12842 : equalityICs.append(ic);
1257 : }
1258 : #endif
1259 :
1260 : } else {
1261 : /* No fusing. Compare, set, and push a boolean. */
1262 :
1263 : /* Should have filtered these out in the caller. */
1264 5948 : JS_ASSERT(!lhs->isType(JSVAL_TYPE_STRING) && !rhs->isType(JSVAL_TYPE_STRING));
1265 :
1266 : /* Test the types. */
1267 5948 : if ((lhs->isTypeKnown() && !lhsInt) || (rhs->isTypeKnown() && !rhsInt)) {
1268 0 : stubcc.linkExit(masm.jump(), Uses(2));
1269 : } else {
1270 5948 : if (!lhsInt) {
1271 5242 : Jump lhsFail = frame.testInt32(Assembler::NotEqual, lhs);
1272 5242 : stubcc.linkExit(lhsFail, Uses(2));
1273 : }
1274 5948 : if (!rhsInt) {
1275 1800 : Jump rhsFail = frame.testInt32(Assembler::NotEqual, rhs);
1276 1800 : stubcc.linkExit(rhsFail, Uses(2));
1277 : }
1278 : }
1279 :
1280 5948 : stubcc.leave();
1281 5948 : OOL_STUBCALL(stub, REJOIN_FALLTHROUGH);
1282 :
1283 5948 : RegisterID reg = frame.ownRegForData(lhs);
1284 :
1285 : /* x86/64's SET instruction can only take single-byte regs.*/
1286 5948 : RegisterID resultReg = reg;
1287 5948 : if (!(Registers::maskReg(reg) & Registers::SingleByteRegs))
1288 3112 : resultReg = frame.allocReg(Registers::SingleByteRegs).reg();
1289 :
1290 : /* Emit the compare & set. */
1291 5948 : if (rhs->isConstant()) {
1292 3880 : masm.set32(cond, reg, Imm32(rhs->getValue().toInt32()), resultReg);
1293 2068 : } else if (frame.shouldAvoidDataRemat(rhs)) {
1294 : masm.set32(cond, reg,
1295 297 : masm.payloadOf(frame.addressOf(rhs)),
1296 297 : resultReg);
1297 : } else {
1298 1771 : masm.set32(cond, reg, frame.tempRegForData(rhs), resultReg);
1299 : }
1300 :
1301 : /* Clean up and push a boolean. */
1302 5948 : frame.pop();
1303 5948 : frame.pop();
1304 5948 : if (reg != resultReg)
1305 3112 : frame.freeReg(reg);
1306 5948 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, resultReg);
1307 5948 : stubcc.rejoin(Changes(1));
1308 : }
1309 19019 : return true;
1310 : }
1311 :
1312 : /*
1313 : * Emit an OOL path for a possibly double LHS, and possibly int32_t or number RHS.
1314 : */
1315 : void
1316 613721 : mjit::Compiler::emitLeftDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState::BinaryAlloc ®s,
1317 : MaybeJump &lhsNotDouble, MaybeJump &rhsNotNumber,
1318 : MaybeJump &lhsUnknownDone)
1319 : {
1320 : /* If the LHS is not a 32-bit integer, take OOL path. */
1321 613721 : Jump lhsNotInt32 = masm.testInt32(Assembler::NotEqual, regs.lhsType.reg());
1322 613721 : stubcc.linkExitDirect(lhsNotInt32, stubcc.masm.label());
1323 :
1324 613721 : if (!masm.supportsFloatingPoint()) {
1325 0 : lhsNotDouble = stubcc.masm.jump();
1326 0 : return;
1327 : }
1328 :
1329 : /* OOL path for LHS as a double - first test LHS is double. */
1330 613721 : lhsNotDouble = stubcc.masm.testDouble(Assembler::NotEqual, regs.lhsType.reg());
1331 :
1332 : /* Ensure the RHS is a number. */
1333 613721 : MaybeJump rhsIsDouble;
1334 613721 : if (!rhs->isTypeKnown()) {
1335 341556 : rhsIsDouble = stubcc.masm.testDouble(Assembler::Equal, regs.rhsType.reg());
1336 341556 : rhsNotNumber = stubcc.masm.testInt32(Assembler::NotEqual, regs.rhsType.reg());
1337 : }
1338 :
1339 : /* If RHS is constant, convert now. */
1340 613721 : if (rhs->isConstant())
1341 266357 : slowLoadConstantDouble(stubcc.masm, rhs, regs.rhsFP);
1342 : else
1343 347364 : stubcc.masm.convertInt32ToDouble(regs.rhsData.reg(), regs.rhsFP);
1344 :
1345 613721 : if (!rhs->isTypeKnown()) {
1346 : /* Jump past double load, bind double type check. */
1347 341556 : Jump converted = stubcc.masm.jump();
1348 341556 : rhsIsDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1349 :
1350 : /* Load the double. */
1351 : frame.loadDouble(regs.rhsType.reg(), regs.rhsData.reg(),
1352 341556 : rhs, regs.rhsFP, stubcc.masm);
1353 :
1354 341556 : converted.linkTo(stubcc.masm.label(), &stubcc.masm);
1355 : }
1356 :
1357 : /* Load the LHS. */
1358 : frame.loadDouble(regs.lhsType.reg(), regs.lhsData.reg(),
1359 613721 : lhs, regs.lhsFP, stubcc.masm);
1360 613721 : lhsUnknownDone = stubcc.masm.jump();
1361 : }
1362 :
1363 : /*
1364 : * Emit an OOL path for an integer LHS, possibly double RHS.
1365 : */
1366 : void
1367 343999 : mjit::Compiler::emitRightDoublePath(FrameEntry *lhs, FrameEntry *rhs, FrameState::BinaryAlloc ®s,
1368 : MaybeJump &rhsNotNumber2)
1369 : {
1370 : /* If the RHS is not a double, take OOL path. */
1371 343999 : Jump notInt32 = masm.testInt32(Assembler::NotEqual, regs.rhsType.reg());
1372 343999 : stubcc.linkExitDirect(notInt32, stubcc.masm.label());
1373 :
1374 343999 : if (!masm.supportsFloatingPoint()) {
1375 0 : rhsNotNumber2 = stubcc.masm.jump();
1376 0 : return;
1377 : }
1378 :
1379 : /* Now test if RHS is a double. */
1380 343999 : rhsNotNumber2 = stubcc.masm.testDouble(Assembler::NotEqual, regs.rhsType.reg());
1381 :
1382 : /* We know LHS is an integer. */
1383 343999 : if (lhs->isConstant())
1384 910 : slowLoadConstantDouble(stubcc.masm, lhs, regs.lhsFP);
1385 : else
1386 343089 : stubcc.masm.convertInt32ToDouble(regs.lhsData.reg(), regs.lhsFP);
1387 :
1388 : /* Load the RHS. */
1389 : frame.loadDouble(regs.rhsType.reg(), regs.rhsData.reg(),
1390 343999 : rhs, regs.rhsFP, stubcc.masm);
1391 : }
1392 :
1393 : static inline Assembler::DoubleCondition
1394 54494 : DoubleCondForOp(JSOp op, JSOp fused)
1395 : {
1396 54494 : bool ifeq = fused == JSOP_IFEQ;
1397 54494 : switch (op) {
1398 : case JSOP_GT:
1399 : return ifeq
1400 : ? Assembler::DoubleLessThanOrEqualOrUnordered
1401 4467 : : Assembler::DoubleGreaterThan;
1402 : case JSOP_GE:
1403 : return ifeq
1404 : ? Assembler::DoubleLessThanOrUnordered
1405 6796 : : Assembler::DoubleGreaterThanOrEqual;
1406 : case JSOP_LT:
1407 : return ifeq
1408 : ? Assembler::DoubleGreaterThanOrEqualOrUnordered
1409 39003 : : Assembler::DoubleLessThan;
1410 : case JSOP_LE:
1411 : return ifeq
1412 : ? Assembler::DoubleGreaterThanOrUnordered
1413 4228 : : Assembler::DoubleLessThanOrEqual;
1414 : default:
1415 0 : JS_NOT_REACHED("unrecognized op");
1416 : return Assembler::DoubleLessThan;
1417 : }
1418 : }
1419 :
1420 : bool
1421 1585 : mjit::Compiler::jsop_relational_double(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused)
1422 : {
1423 1585 : FrameEntry *rhs = frame.peek(-1);
1424 1585 : FrameEntry *lhs = frame.peek(-2);
1425 :
1426 1585 : JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
1427 :
1428 : FPRegisterID fpLeft, fpRight;
1429 : bool allocateLeft, allocateRight;
1430 :
1431 1585 : MaybeJump lhsNotNumber = loadDouble(lhs, &fpLeft, &allocateLeft);
1432 1585 : if (lhsNotNumber.isSet()) {
1433 166 : if (target)
1434 111 : stubcc.linkExitForBranch(lhsNotNumber.get());
1435 : else
1436 55 : stubcc.linkExit(lhsNotNumber.get(), Uses(2));
1437 : }
1438 1585 : if (!allocateLeft)
1439 1195 : frame.pinReg(fpLeft);
1440 :
1441 1585 : MaybeJump rhsNotNumber = loadDouble(rhs, &fpRight, &allocateRight);
1442 1585 : if (rhsNotNumber.isSet()) {
1443 49 : if (target)
1444 18 : stubcc.linkExitForBranch(rhsNotNumber.get());
1445 : else
1446 31 : stubcc.linkExit(rhsNotNumber.get(), Uses(2));
1447 : }
1448 1585 : if (!allocateLeft)
1449 1195 : frame.unpinReg(fpLeft);
1450 :
1451 1585 : Assembler::DoubleCondition dblCond = DoubleCondForOp(op, fused);
1452 :
1453 1585 : if (target) {
1454 1131 : stubcc.leave();
1455 1131 : OOL_STUBCALL(stub, REJOIN_BRANCH);
1456 :
1457 1131 : if (!allocateLeft)
1458 860 : frame.pinReg(fpLeft);
1459 1131 : if (!allocateRight)
1460 742 : frame.pinReg(fpRight);
1461 :
1462 1131 : frame.syncAndKillEverything();
1463 :
1464 1131 : Jump j = masm.branchDouble(dblCond, fpLeft, fpRight);
1465 :
1466 1131 : if (allocateLeft)
1467 271 : frame.freeReg(fpLeft);
1468 : else
1469 860 : frame.unpinKilledReg(fpLeft);
1470 :
1471 1131 : if (allocateRight)
1472 389 : frame.freeReg(fpRight);
1473 : else
1474 742 : frame.unpinKilledReg(fpRight);
1475 :
1476 1131 : frame.popn(2);
1477 :
1478 : Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
1479 1131 : Registers::ReturnReg, Registers::ReturnReg);
1480 :
1481 : /* Rejoin from the slow path. */
1482 1131 : stubcc.rejoin(Changes(0));
1483 :
1484 : /*
1485 : * NB: jumpAndRun emits to the OOL path, so make sure not to use it
1486 : * in the middle of an in-progress slow path.
1487 : */
1488 1131 : if (!jumpAndRun(j, target, &sj))
1489 0 : return false;
1490 : } else {
1491 454 : stubcc.leave();
1492 454 : OOL_STUBCALL(stub, REJOIN_FALLTHROUGH);
1493 :
1494 454 : frame.popn(2);
1495 :
1496 454 : RegisterID reg = frame.allocReg();
1497 454 : Jump j = masm.branchDouble(dblCond, fpLeft, fpRight);
1498 454 : masm.move(Imm32(0), reg);
1499 454 : Jump skip = masm.jump();
1500 454 : j.linkTo(masm.label(), &masm);
1501 454 : masm.move(Imm32(1), reg);
1502 454 : skip.linkTo(masm.label(), &masm);
1503 :
1504 454 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg);
1505 :
1506 454 : stubcc.rejoin(Changes(1));
1507 :
1508 454 : if (allocateLeft)
1509 119 : frame.freeReg(fpLeft);
1510 454 : if (allocateRight)
1511 324 : frame.freeReg(fpRight);
1512 : }
1513 :
1514 1585 : return true;
1515 : }
1516 :
1517 : bool
1518 16115 : mjit::Compiler::jsop_relational_int(JSOp op, jsbytecode *target, JSOp fused)
1519 : {
1520 16115 : FrameEntry *rhs = frame.peek(-1);
1521 16115 : FrameEntry *lhs = frame.peek(-2);
1522 :
1523 : /* Reverse N cmp A comparisons. The left side must be in a register. */
1524 16115 : if (lhs->isConstant()) {
1525 0 : JS_ASSERT(!rhs->isConstant());
1526 0 : FrameEntry *tmp = lhs;
1527 0 : lhs = rhs;
1528 0 : rhs = tmp;
1529 0 : op = ReverseCompareOp(op);
1530 : }
1531 :
1532 16115 : JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
1533 16115 : Assembler::Condition cond = GetCompareCondition(op, fused);
1534 :
1535 16115 : if (target) {
1536 15530 : if (!frame.syncForBranch(target, Uses(2)))
1537 0 : return false;
1538 :
1539 15530 : RegisterID lreg = frame.tempRegForData(lhs);
1540 15530 : Jump fast;
1541 15530 : if (rhs->isConstant()) {
1542 11237 : fast = masm.branch32(cond, lreg, Imm32(rhs->getValue().toInt32()));
1543 : } else {
1544 4293 : frame.pinReg(lreg);
1545 4293 : RegisterID rreg = frame.tempRegForData(rhs);
1546 4293 : frame.unpinReg(lreg);
1547 4293 : fast = masm.branch32(cond, lreg, rreg);
1548 : }
1549 15530 : frame.popn(2);
1550 :
1551 : Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
1552 15530 : Registers::ReturnReg, Registers::ReturnReg);
1553 :
1554 15530 : return jumpAndRun(fast, target, &sj);
1555 : } else {
1556 585 : RegisterID result = frame.allocReg();
1557 585 : RegisterID lreg = frame.tempRegForData(lhs);
1558 :
1559 585 : if (rhs->isConstant()) {
1560 415 : masm.branchValue(cond, lreg, rhs->getValue().toInt32(), result);
1561 : } else {
1562 170 : frame.pinReg(lreg);
1563 170 : RegisterID rreg = frame.tempRegForData(rhs);
1564 170 : frame.unpinReg(lreg);
1565 170 : masm.branchValue(cond, lreg, rreg, result);
1566 : }
1567 :
1568 585 : frame.popn(2);
1569 585 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
1570 : }
1571 :
1572 585 : return true;
1573 : }
1574 :
1575 : /* See jsop_binary_full() for more information on how this works. */
1576 : bool
1577 52909 : mjit::Compiler::jsop_relational_full(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused)
1578 : {
1579 52909 : FrameEntry *rhs = frame.peek(-1);
1580 52909 : FrameEntry *lhs = frame.peek(-2);
1581 :
1582 : /* Allocate all registers up-front. */
1583 52909 : FrameState::BinaryAlloc regs;
1584 52909 : frame.allocForBinary(lhs, rhs, op, regs, !target);
1585 :
1586 52909 : MaybeJump lhsNotDouble, rhsNotNumber, lhsUnknownDone;
1587 52909 : if (!lhs->isTypeKnown())
1588 51805 : emitLeftDoublePath(lhs, rhs, regs, lhsNotDouble, rhsNotNumber, lhsUnknownDone);
1589 :
1590 52909 : MaybeJump rhsNotNumber2;
1591 52909 : if (!rhs->isTypeKnown())
1592 22206 : emitRightDoublePath(lhs, rhs, regs, rhsNotNumber2);
1593 :
1594 : /* Both double paths will join here. */
1595 52909 : bool hasDoublePath = false;
1596 52909 : if (masm.supportsFloatingPoint() && (!rhs->isTypeKnown() || !lhs->isTypeKnown()))
1597 52898 : hasDoublePath = true;
1598 :
1599 : /* Integer path - figure out the immutable side. */
1600 52909 : JSOp cmpOp = op;
1601 52909 : int32_t value = 0;
1602 : RegisterID cmpReg;
1603 52909 : MaybeRegisterID reg;
1604 52909 : if (regs.lhsData.isSet()) {
1605 52768 : cmpReg = regs.lhsData.reg();
1606 52768 : if (!regs.rhsData.isSet())
1607 25666 : value = rhs->getValue().toInt32();
1608 : else
1609 27102 : reg = regs.rhsData.reg();
1610 : } else {
1611 141 : cmpReg = regs.rhsData.reg();
1612 141 : value = lhs->getValue().toInt32();
1613 141 : cmpOp = ReverseCompareOp(op);
1614 : }
1615 :
1616 : /*
1617 : * Emit the actual comparisons. When a fusion is in play, it's faster to
1618 : * combine the comparison with the jump, so these two cases are implemented
1619 : * separately.
1620 : */
1621 :
1622 52909 : if (target) {
1623 : /*
1624 : * Emit the double path now, necessary to complete the OOL fast-path
1625 : * before emitting the slow path.
1626 : *
1627 : * Note: doubles have not been swapped yet. Use original op.
1628 : */
1629 44664 : MaybeJump doubleTest, doubleFall;
1630 44664 : Assembler::DoubleCondition dblCond = DoubleCondForOp(op, fused);
1631 44664 : if (hasDoublePath) {
1632 44658 : if (lhsUnknownDone.isSet())
1633 43726 : lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1634 44658 : frame.sync(stubcc.masm, Uses(frame.frameSlots()));
1635 44658 : doubleTest = stubcc.masm.branchDouble(dblCond, regs.lhsFP, regs.rhsFP);
1636 44658 : doubleFall = stubcc.masm.jump();
1637 : }
1638 :
1639 : /* Link all incoming slow paths to here. */
1640 44664 : if (lhsNotDouble.isSet()) {
1641 43726 : lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1642 43726 : if (rhsNotNumber.isSet())
1643 20183 : rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1644 : }
1645 44664 : if (rhsNotNumber2.isSet())
1646 21115 : rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1647 :
1648 : /*
1649 : * For fusions, spill the tracker state. xmm* remain intact. Note
1650 : * that frame.sync() must be used directly, to avoid syncExit()'s
1651 : * jumping logic.
1652 : */
1653 44664 : frame.sync(stubcc.masm, Uses(frame.frameSlots()));
1654 44664 : stubcc.leave();
1655 44664 : OOL_STUBCALL(stub, REJOIN_BRANCH);
1656 :
1657 : /* Forget the world, preserving data. */
1658 44664 : frame.pinReg(cmpReg);
1659 44664 : if (reg.isSet())
1660 26131 : frame.pinReg(reg.reg());
1661 :
1662 44664 : frame.popn(2);
1663 :
1664 44664 : frame.syncAndKillEverything();
1665 44664 : frame.unpinKilledReg(cmpReg);
1666 44664 : if (reg.isSet())
1667 26131 : frame.unpinKilledReg(reg.reg());
1668 44664 : frame.freeReg(regs.lhsFP);
1669 44664 : frame.freeReg(regs.rhsFP);
1670 :
1671 : /* Operands could have been reordered, so use cmpOp. */
1672 44664 : Assembler::Condition i32Cond = GetCompareCondition(cmpOp, fused);
1673 :
1674 : /* Emit the i32 path. */
1675 44664 : Jump fast;
1676 44664 : if (reg.isSet())
1677 26131 : fast = masm.branch32(i32Cond, cmpReg, reg.reg());
1678 : else
1679 18533 : fast = masm.branch32(i32Cond, cmpReg, Imm32(value));
1680 :
1681 : /*
1682 : * The stub call has no need to rejoin since state is synced. Instead,
1683 : * we can just test the return value.
1684 : */
1685 : Jump j = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
1686 44664 : Registers::ReturnReg, Registers::ReturnReg);
1687 :
1688 : /* Rejoin from the slow path. */
1689 44664 : Jump j2 = stubcc.masm.jump();
1690 44664 : stubcc.crossJump(j2, masm.label());
1691 :
1692 44664 : if (hasDoublePath) {
1693 44658 : j.linkTo(stubcc.masm.label(), &stubcc.masm);
1694 44658 : doubleTest.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1695 44658 : j = stubcc.masm.jump();
1696 : }
1697 :
1698 : /*
1699 : * NB: jumpAndRun emits to the OOL path, so make sure not to use it
1700 : * in the middle of an in-progress slow path.
1701 : */
1702 44664 : if (!jumpAndRun(fast, target, &j))
1703 0 : return false;
1704 :
1705 : /* Rejoin from the double path. */
1706 44664 : if (hasDoublePath)
1707 44658 : stubcc.crossJump(doubleFall.get(), masm.label());
1708 : } else {
1709 : /*
1710 : * Emit the double path now, necessary to complete the OOL fast-path
1711 : * before emitting the slow path.
1712 : */
1713 8245 : MaybeJump doubleDone;
1714 8245 : Assembler::DoubleCondition dblCond = DoubleCondForOp(op, JSOP_NOP);
1715 8245 : if (hasDoublePath) {
1716 8240 : if (lhsUnknownDone.isSet())
1717 8079 : lhsUnknownDone.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1718 : /* :FIXME: Use SET if we can? */
1719 8240 : Jump test = stubcc.masm.branchDouble(dblCond, regs.lhsFP, regs.rhsFP);
1720 8240 : stubcc.masm.move(Imm32(0), regs.result);
1721 8240 : Jump skip = stubcc.masm.jump();
1722 8240 : test.linkTo(stubcc.masm.label(), &stubcc.masm);
1723 8240 : stubcc.masm.move(Imm32(1), regs.result);
1724 8240 : skip.linkTo(stubcc.masm.label(), &stubcc.masm);
1725 8240 : doubleDone = stubcc.masm.jump();
1726 : }
1727 :
1728 : /* Link all incoming slow paths to here. */
1729 8245 : if (lhsNotDouble.isSet()) {
1730 8079 : lhsNotDouble.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1731 8079 : if (rhsNotNumber.isSet())
1732 930 : rhsNotNumber.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1733 : }
1734 8245 : if (rhsNotNumber2.isSet())
1735 1091 : rhsNotNumber2.get().linkTo(stubcc.masm.label(), &stubcc.masm);
1736 :
1737 : /* Emit the slow path - note full frame syncage. */
1738 8245 : frame.sync(stubcc.masm, Uses(2));
1739 8245 : stubcc.leave();
1740 8245 : OOL_STUBCALL(stub, REJOIN_FALLTHROUGH);
1741 :
1742 : /* Get an integer comparison condition. */
1743 8245 : Assembler::Condition i32Cond = GetCompareCondition(cmpOp, fused);
1744 :
1745 : /* Emit the compare & set. */
1746 8245 : if (reg.isSet())
1747 971 : masm.branchValue(i32Cond, cmpReg, reg.reg(), regs.result);
1748 : else
1749 7274 : masm.branchValue(i32Cond, cmpReg, value, regs.result);
1750 :
1751 8245 : frame.popn(2);
1752 8245 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, regs.result);
1753 :
1754 8245 : if (hasDoublePath)
1755 8240 : stubcc.crossJump(doubleDone.get(), masm.label());
1756 8245 : stubcc.rejoin(Changes(1));
1757 :
1758 8245 : frame.freeReg(regs.lhsFP);
1759 8245 : frame.freeReg(regs.rhsFP);
1760 : }
1761 :
1762 52909 : return true;
1763 : }
1764 :
|