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 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "methodjit/Compiler.h"
40 : #include "methodjit/LoopState.h"
41 : #include "methodjit/FrameState-inl.h"
42 : #include "methodjit/StubCalls.h"
43 :
44 : #include "jstypedarrayinlines.h"
45 :
46 : using namespace js;
47 : using namespace js::mjit;
48 : using namespace js::analyze;
49 : using namespace js::types;
50 :
51 : inline bool
52 3756 : SafeAdd(int32_t one, int32_t two, int32_t *res)
53 : {
54 3756 : *res = one + two;
55 3756 : int64_t ores = (int64_t)one + (int64_t)two;
56 3756 : if (ores == (int64_t)*res)
57 3750 : return true;
58 6 : JaegerSpew(JSpew_Analysis, "Overflow computing %d + %d\n", one, two);
59 6 : return false;
60 : }
61 :
62 : inline bool
63 17433 : SafeSub(int32_t one, int32_t two, int32_t *res)
64 : {
65 17433 : *res = one - two;
66 17433 : int64_t ores = (int64_t)one - (int64_t)two;
67 17433 : if (ores == (int64_t)*res)
68 17433 : return true;
69 0 : JaegerSpew(JSpew_Analysis, "Overflow computing %d - %d\n", one, two);
70 0 : return false;
71 : }
72 :
73 : inline bool
74 40 : SafeMul(int32_t one, int32_t two, int32_t *res)
75 : {
76 40 : *res = one * two;
77 40 : int64_t ores = (int64_t)one * (int64_t)two;
78 40 : if (ores == (int64_t)*res)
79 32 : return true;
80 8 : JaegerSpew(JSpew_Analysis, "Overflow computing %d * %d\n", one, two);
81 8 : return false;
82 : }
83 :
84 33602 : LoopState::LoopState(JSContext *cx, analyze::CrossScriptSSA *ssa,
85 : mjit::Compiler *cc, FrameState *frame)
86 : : cx(cx), ssa(ssa),
87 67204 : outerScript(ssa->outerScript()), outerAnalysis(outerScript->analysis()),
88 : cc(*cc), frame(*frame),
89 : lifetime(NULL), alloc(NULL), reachedEntryPoint(false), loopRegs(0), skipAnalysis(false),
90 : loopJoins(CompilerAllocPolicy(cx, *cc)),
91 : loopPatches(CompilerAllocPolicy(cx, *cc)),
92 : restoreInvariantCalls(CompilerAllocPolicy(cx, *cc)),
93 : invariantEntries(CompilerAllocPolicy(cx, *cc)),
94 : outer(NULL), temporariesStart(0),
95 : testLHS(UNASSIGNED), testRHS(UNASSIGNED),
96 : testConstant(0), testLessEqual(false),
97 : increments(CompilerAllocPolicy(cx, *cc)), unknownModset(false),
98 : growArrays(CompilerAllocPolicy(cx, *cc)),
99 : modifiedProperties(CompilerAllocPolicy(cx, *cc)),
100 100806 : constrainedLoop(true)
101 : {
102 33602 : JS_ASSERT(cx->typeInferenceEnabled());
103 33602 : }
104 :
105 : bool
106 33602 : LoopState::init(jsbytecode *head, Jump entry, jsbytecode *entryTarget)
107 : {
108 33602 : this->lifetime = outerAnalysis->getLoop(head);
109 0 : JS_ASSERT(lifetime &&
110 : lifetime->head == uint32_t(head - outerScript->code) &&
111 33602 : lifetime->entry == uint32_t(entryTarget - outerScript->code));
112 :
113 33602 : this->entry = entry;
114 :
115 33602 : analyzeLoopTest();
116 33602 : analyzeLoopIncrements();
117 70750 : for (unsigned i = 0; i < ssa->numFrames(); i++) {
118 : /* Only analyze this frame if it is nested within the loop itself. */
119 37148 : uint32_t index = ssa->iterFrame(i).index;
120 37148 : if (index != CrossScriptSSA::OUTER_FRAME) {
121 3546 : unsigned pframe = index;
122 8226 : while (ssa->getFrame(pframe).parent != CrossScriptSSA::OUTER_FRAME)
123 1134 : pframe = ssa->getFrame(pframe).parent;
124 3546 : uint32_t offset = ssa->getFrame(pframe).parentpc - outerScript->code;
125 3546 : JS_ASSERT(offset < outerScript->length);
126 3546 : if (offset < lifetime->head || offset > lifetime->backedge)
127 1946 : continue;
128 : }
129 35202 : analyzeLoopBody(index);
130 : }
131 :
132 33602 : if (testLHS != UNASSIGNED) {
133 : JaegerSpew(JSpew_Analysis, "loop test at %u: %s %s %s + %d\n", lifetime->head,
134 : frame.entryName(testLHS),
135 : testLessEqual ? "<=" : ">=",
136 2428 : (testRHS == UNASSIGNED) ? "" : frame.entryName(testRHS),
137 11153 : testConstant);
138 : }
139 :
140 43491 : for (unsigned i = 0; i < increments.length(); i++) {
141 : JaegerSpew(JSpew_Analysis, "loop increment at %u for %s: %u\n", lifetime->head,
142 9889 : frame.entryName(increments[i].slot),
143 19778 : increments[i].offset);
144 : }
145 :
146 34584 : for (unsigned i = 0; i < growArrays.length(); i++) {
147 : JaegerSpew(JSpew_Analysis, "loop grow array at %u: %s\n", lifetime->head,
148 982 : types::TypeString(types::Type::ObjectType(growArrays[i])));
149 : }
150 :
151 38193 : for (unsigned i = 0; i < modifiedProperties.length(); i++) {
152 : JaegerSpew(JSpew_Analysis, "loop modified property at %u: %s %s\n", lifetime->head,
153 4591 : types::TypeString(types::Type::ObjectType(modifiedProperties[i].object)),
154 9182 : TypeIdString(modifiedProperties[i].id));
155 : }
156 :
157 33602 : RegisterAllocation *&alloc = outerAnalysis->getAllocation(head);
158 33602 : JS_ASSERT(!alloc);
159 :
160 33602 : alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(true);
161 33602 : if (!alloc) {
162 0 : js_ReportOutOfMemory(cx);
163 0 : return false;
164 : }
165 :
166 33602 : this->alloc = alloc;
167 33602 : this->loopRegs = Registers::AvailAnyRegs;
168 :
169 : /*
170 : * Don't hoist bounds checks or loop invariant code in scripts that have
171 : * had indirect modification of their arguments.
172 : */
173 33602 : if (outerScript->function()) {
174 27088 : if (TypeSet::HasObjectFlags(cx, outerScript->function()->getType(cx), OBJECT_FLAG_UNINLINEABLE))
175 8975 : this->skipAnalysis = true;
176 : }
177 :
178 : /*
179 : * Don't hoist bounds checks or loop invariant code in loops with safe
180 : * points in the middle, which the interpreter can join at directly without
181 : * performing hoisted bounds checks or doing initial computation of loop
182 : * invariant terms.
183 : */
184 33602 : if (lifetime->hasSafePoints)
185 271 : this->skipAnalysis = true;
186 :
187 33602 : return true;
188 : }
189 :
190 : void
191 446582 : LoopState::addJoin(unsigned index, bool script)
192 : {
193 : StubJoin r;
194 446582 : r.index = index;
195 446582 : r.script = script;
196 446582 : loopJoins.append(r);
197 446582 : }
198 :
199 : void
200 31293 : LoopState::addInvariantCall(Jump jump, Label label, bool ool, bool entry, unsigned patchIndex, Uses uses)
201 : {
202 31293 : RestoreInvariantCall call;
203 31293 : call.jump = jump;
204 31293 : call.label = label;
205 31293 : call.ool = ool;
206 31293 : call.entry = entry;
207 31293 : call.patchIndex = patchIndex;
208 31293 : call.temporaryCopies = frame.getTemporaryCopies(uses);
209 :
210 31293 : restoreInvariantCalls.append(call);
211 31293 : }
212 :
213 : void
214 33590 : LoopState::flushLoop(StubCompiler &stubcc)
215 : {
216 33590 : clearLoopRegisters();
217 :
218 : /*
219 : * Patch stub compiler rejoins with loads of loop carried registers
220 : * discovered after the fact.
221 : */
222 67454 : for (unsigned i = 0; i < loopPatches.length(); i++) {
223 33864 : const StubJoinPatch &p = loopPatches[i];
224 33864 : stubcc.patchJoin(p.join.index, p.join.script, p.address, p.reg);
225 : }
226 33590 : loopJoins.clear();
227 33590 : loopPatches.clear();
228 :
229 33590 : if (hasInvariants()) {
230 10704 : for (unsigned i = 0; i < restoreInvariantCalls.length(); i++) {
231 8588 : RestoreInvariantCall &call = restoreInvariantCalls[i];
232 8588 : Assembler &masm = cc.getAssembler(true);
233 17176 : Vector<Jump> failureJumps(cx);
234 :
235 8588 : jsbytecode *pc = cc.getInvariantPC(call.patchIndex);
236 :
237 8588 : if (call.ool) {
238 8406 : call.jump.linkTo(masm.label(), &masm);
239 8406 : restoreInvariants(pc, masm, call.temporaryCopies, &failureJumps);
240 8406 : masm.jump().linkTo(call.label, &masm);
241 : } else {
242 182 : stubcc.linkExitDirect(call.jump, masm.label());
243 182 : restoreInvariants(pc, masm, call.temporaryCopies, &failureJumps);
244 182 : stubcc.crossJump(masm.jump(), call.label);
245 : }
246 :
247 8588 : if (!failureJumps.empty()) {
248 45107 : for (unsigned i = 0; i < failureJumps.length(); i++)
249 36876 : failureJumps[i].linkTo(masm.label(), &masm);
250 :
251 : /*
252 : * Call InvariantFailure, setting up the return address to
253 : * patch and any value for the call to return.
254 : */
255 8231 : InvariantCodePatch *patch = cc.getInvariantPatch(call.patchIndex);
256 8231 : patch->hasPatch = true;
257 : patch->codePatch = masm.storePtrWithPatch(ImmPtr(NULL),
258 8231 : FrameAddress(offsetof(VMFrame, scratch)));
259 : JS_STATIC_ASSERT(Registers::ReturnReg != Registers::ArgReg1);
260 8231 : masm.move(Registers::ReturnReg, Registers::ArgReg1);
261 :
262 8231 : if (call.entry) {
263 : masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::InvariantFailure),
264 1126 : pc, NULL, 0);
265 : } else {
266 : /* f.regs are already coherent, don't write new values to them. */
267 7105 : masm.infallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, stubs::InvariantFailure), -1);
268 : }
269 : }
270 : }
271 : } else {
272 54177 : for (unsigned i = 0; i < restoreInvariantCalls.length(); i++) {
273 22703 : RestoreInvariantCall &call = restoreInvariantCalls[i];
274 22703 : Assembler &masm = cc.getAssembler(call.ool);
275 22703 : call.jump.linkTo(call.label, &masm);
276 : }
277 : }
278 33590 : restoreInvariantCalls.clear();
279 33590 : }
280 :
281 : void
282 154271 : LoopState::clearLoopRegisters()
283 : {
284 154271 : alloc->clearLoops();
285 154271 : loopRegs = 0;
286 154271 : }
287 :
288 : bool
289 15693 : LoopState::loopInvariantEntry(uint32_t slot)
290 : {
291 15693 : if (slot == UNASSIGNED)
292 6297 : return true;
293 :
294 : /* Watch for loop temporaries. :XXX: this is really gross. */
295 9396 : if (slot >= analyze::LocalSlot(outerScript, outerScript->nslots))
296 1779 : return true;
297 :
298 7617 : if (slot == analyze::CalleeSlot() || outerAnalysis->slotEscapes(slot))
299 0 : return false;
300 7617 : return outerAnalysis->liveness(slot).firstWrite(lifetime) == UINT32_MAX;
301 : }
302 :
303 : inline bool
304 3946 : LoopState::entryRedundant(const InvariantEntry &e0, const InvariantEntry &e1)
305 : {
306 3946 : JS_ASSERT(e0.isCheck() && e1.isCheck());
307 :
308 3946 : uint32_t array0 = e0.u.check.arraySlot;
309 3946 : uint32_t array1 = e1.u.check.arraySlot;
310 :
311 3946 : uint32_t value01 = e0.u.check.valueSlot1;
312 3946 : uint32_t value02 = e0.u.check.valueSlot2;
313 :
314 3946 : uint32_t value11 = e1.u.check.valueSlot1;
315 3946 : uint32_t value12 = e1.u.check.valueSlot2;
316 :
317 3946 : int32_t c0 = e0.u.check.constant;
318 3946 : int32_t c1 = e1.u.check.constant;
319 :
320 : /*
321 : * initialized lengths are always <= JSObject::NELEMENTS_LIMIT, check for
322 : * integer overflow checks redundant given initialized length checks.
323 : * If Y <= c0 and Y + c1 < initlen(array):
324 : *
325 : * Y <= c0
326 : * initlen(array) - c1 <= c0
327 : * NSLOTS_LIMIT <= c0 + c1
328 : */
329 3946 : if (e0.kind == InvariantEntry::RANGE_CHECK && e1.isBoundsCheck() &&
330 : value01 == value11 && value02 == value12) {
331 : int32_t constant;
332 12 : if (c1 >= 0)
333 12 : constant = c0;
334 0 : else if (!SafeAdd(c0, c1, &constant))
335 0 : return false;
336 12 : return constant >= (int32_t) JSObject::NELEMENTS_LIMIT;
337 : }
338 :
339 : /* Look for matching tests that differ only in their constants. */
340 3934 : if (e0.kind == e1.kind && array0 == array1 && value01 == value11 && value02 == value12) {
341 1095 : if (e0.isBoundsCheck()) {
342 : /* If e0 is X >= Y + c0 and e1 is X >= Y + c1, e0 is redundant if c0 <= c1 */
343 736 : return (c0 <= c1);
344 : } else {
345 : /* If e0 is c0 >= Y and e1 is c1 >= Y, e0 is redundant if c0 >= c1 */
346 359 : return (c0 >= c1);
347 : }
348 : }
349 :
350 2839 : return false;
351 : }
352 :
353 : bool
354 2005 : LoopState::checkRedundantEntry(const InvariantEntry &entry)
355 : {
356 : /*
357 : * Return true if entry is implied by an existing entry, otherwise filter
358 : * out any existing entries which entry implies.
359 : */
360 2005 : JS_ASSERT(entry.isCheck());
361 :
362 : /* Maintain this separately, GCC miscompiles if the loop test is invariantEntries.length(). */
363 2005 : unsigned length = invariantEntries.length();
364 :
365 4904 : for (unsigned i = 0; i < length; i++) {
366 3615 : InvariantEntry &baseEntry = invariantEntries[i];
367 3615 : if (!baseEntry.isCheck())
368 1284 : continue;
369 2331 : if (entryRedundant(entry, baseEntry))
370 716 : return true;
371 1615 : if (entryRedundant(baseEntry, entry)) {
372 : /*
373 : * Make sure to maintain the existing ordering on how invariant
374 : * entries are generated, this is required for e.g. entries which
375 : * use temporaries or slot computations which appear before any
376 : * bounds checks on the arrays.
377 : */
378 206 : for (unsigned j = i; j < length - 1; j++)
379 8 : invariantEntries[j] = invariantEntries[j + 1];
380 198 : invariantEntries.popBack();
381 198 : i--;
382 198 : length--;
383 : }
384 : }
385 :
386 1289 : return false;
387 : }
388 :
389 : bool
390 1150 : LoopState::addHoistedCheck(InvariantArrayKind arrayKind, uint32_t arraySlot,
391 : uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant)
392 : {
393 : #ifdef DEBUG
394 1150 : JS_ASSERT_IF(valueSlot1 == UNASSIGNED, valueSlot2 == UNASSIGNED);
395 1150 : const char *field = (arrayKind == DENSE_ARRAY) ? "initlen" : "length";
396 1150 : if (valueSlot1 == UNASSIGNED) {
397 468 : JaegerSpew(JSpew_Analysis, "Hoist %s > %d\n", field, constant);
398 682 : } else if (valueSlot2 == UNASSIGNED) {
399 : JaegerSpew(JSpew_Analysis, "Hoisted as %s > %s + %d\n", field,
400 665 : frame.entryName(valueSlot1), constant);
401 : } else {
402 : JaegerSpew(JSpew_Analysis, "Hoisted as %s > %s + %s + %d\n", field,
403 17 : frame.entryName(valueSlot1), frame.entryName(valueSlot2), constant);
404 : }
405 : #endif
406 :
407 1150 : InvariantEntry entry;
408 : entry.kind = (arrayKind == DENSE_ARRAY)
409 : ? InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK
410 1150 : : InvariantEntry::TYPED_ARRAY_BOUNDS_CHECK;
411 1150 : entry.u.check.arraySlot = arraySlot;
412 1150 : entry.u.check.valueSlot1 = valueSlot1;
413 1150 : entry.u.check.valueSlot2 = valueSlot2;
414 1150 : entry.u.check.constant = constant;
415 :
416 1150 : if (checkRedundantEntry(entry))
417 354 : return true;
418 :
419 : /*
420 : * Maintain an invariant that for any array with a hoisted bounds check,
421 : * we also have a loop invariant slot to hold the array's slots pointer.
422 : * The compiler gets invariant array slots only for accesses with a hoisted
423 : * bounds check, so this makes invariantSlots infallible.
424 : */
425 796 : bool hasInvariantSlots = false;
426 : InvariantEntry::EntryKind slotsKind = (arrayKind == DENSE_ARRAY)
427 : ? InvariantEntry::DENSE_ARRAY_SLOTS
428 796 : : InvariantEntry::TYPED_ARRAY_SLOTS;
429 2453 : for (unsigned i = 0; !hasInvariantSlots && i < invariantEntries.length(); i++) {
430 1657 : InvariantEntry &entry = invariantEntries[i];
431 1657 : if (entry.kind == slotsKind && entry.u.array.arraySlot == arraySlot)
432 191 : hasInvariantSlots = true;
433 : }
434 796 : if (!hasInvariantSlots) {
435 605 : uint32_t which = frame.allocTemporary();
436 605 : if (which == UINT32_MAX)
437 0 : return false;
438 605 : FrameEntry *fe = frame.getTemporary(which);
439 :
440 : JaegerSpew(JSpew_Analysis, "Using %s for loop invariant slots of %s\n",
441 605 : frame.entryName(fe), frame.entryName(arraySlot));
442 :
443 605 : InvariantEntry slotsEntry;
444 605 : slotsEntry.kind = slotsKind;
445 605 : slotsEntry.u.array.arraySlot = arraySlot;
446 605 : slotsEntry.u.array.temporary = which;
447 605 : invariantEntries.append(slotsEntry);
448 : }
449 :
450 796 : invariantEntries.append(entry);
451 796 : return true;
452 : }
453 :
454 : void
455 834 : LoopState::addNegativeCheck(uint32_t valueSlot, int32_t constant)
456 : {
457 : JaegerSpew(JSpew_Analysis, "Nonnegative check %s + %d >= 0\n",
458 834 : frame.entryName(valueSlot), constant);
459 :
460 834 : InvariantEntry entry;
461 834 : entry.kind = InvariantEntry::NEGATIVE_CHECK;
462 834 : entry.u.check.valueSlot1 = valueSlot;
463 834 : entry.u.check.constant = constant;
464 :
465 834 : if (!checkRedundantEntry(entry))
466 479 : invariantEntries.append(entry);
467 834 : }
468 :
469 : void
470 21 : LoopState::addRangeCheck(uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant)
471 : {
472 : JaegerSpew(JSpew_Analysis, "Range check %d >= %s + %s\n",
473 : constant, frame.entryName(valueSlot1),
474 21 : valueSlot2 == UINT32_MAX ? "" : frame.entryName(valueSlot2));
475 :
476 21 : InvariantEntry entry;
477 21 : entry.kind = InvariantEntry::RANGE_CHECK;
478 21 : entry.u.check.valueSlot1 = valueSlot1;
479 21 : entry.u.check.valueSlot2 = valueSlot2;
480 21 : entry.u.check.constant = constant;
481 :
482 21 : if (!checkRedundantEntry(entry))
483 14 : invariantEntries.append(entry);
484 21 : }
485 :
486 : void
487 16201 : LoopState::setLoopReg(AnyRegisterID reg, FrameEntry *fe)
488 : {
489 16201 : JS_ASSERT(alloc->loop(reg));
490 16201 : loopRegs.takeReg(reg);
491 :
492 16201 : uint32_t slot = frame.outerSlot(fe);
493 : JaegerSpew(JSpew_Regalloc, "allocating loop register %s for %s\n",
494 16201 : reg.name(), frame.entryName(fe));
495 :
496 16201 : alloc->set(reg, slot, true);
497 :
498 : /*
499 : * Mark pending rejoins to patch up with the load. We don't do this now as that would
500 : * cause us to emit into the slow path, which may be in progress.
501 : */
502 50065 : for (unsigned i = 0; i < loopJoins.length(); i++) {
503 33864 : StubJoinPatch p;
504 33864 : p.join = loopJoins[i];
505 33864 : p.address = frame.addressOf(fe);
506 33864 : p.reg = reg;
507 33864 : loopPatches.append(p);
508 : }
509 :
510 16201 : if (reachedEntryPoint) {
511 : /*
512 : * We've advanced past the entry point of the loop (we're analyzing the condition),
513 : * so need to update the register state at that entry point so that the right
514 : * things get loaded when we enter the loop.
515 : */
516 642 : RegisterAllocation *alloc = outerAnalysis->getAllocation(lifetime->entry);
517 642 : JS_ASSERT(alloc && !alloc->assigned(reg));
518 642 : alloc->set(reg, slot, true);
519 : }
520 16201 : }
521 :
522 : bool
523 15148 : LoopState::hoistArrayLengthCheck(InvariantArrayKind arrayKind, const CrossSSAValue &obj,
524 : const CrossSSAValue &index)
525 : {
526 : /*
527 : * Note: this method requires that the index is definitely an integer, and
528 : * that obj is either a dense array, a typed array or not an object.
529 : */
530 15148 : if (skipAnalysis)
531 12751 : return false;
532 :
533 : uint32_t objSlot;
534 : int32_t objConstant;
535 2397 : if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
536 462 : return false;
537 :
538 : JaegerSpew(JSpew_Analysis, "Trying to hoist bounds check on %s\n",
539 1935 : frame.entryName(objSlot));
540 :
541 1935 : if (!loopInvariantEntry(objSlot)) {
542 40 : JaegerSpew(JSpew_Analysis, "Object is not loop invariant\n");
543 40 : return false;
544 : }
545 :
546 : /*
547 : * Check for an overlap with the arrays we think might grow in this loop.
548 : * This information is only a guess; if we don't think the array can grow
549 : * but it actually can, we will probably recompile after the hoisted
550 : * bounds check fails.
551 : */
552 1895 : TypeSet *objTypes = ssa->getValueTypes(obj);
553 1895 : if (arrayKind == DENSE_ARRAY && !growArrays.empty()) {
554 576 : unsigned count = objTypes->getObjectCount();
555 1299 : for (unsigned i = 0; i < count; i++) {
556 948 : if (objTypes->getSingleObject(i) != NULL) {
557 0 : JaegerSpew(JSpew_Analysis, "Object might be a singleton");
558 0 : return false;
559 : }
560 948 : TypeObject *object = objTypes->getTypeObject(i);
561 948 : if (object && hasGrowArray(object)) {
562 225 : JaegerSpew(JSpew_Analysis, "Object might grow inside loop\n");
563 225 : return false;
564 : }
565 : }
566 : }
567 :
568 : /*
569 : * Get an expression for the index 'index + indexConstant', where index
570 : * is the value of a slot at loop entry.
571 : */
572 : uint32_t indexSlot;
573 : int32_t indexConstant;
574 1670 : if (!getEntryValue(index, &indexSlot, &indexConstant)) {
575 249 : JaegerSpew(JSpew_Analysis, "Could not compute index in terms of loop entry state\n");
576 249 : return false;
577 : }
578 :
579 1421 : if (indexSlot == UNASSIGNED) {
580 : /* Hoist checks on x[n] accesses for constant n. */
581 318 : if (indexConstant < 0) {
582 0 : JaegerSpew(JSpew_Analysis, "Constant index is negative\n");
583 0 : return false;
584 : }
585 318 : return addHoistedCheck(arrayKind, objSlot, UNASSIGNED, UNASSIGNED, indexConstant);
586 : }
587 :
588 1103 : if (loopInvariantEntry(indexSlot)) {
589 : /* Hoist checks on x[y] accesses when y is loop invariant. */
590 416 : addNegativeCheck(indexSlot, indexConstant);
591 416 : return addHoistedCheck(arrayKind, objSlot, indexSlot, UNASSIGNED, indexConstant);
592 : }
593 :
594 : /*
595 : * If the LHS can decrease in the loop, it could become negative and
596 : * underflow the array. We currently only hoist bounds checks for loops
597 : * which walk arrays going forward.
598 : */
599 687 : if (!outerAnalysis->liveness(indexSlot).nonDecreasing(outerScript, lifetime)) {
600 55 : JaegerSpew(JSpew_Analysis, "Index may decrease in future iterations\n");
601 55 : return false;
602 : }
603 :
604 : /*
605 : * If the access is of the form x[y + a] where we know that y <= z + b
606 : * (both in terms of state at the head of the loop), hoist as follows:
607 : *
608 : * y + a < initlen(x)
609 : * y < initlen(x) - a
610 : * z + b < initlen(x) - a
611 : * z + b + a < initlen(x)
612 : */
613 632 : if (indexSlot == testLHS && testLessEqual) {
614 : int32_t constant;
615 399 : if (!SafeAdd(testConstant, indexConstant, &constant))
616 0 : return false;
617 :
618 : /*
619 : * Check that the LHS is nonnegative every time we rejoin the loop.
620 : * This is only really necessary on initial loop entry. Note that this
621 : * test is not sensitive to changes to the LHS between when we make
622 : * the test and the start of the next iteration, as we've ensured the
623 : * LHS is nondecreasing within the body of the loop.
624 : */
625 399 : addNegativeCheck(indexSlot, indexConstant);
626 :
627 399 : return addHoistedCheck(arrayKind, objSlot, testRHS, UNASSIGNED, constant);
628 : }
629 :
630 : /*
631 : * If the access is of the form x[y + a] where we know that z >= b at the
632 : * head of the loop and y has a linear relationship with z such that
633 : * (y + z) always has the same value at the head of the loop, hoist as
634 : * follows:
635 : *
636 : * y + a < initlen(x)
637 : * y + z < initlen(x) + z - a
638 : * y + z < initlen(x) + b - a
639 : * y + z + a - b < initlen(x)
640 : */
641 233 : if (hasTestLinearRelationship(indexSlot)) {
642 : int32_t constant;
643 17 : if (!SafeSub(indexConstant, testConstant, &constant))
644 0 : return false;
645 :
646 17 : addNegativeCheck(indexSlot, indexConstant);
647 17 : return addHoistedCheck(arrayKind, objSlot, indexSlot, testLHS, constant);
648 : }
649 :
650 216 : JaegerSpew(JSpew_Analysis, "No match found\n");
651 216 : return false;
652 : }
653 :
654 : bool
655 56 : LoopState::hoistArgsLengthCheck(const CrossSSAValue &index)
656 : {
657 56 : if (skipAnalysis)
658 21 : return false;
659 :
660 35 : JaegerSpew(JSpew_Analysis, "Trying to hoist argument range check\n");
661 :
662 : uint32_t indexSlot;
663 : int32_t indexConstant;
664 35 : if (!getEntryValue(index, &indexSlot, &indexConstant)) {
665 1 : JaegerSpew(JSpew_Analysis, "Could not compute index in terms of loop entry state\n");
666 1 : return false;
667 : }
668 :
669 : /*
670 : * We only hoist arguments checks which can be completely eliminated, for
671 : * now just tests with 'i < arguments.length' or similar in the condition.
672 : */
673 :
674 34 : if (indexSlot == UNASSIGNED || loopInvariantEntry(indexSlot)) {
675 2 : JaegerSpew(JSpew_Analysis, "Index is constant or loop invariant\n");
676 2 : return false;
677 : }
678 :
679 32 : if (!outerAnalysis->liveness(indexSlot).nonDecreasing(outerScript, lifetime)) {
680 3 : JaegerSpew(JSpew_Analysis, "Index may decrease in future iterations\n");
681 3 : return false;
682 : }
683 :
684 29 : if (indexSlot == testLHS && indexConstant == 0 && testConstant == -1 && testLessEqual) {
685 6 : bool found = false;
686 6 : for (unsigned i = 0; i < invariantEntries.length(); i++) {
687 2 : const InvariantEntry &entry = invariantEntries[i];
688 2 : if (entry.kind == InvariantEntry::INVARIANT_ARGS_LENGTH) {
689 2 : uint32_t slot = frame.outerSlot(frame.getTemporary(entry.u.array.temporary));
690 2 : if (slot == testRHS)
691 2 : found = true;
692 2 : break;
693 : }
694 : }
695 6 : if (found) {
696 2 : addNegativeCheck(indexSlot, indexConstant);
697 2 : JaegerSpew(JSpew_Analysis, "Access implied by loop test\n");
698 2 : return true;
699 : }
700 : }
701 :
702 27 : JaegerSpew(JSpew_Analysis, "No match found\n");
703 27 : return false;
704 : }
705 :
706 : bool
707 810 : LoopState::hasTestLinearRelationship(uint32_t slot)
708 : {
709 : /*
710 : * Determine whether slot has a linear relationship with the loop test
711 : * variable 'test', such that (slot + test) always has the same value at
712 : * the head of the loop.
713 : */
714 :
715 810 : if (testLHS == UNASSIGNED || testRHS != UNASSIGNED || testLessEqual)
716 775 : return false;
717 :
718 35 : uint32_t incrementOffset = getIncrement(slot);
719 35 : if (incrementOffset == UINT32_MAX) {
720 : /*
721 : * Variable is not always incremented in the loop, or is incremented
722 : * multiple times. Note that the nonDecreasing test done earlier
723 : * ensures that if there is a single write, it is an increment.
724 : */
725 5 : return false;
726 : }
727 :
728 30 : uint32_t decrementOffset = getIncrement(testLHS);
729 30 : if (decrementOffset == UINT32_MAX)
730 0 : return false;
731 :
732 30 : JSOp op = JSOp(outerScript->code[decrementOffset]);
733 30 : switch (op) {
734 : case JSOP_DECLOCAL:
735 : case JSOP_LOCALDEC:
736 : case JSOP_DECARG:
737 : case JSOP_ARGDEC:
738 30 : return true;
739 : default:
740 0 : return false;
741 : }
742 : }
743 :
744 : FrameEntry *
745 1150 : LoopState::invariantArraySlots(const CrossSSAValue &obj)
746 : {
747 1150 : JS_ASSERT(!skipAnalysis);
748 :
749 : uint32_t objSlot;
750 : int32_t objConstant;
751 1150 : if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0) {
752 0 : JS_NOT_REACHED("Bad value");
753 : return NULL;
754 : }
755 :
756 : /*
757 : * Note: we don't have to check arrayKind (dense array or typed array) here,
758 : * because an array cannot have entries for both dense array slots and typed
759 : * array slots.
760 : */
761 3074 : for (unsigned i = 0; i < invariantEntries.length(); i++) {
762 3074 : InvariantEntry &entry = invariantEntries[i];
763 3074 : if ((entry.kind == InvariantEntry::DENSE_ARRAY_SLOTS ||
764 : entry.kind == InvariantEntry::TYPED_ARRAY_SLOTS) &&
765 : entry.u.array.arraySlot == objSlot) {
766 1150 : return frame.getTemporary(entry.u.array.temporary);
767 : }
768 : }
769 :
770 : /* addHoistedCheck should have ensured there is an entry for the slots. */
771 0 : JS_NOT_REACHED("Missing invariant slots");
772 : return NULL;
773 : }
774 :
775 : FrameEntry *
776 57 : LoopState::invariantArguments()
777 : {
778 57 : if (skipAnalysis)
779 22 : return NULL;
780 :
781 39 : for (unsigned i = 0; i < invariantEntries.length(); i++) {
782 4 : InvariantEntry &entry = invariantEntries[i];
783 4 : if (entry.kind == InvariantEntry::INVARIANT_ARGS_BASE)
784 0 : return frame.getTemporary(entry.u.array.temporary);
785 : }
786 :
787 35 : uint32_t which = frame.allocTemporary();
788 35 : if (which == UINT32_MAX)
789 0 : return NULL;
790 35 : FrameEntry *fe = frame.getTemporary(which);
791 :
792 35 : InvariantEntry entry;
793 35 : entry.kind = InvariantEntry::INVARIANT_ARGS_BASE;
794 35 : entry.u.array.temporary = which;
795 35 : invariantEntries.append(entry);
796 :
797 : JaegerSpew(JSpew_Analysis, "Using %s for loop invariant args base\n",
798 35 : frame.entryName(fe));
799 35 : return fe;
800 : }
801 :
802 : FrameEntry *
803 2169 : LoopState::invariantLength(const CrossSSAValue &obj)
804 : {
805 2169 : if (skipAnalysis)
806 0 : return NULL;
807 :
808 : uint32_t objSlot;
809 : int32_t objConstant;
810 2169 : if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
811 175 : return NULL;
812 1994 : TypeSet *objTypes = ssa->getValueTypes(obj);
813 :
814 : /* Check for 'length' on the lazy arguments for the current frame. */
815 1994 : if (objTypes->isLazyArguments(cx)) {
816 2 : JS_ASSERT(obj.frame == CrossScriptSSA::OUTER_FRAME);
817 :
818 2 : for (unsigned i = 0; i < invariantEntries.length(); i++) {
819 0 : InvariantEntry &entry = invariantEntries[i];
820 0 : if (entry.kind == InvariantEntry::INVARIANT_ARGS_LENGTH)
821 0 : return frame.getTemporary(entry.u.array.temporary);
822 : }
823 :
824 2 : uint32_t which = frame.allocTemporary();
825 2 : if (which == UINT32_MAX)
826 0 : return NULL;
827 2 : FrameEntry *fe = frame.getTemporary(which);
828 :
829 2 : InvariantEntry entry;
830 2 : entry.kind = InvariantEntry::INVARIANT_ARGS_LENGTH;
831 2 : entry.u.array.temporary = which;
832 2 : invariantEntries.append(entry);
833 :
834 : JaegerSpew(JSpew_Analysis, "Using %s for loop invariant args length\n",
835 2 : frame.entryName(fe));
836 2 : return fe;
837 : }
838 :
839 : /*
840 : * Note: we don't have to check arrayKind (dense array or typed array) here,
841 : * because an array cannot have entries for both dense array length and typed
842 : * array length.
843 : */
844 2089 : for (unsigned i = 0; i < invariantEntries.length(); i++) {
845 256 : InvariantEntry &entry = invariantEntries[i];
846 256 : if ((entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH ||
847 : entry.kind == InvariantEntry::TYPED_ARRAY_LENGTH) &&
848 : entry.u.array.arraySlot == objSlot) {
849 159 : return frame.getTemporary(entry.u.array.temporary);
850 : }
851 : }
852 :
853 1833 : if (!loopInvariantEntry(objSlot))
854 0 : return NULL;
855 :
856 : /* Hoist 'length' access on typed arrays. */
857 1833 : if (!objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_TYPED_ARRAY)) {
858 : /* Recompile if object type changes. */
859 68 : objTypes->addFreeze(cx);
860 :
861 68 : uint32_t which = frame.allocTemporary();
862 68 : if (which == UINT32_MAX)
863 0 : return NULL;
864 68 : FrameEntry *fe = frame.getTemporary(which);
865 :
866 : JaegerSpew(JSpew_Analysis, "Using %s for loop invariant typed array length of %s\n",
867 68 : frame.entryName(fe), frame.entryName(objSlot));
868 :
869 68 : InvariantEntry entry;
870 68 : entry.kind = InvariantEntry::TYPED_ARRAY_LENGTH;
871 68 : entry.u.array.arraySlot = objSlot;
872 68 : entry.u.array.temporary = which;
873 68 : invariantEntries.append(entry);
874 :
875 68 : return fe;
876 : }
877 :
878 1765 : if (objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_DENSE_ARRAY))
879 145 : return NULL;
880 :
881 : /*
882 : * Don't make 'length' loop invariant if the loop might directly write
883 : * to the elements of any of the accessed arrays. This could invoke an
884 : * inline path which updates the length. There is no need to check the
885 : * modset for direct 'length' writes, as we don't generate inline paths
886 : * updating array lengths.
887 : */
888 4739 : for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
889 3119 : if (objTypes->getSingleObject(i) != NULL)
890 0 : return NULL;
891 3119 : TypeObject *object = objTypes->getTypeObject(i);
892 3119 : if (object && hasModifiedProperty(object, JSID_VOID))
893 0 : return NULL;
894 : }
895 1620 : objTypes->addFreeze(cx);
896 :
897 1620 : uint32_t which = frame.allocTemporary();
898 1620 : if (which == UINT32_MAX)
899 0 : return NULL;
900 1620 : FrameEntry *fe = frame.getTemporary(which);
901 :
902 : JaegerSpew(JSpew_Analysis, "Using %s for loop invariant dense array length of %s\n",
903 1620 : frame.entryName(fe), frame.entryName(objSlot));
904 :
905 1620 : InvariantEntry entry;
906 1620 : entry.kind = InvariantEntry::DENSE_ARRAY_LENGTH;
907 1620 : entry.u.array.arraySlot = objSlot;
908 1620 : entry.u.array.temporary = which;
909 1620 : invariantEntries.append(entry);
910 :
911 1620 : return fe;
912 : }
913 :
914 : FrameEntry *
915 1286 : LoopState::invariantProperty(const CrossSSAValue &obj, jsid id)
916 : {
917 1286 : if (skipAnalysis)
918 0 : return NULL;
919 :
920 1286 : if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
921 209 : return NULL;
922 :
923 : uint32_t objSlot;
924 : int32_t objConstant;
925 1077 : if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
926 457 : return NULL;
927 :
928 694 : for (unsigned i = 0; i < invariantEntries.length(); i++) {
929 251 : InvariantEntry &entry = invariantEntries[i];
930 431 : if (entry.kind == InvariantEntry::INVARIANT_PROPERTY &&
931 : entry.u.property.objectSlot == objSlot &&
932 180 : entry.u.property.id == id) {
933 177 : return frame.getTemporary(entry.u.property.temporary);
934 : }
935 : }
936 :
937 443 : if (!loopInvariantEntry(objSlot))
938 51 : return NULL;
939 :
940 : /* Check that the property is definite and not written anywhere in the loop. */
941 392 : TypeSet *objTypes = ssa->getValueTypes(obj);
942 392 : if (objTypes->unknownObject() || objTypes->getObjectCount() != 1)
943 23 : return NULL;
944 369 : TypeObject *object = objTypes->getTypeObject(0);
945 369 : if (!object || object->unknownProperties() || hasModifiedProperty(object, id) || id != MakeTypeId(cx, id))
946 172 : return NULL;
947 197 : TypeSet *propertyTypes = object->getProperty(cx, id, false);
948 197 : if (!propertyTypes)
949 0 : return NULL;
950 197 : if (!propertyTypes->isDefiniteProperty() || propertyTypes->isOwnProperty(cx, object, true))
951 98 : return NULL;
952 99 : objTypes->addFreeze(cx);
953 :
954 99 : uint32_t which = frame.allocTemporary();
955 99 : if (which == UINT32_MAX)
956 0 : return NULL;
957 99 : FrameEntry *fe = frame.getTemporary(which);
958 :
959 : JaegerSpew(JSpew_Analysis, "Using %s for loop invariant property of %s\n",
960 99 : frame.entryName(fe), frame.entryName(objSlot));
961 :
962 99 : InvariantEntry entry;
963 99 : entry.kind = InvariantEntry::INVARIANT_PROPERTY;
964 99 : entry.u.property.objectSlot = objSlot;
965 99 : entry.u.property.propertySlot = propertyTypes->definiteSlot();
966 99 : entry.u.property.temporary = which;
967 99 : entry.u.property.id = id;
968 99 : invariantEntries.append(entry);
969 :
970 99 : return fe;
971 : }
972 :
973 : bool
974 43307 : LoopState::cannotIntegerOverflow(const CrossSSAValue &pushed)
975 : {
976 43307 : if (skipAnalysis)
977 38388 : return false;
978 :
979 : int32_t min, max;
980 4919 : if (computeInterval(pushed, &min, &max)) {
981 116 : JaegerSpew(JSpew_Analysis, "Integer operation fits in range [%d, %d]\n", min, max);
982 116 : return true;
983 : }
984 :
985 : /*
986 : * Compute a slot and constant such that the result of the binary op is
987 : * 'slot + constant', where slot is expressed in terms of its value at
988 : * the head of the loop.
989 : */
990 4803 : JS_ASSERT(pushed.v.kind() == SSAValue::PUSHED);
991 4803 : jsbytecode *PC = ssa->getFrame(pushed.frame).script->code + pushed.v.pushedOffset();
992 4803 : ScriptAnalysis *analysis = ssa->getFrame(pushed.frame).script->analysis();
993 :
994 4803 : if (!analysis->integerOperation(cx, PC))
995 1490 : return false;
996 :
997 3313 : uint32_t baseSlot = UNASSIGNED;
998 3313 : int32_t baseConstant = 0;
999 3313 : JSOp op = JSOp(*PC);
1000 3313 : switch (op) {
1001 :
1002 : case JSOP_INCLOCAL:
1003 : case JSOP_LOCALINC:
1004 : case JSOP_INCARG:
1005 : case JSOP_ARGINC: {
1006 1517 : CrossSSAValue cv(pushed.frame, analysis->poppedValue(PC, 0));
1007 1517 : if (!getEntryValue(cv, &baseSlot, &baseConstant))
1008 11 : return false;
1009 1506 : if (!SafeAdd(baseConstant, 1, &baseConstant))
1010 0 : return false;
1011 1506 : break;
1012 : }
1013 :
1014 : case JSOP_DECLOCAL:
1015 : case JSOP_LOCALDEC:
1016 : case JSOP_DECARG:
1017 : case JSOP_ARGDEC: {
1018 165 : CrossSSAValue cv(pushed.frame, analysis->poppedValue(PC, 0));
1019 165 : if (!getEntryValue(cv, &baseSlot, &baseConstant))
1020 0 : return false;
1021 165 : if (!SafeSub(baseConstant, 1, &baseConstant))
1022 0 : return false;
1023 165 : break;
1024 : }
1025 :
1026 : case JSOP_ADD:
1027 : case JSOP_SUB: {
1028 1372 : uint32_t lhs = UNASSIGNED, rhs = UNASSIGNED;
1029 1372 : int32_t lhsconstant = 0, rhsconstant = 0;
1030 1372 : CrossSSAValue lcv(pushed.frame, analysis->poppedValue(PC, 1));
1031 1372 : CrossSSAValue rcv(pushed.frame, analysis->poppedValue(PC, 0));
1032 1372 : if (!getEntryValue(lcv, &lhs, &lhsconstant))
1033 883 : return false;
1034 489 : if (!getEntryValue(rcv, &rhs, &rhsconstant))
1035 73 : return false;
1036 416 : if (op == JSOP_ADD) {
1037 255 : if (!SafeAdd(lhsconstant, rhsconstant, &baseConstant))
1038 0 : return false;
1039 255 : if (lhs != UNASSIGNED && rhs != UNASSIGNED)
1040 90 : return false;
1041 165 : baseSlot = (lhs == UNASSIGNED) ? rhs : lhs;
1042 : } else {
1043 161 : if (!SafeSub(lhsconstant, rhsconstant, &baseConstant))
1044 0 : return false;
1045 161 : if (rhs != UNASSIGNED)
1046 57 : return false;
1047 104 : baseSlot = lhs;
1048 : }
1049 269 : break;
1050 : }
1051 :
1052 : default:
1053 259 : return false;
1054 : }
1055 :
1056 1940 : if (baseSlot == UNASSIGNED)
1057 0 : return false;
1058 :
1059 : JaegerSpew(JSpew_Analysis, "Trying to hoist integer overflow check on %s + %d\n",
1060 1940 : frame.entryName(baseSlot), baseConstant);
1061 :
1062 1940 : if (baseConstant == 0) {
1063 6 : JaegerSpew(JSpew_Analysis, "Vacuously holds\n");
1064 6 : return true;
1065 : }
1066 :
1067 1934 : if (baseConstant < 0) {
1068 : /*
1069 : * If the access is of the form 'y + a' where a is negative and we know
1070 : * that y >= b at the head of the loop, we can eliminate as follows:
1071 : *
1072 : * y + a >= INT_MIN
1073 : * b + a >= INT_MIN
1074 : */
1075 269 : if (baseSlot == testLHS && !testLessEqual && testRHS == UNASSIGNED) {
1076 : int32_t constant;
1077 125 : if (!SafeAdd(testConstant, baseConstant, &constant))
1078 0 : return false;
1079 :
1080 125 : JaegerSpew(JSpew_Analysis, "Loop test comparison must hold\n");
1081 125 : return true;
1082 : }
1083 :
1084 144 : JaegerSpew(JSpew_Analysis, "No match found\n");
1085 144 : return false;
1086 : }
1087 :
1088 : /*
1089 : * If the access is of the form 'y + a' where we know that y <= z + b
1090 : * (both in terms of state at the head of the loop), hoist as follows:
1091 : *
1092 : * y + a <= INT_MAX
1093 : * y <= INT_MAX - a
1094 : * z + b <= INT_MAX - a
1095 : * z <= INT_MAX - (a + b)
1096 : */
1097 1665 : if (baseSlot == testLHS && testLessEqual) {
1098 : int32_t constant;
1099 1088 : if (!SafeAdd(testConstant, baseConstant, &constant))
1100 2 : return false;
1101 :
1102 1086 : if (testRHS == UNASSIGNED || constant <= 0) {
1103 : /*
1104 : * Reduces to '(a + b) <= INT_MAX', which SafeAdd ensures,
1105 : * or 'z <= INT_MAX', which integer checks on z ensure.
1106 : */
1107 1078 : JaegerSpew(JSpew_Analysis, "Loop test comparison must hold\n");
1108 1078 : return true;
1109 : }
1110 :
1111 8 : constant = JSVAL_INT_MAX - constant;
1112 :
1113 8 : addRangeCheck(testRHS, UNASSIGNED, constant);
1114 8 : return true;
1115 : }
1116 :
1117 : /*
1118 : * If the access is of the form 'y + a' where we know that z >= b at the
1119 : * head of the loop and y has a linear relationship with z such that
1120 : * (y + z) always has the same value at the head of the loop, hoist as
1121 : * follows:
1122 : *
1123 : * y + a <= INT_MAX
1124 : * y + z <= INT_MAX + z - a
1125 : * y + z <= INT_MAX + b - a
1126 : */
1127 577 : if (hasTestLinearRelationship(baseSlot)) {
1128 : int32_t constant;
1129 13 : if (!SafeSub(testConstant, baseConstant, &constant))
1130 0 : return false;
1131 :
1132 13 : if (constant >= 0)
1133 0 : constant = 0;
1134 13 : constant = JSVAL_INT_MAX + constant;
1135 :
1136 13 : addRangeCheck(baseSlot, testLHS, constant);
1137 13 : return true;
1138 : }
1139 :
1140 564 : JaegerSpew(JSpew_Analysis, "No match found\n");
1141 564 : return false;
1142 : }
1143 :
1144 : bool
1145 43307 : LoopState::ignoreIntegerOverflow(const CrossSSAValue &pushed)
1146 : {
1147 43307 : if (skipAnalysis || unknownModset || !constrainedLoop)
1148 42856 : return false;
1149 :
1150 : /*
1151 : * Under certain circumstances, we can ignore arithmetic overflow in adds
1152 : * and multiplies. As long as the result of the add/mul is either only used
1153 : * in bitwise arithmetic or is only used in additions whose result is only
1154 : * used in bitwise arithmetic, then the conversion to integer performed by
1155 : * the bitop will undo the effect of the earlier overflow. There are two
1156 : * additional things to watch for before performing this transformation:
1157 : *
1158 : * 1. If the overflowing double is sufficiently large that it loses
1159 : * precision in its lower bits (with a 48 bit mantissa, this may happen for
1160 : * values of N >= 2^48), the resulting rounding could change the result.
1161 : * We don't ignore overflow on multiplications without range information,
1162 : * though assume that no amount of integer additions we perform in a single
1163 : * loop iteration will overflow 2^48.
1164 : *
1165 : * 2. If used in an addition with a string, the overflowing and truncated
1166 : * results may produce different values (e.g. '(x + "e3") & y'). We must
1167 : * restrict the loop body in such a way that no string operand is possible
1168 : * or becomes possible due to dynamic type changes for such additions.
1169 : * constrainedLoop indicates whether the only operations which can happen
1170 : * in the loop body are int/double arithmetic and bitops, and reads/writes
1171 : * from known dense arrays which can only produce ints and doubles.
1172 : */
1173 :
1174 : /* This value must be in the outer loop: loops with inline calls are not constrained. */
1175 451 : JS_ASSERT(pushed.frame == CrossScriptSSA::OUTER_FRAME);
1176 :
1177 451 : JS_ASSERT(pushed.v.kind() == SSAValue::PUSHED);
1178 451 : jsbytecode *PC = outerScript->code + pushed.v.pushedOffset();
1179 :
1180 451 : JSOp op = JSOp(*PC);
1181 451 : if (op != JSOP_MUL && op != JSOP_ADD)
1182 348 : return false;
1183 :
1184 103 : if (valueFlowsToBitops(pushed.v)) {
1185 23 : JaegerSpew(JSpew_Analysis, "Integer result flows to bitops\n");
1186 23 : return true;
1187 : }
1188 :
1189 80 : if (op == JSOP_MUL) {
1190 : /*
1191 : * If the multiply will only be used in an addition, negative zero can
1192 : * be ignored as long as the other operand in the addition cannot be
1193 : * negative zero.
1194 : */
1195 7 : if (!outerAnalysis->trackUseChain(pushed.v))
1196 0 : return false;
1197 :
1198 7 : SSAUseChain *use = outerAnalysis->useChain(pushed.v);
1199 7 : if (!use || use->next || !use->popped || outerScript->code[use->offset] != JSOP_ADD)
1200 3 : return false;
1201 :
1202 4 : if (use->u.which == 1) {
1203 : /*
1204 : * Only ignore negative zero if this is the RHS of an addition.
1205 : * Otherwise the result of the other side could change to a double
1206 : * after the first LHS has been computed, and be affected by a
1207 : * negative zero LHS.
1208 : */
1209 0 : return false;
1210 : }
1211 :
1212 4 : TypeSet *lhsTypes = outerAnalysis->poppedTypes(use->offset, 1);
1213 4 : if (lhsTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
1214 2 : return false;
1215 :
1216 2 : JaegerSpew(JSpew_Analysis, "Integer result is RHS in integer addition\n");
1217 2 : return true;
1218 : }
1219 :
1220 73 : return false;
1221 : }
1222 :
1223 : bool
1224 242 : LoopState::valueFlowsToBitops(const analyze::SSAValue &v)
1225 : {
1226 : /*
1227 : * Determine whether v can only be used in a bitop later in the same
1228 : * iteration of this loop, or in additions whose result is also only
1229 : * used in such a bitop.
1230 : */
1231 242 : if (!outerAnalysis->trackUseChain(v))
1232 0 : return false;
1233 :
1234 242 : SSAUseChain *use = outerAnalysis->useChain(v);
1235 597 : while (use) {
1236 270 : if (!use->popped) {
1237 : /*
1238 : * Ignore variables used in phi nodes, so long as the variable is
1239 : * dead at the phi. We don't track live variables across back edges
1240 : * or complex control flow.
1241 : */
1242 85 : if (v.kind() == SSAValue::VAR) {
1243 85 : analyze::Lifetime *lifetime = outerAnalysis->liveness(v.varSlot()).live(use->offset);
1244 85 : if (!lifetime) {
1245 14 : use = use->next;
1246 14 : continue;
1247 : }
1248 : }
1249 71 : return false;
1250 : }
1251 :
1252 185 : if (use->offset > lifetime->backedge)
1253 0 : return false;
1254 :
1255 185 : jsbytecode *pc = outerScript->code + use->offset;
1256 185 : JSOp op = JSOp(*pc);
1257 185 : switch (op) {
1258 : case JSOP_ADD:
1259 : case JSOP_GETLOCAL: {
1260 : SSAValue pushv;
1261 54 : pushv.initPushed(use->offset, 0);
1262 54 : if (!valueFlowsToBitops(pushv))
1263 6 : return false;
1264 48 : break;
1265 : }
1266 :
1267 : case JSOP_SETLOCAL: {
1268 85 : uint32_t slot = GetBytecodeSlot(outerScript, pc);
1269 85 : if (!outerAnalysis->trackSlot(slot))
1270 0 : return false;
1271 : SSAValue writev;
1272 85 : writev.initWritten(slot, use->offset);
1273 85 : if (!valueFlowsToBitops(writev))
1274 71 : return false;
1275 14 : break;
1276 : }
1277 :
1278 : case JSOP_BITAND:
1279 : case JSOP_BITOR:
1280 : case JSOP_BITXOR:
1281 : case JSOP_RSH:
1282 : case JSOP_LSH:
1283 : case JSOP_URSH:
1284 : case JSOP_BITNOT:
1285 37 : break;
1286 :
1287 : default:
1288 9 : return false;
1289 : }
1290 :
1291 99 : use = use->next;
1292 : }
1293 :
1294 85 : return true;
1295 : }
1296 :
1297 : void
1298 8588 : LoopState::restoreInvariants(jsbytecode *pc, Assembler &masm,
1299 : Vector<TemporaryCopy> *temporaryCopies, Vector<Jump> *jumps)
1300 : {
1301 : /*
1302 : * Restore all invariants in memory when entering the loop or after any
1303 : * scripted or C++ call, and check that all hoisted conditions still hold.
1304 : * Care should be taken not to clobber the return register or callee-saved
1305 : * registers, which may still be live after some calls.
1306 : */
1307 :
1308 8588 : Registers regs(Registers::TempRegs);
1309 8588 : regs.takeReg(Registers::ReturnReg);
1310 8588 : if (regs.hasReg(JSReturnReg_Data))
1311 0 : regs.takeReg(JSReturnReg_Data);
1312 8588 : if (regs.hasReg(JSReturnReg_Type))
1313 0 : regs.takeReg(JSReturnReg_Type);
1314 :
1315 8588 : RegisterID T0 = regs.takeAnyReg().reg();
1316 8588 : RegisterID T1 = regs.takeAnyReg().reg();
1317 :
1318 42154 : for (unsigned i = 0; i < invariantEntries.length(); i++) {
1319 33566 : const InvariantEntry &entry = invariantEntries[i];
1320 33566 : switch (entry.kind) {
1321 :
1322 : case InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK:
1323 : case InvariantEntry::TYPED_ARRAY_BOUNDS_CHECK: {
1324 : /*
1325 : * Hoisted bounds checks always have preceding invariant slots
1326 : * in the invariant list, so don't recheck this is an object.
1327 : */
1328 11362 : masm.loadPayload(frame.addressOf(entry.u.check.arraySlot), T0);
1329 11362 : if (entry.kind == InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK) {
1330 11277 : masm.loadPtr(Address(T0, JSObject::offsetOfElements()), T0);
1331 11277 : masm.load32(Address(T0, ObjectElements::offsetOfInitializedLength()), T0);
1332 : } else {
1333 85 : masm.loadPayload(Address(T0, TypedArray::lengthOffset()), T0);
1334 : }
1335 :
1336 11362 : int32_t constant = entry.u.check.constant;
1337 :
1338 11362 : if (entry.u.check.valueSlot1 != UINT32_MAX) {
1339 7586 : constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot1);
1340 7586 : masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T1);
1341 7586 : if (entry.u.check.valueSlot2 != UINT32_MAX) {
1342 216 : constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot2);
1343 : Jump overflow = masm.branchAdd32(Assembler::Overflow,
1344 216 : frame.addressOf(entry.u.check.valueSlot2), T1);
1345 216 : jumps->append(overflow);
1346 : }
1347 7586 : if (constant != 0) {
1348 : Jump overflow = masm.branchAdd32(Assembler::Overflow,
1349 3171 : Imm32(constant), T1);
1350 3171 : jumps->append(overflow);
1351 : }
1352 7586 : Jump j = masm.branch32(Assembler::LessThanOrEqual, T0, T1);
1353 7586 : jumps->append(j);
1354 : } else {
1355 : Jump j = masm.branch32(Assembler::LessThanOrEqual, T0,
1356 3776 : Imm32(constant));
1357 3776 : jumps->append(j);
1358 : }
1359 11362 : break;
1360 : }
1361 :
1362 : case InvariantEntry::RANGE_CHECK: {
1363 97 : int32_t constant = 0;
1364 :
1365 97 : constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot1);
1366 97 : masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T0);
1367 97 : if (entry.u.check.valueSlot2 != UINT32_MAX) {
1368 60 : constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot2);
1369 : Jump overflow = masm.branchAdd32(Assembler::Overflow,
1370 60 : frame.addressOf(entry.u.check.valueSlot2), T0);
1371 60 : jumps->append(overflow);
1372 : }
1373 97 : if (constant != 0) {
1374 28 : Jump overflow = masm.branchAdd32(Assembler::Overflow, Imm32(constant), T0);
1375 28 : jumps->append(overflow);
1376 : }
1377 97 : Jump j = masm.branch32(Assembler::GreaterThan, T0, Imm32(entry.u.check.constant));
1378 97 : jumps->append(j);
1379 97 : break;
1380 : }
1381 :
1382 : case InvariantEntry::NEGATIVE_CHECK: {
1383 7062 : masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T0);
1384 7062 : if (entry.u.check.constant != 0) {
1385 : Jump overflow = masm.branchAdd32(Assembler::Overflow,
1386 12 : Imm32(entry.u.check.constant), T0);
1387 12 : jumps->append(overflow);
1388 : }
1389 7062 : Jump j = masm.branch32(Assembler::LessThan, T0, Imm32(0));
1390 7062 : jumps->append(j);
1391 7062 : break;
1392 : }
1393 :
1394 : case InvariantEntry::DENSE_ARRAY_SLOTS:
1395 : case InvariantEntry::DENSE_ARRAY_LENGTH: {
1396 12956 : uint32_t array = entry.u.array.arraySlot;
1397 12956 : Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
1398 12956 : jumps->append(notObject);
1399 12956 : masm.loadPayload(frame.addressOf(array), T0);
1400 12956 : masm.loadPtr(Address(T0, JSObject::offsetOfElements()), T0);
1401 :
1402 12956 : Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
1403 :
1404 12956 : if (entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH) {
1405 1679 : masm.load32(Address(T0, ObjectElements::offsetOfLength()), T0);
1406 1679 : masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
1407 : } else {
1408 11277 : masm.storePayload(T0, address);
1409 : }
1410 12956 : break;
1411 : }
1412 :
1413 : case InvariantEntry::TYPED_ARRAY_SLOTS:
1414 : case InvariantEntry::TYPED_ARRAY_LENGTH: {
1415 433 : uint32_t array = entry.u.array.arraySlot;
1416 433 : Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
1417 433 : jumps->append(notObject);
1418 433 : masm.loadPayload(frame.addressOf(array), T0);
1419 :
1420 433 : Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
1421 :
1422 433 : if (entry.kind == InvariantEntry::TYPED_ARRAY_LENGTH) {
1423 348 : masm.loadPayload(Address(T0, TypedArray::lengthOffset()), T0);
1424 348 : masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
1425 : } else {
1426 85 : masm.loadPtr(Address(T0, js::TypedArray::dataOffset()), T0);
1427 85 : masm.storePayload(T0, address);
1428 : }
1429 433 : break;
1430 : }
1431 :
1432 : case InvariantEntry::INVARIANT_ARGS_BASE: {
1433 377 : Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
1434 377 : masm.loadFrameActuals(outerScript->function(), T0);
1435 377 : masm.storePayload(T0, address);
1436 377 : break;
1437 : }
1438 :
1439 : case InvariantEntry::INVARIANT_ARGS_LENGTH: {
1440 20 : Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
1441 20 : masm.load32(Address(JSFrameReg, StackFrame::offsetOfNumActual()), T0);
1442 20 : masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
1443 20 : break;
1444 : }
1445 :
1446 : case InvariantEntry::INVARIANT_PROPERTY: {
1447 1259 : uint32_t object = entry.u.property.objectSlot;
1448 1259 : Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(object));
1449 1259 : jumps->append(notObject);
1450 1259 : masm.loadPayload(frame.addressOf(object), T0);
1451 :
1452 1259 : masm.loadInlineSlot(T0, entry.u.property.propertySlot, T1, T0);
1453 : masm.storeValueFromComponents(T1, T0,
1454 1259 : frame.addressOf(frame.getTemporary(entry.u.property.temporary)));
1455 1259 : break;
1456 : }
1457 :
1458 : default:
1459 0 : JS_NOT_REACHED("Bad invariant kind");
1460 : }
1461 : }
1462 :
1463 : /*
1464 : * If there were any copies of temporaries on the stack, make sure the
1465 : * value we just reconstructed matches the stored value of that temporary.
1466 : * We sync the entire stack before calls, so the copy's slot holds the old
1467 : * value, but in future code we will assume the copy is valid and use the
1468 : * changed value of the invariant.
1469 : */
1470 :
1471 8698 : for (unsigned i = 0; temporaryCopies && i < temporaryCopies->length(); i++) {
1472 110 : const TemporaryCopy © = (*temporaryCopies)[i];
1473 110 : masm.compareValue(copy.copy, copy.temporary, T0, T1, jumps);
1474 : }
1475 :
1476 8588 : if (temporaryCopies)
1477 90 : cx->delete_(temporaryCopies);
1478 8588 : }
1479 :
1480 : /* Loop analysis methods. */
1481 :
1482 : /*
1483 : * Get any slot/constant accessed by a loop test operand, in terms of its value
1484 : * at the start of the next loop iteration.
1485 : */
1486 : bool
1487 23088 : LoopState::getLoopTestAccess(const SSAValue &v, uint32_t *pslot, int32_t *pconstant)
1488 : {
1489 23088 : *pslot = UNASSIGNED;
1490 23088 : *pconstant = 0;
1491 :
1492 23088 : if (v.kind() == SSAValue::PHI || v.kind() == SSAValue::VAR) {
1493 : /*
1494 : * Getting the value of a variable at a previous offset. Check that it
1495 : * is not updated before the start of the next loop iteration.
1496 : */
1497 : uint32_t slot;
1498 : uint32_t offset;
1499 9965 : if (v.kind() == SSAValue::PHI) {
1500 9183 : slot = v.phiSlot();
1501 9183 : offset = v.phiOffset();
1502 : } else {
1503 782 : slot = v.varSlot();
1504 782 : offset = v.varInitial() ? 0 : v.varOffset();
1505 : }
1506 9965 : if (outerAnalysis->slotEscapes(slot))
1507 0 : return false;
1508 9965 : if (outerAnalysis->liveness(slot).firstWrite(offset + 1, lifetime->backedge) != UINT32_MAX)
1509 0 : return false;
1510 9965 : *pslot = slot;
1511 9965 : *pconstant = 0;
1512 9965 : return true;
1513 : }
1514 :
1515 13123 : jsbytecode *pc = outerScript->code + v.pushedOffset();
1516 :
1517 13123 : JSOp op = JSOp(*pc);
1518 13123 : const JSCodeSpec *cs = &js_CodeSpec[op];
1519 :
1520 : /*
1521 : * If the pc is modifying a variable and the value tested is its earlier value
1522 : * (e.g. 'x++ < n'), we need to account for the modification --- at the start
1523 : * of the next iteration, the value compared will have been 'x - 1'.
1524 : * Note that we don't need to worry about other accesses to the variable
1525 : * in the condition like 'x++ < x', as loop tests where both operands are
1526 : * modified by the loop are rejected.
1527 : */
1528 :
1529 13123 : switch (op) {
1530 :
1531 : case JSOP_INCLOCAL:
1532 : case JSOP_DECLOCAL:
1533 : case JSOP_LOCALINC:
1534 : case JSOP_LOCALDEC:
1535 : case JSOP_INCARG:
1536 : case JSOP_DECARG:
1537 : case JSOP_ARGINC:
1538 : case JSOP_ARGDEC: {
1539 107 : if (!outerAnalysis->integerOperation(cx, pc))
1540 0 : return false;
1541 107 : uint32_t slot = GetBytecodeSlot(outerScript, pc);
1542 107 : if (outerAnalysis->slotEscapes(slot))
1543 0 : return false;
1544 :
1545 107 : *pslot = slot;
1546 107 : if (cs->format & JOF_POST) {
1547 28 : if (cs->format & JOF_INC)
1548 0 : *pconstant = -1;
1549 : else
1550 28 : *pconstant = 1;
1551 : }
1552 107 : return true;
1553 : }
1554 :
1555 : case JSOP_ZERO:
1556 : case JSOP_ONE:
1557 : case JSOP_UINT16:
1558 : case JSOP_UINT24:
1559 : case JSOP_INT8:
1560 : case JSOP_INT32:
1561 8139 : *pconstant = GetBytecodeInteger(pc);
1562 8139 : return true;
1563 :
1564 : default:
1565 4877 : return false;
1566 : }
1567 : }
1568 :
1569 : void
1570 33602 : LoopState::analyzeLoopTest()
1571 : {
1572 33602 : if (cc.debugMode())
1573 15500 : return;
1574 :
1575 : /* Don't handle do-while loops. */
1576 18102 : if (lifetime->entry == lifetime->head)
1577 33 : return;
1578 :
1579 : /* Don't handle loops with branching inside their condition. */
1580 18069 : if (lifetime->entry < lifetime->lastBlock)
1581 54 : return;
1582 :
1583 : /* Get the test performed before branching. */
1584 18015 : jsbytecode *backedge = outerScript->code + lifetime->backedge;
1585 18015 : if (JSOp(*backedge) != JSOP_IFNE)
1586 0 : return;
1587 18015 : const SSAValue &test = outerAnalysis->poppedValue(backedge, 0);
1588 18015 : if (test.kind() != SSAValue::PUSHED)
1589 129 : return;
1590 17886 : JSOp cmpop = JSOp(outerScript->code[test.pushedOffset()]);
1591 17886 : switch (cmpop) {
1592 : case JSOP_GT:
1593 : case JSOP_GE:
1594 : case JSOP_LT:
1595 : case JSOP_LE:
1596 : break;
1597 : default:
1598 2424 : return;
1599 : }
1600 :
1601 15462 : SSAValue one = outerAnalysis->poppedValue(test.pushedOffset(), 1);
1602 15462 : SSAValue two = outerAnalysis->poppedValue(test.pushedOffset(), 0);
1603 :
1604 : /* The test must be comparing known integers. */
1605 27808 : if (outerAnalysis->getValueTypes(one)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32 ||
1606 12346 : outerAnalysis->getValueTypes(two)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
1607 3918 : return;
1608 : }
1609 :
1610 : /* Reverse the condition if the RHS is modified by the loop. */
1611 : uint32_t swapRHS;
1612 : int32_t swapConstant;
1613 11544 : if (getLoopTestAccess(two, &swapRHS, &swapConstant)) {
1614 8961 : if (swapRHS != UNASSIGNED && outerAnalysis->liveness(swapRHS).firstWrite(lifetime) != UINT32_MAX) {
1615 0 : SSAValue tmp = one;
1616 0 : one = two;
1617 0 : two = tmp;
1618 0 : cmpop = ReverseCompareOp(cmpop);
1619 : }
1620 : }
1621 :
1622 : uint32_t lhs;
1623 : int32_t lhsConstant;
1624 11544 : if (!getLoopTestAccess(one, &lhs, &lhsConstant))
1625 2294 : return;
1626 :
1627 9250 : uint32_t rhs = UNASSIGNED;
1628 9250 : int32_t rhsConstant = 0;
1629 9250 : CrossSSAValue rhsv(CrossScriptSSA::OUTER_FRAME, two);
1630 9250 : if (!getEntryValue(rhsv, &rhs, &rhsConstant))
1631 525 : return;
1632 8725 : if (!loopInvariantEntry(rhs))
1633 0 : return;
1634 :
1635 8725 : if (lhs == UNASSIGNED)
1636 0 : return;
1637 :
1638 : int32_t constant;
1639 8725 : if (!SafeSub(rhsConstant, lhsConstant, &constant))
1640 0 : return;
1641 :
1642 : /* x > y ==> x >= y + 1 */
1643 8725 : if (cmpop == JSOP_GT && !SafeAdd(constant, 1, &constant))
1644 0 : return;
1645 :
1646 : /* x < y ==> x <= y - 1 */
1647 8725 : if (cmpop == JSOP_LT && !SafeSub(constant, 1, &constant))
1648 0 : return;
1649 :
1650 : /* Passed all filters, this is a loop test we can capture. */
1651 :
1652 8725 : this->testLHS = lhs;
1653 8725 : this->testRHS = rhs;
1654 8725 : this->testConstant = constant;
1655 8725 : this->testLessEqual = (cmpop == JSOP_LT || cmpop == JSOP_LE);
1656 : }
1657 :
1658 : void
1659 33602 : LoopState::analyzeLoopIncrements()
1660 : {
1661 33602 : if (cc.debugMode())
1662 15500 : return;
1663 :
1664 : /*
1665 : * Find locals and arguments which are used in exactly one inc/dec operation in every
1666 : * iteration of the loop (we only match against the last basic block, but could
1667 : * also handle the first basic block).
1668 : */
1669 :
1670 82762 : for (uint32_t slot = ArgSlot(0); slot < LocalSlot(outerScript, outerScript->nfixed); slot++) {
1671 64660 : if (outerAnalysis->slotEscapes(slot))
1672 8293 : continue;
1673 :
1674 56367 : uint32_t offset = outerAnalysis->liveness(slot).onlyWrite(lifetime);
1675 56367 : if (offset == UINT32_MAX || offset < lifetime->lastBlock)
1676 43745 : continue;
1677 :
1678 12622 : jsbytecode *pc = outerScript->code + offset;
1679 12622 : JSOp op = JSOp(*pc);
1680 12622 : const JSCodeSpec *cs = &js_CodeSpec[op];
1681 12622 : if (cs->format & (JOF_INC | JOF_DEC)) {
1682 9936 : if (!outerAnalysis->integerOperation(cx, pc))
1683 47 : continue;
1684 :
1685 : Increment inc;
1686 9889 : inc.slot = slot;
1687 9889 : inc.offset = offset;
1688 9889 : increments.append(inc);
1689 : }
1690 : }
1691 : }
1692 :
1693 : bool
1694 2211 : LoopState::definiteArrayAccess(const SSAValue &obj, const SSAValue &index)
1695 : {
1696 : /*
1697 : * Check that an index on obj is definitely accessing a dense array, giving
1698 : * either a value modelled by the pushed types or a hole. This needs to be
1699 : * robust against recompilations that could be triggered inside the loop:
1700 : * the array must be loop invariant, and the index must definitely be an
1701 : * integer.
1702 : *
1703 : * This is used to determine if we can ignore possible integer overflow in
1704 : * an operation; if this site could read a non-integer element out of the
1705 : * array or invoke a scripted getter/setter, it could produce a string or
1706 : * other value by which the overflow could be observed.
1707 : */
1708 :
1709 2211 : TypeSet *objTypes = outerAnalysis->getValueTypes(obj);
1710 2211 : TypeSet *elemTypes = outerAnalysis->getValueTypes(index);
1711 :
1712 4191 : if (objTypes->getKnownTypeTag(cx) != JSVAL_TYPE_OBJECT ||
1713 1980 : elemTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
1714 275 : return false;
1715 : }
1716 :
1717 1936 : if (objTypes->hasObjectFlags(cx, OBJECT_FLAG_NON_DENSE_ARRAY))
1718 225 : return false;
1719 :
1720 1711 : if (ArrayPrototypeHasIndexedProperty(cx, outerScript))
1721 1 : return false;
1722 :
1723 : uint32_t objSlot;
1724 : int32_t objConstant;
1725 1710 : CrossSSAValue objv(CrossScriptSSA::OUTER_FRAME, obj);
1726 1710 : if (!getEntryValue(objv, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
1727 88 : return false;
1728 1622 : if (!loopInvariantEntry(objSlot))
1729 0 : return false;
1730 :
1731 : /* Bitops must produce integers. */
1732 1622 : if (index.kind() == SSAValue::PUSHED) {
1733 341 : JSOp op = JSOp(outerScript->code[index.pushedOffset()]);
1734 341 : switch (op) {
1735 : case JSOP_BITAND:
1736 : case JSOP_BITOR:
1737 : case JSOP_BITXOR:
1738 : case JSOP_BITNOT:
1739 : case JSOP_RSH:
1740 : case JSOP_LSH:
1741 : case JSOP_URSH:
1742 7 : return true;
1743 : default:;
1744 : }
1745 : }
1746 :
1747 : uint32_t indexSlot;
1748 : int32_t indexConstant;
1749 1615 : CrossSSAValue indexv(CrossScriptSSA::OUTER_FRAME, index);
1750 1615 : if (!getEntryValue(indexv, &indexSlot, &indexConstant))
1751 259 : return false;
1752 :
1753 : /*
1754 : * The index is determined from a variable's value at loop entry. We don't
1755 : * carry values with ignored overflows around loop back edges, so will know
1756 : * the index is a non-integer before such overflows are ignored.
1757 : */
1758 1356 : return true;
1759 : }
1760 :
1761 : void
1762 35202 : LoopState::analyzeLoopBody(unsigned frame)
1763 : {
1764 35202 : if (cc.debugMode()) {
1765 15500 : skipAnalysis = true;
1766 15500 : return;
1767 : }
1768 :
1769 19702 : JSScript *script = ssa->getFrame(frame).script;
1770 19702 : analyze::ScriptAnalysis *analysis = script->analysis();
1771 19702 : JS_ASSERT(analysis && !analysis->failed() && analysis->ranInference());
1772 :
1773 : /*
1774 : * The temporaries need to be positioned after all values in the deepest
1775 : * inlined frame plus another stack frame pushed by, e.g. ic::Call.
1776 : * This new frame will have been partially initialized by the call, and
1777 : * we don't want to scribble on that frame when restoring invariants.
1778 : */
1779 : temporariesStart =
1780 : Max<uint32_t>(temporariesStart,
1781 19702 : ssa->getFrame(frame).depth + VALUES_PER_STACK_FRAME * 2 + script->nslots);
1782 :
1783 19702 : if (script->failedBoundsCheck || analysis->localsAliasStack())
1784 3190 : skipAnalysis = true;
1785 :
1786 : /* Analyze the entire script for frames inlined in the loop body. */
1787 19702 : unsigned start = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->head + JSOP_LOOPHEAD_LENGTH : 0;
1788 19702 : unsigned end = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->backedge : script->length;
1789 :
1790 19702 : unsigned offset = start;
1791 659515 : while (offset < end) {
1792 620111 : jsbytecode *pc = script->code + offset;
1793 620111 : uint32_t successorOffset = offset + GetBytecodeLength(pc);
1794 :
1795 620111 : analyze::Bytecode *opinfo = analysis->maybeCode(offset);
1796 620111 : if (!opinfo) {
1797 1592 : offset = successorOffset;
1798 1592 : continue;
1799 : }
1800 :
1801 618519 : JSOp op = JSOp(*pc);
1802 :
1803 : /* Don't do any hoisting for outer loops in case of nesting. */
1804 618519 : if (op == JSOP_LOOPHEAD)
1805 2746 : skipAnalysis = true;
1806 :
1807 618519 : switch (op) {
1808 :
1809 : case JSOP_CALL: {
1810 : /*
1811 : * Don't hoist within this loop unless calls at this site are inlined.
1812 : * :XXX: also recognize native calls which will be inlined.
1813 : */
1814 34393 : bool foundInline = false;
1815 92126 : for (unsigned i = 0; !foundInline && i < ssa->numFrames(); i++) {
1816 57733 : if (ssa->iterFrame(i).parent == frame && ssa->iterFrame(i).parentpc == pc)
1817 1478 : foundInline = true;
1818 : }
1819 34393 : if (!foundInline)
1820 32915 : skipAnalysis = true;
1821 34393 : break;
1822 : }
1823 :
1824 : case JSOP_EVAL:
1825 : case JSOP_FUNCALL:
1826 : case JSOP_FUNAPPLY:
1827 : case JSOP_NEW:
1828 3153 : skipAnalysis = true;
1829 3153 : break;
1830 :
1831 : case JSOP_SETELEM: {
1832 5506 : SSAValue objValue = analysis->poppedValue(pc, 2);
1833 5506 : SSAValue elemValue = analysis->poppedValue(pc, 1);
1834 :
1835 5506 : TypeSet *objTypes = analysis->getValueTypes(objValue);
1836 5506 : TypeSet *elemTypes = analysis->getValueTypes(elemValue);
1837 :
1838 : /*
1839 : * Mark the modset as unknown if the index might be non-integer,
1840 : * we don't want to consider the SETELEM PIC here.
1841 : */
1842 5506 : if (objTypes->unknownObject() || elemTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
1843 641 : unknownModset = true;
1844 641 : break;
1845 : }
1846 :
1847 4865 : objTypes->addFreeze(cx);
1848 9983 : for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
1849 5118 : TypeObject *object = objTypes->getTypeObject(i);
1850 5118 : if (!object)
1851 18 : continue;
1852 5100 : if (!addModifiedProperty(object, JSID_VOID))
1853 0 : return;
1854 5100 : if (analysis->getCode(pc).arrayWriteHole && !addGrowArray(object))
1855 0 : return;
1856 : }
1857 :
1858 4865 : if (constrainedLoop && !definiteArrayAccess(objValue, elemValue))
1859 290 : constrainedLoop = false;
1860 4865 : break;
1861 : }
1862 :
1863 : case JSOP_GETELEM: {
1864 17499 : SSAValue objValue = analysis->poppedValue(pc, 1);
1865 17499 : SSAValue elemValue = analysis->poppedValue(pc, 0);
1866 :
1867 17499 : if (constrainedLoop && !definiteArrayAccess(objValue, elemValue))
1868 558 : constrainedLoop = false;
1869 17499 : break;
1870 : }
1871 :
1872 : case JSOP_SETPROP:
1873 : case JSOP_SETMETHOD: {
1874 1376 : JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
1875 1376 : jsid id = MakeTypeId(cx, ATOM_TO_JSID(atom));
1876 :
1877 1376 : TypeSet *objTypes = analysis->poppedTypes(pc, 1);
1878 1376 : if (objTypes->unknownObject()) {
1879 206 : unknownModset = true;
1880 206 : break;
1881 : }
1882 :
1883 1170 : objTypes->addFreeze(cx);
1884 2155 : for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
1885 985 : TypeObject *object = objTypes->getTypeObject(i);
1886 985 : if (!object)
1887 36 : continue;
1888 949 : if (!addModifiedProperty(object, id))
1889 0 : continue;
1890 : }
1891 :
1892 1170 : constrainedLoop = false;
1893 1170 : break;
1894 : }
1895 :
1896 : case JSOP_ENUMELEM:
1897 : case JSOP_ENUMCONSTELEM:
1898 0 : unknownModset = true;
1899 0 : break;
1900 :
1901 : case JSOP_LOOPHEAD:
1902 : case JSOP_LOOPENTRY:
1903 : case JSOP_POP:
1904 : case JSOP_ZERO:
1905 : case JSOP_ONE:
1906 : case JSOP_INT8:
1907 : case JSOP_INT32:
1908 : case JSOP_UINT16:
1909 : case JSOP_UINT24:
1910 : case JSOP_FALSE:
1911 : case JSOP_TRUE:
1912 : case JSOP_GETARG:
1913 : case JSOP_SETARG:
1914 : case JSOP_INCARG:
1915 : case JSOP_DECARG:
1916 : case JSOP_ARGINC:
1917 : case JSOP_ARGDEC:
1918 : case JSOP_THIS:
1919 : case JSOP_GETLOCAL:
1920 : case JSOP_SETLOCAL:
1921 : case JSOP_SETLOCALPOP:
1922 : case JSOP_INCLOCAL:
1923 : case JSOP_DECLOCAL:
1924 : case JSOP_LOCALINC:
1925 : case JSOP_LOCALDEC:
1926 : case JSOP_IFEQ:
1927 : case JSOP_IFNE:
1928 : case JSOP_AND:
1929 : case JSOP_OR:
1930 : case JSOP_GOTO:
1931 268386 : break;
1932 :
1933 : case JSOP_ADD:
1934 : case JSOP_SUB:
1935 : case JSOP_MUL:
1936 : case JSOP_MOD:
1937 : case JSOP_DIV:
1938 : case JSOP_BITAND:
1939 : case JSOP_BITOR:
1940 : case JSOP_BITXOR:
1941 : case JSOP_RSH:
1942 : case JSOP_LSH:
1943 : case JSOP_URSH:
1944 : case JSOP_EQ:
1945 : case JSOP_NE:
1946 : case JSOP_LT:
1947 : case JSOP_LE:
1948 : case JSOP_GT:
1949 : case JSOP_GE:
1950 : case JSOP_STRICTEQ:
1951 : case JSOP_STRICTNE: {
1952 45667 : JSValueType type = analysis->poppedTypes(pc, 1)->getKnownTypeTag(cx);
1953 45667 : if (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_DOUBLE)
1954 15366 : constrainedLoop = false;
1955 : }
1956 : /* FALLTHROUGH */
1957 :
1958 : case JSOP_POS:
1959 : case JSOP_NEG:
1960 : case JSOP_BITNOT: {
1961 50275 : JSValueType type = analysis->poppedTypes(pc, 0)->getKnownTypeTag(cx);
1962 50275 : if (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_DOUBLE)
1963 12566 : constrainedLoop = false;
1964 50275 : break;
1965 : }
1966 :
1967 : default:
1968 237931 : constrainedLoop = false;
1969 237931 : break;
1970 : }
1971 :
1972 618519 : offset = successorOffset;
1973 : }
1974 : }
1975 :
1976 : bool
1977 1035 : LoopState::addGrowArray(TypeObject *object)
1978 : {
1979 : static const uint32_t MAX_SIZE = 10;
1980 1468 : for (unsigned i = 0; i < growArrays.length(); i++) {
1981 486 : if (growArrays[i] == object)
1982 53 : return true;
1983 : }
1984 982 : if (growArrays.length() >= MAX_SIZE) {
1985 0 : unknownModset = true;
1986 0 : return false;
1987 : }
1988 982 : growArrays.append(object);
1989 :
1990 982 : return true;
1991 : }
1992 :
1993 : bool
1994 6049 : LoopState::addModifiedProperty(TypeObject *object, jsid id)
1995 : {
1996 : static const uint32_t MAX_SIZE = 20;
1997 9519 : for (unsigned i = 0; i < modifiedProperties.length(); i++) {
1998 4928 : if (modifiedProperties[i].object == object && modifiedProperties[i].id == id)
1999 1458 : return true;
2000 : }
2001 4591 : if (modifiedProperties.length() >= MAX_SIZE) {
2002 0 : unknownModset = true;
2003 0 : return false;
2004 : }
2005 :
2006 : ModifiedProperty property;
2007 4591 : property.object = object;
2008 4591 : property.id = id;
2009 4591 : modifiedProperties.append(property);
2010 :
2011 4591 : return true;
2012 : }
2013 :
2014 : bool
2015 948 : LoopState::hasGrowArray(TypeObject *object)
2016 : {
2017 948 : if (unknownModset)
2018 0 : return true;
2019 3389 : for (unsigned i = 0; i < growArrays.length(); i++) {
2020 2666 : if (growArrays[i] == object)
2021 225 : return true;
2022 : }
2023 723 : return false;
2024 : }
2025 :
2026 : bool
2027 2516 : LoopState::hasModifiedProperty(TypeObject *object, jsid id)
2028 : {
2029 2516 : if (unknownModset)
2030 14 : return true;
2031 2502 : id = MakeTypeId(cx, id);
2032 2562 : for (unsigned i = 0; i < modifiedProperties.length(); i++) {
2033 215 : if (modifiedProperties[i].object == object && modifiedProperties[i].id == id)
2034 155 : return true;
2035 : }
2036 2347 : return false;
2037 : }
2038 :
2039 : uint32_t
2040 8024 : LoopState::getIncrement(uint32_t slot)
2041 : {
2042 15646 : for (unsigned i = 0; i < increments.length(); i++) {
2043 8234 : if (increments[i].slot == slot)
2044 612 : return increments[i].offset;
2045 : }
2046 7412 : return UINT32_MAX;
2047 : }
2048 :
2049 : int32_t
2050 7959 : LoopState::adjustConstantForIncrement(jsbytecode *pc, uint32_t slot)
2051 : {
2052 : /*
2053 : * The only terms that can appear in a hoisted bounds check are either
2054 : * loop invariant or are incremented or decremented exactly once in each
2055 : * iteration of the loop. Depending on the current pc in the body of the
2056 : * loop, return a constant adjustment if an increment/decrement for slot
2057 : * has not yet happened, such that 'slot + n' at this point is the value
2058 : * of slot at the start of the next iteration.
2059 : */
2060 7959 : uint32_t offset = getIncrement(slot);
2061 :
2062 : /*
2063 : * Note the '<' here. If this PC is at one of the increment opcodes, then
2064 : * behave as if the increment has not happened yet. This is needed for loop
2065 : * entry points, which can be directly at an increment. We won't rejoin
2066 : * after the increment, as we only take stub calls in such situations on
2067 : * integer overflow, which will disable hoisted conditions involving the
2068 : * variable anyways.
2069 : */
2070 7959 : if (offset == UINT32_MAX || offset < uint32_t(pc - outerScript->code))
2071 7536 : return 0;
2072 :
2073 423 : switch (JSOp(outerScript->code[offset])) {
2074 : case JSOP_INCLOCAL:
2075 : case JSOP_LOCALINC:
2076 : case JSOP_INCARG:
2077 : case JSOP_ARGINC:
2078 147 : return 1;
2079 : case JSOP_DECLOCAL:
2080 : case JSOP_LOCALDEC:
2081 : case JSOP_DECARG:
2082 : case JSOP_ARGDEC:
2083 276 : return -1;
2084 : default:
2085 0 : JS_NOT_REACHED("Bad op");
2086 : return 0;
2087 : }
2088 : }
2089 :
2090 : bool
2091 24616 : LoopState::getEntryValue(const CrossSSAValue &iv, uint32_t *pslot, int32_t *pconstant)
2092 : {
2093 24616 : CrossSSAValue cv = ssa->foldValue(iv);
2094 :
2095 24616 : JSScript *script = ssa->getFrame(cv.frame).script;
2096 24616 : ScriptAnalysis *analysis = script->analysis();
2097 24616 : const SSAValue &v = cv.v;
2098 :
2099 : /*
2100 : * For a stack value popped by the bytecode at offset, try to get an
2101 : * expression 'slot + constant' with the same value as the stack value
2102 : * and expressed in terms of the state at loop entry.
2103 : */
2104 :
2105 24616 : if (v.kind() == SSAValue::PHI) {
2106 4602 : if (cv.frame != CrossScriptSSA::OUTER_FRAME)
2107 1 : return false;
2108 4601 : if (v.phiSlot() >= TotalSlots(script))
2109 5 : return false;
2110 4744 : if (v.phiOffset() > lifetime->head &&
2111 148 : outerAnalysis->liveness(v.phiSlot()).firstWrite(lifetime) < v.phiOffset()) {
2112 70 : return false;
2113 : }
2114 4526 : *pslot = v.phiSlot();
2115 4526 : *pconstant = 0;
2116 4526 : return true;
2117 : }
2118 :
2119 20014 : if (v.kind() == SSAValue::VAR) {
2120 7961 : if (cv.frame != CrossScriptSSA::OUTER_FRAME)
2121 16 : return false;
2122 7945 : if (v.varInitial() || v.varOffset() < lifetime->head) {
2123 7619 : *pslot = v.varSlot();
2124 7619 : *pconstant = 0;
2125 7619 : return true;
2126 : }
2127 : }
2128 :
2129 12379 : if (v.kind() != SSAValue::PUSHED)
2130 326 : return false;
2131 :
2132 12053 : jsbytecode *pc = script->code + v.pushedOffset();
2133 12053 : JSOp op = (JSOp)*pc;
2134 :
2135 12053 : switch (op) {
2136 :
2137 : case JSOP_GETLOCAL:
2138 : case JSOP_LOCALINC:
2139 : case JSOP_INCLOCAL:
2140 : case JSOP_GETARG:
2141 : case JSOP_ARGINC:
2142 : case JSOP_INCARG: {
2143 196 : if (cv.frame != CrossScriptSSA::OUTER_FRAME || !analysis->integerOperation(cx, pc))
2144 0 : return false;
2145 196 : uint32_t slot = GetBytecodeSlot(outerScript, pc);
2146 196 : if (outerAnalysis->slotEscapes(slot))
2147 23 : return false;
2148 173 : uint32_t write = outerAnalysis->liveness(slot).firstWrite(lifetime);
2149 173 : if (write != UINT32_MAX && write < v.pushedOffset()) {
2150 : /* Variable has been modified since the start of the loop. */
2151 3 : return false;
2152 : }
2153 170 : *pslot = slot;
2154 170 : *pconstant = (op == JSOP_INCLOCAL || op == JSOP_INCARG) ? 1 : 0;
2155 170 : return true;
2156 : }
2157 :
2158 : case JSOP_THIS:
2159 260 : if (cv.frame != CrossScriptSSA::OUTER_FRAME)
2160 0 : return false;
2161 260 : *pslot = ThisSlot();
2162 260 : *pconstant = 0;
2163 260 : return true;
2164 :
2165 : case JSOP_ZERO:
2166 : case JSOP_ONE:
2167 : case JSOP_UINT16:
2168 : case JSOP_UINT24:
2169 : case JSOP_INT8:
2170 : case JSOP_INT32:
2171 6985 : *pslot = UNASSIGNED;
2172 6985 : *pconstant = GetBytecodeInteger(pc);
2173 6985 : return true;
2174 :
2175 : case JSOP_LENGTH: {
2176 1934 : CrossSSAValue lengthcv(cv.frame, analysis->poppedValue(v.pushedOffset(), 0));
2177 1934 : FrameEntry *tmp = invariantLength(lengthcv);
2178 1934 : if (!tmp)
2179 255 : return false;
2180 1679 : *pslot = frame.outerSlot(tmp);
2181 1679 : *pconstant = 0;
2182 1679 : return true;
2183 : }
2184 :
2185 : case JSOP_GETPROP: {
2186 350 : JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
2187 350 : jsid id = ATOM_TO_JSID(atom);
2188 350 : CrossSSAValue objcv(cv.frame, analysis->poppedValue(v.pushedOffset(), 0));
2189 350 : FrameEntry *tmp = invariantProperty(objcv, id);
2190 350 : if (!tmp)
2191 156 : return false;
2192 194 : *pslot = frame.outerSlot(tmp);
2193 194 : *pconstant = 0;
2194 194 : return true;
2195 : }
2196 :
2197 : default:
2198 2328 : return false;
2199 : }
2200 : }
2201 :
2202 : bool
2203 9584 : LoopState::computeInterval(const CrossSSAValue &cv, int32_t *pmin, int32_t *pmax)
2204 : {
2205 9584 : JSScript *script = ssa->getFrame(cv.frame).script;
2206 9584 : ScriptAnalysis *analysis = script->analysis();
2207 9584 : const SSAValue &v = cv.v;
2208 :
2209 9584 : if (v.kind() == SSAValue::VAR && !v.varInitial()) {
2210 168 : jsbytecode *pc = script->code + v.varOffset();
2211 168 : switch (JSOp(*pc)) {
2212 : case JSOP_SETLOCAL:
2213 : case JSOP_SETARG: {
2214 166 : CrossSSAValue ncv(cv.frame, analysis->poppedValue(pc, 0));
2215 166 : return computeInterval(ncv, pmin, pmax);
2216 : }
2217 :
2218 : default:
2219 2 : return false;
2220 : }
2221 : }
2222 :
2223 9416 : if (v.kind() != SSAValue::PUSHED)
2224 971 : return false;
2225 :
2226 8445 : jsbytecode *pc = script->code + v.pushedOffset();
2227 8445 : JSOp op = (JSOp)*pc;
2228 :
2229 : /* Note: this was adapted from similar code in nanojit/LIR.cpp */
2230 8445 : switch (op) {
2231 :
2232 : case JSOP_ZERO:
2233 : case JSOP_ONE:
2234 : case JSOP_UINT16:
2235 : case JSOP_UINT24:
2236 : case JSOP_INT8:
2237 : case JSOP_INT32: {
2238 476 : int32_t constant = GetBytecodeInteger(pc);
2239 476 : *pmin = constant;
2240 476 : *pmax = constant;
2241 476 : return true;
2242 : }
2243 :
2244 : case JSOP_BITAND: {
2245 : int32_t lhsmin, lhsmax, rhsmin, rhsmax;
2246 164 : CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
2247 164 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2248 164 : bool haslhs = computeInterval(lhsv, &lhsmin, &lhsmax);
2249 164 : bool hasrhs = computeInterval(rhsv, &rhsmin, &rhsmax);
2250 :
2251 : /* Only handle bitand with a constant operand. */
2252 164 : haslhs = haslhs && lhsmin == lhsmax && lhsmin >= 0;
2253 164 : hasrhs = hasrhs && rhsmin == rhsmax && rhsmin >= 0;
2254 :
2255 164 : if (haslhs && hasrhs) {
2256 0 : *pmin = 0;
2257 0 : *pmax = Min(lhsmax, rhsmax);
2258 164 : } else if (haslhs) {
2259 10 : *pmin = 0;
2260 10 : *pmax = lhsmax;
2261 154 : } else if (hasrhs) {
2262 122 : *pmin = 0;
2263 122 : *pmax = rhsmax;
2264 : } else {
2265 32 : return false;
2266 : }
2267 132 : return true;
2268 : }
2269 :
2270 : case JSOP_RSH: {
2271 : int32_t rhsmin, rhsmax;
2272 231 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2273 231 : if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax)
2274 17 : return false;
2275 :
2276 : /* Only use the bottom 5 bits. */
2277 214 : int32_t shift = rhsmin & 0x1f;
2278 214 : *pmin = -(1 << (31 - shift));
2279 214 : *pmax = (1 << (31 - shift)) - 1;
2280 214 : return true;
2281 : }
2282 :
2283 : case JSOP_URSH: {
2284 : int32_t rhsmin, rhsmax;
2285 0 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2286 0 : if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax)
2287 0 : return false;
2288 :
2289 : /* Only use the bottom 5 bits. */
2290 0 : int32_t shift = rhsmin & 0x1f;
2291 0 : if (shift == 0)
2292 0 : return false;
2293 0 : *pmin = 0;
2294 0 : *pmax = (1 << (31 - shift)) - 1;
2295 0 : return true;
2296 : }
2297 :
2298 : case JSOP_MOD: {
2299 : int32_t rhsmin, rhsmax;
2300 0 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2301 0 : if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax)
2302 0 : return false;
2303 :
2304 0 : int32_t rhs = abs(rhsmax);
2305 0 : *pmin = -(rhs - 1);
2306 0 : *pmax = rhs - 1;
2307 0 : return true;
2308 : }
2309 :
2310 : case JSOP_ADD: {
2311 : int32_t lhsmin, lhsmax, rhsmin, rhsmax;
2312 2369 : CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
2313 2369 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2314 2369 : if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax))
2315 2223 : return false;
2316 146 : return SafeAdd(lhsmin, rhsmin, pmin) && SafeAdd(lhsmax, rhsmax, pmax);
2317 : }
2318 :
2319 : case JSOP_SUB: {
2320 : int32_t lhsmin, lhsmax, rhsmin, rhsmax;
2321 592 : CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
2322 592 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2323 592 : if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax))
2324 592 : return false;
2325 0 : return SafeSub(lhsmin, rhsmax, pmin) && SafeSub(lhsmax, rhsmin, pmax);
2326 : }
2327 :
2328 : case JSOP_MUL: {
2329 : int32_t lhsmin, lhsmax, rhsmin, rhsmax;
2330 647 : CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
2331 647 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2332 647 : if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax))
2333 607 : return false;
2334 40 : int32_t nlhs = Max(abs(lhsmin), abs(lhsmax));
2335 40 : int32_t nrhs = Max(abs(rhsmin), abs(rhsmax));
2336 :
2337 40 : if (!SafeMul(nlhs, nrhs, pmax))
2338 8 : return false;
2339 :
2340 32 : if (lhsmin < 0 || rhsmin < 0) {
2341 : /* pmax is nonnegative, so can be negated without overflow. */
2342 16 : *pmin = -*pmax;
2343 : } else {
2344 16 : *pmin = 0;
2345 : }
2346 :
2347 32 : return true;
2348 : }
2349 :
2350 : default:
2351 3966 : return false;
2352 : }
2353 : }
|