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/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"
19 JSGenericLowering::JSGenericLowering(bool is_typing_enabled, JSGraph* jsgraph)
20 : is_typing_enabled_(is_typing_enabled), jsgraph_(jsgraph) {}
23 Reduction JSGenericLowering::Reduce(Node* node) {
24 switch (node->opcode()) {
25 #define DECLARE_CASE(x) \
26 case IrOpcode::k##x: \
29 JS_OP_LIST(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);
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); \
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
72 #define REPLACE_COMPARE_IC_CALL(op, token) \
73 void JSGenericLowering::Lower##op(Node* node) { \
74 ReplaceWithCompareIC(node, token); \
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
87 #define REPLACE_RUNTIME_CALL(op, fun) \
88 void JSGenericLowering::Lower##op(Node* node) { \
89 ReplaceWithRuntimeCall(node, fun); \
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
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
108 static CallDescriptor::Flags FlagsForNode(Node* node) {
109 CallDescriptor::Flags result = CallDescriptor::kNoFlags;
110 if (OperatorProperties::HasFrameStateInput(node->op())) {
111 result |= CallDescriptor::kNeedsFrameState;
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));
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());
137 DCHECK(OperatorProperties::HasFrameStateInput(node->op()) ==
138 FLAG_turbo_deoptimization);
139 if (FLAG_turbo_deoptimization) {
140 inputs.push_back(NodeProperties::GetFrameStateInput(node));
142 inputs.push_back(NodeProperties::GetEffectInput(node));
143 inputs.push_back(NodeProperties::GetControlInput(node));
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())));
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;
157 case Token::EQ: // a == 0
158 case Token::EQ_STRICT:
159 op = machine()->WordEqual();
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);
166 case Token::LT: // a < 0
167 op = machine()->IntLessThan();
169 case Token::GT: // a > 0 becomes !(a <= 0)
170 op = machine()->IntLessThanOrEqual();
171 std::swap(true_value, false_value);
173 case Token::LTE: // a <= 0
174 op = machine()->IntLessThanOrEqual();
176 case Token::GTE: // a >= 0 becomes !(a < 0)
177 op = machine()->IntLessThan();
178 std::swap(true_value, false_value);
183 Node* booleanize = graph()->NewNode(op, compare, jsgraph()->ZeroConstant());
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));
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));
210 void JSGenericLowering::ReplaceWithBuiltinCall(Node* node,
211 Builtins::JavaScript id,
213 Operator::Properties properties = node->op()->properties();
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) -
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));
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));
257 void JSGenericLowering::LowerJSUnaryNot(Node* node) {
258 Callable callable = CodeFactory::ToBoolean(
259 isolate(), ToBooleanStub::RESULT_AS_INVERSE_ODDBALL);
260 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
264 void JSGenericLowering::LowerJSToBoolean(Node* node) {
266 CodeFactory::ToBoolean(isolate(), ToBooleanStub::RESULT_AS_ODDBALL);
267 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
271 void JSGenericLowering::LowerJSToNumber(Node* node) {
272 Callable callable = CodeFactory::ToNumber(isolate());
273 ReplaceWithStubCall(node, callable, FlagsForNode(node));
277 void JSGenericLowering::LowerJSToString(Node* node) {
278 ReplaceWithBuiltinCall(node, Builtins::TO_STRING, 1);
282 void JSGenericLowering::LowerJSToName(Node* node) {
283 ReplaceWithBuiltinCall(node, Builtins::TO_NAME, 1);
287 void JSGenericLowering::LowerJSToObject(Node* node) {
288 ReplaceWithBuiltinCall(node, Builtins::TO_OBJECT, 1);
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()));
300 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
304 void JSGenericLowering::LowerJSLoadNamed(Node* node) {
305 const LoadNamedParameters& p = LoadNamedParametersOf(node->op());
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()));
314 ReplaceWithStubCall(node, callable, CallDescriptor::kPatchableCallSite);
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);
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);
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));
340 void JSGenericLowering::LowerJSHasProperty(Node* node) {
341 ReplaceWithBuiltinCall(node, Builtins::IN, 2);
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,
353 Node* stub_code = jsgraph()->HeapConstant(stub.GetCode());
354 node->InsertInput(zone(), 0, stub_code);
355 node->set_op(common()->Call(desc));
359 void JSGenericLowering::LowerJSLoadContext(Node* node) {
360 const ContextAccess& access = ContextAccessOf(node->op());
361 for (size_t i = 0; i < access.depth(); ++i) {
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),
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));
377 void JSGenericLowering::LowerJSStoreContext(Node* node) {
378 const ContextAccess& access = ContextAccessOf(node->op());
379 for (size_t i = 0; i < access.depth(); ++i) {
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),
388 node->ReplaceInput(2, NodeProperties::GetValueInput(node, 1));
389 node->ReplaceInput(1, jsgraph()->Int32Constant(Context::SlotOffset(
390 static_cast<int>(access.index()))));
392 machine()->Store(StoreRepresentation(kMachAnyTagged, kFullWriteBarrier)));
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);
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));
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);
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()) {
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;
440 int index = NodeProperties::FirstContextIndex(node);
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()));
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));
458 void JSGenericLowering::LowerJSCallFunction(Node* node) {
459 // Fast case: call function directly.
460 if (TryLowerDirectJSCall(node)) return;
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),
470 Node* stub_code = jsgraph()->HeapConstant(stub.GetCode());
471 node->InsertInput(zone(), 0, stub_code);
472 node->set_op(common()->Call(desc));
476 void JSGenericLowering::LowerJSCallRuntime(Node* node) {
477 const CallRuntimeParameters& p = CallRuntimeParametersOf(node->op());
478 ReplaceWithRuntimeCall(node, p.id(), static_cast<int>(p.arity()));
481 } // namespace compiler
482 } // namespace internal