From 4f1597a92daddd529207cf86b6aaf9baa6f3adc2 Mon Sep 17 00:00:00 2001 From: bmeurer Date: Mon, 26 Jan 2015 01:05:47 -0800 Subject: [PATCH] [turbofan] Add new JSIntrinsicsLowering reducer. The lowering of intrinsics is therefore now decoupled from the general inlining logic. TEST=cctest,unittests R=svenpanne@chromium.org Review URL: https://codereview.chromium.org/872363002 Cr-Commit-Position: refs/heads/master@{#26263} --- BUILD.gn | 4 +- src/compiler/js-inlining.cc | 76 +------ src/compiler/js-inlining.h | 1 - src/compiler/js-intrinsic-builder.cc | 140 ------------ src/compiler/js-intrinsic-builder.h | 40 ---- src/compiler/js-intrinsic-lowering.cc | 194 ++++++++++++++++ src/compiler/js-intrinsic-lowering.h | 52 +++++ src/compiler/pipeline.cc | 3 + src/compiler/simplified-lowering.cc | 10 +- src/compiler/typer.cc | 10 + src/flag-definitions.h | 3 - test/cctest/compiler/test-run-intrinsics.cc | 18 -- .../js-intrinsic-lowering-unittest.cc | 215 ++++++++++++++++++ test/unittests/compiler/node-test-utils.cc | 2 + test/unittests/compiler/node-test-utils.h | 2 + test/unittests/unittests.gyp | 1 + tools/gyp/v8.gyp | 4 +- 17 files changed, 491 insertions(+), 284 deletions(-) delete mode 100644 src/compiler/js-intrinsic-builder.cc delete mode 100644 src/compiler/js-intrinsic-builder.h create mode 100644 src/compiler/js-intrinsic-lowering.cc create mode 100644 src/compiler/js-intrinsic-lowering.h create mode 100644 test/unittests/compiler/js-intrinsic-lowering-unittest.cc diff --git a/BUILD.gn b/BUILD.gn index f929a913cd..6510b92292 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -561,8 +561,8 @@ source_set("v8_base") { "src/compiler/js-graph.h", "src/compiler/js-inlining.cc", "src/compiler/js-inlining.h", - "src/compiler/js-intrinsic-builder.cc", - "src/compiler/js-intrinsic-builder.h", + "src/compiler/js-intrinsic-lowering.cc", + "src/compiler/js-intrinsic-lowering.h", "src/compiler/js-operator.cc", "src/compiler/js-operator.h", "src/compiler/js-typed-lowering.cc", diff --git a/src/compiler/js-inlining.cc b/src/compiler/js-inlining.cc index 040c721bad..d68c31a04a 100644 --- a/src/compiler/js-inlining.cc +++ b/src/compiler/js-inlining.cc @@ -10,7 +10,6 @@ #include "src/compiler/graph-inl.h" #include "src/compiler/graph-visualizer.h" #include "src/compiler/js-inlining.h" -#include "src/compiler/js-intrinsic-builder.h" #include "src/compiler/js-operator.h" #include "src/compiler/node-aux-data-inl.h" #include "src/compiler/node-matchers.h" @@ -36,11 +35,6 @@ class InlinerVisitor : public NullNodeVisitor { case IrOpcode::kJSCallFunction: inliner_->TryInlineJSCall(node); break; - case IrOpcode::kJSCallRuntime: - if (FLAG_turbo_inlining_intrinsics) { - inliner_->TryInlineRuntimeCall(node); - } - break; default: break; } @@ -418,70 +412,6 @@ void JSInliner::TryInlineJSCall(Node* call_node) { inlinee.InlineAtCall(jsgraph_, call_node); } - -class JSCallRuntimeAccessor { - public: - explicit JSCallRuntimeAccessor(Node* call) : call_(call) { - DCHECK_EQ(IrOpcode::kJSCallRuntime, call->opcode()); - } - - Node* formal_argument(size_t index) { - DCHECK(index < formal_arguments()); - return call_->InputAt(static_cast(index)); - } - - size_t formal_arguments() { - size_t value_inputs = call_->op()->ValueInputCount(); - return value_inputs; - } - - Node* frame_state() const { - return NodeProperties::GetFrameStateInput(call_); - } - Node* context() const { return NodeProperties::GetContextInput(call_); } - Node* control() const { return NodeProperties::GetControlInput(call_); } - Node* effect() const { return NodeProperties::GetEffectInput(call_); } - - const Runtime::Function* function() const { - return Runtime::FunctionForId(CallRuntimeParametersOf(call_->op()).id()); - } - - NodeVector inputs(Zone* zone) const { - NodeVector inputs(zone); - for (Node* const node : call_->inputs()) { - inputs.push_back(node); - } - return inputs; - } - - private: - Node* call_; -}; - - -void JSInliner::TryInlineRuntimeCall(Node* call_node) { - JSCallRuntimeAccessor call(call_node); - const Runtime::Function* f = call.function(); - - if (f->intrinsic_type != Runtime::IntrinsicType::INLINE) { - return; - } - - JSIntrinsicBuilder intrinsic_builder(jsgraph_); - - ResultAndEffect r = intrinsic_builder.BuildGraphFor( - f->function_id, call.inputs(jsgraph_->zone())); - - if (r.first != NULL) { - if (FLAG_trace_turbo_inlining) { - PrintF("Inlining %s into %s\n", f->name, - info_->shared_info()->DebugName()->ToCString().get()); - } - NodeProperties::ReplaceWithValue(call_node, r.first, r.second); - call_node->RemoveAllInputs(); - DCHECK_EQ(0, call_node->UseCount()); - } -} -} -} -} // namespace v8::internal::compiler +} // namespace compiler +} // namespace internal +} // namespace v8 diff --git a/src/compiler/js-inlining.h b/src/compiler/js-inlining.h index eef29d6a74..acaa4a724a 100644 --- a/src/compiler/js-inlining.h +++ b/src/compiler/js-inlining.h @@ -21,7 +21,6 @@ class JSInliner { void Inline(); void TryInlineJSCall(Node* node); - void TryInlineRuntimeCall(Node* node); private: friend class InlinerVisitor; diff --git a/src/compiler/js-intrinsic-builder.cc b/src/compiler/js-intrinsic-builder.cc deleted file mode 100644 index 80b69682ca..0000000000 --- a/src/compiler/js-intrinsic-builder.cc +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2014 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/compiler/access-builder.h" -#include "src/compiler/common-operator.h" -#include "src/compiler/diamond.h" -#include "src/compiler/js-intrinsic-builder.h" -#include "src/compiler/js-operator.h" -#include "src/compiler/simplified-operator.h" - - -namespace v8 { -namespace internal { -namespace compiler { - -ResultAndEffect JSIntrinsicBuilder::BuildGraphFor(Runtime::FunctionId id, - const NodeVector& arguments) { - switch (id) { - case Runtime::kInlineIsSmi: - return BuildGraphFor_IsSmi(arguments); - case Runtime::kInlineIsNonNegativeSmi: - return BuildGraphFor_IsNonNegativeSmi(arguments); - case Runtime::kInlineIsArray: - return BuildMapCheck(arguments[0], arguments[2], JS_ARRAY_TYPE); - case Runtime::kInlineIsRegExp: - return BuildMapCheck(arguments[0], arguments[2], JS_REGEXP_TYPE); - case Runtime::kInlineIsFunction: - return BuildMapCheck(arguments[0], arguments[2], JS_FUNCTION_TYPE); - case Runtime::kInlineValueOf: - return BuildGraphFor_ValueOf(arguments); - default: - break; - } - return ResultAndEffect(); -} - -ResultAndEffect JSIntrinsicBuilder::BuildGraphFor_IsSmi( - const NodeVector& arguments) { - Node* object = arguments[0]; - SimplifiedOperatorBuilder simplified(jsgraph_->zone()); - Node* condition = graph()->NewNode(simplified.ObjectIsSmi(), object); - - return ResultAndEffect(condition, arguments[2]); -} - - -ResultAndEffect JSIntrinsicBuilder::BuildGraphFor_IsNonNegativeSmi( - const NodeVector& arguments) { - Node* object = arguments[0]; - SimplifiedOperatorBuilder simplified(jsgraph_->zone()); - Node* condition = - graph()->NewNode(simplified.ObjectIsNonNegativeSmi(), object); - - return ResultAndEffect(condition, arguments[2]); -} - - -/* - * if (_isSmi(object)) { - * return false - * } else { - * return %_GetMapInstanceType(object) == map_type - * } - */ -ResultAndEffect JSIntrinsicBuilder::BuildMapCheck(Node* object, Node* effect, - InstanceType map_type) { - SimplifiedOperatorBuilder simplified(jsgraph_->zone()); - - Node* is_smi = graph()->NewNode(simplified.ObjectIsSmi(), object); - Diamond d(graph(), common(), is_smi); - - Node* map = graph()->NewNode(simplified.LoadField(AccessBuilder::ForMap()), - object, effect, d.if_false); - - Node* instance_type = graph()->NewNode( - simplified.LoadField(AccessBuilder::ForMapInstanceType()), map, map, - d.if_false); - - Node* has_map_type = - graph()->NewNode(jsgraph_->machine()->Word32Equal(), instance_type, - jsgraph_->Int32Constant(map_type)); - - Node* phi = d.Phi(static_cast(kTypeBool | kRepTagged), - jsgraph_->FalseConstant(), has_map_type); - - Node* ephi = d.EffectPhi(effect, instance_type); - - return ResultAndEffect(phi, ephi); -} - - -/* - * if (%_isSmi(object)) { - * return object; - * } else if (%_GetMapInstanceType(object) == JS_VALUE_TYPE) { - * return %_LoadValueField(object); - * } else { - * return object; - * } - */ -ResultAndEffect JSIntrinsicBuilder::BuildGraphFor_ValueOf( - const NodeVector& arguments) { - Node* object = arguments[0]; - Node* effect = arguments[2]; - SimplifiedOperatorBuilder simplified(jsgraph_->zone()); - - Node* is_smi = graph()->NewNode(simplified.ObjectIsSmi(), object); - - Diamond if_is_smi(graph(), common(), is_smi); - - Node* map = graph()->NewNode(simplified.LoadField(AccessBuilder::ForMap()), - object, effect, if_is_smi.if_false); - - Node* instance_type = graph()->NewNode( - simplified.LoadField(AccessBuilder::ForMapInstanceType()), map, map, - if_is_smi.if_false); - - Node* is_value = - graph()->NewNode(jsgraph_->machine()->Word32Equal(), instance_type, - jsgraph_->Constant(JS_VALUE_TYPE)); - - Diamond if_is_value(graph(), common(), is_value); - if_is_value.Nest(if_is_smi, false); - - Node* value = - graph()->NewNode(simplified.LoadField(AccessBuilder::ForValue()), object, - instance_type, if_is_value.if_true); - - Node* phi_is_value = if_is_value.Phi(kTypeAny, value, object); - - Node* phi = if_is_smi.Phi(kTypeAny, object, phi_is_value); - - Node* ephi = if_is_smi.EffectPhi(effect, instance_type); - - return ResultAndEffect(phi, ephi); -} -} -} -} // namespace v8::internal::compiler diff --git a/src/compiler/js-intrinsic-builder.h b/src/compiler/js-intrinsic-builder.h deleted file mode 100644 index 9336be6559..0000000000 --- a/src/compiler/js-intrinsic-builder.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2014 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef V8_COMPILER_JS_INTRINSIC_BUILDER_H_ -#define V8_COMPILER_JS_INTRINSIC_BUILDER_H_ - -#include "src/compiler/js-graph.h" -#include "src/v8.h" - -namespace v8 { -namespace internal { -namespace compiler { - -typedef std::pair ResultAndEffect; - -class JSIntrinsicBuilder { - public: - explicit JSIntrinsicBuilder(JSGraph* jsgraph) : jsgraph_(jsgraph) {} - - ResultAndEffect BuildGraphFor(Runtime::FunctionId id, - const NodeVector& arguments); - - private: - ResultAndEffect BuildMapCheck(Node* object, Node* effect, - InstanceType map_type); - ResultAndEffect BuildGraphFor_IsSmi(const NodeVector& arguments); - ResultAndEffect BuildGraphFor_IsNonNegativeSmi(const NodeVector& arguments); - ResultAndEffect BuildGraphFor_ValueOf(const NodeVector& arguments); - - - Graph* graph() const { return jsgraph_->graph(); } - CommonOperatorBuilder* common() const { return jsgraph_->common(); } - JSGraph* jsgraph_; -}; -} -} -} // namespace v8::internal::compiler - -#endif // V8_COMPILER_JS_INTRINSIC_BUILDER_H_ diff --git a/src/compiler/js-intrinsic-lowering.cc b/src/compiler/js-intrinsic-lowering.cc new file mode 100644 index 0000000000..99def3016f --- /dev/null +++ b/src/compiler/js-intrinsic-lowering.cc @@ -0,0 +1,194 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/compiler/js-intrinsic-lowering.h" + +#include "src/compiler/access-builder.h" +#include "src/compiler/js-graph.h" +#include "src/compiler/node-properties-inl.h" + +namespace v8 { +namespace internal { +namespace compiler { + +JSIntrinsicLowering::JSIntrinsicLowering(JSGraph* jsgraph) + : jsgraph_(jsgraph), simplified_(jsgraph->zone()) {} + + +Reduction JSIntrinsicLowering::Reduce(Node* node) { + if (node->opcode() != IrOpcode::kJSCallRuntime) return NoChange(); + const Runtime::Function* const f = + Runtime::FunctionForId(CallRuntimeParametersOf(node->op()).id()); + if (f->intrinsic_type != Runtime::IntrinsicType::INLINE) return NoChange(); + switch (f->function_id) { + case Runtime::kInlineIsSmi: + return ReduceInlineIsSmi(node); + case Runtime::kInlineIsNonNegativeSmi: + return ReduceInlineIsNonNegativeSmi(node); + case Runtime::kInlineIsArray: + return ReduceInlineIsInstanceType(node, JS_ARRAY_TYPE); + case Runtime::kInlineIsFunction: + return ReduceInlineIsInstanceType(node, JS_FUNCTION_TYPE); + case Runtime::kInlineIsRegExp: + return ReduceInlineIsInstanceType(node, JS_REGEXP_TYPE); + case Runtime::kInlineValueOf: + return ReduceInlineValueOf(node); + default: + break; + } + return NoChange(); +} + + +Reduction JSIntrinsicLowering::ReduceInlineIsSmi(Node* node) { + return Change(node, simplified()->ObjectIsSmi()); +} + + +Reduction JSIntrinsicLowering::ReduceInlineIsNonNegativeSmi(Node* node) { + return Change(node, simplified()->ObjectIsNonNegativeSmi()); +} + + +Reduction JSIntrinsicLowering::ReduceInlineIsInstanceType( + Node* node, InstanceType instance_type) { + // if (%_IsSmi(value)) { + // return false; + // } else { + // return %_GetInstanceType(%_GetMap(value)) == instance_type; + // } + MachineType const type = static_cast(kTypeBool | kRepTagged); + + Node* value = NodeProperties::GetValueInput(node, 0); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + + Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); + Node* branch = graph()->NewNode(common()->Branch(), check, control); + + Node* if_true = graph()->NewNode(common()->IfTrue(), branch); + Node* etrue = effect; + Node* vtrue = jsgraph()->FalseConstant(); + + Node* if_false = graph()->NewNode(common()->IfFalse(), branch); + Node* efalse = graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForMapInstanceType()), + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), value, + effect, if_false), + effect, if_false); + Node* vfalse = graph()->NewNode(machine()->Word32Equal(), efalse, + jsgraph()->Int32Constant(instance_type)); + + Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); + + // Replace all effect uses of {node} with the {ephi}. + Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge); + NodeProperties::ReplaceWithValue(node, node, ephi); + + // Turn the {node} into a Phi. + return Change(node, common()->Phi(type, 2), vtrue, vfalse, merge); +} + + +Reduction JSIntrinsicLowering::ReduceInlineValueOf(Node* node) { + // if (%_IsSmi(value)) { + // return value; + // } else if (%_GetInstanceType(%_GetMap(value)) == JS_VALUE_TYPE) { + // return %_GetValue(value); + // } else { + // return value; + // } + const Operator* const merge_op = common()->Merge(2); + const Operator* const ephi_op = common()->EffectPhi(2); + const Operator* const phi_op = common()->Phi(kMachAnyTagged, 2); + + Node* value = NodeProperties::GetValueInput(node, 0); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + + Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value); + Node* branch0 = graph()->NewNode(common()->Branch(), check0, control); + + Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); + Node* etrue0 = effect; + Node* vtrue0 = value; + + Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); + Node* efalse0; + Node* vfalse0; + { + Node* check1 = graph()->NewNode( + machine()->Word32Equal(), + graph()->NewNode( + simplified()->LoadField(AccessBuilder::ForMapInstanceType()), + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), + value, effect, if_false0), + effect, if_false0), + jsgraph()->Int32Constant(JS_VALUE_TYPE)); + Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0); + + Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); + Node* etrue1 = + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForValue()), + value, effect, if_true1); + Node* vtrue1 = etrue1; + + Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); + Node* efalse1 = effect; + Node* vfalse1 = value; + + Node* merge1 = graph()->NewNode(merge_op, if_true1, if_false1); + efalse0 = graph()->NewNode(ephi_op, etrue1, efalse1, merge1); + vfalse0 = graph()->NewNode(phi_op, vtrue1, vfalse1, merge1); + } + + Node* merge0 = graph()->NewNode(merge_op, if_true0, if_false0); + + + // Replace all effect uses of {node} with the {ephi0}. + Node* ephi0 = graph()->NewNode(ephi_op, etrue0, efalse0, merge0); + NodeProperties::ReplaceWithValue(node, node, ephi0); + + // Turn the {node} into a Phi. + return Change(node, phi_op, vtrue0, vfalse0, merge0); +} + + +Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op) { + // Remove the effects from the node and update its effect usages. + NodeProperties::ReplaceWithValue(node, node); + // Remove the inputs corresponding to context, effect and control. + NodeProperties::RemoveNonValueInputs(node); + // Finally update the operator to the new one. + node->set_op(op); + return Changed(node); +} + + +Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a, + Node* b, Node* c) { + node->set_op(op); + node->ReplaceInput(0, a); + node->ReplaceInput(1, b); + node->ReplaceInput(2, c); + node->TrimInputCount(3); + return Changed(node); +} + + +Graph* JSIntrinsicLowering::graph() const { return jsgraph()->graph(); } + + +CommonOperatorBuilder* JSIntrinsicLowering::common() const { + return jsgraph()->common(); +} + + +MachineOperatorBuilder* JSIntrinsicLowering::machine() const { + return jsgraph()->machine(); +} + +} // namespace compiler +} // namespace internal +} // namespace v8 diff --git a/src/compiler/js-intrinsic-lowering.h b/src/compiler/js-intrinsic-lowering.h new file mode 100644 index 0000000000..bc188caa06 --- /dev/null +++ b/src/compiler/js-intrinsic-lowering.h @@ -0,0 +1,52 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_COMPILER_JS_INTRINSIC_LOWERING_H_ +#define V8_COMPILER_JS_INTRINSIC_LOWERING_H_ + +#include "src/compiler/graph-reducer.h" +#include "src/compiler/simplified-operator.h" + +namespace v8 { +namespace internal { +namespace compiler { + +// Forward declarations. +class CommonOperatorBuilder; +class JSGraph; +class MachineOperatorBuilder; + + +// Lowers certain JS-level runtime calls. +class JSIntrinsicLowering FINAL : public Reducer { + public: + explicit JSIntrinsicLowering(JSGraph* jsgraph); + ~JSIntrinsicLowering() FINAL {} + + Reduction Reduce(Node* node) FINAL; + + private: + Reduction ReduceInlineIsSmi(Node* node); + Reduction ReduceInlineIsNonNegativeSmi(Node* node); + Reduction ReduceInlineIsInstanceType(Node* node, InstanceType instance_type); + Reduction ReduceInlineValueOf(Node* node); + + Reduction Change(Node* node, const Operator* op); + Reduction Change(Node* node, const Operator* op, Node* a, Node* b, Node* c); + + Graph* graph() const; + JSGraph* jsgraph() const { return jsgraph_; } + CommonOperatorBuilder* common() const; + MachineOperatorBuilder* machine() const; + SimplifiedOperatorBuilder* simplified() { return &simplified_; } + + JSGraph* jsgraph_; + SimplifiedOperatorBuilder simplified_; +}; + +} // namespace compiler +} // namespace internal +} // namespace v8 + +#endif // V8_COMPILER_JS_INTRINSIC_LOWERING_H_ diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc index 218b06745f..ef0144cfe3 100644 --- a/src/compiler/pipeline.cc +++ b/src/compiler/pipeline.cc @@ -24,6 +24,7 @@ #include "src/compiler/js-context-specialization.h" #include "src/compiler/js-generic-lowering.h" #include "src/compiler/js-inlining.h" +#include "src/compiler/js-intrinsic-lowering.h" #include "src/compiler/js-typed-lowering.h" #include "src/compiler/jump-threading.h" #include "src/compiler/load-elimination.h" @@ -428,12 +429,14 @@ struct TypedLoweringPhase { LoadElimination load_elimination; JSBuiltinReducer builtin_reducer(data->jsgraph()); JSTypedLowering typed_lowering(data->jsgraph(), temp_zone); + JSIntrinsicLowering intrinsic_lowering(data->jsgraph()); SimplifiedOperatorReducer simple_reducer(data->jsgraph()); CommonOperatorReducer common_reducer; GraphReducer graph_reducer(data->graph(), temp_zone); graph_reducer.AddReducer(&vn_reducer); graph_reducer.AddReducer(&builtin_reducer); graph_reducer.AddReducer(&typed_lowering); + graph_reducer.AddReducer(&intrinsic_lowering); graph_reducer.AddReducer(&load_elimination); graph_reducer.AddReducer(&simple_reducer); graph_reducer.AddReducer(&common_reducer); diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc index e334d1faef..9b48ff77fe 100644 --- a/src/compiler/simplified-lowering.cc +++ b/src/compiler/simplified-lowering.cc @@ -854,10 +854,10 @@ class RepresentationSelector { if (lower()) { Node* is_tagged = jsgraph_->graph()->NewNode( jsgraph_->machine()->WordAnd(), node->InputAt(0), - jsgraph_->Int32Constant(static_cast(kSmiTagMask))); + jsgraph_->IntPtrConstant(kSmiTagMask)); Node* is_smi = jsgraph_->graph()->NewNode( jsgraph_->machine()->WordEqual(), is_tagged, - jsgraph_->Int32Constant(kSmiTag)); + jsgraph_->IntPtrConstant(kSmiTag)); DeferReplacement(node, is_smi); } break; @@ -868,13 +868,13 @@ class RepresentationSelector { if (lower()) { Node* is_tagged = jsgraph_->graph()->NewNode( jsgraph_->machine()->WordAnd(), node->InputAt(0), - jsgraph_->Int32Constant(static_cast(kSmiTagMask))); + jsgraph_->IntPtrConstant(kSmiTagMask)); Node* is_smi = jsgraph_->graph()->NewNode( jsgraph_->machine()->WordEqual(), is_tagged, - jsgraph_->Int32Constant(kSmiTag)); + jsgraph_->IntPtrConstant(kSmiTag)); Node* is_non_neg = jsgraph_->graph()->NewNode( jsgraph_->machine()->IntLessThanOrEqual(), - jsgraph_->Int32Constant(0), node->InputAt(0)); + jsgraph_->IntPtrConstant(0), node->InputAt(0)); Node* is_non_neg_smi = jsgraph_->graph()->NewNode( jsgraph_->machine()->Word32And(), is_smi, is_non_neg); DeferReplacement(node, is_non_neg_smi); diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc index 4b3a8830a0..976a1b6b7a 100644 --- a/src/compiler/typer.cc +++ b/src/compiler/typer.cc @@ -1442,6 +1442,16 @@ Bounds Typer::Visitor::TypeJSCallFunction(Node* node) { Bounds Typer::Visitor::TypeJSCallRuntime(Node* node) { + switch (CallRuntimeParametersOf(node->op()).id()) { + case Runtime::kInlineIsSmi: + case Runtime::kInlineIsNonNegativeSmi: + case Runtime::kInlineIsArray: + case Runtime::kInlineIsFunction: + case Runtime::kInlineIsRegExp: + return Bounds(Type::None(zone()), Type::Boolean(zone())); + default: + break; + } return Bounds::Unbounded(zone()); } diff --git a/src/flag-definitions.h b/src/flag-definitions.h index d63342685d..dea3720951 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -403,11 +403,8 @@ DEFINE_BOOL(context_specialization, false, "enable context specialization in TurboFan") DEFINE_BOOL(turbo_deoptimization, false, "enable deoptimization in TurboFan") DEFINE_BOOL(turbo_inlining, false, "enable inlining in TurboFan") -DEFINE_BOOL(turbo_inlining_intrinsics, false, - "enable inlining of intrinsics in TurboFan") DEFINE_BOOL(trace_turbo_inlining, false, "trace TurboFan inlining") DEFINE_BOOL(loop_assignment_analysis, true, "perform loop assignment analysis") -DEFINE_IMPLICATION(turbo_inlining_intrinsics, turbo_inlining) DEFINE_IMPLICATION(turbo_inlining, turbo_types) DEFINE_BOOL(turbo_profiling, false, "enable profiling in TurboFan") // TODO(dcarney): this is just for experimentation, remove when default. diff --git a/test/cctest/compiler/test-run-intrinsics.cc b/test/cctest/compiler/test-run-intrinsics.cc index 76cbb8fc58..bd4038ed91 100644 --- a/test/cctest/compiler/test-run-intrinsics.cc +++ b/test/cctest/compiler/test-run-intrinsics.cc @@ -11,7 +11,6 @@ using namespace v8::internal::compiler; uint32_t flags = CompilationInfo::kInliningEnabled; TEST(IsSmi) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a) { return %_IsSmi(a); })", flags); @@ -25,7 +24,6 @@ TEST(IsSmi) { TEST(IsNonNegativeSmi) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a) { return %_IsNonNegativeSmi(a); })", flags); @@ -39,7 +37,6 @@ TEST(IsNonNegativeSmi) { TEST(IsMinusZero) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a) { return %_IsMinusZero(a); })", flags); @@ -53,7 +50,6 @@ TEST(IsMinusZero) { TEST(IsArray) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a) { return %_IsArray(a); })", flags); @@ -69,7 +65,6 @@ TEST(IsArray) { TEST(IsObject) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a) { return %_IsObject(a); })", flags); @@ -85,7 +80,6 @@ TEST(IsObject) { TEST(IsFunction) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a) { return %_IsFunction(a); })", flags); @@ -101,7 +95,6 @@ TEST(IsFunction) { TEST(IsRegExp) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a) { return %_IsRegExp(a); })", flags); @@ -117,7 +110,6 @@ TEST(IsRegExp) { TEST(ClassOf) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a) { return %_ClassOf(a); })", flags); @@ -133,7 +125,6 @@ TEST(ClassOf) { TEST(ObjectEquals) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a,b) { return %_ObjectEquals(a,b); })", flags); CompileRun("var o = {}"); @@ -148,7 +139,6 @@ TEST(ObjectEquals) { TEST(ValueOf) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a) { return %_ValueOf(a); })", flags); @@ -160,7 +150,6 @@ TEST(ValueOf) { TEST(SetValueOf) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a,b) { return %_SetValueOf(a,b); })", flags); @@ -171,7 +160,6 @@ TEST(SetValueOf) { TEST(StringCharFromCode) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a) { return %_StringCharFromCode(a); })", flags); @@ -182,7 +170,6 @@ TEST(StringCharFromCode) { TEST(StringCharAt) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a,b) { return %_StringCharAt(a,b); })", flags); @@ -193,7 +180,6 @@ TEST(StringCharAt) { TEST(StringCharCodeAt) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a,b) { return %_StringCharCodeAt(a,b); })", flags); @@ -205,7 +191,6 @@ TEST(StringCharCodeAt) { TEST(StringAdd) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a,b) { return %_StringAdd(a,b); })", flags); @@ -216,7 +201,6 @@ TEST(StringAdd) { TEST(StringSubString) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a,b) { return %_SubString(a,b,b+3); })", flags); @@ -227,7 +211,6 @@ TEST(StringSubString) { TEST(StringCompare) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a,b) { return %_StringCompare(a,b); })", flags); @@ -238,7 +221,6 @@ TEST(StringCompare) { TEST(CallFunction) { - FLAG_turbo_inlining_intrinsics = true; FLAG_turbo_deoptimization = true; FunctionTester T("(function(a,b) { return %_CallFunction(a, 1, 2, 3, b); })", flags); diff --git a/test/unittests/compiler/js-intrinsic-lowering-unittest.cc b/test/unittests/compiler/js-intrinsic-lowering-unittest.cc new file mode 100644 index 0000000000..20d5c069fe --- /dev/null +++ b/test/unittests/compiler/js-intrinsic-lowering-unittest.cc @@ -0,0 +1,215 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/compiler/access-builder.h" +#include "src/compiler/js-graph.h" +#include "src/compiler/js-intrinsic-lowering.h" +#include "src/compiler/js-operator.h" +#include "test/unittests/compiler/graph-unittest.h" +#include "test/unittests/compiler/node-test-utils.h" +#include "testing/gmock-support.h" + +using testing::_; +using testing::AllOf; +using testing::BitEq; +using testing::Capture; +using testing::CaptureEq; + + +namespace v8 { +namespace internal { +namespace compiler { + +class JSIntrinsicLoweringTest : public GraphTest { + public: + JSIntrinsicLoweringTest() : GraphTest(3), javascript_(zone()) {} + ~JSIntrinsicLoweringTest() OVERRIDE {} + + protected: + Reduction Reduce(Node* node) { + MachineOperatorBuilder machine(zone()); + JSGraph jsgraph(isolate(), graph(), common(), javascript(), &machine); + JSIntrinsicLowering reducer(&jsgraph); + return reducer.Reduce(node); + } + + JSOperatorBuilder* javascript() { return &javascript_; } + + private: + JSOperatorBuilder javascript_; +}; + + +// ----------------------------------------------------------------------------- +// %_IsSmi + + +TEST_F(JSIntrinsicLoweringTest, InlineIsSmi) { + Node* const input = Parameter(0); + Node* const context = Parameter(1); + Node* const effect = graph()->start(); + Node* const control = graph()->start(); + Reduction const r = Reduce( + graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineIsSmi, 1), + input, context, effect, control)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsObjectIsSmi(input)); +} + + +// ----------------------------------------------------------------------------- +// %_IsNonNegativeSmi + + +TEST_F(JSIntrinsicLoweringTest, InlineIsNonNegativeSmi) { + Node* const input = Parameter(0); + Node* const context = Parameter(1); + Node* const effect = graph()->start(); + Node* const control = graph()->start(); + Reduction const r = Reduce(graph()->NewNode( + javascript()->CallRuntime(Runtime::kInlineIsNonNegativeSmi, 1), input, + context, effect, control)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsObjectIsNonNegativeSmi(input)); +} + + +// ----------------------------------------------------------------------------- +// %_IsArray + + +TEST_F(JSIntrinsicLoweringTest, InlineIsArray) { + Node* const input = Parameter(0); + Node* const context = Parameter(1); + Node* const effect = graph()->start(); + Node* const control = graph()->start(); + Reduction const r = Reduce( + graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineIsArray, 1), + input, context, effect, control)); + ASSERT_TRUE(r.Changed()); + + Node* phi = r.replacement(); + Capture branch, if_false; + EXPECT_THAT( + phi, + IsPhi( + static_cast(kTypeBool | kRepTagged), IsFalseConstant(), + IsWord32Equal(IsLoadField(AccessBuilder::ForMapInstanceType(), + IsLoadField(AccessBuilder::ForMap(), input, + effect, CaptureEq(&if_false)), + effect, _), + IsInt32Constant(JS_ARRAY_TYPE)), + IsMerge(IsIfTrue(AllOf(CaptureEq(&branch), + IsBranch(IsObjectIsSmi(input), control))), + AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); +} + + +// ----------------------------------------------------------------------------- +// %_IsFunction + + +TEST_F(JSIntrinsicLoweringTest, InlineIsFunction) { + Node* const input = Parameter(0); + Node* const context = Parameter(1); + Node* const effect = graph()->start(); + Node* const control = graph()->start(); + Reduction const r = Reduce( + graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineIsFunction, 1), + input, context, effect, control)); + ASSERT_TRUE(r.Changed()); + + Node* phi = r.replacement(); + Capture branch, if_false; + EXPECT_THAT( + phi, + IsPhi( + static_cast(kTypeBool | kRepTagged), IsFalseConstant(), + IsWord32Equal(IsLoadField(AccessBuilder::ForMapInstanceType(), + IsLoadField(AccessBuilder::ForMap(), input, + effect, CaptureEq(&if_false)), + effect, _), + IsInt32Constant(JS_FUNCTION_TYPE)), + IsMerge(IsIfTrue(AllOf(CaptureEq(&branch), + IsBranch(IsObjectIsSmi(input), control))), + AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); +} + + +// ----------------------------------------------------------------------------- +// %_IsRegExp + + +TEST_F(JSIntrinsicLoweringTest, InlineIsRegExp) { + Node* const input = Parameter(0); + Node* const context = Parameter(1); + Node* const effect = graph()->start(); + Node* const control = graph()->start(); + Reduction const r = Reduce( + graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineIsRegExp, 1), + input, context, effect, control)); + ASSERT_TRUE(r.Changed()); + + Node* phi = r.replacement(); + Capture branch, if_false; + EXPECT_THAT( + phi, + IsPhi( + static_cast(kTypeBool | kRepTagged), IsFalseConstant(), + IsWord32Equal(IsLoadField(AccessBuilder::ForMapInstanceType(), + IsLoadField(AccessBuilder::ForMap(), input, + effect, CaptureEq(&if_false)), + effect, _), + IsInt32Constant(JS_REGEXP_TYPE)), + IsMerge(IsIfTrue(AllOf(CaptureEq(&branch), + IsBranch(IsObjectIsSmi(input), control))), + AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); +} + + +// ----------------------------------------------------------------------------- +// %_ValueOf + + +TEST_F(JSIntrinsicLoweringTest, InlineValueOf) { + Node* const input = Parameter(0); + Node* const context = Parameter(1); + Node* const effect = graph()->start(); + Node* const control = graph()->start(); + Reduction const r = Reduce( + graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineValueOf, 1), + input, context, effect, control)); + ASSERT_TRUE(r.Changed()); + + Node* phi = r.replacement(); + Capture branch0, if_false0, branch1, if_true1; + EXPECT_THAT( + phi, + IsPhi( + kMachAnyTagged, input, + IsPhi(kMachAnyTagged, IsLoadField(AccessBuilder::ForValue(), input, + effect, CaptureEq(&if_true1)), + input, + IsMerge( + AllOf(CaptureEq(&if_true1), IsIfTrue(CaptureEq(&branch1))), + IsIfFalse(AllOf( + CaptureEq(&branch1), + IsBranch( + IsWord32Equal( + IsLoadField( + AccessBuilder::ForMapInstanceType(), + IsLoadField(AccessBuilder::ForMap(), input, + effect, CaptureEq(&if_false0)), + effect, _), + IsInt32Constant(JS_VALUE_TYPE)), + CaptureEq(&if_false0)))))), + IsMerge( + IsIfTrue(AllOf(CaptureEq(&branch0), + IsBranch(IsObjectIsSmi(input), control))), + AllOf(CaptureEq(&if_false0), IsIfFalse(CaptureEq(&branch0)))))); +} + +} // namespace compiler +} // namespace internal +} // namespace v8 diff --git a/test/unittests/compiler/node-test-utils.cc b/test/unittests/compiler/node-test-utils.cc index 5f38b7289b..18b0729091 100644 --- a/test/unittests/compiler/node-test-utils.cc +++ b/test/unittests/compiler/node-test-utils.cc @@ -1473,6 +1473,8 @@ IS_UNOP_MATCHER(Float64RoundTruncate) IS_UNOP_MATCHER(Float64RoundTiesAway) IS_UNOP_MATCHER(NumberToInt32) IS_UNOP_MATCHER(NumberToUint32) +IS_UNOP_MATCHER(ObjectIsSmi) +IS_UNOP_MATCHER(ObjectIsNonNegativeSmi) #undef IS_UNOP_MATCHER } // namespace compiler diff --git a/test/unittests/compiler/node-test-utils.h b/test/unittests/compiler/node-test-utils.h index 736cf0fa41..d49d2cf9bd 100644 --- a/test/unittests/compiler/node-test-utils.h +++ b/test/unittests/compiler/node-test-utils.h @@ -131,6 +131,8 @@ Matcher IsStoreElement(const Matcher& access_matcher, const Matcher& value_matcher, const Matcher& effect_matcher, const Matcher& control_matcher); +Matcher IsObjectIsSmi(const Matcher& value_matcher); +Matcher IsObjectIsNonNegativeSmi(const Matcher& value_matcher); Matcher IsLoad(const Matcher& rep_matcher, const Matcher& base_matcher, diff --git a/test/unittests/unittests.gyp b/test/unittests/unittests.gyp index 132769424e..285ce890f7 100644 --- a/test/unittests/unittests.gyp +++ b/test/unittests/unittests.gyp @@ -51,6 +51,7 @@ 'compiler/instruction-sequence-unittest.cc', 'compiler/instruction-sequence-unittest.h', 'compiler/js-builtin-reducer-unittest.cc', + 'compiler/js-intrinsic-lowering-unittest.cc', 'compiler/js-operator-unittest.cc', 'compiler/js-typed-lowering-unittest.cc', 'compiler/load-elimination-unittest.cc', diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 9443911f12..e8081a7507 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -466,8 +466,8 @@ '../../src/compiler/js-graph.h', '../../src/compiler/js-inlining.cc', '../../src/compiler/js-inlining.h', - '../../src/compiler/js-intrinsic-builder.cc', - '../../src/compiler/js-intrinsic-builder.h', + '../../src/compiler/js-intrinsic-lowering.cc', + '../../src/compiler/js-intrinsic-lowering.h', '../../src/compiler/js-operator.cc', '../../src/compiler/js-operator.h', '../../src/compiler/js-typed-lowering.cc',