diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 9e0a5fd44532..14c73f3660ec 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1235,6 +1235,24 @@ void CodeGenerator::visitFloat32ToIntegerInt32(LFloat32ToIntegerInt32* lir) { masm.bind(ool->rejoin()); } +void CodeGenerator::visitAdjustDataViewLength(LAdjustDataViewLength* lir) { + Register output = ToRegister(lir->output()); + MOZ_ASSERT(ToRegister(lir->input()) == output); + + uint32_t byteSize = lir->mir()->byteSize(); + +#ifdef DEBUG + Label ok; + masm.branchTest32(Assembler::NotSigned, output, output, &ok); + masm.assumeUnreachable("Unexpected negative value in LAdjustDataViewLength"); + masm.bind(&ok); +#endif + + Label bail; + masm.branchSub32(Assembler::Signed, Imm32(byteSize - 1), output, &bail); + bailoutFrom(&bail, lir->snapshot()); +} + void CodeGenerator::emitOOLTestObject(Register objreg, Label* ifEmulatesUndefined, Label* ifDoesntEmulateUndefined, diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 984b984bc73b..36bdcbc907e1 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2222,6 +2222,15 @@ void LIRGenerator::visitTruncateToInt32(MTruncateToInt32* truncate) { } } +void LIRGenerator::visitAdjustDataViewLength(MAdjustDataViewLength* ins) { + MDefinition* input = ins->input(); + MOZ_ASSERT(input->type() == MIRType::Int32); + + auto* lir = new (alloc()) LAdjustDataViewLength(useRegisterAtStart(input)); + assignSnapshot(lir, ins->bailoutKind()); + defineReuseInput(lir, ins, 0); +} + void LIRGenerator::visitToBigInt(MToBigInt* ins) { MDefinition* opd = ins->input(); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 87228b6eff56..03d7bda8552c 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -3246,10 +3246,7 @@ class MReturnFromCtor : public MBinaryInstruction, class MToFPInstruction : public MUnaryInstruction, public ToDoublePolicy::Data { public: // Types of values which can be converted. - enum ConversionKind { - NonStringPrimitives, - NumbersOnly - }; + enum ConversionKind { NonStringPrimitives, NumbersOnly }; private: ConversionKind conversion_; @@ -3624,6 +3621,39 @@ class MWasmAnyRefFromJSObject : public MUnaryInstruction, AliasSet getAliasSet() const override { return AliasSet::None(); } }; +// Subtracts (byteSize - 1) from the input value. Bails out if the result is +// negative. This is used to implement bounds checks for DataView accesses. +class MAdjustDataViewLength : public MUnaryInstruction, + public NoTypePolicy::Data { + const uint32_t byteSize_; + + MAdjustDataViewLength(MDefinition* input, uint32_t byteSize) + : MUnaryInstruction(classOpcode, input), byteSize_(byteSize) { + MOZ_ASSERT(input->type() == MIRType::Int32); + MOZ_ASSERT(byteSize > 1); + setResultType(MIRType::Int32); + setMovable(); + setGuard(); + } + + public: + INSTRUCTION_HEADER(AdjustDataViewLength) + TRIVIAL_NEW_WRAPPERS + + uint32_t byteSize() const { return byteSize_; } + + bool congruentTo(const MDefinition* ins) const override { + if (!ins->isAdjustDataViewLength()) { + return false; + } + if (ins->toAdjustDataViewLength()->byteSize() != byteSize()) { + return false; + } + return congruentIfOperandsEqual(ins); + } + AliasSet getAliasSet() const override { return AliasSet::None(); } +}; + class MInt64ToFloatingPoint : public MUnaryInstruction, public NoTypePolicy::Data { bool isUnsigned_; diff --git a/js/src/jit/WarpCacheIRTranspiler.cpp b/js/src/jit/WarpCacheIRTranspiler.cpp index e1607e22a1a7..106109df5614 100644 --- a/js/src/jit/WarpCacheIRTranspiler.cpp +++ b/js/src/jit/WarpCacheIRTranspiler.cpp @@ -2137,25 +2137,10 @@ void WarpCacheIRTranspiler::addDataViewData(MDefinition* obj, Scalar::Type type, // Adjust the length to account for accesses near the end of the dataview. if (size_t byteSize = Scalar::byteSize(type); byteSize > 1) { - // To ensure |0 <= offset && offset + byteSize <= length|, we can either - // emit |BoundsCheck(offset, length)| followed by - // |BoundsCheck(offset + (byteSize - 1), length)|, or alternatively emit - // |BoundsCheck(offset, Max(length - (byteSize - 1), 0))|. The latter should - // result in faster code when LICM moves the length adjustment and also - // ensures Spectre index masking occurs after all bounds checks. - - auto* byteSizeMinusOne = MConstant::New(alloc(), Int32Value(byteSize - 1)); - add(byteSizeMinusOne); - - length = MSub::New(alloc(), length, byteSizeMinusOne, MIRType::Int32); - length->toSub()->setTruncateKind(TruncateKind::Truncate); - add(length); - - // |length| mustn't be negative for MBoundsCheck. - auto* zero = MConstant::New(alloc(), Int32Value(0)); - add(zero); - - length = MMinMax::New(alloc(), length, zero, MIRType::Int32, true); + // To ensure |0 <= offset && offset + byteSize <= length|, first adjust the + // length by subtracting |byteSize - 1| (bailing out if that becomes + // negative). + length = MAdjustDataViewLength::New(alloc(), length, byteSize); add(length); } diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index b767c0b95619..b4e99fa60af4 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -5601,6 +5601,20 @@ class LRest : public LCallInstructionHelper<1, 1, 3> { MRest* mir() const { return mir_->toRest(); } }; +class LAdjustDataViewLength : public LInstructionHelper<1, 1, 0> { + public: + LIR_HEADER(AdjustDataViewLength) + + explicit LAdjustDataViewLength(const LAllocation& input) + : LInstructionHelper(classOpcode) { + setOperand(0, input); + } + + const MAdjustDataViewLength* mir() const { + return mir_->toAdjustDataViewLength(); + } +}; + // Convert a Boolean to an Int64, following ToBigInt. class LBooleanToInt64 : public LInstructionHelper { public: