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 3821 : SafeAdd(int32_t one, int32_t two, int32_t *res)
53 : {
54 3821 : *res = one + two;
55 3821 : int64_t ores = (int64_t)one + (int64_t)two;
56 3821 : if (ores == (int64_t)*res)
57 3815 : return true;
58 6 : JaegerSpew(JSpew_Analysis, "Overflow computing %d + %d\n", one, two);
59 6 : return false;
60 : }
61 :
62 : inline bool
63 17441 : SafeSub(int32_t one, int32_t two, int32_t *res)
64 : {
65 17441 : *res = one - two;
66 17441 : int64_t ores = (int64_t)one - (int64_t)two;
67 17441 : if (ores == (int64_t)*res)
68 17441 : 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 33573 : LoopState::LoopState(JSContext *cx, analyze::CrossScriptSSA *ssa,
85 : mjit::Compiler *cc, FrameState *frame)
86 : : cx(cx), ssa(ssa),
87 67146 : 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 100719 : constrainedLoop(true)
101 : {
102 33573 : JS_ASSERT(cx->typeInferenceEnabled());
103 33573 : }
104 :
105 : bool
106 33573 : LoopState::init(jsbytecode *head, Jump entry, jsbytecode *entryTarget)
107 : {
108 33573 : this->lifetime = outerAnalysis->getLoop(head);
109 0 : JS_ASSERT(lifetime &&
110 : lifetime->head == uint32_t(head - outerScript->code) &&
111 33573 : lifetime->entry == uint32_t(entryTarget - outerScript->code));
112 :
113 33573 : this->entry = entry;
114 :
115 33573 : analyzeLoopTest();
116 33573 : analyzeLoopIncrements();
117 70567 : for (unsigned i = 0; i < ssa->numFrames(); i++) {
118 : /* Only analyze this frame if it is nested within the loop itself. */
119 36994 : uint32_t index = ssa->iterFrame(i).index;
120 36994 : if (index != CrossScriptSSA::OUTER_FRAME) {
121 3421 : unsigned pframe = index;
122 7976 : while (ssa->getFrame(pframe).parent != CrossScriptSSA::OUTER_FRAME)
123 1134 : pframe = ssa->getFrame(pframe).parent;
124 3421 : uint32_t offset = ssa->getFrame(pframe).parentpc - outerScript->code;
125 3421 : JS_ASSERT(offset < outerScript->length);
126 3421 : if (offset < lifetime->head || offset > lifetime->backedge)
127 1821 : continue;
128 : }
129 35173 : analyzeLoopBody(index);
130 : }
131 :
132 33573 : 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 11157 : testConstant);
138 : }
139 :
140 43467 : for (unsigned i = 0; i < increments.length(); i++) {
141 : JaegerSpew(JSpew_Analysis, "loop increment at %u for %s: %u\n", lifetime->head,
142 9894 : frame.entryName(increments[i].slot),
143 19788 : increments[i].offset);
144 : }
145 :
146 34555 : 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 38136 : for (unsigned i = 0; i < modifiedProperties.length(); i++) {
152 : JaegerSpew(JSpew_Analysis, "loop modified property at %u: %s %s\n", lifetime->head,
153 4563 : types::TypeString(types::Type::ObjectType(modifiedProperties[i].object)),
154 9126 : TypeIdString(modifiedProperties[i].id));
155 : }
156 :
157 33573 : RegisterAllocation *&alloc = outerAnalysis->getAllocation(head);
158 33573 : JS_ASSERT(!alloc);
159 :
160 33573 : alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(true);
161 33573 : if (!alloc) {
162 0 : js_ReportOutOfMemory(cx);
163 0 : return false;
164 : }
165 :
166 33573 : this->alloc = alloc;
167 33573 : 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 33573 : if (outerScript->function()) {
174 27017 : if (TypeSet::HasObjectFlags(cx, outerScript->function()->getType(cx), OBJECT_FLAG_UNINLINEABLE))
175 8589 : 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 33573 : if (lifetime->hasSafePoints)
185 281 : this->skipAnalysis = true;
186 :
187 33573 : return true;
188 : }
189 :
190 : void
191 446367 : LoopState::addJoin(unsigned index, bool script)
192 : {
193 : StubJoin r;
194 446367 : r.index = index;
195 446367 : r.script = script;
196 446367 : loopJoins.append(r);
197 446367 : }
198 :
199 : void
200 31932 : LoopState::addInvariantCall(Jump jump, Label label, bool ool, bool entry, unsigned patchIndex, Uses uses)
201 : {
202 31932 : RestoreInvariantCall call;
203 31932 : call.jump = jump;
204 31932 : call.label = label;
205 31932 : call.ool = ool;
206 31932 : call.entry = entry;
207 31932 : call.patchIndex = patchIndex;
208 31932 : call.temporaryCopies = frame.getTemporaryCopies(uses);
209 :
210 31932 : restoreInvariantCalls.append(call);
211 31932 : }
212 :
213 : void
214 33560 : LoopState::flushLoop(StubCompiler &stubcc)
215 : {
216 33560 : clearLoopRegisters();
217 :
218 : /*
219 : * Patch stub compiler rejoins with loads of loop carried registers
220 : * discovered after the fact.
221 : */
222 67391 : for (unsigned i = 0; i < loopPatches.length(); i++) {
223 33831 : const StubJoinPatch &p = loopPatches[i];
224 33831 : stubcc.patchJoin(p.join.index, p.join.script, p.address, p.reg);
225 : }
226 33560 : loopJoins.clear();
227 33560 : loopPatches.clear();
228 :
229 33560 : if (hasInvariants()) {
230 10860 : for (unsigned i = 0; i < restoreInvariantCalls.length(); i++) {
231 8740 : RestoreInvariantCall &call = restoreInvariantCalls[i];
232 8740 : Assembler &masm = cc.getAssembler(true);
233 17480 : Vector<Jump> failureJumps(cx);
234 :
235 8740 : jsbytecode *pc = cc.getInvariantPC(call.patchIndex);
236 :
237 8740 : if (call.ool) {
238 8531 : call.jump.linkTo(masm.label(), &masm);
239 8531 : restoreInvariants(pc, masm, call.temporaryCopies, &failureJumps);
240 8531 : masm.jump().linkTo(call.label, &masm);
241 : } else {
242 209 : stubcc.linkExitDirect(call.jump, masm.label());
243 209 : restoreInvariants(pc, masm, call.temporaryCopies, &failureJumps);
244 209 : stubcc.crossJump(masm.jump(), call.label);
245 : }
246 :
247 8740 : if (!failureJumps.empty()) {
248 45827 : for (unsigned i = 0; i < failureJumps.length(); i++)
249 37476 : 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 8351 : InvariantCodePatch *patch = cc.getInvariantPatch(call.patchIndex);
256 8351 : patch->hasPatch = true;
257 : patch->codePatch = masm.storePtrWithPatch(ImmPtr(NULL),
258 8351 : FrameAddress(offsetof(VMFrame, scratch)));
259 : JS_STATIC_ASSERT(Registers::ReturnReg != Registers::ArgReg1);
260 8351 : masm.move(Registers::ReturnReg, Registers::ArgReg1);
261 :
262 8351 : if (call.entry) {
263 : masm.fallibleVMCall(true, JS_FUNC_TO_DATA_PTR(void *, stubs::InvariantFailure),
264 1146 : pc, NULL, 0);
265 : } else {
266 : /* f.regs are already coherent, don't write new values to them. */
267 7205 : masm.infallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, stubs::InvariantFailure), -1);
268 : }
269 : }
270 : }
271 : } else {
272 54630 : for (unsigned i = 0; i < restoreInvariantCalls.length(); i++) {
273 23190 : RestoreInvariantCall &call = restoreInvariantCalls[i];
274 23190 : Assembler &masm = cc.getAssembler(call.ool);
275 23190 : call.jump.linkTo(call.label, &masm);
276 : }
277 : }
278 33560 : restoreInvariantCalls.clear();
279 33560 : }
280 :
281 : void
282 154366 : LoopState::clearLoopRegisters()
283 : {
284 154366 : alloc->clearLoops();
285 154366 : loopRegs = 0;
286 154366 : }
287 :
288 : bool
289 15731 : LoopState::loopInvariantEntry(uint32_t slot)
290 : {
291 15731 : if (slot == UNASSIGNED)
292 6301 : return true;
293 :
294 : /* Watch for loop temporaries. :XXX: this is really gross. */
295 9430 : if (slot >= analyze::LocalSlot(outerScript, outerScript->nslots))
296 1779 : return true;
297 :
298 7651 : if (slot == analyze::CalleeSlot() || outerAnalysis->slotEscapes(slot))
299 0 : return false;
300 7651 : return outerAnalysis->liveness(slot).firstWrite(lifetime) == UINT32_MAX;
301 : }
302 :
303 : inline bool
304 3966 : LoopState::entryRedundant(const InvariantEntry &e0, const InvariantEntry &e1)
305 : {
306 3966 : JS_ASSERT(e0.isCheck() && e1.isCheck());
307 :
308 3966 : uint32_t array0 = e0.u.check.arraySlot;
309 3966 : uint32_t array1 = e1.u.check.arraySlot;
310 :
311 3966 : uint32_t value01 = e0.u.check.valueSlot1;
312 3966 : uint32_t value02 = e0.u.check.valueSlot2;
313 :
314 3966 : uint32_t value11 = e1.u.check.valueSlot1;
315 3966 : uint32_t value12 = e1.u.check.valueSlot2;
316 :
317 3966 : int32_t c0 = e0.u.check.constant;
318 3966 : 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 3966 : 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 3954 : 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 2859 : return false;
351 : }
352 :
353 : bool
354 2025 : 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 2025 : JS_ASSERT(entry.isCheck());
361 :
362 : /* Maintain this separately, GCC miscompiles if the loop test is invariantEntries.length(). */
363 2025 : unsigned length = invariantEntries.length();
364 :
365 4954 : for (unsigned i = 0; i < length; i++) {
366 3645 : InvariantEntry &baseEntry = invariantEntries[i];
367 3645 : if (!baseEntry.isCheck())
368 1304 : continue;
369 2341 : if (entryRedundant(entry, baseEntry))
370 716 : return true;
371 1625 : 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 1309 : return false;
387 : }
388 :
389 : bool
390 1160 : LoopState::addHoistedCheck(InvariantArrayKind arrayKind, uint32_t arraySlot,
391 : uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant)
392 : {
393 : #ifdef DEBUG
394 1160 : JS_ASSERT_IF(valueSlot1 == UNASSIGNED, valueSlot2 == UNASSIGNED);
395 1160 : const char *field = (arrayKind == DENSE_ARRAY) ? "initlen" : "length";
396 1160 : if (valueSlot1 == UNASSIGNED) {
397 468 : JaegerSpew(JSpew_Analysis, "Hoist %s > %d\n", field, constant);
398 692 : } else if (valueSlot2 == UNASSIGNED) {
399 : JaegerSpew(JSpew_Analysis, "Hoisted as %s > %s + %d\n", field,
400 675 : 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 1160 : InvariantEntry entry;
408 : entry.kind = (arrayKind == DENSE_ARRAY)
409 : ? InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK
410 1160 : : InvariantEntry::TYPED_ARRAY_BOUNDS_CHECK;
411 1160 : entry.u.check.arraySlot = arraySlot;
412 1160 : entry.u.check.valueSlot1 = valueSlot1;
413 1160 : entry.u.check.valueSlot2 = valueSlot2;
414 1160 : entry.u.check.constant = constant;
415 :
416 1160 : 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 806 : bool hasInvariantSlots = false;
426 : InvariantEntry::EntryKind slotsKind = (arrayKind == DENSE_ARRAY)
427 : ? InvariantEntry::DENSE_ARRAY_SLOTS
428 806 : : InvariantEntry::TYPED_ARRAY_SLOTS;
429 2483 : for (unsigned i = 0; !hasInvariantSlots && i < invariantEntries.length(); i++) {
430 1677 : InvariantEntry &entry = invariantEntries[i];
431 1677 : if (entry.kind == slotsKind && entry.u.array.arraySlot == arraySlot)
432 191 : hasInvariantSlots = true;
433 : }
434 806 : if (!hasInvariantSlots) {
435 615 : uint32_t which = frame.allocTemporary();
436 615 : if (which == UINT32_MAX)
437 0 : return false;
438 615 : FrameEntry *fe = frame.getTemporary(which);
439 :
440 : JaegerSpew(JSpew_Analysis, "Using %s for loop invariant slots of %s\n",
441 615 : frame.entryName(fe), frame.entryName(arraySlot));
442 :
443 615 : InvariantEntry slotsEntry;
444 615 : slotsEntry.kind = slotsKind;
445 615 : slotsEntry.u.array.arraySlot = arraySlot;
446 615 : slotsEntry.u.array.temporary = which;
447 615 : invariantEntries.append(slotsEntry);
448 : }
449 :
450 806 : invariantEntries.append(entry);
451 806 : return true;
452 : }
453 :
454 : void
455 844 : LoopState::addNegativeCheck(uint32_t valueSlot, int32_t constant)
456 : {
457 : JaegerSpew(JSpew_Analysis, "Nonnegative check %s + %d >= 0\n",
458 844 : frame.entryName(valueSlot), constant);
459 :
460 844 : InvariantEntry entry;
461 844 : entry.kind = InvariantEntry::NEGATIVE_CHECK;
462 844 : entry.u.check.valueSlot1 = valueSlot;
463 844 : entry.u.check.constant = constant;
464 :
465 844 : if (!checkRedundantEntry(entry))
466 489 : invariantEntries.append(entry);
467 844 : }
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 16187 : LoopState::setLoopReg(AnyRegisterID reg, FrameEntry *fe)
488 : {
489 16187 : JS_ASSERT(alloc->loop(reg));
490 16187 : loopRegs.takeReg(reg);
491 :
492 16187 : uint32_t slot = frame.outerSlot(fe);
493 : JaegerSpew(JSpew_Regalloc, "allocating loop register %s for %s\n",
494 16187 : reg.name(), frame.entryName(fe));
495 :
496 16187 : 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 50018 : for (unsigned i = 0; i < loopJoins.length(); i++) {
503 33831 : StubJoinPatch p;
504 33831 : p.join = loopJoins[i];
505 33831 : p.address = frame.addressOf(fe);
506 33831 : p.reg = reg;
507 33831 : loopPatches.append(p);
508 : }
509 :
510 16187 : 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 16187 : }
521 :
522 : bool
523 15150 : 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 15150 : if (skipAnalysis)
531 12734 : return false;
532 :
533 : uint32_t objSlot;
534 : int32_t objConstant;
535 2416 : 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 1954 : frame.entryName(objSlot));
540 :
541 1954 : 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 1914 : TypeSet *objTypes = ssa->getValueTypes(obj);
553 1914 : if (arrayKind == DENSE_ARRAY && !growArrays.empty()) {
554 578 : unsigned count = objTypes->getObjectCount();
555 1301 : for (unsigned i = 0; i < count; i++) {
556 950 : if (objTypes->getSingleObject(i) != NULL) {
557 0 : JaegerSpew(JSpew_Analysis, "Object might be a singleton");
558 0 : return false;
559 : }
560 950 : TypeObject *object = objTypes->getTypeObject(i);
561 950 : if (object && hasGrowArray(object)) {
562 227 : JaegerSpew(JSpew_Analysis, "Object might grow inside loop\n");
563 227 : 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 1687 : 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 1438 : 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 1120 : 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 704 : 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 649 : if (indexSlot == testLHS && testLessEqual) {
614 : int32_t constant;
615 409 : 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 409 : addNegativeCheck(indexSlot, indexConstant);
626 :
627 409 : 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 240 : 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 223 : JaegerSpew(JSpew_Analysis, "No match found\n");
651 223 : return false;
652 : }
653 :
654 : bool
655 62 : LoopState::hoistArgsLengthCheck(const CrossSSAValue &index)
656 : {
657 62 : if (skipAnalysis)
658 24 : return false;
659 :
660 38 : JaegerSpew(JSpew_Analysis, "Trying to hoist argument range check\n");
661 :
662 : uint32_t indexSlot;
663 : int32_t indexConstant;
664 38 : if (!getEntryValue(index, &indexSlot, &indexConstant)) {
665 4 : JaegerSpew(JSpew_Analysis, "Could not compute index in terms of loop entry state\n");
666 4 : 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 4 : JaegerSpew(JSpew_Analysis, "Index is constant or loop invariant\n");
676 4 : return false;
677 : }
678 :
679 30 : 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 27 : 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 25 : JaegerSpew(JSpew_Analysis, "No match found\n");
703 25 : return false;
704 : }
705 :
706 : bool
707 830 : 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 830 : if (testLHS == UNASSIGNED || testRHS != UNASSIGNED || testLessEqual)
716 795 : 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 1160 : LoopState::invariantArraySlots(const CrossSSAValue &obj)
746 : {
747 1160 : JS_ASSERT(!skipAnalysis);
748 :
749 : uint32_t objSlot;
750 : int32_t objConstant;
751 1160 : 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 3104 : for (unsigned i = 0; i < invariantEntries.length(); i++) {
762 3104 : InvariantEntry &entry = invariantEntries[i];
763 3104 : if ((entry.kind == InvariantEntry::DENSE_ARRAY_SLOTS ||
764 : entry.kind == InvariantEntry::TYPED_ARRAY_SLOTS) &&
765 : entry.u.array.arraySlot == objSlot) {
766 1160 : 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 99 : LoopState::invariantArguments()
777 : {
778 99 : if (skipAnalysis)
779 60 : return NULL;
780 :
781 43 : 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 39 : uint32_t which = frame.allocTemporary();
788 39 : if (which == UINT32_MAX)
789 0 : return NULL;
790 39 : FrameEntry *fe = frame.getTemporary(which);
791 :
792 39 : InvariantEntry entry;
793 39 : entry.kind = InvariantEntry::INVARIANT_ARGS_BASE;
794 39 : entry.u.array.temporary = which;
795 39 : invariantEntries.append(entry);
796 :
797 : JaegerSpew(JSpew_Analysis, "Using %s for loop invariant args base\n",
798 39 : frame.entryName(fe));
799 39 : return fe;
800 : }
801 :
802 : FrameEntry *
803 2181 : LoopState::invariantLength(const CrossSSAValue &obj)
804 : {
805 2181 : if (skipAnalysis)
806 0 : return NULL;
807 :
808 : uint32_t objSlot;
809 : int32_t objConstant;
810 2181 : if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
811 177 : return NULL;
812 2004 : TypeSet *objTypes = ssa->getValueTypes(obj);
813 :
814 : /* Check for 'length' on the lazy arguments for the current frame. */
815 2004 : 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 2099 : for (unsigned i = 0; i < invariantEntries.length(); i++) {
845 266 : InvariantEntry &entry = invariantEntries[i];
846 266 : if ((entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH ||
847 : entry.kind == InvariantEntry::TYPED_ARRAY_LENGTH) &&
848 : entry.u.array.arraySlot == objSlot) {
849 169 : 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 1291 : LoopState::invariantProperty(const CrossSSAValue &obj, jsid id)
916 : {
917 1291 : if (skipAnalysis)
918 0 : return NULL;
919 :
920 1291 : if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
921 210 : return NULL;
922 :
923 : uint32_t objSlot;
924 : int32_t objConstant;
925 1081 : if (!getEntryValue(obj, &objSlot, &objConstant) || objSlot == UNASSIGNED || objConstant != 0)
926 461 : 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 43350 : LoopState::cannotIntegerOverflow(const CrossSSAValue &pushed)
975 : {
976 43350 : if (skipAnalysis)
977 38371 : return false;
978 :
979 : int32_t min, max;
980 4979 : 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 4863 : JS_ASSERT(pushed.v.kind() == SSAValue::PUSHED);
991 4863 : jsbytecode *PC = ssa->getFrame(pushed.frame).script->code + pushed.v.pushedOffset();
992 4863 : ScriptAnalysis *analysis = ssa->getFrame(pushed.frame).script->analysis();
993 :
994 4863 : if (!analysis->integerOperation(cx, PC))
995 1508 : return false;
996 :
997 3355 : uint32_t baseSlot = UNASSIGNED;
998 3355 : int32_t baseConstant = 0;
999 3355 : JSOp op = JSOp(*PC);
1000 3355 : switch (op) {
1001 :
1002 : case JSOP_INCLOCAL:
1003 : case JSOP_LOCALINC:
1004 : case JSOP_INCARG:
1005 : case JSOP_ARGINC: {
1006 1551 : CrossSSAValue cv(pushed.frame, analysis->poppedValue(PC, 0));
1007 1551 : if (!getEntryValue(cv, &baseSlot, &baseConstant))
1008 11 : return false;
1009 1540 : if (!SafeAdd(baseConstant, 1, &baseConstant))
1010 0 : return false;
1011 1540 : 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 1380 : uint32_t lhs = UNASSIGNED, rhs = UNASSIGNED;
1029 1380 : int32_t lhsconstant = 0, rhsconstant = 0;
1030 1380 : CrossSSAValue lcv(pushed.frame, analysis->poppedValue(PC, 1));
1031 1380 : CrossSSAValue rcv(pushed.frame, analysis->poppedValue(PC, 0));
1032 1380 : if (!getEntryValue(lcv, &lhs, &lhsconstant))
1033 891 : 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 1974 : if (baseSlot == UNASSIGNED)
1057 0 : return false;
1058 :
1059 : JaegerSpew(JSpew_Analysis, "Trying to hoist integer overflow check on %s + %d\n",
1060 1974 : frame.entryName(baseSlot), baseConstant);
1061 :
1062 1974 : if (baseConstant == 0) {
1063 6 : JaegerSpew(JSpew_Analysis, "Vacuously holds\n");
1064 6 : return true;
1065 : }
1066 :
1067 1968 : 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 1699 : if (baseSlot == testLHS && testLessEqual) {
1098 : int32_t constant;
1099 1109 : if (!SafeAdd(testConstant, baseConstant, &constant))
1100 2 : return false;
1101 :
1102 1107 : 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 1099 : JaegerSpew(JSpew_Analysis, "Loop test comparison must hold\n");
1108 1099 : 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 590 : 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 577 : JaegerSpew(JSpew_Analysis, "No match found\n");
1141 577 : return false;
1142 : }
1143 :
1144 : bool
1145 43350 : LoopState::ignoreIntegerOverflow(const CrossSSAValue &pushed)
1146 : {
1147 43350 : if (skipAnalysis || unknownModset || !constrainedLoop)
1148 42899 : 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 8740 : 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 8740 : Registers regs(Registers::TempRegs);
1309 8740 : regs.takeReg(Registers::ReturnReg);
1310 8740 : if (regs.hasReg(JSReturnReg_Data))
1311 0 : regs.takeReg(JSReturnReg_Data);
1312 8740 : if (regs.hasReg(JSReturnReg_Type))
1313 0 : regs.takeReg(JSReturnReg_Type);
1314 :
1315 8740 : RegisterID T0 = regs.takeAnyReg().reg();
1316 8740 : RegisterID T1 = regs.takeAnyReg().reg();
1317 :
1318 42818 : for (unsigned i = 0; i < invariantEntries.length(); i++) {
1319 34078 : const InvariantEntry &entry = invariantEntries[i];
1320 34078 : 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 11482 : masm.loadPayload(frame.addressOf(entry.u.check.arraySlot), T0);
1329 11482 : if (entry.kind == InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK) {
1330 11397 : masm.loadPtr(Address(T0, JSObject::offsetOfElements()), T0);
1331 11397 : masm.load32(Address(T0, ObjectElements::offsetOfInitializedLength()), T0);
1332 : } else {
1333 85 : masm.loadPayload(Address(T0, TypedArray::lengthOffset()), T0);
1334 : }
1335 :
1336 11482 : int32_t constant = entry.u.check.constant;
1337 :
1338 11482 : if (entry.u.check.valueSlot1 != UINT32_MAX) {
1339 7706 : constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot1);
1340 7706 : masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T1);
1341 7706 : 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 7706 : if (constant != 0) {
1348 : Jump overflow = masm.branchAdd32(Assembler::Overflow,
1349 3291 : Imm32(constant), T1);
1350 3291 : jumps->append(overflow);
1351 : }
1352 7706 : Jump j = masm.branch32(Assembler::LessThanOrEqual, T0, T1);
1353 7706 : jumps->append(j);
1354 : } else {
1355 : Jump j = masm.branch32(Assembler::LessThanOrEqual, T0,
1356 3776 : Imm32(constant));
1357 3776 : jumps->append(j);
1358 : }
1359 11482 : 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 7182 : masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T0);
1384 7182 : 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 7182 : Jump j = masm.branch32(Assembler::LessThan, T0, Imm32(0));
1390 7182 : jumps->append(j);
1391 7182 : break;
1392 : }
1393 :
1394 : case InvariantEntry::DENSE_ARRAY_SLOTS:
1395 : case InvariantEntry::DENSE_ARRAY_LENGTH: {
1396 13196 : uint32_t array = entry.u.array.arraySlot;
1397 13196 : Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
1398 13196 : jumps->append(notObject);
1399 13196 : masm.loadPayload(frame.addressOf(array), T0);
1400 13196 : masm.loadPtr(Address(T0, JSObject::offsetOfElements()), T0);
1401 :
1402 13196 : Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
1403 :
1404 13196 : if (entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH) {
1405 1799 : masm.load32(Address(T0, ObjectElements::offsetOfLength()), T0);
1406 1799 : masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
1407 : } else {
1408 11397 : masm.storePayload(T0, address);
1409 : }
1410 13196 : 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 409 : Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
1434 409 : masm.loadFrameActuals(outerScript->function(), T0);
1435 409 : masm.storePayload(T0, address);
1436 409 : 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 8850 : 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 8740 : if (temporaryCopies)
1477 90 : cx->delete_(temporaryCopies);
1478 8740 : }
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 23116 : LoopState::getLoopTestAccess(const SSAValue &v, uint32_t *pslot, int32_t *pconstant)
1488 : {
1489 23116 : *pslot = UNASSIGNED;
1490 23116 : *pconstant = 0;
1491 :
1492 23116 : 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 9969 : if (v.kind() == SSAValue::PHI) {
1500 9187 : slot = v.phiSlot();
1501 9187 : offset = v.phiOffset();
1502 : } else {
1503 782 : slot = v.varSlot();
1504 782 : offset = v.varInitial() ? 0 : v.varOffset();
1505 : }
1506 9969 : if (outerAnalysis->slotEscapes(slot))
1507 0 : return false;
1508 9969 : if (outerAnalysis->liveness(slot).firstWrite(offset + 1, lifetime->backedge) != UINT32_MAX)
1509 0 : return false;
1510 9969 : *pslot = slot;
1511 9969 : *pconstant = 0;
1512 9969 : return true;
1513 : }
1514 :
1515 13147 : jsbytecode *pc = outerScript->code + v.pushedOffset();
1516 :
1517 13147 : JSOp op = JSOp(*pc);
1518 13147 : 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 13147 : 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 8153 : *pconstant = GetBytecodeInteger(pc);
1562 8153 : return true;
1563 :
1564 : default:
1565 4887 : return false;
1566 : }
1567 : }
1568 :
1569 : void
1570 33573 : LoopState::analyzeLoopTest()
1571 : {
1572 33573 : if (cc.debugMode())
1573 15533 : return;
1574 :
1575 : /* Don't handle do-while loops. */
1576 18040 : if (lifetime->entry == lifetime->head)
1577 33 : return;
1578 :
1579 : /* Don't handle loops with branching inside their condition. */
1580 18007 : if (lifetime->entry < lifetime->lastBlock)
1581 54 : return;
1582 :
1583 : /* Get the test performed before branching. */
1584 17953 : jsbytecode *backedge = outerScript->code + lifetime->backedge;
1585 17953 : if (JSOp(*backedge) != JSOP_IFNE)
1586 0 : return;
1587 17953 : const SSAValue &test = outerAnalysis->poppedValue(backedge, 0);
1588 17953 : if (test.kind() != SSAValue::PUSHED)
1589 129 : return;
1590 17824 : JSOp cmpop = JSOp(outerScript->code[test.pushedOffset()]);
1591 17824 : switch (cmpop) {
1592 : case JSOP_GT:
1593 : case JSOP_GE:
1594 : case JSOP_LT:
1595 : case JSOP_LE:
1596 : break;
1597 : default:
1598 2337 : return;
1599 : }
1600 :
1601 15487 : SSAValue one = outerAnalysis->poppedValue(test.pushedOffset(), 1);
1602 15487 : SSAValue two = outerAnalysis->poppedValue(test.pushedOffset(), 0);
1603 :
1604 : /* The test must be comparing known integers. */
1605 27852 : if (outerAnalysis->getValueTypes(one)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32 ||
1606 12365 : outerAnalysis->getValueTypes(two)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
1607 3929 : return;
1608 : }
1609 :
1610 : /* Reverse the condition if the RHS is modified by the loop. */
1611 : uint32_t swapRHS;
1612 : int32_t swapConstant;
1613 11558 : if (getLoopTestAccess(two, &swapRHS, &swapConstant)) {
1614 8975 : 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 11558 : if (!getLoopTestAccess(one, &lhs, &lhsConstant))
1625 2304 : return;
1626 :
1627 9254 : uint32_t rhs = UNASSIGNED;
1628 9254 : int32_t rhsConstant = 0;
1629 9254 : CrossSSAValue rhsv(CrossScriptSSA::OUTER_FRAME, two);
1630 9254 : if (!getEntryValue(rhsv, &rhs, &rhsConstant))
1631 525 : return;
1632 8729 : if (!loopInvariantEntry(rhs))
1633 0 : return;
1634 :
1635 8729 : if (lhs == UNASSIGNED)
1636 0 : return;
1637 :
1638 : int32_t constant;
1639 8729 : if (!SafeSub(rhsConstant, lhsConstant, &constant))
1640 0 : return;
1641 :
1642 : /* x > y ==> x >= y + 1 */
1643 8729 : if (cmpop == JSOP_GT && !SafeAdd(constant, 1, &constant))
1644 0 : return;
1645 :
1646 : /* x < y ==> x <= y - 1 */
1647 8729 : 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 8729 : this->testLHS = lhs;
1653 8729 : this->testRHS = rhs;
1654 8729 : this->testConstant = constant;
1655 8729 : this->testLessEqual = (cmpop == JSOP_LT || cmpop == JSOP_LE);
1656 : }
1657 :
1658 : void
1659 33573 : LoopState::analyzeLoopIncrements()
1660 : {
1661 33573 : if (cc.debugMode())
1662 15533 : 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 82557 : for (uint32_t slot = ArgSlot(0); slot < LocalSlot(outerScript, outerScript->nfixed); slot++) {
1671 64517 : if (outerAnalysis->slotEscapes(slot))
1672 8274 : continue;
1673 :
1674 56243 : uint32_t offset = outerAnalysis->liveness(slot).onlyWrite(lifetime);
1675 56243 : if (offset == UINT32_MAX || offset < lifetime->lastBlock)
1676 43710 : continue;
1677 :
1678 12533 : jsbytecode *pc = outerScript->code + offset;
1679 12533 : JSOp op = JSOp(*pc);
1680 12533 : const JSCodeSpec *cs = &js_CodeSpec[op];
1681 12533 : if (cs->format & (JOF_INC | JOF_DEC)) {
1682 9941 : if (!outerAnalysis->integerOperation(cx, pc))
1683 47 : continue;
1684 :
1685 : Increment inc;
1686 9894 : inc.slot = slot;
1687 9894 : inc.offset = offset;
1688 9894 : increments.append(inc);
1689 : }
1690 : }
1691 : }
1692 :
1693 : bool
1694 2212 : 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 2212 : TypeSet *objTypes = outerAnalysis->getValueTypes(obj);
1710 2212 : TypeSet *elemTypes = outerAnalysis->getValueTypes(index);
1711 :
1712 4192 : if (objTypes->getKnownTypeTag(cx) != JSVAL_TYPE_OBJECT ||
1713 1980 : elemTypes->getKnownTypeTag(cx) != JSVAL_TYPE_INT32) {
1714 276 : 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 35173 : LoopState::analyzeLoopBody(unsigned frame)
1763 : {
1764 35173 : if (cc.debugMode()) {
1765 15533 : skipAnalysis = true;
1766 15533 : return;
1767 : }
1768 :
1769 19640 : JSScript *script = ssa->getFrame(frame).script;
1770 19640 : analyze::ScriptAnalysis *analysis = script->analysis();
1771 19640 : 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 19640 : ssa->getFrame(frame).depth + VALUES_PER_STACK_FRAME * 2 + script->nslots);
1782 :
1783 19640 : if (script->failedBoundsCheck || analysis->localsAliasStack())
1784 3106 : skipAnalysis = true;
1785 :
1786 : /* Analyze the entire script for frames inlined in the loop body. */
1787 19640 : unsigned start = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->head + JSOP_LOOPHEAD_LENGTH : 0;
1788 19640 : unsigned end = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->backedge : script->length;
1789 :
1790 19640 : unsigned offset = start;
1791 659497 : while (offset < end) {
1792 620217 : jsbytecode *pc = script->code + offset;
1793 620217 : uint32_t successorOffset = offset + GetBytecodeLength(pc);
1794 :
1795 620217 : analyze::Bytecode *opinfo = analysis->maybeCode(offset);
1796 620217 : if (!opinfo) {
1797 1597 : offset = successorOffset;
1798 1597 : continue;
1799 : }
1800 :
1801 618620 : JSOp op = JSOp(*pc);
1802 :
1803 : /* Don't do any hoisting for outer loops in case of nesting. */
1804 618620 : if (op == JSOP_LOOPHEAD)
1805 2746 : skipAnalysis = true;
1806 :
1807 618620 : 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 34407 : bool foundInline = false;
1815 92155 : for (unsigned i = 0; !foundInline && i < ssa->numFrames(); i++) {
1816 57748 : if (ssa->iterFrame(i).parent == frame && ssa->iterFrame(i).parentpc == pc)
1817 1478 : foundInline = true;
1818 : }
1819 34407 : if (!foundInline)
1820 32929 : skipAnalysis = true;
1821 34407 : break;
1822 : }
1823 :
1824 : case JSOP_EVAL:
1825 : case JSOP_FUNCALL:
1826 : case JSOP_FUNAPPLY:
1827 : case JSOP_NEW:
1828 3154 : skipAnalysis = true;
1829 3154 : 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 27 : continue;
1852 5091 : if (!addModifiedProperty(object, JSID_VOID))
1853 0 : return;
1854 5091 : 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 17490 : SSAValue objValue = analysis->poppedValue(pc, 1);
1865 17490 : SSAValue elemValue = analysis->poppedValue(pc, 0);
1866 :
1867 17490 : if (constrainedLoop && !definiteArrayAccess(objValue, elemValue))
1868 559 : constrainedLoop = false;
1869 17490 : break;
1870 : }
1871 :
1872 : case JSOP_SETPROP: {
1873 1378 : JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
1874 1378 : jsid id = MakeTypeId(cx, ATOM_TO_JSID(atom));
1875 :
1876 1378 : TypeSet *objTypes = analysis->poppedTypes(pc, 1);
1877 1378 : if (objTypes->unknownObject()) {
1878 207 : unknownModset = true;
1879 207 : break;
1880 : }
1881 :
1882 1171 : objTypes->addFreeze(cx);
1883 2157 : for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
1884 986 : TypeObject *object = objTypes->getTypeObject(i);
1885 986 : if (!object)
1886 77 : continue;
1887 909 : if (!addModifiedProperty(object, id))
1888 0 : continue;
1889 : }
1890 :
1891 1171 : constrainedLoop = false;
1892 1171 : break;
1893 : }
1894 :
1895 : case JSOP_ENUMELEM:
1896 : case JSOP_ENUMCONSTELEM:
1897 0 : unknownModset = true;
1898 0 : break;
1899 :
1900 : case JSOP_LOOPHEAD:
1901 : case JSOP_LOOPENTRY:
1902 : case JSOP_POP:
1903 : case JSOP_ZERO:
1904 : case JSOP_ONE:
1905 : case JSOP_INT8:
1906 : case JSOP_INT32:
1907 : case JSOP_UINT16:
1908 : case JSOP_UINT24:
1909 : case JSOP_FALSE:
1910 : case JSOP_TRUE:
1911 : case JSOP_GETARG:
1912 : case JSOP_SETARG:
1913 : case JSOP_INCARG:
1914 : case JSOP_DECARG:
1915 : case JSOP_ARGINC:
1916 : case JSOP_ARGDEC:
1917 : case JSOP_THIS:
1918 : case JSOP_GETLOCAL:
1919 : case JSOP_SETLOCAL:
1920 : case JSOP_INCLOCAL:
1921 : case JSOP_DECLOCAL:
1922 : case JSOP_LOCALINC:
1923 : case JSOP_LOCALDEC:
1924 : case JSOP_IFEQ:
1925 : case JSOP_IFNE:
1926 : case JSOP_AND:
1927 : case JSOP_OR:
1928 : case JSOP_GOTO:
1929 268347 : break;
1930 :
1931 : case JSOP_ADD:
1932 : case JSOP_SUB:
1933 : case JSOP_MUL:
1934 : case JSOP_MOD:
1935 : case JSOP_DIV:
1936 : case JSOP_BITAND:
1937 : case JSOP_BITOR:
1938 : case JSOP_BITXOR:
1939 : case JSOP_RSH:
1940 : case JSOP_LSH:
1941 : case JSOP_URSH:
1942 : case JSOP_EQ:
1943 : case JSOP_NE:
1944 : case JSOP_LT:
1945 : case JSOP_LE:
1946 : case JSOP_GT:
1947 : case JSOP_GE:
1948 : case JSOP_STRICTEQ:
1949 : case JSOP_STRICTNE: {
1950 45710 : JSValueType type = analysis->poppedTypes(pc, 1)->getKnownTypeTag(cx);
1951 45710 : if (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_DOUBLE)
1952 15392 : constrainedLoop = false;
1953 : }
1954 : /* FALLTHROUGH */
1955 :
1956 : case JSOP_POS:
1957 : case JSOP_NEG:
1958 : case JSOP_BITNOT: {
1959 50335 : JSValueType type = analysis->poppedTypes(pc, 0)->getKnownTypeTag(cx);
1960 50335 : if (type != JSVAL_TYPE_INT32 && type != JSVAL_TYPE_DOUBLE)
1961 12586 : constrainedLoop = false;
1962 50335 : break;
1963 : }
1964 :
1965 : default:
1966 238003 : constrainedLoop = false;
1967 238003 : break;
1968 : }
1969 :
1970 618620 : offset = successorOffset;
1971 : }
1972 : }
1973 :
1974 : bool
1975 1035 : LoopState::addGrowArray(TypeObject *object)
1976 : {
1977 : static const uint32_t MAX_SIZE = 10;
1978 1468 : for (unsigned i = 0; i < growArrays.length(); i++) {
1979 486 : if (growArrays[i] == object)
1980 53 : return true;
1981 : }
1982 982 : if (growArrays.length() >= MAX_SIZE) {
1983 0 : unknownModset = true;
1984 0 : return false;
1985 : }
1986 982 : growArrays.append(object);
1987 :
1988 982 : return true;
1989 : }
1990 :
1991 : bool
1992 6000 : LoopState::addModifiedProperty(TypeObject *object, jsid id)
1993 : {
1994 : static const uint32_t MAX_SIZE = 20;
1995 9469 : for (unsigned i = 0; i < modifiedProperties.length(); i++) {
1996 4906 : if (modifiedProperties[i].object == object && modifiedProperties[i].id == id)
1997 1437 : return true;
1998 : }
1999 4563 : if (modifiedProperties.length() >= MAX_SIZE) {
2000 0 : unknownModset = true;
2001 0 : return false;
2002 : }
2003 :
2004 : ModifiedProperty property;
2005 4563 : property.object = object;
2006 4563 : property.id = id;
2007 4563 : modifiedProperties.append(property);
2008 :
2009 4563 : return true;
2010 : }
2011 :
2012 : bool
2013 950 : LoopState::hasGrowArray(TypeObject *object)
2014 : {
2015 950 : if (unknownModset)
2016 0 : return true;
2017 3391 : for (unsigned i = 0; i < growArrays.length(); i++) {
2018 2668 : if (growArrays[i] == object)
2019 227 : return true;
2020 : }
2021 723 : return false;
2022 : }
2023 :
2024 : bool
2025 2514 : LoopState::hasModifiedProperty(TypeObject *object, jsid id)
2026 : {
2027 2514 : if (unknownModset)
2028 14 : return true;
2029 2500 : id = MakeTypeId(cx, id);
2030 2560 : for (unsigned i = 0; i < modifiedProperties.length(); i++) {
2031 213 : if (modifiedProperties[i].object == object && modifiedProperties[i].id == id)
2032 153 : return true;
2033 : }
2034 2347 : return false;
2035 : }
2036 :
2037 : uint32_t
2038 8144 : LoopState::getIncrement(uint32_t slot)
2039 : {
2040 15886 : for (unsigned i = 0; i < increments.length(); i++) {
2041 8354 : if (increments[i].slot == slot)
2042 612 : return increments[i].offset;
2043 : }
2044 7532 : return UINT32_MAX;
2045 : }
2046 :
2047 : int32_t
2048 8079 : LoopState::adjustConstantForIncrement(jsbytecode *pc, uint32_t slot)
2049 : {
2050 : /*
2051 : * The only terms that can appear in a hoisted bounds check are either
2052 : * loop invariant or are incremented or decremented exactly once in each
2053 : * iteration of the loop. Depending on the current pc in the body of the
2054 : * loop, return a constant adjustment if an increment/decrement for slot
2055 : * has not yet happened, such that 'slot + n' at this point is the value
2056 : * of slot at the start of the next iteration.
2057 : */
2058 8079 : uint32_t offset = getIncrement(slot);
2059 :
2060 : /*
2061 : * Note the '<' here. If this PC is at one of the increment opcodes, then
2062 : * behave as if the increment has not happened yet. This is needed for loop
2063 : * entry points, which can be directly at an increment. We won't rejoin
2064 : * after the increment, as we only take stub calls in such situations on
2065 : * integer overflow, which will disable hoisted conditions involving the
2066 : * variable anyways.
2067 : */
2068 8079 : if (offset == UINT32_MAX || offset < uint32_t(pc - outerScript->code))
2069 7656 : return 0;
2070 :
2071 423 : switch (JSOp(outerScript->code[offset])) {
2072 : case JSOP_INCLOCAL:
2073 : case JSOP_LOCALINC:
2074 : case JSOP_INCARG:
2075 : case JSOP_ARGINC:
2076 147 : return 1;
2077 : case JSOP_DECLOCAL:
2078 : case JSOP_LOCALDEC:
2079 : case JSOP_DECARG:
2080 : case JSOP_ARGDEC:
2081 276 : return -1;
2082 : default:
2083 0 : JS_NOT_REACHED("Bad op");
2084 : return 0;
2085 : }
2086 : }
2087 :
2088 : bool
2089 24727 : LoopState::getEntryValue(const CrossSSAValue &iv, uint32_t *pslot, int32_t *pconstant)
2090 : {
2091 24727 : CrossSSAValue cv = ssa->foldValue(iv);
2092 :
2093 24727 : JSScript *script = ssa->getFrame(cv.frame).script;
2094 24727 : ScriptAnalysis *analysis = script->analysis();
2095 24727 : const SSAValue &v = cv.v;
2096 :
2097 : /*
2098 : * For a stack value popped by the bytecode at offset, try to get an
2099 : * expression 'slot + constant' with the same value as the stack value
2100 : * and expressed in terms of the state at loop entry.
2101 : */
2102 :
2103 24727 : if (v.kind() == SSAValue::PHI) {
2104 4651 : if (cv.frame != CrossScriptSSA::OUTER_FRAME)
2105 1 : return false;
2106 4650 : if (v.phiSlot() >= TotalSlots(script))
2107 5 : return false;
2108 4793 : if (v.phiOffset() > lifetime->head &&
2109 148 : outerAnalysis->liveness(v.phiSlot()).firstWrite(lifetime) < v.phiOffset()) {
2110 70 : return false;
2111 : }
2112 4575 : *pslot = v.phiSlot();
2113 4575 : *pconstant = 0;
2114 4575 : return true;
2115 : }
2116 :
2117 20076 : if (v.kind() == SSAValue::VAR) {
2118 8000 : if (cv.frame != CrossScriptSSA::OUTER_FRAME)
2119 16 : return false;
2120 7984 : if (v.varInitial() || v.varOffset() < lifetime->head) {
2121 7658 : *pslot = v.varSlot();
2122 7658 : *pconstant = 0;
2123 7658 : return true;
2124 : }
2125 : }
2126 :
2127 12402 : if (v.kind() != SSAValue::PUSHED)
2128 326 : return false;
2129 :
2130 12076 : jsbytecode *pc = script->code + v.pushedOffset();
2131 12076 : JSOp op = (JSOp)*pc;
2132 :
2133 12076 : switch (op) {
2134 :
2135 : case JSOP_GETLOCAL:
2136 : case JSOP_LOCALINC:
2137 : case JSOP_INCLOCAL:
2138 : case JSOP_GETARG:
2139 : case JSOP_ARGINC:
2140 : case JSOP_INCARG: {
2141 197 : if (cv.frame != CrossScriptSSA::OUTER_FRAME || !analysis->integerOperation(cx, pc))
2142 0 : return false;
2143 197 : uint32_t slot = GetBytecodeSlot(outerScript, pc);
2144 197 : if (outerAnalysis->slotEscapes(slot))
2145 24 : return false;
2146 173 : uint32_t write = outerAnalysis->liveness(slot).firstWrite(lifetime);
2147 173 : if (write != UINT32_MAX && write < v.pushedOffset()) {
2148 : /* Variable has been modified since the start of the loop. */
2149 3 : return false;
2150 : }
2151 170 : *pslot = slot;
2152 170 : *pconstant = (op == JSOP_INCLOCAL || op == JSOP_INCARG) ? 1 : 0;
2153 170 : return true;
2154 : }
2155 :
2156 : case JSOP_THIS:
2157 260 : if (cv.frame != CrossScriptSSA::OUTER_FRAME)
2158 0 : return false;
2159 260 : *pslot = ThisSlot();
2160 260 : *pconstant = 0;
2161 260 : return true;
2162 :
2163 : case JSOP_ZERO:
2164 : case JSOP_ONE:
2165 : case JSOP_UINT16:
2166 : case JSOP_UINT24:
2167 : case JSOP_INT8:
2168 : case JSOP_INT32:
2169 6991 : *pslot = UNASSIGNED;
2170 6991 : *pconstant = GetBytecodeInteger(pc);
2171 6991 : return true;
2172 :
2173 : case JSOP_LENGTH: {
2174 1936 : CrossSSAValue lengthcv(cv.frame, analysis->poppedValue(v.pushedOffset(), 0));
2175 1936 : FrameEntry *tmp = invariantLength(lengthcv);
2176 1936 : if (!tmp)
2177 257 : return false;
2178 1679 : *pslot = frame.outerSlot(tmp);
2179 1679 : *pconstant = 0;
2180 1679 : return true;
2181 : }
2182 :
2183 : case JSOP_GETPROP: {
2184 350 : JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
2185 350 : jsid id = ATOM_TO_JSID(atom);
2186 350 : CrossSSAValue objcv(cv.frame, analysis->poppedValue(v.pushedOffset(), 0));
2187 350 : FrameEntry *tmp = invariantProperty(objcv, id);
2188 350 : if (!tmp)
2189 156 : return false;
2190 194 : *pslot = frame.outerSlot(tmp);
2191 194 : *pconstant = 0;
2192 194 : return true;
2193 : }
2194 :
2195 : default:
2196 2342 : return false;
2197 : }
2198 : }
2199 :
2200 : bool
2201 9669 : LoopState::computeInterval(const CrossSSAValue &cv, int32_t *pmin, int32_t *pmax)
2202 : {
2203 9669 : JSScript *script = ssa->getFrame(cv.frame).script;
2204 9669 : ScriptAnalysis *analysis = script->analysis();
2205 9669 : const SSAValue &v = cv.v;
2206 :
2207 9669 : if (v.kind() == SSAValue::VAR && !v.varInitial()) {
2208 168 : jsbytecode *pc = script->code + v.varOffset();
2209 168 : switch (JSOp(*pc)) {
2210 : case JSOP_SETLOCAL:
2211 : case JSOP_SETARG: {
2212 168 : CrossSSAValue ncv(cv.frame, analysis->poppedValue(pc, 0));
2213 168 : return computeInterval(ncv, pmin, pmax);
2214 : }
2215 :
2216 : default:
2217 0 : return false;
2218 : }
2219 : }
2220 :
2221 9501 : if (v.kind() != SSAValue::PUSHED)
2222 971 : return false;
2223 :
2224 8530 : jsbytecode *pc = script->code + v.pushedOffset();
2225 8530 : JSOp op = (JSOp)*pc;
2226 :
2227 : /* Note: this was adapted from similar code in nanojit/LIR.cpp */
2228 8530 : switch (op) {
2229 :
2230 : case JSOP_ZERO:
2231 : case JSOP_ONE:
2232 : case JSOP_UINT16:
2233 : case JSOP_UINT24:
2234 : case JSOP_INT8:
2235 : case JSOP_INT32: {
2236 476 : int32_t constant = GetBytecodeInteger(pc);
2237 476 : *pmin = constant;
2238 476 : *pmax = constant;
2239 476 : return true;
2240 : }
2241 :
2242 : case JSOP_BITAND: {
2243 : int32_t lhsmin, lhsmax, rhsmin, rhsmax;
2244 164 : CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
2245 164 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2246 164 : bool haslhs = computeInterval(lhsv, &lhsmin, &lhsmax);
2247 164 : bool hasrhs = computeInterval(rhsv, &rhsmin, &rhsmax);
2248 :
2249 : /* Only handle bitand with a constant operand. */
2250 164 : haslhs = haslhs && lhsmin == lhsmax && lhsmin >= 0;
2251 164 : hasrhs = hasrhs && rhsmin == rhsmax && rhsmin >= 0;
2252 :
2253 164 : if (haslhs && hasrhs) {
2254 0 : *pmin = 0;
2255 0 : *pmax = Min(lhsmax, rhsmax);
2256 164 : } else if (haslhs) {
2257 10 : *pmin = 0;
2258 10 : *pmax = lhsmax;
2259 154 : } else if (hasrhs) {
2260 122 : *pmin = 0;
2261 122 : *pmax = rhsmax;
2262 : } else {
2263 32 : return false;
2264 : }
2265 132 : return true;
2266 : }
2267 :
2268 : case JSOP_RSH: {
2269 : int32_t rhsmin, rhsmax;
2270 231 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2271 231 : if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax)
2272 17 : return false;
2273 :
2274 : /* Only use the bottom 5 bits. */
2275 214 : int32_t shift = rhsmin & 0x1f;
2276 214 : *pmin = -(1 << (31 - shift));
2277 214 : *pmax = (1 << (31 - shift)) - 1;
2278 214 : return true;
2279 : }
2280 :
2281 : case JSOP_URSH: {
2282 : int32_t rhsmin, rhsmax;
2283 0 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2284 0 : if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax)
2285 0 : return false;
2286 :
2287 : /* Only use the bottom 5 bits. */
2288 0 : int32_t shift = rhsmin & 0x1f;
2289 0 : if (shift == 0)
2290 0 : return false;
2291 0 : *pmin = 0;
2292 0 : *pmax = (1 << (31 - shift)) - 1;
2293 0 : return true;
2294 : }
2295 :
2296 : case JSOP_MOD: {
2297 : int32_t rhsmin, rhsmax;
2298 0 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2299 0 : if (!computeInterval(rhsv, &rhsmin, &rhsmax) || rhsmin != rhsmax)
2300 0 : return false;
2301 :
2302 0 : int32_t rhs = abs(rhsmax);
2303 0 : *pmin = -(rhs - 1);
2304 0 : *pmax = rhs - 1;
2305 0 : return true;
2306 : }
2307 :
2308 : case JSOP_ADD: {
2309 : int32_t lhsmin, lhsmax, rhsmin, rhsmax;
2310 2380 : CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
2311 2380 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2312 2380 : if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax))
2313 2234 : return false;
2314 146 : return SafeAdd(lhsmin, rhsmin, pmin) && SafeAdd(lhsmax, rhsmax, pmax);
2315 : }
2316 :
2317 : case JSOP_SUB: {
2318 : int32_t lhsmin, lhsmax, rhsmin, rhsmax;
2319 603 : CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
2320 603 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2321 603 : if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax))
2322 603 : return false;
2323 0 : return SafeSub(lhsmin, rhsmax, pmin) && SafeSub(lhsmax, rhsmin, pmax);
2324 : }
2325 :
2326 : case JSOP_MUL: {
2327 : int32_t lhsmin, lhsmax, rhsmin, rhsmax;
2328 648 : CrossSSAValue lhsv(cv.frame, analysis->poppedValue(pc, 1));
2329 648 : CrossSSAValue rhsv(cv.frame, analysis->poppedValue(pc, 0));
2330 648 : if (!computeInterval(lhsv, &lhsmin, &lhsmax) || !computeInterval(rhsv, &rhsmin, &rhsmax))
2331 608 : return false;
2332 40 : int32_t nlhs = Max(abs(lhsmin), abs(lhsmax));
2333 40 : int32_t nrhs = Max(abs(rhsmin), abs(rhsmax));
2334 :
2335 40 : if (!SafeMul(nlhs, nrhs, pmax))
2336 8 : return false;
2337 :
2338 32 : if (lhsmin < 0 || rhsmin < 0) {
2339 : /* pmax is nonnegative, so can be negated without overflow. */
2340 16 : *pmin = -*pmax;
2341 : } else {
2342 16 : *pmin = 0;
2343 : }
2344 :
2345 32 : return true;
2346 : }
2347 :
2348 : default:
2349 4028 : return false;
2350 : }
2351 : }
|