Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/jit/./../../../../js/src/jit/MIR.cpp
Warning:line 1587, 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-09-01-224014-2642839-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(WasmUnsignedToDouble
)NAME(WasmUnsignedToFloat32)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(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(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 UnsigedInt = 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 = UnsigedInt(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 disabled. (Wasm has bailouts disabled.)
212 if (lhs < 0 && rhs == 0 && !ins->toUrsh()->bailoutsDisabled()) {
213 return nullptr;
214 }
215 ret = UnsigedInt(lhs) >> (UnsigedInt(rhs) & shiftMask);
216 break;
217 case MDefinition::Opcode::BigIntPtrLsh:
218 case MDefinition::Opcode::BigIntPtrRsh: {
219 // BigIntPtr shifts are special:
220 // 1. Excess shift amounts produce BigInt larger than IntPtr.
221 // 2. Negative shifts reverse the shift direction.
222
223 // Decline folding for excess shift amounts.
224 UnsigedInt shift = mozilla::Abs(rhs);
225 if ((shift & shiftMask) != shift) {
226 return nullptr;
227 }
228
229 bool isLsh = (ins->isBigIntPtrLsh() && rhs >= 0) ||
230 (ins->isBigIntPtrRsh() && rhs < 0);
231 if (isLsh) {
232 ret = UnsigedInt(lhs) << shift;
233 } else {
234 ret = lhs >> shift;
235 }
236 break;
237 }
238 case MDefinition::Opcode::Add:
239 case MDefinition::Opcode::BigIntPtrAdd: {
240 auto checked = mozilla::CheckedInt<IntT>(lhs) + rhs;
241 if (!checked.isValid()) {
242 return nullptr;
243 }
244 ret = checked.value();
245 break;
246 }
247 case MDefinition::Opcode::Sub:
248 case MDefinition::Opcode::BigIntPtrSub: {
249 auto checked = mozilla::CheckedInt<IntT>(lhs) - rhs;
250 if (!checked.isValid()) {
251 return nullptr;
252 }
253 ret = checked.value();
254 break;
255 }
256 case MDefinition::Opcode::Mul:
257 case MDefinition::Opcode::BigIntPtrMul: {
258 auto checked = mozilla::CheckedInt<IntT>(lhs) * rhs;
259 if (!checked.isValid()) {
260 return nullptr;
261 }
262 ret = checked.value();
263 break;
264 }
265 case MDefinition::Opcode::Div: {
266 if (ins->toDiv()->isUnsigned()) {
267 auto checked =
268 mozilla::CheckedInt<UnsigedInt>(UnsigedInt(lhs)) / UnsigedInt(rhs);
269 if (!checked.isValid()) {
270 return nullptr;
271 }
272 ret = IntT(checked.value());
273 break;
274 }
275 [[fallthrough]];
276 }
277 case MDefinition::Opcode::BigIntPtrDiv: {
278 auto checked = mozilla::CheckedInt<IntT>(lhs) / rhs;
279 if (!checked.isValid()) {
280 return nullptr;
281 }
282 ret = checked.value();
283
284 // Decline folding if the numerator isn't evenly divisible by the
285 // denominator. Only applies for non-truncating int32 division.
286 if constexpr (Type == MIRType::Int32) {
287 if (ret * rhs != lhs && !ins->toDiv()->isTruncated()) {
288 return nullptr;
289 }
290 }
291 break;
292 }
293 case MDefinition::Opcode::Mod: {
294 if (ins->toMod()->isUnsigned()) {
295 auto checked =
296 mozilla::CheckedInt<UnsigedInt>(UnsigedInt(lhs)) % UnsigedInt(rhs);
297 if (!checked.isValid()) {
298 return nullptr;
299 }
300 ret = IntT(checked.value());
301 break;
302 }
303 [[fallthrough]];
304 }
305 case MDefinition::Opcode::BigIntPtrMod: {
306 auto checked = mozilla::CheckedInt<IntT>(lhs) % rhs;
307 if (!checked.isValid()) {
308 return nullptr;
309 }
310 ret = checked.value();
311
312 // Decline folding if the result is negative zero. Only applies for
313 // non-truncating int32 remainder.
314 if constexpr (Type == MIRType::Int32) {
315 if (ret == 0 && lhs < 0 && !ins->toMod()->isTruncated()) {
316 return nullptr;
317 }
318 }
319 break;
320 }
321 default:
322 MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "./../../../../js/src/jit/MIR.cpp"
, 322); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do { MOZ_CrashSequence
(__null, 322); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
323 }
324
325 return NewIntConstant<Type>(alloc, ret);
326}
327
328static MConstant* EvaluateInt32ConstantOperands(TempAllocator& alloc,
329 MBinaryInstruction* ins) {
330 return EvaluateIntConstantOperands<MIRType::Int32>(alloc, ins);
331}
332
333static MConstant* EvaluateInt64ConstantOperands(TempAllocator& alloc,
334 MBinaryInstruction* ins) {
335 return EvaluateIntConstantOperands<MIRType::Int64>(alloc, ins);
336}
337
338static MConstant* EvaluateIntPtrConstantOperands(TempAllocator& alloc,
339 MBinaryInstruction* ins) {
340 return EvaluateIntConstantOperands<MIRType::IntPtr>(alloc, ins);
341}
342
343static MConstant* EvaluateConstantOperands(TempAllocator& alloc,
344 MBinaryInstruction* ins) {
345 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", 345); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsTypeRepresentableAsDouble(ins->type())"
")"); do { MOZ_CrashSequence(__null, 345); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
346
347 if (ins->type() == MIRType::Int32) {
348 return EvaluateInt32ConstantOperands(alloc, ins);
349 }
350
351 MDefinition* left = ins->lhs();
352 MDefinition* right = ins->rhs();
353
354 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"
, 354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsFloatingPointType(left->type())"
")"); do { MOZ_CrashSequence(__null, 354); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
355 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", 355); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsFloatingPointType(right->type())" ")"
); do { MOZ_CrashSequence(__null, 355); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
356
357 if (!left->isConstant() || !right->isConstant()) {
358 return nullptr;
359 }
360
361 double lhs = left->toConstant()->numberToDouble();
362 double rhs = right->toConstant()->numberToDouble();
363 double ret;
364
365 switch (ins->op()) {
366 case MDefinition::Opcode::Add:
367 ret = lhs + rhs;
368 break;
369 case MDefinition::Opcode::Sub:
370 ret = lhs - rhs;
371 break;
372 case MDefinition::Opcode::Mul:
373 ret = lhs * rhs;
374 break;
375 case MDefinition::Opcode::Div:
376 ret = NumberDiv(lhs, rhs);
377 break;
378 case MDefinition::Opcode::Mod:
379 ret = NumberMod(lhs, rhs);
380 break;
381 default:
382 MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "./../../../../js/src/jit/MIR.cpp"
, 382); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do { MOZ_CrashSequence
(__null, 382); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
383 }
384
385 if (ins->type() == MIRType::Float32) {
386 return MConstant::NewFloat32(alloc, float(ret));
387 }
388 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"
, 388); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::Double"
")"); do { MOZ_CrashSequence(__null, 388); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
389 return MConstant::New(alloc, DoubleValue(ret));
390}
391
392static MConstant* EvaluateConstantNaNOperand(MBinaryInstruction* ins) {
393 auto* left = ins->lhs();
394 auto* right = ins->rhs();
395
396 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", 396); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsTypeRepresentableAsDouble(left->type())"
")"); do { MOZ_CrashSequence(__null, 396); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
397 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", 397); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsTypeRepresentableAsDouble(right->type())"
")"); do { MOZ_CrashSequence(__null, 397); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
398 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"
, 398); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == ins->type()"
")"); do { MOZ_CrashSequence(__null, 398); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
399 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"
, 399); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == ins->type()"
")"); do { MOZ_CrashSequence(__null, 399); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
400
401 // Don't fold NaN if we can't return a floating point type.
402 if (!IsFloatingPointType(ins->type())) {
403 return nullptr;
404 }
405
406 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", 407); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!left->isConstant() || !right->isConstant()"
") (" "EvaluateConstantOperands should have handled this case"
")"); do { MOZ_CrashSequence(__null, 407); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
407 "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", 407); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!left->isConstant() || !right->isConstant()"
") (" "EvaluateConstantOperands should have handled this case"
")"); do { MOZ_CrashSequence(__null, 407); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
408
409 // One operand must be a constant NaN.
410 MConstant* cst;
411 if (left->isConstant()) {
412 cst = left->toConstant();
413 } else if (right->isConstant()) {
414 cst = right->toConstant();
415 } else {
416 return nullptr;
417 }
418 if (!std::isnan(cst->numberToDouble())) {
419 return nullptr;
420 }
421
422 // Fold to constant NaN.
423 return cst;
424}
425
426static MMul* EvaluateExactReciprocal(TempAllocator& alloc, MDiv* ins) {
427 // we should fold only when it is a floating point operation
428 if (!IsFloatingPointType(ins->type())) {
429 return nullptr;
430 }
431
432 MDefinition* left = ins->getOperand(0);
433 MDefinition* right = ins->getOperand(1);
434
435 if (!right->isConstant()) {
436 return nullptr;
437 }
438
439 int32_t num;
440 if (!mozilla::NumberIsInt32(right->toConstant()->numberToDouble(), &num)) {
441 return nullptr;
442 }
443
444 // check if rhs is a power of two or zero
445 if (num != 0 && !std::has_single_bit(mozilla::Abs(num))) {
446 return nullptr;
447 }
448
449 double ret = 1.0 / double(num);
450
451 MConstant* foldedRhs;
452 if (ins->type() == MIRType::Float32) {
453 foldedRhs = MConstant::NewFloat32(alloc, ret);
454 } else {
455 foldedRhs = MConstant::NewDouble(alloc, ret);
456 }
457
458 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", 458); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "foldedRhs->type() == ins->type()" ")"
); do { MOZ_CrashSequence(__null, 458); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
459 ins->block()->insertBefore(ins, foldedRhs);
460
461 MMul* mul = MMul::New(alloc, left, foldedRhs, ins->type());
462 mul->setMustPreserveNaN(ins->mustPreserveNaN());
463 return mul;
464}
465
466#ifdef JS_JITSPEW1
467const char* MDefinition::opName() const { return OpcodeName(op()); }
468
469void MDefinition::printName(GenericPrinter& out) const {
470 PrintOpcodeName(out, op());
471 out.printf("#%u", id());
472}
473#endif
474
475HashNumber MDefinition::valueHash() const {
476 HashNumber out = HashNumber(op());
477 for (size_t i = 0, e = numOperands(); i < e; i++) {
478 out = addU32ToHash(out, getOperand(i)->id());
479 }
480 if (MDefinition* dep = dependency()) {
481 out = addU32ToHash(out, dep->id());
482 }
483 return out;
484}
485
486HashNumber MNullaryInstruction::valueHash() const {
487 HashNumber hash = HashNumber(op());
488 if (MDefinition* dep = dependency()) {
489 hash = addU32ToHash(hash, dep->id());
490 }
491 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"
, 491); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hash == MDefinition::valueHash()"
")"); do { MOZ_CrashSequence(__null, 491); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
492 return hash;
493}
494
495HashNumber MUnaryInstruction::valueHash() const {
496 HashNumber hash = HashNumber(op());
497 hash = addU32ToHash(hash, getOperand(0)->id());
498 if (MDefinition* dep = dependency()) {
499 hash = addU32ToHash(hash, dep->id());
500 }
501 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"
, 501); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hash == MDefinition::valueHash()"
")"); do { MOZ_CrashSequence(__null, 501); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
502 return hash;
503}
504
505HashNumber MBinaryInstruction::valueHash() const {
506 HashNumber hash = HashNumber(op());
507 hash = addU32ToHash(hash, getOperand(0)->id());
508 hash = addU32ToHash(hash, getOperand(1)->id());
509 if (MDefinition* dep = dependency()) {
510 hash = addU32ToHash(hash, dep->id());
511 }
512 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"
, 512); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hash == MDefinition::valueHash()"
")"); do { MOZ_CrashSequence(__null, 512); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
513 return hash;
514}
515
516HashNumber MTernaryInstruction::valueHash() const {
517 HashNumber hash = HashNumber(op());
518 hash = addU32ToHash(hash, getOperand(0)->id());
519 hash = addU32ToHash(hash, getOperand(1)->id());
520 hash = addU32ToHash(hash, getOperand(2)->id());
521 if (MDefinition* dep = dependency()) {
522 hash = addU32ToHash(hash, dep->id());
523 }
524 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"
, 524); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hash == MDefinition::valueHash()"
")"); do { MOZ_CrashSequence(__null, 524); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
525 return hash;
526}
527
528HashNumber MQuaternaryInstruction::valueHash() const {
529 HashNumber hash = HashNumber(op());
530 hash = addU32ToHash(hash, getOperand(0)->id());
531 hash = addU32ToHash(hash, getOperand(1)->id());
532 hash = addU32ToHash(hash, getOperand(2)->id());
533 hash = addU32ToHash(hash, getOperand(3)->id());
534 if (MDefinition* dep = dependency()) {
535 hash = addU32ToHash(hash, dep->id());
536 }
537 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"
, 537); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hash == MDefinition::valueHash()"
")"); do { MOZ_CrashSequence(__null, 537); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
538 return hash;
539}
540
541HashNumber MQuinaryInstruction::valueHash() const {
542 HashNumber hash = HashNumber(op());
543 hash = addU32ToHash(hash, getOperand(0)->id());
544 hash = addU32ToHash(hash, getOperand(1)->id());
545 hash = addU32ToHash(hash, getOperand(2)->id());
546 hash = addU32ToHash(hash, getOperand(3)->id());
547 hash = addU32ToHash(hash, getOperand(4)->id());
548 if (MDefinition* dep = dependency()) {
549 hash = addU32ToHash(hash, dep->id());
550 }
551 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"
, 551); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hash == MDefinition::valueHash()"
")"); do { MOZ_CrashSequence(__null, 551); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
552 return hash;
553}
554
555const MDefinition* MDefinition::skipObjectGuards() const {
556 const MDefinition* result = this;
557 // These instructions don't modify the object and just guard specific
558 // properties.
559 while (true) {
560 if (result->isGuardShape()) {
561 result = result->toGuardShape()->object();
562 continue;
563 }
564 if (result->isGuardShapeList()) {
565 result = result->toGuardShapeList()->object();
566 continue;
567 }
568 if (result->isGuardMultipleShapes()) {
569 result = result->toGuardMultipleShapes()->object();
570 continue;
571 }
572 if (result->isGuardShapeListToOffset()) {
573 result = result->toGuardShapeListToOffset()->object();
574 continue;
575 }
576 if (result->isGuardMultipleShapesToOffset()) {
577 result = result->toGuardMultipleShapesToOffset()->object();
578 continue;
579 }
580 if (result->isGuardNullProto()) {
581 result = result->toGuardNullProto()->object();
582 continue;
583 }
584 if (result->isGuardProto()) {
585 result = result->toGuardProto()->object();
586 continue;
587 }
588
589 break;
590 }
591
592 return result;
593}
594
595bool MDefinition::congruentIfOperandsEqual(const MDefinition* ins) const {
596 if (op() != ins->op()) {
597 return false;
598 }
599
600 if (type() != ins->type()) {
601 return false;
602 }
603
604 if (isEffectful() || ins->isEffectful()) {
605 return false;
606 }
607
608 if (numOperands() != ins->numOperands()) {
609 return false;
610 }
611
612 for (size_t i = 0, e = numOperands(); i < e; i++) {
613 if (getOperand(i) != ins->getOperand(i)) {
614 return false;
615 }
616 }
617
618 return true;
619}
620
621bool MDefinition::dominates(const MDefinition* other) const {
622 if (block() != other->block()) {
623 return block()->dominates(other->block());
624 }
625
626 // Nothing in a block dominates a phi in that block.
627 if (other->isPhi()) {
628 return false;
629 }
630
631 // Phis dominate all instructions in the block.
632 if (isPhi()) {
633 return true;
634 }
635
636 // If both defs are instructions in the same block, check whether
637 // `this` precedes `other`.
638 MInstructionIterator opIter = block()->begin(toInstruction());
639 do {
640 ++opIter;
641 if (opIter == block()->end()) {
642 return false;
643 }
644 } while (*opIter != other);
645 return true;
646}
647
648MDefinition* MDefinition::foldsTo(TempAllocator& alloc) {
649 // In the default case, there are no constants to fold.
650 return this;
651}
652
653MDefinition* MInstruction::foldsToStore(TempAllocator& alloc) {
654 if (!dependency()) {
655 return nullptr;
656 }
657
658 MDefinition* store = dependency();
659 if (mightAlias(store) != AliasType::MustAlias) {
660 return nullptr;
661 }
662
663 if (!store->block()->dominates(block())) {
664 return nullptr;
665 }
666
667 MDefinition* value;
668 switch (store->op()) {
669 case Opcode::StoreFixedSlot:
670 value = store->toStoreFixedSlot()->value();
671 break;
672 case Opcode::StoreDynamicSlot:
673 value = store->toStoreDynamicSlot()->value();
674 break;
675 case Opcode::StoreElement:
676 value = store->toStoreElement()->value();
677 break;
678 default:
679 MOZ_CRASH("unknown store")do { do { } while (false); MOZ_ReportCrash("" "unknown store"
, "./../../../../js/src/jit/MIR.cpp", 679); AnnotateMozCrashReason
("MOZ_CRASH(" "unknown store" ")"); do { MOZ_CrashSequence(__null
, 679); __attribute__((nomerge)) ::abort(); } while (false); }
while (false)
;
680 }
681
682 // If the type are matching then we return the value which is used as
683 // argument of the store.
684 if (value->type() != type()) {
685 // If we expect to read a type which is more generic than the type seen
686 // by the store, then we box the value used by the store.
687 if (type() != MIRType::Value) {
688 return nullptr;
689 }
690
691 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"
, 691); AnnotateMozCrashReason("MOZ_ASSERT" "(" "value->type() < MIRType::Value"
")"); do { MOZ_CrashSequence(__null, 691); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
692 MBox* box = MBox::New(alloc, value);
693 value = box;
694 }
695
696 return value;
697}
698
699void MDefinition::analyzeEdgeCasesForward() {}
700
701void MDefinition::analyzeEdgeCasesBackward() {}
702
703void MInstruction::setResumePoint(MResumePoint* resumePoint) {
704 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", 704); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!resumePoint_" ")"); do { MOZ_CrashSequence
(__null, 704); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
705 resumePoint_ = resumePoint;
706 resumePoint_->setInstruction(this);
707}
708
709void MInstruction::stealResumePoint(MInstruction* other) {
710 MResumePoint* resumePoint = other->resumePoint_;
711 other->resumePoint_ = nullptr;
712
713 resumePoint->resetInstruction();
714 setResumePoint(resumePoint);
715}
716
717bool MInstruction::copyResumePointFrom(TempAllocator& alloc,
718 MInstruction* previous) {
719 MResumePoint* rp = previous->resumePoint_->clone(alloc);
720 if (!rp) {
721 return false;
722 }
723 setResumePoint(rp);
724 return true;
725}
726
727void MInstruction::moveResumePointAsEntry() {
728 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"
, 728); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isNop()" ")"
); do { MOZ_CrashSequence(__null, 728); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
729 block()->clearEntryResumePoint();
730 block()->setEntryResumePoint(resumePoint_);
731 resumePoint_->resetInstruction();
732 resumePoint_ = nullptr;
733}
734
735void MInstruction::clearResumePoint() {
736 resumePoint_->resetInstruction();
737 block()->discardPreAllocatedResumePoint(resumePoint_);
738 resumePoint_ = nullptr;
739}
740
741MDefinition* MTest::foldsDoubleNegation(TempAllocator& alloc) {
742 MDefinition* op = getOperand(0);
743
744 if (op->isNot()) {
745 // If the operand of the Not is itself a Not, they cancel out.
746 MDefinition* opop = op->getOperand(0);
747 if (opop->isNot()) {
748 return MTest::New(alloc, opop->toNot()->input(), ifTrue(), ifFalse());
749 }
750 return MTest::New(alloc, op->toNot()->input(), ifFalse(), ifTrue());
751 }
752 return nullptr;
753}
754
755MDefinition* MTest::foldsConstant(TempAllocator& alloc) {
756 MDefinition* op = getOperand(0);
757 if (MConstant* opConst = op->maybeConstantValue()) {
758 bool b;
759 if (opConst->valueToBoolean(&b)) {
760 return MGoto::New(alloc, b ? ifTrue() : ifFalse());
761 }
762 }
763 return nullptr;
764}
765
766MDefinition* MTest::foldsTypes(TempAllocator& alloc) {
767 MDefinition* op = getOperand(0);
768
769 switch (op->type()) {
770 case MIRType::Undefined:
771 case MIRType::Null:
772 return MGoto::New(alloc, ifFalse());
773 case MIRType::Symbol:
774 return MGoto::New(alloc, ifTrue());
775 default:
776 break;
777 }
778 return nullptr;
779}
780
781class UsesIterator {
782 MDefinition* def_;
783
784 public:
785 explicit UsesIterator(MDefinition* def) : def_(def) {}
786 auto begin() const { return def_->usesBegin(); }
787 auto end() const { return def_->usesEnd(); }
788};
789
790static bool AllInstructionsDeadIfUnused(MBasicBlock* block) {
791 for (auto* ins : *block) {
792 // Skip trivial instructions.
793 if (ins->isNop() || ins->isGoto()) {
794 continue;
795 }
796
797 // All uses must be within the current block.
798 for (auto* use : UsesIterator(ins)) {
799 if (use->consumer()->block() != block) {
800 return false;
801 }
802 }
803
804 // All instructions within this block must be dead if unused.
805 if (!DeadIfUnused(ins)) {
806 return false;
807 }
808 }
809 return true;
810}
811
812MDefinition* MTest::foldsNeedlessControlFlow(TempAllocator& alloc) {
813 // All instructions within both successors need be dead if unused.
814 if (!AllInstructionsDeadIfUnused(ifTrue()) ||
815 !AllInstructionsDeadIfUnused(ifFalse())) {
816 return nullptr;
817 }
818
819 // Both successors must have the same target successor.
820 if (ifTrue()->numSuccessors() != 1 || ifFalse()->numSuccessors() != 1) {
821 return nullptr;
822 }
823 if (ifTrue()->getSuccessor(0) != ifFalse()->getSuccessor(0)) {
824 return nullptr;
825 }
826
827 // The target successor's phis must be redundant. Redundant phis should have
828 // been removed in an earlier pass, so only check if any phis are present,
829 // which is a stronger condition.
830 if (ifTrue()->successorWithPhis()) {
831 return nullptr;
832 }
833
834 return MGoto::New(alloc, ifTrue());
835}
836
837// If a test is dominated by either the true or false path of a previous test of
838// the same condition, then the test is redundant and can be converted into a
839// goto true or goto false, respectively.
840MDefinition* MTest::foldsRedundantTest(TempAllocator& alloc) {
841 MBasicBlock* myBlock = this->block();
842 MDefinition* originalInput = getOperand(0);
843
844 // Handle single and double negatives. This ensures that we do not miss a
845 // folding opportunity due to a condition being inverted.
846 MDefinition* newInput = input();
847 bool inverted = false;
848 if (originalInput->isNot()) {
849 newInput = originalInput->toNot()->input();
850 inverted = true;
851 if (originalInput->toNot()->input()->isNot()) {
852 newInput = originalInput->toNot()->input()->toNot()->input();
853 inverted = false;
854 }
855 }
856
857 // The specific order of traversal does not matter. If there are multiple
858 // dominating redundant tests, they will either agree on direction (in which
859 // case we will prune the same way regardless of order), or they will
860 // disagree, in which case we will eventually be marked entirely dead by the
861 // folding of the redundant parent.
862 for (MUseIterator i(newInput->usesBegin()), e(newInput->usesEnd()); i != e;
863 ++i) {
864 if (!i->consumer()->isDefinition()) {
865 continue;
866 }
867 if (!i->consumer()->toDefinition()->isTest()) {
868 continue;
869 }
870 MTest* otherTest = i->consumer()->toDefinition()->toTest();
871 if (otherTest == this) {
872 continue;
873 }
874
875 if (otherTest->ifFalse()->dominates(myBlock)) {
876 // This test cannot be true, so fold to a goto false.
877 return MGoto::New(alloc, inverted ? ifTrue() : ifFalse());
878 }
879 if (otherTest->ifTrue()->dominates(myBlock)) {
880 // This test cannot be false, so fold to a goto true.
881 return MGoto::New(alloc, inverted ? ifFalse() : ifTrue());
882 }
883 }
884
885 return nullptr;
886}
887
888MDefinition* MTest::foldsTo(TempAllocator& alloc) {
889 if (MDefinition* def = foldsRedundantTest(alloc)) {
890 return def;
891 }
892
893 if (MDefinition* def = foldsDoubleNegation(alloc)) {
894 return def;
895 }
896
897 if (MDefinition* def = foldsConstant(alloc)) {
898 return def;
899 }
900
901 if (MDefinition* def = foldsTypes(alloc)) {
902 return def;
903 }
904
905 if (MDefinition* def = foldsNeedlessControlFlow(alloc)) {
906 return def;
907 }
908
909 return this;
910}
911
912#ifdef JS_JITSPEW1
913void MDefinition::printOpcode(GenericPrinter& out) const {
914 PrintOpcodeName(out, op());
915 if (numOperands() > 0) {
916 out.printf(" <- ");
917 }
918 for (size_t j = 0, e = numOperands(); j < e; j++) {
919 if (j > 0) {
920 out.printf(", ");
921 }
922 if (getUseFor(j)->hasProducer()) {
923 getOperand(j)->printName(out);
924 } else {
925 out.printf("(null)");
926 }
927 }
928}
929
930void MDefinition::dump(GenericPrinter& out) const {
931 printName(out);
932 out.printf(":%s", StringFromMIRType(type()));
933 out.printf(" = ");
934 printOpcode(out);
935 out.printf("\n");
936
937 if (isInstruction()) {
938 if (MResumePoint* resume = toInstruction()->resumePoint()) {
939 resume->dump(out);
940 }
941 }
942}
943
944void MDefinition::dump() const {
945 Fprinter out(stderrstderr);
946 dump(out);
947 out.finish();
948}
949
950void MDefinition::dumpLocation(GenericPrinter& out) const {
951 MResumePoint* rp = nullptr;
952 const char* linkWord = nullptr;
953 if (isInstruction() && toInstruction()->resumePoint()) {
954 rp = toInstruction()->resumePoint();
955 linkWord = "at";
956 } else {
957 rp = block()->entryResumePoint();
958 linkWord = "after";
959 }
960
961 while (rp) {
962 JSScript* script = rp->block()->info().script();
963 uint32_t lineno = PCToLineNumber(rp->block()->info().script(), rp->pc());
964 out.printf(" %s %s:%u\n", linkWord, script->filename(), lineno);
965 rp = rp->caller();
966 linkWord = "in";
967 }
968}
969
970void MDefinition::dumpLocation() const {
971 Fprinter out(stderrstderr);
972 dumpLocation(out);
973 out.finish();
974}
975#endif
976
977#if defined(DEBUG1) || defined(JS_JITSPEW1)
978size_t MDefinition::useCount() const {
979 size_t count = 0;
980 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
981 count++;
982 }
983 return count;
984}
985
986size_t MDefinition::defUseCount() const {
987 size_t count = 0;
988 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
989 if ((*i)->consumer()->isDefinition()) {
990 count++;
991 }
992 }
993 return count;
994}
995#endif
996
997bool MDefinition::hasOneUse() const {
998 MUseIterator i(uses_.begin());
999 if (i == uses_.end()) {
1000 return false;
1001 }
1002 i++;
1003 return i == uses_.end();
1004}
1005
1006bool MDefinition::hasOneDefUse() const {
1007 bool hasOneDefUse = false;
1008 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
1009 if (!(*i)->consumer()->isDefinition()) {
1010 continue;
1011 }
1012
1013 // We already have a definition use. So 1+
1014 if (hasOneDefUse) {
1015 return false;
1016 }
1017
1018 // We saw one definition. Loop to test if there is another.
1019 hasOneDefUse = true;
1020 }
1021
1022 return hasOneDefUse;
1023}
1024
1025bool MDefinition::hasOneLiveDefUse() const {
1026 bool hasOneDefUse = false;
1027 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
1028 if (!(*i)->consumer()->isDefinition()) {
1029 continue;
1030 }
1031
1032 MDefinition* def = (*i)->consumer()->toDefinition();
1033 if (def->isRecoveredOnBailout()) {
1034 continue;
1035 }
1036
1037 // We already have a definition use. So 1+
1038 if (hasOneDefUse) {
1039 return false;
1040 }
1041
1042 // We saw one definition. Loop to test if there is another.
1043 hasOneDefUse = true;
1044 }
1045
1046 return hasOneDefUse;
1047}
1048
1049bool MDefinition::hasDefUses() const {
1050 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
1051 if ((*i)->consumer()->isDefinition()) {
1052 return true;
1053 }
1054 }
1055
1056 return false;
1057}
1058
1059bool MDefinition::hasLiveDefUses() const {
1060 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
1061 MNode* ins = (*i)->consumer();
1062 if (ins->isDefinition()) {
1063 if (!ins->toDefinition()->isRecoveredOnBailout()) {
1064 return true;
1065 }
1066 } else {
1067 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", 1067); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ins->isResumePoint()" ")"); do { MOZ_CrashSequence
(__null, 1067); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1068 if (!ins->toResumePoint()->isRecoverableOperand(*i)) {
1069 return true;
1070 }
1071 }
1072 }
1073 return false;
1074}
1075
1076MDefinition* MDefinition::maybeSingleDefUse() const {
1077 MUseDefIterator use(this);
1078 if (!use) {
1079 // No def-uses.
1080 return nullptr;
1081 }
1082
1083 MDefinition* useDef = use.def();
1084
1085 use++;
1086 if (use) {
1087 // More than one def-use.
1088 return nullptr;
1089 }
1090
1091 return useDef;
1092}
1093
1094MDefinition* MDefinition::maybeMostRecentlyAddedDefUse() const {
1095 MUseDefIterator use(this);
1096 if (!use) {
1097 // No def-uses.
1098 return nullptr;
1099 }
1100
1101 MDefinition* mostRecentUse = use.def();
1102
1103#ifdef DEBUG1
1104 // This function relies on addUse adding new uses to the front of the list.
1105 // Check this invariant by asserting the next few uses are 'older'. Skip this
1106 // for phis because setBackedge can add a new use for a loop phi even if the
1107 // loop body has a use with an id greater than the loop phi's id.
1108 if (!mostRecentUse->isPhi()) {
1109 static constexpr size_t NumUsesToCheck = 3;
1110 use++;
1111 for (size_t i = 0; use && i < NumUsesToCheck; i++, use++) {
1112 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", 1112); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "use.def()->id() <= mostRecentUse->id()"
")"); do { MOZ_CrashSequence(__null, 1112); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1113 }
1114 }
1115#endif
1116
1117 return mostRecentUse;
1118}
1119
1120void MDefinition::replaceAllUsesWith(MDefinition* dom) {
1121 for (size_t i = 0, e = numOperands(); i < e; ++i) {
1122 getOperand(i)->setImplicitlyUsedUnchecked();
1123 }
1124
1125 justReplaceAllUsesWith(dom);
1126}
1127
1128void MDefinition::justReplaceAllUsesWith(MDefinition* dom) {
1129 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", 1129); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "dom != nullptr" ")"); do { MOZ_CrashSequence
(__null, 1129); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1130 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"
, 1130); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dom != this"
")"); do { MOZ_CrashSequence(__null, 1130); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1131
1132 // Carry over the fact the value has uses which are no longer inspectable
1133 // with the graph.
1134 if (isImplicitlyUsed()) {
1135 dom->setImplicitlyUsedUnchecked();
1136 }
1137
1138 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ++i) {
1139 i->setProducerUnchecked(dom);
1140 }
1141 dom->uses_.takeElements(uses_);
1142}
1143
1144bool MDefinition::optimizeOutAllUses(TempAllocator& alloc) {
1145 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e;) {
1146 MUse* use = *i++;
1147 MConstant* constant = use->consumer()->block()->optimizedOutConstant(alloc);
1148 if (!alloc.ensureBallast()) {
1149 return false;
1150 }
1151
1152 // Update the resume point operand to use the optimized-out constant.
1153 use->setProducerUnchecked(constant);
1154 constant->addUseUnchecked(use);
1155 }
1156
1157 // Remove dangling pointers.
1158 this->uses_.clear();
1159 return true;
1160}
1161
1162void MDefinition::replaceAllLiveUsesWith(MDefinition* dom) {
1163 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e;) {
1164 MUse* use = *i++;
1165 MNode* consumer = use->consumer();
1166 if (consumer->isResumePoint()) {
1167 continue;
1168 }
1169 if (consumer->isDefinition() &&
1170 consumer->toDefinition()->isRecoveredOnBailout()) {
1171 continue;
1172 }
1173
1174 // Update the operand to use the dominating definition.
1175 use->replaceProducer(dom);
1176 }
1177}
1178
1179MConstant* MConstant::New(TempAllocator& alloc, const Value& v) {
1180 return new (alloc) MConstant(alloc, v);
1181}
1182
1183MConstant* MConstant::New(TempAllocator::Fallible alloc, const Value& v) {
1184 return new (alloc) MConstant(alloc.alloc, v);
1185}
1186
1187MConstant* MConstant::NewBoolean(TempAllocator& alloc, bool b) {
1188 return new (alloc) MConstant(b);
1189}
1190
1191MConstant* MConstant::NewDouble(TempAllocator& alloc, double d) {
1192 return new (alloc) MConstant(d);
1193}
1194
1195MConstant* MConstant::NewFloat32(TempAllocator& alloc, double d) {
1196 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"
, 1196); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::IsFloat32Representable(d)"
")"); do { MOZ_CrashSequence(__null, 1196); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1197 return new (alloc) MConstant(float(d));
1198}
1199
1200MConstant* MConstant::NewInt32(TempAllocator& alloc, int32_t i) {
1201 return new (alloc) MConstant(i);
1202}
1203
1204MConstant* MConstant::NewInt64(TempAllocator& alloc, int64_t i) {
1205 return new (alloc) MConstant(MIRType::Int64, i);
1206}
1207
1208MConstant* MConstant::NewIntPtr(TempAllocator& alloc, intptr_t i) {
1209 return new (alloc) MConstant(MIRType::IntPtr, i);
1210}
1211
1212MConstant* MConstant::NewMagic(TempAllocator& alloc, JSWhyMagic m) {
1213 return new (alloc) MConstant(alloc, MagicValue(m));
1214}
1215
1216MConstant* MConstant::NewNull(TempAllocator& alloc) {
1217 return new (alloc) MConstant(MIRType::Null);
1218}
1219
1220MConstant* MConstant::NewObject(TempAllocator& alloc, JSObject* v) {
1221 return new (alloc) MConstant(v);
1222}
1223
1224MConstant* MConstant::NewShape(TempAllocator& alloc, Shape* s) {
1225 return new (alloc) MConstant(s);
1226}
1227
1228MConstant* MConstant::NewString(TempAllocator& alloc, JSString* s) {
1229 return new (alloc) MConstant(alloc, StringValue(s));
1230}
1231
1232MConstant* MConstant::NewUndefined(TempAllocator& alloc) {
1233 return new (alloc) MConstant(MIRType::Undefined);
1234}
1235
1236static MIRType MIRTypeFromValue(const js::Value& vp) {
1237 if (vp.isDouble()) {
1238 return MIRType::Double;
1239 }
1240 if (vp.isMagic()) {
1241 switch (vp.whyMagic()) {
1242 case JS_OPTIMIZED_OUT:
1243 return MIRType::MagicOptimizedOut;
1244 case JS_ELEMENTS_HOLE:
1245 return MIRType::MagicHole;
1246 case JS_IS_CONSTRUCTING:
1247 return MIRType::MagicIsConstructing;
1248 case JS_UNINITIALIZED_LEXICAL:
1249 return MIRType::MagicUninitializedLexical;
1250 default:
1251 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"
, 1251); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Unexpected magic constant" ")");
do { MOZ_CrashSequence(__null, 1251); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1252 }
1253 }
1254 return MIRTypeFromValueType(vp.extractNonDoubleType());
1255}
1256
1257MConstant::MConstant(TempAllocator& alloc, const js::Value& vp)
1258 : MNullaryInstruction(classOpcode) {
1259 setResultType(MIRTypeFromValue(vp));
1260
1261 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", 1261); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "payload_.asBits == 0" ")"); do { MOZ_CrashSequence
(__null, 1261); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1262
1263 switch (type()) {
1264 case MIRType::Undefined:
1265 case MIRType::Null:
1266 break;
1267 case MIRType::Boolean:
1268 payload_.b = vp.toBoolean();
1269 break;
1270 case MIRType::Int32:
1271 payload_.i32 = vp.toInt32();
1272 break;
1273 case MIRType::Double:
1274 payload_.d = vp.toDouble();
1275 break;
1276 case MIRType::String: {
1277 JSString* str = vp.toString();
1278 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", 1278); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsInsideNursery(str)" ")"); do { MOZ_CrashSequence
(__null, 1278); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1279 payload_.str = &str->asOffThreadAtom();
1280 break;
1281 }
1282 case MIRType::Symbol:
1283 payload_.sym = vp.toSymbol();
1284 break;
1285 case MIRType::BigInt:
1286 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"
, 1286); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(vp.toBigInt())"
")"); do { MOZ_CrashSequence(__null, 1286); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1287 payload_.bi = vp.toBigInt();
1288 break;
1289 case MIRType::Object:
1290 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"
, 1290); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&vp.toObject())"
")"); do { MOZ_CrashSequence(__null, 1290); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1291 payload_.obj = &vp.toObject();
1292 break;
1293 case MIRType::MagicOptimizedOut:
1294 case MIRType::MagicHole:
1295 case MIRType::MagicIsConstructing:
1296 case MIRType::MagicUninitializedLexical:
1297 break;
1298 default:
1299 MOZ_CRASH("Unexpected type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected type"
, "./../../../../js/src/jit/MIR.cpp", 1299); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected type" ")"); do { MOZ_CrashSequence(
__null, 1299); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
1300 }
1301
1302 setMovable();
1303}
1304
1305MConstant::MConstant(JSObject* obj) : MConstant(MIRType::Object) {
1306 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", 1306); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsInsideNursery(obj)" ")"); do { MOZ_CrashSequence
(__null, 1306); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1307 payload_.obj = obj;
1308}
1309
1310MConstant::MConstant(Shape* shape) : MConstant(MIRType::Shape) {
1311 payload_.shape = shape;
1312}
1313
1314#ifdef DEBUG1
1315void MConstant::assertInitializedPayload() const {
1316 // valueHash() and equals() expect the unused payload bits to be
1317 // initialized to zero. Assert this in debug builds.
1318
1319 switch (type()) {
1320 case MIRType::Int32:
1321 case MIRType::Float32:
1322 if constexpr (std::endian::native == std::endian::little) {
1323 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"
, 1323); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(payload_.asBits >> 32) == 0"
")"); do { MOZ_CrashSequence(__null, 1323); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1324 } else {
1325 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"
, 1325); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(payload_.asBits << 32) == 0"
")"); do { MOZ_CrashSequence(__null, 1325); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1326 }
1327 break;
1328 case MIRType::Boolean:
1329 if constexpr (std::endian::native == std::endian::little) {
1330 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"
, 1330); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(payload_.asBits >> 1) == 0"
")"); do { MOZ_CrashSequence(__null, 1330); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1331 } else {
1332 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", 1332); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(payload_.asBits & ~(1ULL << 56)) == 0"
")"); do { MOZ_CrashSequence(__null, 1332); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1333 }
1334 break;
1335 case MIRType::Double:
1336 case MIRType::Int64:
1337 break;
1338 case MIRType::String:
1339 case MIRType::Object:
1340 case MIRType::Symbol:
1341 case MIRType::BigInt:
1342 case MIRType::IntPtr:
1343 case MIRType::Shape:
1344 if constexpr (std::endian::native == std::endian::little) {
1345 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", 1345); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(payload_.asBits >> 32) == 0" ")"); do
{ MOZ_CrashSequence(__null, 1345); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false); } } while (false
)
;
1346 } else {
1347 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", 1347); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(payload_.asBits << 32) == 0" ")"); do
{ MOZ_CrashSequence(__null, 1347); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false); } } while (false
)
;
1348 }
1349 break;
1350 default:
1351 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", 1351); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsNullOrUndefined(type()) || IsMagicType(type())"
")"); do { MOZ_CrashSequence(__null, 1351); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1352 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", 1352); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "payload_.asBits == 0" ")"); do { MOZ_CrashSequence
(__null, 1352); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1353 break;
1354 }
1355}
1356#endif
1357
1358HashNumber MConstant::valueHash() const {
1359 static_assert(sizeof(Payload) == sizeof(uint64_t),
1360 "Code below assumes payload fits in 64 bits");
1361
1362 assertInitializedPayload();
1363 return ConstantValueHash(type(), payload_.asBits);
1364}
1365
1366HashNumber MConstantProto::valueHash() const {
1367 HashNumber hash = protoObject()->valueHash();
1368 const MDefinition* receiverObject = getReceiverObject();
1369 if (receiverObject) {
1370 hash = addU32ToHash(hash, receiverObject->id());
1371 }
1372 return hash;
1373}
1374
1375bool MConstant::congruentTo(const MDefinition* ins) const {
1376 return ins->isConstant() && equals(ins->toConstant());
1377}
1378
1379#ifdef JS_JITSPEW1
1380void MConstant::printOpcode(GenericPrinter& out) const {
1381 PrintOpcodeName(out, op());
1382 out.printf(" ");
1383 switch (type()) {
1384 case MIRType::Undefined:
1385 out.printf("undefined");
1386 break;
1387 case MIRType::Null:
1388 out.printf("null");
1389 break;
1390 case MIRType::Boolean:
1391 out.printf(toBoolean() ? "true" : "false");
1392 break;
1393 case MIRType::Int32:
1394 out.printf("0x%x", uint32_t(toInt32()));
1395 break;
1396 case MIRType::Int64:
1397 out.printf("0x%" PRIx64"l" "x", uint64_t(toInt64()));
1398 break;
1399 case MIRType::IntPtr:
1400 out.printf("0x%" PRIxPTR"l" "x", uintptr_t(toIntPtr()));
1401 break;
1402 case MIRType::Double:
1403 out.printf("%.16g", toDouble());
1404 break;
1405 case MIRType::Float32: {
1406 float val = toFloat32();
1407 out.printf("%.16g", val);
1408 break;
1409 }
1410 case MIRType::Object:
1411 if (toObject().is<JSFunction>()) {
1412 JSFunction* fun = &toObject().as<JSFunction>();
1413 if (fun->maybePartialDisplayAtom()) {
1414 out.put("function ");
1415 EscapedStringPrinter(out, fun->maybePartialDisplayAtom(), 0);
1416 } else {
1417 out.put("unnamed function");
1418 }
1419 if (fun->hasBaseScript()) {
1420 BaseScript* script = fun->baseScript();
1421 out.printf(" (%s:%u)", script->filename() ? script->filename() : "",
1422 script->lineno());
1423 }
1424 out.printf(" at %p", (void*)fun);
1425 break;
1426 }
1427 out.printf("object %p (%s)", (void*)&toObject(),
1428 toObject().getClass()->name);
1429 break;
1430 case MIRType::Symbol:
1431 out.printf("symbol at %p", (void*)toSymbol());
1432 break;
1433 case MIRType::BigInt:
1434 out.printf("BigInt at %p", (void*)toBigInt());
1435 break;
1436 case MIRType::String:
1437 out.printf("string %p", (void*)toString());
1438 break;
1439 case MIRType::Shape:
1440 out.printf("shape at %p", (void*)toShape());
1441 break;
1442 case MIRType::MagicHole:
1443 out.printf("magic hole");
1444 break;
1445 case MIRType::MagicIsConstructing:
1446 out.printf("magic is-constructing");
1447 break;
1448 case MIRType::MagicOptimizedOut:
1449 out.printf("magic optimized-out");
1450 break;
1451 case MIRType::MagicUninitializedLexical:
1452 out.printf("magic uninitialized-lexical");
1453 break;
1454 default:
1455 MOZ_CRASH("unexpected type")do { do { } while (false); MOZ_ReportCrash("" "unexpected type"
, "./../../../../js/src/jit/MIR.cpp", 1455); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected type" ")"); do { MOZ_CrashSequence(
__null, 1455); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
1456 }
1457}
1458#endif
1459
1460bool MConstant::canProduceFloat32() const {
1461 if (!isTypeRepresentableAsDouble()) {
1462 return false;
1463 }
1464
1465 if (type() == MIRType::Int32) {
1466 return IsFloat32Representable(static_cast<double>(toInt32()));
1467 }
1468 if (type() == MIRType::Double) {
1469 return IsFloat32Representable(toDouble());
1470 }
1471 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", 1471); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Float32" ")"); do { MOZ_CrashSequence
(__null, 1471); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1472 return true;
1473}
1474
1475Value MConstant::toJSValue() const {
1476 // Wasm has types like int64 that cannot be stored as js::Value. It also
1477 // doesn't want the NaN canonicalization enforced by js::Value.
1478 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", 1478); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsCompilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 1478); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1479
1480 switch (type()) {
1481 case MIRType::Undefined:
1482 return UndefinedValue();
1483 case MIRType::Null:
1484 return NullValue();
1485 case MIRType::Boolean:
1486 return BooleanValue(toBoolean());
1487 case MIRType::Int32:
1488 return Int32Value(toInt32());
1489 case MIRType::Double:
1490 return DoubleValue(toDouble());
1491 case MIRType::Float32:
1492 return Float32Value(toFloat32());
1493 case MIRType::String:
1494 return StringValue(toString()->unwrap());
1495 case MIRType::Symbol:
1496 return SymbolValue(toSymbol());
1497 case MIRType::BigInt:
1498 return BigIntValue(toBigInt());
1499 case MIRType::Object:
1500 return ObjectValue(toObject());
1501 case MIRType::Shape:
1502 return PrivateGCThingValue(toShape());
1503 case MIRType::MagicOptimizedOut:
1504 return MagicValue(JS_OPTIMIZED_OUT);
1505 case MIRType::MagicHole:
1506 return MagicValue(JS_ELEMENTS_HOLE);
1507 case MIRType::MagicIsConstructing:
1508 return MagicValue(JS_IS_CONSTRUCTING);
1509 case MIRType::MagicUninitializedLexical:
1510 return MagicValue(JS_UNINITIALIZED_LEXICAL);
1511 default:
1512 MOZ_CRASH("Unexpected type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected type"
, "./../../../../js/src/jit/MIR.cpp", 1512); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected type" ")"); do { MOZ_CrashSequence(
__null, 1512); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
1513 }
1514}
1515
1516bool MConstant::valueToBoolean(bool* res) const {
1517 switch (type()) {
1518 case MIRType::Boolean:
1519 *res = toBoolean();
1520 return true;
1521 case MIRType::Int32:
1522 *res = toInt32() != 0;
1523 return true;
1524 case MIRType::Int64:
1525 *res = toInt64() != 0;
1526 return true;
1527 case MIRType::IntPtr:
1528 *res = toIntPtr() != 0;
1529 return true;
1530 case MIRType::Double:
1531 *res = !std::isnan(toDouble()) && toDouble() != 0.0;
1532 return true;
1533 case MIRType::Float32:
1534 *res = !std::isnan(toFloat32()) && toFloat32() != 0.0f;
1535 return true;
1536 case MIRType::Null:
1537 case MIRType::Undefined:
1538 *res = false;
1539 return true;
1540 case MIRType::Symbol:
1541 *res = true;
1542 return true;
1543 case MIRType::BigInt:
1544 *res = !toBigInt()->isZero();
1545 return true;
1546 case MIRType::String:
1547 *res = toString()->length() != 0;
1548 return true;
1549 case MIRType::Object:
1550 // Calling EmulatesUndefined here is racy if we're compiling off-thread
1551 // because it reads obj->shape->base->clasp, so just give up.
1552 // Note that we could use fuses to optimize this (bug 1874905).
1553 return false;
1554 default:
1555 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", 1555); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsMagicType(type())" ")"); do { MOZ_CrashSequence
(__null, 1555); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1556 return false;
1557 }
1558}
1559
1560#ifdef JS_JITSPEW1
1561void MControlInstruction::printOpcode(GenericPrinter& out) const {
1562 MDefinition::printOpcode(out);
1563 if (numSuccessors() > 0) {
1564 out.printf(" -> ");
1565 }
1566 for (size_t j = 0; j < numSuccessors(); j++) {
1567 if (j > 0) {
1568 out.printf(", ");
1569 }
1570 if (getSuccessor(j)) {
1571 out.printf("block %u", getSuccessor(j)->id());
1572 } else {
1573 out.printf("(null-to-be-patched)");
1574 }
1575 }
1576}
1577
1578void MCompare::printOpcode(GenericPrinter& out) const {
1579 MDefinition::printOpcode(out);
1580 out.printf(" %s", CodeName(jsop()));
1581}
1582
1583void MTypeOfIs::printOpcode(GenericPrinter& out) const {
1584 MDefinition::printOpcode(out);
1585 out.printf(" %s", CodeName(jsop()));
1586
1587 const char* name = "";
Value stored to 'name' during its initialization is never read
1588 switch (jstype()) {
1589 case JSTYPE_UNDEFINED:
1590 name = "undefined";
1591 break;
1592 case JSTYPE_OBJECT:
1593 name = "object";
1594 break;
1595 case JSTYPE_FUNCTION:
1596 name = "function";
1597 break;
1598 case JSTYPE_STRING:
1599 name = "string";
1600 break;
1601 case JSTYPE_NUMBER:
1602 name = "number";
1603 break;
1604 case JSTYPE_BOOLEAN:
1605 name = "boolean";
1606 break;
1607 case JSTYPE_SYMBOL:
1608 name = "symbol";
1609 break;
1610 case JSTYPE_BIGINT:
1611 name = "bigint";
1612 break;
1613 case JSTYPE_LIMIT:
1614 MOZ_CRASH("Unexpected type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected type"
, "./../../../../js/src/jit/MIR.cpp", 1614); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected type" ")"); do { MOZ_CrashSequence(
__null, 1614); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
1615 }
1616 out.printf(" '%s'", name);
1617}
1618
1619void MLoadUnboxedScalar::printOpcode(GenericPrinter& out) const {
1620 MDefinition::printOpcode(out);
1621 out.printf(" %s", Scalar::name(storageType()));
1622}
1623
1624void MLoadDataViewElement::printOpcode(GenericPrinter& out) const {
1625 MDefinition::printOpcode(out);
1626 out.printf(" %s", Scalar::name(storageType()));
1627}
1628
1629void MAssertRange::printOpcode(GenericPrinter& out) const {
1630 MDefinition::printOpcode(out);
1631 out.put(" ");
1632 assertedRange()->dump(out);
1633}
1634
1635void MNearbyInt::printOpcode(GenericPrinter& out) const {
1636 MDefinition::printOpcode(out);
1637 const char* roundingModeStr = nullptr;
1638 switch (roundingMode_) {
1639 case RoundingMode::Up:
1640 roundingModeStr = "(up)";
1641 break;
1642 case RoundingMode::Down:
1643 roundingModeStr = "(down)";
1644 break;
1645 case RoundingMode::NearestTiesToEven:
1646 roundingModeStr = "(nearest ties even)";
1647 break;
1648 case RoundingMode::TowardsZero:
1649 roundingModeStr = "(towards zero)";
1650 break;
1651 }
1652 out.printf(" %s", roundingModeStr);
1653}
1654#endif
1655
1656MDefinition* MSign::foldsTo(TempAllocator& alloc) {
1657 MDefinition* input = getOperand(0);
1658 if (!input->isConstant() ||
1659 !input->toConstant()->isTypeRepresentableAsDouble()) {
1660 return this;
1661 }
1662
1663 double in = input->toConstant()->numberToDouble();
1664 double out = js::math_sign_impl(in);
1665
1666 if (type() == MIRType::Int32) {
1667 // Decline folding if this is an int32 operation, but the result type
1668 // isn't an int32.
1669 int32_t i;
1670 if (!mozilla::NumberIsInt32(out, &i)) {
1671 return this;
1672 }
1673 return MConstant::NewInt32(alloc, i);
1674 }
1675
1676 return MConstant::NewDouble(alloc, out);
1677}
1678
1679const char* MMathFunction::FunctionName(UnaryMathFunction function) {
1680 return GetUnaryMathFunctionName(function);
1681}
1682
1683#ifdef JS_JITSPEW1
1684void MMathFunction::printOpcode(GenericPrinter& out) const {
1685 MDefinition::printOpcode(out);
1686 out.printf(" %s", FunctionName(function()));
1687}
1688#endif
1689
1690MDefinition* MMathFunction::foldsTo(TempAllocator& alloc) {
1691 MDefinition* input = getOperand(0);
1692 if (!input->isConstant() ||
1693 !input->toConstant()->isTypeRepresentableAsDouble()) {
1694 return this;
1695 }
1696
1697 UnaryMathFunctionType funPtr = GetUnaryMathFunctionPtr(function());
1698
1699 double in = input->toConstant()->numberToDouble();
1700
1701 // The function pointer call can't GC.
1702 JS::AutoSuppressGCAnalysis nogc;
1703 double out = funPtr(in);
1704
1705 if (input->type() == MIRType::Float32) {
1706 return MConstant::NewFloat32(alloc, out);
1707 }
1708 return MConstant::NewDouble(alloc, out);
1709}
1710
1711MDefinition* MAtomicIsLockFree::foldsTo(TempAllocator& alloc) {
1712 MDefinition* input = getOperand(0);
1713 if (!input->isConstant() || input->type() != MIRType::Int32) {
1714 return this;
1715 }
1716
1717 int32_t i = input->toConstant()->toInt32();
1718 return MConstant::NewBoolean(alloc, AtomicOperations::isLockfreeJS(i));
1719}
1720
1721// Define |THIS_SLOT| as part of this translation unit, as it is used to
1722// specialized the parameterized |New| function calls introduced by
1723// TRIVIAL_NEW_WRAPPERS.
1724const int32_t MParameter::THIS_SLOT;
1725
1726#ifdef JS_JITSPEW1
1727void MParameter::printOpcode(GenericPrinter& out) const {
1728 PrintOpcodeName(out, op());
1729 if (index() == THIS_SLOT) {
1730 out.printf(" THIS_SLOT");
1731 } else {
1732 out.printf(" %d", index());
1733 }
1734}
1735#endif
1736
1737HashNumber MParameter::valueHash() const {
1738 HashNumber hash = MNullaryInstruction::valueHash();
1739 hash = addU32ToHash(hash, index_);
1740 return hash;
1741}
1742
1743bool MParameter::congruentTo(const MDefinition* ins) const {
1744 if (!ins->isParameter()) {
1745 return false;
1746 }
1747
1748 return ins->toParameter()->index() == index_;
1749}
1750
1751WrappedFunction::WrappedFunction(JSFunction* nativeFun, uint16_t nargs,
1752 FunctionFlags flags)
1753 : nativeFun_(nativeFun), nargs_(nargs), flags_(flags) {
1754 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", 1754); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "isNativeWithoutJitEntry()" ")"); do { MOZ_CrashSequence
(__null, 1754); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
1755
1756#ifdef DEBUG1
1757 // If we are not running off-main thread we can assert that the
1758 // metadata is consistent.
1759 if (!CanUseExtraThreads() && nativeFun) {
1760 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"
, 1760); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nativeFun->nargs() == nargs"
")"); do { MOZ_CrashSequence(__null, 1760); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1761
1762 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", 1763); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nativeFun->isNativeWithoutJitEntry() == isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 1763); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1763 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", 1763); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nativeFun->isNativeWithoutJitEntry() == isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 1763); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1764 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", 1764); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nativeFun->hasJitEntry() == hasJitEntry()"
")"); do { MOZ_CrashSequence(__null, 1764); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1765 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", 1765); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nativeFun->isConstructor() == isConstructor()"
")"); do { MOZ_CrashSequence(__null, 1765); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1766 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", 1766); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nativeFun->isClassConstructor() == isClassConstructor()"
")"); do { MOZ_CrashSequence(__null, 1766); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1767 }
1768#endif
1769}
1770
1771MCall* MCall::New(TempAllocator& alloc, WrappedFunction* target, size_t maxArgc,
1772 size_t numActualArgs, bool construct, bool ignoresReturnValue,
1773 bool isDOMCall, mozilla::Maybe<DOMObjectKind> objectKind,
1774 mozilla::Maybe<gc::Heap> initialHeap) {
1775 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"
, 1775); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isDOMCall == objectKind.isSome()"
")"); do { MOZ_CrashSequence(__null, 1775); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1776 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"
, 1776); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isDOMCall == initialHeap.isSome()"
")"); do { MOZ_CrashSequence(__null, 1776); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1777
1778 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", 1778); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "maxArgc >= numActualArgs" ")"); do { MOZ_CrashSequence
(__null, 1778); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1779 MCall* ins;
1780 if (isDOMCall) {
1781 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"
, 1781); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!construct"
")"); do { MOZ_CrashSequence(__null, 1781); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1782 ins = new (alloc)
1783 MCallDOMNative(target, numActualArgs, *objectKind, *initialHeap);
1784 } else {
1785 ins =
1786 new (alloc) MCall(target, numActualArgs, construct, ignoresReturnValue);
1787 }
1788 if (!ins->init(alloc, maxArgc + NumNonArgumentOperands)) {
1789 return nullptr;
1790 }
1791 return ins;
1792}
1793
1794AliasSet MCallDOMNative::getAliasSet() const {
1795 const JSJitInfo* jitInfo = getJitInfo();
1796
1797 // If we don't know anything about the types of our arguments, we have to
1798 // assume that type-coercions can have side-effects, so we need to alias
1799 // everything.
1800 if (jitInfo->aliasSet() == JSJitInfo::AliasEverything ||
1801 !jitInfo->isTypedMethodJitInfo()) {
1802 return AliasSet::Store(AliasSet::Any);
1803 }
1804
1805 uint32_t argIndex = 0;
1806 const JSTypedMethodJitInfo* methodInfo =
1807 reinterpret_cast<const JSTypedMethodJitInfo*>(jitInfo);
1808 for (const JSJitInfo::ArgType* argType = methodInfo->argTypes;
1809 *argType != JSJitInfo::ArgTypeListEnd; ++argType, ++argIndex) {
1810 if (argIndex >= numActualArgs()) {
1811 // Passing through undefined can't have side-effects
1812 continue;
1813 }
1814 // getArg(0) is "this", so skip it
1815 MDefinition* arg = getArg(argIndex + 1);
1816 MIRType actualType = arg->type();
1817 // The only way to reliably avoid side-effects given the information we
1818 // have here is if we're passing in a known primitive value to an
1819 // argument that expects a primitive value.
1820 //
1821 // XXXbz maybe we need to communicate better information. For example,
1822 // a sequence argument will sort of unavoidably have side effects, while
1823 // a typed array argument won't have any, but both are claimed to be
1824 // JSJitInfo::Object. But if we do that, we need to watch out for our
1825 // movability/DCE-ability bits: if we have an arg type that can reliably
1826 // throw an exception on conversion, that might not affect our alias set
1827 // per se, but it should prevent us being moved or DCE-ed, unless we
1828 // know the incoming things match that arg type and won't throw.
1829 //
1830 if ((actualType == MIRType::Value || actualType == MIRType::Object) ||
1831 (*argType & JSJitInfo::Object)) {
1832 return AliasSet::Store(AliasSet::Any);
1833 }
1834 }
1835
1836 // We checked all the args, and they check out. So we only alias DOM
1837 // mutations or alias nothing, depending on the alias set in the jitinfo.
1838 if (jitInfo->aliasSet() == JSJitInfo::AliasNone) {
1839 return AliasSet::None();
1840 }
1841
1842 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", 1842); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "jitInfo->aliasSet() == JSJitInfo::AliasDOMSets"
")"); do { MOZ_CrashSequence(__null, 1842); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1843 return AliasSet::Load(AliasSet::DOMProperty);
1844}
1845
1846void MCallDOMNative::computeMovable() {
1847 // We are movable if the jitinfo says we can be and if we're also not
1848 // effectful. The jitinfo can't check for the latter, since it depends on
1849 // the types of our arguments.
1850 const JSJitInfo* jitInfo = getJitInfo();
1851
1852 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"
, 1853); AnnotateMozCrashReason("MOZ_ASSERT" "(" "jitInfo->aliasSet() != JSJitInfo::AliasEverything"
")"); do { MOZ_CrashSequence(__null, 1853); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
1853 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"
, 1853); AnnotateMozCrashReason("MOZ_ASSERT" "(" "jitInfo->aliasSet() != JSJitInfo::AliasEverything"
")"); do { MOZ_CrashSequence(__null, 1853); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
1854
1855 if (jitInfo->isMovable && !isEffectful()) {
1856 setMovable();
1857 }
1858}
1859
1860bool MCallDOMNative::congruentTo(const MDefinition* ins) const {
1861 if (!isMovable()) {
1862 return false;
1863 }
1864
1865 if (!ins->isCall()) {
1866 return false;
1867 }
1868
1869 const MCall* call = ins->toCall();
1870
1871 if (!call->isCallDOMNative()) {
1872 return false;
1873 }
1874
1875 if (getSingleTarget() != call->getSingleTarget()) {
1876 return false;
1877 }
1878
1879 if (isConstructing() != call->isConstructing()) {
1880 return false;
1881 }
1882
1883 if (numActualArgs() != call->numActualArgs()) {
1884 return false;
1885 }
1886
1887 if (!congruentIfOperandsEqual(call)) {
1888 return false;
1889 }
1890
1891 // The other call had better be movable at this point!
1892 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", 1892); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "call->isMovable()" ")"); do { MOZ_CrashSequence
(__null, 1892); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1893
1894 return true;
1895}
1896
1897const JSJitInfo* MCallDOMNative::getJitInfo() const {
1898 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"
, 1898); AnnotateMozCrashReason("MOZ_ASSERT" "(" "getSingleTarget()->hasJitInfo()"
")"); do { MOZ_CrashSequence(__null, 1898); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1899 return getSingleTarget()->jitInfo();
1900}
1901
1902MCallClassHook* MCallClassHook::New(TempAllocator& alloc, JSNative target,
1903 uint32_t argc, bool constructing) {
1904 auto* ins = new (alloc) MCallClassHook(target, constructing);
1905
1906 // Add callee + |this| + (if constructing) newTarget.
1907 uint32_t numOperands = 2 + argc + constructing;
1908
1909 if (!ins->init(alloc, numOperands)) {
1910 return nullptr;
1911 }
1912
1913 return ins;
1914}
1915
1916MDefinition* MStringLength::foldsTo(TempAllocator& alloc) {
1917 if (string()->isConstant()) {
1918 JSOffThreadAtom* str = string()->toConstant()->toString();
1919 return MConstant::NewInt32(alloc, str->length());
1920 }
1921
1922 // MFromCharCode returns a one-element string.
1923 if (string()->isFromCharCode()) {
1924 return MConstant::NewInt32(alloc, 1);
1925 }
1926
1927 return this;
1928}
1929
1930MDefinition* MConcat::foldsTo(TempAllocator& alloc) {
1931 if (lhs()->isConstant() && lhs()->toConstant()->toString()->empty()) {
1932 return rhs();
1933 }
1934
1935 if (rhs()->isConstant() && rhs()->toConstant()->toString()->empty()) {
1936 return lhs();
1937 }
1938
1939 return this;
1940}
1941
1942MDefinition* MStringConvertCase::foldsTo(TempAllocator& alloc) {
1943 MDefinition* string = this->string();
1944
1945 // Handle the pattern |str[idx].toUpperCase()| and simplify it from
1946 // |StringConvertCase(FromCharCode(CharCodeAt(str, idx)))| to just
1947 // |CharCodeConvertCase(CharCodeAt(str, idx))|.
1948 if (string->isFromCharCode()) {
1949 auto* charCode = string->toFromCharCode()->code();
1950 return MCharCodeConvertCase::New(alloc, charCode, stringCase_);
1951 }
1952
1953 // Handle the pattern |num.toString(base).toUpperCase()| and simplify it to
1954 // directly return the string representation in the correct case.
1955 if (string->isInt32ToStringWithBase()) {
1956 auto* toString = string->toInt32ToStringWithBase();
1957
1958 if (toString->stringCase() == stringCase_) {
1959 return toString;
1960 }
1961 return MInt32ToStringWithBase::New(alloc, toString->input(),
1962 toString->base(), stringCase_);
1963 }
1964
1965 return this;
1966}
1967
1968// Return true if |def| is `MConstant(Int32(0))`.
1969static bool IsConstantZeroInt32(MDefinition* def) {
1970 return def->isConstant() && def->toConstant()->isInt32(0);
1971}
1972
1973// If |def| is `MBitOr` and one operand is `MConstant(Int32(0))`, then return
1974// the other operand. Otherwise return |def|.
1975static MDefinition* RemoveUnnecessaryBitOps(MDefinition* def) {
1976 if (def->isBitOr()) {
1977 auto* bitOr = def->toBitOr();
1978 if (IsConstantZeroInt32(bitOr->lhs())) {
1979 return bitOr->rhs();
1980 }
1981 if (IsConstantZeroInt32(bitOr->rhs())) {
1982 return bitOr->lhs();
1983 }
1984 }
1985 return def;
1986}
1987
1988// Return a match if both operands of |binary| have the requested types. If
1989// |binary| is commutative, the operands may appear in any order.
1990template <typename Lhs, typename Rhs>
1991static mozilla::Maybe<std::pair<Lhs*, Rhs*>> MatchOperands(
1992 MBinaryInstruction* binary) {
1993 auto* lhs = binary->lhs();
1994 auto* rhs = binary->rhs();
1995 if (lhs->is<Lhs>() && rhs->is<Rhs>()) {
1996 return mozilla::Some(std::pair{lhs->to<Lhs>(), rhs->to<Rhs>()});
1997 }
1998 if (binary->isCommutative() && rhs->is<Lhs>() && lhs->is<Rhs>()) {
1999 return mozilla::Some(std::pair{rhs->to<Lhs>(), lhs->to<Rhs>()});
2000 }
2001 return mozilla::Nothing();
2002}
2003
2004static bool IsSubstrTo(MSubstr* substr, int32_t len) {
2005 // We want to match this pattern:
2006 //
2007 // Substr(string, Constant(0), Min(Constant(length), StringLength(string)))
2008 //
2009 // which is generated for the self-hosted `String.p.{substring,slice,substr}`
2010 // functions when called with constants `start` and `end` parameters.
2011
2012 if (!IsConstantZeroInt32(substr->begin())) {
2013 return false;
2014 }
2015
2016 // Unnecessary bit-ops haven't yet been removed.
2017 auto* length = RemoveUnnecessaryBitOps(substr->length());
2018 if (!length->isMinMax() || length->toMinMax()->isMax()) {
2019 return false;
2020 }
2021
2022 auto match = MatchOperands<MConstant, MStringLength>(length->toMinMax());
2023 if (!match) {
2024 return false;
2025 }
2026
2027 // Ensure |len| matches the substring's length.
2028 auto [cst, strLength] = *match;
2029 return cst->isInt32(len) && strLength->string() == substr->string();
2030}
2031
2032static bool IsSubstrLast(MSubstr* substr, int32_t start) {
2033 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"
, 2033); AnnotateMozCrashReason("MOZ_ASSERT" "(" "start < 0"
") (" "start from end is negative" ")"); do { MOZ_CrashSequence
(__null, 2033); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2034
2035 // We want to match either this pattern:
2036 //
2037 // begin = Max(StringLength(string) + start, 0)
2038 // length = Max(StringLength(string) - begin, 0)
2039 // Substr(string, begin, length)
2040 //
2041 // or this pattern:
2042 //
2043 // begin = Max(StringLength(string) + start, 0)
2044 // length = Min(StringLength(string), StringLength(string) - begin)
2045 // Substr(string, begin, length)
2046 //
2047 // which is generated for the self-hosted `String.p.{slice,substr}`
2048 // functions when called with parameters `start < 0` and `end = undefined`.
2049
2050 auto* string = substr->string();
2051
2052 // Unnecessary bit-ops haven't yet been removed.
2053 auto* begin = RemoveUnnecessaryBitOps(substr->begin());
2054 auto* length = RemoveUnnecessaryBitOps(substr->length());
2055
2056 // Matches: Max(StringLength(string) + start, 0)
2057 auto matchesBegin = [&]() {
2058 if (!begin->isMinMax() || !begin->toMinMax()->isMax()) {
2059 return false;
2060 }
2061
2062 auto maxOperands = MatchOperands<MAdd, MConstant>(begin->toMinMax());
2063 if (!maxOperands) {
2064 return false;
2065 }
2066
2067 auto [add, cst] = *maxOperands;
2068 if (!cst->isInt32(0)) {
2069 return false;
2070 }
2071
2072 auto addOperands = MatchOperands<MStringLength, MConstant>(add);
2073 if (!addOperands) {
2074 return false;
2075 }
2076
2077 auto [strLength, cstAdd] = *addOperands;
2078 return strLength->string() == string && cstAdd->isInt32(start);
2079 };
2080
2081 // Matches: Max(StringLength(string) - begin, 0)
2082 auto matchesSliceLength = [&]() {
2083 if (!length->isMinMax() || !length->toMinMax()->isMax()) {
2084 return false;
2085 }
2086
2087 auto maxOperands = MatchOperands<MSub, MConstant>(length->toMinMax());
2088 if (!maxOperands) {
2089 return false;
2090 }
2091
2092 auto [sub, cst] = *maxOperands;
2093 if (!cst->isInt32(0)) {
2094 return false;
2095 }
2096
2097 auto subOperands = MatchOperands<MStringLength, MMinMax>(sub);
2098 if (!subOperands) {
2099 return false;
2100 }
2101
2102 auto [strLength, minmax] = *subOperands;
2103 return strLength->string() == string && minmax == begin;
2104 };
2105
2106 // Matches: Min(StringLength(string), StringLength(string) - begin)
2107 auto matchesSubstrLength = [&]() {
2108 if (!length->isMinMax() || length->toMinMax()->isMax()) {
2109 return false;
2110 }
2111
2112 auto minOperands = MatchOperands<MStringLength, MSub>(length->toMinMax());
2113 if (!minOperands) {
2114 return false;
2115 }
2116
2117 auto [strLength1, sub] = *minOperands;
2118 if (strLength1->string() != string) {
2119 return false;
2120 }
2121
2122 auto subOperands = MatchOperands<MStringLength, MMinMax>(sub);
2123 if (!subOperands) {
2124 return false;
2125 }
2126
2127 auto [strLength2, minmax] = *subOperands;
2128 return strLength2->string() == string && minmax == begin;
2129 };
2130
2131 return matchesBegin() && (matchesSliceLength() || matchesSubstrLength());
2132}
2133
2134MDefinition* MSubstr::foldsTo(TempAllocator& alloc) {
2135 // Fold |str.substring(0, 1)| to |str.charAt(0)|.
2136 if (IsSubstrTo(this, 1)) {
2137 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"
, 2137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsConstantZeroInt32(begin())"
")"); do { MOZ_CrashSequence(__null, 2137); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2138
2139 auto* charCode = MCharCodeAtOrNegative::New(alloc, string(), begin());
2140 block()->insertBefore(this, charCode);
2141
2142 return MFromCharCodeEmptyIfNegative::New(alloc, charCode);
2143 }
2144
2145 // Fold |str.slice(-1)| and |str.substr(-1)| to |str.charAt(str.length + -1)|.
2146 if (IsSubstrLast(this, -1)) {
2147 auto* length = MStringLength::New(alloc, string());
2148 block()->insertBefore(this, length);
2149
2150 auto* index = MConstant::NewInt32(alloc, -1);
2151 block()->insertBefore(this, index);
2152
2153 // Folded MToRelativeStringIndex, see MToRelativeStringIndex::foldsTo.
2154 //
2155 // Safe to truncate because |length| is never negative.
2156 auto* add = MAdd::New(alloc, index, length, TruncateKind::Truncate);
2157 block()->insertBefore(this, add);
2158
2159 auto* charCode = MCharCodeAtOrNegative::New(alloc, string(), add);
2160 block()->insertBefore(this, charCode);
2161
2162 return MFromCharCodeEmptyIfNegative::New(alloc, charCode);
2163 }
2164
2165 return this;
2166}
2167
2168MDefinition* MCharCodeAt::foldsTo(TempAllocator& alloc) {
2169 MDefinition* string = this->string();
2170 if (!string->isConstant() && !string->isFromCharCode()) {
2171 return this;
2172 }
2173
2174 MDefinition* index = this->index();
2175 if (index->isSpectreMaskIndex()) {
2176 index = index->toSpectreMaskIndex()->index();
2177 }
2178 if (!index->isConstant()) {
2179 return this;
2180 }
2181 int32_t idx = index->toConstant()->toInt32();
2182
2183 // Handle the pattern |s[idx].charCodeAt(0)|.
2184 if (string->isFromCharCode()) {
2185 if (idx != 0) {
2186 return this;
2187 }
2188
2189 // Simplify |CharCodeAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just
2190 // |CharCodeAt(s, idx)|.
2191 auto* charCode = string->toFromCharCode()->code();
2192 if (!charCode->isCharCodeAt()) {
2193 return this;
2194 }
2195
2196 return charCode;
2197 }
2198
2199 JSOffThreadAtom* str = string->toConstant()->toString();
2200 if (idx < 0 || uint32_t(idx) >= str->length()) {
2201 return this;
2202 }
2203
2204 char16_t ch = str->latin1OrTwoByteChar(idx);
2205 return MConstant::NewInt32(alloc, ch);
2206}
2207
2208MDefinition* MCodePointAt::foldsTo(TempAllocator& alloc) {
2209 MDefinition* string = this->string();
2210 if (!string->isConstant() && !string->isFromCharCode()) {
2211 return this;
2212 }
2213
2214 MDefinition* index = this->index();
2215 if (index->isSpectreMaskIndex()) {
2216 index = index->toSpectreMaskIndex()->index();
2217 }
2218 if (!index->isConstant()) {
2219 return this;
2220 }
2221 int32_t idx = index->toConstant()->toInt32();
2222
2223 // Handle the pattern |s[idx].codePointAt(0)|.
2224 if (string->isFromCharCode()) {
2225 if (idx != 0) {
2226 return this;
2227 }
2228
2229 // Simplify |CodePointAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just
2230 // |CharCodeAt(s, idx)|.
2231 auto* charCode = string->toFromCharCode()->code();
2232 if (!charCode->isCharCodeAt()) {
2233 return this;
2234 }
2235
2236 return charCode;
2237 }
2238
2239 JSOffThreadAtom* str = string->toConstant()->toString();
2240 if (idx < 0 || uint32_t(idx) >= str->length()) {
2241 return this;
2242 }
2243
2244 char32_t first = str->latin1OrTwoByteChar(idx);
2245 if (unicode::IsLeadSurrogate(first) && uint32_t(idx) + 1 < str->length()) {
2246 char32_t second = str->latin1OrTwoByteChar(idx + 1);
2247 if (unicode::IsTrailSurrogate(second)) {
2248 first = unicode::UTF16Decode(first, second);
2249 }
2250 }
2251 return MConstant::NewInt32(alloc, first);
2252}
2253
2254MDefinition* MLinearizeString::foldsTo(TempAllocator& alloc) {
2255 MDefinition* string = this->string();
2256 if (!string->isConstant()) {
2257 return this;
2258 }
2259
2260 // Constant strings are atoms, which are guaranteed to be linear.
2261 static_assert(std::is_same_v<decltype(string->toConstant()->toString()),
2262 JSOffThreadAtom*>);
2263 return string;
2264}
2265
2266MDefinition* MToRelativeStringIndex::foldsTo(TempAllocator& alloc) {
2267 MDefinition* index = this->index();
2268 MDefinition* length = this->length();
2269
2270 if (!index->isConstant()) {
2271 return this;
2272 }
2273 if (!length->isStringLength() && !length->isConstant()) {
2274 return this;
2275 }
2276 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"
, 2276); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length->toConstant()->toInt32() >= 0"
")"); do { MOZ_CrashSequence(__null, 2276); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
2277
2278 int32_t relativeIndex = index->toConstant()->toInt32();
2279 if (relativeIndex >= 0) {
2280 return index;
2281 }
2282
2283 // Safe to truncate because |length| is never negative.
2284 return MAdd::New(alloc, index, length, TruncateKind::Truncate);
2285}
2286
2287template <size_t Arity>
2288[[nodiscard]] static bool EnsureFloatInputOrConvert(
2289 MAryInstruction<Arity>* owner, TempAllocator& alloc) {
2290 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"
, 2291); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsFloatingPointType(owner->type())"
") (" "Floating point types must check consumers" ")"); do {
MOZ_CrashSequence(__null, 2291); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
2291 "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"
, 2291); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsFloatingPointType(owner->type())"
") (" "Floating point types must check consumers" ")"); do {
MOZ_CrashSequence(__null, 2291); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
2292
2293 if (AllOperandsCanProduceFloat32(owner)) {
2294 return true;
2295 }
2296 ConvertOperandsToDouble(owner, alloc);
2297 return false;
2298}
2299
2300template <size_t Arity>
2301[[nodiscard]] static bool EnsureFloatConsumersAndInputOrConvert(
2302 MAryInstruction<Arity>* owner, TempAllocator& alloc) {
2303 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"
, 2304); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsFloatingPointType(owner->type())"
") (" "Integer types don't need to check consumers" ")"); do
{ MOZ_CrashSequence(__null, 2304); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
2304 "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"
, 2304); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsFloatingPointType(owner->type())"
") (" "Integer types don't need to check consumers" ")"); do
{ MOZ_CrashSequence(__null, 2304); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
2305
2306 if (AllOperandsCanProduceFloat32(owner) &&
2307 CheckUsesAreFloat32Consumers(owner)) {
2308 return true;
2309 }
2310 ConvertOperandsToDouble(owner, alloc);
2311 return false;
2312}
2313
2314void MFloor::trySpecializeFloat32(TempAllocator& alloc) {
2315 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", 2315); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Int32" ")"); do { MOZ_CrashSequence
(__null, 2315); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2316 if (EnsureFloatInputOrConvert(this, alloc)) {
2317 specialization_ = MIRType::Float32;
2318 }
2319}
2320
2321void MCeil::trySpecializeFloat32(TempAllocator& alloc) {
2322 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", 2322); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Int32" ")"); do { MOZ_CrashSequence
(__null, 2322); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2323 if (EnsureFloatInputOrConvert(this, alloc)) {
2324 specialization_ = MIRType::Float32;
2325 }
2326}
2327
2328void MRound::trySpecializeFloat32(TempAllocator& alloc) {
2329 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", 2329); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Int32" ")"); do { MOZ_CrashSequence
(__null, 2329); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2330 if (EnsureFloatInputOrConvert(this, alloc)) {
2331 specialization_ = MIRType::Float32;
2332 }
2333}
2334
2335void MTrunc::trySpecializeFloat32(TempAllocator& alloc) {
2336 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", 2336); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Int32" ")"); do { MOZ_CrashSequence
(__null, 2336); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2337 if (EnsureFloatInputOrConvert(this, alloc)) {
2338 specialization_ = MIRType::Float32;
2339 }
2340}
2341
2342void MNearbyInt::trySpecializeFloat32(TempAllocator& alloc) {
2343 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
2344 specialization_ = MIRType::Float32;
2345 setResultType(MIRType::Float32);
2346 }
2347}
2348
2349void MRoundToDouble::trySpecializeFloat32(TempAllocator& alloc) {
2350 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
2351 specialization_ = MIRType::Float32;
2352 setResultType(MIRType::Float32);
2353 }
2354}
2355
2356MGoto* MGoto::New(TempAllocator& alloc, MBasicBlock* target) {
2357 return new (alloc) MGoto(target);
2358}
2359
2360MGoto* MGoto::New(TempAllocator::Fallible alloc, MBasicBlock* target) {
2361 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"
, 2361); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { MOZ_CrashSequence(__null, 2361); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2362 return new (alloc) MGoto(target);
2363}
2364
2365MGoto* MGoto::New(TempAllocator& alloc) { return new (alloc) MGoto(nullptr); }
2366
2367MDefinition* MBox::foldsTo(TempAllocator& alloc) {
2368 if (input()->isUnbox()) {
2369 return input()->toUnbox()->input();
2370 }
2371 return this;
2372}
2373
2374#ifdef JS_JITSPEW1
2375void MUnbox::printOpcode(GenericPrinter& out) const {
2376 PrintOpcodeName(out, op());
2377 out.printf(" ");
2378 getOperand(0)->printName(out);
2379 out.printf(" ");
2380
2381 switch (type()) {
2382 case MIRType::Int32:
2383 out.printf("to Int32");
2384 break;
2385 case MIRType::Double:
2386 out.printf("to Double");
2387 break;
2388 case MIRType::Boolean:
2389 out.printf("to Boolean");
2390 break;
2391 case MIRType::String:
2392 out.printf("to String");
2393 break;
2394 case MIRType::Symbol:
2395 out.printf("to Symbol");
2396 break;
2397 case MIRType::BigInt:
2398 out.printf("to BigInt");
2399 break;
2400 case MIRType::Object:
2401 out.printf("to Object");
2402 break;
2403 default:
2404 break;
2405 }
2406
2407 switch (mode()) {
2408 case Fallible:
2409 out.printf(" (fallible)");
2410 break;
2411 case Infallible:
2412 out.printf(" (infallible)");
2413 break;
2414 default:
2415 break;
2416 }
2417}
2418#endif
2419
2420MDefinition* MUnbox::foldsTo(TempAllocator& alloc) {
2421 if (input()->isBox()) {
2422 MDefinition* unboxed = input()->toBox()->input();
2423
2424 // Fold MUnbox(MBox(x)) => x if types match.
2425 if (unboxed->type() == type()) {
2426 if (fallible()) {
2427 unboxed->setImplicitlyUsedUnchecked();
2428 }
2429 return unboxed;
2430 }
2431
2432 // Fold MUnbox(MBox(x)) => MToDouble(x) if possible.
2433 if (type() == MIRType::Double &&
2434 IsTypeRepresentableAsDouble(unboxed->type())) {
2435 if (unboxed->isConstant()) {
2436 return MConstant::NewDouble(alloc,
2437 unboxed->toConstant()->numberToDouble());
2438 }
2439
2440 return MToDouble::New(alloc, unboxed);
2441 }
2442
2443 // MUnbox<Int32>(MBox<Double>(x)) will always fail, even if x can be
2444 // represented as an Int32. Fold to avoid unnecessary bailouts.
2445 if (type() == MIRType::Int32 && unboxed->type() == MIRType::Double) {
2446 auto* folded = MToNumberInt32::New(alloc, unboxed,
2447 IntConversionInputKind::NumbersOnly);
2448 folded->setGuard();
2449 return folded;
2450 }
2451 }
2452
2453 return this;
2454}
2455
2456#ifdef DEBUG1
2457void MPhi::assertLoopPhi() const {
2458 // getLoopPredecessorOperand and getLoopBackedgeOperand rely on these
2459 // predecessors being at known indices.
2460 if (block()->numPredecessors() == 2) {
2461 MBasicBlock* pred = block()->getPredecessor(0);
2462 MBasicBlock* back = block()->getPredecessor(1);
2463 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", 2463); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "pred == block()->loopPredecessor()" ")"
); do { MOZ_CrashSequence(__null, 2463); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2464 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", 2464); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "pred->successorWithPhis() == block()" ")"
); do { MOZ_CrashSequence(__null, 2464); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2465 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", 2465); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "pred->positionInPhiSuccessor() == 0" ")"
); do { MOZ_CrashSequence(__null, 2465); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2466 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"
, 2466); AnnotateMozCrashReason("MOZ_ASSERT" "(" "back == block()->backedge()"
")"); do { MOZ_CrashSequence(__null, 2466); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2467 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", 2467); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "back->successorWithPhis() == block()" ")"
); do { MOZ_CrashSequence(__null, 2467); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2468 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", 2468); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "back->positionInPhiSuccessor() == 1" ")"
); do { MOZ_CrashSequence(__null, 2468); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2469 } else {
2470 // After we remove fake loop predecessors for loop headers that
2471 // are only reachable via OSR, the only predecessor is the
2472 // loop backedge.
2473 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"
, 2473); AnnotateMozCrashReason("MOZ_ASSERT" "(" "block()->numPredecessors() == 1"
")"); do { MOZ_CrashSequence(__null, 2473); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2474 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"
, 2474); AnnotateMozCrashReason("MOZ_ASSERT" "(" "block()->graph().osrBlock()"
")"); do { MOZ_CrashSequence(__null, 2474); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2475 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", 2475); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!block()->graph().canBuildDominators()"
")"); do { MOZ_CrashSequence(__null, 2475); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2476 MBasicBlock* back = block()->getPredecessor(0);
2477 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"
, 2477); AnnotateMozCrashReason("MOZ_ASSERT" "(" "back == block()->backedge()"
")"); do { MOZ_CrashSequence(__null, 2477); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2478 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", 2478); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "back->successorWithPhis() == block()" ")"
); do { MOZ_CrashSequence(__null, 2478); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2479 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", 2479); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "back->positionInPhiSuccessor() == 0" ")"
); do { MOZ_CrashSequence(__null, 2479); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2480 }
2481}
2482#endif
2483
2484MDefinition* MPhi::getLoopPredecessorOperand() const {
2485 // This should not be called after removing fake loop predecessors.
2486 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"
, 2486); AnnotateMozCrashReason("MOZ_ASSERT" "(" "block()->numPredecessors() == 2"
")"); do { MOZ_CrashSequence(__null, 2486); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2487 assertLoopPhi();
2488 return getOperand(0);
2489}
2490
2491MDefinition* MPhi::getLoopBackedgeOperand() const {
2492 assertLoopPhi();
2493 uint32_t idx = block()->numPredecessors() == 2 ? 1 : 0;
2494 return getOperand(idx);
2495}
2496
2497void MPhi::removeOperand(size_t index) {
2498 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", 2498); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "index < numOperands()" ")"); do { MOZ_CrashSequence
(__null, 2498); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2499 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", 2499); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "getUseFor(index)->index() == index" ")"
); do { MOZ_CrashSequence(__null, 2499); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2500 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", 2500); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "getUseFor(index)->consumer() == this" ")"
); do { MOZ_CrashSequence(__null, 2500); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2501
2502 // If we have phi(..., a, b, c, d, ..., z) and we plan
2503 // on removing a, then first shift downward so that we have
2504 // phi(..., b, c, d, ..., z, z):
2505 MUse* p = inputs_.begin() + index;
2506 MUse* e = inputs_.end();
2507 p->producer()->removeUse(p);
2508 for (; p < e - 1; ++p) {
2509 MDefinition* producer = (p + 1)->producer();
2510 p->setProducerUnchecked(producer);
2511 producer->replaceUse(p + 1, p);
2512 }
2513
2514 // truncate the inputs_ list:
2515 inputs_.popBack();
2516}
2517
2518void MPhi::removeAllOperands() {
2519 for (MUse& p : inputs_) {
2520 p.producer()->removeUse(&p);
2521 }
2522 inputs_.clear();
2523}
2524
2525MDefinition* MPhi::foldsTernary(TempAllocator& alloc) {
2526 /* Look if this MPhi is a ternary construct.
2527 * This is a very loose term as it actually only checks for
2528 *
2529 * MTest X
2530 * / \
2531 * ... ...
2532 * \ /
2533 * MPhi X Y
2534 *
2535 * Which we will simply call:
2536 * x ? x : y or x ? y : x
2537 */
2538
2539 if (numOperands() != 2) {
2540 return nullptr;
2541 }
2542
2543 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"
, 2543); AnnotateMozCrashReason("MOZ_ASSERT" "(" "block()->numPredecessors() == 2"
")"); do { MOZ_CrashSequence(__null, 2543); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2544
2545 MBasicBlock* pred = block()->immediateDominator();
2546 if (!pred || !pred->lastIns()->isTest()) {
2547 return nullptr;
2548 }
2549
2550 MTest* test = pred->lastIns()->toTest();
2551
2552 // True branch may only dominate one edge of MPhi.
2553 if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
2554 test->ifTrue()->dominates(block()->getPredecessor(1))) {
2555 return nullptr;
2556 }
2557
2558 // False branch may only dominate one edge of MPhi.
2559 if (test->ifFalse()->dominates(block()->getPredecessor(0)) ==
2560 test->ifFalse()->dominates(block()->getPredecessor(1))) {
2561 return nullptr;
2562 }
2563
2564 // True and false branch must dominate different edges of MPhi.
2565 if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
2566 test->ifFalse()->dominates(block()->getPredecessor(0))) {
2567 return nullptr;
2568 }
2569
2570 // We found a ternary construct.
2571 bool firstIsTrueBranch =
2572 test->ifTrue()->dominates(block()->getPredecessor(0));
2573 MDefinition* trueDef = firstIsTrueBranch ? getOperand(0) : getOperand(1);
2574 MDefinition* falseDef = firstIsTrueBranch ? getOperand(1) : getOperand(0);
2575
2576 // Accept either
2577 // testArg ? testArg : constant or
2578 // testArg ? constant : testArg
2579 if (!trueDef->isConstant() && !falseDef->isConstant()) {
2580 return nullptr;
2581 }
2582
2583 MConstant* c =
2584 trueDef->isConstant() ? trueDef->toConstant() : falseDef->toConstant();
2585 MDefinition* testArg = (trueDef == c) ? falseDef : trueDef;
2586 if (testArg != test->input()) {
2587 return nullptr;
2588 }
2589
2590 // This check should be a tautology, except that the constant might be the
2591 // result of the removal of a branch. In such case the domination scope of
2592 // the block which is holding the constant might be incomplete. This
2593 // condition is used to prevent doing this optimization based on incomplete
2594 // information.
2595 //
2596 // As GVN removed a branch, it will update the dominations rules before
2597 // trying to fold this MPhi again. Thus, this condition does not inhibit
2598 // this optimization.
2599 MBasicBlock* truePred = block()->getPredecessor(firstIsTrueBranch ? 0 : 1);
2600 MBasicBlock* falsePred = block()->getPredecessor(firstIsTrueBranch ? 1 : 0);
2601 if (!trueDef->block()->dominates(truePred) ||
2602 !falseDef->block()->dominates(falsePred)) {
2603 return nullptr;
2604 }
2605
2606 // If testArg is an int32 type we can:
2607 // - fold testArg ? testArg : 0 to testArg
2608 // - fold testArg ? 0 : testArg to 0
2609 if (testArg->type() == MIRType::Int32 && c->numberToDouble() == 0) {
2610 testArg->setGuardRangeBailoutsUnchecked();
2611
2612 // When folding to the constant we need to hoist it.
2613 if (trueDef == c && !c->block()->dominates(block())) {
2614 c->block()->moveBefore(pred->lastIns(), c);
2615 }
2616 return trueDef;
2617 }
2618
2619 // If testArg is an double type we can:
2620 // - fold testArg ? testArg : 0.0 to MNaNToZero(testArg)
2621 if (testArg->type() == MIRType::Double &&
2622 mozilla::IsPositiveZero(c->numberToDouble()) && c != trueDef) {
2623 MNaNToZero* replace = MNaNToZero::New(alloc, testArg);
2624 test->block()->insertBefore(test, replace);
2625 return replace;
2626 }
2627
2628 // If testArg is a string type we can:
2629 // - fold testArg ? testArg : "" to testArg
2630 // - fold testArg ? "" : testArg to ""
2631 if (testArg->type() == MIRType::String && c->toString()->empty()) {
2632 // When folding to the constant we need to hoist it.
2633 if (trueDef == c && !c->block()->dominates(block())) {
2634 c->block()->moveBefore(pred->lastIns(), c);
2635 }
2636 return trueDef;
2637 }
2638
2639 return nullptr;
2640}
2641
2642MDefinition* MPhi::operandIfRedundant() {
2643 if (inputs_.length() == 0) {
2644 return nullptr;
2645 }
2646
2647 // If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)),
2648 // returns the operand that it will always be equal to (a, in
2649 // those two cases).
2650 MDefinition* first = getOperand(0);
2651 for (size_t i = 1, e = numOperands(); i < e; i++) {
2652 MDefinition* op = getOperand(i);
2653 if (op != first && op != this) {
2654 return nullptr;
2655 }
2656 }
2657 return first;
2658}
2659
2660MDefinition* MPhi::foldsTo(TempAllocator& alloc) {
2661 if (MDefinition* def = operandIfRedundant()) {
2662 return def;
2663 }
2664
2665 if (MDefinition* def = foldsTernary(alloc)) {
2666 return def;
2667 }
2668
2669 return this;
2670}
2671
2672bool MPhi::congruentTo(const MDefinition* ins) const {
2673 if (!ins->isPhi()) {
2674 return false;
2675 }
2676
2677 // Phis in different blocks may have different control conditions.
2678 // For example, these phis:
2679 //
2680 // if (p)
2681 // goto a
2682 // a:
2683 // t = phi(x, y)
2684 //
2685 // if (q)
2686 // goto b
2687 // b:
2688 // s = phi(x, y)
2689 //
2690 // have identical operands, but they are not equvalent because t is
2691 // effectively p?x:y and s is effectively q?x:y.
2692 //
2693 // For now, consider phis in different blocks incongruent.
2694 if (ins->block() != block()) {
2695 return false;
2696 }
2697
2698 return congruentIfOperandsEqual(ins);
2699}
2700
2701void MPhi::updateForReplacement(MPhi* other) {
2702 // This function is called to fix the current Phi flags using it as a
2703 // replacement of the other Phi instruction |other|.
2704 //
2705 // When dealing with usage analysis, any Use will replace all other values,
2706 // such as Unused and Unknown. Unless both are Unused, the merge would be
2707 // Unknown.
2708 if (usageAnalysis_ == PhiUsage::Used ||
2709 other->usageAnalysis_ == PhiUsage::Used) {
2710 usageAnalysis_ = PhiUsage::Used;
2711 } else if (usageAnalysis_ != other->usageAnalysis_) {
2712 // this == unused && other == unknown
2713 // or this == unknown && other == unused
2714 usageAnalysis_ = PhiUsage::Unknown;
2715 } else {
2716 // this == unused && other == unused
2717 // or this == unknown && other = unknown
2718 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", 2719); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "usageAnalysis_ == PhiUsage::Unused || usageAnalysis_ == PhiUsage::Unknown"
")"); do { MOZ_CrashSequence(__null, 2719); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2719 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", 2719); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "usageAnalysis_ == PhiUsage::Unused || usageAnalysis_ == PhiUsage::Unknown"
")"); do { MOZ_CrashSequence(__null, 2719); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2720 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", 2720); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "usageAnalysis_ == other->usageAnalysis_"
")"); do { MOZ_CrashSequence(__null, 2720); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2721 }
2722}
2723
2724/* static */
2725bool MPhi::markIteratorPhis(const PhiVector& iterators) {
2726 // Find and mark phis that must transitively hold an iterator live.
2727
2728 Vector<MPhi*, 8, SystemAllocPolicy> worklist;
2729
2730 for (MPhi* iter : iterators) {
2731 if (!iter->isInWorklist()) {
2732 if (!worklist.append(iter)) {
2733 return false;
2734 }
2735 iter->setInWorklist();
2736 }
2737 }
2738
2739 while (!worklist.empty()) {
2740 MPhi* phi = worklist.popCopy();
2741 phi->setNotInWorklist();
2742
2743 phi->setIterator();
2744 phi->setImplicitlyUsedUnchecked();
2745
2746 for (MUseDefIterator iter(phi); iter; iter++) {
2747 MDefinition* use = iter.def();
2748 if (!use->isInWorklist() && use->isPhi() && !use->toPhi()->isIterator()) {
2749 if (!worklist.append(use->toPhi())) {
2750 return false;
2751 }
2752 use->setInWorklist();
2753 }
2754 }
2755 }
2756
2757 return true;
2758}
2759
2760void MCallBase::addArg(size_t argnum, MDefinition* arg) {
2761 // The operand vector is initialized in reverse order by WarpBuilder.
2762 // It cannot be checked for consistency until all arguments are added.
2763 // FixedList doesn't initialize its elements, so do an unchecked init.
2764 initOperand(argnum + NumNonArgumentOperands, arg);
2765}
2766
2767static inline bool IsConstant(MDefinition* def, double v) {
2768 if (!def->isConstant()) {
2769 return false;
2770 }
2771
2772 return NumbersAreIdentical(def->toConstant()->numberToDouble(), v);
2773}
2774
2775static inline bool IsConstantInt64(MDefinition* def, int64_t v) {
2776 if (!def->isConstant()) {
2777 return false;
2778 }
2779
2780 return def->toConstant()->toInt64() == v;
2781}
2782
2783static inline bool IsConstantIntPtr(MDefinition* def, intptr_t v) {
2784 if (!def->isConstant()) {
2785 return false;
2786 }
2787
2788 return def->toConstant()->toIntPtr() == v;
2789}
2790
2791MDefinition* MBinaryBitwiseInstruction::foldsTo(TempAllocator& alloc) {
2792 // Identity operations are removed (for int32 only) in foldUnnecessaryBitop.
2793
2794 if (type() == MIRType::Int32) {
2795 if (MDefinition* folded = EvaluateInt32ConstantOperands(alloc, this)) {
2796 return folded;
2797 }
2798 } else if (type() == MIRType::Int64) {
2799 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) {
2800 return folded;
2801 }
2802 } else if (type() == MIRType::IntPtr) {
2803 if (MDefinition* folded = EvaluateIntPtrConstantOperands(alloc, this)) {
2804 return folded;
2805 }
2806 }
2807
2808 return this;
2809}
2810
2811MDefinition* MBinaryBitwiseInstruction::foldUnnecessaryBitop() {
2812 // It's probably OK to perform this optimization only for int32, as JS
2813 // bytecode does not see int64 values.
2814
2815 if (type() != MIRType::Int32) {
2816 return this;
2817 }
2818
2819 // Fold unsigned shift right operator when the second operand is zero and
2820 // the only use is an unsigned modulo. Thus, the expression
2821 // |(x >>> 0) % y| becomes |x % y|.
2822 if (isUrsh() && IsUint32Type(this)) {
2823 MDefinition* defUse = maybeSingleDefUse();
2824 if (defUse && defUse->isMod() && defUse->toMod()->isUnsigned()) {
2825 return getOperand(0);
2826 }
2827 }
2828
2829 // Eliminate bitwise operations that are no-ops when used on integer
2830 // inputs, such as (x | 0).
2831
2832 MDefinition* lhs = getOperand(0);
2833 MDefinition* rhs = getOperand(1);
2834
2835 if (IsConstant(lhs, 0)) {
2836 return foldIfZero(0);
2837 }
2838
2839 if (IsConstant(rhs, 0)) {
2840 return foldIfZero(1);
2841 }
2842
2843 if (IsConstant(lhs, -1)) {
2844 return foldIfNegOne(0);
2845 }
2846
2847 if (IsConstant(rhs, -1)) {
2848 return foldIfNegOne(1);
2849 }
2850
2851 if (lhs == rhs) {
2852 return foldIfEqual();
2853 }
2854
2855 if (maskMatchesRightRange) {
2856 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", 2856); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lhs->isConstant()" ")"); do { MOZ_CrashSequence
(__null, 2856); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2857 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"
, 2857); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 2857); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2858 return foldIfAllBitsSet(0);
2859 }
2860
2861 if (maskMatchesLeftRange) {
2862 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", 2862); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "rhs->isConstant()" ")"); do { MOZ_CrashSequence
(__null, 2862); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2863 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"
, 2863); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rhs->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 2863); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2864 return foldIfAllBitsSet(1);
2865 }
2866
2867 return this;
2868}
2869
2870static inline bool CanProduceNegativeZero(MDefinition* def) {
2871 // Test if this instruction can produce negative zero even when bailing out
2872 // and changing types.
2873 switch (def->op()) {
2874 case MDefinition::Opcode::Constant:
2875 if (def->type() == MIRType::Double &&
2876 def->toConstant()->toDouble() == -0.0) {
2877 return true;
2878 }
2879 [[fallthrough]];
2880 case MDefinition::Opcode::BitAnd:
2881 case MDefinition::Opcode::BitOr:
2882 case MDefinition::Opcode::BitXor:
2883 case MDefinition::Opcode::BitNot:
2884 case MDefinition::Opcode::Lsh:
2885 case MDefinition::Opcode::Rsh:
2886 return false;
2887 default:
2888 return true;
2889 }
2890}
2891
2892static inline bool NeedNegativeZeroCheck(MDefinition* def) {
2893 if (def->isGuard() || def->isGuardRangeBailouts()) {
2894 return true;
2895 }
2896
2897 // Test if all uses have the same semantics for -0 and 0
2898 for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) {
2899 if (use->consumer()->isResumePoint()) {
2900 return true;
2901 }
2902
2903 MDefinition* use_def = use->consumer()->toDefinition();
2904 switch (use_def->op()) {
2905 case MDefinition::Opcode::Add: {
2906 // If add is truncating -0 and 0 are observed as the same.
2907 if (use_def->toAdd()->isTruncated()) {
2908 break;
2909 }
2910
2911 // x + y gives -0, when both x and y are -0
2912
2913 // Figure out the order in which the addition's operands will
2914 // execute. EdgeCaseAnalysis::analyzeLate has renumbered the MIR
2915 // definitions for us so that this just requires comparing ids.
2916 MDefinition* first = use_def->toAdd()->lhs();
2917 MDefinition* second = use_def->toAdd()->rhs();
2918 if (first->id() > second->id()) {
2919 std::swap(first, second);
2920 }
2921 // Negative zero checks can be removed on the first executed
2922 // operand only if it is guaranteed the second executed operand
2923 // will produce a value other than -0. While the second is
2924 // typed as an int32, a bailout taken between execution of the
2925 // operands may change that type and cause a -0 to flow to the
2926 // second.
2927 //
2928 // There is no way to test whether there are any bailouts
2929 // between execution of the operands, so remove negative
2930 // zero checks from the first only if the second's type is
2931 // independent from type changes that may occur after bailing.
2932 if (def == first && CanProduceNegativeZero(second)) {
2933 return true;
2934 }
2935
2936 // The negative zero check can always be removed on the second
2937 // executed operand; by the time this executes the first will have
2938 // been evaluated as int32 and the addition's result cannot be -0.
2939 break;
2940 }
2941 case MDefinition::Opcode::Sub: {
2942 // If sub is truncating -0 and 0 are observed as the same
2943 if (use_def->toSub()->isTruncated()) {
2944 break;
2945 }
2946
2947 // x + y gives -0, when x is -0 and y is 0
2948
2949 // We can remove the negative zero check on the rhs, only if we
2950 // are sure the lhs isn't negative zero.
2951
2952 // The lhs is typed as integer (i.e. not -0.0), but it can bailout
2953 // and change type. This should be fine if the lhs is executed
2954 // first. However if the rhs is executed first, the lhs can bail,
2955 // change type and become -0.0 while the rhs has already been
2956 // optimized to not make a difference between zero and negative zero.
2957 MDefinition* lhs = use_def->toSub()->lhs();
2958 MDefinition* rhs = use_def->toSub()->rhs();
2959 if (rhs->id() < lhs->id() && CanProduceNegativeZero(lhs)) {
2960 return true;
2961 }
2962
2963 [[fallthrough]];
2964 }
2965 case MDefinition::Opcode::StoreElement:
2966 case MDefinition::Opcode::StoreHoleValueElement:
2967 case MDefinition::Opcode::LoadElement:
2968 case MDefinition::Opcode::LoadElementHole:
2969 case MDefinition::Opcode::LoadUnboxedScalar:
2970 case MDefinition::Opcode::LoadDataViewElement:
2971 case MDefinition::Opcode::LoadTypedArrayElementHole:
2972 case MDefinition::Opcode::CharCodeAt:
2973 case MDefinition::Opcode::Mod:
2974 case MDefinition::Opcode::InArray:
2975 // Only allowed to remove check when definition is the second operand
2976 if (use_def->getOperand(0) == def) {
2977 return true;
2978 }
2979 for (size_t i = 2, e = use_def->numOperands(); i < e; i++) {
2980 if (use_def->getOperand(i) == def) {
2981 return true;
2982 }
2983 }
2984 break;
2985 case MDefinition::Opcode::BoundsCheck:
2986 // Only allowed to remove check when definition is the first operand
2987 if (use_def->toBoundsCheck()->getOperand(1) == def) {
2988 return true;
2989 }
2990 break;
2991 case MDefinition::Opcode::ToString:
2992 case MDefinition::Opcode::FromCharCode:
2993 case MDefinition::Opcode::FromCodePoint:
2994 case MDefinition::Opcode::TableSwitch:
2995 case MDefinition::Opcode::Compare:
2996 case MDefinition::Opcode::BitAnd:
2997 case MDefinition::Opcode::BitOr:
2998 case MDefinition::Opcode::BitXor:
2999 case MDefinition::Opcode::Abs:
3000 case MDefinition::Opcode::TruncateToInt32:
3001 // Always allowed to remove check. No matter which operand.
3002 break;
3003 case MDefinition::Opcode::StoreElementHole:
3004 case MDefinition::Opcode::StoreTypedArrayElementHole:
3005 case MDefinition::Opcode::PostWriteElementBarrier:
3006 // Only allowed to remove check when definition is the third operand.
3007 for (size_t i = 0, e = use_def->numOperands(); i < e; i++) {
3008 if (i == 2) {
3009 continue;
3010 }
3011 if (use_def->getOperand(i) == def) {
3012 return true;
3013 }
3014 }
3015 break;
3016 default:
3017 return true;
3018 }
3019 }
3020 return false;
3021}
3022
3023#ifdef JS_JITSPEW1
3024void MBinaryArithInstruction::printOpcode(GenericPrinter& out) const {
3025 MDefinition::printOpcode(out);
3026
3027 switch (type()) {
3028 case MIRType::Int32:
3029 if (isDiv()) {
3030 out.printf(" [%s]", toDiv()->isUnsigned() ? "uint32" : "int32");
3031 } else if (isMod()) {
3032 out.printf(" [%s]", toMod()->isUnsigned() ? "uint32" : "int32");
3033 } else {
3034 out.printf(" [int32]");
3035 }
3036 break;
3037 case MIRType::Int64:
3038 if (isDiv()) {
3039 out.printf(" [%s]", toDiv()->isUnsigned() ? "uint64" : "int64");
3040 } else if (isMod()) {
3041 out.printf(" [%s]", toMod()->isUnsigned() ? "uint64" : "int64");
3042 } else {
3043 out.printf(" [int64]");
3044 }
3045 break;
3046 case MIRType::Float32:
3047 out.printf(" [float]");
3048 break;
3049 case MIRType::Double:
3050 out.printf(" [double]");
3051 break;
3052 default:
3053 break;
3054 }
3055}
3056#endif
3057
3058MDefinition* MRsh::foldsTo(TempAllocator& alloc) {
3059 MDefinition* f = MBinaryBitwiseInstruction::foldsTo(alloc);
3060
3061 if (f != this) {
3062 return f;
3063 }
3064
3065 MDefinition* lhs = getOperand(0);
3066 MDefinition* rhs = getOperand(1);
3067
3068 // It's probably OK to perform this optimization only for int32, as JS
3069 // bytecode does not see int64 values.
3070
3071 if (!lhs->isLsh() || !rhs->isConstant() || rhs->type() != MIRType::Int32) {
3072 return this;
3073 }
3074
3075 if (!lhs->getOperand(1)->isConstant() ||
3076 lhs->getOperand(1)->type() != MIRType::Int32) {
3077 return this;
3078 }
3079
3080 uint32_t shift = rhs->toConstant()->toInt32();
3081 uint32_t shift_lhs = lhs->getOperand(1)->toConstant()->toInt32();
3082 if (shift != shift_lhs) {
3083 return this;
3084 }
3085
3086 switch (shift) {
3087 case 16:
3088 return MSignExtendInt32::New(alloc, lhs->getOperand(0),
3089 MSignExtendInt32::Half);
3090 case 24:
3091 return MSignExtendInt32::New(alloc, lhs->getOperand(0),
3092 MSignExtendInt32::Byte);
3093 }
3094
3095 return this;
3096}
3097
3098MDefinition* MBinaryArithInstruction::foldsTo(TempAllocator& alloc) {
3099 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", 3099); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsNumberType(type())" ")"); do { MOZ_CrashSequence
(__null, 3099); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3100 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", 3100); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isDiv() && !isMod()" ") (" "Div and Mod don't call this method"
")"); do { MOZ_CrashSequence(__null, 3100); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3101
3102 MDefinition* lhs = getOperand(0);
3103 MDefinition* rhs = getOperand(1);
3104
3105 if (type() == MIRType::Int64) {
3106 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", 3106); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isTruncated()" ")"); do { MOZ_CrashSequence
(__null, 3106); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3107
3108 if (MConstant* folded = EvaluateInt64ConstantOperands(alloc, this)) {
3109 return folded;
3110 }
3111 if (IsConstantInt64(rhs, int64_t(getIdentity()))) {
3112 return lhs; // x op id => x
3113 }
3114 if (isCommutative() && IsConstantInt64(lhs, int64_t(getIdentity()))) {
3115 return rhs; // id op x => x
3116 }
3117 return this;
3118 }
3119
3120 if (type() == MIRType::IntPtr) {
3121 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", 3121); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isTruncated()" ")"); do { MOZ_CrashSequence
(__null, 3121); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3122
3123 if (MConstant* folded = EvaluateIntPtrConstantOperands(alloc, this)) {
3124 return folded;
3125 }
3126 if (IsConstantIntPtr(rhs, intptr_t(getIdentity()))) {
3127 return lhs; // x op id => x
3128 }
3129 if (isCommutative() && IsConstantIntPtr(lhs, intptr_t(getIdentity()))) {
3130 return rhs; // id op x => x
3131 }
3132 return this;
3133 }
3134
3135 // The remaining operations expect types representable as doubles.
3136 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"
, 3136); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsTypeRepresentableAsDouble(type())"
")"); do { MOZ_CrashSequence(__null, 3136); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3137
3138 if (MConstant* folded = EvaluateConstantOperands(alloc, this)) {
3139 if (isTruncated()) {
3140 if (folded->type() != MIRType::Int32) {
3141 if (!folded->block()) {
3142 block()->insertBefore(this, folded);
3143 }
3144 return MTruncateToInt32::New(alloc, folded);
3145 }
3146 }
3147 return folded;
3148 }
3149
3150 if (MConstant* folded = EvaluateConstantNaNOperand(this)) {
3151 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", 3151); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isTruncated()" ")"); do { MOZ_CrashSequence
(__null, 3151); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3152 return folded;
3153 }
3154
3155 if (mustPreserveNaN_) {
3156 return this;
3157 }
3158
3159 // 0 + -0 = 0. So we can't remove addition
3160 if (isAdd() && type() != MIRType::Int32) {
3161 return this;
3162 }
3163
3164 if (IsConstant(rhs, getIdentity())) {
3165 if (isTruncated()) {
3166 return MTruncateToInt32::New(alloc, lhs);
3167 }
3168 return lhs;
3169 }
3170
3171 // subtraction isn't commutative. So we can't remove subtraction when lhs
3172 // equals 0
3173 if (isSub()) {
3174 return this;
3175 }
3176
3177 if (IsConstant(lhs, getIdentity())) {
3178 if (isTruncated()) {
3179 return MTruncateToInt32::New(alloc, rhs);
3180 }
3181 return rhs; // id op x => x
3182 }
3183
3184 return this;
3185}
3186
3187void MBinaryArithInstruction::trySpecializeFloat32(TempAllocator& alloc) {
3188 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", 3188); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsNumberType(type())" ")"); do { MOZ_CrashSequence
(__null, 3188); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3189
3190 // Do not use Float32 if we can use integer types.
3191 if (!IsFloatingPointType(type())) {
3192 return;
3193 }
3194
3195 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
3196 setResultType(MIRType::Float32);
3197 }
3198}
3199
3200void MMinMax::trySpecializeFloat32(TempAllocator& alloc) {
3201 if (!IsFloatingPointType(type())) {
3202 return;
3203 }
3204
3205 MDefinition* left = lhs();
3206 MDefinition* right = rhs();
3207
3208 if ((left->canProduceFloat32() ||
3209 (left->isMinMax() && left->type() == MIRType::Float32)) &&
3210 (right->canProduceFloat32() ||
3211 (right->isMinMax() && right->type() == MIRType::Float32))) {
3212 setResultType(MIRType::Float32);
3213 } else {
3214 ConvertOperandsToDouble(this, alloc);
3215 }
3216}
3217
3218template <MIRType Type>
3219static MConstant* EvaluateMinMaxInt(TempAllocator& alloc, MConstant* lhs,
3220 MConstant* rhs, bool isMax) {
3221 auto lnum = ToIntConstant<Type>(lhs);
3222 auto rnum = ToIntConstant<Type>(rhs);
3223 auto result = isMax ? std::max(lnum, rnum) : std::min(lnum, rnum);
3224 return NewIntConstant<Type>(alloc, result);
3225}
3226
3227static MConstant* EvaluateMinMax(TempAllocator& alloc, MConstant* lhs,
3228 MConstant* rhs, bool isMax) {
3229 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"
, 3229); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == rhs->type()"
")"); do { MOZ_CrashSequence(__null, 3229); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3230 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"
, 3230); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsNumberType(lhs->type())"
")"); do { MOZ_CrashSequence(__null, 3230); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3231
3232 // The folded MConstant should maintain the same MIRType with the original
3233 // inputs.
3234 switch (lhs->type()) {
3235 case MIRType::Int32:
3236 return EvaluateMinMaxInt<MIRType::Int32>(alloc, lhs, rhs, isMax);
3237 case MIRType::Int64:
3238 return EvaluateMinMaxInt<MIRType::Int64>(alloc, lhs, rhs, isMax);
3239 case MIRType::IntPtr:
3240 return EvaluateMinMaxInt<MIRType::IntPtr>(alloc, lhs, rhs, isMax);
3241 case MIRType::Float32:
3242 case MIRType::Double: {
3243 double lnum = lhs->numberToDouble();
3244 double rnum = rhs->numberToDouble();
3245
3246 double result;
3247 if (isMax) {
3248 result = js::math_max_impl(lnum, rnum);
3249 } else {
3250 result = js::math_min_impl(lnum, rnum);
3251 }
3252
3253 if (lhs->type() == MIRType::Float32) {
3254 return MConstant::NewFloat32(alloc, result);
3255 }
3256 return MConstant::NewDouble(alloc, result);
3257 }
3258 default:
3259 MOZ_CRASH("not a number type")do { do { } while (false); MOZ_ReportCrash("" "not a number type"
, "./../../../../js/src/jit/MIR.cpp", 3259); AnnotateMozCrashReason
("MOZ_CRASH(" "not a number type" ")"); do { MOZ_CrashSequence
(__null, 3259); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
3260 }
3261}
3262
3263MDefinition* MMinMax::foldsTo(TempAllocator& alloc) {
3264 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", 3264); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lhs()->type() == type()" ")"); do { MOZ_CrashSequence
(__null, 3264); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3265 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", 3265); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "rhs()->type() == type()" ")"); do { MOZ_CrashSequence
(__null, 3265); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3266
3267 if (lhs() == rhs()) {
3268 return lhs();
3269 }
3270
3271 auto foldConstants = [&alloc](MDefinition* lhs, MDefinition* rhs,
3272 bool isMax) -> MConstant* {
3273 return EvaluateMinMax(alloc, lhs->toConstant(), rhs->toConstant(), isMax);
3274 };
3275
3276 auto foldLength = [](MDefinition* operand, MConstant* constant,
3277 bool isMax) -> MDefinition* {
3278 if (operand->isArrayLength() || operand->isArrayBufferViewLength() ||
3279 operand->isArgumentsLength() || operand->isStringLength() ||
3280 operand->isNonNegativeIntPtrToInt32()) {
3281 bool isZeroOrNegative;
3282 switch (constant->type()) {
3283 case MIRType::Int32:
3284 isZeroOrNegative = constant->toInt32() <= 0;
3285 break;
3286 case MIRType::IntPtr:
3287 isZeroOrNegative = constant->toIntPtr() <= 0;
3288 break;
3289 default:
3290 isZeroOrNegative = false;
3291 break;
3292 }
3293
3294 // (Array|ArrayBufferView|Arguments|String)Length is always >= 0.
3295 // max(array.length, cte <= 0) = array.length
3296 // min(array.length, cte <= 0) = cte
3297 if (isZeroOrNegative) {
3298 return isMax ? operand : constant;
3299 }
3300 }
3301 return nullptr;
3302 };
3303
3304 // Try to fold the following patterns when |x| and |y| are constants.
3305 //
3306 // min(min(x, z), min(y, z)) = min(min(x, y), z)
3307 // max(max(x, z), max(y, z)) = max(max(x, y), z)
3308 // max(min(x, z), min(y, z)) = min(max(x, y), z)
3309 // min(max(x, z), max(y, z)) = max(min(x, y), z)
3310 if (lhs()->isMinMax() && rhs()->isMinMax()) {
3311 do {
3312 auto* left = lhs()->toMinMax();
3313 auto* right = rhs()->toMinMax();
3314 if (left->isMax() != right->isMax()) {
3315 break;
3316 }
3317
3318 MDefinition* x;
3319 MDefinition* y;
3320 MDefinition* z;
3321 if (left->lhs() == right->lhs()) {
3322 std::tie(x, y, z) = std::tuple{left->rhs(), right->rhs(), left->lhs()};
3323 } else if (left->lhs() == right->rhs()) {
3324 std::tie(x, y, z) = std::tuple{left->rhs(), right->lhs(), left->lhs()};
3325 } else if (left->rhs() == right->lhs()) {
3326 std::tie(x, y, z) = std::tuple{left->lhs(), right->rhs(), left->rhs()};
3327 } else if (left->rhs() == right->rhs()) {
3328 std::tie(x, y, z) = std::tuple{left->lhs(), right->lhs(), left->rhs()};
3329 } else {
3330 break;
3331 }
3332
3333 if (!x->isConstant() || !y->isConstant()) {
3334 break;
3335 }
3336
3337 if (auto* foldedCst = foldConstants(x, y, isMax())) {
3338 if (auto* folded = foldLength(z, foldedCst, left->isMax())) {
3339 return folded;
3340 }
3341 block()->insertBefore(this, foldedCst);
3342 return MMinMax::New(alloc, foldedCst, z, type(), left->isMax());
3343 }
3344 } while (false);
3345 }
3346
3347 // Fold min/max operations with same inputs.
3348 if (lhs()->isMinMax() || rhs()->isMinMax()) {
3349 auto* other = lhs()->isMinMax() ? lhs()->toMinMax() : rhs()->toMinMax();
3350 auto* operand = lhs()->isMinMax() ? rhs() : lhs();
3351
3352 if (operand == other->lhs() || operand == other->rhs()) {
3353 if (isMax() == other->isMax()) {
3354 // min(x, min(x, y)) = min(x, y)
3355 // max(x, max(x, y)) = max(x, y)
3356 return other;
3357 }
3358 if (!IsFloatingPointType(type())) {
3359 // When neither value is NaN:
3360 // max(x, min(x, y)) = x
3361 // min(x, max(x, y)) = x
3362
3363 // Ensure that any bailouts that we depend on to guarantee that |y| is
3364 // Int32 are not removed.
3365 auto* otherOp = operand == other->lhs() ? other->rhs() : other->lhs();
3366 otherOp->setGuardRangeBailoutsUnchecked();
3367
3368 return operand;
3369 }
3370 }
3371 }
3372
3373 if (!lhs()->isConstant() && !rhs()->isConstant()) {
3374 return this;
3375 }
3376
3377 // Directly apply math utility to compare the rhs() and lhs() when
3378 // they are both constants.
3379 if (lhs()->isConstant() && rhs()->isConstant()) {
3380 if (auto* folded = foldConstants(lhs(), rhs(), isMax())) {
3381 return folded;
3382 }
3383 }
3384
3385 MDefinition* operand = lhs()->isConstant() ? rhs() : lhs();
3386 MConstant* constant =
3387 lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant();
3388
3389 if (operand->isToDouble() &&
3390 operand->getOperand(0)->type() == MIRType::Int32) {
3391 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", 3391); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "constant->type() == MIRType::Double" ")"
); do { MOZ_CrashSequence(__null, 3391); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3392
3393 // min(int32, cte >= INT32_MAX) = int32
3394 if (!isMax() && constant->toDouble() >= INT32_MAX(2147483647)) {
3395 MLimitedTruncate* limit = MLimitedTruncate::New(
3396 alloc, operand->getOperand(0), TruncateKind::NoTruncate);
3397 block()->insertBefore(this, limit);
3398 MToDouble* toDouble = MToDouble::New(alloc, limit);
3399 return toDouble;
3400 }
3401
3402 // max(int32, cte <= INT32_MIN) = int32
3403 if (isMax() && constant->toDouble() <= INT32_MIN(-2147483647-1)) {
3404 MLimitedTruncate* limit = MLimitedTruncate::New(
3405 alloc, operand->getOperand(0), TruncateKind::NoTruncate);
3406 block()->insertBefore(this, limit);
3407 MToDouble* toDouble = MToDouble::New(alloc, limit);
3408 return toDouble;
3409 }
3410 }
3411
3412 if (auto* folded = foldLength(operand, constant, isMax())) {
3413 return folded;
3414 }
3415
3416 // Attempt to fold nested min/max operations which are produced by
3417 // self-hosted built-in functions.
3418 if (operand->isMinMax()) {
3419 auto* other = operand->toMinMax();
3420 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"
, 3420); AnnotateMozCrashReason("MOZ_ASSERT" "(" "other->lhs()->type() == type()"
")"); do { MOZ_CrashSequence(__null, 3420); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3421 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"
, 3421); AnnotateMozCrashReason("MOZ_ASSERT" "(" "other->rhs()->type() == type()"
")"); do { MOZ_CrashSequence(__null, 3421); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3422
3423 MConstant* otherConstant = nullptr;
3424 MDefinition* otherOperand = nullptr;
3425 if (other->lhs()->isConstant()) {
3426 otherConstant = other->lhs()->toConstant();
3427 otherOperand = other->rhs();
3428 } else if (other->rhs()->isConstant()) {
3429 otherConstant = other->rhs()->toConstant();
3430 otherOperand = other->lhs();
3431 }
3432
3433 if (otherConstant) {
3434 if (isMax() == other->isMax()) {
3435 // Fold min(x, min(y, z)) to min(min(x, y), z) with constant min(x, y).
3436 // Fold max(x, max(y, z)) to max(max(x, y), z) with constant max(x, y).
3437 if (auto* left = foldConstants(constant, otherConstant, isMax())) {
3438 if (auto* folded = foldLength(otherOperand, left, isMax())) {
3439 return folded;
3440 }
3441 block()->insertBefore(this, left);
3442 return MMinMax::New(alloc, left, otherOperand, type(), isMax());
3443 }
3444 } else {
3445 // Fold min(x, max(y, z)) to max(min(x, y), min(x, z)).
3446 // Fold max(x, min(y, z)) to min(max(x, y), max(x, z)).
3447 //
3448 // But only do this when min(x, z) can also be simplified.
3449 if (auto* right = foldLength(otherOperand, constant, isMax())) {
3450 if (auto* left = foldConstants(constant, otherConstant, isMax())) {
3451 block()->insertBefore(this, left);
3452 return MMinMax::New(alloc, left, right, type(), !isMax());
3453 }
3454 }
3455 }
3456 }
3457 }
3458
3459 return this;
3460}
3461
3462#ifdef JS_JITSPEW1
3463void MMinMax::printOpcode(GenericPrinter& out) const {
3464 MDefinition::printOpcode(out);
3465 out.printf(" (%s)", isMax() ? "max" : "min");
3466}
3467
3468void MMinMaxArray::printOpcode(GenericPrinter& out) const {
3469 MDefinition::printOpcode(out);
3470 out.printf(" (%s)", isMax() ? "max" : "min");
3471}
3472#endif
3473
3474MDefinition* MPow::foldsConstant(TempAllocator& alloc) {
3475 // Both `x` and `p` in `x^p` must be constants in order to precompute.
3476 if (!input()->isConstant() || !power()->isConstant()) {
3477 return nullptr;
3478 }
3479 if (!power()->toConstant()->isTypeRepresentableAsDouble()) {
3480 return nullptr;
3481 }
3482 if (!input()->toConstant()->isTypeRepresentableAsDouble()) {
3483 return nullptr;
3484 }
3485
3486 double x = input()->toConstant()->numberToDouble();
3487 double p = power()->toConstant()->numberToDouble();
3488 double result = js::ecmaPow(x, p);
3489 if (type() == MIRType::Int32) {
3490 int32_t cast;
3491 if (!mozilla::NumberIsInt32(result, &cast)) {
3492 // Reject folding if the result isn't an int32, because we'll bail anyway.
3493 return nullptr;
3494 }
3495 return MConstant::NewInt32(alloc, cast);
3496 }
3497 return MConstant::NewDouble(alloc, result);
3498}
3499
3500MDefinition* MPow::foldsConstantPower(TempAllocator& alloc) {
3501 // If `p` in `x^p` isn't constant, we can't apply these folds.
3502 if (!power()->isConstant()) {
3503 return nullptr;
3504 }
3505 if (!power()->toConstant()->isTypeRepresentableAsDouble()) {
3506 return nullptr;
3507 }
3508
3509 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"
, 3509); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type() == MIRType::Double || type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 3509); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3510
3511 // NOTE: The optimizations must match the optimizations used in |js::ecmaPow|
3512 // resp. |js::powi| to avoid differential testing issues.
3513
3514 double pow = power()->toConstant()->numberToDouble();
3515
3516 // Math.pow(x, 0.5) is a sqrt with edge-case detection.
3517 if (pow == 0.5) {
3518 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", 3518); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Double" ")"); do { MOZ_CrashSequence
(__null, 3518); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3519 return MPowHalf::New(alloc, input());
3520 }
3521
3522 // Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5), even for edge cases.
3523 if (pow == -0.5) {
3524 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", 3524); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Double" ")"); do { MOZ_CrashSequence
(__null, 3524); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3525 MPowHalf* half = MPowHalf::New(alloc, input());
3526 block()->insertBefore(this, half);
3527 MConstant* one = MConstant::NewDouble(alloc, 1.0);
3528 block()->insertBefore(this, one);
3529 return MDiv::New(alloc, one, half, MIRType::Double);
3530 }
3531
3532 // Math.pow(x, 1) == x.
3533 if (pow == 1.0) {
3534 return input();
3535 }
3536
3537 auto multiply = [this, &alloc](MDefinition* lhs, MDefinition* rhs) {
3538 MMul* mul = MMul::New(alloc, lhs, rhs, type());
3539 mul->setBailoutKind(bailoutKind());
3540
3541 // Multiplying the same number can't yield negative zero.
3542 mul->setCanBeNegativeZero(lhs != rhs && canBeNegativeZero());
3543 return mul;
3544 };
3545
3546 // Math.pow(x, 2) == x*x.
3547 if (pow == 2.0) {
3548 return multiply(input(), input());
3549 }
3550
3551 // Math.pow(x, 3) == x*x*x.
3552 if (pow == 3.0) {
3553 MMul* mul1 = multiply(input(), input());
3554 block()->insertBefore(this, mul1);
3555 return multiply(input(), mul1);
3556 }
3557
3558 // Math.pow(x, 4) == y*y, where y = x*x.
3559 if (pow == 4.0) {
3560 MMul* y = multiply(input(), input());
3561 block()->insertBefore(this, y);
3562 return multiply(y, y);
3563 }
3564
3565 // Math.pow(x, NaN) == NaN.
3566 if (std::isnan(pow)) {
3567 return power();
3568 }
3569
3570 // No optimization
3571 return nullptr;
3572}
3573
3574MDefinition* MPow::foldsTo(TempAllocator& alloc) {
3575 if (MDefinition* def = foldsConstant(alloc)) {
3576 return def;
3577 }
3578 if (MDefinition* def = foldsConstantPower(alloc)) {
3579 return def;
3580 }
3581 return this;
3582}
3583
3584MDefinition* MBigIntPow::foldsTo(TempAllocator& alloc) {
3585 auto* base = lhs();
3586 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"
, 3586); AnnotateMozCrashReason("MOZ_ASSERT" "(" "base->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 3586); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3587
3588 auto* power = rhs();
3589 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"
, 3589); AnnotateMozCrashReason("MOZ_ASSERT" "(" "power->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 3589); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3590
3591 // |power| must be a constant.
3592 if (!power->isConstant()) {
3593 return this;
3594 }
3595
3596 int32_t pow;
3597 if (BigInt::isInt32(power->toConstant()->toBigInt(), &pow)) {
3598 // x ** 1n == x.
3599 if (pow == 1) {
3600 return base;
3601 }
3602
3603 // x ** 2n == x*x.
3604 if (pow == 2) {
3605 auto* mul = MBigIntMul::New(alloc, base, base);
3606 mul->setBailoutKind(bailoutKind());
3607 return mul;
3608 }
3609 }
3610
3611 // No optimization
3612 return this;
3613}
3614
3615MDefinition* MBigIntAsIntN::foldsTo(TempAllocator& alloc) {
3616 auto* bitsDef = bits();
3617 if (!bitsDef->isConstant()) {
3618 return this;
3619 }
3620
3621 // Negative |bits| throw an error and too large |bits| don't fit into Int64.
3622 int32_t bitsInt = bitsDef->toConstant()->toInt32();
3623 if (bitsInt < 0 || bitsInt > 64) {
3624 return this;
3625 }
3626
3627 // Prefer sign-extension if possible.
3628 bool canSignExtend = false;
3629 switch (bitsInt) {
3630 case 8:
3631 case 16:
3632 case 32:
3633 case 64:
3634 canSignExtend = true;
3635 break;
3636 }
3637
3638 // Ensure the input is either IntPtr or Int64 typed.
3639 auto* inputDef = input();
3640 if (inputDef->isIntPtrToBigInt()) {
3641 inputDef = inputDef->toIntPtrToBigInt()->input();
3642
3643 if (!canSignExtend) {
3644 auto* int64 = MIntPtrToInt64::New(alloc, inputDef);
3645 block()->insertBefore(this, int64);
3646 inputDef = int64;
3647 }
3648 } else if (inputDef->isInt64ToBigInt()) {
3649 inputDef = inputDef->toInt64ToBigInt()->input();
3650 } else {
3651 auto* truncate = MTruncateBigIntToInt64::New(alloc, inputDef);
3652 block()->insertBefore(this, truncate);
3653 inputDef = truncate;
3654 }
3655
3656 if (inputDef->type() == MIRType::IntPtr) {
3657 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", 3657); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "canSignExtend" ")"); do { MOZ_CrashSequence
(__null, 3657); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3658
3659 // If |bits| is larger-or-equal to |BigInt::DigitBits|, return the input.
3660 if (size_t(bitsInt) >= BigInt::DigitBits) {
3661 auto* limited = MIntPtrLimitedTruncate::New(alloc, inputDef);
3662 block()->insertBefore(this, limited);
3663 inputDef = limited;
3664 } else {
3665 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", 3665); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "bitsInt < 64" ")"); do { MOZ_CrashSequence
(__null, 3665); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3666
3667 // Otherwise extension is the way to go.
3668 MSignExtendIntPtr::Mode mode;
3669 switch (bitsInt) {
3670 case 8:
3671 mode = MSignExtendIntPtr::Byte;
3672 break;
3673 case 16:
3674 mode = MSignExtendIntPtr::Half;
3675 break;
3676 case 32:
3677 mode = MSignExtendIntPtr::Word;
3678 break;
3679 }
3680
3681 auto* extend = MSignExtendIntPtr::New(alloc, inputDef, mode);
3682 block()->insertBefore(this, extend);
3683 inputDef = extend;
3684 }
3685
3686 return MIntPtrToBigInt::New(alloc, inputDef);
3687 }
3688 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", 3688); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "inputDef->type() == MIRType::Int64" ")"
); do { MOZ_CrashSequence(__null, 3688); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3689
3690 if (canSignExtend) {
3691 // If |bits| is equal to 64, return the input.
3692 if (bitsInt == 64) {
3693 auto* limited = MInt64LimitedTruncate::New(alloc, inputDef);
3694 block()->insertBefore(this, limited);
3695 inputDef = limited;
3696 } else {
3697 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", 3697); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "bitsInt < 64" ")"); do { MOZ_CrashSequence
(__null, 3697); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3698
3699 // Otherwise extension is the way to go.
3700 MSignExtendInt64::Mode mode;
3701 switch (bitsInt) {
3702 case 8:
3703 mode = MSignExtendInt64::Byte;
3704 break;
3705 case 16:
3706 mode = MSignExtendInt64::Half;
3707 break;
3708 case 32:
3709 mode = MSignExtendInt64::Word;
3710 break;
3711 }
3712
3713 auto* extend = MSignExtendInt64::New(alloc, inputDef, mode);
3714 block()->insertBefore(this, extend);
3715 inputDef = extend;
3716 }
3717 } else {
3718 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", 3718); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "bitsInt < 64" ")"); do { MOZ_CrashSequence
(__null, 3718); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3719
3720 uint64_t mask = 0;
3721 if (bitsInt > 0) {
3722 mask = uint64_t(-1) >> (64 - bitsInt);
3723 }
3724
3725 auto* cst = MConstant::NewInt64(alloc, int64_t(mask));
3726 block()->insertBefore(this, cst);
3727
3728 // Mask off any excess bits.
3729 auto* bitAnd = MBitAnd::New(alloc, inputDef, cst, MIRType::Int64);
3730 block()->insertBefore(this, bitAnd);
3731
3732 auto* shift = MConstant::NewInt64(alloc, int64_t(64 - bitsInt));
3733 block()->insertBefore(this, shift);
3734
3735 // Left-shift to make the sign-bit the left-most bit.
3736 auto* lsh = MLsh::New(alloc, bitAnd, shift, MIRType::Int64);
3737 block()->insertBefore(this, lsh);
3738
3739 // Right-shift to propagate the sign-bit.
3740 auto* rsh = MRsh::New(alloc, lsh, shift, MIRType::Int64);
3741 block()->insertBefore(this, rsh);
3742
3743 inputDef = rsh;
3744 }
3745
3746 return MInt64ToBigInt::New(alloc, inputDef, /* isSigned = */ true);
3747}
3748
3749MDefinition* MBigIntAsUintN::foldsTo(TempAllocator& alloc) {
3750 auto* bitsDef = bits();
3751 if (!bitsDef->isConstant()) {
3752 return this;
3753 }
3754
3755 // Negative |bits| throw an error and too large |bits| don't fit into Int64.
3756 int32_t bitsInt = bitsDef->toConstant()->toInt32();
3757 if (bitsInt < 0 || bitsInt > 64) {
3758 return this;
3759 }
3760
3761 // Ensure the input is Int64 typed.
3762 auto* inputDef = input();
3763 if (inputDef->isIntPtrToBigInt()) {
3764 inputDef = inputDef->toIntPtrToBigInt()->input();
3765
3766 auto* int64 = MIntPtrToInt64::New(alloc, inputDef);
3767 block()->insertBefore(this, int64);
3768 inputDef = int64;
3769 } else if (inputDef->isInt64ToBigInt()) {
3770 inputDef = inputDef->toInt64ToBigInt()->input();
3771 } else {
3772 auto* truncate = MTruncateBigIntToInt64::New(alloc, inputDef);
3773 block()->insertBefore(this, truncate);
3774 inputDef = truncate;
3775 }
3776 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", 3776); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "inputDef->type() == MIRType::Int64" ")"
); do { MOZ_CrashSequence(__null, 3776); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3777
3778 if (bitsInt < 64) {
3779 uint64_t mask = 0;
3780 if (bitsInt > 0) {
3781 mask = uint64_t(-1) >> (64 - bitsInt);
3782 }
3783
3784 // Mask off any excess bits.
3785 auto* cst = MConstant::NewInt64(alloc, int64_t(mask));
3786 block()->insertBefore(this, cst);
3787
3788 auto* bitAnd = MBitAnd::New(alloc, inputDef, cst, MIRType::Int64);
3789 block()->insertBefore(this, bitAnd);
3790
3791 inputDef = bitAnd;
3792 }
3793
3794 return MInt64ToBigInt::New(alloc, inputDef, /* isSigned = */ false);
3795}
3796
3797bool MBigIntPtrBinaryArithInstruction::isMaybeZero(MDefinition* ins) {
3798 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"
, 3798); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::IntPtr"
")"); do { MOZ_CrashSequence(__null, 3798); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3799 if (ins->isBigIntToIntPtr()) {
3800 ins = ins->toBigIntToIntPtr()->input();
3801 }
3802 if (ins->isConstant()) {
3803 if (ins->type() == MIRType::IntPtr) {
3804 return ins->toConstant()->toIntPtr() == 0;
3805 }
3806 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"
, 3806); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 3806); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3807 return ins->toConstant()->toBigInt()->isZero();
3808 }
3809 return true;
3810}
3811
3812bool MBigIntPtrBinaryArithInstruction::isMaybeNegative(MDefinition* ins) {
3813 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"
, 3813); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::IntPtr"
")"); do { MOZ_CrashSequence(__null, 3813); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3814 if (ins->isBigIntToIntPtr()) {
3815 ins = ins->toBigIntToIntPtr()->input();
3816 }
3817 if (ins->isConstant()) {
3818 if (ins->type() == MIRType::IntPtr) {
3819 return ins->toConstant()->toIntPtr() < 0;
3820 }
3821 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"
, 3821); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 3821); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3822 return ins->toConstant()->toBigInt()->isNegative();
3823 }
3824 return true;
3825}
3826
3827MDefinition* MBigIntPtrBinaryArithInstruction::foldsTo(TempAllocator& alloc) {
3828 if (auto* folded = EvaluateIntPtrConstantOperands(alloc, this)) {
3829 return folded;
3830 }
3831 return this;
3832}
3833
3834MDefinition* MBigIntPtrPow::foldsTo(TempAllocator& alloc) {
3835 // Follow MPow::foldsTo and fold if:
3836 // 1. Both operands are constants.
3837 // 2. The power operand is ≤ 4 and the operation can be expressed as a series
3838 // of multiplications.
3839
3840 if (!rhs()->isConstant()) {
3841 return this;
3842 }
3843 intptr_t pow = rhs()->toConstant()->toIntPtr();
3844
3845 if (lhs()->isConstant()) {
3846 intptr_t base = lhs()->toConstant()->toIntPtr();
3847 intptr_t result;
3848 if (!BigInt::powIntPtr(base, pow, &result)) {
3849 return this;
3850 }
3851 return MConstant::NewIntPtr(alloc, result);
3852 }
3853
3854 if (pow == 1) {
3855 return lhs();
3856 }
3857
3858 auto multiply = [this, &alloc](MDefinition* lhs, MDefinition* rhs) {
3859 auto* mul = MBigIntPtrMul::New(alloc, lhs, rhs);
3860 mul->setBailoutKind(bailoutKind());
3861 return mul;
3862 };
3863
3864 // (x ** 2n) == x*x.
3865 if (pow == 2) {
3866 return multiply(lhs(), lhs());
3867 }
3868
3869 // (x ** 3n) == x*x*x.
3870 if (pow == 3) {
3871 auto* mul1 = multiply(lhs(), lhs());
3872 block()->insertBefore(this, mul1);
3873 return multiply(lhs(), mul1);
3874 }
3875
3876 // (x ** 4n) == y*y, where y = x*x.
3877 if (pow == 4) {
3878 auto* y = multiply(lhs(), lhs());
3879 block()->insertBefore(this, y);
3880 return multiply(y, y);
3881 }
3882
3883 // No optimization
3884 return this;
3885}
3886
3887MDefinition* MBigIntPtrBinaryBitwiseInstruction::foldsTo(TempAllocator& alloc) {
3888 if (auto* folded = EvaluateIntPtrConstantOperands(alloc, this)) {
3889 return folded;
3890 }
3891 return this;
3892}
3893
3894MDefinition* MBigIntPtrBitNot::foldsTo(TempAllocator& alloc) {
3895 if (!input()->isConstant()) {
3896 return this;
3897 }
3898 return MConstant::NewIntPtr(alloc, ~input()->toConstant()->toIntPtr());
3899}
3900
3901MDefinition* MInt32ToIntPtr::foldsTo(TempAllocator& alloc) {
3902 MDefinition* def = input();
3903 if (def->isConstant()) {
3904 int32_t i = def->toConstant()->toInt32();
3905 return MConstant::NewIntPtr(alloc, intptr_t(i));
3906 }
3907
3908 if (def->isNonNegativeIntPtrToInt32()) {
3909 return def->toNonNegativeIntPtrToInt32()->input();
3910 }
3911
3912 return this;
3913}
3914
3915bool MAbs::fallible() const {
3916 return !implicitTruncate_ && (!range() || !range()->hasInt32Bounds());
3917}
3918
3919void MAbs::trySpecializeFloat32(TempAllocator& alloc) {
3920 // Do not use Float32 if we can use int32.
3921 if (input()->type() == MIRType::Int32) {
3922 return;
3923 }
3924
3925 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
3926 setResultType(MIRType::Float32);
3927 }
3928}
3929
3930MDefinition* MDiv::foldsTo(TempAllocator& alloc) {
3931 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", 3931); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsNumberType(type())" ")"); do { MOZ_CrashSequence
(__null, 3931); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3932 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"
, 3932); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type() != MIRType::IntPtr"
") (" "not yet implemented" ")"); do { MOZ_CrashSequence(__null
, 3932); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
3933
3934 if (type() == MIRType::Int64) {
3935 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) {
3936 return folded;
3937 }
3938 return this;
3939 }
3940
3941 if (MDefinition* folded = EvaluateConstantOperands(alloc, this)) {
3942 return folded;
3943 }
3944
3945 if (MDefinition* folded = EvaluateExactReciprocal(alloc, this)) {
3946 return folded;
3947 }
3948
3949 return this;
3950}
3951
3952void MDiv::analyzeEdgeCasesForward() {
3953 // This is only meaningful when doing integer division.
3954 if (type() != MIRType::Int32) {
3955 return;
3956 }
3957
3958 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"
, 3958); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 3958); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3959 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"
, 3959); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rhs()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 3959); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3960
3961 // Try removing divide by zero check
3962 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) {
3963 canBeDivideByZero_ = false;
3964 }
3965
3966 // If lhs is a constant int != INT32_MIN, then
3967 // negative overflow check can be skipped.
3968 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(INT32_MIN(-2147483647-1))) {
3969 canBeNegativeOverflow_ = false;
3970 }
3971
3972 // If rhs is a constant int != -1, likewise.
3973 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(-1)) {
3974 canBeNegativeOverflow_ = false;
3975 }
3976
3977 // If lhs is != 0, then negative zero check can be skipped.
3978 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(0)) {
3979 setCanBeNegativeZero(false);
3980 }
3981
3982 // If rhs is >= 0, likewise.
3983 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32) {
3984 if (rhs()->toConstant()->toInt32() >= 0) {
3985 setCanBeNegativeZero(false);
3986 }
3987 }
3988}
3989
3990void MDiv::analyzeEdgeCasesBackward() {
3991 // In general, canBeNegativeZero_ is only valid for integer divides.
3992 // It's fine to access here because we're only using it to avoid
3993 // wasting effort to decide whether we can clear an already cleared
3994 // flag.
3995 if (canBeNegativeZero_ && !NeedNegativeZeroCheck(this)) {
3996 setCanBeNegativeZero(false);
3997 }
3998}
3999
4000bool MDiv::fallible() const { return !isTruncated(); }
4001
4002MDefinition* MMod::foldsTo(TempAllocator& alloc) {
4003 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", 4003); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsNumberType(type())" ")"); do { MOZ_CrashSequence
(__null, 4003); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4004 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"
, 4004); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type() != MIRType::IntPtr"
") (" "not yet implemented" ")"); do { MOZ_CrashSequence(__null
, 4004); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
4005
4006 if (type() == MIRType::Int64) {
4007 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) {
4008 return folded;
4009 }
4010 } else {
4011 if (MDefinition* folded = EvaluateConstantOperands(alloc, this)) {
4012 return folded;
4013 }
4014 }
4015 return this;
4016}
4017
4018void MMod::analyzeEdgeCasesForward() {
4019 // These optimizations make sense only for integer division
4020 if (type() != MIRType::Int32) {
4021 return;
4022 }
4023
4024 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) {
4025 canBeDivideByZero_ = false;
4026 }
4027
4028 if (rhs()->isConstant()) {
4029 int32_t n = rhs()->toConstant()->toInt32();
4030 if (n > 0 && !std::has_single_bit(uint32_t(n))) {
4031 canBePowerOfTwoDivisor_ = false;
4032 }
4033 }
4034}
4035
4036bool MMod::fallible() const {
4037 return !isTruncated() &&
4038 (isUnsigned() || canBeDivideByZero() || canBeNegativeDividend());
4039}
4040
4041void MMathFunction::trySpecializeFloat32(TempAllocator& alloc) {
4042 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
4043 setResultType(MIRType::Float32);
4044 specialization_ = MIRType::Float32;
4045 }
4046}
4047
4048bool MMathFunction::isFloat32Commutative() const {
4049 switch (function_) {
4050 case UnaryMathFunction::Floor:
4051 case UnaryMathFunction::Ceil:
4052 case UnaryMathFunction::Round:
4053 case UnaryMathFunction::Trunc:
4054 return true;
4055 default:
4056 return false;
4057 }
4058}
4059
4060MHypot* MHypot::New(TempAllocator& alloc, const MDefinitionVector& vector) {
4061 uint32_t length = vector.length();
4062 MHypot* hypot = new (alloc) MHypot;
4063 if (!hypot->init(alloc, length)) {
4064 return nullptr;
4065 }
4066
4067 for (uint32_t i = 0; i < length; ++i) {
4068 hypot->initOperand(i, vector[i]);
4069 }
4070 return hypot;
4071}
4072
4073bool MAdd::fallible() const {
4074 // the add is fallible if range analysis does not say that it is finite, AND
4075 // either the truncation analysis shows that there are non-truncated uses.
4076 if (truncateKind() >= TruncateKind::IndirectTruncate) {
4077 return false;
4078 }
4079 if (range() && range()->hasInt32Bounds()) {
4080 return false;
4081 }
4082 return true;
4083}
4084
4085bool MSub::fallible() const {
4086 // see comment in MAdd::fallible()
4087 if (truncateKind() >= TruncateKind::IndirectTruncate) {
4088 return false;
4089 }
4090 if (range() && range()->hasInt32Bounds()) {
4091 return false;
4092 }
4093 return true;
4094}
4095
4096MDefinition* MSub::foldsTo(TempAllocator& alloc) {
4097 MDefinition* out = MBinaryArithInstruction::foldsTo(alloc);
4098 if (out != this) {
4099 return out;
4100 }
4101
4102 // Optimize X - X to 0. This optimization is only valid for integer values.
4103 // Subtracting a floating point value from itself returns NaN when the operand
4104 // is either Infinity or NaN.
4105 if (lhs() == rhs()) {
4106 switch (type()) {
4107 case MIRType::Int32:
4108 // Ensure that any bailouts that we depend on to guarantee that X
4109 // is Int32 are not removed.
4110 lhs()->setGuardRangeBailoutsUnchecked();
4111 return MConstant::NewInt32(alloc, 0);
4112 case MIRType::Int64:
4113 return MConstant::NewInt64(alloc, 0);
4114 case MIRType::IntPtr:
4115 return MConstant::NewIntPtr(alloc, 0);
4116 default:
4117 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", 4117); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsFloatingPointType(type())" ")"); do { MOZ_CrashSequence
(__null, 4117); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4118 }
4119 }
4120
4121 return this;
4122}
4123
4124MDefinition* MMul::foldsTo(TempAllocator& alloc) {
4125 MDefinition* out = MBinaryArithInstruction::foldsTo(alloc);
4126 if (out != this) {
4127 return out;
4128 }
4129
4130 if (type() != MIRType::Int32) {
4131 return this;
4132 }
4133
4134 if (lhs() == rhs()) {
4135 setCanBeNegativeZero(false);
4136 }
4137
4138 return this;
4139}
4140
4141void MMul::analyzeEdgeCasesForward() {
4142 // Try to remove the check for negative zero
4143 // This only makes sense when using the integer multiplication
4144 if (type() != MIRType::Int32) {
4145 return;
4146 }
4147
4148 // If lhs is > 0, no need for negative zero check.
4149 if (lhs()->isConstant() && lhs()->type() == MIRType::Int32) {
4150 if (lhs()->toConstant()->toInt32() > 0) {
4151 setCanBeNegativeZero(false);
4152 }
4153 }
4154
4155 // If rhs is > 0, likewise.
4156 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32) {
4157 if (rhs()->toConstant()->toInt32() > 0) {
4158 setCanBeNegativeZero(false);
4159 }
4160 }
4161}
4162
4163void MMul::analyzeEdgeCasesBackward() {
4164 if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) {
4165 setCanBeNegativeZero(false);
4166 }
4167}
4168
4169bool MMul::canOverflow() const {
4170 if (isTruncated()) {
4171 return false;
4172 }
4173 return !range() || !range()->hasInt32Bounds();
4174}
4175
4176bool MUrsh::fallible() const {
4177 if (bailoutsDisabled()) {
4178 return false;
4179 }
4180 return !range() || !range()->hasInt32Bounds();
4181}
4182
4183static inline bool MustBeUInt32(MDefinition* def, MDefinition** pwrapped) {
4184 if (def->isUrsh()) {
4185 *pwrapped = def->toUrsh()->lhs();
4186 MDefinition* rhs = def->toUrsh()->rhs();
4187 return def->toUrsh()->bailoutsDisabled() && rhs->maybeConstantValue() &&
4188 rhs->maybeConstantValue()->isInt32(0);
4189 }
4190
4191 if (MConstant* defConst = def->maybeConstantValue()) {
4192 *pwrapped = defConst;
4193 return defConst->type() == MIRType::Int32 && defConst->toInt32() >= 0;
4194 }
4195
4196 *pwrapped = nullptr; // silence GCC warning
4197 return false;
4198}
4199
4200/* static */
4201bool MBinaryInstruction::unsignedOperands(MDefinition* left,
4202 MDefinition* right) {
4203 MDefinition* replace;
4204 if (!MustBeUInt32(left, &replace)) {
4205 return false;
4206 }
4207 if (replace->type() != MIRType::Int32) {
4208 return false;
4209 }
4210 if (!MustBeUInt32(right, &replace)) {
4211 return false;
4212 }
4213 if (replace->type() != MIRType::Int32) {
4214 return false;
4215 }
4216 return true;
4217}
4218
4219bool MBinaryInstruction::unsignedOperands() {
4220 return unsignedOperands(getOperand(0), getOperand(1));
4221}
4222
4223void MBinaryInstruction::replaceWithUnsignedOperands() {
4224 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", 4224); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "unsignedOperands()" ")"); do { MOZ_CrashSequence
(__null, 4224); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4225
4226 for (size_t i = 0; i < numOperands(); i++) {
4227 MDefinition* replace;
4228 MustBeUInt32(getOperand(i), &replace);
4229 if (replace == getOperand(i)) {
4230 continue;
4231 }
4232
4233 getOperand(i)->setImplicitlyUsedUnchecked();
4234 replaceOperand(i, replace);
4235 }
4236}
4237
4238MDefinition* MBitNot::foldsTo(TempAllocator& alloc) {
4239 if (type() == MIRType::Int64) {
4240 return this;
4241 }
4242 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", 4242); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Int32" ")"); do { MOZ_CrashSequence
(__null, 4242); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4243
4244 MDefinition* input = getOperand(0);
4245
4246 if (input->isConstant()) {
4247 int32_t v = ~(input->toConstant()->toInt32());
4248 return MConstant::NewInt32(alloc, v);
4249 }
4250
4251 if (input->isBitNot()) {
4252 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", 4252); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "input->toBitNot()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 4252); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4253 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", 4253); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "input->toBitNot()->getOperand(0)->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 4253); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4254 return MTruncateToInt32::New(alloc,
4255 input->toBitNot()->input()); // ~~x => x | 0
4256 }
4257
4258 return this;
4259}
4260
4261static void AssertKnownClass(TempAllocator& alloc, MInstruction* ins,
4262 MDefinition* obj) {
4263#ifdef DEBUG1
4264 const JSClass* clasp = GetObjectKnownJSClass(obj);
4265 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"
, 4265); AnnotateMozCrashReason("MOZ_ASSERT" "(" "clasp" ")")
; do { MOZ_CrashSequence(__null, 4265); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4266
4267 auto* assert = MAssertClass::New(alloc, obj, clasp);
4268 ins->block()->insertBefore(ins, assert);
4269#endif
4270}
4271
4272MDefinition* MBoxNonStrictThis::foldsTo(TempAllocator& alloc) {
4273 MDefinition* in = input();
4274
4275 // BoxNonStrictThis is a no-op on objects.
4276 if (in->type() == MIRType::Object) {
4277 return in;
4278 }
4279
4280 if (!in->isBox()) {
4281 return this;
4282 }
4283
4284 MDefinition* unboxed = in->toBox()->input();
4285 if (unboxed->type() == MIRType::Object) {
4286 return unboxed;
4287 }
4288
4289 if (unboxed->typeIsOneOf({MIRType::Undefined, MIRType::Null})) {
4290 return MConstant::NewObject(alloc, this->globalThis());
4291 }
4292
4293 return this;
4294}
4295
4296MDefinition* MIdToStringOrSymbol::foldsTo(TempAllocator& alloc) {
4297 if (idVal()->isBox()) {
4298 auto* input = idVal()->toBox()->input();
4299 MIRType idType = input->type();
4300 if (idType == MIRType::String || idType == MIRType::Symbol) {
4301 return idVal();
4302 }
4303 if (idType == MIRType::Int32) {
4304 auto* toString =
4305 MToString::New(alloc, input, MToString::SideEffectHandling::Bailout);
4306 block()->insertBefore(this, toString);
4307
4308 return MBox::New(alloc, toString);
4309 }
4310 }
4311
4312 return this;
4313}
4314
4315MDefinition* MReturnFromCtor::foldsTo(TempAllocator& alloc) {
4316 MDefinition* rval = value();
4317 if (!rval->isBox()) {
4318 return this;
4319 }
4320
4321 MDefinition* unboxed = rval->toBox()->input();
4322 if (unboxed->type() == MIRType::Object) {
4323 return unboxed;
4324 }
4325
4326 return object();
4327}
4328
4329MDefinition* MTypeOf::foldsTo(TempAllocator& alloc) {
4330 MDefinition* unboxed = input();
4331 if (unboxed->isBox()) {
4332 unboxed = unboxed->toBox()->input();
4333 }
4334
4335 JSType type;
4336 switch (unboxed->type()) {
4337 case MIRType::Double:
4338 case MIRType::Float32:
4339 case MIRType::Int32:
4340 type = JSTYPE_NUMBER;
4341 break;
4342 case MIRType::String:
4343 type = JSTYPE_STRING;
4344 break;
4345 case MIRType::Symbol:
4346 type = JSTYPE_SYMBOL;
4347 break;
4348 case MIRType::BigInt:
4349 type = JSTYPE_BIGINT;
4350 break;
4351 case MIRType::Null:
4352 type = JSTYPE_OBJECT;
4353 break;
4354 case MIRType::Undefined:
4355 type = JSTYPE_UNDEFINED;
4356 break;
4357 case MIRType::Boolean:
4358 type = JSTYPE_BOOLEAN;
4359 break;
4360 case MIRType::Object: {
4361 KnownClass known = GetObjectKnownClass(unboxed);
4362 if (known != KnownClass::None) {
4363 if (known == KnownClass::Function) {
4364 type = JSTYPE_FUNCTION;
4365 } else {
4366 type = JSTYPE_OBJECT;
4367 }
4368
4369 AssertKnownClass(alloc, this, unboxed);
4370 break;
4371 }
4372 [[fallthrough]];
4373 }
4374 default:
4375 return this;
4376 }
4377
4378 return MConstant::NewInt32(alloc, static_cast<int32_t>(type));
4379}
4380
4381MDefinition* MTypeOfName::foldsTo(TempAllocator& alloc) {
4382 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"
, 4382); AnnotateMozCrashReason("MOZ_ASSERT" "(" "input()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 4382); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4383
4384 if (!input()->isConstant()) {
4385 return this;
4386 }
4387
4388 static_assert(JSTYPE_UNDEFINED == 0);
4389
4390 int32_t type = input()->toConstant()->toInt32();
4391 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", 4391); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "JSTYPE_UNDEFINED <= type && type < JSTYPE_LIMIT"
")"); do { MOZ_CrashSequence(__null, 4391); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4392
4393 JSString* name =
4394 TypeName(static_cast<JSType>(type), GetJitContext()->runtime->names());
4395 return MConstant::NewString(alloc, name);
4396}
4397
4398MUrsh* MUrsh::NewWasm(TempAllocator& alloc, MDefinition* left,
4399 MDefinition* right, MIRType type) {
4400 MUrsh* ins = new (alloc) MUrsh(left, right, type);
4401
4402 // Since Ion has no UInt32 type, we use Int32 and we have a special
4403 // exception to the type rules: we can return values in
4404 // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type
4405 // without bailing out. This is necessary because Ion has no UInt32
4406 // type and we can't have bailouts in wasm code.
4407 ins->bailoutsDisabled_ = true;
4408
4409 return ins;
4410}
4411
4412MResumePoint* MResumePoint::New(TempAllocator& alloc, MBasicBlock* block,
4413 jsbytecode* pc, ResumeMode mode) {
4414 MResumePoint* resume = new (alloc) MResumePoint(block, pc, mode);
4415 if (!resume->init(alloc)) {
4416 block->discardPreAllocatedResumePoint(resume);
4417 return nullptr;
4418 }
4419 resume->inherit(block);
4420 return resume;
4421}
4422
4423MResumePoint* MResumePoint::clone(TempAllocator& alloc) {
4424 MResumePoint* resume = new (alloc) MResumePoint(block(), pc_, mode_);
4425 size_t n = this->numOperands();
4426 if (!resume->operands_.init(alloc, n)) {
4427 return nullptr;
4428 }
4429 for (size_t i = 0; i < n; i++) {
4430 resume->initOperand(i, getOperand(i));
4431 }
4432 resume->stores_.copy(this->stores_);
4433 return resume;
4434}
4435
4436MResumePoint::MResumePoint(MBasicBlock* block, jsbytecode* pc, ResumeMode mode)
4437 : MNode(block, Kind::ResumePoint),
4438 pc_(pc),
4439 instruction_(nullptr),
4440 mode_(mode) {
4441 block->addResumePoint(this);
4442}
4443
4444bool MResumePoint::init(TempAllocator& alloc) {
4445 return operands_.init(alloc, block()->stackDepth());
4446}
4447
4448MResumePoint* MResumePoint::caller() const {
4449 return block()->callerResumePoint();
4450}
4451
4452void MResumePoint::inherit(MBasicBlock* block) {
4453 // FixedList doesn't initialize its elements, so do unchecked inits.
4454 for (size_t i = 0; i < stackDepth(); i++) {
4455 initOperand(i, block->getSlot(i));
4456 }
4457}
4458
4459void MResumePoint::addStore(TempAllocator& alloc, MDefinition* store,
4460 const MResumePoint* cache) {
4461 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", 4461); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "block()->outerResumePoint() != this" ")"
); do { MOZ_CrashSequence(__null, 4461); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4462 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", 4462); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!cache->stores_.empty()" ")"); do { MOZ_CrashSequence
(__null, 4462); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
4463
4464 if (cache && cache->stores_.begin()->operand == store) {
4465 // If the last resume point had the same side-effect stack, then we can
4466 // reuse the current side effect without cloning it. This is a simple
4467 // way to share common context by making a spaghetti stack.
4468 if (++cache->stores_.begin() == stores_.begin()) {
4469 stores_.copy(cache->stores_);
4470 return;
4471 }
4472 }
4473
4474 // Ensure that the store would not be deleted by DCE.
4475 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", 4475); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "store->isEffectful()" ")"); do { MOZ_CrashSequence
(__null, 4475); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4476
4477 MStoreToRecover* top = new (alloc) MStoreToRecover(store);
4478 stores_.push(top);
4479}
4480
4481#ifdef JS_JITSPEW1
4482void MResumePoint::dump(GenericPrinter& out) const {
4483 out.printf("resumepoint mode=");
4484
4485 switch (mode()) {
4486 case ResumeMode::ResumeAt:
4487 if (instruction_) {
4488 out.printf("ResumeAt(%u)", instruction_->id());
4489 } else {
4490 out.printf("ResumeAt");
4491 }
4492 break;
4493 default:
4494 out.put(ResumeModeToString(mode()));
4495 break;
4496 }
4497
4498 if (MResumePoint* c = caller()) {
4499 out.printf(" (caller in block%u)", c->block()->id());
4500 }
4501
4502 for (size_t i = 0; i < numOperands(); i++) {
4503 out.printf(" ");
4504 if (operands_[i].hasProducer()) {
4505 getOperand(i)->printName(out);
4506 } else {
4507 out.printf("(null)");
4508 }
4509 }
4510 out.printf("\n");
4511}
4512
4513void MResumePoint::dump() const {
4514 Fprinter out(stderrstderr);
4515 dump(out);
4516 out.finish();
4517}
4518#endif
4519
4520bool MResumePoint::isObservableOperand(MUse* u) const {
4521 return isObservableOperand(indexOf(u));
4522}
4523
4524bool MResumePoint::isObservableOperand(size_t index) const {
4525 return block()->info().isObservableSlot(index);
4526}
4527
4528bool MResumePoint::isRecoverableOperand(MUse* u) const {
4529 return block()->info().isRecoverableOperand(indexOf(u));
4530}
4531
4532MDefinition* MBigIntToIntPtr::foldsTo(TempAllocator& alloc) {
4533 MDefinition* def = input();
4534
4535 // If the operand converts an IntPtr to BigInt, drop both conversions.
4536 if (def->isIntPtrToBigInt()) {
4537 return def->toIntPtrToBigInt()->input();
4538 }
4539
4540 // Fold this operation if the input operand is constant.
4541 if (def->isConstant()) {
4542 BigInt* bigInt = def->toConstant()->toBigInt();
4543 intptr_t i;
4544 if (BigInt::isIntPtr(bigInt, &i)) {
4545 return MConstant::NewIntPtr(alloc, i);
4546 }
4547 }
4548
4549 // Fold BigIntToIntPtr(Int64ToBigInt(int64)) to Int64ToIntPtr(int64)
4550 if (def->isInt64ToBigInt()) {
4551 auto* toBigInt = def->toInt64ToBigInt();
4552 return MInt64ToIntPtr::New(alloc, toBigInt->input(), toBigInt->isSigned());
4553 }
4554
4555 return this;
4556}
4557
4558MDefinition* MIntPtrToBigInt::foldsTo(TempAllocator& alloc) {
4559 MDefinition* def = input();
4560
4561 // If the operand converts a BigInt to IntPtr, drop both conversions.
4562 if (def->isBigIntToIntPtr()) {
4563 return def->toBigIntToIntPtr()->input();
4564 }
4565
4566 return this;
4567}
4568
4569MDefinition* MTruncateBigIntToInt64::foldsTo(TempAllocator& alloc) {
4570 MDefinition* input = this->input();
4571 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"
, 4571); AnnotateMozCrashReason("MOZ_ASSERT" "(" "input->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 4571); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4572
4573 // If the operand converts an I64 to BigInt, drop both conversions.
4574 if (input->isInt64ToBigInt()) {
4575 return input->toInt64ToBigInt()->input();
4576 }
4577
4578 // If the operand is an IntPtr, extend the IntPtr to I64.
4579 if (input->isIntPtrToBigInt()) {
4580 auto* intPtr = input->toIntPtrToBigInt()->input();
4581 if (intPtr->isConstant()) {
4582 intptr_t c = intPtr->toConstant()->toIntPtr();
4583 return MConstant::NewInt64(alloc, int64_t(c));
4584 }
4585 return MIntPtrToInt64::New(alloc, intPtr);
4586 }
4587
4588 // Fold this operation if the input operand is constant.
4589 if (input->isConstant()) {
4590 return MConstant::NewInt64(
4591 alloc, BigInt::toInt64(input->toConstant()->toBigInt()));
4592 }
4593
4594 return this;
4595}
4596
4597MDefinition* MToInt64::foldsTo(TempAllocator& alloc) {
4598 MDefinition* input = getOperand(0);
4599
4600 if (input->isBox()) {
4601 input = input->getOperand(0);
4602 }
4603
4604 // Unwrap MInt64ToBigInt: MToInt64(MInt64ToBigInt(int64)) = int64.
4605 if (input->isInt64ToBigInt()) {
4606 return input->getOperand(0);
4607 }
4608
4609 // Unwrap IntPtrToBigInt:
4610 // MToInt64(MIntPtrToBigInt(intptr)) = MIntPtrToInt64(intptr).
4611 if (input->isIntPtrToBigInt()) {
4612 auto* intPtr = input->toIntPtrToBigInt()->input();
4613 if (intPtr->isConstant()) {
4614 intptr_t c = intPtr->toConstant()->toIntPtr();
4615 return MConstant::NewInt64(alloc, int64_t(c));
4616 }
4617 return MIntPtrToInt64::New(alloc, intPtr);
4618 }
4619
4620 // When the input is an Int64 already, just return it.
4621 if (input->type() == MIRType::Int64) {
4622 return input;
4623 }
4624
4625 // Fold this operation if the input operand is constant.
4626 if (input->isConstant()) {
4627 switch (input->type()) {
4628 case MIRType::Boolean:
4629 return MConstant::NewInt64(alloc, input->toConstant()->toBoolean());
4630 default:
4631 break;
4632 }
4633 }
4634
4635 return this;
4636}
4637
4638MDefinition* MToNumberInt32::foldsTo(TempAllocator& alloc) {
4639 // Fold this operation if the input operand is constant.
4640 if (MConstant* cst = input()->maybeConstantValue()) {
4641 switch (cst->type()) {
4642 case MIRType::Null:
4643 if (conversion() == IntConversionInputKind::Any) {
4644 return MConstant::NewInt32(alloc, 0);
4645 }
4646 break;
4647 case MIRType::Boolean:
4648 if (conversion() == IntConversionInputKind::Any) {
4649 return MConstant::NewInt32(alloc, cst->toBoolean());
4650 }
4651 break;
4652 case MIRType::Int32:
4653 return MConstant::NewInt32(alloc, cst->toInt32());
4654 case MIRType::Float32:
4655 case MIRType::Double:
4656 int32_t ival;
4657 // Only the value within the range of Int32 can be substituted as
4658 // constant.
4659 if (mozilla::NumberIsInt32(cst->numberToDouble(), &ival)) {
4660 return MConstant::NewInt32(alloc, ival);
4661 }
4662 break;
4663 default:
4664 break;
4665 }
4666 }
4667
4668 MDefinition* input = getOperand(0);
4669 if (input->isBox()) {
4670 input = input->toBox()->input();
4671 }
4672
4673 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
4674 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
4675 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
4676 // is folded to a MTruncateToInt32 node, which will result in this MIR:
4677 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
4678 // the MUrsh node's type is int32 (since uint32 is not implemented), and
4679 // that would fold the MTruncateToInt32 node. This will make the modulo
4680 // unsigned, while is should have been signed.
4681 if (input->type() == MIRType::Int32 && !IsUint32Type(input)) {
4682 return input;
4683 }
4684
4685 return this;
4686}
4687
4688MDefinition* MBooleanToInt32::foldsTo(TempAllocator& alloc) {
4689 MDefinition* input = getOperand(0);
4690 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"
, 4690); AnnotateMozCrashReason("MOZ_ASSERT" "(" "input->type() == MIRType::Boolean"
")"); do { MOZ_CrashSequence(__null, 4690); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4691
4692 if (input->isConstant()) {
4693 return MConstant::NewInt32(alloc, input->toConstant()->toBoolean());
4694 }
4695
4696 return this;
4697}
4698
4699void MToNumberInt32::analyzeEdgeCasesBackward() {
4700 if (!NeedNegativeZeroCheck(this)) {
4701 setNeedsNegativeZeroCheck(false);
4702 }
4703}
4704
4705MDefinition* MTruncateToInt32::foldsTo(TempAllocator& alloc) {
4706 MDefinition* input = getOperand(0);
4707 if (input->isBox()) {
4708 input = input->getOperand(0);
4709 }
4710
4711 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
4712 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
4713 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
4714 // is folded to a MTruncateToInt32 node, which will result in this MIR:
4715 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
4716 // the MUrsh node's type is int32 (since uint32 is not implemented), and
4717 // that would fold the MTruncateToInt32 node. This will make the modulo
4718 // unsigned, while is should have been signed.
4719 if (input->type() == MIRType::Int32 && !IsUint32Type(input)) {
4720 return input;
4721 }
4722
4723 if (input->type() == MIRType::Double && input->isConstant()) {
4724 int32_t ret = ToInt32(input->toConstant()->toDouble());
4725 return MConstant::NewInt32(alloc, ret);
4726 }
4727
4728 return this;
4729}
4730
4731MDefinition* MWrapInt64ToInt32::foldsTo(TempAllocator& alloc) {
4732 MDefinition* input = this->input();
4733 if (input->isConstant()) {
4734 uint64_t c = input->toConstant()->toInt64();
4735 int32_t output = bottomHalf() ? int32_t(c) : int32_t(c >> 32);
4736 return MConstant::NewInt32(alloc, output);
4737 }
4738
4739 return this;
4740}
4741
4742MDefinition* MExtendInt32ToInt64::foldsTo(TempAllocator& alloc) {
4743 MDefinition* input = this->input();
4744 if (input->isConstant()) {
4745 int32_t c = input->toConstant()->toInt32();
4746 int64_t res = isUnsigned() ? int64_t(uint32_t(c)) : int64_t(c);
4747 return MConstant::NewInt64(alloc, res);
4748 }
4749
4750 return this;
4751}
4752
4753MDefinition* MSignExtendInt32::foldsTo(TempAllocator& alloc) {
4754 MDefinition* input = this->input();
4755 if (input->isConstant()) {
4756 int32_t c = input->toConstant()->toInt32();
4757 int32_t res;
4758 switch (mode_) {
4759 case Byte:
4760 res = int32_t(int8_t(c & 0xFF));
4761 break;
4762 case Half:
4763 res = int32_t(int16_t(c & 0xFFFF));
4764 break;
4765 }
4766 return MConstant::NewInt32(alloc, res);
4767 }
4768
4769 return this;
4770}
4771
4772MDefinition* MSignExtendInt64::foldsTo(TempAllocator& alloc) {
4773 MDefinition* input = this->input();
4774 if (input->isConstant()) {
4775 int64_t c = input->toConstant()->toInt64();
4776 int64_t res;
4777 switch (mode_) {
4778 case Byte:
4779 res = int64_t(int8_t(c & 0xFF));
4780 break;
4781 case Half:
4782 res = int64_t(int16_t(c & 0xFFFF));
4783 break;
4784 case Word:
4785 res = int64_t(int32_t(c & 0xFFFFFFFFU));
4786 break;
4787 }
4788 return MConstant::NewInt64(alloc, res);
4789 }
4790
4791 return this;
4792}
4793
4794MDefinition* MSignExtendIntPtr::foldsTo(TempAllocator& alloc) {
4795 MDefinition* input = this->input();
4796 if (input->isConstant()) {
4797 intptr_t c = input->toConstant()->toIntPtr();
4798 intptr_t res;
4799 switch (mode_) {
4800 case Byte:
4801 res = intptr_t(int8_t(c & 0xFF));
4802 break;
4803 case Half:
4804 res = intptr_t(int16_t(c & 0xFFFF));
4805 break;
4806 case Word:
4807 res = intptr_t(int32_t(c & 0xFFFFFFFFU));
4808 break;
4809 }
4810 return MConstant::NewIntPtr(alloc, res);
4811 }
4812
4813 return this;
4814}
4815
4816MDefinition* MToDouble::foldsTo(TempAllocator& alloc) {
4817 MDefinition* input = getOperand(0);
4818 if (input->isBox()) {
4819 input = input->getOperand(0);
4820 }
4821
4822 if (input->type() == MIRType::Double) {
4823 return input;
4824 }
4825
4826 if (input->isConstant() &&
4827 input->toConstant()->isTypeRepresentableAsDouble()) {
4828 return MConstant::NewDouble(alloc, input->toConstant()->numberToDouble());
4829 }
4830
4831 return this;
4832}
4833
4834MDefinition* MToFloat32::foldsTo(TempAllocator& alloc) {
4835 MDefinition* input = getOperand(0);
4836 if (input->isBox()) {
4837 input = input->getOperand(0);
4838 }
4839
4840 if (input->type() == MIRType::Float32) {
4841 return input;
4842 }
4843
4844 // If x is a Float32, Float32(Double(x)) == x
4845 if (!mustPreserveNaN_ && input->isToDouble() &&
4846 input->toToDouble()->input()->type() == MIRType::Float32) {
4847 return input->toToDouble()->input();
4848 }
4849
4850 if (input->isConstant() &&
4851 input->toConstant()->isTypeRepresentableAsDouble()) {
4852 return MConstant::NewFloat32(alloc,
4853 float(input->toConstant()->numberToDouble()));
4854 }
4855
4856 // Fold ToFloat32(ToDouble(int32)) to ToFloat32(int32).
4857 if (input->isToDouble() &&
4858 input->toToDouble()->input()->type() == MIRType::Int32) {
4859 return MToFloat32::New(alloc, input->toToDouble()->input());
4860 }
4861
4862 return this;
4863}
4864
4865MDefinition* MToFloat16::foldsTo(TempAllocator& alloc) {
4866 MDefinition* in = input();
4867 if (in->isBox()) {
4868 in = in->toBox()->input();
4869 }
4870
4871 if (in->isConstant()) {
4872 auto* cst = in->toConstant();
4873 if (cst->isTypeRepresentableAsDouble()) {
4874 double num = cst->numberToDouble();
4875 return MConstant::NewFloat32(alloc, static_cast<float>(js::float16{num}));
4876 }
4877 }
4878
4879 auto isFloat16 = [](auto* def) -> MDefinition* {
4880 // ToFloat16(ToDouble(float16)) => float16
4881 // ToFloat16(ToFloat32(float16)) => float16
4882 if (def->isToDouble()) {
4883 def = def->toToDouble()->input();
4884 } else if (def->isToFloat32()) {
4885 def = def->toToFloat32()->input();
4886 }
4887
4888 // ToFloat16(ToFloat16(x)) => ToFloat16(x)
4889 if (def->isToFloat16()) {
4890 return def;
4891 }
4892
4893 // Unwrap CanonicalizeNaN added after load instructions.
4894 MDefinition* load = def;
4895 if (load->isCanonicalizeNaN()) {
4896 load = load->toCanonicalizeNaN()->input();
4897 }
4898
4899 // ToFloat16(LoadFloat16(x)) => LoadFloat16(x)
4900 if (load->isLoadUnboxedScalar() &&
4901 load->toLoadUnboxedScalar()->storageType() == Scalar::Float16) {
4902 return def;
4903 }
4904 if (load->isLoadDataViewElement() &&
4905 load->toLoadDataViewElement()->storageType() == Scalar::Float16) {
4906 return def;
4907 }
4908 return nullptr;
4909 };
4910
4911 // Fold loads which are guaranteed to return Float16.
4912 if (auto* f16 = isFloat16(in)) {
4913 return f16;
4914 }
4915
4916 // Fold ToFloat16(ToDouble(float32)) to ToFloat16(float32).
4917 // Fold ToFloat16(ToDouble(int32)) to ToFloat16(int32).
4918 if (in->isToDouble()) {
4919 auto* toDoubleInput = in->toToDouble()->input();
4920 if (toDoubleInput->type() == MIRType::Float32 ||
4921 toDoubleInput->type() == MIRType::Int32) {
4922 return MToFloat16::New(alloc, toDoubleInput);
4923 }
4924 }
4925
4926 return this;
4927}
4928
4929MDefinition* MToString::foldsTo(TempAllocator& alloc) {
4930 MDefinition* in = input();
4931 if (in->isBox()) {
4932 in = in->getOperand(0);
4933 }
4934
4935 if (in->type() == MIRType::String) {
4936 return in;
4937 }
4938 return this;
4939}
4940
4941MDefinition* MClampToUint8::foldsTo(TempAllocator& alloc) {
4942 if (MConstant* inputConst = input()->maybeConstantValue()) {
4943 if (inputConst->isTypeRepresentableAsDouble()) {
4944 int32_t clamped = ClampDoubleToUint8(inputConst->numberToDouble());
4945 return MConstant::NewInt32(alloc, clamped);
4946 }
4947 }
4948 return this;
4949}
4950
4951bool MCompare::tryFoldEqualOperands(bool* result) {
4952 if (lhs() != rhs()) {
4953 return false;
4954 }
4955
4956 // Intuitively somebody would think that if lhs === rhs,
4957 // then we can just return true. (Or false for !==)
4958 // However NaN !== NaN is true! So we spend some time trying
4959 // to eliminate this case.
4960
4961 if (!IsEqualityOp(jsop())) {
4962 return false;
4963 }
4964
4965 switch (compareType_) {
4966 case Compare_Int32:
4967 case Compare_UInt32:
4968 case Compare_Int64:
4969 case Compare_UInt64:
4970 case Compare_IntPtr:
4971 case Compare_UIntPtr:
4972 case Compare_Float32:
4973 case Compare_Double:
4974 case Compare_String:
4975 case Compare_Object:
4976 case Compare_Symbol:
4977 case Compare_BigInt:
4978 case Compare_WasmAnyRef:
4979 case Compare_Null:
4980 case Compare_Undefined:
4981 break;
4982 case Compare_BigInt_Int32:
4983 case Compare_BigInt_String:
4984 case Compare_BigInt_Double:
4985 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", 4985); AnnotateMozCrashReason
("MOZ_CRASH(" "Expecting different operands for lhs and rhs" ")"
); do { MOZ_CrashSequence(__null, 4985); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
4986 }
4987
4988 if (isDoubleComparison() || isFloat32Comparison()) {
4989 if (!operandsAreNeverNaN()) {
4990 return false;
4991 }
4992 } else {
4993 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", 4993); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsFloatingPointType(lhs()->type())" ")"
); do { MOZ_CrashSequence(__null, 4993); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4994 }
4995
4996 lhs()->setGuardRangeBailoutsUnchecked();
4997
4998 *result = (jsop() == JSOp::StrictEq || jsop() == JSOp::Eq);
4999 return true;
5000}
5001
5002static JSType TypeOfName(const JSOffThreadAtom* str) {
5003 static constexpr std::array types = {
5004 JSTYPE_UNDEFINED, JSTYPE_OBJECT, JSTYPE_FUNCTION, JSTYPE_STRING,
5005 JSTYPE_NUMBER, JSTYPE_BOOLEAN, JSTYPE_SYMBOL, JSTYPE_BIGINT,
5006 };
5007 static_assert(types.size() == JSTYPE_LIMIT);
5008
5009 const JSAtomState& names = GetJitContext()->runtime->names();
5010 for (auto type : types) {
5011 // Both sides are atoms, so we can simply compare pointer identity.
5012 if (TypeName(type, names) == str->unwrap()) {
5013 return type;
5014 }
5015 }
5016 return JSTYPE_LIMIT;
5017}
5018
5019struct TypeOfCompareInput {
5020 // The `typeof expr` side of the comparison.
5021 // MTypeOfName for JSOp::Typeof/JSOp::TypeofExpr, and
5022 // MTypeOf for JSOp::TypeofEq (same pointer as typeOf).
5023 MDefinition* typeOfSide;
5024
5025 // The actual `typeof` operation.
5026 MTypeOf* typeOf;
5027
5028 // The string side of the comparison.
5029 JSType type;
5030
5031 // True if the comparison uses raw JSType (Generated for JSOp::TypeofEq).
5032 bool isIntComparison;
5033
5034 TypeOfCompareInput(MDefinition* typeOfSide, MTypeOf* typeOf, JSType type,
5035 bool isIntComparison)
5036 : typeOfSide(typeOfSide),
5037 typeOf(typeOf),
5038 type(type),
5039 isIntComparison(isIntComparison) {}
5040};
5041
5042static mozilla::Maybe<TypeOfCompareInput> IsTypeOfCompare(MCompare* ins) {
5043 if (!IsEqualityOp(ins->jsop())) {
5044 return mozilla::Nothing();
5045 }
5046
5047 if (ins->compareType() == MCompare::Compare_Int32) {
5048 auto* lhs = ins->lhs();
5049 auto* rhs = ins->rhs();
5050
5051 // NOTE: The comparison is generated inside JIT, and typeof should always
5052 // be in the LHS.
5053 if (!lhs->isTypeOf() || !rhs->isConstant()) {
5054 return mozilla::Nothing();
5055 }
5056
5057 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"
, 5057); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::Boolean"
")"); do { MOZ_CrashSequence(__null, 5057); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5058 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"
, 5058); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 5058); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5059 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"
, 5059); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rhs->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 5059); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5060
5061 auto* typeOf = lhs->toTypeOf();
5062 auto* constant = rhs->toConstant();
5063
5064 JSType type = JSType(constant->toInt32());
5065 return mozilla::Some(TypeOfCompareInput(typeOf, typeOf, type, true));
5066 }
5067
5068 if (ins->compareType() != MCompare::Compare_String) {
5069 return mozilla::Nothing();
5070 }
5071
5072 auto* lhs = ins->lhs();
5073 auto* rhs = ins->rhs();
5074
5075 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"
, 5075); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->type() == MIRType::Boolean"
")"); do { MOZ_CrashSequence(__null, 5075); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5076 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"
, 5076); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5076); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5077 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"
, 5077); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rhs->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5077); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5078
5079 if (!lhs->isTypeOfName() && !rhs->isTypeOfName()) {
5080 return mozilla::Nothing();
5081 }
5082 if (!lhs->isConstant() && !rhs->isConstant()) {
5083 return mozilla::Nothing();
5084 }
5085
5086 auto* typeOfName =
5087 lhs->isTypeOfName() ? lhs->toTypeOfName() : rhs->toTypeOfName();
5088 auto* typeOf = typeOfName->input()->toTypeOf();
5089
5090 auto* constant = lhs->isConstant() ? lhs->toConstant() : rhs->toConstant();
5091
5092 JSType type = TypeOfName(constant->toString());
5093 return mozilla::Some(TypeOfCompareInput(typeOfName, typeOf, type, false));
5094}
5095
5096bool MCompare::tryFoldTypeOf(bool* result) {
5097 auto typeOfCompare = IsTypeOfCompare(this);
5098 if (!typeOfCompare) {
5099 return false;
5100 }
5101 auto* typeOf = typeOfCompare->typeOf;
5102 JSType type = typeOfCompare->type;
5103
5104 // Can't fold if the input is boxed. (Unless the typeof string is bogus.)
5105 MIRType inputType = typeOf->input()->type();
5106 if (inputType == MIRType::Value && type != JSTYPE_LIMIT) {
5107 return false;
5108 }
5109
5110 bool matchesInputType;
5111 switch (type) {
5112 case JSTYPE_BOOLEAN:
5113 matchesInputType = (inputType == MIRType::Boolean);
5114 break;
5115 case JSTYPE_NUMBER:
5116 matchesInputType = IsTypeRepresentableAsDouble(inputType);
5117 break;
5118 case JSTYPE_STRING:
5119 matchesInputType = (inputType == MIRType::String);
5120 break;
5121 case JSTYPE_SYMBOL:
5122 matchesInputType = (inputType == MIRType::Symbol);
5123 break;
5124 case JSTYPE_BIGINT:
5125 matchesInputType = (inputType == MIRType::BigInt);
5126 break;
5127 case JSTYPE_OBJECT:
5128 // Watch out for `object-emulating-undefined` and callable objects.
5129 if (inputType == MIRType::Object) {
5130 return false;
5131 }
5132 matchesInputType = (inputType == MIRType::Null);
5133 break;
5134 case JSTYPE_UNDEFINED:
5135 // Watch out for `object-emulating-undefined`.
5136 if (inputType == MIRType::Object) {
5137 return false;
5138 }
5139 matchesInputType = (inputType == MIRType::Undefined);
5140 break;
5141 case JSTYPE_FUNCTION:
5142 // Can't decide at compile-time if an object is callable.
5143 if (inputType == MIRType::Object) {
5144 return false;
5145 }
5146 matchesInputType = false;
5147 break;
5148 case JSTYPE_LIMIT:
5149 matchesInputType = false;
5150 break;
5151 }
5152
5153 if (matchesInputType) {
5154 *result = (jsop() == JSOp::StrictEq || jsop() == JSOp::Eq);
5155 } else {
5156 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
5157 }
5158 return true;
5159}
5160
5161bool MCompare::tryFold(bool* result) {
5162 JSOp op = jsop();
5163
5164 if (tryFoldEqualOperands(result)) {
5165 return true;
5166 }
5167
5168 if (tryFoldTypeOf(result)) {
5169 return true;
5170 }
5171
5172 if (compareType_ == Compare_Null || compareType_ == Compare_Undefined) {
5173 // The LHS is the value we want to test against null or undefined.
5174 if (IsStrictEqualityOp(op)) {
5175 MIRType expectedType =
5176 compareType_ == Compare_Null ? MIRType::Null : MIRType::Undefined;
5177 if (lhs()->type() == expectedType) {
5178 *result = (op == JSOp::StrictEq);
5179 return true;
5180 }
5181 if (lhs()->type() != MIRType::Value) {
5182 *result = (op == JSOp::StrictNe);
5183 return true;
5184 }
5185 } else {
5186 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", 5186); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 5186); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5187 if (IsNullOrUndefined(lhs()->type())) {
5188 *result = (op == JSOp::Eq);
5189 return true;
5190 }
5191 if (lhs()->type() != MIRType::Object && lhs()->type() != MIRType::Value) {
5192 *result = (op == JSOp::Ne);
5193 return true;
5194 }
5195 }
5196 return false;
5197 }
5198
5199 return false;
5200}
5201
5202template <typename T>
5203static bool FoldComparison(JSOp op, T left, T right) {
5204 switch (op) {
5205 case JSOp::Lt:
5206 return left < right;
5207 case JSOp::Le:
5208 return left <= right;
5209 case JSOp::Gt:
5210 return left > right;
5211 case JSOp::Ge:
5212 return left >= right;
5213 case JSOp::StrictEq:
5214 case JSOp::Eq:
5215 return left == right;
5216 case JSOp::StrictNe:
5217 case JSOp::Ne:
5218 return left != right;
5219 default:
5220 MOZ_CRASH("Unexpected op.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected op."
, "./../../../../js/src/jit/MIR.cpp", 5220); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected op." ")"); do { MOZ_CrashSequence(__null
, 5220); __attribute__((nomerge)) ::abort(); } while (false);
} while (false)
;
5221 }
5222}
5223
5224static bool FoldBigIntComparison(JSOp op, const BigInt* left, double right) {
5225 switch (op) {
5226 case JSOp::Lt:
5227 return BigInt::lessThan(left, right).valueOr(false);
5228 case JSOp::Le:
5229 return !BigInt::lessThan(right, left).valueOr(true);
5230 case JSOp::Gt:
5231 return BigInt::lessThan(right, left).valueOr(false);
5232 case JSOp::Ge:
5233 return !BigInt::lessThan(left, right).valueOr(true);
5234 case JSOp::StrictEq:
5235 case JSOp::Eq:
5236 return BigInt::equal(left, right);
5237 case JSOp::StrictNe:
5238 case JSOp::Ne:
5239 return !BigInt::equal(left, right);
5240 default:
5241 MOZ_CRASH("Unexpected op.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected op."
, "./../../../../js/src/jit/MIR.cpp", 5241); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected op." ")"); do { MOZ_CrashSequence(__null
, 5241); __attribute__((nomerge)) ::abort(); } while (false);
} while (false)
;
5242 }
5243}
5244
5245bool MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result) {
5246 if (type() != MIRType::Boolean && type() != MIRType::Int32) {
5247 return false;
5248 }
5249
5250 MDefinition* left = getOperand(0);
5251 MDefinition* right = getOperand(1);
5252
5253 if (compareType() == Compare_Double) {
5254 // Optimize "MCompare MConstant (MToDouble SomethingInInt32Range).
5255 // In most cases the MToDouble was added, because the constant is
5256 // a double.
5257 // e.g. v < 9007199254740991, where v is an int32 is always true.
5258 if (!lhs()->isConstant() && !rhs()->isConstant()) {
5259 return false;
5260 }
5261
5262 MDefinition* operand = left->isConstant() ? right : left;
5263 MConstant* constant =
5264 left->isConstant() ? left->toConstant() : right->toConstant();
5265 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", 5265); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "constant->type() == MIRType::Double" ")"
); do { MOZ_CrashSequence(__null, 5265); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5266 double cte = constant->toDouble();
5267
5268 if (operand->isToDouble() &&
5269 operand->getOperand(0)->type() == MIRType::Int32) {
5270 bool replaced = false;
5271 switch (jsop_) {
5272 case JSOp::Lt:
5273 if (cte > INT32_MAX(2147483647) || cte < INT32_MIN(-2147483647-1)) {
5274 *result = !((constant == lhs()) ^ (cte < INT32_MIN(-2147483647-1)));
5275 replaced = true;
5276 }
5277 break;
5278 case JSOp::Le:
5279 if (constant == lhs()) {
5280 if (cte > INT32_MAX(2147483647) || cte <= INT32_MIN(-2147483647-1)) {
5281 *result = (cte <= INT32_MIN(-2147483647-1));
5282 replaced = true;
5283 }
5284 } else {
5285 if (cte >= INT32_MAX(2147483647) || cte < INT32_MIN(-2147483647-1)) {
5286 *result = (cte >= INT32_MIN(-2147483647-1));
5287 replaced = true;
5288 }
5289 }
5290 break;
5291 case JSOp::Gt:
5292 if (cte > INT32_MAX(2147483647) || cte < INT32_MIN(-2147483647-1)) {
5293 *result = !((constant == rhs()) ^ (cte < INT32_MIN(-2147483647-1)));
5294 replaced = true;
5295 }
5296 break;
5297 case JSOp::Ge:
5298 if (constant == lhs()) {
5299 if (cte >= INT32_MAX(2147483647) || cte < INT32_MIN(-2147483647-1)) {
5300 *result = (cte >= INT32_MAX(2147483647));
5301 replaced = true;
5302 }
5303 } else {
5304 if (cte > INT32_MAX(2147483647) || cte <= INT32_MIN(-2147483647-1)) {
5305 *result = (cte <= INT32_MIN(-2147483647-1));
5306 replaced = true;
5307 }
5308 }
5309 break;
5310 case JSOp::StrictEq: // Fall through.
5311 case JSOp::Eq:
5312 if (cte > INT32_MAX(2147483647) || cte < INT32_MIN(-2147483647-1)) {
5313 *result = false;
5314 replaced = true;
5315 }
5316 break;
5317 case JSOp::StrictNe: // Fall through.
5318 case JSOp::Ne:
5319 if (cte > INT32_MAX(2147483647) || cte < INT32_MIN(-2147483647-1)) {
5320 *result = true;
5321 replaced = true;
5322 }
5323 break;
5324 default:
5325 MOZ_CRASH("Unexpected op.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected op."
, "./../../../../js/src/jit/MIR.cpp", 5325); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected op." ")"); do { MOZ_CrashSequence(__null
, 5325); __attribute__((nomerge)) ::abort(); } while (false);
} while (false)
;
5326 }
5327 if (replaced) {
5328 MLimitedTruncate* limit = MLimitedTruncate::New(
5329 alloc, operand->getOperand(0), TruncateKind::NoTruncate);
5330 limit->setGuardUnchecked();
5331 block()->insertBefore(this, limit);
5332 return true;
5333 }
5334 }
5335
5336 // Optimize comparison against NaN.
5337 if (std::isnan(cte)) {
5338 switch (jsop_) {
5339 case JSOp::Lt:
5340 case JSOp::Le:
5341 case JSOp::Gt:
5342 case JSOp::Ge:
5343 case JSOp::Eq:
5344 case JSOp::StrictEq:
5345 *result = false;
5346 break;
5347 case JSOp::Ne:
5348 case JSOp::StrictNe:
5349 *result = true;
5350 break;
5351 default:
5352 MOZ_CRASH("Unexpected op.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected op."
, "./../../../../js/src/jit/MIR.cpp", 5352); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected op." ")"); do { MOZ_CrashSequence(__null
, 5352); __attribute__((nomerge)) ::abort(); } while (false);
} while (false)
;
5353 }
5354 return true;
5355 }
5356 }
5357
5358 if (!left->isConstant() || !right->isConstant()) {
5359 return false;
5360 }
5361
5362 MConstant* lhs = left->toConstant();
5363 MConstant* rhs = right->toConstant();
5364
5365 switch (compareType()) {
5366 case Compare_Int32:
5367 case Compare_Double:
5368 case Compare_Float32: {
5369 *result =
5370 FoldComparison(jsop_, lhs->numberToDouble(), rhs->numberToDouble());
5371 return true;
5372 }
5373 case Compare_UInt32: {
5374 *result = FoldComparison(jsop_, uint32_t(lhs->toInt32()),
5375 uint32_t(rhs->toInt32()));
5376 return true;
5377 }
5378 case Compare_Int64: {
5379 *result = FoldComparison(jsop_, lhs->toInt64(), rhs->toInt64());
5380 return true;
5381 }
5382 case Compare_UInt64: {
5383 *result = FoldComparison(jsop_, uint64_t(lhs->toInt64()),
5384 uint64_t(rhs->toInt64()));
5385 return true;
5386 }
5387 case Compare_IntPtr: {
5388 *result = FoldComparison(jsop_, lhs->toIntPtr(), rhs->toIntPtr());
5389 return true;
5390 }
5391 case Compare_UIntPtr: {
5392 *result = FoldComparison(jsop_, uintptr_t(lhs->toIntPtr()),
5393 uintptr_t(rhs->toIntPtr()));
5394 return true;
5395 }
5396 case Compare_String: {
5397 int32_t comp = CompareStrings(lhs->toString(), rhs->toString());
5398 *result = FoldComparison(jsop_, comp, 0);
5399 return true;
5400 }
5401 case Compare_BigInt: {
5402 int32_t comp = BigInt::compare(lhs->toBigInt(), rhs->toBigInt());
5403 *result = FoldComparison(jsop_, comp, 0);
5404 return true;
5405 }
5406 case Compare_BigInt_Int32:
5407 case Compare_BigInt_Double: {
5408 *result =
5409 FoldBigIntComparison(jsop_, lhs->toBigInt(), rhs->numberToDouble());
5410 return true;
5411 }
5412 case Compare_BigInt_String: {
5413 JSOffThreadAtom* str = rhs->toString();
5414 if (!str->hasIndexValue()) {
5415 return false;
5416 }
5417 *result =
5418 FoldBigIntComparison(jsop_, lhs->toBigInt(), str->getIndexValue());
5419 return true;
5420 }
5421
5422 case Compare_Undefined:
5423 case Compare_Null:
5424 case Compare_Symbol:
5425 case Compare_Object:
5426 case Compare_WasmAnyRef:
5427 return false;
5428 }
5429
5430 MOZ_CRASH("unexpected compare type")do { do { } while (false); MOZ_ReportCrash("" "unexpected compare type"
, "./../../../../js/src/jit/MIR.cpp", 5430); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected compare type" ")"); do { MOZ_CrashSequence
(__null, 5430); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
5431}
5432
5433MDefinition* MCompare::tryFoldTypeOf(TempAllocator& alloc) {
5434 auto typeOfCompare = IsTypeOfCompare(this);
5435 if (!typeOfCompare) {
5436 return this;
5437 }
5438 auto* typeOf = typeOfCompare->typeOf;
5439 JSType type = typeOfCompare->type;
5440
5441 auto* input = typeOf->input();
5442 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", 5443); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "input->type() == MIRType::Value || input->type() == MIRType::Object"
")"); do { MOZ_CrashSequence(__null, 5443); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
5443 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", 5443); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "input->type() == MIRType::Value || input->type() == MIRType::Object"
")"); do { MOZ_CrashSequence(__null, 5443); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5444
5445 // Constant typeof folding handles the other cases.
5446 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", 5448); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == JSTYPE_UNDEFINED || type == JSTYPE_OBJECT || type == JSTYPE_FUNCTION"
")"); do { MOZ_CrashSequence(__null, 5448); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
5447 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", 5448); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == JSTYPE_UNDEFINED || type == JSTYPE_OBJECT || type == JSTYPE_FUNCTION"
")"); do { MOZ_CrashSequence(__null, 5448); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
5448 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", 5448); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == JSTYPE_UNDEFINED || type == JSTYPE_OBJECT || type == JSTYPE_FUNCTION"
")"); do { MOZ_CrashSequence(__null, 5448); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5449
5450 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"
, 5450); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type != JSTYPE_LIMIT"
") (" "unknown typeof strings folded earlier" ")"); do { MOZ_CrashSequence
(__null, 5450); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5451
5452 // If there's only a single use, assume this |typeof| is used in a simple
5453 // comparison context.
5454 //
5455 // if (typeof thing === "number") { ... }
5456 //
5457 // It'll be compiled into something similar to:
5458 //
5459 // if (IsNumber(thing)) { ... }
5460 //
5461 // This heuristic can go wrong when repeated |typeof| are used in consecutive
5462 // if-statements.
5463 //
5464 // if (typeof thing === "number") { ... }
5465 // else if (typeof thing === "string") { ... }
5466 // ... repeated for all possible types
5467 //
5468 // In that case it'd more efficient to emit MTypeOf compared to MTypeOfIs. We
5469 // don't yet handle that case, because it'd require a separate optimization
5470 // pass to correctly detect it.
5471 if (typeOfCompare->typeOfSide->hasOneUse()) {
5472 return MTypeOfIs::New(alloc, input, jsop(), type);
5473 }
5474
5475 if (typeOfCompare->isIntComparison) {
5476 // Already optimized.
5477 return this;
5478 }
5479
5480 MConstant* cst = MConstant::NewInt32(alloc, type);
5481 block()->insertBefore(this, cst);
5482
5483 return MCompare::New(alloc, typeOf, cst, jsop(), MCompare::Compare_Int32);
5484}
5485
5486MDefinition* MCompare::tryFoldCharCompare(TempAllocator& alloc) {
5487 if (compareType() != Compare_String) {
5488 return this;
5489 }
5490
5491 MDefinition* left = lhs();
5492 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"
, 5492); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5492); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5493
5494 MDefinition* right = rhs();
5495 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"
, 5495); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5495); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5496
5497 // |str[i]| is compiled as |MFromCharCode(MCharCodeAt(str, i))|.
5498 // Out-of-bounds access is compiled as
5499 // |FromCharCodeEmptyIfNegative(CharCodeAtOrNegative(str, i))|.
5500 auto isCharAccess = [](MDefinition* ins) {
5501 if (ins->isFromCharCode()) {
5502 return ins->toFromCharCode()->code()->isCharCodeAt();
5503 }
5504 if (ins->isFromCharCodeEmptyIfNegative()) {
5505 auto* fromCharCode = ins->toFromCharCodeEmptyIfNegative();
5506 return fromCharCode->code()->isCharCodeAtOrNegative();
5507 }
5508 return false;
5509 };
5510
5511 auto charAccessCode = [](MDefinition* ins) {
5512 if (ins->isFromCharCode()) {
5513 return ins->toFromCharCode()->code();
5514 }
5515 return ins->toFromCharCodeEmptyIfNegative()->code();
5516 };
5517
5518 if (left->isConstant() || right->isConstant()) {
5519 // Try to optimize |MConstant(string) <compare> (MFromCharCode MCharCodeAt)|
5520 // as |MConstant(charcode) <compare> MCharCodeAt|.
5521 MConstant* constant;
5522 MDefinition* operand;
5523 if (left->isConstant()) {
5524 constant = left->toConstant();
5525 operand = right;
5526 } else {
5527 constant = right->toConstant();
5528 operand = left;
5529 }
5530
5531 if (constant->toString()->length() != 1 || !isCharAccess(operand)) {
5532 return this;
5533 }
5534
5535 char16_t charCode = constant->toString()->latin1OrTwoByteChar(0);
5536 MConstant* charCodeConst = MConstant::NewInt32(alloc, charCode);
5537 block()->insertBefore(this, charCodeConst);
5538
5539 MDefinition* charCodeAt = charAccessCode(operand);
5540
5541 if (left->isConstant()) {
5542 left = charCodeConst;
5543 right = charCodeAt;
5544 } else {
5545 left = charCodeAt;
5546 right = charCodeConst;
5547 }
5548 } else if (isCharAccess(left) && isCharAccess(right)) {
5549 // Try to optimize |(MFromCharCode MCharCodeAt) <compare> (MFromCharCode
5550 // MCharCodeAt)| as |MCharCodeAt <compare> MCharCodeAt|.
5551
5552 left = charAccessCode(left);
5553 right = charAccessCode(right);
5554 } else {
5555 return this;
5556 }
5557
5558 return MCompare::New(alloc, left, right, jsop(), MCompare::Compare_Int32);
5559}
5560
5561MDefinition* MCompare::tryFoldStringCompare(TempAllocator& alloc) {
5562 if (compareType() != Compare_String) {
5563 return this;
5564 }
5565
5566 MDefinition* left = lhs();
5567 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"
, 5567); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5567); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5568
5569 MDefinition* right = rhs();
5570 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"
, 5570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5570); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5571
5572 if (!left->isConstant() && !right->isConstant()) {
5573 return this;
5574 }
5575
5576 // Try to optimize |string <compare> MConstant("")| as |MStringLength(string)
5577 // <compare> MConstant(0)|.
5578
5579 MConstant* constant =
5580 left->isConstant() ? left->toConstant() : right->toConstant();
5581 if (!constant->toString()->empty()) {
5582 return this;
5583 }
5584
5585 MDefinition* operand = left->isConstant() ? right : left;
5586
5587 auto* strLength = MStringLength::New(alloc, operand);
5588 block()->insertBefore(this, strLength);
5589
5590 auto* zero = MConstant::NewInt32(alloc, 0);
5591 block()->insertBefore(this, zero);
5592
5593 if (left->isConstant()) {
5594 left = zero;
5595 right = strLength;
5596 } else {
5597 left = strLength;
5598 right = zero;
5599 }
5600
5601 return MCompare::New(alloc, left, right, jsop(), MCompare::Compare_Int32);
5602}
5603
5604MDefinition* MCompare::tryFoldStringSubstring(TempAllocator& alloc) {
5605 if (compareType() != Compare_String) {
5606 return this;
5607 }
5608 if (!IsEqualityOp(jsop())) {
5609 return this;
5610 }
5611
5612 auto* left = lhs();
5613 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"
, 5613); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5613); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5614
5615 auto* right = rhs();
5616 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"
, 5616); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 5616); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5617
5618 // One operand must be a constant string.
5619 if (!left->isConstant() && !right->isConstant()) {
5620 return this;
5621 }
5622
5623 // The constant string must be non-empty.
5624 auto* constant =
5625 left->isConstant() ? left->toConstant() : right->toConstant();
5626 if (constant->toString()->empty()) {
5627 return this;
5628 }
5629
5630 // The other operand must be a substring operation.
5631 auto* operand = left->isConstant() ? right : left;
5632 if (!operand->isSubstr()) {
5633 return this;
5634 }
5635 auto* substr = operand->toSubstr();
5636
5637 static_assert(JSString::MAX_LENGTH < INT32_MAX(2147483647),
5638 "string length can be casted to int32_t");
5639
5640 int32_t stringLength = int32_t(constant->toString()->length());
5641
5642 MInstruction* replacement;
5643 if (IsSubstrTo(substr, stringLength)) {
5644 // Fold |str.substring(0, 2) == "aa"| to |str.startsWith("aa")|.
5645 replacement = MStringStartsWith::New(alloc, substr->string(), constant);
5646 } else if (IsSubstrLast(substr, -stringLength)) {
5647 // Fold |str.slice(-2) == "aa"| to |str.endsWith("aa")|.
5648 replacement = MStringEndsWith::New(alloc, substr->string(), constant);
5649 } else {
5650 return this;
5651 }
5652
5653 if (jsop() == JSOp::Eq || jsop() == JSOp::StrictEq) {
5654 return replacement;
5655 }
5656
5657 // Invert for inequality.
5658 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", 5658); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "jsop() == JSOp::Ne || jsop() == JSOp::StrictNe"
")"); do { MOZ_CrashSequence(__null, 5658); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5659
5660 block()->insertBefore(this, replacement);
5661 return MNot::New(alloc, replacement);
5662}
5663
5664MDefinition* MCompare::tryFoldStringIndexOf(TempAllocator& alloc) {
5665 if (compareType() != Compare_Int32) {
5666 return this;
5667 }
5668 if (!IsEqualityOp(jsop())) {
5669 return this;
5670 }
5671
5672 auto* left = lhs();
5673 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"
, 5673); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 5673); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5674
5675 auto* right = rhs();
5676 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"
, 5676); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 5676); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5677
5678 // One operand must be a constant integer.
5679 if (!left->isConstant() && !right->isConstant()) {
5680 return this;
5681 }
5682
5683 // The constant must be zero.
5684 auto* constant =
5685 left->isConstant() ? left->toConstant() : right->toConstant();
5686 if (!constant->isInt32(0)) {
5687 return this;
5688 }
5689
5690 // The other operand must be an indexOf operation.
5691 auto* operand = left->isConstant() ? right : left;
5692 if (!operand->isStringIndexOf()) {
5693 return this;
5694 }
5695
5696 // Fold |str.indexOf(searchStr) == 0| to |str.startsWith(searchStr)|.
5697
5698 auto* indexOf = operand->toStringIndexOf();
5699 auto* startsWith =
5700 MStringStartsWith::New(alloc, indexOf->string(), indexOf->searchString());
5701 if (jsop() == JSOp::Eq || jsop() == JSOp::StrictEq) {
5702 return startsWith;
5703 }
5704
5705 // Invert for inequality.
5706 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", 5706); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "jsop() == JSOp::Ne || jsop() == JSOp::StrictNe"
")"); do { MOZ_CrashSequence(__null, 5706); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5707
5708 block()->insertBefore(this, startsWith);
5709 return MNot::New(alloc, startsWith);
5710}
5711
5712/**
5713 * Most architectures can generate smaller code for comparison against zero, so
5714 * the macro-assemblers special-case a zero immediate when emitting
5715 * compare-and-branch instructions.
5716 *
5717 * Some comparisons against one resp. negative one can instead be written as a
5718 * comparison against zero. Handle these cases here to avoid duplicating the
5719 * same code across all architectures.
5720 */
5721static bool CanCompareAgainstZero(int64_t value, JSOp op, bool isSigned) {
5722 switch (op) {
5723 case JSOp::Lt:
5724 case JSOp::Ge:
5725 // Can rewrite |operand < 1| as |operand <= 0|.
5726 // Can rewrite |operand >= 1| as |operand > 0|.
5727 return value == 1;
5728
5729 case JSOp::Le:
5730 case JSOp::Gt:
5731 // Can rewrite |operand <= -1| as |operand < 0|.
5732 // Can rewrite |operand > -1| as |operand >= 0|.
5733 return isSigned && value == -1;
5734
5735 default:
5736 return false;
5737 }
5738}
5739
5740MCompare* MCompare::newCompareInt(TempAllocator& alloc, MDefinition* operand,
5741 int64_t value, JSOp op, bool isSigned) {
5742 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", 5742); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsIntType(operand->type()) || operand->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 5742); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5743 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", 5743); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "isSigned" ")"); do { MOZ_CrashSequence(__null
, 5743); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false); } } while (false)
;
5744
5745 // Prefer comparison against zero if possible.
5746 if (CanCompareAgainstZero(value, op, isSigned)) {
5747 value = 0;
5748
5749 // Update operator: (Lt -> Le), (Le -> Lt), (Gt -> Ge), (Ge -> Gt).
5750 op = ReverseCompareOp(NegateCompareOp(op));
5751 }
5752
5753 MConstant* cst;
5754 CompareType compareType;
5755 switch (operand->type()) {
5756 case MIRType::Int32:
5757 cst = MConstant::NewInt32(alloc, mozilla::AssertedCast<int32_t>(value));
5758 compareType = isSigned ? Compare_Int32 : Compare_UInt32;
5759 break;
5760
5761 case MIRType::Int64:
5762 cst = MConstant::NewInt64(alloc, value);
5763 compareType = isSigned ? Compare_Int64 : Compare_UInt64;
5764 break;
5765
5766 case MIRType::IntPtr:
5767 cst = MConstant::NewIntPtr(alloc, mozilla::AssertedCast<intptr_t>(value));
5768 compareType = isSigned ? Compare_IntPtr : Compare_UIntPtr;
5769 break;
5770
5771 case MIRType::BigInt:
5772 cst = MConstant::NewInt32(alloc, mozilla::AssertedCast<int32_t>(value));
5773 compareType = Compare_BigInt_Int32;
5774 break;
5775
5776 default:
5777 MOZ_CRASH("unexpected operand type")do { do { } while (false); MOZ_ReportCrash("" "unexpected operand type"
, "./../../../../js/src/jit/MIR.cpp", 5777); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected operand type" ")"); do { MOZ_CrashSequence
(__null, 5777); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
5778 }
5779 block()->insertBefore(this, cst);
5780
5781 auto* ins = MCompare::New(alloc, operand, cst, op, compareType);
5782 ins->setResultType(type());
5783 return ins;
5784}
5785
5786MDefinition* MCompare::tryFoldBigInt64(TempAllocator& alloc) {
5787 if (compareType() == Compare_BigInt) {
5788 auto* left = lhs();
5789 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"
, 5789); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 5789); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5790
5791 auto* right = rhs();
5792 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"
, 5792); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 5792); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5793
5794 // At least one operand must be MInt64ToBigInt.
5795 if (!left->isInt64ToBigInt() && !right->isInt64ToBigInt()) {
5796 return this;
5797 }
5798
5799 // Unwrap MInt64ToBigInt on both sides and perform a Int64 comparison.
5800 if (left->isInt64ToBigInt() && right->isInt64ToBigInt()) {
5801 auto* lhsInt64 = left->toInt64ToBigInt();
5802 auto* rhsInt64 = right->toInt64ToBigInt();
5803
5804 // Don't optimize if Int64 against Uint64 comparison.
5805 if (lhsInt64->isSigned() != rhsInt64->isSigned()) {
5806 return this;
5807 }
5808
5809 bool isSigned = lhsInt64->isSigned();
5810 auto compareType =
5811 isSigned ? MCompare::Compare_Int64 : MCompare::Compare_UInt64;
5812 return MCompare::New(alloc, lhsInt64->input(), rhsInt64->input(), jsop_,
5813 compareType);
5814 }
5815
5816 // Optimize IntPtr x Int64 comparison to Int64 x Int64 comparison.
5817 if (left->isIntPtrToBigInt() || right->isIntPtrToBigInt()) {
5818 auto* int64ToBigInt = left->isInt64ToBigInt() ? left->toInt64ToBigInt()
5819 : right->toInt64ToBigInt();
5820
5821 // Can't optimize when comparing Uint64 against IntPtr.
5822 if (!int64ToBigInt->isSigned()) {
5823 return this;
5824 }
5825
5826 auto* intPtrToBigInt = left->isIntPtrToBigInt()
5827 ? left->toIntPtrToBigInt()
5828 : right->toIntPtrToBigInt();
5829
5830 auto* intPtrToInt64 = MIntPtrToInt64::New(alloc, intPtrToBigInt->input());
5831 block()->insertBefore(this, intPtrToInt64);
5832
5833 if (left == int64ToBigInt) {
5834 left = int64ToBigInt->input();
5835 right = intPtrToInt64;
5836 } else {
5837 left = intPtrToInt64;
5838 right = int64ToBigInt->input();
5839 }
5840 return MCompare::New(alloc, left, right, jsop_, MCompare::Compare_Int64);
5841 }
5842
5843 // The other operand must be a constant.
5844 if (!left->isConstant() && !right->isConstant()) {
5845 return this;
5846 }
5847
5848 auto* int64ToBigInt = left->isInt64ToBigInt() ? left->toInt64ToBigInt()
5849 : right->toInt64ToBigInt();
5850 bool isSigned = int64ToBigInt->isSigned();
5851
5852 auto* constant =
5853 left->isConstant() ? left->toConstant() : right->toConstant();
5854 auto* bigInt = constant->toBigInt();
5855
5856 // Extract the BigInt value if representable as Int64/Uint64.
5857 mozilla::Maybe<int64_t> value;
5858 if (isSigned) {
5859 int64_t x;
5860 if (BigInt::isInt64(bigInt, &x)) {
5861 value = mozilla::Some(x);
5862 }
5863 } else {
5864 uint64_t x;
5865 if (BigInt::isUint64(bigInt, &x)) {
5866 value = mozilla::Some(static_cast<int64_t>(x));
5867 }
5868 }
5869
5870 // The comparison is a constant if the BigInt has too many digits.
5871 if (!value) {
5872 int32_t repr = bigInt->isNegative() ? -1 : 1;
5873
5874 bool result;
5875 if (left == int64ToBigInt) {
5876 result = FoldComparison(jsop_, 0, repr);
5877 } else {
5878 result = FoldComparison(jsop_, repr, 0);
5879 }
5880 return MConstant::NewBoolean(alloc, result);
5881 }
5882
5883 JSOp op = jsop();
5884 if (right == int64ToBigInt) {
5885 op = ReverseCompareOp(op);
5886 }
5887 return newCompareInt(alloc, int64ToBigInt->input(), *value, op, isSigned);
5888 }
5889
5890 if (compareType() == Compare_BigInt_Int32) {
5891 auto* left = lhs();
5892 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"
, 5892); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 5892); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5893
5894 auto* right = rhs();
5895 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"
, 5895); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 5895); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5896
5897 // Optimize MInt64ToBigInt against a constant int32.
5898 if (!left->isInt64ToBigInt() || !right->isConstant()) {
5899 return this;
5900 }
5901
5902 auto* int64ToBigInt = left->toInt64ToBigInt();
5903 bool isSigned = int64ToBigInt->isSigned();
5904
5905 int32_t constInt32 = right->toConstant()->toInt32();
5906
5907 // The unsigned comparison against a negative operand is a constant.
5908 if (!isSigned && constInt32 < 0) {
5909 bool result = FoldComparison(jsop_, 0, constInt32);
5910 return MConstant::NewBoolean(alloc, result);
5911 }
5912
5913 return newCompareInt(alloc, int64ToBigInt->input(), constInt32, jsop(),
5914 isSigned);
5915 }
5916
5917 return this;
5918}
5919
5920MDefinition* MCompare::tryFoldBigIntPtr(TempAllocator& alloc) {
5921 if (compareType() == Compare_BigInt) {
5922 auto* left = lhs();
5923 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"
, 5923); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 5923); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5924
5925 auto* right = rhs();
5926 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"
, 5926); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 5926); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5927
5928 // At least one operand must be MIntPtrToBigInt.
5929 if (!left->isIntPtrToBigInt() && !right->isIntPtrToBigInt()) {
5930 return this;
5931 }
5932
5933 // Unwrap MIntPtrToBigInt on both sides and perform an IntPtr comparison.
5934 if (left->isIntPtrToBigInt() && right->isIntPtrToBigInt()) {
5935 auto* lhsIntPtr = left->toIntPtrToBigInt();
5936 auto* rhsIntPtr = right->toIntPtrToBigInt();
5937
5938 return MCompare::New(alloc, lhsIntPtr->input(), rhsIntPtr->input(), jsop_,
5939 MCompare::Compare_IntPtr);
5940 }
5941
5942 // The other operand must be a constant.
5943 if (!left->isConstant() && !right->isConstant()) {
5944 return this;
5945 }
5946
5947 auto* intPtrToBigInt = left->isIntPtrToBigInt() ? left->toIntPtrToBigInt()
5948 : right->toIntPtrToBigInt();
5949
5950 auto* constant =
5951 left->isConstant() ? left->toConstant() : right->toConstant();
5952 auto* bigInt = constant->toBigInt();
5953
5954 // Extract the BigInt value if representable as intptr_t.
5955 intptr_t value;
5956 if (!BigInt::isIntPtr(bigInt, &value)) {
5957 // The comparison is a constant if the BigInt has too many digits.
5958 int32_t repr = bigInt->isNegative() ? -1 : 1;
5959
5960 bool result;
5961 if (left == intPtrToBigInt) {
5962 result = FoldComparison(jsop_, 0, repr);
5963 } else {
5964 result = FoldComparison(jsop_, repr, 0);
5965 }
5966 return MConstant::NewBoolean(alloc, result);
5967 }
5968
5969 JSOp op = jsop();
5970 if (right == intPtrToBigInt) {
5971 op = ReverseCompareOp(op);
5972 }
5973 return newCompareInt(alloc, intPtrToBigInt->input(), value, op);
5974 }
5975
5976 if (compareType() == Compare_BigInt_Int32) {
5977 auto* left = lhs();
5978 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"
, 5978); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 5978); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5979
5980 auto* right = rhs();
5981 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"
, 5981); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 5981); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5982
5983 // Optimize MIntPtrToBigInt against a constant int32.
5984 if (!left->isIntPtrToBigInt() || !right->isConstant()) {
5985 return this;
5986 }
5987
5988 return newCompareInt(alloc, left->toIntPtrToBigInt()->input(),
5989 right->toConstant()->toInt32(), jsop());
5990 }
5991
5992 return this;
5993}
5994
5995MDefinition* MCompare::tryFoldBigInt(TempAllocator& alloc) {
5996 if (compareType() != Compare_BigInt) {
5997 return this;
5998 }
5999
6000 auto* left = lhs();
6001 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"
, 6001); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 6001); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6002
6003 auto* right = rhs();
6004 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"
, 6004); AnnotateMozCrashReason("MOZ_ASSERT" "(" "right->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 6004); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6005
6006 // One operand must be a constant.
6007 if (!left->isConstant() && !right->isConstant()) {
6008 return this;
6009 }
6010
6011 auto* constant =
6012 left->isConstant() ? left->toConstant() : right->toConstant();
6013 auto* operand = left->isConstant() ? right : left;
6014
6015 // The constant must be representable as an Int32.
6016 int32_t x;
6017 if (!BigInt::isInt32(constant->toBigInt(), &x)) {
6018 return this;
6019 }
6020
6021 auto op = jsop();
6022 if (IsStrictEqualityOp(op)) {
6023 // Compare_BigInt_Int32 is only valid for loose comparison.
6024 op = op == JSOp::StrictEq ? JSOp::Eq : JSOp::Ne;
6025 } else if (operand == right) {
6026 // Reverse the comparison operator if the operands were reordered.
6027 op = ReverseCompareOp(op);
6028 }
6029 return newCompareInt(alloc, operand, x, op);
6030}
6031
6032MDefinition* MCompare::tryFoldIntZero(TempAllocator& alloc) {
6033 // Expect signed or unsigned integer relational comparison.
6034 if (!IsRelationalOp(jsop())) {
6035 return this;
6036 }
6037
6038 bool isSigned;
6039 switch (compareType()) {
6040 case Compare_Int32:
6041 case Compare_Int64:
6042 case Compare_IntPtr:
6043 isSigned = true;
6044 break;
6045
6046 case Compare_UInt32:
6047 case Compare_UInt64:
6048 case Compare_UIntPtr:
6049 isSigned = false;
6050 break;
6051
6052 case Compare_Undefined:
6053 case Compare_Null:
6054 case Compare_Double:
6055 case Compare_Float32:
6056 case Compare_String:
6057 case Compare_Symbol:
6058 case Compare_Object:
6059 case Compare_BigInt:
6060 case Compare_BigInt_Int32:
6061 case Compare_BigInt_Double:
6062 case Compare_BigInt_String:
6063 case Compare_WasmAnyRef:
6064 return this;
6065 }
6066
6067 auto* left = lhs();
6068 auto* right = rhs();
6069
6070 // Both operands have the same Int type.
6071 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"
, 6071); AnnotateMozCrashReason("MOZ_ASSERT" "(" "left->type() == right->type()"
")"); do { MOZ_CrashSequence(__null, 6071); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6072 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", 6072); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsIntType(left->type())" ")"); do { MOZ_CrashSequence
(__null, 6072); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6073
6074 // One operand must be a constant.
6075 if (!left->isConstant() && !right->isConstant()) {
6076 return this;
6077 }
6078
6079 auto* constant =
6080 left->isConstant() ? left->toConstant() : right->toConstant();
6081 auto* operand = left->isConstant() ? right : left;
6082
6083 int64_t value;
6084 switch (constant->type()) {
6085 case MIRType::Int32:
6086 value = constant->toInt32();
6087 break;
6088
6089 case MIRType::Int64:
6090 value = constant->toInt64();
6091 break;
6092
6093 case MIRType::IntPtr:
6094 value = constant->toIntPtr();
6095 break;
6096
6097 default:
6098 MOZ_CRASH("unexpected int type")do { do { } while (false); MOZ_ReportCrash("" "unexpected int type"
, "./../../../../js/src/jit/MIR.cpp", 6098); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected int type" ")"); do { MOZ_CrashSequence
(__null, 6098); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
6099 }
6100
6101 auto op = jsop();
6102 if (operand == right) {
6103 op = ReverseCompareOp(op);
6104 }
6105
6106 if (!CanCompareAgainstZero(value, op, isSigned)) {
6107 return this;
6108 }
6109 return newCompareInt(alloc, operand, value, op, isSigned);
6110}
6111
6112MDefinition* MCompare::foldsTo(TempAllocator& alloc) {
6113 bool result;
6114
6115 if (tryFold(&result) || evaluateConstantOperands(alloc, &result)) {
6116 if (type() == MIRType::Int32) {
6117 return MConstant::NewInt32(alloc, result);
6118 }
6119
6120 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", 6120); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type() == MIRType::Boolean" ")"); do { MOZ_CrashSequence
(__null, 6120); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6121 return MConstant::NewBoolean(alloc, result);
6122 }
6123
6124 if (MDefinition* folded = tryFoldTypeOf(alloc); folded != this) {
6125 return folded;
6126 }
6127
6128 if (MDefinition* folded = tryFoldCharCompare(alloc); folded != this) {
6129 return folded;
6130 }
6131
6132 if (MDefinition* folded = tryFoldStringCompare(alloc); folded != this) {
6133 return folded;
6134 }
6135
6136 if (MDefinition* folded = tryFoldStringSubstring(alloc); folded != this) {
6137 return folded;
6138 }
6139
6140 if (MDefinition* folded = tryFoldStringIndexOf(alloc); folded != this) {
6141 return folded;
6142 }
6143
6144 if (MDefinition* folded = tryFoldBigInt64(alloc); folded != this) {
6145 return folded;
6146 }
6147
6148 if (MDefinition* folded = tryFoldBigIntPtr(alloc); folded != this) {
6149 return folded;
6150 }
6151
6152 if (MDefinition* folded = tryFoldBigInt(alloc); folded != this) {
6153 return folded;
6154 }
6155
6156 if (MDefinition* folded = tryFoldIntZero(alloc); folded != this) {
6157 return folded;
6158 }
6159
6160 return this;
6161}
6162
6163void MCompare::trySpecializeFloat32(TempAllocator& alloc) {
6164 if (AllOperandsCanProduceFloat32(this) && compareType_ == Compare_Double) {
6165 compareType_ = Compare_Float32;
6166 } else {
6167 ConvertOperandsToDouble(this, alloc);
6168 }
6169}
6170
6171MDefinition* MStrictConstantCompareInt32::foldsTo(TempAllocator& alloc) {
6172 if (!value()->isBox()) {
6173 return this;
6174 }
6175 MDefinition* unboxed = value()->toBox()->input();
6176
6177 if (unboxed->type() == MIRType::Int32) {
6178 if (unboxed->isConstant()) {
6179 bool result =
6180 FoldComparison(jsop(), unboxed->toConstant()->toInt32(), constant());
6181 return MConstant::NewBoolean(alloc, result);
6182 }
6183
6184 auto* cst = MConstant::NewInt32(alloc, constant());
6185 block()->insertBefore(this, cst);
6186
6187 return MCompare::New(alloc, unboxed, cst, jsop(), MCompare::Compare_Int32);
6188 }
6189
6190 if (unboxed->type() == MIRType::Double) {
6191 if (unboxed->isConstant()) {
6192 bool result = FoldComparison(jsop(), unboxed->toConstant()->toDouble(),
6193 double(constant()));
6194 return MConstant::NewBoolean(alloc, result);
6195 }
6196
6197 auto* cst = MConstant::NewDouble(alloc, constant());
6198 block()->insertBefore(this, cst);
6199
6200 return MCompare::New(alloc, unboxed, cst, jsop(), MCompare::Compare_Double);
6201 }
6202
6203 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"
, 6203); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsNumberType(unboxed->type())"
")"); do { MOZ_CrashSequence(__null, 6203); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6204 return MConstant::NewBoolean(alloc, jsop() == JSOp::StrictNe);
6205}
6206
6207MDefinition* MStrictConstantCompareBoolean::foldsTo(TempAllocator& alloc) {
6208 if (!value()->isBox()) {
6209 return this;
6210 }
6211 MDefinition* unboxed = value()->toBox()->input();
6212
6213 if (unboxed->type() == MIRType::Boolean) {
6214 if (unboxed->isConstant()) {
6215 bool result = (jsop() == JSOp::StrictEq) ==
6216 (unboxed->toConstant()->toBoolean() == constant());
6217 return MConstant::NewBoolean(alloc, result);
6218 }
6219
6220 auto* inputI32 = MBooleanToInt32::New(alloc, unboxed);
6221 block()->insertBefore(this, inputI32);
6222
6223 auto* cst = MConstant::NewInt32(alloc, int32_t(constant()));
6224 block()->insertBefore(this, cst);
6225
6226 return MCompare::New(alloc, inputI32, cst, jsop(), MCompare::Compare_Int32);
6227 }
6228
6229 return MConstant::NewBoolean(alloc, jsop() == JSOp::StrictNe);
6230}
6231
6232MDefinition* MStrictConstantCompareString::foldsTo(TempAllocator& alloc) {
6233 if (!value()->isBox()) {
6234 return this;
6235 }
6236 MDefinition* unboxed = value()->toBox()->input();
6237
6238 if (unboxed->type() == MIRType::String) {
6239 if (unboxed->isConstant()) {
6240 int32_t comp =
6241 CompareStrings(unboxed->toConstant()->toString(), constant());
6242 bool result = FoldComparison(jsop(), comp, 0);
6243 return MConstant::NewBoolean(alloc, result);
6244 }
6245
6246 auto* cst = MConstant::NewString(alloc, constant()->unwrap());
6247 block()->insertBefore(this, cst);
6248
6249 return MCompare::New(alloc, unboxed, cst, jsop(), MCompare::Compare_String);
6250 }
6251
6252 return MConstant::NewBoolean(alloc, jsop() == JSOp::StrictNe);
6253}
6254
6255MDefinition* MSameValue::foldsTo(TempAllocator& alloc) {
6256 MDefinition* lhs = left();
6257 if (lhs->isBox()) {
6258 lhs = lhs->toBox()->input();
6259 }
6260
6261 MDefinition* rhs = right();
6262 if (rhs->isBox()) {
6263 rhs = rhs->toBox()->input();
6264 }
6265
6266 // Trivially true if both operands are the same.
6267 if (lhs == rhs) {
6268 return MConstant::NewBoolean(alloc, true);
6269 }
6270
6271 // CacheIR optimizes the following cases, so don't bother to handle them here:
6272 // 1. Both inputs are numbers (int32 or double).
6273 // 2. Both inputs are strictly different types.
6274 // 3. Both inputs are the same type.
6275
6276 // Optimize when one operand is guaranteed to be |null|.
6277 if (lhs->type() == MIRType::Null || rhs->type() == MIRType::Null) {
6278 // The `null` value must be the right-hand side operand.
6279 auto* input = lhs->type() == MIRType::Null ? rhs : lhs;
6280 auto* cst = lhs->type() == MIRType::Null ? lhs : rhs;
6281 return MCompare::New(alloc, input, cst, JSOp::StrictEq,
6282 MCompare::Compare_Null);
6283 }
6284
6285 // Optimize when one operand is guaranteed to be |undefined|.
6286 if (lhs->type() == MIRType::Undefined || rhs->type() == MIRType::Undefined) {
6287 // The `undefined` value must be the right-hand side operand.
6288 auto* input = lhs->type() == MIRType::Undefined ? rhs : lhs;
6289 auto* cst = lhs->type() == MIRType::Undefined ? lhs : rhs;
6290 return MCompare::New(alloc, input, cst, JSOp::StrictEq,
6291 MCompare::Compare_Undefined);
6292 }
6293
6294 return this;
6295}
6296
6297MDefinition* MSameValueDouble::foldsTo(TempAllocator& alloc) {
6298 // Trivially true if both operands are the same.
6299 if (left() == right()) {
6300 return MConstant::NewBoolean(alloc, true);
6301 }
6302
6303 // At least one operand must be a constant.
6304 if (!left()->isConstant() && !right()->isConstant()) {
6305 return this;
6306 }
6307
6308 auto* input = left()->isConstant() ? right() : left();
6309 auto* cst = left()->isConstant() ? left() : right();
6310 double dbl = cst->toConstant()->toDouble();
6311
6312 // Use bitwise comparison for +/-0.
6313 if (dbl == 0.0) {
6314 auto* reinterp = MReinterpretCast::New(alloc, input, MIRType::Int64);
6315 block()->insertBefore(this, reinterp);
6316
6317 auto* zeroBitsCst =
6318 MConstant::NewInt64(alloc, mozilla::BitwiseCast<int64_t>(dbl));
6319 block()->insertBefore(this, zeroBitsCst);
6320
6321 return MCompare::New(alloc, reinterp, zeroBitsCst, JSOp::StrictEq,
6322 MCompare::Compare_Int64);
6323 }
6324
6325 // Fold `Object.is(d, NaN)` to `d !== d`.
6326 if (std::isnan(dbl)) {
6327 return MCompare::New(alloc, input, input, JSOp::StrictNe,
6328 MCompare::Compare_Double);
6329 }
6330
6331 // Otherwise fold to MCompare.
6332 return MCompare::New(alloc, left(), right(), JSOp::StrictEq,
6333 MCompare::Compare_Double);
6334}
6335
6336MDefinition* MNot::foldsTo(TempAllocator& alloc) {
6337 auto foldConstant = [&alloc](MDefinition* input, MIRType type) -> MConstant* {
6338 MConstant* inputConst = input->maybeConstantValue();
6339 if (!inputConst) {
6340 return nullptr;
6341 }
6342 bool b;
6343 if (!inputConst->valueToBoolean(&b)) {
6344 return nullptr;
6345 }
6346 if (type == MIRType::Int32) {
6347 return MConstant::NewInt32(alloc, !b);
6348 }
6349 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", 6349); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::Boolean" ")"); do { MOZ_CrashSequence
(__null, 6349); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6350 return MConstant::NewBoolean(alloc, !b);
6351 };
6352
6353 // Fold if the input is constant.
6354 if (MConstant* folded = foldConstant(input(), type())) {
6355 return folded;
6356 }
6357
6358 // If the operand of the Not is itself a Not, they cancel out. But we can't
6359 // always convert Not(Not(x)) to x because that may loose the conversion to
6360 // boolean. We can simplify Not(Not(Not(x))) to Not(x) though.
6361 MDefinition* op = getOperand(0);
6362 if (op->isNot()) {
6363 MDefinition* opop = op->getOperand(0);
6364 if (opop->isNot()) {
6365 return opop;
6366 }
6367 }
6368
6369 // Not of an undefined or null value is always true
6370 if (input()->type() == MIRType::Undefined ||
6371 input()->type() == MIRType::Null) {
6372 return MConstant::NewBoolean(alloc, true);
6373 }
6374
6375 // Not of a symbol is always false.
6376 if (input()->type() == MIRType::Symbol) {
6377 return MConstant::NewBoolean(alloc, false);
6378 }
6379
6380 // Drop the conversion in `Not(Int64ToBigInt(int64))` to `Not(int64)`.
6381 if (input()->isInt64ToBigInt()) {
6382 MDefinition* int64 = input()->toInt64ToBigInt()->input();
6383 if (MConstant* folded = foldConstant(int64, type())) {
6384 return folded;
6385 }
6386 return MNot::New(alloc, int64);
6387 }
6388
6389 // Drop the conversion in `Not(IntPtrToBigInt(intptr))` to `Not(intptr)`.
6390 if (input()->isIntPtrToBigInt()) {
6391 MDefinition* intPtr = input()->toIntPtrToBigInt()->input();
6392 if (MConstant* folded = foldConstant(intPtr, type())) {
6393 return folded;
6394 }
6395 return MNot::New(alloc, intPtr);
6396 }
6397
6398 return this;
6399}
6400
6401void MNot::trySpecializeFloat32(TempAllocator& alloc) {
6402 (void)EnsureFloatInputOrConvert(this, alloc);
6403}
6404
6405#ifdef JS_JITSPEW1
6406void MBeta::printOpcode(GenericPrinter& out) const {
6407 MDefinition::printOpcode(out);
6408
6409 out.printf(" ");
6410 comparison_->dump(out);
6411}
6412#endif
6413
6414MObjectState::MObjectState(MObjectState* state)
6415 : MVariadicInstruction(classOpcode),
6416 numSlots_(state->numSlots_),
6417 numFixedSlots_(state->numFixedSlots_) {
6418 // This instruction is only used as a summary for bailout paths.
6419 setResultType(MIRType::Object);
6420 setRecoveredOnBailout();
6421}
6422
6423MObjectState::MObjectState(JSObject* templateObject)
6424 : MObjectState(templateObject->as<NativeObject>().shape()) {}
6425
6426MObjectState::MObjectState(const Shape* shape)
6427 : MVariadicInstruction(classOpcode) {
6428 // This instruction is only used as a summary for bailout paths.
6429 setResultType(MIRType::Object);
6430 setRecoveredOnBailout();
6431
6432 numSlots_ = shape->asShared().slotSpan();
6433 numFixedSlots_ = shape->asShared().numFixedSlots();
6434}
6435
6436/* static */
6437JSObject* MObjectState::templateObjectOf(MDefinition* obj) {
6438 // MNewPlainObject uses a shape constant, not an object.
6439 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", 6439); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!obj->isNewPlainObject()" ")"); do { MOZ_CrashSequence
(__null, 6439); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6440
6441 if (obj->isNewObject()) {
6442 return obj->toNewObject()->templateObject();
6443 } else if (obj->isNewCallObject()) {
6444 return obj->toNewCallObject()->templateObject();
6445 } else if (obj->isNewIterator()) {
6446 return obj->toNewIterator()->templateObject();
6447 } else if (obj->isNewBoundFunction()) {
6448 return obj->toNewBoundFunction()->templateObj();
6449 }
6450
6451 MOZ_CRASH("unreachable")do { do { } while (false); MOZ_ReportCrash("" "unreachable", "./../../../../js/src/jit/MIR.cpp"
, 6451); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable" ")"
); do { MOZ_CrashSequence(__null, 6451); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
6452}
6453
6454bool MObjectState::init(TempAllocator& alloc, MDefinition* obj) {
6455 if (!MVariadicInstruction::init(alloc, numSlots() + 1)) {
6456 return false;
6457 }
6458 // +1, for the Object.
6459 initOperand(0, obj);
6460 return true;
6461}
6462
6463void MObjectState::initFromTemplateObject(TempAllocator& alloc,
6464 MDefinition* undefinedVal) {
6465 if (object()->isNewPlainObject()) {
6466 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", 6467); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "object()->toNewPlainObject()->shape()->asShared().slotSpan() == numSlots()"
")"); do { MOZ_CrashSequence(__null, 6467); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6467 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", 6467); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "object()->toNewPlainObject()->shape()->asShared().slotSpan() == numSlots()"
")"); do { MOZ_CrashSequence(__null, 6467); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6468 for (size_t i = 0; i < numSlots(); i++) {
6469 initSlot(i, undefinedVal);
6470 }
6471 return;
6472 }
6473
6474 JSObject* templateObject = templateObjectOf(object());
6475
6476 // Initialize all the slots of the object state with the value contained in
6477 // the template object. This is needed to account values which are baked in
6478 // the template objects and not visible in IonMonkey, such as the
6479 // uninitialized-lexical magic value of call objects.
6480
6481 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", 6481); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "templateObject->is<NativeObject>()"
")"); do { MOZ_CrashSequence(__null, 6481); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6482 NativeObject& nativeObject = templateObject->as<NativeObject>();
6483 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", 6483); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nativeObject.slotSpan() == numSlots()" ")"
); do { MOZ_CrashSequence(__null, 6483); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6484
6485 for (size_t i = 0; i < numSlots(); i++) {
6486 Value val = nativeObject.getSlot(i);
6487 MDefinition* def = undefinedVal;
6488 if (!val.isUndefined()) {
6489 MConstant* ins = MConstant::New(alloc, val);
6490 block()->insertBefore(this, ins);
6491 def = ins;
6492 }
6493 initSlot(i, def);
6494 }
6495}
6496
6497MObjectState* MObjectState::New(TempAllocator& alloc, MDefinition* obj) {
6498 MObjectState* res;
6499 if (obj->isNewPlainObject()) {
6500 const Shape* shape = obj->toNewPlainObject()->shape();
6501 res = new (alloc) MObjectState(shape);
6502 } else {
6503 JSObject* templateObject = templateObjectOf(obj);
6504 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"
, 6504); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateObject"
") (" "Unexpected object creation." ")"); do { MOZ_CrashSequence
(__null, 6504); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6505 res = new (alloc) MObjectState(templateObject);
6506 }
6507
6508 if (!res || !res->init(alloc, obj)) {
6509 return nullptr;
6510 }
6511 return res;
6512}
6513
6514MObjectState* MObjectState::Copy(TempAllocator& alloc, MObjectState* state) {
6515 MObjectState* res = new (alloc) MObjectState(state);
6516 if (!res || !res->init(alloc, state->object())) {
6517 return nullptr;
6518 }
6519 for (size_t i = 0; i < res->numSlots(); i++) {
6520 res->initSlot(i, state->getSlot(i));
6521 }
6522 return res;
6523}
6524
6525MArrayState::MArrayState(MDefinition* arr) : MVariadicInstruction(classOpcode) {
6526 // This instruction is only used as a summary for bailout paths.
6527 setResultType(MIRType::Object);
6528 setRecoveredOnBailout();
6529 if (arr->isNewArrayObject()) {
6530 numElements_ = arr->toNewArrayObject()->length();
6531 } else {
6532 numElements_ = arr->toNewArray()->length();
6533 }
6534}
6535
6536bool MArrayState::init(TempAllocator& alloc, MDefinition* obj,
6537 MDefinition* len) {
6538 if (!MVariadicInstruction::init(alloc, numElements() + 2)) {
6539 return false;
6540 }
6541 // +1, for the Array object.
6542 initOperand(0, obj);
6543 // +1, for the length value of the array.
6544 initOperand(1, len);
6545 return true;
6546}
6547
6548void MArrayState::initFromTemplateObject(TempAllocator& alloc,
6549 MDefinition* undefinedVal) {
6550 for (size_t i = 0; i < numElements(); i++) {
6551 initElement(i, undefinedVal);
6552 }
6553}
6554
6555MArrayState* MArrayState::New(TempAllocator& alloc, MDefinition* arr,
6556 MDefinition* initLength) {
6557 MArrayState* res = new (alloc) MArrayState(arr);
6558 if (!res || !res->init(alloc, arr, initLength)) {
6559 return nullptr;
6560 }
6561 return res;
6562}
6563
6564MArrayState* MArrayState::Copy(TempAllocator& alloc, MArrayState* state) {
6565 MDefinition* arr = state->array();
6566 MDefinition* len = state->initializedLength();
6567 MArrayState* res = new (alloc) MArrayState(arr);
6568 if (!res || !res->init(alloc, arr, len)) {
6569 return nullptr;
6570 }
6571 for (size_t i = 0; i < res->numElements(); i++) {
6572 res->initElement(i, state->getElement(i));
6573 }
6574 return res;
6575}
6576
6577MNewArray::MNewArray(uint32_t length, MConstant* templateConst,
6578 gc::Heap initialHeap, bool vmCall)
6579 : MUnaryInstruction(classOpcode, templateConst),
6580 length_(length),
6581 initialHeap_(initialHeap),
6582 vmCall_(vmCall) {
6583 setResultType(MIRType::Object);
6584}
6585
6586MDefinition::AliasType MLoadFixedSlot::mightAlias(
6587 const MDefinition* def) const {
6588 if (def->isStoreFixedSlot()) {
6589 const MStoreFixedSlot* store = def->toStoreFixedSlot();
6590 if (store->slot() != slot()) {
6591 return AliasType::NoAlias;
6592 }
6593 if (store->object() != object()) {
6594 return AliasType::MayAlias;
6595 }
6596 return AliasType::MustAlias;
6597 }
6598 return AliasType::MayAlias;
6599}
6600
6601HashNumber MLoadFixedSlot::valueHash() const {
6602 HashNumber hash = MUnaryInstruction::valueHash();
6603 hash = addU32ToHash(hash, slot());
6604 return hash;
6605}
6606
6607MDefinition* MLoadFixedSlot::foldsTo(TempAllocator& alloc) {
6608 if (MDefinition* def = foldsToStore(alloc)) {
6609 return def;
6610 }
6611
6612 return this;
6613}
6614
6615MDefinition::AliasType MLoadFixedSlotAndUnbox::mightAlias(
6616 const MDefinition* def) const {
6617 if (def->isStoreFixedSlot()) {
6618 const MStoreFixedSlot* store = def->toStoreFixedSlot();
6619 if (store->slot() != slot()) {
6620 return AliasType::NoAlias;
6621 }
6622 if (store->object() != object()) {
6623 return AliasType::MayAlias;
6624 }
6625 return AliasType::MustAlias;
6626 }
6627 return AliasType::MayAlias;
6628}
6629
6630MDefinition* MLoadFixedSlotAndUnbox::foldsTo(TempAllocator& alloc) {
6631 if (MDefinition* def = foldsToStore(alloc)) {
6632 return def;
6633 }
6634
6635 return this;
6636}
6637
6638MDefinition::AliasType MLoadDynamicSlot::mightAlias(
6639 const MDefinition* def) const {
6640 if (def->isStoreDynamicSlot()) {
6641 const MStoreDynamicSlot* store = def->toStoreDynamicSlot();
6642 if (store->slot() != slot()) {
6643 return AliasType::NoAlias;
6644 }
6645
6646 if (store->slots() != slots()) {
6647 return AliasType::MayAlias;
6648 }
6649
6650 return AliasType::MustAlias;
6651 }
6652 return AliasType::MayAlias;
6653}
6654
6655HashNumber MLoadDynamicSlot::valueHash() const {
6656 HashNumber hash = MUnaryInstruction::valueHash();
6657 hash = addU32ToHash(hash, slot_);
6658 return hash;
6659}
6660
6661MDefinition* MLoadDynamicSlot::foldsTo(TempAllocator& alloc) {
6662 if (MDefinition* def = foldsToStore(alloc)) {
6663 return def;
6664 }
6665
6666 return this;
6667}
6668
6669#ifdef JS_JITSPEW1
6670void MLoadDynamicSlot::printOpcode(GenericPrinter& out) const {
6671 MDefinition::printOpcode(out);
6672 out.printf(" (slot %u)", slot());
6673}
6674
6675void MLoadDynamicSlotAndUnbox::printOpcode(GenericPrinter& out) const {
6676 MDefinition::printOpcode(out);
6677 out.printf(" (slot %zu)", slot());
6678}
6679
6680void MStoreDynamicSlot::printOpcode(GenericPrinter& out) const {
6681 MDefinition::printOpcode(out);
6682 out.printf(" (slot %u)", slot());
6683}
6684
6685void MLoadFixedSlot::printOpcode(GenericPrinter& out) const {
6686 MDefinition::printOpcode(out);
6687 out.printf(" (slot %zu)", slot());
6688}
6689
6690void MLoadFixedSlotAndUnbox::printOpcode(GenericPrinter& out) const {
6691 MDefinition::printOpcode(out);
6692 out.printf(" (slot %zu)", slot());
6693}
6694
6695void MStoreFixedSlot::printOpcode(GenericPrinter& out) const {
6696 MDefinition::printOpcode(out);
6697 out.printf(" (slot %zu)", slot());
6698}
6699#endif
6700
6701MDefinition* MGuardFunctionScript::foldsTo(TempAllocator& alloc) {
6702 MDefinition* in = input();
6703 if (in->isLambda() &&
6704 in->toLambda()->templateFunction()->baseScript() == expected()) {
6705 return in;
6706 }
6707 return this;
6708}
6709
6710MDefinition* MFunctionEnvironment::foldsTo(TempAllocator& alloc) {
6711 if (input()->isLambda()) {
6712 return input()->toLambda()->environmentChain();
6713 }
6714 if (input()->isFunctionWithProto()) {
6715 return input()->toFunctionWithProto()->environmentChain();
6716 }
6717 return this;
6718}
6719
6720static bool AddIsANonZeroAdditionOf(MAdd* add, MDefinition* ins) {
6721 if (add->type() != MIRType::Int32 && add->type() != MIRType::Double) {
6722 return false;
6723 }
6724
6725 if (add->lhs() != ins && add->rhs() != ins) {
6726 return false;
6727 }
6728 MDefinition* other = (add->lhs() == ins) ? add->rhs() : add->lhs();
6729 if (!IsTypeRepresentableAsDouble(other->type())) {
6730 return false;
6731 }
6732 if (!other->isConstant()) {
6733 return false;
6734 }
6735 // The constant must be representable as a non-zero int32. Other doubles may
6736 // leave the index unchanged after conversion.
6737 int32_t n;
6738 if (!mozilla::NumberIsInt32(other->toConstant()->numberToDouble(), &n) ||
6739 n == 0) {
6740 return false;
6741 }
6742 return true;
6743}
6744
6745// Skip over instructions that usually appear between the actual index
6746// value being used and the MLoadElement.
6747// They don't modify the index value in a meaningful way.
6748static MDefinition* SkipUninterestingInstructions(MDefinition* ins) {
6749 // Drop the MToNumberInt32 added by the TypePolicy for double and float
6750 // values.
6751 if (ins->isToNumberInt32()) {
6752 return SkipUninterestingInstructions(ins->toToNumberInt32()->input());
6753 }
6754
6755 // Ignore the bounds check, which don't modify the index.
6756 if (ins->isBoundsCheck()) {
6757 return SkipUninterestingInstructions(ins->toBoundsCheck()->index());
6758 }
6759
6760 // Masking the index for Spectre-mitigation is not observable.
6761 if (ins->isSpectreMaskIndex()) {
6762 return SkipUninterestingInstructions(ins->toSpectreMaskIndex()->index());
6763 }
6764
6765 return ins;
6766}
6767
6768static bool DefinitelyDifferentValue(MDefinition* ins1, MDefinition* ins2) {
6769 ins1 = SkipUninterestingInstructions(ins1);
6770 ins2 = SkipUninterestingInstructions(ins2);
6771
6772 if (ins1 == ins2) {
6773 return false;
6774 }
6775
6776 // For constants check they are not equal.
6777 if (ins1->isConstant() && ins2->isConstant()) {
6778 MConstant* cst1 = ins1->toConstant();
6779 MConstant* cst2 = ins2->toConstant();
6780
6781 if (!cst1->isTypeRepresentableAsDouble() ||
6782 !cst2->isTypeRepresentableAsDouble()) {
6783 return false;
6784 }
6785
6786 // Be conservative and only allow values that fit into int32.
6787 int32_t n1, n2;
6788 if (!mozilla::NumberIsInt32(cst1->numberToDouble(), &n1) ||
6789 !mozilla::NumberIsInt32(cst2->numberToDouble(), &n2)) {
6790 return false;
6791 }
6792
6793 return n1 != n2;
6794 }
6795
6796 // Check if "ins1 = ins2 + cte", which would make both instructions
6797 // have different values.
6798 if (ins1->isAdd()) {
6799 if (AddIsANonZeroAdditionOf(ins1->toAdd(), ins2)) {
6800 return true;
6801 }
6802 }
6803 if (ins2->isAdd()) {
6804 if (AddIsANonZeroAdditionOf(ins2->toAdd(), ins1)) {
6805 return true;
6806 }
6807 }
6808
6809 return false;
6810}
6811
6812MDefinition::AliasType MLoadElement::mightAlias(const MDefinition* def) const {
6813 if (def->isStoreElement()) {
6814 const MStoreElement* store = def->toStoreElement();
6815 if (store->index() != index()) {
6816 if (DefinitelyDifferentValue(store->index(), index())) {
6817 return AliasType::NoAlias;
6818 }
6819 return AliasType::MayAlias;
6820 }
6821
6822 if (store->elements() != elements()) {
6823 return AliasType::MayAlias;
6824 }
6825
6826 return AliasType::MustAlias;
6827 }
6828 return AliasType::MayAlias;
6829}
6830
6831MDefinition* MLoadElement::foldsTo(TempAllocator& alloc) {
6832 if (MDefinition* def = foldsToStore(alloc)) {
6833 return def;
6834 }
6835
6836 return this;
6837}
6838
6839void MSqrt::trySpecializeFloat32(TempAllocator& alloc) {
6840 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
6841 setResultType(MIRType::Float32);
6842 specialization_ = MIRType::Float32;
6843 }
6844}
6845
6846MDefinition* MClz::foldsTo(TempAllocator& alloc) {
6847 if (num()->isConstant()) {
6848 MConstant* c = num()->toConstant();
6849 if (type() == MIRType::Int32) {
6850 uint32_t n = uint32_t(c->toInt32());
6851 return MConstant::NewInt32(alloc, std::countl_zero(n));
6852 }
6853 uint64_t n = uint64_t(c->toInt64());
6854 return MConstant::NewInt64(alloc, int64_t(std::countl_zero(n)));
6855 }
6856
6857 return this;
6858}
6859
6860MDefinition* MCtz::foldsTo(TempAllocator& alloc) {
6861 if (num()->isConstant()) {
6862 MConstant* c = num()->toConstant();
6863 if (type() == MIRType::Int32) {
6864 uint32_t n = uint32_t(num()->toConstant()->toInt32());
6865 return MConstant::NewInt32(alloc, std::countr_zero(n));
6866 }
6867 uint64_t n = uint64_t(c->toInt64());
6868 return MConstant::NewInt64(alloc, std::countr_zero(n));
6869 }
6870
6871 return this;
6872}
6873
6874MDefinition* MPopcnt::foldsTo(TempAllocator& alloc) {
6875 if (num()->isConstant()) {
6876 MConstant* c = num()->toConstant();
6877 if (type() == MIRType::Int32) {
6878 uint32_t n = uint32_t(num()->toConstant()->toInt32());
6879 return MConstant::NewInt32(alloc, std::popcount(n));
6880 }
6881 uint64_t n = uint64_t(c->toInt64());
6882 return MConstant::NewInt64(alloc, int64_t(std::popcount(n)));
6883 }
6884
6885 return this;
6886}
6887
6888MDefinition* MBoundsCheck::foldsTo(TempAllocator& alloc) {
6889 if (type() == MIRType::Int32 && index()->isConstant() &&
6890 length()->isConstant()) {
6891 uint32_t len = length()->toConstant()->toInt32();
6892 uint32_t idx = index()->toConstant()->toInt32();
6893 if (idx + uint32_t(minimum()) < len && idx + uint32_t(maximum()) < len) {
6894 return index();
6895 }
6896 }
6897
6898 return this;
6899}
6900
6901MDefinition* MTableSwitch::foldsTo(TempAllocator& alloc) {
6902 MDefinition* op = getOperand(0);
6903
6904 // If we only have one successor, convert to a plain goto to the only
6905 // successor. TableSwitch indices are numeric; other types will always go to
6906 // the only successor.
6907 if (numSuccessors() == 1 ||
6908 (op->type() != MIRType::Value && !IsNumberType(op->type()))) {
6909 return MGoto::New(alloc, getDefault());
6910 }
6911
6912 if (MConstant* opConst = op->maybeConstantValue()) {
6913 if (op->type() == MIRType::Int32) {
6914 int32_t i = opConst->toInt32() - low_;
6915 MBasicBlock* target;
6916 if (size_t(i) < numCases()) {
6917 target = getCase(size_t(i));
6918 } else {
6919 target = getDefault();
6920 }
6921 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"
, 6921); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { MOZ_CrashSequence(__null, 6921); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6922 return MGoto::New(alloc, target);
6923 }
6924 }
6925
6926 return this;
6927}
6928
6929MDefinition* MArrayJoin::foldsTo(TempAllocator& alloc) {
6930 MDefinition* arr = array();
6931
6932 if (!arr->isStringSplit()) {
6933 return this;
6934 }
6935
6936 setRecoveredOnBailout();
6937 if (arr->hasLiveDefUses()) {
6938 setNotRecoveredOnBailout();
6939 return this;
6940 }
6941
6942 // The MStringSplit won't generate any code.
6943 arr->setRecoveredOnBailout();
6944
6945 // We're replacing foo.split(bar).join(baz) by
6946 // foo.replace(bar, baz). MStringSplit could be recovered by
6947 // a bailout. As we are removing its last use, and its result
6948 // could be captured by a resume point, this MStringSplit will
6949 // be executed on the bailout path.
6950 MDefinition* string = arr->toStringSplit()->string();
6951 MDefinition* pattern = arr->toStringSplit()->separator();
6952 MDefinition* replacement = separator();
6953
6954 MStringReplace* substr =
6955 MStringReplace::New(alloc, string, pattern, replacement);
6956 substr->setFlatReplacement();
6957 return substr;
6958}
6959
6960MDefinition* MGetFirstDollarIndex::foldsTo(TempAllocator& alloc) {
6961 MDefinition* strArg = str();
6962 if (!strArg->isConstant()) {
6963 return this;
6964 }
6965
6966 JSOffThreadAtom* str = strArg->toConstant()->toString();
6967 int32_t index = GetFirstDollarIndexRawFlat(str);
6968 return MConstant::NewInt32(alloc, index);
6969}
6970
6971MDefinition::AliasType MSlots::mightAlias(const MDefinition* store) const {
6972 // ArrayPush only modifies object elements, but not object slots.
6973 if (store->isArrayPush()) {
6974 return AliasType::NoAlias;
6975 }
6976 return MInstruction::mightAlias(store);
6977}
6978
6979AliasSet MResizableTypedArrayLength::getAliasSet() const {
6980 // Loads the length and byteOffset slots, the shared-elements flag, the
6981 // auto-length fixed slot, and the shared raw-buffer length.
6982 auto flags = AliasSet::ArrayBufferViewLengthOrOffset |
6983 AliasSet::ObjectFields | AliasSet::FixedSlot |
6984 AliasSet::SharedArrayRawBufferLength;
6985
6986 // When a barrier is needed make the instruction effectful by giving it a
6987 // "store" effect. Also prevent reordering LoadUnboxedScalar before this
6988 // instruction by including |UnboxedElement| in the alias set.
6989 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) {
6990 return AliasSet::Store(flags | AliasSet::UnboxedElement);
6991 }
6992 return AliasSet::Load(flags);
6993}
6994
6995bool MResizableTypedArrayLength::congruentTo(const MDefinition* ins) const {
6996 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) {
6997 return false;
6998 }
6999 return congruentIfOperandsEqual(ins);
7000}
7001
7002AliasSet MResizableDataViewByteLength::getAliasSet() const {
7003 // Loads the length and byteOffset slots, the shared-elements flag, the
7004 // auto-length fixed slot, and the shared raw-buffer length.
7005 auto flags = AliasSet::ArrayBufferViewLengthOrOffset |
7006 AliasSet::ObjectFields | AliasSet::FixedSlot |
7007 AliasSet::SharedArrayRawBufferLength;
7008
7009 // When a barrier is needed make the instruction effectful by giving it a
7010 // "store" effect. Also prevent reordering LoadUnboxedScalar before this
7011 // instruction by including |UnboxedElement| in the alias set.
7012 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) {
7013 return AliasSet::Store(flags | AliasSet::UnboxedElement);
7014 }
7015 return AliasSet::Load(flags);
7016}
7017
7018bool MResizableDataViewByteLength::congruentTo(const MDefinition* ins) const {
7019 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required) {
7020 return false;
7021 }
7022 return congruentIfOperandsEqual(ins);
7023}
7024
7025MDefinition* MGuardNumberToIntPtrIndex::foldsTo(TempAllocator& alloc) {
7026 MDefinition* input = this->input();
7027
7028 if (input->isToDouble() && input->getOperand(0)->type() == MIRType::Int32) {
7029 return MInt32ToIntPtr::New(alloc, input->getOperand(0));
7030 }
7031
7032 if (!input->isConstant()) {
7033 return this;
7034 }
7035
7036 // Fold constant double representable as intptr to intptr.
7037 int64_t ival;
7038 if (!mozilla::NumberEqualsInt64(input->toConstant()->toDouble(), &ival)) {
7039 // If not representable as an int64, this access is equal to an OOB access.
7040 // So replace it with a known int64/intptr value which also produces an OOB
7041 // access. If we don't support OOB accesses we have to bail out.
7042 if (!supportOOB()) {
7043 return this;
7044 }
7045 ival = -1;
7046 }
7047
7048 if (ival < INTPTR_MIN(-9223372036854775807L-1) || ival > INTPTR_MAX(9223372036854775807L)) {
7049 return this;
7050 }
7051
7052 return MConstant::NewIntPtr(alloc, intptr_t(ival));
7053}
7054
7055MDefinition* MIsObject::foldsTo(TempAllocator& alloc) {
7056 MDefinition* input = object();
7057 if (!input->isBox()) {
7058 return this;
7059 }
7060
7061 MDefinition* unboxed = input->toBox()->input();
7062 return MConstant::NewBoolean(alloc, unboxed->type() == MIRType::Object);
7063}
7064
7065MDefinition* MIsNullOrUndefined::foldsTo(TempAllocator& alloc) {
7066 // MIsNullOrUndefined doesn't have a type-policy, so the value can already be
7067 // unboxed.
7068 MDefinition* unboxed = value();
7069 if (unboxed->type() == MIRType::Value) {
7070 if (!unboxed->isBox()) {
7071 return this;
7072 }
7073 unboxed = unboxed->toBox()->input();
7074 }
7075
7076 return MConstant::NewBoolean(alloc, IsNullOrUndefined(unboxed->type()));
7077}
7078
7079MDefinition* MGuardValue::foldsTo(TempAllocator& alloc) {
7080 if (MConstant* cst = value()->maybeConstantValue()) {
7081 if (expected().isValue() && cst->toJSValue() == expected().toValue()) {
7082 return value();
7083 }
7084 }
7085
7086 return this;
7087}
7088
7089MDefinition* MGuardNullOrUndefined::foldsTo(TempAllocator& alloc) {
7090 MDefinition* input = value();
7091 if (!input->isBox()) {
7092 return this;
7093 }
7094
7095 MDefinition* unboxed = input->toBox()->input();
7096 if (IsNullOrUndefined(unboxed->type())) {
7097 return input;
7098 }
7099
7100 return this;
7101}
7102
7103MDefinition* MGuardIsNotObject::foldsTo(TempAllocator& alloc) {
7104 MDefinition* input = value();
7105 if (!input->isBox()) {
7106 return this;
7107 }
7108
7109 MDefinition* unboxed = input->toBox()->input();
7110 if (unboxed->type() == MIRType::Object) {
7111 return this;
7112 }
7113
7114 return input;
7115}
7116
7117MDefinition* MGuardObjectIdentity::foldsTo(TempAllocator& alloc) {
7118 if (object()->isConstant() && expected()->isConstant()) {
7119 JSObject* obj = &object()->toConstant()->toObject();
7120 JSObject* other = &expected()->toConstant()->toObject();
7121 if (!bailOnEquality()) {
7122 if (obj == other) {
7123 return object();
7124 }
7125 } else {
7126 if (obj != other) {
7127 return object();
7128 }
7129 }
7130 }
7131
7132 if (!bailOnEquality() && object()->isNurseryObject() &&
7133 expected()->isNurseryObject()) {
7134 uint32_t objIndex = object()->toNurseryObject()->nurseryObjectIndex();
7135 uint32_t otherIndex = expected()->toNurseryObject()->nurseryObjectIndex();
7136 if (objIndex == otherIndex) {
7137 return object();
7138 }
7139 }
7140
7141 return this;
7142}
7143
7144MDefinition* MGuardSpecificFunction::foldsTo(TempAllocator& alloc) {
7145 if (function()->isConstant() && expected()->isConstant()) {
7146 JSObject* fun = &function()->toConstant()->toObject();
7147 JSObject* other = &expected()->toConstant()->toObject();
7148 if (fun == other) {
7149 return function();
7150 }
7151 }
7152
7153 if (function()->isNurseryObject() && expected()->isNurseryObject()) {
7154 uint32_t funIndex = function()->toNurseryObject()->nurseryObjectIndex();
7155 uint32_t otherIndex = expected()->toNurseryObject()->nurseryObjectIndex();
7156 if (funIndex == otherIndex) {
7157 return function();
7158 }
7159 }
7160
7161 return this;
7162}
7163
7164MDefinition* MGuardSpecificAtom::foldsTo(TempAllocator& alloc) {
7165 if (str()->isConstant()) {
7166 JSOffThreadAtom* s = str()->toConstant()->toString();
7167 if (s == atom()) {
7168 return str();
7169 }
7170 }
7171
7172 return this;
7173}
7174
7175MDefinition* MGuardSpecificSymbol::foldsTo(TempAllocator& alloc) {
7176 if (symbol()->isConstant()) {
7177 if (symbol()->toConstant()->toSymbol() == expected()) {
7178 return symbol();
7179 }
7180 }
7181
7182 return this;
7183}
7184
7185MDefinition* MGuardSpecificInt32::foldsTo(TempAllocator& alloc) {
7186 if (num()->isConstant() && num()->toConstant()->isInt32(expected())) {
7187 return num();
7188 }
7189 return this;
7190}
7191
7192MDefinition* MGuardShape::foldsTo(TempAllocator& alloc) {
7193 if (object()->isGuardShape() &&
7194 shape() == object()->toGuardShape()->shape() && dependency() &&
7195 object()->dependency() == dependency()) {
7196 return object();
7197 }
7198 return this;
7199}
7200
7201bool MGuardShapeList::congruentTo(const MDefinition* ins) const {
7202 if (!congruentIfOperandsEqual(ins)) {
7203 return false;
7204 }
7205
7206 // Returns true iff all non-nullptr shapes in |a| are also in |b|.
7207 auto hasAllShapes = [](const auto& a, const auto& b) {
7208 for (Shape* shape : a) {
7209 if (!shape) {
7210 continue;
7211 }
7212 auto isSameShape = [shape](Shape* other) { return shape == other; };
7213 if (!std::any_of(b.begin(), b.end(), isSameShape)) {
7214 return false;
7215 }
7216 }
7217 return true;
7218 };
7219
7220 // Return true if all shapes in |shapesA| are also in |shapesB| and vice
7221 // versa.
7222 const auto& shapesA = this->shapeList()->shapes();
7223 const auto& shapesB = ins->toGuardShapeList()->shapeList()->shapes();
7224 return hasAllShapes(shapesA, shapesB) && hasAllShapes(shapesB, shapesA);
7225}
7226
7227bool MGuardShapeListToOffset::congruentTo(const MDefinition* ins) const {
7228 if (!congruentIfOperandsEqual(ins)) {
7229 return false;
7230 }
7231
7232 const auto& shapesA = this->shapeList()->shapes();
7233 const auto& shapesB = ins->toGuardShapeListToOffset()->shapeList()->shapes();
7234 if (!std::equal(shapesA.begin(), shapesA.end(), shapesB.begin(),
7235 shapesB.end()))
7236 return false;
7237
7238 const auto& offsetsA = this->shapeList()->offsets();
7239 const auto& offsetsB =
7240 ins->toGuardShapeListToOffset()->shapeList()->offsets();
7241 return std::equal(offsetsA.begin(), offsetsA.end(), offsetsB.begin(),
7242 offsetsB.end());
7243}
7244
7245MDefinition::AliasType MGuardShape::mightAlias(const MDefinition* store) const {
7246 // These instructions only modify object elements, but not the shape.
7247 if (store->isStoreElementHole() || store->isArrayPush()) {
7248 return AliasType::NoAlias;
7249 }
7250 if (object()->isConstantProto()) {
7251 const MDefinition* receiverObject =
7252 object()->toConstantProto()->getReceiverObject();
7253 switch (store->op()) {
7254 case MDefinition::Opcode::StoreFixedSlot:
7255 if (store->toStoreFixedSlot()->object()->skipObjectGuards() ==
7256 receiverObject) {
7257 return AliasType::NoAlias;
7258 }
7259 break;
7260 case MDefinition::Opcode::StoreDynamicSlot:
7261 if (store->toStoreDynamicSlot()
7262 ->slots()
7263 ->toSlots()
7264 ->object()
7265 ->skipObjectGuards() == receiverObject) {
7266 return AliasType::NoAlias;
7267 }
7268 break;
7269 case MDefinition::Opcode::AddAndStoreSlot:
7270 if (store->toAddAndStoreSlot()->object()->skipObjectGuards() ==
7271 receiverObject) {
7272 return AliasType::NoAlias;
7273 }
7274 break;
7275 case MDefinition::Opcode::AllocateAndStoreSlot:
7276 if (store->toAllocateAndStoreSlot()->object()->skipObjectGuards() ==
7277 receiverObject) {
7278 return AliasType::NoAlias;
7279 }
7280 break;
7281 default:
7282 break;
7283 }
7284 }
7285 return MInstruction::mightAlias(store);
7286}
7287
7288MDefinition* MGuardIsNotProxy::foldsTo(TempAllocator& alloc) {
7289 KnownClass known = GetObjectKnownClass(object());
7290 if (known == KnownClass::None) {
7291 return this;
7292 }
7293
7294 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"
, 7294); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetObjectKnownJSClass(object())->isProxyObject()"
")"); do { MOZ_CrashSequence(__null, 7294); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7295 AssertKnownClass(alloc, this, object());
7296 return object();
7297}
7298
7299static PropertyKey ToNonIntPropertyKey(MDefinition* idval) {
7300 MConstant* constant = idval->maybeConstantValue();
7301 if (!constant) {
7302 return PropertyKey::Void();
7303 }
7304 if (constant->type() == MIRType::String) {
7305 JSOffThreadAtom* str = constant->toString();
7306 if (str->isIndex()) {
7307 return PropertyKey::Void();
7308 }
7309 return PropertyKey::NonIntAtom(str->unwrap());
7310 }
7311 if (constant->type() == MIRType::Symbol) {
7312 return PropertyKey::Symbol(constant->toSymbol());
7313 }
7314 return PropertyKey::Void();
7315}
7316
7317MDefinition* MMegamorphicLoadSlotByValue::foldsTo(TempAllocator& alloc) {
7318 PropertyKey id = ToNonIntPropertyKey(idVal());
7319 if (id.isVoid()) {
7320 return this;
7321 }
7322
7323 auto* result = MMegamorphicLoadSlot::New(alloc, object(), id);
7324 result->setDependency(dependency());
7325 return result;
7326}
7327
7328MDefinition* MMegamorphicLoadSlotByValuePermissive::foldsTo(
7329 TempAllocator& alloc) {
7330 PropertyKey id = ToNonIntPropertyKey(idVal());
7331 if (id.isVoid()) {
7332 return this;
7333 }
7334
7335 auto* result = MMegamorphicLoadSlotPermissive::New(alloc, object(), id);
7336 result->stealResumePoint(this);
7337 return result;
7338}
7339
7340HashNumber MNurseryObject::valueHash() const {
7341 HashNumber hash = MNullaryInstruction::valueHash();
7342 hash = addU32ToHash(hash, nurseryObjectIndex());
7343 return hash;
7344}
7345
7346bool MGuardFunctionScript::congruentTo(const MDefinition* ins) const {
7347 if (!ins->isGuardFunctionScript()) {
7348 return false;
7349 }
7350 if (expected() != ins->toGuardFunctionScript()->expected()) {
7351 return false;
7352 }
7353 return congruentIfOperandsEqual(ins);
7354}
7355
7356AliasSet MGuardFunctionScript::getAliasSet() const {
7357 // A JSFunction's BaseScript pointer is immutable. Relazification of
7358 // top-level/named self-hosted functions is an exception to this, but we don't
7359 // use this guard for those self-hosted functions.
7360 // See IRGenerator::emitCalleeGuard.
7361 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", 7361); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "flags_.isLambda()" ")"); do { MOZ_CrashSequence
(__null, 7361); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
7362 return AliasSet::None();
7363}
7364
7365MDefinition* MGuardStringToIndex::foldsTo(TempAllocator& alloc) {
7366 if (!string()->isConstant()) {
7367 return this;
7368 }
7369
7370 JSOffThreadAtom* str = string()->toConstant()->toString();
7371
7372 uint32_t index = UINT32_MAX(4294967295U);
7373 if (!str->isIndex(&index) || index > INT32_MAX(2147483647)) {
7374 return this;
7375 }
7376
7377 return MConstant::NewInt32(alloc, index);
7378}
7379
7380MDefinition* MGuardStringToInt32::foldsTo(TempAllocator& alloc) {
7381 if (!string()->isConstant()) {
7382 return this;
7383 }
7384
7385 JSOffThreadAtom* str = string()->toConstant()->toString();
7386 double number = OffThreadAtomToNumber(str);
7387
7388 int32_t n;
7389 if (!mozilla::NumberIsInt32(number, &n)) {
7390 return this;
7391 }
7392
7393 return MConstant::NewInt32(alloc, n);
7394}
7395
7396MDefinition* MGuardStringToDouble::foldsTo(TempAllocator& alloc) {
7397 if (!string()->isConstant()) {
7398 return this;
7399 }
7400
7401 JSOffThreadAtom* str = string()->toConstant()->toString();
7402 double number = OffThreadAtomToNumber(str);
7403 return MConstant::NewDouble(alloc, number);
7404}
7405
7406MDefinition* MGuardToClass::foldsTo(TempAllocator& alloc) {
7407 const JSClass* clasp = GetObjectKnownJSClass(object());
7408 if (!clasp || getClass() != clasp) {
7409 return this;
7410 }
7411
7412 AssertKnownClass(alloc, this, object());
7413 return object();
7414}
7415
7416MDefinition* MGuardToFunction::foldsTo(TempAllocator& alloc) {
7417 if (GetObjectKnownClass(object()) != KnownClass::Function) {
7418 return this;
7419 }
7420
7421 AssertKnownClass(alloc, this, object());
7422 return object();
7423}
7424
7425MDefinition* MHasClass::foldsTo(TempAllocator& alloc) {
7426 const JSClass* clasp = GetObjectKnownJSClass(object());
7427 if (!clasp) {
7428 return this;
7429 }
7430
7431 AssertKnownClass(alloc, this, object());
7432 return MConstant::NewBoolean(alloc, getClass() == clasp);
7433}
7434
7435MDefinition* MIsCallable::foldsTo(TempAllocator& alloc) {
7436 if (input()->type() != MIRType::Object) {
7437 return this;
7438 }
7439
7440 KnownClass known = GetObjectKnownClass(input());
7441 if (known == KnownClass::None) {
7442 return this;
7443 }
7444
7445 AssertKnownClass(alloc, this, input());
7446 return MConstant::NewBoolean(alloc, known == KnownClass::Function);
7447}
7448
7449MDefinition* MIsArray::foldsTo(TempAllocator& alloc) {
7450 if (input()->type() != MIRType::Object) {
7451 return this;
7452 }
7453
7454 KnownClass known = GetObjectKnownClass(input());
7455 if (known == KnownClass::None) {
7456 return this;
7457 }
7458
7459 AssertKnownClass(alloc, this, input());
7460 return MConstant::NewBoolean(alloc, known == KnownClass::Array);
7461}
7462
7463MDefinition* MGuardIsNotArrayBufferMaybeShared::foldsTo(TempAllocator& alloc) {
7464 switch (GetObjectKnownClass(object())) {
7465 case KnownClass::PlainObject:
7466 case KnownClass::Array:
7467 case KnownClass::Function:
7468 case KnownClass::RegExp:
7469 case KnownClass::Date:
7470 case KnownClass::ArrayIterator:
7471 case KnownClass::StringIterator:
7472 case KnownClass::RegExpStringIterator: {
7473 AssertKnownClass(alloc, this, object());
7474 return object();
7475 }
7476 case KnownClass::None:
7477 break;
7478 }
7479
7480 return this;
7481}
7482
7483MDefinition* MCheckIsObj::foldsTo(TempAllocator& alloc) {
7484 if (!input()->isBox()) {
7485 return this;
7486 }
7487
7488 MDefinition* unboxed = input()->toBox()->input();
7489 if (unboxed->type() == MIRType::Object) {
7490 return unboxed;
7491 }
7492
7493 return this;
7494}
7495
7496static bool IsBoxedObject(MDefinition* def) {
7497 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"
, 7497); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->type() == MIRType::Value"
")"); do { MOZ_CrashSequence(__null, 7497); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7498
7499 if (def->isBox()) {
7500 return def->toBox()->input()->type() == MIRType::Object;
7501 }
7502
7503 // Construct calls are always returning a boxed object.
7504 //
7505 // TODO: We should consider encoding this directly in the graph instead of
7506 // having to special case it here.
7507 if (def->isCall()) {
7508 return def->toCall()->isConstructing();
7509 }
7510 if (def->isConstructArray()) {
7511 return true;
7512 }
7513 if (def->isConstructArgs()) {
7514 return true;
7515 }
7516
7517 return false;
7518}
7519
7520MDefinition* MCheckReturn::foldsTo(TempAllocator& alloc) {
7521 auto* returnVal = returnValue();
7522 if (!returnVal->isBox()) {
7523 return this;
7524 }
7525
7526 auto* unboxedReturnVal = returnVal->toBox()->input();
7527 if (unboxedReturnVal->type() == MIRType::Object) {
7528 return returnVal;
7529 }
7530
7531 if (unboxedReturnVal->type() != MIRType::Undefined) {
7532 return this;
7533 }
7534
7535 auto* thisVal = thisValue();
7536 if (IsBoxedObject(thisVal)) {
7537 return thisVal;
7538 }
7539
7540 return this;
7541}
7542
7543MDefinition* MCheckThis::foldsTo(TempAllocator& alloc) {
7544 MDefinition* input = thisValue();
7545 if (!input->isBox()) {
7546 return this;
7547 }
7548
7549 MDefinition* unboxed = input->toBox()->input();
7550 if (IsMagicType(unboxed->type())) {
7551 return this;
7552 }
7553
7554 return input;
7555}
7556
7557MDefinition* MCheckThisReinit::foldsTo(TempAllocator& alloc) {
7558 MDefinition* input = thisValue();
7559 if (!input->isBox()) {
7560 return this;
7561 }
7562
7563 MDefinition* unboxed = input->toBox()->input();
7564 if (unboxed->type() != MIRType::MagicUninitializedLexical) {
7565 return this;
7566 }
7567
7568 return input;
7569}
7570
7571MDefinition* MCheckObjCoercible::foldsTo(TempAllocator& alloc) {
7572 MDefinition* input = checkValue();
7573 if (!input->isBox()) {
7574 return this;
7575 }
7576
7577 MDefinition* unboxed = input->toBox()->input();
7578 if (IsNullOrUndefined(unboxed->type())) {
7579 return this;
7580 }
7581
7582 return input;
7583}
7584
7585MDefinition* MGuardInt32IsNonNegative::foldsTo(TempAllocator& alloc) {
7586 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"
, 7586); AnnotateMozCrashReason("MOZ_ASSERT" "(" "index()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 7586); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7587
7588 MDefinition* input = index();
7589 if (!input->isConstant() || input->toConstant()->toInt32() < 0) {
7590 return this;
7591 }
7592 return input;
7593}
7594
7595MDefinition* MGuardIntPtrIsNonNegative::foldsTo(TempAllocator& alloc) {
7596 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", 7596); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "index()->type() == MIRType::IntPtr" ")"
); do { MOZ_CrashSequence(__null, 7596); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7597
7598 MDefinition* input = index();
7599 if (!input->isConstant() || input->toConstant()->toIntPtr() < 0) {
7600 return this;
7601 }
7602 return input;
7603}
7604
7605MDefinition* MGuardInt32Range::foldsTo(TempAllocator& alloc) {
7606 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"
, 7606); AnnotateMozCrashReason("MOZ_ASSERT" "(" "input()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 7606); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7607 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", 7607); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "minimum() <= maximum()" ")"); do { MOZ_CrashSequence
(__null, 7607); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7608
7609 MDefinition* in = input();
7610 if (!in->isConstant()) {
7611 return this;
7612 }
7613 int32_t cst = in->toConstant()->toInt32();
7614 if (cst < minimum() || cst > maximum()) {
7615 return this;
7616 }
7617 return in;
7618}
7619
7620MDefinition* MGuardNonGCThing::foldsTo(TempAllocator& alloc) {
7621 if (!input()->isBox()) {
7622 return this;
7623 }
7624
7625 MDefinition* unboxed = input()->toBox()->input();
7626 if (!IsNonGCThing(unboxed->type())) {
7627 return this;
7628 }
7629 return input();
7630}
7631
7632MBindFunction* MBindFunction::New(TempAllocator& alloc, MDefinition* target,
7633 uint32_t argc, JSObject* templateObj) {
7634 auto* ins = new (alloc) MBindFunction(templateObj);
7635 if (!ins->init(alloc, NumNonArgumentOperands + argc)) {
7636 return nullptr;
7637 }
7638 ins->initOperand(0, target);
7639 return ins;
7640}
7641
7642MCreateInlinedArgumentsObject* MCreateInlinedArgumentsObject::New(
7643 TempAllocator& alloc, MDefinition* callObj, MDefinition* callee,
7644 MDefinitionVector& args, ArgumentsObject* templateObj) {
7645 MCreateInlinedArgumentsObject* ins =
7646 new (alloc) MCreateInlinedArgumentsObject(templateObj);
7647
7648 uint32_t argc = args.length();
7649 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", 7649); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc <= ArgumentsObject::MaxInlinedArgs"
")"); do { MOZ_CrashSequence(__null, 7649); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7650
7651 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7652 return nullptr;
7653 }
7654
7655 ins->initOperand(0, callObj);
7656 ins->initOperand(1, callee);
7657 for (uint32_t i = 0; i < argc; i++) {
7658 ins->initOperand(i + NumNonArgumentOperands, args[i]);
7659 }
7660
7661 return ins;
7662}
7663
7664MGetInlinedArgument* MGetInlinedArgument::New(
7665 TempAllocator& alloc, MDefinition* index,
7666 MCreateInlinedArgumentsObject* args) {
7667 MGetInlinedArgument* ins = new (alloc) MGetInlinedArgument();
7668
7669 uint32_t argc = args->numActuals();
7670 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", 7670); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc <= ArgumentsObject::MaxInlinedArgs"
")"); do { MOZ_CrashSequence(__null, 7670); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7671
7672 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7673 return nullptr;
7674 }
7675
7676 ins->initOperand(0, index);
7677 for (uint32_t i = 0; i < argc; i++) {
7678 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i));
7679 }
7680
7681 return ins;
7682}
7683
7684MGetInlinedArgument* MGetInlinedArgument::New(TempAllocator& alloc,
7685 MDefinition* index,
7686 const CallInfo& callInfo) {
7687 MGetInlinedArgument* ins = new (alloc) MGetInlinedArgument();
7688
7689 uint32_t argc = callInfo.argc();
7690 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", 7690); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc <= ArgumentsObject::MaxInlinedArgs"
")"); do { MOZ_CrashSequence(__null, 7690); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7691
7692 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7693 return nullptr;
7694 }
7695
7696 ins->initOperand(0, index);
7697 for (uint32_t i = 0; i < argc; i++) {
7698 ins->initOperand(i + NumNonArgumentOperands, callInfo.getArg(i));
7699 }
7700
7701 return ins;
7702}
7703
7704MDefinition* MGetInlinedArgument::foldsTo(TempAllocator& alloc) {
7705 MDefinition* indexDef = SkipUninterestingInstructions(index());
7706 if (!indexDef->isConstant() || indexDef->type() != MIRType::Int32) {
7707 return this;
7708 }
7709
7710 int32_t indexConst = indexDef->toConstant()->toInt32();
7711 if (indexConst < 0 || uint32_t(indexConst) >= numActuals()) {
7712 return this;
7713 }
7714
7715 MDefinition* arg = getArg(indexConst);
7716 if (arg->type() != MIRType::Value) {
7717 arg = MBox::New(alloc, arg);
7718 }
7719
7720 return arg;
7721}
7722
7723MGetInlinedArgumentHole* MGetInlinedArgumentHole::New(
7724 TempAllocator& alloc, MDefinition* index,
7725 MCreateInlinedArgumentsObject* args) {
7726 auto* ins = new (alloc) MGetInlinedArgumentHole();
7727
7728 uint32_t argc = args->numActuals();
7729 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", 7729); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc <= ArgumentsObject::MaxInlinedArgs"
")"); do { MOZ_CrashSequence(__null, 7729); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7730
7731 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7732 return nullptr;
7733 }
7734
7735 ins->initOperand(0, index);
7736 for (uint32_t i = 0; i < argc; i++) {
7737 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i));
7738 }
7739
7740 return ins;
7741}
7742
7743MDefinition* MGetInlinedArgumentHole::foldsTo(TempAllocator& alloc) {
7744 MDefinition* indexDef = SkipUninterestingInstructions(index());
7745 if (!indexDef->isConstant() || indexDef->type() != MIRType::Int32) {
7746 return this;
7747 }
7748
7749 int32_t indexConst = indexDef->toConstant()->toInt32();
7750 if (indexConst < 0) {
7751 return this;
7752 }
7753
7754 MDefinition* arg;
7755 if (uint32_t(indexConst) < numActuals()) {
7756 arg = getArg(indexConst);
7757
7758 if (arg->type() != MIRType::Value) {
7759 arg = MBox::New(alloc, arg);
7760 }
7761 } else {
7762 auto* undefined = MConstant::NewUndefined(alloc);
7763 block()->insertBefore(this, undefined);
7764
7765 arg = MBox::New(alloc, undefined);
7766 }
7767
7768 return arg;
7769}
7770
7771MInlineArgumentsSlice* MInlineArgumentsSlice::New(
7772 TempAllocator& alloc, MDefinition* begin, MDefinition* count,
7773 MCreateInlinedArgumentsObject* args, JSObject* templateObj,
7774 gc::Heap initialHeap) {
7775 auto* ins = new (alloc) MInlineArgumentsSlice(templateObj, initialHeap);
7776
7777 uint32_t argc = args->numActuals();
7778 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", 7778); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc <= ArgumentsObject::MaxInlinedArgs"
")"); do { MOZ_CrashSequence(__null, 7778); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7779
7780 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7781 return nullptr;
7782 }
7783
7784 ins->initOperand(0, begin);
7785 ins->initOperand(1, count);
7786 for (uint32_t i = 0; i < argc; i++) {
7787 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i));
7788 }
7789
7790 return ins;
7791}
7792
7793MDefinition* MNormalizeSliceTerm::foldsTo(TempAllocator& alloc) {
7794 auto* length = this->length();
7795 if (!length->isConstant() && !length->isArgumentsLength()) {
7796 return this;
7797 }
7798
7799 if (length->isConstant()) {
7800 int32_t lengthConst = length->toConstant()->toInt32();
7801 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", 7801); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lengthConst >= 0" ")"); do { MOZ_CrashSequence
(__null, 7801); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7802
7803 // Result is always zero when |length| is zero.
7804 if (lengthConst == 0) {
7805 return length;
7806 }
7807
7808 auto* value = this->value();
7809 if (value->isConstant()) {
7810 int32_t valueConst = value->toConstant()->toInt32();
7811
7812 int32_t normalized;
7813 if (valueConst < 0) {
7814 normalized = std::max(valueConst + lengthConst, 0);
7815 } else {
7816 normalized = std::min(valueConst, lengthConst);
7817 }
7818
7819 if (normalized == valueConst) {
7820 return value;
7821 }
7822 if (normalized == lengthConst) {
7823 return length;
7824 }
7825 return MConstant::NewInt32(alloc, normalized);
7826 }
7827
7828 return this;
7829 }
7830
7831 auto* value = this->value();
7832 if (value->isConstant()) {
7833 int32_t valueConst = value->toConstant()->toInt32();
7834
7835 // Minimum of |value| and |length|.
7836 if (valueConst > 0) {
7837 return MMinMax::NewMin(alloc, value, length, MIRType::Int32);
7838 }
7839
7840 // Maximum of |value + length| and zero.
7841 if (valueConst < 0) {
7842 // Safe to truncate because |length| is never negative.
7843 auto* add = MAdd::New(alloc, value, length, TruncateKind::Truncate);
7844 block()->insertBefore(this, add);
7845
7846 auto* zero = MConstant::NewInt32(alloc, 0);
7847 block()->insertBefore(this, zero);
7848
7849 return MMinMax::NewMax(alloc, add, zero, MIRType::Int32);
7850 }
7851
7852 // Directly return the value when it's zero.
7853 return value;
7854 }
7855
7856 // Normalizing MArgumentsLength is a no-op.
7857 if (value->isArgumentsLength()) {
7858 return value;
7859 }
7860
7861 return this;
7862}
7863
7864MDefinition* MToIntegerIndex::foldsTo(TempAllocator& alloc) {
7865 // |length| is guaranteed to be a non-negative value.
7866
7867 auto* index = this->index();
7868 auto* length = this->length();
7869
7870 if (index == length) {
7871 return index;
7872 }
7873
7874 if (index->isConstant()) {
7875 intptr_t indexConst = index->toConstant()->toIntPtr();
7876
7877 // Minimum of |index| and |length|.
7878 if (indexConst > 0) {
7879 return MMinMax::NewMin(alloc, index, length, MIRType::IntPtr);
7880 }
7881
7882 // Maximum of |value + length| and zero.
7883 if (indexConst < 0) {
7884 auto* add = MAdd::New(alloc, index, length, MIRType::IntPtr);
7885 block()->insertBefore(this, add);
7886
7887 auto* zero = MConstant::NewIntPtr(alloc, 0);
7888 block()->insertBefore(this, zero);
7889
7890 return MMinMax::NewMax(alloc, add, zero, MIRType::IntPtr);
7891 }
7892
7893 // Directly return the index when it's zero.
7894 return index;
7895 }
7896
7897 return this;
7898}
7899
7900MDefinition* MDateParse::foldsTo(TempAllocator& alloc) {
7901 auto* string = this->string();
7902 if (!string->isConstant()) {
7903 return this;
7904 }
7905 JSOffThreadAtom* str = string->toConstant()->toString();
7906
7907 ParsedDate parsed;
7908 if (!DateParse(str, &parsed)) {
7909 // Can't parse as date, always NaN.
7910 return MConstant::NewDouble(alloc, JS::GenericNaN());
7911 }
7912 auto [date, isLocalTime] = parsed;
7913
7914 if (isLocalTime) {
7915 auto* localTime = MConstant::NewInt64(alloc, date);
7916 block()->insertBefore(this, localTime);
7917 return MLocalTimeToUTC::New(alloc, localTime);
7918 }
7919
7920 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"
, 7920); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JS::TimeClip(date).isValid()"
")"); do { MOZ_CrashSequence(__null, 7920); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7921 return MConstant::NewDouble(alloc, double(date));
7922}
7923
7924MDefinition* MTimeClip::foldsTo(TempAllocator& alloc) {
7925 auto* time = this->time();
7926 if (!time->isConstant()) {
7927 return this;
7928 }
7929
7930 // NB: TimeClip can return non-canonicalize doubles.
7931 auto clipped = JS::TimeClip(time->toConstant()->toDouble());
7932 return MConstant::NewDouble(alloc, JS::CanonicalizeNaN(clipped.toDouble()));
7933}
7934
7935JSOp MBinaryCache::jsop() const { return JSOp(*resumePoint()->pc()); }
7936
7937template <typename T>
7938static wasm::MaybeRefType GetBaseRefTypeForWasmLoadOrStore(T ins) {
7939 const MDefinition* structObject;
7940 if (ins->base()->type() == MIRType::WasmStructData) {
7941 MOZ_RELEASE_ASSERT(ins->base()->isWasmLoadField())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->base()->isWasmLoadField())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->base()->isWasmLoadField
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("ins->base()->isWasmLoadField()", "./../../../../js/src/jit/MIR.cpp"
, 7941); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "ins->base()->isWasmLoadField()"
")"); do { MOZ_CrashSequence(__null, 7941); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7942 structObject = ins->base()->toWasmLoadField()->base();
7943 } else {
7944 structObject = ins->base();
7945 }
7946 return structObject->wasmRefType().asNonNullable();
7947}
7948
7949// Wasm loads and stores can be proven not to alias if their offsets are
7950// different or their ref types are known and disjoint. (Disjoint alias sets
7951// also mean no aliasing, but this is obvious because that's just what alias
7952// sets already do.)
7953//
7954// Different offsets -> NoAlias is true because each field (whether GC data or
7955// internal data) has one and only one offset that is used to access it.
7956// Disjoint types -> NoAlias is true because, well, types. When considering
7957// types here, we exclude null because null loads and stores will trap anyway.
7958//
7959// For more rationale, see bug 2061530.
7960MDefinition::AliasType MWasmLoadField::mightAlias(
7961 const MDefinition* ins) const {
7962 if (!(getAliasSet().flags() & ins->getAliasSet().flags())) {
7963 return AliasType::NoAlias;
7964 }
7965 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", 7965); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isEffectful() && ins->isEffectful()"
")"); do { MOZ_CrashSequence(__null, 7965); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7966
7967 wasm::MaybeRefType insType;
7968 uint32_t insOffset;
7969 if (ins->isWasmStoreField()) {
7970 const MWasmStoreField* store = ins->toWasmStoreField();
7971 insType = GetBaseRefTypeForWasmLoadOrStore(store);
7972 insOffset = store->offset();
7973 } else if (ins->isWasmStoreFieldRef()) {
7974 const MWasmStoreFieldRef* store = ins->toWasmStoreFieldRef();
7975 insType = GetBaseRefTypeForWasmLoadOrStore(store);
7976 insOffset = store->offset();
7977 } else {
7978 // Safe default, but any other type of store that can operate on the same
7979 // values as a (performance-sensitive) MWasmLoadField should probably be
7980 // added above.
7981 return AliasType::MayAlias;
7982 }
7983
7984 wasm::MaybeRefType thisType = GetBaseRefTypeForWasmLoadOrStore(this);
7985 if (offset() != insOffset ||
7986 !wasm::MaybeRefType::mayHaveValuesInCommon(thisType, insType)) {
7987 return AliasType::NoAlias;
7988 }
7989
7990 return AliasType::MayAlias;
7991}