Commit 2b4eb88c authored by Benedikt Meurer's avatar Benedikt Meurer

[turbofan] Cache conversions inserted during typed lowering.

This greatly reduces the number of nodes in the graph (by more than 20x in
some extreme cases) for the Emscripten python interpreter main function.

BUG=v8:3763
LOG=y
TEST=cctest,mjsunit,unittests
R=svenpanne@chromium.org

Review URL: https://codereview.chromium.org/802353003

Cr-Commit-Position: refs/heads/master@{#25840}
parent d287f225
......@@ -29,8 +29,8 @@ static void RelaxEffects(Node* node) {
}
JSTypedLowering::JSTypedLowering(JSGraph* jsgraph)
: jsgraph_(jsgraph), simplified_(jsgraph->zone()) {
JSTypedLowering::JSTypedLowering(JSGraph* jsgraph, Zone* zone)
: jsgraph_(jsgraph), simplified_(graph()->zone()), conversions_(zone) {
Handle<Object> zero = factory()->NewNumber(0.0);
Handle<Object> one = factory()->NewNumber(1.0);
zero_range_ = Type::Range(zero, zero, graph()->zone());
......@@ -195,9 +195,9 @@ class JSBinopReduction FINAL {
}
Node* ConvertToNumber(Node* node) {
// Avoid introducing too many eager ToNumber() operations.
Reduction reduced = lowering_->ReduceJSToNumberInput(node);
if (reduced.Changed()) return reduced.replacement();
if (NodeProperties::GetBounds(node).upper->Is(Type::PlainPrimitive())) {
return lowering_->ConvertToNumber(node);
}
Node* n = graph()->NewNode(javascript()->ToNumber(), node, context(),
effect(), control());
update_effect(n);
......@@ -497,6 +497,9 @@ Reduction JSTypedLowering::ReduceJSToBooleanInput(Node* input) {
if (result.Changed()) return result;
return Changed(input); // JSToBoolean(JSToBoolean(x)) => JSToBoolean(x)
}
// Check if we have a cached conversion.
Node* conversion = FindConversion<IrOpcode::kJSToBoolean>(input);
if (conversion) return Replace(conversion);
Type* input_type = NodeProperties::GetBounds(input).upper;
if (input_type->Is(Type::Boolean())) {
return Changed(input); // JSToBoolean(x:boolean) => x
......@@ -554,21 +557,13 @@ Reduction JSTypedLowering::ReduceJSToBoolean(Node* node) {
DCHECK(!NodeProperties::GetBounds(input).upper->Is(Type::Boolean()));
node->set_op(common()->Phi(kMachAnyTagged, input_count));
for (int i = 0; i < input_count; ++i) {
Node* value = input->InputAt(i);
// Recursively try to reduce the value first.
Reduction reduction = ReduceJSToBooleanInput(value);
if (reduction.Changed()) {
value = reduction.replacement();
} else {
// We must be very careful not to introduce cycles when pushing
// operations into phis. It is safe for {value}, since it appears
// as input to the phi that we are replacing, but it's not safe
// to simply reuse the context of the {node}. However, ToBoolean()
// does not require a context anyways, so it's safe to discard it
// here and pass the dummy context.
value = graph()->NewNode(javascript()->ToBoolean(), value,
jsgraph()->NoContextConstant());
}
// We must be very careful not to introduce cycles when pushing
// operations into phis. It is safe for {value}, since it appears
// as input to the phi that we are replacing, but it's not safe
// to simply reuse the context of the {node}. However, ToBoolean()
// does not require a context anyways, so it's safe to discard it
// here and pass the dummy context.
Node* const value = ConvertToBoolean(input->InputAt(i));
if (i < node->InputCount()) {
node->ReplaceInput(i, value);
} else {
......@@ -594,26 +589,24 @@ Reduction JSTypedLowering::ReduceJSToBoolean(Node* node) {
node->set_op(common()->Select(kMachAnyTagged, input_hint));
node->InsertInput(graph()->zone(), 0, input->InputAt(0));
for (int i = 1; i < input_count; ++i) {
Node* value = input->InputAt(i);
// Recursively try to reduce the value first.
Reduction reduction = ReduceJSToBooleanInput(value);
if (reduction.Changed()) {
value = reduction.replacement();
} else {
// We must be very careful not to introduce cycles when pushing
// operations into selects. It is safe for {value}, since it appears
// as input to the select that we are replacing, but it's not safe
// to simply reuse the context of the {node}. However, ToBoolean()
// does not require a context anyways, so it's safe to discard it
// here and pass the dummy context.
value = graph()->NewNode(javascript()->ToBoolean(), value,
jsgraph()->NoContextConstant());
}
// We must be very careful not to introduce cycles when pushing
// operations into selects. It is safe for {value}, since it appears
// as input to the select that we are replacing, but it's not safe
// to simply reuse the context of the {node}. However, ToBoolean()
// does not require a context anyways, so it's safe to discard it
// here and pass the dummy context.
Node* const value = ConvertToBoolean(input->InputAt(i));
node->ReplaceInput(i, value);
}
DCHECK_EQ(3, node->InputCount());
return Changed(node);
}
InsertConversion(node);
if (node->InputAt(1) != jsgraph()->NoContextConstant()) {
// JSToBoolean(x,context) => JSToBoolean(x,no-context)
node->ReplaceInput(1, jsgraph()->NoContextConstant());
return Changed(node);
}
return NoChange();
}
......@@ -625,6 +618,9 @@ Reduction JSTypedLowering::ReduceJSToNumberInput(Node* input) {
if (result.Changed()) return result;
return Changed(input); // JSToNumber(JSToNumber(x)) => JSToNumber(x)
}
// Check if we have a cached conversion.
Node* conversion = FindConversion<IrOpcode::kJSToNumber>(input);
if (conversion) return Replace(conversion);
Type* input_type = NodeProperties::GetBounds(input).upper;
if (input_type->Is(Type::Number())) {
// JSToNumber(x:number) => x
......@@ -671,22 +667,13 @@ Reduction JSTypedLowering::ReduceJSToNumber(Node* node) {
RelaxEffects(node);
node->set_op(common()->Phi(kMachAnyTagged, input_count));
for (int i = 0; i < input_count; ++i) {
Node* value = input->InputAt(i);
// Recursively try to reduce the value first.
Reduction reduction = ReduceJSToNumberInput(value);
if (reduction.Changed()) {
value = reduction.replacement();
} else {
// We must be very careful not to introduce cycles when pushing
// operations into phis. It is safe for {value}, since it appears
// as input to the phi that we are replacing, but it's not safe
// to simply reuse the context of the {node}. However, ToNumber()
// does not require a context anyways, so it's safe to discard it
// here and pass the dummy context.
value = graph()->NewNode(javascript()->ToNumber(), value,
jsgraph()->NoContextConstant(),
graph()->start(), graph()->start());
}
// We must be very careful not to introduce cycles when pushing
// operations into phis. It is safe for {value}, since it appears
// as input to the phi that we are replacing, but it's not safe
// to simply reuse the context of the {node}. However, ToNumber()
// does not require a context anyways, so it's safe to discard it
// here and pass the dummy context.
Node* const value = ConvertToNumber(input->InputAt(i));
if (i < node->InputCount()) {
node->ReplaceInput(i, value);
} else {
......@@ -713,32 +700,29 @@ Reduction JSTypedLowering::ReduceJSToNumber(Node* node) {
node->set_op(common()->Select(kMachAnyTagged, input_hint));
node->ReplaceInput(0, input->InputAt(0));
for (int i = 1; i < input_count; ++i) {
Node* value = input->InputAt(i);
// Recursively try to reduce the value first.
Reduction reduction = ReduceJSToNumberInput(value);
if (reduction.Changed()) {
value = reduction.replacement();
} else {
// We must be very careful not to introduce cycles when pushing
// operations into selects. It is safe for {value}, since it appears
// as input to the select that we are replacing, but it's not safe
// to simply reuse the context of the {node}. However, ToNumber()
// does not require a context anyways, so it's safe to discard it
// here and pass the dummy context.
value = graph()->NewNode(javascript()->ToNumber(), value,
jsgraph()->NoContextConstant(),
graph()->start(), graph()->start());
}
// We must be very careful not to introduce cycles when pushing
// operations into selects. It is safe for {value}, since it appears
// as input to the select that we are replacing, but it's not safe
// to simply reuse the context of the {node}. However, ToNumber()
// does not require a context anyways, so it's safe to discard it
// here and pass the dummy context.
Node* const value = ConvertToNumber(input->InputAt(i));
node->ReplaceInput(i, value);
}
node->TrimInputCount(input_count);
return Changed(node);
}
// Remember this conversion.
InsertConversion(node);
if (node->InputAt(1) != jsgraph()->NoContextConstant() ||
node->InputAt(2) != graph()->start()) {
// JSToNumber(x:plain-primitive,context) => JSToNumber(x,no-context)
node->ReplaceInput(1, jsgraph()->NoContextConstant());
node->InputAt(2) != graph()->start() ||
node->InputAt(3) != graph()->start()) {
// JSToNumber(x:plain-primitive,context,effect,control)
// => JSToNumber(x,no-context,start,start)
RelaxEffects(node);
node->ReplaceInput(1, jsgraph()->NoContextConstant());
node->ReplaceInput(2, graph()->start());
node->ReplaceInput(3, graph()->start());
return Changed(node);
}
}
......@@ -1022,6 +1006,54 @@ Reduction JSTypedLowering::Reduce(Node* node) {
}
Node* JSTypedLowering::ConvertToBoolean(Node* input) {
// Avoid inserting too many eager ToBoolean() operations.
Reduction const reduction = ReduceJSToBooleanInput(input);
if (reduction.Changed()) return reduction.replacement();
Node* const conversion = graph()->NewNode(javascript()->ToBoolean(), input,
jsgraph()->NoContextConstant());
InsertConversion(conversion);
return conversion;
}
Node* JSTypedLowering::ConvertToNumber(Node* input) {
DCHECK(NodeProperties::GetBounds(input).upper->Is(Type::PlainPrimitive()));
// Avoid inserting too many eager ToNumber() operations.
Reduction const reduction = ReduceJSToNumberInput(input);
if (reduction.Changed()) return reduction.replacement();
Node* const conversion = graph()->NewNode(javascript()->ToNumber(), input,
jsgraph()->NoContextConstant(),
graph()->start(), graph()->start());
InsertConversion(conversion);
return conversion;
}
template <IrOpcode::Value kOpcode>
Node* JSTypedLowering::FindConversion(Node* input) {
size_t const input_id = input->id();
if (input_id < conversions_.size()) {
Node* const conversion = conversions_[input_id];
if (conversion && conversion->opcode() == kOpcode) {
return conversion;
}
}
return nullptr;
}
void JSTypedLowering::InsertConversion(Node* conversion) {
DCHECK(conversion->opcode() == IrOpcode::kJSToBoolean ||
conversion->opcode() == IrOpcode::kJSToNumber);
size_t const input_id = conversion->InputAt(0)->id();
if (input_id >= conversions_.size()) {
conversions_.resize(2 * input_id + 1);
}
conversions_[input_id] = conversion;
}
Node* JSTypedLowering::Word32Shl(Node* const lhs, int32_t const rhs) {
if (rhs == 0) return lhs;
return graph()->NewNode(machine()->Word32Shl(), lhs,
......
......@@ -22,7 +22,7 @@ class MachineOperatorBuilder;
// Lowers JS-level operators to simplified operators based on types.
class JSTypedLowering FINAL : public Reducer {
public:
explicit JSTypedLowering(JSGraph* jsgraph);
JSTypedLowering(JSGraph* jsgraph, Zone* zone);
~JSTypedLowering() FINAL {}
Reduction Reduce(Node* node) FINAL;
......@@ -52,6 +52,12 @@ class JSTypedLowering FINAL : public Reducer {
Reduction ReduceUI32Shift(Node* node, Signedness left_signedness,
const Operator* shift_op);
Node* ConvertToBoolean(Node* input);
Node* ConvertToNumber(Node* input);
template <IrOpcode::Value>
Node* FindConversion(Node* input);
void InsertConversion(Node* conversion);
Node* Word32Shl(Node* const lhs, int32_t const rhs);
Factory* factory() const;
......@@ -64,6 +70,7 @@ class JSTypedLowering FINAL : public Reducer {
JSGraph* jsgraph_;
SimplifiedOperatorBuilder simplified_;
ZoneVector<Node*> conversions_; // Cache inserted JSToXXX() conversions.
Type* zero_range_;
Type* one_range_;
Type* zero_thirtyone_range_;
......
......@@ -419,7 +419,7 @@ struct TypedLoweringPhase {
ValueNumberingReducer vn_reducer(temp_zone);
LoadElimination load_elimination;
JSBuiltinReducer builtin_reducer(data->jsgraph());
JSTypedLowering typed_lowering(data->jsgraph());
JSTypedLowering typed_lowering(data->jsgraph(), temp_zone);
SimplifiedOperatorReducer simple_reducer(data->jsgraph());
GraphReducer graph_reducer(data->graph(), temp_zone);
graph_reducer.AddReducer(&vn_reducer);
......
......@@ -76,7 +76,7 @@ class JSTypedLoweringTester : public HandleAndZoneScope {
Node* reduce(Node* node) {
JSGraph jsgraph(&graph, &common, &javascript, &machine);
JSTypedLowering reducer(&jsgraph);
JSTypedLowering reducer(&jsgraph, main_zone());
Reduction reduction = reducer.Reduce(node);
if (reduction.Changed()) return reduction.replacement();
return node;
......@@ -990,7 +990,7 @@ TEST(OrderNumberBinopEffects1) {
};
for (size_t j = 0; j < arraysize(ops); j += 2) {
BinopEffectsTester B(ops[j], Type::String(), Type::String());
BinopEffectsTester B(ops[j], Type::Symbol(), Type::Symbol());
CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
......@@ -1063,8 +1063,8 @@ TEST(OrderCompareEffects) {
CHECK_EQ(B.p1, i0->InputAt(0));
CHECK_EQ(B.p0, i1->InputAt(0));
// But effects should be ordered start -> i1 -> i0 -> effect_use
B.CheckEffectOrdering(i1, i0);
// But effects should be ordered start -> i1 -> effect_use
B.CheckEffectOrdering(i1);
}
for (size_t j = 0; j < arraysize(ops); j += 2) {
......
......@@ -46,7 +46,7 @@ class JSTypedLoweringTest : public TypedGraphTest {
Reduction Reduce(Node* node) {
MachineOperatorBuilder machine(zone());
JSGraph jsgraph(graph(), common(), javascript(), &machine);
JSTypedLowering reducer(&jsgraph);
JSTypedLowering reducer(&jsgraph, zone());
return reducer.Reduce(node);
}
......
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