Bug 1445235 part 1 - Also add Spectre mitigations for MBoundsCheck added for stores. r=luke

--HG--
extra : rebase_source : b1ead63e2c8934e7b9a6da722196f242e784a2f8
This commit is contained in:
Jan de Mooij 2018-03-16 12:00:48 +01:00
Родитель d9a734332c
Коммит 2ac711d172
4 изменённых файлов: 56 добавлений и 74 удалений

Просмотреть файл

@ -7893,8 +7893,7 @@ 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);
@ -7921,7 +7920,7 @@ IonBuilder::checkTypedObjectIndexInBounds(uint32_t elemSize,
return false;
}
index = addBoundsCheck(idInt32, length, kind);
index = addBoundsCheck(idInt32, length);
return indexAsByteOffset->add(index, AssertedCast<int32_t>(elemSize));
}
@ -7941,11 +7940,8 @@ IonBuilder::getElemTryScalarElemOfTypedObject(bool* emitted,
MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
LinearSum indexAsByteOffset(alloc());
if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset,
BoundsCheckKind::IsLoad))
{
if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
return Ok();
}
trackOptimizationSuccess();
*emitted = true;
@ -7966,11 +7962,8 @@ IonBuilder::getElemTryReferenceElemOfTypedObject(bool* emitted,
uint32_t elemSize = ReferenceTypeDescr::size(elemType);
LinearSum indexAsByteOffset(alloc());
if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset,
BoundsCheckKind::IsLoad))
{
if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
return Ok();
}
trackOptimizationSuccess();
*emitted = true;
@ -8090,11 +8083,8 @@ IonBuilder::getElemTryComplexElemOfTypedObject(bool* emitted,
MDefinition* elemTypeObj = typeObjectForElementFromArrayStructType(type);
LinearSum indexAsByteOffset(alloc());
if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset,
BoundsCheckKind::IsLoad))
{
if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
return Ok();
}
return pushDerivedTypedObject(emitted, obj, indexAsByteOffset,
elemPrediction, elemTypeObj);
@ -8385,7 +8375,7 @@ IonBuilder::getElemTryString(bool* emitted, MDefinition* obj, MDefinition* index
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);
@ -8427,7 +8417,7 @@ IonBuilder::getElemTryArguments(bool* emitted, MDefinition* obj, MDefinition* in
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();
@ -8515,8 +8505,7 @@ IonBuilder::getElemTryArgumentsInlinedIndex(bool* emitted, MDefinition* obj, MDe
// 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());
@ -8709,7 +8698,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
// 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);
@ -8748,8 +8737,7 @@ 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));
@ -8783,7 +8771,7 @@ IonBuilder::addTypedArrayLengthAndData(MDefinition* obj,
if (index) {
if (checking == DoBoundsCheck)
*index = addBoundsCheck(*index, *length, boundsCheckKind);
*index = addBoundsCheck(*index, *length);
*elements = MConstantElements::New(alloc(), data);
current->add(*elements);
@ -8798,7 +8786,7 @@ IonBuilder::addTypedArrayLengthAndData(MDefinition* obj,
if (index) {
if (checking == DoBoundsCheck)
*index = addBoundsCheck(*index, *length, boundsCheckKind);
*index = addBoundsCheck(*index, *length);
*elements = MTypedArrayElements::New(alloc(), obj);
current->add(*elements);
@ -8880,8 +8868,7 @@ IonBuilder::jsop_getelem_typed(MDefinition* obj, MDefinition* index,
// 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);
@ -9066,11 +9053,8 @@ IonBuilder::setElemTryReferenceElemOfTypedObject(bool* emitted,
uint32_t elemSize = ReferenceTypeDescr::size(elemType);
LinearSum indexAsByteOffset(alloc());
if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset,
BoundsCheckKind::IsStore))
{
if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
return Ok();
}
return setPropTryReferenceTypedObjectValue(emitted, obj, indexAsByteOffset,
elemType, value, nullptr);
@ -9090,11 +9074,8 @@ IonBuilder::setElemTryScalarElemOfTypedObject(bool* emitted,
MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
LinearSum indexAsByteOffset(alloc());
if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset,
BoundsCheckKind::IsStore))
{
if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
return Ok();
}
return setPropTryScalarTypedObjectValue(emitted, obj, indexAsByteOffset, elemType, value);
}
@ -9389,7 +9370,7 @@ IonBuilder::initOrSetElemDense(TemporaryTypeSet::DoubleConversion conversion,
} 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);
@ -9437,8 +9418,7 @@ IonBuilder::jsop_setelem_typed(Scalar::Type arrayType,
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;
@ -12977,7 +12957,7 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
// 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();
}
@ -13322,7 +13302,7 @@ IonBuilder::addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative)
}
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);
@ -13331,7 +13311,20 @@ IonBuilder::addBoundsCheck(MDefinition* index, MDefinition* length, BoundsCheckK
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);
}

Просмотреть файл

@ -199,8 +199,7 @@ class IonBuilder
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);
@ -379,8 +378,7 @@ class IonBuilder
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,
@ -468,16 +466,14 @@ class IonBuilder
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;
}
@ -773,7 +769,7 @@ class IonBuilder
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,
@ -839,8 +835,7 @@ class IonBuilder
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);

Просмотреть файл

@ -1871,7 +1871,7 @@ IonBuilder::inlineStrCharCodeAt(CallInfo& callInfo)
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);
@ -1981,7 +1981,7 @@ IonBuilder::inlineStrCharAt(CallInfo& callInfo)
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);
@ -3324,7 +3324,7 @@ IonBuilder::inlineAtomicsCompareExchange(CallInfo& callInfo)
MInstruction* elements;
MDefinition* index;
atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsLoad);
atomicsCheckBounds(callInfo, &elements, &index);
if (requiresCheck)
addSharedTypedArrayGuard(callInfo.getArg(0));
@ -3360,7 +3360,7 @@ IonBuilder::inlineAtomicsExchange(CallInfo& callInfo)
MInstruction* elements;
MDefinition* index;
atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsLoad);
atomicsCheckBounds(callInfo, &elements, &index);
if (requiresCheck)
addSharedTypedArrayGuard(callInfo.getArg(0));
@ -3392,7 +3392,7 @@ IonBuilder::inlineAtomicsLoad(CallInfo& callInfo)
MInstruction* elements;
MDefinition* index;
atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsLoad);
atomicsCheckBounds(callInfo, &elements, &index);
if (requiresCheck)
addSharedTypedArrayGuard(callInfo.getArg(0));
@ -3444,7 +3444,7 @@ IonBuilder::inlineAtomicsStore(CallInfo& callInfo)
MInstruction* elements;
MDefinition* index;
atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsStore);
atomicsCheckBounds(callInfo, &elements, &index);
if (requiresCheck)
addSharedTypedArrayGuard(callInfo.getArg(0));
@ -3488,7 +3488,7 @@ IonBuilder::inlineAtomicsBinop(CallInfo& callInfo, InlinableNative target)
MInstruction* elements;
MDefinition* index;
atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsLoad);
atomicsCheckBounds(callInfo, &elements, &index);
AtomicOp k = AtomicFetchAddOp;
switch (target) {
@ -3585,15 +3585,14 @@ IonBuilder::atomicsMeetsPreconditions(CallInfo& callInfo, Scalar::Type* arrayTyp
}
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
@ -4308,7 +4307,7 @@ SimdTypeToArrayElementType(SimdType 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);
@ -4334,20 +4333,20 @@ IonBuilder::prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType,
}
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;
}
@ -4364,11 +4363,8 @@ IonBuilder::inlineSimdLoad(CallInfo& callInfo, JSNative native, SimdType type, u
MDefinition* index = nullptr;
MInstruction* elements = nullptr;
Scalar::Type arrayType;
if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType,
BoundsCheckKind::IsLoad))
{
if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType))
return InliningStatus_NotInlined;
}
MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, index, arrayType);
load->setResultType(SimdTypeToMIRType(type));
@ -4389,11 +4385,8 @@ IonBuilder::inlineSimdStore(CallInfo& callInfo, JSNative native, SimdType type,
MDefinition* index = nullptr;
MInstruction* elements = nullptr;
Scalar::Type arrayType;
if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType,
BoundsCheckKind::IsStore))
{
if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType))
return InliningStatus_NotInlined;
}
MDefinition* valueToWrite = unboxSimd(callInfo.getArg(2), type);
MStoreUnboxedScalar* store = MStoreUnboxedScalar::New(alloc(), elements, index,

Просмотреть файл

@ -9705,7 +9705,8 @@ class MSpectreMaskIndex
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);