Commit a953c6f1 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Inline ArrayBuffer.isView into optimized code.

This improves performance of ArrayBuffer.isView by roughly 2.5x itself,
and enables optimizations across ArrayBuffer.isView calls, i.e. map
checks can be eliminated across. This was discovered in a related Node
pull request (https://github.com/nodejs/node/pull/15663).

Bug: v8:6868
Change-Id: I1d56ec385f8daa0e1d44d3bc4d6c9a5558ba4522
Reviewed-on: https://chromium-review.googlesource.com/691660Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48247}
parent 4651f644
......@@ -4445,7 +4445,8 @@ Handle<JSFunction> Genesis::CreateArrayBuffer(Handle<String> name,
array_buffer_fun, DONT_ENUM);
SimpleInstallFunction(array_buffer_fun, factory()->isView_string(),
Builtins::kArrayBufferIsView, 1, true);
Builtins::kArrayBufferIsView, 1, true, DONT_ENUM,
kArrayBufferIsView);
// Install the "byteLength" getter on the {prototype}.
SimpleInstallGetter(prototype, factory()->byte_length_string(),
......
......@@ -712,6 +712,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kCheckedTruncateTaggedToWord32:
result = LowerCheckedTruncateTaggedToWord32(node, frame_state);
break;
case IrOpcode::kObjectIsArrayBufferView:
result = LowerObjectIsArrayBufferView(node);
break;
case IrOpcode::kObjectIsCallable:
result = LowerObjectIsCallable(node);
break;
......@@ -1866,6 +1869,31 @@ Node* EffectControlLinearizer::LowerCheckedTruncateTaggedToWord32(
return done.PhiAt(0);
}
Node* EffectControlLinearizer::LowerObjectIsArrayBufferView(Node* node) {
Node* value = node->InputAt(0);
auto if_smi = __ MakeDeferredLabel();
auto done = __ MakeLabel(MachineRepresentation::kBit);
Node* check = ObjectIsSmi(value);
__ GotoIf(check, &if_smi);
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
Node* value_instance_type =
__ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
STATIC_ASSERT(JS_TYPED_ARRAY_TYPE + 1 == JS_DATA_VIEW_TYPE);
Node* vfalse = __ Uint32LessThan(
__ Int32Sub(value_instance_type, __ Int32Constant(JS_TYPED_ARRAY_TYPE)),
__ Int32Constant(2));
__ Goto(&done, vfalse);
__ Bind(&if_smi);
__ Goto(&done, __ Int32Constant(0));
__ Bind(&done);
return done.PhiAt(0);
}
Node* EffectControlLinearizer::LowerObjectIsCallable(Node* node) {
Node* value = node->InputAt(0);
......
......@@ -84,6 +84,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerTruncateTaggedToFloat64(Node* node);
Node* LowerTruncateTaggedToWord32(Node* node);
Node* LowerCheckedTruncateTaggedToWord32(Node* node, Node* frame_state);
Node* LowerObjectIsArrayBufferView(Node* node);
Node* LowerObjectIsCallable(Node* node);
Node* LowerObjectIsDetectableCallable(Node* node);
Node* LowerObjectIsNaN(Node* node);
......
......@@ -2835,6 +2835,17 @@ Reduction JSBuiltinReducer::ReduceStringToUpperCaseIntl(Node* node) {
return NoChange();
}
Reduction JSBuiltinReducer::ReduceArrayBufferIsView(Node* node) {
Node* value = node->op()->ValueInputCount() >= 3
? NodeProperties::GetValueInput(node, 2)
: jsgraph()->UndefinedConstant();
RelaxEffectsAndControls(node);
node->ReplaceInput(0, value);
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->ObjectIsArrayBufferView());
return Changed(node);
}
Reduction JSBuiltinReducer::ReduceArrayBufferViewAccessor(
Node* node, InstanceType instance_type, FieldAccess const& access) {
Node* receiver = NodeProperties::GetValueInput(node, 1);
......@@ -3076,6 +3087,8 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
return ReduceStringToLowerCaseIntl(node);
case kStringToUpperCaseIntl:
return ReduceStringToUpperCaseIntl(node);
case kArrayBufferIsView:
return ReduceArrayBufferIsView(node);
case kDataViewByteLength:
return ReduceArrayBufferViewAccessor(
node, JS_DATA_VIEW_TYPE,
......
......@@ -119,6 +119,7 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction ReduceStringIteratorNext(Node* node);
Reduction ReduceStringToLowerCaseIntl(Node* node);
Reduction ReduceStringToUpperCaseIntl(Node* node);
Reduction ReduceArrayBufferIsView(Node* node);
Reduction ReduceArrayBufferViewAccessor(Node* node,
InstanceType instance_type,
FieldAccess const& access);
......
......@@ -352,6 +352,7 @@
V(StoreElement) \
V(StoreTypedElement) \
V(TransitionAndStoreElement) \
V(ObjectIsArrayBufferView) \
V(ObjectIsCallable) \
V(ObjectIsDetectableCallable) \
V(ObjectIsNaN) \
......
......@@ -2664,6 +2664,11 @@ class RepresentationSelector {
if (lower()) DeferReplacement(node, node->InputAt(0));
return;
}
case IrOpcode::kObjectIsArrayBufferView: {
// TODO(turbofan): Introduce a Type::ArrayBufferView?
VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit);
return;
}
case IrOpcode::kObjectIsCallable: {
VisitObjectIs(node, Type::Callable(), lowering);
return;
......
......@@ -495,6 +495,7 @@ BailoutReason BailoutReasonOf(const Operator* op) {
V(TruncateTaggedPointerToBit, Operator::kNoProperties, 1, 0) \
V(TruncateTaggedToWord32, Operator::kNoProperties, 1, 0) \
V(TruncateTaggedToFloat64, Operator::kNoProperties, 1, 0) \
V(ObjectIsArrayBufferView, Operator::kNoProperties, 1, 0) \
V(ObjectIsCallable, Operator::kNoProperties, 1, 0) \
V(ObjectIsDetectableCallable, Operator::kNoProperties, 1, 0) \
V(ObjectIsNaN, Operator::kNoProperties, 1, 0) \
......
......@@ -435,6 +435,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* CheckNotTaggedHole();
const Operator* ConvertTaggedHoleToUndefined();
const Operator* ObjectIsArrayBufferView();
const Operator* ObjectIsCallable();
const Operator* ObjectIsDetectableCallable();
const Operator* ObjectIsNaN();
......
......@@ -287,6 +287,7 @@ class Typer::Visitor : public Reducer {
SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_METHOD)
#undef DECLARE_METHOD
static Type* ObjectIsArrayBufferView(Type*, Typer*);
static Type* ObjectIsCallable(Type*, Typer*);
static Type* ObjectIsDetectableCallable(Type*, Typer*);
static Type* ObjectIsNaN(Type*, Typer*);
......@@ -509,6 +510,12 @@ Type* Typer::Visitor::ToString(Type* type, Typer* t) {
// Type checks.
Type* Typer::Visitor::ObjectIsArrayBufferView(Type* type, Typer* t) {
// TODO(turbofan): Introduce a Type::ArrayBufferView?
if (!type->Maybe(Type::OtherObject())) return t->singleton_false_;
return Type::Boolean();
}
Type* Typer::Visitor::ObjectIsCallable(Type* type, Typer* t) {
if (type->Is(Type::Callable())) return t->singleton_true_;
if (!type->Maybe(Type::Callable())) return t->singleton_false_;
......@@ -1525,6 +1532,10 @@ Type* Typer::Visitor::JSCallTyper(Type* fun, Typer* t) {
case kArrayUnshift:
return t->cache_.kPositiveSafeInteger;
// ArrayBuffer functions.
case kArrayBufferIsView:
return Type::Boolean();
// Object functions.
case kObjectAssign:
return Type::Receiver();
......@@ -1954,6 +1965,10 @@ Type* Typer::Visitor::TypeStoreTypedElement(Node* node) {
UNREACHABLE();
}
Type* Typer::Visitor::TypeObjectIsArrayBufferView(Node* node) {
return TypeUnaryOp(node, ObjectIsArrayBufferView);
}
Type* Typer::Visitor::TypeObjectIsCallable(Node* node) {
return TypeUnaryOp(node, ObjectIsCallable);
}
......
......@@ -1005,6 +1005,7 @@ void Verifier::Visitor::Check(Node* node) {
CheckTypeIs(node, Type::Boolean());
break;
case IrOpcode::kObjectIsArrayBufferView:
case IrOpcode::kObjectIsCallable:
case IrOpcode::kObjectIsDetectableCallable:
case IrOpcode::kObjectIsNaN:
......
......@@ -4636,6 +4636,7 @@ enum BuiltinFunctionId {
kMathPowHalf,
// These are manually assigned to special getters during bootstrapping.
kArrayBufferByteLength,
kArrayBufferIsView,
kArrayEntries,
kArrayKeys,
kArrayValues,
......
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