Index: src/hydrogen.cc |
diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
index 9727d91a58a3f9afbdc5aa11c2b2c5ca1d6cf201..071d3a624a13cbc3d2d5654d2db1297792638327 100644 |
--- a/src/hydrogen.cc |
+++ b/src/hydrogen.cc |
@@ -166,7 +166,8 @@ void HBasicBlock::Finish(HControlInstruction* end) { |
void HBasicBlock::Goto(HBasicBlock* block, FunctionState* state) { |
- bool drop_extra = state != NULL && state->drop_extra(); |
+ bool drop_extra = state != NULL && |
+ state->return_handling() == DROP_EXTRA_ON_RETURN; |
bool arguments_pushed = state != NULL && state->arguments_pushed(); |
if (block->IsInlineReturnTarget()) { |
@@ -181,18 +182,15 @@ void HBasicBlock::Goto(HBasicBlock* block, FunctionState* state) { |
void HBasicBlock::AddLeaveInlined(HValue* return_value, |
- HBasicBlock* target, |
FunctionState* state) { |
- bool drop_extra = state != NULL && state->drop_extra(); |
- bool arguments_pushed = state != NULL && state->arguments_pushed(); |
- |
- ASSERT(target->IsInlineReturnTarget()); |
+ ASSERT(state->function_return()->IsInlineReturnTarget()); |
ASSERT(return_value != NULL); |
- AddInstruction(new(zone()) HLeaveInlined(arguments_pushed)); |
- last_environment_ = last_environment()->DiscardInlined(drop_extra); |
+ AddInstruction(new(zone()) HLeaveInlined(state->arguments_pushed())); |
+ last_environment_ = last_environment()->DiscardInlined( |
+ state->return_handling() == DROP_EXTRA_ON_RETURN); |
last_environment()->Push(return_value); |
AddSimulate(BailoutId::None()); |
- HGoto* instr = new(zone()) HGoto(target); |
+ HGoto* instr = new(zone()) HGoto(state->function_return()); |
Finish(instr); |
} |
@@ -3828,23 +3826,24 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { |
ASSERT(!HasStackOverflow()); |
ASSERT(current_block() != NULL); |
ASSERT(current_block()->HasPredecessor()); |
+ FunctionState* state = function_state(); |
AstContext* context = call_context(); |
if (context == NULL) { |
// Not an inlined return, so an actual one. |
CHECK_ALIVE(VisitForValue(stmt->expression())); |
HValue* result = environment()->Pop(); |
current_block()->FinishExit(new(zone()) HReturn(result)); |
- } else if (function_state()->is_construct()) { |
- // Return from an inlined construct call. In a test context the return |
- // value will always evaluate to true, in a value context the return value |
- // needs to be a JSObject. |
+ } else if (state->return_handling() == CONSTRUCT_CALL_RETURN) { |
+ // Return from an inlined construct call. In a test context the return value |
+ // will always evaluate to true, in a value context the return value needs |
+ // to be a JSObject. |
if (context->IsTest()) { |
TestContext* test = TestContext::cast(context); |
CHECK_ALIVE(VisitForEffect(stmt->expression())); |
- current_block()->Goto(test->if_true(), function_state()); |
+ current_block()->Goto(test->if_true(), state); |
} else if (context->IsEffect()) { |
CHECK_ALIVE(VisitForEffect(stmt->expression())); |
- current_block()->Goto(function_return(), function_state()); |
+ current_block()->Goto(function_return(), state); |
} else { |
ASSERT(context->IsValue()); |
CHECK_ALIVE(VisitForValue(stmt->expression())); |
@@ -3859,31 +3858,34 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { |
typecheck->SetSuccessorAt(0, if_spec_object); |
typecheck->SetSuccessorAt(1, not_spec_object); |
current_block()->Finish(typecheck); |
- if_spec_object->AddLeaveInlined(return_value, |
- function_return(), |
- function_state()); |
- not_spec_object->AddLeaveInlined(receiver, |
- function_return(), |
- function_state()); |
+ if_spec_object->AddLeaveInlined(return_value, state); |
+ not_spec_object->AddLeaveInlined(receiver, state); |
+ } |
+ } else if (state->return_handling() == SETTER_CALL_RETURN) { |
+ // Return from an inlined setter call. The returned value is never used, the |
+ // value of an assignment is always the value of the RHS of the assignment. |
+ CHECK_ALIVE(VisitForEffect(stmt->expression())); |
+ if (context->IsTest()) { |
+ context->ReturnValue(environment()->Lookup(1)); |
+ } else if (context->IsEffect()) { |
+ current_block()->Goto(function_return(), state); |
+ } else { |
+ ASSERT(context->IsValue()); |
+ current_block()->AddLeaveInlined(environment()->Lookup(1), state); |
} |
} else { |
- // Return from an inlined function, visit the subexpression in the |
+ // Return from a normal inlined function. Visit the subexpression in the |
// expression context of the call. |
if (context->IsTest()) { |
TestContext* test = TestContext::cast(context); |
- VisitForControl(stmt->expression(), |
- test->if_true(), |
- test->if_false()); |
+ VisitForControl(stmt->expression(), test->if_true(), test->if_false()); |
} else if (context->IsEffect()) { |
CHECK_ALIVE(VisitForEffect(stmt->expression())); |
- current_block()->Goto(function_return(), function_state()); |
+ current_block()->Goto(function_return(), state); |
} else { |
ASSERT(context->IsValue()); |
CHECK_ALIVE(VisitForValue(stmt->expression())); |
- HValue* return_value = Pop(); |
- current_block()->AddLeaveInlined(return_value, |
- function_return(), |
- function_state()); |
+ current_block()->AddLeaveInlined(Pop(), state); |
} |
} |
set_current_block(NULL); |
@@ -5230,8 +5232,15 @@ void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) { |
Handle<AccessorPair> accessors; |
Handle<JSObject> holder; |
if (LookupAccessorPair(map, name, &accessors, &holder)) { |
+ Handle<JSFunction> setter(JSFunction::cast(accessors->setter())); |
+ AddCheckConstantFunction(holder, object, map, true); |
+ if (FLAG_inline_accessors && TryInlineSetter(setter, expr, value)) { |
+ return; |
+ } |
Drop(2); |
- instr = BuildCallSetter(object, value, map, accessors, holder); |
+ AddInstruction(new(zone()) HPushArgument(object)); |
+ AddInstruction(new(zone()) HPushArgument(value)); |
+ instr = new(zone()) HCallConstantFunction(setter, 2); |
} else { |
Drop(2); |
CHECK_ALIVE(instr = BuildStoreNamedMonomorphic(object, |
@@ -6630,7 +6639,7 @@ int HGraphBuilder::InliningAstSize(Handle<JSFunction> target) { |
bool HGraphBuilder::TryInline(CallKind call_kind, |
Handle<JSFunction> target, |
int arguments_count, |
- HValue* receiver, |
+ HValue* implicit_return_value, |
BailoutId ast_id, |
BailoutId return_id, |
ReturnHandlingFlag return_handling) { |
@@ -6795,7 +6804,7 @@ bool HGraphBuilder::TryInline(CallKind call_kind, |
function, |
undefined, |
call_kind, |
- function_state()->is_construct()); |
+ function_state()->return_handling()); |
#ifdef V8_TARGET_ARCH_IA32 |
// IA32 only, overwrite the caller's context in the deoptimization |
// environment with the correct one. |
@@ -6830,7 +6839,7 @@ bool HGraphBuilder::TryInline(CallKind call_kind, |
arguments_count, |
function, |
call_kind, |
- function_state()->is_construct(), |
+ function_state()->return_handling(), |
function->scope()->arguments(), |
arguments_values); |
function_state()->set_entry(enter_inlined); |
@@ -6862,27 +6871,42 @@ bool HGraphBuilder::TryInline(CallKind call_kind, |
TraceInline(target, caller, NULL); |
if (current_block() != NULL) { |
- // Add default return value (i.e. undefined for normals calls or the newly |
- // allocated receiver for construct calls) if control can fall off the |
- // body. In a test context, undefined is false and any JSObject is true. |
- if (call_context()->IsValue()) { |
- ASSERT(function_return() != NULL); |
- HValue* return_value = function_state()->is_construct() |
- ? receiver |
- : undefined; |
- current_block()->AddLeaveInlined(return_value, |
- function_return(), |
- function_state()); |
- } else if (call_context()->IsEffect()) { |
- ASSERT(function_return() != NULL); |
- current_block()->Goto(function_return(), function_state()); |
+ FunctionState* state = function_state(); |
+ if (state->return_handling() == CONSTRUCT_CALL_RETURN) { |
+ // Falling from the end of an inlined construct call. In a test context |
Michael Starzinger
2012/08/08 13:05:33
Hmm, how about "Falling off the end ..."?
Sven Panne
2012/08/08 14:15:20
Done.
|
+ // the return value will always evaluate to true, in a value context the |
+ // return value is the newly allocated receiver. |
+ if (call_context()->IsTest()) { |
+ current_block()->Goto(inlined_test_context()->if_true(), state); |
+ } else if (call_context()->IsEffect()) { |
+ current_block()->Goto(function_return(), state); |
+ } else { |
+ ASSERT(call_context()->IsValue()); |
+ current_block()->AddLeaveInlined(implicit_return_value, state); |
+ } |
+ } else if (state->return_handling() == SETTER_CALL_RETURN) { |
+ // Falling from the end of an inlined setter call. The returned value is |
Michael Starzinger
2012/08/08 13:05:33
Likewise.
Sven Panne
2012/08/08 14:15:20
Done.
|
+ // never used, the value of an assignment is always the value of the RHS |
+ // of the assignment. |
+ if (call_context()->IsTest()) { |
+ inlined_test_context()->ReturnValue(implicit_return_value); |
+ } else if (call_context()->IsEffect()) { |
+ current_block()->Goto(function_return(), state); |
+ } else { |
+ ASSERT(call_context()->IsValue()); |
+ current_block()->AddLeaveInlined(implicit_return_value, state); |
+ } |
} else { |
- ASSERT(call_context()->IsTest()); |
- ASSERT(inlined_test_context() != NULL); |
- HBasicBlock* target = function_state()->is_construct() |
- ? inlined_test_context()->if_true() |
- : inlined_test_context()->if_false(); |
- current_block()->Goto(target, function_state()); |
+ // Falling from the end of a normal inlined function. This basically means |
Michael Starzinger
2012/08/08 13:05:33
Likewise.
Sven Panne
2012/08/08 14:15:20
Done.
|
+ // returning undefined. |
+ if (call_context()->IsTest()) { |
+ current_block()->Goto(inlined_test_context()->if_false(), state); |
+ } else if (call_context()->IsEffect()) { |
+ current_block()->Goto(function_return(), state); |
+ } else { |
+ ASSERT(call_context()->IsValue()); |
+ current_block()->AddLeaveInlined(undefined, state); |
+ } |
} |
} |
@@ -6961,6 +6985,19 @@ bool HGraphBuilder::TryInlineGetter(Handle<JSFunction> getter, |
} |
+bool HGraphBuilder::TryInlineSetter(Handle<JSFunction> setter, |
+ Assignment* assignment, |
+ HValue* implicit_return_value) { |
+ return TryInline(CALL_AS_METHOD, |
+ setter, |
+ 1, |
+ implicit_return_value, |
+ assignment->id(), |
+ assignment->AssignmentId(), |
+ SETTER_CALL_RETURN); |
+} |
+ |
+ |
bool HGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr, bool drop_extra) { |
if (!expr->target()->shared()->HasBuiltinFunctionId()) return false; |
BuiltinFunctionId id = expr->target()->shared()->builtin_function_id(); |
@@ -8644,7 +8681,7 @@ void HGraphBuilder::GenerateIsConstructCall(CallRuntime* call) { |
ASSERT(call->arguments()->length() == 0); |
if (function_state()->outer() != NULL) { |
// We are generating graph for inlined function. |
- HValue* value = function_state()->is_construct() |
+ HValue* value = function_state()->return_handling() == CONSTRUCT_CALL_RETURN |
? graph()->GetConstantTrue() |
: graph()->GetConstantFalse(); |
return ast_context()->ReturnValue(value); |
@@ -9234,7 +9271,7 @@ HEnvironment* HEnvironment::CopyForInlining( |
FunctionLiteral* function, |
HConstant* undefined, |
CallKind call_kind, |
- bool is_construct) const { |
+ ReturnHandlingFlag return_handling) const { |
Michael Starzinger
2012/08/08 13:05:33
Since this flag is not only used to distinguish th
Sven Panne
2012/08/08 14:15:20
Done.
|
ASSERT(frame_type() == JS_FUNCTION); |
// Outer environment is a copy of this one without the arguments. |
@@ -9244,7 +9281,7 @@ HEnvironment* HEnvironment::CopyForInlining( |
outer->Drop(arguments + 1); // Including receiver. |
outer->ClearHistory(); |
- if (is_construct) { |
+ if (return_handling == CONSTRUCT_CALL_RETURN) { |
// Create artificial constructor stub environment. The receiver should |
// actually be the constructor function, but we pass the newly allocated |
// object instead, DoComputeConstructStubFrame() relies on that. |
@@ -9268,7 +9305,8 @@ HEnvironment* HEnvironment::CopyForInlining( |
// builtin function, pass undefined as the receiver for function |
// calls (instead of the global receiver). |
if ((target->shared()->native() || !function->is_classic_mode()) && |
- call_kind == CALL_AS_FUNCTION && !is_construct) { |
+ call_kind == CALL_AS_FUNCTION && |
+ return_handling != CONSTRUCT_CALL_RETURN) { |
inner->SetValueAt(0, undefined); |
} |
inner->SetValueAt(arity + 1, LookupContext()); |