4720c582ec6cf4208e34c354b9f22300594af6ed
[platform/upstream/nodejs.git] / deps / v8 / src / compiler / js-generic-lowering.cc
1 // Copyright 2014 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/code-factory.h"
6 #include "src/code-stubs.h"
7 #include "src/compiler/common-operator.h"
8 #include "src/compiler/js-generic-lowering.h"
9 #include "src/compiler/machine-operator.h"
10 #include "src/compiler/node-matchers.h"
11 #include "src/compiler/node-properties.h"
12 #include "src/compiler/operator-properties.h"
13 #include "src/unique.h"
14
15 namespace v8 {
16 namespace internal {
17 namespace compiler {
18
19 JSGenericLowering::JSGenericLowering(bool is_typing_enabled, JSGraph* jsgraph)
20     : is_typing_enabled_(is_typing_enabled), jsgraph_(jsgraph) {}
21
22
23 Reduction JSGenericLowering::Reduce(Node* node) {
24   switch (node->opcode()) {
25 #define DECLARE_CASE(x)  \
26     case IrOpcode::k##x: \
27       Lower##x(node);    \
28       break;
29     JS_OP_LIST(DECLARE_CASE)
30 #undef DECLARE_CASE
31     case IrOpcode::kBranch:
32       // TODO(mstarzinger): If typing is enabled then simplified lowering will
33       // have inserted the correct ChangeBoolToBit, otherwise we need to perform
34       // poor-man's representation inference here and insert manual change.
35       if (!is_typing_enabled_) {
36         Node* condition = node->InputAt(0);
37         if (condition->opcode() != IrOpcode::kAlways) {
38           Node* test = graph()->NewNode(machine()->WordEqual(), condition,
39                                         jsgraph()->TrueConstant());
40           node->ReplaceInput(0, test);
41         }
42         break;
43       }
44       // Fall-through.
45     default:
46       // Nothing to see.
47       return NoChange();
48   }
49   return Changed(node);
50 }
51
52
53 #define REPLACE_BINARY_OP_IC_CALL(op, token)                             \
54   void JSGenericLowering::Lower##op(Node* node) {                        \
55     ReplaceWithStubCall(node, CodeFactory::BinaryOpIC(isolate(), token), \
56                         CallDescriptor::kPatchableCallSiteWithNop);      \
57   }
58 REPLACE_BINARY_OP_IC_CALL(JSBitwiseOr, Token::BIT_OR)
59 REPLACE_BINARY_OP_IC_CALL(JSBitwiseXor, Token::BIT_XOR)
60 REPLACE_BINARY_OP_IC_CALL(JSBitwiseAnd, Token::BIT_AND)
61 REPLACE_BINARY_OP_IC_CALL(JSShiftLeft, Token::SHL)
62 REPLACE_BINARY_OP_IC_CALL(JSShiftRight, Token::SAR)
63 REPLACE_BINARY_OP_IC_CALL(JSShiftRightLogical, Token::SHR)
64 REPLACE_BINARY_OP_IC_CALL(JSAdd, Token::ADD)
65 REPLACE_BINARY_OP_IC_CALL(JSSubtract, Token::SUB)
66 REPLACE_BINARY_OP_IC_CALL(JSMultiply, Token::MUL)
67 REPLACE_BINARY_OP_IC_CALL(JSDivide, Token::DIV)
68 REPLACE_BINARY_OP_IC_CALL(JSModulus, Token::MOD)
69 #undef REPLACE_BINARY_OP_IC_CALL
70
71
72 #define REPLACE_COMPARE_IC_CALL(op, token)        \
73   void JSGenericLowering::Lower##op(Node* node) { \
74     ReplaceWithCompareIC(node, token);            \
75   }
76 REPLACE_COMPARE_IC_CALL(JSEqual, Token::EQ)
77 REPLACE_COMPARE_IC_CALL(JSNotEqual, Token::NE)
78 REPLACE_COMPARE_IC_CALL(JSStrictEqual, Token::EQ_STRICT)
79 REPLACE_COMPARE_IC_CALL(JSStrictNotEqual, Token::NE_STRICT)
80 REPLACE_COMPARE_IC_CALL(JSLessThan, Token::LT)
81 REPLACE_COMPARE_IC_CALL(JSGreaterThan, Token::GT)
82 REPLACE_COMPARE_IC_CALL(JSLessThanOrEqual, Token::LTE)
83 REPLACE_COMPARE_IC_CALL(JSGreaterThanOrEqual, Token::GTE)
84 #undef REPLACE_COMPARE_IC_CALL
85
86
87 #define REPLACE_RUNTIME_CALL(op, fun)             \
88   void JSGenericLowering::Lower##op(Node* node) { \
89     ReplaceWithRuntimeCall(node, fun);            \
90   }
91 REPLACE_RUNTIME_CALL(JSTypeOf, Runtime::kTypeof)
92 REPLACE_RUNTIME_CALL(JSCreate, Runtime::kAbort)
93 REPLACE_RUNTIME_CALL(JSCreateFunctionContext, Runtime::kNewFunctionContext)
94 REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext)
95 REPLACE_RUNTIME_CALL(JSCreateBlockContext, Runtime::kPushBlockContext)
96 REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext)
97 REPLACE_RUNTIME_CALL(JSCreateScriptContext, Runtime::kAbort)
98 #undef REPLACE_RUNTIME
99
100
101 #define REPLACE_UNIMPLEMENTED(op) \
102   void JSGenericLowering::Lower##op(Node* node) { UNIMPLEMENTED(); }
103 REPLACE_UNIMPLEMENTED(JSYield)
104 REPLACE_UNIMPLEMENTED(JSDebugger)
105 #undef REPLACE_UNIMPLEMENTED
106
107
108 static CallDescriptor::Flags FlagsForNode(Node* node) {
109   CallDescriptor::Flags result = CallDescriptor::kNoFlags;
110   if (OperatorProperties::HasFrameStateInput(node->op())) {
111     result |= CallDescriptor::kNeedsFrameState;
112   }
113   return result;
114 }
115
116
117 void JSGenericLowering::ReplaceWithCompareIC(Node* node, Token::Value token) {
118   Callable callable = CodeFactory::CompareIC(isolate(), token);
119   CallDescriptor* desc_compare = Linkage::GetStubCallDescriptor(
120       isolate(), zone(), callable.descriptor(), 0,
121       CallDescriptor::kPatchableCallSiteWithNop | FlagsForNode(node));
122
123   // Create a new call node asking a CompareIC for help.
124   NodeVector inputs(zone());
125   inputs.reserve(node->InputCount() + 1);
126   inputs.push_back(jsgraph()->HeapConstant(callable.code()));
127   inputs.push_back(NodeProperties::GetValueInput(node, 0));
128   inputs.push_back(NodeProperties::GetValueInput(node, 1));
129   inputs.push_back(NodeProperties::GetContextInput(node));
130   if (node->op()->HasProperty(Operator::kPure)) {
131     // A pure (strict) comparison doesn't have an effect, control or frame
132     // state.  But for the graph, we need to add control and effect inputs.
133     DCHECK(!OperatorProperties::HasFrameStateInput(node->op()));
134     inputs.push_back(graph()->start());
135     inputs.push_back(graph()->start());
136   } else {
137     DCHECK(OperatorProperties::HasFrameStateInput(node->op()) ==
138            FLAG_turbo_deoptimization);
139     if (FLAG_turbo_deoptimization) {
140       inputs.push_back(NodeProperties::GetFrameStateInput(node));
141     }
142     inputs.push_back(NodeProperties::GetEffectInput(node));
143     inputs.push_back(NodeProperties::GetControlInput(node));
144   }
145   Node* compare =
146       graph()->NewNode(common()->Call(desc_compare),
147                        static_cast<int>(inputs.size()), &inputs.front());
148   NodeProperties::SetBounds(
149       compare, Bounds(Type::None(zone()), Type::UntaggedSigned(zone())));
150
151   // Decide how the return value from the above CompareIC can be converted into
152   // a JavaScript boolean oddball depending on the given token.
153   Node* false_value = jsgraph()->FalseConstant();
154   Node* true_value = jsgraph()->TrueConstant();
155   const Operator* op = nullptr;
156   switch (token) {
157     case Token::EQ:  // a == 0
158     case Token::EQ_STRICT:
159       op = machine()->WordEqual();
160       break;
161     case Token::NE:  // a != 0 becomes !(a == 0)
162     case Token::NE_STRICT:
163       op = machine()->WordEqual();
164       std::swap(true_value, false_value);
165       break;
166     case Token::LT:  // a < 0
167       op = machine()->IntLessThan();
168       break;
169     case Token::GT:  // a > 0 becomes !(a <= 0)
170       op = machine()->IntLessThanOrEqual();
171       std::swap(true_value, false_value);
172       break;
173     case Token::LTE:  // a <= 0
174       op = machine()->IntLessThanOrEqual();
175       break;
176     case Token::GTE:  // a >= 0 becomes !(a < 0)
177       op = machine()->IntLessThan();
178       std::swap(true_value, false_value);
179       break;
180     default:
181       UNREACHABLE();
182   }
183   Node* booleanize = graph()->NewNode(op, compare, jsgraph()->ZeroConstant());
184
185   // Finally patch the original node to select a boolean.
186   NodeProperties::ReplaceWithValue(node, node, compare);
187   // TODO(mstarzinger): Just a work-around because SelectLowering might
188   // otherwise introduce a Phi without any uses, making Scheduler unhappy.
189   if (node->UseCount() == 0) return;
190   node->TrimInputCount(3);
191   node->ReplaceInput(0, booleanize);
192   node->ReplaceInput(1, true_value);
193   node->ReplaceInput(2, false_value);
194   node->set_op(common()->Select(kMachAnyTagged));
195 }
196
197
198 void JSGenericLowering::ReplaceWithStubCall(Node* node, Callable callable,
199                                             CallDescriptor::Flags flags) {
200   Operator::Properties properties = node->op()->properties();
201   CallDescriptor* desc =
202       Linkage::GetStubCallDescriptor(isolate(), zone(), callable.descriptor(),
203                                      0, flags | FlagsForNode(node), properties);
204   Node* stub_code = jsgraph()->HeapConstant(callable.code());
205   node->InsertInput(zone(), 0, stub_code);
206   node->set_op(common()->Call(desc));
207 }
208
209
210 void JSGenericLowering::ReplaceWithBuiltinCall(Node* node,
211                                                Builtins::JavaScript id,
212                                                int nargs) {
213   Operator::Properties properties = node->op()->properties();
214   Callable callable =
215       CodeFactory::CallFunction(isolate(), nargs - 1, NO_CALL_FUNCTION_FLAGS);
216   CallDescriptor* desc =
217       Linkage::GetStubCallDescriptor(isolate(), zone(), callable.descriptor(),
218                                      nargs, FlagsForNode(node), properties);
219   Node* global_object = graph()->NewNode(
220       machine()->Load(kMachAnyTagged), NodeProperties::GetContextInput(node),
221       jsgraph()->IntPtrConstant(
222           Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)),
223       NodeProperties::GetEffectInput(node), graph()->start());
224   Node* builtins_object = graph()->NewNode(
225       machine()->Load(kMachAnyTagged), global_object,
226       jsgraph()->IntPtrConstant(GlobalObject::kBuiltinsOffset - kHeapObjectTag),
227       NodeProperties::GetEffectInput(node), graph()->start());
228   Node* function = graph()->NewNode(
229       machine()->Load(kMachAnyTagged), builtins_object,
230       jsgraph()->IntPtrConstant(JSBuiltinsObject::OffsetOfFunctionWithId(id) -
231                                 kHeapObjectTag),
232       NodeProperties::GetEffectInput(node), graph()->start());
233   Node* stub_code = jsgraph()->HeapConstant(callable.code());
234   node->InsertInput(zone(), 0, stub_code);
235   node->InsertInput(zone(), 1, function);
236   node->set_op(common()->Call(desc));
237 }
238
239
240 void JSGenericLowering::ReplaceWithRuntimeCall(Node* node,
241                                                Runtime::FunctionId f,
242                                                int nargs_override) {
243   Operator::Properties properties = node->op()->properties();
244   const Runtime::Function* fun = Runtime::FunctionForId(f);
245   int nargs = (nargs_override < 0) ? fun->nargs : nargs_override;
246   CallDescriptor* desc =
247       Linkage::GetRuntimeCallDescriptor(zone(), f, nargs, properties);
248   Node* ref = jsgraph()->ExternalConstant(ExternalReference(f, isolate()));
249   Node* arity = jsgraph()->Int32Constant(nargs);
250   node->InsertInput(zone(), 0, jsgraph()->CEntryStubConstant(fun->result_size));
251   node->InsertInput(zone(), nargs + 1, ref);
252   node->InsertInput(zone(), nargs + 2, arity);
253   node->set_op(common()->Call(desc));
254 }
255
256
257 void JSGenericLowering::LowerJSUnaryNot(Node* node) {
258   Callable callable = CodeFactory::ToBoolean(
259       isolate(), ToBooleanStub::RESULT_AS_INVERSE_ODDBALL);
260   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
261 }
262
263
264 void JSGenericLowering::LowerJSToBoolean(Node* node) {
265   Callable callable =
266       CodeFactory::ToBoolean(isolate(), ToBooleanStub::RESULT_AS_ODDBALL);
267   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
268 }
269
270
271 void JSGenericLowering::LowerJSToNumber(Node* node) {
272   Callable callable = CodeFactory::ToNumber(isolate());
273   ReplaceWithStubCall(node, callable, FlagsForNode(node));
274 }
275
276
277 void JSGenericLowering::LowerJSToString(Node* node) {
278   ReplaceWithBuiltinCall(node, Builtins::TO_STRING, 1);
279 }
280
281
282 void JSGenericLowering::LowerJSToName(Node* node) {
283   ReplaceWithBuiltinCall(node, Builtins::TO_NAME, 1);
284 }
285
286
287 void JSGenericLowering::LowerJSToObject(Node* node) {
288   ReplaceWithBuiltinCall(node, Builtins::TO_OBJECT, 1);
289 }
290
291
292 void JSGenericLowering::LowerJSLoadProperty(Node* node) {
293   const LoadPropertyParameters& p = LoadPropertyParametersOf(node->op());
294   Callable callable = CodeFactory::KeyedLoadICInOptimizedCode(isolate());
295   if (FLAG_vector_ics) {
296     node->InsertInput(zone(), 2, jsgraph()->SmiConstant(p.feedback().index()));
297     node->InsertInput(zone(), 3,
298                       jsgraph()->HeapConstant(p.feedback().vector()));
299   }
300   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
301 }
302
303
304 void JSGenericLowering::LowerJSLoadNamed(Node* node) {
305   const LoadNamedParameters& p = LoadNamedParametersOf(node->op());
306   Callable callable =
307       CodeFactory::LoadICInOptimizedCode(isolate(), p.contextual_mode());
308   node->InsertInput(zone(), 1, jsgraph()->HeapConstant(p.name()));
309   if (FLAG_vector_ics) {
310     node->InsertInput(zone(), 2, jsgraph()->SmiConstant(p.feedback().index()));
311     node->InsertInput(zone(), 3,
312                       jsgraph()->HeapConstant(p.feedback().vector()));
313   }
314   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
315 }
316
317
318 void JSGenericLowering::LowerJSStoreProperty(Node* node) {
319   LanguageMode language_mode = OpParameter<LanguageMode>(node);
320   Callable callable = CodeFactory::KeyedStoreIC(isolate(), language_mode);
321   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
322 }
323
324
325 void JSGenericLowering::LowerJSStoreNamed(Node* node) {
326   const StoreNamedParameters& p = StoreNamedParametersOf(node->op());
327   Callable callable = CodeFactory::StoreIC(isolate(), p.language_mode());
328   node->InsertInput(zone(), 1, jsgraph()->HeapConstant(p.name()));
329   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
330 }
331
332
333 void JSGenericLowering::LowerJSDeleteProperty(Node* node) {
334   LanguageMode language_mode = OpParameter<LanguageMode>(node);
335   ReplaceWithBuiltinCall(node, Builtins::DELETE, 3);
336   node->InsertInput(zone(), 4, jsgraph()->SmiConstant(language_mode));
337 }
338
339
340 void JSGenericLowering::LowerJSHasProperty(Node* node) {
341   ReplaceWithBuiltinCall(node, Builtins::IN, 2);
342 }
343
344
345 void JSGenericLowering::LowerJSInstanceOf(Node* node) {
346   InstanceofStub::Flags flags = static_cast<InstanceofStub::Flags>(
347       InstanceofStub::kReturnTrueFalseObject |
348       InstanceofStub::kArgsInRegisters);
349   InstanceofStub stub(isolate(), flags);
350   CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
351   CallDescriptor* desc = Linkage::GetStubCallDescriptor(isolate(), zone(), d, 0,
352                                                         FlagsForNode(node));
353   Node* stub_code = jsgraph()->HeapConstant(stub.GetCode());
354   node->InsertInput(zone(), 0, stub_code);
355   node->set_op(common()->Call(desc));
356 }
357
358
359 void JSGenericLowering::LowerJSLoadContext(Node* node) {
360   const ContextAccess& access = ContextAccessOf(node->op());
361   for (size_t i = 0; i < access.depth(); ++i) {
362     node->ReplaceInput(
363         0, graph()->NewNode(machine()->Load(kMachAnyTagged),
364                             NodeProperties::GetValueInput(node, 0),
365                             jsgraph()->Int32Constant(
366                                 Context::SlotOffset(Context::PREVIOUS_INDEX)),
367                             NodeProperties::GetEffectInput(node),
368                             graph()->start()));
369   }
370   node->ReplaceInput(1, jsgraph()->Int32Constant(Context::SlotOffset(
371                             static_cast<int>(access.index()))));
372   node->AppendInput(zone(), graph()->start());
373   node->set_op(machine()->Load(kMachAnyTagged));
374 }
375
376
377 void JSGenericLowering::LowerJSStoreContext(Node* node) {
378   const ContextAccess& access = ContextAccessOf(node->op());
379   for (size_t i = 0; i < access.depth(); ++i) {
380     node->ReplaceInput(
381         0, graph()->NewNode(machine()->Load(kMachAnyTagged),
382                             NodeProperties::GetValueInput(node, 0),
383                             jsgraph()->Int32Constant(
384                                 Context::SlotOffset(Context::PREVIOUS_INDEX)),
385                             NodeProperties::GetEffectInput(node),
386                             graph()->start()));
387   }
388   node->ReplaceInput(2, NodeProperties::GetValueInput(node, 1));
389   node->ReplaceInput(1, jsgraph()->Int32Constant(Context::SlotOffset(
390                             static_cast<int>(access.index()))));
391   node->set_op(
392       machine()->Store(StoreRepresentation(kMachAnyTagged, kFullWriteBarrier)));
393 }
394
395
396 void JSGenericLowering::LowerJSCreateCatchContext(Node* node) {
397   Unique<String> name = OpParameter<Unique<String>>(node);
398   node->InsertInput(zone(), 0, jsgraph()->HeapConstant(name));
399   ReplaceWithRuntimeCall(node, Runtime::kPushCatchContext);
400 }
401
402
403 void JSGenericLowering::LowerJSCallConstruct(Node* node) {
404   int arity = OpParameter<int>(node);
405   CallConstructStub stub(isolate(), NO_CALL_CONSTRUCTOR_FLAGS);
406   CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
407   CallDescriptor* desc = Linkage::GetStubCallDescriptor(
408       isolate(), zone(), d, arity, FlagsForNode(node));
409   Node* stub_code = jsgraph()->HeapConstant(stub.GetCode());
410   Node* construct = NodeProperties::GetValueInput(node, 0);
411   node->InsertInput(zone(), 0, stub_code);
412   node->InsertInput(zone(), 1, jsgraph()->Int32Constant(arity - 1));
413   node->InsertInput(zone(), 2, construct);
414   node->InsertInput(zone(), 3, jsgraph()->UndefinedConstant());
415   node->set_op(common()->Call(desc));
416 }
417
418
419 bool JSGenericLowering::TryLowerDirectJSCall(Node* node) {
420   // Lower to a direct call to a constant JSFunction if legal.
421   const CallFunctionParameters& p = CallFunctionParametersOf(node->op());
422   int arg_count = static_cast<int>(p.arity() - 2);
423
424   // Check the function is a constant and is really a JSFunction.
425   HeapObjectMatcher<Object> function_const(node->InputAt(0));
426   if (!function_const.HasValue()) return false;  // not a constant.
427   Handle<Object> func = function_const.Value().handle();
428   if (!func->IsJSFunction()) return false;  // not a function.
429   Handle<JSFunction> function = Handle<JSFunction>::cast(func);
430   if (arg_count != function->shared()->internal_formal_parameter_count()) {
431     return false;
432   }
433
434   // Check the receiver doesn't need to be wrapped.
435   Node* receiver = node->InputAt(1);
436   if (!NodeProperties::IsTyped(receiver)) return false;
437   Type* ok_receiver = Type::Union(Type::Undefined(), Type::Receiver(), zone());
438   if (!NodeProperties::GetBounds(receiver).upper->Is(ok_receiver)) return false;
439
440   int index = NodeProperties::FirstContextIndex(node);
441
442   // TODO(titzer): total hack to share function context constants.
443   // Remove this when the JSGraph canonicalizes heap constants.
444   Node* context = node->InputAt(index);
445   HeapObjectMatcher<Context> context_const(context);
446   if (!context_const.HasValue() ||
447       *(context_const.Value().handle()) != function->context()) {
448     context = jsgraph()->HeapConstant(Handle<Context>(function->context()));
449   }
450   node->ReplaceInput(index, context);
451   CallDescriptor* desc = Linkage::GetJSCallDescriptor(
452       zone(), false, 1 + arg_count, FlagsForNode(node));
453   node->set_op(common()->Call(desc));
454   return true;
455 }
456
457
458 void JSGenericLowering::LowerJSCallFunction(Node* node) {
459   // Fast case: call function directly.
460   if (TryLowerDirectJSCall(node)) return;
461
462   // General case: CallFunctionStub.
463   const CallFunctionParameters& p = CallFunctionParametersOf(node->op());
464   int arg_count = static_cast<int>(p.arity() - 2);
465   CallFunctionStub stub(isolate(), arg_count, p.flags());
466   CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
467   CallDescriptor* desc = Linkage::GetStubCallDescriptor(
468       isolate(), zone(), d, static_cast<int>(p.arity() - 1),
469       FlagsForNode(node));
470   Node* stub_code = jsgraph()->HeapConstant(stub.GetCode());
471   node->InsertInput(zone(), 0, stub_code);
472   node->set_op(common()->Call(desc));
473 }
474
475
476 void JSGenericLowering::LowerJSCallRuntime(Node* node) {
477   const CallRuntimeParameters& p = CallRuntimeParametersOf(node->op());
478   ReplaceWithRuntimeCall(node, p.id(), static_cast<int>(p.arity()));
479 }
480
481 }  // namespace compiler
482 }  // namespace internal
483 }  // namespace v8