diff --git a/js/src/jit-test/tests/warp/non-int32-array-length.js b/js/src/jit-test/tests/warp/non-int32-array-length.js new file mode 100644 index 000000000000..4213f1991c2f --- /dev/null +++ b/js/src/jit-test/tests/warp/non-int32-array-length.js @@ -0,0 +1,10 @@ +function f(arr, len) { + for (var i = 0; i < 2000; i++) { + assertEq(arr.length, len); + } +} +var arr = [0]; +f(arr, 1); + +arr.length = 0xffff_ffff; +f(arr, 0xffff_ffff); diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index 50eba6f9a284..6671c17c5e6c 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -2001,6 +2001,7 @@ bool jit::FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfoArg) { case Bailout_Debugger: case Bailout_SpecificAtomGuard: case Bailout_SpecificSymbolGuard: + case Bailout_NonInt32ArrayLength: // Do nothing. break; diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 78bef2075b05..d2e5e7fc6f0b 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -7640,8 +7640,19 @@ void CodeGenerator::visitArrowNewTarget(LArrowNewTarget* lir) { } void CodeGenerator::visitArrayLength(LArrayLength* lir) { - Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength()); - masm.load32(length, ToRegister(lir->output())); + Register elements = ToRegister(lir->elements()); + Register output = ToRegister(lir->output()); + + Address length(elements, ObjectElements::offsetOfLength()); + masm.load32(length, output); + + // IonBuilder relies on TI knowing the length fits in int32, but Warp needs to + // check this dynamically. + if (JitOptions.warpBuilder) { + Label bail; + masm.branchTest32(Assembler::Signed, output, output, &bail); + bailoutFrom(&bail, lir->snapshot()); + } } static void SetLengthFromIndex(MacroAssembler& masm, const LAllocation* index, diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h index a70b79c01bd2..a97ea0cfc2ab 100644 --- a/js/src/jit/IonTypes.h +++ b/js/src/jit/IonTypes.h @@ -144,6 +144,9 @@ enum BailoutKind { // We hit this code for the first time. Bailout_FirstExecution, + // Array length did not fit in int32. + Bailout_NonInt32ArrayLength, + // END Normal bailouts // Bailouts caused by invalid assumptions based on Baseline code. @@ -246,6 +249,8 @@ inline const char* BailoutKindString(BailoutKind kind) { return "Bailout_Debugger"; case Bailout_FirstExecution: return "Bailout_FirstExecution"; + case Bailout_NonInt32ArrayLength: + return "Bailout_NonInt32ArrayLength"; // Bailouts caused by invalid assumptions. case Bailout_OverflowInvalidate: diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 8ef7e1844838..7f5d67fbbb10 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2996,7 +2996,11 @@ void LIRGenerator::visitPostWriteElementBarrier(MPostWriteElementBarrier* ins) { void LIRGenerator::visitArrayLength(MArrayLength* ins) { MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); - define(new (alloc()) LArrayLength(useRegisterAtStart(ins->elements())), ins); + auto* lir = new (alloc()) LArrayLength(useRegisterAtStart(ins->elements())); + if (JitOptions.warpBuilder) { + assignSnapshot(lir, Bailout_NonInt32ArrayLength); + } + define(lir, ins); } void LIRGenerator::visitSetArrayLength(MSetArrayLength* ins) { diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 9b313a254887..b3df5549c92e 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -1769,10 +1769,12 @@ void MLoadDataViewElement::computeRange(TempAllocator& alloc) { } void MArrayLength::computeRange(TempAllocator& alloc) { - // Array lengths can go up to UINT32_MAX, but we only create MArrayLength + // Array lengths can go up to UINT32_MAX. IonBuilder only creates MArrayLength // nodes when the value is known to be int32 (see the - // OBJECT_FLAG_LENGTH_OVERFLOW flag). - setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX)); + // OBJECT_FLAG_LENGTH_OVERFLOW flag). WarpBuilder does a dynamic check and we + // have to return the range pre-bailouts, so use UINT32_MAX for Warp. + uint32_t max = JitOptions.warpBuilder ? UINT32_MAX : INT32_MAX; + setRange(Range::NewUInt32Range(alloc, 0, max)); } void MInitializedLength::computeRange(TempAllocator& alloc) {