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, |