diff --git a/src/compiler/arm64/instruction-selector-arm64.cc b/src/compiler/arm64/instruction-selector-arm64.cc index dd9914b8bc..c3a5362e59 100644 --- a/src/compiler/arm64/instruction-selector-arm64.cc +++ b/src/compiler/arm64/instruction-selector-arm64.cc @@ -1203,6 +1203,7 @@ void InstructionSelector::VisitWord64Ror(Node* node) { V(TruncateFloat32ToUint32, kArm64Float32ToUint32) \ V(ChangeFloat64ToUint32, kArm64Float64ToUint32) \ V(ChangeFloat64ToUint64, kArm64Float64ToUint64) \ + V(TruncateFloat64ToInt64, kArm64Float64ToInt64) \ V(TruncateFloat64ToUint32, kArm64Float64ToUint32) \ V(TruncateFloat64ToFloat32, kArm64Float64ToFloat32) \ V(TruncateFloat64ToWord32, kArchTruncateDoubleToI) \ diff --git a/src/compiler/effect-control-linearizer.cc b/src/compiler/effect-control-linearizer.cc index ce702bd298..f03aa6ec12 100644 --- a/src/compiler/effect-control-linearizer.cc +++ b/src/compiler/effect-control-linearizer.cc @@ -676,9 +676,6 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, case IrOpcode::kTruncateTaggedToFloat64: result = LowerTruncateTaggedToFloat64(node); break; - case IrOpcode::kCheckBounds: - result = LowerCheckBounds(node, frame_state); - break; case IrOpcode::kPoisonIndex: result = LowerPoisonIndex(node); break; @@ -739,12 +736,18 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, case IrOpcode::kCheckedInt64ToTaggedSigned: result = LowerCheckedInt64ToTaggedSigned(node, frame_state); break; + case IrOpcode::kCheckedUint32Bounds: + result = LowerCheckedUint32Bounds(node, frame_state); + break; case IrOpcode::kCheckedUint32ToInt32: result = LowerCheckedUint32ToInt32(node, frame_state); break; case IrOpcode::kCheckedUint32ToTaggedSigned: result = LowerCheckedUint32ToTaggedSigned(node, frame_state); break; + case IrOpcode::kCheckedUint64Bounds: + result = LowerCheckedUint64Bounds(node, frame_state); + break; case IrOpcode::kCheckedUint64ToInt32: result = LowerCheckedUint64ToInt32(node, frame_state); break; @@ -754,6 +757,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, case IrOpcode::kCheckedFloat64ToInt32: result = LowerCheckedFloat64ToInt32(node, frame_state); break; + case IrOpcode::kCheckedFloat64ToInt64: + result = LowerCheckedFloat64ToInt64(node, frame_state); + break; case IrOpcode::kCheckedTaggedSignedToInt32: if (frame_state == nullptr) { FATAL("No frame state (zapped by #%d: %s)", frame_state_zapper_->id(), @@ -764,6 +770,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, case IrOpcode::kCheckedTaggedToInt32: result = LowerCheckedTaggedToInt32(node, frame_state); break; + case IrOpcode::kCheckedTaggedToInt64: + result = LowerCheckedTaggedToInt64(node, frame_state); + break; case IrOpcode::kCheckedTaggedToFloat64: result = LowerCheckedTaggedToFloat64(node, frame_state); break; @@ -1427,17 +1436,6 @@ Node* EffectControlLinearizer::LowerTruncateTaggedToFloat64(Node* node) { return done.PhiAt(0); } -Node* EffectControlLinearizer::LowerCheckBounds(Node* node, Node* frame_state) { - Node* index = node->InputAt(0); - Node* limit = node->InputAt(1); - const CheckParameters& params = CheckParametersOf(node->op()); - - Node* check = __ Uint32LessThan(index, limit); - __ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds, params.feedback(), check, - frame_state, IsSafetyCheck::kCriticalSafetyCheck); - return index; -} - Node* EffectControlLinearizer::LowerPoisonIndex(Node* node) { Node* index = node->InputAt(0); if (mask_array_index_ == kMaskArrayIndex) { @@ -2055,6 +2053,18 @@ Node* EffectControlLinearizer::LowerCheckedInt64ToTaggedSigned( } } +Node* EffectControlLinearizer::LowerCheckedUint32Bounds(Node* node, + Node* frame_state) { + Node* index = node->InputAt(0); + Node* limit = node->InputAt(1); + const CheckParameters& params = CheckParametersOf(node->op()); + + Node* check = __ Uint32LessThan(index, limit); + __ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds, params.feedback(), check, + frame_state, IsSafetyCheck::kCriticalSafetyCheck); + return index; +} + Node* EffectControlLinearizer::LowerCheckedUint32ToInt32(Node* node, Node* frame_state) { Node* value = node->InputAt(0); @@ -2075,6 +2085,18 @@ Node* EffectControlLinearizer::LowerCheckedUint32ToTaggedSigned( return ChangeUint32ToSmi(value); } +Node* EffectControlLinearizer::LowerCheckedUint64Bounds(Node* node, + Node* frame_state) { + CheckParameters const& params = CheckParametersOf(node->op()); + Node* const index = node->InputAt(0); + Node* const limit = node->InputAt(1); + + Node* check = __ Uint64LessThan(index, limit); + __ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds, params.feedback(), check, + frame_state, IsSafetyCheck::kCriticalSafetyCheck); + return index; +} + Node* EffectControlLinearizer::LowerCheckedUint64ToInt32(Node* node, Node* frame_state) { Node* value = node->InputAt(0); @@ -2137,6 +2159,45 @@ Node* EffectControlLinearizer::LowerCheckedFloat64ToInt32(Node* node, frame_state); } +Node* EffectControlLinearizer::BuildCheckedFloat64ToInt64( + CheckForMinusZeroMode mode, const VectorSlotPair& feedback, Node* value, + Node* frame_state) { + Node* value64 = __ TruncateFloat64ToInt64(value); + Node* check_same = __ Float64Equal(value, __ ChangeInt64ToFloat64(value64)); + __ DeoptimizeIfNot(DeoptimizeReason::kLostPrecisionOrNaN, feedback, + check_same, frame_state); + + if (mode == CheckForMinusZeroMode::kCheckForMinusZero) { + // Check if {value} is -0. + auto if_zero = __ MakeDeferredLabel(); + auto check_done = __ MakeLabel(); + + Node* check_zero = __ Word64Equal(value64, __ Int64Constant(0)); + __ GotoIf(check_zero, &if_zero); + __ Goto(&check_done); + + __ Bind(&if_zero); + // In case of 0, we need to check the high bits for the IEEE -0 pattern. + Node* check_negative = __ Int32LessThan(__ Float64ExtractHighWord32(value), + __ Int32Constant(0)); + __ DeoptimizeIf(DeoptimizeReason::kMinusZero, feedback, check_negative, + frame_state); + __ Goto(&check_done); + + __ Bind(&check_done); + } + return value64; +} + +Node* EffectControlLinearizer::LowerCheckedFloat64ToInt64(Node* node, + Node* frame_state) { + const CheckMinusZeroParameters& params = + CheckMinusZeroParametersOf(node->op()); + Node* value = node->InputAt(0); + return BuildCheckedFloat64ToInt64(params.mode(), params.feedback(), value, + frame_state); +} + Node* EffectControlLinearizer::LowerCheckedTaggedSignedToInt32( Node* node, Node* frame_state) { Node* value = node->InputAt(0); @@ -2177,6 +2238,36 @@ Node* EffectControlLinearizer::LowerCheckedTaggedToInt32(Node* node, return done.PhiAt(0); } +Node* EffectControlLinearizer::LowerCheckedTaggedToInt64(Node* node, + Node* frame_state) { + const CheckMinusZeroParameters& params = + CheckMinusZeroParametersOf(node->op()); + Node* value = node->InputAt(0); + + auto if_not_smi = __ MakeDeferredLabel(); + auto done = __ MakeLabel(MachineRepresentation::kWord64); + + Node* check = ObjectIsSmi(value); + __ GotoIfNot(check, &if_not_smi); + // In the Smi case, just convert to int64. + __ Goto(&done, ChangeSmiToInt64(value)); + + // In the non-Smi case, check the heap numberness, load the number and convert + // to int64. + __ Bind(&if_not_smi); + Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); + Node* check_map = __ WordEqual(value_map, __ HeapNumberMapConstant()); + __ DeoptimizeIfNot(DeoptimizeReason::kNotAHeapNumber, params.feedback(), + check_map, frame_state); + Node* vfalse = __ LoadField(AccessBuilder::ForHeapNumberValue(), value); + vfalse = BuildCheckedFloat64ToInt64(params.mode(), params.feedback(), vfalse, + frame_state); + __ Goto(&done, vfalse); + + __ Bind(&done); + return done.PhiAt(0); +} + Node* EffectControlLinearizer::BuildCheckedHeapNumberOrOddballToFloat64( CheckTaggedInputMode mode, const VectorSlotPair& feedback, Node* value, Node* frame_state) { @@ -4112,17 +4203,21 @@ Node* EffectControlLinearizer::LowerLoadDataViewElement(Node* node) { ExternalArrayType element_type = ExternalArrayTypeOf(node->op()); Node* buffer = node->InputAt(0); Node* storage = node->InputAt(1); - Node* index = node->InputAt(2); - Node* is_little_endian = node->InputAt(3); + Node* byte_offset = node->InputAt(2); + Node* index = node->InputAt(3); + Node* is_little_endian = node->InputAt(4); // We need to keep the {buffer} alive so that the GC will not release the // ArrayBuffer (if there's any) as long as we are still operating on it. __ Retain(buffer); + // Compute the effective offset. + Node* offset = __ IntAdd(byte_offset, index); + MachineType const machine_type = AccessBuilder::ForTypedArrayElement(element_type, true).machine_type; - Node* value = __ LoadUnaligned(machine_type, storage, index); + Node* value = __ LoadUnaligned(machine_type, storage, offset); auto big_endian = __ MakeLabel(); auto done = __ MakeLabel(machine_type.representation()); @@ -4153,14 +4248,18 @@ void EffectControlLinearizer::LowerStoreDataViewElement(Node* node) { ExternalArrayType element_type = ExternalArrayTypeOf(node->op()); Node* buffer = node->InputAt(0); Node* storage = node->InputAt(1); - Node* index = node->InputAt(2); - Node* value = node->InputAt(3); - Node* is_little_endian = node->InputAt(4); + Node* byte_offset = node->InputAt(2); + Node* index = node->InputAt(3); + Node* value = node->InputAt(4); + Node* is_little_endian = node->InputAt(5); // We need to keep the {buffer} alive so that the GC will not release the // ArrayBuffer (if there's any) as long as we are still operating on it. __ Retain(buffer); + // Compute the effective offset. + Node* offset = __ IntAdd(byte_offset, index); + MachineType const machine_type = AccessBuilder::ForTypedArrayElement(element_type, true).machine_type; @@ -4186,7 +4285,7 @@ void EffectControlLinearizer::LowerStoreDataViewElement(Node* node) { } __ Bind(&done); - __ StoreUnaligned(machine_type.representation(), storage, index, + __ StoreUnaligned(machine_type.representation(), storage, offset, done.PhiAt(0)); } diff --git a/src/compiler/effect-control-linearizer.h b/src/compiler/effect-control-linearizer.h index 040d9f7ca6..cd7363a35d 100644 --- a/src/compiler/effect-control-linearizer.h +++ b/src/compiler/effect-control-linearizer.h @@ -61,7 +61,6 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { Node* LowerChangeTaggedToUint32(Node* node); Node* LowerChangeTaggedToInt64(Node* node); Node* LowerChangeTaggedToTaggedSigned(Node* node); - Node* LowerCheckBounds(Node* node, Node* frame_state); Node* LowerPoisonIndex(Node* node); Node* LowerCheckInternalizedString(Node* node, Node* frame_state); void LowerCheckMaps(Node* node, Node* frame_state); @@ -82,13 +81,17 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { Node* LowerCheckedInt32ToTaggedSigned(Node* node, Node* frame_state); Node* LowerCheckedInt64ToInt32(Node* node, Node* frame_state); Node* LowerCheckedInt64ToTaggedSigned(Node* node, Node* frame_state); + Node* LowerCheckedUint32Bounds(Node* node, Node* frame_state); Node* LowerCheckedUint32ToInt32(Node* node, Node* frame_state); Node* LowerCheckedUint32ToTaggedSigned(Node* node, Node* frame_state); + Node* LowerCheckedUint64Bounds(Node* node, Node* frame_state); Node* LowerCheckedUint64ToInt32(Node* node, Node* frame_state); Node* LowerCheckedUint64ToTaggedSigned(Node* node, Node* frame_state); Node* LowerCheckedFloat64ToInt32(Node* node, Node* frame_state); + Node* LowerCheckedFloat64ToInt64(Node* node, Node* frame_state); Node* LowerCheckedTaggedSignedToInt32(Node* node, Node* frame_state); Node* LowerCheckedTaggedToInt32(Node* node, Node* frame_state); + Node* LowerCheckedTaggedToInt64(Node* node, Node* frame_state); Node* LowerCheckedTaggedToFloat64(Node* node, Node* frame_state); Node* LowerCheckedTaggedToTaggedSigned(Node* node, Node* frame_state); Node* LowerCheckedTaggedToTaggedPointer(Node* node, Node* frame_state); @@ -183,6 +186,9 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { Node* BuildCheckedFloat64ToInt32(CheckForMinusZeroMode mode, const VectorSlotPair& feedback, Node* value, Node* frame_state); + Node* BuildCheckedFloat64ToInt64(CheckForMinusZeroMode mode, + const VectorSlotPair& feedback, Node* value, + Node* frame_state); Node* BuildCheckedHeapNumberOrOddballToFloat64(CheckTaggedInputMode mode, const VectorSlotPair& feedback, Node* value, diff --git a/src/compiler/graph-assembler.h b/src/compiler/graph-assembler.h index 93795bacd2..8cb35e4a90 100644 --- a/src/compiler/graph-assembler.h +++ b/src/compiler/graph-assembler.h @@ -29,6 +29,7 @@ namespace compiler { V(ChangeFloat64ToUint32) \ V(TruncateInt64ToInt32) \ V(RoundFloat64ToInt32) \ + V(TruncateFloat64ToInt64) \ V(TruncateFloat64ToWord32) \ V(Float64ExtractLowWord32) \ V(Float64ExtractHighWord32) \ diff --git a/src/compiler/instruction-selector.cc b/src/compiler/instruction-selector.cc index 8775d00e5a..3c8c202a6d 100644 --- a/src/compiler/instruction-selector.cc +++ b/src/compiler/instruction-selector.cc @@ -1493,6 +1493,8 @@ void InstructionSelector::VisitNode(Node* node) { } else { return EmitIdentity(node); } + case IrOpcode::kTruncateFloat64ToInt64: + return MarkAsWord64(node), VisitTruncateFloat64ToInt64(node); case IrOpcode::kTruncateFloat64ToUint32: return MarkAsWord32(node), VisitTruncateFloat64ToUint32(node); case IrOpcode::kTruncateFloat32ToInt32: @@ -2311,6 +2313,10 @@ void InstructionSelector::VisitChangeFloat64ToUint64(Node* node) { UNIMPLEMENTED(); } +void InstructionSelector::VisitTruncateFloat64ToInt64(Node* node) { + UNIMPLEMENTED(); +} + void InstructionSelector::VisitTryTruncateFloat32ToInt64(Node* node) { UNIMPLEMENTED(); } diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc index 06f002cdaf..0f48064ad6 100644 --- a/src/compiler/js-call-reducer.cc +++ b/src/compiler/js-call-reducer.cc @@ -6802,21 +6802,15 @@ Reduction JSCallReducer::ReduceDataViewAccess(Node* node, DataViewAccess access, // Only do stuff if the {receiver} is really a DataView. if (NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect, JS_DATA_VIEW_TYPE)) { + Node* byte_offset; + // Check that the {offset} is within range for the {receiver}. HeapObjectMatcher m(receiver); if (m.HasValue()) { // We only deal with DataViews here whose [[ByteLength]] is at least - // {element_size} and less than 2^31-{element_size}. + // {element_size}, as for all other DataViews it'll be out-of-bounds. Handle dataview = Handle::cast(m.Value()); - if (dataview->byte_length() < element_size || - dataview->byte_length() - element_size > kMaxInt) { - return NoChange(); - } - - // The {receiver}s [[ByteOffset]] must be within Unsigned31 range. - if (dataview->byte_offset() > kMaxInt) { - return NoChange(); - } + if (dataview->byte_length() < element_size) return NoChange(); // Check that the {offset} is within range of the {byte_length}. Node* byte_length = @@ -6825,43 +6819,38 @@ Reduction JSCallReducer::ReduceDataViewAccess(Node* node, DataViewAccess access, graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset, byte_length, effect, control); - // Add the [[ByteOffset]] to compute the effective offset. - Node* byte_offset = jsgraph()->Constant(dataview->byte_offset()); - offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset); + // Load the [[ByteOffset]] from the {dataview}. + byte_offset = jsgraph()->Constant(dataview->byte_offset()); } else { // We only deal with DataViews here that have Smi [[ByteLength]]s. Node* byte_length = effect = graph()->NewNode(simplified()->LoadField( AccessBuilder::ForJSArrayBufferViewByteLength()), receiver, effect, control); - byte_length = effect = graph()->NewNode( - simplified()->CheckSmi(p.feedback()), byte_length, effect, control); + + if (element_size > 1) { + // For non-byte accesses we also need to check that the {offset} + // plus the {element_size}-1 fits within the given {byte_length}. + // So to keep this as a single check on the {offset}, we subtract + // the {element_size}-1 from the {byte_length} here (clamped to + // positive safe integer range), and perform a check against that + // with the {offset} below. + byte_length = graph()->NewNode( + simplified()->NumberMax(), jsgraph()->ZeroConstant(), + graph()->NewNode(simplified()->NumberSubtract(), byte_length, + jsgraph()->Constant(element_size - 1))); + } // Check that the {offset} is within range of the {byte_length}. offset = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset, byte_length, effect, control); - if (element_size > 0) { - // For non-byte accesses we also need to check that the {offset} - // plus the {element_size}-1 fits within the given {byte_length}. - Node* end_offset = - graph()->NewNode(simplified()->NumberAdd(), offset, - jsgraph()->Constant(element_size - 1)); - effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), - end_offset, byte_length, effect, control); - } - - // The {receiver}s [[ByteOffset]] also needs to be a (positive) Smi. - Node* byte_offset = effect = + // Also load the [[ByteOffset]] from the {receiver}. + byte_offset = effect = graph()->NewNode(simplified()->LoadField( AccessBuilder::ForJSArrayBufferViewByteOffset()), receiver, effect, control); - byte_offset = effect = graph()->NewNode( - simplified()->CheckSmi(p.feedback()), byte_offset, effect, control); - - // Compute the buffer index at which we'll read. - offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset); } // Coerce {is_little_endian} to boolean. @@ -6911,15 +6900,17 @@ Reduction JSCallReducer::ReduceDataViewAccess(Node* node, DataViewAccess access, switch (access) { case DataViewAccess::kGet: // Perform the load. - value = effect = graph()->NewNode( - simplified()->LoadDataViewElement(element_type), buffer, - backing_store, offset, is_little_endian, effect, control); + value = effect = + graph()->NewNode(simplified()->LoadDataViewElement(element_type), + buffer, backing_store, byte_offset, offset, + is_little_endian, effect, control); break; case DataViewAccess::kSet: // Perform the store. - effect = graph()->NewNode( - simplified()->StoreDataViewElement(element_type), buffer, - backing_store, offset, value, is_little_endian, effect, control); + effect = + graph()->NewNode(simplified()->StoreDataViewElement(element_type), + buffer, backing_store, byte_offset, offset, value, + is_little_endian, effect, control); value = jsgraph()->UndefinedConstant(); break; } diff --git a/src/compiler/machine-operator.cc b/src/compiler/machine-operator.cc index f3fcd7758c..f520cfd892 100644 --- a/src/compiler/machine-operator.cc +++ b/src/compiler/machine-operator.cc @@ -147,6 +147,7 @@ MachineType AtomicOpType(Operator const* op) { V(ChangeFloat64ToInt64, Operator::kNoProperties, 1, 0, 1) \ V(ChangeFloat64ToUint32, Operator::kNoProperties, 1, 0, 1) \ V(ChangeFloat64ToUint64, Operator::kNoProperties, 1, 0, 1) \ + V(TruncateFloat64ToInt64, Operator::kNoProperties, 1, 0, 1) \ V(TruncateFloat64ToUint32, Operator::kNoProperties, 1, 0, 1) \ V(TruncateFloat32ToInt32, Operator::kNoProperties, 1, 0, 1) \ V(TruncateFloat32ToUint32, Operator::kNoProperties, 1, 0, 1) \ diff --git a/src/compiler/machine-operator.h b/src/compiler/machine-operator.h index a34360a375..5ca3a1faa5 100644 --- a/src/compiler/machine-operator.h +++ b/src/compiler/machine-operator.h @@ -322,6 +322,7 @@ class V8_EXPORT_PRIVATE MachineOperatorBuilder final const Operator* ChangeFloat64ToInt64(); const Operator* ChangeFloat64ToUint32(); // narrowing const Operator* ChangeFloat64ToUint64(); + const Operator* TruncateFloat64ToInt64(); const Operator* TruncateFloat64ToUint32(); const Operator* TruncateFloat32ToInt32(); const Operator* TruncateFloat32ToUint32(); diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index 31e6db3f97..aa4caa657d 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -262,15 +262,19 @@ V(CheckedInt32ToTaggedSigned) \ V(CheckedInt64ToInt32) \ V(CheckedInt64ToTaggedSigned) \ + V(CheckedUint32Bounds) \ V(CheckedUint32ToInt32) \ V(CheckedUint32ToTaggedSigned) \ + V(CheckedUint64Bounds) \ V(CheckedUint64ToInt32) \ V(CheckedUint64ToTaggedSigned) \ V(CheckedFloat64ToInt32) \ + V(CheckedFloat64ToInt64) \ V(CheckedTaggedSignedToInt32) \ V(CheckedTaggedToInt32) \ V(CheckedTruncateTaggedToWord32) \ V(CheckedTaggedToFloat64) \ + V(CheckedTaggedToInt64) \ V(CheckedTaggedToTaggedSigned) \ V(CheckedTaggedToTaggedPointer) @@ -624,6 +628,7 @@ V(ChangeFloat64ToUint32) \ V(ChangeFloat64ToUint64) \ V(Float64SilenceNaN) \ + V(TruncateFloat64ToInt64) \ V(TruncateFloat64ToUint32) \ V(TruncateFloat32ToInt32) \ V(TruncateFloat32ToUint32) \ diff --git a/src/compiler/operation-typer.cc b/src/compiler/operation-typer.cc index 382c08de70..317bbe1888 100644 --- a/src/compiler/operation-typer.cc +++ b/src/compiler/operation-typer.cc @@ -1206,16 +1206,13 @@ Type OperationTyper::StrictEqual(Type lhs, Type rhs) { } Type OperationTyper::CheckBounds(Type index, Type length) { - DCHECK(length.Is(Type::Unsigned31())); + DCHECK(length.Is(cache_.kPositiveSafeInteger)); + if (length.Is(cache_.kSingletonZero)) return Type::None(); + Type mask = Type::Range(0.0, length.Max() - 1, zone()); if (index.Maybe(Type::MinusZero())) { index = Type::Union(index, cache_.kSingletonZero, zone()); } - index = Type::Intersect(index, Type::Integral32(), zone()); - if (index.IsNone() || length.IsNone()) return Type::None(); - double min = std::max(index.Min(), 0.0); - double max = std::min(index.Max(), length.Max() - 1); - if (max < min) return Type::None(); - return Type::Range(min, max, zone()); + return Type::Intersect(index, mask, zone()); } Type OperationTyper::CheckFloat64Hole(Type type) { diff --git a/src/compiler/redundancy-elimination.cc b/src/compiler/redundancy-elimination.cc index 4352c1d2b8..b91b82e766 100644 --- a/src/compiler/redundancy-elimination.cc +++ b/src/compiler/redundancy-elimination.cc @@ -154,13 +154,17 @@ bool CheckSubsumes(Node const* a, Node const* b) { case IrOpcode::kCheckedTaggedSignedToInt32: case IrOpcode::kCheckedTaggedToTaggedPointer: case IrOpcode::kCheckedTaggedToTaggedSigned: + case IrOpcode::kCheckedUint32Bounds: case IrOpcode::kCheckedUint32ToInt32: case IrOpcode::kCheckedUint32ToTaggedSigned: + case IrOpcode::kCheckedUint64Bounds: case IrOpcode::kCheckedUint64ToInt32: case IrOpcode::kCheckedUint64ToTaggedSigned: break; case IrOpcode::kCheckedFloat64ToInt32: - case IrOpcode::kCheckedTaggedToInt32: { + case IrOpcode::kCheckedFloat64ToInt64: + case IrOpcode::kCheckedTaggedToInt32: + case IrOpcode::kCheckedTaggedToInt64: { const CheckMinusZeroParameters& ap = CheckMinusZeroParametersOf(a->op()); const CheckMinusZeroParameters& bp = diff --git a/src/compiler/representation-change.cc b/src/compiler/representation-change.cc index b141cad773..5ea7959070 100644 --- a/src/compiler/representation-change.cc +++ b/src/compiler/representation-change.cc @@ -192,8 +192,10 @@ Node* RepresentationChanger::GetRepresentationFor( return GetWord32RepresentationFor(node, output_rep, output_type, use_node, use_info); case MachineRepresentation::kWord64: - DCHECK_EQ(TypeCheckKind::kNone, use_info.type_check()); - return GetWord64RepresentationFor(node, output_rep, output_type); + DCHECK(use_info.type_check() == TypeCheckKind::kNone || + use_info.type_check() == TypeCheckKind::kSigned64); + return GetWord64RepresentationFor(node, output_rep, output_type, use_node, + use_info); case MachineRepresentation::kSimd128: case MachineRepresentation::kNone: return node; @@ -948,7 +950,8 @@ Node* RepresentationChanger::GetBitRepresentationFor( } Node* RepresentationChanger::GetWord64RepresentationFor( - Node* node, MachineRepresentation output_rep, Type output_type) { + Node* node, MachineRepresentation output_rep, Type output_type, + Node* use_node, UseInfo use_info) { // Eagerly fold representation changes for constants. switch (node->opcode()) { case IrOpcode::kInt32Constant: @@ -995,6 +998,14 @@ Node* RepresentationChanger::GetWord64RepresentationFor( // float32 -> float64 -> uint64 node = InsertChangeFloat32ToFloat64(node); op = machine()->ChangeFloat64ToUint64(); + } else if (use_info.type_check() == TypeCheckKind::kSigned64) { + // float32 -> float64 -> int64 + node = InsertChangeFloat32ToFloat64(node); + op = simplified()->CheckedFloat64ToInt64( + output_type.Maybe(Type::MinusZero()) + ? use_info.minus_zero_check() + : CheckForMinusZeroMode::kDontCheckForMinusZero, + use_info.feedback()); } else { return TypeError(node, output_rep, output_type, MachineRepresentation::kWord64); @@ -1004,6 +1015,12 @@ Node* RepresentationChanger::GetWord64RepresentationFor( op = machine()->ChangeFloat64ToInt64(); } else if (output_type.Is(cache_.kUint64)) { op = machine()->ChangeFloat64ToUint64(); + } else if (use_info.type_check() == TypeCheckKind::kSigned64) { + op = simplified()->CheckedFloat64ToInt64( + output_type.Maybe(Type::MinusZero()) + ? use_info.minus_zero_check() + : CheckForMinusZeroMode::kDontCheckForMinusZero, + use_info.feedback()); } else { return TypeError(node, output_rep, output_type, MachineRepresentation::kWord64); @@ -1018,6 +1035,12 @@ Node* RepresentationChanger::GetWord64RepresentationFor( } else if (CanBeTaggedPointer(output_rep)) { if (output_type.Is(cache_.kInt64)) { op = simplified()->ChangeTaggedToInt64(); + } else if (use_info.type_check() == TypeCheckKind::kSigned64) { + op = simplified()->CheckedTaggedToInt64( + output_type.Maybe(Type::MinusZero()) + ? use_info.minus_zero_check() + : CheckForMinusZeroMode::kDontCheckForMinusZero, + use_info.feedback()); } else { return TypeError(node, output_rep, output_type, MachineRepresentation::kWord64); @@ -1026,7 +1049,7 @@ Node* RepresentationChanger::GetWord64RepresentationFor( return TypeError(node, output_rep, output_type, MachineRepresentation::kWord64); } - return jsgraph()->graph()->NewNode(op, node); + return InsertConversion(node, op, use_node); } const Operator* RepresentationChanger::Int32OperatorFor( diff --git a/src/compiler/representation-change.h b/src/compiler/representation-change.h index 673c062d94..604e7ea3c1 100644 --- a/src/compiler/representation-change.h +++ b/src/compiler/representation-change.h @@ -106,6 +106,7 @@ enum class TypeCheckKind : uint8_t { kNone, kSignedSmall, kSigned32, + kSigned64, kNumber, kNumberOrOddball, kHeapObject @@ -119,6 +120,8 @@ inline std::ostream& operator<<(std::ostream& os, TypeCheckKind type_check) { return os << "SignedSmall"; case TypeCheckKind::kSigned32: return os << "Signed32"; + case TypeCheckKind::kSigned64: + return os << "Signed64"; case TypeCheckKind::kNumber: return os << "Number"; case TypeCheckKind::kNumberOrOddball: @@ -208,6 +211,12 @@ class UseInfo { Truncation::Any(identify_zeros), TypeCheckKind::kSigned32, feedback); } + static UseInfo CheckedSigned64AsWord64(IdentifyZeros identify_zeros, + const VectorSlotPair& feedback) { + return UseInfo(MachineRepresentation::kWord64, + Truncation::Any(identify_zeros), TypeCheckKind::kSigned64, + feedback); + } static UseInfo CheckedNumberAsFloat64(IdentifyZeros identify_zeros, const VectorSlotPair& feedback) { return UseInfo(MachineRepresentation::kFloat64, @@ -325,7 +334,8 @@ class RepresentationChanger final { Node* GetBitRepresentationFor(Node* node, MachineRepresentation output_rep, Type output_type); Node* GetWord64RepresentationFor(Node* node, MachineRepresentation output_rep, - Type output_type); + Type output_type, Node* use_node, + UseInfo use_info); Node* TypeError(Node* node, MachineRepresentation output_rep, Type output_type, MachineRepresentation use); Node* MakeTruncatedInt32Constant(double value); diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc index 9b2db05621..e4e77a03bf 100644 --- a/src/compiler/simplified-lowering.cc +++ b/src/compiler/simplified-lowering.cc @@ -1520,6 +1520,53 @@ class RepresentationSelector { } } + void VisitCheckBounds(Node* node, SimplifiedLowering* lowering) { + CheckParameters const& p = CheckParametersOf(node->op()); + Type const index_type = TypeOf(node->InputAt(0)); + Type const length_type = TypeOf(node->InputAt(1)); + if (length_type.Is(Type::Unsigned31())) { + if (index_type.Is(Type::Integral32OrMinusZero())) { + // Map -0 to 0, and the values in the [-2^31,-1] range to the + // [2^31,2^32-1] range, which will be considered out-of-bounds + // as well, because the {length_type} is limited to Unsigned31. + VisitBinop(node, UseInfo::TruncatingWord32(), + MachineRepresentation::kWord32); + if (lower()) { + if (lowering->poisoning_level_ == + PoisoningMitigationLevel::kDontPoison && + (index_type.IsNone() || length_type.IsNone() || + (index_type.Min() >= 0.0 && + index_type.Max() < length_type.Min()))) { + // The bounds check is redundant if we already know that + // the index is within the bounds of [0.0, length[. + DeferReplacement(node, node->InputAt(0)); + } else { + NodeProperties::ChangeOp( + node, simplified()->CheckedUint32Bounds(p.feedback())); + } + } + } else { + VisitBinop( + node, + UseInfo::CheckedSigned32AsWord32(kIdentifyZeros, p.feedback()), + UseInfo::TruncatingWord32(), MachineRepresentation::kWord32); + if (lower()) { + NodeProperties::ChangeOp( + node, simplified()->CheckedUint32Bounds(p.feedback())); + } + } + } else { + DCHECK(length_type.Is(type_cache_.kPositiveSafeInteger)); + VisitBinop(node, + UseInfo::CheckedSigned64AsWord64(kIdentifyZeros, p.feedback()), + UseInfo::Word64(), MachineRepresentation::kWord64); + if (lower()) { + NodeProperties::ChangeOp( + node, simplified()->CheckedUint64Bounds(p.feedback())); + } + } + } + // Dispatching routine for visiting the node {node} with the usage {use}. // Depending on the operator, propagate new usage info to the inputs. void VisitNode(Node* node, Truncation truncation, @@ -2552,34 +2599,8 @@ class RepresentationSelector { MachineRepresentation::kTaggedPointer); return; } - case IrOpcode::kCheckBounds: { - const CheckParameters& p = CheckParametersOf(node->op()); - Type index_type = TypeOf(node->InputAt(0)); - Type length_type = TypeOf(node->InputAt(1)); - if (index_type.Is(Type::Integral32OrMinusZero())) { - // Map -0 to 0, and the values in the [-2^31,-1] range to the - // [2^31,2^32-1] range, which will be considered out-of-bounds - // as well, because the {length_type} is limited to Unsigned31. - VisitBinop(node, UseInfo::TruncatingWord32(), - MachineRepresentation::kWord32); - if (lower() && lowering->poisoning_level_ == - PoisoningMitigationLevel::kDontPoison) { - if (index_type.IsNone() || length_type.IsNone() || - (index_type.Min() >= 0.0 && - index_type.Max() < length_type.Min())) { - // The bounds check is redundant if we already know that - // the index is within the bounds of [0.0, length[. - DeferReplacement(node, node->InputAt(0)); - } - } - } else { - VisitBinop( - node, - UseInfo::CheckedSigned32AsWord32(kIdentifyZeros, p.feedback()), - UseInfo::TruncatingWord32(), MachineRepresentation::kWord32); - } - return; - } + case IrOpcode::kCheckBounds: + return VisitCheckBounds(node, lowering); case IrOpcode::kPoisonIndex: { VisitUnop(node, UseInfo::TruncatingWord32(), MachineRepresentation::kWord32); @@ -2793,9 +2814,10 @@ class RepresentationSelector { MachineRepresentationFromArrayType(ExternalArrayTypeOf(node->op())); ProcessInput(node, 0, UseInfo::AnyTagged()); // buffer ProcessInput(node, 1, UseInfo::Word()); // external pointer - ProcessInput(node, 2, UseInfo::Word()); // index - ProcessInput(node, 3, UseInfo::Bool()); // little-endian - ProcessRemainingInputs(node, 4); + ProcessInput(node, 2, UseInfo::Word()); // byte offset + ProcessInput(node, 3, UseInfo::Word()); // index + ProcessInput(node, 4, UseInfo::Bool()); // little-endian + ProcessRemainingInputs(node, 5); SetOutput(node, rep); return; } @@ -2817,11 +2839,12 @@ class RepresentationSelector { MachineRepresentationFromArrayType(ExternalArrayTypeOf(node->op())); ProcessInput(node, 0, UseInfo::AnyTagged()); // buffer ProcessInput(node, 1, UseInfo::Word()); // external pointer - ProcessInput(node, 2, UseInfo::Word()); // index - ProcessInput(node, 3, + ProcessInput(node, 2, UseInfo::Word()); // byte offset + ProcessInput(node, 3, UseInfo::Word()); // index + ProcessInput(node, 4, TruncatingUseInfoFromRepresentation(rep)); // value - ProcessInput(node, 4, UseInfo::Bool()); // little-endian - ProcessRemainingInputs(node, 5); + ProcessInput(node, 5, UseInfo::Bool()); // little-endian + ProcessRemainingInputs(node, 6); SetOutput(node, MachineRepresentation::kNone); return; } diff --git a/src/compiler/simplified-operator.cc b/src/compiler/simplified-operator.cc index 9240fdbc76..02cdb5f9c7 100644 --- a/src/compiler/simplified-operator.cc +++ b/src/compiler/simplified-operator.cc @@ -631,8 +631,10 @@ bool operator==(CheckTaggedInputParameters const& lhs, } const CheckMinusZeroParameters& CheckMinusZeroParametersOf(const Operator* op) { - DCHECK(IrOpcode::kCheckedTaggedToInt32 == op->opcode() || - IrOpcode::kCheckedFloat64ToInt32 == op->opcode()); + DCHECK(op->opcode() == IrOpcode::kCheckedTaggedToInt32 || + op->opcode() == IrOpcode::kCheckedTaggedToInt64 || + op->opcode() == IrOpcode::kCheckedFloat64ToInt32 || + op->opcode() == IrOpcode::kCheckedFloat64ToInt64); return OpParameter(op); } @@ -809,8 +811,10 @@ bool operator==(CheckMinusZeroParameters const& lhs, V(CheckedTaggedSignedToInt32, 1, 1) \ V(CheckedTaggedToTaggedPointer, 1, 1) \ V(CheckedTaggedToTaggedSigned, 1, 1) \ + V(CheckedUint32Bounds, 2, 1) \ V(CheckedUint32ToInt32, 1, 1) \ V(CheckedUint32ToTaggedSigned, 1, 1) \ + V(CheckedUint64Bounds, 2, 1) \ V(CheckedUint64ToInt32, 1, 1) \ V(CheckedUint64ToTaggedSigned, 1, 1) @@ -966,6 +970,21 @@ struct SimplifiedOperatorGlobalCache final { CheckedFloat64ToInt32Operator kCheckedFloat64ToInt32DontCheckForMinusZeroOperator; + template + struct CheckedFloat64ToInt64Operator final + : public Operator1 { + CheckedFloat64ToInt64Operator() + : Operator1( + IrOpcode::kCheckedFloat64ToInt64, + Operator::kFoldable | Operator::kNoThrow, "CheckedFloat64ToInt64", + 1, 1, 1, 1, 1, 0, + CheckMinusZeroParameters(kMode, VectorSlotPair())) {} + }; + CheckedFloat64ToInt64Operator + kCheckedFloat64ToInt64CheckForMinusZeroOperator; + CheckedFloat64ToInt64Operator + kCheckedFloat64ToInt64DontCheckForMinusZeroOperator; + template struct CheckedTaggedToInt32Operator final : public Operator1 { @@ -981,6 +1000,21 @@ struct SimplifiedOperatorGlobalCache final { CheckedTaggedToInt32Operator kCheckedTaggedToInt32DontCheckForMinusZeroOperator; + template + struct CheckedTaggedToInt64Operator final + : public Operator1 { + CheckedTaggedToInt64Operator() + : Operator1( + IrOpcode::kCheckedTaggedToInt64, + Operator::kFoldable | Operator::kNoThrow, "CheckedTaggedToInt64", + 1, 1, 1, 1, 1, 0, + CheckMinusZeroParameters(kMode, VectorSlotPair())) {} + }; + CheckedTaggedToInt64Operator + kCheckedTaggedToInt64CheckForMinusZeroOperator; + CheckedTaggedToInt64Operator + kCheckedTaggedToInt64DontCheckForMinusZeroOperator; + template struct CheckedTaggedToFloat64Operator final : public Operator1 { @@ -1221,6 +1255,22 @@ const Operator* SimplifiedOperatorBuilder::CheckedFloat64ToInt32( 1, 1, 1, 0, CheckMinusZeroParameters(mode, feedback)); } +const Operator* SimplifiedOperatorBuilder::CheckedFloat64ToInt64( + CheckForMinusZeroMode mode, const VectorSlotPair& feedback) { + if (!feedback.IsValid()) { + switch (mode) { + case CheckForMinusZeroMode::kCheckForMinusZero: + return &cache_.kCheckedFloat64ToInt64CheckForMinusZeroOperator; + case CheckForMinusZeroMode::kDontCheckForMinusZero: + return &cache_.kCheckedFloat64ToInt64DontCheckForMinusZeroOperator; + } + } + return new (zone()) Operator1( + IrOpcode::kCheckedFloat64ToInt64, + Operator::kFoldable | Operator::kNoThrow, "CheckedFloat64ToInt64", 1, 1, + 1, 1, 1, 0, CheckMinusZeroParameters(mode, feedback)); +} + const Operator* SimplifiedOperatorBuilder::CheckedTaggedToInt32( CheckForMinusZeroMode mode, const VectorSlotPair& feedback) { if (!feedback.IsValid()) { @@ -1237,6 +1287,22 @@ const Operator* SimplifiedOperatorBuilder::CheckedTaggedToInt32( CheckMinusZeroParameters(mode, feedback)); } +const Operator* SimplifiedOperatorBuilder::CheckedTaggedToInt64( + CheckForMinusZeroMode mode, const VectorSlotPair& feedback) { + if (!feedback.IsValid()) { + switch (mode) { + case CheckForMinusZeroMode::kCheckForMinusZero: + return &cache_.kCheckedTaggedToInt64CheckForMinusZeroOperator; + case CheckForMinusZeroMode::kDontCheckForMinusZero: + return &cache_.kCheckedTaggedToInt64DontCheckForMinusZeroOperator; + } + } + return new (zone()) Operator1( + IrOpcode::kCheckedTaggedToInt64, Operator::kFoldable | Operator::kNoThrow, + "CheckedTaggedToInt64", 1, 1, 1, 1, 1, 0, + CheckMinusZeroParameters(mode, feedback)); +} + const Operator* SimplifiedOperatorBuilder::CheckedTaggedToFloat64( CheckTaggedInputMode mode, const VectorSlotPair& feedback) { if (!feedback.IsValid()) { @@ -1563,8 +1629,8 @@ SPECULATIVE_NUMBER_BINOP_LIST(SPECULATIVE_NUMBER_BINOP) V(StoreElement, ElementAccess, Operator::kNoRead, 3, 1, 0) \ V(LoadTypedElement, ExternalArrayType, Operator::kNoWrite, 4, 1, 1) \ V(StoreTypedElement, ExternalArrayType, Operator::kNoRead, 5, 1, 0) \ - V(LoadDataViewElement, ExternalArrayType, Operator::kNoWrite, 4, 1, 1) \ - V(StoreDataViewElement, ExternalArrayType, Operator::kNoRead, 5, 1, 0) + V(LoadDataViewElement, ExternalArrayType, Operator::kNoWrite, 5, 1, 1) \ + V(StoreDataViewElement, ExternalArrayType, Operator::kNoRead, 6, 1, 0) #define ACCESS(Name, Type, properties, value_input_count, control_input_count, \ output_count) \ diff --git a/src/compiler/simplified-operator.h b/src/compiler/simplified-operator.h index 32960e4642..3726cbec59 100644 --- a/src/compiler/simplified-operator.h +++ b/src/compiler/simplified-operator.h @@ -686,6 +686,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final const Operator* CheckedFloat64ToInt32(CheckForMinusZeroMode, const VectorSlotPair& feedback); + const Operator* CheckedFloat64ToInt64(CheckForMinusZeroMode, + const VectorSlotPair& feedback); const Operator* CheckedInt32Add(); const Operator* CheckedInt32Div(); const Operator* CheckedInt32Mod(); @@ -699,14 +701,18 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final const VectorSlotPair& feedback); const Operator* CheckedTaggedToInt32(CheckForMinusZeroMode, const VectorSlotPair& feedback); + const Operator* CheckedTaggedToInt64(CheckForMinusZeroMode, + const VectorSlotPair& feedback); const Operator* CheckedTaggedToTaggedPointer(const VectorSlotPair& feedback); const Operator* CheckedTaggedToTaggedSigned(const VectorSlotPair& feedback); const Operator* CheckedTruncateTaggedToWord32(CheckTaggedInputMode, const VectorSlotPair& feedback); const Operator* CheckedUint32Div(); const Operator* CheckedUint32Mod(); + const Operator* CheckedUint32Bounds(const VectorSlotPair& feedback); const Operator* CheckedUint32ToInt32(const VectorSlotPair& feedback); const Operator* CheckedUint32ToTaggedSigned(const VectorSlotPair& feedback); + const Operator* CheckedUint64Bounds(const VectorSlotPair& feedback); const Operator* CheckedUint64ToInt32(const VectorSlotPair& feedback); const Operator* CheckedUint64ToTaggedSigned(const VectorSlotPair& feedback); @@ -790,13 +796,13 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final // load-typed-element buffer, [base + external + index] const Operator* LoadTypedElement(ExternalArrayType const&); - // load-data-view-element buffer, [base + index] + // load-data-view-element buffer, [base + byte_offset + index] const Operator* LoadDataViewElement(ExternalArrayType const&); // store-typed-element buffer, [base + external + index], value const Operator* StoreTypedElement(ExternalArrayType const&); - // store-data-view-element buffer, [base + index], value + // store-data-view-element buffer, [base + byte_offset + index], value const Operator* StoreDataViewElement(ExternalArrayType const&); // Abort (for terminating execution on internal error). diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index 4c8c752200..744380c16d 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -1420,8 +1420,8 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { case IrOpcode::kCheckBounds: CheckValueInputIs(node, 0, Type::Any()); - CheckValueInputIs(node, 1, Type::Unsigned31()); - CheckTypeIs(node, Type::Unsigned31()); + CheckValueInputIs(node, 1, TypeCache::Get().kPositiveSafeInteger); + CheckTypeIs(node, TypeCache::Get().kPositiveSafeInteger); break; case IrOpcode::kPoisonIndex: CheckValueInputIs(node, 0, Type::Unsigned32()); @@ -1486,13 +1486,17 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { case IrOpcode::kCheckedInt32ToTaggedSigned: case IrOpcode::kCheckedInt64ToInt32: case IrOpcode::kCheckedInt64ToTaggedSigned: + case IrOpcode::kCheckedUint32Bounds: case IrOpcode::kCheckedUint32ToInt32: case IrOpcode::kCheckedUint32ToTaggedSigned: + case IrOpcode::kCheckedUint64Bounds: case IrOpcode::kCheckedUint64ToInt32: case IrOpcode::kCheckedUint64ToTaggedSigned: case IrOpcode::kCheckedFloat64ToInt32: + case IrOpcode::kCheckedFloat64ToInt64: case IrOpcode::kCheckedTaggedSignedToInt32: case IrOpcode::kCheckedTaggedToInt32: + case IrOpcode::kCheckedTaggedToInt64: case IrOpcode::kCheckedTaggedToFloat64: case IrOpcode::kCheckedTaggedToTaggedSigned: case IrOpcode::kCheckedTaggedToTaggedPointer: @@ -1739,6 +1743,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { case IrOpcode::kChangeFloat64ToUint32: case IrOpcode::kChangeFloat64ToUint64: case IrOpcode::kFloat64SilenceNaN: + case IrOpcode::kTruncateFloat64ToInt64: case IrOpcode::kTruncateFloat64ToUint32: case IrOpcode::kTruncateFloat32ToInt32: case IrOpcode::kTruncateFloat32ToUint32: diff --git a/src/compiler/x64/instruction-selector-x64.cc b/src/compiler/x64/instruction-selector-x64.cc index 5555da7907..4fa038a706 100644 --- a/src/compiler/x64/instruction-selector-x64.cc +++ b/src/compiler/x64/instruction-selector-x64.cc @@ -1319,6 +1319,7 @@ void VisitFloatUnop(InstructionSelector* selector, Node* node, Node* input, V(ChangeFloat64ToInt32, kSSEFloat64ToInt32) \ V(ChangeFloat64ToInt64, kSSEFloat64ToInt64) \ V(ChangeFloat64ToUint32, kSSEFloat64ToUint32 | MiscField::encode(1)) \ + V(TruncateFloat64ToInt64, kSSEFloat64ToInt64) \ V(TruncateFloat64ToUint32, kSSEFloat64ToUint32 | MiscField::encode(0)) \ V(ChangeFloat64ToUint64, kSSEFloat64ToUint64) \ V(TruncateFloat64ToFloat32, kSSEFloat64ToFloat32) \ diff --git a/test/cctest/compiler/test-representation-change.cc b/test/cctest/compiler/test-representation-change.cc index c334ecb383..82fd10b946 100644 --- a/test/cctest/compiler/test-representation-change.cc +++ b/test/cctest/compiler/test-representation-change.cc @@ -299,7 +299,8 @@ static void CheckChange(IrOpcode::Value expected, MachineRepresentation from, CHECK_EQ(expected, c->opcode()); CHECK_EQ(n, c->InputAt(0)); - if (expected == IrOpcode::kCheckedFloat64ToInt32) { + if (expected == IrOpcode::kCheckedFloat64ToInt32 || + expected == IrOpcode::kCheckedFloat64ToInt64) { CheckForMinusZeroMode mode = from_type.Maybe(Type::MinusZero()) ? use_info.minus_zero_check() @@ -316,13 +317,13 @@ static void CheckChange(IrOpcode::Value expected, MachineRepresentation from, static void CheckTwoChanges(IrOpcode::Value expected2, IrOpcode::Value expected1, MachineRepresentation from, Type from_type, - MachineRepresentation to) { + MachineRepresentation to, UseInfo use_info) { RepresentationChangerTester r; Node* n = r.Parameter(); Node* use = r.Return(n); - Node* c1 = r.changer()->GetRepresentationFor(n, from, from_type, use, - UseInfo(to, Truncation::None())); + Node* c1 = + r.changer()->GetRepresentationFor(n, from, from_type, use, use_info); CHECK_NE(c1, n); CHECK_EQ(expected1, c1->opcode()); @@ -332,6 +333,14 @@ static void CheckTwoChanges(IrOpcode::Value expected2, CHECK_EQ(n, c2->InputAt(0)); } +static void CheckTwoChanges(IrOpcode::Value expected2, + IrOpcode::Value expected1, + MachineRepresentation from, Type from_type, + MachineRepresentation to) { + CheckTwoChanges(expected2, expected1, from, from_type, to, + UseInfo(to, Truncation::None())); +} + static void CheckChange(IrOpcode::Value expected, MachineRepresentation from, Type from_type, MachineRepresentation to, UseInfo use_info) { @@ -387,6 +396,10 @@ TEST(Word64) { TypeCache::Get().kInt64, MachineRepresentation::kWord64); CheckChange(IrOpcode::kChangeFloat64ToUint64, MachineRepresentation::kFloat64, TypeCache::Get().kUint64, MachineRepresentation::kWord64); + CheckChange( + IrOpcode::kCheckedFloat64ToInt64, MachineRepresentation::kFloat64, + Type::Number(), MachineRepresentation::kWord64, + UseInfo::CheckedSigned64AsWord64(kIdentifyZeros, VectorSlotPair())); CheckChange(IrOpcode::kChangeInt64ToFloat64, MachineRepresentation::kWord64, Type::Signed32(), MachineRepresentation::kFloat64); @@ -411,6 +424,11 @@ TEST(Word64) { IrOpcode::kChangeFloat64ToUint64, MachineRepresentation::kFloat32, TypeCache::Get().kUint64, MachineRepresentation::kWord64); + CheckTwoChanges( + IrOpcode::kChangeFloat32ToFloat64, IrOpcode::kCheckedFloat64ToInt64, + MachineRepresentation::kFloat32, Type::Number(), + MachineRepresentation::kWord64, + UseInfo::CheckedSigned64AsWord64(kIdentifyZeros, VectorSlotPair())); CheckTwoChanges(IrOpcode::kChangeInt64ToFloat64, IrOpcode::kTruncateFloat64ToFloat32, @@ -428,6 +446,14 @@ TEST(Word64) { CheckChange(IrOpcode::kChangeTaggedSignedToInt64, MachineRepresentation::kTaggedSigned, Type::SignedSmall(), MachineRepresentation::kWord64); + CheckChange( + IrOpcode::kCheckedTaggedToInt64, MachineRepresentation::kTagged, + Type::Number(), MachineRepresentation::kWord64, + UseInfo::CheckedSigned64AsWord64(kIdentifyZeros, VectorSlotPair())); + CheckChange( + IrOpcode::kCheckedTaggedToInt64, MachineRepresentation::kTaggedPointer, + Type::Number(), MachineRepresentation::kWord64, + UseInfo::CheckedSigned64AsWord64(kIdentifyZeros, VectorSlotPair())); CheckTwoChanges(IrOpcode::kTruncateInt64ToInt32, IrOpcode::kChangeInt31ToTaggedSigned, diff --git a/test/mjsunit/compiler/int64.js b/test/mjsunit/compiler/int64.js index 0a88a95895..b2c53913da 100644 --- a/test/mjsunit/compiler/int64.js +++ b/test/mjsunit/compiler/int64.js @@ -89,3 +89,43 @@ %OptimizeFunctionOnNextCall(foo); assertEquals(0, foo(0xFFFFFFFF)); })(); + +// Test checked Float32->Word64 conversions. +(function() { + function foo(dv, i) { + i = dv.getFloat32(i, true); + return dv.getInt8(i, true); + } + + const dv = new DataView(new ArrayBuffer(10)); + dv.setFloat32(0, 8, true); + dv.setFloat32(4, 9, true); + dv.setInt8(8, 42); + dv.setInt8(9, 24); + + assertEquals(42, foo(dv, 0)); + assertEquals(24, foo(dv, 4)); + %OptimizeFunctionOnNextCall(foo); + assertEquals(42, foo(dv, 0)); + assertEquals(24, foo(dv, 4)); +})(); + +// Test checked Float64->Word64 conversions. +(function() { + function foo(dv, i) { + i = dv.getFloat64(i, true); + return dv.getInt8(i, true); + } + + const dv = new DataView(new ArrayBuffer(18)); + dv.setFloat64(0, 16, true); + dv.setFloat64(8, 17, true); + dv.setInt8(16, 42); + dv.setInt8(17, 24); + + assertEquals(42, foo(dv, 0)); + assertEquals(24, foo(dv, 8)); + %OptimizeFunctionOnNextCall(foo); + assertEquals(42, foo(dv, 0)); + assertEquals(24, foo(dv, 8)); +})(); diff --git a/test/unittests/compiler/redundancy-elimination-unittest.cc b/test/unittests/compiler/redundancy-elimination-unittest.cc index 8d1462b8c0..079cc4b99a 100644 --- a/test/unittests/compiler/redundancy-elimination-unittest.cc +++ b/test/unittests/compiler/redundancy-elimination-unittest.cc @@ -267,6 +267,35 @@ TEST_F(RedundancyEliminationTest, CheckedFloat64ToInt32) { } } +// ----------------------------------------------------------------------------- +// CheckedFloat64ToInt64 + +TEST_F(RedundancyEliminationTest, CheckedFloat64ToInt64) { + TRACED_FOREACH(VectorSlotPair, feedback1, vector_slot_pairs()) { + TRACED_FOREACH(VectorSlotPair, feedback2, vector_slot_pairs()) { + TRACED_FOREACH(CheckForMinusZeroMode, mode, kCheckForMinusZeroModes) { + Node* value = Parameter(0); + Node* effect = graph()->start(); + Node* control = graph()->start(); + + Node* check1 = effect = graph()->NewNode( + simplified()->CheckedFloat64ToInt64(mode, feedback1), value, effect, + control); + Reduction r1 = Reduce(check1); + ASSERT_TRUE(r1.Changed()); + EXPECT_EQ(r1.replacement(), check1); + + Node* check2 = effect = graph()->NewNode( + simplified()->CheckedFloat64ToInt64(mode, feedback2), value, effect, + control); + Reduction r2 = Reduce(check2); + ASSERT_TRUE(r2.Changed()); + EXPECT_EQ(r2.replacement(), check1); + } + } + } +} + // ----------------------------------------------------------------------------- // CheckedInt32ToTaggedSigned @@ -487,6 +516,35 @@ TEST_F(RedundancyEliminationTest, } } +// ----------------------------------------------------------------------------- +// CheckedTaggedToInt64 + +TEST_F(RedundancyEliminationTest, CheckedTaggedToInt64) { + TRACED_FOREACH(VectorSlotPair, feedback1, vector_slot_pairs()) { + TRACED_FOREACH(VectorSlotPair, feedback2, vector_slot_pairs()) { + TRACED_FOREACH(CheckForMinusZeroMode, mode, kCheckForMinusZeroModes) { + Node* value = Parameter(0); + Node* effect = graph()->start(); + Node* control = graph()->start(); + + Node* check1 = effect = graph()->NewNode( + simplified()->CheckedTaggedToInt64(mode, feedback1), value, effect, + control); + Reduction r1 = Reduce(check1); + ASSERT_TRUE(r1.Changed()); + EXPECT_EQ(r1.replacement(), check1); + + Node* check2 = effect = graph()->NewNode( + simplified()->CheckedTaggedToInt64(mode, feedback2), value, effect, + control); + Reduction r2 = Reduce(check2); + ASSERT_TRUE(r2.Changed()); + EXPECT_EQ(r2.replacement(), check1); + } + } + } +} + // ----------------------------------------------------------------------------- // CheckedTaggedToTaggedPointer @@ -599,6 +657,34 @@ TEST_F(RedundancyEliminationTest, } } +// ----------------------------------------------------------------------------- +// CheckedUint32Bounds + +TEST_F(RedundancyEliminationTest, CheckedUint32Bounds) { + TRACED_FOREACH(VectorSlotPair, feedback1, vector_slot_pairs()) { + TRACED_FOREACH(VectorSlotPair, feedback2, vector_slot_pairs()) { + Node* index = Parameter(0); + Node* length = Parameter(1); + Node* effect = graph()->start(); + Node* control = graph()->start(); + + Node* check1 = effect = + graph()->NewNode(simplified()->CheckedUint32Bounds(feedback1), index, + length, effect, control); + Reduction r1 = Reduce(check1); + ASSERT_TRUE(r1.Changed()); + EXPECT_EQ(r1.replacement(), check1); + + Node* check2 = effect = + graph()->NewNode(simplified()->CheckedUint32Bounds(feedback2), index, + length, effect, control); + Reduction r2 = Reduce(check2); + ASSERT_TRUE(r2.Changed()); + EXPECT_EQ(r2.replacement(), check1); + } + } +} + // ----------------------------------------------------------------------------- // CheckedUint32ToInt32 @@ -653,6 +739,34 @@ TEST_F(RedundancyEliminationTest, CheckedUint32ToTaggedSigned) { } } +// ----------------------------------------------------------------------------- +// CheckedUint64Bounds + +TEST_F(RedundancyEliminationTest, CheckedUint64Bounds) { + TRACED_FOREACH(VectorSlotPair, feedback1, vector_slot_pairs()) { + TRACED_FOREACH(VectorSlotPair, feedback2, vector_slot_pairs()) { + Node* index = Parameter(0); + Node* length = Parameter(1); + Node* effect = graph()->start(); + Node* control = graph()->start(); + + Node* check1 = effect = + graph()->NewNode(simplified()->CheckedUint64Bounds(feedback1), index, + length, effect, control); + Reduction r1 = Reduce(check1); + ASSERT_TRUE(r1.Changed()); + EXPECT_EQ(r1.replacement(), check1); + + Node* check2 = effect = + graph()->NewNode(simplified()->CheckedUint64Bounds(feedback2), index, + length, effect, control); + Reduction r2 = Reduce(check2); + ASSERT_TRUE(r2.Changed()); + EXPECT_EQ(r2.replacement(), check1); + } + } +} + // ----------------------------------------------------------------------------- // CheckedUint64ToInt32