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.
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-matchers.h"
13 #include "src/compiler/node-properties-inl.h"
14 #include "src/unique.h"
20 JSGenericLowering::JSGenericLowering(CompilationInfo* info, JSGraph* jsgraph)
23 linkage_(new (jsgraph->zone()) Linkage(jsgraph->zone(), info)) {}
26 void JSGenericLowering::PatchOperator(Node* node, const Operator* op) {
31 void JSGenericLowering::PatchInsertInput(Node* node, int index, Node* input) {
32 node->InsertInput(zone(), index, input);
36 Node* JSGenericLowering::SmiConstant(int32_t immediate) {
37 return jsgraph()->SmiConstant(immediate);
41 Node* JSGenericLowering::Int32Constant(int immediate) {
42 return jsgraph()->Int32Constant(immediate);
46 Node* JSGenericLowering::CodeConstant(Handle<Code> code) {
47 return jsgraph()->HeapConstant(code);
51 Node* JSGenericLowering::FunctionConstant(Handle<JSFunction> function) {
52 return jsgraph()->HeapConstant(function);
56 Node* JSGenericLowering::ExternalConstant(ExternalReference ref) {
57 return jsgraph()->ExternalConstant(ref);
61 Reduction JSGenericLowering::Reduce(Node* node) {
62 switch (node->opcode()) {
63 #define DECLARE_CASE(x) \
64 case IrOpcode::k##x: \
68 JS_OP_LIST(DECLARE_CASE)
78 #define REPLACE_BINARY_OP_IC_CALL(op, token) \
79 void JSGenericLowering::Lower##op(Node* node) { \
80 ReplaceWithStubCall(node, CodeFactory::BinaryOpIC(isolate(), token), \
81 CallDescriptor::kPatchableCallSiteWithNop); \
83 REPLACE_BINARY_OP_IC_CALL(JSBitwiseOr, Token::BIT_OR)
84 REPLACE_BINARY_OP_IC_CALL(JSBitwiseXor, Token::BIT_XOR)
85 REPLACE_BINARY_OP_IC_CALL(JSBitwiseAnd, Token::BIT_AND)
86 REPLACE_BINARY_OP_IC_CALL(JSShiftLeft, Token::SHL)
87 REPLACE_BINARY_OP_IC_CALL(JSShiftRight, Token::SAR)
88 REPLACE_BINARY_OP_IC_CALL(JSShiftRightLogical, Token::SHR)
89 REPLACE_BINARY_OP_IC_CALL(JSAdd, Token::ADD)
90 REPLACE_BINARY_OP_IC_CALL(JSSubtract, Token::SUB)
91 REPLACE_BINARY_OP_IC_CALL(JSMultiply, Token::MUL)
92 REPLACE_BINARY_OP_IC_CALL(JSDivide, Token::DIV)
93 REPLACE_BINARY_OP_IC_CALL(JSModulus, Token::MOD)
94 #undef REPLACE_BINARY_OP_IC_CALL
97 #define REPLACE_COMPARE_IC_CALL(op, token, pure) \
98 void JSGenericLowering::Lower##op(Node* node) { \
99 ReplaceWithCompareIC(node, token, pure); \
101 REPLACE_COMPARE_IC_CALL(JSEqual, Token::EQ, false)
102 REPLACE_COMPARE_IC_CALL(JSNotEqual, Token::NE, false)
103 REPLACE_COMPARE_IC_CALL(JSStrictEqual, Token::EQ_STRICT, true)
104 REPLACE_COMPARE_IC_CALL(JSStrictNotEqual, Token::NE_STRICT, true)
105 REPLACE_COMPARE_IC_CALL(JSLessThan, Token::LT, false)
106 REPLACE_COMPARE_IC_CALL(JSGreaterThan, Token::GT, false)
107 REPLACE_COMPARE_IC_CALL(JSLessThanOrEqual, Token::LTE, false)
108 REPLACE_COMPARE_IC_CALL(JSGreaterThanOrEqual, Token::GTE, false)
109 #undef REPLACE_COMPARE_IC_CALL
112 #define REPLACE_RUNTIME_CALL(op, fun) \
113 void JSGenericLowering::Lower##op(Node* node) { \
114 ReplaceWithRuntimeCall(node, fun); \
116 REPLACE_RUNTIME_CALL(JSTypeOf, Runtime::kTypeof)
117 REPLACE_RUNTIME_CALL(JSCreate, Runtime::kAbort)
118 REPLACE_RUNTIME_CALL(JSCreateFunctionContext, Runtime::kNewFunctionContext)
119 REPLACE_RUNTIME_CALL(JSCreateCatchContext, Runtime::kPushCatchContext)
120 REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext)
121 REPLACE_RUNTIME_CALL(JSCreateBlockContext, Runtime::kPushBlockContext)
122 REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext)
123 REPLACE_RUNTIME_CALL(JSCreateGlobalContext, Runtime::kAbort)
124 #undef REPLACE_RUNTIME
127 #define REPLACE_UNIMPLEMENTED(op) \
128 void JSGenericLowering::Lower##op(Node* node) { UNIMPLEMENTED(); }
129 REPLACE_UNIMPLEMENTED(JSToName)
130 REPLACE_UNIMPLEMENTED(JSYield)
131 REPLACE_UNIMPLEMENTED(JSDebugger)
132 #undef REPLACE_UNIMPLEMENTED
135 static CallDescriptor::Flags FlagsForNode(Node* node) {
136 CallDescriptor::Flags result = CallDescriptor::kNoFlags;
137 if (OperatorProperties::HasFrameStateInput(node->op())) {
138 result |= CallDescriptor::kNeedsFrameState;
144 void JSGenericLowering::ReplaceWithCompareIC(Node* node, Token::Value token,
146 Callable callable = CodeFactory::CompareIC(isolate(), token);
147 bool has_frame_state = OperatorProperties::HasFrameStateInput(node->op());
148 CallDescriptor* desc_compare = linkage()->GetStubCallDescriptor(
149 callable.descriptor(), 0,
150 CallDescriptor::kPatchableCallSiteWithNop | FlagsForNode(node));
151 NodeVector inputs(zone());
152 inputs.reserve(node->InputCount() + 1);
153 inputs.push_back(CodeConstant(callable.code()));
154 inputs.push_back(NodeProperties::GetValueInput(node, 0));
155 inputs.push_back(NodeProperties::GetValueInput(node, 1));
156 inputs.push_back(NodeProperties::GetContextInput(node));
158 // A pure (strict) comparison doesn't have an effect, control or frame
159 // state. But for the graph, we need to add control and effect inputs.
160 DCHECK(!has_frame_state);
161 inputs.push_back(graph()->start());
162 inputs.push_back(graph()->start());
164 DCHECK(has_frame_state == FLAG_turbo_deoptimization);
165 if (FLAG_turbo_deoptimization) {
166 inputs.push_back(NodeProperties::GetFrameStateInput(node));
168 inputs.push_back(NodeProperties::GetEffectInput(node));
169 inputs.push_back(NodeProperties::GetControlInput(node));
172 graph()->NewNode(common()->Call(desc_compare),
173 static_cast<int>(inputs.size()), &inputs.front());
175 node->ReplaceInput(0, compare);
176 node->ReplaceInput(1, SmiConstant(token));
178 if (has_frame_state) {
179 // Remove the frame state from inputs.
180 node->RemoveInput(NodeProperties::FirstFrameStateIndex(node));
183 ReplaceWithRuntimeCall(node, Runtime::kBooleanize);
187 void JSGenericLowering::ReplaceWithStubCall(Node* node, Callable callable,
188 CallDescriptor::Flags flags) {
189 CallDescriptor* desc = linkage()->GetStubCallDescriptor(
190 callable.descriptor(), 0, flags | FlagsForNode(node));
191 Node* stub_code = CodeConstant(callable.code());
192 PatchInsertInput(node, 0, stub_code);
193 PatchOperator(node, common()->Call(desc));
197 void JSGenericLowering::ReplaceWithBuiltinCall(Node* node,
198 Builtins::JavaScript id,
201 CodeFactory::CallFunction(isolate(), nargs - 1, NO_CALL_FUNCTION_FLAGS);
202 CallDescriptor* desc = linkage()->GetStubCallDescriptor(
203 callable.descriptor(), nargs, FlagsForNode(node));
204 // TODO(mstarzinger): Accessing the builtins object this way prevents sharing
205 // of code across native contexts. Fix this by loading from given context.
206 Handle<JSFunction> function(
207 JSFunction::cast(info()->context()->builtins()->javascript_builtin(id)));
208 Node* stub_code = CodeConstant(callable.code());
209 Node* function_node = FunctionConstant(function);
210 PatchInsertInput(node, 0, stub_code);
211 PatchInsertInput(node, 1, function_node);
212 PatchOperator(node, common()->Call(desc));
216 void JSGenericLowering::ReplaceWithRuntimeCall(Node* node,
217 Runtime::FunctionId f,
218 int nargs_override) {
219 Operator::Properties properties = node->op()->properties();
220 const Runtime::Function* fun = Runtime::FunctionForId(f);
221 int nargs = (nargs_override < 0) ? fun->nargs : nargs_override;
222 CallDescriptor* desc =
223 linkage()->GetRuntimeCallDescriptor(f, nargs, properties);
224 Node* ref = ExternalConstant(ExternalReference(f, isolate()));
225 Node* arity = Int32Constant(nargs);
226 PatchInsertInput(node, 0, jsgraph()->CEntryStubConstant());
227 PatchInsertInput(node, nargs + 1, ref);
228 PatchInsertInput(node, nargs + 2, arity);
229 PatchOperator(node, common()->Call(desc));
233 void JSGenericLowering::LowerBranch(Node* node) {
234 if (!info()->is_typing_enabled()) {
235 // TODO(mstarzinger): If typing is enabled then simplified lowering will
236 // have inserted the correct ChangeBoolToBit, otherwise we need to perform
237 // poor-man's representation inference here and insert manual change.
238 Node* test = graph()->NewNode(machine()->WordEqual(), node->InputAt(0),
239 jsgraph()->TrueConstant());
240 node->ReplaceInput(0, test);
245 void JSGenericLowering::LowerJSUnaryNot(Node* node) {
246 Callable callable = CodeFactory::ToBoolean(
247 isolate(), ToBooleanStub::RESULT_AS_INVERSE_ODDBALL);
248 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
252 void JSGenericLowering::LowerJSToBoolean(Node* node) {
254 CodeFactory::ToBoolean(isolate(), ToBooleanStub::RESULT_AS_ODDBALL);
255 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
259 void JSGenericLowering::LowerJSToNumber(Node* node) {
260 Callable callable = CodeFactory::ToNumber(isolate());
261 ReplaceWithStubCall(node, callable, FlagsForNode(node));
265 void JSGenericLowering::LowerJSToString(Node* node) {
266 ReplaceWithBuiltinCall(node, Builtins::TO_STRING, 1);
270 void JSGenericLowering::LowerJSToObject(Node* node) {
271 ReplaceWithBuiltinCall(node, Builtins::TO_OBJECT, 1);
275 void JSGenericLowering::LowerJSLoadProperty(Node* node) {
276 const LoadPropertyParameters& p = LoadPropertyParametersOf(node->op());
277 Callable callable = CodeFactory::KeyedLoadICInOptimizedCode(isolate());
278 if (FLAG_vector_ics) {
279 PatchInsertInput(node, 2, jsgraph()->SmiConstant(p.feedback().index()));
280 PatchInsertInput(node, 3, jsgraph()->HeapConstant(p.feedback().vector()));
282 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
286 void JSGenericLowering::LowerJSLoadNamed(Node* node) {
287 const LoadNamedParameters& p = LoadNamedParametersOf(node->op());
289 CodeFactory::LoadICInOptimizedCode(isolate(), p.contextual_mode());
290 PatchInsertInput(node, 1, jsgraph()->HeapConstant(p.name()));
291 if (FLAG_vector_ics) {
292 PatchInsertInput(node, 2, jsgraph()->SmiConstant(p.feedback().index()));
293 PatchInsertInput(node, 3, jsgraph()->HeapConstant(p.feedback().vector()));
295 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
299 void JSGenericLowering::LowerJSStoreProperty(Node* node) {
300 StrictMode strict_mode = OpParameter<StrictMode>(node);
301 Callable callable = CodeFactory::KeyedStoreIC(isolate(), strict_mode);
302 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
306 void JSGenericLowering::LowerJSStoreNamed(Node* node) {
307 const StoreNamedParameters& p = StoreNamedParametersOf(node->op());
308 Callable callable = CodeFactory::StoreIC(isolate(), p.strict_mode());
309 PatchInsertInput(node, 1, jsgraph()->HeapConstant(p.name()));
310 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
314 void JSGenericLowering::LowerJSDeleteProperty(Node* node) {
315 StrictMode strict_mode = OpParameter<StrictMode>(node);
316 PatchInsertInput(node, 2, SmiConstant(strict_mode));
317 ReplaceWithBuiltinCall(node, Builtins::DELETE, 3);
321 void JSGenericLowering::LowerJSHasProperty(Node* node) {
322 ReplaceWithBuiltinCall(node, Builtins::IN, 2);
326 void JSGenericLowering::LowerJSInstanceOf(Node* node) {
327 InstanceofStub::Flags flags = static_cast<InstanceofStub::Flags>(
328 InstanceofStub::kReturnTrueFalseObject |
329 InstanceofStub::kArgsInRegisters);
330 InstanceofStub stub(isolate(), flags);
331 CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
332 CallDescriptor* desc =
333 linkage()->GetStubCallDescriptor(d, 0, FlagsForNode(node));
334 Node* stub_code = CodeConstant(stub.GetCode());
335 PatchInsertInput(node, 0, stub_code);
336 PatchOperator(node, common()->Call(desc));
340 void JSGenericLowering::LowerJSLoadContext(Node* node) {
341 const ContextAccess& access = ContextAccessOf(node->op());
342 for (size_t i = 0; i < access.depth(); ++i) {
345 machine()->Load(kMachAnyTagged),
346 NodeProperties::GetValueInput(node, 0),
347 Int32Constant(Context::SlotOffset(Context::PREVIOUS_INDEX)),
348 NodeProperties::GetEffectInput(node), graph()->start()));
351 1, Int32Constant(Context::SlotOffset(static_cast<int>(access.index()))));
352 node->AppendInput(zone(), graph()->start());
353 PatchOperator(node, machine()->Load(kMachAnyTagged));
357 void JSGenericLowering::LowerJSStoreContext(Node* node) {
358 const ContextAccess& access = ContextAccessOf(node->op());
359 for (size_t i = 0; i < access.depth(); ++i) {
362 machine()->Load(kMachAnyTagged),
363 NodeProperties::GetValueInput(node, 0),
364 Int32Constant(Context::SlotOffset(Context::PREVIOUS_INDEX)),
365 NodeProperties::GetEffectInput(node), graph()->start()));
367 node->ReplaceInput(2, NodeProperties::GetValueInput(node, 1));
369 1, Int32Constant(Context::SlotOffset(static_cast<int>(access.index()))));
370 PatchOperator(node, machine()->Store(StoreRepresentation(kMachAnyTagged,
371 kFullWriteBarrier)));
375 void JSGenericLowering::LowerJSCallConstruct(Node* node) {
376 int arity = OpParameter<int>(node);
377 CallConstructStub stub(isolate(), NO_CALL_CONSTRUCTOR_FLAGS);
378 CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
379 CallDescriptor* desc =
380 linkage()->GetStubCallDescriptor(d, arity, FlagsForNode(node));
381 Node* stub_code = CodeConstant(stub.GetCode());
382 Node* construct = NodeProperties::GetValueInput(node, 0);
383 PatchInsertInput(node, 0, stub_code);
384 PatchInsertInput(node, 1, Int32Constant(arity - 1));
385 PatchInsertInput(node, 2, construct);
386 PatchInsertInput(node, 3, jsgraph()->UndefinedConstant());
387 PatchOperator(node, common()->Call(desc));
391 bool JSGenericLowering::TryLowerDirectJSCall(Node* node) {
392 // Lower to a direct call to a constant JSFunction if legal.
393 const CallFunctionParameters& p = CallFunctionParametersOf(node->op());
394 int arg_count = static_cast<int>(p.arity() - 2);
396 // Check the function is a constant and is really a JSFunction.
397 HeapObjectMatcher<Object> function_const(node->InputAt(0));
398 if (!function_const.HasValue()) return false; // not a constant.
399 Handle<Object> func = function_const.Value().handle();
400 if (!func->IsJSFunction()) return false; // not a function.
401 Handle<JSFunction> function = Handle<JSFunction>::cast(func);
402 if (arg_count != function->shared()->formal_parameter_count()) return false;
404 // Check the receiver doesn't need to be wrapped.
405 Node* receiver = node->InputAt(1);
406 if (!NodeProperties::IsTyped(receiver)) return false;
407 Type* ok_receiver = Type::Union(Type::Undefined(), Type::Receiver(), zone());
408 if (!NodeProperties::GetBounds(receiver).upper->Is(ok_receiver)) return false;
410 int index = NodeProperties::FirstContextIndex(node);
412 // TODO(titzer): total hack to share function context constants.
413 // Remove this when the JSGraph canonicalizes heap constants.
414 Node* context = node->InputAt(index);
415 HeapObjectMatcher<Context> context_const(context);
416 if (!context_const.HasValue() ||
417 *(context_const.Value().handle()) != function->context()) {
418 context = jsgraph()->HeapConstant(Handle<Context>(function->context()));
420 node->ReplaceInput(index, context);
421 CallDescriptor* desc = linkage()->GetJSCallDescriptor(
422 1 + arg_count, jsgraph()->zone(), FlagsForNode(node));
423 PatchOperator(node, common()->Call(desc));
428 void JSGenericLowering::LowerJSCallFunction(Node* node) {
429 // Fast case: call function directly.
430 if (TryLowerDirectJSCall(node)) return;
432 // General case: CallFunctionStub.
433 const CallFunctionParameters& p = CallFunctionParametersOf(node->op());
434 int arg_count = static_cast<int>(p.arity() - 2);
435 CallFunctionStub stub(isolate(), arg_count, p.flags());
436 CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
437 CallDescriptor* desc = linkage()->GetStubCallDescriptor(
438 d, static_cast<int>(p.arity() - 1), FlagsForNode(node));
439 Node* stub_code = CodeConstant(stub.GetCode());
440 PatchInsertInput(node, 0, stub_code);
441 PatchOperator(node, common()->Call(desc));
445 void JSGenericLowering::LowerJSCallRuntime(Node* node) {
446 const CallRuntimeParameters& p = CallRuntimeParametersOf(node->op());
447 ReplaceWithRuntimeCall(node, p.id(), static_cast<int>(p.arity()));
450 } // namespace compiler
451 } // namespace internal