Home | History | Annotate | Download | only in compiler
      1 // Copyright 2015 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/compiler/js-call-reducer.h"
      6 
      7 #include "src/compiler/js-graph.h"
      8 #include "src/compiler/node-matchers.h"
      9 #include "src/objects-inl.h"
     10 #include "src/type-feedback-vector-inl.h"
     11 
     12 namespace v8 {
     13 namespace internal {
     14 namespace compiler {
     15 
     16 namespace {
     17 
     18 VectorSlotPair CallCountFeedback(VectorSlotPair p) {
     19   // Extract call count from {p}.
     20   if (!p.IsValid()) return VectorSlotPair();
     21   CallICNexus n(p.vector(), p.slot());
     22   int const call_count = n.ExtractCallCount();
     23   if (call_count <= 0) return VectorSlotPair();
     24 
     25   // Create megamorphic CallIC feedback with the given {call_count}.
     26   StaticFeedbackVectorSpec spec;
     27   FeedbackVectorSlot slot = spec.AddCallICSlot();
     28   Handle<TypeFeedbackMetadata> metadata =
     29       TypeFeedbackMetadata::New(n.GetIsolate(), &spec);
     30   Handle<TypeFeedbackVector> vector =
     31       TypeFeedbackVector::New(n.GetIsolate(), metadata);
     32   CallICNexus nexus(vector, slot);
     33   nexus.ConfigureMegamorphic(call_count);
     34   return VectorSlotPair(vector, slot);
     35 }
     36 
     37 }  // namespace
     38 
     39 
     40 Reduction JSCallReducer::Reduce(Node* node) {
     41   switch (node->opcode()) {
     42     case IrOpcode::kJSCallConstruct:
     43       return ReduceJSCallConstruct(node);
     44     case IrOpcode::kJSCallFunction:
     45       return ReduceJSCallFunction(node);
     46     default:
     47       break;
     48   }
     49   return NoChange();
     50 }
     51 
     52 
     53 // ES6 section 22.1.1 The Array Constructor
     54 Reduction JSCallReducer::ReduceArrayConstructor(Node* node) {
     55   DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
     56   Node* target = NodeProperties::GetValueInput(node, 0);
     57   CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
     58 
     59   // Check if we have an allocation site from the CallIC.
     60   Handle<AllocationSite> site;
     61   if (p.feedback().IsValid()) {
     62     CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
     63     Handle<Object> feedback(nexus.GetFeedback(), isolate());
     64     if (feedback->IsAllocationSite()) {
     65       site = Handle<AllocationSite>::cast(feedback);
     66     }
     67   }
     68 
     69   // Turn the {node} into a {JSCreateArray} call.
     70   DCHECK_LE(2u, p.arity());
     71   size_t const arity = p.arity() - 2;
     72   NodeProperties::ReplaceValueInput(node, target, 0);
     73   NodeProperties::ReplaceValueInput(node, target, 1);
     74   NodeProperties::RemoveFrameStateInput(node, 1);
     75   // TODO(bmeurer): We might need to propagate the tail call mode to
     76   // the JSCreateArray operator, because an Array call in tail call
     77   // position must always properly consume the parent stack frame.
     78   NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
     79   return Changed(node);
     80 }
     81 
     82 
     83 // ES6 section 20.1.1 The Number Constructor
     84 Reduction JSCallReducer::ReduceNumberConstructor(Node* node) {
     85   DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
     86   CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
     87 
     88   // Turn the {node} into a {JSToNumber} call.
     89   DCHECK_LE(2u, p.arity());
     90   Node* value = (p.arity() == 2) ? jsgraph()->ZeroConstant()
     91                                  : NodeProperties::GetValueInput(node, 2);
     92   NodeProperties::RemoveFrameStateInput(node, 1);
     93   NodeProperties::ReplaceValueInputs(node, value);
     94   NodeProperties::ChangeOp(node, javascript()->ToNumber());
     95   return Changed(node);
     96 }
     97 
     98 
     99 // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray )
    100 Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
    101   DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
    102   Node* target = NodeProperties::GetValueInput(node, 0);
    103   CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
    104   Handle<JSFunction> apply =
    105       Handle<JSFunction>::cast(HeapObjectMatcher(target).Value());
    106   size_t arity = p.arity();
    107   DCHECK_LE(2u, arity);
    108   ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny;
    109   if (arity == 2) {
    110     // Neither thisArg nor argArray was provided.
    111     convert_mode = ConvertReceiverMode::kNullOrUndefined;
    112     node->ReplaceInput(0, node->InputAt(1));
    113     node->ReplaceInput(1, jsgraph()->UndefinedConstant());
    114   } else if (arity == 3) {
    115     // The argArray was not provided, just remove the {target}.
    116     node->RemoveInput(0);
    117     --arity;
    118   } else if (arity == 4) {
    119     // Check if argArray is an arguments object, and {node} is the only value
    120     // user of argArray (except for value uses in frame states).
    121     Node* arg_array = NodeProperties::GetValueInput(node, 3);
    122     if (arg_array->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
    123     for (Edge edge : arg_array->use_edges()) {
    124       if (edge.from()->opcode() == IrOpcode::kStateValues) continue;
    125       if (!NodeProperties::IsValueEdge(edge)) continue;
    126       if (edge.from() == node) continue;
    127       return NoChange();
    128     }
    129     // Get to the actual frame state from which to extract the arguments;
    130     // we can only optimize this in case the {node} was already inlined into
    131     // some other function (and same for the {arg_array}).
    132     CreateArgumentsParameters const& p =
    133         CreateArgumentsParametersOf(arg_array->op());
    134     Node* frame_state = NodeProperties::GetFrameStateInput(arg_array, 0);
    135     Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
    136     if (outer_state->opcode() != IrOpcode::kFrameState) return NoChange();
    137     FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state);
    138     if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
    139       // Need to take the parameters from the arguments adaptor.
    140       frame_state = outer_state;
    141     }
    142     FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
    143     if (p.type() == CreateArgumentsParameters::kMappedArguments) {
    144       // Mapped arguments (sloppy mode) cannot be handled if they are aliased.
    145       Handle<SharedFunctionInfo> shared;
    146       if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
    147       if (shared->internal_formal_parameter_count() != 0) return NoChange();
    148     }
    149     // Remove the argArray input from the {node}.
    150     node->RemoveInput(static_cast<int>(--arity));
    151     // Add the actual parameters to the {node}, skipping the receiver.
    152     Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
    153     for (int i = p.start_index() + 1; i < state_info.parameter_count(); ++i) {
    154       node->InsertInput(graph()->zone(), static_cast<int>(arity),
    155                         parameters->InputAt(i));
    156       ++arity;
    157     }
    158     // Drop the {target} from the {node}.
    159     node->RemoveInput(0);
    160     --arity;
    161   } else {
    162     return NoChange();
    163   }
    164   // Change {node} to the new {JSCallFunction} operator.
    165   NodeProperties::ChangeOp(
    166       node, javascript()->CallFunction(arity, p.language_mode(),
    167                                        CallCountFeedback(p.feedback()),
    168                                        convert_mode, p.tail_call_mode()));
    169   // Change context of {node} to the Function.prototype.apply context,
    170   // to ensure any exception is thrown in the correct context.
    171   NodeProperties::ReplaceContextInput(
    172       node, jsgraph()->HeapConstant(handle(apply->context(), isolate())));
    173   // Try to further reduce the JSCallFunction {node}.
    174   Reduction const reduction = ReduceJSCallFunction(node);
    175   return reduction.Changed() ? reduction : Changed(node);
    176 }
    177 
    178 
    179 // ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args)
    180 Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) {
    181   DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
    182   CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
    183   Handle<JSFunction> call = Handle<JSFunction>::cast(
    184       HeapObjectMatcher(NodeProperties::GetValueInput(node, 0)).Value());
    185   // Change context of {node} to the Function.prototype.call context,
    186   // to ensure any exception is thrown in the correct context.
    187   NodeProperties::ReplaceContextInput(
    188       node, jsgraph()->HeapConstant(handle(call->context(), isolate())));
    189   // Remove the target from {node} and use the receiver as target instead, and
    190   // the thisArg becomes the new target.  If thisArg was not provided, insert
    191   // undefined instead.
    192   size_t arity = p.arity();
    193   DCHECK_LE(2u, arity);
    194   ConvertReceiverMode convert_mode;
    195   if (arity == 2) {
    196     // The thisArg was not provided, use undefined as receiver.
    197     convert_mode = ConvertReceiverMode::kNullOrUndefined;
    198     node->ReplaceInput(0, node->InputAt(1));
    199     node->ReplaceInput(1, jsgraph()->UndefinedConstant());
    200   } else {
    201     // Just remove the target, which is the first value input.
    202     convert_mode = ConvertReceiverMode::kAny;
    203     node->RemoveInput(0);
    204     --arity;
    205   }
    206   NodeProperties::ChangeOp(
    207       node, javascript()->CallFunction(arity, p.language_mode(),
    208                                        CallCountFeedback(p.feedback()),
    209                                        convert_mode, p.tail_call_mode()));
    210   // Try to further reduce the JSCallFunction {node}.
    211   Reduction const reduction = ReduceJSCallFunction(node);
    212   return reduction.Changed() ? reduction : Changed(node);
    213 }
    214 
    215 
    216 Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
    217   DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
    218   CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
    219   Node* target = NodeProperties::GetValueInput(node, 0);
    220   Node* context = NodeProperties::GetContextInput(node);
    221   Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
    222   Node* control = NodeProperties::GetControlInput(node);
    223   Node* effect = NodeProperties::GetEffectInput(node);
    224 
    225   // Try to specialize JSCallFunction {node}s with constant {target}s.
    226   HeapObjectMatcher m(target);
    227   if (m.HasValue()) {
    228     if (m.Value()->IsJSFunction()) {
    229       Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
    230       Handle<SharedFunctionInfo> shared(function->shared(), isolate());
    231 
    232       // Raise a TypeError if the {target} is a "classConstructor".
    233       if (IsClassConstructor(shared->kind())) {
    234         NodeProperties::RemoveFrameStateInput(node, 0);
    235         NodeProperties::ReplaceValueInputs(node, target);
    236         NodeProperties::ChangeOp(
    237             node, javascript()->CallRuntime(
    238                       Runtime::kThrowConstructorNonCallableError, 1));
    239         return Changed(node);
    240       }
    241 
    242       // Check for known builtin functions.
    243       if (shared->HasBuiltinFunctionId()) {
    244         switch (shared->builtin_function_id()) {
    245           case kFunctionApply:
    246             return ReduceFunctionPrototypeApply(node);
    247           case kFunctionCall:
    248             return ReduceFunctionPrototypeCall(node);
    249           default:
    250             break;
    251         }
    252       }
    253 
    254       // Check for the Array constructor.
    255       if (*function == function->native_context()->array_function()) {
    256         return ReduceArrayConstructor(node);
    257       }
    258 
    259       // Check for the Number constructor.
    260       if (*function == function->native_context()->number_function()) {
    261         return ReduceNumberConstructor(node);
    262       }
    263     } else if (m.Value()->IsJSBoundFunction()) {
    264       Handle<JSBoundFunction> function =
    265           Handle<JSBoundFunction>::cast(m.Value());
    266       Handle<JSReceiver> bound_target_function(
    267           function->bound_target_function(), isolate());
    268       Handle<Object> bound_this(function->bound_this(), isolate());
    269       Handle<FixedArray> bound_arguments(function->bound_arguments(),
    270                                          isolate());
    271       CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
    272       ConvertReceiverMode const convert_mode =
    273           (bound_this->IsNull() || bound_this->IsUndefined())
    274               ? ConvertReceiverMode::kNullOrUndefined
    275               : ConvertReceiverMode::kNotNullOrUndefined;
    276       size_t arity = p.arity();
    277       DCHECK_LE(2u, arity);
    278       // Patch {node} to use [[BoundTargetFunction]] and [[BoundThis]].
    279       NodeProperties::ReplaceValueInput(
    280           node, jsgraph()->Constant(bound_target_function), 0);
    281       NodeProperties::ReplaceValueInput(node, jsgraph()->Constant(bound_this),
    282                                         1);
    283       // Insert the [[BoundArguments]] for {node}.
    284       for (int i = 0; i < bound_arguments->length(); ++i) {
    285         node->InsertInput(
    286             graph()->zone(), i + 2,
    287             jsgraph()->Constant(handle(bound_arguments->get(i), isolate())));
    288         arity++;
    289       }
    290       NodeProperties::ChangeOp(
    291           node, javascript()->CallFunction(arity, p.language_mode(),
    292                                            CallCountFeedback(p.feedback()),
    293                                            convert_mode, p.tail_call_mode()));
    294       // Try to further reduce the JSCallFunction {node}.
    295       Reduction const reduction = ReduceJSCallFunction(node);
    296       return reduction.Changed() ? reduction : Changed(node);
    297     }
    298 
    299     // Don't mess with other {node}s that have a constant {target}.
    300     // TODO(bmeurer): Also support proxies here.
    301     return NoChange();
    302   }
    303 
    304   // Not much we can do if deoptimization support is disabled.
    305   if (!(flags() & kDeoptimizationEnabled)) return NoChange();
    306 
    307   // Extract feedback from the {node} using the CallICNexus.
    308   if (!p.feedback().IsValid()) return NoChange();
    309   CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
    310   Handle<Object> feedback(nexus.GetFeedback(), isolate());
    311   if (feedback->IsAllocationSite()) {
    312     // Retrieve the Array function from the {node}.
    313     Node* array_function;
    314     Handle<Context> native_context;
    315     if (GetNativeContext(node).ToHandle(&native_context)) {
    316       array_function = jsgraph()->HeapConstant(
    317           handle(native_context->array_function(), isolate()));
    318     } else {
    319       Node* native_context = effect = graph()->NewNode(
    320           javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
    321           context, context, effect);
    322       array_function = effect = graph()->NewNode(
    323           javascript()->LoadContext(0, Context::ARRAY_FUNCTION_INDEX, true),
    324           native_context, native_context, effect);
    325     }
    326 
    327     // Check that the {target} is still the {array_function}.
    328     Node* check = effect =
    329         graph()->NewNode(javascript()->StrictEqual(), target, array_function,
    330                          context, effect, control);
    331     Node* branch =
    332         graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
    333     Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
    334     Node* deoptimize =
    335         graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
    336                          frame_state, effect, if_false);
    337     // TODO(bmeurer): This should be on the AdvancedReducer somehow.
    338     NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
    339     control = graph()->NewNode(common()->IfTrue(), branch);
    340 
    341     // Turn the {node} into a {JSCreateArray} call.
    342     NodeProperties::ReplaceValueInput(node, array_function, 0);
    343     NodeProperties::ReplaceEffectInput(node, effect);
    344     NodeProperties::ReplaceControlInput(node, control);
    345     return ReduceArrayConstructor(node);
    346   } else if (feedback->IsWeakCell()) {
    347     Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback);
    348     if (cell->value()->IsJSFunction()) {
    349       Node* target_function =
    350           jsgraph()->Constant(handle(cell->value(), isolate()));
    351 
    352       // Check that the {target} is still the {target_function}.
    353       Node* check = effect =
    354           graph()->NewNode(javascript()->StrictEqual(), target, target_function,
    355                            context, effect, control);
    356       Node* branch =
    357           graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
    358       Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
    359       Node* deoptimize =
    360           graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
    361                            frame_state, effect, if_false);
    362       // TODO(bmeurer): This should be on the AdvancedReducer somehow.
    363       NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
    364       control = graph()->NewNode(common()->IfTrue(), branch);
    365 
    366       // Specialize the JSCallFunction node to the {target_function}.
    367       NodeProperties::ReplaceValueInput(node, target_function, 0);
    368       NodeProperties::ReplaceEffectInput(node, effect);
    369       NodeProperties::ReplaceControlInput(node, control);
    370 
    371       // Try to further reduce the JSCallFunction {node}.
    372       Reduction const reduction = ReduceJSCallFunction(node);
    373       return reduction.Changed() ? reduction : Changed(node);
    374     }
    375   }
    376   return NoChange();
    377 }
    378 
    379 
    380 Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
    381   DCHECK_EQ(IrOpcode::kJSCallConstruct, node->opcode());
    382   CallConstructParameters const& p = CallConstructParametersOf(node->op());
    383   DCHECK_LE(2u, p.arity());
    384   int const arity = static_cast<int>(p.arity() - 2);
    385   Node* target = NodeProperties::GetValueInput(node, 0);
    386   Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
    387   Node* context = NodeProperties::GetContextInput(node);
    388   Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
    389   Node* effect = NodeProperties::GetEffectInput(node);
    390   Node* control = NodeProperties::GetControlInput(node);
    391 
    392   // Try to specialize JSCallConstruct {node}s with constant {target}s.
    393   HeapObjectMatcher m(target);
    394   if (m.HasValue()) {
    395     if (m.Value()->IsJSFunction()) {
    396       Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
    397 
    398       // Raise a TypeError if the {target} is not a constructor.
    399       if (!function->IsConstructor()) {
    400         // Drop the lazy bailout location and use the eager bailout point for
    401         // the runtime function (actually as lazy bailout point). It doesn't
    402         // really matter which bailout location we use since we never really
    403         // go back after throwing the exception.
    404         NodeProperties::RemoveFrameStateInput(node, 0);
    405         NodeProperties::ReplaceValueInputs(node, target);
    406         NodeProperties::ChangeOp(
    407             node,
    408             javascript()->CallRuntime(Runtime::kThrowCalledNonCallable, 1));
    409         return Changed(node);
    410       }
    411 
    412       // Check for the ArrayConstructor.
    413       if (*function == function->native_context()->array_function()) {
    414         // Check if we have an allocation site.
    415         Handle<AllocationSite> site;
    416         if (p.feedback().IsValid()) {
    417           Handle<Object> feedback(
    418               p.feedback().vector()->Get(p.feedback().slot()), isolate());
    419           if (feedback->IsAllocationSite()) {
    420             site = Handle<AllocationSite>::cast(feedback);
    421           }
    422         }
    423 
    424         // Turn the {node} into a {JSCreateArray} call.
    425         NodeProperties::RemoveFrameStateInput(node, 1);
    426         for (int i = arity; i > 0; --i) {
    427           NodeProperties::ReplaceValueInput(
    428               node, NodeProperties::GetValueInput(node, i), i + 1);
    429         }
    430         NodeProperties::ReplaceValueInput(node, new_target, 1);
    431         NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
    432         return Changed(node);
    433       }
    434     }
    435 
    436     // Don't mess with other {node}s that have a constant {target}.
    437     // TODO(bmeurer): Also support optimizing bound functions and proxies here.
    438     return NoChange();
    439   }
    440 
    441   // Not much we can do if deoptimization support is disabled.
    442   if (!(flags() & kDeoptimizationEnabled)) return NoChange();
    443 
    444   // TODO(mvstanton): Use ConstructICNexus here, once available.
    445   Handle<Object> feedback;
    446   if (!p.feedback().IsValid()) return NoChange();
    447   feedback = handle(p.feedback().vector()->Get(p.feedback().slot()), isolate());
    448   if (feedback->IsAllocationSite()) {
    449     // The feedback is an AllocationSite, which means we have called the
    450     // Array function and collected transition (and pretenuring) feedback
    451     // for the resulting arrays.  This has to be kept in sync with the
    452     // implementation of the CallConstructStub.
    453     Handle<AllocationSite> site = Handle<AllocationSite>::cast(feedback);
    454 
    455     // Retrieve the Array function from the {node}.
    456     Node* array_function;
    457     Handle<Context> native_context;
    458     if (GetNativeContext(node).ToHandle(&native_context)) {
    459       array_function = jsgraph()->HeapConstant(
    460           handle(native_context->array_function(), isolate()));
    461     } else {
    462       Node* native_context = effect = graph()->NewNode(
    463           javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true),
    464           context, context, effect);
    465       array_function = effect = graph()->NewNode(
    466           javascript()->LoadContext(0, Context::ARRAY_FUNCTION_INDEX, true),
    467           native_context, native_context, effect);
    468     }
    469 
    470     // Check that the {target} is still the {array_function}.
    471     Node* check = effect =
    472         graph()->NewNode(javascript()->StrictEqual(), target, array_function,
    473                          context, effect, control);
    474     Node* branch =
    475         graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
    476     Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
    477     Node* deoptimize =
    478         graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
    479                          frame_state, effect, if_false);
    480     // TODO(bmeurer): This should be on the AdvancedReducer somehow.
    481     NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
    482     control = graph()->NewNode(common()->IfTrue(), branch);
    483 
    484     // Turn the {node} into a {JSCreateArray} call.
    485     NodeProperties::ReplaceEffectInput(node, effect);
    486     NodeProperties::ReplaceControlInput(node, control);
    487     NodeProperties::RemoveFrameStateInput(node, 1);
    488     for (int i = arity; i > 0; --i) {
    489       NodeProperties::ReplaceValueInput(
    490           node, NodeProperties::GetValueInput(node, i), i + 1);
    491     }
    492     NodeProperties::ReplaceValueInput(node, new_target, 1);
    493     NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
    494     return Changed(node);
    495   } else if (feedback->IsWeakCell()) {
    496     Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback);
    497     if (cell->value()->IsJSFunction()) {
    498       Node* target_function =
    499           jsgraph()->Constant(handle(cell->value(), isolate()));
    500 
    501       // Check that the {target} is still the {target_function}.
    502       Node* check = effect =
    503           graph()->NewNode(javascript()->StrictEqual(), target, target_function,
    504                            context, effect, control);
    505       Node* branch =
    506           graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
    507       Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
    508       Node* deoptimize =
    509           graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
    510                            frame_state, effect, if_false);
    511       // TODO(bmeurer): This should be on the AdvancedReducer somehow.
    512       NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
    513       control = graph()->NewNode(common()->IfTrue(), branch);
    514 
    515       // Specialize the JSCallConstruct node to the {target_function}.
    516       NodeProperties::ReplaceValueInput(node, target_function, 0);
    517       NodeProperties::ReplaceEffectInput(node, effect);
    518       NodeProperties::ReplaceControlInput(node, control);
    519       if (target == new_target) {
    520         NodeProperties::ReplaceValueInput(node, target_function, arity + 1);
    521       }
    522 
    523       // Try to further reduce the JSCallConstruct {node}.
    524       Reduction const reduction = ReduceJSCallConstruct(node);
    525       return reduction.Changed() ? reduction : Changed(node);
    526     }
    527   }
    528 
    529   return NoChange();
    530 }
    531 
    532 
    533 MaybeHandle<Context> JSCallReducer::GetNativeContext(Node* node) {
    534   Node* const context = NodeProperties::GetContextInput(node);
    535   return NodeProperties::GetSpecializationNativeContext(context,
    536                                                         native_context());
    537 }
    538 
    539 
    540 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
    541 
    542 
    543 Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
    544 
    545 
    546 CommonOperatorBuilder* JSCallReducer::common() const {
    547   return jsgraph()->common();
    548 }
    549 
    550 
    551 JSOperatorBuilder* JSCallReducer::javascript() const {
    552   return jsgraph()->javascript();
    553 }
    554 
    555 }  // namespace compiler
    556 }  // namespace internal
    557 }  // namespace v8
    558