Bug 1920241 - Update Persisted Search preferences text - r=daleharvey,fluent-reviewer...
[gecko.git] / js / src / jit / TypePolicy.cpp
blob5468c395a140487b53830b23c72f20f7ecc8809d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
7 #include "jit/TypePolicy.h"
9 #include "jit/JitAllocPolicy.h"
10 #include "jit/MIR-wasm.h"
11 #include "jit/MIR.h"
12 #include "jit/MIRGraph.h"
13 #include "js/ScalarType.h" // js::Scalar::Type
15 using namespace js;
16 using namespace js::jit;
18 static void EnsureOperandNotFloat32(TempAllocator& alloc, MInstruction* def,
19 unsigned op) {
20 MDefinition* in = def->getOperand(op);
21 if (in->type() == MIRType::Float32) {
22 MToDouble* replace = MToDouble::New(alloc, in);
23 def->block()->insertBefore(def, replace);
24 if (def->isRecoveredOnBailout()) {
25 replace->setRecoveredOnBailout();
27 def->replaceOperand(op, replace);
31 template <class T>
32 [[nodiscard]] static bool ConvertOperand(TempAllocator& alloc,
33 MInstruction* def, unsigned op,
34 MIRType expected) {
35 MDefinition* in = def->getOperand(op);
36 if (in->type() == expected) {
37 return true;
40 auto* replace = T::New(alloc, in);
41 def->block()->insertBefore(def, replace);
42 def->replaceOperand(op, replace);
44 return replace->typePolicy()->adjustInputs(alloc, replace);
47 static void SetTypePolicyBailoutKind(MInstruction* newIns,
48 MInstruction* forIns) {
49 // Infallible ToFloat32 or ToDouble doesn't bail out.
50 if ((newIns->isToFloat32() || newIns->isToDouble()) && !newIns->isGuard()) {
51 return;
54 // Ensure we're not introducing TypePolicy bailouts for transpiled CacheIR
55 // instructions. Unbox operations and other guards should have been inserted
56 // by the transpiler.
58 // This avoids a performance cliff because frequent TypePolicy bailouts will
59 // disable Warp compilation instead of invalidating the script.
60 // See bug 1850305.
61 MOZ_ASSERT(forIns->bailoutKind() != BailoutKind::TranspiledCacheIR);
63 newIns->setBailoutKind(BailoutKind::TypePolicy);
66 [[nodiscard]] static bool UnboxOperand(TempAllocator& alloc, MInstruction* def,
67 unsigned op, MIRType expected) {
68 MDefinition* in = def->getOperand(op);
69 if (in->type() == expected) {
70 return true;
73 auto* replace = MUnbox::New(alloc, in, expected, MUnbox::Fallible);
74 SetTypePolicyBailoutKind(replace, def);
75 def->block()->insertBefore(def, replace);
76 def->replaceOperand(op, replace);
78 return replace->typePolicy()->adjustInputs(alloc, replace);
81 MDefinition* js::jit::AlwaysBoxAt(TempAllocator& alloc, MInstruction* at,
82 MDefinition* operand) {
83 MDefinition* boxedOperand = operand;
84 // Replace Float32 by double
85 if (operand->type() == MIRType::Float32) {
86 MInstruction* replace = MToDouble::New(alloc, operand);
87 at->block()->insertBefore(at, replace);
88 boxedOperand = replace;
90 MBox* box = MBox::New(alloc, boxedOperand);
91 at->block()->insertBefore(at, box);
92 return box;
95 static MDefinition* BoxAt(TempAllocator& alloc, MInstruction* at,
96 MDefinition* operand) {
97 if (operand->isUnbox()) {
98 return operand->toUnbox()->input();
100 return AlwaysBoxAt(alloc, at, operand);
103 bool BoxInputsPolicy::staticAdjustInputs(TempAllocator& alloc,
104 MInstruction* ins) {
105 for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
106 MDefinition* in = ins->getOperand(i);
107 if (in->type() == MIRType::Value) {
108 continue;
110 ins->replaceOperand(i, BoxAt(alloc, ins, in));
112 return true;
115 bool ArithPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const {
116 MOZ_ASSERT(IsNumberType(ins->type()));
117 MOZ_ASSERT(ins->type() == MIRType::Double || ins->type() == MIRType::Int32 ||
118 ins->type() == MIRType::Float32);
120 for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
121 MDefinition* in = ins->getOperand(i);
122 if (in->type() == ins->type()) {
123 continue;
126 MInstruction* replace;
128 if (ins->type() == MIRType::Double) {
129 replace = MToDouble::New(alloc, in);
130 } else if (ins->type() == MIRType::Float32) {
131 replace = MToFloat32::New(alloc, in);
132 } else {
133 replace = MToNumberInt32::New(alloc, in);
136 SetTypePolicyBailoutKind(replace, ins);
137 ins->block()->insertBefore(ins, replace);
138 ins->replaceOperand(i, replace);
140 if (!replace->typePolicy()->adjustInputs(alloc, replace)) {
141 return false;
145 return true;
148 bool BigIntArithPolicy::adjustInputs(TempAllocator& alloc,
149 MInstruction* ins) const {
150 MOZ_ASSERT(ins->type() == MIRType::BigInt);
152 for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
153 if (!ConvertOperand<MToBigInt>(alloc, ins, i, MIRType::BigInt)) {
154 return false;
158 return true;
161 bool AllDoublePolicy::staticAdjustInputs(TempAllocator& alloc,
162 MInstruction* ins) {
163 for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
164 MDefinition* in = ins->getOperand(i);
165 if (in->type() == MIRType::Double) {
166 continue;
169 if (!alloc.ensureBallast()) {
170 return false;
172 MInstruction* replace = MToDouble::New(alloc, in);
174 ins->block()->insertBefore(ins, replace);
175 ins->replaceOperand(i, replace);
177 if (!replace->typePolicy()->adjustInputs(alloc, replace)) {
178 return false;
182 return true;
185 bool ComparePolicy::adjustInputs(TempAllocator& alloc,
186 MInstruction* def) const {
187 auto convertOperand = [&](size_t index, MIRType expected) {
188 MDefinition* operand = def->getOperand(index);
189 if (operand->type() == expected) {
190 return true;
192 MInstruction* replace = nullptr;
193 switch (expected) {
194 case MIRType::Double:
195 replace = MToDouble::New(alloc, operand);
196 break;
197 case MIRType::Int32:
198 replace = MToNumberInt32::New(alloc, operand);
199 break;
200 case MIRType::Float32:
201 replace = MToFloat32::New(alloc, operand);
202 break;
203 case MIRType::String:
204 replace =
205 MUnbox::New(alloc, operand, MIRType::String, MUnbox::Fallible);
206 break;
207 case MIRType::Symbol:
208 replace =
209 MUnbox::New(alloc, operand, MIRType::Symbol, MUnbox::Fallible);
210 break;
211 case MIRType::Object:
212 replace =
213 MUnbox::New(alloc, operand, MIRType::Object, MUnbox::Fallible);
214 break;
215 case MIRType::BigInt:
216 replace =
217 MUnbox::New(alloc, operand, MIRType::BigInt, MUnbox::Fallible);
218 break;
219 default:
220 MOZ_CRASH("Unsupported MIRType");
222 replace->setBailoutKind(BailoutKind::TypePolicy);
223 def->block()->insertBefore(def, replace);
224 def->replaceOperand(index, replace);
225 return replace->typePolicy()->adjustInputs(alloc, replace);
228 MOZ_ASSERT(def->isCompare());
229 MCompare* compare = def->toCompare();
230 switch (compare->compareType()) {
231 case MCompare::Compare_Undefined:
232 case MCompare::Compare_Null:
233 MOZ_ASSERT(compare->rhs()->type() == MIRType::Undefined ||
234 compare->rhs()->type() == MIRType::Null);
235 // IF the operand is float32, we must convert it to a double.
236 if (compare->lhs()->type() == MIRType::Float32) {
237 MInstruction* replace = MToDouble::New(alloc, compare->lhs());
238 def->block()->insertBefore(def, replace);
239 def->replaceOperand(0, replace);
240 return replace->typePolicy()->adjustInputs(alloc, replace);
242 // GVN and lowering handle all other types.
243 return true;
244 case MCompare::Compare_Int32:
245 return convertOperand(0, MIRType::Int32) &&
246 convertOperand(1, MIRType::Int32);
247 case MCompare::Compare_IntPtr:
248 case MCompare::Compare_UIntPtr:
249 MOZ_ASSERT(compare->lhs()->type() == MIRType::IntPtr);
250 MOZ_ASSERT(compare->rhs()->type() == MIRType::IntPtr);
251 return true;
252 case MCompare::Compare_Double:
253 return convertOperand(0, MIRType::Double) &&
254 convertOperand(1, MIRType::Double);
255 case MCompare::Compare_Float32:
256 return convertOperand(0, MIRType::Float32) &&
257 convertOperand(1, MIRType::Float32);
258 case MCompare::Compare_String:
259 return convertOperand(0, MIRType::String) &&
260 convertOperand(1, MIRType::String);
261 case MCompare::Compare_Symbol:
262 return convertOperand(0, MIRType::Symbol) &&
263 convertOperand(1, MIRType::Symbol);
264 case MCompare::Compare_Object:
265 return convertOperand(0, MIRType::Object) &&
266 convertOperand(1, MIRType::Object);
267 case MCompare::Compare_BigInt:
268 return convertOperand(0, MIRType::BigInt) &&
269 convertOperand(1, MIRType::BigInt);
270 case MCompare::Compare_BigInt_Int32:
271 return convertOperand(0, MIRType::BigInt) &&
272 convertOperand(1, MIRType::Int32);
273 case MCompare::Compare_BigInt_Double:
274 return convertOperand(0, MIRType::BigInt) &&
275 convertOperand(1, MIRType::Double);
276 case MCompare::Compare_BigInt_String:
277 return convertOperand(0, MIRType::BigInt) &&
278 convertOperand(1, MIRType::String);
279 default:
280 MOZ_CRASH("Unexpected compare type");
283 return true;
286 bool TestPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const {
287 MDefinition* op = ins->getOperand(0);
288 switch (op->type()) {
289 case MIRType::Value:
290 case MIRType::Null:
291 case MIRType::Undefined:
292 case MIRType::Boolean:
293 case MIRType::Int32:
294 case MIRType::Double:
295 case MIRType::Float32:
296 case MIRType::Symbol:
297 case MIRType::BigInt:
298 case MIRType::Object:
299 break;
301 case MIRType::String: {
302 MStringLength* length = MStringLength::New(alloc, op);
303 ins->block()->insertBefore(ins, length);
304 ins->replaceOperand(0, length);
305 break;
308 default:
309 MOZ_ASSERT(IsMagicType(op->type()));
310 ins->replaceOperand(0, BoxAt(alloc, ins, op));
311 break;
313 return true;
316 bool BitwisePolicy::adjustInputs(TempAllocator& alloc,
317 MInstruction* ins) const {
318 MOZ_ASSERT(ins->type() == MIRType::Int32 || ins->type() == MIRType::Double);
320 // This policy works for both unary and binary bitwise operations.
321 for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
322 if (!ConvertOperand<MTruncateToInt32>(alloc, ins, i, MIRType::Int32)) {
323 return false;
327 return true;
330 bool PowPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const {
331 MOZ_ASSERT(ins->type() == MIRType::Int32 || ins->type() == MIRType::Double);
333 if (ins->type() == MIRType::Int32) {
334 // Both operands must be int32.
335 return UnboxedInt32Policy<0>::staticAdjustInputs(alloc, ins) &&
336 UnboxedInt32Policy<1>::staticAdjustInputs(alloc, ins);
339 // Otherwise, input must be a double.
340 if (!DoublePolicy<0>::staticAdjustInputs(alloc, ins)) {
341 return false;
344 // Power may be an int32 or a double. Integers receive a faster path.
345 MDefinition* power = ins->toPow()->power();
346 if (power->isToDouble()) {
347 MDefinition* input = power->toToDouble()->input();
348 if (input->type() == MIRType::Int32) {
349 power->setImplicitlyUsedUnchecked();
350 ins->replaceOperand(1, input);
351 return true;
354 return DoublePolicy<1>::staticAdjustInputs(alloc, ins);
357 bool SignPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const {
358 MOZ_ASSERT(ins->isSign());
359 MIRType specialization = ins->typePolicySpecialization();
361 // MSign is specialized for int32 input types.
362 if (specialization == MIRType::Int32) {
363 return UnboxedInt32Policy<0>::staticAdjustInputs(alloc, ins);
366 // Otherwise convert input to double.
367 MOZ_ASSERT(IsFloatingPointType(specialization));
368 return DoublePolicy<0>::staticAdjustInputs(alloc, ins);
371 template <unsigned Op>
372 bool SymbolPolicy<Op>::staticAdjustInputs(TempAllocator& alloc,
373 MInstruction* ins) {
374 return UnboxOperand(alloc, ins, Op, MIRType::Symbol);
377 template bool SymbolPolicy<0>::staticAdjustInputs(TempAllocator& alloc,
378 MInstruction* ins);
380 template <unsigned Op>
381 bool BooleanPolicy<Op>::staticAdjustInputs(TempAllocator& alloc,
382 MInstruction* ins) {
383 return UnboxOperand(alloc, ins, Op, MIRType::Boolean);
386 template bool BooleanPolicy<0>::staticAdjustInputs(TempAllocator& alloc,
387 MInstruction* ins);
389 template <unsigned Op>
390 bool StringPolicy<Op>::staticAdjustInputs(TempAllocator& alloc,
391 MInstruction* ins) {
392 return UnboxOperand(alloc, ins, Op, MIRType::String);
395 template bool StringPolicy<0>::staticAdjustInputs(TempAllocator& alloc,
396 MInstruction* ins);
397 template bool StringPolicy<1>::staticAdjustInputs(TempAllocator& alloc,
398 MInstruction* ins);
399 template bool StringPolicy<2>::staticAdjustInputs(TempAllocator& alloc,
400 MInstruction* ins);
402 template <unsigned Op>
403 bool ConvertToStringPolicy<Op>::staticAdjustInputs(TempAllocator& alloc,
404 MInstruction* ins) {
405 MDefinition* in = ins->getOperand(Op);
406 if (in->type() == MIRType::String) {
407 return true;
410 MToString* replace =
411 MToString::New(alloc, in, MToString::SideEffectHandling::Bailout);
412 ins->block()->insertBefore(ins, replace);
413 ins->replaceOperand(Op, replace);
415 return ToStringPolicy::staticAdjustInputs(alloc, replace);
418 template bool ConvertToStringPolicy<0>::staticAdjustInputs(TempAllocator& alloc,
419 MInstruction* ins);
420 template bool ConvertToStringPolicy<1>::staticAdjustInputs(TempAllocator& alloc,
421 MInstruction* ins);
422 template bool ConvertToStringPolicy<2>::staticAdjustInputs(TempAllocator& alloc,
423 MInstruction* ins);
425 template <unsigned Op>
426 bool BigIntPolicy<Op>::staticAdjustInputs(TempAllocator& alloc,
427 MInstruction* ins) {
428 return UnboxOperand(alloc, ins, Op, MIRType::BigInt);
431 template bool BigIntPolicy<0>::staticAdjustInputs(TempAllocator& alloc,
432 MInstruction* ins);
433 template bool BigIntPolicy<1>::staticAdjustInputs(TempAllocator& alloc,
434 MInstruction* ins);
436 template <unsigned Op>
437 bool UnboxedInt32Policy<Op>::staticAdjustInputs(TempAllocator& alloc,
438 MInstruction* def) {
439 return UnboxOperand(alloc, def, Op, MIRType::Int32);
442 template bool UnboxedInt32Policy<0>::staticAdjustInputs(TempAllocator& alloc,
443 MInstruction* def);
444 template bool UnboxedInt32Policy<1>::staticAdjustInputs(TempAllocator& alloc,
445 MInstruction* def);
446 template bool UnboxedInt32Policy<2>::staticAdjustInputs(TempAllocator& alloc,
447 MInstruction* def);
448 template bool UnboxedInt32Policy<3>::staticAdjustInputs(TempAllocator& alloc,
449 MInstruction* def);
451 template <unsigned Op>
452 bool Int32OrIntPtrPolicy<Op>::staticAdjustInputs(TempAllocator& alloc,
453 MInstruction* def) {
454 MDefinition* in = def->getOperand(Op);
455 if (in->type() == MIRType::IntPtr) {
456 return true;
459 return UnboxedInt32Policy<Op>::staticAdjustInputs(alloc, def);
462 template bool Int32OrIntPtrPolicy<0>::staticAdjustInputs(TempAllocator& alloc,
463 MInstruction* def);
464 template bool Int32OrIntPtrPolicy<1>::staticAdjustInputs(TempAllocator& alloc,
465 MInstruction* def);
467 template <unsigned Op>
468 bool IntPtrPolicy<Op>::staticAdjustInputs(TempAllocator& alloc,
469 MInstruction* def) {
470 // We don't (yet) support converting other types to IntPtr.
471 MOZ_ASSERT(def->getOperand(Op)->type() == MIRType::IntPtr);
472 return true;
475 template bool IntPtrPolicy<0>::staticAdjustInputs(TempAllocator& alloc,
476 MInstruction* def);
478 template <unsigned Op>
479 bool ConvertToInt32Policy<Op>::staticAdjustInputs(TempAllocator& alloc,
480 MInstruction* def) {
481 return ConvertOperand<MToNumberInt32>(alloc, def, Op, MIRType::Int32);
484 template bool ConvertToInt32Policy<0>::staticAdjustInputs(TempAllocator& alloc,
485 MInstruction* def);
487 template <unsigned Op>
488 bool TruncateToInt32OrToInt64Policy<Op>::staticAdjustInputs(
489 TempAllocator& alloc, MInstruction* def) {
490 MOZ_ASSERT(def->isCompareExchangeTypedArrayElement() ||
491 def->isAtomicExchangeTypedArrayElement() ||
492 def->isAtomicTypedArrayElementBinop());
494 Scalar::Type type;
495 if (def->isCompareExchangeTypedArrayElement()) {
496 type = def->toCompareExchangeTypedArrayElement()->arrayType();
497 } else if (def->isAtomicExchangeTypedArrayElement()) {
498 type = def->toAtomicExchangeTypedArrayElement()->arrayType();
499 } else {
500 type = def->toAtomicTypedArrayElementBinop()->arrayType();
503 if (Scalar::isBigIntType(type)) {
504 return ConvertOperand<MToInt64>(alloc, def, Op, MIRType::Int64);
506 return ConvertOperand<MTruncateToInt32>(alloc, def, Op, MIRType::Int32);
509 template bool TruncateToInt32OrToInt64Policy<2>::staticAdjustInputs(
510 TempAllocator& alloc, MInstruction* def);
511 template bool TruncateToInt32OrToInt64Policy<3>::staticAdjustInputs(
512 TempAllocator& alloc, MInstruction* def);
514 template <unsigned Op>
515 bool DoublePolicy<Op>::staticAdjustInputs(TempAllocator& alloc,
516 MInstruction* def) {
517 return ConvertOperand<MToDouble>(alloc, def, Op, MIRType::Double);
520 template bool DoublePolicy<0>::staticAdjustInputs(TempAllocator& alloc,
521 MInstruction* def);
522 template bool DoublePolicy<1>::staticAdjustInputs(TempAllocator& alloc,
523 MInstruction* def);
525 template <unsigned Op>
526 bool Float32Policy<Op>::staticAdjustInputs(TempAllocator& alloc,
527 MInstruction* def) {
528 return ConvertOperand<MToFloat32>(alloc, def, Op, MIRType::Float32);
531 template bool Float32Policy<0>::staticAdjustInputs(TempAllocator& alloc,
532 MInstruction* def);
533 template bool Float32Policy<1>::staticAdjustInputs(TempAllocator& alloc,
534 MInstruction* def);
535 template bool Float32Policy<2>::staticAdjustInputs(TempAllocator& alloc,
536 MInstruction* def);
538 template <unsigned Op>
539 bool FloatingPointPolicy<Op>::adjustInputs(TempAllocator& alloc,
540 MInstruction* def) const {
541 MIRType policyType = def->typePolicySpecialization();
542 if (policyType == MIRType::Double) {
543 return DoublePolicy<Op>::staticAdjustInputs(alloc, def);
545 return Float32Policy<Op>::staticAdjustInputs(alloc, def);
548 template bool FloatingPointPolicy<0>::adjustInputs(TempAllocator& alloc,
549 MInstruction* def) const;
551 template <unsigned Op>
552 bool NoFloatPolicy<Op>::staticAdjustInputs(TempAllocator& alloc,
553 MInstruction* def) {
554 EnsureOperandNotFloat32(alloc, def, Op);
555 return true;
558 template bool NoFloatPolicy<0>::staticAdjustInputs(TempAllocator& alloc,
559 MInstruction* def);
560 template bool NoFloatPolicy<1>::staticAdjustInputs(TempAllocator& alloc,
561 MInstruction* def);
562 template bool NoFloatPolicy<2>::staticAdjustInputs(TempAllocator& alloc,
563 MInstruction* def);
564 template bool NoFloatPolicy<3>::staticAdjustInputs(TempAllocator& alloc,
565 MInstruction* def);
567 template <unsigned FirstOp>
568 bool NoFloatPolicyAfter<FirstOp>::staticAdjustInputs(TempAllocator& alloc,
569 MInstruction* def) {
570 for (size_t op = FirstOp, e = def->numOperands(); op < e; op++) {
571 EnsureOperandNotFloat32(alloc, def, op);
573 return true;
576 template bool NoFloatPolicyAfter<0>::staticAdjustInputs(TempAllocator& alloc,
577 MInstruction* def);
578 template bool NoFloatPolicyAfter<1>::staticAdjustInputs(TempAllocator& alloc,
579 MInstruction* def);
580 template bool NoFloatPolicyAfter<2>::staticAdjustInputs(TempAllocator& alloc,
581 MInstruction* def);
583 template <unsigned Op>
584 bool BoxPolicy<Op>::staticAdjustInputs(TempAllocator& alloc,
585 MInstruction* ins) {
586 MDefinition* in = ins->getOperand(Op);
587 if (in->type() == MIRType::Value) {
588 return true;
591 ins->replaceOperand(Op, BoxAt(alloc, ins, in));
592 return true;
595 template bool BoxPolicy<0>::staticAdjustInputs(TempAllocator& alloc,
596 MInstruction* ins);
597 template bool BoxPolicy<1>::staticAdjustInputs(TempAllocator& alloc,
598 MInstruction* ins);
599 template bool BoxPolicy<2>::staticAdjustInputs(TempAllocator& alloc,
600 MInstruction* ins);
602 template <unsigned Op, MIRType Type>
603 bool BoxExceptPolicy<Op, Type>::staticAdjustInputs(TempAllocator& alloc,
604 MInstruction* ins) {
605 MDefinition* in = ins->getOperand(Op);
606 if (in->type() == Type) {
607 return true;
609 return BoxPolicy<Op>::staticAdjustInputs(alloc, ins);
612 template bool BoxExceptPolicy<0, MIRType::Object>::staticAdjustInputs(
613 TempAllocator& alloc, MInstruction* ins);
615 template <unsigned Op>
616 bool CacheIdPolicy<Op>::staticAdjustInputs(TempAllocator& alloc,
617 MInstruction* ins) {
618 MDefinition* in = ins->getOperand(Op);
619 switch (in->type()) {
620 case MIRType::Int32:
621 case MIRType::String:
622 case MIRType::Symbol:
623 return true;
624 default:
625 return BoxPolicy<Op>::staticAdjustInputs(alloc, ins);
629 template bool CacheIdPolicy<0>::staticAdjustInputs(TempAllocator& alloc,
630 MInstruction* ins);
631 template bool CacheIdPolicy<1>::staticAdjustInputs(TempAllocator& alloc,
632 MInstruction* ins);
634 bool ToDoublePolicy::staticAdjustInputs(TempAllocator& alloc,
635 MInstruction* ins) {
636 MOZ_ASSERT(ins->isToDouble() || ins->isToFloat32() || ins->isToFloat16());
638 MDefinition* in = ins->getOperand(0);
639 switch (in->type()) {
640 case MIRType::Int32:
641 case MIRType::Float32:
642 case MIRType::Double:
643 case MIRType::Value:
644 case MIRType::Null:
645 case MIRType::Undefined:
646 case MIRType::Boolean:
647 // No need for boxing for these types.
648 return true;
649 case MIRType::Object:
650 case MIRType::String:
651 case MIRType::Symbol:
652 case MIRType::BigInt:
653 // Objects might be effectful. Symbols and BigInts give TypeError.
654 break;
655 default:
656 break;
659 in = BoxAt(alloc, ins, in);
660 ins->replaceOperand(0, in);
661 return true;
664 bool ToInt32Policy::staticAdjustInputs(TempAllocator& alloc,
665 MInstruction* ins) {
666 MOZ_ASSERT(ins->isToNumberInt32() || ins->isTruncateToInt32());
668 IntConversionInputKind conversion = IntConversionInputKind::Any;
669 if (ins->isToNumberInt32()) {
670 conversion = ins->toToNumberInt32()->conversion();
673 MDefinition* in = ins->getOperand(0);
674 switch (in->type()) {
675 case MIRType::Int32:
676 case MIRType::Float32:
677 case MIRType::Double:
678 case MIRType::Value:
679 // No need for boxing for these types.
680 return true;
681 case MIRType::Undefined:
682 // No need for boxing when truncating.
683 if (ins->isTruncateToInt32()) {
684 return true;
686 break;
687 case MIRType::Null:
688 // No need for boxing, when we will convert.
689 if (conversion == IntConversionInputKind::Any) {
690 return true;
692 break;
693 case MIRType::Boolean:
694 // No need for boxing, when we will convert.
695 if (conversion == IntConversionInputKind::Any) {
696 return true;
698 break;
699 case MIRType::Object:
700 case MIRType::String:
701 case MIRType::Symbol:
702 case MIRType::BigInt:
703 // Objects might be effectful. Symbols and BigInts give TypeError.
704 break;
705 default:
706 break;
709 in = BoxAt(alloc, ins, in);
710 ins->replaceOperand(0, in);
711 return true;
714 bool ToBigIntPolicy::staticAdjustInputs(TempAllocator& alloc,
715 MInstruction* ins) {
716 MOZ_ASSERT(ins->isToBigInt());
718 MDefinition* in = ins->getOperand(0);
719 switch (in->type()) {
720 case MIRType::BigInt:
721 case MIRType::Value:
722 // No need for boxing for these types.
723 return true;
724 default:
725 // Any other types need to be boxed.
726 break;
729 in = BoxAt(alloc, ins, in);
730 ins->replaceOperand(0, in);
731 return true;
734 bool ToStringPolicy::staticAdjustInputs(TempAllocator& alloc,
735 MInstruction* ins) {
736 MOZ_ASSERT(ins->isToString());
738 MIRType type = ins->getOperand(0)->type();
739 if (type == MIRType::Object || type == MIRType::Symbol ||
740 type == MIRType::BigInt) {
741 ins->replaceOperand(0, BoxAt(alloc, ins, ins->getOperand(0)));
742 return true;
745 // TODO remove the following line once 966957 has landed
746 EnsureOperandNotFloat32(alloc, ins, 0);
748 return true;
751 bool ToInt64Policy::staticAdjustInputs(TempAllocator& alloc,
752 MInstruction* ins) {
753 MOZ_ASSERT(ins->isToInt64());
755 MDefinition* input = ins->getOperand(0);
756 MIRType type = input->type();
758 switch (type) {
759 case MIRType::BigInt: {
760 auto* replace = MTruncateBigIntToInt64::New(alloc, input);
761 ins->block()->insertBefore(ins, replace);
762 ins->replaceOperand(0, replace);
763 break;
765 // No need for boxing for these types, because they are handled specially
766 // when this instruction is lowered to LIR.
767 case MIRType::Boolean:
768 case MIRType::String:
769 case MIRType::Int64:
770 case MIRType::Value:
771 break;
772 default:
773 ins->replaceOperand(0, BoxAt(alloc, ins, ins->getOperand(0)));
774 break;
777 return true;
780 template <unsigned Op>
781 bool ObjectPolicy<Op>::staticAdjustInputs(TempAllocator& alloc,
782 MInstruction* ins) {
783 MOZ_ASSERT(ins->getOperand(Op)->type() != MIRType::Slots);
784 MOZ_ASSERT(ins->getOperand(Op)->type() != MIRType::Elements);
786 return UnboxOperand(alloc, ins, Op, MIRType::Object);
789 template bool ObjectPolicy<0>::staticAdjustInputs(TempAllocator& alloc,
790 MInstruction* ins);
791 template bool ObjectPolicy<1>::staticAdjustInputs(TempAllocator& alloc,
792 MInstruction* ins);
793 template bool ObjectPolicy<2>::staticAdjustInputs(TempAllocator& alloc,
794 MInstruction* ins);
795 template bool ObjectPolicy<3>::staticAdjustInputs(TempAllocator& alloc,
796 MInstruction* ins);
798 bool CallPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const {
799 MCallBase* call;
800 if (ins->isCall()) {
801 call = ins->toCall();
802 } else {
803 call = ins->toCallClassHook();
806 MDefinition* func = call->getCallee();
807 if (func->type() != MIRType::Object) {
808 MInstruction* unbox =
809 MUnbox::New(alloc, func, MIRType::Object, MUnbox::Fallible);
810 SetTypePolicyBailoutKind(unbox, call);
811 call->block()->insertBefore(call, unbox);
812 call->replaceCallee(unbox);
814 if (!unbox->typePolicy()->adjustInputs(alloc, unbox)) {
815 return false;
819 for (uint32_t i = 0; i < call->numStackArgs(); i++) {
820 if (!alloc.ensureBallast()) {
821 return false;
823 EnsureOperandNotFloat32(alloc, call, MCallBase::IndexOfStackArg(i));
826 return true;
829 bool MegamorphicSetElementPolicy::adjustInputs(TempAllocator& alloc,
830 MInstruction* ins) const {
831 // The first operand should be an object.
832 if (!SingleObjectPolicy::staticAdjustInputs(alloc, ins)) {
833 return false;
836 // Box the index and value operands.
837 for (size_t i = 1, e = ins->numOperands(); i < e; i++) {
838 MDefinition* in = ins->getOperand(i);
839 if (in->type() == MIRType::Value) {
840 continue;
842 ins->replaceOperand(i, BoxAt(alloc, ins, in));
844 return true;
847 bool StoreUnboxedScalarPolicy::adjustValueInput(TempAllocator& alloc,
848 MInstruction* ins,
849 Scalar::Type writeType,
850 MDefinition* value,
851 int valueOperand) {
852 if (Scalar::isBigIntType(writeType)) {
853 return ConvertOperand<MToInt64>(alloc, ins, valueOperand, MIRType::Int64);
856 MDefinition* curValue = value;
857 // First, ensure the value is int32, boolean, double or Value.
858 // The conversion is based on TypedArrayObjectTemplate::setElementTail.
859 switch (value->type()) {
860 case MIRType::Int32:
861 case MIRType::Double:
862 case MIRType::Float32:
863 case MIRType::Boolean:
864 case MIRType::Value:
865 break;
866 case MIRType::Null:
867 value->setImplicitlyUsedUnchecked();
868 value = MConstant::New(alloc, Int32Value(0));
869 ins->block()->insertBefore(ins, value->toInstruction());
870 break;
871 case MIRType::Undefined:
872 value->setImplicitlyUsedUnchecked();
873 value = MConstant::New(alloc, JS::NaNValue());
874 ins->block()->insertBefore(ins, value->toInstruction());
875 break;
876 case MIRType::Object:
877 case MIRType::String:
878 case MIRType::Symbol:
879 case MIRType::BigInt:
880 value = BoxAt(alloc, ins, value);
881 break;
882 default:
883 MOZ_CRASH("Unexpected type");
886 if (value != curValue) {
887 ins->replaceOperand(valueOperand, value);
888 curValue = value;
891 MOZ_ASSERT(
892 value->type() == MIRType::Int32 || value->type() == MIRType::Boolean ||
893 value->type() == MIRType::Double || value->type() == MIRType::Float32 ||
894 value->type() == MIRType::Value);
896 switch (writeType) {
897 case Scalar::Int8:
898 case Scalar::Uint8:
899 case Scalar::Int16:
900 case Scalar::Uint16:
901 case Scalar::Int32:
902 case Scalar::Uint32:
903 if (value->type() != MIRType::Int32) {
904 value = MTruncateToInt32::New(alloc, value);
905 ins->block()->insertBefore(ins, value->toInstruction());
907 break;
908 case Scalar::Uint8Clamped:
909 // The transpiler should have inserted MClampToUint8.
910 MOZ_ASSERT(value->type() == MIRType::Int32);
911 break;
912 case Scalar::Float16:
913 value = MToFloat16::New(alloc, value);
914 ins->block()->insertBefore(ins, value->toInstruction());
915 break;
916 case Scalar::Float32:
917 if (value->type() != MIRType::Float32) {
918 value = MToFloat32::New(alloc, value);
919 ins->block()->insertBefore(ins, value->toInstruction());
921 break;
922 case Scalar::Float64:
923 if (value->type() != MIRType::Double) {
924 value = MToDouble::New(alloc, value);
925 ins->block()->insertBefore(ins, value->toInstruction());
927 break;
928 default:
929 MOZ_CRASH("Invalid array type");
932 if (value != curValue) {
933 ins->replaceOperand(valueOperand, value);
936 return true;
939 bool StoreUnboxedScalarPolicy::adjustInputs(TempAllocator& alloc,
940 MInstruction* ins) const {
941 MStoreUnboxedScalar* store = ins->toStoreUnboxedScalar();
942 MOZ_ASSERT(store->elements()->type() == MIRType::Elements);
943 MOZ_ASSERT(store->index()->type() == MIRType::IntPtr);
945 return adjustValueInput(alloc, store, store->writeType(), store->value(), 2);
948 bool StoreDataViewElementPolicy::adjustInputs(TempAllocator& alloc,
949 MInstruction* ins) const {
950 auto* store = ins->toStoreDataViewElement();
951 MOZ_ASSERT(store->elements()->type() == MIRType::Elements);
952 MOZ_ASSERT(store->index()->type() == MIRType::IntPtr);
953 MOZ_ASSERT(store->littleEndian()->type() == MIRType::Boolean);
955 return StoreUnboxedScalarPolicy::adjustValueInput(
956 alloc, ins, store->writeType(), store->value(), 2);
959 bool StoreTypedArrayHolePolicy::adjustInputs(TempAllocator& alloc,
960 MInstruction* ins) const {
961 MStoreTypedArrayElementHole* store = ins->toStoreTypedArrayElementHole();
962 MOZ_ASSERT(store->elements()->type() == MIRType::Elements);
963 MOZ_ASSERT(store->index()->type() == MIRType::IntPtr);
964 MOZ_ASSERT(store->length()->type() == MIRType::IntPtr);
966 return StoreUnboxedScalarPolicy::adjustValueInput(
967 alloc, ins, store->arrayType(), store->value(), 3);
970 bool ClampPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const {
971 MDefinition* in = ins->toClampToUint8()->input();
973 switch (in->type()) {
974 case MIRType::Int32:
975 case MIRType::Double:
976 case MIRType::Value:
977 break;
978 default:
979 ins->replaceOperand(0, BoxAt(alloc, ins, in));
980 break;
983 return true;
986 // Lists of all TypePolicy specializations which are used by MIR Instructions.
987 #define TYPE_POLICY_LIST(_) \
988 _(AllDoublePolicy) \
989 _(ArithPolicy) \
990 _(BigIntArithPolicy) \
991 _(BitwisePolicy) \
992 _(BoxInputsPolicy) \
993 _(CallPolicy) \
994 _(MegamorphicSetElementPolicy) \
995 _(ClampPolicy) \
996 _(ComparePolicy) \
997 _(PowPolicy) \
998 _(SignPolicy) \
999 _(StoreDataViewElementPolicy) \
1000 _(StoreTypedArrayHolePolicy) \
1001 _(StoreUnboxedScalarPolicy) \
1002 _(TestPolicy) \
1003 _(ToDoublePolicy) \
1004 _(ToInt32Policy) \
1005 _(ToBigIntPolicy) \
1006 _(ToStringPolicy) \
1007 _(ToInt64Policy)
1009 #define TEMPLATE_TYPE_POLICY_LIST(_) \
1010 _(BigIntPolicy<0>) \
1011 _(BooleanPolicy<0>) \
1012 _(BoxExceptPolicy<0, MIRType::Object>) \
1013 _(BoxPolicy<0>) \
1014 _(ConvertToInt32Policy<0>) \
1015 _(ConvertToStringPolicy<0>) \
1016 _(ConvertToStringPolicy<2>) \
1017 _(DoublePolicy<0>) \
1018 _(FloatingPointPolicy<0>) \
1019 _(UnboxedInt32Policy<0>) \
1020 _(UnboxedInt32Policy<1>) \
1021 _(IntPtrPolicy<0>) \
1022 _(TruncateToInt32OrToInt64Policy<2>) \
1023 _(MixPolicy<ObjectPolicy<0>, StringPolicy<1>, BoxPolicy<2>>) \
1024 _(MixPolicy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2>>) \
1025 IF_EXPLICIT_RESOURCE_MANAGEMENT( \
1026 _(MixPolicy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2>, \
1027 BooleanPolicy<3>>)) \
1028 _(MixPolicy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2>>) \
1029 _(MixPolicy<ObjectPolicy<0>, BoxPolicy<1>, UnboxedInt32Policy<2>>) \
1030 _(MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>, BoxPolicy<2>>) \
1031 _(MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>, UnboxedInt32Policy<2>>) \
1032 _(MixPolicy<ObjectPolicy<0>, BoxPolicy<2>>) \
1033 _(MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, UnboxedInt32Policy<2>>) \
1034 _(MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, ObjectPolicy<2>>) \
1035 _(MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2>>) \
1036 _(MixPolicy<StringPolicy<0>, UnboxedInt32Policy<1>, UnboxedInt32Policy<2>>) \
1037 _(MixPolicy<StringPolicy<0>, ObjectPolicy<1>, StringPolicy<2>>) \
1038 _(MixPolicy<StringPolicy<0>, StringPolicy<1>, StringPolicy<2>>) \
1039 _(MixPolicy<ObjectPolicy<0>, StringPolicy<1>, UnboxedInt32Policy<2>>) \
1040 _(MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>, BoxPolicy<2>, \
1041 ObjectPolicy<3>>) \
1042 _(MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>, UnboxedInt32Policy<2>, \
1043 UnboxedInt32Policy<3>>) \
1044 _(MixPolicy<TruncateToInt32OrToInt64Policy<2>, \
1045 TruncateToInt32OrToInt64Policy<3>>) \
1046 _(MixPolicy<ObjectPolicy<0>, CacheIdPolicy<1>, NoFloatPolicy<2>>) \
1047 _(MixPolicy<ObjectPolicy<0>, BoxExceptPolicy<1, MIRType::Object>, \
1048 CacheIdPolicy<2>>) \
1049 _(MixPolicy<BoxPolicy<0>, ObjectPolicy<1>>) \
1050 _(MixPolicy<ConvertToStringPolicy<0>, ConvertToStringPolicy<1>>) \
1051 _(MixPolicy<ConvertToStringPolicy<0>, ObjectPolicy<1>>) \
1052 _(MixPolicy<DoublePolicy<0>, DoublePolicy<1>>) \
1053 _(MixPolicy<UnboxedInt32Policy<0>, UnboxedInt32Policy<1>>) \
1054 _(MixPolicy<Int32OrIntPtrPolicy<0>, Int32OrIntPtrPolicy<1>>) \
1055 _(MixPolicy<ObjectPolicy<0>, BoxPolicy<1>>) \
1056 _(MixPolicy<BoxExceptPolicy<0, MIRType::Object>, CacheIdPolicy<1>>) \
1057 _(MixPolicy<CacheIdPolicy<0>, ObjectPolicy<1>>) \
1058 _(MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1>>) \
1059 _(MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>>) \
1060 _(MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<2>>) \
1061 _(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<1>>) \
1062 _(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<2>>) \
1063 _(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<3>>) \
1064 _(MixPolicy<ObjectPolicy<0>, NoFloatPolicyAfter<1>>) \
1065 _(MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>>) \
1066 _(MixPolicy<ObjectPolicy<0>, StringPolicy<1>>) \
1067 _(MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<2>>) \
1068 _(MixPolicy<ObjectPolicy<1>, ConvertToStringPolicy<0>>) \
1069 _(MixPolicy<StringPolicy<0>, UnboxedInt32Policy<1>>) \
1070 _(MixPolicy<StringPolicy<0>, StringPolicy<1>>) \
1071 _(MixPolicy<BoxPolicy<0>, BoxPolicy<1>>) \
1072 _(MixPolicy<BoxPolicy<0>, BoxPolicy<1>, BoxPolicy<2>>) \
1073 _(MixPolicy<ObjectPolicy<0>, BoxPolicy<2>, ObjectPolicy<3>>) \
1074 _(MixPolicy<BoxExceptPolicy<0, MIRType::Object>, ObjectPolicy<1>>) \
1075 _(MixPolicy<UnboxedInt32Policy<0>, BigIntPolicy<1>>) \
1076 _(MixPolicy<UnboxedInt32Policy<0>, NoFloatPolicyAfter<1>>) \
1077 _(MixPolicy<UnboxedInt32Policy<0>, UnboxedInt32Policy<1>, \
1078 NoFloatPolicyAfter<2>>) \
1079 _(NoFloatPolicy<0>) \
1080 _(NoFloatPolicy<1>) \
1081 _(NoFloatPolicy<2>) \
1082 _(NoFloatPolicyAfter<0>) \
1083 _(NoFloatPolicyAfter<1>) \
1084 _(NoFloatPolicyAfter<2>) \
1085 _(ObjectPolicy<0>) \
1086 _(ObjectPolicy<1>) \
1087 _(ObjectPolicy<3>) \
1088 _(StringPolicy<0>) \
1089 _(SymbolPolicy<0>)
1091 namespace js {
1092 namespace jit {
1094 // Define for all used TypePolicy specialization, the definition for
1095 // |TypePolicy::Data::thisTypePolicy|. This function returns one constant
1096 // instance of the TypePolicy which is shared among all MIR Instructions of the
1097 // same type.
1099 // This Macro use __VA_ARGS__ to account for commas of template parameters.
1100 #define DEFINE_TYPE_POLICY_SINGLETON_INSTANCES_(...) \
1101 const TypePolicy* __VA_ARGS__::Data::thisTypePolicy() { \
1102 static constexpr __VA_ARGS__ singletonType; \
1103 return &singletonType; \
1106 TYPE_POLICY_LIST(DEFINE_TYPE_POLICY_SINGLETON_INSTANCES_)
1107 TEMPLATE_TYPE_POLICY_LIST(template <> DEFINE_TYPE_POLICY_SINGLETON_INSTANCES_)
1108 #undef DEFINE_TYPE_POLICY_SINGLETON_INSTANCES_
1110 } // namespace jit
1111 } // namespace js
1113 namespace {
1115 // For extra-good measure in case an unqualified use is ever introduced. (The
1116 // main use in the macro below is explicitly qualified so as not to consult
1117 // this scope and find this function.)
1118 inline TypePolicy* thisTypePolicy() = delete;
1120 static MIRType thisTypeSpecialization() {
1121 MOZ_CRASH("TypeSpecialization lacks definition of thisTypeSpecialization.");
1124 } // namespace
1126 // For each MIR Instruction, this macro define the |typePolicy| method which is
1127 // using the |thisTypePolicy| method. The |thisTypePolicy| method is either a
1128 // member of the MIR Instruction, such as with MGetElementCache, a member
1129 // inherited from the TypePolicy::Data structure, or a member inherited from
1130 // NoTypePolicy if the MIR instruction has no type policy.
1131 #define DEFINE_MIR_TYPEPOLICY_MEMBERS_(op) \
1132 const TypePolicy* js::jit::M##op::typePolicy() { \
1133 return M##op::thisTypePolicy(); \
1136 MIRType js::jit::M##op::typePolicySpecialization() { \
1137 return thisTypeSpecialization(); \
1140 MIR_OPCODE_LIST(DEFINE_MIR_TYPEPOLICY_MEMBERS_)
1141 #undef DEFINE_MIR_TYPEPOLICY_MEMBERS_