Chromium Code Reviews| 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()); |