| Index: src/hydrogen.cc
|
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc
|
| index 4db3c7fe20427af2a6a82d138f1b07b00fb1c528..a5e2d07fe4926cc4e7db1047ac6888d16116070a 100644
|
| --- a/src/hydrogen.cc
|
| +++ b/src/hydrogen.cc
|
| @@ -8338,7 +8338,16 @@ bool HOptimizedGraphBuilder::TryInlineApiCall(Handle<JSFunction> function,
|
| }
|
|
|
|
|
| -bool HOptimizedGraphBuilder::TryCallApply(Call* expr) {
|
| +static Handle<JSFunction> BuiltinFunctionById(Handle<Context> native_context,
|
| + BuiltinFunctionId id) {
|
| + Handle<FixedArray> builtins(native_context->builtin_js_functions());
|
| + Handle<JSFunction> result(
|
| + JSFunction::cast(builtins->get(static_cast<int>(id))));
|
| + return result;
|
| +}
|
| +
|
| +
|
| +bool HOptimizedGraphBuilder::TryIndirectCall(Call* expr) {
|
| ASSERT(expr->expression()->IsProperty());
|
|
|
| if (!expr->IsMonomorphic()) {
|
| @@ -8346,20 +8355,159 @@ bool HOptimizedGraphBuilder::TryCallApply(Call* expr) {
|
| }
|
| Handle<Map> function_map = expr->GetReceiverTypes()->first();
|
| if (function_map->instance_type() != JS_FUNCTION_TYPE ||
|
| - !expr->target()->shared()->HasBuiltinFunctionId() ||
|
| - expr->target()->shared()->builtin_function_id() != kFunctionApply) {
|
| + !expr->target()->shared()->HasBuiltinFunctionId()) {
|
| return false;
|
| }
|
|
|
| + switch (expr->target()->shared()->builtin_function_id()) {
|
| + case kFunctionApply:
|
| + return TryCallApply(expr);
|
| + case kFunctionCall:
|
| + return TryCallCall(expr);
|
| + default:
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +
|
| +bool HOptimizedGraphBuilder::TryCallCall(Call* expr) {
|
| if (current_info()->scope()->arguments() == NULL) return false;
|
| + // It is very likely that x in x.call(...) is reference to .slice
|
| + // in all the recognized patterns, however, if it's not,
|
| + // we shouldn't go into a deopt loop
|
| + if (!current_info()->shared_info()->use_optimistic_optimizations()) {
|
| + return false;
|
| + }
|
|
|
| ZoneList<Expression*>* args = expr->arguments();
|
| - if (args->length() != 2) return false;
|
| + if (args->length() < 1 || args->length() > 3) return false;
|
| + if (!IsLiveArguments(args->at(0))) return false;
|
| +
|
| + HValue* lower_bound = graph()->GetConstant0();
|
| + HValue* upper_bound = NULL;
|
| + // f.call(arguments, ConstantSmi) or
|
| + // f.call(arguments, ConstantSmi, ConstantSmi)
|
| + if (args->length() >= 2) {
|
| + CHECK_ALIVE_OR_RETURN(VisitForValue(args->at(1)), true);
|
| + HValue* lower_bound_arg = Pop();
|
| + if (!lower_bound_arg->IsConstant() ||
|
| + !HConstant::cast(lower_bound_arg)->HasSmiValue()) {
|
| + return false;
|
| + }
|
| + lower_bound = lower_bound_arg;
|
| + }
|
| + // f.call(arguments, ConstantSmi, ConstantSmi)
|
| + if (args->length() >= 3) {
|
| + CHECK_ALIVE_OR_RETURN(VisitForValue(args->at(2)), true);
|
| + HValue* upper_bound_arg = Pop();
|
| + if (!upper_bound_arg->IsConstant() ||
|
| + !HConstant::cast(upper_bound_arg)->HasSmiValue()) {
|
| + return false;
|
| + }
|
| + upper_bound = upper_bound_arg;
|
| + }
|
|
|
| - 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;
|
| + NoObservableSideEffectsScope scope(this);
|
| +
|
| + HValue* arguments_length = NULL;
|
| + HInstruction* arguments_elements = NULL;
|
| + int max_length = JSObject::kInitialMaxFastElementArray;
|
| + HValue* function = Pop(); // f
|
| + Drop(1); // call
|
| +
|
| + {
|
| + HConstant* slice = Add<HConstant>(
|
| + BuiltinFunctionById(isolate()->native_context(), kArraySlice));
|
| + IfBuilder check_slice(this);
|
| + check_slice.IfNot<HCompareObjectEqAndBranch>(function, slice);
|
| + check_slice.Then();
|
| + Handle<SharedFunctionInfo> shared_info = current_info()->shared_info();
|
| + Add<HPushArguments>(AddUncasted<HConstant>(shared_info));
|
| + Add<HCallRuntime>(
|
| + isolate()->factory()->empty_string(),
|
| + Runtime::FunctionForId(Runtime::kDisableOptimisticOptimizations),
|
| + 1);
|
| + check_slice.Deopt("expected a reference to Array.prototype.slice");
|
| + check_slice.End();
|
| + }
|
| +
|
| + if (function_state()->outer() == NULL) {
|
| + arguments_elements = Add<HArgumentsElements>(false);
|
| + arguments_length = AddUncasted<HArgumentsLength>(arguments_elements);
|
| + } else {
|
| + EnsureArgumentsArePushedForAccess();
|
| +
|
| + arguments_elements = function_state()->arguments_elements();
|
| + int args_count = environment()->
|
| + arguments_environment()->parameter_count() - 1;
|
| + arguments_length = AddUncasted<HConstant>(
|
| + static_cast<int32_t>(args_count));
|
| + }
|
| + Add<HBoundsCheck>(arguments_length, Add<HConstant>(max_length));
|
| +
|
| + // Sanitize lower bound
|
| + if (HConstant::cast(lower_bound)->Integer32Value() < 0) {
|
| + lower_bound = AddUncasted<HAdd>(lower_bound, arguments_length);
|
| + lower_bound = AddUncasted<HMathMinMax>(
|
| + lower_bound, graph()->GetConstant0(), HMathMinMax::kMathMax);
|
| + } else {
|
| + lower_bound = AddUncasted<HMathMinMax>(
|
| + lower_bound, arguments_length, HMathMinMax::kMathMin);
|
| + }
|
| +
|
| + // Sanitize upper bound
|
| + if (upper_bound == NULL) {
|
| + upper_bound = arguments_length;
|
| + } else if (HConstant::cast(upper_bound)->Integer32Value() < 0) {
|
| + upper_bound = AddUncasted<HAdd>(upper_bound, arguments_length);
|
| + upper_bound = AddUncasted<HMathMinMax>(
|
| + upper_bound, graph()->GetConstant0(), HMathMinMax::kMathMax);
|
| + } else {
|
| + upper_bound = AddUncasted<HMathMinMax>(
|
| + upper_bound, arguments_length, HMathMinMax::kMathMin);
|
| + }
|
| +
|
| + // Allocate correctly sized array
|
| + HValue* result_length = AddUncasted<HSub>(upper_bound, lower_bound);
|
| + result_length->ClearFlag(HValue::kCanOverflow);
|
| + result_length = AddUncasted<HMathMinMax>(
|
| + result_length, graph()->GetConstant0(), HMathMinMax::kMathMax);
|
| +
|
| + ElementsKind elements_kind = FAST_HOLEY_ELEMENTS;
|
| + Handle<JSFunction> array_function(
|
| + isolate()->native_context()->array_function());
|
| + JSArrayBuilder array_builder(this,
|
| + elements_kind,
|
| + AddUncasted<HConstant>(array_function));
|
| + HValue* result =
|
| + BuildAllocateArrayFromLength(&array_builder, result_length);
|
| + HValue* result_elements = array_builder.elements_location();
|
| +
|
| + // Copy arguments to the array
|
| + LoopBuilder loop(this, context(), LoopBuilder::kPostIncrement);
|
| +
|
| + HValue* index =
|
| + loop.BeginBody(graph()->GetConstant0(), result_length, Token::LT);
|
| + {
|
| + HValue* arguments_index = AddUncasted<HAdd>(lower_bound, index);
|
| + arguments_index->ClearFlag(HValue::kCanOverflow);
|
| + HValue* value = AddUncasted<HAccessArgumentsAt>(
|
| + arguments_elements, arguments_length, arguments_index);
|
| + Add<HStoreKeyed>(result_elements, index, value, elements_kind);
|
| + }
|
| + loop.EndBody();
|
| + ast_context()->ReturnValue(result);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +
|
| +bool HOptimizedGraphBuilder::TryCallApply(Call* expr) {
|
| + if (current_info()->scope()->arguments() == NULL) return false;
|
| +
|
| + ZoneList<Expression*>* args = expr->arguments();
|
| + if (args->length() != 2) return false;
|
| + if (!IsLiveArguments(args->at(1))) return false;
|
|
|
| // Found pattern f.apply(receiver, arguments).
|
| CHECK_ALIVE_OR_RETURN(VisitForValue(args->at(0)), true);
|
| @@ -8671,7 +8819,7 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
|
| HConstant::cast(function)->handle(isolate()));
|
| expr->set_target(known_function);
|
|
|
| - if (TryCallApply(expr)) return;
|
| + if (TryIndirectCall(expr)) return;
|
| CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
|
|
| Handle<Map> map = types->length() == 1 ? types->first() : Handle<Map>();
|
| @@ -9866,6 +10014,14 @@ HValue* HGraphBuilder::TruncateToNumber(HValue* value, Type** expected) {
|
| }
|
|
|
|
|
| +bool HOptimizedGraphBuilder::IsLiveArguments(Expression* expr) {
|
| + VariableProxy* proxy = expr->AsVariableProxy();
|
| + if (proxy == NULL || !proxy->var()->IsStackAllocated()) return false;
|
| + HValue* value = LookupAndMakeLive(proxy->var());
|
| + return value->CheckFlag(HValue::kIsArguments);
|
| +}
|
| +
|
| +
|
| HValue* HOptimizedGraphBuilder::BuildBinaryOperation(
|
| BinaryOperation* expr,
|
| HValue* left,
|
|
|