blob: f59c8bc909ab03cb3b945213177bcef50fc8db1e [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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/representation-change.h"
6
7#include <sstream>
8
9#include "src/base/bits.h"
10#include "src/code-factory.h"
11#include "src/compiler/machine-operator.h"
12
13namespace v8 {
14namespace internal {
15namespace compiler {
16
17const char* Truncation::description() const {
18 switch (kind()) {
19 case TruncationKind::kNone:
20 return "no-value-use";
21 case TruncationKind::kBool:
22 return "truncate-to-bool";
23 case TruncationKind::kWord32:
24 return "truncate-to-word32";
25 case TruncationKind::kWord64:
26 return "truncate-to-word64";
27 case TruncationKind::kFloat32:
28 return "truncate-to-float32";
29 case TruncationKind::kFloat64:
30 return "truncate-to-float64";
31 case TruncationKind::kAny:
32 return "no-truncation";
33 }
34 UNREACHABLE();
35 return nullptr;
36}
37
38
39// Partial order for truncations:
40//
41// kWord64 kAny
42// ^ ^
43// \ |
44// \ kFloat64 <--+
45// \ ^ ^ |
46// \ / | |
47// kWord32 kFloat32 kBool
48// ^ ^ ^
49// \ | /
50// \ | /
51// \ | /
52// \ | /
53// \ | /
54// kNone
55
56// static
57Truncation::TruncationKind Truncation::Generalize(TruncationKind rep1,
58 TruncationKind rep2) {
59 if (LessGeneral(rep1, rep2)) return rep2;
60 if (LessGeneral(rep2, rep1)) return rep1;
61 // Handle the generalization of float64-representable values.
62 if (LessGeneral(rep1, TruncationKind::kFloat64) &&
63 LessGeneral(rep2, TruncationKind::kFloat64)) {
64 return TruncationKind::kFloat64;
65 }
66 // All other combinations are illegal.
67 FATAL("Tried to combine incompatible truncations");
68 return TruncationKind::kNone;
69}
70
71
72// static
73bool Truncation::LessGeneral(TruncationKind rep1, TruncationKind rep2) {
74 switch (rep1) {
75 case TruncationKind::kNone:
76 return true;
77 case TruncationKind::kBool:
78 return rep2 == TruncationKind::kBool || rep2 == TruncationKind::kAny;
79 case TruncationKind::kWord32:
80 return rep2 == TruncationKind::kWord32 ||
81 rep2 == TruncationKind::kWord64 ||
82 rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny;
83 case TruncationKind::kWord64:
84 return rep2 == TruncationKind::kWord64;
85 case TruncationKind::kFloat32:
86 return rep2 == TruncationKind::kFloat32 ||
87 rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny;
88 case TruncationKind::kFloat64:
89 return rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny;
90 case TruncationKind::kAny:
91 return rep2 == TruncationKind::kAny;
92 }
93 UNREACHABLE();
94 return false;
95}
96
97
98namespace {
99
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000100bool IsWord(MachineRepresentation rep) {
101 return rep == MachineRepresentation::kWord8 ||
102 rep == MachineRepresentation::kWord16 ||
103 rep == MachineRepresentation::kWord32;
104}
105
106} // namespace
107
108
109// Changes representation from {output_rep} to {use_rep}. The {truncation}
110// parameter is only used for sanity checking - if the changer cannot figure
111// out signedness for the word32->float64 conversion, then we check that the
112// uses truncate to word32 (so they do not care about signedness).
113Node* RepresentationChanger::GetRepresentationFor(
114 Node* node, MachineRepresentation output_rep, Type* output_type,
115 MachineRepresentation use_rep, Truncation truncation) {
116 if (output_rep == MachineRepresentation::kNone) {
117 // The output representation should be set.
118 return TypeError(node, output_rep, output_type, use_rep);
119 }
120 if (use_rep == output_rep) {
121 // Representations are the same. That's a no-op.
122 return node;
123 }
124 if (IsWord(use_rep) && IsWord(output_rep)) {
125 // Both are words less than or equal to 32-bits.
126 // Since loads of integers from memory implicitly sign or zero extend the
127 // value to the full machine word size and stores implicitly truncate,
128 // no representation change is necessary.
129 return node;
130 }
131 switch (use_rep) {
132 case MachineRepresentation::kTagged:
133 return GetTaggedRepresentationFor(node, output_rep, output_type);
134 case MachineRepresentation::kFloat32:
135 return GetFloat32RepresentationFor(node, output_rep, output_type,
136 truncation);
137 case MachineRepresentation::kFloat64:
138 return GetFloat64RepresentationFor(node, output_rep, output_type,
139 truncation);
140 case MachineRepresentation::kBit:
141 return GetBitRepresentationFor(node, output_rep, output_type);
142 case MachineRepresentation::kWord8:
143 case MachineRepresentation::kWord16:
144 case MachineRepresentation::kWord32:
Ben Murdochda12d292016-06-02 14:46:10 +0100145 return GetWord32RepresentationFor(node, output_rep, output_type,
146 truncation);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000147 case MachineRepresentation::kWord64:
148 return GetWord64RepresentationFor(node, output_rep, output_type);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100149 case MachineRepresentation::kSimd128: // Fall through.
150 // TODO(bbudge) Handle conversions between tagged and untagged.
151 break;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000152 case MachineRepresentation::kNone:
153 return node;
154 }
155 UNREACHABLE();
156 return nullptr;
157}
158
159
160Node* RepresentationChanger::GetTaggedRepresentationFor(
161 Node* node, MachineRepresentation output_rep, Type* output_type) {
162 // Eagerly fold representation changes for constants.
163 switch (node->opcode()) {
164 case IrOpcode::kNumberConstant:
165 case IrOpcode::kHeapConstant:
166 return node; // No change necessary.
167 case IrOpcode::kInt32Constant:
168 if (output_type->Is(Type::Signed32())) {
169 int32_t value = OpParameter<int32_t>(node);
170 return jsgraph()->Constant(value);
171 } else if (output_type->Is(Type::Unsigned32())) {
172 uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
173 return jsgraph()->Constant(static_cast<double>(value));
174 } else if (output_rep == MachineRepresentation::kBit) {
175 return OpParameter<int32_t>(node) == 0 ? jsgraph()->FalseConstant()
176 : jsgraph()->TrueConstant();
177 } else {
178 return TypeError(node, output_rep, output_type,
179 MachineRepresentation::kTagged);
180 }
181 case IrOpcode::kFloat64Constant:
182 return jsgraph()->Constant(OpParameter<double>(node));
183 case IrOpcode::kFloat32Constant:
184 return jsgraph()->Constant(OpParameter<float>(node));
185 default:
186 break;
187 }
188 // Select the correct X -> Tagged operator.
189 const Operator* op;
190 if (output_rep == MachineRepresentation::kBit) {
191 op = simplified()->ChangeBitToBool();
192 } else if (IsWord(output_rep)) {
193 if (output_type->Is(Type::Unsigned32())) {
194 op = simplified()->ChangeUint32ToTagged();
195 } else if (output_type->Is(Type::Signed32())) {
196 op = simplified()->ChangeInt32ToTagged();
197 } else {
198 return TypeError(node, output_rep, output_type,
199 MachineRepresentation::kTagged);
200 }
201 } else if (output_rep ==
202 MachineRepresentation::kFloat32) { // float32 -> float64 -> tagged
203 node = InsertChangeFloat32ToFloat64(node);
204 op = simplified()->ChangeFloat64ToTagged();
205 } else if (output_rep == MachineRepresentation::kFloat64) {
206 op = simplified()->ChangeFloat64ToTagged();
207 } else {
208 return TypeError(node, output_rep, output_type,
209 MachineRepresentation::kTagged);
210 }
211 return jsgraph()->graph()->NewNode(op, node);
212}
213
214
215Node* RepresentationChanger::GetFloat32RepresentationFor(
216 Node* node, MachineRepresentation output_rep, Type* output_type,
217 Truncation truncation) {
218 // Eagerly fold representation changes for constants.
219 switch (node->opcode()) {
220 case IrOpcode::kFloat64Constant:
221 case IrOpcode::kNumberConstant:
222 return jsgraph()->Float32Constant(
223 DoubleToFloat32(OpParameter<double>(node)));
224 case IrOpcode::kInt32Constant:
225 if (output_type->Is(Type::Unsigned32())) {
226 uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
227 return jsgraph()->Float32Constant(static_cast<float>(value));
228 } else {
229 int32_t value = OpParameter<int32_t>(node);
230 return jsgraph()->Float32Constant(static_cast<float>(value));
231 }
232 case IrOpcode::kFloat32Constant:
233 return node; // No change necessary.
234 default:
235 break;
236 }
237 // Select the correct X -> Float32 operator.
Ben Murdochda12d292016-06-02 14:46:10 +0100238 const Operator* op = nullptr;
239 if (IsWord(output_rep)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000240 if (output_type->Is(Type::Signed32())) {
Ben Murdochda12d292016-06-02 14:46:10 +0100241 // int32 -> float64 -> float32
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000242 op = machine()->ChangeInt32ToFloat64();
Ben Murdochda12d292016-06-02 14:46:10 +0100243 node = jsgraph()->graph()->NewNode(op, node);
244 op = machine()->TruncateFloat64ToFloat32();
245 } else if (output_type->Is(Type::Unsigned32()) ||
246 truncation.TruncatesToWord32()) {
247 // Either the output is uint32 or the uses only care about the
248 // low 32 bits (so we can pick uint32 safely).
249
250 // uint32 -> float64 -> float32
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000251 op = machine()->ChangeUint32ToFloat64();
Ben Murdochda12d292016-06-02 14:46:10 +0100252 node = jsgraph()->graph()->NewNode(op, node);
253 op = machine()->TruncateFloat64ToFloat32();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000254 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000255 } else if (output_rep == MachineRepresentation::kTagged) {
Ben Murdochda12d292016-06-02 14:46:10 +0100256 if (output_type->Is(Type::Number())) {
257 op = simplified()
258 ->ChangeTaggedToFloat64(); // tagged -> float64 -> float32
259 node = jsgraph()->graph()->NewNode(op, node);
260 op = machine()->TruncateFloat64ToFloat32();
261 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000262 } else if (output_rep == MachineRepresentation::kFloat64) {
263 op = machine()->TruncateFloat64ToFloat32();
Ben Murdochda12d292016-06-02 14:46:10 +0100264 }
265 if (op == nullptr) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000266 return TypeError(node, output_rep, output_type,
267 MachineRepresentation::kFloat32);
268 }
269 return jsgraph()->graph()->NewNode(op, node);
270}
271
272
273Node* RepresentationChanger::GetFloat64RepresentationFor(
274 Node* node, MachineRepresentation output_rep, Type* output_type,
275 Truncation truncation) {
276 // Eagerly fold representation changes for constants.
277 switch (node->opcode()) {
278 case IrOpcode::kNumberConstant:
279 return jsgraph()->Float64Constant(OpParameter<double>(node));
280 case IrOpcode::kInt32Constant:
281 if (output_type->Is(Type::Signed32())) {
282 int32_t value = OpParameter<int32_t>(node);
283 return jsgraph()->Float64Constant(value);
284 } else {
285 DCHECK(output_type->Is(Type::Unsigned32()));
286 uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
287 return jsgraph()->Float64Constant(static_cast<double>(value));
288 }
289 case IrOpcode::kFloat64Constant:
290 return node; // No change necessary.
291 case IrOpcode::kFloat32Constant:
292 return jsgraph()->Float64Constant(OpParameter<float>(node));
293 default:
294 break;
295 }
296 // Select the correct X -> Float64 operator.
Ben Murdochda12d292016-06-02 14:46:10 +0100297 const Operator* op = nullptr;
298 if (IsWord(output_rep)) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000299 if (output_type->Is(Type::Signed32())) {
300 op = machine()->ChangeInt32ToFloat64();
Ben Murdochda12d292016-06-02 14:46:10 +0100301 } else if (output_type->Is(Type::Unsigned32()) ||
302 truncation.TruncatesToWord32()) {
303 // Either the output is uint32 or the uses only care about the
304 // low 32 bits (so we can pick uint32 safely).
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000305 op = machine()->ChangeUint32ToFloat64();
306 }
307 } else if (output_rep == MachineRepresentation::kTagged) {
Ben Murdochda12d292016-06-02 14:46:10 +0100308 if (output_type->Is(Type::Number())) {
309 op = simplified()->ChangeTaggedToFloat64();
310 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000311 } else if (output_rep == MachineRepresentation::kFloat32) {
312 op = machine()->ChangeFloat32ToFloat64();
Ben Murdochda12d292016-06-02 14:46:10 +0100313 }
314 if (op == nullptr) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000315 return TypeError(node, output_rep, output_type,
316 MachineRepresentation::kFloat64);
317 }
318 return jsgraph()->graph()->NewNode(op, node);
319}
320
321
322Node* RepresentationChanger::MakeTruncatedInt32Constant(double value) {
323 return jsgraph()->Int32Constant(DoubleToInt32(value));
324}
325
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000326Node* RepresentationChanger::GetWord32RepresentationFor(
Ben Murdochda12d292016-06-02 14:46:10 +0100327 Node* node, MachineRepresentation output_rep, Type* output_type,
328 Truncation truncation) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000329 // Eagerly fold representation changes for constants.
330 switch (node->opcode()) {
331 case IrOpcode::kInt32Constant:
332 return node; // No change necessary.
333 case IrOpcode::kFloat32Constant:
334 return MakeTruncatedInt32Constant(OpParameter<float>(node));
335 case IrOpcode::kNumberConstant:
336 case IrOpcode::kFloat64Constant:
337 return MakeTruncatedInt32Constant(OpParameter<double>(node));
338 default:
339 break;
340 }
341 // Select the correct X -> Word32 operator.
Ben Murdochda12d292016-06-02 14:46:10 +0100342 const Operator* op = nullptr;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000343 if (output_rep == MachineRepresentation::kBit) {
344 return node; // Sloppy comparison -> word32
345 } else if (output_rep == MachineRepresentation::kFloat64) {
Ben Murdochda12d292016-06-02 14:46:10 +0100346 if (output_type->Is(Type::Unsigned32())) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000347 op = machine()->ChangeFloat64ToUint32();
Ben Murdochda12d292016-06-02 14:46:10 +0100348 } else if (output_type->Is(Type::Signed32())) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000349 op = machine()->ChangeFloat64ToInt32();
Ben Murdochda12d292016-06-02 14:46:10 +0100350 } else if (truncation.TruncatesToWord32()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000351 op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript);
352 }
353 } else if (output_rep == MachineRepresentation::kFloat32) {
354 node = InsertChangeFloat32ToFloat64(node); // float32 -> float64 -> int32
Ben Murdochda12d292016-06-02 14:46:10 +0100355 if (output_type->Is(Type::Unsigned32())) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000356 op = machine()->ChangeFloat64ToUint32();
Ben Murdochda12d292016-06-02 14:46:10 +0100357 } else if (output_type->Is(Type::Signed32())) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000358 op = machine()->ChangeFloat64ToInt32();
Ben Murdochda12d292016-06-02 14:46:10 +0100359 } else if (truncation.TruncatesToWord32()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000360 op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript);
361 }
362 } else if (output_rep == MachineRepresentation::kTagged) {
Ben Murdochda12d292016-06-02 14:46:10 +0100363 if (output_type->Is(Type::Unsigned32())) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000364 op = simplified()->ChangeTaggedToUint32();
Ben Murdochda12d292016-06-02 14:46:10 +0100365 } else if (output_type->Is(Type::Signed32())) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000366 op = simplified()->ChangeTaggedToInt32();
Ben Murdochda12d292016-06-02 14:46:10 +0100367 } else if (truncation.TruncatesToWord32()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000368 node = InsertChangeTaggedToFloat64(node);
369 op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript);
370 }
Ben Murdochda12d292016-06-02 14:46:10 +0100371 }
372 if (op == nullptr) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000373 return TypeError(node, output_rep, output_type,
374 MachineRepresentation::kWord32);
375 }
376 return jsgraph()->graph()->NewNode(op, node);
377}
378
379
380Node* RepresentationChanger::GetBitRepresentationFor(
381 Node* node, MachineRepresentation output_rep, Type* output_type) {
382 // Eagerly fold representation changes for constants.
383 switch (node->opcode()) {
384 case IrOpcode::kHeapConstant: {
385 Handle<HeapObject> value = OpParameter<Handle<HeapObject>>(node);
386 DCHECK(value.is_identical_to(factory()->true_value()) ||
387 value.is_identical_to(factory()->false_value()));
388 return jsgraph()->Int32Constant(
389 value.is_identical_to(factory()->true_value()) ? 1 : 0);
390 }
391 default:
392 break;
393 }
394 // Select the correct X -> Bit operator.
395 const Operator* op;
396 if (output_rep == MachineRepresentation::kTagged) {
397 op = simplified()->ChangeBoolToBit();
398 } else {
399 return TypeError(node, output_rep, output_type,
400 MachineRepresentation::kBit);
401 }
402 return jsgraph()->graph()->NewNode(op, node);
403}
404
405
406Node* RepresentationChanger::GetWord64RepresentationFor(
407 Node* node, MachineRepresentation output_rep, Type* output_type) {
408 if (output_rep == MachineRepresentation::kBit) {
409 return node; // Sloppy comparison -> word64
410 }
411 // Can't really convert Word64 to anything else. Purported to be internal.
412 return TypeError(node, output_rep, output_type,
413 MachineRepresentation::kWord64);
414}
415
416
417const Operator* RepresentationChanger::Int32OperatorFor(
418 IrOpcode::Value opcode) {
419 switch (opcode) {
420 case IrOpcode::kNumberAdd:
421 return machine()->Int32Add();
422 case IrOpcode::kNumberSubtract:
423 return machine()->Int32Sub();
424 case IrOpcode::kNumberMultiply:
425 return machine()->Int32Mul();
426 case IrOpcode::kNumberDivide:
427 return machine()->Int32Div();
428 case IrOpcode::kNumberModulus:
429 return machine()->Int32Mod();
430 case IrOpcode::kNumberBitwiseOr:
431 return machine()->Word32Or();
432 case IrOpcode::kNumberBitwiseXor:
433 return machine()->Word32Xor();
434 case IrOpcode::kNumberBitwiseAnd:
435 return machine()->Word32And();
436 case IrOpcode::kNumberEqual:
437 return machine()->Word32Equal();
438 case IrOpcode::kNumberLessThan:
439 return machine()->Int32LessThan();
440 case IrOpcode::kNumberLessThanOrEqual:
441 return machine()->Int32LessThanOrEqual();
442 default:
443 UNREACHABLE();
444 return nullptr;
445 }
446}
447
448
449const Operator* RepresentationChanger::Uint32OperatorFor(
450 IrOpcode::Value opcode) {
451 switch (opcode) {
452 case IrOpcode::kNumberAdd:
453 return machine()->Int32Add();
454 case IrOpcode::kNumberSubtract:
455 return machine()->Int32Sub();
456 case IrOpcode::kNumberMultiply:
457 return machine()->Int32Mul();
458 case IrOpcode::kNumberDivide:
459 return machine()->Uint32Div();
460 case IrOpcode::kNumberModulus:
461 return machine()->Uint32Mod();
462 case IrOpcode::kNumberEqual:
463 return machine()->Word32Equal();
464 case IrOpcode::kNumberLessThan:
465 return machine()->Uint32LessThan();
466 case IrOpcode::kNumberLessThanOrEqual:
467 return machine()->Uint32LessThanOrEqual();
Ben Murdochda12d292016-06-02 14:46:10 +0100468 case IrOpcode::kNumberClz32:
469 return machine()->Word32Clz();
470 case IrOpcode::kNumberImul:
471 return machine()->Int32Mul();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000472 default:
473 UNREACHABLE();
474 return nullptr;
475 }
476}
477
478
479const Operator* RepresentationChanger::Float64OperatorFor(
480 IrOpcode::Value opcode) {
481 switch (opcode) {
482 case IrOpcode::kNumberAdd:
483 return machine()->Float64Add();
484 case IrOpcode::kNumberSubtract:
485 return machine()->Float64Sub();
486 case IrOpcode::kNumberMultiply:
487 return machine()->Float64Mul();
488 case IrOpcode::kNumberDivide:
489 return machine()->Float64Div();
490 case IrOpcode::kNumberModulus:
491 return machine()->Float64Mod();
492 case IrOpcode::kNumberEqual:
493 return machine()->Float64Equal();
494 case IrOpcode::kNumberLessThan:
495 return machine()->Float64LessThan();
496 case IrOpcode::kNumberLessThanOrEqual:
497 return machine()->Float64LessThanOrEqual();
498 default:
499 UNREACHABLE();
500 return nullptr;
501 }
502}
503
504
505Node* RepresentationChanger::TypeError(Node* node,
506 MachineRepresentation output_rep,
507 Type* output_type,
508 MachineRepresentation use) {
509 type_error_ = true;
510 if (!testing_type_errors_) {
511 std::ostringstream out_str;
512 out_str << output_rep << " (";
513 output_type->PrintTo(out_str, Type::SEMANTIC_DIM);
514 out_str << ")";
515
516 std::ostringstream use_str;
517 use_str << use;
518
519 V8_Fatal(__FILE__, __LINE__,
520 "RepresentationChangerError: node #%d:%s of "
521 "%s cannot be changed to %s",
522 node->id(), node->op()->mnemonic(), out_str.str().c_str(),
523 use_str.str().c_str());
524 }
525 return node;
526}
527
528
529Node* RepresentationChanger::InsertChangeFloat32ToFloat64(Node* node) {
530 return jsgraph()->graph()->NewNode(machine()->ChangeFloat32ToFloat64(), node);
531}
532
533
534Node* RepresentationChanger::InsertChangeTaggedToFloat64(Node* node) {
535 return jsgraph()->graph()->NewNode(simplified()->ChangeTaggedToFloat64(),
536 node);
537}
538
539} // namespace compiler
540} // namespace internal
541} // namespace v8