TypePolicy.h (20321B)
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/. */ 6 7 #ifndef jit_TypePolicy_h 8 #define jit_TypePolicy_h 9 10 #include "jit/IonTypes.h" 11 #include "js/ScalarType.h" // js::Scalar::Type 12 13 namespace js { 14 namespace jit { 15 16 class MInstruction; 17 class MDefinition; 18 class TempAllocator; 19 20 extern MDefinition* BoxAt(TempAllocator& alloc, MInstruction* at, 21 MDefinition* operand); 22 23 // A type policy directs the type analysis phases, which insert conversion, 24 // boxing, unboxing, and type changes as necessary. 25 class TypePolicy { 26 public: 27 // Analyze the inputs of the instruction and perform one of the following 28 // actions for each input: 29 // * Nothing; the input already type-checks. 30 // * If untyped, optionally ask the input to try and specialize its value. 31 // * Replace the operand with a conversion instruction. 32 // * Insert an unconditional deoptimization (no conversion possible). 33 [[nodiscard]] virtual bool adjustInputs(TempAllocator& alloc, 34 MInstruction* def) const = 0; 35 }; 36 37 struct TypeSpecializationData { 38 protected: 39 // Specifies three levels of specialization: 40 // - < Value. This input is expected and required. 41 // - == None. This op should not be specialized. 42 MIRType specialization_; 43 44 MIRType thisTypeSpecialization() { return specialization_; } 45 46 public: 47 MIRType specialization() const { return specialization_; } 48 }; 49 50 #define EMPTY_DATA_ \ 51 struct Data { \ 52 static const TypePolicy* thisTypePolicy(); \ 53 } 54 55 #define INHERIT_DATA_(DATA_TYPE) \ 56 struct Data : public DATA_TYPE { \ 57 static const TypePolicy* thisTypePolicy(); \ 58 } 59 60 #define SPECIALIZATION_DATA_ INHERIT_DATA_(TypeSpecializationData) 61 62 class NoTypePolicy { 63 public: 64 struct Data { 65 static const TypePolicy* thisTypePolicy() { return nullptr; } 66 }; 67 }; 68 69 class BoxInputsPolicy final : public TypePolicy { 70 public: 71 constexpr BoxInputsPolicy() = default; 72 EMPTY_DATA_; 73 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 74 MInstruction* def); 75 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 76 MInstruction* def) const override { 77 return staticAdjustInputs(alloc, def); 78 } 79 }; 80 81 class ArithPolicy final : public TypePolicy { 82 public: 83 constexpr ArithPolicy() = default; 84 EMPTY_DATA_; 85 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 86 MInstruction* def) const override; 87 }; 88 89 class BigIntArithPolicy final : public TypePolicy { 90 public: 91 constexpr BigIntArithPolicy() = default; 92 EMPTY_DATA_; 93 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 94 MInstruction* def) const override; 95 }; 96 97 class AllDoublePolicy final : public TypePolicy { 98 public: 99 constexpr AllDoublePolicy() = default; 100 EMPTY_DATA_; 101 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 102 MInstruction* def); 103 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 104 MInstruction* def) const override { 105 return staticAdjustInputs(alloc, def); 106 } 107 }; 108 109 class BitwisePolicy final : public TypePolicy { 110 public: 111 constexpr BitwisePolicy() = default; 112 EMPTY_DATA_; 113 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 114 MInstruction* def) const override; 115 }; 116 117 class ComparePolicy final : public TypePolicy { 118 public: 119 constexpr ComparePolicy() = default; 120 EMPTY_DATA_; 121 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 122 MInstruction* def) const override; 123 }; 124 125 // Policy for MTest instructions. 126 class TestPolicy final : public TypePolicy { 127 public: 128 constexpr TestPolicy() = default; 129 EMPTY_DATA_; 130 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 131 MInstruction* ins) const override; 132 }; 133 134 class CallPolicy final : public TypePolicy { 135 public: 136 constexpr CallPolicy() = default; 137 EMPTY_DATA_; 138 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 139 MInstruction* def) const override; 140 }; 141 142 // Policy for MPow: 143 // 144 // * If return type is MIRType::Double, we need (Double, Double) or 145 // (Double, Int32) operands. 146 // * If return type is MIRType::Int32, we need (Int32, Int32) operands. 147 class PowPolicy final : public TypePolicy { 148 public: 149 constexpr PowPolicy() = default; 150 EMPTY_DATA_; 151 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 152 MInstruction* ins) const override; 153 }; 154 155 // Policy for MSign. Operand is either Double or Int32. 156 class SignPolicy final : public TypePolicy { 157 public: 158 constexpr SignPolicy() = default; 159 SPECIALIZATION_DATA_; 160 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 161 MInstruction* ins) const override; 162 }; 163 164 // Expect a symbol for operand Op. If the input is a Value, it is unboxed. 165 template <unsigned Op> 166 class SymbolPolicy final : public TypePolicy { 167 public: 168 constexpr SymbolPolicy() = default; 169 EMPTY_DATA_; 170 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 171 MInstruction* def); 172 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 173 MInstruction* def) const override { 174 return staticAdjustInputs(alloc, def); 175 } 176 }; 177 178 // Expect a boolean for operand Op. If the input is a Value, it is unboxed. 179 template <unsigned Op> 180 class BooleanPolicy final : public TypePolicy { 181 public: 182 constexpr BooleanPolicy() = default; 183 EMPTY_DATA_; 184 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 185 MInstruction* def); 186 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 187 MInstruction* def) const override { 188 return staticAdjustInputs(alloc, def); 189 } 190 }; 191 192 // Expect a string for operand Op. If the input is a Value, it is unboxed. 193 template <unsigned Op> 194 class StringPolicy final : public TypePolicy { 195 public: 196 constexpr StringPolicy() = default; 197 EMPTY_DATA_; 198 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 199 MInstruction* def); 200 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 201 MInstruction* def) const override { 202 return staticAdjustInputs(alloc, def); 203 } 204 }; 205 206 // Expect a string for operand Op. Else a ToString instruction is inserted. 207 template <unsigned Op> 208 class ConvertToStringPolicy final : public TypePolicy { 209 public: 210 constexpr ConvertToStringPolicy() = default; 211 EMPTY_DATA_; 212 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 213 MInstruction* def); 214 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 215 MInstruction* def) const override { 216 return staticAdjustInputs(alloc, def); 217 } 218 }; 219 220 // Expect a BigInt for operand Op. If the input is a Value, it is unboxed. 221 template <unsigned Op> 222 class BigIntPolicy final : public TypePolicy { 223 public: 224 constexpr BigIntPolicy() = default; 225 EMPTY_DATA_; 226 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 227 MInstruction* def); 228 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 229 MInstruction* def) const override { 230 return staticAdjustInputs(alloc, def); 231 } 232 }; 233 234 // Expects either an Int32 or a boxed Int32 for operand Op; may unbox if needed. 235 template <unsigned Op> 236 class UnboxedInt32Policy final : private TypePolicy { 237 public: 238 constexpr UnboxedInt32Policy() = default; 239 EMPTY_DATA_; 240 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 241 MInstruction* def); 242 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 243 MInstruction* def) const override { 244 return staticAdjustInputs(alloc, def); 245 } 246 }; 247 248 // Expects either an Int32 or IntPtr for operand Op. 249 template <unsigned Op> 250 class Int32OrIntPtrPolicy final : private TypePolicy { 251 public: 252 constexpr Int32OrIntPtrPolicy() = default; 253 EMPTY_DATA_; 254 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 255 MInstruction* def); 256 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 257 MInstruction* def) const override { 258 return staticAdjustInputs(alloc, def); 259 } 260 }; 261 262 // Expect an IntPtr for operand Op. 263 template <unsigned Op> 264 class IntPtrPolicy final : private TypePolicy { 265 public: 266 constexpr IntPtrPolicy() = default; 267 EMPTY_DATA_; 268 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 269 MInstruction* def); 270 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 271 MInstruction* def) const override { 272 return staticAdjustInputs(alloc, def); 273 } 274 }; 275 276 // Expect an Int for operand Op. Else a ToInt32 instruction is inserted. 277 template <unsigned Op> 278 class ConvertToInt32Policy final : public TypePolicy { 279 public: 280 constexpr ConvertToInt32Policy() = default; 281 EMPTY_DATA_; 282 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 283 MInstruction* def); 284 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 285 MInstruction* def) const override { 286 return staticAdjustInputs(alloc, def); 287 } 288 }; 289 290 // Expect either an Int32 or Int64 for operand Op. Else a TruncateToInt32 or 291 // ToInt64 instruction is inserted. 292 template <unsigned Op> 293 class TruncateToInt32OrToInt64Policy final : public TypePolicy { 294 public: 295 constexpr TruncateToInt32OrToInt64Policy() = default; 296 EMPTY_DATA_; 297 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 298 MInstruction* def); 299 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 300 MInstruction* def) const override { 301 return staticAdjustInputs(alloc, def); 302 } 303 }; 304 305 // Expect a double for operand Op. If the input is a Value, it is unboxed. 306 template <unsigned Op> 307 class DoublePolicy final : public TypePolicy { 308 public: 309 constexpr DoublePolicy() = default; 310 EMPTY_DATA_; 311 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 312 MInstruction* def); 313 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 314 MInstruction* def) const override { 315 return staticAdjustInputs(alloc, def); 316 } 317 }; 318 319 // Expect a float32 for operand Op. If the input is a Value, it is unboxed. 320 template <unsigned Op> 321 class Float32Policy final : public TypePolicy { 322 public: 323 constexpr Float32Policy() = default; 324 EMPTY_DATA_; 325 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 326 MInstruction* def); 327 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 328 MInstruction* def) const override { 329 return staticAdjustInputs(alloc, def); 330 } 331 }; 332 333 // Expect a float32 OR a double for operand Op, but will prioritize Float32 334 // if the result type is set as such. If the input is a Value, it is unboxed. 335 template <unsigned Op> 336 class FloatingPointPolicy final : public TypePolicy { 337 public: 338 constexpr FloatingPointPolicy() = default; 339 SPECIALIZATION_DATA_; 340 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 341 MInstruction* def) const override; 342 }; 343 344 template <unsigned Op> 345 class NoFloatPolicy final : public TypePolicy { 346 public: 347 constexpr NoFloatPolicy() = default; 348 EMPTY_DATA_; 349 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 350 MInstruction* def); 351 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 352 MInstruction* def) const override { 353 return staticAdjustInputs(alloc, def); 354 } 355 }; 356 357 // Policy for guarding variadic instructions such as object / array state 358 // instructions. 359 template <unsigned FirstOp> 360 class NoFloatPolicyAfter final : public TypePolicy { 361 public: 362 constexpr NoFloatPolicyAfter() = default; 363 EMPTY_DATA_; 364 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 365 MInstruction* def); 366 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 367 MInstruction* ins) const override { 368 return staticAdjustInputs(alloc, ins); 369 } 370 }; 371 372 // Box objects or strings as an input to a ToDouble instruction. 373 class ToDoublePolicy final : public TypePolicy { 374 public: 375 constexpr ToDoublePolicy() = default; 376 EMPTY_DATA_; 377 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 378 MInstruction* def); 379 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 380 MInstruction* def) const override { 381 return staticAdjustInputs(alloc, def); 382 } 383 }; 384 385 // Box objects, strings and undefined as input to a ToInt32 instruction. 386 class ToInt32Policy final : public TypePolicy { 387 public: 388 constexpr ToInt32Policy() = default; 389 EMPTY_DATA_; 390 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 391 MInstruction* def); 392 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 393 MInstruction* def) const override { 394 return staticAdjustInputs(alloc, def); 395 } 396 }; 397 398 // Box any non-BigInts as input to a ToBigInt instruction. 399 class ToBigIntPolicy final : public TypePolicy { 400 public: 401 constexpr ToBigIntPolicy() = default; 402 EMPTY_DATA_; 403 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 404 MInstruction* def); 405 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 406 MInstruction* def) const override { 407 return staticAdjustInputs(alloc, def); 408 } 409 }; 410 411 // Box objects as input to a ToString instruction. 412 class ToStringPolicy final : public TypePolicy { 413 public: 414 constexpr ToStringPolicy() = default; 415 EMPTY_DATA_; 416 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 417 MInstruction* def); 418 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 419 MInstruction* def) const override { 420 return staticAdjustInputs(alloc, def); 421 } 422 }; 423 424 // Box non-Boolean, non-String, non-BigInt as input to a ToInt64 instruction. 425 class ToInt64Policy final : public TypePolicy { 426 public: 427 constexpr ToInt64Policy() = default; 428 EMPTY_DATA_; 429 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 430 MInstruction* ins); 431 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 432 MInstruction* ins) const override { 433 return staticAdjustInputs(alloc, ins); 434 } 435 }; 436 437 template <unsigned Op> 438 class ObjectPolicy final : public TypePolicy { 439 public: 440 constexpr ObjectPolicy() = default; 441 EMPTY_DATA_; 442 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 443 MInstruction* ins); 444 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 445 MInstruction* ins) const override { 446 return staticAdjustInputs(alloc, ins); 447 } 448 }; 449 450 // Single-object input. If the input is a Value, it is unboxed. If it is 451 // a primitive, we use ValueToNonNullObject. 452 using SingleObjectPolicy = ObjectPolicy<0>; 453 454 template <unsigned Op> 455 class BoxPolicy final : public TypePolicy { 456 public: 457 constexpr BoxPolicy() = default; 458 EMPTY_DATA_; 459 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 460 MInstruction* ins); 461 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 462 MInstruction* ins) const override { 463 return staticAdjustInputs(alloc, ins); 464 } 465 }; 466 467 // Boxes everything except inputs of type Type. 468 template <unsigned Op, MIRType Type> 469 class BoxExceptPolicy final : public TypePolicy { 470 public: 471 constexpr BoxExceptPolicy() = default; 472 EMPTY_DATA_; 473 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 474 MInstruction* ins); 475 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 476 MInstruction* ins) const override { 477 return staticAdjustInputs(alloc, ins); 478 } 479 }; 480 481 // Box if not a typical property id (string, symbol, int32). 482 template <unsigned Op> 483 class CacheIdPolicy final : public TypePolicy { 484 public: 485 EMPTY_DATA_; 486 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 487 MInstruction* ins); 488 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 489 MInstruction* ins) const override { 490 return staticAdjustInputs(alloc, ins); 491 } 492 }; 493 494 // Combine multiple policies. 495 template <class... Policies> 496 class MixPolicy final : public TypePolicy { 497 public: 498 constexpr MixPolicy() = default; 499 EMPTY_DATA_; 500 [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, 501 MInstruction* ins) { 502 return (Policies::staticAdjustInputs(alloc, ins) && ...); 503 } 504 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 505 MInstruction* ins) const override { 506 return staticAdjustInputs(alloc, ins); 507 } 508 }; 509 510 class MegamorphicSetElementPolicy final : public TypePolicy { 511 public: 512 constexpr MegamorphicSetElementPolicy() = default; 513 EMPTY_DATA_; 514 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 515 MInstruction* def) const override; 516 }; 517 518 class StoreDataViewElementPolicy; 519 class StoreTypedArrayHolePolicy; 520 521 class StoreUnboxedScalarPolicy : public TypePolicy { 522 private: 523 constexpr StoreUnboxedScalarPolicy() = default; 524 [[nodiscard]] static bool adjustValueInput(TempAllocator& alloc, 525 MInstruction* ins, 526 Scalar::Type arrayType, 527 MDefinition* value, 528 int valueOperand); 529 530 friend class StoreDataViewElementPolicy; 531 friend class StoreTypedArrayHolePolicy; 532 friend class TypedArrayFillPolicy; 533 534 public: 535 EMPTY_DATA_; 536 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 537 MInstruction* ins) const override; 538 }; 539 540 class StoreDataViewElementPolicy final : public StoreUnboxedScalarPolicy { 541 public: 542 constexpr StoreDataViewElementPolicy() = default; 543 EMPTY_DATA_; 544 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 545 MInstruction* ins) const override; 546 }; 547 548 class StoreTypedArrayHolePolicy final : public StoreUnboxedScalarPolicy { 549 public: 550 constexpr StoreTypedArrayHolePolicy() = default; 551 EMPTY_DATA_; 552 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 553 MInstruction* ins) const override; 554 }; 555 556 class TypedArrayFillPolicy final : public StoreUnboxedScalarPolicy { 557 public: 558 constexpr TypedArrayFillPolicy() = default; 559 EMPTY_DATA_; 560 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 561 MInstruction* ins) const override; 562 }; 563 564 // Accepts integers and doubles. Everything else is boxed. 565 class ClampPolicy final : public TypePolicy { 566 public: 567 constexpr ClampPolicy() = default; 568 EMPTY_DATA_; 569 [[nodiscard]] bool adjustInputs(TempAllocator& alloc, 570 MInstruction* ins) const override; 571 }; 572 573 #undef SPECIALIZATION_DATA_ 574 #undef INHERIT_DATA_ 575 #undef EMPTY_DATA_ 576 577 } // namespace jit 578 } // namespace js 579 580 #endif /* jit_TypePolicy_h */