Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/jit/./../../../../js/src/jit/MIR.cpp
Warning:line 1592, column 15
Value stored to 'name' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_js_src_jit9.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/jit -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/jit -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D WASM_SUPPORTS_HUGE_MEMORY -D JS_CACHEIR_SPEW -D JS_STRUCTURED_SPEW -D JS_HAS_CTYPES -D FFI_BUILDING -D EXPORT_JS_API -D MOZ_HAS_MOZGLUE -D MOZ_SUPPORT_LEAKCHECKING -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src -I /root/firefox-clang/js/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-10-15-230929-1402748-1 -x c++ Unified_cpp_js_src_jit9.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#include "jit/MIR.h"
6
7#include "mozilla/Casting.h"
8#include "mozilla/FloatingPoint.h"
9#include "mozilla/MathAlgorithms.h"
10#include "mozilla/Maybe.h"
11
12#include <algorithm>
13#include <array>
14#include <bit>
15#include <utility>
16
17#include "builtin/Date.h"
18#include "builtin/Math.h"
19#include "builtin/Number.h"
20#include "builtin/RegExp.h"
21#include "jit/AtomicOperations.h"
22#include "jit/CompileInfo.h"
23#include "jit/KnownClass.h"
24#include "jit/MIR-wasm.h"
25#include "jit/MIRGraph.h"
26#include "jit/RangeAnalysis.h"
27#include "jit/VMFunctions.h"
28#include "jit/WarpBuilderShared.h"
29#include "jit/WarpSnapshot.h"
30#include "js/Conversions.h"
31#include "js/Date.h"
32#include "js/experimental/JitInfo.h" // JSJitInfo, JSTypedMethodJitInfo
33#include "js/ScalarType.h" // js::Scalar::Type
34#include "util/PortableMath.h"
35#include "util/Text.h"
36#include "util/Unicode.h"
37#include "vm/BigIntType.h"
38#include "vm/Float16.h"
39#include "vm/Iteration.h" // js::NativeIterator
40#include "vm/PlainObject.h" // js::PlainObject
41#include "vm/Uint8Clamped.h"
42
43#include "vm/BytecodeUtil-inl.h"
44#include "vm/JSAtomUtils-inl.h" // TypeName
45
46using namespace js;
47using namespace js::jit;
48
49using JS::ToInt32;
50
51using mozilla::IsFloat32Representable;
52using mozilla::NumbersAreIdentical;
53
54NON_GC_POINTER_TYPE_ASSERTIONS_GENERATEDstatic_assert(!std::is_base_of_v<gc::Cell, BuiltinObjectKind
>, "Ensure that BuiltinObjectKind is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, FunctionFlags
>, "Ensure that FunctionFlags is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, FunctionFlags
::FunctionKind>, "Ensure that FunctionFlags::FunctionKind is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, ImportPhase>
, "Ensure that ImportPhase is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, JSOp>, "Ensure that JSOp is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, MemoryBarrierRequirement
>, "Ensure that MemoryBarrierRequirement is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, PropertyKey>
, "Ensure that PropertyKey is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, RealmFuses::FuseIndex
>, "Ensure that RealmFuses::FuseIndex is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, SimdShuffle>
, "Ensure that SimdShuffle is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, StringCase>
, "Ensure that StringCase is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, ThrowMsgKind>
, "Ensure that ThrowMsgKind is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, ValueOrNurseryValueIndex
>, "Ensure that ValueOrNurseryValueIndex is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, bool>, "Ensure that bool is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, const ShapeListSnapshot
>, "Ensure that const ShapeListSnapshot is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, const ShapeListWithOffsetsSnapshot
>, "Ensure that const ShapeListWithOffsetsSnapshot is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, const void>
, "Ensure that const void is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, gc::Heap>,
"Ensure that gc::Heap is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, int32_t>, "Ensure that int32_t is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, jsid>, "Ensure that jsid is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, size_t>, "Ensure that size_t is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, uint16_t>,
"Ensure that uint16_t is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, uint32_t>,
"Ensure that uint32_t is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, uint8_t>, "Ensure that uint8_t is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, unsigned>,
"Ensure that unsigned is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, wasm::CallSiteDesc
>, "Ensure that wasm::CallSiteDesc is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, wasm::FieldWideningOp
>, "Ensure that wasm::FieldWideningOp is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, wasm::RefType
>, "Ensure that wasm::RefType is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, wasm::SimdOp>
, "Ensure that wasm::SimdOp is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, wasm::Trap>
, "Ensure that wasm::Trap is added to the gc_pointer_types list in GenerateMIRFiles.py."
);static_assert(!std::is_base_of_v<gc::Cell, wasm::TrapSiteDesc
>, "Ensure that wasm::TrapSiteDesc is added to the gc_pointer_types list in GenerateMIRFiles.py."
);
55
56#ifdef DEBUG1
57size_t MUse::index() const { return consumer()->indexOf(this); }
58#endif
59
60template <size_t Op>
61static void ConvertDefinitionToDouble(TempAllocator& alloc, MDefinition* def,
62 MInstruction* consumer) {
63 MInstruction* replace = MToDouble::New(alloc, def);
64 consumer->replaceOperand(Op, replace);
65 consumer->block()->insertBefore(consumer, replace);
66}
67
68template <size_t Arity, size_t Index>
69static void ConvertOperandToDouble(MAryInstruction<Arity>* def,
70 TempAllocator& alloc) {
71 static_assert(Index < Arity);
72 auto* operand = def->getOperand(Index);
73 if (operand->type() == MIRType::Float32) {
74 ConvertDefinitionToDouble<Index>(alloc, operand, def);
75 }
76}
77
78template <size_t Arity, size_t... ISeq>
79static void ConvertOperandsToDouble(MAryInstruction<Arity>* def,
80 TempAllocator& alloc,
81 std::index_sequence<ISeq...>) {
82 (ConvertOperandToDouble<Arity, ISeq>(def, alloc), ...);
83}
84
85template <size_t Arity>
86static void ConvertOperandsToDouble(MAryInstruction<Arity>* def,
87 TempAllocator& alloc) {
88 ConvertOperandsToDouble<Arity>(def, alloc, std::make_index_sequence<Arity>{});
89}
90
91template <size_t Arity, size_t... ISeq>
92static bool AllOperandsCanProduceFloat32(MAryInstruction<Arity>* def,
93 std::index_sequence<ISeq...>) {
94 return (def->getOperand(ISeq)->canProduceFloat32() && ...);
95}
96
97template <size_t Arity>
98static bool AllOperandsCanProduceFloat32(MAryInstruction<Arity>* def) {
99 return AllOperandsCanProduceFloat32<Arity>(def,
100 std::make_index_sequence<Arity>{});
101}
102
103static bool CheckUsesAreFloat32Consumers(const MInstruction* ins) {
104 if (ins->isImplicitlyUsed()) {
105 return false;
106 }
107 bool allConsumerUses = true;
108 for (MUseDefIterator use(ins); allConsumerUses && use; use++) {
109 allConsumerUses &= use.def()->canConsumeFloat32(use.use());
110 }
111 return allConsumerUses;
112}
113
114#ifdef JS_JITSPEW1
115static const char* OpcodeName(MDefinition::Opcode op) {
116 static const char* const names[] = {
117# define NAME(x) #x,
118 MIR_OPCODE_LIST(NAME)NAME(Start)NAME(OsrEntry)NAME(Nop)NAME(LimitedTruncate)NAME(IntPtrLimitedTruncate
)NAME(Int64LimitedTruncate)NAME(Constant)NAME(WasmNullConstant
)NAME(WasmFloatConstant)NAME(Parameter)NAME(Callee)NAME(IsConstructing
)NAME(TableSwitch)NAME(Goto)NAME(Test)NAME(Return)NAME(Throw)
NAME(ThrowWithStack)NAME(NewArray)NAME(NewArrayDynamicLength)
NAME(NewTypedArray)NAME(NewTypedArrayDynamicLength)NAME(NewTypedArrayFromArray
)NAME(NewTypedArrayFromArrayBuffer)NAME(NewMapObject)NAME(NewSetObject
)NAME(NewMapObjectFromIterable)NAME(NewSetObjectFromIterable)
NAME(NewObject)NAME(NewPlainObject)NAME(NewArrayObject)NAME(NewIterator
)NAME(ObjectState)NAME(ArrayState)NAME(BindFunction)NAME(NewBoundFunction
)NAME(BoundFunctionNumArgs)NAME(GuardBoundFunctionIsConstructor
)NAME(MutateProto)NAME(InitPropGetterSetter)NAME(InitElemGetterSetter
)NAME(Call)NAME(CallClassHook)NAME(ApplyArgs)NAME(ApplyArgsObj
)NAME(ApplyArray)NAME(ConstructArgs)NAME(ConstructArray)NAME(
Bail)NAME(Unreachable)NAME(EncodeSnapshot)NAME(AssertRecoveredOnBailout
)NAME(AssertFloat32)NAME(Compare)NAME(StrictConstantCompareInt32
)NAME(StrictConstantCompareBoolean)NAME(StrictConstantCompareString
)NAME(StrictConstantCompareObject)NAME(SameValueDouble)NAME(SameValue
)NAME(Box)NAME(Unbox)NAME(AssertRange)NAME(AssertClass)NAME(AssertShape
)NAME(CreateThis)NAME(CreateArgumentsObject)NAME(CreateInlinedArgumentsObject
)NAME(GetInlinedArgument)NAME(GetInlinedArgumentHole)NAME(GetArgumentsObjectArg
)NAME(SetArgumentsObjectArg)NAME(LoadArgumentsObjectArg)NAME(
LoadArgumentsObjectArgHole)NAME(InArgumentsObjectArg)NAME(ArgumentsObjectLength
)NAME(ArrayFromArgumentsObject)NAME(GuardArgumentsObjectFlags
)NAME(GuardObjectHasSameRealm)NAME(LoadScriptedProxyHandler)NAME
(CheckScriptedProxyGetResult)NAME(IdToStringOrSymbol)NAME(ReturnFromCtor
)NAME(ToDouble)NAME(ToFloat32)NAME(ToFloat16)NAME(UnsignedToDouble
)NAME(UnsignedToFloat32)NAME(WrapInt64ToInt32)NAME(ExtendInt32ToInt64
)NAME(WasmBuiltinTruncateToInt64)NAME(WasmTruncateToInt64)NAME
(WasmTruncateToInt32)NAME(WasmAnyRefFromJSValue)NAME(WasmAnyRefFromJSObject
)NAME(WasmAnyRefFromJSString)NAME(WasmAnyRefIsJSString)NAME(WasmTrapIfAnyRefIsNotJSString
)NAME(WasmAnyRefJSStringLength)NAME(WasmNewI31Ref)NAME(WasmI31RefGet
)NAME(Int32ToIntPtr)NAME(NonNegativeIntPtrToInt32)NAME(IntPtrToDouble
)NAME(AdjustDataViewLength)NAME(Int64ToFloatingPoint)NAME(BuiltinInt64ToFloatingPoint
)NAME(ToNumberInt32)NAME(BooleanToInt32)NAME(TruncateToInt32)
NAME(WasmBuiltinTruncateToInt32)NAME(ToBigInt)NAME(ToInt64)NAME
(TruncateBigIntToInt64)NAME(Int64ToBigInt)NAME(Int64ToIntPtr)
NAME(IntPtrToInt64)NAME(ToString)NAME(BitNot)NAME(TypeOf)NAME
(TypeOfName)NAME(TypeOfIs)NAME(ToAsyncIter)NAME(ToPropertyKeyCache
)NAME(BitAnd)NAME(BitOr)NAME(BitXor)NAME(Lsh)NAME(Rsh)NAME(Ursh
)NAME(SignExtendInt32)NAME(SignExtendInt64)NAME(SignExtendIntPtr
)NAME(MinMax)NAME(MinMaxArray)NAME(Abs)NAME(Clz)NAME(Ctz)NAME
(Popcnt)NAME(Sqrt)NAME(CopySign)NAME(Atan2)NAME(Hypot)NAME(Pow
)NAME(PowHalf)NAME(Random)NAME(Sign)NAME(MathFunction)NAME(Add
)NAME(Sub)NAME(Mul)NAME(Div)NAME(WasmBuiltinDivI64)NAME(Mod)NAME
(WasmBuiltinModD)NAME(WasmBuiltinModI64)NAME(BigIntAdd)NAME(BigIntSub
)NAME(BigIntMul)NAME(BigIntDiv)NAME(BigIntMod)NAME(BigIntPow)
NAME(BigIntBitAnd)NAME(BigIntBitOr)NAME(BigIntBitXor)NAME(BigIntLsh
)NAME(BigIntRsh)NAME(BigIntIncrement)NAME(BigIntDecrement)NAME
(BigIntNegate)NAME(BigIntBitNot)NAME(BigIntToIntPtr)NAME(IntPtrToBigInt
)NAME(BigIntPtrAdd)NAME(BigIntPtrSub)NAME(BigIntPtrMul)NAME(BigIntPtrDiv
)NAME(BigIntPtrMod)NAME(BigIntPtrPow)NAME(BigIntPtrBitAnd)NAME
(BigIntPtrBitOr)NAME(BigIntPtrBitXor)NAME(BigIntPtrLsh)NAME(BigIntPtrRsh
)NAME(BigIntPtrBitNot)NAME(Int32ToStringWithBase)NAME(NumberParseInt
)NAME(DoubleParseInt)NAME(Concat)NAME(LinearizeString)NAME(LinearizeForCharAccess
)NAME(LinearizeForCodePointAccess)NAME(ToRelativeStringIndex)
NAME(CharCodeAt)NAME(CharCodeAtOrNegative)NAME(CodePointAt)NAME
(CodePointAtOrNegative)NAME(NegativeToNaN)NAME(NegativeToUndefined
)NAME(FromCharCode)NAME(FromCharCodeEmptyIfNegative)NAME(FromCharCodeUndefinedIfNegative
)NAME(FromCodePoint)NAME(StringIncludes)NAME(StringIndexOf)NAME
(StringLastIndexOf)NAME(StringStartsWith)NAME(StringEndsWith)
NAME(StringConvertCase)NAME(CharCodeConvertCase)NAME(StringTrimStartIndex
)NAME(StringTrimEndIndex)NAME(StringSplit)NAME(BoxNonStrictThis
)NAME(ImplicitThis)NAME(Phi)NAME(Beta)NAME(NaNToZero)NAME(OsrValue
)NAME(OsrEnvironmentChain)NAME(OsrArgumentsObject)NAME(OsrReturnValue
)NAME(BinaryCache)NAME(UnaryCache)NAME(CheckOverRecursed)NAME
(InterruptCheck)NAME(WasmInterruptCheck)NAME(WasmTrap)NAME(LexicalCheck
)NAME(ThrowRuntimeLexicalError)NAME(ThrowMsg)NAME(GlobalDeclInstantiation
)NAME(RegExp)NAME(RegExpMatcher)NAME(RegExpSearcher)NAME(RegExpSearcherLastLimit
)NAME(RegExpExecMatch)NAME(RegExpExecTest)NAME(RegExpHasCaptureGroups
)NAME(GetFirstDollarIndex)NAME(StringReplace)NAME(Substr)NAME
(ModuleMetadata)NAME(DynamicImport)NAME(Lambda)NAME(FunctionWithProto
)NAME(SetFunName)NAME(Slots)NAME(Elements)NAME(InitializedLength
)NAME(SetInitializedLength)NAME(ArrayLength)NAME(SetArrayLength
)NAME(FunctionLength)NAME(FunctionName)NAME(GetNextEntryForIterator
)NAME(ArrayBufferByteLength)NAME(ArrayBufferViewLength)NAME(ArrayBufferViewByteOffset
)NAME(ArrayBufferViewElements)NAME(ResizableTypedArrayLength)
NAME(ResizableDataViewByteLength)NAME(GrowableSharedArrayBufferByteLength
)NAME(TypedArrayElementSize)NAME(GuardHasAttachedArrayBuffer)
NAME(GuardResizableArrayBufferViewInBounds)NAME(GuardResizableArrayBufferViewInBoundsOrDetached
)NAME(TypedArrayFill)NAME(TypedArraySet)NAME(TypedArraySetFromSubarray
)NAME(GuardTypedArraySetOffset)NAME(TypedArraySubarray)NAME(ToIntegerIndex
)NAME(GuardNumberToIntPtrIndex)NAME(KeepAliveObject)NAME(DebugEnterGCUnsafeRegion
)NAME(DebugLeaveGCUnsafeRegion)NAME(Not)NAME(BoundsCheck)NAME
(BoundsCheckLower)NAME(SpectreMaskIndex)NAME(LoadElement)NAME
(LoadElementAndUnbox)NAME(LoadElementHole)NAME(StoreElement)NAME
(StoreHoleValueElement)NAME(StoreElementHole)NAME(ArrayPopShift
)NAME(ArrayPush)NAME(ArraySlice)NAME(ArgumentsSlice)NAME(FrameArgumentsSlice
)NAME(InlineArgumentsSlice)NAME(NormalizeSliceTerm)NAME(ArrayJoin
)NAME(ObjectKeys)NAME(ObjectKeysFromIterator)NAME(LoadUnboxedScalar
)NAME(LoadDataViewElement)NAME(LoadTypedArrayElementHole)NAME
(StoreUnboxedScalar)NAME(StoreDataViewElement)NAME(StoreTypedArrayElementHole
)NAME(EffectiveAddress3)NAME(EffectiveAddress2)NAME(ClampToUint8
)NAME(LoadFixedSlot)NAME(LoadFixedSlotFromOffset)NAME(LoadFixedSlotAndUnbox
)NAME(LoadDynamicSlotAndUnbox)NAME(StoreFixedSlot)NAME(StoreFixedSlotFromOffset
)NAME(StoreDynamicSlotFromOffset)NAME(GetPropertyCache)NAME(HomeObjectSuperBase
)NAME(GetPropSuperCache)NAME(BindNameCache)NAME(GuardShape)NAME
(HasShape)NAME(GuardFuse)NAME(GuardMultipleShapes)NAME(GuardShapeList
)NAME(GuardShapeListToOffset)NAME(GuardMultipleShapesToOffset
)NAME(GuardProto)NAME(GuardNullProto)NAME(GuardIsNativeObject
)NAME(GuardGlobalGeneration)NAME(GuardIsProxy)NAME(GuardIsNotDOMProxy
)NAME(GuardIsNotProxy)NAME(ProxyGet)NAME(ProxyGetByValue)NAME
(ProxyHasProp)NAME(ProxySet)NAME(ProxySetByValue)NAME(CallSetArrayLength
)NAME(MegamorphicLoadSlot)NAME(MegamorphicLoadSlotPermissive)
NAME(MegamorphicLoadSlotByValue)NAME(MegamorphicLoadSlotByValuePermissive
)NAME(MegamorphicStoreSlot)NAME(MegamorphicHasProp)NAME(SmallObjectVariableKeyHasProp
)NAME(GuardToArrayBuffer)NAME(GuardToSharedArrayBuffer)NAME(GuardIsNotArrayBufferMaybeShared
)NAME(GuardIsNonResizableTypedArray)NAME(GuardIsResizableTypedArray
)NAME(GuardHasProxyHandler)NAME(NurseryObject)NAME(GuardValue
)NAME(GuardNullOrUndefined)NAME(GuardIsNotObject)NAME(GuardFunctionFlags
)NAME(GuardFunctionIsNonBuiltinCtor)NAME(GuardFunctionKind)NAME
(GuardFunctionScript)NAME(GuardObjectIdentity)NAME(GuardSpecificFunction
)NAME(GuardSpecificAtom)NAME(GuardSpecificSymbol)NAME(GuardSpecificInt32
)NAME(GuardStringToIndex)NAME(GuardStringToInt32)NAME(GuardStringToDouble
)NAME(GuardNoDenseElements)NAME(GuardTagNotEqual)NAME(LoadDynamicSlot
)NAME(LoadDynamicSlotFromOffset)NAME(FunctionEnvironment)NAME
(NewLexicalEnvironmentObject)NAME(NewClassBodyEnvironmentObject
)NAME(NewVarEnvironmentObject)NAME(HomeObject)NAME(AddAndStoreSlot
)NAME(AllocateAndStoreSlot)NAME(StoreDynamicSlot)NAME(GetNameCache
)NAME(CallGetIntrinsicValue)NAME(DeleteProperty)NAME(DeleteElement
)NAME(SetPropertyCache)NAME(MegamorphicSetElement)NAME(SetDOMProperty
)NAME(GetDOMProperty)NAME(GetDOMMember)NAME(ObjectToIterator)
NAME(IteratorLength)NAME(LoadIteratorElement)NAME(ValueToIterator
)NAME(IteratorHasIndices)NAME(IteratorsMatchAndHaveIndices)NAME
(LoadSlotByIteratorIndex)NAME(StoreSlotByIteratorIndex)NAME(LoadSlotByIteratorIndexIndexed
)NAME(StoreSlotByIteratorIndexIndexed)NAME(LoadDOMExpandoValue
)NAME(LoadDOMExpandoValueGuardGeneration)NAME(LoadDOMExpandoValueIgnoreGeneration
)NAME(GuardDOMExpandoMissingOrGuardShape)NAME(StringLength)NAME
(Floor)NAME(Ceil)NAME(Round)NAME(Trunc)NAME(NearbyInt)NAME(RoundToDouble
)NAME(GetIteratorCache)NAME(OptimizeSpreadCallCache)NAME(IteratorMore
)NAME(IsNoIter)NAME(IteratorEnd)NAME(CloseIterCache)NAME(OptimizeGetIteratorCache
)NAME(InCache)NAME(InArray)NAME(GuardElementNotHole)NAME(NewPrivateName
)NAME(CheckPrivateFieldCache)NAME(HasOwnCache)NAME(InstanceOf
)NAME(InstanceOfCache)NAME(ArgumentsLength)NAME(GetFrameArgument
)NAME(GetFrameArgumentHole)NAME(NewTarget)NAME(Rest)NAME(PostWriteBarrier
)NAME(PostWriteElementBarrier)NAME(AssertCanElidePostWriteBarrier
)NAME(NewNamedLambdaObject)NAME(NewCallObject)NAME(NewStringObject
)NAME(IsCallable)NAME(IsConstructor)NAME(IsCrossRealmArrayConstructor
)NAME(IsObject)NAME(IsSuspendedGenerator)NAME(IsNullOrUndefined
)NAME(HasClass)NAME(GuardToClass)NAME(GuardToFunction)NAME(IsArray
)NAME(IsTypedArray)NAME(ObjectClassToString)NAME(CheckReturn)
NAME(CheckThis)NAME(AsyncResolve)NAME(AsyncAwait)NAME(GeneratorResume
)NAME(IsResumingGenerator)NAME(ResumeFrameArg)NAME(ClearResumingGeneratorFlag
)NAME(CheckThisReinit)NAME(Generator)NAME(CanSkipAwait)NAME(MaybeExtractAwaitValue
)NAME(IncrementWarmUpCounter)NAME(AtomicIsLockFree)NAME(AtomicPause
)NAME(CompareExchangeTypedArrayElement)NAME(AtomicExchangeTypedArrayElement
)NAME(AtomicTypedArrayElementBinop)NAME(Debugger)NAME(CheckIsObj
)NAME(CheckObjCoercible)NAME(CheckClassHeritage)NAME(DebugCheckSelfHosted
)NAME(IsPackedArray)NAME(GuardArrayIsPacked)NAME(GuardElementsArePacked
)NAME(GetPrototypeOf)NAME(ObjectWithProto)NAME(ObjectStaticProto
)NAME(ConstantProto)NAME(BuiltinObject)NAME(SuperFunction)NAME
(SuperFunctionAndUnbox)NAME(InitHomeObject)NAME(IsTypedArrayConstructor
)NAME(LoadValueTag)NAME(LoadWrapperTarget)NAME(LoadGetterSetterFunction
)NAME(GuardHasGetterSetter)NAME(GuardIsExtensible)NAME(GuardInt32IsNonNegative
)NAME(GuardIntPtrIsNonNegative)NAME(GuardInt32Range)NAME(GuardIndexIsNotDenseElement
)NAME(GuardIndexIsValidUpdateOrAdd)NAME(CallAddOrUpdateSparseElement
)NAME(CallGetSparseElement)NAME(CallNativeGetElement)NAME(CallNativeGetElementSuper
)NAME(CallObjectHasSparseElement)NAME(BigIntAsIntN)NAME(BigIntAsUintN
)NAME(GuardNonGCThing)NAME(ToHashableNonGCThing)NAME(ToHashableString
)NAME(ToHashableValue)NAME(HashNonGCThing)NAME(HashString)NAME
(HashSymbol)NAME(HashBigInt)NAME(HashObject)NAME(HashValue)NAME
(SetObjectHasNonBigInt)NAME(SetObjectHasBigInt)NAME(SetObjectHasValue
)NAME(SetObjectHasValueVMCall)NAME(SetObjectDelete)NAME(SetObjectAdd
)NAME(SetObjectSize)NAME(MapObjectHasNonBigInt)NAME(MapObjectHasBigInt
)NAME(MapObjectHasValue)NAME(MapObjectHasValueVMCall)NAME(MapObjectGetNonBigInt
)NAME(MapObjectGetBigInt)NAME(MapObjectGetValue)NAME(MapObjectGetValueVMCall
)NAME(MapObjectDelete)NAME(MapObjectSet)NAME(MapObjectSize)NAME
(WeakMapGetObject)NAME(WeakMapHasObject)NAME(WeakSetHasObject
)NAME(DateFillLocalTimeSlots)NAME(DateHoursFromSecondsIntoYear
)NAME(DateMinutesFromSecondsIntoYear)NAME(DateSecondsFromSecondsIntoYear
)NAME(DateNow)NAME(DateParse)NAME(TimeClip)NAME(LocalTimeToUTC
)NAME(YearFromTime)NAME(MonthFromTime)NAME(DateFromTime)NAME(
NewDateObject)NAME(UnpackTime)NAME(EpochMilliseconds)NAME(PostIntPtrConversion
)NAME(CanonicalizeNaN)NAME(WasmNeg)NAME(WasmBinaryBitwise)NAME
(WasmLoadInstance)NAME(WasmHeapReg)NAME(WasmBoundsCheck)NAME(
WasmBoundsCheckRange32)NAME(WasmExtendU32Index)NAME(WasmWrapU32Index
)NAME(WasmClampTable64Address)NAME(WasmAddOffset)NAME(WasmAlignmentCheck
)NAME(WasmLoad)NAME(WasmStore)NAME(WasmFence)NAME(WasmCompareExchangeHeap
)NAME(WasmAtomicExchangeHeap)NAME(WasmAtomicBinopHeap)NAME(WasmLoadInstanceDataField
)NAME(WasmLoadInstanceScratch2xI32)NAME(WasmLoadGlobalCell)NAME
(WasmLoadTableElement)NAME(WasmStoreInstanceDataField)NAME(WasmStoreInstanceScratch2xI32
)NAME(WasmStoreGlobalCell)NAME(WasmStoreStackResult)NAME(WasmDerivedPointer
)NAME(WasmDerivedIndexPointer)NAME(WasmStoreRef)NAME(WasmPostWriteBarrierWholeCell
)NAME(WasmPostWriteBarrierEdgeAtIndex)NAME(WasmParameter)NAME
(WasmReturn)NAME(WasmReturnVoid)NAME(WasmStackArg)NAME(WasmRegisterResult
)NAME(WasmFloatRegisterResult)NAME(WasmSystemFloatRegisterResult
)NAME(WasmRegister64Result)NAME(WasmStackResultArea)NAME(WasmStackResult
)NAME(WasmCallCatchable)NAME(WasmCallUncatchable)NAME(WasmCallLandingPrePad
)NAME(WasmReturnCall)NAME(WasmFindHandler)NAME(WasmSuspend)NAME
(WasmResumeBarrier)NAME(WasmPrepareResume)NAME(WasmResume)NAME
(WasmSelect)NAME(ReinterpretCast)NAME(Rotate)NAME(WasmBinarySimd128
)NAME(WasmBinarySimd128WithConstant)NAME(WasmShiftSimd128)NAME
(WasmShuffleSimd128)NAME(WasmReplaceLaneSimd128)NAME(WasmMoveLaneSimd128
)NAME(WasmUnarySimd128)NAME(WasmTernarySimd128)NAME(WasmScalarToSimd128
)NAME(WasmReduceSimd128)NAME(WasmLoadLaneSimd128)NAME(WasmStoreLaneSimd128
)NAME(UnreachableResult)NAME(IonToWasmCall)NAME(WasmLoadField
)NAME(WasmLoadElement)NAME(WasmStoreField)NAME(WasmStoreFieldRef
)NAME(WasmStoreElement)NAME(WasmStoreElementRef)NAME(WasmRefAsNonNull
)NAME(WasmRefTestConcrete)NAME(WasmRefTestAbstract)NAME(WasmRefCastConcrete
)NAME(WasmRefCastAbstract)NAME(WasmRefCastInfallible)NAME(WasmRefConvertAnyExtern
)NAME(WasmNewStructObject)NAME(WasmNewArrayObject)NAME(WasmAddSubI128HI64
)NAME(WasmMulI64WideHI64)NAME(AddDisposableResource)NAME(TakeDisposeCapability
)
119# undef NAME
120 };
121 return names[unsigned(op)];
122}
123
124void MDefinition::PrintOpcodeName(GenericPrinter& out, Opcode op) {
125 out.printf("%s", OpcodeName(op));
126}
127
128uint32_t js::jit::GetMBasicBlockId(const MBasicBlock* block) {
129 return block->id();
130}
131#endif
132
133template <MIRType Type>
134static auto ToIntConstant(MConstant* cst) {
135 MOZ_ASSERT(cst->type() == Type)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(cst->type() == Type)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(cst->type() == Type))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("cst->type() == Type"
, "./../../../../js/src/jit/MIR.cpp", 135); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "cst->type() == Type" ")"); do { MOZ_CrashSequence
(__null, 135); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
136 if constexpr (Type == MIRType::Int32) {
137 return cst->toInt32();
138 } else if constexpr (Type == MIRType::Int64) {
139 return cst->toInt64();
140 } else if constexpr (Type == MIRType::IntPtr) {
141 return cst->toIntPtr();
142 }
143}
144
145template <MIRType Type, typename IntT>
146static MConstant* NewIntConstant(TempAllocator& alloc, IntT i) {
147 if constexpr (Type == MIRType::Int32) {
148 static_assert(std::is_same_v<IntT, int32_t>);
149 return MConstant::NewInt32(alloc, i);
150 } else if constexpr (Type == MIRType::Int64) {
151 static_assert(std::is_same_v<IntT, int64_t>);
152 return MConstant::NewInt64(alloc, i);
153 } else if constexpr (Type == MIRType::IntPtr) {
154 static_assert(std::is_same_v<IntT, intptr_t>);
155 return MConstant::NewIntPtr(alloc, i);
156 }
157}
158
159template <MIRType Type>
160static MConstant* EvaluateIntConstantOperands(TempAllocator& alloc,
161 MBinaryInstruction* ins) {
162 MDefinition* left = ins->lhs();
163 MDefinition* right = ins->rhs();
164
165 if (!left->isConstant() || !right->isConstant()) {
166 return nullptr;
167 }
168
169 using IntT = decltype(ToIntConstant<Type>(nullptr));
170 using UnsignedInt = std::make_unsigned_t<IntT>;
171
172 // Right-hand side operand of shift must be non-negative and be less-than the
173 // number of bits in the left-hand side operand. Otherwise the behavior is
174 // undefined.
175 static constexpr IntT shiftMask = (sizeof(IntT) * CHAR_BIT8) - 1;
176
177 IntT lhs = ToIntConstant<Type>(left->toConstant());
178 IntT rhs = ToIntConstant<Type>(right->toConstant());
179 IntT ret;
180
181 switch (ins->op()) {
182 case MDefinition::Opcode::BitAnd:
183 case MDefinition::Opcode::BigIntPtrBitAnd:
184 ret = lhs & rhs;
185 break;
186 case MDefinition::Opcode::BitOr:
187 case MDefinition::Opcode::BigIntPtrBitOr:
188 ret = lhs | rhs;
189 break;
190 case MDefinition::Opcode::BitXor:
191 case MDefinition::Opcode::BigIntPtrBitXor:
192 ret = lhs ^ rhs;
193 break;
194 case MDefinition::Opcode::Lsh:
195 // Left-hand side operand must be non-negative, otherwise the behavior is
196 // undefined. Cast to unsigned to ensure the behavior is always defined.
197 //
198 // Note: Cast to unsigned is no longer needed when compiling to C++20.
199 ret = UnsignedInt(lhs) << (rhs & shiftMask);
200 break;
201 case MDefinition::Opcode::Rsh:
202 // The result is implementation-defined if the left-hand side operand is
203 // negative. Most implementations perform an arithmetic right-shift, which
204 // we rely on here.
205 //
206 // Note: Guaranteed to be an arithmetic right-shift in C++20.
207 ret = lhs >> (rhs & shiftMask);
208 break;
209 case MDefinition::Opcode::Ursh:
210 // Decline folding if the output doesn't fit into a signed result and
211 // bailouts are enabled. (Wasm has bailouts disabled.)
212 if (lhs < 0 && (rhs & shiftMask) == 0 &&
213 !ins->toUrsh()->bailoutsDisabled()) {
214 return nullptr;
215 }
216 ret = UnsignedInt(lhs) >> (UnsignedInt(rhs) & shiftMask);
217 break;
218 case MDefinition::Opcode::BigIntPtrLsh:
219 case MDefinition::Opcode::BigIntPtrRsh: {
220 // BigIntPtr shifts are special:
221 // 1. Excess shift amounts produce BigInt larger than IntPtr.
222 // 2. Negative shifts reverse the shift direction.
223
224 // Decline folding for excess shift amounts.
225 UnsignedInt shift = mozilla::Abs(rhs);
226 if ((shift & shiftMask) != shift) {
227 return nullptr;
228 }
229
230 bool isLsh = (ins->isBigIntPtrLsh() && rhs >= 0) ||
231 (ins->isBigIntPtrRsh() && rhs < 0);
232 if (isLsh) {
233 ret = UnsignedInt(lhs) << shift;
234 // Decline folding that overflows signed Intptr.
235 if ((ret >> shift) != lhs) {
236 return nullptr;
237 }
238 } else {
239 ret = lhs >> shift;
240 }
241 break;
242 }
243 case MDefinition::Opcode::Add:
244 case MDefinition::Opcode::BigIntPtrAdd: {
245 auto checked = mozilla::CheckedInt<IntT>(lhs) + rhs;
246 if (!checked.isValid()) {
247 return nullptr;
248 }
249 ret = checked.value();
250 break;
251 }
252 case MDefinition::Opcode::Sub:
253 case MDefinition::Opcode::BigIntPtrSub: {
254 auto checked = mozilla::CheckedInt<IntT>(lhs) - rhs;
255 if (!checked.isValid()) {
256 return nullptr;
257 }
258 ret = checked.value();
259 break;
260 }
261 case MDefinition::Opcode::Mul:
262 case MDefinition::Opcode::BigIntPtrMul: {
263 auto checked = mozilla::CheckedInt<IntT>(lhs) * rhs;
264 if (!checked.isValid()) {
265 return nullptr;
266 }
267 ret = checked.value();
268 break;
269 }
270 case MDefinition::Opcode::Div: {
271 if (ins->toDiv()->isUnsigned()) {
272 auto checked = mozilla::CheckedInt<UnsignedInt>(UnsignedInt(lhs)) /
273 UnsignedInt(rhs);
274 if (!checked.isValid()) {
275 return nullptr;
276 }
277 ret = IntT(checked.value());
278 break;
279 }
280 [[fallthrough]];
281 }
282 case MDefinition::Opcode::BigIntPtrDiv: {
283 auto checked = mozilla::CheckedInt<IntT>(lhs) / rhs;
284 if (!checked.isValid()) {
285 return nullptr;
286 }
287 ret = checked.value();
288
289 // Decline folding if the numerator isn't evenly divisible by the
290 // denominator. Only applies for non-truncating int32 division.
291 if constexpr (Type == MIRType::Int32) {
292 if (ret * rhs != lhs && !ins->toDiv()->isTruncated()) {
293 return nullptr;
294 }
295 }
296 break;
297 }
298 case MDefinition::Opcode::Mod: {
299 if (ins->toMod()->isUnsigned()) {
300 auto checked = mozilla::CheckedInt<UnsignedInt>(UnsignedInt(lhs)) %
301 UnsignedInt(rhs);
302 if (!checked.isValid()) {
303 return nullptr;
304 }
305 ret = IntT(checked.value());
306 break;
307 }
308 [[fallthrough]];
309 }
310 case MDefinition::Opcode::BigIntPtrMod: {
311 auto checked = mozilla::CheckedInt<IntT>(lhs) % rhs;
312 if (!checked.isValid()) {
313 return nullptr;
314 }
315 ret = checked.value();
316
317 // Decline folding if the result is negative zero. Only applies for
318 // non-truncating int32 remainder.
319 if constexpr (Type == MIRType::Int32) {
320 if (ret == 0 && lhs < 0 && !ins->toMod()->isTruncated()) {
321 return nullptr;
322 }
323 }
324 break;
325 }
326 default:
327 MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "./../../../../js/src/jit/MIR.cpp"
, 327); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do { MOZ_CrashSequence
(__null, 327); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
328 }
329
330 return NewIntConstant<Type>(alloc, ret);
331}
332
333static MConstant* EvaluateInt32ConstantOperands(TempAllocator& alloc,
334 MBinaryInstruction* ins) {
335 return EvaluateIntConstantOperands<MIRType::Int32>(alloc, ins);
336}
337
338static MConstant* EvaluateInt64ConstantOperands(TempAllocator& alloc,
339 MBinaryInstruction* ins) {
340 return EvaluateIntConstantOperands<MIRType::Int64>(alloc, ins);
341}
342
343static MConstant* EvaluateIntPtrConstantOperands(TempAllocator& alloc,
344 MBinaryInstruction* ins) {
345 return EvaluateIntConstantOperands<MIRType::IntPtr>(alloc, ins);
346}
347
348static MConstant* EvaluateConstantOperands(TempAllocator& alloc,
349 MBinaryInstruction* ins) {
350 MOZ_ASSERT(IsTypeRepresentableAsDouble(ins->type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsTypeRepresentableAsDouble(ins->type()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(IsTypeRepresentableAsDouble(ins->type())))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("IsTypeRepresentableAsDouble(ins->type())"
, "./../../../../js/src/jit/MIR.cpp", 350); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsTypeRepresentableAsDouble(ins->type())"
")"); do { MOZ_CrashSequence(__null, 350); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
351
352 if (ins->type() == MIRType::Int32) {
353 return EvaluateInt32ConstantOperands(alloc, ins);
354 }
355
356 MDefinition* left = ins->lhs();
357 MDefinition* right = ins->rhs();
358
359 MOZ_ASSERT(IsFloatingPointType(left->type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsFloatingPointType(left->type()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsFloatingPointType(left->
type())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("IsFloatingPointType(left->type())", "./../../../../js/src/jit/MIR.cpp"
, 359); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsFloatingPointType(left->type())"
")"); do { MOZ_CrashSequence(__null, 359); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
360 MOZ_ASSERT(IsFloatingPointType(right->type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsFloatingPointType(right->type()))>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(IsFloatingPointType(right->type())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("IsFloatingPointType(right->type())"
, "./../../../../js/src/jit/MIR.cpp", 360); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsFloatingPointType(right->type())" ")"
); do { MOZ_CrashSequence(__null, 360); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
361
362 if (!left->isConstant() || !right->isConstant()) {
363 return nullptr;
364 }
365
366 double lhs = left->toConstant()->numberToDouble();
367 double rhs = right->toConstant()->numberToDouble();
368 double ret;
369
370 switch (ins->op()) {
371 case MDefinition::Opcode::Add:
372 ret = lhs + rhs;
373 break;
374 case MDefinition::Opcode::Sub:
375 ret = lhs - rhs;
376 break;
377 case MDefinition::Opcode::Mul:
378 ret = lhs * rhs;
379 break;
380 case MDefinition::Opcode::Div:
381 ret = NumberDiv(lhs, rhs);
382 break;
383 case MDefinition::Opcode::Mod:
384 ret = NumberMod(lhs, rhs);
385 break;
386 default:
387 MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "./../../../../js/src/jit/MIR.cpp"
, 387); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do { MOZ_CrashSequence
(__null, 387); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
388 }
389
390 if (ins->type() == MIRType::Float32) {
391 return MConstant::NewFloat32(alloc, float(ret));
392 }
393 MOZ_ASSERT(ins->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->type() == MIRType::Double)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->type() == MIRType::Double
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->type() == MIRType::Double", "./../../../../js/src/jit/MIR.cpp"
, 393); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::Double"
")"); do { MOZ_CrashSequence(__null, 393); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
394 return MConstant::New(alloc, DoubleValue(ret));
395}
396
397static MConstant* EvaluateConstantNaNOperand(MBinaryInstruction* ins) {
398 auto* left = ins->lhs();
399 auto* right = ins->rhs();
400
401 MOZ_ASSERT(IsTypeRepresentableAsDouble(left->type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsTypeRepresentableAsDouble(left->type()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(IsTypeRepresentableAsDouble(left->type())))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("IsTypeRepresentableAsDouble(left->type())"
, "./../../../../js/src/jit/MIR.cpp", 401); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsTypeRepresentableAsDouble(left->type())"
")"); do { MOZ_CrashSequence(__null, 401); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
402 MOZ_ASSERT(IsTypeRepresentableAsDouble(right->type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsTypeRepresentableAsDouble(right->type()))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(IsTypeRepresentableAsDouble(right->type())))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("IsTypeRepresentableAsDouble(right->type())"
, "./../../../../js/src/jit/MIR.cpp", 402); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsTypeRepresentableAsDouble(right->type())"
")"); do { MOZ_CrashSequence(__null, 402); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
403 MOZ_ASSERT(left->type() == ins->type())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(left->type() == ins->type())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(left->type() == ins->type
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("left->type() == ins->type()", "./../../../../js/src/jit/MIR.cpp"
, 403); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == ins->type()"
")"); do { MOZ_CrashSequence(__null, 403); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
404 MOZ_ASSERT(right->type() == ins->type())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(right->type() == ins->type())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(right->type() == ins->
type()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("right->type() == ins->type()", "./../../../../js/src/jit/MIR.cpp"
, 404); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == ins->type()"
")"); do { MOZ_CrashSequence(__null, 404); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
405
406 // Don't fold NaN if we can't return a floating point type.
407 if (!IsFloatingPointType(ins->type())) {
408 return nullptr;
409 }
410
411 MOZ_ASSERT(!left->isConstant() || !right->isConstant(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!left->isConstant() || !right->isConstant())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!left->isConstant() || !right->isConstant())))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("!left->isConstant() || !right->isConstant()"
" (" "EvaluateConstantOperands should have handled this case"
")", "./../../../../js/src/jit/MIR.cpp", 412); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!left->isConstant() || !right->isConstant()"
") (" "EvaluateConstantOperands should have handled this case"
")"); do { MOZ_CrashSequence(__null, 412); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
412 "EvaluateConstantOperands should have handled this case")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!left->isConstant() || !right->isConstant())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!left->isConstant() || !right->isConstant())))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("!left->isConstant() || !right->isConstant()"
" (" "EvaluateConstantOperands should have handled this case"
")", "./../../../../js/src/jit/MIR.cpp", 412); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!left->isConstant() || !right->isConstant()"
") (" "EvaluateConstantOperands should have handled this case"
")"); do { MOZ_CrashSequence(__null, 412); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
413
414 // One operand must be a constant NaN.
415 MConstant* cst;
416 if (left->isConstant()) {
417 cst = left->toConstant();
418 } else if (right->isConstant()) {
419 cst = right->toConstant();
420 } else {
421 return nullptr;
422 }
423 if (!std::isnan(cst->numberToDouble())) {
424 return nullptr;
425 }
426
427 // Fold to constant NaN.
428 return cst;
429}
430
431static MMul* EvaluateExactReciprocal(TempAllocator& alloc, MDiv* ins) {
432 // we should fold only when it is a floating point operation
433 if (!IsFloatingPointType(ins->type())) {
434 return nullptr;
435 }
436
437 MDefinition* left = ins->getOperand(0);
438 MDefinition* right = ins->getOperand(1);
439
440 if (!right->isConstant()) {
441 return nullptr;
442 }
443
444 int32_t num;
445 if (!mozilla::NumberIsInt32(right->toConstant()->numberToDouble(), &num)) {
446 return nullptr;
447 }
448
449 // check if rhs is a power of two or zero
450 if (num != 0 && !std::has_single_bit(mozilla::Abs(num))) {
451 return nullptr;
452 }
453
454 double ret = 1.0 / double(num);
455
456 MConstant* foldedRhs;
457 if (ins->type() == MIRType::Float32) {
458 foldedRhs = MConstant::NewFloat32(alloc, ret);
459 } else {
460 foldedRhs = MConstant::NewDouble(alloc, ret);
461 }
462
463 MOZ_ASSERT(foldedRhs->type() == ins->type())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(foldedRhs->type() == ins->type())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(foldedRhs->type() == ins->type()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("foldedRhs->type() == ins->type()"
, "./../../../../js/src/jit/MIR.cpp", 463); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "foldedRhs->type() == ins->type()" ")"
); do { MOZ_CrashSequence(__null, 463); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
464 ins->block()->insertBefore(ins, foldedRhs);
465
466 MMul* mul = MMul::New(alloc, left, foldedRhs, ins->type());
467 mul->setMustPreserveNaN(ins->mustPreserveNaN());
468 return mul;
469}
470
471#ifdef JS_JITSPEW1
472const char* MDefinition::opName() const { return OpcodeName(op()); }
473
474void MDefinition::printName(GenericPrinter& out) const {
475 PrintOpcodeName(out, op());
476 out.printf("#%u", id());
477}
478#endif
479
480HashNumber MDefinition::valueHash() const {
481 HashNumber out = HashNumber(op());
482 for (size_t i = 0, e = numOperands(); i < e; i++) {
483 out = addU32ToHash(out, getOperand(i)->id());
484 }
485 if (MDefinition* dep = dependency()) {
486 out = addU32ToHash(out, dep->id());
487 }
488 return out;
489}
490
491HashNumber MNullaryInstruction::valueHash() const {
492 HashNumber hash = HashNumber(op());
493 if (MDefinition* dep = dependency()) {
494 hash = addU32ToHash(hash, dep->id());
495 }
496 MOZ_ASSERT(hash == MDefinition::valueHash())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hash == MDefinition::valueHash())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hash == MDefinition::valueHash
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("hash == MDefinition::valueHash()", "./../../../../js/src/jit/MIR.cpp"
, 496); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hash == MDefinition::valueHash()"
")"); do { MOZ_CrashSequence(__null, 496); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
497 return hash;
498}
499
500HashNumber MUnaryInstruction::valueHash() const {
501 HashNumber hash = HashNumber(op());
502 hash = addU32ToHash(hash, getOperand(0)->id());
503 if (MDefinition* dep = dependency()) {
504 hash = addU32ToHash(hash, dep->id());
505 }
506 MOZ_ASSERT(hash == MDefinition::valueHash())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hash == MDefinition::valueHash())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hash == MDefinition::valueHash
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("hash == MDefinition::valueHash()", "./../../../../js/src/jit/MIR.cpp"
, 506); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hash == MDefinition::valueHash()"
")"); do { MOZ_CrashSequence(__null, 506); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
507 return hash;
508}
509
510HashNumber MBinaryInstruction::valueHash() const {
511 HashNumber hash = HashNumber(op());
512 hash = addU32ToHash(hash, getOperand(0)->id());
513 hash = addU32ToHash(hash, getOperand(1)->id());
514 if (MDefinition* dep = dependency()) {
515 hash = addU32ToHash(hash, dep->id());
516 }
517 MOZ_ASSERT(hash == MDefinition::valueHash())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hash == MDefinition::valueHash())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hash == MDefinition::valueHash
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("hash == MDefinition::valueHash()", "./../../../../js/src/jit/MIR.cpp"
, 517); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hash == MDefinition::valueHash()"
")"); do { MOZ_CrashSequence(__null, 517); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
518 return hash;
519}
520
521HashNumber MTernaryInstruction::valueHash() const {
522 HashNumber hash = HashNumber(op());
523 hash = addU32ToHash(hash, getOperand(0)->id());
524 hash = addU32ToHash(hash, getOperand(1)->id());
525 hash = addU32ToHash(hash, getOperand(2)->id());
526 if (MDefinition* dep = dependency()) {
527 hash = addU32ToHash(hash, dep->id());
528 }
529 MOZ_ASSERT(hash == MDefinition::valueHash())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hash == MDefinition::valueHash())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hash == MDefinition::valueHash
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("hash == MDefinition::valueHash()", "./../../../../js/src/jit/MIR.cpp"
, 529); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hash == MDefinition::valueHash()"
")"); do { MOZ_CrashSequence(__null, 529); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
530 return hash;
531}
532
533HashNumber MQuaternaryInstruction::valueHash() const {
534 HashNumber hash = HashNumber(op());
535 hash = addU32ToHash(hash, getOperand(0)->id());
536 hash = addU32ToHash(hash, getOperand(1)->id());
537 hash = addU32ToHash(hash, getOperand(2)->id());
538 hash = addU32ToHash(hash, getOperand(3)->id());
539 if (MDefinition* dep = dependency()) {
540 hash = addU32ToHash(hash, dep->id());
541 }
542 MOZ_ASSERT(hash == MDefinition::valueHash())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hash == MDefinition::valueHash())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hash == MDefinition::valueHash
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("hash == MDefinition::valueHash()", "./../../../../js/src/jit/MIR.cpp"
, 542); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hash == MDefinition::valueHash()"
")"); do { MOZ_CrashSequence(__null, 542); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
543 return hash;
544}
545
546HashNumber MQuinaryInstruction::valueHash() const {
547 HashNumber hash = HashNumber(op());
548 hash = addU32ToHash(hash, getOperand(0)->id());
549 hash = addU32ToHash(hash, getOperand(1)->id());
550 hash = addU32ToHash(hash, getOperand(2)->id());
551 hash = addU32ToHash(hash, getOperand(3)->id());
552 hash = addU32ToHash(hash, getOperand(4)->id());
553 if (MDefinition* dep = dependency()) {
554 hash = addU32ToHash(hash, dep->id());
555 }
556 MOZ_ASSERT(hash == MDefinition::valueHash())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hash == MDefinition::valueHash())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hash == MDefinition::valueHash
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("hash == MDefinition::valueHash()", "./../../../../js/src/jit/MIR.cpp"
, 556); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hash == MDefinition::valueHash()"
")"); do { MOZ_CrashSequence(__null, 556); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
557 return hash;
558}
559
560const MDefinition* MDefinition::skipObjectGuards() const {
561 const MDefinition* result = this;
562 // These instructions don't modify the object and just guard specific
563 // properties.
564 while (true) {
565 if (result->isGuardShape()) {
566 result = result->toGuardShape()->object();
567 continue;
568 }
569 if (result->isGuardShapeList()) {
570 result = result->toGuardShapeList()->object();
571 continue;
572 }
573 if (result->isGuardMultipleShapes()) {
574 result = result->toGuardMultipleShapes()->object();
575 continue;
576 }
577 if (result->isGuardShapeListToOffset()) {
578 result = result->toGuardShapeListToOffset()->object();
579 continue;
580 }
581 if (result->isGuardMultipleShapesToOffset()) {
582 result = result->toGuardMultipleShapesToOffset()->object();
583 continue;
584 }
585 if (result->isGuardNullProto()) {
586 result = result->toGuardNullProto()->object();
587 continue;
588 }
589 if (result->isGuardProto()) {
590 result = result->toGuardProto()->object();
591 continue;
592 }
593
594 break;
595 }
596
597 return result;
598}
599
600bool MDefinition::congruentIfOperandsEqual(const MDefinition* ins) const {
601 if (op() != ins->op()) {
602 return false;
603 }
604
605 if (type() != ins->type()) {
606 return false;
607 }
608
609 if (isEffectful() || ins->isEffectful()) {
610 return false;
611 }
612
613 if (numOperands() != ins->numOperands()) {
614 return false;
615 }
616
617 for (size_t i = 0, e = numOperands(); i < e; i++) {
618 if (getOperand(i) != ins->getOperand(i)) {
619 return false;
620 }
621 }
622
623 return true;
624}
625
626bool MDefinition::dominates(const MDefinition* other) const {
627 if (block() != other->block()) {
628 return block()->dominates(other->block());
629 }
630
631 // Nothing in a block dominates a phi in that block.
632 if (other->isPhi()) {
633 return false;
634 }
635
636 // Phis dominate all instructions in the block.
637 if (isPhi()) {
638 return true;
639 }
640
641 // If both defs are instructions in the same block, check whether
642 // `this` precedes `other`.
643 MInstructionIterator opIter = block()->begin(toInstruction());
644 do {
645 ++opIter;
646 if (opIter == block()->end()) {
647 return false;
648 }
649 } while (*opIter != other);
650 return true;
651}
652
653MDefinition* MDefinition::foldsTo(TempAllocator& alloc) {
654 // In the default case, there are no constants to fold.
655 return this;
656}
657
658MDefinition* MInstruction::foldsToStore(TempAllocator& alloc) {
659 if (!dependency()) {
660 return nullptr;
661 }
662
663 MDefinition* store = dependency();
664 if (mightAlias(store) != AliasType::MustAlias) {
665 return nullptr;
666 }
667
668 if (!store->block()->dominates(block())) {
669 return nullptr;
670 }
671
672 MDefinition* value;
673 switch (store->op()) {
674 case Opcode::StoreFixedSlot:
675 value = store->toStoreFixedSlot()->value();
676 break;
677 case Opcode::StoreDynamicSlot:
678 value = store->toStoreDynamicSlot()->value();
679 break;
680 case Opcode::StoreElement:
681 value = store->toStoreElement()->value();
682 break;
683 default:
684 MOZ_CRASH("unknown store")do { do { } while (false); MOZ_ReportCrash("" "unknown store"
, "./../../../../js/src/jit/MIR.cpp", 684); AnnotateMozCrashReason
("MOZ_CRASH(" "unknown store" ")"); do { MOZ_CrashSequence(__null
, 684); __attribute__((nomerge)) ::abort(); } while (false); }
while (false)
;
685 }
686
687 // If the type are matching then we return the value which is used as
688 // argument of the store.
689 if (value->type() != type()) {
690 // If we expect to read a type which is more generic than the type seen
691 // by the store, then we box the value used by the store.
692 if (type() != MIRType::Value) {
693 return nullptr;
694 }
695
696 MOZ_ASSERT(value->type() < MIRType::Value)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(value->type() < MIRType::Value)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(value->type() < MIRType
::Value))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("value->type() < MIRType::Value", "./../../../../js/src/jit/MIR.cpp"
, 696); AnnotateMozCrashReason("MOZ_ASSERT" "(" "value->type() < MIRType::Value"
")"); do { MOZ_CrashSequence(__null, 696); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
697 MBox* box = MBox::New(alloc, value);
698 value = box;
699 }
700
701 return value;
702}
703
704void MDefinition::analyzeEdgeCasesForward() {}
705
706void MDefinition::analyzeEdgeCasesBackward() {}
707
708void MInstruction::setResumePoint(MResumePoint* resumePoint) {
709 MOZ_ASSERT(!resumePoint_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!resumePoint_)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!resumePoint_))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!resumePoint_",
"./../../../../js/src/jit/MIR.cpp", 709); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!resumePoint_" ")"); do { MOZ_CrashSequence
(__null, 709); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
710 resumePoint_ = resumePoint;
711 resumePoint_->setInstruction(this);
712}
713
714void MInstruction::stealResumePoint(MInstruction* other) {
715 MResumePoint* resumePoint = other->resumePoint_;
716 other->resumePoint_ = nullptr;
717
718 resumePoint->resetInstruction();
719 setResumePoint(resumePoint);
720}
721
722bool MInstruction::copyResumePointFrom(TempAllocator& alloc,
723 MInstruction* previous) {
724 MResumePoint* rp = previous->resumePoint_->clone(alloc);
725 if (!rp) {
726 return false;
727 }
728 setResumePoint(rp);
729 return true;
730}
731
732void MInstruction::moveResumePointAsEntry() {
733 MOZ_ASSERT(isNop())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isNop())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(isNop()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("isNop()", "./../../../../js/src/jit/MIR.cpp"
, 733); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isNop()" ")"
); do { MOZ_CrashSequence(__null, 733); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
734 block()->clearEntryResumePoint();
735 block()->setEntryResumePoint(resumePoint_);
736 resumePoint_->resetInstruction();
737 resumePoint_ = nullptr;
738}
739
740void MInstruction::clearResumePoint() {
741 resumePoint_->resetInstruction();
742 block()->discardPreAllocatedResumePoint(resumePoint_);
743 resumePoint_ = nullptr;
744}
745
746MDefinition* MTest::foldsDoubleNegation(TempAllocator& alloc) {
747 MDefinition* op = getOperand(0);
748
749 if (op->isNot()) {
750 // If the operand of the Not is itself a Not, they cancel out.
751 MDefinition* opop = op->getOperand(0);
752 if (opop->isNot()) {
753 return MTest::New(alloc, opop->toNot()->input(), ifTrue(), ifFalse());
754 }
755 return MTest::New(alloc, op->toNot()->input(), ifFalse(), ifTrue());
756 }
757 return nullptr;
758}
759
760MDefinition* MTest::foldsConstant(TempAllocator& alloc) {
761 MDefinition* op = getOperand(0);
762 if (MConstant* opConst = op->maybeConstantValue()) {
763 bool b;
764 if (opConst->valueToBoolean(&b)) {
765 return MGoto::New(alloc, b ? ifTrue() : ifFalse());
766 }
767 }
768 return nullptr;
769}
770
771MDefinition* MTest::foldsTypes(TempAllocator& alloc) {
772 MDefinition* op = getOperand(0);
773
774 switch (op->type()) {
775 case MIRType::Undefined:
776 case MIRType::Null:
777 return MGoto::New(alloc, ifFalse());
778 case MIRType::Symbol:
779 return MGoto::New(alloc, ifTrue());
780 default:
781 break;
782 }
783 return nullptr;
784}
785
786class UsesIterator {
787 MDefinition* def_;
788
789 public:
790 explicit UsesIterator(MDefinition* def) : def_(def) {}
791 auto begin() const { return def_->usesBegin(); }
792 auto end() const { return def_->usesEnd(); }
793};
794
795static bool AllInstructionsDeadIfUnused(MBasicBlock* block) {
796 for (auto* ins : *block) {
797 // Skip trivial instructions.
798 if (ins->isNop() || ins->isGoto()) {
799 continue;
800 }
801
802 // All uses must be within the current block.
803 for (auto* use : UsesIterator(ins)) {
804 if (use->consumer()->block() != block) {
805 return false;
806 }
807 }
808
809 // All instructions within this block must be dead if unused.
810 if (!DeadIfUnused(ins)) {
811 return false;
812 }
813 }
814 return true;
815}
816
817MDefinition* MTest::foldsNeedlessControlFlow(TempAllocator& alloc) {
818 // All instructions within both successors need be dead if unused.
819 if (!AllInstructionsDeadIfUnused(ifTrue()) ||
820 !AllInstructionsDeadIfUnused(ifFalse())) {
821 return nullptr;
822 }
823
824 // Both successors must have the same target successor.
825 if (ifTrue()->numSuccessors() != 1 || ifFalse()->numSuccessors() != 1) {
826 return nullptr;
827 }
828 if (ifTrue()->getSuccessor(0) != ifFalse()->getSuccessor(0)) {
829 return nullptr;
830 }
831
832 // The target successor's phis must be redundant. Redundant phis should have
833 // been removed in an earlier pass, so only check if any phis are present,
834 // which is a stronger condition.
835 if (ifTrue()->successorWithPhis()) {
836 return nullptr;
837 }
838
839 return MGoto::New(alloc, ifTrue());
840}
841
842// If a test is dominated by either the true or false path of a previous test of
843// the same condition, then the test is redundant and can be converted into a
844// goto true or goto false, respectively.
845MDefinition* MTest::foldsRedundantTest(TempAllocator& alloc) {
846 MBasicBlock* myBlock = this->block();
847 MDefinition* originalInput = getOperand(0);
848
849 // Handle single and double negatives. This ensures that we do not miss a
850 // folding opportunity due to a condition being inverted.
851 MDefinition* newInput = input();
852 bool inverted = false;
853 if (originalInput->isNot()) {
854 newInput = originalInput->toNot()->input();
855 inverted = true;
856 if (originalInput->toNot()->input()->isNot()) {
857 newInput = originalInput->toNot()->input()->toNot()->input();
858 inverted = false;
859 }
860 }
861
862 // The specific order of traversal does not matter. If there are multiple
863 // dominating redundant tests, they will either agree on direction (in which
864 // case we will prune the same way regardless of order), or they will
865 // disagree, in which case we will eventually be marked entirely dead by the
866 // folding of the redundant parent.
867 for (MUseIterator i(newInput->usesBegin()), e(newInput->usesEnd()); i != e;
868 ++i) {
869 if (!i->consumer()->isDefinition()) {
870 continue;
871 }
872 if (!i->consumer()->toDefinition()->isTest()) {
873 continue;
874 }
875 MTest* otherTest = i->consumer()->toDefinition()->toTest();
876 if (otherTest == this) {
877 continue;
878 }
879
880 if (otherTest->ifFalse()->dominates(myBlock)) {
881 // This test cannot be true, so fold to a goto false.
882 return MGoto::New(alloc, inverted ? ifTrue() : ifFalse());
883 }
884 if (otherTest->ifTrue()->dominates(myBlock)) {
885 // This test cannot be false, so fold to a goto true.
886 return MGoto::New(alloc, inverted ? ifFalse() : ifTrue());
887 }
888 }
889
890 return nullptr;
891}
892
893MDefinition* MTest::foldsTo(TempAllocator& alloc) {
894 if (MDefinition* def = foldsRedundantTest(alloc)) {
895 return def;
896 }
897
898 if (MDefinition* def = foldsDoubleNegation(alloc)) {
899 return def;
900 }
901
902 if (MDefinition* def = foldsConstant(alloc)) {
903 return def;
904 }
905
906 if (MDefinition* def = foldsTypes(alloc)) {
907 return def;
908 }
909
910 if (MDefinition* def = foldsNeedlessControlFlow(alloc)) {
911 return def;
912 }
913
914 return this;
915}
916
917#ifdef JS_JITSPEW1
918void MDefinition::printOpcode(GenericPrinter& out) const {
919 PrintOpcodeName(out, op());
920 if (numOperands() > 0) {
921 out.printf(" <- ");
922 }
923 for (size_t j = 0, e = numOperands(); j < e; j++) {
924 if (j > 0) {
925 out.printf(", ");
926 }
927 if (getUseFor(j)->hasProducer()) {
928 getOperand(j)->printName(out);
929 } else {
930 out.printf("(null)");
931 }
932 }
933}
934
935void MDefinition::dump(GenericPrinter& out) const {
936 printName(out);
937 out.printf(":%s", StringFromMIRType(type()));
938 out.printf(" = ");
939 printOpcode(out);
940 out.printf("\n");
941
942 if (isInstruction()) {
943 if (MResumePoint* resume = toInstruction()->resumePoint()) {
944 resume->dump(out);
945 }
946 }
947}
948
949void MDefinition::dump() const {
950 Fprinter out(stderrstderr);
951 dump(out);
952 out.finish();
953}
954
955void MDefinition::dumpLocation(GenericPrinter& out) const {
956 MResumePoint* rp = nullptr;
957 const char* linkWord = nullptr;
958 if (isInstruction() && toInstruction()->resumePoint()) {
959 rp = toInstruction()->resumePoint();
960 linkWord = "at";
961 } else {
962 rp = block()->entryResumePoint();
963 linkWord = "after";
964 }
965
966 while (rp) {
967 JSScript* script = rp->block()->info().script();
968 uint32_t lineno = PCToLineNumber(rp->block()->info().script(), rp->pc());
969 out.printf(" %s %s:%u\n", linkWord, script->filename(), lineno);
970 rp = rp->caller();
971 linkWord = "in";
972 }
973}
974
975void MDefinition::dumpLocation() const {
976 Fprinter out(stderrstderr);
977 dumpLocation(out);
978 out.finish();
979}
980#endif
981
982#if defined(DEBUG1) || defined(JS_JITSPEW1)
983size_t MDefinition::useCount() const {
984 size_t count = 0;
985 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
986 count++;
987 }
988 return count;
989}
990
991size_t MDefinition::defUseCount() const {
992 size_t count = 0;
993 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
994 if ((*i)->consumer()->isDefinition()) {
995 count++;
996 }
997 }
998 return count;
999}
1000#endif
1001
1002bool MDefinition::hasOneUse() const {
1003 MUseIterator i(uses_.begin());
1004 if (i == uses_.end()) {
1005 return false;
1006 }
1007 i++;
1008 return i == uses_.end();
1009}
1010
1011bool MDefinition::hasOneDefUse() const {
1012 bool hasOneDefUse = false;
1013 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
1014 if (!(*i)->consumer()->isDefinition()) {
1015 continue;
1016 }
1017
1018 // We already have a definition use. So 1+
1019 if (hasOneDefUse) {
1020 return false;
1021 }
1022
1023 // We saw one definition. Loop to test if there is another.
1024 hasOneDefUse = true;
1025 }
1026
1027 return hasOneDefUse;
1028}
1029
1030bool MDefinition::hasOneLiveDefUse() const {
1031 bool hasOneDefUse = false;
1032 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
1033 if (!(*i)->consumer()->isDefinition()) {
1034 continue;
1035 }
1036
1037 MDefinition* def = (*i)->consumer()->toDefinition();
1038 if (def->isRecoveredOnBailout()) {
1039 continue;
1040 }
1041
1042 // We already have a definition use. So 1+
1043 if (hasOneDefUse) {
1044 return false;
1045 }
1046
1047 // We saw one definition. Loop to test if there is another.
1048 hasOneDefUse = true;
1049 }
1050
1051 return hasOneDefUse;
1052}
1053
1054bool MDefinition::hasDefUses() const {
1055 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
1056 if ((*i)->consumer()->isDefinition()) {
1057 return true;
1058 }
1059 }
1060
1061 return false;
1062}
1063
1064bool MDefinition::hasLiveDefUses() const {
1065 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
1066 MNode* ins = (*i)->consumer();
1067 if (ins->isDefinition()) {
1068 if (!ins->toDefinition()->isRecoveredOnBailout()) {
1069 return true;
1070 }
1071 } else {
1072 MOZ_ASSERT(ins->isResumePoint())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->isResumePoint())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->isResumePoint()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("ins->isResumePoint()"
, "./../../../../js/src/jit/MIR.cpp", 1072); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ins->isResumePoint()" ")"); do { MOZ_CrashSequence
(__null, 1072); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1073 if (!ins->toResumePoint()->isRecoverableOperand(*i)) {
1074 return true;
1075 }
1076 }
1077 }
1078 return false;
1079}
1080
1081MDefinition* MDefinition::maybeSingleDefUse() const {
1082 MUseDefIterator use(this);
1083 if (!use) {
1084 // No def-uses.
1085 return nullptr;
1086 }
1087
1088 MDefinition* useDef = use.def();
1089
1090 use++;
1091 if (use) {
1092 // More than one def-use.
1093 return nullptr;
1094 }
1095
1096 return useDef;
1097}
1098
1099MDefinition* MDefinition::maybeMostRecentlyAddedDefUse() const {
1100 MUseDefIterator use(this);
1101 if (!use) {
1102 // No def-uses.
1103 return nullptr;
1104 }
1105
1106 MDefinition* mostRecentUse = use.def();
1107
1108#ifdef DEBUG1
1109 // This function relies on addUse adding new uses to the front of the list.
1110 // Check this invariant by asserting the next few uses are 'older'. Skip this
1111 // for phis because setBackedge can add a new use for a loop phi even if the
1112 // loop body has a use with an id greater than the loop phi's id.
1113 if (!mostRecentUse->isPhi()) {
1114 static constexpr size_t NumUsesToCheck = 3;
1115 use++;
1116 for (size_t i = 0; use && i < NumUsesToCheck; i++, use++) {
1117 MOZ_ASSERT(use.def()->id() <= mostRecentUse->id())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(use.def()->id() <= mostRecentUse->id())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(use.def()->id() <= mostRecentUse->id()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("use.def()->id() <= mostRecentUse->id()"
, "./../../../../js/src/jit/MIR.cpp", 1117); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "use.def()->id() <= mostRecentUse->id()"
")"); do { MOZ_CrashSequence(__null, 1117); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1118 }
1119 }
1120#endif
1121
1122 return mostRecentUse;
1123}
1124
1125void MDefinition::replaceAllUsesWith(MDefinition* dom) {
1126 for (size_t i = 0, e = numOperands(); i < e; ++i) {
1127 getOperand(i)->setImplicitlyUsedUnchecked();
1128 }
1129
1130 justReplaceAllUsesWith(dom);
1131}
1132
1133void MDefinition::justReplaceAllUsesWith(MDefinition* dom) {
1134 MOZ_ASSERT(dom != nullptr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dom != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(dom != nullptr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("dom != nullptr"
, "./../../../../js/src/jit/MIR.cpp", 1134); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "dom != nullptr" ")"); do { MOZ_CrashSequence
(__null, 1134); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1135 MOZ_ASSERT(dom != this)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dom != this)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(dom != this))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("dom != this", "./../../../../js/src/jit/MIR.cpp"
, 1135); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dom != this"
")"); do { MOZ_CrashSequence(__null, 1135); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1136
1137 // Carry over the fact the value has uses which are no longer inspectable
1138 // with the graph.
1139 if (isImplicitlyUsed()) {
1140 dom->setImplicitlyUsedUnchecked();
1141 }
1142
1143 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ++i) {
1144 i->setProducerUnchecked(dom);
1145 }
1146 dom->uses_.takeElements(uses_);
1147}
1148
1149bool MDefinition::optimizeOutAllUses(TempAllocator& alloc) {
1150 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e;) {
1151 MUse* use = *i++;
1152 MConstant* constant = use->consumer()->block()->optimizedOutConstant(alloc);
1153 if (!alloc.ensureBallast()) {
1154 return false;
1155 }
1156
1157 // Update the resume point operand to use the optimized-out constant.
1158 use->setProducerUnchecked(constant);
1159 constant->addUseUnchecked(use);
1160 }
1161
1162 // Remove dangling pointers.
1163 this->uses_.clear();
1164 return true;
1165}
1166
1167void MDefinition::replaceAllLiveUsesWith(MDefinition* dom) {
1168 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e;) {
1169 MUse* use = *i++;
1170 MNode* consumer = use->consumer();
1171 if (consumer->isResumePoint()) {
1172 continue;
1173 }
1174 if (consumer->isDefinition() &&
1175 consumer->toDefinition()->isRecoveredOnBailout()) {
1176 continue;
1177 }
1178
1179 // Update the operand to use the dominating definition.
1180 use->replaceProducer(dom);
1181 }
1182}
1183
1184MConstant* MConstant::New(TempAllocator& alloc, const Value& v) {
1185 return new (alloc) MConstant(alloc, v);
1186}
1187
1188MConstant* MConstant::New(TempAllocator::Fallible alloc, const Value& v) {
1189 return new (alloc) MConstant(alloc.alloc, v);
1190}
1191
1192MConstant* MConstant::NewBoolean(TempAllocator& alloc, bool b) {
1193 return new (alloc) MConstant(b);
1194}
1195
1196MConstant* MConstant::NewDouble(TempAllocator& alloc, double d) {
1197 return new (alloc) MConstant(d);
1198}
1199
1200MConstant* MConstant::NewFloat32(TempAllocator& alloc, double d) {
1201 MOZ_ASSERT(mozilla::IsFloat32Representable(d))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mozilla::IsFloat32Representable(d))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mozilla::IsFloat32Representable
(d)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mozilla::IsFloat32Representable(d)", "./../../../../js/src/jit/MIR.cpp"
, 1201); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::IsFloat32Representable(d)"
")"); do { MOZ_CrashSequence(__null, 1201); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1202 return new (alloc) MConstant(float(d));
1203}
1204
1205MConstant* MConstant::NewInt32(TempAllocator& alloc, int32_t i) {
1206 return new (alloc) MConstant(i);
1207}
1208
1209MConstant* MConstant::NewInt64(TempAllocator& alloc, int64_t i) {
1210 return new (alloc) MConstant(MIRType::Int64, i);
1211}
1212
1213MConstant* MConstant::NewIntPtr(TempAllocator& alloc, intptr_t i) {
1214 return new (alloc) MConstant(MIRType::IntPtr, i);
1215}
1216
1217MConstant* MConstant::NewMagic(TempAllocator& alloc, JSWhyMagic m) {
1218 return new (alloc) MConstant(alloc, MagicValue(m));
1219}
1220
1221MConstant* MConstant::NewNull(TempAllocator& alloc) {
1222 return new (alloc) MConstant(MIRType::Null);
1223}
1224
1225MConstant* MConstant::NewObject(TempAllocator& alloc, JSObject* v) {
1226 return new (alloc) MConstant(v);
1227}
1228
1229MConstant* MConstant::NewShape(TempAllocator& alloc, Shape* s) {
1230 return new (alloc) MConstant(s);
1231}
1232
1233MConstant* MConstant::NewString(TempAllocator& alloc, JSString* s) {
1234 return new (alloc) MConstant(alloc, StringValue(s));
1235}
1236
1237MConstant* MConstant::NewUndefined(TempAllocator& alloc) {
1238 return new (alloc) MConstant(MIRType::Undefined);
1239}
1240
1241static MIRType MIRTypeFromValue(const js::Value& vp) {
1242 if (vp.isDouble()) {
1243 return MIRType::Double;
1244 }
1245 if (vp.isMagic()) {
1246 switch (vp.whyMagic()) {
1247 case JS_OPTIMIZED_OUT:
1248 return MIRType::MagicOptimizedOut;
1249 case JS_ELEMENTS_HOLE:
1250 return MIRType::MagicHole;
1251 case JS_IS_CONSTRUCTING:
1252 return MIRType::MagicIsConstructing;
1253 case JS_UNINITIALIZED_LEXICAL:
1254 return MIRType::MagicUninitializedLexical;
1255 default:
1256 MOZ_ASSERT_UNREACHABLE("Unexpected magic constant")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Unexpected magic constant" ")", "./../../../../js/src/jit/MIR.cpp"
, 1256); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Unexpected magic constant" ")");
do { MOZ_CrashSequence(__null, 1256); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1257 }
1258 }
1259 return MIRTypeFromValueType(vp.extractNonDoubleType());
1260}
1261
1262MConstant::MConstant(TempAllocator& alloc, const js::Value& vp)
1263 : MNullaryInstruction(classOpcode) {
1264 setResultType(MIRTypeFromValue(vp));
1265
1266 MOZ_ASSERT(payload_.asBits == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(payload_.asBits == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(payload_.asBits == 0))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("payload_.asBits == 0"
, "./../../../../js/src/jit/MIR.cpp", 1266); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "payload_.asBits == 0" ")"); do { MOZ_CrashSequence
(__null, 1266); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1267
1268 switch (type()) {
1269 case MIRType::Undefined:
1270 case MIRType::Null:
1271 break;
1272 case MIRType::Boolean:
1273 payload_.b = vp.toBoolean();
1274 break;
1275 case MIRType::Int32:
1276 payload_.i32 = vp.toInt32();
1277 break;
1278 case MIRType::Double:
1279 payload_.d = vp.toDouble();
1280 break;
1281 case MIRType::String: {
1282 JSString* str = vp.toString();
1283 MOZ_ASSERT(!IsInsideNursery(str))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsInsideNursery(str))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(str)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("!IsInsideNursery(str)"
, "./../../../../js/src/jit/MIR.cpp", 1283); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsInsideNursery(str)" ")"); do { MOZ_CrashSequence
(__null, 1283); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1284 payload_.str = &str->asOffThreadAtom();
1285 break;
1286 }
1287 case MIRType::Symbol:
1288 payload_.sym = vp.toSymbol();
1289 break;
1290 case MIRType::BigInt:
1291 MOZ_ASSERT(!IsInsideNursery(vp.toBigInt()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsInsideNursery(vp.toBigInt()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(vp.toBigInt
())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!IsInsideNursery(vp.toBigInt())", "./../../../../js/src/jit/MIR.cpp"
, 1291); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(vp.toBigInt())"
")"); do { MOZ_CrashSequence(__null, 1291); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1292 payload_.bi = vp.toBigInt();
1293 break;
1294 case MIRType::Object:
1295 MOZ_ASSERT(!IsInsideNursery(&vp.toObject()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsInsideNursery(&vp.toObject()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(&vp.toObject
())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!IsInsideNursery(&vp.toObject())", "./../../../../js/src/jit/MIR.cpp"
, 1295); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&vp.toObject())"
")"); do { MOZ_CrashSequence(__null, 1295); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1296 payload_.obj = &vp.toObject();
1297 break;
1298 case MIRType::MagicOptimizedOut:
1299 case MIRType::MagicHole:
1300 case MIRType::MagicIsConstructing:
1301 case MIRType::MagicUninitializedLexical:
1302 break;
1303 default:
1304 MOZ_CRASH("Unexpected type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected type"
, "./../../../../js/src/jit/MIR.cpp", 1304); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected type" ")"); do { MOZ_CrashSequence(
__null, 1304); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
1305 }
1306
1307 setMovable();
1308}
1309
1310MConstant::MConstant(JSObject* obj) : MConstant(MIRType::Object) {
1311 MOZ_ASSERT(!IsInsideNursery(obj))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsInsideNursery(obj))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(obj)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("!IsInsideNursery(obj)"
, "./../../../../js/src/jit/MIR.cpp", 1311); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsInsideNursery(obj)" ")"); do { MOZ_CrashSequence
(__null, 1311); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1312 payload_.obj = obj;
1313}
1314
1315MConstant::MConstant(Shape* shape) : MConstant(MIRType::Shape) {
1316 payload_.shape = shape;
1317}
1318
1319#ifdef DEBUG1
1320void MConstant::assertInitializedPayload() const {
1321 // valueHash() and equals() expect the unused payload bits to be
1322 // initialized to zero. Assert this in debug builds.
1323
1324 switch (type()) {
1325 case MIRType::Int32:
1326 case MIRType::Float32:
1327 if constexpr (std::endian::native == std::endian::little) {
1328 MOZ_ASSERT((payload_.asBits >> 32) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((payload_.asBits >> 32) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((payload_.asBits >> 32
) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(payload_.asBits >> 32) == 0", "./../../../../js/src/jit/MIR.cpp"
, 1328); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(payload_.asBits >> 32) == 0"
")"); do { MOZ_CrashSequence(__null, 1328); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1329 } else {
1330 MOZ_ASSERT((payload_.asBits << 32) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((payload_.asBits << 32) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((payload_.asBits << 32
) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(payload_.asBits << 32) == 0", "./../../../../js/src/jit/MIR.cpp"
, 1330); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(payload_.asBits << 32) == 0"
")"); do { MOZ_CrashSequence(__null, 1330); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1331 }
1332 break;
1333 case MIRType::Boolean:
1334 if constexpr (std::endian::native == std::endian::little) {
1335 MOZ_ASSERT((payload_.asBits >> 1) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((payload_.asBits >> 1) == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((payload_.asBits >> 1)
== 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(payload_.asBits >> 1) == 0", "./../../../../js/src/jit/MIR.cpp"
, 1335); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(payload_.asBits >> 1) == 0"
")"); do { MOZ_CrashSequence(__null, 1335); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1336 } else {
1337 MOZ_ASSERT((payload_.asBits & ~(1ULL << 56)) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((payload_.asBits & ~(1ULL << 56)) == 0)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((payload_.asBits & ~(1ULL << 56)) == 0))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("(payload_.asBits & ~(1ULL << 56)) == 0"
, "./../../../../js/src/jit/MIR.cpp", 1337); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(payload_.asBits & ~(1ULL << 56)) == 0"
")"); do { MOZ_CrashSequence(__null, 1337); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1338 }
1339 break;
1340 case MIRType::Double:
1341 case MIRType::Int64:
1342 break;
1343 case MIRType::String:
1344 case MIRType::Object:
1345 case MIRType::Symbol:
1346 case MIRType::BigInt:
1347 case MIRType::IntPtr:
1348 case MIRType::Shape:
1349 if constexpr (std::endian::native == std::endian::little) {
1350 MOZ_ASSERT_IF(JS_BITS_PER_WORD == 32, (payload_.asBits >> 32) == 0)do { if (64 == 32) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype((payload_.asBits >> 32) == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!((payload_.asBits >> 32) == 0))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("(payload_.asBits >> 32) == 0"
, "./../../../../js/src/jit/MIR.cpp", 1350); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(payload_.asBits >> 32) == 0" ")"); do
{ MOZ_CrashSequence(__null, 1350); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false); } } while (false
)
;
1351 } else {
1352 MOZ_ASSERT_IF(JS_BITS_PER_WORD == 32, (payload_.asBits << 32) == 0)do { if (64 == 32) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype((payload_.asBits << 32) == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!((payload_.asBits << 32) == 0))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("(payload_.asBits << 32) == 0"
, "./../../../../js/src/jit/MIR.cpp", 1352); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(payload_.asBits << 32) == 0" ")"); do
{ MOZ_CrashSequence(__null, 1352); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false); } } while (false
)
;
1353 }
1354 break;
1355 default:
1356 MOZ_ASSERT(IsNullOrUndefined(type()) || IsMagicType(type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsNullOrUndefined(type()) || IsMagicType(type()))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(IsNullOrUndefined(type()) || IsMagicType(type())))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("IsNullOrUndefined(type()) || IsMagicType(type())"
, "./../../../../js/src/jit/MIR.cpp", 1356); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsNullOrUndefined(type()) || IsMagicType(type())"
")"); do { MOZ_CrashSequence(__null, 1356); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1357 MOZ_ASSERT(payload_.asBits == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(payload_.asBits == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(payload_.asBits == 0))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("payload_.asBits == 0"
, "./../../../../js/src/jit/MIR.cpp", 1357); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "payload_.asBits == 0" ")"); do { MOZ_CrashSequence
(__null, 1357); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1358 break;
1359 }
1360}
1361#endif
1362
1363HashNumber MConstant::valueHash() const {
1364 static_assert(sizeof(Payload) == sizeof(uint64_t),
1365 "Code below assumes payload fits in 64 bits");
1366
1367 assertInitializedPayload();
1368 return ConstantValueHash(type(), payload_.asBits);
1369}
1370
1371HashNumber MConstantProto::valueHash() const {
1372 HashNumber hash = protoObject()->valueHash();
1373 const MDefinition* receiverObject = getReceiverObject();
1374 if (receiverObject) {
1375 hash = addU32ToHash(hash, receiverObject->id());
1376 }
1377 return hash;
1378}
1379
1380bool MConstant::congruentTo(const MDefinition* ins) const {
1381 return ins->isConstant() && equals(ins->toConstant());
1382}
1383
1384#ifdef JS_JITSPEW1
1385void MConstant::printOpcode(GenericPrinter& out) const {
1386 PrintOpcodeName(out, op());
1387 out.printf(" ");
1388 switch (type()) {
1389 case MIRType::Undefined:
1390 out.printf("undefined");
1391 break;
1392 case MIRType::Null:
1393 out.printf("null");
1394 break;
1395 case MIRType::Boolean:
1396 out.printf(toBoolean() ? "true" : "false");
1397 break;
1398 case MIRType::Int32:
1399 out.printf("0x%x", uint32_t(toInt32()));
1400 break;
1401 case MIRType::Int64:
1402 out.printf("0x%" PRIx64"l" "x", uint64_t(toInt64()));
1403 break;
1404 case MIRType::IntPtr:
1405 out.printf("0x%" PRIxPTR"l" "x", uintptr_t(toIntPtr()));
1406 break;
1407 case MIRType::Double:
1408 out.printf("%.16g", toDouble());
1409 break;
1410 case MIRType::Float32: {
1411 float val = toFloat32();
1412 out.printf("%.16g", val);
1413 break;
1414 }
1415 case MIRType::Object:
1416 if (toObject().is<JSFunction>()) {
1417 JSFunction* fun = &toObject().as<JSFunction>();
1418 if (fun->maybePartialDisplayAtom()) {
1419 out.put("function ");
1420 EscapedStringPrinter(out, fun->maybePartialDisplayAtom(), 0);
1421 } else {
1422 out.put("unnamed function");
1423 }
1424 if (fun->hasBaseScript()) {
1425 BaseScript* script = fun->baseScript();
1426 out.printf(" (%s:%u)", script->filename() ? script->filename() : "",
1427 script->lineno());
1428 }
1429 out.printf(" at %p", (void*)fun);
1430 break;
1431 }
1432 out.printf("object %p (%s)", (void*)&toObject(),
1433 toObject().getClass()->name);
1434 break;
1435 case MIRType::Symbol:
1436 out.printf("symbol at %p", (void*)toSymbol());
1437 break;
1438 case MIRType::BigInt:
1439 out.printf("BigInt at %p", (void*)toBigInt());
1440 break;
1441 case MIRType::String:
1442 out.printf("string %p", (void*)toString());
1443 break;
1444 case MIRType::Shape:
1445 out.printf("shape at %p", (void*)toShape());
1446 break;
1447 case MIRType::MagicHole:
1448 out.printf("magic hole");
1449 break;
1450 case MIRType::MagicIsConstructing:
1451 out.printf("magic is-constructing");
1452 break;
1453 case MIRType::MagicOptimizedOut:
1454 out.printf("magic optimized-out");
1455 break;
1456 case MIRType::MagicUninitializedLexical:
1457 out.printf("magic uninitialized-lexical");
1458 break;
1459 default:
1460 MOZ_CRASH("unexpected type")do { do { } while (false); MOZ_ReportCrash("" "unexpected type"
, "./../../../../js/src/jit/MIR.cpp", 1460); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected type" ")"); do { MOZ_CrashSequence(
__null, 1460); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
1461 }
1462}
1463#endif
1464
1465bool MConstant::canProduceFloat32() const {
1466 if (!isTypeRepresentableAsDouble()) {
1467 return false;
1468 }
1469
1470 if (type() == MIRType::Int32) {
1471 return IsFloat32Representable(static_cast<double>(toInt32()));
1472 }
1473 if (type() == MIRType::Double) {
1474 return IsFloat32Representable(toDouble());
1475 }
1476 MOZ_ASSERT(type() == MIRType::Float32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type() == MIRType::Float32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type() == MIRType::Float32))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("type() == MIRType::Float32"
, "./../../../../js/src/jit/MIR.cpp", 1476); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Float32" ")"); do { MOZ_CrashSequence
(__null, 1476); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1477 return true;
1478}
1479
1480Value MConstant::toJSValue() const {
1481 // Wasm has types like int64 that cannot be stored as js::Value. It also
1482 // doesn't want the NaN canonicalization enforced by js::Value.
1483 MOZ_ASSERT(!IsCompilingWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsCompilingWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsCompilingWasm()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("!IsCompilingWasm()"
, "./../../../../js/src/jit/MIR.cpp", 1483); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsCompilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 1483); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1484
1485 switch (type()) {
1486 case MIRType::Undefined:
1487 return UndefinedValue();
1488 case MIRType::Null:
1489 return NullValue();
1490 case MIRType::Boolean:
1491 return BooleanValue(toBoolean());
1492 case MIRType::Int32:
1493 return Int32Value(toInt32());
1494 case MIRType::Double:
1495 return DoubleValue(toDouble());
1496 case MIRType::Float32:
1497 return Float32Value(toFloat32());
1498 case MIRType::String:
1499 return StringValue(toString()->unwrap());
1500 case MIRType::Symbol:
1501 return SymbolValue(toSymbol());
1502 case MIRType::BigInt:
1503 return BigIntValue(toBigInt());
1504 case MIRType::Object:
1505 return ObjectValue(toObject());
1506 case MIRType::Shape:
1507 return PrivateGCThingValue(toShape());
1508 case MIRType::MagicOptimizedOut:
1509 return MagicValue(JS_OPTIMIZED_OUT);
1510 case MIRType::MagicHole:
1511 return MagicValue(JS_ELEMENTS_HOLE);
1512 case MIRType::MagicIsConstructing:
1513 return MagicValue(JS_IS_CONSTRUCTING);
1514 case MIRType::MagicUninitializedLexical:
1515 return MagicValue(JS_UNINITIALIZED_LEXICAL);
1516 default:
1517 MOZ_CRASH("Unexpected type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected type"
, "./../../../../js/src/jit/MIR.cpp", 1517); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected type" ")"); do { MOZ_CrashSequence(
__null, 1517); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
1518 }
1519}
1520
1521bool MConstant::valueToBoolean(bool* res) const {
1522 switch (type()) {
1523 case MIRType::Boolean:
1524 *res = toBoolean();
1525 return true;
1526 case MIRType::Int32:
1527 *res = toInt32() != 0;
1528 return true;
1529 case MIRType::Int64:
1530 *res = toInt64() != 0;
1531 return true;
1532 case MIRType::IntPtr:
1533 *res = toIntPtr() != 0;
1534 return true;
1535 case MIRType::Double:
1536 *res = !std::isnan(toDouble()) && toDouble() != 0.0;
1537 return true;
1538 case MIRType::Float32:
1539 *res = !std::isnan(toFloat32()) && toFloat32() != 0.0f;
1540 return true;
1541 case MIRType::Null:
1542 case MIRType::Undefined:
1543 *res = false;
1544 return true;
1545 case MIRType::Symbol:
1546 *res = true;
1547 return true;
1548 case MIRType::BigInt:
1549 *res = !toBigInt()->isZero();
1550 return true;
1551 case MIRType::String:
1552 *res = toString()->length() != 0;
1553 return true;
1554 case MIRType::Object:
1555 // Calling EmulatesUndefined here is racy if we're compiling off-thread
1556 // because it reads obj->shape->base->clasp, so just give up.
1557 // Note that we could use fuses to optimize this (bug 1874905).
1558 return false;
1559 default:
1560 MOZ_ASSERT(IsMagicType(type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsMagicType(type()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsMagicType(type())))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("IsMagicType(type())"
, "./../../../../js/src/jit/MIR.cpp", 1560); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsMagicType(type())" ")"); do { MOZ_CrashSequence
(__null, 1560); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1561 return false;
1562 }
1563}
1564
1565#ifdef JS_JITSPEW1
1566void MControlInstruction::printOpcode(GenericPrinter& out) const {
1567 MDefinition::printOpcode(out);
1568 if (numSuccessors() > 0) {
1569 out.printf(" -> ");
1570 }
1571 for (size_t j = 0; j < numSuccessors(); j++) {
1572 if (j > 0) {
1573 out.printf(", ");
1574 }
1575 if (getSuccessor(j)) {
1576 out.printf("block %u", getSuccessor(j)->id());
1577 } else {
1578 out.printf("(null-to-be-patched)");
1579 }
1580 }
1581}
1582
1583void MCompare::printOpcode(GenericPrinter& out) const {
1584 MDefinition::printOpcode(out);
1585 out.printf(" %s", CodeName(jsop()));
1586}
1587
1588void MTypeOfIs::printOpcode(GenericPrinter& out) const {
1589 MDefinition::printOpcode(out);
1590 out.printf(" %s", CodeName(jsop()));
1591
1592 const char* name = "";
Value stored to 'name' during its initialization is never read
1593 switch (jstype()) {
1594 case JSTYPE_UNDEFINED:
1595 name = "undefined";
1596 break;
1597 case JSTYPE_OBJECT:
1598 name = "object";
1599 break;
1600 case JSTYPE_FUNCTION:
1601 name = "function";
1602 break;
1603 case JSTYPE_STRING:
1604 name = "string";
1605 break;
1606 case JSTYPE_NUMBER:
1607 name = "number";
1608 break;
1609 case JSTYPE_BOOLEAN:
1610 name = "boolean";
1611 break;
1612 case JSTYPE_SYMBOL:
1613 name = "symbol";
1614 break;
1615 case JSTYPE_BIGINT:
1616 name = "bigint";
1617 break;
1618 case JSTYPE_LIMIT:
1619 MOZ_CRASH("Unexpected type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected type"
, "./../../../../js/src/jit/MIR.cpp", 1619); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected type" ")"); do { MOZ_CrashSequence(
__null, 1619); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
1620 }
1621 out.printf(" '%s'", name);
1622}
1623
1624void MLoadUnboxedScalar::printOpcode(GenericPrinter& out) const {
1625 MDefinition::printOpcode(out);
1626 out.printf(" %s", Scalar::name(storageType()));
1627}
1628
1629void MLoadDataViewElement::printOpcode(GenericPrinter& out) const {
1630 MDefinition::printOpcode(out);
1631 out.printf(" %s", Scalar::name(storageType()));
1632}
1633
1634void MAssertRange::printOpcode(GenericPrinter& out) const {
1635 MDefinition::printOpcode(out);
1636 out.put(" ");
1637 assertedRange()->dump(out);
1638}
1639
1640void MNearbyInt::printOpcode(GenericPrinter& out) const {
1641 MDefinition::printOpcode(out);
1642 const char* roundingModeStr = nullptr;
1643 switch (roundingMode_) {
1644 case RoundingMode::Up:
1645 roundingModeStr = "(up)";
1646 break;
1647 case RoundingMode::Down:
1648 roundingModeStr = "(down)";
1649 break;
1650 case RoundingMode::NearestTiesToEven:
1651 roundingModeStr = "(nearest ties even)";
1652 break;
1653 case RoundingMode::TowardsZero:
1654 roundingModeStr = "(towards zero)";
1655 break;
1656 }
1657 out.printf(" %s", roundingModeStr);
1658}
1659#endif
1660
1661MDefinition* MSign::foldsTo(TempAllocator& alloc) {
1662 MDefinition* input = getOperand(0);
1663 if (!input->isConstant() ||
1664 !input->toConstant()->isTypeRepresentableAsDouble()) {
1665 return this;
1666 }
1667
1668 double in = input->toConstant()->numberToDouble();
1669 double out = js::math_sign_impl(in);
1670
1671 if (type() == MIRType::Int32) {
1672 // Decline folding if this is an int32 operation, but the result type
1673 // isn't an int32.
1674 int32_t i;
1675 if (!mozilla::NumberIsInt32(out, &i)) {
1676 return this;
1677 }
1678 return MConstant::NewInt32(alloc, i);
1679 }
1680
1681 return MConstant::NewDouble(alloc, out);
1682}
1683
1684const char* MMathFunction::FunctionName(UnaryMathFunction function) {
1685 return GetUnaryMathFunctionName(function);
1686}
1687
1688#ifdef JS_JITSPEW1
1689void MMathFunction::printOpcode(GenericPrinter& out) const {
1690 MDefinition::printOpcode(out);
1691 out.printf(" %s", FunctionName(function()));
1692}
1693#endif
1694
1695MDefinition* MMathFunction::foldsTo(TempAllocator& alloc) {
1696 MDefinition* input = getOperand(0);
1697 if (!input->isConstant() ||
1698 !input->toConstant()->isTypeRepresentableAsDouble()) {
1699 return this;
1700 }
1701
1702 UnaryMathFunctionType funPtr = GetUnaryMathFunctionPtr(function());
1703
1704 double in = input->toConstant()->numberToDouble();
1705
1706 // The function pointer call can't GC.
1707 JS::AutoSuppressGCAnalysis nogc;
1708 double out = funPtr(in);
1709
1710 if (input->type() == MIRType::Float32) {
1711 return MConstant::NewFloat32(alloc, out);
1712 }
1713 return MConstant::NewDouble(alloc, out);
1714}
1715
1716MDefinition* MAtomicIsLockFree::foldsTo(TempAllocator& alloc) {
1717 MDefinition* input = getOperand(0);
1718 if (!input->isConstant() || input->type() != MIRType::Int32) {
1719 return this;
1720 }
1721
1722 int32_t i = input->toConstant()->toInt32();
1723 return MConstant::NewBoolean(alloc, AtomicOperations::isLockfreeJS(i));
1724}
1725
1726// Define |THIS_SLOT| as part of this translation unit, as it is used to
1727// specialized the parameterized |New| function calls introduced by
1728// TRIVIAL_NEW_WRAPPERS.
1729const int32_t MParameter::THIS_SLOT;
1730
1731#ifdef JS_JITSPEW1
1732void MParameter::printOpcode(GenericPrinter& out) const {
1733 PrintOpcodeName(out, op());
1734 if (index() == THIS_SLOT) {
1735 out.printf(" THIS_SLOT");
1736 } else {
1737 out.printf(" %d", index());
1738 }
1739}
1740#endif
1741
1742HashNumber MParameter::valueHash() const {
1743 HashNumber hash = MNullaryInstruction::valueHash();
1744 hash = addU32ToHash(hash, index_);
1745 return hash;
1746}
1747
1748bool MParameter::congruentTo(const MDefinition* ins) const {
1749 if (!ins->isParameter()) {
1750 return false;
1751 }
1752
1753 return ins->toParameter()->index() == index_;
1754}
1755
1756WrappedFunction::WrappedFunction(JSFunction* nativeFun, uint16_t nargs,
1757 FunctionFlags flags)
1758 : nativeFun_(nativeFun), nargs_(nargs), flags_(flags) {
1759 MOZ_ASSERT_IF(nativeFun, isNativeWithoutJitEntry())do { if (nativeFun) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(isNativeWithoutJitEntry())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(isNativeWithoutJitEntry())))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("isNativeWithoutJitEntry()"
, "./../../../../js/src/jit/MIR.cpp", 1759); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "isNativeWithoutJitEntry()" ")"); do { MOZ_CrashSequence
(__null, 1759); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
1760
1761#ifdef DEBUG1
1762 // If we are not running off-main thread we can assert that the
1763 // metadata is consistent.
1764 if (!CanUseExtraThreads() && nativeFun) {
1765 MOZ_ASSERT(nativeFun->nargs() == nargs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nativeFun->nargs() == nargs)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(nativeFun->nargs() == nargs
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"nativeFun->nargs() == nargs", "./../../../../js/src/jit/MIR.cpp"
, 1765); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nativeFun->nargs() == nargs"
")"); do { MOZ_CrashSequence(__null, 1765); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1766
1767 MOZ_ASSERT(nativeFun->isNativeWithoutJitEntry() ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nativeFun->isNativeWithoutJitEntry() == isNativeWithoutJitEntry
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(nativeFun->isNativeWithoutJitEntry() == isNativeWithoutJitEntry
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("nativeFun->isNativeWithoutJitEntry() == isNativeWithoutJitEntry()"
, "./../../../../js/src/jit/MIR.cpp", 1768); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nativeFun->isNativeWithoutJitEntry() == isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 1768); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1768 isNativeWithoutJitEntry())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nativeFun->isNativeWithoutJitEntry() == isNativeWithoutJitEntry
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(nativeFun->isNativeWithoutJitEntry() == isNativeWithoutJitEntry
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("nativeFun->isNativeWithoutJitEntry() == isNativeWithoutJitEntry()"
, "./../../../../js/src/jit/MIR.cpp", 1768); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nativeFun->isNativeWithoutJitEntry() == isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 1768); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1769 MOZ_ASSERT(nativeFun->hasJitEntry() == hasJitEntry())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nativeFun->hasJitEntry() == hasJitEntry())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(nativeFun->hasJitEntry() == hasJitEntry()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("nativeFun->hasJitEntry() == hasJitEntry()"
, "./../../../../js/src/jit/MIR.cpp", 1769); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nativeFun->hasJitEntry() == hasJitEntry()"
")"); do { MOZ_CrashSequence(__null, 1769); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1770 MOZ_ASSERT(nativeFun->isConstructor() == isConstructor())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nativeFun->isConstructor() == isConstructor())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(nativeFun->isConstructor() == isConstructor()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("nativeFun->isConstructor() == isConstructor()"
, "./../../../../js/src/jit/MIR.cpp", 1770); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nativeFun->isConstructor() == isConstructor()"
")"); do { MOZ_CrashSequence(__null, 1770); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1771 MOZ_ASSERT(nativeFun->isClassConstructor() == isClassConstructor())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nativeFun->isClassConstructor() == isClassConstructor
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(nativeFun->isClassConstructor() == isClassConstructor
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("nativeFun->isClassConstructor() == isClassConstructor()"
, "./../../../../js/src/jit/MIR.cpp", 1771); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nativeFun->isClassConstructor() == isClassConstructor()"
")"); do { MOZ_CrashSequence(__null, 1771); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1772 }
1773#endif
1774}
1775
1776MCall* MCall::New(TempAllocator& alloc, WrappedFunction* target, size_t maxArgc,
1777 size_t numActualArgs, bool construct, bool ignoresReturnValue,
1778 bool isDOMCall, mozilla::Maybe<DOMObjectKind> objectKind,
1779 mozilla::Maybe<gc::Heap> initialHeap) {
1780 MOZ_ASSERT(isDOMCall == objectKind.isSome())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isDOMCall == objectKind.isSome())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(isDOMCall == objectKind.isSome
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("isDOMCall == objectKind.isSome()", "./../../../../js/src/jit/MIR.cpp"
, 1780); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isDOMCall == objectKind.isSome()"
")"); do { MOZ_CrashSequence(__null, 1780); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1781 MOZ_ASSERT(isDOMCall == initialHeap.isSome())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isDOMCall == initialHeap.isSome())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(isDOMCall == initialHeap.isSome
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("isDOMCall == initialHeap.isSome()", "./../../../../js/src/jit/MIR.cpp"
, 1781); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isDOMCall == initialHeap.isSome()"
")"); do { MOZ_CrashSequence(__null, 1781); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1782
1783 MOZ_ASSERT(maxArgc >= numActualArgs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(maxArgc >= numActualArgs)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(maxArgc >= numActualArgs)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("maxArgc >= numActualArgs"
, "./../../../../js/src/jit/MIR.cpp", 1783); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "maxArgc >= numActualArgs" ")"); do { MOZ_CrashSequence
(__null, 1783); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1784 MCall* ins;
1785 if (isDOMCall) {
1786 MOZ_ASSERT(!construct)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!construct)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!construct))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!construct", "./../../../../js/src/jit/MIR.cpp"
, 1786); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!construct"
")"); do { MOZ_CrashSequence(__null, 1786); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1787 ins = new (alloc)
1788 MCallDOMNative(target, numActualArgs, *objectKind, *initialHeap);
1789 } else {
1790 ins =
1791 new (alloc) MCall(target, numActualArgs, construct, ignoresReturnValue);
1792 }
1793 if (!ins->init(alloc, maxArgc + NumNonArgumentOperands)) {
1794 return nullptr;
1795 }
1796 return ins;
1797}
1798
1799AliasSet MCallDOMNative::getAliasSet() const {
1800 const JSJitInfo* jitInfo = getJitInfo();
1801
1802 // If we don't know anything about the types of our arguments, we have to
1803 // assume that type-coercions can have side-effects, so we need to alias
1804 // everything.
1805 if (jitInfo->aliasSet() == JSJitInfo::AliasEverything ||
1806 !jitInfo->isTypedMethodJitInfo()) {
1807 return AliasSet::Store(AliasSet::Any);
1808 }
1809
1810 uint32_t argIndex = 0;
1811 const JSTypedMethodJitInfo* methodInfo =
1812 reinterpret_cast<const JSTypedMethodJitInfo*>(jitInfo);
1813 for (const JSJitInfo::ArgType* argType = methodInfo->argTypes;
1814 *argType != JSJitInfo::ArgTypeListEnd; ++argType, ++argIndex) {
1815 if (argIndex >= numActualArgs()) {
1816 // Passing through undefined can't have side-effects
1817 continue;
1818 }
1819 // getArg(0) is "this", so skip it
1820 MDefinition* arg = getArg(argIndex + 1);
1821 MIRType actualType = arg->type();
1822 // The only way to reliably avoid side-effects given the information we
1823 // have here is if we're passing in a known primitive value to an
1824 // argument that expects a primitive value.
1825 //
1826 // XXXbz maybe we need to communicate better information. For example,
1827 // a sequence argument will sort of unavoidably have side effects, while
1828 // a typed array argument won't have any, but both are claimed to be
1829 // JSJitInfo::Object. But if we do that, we need to watch out for our
1830 // movability/DCE-ability bits: if we have an arg type that can reliably
1831 // throw an exception on conversion, that might not affect our alias set
1832 // per se, but it should prevent us being moved or DCE-ed, unless we
1833 // know the incoming things match that arg type and won't throw.
1834 //
1835 if ((actualType == MIRType::Value || actualType == MIRType::Object) ||
1836 (*argType & JSJitInfo::Object)) {
1837 return AliasSet::Store(AliasSet::Any);
1838 }
1839 }
1840
1841 // We checked all the args, and they check out. So we only alias DOM
1842 // mutations or alias nothing, depending on the alias set in the jitinfo.
1843 if (jitInfo->aliasSet() == JSJitInfo::AliasNone) {
1844 return AliasSet::None();
1845 }
1846
1847 MOZ_ASSERT(jitInfo->aliasSet() == JSJitInfo::AliasDOMSets)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(jitInfo->aliasSet() == JSJitInfo::AliasDOMSets)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(jitInfo->aliasSet() == JSJitInfo::AliasDOMSets)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("jitInfo->aliasSet() == JSJitInfo::AliasDOMSets"
, "./../../../../js/src/jit/MIR.cpp", 1847); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "jitInfo->aliasSet() == JSJitInfo::AliasDOMSets"
")"); do { MOZ_CrashSequence(__null, 1847); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1848 return AliasSet::Load(AliasSet::DOMProperty);
1849}
1850
1851void MCallDOMNative::computeMovable() {
1852 // We are movable if the jitinfo says we can be and if we're also not
1853 // effectful. The jitinfo can't check for the latter, since it depends on
1854 // the types of our arguments.
1855 const JSJitInfo* jitInfo = getJitInfo();
1856
1857 MOZ_ASSERT_IF(jitInfo->isMovable,do { if (jitInfo->isMovable) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(jitInfo->aliasSet
() != JSJitInfo::AliasEverything)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(jitInfo->aliasSet() != JSJitInfo
::AliasEverything))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("jitInfo->aliasSet() != JSJitInfo::AliasEverything", "./../../../../js/src/jit/MIR.cpp"
, 1858); AnnotateMozCrashReason("MOZ_ASSERT" "(" "jitInfo->aliasSet() != JSJitInfo::AliasEverything"
")"); do { MOZ_CrashSequence(__null, 1858); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
1858 jitInfo->aliasSet() != JSJitInfo::AliasEverything)do { if (jitInfo->isMovable) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(jitInfo->aliasSet
() != JSJitInfo::AliasEverything)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(jitInfo->aliasSet() != JSJitInfo
::AliasEverything))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("jitInfo->aliasSet() != JSJitInfo::AliasEverything", "./../../../../js/src/jit/MIR.cpp"
, 1858); AnnotateMozCrashReason("MOZ_ASSERT" "(" "jitInfo->aliasSet() != JSJitInfo::AliasEverything"
")"); do { MOZ_CrashSequence(__null, 1858); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
1859
1860 if (jitInfo->isMovable && !isEffectful()) {
1861 setMovable();
1862 }
1863}
1864
1865bool MCallDOMNative::congruentTo(const MDefinition* ins) const {
1866 if (!isMovable()) {
1867 return false;
1868 }
1869
1870 if (!ins->isCall()) {
1871 return false;
1872 }
1873
1874 const MCall* call = ins->toCall();
1875
1876 if (!call->isCallDOMNative()) {
1877 return false;
1878 }
1879
1880 if (getSingleTarget() != call->getSingleTarget()) {
1881 return false;
1882 }
1883
1884 if (isConstructing() != call->isConstructing()) {
1885 return false;
1886 }
1887
1888 if (numActualArgs() != call->numActualArgs()) {
1889 return false;
1890 }
1891
1892 if (!congruentIfOperandsEqual(call)) {
1893 return false;
1894 }
1895
1896 // The other call had better be movable at this point!
1897 MOZ_ASSERT(call->isMovable())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(call->isMovable())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(call->isMovable()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("call->isMovable()"
, "./../../../../js/src/jit/MIR.cpp", 1897); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "call->isMovable()" ")"); do { MOZ_CrashSequence
(__null, 1897); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1898
1899 return true;
1900}
1901
1902const JSJitInfo* MCallDOMNative::getJitInfo() const {
1903 MOZ_ASSERT(getSingleTarget()->hasJitInfo())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(getSingleTarget()->hasJitInfo())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(getSingleTarget()->hasJitInfo
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("getSingleTarget()->hasJitInfo()", "./../../../../js/src/jit/MIR.cpp"
, 1903); AnnotateMozCrashReason("MOZ_ASSERT" "(" "getSingleTarget()->hasJitInfo()"
")"); do { MOZ_CrashSequence(__null, 1903); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1904 return getSingleTarget()->jitInfo();
1905}
1906
1907MCallClassHook* MCallClassHook::New(TempAllocator& alloc, JSNative target,
1908 uint32_t argc, bool constructing) {
1909 auto* ins = new (alloc) MCallClassHook(target, constructing);
1910
1911 // Add callee + |this| + (if constructing) newTarget.
1912 uint32_t numOperands = 2 + argc + constructing;
1913
1914 if (!ins->init(alloc, numOperands)) {
1915 return nullptr;
1916 }
1917
1918 return ins;
1919}
1920
1921MDefinition* MStringLength::foldsTo(TempAllocator& alloc) {
1922 if (string()->isConstant()) {
1923 JSOffThreadAtom* str = string()->toConstant()->toString();
1924 return MConstant::NewInt32(alloc, str->length());
1925 }
1926
1927 // MFromCharCode returns a one-element string.
1928 if (string()->isFromCharCode()) {
1929 return MConstant::NewInt32(alloc, 1);
1930 }
1931
1932 return this;
1933}
1934
1935MDefinition* MConcat::foldsTo(TempAllocator& alloc) {
1936 if (lhs()->isConstant() && lhs()->toConstant()->toString()->empty()) {
1937 return rhs();
1938 }
1939
1940 if (rhs()->isConstant() && rhs()->toConstant()->toString()->empty()) {
1941 return lhs();
1942 }
1943
1944 return this;
1945}
1946
1947MDefinition* MStringConvertCase::foldsTo(TempAllocator& alloc) {
1948 MDefinition* string = this->string();
1949
1950 // Handle the pattern |str[idx].toUpperCase()| and simplify it from
1951 // |StringConvertCase(FromCharCode(CharCodeAt(str, idx)))| to just
1952 // |CharCodeConvertCase(CharCodeAt(str, idx))|.
1953 if (string->isFromCharCode()) {
1954 auto* charCode = string->toFromCharCode()->code();
1955 return MCharCodeConvertCase::New(alloc, charCode, stringCase_);
1956 }
1957
1958 // Handle the pattern |num.toString(base).toUpperCase()| and simplify it to
1959 // directly return the string representation in the correct case.
1960 if (string->isInt32ToStringWithBase()) {
1961 auto* toString = string->toInt32ToStringWithBase();
1962
1963 if (toString->stringCase() == stringCase_) {
1964 return toString;
1965 }
1966 return MInt32ToStringWithBase::New(alloc, toString->input(),
1967 toString->base(), stringCase_);
1968 }
1969
1970 return this;
1971}
1972
1973// Return true if |def| is `MConstant(Int32(0))`.
1974static bool IsConstantZeroInt32(MDefinition* def) {
1975 return def->isConstant() && def->toConstant()->isInt32(0);
1976}
1977
1978// If |def| is `MBitOr` and one operand is `MConstant(Int32(0))`, then return
1979// the other operand. Otherwise return |def|.
1980static MDefinition* RemoveUnnecessaryBitOps(MDefinition* def) {
1981 if (def->isBitOr()) {
1982 auto* bitOr = def->toBitOr();
1983 if (IsConstantZeroInt32(bitOr->lhs())) {
1984 return bitOr->rhs();
1985 }
1986 if (IsConstantZeroInt32(bitOr->rhs())) {
1987 return bitOr->lhs();
1988 }
1989 }
1990 return def;
1991}
1992
1993// Return a match if both operands of |binary| have the requested types. If
1994// |binary| is commutative, the operands may appear in any order.
1995template <typename Lhs, typename Rhs>
1996static mozilla::Maybe<std::pair<Lhs*, Rhs*>> MatchOperands(
1997 MBinaryInstruction* binary) {
1998 auto* lhs = binary->lhs();
1999 auto* rhs = binary->rhs();
2000 if (lhs->is<Lhs>() && rhs->is<Rhs>()) {
2001 return mozilla::Some(std::pair{lhs->to<Lhs>(), rhs->to<Rhs>()});
2002 }
2003 if (binary->isCommutative() && rhs->is<Lhs>() && lhs->is<Rhs>()) {
2004 return mozilla::Some(std::pair{rhs->to<Lhs>(), lhs->to<Rhs>()});
2005 }
2006 return mozilla::Nothing();
2007}
2008
2009static bool IsSubstrTo(MSubstr* substr, int32_t len) {
2010 // We want to match this pattern:
2011 //
2012 // Substr(string, Constant(0), Min(Constant(length), StringLength(string)))
2013 //
2014 // which is generated for the self-hosted `String.p.{substring,slice,substr}`
2015 // functions when called with constants `start` and `end` parameters.
2016
2017 if (!IsConstantZeroInt32(substr->begin())) {
2018 return false;
2019 }
2020
2021 // Unnecessary bit-ops haven't yet been removed.
2022 auto* length = RemoveUnnecessaryBitOps(substr->length());
2023 if (!length->isMinMax() || length->toMinMax()->isMax()) {
2024 return false;
2025 }
2026
2027 auto match = MatchOperands<MConstant, MStringLength>(length->toMinMax());
2028 if (!match) {
2029 return false;
2030 }
2031
2032 // Ensure |len| matches the substring's length.
2033 auto [cst, strLength] = *match;
2034 return cst->isInt32(len) && strLength->string() == substr->string();
2035}
2036
2037static bool IsSubstrLast(MSubstr* substr, int32_t start) {
2038 MOZ_ASSERT(start < 0, "start from end is negative")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(start < 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(start < 0))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("start < 0" " ("
"start from end is negative" ")", "./../../../../js/src/jit/MIR.cpp"
, 2038); AnnotateMozCrashReason("MOZ_ASSERT" "(" "start < 0"
") (" "start from end is negative" ")"); do { MOZ_CrashSequence
(__null, 2038); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2039
2040 // We want to match either this pattern:
2041 //
2042 // begin = Max(StringLength(string) + start, 0)
2043 // length = Max(StringLength(string) - begin, 0)
2044 // Substr(string, begin, length)
2045 //
2046 // or this pattern:
2047 //
2048 // begin = Max(StringLength(string) + start, 0)
2049 // length = Min(StringLength(string), StringLength(string) - begin)
2050 // Substr(string, begin, length)
2051 //
2052 // which is generated for the self-hosted `String.p.{slice,substr}`
2053 // functions when called with parameters `start < 0` and `end = undefined`.
2054
2055 auto* string = substr->string();
2056
2057 // Unnecessary bit-ops haven't yet been removed.
2058 auto* begin = RemoveUnnecessaryBitOps(substr->begin());
2059 auto* length = RemoveUnnecessaryBitOps(substr->length());
2060
2061 // Matches: Max(StringLength(string) + start, 0)
2062 auto matchesBegin = [&]() {
2063 if (!begin->isMinMax() || !begin->toMinMax()->isMax()) {
2064 return false;
2065 }
2066
2067 auto maxOperands = MatchOperands<MAdd, MConstant>(begin->toMinMax());
2068 if (!maxOperands) {
2069 return false;
2070 }
2071
2072 auto [add, cst] = *maxOperands;
2073 if (!cst->isInt32(0)) {
2074 return false;
2075 }
2076
2077 auto addOperands = MatchOperands<MStringLength, MConstant>(add);
2078 if (!addOperands) {
2079 return false;
2080 }
2081
2082 auto [strLength, cstAdd] = *addOperands;
2083 return strLength->string() == string && cstAdd->isInt32(start);
2084 };
2085
2086 // Matches: Max(StringLength(string) - begin, 0)
2087 auto matchesSliceLength = [&]() {
2088 if (!length->isMinMax() || !length->toMinMax()->isMax()) {
2089 return false;
2090 }
2091
2092 auto maxOperands = MatchOperands<MSub, MConstant>(length->toMinMax());
2093 if (!maxOperands) {
2094 return false;
2095 }
2096
2097 auto [sub, cst] = *maxOperands;
2098 if (!cst->isInt32(0)) {
2099 return false;
2100 }
2101
2102 auto subOperands = MatchOperands<MStringLength, MMinMax>(sub);
2103 if (!subOperands) {
2104 return false;
2105 }
2106
2107 auto [strLength, minmax] = *subOperands;
2108 return strLength->string() == string && minmax == begin;
2109 };
2110
2111 // Matches: Min(StringLength(string), StringLength(string) - begin)
2112 auto matchesSubstrLength = [&]() {
2113 if (!length->isMinMax() || length->toMinMax()->isMax()) {
2114 return false;
2115 }
2116
2117 auto minOperands = MatchOperands<MStringLength, MSub>(length->toMinMax());
2118 if (!minOperands) {
2119 return false;
2120 }
2121
2122 auto [strLength1, sub] = *minOperands;
2123 if (strLength1->string() != string) {
2124 return false;
2125 }
2126
2127 auto subOperands = MatchOperands<MStringLength, MMinMax>(sub);
2128 if (!subOperands) {
2129 return false;
2130 }
2131
2132 auto [strLength2, minmax] = *subOperands;
2133 return strLength2->string() == string && minmax == begin;
2134 };
2135
2136 return matchesBegin() && (matchesSliceLength() || matchesSubstrLength());
2137}
2138
2139MDefinition* MSubstr::foldsTo(TempAllocator& alloc) {
2140 // Fold |str.substring(0, 1)| to |str.charAt(0)|.
2141 if (IsSubstrTo(this, 1)) {
2142 MOZ_ASSERT(IsConstantZeroInt32(begin()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsConstantZeroInt32(begin()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsConstantZeroInt32(begin())
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"IsConstantZeroInt32(begin())", "./../../../../js/src/jit/MIR.cpp"
, 2142); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsConstantZeroInt32(begin())"
")"); do { MOZ_CrashSequence(__null, 2142); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2143
2144 auto* charCode = MCharCodeAtOrNegative::New(alloc, string(), begin());
2145 block()->insertBefore(this, charCode);
2146
2147 return MFromCharCodeEmptyIfNegative::New(alloc, charCode);
2148 }
2149
2150 // Fold |str.slice(-1)| and |str.substr(-1)| to |str.charAt(str.length + -1)|.
2151 if (IsSubstrLast(this, -1)) {
2152 auto* length = MStringLength::New(alloc, string());
2153 block()->insertBefore(this, length);
2154
2155 auto* index = MConstant::NewInt32(alloc, -1);
2156 block()->insertBefore(this, index);
2157
2158 // Folded MToRelativeStringIndex, see MToRelativeStringIndex::foldsTo.
2159 //
2160 // Safe to truncate because |length| is never negative.
2161 auto* add = MAdd::New(alloc, index, length, TruncateKind::Truncate);
2162 block()->insertBefore(this, add);
2163
2164 auto* charCode = MCharCodeAtOrNegative::New(alloc, string(), add);
2165 block()->insertBefore(this, charCode);
2166
2167 return MFromCharCodeEmptyIfNegative::New(alloc, charCode);
2168 }
2169
2170 return this;
2171}
2172
2173MDefinition* MCharCodeAt::foldsTo(TempAllocator& alloc) {
2174 MDefinition* string = this->string();
2175 if (!string->isConstant() && !string->isFromCharCode()) {
2176 return this;
2177 }
2178
2179 MDefinition* index = this->index();
2180 if (index->isSpectreMaskIndex()) {
2181 index = index->toSpectreMaskIndex()->index();
2182 }
2183 if (!index->isConstant()) {
2184 return this;
2185 }
2186 int32_t idx = index->toConstant()->toInt32();
2187
2188 // Handle the pattern |s[idx].charCodeAt(0)|.
2189 if (string->isFromCharCode()) {
2190 if (idx != 0) {
2191 return this;
2192 }
2193
2194 // Simplify |CharCodeAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just
2195 // |CharCodeAt(s, idx)|.
2196 auto* charCode = string->toFromCharCode()->code();
2197 if (!charCode->isCharCodeAt()) {
2198 return this;
2199 }
2200
2201 return charCode;
2202 }
2203
2204 JSOffThreadAtom* str = string->toConstant()->toString();
2205 if (idx < 0 || uint32_t(idx) >= str->length()) {
2206 return this;
2207 }
2208
2209 char16_t ch = str->latin1OrTwoByteChar(idx);
2210 return MConstant::NewInt32(alloc, ch);
2211}
2212
2213MDefinition* MCodePointAt::foldsTo(TempAllocator& alloc) {
2214 MDefinition* string = this->string();
2215 if (!string->isConstant() && !string->isFromCharCode()) {
2216 return this;
2217 }
2218
2219 MDefinition* index = this->index();
2220 if (index->isSpectreMaskIndex()) {
2221 index = index->toSpectreMaskIndex()->index();
2222 }
2223 if (!index->isConstant()) {
2224 return this;
2225 }
2226 int32_t idx = index->toConstant()->toInt32();
2227
2228 // Handle the pattern |s[idx].codePointAt(0)|.
2229 if (string->isFromCharCode()) {
2230 if (idx != 0) {
2231 return this;
2232 }
2233
2234 // Simplify |CodePointAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just
2235 // |CharCodeAt(s, idx)|.
2236 auto* charCode = string->toFromCharCode()->code();
2237 if (!charCode->isCharCodeAt()) {
2238 return this;
2239 }
2240
2241 return charCode;
2242 }
2243
2244 JSOffThreadAtom* str = string->toConstant()->toString();
2245 if (idx < 0 || uint32_t(idx) >= str->length()) {
2246 return this;
2247 }
2248
2249 char32_t first = str->latin1OrTwoByteChar(idx);
2250 if (unicode::IsLeadSurrogate(first) && uint32_t(idx) + 1 < str->length()) {
2251 char32_t second = str->latin1OrTwoByteChar(idx + 1);
2252 if (unicode::IsTrailSurrogate(second)) {
2253 first = unicode::UTF16Decode(first, second);
2254 }
2255 }
2256 return MConstant::NewInt32(alloc, first);
2257}
2258
2259MDefinition* MLinearizeString::foldsTo(TempAllocator& alloc) {
2260 MDefinition* string = this->string();
2261 if (!string->isConstant()) {
2262 return this;
2263 }
2264
2265 // Constant strings are atoms, which are guaranteed to be linear.
2266 static_assert(std::is_same_v<decltype(string->toConstant()->toString()),
2267 JSOffThreadAtom*>);
2268 return string;
2269}
2270
2271MDefinition* MToRelativeStringIndex::foldsTo(TempAllocator& alloc) {
2272 MDefinition* index = this->index();
2273 MDefinition* length = this->length();
2274
2275 if (!index->isConstant()) {
2276 return this;
2277 }
2278 if (!length->isStringLength() && !length->isConstant()) {
2279 return this;
2280 }
2281 MOZ_ASSERT_IF(length->isConstant(), length->toConstant()->toInt32() >= 0)do { if (length->isConstant()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(length->toConstant
()->toInt32() >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(length->toConstant()->
toInt32() >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("length->toConstant()->toInt32() >= 0", "./../../../../js/src/jit/MIR.cpp"
, 2281); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length->toConstant()->toInt32() >= 0"
")"); do { MOZ_CrashSequence(__null, 2281); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
2282
2283 int32_t relativeIndex = index->toConstant()->toInt32();
2284 if (relativeIndex >= 0) {
2285 return index;
2286 }
2287
2288 // Safe to truncate because |length| is never negative.
2289 return MAdd::New(alloc, index, length, TruncateKind::Truncate);
2290}
2291
2292template <size_t Arity>
2293[[nodiscard]] static bool EnsureFloatInputOrConvert(
2294 MAryInstruction<Arity>* owner, TempAllocator& alloc) {
2295 MOZ_ASSERT(!IsFloatingPointType(owner->type()),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsFloatingPointType(owner->type()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!IsFloatingPointType(owner->type())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!IsFloatingPointType(owner->type())"
" (" "Floating point types must check consumers" ")", "./../../../../js/src/jit/MIR.cpp"
, 2296); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsFloatingPointType(owner->type())"
") (" "Floating point types must check consumers" ")"); do {
MOZ_CrashSequence(__null, 2296); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
2296 "Floating point types must check consumers")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsFloatingPointType(owner->type()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!IsFloatingPointType(owner->type())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!IsFloatingPointType(owner->type())"
" (" "Floating point types must check consumers" ")", "./../../../../js/src/jit/MIR.cpp"
, 2296); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsFloatingPointType(owner->type())"
") (" "Floating point types must check consumers" ")"); do {
MOZ_CrashSequence(__null, 2296); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
2297
2298 if (AllOperandsCanProduceFloat32(owner)) {
2299 return true;
2300 }
2301 ConvertOperandsToDouble(owner, alloc);
2302 return false;
2303}
2304
2305template <size_t Arity>
2306[[nodiscard]] static bool EnsureFloatConsumersAndInputOrConvert(
2307 MAryInstruction<Arity>* owner, TempAllocator& alloc) {
2308 MOZ_ASSERT(IsFloatingPointType(owner->type()),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsFloatingPointType(owner->type()))>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(IsFloatingPointType(owner->type())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("IsFloatingPointType(owner->type())"
" (" "Integer types don't need to check consumers" ")", "./../../../../js/src/jit/MIR.cpp"
, 2309); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsFloatingPointType(owner->type())"
") (" "Integer types don't need to check consumers" ")"); do
{ MOZ_CrashSequence(__null, 2309); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
2309 "Integer types don't need to check consumers")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsFloatingPointType(owner->type()))>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(IsFloatingPointType(owner->type())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("IsFloatingPointType(owner->type())"
" (" "Integer types don't need to check consumers" ")", "./../../../../js/src/jit/MIR.cpp"
, 2309); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsFloatingPointType(owner->type())"
") (" "Integer types don't need to check consumers" ")"); do
{ MOZ_CrashSequence(__null, 2309); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
2310
2311 if (AllOperandsCanProduceFloat32(owner) &&
2312 CheckUsesAreFloat32Consumers(owner)) {
2313 return true;
2314 }
2315 ConvertOperandsToDouble(owner, alloc);
2316 return false;
2317}
2318
2319void MFloor::trySpecializeFloat32(TempAllocator& alloc) {
2320 MOZ_ASSERT(type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type() == MIRType::Int32))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("type() == MIRType::Int32"
, "./../../../../js/src/jit/MIR.cpp", 2320); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Int32" ")"); do { MOZ_CrashSequence
(__null, 2320); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2321 if (EnsureFloatInputOrConvert(this, alloc)) {
2322 specialization_ = MIRType::Float32;
2323 }
2324}
2325
2326void MCeil::trySpecializeFloat32(TempAllocator& alloc) {
2327 MOZ_ASSERT(type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type() == MIRType::Int32))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("type() == MIRType::Int32"
, "./../../../../js/src/jit/MIR.cpp", 2327); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Int32" ")"); do { MOZ_CrashSequence
(__null, 2327); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2328 if (EnsureFloatInputOrConvert(this, alloc)) {
2329 specialization_ = MIRType::Float32;
2330 }
2331}
2332
2333void MRound::trySpecializeFloat32(TempAllocator& alloc) {
2334 MOZ_ASSERT(type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type() == MIRType::Int32))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("type() == MIRType::Int32"
, "./../../../../js/src/jit/MIR.cpp", 2334); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Int32" ")"); do { MOZ_CrashSequence
(__null, 2334); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2335 if (EnsureFloatInputOrConvert(this, alloc)) {
2336 specialization_ = MIRType::Float32;
2337 }
2338}
2339
2340void MTrunc::trySpecializeFloat32(TempAllocator& alloc) {
2341 MOZ_ASSERT(type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type() == MIRType::Int32))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("type() == MIRType::Int32"
, "./../../../../js/src/jit/MIR.cpp", 2341); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Int32" ")"); do { MOZ_CrashSequence
(__null, 2341); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2342 if (EnsureFloatInputOrConvert(this, alloc)) {
2343 specialization_ = MIRType::Float32;
2344 }
2345}
2346
2347void MNearbyInt::trySpecializeFloat32(TempAllocator& alloc) {
2348 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
2349 specialization_ = MIRType::Float32;
2350 setResultType(MIRType::Float32);
2351 }
2352}
2353
2354void MRoundToDouble::trySpecializeFloat32(TempAllocator& alloc) {
2355 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
2356 specialization_ = MIRType::Float32;
2357 setResultType(MIRType::Float32);
2358 }
2359}
2360
2361MGoto* MGoto::New(TempAllocator& alloc, MBasicBlock* target) {
2362 return new (alloc) MGoto(target);
2363}
2364
2365MGoto* MGoto::New(TempAllocator::Fallible alloc, MBasicBlock* target) {
2366 MOZ_ASSERT(target)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(target)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(target))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("target", "./../../../../js/src/jit/MIR.cpp"
, 2366); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { MOZ_CrashSequence(__null, 2366); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2367 return new (alloc) MGoto(target);
2368}
2369
2370MGoto* MGoto::New(TempAllocator& alloc) { return new (alloc) MGoto(nullptr); }
2371
2372MDefinition* MBox::foldsTo(TempAllocator& alloc) {
2373 if (input()->isUnbox()) {
2374 return input()->toUnbox()->input();
2375 }
2376 return this;
2377}
2378
2379#ifdef JS_JITSPEW1
2380void MUnbox::printOpcode(GenericPrinter& out) const {
2381 PrintOpcodeName(out, op());
2382 out.printf(" ");
2383 getOperand(0)->printName(out);
2384 out.printf(" ");
2385
2386 switch (type()) {
2387 case MIRType::Int32:
2388 out.printf("to Int32");
2389 break;
2390 case MIRType::Double:
2391 out.printf("to Double");
2392 break;
2393 case MIRType::Boolean:
2394 out.printf("to Boolean");
2395 break;
2396 case MIRType::String:
2397 out.printf("to String");
2398 break;
2399 case MIRType::Symbol:
2400 out.printf("to Symbol");
2401 break;
2402 case MIRType::BigInt:
2403 out.printf("to BigInt");
2404 break;
2405 case MIRType::Object:
2406 out.printf("to Object");
2407 break;
2408 default:
2409 break;
2410 }
2411
2412 switch (mode()) {
2413 case Fallible:
2414 out.printf(" (fallible)");
2415 break;
2416 case Infallible:
2417 out.printf(" (infallible)");
2418 break;
2419 default:
2420 break;
2421 }
2422}
2423#endif
2424
2425MDefinition* MUnbox::foldsTo(TempAllocator& alloc) {
2426 if (input()->isBox()) {
2427 MDefinition* unboxed = input()->toBox()->input();
2428
2429 // Fold MUnbox(MBox(x)) => x if types match.
2430 if (unboxed->type() == type()) {
2431 if (fallible()) {
2432 unboxed->setImplicitlyUsedUnchecked();
2433 }
2434 return unboxed;
2435 }
2436
2437 // Fold MUnbox(MBox(x)) => MToDouble(x) if possible.
2438 if (type() == MIRType::Double &&
2439 IsTypeRepresentableAsDouble(unboxed->type())) {
2440 if (unboxed->isConstant()) {
2441 return MConstant::NewDouble(alloc,
2442 unboxed->toConstant()->numberToDouble());
2443 }
2444
2445 return MToDouble::New(alloc, unboxed);
2446 }
2447
2448 // MUnbox<Int32>(MBox<Double>(x)) will always fail, even if x can be
2449 // represented as an Int32. Fold to avoid unnecessary bailouts.
2450 if (type() == MIRType::Int32 && unboxed->type() == MIRType::Double) {
2451 auto* folded = MToNumberInt32::New(alloc, unboxed,
2452 IntConversionInputKind::NumbersOnly);
2453 folded->setGuard();
2454 return folded;
2455 }
2456 }
2457
2458 return this;
2459}
2460
2461#ifdef DEBUG1
2462void MPhi::assertLoopPhi() const {
2463 // getLoopPredecessorOperand and getLoopBackedgeOperand rely on these
2464 // predecessors being at known indices.
2465 if (block()->numPredecessors() == 2) {
2466 MBasicBlock* pred = block()->getPredecessor(0);
2467 MBasicBlock* back = block()->getPredecessor(1);
2468 MOZ_ASSERT(pred == block()->loopPredecessor())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pred == block()->loopPredecessor())>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(pred == block()->loopPredecessor()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("pred == block()->loopPredecessor()"
, "./../../../../js/src/jit/MIR.cpp", 2468); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "pred == block()->loopPredecessor()" ")"
); do { MOZ_CrashSequence(__null, 2468); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2469 MOZ_ASSERT(pred->successorWithPhis() == block())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pred->successorWithPhis() == block())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(pred->successorWithPhis() == block()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("pred->successorWithPhis() == block()"
, "./../../../../js/src/jit/MIR.cpp", 2469); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "pred->successorWithPhis() == block()" ")"
); do { MOZ_CrashSequence(__null, 2469); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2470 MOZ_ASSERT(pred->positionInPhiSuccessor() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pred->positionInPhiSuccessor() == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(pred->positionInPhiSuccessor() == 0))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("pred->positionInPhiSuccessor() == 0"
, "./../../../../js/src/jit/MIR.cpp", 2470); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "pred->positionInPhiSuccessor() == 0" ")"
); do { MOZ_CrashSequence(__null, 2470); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2471 MOZ_ASSERT(back == block()->backedge())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(back == block()->backedge())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(back == block()->backedge
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("back == block()->backedge()", "./../../../../js/src/jit/MIR.cpp"
, 2471); AnnotateMozCrashReason("MOZ_ASSERT" "(" "back == block()->backedge()"
")"); do { MOZ_CrashSequence(__null, 2471); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2472 MOZ_ASSERT(back->successorWithPhis() == block())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(back->successorWithPhis() == block())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(back->successorWithPhis() == block()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("back->successorWithPhis() == block()"
, "./../../../../js/src/jit/MIR.cpp", 2472); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "back->successorWithPhis() == block()" ")"
); do { MOZ_CrashSequence(__null, 2472); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2473 MOZ_ASSERT(back->positionInPhiSuccessor() == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(back->positionInPhiSuccessor() == 1)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(back->positionInPhiSuccessor() == 1))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("back->positionInPhiSuccessor() == 1"
, "./../../../../js/src/jit/MIR.cpp", 2473); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "back->positionInPhiSuccessor() == 1" ")"
); do { MOZ_CrashSequence(__null, 2473); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2474 } else {
2475 // After we remove fake loop predecessors for loop headers that
2476 // are only reachable via OSR, the only predecessor is the
2477 // loop backedge.
2478 MOZ_ASSERT(block()->numPredecessors() == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(block()->numPredecessors() == 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(block()->numPredecessors(
) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("block()->numPredecessors() == 1", "./../../../../js/src/jit/MIR.cpp"
, 2478); AnnotateMozCrashReason("MOZ_ASSERT" "(" "block()->numPredecessors() == 1"
")"); do { MOZ_CrashSequence(__null, 2478); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2479 MOZ_ASSERT(block()->graph().osrBlock())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(block()->graph().osrBlock())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(block()->graph().osrBlock
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("block()->graph().osrBlock()", "./../../../../js/src/jit/MIR.cpp"
, 2479); AnnotateMozCrashReason("MOZ_ASSERT" "(" "block()->graph().osrBlock()"
")"); do { MOZ_CrashSequence(__null, 2479); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2480 MOZ_ASSERT(!block()->graph().canBuildDominators())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!block()->graph().canBuildDominators())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!block()->graph().canBuildDominators()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!block()->graph().canBuildDominators()"
, "./../../../../js/src/jit/MIR.cpp", 2480); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!block()->graph().canBuildDominators()"
")"); do { MOZ_CrashSequence(__null, 2480); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2481 MBasicBlock* back = block()->getPredecessor(0);
2482 MOZ_ASSERT(back == block()->backedge())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(back == block()->backedge())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(back == block()->backedge
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("back == block()->backedge()", "./../../../../js/src/jit/MIR.cpp"
, 2482); AnnotateMozCrashReason("MOZ_ASSERT" "(" "back == block()->backedge()"
")"); do { MOZ_CrashSequence(__null, 2482); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2483 MOZ_ASSERT(back->successorWithPhis() == block())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(back->successorWithPhis() == block())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(back->successorWithPhis() == block()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("back->successorWithPhis() == block()"
, "./../../../../js/src/jit/MIR.cpp", 2483); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "back->successorWithPhis() == block()" ")"
); do { MOZ_CrashSequence(__null, 2483); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2484 MOZ_ASSERT(back->positionInPhiSuccessor() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(back->positionInPhiSuccessor() == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(back->positionInPhiSuccessor() == 0))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("back->positionInPhiSuccessor() == 0"
, "./../../../../js/src/jit/MIR.cpp", 2484); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "back->positionInPhiSuccessor() == 0" ")"
); do { MOZ_CrashSequence(__null, 2484); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2485 }
2486}
2487#endif
2488
2489MDefinition* MPhi::getLoopPredecessorOperand() const {
2490 // This should not be called after removing fake loop predecessors.
2491 MOZ_ASSERT(block()->numPredecessors() == 2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(block()->numPredecessors() == 2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(block()->numPredecessors(
) == 2))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("block()->numPredecessors() == 2", "./../../../../js/src/jit/MIR.cpp"
, 2491); AnnotateMozCrashReason("MOZ_ASSERT" "(" "block()->numPredecessors() == 2"
")"); do { MOZ_CrashSequence(__null, 2491); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2492 assertLoopPhi();
2493 return getOperand(0);
2494}
2495
2496MDefinition* MPhi::getLoopBackedgeOperand() const {
2497 assertLoopPhi();
2498 uint32_t idx = block()->numPredecessors() == 2 ? 1 : 0;
2499 return getOperand(idx);
2500}
2501
2502void MPhi::removeOperand(size_t index) {
2503 MOZ_ASSERT(index < numOperands())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(index < numOperands())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(index < numOperands()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("index < numOperands()"
, "./../../../../js/src/jit/MIR.cpp", 2503); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "index < numOperands()" ")"); do { MOZ_CrashSequence
(__null, 2503); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2504 MOZ_ASSERT(getUseFor(index)->index() == index)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(getUseFor(index)->index() == index)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(getUseFor(index)->index() == index))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("getUseFor(index)->index() == index"
, "./../../../../js/src/jit/MIR.cpp", 2504); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "getUseFor(index)->index() == index" ")"
); do { MOZ_CrashSequence(__null, 2504); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2505 MOZ_ASSERT(getUseFor(index)->consumer() == this)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(getUseFor(index)->consumer() == this)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(getUseFor(index)->consumer() == this))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("getUseFor(index)->consumer() == this"
, "./../../../../js/src/jit/MIR.cpp", 2505); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "getUseFor(index)->consumer() == this" ")"
); do { MOZ_CrashSequence(__null, 2505); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2506
2507 // If we have phi(..., a, b, c, d, ..., z) and we plan
2508 // on removing a, then first shift downward so that we have
2509 // phi(..., b, c, d, ..., z, z):
2510 MUse* p = inputs_.begin() + index;
2511 MUse* e = inputs_.end();
2512 p->producer()->removeUse(p);
2513 for (; p < e - 1; ++p) {
2514 MDefinition* producer = (p + 1)->producer();
2515 p->setProducerUnchecked(producer);
2516 producer->replaceUse(p + 1, p);
2517 }
2518
2519 // truncate the inputs_ list:
2520 inputs_.popBack();
2521}
2522
2523void MPhi::removeAllOperands() {
2524 for (MUse& p : inputs_) {
2525 p.producer()->removeUse(&p);
2526 }
2527 inputs_.clear();
2528}
2529
2530MDefinition* MPhi::foldsTernary(TempAllocator& alloc) {
2531 /* Look if this MPhi is a ternary construct.
2532 * This is a very loose term as it actually only checks for
2533 *
2534 * MTest X
2535 * / \
2536 * ... ...
2537 * \ /
2538 * MPhi X Y
2539 *
2540 * Which we will simply call:
2541 * x ? x : y or x ? y : x
2542 */
2543
2544 if (numOperands() != 2) {
2545 return nullptr;
2546 }
2547
2548 MOZ_ASSERT(block()->numPredecessors() == 2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(block()->numPredecessors() == 2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(block()->numPredecessors(
) == 2))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("block()->numPredecessors() == 2", "./../../../../js/src/jit/MIR.cpp"
, 2548); AnnotateMozCrashReason("MOZ_ASSERT" "(" "block()->numPredecessors() == 2"
")"); do { MOZ_CrashSequence(__null, 2548); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2549
2550 MBasicBlock* pred = block()->immediateDominator();
2551 if (!pred || !pred->lastIns()->isTest()) {
2552 return nullptr;
2553 }
2554
2555 MTest* test = pred->lastIns()->toTest();
2556
2557 // True branch may only dominate one edge of MPhi.
2558 if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
2559 test->ifTrue()->dominates(block()->getPredecessor(1))) {
2560 return nullptr;
2561 }
2562
2563 // False branch may only dominate one edge of MPhi.
2564 if (test->ifFalse()->dominates(block()->getPredecessor(0)) ==
2565 test->ifFalse()->dominates(block()->getPredecessor(1))) {
2566 return nullptr;
2567 }
2568
2569 // True and false branch must dominate different edges of MPhi.
2570 if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
2571 test->ifFalse()->dominates(block()->getPredecessor(0))) {
2572 return nullptr;
2573 }
2574
2575 // We found a ternary construct.
2576 bool firstIsTrueBranch =
2577 test->ifTrue()->dominates(block()->getPredecessor(0));
2578 MDefinition* trueDef = firstIsTrueBranch ? getOperand(0) : getOperand(1);
2579 MDefinition* falseDef = firstIsTrueBranch ? getOperand(1) : getOperand(0);
2580
2581 // Accept either
2582 // testArg ? testArg : constant or
2583 // testArg ? constant : testArg
2584 if (!trueDef->isConstant() && !falseDef->isConstant()) {
2585 return nullptr;
2586 }
2587
2588 MConstant* c =
2589 trueDef->isConstant() ? trueDef->toConstant() : falseDef->toConstant();
2590 MDefinition* testArg = (trueDef == c) ? falseDef : trueDef;
2591 if (testArg != test->input()) {
2592 return nullptr;
2593 }
2594
2595 // This check should be a tautology, except that the constant might be the
2596 // result of the removal of a branch. In such case the domination scope of
2597 // the block which is holding the constant might be incomplete. This
2598 // condition is used to prevent doing this optimization based on incomplete
2599 // information.
2600 //
2601 // As GVN removed a branch, it will update the dominations rules before
2602 // trying to fold this MPhi again. Thus, this condition does not inhibit
2603 // this optimization.
2604 MBasicBlock* truePred = block()->getPredecessor(firstIsTrueBranch ? 0 : 1);
2605 MBasicBlock* falsePred = block()->getPredecessor(firstIsTrueBranch ? 1 : 0);
2606 if (!trueDef->block()->dominates(truePred) ||
2607 !falseDef->block()->dominates(falsePred)) {
2608 return nullptr;
2609 }
2610
2611 // If testArg is an int32 type we can:
2612 // - fold testArg ? testArg : 0 to testArg
2613 // - fold testArg ? 0 : testArg to 0
2614 if (testArg->type() == MIRType::Int32 && c->numberToDouble() == 0) {
2615 testArg->setGuardRangeBailoutsUnchecked();
2616
2617 // When folding to the constant we need to hoist it.
2618 if (trueDef == c && !c->block()->dominates(block())) {
2619 c->block()->moveBefore(pred->lastIns(), c);
2620 }
2621 return trueDef;
2622 }
2623
2624 // If testArg is an double type we can:
2625 // - fold testArg ? testArg : 0.0 to MNaNToZero(testArg)
2626 if (testArg->type() == MIRType::Double &&
2627 mozilla::IsPositiveZero(c->numberToDouble()) && c != trueDef) {
2628 MNaNToZero* replace = MNaNToZero::New(alloc, testArg);
2629 test->block()->insertBefore(test, replace);
2630 return replace;
2631 }
2632
2633 // If testArg is a string type we can:
2634 // - fold testArg ? testArg : "" to testArg
2635 // - fold testArg ? "" : testArg to ""
2636 if (testArg->type() == MIRType::String && c->toString()->empty()) {
2637 // When folding to the constant we need to hoist it.
2638 if (trueDef == c && !c->block()->dominates(block())) {
2639 c->block()->moveBefore(pred->lastIns(), c);
2640 }
2641 return trueDef;
2642 }
2643
2644 return nullptr;
2645}
2646
2647MDefinition* MPhi::operandIfRedundant() {
2648 if (inputs_.length() == 0) {
2649 return nullptr;
2650 }
2651
2652 // If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)),
2653 // returns the operand that it will always be equal to (a, in
2654 // those two cases).
2655 MDefinition* first = getOperand(0);
2656 for (size_t i = 1, e = numOperands(); i < e; i++) {
2657 MDefinition* op = getOperand(i);
2658 if (op != first && op != this) {
2659 return nullptr;
2660 }
2661 }
2662 return first;
2663}
2664
2665MDefinition* MPhi::foldsTo(TempAllocator& alloc) {
2666 if (MDefinition* def = operandIfRedundant()) {
2667 return def;
2668 }
2669
2670 if (MDefinition* def = foldsTernary(alloc)) {
2671 return def;
2672 }
2673
2674 return this;
2675}
2676
2677bool MPhi::congruentTo(const MDefinition* ins) const {
2678 if (!ins->isPhi()) {
2679 return false;
2680 }
2681
2682 // Phis in different blocks may have different control conditions.
2683 // For example, these phis:
2684 //
2685 // if (p)
2686 // goto a
2687 // a:
2688 // t = phi(x, y)
2689 //
2690 // if (q)
2691 // goto b
2692 // b:
2693 // s = phi(x, y)
2694 //
2695 // have identical operands, but they are not equvalent because t is
2696 // effectively p?x:y and s is effectively q?x:y.
2697 //
2698 // For now, consider phis in different blocks incongruent.
2699 if (ins->block() != block()) {
2700 return false;
2701 }
2702
2703 return congruentIfOperandsEqual(ins);
2704}
2705
2706void MPhi::updateForReplacement(MPhi* other) {
2707 // This function is called to fix the current Phi flags using it as a
2708 // replacement of the other Phi instruction |other|.
2709 //
2710 // When dealing with usage analysis, any Use will replace all other values,
2711 // such as Unused and Unknown. Unless both are Unused, the merge would be
2712 // Unknown.
2713 if (usageAnalysis_ == PhiUsage::Used ||
2714 other->usageAnalysis_ == PhiUsage::Used) {
2715 usageAnalysis_ = PhiUsage::Used;
2716 } else if (usageAnalysis_ != other->usageAnalysis_) {
2717 // this == unused && other == unknown
2718 // or this == unknown && other == unused
2719 usageAnalysis_ = PhiUsage::Unknown;
2720 } else {
2721 // this == unused && other == unused
2722 // or this == unknown && other = unknown
2723 MOZ_ASSERT(usageAnalysis_ == PhiUsage::Unused ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(usageAnalysis_ == PhiUsage::Unused || usageAnalysis_
== PhiUsage::Unknown)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(usageAnalysis_ == PhiUsage::
Unused || usageAnalysis_ == PhiUsage::Unknown))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("usageAnalysis_ == PhiUsage::Unused || usageAnalysis_ == PhiUsage::Unknown"
, "./../../../../js/src/jit/MIR.cpp", 2724); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "usageAnalysis_ == PhiUsage::Unused || usageAnalysis_ == PhiUsage::Unknown"
")"); do { MOZ_CrashSequence(__null, 2724); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2724 usageAnalysis_ == PhiUsage::Unknown)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(usageAnalysis_ == PhiUsage::Unused || usageAnalysis_
== PhiUsage::Unknown)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(usageAnalysis_ == PhiUsage::
Unused || usageAnalysis_ == PhiUsage::Unknown))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("usageAnalysis_ == PhiUsage::Unused || usageAnalysis_ == PhiUsage::Unknown"
, "./../../../../js/src/jit/MIR.cpp", 2724); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "usageAnalysis_ == PhiUsage::Unused || usageAnalysis_ == PhiUsage::Unknown"
")"); do { MOZ_CrashSequence(__null, 2724); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2725 MOZ_ASSERT(usageAnalysis_ == other->usageAnalysis_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(usageAnalysis_ == other->usageAnalysis_)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(usageAnalysis_ == other->usageAnalysis_))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("usageAnalysis_ == other->usageAnalysis_"
, "./../../../../js/src/jit/MIR.cpp", 2725); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "usageAnalysis_ == other->usageAnalysis_"
")"); do { MOZ_CrashSequence(__null, 2725); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2726 }
2727}
2728
2729/* static */
2730bool MPhi::markIteratorPhis(const PhiVector& iterators) {
2731 // Find and mark phis that must transitively hold an iterator live.
2732
2733 Vector<MPhi*, 8, SystemAllocPolicy> worklist;
2734
2735 for (MPhi* iter : iterators) {
2736 if (!iter->isInWorklist()) {
2737 if (!worklist.append(iter)) {
2738 return false;
2739 }
2740 iter->setInWorklist();
2741 }
2742 }
2743
2744 while (!worklist.empty()) {
2745 MPhi* phi = worklist.popCopy();
2746 phi->setNotInWorklist();
2747
2748 phi->setIterator();
2749 phi->setImplicitlyUsedUnchecked();
2750
2751 for (MUseDefIterator iter(phi); iter; iter++) {
2752 MDefinition* use = iter.def();
2753 if (!use->isInWorklist() && use->isPhi() && !use->toPhi()->isIterator()) {
2754 if (!worklist.append(use->toPhi())) {
2755 return false;
2756 }
2757 use->setInWorklist();
2758 }
2759 }
2760 }
2761
2762 return true;
2763}
2764
2765void MCallBase::addArg(size_t argnum, MDefinition* arg) {
2766 // The operand vector is initialized in reverse order by WarpBuilder.
2767 // It cannot be checked for consistency until all arguments are added.
2768 // FixedList doesn't initialize its elements, so do an unchecked init.
2769 initOperand(argnum + NumNonArgumentOperands, arg);
2770}
2771
2772static inline bool IsConstant(MDefinition* def, double v) {
2773 if (!def->isConstant()) {
2774 return false;
2775 }
2776
2777 return NumbersAreIdentical(def->toConstant()->numberToDouble(), v);
2778}
2779
2780static inline bool IsConstantInt64(MDefinition* def, int64_t v) {
2781 if (!def->isConstant()) {
2782 return false;
2783 }
2784
2785 return def->toConstant()->toInt64() == v;
2786}
2787
2788static inline bool IsConstantIntPtr(MDefinition* def, intptr_t v) {
2789 if (!def->isConstant()) {
2790 return false;
2791 }
2792
2793 return def->toConstant()->toIntPtr() == v;
2794}
2795
2796MDefinition* MBinaryBitwiseInstruction::foldsTo(TempAllocator& alloc) {
2797 // Identity operations are removed (for int32 only) in foldUnnecessaryBitop.
2798
2799 if (type() == MIRType::Int32) {
2800 if (MDefinition* folded = EvaluateInt32ConstantOperands(alloc, this)) {
2801 return folded;
2802 }
2803 } else if (type() == MIRType::Int64) {
2804 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) {
2805 return folded;
2806 }
2807 } else if (type() == MIRType::IntPtr) {
2808 if (MDefinition* folded = EvaluateIntPtrConstantOperands(alloc, this)) {
2809 return folded;
2810 }
2811 }
2812
2813 return this;
2814}
2815
2816MDefinition* MBitOr::foldsTo(TempAllocator& alloc) {
2817 MDefinition* folded = MBinaryBitwiseInstruction::foldsTo(alloc);
2818 if (folded != this || type() != MIRType::Int32) {
2819 return folded;
2820 }
2821
2822 MDefinition* lhs = getOperand(0);
2823 MDefinition* rhs = getOperand(1);
2824
2825 // Convert the pattern (x >>> C) | (x << (32 - D)) into an MRotate,
2826 // where D = 32-C.
2827 auto isRotateRight = [&](MDefinition* ursh,
2828 MDefinition* lsh) -> MDefinition* {
2829 if (!ursh->isUrsh() || !lsh->isLsh()) {
2830 return nullptr;
2831 }
2832 MDefinition* x = ursh->getOperand(0);
2833 if (x != lsh->getOperand(0) || x->type() != MIRType::Int32) {
2834 return nullptr;
2835 }
2836 MDefinition* urshConst = ursh->getOperand(1);
2837 MDefinition* lshConst = lsh->getOperand(1);
2838 if (!urshConst->isConstant() || urshConst->type() != MIRType::Int32 ||
2839 !lshConst->isConstant() || lshConst->type() != MIRType::Int32) {
2840 return nullptr;
2841 }
2842 int32_t c = urshConst->toConstant()->toInt32();
2843 int32_t d = 32 - c;
2844 if (c < 1 || c > 31 || !lshConst->toConstant()->isInt32(d)) {
2845 return nullptr;
2846 }
2847 return urshConst;
2848 };
2849 if (rhs->type() == MIRType::Int32 && lhs->type() == MIRType::Int32) {
2850 if (MDefinition* count = isRotateRight(lhs, rhs)) {
2851 return MRotate::New(alloc, lhs->getOperand(0), count, MIRType::Int32,
2852 /* left = */ false);
2853 }
2854 if (MDefinition* count = isRotateRight(rhs, lhs)) {
2855 return MRotate::New(alloc, rhs->getOperand(0), count, MIRType::Int32,
2856 /* left = */ false);
2857 }
2858 }
2859 return this;
2860}
2861
2862MDefinition* MBinaryBitwiseInstruction::foldUnnecessaryBitop() {
2863 // It's probably OK to perform this optimization only for int32, as JS
2864 // bytecode does not see int64 values.
2865
2866 if (type() != MIRType::Int32) {
2867 return this;
2868 }
2869
2870 // Fold unsigned shift right operator when the second operand is zero and
2871 // the only use is an unsigned modulo. Thus, the expression
2872 // |(x >>> 0) % y| becomes |x % y|.
2873 if (isUrsh() && IsUint32Type(this)) {
2874 MDefinition* defUse = maybeSingleDefUse();
2875 if (defUse && defUse->isMod() && defUse->toMod()->isUnsigned()) {
2876 return getOperand(0);
2877 }
2878 }
2879
2880 // Eliminate bitwise operations that are no-ops when used on integer
2881 // inputs, such as (x | 0).
2882
2883 MDefinition* lhs = getOperand(0);
2884 MDefinition* rhs = getOperand(1);
2885
2886 if (IsConstant(lhs, 0)) {
2887 return foldIfZero(0);
2888 }
2889
2890 if (IsConstant(rhs, 0)) {
2891 return foldIfZero(1);
2892 }
2893
2894 if (IsConstant(lhs, -1)) {
2895 return foldIfNegOne(0);
2896 }
2897
2898 if (IsConstant(rhs, -1)) {
2899 return foldIfNegOne(1);
2900 }
2901
2902 if (lhs == rhs) {
2903 return foldIfEqual();
2904 }
2905
2906 if (maskMatchesRightRange) {
2907 MOZ_ASSERT(lhs->isConstant())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lhs->isConstant())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lhs->isConstant()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("lhs->isConstant()"
, "./../../../../js/src/jit/MIR.cpp", 2907); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lhs->isConstant()" ")"); do { MOZ_CrashSequence
(__null, 2907); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2908 MOZ_ASSERT(lhs->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lhs->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lhs->type() == MIRType::Int32
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lhs->type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 2908); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 2908); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2909 return foldIfAllBitsSet(0);
2910 }
2911
2912 if (maskMatchesLeftRange) {
2913 MOZ_ASSERT(rhs->isConstant())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(rhs->isConstant())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(rhs->isConstant()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("rhs->isConstant()"
, "./../../../../js/src/jit/MIR.cpp", 2913); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "rhs->isConstant()" ")"); do { MOZ_CrashSequence
(__null, 2913); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2914 MOZ_ASSERT(rhs->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(rhs->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(rhs->type() == MIRType::Int32
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"rhs->type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 2914); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rhs->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 2914); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2915 return foldIfAllBitsSet(1);
2916 }
2917
2918 return this;
2919}
2920
2921static inline bool CanProduceNegativeZero(MDefinition* def) {
2922 // Test if this instruction can produce negative zero even when bailing out
2923 // and changing types.
2924 switch (def->op()) {
2925 case MDefinition::Opcode::Constant:
2926 if (def->type() == MIRType::Double &&
2927 def->toConstant()->toDouble() == -0.0) {
2928 return true;
2929 }
2930 [[fallthrough]];
2931 case MDefinition::Opcode::BitAnd:
2932 case MDefinition::Opcode::BitOr:
2933 case MDefinition::Opcode::BitXor:
2934 case MDefinition::Opcode::BitNot:
2935 case MDefinition::Opcode::Lsh:
2936 case MDefinition::Opcode::Rsh:
2937 return false;
2938 default:
2939 return true;
2940 }
2941}
2942
2943static inline bool NeedNegativeZeroCheck(MDefinition* def) {
2944 if (def->isGuard() || def->isGuardRangeBailouts()) {
2945 return true;
2946 }
2947
2948 // Test if all uses have the same semantics for -0 and 0
2949 for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) {
2950 if (use->consumer()->isResumePoint()) {
2951 return true;
2952 }
2953
2954 MDefinition* use_def = use->consumer()->toDefinition();
2955 switch (use_def->op()) {
2956 case MDefinition::Opcode::Add: {
2957 // If add is truncating -0 and 0 are observed as the same.
2958 if (use_def->toAdd()->isTruncated()) {
2959 break;
2960 }
2961
2962 // x + y gives -0, when both x and y are -0
2963
2964 // Figure out the order in which the addition's operands will
2965 // execute. EdgeCaseAnalysis::analyzeLate has renumbered the MIR
2966 // definitions for us so that this just requires comparing ids.
2967 MDefinition* first = use_def->toAdd()->lhs();
2968 MDefinition* second = use_def->toAdd()->rhs();
2969 if (first->id() > second->id()) {
2970 std::swap(first, second);
2971 }
2972 // Negative zero checks can be removed on the first executed
2973 // operand only if it is guaranteed the second executed operand
2974 // will produce a value other than -0. While the second is
2975 // typed as an int32, a bailout taken between execution of the
2976 // operands may change that type and cause a -0 to flow to the
2977 // second.
2978 //
2979 // There is no way to test whether there are any bailouts
2980 // between execution of the operands, so remove negative
2981 // zero checks from the first only if the second's type is
2982 // independent from type changes that may occur after bailing.
2983 if (def == first && CanProduceNegativeZero(second)) {
2984 return true;
2985 }
2986
2987 // The negative zero check can always be removed on the second
2988 // executed operand; by the time this executes the first will have
2989 // been evaluated as int32 and the addition's result cannot be -0.
2990 break;
2991 }
2992 case MDefinition::Opcode::Sub: {
2993 // If sub is truncating -0 and 0 are observed as the same
2994 if (use_def->toSub()->isTruncated()) {
2995 break;
2996 }
2997
2998 // x + y gives -0, when x is -0 and y is 0
2999
3000 // We can remove the negative zero check on the rhs, only if we
3001 // are sure the lhs isn't negative zero.
3002
3003 // The lhs is typed as integer (i.e. not -0.0), but it can bailout
3004 // and change type. This should be fine if the lhs is executed
3005 // first. However if the rhs is executed first, the lhs can bail,
3006 // change type and become -0.0 while the rhs has already been
3007 // optimized to not make a difference between zero and negative zero.
3008 MDefinition* lhs = use_def->toSub()->lhs();
3009 MDefinition* rhs = use_def->toSub()->rhs();
3010 if (rhs->id() < lhs->id() && CanProduceNegativeZero(lhs)) {
3011 return true;
3012 }
3013
3014 [[fallthrough]];
3015 }
3016 case MDefinition::Opcode::StoreElement:
3017 case MDefinition::Opcode::StoreHoleValueElement:
3018 case MDefinition::Opcode::LoadElement:
3019 case MDefinition::Opcode::LoadElementHole:
3020 case MDefinition::Opcode::LoadUnboxedScalar:
3021 case MDefinition::Opcode::LoadDataViewElement:
3022 case MDefinition::Opcode::LoadTypedArrayElementHole:
3023 case MDefinition::Opcode::CharCodeAt:
3024 case MDefinition::Opcode::Mod:
3025 case MDefinition::Opcode::InArray:
3026 // Only allowed to remove check when definition is the second operand
3027 if (use_def->getOperand(0) == def) {
3028 return true;
3029 }
3030 for (size_t i = 2, e = use_def->numOperands(); i < e; i++) {
3031 if (use_def->getOperand(i) == def) {
3032 return true;
3033 }
3034 }
3035 break;
3036 case MDefinition::Opcode::BoundsCheck:
3037 // Only allowed to remove check when definition is the first operand
3038 if (use_def->toBoundsCheck()->getOperand(1) == def) {
3039 return true;
3040 }
3041 break;
3042 case MDefinition::Opcode::ToString:
3043 case MDefinition::Opcode::FromCharCode:
3044 case MDefinition::Opcode::FromCodePoint:
3045 case MDefinition::Opcode::TableSwitch:
3046 case MDefinition::Opcode::Compare:
3047 case MDefinition::Opcode::BitAnd:
3048 case MDefinition::Opcode::BitOr:
3049 case MDefinition::Opcode::BitXor:
3050 case MDefinition::Opcode::Abs:
3051 case MDefinition::Opcode::TruncateToInt32:
3052 // Always allowed to remove check. No matter which operand.
3053 break;
3054 case MDefinition::Opcode::StoreElementHole:
3055 case MDefinition::Opcode::StoreTypedArrayElementHole:
3056 // Only allowed to remove check when definition is the third operand.
3057 for (size_t i = 0, e = use_def->numOperands(); i < e; i++) {
3058 if (i == 2) {
3059 continue;
3060 }
3061 if (use_def->getOperand(i) == def) {
3062 return true;
3063 }
3064 }
3065 break;
3066 default:
3067 return true;
3068 }
3069 }
3070 return false;
3071}
3072
3073#ifdef JS_JITSPEW1
3074void MBinaryArithInstruction::printOpcode(GenericPrinter& out) const {
3075 MDefinition::printOpcode(out);
3076
3077 switch (type()) {
3078 case MIRType::Int32:
3079 if (isDiv()) {
3080 out.printf(" [%s]", toDiv()->isUnsigned() ? "uint32" : "int32");
3081 } else if (isMod()) {
3082 out.printf(" [%s]", toMod()->isUnsigned() ? "uint32" : "int32");
3083 } else {
3084 out.printf(" [int32]");
3085 }
3086 break;
3087 case MIRType::Int64:
3088 if (isDiv()) {
3089 out.printf(" [%s]", toDiv()->isUnsigned() ? "uint64" : "int64");
3090 } else if (isMod()) {
3091 out.printf(" [%s]", toMod()->isUnsigned() ? "uint64" : "int64");
3092 } else {
3093 out.printf(" [int64]");
3094 }
3095 break;
3096 case MIRType::Float32:
3097 out.printf(" [float]");
3098 break;
3099 case MIRType::Double:
3100 out.printf(" [double]");
3101 break;
3102 default:
3103 break;
3104 }
3105}
3106#endif
3107
3108MDefinition* MRsh::foldsTo(TempAllocator& alloc) {
3109 MDefinition* f = MBinaryBitwiseInstruction::foldsTo(alloc);
3110
3111 if (f != this) {
3112 return f;
3113 }
3114
3115 MDefinition* lhs = getOperand(0);
3116 MDefinition* rhs = getOperand(1);
3117
3118 // It's probably OK to perform this optimization only for int32, as JS
3119 // bytecode does not see int64 values.
3120
3121 if (!lhs->isLsh() || !rhs->isConstant() || rhs->type() != MIRType::Int32) {
3122 return this;
3123 }
3124
3125 if (!lhs->getOperand(1)->isConstant() ||
3126 lhs->getOperand(1)->type() != MIRType::Int32) {
3127 return this;
3128 }
3129
3130 uint32_t shift = rhs->toConstant()->toInt32();
3131 uint32_t shift_lhs = lhs->getOperand(1)->toConstant()->toInt32();
3132 if (shift != shift_lhs) {
3133 return this;
3134 }
3135
3136 switch (shift) {
3137 case 16:
3138 return MSignExtendInt32::New(alloc, lhs->getOperand(0),
3139 MSignExtendInt32::Half);
3140 case 24:
3141 return MSignExtendInt32::New(alloc, lhs->getOperand(0),
3142 MSignExtendInt32::Byte);
3143 }
3144
3145 return this;
3146}
3147
3148MDefinition* MBinaryArithInstruction::foldsTo(TempAllocator& alloc) {
3149 MOZ_ASSERT(IsNumberType(type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsNumberType(type()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsNumberType(type())))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("IsNumberType(type())"
, "./../../../../js/src/jit/MIR.cpp", 3149); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsNumberType(type())" ")"); do { MOZ_CrashSequence
(__null, 3149); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3150 MOZ_ASSERT(!isDiv() && !isMod(), "Div and Mod don't call this method")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isDiv() && !isMod())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isDiv() && !isMod()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!isDiv() && !isMod()" " (" "Div and Mod don't call this method"
")", "./../../../../js/src/jit/MIR.cpp", 3150); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isDiv() && !isMod()" ") (" "Div and Mod don't call this method"
")"); do { MOZ_CrashSequence(__null, 3150); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3151
3152 MDefinition* lhs = getOperand(0);
3153 MDefinition* rhs = getOperand(1);
3154
3155 if (type() == MIRType::Int64) {
3156 MOZ_ASSERT(!isTruncated())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isTruncated())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isTruncated()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!isTruncated()"
, "./../../../../js/src/jit/MIR.cpp", 3156); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isTruncated()" ")"); do { MOZ_CrashSequence
(__null, 3156); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3157
3158 if (MConstant* folded = EvaluateInt64ConstantOperands(alloc, this)) {
3159 return folded;
3160 }
3161 if (IsConstantInt64(rhs, int64_t(getIdentity()))) {
3162 return lhs; // x op id => x
3163 }
3164 if (isCommutative() && IsConstantInt64(lhs, int64_t(getIdentity()))) {
3165 return rhs; // id op x => x
3166 }
3167 return this;
3168 }
3169
3170 if (type() == MIRType::IntPtr) {
3171 MOZ_ASSERT(!isTruncated())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isTruncated())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isTruncated()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!isTruncated()"
, "./../../../../js/src/jit/MIR.cpp", 3171); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isTruncated()" ")"); do { MOZ_CrashSequence
(__null, 3171); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3172
3173 if (MConstant* folded = EvaluateIntPtrConstantOperands(alloc, this)) {
3174 return folded;
3175 }
3176 if (IsConstantIntPtr(rhs, intptr_t(getIdentity()))) {
3177 return lhs; // x op id => x
3178 }
3179 if (isCommutative() && IsConstantIntPtr(lhs, intptr_t(getIdentity()))) {
3180 return rhs; // id op x => x
3181 }
3182 return this;
3183 }
3184
3185 // The remaining operations expect types representable as doubles.
3186 MOZ_ASSERT(IsTypeRepresentableAsDouble(type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsTypeRepresentableAsDouble(type()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsTypeRepresentableAsDouble(
type())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("IsTypeRepresentableAsDouble(type())", "./../../../../js/src/jit/MIR.cpp"
, 3186); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsTypeRepresentableAsDouble(type())"
")"); do { MOZ_CrashSequence(__null, 3186); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3187
3188 if (MConstant* folded = EvaluateConstantOperands(alloc, this)) {
3189 if (isTruncated()) {
3190 if (folded->type() != MIRType::Int32) {
3191 if (!folded->block()) {
3192 block()->insertBefore(this, folded);
3193 }
3194 return MTruncateToInt32::New(alloc, folded);
3195 }
3196 }
3197 return folded;
3198 }
3199
3200 if (MConstant* folded = EvaluateConstantNaNOperand(this)) {
3201 MOZ_ASSERT(!isTruncated())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isTruncated())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isTruncated()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!isTruncated()"
, "./../../../../js/src/jit/MIR.cpp", 3201); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isTruncated()" ")"); do { MOZ_CrashSequence
(__null, 3201); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3202 return folded;
3203 }
3204
3205 if (mustPreserveNaN_) {
3206 return this;
3207 }
3208
3209 // 0 + -0 = 0. So we can't remove addition
3210 if (isAdd() && type() != MIRType::Int32) {
3211 return this;
3212 }
3213
3214 if (IsConstant(rhs, getIdentity())) {
3215 if (isTruncated()) {
3216 return MTruncateToInt32::New(alloc, lhs);
3217 }
3218 return lhs;
3219 }
3220
3221 // subtraction isn't commutative. So we can't remove subtraction when lhs
3222 // equals 0
3223 if (isSub()) {
3224 return this;
3225 }
3226
3227 if (IsConstant(lhs, getIdentity())) {
3228 if (isTruncated()) {
3229 return MTruncateToInt32::New(alloc, rhs);
3230 }
3231 return rhs; // id op x => x
3232 }
3233
3234 return this;
3235}
3236
3237void MBinaryArithInstruction::trySpecializeFloat32(TempAllocator& alloc) {
3238 MOZ_ASSERT(IsNumberType(type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsNumberType(type()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsNumberType(type())))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("IsNumberType(type())"
, "./../../../../js/src/jit/MIR.cpp", 3238); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsNumberType(type())" ")"); do { MOZ_CrashSequence
(__null, 3238); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3239
3240 // Do not use Float32 if we can use integer types.
3241 if (!IsFloatingPointType(type())) {
3242 return;
3243 }
3244
3245 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
3246 setResultType(MIRType::Float32);
3247 }
3248}
3249
3250void MMinMax::trySpecializeFloat32(TempAllocator& alloc) {
3251 if (!IsFloatingPointType(type())) {
3252 return;
3253 }
3254
3255 MDefinition* left = lhs();
3256 MDefinition* right = rhs();
3257
3258 if ((left->canProduceFloat32() ||
3259 (left->isMinMax() && left->type() == MIRType::Float32)) &&
3260 (right->canProduceFloat32() ||
3261 (right->isMinMax() && right->type() == MIRType::Float32))) {
3262 setResultType(MIRType::Float32);
3263 } else {
3264 ConvertOperandsToDouble(this, alloc);
3265 }
3266}
3267
3268template <MIRType Type>
3269static MConstant* EvaluateMinMaxInt(TempAllocator& alloc, MConstant* lhs,
3270 MConstant* rhs, bool isMax) {
3271 auto lnum = ToIntConstant<Type>(lhs);
3272 auto rnum = ToIntConstant<Type>(rhs);
3273 auto result = isMax ? std::max(lnum, rnum) : std::min(lnum, rnum);
3274 return NewIntConstant<Type>(alloc, result);
3275}
3276
3277static MConstant* EvaluateMinMax(TempAllocator& alloc, MConstant* lhs,
3278 MConstant* rhs, bool isMax) {
3279 MOZ_ASSERT(lhs->type() == rhs->type())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lhs->type() == rhs->type())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lhs->type() == rhs->type
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("lhs->type() == rhs->type()", "./../../../../js/src/jit/MIR.cpp"
, 3279); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == rhs->type()"
")"); do { MOZ_CrashSequence(__null, 3279); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3280 MOZ_ASSERT(IsNumberType(lhs->type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsNumberType(lhs->type()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsNumberType(lhs->type())
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"IsNumberType(lhs->type())", "./../../../../js/src/jit/MIR.cpp"
, 3280); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsNumberType(lhs->type())"
")"); do { MOZ_CrashSequence(__null, 3280); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3281
3282 // The folded MConstant should maintain the same MIRType with the original
3283 // inputs.
3284 switch (lhs->type()) {
3285 case MIRType::Int32:
3286 return EvaluateMinMaxInt<MIRType::Int32>(alloc, lhs, rhs, isMax);
3287 case MIRType::Int64:
3288 return EvaluateMinMaxInt<MIRType::Int64>(alloc, lhs, rhs, isMax);
3289 case MIRType::IntPtr:
3290 return EvaluateMinMaxInt<MIRType::IntPtr>(alloc, lhs, rhs, isMax);
3291 case MIRType::Float32:
3292 case MIRType::Double: {
3293 double lnum = lhs->numberToDouble();
3294 double rnum = rhs->numberToDouble();
3295
3296 double result;
3297 if (isMax) {
3298 result = js::math_max_impl(lnum, rnum);
3299 } else {
3300 result = js::math_min_impl(lnum, rnum);
3301 }
3302
3303 if (lhs->type() == MIRType::Float32) {
3304 return MConstant::NewFloat32(alloc, result);
3305 }
3306 return MConstant::NewDouble(alloc, result);
3307 }
3308 default:
3309 MOZ_CRASH("not a number type")do { do { } while (false); MOZ_ReportCrash("" "not a number type"
, "./../../../../js/src/jit/MIR.cpp", 3309); AnnotateMozCrashReason
("MOZ_CRASH(" "not a number type" ")"); do { MOZ_CrashSequence
(__null, 3309); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
3310 }
3311}
3312
3313MDefinition* MMinMax::foldsTo(TempAllocator& alloc) {
3314 MOZ_ASSERT(lhs()->type() == type())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lhs()->type() == type())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lhs()->type() == type()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lhs()->type() == type()"
, "./../../../../js/src/jit/MIR.cpp", 3314); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lhs()->type() == type()" ")"); do { MOZ_CrashSequence
(__null, 3314); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3315 MOZ_ASSERT(rhs()->type() == type())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(rhs()->type() == type())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(rhs()->type() == type()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("rhs()->type() == type()"
, "./../../../../js/src/jit/MIR.cpp", 3315); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "rhs()->type() == type()" ")"); do { MOZ_CrashSequence
(__null, 3315); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3316
3317 if (lhs() == rhs()) {
3318 return lhs();
3319 }
3320
3321 auto foldConstants = [&alloc](MDefinition* lhs, MDefinition* rhs,
3322 bool isMax) -> MConstant* {
3323 return EvaluateMinMax(alloc, lhs->toConstant(), rhs->toConstant(), isMax);
3324 };
3325
3326 auto foldLength = [](MDefinition* operand, MConstant* constant,
3327 bool isMax) -> MDefinition* {
3328 if (operand->isArrayLength() || operand->isArrayBufferViewLength() ||
3329 operand->isArgumentsLength() || operand->isStringLength() ||
3330 operand->isNonNegativeIntPtrToInt32()) {
3331 bool isZeroOrNegative;
3332 switch (constant->type()) {
3333 case MIRType::Int32:
3334 isZeroOrNegative = constant->toInt32() <= 0;
3335 break;
3336 case MIRType::IntPtr:
3337 isZeroOrNegative = constant->toIntPtr() <= 0;
3338 break;
3339 default:
3340 isZeroOrNegative = false;
3341 break;
3342 }
3343
3344 // (Array|ArrayBufferView|Arguments|String)Length is always >= 0.
3345 // max(array.length, cte <= 0) = array.length
3346 // min(array.length, cte <= 0) = cte
3347 if (isZeroOrNegative) {
3348 return isMax ? operand : constant;
3349 }
3350 }
3351 return nullptr;
3352 };
3353
3354 // Try to fold the following patterns when |x| and |y| are constants.
3355 //
3356 // min(min(x, z), min(y, z)) = min(min(x, y), z)
3357 // max(max(x, z), max(y, z)) = max(max(x, y), z)
3358 // max(min(x, z), min(y, z)) = min(max(x, y), z)
3359 // min(max(x, z), max(y, z)) = max(min(x, y), z)
3360 if (lhs()->isMinMax() && rhs()->isMinMax()) {
3361 do {
3362 auto* left = lhs()->toMinMax();
3363 auto* right = rhs()->toMinMax();
3364 if (left->isMax() != right->isMax()) {
3365 break;
3366 }
3367
3368 MDefinition* x;
3369 MDefinition* y;
3370 MDefinition* z;
3371 if (left->lhs() == right->lhs()) {
3372 std::tie(x, y, z) = std::tuple{left->rhs(), right->rhs(), left->lhs()};
3373 } else if (left->lhs() == right->rhs()) {
3374 std::tie(x, y, z) = std::tuple{left->rhs(), right->lhs(), left->lhs()};
3375 } else if (left->rhs() == right->lhs()) {
3376 std::tie(x, y, z) = std::tuple{left->lhs(), right->rhs(), left->rhs()};
3377 } else if (left->rhs() == right->rhs()) {
3378 std::tie(x, y, z) = std::tuple{left->lhs(), right->lhs(), left->rhs()};
3379 } else {
3380 break;
3381 }
3382
3383 if (!x->isConstant() || !y->isConstant()) {
3384 break;
3385 }
3386
3387 if (auto* foldedCst = foldConstants(x, y, isMax())) {
3388 if (auto* folded = foldLength(z, foldedCst, left->isMax())) {
3389 return folded;
3390 }
3391 block()->insertBefore(this, foldedCst);
3392 return MMinMax::New(alloc, foldedCst, z, type(), left->isMax());
3393 }
3394 } while (false);
3395 }
3396
3397 // Fold min/max operations with same inputs.
3398 if (lhs()->isMinMax() || rhs()->isMinMax()) {
3399 auto* other = lhs()->isMinMax() ? lhs()->toMinMax() : rhs()->toMinMax();
3400 auto* operand = lhs()->isMinMax() ? rhs() : lhs();
3401
3402 if (operand == other->lhs() || operand == other->rhs()) {
3403 if (isMax() == other->isMax()) {
3404 // min(x, min(x, y)) = min(x, y)
3405 // max(x, max(x, y)) = max(x, y)
3406 return other;
3407 }
3408 if (!IsFloatingPointType(type())) {
3409 // When neither value is NaN:
3410 // max(x, min(x, y)) = x
3411 // min(x, max(x, y)) = x
3412
3413 // Ensure that any bailouts that we depend on to guarantee that |y| is
3414 // Int32 are not removed.
3415 auto* otherOp = operand == other->lhs() ? other->rhs() : other->lhs();
3416 otherOp->setGuardRangeBailoutsUnchecked();
3417
3418 return operand;
3419 }
3420 }
3421 }
3422
3423 if (!lhs()->isConstant() && !rhs()->isConstant()) {
3424 return this;
3425 }
3426
3427 // Directly apply math utility to compare the rhs() and lhs() when
3428 // they are both constants.
3429 if (lhs()->isConstant() && rhs()->isConstant()) {
3430 if (auto* folded = foldConstants(lhs(), rhs(), isMax())) {
3431 return folded;
3432 }
3433 }
3434
3435 MDefinition* operand = lhs()->isConstant() ? rhs() : lhs();
3436 MConstant* constant =
3437 lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant();
3438
3439 if (operand->isToDouble() &&
3440 operand->getOperand(0)->type() == MIRType::Int32) {
3441 MOZ_ASSERT(constant->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(constant->type() == MIRType::Double)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(constant->type() == MIRType::Double))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("constant->type() == MIRType::Double"
, "./../../../../js/src/jit/MIR.cpp", 3441); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "constant->type() == MIRType::Double" ")"
); do { MOZ_CrashSequence(__null, 3441); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3442
3443 // min(int32, cte >= INT32_MAX) = int32
3444 if (!isMax() && constant->toDouble() >= INT32_MAX(2147483647)) {
3445 MLimitedTruncate* limit = MLimitedTruncate::New(
3446 alloc, operand->getOperand(0), TruncateKind::NoTruncate);
3447 block()->insertBefore(this, limit);
3448 MToDouble* toDouble = MToDouble::New(alloc, limit);
3449 return toDouble;
3450 }
3451
3452 // max(int32, cte <= INT32_MIN) = int32
3453 if (isMax() && constant->toDouble() <= INT32_MIN(-2147483647-1)) {
3454 MLimitedTruncate* limit = MLimitedTruncate::New(
3455 alloc, operand->getOperand(0), TruncateKind::NoTruncate);
3456 block()->insertBefore(this, limit);
3457 MToDouble* toDouble = MToDouble::New(alloc, limit);
3458 return toDouble;
3459 }
3460 }
3461
3462 if (auto* folded = foldLength(operand, constant, isMax())) {
3463 return folded;
3464 }
3465
3466 // Attempt to fold nested min/max operations which are produced by
3467 // self-hosted built-in functions.
3468 if (operand->isMinMax()) {
3469 auto* other = operand->toMinMax();
3470 MOZ_ASSERT(other->lhs()->type() == type())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(other->lhs()->type() == type())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(other->lhs()->type() ==
type()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("other->lhs()->type() == type()", "./../../../../js/src/jit/MIR.cpp"
, 3470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "other->lhs()->type() == type()"
")"); do { MOZ_CrashSequence(__null, 3470); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3471 MOZ_ASSERT(other->rhs()->type() == type())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(other->rhs()->type() == type())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(other->rhs()->type() ==
type()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("other->rhs()->type() == type()", "./../../../../js/src/jit/MIR.cpp"
, 3471); AnnotateMozCrashReason("MOZ_ASSERT" "(" "other->rhs()->type() == type()"
")"); do { MOZ_CrashSequence(__null, 3471); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3472
3473 MConstant* otherConstant = nullptr;
3474 MDefinition* otherOperand = nullptr;
3475 if (other->lhs()->isConstant()) {
3476 otherConstant = other->lhs()->toConstant();
3477 otherOperand = other->rhs();
3478 } else if (other->rhs()->isConstant()) {
3479 otherConstant = other->rhs()->toConstant();
3480 otherOperand = other->lhs();
3481 }
3482
3483 if (otherConstant) {
3484 if (isMax() == other->isMax()) {
3485 // Fold min(x, min(y, z)) to min(min(x, y), z) with constant min(x, y).
3486 // Fold max(x, max(y, z)) to max(max(x, y), z) with constant max(x, y).
3487 if (auto* left = foldConstants(constant, otherConstant, isMax())) {
3488 if (auto* folded = foldLength(otherOperand, left, isMax())) {
3489 return folded;
3490 }
3491 block()->insertBefore(this, left);
3492 return MMinMax::New(alloc, left, otherOperand, type(), isMax());
3493 }
3494 } else {
3495 // Fold min(x, max(y, z)) to max(min(x, y), min(x, z)).
3496 // Fold max(x, min(y, z)) to min(max(x, y), max(x, z)).
3497 //
3498 // But only do this when min(x, z) can also be simplified.
3499 if (auto* right = foldLength(otherOperand, constant, isMax())) {
3500 if (auto* left = foldConstants(constant, otherConstant, isMax())) {
3501 block()->insertBefore(this, left);
3502 return MMinMax::New(alloc, left, right, type(), !isMax());
3503 }
3504 }
3505 }
3506 }
3507 }
3508
3509 return this;
3510}
3511
3512#ifdef JS_JITSPEW1
3513void MMinMax::printOpcode(GenericPrinter& out) const {
3514 MDefinition::printOpcode(out);
3515 out.printf(" (%s)", isMax() ? "max" : "min");
3516}
3517
3518void MMinMaxArray::printOpcode(GenericPrinter& out) const {
3519 MDefinition::printOpcode(out);
3520 out.printf(" (%s)", isMax() ? "max" : "min");
3521}
3522#endif
3523
3524MDefinition* MPow::foldsConstant(TempAllocator& alloc) {
3525 // Both `x` and `p` in `x^p` must be constants in order to precompute.
3526 if (!input()->isConstant() || !power()->isConstant()) {
3527 return nullptr;
3528 }
3529 if (!power()->toConstant()->isTypeRepresentableAsDouble()) {
3530 return nullptr;
3531 }
3532 if (!input()->toConstant()->isTypeRepresentableAsDouble()) {
3533 return nullptr;
3534 }
3535
3536 double x = input()->toConstant()->numberToDouble();
3537 double p = power()->toConstant()->numberToDouble();
3538 double result = js::ecmaPow(x, p);
3539 if (type() == MIRType::Int32) {
3540 int32_t cast;
3541 if (!mozilla::NumberIsInt32(result, &cast)) {
3542 // Reject folding if the result isn't an int32, because we'll bail anyway.
3543 return nullptr;
3544 }
3545 return MConstant::NewInt32(alloc, cast);
3546 }
3547 return MConstant::NewDouble(alloc, result);
3548}
3549
3550MDefinition* MPow::foldsConstantPower(TempAllocator& alloc) {
3551 // If `p` in `x^p` isn't constant, we can't apply these folds.
3552 if (!power()->isConstant()) {
3553 return nullptr;
3554 }
3555 if (!power()->toConstant()->isTypeRepresentableAsDouble()) {
3556 return nullptr;
3557 }
3558
3559 MOZ_ASSERT(type() == MIRType::Double || type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type() == MIRType::Double || type() == MIRType::Int32
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(type() == MIRType::Double || type() == MIRType::Int32
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"type() == MIRType::Double || type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 3559); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type() == MIRType::Double || type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 3559); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3560
3561 // NOTE: The optimizations must match the optimizations used in |js::ecmaPow|
3562 // resp. |js::powi| to avoid differential testing issues.
3563
3564 double pow = power()->toConstant()->numberToDouble();
3565
3566 // Math.pow(x, 0.5) is a sqrt with edge-case detection.
3567 if (pow == 0.5) {
3568 MOZ_ASSERT(type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type() == MIRType::Double)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type() == MIRType::Double)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("type() == MIRType::Double"
, "./../../../../js/src/jit/MIR.cpp", 3568); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Double" ")"); do { MOZ_CrashSequence
(__null, 3568); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3569 return MPowHalf::New(alloc, input());
3570 }
3571
3572 // Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5), even for edge cases.
3573 if (pow == -0.5) {
3574 MOZ_ASSERT(type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type() == MIRType::Double)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type() == MIRType::Double)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("type() == MIRType::Double"
, "./../../../../js/src/jit/MIR.cpp", 3574); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Double" ")"); do { MOZ_CrashSequence
(__null, 3574); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3575 MPowHalf* half = MPowHalf::New(alloc, input());
3576 block()->insertBefore(this, half);
3577 MConstant* one = MConstant::NewDouble(alloc, 1.0);
3578 block()->insertBefore(this, one);
3579 return MDiv::New(alloc, one, half, MIRType::Double);
3580 }
3581
3582 // Math.pow(x, 1) == x.
3583 if (pow == 1.0) {
3584 return input();
3585 }
3586
3587 auto multiply = [this, &alloc](MDefinition* lhs, MDefinition* rhs) {
3588 MMul* mul = MMul::New(alloc, lhs, rhs, type());
3589 mul->setBailoutKind(bailoutKind());
3590
3591 // Multiplying the same number can't yield negative zero.
3592 mul->setCanBeNegativeZero(lhs != rhs && canBeNegativeZero());
3593 return mul;
3594 };
3595
3596 // Math.pow(x, 2) == x*x.
3597 if (pow == 2.0) {
3598 return multiply(input(), input());
3599 }
3600
3601 // Math.pow(x, 3) == x*x*x.
3602 if (pow == 3.0) {
3603 MMul* mul1 = multiply(input(), input());
3604 block()->insertBefore(this, mul1);
3605 return multiply(input(), mul1);
3606 }
3607
3608 // Math.pow(x, 4) == y*y, where y = x*x.
3609 if (pow == 4.0) {
3610 MMul* y = multiply(input(), input());
3611 block()->insertBefore(this, y);
3612 return multiply(y, y);
3613 }
3614
3615 // Math.pow(x, NaN) == NaN.
3616 if (std::isnan(pow)) {
3617 return power();
3618 }
3619
3620 // No optimization
3621 return nullptr;
3622}
3623
3624MDefinition* MPow::foldsTo(TempAllocator& alloc) {
3625 if (MDefinition* def = foldsConstant(alloc)) {
3626 return def;
3627 }
3628 if (MDefinition* def = foldsConstantPower(alloc)) {
3629 return def;
3630 }
3631 return this;
3632}
3633
3634MDefinition* MBigIntPow::foldsTo(TempAllocator& alloc) {
3635 auto* base = lhs();
3636 MOZ_ASSERT(base->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(base->type() == MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(base->type() == MIRType::
BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("base->type() == MIRType::BigInt", "./../../../../js/src/jit/MIR.cpp"
, 3636); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 3636); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3637
3638 auto* power = rhs();
3639 MOZ_ASSERT(power->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(power->type() == MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(power->type() == MIRType::
BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("power->type() == MIRType::BigInt", "./../../../../js/src/jit/MIR.cpp"
, 3639); AnnotateMozCrashReason("MOZ_ASSERT" "(" "power->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 3639); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3640
3641 // |power| must be a constant.
3642 if (!power->isConstant()) {
3643 return this;
3644 }
3645
3646 int32_t pow;
3647 if (BigInt::isInt32(power->toConstant()->toBigInt(), &pow)) {
3648 // x ** 1n == x.
3649 if (pow == 1) {
3650 return base;
3651 }
3652
3653 // x ** 2n == x*x.
3654 if (pow == 2) {
3655 auto* mul = MBigIntMul::New(alloc, base, base);
3656 mul->setBailoutKind(bailoutKind());
3657 return mul;
3658 }
3659 }
3660
3661 // No optimization
3662 return this;
3663}
3664
3665MDefinition* MBigIntAsIntN::foldsTo(TempAllocator& alloc) {
3666 auto* bitsDef = bits();
3667 if (!bitsDef->isConstant()) {
3668 return this;
3669 }
3670
3671 // Negative |bits| throw an error and too large |bits| don't fit into Int64.
3672 int32_t bitsInt = bitsDef->toConstant()->toInt32();
3673 if (bitsInt < 0 || bitsInt > 64) {
3674 return this;
3675 }
3676
3677 // Prefer sign-extension if possible.
3678 bool canSignExtend = false;
3679 switch (bitsInt) {
3680 case 8:
3681 case 16:
3682 case 32:
3683 case 64:
3684 canSignExtend = true;
3685 break;
3686 }
3687
3688 // Ensure the input is either IntPtr or Int64 typed.
3689 auto* inputDef = input();
3690 if (inputDef->isIntPtrToBigInt()) {
3691 inputDef = inputDef->toIntPtrToBigInt()->input();
3692
3693 if (!canSignExtend) {
3694 auto* int64 = MIntPtrToInt64::New(alloc, inputDef);
3695 block()->insertBefore(this, int64);
3696 inputDef = int64;
3697 }
3698 } else if (inputDef->isInt64ToBigInt()) {
3699 inputDef = inputDef->toInt64ToBigInt()->input();
3700 } else {
3701 auto* truncate = MTruncateBigIntToInt64::New(alloc, inputDef);
3702 block()->insertBefore(this, truncate);
3703 inputDef = truncate;
3704 }
3705
3706 if (inputDef->type() == MIRType::IntPtr) {
3707 MOZ_ASSERT(canSignExtend)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(canSignExtend)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(canSignExtend))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("canSignExtend",
"./../../../../js/src/jit/MIR.cpp", 3707); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "canSignExtend" ")"); do { MOZ_CrashSequence
(__null, 3707); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3708
3709 // If |bits| is larger-or-equal to |BigInt::DigitBits|, return the input.
3710 if (size_t(bitsInt) >= BigInt::DigitBits) {
3711 auto* limited = MIntPtrLimitedTruncate::New(alloc, inputDef);
3712 block()->insertBefore(this, limited);
3713 inputDef = limited;
3714 } else {
3715 MOZ_ASSERT(bitsInt < 64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(bitsInt < 64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(bitsInt < 64))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("bitsInt < 64"
, "./../../../../js/src/jit/MIR.cpp", 3715); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "bitsInt < 64" ")"); do { MOZ_CrashSequence
(__null, 3715); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3716
3717 // Otherwise extension is the way to go.
3718 MSignExtendIntPtr::Mode mode;
3719 switch (bitsInt) {
3720 case 8:
3721 mode = MSignExtendIntPtr::Byte;
3722 break;
3723 case 16:
3724 mode = MSignExtendIntPtr::Half;
3725 break;
3726 case 32:
3727 mode = MSignExtendIntPtr::Word;
3728 break;
3729 }
3730
3731 auto* extend = MSignExtendIntPtr::New(alloc, inputDef, mode);
3732 block()->insertBefore(this, extend);
3733 inputDef = extend;
3734 }
3735
3736 return MIntPtrToBigInt::New(alloc, inputDef);
3737 }
3738 MOZ_ASSERT(inputDef->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(inputDef->type() == MIRType::Int64)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(inputDef->type() == MIRType::Int64))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("inputDef->type() == MIRType::Int64"
, "./../../../../js/src/jit/MIR.cpp", 3738); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "inputDef->type() == MIRType::Int64" ")"
); do { MOZ_CrashSequence(__null, 3738); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3739
3740 if (canSignExtend) {
3741 // If |bits| is equal to 64, return the input.
3742 if (bitsInt == 64) {
3743 auto* limited = MInt64LimitedTruncate::New(alloc, inputDef);
3744 block()->insertBefore(this, limited);
3745 inputDef = limited;
3746 } else {
3747 MOZ_ASSERT(bitsInt < 64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(bitsInt < 64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(bitsInt < 64))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("bitsInt < 64"
, "./../../../../js/src/jit/MIR.cpp", 3747); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "bitsInt < 64" ")"); do { MOZ_CrashSequence
(__null, 3747); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3748
3749 // Otherwise extension is the way to go.
3750 MSignExtendInt64::Mode mode;
3751 switch (bitsInt) {
3752 case 8:
3753 mode = MSignExtendInt64::Byte;
3754 break;
3755 case 16:
3756 mode = MSignExtendInt64::Half;
3757 break;
3758 case 32:
3759 mode = MSignExtendInt64::Word;
3760 break;
3761 }
3762
3763 auto* extend = MSignExtendInt64::New(alloc, inputDef, mode);
3764 block()->insertBefore(this, extend);
3765 inputDef = extend;
3766 }
3767 } else {
3768 MOZ_ASSERT(bitsInt < 64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(bitsInt < 64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(bitsInt < 64))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("bitsInt < 64"
, "./../../../../js/src/jit/MIR.cpp", 3768); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "bitsInt < 64" ")"); do { MOZ_CrashSequence
(__null, 3768); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3769
3770 uint64_t mask = 0;
3771 if (bitsInt > 0) {
3772 mask = uint64_t(-1) >> (64 - bitsInt);
3773 }
3774
3775 auto* cst = MConstant::NewInt64(alloc, int64_t(mask));
3776 block()->insertBefore(this, cst);
3777
3778 // Mask off any excess bits.
3779 auto* bitAnd = MBitAnd::New(alloc, inputDef, cst, MIRType::Int64);
3780 block()->insertBefore(this, bitAnd);
3781
3782 auto* shift = MConstant::NewInt64(alloc, int64_t(64 - bitsInt));
3783 block()->insertBefore(this, shift);
3784
3785 // Left-shift to make the sign-bit the left-most bit.
3786 auto* lsh = MLsh::New(alloc, bitAnd, shift, MIRType::Int64);
3787 block()->insertBefore(this, lsh);
3788
3789 // Right-shift to propagate the sign-bit.
3790 auto* rsh = MRsh::New(alloc, lsh, shift, MIRType::Int64);
3791 block()->insertBefore(this, rsh);
3792
3793 inputDef = rsh;
3794 }
3795
3796 return MInt64ToBigInt::New(alloc, inputDef, /* isSigned = */ true);
3797}
3798
3799MDefinition* MBigIntAsUintN::foldsTo(TempAllocator& alloc) {
3800 auto* bitsDef = bits();
3801 if (!bitsDef->isConstant()) {
3802 return this;
3803 }
3804
3805 // Negative |bits| throw an error and too large |bits| don't fit into Int64.
3806 int32_t bitsInt = bitsDef->toConstant()->toInt32();
3807 if (bitsInt < 0 || bitsInt > 64) {
3808 return this;
3809 }
3810
3811 // Ensure the input is Int64 typed.
3812 auto* inputDef = input();
3813 if (inputDef->isIntPtrToBigInt()) {
3814 inputDef = inputDef->toIntPtrToBigInt()->input();
3815
3816 auto* int64 = MIntPtrToInt64::New(alloc, inputDef);
3817 block()->insertBefore(this, int64);
3818 inputDef = int64;
3819 } else if (inputDef->isInt64ToBigInt()) {
3820 inputDef = inputDef->toInt64ToBigInt()->input();
3821 } else {
3822 auto* truncate = MTruncateBigIntToInt64::New(alloc, inputDef);
3823 block()->insertBefore(this, truncate);
3824 inputDef = truncate;
3825 }
3826 MOZ_ASSERT(inputDef->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(inputDef->type() == MIRType::Int64)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(inputDef->type() == MIRType::Int64))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("inputDef->type() == MIRType::Int64"
, "./../../../../js/src/jit/MIR.cpp", 3826); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "inputDef->type() == MIRType::Int64" ")"
); do { MOZ_CrashSequence(__null, 3826); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3827
3828 if (bitsInt < 64) {
3829 uint64_t mask = 0;
3830 if (bitsInt > 0) {
3831 mask = uint64_t(-1) >> (64 - bitsInt);
3832 }
3833
3834 // Mask off any excess bits.
3835 auto* cst = MConstant::NewInt64(alloc, int64_t(mask));
3836 block()->insertBefore(this, cst);
3837
3838 auto* bitAnd = MBitAnd::New(alloc, inputDef, cst, MIRType::Int64);
3839 block()->insertBefore(this, bitAnd);
3840
3841 inputDef = bitAnd;
3842 }
3843
3844 return MInt64ToBigInt::New(alloc, inputDef, /* isSigned = */ false);
3845}
3846
3847bool MBigIntPtrBinaryArithInstruction::isMaybeZero(MDefinition* ins) {
3848 MOZ_ASSERT(ins->type() == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->type() == MIRType::IntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->type() == MIRType::IntPtr
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->type() == MIRType::IntPtr", "./../../../../js/src/jit/MIR.cpp"
, 3848); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::IntPtr"
")"); do { MOZ_CrashSequence(__null, 3848); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3849 if (ins->isBigIntToIntPtr()) {
3850 ins = ins->toBigIntToIntPtr()->input();
3851 }
3852 if (ins->isConstant()) {
3853 if (ins->type() == MIRType::IntPtr) {
3854 return ins->toConstant()->toIntPtr() == 0;
3855 }
3856 MOZ_ASSERT(ins->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->type() == MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->type() == MIRType::BigInt
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->type() == MIRType::BigInt", "./../../../../js/src/jit/MIR.cpp"
, 3856); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 3856); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3857 return ins->toConstant()->toBigInt()->isZero();
3858 }
3859 return true;
3860}
3861
3862bool MBigIntPtrBinaryArithInstruction::isMaybeNegative(MDefinition* ins) {
3863 MOZ_ASSERT(ins->type() == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->type() == MIRType::IntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->type() == MIRType::IntPtr
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->type() == MIRType::IntPtr", "./../../../../js/src/jit/MIR.cpp"
, 3863); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::IntPtr"
")"); do { MOZ_CrashSequence(__null, 3863); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3864 if (ins->isBigIntToIntPtr()) {
3865 ins = ins->toBigIntToIntPtr()->input();
3866 }
3867 if (ins->isConstant()) {
3868 if (ins->type() == MIRType::IntPtr) {
3869 return ins->toConstant()->toIntPtr() < 0;
3870 }
3871 MOZ_ASSERT(ins->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->type() == MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->type() == MIRType::BigInt
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->type() == MIRType::BigInt", "./../../../../js/src/jit/MIR.cpp"
, 3871); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 3871); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3872 return ins->toConstant()->toBigInt()->isNegative();
3873 }
3874 return true;
3875}
3876
3877MDefinition* MBigIntPtrBinaryArithInstruction::foldsTo(TempAllocator& alloc) {
3878 if (auto* folded = EvaluateIntPtrConstantOperands(alloc, this)) {
3879 return folded;
3880 }
3881 return this;
3882}
3883
3884MDefinition* MBigIntPtrPow::foldsTo(TempAllocator& alloc) {
3885 // Follow MPow::foldsTo and fold if:
3886 // 1. Both operands are constants.
3887 // 2. The power operand is ≤ 4 and the operation can be expressed as a series
3888 // of multiplications.
3889
3890 if (!rhs()->isConstant()) {
3891 return this;
3892 }
3893 intptr_t pow = rhs()->toConstant()->toIntPtr();
3894
3895 if (lhs()->isConstant()) {
3896 intptr_t base = lhs()->toConstant()->toIntPtr();
3897 intptr_t result;
3898 if (!BigInt::powIntPtr(base, pow, &result)) {
3899 return this;
3900 }
3901 return MConstant::NewIntPtr(alloc, result);
3902 }
3903
3904 if (pow == 1) {
3905 return lhs();
3906 }
3907
3908 auto multiply = [this, &alloc](MDefinition* lhs, MDefinition* rhs) {
3909 auto* mul = MBigIntPtrMul::New(alloc, lhs, rhs);
3910 mul->setBailoutKind(bailoutKind());
3911 return mul;
3912 };
3913
3914 // (x ** 2n) == x*x.
3915 if (pow == 2) {
3916 return multiply(lhs(), lhs());
3917 }
3918
3919 // (x ** 3n) == x*x*x.
3920 if (pow == 3) {
3921 auto* mul1 = multiply(lhs(), lhs());
3922 block()->insertBefore(this, mul1);
3923 return multiply(lhs(), mul1);
3924 }
3925
3926 // (x ** 4n) == y*y, where y = x*x.
3927 if (pow == 4) {
3928 auto* y = multiply(lhs(), lhs());
3929 block()->insertBefore(this, y);
3930 return multiply(y, y);
3931 }
3932
3933 // No optimization
3934 return this;
3935}
3936
3937MDefinition* MBigIntPtrBinaryBitwiseInstruction::foldsTo(TempAllocator& alloc) {
3938 if (auto* folded = EvaluateIntPtrConstantOperands(alloc, this)) {
3939 return folded;
3940 }
3941 return this;
3942}
3943
3944MDefinition* MBigIntPtrBitNot::foldsTo(TempAllocator& alloc) {
3945 if (!input()->isConstant()) {
3946 return this;
3947 }
3948 return MConstant::NewIntPtr(alloc, ~input()->toConstant()->toIntPtr());
3949}
3950
3951MDefinition* MInt32ToIntPtr::foldsTo(TempAllocator& alloc) {
3952 MDefinition* def = input();
3953 if (def->isConstant()) {
3954 int32_t i = def->toConstant()->toInt32();
3955 return MConstant::NewIntPtr(alloc, intptr_t(i));
3956 }
3957
3958 if (def->isNonNegativeIntPtrToInt32()) {
3959 return def->toNonNegativeIntPtrToInt32()->input();
3960 }
3961
3962 return this;
3963}
3964
3965bool MAbs::fallible() const {
3966 return !implicitTruncate_ && (!range() || !range()->hasInt32Bounds());
3967}
3968
3969void MAbs::trySpecializeFloat32(TempAllocator& alloc) {
3970 // Do not use Float32 if we can use int32.
3971 if (input()->type() == MIRType::Int32) {
3972 return;
3973 }
3974
3975 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
3976 setResultType(MIRType::Float32);
3977 }
3978}
3979
3980MDefinition* MDiv::foldsTo(TempAllocator& alloc) {
3981 MOZ_ASSERT(IsNumberType(type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsNumberType(type()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsNumberType(type())))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("IsNumberType(type())"
, "./../../../../js/src/jit/MIR.cpp", 3981); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsNumberType(type())" ")"); do { MOZ_CrashSequence
(__null, 3981); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3982 MOZ_ASSERT(type() != MIRType::IntPtr, "not yet implemented")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type() != MIRType::IntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type() != MIRType::IntPtr)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("type() != MIRType::IntPtr"
" (" "not yet implemented" ")", "./../../../../js/src/jit/MIR.cpp"
, 3982); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type() != MIRType::IntPtr"
") (" "not yet implemented" ")"); do { MOZ_CrashSequence(__null
, 3982); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
3983
3984 if (type() == MIRType::Int64) {
3985 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) {
3986 return folded;
3987 }
3988 return this;
3989 }
3990
3991 if (MDefinition* folded = EvaluateConstantOperands(alloc, this)) {
3992 return folded;
3993 }
3994
3995 if (MDefinition* folded = EvaluateExactReciprocal(alloc, this)) {
3996 return folded;
3997 }
3998
3999 return this;
4000}
4001
4002void MDiv::analyzeEdgeCasesForward() {
4003 // This is only meaningful when doing integer division.
4004 if (type() != MIRType::Int32) {
4005 return;
4006 }
4007
4008 MOZ_ASSERT(lhs()->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lhs()->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lhs()->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("lhs()->type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 4008); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 4008); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4009 MOZ_ASSERT(rhs()->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(rhs()->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(rhs()->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("rhs()->type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 4009); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rhs()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 4009); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4010
4011 // Try removing divide by zero check
4012 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) {
4013 canBeDivideByZero_ = false;
4014 }
4015
4016 // If lhs is a constant int != INT32_MIN, then
4017 // negative overflow check can be skipped.
4018 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(INT32_MIN(-2147483647-1))) {
4019 canBeNegativeOverflow_ = false;
4020 }
4021
4022 // If rhs is a constant int != -1, likewise.
4023 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(-1)) {
4024 canBeNegativeOverflow_ = false;
4025 }
4026
4027 // If lhs is != 0, then negative zero check can be skipped.
4028 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(0)) {
4029 setCanBeNegativeZero(false);
4030 }
4031
4032 // If rhs is >= 0, likewise.
4033 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32) {
4034 if (rhs()->toConstant()->toInt32() >= 0) {
4035 setCanBeNegativeZero(false);
4036 }
4037 }
4038}
4039
4040void MDiv::analyzeEdgeCasesBackward() {
4041 // In general, canBeNegativeZero_ is only valid for integer divides.
4042 // It's fine to access here because we're only using it to avoid
4043 // wasting effort to decide whether we can clear an already cleared
4044 // flag.
4045 if (canBeNegativeZero_ && !NeedNegativeZeroCheck(this)) {
4046 setCanBeNegativeZero(false);
4047 }
4048}
4049
4050bool MDiv::fallible() const { return !isTruncated(); }
4051
4052MDefinition* MMod::foldsTo(TempAllocator& alloc) {
4053 MOZ_ASSERT(IsNumberType(type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsNumberType(type()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsNumberType(type())))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("IsNumberType(type())"
, "./../../../../js/src/jit/MIR.cpp", 4053); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsNumberType(type())" ")"); do { MOZ_CrashSequence
(__null, 4053); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4054 MOZ_ASSERT(type() != MIRType::IntPtr, "not yet implemented")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type() != MIRType::IntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type() != MIRType::IntPtr)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("type() != MIRType::IntPtr"
" (" "not yet implemented" ")", "./../../../../js/src/jit/MIR.cpp"
, 4054); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type() != MIRType::IntPtr"
") (" "not yet implemented" ")"); do { MOZ_CrashSequence(__null
, 4054); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
4055
4056 if (type() == MIRType::Int64) {
4057 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) {
4058 return folded;
4059 }
4060 } else {
4061 if (MDefinition* folded = EvaluateConstantOperands(alloc, this)) {
4062 return folded;
4063 }
4064 }
4065 return this;
4066}
4067
4068void MMod::analyzeEdgeCasesForward() {
4069 // These optimizations make sense only for integer division
4070 if (type() != MIRType::Int32) {
4071 return;
4072 }
4073
4074 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) {
4075 canBeDivideByZero_ = false;
4076 }
4077
4078 if (rhs()->isConstant()) {
4079 int32_t n = rhs()->toConstant()->toInt32();
4080 if (n > 0 && !std::has_single_bit(uint32_t(n))) {
4081 canBePowerOfTwoDivisor_ = false;
4082 }
4083 }
4084}
4085
4086bool MMod::fallible() const {
4087 return !isTruncated() &&
4088 (isUnsigned() || canBeDivideByZero() || canBeNegativeDividend());
4089}
4090
4091void MMathFunction::trySpecializeFloat32(TempAllocator& alloc) {
4092 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
4093 setResultType(MIRType::Float32);
4094 specialization_ = MIRType::Float32;
4095 }
4096}
4097
4098bool MMathFunction::isFloat32Commutative() const {
4099 switch (function_) {
4100 case UnaryMathFunction::Floor:
4101 case UnaryMathFunction::Ceil:
4102 case UnaryMathFunction::Round:
4103 case UnaryMathFunction::Trunc:
4104 return true;
4105 default:
4106 return false;
4107 }
4108}
4109
4110MHypot* MHypot::New(TempAllocator& alloc, const MDefinitionVector& vector) {
4111 uint32_t length = vector.length();
4112 MHypot* hypot = new (alloc) MHypot;
4113 if (!hypot->init(alloc, length)) {
4114 return nullptr;
4115 }
4116
4117 for (uint32_t i = 0; i < length; ++i) {
4118 hypot->initOperand(i, vector[i]);
4119 }
4120 return hypot;
4121}
4122
4123bool MAdd::fallible() const {
4124 // the add is fallible if range analysis does not say that it is finite, AND
4125 // either the truncation analysis shows that there are non-truncated uses.
4126 if (truncateKind() >= TruncateKind::IndirectTruncate) {
4127 return false;
4128 }
4129 if (range() && range()->hasInt32Bounds()) {
4130 return false;
4131 }
4132 return true;
4133}
4134
4135bool MSub::fallible() const {
4136 // see comment in MAdd::fallible()
4137 if (truncateKind() >= TruncateKind::IndirectTruncate) {
4138 return false;
4139 }
4140 if (range() && range()->hasInt32Bounds()) {
4141 return false;
4142 }
4143 return true;
4144}
4145
4146MDefinition* MSub::foldsTo(TempAllocator& alloc) {
4147 MDefinition* out = MBinaryArithInstruction::foldsTo(alloc);
4148 if (out != this) {
4149 return out;
4150 }
4151
4152 // Optimize X - X to 0. This optimization is only valid for integer values.
4153 // Subtracting a floating point value from itself returns NaN when the operand
4154 // is either Infinity or NaN.
4155 if (lhs() == rhs()) {
4156 switch (type()) {
4157 case MIRType::Int32:
4158 // Ensure that any bailouts that we depend on to guarantee that X
4159 // is Int32 are not removed.
4160 lhs()->setGuardRangeBailoutsUnchecked();
4161 return MConstant::NewInt32(alloc, 0);
4162 case MIRType::Int64:
4163 return MConstant::NewInt64(alloc, 0);
4164 case MIRType::IntPtr:
4165 return MConstant::NewIntPtr(alloc, 0);
4166 default:
4167 MOZ_ASSERT(IsFloatingPointType(type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsFloatingPointType(type()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsFloatingPointType(type()))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsFloatingPointType(type())"
, "./../../../../js/src/jit/MIR.cpp", 4167); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsFloatingPointType(type())" ")"); do { MOZ_CrashSequence
(__null, 4167); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4168 }
4169 }
4170
4171 return this;
4172}
4173
4174MDefinition* MMul::foldsTo(TempAllocator& alloc) {
4175 MDefinition* out = MBinaryArithInstruction::foldsTo(alloc);
4176 if (out != this) {
4177 return out;
4178 }
4179
4180 if (type() != MIRType::Int32) {
4181 return this;
4182 }
4183
4184 if (lhs() == rhs()) {
4185 setCanBeNegativeZero(false);
4186 }
4187
4188 return this;
4189}
4190
4191void MMul::analyzeEdgeCasesForward() {
4192 // Try to remove the check for negative zero
4193 // This only makes sense when using the integer multiplication
4194 if (type() != MIRType::Int32) {
4195 return;
4196 }
4197
4198 // If lhs is > 0, no need for negative zero check.
4199 if (lhs()->isConstant() && lhs()->type() == MIRType::Int32) {
4200 if (lhs()->toConstant()->toInt32() > 0) {
4201 setCanBeNegativeZero(false);
4202 }
4203 }
4204
4205 // If rhs is > 0, likewise.
4206 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32) {
4207 if (rhs()->toConstant()->toInt32() > 0) {
4208 setCanBeNegativeZero(false);
4209 }
4210 }
4211}
4212
4213void MMul::analyzeEdgeCasesBackward() {
4214 if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) {
4215 setCanBeNegativeZero(false);
4216 }
4217}
4218
4219bool MMul::canOverflow() const {
4220 if (isTruncated()) {
4221 return false;
4222 }
4223 return !range() || !range()->hasInt32Bounds();
4224}
4225
4226bool MUrsh::fallible() const {
4227 if (bailoutsDisabled()) {
4228 return false;
4229 }
4230 return !range() || !range()->hasInt32Bounds();
4231}
4232
4233static inline bool MustBeUInt32(MDefinition* def, MDefinition** pwrapped) {
4234 if (def->isUrsh()) {
4235 *pwrapped = def->toUrsh()->lhs();
4236 MDefinition* rhs = def->toUrsh()->rhs();
4237 return def->toUrsh()->bailoutsDisabled() && rhs->maybeConstantValue() &&
4238 rhs->maybeConstantValue()->isInt32(0);
4239 }
4240
4241 if (MConstant* defConst = def->maybeConstantValue()) {
4242 *pwrapped = defConst;
4243 return defConst->type() == MIRType::Int32 && defConst->toInt32() >= 0;
4244 }
4245
4246 *pwrapped = nullptr; // silence GCC warning
4247 return false;
4248}
4249
4250/* static */
4251bool MBinaryInstruction::unsignedOperands(MDefinition* left,
4252 MDefinition* right) {
4253 MDefinition* replace;
4254 if (!MustBeUInt32(left, &replace)) {
4255 return false;
4256 }
4257 if (replace->type() != MIRType::Int32) {
4258 return false;
4259 }
4260 if (!MustBeUInt32(right, &replace)) {
4261 return false;
4262 }
4263 if (replace->type() != MIRType::Int32) {
4264 return false;
4265 }
4266 return true;
4267}
4268
4269bool MBinaryInstruction::unsignedOperands() {
4270 return unsignedOperands(getOperand(0), getOperand(1));
4271}
4272
4273void MBinaryInstruction::replaceWithUnsignedOperands() {
4274 MOZ_ASSERT(unsignedOperands())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(unsignedOperands())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(unsignedOperands()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("unsignedOperands()"
, "./../../../../js/src/jit/MIR.cpp", 4274); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "unsignedOperands()" ")"); do { MOZ_CrashSequence
(__null, 4274); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4275
4276 for (size_t i = 0; i < numOperands(); i++) {
4277 MDefinition* replace;
4278 MustBeUInt32(getOperand(i), &replace);
4279 if (replace == getOperand(i)) {
4280 continue;
4281 }
4282
4283 getOperand(i)->setImplicitlyUsedUnchecked();
4284 replaceOperand(i, replace);
4285 }
4286}
4287
4288MDefinition* MBitNot::foldsTo(TempAllocator& alloc) {
4289 if (type() == MIRType::Int64) {
4290 return this;
4291 }
4292 MOZ_ASSERT(type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type() == MIRType::Int32))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("type() == MIRType::Int32"
, "./../../../../js/src/jit/MIR.cpp", 4292); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Int32" ")"); do { MOZ_CrashSequence
(__null, 4292); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4293
4294 MDefinition* input = getOperand(0);
4295
4296 if (input->isConstant()) {
4297 int32_t v = ~(input->toConstant()->toInt32());
4298 return MConstant::NewInt32(alloc, v);
4299 }
4300
4301 if (input->isBitNot()) {
4302 MOZ_ASSERT(input->toBitNot()->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(input->toBitNot()->type() == MIRType::Int32)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(input->toBitNot()->type() == MIRType::Int32)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("input->toBitNot()->type() == MIRType::Int32"
, "./../../../../js/src/jit/MIR.cpp", 4302); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "input->toBitNot()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 4302); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4303 MOZ_ASSERT(input->toBitNot()->getOperand(0)->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(input->toBitNot()->getOperand(0)->type() ==
MIRType::Int32)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(input->toBitNot()->getOperand
(0)->type() == MIRType::Int32))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("input->toBitNot()->getOperand(0)->type() == MIRType::Int32"
, "./../../../../js/src/jit/MIR.cpp", 4303); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "input->toBitNot()->getOperand(0)->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 4303); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4304 return MTruncateToInt32::New(alloc,
4305 input->toBitNot()->input()); // ~~x => x | 0
4306 }
4307
4308 return this;
4309}
4310
4311static void AssertKnownClass(TempAllocator& alloc, MInstruction* ins,
4312 MDefinition* obj) {
4313#ifdef DEBUG1
4314 const JSClass* clasp = GetObjectKnownJSClass(obj);
4315 MOZ_ASSERT(clasp)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(clasp)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(clasp))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("clasp", "./../../../../js/src/jit/MIR.cpp"
, 4315); AnnotateMozCrashReason("MOZ_ASSERT" "(" "clasp" ")")
; do { MOZ_CrashSequence(__null, 4315); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4316
4317 auto* assert = MAssertClass::New(alloc, obj, clasp);
4318 ins->block()->insertBefore(ins, assert);
4319#endif
4320}
4321
4322MDefinition* MBoxNonStrictThis::foldsTo(TempAllocator& alloc) {
4323 MDefinition* in = input();
4324
4325 // BoxNonStrictThis is a no-op on objects.
4326 if (in->type() == MIRType::Object) {
4327 return in;
4328 }
4329
4330 if (!in->isBox()) {
4331 return this;
4332 }
4333
4334 MDefinition* unboxed = in->toBox()->input();
4335 if (unboxed->type() == MIRType::Object) {
4336 return unboxed;
4337 }
4338
4339 if (unboxed->typeIsOneOf({MIRType::Undefined, MIRType::Null})) {
4340 return MConstant::NewObject(alloc, this->globalThis());
4341 }
4342
4343 return this;
4344}
4345
4346MDefinition* MIdToStringOrSymbol::foldsTo(TempAllocator& alloc) {
4347 if (idVal()->isBox()) {
4348 auto* input = idVal()->toBox()->input();
4349 MIRType idType = input->type();
4350 if (idType == MIRType::String || idType == MIRType::Symbol) {
4351 return idVal();
4352 }
4353 if (idType == MIRType::Int32) {
4354 auto* toString =
4355 MToString::New(alloc, input, MToString::SideEffectHandling::Bailout);
4356 block()->insertBefore(this, toString);
4357
4358 return MBox::New(alloc, toString);
4359 }
4360 }
4361
4362 return this;
4363}
4364
4365MDefinition* MReturnFromCtor::foldsTo(TempAllocator& alloc) {
4366 MDefinition* rval = value();
4367 if (!rval->isBox()) {
4368 return this;
4369 }
4370
4371 MDefinition* unboxed = rval->toBox()->input();
4372 if (unboxed->type() == MIRType::Object) {
4373 return unboxed;
4374 }
4375
4376 return object();
4377}
4378
4379MDefinition* MTypeOf::foldsTo(TempAllocator& alloc) {
4380 MDefinition* unboxed = input();
4381 if (unboxed->isBox()) {
4382 unboxed = unboxed->toBox()->input();
4383 }
4384
4385 JSType type;
4386 switch (unboxed->type()) {
4387 case MIRType::Double:
4388 case MIRType::Float32:
4389 case MIRType::Int32:
4390 type = JSTYPE_NUMBER;
4391 break;
4392 case MIRType::String:
4393 type = JSTYPE_STRING;
4394 break;
4395 case MIRType::Symbol:
4396 type = JSTYPE_SYMBOL;
4397 break;
4398 case MIRType::BigInt:
4399 type = JSTYPE_BIGINT;
4400 break;
4401 case MIRType::Null:
4402 type = JSTYPE_OBJECT;
4403 break;
4404 case MIRType::Undefined:
4405 type = JSTYPE_UNDEFINED;
4406 break;
4407 case MIRType::Boolean:
4408 type = JSTYPE_BOOLEAN;
4409 break;
4410 case MIRType::Object: {
4411 KnownClass known = GetObjectKnownClass(unboxed);
4412 if (known != KnownClass::None) {
4413 if (known == KnownClass::Function) {
4414 type = JSTYPE_FUNCTION;
4415 } else {
4416 type = JSTYPE_OBJECT;
4417 }
4418
4419 AssertKnownClass(alloc, this, unboxed);
4420 break;
4421 }
4422 [[fallthrough]];
4423 }
4424 default:
4425 return this;
4426 }
4427
4428 return MConstant::NewInt32(alloc, static_cast<int32_t>(type));
4429}
4430
4431MDefinition* MTypeOfName::foldsTo(TempAllocator& alloc) {
4432 MOZ_ASSERT(input()->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(input()->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(input()->type() == MIRType
::Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("input()->type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 4432); AnnotateMozCrashReason("MOZ_ASSERT" "(" "input()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 4432); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4433
4434 if (!input()->isConstant()) {
4435 return this;
4436 }
4437
4438 static_assert(JSTYPE_UNDEFINED == 0);
4439
4440 int32_t type = input()->toConstant()->toInt32();
4441 MOZ_ASSERT(JSTYPE_UNDEFINED <= type && type < JSTYPE_LIMIT)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(JSTYPE_UNDEFINED <= type && type < JSTYPE_LIMIT
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(JSTYPE_UNDEFINED <= type && type < JSTYPE_LIMIT
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"JSTYPE_UNDEFINED <= type && type < JSTYPE_LIMIT"
, "./../../../../js/src/jit/MIR.cpp", 4441); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "JSTYPE_UNDEFINED <= type && type < JSTYPE_LIMIT"
")"); do { MOZ_CrashSequence(__null, 4441); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4442
4443 JSString* name =
4444 TypeName(static_cast<JSType>(type), GetJitContext()->runtime->names());
4445 return MConstant::NewString(alloc, name);
4446}
4447
4448MUrsh* MUrsh::NewWasm(TempAllocator& alloc, MDefinition* left,
4449 MDefinition* right, MIRType type) {
4450 MUrsh* ins = new (alloc) MUrsh(left, right, type);
4451
4452 // Since Ion has no UInt32 type, we use Int32 and we have a special
4453 // exception to the type rules: we can return values in
4454 // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type
4455 // without bailing out. This is necessary because Ion has no UInt32
4456 // type and we can't have bailouts in wasm code.
4457 ins->bailoutsDisabled_ = true;
4458
4459 return ins;
4460}
4461
4462MResumePoint* MResumePoint::New(TempAllocator& alloc, MBasicBlock* block,
4463 jsbytecode* pc, ResumeMode mode) {
4464 MResumePoint* resume = new (alloc) MResumePoint(block, pc, mode);
4465 if (!resume->init(alloc)) {
4466 block->discardPreAllocatedResumePoint(resume);
4467 return nullptr;
4468 }
4469 resume->inherit(block);
4470 return resume;
4471}
4472
4473MResumePoint* MResumePoint::clone(TempAllocator& alloc) {
4474 MResumePoint* resume = new (alloc) MResumePoint(block(), pc_, mode_);
4475 size_t n = this->numOperands();
4476 if (!resume->operands_.init(alloc, n)) {
4477 return nullptr;
4478 }
4479 for (size_t i = 0; i < n; i++) {
4480 resume->initOperand(i, getOperand(i));
4481 }
4482 resume->stores_.copy(this->stores_);
4483 return resume;
4484}
4485
4486MResumePoint::MResumePoint(MBasicBlock* block, jsbytecode* pc, ResumeMode mode)
4487 : MNode(block, Kind::ResumePoint),
4488 pc_(pc),
4489 instruction_(nullptr),
4490 mode_(mode) {
4491 block->addResumePoint(this);
4492}
4493
4494bool MResumePoint::init(TempAllocator& alloc) {
4495 return operands_.init(alloc, block()->stackDepth());
4496}
4497
4498MResumePoint* MResumePoint::caller() const {
4499 return block()->callerResumePoint();
4500}
4501
4502void MResumePoint::inherit(MBasicBlock* block) {
4503 // FixedList doesn't initialize its elements, so do unchecked inits.
4504 for (size_t i = 0; i < stackDepth(); i++) {
4505 initOperand(i, block->getSlot(i));
4506 }
4507}
4508
4509void MResumePoint::addStore(TempAllocator& alloc, MDefinition* store,
4510 const MResumePoint* cache) {
4511 MOZ_ASSERT(block()->outerResumePoint() != this)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(block()->outerResumePoint() != this)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(block()->outerResumePoint() != this))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("block()->outerResumePoint() != this"
, "./../../../../js/src/jit/MIR.cpp", 4511); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "block()->outerResumePoint() != this" ")"
); do { MOZ_CrashSequence(__null, 4511); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4512 MOZ_ASSERT_IF(cache, !cache->stores_.empty())do { if (cache) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(!cache->stores_.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!cache->stores_.empty()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!cache->stores_.empty()"
, "./../../../../js/src/jit/MIR.cpp", 4512); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!cache->stores_.empty()" ")"); do { MOZ_CrashSequence
(__null, 4512); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
4513
4514 if (cache && cache->stores_.begin()->operand == store) {
4515 // If the last resume point had the same side-effect stack, then we can
4516 // reuse the current side effect without cloning it. This is a simple
4517 // way to share common context by making a spaghetti stack.
4518 if (++cache->stores_.begin() == stores_.begin()) {
4519 stores_.copy(cache->stores_);
4520 return;
4521 }
4522 }
4523
4524 // Ensure that the store would not be deleted by DCE.
4525 MOZ_ASSERT(store->isEffectful())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(store->isEffectful())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(store->isEffectful()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("store->isEffectful()"
, "./../../../../js/src/jit/MIR.cpp", 4525); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "store->isEffectful()" ")"); do { MOZ_CrashSequence
(__null, 4525); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4526
4527 MStoreToRecover* top = new (alloc) MStoreToRecover(store);
4528 stores_.push(top);
4529}
4530
4531#ifdef JS_JITSPEW1
4532void MResumePoint::dump(GenericPrinter& out) const {
4533 out.printf("resumepoint mode=");
4534
4535 switch (mode()) {
4536 case ResumeMode::ResumeAt:
4537 if (instruction_) {
4538 out.printf("ResumeAt(%u)", instruction_->id());
4539 } else {
4540 out.printf("ResumeAt");
4541 }
4542 break;
4543 default:
4544 out.put(ResumeModeToString(mode()));
4545 break;
4546 }
4547
4548 if (MResumePoint* c = caller()) {
4549 out.printf(" (caller in block%u)", c->block()->id());
4550 }
4551
4552 for (size_t i = 0; i < numOperands(); i++) {
4553 out.printf(" ");
4554 if (operands_[i].hasProducer()) {
4555 getOperand(i)->printName(out);
4556 } else {
4557 out.printf("(null)");
4558 }
4559 }
4560 out.printf("\n");
4561}
4562
4563void MResumePoint::dump() const {
4564 Fprinter out(stderrstderr);
4565 dump(out);
4566 out.finish();
4567}
4568#endif
4569
4570bool MResumePoint::isObservableOperand(MUse* u) const {
4571 return isObservableOperand(indexOf(u));
4572}
4573
4574bool MResumePoint::isObservableOperand(size_t index) const {
4575 return block()->info().isObservableSlot(index);
4576}
4577
4578bool MResumePoint::isRecoverableOperand(MUse* u) const {
4579 return block()->info().isRecoverableOperand(indexOf(u));
4580}
4581
4582MDefinition* MBigIntToIntPtr::foldsTo(TempAllocator& alloc) {
4583 MDefinition* def = input();
4584
4585 // If the operand converts an IntPtr to BigInt, drop both conversions.
4586 if (def->isIntPtrToBigInt()) {
4587 return def->toIntPtrToBigInt()->input();
4588 }
4589
4590 // Fold this operation if the input operand is constant.
4591 if (def->isConstant()) {
4592 BigInt* bigInt = def->toConstant()->toBigInt();
4593 intptr_t i;
4594 if (BigInt::isIntPtr(bigInt, &i)) {
4595 return MConstant::NewIntPtr(alloc, i);
4596 }
4597 }
4598
4599 // Fold BigIntToIntPtr(Int64ToBigInt(int64)) to Int64ToIntPtr(int64)
4600 if (def->isInt64ToBigInt()) {
4601 auto* toBigInt = def->toInt64ToBigInt();
4602 return MInt64ToIntPtr::New(alloc, toBigInt->input(), toBigInt->isSigned());
4603 }
4604
4605 return this;
4606}
4607
4608MDefinition* MIntPtrToBigInt::foldsTo(TempAllocator& alloc) {
4609 MDefinition* def = input();
4610
4611 // If the operand converts a BigInt to IntPtr, drop both conversions.
4612 if (def->isBigIntToIntPtr()) {
4613 return def->toBigIntToIntPtr()->input();
4614 }
4615
4616 return this;
4617}
4618
4619MDefinition* MTruncateBigIntToInt64::foldsTo(TempAllocator& alloc) {
4620 MDefinition* input = this->input();
4621 MOZ_ASSERT(input->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(input->type() == MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(input->type() == MIRType::
BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("input->type() == MIRType::BigInt", "./../../../../js/src/jit/MIR.cpp"
, 4621); AnnotateMozCrashReason("MOZ_ASSERT" "(" "input->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 4621); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4622
4623 // If the operand converts an I64 to BigInt, drop both conversions.
4624 if (input->isInt64ToBigInt()) {
4625 return input->toInt64ToBigInt()->input();
4626 }
4627
4628 // If the operand is an IntPtr, extend the IntPtr to I64.
4629 if (input->isIntPtrToBigInt()) {
4630 auto* intPtr = input->toIntPtrToBigInt()->input();
4631 if (intPtr->isConstant()) {
4632 intptr_t c = intPtr->toConstant()->toIntPtr();
4633 return MConstant::NewInt64(alloc, int64_t(c));
4634 }
4635 return MIntPtrToInt64::New(alloc, intPtr);
4636 }
4637
4638 // Fold this operation if the input operand is constant.
4639 if (input->isConstant()) {
4640 return MConstant::NewInt64(
4641 alloc, BigInt::toInt64(input->toConstant()->toBigInt()));
4642 }
4643
4644 return this;
4645}
4646
4647MDefinition* MToInt64::foldsTo(TempAllocator& alloc) {
4648 MDefinition* input = getOperand(0);
4649
4650 if (input->isBox()) {
4651 input = input->getOperand(0);
4652 }
4653
4654 // Unwrap MInt64ToBigInt: MToInt64(MInt64ToBigInt(int64)) = int64.
4655 if (input->isInt64ToBigInt()) {
4656 return input->getOperand(0);
4657 }
4658
4659 // Unwrap IntPtrToBigInt:
4660 // MToInt64(MIntPtrToBigInt(intptr)) = MIntPtrToInt64(intptr).
4661 if (input->isIntPtrToBigInt()) {
4662 auto* intPtr = input->toIntPtrToBigInt()->input();
4663 if (intPtr->isConstant()) {
4664 intptr_t c = intPtr->toConstant()->toIntPtr();
4665 return MConstant::NewInt64(alloc, int64_t(c));
4666 }
4667 return MIntPtrToInt64::New(alloc, intPtr);
4668 }
4669
4670 // When the input is an Int64 already, just return it.
4671 if (input->type() == MIRType::Int64) {
4672 return input;
4673 }
4674
4675 // Fold this operation if the input operand is constant.
4676 if (input->isConstant()) {
4677 switch (input->type()) {
4678 case MIRType::Boolean:
4679 return MConstant::NewInt64(alloc, input->toConstant()->toBoolean());
4680 default:
4681 break;
4682 }
4683 }
4684
4685 return this;
4686}
4687
4688MDefinition* MToNumberInt32::foldsTo(TempAllocator& alloc) {
4689 // Fold this operation if the input operand is constant.
4690 if (MConstant* cst = input()->maybeConstantValue()) {
4691 switch (cst->type()) {
4692 case MIRType::Null:
4693 if (conversion() == IntConversionInputKind::Any) {
4694 return MConstant::NewInt32(alloc, 0);
4695 }
4696 break;
4697 case MIRType::Boolean:
4698 if (conversion() == IntConversionInputKind::Any) {
4699 return MConstant::NewInt32(alloc, cst->toBoolean());
4700 }
4701 break;
4702 case MIRType::Int32:
4703 return MConstant::NewInt32(alloc, cst->toInt32());
4704 case MIRType::Float32:
4705 case MIRType::Double:
4706 int32_t ival;
4707 // Only the value within the range of Int32 can be substituted as
4708 // constant.
4709 if (mozilla::NumberIsInt32(cst->numberToDouble(), &ival)) {
4710 return MConstant::NewInt32(alloc, ival);
4711 }
4712 break;
4713 default:
4714 break;
4715 }
4716 }
4717
4718 MDefinition* input = getOperand(0);
4719 if (input->isBox()) {
4720 input = input->toBox()->input();
4721 }
4722
4723 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
4724 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
4725 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
4726 // is folded to a MTruncateToInt32 node, which will result in this MIR:
4727 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
4728 // the MUrsh node's type is int32 (since uint32 is not implemented), and
4729 // that would fold the MTruncateToInt32 node. This will make the modulo
4730 // unsigned, while is should have been signed.
4731 if (input->type() == MIRType::Int32 && !IsUint32Type(input)) {
4732 return input;
4733 }
4734
4735 return this;
4736}
4737
4738MDefinition* MBooleanToInt32::foldsTo(TempAllocator& alloc) {
4739 MDefinition* input = getOperand(0);
4740 MOZ_ASSERT(input->type() == MIRType::Boolean)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(input->type() == MIRType::Boolean)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(input->type() == MIRType::
Boolean))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("input->type() == MIRType::Boolean", "./../../../../js/src/jit/MIR.cpp"
, 4740); AnnotateMozCrashReason("MOZ_ASSERT" "(" "input->type() == MIRType::Boolean"
")"); do { MOZ_CrashSequence(__null, 4740); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4741
4742 if (input->isConstant()) {
4743 return MConstant::NewInt32(alloc, input->toConstant()->toBoolean());
4744 }
4745
4746 return this;
4747}
4748
4749void MToNumberInt32::analyzeEdgeCasesBackward() {
4750 if (!NeedNegativeZeroCheck(this)) {
4751 setNeedsNegativeZeroCheck(false);
4752 }
4753}
4754
4755MDefinition* MTruncateToInt32::foldsTo(TempAllocator& alloc) {
4756 MDefinition* input = getOperand(0);
4757 if (input->isBox()) {
4758 input = input->getOperand(0);
4759 }
4760
4761 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
4762 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
4763 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
4764 // is folded to a MTruncateToInt32 node, which will result in this MIR:
4765 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
4766 // the MUrsh node's type is int32 (since uint32 is not implemented), and
4767 // that would fold the MTruncateToInt32 node. This will make the modulo
4768 // unsigned, while is should have been signed.
4769 if (input->type() == MIRType::Int32 && !IsUint32Type(input)) {
4770 return input;
4771 }
4772
4773 if (input->type() == MIRType::Double && input->isConstant()) {
4774 int32_t ret = ToInt32(input->toConstant()->toDouble());
4775 return MConstant::NewInt32(alloc, ret);
4776 }
4777
4778 return this;
4779}
4780
4781MDefinition* MWrapInt64ToInt32::foldsTo(TempAllocator& alloc) {
4782 MDefinition* input = this->input();
4783 if (input->isConstant()) {
4784 uint64_t c = input->toConstant()->toInt64();
4785 int32_t output = bottomHalf() ? int32_t(c) : int32_t(c >> 32);
4786 return MConstant::NewInt32(alloc, output);
4787 }
4788
4789 return this;
4790}
4791
4792MDefinition* MExtendInt32ToInt64::foldsTo(TempAllocator& alloc) {
4793 MDefinition* input = this->input();
4794 if (input->isConstant()) {
4795 int32_t c = input->toConstant()->toInt32();
4796 int64_t res = isUnsigned() ? int64_t(uint32_t(c)) : int64_t(c);
4797 return MConstant::NewInt64(alloc, res);
4798 }
4799
4800 return this;
4801}
4802
4803MDefinition* MSignExtendInt32::foldsTo(TempAllocator& alloc) {
4804 MDefinition* input = this->input();
4805 if (input->isConstant()) {
4806 int32_t c = input->toConstant()->toInt32();
4807 int32_t res;
4808 switch (mode_) {
4809 case Byte:
4810 res = int32_t(int8_t(c & 0xFF));
4811 break;
4812 case Half:
4813 res = int32_t(int16_t(c & 0xFFFF));
4814 break;
4815 }
4816 return MConstant::NewInt32(alloc, res);
4817 }
4818
4819 return this;
4820}
4821
4822MDefinition* MSignExtendInt64::foldsTo(TempAllocator& alloc) {
4823 MDefinition* input = this->input();
4824 if (input->isConstant()) {
4825 int64_t c = input->toConstant()->toInt64();
4826 int64_t res;
4827 switch (mode_) {
4828 case Byte:
4829 res = int64_t(int8_t(c & 0xFF));
4830 break;
4831 case Half:
4832 res = int64_t(int16_t(c & 0xFFFF));
4833 break;
4834 case Word:
4835 res = int64_t(int32_t(c & 0xFFFFFFFFU));
4836 break;
4837 }
4838 return MConstant::NewInt64(alloc, res);
4839 }
4840
4841 return this;
4842}
4843
4844MDefinition* MSignExtendIntPtr::foldsTo(TempAllocator& alloc) {
4845 MDefinition* input = this->input();
4846 if (input->isConstant()) {
4847 intptr_t c = input->toConstant()->toIntPtr();
4848 intptr_t res;
4849 switch (mode_) {
4850 case Byte:
4851 res = intptr_t(int8_t(c & 0xFF));
4852 break;
4853 case Half:
4854 res = intptr_t(int16_t(c & 0xFFFF));
4855 break;
4856 case Word:
4857 res = intptr_t(int32_t(c & 0xFFFFFFFFU));
4858 break;
4859 }
4860 return MConstant::NewIntPtr(alloc, res);
4861 }
4862
4863 return this;
4864}
4865
4866MDefinition* MToDouble::foldsTo(TempAllocator& alloc) {
4867 MDefinition* input = getOperand(0);
4868 if (input->isBox()) {
4869 input = input->getOperand(0);
4870 }
4871
4872 if (input->type() == MIRType::Double) {
4873 return input;
4874 }
4875
4876 if (input->isConstant() &&
4877 input->toConstant()->isTypeRepresentableAsDouble()) {
4878 return MConstant::NewDouble(alloc, input->toConstant()->numberToDouble());
4879 }
4880
4881 return this;
4882}
4883
4884MDefinition* MToFloat32::foldsTo(TempAllocator& alloc) {
4885 MDefinition* input = getOperand(0);
4886 if (input->isBox()) {
4887 input = input->getOperand(0);
4888 }
4889
4890 if (input->type() == MIRType::Float32) {
4891 return input;
4892 }
4893
4894 // If x is a Float32, Float32(Double(x)) == x
4895 if (!mustPreserveNaN_ && input->isToDouble() &&
4896 input->toToDouble()->input()->type() == MIRType::Float32) {
4897 return input->toToDouble()->input();
4898 }
4899
4900 if (input->isConstant() &&
4901 input->toConstant()->isTypeRepresentableAsDouble()) {
4902 return MConstant::NewFloat32(alloc,
4903 float(input->toConstant()->numberToDouble()));
4904 }
4905
4906 // Fold ToFloat32(ToDouble(int32)) to ToFloat32(int32).
4907 if (input->isToDouble() &&
4908 input->toToDouble()->input()->type() == MIRType::Int32) {
4909 return MToFloat32::New(alloc, input->toToDouble()->input());
4910 }
4911
4912 return this;
4913}
4914
4915MDefinition* MToFloat16::foldsTo(TempAllocator& alloc) {
4916 MDefinition* in = input();
4917 if (in->isBox()) {
4918 in = in->toBox()->input();
4919 }
4920
4921 if (in->isConstant()) {
4922 auto* cst = in->toConstant();
4923 if (cst->isTypeRepresentableAsDouble()) {
4924 double num = cst->numberToDouble();
4925 return MConstant::NewFloat32(alloc, static_cast<float>(js::float16{num}));
4926 }
4927 }
4928
4929 auto isFloat16 = [](auto* def) -> MDefinition* {
4930 // ToFloat16(ToDouble(float16)) => float16
4931 // ToFloat16(ToFloat32(float16)) => float16
4932 if (def->isToDouble()) {
4933 def = def->toToDouble()->input();
4934 } else if (def->isToFloat32()) {
4935 def = def->toToFloat32()->input();
4936 }
4937
4938 // ToFloat16(ToFloat16(x)) => ToFloat16(x)
4939 if (def->isToFloat16()) {
4940 return def;
4941 }
4942
4943 // Unwrap CanonicalizeNaN added after load instructions.
4944 MDefinition* load = def;
4945 if (load->isCanonicalizeNaN()) {
4946 load = load->toCanonicalizeNaN()->input();
4947 }
4948
4949 // ToFloat16(LoadFloat16(x)) => LoadFloat16(x)
4950 if (load->isLoadUnboxedScalar() &&
4951 load->toLoadUnboxedScalar()->storageType() == Scalar::Float16) {
4952 return def;
4953 }
4954 if (load->isLoadDataViewElement() &&
4955 load->toLoadDataViewElement()->storageType() == Scalar::Float16) {
4956 return def;
4957 }
4958 return nullptr;
4959 };
4960
4961 // Fold loads which are guaranteed to return Float16.
4962 if (auto* f16 = isFloat16(in)) {
4963 return f16;
4964 }
4965
4966 // Fold ToFloat16(ToDouble(float32)) to ToFloat16(float32).
4967 // Fold ToFloat16(ToDouble(int32)) to ToFloat16(int32).
4968 if (in->isToDouble()) {
4969 auto* toDoubleInput = in->toToDouble()->input();
4970 if (toDoubleInput->type() == MIRType::Float32 ||
4971 toDoubleInput->type() == MIRType::Int32) {
4972 return MToFloat16::New(alloc, toDoubleInput);
4973 }
4974 }
4975
4976 return this;
4977}
4978
4979MDefinition* MUnsignedToDouble::foldsTo(TempAllocator& alloc) {
4980 if (input()->isConstant()) {
4981 return MConstant::NewDouble(alloc,
4982 uint32_t(input()->toConstant()->toInt32()));
4983 }
4984
4985 return this;
4986}
4987
4988MDefinition* MUnsignedToFloat32::foldsTo(TempAllocator& alloc) {
4989 if (input()->isConstant()) {
4990 double dval = double(uint32_t(input()->toConstant()->toInt32()));
4991 if (IsFloat32Representable(dval)) {
4992 return MConstant::NewFloat32(alloc, float(dval));
4993 }
4994 }
4995
4996 return this;
4997}
4998
4999MDefinition* MToString::foldsTo(TempAllocator& alloc) {
5000 MDefinition* in = input();
5001 if (in->isBox()) {
5002 in = in->getOperand(0);
5003 }
5004
5005 if (in->type() == MIRType::String) {
5006 return in;
5007 }
5008 return this;
5009}
5010
5011MDefinition* MClampToUint8::foldsTo(TempAllocator& alloc) {
5012 if (MConstant* inputConst = input()->maybeConstantValue()) {
5013 if (inputConst->isTypeRepresentableAsDouble()) {
5014 int32_t clamped = ClampDoubleToUint8(inputConst->numberToDouble());
5015 return MConstant::NewInt32(alloc, clamped);
5016 }
5017 }
5018 return this;
5019}
5020
5021bool MCompare::tryFoldEqualOperands(bool* result) {
5022 if (lhs() != rhs()) {
5023 return false;
5024 }
5025
5026 // Intuitively somebody would think that if lhs === rhs,
5027 // then we can just return true. (Or false for !==)
5028 // However NaN !== NaN is true! So we spend some time trying
5029 // to eliminate this case.
5030
5031 if (!IsEqualityOp(jsop())) {
5032 return false;
5033 }
5034
5035 switch (compareType_) {
5036 case Compare_Int32:
5037 case Compare_UInt32:
5038 case Compare_Int64:
5039 case Compare_UInt64:
5040 case Compare_IntPtr:
5041 case Compare_UIntPtr:
5042 case Compare_Float32:
5043 case Compare_Double:
5044 case Compare_String:
5045 case Compare_Object:
5046 case Compare_Symbol:
5047 case Compare_BigInt:
5048 case Compare_WasmAnyRef:
5049 case Compare_Null:
5050 case Compare_Undefined:
5051 break;
5052 case Compare_BigInt_Int32:
5053 case Compare_BigInt_String:
5054 case Compare_BigInt_Double:
5055 MOZ_CRASH("Expecting different operands for lhs and rhs")do { do { } while (false); MOZ_ReportCrash("" "Expecting different operands for lhs and rhs"
, "./../../../../js/src/jit/MIR.cpp", 5055); AnnotateMozCrashReason
("MOZ_CRASH(" "Expecting different operands for lhs and rhs" ")"
); do { MOZ_CrashSequence(__null, 5055); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
5056 }
5057
5058 if (isDoubleComparison() || isFloat32Comparison()) {
5059 if (!operandsAreNeverNaN()) {
5060 return false;
5061 }
5062 } else {
5063 MOZ_ASSERT(!IsFloatingPointType(lhs()->type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsFloatingPointType(lhs()->type()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!IsFloatingPointType(lhs()->type())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!IsFloatingPointType(lhs()->type())"
, "./../../../../js/src/jit/MIR.cpp", 5063); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsFloatingPointType(lhs()->type())" ")"
); do { MOZ_CrashSequence(__null, 5063); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5064 }
5065
5066 lhs()->setGuardRangeBailoutsUnchecked();
5067
5068 *result = (jsop() == JSOp::StrictEq || jsop() == JSOp::Eq);
5069 return true;
5070}
5071
5072static JSType TypeOfName(const JSOffThreadAtom* str) {
5073 static constexpr std::array types = {
5074 JSTYPE_UNDEFINED, JSTYPE_OBJECT, JSTYPE_FUNCTION, JSTYPE_STRING,
5075 JSTYPE_NUMBER, JSTYPE_BOOLEAN, JSTYPE_SYMBOL, JSTYPE_BIGINT,
5076 };
5077 static_assert(types.size() == JSTYPE_LIMIT);
5078
5079 const JSAtomState& names = GetJitContext()->runtime->names();
5080 for (auto type : types) {
5081 // Both sides are atoms, so we can simply compare pointer identity.
5082 if (TypeName(type, names) == str->unwrap()) {
5083 return type;
5084 }
5085 }
5086 return JSTYPE_LIMIT;
5087}
5088
5089struct TypeOfCompareInput {
5090 // The `typeof expr` side of the comparison.
5091 // MTypeOfName for JSOp::Typeof/JSOp::TypeofExpr, and
5092 // MTypeOf for JSOp::TypeofEq (same pointer as typeOf).
5093 MDefinition* typeOfSide;
5094
5095 // The actual `typeof` operation.
5096 MTypeOf* typeOf;
5097
5098 // The string side of the comparison.
5099 JSType type;
5100
5101 // True if the comparison uses raw JSType (Generated for JSOp::TypeofEq).
5102 bool isIntComparison;
5103
5104 TypeOfCompareInput(MDefinition* typeOfSide, MTypeOf* typeOf, JSType type,
5105 bool isIntComparison)
5106 : typeOfSide(typeOfSide),
5107 typeOf(typeOf),
5108 type(type),
5109 isIntComparison(isIntComparison) {}
5110};
5111
5112static mozilla::Maybe<TypeOfCompareInput> IsTypeOfCompare(MCompare* ins) {
5113 if (!IsEqualityOp(ins->jsop())) {
5114 return mozilla::Nothing();
5115 }
5116
5117 if (ins->compareType() == MCompare::Compare_Int32) {
5118 auto* lhs = ins->lhs();
5119 auto* rhs = ins->rhs();
5120
5121 // NOTE: The comparison is generated inside JIT, and typeof should always
5122 // be in the LHS.
5123 if (!lhs->isTypeOf() || !rhs->isConstant()) {
5124 return mozilla::Nothing();
5125 }
5126
5127 MOZ_ASSERT(ins->type() == MIRType::Boolean)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->type() == MIRType::Boolean)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->type() == MIRType::Boolean
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->type() == MIRType::Boolean", "./../../../../js/src/jit/MIR.cpp"
, 5127); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::Boolean"
")"); do { MOZ_CrashSequence(__null, 5127); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5128 MOZ_ASSERT(lhs->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lhs->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lhs->type() == MIRType::Int32
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lhs->type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 5128); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 5128); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5129 MOZ_ASSERT(rhs->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(rhs->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(rhs->type() == MIRType::Int32
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"rhs->type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 5129); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rhs->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 5129); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5130
5131 auto* typeOf = lhs->toTypeOf();
5132 auto* constant = rhs->toConstant();
5133
5134 JSType type = JSType(constant->toInt32());
5135 return mozilla::Some(TypeOfCompareInput(typeOf, typeOf, type, true));
5136 }
5137
5138 if (ins->compareType() != MCompare::Compare_String) {
5139 return mozilla::Nothing();
5140 }
5141
5142 auto* lhs = ins->lhs();
5143 auto* rhs = ins->rhs();
5144
5145 MOZ_ASSERT(ins->type() == MIRType::Boolean)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->type() == MIRType::Boolean)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->type() == MIRType::Boolean
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->type() == MIRType::Boolean", "./../../../../js/src/jit/MIR.cpp"
, 5145); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::Boolean"
")"); do { MOZ_CrashSequence(__null, 5145); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5146 MOZ_ASSERT(lhs->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lhs->type() == MIRType::String)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lhs->type() == MIRType::String
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lhs->type() == MIRType::String", "./../../../../js/src/jit/MIR.cpp"
, 5146); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5146); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5147 MOZ_ASSERT(rhs->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(rhs->type() == MIRType::String)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(rhs->type() == MIRType::String
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"rhs->type() == MIRType::String", "./../../../../js/src/jit/MIR.cpp"
, 5147); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rhs->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5147); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5148
5149 if (!lhs->isTypeOfName() && !rhs->isTypeOfName()) {
5150 return mozilla::Nothing();
5151 }
5152 if (!lhs->isConstant() && !rhs->isConstant()) {
5153 return mozilla::Nothing();
5154 }
5155
5156 auto* typeOfName =
5157 lhs->isTypeOfName() ? lhs->toTypeOfName() : rhs->toTypeOfName();
5158 auto* typeOf = typeOfName->input()->toTypeOf();
5159
5160 auto* constant = lhs->isConstant() ? lhs->toConstant() : rhs->toConstant();
5161
5162 JSType type = TypeOfName(constant->toString());
5163 return mozilla::Some(TypeOfCompareInput(typeOfName, typeOf, type, false));
5164}
5165
5166bool MCompare::tryFoldTypeOf(bool* result) {
5167 auto typeOfCompare = IsTypeOfCompare(this);
5168 if (!typeOfCompare) {
5169 return false;
5170 }
5171 auto* typeOf = typeOfCompare->typeOf;
5172 JSType type = typeOfCompare->type;
5173
5174 // Can't fold if the input is boxed. (Unless the typeof string is bogus.)
5175 MIRType inputType = typeOf->input()->type();
5176 if (inputType == MIRType::Value && type != JSTYPE_LIMIT) {
5177 return false;
5178 }
5179
5180 bool matchesInputType;
5181 switch (type) {
5182 case JSTYPE_BOOLEAN:
5183 matchesInputType = (inputType == MIRType::Boolean);
5184 break;
5185 case JSTYPE_NUMBER:
5186 matchesInputType = IsTypeRepresentableAsDouble(inputType);
5187 break;
5188 case JSTYPE_STRING:
5189 matchesInputType = (inputType == MIRType::String);
5190 break;
5191 case JSTYPE_SYMBOL:
5192 matchesInputType = (inputType == MIRType::Symbol);
5193 break;
5194 case JSTYPE_BIGINT:
5195 matchesInputType = (inputType == MIRType::BigInt);
5196 break;
5197 case JSTYPE_OBJECT:
5198 // Watch out for `object-emulating-undefined` and callable objects.
5199 if (inputType == MIRType::Object) {
5200 return false;
5201 }
5202 matchesInputType = (inputType == MIRType::Null);
5203 break;
5204 case JSTYPE_UNDEFINED:
5205 // Watch out for `object-emulating-undefined`.
5206 if (inputType == MIRType::Object) {
5207 return false;
5208 }
5209 matchesInputType = (inputType == MIRType::Undefined);
5210 break;
5211 case JSTYPE_FUNCTION:
5212 // Can't decide at compile-time if an object is callable.
5213 if (inputType == MIRType::Object) {
5214 return false;
5215 }
5216 matchesInputType = false;
5217 break;
5218 case JSTYPE_LIMIT:
5219 matchesInputType = false;
5220 break;
5221 }
5222
5223 if (matchesInputType) {
5224 *result = (jsop() == JSOp::StrictEq || jsop() == JSOp::Eq);
5225 } else {
5226 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
5227 }
5228 return true;
5229}
5230
5231bool MCompare::tryFold(bool* result) {
5232 JSOp op = jsop();
5233
5234 if (tryFoldEqualOperands(result)) {
5235 return true;
5236 }
5237
5238 if (tryFoldTypeOf(result)) {
5239 return true;
5240 }
5241
5242 if (compareType_ == Compare_Null || compareType_ == Compare_Undefined) {
5243 // The LHS is the value we want to test against null or undefined.
5244 if (IsStrictEqualityOp(op)) {
5245 MIRType expectedType =
5246 compareType_ == Compare_Null ? MIRType::Null : MIRType::Undefined;
5247 if (lhs()->type() == expectedType) {
5248 *result = (op == JSOp::StrictEq);
5249 return true;
5250 }
5251 if (lhs()->type() != MIRType::Value) {
5252 *result = (op == JSOp::StrictNe);
5253 return true;
5254 }
5255 } else {
5256 MOZ_ASSERT(IsLooseEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)"
, "./../../../../js/src/jit/MIR.cpp", 5256); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 5256); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5257 if (IsNullOrUndefined(lhs()->type())) {
5258 *result = (op == JSOp::Eq);
5259 return true;
5260 }
5261 if (lhs()->type() != MIRType::Object && lhs()->type() != MIRType::Value) {
5262 *result = (op == JSOp::Ne);
5263 return true;
5264 }
5265 }
5266 return false;
5267 }
5268
5269 return false;
5270}
5271
5272template <typename T>
5273static bool FoldComparison(JSOp op, T left, T right) {
5274 switch (op) {
5275 case JSOp::Lt:
5276 return left < right;
5277 case JSOp::Le:
5278 return left <= right;
5279 case JSOp::Gt:
5280 return left > right;
5281 case JSOp::Ge:
5282 return left >= right;
5283 case JSOp::StrictEq:
5284 case JSOp::Eq:
5285 return left == right;
5286 case JSOp::StrictNe:
5287 case JSOp::Ne:
5288 return left != right;
5289 default:
5290 MOZ_CRASH("Unexpected op.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected op."
, "./../../../../js/src/jit/MIR.cpp", 5290); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected op." ")"); do { MOZ_CrashSequence(__null
, 5290); __attribute__((nomerge)) ::abort(); } while (false);
} while (false)
;
5291 }
5292}
5293
5294static bool FoldBigIntComparison(JSOp op, const BigInt* left, double right) {
5295 switch (op) {
5296 case JSOp::Lt:
5297 return BigInt::lessThan(left, right).valueOr(false);
5298 case JSOp::Le:
5299 return !BigInt::lessThan(right, left).valueOr(true);
5300 case JSOp::Gt:
5301 return BigInt::lessThan(right, left).valueOr(false);
5302 case JSOp::Ge:
5303 return !BigInt::lessThan(left, right).valueOr(true);
5304 case JSOp::StrictEq:
5305 case JSOp::Eq:
5306 return BigInt::equal(left, right);
5307 case JSOp::StrictNe:
5308 case JSOp::Ne:
5309 return !BigInt::equal(left, right);
5310 default:
5311 MOZ_CRASH("Unexpected op.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected op."
, "./../../../../js/src/jit/MIR.cpp", 5311); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected op." ")"); do { MOZ_CrashSequence(__null
, 5311); __attribute__((nomerge)) ::abort(); } while (false);
} while (false)
;
5312 }
5313}
5314
5315bool MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result) {
5316 if (type() != MIRType::Boolean && type() != MIRType::Int32) {
5317 return false;
5318 }
5319
5320 MDefinition* left = getOperand(0);
5321 MDefinition* right = getOperand(1);
5322
5323 if (compareType() == Compare_Double) {
5324 // Optimize "MCompare MConstant (MToDouble SomethingInInt32Range).
5325 // In most cases the MToDouble was added, because the constant is
5326 // a double.
5327 // e.g. v < 9007199254740991, where v is an int32 is always true.
5328 if (!lhs()->isConstant() && !rhs()->isConstant()) {
5329 return false;
5330 }
5331
5332 MDefinition* operand = left->isConstant() ? right : left;
5333 MConstant* constant =
5334 left->isConstant() ? left->toConstant() : right->toConstant();
5335 MOZ_ASSERT(constant->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(constant->type() == MIRType::Double)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(constant->type() == MIRType::Double))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("constant->type() == MIRType::Double"
, "./../../../../js/src/jit/MIR.cpp", 5335); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "constant->type() == MIRType::Double" ")"
); do { MOZ_CrashSequence(__null, 5335); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5336 double cte = constant->toDouble();
5337
5338 if (operand->isToDouble() &&
5339 operand->getOperand(0)->type() == MIRType::Int32) {
5340 bool replaced = false;
5341 switch (jsop_) {
5342 case JSOp::Lt:
5343 if (cte > INT32_MAX(2147483647) || cte < INT32_MIN(-2147483647-1)) {
5344 *result = !((constant == lhs()) ^ (cte < INT32_MIN(-2147483647-1)));
5345 replaced = true;
5346 }
5347 break;
5348 case JSOp::Le:
5349 if (constant == lhs()) {
5350 if (cte > INT32_MAX(2147483647) || cte <= INT32_MIN(-2147483647-1)) {
5351 *result = (cte <= INT32_MIN(-2147483647-1));
5352 replaced = true;
5353 }
5354 } else {
5355 if (cte >= INT32_MAX(2147483647) || cte < INT32_MIN(-2147483647-1)) {
5356 *result = (cte >= INT32_MIN(-2147483647-1));
5357 replaced = true;
5358 }
5359 }
5360 break;
5361 case JSOp::Gt:
5362 if (cte > INT32_MAX(2147483647) || cte < INT32_MIN(-2147483647-1)) {
5363 *result = !((constant == rhs()) ^ (cte < INT32_MIN(-2147483647-1)));
5364 replaced = true;
5365 }
5366 break;
5367 case JSOp::Ge:
5368 if (constant == lhs()) {
5369 if (cte >= INT32_MAX(2147483647) || cte < INT32_MIN(-2147483647-1)) {
5370 *result = (cte >= INT32_MAX(2147483647));
5371 replaced = true;
5372 }
5373 } else {
5374 if (cte > INT32_MAX(2147483647) || cte <= INT32_MIN(-2147483647-1)) {
5375 *result = (cte <= INT32_MIN(-2147483647-1));
5376 replaced = true;
5377 }
5378 }
5379 break;
5380 case JSOp::StrictEq: // Fall through.
5381 case JSOp::Eq:
5382 if (cte > INT32_MAX(2147483647) || cte < INT32_MIN(-2147483647-1)) {
5383 *result = false;
5384 replaced = true;
5385 }
5386 break;
5387 case JSOp::StrictNe: // Fall through.
5388 case JSOp::Ne:
5389 if (cte > INT32_MAX(2147483647) || cte < INT32_MIN(-2147483647-1)) {
5390 *result = true;
5391 replaced = true;
5392 }
5393 break;
5394 default:
5395 MOZ_CRASH("Unexpected op.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected op."
, "./../../../../js/src/jit/MIR.cpp", 5395); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected op." ")"); do { MOZ_CrashSequence(__null
, 5395); __attribute__((nomerge)) ::abort(); } while (false);
} while (false)
;
5396 }
5397 if (replaced) {
5398 MLimitedTruncate* limit = MLimitedTruncate::New(
5399 alloc, operand->getOperand(0), TruncateKind::NoTruncate);
5400 limit->setGuardUnchecked();
5401 block()->insertBefore(this, limit);
5402 return true;
5403 }
5404 }
5405
5406 // Optimize comparison against NaN.
5407 if (std::isnan(cte)) {
5408 switch (jsop_) {
5409 case JSOp::Lt:
5410 case JSOp::Le:
5411 case JSOp::Gt:
5412 case JSOp::Ge:
5413 case JSOp::Eq:
5414 case JSOp::StrictEq:
5415 *result = false;
5416 break;
5417 case JSOp::Ne:
5418 case JSOp::StrictNe:
5419 *result = true;
5420 break;
5421 default:
5422 MOZ_CRASH("Unexpected op.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected op."
, "./../../../../js/src/jit/MIR.cpp", 5422); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected op." ")"); do { MOZ_CrashSequence(__null
, 5422); __attribute__((nomerge)) ::abort(); } while (false);
} while (false)
;
5423 }
5424 return true;
5425 }
5426 }
5427
5428 if (!left->isConstant() || !right->isConstant()) {
5429 return false;
5430 }
5431
5432 MConstant* lhs = left->toConstant();
5433 MConstant* rhs = right->toConstant();
5434
5435 switch (compareType()) {
5436 case Compare_Int32:
5437 case Compare_Double:
5438 case Compare_Float32: {
5439 *result =
5440 FoldComparison(jsop_, lhs->numberToDouble(), rhs->numberToDouble());
5441 return true;
5442 }
5443 case Compare_UInt32: {
5444 *result = FoldComparison(jsop_, uint32_t(lhs->toInt32()),
5445 uint32_t(rhs->toInt32()));
5446 return true;
5447 }
5448 case Compare_Int64: {
5449 *result = FoldComparison(jsop_, lhs->toInt64(), rhs->toInt64());
5450 return true;
5451 }
5452 case Compare_UInt64: {
5453 *result = FoldComparison(jsop_, uint64_t(lhs->toInt64()),
5454 uint64_t(rhs->toInt64()));
5455 return true;
5456 }
5457 case Compare_IntPtr: {
5458 *result = FoldComparison(jsop_, lhs->toIntPtr(), rhs->toIntPtr());
5459 return true;
5460 }
5461 case Compare_UIntPtr: {
5462 *result = FoldComparison(jsop_, uintptr_t(lhs->toIntPtr()),
5463 uintptr_t(rhs->toIntPtr()));
5464 return true;
5465 }
5466 case Compare_String: {
5467 int32_t comp = CompareStrings(lhs->toString(), rhs->toString());
5468 *result = FoldComparison(jsop_, comp, 0);
5469 return true;
5470 }
5471 case Compare_BigInt: {
5472 int32_t comp = BigInt::compare(lhs->toBigInt(), rhs->toBigInt());
5473 *result = FoldComparison(jsop_, comp, 0);
5474 return true;
5475 }
5476 case Compare_BigInt_Int32:
5477 case Compare_BigInt_Double: {
5478 *result =
5479 FoldBigIntComparison(jsop_, lhs->toBigInt(), rhs->numberToDouble());
5480 return true;
5481 }
5482 case Compare_BigInt_String: {
5483 JSOffThreadAtom* str = rhs->toString();
5484 if (!str->hasIndexValue()) {
5485 return false;
5486 }
5487 *result =
5488 FoldBigIntComparison(jsop_, lhs->toBigInt(), str->getIndexValue());
5489 return true;
5490 }
5491
5492 case Compare_Undefined:
5493 case Compare_Null:
5494 case Compare_Symbol:
5495 case Compare_Object:
5496 case Compare_WasmAnyRef:
5497 return false;
5498 }
5499
5500 MOZ_CRASH("unexpected compare type")do { do { } while (false); MOZ_ReportCrash("" "unexpected compare type"
, "./../../../../js/src/jit/MIR.cpp", 5500); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected compare type" ")"); do { MOZ_CrashSequence
(__null, 5500); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
5501}
5502
5503MDefinition* MCompare::tryFoldTypeOf(TempAllocator& alloc) {
5504 auto typeOfCompare = IsTypeOfCompare(this);
5505 if (!typeOfCompare) {
5506 return this;
5507 }
5508 auto* typeOf = typeOfCompare->typeOf;
5509 JSType type = typeOfCompare->type;
5510
5511 auto* input = typeOf->input();
5512 MOZ_ASSERT(input->type() == MIRType::Value ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(input->type() == MIRType::Value || input->type
() == MIRType::Object)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(input->type() == MIRType::
Value || input->type() == MIRType::Object))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("input->type() == MIRType::Value || input->type() == MIRType::Object"
, "./../../../../js/src/jit/MIR.cpp", 5513); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "input->type() == MIRType::Value || input->type() == MIRType::Object"
")"); do { MOZ_CrashSequence(__null, 5513); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
5513 input->type() == MIRType::Object)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(input->type() == MIRType::Value || input->type
() == MIRType::Object)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(input->type() == MIRType::
Value || input->type() == MIRType::Object))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("input->type() == MIRType::Value || input->type() == MIRType::Object"
, "./../../../../js/src/jit/MIR.cpp", 5513); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "input->type() == MIRType::Value || input->type() == MIRType::Object"
")"); do { MOZ_CrashSequence(__null, 5513); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5514
5515 // Constant typeof folding handles the other cases.
5516 MOZ_ASSERT_IF(input->type() == MIRType::Object, type == JSTYPE_UNDEFINED ||do { if (input->type() == MIRType::Object) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(type ==
JSTYPE_UNDEFINED || type == JSTYPE_OBJECT || type == JSTYPE_FUNCTION
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(type == JSTYPE_UNDEFINED || type == JSTYPE_OBJECT ||
type == JSTYPE_FUNCTION))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("type == JSTYPE_UNDEFINED || type == JSTYPE_OBJECT || type == JSTYPE_FUNCTION"
, "./../../../../js/src/jit/MIR.cpp", 5518); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == JSTYPE_UNDEFINED || type == JSTYPE_OBJECT || type == JSTYPE_FUNCTION"
")"); do { MOZ_CrashSequence(__null, 5518); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
5517 type == JSTYPE_OBJECT ||do { if (input->type() == MIRType::Object) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(type ==
JSTYPE_UNDEFINED || type == JSTYPE_OBJECT || type == JSTYPE_FUNCTION
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(type == JSTYPE_UNDEFINED || type == JSTYPE_OBJECT ||
type == JSTYPE_FUNCTION))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("type == JSTYPE_UNDEFINED || type == JSTYPE_OBJECT || type == JSTYPE_FUNCTION"
, "./../../../../js/src/jit/MIR.cpp", 5518); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == JSTYPE_UNDEFINED || type == JSTYPE_OBJECT || type == JSTYPE_FUNCTION"
")"); do { MOZ_CrashSequence(__null, 5518); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
5518 type == JSTYPE_FUNCTION)do { if (input->type() == MIRType::Object) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(type ==
JSTYPE_UNDEFINED || type == JSTYPE_OBJECT || type == JSTYPE_FUNCTION
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(type == JSTYPE_UNDEFINED || type == JSTYPE_OBJECT ||
type == JSTYPE_FUNCTION))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("type == JSTYPE_UNDEFINED || type == JSTYPE_OBJECT || type == JSTYPE_FUNCTION"
, "./../../../../js/src/jit/MIR.cpp", 5518); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == JSTYPE_UNDEFINED || type == JSTYPE_OBJECT || type == JSTYPE_FUNCTION"
")"); do { MOZ_CrashSequence(__null, 5518); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5519
5520 MOZ_ASSERT(type != JSTYPE_LIMIT, "unknown typeof strings folded earlier")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type != JSTYPE_LIMIT)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type != JSTYPE_LIMIT))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("type != JSTYPE_LIMIT"
" (" "unknown typeof strings folded earlier" ")", "./../../../../js/src/jit/MIR.cpp"
, 5520); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type != JSTYPE_LIMIT"
") (" "unknown typeof strings folded earlier" ")"); do { MOZ_CrashSequence
(__null, 5520); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5521
5522 // If there's only a single use, assume this |typeof| is used in a simple
5523 // comparison context.
5524 //
5525 // if (typeof thing === "number") { ... }
5526 //
5527 // It'll be compiled into something similar to:
5528 //
5529 // if (IsNumber(thing)) { ... }
5530 //
5531 // This heuristic can go wrong when repeated |typeof| are used in consecutive
5532 // if-statements.
5533 //
5534 // if (typeof thing === "number") { ... }
5535 // else if (typeof thing === "string") { ... }
5536 // ... repeated for all possible types
5537 //
5538 // In that case it'd more efficient to emit MTypeOf compared to MTypeOfIs. We
5539 // don't yet handle that case, because it'd require a separate optimization
5540 // pass to correctly detect it.
5541 if (typeOfCompare->typeOfSide->hasOneUse()) {
5542 return MTypeOfIs::New(alloc, input, jsop(), type);
5543 }
5544
5545 if (typeOfCompare->isIntComparison) {
5546 // Already optimized.
5547 return this;
5548 }
5549
5550 MConstant* cst = MConstant::NewInt32(alloc, type);
5551 block()->insertBefore(this, cst);
5552
5553 return MCompare::New(alloc, typeOf, cst, jsop(), MCompare::Compare_Int32);
5554}
5555
5556MDefinition* MCompare::tryFoldCharCompare(TempAllocator& alloc) {
5557 if (compareType() != Compare_String) {
5558 return this;
5559 }
5560
5561 MDefinition* left = lhs();
5562 MOZ_ASSERT(left->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(left->type() == MIRType::String)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(left->type() == MIRType::
String))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("left->type() == MIRType::String", "./../../../../js/src/jit/MIR.cpp"
, 5562); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5562); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5563
5564 MDefinition* right = rhs();
5565 MOZ_ASSERT(right->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(right->type() == MIRType::String)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(right->type() == MIRType::
String))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("right->type() == MIRType::String", "./../../../../js/src/jit/MIR.cpp"
, 5565); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5565); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5566
5567 // |str[i]| is compiled as |MFromCharCode(MCharCodeAt(str, i))|.
5568 // Out-of-bounds access is compiled as
5569 // |FromCharCodeEmptyIfNegative(CharCodeAtOrNegative(str, i))|.
5570 auto isCharAccess = [](MDefinition* ins) {
5571 if (ins->isFromCharCode()) {
5572 return ins->toFromCharCode()->code()->isCharCodeAt();
5573 }
5574 if (ins->isFromCharCodeEmptyIfNegative()) {
5575 auto* fromCharCode = ins->toFromCharCodeEmptyIfNegative();
5576 return fromCharCode->code()->isCharCodeAtOrNegative();
5577 }
5578 return false;
5579 };
5580
5581 auto charAccessCode = [](MDefinition* ins) {
5582 if (ins->isFromCharCode()) {
5583 return ins->toFromCharCode()->code();
5584 }
5585 return ins->toFromCharCodeEmptyIfNegative()->code();
5586 };
5587
5588 if (left->isConstant() || right->isConstant()) {
5589 // Try to optimize |MConstant(string) <compare> (MFromCharCode MCharCodeAt)|
5590 // as |MConstant(charcode) <compare> MCharCodeAt|.
5591 MConstant* constant;
5592 MDefinition* operand;
5593 if (left->isConstant()) {
5594 constant = left->toConstant();
5595 operand = right;
5596 } else {
5597 constant = right->toConstant();
5598 operand = left;
5599 }
5600
5601 if (constant->toString()->length() != 1 || !isCharAccess(operand)) {
5602 return this;
5603 }
5604
5605 char16_t charCode = constant->toString()->latin1OrTwoByteChar(0);
5606 MConstant* charCodeConst = MConstant::NewInt32(alloc, charCode);
5607 block()->insertBefore(this, charCodeConst);
5608
5609 MDefinition* charCodeAt = charAccessCode(operand);
5610
5611 if (left->isConstant()) {
5612 left = charCodeConst;
5613 right = charCodeAt;
5614 } else {
5615 left = charCodeAt;
5616 right = charCodeConst;
5617 }
5618 } else if (isCharAccess(left) && isCharAccess(right)) {
5619 // Try to optimize |(MFromCharCode MCharCodeAt) <compare> (MFromCharCode
5620 // MCharCodeAt)| as |MCharCodeAt <compare> MCharCodeAt|.
5621
5622 left = charAccessCode(left);
5623 right = charAccessCode(right);
5624 } else {
5625 return this;
5626 }
5627
5628 return MCompare::New(alloc, left, right, jsop(), MCompare::Compare_Int32);
5629}
5630
5631MDefinition* MCompare::tryFoldStringCompare(TempAllocator& alloc) {
5632 if (compareType() != Compare_String) {
5633 return this;
5634 }
5635
5636 MDefinition* left = lhs();
5637 MOZ_ASSERT(left->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(left->type() == MIRType::String)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(left->type() == MIRType::
String))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("left->type() == MIRType::String", "./../../../../js/src/jit/MIR.cpp"
, 5637); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5637); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5638
5639 MDefinition* right = rhs();
5640 MOZ_ASSERT(right->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(right->type() == MIRType::String)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(right->type() == MIRType::
String))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("right->type() == MIRType::String", "./../../../../js/src/jit/MIR.cpp"
, 5640); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5640); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5641
5642 if (!left->isConstant() && !right->isConstant()) {
5643 return this;
5644 }
5645
5646 // Try to optimize |string <compare> MConstant("")| as |MStringLength(string)
5647 // <compare> MConstant(0)|.
5648
5649 MConstant* constant =
5650 left->isConstant() ? left->toConstant() : right->toConstant();
5651 if (!constant->toString()->empty()) {
5652 return this;
5653 }
5654
5655 MDefinition* operand = left->isConstant() ? right : left;
5656
5657 auto* strLength = MStringLength::New(alloc, operand);
5658 block()->insertBefore(this, strLength);
5659
5660 auto* zero = MConstant::NewInt32(alloc, 0);
5661 block()->insertBefore(this, zero);
5662
5663 if (left->isConstant()) {
5664 left = zero;
5665 right = strLength;
5666 } else {
5667 left = strLength;
5668 right = zero;
5669 }
5670
5671 return MCompare::New(alloc, left, right, jsop(), MCompare::Compare_Int32);
5672}
5673
5674MDefinition* MCompare::tryFoldStringSubstring(TempAllocator& alloc) {
5675 if (compareType() != Compare_String) {
5676 return this;
5677 }
5678 if (!IsEqualityOp(jsop())) {
5679 return this;
5680 }
5681
5682 auto* left = lhs();
5683 MOZ_ASSERT(left->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(left->type() == MIRType::String)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(left->type() == MIRType::
String))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("left->type() == MIRType::String", "./../../../../js/src/jit/MIR.cpp"
, 5683); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5683); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5684
5685 auto* right = rhs();
5686 MOZ_ASSERT(right->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(right->type() == MIRType::String)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(right->type() == MIRType::
String))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("right->type() == MIRType::String", "./../../../../js/src/jit/MIR.cpp"
, 5686); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5686); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5687
5688 // One operand must be a constant string.
5689 if (!left->isConstant() && !right->isConstant()) {
5690 return this;
5691 }
5692
5693 // The constant string must be non-empty.
5694 auto* constant =
5695 left->isConstant() ? left->toConstant() : right->toConstant();
5696 if (constant->toString()->empty()) {
5697 return this;
5698 }
5699
5700 // The other operand must be a substring operation.
5701 auto* operand = left->isConstant() ? right : left;
5702 if (!operand->isSubstr()) {
5703 return this;
5704 }
5705 auto* substr = operand->toSubstr();
5706
5707 static_assert(JSString::MAX_LENGTH < INT32_MAX(2147483647),
5708 "string length can be casted to int32_t");
5709
5710 int32_t stringLength = int32_t(constant->toString()->length());
5711
5712 MInstruction* replacement;
5713 if (IsSubstrTo(substr, stringLength)) {
5714 // Fold |str.substring(0, 2) == "aa"| to |str.startsWith("aa")|.
5715 replacement = MStringStartsWith::New(alloc, substr->string(), constant);
5716 } else if (IsSubstrLast(substr, -stringLength)) {
5717 // Fold |str.slice(-2) == "aa"| to |str.endsWith("aa")|.
5718 replacement = MStringEndsWith::New(alloc, substr->string(), constant);
5719 } else {
5720 return this;
5721 }
5722
5723 if (jsop() == JSOp::Eq || jsop() == JSOp::StrictEq) {
5724 return replacement;
5725 }
5726
5727 // Invert for inequality.
5728 MOZ_ASSERT(jsop() == JSOp::Ne || jsop() == JSOp::StrictNe)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(jsop() == JSOp::Ne || jsop() == JSOp::StrictNe)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(jsop() == JSOp::Ne || jsop() == JSOp::StrictNe))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("jsop() == JSOp::Ne || jsop() == JSOp::StrictNe"
, "./../../../../js/src/jit/MIR.cpp", 5728); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "jsop() == JSOp::Ne || jsop() == JSOp::StrictNe"
")"); do { MOZ_CrashSequence(__null, 5728); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5729
5730 block()->insertBefore(this, replacement);
5731 return MNot::New(alloc, replacement);
5732}
5733
5734MDefinition* MCompare::tryFoldStringIndexOf(TempAllocator& alloc) {
5735 if (compareType() != Compare_Int32) {
5736 return this;
5737 }
5738 if (!IsEqualityOp(jsop())) {
5739 return this;
5740 }
5741
5742 auto* left = lhs();
5743 MOZ_ASSERT(left->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(left->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(left->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("left->type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 5743); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 5743); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5744
5745 auto* right = rhs();
5746 MOZ_ASSERT(right->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(right->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(right->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("right->type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 5746); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 5746); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5747
5748 // One operand must be a constant integer.
5749 if (!left->isConstant() && !right->isConstant()) {
5750 return this;
5751 }
5752
5753 // The constant must be zero.
5754 auto* constant =
5755 left->isConstant() ? left->toConstant() : right->toConstant();
5756 if (!constant->isInt32(0)) {
5757 return this;
5758 }
5759
5760 // The other operand must be an indexOf operation.
5761 auto* operand = left->isConstant() ? right : left;
5762 if (!operand->isStringIndexOf()) {
5763 return this;
5764 }
5765
5766 // Fold |str.indexOf(searchStr) == 0| to |str.startsWith(searchStr)|.
5767
5768 auto* indexOf = operand->toStringIndexOf();
5769 auto* startsWith =
5770 MStringStartsWith::New(alloc, indexOf->string(), indexOf->searchString());
5771 if (jsop() == JSOp::Eq || jsop() == JSOp::StrictEq) {
5772 return startsWith;
5773 }
5774
5775 // Invert for inequality.
5776 MOZ_ASSERT(jsop() == JSOp::Ne || jsop() == JSOp::StrictNe)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(jsop() == JSOp::Ne || jsop() == JSOp::StrictNe)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(jsop() == JSOp::Ne || jsop() == JSOp::StrictNe))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("jsop() == JSOp::Ne || jsop() == JSOp::StrictNe"
, "./../../../../js/src/jit/MIR.cpp", 5776); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "jsop() == JSOp::Ne || jsop() == JSOp::StrictNe"
")"); do { MOZ_CrashSequence(__null, 5776); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5777
5778 block()->insertBefore(this, startsWith);
5779 return MNot::New(alloc, startsWith);
5780}
5781
5782/**
5783 * Most architectures can generate smaller code for comparison against zero, so
5784 * the macro-assemblers special-case a zero immediate when emitting
5785 * compare-and-branch instructions.
5786 *
5787 * Some comparisons against one resp. negative one can instead be written as a
5788 * comparison against zero. Handle these cases here to avoid duplicating the
5789 * same code across all architectures.
5790 */
5791static bool CanCompareAgainstZero(int64_t value, JSOp op, bool isSigned) {
5792 switch (op) {
5793 case JSOp::Lt:
5794 case JSOp::Ge:
5795 // Can rewrite |operand < 1| as |operand <= 0|.
5796 // Can rewrite |operand >= 1| as |operand > 0|.
5797 return value == 1;
5798
5799 case JSOp::Le:
5800 case JSOp::Gt:
5801 // Can rewrite |operand <= -1| as |operand < 0|.
5802 // Can rewrite |operand > -1| as |operand >= 0|.
5803 return isSigned && value == -1;
5804
5805 default:
5806 return false;
5807 }
5808}
5809
5810MCompare* MCompare::newCompareInt(TempAllocator& alloc, MDefinition* operand,
5811 int64_t value, JSOp op, bool isSigned) {
5812 MOZ_ASSERT(IsIntType(operand->type()) || operand->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsIntType(operand->type()) || operand->type() ==
MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsIntType(operand->type()
) || operand->type() == MIRType::BigInt))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("IsIntType(operand->type()) || operand->type() == MIRType::BigInt"
, "./../../../../js/src/jit/MIR.cpp", 5812); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsIntType(operand->type()) || operand->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 5812); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5813 MOZ_ASSERT_IF(operand->type() == MIRType::BigInt, isSigned)do { if (operand->type() == MIRType::BigInt) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(isSigned
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(isSigned))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("isSigned", "./../../../../js/src/jit/MIR.cpp", 5813); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "isSigned" ")"); do { MOZ_CrashSequence(__null
, 5813); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false); } } while (false)
;
5814
5815 // Prefer comparison against zero if possible.
5816 if (CanCompareAgainstZero(value, op, isSigned)) {
5817 value = 0;
5818
5819 // Update operator: (Lt -> Le), (Le -> Lt), (Gt -> Ge), (Ge -> Gt).
5820 op = ReverseCompareOp(NegateCompareOp(op));
5821 }
5822
5823 MConstant* cst;
5824 CompareType compareType;
5825 switch (operand->type()) {
5826 case MIRType::Int32:
5827 cst = MConstant::NewInt32(alloc, mozilla::AssertedCast<int32_t>(value));
5828 compareType = isSigned ? Compare_Int32 : Compare_UInt32;
5829 break;
5830
5831 case MIRType::Int64:
5832 cst = MConstant::NewInt64(alloc, value);
5833 compareType = isSigned ? Compare_Int64 : Compare_UInt64;
5834 break;
5835
5836 case MIRType::IntPtr:
5837 cst = MConstant::NewIntPtr(alloc, mozilla::AssertedCast<intptr_t>(value));
5838 compareType = isSigned ? Compare_IntPtr : Compare_UIntPtr;
5839 break;
5840
5841 case MIRType::BigInt:
5842 cst = MConstant::NewInt32(alloc, mozilla::AssertedCast<int32_t>(value));
5843 compareType = Compare_BigInt_Int32;
5844 break;
5845
5846 default:
5847 MOZ_CRASH("unexpected operand type")do { do { } while (false); MOZ_ReportCrash("" "unexpected operand type"
, "./../../../../js/src/jit/MIR.cpp", 5847); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected operand type" ")"); do { MOZ_CrashSequence
(__null, 5847); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
5848 }
5849 block()->insertBefore(this, cst);
5850
5851 auto* ins = MCompare::New(alloc, operand, cst, op, compareType);
5852 ins->setResultType(type());
5853 return ins;
5854}
5855
5856MDefinition* MCompare::tryFoldBigInt64(TempAllocator& alloc) {
5857 if (compareType() == Compare_BigInt) {
5858 auto* left = lhs();
5859 MOZ_ASSERT(left->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(left->type() == MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(left->type() == MIRType::
BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("left->type() == MIRType::BigInt", "./../../../../js/src/jit/MIR.cpp"
, 5859); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 5859); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5860
5861 auto* right = rhs();
5862 MOZ_ASSERT(right->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(right->type() == MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(right->type() == MIRType::
BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("right->type() == MIRType::BigInt", "./../../../../js/src/jit/MIR.cpp"
, 5862); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 5862); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5863
5864 // At least one operand must be MInt64ToBigInt.
5865 if (!left->isInt64ToBigInt() && !right->isInt64ToBigInt()) {
5866 return this;
5867 }
5868
5869 // Unwrap MInt64ToBigInt on both sides and perform a Int64 comparison.
5870 if (left->isInt64ToBigInt() && right->isInt64ToBigInt()) {
5871 auto* lhsInt64 = left->toInt64ToBigInt();
5872 auto* rhsInt64 = right->toInt64ToBigInt();
5873
5874 // Don't optimize if Int64 against Uint64 comparison.
5875 if (lhsInt64->isSigned() != rhsInt64->isSigned()) {
5876 return this;
5877 }
5878
5879 bool isSigned = lhsInt64->isSigned();
5880 auto compareType =
5881 isSigned ? MCompare::Compare_Int64 : MCompare::Compare_UInt64;
5882 return MCompare::New(alloc, lhsInt64->input(), rhsInt64->input(), jsop_,
5883 compareType);
5884 }
5885
5886 // Optimize IntPtr x Int64 comparison to Int64 x Int64 comparison.
5887 if (left->isIntPtrToBigInt() || right->isIntPtrToBigInt()) {
5888 auto* int64ToBigInt = left->isInt64ToBigInt() ? left->toInt64ToBigInt()
5889 : right->toInt64ToBigInt();
5890
5891 // Can't optimize when comparing Uint64 against IntPtr.
5892 if (!int64ToBigInt->isSigned()) {
5893 return this;
5894 }
5895
5896 auto* intPtrToBigInt = left->isIntPtrToBigInt()
5897 ? left->toIntPtrToBigInt()
5898 : right->toIntPtrToBigInt();
5899
5900 auto* intPtrToInt64 = MIntPtrToInt64::New(alloc, intPtrToBigInt->input());
5901 block()->insertBefore(this, intPtrToInt64);
5902
5903 if (left == int64ToBigInt) {
5904 left = int64ToBigInt->input();
5905 right = intPtrToInt64;
5906 } else {
5907 left = intPtrToInt64;
5908 right = int64ToBigInt->input();
5909 }
5910 return MCompare::New(alloc, left, right, jsop_, MCompare::Compare_Int64);
5911 }
5912
5913 // The other operand must be a constant.
5914 if (!left->isConstant() && !right->isConstant()) {
5915 return this;
5916 }
5917
5918 auto* int64ToBigInt = left->isInt64ToBigInt() ? left->toInt64ToBigInt()
5919 : right->toInt64ToBigInt();
5920 bool isSigned = int64ToBigInt->isSigned();
5921
5922 auto* constant =
5923 left->isConstant() ? left->toConstant() : right->toConstant();
5924 auto* bigInt = constant->toBigInt();
5925
5926 // Extract the BigInt value if representable as Int64/Uint64.
5927 mozilla::Maybe<int64_t> value;
5928 if (isSigned) {
5929 int64_t x;
5930 if (BigInt::isInt64(bigInt, &x)) {
5931 value = mozilla::Some(x);
5932 }
5933 } else {
5934 uint64_t x;
5935 if (BigInt::isUint64(bigInt, &x)) {
5936 value = mozilla::Some(static_cast<int64_t>(x));
5937 }
5938 }
5939
5940 // The comparison is a constant if the BigInt has too many digits.
5941 if (!value) {
5942 int32_t repr = bigInt->isNegative() ? -1 : 1;
5943
5944 bool result;
5945 if (left == int64ToBigInt) {
5946 result = FoldComparison(jsop_, 0, repr);
5947 } else {
5948 result = FoldComparison(jsop_, repr, 0);
5949 }
5950 return MConstant::NewBoolean(alloc, result);
5951 }
5952
5953 JSOp op = jsop();
5954 if (right == int64ToBigInt) {
5955 op = ReverseCompareOp(op);
5956 }
5957 return newCompareInt(alloc, int64ToBigInt->input(), *value, op, isSigned);
5958 }
5959
5960 if (compareType() == Compare_BigInt_Int32) {
5961 auto* left = lhs();
5962 MOZ_ASSERT(left->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(left->type() == MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(left->type() == MIRType::
BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("left->type() == MIRType::BigInt", "./../../../../js/src/jit/MIR.cpp"
, 5962); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 5962); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5963
5964 auto* right = rhs();
5965 MOZ_ASSERT(right->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(right->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(right->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("right->type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 5965); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 5965); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5966
5967 // Optimize MInt64ToBigInt against a constant int32.
5968 if (!left->isInt64ToBigInt() || !right->isConstant()) {
5969 return this;
5970 }
5971
5972 auto* int64ToBigInt = left->toInt64ToBigInt();
5973 bool isSigned = int64ToBigInt->isSigned();
5974
5975 int32_t constInt32 = right->toConstant()->toInt32();
5976
5977 // The unsigned comparison against a negative operand is a constant.
5978 if (!isSigned && constInt32 < 0) {
5979 bool result = FoldComparison(jsop_, 0, constInt32);
5980 return MConstant::NewBoolean(alloc, result);
5981 }
5982
5983 return newCompareInt(alloc, int64ToBigInt->input(), constInt32, jsop(),
5984 isSigned);
5985 }
5986
5987 return this;
5988}
5989
5990MDefinition* MCompare::tryFoldBigIntPtr(TempAllocator& alloc) {
5991 if (compareType() == Compare_BigInt) {
5992 auto* left = lhs();
5993 MOZ_ASSERT(left->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(left->type() == MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(left->type() == MIRType::
BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("left->type() == MIRType::BigInt", "./../../../../js/src/jit/MIR.cpp"
, 5993); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 5993); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5994
5995 auto* right = rhs();
5996 MOZ_ASSERT(right->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(right->type() == MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(right->type() == MIRType::
BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("right->type() == MIRType::BigInt", "./../../../../js/src/jit/MIR.cpp"
, 5996); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 5996); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5997
5998 // At least one operand must be MIntPtrToBigInt.
5999 if (!left->isIntPtrToBigInt() && !right->isIntPtrToBigInt()) {
6000 return this;
6001 }
6002
6003 // Unwrap MIntPtrToBigInt on both sides and perform an IntPtr comparison.
6004 if (left->isIntPtrToBigInt() && right->isIntPtrToBigInt()) {
6005 auto* lhsIntPtr = left->toIntPtrToBigInt();
6006 auto* rhsIntPtr = right->toIntPtrToBigInt();
6007
6008 return MCompare::New(alloc, lhsIntPtr->input(), rhsIntPtr->input(), jsop_,
6009 MCompare::Compare_IntPtr);
6010 }
6011
6012 // The other operand must be a constant.
6013 if (!left->isConstant() && !right->isConstant()) {
6014 return this;
6015 }
6016
6017 auto* intPtrToBigInt = left->isIntPtrToBigInt() ? left->toIntPtrToBigInt()
6018 : right->toIntPtrToBigInt();
6019
6020 auto* constant =
6021 left->isConstant() ? left->toConstant() : right->toConstant();
6022 auto* bigInt = constant->toBigInt();
6023
6024 // Extract the BigInt value if representable as intptr_t.
6025 intptr_t value;
6026 if (!BigInt::isIntPtr(bigInt, &value)) {
6027 // The comparison is a constant if the BigInt has too many digits.
6028 int32_t repr = bigInt->isNegative() ? -1 : 1;
6029
6030 bool result;
6031 if (left == intPtrToBigInt) {
6032 result = FoldComparison(jsop_, 0, repr);
6033 } else {
6034 result = FoldComparison(jsop_, repr, 0);
6035 }
6036 return MConstant::NewBoolean(alloc, result);
6037 }
6038
6039 JSOp op = jsop();
6040 if (right == intPtrToBigInt) {
6041 op = ReverseCompareOp(op);
6042 }
6043 return newCompareInt(alloc, intPtrToBigInt->input(), value, op);
6044 }
6045
6046 if (compareType() == Compare_BigInt_Int32) {
6047 auto* left = lhs();
6048 MOZ_ASSERT(left->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(left->type() == MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(left->type() == MIRType::
BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("left->type() == MIRType::BigInt", "./../../../../js/src/jit/MIR.cpp"
, 6048); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 6048); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6049
6050 auto* right = rhs();
6051 MOZ_ASSERT(right->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(right->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(right->type() == MIRType::
Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("right->type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 6051); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 6051); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6052
6053 // Optimize MIntPtrToBigInt against a constant int32.
6054 if (!left->isIntPtrToBigInt() || !right->isConstant()) {
6055 return this;
6056 }
6057
6058 return newCompareInt(alloc, left->toIntPtrToBigInt()->input(),
6059 right->toConstant()->toInt32(), jsop());
6060 }
6061
6062 return this;
6063}
6064
6065MDefinition* MCompare::tryFoldBigInt(TempAllocator& alloc) {
6066 if (compareType() != Compare_BigInt) {
6067 return this;
6068 }
6069
6070 auto* left = lhs();
6071 MOZ_ASSERT(left->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(left->type() == MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(left->type() == MIRType::
BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("left->type() == MIRType::BigInt", "./../../../../js/src/jit/MIR.cpp"
, 6071); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 6071); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6072
6073 auto* right = rhs();
6074 MOZ_ASSERT(right->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(right->type() == MIRType::BigInt)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(right->type() == MIRType::
BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("right->type() == MIRType::BigInt", "./../../../../js/src/jit/MIR.cpp"
, 6074); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 6074); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6075
6076 // One operand must be a constant.
6077 if (!left->isConstant() && !right->isConstant()) {
6078 return this;
6079 }
6080
6081 auto* constant =
6082 left->isConstant() ? left->toConstant() : right->toConstant();
6083 auto* operand = left->isConstant() ? right : left;
6084
6085 // The constant must be representable as an Int32.
6086 int32_t x;
6087 if (!BigInt::isInt32(constant->toBigInt(), &x)) {
6088 return this;
6089 }
6090
6091 auto op = jsop();
6092 if (IsStrictEqualityOp(op)) {
6093 // Compare_BigInt_Int32 is only valid for loose comparison.
6094 op = op == JSOp::StrictEq ? JSOp::Eq : JSOp::Ne;
6095 } else if (operand == right) {
6096 // Reverse the comparison operator if the operands were reordered.
6097 op = ReverseCompareOp(op);
6098 }
6099 return newCompareInt(alloc, operand, x, op);
6100}
6101
6102MDefinition* MCompare::tryFoldIntZero(TempAllocator& alloc) {
6103 // Expect signed or unsigned integer relational comparison.
6104 if (!IsRelationalOp(jsop())) {
6105 return this;
6106 }
6107
6108 bool isSigned;
6109 switch (compareType()) {
6110 case Compare_Int32:
6111 case Compare_Int64:
6112 case Compare_IntPtr:
6113 isSigned = true;
6114 break;
6115
6116 case Compare_UInt32:
6117 case Compare_UInt64:
6118 case Compare_UIntPtr:
6119 isSigned = false;
6120 break;
6121
6122 case Compare_Undefined:
6123 case Compare_Null:
6124 case Compare_Double:
6125 case Compare_Float32:
6126 case Compare_String:
6127 case Compare_Symbol:
6128 case Compare_Object:
6129 case Compare_BigInt:
6130 case Compare_BigInt_Int32:
6131 case Compare_BigInt_Double:
6132 case Compare_BigInt_String:
6133 case Compare_WasmAnyRef:
6134 return this;
6135 }
6136
6137 auto* left = lhs();
6138 auto* right = rhs();
6139
6140 // Both operands have the same Int type.
6141 MOZ_ASSERT(left->type() == right->type())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(left->type() == right->type())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(left->type() == right->
type()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("left->type() == right->type()", "./../../../../js/src/jit/MIR.cpp"
, 6141); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == right->type()"
")"); do { MOZ_CrashSequence(__null, 6141); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6142 MOZ_ASSERT(IsIntType(left->type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsIntType(left->type()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsIntType(left->type())))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsIntType(left->type())"
, "./../../../../js/src/jit/MIR.cpp", 6142); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsIntType(left->type())" ")"); do { MOZ_CrashSequence
(__null, 6142); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6143
6144 // One operand must be a constant.
6145 if (!left->isConstant() && !right->isConstant()) {
6146 return this;
6147 }
6148
6149 auto* constant =
6150 left->isConstant() ? left->toConstant() : right->toConstant();
6151 auto* operand = left->isConstant() ? right : left;
6152
6153 int64_t value;
6154 switch (constant->type()) {
6155 case MIRType::Int32:
6156 value = constant->toInt32();
6157 break;
6158
6159 case MIRType::Int64:
6160 value = constant->toInt64();
6161 break;
6162
6163 case MIRType::IntPtr:
6164 value = constant->toIntPtr();
6165 break;
6166
6167 default:
6168 MOZ_CRASH("unexpected int type")do { do { } while (false); MOZ_ReportCrash("" "unexpected int type"
, "./../../../../js/src/jit/MIR.cpp", 6168); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected int type" ")"); do { MOZ_CrashSequence
(__null, 6168); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
6169 }
6170
6171 auto op = jsop();
6172 if (operand == right) {
6173 op = ReverseCompareOp(op);
6174 }
6175
6176 if (!CanCompareAgainstZero(value, op, isSigned)) {
6177 return this;
6178 }
6179 return newCompareInt(alloc, operand, value, op, isSigned);
6180}
6181
6182MDefinition* MCompare::foldsTo(TempAllocator& alloc) {
6183 bool result;
6184
6185 if (tryFold(&result) || evaluateConstantOperands(alloc, &result)) {
6186 if (type() == MIRType::Int32) {
6187 return MConstant::NewInt32(alloc, result);
6188 }
6189
6190 MOZ_ASSERT(type() == MIRType::Boolean)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type() == MIRType::Boolean)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type() == MIRType::Boolean))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("type() == MIRType::Boolean"
, "./../../../../js/src/jit/MIR.cpp", 6190); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Boolean" ")"); do { MOZ_CrashSequence
(__null, 6190); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6191 return MConstant::NewBoolean(alloc, result);
6192 }
6193
6194 if (MDefinition* folded = tryFoldTypeOf(alloc); folded != this) {
6195 return folded;
6196 }
6197
6198 if (MDefinition* folded = tryFoldCharCompare(alloc); folded != this) {
6199 return folded;
6200 }
6201
6202 if (MDefinition* folded = tryFoldStringCompare(alloc); folded != this) {
6203 return folded;
6204 }
6205
6206 if (MDefinition* folded = tryFoldStringSubstring(alloc); folded != this) {
6207 return folded;
6208 }
6209
6210 if (MDefinition* folded = tryFoldStringIndexOf(alloc); folded != this) {
6211 return folded;
6212 }
6213
6214 if (MDefinition* folded = tryFoldBigInt64(alloc); folded != this) {
6215 return folded;
6216 }
6217
6218 if (MDefinition* folded = tryFoldBigIntPtr(alloc); folded != this) {
6219 return folded;
6220 }
6221
6222 if (MDefinition* folded = tryFoldBigInt(alloc); folded != this) {
6223 return folded;
6224 }
6225
6226 if (MDefinition* folded = tryFoldIntZero(alloc); folded != this) {
6227 return folded;
6228 }
6229
6230 return this;
6231}
6232
6233void MCompare::trySpecializeFloat32(TempAllocator& alloc) {
6234 if (AllOperandsCanProduceFloat32(this) && compareType_ == Compare_Double) {
6235 compareType_ = Compare_Float32;
6236 } else {
6237 ConvertOperandsToDouble(this, alloc);
6238 }
6239}
6240
6241MDefinition* MStrictConstantCompareInt32::foldsTo(TempAllocator& alloc) {
6242 if (!value()->isBox()) {
6243 return this;
6244 }
6245 MDefinition* unboxed = value()->toBox()->input();
6246
6247 if (unboxed->type() == MIRType::Int32) {
6248 if (unboxed->isConstant()) {
6249 bool result =
6250 FoldComparison(jsop(), unboxed->toConstant()->toInt32(), constant());
6251 return MConstant::NewBoolean(alloc, result);
6252 }
6253
6254 auto* cst = MConstant::NewInt32(alloc, constant());
6255 block()->insertBefore(this, cst);
6256
6257 return MCompare::New(alloc, unboxed, cst, jsop(), MCompare::Compare_Int32);
6258 }
6259
6260 if (unboxed->type() == MIRType::Double) {
6261 if (unboxed->isConstant()) {
6262 bool result = FoldComparison(jsop(), unboxed->toConstant()->toDouble(),
6263 double(constant()));
6264 return MConstant::NewBoolean(alloc, result);
6265 }
6266
6267 auto* cst = MConstant::NewDouble(alloc, constant());
6268 block()->insertBefore(this, cst);
6269
6270 return MCompare::New(alloc, unboxed, cst, jsop(), MCompare::Compare_Double);
6271 }
6272
6273 MOZ_ASSERT(!IsNumberType(unboxed->type()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsNumberType(unboxed->type()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsNumberType(unboxed->type
())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!IsNumberType(unboxed->type())", "./../../../../js/src/jit/MIR.cpp"
, 6273); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsNumberType(unboxed->type())"
")"); do { MOZ_CrashSequence(__null, 6273); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6274 return MConstant::NewBoolean(alloc, jsop() == JSOp::StrictNe);
6275}
6276
6277MDefinition* MStrictConstantCompareBoolean::foldsTo(TempAllocator& alloc) {
6278 if (!value()->isBox()) {
6279 return this;
6280 }
6281 MDefinition* unboxed = value()->toBox()->input();
6282
6283 if (unboxed->type() == MIRType::Boolean) {
6284 if (unboxed->isConstant()) {
6285 bool result = (jsop() == JSOp::StrictEq) ==
6286 (unboxed->toConstant()->toBoolean() == constant());
6287 return MConstant::NewBoolean(alloc, result);
6288 }
6289
6290 auto* inputI32 = MBooleanToInt32::New(alloc, unboxed);
6291 block()->insertBefore(this, inputI32);
6292
6293 auto* cst = MConstant::NewInt32(alloc, int32_t(constant()));
6294 block()->insertBefore(this, cst);
6295
6296 return MCompare::New(alloc, inputI32, cst, jsop(), MCompare::Compare_Int32);
6297 }
6298
6299 return MConstant::NewBoolean(alloc, jsop() == JSOp::StrictNe);
6300}
6301
6302MDefinition* MStrictConstantCompareString::foldsTo(TempAllocator& alloc) {
6303 if (!value()->isBox()) {
6304 return this;
6305 }
6306 MDefinition* unboxed = value()->toBox()->input();
6307
6308 if (unboxed->type() == MIRType::String) {
6309 if (unboxed->isConstant()) {
6310 int32_t comp =
6311 CompareStrings(unboxed->toConstant()->toString(), constant());
6312 bool result = FoldComparison(jsop(), comp, 0);
6313 return MConstant::NewBoolean(alloc, result);
6314 }
6315
6316 auto* cst = MConstant::NewString(alloc, constant()->unwrap());
6317 block()->insertBefore(this, cst);
6318
6319 return MCompare::New(alloc, unboxed, cst, jsop(), MCompare::Compare_String);
6320 }
6321
6322 return MConstant::NewBoolean(alloc, jsop() == JSOp::StrictNe);
6323}
6324
6325MDefinition* MSameValue::foldsTo(TempAllocator& alloc) {
6326 MDefinition* lhs = left();
6327 if (lhs->isBox()) {
6328 lhs = lhs->toBox()->input();
6329 }
6330
6331 MDefinition* rhs = right();
6332 if (rhs->isBox()) {
6333 rhs = rhs->toBox()->input();
6334 }
6335
6336 // Trivially true if both operands are the same.
6337 if (lhs == rhs) {
6338 return MConstant::NewBoolean(alloc, true);
6339 }
6340
6341 // CacheIR optimizes the following cases, so don't bother to handle them here:
6342 // 1. Both inputs are numbers (int32 or double).
6343 // 2. Both inputs are strictly different types.
6344 // 3. Both inputs are the same type.
6345
6346 // Optimize when one operand is guaranteed to be |null|.
6347 if (lhs->type() == MIRType::Null || rhs->type() == MIRType::Null) {
6348 // The `null` value must be the right-hand side operand.
6349 auto* input = lhs->type() == MIRType::Null ? rhs : lhs;
6350 auto* cst = lhs->type() == MIRType::Null ? lhs : rhs;
6351 return MCompare::New(alloc, input, cst, JSOp::StrictEq,
6352 MCompare::Compare_Null);
6353 }
6354
6355 // Optimize when one operand is guaranteed to be |undefined|.
6356 if (lhs->type() == MIRType::Undefined || rhs->type() == MIRType::Undefined) {
6357 // The `undefined` value must be the right-hand side operand.
6358 auto* input = lhs->type() == MIRType::Undefined ? rhs : lhs;
6359 auto* cst = lhs->type() == MIRType::Undefined ? lhs : rhs;
6360 return MCompare::New(alloc, input, cst, JSOp::StrictEq,
6361 MCompare::Compare_Undefined);
6362 }
6363
6364 return this;
6365}
6366
6367MDefinition* MSameValueDouble::foldsTo(TempAllocator& alloc) {
6368 // Trivially true if both operands are the same.
6369 if (left() == right()) {
6370 return MConstant::NewBoolean(alloc, true);
6371 }
6372
6373 // At least one operand must be a constant.
6374 if (!left()->isConstant() && !right()->isConstant()) {
6375 return this;
6376 }
6377
6378 auto* input = left()->isConstant() ? right() : left();
6379 auto* cst = left()->isConstant() ? left() : right();
6380 double dbl = cst->toConstant()->toDouble();
6381
6382 // Use bitwise comparison for +/-0.
6383 if (dbl == 0.0) {
6384 auto* reinterp = MReinterpretCast::New(alloc, input, MIRType::Int64);
6385 block()->insertBefore(this, reinterp);
6386
6387 auto* zeroBitsCst =
6388 MConstant::NewInt64(alloc, mozilla::BitwiseCast<int64_t>(dbl));
6389 block()->insertBefore(this, zeroBitsCst);
6390
6391 return MCompare::New(alloc, reinterp, zeroBitsCst, JSOp::StrictEq,
6392 MCompare::Compare_Int64);
6393 }
6394
6395 // Fold `Object.is(d, NaN)` to `d !== d`.
6396 if (std::isnan(dbl)) {
6397 return MCompare::New(alloc, input, input, JSOp::StrictNe,
6398 MCompare::Compare_Double);
6399 }
6400
6401 // Otherwise fold to MCompare.
6402 return MCompare::New(alloc, left(), right(), JSOp::StrictEq,
6403 MCompare::Compare_Double);
6404}
6405
6406MDefinition* MNot::foldsTo(TempAllocator& alloc) {
6407 auto foldConstant = [&alloc](MDefinition* input, MIRType type) -> MConstant* {
6408 MConstant* inputConst = input->maybeConstantValue();
6409 if (!inputConst) {
6410 return nullptr;
6411 }
6412 bool b;
6413 if (!inputConst->valueToBoolean(&b)) {
6414 return nullptr;
6415 }
6416 if (type == MIRType::Int32) {
6417 return MConstant::NewInt32(alloc, !b);
6418 }
6419 MOZ_ASSERT(type == MIRType::Boolean)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(type == MIRType::Boolean)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(type == MIRType::Boolean))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::Boolean"
, "./../../../../js/src/jit/MIR.cpp", 6419); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::Boolean" ")"); do { MOZ_CrashSequence
(__null, 6419); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6420 return MConstant::NewBoolean(alloc, !b);
6421 };
6422
6423 // Fold if the input is constant.
6424 if (MConstant* folded = foldConstant(input(), type())) {
6425 return folded;
6426 }
6427
6428 // If the operand of the Not is itself a Not, they cancel out. But we can't
6429 // always convert Not(Not(x)) to x because that may loose the conversion to
6430 // boolean. We can simplify Not(Not(Not(x))) to Not(x) though.
6431 MDefinition* op = getOperand(0);
6432 if (op->isNot()) {
6433 MDefinition* opop = op->getOperand(0);
6434 if (opop->isNot()) {
6435 return opop;
6436 }
6437 }
6438
6439 // Fold a Not of a single-use integer comparison into the negated comparison,
6440 // and Not(Not(int32)) into a comparison against zero, so that the result is
6441 // materialized once instead of being computed and then flipped.
6442 if (type() == MIRType::Boolean && op->type() == MIRType::Boolean &&
6443 op->hasOneUse()) {
6444 if (op->isCompare()) {
6445 MCompare* cmp = op->toCompare();
6446 switch (cmp->compareType()) {
6447 case MCompare::Compare_Int32:
6448 case MCompare::Compare_UInt32:
6449 case MCompare::Compare_Int64:
6450 case MCompare::Compare_UInt64:
6451 case MCompare::Compare_IntPtr:
6452 case MCompare::Compare_UIntPtr:
6453 return MCompare::New(alloc, cmp->lhs(), cmp->rhs(),
6454 NegateCompareOp(cmp->jsop()),
6455 cmp->compareType());
6456 default:
6457 break;
6458 }
6459 }
6460 if (op->isNot() && op->getOperand(0)->type() == MIRType::Int32) {
6461 MConstant* zero = MConstant::NewInt32(alloc, 0);
6462 block()->insertBefore(this, zero);
6463 return MCompare::New(alloc, op->getOperand(0), zero, JSOp::Ne,
6464 MCompare::Compare_Int32);
6465 }
6466 }
6467
6468 // Not of an undefined or null value is always true
6469 if (input()->type() == MIRType::Undefined ||
6470 input()->type() == MIRType::Null) {
6471 return MConstant::NewBoolean(alloc, true);
6472 }
6473
6474 // Not of a symbol is always false.
6475 if (input()->type() == MIRType::Symbol) {
6476 return MConstant::NewBoolean(alloc, false);
6477 }
6478
6479 // Drop the conversion in `Not(Int64ToBigInt(int64))` to `Not(int64)`.
6480 if (input()->isInt64ToBigInt()) {
6481 MDefinition* int64 = input()->toInt64ToBigInt()->input();
6482 if (MConstant* folded = foldConstant(int64, type())) {
6483 return folded;
6484 }
6485 return MNot::New(alloc, int64);
6486 }
6487
6488 // Drop the conversion in `Not(IntPtrToBigInt(intptr))` to `Not(intptr)`.
6489 if (input()->isIntPtrToBigInt()) {
6490 MDefinition* intPtr = input()->toIntPtrToBigInt()->input();
6491 if (MConstant* folded = foldConstant(intPtr, type())) {
6492 return folded;
6493 }
6494 return MNot::New(alloc, intPtr);
6495 }
6496
6497 return this;
6498}
6499
6500void MNot::trySpecializeFloat32(TempAllocator& alloc) {
6501 (void)EnsureFloatInputOrConvert(this, alloc);
6502}
6503
6504#ifdef JS_JITSPEW1
6505void MBeta::printOpcode(GenericPrinter& out) const {
6506 MDefinition::printOpcode(out);
6507
6508 out.printf(" ");
6509 comparison_->dump(out);
6510}
6511#endif
6512
6513MObjectState::MObjectState(MObjectState* state)
6514 : MVariadicInstruction(classOpcode),
6515 numSlots_(state->numSlots_),
6516 numFixedSlots_(state->numFixedSlots_) {
6517 // This instruction is only used as a summary for bailout paths.
6518 setResultType(MIRType::Object);
6519 setRecoveredOnBailout();
6520}
6521
6522MObjectState::MObjectState(JSObject* templateObject)
6523 : MObjectState(templateObject->as<NativeObject>().shape()) {}
6524
6525MObjectState::MObjectState(const Shape* shape)
6526 : MVariadicInstruction(classOpcode) {
6527 // This instruction is only used as a summary for bailout paths.
6528 setResultType(MIRType::Object);
6529 setRecoveredOnBailout();
6530
6531 numSlots_ = shape->asShared().slotSpan();
6532 numFixedSlots_ = shape->asShared().numFixedSlots();
6533}
6534
6535/* static */
6536JSObject* MObjectState::templateObjectOf(MDefinition* obj) {
6537 // MNewPlainObject uses a shape constant, not an object.
6538 MOZ_ASSERT(!obj->isNewPlainObject())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!obj->isNewPlainObject())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!obj->isNewPlainObject())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!obj->isNewPlainObject()"
, "./../../../../js/src/jit/MIR.cpp", 6538); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!obj->isNewPlainObject()" ")"); do { MOZ_CrashSequence
(__null, 6538); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6539
6540 if (obj->isNewObject()) {
6541 return obj->toNewObject()->templateObject();
6542 } else if (obj->isNewCallObject()) {
6543 return obj->toNewCallObject()->templateObject();
6544 } else if (obj->isNewIterator()) {
6545 return obj->toNewIterator()->templateObject();
6546 } else if (obj->isNewBoundFunction()) {
6547 return obj->toNewBoundFunction()->templateObj();
6548 }
6549
6550 MOZ_CRASH("unreachable")do { do { } while (false); MOZ_ReportCrash("" "unreachable", "./../../../../js/src/jit/MIR.cpp"
, 6550); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable" ")"
); do { MOZ_CrashSequence(__null, 6550); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
6551}
6552
6553bool MObjectState::init(TempAllocator& alloc, MDefinition* obj) {
6554 if (!MVariadicInstruction::init(alloc, numSlots() + 1)) {
6555 return false;
6556 }
6557 // +1, for the Object.
6558 initOperand(0, obj);
6559 return true;
6560}
6561
6562void MObjectState::initFromTemplateObject(TempAllocator& alloc,
6563 MDefinition* undefinedVal) {
6564 if (object()->isNewPlainObject()) {
6565 MOZ_ASSERT(object()->toNewPlainObject()->shape()->asShared().slotSpan() ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(object()->toNewPlainObject()->shape()->asShared
().slotSpan() == numSlots())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(object()->toNewPlainObject
()->shape()->asShared().slotSpan() == numSlots()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("object()->toNewPlainObject()->shape()->asShared().slotSpan() == numSlots()"
, "./../../../../js/src/jit/MIR.cpp", 6566); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "object()->toNewPlainObject()->shape()->asShared().slotSpan() == numSlots()"
")"); do { MOZ_CrashSequence(__null, 6566); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6566 numSlots())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(object()->toNewPlainObject()->shape()->asShared
().slotSpan() == numSlots())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(object()->toNewPlainObject
()->shape()->asShared().slotSpan() == numSlots()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("object()->toNewPlainObject()->shape()->asShared().slotSpan() == numSlots()"
, "./../../../../js/src/jit/MIR.cpp", 6566); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "object()->toNewPlainObject()->shape()->asShared().slotSpan() == numSlots()"
")"); do { MOZ_CrashSequence(__null, 6566); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6567 for (size_t i = 0; i < numSlots(); i++) {
6568 initSlot(i, undefinedVal);
6569 }
6570 return;
6571 }
6572
6573 JSObject* templateObject = templateObjectOf(object());
6574
6575 // Initialize all the slots of the object state with the value contained in
6576 // the template object. This is needed to account values which are baked in
6577 // the template objects and not visible in IonMonkey, such as the
6578 // uninitialized-lexical magic value of call objects.
6579
6580 MOZ_ASSERT(templateObject->is<NativeObject>())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(templateObject->is<NativeObject>())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(templateObject->is<NativeObject>()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("templateObject->is<NativeObject>()"
, "./../../../../js/src/jit/MIR.cpp", 6580); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "templateObject->is<NativeObject>()"
")"); do { MOZ_CrashSequence(__null, 6580); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6581 NativeObject& nativeObject = templateObject->as<NativeObject>();
6582 MOZ_ASSERT(nativeObject.slotSpan() == numSlots())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nativeObject.slotSpan() == numSlots())>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(nativeObject.slotSpan() == numSlots()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("nativeObject.slotSpan() == numSlots()"
, "./../../../../js/src/jit/MIR.cpp", 6582); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nativeObject.slotSpan() == numSlots()" ")"
); do { MOZ_CrashSequence(__null, 6582); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6583
6584 for (size_t i = 0; i < numSlots(); i++) {
6585 Value val = nativeObject.getSlot(i);
6586 MDefinition* def = undefinedVal;
6587 if (!val.isUndefined()) {
6588 MConstant* ins = MConstant::New(alloc, val);
6589 block()->insertBefore(this, ins);
6590 def = ins;
6591 }
6592 initSlot(i, def);
6593 }
6594}
6595
6596MObjectState* MObjectState::New(TempAllocator& alloc, MDefinition* obj) {
6597 MObjectState* res;
6598 if (obj->isNewPlainObject()) {
6599 const Shape* shape = obj->toNewPlainObject()->shape();
6600 res = new (alloc) MObjectState(shape);
6601 } else {
6602 JSObject* templateObject = templateObjectOf(obj);
6603 MOZ_ASSERT(templateObject, "Unexpected object creation.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(templateObject)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(templateObject))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("templateObject"
" (" "Unexpected object creation." ")", "./../../../../js/src/jit/MIR.cpp"
, 6603); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateObject"
") (" "Unexpected object creation." ")"); do { MOZ_CrashSequence
(__null, 6603); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6604 res = new (alloc) MObjectState(templateObject);
6605 }
6606
6607 if (!res || !res->init(alloc, obj)) {
6608 return nullptr;
6609 }
6610 return res;
6611}
6612
6613MObjectState* MObjectState::Copy(TempAllocator& alloc, MObjectState* state) {
6614 MObjectState* res = new (alloc) MObjectState(state);
6615 if (!res || !res->init(alloc, state->object())) {
6616 return nullptr;
6617 }
6618 for (size_t i = 0; i < res->numSlots(); i++) {
6619 res->initSlot(i, state->getSlot(i));
6620 }
6621 return res;
6622}
6623
6624MArrayState::MArrayState(MDefinition* arr) : MVariadicInstruction(classOpcode) {
6625 // This instruction is only used as a summary for bailout paths.
6626 setResultType(MIRType::Object);
6627 setRecoveredOnBailout();
6628 if (arr->isNewArrayObject()) {
6629 numElements_ = arr->toNewArrayObject()->length();
6630 } else {
6631 numElements_ = arr->toNewArray()->length();
6632 }
6633}
6634
6635bool MArrayState::init(TempAllocator& alloc, MDefinition* obj,
6636 MDefinition* len) {
6637 if (!MVariadicInstruction::init(alloc, numElements() + 2)) {
6638 return false;
6639 }
6640 // +1, for the Array object.
6641 initOperand(0, obj);
6642 // +1, for the length value of the array.
6643 initOperand(1, len);
6644 return true;
6645}
6646
6647void MArrayState::initFromTemplateObject(TempAllocator& alloc,
6648 MDefinition* undefinedVal) {
6649 for (size_t i = 0; i < numElements(); i++) {
6650 initElement(i, undefinedVal);
6651 }
6652}
6653
6654MArrayState* MArrayState::New(TempAllocator& alloc, MDefinition* arr,
6655 MDefinition* initLength) {
6656 MArrayState* res = new (alloc) MArrayState(arr);
6657 if (!res || !res->init(alloc, arr, initLength)) {
6658 return nullptr;
6659 }
6660 return res;
6661}
6662
6663MArrayState* MArrayState::Copy(TempAllocator& alloc, MArrayState* state) {
6664 MDefinition* arr = state->array();
6665 MDefinition* len = state->initializedLength();
6666 MArrayState* res = new (alloc) MArrayState(arr);
6667 if (!res || !res->init(alloc, arr, len)) {
6668 return nullptr;
6669 }
6670 for (size_t i = 0; i < res->numElements(); i++) {
6671 res->initElement(i, state->getElement(i));
6672 }
6673 return res;
6674}
6675
6676MNewArray::MNewArray(uint32_t length, MConstant* templateConst,
6677 gc::Heap initialHeap, bool vmCall)
6678 : MUnaryInstruction(classOpcode, templateConst),
6679 length_(length),
6680 initialHeap_(initialHeap),
6681 vmCall_(vmCall) {
6682 setResultType(MIRType::Object);
6683}
6684
6685MDefinition::AliasType MLoadFixedSlot::mightAlias(
6686 const MDefinition* def) const {
6687 if (def->isStoreFixedSlot()) {
6688 const MStoreFixedSlot* store = def->toStoreFixedSlot();
6689 if (store->slot() != slot()) {
6690 return AliasType::NoAlias;
6691 }
6692 if (store->object() != object()) {
6693 return AliasType::MayAlias;
6694 }
6695 return AliasType::MustAlias;
6696 }
6697 return AliasType::MayAlias;
6698}
6699
6700HashNumber MLoadFixedSlot::valueHash() const {
6701 HashNumber hash = MUnaryInstruction::valueHash();
6702 hash = addU32ToHash(hash, slot());
6703 return hash;
6704}
6705
6706MDefinition* MLoadFixedSlot::foldsTo(TempAllocator& alloc) {
6707 if (MDefinition* def = foldsToStore(alloc)) {
6708 return def;
6709 }
6710
6711 return this;
6712}
6713
6714MDefinition::AliasType MLoadFixedSlotAndUnbox::mightAlias(
6715 const MDefinition* def) const {
6716 if (def->isStoreFixedSlot()) {
6717 const MStoreFixedSlot* store = def->toStoreFixedSlot();
6718 if (store->slot() != slot()) {
6719 return AliasType::NoAlias;
6720 }
6721 if (store->object() != object()) {
6722 return AliasType::MayAlias;
6723 }
6724 return AliasType::MustAlias;
6725 }
6726 return AliasType::MayAlias;
6727}
6728
6729MDefinition* MLoadFixedSlotAndUnbox::foldsTo(TempAllocator& alloc) {
6730 if (MDefinition* def = foldsToStore(alloc)) {
6731 return def;
6732 }
6733
6734 return this;
6735}
6736
6737MDefinition::AliasType MLoadDynamicSlot::mightAlias(
6738 const MDefinition* def) const {
6739 if (def->isStoreDynamicSlot()) {
6740 const MStoreDynamicSlot* store = def->toStoreDynamicSlot();
6741 if (store->slot() != slot()) {
6742 return AliasType::NoAlias;
6743 }
6744
6745 if (store->slots() != slots()) {
6746 return AliasType::MayAlias;
6747 }
6748
6749 return AliasType::MustAlias;
6750 }
6751 return AliasType::MayAlias;
6752}
6753
6754HashNumber MLoadDynamicSlot::valueHash() const {
6755 HashNumber hash = MUnaryInstruction::valueHash();
6756 hash = addU32ToHash(hash, slot_);
6757 return hash;
6758}
6759
6760MDefinition* MLoadDynamicSlot::foldsTo(TempAllocator& alloc) {
6761 if (MDefinition* def = foldsToStore(alloc)) {
6762 return def;
6763 }
6764
6765 return this;
6766}
6767
6768#ifdef JS_JITSPEW1
6769void MLoadDynamicSlot::printOpcode(GenericPrinter& out) const {
6770 MDefinition::printOpcode(out);
6771 out.printf(" (slot %u)", slot());
6772}
6773
6774void MLoadDynamicSlotAndUnbox::printOpcode(GenericPrinter& out) const {
6775 MDefinition::printOpcode(out);
6776 out.printf(" (slot %zu)", slot());
6777}
6778
6779void MStoreDynamicSlot::printOpcode(GenericPrinter& out) const {
6780 MDefinition::printOpcode(out);
6781 out.printf(" (slot %u)", slot());
6782}
6783
6784void MLoadFixedSlot::printOpcode(GenericPrinter& out) const {
6785 MDefinition::printOpcode(out);
6786 out.printf(" (slot %zu)", slot());
6787}
6788
6789void MLoadFixedSlotAndUnbox::printOpcode(GenericPrinter& out) const {
6790 MDefinition::printOpcode(out);
6791 out.printf(" (slot %zu)", slot());
6792}
6793
6794void MStoreFixedSlot::printOpcode(GenericPrinter& out) const {
6795 MDefinition::printOpcode(out);
6796 out.printf(" (slot %zu)", slot());
6797}
6798#endif
6799
6800MDefinition* MGuardFunctionScript::foldsTo(TempAllocator& alloc) {
6801 MDefinition* in = input();
6802 if (in->isLambda() &&
6803 in->toLambda()->templateFunction()->baseScript() == expected()) {
6804 return in;
6805 }
6806 return this;
6807}
6808
6809MDefinition* MFunctionEnvironment::foldsTo(TempAllocator& alloc) {
6810 if (input()->isLambda()) {
6811 return input()->toLambda()->environmentChain();
6812 }
6813 if (input()->isFunctionWithProto()) {
6814 return input()->toFunctionWithProto()->environmentChain();
6815 }
6816 return this;
6817}
6818
6819static bool AddIsANonZeroAdditionOf(MAdd* add, MDefinition* ins) {
6820 if (add->type() != MIRType::Int32 && add->type() != MIRType::Double) {
6821 return false;
6822 }
6823
6824 if (add->lhs() != ins && add->rhs() != ins) {
6825 return false;
6826 }
6827 MDefinition* other = (add->lhs() == ins) ? add->rhs() : add->lhs();
6828 if (!IsTypeRepresentableAsDouble(other->type())) {
6829 return false;
6830 }
6831 if (!other->isConstant()) {
6832 return false;
6833 }
6834 // The constant must be representable as a non-zero int32. Other doubles may
6835 // leave the index unchanged after conversion.
6836 int32_t n;
6837 if (!mozilla::NumberIsInt32(other->toConstant()->numberToDouble(), &n) ||
6838 n == 0) {
6839 return false;
6840 }
6841 return true;
6842}
6843
6844// Skip over instructions that usually appear between the actual index
6845// value being used and the MLoadElement.
6846// They don't modify the index value in a meaningful way.
6847static MDefinition* SkipUninterestingInstructions(MDefinition* ins) {
6848 // Drop the MToNumberInt32 added by the TypePolicy for double and float
6849 // values.
6850 if (ins->isToNumberInt32()) {
6851 return SkipUninterestingInstructions(ins->toToNumberInt32()->input());
6852 }
6853
6854 // Ignore the bounds check, which don't modify the index.
6855 if (ins->isBoundsCheck()) {
6856 return SkipUninterestingInstructions(ins->toBoundsCheck()->index());
6857 }
6858
6859 // Masking the index for Spectre-mitigation is not observable.
6860 if (ins->isSpectreMaskIndex()) {
6861 return SkipUninterestingInstructions(ins->toSpectreMaskIndex()->index());
6862 }
6863
6864 return ins;
6865}
6866
6867static bool DefinitelyDifferentValue(MDefinition* ins1, MDefinition* ins2) {
6868 ins1 = SkipUninterestingInstructions(ins1);
6869 ins2 = SkipUninterestingInstructions(ins2);
6870
6871 if (ins1 == ins2) {
6872 return false;
6873 }
6874
6875 // For constants check they are not equal.
6876 if (ins1->isConstant() && ins2->isConstant()) {
6877 MConstant* cst1 = ins1->toConstant();
6878 MConstant* cst2 = ins2->toConstant();
6879
6880 if (!cst1->isTypeRepresentableAsDouble() ||
6881 !cst2->isTypeRepresentableAsDouble()) {
6882 return false;
6883 }
6884
6885 // Be conservative and only allow values that fit into int32.
6886 int32_t n1, n2;
6887 if (!mozilla::NumberIsInt32(cst1->numberToDouble(), &n1) ||
6888 !mozilla::NumberIsInt32(cst2->numberToDouble(), &n2)) {
6889 return false;
6890 }
6891
6892 return n1 != n2;
6893 }
6894
6895 // Check if "ins1 = ins2 + cte", which would make both instructions
6896 // have different values.
6897 if (ins1->isAdd()) {
6898 if (AddIsANonZeroAdditionOf(ins1->toAdd(), ins2)) {
6899 return true;
6900 }
6901 }
6902 if (ins2->isAdd()) {
6903 if (AddIsANonZeroAdditionOf(ins2->toAdd(), ins1)) {
6904 return true;
6905 }
6906 }
6907
6908 return false;
6909}
6910
6911MDefinition::AliasType MLoadElement::mightAlias(const MDefinition* def) const {
6912 if (def->isStoreElement()) {
6913 const MStoreElement* store = def->toStoreElement();
6914 if (store->index() != index()) {
6915 if (DefinitelyDifferentValue(store->index(), index())) {
6916 return AliasType::NoAlias;
6917 }
6918 return AliasType::MayAlias;
6919 }
6920
6921 if (store->elements() != elements()) {
6922 return AliasType::MayAlias;
6923 }
6924
6925 return AliasType::MustAlias;
6926 }
6927 return AliasType::MayAlias;
6928}
6929
6930MDefinition* MLoadElement::foldsTo(TempAllocator& alloc) {
6931 if (MDefinition* def = foldsToStore(alloc)) {
6932 return def;
6933 }
6934
6935 return this;
6936}
6937
6938void MSqrt::trySpecializeFloat32(TempAllocator& alloc) {
6939 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
6940 setResultType(MIRType::Float32);
6941 specialization_ = MIRType::Float32;
6942 }
6943}
6944
6945MDefinition* MClz::foldsTo(TempAllocator& alloc) {
6946 if (num()->isConstant()) {
6947 MConstant* c = num()->toConstant();
6948 if (type() == MIRType::Int32) {
6949 uint32_t n = uint32_t(c->toInt32());
6950 return MConstant::NewInt32(alloc, std::countl_zero(n));
6951 }
6952 uint64_t n = uint64_t(c->toInt64());
6953 return MConstant::NewInt64(alloc, int64_t(std::countl_zero(n)));
6954 }
6955
6956 return this;
6957}
6958
6959MDefinition* MCtz::foldsTo(TempAllocator& alloc) {
6960 if (num()->isConstant()) {
6961 MConstant* c = num()->toConstant();
6962 if (type() == MIRType::Int32) {
6963 uint32_t n = uint32_t(num()->toConstant()->toInt32());
6964 return MConstant::NewInt32(alloc, std::countr_zero(n));
6965 }
6966 uint64_t n = uint64_t(c->toInt64());
6967 return MConstant::NewInt64(alloc, std::countr_zero(n));
6968 }
6969
6970 return this;
6971}
6972
6973MDefinition* MPopcnt::foldsTo(TempAllocator& alloc) {
6974 if (num()->isConstant()) {
6975 MConstant* c = num()->toConstant();
6976 if (type() == MIRType::Int32) {
6977 uint32_t n = uint32_t(num()->toConstant()->toInt32());
6978 return MConstant::NewInt32(alloc, std::popcount(n));
6979 }
6980 uint64_t n = uint64_t(c->toInt64());
6981 return MConstant::NewInt64(alloc, int64_t(std::popcount(n)));
6982 }
6983
6984 return this;
6985}
6986
6987MDefinition* MBoundsCheck::foldsTo(TempAllocator& alloc) {
6988 if (type() == MIRType::Int32 && index()->isConstant() &&
6989 length()->isConstant()) {
6990 uint32_t len = length()->toConstant()->toInt32();
6991 uint32_t idx = index()->toConstant()->toInt32();
6992 if (idx + uint32_t(minimum()) < len && idx + uint32_t(maximum()) < len) {
6993 return index();
6994 }
6995 }
6996
6997 return this;
6998}
6999
7000MDefinition* MTableSwitch::foldsTo(TempAllocator& alloc) {
7001 MDefinition* op = getOperand(0);
7002
7003 // If we only have one successor, convert to a plain goto to the only
7004 // successor. TableSwitch indices are numeric; other types will always go to
7005 // the only successor.
7006 if (numSuccessors() == 1 ||
7007 (op->type() != MIRType::Value && !IsNumberType(op->type()))) {
7008 return MGoto::New(alloc, getDefault());
7009 }
7010
7011 if (MConstant* opConst = op->maybeConstantValue()) {
7012 if (op->type() == MIRType::Int32) {
7013 int32_t i = opConst->toInt32() - low_;
7014 MBasicBlock* target;
7015 if (size_t(i) < numCases()) {
7016 target = getCase(size_t(i));
7017 } else {
7018 target = getDefault();
7019 }
7020 MOZ_ASSERT(target)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(target)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(target))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("target", "./../../../../js/src/jit/MIR.cpp"
, 7020); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { MOZ_CrashSequence(__null, 7020); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7021 return MGoto::New(alloc, target);
7022 }
7023 }
7024
7025 return this;
7026}
7027
7028MDefinition* MArrayJoin::foldsTo(TempAllocator& alloc) {
7029 MDefinition* arr = array();
7030
7031 if (!arr->isStringSplit()) {
7032 return this;
7033 }
7034
7035 setRecoveredOnBailout();
7036 if (arr->hasLiveDefUses()) {
7037 setNotRecoveredOnBailout();
7038 return this;
7039 }
7040
7041 // The MStringSplit won't generate any code.
7042 arr->setRecoveredOnBailout();
7043
7044 // We're replacing foo.split(bar).join(baz) by
7045 // foo.replace(bar, baz). MStringSplit could be recovered by
7046 // a bailout. As we are removing its last use, and its result
7047 // could be captured by a resume point, this MStringSplit will
7048 // be executed on the bailout path.
7049 MDefinition* string = arr->toStringSplit()->string();
7050 MDefinition* pattern = arr->toStringSplit()->separator();
7051 MDefinition* replacement = separator();
7052
7053 MStringReplace* substr =
7054 MStringReplace::New(alloc, string, pattern, replacement);
7055 substr->setFlatReplacement();
7056 return substr;
7057}
7058
7059MDefinition* MGetFirstDollarIndex::foldsTo(TempAllocator& alloc) {
7060 MDefinition* strArg = str();
7061 if (!strArg->isConstant()) {
7062 return this;
7063 }
7064
7065 JSOffThreadAtom* str = strArg->toConstant()->toString();
7066 int32_t index = GetFirstDollarIndexRawFlat(str);
7067 return MConstant::NewInt32(alloc, index);
7068}
7069
7070MDefinition::AliasType MSlots::mightAlias(const MDefinition* store) const {
7071 // ArrayPush only modifies object elements, but not object slots.
7072 if (store->isArrayPush()) {
7073 return AliasType::NoAlias;
7074 }
7075 return MInstruction::mightAlias(store);
7076}
7077
7078AliasSet MResizableTypedArrayLength::getAliasSet() const {
7079 // Loads the length and byteOffset slots, the shared-elements flag, the
7080 // auto-length fixed slot, and the shared raw-buffer length.
7081 auto flags = AliasSet::ArrayBufferViewLengthOrOffset |
7082 AliasSet::ObjectFields | AliasSet::FixedSlot |
7083 AliasSet::SharedArrayRawBufferLength;
7084
7085 // When a barrier is needed make the instruction effectful by giving it a
7086 // "store" effect. Also prevent reordering LoadUnboxedScalar before this
7087 // instruction by including |UnboxedElement| in the alias set.
7088 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) {
7089 return AliasSet::Store(flags | AliasSet::UnboxedElement);
7090 }
7091 return AliasSet::Load(flags);
7092}
7093
7094bool MResizableTypedArrayLength::congruentTo(const MDefinition* ins) const {
7095 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) {
7096 return false;
7097 }
7098 return congruentIfOperandsEqual(ins);
7099}
7100
7101AliasSet MResizableDataViewByteLength::getAliasSet() const {
7102 // Loads the length and byteOffset slots, the shared-elements flag, the
7103 // auto-length fixed slot, and the shared raw-buffer length.
7104 auto flags = AliasSet::ArrayBufferViewLengthOrOffset |
7105 AliasSet::ObjectFields | AliasSet::FixedSlot |
7106 AliasSet::SharedArrayRawBufferLength;
7107
7108 // When a barrier is needed make the instruction effectful by giving it a
7109 // "store" effect. Also prevent reordering LoadUnboxedScalar before this
7110 // instruction by including |UnboxedElement| in the alias set.
7111 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) {
7112 return AliasSet::Store(flags | AliasSet::UnboxedElement);
7113 }
7114 return AliasSet::Load(flags);
7115}
7116
7117bool MResizableDataViewByteLength::congruentTo(const MDefinition* ins) const {
7118 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) {
7119 return false;
7120 }
7121 return congruentIfOperandsEqual(ins);
7122}
7123
7124MDefinition* MGuardNumberToIntPtrIndex::foldsTo(TempAllocator& alloc) {
7125 MDefinition* input = this->input();
7126
7127 if (input->isToDouble() && input->getOperand(0)->type() == MIRType::Int32) {
7128 return MInt32ToIntPtr::New(alloc, input->getOperand(0));
7129 }
7130
7131 if (!input->isConstant()) {
7132 return this;
7133 }
7134
7135 // Fold constant double representable as intptr to intptr.
7136 int64_t ival;
7137 if (!mozilla::NumberEqualsInt64(input->toConstant()->toDouble(), &ival)) {
7138 // If not representable as an int64, this access is equal to an OOB access.
7139 // So replace it with a known int64/intptr value which also produces an OOB
7140 // access. If we don't support OOB accesses we have to bail out.
7141 if (!supportOOB()) {
7142 return this;
7143 }
7144 ival = -1;
7145 }
7146
7147 if (ival < INTPTR_MIN(-9223372036854775807L-1) || ival > INTPTR_MAX(9223372036854775807L)) {
7148 return this;
7149 }
7150
7151 return MConstant::NewIntPtr(alloc, intptr_t(ival));
7152}
7153
7154MDefinition* MIsObject::foldsTo(TempAllocator& alloc) {
7155 MDefinition* input = object();
7156 if (!input->isBox()) {
7157 return this;
7158 }
7159
7160 MDefinition* unboxed = input->toBox()->input();
7161 return MConstant::NewBoolean(alloc, unboxed->type() == MIRType::Object);
7162}
7163
7164MDefinition* MIsNullOrUndefined::foldsTo(TempAllocator& alloc) {
7165 // MIsNullOrUndefined doesn't have a type-policy, so the value can already be
7166 // unboxed.
7167 MDefinition* unboxed = value();
7168 if (unboxed->type() == MIRType::Value) {
7169 if (!unboxed->isBox()) {
7170 return this;
7171 }
7172 unboxed = unboxed->toBox()->input();
7173 }
7174
7175 return MConstant::NewBoolean(alloc, IsNullOrUndefined(unboxed->type()));
7176}
7177
7178MDefinition* MGuardValue::foldsTo(TempAllocator& alloc) {
7179 if (MConstant* cst = value()->maybeConstantValue()) {
7180 if (expected().isValue() && cst->toJSValue() == expected().toValue()) {
7181 return value();
7182 }
7183 }
7184
7185 return this;
7186}
7187
7188MDefinition* MGuardNullOrUndefined::foldsTo(TempAllocator& alloc) {
7189 MDefinition* input = value();
7190 if (!input->isBox()) {
7191 return this;
7192 }
7193
7194 MDefinition* unboxed = input->toBox()->input();
7195 if (IsNullOrUndefined(unboxed->type())) {
7196 return input;
7197 }
7198
7199 return this;
7200}
7201
7202MDefinition* MGuardIsNotObject::foldsTo(TempAllocator& alloc) {
7203 MDefinition* input = value();
7204 if (!input->isBox()) {
7205 return this;
7206 }
7207
7208 MDefinition* unboxed = input->toBox()->input();
7209 if (unboxed->type() == MIRType::Object) {
7210 return this;
7211 }
7212
7213 return input;
7214}
7215
7216MDefinition* MGuardObjectIdentity::foldsTo(TempAllocator& alloc) {
7217 if (object()->isConstant() && expected()->isConstant()) {
7218 JSObject* obj = &object()->toConstant()->toObject();
7219 JSObject* other = &expected()->toConstant()->toObject();
7220 if (!bailOnEquality()) {
7221 if (obj == other) {
7222 return object();
7223 }
7224 } else {
7225 if (obj != other) {
7226 return object();
7227 }
7228 }
7229 }
7230
7231 if (!bailOnEquality() && object()->isNurseryObject() &&
7232 expected()->isNurseryObject()) {
7233 uint32_t objIndex = object()->toNurseryObject()->nurseryObjectIndex();
7234 uint32_t otherIndex = expected()->toNurseryObject()->nurseryObjectIndex();
7235 if (objIndex == otherIndex) {
7236 return object();
7237 }
7238 }
7239
7240 return this;
7241}
7242
7243MDefinition* MGuardSpecificFunction::foldsTo(TempAllocator& alloc) {
7244 if (function()->isConstant() && expected()->isConstant()) {
7245 JSObject* fun = &function()->toConstant()->toObject();
7246 JSObject* other = &expected()->toConstant()->toObject();
7247 if (fun == other) {
7248 return function();
7249 }
7250 }
7251
7252 if (function()->isNurseryObject() && expected()->isNurseryObject()) {
7253 uint32_t funIndex = function()->toNurseryObject()->nurseryObjectIndex();
7254 uint32_t otherIndex = expected()->toNurseryObject()->nurseryObjectIndex();
7255 if (funIndex == otherIndex) {
7256 return function();
7257 }
7258 }
7259
7260 return this;
7261}
7262
7263MDefinition* MGuardSpecificAtom::foldsTo(TempAllocator& alloc) {
7264 if (str()->isConstant()) {
7265 JSOffThreadAtom* s = str()->toConstant()->toString();
7266 if (s == atom()) {
7267 return str();
7268 }
7269 }
7270
7271 return this;
7272}
7273
7274MDefinition* MGuardSpecificSymbol::foldsTo(TempAllocator& alloc) {
7275 if (symbol()->isConstant()) {
7276 if (symbol()->toConstant()->toSymbol() == expected()) {
7277 return symbol();
7278 }
7279 }
7280
7281 return this;
7282}
7283
7284MDefinition* MGuardSpecificInt32::foldsTo(TempAllocator& alloc) {
7285 if (num()->isConstant() && num()->toConstant()->isInt32(expected())) {
7286 return num();
7287 }
7288 return this;
7289}
7290
7291MDefinition* MGuardShape::foldsTo(TempAllocator& alloc) {
7292 if (object()->isGuardShape() &&
7293 shape() == object()->toGuardShape()->shape() && dependency() &&
7294 object()->dependency() == dependency()) {
7295 return object();
7296 }
7297 return this;
7298}
7299
7300bool MGuardShapeList::congruentTo(const MDefinition* ins) const {
7301 if (!congruentIfOperandsEqual(ins)) {
7302 return false;
7303 }
7304
7305 // Returns true iff all non-nullptr shapes in |a| are also in |b|.
7306 auto hasAllShapes = [](const auto& a, const auto& b) {
7307 for (Shape* shape : a) {
7308 if (!shape) {
7309 continue;
7310 }
7311 auto isSameShape = [shape](Shape* other) { return shape == other; };
7312 if (!std::any_of(b.begin(), b.end(), isSameShape)) {
7313 return false;
7314 }
7315 }
7316 return true;
7317 };
7318
7319 // Return true if all shapes in |shapesA| are also in |shapesB| and vice
7320 // versa.
7321 const auto& shapesA = this->shapeList()->shapes();
7322 const auto& shapesB = ins->toGuardShapeList()->shapeList()->shapes();
7323 return hasAllShapes(shapesA, shapesB) && hasAllShapes(shapesB, shapesA);
7324}
7325
7326bool MGuardShapeListToOffset::congruentTo(const MDefinition* ins) const {
7327 if (!congruentIfOperandsEqual(ins)) {
7328 return false;
7329 }
7330
7331 const auto& shapesA = this->shapeList()->shapes();
7332 const auto& shapesB = ins->toGuardShapeListToOffset()->shapeList()->shapes();
7333 if (!std::equal(shapesA.begin(), shapesA.end(), shapesB.begin(),
7334 shapesB.end()))
7335 return false;
7336
7337 const auto& offsetsA = this->shapeList()->offsets();
7338 const auto& offsetsB =
7339 ins->toGuardShapeListToOffset()->shapeList()->offsets();
7340 return std::equal(offsetsA.begin(), offsetsA.end(), offsetsB.begin(),
7341 offsetsB.end());
7342}
7343
7344MDefinition::AliasType MGuardShape::mightAlias(const MDefinition* store) const {
7345 // These instructions only modify object elements, but not the shape.
7346 if (store->isStoreElementHole() || store->isArrayPush()) {
7347 return AliasType::NoAlias;
7348 }
7349 if (object()->isConstantProto()) {
7350 const MDefinition* receiverObject =
7351 object()->toConstantProto()->getReceiverObject();
7352 switch (store->op()) {
7353 case MDefinition::Opcode::StoreFixedSlot:
7354 if (store->toStoreFixedSlot()->object()->skipObjectGuards() ==
7355 receiverObject) {
7356 return AliasType::NoAlias;
7357 }
7358 break;
7359 case MDefinition::Opcode::StoreDynamicSlot:
7360 if (store->toStoreDynamicSlot()
7361 ->slots()
7362 ->toSlots()
7363 ->object()
7364 ->skipObjectGuards() == receiverObject) {
7365 return AliasType::NoAlias;
7366 }
7367 break;
7368 case MDefinition::Opcode::AddAndStoreSlot:
7369 if (store->toAddAndStoreSlot()->object()->skipObjectGuards() ==
7370 receiverObject) {
7371 return AliasType::NoAlias;
7372 }
7373 break;
7374 case MDefinition::Opcode::AllocateAndStoreSlot:
7375 if (store->toAllocateAndStoreSlot()->object()->skipObjectGuards() ==
7376 receiverObject) {
7377 return AliasType::NoAlias;
7378 }
7379 break;
7380 default:
7381 break;
7382 }
7383 }
7384 return MInstruction::mightAlias(store);
7385}
7386
7387MDefinition* MGuardIsNotProxy::foldsTo(TempAllocator& alloc) {
7388 KnownClass known = GetObjectKnownClass(object());
7389 if (known == KnownClass::None) {
7390 return this;
7391 }
7392
7393 MOZ_ASSERT(!GetObjectKnownJSClass(object())->isProxyObject())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!GetObjectKnownJSClass(object())->isProxyObject()
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!GetObjectKnownJSClass(object())->isProxyObject()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!GetObjectKnownJSClass(object())->isProxyObject()", "./../../../../js/src/jit/MIR.cpp"
, 7393); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetObjectKnownJSClass(object())->isProxyObject()"
")"); do { MOZ_CrashSequence(__null, 7393); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7394 AssertKnownClass(alloc, this, object());
7395 return object();
7396}
7397
7398static PropertyKey ToNonIntPropertyKey(MDefinition* idval) {
7399 MConstant* constant = idval->maybeConstantValue();
7400 if (!constant) {
7401 return PropertyKey::Void();
7402 }
7403 if (constant->type() == MIRType::String) {
7404 JSOffThreadAtom* str = constant->toString();
7405 if (str->isIndex()) {
7406 return PropertyKey::Void();
7407 }
7408 return PropertyKey::NonIntAtom(str->unwrap());
7409 }
7410 if (constant->type() == MIRType::Symbol) {
7411 return PropertyKey::Symbol(constant->toSymbol());
7412 }
7413 return PropertyKey::Void();
7414}
7415
7416MDefinition* MMegamorphicLoadSlotByValue::foldsTo(TempAllocator& alloc) {
7417 PropertyKey id = ToNonIntPropertyKey(idVal());
7418 if (id.isVoid()) {
7419 return this;
7420 }
7421
7422 auto* result = MMegamorphicLoadSlot::New(alloc, object(), id);
7423 result->setDependency(dependency());
7424 return result;
7425}
7426
7427MDefinition* MMegamorphicLoadSlotByValuePermissive::foldsTo(
7428 TempAllocator& alloc) {
7429 PropertyKey id = ToNonIntPropertyKey(idVal());
7430 if (id.isVoid()) {
7431 return this;
7432 }
7433
7434 auto* result = MMegamorphicLoadSlotPermissive::New(alloc, object(), id);
7435 result->stealResumePoint(this);
7436 return result;
7437}
7438
7439HashNumber MNurseryObject::valueHash() const {
7440 HashNumber hash = MNullaryInstruction::valueHash();
7441 hash = addU32ToHash(hash, nurseryObjectIndex());
7442 return hash;
7443}
7444
7445bool MGuardFunctionScript::congruentTo(const MDefinition* ins) const {
7446 if (!ins->isGuardFunctionScript()) {
7447 return false;
7448 }
7449 if (expected() != ins->toGuardFunctionScript()->expected()) {
7450 return false;
7451 }
7452 return congruentIfOperandsEqual(ins);
7453}
7454
7455AliasSet MGuardFunctionScript::getAliasSet() const {
7456 // A JSFunction's BaseScript pointer is immutable. Relazification of
7457 // top-level/named self-hosted functions is an exception to this, but we don't
7458 // use this guard for those self-hosted functions.
7459 // See IRGenerator::emitCalleeGuard.
7460 MOZ_ASSERT_IF(flags_.isSelfHostedOrIntrinsic(), flags_.isLambda())do { if (flags_.isSelfHostedOrIntrinsic()) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(flags_.
isLambda())>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(flags_.isLambda()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("flags_.isLambda()"
, "./../../../../js/src/jit/MIR.cpp", 7460); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "flags_.isLambda()" ")"); do { MOZ_CrashSequence
(__null, 7460); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
7461 return AliasSet::None();
7462}
7463
7464MDefinition* MGuardStringToIndex::foldsTo(TempAllocator& alloc) {
7465 if (!string()->isConstant()) {
7466 return this;
7467 }
7468
7469 JSOffThreadAtom* str = string()->toConstant()->toString();
7470
7471 uint32_t index = UINT32_MAX(4294967295U);
7472 if (!str->isIndex(&index) || index > INT32_MAX(2147483647)) {
7473 return this;
7474 }
7475
7476 return MConstant::NewInt32(alloc, index);
7477}
7478
7479MDefinition* MGuardStringToInt32::foldsTo(TempAllocator& alloc) {
7480 if (!string()->isConstant()) {
7481 return this;
7482 }
7483
7484 JSOffThreadAtom* str = string()->toConstant()->toString();
7485 double number = OffThreadAtomToNumber(str);
7486
7487 int32_t n;
7488 if (!mozilla::NumberIsInt32(number, &n)) {
7489 return this;
7490 }
7491
7492 return MConstant::NewInt32(alloc, n);
7493}
7494
7495MDefinition* MGuardStringToDouble::foldsTo(TempAllocator& alloc) {
7496 if (!string()->isConstant()) {
7497 return this;
7498 }
7499
7500 JSOffThreadAtom* str = string()->toConstant()->toString();
7501 double number = OffThreadAtomToNumber(str);
7502 return MConstant::NewDouble(alloc, number);
7503}
7504
7505MDefinition* MGuardToClass::foldsTo(TempAllocator& alloc) {
7506 const JSClass* clasp = GetObjectKnownJSClass(object());
7507 if (!clasp || getClass() != clasp) {
7508 return this;
7509 }
7510
7511 AssertKnownClass(alloc, this, object());
7512 return object();
7513}
7514
7515MDefinition* MGuardToFunction::foldsTo(TempAllocator& alloc) {
7516 if (GetObjectKnownClass(object()) != KnownClass::Function) {
7517 return this;
7518 }
7519
7520 AssertKnownClass(alloc, this, object());
7521 return object();
7522}
7523
7524MDefinition* MHasClass::foldsTo(TempAllocator& alloc) {
7525 const JSClass* clasp = GetObjectKnownJSClass(object());
7526 if (!clasp) {
7527 return this;
7528 }
7529
7530 AssertKnownClass(alloc, this, object());
7531 return MConstant::NewBoolean(alloc, getClass() == clasp);
7532}
7533
7534MDefinition* MIsCallable::foldsTo(TempAllocator& alloc) {
7535 if (input()->type() != MIRType::Object) {
7536 return this;
7537 }
7538
7539 KnownClass known = GetObjectKnownClass(input());
7540 if (known == KnownClass::None) {
7541 return this;
7542 }
7543
7544 AssertKnownClass(alloc, this, input());
7545 return MConstant::NewBoolean(alloc, known == KnownClass::Function);
7546}
7547
7548MDefinition* MIsArray::foldsTo(TempAllocator& alloc) {
7549 if (input()->type() != MIRType::Object) {
7550 return this;
7551 }
7552
7553 KnownClass known = GetObjectKnownClass(input());
7554 if (known == KnownClass::None) {
7555 return this;
7556 }
7557
7558 AssertKnownClass(alloc, this, input());
7559 return MConstant::NewBoolean(alloc, known == KnownClass::Array);
7560}
7561
7562MDefinition* MGuardIsNotArrayBufferMaybeShared::foldsTo(TempAllocator& alloc) {
7563 switch (GetObjectKnownClass(object())) {
7564 case KnownClass::PlainObject:
7565 case KnownClass::Array:
7566 case KnownClass::Function:
7567 case KnownClass::RegExp:
7568 case KnownClass::Date:
7569 case KnownClass::ArrayIterator:
7570 case KnownClass::StringIterator:
7571 case KnownClass::RegExpStringIterator: {
7572 AssertKnownClass(alloc, this, object());
7573 return object();
7574 }
7575 case KnownClass::None:
7576 break;
7577 }
7578
7579 return this;
7580}
7581
7582MDefinition* MCheckIsObj::foldsTo(TempAllocator& alloc) {
7583 if (!input()->isBox()) {
7584 return this;
7585 }
7586
7587 MDefinition* unboxed = input()->toBox()->input();
7588 if (unboxed->type() == MIRType::Object) {
7589 return unboxed;
7590 }
7591
7592 return this;
7593}
7594
7595static bool IsBoxedObject(MDefinition* def) {
7596 MOZ_ASSERT(def->type() == MIRType::Value)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(def->type() == MIRType::Value)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(def->type() == MIRType::Value
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"def->type() == MIRType::Value", "./../../../../js/src/jit/MIR.cpp"
, 7596); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->type() == MIRType::Value"
")"); do { MOZ_CrashSequence(__null, 7596); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7597
7598 if (def->isBox()) {
7599 return def->toBox()->input()->type() == MIRType::Object;
7600 }
7601
7602 // Construct calls are always returning a boxed object.
7603 //
7604 // TODO: We should consider encoding this directly in the graph instead of
7605 // having to special case it here.
7606 if (def->isCall()) {
7607 return def->toCall()->isConstructing();
7608 }
7609 if (def->isConstructArray()) {
7610 return true;
7611 }
7612 if (def->isConstructArgs()) {
7613 return true;
7614 }
7615
7616 return false;
7617}
7618
7619MDefinition* MCheckReturn::foldsTo(TempAllocator& alloc) {
7620 auto* returnVal = returnValue();
7621 if (!returnVal->isBox()) {
7622 return this;
7623 }
7624
7625 auto* unboxedReturnVal = returnVal->toBox()->input();
7626 if (unboxedReturnVal->type() == MIRType::Object) {
7627 return returnVal;
7628 }
7629
7630 if (unboxedReturnVal->type() != MIRType::Undefined) {
7631 return this;
7632 }
7633
7634 auto* thisVal = thisValue();
7635 if (IsBoxedObject(thisVal)) {
7636 return thisVal;
7637 }
7638
7639 return this;
7640}
7641
7642MDefinition* MCheckThis::foldsTo(TempAllocator& alloc) {
7643 MDefinition* input = thisValue();
7644 if (!input->isBox()) {
7645 return this;
7646 }
7647
7648 MDefinition* unboxed = input->toBox()->input();
7649 if (IsMagicType(unboxed->type())) {
7650 return this;
7651 }
7652
7653 return input;
7654}
7655
7656MDefinition* MCheckThisReinit::foldsTo(TempAllocator& alloc) {
7657 MDefinition* input = thisValue();
7658 if (!input->isBox()) {
7659 return this;
7660 }
7661
7662 MDefinition* unboxed = input->toBox()->input();
7663 if (unboxed->type() != MIRType::MagicUninitializedLexical) {
7664 return this;
7665 }
7666
7667 return input;
7668}
7669
7670MDefinition* MCheckObjCoercible::foldsTo(TempAllocator& alloc) {
7671 MDefinition* input = checkValue();
7672 if (!input->isBox()) {
7673 return this;
7674 }
7675
7676 MDefinition* unboxed = input->toBox()->input();
7677 if (IsNullOrUndefined(unboxed->type())) {
7678 return this;
7679 }
7680
7681 return input;
7682}
7683
7684MDefinition* MGuardInt32IsNonNegative::foldsTo(TempAllocator& alloc) {
7685 MOZ_ASSERT(index()->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(index()->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(index()->type() == MIRType
::Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("index()->type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 7685); AnnotateMozCrashReason("MOZ_ASSERT" "(" "index()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 7685); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7686
7687 MDefinition* input = index();
7688 if (!input->isConstant() || input->toConstant()->toInt32() < 0) {
7689 return this;
7690 }
7691 return input;
7692}
7693
7694MDefinition* MGuardIntPtrIsNonNegative::foldsTo(TempAllocator& alloc) {
7695 MOZ_ASSERT(index()->type() == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(index()->type() == MIRType::IntPtr)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(index()->type() == MIRType::IntPtr))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("index()->type() == MIRType::IntPtr"
, "./../../../../js/src/jit/MIR.cpp", 7695); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "index()->type() == MIRType::IntPtr" ")"
); do { MOZ_CrashSequence(__null, 7695); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7696
7697 MDefinition* input = index();
7698 if (!input->isConstant() || input->toConstant()->toIntPtr() < 0) {
7699 return this;
7700 }
7701 return input;
7702}
7703
7704MDefinition* MGuardInt32Range::foldsTo(TempAllocator& alloc) {
7705 MOZ_ASSERT(input()->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(input()->type() == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(input()->type() == MIRType
::Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("input()->type() == MIRType::Int32", "./../../../../js/src/jit/MIR.cpp"
, 7705); AnnotateMozCrashReason("MOZ_ASSERT" "(" "input()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 7705); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7706 MOZ_ASSERT(minimum() <= maximum())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(minimum() <= maximum())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(minimum() <= maximum())))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("minimum() <= maximum()"
, "./../../../../js/src/jit/MIR.cpp", 7706); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "minimum() <= maximum()" ")"); do { MOZ_CrashSequence
(__null, 7706); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7707
7708 MDefinition* in = input();
7709 if (!in->isConstant()) {
7710 return this;
7711 }
7712 int32_t cst = in->toConstant()->toInt32();
7713 if (cst < minimum() || cst > maximum()) {
7714 return this;
7715 }
7716 return in;
7717}
7718
7719MDefinition* MGuardNonGCThing::foldsTo(TempAllocator& alloc) {
7720 if (!input()->isBox()) {
7721 return this;
7722 }
7723
7724 MDefinition* unboxed = input()->toBox()->input();
7725 if (!IsNonGCThing(unboxed->type())) {
7726 return this;
7727 }
7728 return input();
7729}
7730
7731MBindFunction* MBindFunction::New(TempAllocator& alloc, MDefinition* target,
7732 uint32_t argc, JSObject* templateObj) {
7733 auto* ins = new (alloc) MBindFunction(templateObj);
7734 if (!ins->init(alloc, NumNonArgumentOperands + argc)) {
7735 return nullptr;
7736 }
7737 ins->initOperand(0, target);
7738 return ins;
7739}
7740
7741MCreateInlinedArgumentsObject* MCreateInlinedArgumentsObject::New(
7742 TempAllocator& alloc, MDefinition* callObj, MDefinition* callee,
7743 MDefinitionVector& args, ArgumentsObject* templateObj) {
7744 MCreateInlinedArgumentsObject* ins =
7745 new (alloc) MCreateInlinedArgumentsObject(templateObj);
7746
7747 uint32_t argc = args.length();
7748 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(argc <= ArgumentsObject::MaxInlinedArgs)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(argc <= ArgumentsObject::MaxInlinedArgs))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("argc <= ArgumentsObject::MaxInlinedArgs"
, "./../../../../js/src/jit/MIR.cpp", 7748); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc <= ArgumentsObject::MaxInlinedArgs"
")"); do { MOZ_CrashSequence(__null, 7748); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7749
7750 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7751 return nullptr;
7752 }
7753
7754 ins->initOperand(0, callObj);
7755 ins->initOperand(1, callee);
7756 for (uint32_t i = 0; i < argc; i++) {
7757 ins->initOperand(i + NumNonArgumentOperands, args[i]);
7758 }
7759
7760 return ins;
7761}
7762
7763MGetInlinedArgument* MGetInlinedArgument::New(
7764 TempAllocator& alloc, MDefinition* index,
7765 MCreateInlinedArgumentsObject* args) {
7766 MGetInlinedArgument* ins = new (alloc) MGetInlinedArgument();
7767
7768 uint32_t argc = args->numActuals();
7769 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(argc <= ArgumentsObject::MaxInlinedArgs)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(argc <= ArgumentsObject::MaxInlinedArgs))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("argc <= ArgumentsObject::MaxInlinedArgs"
, "./../../../../js/src/jit/MIR.cpp", 7769); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc <= ArgumentsObject::MaxInlinedArgs"
")"); do { MOZ_CrashSequence(__null, 7769); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7770
7771 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7772 return nullptr;
7773 }
7774
7775 ins->initOperand(0, index);
7776 for (uint32_t i = 0; i < argc; i++) {
7777 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i));
7778 }
7779
7780 return ins;
7781}
7782
7783MGetInlinedArgument* MGetInlinedArgument::New(TempAllocator& alloc,
7784 MDefinition* index,
7785 const CallInfo& callInfo) {
7786 MGetInlinedArgument* ins = new (alloc) MGetInlinedArgument();
7787
7788 uint32_t argc = callInfo.argc();
7789 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(argc <= ArgumentsObject::MaxInlinedArgs)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(argc <= ArgumentsObject::MaxInlinedArgs))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("argc <= ArgumentsObject::MaxInlinedArgs"
, "./../../../../js/src/jit/MIR.cpp", 7789); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc <= ArgumentsObject::MaxInlinedArgs"
")"); do { MOZ_CrashSequence(__null, 7789); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7790
7791 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7792 return nullptr;
7793 }
7794
7795 ins->initOperand(0, index);
7796 for (uint32_t i = 0; i < argc; i++) {
7797 ins->initOperand(i + NumNonArgumentOperands, callInfo.getArg(i));
7798 }
7799
7800 return ins;
7801}
7802
7803MDefinition* MGetInlinedArgument::foldsTo(TempAllocator& alloc) {
7804 MDefinition* indexDef = SkipUninterestingInstructions(index());
7805 if (!indexDef->isConstant() || indexDef->type() != MIRType::Int32) {
7806 return this;
7807 }
7808
7809 int32_t indexConst = indexDef->toConstant()->toInt32();
7810 if (indexConst < 0 || uint32_t(indexConst) >= numActuals()) {
7811 return this;
7812 }
7813
7814 MDefinition* arg = getArg(indexConst);
7815 if (arg->type() != MIRType::Value) {
7816 arg = MBox::New(alloc, arg);
7817 }
7818
7819 return arg;
7820}
7821
7822MGetInlinedArgumentHole* MGetInlinedArgumentHole::New(
7823 TempAllocator& alloc, MDefinition* index,
7824 MCreateInlinedArgumentsObject* args) {
7825 auto* ins = new (alloc) MGetInlinedArgumentHole();
7826
7827 uint32_t argc = args->numActuals();
7828 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(argc <= ArgumentsObject::MaxInlinedArgs)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(argc <= ArgumentsObject::MaxInlinedArgs))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("argc <= ArgumentsObject::MaxInlinedArgs"
, "./../../../../js/src/jit/MIR.cpp", 7828); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc <= ArgumentsObject::MaxInlinedArgs"
")"); do { MOZ_CrashSequence(__null, 7828); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7829
7830 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7831 return nullptr;
7832 }
7833
7834 ins->initOperand(0, index);
7835 for (uint32_t i = 0; i < argc; i++) {
7836 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i));
7837 }
7838
7839 return ins;
7840}
7841
7842MDefinition* MGetInlinedArgumentHole::foldsTo(TempAllocator& alloc) {
7843 MDefinition* indexDef = SkipUninterestingInstructions(index());
7844 if (!indexDef->isConstant() || indexDef->type() != MIRType::Int32) {
7845 return this;
7846 }
7847
7848 int32_t indexConst = indexDef->toConstant()->toInt32();
7849 if (indexConst < 0) {
7850 return this;
7851 }
7852
7853 MDefinition* arg;
7854 if (uint32_t(indexConst) < numActuals()) {
7855 arg = getArg(indexConst);
7856
7857 if (arg->type() != MIRType::Value) {
7858 arg = MBox::New(alloc, arg);
7859 }
7860 } else {
7861 auto* undefined = MConstant::NewUndefined(alloc);
7862 block()->insertBefore(this, undefined);
7863
7864 arg = MBox::New(alloc, undefined);
7865 }
7866
7867 return arg;
7868}
7869
7870MInlineArgumentsSlice* MInlineArgumentsSlice::New(
7871 TempAllocator& alloc, MDefinition* begin, MDefinition* count,
7872 MCreateInlinedArgumentsObject* args, JSObject* templateObj,
7873 gc::Heap initialHeap) {
7874 auto* ins = new (alloc) MInlineArgumentsSlice(templateObj, initialHeap);
7875
7876 uint32_t argc = args->numActuals();
7877 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(argc <= ArgumentsObject::MaxInlinedArgs)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(argc <= ArgumentsObject::MaxInlinedArgs))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("argc <= ArgumentsObject::MaxInlinedArgs"
, "./../../../../js/src/jit/MIR.cpp", 7877); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc <= ArgumentsObject::MaxInlinedArgs"
")"); do { MOZ_CrashSequence(__null, 7877); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7878
7879 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7880 return nullptr;
7881 }
7882
7883 ins->initOperand(0, begin);
7884 ins->initOperand(1, count);
7885 for (uint32_t i = 0; i < argc; i++) {
7886 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i));
7887 }
7888
7889 return ins;
7890}
7891
7892MDefinition* MNormalizeSliceTerm::foldsTo(TempAllocator& alloc) {
7893 auto* length = this->length();
7894 if (!length->isConstant() && !length->isArgumentsLength()) {
7895 return this;
7896 }
7897
7898 if (length->isConstant()) {
7899 int32_t lengthConst = length->toConstant()->toInt32();
7900 MOZ_ASSERT(lengthConst >= 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lengthConst >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lengthConst >= 0))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("lengthConst >= 0"
, "./../../../../js/src/jit/MIR.cpp", 7900); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lengthConst >= 0" ")"); do { MOZ_CrashSequence
(__null, 7900); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7901
7902 // Result is always zero when |length| is zero.
7903 if (lengthConst == 0) {
7904 return length;
7905 }
7906
7907 auto* value = this->value();
7908 if (value->isConstant()) {
7909 int32_t valueConst = value->toConstant()->toInt32();
7910
7911 int32_t normalized;
7912 if (valueConst < 0) {
7913 normalized = std::max(valueConst + lengthConst, 0);
7914 } else {
7915 normalized = std::min(valueConst, lengthConst);
7916 }
7917
7918 if (normalized == valueConst) {
7919 return value;
7920 }
7921 if (normalized == lengthConst) {
7922 return length;
7923 }
7924 return MConstant::NewInt32(alloc, normalized);
7925 }
7926
7927 return this;
7928 }
7929
7930 auto* value = this->value();
7931 if (value->isConstant()) {
7932 int32_t valueConst = value->toConstant()->toInt32();
7933
7934 // Minimum of |value| and |length|.
7935 if (valueConst > 0) {
7936 return MMinMax::NewMin(alloc, value, length, MIRType::Int32);
7937 }
7938
7939 // Maximum of |value + length| and zero.
7940 if (valueConst < 0) {
7941 // Safe to truncate because |length| is never negative.
7942 auto* add = MAdd::New(alloc, value, length, TruncateKind::Truncate);
7943 block()->insertBefore(this, add);
7944
7945 auto* zero = MConstant::NewInt32(alloc, 0);
7946 block()->insertBefore(this, zero);
7947
7948 return MMinMax::NewMax(alloc, add, zero, MIRType::Int32);
7949 }
7950
7951 // Directly return the value when it's zero.
7952 return value;
7953 }
7954
7955 // Normalizing MArgumentsLength is a no-op.
7956 if (value->isArgumentsLength()) {
7957 return value;
7958 }
7959
7960 return this;
7961}
7962
7963MDefinition* MToIntegerIndex::foldsTo(TempAllocator& alloc) {
7964 // |length| is guaranteed to be a non-negative value.
7965
7966 auto* index = this->index();
7967 auto* length = this->length();
7968
7969 if (index == length) {
7970 return index;
7971 }
7972
7973 if (index->isConstant()) {
7974 intptr_t indexConst = index->toConstant()->toIntPtr();
7975
7976 // Minimum of |index| and |length|.
7977 if (indexConst > 0) {
7978 return MMinMax::NewMin(alloc, index, length, MIRType::IntPtr);
7979 }
7980
7981 // Maximum of |value + length| and zero.
7982 if (indexConst < 0) {
7983 auto* add = MAdd::New(alloc, index, length, MIRType::IntPtr);
7984 block()->insertBefore(this, add);
7985
7986 auto* zero = MConstant::NewIntPtr(alloc, 0);
7987 block()->insertBefore(this, zero);
7988
7989 return MMinMax::NewMax(alloc, add, zero, MIRType::IntPtr);
7990 }
7991
7992 // Directly return the index when it's zero.
7993 return index;
7994 }
7995
7996 return this;
7997}
7998
7999MDefinition* MDateParse::foldsTo(TempAllocator& alloc) {
8000 auto* string = this->string();
8001 if (!string->isConstant()) {
8002 return this;
8003 }
8004 JSOffThreadAtom* str = string->toConstant()->toString();
8005
8006 ParsedDate parsed;
8007 if (!DateParse(str, &parsed)) {
8008 // Can't parse as date, always NaN.
8009 return MConstant::NewDouble(alloc, JS::GenericNaN());
8010 }
8011 auto [date, isLocalTime] = parsed;
8012
8013 if (isLocalTime) {
8014 auto* localTime = MConstant::NewInt64(alloc, date);
8015 block()->insertBefore(this, localTime);
8016 return MLocalTimeToUTC::New(alloc, localTime);
8017 }
8018
8019 MOZ_ASSERT(JS::TimeClip(date).isValid())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(JS::TimeClip(date).isValid())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(JS::TimeClip(date).isValid()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"JS::TimeClip(date).isValid()", "./../../../../js/src/jit/MIR.cpp"
, 8019); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JS::TimeClip(date).isValid()"
")"); do { MOZ_CrashSequence(__null, 8019); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8020 return MConstant::NewDouble(alloc, double(date));
8021}
8022
8023MDefinition* MTimeClip::foldsTo(TempAllocator& alloc) {
8024 auto* time = this->time();
8025 if (!time->isConstant()) {
8026 return this;
8027 }
8028
8029 // NB: TimeClip can return non-canonicalize doubles.
8030 auto clipped = JS::TimeClip(time->toConstant()->toDouble());
8031 return MConstant::NewDouble(alloc, JS::CanonicalizeNaN(clipped.toDouble()));
8032}
8033
8034JSOp MBinaryCache::jsop() const { return JSOp(*resumePoint()->pc()); }
8035
8036template <typename T>
8037static wasm::MaybeRefType GetBaseRefTypeForWasmLoadOrStore(T ins) {
8038 const MDefinition* structObject;
8039 if (ins->base()->type() == MIRType::WasmStructData &&
8040 ins->base()->isWasmLoadField()) {
8041 // Struct data pointers have no ref type, but always come from some struct
8042 // object, so go back to that base object if possible. (We cannot do this
8043 // 100% of the time because of phis.)
8044 structObject = ins->base()->toWasmLoadField()->base();
8045 } else {
8046 structObject = ins->base();
8047 }
8048 return structObject->wasmRefType().asNonNullable();
8049}
8050
8051// Wasm loads and stores can be proven not to alias if their offsets are
8052// different or their ref types are known and disjoint. (Disjoint alias sets
8053// also mean no aliasing, but this is obvious because that's just what alias
8054// sets already do.)
8055//
8056// Different offsets -> NoAlias is true because each field (whether GC data or
8057// internal data) has one and only one offset that is used to access it.
8058// Disjoint types -> NoAlias is true because, well, types. When considering
8059// types here, we exclude null because null loads and stores will trap anyway.
8060//
8061// For more rationale, see bug 2061530.
8062MDefinition::AliasType MWasmLoadField::mightAlias(
8063 const MDefinition* ins) const {
8064 if (!(getAliasSet().flags() & ins->getAliasSet().flags())) {
8065 return AliasType::NoAlias;
8066 }
8067 MOZ_ASSERT(!isEffectful() && ins->isEffectful())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isEffectful() && ins->isEffectful())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!isEffectful() && ins->isEffectful()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!isEffectful() && ins->isEffectful()"
, "./../../../../js/src/jit/MIR.cpp", 8067); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isEffectful() && ins->isEffectful()"
")"); do { MOZ_CrashSequence(__null, 8067); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8068
8069 wasm::MaybeRefType insType;
8070 uint32_t insOffset;
8071 if (ins->isWasmStoreField()) {
8072 const MWasmStoreField* store = ins->toWasmStoreField();
8073 insType = GetBaseRefTypeForWasmLoadOrStore(store);
8074 insOffset = store->offset();
8075 } else if (ins->isWasmStoreFieldRef()) {
8076 const MWasmStoreFieldRef* store = ins->toWasmStoreFieldRef();
8077 insType = GetBaseRefTypeForWasmLoadOrStore(store);
8078 insOffset = store->offset();
8079 } else {
8080 // Safe default, but any other type of store that can operate on the same
8081 // values as a (performance-sensitive) MWasmLoadField should probably be
8082 // added above.
8083 return AliasType::MayAlias;
8084 }
8085
8086 wasm::MaybeRefType thisType = GetBaseRefTypeForWasmLoadOrStore(this);
8087 if (offset() != insOffset ||
8088 !wasm::MaybeRefType::mayHaveValuesInCommon(thisType, insType)) {
8089 return AliasType::NoAlias;
8090 }
8091
8092 return AliasType::MayAlias;
8093}