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 : *
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.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "jsbool.h"
42 : #include "jscntxt.h"
43 : #include "jslibmath.h"
44 : #include "jsnum.h"
45 : #include "jsscope.h"
46 : #include "jsobjinlines.h"
47 : #include "jsscriptinlines.h"
48 : #include "jstypedarrayinlines.h"
49 :
50 : #include "frontend/BytecodeEmitter.h"
51 : #include "methodjit/MethodJIT.h"
52 : #include "methodjit/Compiler.h"
53 : #include "methodjit/StubCalls.h"
54 : #include "methodjit/FrameState-inl.h"
55 :
56 : #include "jsautooplen.h"
57 :
58 : using namespace js;
59 : using namespace js::mjit;
60 :
61 : typedef JSC::MacroAssembler::RegisterID RegisterID;
62 :
63 : void
64 24487 : mjit::Compiler::ensureInteger(FrameEntry *fe, Uses uses)
65 : {
66 24487 : if (fe->isConstant()) {
67 8175 : if (!fe->isType(JSVAL_TYPE_INT32)) {
68 456 : JS_ASSERT(fe->isType(JSVAL_TYPE_DOUBLE));
69 456 : fe->convertConstantDoubleToInt32(cx);
70 : }
71 16312 : } else if (fe->isType(JSVAL_TYPE_DOUBLE)) {
72 290 : FPRegisterID fpreg = frame.tempFPRegForData(fe);
73 290 : FPRegisterID fptemp = frame.allocFPReg();
74 290 : RegisterID data = frame.allocReg();
75 290 : Jump truncateGuard = masm.branchTruncateDoubleToInt32(fpreg, data);
76 :
77 290 : Label syncPath = stubcc.syncExitAndJump(uses);
78 290 : stubcc.linkExitDirect(truncateGuard, stubcc.masm.label());
79 :
80 : /*
81 : * Try an OOL path to convert doubles representing integers within 2^32
82 : * of a signed integer, by adding/subtracting 2^32 and then trying to
83 : * convert to int32. This has to be an exact conversion, as otherwise
84 : * the truncation works incorrectly on the modified value.
85 : */
86 :
87 290 : stubcc.masm.zeroDouble(fptemp);
88 290 : Jump positive = stubcc.masm.branchDouble(Assembler::DoubleGreaterThan, fpreg, fptemp);
89 290 : stubcc.masm.slowLoadConstantDouble(double(4294967296.0), fptemp);
90 290 : Jump skip = stubcc.masm.jump();
91 290 : positive.linkTo(stubcc.masm.label(), &stubcc.masm);
92 290 : stubcc.masm.slowLoadConstantDouble(double(-4294967296.0), fptemp);
93 290 : skip.linkTo(stubcc.masm.label(), &stubcc.masm);
94 :
95 580 : JumpList isDouble;
96 290 : stubcc.masm.addDouble(fpreg, fptemp);
97 290 : stubcc.masm.branchConvertDoubleToInt32(fptemp, data, isDouble, Registers::FPConversionTemp);
98 290 : stubcc.crossJump(stubcc.masm.jump(), masm.label());
99 290 : isDouble.linkTo(syncPath, &stubcc.masm);
100 :
101 290 : frame.freeReg(fptemp);
102 290 : frame.learnType(fe, JSVAL_TYPE_INT32, data);
103 16022 : } else if (!fe->isType(JSVAL_TYPE_INT32)) {
104 5580 : if (masm.supportsFloatingPoint()) {
105 5580 : FPRegisterID fptemp = frame.allocFPReg();
106 5580 : RegisterID typeReg = frame.tempRegForType(fe);
107 5580 : frame.pinReg(typeReg);
108 5580 : RegisterID dataReg = frame.copyDataIntoReg(fe);
109 5580 : frame.unpinReg(typeReg);
110 :
111 5580 : Jump intGuard = masm.testInt32(Assembler::NotEqual, typeReg);
112 :
113 5580 : Label syncPath = stubcc.syncExitAndJump(uses);
114 5580 : stubcc.linkExitDirect(intGuard, stubcc.masm.label());
115 :
116 : /* Try an OOL path to truncate doubles representing int32s. */
117 5580 : Jump doubleGuard = stubcc.masm.testDouble(Assembler::NotEqual, typeReg);
118 5580 : doubleGuard.linkTo(syncPath, &stubcc.masm);
119 :
120 5580 : frame.loadDouble(fe, fptemp, stubcc.masm);
121 5580 : Jump truncateGuard = stubcc.masm.branchTruncateDoubleToInt32(fptemp, dataReg);
122 5580 : truncateGuard.linkTo(syncPath, &stubcc.masm);
123 5580 : stubcc.crossJump(stubcc.masm.jump(), masm.label());
124 :
125 5580 : frame.freeReg(fptemp);
126 5580 : frame.learnType(fe, JSVAL_TYPE_INT32, dataReg);
127 : } else {
128 0 : RegisterID typeReg = frame.tempRegForType(fe);
129 0 : frame.pinReg(typeReg);
130 0 : RegisterID dataReg = frame.copyDataIntoReg(fe);
131 0 : frame.unpinReg(typeReg);
132 :
133 0 : Jump intGuard = masm.testInt32(Assembler::NotEqual, typeReg);
134 :
135 0 : Label syncPath = stubcc.syncExitAndJump(uses);
136 0 : stubcc.linkExitDirect(intGuard, syncPath);
137 :
138 0 : frame.learnType(fe, JSVAL_TYPE_INT32, dataReg);
139 : }
140 : }
141 24487 : }
142 :
143 : void
144 167 : mjit::Compiler::jsop_bitnot()
145 : {
146 167 : FrameEntry *top = frame.peek(-1);
147 :
148 : /* We only want to handle integers here. */
149 167 : if (top->isNotType(JSVAL_TYPE_INT32) && top->isNotType(JSVAL_TYPE_DOUBLE)) {
150 0 : prepareStubCall(Uses(1));
151 0 : INLINE_STUBCALL(stubs::BitNot, REJOIN_FALLTHROUGH);
152 0 : frame.pop();
153 0 : frame.pushSynced(JSVAL_TYPE_INT32);
154 0 : return;
155 : }
156 :
157 167 : ensureInteger(top, Uses(1));
158 :
159 167 : stubcc.leave();
160 167 : OOL_STUBCALL(stubs::BitNot, REJOIN_FALLTHROUGH);
161 :
162 167 : RegisterID reg = frame.ownRegForData(top);
163 167 : masm.not32(reg);
164 167 : frame.pop();
165 167 : frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
166 :
167 167 : stubcc.rejoin(Changes(1));
168 : }
169 :
170 : void
171 13099 : mjit::Compiler::jsop_bitop(JSOp op)
172 : {
173 13099 : FrameEntry *rhs = frame.peek(-1);
174 13099 : FrameEntry *lhs = frame.peek(-2);
175 :
176 : /* The operands we ensure are integers cannot be copied by each other. */
177 13099 : frame.separateBinaryEntries(lhs, rhs);
178 :
179 : VoidStub stub;
180 13099 : switch (op) {
181 : case JSOP_BITOR:
182 1695 : stub = stubs::BitOr;
183 1695 : break;
184 : case JSOP_BITAND:
185 4009 : stub = stubs::BitAnd;
186 4009 : break;
187 : case JSOP_BITXOR:
188 1578 : stub = stubs::BitXor;
189 1578 : break;
190 : case JSOP_LSH:
191 2132 : stub = stubs::Lsh;
192 2132 : break;
193 : case JSOP_RSH:
194 2847 : stub = stubs::Rsh;
195 2847 : break;
196 : case JSOP_URSH:
197 838 : stub = stubs::Ursh;
198 838 : break;
199 : default:
200 0 : JS_NOT_REACHED("wat");
201 : return;
202 : }
203 :
204 : /* Convert a double RHS to integer if it's constant for the test below. */
205 13099 : if (rhs->isConstant() && rhs->getValue().isDouble())
206 630 : rhs->convertConstantDoubleToInt32(cx);
207 :
208 : /* We only want to handle integers here. */
209 27144 : if ((lhs->isNotType(JSVAL_TYPE_INT32) && lhs->isNotType(JSVAL_TYPE_DOUBLE)) ||
210 12980 : (rhs->isNotType(JSVAL_TYPE_INT32) && rhs->isNotType(JSVAL_TYPE_DOUBLE)) ||
211 1065 : (op == JSOP_URSH && rhs->isConstant() && rhs->getValue().toInt32() % 32 == 0)) {
212 939 : prepareStubCall(Uses(2));
213 939 : INLINE_STUBCALL(stub, REJOIN_FALLTHROUGH);
214 939 : frame.popn(2);
215 939 : frame.pushSynced(op != JSOP_URSH ? JSVAL_TYPE_INT32 : knownPushedType(0));
216 939 : return;
217 : }
218 :
219 12160 : ensureInteger(lhs, Uses(2));
220 12160 : ensureInteger(rhs, Uses(2));
221 :
222 12160 : if (lhs->isConstant() && rhs->isConstant()) {
223 210 : int32_t L = lhs->getValue().toInt32();
224 210 : int32_t R = rhs->getValue().toInt32();
225 :
226 210 : frame.popn(2);
227 210 : switch (op) {
228 : case JSOP_BITOR:
229 23 : frame.push(Int32Value(L | R));
230 23 : return;
231 : case JSOP_BITXOR:
232 20 : frame.push(Int32Value(L ^ R));
233 20 : return;
234 : case JSOP_BITAND:
235 88 : frame.push(Int32Value(L & R));
236 88 : return;
237 : case JSOP_LSH:
238 0 : frame.push(Int32Value(L << (R & 31)));
239 0 : return;
240 : case JSOP_RSH:
241 79 : frame.push(Int32Value(L >> (R & 31)));
242 79 : return;
243 : case JSOP_URSH:
244 : {
245 : uint32_t unsignedL;
246 0 : ToUint32(cx, Int32Value(L), (uint32_t*)&unsignedL); /* Can't fail. */
247 0 : Value v = NumberValue(uint32_t(unsignedL >> (R & 31)));
248 0 : JS_ASSERT(v.isInt32());
249 0 : frame.push(v);
250 0 : return;
251 : }
252 : default:
253 0 : JS_NOT_REACHED("say wat");
254 : }
255 : }
256 :
257 : RegisterID reg;
258 :
259 11950 : switch (op) {
260 : case JSOP_BITOR:
261 : case JSOP_BITXOR:
262 : case JSOP_BITAND:
263 : {
264 : /* Commutative, and we're guaranteed both are ints. */
265 6547 : if (lhs->isConstant()) {
266 425 : JS_ASSERT(!rhs->isConstant());
267 425 : FrameEntry *temp = rhs;
268 425 : rhs = lhs;
269 425 : lhs = temp;
270 : }
271 :
272 6547 : reg = frame.ownRegForData(lhs);
273 6547 : if (rhs->isConstant()) {
274 3588 : int32_t rhsInt = rhs->getValue().toInt32();
275 3588 : if (op == JSOP_BITAND)
276 2839 : masm.and32(Imm32(rhsInt), reg);
277 749 : else if (op == JSOP_BITXOR)
278 361 : masm.xor32(Imm32(rhsInt), reg);
279 388 : else if (rhsInt != 0)
280 233 : masm.or32(Imm32(rhsInt), reg);
281 2959 : } else if (frame.shouldAvoidDataRemat(rhs)) {
282 20 : Address rhsAddr = masm.payloadOf(frame.addressOf(rhs));
283 20 : if (op == JSOP_BITAND)
284 20 : masm.and32(rhsAddr, reg);
285 0 : else if (op == JSOP_BITXOR)
286 0 : masm.xor32(rhsAddr, reg);
287 : else
288 0 : masm.or32(rhsAddr, reg);
289 : } else {
290 2939 : RegisterID rhsReg = frame.tempRegForData(rhs);
291 2939 : if (op == JSOP_BITAND)
292 914 : masm.and32(rhsReg, reg);
293 2025 : else if (op == JSOP_BITXOR)
294 928 : masm.xor32(rhsReg, reg);
295 : else
296 1097 : masm.or32(rhsReg, reg);
297 : }
298 :
299 6547 : break;
300 : }
301 :
302 : case JSOP_LSH:
303 : case JSOP_RSH:
304 : case JSOP_URSH:
305 : {
306 : /* Not commutative. */
307 5403 : if (rhs->isConstant()) {
308 2937 : RegisterID reg = frame.ownRegForData(lhs);
309 2937 : int shift = rhs->getValue().toInt32() & 0x1F;
310 :
311 2937 : stubcc.leave();
312 2937 : OOL_STUBCALL(stub, REJOIN_FALLTHROUGH);
313 :
314 2937 : if (shift) {
315 2825 : if (op == JSOP_LSH)
316 838 : masm.lshift32(Imm32(shift), reg);
317 1987 : else if (op == JSOP_RSH)
318 1736 : masm.rshift32(Imm32(shift), reg);
319 : else
320 251 : masm.urshift32(Imm32(shift), reg);
321 : }
322 2937 : frame.popn(2);
323 :
324 : /* x >>> 0 may result in a double, handled above. */
325 2937 : JS_ASSERT_IF(op == JSOP_URSH, shift >= 1);
326 2937 : frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
327 :
328 2937 : stubcc.rejoin(Changes(1));
329 2937 : return;
330 : }
331 : #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
332 : /* Grosssssss! RHS _must_ be in ECX, on x86 */
333 : RegisterID rr = frame.tempRegInMaskForData(rhs,
334 2466 : Registers::maskReg(JSC::X86Registers::ecx)).reg();
335 : #else
336 : RegisterID rr = frame.tempRegForData(rhs);
337 : #endif
338 :
339 2466 : if (frame.haveSameBacking(lhs, rhs)) {
340 : // It's okay to allocReg(). If |rr| is evicted, it won't result in
341 : // a load, and |rr == reg| is fine since this is (x << x).
342 4 : reg = frame.allocReg();
343 4 : if (rr != reg)
344 4 : masm.move(rr, reg);
345 : } else {
346 2462 : frame.pinReg(rr);
347 2462 : if (lhs->isConstant()) {
348 1230 : reg = frame.allocReg();
349 1230 : masm.move(Imm32(lhs->getValue().toInt32()), reg);
350 : } else {
351 1232 : reg = frame.copyDataIntoReg(lhs);
352 : }
353 2462 : frame.unpinReg(rr);
354 : }
355 :
356 2466 : if (op == JSOP_LSH) {
357 1153 : masm.lshift32(rr, reg);
358 1313 : } else if (op == JSOP_RSH) {
359 884 : masm.rshift32(rr, reg);
360 : } else {
361 429 : masm.urshift32(rr, reg);
362 :
363 429 : Jump isNegative = masm.branch32(Assembler::LessThan, reg, Imm32(0));
364 429 : stubcc.linkExit(isNegative, Uses(2));
365 : }
366 2466 : break;
367 : }
368 :
369 : default:
370 0 : JS_NOT_REACHED("NYI");
371 : return;
372 : }
373 :
374 9013 : stubcc.leave();
375 9013 : OOL_STUBCALL(stub, REJOIN_FALLTHROUGH);
376 :
377 9013 : frame.pop();
378 9013 : frame.pop();
379 :
380 9013 : JSValueType type = knownPushedType(0);
381 :
382 9013 : if (type != JSVAL_TYPE_UNKNOWN && type != JSVAL_TYPE_DOUBLE)
383 6661 : frame.pushTypedPayload(type, reg);
384 2352 : else if (op == JSOP_URSH)
385 158 : frame.pushNumber(reg, true);
386 : else
387 2194 : frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
388 :
389 9013 : stubcc.rejoin(Changes(1));
390 : }
391 :
392 : static inline bool
393 3558 : CheckNullOrUndefined(FrameEntry *fe)
394 : {
395 3558 : if (!fe->isTypeKnown())
396 1352 : return false;
397 2206 : JSValueType type = fe->getKnownType();
398 2206 : return type == JSVAL_TYPE_NULL || type == JSVAL_TYPE_UNDEFINED;
399 : }
400 :
401 : CompileStatus
402 209 : mjit::Compiler::jsop_equality_obj_obj(JSOp op, jsbytecode *target, JSOp fused)
403 : {
404 209 : FrameEntry *rhs = frame.peek(-1);
405 209 : FrameEntry *lhs = frame.peek(-2);
406 :
407 627 : JS_ASSERT(cx->typeInferenceEnabled() &&
408 627 : lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT));
409 :
410 : /*
411 : * Handle equality between two objects. We have to ensure there is no
412 : * special equality operator on either object, if that passes then
413 : * this is a pointer comparison.
414 : */
415 209 : types::TypeSet *lhsTypes = analysis->poppedTypes(PC, 1);
416 209 : types::TypeSet *rhsTypes = analysis->poppedTypes(PC, 0);
417 407 : if (!lhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY) &&
418 198 : !rhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY)) {
419 : /* :TODO: Merge with jsop_relational_int? */
420 198 : JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
421 198 : frame.forgetMismatchedObject(lhs);
422 198 : frame.forgetMismatchedObject(rhs);
423 198 : Assembler::Condition cond = GetCompareCondition(op, fused);
424 198 : if (target) {
425 : Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
426 35 : Registers::ReturnReg, Registers::ReturnReg);
427 35 : if (!frame.syncForBranch(target, Uses(2)))
428 0 : return Compile_Error;
429 35 : RegisterID lreg = frame.tempRegForData(lhs);
430 35 : frame.pinReg(lreg);
431 35 : RegisterID rreg = frame.tempRegForData(rhs);
432 35 : frame.unpinReg(lreg);
433 35 : Jump fast = masm.branchPtr(cond, lreg, rreg);
434 35 : frame.popn(2);
435 35 : return jumpAndRun(fast, target, &sj) ? Compile_Okay : Compile_Error;
436 : } else {
437 163 : RegisterID result = frame.allocReg();
438 163 : RegisterID lreg = frame.tempRegForData(lhs);
439 163 : frame.pinReg(lreg);
440 163 : RegisterID rreg = frame.tempRegForData(rhs);
441 163 : frame.unpinReg(lreg);
442 163 : masm.branchValue(cond, lreg, rreg, result);
443 :
444 163 : frame.popn(2);
445 163 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
446 163 : return Compile_Okay;
447 : }
448 : }
449 :
450 11 : return Compile_Skipped;
451 : }
452 :
453 : bool
454 1873 : mjit::Compiler::jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused)
455 : {
456 1873 : FrameEntry *rhs = frame.peek(-1);
457 1873 : FrameEntry *lhs = frame.peek(-2);
458 :
459 : /* The compiler should have handled constant folding. */
460 1873 : JS_ASSERT(!(rhs->isConstant() && lhs->isConstant()));
461 :
462 : bool lhsTest;
463 1873 : if ((lhsTest = CheckNullOrUndefined(lhs)) || CheckNullOrUndefined(rhs)) {
464 : /* What's the other mask? */
465 1657 : FrameEntry *test = lhsTest ? rhs : lhs;
466 :
467 1657 : if (test->isType(JSVAL_TYPE_NULL) || test->isType(JSVAL_TYPE_UNDEFINED)) {
468 138 : return emitStubCmpOp(stub, target, fused);
469 1519 : } else if (test->isTypeKnown()) {
470 : /* The test will not succeed, constant fold the compare. */
471 213 : bool result = GetCompareCondition(op, fused) == Assembler::NotEqual;
472 213 : frame.pop();
473 213 : frame.pop();
474 213 : if (target)
475 147 : return constantFoldBranch(target, result);
476 66 : frame.push(BooleanValue(result));
477 66 : return true;
478 : }
479 :
480 : /* The other side must be null or undefined. */
481 1306 : RegisterID reg = frame.ownRegForType(test);
482 1306 : frame.pop();
483 1306 : frame.pop();
484 :
485 : /*
486 : * :FIXME: Easier test for undefined || null?
487 : * Maybe put them next to each other, subtract, do a single compare?
488 : */
489 :
490 1306 : if (target) {
491 1016 : frame.syncAndKillEverything();
492 1016 : frame.freeReg(reg);
493 :
494 : Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
495 1016 : Registers::ReturnReg, Registers::ReturnReg);
496 :
497 1016 : if ((op == JSOP_EQ && fused == JSOP_IFNE) ||
498 : (op == JSOP_NE && fused == JSOP_IFEQ)) {
499 650 : Jump b1 = masm.branchPtr(Assembler::Equal, reg, ImmType(JSVAL_TYPE_UNDEFINED));
500 650 : Jump b2 = masm.branchPtr(Assembler::Equal, reg, ImmType(JSVAL_TYPE_NULL));
501 650 : Jump j1 = masm.jump();
502 650 : b1.linkTo(masm.label(), &masm);
503 650 : b2.linkTo(masm.label(), &masm);
504 650 : Jump j2 = masm.jump();
505 650 : if (!jumpAndRun(j2, target, &sj))
506 0 : return false;
507 650 : j1.linkTo(masm.label(), &masm);
508 : } else {
509 366 : Jump j = masm.branchPtr(Assembler::Equal, reg, ImmType(JSVAL_TYPE_UNDEFINED));
510 366 : Jump j2 = masm.branchPtr(Assembler::NotEqual, reg, ImmType(JSVAL_TYPE_NULL));
511 366 : if (!jumpAndRun(j2, target, &sj))
512 0 : return false;
513 366 : j.linkTo(masm.label(), &masm);
514 : }
515 : } else {
516 290 : Jump j = masm.branchPtr(Assembler::Equal, reg, ImmType(JSVAL_TYPE_UNDEFINED));
517 290 : Jump j2 = masm.branchPtr(Assembler::Equal, reg, ImmType(JSVAL_TYPE_NULL));
518 290 : masm.move(Imm32(op == JSOP_NE), reg);
519 290 : Jump j3 = masm.jump();
520 290 : j2.linkTo(masm.label(), &masm);
521 290 : j.linkTo(masm.label(), &masm);
522 290 : masm.move(Imm32(op == JSOP_EQ), reg);
523 290 : j3.linkTo(masm.label(), &masm);
524 290 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg);
525 : }
526 1306 : return true;
527 : }
528 :
529 518 : if (cx->typeInferenceEnabled() &&
530 302 : lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT))
531 : {
532 92 : CompileStatus status = jsop_equality_obj_obj(op, target, fused);
533 92 : if (status == Compile_Okay) return true;
534 1 : else if (status == Compile_Error) return false;
535 : }
536 :
537 125 : return emitStubCmpOp(stub, target, fused);
538 : }
539 :
540 : bool
541 94282 : mjit::Compiler::jsop_relational(JSOp op, BoolStub stub,
542 : jsbytecode *target, JSOp fused)
543 : {
544 94282 : FrameEntry *rhs = frame.peek(-1);
545 94282 : FrameEntry *lhs = frame.peek(-2);
546 :
547 : /* The compiler should have handled constant folding. */
548 94282 : JS_ASSERT(!(rhs->isConstant() && lhs->isConstant()));
549 94282 : JS_ASSERT(fused == JSOP_NOP || fused == JSOP_IFEQ || fused == JSOP_IFNE);
550 :
551 : /* Always slow path... */
552 205589 : if ((lhs->isNotType(JSVAL_TYPE_INT32) && lhs->isNotType(JSVAL_TYPE_DOUBLE) &&
553 1234 : lhs->isNotType(JSVAL_TYPE_STRING)) ||
554 102596 : (rhs->isNotType(JSVAL_TYPE_INT32) && rhs->isNotType(JSVAL_TYPE_DOUBLE) &&
555 7477 : rhs->isNotType(JSVAL_TYPE_STRING))) {
556 2174 : if (op == JSOP_EQ || op == JSOP_NE)
557 1873 : return jsop_equality(op, stub, target, fused);
558 301 : return emitStubCmpOp(stub, target, fused);
559 : }
560 :
561 92108 : if (op == JSOP_EQ || op == JSOP_NE) {
562 48450 : if ((lhs->isNotType(JSVAL_TYPE_INT32) && lhs->isNotType(JSVAL_TYPE_STRING)) ||
563 27011 : (rhs->isNotType(JSVAL_TYPE_INT32) && rhs->isNotType(JSVAL_TYPE_STRING))) {
564 534 : return emitStubCmpOp(stub, target, fused);
565 20905 : } else if (!target && (lhs->isType(JSVAL_TYPE_STRING) || rhs->isType(JSVAL_TYPE_STRING))) {
566 1865 : return emitStubCmpOp(stub, target, fused);
567 19040 : } else if (frame.haveSameBacking(lhs, rhs)) {
568 21 : return emitStubCmpOp(stub, target, fused);
569 : } else {
570 19019 : return jsop_equality_int_string(op, stub, target, fused);
571 : }
572 : }
573 :
574 70669 : if (frame.haveSameBacking(lhs, rhs)) {
575 0 : return emitStubCmpOp(stub, target, fused);
576 70669 : } else if (lhs->isType(JSVAL_TYPE_STRING) || rhs->isType(JSVAL_TYPE_STRING)) {
577 60 : return emitStubCmpOp(stub, target, fused);
578 70609 : } else if (lhs->isType(JSVAL_TYPE_DOUBLE) || rhs->isType(JSVAL_TYPE_DOUBLE)) {
579 1585 : if (!masm.supportsFloatingPoint())
580 0 : return emitStubCmpOp(stub, target, fused);
581 1585 : return jsop_relational_double(op, stub, target, fused);
582 119423 : } else if (cx->typeInferenceEnabled() &&
583 50399 : lhs->isType(JSVAL_TYPE_INT32) && rhs->isType(JSVAL_TYPE_INT32)) {
584 16115 : return jsop_relational_int(op, target, fused);
585 : } else {
586 52909 : return jsop_relational_full(op, stub, target, fused);
587 : }
588 : }
589 :
590 : void
591 32963 : mjit::Compiler::jsop_not()
592 : {
593 32963 : FrameEntry *top = frame.peek(-1);
594 :
595 32963 : if (top->isConstant()) {
596 7 : const Value &v = top->getValue();
597 7 : frame.pop();
598 7 : frame.push(BooleanValue(!js_ValueToBoolean(v)));
599 7 : return;
600 : }
601 :
602 32956 : if (top->isTypeKnown()) {
603 17944 : JSValueType type = top->getKnownType();
604 17944 : switch (type) {
605 : case JSVAL_TYPE_INT32:
606 : {
607 102 : RegisterID data = frame.allocReg(Registers::SingleByteRegs).reg();
608 102 : if (frame.shouldAvoidDataRemat(top))
609 91 : masm.loadPayload(frame.addressOf(top), data);
610 : else
611 11 : masm.move(frame.tempRegForData(top), data);
612 :
613 102 : masm.set32(Assembler::Equal, data, Imm32(0), data);
614 :
615 102 : frame.pop();
616 102 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, data);
617 102 : break;
618 : }
619 :
620 : case JSVAL_TYPE_BOOLEAN:
621 : {
622 17525 : RegisterID reg = frame.ownRegForData(top);
623 :
624 17525 : masm.xor32(Imm32(1), reg);
625 :
626 17525 : frame.pop();
627 17525 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg);
628 17525 : break;
629 : }
630 :
631 : case JSVAL_TYPE_OBJECT:
632 : {
633 37 : RegisterID reg = frame.allocReg();
634 37 : masm.move(Imm32(0), reg);
635 :
636 37 : frame.pop();
637 37 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg);
638 37 : break;
639 : }
640 :
641 : default:
642 : {
643 280 : prepareStubCall(Uses(1));
644 280 : INLINE_STUBCALL_USES(stubs::ValueToBoolean, REJOIN_NONE, Uses(1));
645 :
646 280 : RegisterID reg = Registers::ReturnReg;
647 280 : frame.takeReg(reg);
648 280 : masm.xor32(Imm32(1), reg);
649 :
650 280 : frame.pop();
651 280 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg);
652 280 : break;
653 : }
654 : }
655 :
656 17944 : return;
657 : }
658 :
659 15012 : RegisterID data = frame.allocReg(Registers::SingleByteRegs).reg();
660 15012 : if (frame.shouldAvoidDataRemat(top))
661 3246 : masm.loadPayload(frame.addressOf(top), data);
662 : else
663 11766 : masm.move(frame.tempRegForData(top), data);
664 15012 : RegisterID type = frame.tempRegForType(top);
665 15012 : Label syncTarget = stubcc.syncExitAndJump(Uses(1));
666 :
667 :
668 : /* Inline path is for booleans. */
669 15012 : Jump jmpNotBool = masm.testBoolean(Assembler::NotEqual, type);
670 15012 : masm.xor32(Imm32(1), data);
671 :
672 :
673 : /* OOL path is for int + object. */
674 15012 : Label lblMaybeInt32 = stubcc.masm.label();
675 :
676 15012 : Jump jmpNotInt32 = stubcc.masm.testInt32(Assembler::NotEqual, type);
677 15012 : stubcc.masm.set32(Assembler::Equal, data, Imm32(0), data);
678 15012 : Jump jmpInt32Exit = stubcc.masm.jump();
679 :
680 15012 : Label lblMaybeObject = stubcc.masm.label();
681 15012 : Jump jmpNotObject = stubcc.masm.testPrimitive(Assembler::Equal, type);
682 15012 : stubcc.masm.move(Imm32(0), data);
683 15012 : Jump jmpObjectExit = stubcc.masm.jump();
684 :
685 :
686 : /* Rejoin location. */
687 15012 : Label lblRejoin = masm.label();
688 :
689 : /* Patch up jumps. */
690 15012 : stubcc.linkExitDirect(jmpNotBool, lblMaybeInt32);
691 :
692 15012 : jmpNotInt32.linkTo(lblMaybeObject, &stubcc.masm);
693 15012 : stubcc.crossJump(jmpInt32Exit, lblRejoin);
694 :
695 15012 : jmpNotObject.linkTo(syncTarget, &stubcc.masm);
696 15012 : stubcc.crossJump(jmpObjectExit, lblRejoin);
697 :
698 :
699 : /* Leave. */
700 15012 : stubcc.leave();
701 15012 : OOL_STUBCALL(stubs::Not, REJOIN_FALLTHROUGH);
702 :
703 15012 : frame.pop();
704 15012 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, data);
705 :
706 15012 : stubcc.rejoin(Changes(1));
707 : }
708 :
709 : void
710 2549 : mjit::Compiler::jsop_typeof()
711 : {
712 2549 : FrameEntry *fe = frame.peek(-1);
713 :
714 2549 : if (fe->isTypeKnown()) {
715 572 : JSRuntime *rt = cx->runtime;
716 :
717 572 : JSAtom *atom = NULL;
718 572 : switch (fe->getKnownType()) {
719 : case JSVAL_TYPE_STRING:
720 17 : atom = rt->atomState.typeAtoms[JSTYPE_STRING];
721 17 : break;
722 : case JSVAL_TYPE_UNDEFINED:
723 393 : atom = rt->atomState.typeAtoms[JSTYPE_VOID];
724 393 : break;
725 : case JSVAL_TYPE_NULL:
726 8 : atom = rt->atomState.typeAtoms[JSTYPE_OBJECT];
727 8 : break;
728 : case JSVAL_TYPE_OBJECT:
729 105 : atom = NULL;
730 105 : break;
731 : case JSVAL_TYPE_BOOLEAN:
732 4 : atom = rt->atomState.typeAtoms[JSTYPE_BOOLEAN];
733 4 : break;
734 : default:
735 45 : atom = rt->atomState.typeAtoms[JSTYPE_NUMBER];
736 45 : break;
737 : }
738 :
739 572 : if (atom) {
740 467 : frame.pop();
741 467 : frame.push(StringValue(atom));
742 467 : return;
743 : }
744 : }
745 :
746 2082 : JSOp fused = JSOp(PC[JSOP_TYPEOF_LENGTH]);
747 2082 : if (fused == JSOP_STRING && !fe->isTypeKnown()) {
748 1691 : JSOp op = JSOp(PC[JSOP_TYPEOF_LENGTH + JSOP_STRING_LENGTH]);
749 :
750 1691 : if (op == JSOP_STRICTEQ || op == JSOP_EQ || op == JSOP_STRICTNE || op == JSOP_NE) {
751 1502 : JSAtom *atom = script->getAtom(GET_UINT32_INDEX(PC + JSOP_TYPEOF_LENGTH));
752 1502 : JSRuntime *rt = cx->runtime;
753 1502 : JSValueType type = JSVAL_TYPE_UNKNOWN;
754 : Assembler::Condition cond = (op == JSOP_STRICTEQ || op == JSOP_EQ)
755 : ? Assembler::Equal
756 1502 : : Assembler::NotEqual;
757 :
758 1502 : if (atom == rt->atomState.typeAtoms[JSTYPE_VOID]) {
759 949 : type = JSVAL_TYPE_UNDEFINED;
760 553 : } else if (atom == rt->atomState.typeAtoms[JSTYPE_STRING]) {
761 247 : type = JSVAL_TYPE_STRING;
762 306 : } else if (atom == rt->atomState.typeAtoms[JSTYPE_BOOLEAN]) {
763 2 : type = JSVAL_TYPE_BOOLEAN;
764 304 : } else if (atom == rt->atomState.typeAtoms[JSTYPE_NUMBER]) {
765 27 : type = JSVAL_TYPE_INT32;
766 :
767 : /* JSVAL_TYPE_DOUBLE is 0x0 and JSVAL_TYPE_INT32 is 0x1, use <= or > to match both */
768 27 : cond = (cond == Assembler::Equal) ? Assembler::BelowOrEqual : Assembler::Above;
769 : }
770 :
771 1502 : jsbytecode *afterPC = PC + JSOP_STRING_LENGTH + JSOP_EQ_LENGTH;
772 :
773 1502 : if (type != JSVAL_TYPE_UNKNOWN && bytecodeInChunk(afterPC)) {
774 1225 : PC = afterPC;
775 :
776 1225 : RegisterID result = frame.allocReg(Registers::SingleByteRegs).reg();
777 :
778 : #if defined JS_NUNBOX32
779 1225 : if (frame.shouldAvoidTypeRemat(fe))
780 6 : masm.set32(cond, masm.tagOf(frame.addressOf(fe)), ImmType(type), result);
781 : else
782 1219 : masm.set32(cond, frame.tempRegForType(fe), ImmType(type), result);
783 : #elif defined JS_PUNBOX64
784 : masm.setPtr(cond, frame.tempRegForType(fe), ImmType(type), result);
785 : #endif
786 :
787 1225 : frame.pop();
788 1225 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
789 1225 : return;
790 : }
791 : }
792 : }
793 :
794 857 : prepareStubCall(Uses(1));
795 857 : INLINE_STUBCALL(stubs::TypeOf, REJOIN_NONE);
796 857 : frame.pop();
797 857 : frame.takeReg(Registers::ReturnReg);
798 857 : frame.pushTypedPayload(JSVAL_TYPE_STRING, Registers::ReturnReg);
799 : }
800 :
801 : bool
802 87401 : mjit::Compiler::booleanJumpScript(JSOp op, jsbytecode *target)
803 : {
804 : // JSOP_AND and JSOP_OR may leave the value on the stack (despite
805 : // the frame.pop() below), so we need to sync it.
806 87401 : if (op == JSOP_AND || op == JSOP_OR) {
807 17609 : frame.syncForBranch(target, Uses(0));
808 : } else {
809 69792 : JS_ASSERT(op == JSOP_IFEQ || op == JSOP_IFNE);
810 69792 : frame.syncForBranch(target, Uses(1));
811 : }
812 :
813 87401 : FrameEntry *fe = frame.peek(-1);
814 : Assembler::Condition cond = (op == JSOP_IFNE || op == JSOP_OR)
815 : ? Assembler::NonZero
816 87401 : : Assembler::Zero;
817 :
818 : // Load data register and pin it so that frame.testBoolean
819 : // below cannot evict it.
820 87401 : MaybeRegisterID data;
821 87401 : if (!fe->isType(JSVAL_TYPE_DOUBLE)) {
822 87384 : data = frame.tempRegForData(fe);
823 87384 : frame.pinReg(data.reg());
824 : }
825 :
826 : // Test for boolean if needed.
827 87401 : bool needStub = false;
828 87401 : if (!fe->isType(JSVAL_TYPE_BOOLEAN) && !fe->isType(JSVAL_TYPE_INT32)) {
829 70332 : Jump notBool;
830 70332 : if (fe->mightBeType(JSVAL_TYPE_BOOLEAN))
831 69748 : notBool = frame.testBoolean(Assembler::NotEqual, fe);
832 : else
833 584 : notBool = masm.jump();
834 :
835 70332 : stubcc.linkExitForBranch(notBool);
836 70332 : needStub = true;
837 : }
838 87401 : if (data.isSet())
839 87384 : frame.unpinReg(data.reg());
840 :
841 : // Test + branch.
842 87401 : Jump branch;
843 87401 : if (!fe->isType(JSVAL_TYPE_DOUBLE))
844 87384 : branch = masm.branchTest32(cond, data.reg());
845 : else
846 17 : branch = masm.jump(); // dummy jump
847 :
848 : // OOL path: call ValueToBoolean and branch.
849 87401 : if (needStub) {
850 70332 : stubcc.leave();
851 :
852 : // Note: this cannot overwrite slots holding loop invariants.
853 : stubcc.masm.infallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, stubs::ValueToBoolean),
854 70332 : frame.totalDepth());
855 : }
856 :
857 87401 : Jump stubBranch = stubcc.masm.branchTest32(cond, Registers::ReturnReg);
858 :
859 : // Rejoin from the stub call fallthrough.
860 87401 : if (needStub)
861 70332 : stubcc.rejoin(Changes(0));
862 :
863 87401 : frame.pop();
864 :
865 87401 : return jumpAndRun(branch, target, &stubBranch);
866 : }
867 :
868 : bool
869 70707 : mjit::Compiler::jsop_ifneq(JSOp op, jsbytecode *target)
870 : {
871 70707 : FrameEntry *fe = frame.peek(-1);
872 :
873 70707 : if (fe->isConstant()) {
874 915 : JSBool b = js_ValueToBoolean(fe->getValue());
875 :
876 915 : frame.pop();
877 :
878 915 : if (op == JSOP_IFEQ)
879 308 : b = !b;
880 915 : if (b) {
881 609 : if (!frame.syncForBranch(target, Uses(0)))
882 0 : return false;
883 609 : if (!jumpAndRun(masm.jump(), target))
884 0 : return false;
885 : } else {
886 306 : if (target < PC && !finishLoop(target))
887 0 : return false;
888 : }
889 915 : return true;
890 : }
891 :
892 69792 : return booleanJumpScript(op, target);
893 : }
894 :
895 : bool
896 18723 : mjit::Compiler::jsop_andor(JSOp op, jsbytecode *target)
897 : {
898 18723 : FrameEntry *fe = frame.peek(-1);
899 :
900 18723 : if (fe->isConstant()) {
901 1114 : JSBool b = js_ValueToBoolean(fe->getValue());
902 :
903 : /* Short-circuit. */
904 1114 : if ((op == JSOP_OR && b == JS_TRUE) ||
905 : (op == JSOP_AND && b == JS_FALSE)) {
906 602 : if (!frame.syncForBranch(target, Uses(0)))
907 0 : return false;
908 602 : if (!jumpAndRun(masm.jump(), target))
909 0 : return false;
910 : }
911 :
912 1114 : frame.pop();
913 1114 : return true;
914 : }
915 :
916 17609 : return booleanJumpScript(op, target);
917 : }
918 :
919 : bool
920 38629 : mjit::Compiler::jsop_localinc(JSOp op, uint32_t slot)
921 : {
922 38629 : restoreVarType();
923 :
924 38629 : types::TypeSet *types = pushedTypeSet(0);
925 38629 : JSValueType type = types ? types->getKnownTypeTag(cx) : JSVAL_TYPE_UNKNOWN;
926 :
927 38629 : int amt = (op == JSOP_LOCALINC || op == JSOP_INCLOCAL) ? 1 : -1;
928 :
929 38629 : if (!analysis->incrementInitialValueObserved(PC)) {
930 : // Before:
931 : // After: V
932 37707 : frame.pushLocal(slot);
933 :
934 : // Before: V
935 : // After: V 1
936 37707 : frame.push(Int32Value(-amt));
937 :
938 : // Note, SUB will perform integer conversion for us.
939 : // Before: V 1
940 : // After: N+1
941 37707 : if (!jsop_binary(JSOP_SUB, stubs::Sub, type, types))
942 1 : return false;
943 :
944 : // Before: N+1
945 : // After: N+1
946 37706 : frame.storeLocal(slot, analysis->popGuaranteed(PC));
947 : } else {
948 : // Before:
949 : // After: V
950 922 : frame.pushLocal(slot);
951 :
952 : // Before: V
953 : // After: N
954 922 : jsop_pos();
955 :
956 : // Before: N
957 : // After: N N
958 922 : frame.dup();
959 :
960 : // Before: N N
961 : // After: N N 1
962 922 : frame.push(Int32Value(amt));
963 :
964 : // Before: N N 1
965 : // After: N N+1
966 922 : if (!jsop_binary(JSOP_ADD, stubs::Add, type, types))
967 0 : return false;
968 :
969 : // Before: N N+1
970 : // After: N N+1
971 922 : frame.storeLocal(slot, true);
972 :
973 : // Before: N N+1
974 : // After: N
975 922 : frame.pop();
976 : }
977 :
978 38628 : updateVarType();
979 38628 : return true;
980 : }
981 :
982 : bool
983 444 : mjit::Compiler::jsop_arginc(JSOp op, uint32_t slot)
984 : {
985 444 : restoreVarType();
986 :
987 444 : types::TypeSet *types = pushedTypeSet(0);
988 444 : JSValueType type = types ? types->getKnownTypeTag(cx) : JSVAL_TYPE_UNKNOWN;
989 :
990 444 : int amt = (op == JSOP_ARGINC || op == JSOP_INCARG) ? 1 : -1;
991 :
992 444 : if (!analysis->incrementInitialValueObserved(PC)) {
993 : // Before:
994 : // After: V
995 257 : frame.pushArg(slot);
996 :
997 : // Before: V
998 : // After: V 1
999 257 : frame.push(Int32Value(-amt));
1000 :
1001 : // Note, SUB will perform integer conversion for us.
1002 : // Before: V 1
1003 : // After: N+1
1004 257 : if (!jsop_binary(JSOP_SUB, stubs::Sub, type, types))
1005 0 : return false;
1006 :
1007 : // Before: N+1
1008 : // After: N+1
1009 257 : frame.storeArg(slot, analysis->popGuaranteed(PC));
1010 : } else {
1011 : // Before:
1012 : // After: V
1013 187 : frame.pushArg(slot);
1014 :
1015 : // Before: V
1016 : // After: N
1017 187 : jsop_pos();
1018 :
1019 : // Before: N
1020 : // After: N N
1021 187 : frame.dup();
1022 :
1023 : // Before: N N
1024 : // After: N N 1
1025 187 : frame.push(Int32Value(amt));
1026 :
1027 : // Before: N N 1
1028 : // After: N N+1
1029 187 : if (!jsop_binary(JSOP_ADD, stubs::Add, type, types))
1030 0 : return false;
1031 :
1032 : // Before: N N+1
1033 : // After: N N+1
1034 187 : frame.storeArg(slot, true);
1035 :
1036 : // Before: N N+1
1037 : // After: N
1038 187 : frame.pop();
1039 : }
1040 :
1041 444 : updateVarType();
1042 444 : return true;
1043 : }
1044 :
1045 : static inline bool
1046 211207 : IsCacheableSetElem(FrameEntry *obj, FrameEntry *id, FrameEntry *value)
1047 : {
1048 211207 : if (obj->isNotType(JSVAL_TYPE_OBJECT))
1049 498 : return false;
1050 210709 : if (id->isNotType(JSVAL_TYPE_INT32) && id->isNotType(JSVAL_TYPE_DOUBLE))
1051 1232 : return false;
1052 209477 : if (id->isConstant()) {
1053 196315 : if (id->isNotType(JSVAL_TYPE_INT32))
1054 16 : return false;
1055 196299 : if (id->getValue().toInt32() < 0)
1056 6 : return false;
1057 196293 : if (id->getValue().toInt32() + 1 < 0) // watch for overflow in hole paths
1058 4 : return false;
1059 : }
1060 :
1061 : // obj[obj] * is not allowed, since it will never optimize.
1062 : // obj[id] = id is allowed.
1063 : // obj[id] = obj is allowed.
1064 209451 : if (obj->hasSameBacking(id))
1065 1 : return false;
1066 :
1067 209450 : return true;
1068 : }
1069 :
1070 : void
1071 7815 : mjit::Compiler::jsop_setelem_dense()
1072 : {
1073 7815 : FrameEntry *obj = frame.peek(-3);
1074 7815 : FrameEntry *id = frame.peek(-2);
1075 7815 : FrameEntry *value = frame.peek(-1);
1076 :
1077 : // We might not know whether this is an object, but if it is an object we
1078 : // know it is a dense array.
1079 7815 : if (!obj->isTypeKnown()) {
1080 1741 : Jump guard = frame.testObject(Assembler::NotEqual, obj);
1081 1741 : stubcc.linkExit(guard, Uses(3));
1082 : }
1083 :
1084 7815 : if (id->isType(JSVAL_TYPE_DOUBLE))
1085 0 : tryConvertInteger(id, Uses(2));
1086 :
1087 : // Test for integer index.
1088 7815 : if (!id->isTypeKnown()) {
1089 2692 : Jump guard = frame.testInt32(Assembler::NotEqual, id);
1090 2692 : stubcc.linkExit(guard, Uses(3));
1091 : }
1092 :
1093 : // Allocate registers.
1094 :
1095 : ValueRemat vr;
1096 7815 : frame.pinEntry(value, vr, /* breakDouble = */ false);
1097 :
1098 7815 : Int32Key key = id->isConstant()
1099 1220 : ? Int32Key::FromConstant(id->getValue().toInt32())
1100 9035 : : Int32Key::FromRegister(frame.tempRegForData(id));
1101 7815 : bool pinKey = !key.isConstant() && !frame.haveSameBacking(id, value);
1102 7815 : if (pinKey)
1103 6560 : frame.pinReg(key.reg());
1104 :
1105 : // Register to hold the computed slots pointer for the object. If we can
1106 : // hoist the initialized length check, we make the slots pointer loop
1107 : // invariant and never access the object itself.
1108 : RegisterID slotsReg;
1109 7815 : analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 2));
1110 7815 : analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 1));
1111 6480 : bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) &&
1112 14295 : loop->hoistArrayLengthCheck(DENSE_ARRAY, objv, indexv);
1113 :
1114 7815 : MaybeJump initlenExit;
1115 :
1116 7815 : if (hoisted) {
1117 290 : FrameEntry *slotsFe = loop->invariantArraySlots(objv);
1118 290 : slotsReg = frame.tempRegForData(slotsFe);
1119 :
1120 290 : frame.unpinEntry(vr);
1121 290 : if (pinKey)
1122 173 : frame.unpinReg(key.reg());
1123 : } else {
1124 : // Get a register for the object which we can clobber, and load its elements.
1125 7525 : if (frame.haveSameBacking(obj, value)) {
1126 4 : slotsReg = frame.allocReg();
1127 4 : masm.move(vr.dataReg(), slotsReg);
1128 7521 : } else if (frame.haveSameBacking(obj, id)) {
1129 0 : slotsReg = frame.allocReg();
1130 0 : masm.move(key.reg(), slotsReg);
1131 : } else {
1132 7521 : slotsReg = frame.copyDataIntoReg(obj);
1133 : }
1134 7525 : masm.loadPtr(Address(slotsReg, JSObject::offsetOfElements()), slotsReg);
1135 :
1136 7525 : frame.unpinEntry(vr);
1137 7525 : if (pinKey)
1138 6387 : frame.unpinReg(key.reg());
1139 :
1140 : // Make an OOL path for setting exactly the initialized length.
1141 7525 : Label syncTarget = stubcc.syncExitAndJump(Uses(3));
1142 :
1143 : Jump initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(),
1144 7525 : slotsReg, key, Assembler::BelowOrEqual);
1145 7525 : stubcc.linkExitDirect(initlenGuard, stubcc.masm.label());
1146 :
1147 : // Recheck for an exact initialized length. :TODO: would be nice to
1148 : // reuse the condition bits from the previous test.
1149 : Jump exactlenGuard = stubcc.masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(),
1150 7525 : slotsReg, key, Assembler::NotEqual);
1151 7525 : exactlenGuard.linkTo(syncTarget, &stubcc.masm);
1152 :
1153 : // Check array capacity.
1154 : Jump capacityGuard = stubcc.masm.guardArrayExtent(ObjectElements::offsetOfCapacity(),
1155 7525 : slotsReg, key, Assembler::BelowOrEqual);
1156 7525 : capacityGuard.linkTo(syncTarget, &stubcc.masm);
1157 :
1158 : // Bump the index for setting the array length. The above guard
1159 : // ensures this won't overflow, due to NSLOTS_LIMIT.
1160 7525 : stubcc.masm.bumpKey(key, 1);
1161 :
1162 : // Update the initialized length.
1163 7525 : stubcc.masm.storeKey(key, Address(slotsReg, ObjectElements::offsetOfInitializedLength()));
1164 :
1165 : // Update the array length if needed.
1166 : Jump lengthGuard = stubcc.masm.guardArrayExtent(ObjectElements::offsetOfLength(),
1167 7525 : slotsReg, key, Assembler::AboveOrEqual);
1168 7525 : stubcc.masm.storeKey(key, Address(slotsReg, ObjectElements::offsetOfLength()));
1169 7525 : lengthGuard.linkTo(stubcc.masm.label(), &stubcc.masm);
1170 :
1171 : // Restore the index.
1172 7525 : stubcc.masm.bumpKey(key, -1);
1173 :
1174 7525 : initlenExit = stubcc.masm.jump();
1175 : }
1176 :
1177 : #ifdef JSGC_INCREMENTAL_MJ
1178 : /*
1179 : * Write barrier.
1180 : * We skip over the barrier if we incremented initializedLength above,
1181 : * because in that case the slot we're overwriting was previously
1182 : * undefined.
1183 : */
1184 7815 : types::TypeSet *types = frame.extra(obj).types;
1185 7815 : if (cx->compartment->needsBarrier() && (!types || types->propertyNeedsBarrier(cx, JSID_VOID))) {
1186 2 : Label barrierStart = stubcc.masm.label();
1187 2 : stubcc.linkExitDirect(masm.jump(), barrierStart);
1188 :
1189 : /*
1190 : * The sync call below can potentially clobber key.reg() and slotsReg.
1191 : * We pin key.reg() to avoid it being clobbered. If |hoisted| is true,
1192 : * we can also pin slotsReg. If not, then slotsReg is owned by the
1193 : * compiler and we save in manually to VMFrame::scratch.
1194 : *
1195 : * Additionally, the WriteBarrier stub can clobber both registers. The
1196 : * rejoin call will restore key.reg() but not slotsReg. So we save
1197 : * slotsReg in the frame and restore it after the stub call.
1198 : */
1199 2 : stubcc.masm.storePtr(slotsReg, FrameAddress(offsetof(VMFrame, scratch)));
1200 2 : if (hoisted)
1201 1 : frame.pinReg(slotsReg);
1202 2 : if (!key.isConstant())
1203 2 : frame.pinReg(key.reg());
1204 2 : frame.sync(stubcc.masm, Uses(3));
1205 2 : if (!key.isConstant())
1206 2 : frame.unpinReg(key.reg());
1207 2 : if (hoisted)
1208 1 : frame.unpinReg(slotsReg);
1209 : else
1210 1 : stubcc.masm.loadPtr(FrameAddress(offsetof(VMFrame, scratch)), slotsReg);
1211 :
1212 2 : if (key.isConstant())
1213 0 : stubcc.masm.lea(Address(slotsReg, key.index() * sizeof(Value)), Registers::ArgReg1);
1214 : else
1215 2 : stubcc.masm.lea(BaseIndex(slotsReg, key.reg(), masm.JSVAL_SCALE), Registers::ArgReg1);
1216 2 : OOL_STUBCALL(stubs::WriteBarrier, REJOIN_NONE);
1217 2 : stubcc.masm.loadPtr(FrameAddress(offsetof(VMFrame, scratch)), slotsReg);
1218 2 : stubcc.rejoin(Changes(0));
1219 : }
1220 : #endif
1221 :
1222 : /* Jump over the write barrier in the initlen case. */
1223 7815 : if (initlenExit.isSet())
1224 7525 : stubcc.crossJump(initlenExit.get(), masm.label());
1225 :
1226 : // Fully store the value. :TODO: don't need to do this in the non-initlen case
1227 : // if the array is packed and monomorphic.
1228 7815 : if (key.isConstant())
1229 1220 : masm.storeValue(vr, Address(slotsReg, key.index() * sizeof(Value)));
1230 : else
1231 6595 : masm.storeValue(vr, BaseIndex(slotsReg, key.reg(), masm.JSVAL_SCALE));
1232 :
1233 7815 : stubcc.leave();
1234 7815 : OOL_STUBCALL(STRICT_VARIANT(stubs::SetElem), REJOIN_FALLTHROUGH);
1235 :
1236 7815 : if (!hoisted)
1237 7525 : frame.freeReg(slotsReg);
1238 7815 : frame.shimmy(2);
1239 7815 : stubcc.rejoin(Changes(2));
1240 7815 : }
1241 :
1242 : #ifdef JS_METHODJIT_TYPED_ARRAY
1243 : void
1244 752 : mjit::Compiler::convertForTypedArray(int atype, ValueRemat *vr, bool *allocated)
1245 : {
1246 752 : FrameEntry *value = frame.peek(-1);
1247 : bool floatArray = (atype == TypedArray::TYPE_FLOAT32 ||
1248 752 : atype == TypedArray::TYPE_FLOAT64);
1249 752 : *allocated = false;
1250 :
1251 752 : if (value->isConstant()) {
1252 452 : Value v = value->getValue();
1253 452 : if (floatArray) {
1254 53 : double d = v.isDouble() ? v.toDouble() : v.toInt32();
1255 53 : *vr = ValueRemat::FromConstant(DoubleValue(d));
1256 : } else {
1257 : int i32;
1258 399 : if (v.isInt32()) {
1259 349 : i32 = v.toInt32();
1260 349 : if (atype == TypedArray::TYPE_UINT8_CLAMPED)
1261 43 : i32 = ClampIntForUint8Array(i32);
1262 : } else {
1263 : i32 = (atype == TypedArray::TYPE_UINT8_CLAMPED)
1264 4 : ? js_TypedArray_uint8_clamp_double(v.toDouble())
1265 54 : : js_DoubleToECMAInt32(v.toDouble());
1266 : }
1267 399 : *vr = ValueRemat::FromConstant(Int32Value(i32));
1268 : }
1269 : } else {
1270 300 : if (floatArray) {
1271 : FPRegisterID fpReg;
1272 78 : MaybeJump notNumber = loadDouble(value, &fpReg, allocated);
1273 78 : if (notNumber.isSet())
1274 16 : stubcc.linkExit(notNumber.get(), Uses(3));
1275 :
1276 78 : if (atype == TypedArray::TYPE_FLOAT32) {
1277 64 : if (!*allocated) {
1278 36 : frame.pinReg(fpReg);
1279 36 : FPRegisterID newFpReg = frame.allocFPReg();
1280 36 : masm.convertDoubleToFloat(fpReg, newFpReg);
1281 36 : frame.unpinReg(fpReg);
1282 36 : fpReg = newFpReg;
1283 36 : *allocated = true;
1284 : } else {
1285 28 : masm.convertDoubleToFloat(fpReg, fpReg);
1286 : }
1287 : }
1288 78 : *vr = ValueRemat::FromFPRegister(fpReg);
1289 : } else {
1290 : /*
1291 : * Allocate a register with the following properties:
1292 : * 1) For byte arrays the value must be in a byte register.
1293 : * 2) For Uint8ClampedArray the register must be writable.
1294 : * 3) If the value is definitely int32_t (and the array is not
1295 : * Uint8ClampedArray) we don't have to allocate a new register.
1296 : * 4) If id and value have the same backing (e.g. arr[i] = i) and
1297 : * we need a byte register, we have to allocate a new register
1298 : * because we've already pinned a key register and can't use
1299 : * tempRegInMaskForData.
1300 : */
1301 222 : MaybeRegisterID reg, dataReg;
1302 : bool needsByteReg = (atype == TypedArray::TYPE_INT8 ||
1303 : atype == TypedArray::TYPE_UINT8 ||
1304 222 : atype == TypedArray::TYPE_UINT8_CLAMPED);
1305 222 : FrameEntry *id = frame.peek(-2);
1306 265 : if (!value->isType(JSVAL_TYPE_INT32) || atype == TypedArray::TYPE_UINT8_CLAMPED ||
1307 43 : (needsByteReg && frame.haveSameBacking(id, value))) {
1308 : // Grab data register before branching.
1309 116 : if (value->mightBeType(JSVAL_TYPE_INT32)) {
1310 81 : dataReg = frame.tempRegForData(value);
1311 :
1312 : // Make sure it's not clobbered by allocReg or tempRegForType.
1313 81 : if (!frame.haveSameBacking(id, value))
1314 74 : frame.pinReg(dataReg.reg());
1315 : }
1316 :
1317 : // x86 has 4 single byte registers. Worst case we've pinned 3
1318 : // registers, one for each of object, key and value. This means
1319 : // there must be at least one single byte register available.
1320 116 : if (needsByteReg)
1321 56 : reg = frame.allocReg(Registers::SingleByteRegs).reg();
1322 : else
1323 60 : reg = frame.allocReg();
1324 116 : *allocated = true;
1325 : } else {
1326 106 : if (needsByteReg)
1327 38 : reg = frame.tempRegInMaskForData(value, Registers::SingleByteRegs).reg();
1328 : else
1329 68 : reg = frame.tempRegForData(value);
1330 : }
1331 :
1332 : // Get type register before branching.
1333 222 : MaybeRegisterID typeReg;
1334 222 : if (!value->isTypeKnown()) {
1335 : // Note: we don't need to pin reg, it's never a temporary register if the
1336 : // type of value is not known.
1337 55 : JS_ASSERT(*allocated);
1338 55 : typeReg = frame.tempRegForType(value);
1339 : }
1340 :
1341 222 : MaybeJump intDone;
1342 222 : if (value->mightBeType(JSVAL_TYPE_INT32)) {
1343 : // Check if the value is an integer.
1344 187 : MaybeJump notInt;
1345 187 : if (!value->isTypeKnown()) {
1346 55 : JS_ASSERT(*allocated);
1347 55 : notInt = masm.testInt32(Assembler::NotEqual, typeReg.reg());
1348 : }
1349 :
1350 187 : if (*allocated) {
1351 81 : masm.move(dataReg.reg(), reg.reg());
1352 81 : if (!frame.haveSameBacking(id, value))
1353 74 : frame.unpinReg(dataReg.reg());
1354 : }
1355 :
1356 187 : if (atype == TypedArray::TYPE_UINT8_CLAMPED)
1357 25 : masm.clampInt32ToUint8(reg.reg());
1358 :
1359 187 : if (notInt.isSet()) {
1360 55 : intDone = masm.jump();
1361 55 : notInt.get().linkTo(masm.label(), &masm);
1362 : }
1363 : }
1364 222 : if (value->mightBeType(JSVAL_TYPE_DOUBLE)) {
1365 : // Check if the value is a double.
1366 90 : if (!value->isTypeKnown()) {
1367 55 : Jump notNumber = masm.testDouble(Assembler::NotEqual, typeReg.reg());
1368 55 : stubcc.linkExit(notNumber, Uses(3));
1369 : }
1370 :
1371 : // Load value in fpReg.
1372 : FPRegisterID fpReg;
1373 90 : if (value->isTypeKnown()) {
1374 35 : fpReg = frame.tempFPRegForData(value);
1375 : } else {
1376 55 : fpReg = frame.allocFPReg();
1377 55 : frame.loadDouble(value, fpReg, masm);
1378 : }
1379 :
1380 : // Convert double to integer.
1381 90 : if (atype == TypedArray::TYPE_UINT8_CLAMPED) {
1382 11 : if (value->isTypeKnown())
1383 7 : frame.pinReg(fpReg);
1384 11 : FPRegisterID fpTemp = frame.allocFPReg();
1385 11 : if (value->isTypeKnown())
1386 7 : frame.unpinReg(fpReg);
1387 11 : masm.clampDoubleToUint8(fpReg, fpTemp, reg.reg());
1388 11 : frame.freeReg(fpTemp);
1389 : } else {
1390 79 : Jump j = masm.branchTruncateDoubleToInt32(fpReg, reg.reg());
1391 79 : stubcc.linkExit(j, Uses(3));
1392 : }
1393 90 : if (!value->isTypeKnown())
1394 55 : frame.freeReg(fpReg);
1395 : }
1396 222 : if (intDone.isSet())
1397 55 : intDone.get().linkTo(masm.label(), &masm);
1398 222 : *vr = ValueRemat::FromKnownType(JSVAL_TYPE_INT32, reg.reg());
1399 : }
1400 : }
1401 752 : }
1402 :
1403 : void
1404 752 : mjit::Compiler::jsop_setelem_typed(int atype)
1405 : {
1406 752 : FrameEntry *obj = frame.peek(-3);
1407 752 : FrameEntry *id = frame.peek(-2);
1408 752 : FrameEntry *value = frame.peek(-1);
1409 :
1410 : // We might not know whether this is an object, but if it is an object we
1411 : // know it's a typed array.
1412 752 : if (!obj->isTypeKnown()) {
1413 294 : Jump guard = frame.testObject(Assembler::NotEqual, obj);
1414 294 : stubcc.linkExit(guard, Uses(3));
1415 : }
1416 :
1417 752 : if (id->isType(JSVAL_TYPE_DOUBLE))
1418 0 : tryConvertInteger(id, Uses(2));
1419 :
1420 : // Test for integer index.
1421 752 : if (!id->isTypeKnown()) {
1422 66 : Jump guard = frame.testInt32(Assembler::NotEqual, id);
1423 66 : stubcc.linkExit(guard, Uses(3));
1424 : }
1425 :
1426 : // Pin value.
1427 : ValueRemat vr;
1428 752 : frame.pinEntry(value, vr, /* breakDouble = */ false);
1429 :
1430 : // Allocate and pin object and key regs.
1431 752 : Int32Key key = id->isConstant()
1432 264 : ? Int32Key::FromConstant(id->getValue().toInt32())
1433 1016 : : Int32Key::FromRegister(frame.tempRegForData(id));
1434 :
1435 752 : bool pinKey = !key.isConstant() && !frame.haveSameBacking(id, value);
1436 752 : if (pinKey)
1437 474 : frame.pinReg(key.reg());
1438 :
1439 752 : analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 1));
1440 752 : analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0));
1441 343 : bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) &&
1442 1095 : loop->hoistArrayLengthCheck(TYPED_ARRAY, objv, indexv);
1443 :
1444 : RegisterID objReg;
1445 752 : if (hoisted) {
1446 0 : FrameEntry *slotsFe = loop->invariantArraySlots(objv);
1447 0 : objReg = frame.tempRegForData(slotsFe);
1448 0 : frame.pinReg(objReg);
1449 : } else {
1450 752 : objReg = frame.copyDataIntoReg(obj);
1451 :
1452 : // Bounds check.
1453 752 : int lengthOffset = TypedArray::lengthOffset() + offsetof(jsval_layout, s.payload);
1454 : Jump lengthGuard = masm.guardArrayExtent(lengthOffset,
1455 752 : objReg, key, Assembler::BelowOrEqual);
1456 752 : stubcc.linkExit(lengthGuard, Uses(3));
1457 :
1458 : // Load the array's packed data vector.
1459 752 : masm.loadPtr(Address(objReg, TypedArray::dataOffset()), objReg);
1460 : }
1461 :
1462 : // Unpin value so that convertForTypedArray can assign a new data
1463 : // register using tempRegInMaskForData.
1464 752 : frame.unpinEntry(vr);
1465 :
1466 : // Make sure key is pinned.
1467 752 : if (frame.haveSameBacking(id, value)) {
1468 14 : frame.pinReg(key.reg());
1469 14 : pinKey = true;
1470 : }
1471 752 : JS_ASSERT(pinKey == !id->isConstant());
1472 :
1473 : bool allocated;
1474 752 : convertForTypedArray(atype, &vr, &allocated);
1475 :
1476 : // Store the value.
1477 752 : masm.storeToTypedArray(atype, objReg, key, vr);
1478 752 : if (allocated) {
1479 188 : if (vr.isFPRegister())
1480 72 : frame.freeReg(vr.fpReg());
1481 : else
1482 116 : frame.freeReg(vr.dataReg());
1483 : }
1484 752 : if (pinKey)
1485 488 : frame.unpinReg(key.reg());
1486 752 : if (hoisted)
1487 0 : frame.unpinReg(objReg);
1488 : else
1489 752 : frame.freeReg(objReg);
1490 :
1491 752 : stubcc.leave();
1492 752 : OOL_STUBCALL(STRICT_VARIANT(stubs::SetElem), REJOIN_FALLTHROUGH);
1493 :
1494 752 : frame.shimmy(2);
1495 752 : stubcc.rejoin(Changes(2));
1496 752 : }
1497 : #endif /* JS_METHODJIT_TYPED_ARRAY */
1498 :
1499 : void
1500 2 : mjit::Compiler::tryConvertInteger(FrameEntry *fe, Uses uses)
1501 : {
1502 2 : JS_ASSERT(fe->isType(JSVAL_TYPE_DOUBLE));
1503 :
1504 4 : JumpList isDouble;
1505 2 : FPRegisterID fpreg = frame.tempFPRegForData(fe);
1506 2 : RegisterID reg = frame.allocReg();
1507 2 : masm.branchConvertDoubleToInt32(fpreg, reg, isDouble, Registers::FPConversionTemp);
1508 2 : Jump j = masm.jump();
1509 2 : isDouble.linkTo(masm.label(), &masm);
1510 2 : stubcc.linkExit(masm.jump(), uses);
1511 2 : j.linkTo(masm.label(), &masm);
1512 2 : frame.learnType(fe, JSVAL_TYPE_INT32, reg);
1513 2 : }
1514 :
1515 : /* Get the common shape used by all dense arrays with a prototype at globalObj. */
1516 : static inline Shape *
1517 26089 : GetDenseArrayShape(JSContext *cx, JSObject *globalObj)
1518 : {
1519 26089 : JS_ASSERT(globalObj);
1520 :
1521 26089 : JSObject *proto = globalObj->global().getOrCreateArrayPrototype(cx);
1522 26089 : if (!proto)
1523 0 : return NULL;
1524 :
1525 : return EmptyShape::getInitialShape(cx, &ArrayClass, proto,
1526 26089 : proto->getParent(), gc::FINALIZE_OBJECT0);
1527 : }
1528 :
1529 : bool
1530 211207 : mjit::Compiler::jsop_setelem(bool popGuaranteed)
1531 : {
1532 211207 : FrameEntry *obj = frame.peek(-3);
1533 211207 : FrameEntry *id = frame.peek(-2);
1534 211207 : FrameEntry *value = frame.peek(-1);
1535 :
1536 211207 : if (!IsCacheableSetElem(obj, id, value) || monitored(PC)) {
1537 1911 : jsop_setelem_slow();
1538 1911 : return true;
1539 : }
1540 :
1541 209296 : frame.forgetMismatchedObject(obj);
1542 :
1543 : // If the object is definitely a dense array or a typed array we can generate
1544 : // code directly without using an inline cache.
1545 209296 : if (cx->typeInferenceEnabled()) {
1546 10991 : types::TypeSet *types = analysis->poppedTypes(PC, 2);
1547 :
1548 18818 : if (!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) &&
1549 7827 : !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) {
1550 : // Inline dense array path.
1551 7815 : jsop_setelem_dense();
1552 7815 : return true;
1553 : }
1554 :
1555 : #ifdef JS_METHODJIT_TYPED_ARRAY
1556 5470 : if ((value->mightBeType(JSVAL_TYPE_INT32) || value->mightBeType(JSVAL_TYPE_DOUBLE)) &&
1557 2294 : !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_TYPED_ARRAY)) {
1558 : // Inline typed array path.
1559 857 : int atype = types->getTypedArrayType(cx);
1560 857 : if (atype != TypedArray::TYPE_MAX) {
1561 752 : jsop_setelem_typed(atype);
1562 752 : return true;
1563 : }
1564 : }
1565 : #endif
1566 : }
1567 :
1568 200729 : if (id->isType(JSVAL_TYPE_DOUBLE) || !globalObj) {
1569 195035 : jsop_setelem_slow();
1570 195035 : return true;
1571 : }
1572 :
1573 : #ifdef JSGC_INCREMENTAL_MJ
1574 : // Write barrier.
1575 5694 : if (cx->compartment->needsBarrier()) {
1576 2 : jsop_setelem_slow();
1577 2 : return true;
1578 : }
1579 : #endif
1580 :
1581 5692 : SetElementICInfo ic = SetElementICInfo(JSOp(*PC));
1582 :
1583 : // One by one, check if the most important stack entries have registers,
1584 : // and if so, pin them. This is to avoid spilling and reloading from the
1585 : // stack as we incrementally allocate other registers.
1586 5692 : MaybeRegisterID pinnedValueType = frame.maybePinType(value);
1587 5692 : MaybeRegisterID pinnedValueData = frame.maybePinData(value);
1588 :
1589 : // Pin |obj| if it doesn't share a backing with |value|.
1590 5692 : MaybeRegisterID pinnedObjData;
1591 5692 : if (!obj->hasSameBacking(value))
1592 5626 : pinnedObjData = frame.maybePinData(obj);
1593 :
1594 : // Pin |id| if it doesn't share a backing with |value|.
1595 5692 : MaybeRegisterID pinnedIdData;
1596 5692 : if (!id->hasSameBacking(value))
1597 5645 : pinnedIdData = frame.maybePinData(id);
1598 :
1599 : // Note: The fact that |obj| and |value|, or |id| and |value| can be
1600 : // copies, is a little complicated, but it is safe. Explanations
1601 : // follow at each point. Keep in mind two points:
1602 : // 1) maybePin() never allocates a register, it only pins if a register
1603 : // already existed.
1604 : // 2) tempRegForData() will work fine on a pinned register.
1605 :
1606 : // Guard that the object is an object.
1607 5692 : if (!obj->isTypeKnown()) {
1608 4934 : Jump j = frame.testObject(Assembler::NotEqual, obj);
1609 4934 : stubcc.linkExit(j, Uses(3));
1610 : }
1611 :
1612 : // Guard that the id is int32.
1613 5692 : if (!id->isTypeKnown()) {
1614 2897 : Jump j = frame.testInt32(Assembler::NotEqual, id);
1615 2897 : stubcc.linkExit(j, Uses(3));
1616 : }
1617 :
1618 : // Grab a register for the object. It's safe to unpin |obj| because it
1619 : // won't have been pinned if it shares a backing with |value|. However,
1620 : // it would not be safe to copyDataIntoReg() if the value was pinned,
1621 : // since this could evict the register. So we special case.
1622 5692 : frame.maybeUnpinReg(pinnedObjData);
1623 5692 : if (obj->hasSameBacking(value) && pinnedValueData.isSet()) {
1624 61 : ic.objReg = frame.allocReg();
1625 61 : masm.move(pinnedValueData.reg(), ic.objReg);
1626 : } else {
1627 5631 : ic.objReg = frame.copyDataIntoReg(obj);
1628 : }
1629 :
1630 : // pinEntry() will ensure pinned registers for |value|. To avoid a
1631 : // double-pin assert, first unpin any registers that |value| had.
1632 5692 : frame.maybeUnpinReg(pinnedValueType);
1633 5692 : frame.maybeUnpinReg(pinnedValueData);
1634 5692 : frame.pinEntry(value, ic.vr);
1635 :
1636 : // Store rematerialization information about the key. This is the final
1637 : // register we allocate, and thus it can use tempRegForData() without
1638 : // the worry of being spilled. Once again, this is safe even if |id|
1639 : // shares a backing with |value|, because tempRegForData() will work on
1640 : // the pinned register, and |pinnedIdData| will not double-pin.
1641 5692 : frame.maybeUnpinReg(pinnedIdData);
1642 5692 : if (id->isConstant())
1643 1777 : ic.key = Int32Key::FromConstant(id->getValue().toInt32());
1644 : else
1645 3915 : ic.key = Int32Key::FromRegister(frame.tempRegForData(id));
1646 :
1647 : // Unpin the value since register allocation is complete.
1648 5692 : frame.unpinEntry(ic.vr);
1649 :
1650 : // Now it's also safe to grab remat info for obj (all exits that can
1651 : // generate stubs must have the same register state).
1652 5692 : ic.objRemat = frame.dataRematInfo(obj);
1653 :
1654 : // All patchable guards must occur after this point.
1655 : RESERVE_IC_SPACE(masm);
1656 5692 : ic.fastPathStart = masm.label();
1657 :
1658 : // Create the common out-of-line sync block, taking care to link previous
1659 : // guards here after.
1660 : RESERVE_OOL_SPACE(stubcc.masm);
1661 5692 : ic.slowPathStart = stubcc.syncExit(Uses(3));
1662 :
1663 : // Guard obj is a dense array.
1664 5692 : Shape *shape = GetDenseArrayShape(cx, globalObj);
1665 5692 : if (!shape)
1666 0 : return false;
1667 5692 : ic.shapeGuard = masm.guardShape(ic.objReg, shape);
1668 5692 : stubcc.linkExitDirect(ic.shapeGuard, ic.slowPathStart);
1669 :
1670 : // Load the dynamic elements vector.
1671 5692 : masm.loadPtr(Address(ic.objReg, JSObject::offsetOfElements()), ic.objReg);
1672 :
1673 : // Guard in range of initialized length.
1674 : Jump initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(),
1675 5692 : ic.objReg, ic.key, Assembler::BelowOrEqual);
1676 5692 : stubcc.linkExitDirect(initlenGuard, ic.slowPathStart);
1677 :
1678 : // Guard there's no hole, then store directly to the slot.
1679 5692 : if (ic.key.isConstant()) {
1680 1777 : Address slot(ic.objReg, ic.key.index() * sizeof(Value));
1681 1777 : ic.holeGuard = masm.guardNotHole(slot);
1682 1777 : masm.storeValue(ic.vr, slot);
1683 : } else {
1684 3915 : BaseIndex slot(ic.objReg, ic.key.reg(), Assembler::JSVAL_SCALE);
1685 3915 : ic.holeGuard = masm.guardNotHole(slot);
1686 3915 : masm.storeValue(ic.vr, slot);
1687 : }
1688 5692 : stubcc.linkExitDirect(ic.holeGuard, ic.slowPathStart);
1689 :
1690 5692 : stubcc.leave();
1691 : #if defined JS_POLYIC
1692 5692 : passICAddress(&ic);
1693 5692 : ic.slowPathCall = OOL_STUBCALL(STRICT_VARIANT(ic::SetElement), REJOIN_FALLTHROUGH);
1694 : #else
1695 : OOL_STUBCALL(STRICT_VARIANT(stubs::SetElem), REJOIN_FALLTHROUGH);
1696 : #endif
1697 :
1698 5692 : ic.fastPathRejoin = masm.label();
1699 :
1700 : // When generating typed array stubs, it may be necessary to call
1701 : // js_DoubleToECMAInt32(), which would clobber registers. To deal with
1702 : // this, we tell the IC exactly which registers need to be saved
1703 : // across calls.
1704 5692 : ic.volatileMask = frame.regsInUse();
1705 :
1706 : // If the RHS will be popped, and doesn't overlap any live values, then
1707 : // there's no need to save it across calls. Note that this is not true of
1708 : // |obj| or |key|, which will be used to compute the LHS reference for
1709 : // assignment.
1710 : //
1711 : // Note that the IC wants to clobber |vr.dataReg| to convert for typed
1712 : // arrays. If this clobbering is necessary, we must preserve dataReg,
1713 : // even if it's not in a volatile register.
1714 21509 : if (popGuaranteed &&
1715 5476 : !ic.vr.isConstant() &&
1716 3655 : !value->isCopy() &&
1717 3343 : !frame.haveSameBacking(value, obj) &&
1718 3343 : !frame.haveSameBacking(value, id))
1719 : {
1720 3343 : ic.volatileMask &= ~Registers::maskReg(ic.vr.dataReg());
1721 3343 : if (!ic.vr.isTypeKnown())
1722 2411 : ic.volatileMask &= ~Registers::maskReg(ic.vr.typeReg());
1723 2349 : } else if (!ic.vr.isConstant()) {
1724 470 : ic.volatileMask |= Registers::maskReg(ic.vr.dataReg());
1725 : }
1726 :
1727 5692 : frame.freeReg(ic.objReg);
1728 5692 : frame.shimmy(2);
1729 5692 : stubcc.rejoin(Changes(2));
1730 :
1731 : #if defined JS_POLYIC
1732 5692 : if (!setElemICs.append(ic))
1733 0 : return false;
1734 : #endif
1735 :
1736 5692 : return true;
1737 : }
1738 :
1739 : static inline bool
1740 252899 : IsCacheableGetElem(FrameEntry *obj, FrameEntry *id)
1741 : {
1742 476779 : if (id->isTypeKnown() &&
1743 225209 : !(id->isType(JSVAL_TYPE_INT32) || id->isType(JSVAL_TYPE_DOUBLE)
1744 : #if defined JS_POLYIC
1745 1370 : || id->isType(JSVAL_TYPE_STRING)
1746 : #endif
1747 2762 : )) {
1748 63 : return false;
1749 : }
1750 :
1751 252836 : if (id->isType(JSVAL_TYPE_DOUBLE) && id->isConstant())
1752 20 : return false;
1753 :
1754 464866 : if (id->isType(JSVAL_TYPE_INT32) && id->isConstant() &&
1755 212050 : id->getValue().toInt32() < 0) {
1756 6 : return false;
1757 : }
1758 :
1759 : // obj[obj] is not allowed, since it will never optimize.
1760 252810 : if (obj->hasSameBacking(id))
1761 5 : return false;
1762 :
1763 252805 : return true;
1764 : }
1765 :
1766 : void
1767 24592 : mjit::Compiler::jsop_getelem_dense(bool isPacked)
1768 : {
1769 24592 : FrameEntry *obj = frame.peek(-2);
1770 24592 : FrameEntry *id = frame.peek(-1);
1771 :
1772 : // We might not know whether this is an object, but if it is an object we
1773 : // know it is a dense array.
1774 24592 : if (!obj->isTypeKnown()) {
1775 3246 : Jump guard = frame.testObject(Assembler::NotEqual, obj);
1776 3246 : stubcc.linkExit(guard, Uses(2));
1777 : }
1778 :
1779 24592 : if (id->isType(JSVAL_TYPE_DOUBLE))
1780 2 : tryConvertInteger(id, Uses(2));
1781 :
1782 : // Test for integer index.
1783 24592 : if (!id->isTypeKnown()) {
1784 9663 : Jump guard = frame.testInt32(Assembler::NotEqual, id);
1785 9663 : stubcc.linkExit(guard, Uses(2));
1786 : }
1787 :
1788 24592 : JSValueType type = knownPushedType(0);
1789 :
1790 : // Allocate registers.
1791 :
1792 : // If we know the result of the GETELEM may be undefined, then misses on the
1793 : // initialized length or hole checks can just produce an undefined value.
1794 : // We checked in the caller that prototypes do not have indexed properties.
1795 24592 : bool allowUndefined = mayPushUndefined(0);
1796 :
1797 24592 : analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 1));
1798 24592 : analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0));
1799 20053 : bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) &&
1800 44645 : loop->hoistArrayLengthCheck(DENSE_ARRAY, objv, indexv);
1801 :
1802 : // Get a register with either the object or its slots, depending on whether
1803 : // we are hoisting the slots computation.
1804 : RegisterID baseReg;
1805 24592 : if (hoisted) {
1806 848 : FrameEntry *slotsFe = loop->invariantArraySlots(objv);
1807 848 : baseReg = frame.tempRegForData(slotsFe);
1808 : } else {
1809 23744 : baseReg = frame.tempRegForData(obj);
1810 : }
1811 24592 : frame.pinReg(baseReg);
1812 :
1813 24592 : Int32Key key = id->isConstant()
1814 6688 : ? Int32Key::FromConstant(id->getValue().toInt32())
1815 31280 : : Int32Key::FromRegister(frame.tempRegForData(id));
1816 24592 : bool pinKey = !key.isConstant() && key.reg() != baseReg;
1817 24592 : if (pinKey)
1818 17904 : frame.pinReg(key.reg());
1819 :
1820 24592 : RegisterID dataReg = frame.allocReg();
1821 :
1822 24592 : MaybeRegisterID typeReg;
1823 24592 : if (type == JSVAL_TYPE_UNKNOWN || type == JSVAL_TYPE_DOUBLE || hasTypeBarriers(PC))
1824 19863 : typeReg = frame.allocReg();
1825 :
1826 24592 : frame.unpinReg(baseReg);
1827 24592 : if (pinKey)
1828 17904 : frame.unpinReg(key.reg());
1829 :
1830 : RegisterID slotsReg;
1831 24592 : if (hoisted) {
1832 848 : slotsReg = baseReg;
1833 : } else {
1834 23744 : masm.loadPtr(Address(baseReg, JSObject::offsetOfElements()), dataReg);
1835 23744 : slotsReg = dataReg;
1836 : }
1837 :
1838 : // Guard on the array's initialized length.
1839 24592 : MaybeJump initlenGuard;
1840 24592 : if (!hoisted) {
1841 : initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(),
1842 23744 : slotsReg, key, Assembler::BelowOrEqual);
1843 23744 : if (!allowUndefined)
1844 22732 : stubcc.linkExit(initlenGuard.get(), Uses(2));
1845 : }
1846 :
1847 : // Get the slot, skipping the hole check if the array is known to be packed.
1848 24592 : Jump holeCheck;
1849 24592 : if (key.isConstant()) {
1850 6688 : Address slot(slotsReg, key.index() * sizeof(Value));
1851 6688 : holeCheck = masm.fastArrayLoadSlot(slot, !isPacked, typeReg, dataReg);
1852 : } else {
1853 17904 : JS_ASSERT(key.reg() != dataReg);
1854 17904 : BaseIndex slot(slotsReg, key.reg(), masm.JSVAL_SCALE);
1855 17904 : holeCheck = masm.fastArrayLoadSlot(slot, !isPacked, typeReg, dataReg);
1856 : }
1857 :
1858 24592 : if (!isPacked && !allowUndefined)
1859 1897 : stubcc.linkExit(holeCheck, Uses(2));
1860 :
1861 24592 : stubcc.leave();
1862 24592 : OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
1863 24592 : testPushedType(REJOIN_FALLTHROUGH, -2);
1864 :
1865 24592 : frame.popn(2);
1866 :
1867 24592 : BarrierState barrier;
1868 24592 : if (typeReg.isSet()) {
1869 19863 : frame.pushRegs(typeReg.reg(), dataReg, type);
1870 19863 : barrier = testBarrier(typeReg.reg(), dataReg, false);
1871 : } else {
1872 4729 : frame.pushTypedPayload(type, dataReg);
1873 : }
1874 :
1875 24592 : stubcc.rejoin(Changes(2));
1876 :
1877 24592 : if (allowUndefined) {
1878 1022 : if (!hoisted)
1879 1012 : stubcc.linkExitDirect(initlenGuard.get(), stubcc.masm.label());
1880 1022 : if (!isPacked)
1881 131 : stubcc.linkExitDirect(holeCheck, stubcc.masm.label());
1882 1022 : JS_ASSERT(type == JSVAL_TYPE_UNKNOWN || type == JSVAL_TYPE_UNDEFINED);
1883 1022 : if (type == JSVAL_TYPE_UNDEFINED)
1884 224 : stubcc.masm.loadValuePayload(UndefinedValue(), dataReg);
1885 : else
1886 798 : stubcc.masm.loadValueAsComponents(UndefinedValue(), typeReg.reg(), dataReg);
1887 1022 : stubcc.linkRejoin(stubcc.masm.jump());
1888 : }
1889 :
1890 24592 : finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
1891 24592 : }
1892 :
1893 : void
1894 194 : mjit::Compiler::jsop_getelem_args()
1895 : {
1896 194 : FrameEntry *id = frame.peek(-1);
1897 :
1898 194 : if (id->isType(JSVAL_TYPE_DOUBLE))
1899 0 : tryConvertInteger(id, Uses(2));
1900 :
1901 : // Test for integer index.
1902 194 : if (!id->isTypeKnown()) {
1903 1 : Jump guard = frame.testInt32(Assembler::NotEqual, id);
1904 1 : stubcc.linkExit(guard, Uses(2));
1905 : }
1906 :
1907 : // Allocate registers.
1908 :
1909 194 : analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0));
1910 57 : bool hoistedLength = loop && id->isType(JSVAL_TYPE_INT32) &&
1911 251 : loop->hoistArgsLengthCheck(indexv);
1912 194 : FrameEntry *actualsFe = loop ? loop->invariantArguments() : NULL;
1913 :
1914 194 : Int32Key key = id->isConstant()
1915 135 : ? Int32Key::FromConstant(id->getValue().toInt32())
1916 329 : : Int32Key::FromRegister(frame.tempRegForData(id));
1917 194 : if (!key.isConstant())
1918 59 : frame.pinReg(key.reg());
1919 :
1920 194 : RegisterID dataReg = frame.allocReg();
1921 194 : RegisterID typeReg = frame.allocReg();
1922 :
1923 : // Guard on nactual.
1924 194 : if (!hoistedLength) {
1925 192 : Address nactualAddr(JSFrameReg, StackFrame::offsetOfNumActual());
1926 192 : MaybeJump rangeGuard;
1927 192 : if (key.isConstant()) {
1928 135 : JS_ASSERT(key.index() >= 0);
1929 135 : rangeGuard = masm.branch32(Assembler::BelowOrEqual, nactualAddr, Imm32(key.index()));
1930 : } else {
1931 57 : rangeGuard = masm.branch32(Assembler::BelowOrEqual, nactualAddr, key.reg());
1932 : }
1933 192 : stubcc.linkExit(rangeGuard.get(), Uses(2));
1934 : }
1935 :
1936 : RegisterID actualsReg;
1937 194 : if (actualsFe) {
1938 35 : actualsReg = frame.tempRegForData(actualsFe);
1939 : } else {
1940 159 : actualsReg = dataReg;
1941 159 : masm.loadFrameActuals(outerScript->function(), actualsReg);
1942 : }
1943 :
1944 194 : if (!key.isConstant())
1945 59 : frame.unpinReg(key.reg());
1946 :
1947 194 : if (key.isConstant()) {
1948 135 : Address arg(actualsReg, key.index() * sizeof(Value));
1949 135 : masm.loadValueAsComponents(arg, typeReg, dataReg);
1950 : } else {
1951 59 : JS_ASSERT(key.reg() != dataReg);
1952 59 : BaseIndex arg(actualsReg, key.reg(), masm.JSVAL_SCALE);
1953 59 : masm.loadValueAsComponents(arg, typeReg, dataReg);
1954 : }
1955 :
1956 194 : stubcc.leave();
1957 194 : OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
1958 194 : testPushedType(REJOIN_FALLTHROUGH, -2);
1959 :
1960 194 : frame.popn(2);
1961 194 : frame.pushRegs(typeReg, dataReg, knownPushedType(0));
1962 194 : BarrierState barrier = testBarrier(typeReg, dataReg, false);
1963 :
1964 194 : stubcc.rejoin(Changes(2));
1965 :
1966 194 : finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
1967 194 : }
1968 :
1969 : #ifdef JS_METHODJIT_TYPED_ARRAY
1970 : bool
1971 1286 : mjit::Compiler::jsop_getelem_typed(int atype)
1972 : {
1973 : // Unlike dense arrays, the types of elements in typed arrays are not
1974 : // guaranteed to be present in the object's type, and we need to use
1975 : // knowledge about the possible contents of the array vs. the types
1976 : // that have been read out of it to figure out how to do the load.
1977 :
1978 : // Array contents
1979 : // Float Uint32_t Int32
1980 : // Observed types
1981 : //
1982 : // {int} XXX reg pair+test reg
1983 : // {int,float} FP reg FP reg reg pair
1984 : // {X,int} XXX reg pair+test reg pair
1985 : // {X,int,float} reg pair reg pair reg pair
1986 : // {X} XXX XXX XXX
1987 :
1988 : // Reject entries marked 'XXX' above, and compile a normal GETELEM.
1989 1286 : types::TypeSet *pushedTypes = pushedTypeSet(0);
1990 1286 : if (atype == TypedArray::TYPE_FLOAT32 || atype == TypedArray::TYPE_FLOAT64) {
1991 710 : if (!pushedTypes->hasType(types::Type::DoubleType()))
1992 90 : return false;
1993 : } else {
1994 886 : if (!pushedTypes->hasType(types::Type::Int32Type()))
1995 219 : return false;
1996 : }
1997 :
1998 977 : FrameEntry *obj = frame.peek(-2);
1999 977 : FrameEntry *id = frame.peek(-1);
2000 :
2001 : // We might not know whether this is an object, but if it's an object we
2002 : // know it is a typed array.
2003 977 : if (!obj->isTypeKnown()) {
2004 331 : Jump guard = frame.testObject(Assembler::NotEqual, obj);
2005 331 : stubcc.linkExit(guard, Uses(2));
2006 : }
2007 :
2008 977 : if (id->isType(JSVAL_TYPE_DOUBLE))
2009 0 : tryConvertInteger(id, Uses(2));
2010 :
2011 : // Test for integer index.
2012 977 : if (!id->isTypeKnown()) {
2013 52 : Jump guard = frame.testInt32(Assembler::NotEqual, id);
2014 52 : stubcc.linkExit(guard, Uses(2));
2015 : }
2016 :
2017 : // Load object and key.
2018 977 : Int32Key key = id->isConstant()
2019 816 : ? Int32Key::FromConstant(id->getValue().toInt32())
2020 1793 : : Int32Key::FromRegister(frame.tempRegForData(id));
2021 977 : if (!key.isConstant())
2022 161 : frame.pinReg(key.reg());
2023 :
2024 977 : analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 1));
2025 977 : analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0));
2026 305 : bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) &&
2027 1282 : loop->hoistArrayLengthCheck(TYPED_ARRAY, objv, indexv);
2028 :
2029 : RegisterID objReg;
2030 977 : if (hoisted) {
2031 12 : FrameEntry *slotsFe = loop->invariantArraySlots(objv);
2032 12 : objReg = frame.tempRegForData(slotsFe);
2033 12 : frame.pinReg(objReg);
2034 : } else {
2035 965 : objReg = frame.copyDataIntoReg(obj);
2036 :
2037 : // Bounds check.
2038 965 : int lengthOffset = TypedArray::lengthOffset() + offsetof(jsval_layout, s.payload);
2039 : Jump lengthGuard = masm.guardArrayExtent(lengthOffset,
2040 965 : objReg, key, Assembler::BelowOrEqual);
2041 965 : stubcc.linkExit(lengthGuard, Uses(2));
2042 :
2043 : // Load the array's packed data vector.
2044 965 : masm.loadPtr(Address(objReg, TypedArray::dataOffset()), objReg);
2045 : }
2046 :
2047 : // We can load directly into an FP-register if the following conditions
2048 : // are met:
2049 : // 1) The array is an Uint32Array or a float array (loadFromTypedArray
2050 : // can't load into an FP-register for other arrays).
2051 : // 2) The result is definitely a double (the result type set can include
2052 : // other types after reading out-of-bound values).
2053 977 : AnyRegisterID dataReg;
2054 977 : MaybeRegisterID typeReg, tempReg;
2055 977 : JSValueType type = knownPushedType(0);
2056 : bool maybeReadFloat = (atype == TypedArray::TYPE_FLOAT32 ||
2057 : atype == TypedArray::TYPE_FLOAT64 ||
2058 977 : atype == TypedArray::TYPE_UINT32);
2059 977 : if (maybeReadFloat && type == JSVAL_TYPE_DOUBLE) {
2060 325 : dataReg = frame.allocFPReg();
2061 : // Need an extra reg to convert uint32_t to double.
2062 650 : if (atype == TypedArray::TYPE_UINT32)
2063 19 : tempReg = frame.allocReg();
2064 : } else {
2065 652 : dataReg = frame.allocReg();
2066 : // loadFromTypedArray expects a type register for Uint32Array or
2067 : // float arrays. Also allocate a type register if the result may not
2068 : // be int32_t (due to reading out-of-bound values) or if there's a
2069 : // type barrier.
2070 652 : if (maybeReadFloat || type != JSVAL_TYPE_INT32)
2071 113 : typeReg = frame.allocReg();
2072 : }
2073 :
2074 : // Load value from the array.
2075 977 : masm.loadFromTypedArray(atype, objReg, key, typeReg, dataReg, tempReg);
2076 :
2077 977 : if (hoisted)
2078 12 : frame.unpinReg(objReg);
2079 : else
2080 965 : frame.freeReg(objReg);
2081 977 : if (!key.isConstant())
2082 161 : frame.unpinReg(key.reg());
2083 977 : if (tempReg.isSet())
2084 19 : frame.freeReg(tempReg.reg());
2085 :
2086 1074 : if (atype == TypedArray::TYPE_UINT32 &&
2087 97 : !pushedTypes->hasType(types::Type::DoubleType())) {
2088 78 : Jump isDouble = masm.testDouble(Assembler::Equal, typeReg.reg());
2089 78 : stubcc.linkExit(isDouble, Uses(2));
2090 : }
2091 :
2092 977 : stubcc.leave();
2093 977 : OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
2094 977 : testPushedType(REJOIN_FALLTHROUGH, -2);
2095 :
2096 977 : frame.popn(2);
2097 :
2098 977 : BarrierState barrier;
2099 977 : if (dataReg.isFPReg()) {
2100 325 : frame.pushDouble(dataReg.fpreg());
2101 652 : } else if (typeReg.isSet()) {
2102 113 : frame.pushRegs(typeReg.reg(), dataReg.reg(), knownPushedType(0));
2103 : } else {
2104 539 : JS_ASSERT(type == JSVAL_TYPE_INT32);
2105 539 : frame.pushTypedPayload(JSVAL_TYPE_INT32, dataReg.reg());
2106 : }
2107 977 : stubcc.rejoin(Changes(2));
2108 :
2109 977 : finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
2110 :
2111 977 : return true;
2112 : }
2113 : #endif /* JS_METHODJIT_TYPED_ARRAY */
2114 :
2115 : bool
2116 252899 : mjit::Compiler::jsop_getelem()
2117 : {
2118 252899 : FrameEntry *obj = frame.peek(-2);
2119 252899 : FrameEntry *id = frame.peek(-1);
2120 :
2121 252899 : if (!IsCacheableGetElem(obj, id)) {
2122 94 : jsop_getelem_slow();
2123 94 : return true;
2124 : }
2125 :
2126 : // If the object is definitely an arguments object, a dense array or a typed array
2127 : // we can generate code directly without using an inline cache.
2128 252805 : if (cx->typeInferenceEnabled() && !id->isType(JSVAL_TYPE_STRING)) {
2129 37609 : types::TypeSet *types = analysis->poppedTypes(PC, 1);
2130 37609 : if (types->isLazyArguments(cx) && !outerScript->analysis()->modifiesArguments()) {
2131 : // Inline arguments path.
2132 194 : jsop_getelem_args();
2133 194 : return true;
2134 : }
2135 :
2136 98470 : if (obj->mightBeType(JSVAL_TYPE_OBJECT) &&
2137 36425 : !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) &&
2138 24630 : !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) {
2139 : // Inline dense array path.
2140 24592 : bool packed = !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY);
2141 24592 : jsop_getelem_dense(packed);
2142 24592 : return true;
2143 : }
2144 :
2145 : #ifdef JS_METHODJIT_TYPED_ARRAY
2146 24656 : if (obj->mightBeType(JSVAL_TYPE_OBJECT) &&
2147 11833 : !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_TYPED_ARRAY)) {
2148 : // Inline typed array path.
2149 1475 : int atype = types->getTypedArrayType(cx);
2150 1475 : if (atype != TypedArray::TYPE_MAX) {
2151 1286 : if (jsop_getelem_typed(atype))
2152 977 : return true;
2153 : // Fallthrough to the normal GETELEM path.
2154 : }
2155 : }
2156 : #endif
2157 : }
2158 :
2159 227042 : frame.forgetMismatchedObject(obj);
2160 :
2161 227042 : if (id->isType(JSVAL_TYPE_DOUBLE) || !globalObj) {
2162 205347 : jsop_getelem_slow();
2163 205347 : return true;
2164 : }
2165 :
2166 21695 : GetElementICInfo ic = GetElementICInfo(JSOp(*PC));
2167 :
2168 : // Pin the top of the stack to avoid spills, before allocating registers.
2169 21695 : MaybeRegisterID pinnedIdData = frame.maybePinData(id);
2170 21695 : MaybeRegisterID pinnedIdType = frame.maybePinType(id);
2171 :
2172 21695 : MaybeJump objTypeGuard;
2173 21695 : if (!obj->isTypeKnown()) {
2174 : // Test the type of the object without spilling the payload.
2175 17682 : MaybeRegisterID pinnedObjData = frame.maybePinData(obj);
2176 17682 : Jump guard = frame.testObject(Assembler::NotEqual, obj);
2177 17682 : frame.maybeUnpinReg(pinnedObjData);
2178 :
2179 : // Create a sync path, which we'll rejoin manually later. This is safe
2180 : // as long as the IC does not build a stub; it won't, because |obj|
2181 : // won't be an object. If we extend this IC to support strings, all
2182 : // that needs to change is a little code movement.
2183 17682 : stubcc.linkExit(guard, Uses(2));
2184 17682 : objTypeGuard = stubcc.masm.jump();
2185 : }
2186 :
2187 : // Get a mutable register for the object. This will be the data reg.
2188 21695 : ic.objReg = frame.copyDataIntoReg(obj);
2189 :
2190 : // Get a mutable register for pushing the result type. We kill two birds
2191 : // with one stone by making sure, if the key type is not known, to be loaded
2192 : // into this register. In this case it is both an input and an output.
2193 21695 : frame.maybeUnpinReg(pinnedIdType);
2194 21695 : if (id->isConstant() || id->isTypeKnown())
2195 13454 : ic.typeReg = frame.allocReg();
2196 : else
2197 8241 : ic.typeReg = frame.copyTypeIntoReg(id);
2198 :
2199 : // Fill in the id value.
2200 21695 : frame.maybeUnpinReg(pinnedIdData);
2201 21695 : if (id->isConstant()) {
2202 10232 : ic.id = ValueRemat::FromConstant(id->getValue());
2203 : } else {
2204 11463 : RegisterID dataReg = frame.tempRegForData(id);
2205 11463 : if (id->isTypeKnown())
2206 3222 : ic.id = ValueRemat::FromKnownType(id->getKnownType(), dataReg);
2207 : else
2208 8241 : ic.id = ValueRemat::FromRegisters(ic.typeReg, dataReg);
2209 : }
2210 :
2211 : RESERVE_IC_SPACE(masm);
2212 21695 : ic.fastPathStart = masm.label();
2213 :
2214 : // Note: slow path here is safe, since the frame will not be modified.
2215 : RESERVE_OOL_SPACE(stubcc.masm);
2216 21695 : ic.slowPathStart = stubcc.masm.label();
2217 21695 : frame.sync(stubcc.masm, Uses(2));
2218 :
2219 21695 : if (id->mightBeType(JSVAL_TYPE_INT32)) {
2220 : // Always test the type first (see comment in PolyIC.h).
2221 20397 : if (!id->isTypeKnown()) {
2222 8241 : ic.typeGuard = masm.testInt32(Assembler::NotEqual, ic.typeReg);
2223 8241 : stubcc.linkExitDirect(ic.typeGuard.get(), ic.slowPathStart);
2224 : }
2225 :
2226 : // Guard obj is a dense array.
2227 20397 : Shape *shape = GetDenseArrayShape(cx, globalObj);
2228 20397 : if (!shape)
2229 0 : return false;
2230 20397 : ic.shapeGuard = masm.guardShape(ic.objReg, shape);
2231 20397 : stubcc.linkExitDirect(ic.shapeGuard, ic.slowPathStart);
2232 :
2233 20397 : Int32Key key = id->isConstant()
2234 10205 : ? Int32Key::FromConstant(id->getValue().toInt32())
2235 30602 : : Int32Key::FromRegister(ic.id.dataReg());
2236 :
2237 : Assembler::FastArrayLoadFails fails =
2238 20397 : masm.fastArrayLoad(ic.objReg, key, ic.typeReg, ic.objReg);
2239 :
2240 20397 : stubcc.linkExitDirect(fails.rangeCheck, ic.slowPathStart);
2241 20397 : stubcc.linkExitDirect(fails.holeCheck, ic.slowPathStart);
2242 : } else {
2243 : // The type is known to not be dense-friendly ahead of time, so always
2244 : // fall back to a slow path.
2245 1298 : ic.shapeGuard = masm.jump();
2246 1298 : stubcc.linkExitDirect(ic.shapeGuard, ic.slowPathStart);
2247 : }
2248 :
2249 21695 : stubcc.leave();
2250 21695 : if (objTypeGuard.isSet())
2251 17682 : objTypeGuard.get().linkTo(stubcc.masm.label(), &stubcc.masm);
2252 : #ifdef JS_POLYIC
2253 21695 : passICAddress(&ic);
2254 21695 : ic.slowPathCall = OOL_STUBCALL(ic::GetElement, REJOIN_FALLTHROUGH);
2255 : #else
2256 : ic.slowPathCall = OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
2257 : #endif
2258 :
2259 21695 : testPushedType(REJOIN_FALLTHROUGH, -2);
2260 :
2261 21695 : ic.fastPathRejoin = masm.label();
2262 21695 : ic.forcedTypeBarrier = analysis->getCode(PC).getStringElement;
2263 :
2264 : CHECK_IC_SPACE();
2265 :
2266 21695 : frame.popn(2);
2267 21695 : frame.pushRegs(ic.typeReg, ic.objReg, knownPushedType(0));
2268 : BarrierState barrier = testBarrier(ic.typeReg, ic.objReg, false, false,
2269 21695 : /* force = */ ic.forcedTypeBarrier);
2270 :
2271 21695 : stubcc.rejoin(Changes(1));
2272 :
2273 : #ifdef JS_POLYIC
2274 21695 : if (!getElemICs.append(ic))
2275 0 : return false;
2276 : #endif
2277 :
2278 21695 : finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
2279 21695 : return true;
2280 : }
2281 :
2282 : static inline bool
2283 28425 : ReallySimpleStrictTest(FrameEntry *fe)
2284 : {
2285 28425 : if (!fe->isTypeKnown())
2286 15445 : return false;
2287 12980 : JSValueType type = fe->getKnownType();
2288 12980 : return type == JSVAL_TYPE_NULL || type == JSVAL_TYPE_UNDEFINED;
2289 : }
2290 :
2291 : static inline bool
2292 24742 : BooleanStrictTest(FrameEntry *fe)
2293 : {
2294 24742 : return fe->isConstant() && fe->getKnownType() == JSVAL_TYPE_BOOLEAN;
2295 : }
2296 :
2297 : void
2298 15082 : mjit::Compiler::jsop_stricteq(JSOp op)
2299 : {
2300 15082 : FrameEntry *rhs = frame.peek(-1);
2301 15082 : FrameEntry *lhs = frame.peek(-2);
2302 :
2303 15082 : Assembler::Condition cond = (op == JSOP_STRICTEQ) ? Assembler::Equal : Assembler::NotEqual;
2304 :
2305 : /*
2306 : * NB: x64 can do full-Value comparisons. This is beneficial
2307 : * to do if the payload/type are not yet in registers.
2308 : */
2309 :
2310 : /* Constant-fold. */
2311 15082 : if (lhs->isConstant() && rhs->isConstant()) {
2312 : bool b;
2313 408 : StrictlyEqual(cx, lhs->getValue(), rhs->getValue(), &b);
2314 408 : frame.popn(2);
2315 408 : frame.push(BooleanValue((op == JSOP_STRICTEQ) ? b : !b));
2316 408 : return;
2317 : }
2318 :
2319 14674 : if (frame.haveSameBacking(lhs, rhs)) {
2320 : /* False iff NaN. */
2321 269 : frame.pop();
2322 :
2323 269 : if (lhs->isTypeKnown() && lhs->isNotType(JSVAL_TYPE_DOUBLE)) {
2324 113 : frame.pop();
2325 113 : frame.push(BooleanValue(op == JSOP_STRICTEQ));
2326 113 : return;
2327 : }
2328 :
2329 156 : if (lhs->isType(JSVAL_TYPE_DOUBLE))
2330 7 : frame.forgetKnownDouble(lhs);
2331 :
2332 : /* Assume NaN is either in canonical form or has the sign bit set (by jsop_neg). */
2333 156 : RegisterID result = frame.allocReg(Registers::SingleByteRegs).reg();
2334 156 : RegisterID treg = frame.copyTypeIntoReg(lhs);
2335 :
2336 156 : Assembler::Condition oppositeCond = (op == JSOP_STRICTEQ) ? Assembler::NotEqual : Assembler::Equal;
2337 :
2338 : /* Ignore the sign bit. */
2339 156 : masm.lshiftPtr(Imm32(1), treg);
2340 : #ifdef JS_CPU_SPARC
2341 : /* On Sparc the result 0/0 is 0x7FFFFFFF not 0x7FF80000 */
2342 : static const int ShiftedCanonicalNaNType1 = 0x7FFFFFFF << 1;
2343 : static const int ShiftedCanonicalNaNType2 = 0x7FF80000 << 1;
2344 : RegisterID result1 = frame.allocReg();
2345 : masm.setPtr(oppositeCond, treg, Imm32(ShiftedCanonicalNaNType1), result1);
2346 : masm.setPtr(oppositeCond, treg, Imm32(ShiftedCanonicalNaNType2), result);
2347 : if(op == JSOP_STRICTEQ) {
2348 : masm.and32(result1, result);
2349 : } else {
2350 : masm.or32(result1, result);
2351 : }
2352 : frame.freeReg(result1);
2353 : #elif defined(JS_CPU_MIPS)
2354 : /* On MIPS the result 0.0/0.0 is 0x7FF7FFFF.
2355 : We need to manually set it to 0x7FF80000. */
2356 : static const int ShiftedCanonicalNaNType = 0x7FF80000 << 1;
2357 : masm.setShiftedCanonicalNaN(treg, treg);
2358 : masm.setPtr(oppositeCond, treg, Imm32(ShiftedCanonicalNaNType), result);
2359 : #elif !defined(JS_CPU_X64)
2360 : static const int ShiftedCanonicalNaNType = 0x7FF80000 << 1;
2361 156 : masm.setPtr(oppositeCond, treg, Imm32(ShiftedCanonicalNaNType), result);
2362 : #else
2363 : static const void *ShiftedCanonicalNaNType = (void *)(0x7FF8000000000000 << 1);
2364 : masm.move(ImmPtr(ShiftedCanonicalNaNType), Registers::ScratchReg);
2365 : masm.setPtr(oppositeCond, treg, Registers::ScratchReg, result);
2366 : #endif
2367 156 : frame.freeReg(treg);
2368 :
2369 156 : frame.pop();
2370 156 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
2371 156 : return;
2372 : }
2373 :
2374 : /* Comparison against undefined or null is super easy. */
2375 : bool lhsTest;
2376 14405 : if ((lhsTest = ReallySimpleStrictTest(lhs)) || ReallySimpleStrictTest(rhs)) {
2377 2034 : FrameEntry *test = lhsTest ? rhs : lhs;
2378 2034 : FrameEntry *known = lhsTest ? lhs : rhs;
2379 2034 : RegisterID result = frame.allocReg(Registers::SingleByteRegs).reg();
2380 :
2381 2034 : if (test->isTypeKnown()) {
2382 585 : masm.move(Imm32((test->getKnownType() == known->getKnownType()) ==
2383 585 : (op == JSOP_STRICTEQ)), result);
2384 585 : frame.popn(2);
2385 585 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
2386 585 : return;
2387 : }
2388 :
2389 : /* This is only true if the other side is |null|. */
2390 : #ifndef JS_CPU_X64
2391 1449 : JSValueTag mask = known->getKnownTag();
2392 1449 : if (frame.shouldAvoidTypeRemat(test))
2393 1 : masm.set32(cond, masm.tagOf(frame.addressOf(test)), Imm32(mask), result);
2394 : else
2395 1448 : masm.set32(cond, frame.tempRegForType(test), Imm32(mask), result);
2396 : #else
2397 : RegisterID maskReg = frame.allocReg();
2398 : masm.move(ImmTag(known->getKnownTag()), maskReg);
2399 :
2400 : RegisterID r = frame.tempRegForType(test);
2401 : masm.setPtr(cond, r, maskReg, result);
2402 :
2403 : frame.freeReg(maskReg);
2404 : #endif
2405 1449 : frame.popn(2);
2406 1449 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
2407 1449 : return;
2408 : }
2409 :
2410 : /* Hardcoded booleans are easy too. */
2411 12371 : if ((lhsTest = BooleanStrictTest(lhs)) || BooleanStrictTest(rhs)) {
2412 940 : FrameEntry *test = lhsTest ? rhs : lhs;
2413 :
2414 940 : if (test->isTypeKnown() && test->isNotType(JSVAL_TYPE_BOOLEAN)) {
2415 26 : RegisterID result = frame.allocReg(Registers::SingleByteRegs).reg();
2416 26 : frame.popn(2);
2417 :
2418 26 : masm.move(Imm32(op == JSOP_STRICTNE), result);
2419 26 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
2420 26 : return;
2421 : }
2422 :
2423 914 : if (test->isConstant()) {
2424 0 : frame.popn(2);
2425 0 : const Value &L = lhs->getValue();
2426 0 : const Value &R = rhs->getValue();
2427 0 : frame.push(BooleanValue((L.toBoolean() == R.toBoolean()) == (op == JSOP_STRICTEQ)));
2428 0 : return;
2429 : }
2430 :
2431 914 : RegisterID data = frame.copyDataIntoReg(test);
2432 :
2433 914 : RegisterID result = data;
2434 914 : if (!(Registers::maskReg(data) & Registers::SingleByteRegs))
2435 284 : result = frame.allocReg(Registers::SingleByteRegs).reg();
2436 :
2437 914 : Jump notBoolean;
2438 914 : if (!test->isTypeKnown())
2439 812 : notBoolean = frame.testBoolean(Assembler::NotEqual, test);
2440 :
2441 : /* Do a dynamic test. */
2442 914 : bool val = lhsTest ? lhs->getValue().toBoolean() : rhs->getValue().toBoolean();
2443 914 : masm.set32(cond, data, Imm32(val), result);
2444 :
2445 914 : if (!test->isTypeKnown()) {
2446 812 : Jump done = masm.jump();
2447 812 : notBoolean.linkTo(masm.label(), &masm);
2448 812 : masm.move(Imm32((op == JSOP_STRICTNE)), result);
2449 812 : done.linkTo(masm.label(), &masm);
2450 : }
2451 :
2452 914 : if (data != result)
2453 284 : frame.freeReg(data);
2454 :
2455 914 : frame.popn(2);
2456 914 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
2457 914 : return;
2458 : }
2459 :
2460 11431 : if (lhs->isType(JSVAL_TYPE_STRING) || rhs->isType(JSVAL_TYPE_STRING)) {
2461 1431 : FrameEntry *maybeNotStr = lhs->isType(JSVAL_TYPE_STRING) ? rhs : lhs;
2462 :
2463 1431 : if (maybeNotStr->isNotType(JSVAL_TYPE_STRING)) {
2464 83 : frame.popn(2);
2465 83 : frame.push(BooleanValue(op == JSOP_STRICTNE));
2466 83 : return;
2467 : }
2468 :
2469 1348 : if (!maybeNotStr->isTypeKnown()) {
2470 1057 : JS_ASSERT(!maybeNotStr->isConstant());
2471 1057 : Jump j = frame.testString(Assembler::NotEqual, maybeNotStr);
2472 1057 : stubcc.linkExit(j, Uses(2));
2473 : }
2474 :
2475 1348 : FrameEntry *op1 = lhs->isConstant() ? rhs : lhs;
2476 1348 : FrameEntry *op2 = lhs->isConstant() ? lhs : rhs;
2477 1348 : JS_ASSERT(!op1->isConstant());
2478 :
2479 : /* ReturnReg is safely usable with set32, since %ah can be accessed. */
2480 1348 : RegisterID resultReg = Registers::ReturnReg;
2481 1348 : frame.takeReg(resultReg);
2482 1348 : RegisterID tmpReg = frame.allocReg();
2483 1348 : RegisterID reg1 = frame.tempRegForData(op1);
2484 1348 : frame.pinReg(reg1);
2485 :
2486 : RegisterID reg2;
2487 1348 : if (op2->isConstant()) {
2488 1137 : reg2 = frame.allocReg();
2489 1137 : JSString *str = op2->getValue().toString();
2490 1137 : JS_ASSERT(str->isAtom());
2491 1137 : masm.move(ImmPtr(str), reg2);
2492 : } else {
2493 211 : reg2 = frame.tempRegForData(op2);
2494 211 : frame.pinReg(reg2);
2495 : }
2496 :
2497 1348 : JS_ASSERT(reg1 != resultReg);
2498 1348 : JS_ASSERT(reg1 != tmpReg);
2499 1348 : JS_ASSERT(reg2 != resultReg);
2500 1348 : JS_ASSERT(reg2 != tmpReg);
2501 :
2502 : /* JSString::isAtom === (lengthAndFlags & ATOM_MASK == 0) */
2503 : JS_STATIC_ASSERT(JSString::ATOM_FLAGS == 0);
2504 1348 : Imm32 atomMask(JSString::ATOM_MASK);
2505 :
2506 1348 : masm.load32(Address(reg1, JSString::offsetOfLengthAndFlags()), tmpReg);
2507 1348 : Jump op1NotAtomized = masm.branchTest32(Assembler::NonZero, tmpReg, atomMask);
2508 1348 : stubcc.linkExit(op1NotAtomized, Uses(2));
2509 :
2510 1348 : if (!op2->isConstant()) {
2511 211 : masm.load32(Address(reg2, JSString::offsetOfLengthAndFlags()), tmpReg);
2512 211 : Jump op2NotAtomized = masm.branchTest32(Assembler::NonZero, tmpReg, atomMask);
2513 211 : stubcc.linkExit(op2NotAtomized, Uses(2));
2514 : }
2515 :
2516 1348 : masm.set32(cond, reg1, reg2, resultReg);
2517 :
2518 1348 : frame.unpinReg(reg1);
2519 1348 : if (op2->isConstant())
2520 1137 : frame.freeReg(reg2);
2521 : else
2522 211 : frame.unpinReg(reg2);
2523 1348 : frame.freeReg(tmpReg);
2524 :
2525 1348 : stubcc.leave();
2526 1348 : if (op == JSOP_STRICTEQ)
2527 1178 : OOL_STUBCALL_USES(stubs::StrictEq, REJOIN_NONE, Uses(2));
2528 : else
2529 170 : OOL_STUBCALL_USES(stubs::StrictNe, REJOIN_NONE, Uses(2));
2530 :
2531 1348 : frame.popn(2);
2532 1348 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, resultReg);
2533 :
2534 1348 : stubcc.rejoin(Changes(1));
2535 1348 : return;
2536 : }
2537 :
2538 13732 : if (cx->typeInferenceEnabled() &&
2539 3732 : lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT))
2540 : {
2541 117 : CompileStatus status = jsop_equality_obj_obj(op, NULL, JSOP_NOP);
2542 117 : if (status == Compile_Okay) return;
2543 10 : JS_ASSERT(status == Compile_Skipped);
2544 : }
2545 :
2546 : /* Is it impossible that both Values are ints? */
2547 24646 : if ((lhs->isTypeKnown() && lhs->isNotType(JSVAL_TYPE_INT32)) ||
2548 14753 : (rhs->isTypeKnown() && rhs->isNotType(JSVAL_TYPE_INT32))) {
2549 687 : prepareStubCall(Uses(2));
2550 :
2551 687 : if (op == JSOP_STRICTEQ)
2552 357 : INLINE_STUBCALL_USES(stubs::StrictEq, REJOIN_NONE, Uses(2));
2553 : else
2554 330 : INLINE_STUBCALL_USES(stubs::StrictNe, REJOIN_NONE, Uses(2));
2555 :
2556 687 : frame.popn(2);
2557 687 : frame.pushSynced(JSVAL_TYPE_BOOLEAN);
2558 687 : return;
2559 : }
2560 :
2561 : #if !defined JS_CPU_ARM && !defined JS_CPU_SPARC
2562 : /* Try an integer fast-path. */
2563 9206 : bool needStub = false;
2564 9206 : if (!lhs->isTypeKnown()) {
2565 8172 : Jump j = frame.testInt32(Assembler::NotEqual, lhs);
2566 8172 : stubcc.linkExit(j, Uses(2));
2567 8172 : needStub = true;
2568 : }
2569 :
2570 9206 : if (!rhs->isTypeKnown() && !frame.haveSameBacking(lhs, rhs)) {
2571 3893 : Jump j = frame.testInt32(Assembler::NotEqual, rhs);
2572 3893 : stubcc.linkExit(j, Uses(2));
2573 3893 : needStub = true;
2574 : }
2575 :
2576 9206 : FrameEntry *test = lhs->isConstant() ? rhs : lhs;
2577 9206 : FrameEntry *other = lhs->isConstant() ? lhs : rhs;
2578 :
2579 : /* ReturnReg is safely usable with set32, since %ah can be accessed. */
2580 9206 : RegisterID resultReg = Registers::ReturnReg;
2581 9206 : frame.takeReg(resultReg);
2582 9206 : RegisterID testReg = frame.tempRegForData(test);
2583 9206 : frame.pinReg(testReg);
2584 :
2585 9206 : JS_ASSERT(resultReg != testReg);
2586 :
2587 : /* Set boolean in resultReg. */
2588 9206 : if (other->isConstant()) {
2589 5022 : masm.set32(cond, testReg, Imm32(other->getValue().toInt32()), resultReg);
2590 4184 : } else if (frame.shouldAvoidDataRemat(other)) {
2591 807 : masm.set32(cond, testReg, frame.addressOf(other), resultReg);
2592 : } else {
2593 3377 : RegisterID otherReg = frame.tempRegForData(other);
2594 :
2595 3377 : JS_ASSERT(otherReg != resultReg);
2596 3377 : JS_ASSERT(otherReg != testReg);
2597 :
2598 3377 : masm.set32(cond, testReg, otherReg, resultReg);
2599 : }
2600 :
2601 9206 : frame.unpinReg(testReg);
2602 :
2603 9206 : if (needStub) {
2604 8264 : stubcc.leave();
2605 8264 : if (op == JSOP_STRICTEQ)
2606 6920 : OOL_STUBCALL_USES(stubs::StrictEq, REJOIN_NONE, Uses(2));
2607 : else
2608 1344 : OOL_STUBCALL_USES(stubs::StrictNe, REJOIN_NONE, Uses(2));
2609 : }
2610 :
2611 9206 : frame.popn(2);
2612 9206 : frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, resultReg);
2613 :
2614 9206 : if (needStub)
2615 8264 : stubcc.rejoin(Changes(1));
2616 : #else
2617 : /* TODO: Port set32() logic to ARM. */
2618 : prepareStubCall(Uses(2));
2619 :
2620 : if (op == JSOP_STRICTEQ)
2621 : INLINE_STUBCALL_USES(stubs::StrictEq, REJOIN_NONE, Uses(2));
2622 : else
2623 : INLINE_STUBCALL_USES(stubs::StrictNe, REJOIN_NONE, Uses(2));
2624 :
2625 : frame.popn(2);
2626 : frame.pushSynced(JSVAL_TYPE_BOOLEAN);
2627 : return;
2628 : #endif
2629 : }
2630 :
2631 : void
2632 206519 : mjit::Compiler::jsop_pos()
2633 : {
2634 206519 : FrameEntry *top = frame.peek(-1);
2635 :
2636 206519 : if (top->isTypeKnown()) {
2637 5184 : if (top->getKnownType() <= JSVAL_TYPE_INT32)
2638 5016 : return;
2639 168 : prepareStubCall(Uses(1));
2640 168 : INLINE_STUBCALL(stubs::Pos, REJOIN_POS);
2641 168 : frame.pop();
2642 168 : frame.pushSynced(knownPushedType(0));
2643 168 : return;
2644 : }
2645 :
2646 201335 : frame.giveOwnRegs(top);
2647 :
2648 201335 : Jump j;
2649 201335 : if (frame.shouldAvoidTypeRemat(top))
2650 192623 : j = masm.testNumber(Assembler::NotEqual, frame.addressOf(top));
2651 : else
2652 8712 : j = masm.testNumber(Assembler::NotEqual, frame.tempRegForType(top));
2653 201335 : stubcc.linkExit(j, Uses(1));
2654 :
2655 201335 : stubcc.leave();
2656 201335 : OOL_STUBCALL(stubs::Pos, REJOIN_POS);
2657 :
2658 201335 : stubcc.rejoin(Changes(1));
2659 : }
2660 :
2661 : void
2662 0 : mjit::Compiler::jsop_initmethod()
2663 : {
2664 : #ifdef DEBUG
2665 0 : FrameEntry *obj = frame.peek(-2);
2666 : #endif
2667 0 : JSAtom *atom = script->getAtom(GET_UINT32_INDEX(PC));
2668 :
2669 : /* Initializers with INITMETHOD are not fast yet. */
2670 0 : JS_ASSERT(!frame.extra(obj).initObject);
2671 :
2672 0 : prepareStubCall(Uses(2));
2673 0 : masm.move(ImmPtr(atom), Registers::ArgReg1);
2674 0 : INLINE_STUBCALL(stubs::InitMethod, REJOIN_FALLTHROUGH);
2675 0 : }
2676 :
2677 : void
2678 7628 : mjit::Compiler::jsop_initprop()
2679 : {
2680 7628 : FrameEntry *obj = frame.peek(-2);
2681 7628 : FrameEntry *fe = frame.peek(-1);
2682 7628 : JSAtom *atom = script->getAtom(GET_UINT32_INDEX(PC));
2683 :
2684 7628 : JSObject *baseobj = frame.extra(obj).initObject;
2685 :
2686 7628 : if (!baseobj || monitored(PC)) {
2687 3029 : prepareStubCall(Uses(2));
2688 3029 : masm.move(ImmPtr(atom), Registers::ArgReg1);
2689 3029 : INLINE_STUBCALL(stubs::InitProp, REJOIN_FALLTHROUGH);
2690 3029 : return;
2691 : }
2692 :
2693 : JSObject *holder;
2694 4599 : JSProperty *prop = NULL;
2695 : #ifdef DEBUG
2696 : bool res =
2697 : #endif
2698 : LookupPropertyWithFlags(cx, baseobj, ATOM_TO_JSID(atom),
2699 4599 : JSRESOLVE_QUALIFIED, &holder, &prop);
2700 4599 : JS_ASSERT(res && prop && holder == baseobj);
2701 :
2702 4599 : RegisterID objReg = frame.copyDataIntoReg(obj);
2703 :
2704 : /* Perform the store. */
2705 4599 : Shape *shape = (Shape *) prop;
2706 4599 : Address address = masm.objPropAddress(baseobj, objReg, shape->slot());
2707 4599 : frame.storeTo(fe, address);
2708 4599 : frame.freeReg(objReg);
2709 : }
2710 :
2711 : void
2712 41805 : mjit::Compiler::jsop_initelem()
2713 : {
2714 41805 : FrameEntry *obj = frame.peek(-3);
2715 41805 : FrameEntry *id = frame.peek(-2);
2716 41805 : FrameEntry *fe = frame.peek(-1);
2717 :
2718 : /*
2719 : * The initialized index is always a constant, but we won't remember which
2720 : * constant if there are branches inside the code computing the initializer
2721 : * expression (e.g. the expression uses the '?' operator). Slow path those
2722 : * cases, as well as those where INITELEM is used on an object initializer
2723 : * or a non-fast array initializer.
2724 : */
2725 41805 : if (!id->isConstant() || !frame.extra(obj).initArray) {
2726 378 : JSOp next = JSOp(PC[JSOP_INITELEM_LENGTH]);
2727 :
2728 378 : prepareStubCall(Uses(3));
2729 378 : masm.move(Imm32(next == JSOP_ENDINIT ? 1 : 0), Registers::ArgReg1);
2730 378 : INLINE_STUBCALL(stubs::InitElem, REJOIN_FALLTHROUGH);
2731 378 : return;
2732 : }
2733 :
2734 41427 : int32_t idx = id->getValue().toInt32();
2735 :
2736 41427 : RegisterID objReg = frame.copyDataIntoReg(obj);
2737 41427 : masm.loadPtr(Address(objReg, JSObject::offsetOfElements()), objReg);
2738 :
2739 : /* Update the initialized length. */
2740 41427 : masm.store32(Imm32(idx + 1), Address(objReg, ObjectElements::offsetOfInitializedLength()));
2741 :
2742 : /* Perform the store. */
2743 41427 : frame.storeTo(fe, Address(objReg, idx * sizeof(Value)));
2744 41427 : frame.freeReg(objReg);
2745 : }
|