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 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 32 JSGenericLowering::JSGenericLowering(bool is_typing_enabled, JSGraph* jsgraph) 33 : is_typing_enabled_(is_typing_enabled), jsgraph_(jsgraph) {} 34 35 36 JSGenericLowering::~JSGenericLowering() {} 37 38 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 } 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 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 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 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 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 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 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 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 269 void JSGenericLowering::LowerJSToName(Node* node) { 270 ReplaceWithRuntimeCall(node, Runtime::kToName); 271 } 272 273 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 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 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 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 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 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 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 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 386 void JSGenericLowering::LowerJSHasProperty(Node* node) { 387 ReplaceWithRuntimeCall(node, Runtime::kHasProperty); 388 } 389 390 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 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 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 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 451 void JSGenericLowering::LowerJSCreate(Node* node) { 452 ReplaceWithRuntimeCall(node, Runtime::kNewObject); 453 } 454 455 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 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 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 502 void JSGenericLowering::LowerJSCreateIterResultObject(Node* node) { 503 ReplaceWithRuntimeCall(node, Runtime::kCreateIterResultObject); 504 } 505 506 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 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 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 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 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 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 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 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 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 606 void JSGenericLowering::LowerJSForInDone(Node* node) { 607 ReplaceWithRuntimeCall(node, Runtime::kForInDone); 608 } 609 610 611 void JSGenericLowering::LowerJSForInNext(Node* node) { 612 ReplaceWithRuntimeCall(node, Runtime::kForInNext); 613 } 614 615 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 787 void JSGenericLowering::LowerJSForInStep(Node* node) { 788 ReplaceWithRuntimeCall(node, Runtime::kForInStep); 789 } 790 791 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 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 814 void JSGenericLowering::LowerJSYield(Node* node) { UNIMPLEMENTED(); } 815 816 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 860 Zone* JSGenericLowering::zone() const { return graph()->zone(); } 861 862 863 Isolate* JSGenericLowering::isolate() const { return jsgraph()->isolate(); } 864 865 866 Graph* JSGenericLowering::graph() const { return jsgraph()->graph(); } 867 868 869 CommonOperatorBuilder* JSGenericLowering::common() const { 870 return jsgraph()->common(); 871 } 872 873 874 MachineOperatorBuilder* JSGenericLowering::machine() const { 875 return jsgraph()->machine(); 876 } 877 878 } // namespace compiler 879 } // namespace internal 880 } // namespace v8 881