|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "jit/MIR.h" |
|
8 |
|
9 #include "mozilla/FloatingPoint.h" |
|
10 |
|
11 #include <ctype.h> |
|
12 |
|
13 #include "jslibmath.h" |
|
14 #include "jsstr.h" |
|
15 |
|
16 #include "jit/BaselineInspector.h" |
|
17 #include "jit/IonBuilder.h" |
|
18 #include "jit/IonSpewer.h" |
|
19 #include "jit/MIRGraph.h" |
|
20 #include "jit/RangeAnalysis.h" |
|
21 |
|
22 #include "jsatominlines.h" |
|
23 #include "jsinferinlines.h" |
|
24 #include "jsobjinlines.h" |
|
25 |
|
26 using namespace js; |
|
27 using namespace js::jit; |
|
28 |
|
29 using mozilla::NumbersAreIdentical; |
|
30 using mozilla::IsFloat32Representable; |
|
31 using mozilla::Maybe; |
|
32 |
|
33 template<size_t Op> static void |
|
34 ConvertDefinitionToDouble(TempAllocator &alloc, MDefinition *def, MInstruction *consumer) |
|
35 { |
|
36 MInstruction *replace = MToDouble::New(alloc, def); |
|
37 consumer->replaceOperand(Op, replace); |
|
38 consumer->block()->insertBefore(consumer, replace); |
|
39 } |
|
40 |
|
41 static bool |
|
42 CheckUsesAreFloat32Consumers(MInstruction *ins) |
|
43 { |
|
44 bool allConsumerUses = true; |
|
45 for (MUseDefIterator use(ins); allConsumerUses && use; use++) |
|
46 allConsumerUses &= use.def()->canConsumeFloat32(use.use()); |
|
47 return allConsumerUses; |
|
48 } |
|
49 |
|
50 void |
|
51 MDefinition::PrintOpcodeName(FILE *fp, MDefinition::Opcode op) |
|
52 { |
|
53 static const char * const names[] = |
|
54 { |
|
55 #define NAME(x) #x, |
|
56 MIR_OPCODE_LIST(NAME) |
|
57 #undef NAME |
|
58 }; |
|
59 const char *name = names[op]; |
|
60 size_t len = strlen(name); |
|
61 for (size_t i = 0; i < len; i++) |
|
62 fprintf(fp, "%c", tolower(name[i])); |
|
63 } |
|
64 |
|
65 static inline bool |
|
66 EqualValues(bool useGVN, MDefinition *left, MDefinition *right) |
|
67 { |
|
68 if (useGVN) |
|
69 return left->valueNumber() == right->valueNumber(); |
|
70 |
|
71 return left == right; |
|
72 } |
|
73 |
|
74 static MConstant * |
|
75 EvaluateConstantOperands(TempAllocator &alloc, MBinaryInstruction *ins, bool *ptypeChange = nullptr) |
|
76 { |
|
77 MDefinition *left = ins->getOperand(0); |
|
78 MDefinition *right = ins->getOperand(1); |
|
79 |
|
80 if (!left->isConstant() || !right->isConstant()) |
|
81 return nullptr; |
|
82 |
|
83 Value lhs = left->toConstant()->value(); |
|
84 Value rhs = right->toConstant()->value(); |
|
85 Value ret = UndefinedValue(); |
|
86 |
|
87 switch (ins->op()) { |
|
88 case MDefinition::Op_BitAnd: |
|
89 ret = Int32Value(lhs.toInt32() & rhs.toInt32()); |
|
90 break; |
|
91 case MDefinition::Op_BitOr: |
|
92 ret = Int32Value(lhs.toInt32() | rhs.toInt32()); |
|
93 break; |
|
94 case MDefinition::Op_BitXor: |
|
95 ret = Int32Value(lhs.toInt32() ^ rhs.toInt32()); |
|
96 break; |
|
97 case MDefinition::Op_Lsh: |
|
98 ret = Int32Value(uint32_t(lhs.toInt32()) << (rhs.toInt32() & 0x1F)); |
|
99 break; |
|
100 case MDefinition::Op_Rsh: |
|
101 ret = Int32Value(lhs.toInt32() >> (rhs.toInt32() & 0x1F)); |
|
102 break; |
|
103 case MDefinition::Op_Ursh: |
|
104 ret.setNumber(uint32_t(lhs.toInt32()) >> (rhs.toInt32() & 0x1F)); |
|
105 break; |
|
106 case MDefinition::Op_Add: |
|
107 ret.setNumber(lhs.toNumber() + rhs.toNumber()); |
|
108 break; |
|
109 case MDefinition::Op_Sub: |
|
110 ret.setNumber(lhs.toNumber() - rhs.toNumber()); |
|
111 break; |
|
112 case MDefinition::Op_Mul: |
|
113 ret.setNumber(lhs.toNumber() * rhs.toNumber()); |
|
114 break; |
|
115 case MDefinition::Op_Div: |
|
116 ret.setNumber(NumberDiv(lhs.toNumber(), rhs.toNumber())); |
|
117 break; |
|
118 case MDefinition::Op_Mod: |
|
119 ret.setNumber(NumberMod(lhs.toNumber(), rhs.toNumber())); |
|
120 break; |
|
121 default: |
|
122 MOZ_ASSUME_UNREACHABLE("NYI"); |
|
123 } |
|
124 |
|
125 // setNumber eagerly transforms a number to int32. |
|
126 // Transform back to double, if the output type is double. |
|
127 if (ins->type() == MIRType_Double && ret.isInt32()) |
|
128 ret.setDouble(ret.toNumber()); |
|
129 |
|
130 if (ins->type() != MIRTypeFromValue(ret)) { |
|
131 if (ptypeChange) |
|
132 *ptypeChange = true; |
|
133 return nullptr; |
|
134 } |
|
135 |
|
136 return MConstant::New(alloc, ret); |
|
137 } |
|
138 |
|
139 void |
|
140 MDefinition::printName(FILE *fp) const |
|
141 { |
|
142 PrintOpcodeName(fp, op()); |
|
143 fprintf(fp, "%u", id()); |
|
144 |
|
145 if (valueNumber() != 0) |
|
146 fprintf(fp, "-vn%u", valueNumber()); |
|
147 } |
|
148 |
|
149 HashNumber |
|
150 MDefinition::valueHash() const |
|
151 { |
|
152 HashNumber out = op(); |
|
153 for (size_t i = 0, e = numOperands(); i < e; i++) { |
|
154 uint32_t valueNumber = getOperand(i)->valueNumber(); |
|
155 out = valueNumber + (out << 6) + (out << 16) - out; |
|
156 } |
|
157 return out; |
|
158 } |
|
159 |
|
160 bool |
|
161 MDefinition::congruentIfOperandsEqual(const MDefinition *ins) const |
|
162 { |
|
163 if (op() != ins->op()) |
|
164 return false; |
|
165 |
|
166 if (type() != ins->type()) |
|
167 return false; |
|
168 |
|
169 if (isEffectful() || ins->isEffectful()) |
|
170 return false; |
|
171 |
|
172 if (numOperands() != ins->numOperands()) |
|
173 return false; |
|
174 |
|
175 for (size_t i = 0, e = numOperands(); i < e; i++) { |
|
176 if (getOperand(i)->valueNumber() != ins->getOperand(i)->valueNumber()) |
|
177 return false; |
|
178 } |
|
179 |
|
180 return true; |
|
181 } |
|
182 |
|
183 MDefinition * |
|
184 MDefinition::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
185 { |
|
186 // In the default case, there are no constants to fold. |
|
187 return this; |
|
188 } |
|
189 |
|
190 void |
|
191 MDefinition::analyzeEdgeCasesForward() |
|
192 { |
|
193 } |
|
194 |
|
195 void |
|
196 MDefinition::analyzeEdgeCasesBackward() |
|
197 { |
|
198 } |
|
199 |
|
200 static bool |
|
201 MaybeEmulatesUndefined(MDefinition *op) |
|
202 { |
|
203 if (!op->mightBeType(MIRType_Object)) |
|
204 return false; |
|
205 |
|
206 types::TemporaryTypeSet *types = op->resultTypeSet(); |
|
207 if (!types) |
|
208 return true; |
|
209 |
|
210 return types->maybeEmulatesUndefined(); |
|
211 } |
|
212 |
|
213 static bool |
|
214 MaybeCallable(MDefinition *op) |
|
215 { |
|
216 if (!op->mightBeType(MIRType_Object)) |
|
217 return false; |
|
218 |
|
219 types::TemporaryTypeSet *types = op->resultTypeSet(); |
|
220 if (!types) |
|
221 return true; |
|
222 |
|
223 return types->maybeCallable(); |
|
224 } |
|
225 |
|
226 MTest * |
|
227 MTest::New(TempAllocator &alloc, MDefinition *ins, MBasicBlock *ifTrue, MBasicBlock *ifFalse) |
|
228 { |
|
229 return new(alloc) MTest(ins, ifTrue, ifFalse); |
|
230 } |
|
231 |
|
232 void |
|
233 MTest::infer() |
|
234 { |
|
235 JS_ASSERT(operandMightEmulateUndefined()); |
|
236 |
|
237 if (!MaybeEmulatesUndefined(getOperand(0))) |
|
238 markOperandCantEmulateUndefined(); |
|
239 } |
|
240 |
|
241 MDefinition * |
|
242 MTest::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
243 { |
|
244 MDefinition *op = getOperand(0); |
|
245 |
|
246 if (op->isNot()) |
|
247 return MTest::New(alloc, op->toNot()->operand(), ifFalse(), ifTrue()); |
|
248 |
|
249 return this; |
|
250 } |
|
251 |
|
252 void |
|
253 MTest::filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined, |
|
254 bool *filtersNull) |
|
255 { |
|
256 MDefinition *ins = getOperand(0); |
|
257 if (ins->isCompare()) { |
|
258 ins->toCompare()->filtersUndefinedOrNull(trueBranch, subject, filtersUndefined, filtersNull); |
|
259 return; |
|
260 } |
|
261 |
|
262 if (!trueBranch && ins->isNot()) { |
|
263 *subject = ins->getOperand(0); |
|
264 *filtersUndefined = *filtersNull = true; |
|
265 return; |
|
266 } |
|
267 |
|
268 if (trueBranch) { |
|
269 *subject = ins; |
|
270 *filtersUndefined = *filtersNull = true; |
|
271 return; |
|
272 } |
|
273 |
|
274 *filtersUndefined = *filtersNull = false; |
|
275 *subject = nullptr; |
|
276 } |
|
277 |
|
278 void |
|
279 MDefinition::printOpcode(FILE *fp) const |
|
280 { |
|
281 PrintOpcodeName(fp, op()); |
|
282 for (size_t j = 0, e = numOperands(); j < e; j++) { |
|
283 fprintf(fp, " "); |
|
284 getOperand(j)->printName(fp); |
|
285 } |
|
286 } |
|
287 |
|
288 void |
|
289 MDefinition::dump(FILE *fp) const |
|
290 { |
|
291 printName(fp); |
|
292 fprintf(fp, " = "); |
|
293 printOpcode(fp); |
|
294 fprintf(fp, "\n"); |
|
295 } |
|
296 |
|
297 void |
|
298 MDefinition::dump() const |
|
299 { |
|
300 dump(stderr); |
|
301 } |
|
302 |
|
303 size_t |
|
304 MDefinition::useCount() const |
|
305 { |
|
306 size_t count = 0; |
|
307 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) |
|
308 count++; |
|
309 return count; |
|
310 } |
|
311 |
|
312 size_t |
|
313 MDefinition::defUseCount() const |
|
314 { |
|
315 size_t count = 0; |
|
316 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) |
|
317 if ((*i)->consumer()->isDefinition()) |
|
318 count++; |
|
319 return count; |
|
320 } |
|
321 |
|
322 bool |
|
323 MDefinition::hasOneUse() const |
|
324 { |
|
325 MUseIterator i(uses_.begin()); |
|
326 if (i == uses_.end()) |
|
327 return false; |
|
328 i++; |
|
329 return i == uses_.end(); |
|
330 } |
|
331 |
|
332 bool |
|
333 MDefinition::hasOneDefUse() const |
|
334 { |
|
335 bool hasOneDefUse = false; |
|
336 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) { |
|
337 if (!(*i)->consumer()->isDefinition()) |
|
338 continue; |
|
339 |
|
340 // We already have a definition use. So 1+ |
|
341 if (hasOneDefUse) |
|
342 return false; |
|
343 |
|
344 // We saw one definition. Loop to test if there is another. |
|
345 hasOneDefUse = true; |
|
346 } |
|
347 |
|
348 return hasOneDefUse; |
|
349 } |
|
350 |
|
351 bool |
|
352 MDefinition::hasDefUses() const |
|
353 { |
|
354 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) { |
|
355 if ((*i)->consumer()->isDefinition()) |
|
356 return true; |
|
357 } |
|
358 |
|
359 return false; |
|
360 } |
|
361 |
|
362 MUseIterator |
|
363 MDefinition::removeUse(MUseIterator use) |
|
364 { |
|
365 return uses_.removeAt(use); |
|
366 } |
|
367 |
|
368 MUseIterator |
|
369 MNode::replaceOperand(MUseIterator use, MDefinition *def) |
|
370 { |
|
371 JS_ASSERT(def != nullptr); |
|
372 uint32_t index = use->index(); |
|
373 MDefinition *prev = use->producer(); |
|
374 |
|
375 JS_ASSERT(use->index() < numOperands()); |
|
376 JS_ASSERT(use->producer() == getOperand(index)); |
|
377 JS_ASSERT(use->consumer() == this); |
|
378 |
|
379 if (prev == def) |
|
380 return use; |
|
381 |
|
382 MUseIterator result(prev->removeUse(use)); |
|
383 setOperand(index, def); |
|
384 return result; |
|
385 } |
|
386 |
|
387 void |
|
388 MNode::replaceOperand(size_t index, MDefinition *def) |
|
389 { |
|
390 JS_ASSERT(def != nullptr); |
|
391 MUse *use = getUseFor(index); |
|
392 MDefinition *prev = use->producer(); |
|
393 |
|
394 JS_ASSERT(use->index() == index); |
|
395 JS_ASSERT(use->index() < numOperands()); |
|
396 JS_ASSERT(use->producer() == getOperand(index)); |
|
397 JS_ASSERT(use->consumer() == this); |
|
398 |
|
399 if (prev == def) |
|
400 return; |
|
401 |
|
402 prev->removeUse(use); |
|
403 setOperand(index, def); |
|
404 } |
|
405 |
|
406 void |
|
407 MNode::discardOperand(size_t index) |
|
408 { |
|
409 MUse *use = getUseFor(index); |
|
410 |
|
411 JS_ASSERT(use->index() == index); |
|
412 JS_ASSERT(use->producer() == getOperand(index)); |
|
413 JS_ASSERT(use->consumer() == this); |
|
414 |
|
415 use->producer()->removeUse(use); |
|
416 |
|
417 #ifdef DEBUG |
|
418 // Causes any producer/consumer lookups to trip asserts. |
|
419 use->set(nullptr, nullptr, index); |
|
420 #endif |
|
421 } |
|
422 |
|
423 void |
|
424 MDefinition::replaceAllUsesWith(MDefinition *dom) |
|
425 { |
|
426 JS_ASSERT(dom != nullptr); |
|
427 if (dom == this) |
|
428 return; |
|
429 |
|
430 for (size_t i = 0, e = numOperands(); i < e; i++) |
|
431 getOperand(i)->setUseRemovedUnchecked(); |
|
432 |
|
433 for (MUseIterator i(usesBegin()); i != usesEnd(); ) { |
|
434 JS_ASSERT(i->producer() == this); |
|
435 i = i->consumer()->replaceOperand(i, dom); |
|
436 } |
|
437 } |
|
438 |
|
439 bool |
|
440 MDefinition::emptyResultTypeSet() const |
|
441 { |
|
442 return resultTypeSet() && resultTypeSet()->empty(); |
|
443 } |
|
444 |
|
445 MConstant * |
|
446 MConstant::New(TempAllocator &alloc, const Value &v, types::CompilerConstraintList *constraints) |
|
447 { |
|
448 return new(alloc) MConstant(v, constraints); |
|
449 } |
|
450 |
|
451 MConstant * |
|
452 MConstant::NewAsmJS(TempAllocator &alloc, const Value &v, MIRType type) |
|
453 { |
|
454 MConstant *constant = new(alloc) MConstant(v, nullptr); |
|
455 constant->setResultType(type); |
|
456 return constant; |
|
457 } |
|
458 |
|
459 types::TemporaryTypeSet * |
|
460 jit::MakeSingletonTypeSet(types::CompilerConstraintList *constraints, JSObject *obj) |
|
461 { |
|
462 // Invalidate when this object's TypeObject gets unknown properties. This |
|
463 // happens for instance when we mutate an object's __proto__, in this case |
|
464 // we want to invalidate and mark this TypeSet as containing AnyObject |
|
465 // (because mutating __proto__ will change an object's TypeObject). |
|
466 JS_ASSERT(constraints); |
|
467 types::TypeObjectKey *objType = types::TypeObjectKey::get(obj); |
|
468 objType->hasFlags(constraints, types::OBJECT_FLAG_UNKNOWN_PROPERTIES); |
|
469 |
|
470 return GetIonContext()->temp->lifoAlloc()->new_<types::TemporaryTypeSet>(types::Type::ObjectType(obj)); |
|
471 } |
|
472 |
|
473 MConstant::MConstant(const js::Value &vp, types::CompilerConstraintList *constraints) |
|
474 : value_(vp) |
|
475 { |
|
476 setResultType(MIRTypeFromValue(vp)); |
|
477 if (vp.isObject()) { |
|
478 // Create a singleton type set for the object. This isn't necessary for |
|
479 // other types as the result type encodes all needed information. |
|
480 setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject())); |
|
481 } |
|
482 |
|
483 setMovable(); |
|
484 } |
|
485 |
|
486 HashNumber |
|
487 MConstant::valueHash() const |
|
488 { |
|
489 // This disregards some state, since values are 64 bits. But for a hash, |
|
490 // it's completely acceptable. |
|
491 return (HashNumber)JSVAL_TO_IMPL(value_).asBits; |
|
492 } |
|
493 bool |
|
494 MConstant::congruentTo(const MDefinition *ins) const |
|
495 { |
|
496 if (!ins->isConstant()) |
|
497 return false; |
|
498 return ins->toConstant()->value() == value(); |
|
499 } |
|
500 |
|
501 void |
|
502 MConstant::printOpcode(FILE *fp) const |
|
503 { |
|
504 PrintOpcodeName(fp, op()); |
|
505 fprintf(fp, " "); |
|
506 switch (type()) { |
|
507 case MIRType_Undefined: |
|
508 fprintf(fp, "undefined"); |
|
509 break; |
|
510 case MIRType_Null: |
|
511 fprintf(fp, "null"); |
|
512 break; |
|
513 case MIRType_Boolean: |
|
514 fprintf(fp, value().toBoolean() ? "true" : "false"); |
|
515 break; |
|
516 case MIRType_Int32: |
|
517 fprintf(fp, "0x%x", value().toInt32()); |
|
518 break; |
|
519 case MIRType_Double: |
|
520 fprintf(fp, "%f", value().toDouble()); |
|
521 break; |
|
522 case MIRType_Float32: |
|
523 { |
|
524 float val = value().toDouble(); |
|
525 fprintf(fp, "%f", val); |
|
526 break; |
|
527 } |
|
528 case MIRType_Object: |
|
529 if (value().toObject().is<JSFunction>()) { |
|
530 JSFunction *fun = &value().toObject().as<JSFunction>(); |
|
531 if (fun->displayAtom()) { |
|
532 fputs("function ", fp); |
|
533 FileEscapedString(fp, fun->displayAtom(), 0); |
|
534 } else { |
|
535 fputs("unnamed function", fp); |
|
536 } |
|
537 if (fun->hasScript()) { |
|
538 JSScript *script = fun->nonLazyScript(); |
|
539 fprintf(fp, " (%s:%d)", |
|
540 script->filename() ? script->filename() : "", (int) script->lineno()); |
|
541 } |
|
542 fprintf(fp, " at %p", (void *) fun); |
|
543 break; |
|
544 } |
|
545 fprintf(fp, "object %p (%s)", (void *)&value().toObject(), |
|
546 value().toObject().getClass()->name); |
|
547 break; |
|
548 case MIRType_String: |
|
549 fprintf(fp, "string %p", (void *)value().toString()); |
|
550 break; |
|
551 case MIRType_MagicOptimizedArguments: |
|
552 fprintf(fp, "magic lazyargs"); |
|
553 break; |
|
554 case MIRType_MagicHole: |
|
555 fprintf(fp, "magic hole"); |
|
556 break; |
|
557 case MIRType_MagicIsConstructing: |
|
558 fprintf(fp, "magic is-constructing"); |
|
559 break; |
|
560 case MIRType_MagicOptimizedOut: |
|
561 fprintf(fp, "magic optimized-out"); |
|
562 break; |
|
563 default: |
|
564 MOZ_ASSUME_UNREACHABLE("unexpected type"); |
|
565 } |
|
566 } |
|
567 |
|
568 bool |
|
569 MConstant::canProduceFloat32() const |
|
570 { |
|
571 if (!IsNumberType(type())) |
|
572 return false; |
|
573 |
|
574 if (type() == MIRType_Int32) |
|
575 return IsFloat32Representable(static_cast<double>(value_.toInt32())); |
|
576 if (type() == MIRType_Double) |
|
577 return IsFloat32Representable(value_.toDouble()); |
|
578 return true; |
|
579 } |
|
580 |
|
581 MCloneLiteral * |
|
582 MCloneLiteral::New(TempAllocator &alloc, MDefinition *obj) |
|
583 { |
|
584 return new(alloc) MCloneLiteral(obj); |
|
585 } |
|
586 |
|
587 void |
|
588 MControlInstruction::printOpcode(FILE *fp) const |
|
589 { |
|
590 MDefinition::printOpcode(fp); |
|
591 for (size_t j = 0; j < numSuccessors(); j++) |
|
592 fprintf(fp, " block%d", getSuccessor(j)->id()); |
|
593 } |
|
594 |
|
595 void |
|
596 MCompare::printOpcode(FILE *fp) const |
|
597 { |
|
598 MDefinition::printOpcode(fp); |
|
599 fprintf(fp, " %s", js_CodeName[jsop()]); |
|
600 } |
|
601 |
|
602 void |
|
603 MConstantElements::printOpcode(FILE *fp) const |
|
604 { |
|
605 PrintOpcodeName(fp, op()); |
|
606 fprintf(fp, " %p", value()); |
|
607 } |
|
608 |
|
609 void |
|
610 MLoadTypedArrayElement::printOpcode(FILE *fp) const |
|
611 { |
|
612 MDefinition::printOpcode(fp); |
|
613 fprintf(fp, " %s", ScalarTypeDescr::typeName(arrayType())); |
|
614 } |
|
615 |
|
616 void |
|
617 MAssertRange::printOpcode(FILE *fp) const |
|
618 { |
|
619 MDefinition::printOpcode(fp); |
|
620 Sprinter sp(GetIonContext()->cx); |
|
621 sp.init(); |
|
622 assertedRange()->print(sp); |
|
623 fprintf(fp, " %s", sp.string()); |
|
624 } |
|
625 |
|
626 const char * |
|
627 MMathFunction::FunctionName(Function function) |
|
628 { |
|
629 switch (function) { |
|
630 case Log: return "Log"; |
|
631 case Sin: return "Sin"; |
|
632 case Cos: return "Cos"; |
|
633 case Exp: return "Exp"; |
|
634 case Tan: return "Tan"; |
|
635 case ACos: return "ACos"; |
|
636 case ASin: return "ASin"; |
|
637 case ATan: return "ATan"; |
|
638 case Log10: return "Log10"; |
|
639 case Log2: return "Log2"; |
|
640 case Log1P: return "Log1P"; |
|
641 case ExpM1: return "ExpM1"; |
|
642 case CosH: return "CosH"; |
|
643 case SinH: return "SinH"; |
|
644 case TanH: return "TanH"; |
|
645 case ACosH: return "ACosH"; |
|
646 case ASinH: return "ASinH"; |
|
647 case ATanH: return "ATanH"; |
|
648 case Sign: return "Sign"; |
|
649 case Trunc: return "Trunc"; |
|
650 case Cbrt: return "Cbrt"; |
|
651 case Floor: return "Floor"; |
|
652 case Ceil: return "Ceil"; |
|
653 case Round: return "Round"; |
|
654 default: |
|
655 MOZ_ASSUME_UNREACHABLE("Unknown math function"); |
|
656 } |
|
657 } |
|
658 |
|
659 void |
|
660 MMathFunction::printOpcode(FILE *fp) const |
|
661 { |
|
662 MDefinition::printOpcode(fp); |
|
663 fprintf(fp, " %s", FunctionName(function())); |
|
664 } |
|
665 |
|
666 MParameter * |
|
667 MParameter::New(TempAllocator &alloc, int32_t index, types::TemporaryTypeSet *types) |
|
668 { |
|
669 return new(alloc) MParameter(index, types); |
|
670 } |
|
671 |
|
672 void |
|
673 MParameter::printOpcode(FILE *fp) const |
|
674 { |
|
675 PrintOpcodeName(fp, op()); |
|
676 fprintf(fp, " %d", index()); |
|
677 } |
|
678 |
|
679 HashNumber |
|
680 MParameter::valueHash() const |
|
681 { |
|
682 return index_; // Why not? |
|
683 } |
|
684 |
|
685 bool |
|
686 MParameter::congruentTo(const MDefinition *ins) const |
|
687 { |
|
688 if (!ins->isParameter()) |
|
689 return false; |
|
690 |
|
691 return ins->toParameter()->index() == index_; |
|
692 } |
|
693 |
|
694 MCall * |
|
695 MCall::New(TempAllocator &alloc, JSFunction *target, size_t maxArgc, size_t numActualArgs, |
|
696 bool construct, bool isDOMCall) |
|
697 { |
|
698 JS_ASSERT(maxArgc >= numActualArgs); |
|
699 MCall *ins; |
|
700 if (isDOMCall) { |
|
701 JS_ASSERT(!construct); |
|
702 ins = new(alloc) MCallDOMNative(target, numActualArgs); |
|
703 } else { |
|
704 ins = new(alloc) MCall(target, numActualArgs, construct); |
|
705 } |
|
706 if (!ins->init(alloc, maxArgc + NumNonArgumentOperands)) |
|
707 return nullptr; |
|
708 return ins; |
|
709 } |
|
710 |
|
711 AliasSet |
|
712 MCallDOMNative::getAliasSet() const |
|
713 { |
|
714 const JSJitInfo *jitInfo = getJitInfo(); |
|
715 |
|
716 JS_ASSERT(jitInfo->aliasSet() != JSJitInfo::AliasNone); |
|
717 // If we don't know anything about the types of our arguments, we have to |
|
718 // assume that type-coercions can have side-effects, so we need to alias |
|
719 // everything. |
|
720 if (jitInfo->aliasSet() != JSJitInfo::AliasDOMSets || !jitInfo->isTypedMethodJitInfo()) |
|
721 return AliasSet::Store(AliasSet::Any); |
|
722 |
|
723 uint32_t argIndex = 0; |
|
724 const JSTypedMethodJitInfo *methodInfo = |
|
725 reinterpret_cast<const JSTypedMethodJitInfo*>(jitInfo); |
|
726 for (const JSJitInfo::ArgType *argType = methodInfo->argTypes; |
|
727 *argType != JSJitInfo::ArgTypeListEnd; |
|
728 ++argType, ++argIndex) |
|
729 { |
|
730 if (argIndex >= numActualArgs()) { |
|
731 // Passing through undefined can't have side-effects |
|
732 continue; |
|
733 } |
|
734 // getArg(0) is "this", so skip it |
|
735 MDefinition *arg = getArg(argIndex+1); |
|
736 MIRType actualType = arg->type(); |
|
737 // The only way to reliably avoid side-effects given the informtion we |
|
738 // have here is if we're passing in a known primitive value to an |
|
739 // argument that expects a primitive value. XXXbz maybe we need to |
|
740 // communicate better information. For example, a sequence argument |
|
741 // will sort of unavoidably have side effects, while a typed array |
|
742 // argument won't have any, but both are claimed to be |
|
743 // JSJitInfo::Object. |
|
744 if ((actualType == MIRType_Value || actualType == MIRType_Object) || |
|
745 (*argType & JSJitInfo::Object)) |
|
746 { |
|
747 return AliasSet::Store(AliasSet::Any); |
|
748 } |
|
749 } |
|
750 |
|
751 // We checked all the args, and they check out. So we only |
|
752 // alias DOM mutations. |
|
753 return AliasSet::Load(AliasSet::DOMProperty); |
|
754 } |
|
755 |
|
756 void |
|
757 MCallDOMNative::computeMovable() |
|
758 { |
|
759 // We are movable if the jitinfo says we can be and if we're also not |
|
760 // effectful. The jitinfo can't check for the latter, since it depends on |
|
761 // the types of our arguments. |
|
762 const JSJitInfo *jitInfo = getJitInfo(); |
|
763 |
|
764 JS_ASSERT_IF(jitInfo->isMovable, |
|
765 jitInfo->aliasSet() != JSJitInfo::AliasEverything); |
|
766 |
|
767 if (jitInfo->isMovable && !isEffectful()) |
|
768 setMovable(); |
|
769 } |
|
770 |
|
771 bool |
|
772 MCallDOMNative::congruentTo(const MDefinition *ins) const |
|
773 { |
|
774 if (!isMovable()) |
|
775 return false; |
|
776 |
|
777 if (!ins->isCall()) |
|
778 return false; |
|
779 |
|
780 const MCall *call = ins->toCall(); |
|
781 |
|
782 if (!call->isCallDOMNative()) |
|
783 return false; |
|
784 |
|
785 if (getSingleTarget() != call->getSingleTarget()) |
|
786 return false; |
|
787 |
|
788 if (isConstructing() != call->isConstructing()) |
|
789 return false; |
|
790 |
|
791 if (numActualArgs() != call->numActualArgs()) |
|
792 return false; |
|
793 |
|
794 if (needsArgCheck() != call->needsArgCheck()) |
|
795 return false; |
|
796 |
|
797 if (!congruentIfOperandsEqual(call)) |
|
798 return false; |
|
799 |
|
800 // The other call had better be movable at this point! |
|
801 JS_ASSERT(call->isMovable()); |
|
802 |
|
803 return true; |
|
804 } |
|
805 |
|
806 const JSJitInfo * |
|
807 MCallDOMNative::getJitInfo() const |
|
808 { |
|
809 JS_ASSERT(getSingleTarget() && getSingleTarget()->isNative()); |
|
810 |
|
811 const JSJitInfo *jitInfo = getSingleTarget()->jitInfo(); |
|
812 JS_ASSERT(jitInfo); |
|
813 |
|
814 return jitInfo; |
|
815 } |
|
816 |
|
817 MApplyArgs * |
|
818 MApplyArgs::New(TempAllocator &alloc, JSFunction *target, MDefinition *fun, MDefinition *argc, |
|
819 MDefinition *self) |
|
820 { |
|
821 return new(alloc) MApplyArgs(target, fun, argc, self); |
|
822 } |
|
823 |
|
824 MDefinition* |
|
825 MStringLength::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
826 { |
|
827 if ((type() == MIRType_Int32) && (string()->isConstant())) { |
|
828 Value value = string()->toConstant()->value(); |
|
829 JSAtom *atom = &value.toString()->asAtom(); |
|
830 return MConstant::New(alloc, Int32Value(atom->length())); |
|
831 } |
|
832 |
|
833 return this; |
|
834 } |
|
835 |
|
836 void |
|
837 MFloor::trySpecializeFloat32(TempAllocator &alloc) |
|
838 { |
|
839 // No need to look at the output, as it's an integer (see IonBuilder::inlineMathFloor) |
|
840 if (!input()->canProduceFloat32()) { |
|
841 if (input()->type() == MIRType_Float32) |
|
842 ConvertDefinitionToDouble<0>(alloc, input(), this); |
|
843 return; |
|
844 } |
|
845 |
|
846 if (type() == MIRType_Double) |
|
847 setResultType(MIRType_Float32); |
|
848 |
|
849 setPolicyType(MIRType_Float32); |
|
850 } |
|
851 |
|
852 void |
|
853 MRound::trySpecializeFloat32(TempAllocator &alloc) |
|
854 { |
|
855 // No need to look at the output, as it's an integer (unique way to have |
|
856 // this instruction in IonBuilder::inlineMathRound) |
|
857 JS_ASSERT(type() == MIRType_Int32); |
|
858 |
|
859 if (!input()->canProduceFloat32()) { |
|
860 if (input()->type() == MIRType_Float32) |
|
861 ConvertDefinitionToDouble<0>(alloc, input(), this); |
|
862 return; |
|
863 } |
|
864 |
|
865 setPolicyType(MIRType_Float32); |
|
866 } |
|
867 |
|
868 MCompare * |
|
869 MCompare::New(TempAllocator &alloc, MDefinition *left, MDefinition *right, JSOp op) |
|
870 { |
|
871 return new(alloc) MCompare(left, right, op); |
|
872 } |
|
873 |
|
874 MCompare * |
|
875 MCompare::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, JSOp op, |
|
876 CompareType compareType) |
|
877 { |
|
878 JS_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 || |
|
879 compareType == Compare_Double || compareType == Compare_Float32); |
|
880 MCompare *comp = new(alloc) MCompare(left, right, op); |
|
881 comp->compareType_ = compareType; |
|
882 comp->operandMightEmulateUndefined_ = false; |
|
883 comp->setResultType(MIRType_Int32); |
|
884 return comp; |
|
885 } |
|
886 |
|
887 MTableSwitch * |
|
888 MTableSwitch::New(TempAllocator &alloc, MDefinition *ins, int32_t low, int32_t high) |
|
889 { |
|
890 return new(alloc) MTableSwitch(alloc, ins, low, high); |
|
891 } |
|
892 |
|
893 MGoto * |
|
894 MGoto::New(TempAllocator &alloc, MBasicBlock *target) |
|
895 { |
|
896 JS_ASSERT(target); |
|
897 return new(alloc) MGoto(target); |
|
898 } |
|
899 |
|
900 void |
|
901 MUnbox::printOpcode(FILE *fp) const |
|
902 { |
|
903 PrintOpcodeName(fp, op()); |
|
904 fprintf(fp, " "); |
|
905 getOperand(0)->printName(fp); |
|
906 fprintf(fp, " "); |
|
907 |
|
908 switch (type()) { |
|
909 case MIRType_Int32: fprintf(fp, "to Int32"); break; |
|
910 case MIRType_Double: fprintf(fp, "to Double"); break; |
|
911 case MIRType_Boolean: fprintf(fp, "to Boolean"); break; |
|
912 case MIRType_String: fprintf(fp, "to String"); break; |
|
913 case MIRType_Object: fprintf(fp, "to Object"); break; |
|
914 default: break; |
|
915 } |
|
916 |
|
917 switch (mode()) { |
|
918 case Fallible: fprintf(fp, " (fallible)"); break; |
|
919 case Infallible: fprintf(fp, " (infallible)"); break; |
|
920 case TypeBarrier: fprintf(fp, " (typebarrier)"); break; |
|
921 default: break; |
|
922 } |
|
923 } |
|
924 |
|
925 void |
|
926 MTypeBarrier::printOpcode(FILE *fp) const |
|
927 { |
|
928 PrintOpcodeName(fp, op()); |
|
929 fprintf(fp, " "); |
|
930 getOperand(0)->printName(fp); |
|
931 } |
|
932 |
|
933 void |
|
934 MPhi::removeOperand(size_t index) |
|
935 { |
|
936 MUse *use = getUseFor(index); |
|
937 |
|
938 JS_ASSERT(index < inputs_.length()); |
|
939 JS_ASSERT(inputs_.length() > 1); |
|
940 |
|
941 JS_ASSERT(use->index() == index); |
|
942 JS_ASSERT(use->producer() == getOperand(index)); |
|
943 JS_ASSERT(use->consumer() == this); |
|
944 |
|
945 // Remove use from producer's use chain. |
|
946 use->producer()->removeUse(use); |
|
947 |
|
948 // If we have phi(..., a, b, c, d, ..., z) and we plan |
|
949 // on removing a, then first shift downward so that we have |
|
950 // phi(..., b, c, d, ..., z, z): |
|
951 size_t length = inputs_.length(); |
|
952 for (size_t i = index; i < length - 1; i++) { |
|
953 MUse *next = MPhi::getUseFor(i + 1); |
|
954 next->producer()->removeUse(next); |
|
955 MPhi::setOperand(i, next->producer()); |
|
956 } |
|
957 |
|
958 // truncate the inputs_ list: |
|
959 inputs_.shrinkBy(1); |
|
960 } |
|
961 |
|
962 MDefinition * |
|
963 MPhi::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
964 { |
|
965 JS_ASSERT(!inputs_.empty()); |
|
966 |
|
967 MDefinition *first = getOperand(0); |
|
968 |
|
969 for (size_t i = 1; i < inputs_.length(); i++) { |
|
970 // Phis need dominator information to fold based on value numbers. For |
|
971 // simplicity, we only compare SSA names right now (bug 714727). |
|
972 if (!EqualValues(false, getOperand(i), first)) |
|
973 return this; |
|
974 } |
|
975 |
|
976 return first; |
|
977 } |
|
978 |
|
979 bool |
|
980 MPhi::congruentTo(const MDefinition *ins) const |
|
981 { |
|
982 if (!ins->isPhi()) |
|
983 return false; |
|
984 // Since we do not know which predecessor we are merging from, we must |
|
985 // assume that phi instructions in different blocks are not equal. |
|
986 // (Bug 674656) |
|
987 if (ins->block()->id() != block()->id()) |
|
988 return false; |
|
989 |
|
990 return congruentIfOperandsEqual(ins); |
|
991 } |
|
992 |
|
993 bool |
|
994 MPhi::reserveLength(size_t length) |
|
995 { |
|
996 // Initializes a new MPhi to have an Operand vector of at least the given |
|
997 // capacity. This permits use of addInput() instead of addInputSlow(), the |
|
998 // latter of which may call realloc_(). |
|
999 JS_ASSERT(numOperands() == 0); |
|
1000 #if DEBUG |
|
1001 capacity_ = length; |
|
1002 #endif |
|
1003 return inputs_.reserve(length); |
|
1004 } |
|
1005 |
|
1006 static inline types::TemporaryTypeSet * |
|
1007 MakeMIRTypeSet(MIRType type) |
|
1008 { |
|
1009 JS_ASSERT(type != MIRType_Value); |
|
1010 types::Type ntype = type == MIRType_Object |
|
1011 ? types::Type::AnyObjectType() |
|
1012 : types::Type::PrimitiveType(ValueTypeFromMIRType(type)); |
|
1013 return GetIonContext()->temp->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype); |
|
1014 } |
|
1015 |
|
1016 bool |
|
1017 jit::MergeTypes(MIRType *ptype, types::TemporaryTypeSet **ptypeSet, |
|
1018 MIRType newType, types::TemporaryTypeSet *newTypeSet) |
|
1019 { |
|
1020 if (newTypeSet && newTypeSet->empty()) |
|
1021 return true; |
|
1022 if (newType != *ptype) { |
|
1023 if (IsNumberType(newType) && IsNumberType(*ptype)) { |
|
1024 *ptype = MIRType_Double; |
|
1025 } else if (*ptype != MIRType_Value) { |
|
1026 if (!*ptypeSet) { |
|
1027 *ptypeSet = MakeMIRTypeSet(*ptype); |
|
1028 if (!*ptypeSet) |
|
1029 return false; |
|
1030 } |
|
1031 *ptype = MIRType_Value; |
|
1032 } else if (*ptypeSet && (*ptypeSet)->empty()) { |
|
1033 *ptype = newType; |
|
1034 } |
|
1035 } |
|
1036 if (*ptypeSet) { |
|
1037 LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc(); |
|
1038 if (!newTypeSet && newType != MIRType_Value) { |
|
1039 newTypeSet = MakeMIRTypeSet(newType); |
|
1040 if (!newTypeSet) |
|
1041 return false; |
|
1042 } |
|
1043 if (newTypeSet) { |
|
1044 if (!newTypeSet->isSubset(*ptypeSet)) |
|
1045 *ptypeSet = types::TypeSet::unionSets(*ptypeSet, newTypeSet, alloc); |
|
1046 } else { |
|
1047 *ptypeSet = nullptr; |
|
1048 } |
|
1049 } |
|
1050 return true; |
|
1051 } |
|
1052 |
|
1053 bool |
|
1054 MPhi::specializeType() |
|
1055 { |
|
1056 #ifdef DEBUG |
|
1057 JS_ASSERT(!specialized_); |
|
1058 specialized_ = true; |
|
1059 #endif |
|
1060 |
|
1061 JS_ASSERT(!inputs_.empty()); |
|
1062 |
|
1063 size_t start; |
|
1064 if (hasBackedgeType_) { |
|
1065 // The type of this phi has already been populated with potential types |
|
1066 // that could come in via loop backedges. |
|
1067 start = 0; |
|
1068 } else { |
|
1069 setResultType(getOperand(0)->type()); |
|
1070 setResultTypeSet(getOperand(0)->resultTypeSet()); |
|
1071 start = 1; |
|
1072 } |
|
1073 |
|
1074 MIRType resultType = this->type(); |
|
1075 types::TemporaryTypeSet *resultTypeSet = this->resultTypeSet(); |
|
1076 |
|
1077 for (size_t i = start; i < inputs_.length(); i++) { |
|
1078 MDefinition *def = getOperand(i); |
|
1079 if (!MergeTypes(&resultType, &resultTypeSet, def->type(), def->resultTypeSet())) |
|
1080 return false; |
|
1081 } |
|
1082 |
|
1083 setResultType(resultType); |
|
1084 setResultTypeSet(resultTypeSet); |
|
1085 return true; |
|
1086 } |
|
1087 |
|
1088 bool |
|
1089 MPhi::addBackedgeType(MIRType type, types::TemporaryTypeSet *typeSet) |
|
1090 { |
|
1091 JS_ASSERT(!specialized_); |
|
1092 |
|
1093 if (hasBackedgeType_) { |
|
1094 MIRType resultType = this->type(); |
|
1095 types::TemporaryTypeSet *resultTypeSet = this->resultTypeSet(); |
|
1096 |
|
1097 if (!MergeTypes(&resultType, &resultTypeSet, type, typeSet)) |
|
1098 return false; |
|
1099 |
|
1100 setResultType(resultType); |
|
1101 setResultTypeSet(resultTypeSet); |
|
1102 } else { |
|
1103 setResultType(type); |
|
1104 setResultTypeSet(typeSet); |
|
1105 hasBackedgeType_ = true; |
|
1106 } |
|
1107 return true; |
|
1108 } |
|
1109 |
|
1110 bool |
|
1111 MPhi::typeIncludes(MDefinition *def) |
|
1112 { |
|
1113 if (def->type() == MIRType_Int32 && this->type() == MIRType_Double) |
|
1114 return true; |
|
1115 |
|
1116 if (types::TemporaryTypeSet *types = def->resultTypeSet()) { |
|
1117 if (this->resultTypeSet()) |
|
1118 return types->isSubset(this->resultTypeSet()); |
|
1119 if (this->type() == MIRType_Value || types->empty()) |
|
1120 return true; |
|
1121 return this->type() == types->getKnownMIRType(); |
|
1122 } |
|
1123 |
|
1124 if (def->type() == MIRType_Value) { |
|
1125 // This phi must be able to be any value. |
|
1126 return this->type() == MIRType_Value |
|
1127 && (!this->resultTypeSet() || this->resultTypeSet()->unknown()); |
|
1128 } |
|
1129 |
|
1130 return this->mightBeType(def->type()); |
|
1131 } |
|
1132 |
|
1133 void |
|
1134 MPhi::addInput(MDefinition *ins) |
|
1135 { |
|
1136 // This can only been done if the length was reserved through reserveLength, |
|
1137 // else the slower addInputSlow need to get called. |
|
1138 JS_ASSERT(inputs_.length() < capacity_); |
|
1139 |
|
1140 uint32_t index = inputs_.length(); |
|
1141 inputs_.append(MUse()); |
|
1142 MPhi::setOperand(index, ins); |
|
1143 } |
|
1144 |
|
1145 bool |
|
1146 MPhi::addInputSlow(MDefinition *ins, bool *ptypeChange) |
|
1147 { |
|
1148 // The list of inputs to an MPhi is given as a vector of MUse nodes, |
|
1149 // each of which is in the list of the producer MDefinition. |
|
1150 // Because appending to a vector may reallocate the vector, it is possible |
|
1151 // that this operation may cause the producers' linked lists to reference |
|
1152 // invalid memory. Therefore, in the event of moving reallocation, each |
|
1153 // MUse must be removed and reinserted from/into its producer's use chain. |
|
1154 uint32_t index = inputs_.length(); |
|
1155 bool performingRealloc = !inputs_.canAppendWithoutRealloc(1); |
|
1156 |
|
1157 // Remove all MUses from all use lists, in case realloc_() moves. |
|
1158 if (performingRealloc) { |
|
1159 for (uint32_t i = 0; i < index; i++) { |
|
1160 MUse *use = &inputs_[i]; |
|
1161 use->producer()->removeUse(use); |
|
1162 } |
|
1163 } |
|
1164 |
|
1165 // Insert the new input. |
|
1166 if (!inputs_.append(MUse())) |
|
1167 return false; |
|
1168 |
|
1169 MPhi::setOperand(index, ins); |
|
1170 |
|
1171 if (ptypeChange) { |
|
1172 MIRType resultType = this->type(); |
|
1173 types::TemporaryTypeSet *resultTypeSet = this->resultTypeSet(); |
|
1174 |
|
1175 if (!MergeTypes(&resultType, &resultTypeSet, ins->type(), ins->resultTypeSet())) |
|
1176 return false; |
|
1177 |
|
1178 if (resultType != this->type() || resultTypeSet != this->resultTypeSet()) { |
|
1179 *ptypeChange = true; |
|
1180 setResultType(resultType); |
|
1181 setResultTypeSet(resultTypeSet); |
|
1182 } |
|
1183 } |
|
1184 |
|
1185 // Add all previously-removed MUses back. |
|
1186 if (performingRealloc) { |
|
1187 for (uint32_t i = 0; i < index; i++) { |
|
1188 MUse *use = &inputs_[i]; |
|
1189 use->producer()->addUse(use); |
|
1190 } |
|
1191 } |
|
1192 |
|
1193 return true; |
|
1194 } |
|
1195 |
|
1196 void |
|
1197 MCall::addArg(size_t argnum, MDefinition *arg) |
|
1198 { |
|
1199 // The operand vector is initialized in reverse order by the IonBuilder. |
|
1200 // It cannot be checked for consistency until all arguments are added. |
|
1201 setOperand(argnum + NumNonArgumentOperands, arg); |
|
1202 } |
|
1203 |
|
1204 void |
|
1205 MBitNot::infer() |
|
1206 { |
|
1207 if (getOperand(0)->mightBeType(MIRType_Object)) |
|
1208 specialization_ = MIRType_None; |
|
1209 else |
|
1210 specialization_ = MIRType_Int32; |
|
1211 } |
|
1212 |
|
1213 static inline bool |
|
1214 IsConstant(MDefinition *def, double v) |
|
1215 { |
|
1216 if (!def->isConstant()) |
|
1217 return false; |
|
1218 |
|
1219 return NumbersAreIdentical(def->toConstant()->value().toNumber(), v); |
|
1220 } |
|
1221 |
|
1222 MDefinition * |
|
1223 MBinaryBitwiseInstruction::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
1224 { |
|
1225 if (specialization_ != MIRType_Int32) |
|
1226 return this; |
|
1227 |
|
1228 if (MDefinition *folded = EvaluateConstantOperands(alloc, this)) |
|
1229 return folded; |
|
1230 |
|
1231 return this; |
|
1232 } |
|
1233 |
|
1234 MDefinition * |
|
1235 MBinaryBitwiseInstruction::foldUnnecessaryBitop() |
|
1236 { |
|
1237 if (specialization_ != MIRType_Int32) |
|
1238 return this; |
|
1239 |
|
1240 // Eliminate bitwise operations that are no-ops when used on integer |
|
1241 // inputs, such as (x | 0). |
|
1242 |
|
1243 MDefinition *lhs = getOperand(0); |
|
1244 MDefinition *rhs = getOperand(1); |
|
1245 |
|
1246 if (IsConstant(lhs, 0)) |
|
1247 return foldIfZero(0); |
|
1248 |
|
1249 if (IsConstant(rhs, 0)) |
|
1250 return foldIfZero(1); |
|
1251 |
|
1252 if (IsConstant(lhs, -1)) |
|
1253 return foldIfNegOne(0); |
|
1254 |
|
1255 if (IsConstant(rhs, -1)) |
|
1256 return foldIfNegOne(1); |
|
1257 |
|
1258 if (EqualValues(false, lhs, rhs)) |
|
1259 return foldIfEqual(); |
|
1260 |
|
1261 return this; |
|
1262 } |
|
1263 |
|
1264 void |
|
1265 MBinaryBitwiseInstruction::infer(BaselineInspector *, jsbytecode *) |
|
1266 { |
|
1267 if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) |
|
1268 specialization_ = MIRType_None; |
|
1269 else |
|
1270 specializeAsInt32(); |
|
1271 } |
|
1272 |
|
1273 void |
|
1274 MBinaryBitwiseInstruction::specializeAsInt32() |
|
1275 { |
|
1276 specialization_ = MIRType_Int32; |
|
1277 JS_ASSERT(type() == MIRType_Int32); |
|
1278 |
|
1279 if (isBitOr() || isBitAnd() || isBitXor()) |
|
1280 setCommutative(); |
|
1281 } |
|
1282 |
|
1283 void |
|
1284 MShiftInstruction::infer(BaselineInspector *, jsbytecode *) |
|
1285 { |
|
1286 if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) |
|
1287 specialization_ = MIRType_None; |
|
1288 else |
|
1289 specialization_ = MIRType_Int32; |
|
1290 } |
|
1291 |
|
1292 void |
|
1293 MUrsh::infer(BaselineInspector *inspector, jsbytecode *pc) |
|
1294 { |
|
1295 if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) { |
|
1296 specialization_ = MIRType_None; |
|
1297 setResultType(MIRType_Value); |
|
1298 return; |
|
1299 } |
|
1300 |
|
1301 if (inspector->hasSeenDoubleResult(pc)) { |
|
1302 specialization_ = MIRType_Double; |
|
1303 setResultType(MIRType_Double); |
|
1304 return; |
|
1305 } |
|
1306 |
|
1307 specialization_ = MIRType_Int32; |
|
1308 setResultType(MIRType_Int32); |
|
1309 } |
|
1310 |
|
1311 static inline bool |
|
1312 NeedNegativeZeroCheck(MDefinition *def) |
|
1313 { |
|
1314 // Test if all uses have the same semantics for -0 and 0 |
|
1315 for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) { |
|
1316 if (use->consumer()->isResumePoint()) |
|
1317 continue; |
|
1318 |
|
1319 MDefinition *use_def = use->consumer()->toDefinition(); |
|
1320 switch (use_def->op()) { |
|
1321 case MDefinition::Op_Add: { |
|
1322 // If add is truncating -0 and 0 are observed as the same. |
|
1323 if (use_def->toAdd()->isTruncated()) |
|
1324 break; |
|
1325 |
|
1326 // x + y gives -0, when both x and y are -0 |
|
1327 |
|
1328 // Figure out the order in which the addition's operands will |
|
1329 // execute. EdgeCaseAnalysis::analyzeLate has renumbered the MIR |
|
1330 // definitions for us so that this just requires comparing ids. |
|
1331 MDefinition *first = use_def->toAdd()->getOperand(0); |
|
1332 MDefinition *second = use_def->toAdd()->getOperand(1); |
|
1333 if (first->id() > second->id()) { |
|
1334 MDefinition *temp = first; |
|
1335 first = second; |
|
1336 second = temp; |
|
1337 } |
|
1338 |
|
1339 if (def == first) { |
|
1340 // Negative zero checks can be removed on the first executed |
|
1341 // operand only if it is guaranteed the second executed operand |
|
1342 // will produce a value other than -0. While the second is |
|
1343 // typed as an int32, a bailout taken between execution of the |
|
1344 // operands may change that type and cause a -0 to flow to the |
|
1345 // second. |
|
1346 // |
|
1347 // There is no way to test whether there are any bailouts |
|
1348 // between execution of the operands, so remove negative |
|
1349 // zero checks from the first only if the second's type is |
|
1350 // independent from type changes that may occur after bailing. |
|
1351 switch (second->op()) { |
|
1352 case MDefinition::Op_Constant: |
|
1353 case MDefinition::Op_BitAnd: |
|
1354 case MDefinition::Op_BitOr: |
|
1355 case MDefinition::Op_BitXor: |
|
1356 case MDefinition::Op_BitNot: |
|
1357 case MDefinition::Op_Lsh: |
|
1358 case MDefinition::Op_Rsh: |
|
1359 break; |
|
1360 default: |
|
1361 return true; |
|
1362 } |
|
1363 } |
|
1364 |
|
1365 // The negative zero check can always be removed on the second |
|
1366 // executed operand; by the time this executes the first will have |
|
1367 // been evaluated as int32 and the addition's result cannot be -0. |
|
1368 break; |
|
1369 } |
|
1370 case MDefinition::Op_Sub: |
|
1371 // If sub is truncating -0 and 0 are observed as the same |
|
1372 if (use_def->toSub()->isTruncated()) |
|
1373 break; |
|
1374 /* Fall through... */ |
|
1375 case MDefinition::Op_StoreElement: |
|
1376 case MDefinition::Op_StoreElementHole: |
|
1377 case MDefinition::Op_LoadElement: |
|
1378 case MDefinition::Op_LoadElementHole: |
|
1379 case MDefinition::Op_LoadTypedArrayElement: |
|
1380 case MDefinition::Op_LoadTypedArrayElementHole: |
|
1381 case MDefinition::Op_CharCodeAt: |
|
1382 case MDefinition::Op_Mod: |
|
1383 // Only allowed to remove check when definition is the second operand |
|
1384 if (use_def->getOperand(0) == def) |
|
1385 return true; |
|
1386 for (size_t i = 2, e = use_def->numOperands(); i < e; i++) { |
|
1387 if (use_def->getOperand(i) == def) |
|
1388 return true; |
|
1389 } |
|
1390 break; |
|
1391 case MDefinition::Op_BoundsCheck: |
|
1392 // Only allowed to remove check when definition is the first operand |
|
1393 if (use_def->toBoundsCheck()->getOperand(1) == def) |
|
1394 return true; |
|
1395 break; |
|
1396 case MDefinition::Op_ToString: |
|
1397 case MDefinition::Op_FromCharCode: |
|
1398 case MDefinition::Op_TableSwitch: |
|
1399 case MDefinition::Op_Compare: |
|
1400 case MDefinition::Op_BitAnd: |
|
1401 case MDefinition::Op_BitOr: |
|
1402 case MDefinition::Op_BitXor: |
|
1403 case MDefinition::Op_Abs: |
|
1404 case MDefinition::Op_TruncateToInt32: |
|
1405 // Always allowed to remove check. No matter which operand. |
|
1406 break; |
|
1407 default: |
|
1408 return true; |
|
1409 } |
|
1410 } |
|
1411 return false; |
|
1412 } |
|
1413 |
|
1414 MDefinition * |
|
1415 MBinaryArithInstruction::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
1416 { |
|
1417 if (specialization_ == MIRType_None) |
|
1418 return this; |
|
1419 |
|
1420 MDefinition *lhs = getOperand(0); |
|
1421 MDefinition *rhs = getOperand(1); |
|
1422 if (MDefinition *folded = EvaluateConstantOperands(alloc, this)) |
|
1423 return folded; |
|
1424 |
|
1425 // 0 + -0 = 0. So we can't remove addition |
|
1426 if (isAdd() && specialization_ != MIRType_Int32) |
|
1427 return this; |
|
1428 |
|
1429 if (IsConstant(rhs, getIdentity())) |
|
1430 return lhs; |
|
1431 |
|
1432 // subtraction isn't commutative. So we can't remove subtraction when lhs equals 0 |
|
1433 if (isSub()) |
|
1434 return this; |
|
1435 |
|
1436 if (IsConstant(lhs, getIdentity())) |
|
1437 return rhs; // x op id => x |
|
1438 |
|
1439 return this; |
|
1440 } |
|
1441 |
|
1442 void |
|
1443 MBinaryArithInstruction::trySpecializeFloat32(TempAllocator &alloc) |
|
1444 { |
|
1445 MDefinition *left = lhs(); |
|
1446 MDefinition *right = rhs(); |
|
1447 |
|
1448 if (!left->canProduceFloat32() || !right->canProduceFloat32() |
|
1449 || !CheckUsesAreFloat32Consumers(this)) |
|
1450 { |
|
1451 if (left->type() == MIRType_Float32) |
|
1452 ConvertDefinitionToDouble<0>(alloc, left, this); |
|
1453 if (right->type() == MIRType_Float32) |
|
1454 ConvertDefinitionToDouble<1>(alloc, right, this); |
|
1455 return; |
|
1456 } |
|
1457 |
|
1458 specialization_ = MIRType_Float32; |
|
1459 setResultType(MIRType_Float32); |
|
1460 } |
|
1461 |
|
1462 bool |
|
1463 MAbs::fallible() const |
|
1464 { |
|
1465 return !implicitTruncate_ && (!range() || !range()->hasInt32Bounds()); |
|
1466 } |
|
1467 |
|
1468 void |
|
1469 MAbs::trySpecializeFloat32(TempAllocator &alloc) |
|
1470 { |
|
1471 if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) { |
|
1472 if (input()->type() == MIRType_Float32) |
|
1473 ConvertDefinitionToDouble<0>(alloc, input(), this); |
|
1474 return; |
|
1475 } |
|
1476 |
|
1477 setResultType(MIRType_Float32); |
|
1478 specialization_ = MIRType_Float32; |
|
1479 } |
|
1480 |
|
1481 MDefinition * |
|
1482 MDiv::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
1483 { |
|
1484 if (specialization_ == MIRType_None) |
|
1485 return this; |
|
1486 |
|
1487 if (MDefinition *folded = EvaluateConstantOperands(alloc, this)) |
|
1488 return folded; |
|
1489 |
|
1490 return this; |
|
1491 } |
|
1492 |
|
1493 void |
|
1494 MDiv::analyzeEdgeCasesForward() |
|
1495 { |
|
1496 // This is only meaningful when doing integer division. |
|
1497 if (specialization_ != MIRType_Int32) |
|
1498 return; |
|
1499 |
|
1500 // Try removing divide by zero check |
|
1501 if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(0)) |
|
1502 canBeDivideByZero_ = false; |
|
1503 |
|
1504 // If lhs is a constant int != INT32_MIN, then |
|
1505 // negative overflow check can be skipped. |
|
1506 if (lhs()->isConstant() && !lhs()->toConstant()->value().isInt32(INT32_MIN)) |
|
1507 canBeNegativeOverflow_ = false; |
|
1508 |
|
1509 // If rhs is a constant int != -1, likewise. |
|
1510 if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(-1)) |
|
1511 canBeNegativeOverflow_ = false; |
|
1512 |
|
1513 // If lhs is != 0, then negative zero check can be skipped. |
|
1514 if (lhs()->isConstant() && !lhs()->toConstant()->value().isInt32(0)) |
|
1515 setCanBeNegativeZero(false); |
|
1516 |
|
1517 // If rhs is >= 0, likewise. |
|
1518 if (rhs()->isConstant()) { |
|
1519 const js::Value &val = rhs()->toConstant()->value(); |
|
1520 if (val.isInt32() && val.toInt32() >= 0) |
|
1521 setCanBeNegativeZero(false); |
|
1522 } |
|
1523 } |
|
1524 |
|
1525 void |
|
1526 MDiv::analyzeEdgeCasesBackward() |
|
1527 { |
|
1528 if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) |
|
1529 setCanBeNegativeZero(false); |
|
1530 } |
|
1531 |
|
1532 bool |
|
1533 MDiv::fallible() const |
|
1534 { |
|
1535 return !isTruncated(); |
|
1536 } |
|
1537 |
|
1538 bool |
|
1539 MMod::canBeDivideByZero() const |
|
1540 { |
|
1541 JS_ASSERT(specialization_ == MIRType_Int32); |
|
1542 return !rhs()->isConstant() || rhs()->toConstant()->value().toInt32() == 0; |
|
1543 } |
|
1544 |
|
1545 bool |
|
1546 MMod::canBePowerOfTwoDivisor() const |
|
1547 { |
|
1548 JS_ASSERT(specialization_ == MIRType_Int32); |
|
1549 |
|
1550 if (!rhs()->isConstant()) |
|
1551 return true; |
|
1552 |
|
1553 int32_t i = rhs()->toConstant()->value().toInt32(); |
|
1554 if (i <= 0 || !IsPowerOfTwo(i)) |
|
1555 return false; |
|
1556 |
|
1557 return true; |
|
1558 } |
|
1559 |
|
1560 MDefinition * |
|
1561 MMod::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
1562 { |
|
1563 if (specialization_ == MIRType_None) |
|
1564 return this; |
|
1565 |
|
1566 if (MDefinition *folded = EvaluateConstantOperands(alloc, this)) |
|
1567 return folded; |
|
1568 |
|
1569 return this; |
|
1570 } |
|
1571 |
|
1572 bool |
|
1573 MMod::fallible() const |
|
1574 { |
|
1575 return !isTruncated() && |
|
1576 (isUnsigned() || canBeDivideByZero() || canBeNegativeDividend()); |
|
1577 } |
|
1578 |
|
1579 void |
|
1580 MMathFunction::trySpecializeFloat32(TempAllocator &alloc) |
|
1581 { |
|
1582 if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) { |
|
1583 if (input()->type() == MIRType_Float32) |
|
1584 ConvertDefinitionToDouble<0>(alloc, input(), this); |
|
1585 return; |
|
1586 } |
|
1587 |
|
1588 setResultType(MIRType_Float32); |
|
1589 setPolicyType(MIRType_Float32); |
|
1590 } |
|
1591 |
|
1592 bool |
|
1593 MAdd::fallible() const |
|
1594 { |
|
1595 // the add is fallible if range analysis does not say that it is finite, AND |
|
1596 // either the truncation analysis shows that there are non-truncated uses. |
|
1597 if (isTruncated()) |
|
1598 return false; |
|
1599 if (range() && range()->hasInt32Bounds()) |
|
1600 return false; |
|
1601 return true; |
|
1602 } |
|
1603 |
|
1604 bool |
|
1605 MSub::fallible() const |
|
1606 { |
|
1607 // see comment in MAdd::fallible() |
|
1608 if (isTruncated()) |
|
1609 return false; |
|
1610 if (range() && range()->hasInt32Bounds()) |
|
1611 return false; |
|
1612 return true; |
|
1613 } |
|
1614 |
|
1615 MDefinition * |
|
1616 MMul::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
1617 { |
|
1618 MDefinition *out = MBinaryArithInstruction::foldsTo(alloc, useValueNumbers); |
|
1619 if (out != this) |
|
1620 return out; |
|
1621 |
|
1622 if (specialization() != MIRType_Int32) |
|
1623 return this; |
|
1624 |
|
1625 if (EqualValues(useValueNumbers, lhs(), rhs())) |
|
1626 setCanBeNegativeZero(false); |
|
1627 |
|
1628 return this; |
|
1629 } |
|
1630 |
|
1631 void |
|
1632 MMul::analyzeEdgeCasesForward() |
|
1633 { |
|
1634 // Try to remove the check for negative zero |
|
1635 // This only makes sense when using the integer multiplication |
|
1636 if (specialization() != MIRType_Int32) |
|
1637 return; |
|
1638 |
|
1639 // If lhs is > 0, no need for negative zero check. |
|
1640 if (lhs()->isConstant()) { |
|
1641 const js::Value &val = lhs()->toConstant()->value(); |
|
1642 if (val.isInt32() && val.toInt32() > 0) |
|
1643 setCanBeNegativeZero(false); |
|
1644 } |
|
1645 |
|
1646 // If rhs is > 0, likewise. |
|
1647 if (rhs()->isConstant()) { |
|
1648 const js::Value &val = rhs()->toConstant()->value(); |
|
1649 if (val.isInt32() && val.toInt32() > 0) |
|
1650 setCanBeNegativeZero(false); |
|
1651 } |
|
1652 } |
|
1653 |
|
1654 void |
|
1655 MMul::analyzeEdgeCasesBackward() |
|
1656 { |
|
1657 if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) |
|
1658 setCanBeNegativeZero(false); |
|
1659 } |
|
1660 |
|
1661 bool |
|
1662 MMul::updateForReplacement(MDefinition *ins_) |
|
1663 { |
|
1664 MMul *ins = ins_->toMul(); |
|
1665 bool negativeZero = canBeNegativeZero() || ins->canBeNegativeZero(); |
|
1666 setCanBeNegativeZero(negativeZero); |
|
1667 // Remove the imul annotation when merging imul and normal multiplication. |
|
1668 if (mode_ == Integer && ins->mode() != Integer) |
|
1669 mode_ = Normal; |
|
1670 return true; |
|
1671 } |
|
1672 |
|
1673 bool |
|
1674 MMul::canOverflow() const |
|
1675 { |
|
1676 if (isTruncated()) |
|
1677 return false; |
|
1678 return !range() || !range()->hasInt32Bounds(); |
|
1679 } |
|
1680 |
|
1681 bool |
|
1682 MUrsh::fallible() const |
|
1683 { |
|
1684 if (bailoutsDisabled()) |
|
1685 return false; |
|
1686 return !range() || !range()->hasInt32Bounds(); |
|
1687 } |
|
1688 |
|
1689 static inline bool |
|
1690 KnownNonStringPrimitive(MDefinition *op) |
|
1691 { |
|
1692 return !op->mightBeType(MIRType_Object) |
|
1693 && !op->mightBeType(MIRType_String) |
|
1694 && !op->mightBeType(MIRType_MagicOptimizedArguments) |
|
1695 && !op->mightBeType(MIRType_MagicHole) |
|
1696 && !op->mightBeType(MIRType_MagicIsConstructing); |
|
1697 } |
|
1698 |
|
1699 void |
|
1700 MBinaryArithInstruction::infer(TempAllocator &alloc, BaselineInspector *inspector, jsbytecode *pc) |
|
1701 { |
|
1702 JS_ASSERT(this->type() == MIRType_Value); |
|
1703 |
|
1704 specialization_ = MIRType_None; |
|
1705 |
|
1706 // Don't specialize if one operand could be an object. If we specialize |
|
1707 // as int32 or double based on baseline feedback, we could DCE this |
|
1708 // instruction and fail to invoke any valueOf methods. |
|
1709 if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) |
|
1710 return; |
|
1711 |
|
1712 // Anything complex - strings and objects - are not specialized |
|
1713 // unless baseline type hints suggest it might be profitable |
|
1714 if (!KnownNonStringPrimitive(getOperand(0)) || !KnownNonStringPrimitive(getOperand(1))) |
|
1715 return inferFallback(inspector, pc); |
|
1716 |
|
1717 // Retrieve type information of lhs and rhs. |
|
1718 MIRType lhs = getOperand(0)->type(); |
|
1719 MIRType rhs = getOperand(1)->type(); |
|
1720 |
|
1721 // Guess a result type based on the inputs. |
|
1722 // Don't specialize for neither-integer-nor-double results. |
|
1723 if (lhs == MIRType_Int32 && rhs == MIRType_Int32) |
|
1724 setResultType(MIRType_Int32); |
|
1725 // Double operations are prioritary over float32 operations (i.e. if any operand needs |
|
1726 // a double as an input, convert all operands to doubles) |
|
1727 else if (IsFloatingPointType(lhs) || IsFloatingPointType(rhs)) |
|
1728 setResultType(MIRType_Double); |
|
1729 else |
|
1730 return inferFallback(inspector, pc); |
|
1731 |
|
1732 // If the operation has ever overflowed, use a double specialization. |
|
1733 if (inspector->hasSeenDoubleResult(pc)) |
|
1734 setResultType(MIRType_Double); |
|
1735 |
|
1736 // If the operation will always overflow on its constant operands, use a |
|
1737 // double specialization so that it can be constant folded later. |
|
1738 if ((isMul() || isDiv()) && lhs == MIRType_Int32 && rhs == MIRType_Int32) { |
|
1739 bool typeChange = false; |
|
1740 EvaluateConstantOperands(alloc, this, &typeChange); |
|
1741 if (typeChange) |
|
1742 setResultType(MIRType_Double); |
|
1743 } |
|
1744 |
|
1745 JS_ASSERT(lhs < MIRType_String || lhs == MIRType_Value); |
|
1746 JS_ASSERT(rhs < MIRType_String || rhs == MIRType_Value); |
|
1747 |
|
1748 MIRType rval = this->type(); |
|
1749 |
|
1750 // Don't specialize values when result isn't double |
|
1751 if (lhs == MIRType_Value || rhs == MIRType_Value) { |
|
1752 if (!IsFloatingPointType(rval)) { |
|
1753 specialization_ = MIRType_None; |
|
1754 return; |
|
1755 } |
|
1756 } |
|
1757 |
|
1758 // Don't specialize as int32 if one of the operands is undefined, |
|
1759 // since ToNumber(undefined) is NaN. |
|
1760 if (rval == MIRType_Int32 && (lhs == MIRType_Undefined || rhs == MIRType_Undefined)) { |
|
1761 specialization_ = MIRType_None; |
|
1762 return; |
|
1763 } |
|
1764 |
|
1765 specialization_ = rval; |
|
1766 |
|
1767 if (isAdd() || isMul()) |
|
1768 setCommutative(); |
|
1769 setResultType(rval); |
|
1770 } |
|
1771 |
|
1772 void |
|
1773 MBinaryArithInstruction::inferFallback(BaselineInspector *inspector, |
|
1774 jsbytecode *pc) |
|
1775 { |
|
1776 // Try to specialize based on what baseline observed in practice. |
|
1777 specialization_ = inspector->expectedBinaryArithSpecialization(pc); |
|
1778 if (specialization_ != MIRType_None) { |
|
1779 setResultType(specialization_); |
|
1780 return; |
|
1781 } |
|
1782 |
|
1783 // In parallel execution, for now anyhow, we *only* support adding |
|
1784 // and manipulating numbers (not strings or objects). So no |
|
1785 // matter what we can specialize to double...if the result ought |
|
1786 // to have been something else, we'll fail in the various type |
|
1787 // guards that get inserted later. |
|
1788 if (block()->info().executionMode() == ParallelExecution) { |
|
1789 specialization_ = MIRType_Double; |
|
1790 setResultType(MIRType_Double); |
|
1791 return; |
|
1792 } |
|
1793 |
|
1794 // If we can't specialize because we have no type information at all for |
|
1795 // the lhs or rhs, mark the binary instruction as having no possible types |
|
1796 // either to avoid degrading subsequent analysis. |
|
1797 if (getOperand(0)->emptyResultTypeSet() || getOperand(1)->emptyResultTypeSet()) { |
|
1798 LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc(); |
|
1799 types::TemporaryTypeSet *types = alloc->new_<types::TemporaryTypeSet>(); |
|
1800 if (types) |
|
1801 setResultTypeSet(types); |
|
1802 } |
|
1803 } |
|
1804 |
|
1805 static bool |
|
1806 SafelyCoercesToDouble(MDefinition *op) |
|
1807 { |
|
1808 // Strings are unhandled -- visitToDouble() doesn't support them yet. |
|
1809 // Null is unhandled -- ToDouble(null) == 0, but (0 == null) is false. |
|
1810 return KnownNonStringPrimitive(op) && !op->mightBeType(MIRType_Null); |
|
1811 } |
|
1812 |
|
1813 static bool |
|
1814 ObjectOrSimplePrimitive(MDefinition *op) |
|
1815 { |
|
1816 // Return true if op is either undefined/null/boolean/int32 or an object. |
|
1817 return !op->mightBeType(MIRType_String) |
|
1818 && !op->mightBeType(MIRType_Double) |
|
1819 && !op->mightBeType(MIRType_Float32) |
|
1820 && !op->mightBeType(MIRType_MagicOptimizedArguments) |
|
1821 && !op->mightBeType(MIRType_MagicHole) |
|
1822 && !op->mightBeType(MIRType_MagicIsConstructing); |
|
1823 } |
|
1824 |
|
1825 static bool |
|
1826 CanDoValueBitwiseCmp(MDefinition *lhs, MDefinition *rhs, bool looseEq) |
|
1827 { |
|
1828 // Only primitive (not double/string) or objects are supported. |
|
1829 // I.e. Undefined/Null/Boolean/Int32 and Object |
|
1830 if (!ObjectOrSimplePrimitive(lhs) || !ObjectOrSimplePrimitive(rhs)) |
|
1831 return false; |
|
1832 |
|
1833 // Objects that emulate undefined are not supported. |
|
1834 if (MaybeEmulatesUndefined(lhs) || MaybeEmulatesUndefined(rhs)) |
|
1835 return false; |
|
1836 |
|
1837 // In the loose comparison more values could be the same, |
|
1838 // but value comparison reporting otherwise. |
|
1839 if (looseEq) { |
|
1840 |
|
1841 // Undefined compared loosy to Null is not supported, |
|
1842 // because tag is different, but value can be the same (undefined == null). |
|
1843 if ((lhs->mightBeType(MIRType_Undefined) && rhs->mightBeType(MIRType_Null)) || |
|
1844 (lhs->mightBeType(MIRType_Null) && rhs->mightBeType(MIRType_Undefined))) |
|
1845 { |
|
1846 return false; |
|
1847 } |
|
1848 |
|
1849 // Int32 compared loosy to Boolean is not supported, |
|
1850 // because tag is different, but value can be the same (1 == true). |
|
1851 if ((lhs->mightBeType(MIRType_Int32) && rhs->mightBeType(MIRType_Boolean)) || |
|
1852 (lhs->mightBeType(MIRType_Boolean) && rhs->mightBeType(MIRType_Int32))) |
|
1853 { |
|
1854 return false; |
|
1855 } |
|
1856 |
|
1857 // For loosy comparison of an object with a Boolean/Number/String |
|
1858 // the valueOf the object is taken. Therefore not supported. |
|
1859 bool simpleLHS = lhs->mightBeType(MIRType_Boolean) || lhs->mightBeType(MIRType_Int32); |
|
1860 bool simpleRHS = rhs->mightBeType(MIRType_Boolean) || rhs->mightBeType(MIRType_Int32); |
|
1861 if ((lhs->mightBeType(MIRType_Object) && simpleRHS) || |
|
1862 (rhs->mightBeType(MIRType_Object) && simpleLHS)) |
|
1863 { |
|
1864 return false; |
|
1865 } |
|
1866 } |
|
1867 |
|
1868 return true; |
|
1869 } |
|
1870 |
|
1871 MIRType |
|
1872 MCompare::inputType() |
|
1873 { |
|
1874 switch(compareType_) { |
|
1875 case Compare_Undefined: |
|
1876 return MIRType_Undefined; |
|
1877 case Compare_Null: |
|
1878 return MIRType_Null; |
|
1879 case Compare_Boolean: |
|
1880 return MIRType_Boolean; |
|
1881 case Compare_UInt32: |
|
1882 case Compare_Int32: |
|
1883 case Compare_Int32MaybeCoerceBoth: |
|
1884 case Compare_Int32MaybeCoerceLHS: |
|
1885 case Compare_Int32MaybeCoerceRHS: |
|
1886 return MIRType_Int32; |
|
1887 case Compare_Double: |
|
1888 case Compare_DoubleMaybeCoerceLHS: |
|
1889 case Compare_DoubleMaybeCoerceRHS: |
|
1890 return MIRType_Double; |
|
1891 case Compare_Float32: |
|
1892 return MIRType_Float32; |
|
1893 case Compare_String: |
|
1894 case Compare_StrictString: |
|
1895 return MIRType_String; |
|
1896 case Compare_Object: |
|
1897 return MIRType_Object; |
|
1898 case Compare_Unknown: |
|
1899 case Compare_Value: |
|
1900 return MIRType_Value; |
|
1901 default: |
|
1902 MOZ_ASSUME_UNREACHABLE("No known conversion"); |
|
1903 } |
|
1904 } |
|
1905 |
|
1906 static inline bool |
|
1907 MustBeUInt32(MDefinition *def, MDefinition **pwrapped) |
|
1908 { |
|
1909 if (def->isUrsh()) { |
|
1910 *pwrapped = def->toUrsh()->getOperand(0); |
|
1911 MDefinition *rhs = def->toUrsh()->getOperand(1); |
|
1912 return !def->toUrsh()->bailoutsDisabled() |
|
1913 && rhs->isConstant() |
|
1914 && rhs->toConstant()->value().isInt32() |
|
1915 && rhs->toConstant()->value().toInt32() == 0; |
|
1916 } |
|
1917 |
|
1918 if (def->isConstant()) { |
|
1919 *pwrapped = def; |
|
1920 return def->toConstant()->value().isInt32() |
|
1921 && def->toConstant()->value().toInt32() >= 0; |
|
1922 } |
|
1923 |
|
1924 return false; |
|
1925 } |
|
1926 |
|
1927 bool |
|
1928 MBinaryInstruction::tryUseUnsignedOperands() |
|
1929 { |
|
1930 MDefinition *newlhs, *newrhs; |
|
1931 if (MustBeUInt32(getOperand(0), &newlhs) && MustBeUInt32(getOperand(1), &newrhs)) { |
|
1932 if (newlhs->type() != MIRType_Int32 || newrhs->type() != MIRType_Int32) |
|
1933 return false; |
|
1934 if (newlhs != getOperand(0)) { |
|
1935 getOperand(0)->setImplicitlyUsedUnchecked(); |
|
1936 replaceOperand(0, newlhs); |
|
1937 } |
|
1938 if (newrhs != getOperand(1)) { |
|
1939 getOperand(1)->setImplicitlyUsedUnchecked(); |
|
1940 replaceOperand(1, newrhs); |
|
1941 } |
|
1942 return true; |
|
1943 } |
|
1944 return false; |
|
1945 } |
|
1946 |
|
1947 void |
|
1948 MCompare::infer(BaselineInspector *inspector, jsbytecode *pc) |
|
1949 { |
|
1950 JS_ASSERT(operandMightEmulateUndefined()); |
|
1951 |
|
1952 if (!MaybeEmulatesUndefined(getOperand(0)) && !MaybeEmulatesUndefined(getOperand(1))) |
|
1953 markNoOperandEmulatesUndefined(); |
|
1954 |
|
1955 MIRType lhs = getOperand(0)->type(); |
|
1956 MIRType rhs = getOperand(1)->type(); |
|
1957 |
|
1958 bool looseEq = jsop() == JSOP_EQ || jsop() == JSOP_NE; |
|
1959 bool strictEq = jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE; |
|
1960 bool relationalEq = !(looseEq || strictEq); |
|
1961 |
|
1962 // Comparisons on unsigned integers may be treated as UInt32. |
|
1963 if (tryUseUnsignedOperands()) { |
|
1964 compareType_ = Compare_UInt32; |
|
1965 return; |
|
1966 } |
|
1967 |
|
1968 // Integer to integer or boolean to boolean comparisons may be treated as Int32. |
|
1969 if ((lhs == MIRType_Int32 && rhs == MIRType_Int32) || |
|
1970 (lhs == MIRType_Boolean && rhs == MIRType_Boolean)) |
|
1971 { |
|
1972 compareType_ = Compare_Int32MaybeCoerceBoth; |
|
1973 return; |
|
1974 } |
|
1975 |
|
1976 // Loose/relational cross-integer/boolean comparisons may be treated as Int32. |
|
1977 if (!strictEq && |
|
1978 (lhs == MIRType_Int32 || lhs == MIRType_Boolean) && |
|
1979 (rhs == MIRType_Int32 || rhs == MIRType_Boolean)) |
|
1980 { |
|
1981 compareType_ = Compare_Int32MaybeCoerceBoth; |
|
1982 return; |
|
1983 } |
|
1984 |
|
1985 // Numeric comparisons against a double coerce to double. |
|
1986 if (IsNumberType(lhs) && IsNumberType(rhs)) { |
|
1987 compareType_ = Compare_Double; |
|
1988 return; |
|
1989 } |
|
1990 |
|
1991 // Any comparison is allowed except strict eq. |
|
1992 if (!strictEq && IsFloatingPointType(lhs) && SafelyCoercesToDouble(getOperand(1))) { |
|
1993 compareType_ = Compare_DoubleMaybeCoerceRHS; |
|
1994 return; |
|
1995 } |
|
1996 if (!strictEq && IsFloatingPointType(rhs) && SafelyCoercesToDouble(getOperand(0))) { |
|
1997 compareType_ = Compare_DoubleMaybeCoerceLHS; |
|
1998 return; |
|
1999 } |
|
2000 |
|
2001 // Handle object comparison. |
|
2002 if (!relationalEq && lhs == MIRType_Object && rhs == MIRType_Object) { |
|
2003 compareType_ = Compare_Object; |
|
2004 return; |
|
2005 } |
|
2006 |
|
2007 // Handle string comparisons. (Relational string compares are still unsupported). |
|
2008 if (!relationalEq && lhs == MIRType_String && rhs == MIRType_String) { |
|
2009 compareType_ = Compare_String; |
|
2010 return; |
|
2011 } |
|
2012 |
|
2013 if (strictEq && lhs == MIRType_String) { |
|
2014 // Lowering expects the rhs to be definitly string. |
|
2015 compareType_ = Compare_StrictString; |
|
2016 swapOperands(); |
|
2017 return; |
|
2018 } |
|
2019 |
|
2020 if (strictEq && rhs == MIRType_String) { |
|
2021 compareType_ = Compare_StrictString; |
|
2022 return; |
|
2023 } |
|
2024 |
|
2025 // Handle compare with lhs being Undefined or Null. |
|
2026 if (!relationalEq && IsNullOrUndefined(lhs)) { |
|
2027 // Lowering expects the rhs to be null/undefined, so we have to |
|
2028 // swap the operands. This is necessary since we may not know which |
|
2029 // operand was null/undefined during lowering (both operands may have |
|
2030 // MIRType_Value). |
|
2031 compareType_ = (lhs == MIRType_Null) ? Compare_Null : Compare_Undefined; |
|
2032 swapOperands(); |
|
2033 return; |
|
2034 } |
|
2035 |
|
2036 // Handle compare with rhs being Undefined or Null. |
|
2037 if (!relationalEq && IsNullOrUndefined(rhs)) { |
|
2038 compareType_ = (rhs == MIRType_Null) ? Compare_Null : Compare_Undefined; |
|
2039 return; |
|
2040 } |
|
2041 |
|
2042 // Handle strict comparison with lhs/rhs being typed Boolean. |
|
2043 if (strictEq && (lhs == MIRType_Boolean || rhs == MIRType_Boolean)) { |
|
2044 // bool/bool case got an int32 specialization earlier. |
|
2045 JS_ASSERT(!(lhs == MIRType_Boolean && rhs == MIRType_Boolean)); |
|
2046 |
|
2047 // Ensure the boolean is on the right so that the type policy knows |
|
2048 // which side to unbox. |
|
2049 if (lhs == MIRType_Boolean) |
|
2050 swapOperands(); |
|
2051 |
|
2052 compareType_ = Compare_Boolean; |
|
2053 return; |
|
2054 } |
|
2055 |
|
2056 // Determine if we can do the compare based on a quick value check. |
|
2057 if (!relationalEq && CanDoValueBitwiseCmp(getOperand(0), getOperand(1), looseEq)) { |
|
2058 compareType_ = Compare_Value; |
|
2059 return; |
|
2060 } |
|
2061 |
|
2062 // Type information is not good enough to pick out a particular type of |
|
2063 // comparison we can do here. Try to specialize based on any baseline |
|
2064 // caches that have been generated for the opcode. These will cause the |
|
2065 // instruction's type policy to insert fallible unboxes to the appropriate |
|
2066 // input types. |
|
2067 |
|
2068 if (!strictEq) |
|
2069 compareType_ = inspector->expectedCompareType(pc); |
|
2070 } |
|
2071 |
|
2072 MBitNot * |
|
2073 MBitNot::New(TempAllocator &alloc, MDefinition *input) |
|
2074 { |
|
2075 return new(alloc) MBitNot(input); |
|
2076 } |
|
2077 |
|
2078 MBitNot * |
|
2079 MBitNot::NewAsmJS(TempAllocator &alloc, MDefinition *input) |
|
2080 { |
|
2081 MBitNot *ins = new(alloc) MBitNot(input); |
|
2082 ins->specialization_ = MIRType_Int32; |
|
2083 JS_ASSERT(ins->type() == MIRType_Int32); |
|
2084 return ins; |
|
2085 } |
|
2086 |
|
2087 MDefinition * |
|
2088 MBitNot::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
2089 { |
|
2090 if (specialization_ != MIRType_Int32) |
|
2091 return this; |
|
2092 |
|
2093 MDefinition *input = getOperand(0); |
|
2094 |
|
2095 if (input->isConstant()) { |
|
2096 js::Value v = Int32Value(~(input->toConstant()->value().toInt32())); |
|
2097 return MConstant::New(alloc, v); |
|
2098 } |
|
2099 |
|
2100 if (input->isBitNot() && input->toBitNot()->specialization_ == MIRType_Int32) { |
|
2101 JS_ASSERT(input->toBitNot()->getOperand(0)->type() == MIRType_Int32); |
|
2102 return input->toBitNot()->getOperand(0); // ~~x => x |
|
2103 } |
|
2104 |
|
2105 return this; |
|
2106 } |
|
2107 |
|
2108 MDefinition * |
|
2109 MTypeOf::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
2110 { |
|
2111 // Note: we can't use input->type() here, type analysis has |
|
2112 // boxed the input. |
|
2113 JS_ASSERT(input()->type() == MIRType_Value); |
|
2114 |
|
2115 JSType type; |
|
2116 |
|
2117 switch (inputType()) { |
|
2118 case MIRType_Double: |
|
2119 case MIRType_Int32: |
|
2120 type = JSTYPE_NUMBER; |
|
2121 break; |
|
2122 case MIRType_String: |
|
2123 type = JSTYPE_STRING; |
|
2124 break; |
|
2125 case MIRType_Null: |
|
2126 type = JSTYPE_OBJECT; |
|
2127 break; |
|
2128 case MIRType_Undefined: |
|
2129 type = JSTYPE_VOID; |
|
2130 break; |
|
2131 case MIRType_Boolean: |
|
2132 type = JSTYPE_BOOLEAN; |
|
2133 break; |
|
2134 case MIRType_Object: |
|
2135 if (!inputMaybeCallableOrEmulatesUndefined()) { |
|
2136 // Object is not callable and does not emulate undefined, so it's |
|
2137 // safe to fold to "object". |
|
2138 type = JSTYPE_OBJECT; |
|
2139 break; |
|
2140 } |
|
2141 // FALL THROUGH |
|
2142 default: |
|
2143 return this; |
|
2144 } |
|
2145 |
|
2146 return MConstant::New(alloc, StringValue(TypeName(type, GetIonContext()->runtime->names()))); |
|
2147 } |
|
2148 |
|
2149 void |
|
2150 MTypeOf::infer() |
|
2151 { |
|
2152 JS_ASSERT(inputMaybeCallableOrEmulatesUndefined()); |
|
2153 |
|
2154 if (!MaybeEmulatesUndefined(input()) && !MaybeCallable(input())) |
|
2155 markInputNotCallableOrEmulatesUndefined(); |
|
2156 } |
|
2157 |
|
2158 MBitAnd * |
|
2159 MBitAnd::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
|
2160 { |
|
2161 return new(alloc) MBitAnd(left, right); |
|
2162 } |
|
2163 |
|
2164 MBitAnd * |
|
2165 MBitAnd::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
|
2166 { |
|
2167 MBitAnd *ins = new(alloc) MBitAnd(left, right); |
|
2168 ins->specializeAsInt32(); |
|
2169 return ins; |
|
2170 } |
|
2171 |
|
2172 MBitOr * |
|
2173 MBitOr::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
|
2174 { |
|
2175 return new(alloc) MBitOr(left, right); |
|
2176 } |
|
2177 |
|
2178 MBitOr * |
|
2179 MBitOr::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
|
2180 { |
|
2181 MBitOr *ins = new(alloc) MBitOr(left, right); |
|
2182 ins->specializeAsInt32(); |
|
2183 return ins; |
|
2184 } |
|
2185 |
|
2186 MBitXor * |
|
2187 MBitXor::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
|
2188 { |
|
2189 return new(alloc) MBitXor(left, right); |
|
2190 } |
|
2191 |
|
2192 MBitXor * |
|
2193 MBitXor::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
|
2194 { |
|
2195 MBitXor *ins = new(alloc) MBitXor(left, right); |
|
2196 ins->specializeAsInt32(); |
|
2197 return ins; |
|
2198 } |
|
2199 |
|
2200 MLsh * |
|
2201 MLsh::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
|
2202 { |
|
2203 return new(alloc) MLsh(left, right); |
|
2204 } |
|
2205 |
|
2206 MLsh * |
|
2207 MLsh::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
|
2208 { |
|
2209 MLsh *ins = new(alloc) MLsh(left, right); |
|
2210 ins->specializeAsInt32(); |
|
2211 return ins; |
|
2212 } |
|
2213 |
|
2214 MRsh * |
|
2215 MRsh::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
|
2216 { |
|
2217 return new(alloc) MRsh(left, right); |
|
2218 } |
|
2219 |
|
2220 MRsh * |
|
2221 MRsh::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
|
2222 { |
|
2223 MRsh *ins = new(alloc) MRsh(left, right); |
|
2224 ins->specializeAsInt32(); |
|
2225 return ins; |
|
2226 } |
|
2227 |
|
2228 MUrsh * |
|
2229 MUrsh::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
|
2230 { |
|
2231 return new(alloc) MUrsh(left, right); |
|
2232 } |
|
2233 |
|
2234 MUrsh * |
|
2235 MUrsh::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
|
2236 { |
|
2237 MUrsh *ins = new(alloc) MUrsh(left, right); |
|
2238 ins->specializeAsInt32(); |
|
2239 |
|
2240 // Since Ion has no UInt32 type, we use Int32 and we have a special |
|
2241 // exception to the type rules: we can return values in |
|
2242 // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type |
|
2243 // without bailing out. This is necessary because Ion has no UInt32 |
|
2244 // type and we can't have bailouts in asm.js code. |
|
2245 ins->bailoutsDisabled_ = true; |
|
2246 |
|
2247 return ins; |
|
2248 } |
|
2249 |
|
2250 MResumePoint * |
|
2251 MResumePoint::New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc, MResumePoint *parent, |
|
2252 Mode mode) |
|
2253 { |
|
2254 MResumePoint *resume = new(alloc) MResumePoint(block, pc, parent, mode); |
|
2255 if (!resume->init(alloc)) |
|
2256 return nullptr; |
|
2257 resume->inherit(block); |
|
2258 return resume; |
|
2259 } |
|
2260 |
|
2261 MResumePoint::MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *caller, |
|
2262 Mode mode) |
|
2263 : MNode(block), |
|
2264 stackDepth_(block->stackDepth()), |
|
2265 pc_(pc), |
|
2266 caller_(caller), |
|
2267 instruction_(nullptr), |
|
2268 mode_(mode) |
|
2269 { |
|
2270 block->addResumePoint(this); |
|
2271 } |
|
2272 |
|
2273 void |
|
2274 MResumePoint::inherit(MBasicBlock *block) |
|
2275 { |
|
2276 for (size_t i = 0; i < stackDepth(); i++) { |
|
2277 MDefinition *def = block->getSlot(i); |
|
2278 setOperand(i, def); |
|
2279 } |
|
2280 } |
|
2281 |
|
2282 MDefinition * |
|
2283 MToInt32::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
2284 { |
|
2285 MDefinition *input = getOperand(0); |
|
2286 if (input->type() == MIRType_Int32) |
|
2287 return input; |
|
2288 return this; |
|
2289 } |
|
2290 |
|
2291 void |
|
2292 MToInt32::analyzeEdgeCasesBackward() |
|
2293 { |
|
2294 if (!NeedNegativeZeroCheck(this)) |
|
2295 setCanBeNegativeZero(false); |
|
2296 } |
|
2297 |
|
2298 MDefinition * |
|
2299 MTruncateToInt32::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
2300 { |
|
2301 MDefinition *input = getOperand(0); |
|
2302 if (input->type() == MIRType_Int32) |
|
2303 return input; |
|
2304 |
|
2305 if (input->type() == MIRType_Double && input->isConstant()) { |
|
2306 const Value &v = input->toConstant()->value(); |
|
2307 int32_t ret = ToInt32(v.toDouble()); |
|
2308 return MConstant::New(alloc, Int32Value(ret)); |
|
2309 } |
|
2310 |
|
2311 return this; |
|
2312 } |
|
2313 |
|
2314 MDefinition * |
|
2315 MToDouble::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
2316 { |
|
2317 MDefinition *in = input(); |
|
2318 if (in->type() == MIRType_Double) |
|
2319 return in; |
|
2320 |
|
2321 if (in->isConstant()) { |
|
2322 const Value &v = in->toConstant()->value(); |
|
2323 if (v.isNumber()) { |
|
2324 double out = v.toNumber(); |
|
2325 return MConstant::New(alloc, DoubleValue(out)); |
|
2326 } |
|
2327 } |
|
2328 |
|
2329 return this; |
|
2330 } |
|
2331 |
|
2332 MDefinition * |
|
2333 MToFloat32::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
2334 { |
|
2335 if (input()->type() == MIRType_Float32) |
|
2336 return input(); |
|
2337 |
|
2338 // If x is a Float32, Float32(Double(x)) == x |
|
2339 if (input()->isToDouble() && input()->toToDouble()->input()->type() == MIRType_Float32) |
|
2340 return input()->toToDouble()->input(); |
|
2341 |
|
2342 if (input()->isConstant()) { |
|
2343 const Value &v = input()->toConstant()->value(); |
|
2344 if (v.isNumber()) { |
|
2345 float out = v.toNumber(); |
|
2346 MConstant *c = MConstant::New(alloc, DoubleValue(out)); |
|
2347 c->setResultType(MIRType_Float32); |
|
2348 return c; |
|
2349 } |
|
2350 } |
|
2351 return this; |
|
2352 } |
|
2353 |
|
2354 MDefinition * |
|
2355 MToString::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
2356 { |
|
2357 MDefinition *in = input(); |
|
2358 if (in->type() == MIRType_String) |
|
2359 return in; |
|
2360 return this; |
|
2361 } |
|
2362 |
|
2363 MDefinition * |
|
2364 MClampToUint8::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
2365 { |
|
2366 if (input()->isConstant()) { |
|
2367 const Value &v = input()->toConstant()->value(); |
|
2368 if (v.isDouble()) { |
|
2369 int32_t clamped = ClampDoubleToUint8(v.toDouble()); |
|
2370 return MConstant::New(alloc, Int32Value(clamped)); |
|
2371 } |
|
2372 if (v.isInt32()) { |
|
2373 int32_t clamped = ClampIntForUint8Array(v.toInt32()); |
|
2374 return MConstant::New(alloc, Int32Value(clamped)); |
|
2375 } |
|
2376 } |
|
2377 return this; |
|
2378 } |
|
2379 |
|
2380 bool |
|
2381 MCompare::tryFold(bool *result) |
|
2382 { |
|
2383 JSOp op = jsop(); |
|
2384 |
|
2385 if (compareType_ == Compare_Null || compareType_ == Compare_Undefined) { |
|
2386 JS_ASSERT(op == JSOP_EQ || op == JSOP_STRICTEQ || |
|
2387 op == JSOP_NE || op == JSOP_STRICTNE); |
|
2388 |
|
2389 // The LHS is the value we want to test against null or undefined. |
|
2390 switch (lhs()->type()) { |
|
2391 case MIRType_Value: |
|
2392 return false; |
|
2393 case MIRType_Undefined: |
|
2394 case MIRType_Null: |
|
2395 if (lhs()->type() == inputType()) { |
|
2396 // Both sides have the same type, null or undefined. |
|
2397 *result = (op == JSOP_EQ || op == JSOP_STRICTEQ); |
|
2398 } else { |
|
2399 // One side is null, the other side is undefined. The result is only |
|
2400 // true for loose equality. |
|
2401 *result = (op == JSOP_EQ || op == JSOP_STRICTNE); |
|
2402 } |
|
2403 return true; |
|
2404 case MIRType_Object: |
|
2405 if ((op == JSOP_EQ || op == JSOP_NE) && operandMightEmulateUndefined()) |
|
2406 return false; |
|
2407 /* FALL THROUGH */ |
|
2408 case MIRType_Int32: |
|
2409 case MIRType_Double: |
|
2410 case MIRType_Float32: |
|
2411 case MIRType_String: |
|
2412 case MIRType_Boolean: |
|
2413 *result = (op == JSOP_NE || op == JSOP_STRICTNE); |
|
2414 return true; |
|
2415 default: |
|
2416 MOZ_ASSUME_UNREACHABLE("Unexpected type"); |
|
2417 } |
|
2418 } |
|
2419 |
|
2420 if (compareType_ == Compare_Boolean) { |
|
2421 JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE); |
|
2422 JS_ASSERT(rhs()->type() == MIRType_Boolean); |
|
2423 |
|
2424 switch (lhs()->type()) { |
|
2425 case MIRType_Value: |
|
2426 return false; |
|
2427 case MIRType_Int32: |
|
2428 case MIRType_Double: |
|
2429 case MIRType_Float32: |
|
2430 case MIRType_String: |
|
2431 case MIRType_Object: |
|
2432 case MIRType_Null: |
|
2433 case MIRType_Undefined: |
|
2434 *result = (op == JSOP_STRICTNE); |
|
2435 return true; |
|
2436 case MIRType_Boolean: |
|
2437 // Int32 specialization should handle this. |
|
2438 MOZ_ASSUME_UNREACHABLE("Wrong specialization"); |
|
2439 default: |
|
2440 MOZ_ASSUME_UNREACHABLE("Unexpected type"); |
|
2441 } |
|
2442 } |
|
2443 |
|
2444 if (compareType_ == Compare_StrictString) { |
|
2445 JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE); |
|
2446 JS_ASSERT(rhs()->type() == MIRType_String); |
|
2447 |
|
2448 switch (lhs()->type()) { |
|
2449 case MIRType_Value: |
|
2450 return false; |
|
2451 case MIRType_Boolean: |
|
2452 case MIRType_Int32: |
|
2453 case MIRType_Double: |
|
2454 case MIRType_Float32: |
|
2455 case MIRType_Object: |
|
2456 case MIRType_Null: |
|
2457 case MIRType_Undefined: |
|
2458 *result = (op == JSOP_STRICTNE); |
|
2459 return true; |
|
2460 case MIRType_String: |
|
2461 // Compare_String specialization should handle this. |
|
2462 MOZ_ASSUME_UNREACHABLE("Wrong specialization"); |
|
2463 default: |
|
2464 MOZ_ASSUME_UNREACHABLE("Unexpected type"); |
|
2465 } |
|
2466 } |
|
2467 |
|
2468 return false; |
|
2469 } |
|
2470 |
|
2471 bool |
|
2472 MCompare::evaluateConstantOperands(bool *result) |
|
2473 { |
|
2474 if (type() != MIRType_Boolean && type() != MIRType_Int32) |
|
2475 return false; |
|
2476 |
|
2477 MDefinition *left = getOperand(0); |
|
2478 MDefinition *right = getOperand(1); |
|
2479 |
|
2480 if (!left->isConstant() || !right->isConstant()) |
|
2481 return false; |
|
2482 |
|
2483 Value lhs = left->toConstant()->value(); |
|
2484 Value rhs = right->toConstant()->value(); |
|
2485 |
|
2486 // Fold away some String equality comparisons. |
|
2487 if (lhs.isString() && rhs.isString()) { |
|
2488 int32_t comp = 0; // Default to equal. |
|
2489 if (left != right) |
|
2490 comp = CompareAtoms(&lhs.toString()->asAtom(), &rhs.toString()->asAtom()); |
|
2491 |
|
2492 switch (jsop_) { |
|
2493 case JSOP_LT: |
|
2494 *result = (comp < 0); |
|
2495 break; |
|
2496 case JSOP_LE: |
|
2497 *result = (comp <= 0); |
|
2498 break; |
|
2499 case JSOP_GT: |
|
2500 *result = (comp > 0); |
|
2501 break; |
|
2502 case JSOP_GE: |
|
2503 *result = (comp >= 0); |
|
2504 break; |
|
2505 case JSOP_STRICTEQ: // Fall through. |
|
2506 case JSOP_EQ: |
|
2507 *result = (comp == 0); |
|
2508 break; |
|
2509 case JSOP_STRICTNE: // Fall through. |
|
2510 case JSOP_NE: |
|
2511 *result = (comp != 0); |
|
2512 break; |
|
2513 default: |
|
2514 MOZ_ASSUME_UNREACHABLE("Unexpected op."); |
|
2515 } |
|
2516 |
|
2517 return true; |
|
2518 } |
|
2519 |
|
2520 if (compareType_ == Compare_UInt32) { |
|
2521 uint32_t lhsUint = uint32_t(lhs.toInt32()); |
|
2522 uint32_t rhsUint = uint32_t(rhs.toInt32()); |
|
2523 |
|
2524 switch (jsop_) { |
|
2525 case JSOP_LT: |
|
2526 *result = (lhsUint < rhsUint); |
|
2527 break; |
|
2528 case JSOP_LE: |
|
2529 *result = (lhsUint <= rhsUint); |
|
2530 break; |
|
2531 case JSOP_GT: |
|
2532 *result = (lhsUint > rhsUint); |
|
2533 break; |
|
2534 case JSOP_GE: |
|
2535 *result = (lhsUint >= rhsUint); |
|
2536 break; |
|
2537 case JSOP_EQ: |
|
2538 case JSOP_STRICTEQ: |
|
2539 *result = (lhsUint == rhsUint); |
|
2540 break; |
|
2541 case JSOP_NE: |
|
2542 case JSOP_STRICTNE: |
|
2543 *result = (lhsUint != rhsUint); |
|
2544 break; |
|
2545 default: |
|
2546 MOZ_ASSUME_UNREACHABLE("Unexpected op."); |
|
2547 } |
|
2548 |
|
2549 return true; |
|
2550 } |
|
2551 |
|
2552 if (!lhs.isNumber() || !rhs.isNumber()) |
|
2553 return false; |
|
2554 |
|
2555 switch (jsop_) { |
|
2556 case JSOP_LT: |
|
2557 *result = (lhs.toNumber() < rhs.toNumber()); |
|
2558 break; |
|
2559 case JSOP_LE: |
|
2560 *result = (lhs.toNumber() <= rhs.toNumber()); |
|
2561 break; |
|
2562 case JSOP_GT: |
|
2563 *result = (lhs.toNumber() > rhs.toNumber()); |
|
2564 break; |
|
2565 case JSOP_GE: |
|
2566 *result = (lhs.toNumber() >= rhs.toNumber()); |
|
2567 break; |
|
2568 case JSOP_EQ: |
|
2569 *result = (lhs.toNumber() == rhs.toNumber()); |
|
2570 break; |
|
2571 case JSOP_NE: |
|
2572 *result = (lhs.toNumber() != rhs.toNumber()); |
|
2573 break; |
|
2574 default: |
|
2575 return false; |
|
2576 } |
|
2577 |
|
2578 return true; |
|
2579 } |
|
2580 |
|
2581 MDefinition * |
|
2582 MCompare::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
2583 { |
|
2584 bool result; |
|
2585 |
|
2586 if (tryFold(&result) || evaluateConstantOperands(&result)) { |
|
2587 if (type() == MIRType_Int32) |
|
2588 return MConstant::New(alloc, Int32Value(result)); |
|
2589 |
|
2590 JS_ASSERT(type() == MIRType_Boolean); |
|
2591 return MConstant::New(alloc, BooleanValue(result)); |
|
2592 } |
|
2593 |
|
2594 return this; |
|
2595 } |
|
2596 |
|
2597 void |
|
2598 MCompare::trySpecializeFloat32(TempAllocator &alloc) |
|
2599 { |
|
2600 MDefinition *lhs = getOperand(0); |
|
2601 MDefinition *rhs = getOperand(1); |
|
2602 |
|
2603 if (lhs->canProduceFloat32() && rhs->canProduceFloat32() && compareType_ == Compare_Double) { |
|
2604 compareType_ = Compare_Float32; |
|
2605 } else { |
|
2606 if (lhs->type() == MIRType_Float32) |
|
2607 ConvertDefinitionToDouble<0>(alloc, lhs, this); |
|
2608 if (rhs->type() == MIRType_Float32) |
|
2609 ConvertDefinitionToDouble<1>(alloc, rhs, this); |
|
2610 } |
|
2611 } |
|
2612 |
|
2613 void |
|
2614 MCompare::filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined, |
|
2615 bool *filtersNull) |
|
2616 { |
|
2617 *filtersNull = *filtersUndefined = false; |
|
2618 *subject = nullptr; |
|
2619 |
|
2620 if (compareType() != Compare_Undefined && compareType() != Compare_Null) |
|
2621 return; |
|
2622 |
|
2623 JS_ASSERT(jsop() == JSOP_STRICTNE || jsop() == JSOP_NE || |
|
2624 jsop() == JSOP_STRICTEQ || jsop() == JSOP_EQ); |
|
2625 |
|
2626 // JSOP_*NE only removes undefined/null from if/true branch |
|
2627 if (!trueBranch && (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE)) |
|
2628 return; |
|
2629 |
|
2630 // JSOP_*EQ only removes undefined/null from else/false branch |
|
2631 if (trueBranch && (jsop() == JSOP_STRICTEQ || jsop() == JSOP_EQ)) |
|
2632 return; |
|
2633 |
|
2634 if (jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE) { |
|
2635 *filtersUndefined = compareType() == Compare_Undefined; |
|
2636 *filtersNull = compareType() == Compare_Null; |
|
2637 } else { |
|
2638 *filtersUndefined = *filtersNull = true; |
|
2639 } |
|
2640 |
|
2641 *subject = lhs(); |
|
2642 } |
|
2643 |
|
2644 void |
|
2645 MNot::infer() |
|
2646 { |
|
2647 JS_ASSERT(operandMightEmulateUndefined()); |
|
2648 |
|
2649 if (!MaybeEmulatesUndefined(getOperand(0))) |
|
2650 markOperandCantEmulateUndefined(); |
|
2651 } |
|
2652 |
|
2653 MDefinition * |
|
2654 MNot::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
2655 { |
|
2656 // Fold if the input is constant |
|
2657 if (operand()->isConstant()) { |
|
2658 bool result = operand()->toConstant()->valueToBoolean(); |
|
2659 if (type() == MIRType_Int32) |
|
2660 return MConstant::New(alloc, Int32Value(!result)); |
|
2661 |
|
2662 // ToBoolean can't cause side effects, so this is safe. |
|
2663 return MConstant::New(alloc, BooleanValue(!result)); |
|
2664 } |
|
2665 |
|
2666 // NOT of an undefined or null value is always true |
|
2667 if (operand()->type() == MIRType_Undefined || operand()->type() == MIRType_Null) |
|
2668 return MConstant::New(alloc, BooleanValue(true)); |
|
2669 |
|
2670 // NOT of an object that can't emulate undefined is always false. |
|
2671 if (operand()->type() == MIRType_Object && !operandMightEmulateUndefined()) |
|
2672 return MConstant::New(alloc, BooleanValue(false)); |
|
2673 |
|
2674 return this; |
|
2675 } |
|
2676 |
|
2677 void |
|
2678 MNot::trySpecializeFloat32(TempAllocator &alloc) |
|
2679 { |
|
2680 MDefinition *in = input(); |
|
2681 if (!in->canProduceFloat32() && in->type() == MIRType_Float32) |
|
2682 ConvertDefinitionToDouble<0>(alloc, in, this); |
|
2683 } |
|
2684 |
|
2685 void |
|
2686 MBeta::printOpcode(FILE *fp) const |
|
2687 { |
|
2688 MDefinition::printOpcode(fp); |
|
2689 |
|
2690 Sprinter sp(GetIonContext()->cx); |
|
2691 sp.init(); |
|
2692 comparison_->print(sp); |
|
2693 fprintf(fp, " %s", sp.string()); |
|
2694 } |
|
2695 |
|
2696 bool |
|
2697 MNewObject::shouldUseVM() const |
|
2698 { |
|
2699 return templateObject()->hasSingletonType() || |
|
2700 templateObject()->hasDynamicSlots(); |
|
2701 } |
|
2702 |
|
2703 bool |
|
2704 MNewArray::shouldUseVM() const |
|
2705 { |
|
2706 JS_ASSERT(count() < JSObject::NELEMENTS_LIMIT); |
|
2707 |
|
2708 size_t arraySlots = |
|
2709 gc::GetGCKindSlots(templateObject()->tenuredGetAllocKind()) - ObjectElements::VALUES_PER_HEADER; |
|
2710 |
|
2711 // Allocate space using the VMCall |
|
2712 // when mir hints it needs to get allocated immediately, |
|
2713 // but only when data doesn't fit the available array slots. |
|
2714 bool allocating = isAllocating() && count() > arraySlots; |
|
2715 |
|
2716 return templateObject()->hasSingletonType() || allocating; |
|
2717 } |
|
2718 |
|
2719 bool |
|
2720 MLoadFixedSlot::mightAlias(const MDefinition *store) const |
|
2721 { |
|
2722 if (store->isStoreFixedSlot() && store->toStoreFixedSlot()->slot() != slot()) |
|
2723 return false; |
|
2724 return true; |
|
2725 } |
|
2726 |
|
2727 bool |
|
2728 MAsmJSLoadHeap::mightAlias(const MDefinition *def) const |
|
2729 { |
|
2730 if (def->isAsmJSStoreHeap()) { |
|
2731 const MAsmJSStoreHeap *store = def->toAsmJSStoreHeap(); |
|
2732 if (store->viewType() != viewType()) |
|
2733 return true; |
|
2734 if (!ptr()->isConstant() || !store->ptr()->isConstant()) |
|
2735 return true; |
|
2736 const MConstant *otherPtr = store->ptr()->toConstant(); |
|
2737 return ptr()->toConstant()->value() == otherPtr->value(); |
|
2738 } |
|
2739 return true; |
|
2740 } |
|
2741 |
|
2742 bool |
|
2743 MAsmJSLoadHeap::congruentTo(const MDefinition *ins) const |
|
2744 { |
|
2745 if (!ins->isAsmJSLoadHeap()) |
|
2746 return false; |
|
2747 const MAsmJSLoadHeap *load = ins->toAsmJSLoadHeap(); |
|
2748 return load->viewType() == viewType() && congruentIfOperandsEqual(load); |
|
2749 } |
|
2750 |
|
2751 bool |
|
2752 MAsmJSLoadGlobalVar::mightAlias(const MDefinition *def) const |
|
2753 { |
|
2754 if (def->isAsmJSStoreGlobalVar()) { |
|
2755 const MAsmJSStoreGlobalVar *store = def->toAsmJSStoreGlobalVar(); |
|
2756 return store->globalDataOffset() == globalDataOffset_; |
|
2757 } |
|
2758 return true; |
|
2759 } |
|
2760 |
|
2761 bool |
|
2762 MAsmJSLoadGlobalVar::congruentTo(const MDefinition *ins) const |
|
2763 { |
|
2764 if (ins->isAsmJSLoadGlobalVar()) { |
|
2765 const MAsmJSLoadGlobalVar *load = ins->toAsmJSLoadGlobalVar(); |
|
2766 return globalDataOffset_ == load->globalDataOffset_; |
|
2767 } |
|
2768 return false; |
|
2769 } |
|
2770 |
|
2771 bool |
|
2772 MLoadSlot::mightAlias(const MDefinition *store) const |
|
2773 { |
|
2774 if (store->isStoreSlot() && store->toStoreSlot()->slot() != slot()) |
|
2775 return false; |
|
2776 return true; |
|
2777 } |
|
2778 |
|
2779 void |
|
2780 InlinePropertyTable::trimTo(ObjectVector &targets, BoolVector &choiceSet) |
|
2781 { |
|
2782 for (size_t i = 0; i < targets.length(); i++) { |
|
2783 // If the target was inlined, don't erase the entry. |
|
2784 if (choiceSet[i]) |
|
2785 continue; |
|
2786 |
|
2787 JSFunction *target = &targets[i]->as<JSFunction>(); |
|
2788 |
|
2789 // Eliminate all entries containing the vetoed function from the map. |
|
2790 size_t j = 0; |
|
2791 while (j < numEntries()) { |
|
2792 if (entries_[j]->func == target) |
|
2793 entries_.erase(&entries_[j]); |
|
2794 else |
|
2795 j++; |
|
2796 } |
|
2797 } |
|
2798 } |
|
2799 |
|
2800 void |
|
2801 InlinePropertyTable::trimToTargets(ObjectVector &targets) |
|
2802 { |
|
2803 IonSpew(IonSpew_Inlining, "Got inlineable property cache with %d cases", |
|
2804 (int)numEntries()); |
|
2805 |
|
2806 size_t i = 0; |
|
2807 while (i < numEntries()) { |
|
2808 bool foundFunc = false; |
|
2809 for (size_t j = 0; j < targets.length(); j++) { |
|
2810 if (entries_[i]->func == targets[j]) { |
|
2811 foundFunc = true; |
|
2812 break; |
|
2813 } |
|
2814 } |
|
2815 if (!foundFunc) |
|
2816 entries_.erase(&(entries_[i])); |
|
2817 else |
|
2818 i++; |
|
2819 } |
|
2820 |
|
2821 IonSpew(IonSpew_Inlining, "%d inlineable cases left after trimming to %d targets", |
|
2822 (int)numEntries(), (int)targets.length()); |
|
2823 } |
|
2824 |
|
2825 bool |
|
2826 InlinePropertyTable::hasFunction(JSFunction *func) const |
|
2827 { |
|
2828 for (size_t i = 0; i < numEntries(); i++) { |
|
2829 if (entries_[i]->func == func) |
|
2830 return true; |
|
2831 } |
|
2832 return false; |
|
2833 } |
|
2834 |
|
2835 types::TemporaryTypeSet * |
|
2836 InlinePropertyTable::buildTypeSetForFunction(JSFunction *func) const |
|
2837 { |
|
2838 LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc(); |
|
2839 types::TemporaryTypeSet *types = alloc->new_<types::TemporaryTypeSet>(); |
|
2840 if (!types) |
|
2841 return nullptr; |
|
2842 for (size_t i = 0; i < numEntries(); i++) { |
|
2843 if (entries_[i]->func == func) |
|
2844 types->addType(types::Type::ObjectType(entries_[i]->typeObj), alloc); |
|
2845 } |
|
2846 return types; |
|
2847 } |
|
2848 |
|
2849 void * |
|
2850 MLoadTypedArrayElementStatic::base() const |
|
2851 { |
|
2852 return typedArray_->viewData(); |
|
2853 } |
|
2854 |
|
2855 size_t |
|
2856 MLoadTypedArrayElementStatic::length() const |
|
2857 { |
|
2858 return typedArray_->byteLength(); |
|
2859 } |
|
2860 |
|
2861 void * |
|
2862 MStoreTypedArrayElementStatic::base() const |
|
2863 { |
|
2864 return typedArray_->viewData(); |
|
2865 } |
|
2866 |
|
2867 bool |
|
2868 MGetElementCache::allowDoubleResult() const |
|
2869 { |
|
2870 if (!resultTypeSet()) |
|
2871 return true; |
|
2872 |
|
2873 return resultTypeSet()->hasType(types::Type::DoubleType()); |
|
2874 } |
|
2875 |
|
2876 size_t |
|
2877 MStoreTypedArrayElementStatic::length() const |
|
2878 { |
|
2879 return typedArray_->byteLength(); |
|
2880 } |
|
2881 |
|
2882 bool |
|
2883 MGetPropertyPolymorphic::mightAlias(const MDefinition *store) const |
|
2884 { |
|
2885 // Allow hoisting this instruction if the store does not write to a |
|
2886 // slot read by this instruction. |
|
2887 |
|
2888 if (!store->isStoreFixedSlot() && !store->isStoreSlot()) |
|
2889 return true; |
|
2890 |
|
2891 for (size_t i = 0; i < numShapes(); i++) { |
|
2892 const Shape *shape = this->shape(i); |
|
2893 if (shape->slot() < shape->numFixedSlots()) { |
|
2894 // Fixed slot. |
|
2895 uint32_t slot = shape->slot(); |
|
2896 if (store->isStoreFixedSlot() && store->toStoreFixedSlot()->slot() != slot) |
|
2897 continue; |
|
2898 if (store->isStoreSlot()) |
|
2899 continue; |
|
2900 } else { |
|
2901 // Dynamic slot. |
|
2902 uint32_t slot = shape->slot() - shape->numFixedSlots(); |
|
2903 if (store->isStoreSlot() && store->toStoreSlot()->slot() != slot) |
|
2904 continue; |
|
2905 if (store->isStoreFixedSlot()) |
|
2906 continue; |
|
2907 } |
|
2908 |
|
2909 return true; |
|
2910 } |
|
2911 |
|
2912 return false; |
|
2913 } |
|
2914 |
|
2915 void |
|
2916 MGetPropertyCache::setBlock(MBasicBlock *block) |
|
2917 { |
|
2918 MDefinition::setBlock(block); |
|
2919 // Track where we started. |
|
2920 if (!location_.pc) { |
|
2921 location_.pc = block->trackedPc(); |
|
2922 location_.script = block->info().script(); |
|
2923 } |
|
2924 } |
|
2925 |
|
2926 bool |
|
2927 MGetPropertyCache::updateForReplacement(MDefinition *ins) { |
|
2928 MGetPropertyCache *other = ins->toGetPropertyCache(); |
|
2929 location_.append(&other->location_); |
|
2930 return true; |
|
2931 } |
|
2932 |
|
2933 MDefinition * |
|
2934 MAsmJSUnsignedToDouble::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
2935 { |
|
2936 if (input()->isConstant()) { |
|
2937 const Value &v = input()->toConstant()->value(); |
|
2938 if (v.isInt32()) |
|
2939 return MConstant::New(alloc, DoubleValue(uint32_t(v.toInt32()))); |
|
2940 } |
|
2941 |
|
2942 return this; |
|
2943 } |
|
2944 |
|
2945 MDefinition * |
|
2946 MAsmJSUnsignedToFloat32::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
|
2947 { |
|
2948 if (input()->isConstant()) { |
|
2949 const Value &v = input()->toConstant()->value(); |
|
2950 if (v.isInt32()) { |
|
2951 double dval = double(uint32_t(v.toInt32())); |
|
2952 if (IsFloat32Representable(dval)) |
|
2953 return MConstant::NewAsmJS(alloc, JS::Float32Value(float(dval)), MIRType_Float32); |
|
2954 } |
|
2955 } |
|
2956 |
|
2957 return this; |
|
2958 } |
|
2959 |
|
2960 MAsmJSCall * |
|
2961 MAsmJSCall::New(TempAllocator &alloc, const CallSiteDesc &desc, Callee callee, |
|
2962 const Args &args, MIRType resultType, size_t spIncrement) |
|
2963 { |
|
2964 MAsmJSCall *call = new(alloc) MAsmJSCall(desc, callee, spIncrement); |
|
2965 call->setResultType(resultType); |
|
2966 |
|
2967 if (!call->argRegs_.init(alloc, args.length())) |
|
2968 return nullptr; |
|
2969 for (size_t i = 0; i < call->argRegs_.length(); i++) |
|
2970 call->argRegs_[i] = args[i].reg; |
|
2971 |
|
2972 if (!call->operands_.init(alloc, call->argRegs_.length() + (callee.which() == Callee::Dynamic ? 1 : 0))) |
|
2973 return nullptr; |
|
2974 for (size_t i = 0; i < call->argRegs_.length(); i++) |
|
2975 call->setOperand(i, args[i].def); |
|
2976 if (callee.which() == Callee::Dynamic) |
|
2977 call->setOperand(call->argRegs_.length(), callee.dynamic()); |
|
2978 |
|
2979 return call; |
|
2980 } |
|
2981 |
|
2982 void |
|
2983 MSqrt::trySpecializeFloat32(TempAllocator &alloc) { |
|
2984 if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) { |
|
2985 if (input()->type() == MIRType_Float32) |
|
2986 ConvertDefinitionToDouble<0>(alloc, input(), this); |
|
2987 return; |
|
2988 } |
|
2989 |
|
2990 setResultType(MIRType_Float32); |
|
2991 setPolicyType(MIRType_Float32); |
|
2992 } |
|
2993 |
|
2994 bool |
|
2995 jit::ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id) |
|
2996 { |
|
2997 if (obj->mightBeType(MIRType_String)) |
|
2998 return false; |
|
2999 |
|
3000 if (id->type() != MIRType_Int32 && id->type() != MIRType_Double) |
|
3001 return false; |
|
3002 |
|
3003 types::TemporaryTypeSet *types = obj->resultTypeSet(); |
|
3004 if (!types) |
|
3005 return false; |
|
3006 |
|
3007 // Typed arrays are native classes but do not have dense elements. |
|
3008 const Class *clasp = types->getKnownClass(); |
|
3009 return clasp && clasp->isNative() && !IsTypedArrayClass(clasp); |
|
3010 } |
|
3011 |
|
3012 bool |
|
3013 jit::ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id, |
|
3014 ScalarTypeDescr::Type *arrayType) |
|
3015 { |
|
3016 if (obj->mightBeType(MIRType_String)) |
|
3017 return false; |
|
3018 |
|
3019 if (id->type() != MIRType_Int32 && id->type() != MIRType_Double) |
|
3020 return false; |
|
3021 |
|
3022 types::TemporaryTypeSet *types = obj->resultTypeSet(); |
|
3023 if (!types) |
|
3024 return false; |
|
3025 |
|
3026 *arrayType = (ScalarTypeDescr::Type) types->getTypedArrayType(); |
|
3027 return *arrayType != ScalarTypeDescr::TYPE_MAX; |
|
3028 } |
|
3029 |
|
3030 bool |
|
3031 jit::ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj) |
|
3032 { |
|
3033 types::TemporaryTypeSet *types = obj->resultTypeSet(); |
|
3034 return types && !types->hasObjectFlags(constraints, types::OBJECT_FLAG_NON_PACKED); |
|
3035 } |
|
3036 |
|
3037 bool |
|
3038 jit::ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints, |
|
3039 MDefinition *obj) |
|
3040 { |
|
3041 types::TemporaryTypeSet *types = obj->resultTypeSet(); |
|
3042 |
|
3043 if (!types || types->hasObjectFlags(constraints, types::OBJECT_FLAG_LENGTH_OVERFLOW)) |
|
3044 return true; |
|
3045 |
|
3046 return types::TypeCanHaveExtraIndexedProperties(constraints, types); |
|
3047 } |
|
3048 |
|
3049 MIRType |
|
3050 jit::DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj) |
|
3051 { |
|
3052 types::TemporaryTypeSet *types = obj->resultTypeSet(); |
|
3053 MIRType elementType = MIRType_None; |
|
3054 unsigned count = types->getObjectCount(); |
|
3055 |
|
3056 for (unsigned i = 0; i < count; i++) { |
|
3057 types::TypeObjectKey *object = types->getObject(i); |
|
3058 if (!object) |
|
3059 continue; |
|
3060 |
|
3061 if (object->unknownProperties()) |
|
3062 return MIRType_None; |
|
3063 |
|
3064 types::HeapTypeSetKey elementTypes = object->property(JSID_VOID); |
|
3065 |
|
3066 MIRType type = elementTypes.knownMIRType(constraints); |
|
3067 if (type == MIRType_None) |
|
3068 return MIRType_None; |
|
3069 |
|
3070 if (elementType == MIRType_None) |
|
3071 elementType = type; |
|
3072 else if (elementType != type) |
|
3073 return MIRType_None; |
|
3074 } |
|
3075 |
|
3076 return elementType; |
|
3077 } |
|
3078 |
|
3079 static bool |
|
3080 PropertyReadNeedsTypeBarrier(types::CompilerConstraintList *constraints, |
|
3081 types::TypeObjectKey *object, PropertyName *name, |
|
3082 types::TypeSet *observed) |
|
3083 { |
|
3084 // If the object being read from has types for the property which haven't |
|
3085 // been observed at this access site, the read could produce a new type and |
|
3086 // a barrier is needed. Note that this only covers reads from properties |
|
3087 // which are accounted for by type information, i.e. native data properties |
|
3088 // and elements. |
|
3089 // |
|
3090 // We also need a barrier if the object is a proxy, because then all bets |
|
3091 // are off, just as if it has unknown properties. |
|
3092 if (object->unknownProperties() || observed->empty() || |
|
3093 object->clasp()->isProxy()) |
|
3094 { |
|
3095 return true; |
|
3096 } |
|
3097 |
|
3098 jsid id = name ? NameToId(name) : JSID_VOID; |
|
3099 types::HeapTypeSetKey property = object->property(id); |
|
3100 if (property.maybeTypes() && !TypeSetIncludes(observed, MIRType_Value, property.maybeTypes())) |
|
3101 return true; |
|
3102 |
|
3103 // Type information for global objects is not required to reflect the |
|
3104 // initial 'undefined' value for properties, in particular global |
|
3105 // variables declared with 'var'. Until the property is assigned a value |
|
3106 // other than undefined, a barrier is required. |
|
3107 if (JSObject *obj = object->singleton()) { |
|
3108 if (name && types::CanHaveEmptyPropertyTypesForOwnProperty(obj) && |
|
3109 (!property.maybeTypes() || property.maybeTypes()->empty())) |
|
3110 { |
|
3111 return true; |
|
3112 } |
|
3113 } |
|
3114 |
|
3115 property.freeze(constraints); |
|
3116 return false; |
|
3117 } |
|
3118 |
|
3119 bool |
|
3120 jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx, |
|
3121 types::CompilerConstraintList *constraints, |
|
3122 types::TypeObjectKey *object, PropertyName *name, |
|
3123 types::TemporaryTypeSet *observed, bool updateObserved) |
|
3124 { |
|
3125 // If this access has never executed, try to add types to the observed set |
|
3126 // according to any property which exists on the object or its prototype. |
|
3127 if (updateObserved && observed->empty() && name) { |
|
3128 JSObject *obj; |
|
3129 if (object->singleton()) |
|
3130 obj = object->singleton(); |
|
3131 else if (object->hasTenuredProto()) |
|
3132 obj = object->proto().toObjectOrNull(); |
|
3133 else |
|
3134 obj = nullptr; |
|
3135 |
|
3136 while (obj) { |
|
3137 if (!obj->getClass()->isNative()) |
|
3138 break; |
|
3139 |
|
3140 types::TypeObjectKey *typeObj = types::TypeObjectKey::get(obj); |
|
3141 if (propertycx) |
|
3142 typeObj->ensureTrackedProperty(propertycx, NameToId(name)); |
|
3143 |
|
3144 if (!typeObj->unknownProperties()) { |
|
3145 types::HeapTypeSetKey property = typeObj->property(NameToId(name)); |
|
3146 if (property.maybeTypes()) { |
|
3147 types::TypeSet::TypeList types; |
|
3148 if (!property.maybeTypes()->enumerateTypes(&types)) |
|
3149 break; |
|
3150 if (types.length()) { |
|
3151 // Note: the return value here is ignored. |
|
3152 observed->addType(types[0], GetIonContext()->temp->lifoAlloc()); |
|
3153 break; |
|
3154 } |
|
3155 } |
|
3156 } |
|
3157 |
|
3158 if (!obj->hasTenuredProto()) |
|
3159 break; |
|
3160 obj = obj->getProto(); |
|
3161 } |
|
3162 } |
|
3163 |
|
3164 return PropertyReadNeedsTypeBarrier(constraints, object, name, observed); |
|
3165 } |
|
3166 |
|
3167 bool |
|
3168 jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx, |
|
3169 types::CompilerConstraintList *constraints, |
|
3170 MDefinition *obj, PropertyName *name, |
|
3171 types::TemporaryTypeSet *observed) |
|
3172 { |
|
3173 if (observed->unknown()) |
|
3174 return false; |
|
3175 |
|
3176 types::TypeSet *types = obj->resultTypeSet(); |
|
3177 if (!types || types->unknownObject()) |
|
3178 return true; |
|
3179 |
|
3180 bool updateObserved = types->getObjectCount() == 1; |
|
3181 for (size_t i = 0; i < types->getObjectCount(); i++) { |
|
3182 types::TypeObjectKey *object = types->getObject(i); |
|
3183 if (object) { |
|
3184 if (PropertyReadNeedsTypeBarrier(propertycx, constraints, object, name, |
|
3185 observed, updateObserved)) |
|
3186 { |
|
3187 return true; |
|
3188 } |
|
3189 } |
|
3190 } |
|
3191 |
|
3192 return false; |
|
3193 } |
|
3194 |
|
3195 bool |
|
3196 jit::PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints, |
|
3197 MDefinition *obj, PropertyName *name, |
|
3198 types::TemporaryTypeSet *observed) |
|
3199 { |
|
3200 if (observed->unknown()) |
|
3201 return false; |
|
3202 |
|
3203 types::TypeSet *types = obj->resultTypeSet(); |
|
3204 if (!types || types->unknownObject()) |
|
3205 return true; |
|
3206 |
|
3207 for (size_t i = 0; i < types->getObjectCount(); i++) { |
|
3208 types::TypeObjectKey *object = types->getObject(i); |
|
3209 if (!object) |
|
3210 continue; |
|
3211 while (true) { |
|
3212 if (!object->hasTenuredProto()) |
|
3213 return true; |
|
3214 if (!object->proto().isObject()) |
|
3215 break; |
|
3216 object = types::TypeObjectKey::get(object->proto().toObject()); |
|
3217 if (PropertyReadNeedsTypeBarrier(constraints, object, name, observed)) |
|
3218 return true; |
|
3219 } |
|
3220 } |
|
3221 |
|
3222 return false; |
|
3223 } |
|
3224 |
|
3225 bool |
|
3226 jit::PropertyReadIsIdempotent(types::CompilerConstraintList *constraints, |
|
3227 MDefinition *obj, PropertyName *name) |
|
3228 { |
|
3229 // Determine if reading a property from obj is likely to be idempotent. |
|
3230 |
|
3231 types::TypeSet *types = obj->resultTypeSet(); |
|
3232 if (!types || types->unknownObject()) |
|
3233 return false; |
|
3234 |
|
3235 for (size_t i = 0; i < types->getObjectCount(); i++) { |
|
3236 types::TypeObjectKey *object = types->getObject(i); |
|
3237 if (object) { |
|
3238 if (object->unknownProperties()) |
|
3239 return false; |
|
3240 |
|
3241 // Check if the property has been reconfigured or is a getter. |
|
3242 types::HeapTypeSetKey property = object->property(NameToId(name)); |
|
3243 if (property.nonData(constraints)) |
|
3244 return false; |
|
3245 } |
|
3246 } |
|
3247 |
|
3248 return true; |
|
3249 } |
|
3250 |
|
3251 void |
|
3252 jit::AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name, |
|
3253 types::TemporaryTypeSet *observed) |
|
3254 { |
|
3255 // Add objects to observed which *could* be observed by reading name from obj, |
|
3256 // to hopefully avoid unnecessary type barriers and code invalidations. |
|
3257 |
|
3258 LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc(); |
|
3259 |
|
3260 types::TemporaryTypeSet *types = obj->resultTypeSet(); |
|
3261 if (!types || types->unknownObject()) { |
|
3262 observed->addType(types::Type::AnyObjectType(), alloc); |
|
3263 return; |
|
3264 } |
|
3265 |
|
3266 for (size_t i = 0; i < types->getObjectCount(); i++) { |
|
3267 types::TypeObjectKey *object = types->getObject(i); |
|
3268 if (!object) |
|
3269 continue; |
|
3270 |
|
3271 if (object->unknownProperties()) { |
|
3272 observed->addType(types::Type::AnyObjectType(), alloc); |
|
3273 return; |
|
3274 } |
|
3275 |
|
3276 jsid id = name ? NameToId(name) : JSID_VOID; |
|
3277 types::HeapTypeSetKey property = object->property(id); |
|
3278 types::HeapTypeSet *types = property.maybeTypes(); |
|
3279 if (!types) |
|
3280 continue; |
|
3281 |
|
3282 if (types->unknownObject()) { |
|
3283 observed->addType(types::Type::AnyObjectType(), alloc); |
|
3284 return; |
|
3285 } |
|
3286 |
|
3287 for (size_t i = 0; i < types->getObjectCount(); i++) { |
|
3288 types::TypeObjectKey *object = types->getObject(i); |
|
3289 if (object) |
|
3290 observed->addType(types::Type::ObjectType(object), alloc); |
|
3291 } |
|
3292 } |
|
3293 } |
|
3294 |
|
3295 static bool |
|
3296 TryAddTypeBarrierForWrite(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
|
3297 MBasicBlock *current, types::TemporaryTypeSet *objTypes, |
|
3298 PropertyName *name, MDefinition **pvalue) |
|
3299 { |
|
3300 // Return whether pvalue was modified to include a type barrier ensuring |
|
3301 // that writing the value to objTypes/id will not require changing type |
|
3302 // information. |
|
3303 |
|
3304 // All objects in the set must have the same types for name. Otherwise, we |
|
3305 // could bail out without subsequently triggering a type change that |
|
3306 // invalidates the compiled code. |
|
3307 Maybe<types::HeapTypeSetKey> aggregateProperty; |
|
3308 |
|
3309 for (size_t i = 0; i < objTypes->getObjectCount(); i++) { |
|
3310 types::TypeObjectKey *object = objTypes->getObject(i); |
|
3311 if (!object) |
|
3312 continue; |
|
3313 |
|
3314 if (object->unknownProperties()) |
|
3315 return false; |
|
3316 |
|
3317 jsid id = name ? NameToId(name) : JSID_VOID; |
|
3318 types::HeapTypeSetKey property = object->property(id); |
|
3319 if (!property.maybeTypes()) |
|
3320 return false; |
|
3321 |
|
3322 if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet())) |
|
3323 return false; |
|
3324 |
|
3325 // This freeze is not required for correctness, but ensures that we |
|
3326 // will recompile if the property types change and the barrier can |
|
3327 // potentially be removed. |
|
3328 property.freeze(constraints); |
|
3329 |
|
3330 if (aggregateProperty.empty()) { |
|
3331 aggregateProperty.construct(property); |
|
3332 } else { |
|
3333 if (!aggregateProperty.ref().maybeTypes()->isSubset(property.maybeTypes()) || |
|
3334 !property.maybeTypes()->isSubset(aggregateProperty.ref().maybeTypes())) |
|
3335 { |
|
3336 return false; |
|
3337 } |
|
3338 } |
|
3339 } |
|
3340 |
|
3341 JS_ASSERT(!aggregateProperty.empty()); |
|
3342 |
|
3343 MIRType propertyType = aggregateProperty.ref().knownMIRType(constraints); |
|
3344 switch (propertyType) { |
|
3345 case MIRType_Boolean: |
|
3346 case MIRType_Int32: |
|
3347 case MIRType_Double: |
|
3348 case MIRType_String: { |
|
3349 // The property is a particular primitive type, guard by unboxing the |
|
3350 // value before the write. |
|
3351 if (!(*pvalue)->mightBeType(propertyType)) { |
|
3352 // The value's type does not match the property type. Just do a VM |
|
3353 // call as it will always trigger invalidation of the compiled code. |
|
3354 JS_ASSERT_IF((*pvalue)->type() != MIRType_Value, (*pvalue)->type() != propertyType); |
|
3355 return false; |
|
3356 } |
|
3357 MInstruction *ins = MUnbox::New(alloc, *pvalue, propertyType, MUnbox::Fallible); |
|
3358 current->add(ins); |
|
3359 *pvalue = ins; |
|
3360 return true; |
|
3361 } |
|
3362 default:; |
|
3363 } |
|
3364 |
|
3365 if ((*pvalue)->type() != MIRType_Value) |
|
3366 return false; |
|
3367 |
|
3368 types::TemporaryTypeSet *types = aggregateProperty.ref().maybeTypes()->clone(alloc.lifoAlloc()); |
|
3369 if (!types) |
|
3370 return false; |
|
3371 |
|
3372 MInstruction *ins = MMonitorTypes::New(alloc, *pvalue, types); |
|
3373 current->add(ins); |
|
3374 return true; |
|
3375 } |
|
3376 |
|
3377 static MInstruction * |
|
3378 AddTypeGuard(TempAllocator &alloc, MBasicBlock *current, MDefinition *obj, |
|
3379 types::TypeObjectKey *type, bool bailOnEquality) |
|
3380 { |
|
3381 MInstruction *guard; |
|
3382 |
|
3383 if (type->isTypeObject()) |
|
3384 guard = MGuardObjectType::New(alloc, obj, type->asTypeObject(), bailOnEquality); |
|
3385 else |
|
3386 guard = MGuardObjectIdentity::New(alloc, obj, type->asSingleObject(), bailOnEquality); |
|
3387 |
|
3388 current->add(guard); |
|
3389 |
|
3390 // For now, never move type object guards. |
|
3391 guard->setNotMovable(); |
|
3392 |
|
3393 return guard; |
|
3394 } |
|
3395 |
|
3396 bool |
|
3397 jit::PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
|
3398 MBasicBlock *current, MDefinition **pobj, |
|
3399 PropertyName *name, MDefinition **pvalue, bool canModify) |
|
3400 { |
|
3401 // If any value being written is not reflected in the type information for |
|
3402 // objects which obj could represent, a type barrier is needed when writing |
|
3403 // the value. As for propertyReadNeedsTypeBarrier, this only applies for |
|
3404 // properties that are accounted for by type information, i.e. normal data |
|
3405 // properties and elements. |
|
3406 |
|
3407 types::TemporaryTypeSet *types = (*pobj)->resultTypeSet(); |
|
3408 if (!types || types->unknownObject()) |
|
3409 return true; |
|
3410 |
|
3411 // If all of the objects being written to have property types which already |
|
3412 // reflect the value, no barrier at all is needed. Additionally, if all |
|
3413 // objects being written to have the same types for the property, and those |
|
3414 // types do *not* reflect the value, add a type barrier for the value. |
|
3415 |
|
3416 bool success = true; |
|
3417 for (size_t i = 0; i < types->getObjectCount(); i++) { |
|
3418 types::TypeObjectKey *object = types->getObject(i); |
|
3419 if (!object || object->unknownProperties()) |
|
3420 continue; |
|
3421 |
|
3422 // TI doesn't track TypedArray objects and should never insert a type |
|
3423 // barrier for them. |
|
3424 if (!name && IsTypedArrayClass(object->clasp())) |
|
3425 continue; |
|
3426 |
|
3427 jsid id = name ? NameToId(name) : JSID_VOID; |
|
3428 types::HeapTypeSetKey property = object->property(id); |
|
3429 if (!TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet())) { |
|
3430 // Either pobj or pvalue needs to be modified to filter out the |
|
3431 // types which the value could have but are not in the property, |
|
3432 // or a VM call is required. A VM call is always required if pobj |
|
3433 // and pvalue cannot be modified. |
|
3434 if (!canModify) |
|
3435 return true; |
|
3436 success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue); |
|
3437 break; |
|
3438 } |
|
3439 } |
|
3440 |
|
3441 if (success) |
|
3442 return false; |
|
3443 |
|
3444 // If all of the objects except one have property types which reflect the |
|
3445 // value, and the remaining object has no types at all for the property, |
|
3446 // add a guard that the object does not have that remaining object's type. |
|
3447 |
|
3448 if (types->getObjectCount() <= 1) |
|
3449 return true; |
|
3450 |
|
3451 types::TypeObjectKey *excluded = nullptr; |
|
3452 for (size_t i = 0; i < types->getObjectCount(); i++) { |
|
3453 types::TypeObjectKey *object = types->getObject(i); |
|
3454 if (!object || object->unknownProperties()) |
|
3455 continue; |
|
3456 if (!name && IsTypedArrayClass(object->clasp())) |
|
3457 continue; |
|
3458 |
|
3459 jsid id = name ? NameToId(name) : JSID_VOID; |
|
3460 types::HeapTypeSetKey property = object->property(id); |
|
3461 if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet())) |
|
3462 continue; |
|
3463 |
|
3464 if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded) |
|
3465 return true; |
|
3466 excluded = object; |
|
3467 } |
|
3468 |
|
3469 JS_ASSERT(excluded); |
|
3470 |
|
3471 *pobj = AddTypeGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true); |
|
3472 return false; |
|
3473 } |