зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
c23ea6eeb6
Коммит
09ce393b93
|
@ -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:
|
||||
|
|
Загрузка…
Ссылка в новой задаче