/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jit_TypePolicy_h #define jit_TypePolicy_h #include "jit/IonTypes.h" #include "js/ScalarType.h" // js::Scalar::Type namespace js { namespace jit { class MInstruction; class MDefinition; class TempAllocator; extern MDefinition* AlwaysBoxAt(TempAllocator& alloc, MInstruction* at, MDefinition* operand); // A type policy directs the type analysis phases, which insert conversion, // boxing, unboxing, and type changes as necessary. class TypePolicy { public: // Analyze the inputs of the instruction and perform one of the following // actions for each input: // * Nothing; the input already type-checks. // * If untyped, optionally ask the input to try and specialize its value. // * Replace the operand with a conversion instruction. // * Insert an unconditional deoptimization (no conversion possible). [[nodiscard]] virtual bool adjustInputs(TempAllocator& alloc, MInstruction* def) const = 0; }; struct TypeSpecializationData { protected: // Specifies three levels of specialization: // - < Value. This input is expected and required. // - == None. This op should not be specialized. MIRType specialization_; MIRType thisTypeSpecialization() { return specialization_; } public: MIRType specialization() const { return specialization_; } }; #define EMPTY_DATA_ \ struct Data { \ static const TypePolicy* thisTypePolicy(); \ } #define INHERIT_DATA_(DATA_TYPE) \ struct Data : public DATA_TYPE { \ static const TypePolicy* thisTypePolicy(); \ } #define SPECIALIZATION_DATA_ INHERIT_DATA_(TypeSpecializationData) class NoTypePolicy { public: struct Data { static const TypePolicy* thisTypePolicy() { return nullptr; } }; }; class BoxInputsPolicy final : public TypePolicy { public: constexpr BoxInputsPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; class ArithPolicy final : public TypePolicy { public: constexpr ArithPolicy() = default; EMPTY_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; class BigIntArithPolicy final : public TypePolicy { public: constexpr BigIntArithPolicy() = default; EMPTY_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; class AllDoublePolicy final : public TypePolicy { public: constexpr AllDoublePolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; class BitwisePolicy final : public TypePolicy { public: constexpr BitwisePolicy() = default; EMPTY_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; class ComparePolicy final : public TypePolicy { public: constexpr ComparePolicy() = default; EMPTY_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; // Policy for MTest instructions. class TestPolicy final : public TypePolicy { public: constexpr TestPolicy() = default; EMPTY_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; class CallPolicy final : public TypePolicy { public: constexpr CallPolicy() = default; EMPTY_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; // Policy for MPow: // // * If return type is MIRType::Double, we need (Double, Double) or // (Double, Int32) operands. // * If return type is MIRType::Int32, we need (Int32, Int32) operands. class PowPolicy final : public TypePolicy { public: constexpr PowPolicy() = default; EMPTY_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; // Policy for MSign. Operand is either Double or Int32. class SignPolicy final : public TypePolicy { public: constexpr SignPolicy() = default; SPECIALIZATION_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; // Expect a symbol for operand Op. If the input is a Value, it is unboxed. template class SymbolPolicy final : public TypePolicy { public: constexpr SymbolPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect a boolean for operand Op. If the input is a Value, it is unboxed. template class BooleanPolicy final : public TypePolicy { public: constexpr BooleanPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect a string for operand Op. If the input is a Value, it is unboxed. template class StringPolicy final : public TypePolicy { public: constexpr StringPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect a string for operand Op. Else a ToString instruction is inserted. template class ConvertToStringPolicy final : public TypePolicy { public: constexpr ConvertToStringPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect a BigInt for operand Op. If the input is a Value, it is unboxed. template class BigIntPolicy final : public TypePolicy { public: constexpr BigIntPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expects either an Int32 or a boxed Int32 for operand Op; may unbox if needed. template class UnboxedInt32Policy final : private TypePolicy { public: constexpr UnboxedInt32Policy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expects either an Int32 or IntPtr for operand Op. template class Int32OrIntPtrPolicy final : private TypePolicy { public: constexpr Int32OrIntPtrPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect an Int for operand Op. Else a ToInt32 instruction is inserted. template class ConvertToInt32Policy final : public TypePolicy { public: constexpr ConvertToInt32Policy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect either an Int or BigInt for operand Op. Else a TruncateToInt32 or // ToBigInt instruction is inserted. template class TruncateToInt32OrToBigIntPolicy final : public TypePolicy { public: constexpr TruncateToInt32OrToBigIntPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect a double for operand Op. If the input is a Value, it is unboxed. template class DoublePolicy final : public TypePolicy { public: constexpr DoublePolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect a float32 for operand Op. If the input is a Value, it is unboxed. template class Float32Policy final : public TypePolicy { public: constexpr Float32Policy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect a float32 OR a double for operand Op, but will prioritize Float32 // if the result type is set as such. If the input is a Value, it is unboxed. template class FloatingPointPolicy final : public TypePolicy { public: constexpr FloatingPointPolicy() = default; SPECIALIZATION_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; template class NoFloatPolicy final : public TypePolicy { public: constexpr NoFloatPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Policy for guarding variadic instructions such as object / array state // instructions. template class NoFloatPolicyAfter final : public TypePolicy { public: constexpr NoFloatPolicyAfter() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override { return staticAdjustInputs(alloc, ins); } }; // Box objects or strings as an input to a ToDouble instruction. class ToDoublePolicy final : public TypePolicy { public: constexpr ToDoublePolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Box objects, strings and undefined as input to a ToInt32 instruction. class ToInt32Policy final : public TypePolicy { public: constexpr ToInt32Policy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Box any non-BigInts as input to a ToBigInt instruction. class ToBigIntPolicy final : public TypePolicy { public: constexpr ToBigIntPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Box objects as input to a ToString instruction. class ToStringPolicy final : public TypePolicy { public: constexpr ToStringPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Box non-Boolean, non-String, non-BigInt as input to a ToInt64 instruction. class ToInt64Policy final : public TypePolicy { public: constexpr ToInt64Policy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override { return staticAdjustInputs(alloc, ins); } }; template class ObjectPolicy final : public TypePolicy { public: constexpr ObjectPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override { return staticAdjustInputs(alloc, ins); } }; // Single-object input. If the input is a Value, it is unboxed. If it is // a primitive, we use ValueToNonNullObject. using SingleObjectPolicy = ObjectPolicy<0>; template class BoxPolicy final : public TypePolicy { public: constexpr BoxPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override { return staticAdjustInputs(alloc, ins); } }; // Boxes everything except inputs of type Type. template class BoxExceptPolicy final : public TypePolicy { public: constexpr BoxExceptPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override { return staticAdjustInputs(alloc, ins); } }; // Box if not a typical property id (string, symbol, int32). template class CacheIdPolicy final : public TypePolicy { public: EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins); [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override { return staticAdjustInputs(alloc, ins); } }; // Combine multiple policies. template class MixPolicy final : public TypePolicy { public: constexpr MixPolicy() = default; EMPTY_DATA_; [[nodiscard]] static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins) { return (Policies::staticAdjustInputs(alloc, ins) && ...); } [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override { return staticAdjustInputs(alloc, ins); } }; class MegamorphicSetElementPolicy final : public TypePolicy { public: constexpr MegamorphicSetElementPolicy() = default; EMPTY_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; class StoreDataViewElementPolicy; class StoreTypedArrayHolePolicy; class StoreUnboxedScalarPolicy : public TypePolicy { private: constexpr StoreUnboxedScalarPolicy() = default; [[nodiscard]] static bool adjustValueInput(TempAllocator& alloc, MInstruction* ins, Scalar::Type arrayType, MDefinition* value, int valueOperand); friend class StoreDataViewElementPolicy; friend class StoreTypedArrayHolePolicy; public: EMPTY_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; class StoreDataViewElementPolicy final : public StoreUnboxedScalarPolicy { public: constexpr StoreDataViewElementPolicy() = default; EMPTY_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; class StoreTypedArrayHolePolicy final : public StoreUnboxedScalarPolicy { public: constexpr StoreTypedArrayHolePolicy() = default; EMPTY_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; // Accepts integers and doubles. Everything else is boxed. class ClampPolicy final : public TypePolicy { public: constexpr ClampPolicy() = default; EMPTY_DATA_; [[nodiscard]] bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; #undef SPECIALIZATION_DATA_ #undef INHERIT_DATA_ #undef EMPTY_DATA_ } // namespace jit } // namespace js #endif /* jit_TypePolicy_h */