1 // 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.
5 #include "src/compiler/js-intrinsic-lowering.h"
9 #include "src/compiler/access-builder.h"
10 #include "src/compiler/js-graph.h"
11 #include "src/compiler/node-matchers.h"
12 #include "src/compiler/node-properties.h"
13 #include "src/compiler/operator-properties.h"
19 JSIntrinsicLowering::JSIntrinsicLowering(Editor* editor, JSGraph* jsgraph,
20 DeoptimizationMode mode)
21 : AdvancedReducer(editor),
24 simplified_(jsgraph->zone()) {}
27 Reduction JSIntrinsicLowering::Reduce(Node* node) {
28 if (node->opcode() != IrOpcode::kJSCallRuntime) return NoChange();
29 const Runtime::Function* const f =
30 Runtime::FunctionForId(CallRuntimeParametersOf(node->op()).id());
31 if (f->intrinsic_type != Runtime::IntrinsicType::INLINE) return NoChange();
32 switch (f->function_id) {
33 case Runtime::kInlineConstructDouble:
34 return ReduceConstructDouble(node);
35 case Runtime::kInlineDateField:
36 return ReduceDateField(node);
37 case Runtime::kInlineDeoptimizeNow:
38 return ReduceDeoptimizeNow(node);
39 case Runtime::kInlineDoubleHi:
40 return ReduceDoubleHi(node);
41 case Runtime::kInlineDoubleLo:
42 return ReduceDoubleLo(node);
43 case Runtime::kInlineHeapObjectGetMap:
44 return ReduceHeapObjectGetMap(node);
45 case Runtime::kInlineIncrementStatsCounter:
46 return ReduceIncrementStatsCounter(node);
47 case Runtime::kInlineIsArray:
48 return ReduceIsInstanceType(node, JS_ARRAY_TYPE);
49 case Runtime::kInlineIsDate:
50 return ReduceIsInstanceType(node, JS_DATE_TYPE);
51 case Runtime::kInlineIsTypedArray:
52 return ReduceIsInstanceType(node, JS_TYPED_ARRAY_TYPE);
53 case Runtime::kInlineIsFunction:
54 return ReduceIsInstanceType(node, JS_FUNCTION_TYPE);
55 case Runtime::kInlineIsRegExp:
56 return ReduceIsInstanceType(node, JS_REGEXP_TYPE);
57 case Runtime::kInlineIsSmi:
58 return ReduceIsSmi(node);
59 case Runtime::kInlineJSValueGetValue:
60 return ReduceJSValueGetValue(node);
61 case Runtime::kInlineLikely:
62 return ReduceUnLikely(node, BranchHint::kTrue);
63 case Runtime::kInlineMapGetInstanceType:
64 return ReduceMapGetInstanceType(node);
65 case Runtime::kInlineMathClz32:
66 return ReduceMathClz32(node);
67 case Runtime::kInlineMathFloor:
68 return ReduceMathFloor(node);
69 case Runtime::kInlineMathSqrt:
70 return ReduceMathSqrt(node);
71 case Runtime::kInlineOneByteSeqStringGetChar:
72 return ReduceSeqStringGetChar(node, String::ONE_BYTE_ENCODING);
73 case Runtime::kInlineOneByteSeqStringSetChar:
74 return ReduceSeqStringSetChar(node, String::ONE_BYTE_ENCODING);
75 case Runtime::kInlineStringGetLength:
76 return ReduceStringGetLength(node);
77 case Runtime::kInlineTwoByteSeqStringGetChar:
78 return ReduceSeqStringGetChar(node, String::TWO_BYTE_ENCODING);
79 case Runtime::kInlineTwoByteSeqStringSetChar:
80 return ReduceSeqStringSetChar(node, String::TWO_BYTE_ENCODING);
81 case Runtime::kInlineUnlikely:
82 return ReduceUnLikely(node, BranchHint::kFalse);
83 case Runtime::kInlineValueOf:
84 return ReduceValueOf(node);
85 case Runtime::kInlineIsMinusZero:
86 return ReduceIsMinusZero(node);
87 case Runtime::kInlineFixedArrayGet:
88 return ReduceFixedArrayGet(node);
89 case Runtime::kInlineFixedArraySet:
90 return ReduceFixedArraySet(node);
91 case Runtime::kInlineGetTypeFeedbackVector:
92 return ReduceGetTypeFeedbackVector(node);
93 case Runtime::kInlineGetCallerJSFunction:
94 return ReduceGetCallerJSFunction(node);
95 case Runtime::kInlineToObject:
96 return ReduceToObject(node);
97 case Runtime::kInlineThrowNotDateError:
98 return ReduceThrowNotDateError(node);
99 case Runtime::kInlineCallFunction:
100 return ReduceCallFunction(node);
108 Reduction JSIntrinsicLowering::ReduceConstructDouble(Node* node) {
109 Node* high = NodeProperties::GetValueInput(node, 0);
110 Node* low = NodeProperties::GetValueInput(node, 1);
112 graph()->NewNode(machine()->Float64InsertHighWord32(),
113 graph()->NewNode(machine()->Float64InsertLowWord32(),
114 jsgraph()->Constant(0), low),
116 ReplaceWithValue(node, value);
117 return Replace(value);
121 Reduction JSIntrinsicLowering::ReduceDateField(Node* node) {
122 Node* const value = NodeProperties::GetValueInput(node, 0);
123 Node* const index = NodeProperties::GetValueInput(node, 1);
124 Node* const effect = NodeProperties::GetEffectInput(node);
125 Node* const control = NodeProperties::GetControlInput(node);
126 NumberMatcher mindex(index);
127 if (mindex.Is(JSDate::kDateValue)) {
130 simplified()->LoadField(AccessBuilder::ForJSDateField(
131 static_cast<JSDate::FieldIndex>(static_cast<int>(mindex.Value())))),
132 value, effect, control);
134 // TODO(turbofan): Optimize more patterns.
139 Reduction JSIntrinsicLowering::ReduceDeoptimizeNow(Node* node) {
140 if (mode() != kDeoptimizationEnabled) return NoChange();
141 Node* const frame_state = NodeProperties::GetFrameStateInput(node, 0);
142 Node* const effect = NodeProperties::GetEffectInput(node);
143 Node* const control = NodeProperties::GetControlInput(node);
145 // TODO(bmeurer): Move MergeControlToEnd() to the AdvancedReducer.
147 graph()->NewNode(common()->Deoptimize(), frame_state, effect, control);
148 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
150 node->set_op(common()->Dead());
151 node->TrimInputCount(0);
152 return Changed(node);
156 Reduction JSIntrinsicLowering::ReduceDoubleHi(Node* node) {
157 return Change(node, machine()->Float64ExtractHighWord32());
161 Reduction JSIntrinsicLowering::ReduceDoubleLo(Node* node) {
162 return Change(node, machine()->Float64ExtractLowWord32());
166 Reduction JSIntrinsicLowering::ReduceHeapObjectGetMap(Node* node) {
167 Node* value = NodeProperties::GetValueInput(node, 0);
168 Node* effect = NodeProperties::GetEffectInput(node);
169 Node* control = NodeProperties::GetControlInput(node);
170 return Change(node, simplified()->LoadField(AccessBuilder::ForMap()), value,
175 Reduction JSIntrinsicLowering::ReduceIncrementStatsCounter(Node* node) {
176 if (!FLAG_native_code_counters) return ChangeToUndefined(node);
177 HeapObjectMatcher m(NodeProperties::GetValueInput(node, 0));
178 if (!m.HasValue() || !m.Value().handle()->IsString()) {
179 return ChangeToUndefined(node);
181 base::SmartArrayPointer<char> name =
182 Handle<String>::cast(m.Value().handle())->ToCString();
183 StatsCounter counter(jsgraph()->isolate(), name.get());
184 if (!counter.Enabled()) return ChangeToUndefined(node);
186 Node* effect = NodeProperties::GetEffectInput(node);
187 Node* control = NodeProperties::GetControlInput(node);
188 FieldAccess access = AccessBuilder::ForStatsCounter();
189 Node* cnt = jsgraph()->ExternalConstant(ExternalReference(&counter));
191 graph()->NewNode(simplified()->LoadField(access), cnt, effect, control);
193 graph()->NewNode(machine()->Int32Add(), load, jsgraph()->OneConstant());
194 Node* store = graph()->NewNode(simplified()->StoreField(access), cnt, inc,
196 return ChangeToUndefined(node, store);
200 Reduction JSIntrinsicLowering::ReduceIsInstanceType(
201 Node* node, InstanceType instance_type) {
202 // if (%_IsSmi(value)) {
205 // return %_GetInstanceType(%_GetMap(value)) == instance_type;
207 MachineType const type = static_cast<MachineType>(kTypeBool | kRepTagged);
209 Node* value = NodeProperties::GetValueInput(node, 0);
210 Node* effect = NodeProperties::GetEffectInput(node);
211 Node* control = NodeProperties::GetControlInput(node);
213 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
214 Node* branch = graph()->NewNode(common()->Branch(), check, control);
216 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
217 Node* etrue = effect;
218 Node* vtrue = jsgraph()->FalseConstant();
220 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
221 Node* efalse = graph()->NewNode(
222 simplified()->LoadField(AccessBuilder::ForMapInstanceType()),
223 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), value,
226 Node* vfalse = graph()->NewNode(machine()->Word32Equal(), efalse,
227 jsgraph()->Int32Constant(instance_type));
229 Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
231 // Replace all effect uses of {node} with the {ephi}.
232 Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge);
233 ReplaceWithValue(node, node, ephi);
235 // Turn the {node} into a Phi.
236 return Change(node, common()->Phi(type, 2), vtrue, vfalse, merge);
240 Reduction JSIntrinsicLowering::ReduceIsSmi(Node* node) {
241 return Change(node, simplified()->ObjectIsSmi());
245 Reduction JSIntrinsicLowering::ReduceJSValueGetValue(Node* node) {
246 Node* value = NodeProperties::GetValueInput(node, 0);
247 Node* effect = NodeProperties::GetEffectInput(node);
248 Node* control = NodeProperties::GetControlInput(node);
249 return Change(node, simplified()->LoadField(AccessBuilder::ForValue()), value,
254 Reduction JSIntrinsicLowering::ReduceMapGetInstanceType(Node* node) {
255 Node* value = NodeProperties::GetValueInput(node, 0);
256 Node* effect = NodeProperties::GetEffectInput(node);
257 Node* control = NodeProperties::GetControlInput(node);
259 simplified()->LoadField(AccessBuilder::ForMapInstanceType()),
260 value, effect, control);
264 Reduction JSIntrinsicLowering::ReduceMathClz32(Node* node) {
265 return Change(node, machine()->Word32Clz());
269 Reduction JSIntrinsicLowering::ReduceMathFloor(Node* node) {
270 if (!machine()->Float64RoundDown().IsSupported()) return NoChange();
271 return Change(node, machine()->Float64RoundDown().op());
275 Reduction JSIntrinsicLowering::ReduceMathSqrt(Node* node) {
276 return Change(node, machine()->Float64Sqrt());
280 Reduction JSIntrinsicLowering::ReduceSeqStringGetChar(
281 Node* node, String::Encoding encoding) {
282 Node* effect = NodeProperties::GetEffectInput(node);
283 Node* control = NodeProperties::GetControlInput(node);
285 simplified()->LoadElement(AccessBuilder::ForSeqStringChar(encoding)));
286 node->ReplaceInput(2, effect);
287 node->ReplaceInput(3, control);
288 node->TrimInputCount(4);
290 return Changed(node);
294 Reduction JSIntrinsicLowering::ReduceSeqStringSetChar(
295 Node* node, String::Encoding encoding) {
296 // Note: The intrinsic has a strange argument order, so we need to reshuffle.
297 Node* index = NodeProperties::GetValueInput(node, 0);
298 Node* chr = NodeProperties::GetValueInput(node, 1);
299 Node* string = NodeProperties::GetValueInput(node, 2);
300 Node* effect = NodeProperties::GetEffectInput(node);
301 Node* control = NodeProperties::GetControlInput(node);
303 simplified()->StoreElement(AccessBuilder::ForSeqStringChar(encoding)));
304 node->ReplaceInput(0, string);
305 node->ReplaceInput(1, index);
306 node->ReplaceInput(2, chr);
307 node->ReplaceInput(3, effect);
308 node->ReplaceInput(4, control);
309 node->TrimInputCount(5);
310 NodeProperties::RemoveBounds(node);
311 ReplaceWithValue(node, string, node);
312 return Changed(node);
316 Reduction JSIntrinsicLowering::ReduceStringGetLength(Node* node) {
317 Node* value = NodeProperties::GetValueInput(node, 0);
318 Node* effect = NodeProperties::GetEffectInput(node);
319 Node* control = NodeProperties::GetControlInput(node);
320 return Change(node, simplified()->LoadField(
321 AccessBuilder::ForStringLength(graph()->zone())),
322 value, effect, control);
326 Reduction JSIntrinsicLowering::ReduceUnLikely(Node* node, BranchHint hint) {
327 std::stack<Node*> nodes_to_visit;
328 nodes_to_visit.push(node);
329 while (!nodes_to_visit.empty()) {
330 Node* current = nodes_to_visit.top();
331 nodes_to_visit.pop();
332 for (Node* use : current->uses()) {
333 if (use->opcode() == IrOpcode::kJSToBoolean) {
334 // We have to "look through" ToBoolean calls.
335 nodes_to_visit.push(use);
336 } else if (use->opcode() == IrOpcode::kBranch) {
337 // Actually set the hint on any branch using the intrinsic node.
338 use->set_op(common()->Branch(hint));
342 // Apart from adding hints to branchs nodes, this is the identity function.
343 Node* value = NodeProperties::GetValueInput(node, 0);
344 ReplaceWithValue(node, value);
345 return Changed(value);
349 Reduction JSIntrinsicLowering::ReduceValueOf(Node* node) {
350 // if (%_IsSmi(value)) {
352 // } else if (%_GetInstanceType(%_GetMap(value)) == JS_VALUE_TYPE) {
353 // return %_GetValue(value);
357 const Operator* const merge_op = common()->Merge(2);
358 const Operator* const ephi_op = common()->EffectPhi(2);
359 const Operator* const phi_op = common()->Phi(kMachAnyTagged, 2);
361 Node* value = NodeProperties::GetValueInput(node, 0);
362 Node* effect = NodeProperties::GetEffectInput(node);
363 Node* control = NodeProperties::GetControlInput(node);
365 Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value);
366 Node* branch0 = graph()->NewNode(common()->Branch(), check0, control);
368 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
369 Node* etrue0 = effect;
370 Node* vtrue0 = value;
372 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
376 Node* check1 = graph()->NewNode(
377 machine()->Word32Equal(),
379 simplified()->LoadField(AccessBuilder::ForMapInstanceType()),
380 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
381 value, effect, if_false0),
383 jsgraph()->Int32Constant(JS_VALUE_TYPE));
384 Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0);
386 Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
388 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForValue()),
389 value, effect, if_true1);
390 Node* vtrue1 = etrue1;
392 Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
393 Node* efalse1 = effect;
394 Node* vfalse1 = value;
396 Node* merge1 = graph()->NewNode(merge_op, if_true1, if_false1);
397 efalse0 = graph()->NewNode(ephi_op, etrue1, efalse1, merge1);
398 vfalse0 = graph()->NewNode(phi_op, vtrue1, vfalse1, merge1);
401 Node* merge0 = graph()->NewNode(merge_op, if_true0, if_false0);
403 // Replace all effect uses of {node} with the {ephi0}.
404 Node* ephi0 = graph()->NewNode(ephi_op, etrue0, efalse0, merge0);
405 ReplaceWithValue(node, node, ephi0);
407 // Turn the {node} into a Phi.
408 return Change(node, phi_op, vtrue0, vfalse0, merge0);
412 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op) {
413 // Replace all effect uses of {node} with the effect dependency.
414 RelaxEffectsAndControls(node);
415 // Remove the inputs corresponding to context, effect and control.
416 NodeProperties::RemoveNonValueInputs(node);
417 // Finally update the operator to the new one.
419 return Changed(node);
423 Reduction JSIntrinsicLowering::ReduceIsMinusZero(Node* node) {
424 Node* value = NodeProperties::GetValueInput(node, 0);
425 Node* effect = NodeProperties::GetEffectInput(node);
428 graph()->NewNode(machine()->Float64ExtractLowWord32(), value);
429 Node* check1 = graph()->NewNode(machine()->Word32Equal(), double_lo,
430 jsgraph()->ZeroConstant());
433 graph()->NewNode(machine()->Float64ExtractHighWord32(), value);
434 Node* check2 = graph()->NewNode(
435 machine()->Word32Equal(), double_hi,
436 jsgraph()->Int32Constant(static_cast<int32_t>(0x80000000)));
438 ReplaceWithValue(node, node, effect);
440 Node* and_result = graph()->NewNode(machine()->Word32And(), check1, check2);
442 return Change(node, machine()->Word32Equal(), and_result,
443 jsgraph()->Int32Constant(1));
447 Reduction JSIntrinsicLowering::ReduceFixedArrayGet(Node* node) {
448 Node* base = node->InputAt(0);
449 Node* index = node->InputAt(1);
450 Node* effect = NodeProperties::GetEffectInput(node);
451 Node* control = NodeProperties::GetControlInput(node);
453 node, simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
454 base, index, effect, control);
458 Reduction JSIntrinsicLowering::ReduceFixedArraySet(Node* node) {
459 Node* base = node->InputAt(0);
460 Node* index = node->InputAt(1);
461 Node* value = node->InputAt(2);
462 Node* effect = NodeProperties::GetEffectInput(node);
463 Node* control = NodeProperties::GetControlInput(node);
464 Node* store = (graph()->NewNode(
465 simplified()->StoreElement(AccessBuilder::ForFixedArrayElement()), base,
466 index, value, effect, control));
467 ReplaceWithValue(node, value, store);
468 return Changed(store);
472 Reduction JSIntrinsicLowering::ReduceGetTypeFeedbackVector(Node* node) {
473 Node* func = node->InputAt(0);
474 Node* effect = NodeProperties::GetEffectInput(node);
475 Node* control = NodeProperties::GetControlInput(node);
476 FieldAccess access = AccessBuilder::ForJSFunctionSharedFunctionInfo();
478 graph()->NewNode(simplified()->LoadField(access), func, effect, control);
479 access = AccessBuilder::ForSharedFunctionInfoTypeFeedbackVector();
480 return Change(node, simplified()->LoadField(access), load, load, control);
484 Reduction JSIntrinsicLowering::ReduceGetCallerJSFunction(Node* node) {
485 Node* effect = NodeProperties::GetEffectInput(node);
486 Node* control = NodeProperties::GetControlInput(node);
488 Node* const frame_state = NodeProperties::GetFrameStateInput(node, 0);
489 Node* outer_frame = frame_state->InputAt(kFrameStateOuterStateInput);
490 if (outer_frame->opcode() == IrOpcode::kFrameState) {
491 // Use the runtime implementation to throw the appropriate error if the
492 // containing function is inlined.
496 // TODO(danno): This implementation forces intrinsic lowering to happen after
497 // inlining, which is fine for now, but eventually the frame-querying logic
498 // probably should go later, e.g. in instruction selection, so that there is
499 // no phase-ordering dependency.
500 FieldAccess access = AccessBuilder::ForFrameCallerFramePtr();
501 Node* fp = graph()->NewNode(machine()->LoadFramePointer());
503 graph()->NewNode(simplified()->LoadField(access), fp, effect, control);
504 return Change(node, simplified()->LoadField(AccessBuilder::ForFrameMarker()),
505 next_fp, effect, control);
509 Reduction JSIntrinsicLowering::ReduceThrowNotDateError(Node* node) {
510 if (mode() != kDeoptimizationEnabled) return NoChange();
511 Node* const frame_state = NodeProperties::GetFrameStateInput(node, 1);
512 Node* const effect = NodeProperties::GetEffectInput(node);
513 Node* const control = NodeProperties::GetControlInput(node);
515 // TODO(bmeurer): Move MergeControlToEnd() to the AdvancedReducer.
517 graph()->NewNode(common()->Deoptimize(), frame_state, effect, control);
518 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
520 node->set_op(common()->Dead());
521 node->TrimInputCount(0);
522 return Changed(node);
526 Reduction JSIntrinsicLowering::ReduceToObject(Node* node) {
527 node->set_op(javascript()->ToObject());
528 return Changed(node);
532 Reduction JSIntrinsicLowering::ReduceCallFunction(Node* node) {
533 CallRuntimeParameters params = OpParameter<CallRuntimeParameters>(node->op());
534 size_t arity = params.arity();
535 node->set_op(javascript()->CallFunction(arity, NO_CALL_FUNCTION_FLAGS, STRICT,
536 VectorSlotPair(), ALLOW_TAIL_CALLS));
537 Node* function = node->InputAt(static_cast<int>(arity - 1));
538 while (--arity != 0) {
539 node->ReplaceInput(static_cast<int>(arity),
540 node->InputAt(static_cast<int>(arity - 1)));
542 node->ReplaceInput(0, function);
543 return Changed(node);
547 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
550 node->ReplaceInput(0, a);
551 node->ReplaceInput(1, b);
552 node->TrimInputCount(2);
554 return Changed(node);
558 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
561 node->ReplaceInput(0, a);
562 node->ReplaceInput(1, b);
563 node->ReplaceInput(2, c);
564 node->TrimInputCount(3);
566 return Changed(node);
570 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
571 Node* b, Node* c, Node* d) {
573 node->ReplaceInput(0, a);
574 node->ReplaceInput(1, b);
575 node->ReplaceInput(2, c);
576 node->ReplaceInput(3, d);
577 node->TrimInputCount(4);
579 return Changed(node);
583 Reduction JSIntrinsicLowering::ChangeToUndefined(Node* node, Node* effect) {
584 ReplaceWithValue(node, jsgraph()->UndefinedConstant(), effect);
585 return Changed(node);
589 Graph* JSIntrinsicLowering::graph() const { return jsgraph()->graph(); }
592 CommonOperatorBuilder* JSIntrinsicLowering::common() const {
593 return jsgraph()->common();
596 JSOperatorBuilder* JSIntrinsicLowering::javascript() const {
597 return jsgraph_->javascript();
601 MachineOperatorBuilder* JSIntrinsicLowering::machine() const {
602 return jsgraph()->machine();
605 } // namespace compiler
606 } // namespace internal