blob: 300604e198e781022d8c0952987c7d79dc02e409 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// 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
15namespace v8 {
16namespace internal {
17namespace compiler {
18
19JSGenericLowering::JSGenericLowering(CompilationInfo* info, JSGraph* jsgraph)
20 : info_(info),
21 jsgraph_(jsgraph),
22 linkage_(new (jsgraph->zone()) Linkage(info)) {}
23
24
25void JSGenericLowering::PatchOperator(Node* node, const Operator* op) {
26 node->set_op(op);
27}
28
29
30void JSGenericLowering::PatchInsertInput(Node* node, int index, Node* input) {
31 node->InsertInput(zone(), index, input);
32}
33
34
35Node* JSGenericLowering::SmiConstant(int32_t immediate) {
36 return jsgraph()->SmiConstant(immediate);
37}
38
39
40Node* JSGenericLowering::Int32Constant(int immediate) {
41 return jsgraph()->Int32Constant(immediate);
42}
43
44
45Node* JSGenericLowering::CodeConstant(Handle<Code> code) {
46 return jsgraph()->HeapConstant(code);
47}
48
49
50Node* JSGenericLowering::FunctionConstant(Handle<JSFunction> function) {
51 return jsgraph()->HeapConstant(function);
52}
53
54
55Node* JSGenericLowering::ExternalConstant(ExternalReference ref) {
56 return jsgraph()->ExternalConstant(ref);
57}
58
59
60Reduction 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 }
82REPLACE_BINARY_OP_IC_CALL(JSBitwiseOr, Token::BIT_OR)
83REPLACE_BINARY_OP_IC_CALL(JSBitwiseXor, Token::BIT_XOR)
84REPLACE_BINARY_OP_IC_CALL(JSBitwiseAnd, Token::BIT_AND)
85REPLACE_BINARY_OP_IC_CALL(JSShiftLeft, Token::SHL)
86REPLACE_BINARY_OP_IC_CALL(JSShiftRight, Token::SAR)
87REPLACE_BINARY_OP_IC_CALL(JSShiftRightLogical, Token::SHR)
88REPLACE_BINARY_OP_IC_CALL(JSAdd, Token::ADD)
89REPLACE_BINARY_OP_IC_CALL(JSSubtract, Token::SUB)
90REPLACE_BINARY_OP_IC_CALL(JSMultiply, Token::MUL)
91REPLACE_BINARY_OP_IC_CALL(JSDivide, Token::DIV)
92REPLACE_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 }
100REPLACE_COMPARE_IC_CALL(JSEqual, Token::EQ, false)
101REPLACE_COMPARE_IC_CALL(JSNotEqual, Token::NE, false)
102REPLACE_COMPARE_IC_CALL(JSStrictEqual, Token::EQ_STRICT, true)
103REPLACE_COMPARE_IC_CALL(JSStrictNotEqual, Token::NE_STRICT, true)
104REPLACE_COMPARE_IC_CALL(JSLessThan, Token::LT, false)
105REPLACE_COMPARE_IC_CALL(JSGreaterThan, Token::GT, false)
106REPLACE_COMPARE_IC_CALL(JSLessThanOrEqual, Token::LTE, false)
107REPLACE_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 }
115REPLACE_RUNTIME_CALL(JSTypeOf, Runtime::kTypeof)
116REPLACE_RUNTIME_CALL(JSCreate, Runtime::kAbort)
117REPLACE_RUNTIME_CALL(JSCreateFunctionContext, Runtime::kNewFunctionContext)
118REPLACE_RUNTIME_CALL(JSCreateCatchContext, Runtime::kPushCatchContext)
119REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext)
120REPLACE_RUNTIME_CALL(JSCreateBlockContext, Runtime::kPushBlockContext)
121REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext)
122REPLACE_RUNTIME_CALL(JSCreateGlobalContext, Runtime::kAbort)
123#undef REPLACE_RUNTIME
124
125
126#define REPLACE_UNIMPLEMENTED(op) \
127 void JSGenericLowering::Lower##op(Node* node) { UNIMPLEMENTED(); }
128REPLACE_UNIMPLEMENTED(JSToName)
129REPLACE_UNIMPLEMENTED(JSYield)
130REPLACE_UNIMPLEMENTED(JSDebugger)
131#undef REPLACE_UNIMPLEMENTED
132
133
134static 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
143void 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
186void 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
196void 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
215void 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
235void 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
247void 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
254void JSGenericLowering::LowerJSToBoolean(Node* node) {
255 Callable callable =
256 CodeFactory::ToBoolean(isolate(), ToBooleanStub::RESULT_AS_ODDBALL);
257 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
258}
259
260
261void JSGenericLowering::LowerJSToNumber(Node* node) {
262 Callable callable = CodeFactory::ToNumber(isolate());
263 ReplaceWithStubCall(node, callable, CallDescriptor::kNoFlags);
264}
265
266
267void JSGenericLowering::LowerJSToString(Node* node) {
268 ReplaceWithBuiltinCall(node, Builtins::TO_STRING, 1);
269}
270
271
272void JSGenericLowering::LowerJSToObject(Node* node) {
273 ReplaceWithBuiltinCall(node, Builtins::TO_OBJECT, 1);
274}
275
276
277void JSGenericLowering::LowerJSLoadProperty(Node* node) {
278 Callable callable = CodeFactory::KeyedLoadIC(isolate());
279 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
280}
281
282
283void 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
291void 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
298void 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
306void 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
313void JSGenericLowering::LowerJSHasProperty(Node* node) {
314 ReplaceWithBuiltinCall(node, Builtins::IN, 2);
315}
316
317
318void 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
331void 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
348void 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
367void 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
383void 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
395void 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