• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/graph-inl.h"
9 #include "src/compiler/js-generic-lowering.h"
10 #include "src/compiler/machine-operator.h"
11 #include "src/compiler/node-aux-data-inl.h"
12 #include "src/compiler/node-properties-inl.h"
13 #include "src/unique.h"
14 
15 namespace v8 {
16 namespace internal {
17 namespace compiler {
18 
JSGenericLowering(CompilationInfo * info,JSGraph * jsgraph)19 JSGenericLowering::JSGenericLowering(CompilationInfo* info, JSGraph* jsgraph)
20     : info_(info),
21       jsgraph_(jsgraph),
22       linkage_(new (jsgraph->zone()) Linkage(info)) {}
23 
24 
PatchOperator(Node * node,const Operator * op)25 void JSGenericLowering::PatchOperator(Node* node, const Operator* op) {
26   node->set_op(op);
27 }
28 
29 
PatchInsertInput(Node * node,int index,Node * input)30 void JSGenericLowering::PatchInsertInput(Node* node, int index, Node* input) {
31   node->InsertInput(zone(), index, input);
32 }
33 
34 
SmiConstant(int32_t immediate)35 Node* JSGenericLowering::SmiConstant(int32_t immediate) {
36   return jsgraph()->SmiConstant(immediate);
37 }
38 
39 
Int32Constant(int immediate)40 Node* JSGenericLowering::Int32Constant(int immediate) {
41   return jsgraph()->Int32Constant(immediate);
42 }
43 
44 
CodeConstant(Handle<Code> code)45 Node* JSGenericLowering::CodeConstant(Handle<Code> code) {
46   return jsgraph()->HeapConstant(code);
47 }
48 
49 
FunctionConstant(Handle<JSFunction> function)50 Node* JSGenericLowering::FunctionConstant(Handle<JSFunction> function) {
51   return jsgraph()->HeapConstant(function);
52 }
53 
54 
ExternalConstant(ExternalReference ref)55 Node* JSGenericLowering::ExternalConstant(ExternalReference ref) {
56   return jsgraph()->ExternalConstant(ref);
57 }
58 
59 
Reduce(Node * node)60 Reduction JSGenericLowering::Reduce(Node* node) {
61   switch (node->opcode()) {
62 #define DECLARE_CASE(x) \
63   case IrOpcode::k##x:  \
64     Lower##x(node);     \
65     break;
66     DECLARE_CASE(Branch)
67     JS_OP_LIST(DECLARE_CASE)
68 #undef DECLARE_CASE
69     default:
70       // Nothing to see.
71       return NoChange();
72   }
73   return Changed(node);
74 }
75 
76 
77 #define REPLACE_BINARY_OP_IC_CALL(op, token)                             \
78   void JSGenericLowering::Lower##op(Node* node) {                        \
79     ReplaceWithStubCall(node, CodeFactory::BinaryOpIC(isolate(), token), \
80                         CallDescriptor::kPatchableCallSiteWithNop);      \
81   }
REPLACE_BINARY_OP_IC_CALL(JSBitwiseOr,Token::BIT_OR)82 REPLACE_BINARY_OP_IC_CALL(JSBitwiseOr, Token::BIT_OR)
83 REPLACE_BINARY_OP_IC_CALL(JSBitwiseXor, Token::BIT_XOR)
84 REPLACE_BINARY_OP_IC_CALL(JSBitwiseAnd, Token::BIT_AND)
85 REPLACE_BINARY_OP_IC_CALL(JSShiftLeft, Token::SHL)
86 REPLACE_BINARY_OP_IC_CALL(JSShiftRight, Token::SAR)
87 REPLACE_BINARY_OP_IC_CALL(JSShiftRightLogical, Token::SHR)
88 REPLACE_BINARY_OP_IC_CALL(JSAdd, Token::ADD)
89 REPLACE_BINARY_OP_IC_CALL(JSSubtract, Token::SUB)
90 REPLACE_BINARY_OP_IC_CALL(JSMultiply, Token::MUL)
91 REPLACE_BINARY_OP_IC_CALL(JSDivide, Token::DIV)
92 REPLACE_BINARY_OP_IC_CALL(JSModulus, Token::MOD)
93 #undef REPLACE_BINARY_OP_IC_CALL
94 
95 
96 #define REPLACE_COMPARE_IC_CALL(op, token, pure)  \
97   void JSGenericLowering::Lower##op(Node* node) { \
98     ReplaceWithCompareIC(node, token, pure);      \
99   }
100 REPLACE_COMPARE_IC_CALL(JSEqual, Token::EQ, false)
101 REPLACE_COMPARE_IC_CALL(JSNotEqual, Token::NE, false)
102 REPLACE_COMPARE_IC_CALL(JSStrictEqual, Token::EQ_STRICT, true)
103 REPLACE_COMPARE_IC_CALL(JSStrictNotEqual, Token::NE_STRICT, true)
104 REPLACE_COMPARE_IC_CALL(JSLessThan, Token::LT, false)
105 REPLACE_COMPARE_IC_CALL(JSGreaterThan, Token::GT, false)
106 REPLACE_COMPARE_IC_CALL(JSLessThanOrEqual, Token::LTE, false)
107 REPLACE_COMPARE_IC_CALL(JSGreaterThanOrEqual, Token::GTE, false)
108 #undef REPLACE_COMPARE_IC_CALL
109 
110 
111 #define REPLACE_RUNTIME_CALL(op, fun)             \
112   void JSGenericLowering::Lower##op(Node* node) { \
113     ReplaceWithRuntimeCall(node, fun);            \
114   }
115 REPLACE_RUNTIME_CALL(JSTypeOf, Runtime::kTypeof)
116 REPLACE_RUNTIME_CALL(JSCreate, Runtime::kAbort)
117 REPLACE_RUNTIME_CALL(JSCreateFunctionContext, Runtime::kNewFunctionContext)
118 REPLACE_RUNTIME_CALL(JSCreateCatchContext, Runtime::kPushCatchContext)
119 REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext)
120 REPLACE_RUNTIME_CALL(JSCreateBlockContext, Runtime::kPushBlockContext)
121 REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext)
122 REPLACE_RUNTIME_CALL(JSCreateGlobalContext, Runtime::kAbort)
123 #undef REPLACE_RUNTIME
124 
125 
126 #define REPLACE_UNIMPLEMENTED(op) \
127   void JSGenericLowering::Lower##op(Node* node) { UNIMPLEMENTED(); }
128 REPLACE_UNIMPLEMENTED(JSToName)
129 REPLACE_UNIMPLEMENTED(JSYield)
130 REPLACE_UNIMPLEMENTED(JSDebugger)
131 #undef REPLACE_UNIMPLEMENTED
132 
133 
134 static CallDescriptor::Flags FlagsForNode(Node* node) {
135   CallDescriptor::Flags result = CallDescriptor::kNoFlags;
136   if (OperatorProperties::HasFrameStateInput(node->op())) {
137     result |= CallDescriptor::kNeedsFrameState;
138   }
139   return result;
140 }
141 
142 
ReplaceWithCompareIC(Node * node,Token::Value token,bool pure)143 void JSGenericLowering::ReplaceWithCompareIC(Node* node, Token::Value token,
144                                              bool pure) {
145   Callable callable = CodeFactory::CompareIC(isolate(), token);
146   bool has_frame_state = OperatorProperties::HasFrameStateInput(node->op());
147   CallDescriptor* desc_compare = linkage()->GetStubCallDescriptor(
148       callable.descriptor(), 0,
149       CallDescriptor::kPatchableCallSiteWithNop | FlagsForNode(node));
150   NodeVector inputs(zone());
151   inputs.reserve(node->InputCount() + 1);
152   inputs.push_back(CodeConstant(callable.code()));
153   inputs.push_back(NodeProperties::GetValueInput(node, 0));
154   inputs.push_back(NodeProperties::GetValueInput(node, 1));
155   inputs.push_back(NodeProperties::GetContextInput(node));
156   if (pure) {
157     // A pure (strict) comparison doesn't have an effect, control or frame
158     // state.  But for the graph, we need to add control and effect inputs.
159     DCHECK(!has_frame_state);
160     inputs.push_back(graph()->start());
161     inputs.push_back(graph()->start());
162   } else {
163     DCHECK(has_frame_state == FLAG_turbo_deoptimization);
164     if (FLAG_turbo_deoptimization) {
165       inputs.push_back(NodeProperties::GetFrameStateInput(node));
166     }
167     inputs.push_back(NodeProperties::GetEffectInput(node));
168     inputs.push_back(NodeProperties::GetControlInput(node));
169   }
170   Node* compare =
171       graph()->NewNode(common()->Call(desc_compare),
172                        static_cast<int>(inputs.size()), &inputs.front());
173 
174   node->ReplaceInput(0, compare);
175   node->ReplaceInput(1, SmiConstant(token));
176 
177   if (has_frame_state) {
178     // Remove the frame state from inputs.
179     node->RemoveInput(NodeProperties::FirstFrameStateIndex(node));
180   }
181 
182   ReplaceWithRuntimeCall(node, Runtime::kBooleanize);
183 }
184 
185 
ReplaceWithStubCall(Node * node,Callable callable,CallDescriptor::Flags flags)186 void JSGenericLowering::ReplaceWithStubCall(Node* node, Callable callable,
187                                             CallDescriptor::Flags flags) {
188   CallDescriptor* desc = linkage()->GetStubCallDescriptor(
189       callable.descriptor(), 0, flags | FlagsForNode(node));
190   Node* stub_code = CodeConstant(callable.code());
191   PatchInsertInput(node, 0, stub_code);
192   PatchOperator(node, common()->Call(desc));
193 }
194 
195 
ReplaceWithBuiltinCall(Node * node,Builtins::JavaScript id,int nargs)196 void JSGenericLowering::ReplaceWithBuiltinCall(Node* node,
197                                                Builtins::JavaScript id,
198                                                int nargs) {
199   Callable callable =
200       CodeFactory::CallFunction(isolate(), nargs - 1, NO_CALL_FUNCTION_FLAGS);
201   CallDescriptor* desc =
202       linkage()->GetStubCallDescriptor(callable.descriptor(), nargs);
203   // TODO(mstarzinger): Accessing the builtins object this way prevents sharing
204   // of code across native contexts. Fix this by loading from given context.
205   Handle<JSFunction> function(
206       JSFunction::cast(info()->context()->builtins()->javascript_builtin(id)));
207   Node* stub_code = CodeConstant(callable.code());
208   Node* function_node = FunctionConstant(function);
209   PatchInsertInput(node, 0, stub_code);
210   PatchInsertInput(node, 1, function_node);
211   PatchOperator(node, common()->Call(desc));
212 }
213 
214 
ReplaceWithRuntimeCall(Node * node,Runtime::FunctionId f,int nargs_override)215 void JSGenericLowering::ReplaceWithRuntimeCall(Node* node,
216                                                Runtime::FunctionId f,
217                                                int nargs_override) {
218   Operator::Properties properties = node->op()->properties();
219   const Runtime::Function* fun = Runtime::FunctionForId(f);
220   int nargs = (nargs_override < 0) ? fun->nargs : nargs_override;
221   CallDescriptor* desc =
222       linkage()->GetRuntimeCallDescriptor(f, nargs, properties);
223   Node* ref = ExternalConstant(ExternalReference(f, isolate()));
224   Node* arity = Int32Constant(nargs);
225   if (!centrystub_constant_.is_set()) {
226     centrystub_constant_.set(CodeConstant(CEntryStub(isolate(), 1).GetCode()));
227   }
228   PatchInsertInput(node, 0, centrystub_constant_.get());
229   PatchInsertInput(node, nargs + 1, ref);
230   PatchInsertInput(node, nargs + 2, arity);
231   PatchOperator(node, common()->Call(desc));
232 }
233 
234 
LowerBranch(Node * node)235 void JSGenericLowering::LowerBranch(Node* node) {
236   if (!info()->is_typing_enabled()) {
237     // TODO(mstarzinger): If typing is enabled then simplified lowering will
238     // have inserted the correct ChangeBoolToBit, otherwise we need to perform
239     // poor-man's representation inference here and insert manual change.
240     Node* test = graph()->NewNode(machine()->WordEqual(), node->InputAt(0),
241                                   jsgraph()->TrueConstant());
242     node->ReplaceInput(0, test);
243   }
244 }
245 
246 
LowerJSUnaryNot(Node * node)247 void JSGenericLowering::LowerJSUnaryNot(Node* node) {
248   Callable callable = CodeFactory::ToBoolean(
249       isolate(), ToBooleanStub::RESULT_AS_INVERSE_ODDBALL);
250   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
251 }
252 
253 
LowerJSToBoolean(Node * node)254 void JSGenericLowering::LowerJSToBoolean(Node* node) {
255   Callable callable =
256       CodeFactory::ToBoolean(isolate(), ToBooleanStub::RESULT_AS_ODDBALL);
257   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
258 }
259 
260 
LowerJSToNumber(Node * node)261 void JSGenericLowering::LowerJSToNumber(Node* node) {
262   Callable callable = CodeFactory::ToNumber(isolate());
263   ReplaceWithStubCall(node, callable, CallDescriptor::kNoFlags);
264 }
265 
266 
LowerJSToString(Node * node)267 void JSGenericLowering::LowerJSToString(Node* node) {
268   ReplaceWithBuiltinCall(node, Builtins::TO_STRING, 1);
269 }
270 
271 
LowerJSToObject(Node * node)272 void JSGenericLowering::LowerJSToObject(Node* node) {
273   ReplaceWithBuiltinCall(node, Builtins::TO_OBJECT, 1);
274 }
275 
276 
LowerJSLoadProperty(Node * node)277 void JSGenericLowering::LowerJSLoadProperty(Node* node) {
278   Callable callable = CodeFactory::KeyedLoadIC(isolate());
279   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
280 }
281 
282 
LowerJSLoadNamed(Node * node)283 void JSGenericLowering::LowerJSLoadNamed(Node* node) {
284   LoadNamedParameters p = OpParameter<LoadNamedParameters>(node);
285   Callable callable = CodeFactory::LoadIC(isolate(), p.contextual_mode);
286   PatchInsertInput(node, 1, jsgraph()->HeapConstant(p.name));
287   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
288 }
289 
290 
LowerJSStoreProperty(Node * node)291 void JSGenericLowering::LowerJSStoreProperty(Node* node) {
292   StrictMode strict_mode = OpParameter<StrictMode>(node);
293   Callable callable = CodeFactory::KeyedStoreIC(isolate(), strict_mode);
294   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
295 }
296 
297 
LowerJSStoreNamed(Node * node)298 void JSGenericLowering::LowerJSStoreNamed(Node* node) {
299   StoreNamedParameters params = OpParameter<StoreNamedParameters>(node);
300   Callable callable = CodeFactory::StoreIC(isolate(), params.strict_mode);
301   PatchInsertInput(node, 1, jsgraph()->HeapConstant(params.name));
302   ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
303 }
304 
305 
LowerJSDeleteProperty(Node * node)306 void JSGenericLowering::LowerJSDeleteProperty(Node* node) {
307   StrictMode strict_mode = OpParameter<StrictMode>(node);
308   PatchInsertInput(node, 2, SmiConstant(strict_mode));
309   ReplaceWithBuiltinCall(node, Builtins::DELETE, 3);
310 }
311 
312 
LowerJSHasProperty(Node * node)313 void JSGenericLowering::LowerJSHasProperty(Node* node) {
314   ReplaceWithBuiltinCall(node, Builtins::IN, 2);
315 }
316 
317 
LowerJSInstanceOf(Node * node)318 void JSGenericLowering::LowerJSInstanceOf(Node* node) {
319   InstanceofStub::Flags flags = static_cast<InstanceofStub::Flags>(
320       InstanceofStub::kReturnTrueFalseObject |
321       InstanceofStub::kArgsInRegisters);
322   InstanceofStub stub(isolate(), flags);
323   CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
324   CallDescriptor* desc = linkage()->GetStubCallDescriptor(d, 0);
325   Node* stub_code = CodeConstant(stub.GetCode());
326   PatchInsertInput(node, 0, stub_code);
327   PatchOperator(node, common()->Call(desc));
328 }
329 
330 
LowerJSLoadContext(Node * node)331 void JSGenericLowering::LowerJSLoadContext(Node* node) {
332   ContextAccess access = OpParameter<ContextAccess>(node);
333   // TODO(mstarzinger): Use simplified operators instead of machine operators
334   // here so that load/store optimization can be applied afterwards.
335   for (int i = 0; i < access.depth(); ++i) {
336     node->ReplaceInput(
337         0, graph()->NewNode(
338                machine()->Load(kMachAnyTagged),
339                NodeProperties::GetValueInput(node, 0),
340                Int32Constant(Context::SlotOffset(Context::PREVIOUS_INDEX)),
341                NodeProperties::GetEffectInput(node)));
342   }
343   node->ReplaceInput(1, Int32Constant(Context::SlotOffset(access.index())));
344   PatchOperator(node, machine()->Load(kMachAnyTagged));
345 }
346 
347 
LowerJSStoreContext(Node * node)348 void JSGenericLowering::LowerJSStoreContext(Node* node) {
349   ContextAccess access = OpParameter<ContextAccess>(node);
350   // TODO(mstarzinger): Use simplified operators instead of machine operators
351   // here so that load/store optimization can be applied afterwards.
352   for (int i = 0; i < access.depth(); ++i) {
353     node->ReplaceInput(
354         0, graph()->NewNode(
355                machine()->Load(kMachAnyTagged),
356                NodeProperties::GetValueInput(node, 0),
357                Int32Constant(Context::SlotOffset(Context::PREVIOUS_INDEX)),
358                NodeProperties::GetEffectInput(node)));
359   }
360   node->ReplaceInput(2, NodeProperties::GetValueInput(node, 1));
361   node->ReplaceInput(1, Int32Constant(Context::SlotOffset(access.index())));
362   PatchOperator(node, machine()->Store(StoreRepresentation(kMachAnyTagged,
363                                                            kFullWriteBarrier)));
364 }
365 
366 
LowerJSCallConstruct(Node * node)367 void JSGenericLowering::LowerJSCallConstruct(Node* node) {
368   int arity = OpParameter<int>(node);
369   CallConstructStub stub(isolate(), NO_CALL_CONSTRUCTOR_FLAGS);
370   CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
371   CallDescriptor* desc =
372       linkage()->GetStubCallDescriptor(d, arity, FlagsForNode(node));
373   Node* stub_code = CodeConstant(stub.GetCode());
374   Node* construct = NodeProperties::GetValueInput(node, 0);
375   PatchInsertInput(node, 0, stub_code);
376   PatchInsertInput(node, 1, Int32Constant(arity - 1));
377   PatchInsertInput(node, 2, construct);
378   PatchInsertInput(node, 3, jsgraph()->UndefinedConstant());
379   PatchOperator(node, common()->Call(desc));
380 }
381 
382 
LowerJSCallFunction(Node * node)383 void JSGenericLowering::LowerJSCallFunction(Node* node) {
384   CallParameters p = OpParameter<CallParameters>(node);
385   CallFunctionStub stub(isolate(), p.arity - 2, p.flags);
386   CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
387   CallDescriptor* desc =
388       linkage()->GetStubCallDescriptor(d, p.arity - 1, FlagsForNode(node));
389   Node* stub_code = CodeConstant(stub.GetCode());
390   PatchInsertInput(node, 0, stub_code);
391   PatchOperator(node, common()->Call(desc));
392 }
393 
394 
LowerJSCallRuntime(Node * node)395 void JSGenericLowering::LowerJSCallRuntime(Node* node) {
396   Runtime::FunctionId function = OpParameter<Runtime::FunctionId>(node);
397   int arity = OperatorProperties::GetValueInputCount(node->op());
398   ReplaceWithRuntimeCall(node, function, arity);
399 }
400 
401 }  // namespace compiler
402 }  // namespace internal
403 }  // namespace v8
404