Bug 1129202 - Part 7: Add MTypedArrayIndexToInt32 for accessing TypedArrays with any Double index. r=jandem

The definition of canonical numeric index strings means that any access into a TypedArray
with a Double index never reads from the prototype chain. Instead if the Double is not
representable as an Int32, the access is equivalent to an out-of-bounds access. That
means we can substitute any non-Int32 Double index with an arbitrary index which is OOB.
This helps to avoid deoptimising TypedArray accesses with non-Int32 Double values by
handling them as normal out-of-bounds accesses, which are already optimisable.

Differential Revision: https://phabricator.services.mozilla.com/D39041

--HG--
extra : moz-landing-system : lando
This commit is contained in:
André Bargull 2019-10-29 09:38:46 +00:00
Родитель c23ea6eeb6
Коммит 09ce393b93
8 изменённых файлов: 146 добавлений и 17 удалений

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

@ -0,0 +1,20 @@
function f(n) {
const ta = new Int32Array(n);
// When the TypedArray has a zero length, accessing the element at index 0
// should return undefined. That'll lead to including undefined in the
// typeset of |ta[k]|, which in turn triggers to use MTypedArrayIndexToInt32
// for the TypedArray index conversion.
const r = n === 0 ? undefined : 0;
// Math.cbrt always returns a double number.
const k = Math.cbrt(0);
for (var i = 0; i < 10; ++i) {
assertEq(ta[k], r);
}
}
for (var i = 0; i < 2; ++i) {
f(i);
}

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

@ -7720,6 +7720,41 @@ void CodeGenerator::visitTypedArrayElementShift(LTypedArrayElementShift* lir) {
masm.bind(&done);
}
class OutOfLineTypedArrayIndexToInt32
: public OutOfLineCodeBase<CodeGenerator> {
LTypedArrayIndexToInt32* lir_;
public:
explicit OutOfLineTypedArrayIndexToInt32(LTypedArrayIndexToInt32* lir)
: lir_(lir) {}
void accept(CodeGenerator* codegen) override {
codegen->visitOutOfLineTypedArrayIndexToInt32(this);
}
LTypedArrayIndexToInt32* lir() const { return lir_; }
};
void CodeGenerator::visitTypedArrayIndexToInt32(LTypedArrayIndexToInt32* lir) {
FloatRegister index = ToFloatRegister(lir->index());
Register output = ToRegister(lir->output());
auto* ool = new (alloc()) OutOfLineTypedArrayIndexToInt32(lir);
addOutOfLineCode(ool, lir->mir());
static_assert(TypedArrayObject::MAX_BYTE_LENGTH <= INT32_MAX,
"Double exceeding Int32 range can't be in-bounds array access");
masm.convertDoubleToInt32(index, output, ool->entry(), false);
masm.bind(ool->rejoin());
}
void CodeGenerator::visitOutOfLineTypedArrayIndexToInt32(
OutOfLineTypedArrayIndexToInt32* ool) {
// Substitute the invalid index with an arbitrary out-of-bounds index.
masm.move32(Imm32(-1), ToRegister(ool->lir()->output()));
masm.jump(ool->rejoin());
}
void CodeGenerator::visitTypedObjectDescr(LTypedObjectDescr* lir) {
Register obj = ToRegister(lir->object());
Register out = ToRegister(lir->output());

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

@ -63,6 +63,7 @@ class OutOfLineRegExpInstanceOptimizable;
class OutOfLineLambdaArrow;
class OutOfLineNaNToZero;
class OutOfLineZeroIfNaN;
class OutOfLineTypedArrayIndexToInt32;
class CodeGenerator final : public CodeGeneratorSpecific {
void generateArgumentsChecks(bool assert = false);
@ -146,6 +147,9 @@ class CodeGenerator final : public CodeGeneratorSpecific {
void visitOutOfLineNewArray(OutOfLineNewArray* ool);
void visitOutOfLineNewObject(OutOfLineNewObject* ool);
void visitOutOfLineTypedArrayIndexToInt32(
OutOfLineTypedArrayIndexToInt32* ool);
private:
void emitPostWriteBarrier(const LAllocation* obj);
void emitPostWriteBarrier(Register objreg);

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

@ -9027,18 +9027,6 @@ AbortReasonOr<Ok> IonBuilder::getElemTryTypedArray(bool* emitted,
return Ok();
}
// Don't generate a fast path if this pc has seen floating-point
// indexes accessed to avoid repeated bailouts. Unlike
// getElemTryDense, we still generate a fast path if we have seen
// negative indices. We expect code to occasionally generate
// negative indices by accident, but not to use negative indices
// intentionally, because typed arrays always return undefined for
// negative indices. See Bug 1535031.
if (inspector->hasSeenNonIntegerIndex(pc)) {
trackOptimizationOutcome(TrackedOutcome::ArraySeenNonIntegerIndex);
return Ok();
}
// Emit typed getelem variant.
MOZ_TRY(jsop_getelem_typed(obj, index, arrayType));
@ -9569,15 +9557,20 @@ AbortReasonOr<Ok> IonBuilder::jsop_getelem_typed(MDefinition* obj,
// and the instruction is not known to return a double.
bool allowDouble = types->hasType(TypeSet::DoubleType());
// Ensure id is an integer.
MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
current->add(idInt32);
index = idInt32;
if (!maybeUndefined) {
// Assume the index is in range, so that we can hoist the length,
// elements vector and bounds check.
// Ensure the index is an integer. This is a stricter requirement than
// enforcing that it is a TypedArray index via MTypedArrayIndexToInt32,
// because any double which isn't exactly representable as an int32 will
// lead to a bailout. But it's okay to have this stricter requirement here,
// since any non-int32 index is equivalent to an out-of-bounds access, which
// will lead to a bailout anyway.
MInstruction* indexInt32 = MToNumberInt32::New(alloc(), index);
current->add(indexInt32);
index = indexInt32;
// If we are reading in-bounds elements, we can use knowledge about
// the array type to determine the result type, even if the opcode has
// never executed. The known pushed type is only used to distinguish
@ -9600,6 +9593,11 @@ AbortReasonOr<Ok> IonBuilder::jsop_getelem_typed(MDefinition* obj,
load->setResultType(knownType);
return Ok();
} else {
// Ensure the index is a TypedArray index.
auto* indexInt32 = MTypedArrayIndexToInt32::New(alloc(), index);
current->add(indexInt32);
index = indexInt32;
// We need a type barrier if the array's element type has never been
// observed (we've only read out-of-bounds values). Note that for
// Uint32Array, we only check for int32: if allowDouble is false we

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

@ -2898,6 +2898,16 @@ void LIRGenerator::visitTypedArrayElementShift(MTypedArrayElementShift* ins) {
ins);
}
void LIRGenerator::visitTypedArrayIndexToInt32(MTypedArrayIndexToInt32* ins) {
MDefinition* input = ins->input();
if (input->type() == MIRType::Int32) {
redefine(ins, input);
} else {
MOZ_ASSERT(input->type() == MIRType::Double);
define(new (alloc()) LTypedArrayIndexToInt32(useRegister(input)), ins);
}
}
void LIRGenerator::visitTypedObjectDescr(MTypedObjectDescr* ins) {
MOZ_ASSERT(ins->type() == MIRType::Object);
define(new (alloc()) LTypedObjectDescr(useRegisterAtStart(ins->object())),

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

@ -5672,6 +5672,23 @@ MDefinition* MGetFirstDollarIndex::foldsTo(TempAllocator& alloc) {
return MConstant::New(alloc, Int32Value(index));
}
MDefinition* MTypedArrayIndexToInt32::foldsTo(TempAllocator& alloc) {
MDefinition* input = getOperand(0);
if (!input->isConstant() || input->type() != MIRType::Double) {
return this;
}
// Fold constant double representable as int32 to int32.
int32_t ival;
if (!mozilla::NumberEqualsInt32(input->toConstant()->numberToDouble(),
&ival)) {
// If not representable as an int32, this access is equal to an OOB access.
// So replace it with a known int32 value which also produces an OOB access.
ival = -1;
}
return MConstant::New(alloc, Int32Value(ival));
}
bool jit::ElementAccessIsDenseNative(CompilerConstraintList* constraints,
MDefinition* obj, MDefinition* id) {
if (obj->mightBeType(MIRType::String)) {

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

@ -7268,6 +7268,33 @@ class MTypedArrayElementShift : public MUnaryInstruction,
void computeRange(TempAllocator& alloc) override;
};
// Convert the input into an Int32 value for accessing a TypedArray element.
// If the input is non-finite, not an integer, negative, or outside the Int32
// range, produces a value which is known to trigger an out-of-bounds access.
class MTypedArrayIndexToInt32 : public MUnaryInstruction,
public NoTypePolicy::Data {
explicit MTypedArrayIndexToInt32(MDefinition* def)
: MUnaryInstruction(classOpcode, def) {
MOZ_ASSERT(def->type() == MIRType::Int32 || def->type() == MIRType::Double);
setResultType(MIRType::Int32);
setMovable();
}
public:
INSTRUCTION_HEADER(TypedArrayIndexToInt32)
TRIVIAL_NEW_WRAPPERS
MDefinition* foldsTo(TempAllocator& alloc) override;
bool congruentTo(const MDefinition* ins) const override {
return congruentIfOperandsEqual(ins);
}
AliasSet getAliasSet() const override { return AliasSet::None(); }
ALLOW_CLONE(MTypedArrayIndexToInt32)
};
// Load a binary data object's "elements", which is just its opaque
// binary data space. Eventually this should probably be
// unified with `MTypedArrayElements`.

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

@ -3867,6 +3867,24 @@ class LTypedArrayElementShift : public LInstructionHelper<1, 1, 0> {
const LAllocation* object() { return getOperand(0); }
};
// Double to Int32, eligible for accessing into a TypedArray. If the index isn't
// exactly representable as an Int32, produce any Int32 which is equivalent to
// an OOB access into a TypedArray.
class LTypedArrayIndexToInt32 : public LInstructionHelper<1, 1, 0> {
public:
LIR_HEADER(TypedArrayIndexToInt32)
explicit LTypedArrayIndexToInt32(const LAllocation& obj)
: LInstructionHelper(classOpcode) {
setOperand(0, obj);
}
const LAllocation* index() { return getOperand(0); }
const MTypedArrayIndexToInt32* mir() const {
return mir_->toTypedArrayIndexToInt32();
}
};
// Load a typed object's descriptor.
class LTypedObjectDescr : public LInstructionHelper<1, 1, 0> {
public: