Commit dc889623 authored by ulan's avatar ulan Committed by Commit bot

Do not bailout from optimizing functions that use f(x, arguments)

if there is not enough type-feedback to detect that f is Function.prototype.apply.

BUG=v8:3709
LOG=N
TEST=mjsunit/regress/regress-3709

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

Cr-Commit-Position: refs/heads/master@{#25447}
parent 87e9d839
...@@ -1879,6 +1879,13 @@ class Call FINAL : public Expression { ...@@ -1879,6 +1879,13 @@ class Call FINAL : public Expression {
BailoutId ReturnId() const { return BailoutId(local_id(0)); } BailoutId ReturnId() const { return BailoutId(local_id(0)); }
BailoutId EvalOrLookupId() const { return BailoutId(local_id(1)); } BailoutId EvalOrLookupId() const { return BailoutId(local_id(1)); }
bool is_uninitialized() const {
return IsUninitializedField::decode(bit_field_);
}
void set_is_uninitialized(bool b) {
bit_field_ = IsUninitializedField::update(bit_field_, b);
}
enum CallType { enum CallType {
POSSIBLY_EVAL_CALL, POSSIBLY_EVAL_CALL,
GLOBAL_CALL, GLOBAL_CALL,
...@@ -1903,7 +1910,8 @@ class Call FINAL : public Expression { ...@@ -1903,7 +1910,8 @@ class Call FINAL : public Expression {
: Expression(zone, pos), : Expression(zone, pos),
call_feedback_slot_(FeedbackVectorICSlot::Invalid()), call_feedback_slot_(FeedbackVectorICSlot::Invalid()),
expression_(expression), expression_(expression),
arguments_(arguments) { arguments_(arguments),
bit_field_(IsUninitializedField::encode(false)) {
if (expression->IsProperty()) { if (expression->IsProperty()) {
expression->AsProperty()->mark_for_call(); expression->AsProperty()->mark_for_call();
} }
...@@ -1919,6 +1927,8 @@ class Call FINAL : public Expression { ...@@ -1919,6 +1927,8 @@ class Call FINAL : public Expression {
Handle<JSFunction> target_; Handle<JSFunction> target_;
Handle<Cell> cell_; Handle<Cell> cell_;
Handle<AllocationSite> allocation_site_; Handle<AllocationSite> allocation_site_;
class IsUninitializedField : public BitField8<bool, 0, 1> {};
uint8_t bit_field_;
}; };
......
...@@ -4050,8 +4050,12 @@ void EffectContext::ReturnValue(HValue* value) { ...@@ -4050,8 +4050,12 @@ void EffectContext::ReturnValue(HValue* value) {
void ValueContext::ReturnValue(HValue* value) { void ValueContext::ReturnValue(HValue* value) {
// The value is tracked in the bailout environment, and communicated // The value is tracked in the bailout environment, and communicated
// through the environment as the result of the expression. // through the environment as the result of the expression.
if (!arguments_allowed() && value->CheckFlag(HValue::kIsArguments)) { if (value->CheckFlag(HValue::kIsArguments)) {
owner()->Bailout(kBadValueContextForArgumentsValue); if (flag_ == ARGUMENTS_FAKED) {
value = owner()->graph()->GetConstantUndefined();
} else if (!arguments_allowed()) {
owner()->Bailout(kBadValueContextForArgumentsValue);
}
} }
owner()->Push(value); owner()->Push(value);
} }
...@@ -4277,6 +4281,14 @@ void HOptimizedGraphBuilder::VisitExpressions( ...@@ -4277,6 +4281,14 @@ void HOptimizedGraphBuilder::VisitExpressions(
} }
void HOptimizedGraphBuilder::VisitExpressions(ZoneList<Expression*>* exprs,
ArgumentsAllowedFlag flag) {
for (int i = 0; i < exprs->length(); ++i) {
CHECK_ALIVE(VisitForValue(exprs->at(i), flag));
}
}
bool HOptimizedGraphBuilder::BuildGraph() { bool HOptimizedGraphBuilder::BuildGraph() {
if (current_info()->function()->is_generator()) { if (current_info()->function()->is_generator()) {
Bailout(kFunctionIsAGenerator); Bailout(kFunctionIsAGenerator);
...@@ -8861,13 +8873,8 @@ bool HOptimizedGraphBuilder::TryIndirectCall(Call* expr) { ...@@ -8861,13 +8873,8 @@ bool HOptimizedGraphBuilder::TryIndirectCall(Call* expr) {
// is supported. // is supported.
if (current_info()->scope()->arguments() == NULL) return false; if (current_info()->scope()->arguments() == NULL) return false;
ZoneList<Expression*>* args = expr->arguments(); if (!CanBeFunctionApplyArguments(expr)) return false;
if (args->length() != 2) return false;
VariableProxy* arg_two = args->at(1)->AsVariableProxy();
if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false;
HValue* arg_two_value = LookupAndMakeLive(arg_two->var());
if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
BuildFunctionApply(expr); BuildFunctionApply(expr);
return true; return true;
} }
...@@ -9154,6 +9161,17 @@ bool HOptimizedGraphBuilder::TryHandleArrayCallNew(CallNew* expr, ...@@ -9154,6 +9161,17 @@ bool HOptimizedGraphBuilder::TryHandleArrayCallNew(CallNew* expr,
} }
bool HOptimizedGraphBuilder::CanBeFunctionApplyArguments(Call* expr) {
ZoneList<Expression*>* args = expr->arguments();
if (args->length() != 2) return false;
VariableProxy* arg_two = args->at(1)->AsVariableProxy();
if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false;
HValue* arg_two_value = LookupAndMakeLive(arg_two->var());
if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
return true;
}
void HOptimizedGraphBuilder::VisitCall(Call* expr) { void HOptimizedGraphBuilder::VisitCall(Call* expr) {
DCHECK(!HasStackOverflow()); DCHECK(!HasStackOverflow());
DCHECK(current_block() != NULL); DCHECK(current_block() != NULL);
...@@ -9190,13 +9208,14 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) { ...@@ -9190,13 +9208,14 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
if (FLAG_hydrogen_track_positions) SetSourcePosition(expr->position()); if (FLAG_hydrogen_track_positions) SetSourcePosition(expr->position());
// Push the function under the receiver.
environment()->SetExpressionStackAt(0, function);
Push(receiver);
if (function->IsConstant() && if (function->IsConstant() &&
HConstant::cast(function)->handle(isolate())->IsJSFunction()) { HConstant::cast(function)->handle(isolate())->IsJSFunction()) {
// Push the function under the receiver.
environment()->SetExpressionStackAt(0, function);
Push(receiver);
Handle<JSFunction> known_function = Handle<JSFunction>::cast( Handle<JSFunction> known_function = Handle<JSFunction>::cast(
HConstant::cast(function)->handle(isolate())); HConstant::cast(function)->handle(isolate()));
expr->set_target(known_function); expr->set_target(known_function);
...@@ -9232,7 +9251,20 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) { ...@@ -9232,7 +9251,20 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
} }
} else { } else {
CHECK_ALIVE(VisitExpressions(expr->arguments())); ArgumentsAllowedFlag arguments_flag = ARGUMENTS_NOT_ALLOWED;
if (CanBeFunctionApplyArguments(expr) && expr->is_uninitialized()) {
// We have to use EAGER deoptimization here because Deoptimizer::SOFT
// gets ignored by the always-opt flag, which leads to incorrect code.
Add<HDeoptimize>("Insufficient type feedback for call with arguments",
Deoptimizer::EAGER);
arguments_flag = ARGUMENTS_FAKED;
}
// Push the function under the receiver.
environment()->SetExpressionStackAt(0, function);
Push(receiver);
CHECK_ALIVE(VisitExpressions(expr->arguments(), arguments_flag));
CallFunctionFlags flags = receiver->type().IsJSObject() CallFunctionFlags flags = receiver->type().IsJSObject()
? NO_CALL_FUNCTION_FLAGS : CALL_AS_METHOD; ? NO_CALL_FUNCTION_FLAGS : CALL_AS_METHOD;
call = New<HCallFunction>(function, argument_count, flags); call = New<HCallFunction>(function, argument_count, flags);
......
...@@ -748,7 +748,8 @@ class HOptimizedGraphBuilder; ...@@ -748,7 +748,8 @@ class HOptimizedGraphBuilder;
enum ArgumentsAllowedFlag { enum ArgumentsAllowedFlag {
ARGUMENTS_NOT_ALLOWED, ARGUMENTS_NOT_ALLOWED,
ARGUMENTS_ALLOWED ARGUMENTS_ALLOWED,
ARGUMENTS_FAKED
}; };
...@@ -2257,7 +2258,6 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { ...@@ -2257,7 +2258,6 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
#endif #endif
} }
} }
HValue* LookupAndMakeLive(Variable* var) { HValue* LookupAndMakeLive(Variable* var) {
HEnvironment* env = environment(); HEnvironment* env = environment();
int index = env->IndexFor(var); int index = env->IndexFor(var);
...@@ -2286,6 +2286,8 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { ...@@ -2286,6 +2286,8 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
// Visit a list of expressions from left to right, each in a value context. // Visit a list of expressions from left to right, each in a value context.
void VisitExpressions(ZoneList<Expression*>* exprs); void VisitExpressions(ZoneList<Expression*>* exprs);
void VisitExpressions(ZoneList<Expression*>* exprs,
ArgumentsAllowedFlag flag);
// Remove the arguments from the bailout environment and emit instructions // Remove the arguments from the bailout environment and emit instructions
// to push them as outgoing parameters. // to push them as outgoing parameters.
...@@ -2719,6 +2721,8 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { ...@@ -2719,6 +2721,8 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
HInstruction* BuildCallConstantFunction(Handle<JSFunction> target, HInstruction* BuildCallConstantFunction(Handle<JSFunction> target,
int argument_count); int argument_count);
bool CanBeFunctionApplyArguments(Call* expr);
// The translation state of the currently-being-translated function. // The translation state of the currently-being-translated function.
FunctionState* function_state_; FunctionState* function_state_;
......
...@@ -89,6 +89,14 @@ bool TypeFeedbackOracle::StoreIsUninitialized(TypeFeedbackId ast_id) { ...@@ -89,6 +89,14 @@ bool TypeFeedbackOracle::StoreIsUninitialized(TypeFeedbackId ast_id) {
} }
bool TypeFeedbackOracle::CallIsUninitialized(FeedbackVectorICSlot slot) {
Handle<Object> value = GetInfo(slot);
return value->IsUndefined() ||
value.is_identical_to(
TypeFeedbackVector::UninitializedSentinel(isolate()));
}
bool TypeFeedbackOracle::CallIsMonomorphic(FeedbackVectorICSlot slot) { bool TypeFeedbackOracle::CallIsMonomorphic(FeedbackVectorICSlot slot) {
Handle<Object> value = GetInfo(slot); Handle<Object> value = GetInfo(slot);
return value->IsAllocationSite() || value->IsJSFunction(); return value->IsAllocationSite() || value->IsJSFunction();
......
...@@ -25,6 +25,7 @@ class TypeFeedbackOracle: public ZoneObject { ...@@ -25,6 +25,7 @@ class TypeFeedbackOracle: public ZoneObject {
bool LoadIsUninitialized(TypeFeedbackId id); bool LoadIsUninitialized(TypeFeedbackId id);
bool StoreIsUninitialized(TypeFeedbackId id); bool StoreIsUninitialized(TypeFeedbackId id);
bool CallIsUninitialized(FeedbackVectorICSlot slot);
bool CallIsMonomorphic(FeedbackVectorICSlot slot); bool CallIsMonomorphic(FeedbackVectorICSlot slot);
bool KeyedArrayCallIsHoley(TypeFeedbackId id); bool KeyedArrayCallIsHoley(TypeFeedbackId id);
bool CallNewIsMonomorphic(FeedbackVectorSlot slot); bool CallNewIsMonomorphic(FeedbackVectorSlot slot);
......
...@@ -510,12 +510,13 @@ void AstTyper::VisitProperty(Property* expr) { ...@@ -510,12 +510,13 @@ void AstTyper::VisitProperty(Property* expr) {
void AstTyper::VisitCall(Call* expr) { void AstTyper::VisitCall(Call* expr) {
// Collect type feedback. // Collect type feedback.
RECURSE(Visit(expr->expression())); RECURSE(Visit(expr->expression()));
FeedbackVectorICSlot slot = expr->CallFeedbackSlot();
expr->set_is_uninitialized(oracle()->CallIsUninitialized(slot));
if (!expr->expression()->IsProperty() && if (!expr->expression()->IsProperty() &&
expr->IsUsingCallFeedbackSlot(isolate()) && expr->IsUsingCallFeedbackSlot(isolate()) &&
oracle()->CallIsMonomorphic(expr->CallFeedbackSlot())) { oracle()->CallIsMonomorphic(slot)) {
expr->set_target(oracle()->GetCallTarget(expr->CallFeedbackSlot())); expr->set_target(oracle()->GetCallTarget(slot));
Handle<AllocationSite> site = Handle<AllocationSite> site = oracle()->GetCallAllocationSite(slot);
oracle()->GetCallAllocationSite(expr->CallFeedbackSlot());
expr->set_allocation_site(site); expr->set_allocation_site(site);
} }
......
// 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.
// Flags: --allow-natives-syntax
function getobj() {
return { bar : function() { return 0}};
}
function foo() {
var obj = getobj();
var length = arguments.length;
if (length == 0) {
obj.bar();
} else {
obj.bar.apply(obj, arguments);
}
}
foo();
foo();
%OptimizeFunctionOnNextCall(foo);
foo();
assertOptimized(foo);
foo(10);
assertUnoptimized(foo);
%ClearFunctionTypeFeedback(foo);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment