v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
node.cc
Go to the documentation of this file.
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
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
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();
31 for (int current = 0; current < count; current++) {
32 new_use_ptr->bit_field_ =
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.
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.
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;
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
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.
163 *GetInputPtr(inline_count) = new_to;
164 Use* use = GetUsePtr(inline_count);
166 use->bit_field_ = Use::InputIndexField::encode(inline_count) |
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);
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);
195 use->bit_field_ = Use::InputIndexField::encode(input_count) |
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) {
225 }
226 for (int i = 0; i < count; i++) {
227 ReplaceInput(index + i, nullptr);
228 }
229 Verify();
230}
231
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 }
240 Verify();
241 return result;
242}
243
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
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()) {
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
297 int use_count = 0;
298 for (Use* use = first_use_; use; use = use->next) {
299 if (use->from()->opcode() == IrOpcode::kBranch) {
300 ++use_count;
301 }
302 }
303 return use_count;
304}
305
307 DCHECK(this->first_use_ == nullptr || this->first_use_->prev == nullptr);
308 DCHECK(that->first_use_ == nullptr || that->first_use_->prev == nullptr);
309
310 // Update the pointers to {this} to point to {that}.
311 Use* last_use = nullptr;
312 for (Use* use = this->first_use_; use; use = use->next) {
313 *use->input_ptr() = that;
314 last_use = use;
315 }
316 if (last_use) {
317 // Concat the use list of {this} and {that}.
318 last_use->next = that->first_use_;
319 if (that->first_use_) that->first_use_->prev = last_use;
320 that->first_use_ = this->first_use_;
321 }
322 first_use_ = nullptr;
323}
324
325bool Node::OwnedBy(Node const* owner) const {
326 for (Use* use = first_use_; use; use = use->next) {
327 if (use->from() != owner) {
328 return false;
329 }
330 }
331 return first_use_ != nullptr;
332}
333
334bool Node::OwnedBy(Node const* owner1, Node const* owner2) const {
335 unsigned mask = 0;
336 for (Use* use = first_use_; use; use = use->next) {
337 Node* from = use->from();
338 if (from == owner1) {
339 mask |= 1;
340 } else if (from == owner2) {
341 mask |= 2;
342 } else {
343 return false;
344 }
345 }
346 return mask == 3;
347}
348
349void Node::Print(int depth) const {
350 StdoutStream os;
351 Print(os, depth);
352}
353
354namespace {
355void PrintNode(const Node* node, std::ostream& os, int depth,
356 int indentation = 0) {
357 for (int i = 0; i < indentation; ++i) {
358 os << " ";
359 }
360 if (node) {
361 os << *node << std::endl;
362 } else {
363 os << "(NULL)" << std::endl;
364 return;
365 }
366 if (depth <= 0) return;
367 for (Node* input : node->inputs()) {
368 PrintNode(input, os, depth - 1, indentation + 1);
369 }
370}
371} // namespace
372
373void Node::Print(std::ostream& os, int depth) const {
374 PrintNode(this, os, depth);
375}
376
377std::ostream& operator<<(std::ostream& os, const Node& n) {
378 os << n.id() << ": " << *n.op();
379 if (n.InputCount() > 0) {
380 os << "(";
381 for (int i = 0; i < n.InputCount(); ++i) {
382 if (i != 0) os << ", ";
383 if (n.InputAt(i)) {
384 os << n.InputAt(i)->id();
385 } else {
386 os << "null";
387 }
388 }
389 os << ")";
390 }
391 return os;
392}
393
394Node::Node(NodeId id, const Operator* op, int inline_count, int inline_capacity)
395 : op_(op),
396 mark_(0),
397 bit_field_(IdField::encode(id) | InlineCountField::encode(inline_count) |
398 InlineCapacityField::encode(inline_capacity)),
399 first_use_(nullptr) {
400 // Check that the id didn't overflow.
401 static_assert(IdField::kMax < std::numeric_limits<NodeId>::max());
403
404 // Inputs must either be out of line or within the inline capacity.
405 DCHECK(inline_count == kOutlineMarker || inline_count <= inline_capacity);
406 DCHECK_LE(inline_capacity, kMaxInlineCapacity);
407}
408
410 DCHECK(first_use_ == nullptr || first_use_->prev == nullptr);
411 DCHECK_EQ(this, *use->input_ptr());
412 use->next = first_use_;
413 use->prev = nullptr;
414 if (first_use_) first_use_->prev = use;
415 first_use_ = use;
416}
417
418
420 DCHECK(first_use_ == nullptr || first_use_->prev == nullptr);
421 if (use->prev) {
422 DCHECK_NE(first_use_, use);
423 use->prev->next = use->next;
424 } else {
425 DCHECK_EQ(first_use_, use);
426 first_use_ = use->next;
427 }
428 if (use->next) {
429 use->next->prev = use->prev;
430 }
431}
432
433
434#if DEBUG
435void Node::Verify() {
436 // Check basic validity of input data structures.
437 fflush(stdout);
438 int count = this->InputCount();
439 // Avoid quadratic explosion for mega nodes; only verify if the input
440 // count is less than 200 or is a round number of 100s.
441 if (count > 200 && count % 100) return;
442
443 for (int i = 0; i < count; i++) {
444 DCHECK_EQ(i, this->GetUsePtr(i)->input_index());
445 DCHECK_EQ(this->GetInputPtr(i), this->GetUsePtr(i)->input_ptr());
446 DCHECK_EQ(count, this->InputCount());
447 }
448 { // Direct input iteration.
449 int index = 0;
450 for (Node* input : this->inputs()) {
451 DCHECK_EQ(this->InputAt(index), input);
452 index++;
453 }
454 DCHECK_EQ(count, index);
455 DCHECK_EQ(this->InputCount(), index);
456 }
457 { // Input edge iteration.
458 int index = 0;
459 for (Edge edge : this->input_edges()) {
460 DCHECK_EQ(edge.from(), this);
461 DCHECK_EQ(index, edge.index());
462 DCHECK_EQ(this->InputAt(index), edge.to());
463 index++;
464 }
465 DCHECK_EQ(count, index);
466 DCHECK_EQ(this->InputCount(), index);
467 }
468}
469#endif
470
476
477
483
484
486 iterator result(*this);
487 ++(*this);
488 return result;
489}
490
491
492bool Node::UseEdges::empty() const { return begin() == end(); }
493
494
500
501
502bool Node::Uses::empty() const { return begin() == end(); }
503
504} // namespace compiler
505} // namespace internal
506} // namespace v8
507
509 reinterpret_cast<i::compiler::Node*>(object)->Print();
510}
static constexpr U kMax
Definition bit-field.h:44
static constexpr T decode(U value)
Definition bit-field.h:66
static constexpr bool is_valid(T value)
Definition bit-field.h:50
static constexpr U encode(T value)
Definition bit-field.h:55
static V8_NODISCARD constexpr U update(U previous, T value)
Definition bit-field.h:61
bool supports_compression() const
Definition zone.h:50
void * Allocate(size_t size)
Definition zone.h:61
static Node * New(Zone *zone, NodeId id, const Operator *op, int input_count, Node *const *inputs, bool has_extensible_inputs)
Definition node.cc:131
int BranchUseCount() const
Definition node.cc:296
static Node * Clone(Zone *zone, NodeId id, const Node *node)
Definition node.cc:136
ZoneNodePtr * GetInputPtr(int input_index)
Definition node.h:259
const Operator * op_
Definition node.h:297
InputEdges input_edges()
Definition node.h:466
GraphZoneTraits::Ptr< OutOfLineInputs > ZoneOutOfLineInputsPtr
Definition node.h:178
bool has_inline_inputs() const
Definition node.h:285
static Node * NewImpl(Zone *zone, NodeId id, const Operator *op, int input_count, NodePtrT const *inputs, bool has_extensible_inputs)
Definition node.cc:58
void AppendUse(Use *use)
Definition node.cc:409
Use * GetUsePtr(int input_index)
Definition node.h:263
Inputs inputs() const
Definition node.h:478
OutOfLineInputs * outline_inputs() const
Definition node.h:248
Node(NodeId id, const Operator *op, int inline_count, int inline_capacity)
Definition node.cc:394
void ClearInputs(int start, int count)
Definition node.cc:244
void TrimInputCount(int new_input_count)
Definition node.cc:262
void RemoveUse(Use *use)
Definition node.cc:419
const Operator * op() const
Definition node.h:50
void set_outline_inputs(OutOfLineInputs *outline)
Definition node.h:251
int InputCount() const
Definition node.h:59
void ReplaceInput(int index, Node *new_to)
Definition node.h:76
Node * InputAt(int index) const
Definition node.h:70
void InsertInputs(Zone *zone, int index, int count)
Definition node.cc:215
void AppendInput(Zone *zone, Node *new_to)
Definition node.cc:154
static const int kMaxInlineCapacity
Definition node.h:295
bool OwnedBy(Node const *owner) const
Definition node.cc:325
Node * RemoveInput(int index)
Definition node.cc:232
static const int kOutlineMarker
Definition node.h:294
void ReplaceUses(Node *replace_to)
Definition node.cc:306
void InsertInput(Zone *zone, int index, Node *new_to)
Definition node.cc:203
void EnsureInputCount(Zone *zone, int new_input_count)
Definition node.cc:274
size_t count_
Definition sweeper.cc:55
int start
uint32_t count
LineAndColumn current
Node * node
ZoneVector< RpoNumber > & result
Point to
uint32_t const mask
GraphZoneTraits::Ptr< Node > ZoneNodePtr
std::ostream & operator<<(std::ostream &os, AccessMode access_mode)
void Print(Tagged< Object > obj)
Definition objects.h:774
static constexpr bool kCompressGraphZone
Definition globals.h:525
other heap size generate builtins concurrently on separate threads in mksnapshot track concurrent recompilation artificial compilation delay in ms max number of threads that concurrent Turbofan can use(0 for unbounded)") DEFINE_BOOL( stress_concurrent_inlining
V8_DEBUGGING_EXPORT void _v8_internal_Node_Print(void *object)
Definition node.cc:508
#define V8_DEBUGGING_EXPORT
#define FATAL(...)
Definition logging.h:47
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK_IMPLIES(lhs, rhs)
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
void ExtractFrom(Use *use_ptr, ZoneNodePtr *input_ptr, int count)
Definition node.cc:23
static OutOfLineInputs * New(Zone *zone, int capacity)
Definition node.cc:11
ZoneNodePtr * input_ptr()
Definition node.h:189