| Index: src/compiler/instruction-selector.cc
 | 
| diff --git a/src/compiler/instruction-selector.cc b/src/compiler/instruction-selector.cc
 | 
| index 3fc209e83422a1c91609a97164c0e4a62a19b332..282c9c5dca7e8f9a115110175db46d58069542d8 100644
 | 
| --- a/src/compiler/instruction-selector.cc
 | 
| +++ b/src/compiler/instruction-selector.cc
 | 
| @@ -307,6 +307,9 @@ InstructionOperand OperandForDeopt(OperandGenerator* g, Node* input,
 | 
|      case IrOpcode::kFloat64Constant:
 | 
|      case IrOpcode::kHeapConstant:
 | 
|        return g->UseImmediate(input);
 | 
| +    case IrOpcode::kObjectState:
 | 
| +      UNREACHABLE();
 | 
| +      break;
 | 
|      default:
 | 
|        switch (kind) {
 | 
|          case FrameStateInputKind::kStackSlot:
 | 
| @@ -314,21 +317,94 @@ InstructionOperand OperandForDeopt(OperandGenerator* g, Node* input,
 | 
|          case FrameStateInputKind::kAny:
 | 
|            return g->UseAny(input);
 | 
|        }
 | 
| -      UNREACHABLE();
 | 
| -      return InstructionOperand();
 | 
| +  }
 | 
| +  UNREACHABLE();
 | 
| +  return InstructionOperand();
 | 
| +}
 | 
| +
 | 
| +
 | 
| +class StateObjectDeduplicator {
 | 
| + public:
 | 
| +  explicit StateObjectDeduplicator(Zone* zone) : objects_(zone) {}
 | 
| +  static const size_t kNotDuplicated = SIZE_MAX;
 | 
| +
 | 
| +  size_t GetObjectId(Node* node) {
 | 
| +    for (size_t i = 0; i < objects_.size(); ++i) {
 | 
| +      if (objects_[i] == node) {
 | 
| +        return i;
 | 
| +      }
 | 
| +    }
 | 
| +    return kNotDuplicated;
 | 
| +  }
 | 
| +
 | 
| +  size_t InsertObject(Node* node) {
 | 
| +    size_t id = objects_.size();
 | 
| +    objects_.push_back(node);
 | 
| +    return id;
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  ZoneVector<Node*> objects_;
 | 
| +};
 | 
| +
 | 
| +
 | 
| +// Returns the number of instruction operands added to inputs.
 | 
| +size_t AddOperandToStateValueDescriptor(StateValueDescriptor* descriptor,
 | 
| +                                        InstructionOperandVector* inputs,
 | 
| +                                        OperandGenerator* g,
 | 
| +                                        StateObjectDeduplicator* deduplicator,
 | 
| +                                        Node* input, MachineType type,
 | 
| +                                        FrameStateInputKind kind, Zone* zone) {
 | 
| +  switch (input->opcode()) {
 | 
| +    case IrOpcode::kObjectState: {
 | 
| +      size_t id = deduplicator->GetObjectId(input);
 | 
| +      if (id == StateObjectDeduplicator::kNotDuplicated) {
 | 
| +        size_t entries = 0;
 | 
| +        id = deduplicator->InsertObject(input);
 | 
| +        descriptor->fields().push_back(
 | 
| +            StateValueDescriptor::Recursive(zone, id));
 | 
| +        StateValueDescriptor* new_desc = &descriptor->fields().back();
 | 
| +        for (Edge edge : input->input_edges()) {
 | 
| +          entries += AddOperandToStateValueDescriptor(
 | 
| +              new_desc, inputs, g, deduplicator, edge.to(),
 | 
| +              MachineType::AnyTagged(), kind, zone);
 | 
| +        }
 | 
| +        return entries;
 | 
| +      } else {
 | 
| +        // Crankshaft counts duplicate objects for the running id, so we have
 | 
| +        // to push the input again.
 | 
| +        deduplicator->InsertObject(input);
 | 
| +        descriptor->fields().push_back(
 | 
| +            StateValueDescriptor::Duplicate(zone, id));
 | 
| +        return 0;
 | 
| +      }
 | 
| +      break;
 | 
| +    }
 | 
| +    default: {
 | 
| +      inputs->push_back(OperandForDeopt(g, input, kind));
 | 
| +      descriptor->fields().push_back(StateValueDescriptor::Plain(zone, type));
 | 
| +      return 1;
 | 
| +    }
 | 
|    }
 | 
|  }
 | 
|  
 | 
|  
 | 
| -void AddFrameStateInputs(Node* state, OperandGenerator* g,
 | 
| -                         InstructionOperandVector* inputs,
 | 
| -                         FrameStateDescriptor* descriptor,
 | 
| -                         FrameStateInputKind kind, Zone* zone) {
 | 
| +// Returns the number of instruction operands added to inputs.
 | 
| +size_t AddInputsToFrameStateDescriptor(FrameStateDescriptor* descriptor,
 | 
| +                                       Node* state, OperandGenerator* g,
 | 
| +                                       StateObjectDeduplicator* deduplicator,
 | 
| +                                       InstructionOperandVector* inputs,
 | 
| +                                       FrameStateInputKind kind, Zone* zone) {
 | 
|    DCHECK_EQ(IrOpcode::kFrameState, state->op()->opcode());
 | 
|  
 | 
| +  size_t entries = 0;
 | 
| +  size_t initial_size = inputs->size();
 | 
| +  USE(initial_size);  // initial_size is only used for debug.
 | 
| +
 | 
|    if (descriptor->outer_state()) {
 | 
| -    AddFrameStateInputs(state->InputAt(kFrameStateOuterStateInput), g, inputs,
 | 
| -                        descriptor->outer_state(), kind, zone);
 | 
| +    entries += AddInputsToFrameStateDescriptor(
 | 
| +        descriptor->outer_state(), state->InputAt(kFrameStateOuterStateInput),
 | 
| +        g, deduplicator, inputs, kind, zone);
 | 
|    }
 | 
|  
 | 
|    Node* parameters = state->InputAt(kFrameStateParametersInput);
 | 
| @@ -342,32 +418,34 @@ void AddFrameStateInputs(Node* state, OperandGenerator* g,
 | 
|    DCHECK_EQ(descriptor->locals_count(), StateValuesAccess(locals).size());
 | 
|    DCHECK_EQ(descriptor->stack_count(), StateValuesAccess(stack).size());
 | 
|  
 | 
| -  ZoneVector<MachineType> types(zone);
 | 
| -  types.reserve(descriptor->GetSize());
 | 
| -
 | 
| -  size_t value_index = 0;
 | 
| -  inputs->push_back(
 | 
| -      OperandForDeopt(g, function, FrameStateInputKind::kStackSlot));
 | 
| -  descriptor->SetType(value_index++, MachineType::AnyTagged());
 | 
| +  StateValueDescriptor* values_descriptor =
 | 
| +      descriptor->GetStateValueDescriptor();
 | 
| +  entries += AddOperandToStateValueDescriptor(
 | 
| +      values_descriptor, inputs, g, deduplicator, function,
 | 
| +      MachineType::AnyTagged(), FrameStateInputKind::kStackSlot, zone);
 | 
|    for (StateValuesAccess::TypedNode input_node :
 | 
|         StateValuesAccess(parameters)) {
 | 
| -    inputs->push_back(OperandForDeopt(g, input_node.node, kind));
 | 
| -    descriptor->SetType(value_index++, input_node.type);
 | 
| +    entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g,
 | 
| +                                                deduplicator, input_node.node,
 | 
| +                                                input_node.type, kind, zone);
 | 
|    }
 | 
|    if (descriptor->HasContext()) {
 | 
| -    inputs->push_back(
 | 
| -        OperandForDeopt(g, context, FrameStateInputKind::kStackSlot));
 | 
| -    descriptor->SetType(value_index++, MachineType::AnyTagged());
 | 
| +    entries += AddOperandToStateValueDescriptor(
 | 
| +        values_descriptor, inputs, g, deduplicator, context,
 | 
| +        MachineType::AnyTagged(), FrameStateInputKind::kStackSlot, zone);
 | 
|    }
 | 
|    for (StateValuesAccess::TypedNode input_node : StateValuesAccess(locals)) {
 | 
| -    inputs->push_back(OperandForDeopt(g, input_node.node, kind));
 | 
| -    descriptor->SetType(value_index++, input_node.type);
 | 
| +    entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g,
 | 
| +                                                deduplicator, input_node.node,
 | 
| +                                                input_node.type, kind, zone);
 | 
|    }
 | 
|    for (StateValuesAccess::TypedNode input_node : StateValuesAccess(stack)) {
 | 
| -    inputs->push_back(OperandForDeopt(g, input_node.node, kind));
 | 
| -    descriptor->SetType(value_index++, input_node.type);
 | 
| +    entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g,
 | 
| +                                                deduplicator, input_node.node,
 | 
| +                                                input_node.type, kind, zone);
 | 
|    }
 | 
| -  DCHECK(value_index == descriptor->GetSize());
 | 
| +  DCHECK_EQ(initial_size + entries, inputs->size());
 | 
| +  return entries;
 | 
|  }
 | 
|  
 | 
|  }  // namespace
 | 
| @@ -500,6 +578,8 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
 | 
|    // follows (n is the number of value inputs to the frame state):
 | 
|    // arg 1               : deoptimization id.
 | 
|    // arg 2 - arg (n + 1) : value inputs to the frame state.
 | 
| +  size_t frame_state_entries = 0;
 | 
| +  USE(frame_state_entries);  // frame_state_entries is only used for debug.
 | 
|    if (buffer->frame_state_descriptor != NULL) {
 | 
|      InstructionSequence::StateId state_id =
 | 
|          sequence()->AddFrameStateDescriptor(buffer->frame_state_descriptor);
 | 
| @@ -507,12 +587,17 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
 | 
|  
 | 
|      Node* frame_state =
 | 
|          call->InputAt(static_cast<int>(buffer->descriptor->InputCount()));
 | 
| -    AddFrameStateInputs(frame_state, &g, &buffer->instruction_args,
 | 
| -                        buffer->frame_state_descriptor,
 | 
| -                        FrameStateInputKind::kStackSlot, instruction_zone());
 | 
| +
 | 
| +    StateObjectDeduplicator deduplicator(instruction_zone());
 | 
| +
 | 
| +    frame_state_entries =
 | 
| +        1 + AddInputsToFrameStateDescriptor(
 | 
| +                buffer->frame_state_descriptor, frame_state, &g, &deduplicator,
 | 
| +                &buffer->instruction_args, FrameStateInputKind::kStackSlot,
 | 
| +                instruction_zone());
 | 
| +
 | 
| +    DCHECK_EQ(1 + frame_state_entries, buffer->instruction_args.size());
 | 
|    }
 | 
| -  DCHECK(1 + buffer->frame_state_value_count() ==
 | 
| -         buffer->instruction_args.size());
 | 
|  
 | 
|    size_t input_count = static_cast<size_t>(buffer->input_count());
 | 
|  
 | 
| @@ -549,7 +634,7 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
 | 
|      }
 | 
|    }
 | 
|    DCHECK_EQ(input_count, buffer->instruction_args.size() + pushed_count -
 | 
| -                             buffer->frame_state_value_count());
 | 
| +                             frame_state_entries);
 | 
|    if (V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK && call_tail &&
 | 
|        stack_param_delta != 0) {
 | 
|      // For tail calls that change the size of their parameter list and keep
 | 
| @@ -752,6 +837,7 @@ void InstructionSelector::VisitNode(Node* node) {
 | 
|        return VisitCall(node);
 | 
|      case IrOpcode::kFrameState:
 | 
|      case IrOpcode::kStateValues:
 | 
| +    case IrOpcode::kObjectState:
 | 
|        return;
 | 
|      case IrOpcode::kLoad: {
 | 
|        LoadRepresentation type = LoadRepresentationOf(node->op());
 | 
| @@ -1470,19 +1556,19 @@ void InstructionSelector::VisitDeoptimize(DeoptimizeKind kind, Node* value) {
 | 
|    OperandGenerator g(this);
 | 
|  
 | 
|    FrameStateDescriptor* desc = GetFrameStateDescriptor(value);
 | 
| -  size_t arg_count = desc->GetTotalSize() + 1;  // Include deopt id.
 | 
|  
 | 
|    InstructionOperandVector args(instruction_zone());
 | 
| -  args.reserve(arg_count);
 | 
| +  args.reserve(desc->GetTotalSize() + 1);  // Include deopt id.
 | 
|  
 | 
|    InstructionSequence::StateId state_id =
 | 
|        sequence()->AddFrameStateDescriptor(desc);
 | 
|    args.push_back(g.TempImmediate(state_id.ToInt()));
 | 
|  
 | 
| -  AddFrameStateInputs(value, &g, &args, desc, FrameStateInputKind::kAny,
 | 
| -                      instruction_zone());
 | 
| +  StateObjectDeduplicator deduplicator(instruction_zone());
 | 
|  
 | 
| -  DCHECK_EQ(args.size(), arg_count);
 | 
| +  AddInputsToFrameStateDescriptor(desc, value, &g, &deduplicator, &args,
 | 
| +                                  FrameStateInputKind::kAny,
 | 
| +                                  instruction_zone());
 | 
|  
 | 
|    InstructionCode opcode = kArchDeoptimize;
 | 
|    switch (kind) {
 | 
| @@ -1493,7 +1579,7 @@ void InstructionSelector::VisitDeoptimize(DeoptimizeKind kind, Node* value) {
 | 
|        opcode |= MiscField::encode(Deoptimizer::SOFT);
 | 
|        break;
 | 
|    }
 | 
| -  Emit(opcode, 0, nullptr, arg_count, &args.front(), 0, nullptr);
 | 
| +  Emit(opcode, 0, nullptr, args.size(), &args.front(), 0, nullptr);
 | 
|  }
 | 
|  
 | 
|  
 | 
| 
 |