From bc475b4a6bf055ddf7064871ea1d90030a856386 Mon Sep 17 00:00:00 2001 From: "sigurds@chromium.org" Date: Mon, 20 Oct 2014 07:56:50 +0000 Subject: [PATCH] Add inlining for intrinsics. This issue is for discussion on how to proceed. I think the implementation of ValueOf shows that directly creating the IR does not scale. BUG= R=mstarzinger@chromium.org Review URL: https://codereview.chromium.org/612043003 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24719 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- BUILD.gn | 2 + src/compiler/access-builder.cc | 14 +++ src/compiler/access-builder.h | 6 ++ src/compiler/graph.h | 5 + src/compiler/js-inlining.cc | 93 +++++++++++++--- src/compiler/js-inlining.h | 3 +- src/compiler/js-intrinsic-builder.cc | 158 ++++++++++++++++++++++++++++ src/compiler/js-intrinsic-builder.h | 40 +++++++ src/compiler/linkage.cc | 6 ++ src/compiler/opcodes.h | 4 +- src/compiler/simplified-lowering.cc | 33 ++++++ src/compiler/simplified-operator.cc | 4 +- src/compiler/simplified-operator.h | 3 + src/compiler/typer.cc | 10 ++ src/compiler/verifier.cc | 8 ++ src/flag-definitions.h | 3 + test/cctest/compiler/test-run-inlining.cc | 97 +++++++++++++++++ test/cctest/compiler/test-run-intrinsics.cc | 76 +++++++++---- tools/gyp/v8.gyp | 2 + 19 files changed, 528 insertions(+), 39 deletions(-) create mode 100644 src/compiler/js-intrinsic-builder.cc create mode 100644 src/compiler/js-intrinsic-builder.h diff --git a/BUILD.gn b/BUILD.gn index 08dd231..56fded6 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -524,6 +524,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-operator.cc", "src/compiler/js-operator.h", "src/compiler/js-typed-lowering.cc", diff --git a/src/compiler/access-builder.cc b/src/compiler/access-builder.cc index 959c60a..051b47c 100644 --- a/src/compiler/access-builder.cc +++ b/src/compiler/access-builder.cc @@ -52,6 +52,20 @@ FieldAccess AccessBuilder::ForExternalArrayPointer() { // static +FieldAccess AccessBuilder::ForMapInstanceType() { + return {kTaggedBase, Map::kInstanceTypeOffset, Handle(), + Type::UntaggedInt8(), kMachUint8}; +} + + +// static +FieldAccess AccessBuilder::ForValue() { + return {kTaggedBase, JSValue::kValueOffset, Handle(), Type::Any(), + kMachAnyTagged}; +} + + +// static ElementAccess AccessBuilder::ForFixedArrayElement() { return {kNoBoundsCheck, kTaggedBase, FixedArray::kHeaderSize, Type::Any(), kMachAnyTagged}; diff --git a/src/compiler/access-builder.h b/src/compiler/access-builder.h index 72dd023..73c81ef 100644 --- a/src/compiler/access-builder.h +++ b/src/compiler/access-builder.h @@ -34,6 +34,12 @@ class AccessBuilder FINAL : public AllStatic { // Provides access to ExternalArray::external_pointer() field. static FieldAccess ForExternalArrayPointer(); + // Provides access to Map::instance_type() field. + static FieldAccess ForMapInstanceType(); + + // Provides access to JSValue::value() field. + static FieldAccess ForValue(); + // Provides access to FixedArray elements. static ElementAccess ForFixedArrayElement(); diff --git a/src/compiler/graph.h b/src/compiler/graph.h index 8f24cf5..9b0d234 100644 --- a/src/compiler/graph.h +++ b/src/compiler/graph.h @@ -55,6 +55,11 @@ class Graph : public GenericGraph { Node* nodes[] = {n1, n2, n3, n4, n5, n6}; return NewNode(op, arraysize(nodes), nodes); } + Node* NewNode(const Operator* op, Node* n1, Node* n2, Node* n3, Node* n4, + Node* n5, Node* n6, Node* n7) { + Node* nodes[] = {n1, n2, n3, n4, n5, n6, n7}; + return NewNode(op, arraysize(nodes), nodes); + } template void VisitNodeUsesFrom(Node* node, Visitor* visitor); diff --git a/src/compiler/js-inlining.cc b/src/compiler/js-inlining.cc index 8762a97..f834602 100644 --- a/src/compiler/js-inlining.cc +++ b/src/compiler/js-inlining.cc @@ -9,6 +9,7 @@ #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" @@ -32,7 +33,12 @@ class InlinerVisitor : public NullNodeVisitor { GenericGraphVisit::Control Post(Node* node) { switch (node->opcode()) { case IrOpcode::kJSCallFunction: - inliner_->TryInlineCall(node); + inliner_->TryInlineJSCall(node); + break; + case IrOpcode::kJSCallRuntime: + if (FLAG_turbo_inlining_intrinsics) { + inliner_->TryInlineRuntimeCall(node); + } break; default: break; @@ -285,22 +291,9 @@ void Inlinee::InlineAtCall(JSGraph* jsgraph, Node* call) { } } - // Iterate over all uses of the call node. - iter = call->uses().begin(); - while (iter != call->uses().end()) { - if (NodeProperties::IsEffectEdge(iter.edge())) { - iter.UpdateToAndIncrement(effect_output()); - } else if (NodeProperties::IsControlEdge(iter.edge())) { - UNREACHABLE(); - } else { - DCHECK(NodeProperties::IsValueEdge(iter.edge())); - iter.UpdateToAndIncrement(value_output()); - } - } + NodeProperties::ReplaceWithValue(call, value_output(), effect_output()); call->RemoveAllInputs(); DCHECK_EQ(0, call->UseCount()); - // TODO(sigurds) Remove this once we copy. - unique_return()->RemoveAllInputs(); } @@ -368,7 +361,7 @@ Node* JSInliner::CreateArgumentsAdaptorFrameState(JSCallFunctionAccessor* call, } -void JSInliner::TryInlineCall(Node* call_node) { +void JSInliner::TryInlineJSCall(Node* call_node) { JSCallFunctionAccessor call(call_node); HeapObjectMatcher match(call.jsfunction()); @@ -391,7 +384,7 @@ void JSInliner::TryInlineCall(Node* call_node) { CompilationInfoWithZone info(function); Parse(function, &info); - if (info.scope()->arguments() != NULL) { + if (info.scope()->arguments() != NULL && info.strict_mode() != STRICT) { // For now do not inline functions that use their arguments array. SmartArrayPointer name = function->shared()->DebugName()->ToCString(); if (FLAG_trace_turbo_inlining) { @@ -440,6 +433,72 @@ void JSInliner::TryInlineCall(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 = OperatorProperties::GetValueInputCount(call_->op()); + 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(OpParameter(call_)); + } + + NodeVector inputs(Zone* zone) const { + NodeVector inputs(zone); + for (InputIter it = call_->inputs().begin(); it != call_->inputs().end(); + ++it) { + inputs.push_back(*it); + } + 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 diff --git a/src/compiler/js-inlining.h b/src/compiler/js-inlining.h index f135170..98cf3aa 100644 --- a/src/compiler/js-inlining.h +++ b/src/compiler/js-inlining.h @@ -20,7 +20,8 @@ class JSInliner { : info_(info), jsgraph_(jsgraph) {} void Inline(); - void TryInlineCall(Node* node); + 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 new file mode 100644 index 0000000..4d8f607 --- /dev/null +++ b/src/compiler/js-intrinsic-builder.cc @@ -0,0 +1,158 @@ +// 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/generic-node-inl.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); + Node* branch = graph()->NewNode(common()->Branch(), is_smi, graph()->start()); + Node* if_true = graph()->NewNode(common()->IfTrue(), branch); + Node* if_false = graph()->NewNode(common()->IfFalse(), branch); + + Node* map = graph()->NewNode(simplified.LoadField(AccessBuilder::ForMap()), + object, effect, if_false); + + Node* instance_type = graph()->NewNode( + simplified.LoadField(AccessBuilder::ForMapInstanceType()), map, map, + if_false); + + Node* has_map_type = + graph()->NewNode(jsgraph_->machine()->Word32Equal(), instance_type, + jsgraph_->Int32Constant(map_type)); + + Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); + + Node* phi = + graph()->NewNode(common()->Phi((MachineType)(kTypeBool | kRepTagged), 2), + jsgraph_->FalseConstant(), has_map_type, merge); + + Node* ephi = + graph()->NewNode(common()->EffectPhi(2), effect, instance_type, merge); + + 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); + Node* branch = graph()->NewNode(common()->Branch(), is_smi, graph()->start()); + Node* if_true = graph()->NewNode(common()->IfTrue(), branch); + Node* if_false = graph()->NewNode(common()->IfFalse(), branch); + + Node* map = graph()->NewNode(simplified.LoadField(AccessBuilder::ForMap()), + object, effect, if_false); + + Node* instance_type = graph()->NewNode( + simplified.LoadField(AccessBuilder::ForMapInstanceType()), map, map, + if_false); + + Node* is_value = + graph()->NewNode(jsgraph_->machine()->Word32Equal(), instance_type, + jsgraph_->Constant(JS_VALUE_TYPE)); + + Node* branch_is_value = + graph()->NewNode(common()->Branch(), is_value, if_false); + Node* is_value_true = graph()->NewNode(common()->IfTrue(), branch_is_value); + Node* is_value_false = graph()->NewNode(common()->IfFalse(), branch_is_value); + + Node* value = + graph()->NewNode(simplified.LoadField(AccessBuilder::ForValue()), object, + instance_type, is_value_true); + + Node* merge_is_value = + graph()->NewNode(common()->Merge(2), is_value_true, is_value_false); + + Node* phi_is_value = graph()->NewNode(common()->Phi((MachineType)kTypeAny, 2), + value, object, merge_is_value); + + + Node* merge = graph()->NewNode(common()->Merge(2), if_true, merge_is_value); + + Node* phi = graph()->NewNode(common()->Phi((MachineType)kTypeAny, 2), object, + phi_is_value, merge); + + Node* ephi = + graph()->NewNode(common()->EffectPhi(2), effect, instance_type, merge); + + return ResultAndEffect(phi, ephi); +} +} +} +} // namespace v8::internal::compiler diff --git a/src/compiler/js-intrinsic-builder.h b/src/compiler/js-intrinsic-builder.h new file mode 100644 index 0000000..9336be6 --- /dev/null +++ b/src/compiler/js-intrinsic-builder.h @@ -0,0 +1,40 @@ +// 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/linkage.cc b/src/compiler/linkage.cc index cd59799..5b7bc68 100644 --- a/src/compiler/linkage.cc +++ b/src/compiler/linkage.cc @@ -178,6 +178,12 @@ bool Linkage::NeedsFrameState(Runtime::FunctionId function) { case Runtime::kThrow: case Runtime::kTypedArraySetFastCases: case Runtime::kTypedArrayInitializeFromArrayLike: + case Runtime::kDebugEvaluateGlobal: + case Runtime::kOwnKeys: + case Runtime::kGetOwnPropertyNames: + case Runtime::kIsPropertyEnumerable: + case Runtime::kGetPrototype: + case Runtime::kSparseJoinWithSeparator: return true; default: return false; diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index ea8263d..8b68e60 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -158,7 +158,9 @@ V(LoadField) \ V(LoadElement) \ V(StoreField) \ - V(StoreElement) + V(StoreElement) \ + V(ObjectIsSmi) \ + V(ObjectIsNonNegativeSmi) // Opcodes for Machine-level operators. #define MACHINE_OP_LIST(V) \ diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc index 8db430e..ae65d16 100644 --- a/src/compiler/simplified-lowering.cc +++ b/src/compiler/simplified-lowering.cc @@ -699,6 +699,39 @@ class RepresentationSelector { if (lower()) lowering->DoStoreElement(node); break; } + case IrOpcode::kObjectIsSmi: { + ProcessInput(node, 0, kMachAnyTagged); + SetOutput(node, kRepBit | kTypeBool); + if (lower()) { + Node* is_tagged = jsgraph_->graph()->NewNode( + jsgraph_->machine()->WordAnd(), node->InputAt(0), + jsgraph_->Int32Constant(static_cast(kSmiTagMask))); + Node* is_smi = jsgraph_->graph()->NewNode( + jsgraph_->machine()->WordEqual(), is_tagged, + jsgraph_->Int32Constant(kSmiTag)); + DeferReplacement(node, is_smi); + } + break; + } + case IrOpcode::kObjectIsNonNegativeSmi: { + ProcessInput(node, 0, kMachAnyTagged); + SetOutput(node, kRepBit | kTypeBool); + if (lower()) { + Node* is_tagged = jsgraph_->graph()->NewNode( + jsgraph_->machine()->WordAnd(), node->InputAt(0), + jsgraph_->Int32Constant(static_cast(kSmiTagMask))); + Node* is_smi = jsgraph_->graph()->NewNode( + jsgraph_->machine()->WordEqual(), is_tagged, + jsgraph_->Int32Constant(kSmiTag)); + Node* is_non_neg = jsgraph_->graph()->NewNode( + jsgraph_->machine()->IntLessThanOrEqual(), + jsgraph_->Int32Constant(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); + } + break; + } //------------------------------------------------------------------ // Machine-level operators. diff --git a/src/compiler/simplified-operator.cc b/src/compiler/simplified-operator.cc index fc1d464..fc8415f 100644 --- a/src/compiler/simplified-operator.cc +++ b/src/compiler/simplified-operator.cc @@ -135,7 +135,9 @@ const ElementAccess& ElementAccessOf(const Operator* op) { V(ChangeUint32ToTagged, Operator::kNoProperties, 1) \ V(ChangeFloat64ToTagged, Operator::kNoProperties, 1) \ V(ChangeBoolToBit, Operator::kNoProperties, 1) \ - V(ChangeBitToBool, Operator::kNoProperties, 1) + V(ChangeBitToBool, Operator::kNoProperties, 1) \ + V(ObjectIsSmi, Operator::kNoProperties, 1) \ + V(ObjectIsNonNegativeSmi, Operator::kNoProperties, 1) #define ACCESS_OP_LIST(V) \ diff --git a/src/compiler/simplified-operator.h b/src/compiler/simplified-operator.h index db29fd1..7e80fc3 100644 --- a/src/compiler/simplified-operator.h +++ b/src/compiler/simplified-operator.h @@ -146,6 +146,9 @@ class SimplifiedOperatorBuilder FINAL { const Operator* ChangeBoolToBit(); const Operator* ChangeBitToBool(); + const Operator* ObjectIsSmi(); + const Operator* ObjectIsNonNegativeSmi(); + const Operator* LoadField(const FieldAccess&); const Operator* StoreField(const FieldAccess&); diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc index fc71fd2..70f80cb 100644 --- a/src/compiler/typer.cc +++ b/src/compiler/typer.cc @@ -1228,6 +1228,16 @@ Bounds Typer::Visitor::TypeStoreElement(Node* node) { } +Bounds Typer::Visitor::TypeObjectIsSmi(Node* node) { + return Bounds(Type::Boolean()); +} + + +Bounds Typer::Visitor::TypeObjectIsNonNegativeSmi(Node* node) { + return Bounds(Type::Boolean()); +} + + // Machine operators. Bounds Typer::Visitor::TypeLoad(Node* node) { diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index 0400500..f2ad4ba 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -486,6 +486,14 @@ GenericGraphVisit::Control Verifier::Visitor::Pre(Node* node) { CHECK(bounds(node).upper->Is(Type::Boolean())); break; } + case IrOpcode::kObjectIsSmi: + CHECK(bounds(Operand(node)).upper->Is(Type::Any())); + CHECK(bounds(node).upper->Is(Type::Boolean())); + break; + case IrOpcode::kObjectIsNonNegativeSmi: + CHECK(bounds(Operand(node)).upper->Is(Type::Any())); + CHECK(bounds(node).upper->Is(Type::Boolean())); + break; case IrOpcode::kChangeTaggedToInt32: { // Signed32 /\ Tagged -> Signed32 /\ UntaggedInt32 diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 83b0abf..2f291a4 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -347,7 +347,10 @@ 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_IMPLICATION(turbo_inlining_intrinsics, turbo_inlining) DEFINE_IMPLICATION(turbo_inlining, turbo_types) DEFINE_BOOL(turbo_profiling, false, "enable profiling in TurboFan") diff --git a/test/cctest/compiler/test-run-inlining.cc b/test/cctest/compiler/test-run-inlining.cc index ad82fec..8656b98 100644 --- a/test/cctest/compiler/test-run-inlining.cc +++ b/test/cctest/compiler/test-run-inlining.cc @@ -350,4 +350,101 @@ TEST(InlineNonStrictIntoStrict) { } +TEST(InlineIntrinsicIsSmi) { + FLAG_turbo_deoptimization = true; + FunctionTester T( + "(function () {" + "var x = 42;" + "function bar(s,t) { return %_IsSmi(x); };" + "return bar;" + "})();", + CompilationInfo::kInliningEnabled | + CompilationInfo::kContextSpecializing | + CompilationInfo::kTypingEnabled); + + InstallAssertInlineCountHelper(CcTest::isolate()); + T.CheckCall(T.true_value(), T.Val(12), T.Val(4)); +} + + +TEST(InlineIntrinsicIsNonNegativeSmi) { + FLAG_turbo_deoptimization = true; + FunctionTester T( + "(function () {" + "var x = 42;" + "function bar(s,t) { return %_IsNonNegativeSmi(x); };" + "return bar;" + "})();", + CompilationInfo::kInliningEnabled | + CompilationInfo::kContextSpecializing | + CompilationInfo::kTypingEnabled); + + InstallAssertInlineCountHelper(CcTest::isolate()); + T.CheckCall(T.true_value(), T.Val(12), T.Val(4)); +} + + +TEST(InlineIntrinsicIsArray) { + FLAG_turbo_deoptimization = true; + FunctionTester T( + "(function () {" + "var x = [1,2,3];" + "function bar(s,t) { return %_IsArray(x); };" + "return bar;" + "})();", + CompilationInfo::kInliningEnabled | + CompilationInfo::kContextSpecializing | + CompilationInfo::kTypingEnabled); + + InstallAssertInlineCountHelper(CcTest::isolate()); + T.CheckCall(T.true_value(), T.Val(12), T.Val(4)); + + FunctionTester T2( + "(function () {" + "var x = 32;" + "function bar(s,t) { return %_IsArray(x); };" + "return bar;" + "})();", + CompilationInfo::kInliningEnabled | + CompilationInfo::kContextSpecializing | + CompilationInfo::kTypingEnabled); + + T2.CheckCall(T.false_value(), T.Val(12), T.Val(4)); + + FunctionTester T3( + "(function () {" + "var x = bar;" + "function bar(s,t) { return %_IsArray(x); };" + "return bar;" + "})();", + CompilationInfo::kInliningEnabled | + CompilationInfo::kContextSpecializing | + CompilationInfo::kTypingEnabled); + + T3.CheckCall(T.false_value(), T.Val(12), T.Val(4)); +} + + +TEST(InlineWithArguments) { + FLAG_turbo_deoptimization = true; + FunctionTester T( + "(function () {" + " function foo(s,t,u) { AssertInlineCount(2); " + " return foo.arguments.length == 3 && " + " foo.arguments[0] == 13 && " + " foo.arguments[1] == 14 && " + " foo.arguments[2] == 15; " + " }" + " function bar() { return foo(13, 14, 15); };" + " return bar;" + "}" + ")();", + CompilationInfo::kInliningEnabled | + CompilationInfo::kContextSpecializing | + CompilationInfo::kTypingEnabled); + + InstallAssertInlineCountHelper(CcTest::isolate()); + T.CheckCall(T.true_value(), T.Val(12), T.Val(14)); +} + #endif // V8_TURBOFAN_TARGET diff --git a/test/cctest/compiler/test-run-intrinsics.cc b/test/cctest/compiler/test-run-intrinsics.cc index a1b5676..76cbb8f 100644 --- a/test/cctest/compiler/test-run-intrinsics.cc +++ b/test/cctest/compiler/test-run-intrinsics.cc @@ -8,10 +8,12 @@ using namespace v8::internal; using namespace v8::internal::compiler; - +uint32_t flags = CompilationInfo::kInliningEnabled; TEST(IsSmi) { - FunctionTester T("(function(a) { return %_IsSmi(a); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a) { return %_IsSmi(a); })", flags); T.CheckTrue(T.Val(1)); T.CheckFalse(T.Val(1.1)); @@ -23,7 +25,9 @@ TEST(IsSmi) { TEST(IsNonNegativeSmi) { - FunctionTester T("(function(a) { return %_IsNonNegativeSmi(a); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a) { return %_IsNonNegativeSmi(a); })", flags); T.CheckTrue(T.Val(1)); T.CheckFalse(T.Val(1.1)); @@ -35,7 +39,9 @@ TEST(IsNonNegativeSmi) { TEST(IsMinusZero) { - FunctionTester T("(function(a) { return %_IsMinusZero(a); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a) { return %_IsMinusZero(a); })", flags); T.CheckFalse(T.Val(1)); T.CheckFalse(T.Val(1.1)); @@ -47,7 +53,9 @@ TEST(IsMinusZero) { TEST(IsArray) { - FunctionTester T("(function(a) { return %_IsArray(a); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a) { return %_IsArray(a); })", flags); T.CheckFalse(T.NewObject("(function() {})")); T.CheckTrue(T.NewObject("([1])")); @@ -61,7 +69,9 @@ TEST(IsArray) { TEST(IsObject) { - FunctionTester T("(function(a) { return %_IsObject(a); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a) { return %_IsObject(a); })", flags); T.CheckFalse(T.NewObject("(function() {})")); T.CheckTrue(T.NewObject("([1])")); @@ -75,7 +85,9 @@ TEST(IsObject) { TEST(IsFunction) { - FunctionTester T("(function(a) { return %_IsFunction(a); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a) { return %_IsFunction(a); })", flags); T.CheckTrue(T.NewObject("(function() {})")); T.CheckFalse(T.NewObject("([1])")); @@ -89,7 +101,9 @@ TEST(IsFunction) { TEST(IsRegExp) { - FunctionTester T("(function(a) { return %_IsRegExp(a); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a) { return %_IsRegExp(a); })", flags); T.CheckFalse(T.NewObject("(function() {})")); T.CheckFalse(T.NewObject("([1])")); @@ -103,7 +117,9 @@ TEST(IsRegExp) { TEST(ClassOf) { - FunctionTester T("(function(a) { return %_ClassOf(a); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a) { return %_ClassOf(a); })", flags); T.CheckCall(T.Val("Function"), T.NewObject("(function() {})")); T.CheckCall(T.Val("Array"), T.NewObject("([1])")); @@ -117,7 +133,9 @@ TEST(ClassOf) { TEST(ObjectEquals) { - FunctionTester T("(function(a,b) { return %_ObjectEquals(a,b); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a,b) { return %_ObjectEquals(a,b); })", flags); CompileRun("var o = {}"); T.CheckTrue(T.NewObject("(o)"), T.NewObject("(o)")); @@ -130,7 +148,9 @@ TEST(ObjectEquals) { TEST(ValueOf) { - FunctionTester T("(function(a) { return %_ValueOf(a); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a) { return %_ValueOf(a); })", flags); T.CheckCall(T.Val("a"), T.Val("a")); T.CheckCall(T.Val("b"), T.NewObject("(new String('b'))")); @@ -140,7 +160,9 @@ TEST(ValueOf) { TEST(SetValueOf) { - FunctionTester T("(function(a,b) { return %_SetValueOf(a,b); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a,b) { return %_SetValueOf(a,b); })", flags); T.CheckCall(T.Val("a"), T.NewObject("(new String)"), T.Val("a")); T.CheckCall(T.Val(123), T.NewObject("(new Number)"), T.Val(123)); @@ -149,7 +171,9 @@ TEST(SetValueOf) { TEST(StringCharFromCode) { - FunctionTester T("(function(a) { return %_StringCharFromCode(a); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a) { return %_StringCharFromCode(a); })", flags); T.CheckCall(T.Val("a"), T.Val(97)); T.CheckCall(T.Val("\xE2\x9D\x8A"), T.Val(0x274A)); @@ -158,7 +182,9 @@ TEST(StringCharFromCode) { TEST(StringCharAt) { - FunctionTester T("(function(a,b) { return %_StringCharAt(a,b); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a,b) { return %_StringCharAt(a,b); })", flags); T.CheckCall(T.Val("e"), T.Val("huge fan!"), T.Val(3)); T.CheckCall(T.Val("f"), T.Val("\xE2\x9D\x8A fan!"), T.Val(2)); @@ -167,7 +193,10 @@ TEST(StringCharAt) { TEST(StringCharCodeAt) { - FunctionTester T("(function(a,b) { return %_StringCharCodeAt(a,b); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a,b) { return %_StringCharCodeAt(a,b); })", + flags); T.CheckCall(T.Val('e'), T.Val("huge fan!"), T.Val(3)); T.CheckCall(T.Val('f'), T.Val("\xE2\x9D\x8A fan!"), T.Val(2)); @@ -176,7 +205,9 @@ TEST(StringCharCodeAt) { TEST(StringAdd) { - FunctionTester T("(function(a,b) { return %_StringAdd(a,b); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a,b) { return %_StringAdd(a,b); })", flags); T.CheckCall(T.Val("aaabbb"), T.Val("aaa"), T.Val("bbb")); T.CheckCall(T.Val("aaa"), T.Val("aaa"), T.Val("")); @@ -185,7 +216,9 @@ TEST(StringAdd) { TEST(StringSubString) { - FunctionTester T("(function(a,b) { return %_SubString(a,b,b+3); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a,b) { return %_SubString(a,b,b+3); })", flags); T.CheckCall(T.Val("aaa"), T.Val("aaabbb"), T.Val(0.0)); T.CheckCall(T.Val("abb"), T.Val("aaabbb"), T.Val(2)); @@ -194,7 +227,9 @@ TEST(StringSubString) { TEST(StringCompare) { - FunctionTester T("(function(a,b) { return %_StringCompare(a,b); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a,b) { return %_StringCompare(a,b); })", flags); T.CheckCall(T.Val(-1), T.Val("aaa"), T.Val("bbb")); T.CheckCall(T.Val(0.0), T.Val("bbb"), T.Val("bbb")); @@ -203,7 +238,10 @@ TEST(StringCompare) { TEST(CallFunction) { - FunctionTester T("(function(a,b) { return %_CallFunction(a, 1, 2, 3, b); })"); + FLAG_turbo_inlining_intrinsics = true; + FLAG_turbo_deoptimization = true; + FunctionTester T("(function(a,b) { return %_CallFunction(a, 1, 2, 3, b); })", + flags); CompileRun("function f(a,b,c) { return a + b + c + this.d; }"); T.CheckCall(T.Val(129), T.NewObject("({d:123})"), T.NewObject("f")); diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 196df4f..028b5d5 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -438,6 +438,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-operator.cc', '../../src/compiler/js-operator.h', '../../src/compiler/js-typed-lowering.cc', -- 2.7.4