Commit 15c31fe4 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Add support for huge DataViews.

This introduces Word64 support for the CheckBounds operator, which now
lowers to either CheckedUint32Bounds or CheckedUint64Bounds after the
representation selection. The right hand side of CheckBounds can now
be any positive safe integer on 64-bit architectures, whereas it remains
Unsigned31 for 32-bit architectures. We only use the extended Word64
support when the right hand side is outside the Unsigned31 range, so
for everything except DataViews this means that the performance should
remain the same. The typing rule for the CheckBounds operator was
updated to reflect this new behavior.

The CheckBounds with a right hand side outside the Unsigned31 range will
pass a new Signed64 feedback kind, which is handled with newly introduced
CheckedFloat64ToInt64 and CheckedTaggedToInt64 operators in representation
selection.

The JSCallReducer lowering for DataView getType()/setType() methods was
updated to not smi-check the [[ByteLength]] and [[ByteOffset]] anymore,
but instead just use the raw uintptr_t values and operate on any value
(for 64-bit architectures these fields can hold any positive safe
integer, for 32-bit architectures it's limited to Unsigned31 range as
before). This means that V8 can now handle huge DataViews fully, without
falling off a performance cliff.

This refactoring even gave us some performance improvements, on a simple
micro-benchmark just exercising different DataView accesses we go from

  testDataViewGetUint8: 796 ms.
  testDataViewGetUint16: 997 ms.
  testDataViewGetInt32: 994 ms.
  testDataViewGetFloat64: 997 ms.

to

  testDataViewGetUint8: 895 ms.
  testDataViewGetUint16: 889 ms.
  testDataViewGetInt32: 888 ms.
  testDataViewGetFloat64: 890 ms.

meaning we lost around 10% on the single byte case, but gained 10% across
the board for all the other element sizes.

Design-Document: http://bit.ly/turbofan-word64
Bug: chromium:225811, v8:4153, v8:7881, v8:8171, v8:8383
Change-Id: Ic9d1bf152e47802c04dcfd679372e5c85e4abc83
Reviewed-on: https://chromium-review.googlesource.com/c/1303732Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57095}
parent dd5c3631
......@@ -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) \
......
......@@ -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));
}
......
......@@ -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,
......
......@@ -29,6 +29,7 @@ namespace compiler {
V(ChangeFloat64ToUint32) \
V(TruncateInt64ToInt32) \
V(RoundFloat64ToInt32) \
V(TruncateFloat64ToInt64) \
V(TruncateFloat64ToWord32) \
V(Float64ExtractLowWord32) \
V(Float64ExtractHighWord32) \
......
......@@ -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();
}
......
......@@ -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<JSDataView> dataview = Handle<JSDataView>::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;
}
......
......@@ -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) \
......
......@@ -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();
......
......@@ -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) \
......
......@@ -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) {
......
......@@ -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 =
......
......@@ -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(
......
......@@ -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);
......
......@@ -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;
}
......
......@@ -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<CheckMinusZeroParameters>(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<CheckForMinusZeroMode::kDontCheckForMinusZero>
kCheckedFloat64ToInt32DontCheckForMinusZeroOperator;
template <CheckForMinusZeroMode kMode>
struct CheckedFloat64ToInt64Operator final
: public Operator1<CheckMinusZeroParameters> {
CheckedFloat64ToInt64Operator()
: Operator1<CheckMinusZeroParameters>(
IrOpcode::kCheckedFloat64ToInt64,
Operator::kFoldable | Operator::kNoThrow, "CheckedFloat64ToInt64",
1, 1, 1, 1, 1, 0,
CheckMinusZeroParameters(kMode, VectorSlotPair())) {}
};
CheckedFloat64ToInt64Operator<CheckForMinusZeroMode::kCheckForMinusZero>
kCheckedFloat64ToInt64CheckForMinusZeroOperator;
CheckedFloat64ToInt64Operator<CheckForMinusZeroMode::kDontCheckForMinusZero>
kCheckedFloat64ToInt64DontCheckForMinusZeroOperator;
template <CheckForMinusZeroMode kMode>
struct CheckedTaggedToInt32Operator final
: public Operator1<CheckMinusZeroParameters> {
......@@ -981,6 +1000,21 @@ struct SimplifiedOperatorGlobalCache final {
CheckedTaggedToInt32Operator<CheckForMinusZeroMode::kDontCheckForMinusZero>
kCheckedTaggedToInt32DontCheckForMinusZeroOperator;
template <CheckForMinusZeroMode kMode>
struct CheckedTaggedToInt64Operator final
: public Operator1<CheckMinusZeroParameters> {
CheckedTaggedToInt64Operator()
: Operator1<CheckMinusZeroParameters>(
IrOpcode::kCheckedTaggedToInt64,
Operator::kFoldable | Operator::kNoThrow, "CheckedTaggedToInt64",
1, 1, 1, 1, 1, 0,
CheckMinusZeroParameters(kMode, VectorSlotPair())) {}
};
CheckedTaggedToInt64Operator<CheckForMinusZeroMode::kCheckForMinusZero>
kCheckedTaggedToInt64CheckForMinusZeroOperator;
CheckedTaggedToInt64Operator<CheckForMinusZeroMode::kDontCheckForMinusZero>
kCheckedTaggedToInt64DontCheckForMinusZeroOperator;
template <CheckTaggedInputMode kMode>
struct CheckedTaggedToFloat64Operator final
: public Operator1<CheckTaggedInputParameters> {
......@@ -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<CheckMinusZeroParameters>(
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<CheckMinusZeroParameters>(
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) \
......
......@@ -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).
......
......@@ -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:
......
......@@ -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) \
......
......@@ -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,
......
......@@ -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));
})();
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment