Commit c4323e08 authored by Théotime Grohens's avatar Théotime Grohens Committed by Commit Bot

[turbofan] Add DataView setters in TurboFan

This CL completes the implementation of DataView prototype methods
in TurboFan, by implementing the Uint8, Int8, Uint16, Int16,
Uint32, Int32, Float32 and Float64 setters.

DataView performance is now ahead of the equivalent TypedArray wrapper,
and is now expected to at least match TypedArray performance in
the general case as well.

This CL also adds a test file in the compiler directory, to make
sure that the setters actually behave correctly.

Change-Id: I4ad4341c6b9b9d461348b62216f37a73abe321e8
Reviewed-on: https://chromium-review.googlesource.com/1128867Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Commit-Queue: Théotime Grohens <theotime@google.com>
Cr-Commit-Position: refs/heads/master@{#54331}
parent 95ab7579
...@@ -941,6 +941,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -941,6 +941,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kStoreTypedElement: case IrOpcode::kStoreTypedElement:
LowerStoreTypedElement(node); LowerStoreTypedElement(node);
break; break;
case IrOpcode::kStoreDataViewElement:
LowerStoreDataViewElement(node);
break;
case IrOpcode::kStoreSignedSmallElement: case IrOpcode::kStoreSignedSmallElement:
LowerStoreSignedSmallElement(node); LowerStoreSignedSmallElement(node);
break; break;
...@@ -3918,6 +3921,177 @@ Node* EffectControlLinearizer::LowerLoadDataViewElement(Node* node) { ...@@ -3918,6 +3921,177 @@ Node* EffectControlLinearizer::LowerLoadDataViewElement(Node* node) {
} }
} }
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);
// 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);
ElementAccess access =
AccessBuilder::ForTypedArrayElement(kExternalUint8Array, true);
switch (element_type) {
case kExternalUint8Array: // Fall through.
case kExternalInt8Array: {
Node* b0 = __ Word32And(value, __ Int32Constant(0xFF));
__ StoreElement(access, storage, index, b0);
break;
}
case kExternalUint16Array: // Fall through.
case kExternalInt16Array: {
Node* b0 = __ Word32And(value, __ Int32Constant(0xFF));
Node* b1 = __ Word32And(__ Word32Shr(value, __ Int32Constant(8)),
__ Int32Constant(0xFF));
auto big_endian = __ MakeLabel();
auto done = __ MakeLabel();
__ GotoIfNot(is_little_endian, &big_endian);
{
// Little-endian store.
__ StoreElement(access, storage, index, b0);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(1)), b1);
__ Goto(&done);
}
__ Bind(&big_endian);
{
// Big-endian store.
__ StoreElement(access, storage, index, b1);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(1)), b0);
__ Goto(&done);
}
__ Bind(&done);
break;
}
case kExternalUint32Array: // Fall through.
case kExternalInt32Array: // Fall through.
case kExternalFloat32Array: {
if (element_type == kExternalFloat32Array) {
value = __ BitcastFloat32ToInt32(value);
}
Node* b0 = __ Word32And(value, __ Int32Constant(0xFF));
Node* b1 = __ Word32And(__ Word32Shr(value, __ Int32Constant(8)),
__ Int32Constant(0xFF));
Node* b2 = __ Word32And(__ Word32Shr(value, __ Int32Constant(16)),
__ Int32Constant(0xFF));
Node* b3 = __ Word32Shr(value, __ Int32Constant(24));
auto big_endian = __ MakeLabel();
auto done = __ MakeLabel();
__ GotoIfNot(is_little_endian, &big_endian);
{
// Little-endian store.
__ StoreElement(access, storage, index, b0);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(1)), b1);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(2)), b2);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(3)), b3);
__ Goto(&done);
}
__ Bind(&big_endian);
{
// Big-endian store.
__ StoreElement(access, storage, index, b3);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(1)), b2);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(2)), b1);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(3)), b0);
__ Goto(&done);
}
__ Bind(&done);
break;
}
case kExternalFloat64Array: {
Node* low_word = __ Float64ExtractLowWord32(value);
Node* high_word = __ Float64ExtractHighWord32(value);
Node* b0 = __ Word32And(low_word, __ Int32Constant(0xFF));
Node* b1 = __ Word32And(__ Word32Shr(low_word, __ Int32Constant(8)),
__ Int32Constant(0xFF));
Node* b2 = __ Word32And(__ Word32Shr(low_word, __ Int32Constant(16)),
__ Int32Constant(0xFF));
Node* b3 = __ Word32Shr(low_word, __ Int32Constant(24));
Node* b4 = __ Word32And(high_word, __ Int32Constant(0xFF));
Node* b5 = __ Word32And(__ Word32Shr(high_word, __ Int32Constant(8)),
__ Int32Constant(0xFF));
Node* b6 = __ Word32And(__ Word32Shr(high_word, __ Int32Constant(16)),
__ Int32Constant(0xFF));
Node* b7 = __ Word32Shr(high_word, __ Int32Constant(24));
auto big_endian = __ MakeLabel();
auto done = __ MakeLabel();
__ GotoIfNot(is_little_endian, &big_endian);
{
// Little-endian store.
__ StoreElement(access, storage, index, b0);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(1)), b1);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(2)), b2);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(3)), b3);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(4)), b4);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(5)), b5);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(6)), b6);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(7)), b7);
__ Goto(&done);
}
__ Bind(&big_endian);
{
// Big-endian store.
__ StoreElement(access, storage, index, b7);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(1)), b6);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(2)), b5);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(3)), b4);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(4)), b3);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(5)), b2);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(6)), b1);
__ StoreElement(access, storage,
__ Int32Add(index, __ Int32Constant(7)), b0);
__ Goto(&done);
}
__ Bind(&done);
break;
}
default:
UNREACHABLE();
}
}
Node* EffectControlLinearizer::LowerLoadTypedElement(Node* node) { Node* EffectControlLinearizer::LowerLoadTypedElement(Node* node) {
ExternalArrayType array_type = ExternalArrayTypeOf(node->op()); ExternalArrayType array_type = ExternalArrayTypeOf(node->op());
Node* buffer = node->InputAt(0); Node* buffer = node->InputAt(0);
......
...@@ -153,6 +153,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ...@@ -153,6 +153,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerLoadTypedElement(Node* node); Node* LowerLoadTypedElement(Node* node);
Node* LowerLoadDataViewElement(Node* node); Node* LowerLoadDataViewElement(Node* node);
void LowerStoreTypedElement(Node* node); void LowerStoreTypedElement(Node* node);
void LowerStoreDataViewElement(Node* node);
void LowerStoreSignedSmallElement(Node* node); void LowerStoreSignedSmallElement(Node* node);
Node* LowerFindOrderedHashMapEntry(Node* node); Node* LowerFindOrderedHashMapEntry(Node* node);
Node* LowerFindOrderedHashMapEntryForInt32Key(Node* node); Node* LowerFindOrderedHashMapEntryForInt32Key(Node* node);
......
...@@ -28,8 +28,10 @@ namespace compiler { ...@@ -28,8 +28,10 @@ namespace compiler {
V(TruncateInt64ToInt32) \ V(TruncateInt64ToInt32) \
V(RoundFloat64ToInt32) \ V(RoundFloat64ToInt32) \
V(TruncateFloat64ToWord32) \ V(TruncateFloat64ToWord32) \
V(Float64ExtractLowWord32) \
V(Float64ExtractHighWord32) \ V(Float64ExtractHighWord32) \
V(BitcastInt32ToFloat32) \ V(BitcastInt32ToFloat32) \
V(BitcastFloat32ToInt32) \
V(Float64Abs) V(Float64Abs)
#define PURE_ASSEMBLER_MACH_BINOP_LIST(V) \ #define PURE_ASSEMBLER_MACH_BINOP_LIST(V) \
......
...@@ -3453,6 +3453,30 @@ Reduction JSCallReducer::ReduceJSCall(Node* node, ...@@ -3453,6 +3453,30 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
case Builtins::kDataViewPrototypeGetFloat64: case Builtins::kDataViewPrototypeGetFloat64:
return ReduceDataViewPrototypeGet( return ReduceDataViewPrototypeGet(
node, ExternalArrayType::kExternalFloat64Array); node, ExternalArrayType::kExternalFloat64Array);
case Builtins::kDataViewPrototypeSetUint8:
return ReduceDataViewPrototypeSet(node,
ExternalArrayType::kExternalUint8Array);
case Builtins::kDataViewPrototypeSetInt8:
return ReduceDataViewPrototypeSet(node,
ExternalArrayType::kExternalInt8Array);
case Builtins::kDataViewPrototypeSetUint16:
return ReduceDataViewPrototypeSet(
node, ExternalArrayType::kExternalUint16Array);
case Builtins::kDataViewPrototypeSetInt16:
return ReduceDataViewPrototypeSet(node,
ExternalArrayType::kExternalInt16Array);
case Builtins::kDataViewPrototypeSetUint32:
return ReduceDataViewPrototypeSet(
node, ExternalArrayType::kExternalUint32Array);
case Builtins::kDataViewPrototypeSetInt32:
return ReduceDataViewPrototypeSet(node,
ExternalArrayType::kExternalInt32Array);
case Builtins::kDataViewPrototypeSetFloat32:
return ReduceDataViewPrototypeSet(
node, ExternalArrayType::kExternalFloat32Array);
case Builtins::kDataViewPrototypeSetFloat64:
return ReduceDataViewPrototypeSet(
node, ExternalArrayType::kExternalFloat64Array);
case Builtins::kTypedArrayPrototypeByteLength: case Builtins::kTypedArrayPrototypeByteLength:
return ReduceArrayBufferViewAccessor( return ReduceArrayBufferViewAccessor(
node, JS_TYPED_ARRAY_TYPE, node, JS_TYPED_ARRAY_TYPE,
...@@ -6759,6 +6783,181 @@ Reduction JSCallReducer::ReduceDataViewPrototypeGet( ...@@ -6759,6 +6783,181 @@ Reduction JSCallReducer::ReduceDataViewPrototypeGet(
return NoChange(); return NoChange();
} }
Reduction JSCallReducer::ReduceDataViewPrototypeSet(
Node* node, ExternalArrayType element_type) {
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
CallParameters const& p = CallParametersOf(node->op());
Node* offset = node->op()->ValueInputCount() > 2
? NodeProperties::GetValueInput(node, 2)
: jsgraph()->ZeroConstant();
Node* value = node->op()->ValueInputCount() > 3
? NodeProperties::GetValueInput(node, 3)
: jsgraph()->ZeroConstant();
Node* is_little_endian = node->op()->ValueInputCount() > 4
? NodeProperties::GetValueInput(node, 4)
: jsgraph()->FalseConstant();
// Only do stuff if the {receiver} is really a DataView.
if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect,
JS_DATA_VIEW_TYPE)) {
// Check that the {offset} is a positive Smi.
offset = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
offset, effect, control);
Node* is_positive = graph()->NewNode(simplified()->NumberLessThanOrEqual(),
jsgraph()->ZeroConstant(), offset);
effect = graph()->NewNode(
simplified()->CheckIf(DeoptimizeReason::kNotASmi, p.feedback()),
is_positive, effect, control);
// Coerce {is_little_endian} to boolean.
is_little_endian =
graph()->NewNode(simplified()->ToBoolean(), is_little_endian);
// Coerce {value} to Number.
value = effect = graph()->NewNode(
simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball,
p.feedback()),
value, effect, control);
// Get the underlying buffer and check that it has not been neutered.
Node* buffer = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
receiver, effect, control);
Node* check_neutered = effect = graph()->NewNode(
simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
Node* branch_neutered = graph()->NewNode(
common()->Branch(BranchHint::kFalse), check_neutered, control);
// Raise an error if it was neuteured.
Node* if_true_neutered =
graph()->NewNode(common()->IfTrue(), branch_neutered);
Node* etrue_neutered = effect;
{
if_true_neutered = etrue_neutered = graph()->NewNode(
javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
jsgraph()->Constant(MessageTemplate::kDetachedOperation),
jsgraph()->HeapConstant(
factory()->NewStringFromAsciiChecked("DataView.prototype.set")),
context, frame_state, etrue_neutered, if_true_neutered);
}
// Otherwise, proceed.
Node* if_false_neutered =
graph()->NewNode(common()->IfFalse(), branch_neutered);
Node* efalse_neutered = effect;
// Get the byte offset and byte length of the {receiver}.
Node* byte_offset = efalse_neutered =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForJSArrayBufferViewByteOffset()),
receiver, efalse_neutered, if_false_neutered);
Node* byte_length = efalse_neutered =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForJSArrayBufferViewByteLength()),
receiver, efalse_neutered, if_false_neutered);
// The end offset is the offset plus the element size
// of the type that we want to store.
int element_size = ExternalArrayElementSize(element_type);
Node* end_offset = graph()->NewNode(simplified()->NumberAdd(), offset,
jsgraph()->Constant(element_size));
// We need to check that {end_offset} <= {byte_length}, ie
// throw a RangeError if {byte_length} < {end_offset}.
Node* check_range = graph()->NewNode(simplified()->NumberLessThan(),
byte_length, end_offset);
Node* branch_range = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check_range, if_false_neutered);
Node* if_true_range = graph()->NewNode(common()->IfTrue(), branch_range);
Node* etrue_range = efalse_neutered;
{
if_true_range = etrue_range = graph()->NewNode(
javascript()->CallRuntime(Runtime::kThrowRangeError, 2),
jsgraph()->Constant(MessageTemplate::kInvalidDataViewAccessorOffset),
jsgraph()->HeapConstant(
factory()->NewStringFromAsciiChecked("DataView.prototype.set")),
context, frame_state, etrue_range, if_true_range);
}
Node* if_false_range = graph()->NewNode(common()->IfFalse(), branch_range);
Node* efalse_range = efalse_neutered;
Node* vfalse_range = jsgraph()->UndefinedConstant(); // Return value.
{
// Get the buffer's backing store.
Node* backing_store = efalse_range =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForJSArrayBufferBackingStore()),
buffer, efalse_range, if_false_range);
// Compute the buffer index at which we'll write.
Node* buffer_index =
graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset);
// Perform the store.
efalse_range =
graph()->NewNode(simplified()->StoreDataViewElement(element_type),
buffer, backing_store, buffer_index, value,
is_little_endian, efalse_range, if_false_range);
}
// Rewire potential exception edges.
Node* on_exception = nullptr;
if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
// Create appropriate {IfException} and {IfSuccess} nodes.
Node* extrue_neutered = graph()->NewNode(
common()->IfException(), etrue_neutered,
if_true_neutered); // We threw because the array was neutered.
if_true_neutered =
graph()->NewNode(common()->IfSuccess(), if_true_neutered);
Node* extrue_range =
graph()->NewNode(common()->IfException(), etrue_range,
if_true_range); // We threw because out of bounds.
if_true_range = graph()->NewNode(common()->IfSuccess(), if_true_range);
// We can't throw in StoreDataViewElement(),
// so we don't need to handle that path here.
// Join the exception edges.
Node* merge =
graph()->NewNode(common()->Merge(2), extrue_neutered, extrue_range);
Node* ephi = graph()->NewNode(common()->EffectPhi(2), extrue_neutered,
extrue_range, merge);
Node* phi =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
extrue_neutered, extrue_range, merge);
ReplaceWithValue(on_exception, phi, ephi, merge);
}
// Connect the throwing paths to end.
if_true_neutered =
graph()->NewNode(common()->Throw(), etrue_neutered, if_true_neutered);
NodeProperties::MergeControlToEnd(graph(), common(), if_true_neutered);
if_true_range =
graph()->NewNode(common()->Throw(), etrue_range, if_true_range);
NodeProperties::MergeControlToEnd(graph(), common(), if_true_range);
// Continue on the regular path.
ReplaceWithValue(node, vfalse_range, efalse_range, if_false_range);
return Changed(vfalse_range);
}
return NoChange();
}
// ES6 section 18.2.2 isFinite ( number ) // ES6 section 18.2.2 isFinite ( number )
Reduction JSCallReducer::ReduceGlobalIsFinite(Node* node) { Reduction JSCallReducer::ReduceGlobalIsFinite(Node* node) {
......
...@@ -183,6 +183,8 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer { ...@@ -183,6 +183,8 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
Reduction ReduceDataViewPrototypeGet(Node* node, Reduction ReduceDataViewPrototypeGet(Node* node,
ExternalArrayType element_type); ExternalArrayType element_type);
Reduction ReduceDataViewPrototypeSet(Node* node,
ExternalArrayType element_type);
Reduction ReduceDatePrototypeGetTime(Node* node); Reduction ReduceDatePrototypeGetTime(Node* node);
Reduction ReduceDateNow(Node* node); Reduction ReduceDateNow(Node* node);
......
...@@ -384,6 +384,7 @@ ...@@ -384,6 +384,7 @@
V(StoreField) \ V(StoreField) \
V(StoreElement) \ V(StoreElement) \
V(StoreTypedElement) \ V(StoreTypedElement) \
V(StoreDataViewElement) \
V(StoreSignedSmallElement) \ V(StoreSignedSmallElement) \
V(TransitionAndStoreElement) \ V(TransitionAndStoreElement) \
V(TransitionAndStoreNumberElement) \ V(TransitionAndStoreNumberElement) \
......
...@@ -2636,7 +2636,7 @@ class RepresentationSelector { ...@@ -2636,7 +2636,7 @@ class RepresentationSelector {
ProcessInput(node, 0, UseInfo::AnyTagged()); // buffer ProcessInput(node, 0, UseInfo::AnyTagged()); // buffer
ProcessInput(node, 1, UseInfo::PointerInt()); // external pointer ProcessInput(node, 1, UseInfo::PointerInt()); // external pointer
ProcessInput(node, 2, UseInfo::TruncatingWord32()); // index ProcessInput(node, 2, UseInfo::TruncatingWord32()); // index
ProcessInput(node, 3, UseInfo::Bool()); // little endian ProcessInput(node, 3, UseInfo::Bool()); // little-endian
ProcessRemainingInputs(node, 4); ProcessRemainingInputs(node, 4);
SetOutput(node, rep); SetOutput(node, rep);
return; return;
...@@ -2654,6 +2654,19 @@ class RepresentationSelector { ...@@ -2654,6 +2654,19 @@ class RepresentationSelector {
SetOutput(node, MachineRepresentation::kNone); SetOutput(node, MachineRepresentation::kNone);
return; return;
} }
case IrOpcode::kStoreDataViewElement: {
MachineRepresentation const rep =
MachineRepresentationFromArrayType(ExternalArrayTypeOf(node->op()));
ProcessInput(node, 0, UseInfo::AnyTagged()); // buffer
ProcessInput(node, 1, UseInfo::PointerInt()); // external pointer
ProcessInput(node, 2, UseInfo::TruncatingWord32()); // index
ProcessInput(node, 3,
TruncatingUseInfoFromRepresentation(rep)); // value
ProcessInput(node, 4, UseInfo::Bool()); // little-endian
ProcessRemainingInputs(node, 5);
SetOutput(node, MachineRepresentation::kNone);
return;
}
case IrOpcode::kConvertReceiver: { case IrOpcode::kConvertReceiver: {
Type input_type = TypeOf(node->InputAt(0)); Type input_type = TypeOf(node->InputAt(0));
VisitBinop(node, UseInfo::AnyTagged(), VisitBinop(node, UseInfo::AnyTagged(),
......
...@@ -142,7 +142,8 @@ const ElementAccess& ElementAccessOf(const Operator* op) { ...@@ -142,7 +142,8 @@ const ElementAccess& ElementAccessOf(const Operator* op) {
ExternalArrayType ExternalArrayTypeOf(const Operator* op) { ExternalArrayType ExternalArrayTypeOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kLoadTypedElement || DCHECK(op->opcode() == IrOpcode::kLoadTypedElement ||
op->opcode() == IrOpcode::kLoadDataViewElement || op->opcode() == IrOpcode::kLoadDataViewElement ||
op->opcode() == IrOpcode::kStoreTypedElement); op->opcode() == IrOpcode::kStoreTypedElement ||
op->opcode() == IrOpcode::kStoreDataViewElement);
return OpParameter<ExternalArrayType>(op); return OpParameter<ExternalArrayType>(op);
} }
...@@ -1524,14 +1525,15 @@ const Operator* SimplifiedOperatorBuilder::StringFromSingleCodePoint( ...@@ -1524,14 +1525,15 @@ const Operator* SimplifiedOperatorBuilder::StringFromSingleCodePoint(
SPECULATIVE_NUMBER_BINOP_LIST(SPECULATIVE_NUMBER_BINOP) SPECULATIVE_NUMBER_BINOP_LIST(SPECULATIVE_NUMBER_BINOP)
#undef SPECULATIVE_NUMBER_BINOP #undef SPECULATIVE_NUMBER_BINOP
#define ACCESS_OP_LIST(V) \ #define ACCESS_OP_LIST(V) \
V(LoadField, FieldAccess, Operator::kNoWrite, 1, 1, 1) \ V(LoadField, FieldAccess, Operator::kNoWrite, 1, 1, 1) \
V(StoreField, FieldAccess, Operator::kNoRead, 2, 1, 0) \ V(StoreField, FieldAccess, Operator::kNoRead, 2, 1, 0) \
V(LoadElement, ElementAccess, Operator::kNoWrite, 2, 1, 1) \ V(LoadElement, ElementAccess, Operator::kNoWrite, 2, 1, 1) \
V(StoreElement, ElementAccess, Operator::kNoRead, 3, 1, 0) \ V(StoreElement, ElementAccess, Operator::kNoRead, 3, 1, 0) \
V(LoadTypedElement, ExternalArrayType, Operator::kNoWrite, 4, 1, 1) \ V(LoadTypedElement, ExternalArrayType, Operator::kNoWrite, 4, 1, 1) \
V(StoreTypedElement, ExternalArrayType, Operator::kNoRead, 5, 1, 0) \ V(StoreTypedElement, ExternalArrayType, Operator::kNoRead, 5, 1, 0) \
V(LoadDataViewElement, ExternalArrayType, Operator::kNoWrite, 4, 1, 1) V(LoadDataViewElement, ExternalArrayType, Operator::kNoWrite, 4, 1, 1) \
V(StoreDataViewElement, ExternalArrayType, Operator::kNoRead, 5, 1, 0)
#define ACCESS(Name, Type, properties, value_input_count, control_input_count, \ #define ACCESS(Name, Type, properties, value_input_count, control_input_count, \
output_count) \ output_count) \
......
...@@ -765,6 +765,9 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -765,6 +765,9 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
// store-typed-element buffer, [base + external + index], value // store-typed-element buffer, [base + external + index], value
const Operator* StoreTypedElement(ExternalArrayType const&); const Operator* StoreTypedElement(ExternalArrayType const&);
// store-data-view-element buffer, [base + index], value
const Operator* StoreDataViewElement(ExternalArrayType const&);
// Abort (for terminating execution on internal error). // Abort (for terminating execution on internal error).
const Operator* RuntimeAbort(AbortReason reason); const Operator* RuntimeAbort(AbortReason reason);
......
...@@ -2074,6 +2074,8 @@ Type Typer::Visitor::TypeStoreSignedSmallElement(Node* node) { UNREACHABLE(); } ...@@ -2074,6 +2074,8 @@ Type Typer::Visitor::TypeStoreSignedSmallElement(Node* node) { UNREACHABLE(); }
Type Typer::Visitor::TypeStoreTypedElement(Node* node) { UNREACHABLE(); } Type Typer::Visitor::TypeStoreTypedElement(Node* node) { UNREACHABLE(); }
Type Typer::Visitor::TypeStoreDataViewElement(Node* node) { UNREACHABLE(); }
Type Typer::Visitor::TypeObjectIsArrayBufferView(Node* node) { Type Typer::Visitor::TypeObjectIsArrayBufferView(Node* node) {
return TypeUnaryOp(node, ObjectIsArrayBufferView); return TypeUnaryOp(node, ObjectIsArrayBufferView);
} }
......
...@@ -1529,6 +1529,9 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { ...@@ -1529,6 +1529,9 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
case IrOpcode::kStoreTypedElement: case IrOpcode::kStoreTypedElement:
CheckNotTyped(node); CheckNotTyped(node);
break; break;
case IrOpcode::kStoreDataViewElement:
CheckNotTyped(node);
break;
case IrOpcode::kNumberSilenceNaN: case IrOpcode::kNumberSilenceNaN:
CheckValueInputIs(node, 0, Type::Number()); CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::Number()); CheckTypeIs(node, Type::Number());
......
...@@ -165,11 +165,6 @@ assertOptimized(readInt8Handled); ...@@ -165,11 +165,6 @@ assertOptimized(readInt8Handled);
assertEquals(values[3], readInt8Handled(3.14)); // Non-Smi index deopts. assertEquals(values[3], readInt8Handled(3.14)); // Non-Smi index deopts.
assertUnoptimized(readInt8Handled); assertUnoptimized(readInt8Handled);
// None of the stores wrote out of bounds.
var bytes = new Uint8Array(buffer);
for (var i = 0; i < 8; i++) assertEquals(0, bytes[i]);
for (var i = 32; i < 64; i++) assertEquals(0, bytes[i]);
// TurboFan neutered buffer. // TurboFan neutered buffer.
warmup(readInt8Handled); warmup(readInt8Handled);
assertOptimized(readInt8Handled); assertOptimized(readInt8Handled);
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --opt --no-always-opt
var buffer = new ArrayBuffer(64);
var dataview = new DataView(buffer, 8, 24);
function writeUint8(offset, value) {
dataview.setUint8(offset, value);
}
function writeInt8Handled(offset, value) {
try {
dataview.setInt8(offset, value);
} catch(e) {
return e;
}
}
function writeUint16(offset, value, little_endian) {
dataview.setUint16(offset, value, little_endian);
}
function writeInt16(offset, value, little_endian) {
dataview.setInt16(offset, value, little_endian);
}
function writeUint32(offset, value, little_endian) {
dataview.setUint32(offset, value, little_endian);
}
function writeInt32(offset, value, little_endian) {
dataview.setInt32(offset, value, little_endian);
}
function writeFloat32(offset, value, little_endian) {
dataview.setFloat32(offset, value, little_endian);
}
function writeFloat64(offset, value, little_endian) {
dataview.setFloat64(offset, value, little_endian);
}
function warmup(f) {
f(0, 0);
f(0, 1);
%OptimizeFunctionOnNextCall(f);
f(0, 2);
f(0, 3);
}
// TurboFan valid setUint8.
warmup(writeUint8);
assertOptimized(writeUint8);
writeUint8(0, 0xde);
writeUint8(1, 0xad);
writeUint8(2, 0xbe);
writeUint8(3, 0xef);
assertEquals(0xdeadbeef, dataview.getUint32(0));
// TurboFan valid setInt8.
warmup(writeInt8Handled);
assertOptimized(writeInt8Handled);
writeInt8Handled(0, -34);
writeInt8Handled(1, -83);
writeInt8Handled(2, -66);
writeInt8Handled(3, -17);
assertEquals(0xdeadbeef, dataview.getUint32(0));
// TurboFan valid setUint16.
warmup(writeUint16);
assertOptimized(writeUint16);
writeUint16(0, 0xdead);
writeUint16(2, 0xefbe, true);
assertEquals(0xdeadbeef, dataview.getUint32(0));
// TurboFan valid setInt16.
warmup(writeInt16);
assertOptimized(writeInt16);
writeInt16(0, -8531);
writeInt16(2, -4162, true);
assertEquals(0xdeadbeef, dataview.getUint32(0));
// TurboFan valid setUint32.
warmup(writeUint32);
assertOptimized(writeUint32);
writeUint32(0, 0xdeadbeef);
assertEquals(0xdeadbeef, dataview.getUint32(0));
writeUint32(0, 0xefbeadde, true);
assertEquals(0xdeadbeef, dataview.getUint32(0));
// TurboFan valid setInt32.
warmup(writeInt32);
assertOptimized(writeInt32);
writeInt32(0, -559038737);
assertEquals(0xdeadbeef, dataview.getUint32(0));
writeInt32(0, -272716322, true);
assertEquals(0xdeadbeef, dataview.getUint32(0));
// TurboFan valid setFloat32.
let b3 = Math.fround(Math.E); // Round Math.E to float32.
warmup(writeFloat32);
assertOptimized(writeFloat32);
writeFloat32(4, b3);
assertEquals(b3, dataview.getFloat32(4));
writeFloat32(4, b3, true);
assertEquals(b3, dataview.getFloat32(4, true));
// TurboFan valid setFloat64.
let b4 = Math.PI;
warmup(writeFloat64);
assertOptimized(writeFloat64);
writeFloat64(8, b4);
assertEquals(b4, dataview.getFloat64(8));
writeFloat64(8, b4, true);
assertEquals(b4, dataview.getFloat64(8, true));
// TurboFan out of bounds read, throw with exception handler.
assertOptimized(writeInt8Handled);
assertInstanceof(writeInt8Handled(24, 0), RangeError);
assertOptimized(writeInt8Handled);
// Without exception handler.
assertOptimized(writeUint8);
assertThrows(() => writeUint8(24, 0));
assertOptimized(writeUint8);
// None of the stores wrote out of bounds.
var bytes = new Uint8Array(buffer);
for (var i = 0; i < 8; i++) assertEquals(0, bytes[i]);
for (var i = 32; i < 64; i++) assertEquals(0, bytes[i]);
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