[Turbofan] Specialize TransitionAndStoreElement

We can improve performance of inlined Array.prototype.map if we statically
know the type of the callback return result is a SignedSmall. Indeed,
we no longer need bother with transitioning the output array, because we
can store a SignedSmall (aka "Smi") anywhere.

Bug: v8:6896
Change-Id: I140ce9a7c15ff77d05afeda6cda58f0560d922c8
Reviewed-on: https://chromium-review.googlesource.com/707139
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Michael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48387}
This commit is contained in:
Mike Stanton 2017-10-09 16:03:51 +02:00 committed by Commit Bot
parent 3baf964aeb
commit b4f249e48c
8 changed files with 86 additions and 0 deletions

View File

@ -891,6 +891,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kStoreTypedElement: case IrOpcode::kStoreTypedElement:
LowerStoreTypedElement(node); LowerStoreTypedElement(node);
break; break;
case IrOpcode::kStoreSignedSmallElement:
LowerStoreSignedSmallElement(node);
break;
case IrOpcode::kLookupHashStorageIndex: case IrOpcode::kLookupHashStorageIndex:
result = LowerLookupHashStorageIndex(node); result = LowerLookupHashStorageIndex(node);
break; break;
@ -3300,6 +3303,63 @@ void EffectControlLinearizer::LowerTransitionAndStoreElement(Node* node) {
__ Bind(&done); __ Bind(&done);
} }
void EffectControlLinearizer::LowerStoreSignedSmallElement(Node* node) {
Node* array = node->InputAt(0);
Node* index = node->InputAt(1);
Node* value = node->InputAt(2);
// Store a signed small in an output array.
//
// kind = ElementsKind(array)
//
// -- STORE PHASE ----------------------
// if kind == HOLEY_DOUBLE_ELEMENTS {
// float_value = convert smi to float
// Store array[index] = float_value
// } else {
// // kind is HOLEY_SMI_ELEMENTS or HOLEY_ELEMENTS
// Store array[index] = value
// }
//
Node* map = __ LoadField(AccessBuilder::ForMap(), array);
Node* kind;
{
Node* bit_field2 = __ LoadField(AccessBuilder::ForMapBitField2(), map);
Node* mask = __ Int32Constant(Map::ElementsKindBits::kMask);
Node* andit = __ Word32And(bit_field2, mask);
Node* shift = __ Int32Constant(Map::ElementsKindBits::kShift);
kind = __ Word32Shr(andit, shift);
}
Node* elements = __ LoadField(AccessBuilder::ForJSObjectElements(), array);
auto if_kind_is_double = __ MakeLabel();
auto done = __ MakeLabel();
__ GotoIf(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS),
&if_kind_is_double);
{
// Our ElementsKind is HOLEY_SMI_ELEMENTS or HOLEY_ELEMENTS.
// In this case, we know our value is a signed small, and we can optimize
// the ElementAccess information.
ElementAccess access = AccessBuilder::ForFixedArrayElement();
access.type = Type::SignedSmall();
access.machine_type = MachineType::TaggedSigned();
access.write_barrier_kind = kNoWriteBarrier;
__ StoreElement(access, elements, index, value);
__ Goto(&done);
}
__ Bind(&if_kind_is_double);
{
// Our ElementsKind is HOLEY_DOUBLE_ELEMENTS.
Node* int_value = ChangeSmiToInt32(value);
Node* float_value = __ ChangeInt32ToFloat64(int_value);
__ StoreElement(AccessBuilder::ForFixedDoubleArrayElement(), elements,
index, float_value);
__ Goto(&done);
}
__ Bind(&done);
}
void EffectControlLinearizer::LowerRuntimeAbort(Node* node) { void EffectControlLinearizer::LowerRuntimeAbort(Node* node) {
BailoutReason reason = BailoutReasonOf(node->op()); BailoutReason reason = BailoutReasonOf(node->op());
Operator::Properties properties = Operator::kNoDeopt | Operator::kNoThrow; Operator::Properties properties = Operator::kNoDeopt | Operator::kNoThrow;

View File

@ -125,6 +125,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerLoadFieldByIndex(Node* node); Node* LowerLoadFieldByIndex(Node* node);
Node* LowerLoadTypedElement(Node* node); Node* LowerLoadTypedElement(Node* node);
void LowerStoreTypedElement(Node* node); void LowerStoreTypedElement(Node* node);
void LowerStoreSignedSmallElement(Node* node);
Node* LowerLookupHashStorageIndex(Node* node); Node* LowerLookupHashStorageIndex(Node* node);
Node* LowerLookupSigned32HashStorageIndex(Node* node); Node* LowerLookupSigned32HashStorageIndex(Node* node);
void LowerTransitionAndStoreElement(Node* node); void LowerTransitionAndStoreElement(Node* node);

View File

@ -353,6 +353,7 @@
V(StoreField) \ V(StoreField) \
V(StoreElement) \ V(StoreElement) \
V(StoreTypedElement) \ V(StoreTypedElement) \
V(StoreSignedSmallElement) \
V(TransitionAndStoreElement) \ V(TransitionAndStoreElement) \
V(ObjectIsArrayBufferView) \ V(ObjectIsArrayBufferView) \
V(ObjectIsCallable) \ V(ObjectIsCallable) \

View File

@ -2577,9 +2577,19 @@ class RepresentationSelector {
return; return;
} }
case IrOpcode::kTransitionAndStoreElement: { case IrOpcode::kTransitionAndStoreElement: {
Type* value_type = TypeOf(node->InputAt(2));
ProcessInput(node, 0, UseInfo::AnyTagged()); // array ProcessInput(node, 0, UseInfo::AnyTagged()); // array
ProcessInput(node, 1, UseInfo::TruncatingWord32()); // index ProcessInput(node, 1, UseInfo::TruncatingWord32()); // index
ProcessInput(node, 2, UseInfo::AnyTagged()); // value ProcessInput(node, 2, UseInfo::AnyTagged()); // value
if (value_type->Is(Type::SignedSmall())) {
if (lower()) {
NodeProperties::ChangeOp(node,
simplified()->StoreSignedSmallElement());
}
}
ProcessRemainingInputs(node, 3); ProcessRemainingInputs(node, 3);
SetOutput(node, MachineRepresentation::kNone); SetOutput(node, MachineRepresentation::kNone);
return; return;

View File

@ -1076,6 +1076,12 @@ const Operator* SimplifiedOperatorBuilder::TransitionAndStoreElement(
1, 1, 0, 1, 0, parameters); 1, 1, 0, 1, 0, parameters);
} }
const Operator* SimplifiedOperatorBuilder::StoreSignedSmallElement() {
return new (zone()) Operator(IrOpcode::kStoreSignedSmallElement,
Operator::kNoDeopt | Operator::kNoThrow,
"StoreSignedSmallElement", 3, 1, 1, 0, 1, 0);
}
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

View File

@ -485,6 +485,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
// store-element [base + index], value, only with fast arrays. // store-element [base + index], value, only with fast arrays.
const Operator* TransitionAndStoreElement(Handle<Map> double_map, const Operator* TransitionAndStoreElement(Handle<Map> double_map,
Handle<Map> fast_map); Handle<Map> fast_map);
// store-element [base + index], smi value, only with fast arrays.
const Operator* StoreSignedSmallElement();
// load-typed-element buffer, [base + external + index] // load-typed-element buffer, [base + external + index]
const Operator* LoadTypedElement(ExternalArrayType const&); const Operator* LoadTypedElement(ExternalArrayType const&);

View File

@ -1976,6 +1976,8 @@ Type* Typer::Visitor::TypeTransitionAndStoreElement(Node* node) {
UNREACHABLE(); UNREACHABLE();
} }
Type* Typer::Visitor::TypeStoreSignedSmallElement(Node* node) { UNREACHABLE(); }
Type* Typer::Visitor::TypeStoreTypedElement(Node* node) { Type* Typer::Visitor::TypeStoreTypedElement(Node* node) {
UNREACHABLE(); UNREACHABLE();
} }

View File

@ -1306,6 +1306,10 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kTransitionAndStoreElement: case IrOpcode::kTransitionAndStoreElement:
CheckNotTyped(node); CheckNotTyped(node);
break; break;
case IrOpcode::kStoreSignedSmallElement:
CheckValueInputIs(node, 1, Type::SignedSmall());
CheckNotTyped(node);
break;
case IrOpcode::kStoreTypedElement: case IrOpcode::kStoreTypedElement:
CheckNotTyped(node); CheckNotTyped(node);
break; break;