Bug 1445235 part 1 - Also add Spectre mitigations for MBoundsCheck added for stores. r=luke, a=RyanVM
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 16 Mar 2018 12:00:48 +0100
changeset 463126 39a6945f06ed8899c0146e91418ee8e322c3a0aa
parent 463125 5ee0d4fb20f40403df6ef9106d64116e5dcfe630
child 463127 02a7736cbf001656b7aa15480fd92520632f5872
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke, RyanVM
bugs1445235
milestone60.0
Bug 1445235 part 1 - Also add Spectre mitigations for MBoundsCheck added for stores. r=luke, a=RyanVM
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.h
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7888,18 +7888,17 @@ IonBuilder::getElemTryTypedObject(bool* 
 
     MOZ_CRASH("Bad kind");
 }
 
 bool
 IonBuilder::checkTypedObjectIndexInBounds(uint32_t elemSize,
                                           MDefinition* index,
                                           TypedObjectPrediction objPrediction,
-                                          LinearSum* indexAsByteOffset,
-                                          BoundsCheckKind kind)
+                                          LinearSum* indexAsByteOffset)
 {
     // Ensure index is an integer.
     MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
     current->add(idInt32);
 
     // If we know the length statically from the type, just embed it.
     // Otherwise, load it from the appropriate reserved slot on the
     // typed object.  We know it's an int32, so we can convert from
@@ -7916,17 +7915,17 @@ IonBuilder::checkTypedObjectIndexInBound
             trackOptimizationOutcome(TrackedOutcome::TypedObjectHasDetachedBuffer);
             return false;
         }
     } else {
         trackOptimizationOutcome(TrackedOutcome::TypedObjectArrayRange);
         return false;
     }
 
-    index = addBoundsCheck(idInt32, length, kind);
+    index = addBoundsCheck(idInt32, length);
 
     return indexAsByteOffset->add(index, AssertedCast<int32_t>(elemSize));
 }
 
 AbortReasonOr<Ok>
 IonBuilder::getElemTryScalarElemOfTypedObject(bool* emitted,
                                               MDefinition* obj,
                                               MDefinition* index,
@@ -7936,21 +7935,18 @@ IonBuilder::getElemTryScalarElemOfTypedO
 {
     MOZ_ASSERT(objPrediction.ofArrayKind());
 
     // Must always be loading the same scalar type
     ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
     MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
 
     LinearSum indexAsByteOffset(alloc());
-    if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset,
-                                       BoundsCheckKind::IsLoad))
-    {
-        return Ok();
-    }
+    if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
+        return Ok();
 
     trackOptimizationSuccess();
     *emitted = true;
 
     return pushScalarLoadFromTypedObject(obj, indexAsByteOffset, elemType);
 }
 
 AbortReasonOr<Ok>
@@ -7961,21 +7957,18 @@ IonBuilder::getElemTryReferenceElemOfTyp
                                                  TypedObjectPrediction elemPrediction)
 {
     MOZ_ASSERT(objPrediction.ofArrayKind());
 
     ReferenceTypeDescr::Type elemType = elemPrediction.referenceType();
     uint32_t elemSize = ReferenceTypeDescr::size(elemType);
 
     LinearSum indexAsByteOffset(alloc());
-    if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset,
-                                       BoundsCheckKind::IsLoad))
-    {
-        return Ok();
-    }
+    if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
+        return Ok();
 
     trackOptimizationSuccess();
     *emitted = true;
 
     return pushReferenceLoadFromTypedObject(obj, indexAsByteOffset, elemType, nullptr);
 }
 
 AbortReasonOr<Ok>
@@ -8085,21 +8078,18 @@ IonBuilder::getElemTryComplexElemOfTyped
                                                uint32_t elemSize)
 {
     MOZ_ASSERT(objPrediction.ofArrayKind());
 
     MDefinition* type = loadTypedObjectType(obj);
     MDefinition* elemTypeObj = typeObjectForElementFromArrayStructType(type);
 
     LinearSum indexAsByteOffset(alloc());
-    if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset,
-                                       BoundsCheckKind::IsLoad))
-    {
-        return Ok();
-    }
+    if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
+        return Ok();
 
     return pushDerivedTypedObject(emitted, obj, indexAsByteOffset,
                                   elemPrediction, elemTypeObj);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::pushDerivedTypedObject(bool* emitted,
                                    MDefinition* obj,
@@ -8380,17 +8370,17 @@ IonBuilder::getElemTryString(bool* emitt
     // Emit fast path for string[index].
     MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
     current->add(idInt32);
     index = idInt32;
 
     MStringLength* length = MStringLength::New(alloc(), obj);
     current->add(length);
 
-    index = addBoundsCheck(index, length, BoundsCheckKind::IsLoad);
+    index = addBoundsCheck(index, length);
 
     MCharCodeAt* charCode = MCharCodeAt::New(alloc(), obj, index);
     current->add(charCode);
 
     MFromCharCode* result = MFromCharCode::New(alloc(), charCode);
     current->add(result);
     current->push(result);
 
@@ -8422,17 +8412,17 @@ IonBuilder::getElemTryArguments(bool* em
     current->add(length);
 
     // Ensure index is an integer.
     MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
     current->add(idInt32);
     index = idInt32;
 
     // Bailouts if we read more than the number of actual arguments.
-    index = addBoundsCheck(index, length, BoundsCheckKind::IsLoad);
+    index = addBoundsCheck(index, length);
 
     // Load the argument from the actual arguments.
     bool modifiesArgs = script()->baselineScript()->modifiesArguments();
     MGetFrameArgument* load = MGetFrameArgument::New(alloc(), index, modifiesArgs);
     current->add(load);
     current->push(load);
 
     TemporaryTypeSet* types = bytecodeTypes(pc);
@@ -8510,18 +8500,17 @@ IonBuilder::getElemTryArgumentsInlinedIn
     MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
     current->add(idInt32);
     index = idInt32;
 
     // Bailout if we read more than the number of actual arguments. This bailout
     // cannot re-enter because reading out of bounds arguments will disable the
     // lazy arguments optimization for this script, when this code would be
     // executed in Baseline. (see GetElemOptimizedArguments)
-    index = addBoundsCheck(index, constantInt(inlineCallInfo_->argc()),
-                           BoundsCheckKind::IsLoad);
+    index = addBoundsCheck(index, constantInt(inlineCallInfo_->argc()));
 
     // Get an instruction to represent the state of the argument vector.
     MInstruction* args = MArgumentState::New(alloc().fallible(), inlineCallInfo_->argv());
     if (!args)
         return abort(AbortReason::Alloc);
     current->add(args);
 
     // Select a value to pick from a vector.
@@ -8704,17 +8693,17 @@ IonBuilder::jsop_getelem_dense(MDefiniti
 
     MInstruction* load;
 
     if (!readOutOfBounds) {
         // This load should not return undefined, so likely we're reading
         // in-bounds elements, and the array is packed or its holes are not
         // read. This is the best case: we can separate the bounds check for
         // hoisting.
-        index = addBoundsCheck(index, initLength, BoundsCheckKind::IsLoad);
+        index = addBoundsCheck(index, initLength);
 
         load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble);
         current->add(load);
     } else {
         // This load may return undefined, so assume that we *can* read holes,
         // or that we can read out-of-bounds accesses. In this case, the bounds
         // check is part of the opcode.
         load = MLoadElementHole::New(alloc(), elements, index, initLength, needsHoleCheck);
@@ -8743,18 +8732,17 @@ IonBuilder::addArrayBufferByteLength(MDe
     ins->setResultType(MIRType::Int32);
     return ins;
 }
 
 void
 IonBuilder::addTypedArrayLengthAndData(MDefinition* obj,
                                        BoundsChecking checking,
                                        MDefinition** index,
-                                       MInstruction** length, MInstruction** elements,
-                                       BoundsCheckKind boundsCheckKind)
+                                       MInstruction** length, MInstruction** elements)
 {
     MOZ_ASSERT((index != nullptr) == (elements != nullptr));
 
     JSObject* tarr = nullptr;
 
     if (MConstant* objConst = obj->maybeConstantValue()) {
         if (objConst->type() == MIRType::Object)
             tarr = &objConst->toObject();
@@ -8778,32 +8766,32 @@ IonBuilder::addTypedArrayLengthAndData(M
                 obj->setImplicitlyUsedUnchecked();
 
                 int32_t len = AssertedCast<int32_t>(tarr->as<TypedArrayObject>().length());
                 *length = MConstant::New(alloc(), Int32Value(len));
                 current->add(*length);
 
                 if (index) {
                     if (checking == DoBoundsCheck)
-                        *index = addBoundsCheck(*index, *length, boundsCheckKind);
+                        *index = addBoundsCheck(*index, *length);
 
                     *elements = MConstantElements::New(alloc(), data);
                     current->add(*elements);
                 }
                 return;
             }
         }
     }
 
     *length = MTypedArrayLength::New(alloc(), obj);
     current->add(*length);
 
     if (index) {
         if (checking == DoBoundsCheck)
-            *index = addBoundsCheck(*index, *length, boundsCheckKind);
+            *index = addBoundsCheck(*index, *length);
 
         *elements = MTypedArrayElements::New(alloc(), obj);
         current->add(*elements);
     }
 }
 
 MDefinition*
 IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition* id,
@@ -8875,18 +8863,17 @@ IonBuilder::jsop_getelem_typed(MDefiniti
         // the array type to determine the result type, even if the opcode has
         // never executed. The known pushed type is only used to distinguish
         // uint32 reads that may produce either doubles or integers.
         MIRType knownType = MIRTypeForTypedArrayRead(arrayType, allowDouble);
 
         // Get length, bounds-check, then get elements, and add all instructions.
         MInstruction* length;
         MInstruction* elements;
-        addTypedArrayLengthAndData(obj, DoBoundsCheck, &index, &length, &elements,
-                                   BoundsCheckKind::IsLoad);
+        addTypedArrayLengthAndData(obj, DoBoundsCheck, &index, &length, &elements);
 
         // Load the element.
         MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, index, arrayType);
         current->add(load);
         current->push(load);
 
         // Note: we can ignore the type barrier here, we know the type must
         // be valid and unbarriered.
@@ -9061,21 +9048,18 @@ IonBuilder::setElemTryReferenceElemOfTyp
                                                  TypedObjectPrediction objPrediction,
                                                  MDefinition* value,
                                                  TypedObjectPrediction elemPrediction)
 {
     ReferenceTypeDescr::Type elemType = elemPrediction.referenceType();
     uint32_t elemSize = ReferenceTypeDescr::size(elemType);
 
     LinearSum indexAsByteOffset(alloc());
-    if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset,
-                                       BoundsCheckKind::IsStore))
-    {
-        return Ok();
-    }
+    if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
+        return Ok();
 
     return setPropTryReferenceTypedObjectValue(emitted, obj, indexAsByteOffset,
                                                elemType, value, nullptr);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::setElemTryScalarElemOfTypedObject(bool* emitted,
                                               MDefinition* obj,
@@ -9085,21 +9069,18 @@ IonBuilder::setElemTryScalarElemOfTypedO
                                               TypedObjectPrediction elemPrediction,
                                               uint32_t elemSize)
 {
     // Must always be loading the same scalar type
     ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
     MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
 
     LinearSum indexAsByteOffset(alloc());
-    if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset,
-                                       BoundsCheckKind::IsStore))
-    {
-        return Ok();
-    }
+    if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
+        return Ok();
 
     return setPropTryScalarTypedObjectValue(emitted, obj, indexAsByteOffset, elemType, value);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::setElemTryTypedStatic(bool* emitted, MDefinition* object,
                                   MDefinition* index, MDefinition* value)
 {
@@ -9384,17 +9365,17 @@ IonBuilder::initOrSetElemDense(Temporary
                                                                 newValue, strict);
         store = ins;
         common = ins;
 
         current->add(ins);
     } else {
         MInstruction* initLength = initializedLength(elements);
 
-        id = addBoundsCheck(id, initLength, BoundsCheckKind::IsStore);
+        id = addBoundsCheck(id, initLength);
         bool needsHoleCheck = !packed && hasExtraIndexedProperty;
 
         MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck);
         store = ins;
         common = ins;
 
         current->add(store);
     }
@@ -9432,18 +9413,17 @@ IonBuilder::jsop_setelem_typed(Scalar::T
     MInstruction* idInt32 = MToNumberInt32::New(alloc(), id);
     current->add(idInt32);
     id = idInt32;
 
     // Get length, bounds-check, then get elements, and add all instructions.
     MInstruction* length;
     MInstruction* elements;
     BoundsChecking checking = expectOOB ? SkipBoundsCheck : DoBoundsCheck;
-    addTypedArrayLengthAndData(obj, checking, &id, &length, &elements,
-                               BoundsCheckKind::IsStore);
+    addTypedArrayLengthAndData(obj, checking, &id, &length, &elements);
 
     // Clamp value to [0, 255] for Uint8ClampedArray.
     MDefinition* toWrite = value;
     if (arrayType == Scalar::Uint8Clamped) {
         toWrite = MClampToUint8::New(alloc(), value);
         current->add(toWrite->toInstruction());
     }
 
@@ -12972,17 +12952,17 @@ IonBuilder::inTryDense(bool* emitted, MD
     // Get the elements vector.
     MElements* elements = MElements::New(alloc(), obj);
     current->add(elements);
 
     MInstruction* initLength = initializedLength(elements);
 
     // If there are no holes, speculate the InArray check will not fail.
     if (!needsHoleCheck && !failedBoundsCheck_) {
-        addBoundsCheck(idInt32, initLength, BoundsCheckKind::UnusedIndex);
+        addBoundsCheck(idInt32, initLength);
         pushConstant(BooleanValue(true));
         return Ok();
     }
 
     // Check if id < initLength and elem[id] not a hole.
     MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck);
 
     current->add(ins);
@@ -13317,26 +13297,39 @@ IonBuilder::addMaybeCopyElementsForWrite
     if (!ElementAccessMightBeCopyOnWrite(constraints(), object))
         return object;
     MInstruction* copy = MMaybeCopyElementsForWrite::New(alloc(), object, checkNative);
     current->add(copy);
     return copy;
 }
 
 MInstruction*
-IonBuilder::addBoundsCheck(MDefinition* index, MDefinition* length, BoundsCheckKind kind)
+IonBuilder::addBoundsCheck(MDefinition* index, MDefinition* length)
 {
     MInstruction* check = MBoundsCheck::New(alloc(), index, length);
     current->add(check);
 
     // If a bounds check failed in the past, don't optimize bounds checks.
     if (failedBoundsCheck_)
         check->setNotMovable();
 
-    if (kind == BoundsCheckKind::IsLoad && JitOptions.spectreIndexMasking) {
+    if (JitOptions.spectreIndexMasking) {
+        // Use a separate MIR instruction for the index masking. Doing this as
+        // part of MBoundsCheck would be unsound because bounds checks can be
+        // optimized or eliminated completely. Consider this:
+        //
+        //   for (var i = 0; i < x; i++)
+        //        res = arr[i];
+        //
+        // If we can prove |x < arr.length|, we are able to eliminate the bounds
+        // check, but we should not get rid of the index masking because the
+        // |i < x| branch could still be mispredicted.
+        //
+        // Using a separate instruction lets us eliminate the bounds check
+        // without affecting the index masking.
         check = MSpectreMaskIndex::New(alloc(), check, length);
         current->add(check);
     }
 
     return check;
 }
 
 MInstruction*
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -193,18 +193,17 @@ class IonBuilder
     MInstruction* createNamedLambdaObject(MDefinition* callee, MDefinition* envObj);
     AbortReasonOr<MInstruction*> createCallObject(MDefinition* callee, MDefinition* envObj);
 
     MDefinition* walkEnvironmentChain(unsigned hops);
 
     MInstruction* addConvertElementsToDoubles(MDefinition* elements);
     MDefinition* addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative);
 
-    enum class BoundsCheckKind { IsLoad, IsStore, UnusedIndex };
-    MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length, BoundsCheckKind kind);
+    MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
 
     MInstruction* addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind);
     MInstruction* addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind);
     MInstruction* addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind);
     MInstruction* addSharedTypedArrayGuard(MDefinition* obj);
 
     MInstruction*
     addGuardReceiverPolymorphic(MDefinition* obj, const BaselineInspector::ReceiverVector& receivers);
@@ -373,18 +372,17 @@ class IonBuilder
                                               MDefinition** ownerScaledOffset,
                                               int32_t* ownerByteAdjustment);
     MDefinition* typeObjectForElementFromArrayStructType(MDefinition* typedObj);
     MDefinition* typeObjectForFieldFromStructType(MDefinition* type,
                                                   size_t fieldIndex);
     bool checkTypedObjectIndexInBounds(uint32_t elemSize,
                                        MDefinition* index,
                                        TypedObjectPrediction objTypeDescrs,
-                                       LinearSum* indexAsByteOffset,
-                                       BoundsCheckKind kind);
+                                       LinearSum* indexAsByteOffset);
     AbortReasonOr<Ok> pushDerivedTypedObject(bool* emitted,
                                              MDefinition* obj,
                                              const LinearSum& byteOffset,
                                              TypedObjectPrediction derivedTypeDescrs,
                                              MDefinition* derivedTypeObj);
     AbortReasonOr<Ok> pushScalarLoadFromTypedObject(MDefinition* obj,
                                                     const LinearSum& byteoffset,
                                                     ScalarTypeDescr::Type type);
@@ -462,26 +460,24 @@ class IonBuilder
     // Add instructions to compute a typed array's length and data.  Also
     // optionally convert |*index| into a bounds-checked definition, if
     // requested.
     //
     // If you only need the array's length, use addTypedArrayLength below.
     void addTypedArrayLengthAndData(MDefinition* obj,
                                     BoundsChecking checking,
                                     MDefinition** index,
-                                    MInstruction** length, MInstruction** elements,
-                                    BoundsCheckKind boundsCheckKind);
+                                    MInstruction** length, MInstruction** elements);
 
     // Add an instruction to compute a typed array's length to the current
     // block.  If you also need the typed array's data, use the above method
     // instead.
     MInstruction* addTypedArrayLength(MDefinition* obj) {
         MInstruction* length;
-        addTypedArrayLengthAndData(obj, SkipBoundsCheck, nullptr, &length, nullptr,
-                                   BoundsCheckKind::UnusedIndex);
+        addTypedArrayLengthAndData(obj, SkipBoundsCheck, nullptr, &length, nullptr);
         return length;
     }
 
     AbortReasonOr<Ok> improveThisTypesForCall();
 
     MDefinition* getCallee();
     MDefinition* getAliasedVar(EnvironmentCoordinate ec);
     AbortReasonOr<MDefinition*> addLexicalCheck(MDefinition* input);
@@ -767,17 +763,17 @@ class IonBuilder
                                      unsigned numVectors);
     InliningResult inlineSimdCheck(CallInfo& callInfo, JSNative native, SimdType type);
     InliningResult inlineSimdConvert(CallInfo& callInfo, JSNative native, bool isCast,
                                      SimdType from, SimdType to);
     InliningResult inlineSimdSelect(CallInfo& callInfo, JSNative native, SimdType type);
 
     bool prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType,
                                  MInstruction** elements, MDefinition** index,
-                                 Scalar::Type* arrayType, BoundsCheckKind boundsCheckKind);
+                                 Scalar::Type* arrayType);
     InliningResult inlineSimdLoad(CallInfo& callInfo, JSNative native, SimdType type,
                                   unsigned numElems);
     InliningResult inlineSimdStore(CallInfo& callInfo, JSNative native, SimdType type,
                                    unsigned numElems);
 
     InliningResult inlineSimdAnyAllTrue(CallInfo& callInfo, bool IsAllTrue, JSNative native,
                                         SimdType type);
 
@@ -833,18 +829,17 @@ class IonBuilder
     enum AtomicCheckResult {
         DontCheckAtomicResult,
         DoCheckAtomicResult
     };
 
     bool atomicsMeetsPreconditions(CallInfo& callInfo, Scalar::Type* arrayElementType,
                                    bool* requiresDynamicCheck,
                                    AtomicCheckResult checkResult=DoCheckAtomicResult);
-    void atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements, MDefinition** index,
-                            BoundsCheckKind kind);
+    void atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements, MDefinition** index);
 
     bool testNeedsArgumentCheck(JSFunction* target, CallInfo& callInfo);
 
     AbortReasonOr<MCall*> makeCallHelper(const Maybe<CallTargets>& targets, CallInfo& callInfo);
     AbortReasonOr<Ok> makeCall(const Maybe<CallTargets>& targets, CallInfo& callInfo);
     AbortReasonOr<Ok> makeCall(JSFunction* target, CallInfo& callInfo);
 
     MDefinition* patchInlinedReturn(CallInfo& callInfo, MBasicBlock* exit, MBasicBlock* bottom);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1866,17 +1866,17 @@ IonBuilder::inlineStrCharCodeAt(CallInfo
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* index = MToNumberInt32::New(alloc(), callInfo.getArg(0));
     current->add(index);
 
     MStringLength* length = MStringLength::New(alloc(), callInfo.thisArg());
     current->add(length);
 
-    index = addBoundsCheck(index, length, BoundsCheckKind::IsLoad);
+    index = addBoundsCheck(index, length);
 
     MCharCodeAt* charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index);
     current->add(charCode);
     current->push(charCode);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
@@ -1976,17 +1976,17 @@ IonBuilder::inlineStrCharAt(CallInfo& ca
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* index = MToNumberInt32::New(alloc(), callInfo.getArg(0));
     current->add(index);
 
     MStringLength* length = MStringLength::New(alloc(), callInfo.thisArg());
     current->add(length);
 
-    index = addBoundsCheck(index, length, BoundsCheckKind::IsLoad);
+    index = addBoundsCheck(index, length);
 
     // String.charAt(x) = String.fromCharCode(String.charCodeAt(x))
     MCharCodeAt* charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index);
     current->add(charCode);
 
     MFromCharCode* string = MFromCharCode::New(alloc(), charCode);
     current->add(string);
     current->push(string);
@@ -3319,17 +3319,17 @@ IonBuilder::inlineAtomicsCompareExchange
     bool requiresCheck = false;
     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* elements;
     MDefinition* index;
-    atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsLoad);
+    atomicsCheckBounds(callInfo, &elements, &index);
 
     if (requiresCheck)
         addSharedTypedArrayGuard(callInfo.getArg(0));
 
     MCompareExchangeTypedArrayElement* cas =
         MCompareExchangeTypedArrayElement::New(alloc(), elements, index, arrayType, oldval, newval);
     cas->setResultType(getInlineReturnType());
     current->add(cas);
@@ -3355,17 +3355,17 @@ IonBuilder::inlineAtomicsExchange(CallIn
     bool requiresCheck = false;
     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* elements;
     MDefinition* index;
-    atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsLoad);
+    atomicsCheckBounds(callInfo, &elements, &index);
 
     if (requiresCheck)
         addSharedTypedArrayGuard(callInfo.getArg(0));
 
     MInstruction* exchange =
         MAtomicExchangeTypedArrayElement::New(alloc(), elements, index, value, arrayType);
     exchange->setResultType(getInlineReturnType());
     current->add(exchange);
@@ -3387,17 +3387,17 @@ IonBuilder::inlineAtomicsLoad(CallInfo& 
     bool requiresCheck = false;
     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* elements;
     MDefinition* index;
-    atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsLoad);
+    atomicsCheckBounds(callInfo, &elements, &index);
 
     if (requiresCheck)
         addSharedTypedArrayGuard(callInfo.getArg(0));
 
     MLoadUnboxedScalar* load =
         MLoadUnboxedScalar::New(alloc(), elements, index, arrayType,
                                 DoesRequireMemoryBarrier);
     load->setResultType(getInlineReturnType());
@@ -3439,17 +3439,17 @@ IonBuilder::inlineAtomicsStore(CallInfo&
     bool requiresCheck = false;
     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck, DontCheckAtomicResult))
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* elements;
     MDefinition* index;
-    atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsStore);
+    atomicsCheckBounds(callInfo, &elements, &index);
 
     if (requiresCheck)
         addSharedTypedArrayGuard(callInfo.getArg(0));
 
     MDefinition* toWrite = value;
     if (toWrite->type() != MIRType::Int32) {
         toWrite = MTruncateToInt32::New(alloc(), toWrite);
         current->add(toWrite->toInstruction());
@@ -3483,17 +3483,17 @@ IonBuilder::inlineAtomicsBinop(CallInfo&
 
     callInfo.setImplicitlyUsedUnchecked();
 
     if (requiresCheck)
         addSharedTypedArrayGuard(callInfo.getArg(0));
 
     MInstruction* elements;
     MDefinition* index;
-    atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsLoad);
+    atomicsCheckBounds(callInfo, &elements, &index);
 
     AtomicOp k = AtomicFetchAddOp;
     switch (target) {
       case InlinableNative::AtomicsAdd:
         k = AtomicFetchAddOp;
         break;
       case InlinableNative::AtomicsSub:
         k = AtomicFetchSubOp;
@@ -3580,25 +3580,24 @@ IonBuilder::atomicsMeetsPreconditions(Ca
         return checkResult == DontCheckAtomicResult || getInlineReturnType() == MIRType::Double;
       default:
         // Excludes floating types and Uint8Clamped.
         return false;
     }
 }
 
 void
-IonBuilder::atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements, MDefinition** index,
-                               BoundsCheckKind kind)
+IonBuilder::atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements, MDefinition** index)
 {
     // Perform bounds checking and extract the elements vector.
     MDefinition* obj = callInfo.getArg(0);
     MInstruction* length = nullptr;
     *index = callInfo.getArg(1);
     *elements = nullptr;
-    addTypedArrayLengthAndData(obj, DoBoundsCheck, index, &length, elements, kind);
+    addTypedArrayLengthAndData(obj, DoBoundsCheck, index, &length, elements);
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineIsConstructing(CallInfo& callInfo)
 {
     MOZ_ASSERT(!callInfo.constructing());
     MOZ_ASSERT(callInfo.argc() == 0);
     MOZ_ASSERT(script()->functionNonDelazifying(),
@@ -4303,17 +4302,17 @@ SimdTypeToArrayElementType(SimdType type
       case SimdType::Uint32x4:  return Scalar::Int32x4;
       default:                MOZ_CRASH("unexpected simd type");
     }
 }
 
 bool
 IonBuilder::prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType,
                                     MInstruction** elements, MDefinition** index,
-                                    Scalar::Type* arrayType, BoundsCheckKind boundsCheckKind)
+                                    Scalar::Type* arrayType)
 {
     MDefinition* array = callInfo.getArg(0);
     *index = callInfo.getArg(1);
 
     if (!ElementAccessIsTypedArray(constraints(), array, *index, arrayType))
         return false;
 
     MInstruction* indexAsInt32 = MToNumberInt32::New(alloc(), *index);
@@ -4329,51 +4328,48 @@ IonBuilder::prepareForSimdLoadStore(Call
         // because the bounds check code uses an unsigned comparison.
         MAdd* addedIndex = MAdd::New(alloc(), *index, constant(Int32Value(byteLoadSize - 1)));
         addedIndex->setInt32Specialization();
         current->add(addedIndex);
         indexLoadEnd = addedIndex;
     }
 
     MInstruction* length;
-    addTypedArrayLengthAndData(array, SkipBoundsCheck, index, &length, elements, boundsCheckKind);
+    addTypedArrayLengthAndData(array, SkipBoundsCheck, index, &length, elements);
 
     // If the index+size addition overflows, then indexLoadEnd might be
     // in bounds while the actual index isn't, so we need two bounds checks
     // here.
     if (byteLoadSize > 1) {
-        indexLoadEnd = addBoundsCheck(indexLoadEnd, length, BoundsCheckKind::IsLoad);
+        indexLoadEnd = addBoundsCheck(indexLoadEnd, length);
         auto* sub = MSub::New(alloc(), indexLoadEnd, constant(Int32Value(byteLoadSize - 1)));
         sub->setInt32Specialization();
         current->add(sub);
         *index = sub;
     }
 
-    *index = addBoundsCheck(*index, length, boundsCheckKind);
+    *index = addBoundsCheck(*index, length);
 
     return true;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineSimdLoad(CallInfo& callInfo, JSNative native, SimdType type, unsigned numElems)
 {
     InlineTypedObject* templateObj = nullptr;
     if (!canInlineSimd(callInfo, native, 2, &templateObj))
         return InliningStatus_NotInlined;
 
     Scalar::Type elemType = SimdTypeToArrayElementType(type);
 
     MDefinition* index = nullptr;
     MInstruction* elements = nullptr;
     Scalar::Type arrayType;
-    if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType,
-                                 BoundsCheckKind::IsLoad))
-    {
-        return InliningStatus_NotInlined;
-    }
+    if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType))
+        return InliningStatus_NotInlined;
 
     MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, index, arrayType);
     load->setResultType(SimdTypeToMIRType(type));
     load->setSimdRead(elemType, numElems);
 
     return boxSimd(callInfo, load, templateObj);
 }
 
@@ -4384,21 +4380,18 @@ IonBuilder::inlineSimdStore(CallInfo& ca
     if (!canInlineSimd(callInfo, native, 3, &templateObj))
         return InliningStatus_NotInlined;
 
     Scalar::Type elemType = SimdTypeToArrayElementType(type);
 
     MDefinition* index = nullptr;
     MInstruction* elements = nullptr;
     Scalar::Type arrayType;
-    if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType,
-                                 BoundsCheckKind::IsStore))
-    {
-        return InliningStatus_NotInlined;
-    }
+    if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType))
+        return InliningStatus_NotInlined;
 
     MDefinition* valueToWrite = unboxSimd(callInfo.getArg(2), type);
     MStoreUnboxedScalar* store = MStoreUnboxedScalar::New(alloc(), elements, index,
                                                           valueToWrite, arrayType,
                                                           MStoreUnboxedScalar::TruncateInput);
     store->setSimdWrite(elemType, numElems);
 
     current->add(store);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -9623,17 +9623,18 @@ class MBoundsCheckLower
 
 class MSpectreMaskIndex
   : public MBinaryInstruction,
     public MixPolicy<UnboxedInt32Policy<0>, UnboxedInt32Policy<1>>::Data
 {
     MSpectreMaskIndex(MDefinition* index, MDefinition* length)
       : MBinaryInstruction(classOpcode, index, length)
     {
-        setGuard();
+        // Note: this instruction does not need setGuard(): if there are no uses
+        // it's fine for DCE to eliminate this instruction.
         setMovable();
         MOZ_ASSERT(index->type() == MIRType::Int32);
         MOZ_ASSERT(length->type() == MIRType::Int32);
 
         // Returns the masked index.
         setResultType(MIRType::Int32);
     }