xref: /third_party/node/deps/v8/src/compiler/node.cc (revision 1cb0ef41)
1// Copyright 2013 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/node.h"
6
7namespace v8 {
8namespace internal {
9namespace compiler {
10
11Node::OutOfLineInputs* Node::OutOfLineInputs::New(Zone* zone, int capacity) {
12  size_t size =
13      sizeof(OutOfLineInputs) + capacity * (sizeof(Node*) + sizeof(Use));
14  intptr_t raw_buffer =
15      reinterpret_cast<intptr_t>(zone->Allocate<Node::OutOfLineInputs>(size));
16  Node::OutOfLineInputs* outline =
17      reinterpret_cast<OutOfLineInputs*>(raw_buffer + capacity * sizeof(Use));
18  outline->capacity_ = capacity;
19  outline->count_ = 0;
20  return outline;
21}
22
23void Node::OutOfLineInputs::ExtractFrom(Use* old_use_ptr,
24                                        ZoneNodePtr* old_input_ptr, int count) {
25  DCHECK_GE(count, 0);
26  // Extract the inputs from the old use and input pointers and copy them
27  // to this out-of-line-storage.
28  Use* new_use_ptr = reinterpret_cast<Use*>(this) - 1;
29  ZoneNodePtr* new_input_ptr = inputs();
30  CHECK_IMPLIES(count > 0, Use::InputIndexField::is_valid(count - 1));
31  for (int current = 0; current < count; current++) {
32    new_use_ptr->bit_field_ =
33        Use::InputIndexField::encode(current) | Use::InlineField::encode(false);
34    DCHECK_EQ(old_input_ptr, old_use_ptr->input_ptr());
35    DCHECK_EQ(new_input_ptr, new_use_ptr->input_ptr());
36    Node* old_to = *old_input_ptr;
37    if (old_to) {
38      *old_input_ptr = nullptr;
39      old_to->RemoveUse(old_use_ptr);
40      *new_input_ptr = old_to;
41      old_to->AppendUse(new_use_ptr);
42    } else {
43      *new_input_ptr = nullptr;
44    }
45    old_input_ptr++;
46    new_input_ptr++;
47    old_use_ptr--;
48    new_use_ptr--;
49  }
50  this->count_ = count;
51}
52
53// These structs are just type tags for Zone::Allocate<T>(size_t) calls.
54struct NodeWithOutOfLineInputs {};
55struct NodeWithInLineInputs {};
56
57template <typename NodePtrT>
58Node* Node::NewImpl(Zone* zone, NodeId id, const Operator* op, int input_count,
59                    NodePtrT const* inputs, bool has_extensible_inputs) {
60  // Node uses compressed pointers, so zone must support pointer compression.
61  DCHECK_IMPLIES(kCompressGraphZone, zone->supports_compression());
62  DCHECK_GE(input_count, 0);
63
64  ZoneNodePtr* input_ptr;
65  Use* use_ptr;
66  Node* node;
67  bool is_inline;
68
69  // Verify that none of the inputs are {nullptr}.
70  for (int i = 0; i < input_count; i++) {
71    if (inputs[i] == nullptr) {
72      FATAL("Node::New() Error: #%d:%s[%d] is nullptr", static_cast<int>(id),
73            op->mnemonic(), i);
74    }
75  }
76
77  if (input_count > kMaxInlineCapacity) {
78    // Allocate out-of-line inputs.
79    int capacity =
80        has_extensible_inputs ? input_count + kMaxInlineCapacity : input_count;
81    OutOfLineInputs* outline = OutOfLineInputs::New(zone, capacity);
82
83    // Allocate node, with space for OutOfLineInputs pointer.
84    void* node_buffer = zone->Allocate<NodeWithOutOfLineInputs>(
85        sizeof(Node) + sizeof(ZoneOutOfLineInputsPtr));
86    node = new (node_buffer) Node(id, op, kOutlineMarker, 0);
87    node->set_outline_inputs(outline);
88
89    outline->node_ = node;
90    outline->count_ = input_count;
91
92    input_ptr = outline->inputs();
93    use_ptr = reinterpret_cast<Use*>(outline);
94    is_inline = false;
95  } else {
96    // Allocate node with inline inputs. Capacity must be at least 1 so that
97    // an OutOfLineInputs pointer can be stored when inputs are added later.
98    int capacity = std::max(1, input_count);
99    if (has_extensible_inputs) {
100      const int max = kMaxInlineCapacity;
101      capacity = std::min(input_count + 3, max);
102    }
103
104    size_t size = sizeof(Node) + capacity * (sizeof(ZoneNodePtr) + sizeof(Use));
105    intptr_t raw_buffer =
106        reinterpret_cast<intptr_t>(zone->Allocate<NodeWithInLineInputs>(size));
107    void* node_buffer =
108        reinterpret_cast<void*>(raw_buffer + capacity * sizeof(Use));
109
110    node = new (node_buffer) Node(id, op, input_count, capacity);
111    input_ptr = node->inline_inputs();
112    use_ptr = reinterpret_cast<Use*>(node);
113    is_inline = true;
114  }
115
116  // Initialize the input pointers and the uses.
117  CHECK_IMPLIES(input_count > 0,
118                Use::InputIndexField::is_valid(input_count - 1));
119  for (int current = 0; current < input_count; ++current) {
120    Node* to = *inputs++;
121    input_ptr[current] = to;
122    Use* use = use_ptr - 1 - current;
123    use->bit_field_ = Use::InputIndexField::encode(current) |
124                      Use::InlineField::encode(is_inline);
125    to->AppendUse(use);
126  }
127  node->Verify();
128  return node;
129}
130
131Node* Node::New(Zone* zone, NodeId id, const Operator* op, int input_count,
132                Node* const* inputs, bool has_extensible_inputs) {
133  return NewImpl(zone, id, op, input_count, inputs, has_extensible_inputs);
134}
135
136Node* Node::Clone(Zone* zone, NodeId id, const Node* node) {
137  int const input_count = node->InputCount();
138  ZoneNodePtr const* const inputs = node->has_inline_inputs()
139                                        ? node->inline_inputs()
140                                        : node->outline_inputs()->inputs();
141  Node* const clone = NewImpl(zone, id, node->op(), input_count, inputs, false);
142  clone->set_type(node->type());
143  return clone;
144}
145
146
147void Node::Kill() {
148  DCHECK_NOT_NULL(op());
149  NullAllInputs();
150  DCHECK(uses().empty());
151}
152
153
154void Node::AppendInput(Zone* zone, Node* new_to) {
155  DCHECK_NOT_NULL(zone);
156  DCHECK_NOT_NULL(new_to);
157
158  int const inline_count = InlineCountField::decode(bit_field_);
159  int const inline_capacity = InlineCapacityField::decode(bit_field_);
160  if (inline_count < inline_capacity) {
161    // Append inline input.
162    bit_field_ = InlineCountField::update(bit_field_, inline_count + 1);
163    *GetInputPtr(inline_count) = new_to;
164    Use* use = GetUsePtr(inline_count);
165    STATIC_ASSERT(InlineCapacityField::kMax <= Use::InputIndexField::kMax);
166    use->bit_field_ = Use::InputIndexField::encode(inline_count) |
167                      Use::InlineField::encode(true);
168    new_to->AppendUse(use);
169  } else {
170    // Append out-of-line input.
171    int const input_count = InputCount();
172    OutOfLineInputs* outline = nullptr;
173    if (inline_count != kOutlineMarker) {
174      // switch to out of line inputs.
175      outline = OutOfLineInputs::New(zone, input_count * 2 + 3);
176      outline->node_ = this;
177      outline->ExtractFrom(GetUsePtr(0), GetInputPtr(0), input_count);
178      bit_field_ = InlineCountField::update(bit_field_, kOutlineMarker);
179      set_outline_inputs(outline);
180    } else {
181      // use current out of line inputs.
182      outline = outline_inputs();
183      if (input_count >= outline->capacity_) {
184        // out of space in out-of-line inputs.
185        outline = OutOfLineInputs::New(zone, input_count * 2 + 3);
186        outline->node_ = this;
187        outline->ExtractFrom(GetUsePtr(0), GetInputPtr(0), input_count);
188        set_outline_inputs(outline);
189      }
190    }
191    outline->count_++;
192    *GetInputPtr(input_count) = new_to;
193    Use* use = GetUsePtr(input_count);
194    CHECK(Use::InputIndexField::is_valid(input_count));
195    use->bit_field_ = Use::InputIndexField::encode(input_count) |
196                      Use::InlineField::encode(false);
197    new_to->AppendUse(use);
198  }
199  Verify();
200}
201
202
203void Node::InsertInput(Zone* zone, int index, Node* new_to) {
204  DCHECK_NOT_NULL(zone);
205  DCHECK_LE(0, index);
206  DCHECK_LT(index, InputCount());
207  AppendInput(zone, InputAt(InputCount() - 1));
208  for (int i = InputCount() - 1; i > index; --i) {
209    ReplaceInput(i, InputAt(i - 1));
210  }
211  ReplaceInput(index, new_to);
212  Verify();
213}
214
215void Node::InsertInputs(Zone* zone, int index, int count) {
216  DCHECK_NOT_NULL(zone);
217  DCHECK_LE(0, index);
218  DCHECK_LT(0, count);
219  DCHECK_LT(index, InputCount());
220  for (int i = 0; i < count; i++) {
221    AppendInput(zone, InputAt(std::max(InputCount() - count, 0)));
222  }
223  for (int i = InputCount() - count - 1; i >= std::max(index, count); --i) {
224    ReplaceInput(i, InputAt(i - count));
225  }
226  for (int i = 0; i < count; i++) {
227    ReplaceInput(index + i, nullptr);
228  }
229  Verify();
230}
231
232Node* Node::RemoveInput(int index) {
233  DCHECK_LE(0, index);
234  DCHECK_LT(index, InputCount());
235  Node* result = InputAt(index);
236  for (; index < InputCount() - 1; ++index) {
237    ReplaceInput(index, InputAt(index + 1));
238  }
239  TrimInputCount(InputCount() - 1);
240  Verify();
241  return result;
242}
243
244void Node::ClearInputs(int start, int count) {
245  ZoneNodePtr* input_ptr = GetInputPtr(start);
246  Use* use_ptr = GetUsePtr(start);
247  while (count-- > 0) {
248    DCHECK_EQ(input_ptr, use_ptr->input_ptr());
249    Node* input = *input_ptr;
250    *input_ptr = nullptr;
251    if (input) input->RemoveUse(use_ptr);
252    input_ptr++;
253    use_ptr--;
254  }
255  Verify();
256}
257
258
259void Node::NullAllInputs() { ClearInputs(0, InputCount()); }
260
261
262void Node::TrimInputCount(int new_input_count) {
263  int current_count = InputCount();
264  DCHECK_LE(new_input_count, current_count);
265  if (new_input_count == current_count) return;  // Nothing to do.
266  ClearInputs(new_input_count, current_count - new_input_count);
267  if (has_inline_inputs()) {
268    bit_field_ = InlineCountField::update(bit_field_, new_input_count);
269  } else {
270    outline_inputs()->count_ = new_input_count;
271  }
272}
273
274void Node::EnsureInputCount(Zone* zone, int new_input_count) {
275  int current_count = InputCount();
276  DCHECK_NE(current_count, 0);
277  if (current_count > new_input_count) {
278    TrimInputCount(new_input_count);
279  } else if (current_count < new_input_count) {
280    Node* dummy = InputAt(current_count - 1);
281    do {
282      AppendInput(zone, dummy);
283      current_count++;
284    } while (current_count < new_input_count);
285  }
286}
287
288int Node::UseCount() const {
289  int use_count = 0;
290  for (const Use* use = first_use_; use; use = use->next) {
291    ++use_count;
292  }
293  return use_count;
294}
295
296
297void Node::ReplaceUses(Node* that) {
298  DCHECK(this->first_use_ == nullptr || this->first_use_->prev == nullptr);
299  DCHECK(that->first_use_ == nullptr || that->first_use_->prev == nullptr);
300
301  // Update the pointers to {this} to point to {that}.
302  Use* last_use = nullptr;
303  for (Use* use = this->first_use_; use; use = use->next) {
304    *use->input_ptr() = that;
305    last_use = use;
306  }
307  if (last_use) {
308    // Concat the use list of {this} and {that}.
309    last_use->next = that->first_use_;
310    if (that->first_use_) that->first_use_->prev = last_use;
311    that->first_use_ = this->first_use_;
312  }
313  first_use_ = nullptr;
314}
315
316bool Node::OwnedBy(Node const* owner) const {
317  for (Use* use = first_use_; use; use = use->next) {
318    if (use->from() != owner) {
319      return false;
320    }
321  }
322  return first_use_ != nullptr;
323}
324
325bool Node::OwnedBy(Node const* owner1, Node const* owner2) const {
326  unsigned mask = 0;
327  for (Use* use = first_use_; use; use = use->next) {
328    Node* from = use->from();
329    if (from == owner1) {
330      mask |= 1;
331    } else if (from == owner2) {
332      mask |= 2;
333    } else {
334      return false;
335    }
336  }
337  return mask == 3;
338}
339
340void Node::Print(int depth) const {
341  StdoutStream os;
342  Print(os, depth);
343}
344
345namespace {
346void PrintNode(const Node* node, std::ostream& os, int depth,
347               int indentation = 0) {
348  for (int i = 0; i < indentation; ++i) {
349    os << "  ";
350  }
351  if (node) {
352    os << *node;
353  } else {
354    os << "(NULL)";
355  }
356  os << std::endl;
357  if (depth <= 0) return;
358  for (Node* input : node->inputs()) {
359    PrintNode(input, os, depth - 1, indentation + 1);
360  }
361}
362}  // namespace
363
364void Node::Print(std::ostream& os, int depth) const {
365  PrintNode(this, os, depth);
366}
367
368std::ostream& operator<<(std::ostream& os, const Node& n) {
369  os << n.id() << ": " << *n.op();
370  if (n.InputCount() > 0) {
371    os << "(";
372    for (int i = 0; i < n.InputCount(); ++i) {
373      if (i != 0) os << ", ";
374      if (n.InputAt(i)) {
375        os << n.InputAt(i)->id();
376      } else {
377        os << "null";
378      }
379    }
380    os << ")";
381  }
382  return os;
383}
384
385Node::Node(NodeId id, const Operator* op, int inline_count, int inline_capacity)
386    : op_(op),
387      mark_(0),
388      bit_field_(IdField::encode(id) | InlineCountField::encode(inline_count) |
389                 InlineCapacityField::encode(inline_capacity)),
390      first_use_(nullptr) {
391  // Check that the id didn't overflow.
392  STATIC_ASSERT(IdField::kMax < std::numeric_limits<NodeId>::max());
393  CHECK(IdField::is_valid(id));
394
395  // Inputs must either be out of line or within the inline capacity.
396  DCHECK(inline_count == kOutlineMarker || inline_count <= inline_capacity);
397  DCHECK_LE(inline_capacity, kMaxInlineCapacity);
398}
399
400
401void Node::AppendUse(Use* use) {
402  DCHECK(first_use_ == nullptr || first_use_->prev == nullptr);
403  DCHECK_EQ(this, *use->input_ptr());
404  use->next = first_use_;
405  use->prev = nullptr;
406  if (first_use_) first_use_->prev = use;
407  first_use_ = use;
408}
409
410
411void Node::RemoveUse(Use* use) {
412  DCHECK(first_use_ == nullptr || first_use_->prev == nullptr);
413  if (use->prev) {
414    DCHECK_NE(first_use_, use);
415    use->prev->next = use->next;
416  } else {
417    DCHECK_EQ(first_use_, use);
418    first_use_ = use->next;
419  }
420  if (use->next) {
421    use->next->prev = use->prev;
422  }
423}
424
425
426#if DEBUG
427void Node::Verify() {
428  // Check basic validity of input data structures.
429  fflush(stdout);
430  int count = this->InputCount();
431  // Avoid quadratic explosion for mega nodes; only verify if the input
432  // count is less than 200 or is a round number of 100s.
433  if (count > 200 && count % 100) return;
434
435  for (int i = 0; i < count; i++) {
436    DCHECK_EQ(i, this->GetUsePtr(i)->input_index());
437    DCHECK_EQ(this->GetInputPtr(i), this->GetUsePtr(i)->input_ptr());
438    DCHECK_EQ(count, this->InputCount());
439  }
440  {  // Direct input iteration.
441    int index = 0;
442    for (Node* input : this->inputs()) {
443      DCHECK_EQ(this->InputAt(index), input);
444      index++;
445    }
446    DCHECK_EQ(count, index);
447    DCHECK_EQ(this->InputCount(), index);
448  }
449  {  // Input edge iteration.
450    int index = 0;
451    for (Edge edge : this->input_edges()) {
452      DCHECK_EQ(edge.from(), this);
453      DCHECK_EQ(index, edge.index());
454      DCHECK_EQ(this->InputAt(index), edge.to());
455      index++;
456    }
457    DCHECK_EQ(count, index);
458    DCHECK_EQ(this->InputCount(), index);
459  }
460}
461#endif
462
463Node::InputEdges::iterator Node::InputEdges::iterator::operator++(int n) {
464  iterator result(*this);
465  ++(*this);
466  return result;
467}
468
469
470Node::Inputs::const_iterator Node::Inputs::const_iterator::operator++(int n) {
471  const_iterator result(*this);
472  ++(*this);
473  return result;
474}
475
476
477Node::UseEdges::iterator Node::UseEdges::iterator::operator++(int n) {
478  iterator result(*this);
479  ++(*this);
480  return result;
481}
482
483
484bool Node::UseEdges::empty() const { return begin() == end(); }
485
486
487Node::Uses::const_iterator Node::Uses::const_iterator::operator++(int n) {
488  const_iterator result(*this);
489  ++(*this);
490  return result;
491}
492
493
494bool Node::Uses::empty() const { return begin() == end(); }
495
496}  // namespace compiler
497}  // namespace internal
498}  // namespace v8
499
500V8_EXPORT_PRIVATE extern void _v8_internal_Node_Print(void* object) {
501  reinterpret_cast<i::compiler::Node*>(object)->Print();
502}
503