Commit 2744fcbb authored by bmeurer's avatar bmeurer Committed by Commit bot

[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}
parent c4ef8a8d
......@@ -390,10 +390,10 @@ struct CommonOperatorGlobalCache final {
template <int kEffectInputCount>
struct EffectPhiOperator final : public Operator {
EffectPhiOperator()
: Operator( // --
IrOpcode::kEffectPhi, Operator::kPure, // opcode
"EffectPhi", // name
0, kEffectInputCount, 1, 0, 1, 0) {} // counts
: Operator( // --
IrOpcode::kEffectPhi, Operator::kKontrol, // opcode
"EffectPhi", // name
0, kEffectInputCount, 1, 0, 1, 0) {} // counts
};
#define CACHED_EFFECT_PHI(input_count) \
EffectPhiOperator<input_count> kEffectPhi##input_count##Operator;
......@@ -827,10 +827,10 @@ const Operator* CommonOperatorBuilder::EffectPhi(int effect_input_count) {
break;
}
// Uncached.
return new (zone()) Operator( // --
IrOpcode::kEffectPhi, Operator::kPure, // opcode
"EffectPhi", // name
0, effect_input_count, 1, 0, 1, 0); // counts
return new (zone()) Operator( // --
IrOpcode::kEffectPhi, Operator::kKontrol, // opcode
"EffectPhi", // name
0, effect_input_count, 1, 0, 1, 0); // counts
}
const Operator* CommonOperatorBuilder::BeginRegion(
......
......@@ -28,6 +28,7 @@ class Truncation final {
}
// Queries.
bool IsUnused() const { return kind_ == TruncationKind::kNone; }
bool TruncatesToWord32() const {
return LessGeneral(kind_, TruncationKind::kWord32);
}
......
......@@ -761,17 +761,30 @@ class RepresentationSelector {
// values {kTypeAny}.
void VisitInputs(Node* node) {
int tagged_count = node->op()->ValueInputCount() +
OperatorProperties::GetContextInputCount(node->op());
// Visit value and context inputs as tagged.
OperatorProperties::GetContextInputCount(node->op()) +
OperatorProperties::GetFrameStateInputCount(node->op());
// Visit value, context and frame state inputs as tagged.
for (int i = 0; i < tagged_count; i++) {
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++) {
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.
void VisitBinop(Node* node, UseInfo left_use, UseInfo right_use,
MachineRepresentation output,
......@@ -934,18 +947,20 @@ class RepresentationSelector {
void VisitCall(Node* node, SimplifiedLowering* lowering) {
const CallDescriptor* desc = CallDescriptorOf(node->op());
int params = static_cast<int>(desc->ParameterCount());
int value_input_count = node->op()->ValueInputCount();
// 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) {
// The target of the call.
ProcessInput(node, i, UseInfo::None());
ProcessInput(node, i, UseInfo::Any());
} else if ((i - 1) < params) {
ProcessInput(node, i, TruncatingUseInfoFromRepresentation(
desc->GetInputType(i).representation()));
} else {
ProcessInput(node, i, UseInfo::None());
ProcessInput(node, i, UseInfo::AnyTagged());
}
}
ProcessRemainingInputs(node, value_input_count);
if (desc->ReturnCount() > 0) {
SetOutput(node, desc->GetReturnType(0).representation());
......@@ -1109,6 +1124,7 @@ class RepresentationSelector {
void VisitSpeculativeAdditiveOp(Node* node, Truncation truncation,
SimplifiedLowering* lowering) {
if (truncation.IsUnused()) return VisitUnused(node);
if (BothInputsAre(node, type_cache_.kSigned32OrMinusZero) &&
NodeProperties::GetType(node)->Is(Type::Signed32())) {
// int32 + int32 = int32 ==> signed Int32Add/Sub
......@@ -1166,11 +1182,21 @@ class RepresentationSelector {
// Depending on the operator, propagate new usage info to the inputs.
void VisitNode(Node* node, Truncation truncation,
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()) {
//------------------------------------------------------------------
// Common operators.
//------------------------------------------------------------------
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:
return VisitLeaf(node, MachineRepresentation::kNone);
case IrOpcode::kParameter: {
......@@ -1284,6 +1310,7 @@ class RepresentationSelector {
case IrOpcode::kSpeculativeNumberLessThan:
case IrOpcode::kSpeculativeNumberLessThanOrEqual:
case IrOpcode::kSpeculativeNumberEqual: {
if (truncation.IsUnused()) return VisitUnused(node);
// Number comparisons reduce to integer comparisons for integer inputs.
if (TypeOf(node->InputAt(0))->Is(Type::Unsigned32()) &&
TypeOf(node->InputAt(1))->Is(Type::Unsigned32())) {
......@@ -1338,6 +1365,7 @@ class RepresentationSelector {
return;
}
case IrOpcode::kSpeculativeNumberMultiply: {
if (truncation.IsUnused()) return VisitUnused(node);
if (BothInputsAre(node, Type::Integral32()) &&
(NodeProperties::GetType(node)->Is(Type::Signed32()) ||
NodeProperties::GetType(node)->Is(Type::Unsigned32()) ||
......@@ -1412,6 +1440,7 @@ class RepresentationSelector {
return;
}
case IrOpcode::kSpeculativeNumberDivide: {
if (truncation.IsUnused()) return VisitUnused(node);
if (BothInputsAreUnsigned32(node) && truncation.TruncatesToWord32()) {
// => unsigned Uint32Div
VisitWord32TruncatingBinop(node);
......@@ -1517,6 +1546,7 @@ class RepresentationSelector {
return;
}
case IrOpcode::kSpeculativeNumberModulus: {
if (truncation.IsUnused()) return VisitUnused(node);
if (BothInputsAreUnsigned32(node) && truncation.TruncatesToWord32()) {
// => unsigned Uint32Mod
VisitWord32TruncatingBinop(node);
......@@ -1630,6 +1660,7 @@ class RepresentationSelector {
return;
}
case IrOpcode::kSpeculativeNumberShiftLeft: {
if (truncation.IsUnused()) return VisitUnused(node);
if (BothInputsAre(node, Type::NumberOrOddball())) {
Type* rhs_type = GetUpperBound(node->InputAt(1));
VisitBinop(node, UseInfo::TruncatingWord32(),
......@@ -1941,8 +1972,9 @@ class RepresentationSelector {
return;
}
case IrOpcode::kLoadField: {
if (truncation.IsUnused()) return VisitUnused(node);
FieldAccess access = FieldAccessOf(node->op());
MachineRepresentation representation =
MachineRepresentation const representation =
access.machine_type.representation();
// 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
......@@ -1989,6 +2021,7 @@ class RepresentationSelector {
return;
}
case IrOpcode::kLoadBuffer: {
if (truncation.IsUnused()) return VisitUnused(node);
BufferAccess access = BufferAccessOf(node->op());
ProcessInput(node, 0, UseInfo::PointerInt()); // buffer
ProcessInput(node, 1, UseInfo::TruncatingWord32()); // offset
......@@ -2035,11 +2068,11 @@ class RepresentationSelector {
return;
}
case IrOpcode::kLoadElement: {
if (truncation.IsUnused()) return VisitUnused(node);
ElementAccess access = ElementAccessOf(node->op());
ProcessInput(node, 0, UseInfoForBasePointer(access)); // base
ProcessInput(node, 1, UseInfo::TruncatingWord32()); // index
ProcessRemainingInputs(node, 2);
SetOutput(node, access.machine_type.representation());
VisitBinop(node, UseInfoForBasePointer(access),
UseInfo::TruncatingWord32(),
access.machine_type.representation());
return;
}
case IrOpcode::kStoreElement: {
......@@ -2112,6 +2145,7 @@ class RepresentationSelector {
return;
}
case IrOpcode::kCheckFloat64Hole: {
if (truncation.IsUnused()) return VisitUnused(node);
CheckFloat64HoleMode mode = CheckFloat64HoleModeOf(node->op());
ProcessInput(node, 0, UseInfo::TruncatingFloat64());
ProcessRemainingInputs(node, 1);
......@@ -2348,6 +2382,27 @@ class RepresentationSelector {
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) {
if (FLAG_trace_representation) {
OFStream os(stdout);
......
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