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/js-graph.h"
10 #include "src/compiler/machine-operator.h"
11 #include "src/compiler/node-matchers.h"
12 #include "src/compiler/node-properties.h"
13 #include "src/compiler/operator-properties.h"
14
15 namespace v8 {
16 namespace internal {
17 namespace compiler {
18
AdjustFrameStatesForCall(Node * node)19 static CallDescriptor::Flags AdjustFrameStatesForCall(Node* node) {
20 int count = OperatorProperties::GetFrameStateInputCount(node->op());
21 if (count > 1) {
22 int index = NodeProperties::FirstFrameStateIndex(node) + 1;
23 do {
24 node->RemoveInput(index);
25 } while (--count > 1);
26 }
27 return count > 0 ? CallDescriptor::kNeedsFrameState
28 : CallDescriptor::kNoFlags;
29 }
30
31
JSGenericLowering(bool is_typing_enabled,JSGraph * jsgraph)32 JSGenericLowering::JSGenericLowering(bool is_typing_enabled, JSGraph* jsgraph)
33 : is_typing_enabled_(is_typing_enabled), jsgraph_(jsgraph) {}
34
35
~JSGenericLowering()36 JSGenericLowering::~JSGenericLowering() {}
37
38
Reduce(Node * node)39 Reduction JSGenericLowering::Reduce(Node* node) {
40 switch (node->opcode()) {
41 #define DECLARE_CASE(x) \
42 case IrOpcode::k##x: \
43 Lower##x(node); \
44 break;
45 JS_OP_LIST(DECLARE_CASE)
46 #undef DECLARE_CASE
47 case IrOpcode::kBranch:
48 // TODO(mstarzinger): If typing is enabled then simplified lowering will
49 // have inserted the correct ChangeBoolToBit, otherwise we need to perform
50 // poor-man's representation inference here and insert manual change.
51 if (!is_typing_enabled_) {
52 Node* condition = node->InputAt(0);
53 Node* test = graph()->NewNode(machine()->WordEqual(), condition,
54 jsgraph()->TrueConstant());
55 node->ReplaceInput(0, test);
56 }
57 // Fall-through.
58 default:
59 // Nothing to see.
60 return NoChange();
61 }
62 return Changed(node);
63 }
64
65
66 #define REPLACE_BINARY_OP_IC_CALL(Op, token) \
67 void JSGenericLowering::Lower##Op(Node* node) { \
68 BinaryOperationParameters const& p = \
69 BinaryOperationParametersOf(node->op()); \
70 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); \
71 ReplaceWithStubCall(node, \
72 CodeFactory::BinaryOpIC(isolate(), token, \
73 strength(p.language_mode())), \
74 CallDescriptor::kPatchableCallSiteWithNop | flags); \
75 }
REPLACE_BINARY_OP_IC_CALL(JSBitwiseOr,Token::BIT_OR)76 REPLACE_BINARY_OP_IC_CALL(JSBitwiseOr, Token::BIT_OR)
77 REPLACE_BINARY_OP_IC_CALL(JSBitwiseXor, Token::BIT_XOR)
78 REPLACE_BINARY_OP_IC_CALL(JSBitwiseAnd, Token::BIT_AND)
79 REPLACE_BINARY_OP_IC_CALL(JSShiftLeft, Token::SHL)
80 REPLACE_BINARY_OP_IC_CALL(JSShiftRight, Token::SAR)
81 REPLACE_BINARY_OP_IC_CALL(JSShiftRightLogical, Token::SHR)
82 REPLACE_BINARY_OP_IC_CALL(JSAdd, Token::ADD)
83 REPLACE_BINARY_OP_IC_CALL(JSSubtract, Token::SUB)
84 REPLACE_BINARY_OP_IC_CALL(JSMultiply, Token::MUL)
85 REPLACE_BINARY_OP_IC_CALL(JSDivide, Token::DIV)
86 REPLACE_BINARY_OP_IC_CALL(JSModulus, Token::MOD)
87 #undef REPLACE_BINARY_OP_IC_CALL
88
89
90 // These ops are not language mode dependent; we arbitrarily pass Strength::WEAK
91 // here.
92 #define REPLACE_COMPARE_IC_CALL(op, token) \
93 void JSGenericLowering::Lower##op(Node* node) { \
94 ReplaceWithCompareIC(node, token, Strength::WEAK); \
95 }
96 REPLACE_COMPARE_IC_CALL(JSEqual, Token::EQ)
97 REPLACE_COMPARE_IC_CALL(JSNotEqual, Token::NE)
98 REPLACE_COMPARE_IC_CALL(JSStrictEqual, Token::EQ_STRICT)
99 REPLACE_COMPARE_IC_CALL(JSStrictNotEqual, Token::NE_STRICT)
100 #undef REPLACE_COMPARE_IC_CALL
101
102
103 #define REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE(op, token) \
104 void JSGenericLowering::Lower##op(Node* node) { \
105 ReplaceWithCompareIC(node, token, \
106 strength(OpParameter<LanguageMode>(node))); \
107 }
108 REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE(JSLessThan, Token::LT)
109 REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE(JSGreaterThan, Token::GT)
110 REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE(JSLessThanOrEqual, Token::LTE)
111 REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE(JSGreaterThanOrEqual, Token::GTE)
112 #undef REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE
113
114
115 #define REPLACE_RUNTIME_CALL(op, fun) \
116 void JSGenericLowering::Lower##op(Node* node) { \
117 ReplaceWithRuntimeCall(node, fun); \
118 }
119 REPLACE_RUNTIME_CALL(JSCreateFunctionContext, Runtime::kNewFunctionContext)
120 REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext)
121 REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext)
122 REPLACE_RUNTIME_CALL(JSConvertReceiver, Runtime::kConvertReceiver)
123 #undef REPLACE_RUNTIME
124
125
126 static CallDescriptor::Flags FlagsForNode(Node* node) {
127 CallDescriptor::Flags result = CallDescriptor::kNoFlags;
128 if (OperatorProperties::GetFrameStateInputCount(node->op()) > 0) {
129 result |= CallDescriptor::kNeedsFrameState;
130 }
131 return result;
132 }
133
134
ReplaceWithCompareIC(Node * node,Token::Value token,Strength str)135 void JSGenericLowering::ReplaceWithCompareIC(Node* node, Token::Value token,
136 Strength str) {
137 Callable callable = CodeFactory::CompareIC(isolate(), token, str);
138
139 // Create a new call node asking a CompareIC for help.
140 NodeVector inputs(zone());
141 inputs.reserve(node->InputCount() + 1);
142 inputs.push_back(jsgraph()->HeapConstant(callable.code()));
143 inputs.push_back(NodeProperties::GetValueInput(node, 0));
144 inputs.push_back(NodeProperties::GetValueInput(node, 1));
145 inputs.push_back(NodeProperties::GetContextInput(node));
146 // Some comparisons (StrictEqual) don't have an effect, control or frame
147 // state inputs, so handle those cases here.
148 if (OperatorProperties::GetFrameStateInputCount(node->op()) > 0) {
149 inputs.push_back(NodeProperties::GetFrameStateInput(node, 0));
150 }
151 Node* effect = (node->op()->EffectInputCount() > 0)
152 ? NodeProperties::GetEffectInput(node)
153 : graph()->start();
154 inputs.push_back(effect);
155 Node* control = (node->op()->ControlInputCount() > 0)
156 ? NodeProperties::GetControlInput(node)
157 : graph()->start();
158 inputs.push_back(control);
159 CallDescriptor* desc_compare = Linkage::GetStubCallDescriptor(
160 isolate(), zone(), callable.descriptor(), 0,
161 CallDescriptor::kPatchableCallSiteWithNop | FlagsForNode(node),
162 Operator::kNoProperties, MachineType::IntPtr());
163 Node* compare =
164 graph()->NewNode(common()->Call(desc_compare),
165 static_cast<int>(inputs.size()), &inputs.front());
166
167 // Decide how the return value from the above CompareIC can be converted into
168 // a JavaScript boolean oddball depending on the given token.
169 Node* false_value = jsgraph()->FalseConstant();
170 Node* true_value = jsgraph()->TrueConstant();
171 const Operator* op = nullptr;
172 switch (token) {
173 case Token::EQ: // a == 0
174 case Token::EQ_STRICT:
175 op = machine()->WordEqual();
176 break;
177 case Token::NE: // a != 0 becomes !(a == 0)
178 case Token::NE_STRICT:
179 op = machine()->WordEqual();
180 std::swap(true_value, false_value);
181 break;
182 case Token::LT: // a < 0
183 op = machine()->IntLessThan();
184 break;
185 case Token::GT: // a > 0 becomes !(a <= 0)
186 op = machine()->IntLessThanOrEqual();
187 std::swap(true_value, false_value);
188 break;
189 case Token::LTE: // a <= 0
190 op = machine()->IntLessThanOrEqual();
191 break;
192 case Token::GTE: // a >= 0 becomes !(a < 0)
193 op = machine()->IntLessThan();
194 std::swap(true_value, false_value);
195 break;
196 default:
197 UNREACHABLE();
198 }
199 Node* booleanize = graph()->NewNode(op, compare, jsgraph()->ZeroConstant());
200
201 // Finally patch the original node to select a boolean.
202 NodeProperties::ReplaceUses(node, node, compare, compare, compare);
203 node->TrimInputCount(3);
204 node->ReplaceInput(0, booleanize);
205 node->ReplaceInput(1, true_value);
206 node->ReplaceInput(2, false_value);
207 NodeProperties::ChangeOp(node,
208 common()->Select(MachineRepresentation::kTagged));
209 }
210
211
ReplaceWithStubCall(Node * node,Callable callable,CallDescriptor::Flags flags)212 void JSGenericLowering::ReplaceWithStubCall(Node* node, Callable callable,
213 CallDescriptor::Flags flags) {
214 Operator::Properties properties = node->op()->properties();
215 CallDescriptor* desc = Linkage::GetStubCallDescriptor(
216 isolate(), zone(), callable.descriptor(), 0, flags, properties);
217 Node* stub_code = jsgraph()->HeapConstant(callable.code());
218 node->InsertInput(zone(), 0, stub_code);
219 NodeProperties::ChangeOp(node, common()->Call(desc));
220 }
221
222
ReplaceWithRuntimeCall(Node * node,Runtime::FunctionId f,int nargs_override)223 void JSGenericLowering::ReplaceWithRuntimeCall(Node* node,
224 Runtime::FunctionId f,
225 int nargs_override) {
226 Operator::Properties properties = node->op()->properties();
227 const Runtime::Function* fun = Runtime::FunctionForId(f);
228 int nargs = (nargs_override < 0) ? fun->nargs : nargs_override;
229 CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor(
230 zone(), f, nargs, properties, CallDescriptor::kNeedsFrameState);
231 Node* ref = jsgraph()->ExternalConstant(ExternalReference(f, isolate()));
232 Node* arity = jsgraph()->Int32Constant(nargs);
233 node->InsertInput(zone(), 0, jsgraph()->CEntryStubConstant(fun->result_size));
234 node->InsertInput(zone(), nargs + 1, ref);
235 node->InsertInput(zone(), nargs + 2, arity);
236 NodeProperties::ChangeOp(node, common()->Call(desc));
237 }
238
239
LowerJSTypeOf(Node * node)240 void JSGenericLowering::LowerJSTypeOf(Node* node) {
241 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
242 Callable callable = CodeFactory::Typeof(isolate());
243 ReplaceWithStubCall(node, callable, flags);
244 }
245
246
LowerJSToBoolean(Node * node)247 void JSGenericLowering::LowerJSToBoolean(Node* node) {
248 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
249 Callable callable = CodeFactory::ToBoolean(isolate());
250 ReplaceWithStubCall(node, callable,
251 CallDescriptor::kPatchableCallSite | flags);
252 }
253
254
LowerJSToNumber(Node * node)255 void JSGenericLowering::LowerJSToNumber(Node* node) {
256 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
257 Callable callable = CodeFactory::ToNumber(isolate());
258 ReplaceWithStubCall(node, callable, flags);
259 }
260
261
LowerJSToString(Node * node)262 void JSGenericLowering::LowerJSToString(Node* node) {
263 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
264 Callable callable = CodeFactory::ToString(isolate());
265 ReplaceWithStubCall(node, callable, flags);
266 }
267
268
LowerJSToName(Node * node)269 void JSGenericLowering::LowerJSToName(Node* node) {
270 ReplaceWithRuntimeCall(node, Runtime::kToName);
271 }
272
273
LowerJSToObject(Node * node)274 void JSGenericLowering::LowerJSToObject(Node* node) {
275 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
276 Callable callable = CodeFactory::ToObject(isolate());
277 ReplaceWithStubCall(node, callable, flags);
278 }
279
280
LowerJSLoadProperty(Node * node)281 void JSGenericLowering::LowerJSLoadProperty(Node* node) {
282 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
283 const PropertyAccess& p = PropertyAccessOf(node->op());
284 Callable callable = CodeFactory::KeyedLoadICInOptimizedCode(
285 isolate(), p.language_mode(), UNINITIALIZED);
286 node->InsertInput(zone(), 2, jsgraph()->SmiConstant(p.feedback().index()));
287 ReplaceWithStubCall(node, callable, flags);
288 }
289
290
LowerJSLoadNamed(Node * node)291 void JSGenericLowering::LowerJSLoadNamed(Node* node) {
292 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
293 NamedAccess const& p = NamedAccessOf(node->op());
294 Callable callable = CodeFactory::LoadICInOptimizedCode(
295 isolate(), NOT_INSIDE_TYPEOF, p.language_mode(), UNINITIALIZED);
296 node->InsertInput(zone(), 1, jsgraph()->HeapConstant(p.name()));
297 node->InsertInput(zone(), 2, jsgraph()->SmiConstant(p.feedback().index()));
298 ReplaceWithStubCall(node, callable, flags);
299 }
300
301
LowerJSLoadGlobal(Node * node)302 void JSGenericLowering::LowerJSLoadGlobal(Node* node) {
303 Node* context = NodeProperties::GetContextInput(node);
304 Node* effect = NodeProperties::GetEffectInput(node);
305 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
306 const LoadGlobalParameters& p = LoadGlobalParametersOf(node->op());
307 Callable callable = CodeFactory::LoadICInOptimizedCode(
308 isolate(), p.typeof_mode(), SLOPPY, UNINITIALIZED);
309 // Load global object from the context.
310 Node* native_context =
311 graph()->NewNode(machine()->Load(MachineType::AnyTagged()), context,
312 jsgraph()->IntPtrConstant(
313 Context::SlotOffset(Context::NATIVE_CONTEXT_INDEX)),
314 effect, graph()->start());
315 Node* global = graph()->NewNode(
316 machine()->Load(MachineType::AnyTagged()), native_context,
317 jsgraph()->IntPtrConstant(Context::SlotOffset(Context::EXTENSION_INDEX)),
318 effect, graph()->start());
319 node->InsertInput(zone(), 0, global);
320 node->InsertInput(zone(), 1, jsgraph()->HeapConstant(p.name()));
321 node->InsertInput(zone(), 2, jsgraph()->SmiConstant(p.feedback().index()));
322 ReplaceWithStubCall(node, callable, flags);
323 }
324
325
LowerJSStoreProperty(Node * node)326 void JSGenericLowering::LowerJSStoreProperty(Node* node) {
327 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
328 PropertyAccess const& p = PropertyAccessOf(node->op());
329 LanguageMode language_mode = p.language_mode();
330 Callable callable = CodeFactory::KeyedStoreICInOptimizedCode(
331 isolate(), language_mode, UNINITIALIZED);
332 DCHECK(p.feedback().index() != -1);
333 node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.feedback().index()));
334 ReplaceWithStubCall(node, callable,
335 CallDescriptor::kPatchableCallSite | flags);
336 }
337
338
LowerJSStoreNamed(Node * node)339 void JSGenericLowering::LowerJSStoreNamed(Node* node) {
340 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
341 NamedAccess const& p = NamedAccessOf(node->op());
342 Callable callable = CodeFactory::StoreICInOptimizedCode(
343 isolate(), p.language_mode(), UNINITIALIZED);
344 node->InsertInput(zone(), 1, jsgraph()->HeapConstant(p.name()));
345 DCHECK(p.feedback().index() != -1);
346 node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.feedback().index()));
347 ReplaceWithStubCall(node, callable,
348 CallDescriptor::kPatchableCallSite | flags);
349 }
350
351
LowerJSStoreGlobal(Node * node)352 void JSGenericLowering::LowerJSStoreGlobal(Node* node) {
353 Node* context = NodeProperties::GetContextInput(node);
354 Node* effect = NodeProperties::GetEffectInput(node);
355 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
356 const StoreGlobalParameters& p = StoreGlobalParametersOf(node->op());
357 Callable callable = CodeFactory::StoreICInOptimizedCode(
358 isolate(), p.language_mode(), UNINITIALIZED);
359 // Load global object from the context.
360 Node* native_context =
361 graph()->NewNode(machine()->Load(MachineType::AnyTagged()), context,
362 jsgraph()->IntPtrConstant(
363 Context::SlotOffset(Context::NATIVE_CONTEXT_INDEX)),
364 effect, graph()->start());
365 Node* global = graph()->NewNode(
366 machine()->Load(MachineType::AnyTagged()), native_context,
367 jsgraph()->IntPtrConstant(Context::SlotOffset(Context::EXTENSION_INDEX)),
368 effect, graph()->start());
369 node->InsertInput(zone(), 0, global);
370 node->InsertInput(zone(), 1, jsgraph()->HeapConstant(p.name()));
371 DCHECK(p.feedback().index() != -1);
372 node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.feedback().index()));
373 ReplaceWithStubCall(node, callable,
374 CallDescriptor::kPatchableCallSite | flags);
375 }
376
377
LowerJSDeleteProperty(Node * node)378 void JSGenericLowering::LowerJSDeleteProperty(Node* node) {
379 LanguageMode language_mode = OpParameter<LanguageMode>(node);
380 ReplaceWithRuntimeCall(node, is_strict(language_mode)
381 ? Runtime::kDeleteProperty_Strict
382 : Runtime::kDeleteProperty_Sloppy);
383 }
384
385
LowerJSHasProperty(Node * node)386 void JSGenericLowering::LowerJSHasProperty(Node* node) {
387 ReplaceWithRuntimeCall(node, Runtime::kHasProperty);
388 }
389
390
LowerJSInstanceOf(Node * node)391 void JSGenericLowering::LowerJSInstanceOf(Node* node) {
392 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
393 Callable callable = CodeFactory::InstanceOf(isolate());
394 ReplaceWithStubCall(node, callable, flags);
395 }
396
397
LowerJSLoadContext(Node * node)398 void JSGenericLowering::LowerJSLoadContext(Node* node) {
399 const ContextAccess& access = ContextAccessOf(node->op());
400 for (size_t i = 0; i < access.depth(); ++i) {
401 node->ReplaceInput(
402 0, graph()->NewNode(machine()->Load(MachineType::AnyTagged()),
403 NodeProperties::GetValueInput(node, 0),
404 jsgraph()->Int32Constant(
405 Context::SlotOffset(Context::PREVIOUS_INDEX)),
406 NodeProperties::GetEffectInput(node),
407 graph()->start()));
408 }
409 node->ReplaceInput(1, jsgraph()->Int32Constant(Context::SlotOffset(
410 static_cast<int>(access.index()))));
411 node->AppendInput(zone(), graph()->start());
412 NodeProperties::ChangeOp(node, machine()->Load(MachineType::AnyTagged()));
413 }
414
415
LowerJSStoreContext(Node * node)416 void JSGenericLowering::LowerJSStoreContext(Node* node) {
417 const ContextAccess& access = ContextAccessOf(node->op());
418 for (size_t i = 0; i < access.depth(); ++i) {
419 node->ReplaceInput(
420 0, graph()->NewNode(machine()->Load(MachineType::AnyTagged()),
421 NodeProperties::GetValueInput(node, 0),
422 jsgraph()->Int32Constant(
423 Context::SlotOffset(Context::PREVIOUS_INDEX)),
424 NodeProperties::GetEffectInput(node),
425 graph()->start()));
426 }
427 node->ReplaceInput(2, NodeProperties::GetValueInput(node, 1));
428 node->ReplaceInput(1, jsgraph()->Int32Constant(Context::SlotOffset(
429 static_cast<int>(access.index()))));
430 NodeProperties::ChangeOp(
431 node, machine()->Store(StoreRepresentation(MachineRepresentation::kTagged,
432 kFullWriteBarrier)));
433 }
434
435
LowerJSLoadDynamic(Node * node)436 void JSGenericLowering::LowerJSLoadDynamic(Node* node) {
437 const DynamicAccess& access = DynamicAccessOf(node->op());
438 Runtime::FunctionId function_id =
439 (access.typeof_mode() == NOT_INSIDE_TYPEOF)
440 ? Runtime::kLoadLookupSlot
441 : Runtime::kLoadLookupSlotNoReferenceError;
442 Node* projection = graph()->NewNode(common()->Projection(0), node);
443 NodeProperties::ReplaceUses(node, projection, node, node, node);
444 node->RemoveInput(NodeProperties::FirstValueIndex(node));
445 node->InsertInput(zone(), 1, jsgraph()->Constant(access.name()));
446 ReplaceWithRuntimeCall(node, function_id);
447 projection->ReplaceInput(0, node);
448 }
449
450
LowerJSCreate(Node * node)451 void JSGenericLowering::LowerJSCreate(Node* node) {
452 ReplaceWithRuntimeCall(node, Runtime::kNewObject);
453 }
454
455
LowerJSCreateArguments(Node * node)456 void JSGenericLowering::LowerJSCreateArguments(Node* node) {
457 const CreateArgumentsParameters& p = CreateArgumentsParametersOf(node->op());
458 switch (p.type()) {
459 case CreateArgumentsParameters::kMappedArguments:
460 ReplaceWithRuntimeCall(node, Runtime::kNewSloppyArguments_Generic);
461 break;
462 case CreateArgumentsParameters::kUnmappedArguments:
463 ReplaceWithRuntimeCall(node, Runtime::kNewStrictArguments_Generic);
464 break;
465 case CreateArgumentsParameters::kRestArray:
466 node->InsertInput(zone(), 1, jsgraph()->Constant(p.start_index()));
467 ReplaceWithRuntimeCall(node, Runtime::kNewRestArguments_Generic);
468 break;
469 }
470 }
471
472
LowerJSCreateArray(Node * node)473 void JSGenericLowering::LowerJSCreateArray(Node* node) {
474 CreateArrayParameters const& p = CreateArrayParametersOf(node->op());
475 int const arity = static_cast<int>(p.arity());
476 Node* new_target = node->InputAt(1);
477 // TODO(turbofan): We embed the AllocationSite from the Operator at this
478 // point, which we should not do once we want to both consume the feedback
479 // but at the same time shared the optimized code across native contexts,
480 // as the AllocationSite is associated with a single native context (it's
481 // stored in the type feedback vector after all). Once we go for cross
482 // context code generation, we should somehow find a way to get to the
483 // allocation site for the actual native context at runtime.
484 Node* type_info = p.site().is_null() ? jsgraph()->UndefinedConstant()
485 : jsgraph()->HeapConstant(p.site());
486 node->RemoveInput(1);
487 node->InsertInput(zone(), 1 + arity, new_target);
488 node->InsertInput(zone(), 2 + arity, type_info);
489 ReplaceWithRuntimeCall(node, Runtime::kNewArray, arity + 3);
490 }
491
492
LowerJSCreateClosure(Node * node)493 void JSGenericLowering::LowerJSCreateClosure(Node* node) {
494 CreateClosureParameters p = CreateClosureParametersOf(node->op());
495 node->InsertInput(zone(), 0, jsgraph()->HeapConstant(p.shared_info()));
496 ReplaceWithRuntimeCall(node, (p.pretenure() == TENURED)
497 ? Runtime::kNewClosure_Tenured
498 : Runtime::kNewClosure);
499 }
500
501
LowerJSCreateIterResultObject(Node * node)502 void JSGenericLowering::LowerJSCreateIterResultObject(Node* node) {
503 ReplaceWithRuntimeCall(node, Runtime::kCreateIterResultObject);
504 }
505
506
LowerJSCreateLiteralArray(Node * node)507 void JSGenericLowering::LowerJSCreateLiteralArray(Node* node) {
508 CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());
509 node->InsertInput(zone(), 1, jsgraph()->SmiConstant(p.index()));
510 node->InsertInput(zone(), 2, jsgraph()->HeapConstant(p.constant()));
511 node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.flags()));
512 ReplaceWithRuntimeCall(node, Runtime::kCreateArrayLiteral);
513 }
514
515
LowerJSCreateLiteralObject(Node * node)516 void JSGenericLowering::LowerJSCreateLiteralObject(Node* node) {
517 CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());
518 node->InsertInput(zone(), 1, jsgraph()->SmiConstant(p.index()));
519 node->InsertInput(zone(), 2, jsgraph()->HeapConstant(p.constant()));
520 node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.flags()));
521 ReplaceWithRuntimeCall(node, Runtime::kCreateObjectLiteral);
522 }
523
524
LowerJSCreateLiteralRegExp(Node * node)525 void JSGenericLowering::LowerJSCreateLiteralRegExp(Node* node) {
526 CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());
527 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
528 Callable callable = CodeFactory::FastCloneRegExp(isolate());
529 Node* literal_index = jsgraph()->SmiConstant(p.index());
530 Node* literal_flags = jsgraph()->SmiConstant(p.flags());
531 Node* pattern = jsgraph()->HeapConstant(p.constant());
532 node->InsertInput(graph()->zone(), 1, literal_index);
533 node->InsertInput(graph()->zone(), 2, pattern);
534 node->InsertInput(graph()->zone(), 3, literal_flags);
535 ReplaceWithStubCall(node, callable, flags);
536 }
537
538
LowerJSCreateCatchContext(Node * node)539 void JSGenericLowering::LowerJSCreateCatchContext(Node* node) {
540 Handle<String> name = OpParameter<Handle<String>>(node);
541 node->InsertInput(zone(), 0, jsgraph()->HeapConstant(name));
542 ReplaceWithRuntimeCall(node, Runtime::kPushCatchContext);
543 }
544
545
LowerJSCreateBlockContext(Node * node)546 void JSGenericLowering::LowerJSCreateBlockContext(Node* node) {
547 Handle<ScopeInfo> scope_info = OpParameter<Handle<ScopeInfo>>(node);
548 node->InsertInput(zone(), 0, jsgraph()->HeapConstant(scope_info));
549 ReplaceWithRuntimeCall(node, Runtime::kPushBlockContext);
550 }
551
552
LowerJSCreateScriptContext(Node * node)553 void JSGenericLowering::LowerJSCreateScriptContext(Node* node) {
554 Handle<ScopeInfo> scope_info = OpParameter<Handle<ScopeInfo>>(node);
555 node->InsertInput(zone(), 1, jsgraph()->HeapConstant(scope_info));
556 ReplaceWithRuntimeCall(node, Runtime::kNewScriptContext);
557 }
558
559
LowerJSCallConstruct(Node * node)560 void JSGenericLowering::LowerJSCallConstruct(Node* node) {
561 CallConstructParameters const& p = CallConstructParametersOf(node->op());
562 int const arg_count = static_cast<int>(p.arity() - 2);
563 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
564 Callable callable = CodeFactory::Construct(isolate());
565 CallDescriptor* desc = Linkage::GetStubCallDescriptor(
566 isolate(), zone(), callable.descriptor(), arg_count + 1, flags);
567 Node* stub_code = jsgraph()->HeapConstant(callable.code());
568 Node* stub_arity = jsgraph()->Int32Constant(arg_count);
569 Node* new_target = node->InputAt(arg_count + 1);
570 Node* receiver = jsgraph()->UndefinedConstant();
571 node->RemoveInput(arg_count + 1); // Drop new target.
572 node->InsertInput(zone(), 0, stub_code);
573 node->InsertInput(zone(), 2, new_target);
574 node->InsertInput(zone(), 3, stub_arity);
575 node->InsertInput(zone(), 4, receiver);
576 NodeProperties::ChangeOp(node, common()->Call(desc));
577 }
578
579
LowerJSCallFunction(Node * node)580 void JSGenericLowering::LowerJSCallFunction(Node* node) {
581 CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
582 int const arg_count = static_cast<int>(p.arity() - 2);
583 ConvertReceiverMode const mode = p.convert_mode();
584 Callable callable = CodeFactory::Call(isolate(), mode);
585 CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
586 if (p.tail_call_mode() == TailCallMode::kAllow) {
587 flags |= CallDescriptor::kSupportsTailCalls;
588 }
589 CallDescriptor* desc = Linkage::GetStubCallDescriptor(
590 isolate(), zone(), callable.descriptor(), arg_count + 1, flags);
591 Node* stub_code = jsgraph()->HeapConstant(callable.code());
592 Node* stub_arity = jsgraph()->Int32Constant(arg_count);
593 node->InsertInput(zone(), 0, stub_code);
594 node->InsertInput(zone(), 2, stub_arity);
595 NodeProperties::ChangeOp(node, common()->Call(desc));
596 }
597
598
LowerJSCallRuntime(Node * node)599 void JSGenericLowering::LowerJSCallRuntime(Node* node) {
600 const CallRuntimeParameters& p = CallRuntimeParametersOf(node->op());
601 AdjustFrameStatesForCall(node);
602 ReplaceWithRuntimeCall(node, p.id(), static_cast<int>(p.arity()));
603 }
604
605
LowerJSForInDone(Node * node)606 void JSGenericLowering::LowerJSForInDone(Node* node) {
607 ReplaceWithRuntimeCall(node, Runtime::kForInDone);
608 }
609
610
LowerJSForInNext(Node * node)611 void JSGenericLowering::LowerJSForInNext(Node* node) {
612 ReplaceWithRuntimeCall(node, Runtime::kForInNext);
613 }
614
615
LowerJSForInPrepare(Node * node)616 void JSGenericLowering::LowerJSForInPrepare(Node* node) {
617 Node* object = NodeProperties::GetValueInput(node, 0);
618 Node* context = NodeProperties::GetContextInput(node);
619 Node* effect = NodeProperties::GetEffectInput(node);
620 Node* control = NodeProperties::GetControlInput(node);
621 Node* frame_state = NodeProperties::GetFrameStateInput(node, 0);
622
623 // Get the set of properties to enumerate.
624 Runtime::Function const* function =
625 Runtime::FunctionForId(Runtime::kGetPropertyNamesFast);
626 CallDescriptor const* descriptor = Linkage::GetRuntimeCallDescriptor(
627 zone(), function->function_id, 1, Operator::kNoProperties,
628 CallDescriptor::kNeedsFrameState);
629 Node* cache_type = effect = graph()->NewNode(
630 common()->Call(descriptor),
631 jsgraph()->CEntryStubConstant(function->result_size), object,
632 jsgraph()->ExternalConstant(function->function_id),
633 jsgraph()->Int32Constant(1), context, frame_state, effect, control);
634 control = graph()->NewNode(common()->IfSuccess(), cache_type);
635
636 Node* object_map = effect = graph()->NewNode(
637 machine()->Load(MachineType::AnyTagged()), object,
638 jsgraph()->IntPtrConstant(HeapObject::kMapOffset - kHeapObjectTag),
639 effect, control);
640 Node* cache_type_map = effect = graph()->NewNode(
641 machine()->Load(MachineType::AnyTagged()), cache_type,
642 jsgraph()->IntPtrConstant(HeapObject::kMapOffset - kHeapObjectTag),
643 effect, control);
644 Node* meta_map = jsgraph()->HeapConstant(isolate()->factory()->meta_map());
645
646 // If we got a map from the GetPropertyNamesFast runtime call, we can do a
647 // fast modification check. Otherwise, we got a fixed array, and we have to
648 // perform a slow check on every iteration.
649 Node* check0 =
650 graph()->NewNode(machine()->WordEqual(), cache_type_map, meta_map);
651 Node* branch0 =
652 graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);
653
654 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
655 Node* cache_array_true0;
656 Node* cache_length_true0;
657 Node* cache_type_true0;
658 Node* etrue0;
659 {
660 // Enum cache case.
661 Node* cache_type_enum_length = etrue0 = graph()->NewNode(
662 machine()->Load(MachineType::Uint32()), cache_type,
663 jsgraph()->IntPtrConstant(Map::kBitField3Offset - kHeapObjectTag),
664 effect, if_true0);
665 cache_type_enum_length =
666 graph()->NewNode(machine()->Word32And(), cache_type_enum_length,
667 jsgraph()->Uint32Constant(Map::EnumLengthBits::kMask));
668
669 Node* check1 =
670 graph()->NewNode(machine()->Word32Equal(), cache_type_enum_length,
671 jsgraph()->Int32Constant(0));
672 Node* branch1 =
673 graph()->NewNode(common()->Branch(BranchHint::kTrue), check1, if_true0);
674
675 Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
676 Node* cache_array_true1;
677 Node* etrue1;
678 {
679 // No properties to enumerate.
680 cache_array_true1 =
681 jsgraph()->HeapConstant(isolate()->factory()->empty_fixed_array());
682 etrue1 = etrue0;
683 }
684
685 Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
686 Node* cache_array_false1;
687 Node* efalse1;
688 {
689 // Load the enumeration cache from the instance descriptors of {object}.
690 Node* object_map_descriptors = efalse1 = graph()->NewNode(
691 machine()->Load(MachineType::AnyTagged()), object_map,
692 jsgraph()->IntPtrConstant(Map::kDescriptorsOffset - kHeapObjectTag),
693 etrue0, if_false1);
694 Node* object_map_enum_cache = efalse1 = graph()->NewNode(
695 machine()->Load(MachineType::AnyTagged()), object_map_descriptors,
696 jsgraph()->IntPtrConstant(DescriptorArray::kEnumCacheOffset -
697 kHeapObjectTag),
698 efalse1, if_false1);
699 cache_array_false1 = efalse1 = graph()->NewNode(
700 machine()->Load(MachineType::AnyTagged()), object_map_enum_cache,
701 jsgraph()->IntPtrConstant(
702 DescriptorArray::kEnumCacheBridgeCacheOffset - kHeapObjectTag),
703 efalse1, if_false1);
704 }
705
706 if_true0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
707 etrue0 =
708 graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_true0);
709 cache_array_true0 =
710 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
711 cache_array_true1, cache_array_false1, if_true0);
712
713 cache_length_true0 = graph()->NewNode(
714 machine()->WordShl(),
715 machine()->Is64()
716 ? graph()->NewNode(machine()->ChangeUint32ToUint64(),
717 cache_type_enum_length)
718 : cache_type_enum_length,
719 jsgraph()->Int32Constant(kSmiShiftSize + kSmiTagSize));
720 cache_type_true0 = cache_type;
721 }
722
723 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
724 Node* cache_array_false0;
725 Node* cache_length_false0;
726 Node* cache_type_false0;
727 Node* efalse0;
728 {
729 // FixedArray case.
730 cache_type_false0 = jsgraph()->OneConstant(); // Smi means slow check
731 cache_array_false0 = cache_type;
732 cache_length_false0 = efalse0 = graph()->NewNode(
733 machine()->Load(MachineType::AnyTagged()), cache_array_false0,
734 jsgraph()->IntPtrConstant(FixedArray::kLengthOffset - kHeapObjectTag),
735 effect, if_false0);
736 }
737
738 control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
739 effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
740 Node* cache_array =
741 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
742 cache_array_true0, cache_array_false0, control);
743 Node* cache_length =
744 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
745 cache_length_true0, cache_length_false0, control);
746 cache_type =
747 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
748 cache_type_true0, cache_type_false0, control);
749
750 for (auto edge : node->use_edges()) {
751 if (NodeProperties::IsEffectEdge(edge)) {
752 edge.UpdateTo(effect);
753 } else if (NodeProperties::IsControlEdge(edge)) {
754 Node* const use = edge.from();
755 if (use->opcode() == IrOpcode::kIfSuccess) {
756 use->ReplaceUses(control);
757 use->Kill();
758 } else if (use->opcode() == IrOpcode::kIfException) {
759 edge.UpdateTo(cache_type_true0);
760 } else {
761 UNREACHABLE();
762 }
763 } else {
764 Node* const use = edge.from();
765 DCHECK(NodeProperties::IsValueEdge(edge));
766 DCHECK_EQ(IrOpcode::kProjection, use->opcode());
767 switch (ProjectionIndexOf(use->op())) {
768 case 0:
769 use->ReplaceUses(cache_type);
770 break;
771 case 1:
772 use->ReplaceUses(cache_array);
773 break;
774 case 2:
775 use->ReplaceUses(cache_length);
776 break;
777 default:
778 UNREACHABLE();
779 break;
780 }
781 use->Kill();
782 }
783 }
784 }
785
786
LowerJSForInStep(Node * node)787 void JSGenericLowering::LowerJSForInStep(Node* node) {
788 ReplaceWithRuntimeCall(node, Runtime::kForInStep);
789 }
790
791
LowerJSLoadMessage(Node * node)792 void JSGenericLowering::LowerJSLoadMessage(Node* node) {
793 ExternalReference message_address =
794 ExternalReference::address_of_pending_message_obj(isolate());
795 node->RemoveInput(NodeProperties::FirstContextIndex(node));
796 node->InsertInput(zone(), 0, jsgraph()->ExternalConstant(message_address));
797 node->InsertInput(zone(), 1, jsgraph()->IntPtrConstant(0));
798 NodeProperties::ChangeOp(node, machine()->Load(MachineType::AnyTagged()));
799 }
800
801
LowerJSStoreMessage(Node * node)802 void JSGenericLowering::LowerJSStoreMessage(Node* node) {
803 ExternalReference message_address =
804 ExternalReference::address_of_pending_message_obj(isolate());
805 node->RemoveInput(NodeProperties::FirstContextIndex(node));
806 node->InsertInput(zone(), 0, jsgraph()->ExternalConstant(message_address));
807 node->InsertInput(zone(), 1, jsgraph()->IntPtrConstant(0));
808 StoreRepresentation representation(MachineRepresentation::kTagged,
809 kNoWriteBarrier);
810 NodeProperties::ChangeOp(node, machine()->Store(representation));
811 }
812
813
LowerJSYield(Node * node)814 void JSGenericLowering::LowerJSYield(Node* node) { UNIMPLEMENTED(); }
815
816
LowerJSStackCheck(Node * node)817 void JSGenericLowering::LowerJSStackCheck(Node* node) {
818 Node* effect = NodeProperties::GetEffectInput(node);
819 Node* control = NodeProperties::GetControlInput(node);
820
821 Node* limit = graph()->NewNode(
822 machine()->Load(MachineType::Pointer()),
823 jsgraph()->ExternalConstant(
824 ExternalReference::address_of_stack_limit(isolate())),
825 jsgraph()->IntPtrConstant(0), effect, control);
826 Node* pointer = graph()->NewNode(machine()->LoadStackPointer());
827
828 Node* check = graph()->NewNode(machine()->UintLessThan(), limit, pointer);
829 Node* branch =
830 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
831
832 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
833 Node* etrue = effect;
834
835 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
836 NodeProperties::ReplaceControlInput(node, if_false);
837 Node* efalse = node;
838
839 Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
840 Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge);
841
842 // Wire the new diamond into the graph, {node} can still throw.
843 NodeProperties::ReplaceUses(node, node, ephi, node, node);
844 NodeProperties::ReplaceEffectInput(ephi, efalse, 1);
845
846 // TODO(mstarzinger): This iteration cuts out the IfSuccess projection from
847 // the node and places it inside the diamond. Come up with a helper method!
848 for (Node* use : node->uses()) {
849 if (use->opcode() == IrOpcode::kIfSuccess) {
850 use->ReplaceUses(merge);
851 merge->ReplaceInput(1, use);
852 }
853 }
854
855 // Turn the stack check into a runtime call.
856 ReplaceWithRuntimeCall(node, Runtime::kStackGuard);
857 }
858
859
zone() const860 Zone* JSGenericLowering::zone() const { return graph()->zone(); }
861
862
isolate() const863 Isolate* JSGenericLowering::isolate() const { return jsgraph()->isolate(); }
864
865
graph() const866 Graph* JSGenericLowering::graph() const { return jsgraph()->graph(); }
867
868
common() const869 CommonOperatorBuilder* JSGenericLowering::common() const {
870 return jsgraph()->common();
871 }
872
873
machine() const874 MachineOperatorBuilder* JSGenericLowering::machine() const {
875 return jsgraph()->machine();
876 }
877
878 } // namespace compiler
879 } // namespace internal
880 } // namespace v8
881