[turbofan] Eliminate unused effectful nodes during representation selection.
We can actually eliminate certain effectful operations like loads and speculative number operations during representation selection if we discover that their value outputs are unused (we also propagate this information through pure operations as well, so that we remove the maximum number of effectful nodes possible). R=jarin@chromium.org Review-Url: https://codereview.chromium.org/2168023002 Cr-Commit-Position: refs/heads/master@{#37928}
This commit is contained in:
parent
c4ef8a8d6e
commit
2744fcbb6c
@ -390,10 +390,10 @@ struct CommonOperatorGlobalCache final {
|
|||||||
template <int kEffectInputCount>
|
template <int kEffectInputCount>
|
||||||
struct EffectPhiOperator final : public Operator {
|
struct EffectPhiOperator final : public Operator {
|
||||||
EffectPhiOperator()
|
EffectPhiOperator()
|
||||||
: Operator( // --
|
: Operator( // --
|
||||||
IrOpcode::kEffectPhi, Operator::kPure, // opcode
|
IrOpcode::kEffectPhi, Operator::kKontrol, // opcode
|
||||||
"EffectPhi", // name
|
"EffectPhi", // name
|
||||||
0, kEffectInputCount, 1, 0, 1, 0) {} // counts
|
0, kEffectInputCount, 1, 0, 1, 0) {} // counts
|
||||||
};
|
};
|
||||||
#define CACHED_EFFECT_PHI(input_count) \
|
#define CACHED_EFFECT_PHI(input_count) \
|
||||||
EffectPhiOperator<input_count> kEffectPhi##input_count##Operator;
|
EffectPhiOperator<input_count> kEffectPhi##input_count##Operator;
|
||||||
@ -827,10 +827,10 @@ const Operator* CommonOperatorBuilder::EffectPhi(int effect_input_count) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Uncached.
|
// Uncached.
|
||||||
return new (zone()) Operator( // --
|
return new (zone()) Operator( // --
|
||||||
IrOpcode::kEffectPhi, Operator::kPure, // opcode
|
IrOpcode::kEffectPhi, Operator::kKontrol, // opcode
|
||||||
"EffectPhi", // name
|
"EffectPhi", // name
|
||||||
0, effect_input_count, 1, 0, 1, 0); // counts
|
0, effect_input_count, 1, 0, 1, 0); // counts
|
||||||
}
|
}
|
||||||
|
|
||||||
const Operator* CommonOperatorBuilder::BeginRegion(
|
const Operator* CommonOperatorBuilder::BeginRegion(
|
||||||
|
@ -28,6 +28,7 @@ class Truncation final {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Queries.
|
// Queries.
|
||||||
|
bool IsUnused() const { return kind_ == TruncationKind::kNone; }
|
||||||
bool TruncatesToWord32() const {
|
bool TruncatesToWord32() const {
|
||||||
return LessGeneral(kind_, TruncationKind::kWord32);
|
return LessGeneral(kind_, TruncationKind::kWord32);
|
||||||
}
|
}
|
||||||
|
@ -761,17 +761,30 @@ class RepresentationSelector {
|
|||||||
// values {kTypeAny}.
|
// values {kTypeAny}.
|
||||||
void VisitInputs(Node* node) {
|
void VisitInputs(Node* node) {
|
||||||
int tagged_count = node->op()->ValueInputCount() +
|
int tagged_count = node->op()->ValueInputCount() +
|
||||||
OperatorProperties::GetContextInputCount(node->op());
|
OperatorProperties::GetContextInputCount(node->op()) +
|
||||||
// Visit value and context inputs as tagged.
|
OperatorProperties::GetFrameStateInputCount(node->op());
|
||||||
|
// Visit value, context and frame state inputs as tagged.
|
||||||
for (int i = 0; i < tagged_count; i++) {
|
for (int i = 0; i < tagged_count; i++) {
|
||||||
ProcessInput(node, i, UseInfo::AnyTagged());
|
ProcessInput(node, i, UseInfo::AnyTagged());
|
||||||
}
|
}
|
||||||
// Only enqueue other inputs (framestates, effects, control).
|
// Only enqueue other inputs (effects, control).
|
||||||
for (int i = tagged_count; i < node->InputCount(); i++) {
|
for (int i = tagged_count; i < node->InputCount(); i++) {
|
||||||
EnqueueInput(node, i);
|
EnqueueInput(node, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper for an unused node.
|
||||||
|
void VisitUnused(Node* node) {
|
||||||
|
int value_count = node->op()->ValueInputCount() +
|
||||||
|
OperatorProperties::GetContextInputCount(node->op()) +
|
||||||
|
OperatorProperties::GetFrameStateInputCount(node->op());
|
||||||
|
for (int i = 0; i < value_count; i++) {
|
||||||
|
ProcessInput(node, i, UseInfo::None());
|
||||||
|
}
|
||||||
|
ProcessRemainingInputs(node, value_count);
|
||||||
|
if (lower()) Kill(node);
|
||||||
|
}
|
||||||
|
|
||||||
// Helper for binops of the R x L -> O variety.
|
// Helper for binops of the R x L -> O variety.
|
||||||
void VisitBinop(Node* node, UseInfo left_use, UseInfo right_use,
|
void VisitBinop(Node* node, UseInfo left_use, UseInfo right_use,
|
||||||
MachineRepresentation output,
|
MachineRepresentation output,
|
||||||
@ -934,18 +947,20 @@ class RepresentationSelector {
|
|||||||
void VisitCall(Node* node, SimplifiedLowering* lowering) {
|
void VisitCall(Node* node, SimplifiedLowering* lowering) {
|
||||||
const CallDescriptor* desc = CallDescriptorOf(node->op());
|
const CallDescriptor* desc = CallDescriptorOf(node->op());
|
||||||
int params = static_cast<int>(desc->ParameterCount());
|
int params = static_cast<int>(desc->ParameterCount());
|
||||||
|
int value_input_count = node->op()->ValueInputCount();
|
||||||
// Propagate representation information from call descriptor.
|
// Propagate representation information from call descriptor.
|
||||||
for (int i = 0; i < node->InputCount(); i++) {
|
for (int i = 0; i < value_input_count; i++) {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
// The target of the call.
|
// The target of the call.
|
||||||
ProcessInput(node, i, UseInfo::None());
|
ProcessInput(node, i, UseInfo::Any());
|
||||||
} else if ((i - 1) < params) {
|
} else if ((i - 1) < params) {
|
||||||
ProcessInput(node, i, TruncatingUseInfoFromRepresentation(
|
ProcessInput(node, i, TruncatingUseInfoFromRepresentation(
|
||||||
desc->GetInputType(i).representation()));
|
desc->GetInputType(i).representation()));
|
||||||
} else {
|
} else {
|
||||||
ProcessInput(node, i, UseInfo::None());
|
ProcessInput(node, i, UseInfo::AnyTagged());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ProcessRemainingInputs(node, value_input_count);
|
||||||
|
|
||||||
if (desc->ReturnCount() > 0) {
|
if (desc->ReturnCount() > 0) {
|
||||||
SetOutput(node, desc->GetReturnType(0).representation());
|
SetOutput(node, desc->GetReturnType(0).representation());
|
||||||
@ -1109,6 +1124,7 @@ class RepresentationSelector {
|
|||||||
|
|
||||||
void VisitSpeculativeAdditiveOp(Node* node, Truncation truncation,
|
void VisitSpeculativeAdditiveOp(Node* node, Truncation truncation,
|
||||||
SimplifiedLowering* lowering) {
|
SimplifiedLowering* lowering) {
|
||||||
|
if (truncation.IsUnused()) return VisitUnused(node);
|
||||||
if (BothInputsAre(node, type_cache_.kSigned32OrMinusZero) &&
|
if (BothInputsAre(node, type_cache_.kSigned32OrMinusZero) &&
|
||||||
NodeProperties::GetType(node)->Is(Type::Signed32())) {
|
NodeProperties::GetType(node)->Is(Type::Signed32())) {
|
||||||
// int32 + int32 = int32 ==> signed Int32Add/Sub
|
// int32 + int32 = int32 ==> signed Int32Add/Sub
|
||||||
@ -1166,11 +1182,21 @@ class RepresentationSelector {
|
|||||||
// Depending on the operator, propagate new usage info to the inputs.
|
// Depending on the operator, propagate new usage info to the inputs.
|
||||||
void VisitNode(Node* node, Truncation truncation,
|
void VisitNode(Node* node, Truncation truncation,
|
||||||
SimplifiedLowering* lowering) {
|
SimplifiedLowering* lowering) {
|
||||||
|
// Unconditionally eliminate unused pure nodes (only relevant if there's
|
||||||
|
// a pure operation in between two effectful ones, where the last one
|
||||||
|
// is unused).
|
||||||
|
if (node->op()->HasProperty(Operator::kPure) && truncation.IsUnused()) {
|
||||||
|
return VisitUnused(node);
|
||||||
|
}
|
||||||
switch (node->opcode()) {
|
switch (node->opcode()) {
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
// Common operators.
|
// Common operators.
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
case IrOpcode::kStart:
|
case IrOpcode::kStart:
|
||||||
|
// We use Start as a terminator for the frame state chain, so even
|
||||||
|
// tho Start doesn't really produce a value, we have to say Tagged
|
||||||
|
// here, otherwise the input conversion will fail.
|
||||||
|
return VisitLeaf(node, MachineRepresentation::kTagged);
|
||||||
case IrOpcode::kDead:
|
case IrOpcode::kDead:
|
||||||
return VisitLeaf(node, MachineRepresentation::kNone);
|
return VisitLeaf(node, MachineRepresentation::kNone);
|
||||||
case IrOpcode::kParameter: {
|
case IrOpcode::kParameter: {
|
||||||
@ -1284,6 +1310,7 @@ class RepresentationSelector {
|
|||||||
case IrOpcode::kSpeculativeNumberLessThan:
|
case IrOpcode::kSpeculativeNumberLessThan:
|
||||||
case IrOpcode::kSpeculativeNumberLessThanOrEqual:
|
case IrOpcode::kSpeculativeNumberLessThanOrEqual:
|
||||||
case IrOpcode::kSpeculativeNumberEqual: {
|
case IrOpcode::kSpeculativeNumberEqual: {
|
||||||
|
if (truncation.IsUnused()) return VisitUnused(node);
|
||||||
// Number comparisons reduce to integer comparisons for integer inputs.
|
// Number comparisons reduce to integer comparisons for integer inputs.
|
||||||
if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32()) &&
|
if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32()) &&
|
||||||
TypeOf(node->InputAt(1))->Is(Type::Unsigned32())) {
|
TypeOf(node->InputAt(1))->Is(Type::Unsigned32())) {
|
||||||
@ -1338,6 +1365,7 @@ class RepresentationSelector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case IrOpcode::kSpeculativeNumberMultiply: {
|
case IrOpcode::kSpeculativeNumberMultiply: {
|
||||||
|
if (truncation.IsUnused()) return VisitUnused(node);
|
||||||
if (BothInputsAre(node, Type::Integral32()) &&
|
if (BothInputsAre(node, Type::Integral32()) &&
|
||||||
(NodeProperties::GetType(node)->Is(Type::Signed32()) ||
|
(NodeProperties::GetType(node)->Is(Type::Signed32()) ||
|
||||||
NodeProperties::GetType(node)->Is(Type::Unsigned32()) ||
|
NodeProperties::GetType(node)->Is(Type::Unsigned32()) ||
|
||||||
@ -1412,6 +1440,7 @@ class RepresentationSelector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case IrOpcode::kSpeculativeNumberDivide: {
|
case IrOpcode::kSpeculativeNumberDivide: {
|
||||||
|
if (truncation.IsUnused()) return VisitUnused(node);
|
||||||
if (BothInputsAreUnsigned32(node) && truncation.TruncatesToWord32()) {
|
if (BothInputsAreUnsigned32(node) && truncation.TruncatesToWord32()) {
|
||||||
// => unsigned Uint32Div
|
// => unsigned Uint32Div
|
||||||
VisitWord32TruncatingBinop(node);
|
VisitWord32TruncatingBinop(node);
|
||||||
@ -1517,6 +1546,7 @@ class RepresentationSelector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case IrOpcode::kSpeculativeNumberModulus: {
|
case IrOpcode::kSpeculativeNumberModulus: {
|
||||||
|
if (truncation.IsUnused()) return VisitUnused(node);
|
||||||
if (BothInputsAreUnsigned32(node) && truncation.TruncatesToWord32()) {
|
if (BothInputsAreUnsigned32(node) && truncation.TruncatesToWord32()) {
|
||||||
// => unsigned Uint32Mod
|
// => unsigned Uint32Mod
|
||||||
VisitWord32TruncatingBinop(node);
|
VisitWord32TruncatingBinop(node);
|
||||||
@ -1630,6 +1660,7 @@ class RepresentationSelector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case IrOpcode::kSpeculativeNumberShiftLeft: {
|
case IrOpcode::kSpeculativeNumberShiftLeft: {
|
||||||
|
if (truncation.IsUnused()) return VisitUnused(node);
|
||||||
if (BothInputsAre(node, Type::NumberOrOddball())) {
|
if (BothInputsAre(node, Type::NumberOrOddball())) {
|
||||||
Type* rhs_type = GetUpperBound(node->InputAt(1));
|
Type* rhs_type = GetUpperBound(node->InputAt(1));
|
||||||
VisitBinop(node, UseInfo::TruncatingWord32(),
|
VisitBinop(node, UseInfo::TruncatingWord32(),
|
||||||
@ -1941,8 +1972,9 @@ class RepresentationSelector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case IrOpcode::kLoadField: {
|
case IrOpcode::kLoadField: {
|
||||||
|
if (truncation.IsUnused()) return VisitUnused(node);
|
||||||
FieldAccess access = FieldAccessOf(node->op());
|
FieldAccess access = FieldAccessOf(node->op());
|
||||||
MachineRepresentation representation =
|
MachineRepresentation const representation =
|
||||||
access.machine_type.representation();
|
access.machine_type.representation();
|
||||||
// If we are loading from a Smi field and truncate the result to Word32,
|
// If we are loading from a Smi field and truncate the result to Word32,
|
||||||
// we can instead just load the high word on 64-bit architectures, which
|
// we can instead just load the high word on 64-bit architectures, which
|
||||||
@ -1989,6 +2021,7 @@ class RepresentationSelector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case IrOpcode::kLoadBuffer: {
|
case IrOpcode::kLoadBuffer: {
|
||||||
|
if (truncation.IsUnused()) return VisitUnused(node);
|
||||||
BufferAccess access = BufferAccessOf(node->op());
|
BufferAccess access = BufferAccessOf(node->op());
|
||||||
ProcessInput(node, 0, UseInfo::PointerInt()); // buffer
|
ProcessInput(node, 0, UseInfo::PointerInt()); // buffer
|
||||||
ProcessInput(node, 1, UseInfo::TruncatingWord32()); // offset
|
ProcessInput(node, 1, UseInfo::TruncatingWord32()); // offset
|
||||||
@ -2035,11 +2068,11 @@ class RepresentationSelector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case IrOpcode::kLoadElement: {
|
case IrOpcode::kLoadElement: {
|
||||||
|
if (truncation.IsUnused()) return VisitUnused(node);
|
||||||
ElementAccess access = ElementAccessOf(node->op());
|
ElementAccess access = ElementAccessOf(node->op());
|
||||||
ProcessInput(node, 0, UseInfoForBasePointer(access)); // base
|
VisitBinop(node, UseInfoForBasePointer(access),
|
||||||
ProcessInput(node, 1, UseInfo::TruncatingWord32()); // index
|
UseInfo::TruncatingWord32(),
|
||||||
ProcessRemainingInputs(node, 2);
|
access.machine_type.representation());
|
||||||
SetOutput(node, access.machine_type.representation());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case IrOpcode::kStoreElement: {
|
case IrOpcode::kStoreElement: {
|
||||||
@ -2112,6 +2145,7 @@ class RepresentationSelector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case IrOpcode::kCheckFloat64Hole: {
|
case IrOpcode::kCheckFloat64Hole: {
|
||||||
|
if (truncation.IsUnused()) return VisitUnused(node);
|
||||||
CheckFloat64HoleMode mode = CheckFloat64HoleModeOf(node->op());
|
CheckFloat64HoleMode mode = CheckFloat64HoleModeOf(node->op());
|
||||||
ProcessInput(node, 0, UseInfo::TruncatingFloat64());
|
ProcessInput(node, 0, UseInfo::TruncatingFloat64());
|
||||||
ProcessRemainingInputs(node, 1);
|
ProcessRemainingInputs(node, 1);
|
||||||
@ -2348,6 +2382,27 @@ class RepresentationSelector {
|
|||||||
node->NullAllInputs(); // Node is now dead.
|
node->NullAllInputs(); // Node is now dead.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Kill(Node* node) {
|
||||||
|
TRACE("killing #%d:%s\n", node->id(), node->op()->mnemonic());
|
||||||
|
|
||||||
|
if (node->op()->EffectInputCount() == 1) {
|
||||||
|
DCHECK_LT(0, node->op()->ControlInputCount());
|
||||||
|
// Disconnect the node from effect and control chains.
|
||||||
|
Node* control = NodeProperties::GetControlInput(node);
|
||||||
|
Node* effect = NodeProperties::GetEffectInput(node);
|
||||||
|
ReplaceEffectControlUses(node, effect, control);
|
||||||
|
} else {
|
||||||
|
DCHECK_EQ(0, node->op()->ControlInputCount());
|
||||||
|
DCHECK_EQ(0, node->op()->EffectInputCount());
|
||||||
|
DCHECK_EQ(0, node->op()->ControlOutputCount());
|
||||||
|
DCHECK_EQ(0, node->op()->EffectOutputCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
node->ReplaceUses(jsgraph_->Dead());
|
||||||
|
|
||||||
|
node->NullAllInputs(); // The {node} is now dead.
|
||||||
|
}
|
||||||
|
|
||||||
void PrintOutputInfo(NodeInfo* info) {
|
void PrintOutputInfo(NodeInfo* info) {
|
||||||
if (FLAG_trace_representation) {
|
if (FLAG_trace_representation) {
|
||||||
OFStream os(stdout);
|
OFStream os(stdout);
|
||||||
|
Loading…
Reference in New Issue
Block a user