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
This commit is contained in:
parent
d7e0820a69
commit
bc475b4a6b
2
BUILD.gn
2
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",
|
||||
|
@ -51,6 +51,20 @@ FieldAccess AccessBuilder::ForExternalArrayPointer() {
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
FieldAccess AccessBuilder::ForMapInstanceType() {
|
||||
return {kTaggedBase, Map::kInstanceTypeOffset, Handle<Name>(),
|
||||
Type::UntaggedInt8(), kMachUint8};
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
FieldAccess AccessBuilder::ForValue() {
|
||||
return {kTaggedBase, JSValue::kValueOffset, Handle<Name>(), Type::Any(),
|
||||
kMachAnyTagged};
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
ElementAccess AccessBuilder::ForFixedArrayElement() {
|
||||
return {kNoBoundsCheck, kTaggedBase, FixedArray::kHeaderSize, Type::Any(),
|
||||
|
@ -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();
|
||||
|
||||
|
@ -55,6 +55,11 @@ class Graph : public GenericGraph<Node> {
|
||||
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 <class Visitor>
|
||||
void VisitNodeUsesFrom(Node* node, Visitor* visitor);
|
||||
|
@ -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<JSFunction> 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<char> 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<int>(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<Runtime::FunctionId>(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
|
||||
|
@ -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;
|
||||
|
158
src/compiler/js-intrinsic-builder.cc
Normal file
158
src/compiler/js-intrinsic-builder.cc
Normal file
@ -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
|
40
src/compiler/js-intrinsic-builder.h
Normal file
40
src/compiler/js-intrinsic-builder.h
Normal file
@ -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<Node*, Node*> 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_
|
@ -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;
|
||||
|
@ -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) \
|
||||
|
@ -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<int>(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<int>(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.
|
||||
|
@ -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) \
|
||||
|
@ -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&);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"));
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user