1 | // Copyright 2017 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-type-hint-lowering.h" |
6 | |
7 | #include "src/compiler/access-builder.h" |
8 | #include "src/compiler/js-graph.h" |
9 | #include "src/compiler/operator-properties.h" |
10 | #include "src/compiler/simplified-operator.h" |
11 | #include "src/feedback-vector.h" |
12 | #include "src/type-hints.h" |
13 | |
14 | namespace v8 { |
15 | namespace internal { |
16 | namespace compiler { |
17 | |
18 | namespace { |
19 | |
20 | bool BinaryOperationHintToNumberOperationHint( |
21 | BinaryOperationHint binop_hint, NumberOperationHint* number_hint) { |
22 | switch (binop_hint) { |
23 | case BinaryOperationHint::kSignedSmall: |
24 | *number_hint = NumberOperationHint::kSignedSmall; |
25 | return true; |
26 | case BinaryOperationHint::kSignedSmallInputs: |
27 | *number_hint = NumberOperationHint::kSignedSmallInputs; |
28 | return true; |
29 | case BinaryOperationHint::kSigned32: |
30 | *number_hint = NumberOperationHint::kSigned32; |
31 | return true; |
32 | case BinaryOperationHint::kNumber: |
33 | *number_hint = NumberOperationHint::kNumber; |
34 | return true; |
35 | case BinaryOperationHint::kNumberOrOddball: |
36 | *number_hint = NumberOperationHint::kNumberOrOddball; |
37 | return true; |
38 | case BinaryOperationHint::kAny: |
39 | case BinaryOperationHint::kNone: |
40 | case BinaryOperationHint::kConsOneByteString: |
41 | case BinaryOperationHint::kConsTwoByteString: |
42 | case BinaryOperationHint::kConsString: |
43 | case BinaryOperationHint::kString: |
44 | case BinaryOperationHint::kBigInt: |
45 | break; |
46 | } |
47 | return false; |
48 | } |
49 | |
50 | } // namespace |
51 | |
52 | class JSSpeculativeBinopBuilder final { |
53 | public: |
54 | JSSpeculativeBinopBuilder(const JSTypeHintLowering* lowering, |
55 | const Operator* op, Node* left, Node* right, |
56 | Node* effect, Node* control, FeedbackSlot slot) |
57 | : lowering_(lowering), |
58 | op_(op), |
59 | left_(left), |
60 | right_(right), |
61 | effect_(effect), |
62 | control_(control), |
63 | slot_(slot) {} |
64 | |
65 | BinaryOperationHint GetBinaryOperationHint() { |
66 | FeedbackNexus nexus(feedback_vector(), slot_); |
67 | return nexus.GetBinaryOperationFeedback(); |
68 | } |
69 | |
70 | CompareOperationHint GetCompareOperationHint() { |
71 | FeedbackNexus nexus(feedback_vector(), slot_); |
72 | return nexus.GetCompareOperationFeedback(); |
73 | } |
74 | |
75 | bool GetBinaryNumberOperationHint(NumberOperationHint* hint) { |
76 | return BinaryOperationHintToNumberOperationHint(GetBinaryOperationHint(), |
77 | hint); |
78 | } |
79 | |
80 | bool GetCompareNumberOperationHint(NumberOperationHint* hint) { |
81 | switch (GetCompareOperationHint()) { |
82 | case CompareOperationHint::kSignedSmall: |
83 | *hint = NumberOperationHint::kSignedSmall; |
84 | return true; |
85 | case CompareOperationHint::kNumber: |
86 | *hint = NumberOperationHint::kNumber; |
87 | return true; |
88 | case CompareOperationHint::kNumberOrOddball: |
89 | *hint = NumberOperationHint::kNumberOrOddball; |
90 | return true; |
91 | case CompareOperationHint::kAny: |
92 | case CompareOperationHint::kNone: |
93 | case CompareOperationHint::kString: |
94 | case CompareOperationHint::kSymbol: |
95 | case CompareOperationHint::kBigInt: |
96 | case CompareOperationHint::kReceiver: |
97 | case CompareOperationHint::kReceiverOrNullOrUndefined: |
98 | case CompareOperationHint::kInternalizedString: |
99 | break; |
100 | } |
101 | return false; |
102 | } |
103 | |
104 | const Operator* SpeculativeNumberOp(NumberOperationHint hint) { |
105 | switch (op_->opcode()) { |
106 | case IrOpcode::kJSAdd: |
107 | if (hint == NumberOperationHint::kSignedSmall || |
108 | hint == NumberOperationHint::kSigned32) { |
109 | return simplified()->SpeculativeSafeIntegerAdd(hint); |
110 | } else { |
111 | return simplified()->SpeculativeNumberAdd(hint); |
112 | } |
113 | case IrOpcode::kJSSubtract: |
114 | if (hint == NumberOperationHint::kSignedSmall || |
115 | hint == NumberOperationHint::kSigned32) { |
116 | return simplified()->SpeculativeSafeIntegerSubtract(hint); |
117 | } else { |
118 | return simplified()->SpeculativeNumberSubtract(hint); |
119 | } |
120 | case IrOpcode::kJSMultiply: |
121 | return simplified()->SpeculativeNumberMultiply(hint); |
122 | case IrOpcode::kJSDivide: |
123 | return simplified()->SpeculativeNumberDivide(hint); |
124 | case IrOpcode::kJSModulus: |
125 | return simplified()->SpeculativeNumberModulus(hint); |
126 | case IrOpcode::kJSBitwiseAnd: |
127 | return simplified()->SpeculativeNumberBitwiseAnd(hint); |
128 | case IrOpcode::kJSBitwiseOr: |
129 | return simplified()->SpeculativeNumberBitwiseOr(hint); |
130 | case IrOpcode::kJSBitwiseXor: |
131 | return simplified()->SpeculativeNumberBitwiseXor(hint); |
132 | case IrOpcode::kJSShiftLeft: |
133 | return simplified()->SpeculativeNumberShiftLeft(hint); |
134 | case IrOpcode::kJSShiftRight: |
135 | return simplified()->SpeculativeNumberShiftRight(hint); |
136 | case IrOpcode::kJSShiftRightLogical: |
137 | return simplified()->SpeculativeNumberShiftRightLogical(hint); |
138 | default: |
139 | break; |
140 | } |
141 | UNREACHABLE(); |
142 | } |
143 | |
144 | const Operator* SpeculativeCompareOp(NumberOperationHint hint) { |
145 | switch (op_->opcode()) { |
146 | case IrOpcode::kJSEqual: |
147 | return simplified()->SpeculativeNumberEqual(hint); |
148 | case IrOpcode::kJSLessThan: |
149 | return simplified()->SpeculativeNumberLessThan(hint); |
150 | case IrOpcode::kJSGreaterThan: |
151 | std::swap(left_, right_); // a > b => b < a |
152 | return simplified()->SpeculativeNumberLessThan(hint); |
153 | case IrOpcode::kJSLessThanOrEqual: |
154 | return simplified()->SpeculativeNumberLessThanOrEqual(hint); |
155 | case IrOpcode::kJSGreaterThanOrEqual: |
156 | std::swap(left_, right_); // a >= b => b <= a |
157 | return simplified()->SpeculativeNumberLessThanOrEqual(hint); |
158 | default: |
159 | break; |
160 | } |
161 | UNREACHABLE(); |
162 | } |
163 | |
164 | Node* BuildSpeculativeOperation(const Operator* op) { |
165 | DCHECK_EQ(2, op->ValueInputCount()); |
166 | DCHECK_EQ(1, op->EffectInputCount()); |
167 | DCHECK_EQ(1, op->ControlInputCount()); |
168 | DCHECK_EQ(false, OperatorProperties::HasFrameStateInput(op)); |
169 | DCHECK_EQ(false, OperatorProperties::HasContextInput(op)); |
170 | DCHECK_EQ(1, op->EffectOutputCount()); |
171 | DCHECK_EQ(0, op->ControlOutputCount()); |
172 | return graph()->NewNode(op, left_, right_, effect_, control_); |
173 | } |
174 | |
175 | Node* TryBuildNumberBinop() { |
176 | NumberOperationHint hint; |
177 | if (GetBinaryNumberOperationHint(&hint)) { |
178 | const Operator* op = SpeculativeNumberOp(hint); |
179 | Node* node = BuildSpeculativeOperation(op); |
180 | return node; |
181 | } |
182 | return nullptr; |
183 | } |
184 | |
185 | Node* TryBuildNumberCompare() { |
186 | NumberOperationHint hint; |
187 | if (GetCompareNumberOperationHint(&hint)) { |
188 | const Operator* op = SpeculativeCompareOp(hint); |
189 | Node* node = BuildSpeculativeOperation(op); |
190 | return node; |
191 | } |
192 | return nullptr; |
193 | } |
194 | |
195 | JSGraph* jsgraph() const { return lowering_->jsgraph(); } |
196 | Isolate* isolate() const { return jsgraph()->isolate(); } |
197 | Graph* graph() const { return jsgraph()->graph(); } |
198 | JSOperatorBuilder* javascript() { return jsgraph()->javascript(); } |
199 | SimplifiedOperatorBuilder* simplified() { return jsgraph()->simplified(); } |
200 | CommonOperatorBuilder* common() { return jsgraph()->common(); } |
201 | const Handle<FeedbackVector>& feedback_vector() const { |
202 | return lowering_->feedback_vector(); |
203 | } |
204 | |
205 | private: |
206 | const JSTypeHintLowering* lowering_; |
207 | const Operator* op_; |
208 | Node* left_; |
209 | Node* right_; |
210 | Node* effect_; |
211 | Node* control_; |
212 | FeedbackSlot slot_; |
213 | }; |
214 | |
215 | JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph, |
216 | Handle<FeedbackVector> feedback_vector, |
217 | Flags flags) |
218 | : jsgraph_(jsgraph), flags_(flags), feedback_vector_(feedback_vector) {} |
219 | |
220 | Isolate* JSTypeHintLowering::isolate() const { return jsgraph()->isolate(); } |
221 | |
222 | JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceUnaryOperation( |
223 | const Operator* op, Node* operand, Node* effect, Node* control, |
224 | FeedbackSlot slot) const { |
225 | DCHECK(!slot.IsInvalid()); |
226 | FeedbackNexus nexus(feedback_vector(), slot); |
227 | if (Node* node = TryBuildSoftDeopt( |
228 | nexus, effect, control, |
229 | DeoptimizeReason::kInsufficientTypeFeedbackForUnaryOperation)) { |
230 | return LoweringResult::Exit(node); |
231 | } |
232 | |
233 | Node* node; |
234 | switch (op->opcode()) { |
235 | case IrOpcode::kJSBitwiseNot: { |
236 | // Lower to a speculative xor with -1 if we have some kind of Number |
237 | // feedback. |
238 | JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->BitwiseXor(), |
239 | operand, jsgraph()->SmiConstant(-1), effect, |
240 | control, slot); |
241 | node = b.TryBuildNumberBinop(); |
242 | break; |
243 | } |
244 | case IrOpcode::kJSDecrement: { |
245 | // Lower to a speculative subtraction of 1 if we have some kind of Number |
246 | // feedback. |
247 | JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->Subtract(), |
248 | operand, jsgraph()->SmiConstant(1), effect, |
249 | control, slot); |
250 | node = b.TryBuildNumberBinop(); |
251 | break; |
252 | } |
253 | case IrOpcode::kJSIncrement: { |
254 | // Lower to a speculative addition of 1 if we have some kind of Number |
255 | // feedback. |
256 | BinaryOperationHint hint = BinaryOperationHint::kAny; // Dummy. |
257 | JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->Add(hint), |
258 | operand, jsgraph()->SmiConstant(1), effect, |
259 | control, slot); |
260 | node = b.TryBuildNumberBinop(); |
261 | break; |
262 | } |
263 | case IrOpcode::kJSNegate: { |
264 | // Lower to a speculative multiplication with -1 if we have some kind of |
265 | // Number feedback. |
266 | JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->Multiply(), |
267 | operand, jsgraph()->SmiConstant(-1), effect, |
268 | control, slot); |
269 | node = b.TryBuildNumberBinop(); |
270 | break; |
271 | } |
272 | default: |
273 | UNREACHABLE(); |
274 | break; |
275 | } |
276 | |
277 | if (node != nullptr) { |
278 | return LoweringResult::SideEffectFree(node, node, control); |
279 | } else { |
280 | return LoweringResult::NoChange(); |
281 | } |
282 | } |
283 | |
284 | JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation( |
285 | const Operator* op, Node* left, Node* right, Node* effect, Node* control, |
286 | FeedbackSlot slot) const { |
287 | switch (op->opcode()) { |
288 | case IrOpcode::kJSStrictEqual: { |
289 | DCHECK(!slot.IsInvalid()); |
290 | FeedbackNexus nexus(feedback_vector(), slot); |
291 | if (Node* node = TryBuildSoftDeopt( |
292 | nexus, effect, control, |
293 | DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) { |
294 | return LoweringResult::Exit(node); |
295 | } |
296 | // TODO(turbofan): Should we generally support early lowering of |
297 | // JSStrictEqual operators here? |
298 | break; |
299 | } |
300 | case IrOpcode::kJSEqual: |
301 | case IrOpcode::kJSLessThan: |
302 | case IrOpcode::kJSGreaterThan: |
303 | case IrOpcode::kJSLessThanOrEqual: |
304 | case IrOpcode::kJSGreaterThanOrEqual: { |
305 | DCHECK(!slot.IsInvalid()); |
306 | FeedbackNexus nexus(feedback_vector(), slot); |
307 | if (Node* node = TryBuildSoftDeopt( |
308 | nexus, effect, control, |
309 | DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) { |
310 | return LoweringResult::Exit(node); |
311 | } |
312 | JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot); |
313 | if (Node* node = b.TryBuildNumberCompare()) { |
314 | return LoweringResult::SideEffectFree(node, node, control); |
315 | } |
316 | break; |
317 | } |
318 | case IrOpcode::kJSInstanceOf: { |
319 | DCHECK(!slot.IsInvalid()); |
320 | FeedbackNexus nexus(feedback_vector(), slot); |
321 | if (Node* node = TryBuildSoftDeopt( |
322 | nexus, effect, control, |
323 | DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) { |
324 | return LoweringResult::Exit(node); |
325 | } |
326 | // TODO(turbofan): Should we generally support early lowering of |
327 | // JSInstanceOf operators here? |
328 | break; |
329 | } |
330 | case IrOpcode::kJSBitwiseOr: |
331 | case IrOpcode::kJSBitwiseXor: |
332 | case IrOpcode::kJSBitwiseAnd: |
333 | case IrOpcode::kJSShiftLeft: |
334 | case IrOpcode::kJSShiftRight: |
335 | case IrOpcode::kJSShiftRightLogical: |
336 | case IrOpcode::kJSAdd: |
337 | case IrOpcode::kJSSubtract: |
338 | case IrOpcode::kJSMultiply: |
339 | case IrOpcode::kJSDivide: |
340 | case IrOpcode::kJSModulus: { |
341 | DCHECK(!slot.IsInvalid()); |
342 | FeedbackNexus nexus(feedback_vector(), slot); |
343 | if (Node* node = TryBuildSoftDeopt( |
344 | nexus, effect, control, |
345 | DeoptimizeReason::kInsufficientTypeFeedbackForBinaryOperation)) { |
346 | return LoweringResult::Exit(node); |
347 | } |
348 | JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot); |
349 | if (Node* node = b.TryBuildNumberBinop()) { |
350 | return LoweringResult::SideEffectFree(node, node, control); |
351 | } |
352 | break; |
353 | } |
354 | case IrOpcode::kJSExponentiate: { |
355 | // TODO(neis): Introduce a SpeculativeNumberPow operator? |
356 | break; |
357 | } |
358 | default: |
359 | UNREACHABLE(); |
360 | break; |
361 | } |
362 | return LoweringResult::NoChange(); |
363 | } |
364 | |
365 | JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceForInNextOperation( |
366 | Node* receiver, Node* cache_array, Node* cache_type, Node* index, |
367 | Node* effect, Node* control, FeedbackSlot slot) const { |
368 | DCHECK(!slot.IsInvalid()); |
369 | FeedbackNexus nexus(feedback_vector(), slot); |
370 | if (Node* node = TryBuildSoftDeopt( |
371 | nexus, effect, control, |
372 | DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) { |
373 | return LoweringResult::Exit(node); |
374 | } |
375 | return LoweringResult::NoChange(); |
376 | } |
377 | |
378 | JSTypeHintLowering::LoweringResult |
379 | JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect, |
380 | Node* control, |
381 | FeedbackSlot slot) const { |
382 | DCHECK(!slot.IsInvalid()); |
383 | FeedbackNexus nexus(feedback_vector(), slot); |
384 | if (Node* node = TryBuildSoftDeopt( |
385 | nexus, effect, control, |
386 | DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) { |
387 | return LoweringResult::Exit(node); |
388 | } |
389 | return LoweringResult::NoChange(); |
390 | } |
391 | |
392 | JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceToNumberOperation( |
393 | Node* input, Node* effect, Node* control, FeedbackSlot slot) const { |
394 | DCHECK(!slot.IsInvalid()); |
395 | FeedbackNexus nexus(feedback_vector(), slot); |
396 | NumberOperationHint hint; |
397 | if (BinaryOperationHintToNumberOperationHint( |
398 | nexus.GetBinaryOperationFeedback(), &hint)) { |
399 | Node* node = jsgraph()->graph()->NewNode( |
400 | jsgraph()->simplified()->SpeculativeToNumber(hint, VectorSlotPair()), |
401 | input, effect, control); |
402 | return LoweringResult::SideEffectFree(node, node, control); |
403 | } |
404 | return LoweringResult::NoChange(); |
405 | } |
406 | |
407 | JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceCallOperation( |
408 | const Operator* op, Node* const* args, int arg_count, Node* effect, |
409 | Node* control, FeedbackSlot slot) const { |
410 | DCHECK(op->opcode() == IrOpcode::kJSCall || |
411 | op->opcode() == IrOpcode::kJSCallWithSpread); |
412 | DCHECK(!slot.IsInvalid()); |
413 | FeedbackNexus nexus(feedback_vector(), slot); |
414 | if (Node* node = TryBuildSoftDeopt( |
415 | nexus, effect, control, |
416 | DeoptimizeReason::kInsufficientTypeFeedbackForCall)) { |
417 | return LoweringResult::Exit(node); |
418 | } |
419 | return LoweringResult::NoChange(); |
420 | } |
421 | |
422 | JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceConstructOperation( |
423 | const Operator* op, Node* const* args, int arg_count, Node* effect, |
424 | Node* control, FeedbackSlot slot) const { |
425 | DCHECK(op->opcode() == IrOpcode::kJSConstruct || |
426 | op->opcode() == IrOpcode::kJSConstructWithSpread); |
427 | DCHECK(!slot.IsInvalid()); |
428 | FeedbackNexus nexus(feedback_vector(), slot); |
429 | if (Node* node = TryBuildSoftDeopt( |
430 | nexus, effect, control, |
431 | DeoptimizeReason::kInsufficientTypeFeedbackForConstruct)) { |
432 | return LoweringResult::Exit(node); |
433 | } |
434 | return LoweringResult::NoChange(); |
435 | } |
436 | |
437 | JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadNamedOperation( |
438 | const Operator* op, Node* receiver, Node* effect, Node* control, |
439 | FeedbackSlot slot) const { |
440 | DCHECK_EQ(IrOpcode::kJSLoadNamed, op->opcode()); |
441 | DCHECK(!slot.IsInvalid()); |
442 | FeedbackNexus nexus(feedback_vector(), slot); |
443 | if (Node* node = TryBuildSoftDeopt( |
444 | nexus, effect, control, |
445 | DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) { |
446 | return LoweringResult::Exit(node); |
447 | } |
448 | return LoweringResult::NoChange(); |
449 | } |
450 | |
451 | JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadKeyedOperation( |
452 | const Operator* op, Node* obj, Node* key, Node* effect, Node* control, |
453 | FeedbackSlot slot) const { |
454 | DCHECK_EQ(IrOpcode::kJSLoadProperty, op->opcode()); |
455 | DCHECK(!slot.IsInvalid()); |
456 | FeedbackNexus nexus(feedback_vector(), slot); |
457 | if (Node* node = TryBuildSoftDeopt( |
458 | nexus, effect, control, |
459 | DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) { |
460 | return LoweringResult::Exit(node); |
461 | } |
462 | return LoweringResult::NoChange(); |
463 | } |
464 | |
465 | JSTypeHintLowering::LoweringResult |
466 | JSTypeHintLowering::ReduceStoreNamedOperation(const Operator* op, Node* obj, |
467 | Node* val, Node* effect, |
468 | Node* control, |
469 | FeedbackSlot slot) const { |
470 | DCHECK(op->opcode() == IrOpcode::kJSStoreNamed || |
471 | op->opcode() == IrOpcode::kJSStoreNamedOwn); |
472 | DCHECK(!slot.IsInvalid()); |
473 | FeedbackNexus nexus(feedback_vector(), slot); |
474 | if (Node* node = TryBuildSoftDeopt( |
475 | nexus, effect, control, |
476 | DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) { |
477 | return LoweringResult::Exit(node); |
478 | } |
479 | return LoweringResult::NoChange(); |
480 | } |
481 | |
482 | JSTypeHintLowering::LoweringResult |
483 | JSTypeHintLowering::ReduceStoreKeyedOperation(const Operator* op, Node* obj, |
484 | Node* key, Node* val, |
485 | Node* effect, Node* control, |
486 | FeedbackSlot slot) const { |
487 | DCHECK(op->opcode() == IrOpcode::kJSStoreProperty || |
488 | op->opcode() == IrOpcode::kJSStoreInArrayLiteral); |
489 | DCHECK(!slot.IsInvalid()); |
490 | FeedbackNexus nexus(feedback_vector(), slot); |
491 | if (Node* node = TryBuildSoftDeopt( |
492 | nexus, effect, control, |
493 | DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) { |
494 | return LoweringResult::Exit(node); |
495 | } |
496 | return LoweringResult::NoChange(); |
497 | } |
498 | |
499 | Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus& nexus, Node* effect, |
500 | Node* control, |
501 | DeoptimizeReason reason) const { |
502 | if ((flags() & kBailoutOnUninitialized) && nexus.IsUninitialized()) { |
503 | Node* deoptimize = jsgraph()->graph()->NewNode( |
504 | jsgraph()->common()->Deoptimize(DeoptimizeKind::kSoft, reason, |
505 | VectorSlotPair()), |
506 | jsgraph()->Dead(), effect, control); |
507 | Node* frame_state = NodeProperties::FindFrameStateBefore(deoptimize); |
508 | deoptimize->ReplaceInput(0, frame_state); |
509 | return deoptimize; |
510 | } |
511 | return nullptr; |
512 | } |
513 | |
514 | } // namespace compiler |
515 | } // namespace internal |
516 | } // namespace v8 |
517 | |