1// Copyright 2015 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/compiler/js-intrinsic-lowering.h" 6 7#include <stack> 8 9#include "src/codegen/code-factory.h" 10#include "src/compiler/access-builder.h" 11#include "src/compiler/js-graph.h" 12#include "src/compiler/js-heap-broker.h" 13#include "src/compiler/linkage.h" 14#include "src/compiler/node-matchers.h" 15#include "src/compiler/node-properties.h" 16#include "src/compiler/operator-properties.h" 17#include "src/logging/counters.h" 18#include "src/objects/js-generator.h" 19#include "src/objects/objects-inl.h" 20 21namespace v8 { 22namespace internal { 23namespace compiler { 24 25JSIntrinsicLowering::JSIntrinsicLowering(Editor* editor, JSGraph* jsgraph, 26 JSHeapBroker* broker) 27 : AdvancedReducer(editor), jsgraph_(jsgraph), broker_(broker) {} 28 29Reduction JSIntrinsicLowering::Reduce(Node* node) { 30 if (node->opcode() != IrOpcode::kJSCallRuntime) return NoChange(); 31 const Runtime::Function* const f = 32 Runtime::FunctionForId(CallRuntimeParametersOf(node->op()).id()); 33 switch (f->function_id) { 34 case Runtime::kIsBeingInterpreted: 35 return ReduceIsBeingInterpreted(node); 36 case Runtime::kTurbofanStaticAssert: 37 return ReduceTurbofanStaticAssert(node); 38 case Runtime::kVerifyType: 39 return ReduceVerifyType(node); 40 default: 41 break; 42 } 43 if (f->intrinsic_type != Runtime::IntrinsicType::INLINE) return NoChange(); 44 switch (f->function_id) { 45 case Runtime::kInlineCopyDataProperties: 46 return ReduceCopyDataProperties(node); 47 case Runtime::kInlineCopyDataPropertiesWithExcludedPropertiesOnStack: 48 return ReduceCopyDataPropertiesWithExcludedPropertiesOnStack(node); 49 case Runtime::kInlineCreateIterResultObject: 50 return ReduceCreateIterResultObject(node); 51 case Runtime::kInlineDeoptimizeNow: 52 return ReduceDeoptimizeNow(node); 53 case Runtime::kInlineGeneratorClose: 54 return ReduceGeneratorClose(node); 55 case Runtime::kInlineCreateJSGeneratorObject: 56 return ReduceCreateJSGeneratorObject(node); 57 case Runtime::kInlineAsyncFunctionAwaitCaught: 58 return ReduceAsyncFunctionAwaitCaught(node); 59 case Runtime::kInlineAsyncFunctionAwaitUncaught: 60 return ReduceAsyncFunctionAwaitUncaught(node); 61 case Runtime::kInlineAsyncFunctionEnter: 62 return ReduceAsyncFunctionEnter(node); 63 case Runtime::kInlineAsyncFunctionReject: 64 return ReduceAsyncFunctionReject(node); 65 case Runtime::kInlineAsyncFunctionResolve: 66 return ReduceAsyncFunctionResolve(node); 67 case Runtime::kInlineAsyncGeneratorAwaitCaught: 68 return ReduceAsyncGeneratorAwaitCaught(node); 69 case Runtime::kInlineAsyncGeneratorAwaitUncaught: 70 return ReduceAsyncGeneratorAwaitUncaught(node); 71 case Runtime::kInlineAsyncGeneratorReject: 72 return ReduceAsyncGeneratorReject(node); 73 case Runtime::kInlineAsyncGeneratorResolve: 74 return ReduceAsyncGeneratorResolve(node); 75 case Runtime::kInlineAsyncGeneratorYield: 76 return ReduceAsyncGeneratorYield(node); 77 case Runtime::kInlineGeneratorGetResumeMode: 78 return ReduceGeneratorGetResumeMode(node); 79 case Runtime::kInlineIncBlockCounter: 80 return ReduceIncBlockCounter(node); 81 case Runtime::kInlineGetImportMetaObject: 82 return ReduceGetImportMetaObject(node); 83 default: 84 break; 85 } 86 return NoChange(); 87} 88 89Reduction JSIntrinsicLowering::ReduceCopyDataProperties(Node* node) { 90 return Change( 91 node, Builtins::CallableFor(isolate(), Builtin::kCopyDataProperties), 0); 92} 93 94Reduction 95JSIntrinsicLowering::ReduceCopyDataPropertiesWithExcludedPropertiesOnStack( 96 Node* node) { 97 int input_count = 98 static_cast<int>(CallRuntimeParametersOf(node->op()).arity()); 99 CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState; 100 auto callable = Builtins::CallableFor( 101 isolate(), Builtin::kCopyDataPropertiesWithExcludedProperties); 102 auto call_descriptor = Linkage::GetStubCallDescriptor( 103 graph()->zone(), callable.descriptor(), input_count - 1, flags, 104 node->op()->properties()); 105 node->InsertInput(graph()->zone(), 0, 106 jsgraph()->HeapConstant(callable.code())); 107 node->InsertInput(graph()->zone(), 2, 108 jsgraph()->SmiConstant(input_count - 1)); 109 NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); 110 return Changed(node); 111} 112 113Reduction JSIntrinsicLowering::ReduceCreateIterResultObject(Node* node) { 114 Node* const value = NodeProperties::GetValueInput(node, 0); 115 Node* const done = NodeProperties::GetValueInput(node, 1); 116 Node* const context = NodeProperties::GetContextInput(node); 117 Node* const effect = NodeProperties::GetEffectInput(node); 118 return Change(node, javascript()->CreateIterResultObject(), value, done, 119 context, effect); 120} 121 122Reduction JSIntrinsicLowering::ReduceDeoptimizeNow(Node* node) { 123 Node* const frame_state = NodeProperties::GetFrameStateInput(node); 124 Node* const effect = NodeProperties::GetEffectInput(node); 125 Node* const control = NodeProperties::GetControlInput(node); 126 127 // TODO(bmeurer): Move MergeControlToEnd() to the AdvancedReducer. 128 Node* deoptimize = graph()->NewNode( 129 common()->Deoptimize(DeoptimizeReason::kDeoptimizeNow, FeedbackSource()), 130 frame_state, effect, control); 131 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); 132 Revisit(graph()->end()); 133 134 node->TrimInputCount(0); 135 NodeProperties::ChangeOp(node, common()->Dead()); 136 return Changed(node); 137} 138 139Reduction JSIntrinsicLowering::ReduceCreateJSGeneratorObject(Node* node) { 140 Node* const closure = NodeProperties::GetValueInput(node, 0); 141 Node* const receiver = NodeProperties::GetValueInput(node, 1); 142 Node* const context = NodeProperties::GetContextInput(node); 143 Node* const effect = NodeProperties::GetEffectInput(node); 144 Node* const control = NodeProperties::GetControlInput(node); 145 Operator const* const op = javascript()->CreateGeneratorObject(); 146 Node* create_generator = 147 graph()->NewNode(op, closure, receiver, context, effect, control); 148 ReplaceWithValue(node, create_generator, create_generator); 149 return Changed(create_generator); 150} 151 152Reduction JSIntrinsicLowering::ReduceGeneratorClose(Node* node) { 153 Node* const generator = NodeProperties::GetValueInput(node, 0); 154 Node* const effect = NodeProperties::GetEffectInput(node); 155 Node* const control = NodeProperties::GetControlInput(node); 156 Node* const closed = jsgraph()->Constant(JSGeneratorObject::kGeneratorClosed); 157 Node* const undefined = jsgraph()->UndefinedConstant(); 158 Operator const* const op = simplified()->StoreField( 159 AccessBuilder::ForJSGeneratorObjectContinuation()); 160 161 ReplaceWithValue(node, undefined, node); 162 NodeProperties::RemoveType(node); 163 return Change(node, op, generator, closed, effect, control); 164} 165 166Reduction JSIntrinsicLowering::ReduceAsyncFunctionAwaitCaught(Node* node) { 167 return Change( 168 node, 169 Builtins::CallableFor(isolate(), Builtin::kAsyncFunctionAwaitCaught), 0); 170} 171 172Reduction JSIntrinsicLowering::ReduceAsyncFunctionAwaitUncaught(Node* node) { 173 return Change( 174 node, 175 Builtins::CallableFor(isolate(), Builtin::kAsyncFunctionAwaitUncaught), 176 0); 177} 178 179Reduction JSIntrinsicLowering::ReduceAsyncFunctionEnter(Node* node) { 180 NodeProperties::ChangeOp(node, javascript()->AsyncFunctionEnter()); 181 return Changed(node); 182} 183 184Reduction JSIntrinsicLowering::ReduceAsyncFunctionReject(Node* node) { 185 RelaxControls(node); 186 NodeProperties::ChangeOp(node, javascript()->AsyncFunctionReject()); 187 return Changed(node); 188} 189 190Reduction JSIntrinsicLowering::ReduceAsyncFunctionResolve(Node* node) { 191 RelaxControls(node); 192 NodeProperties::ChangeOp(node, javascript()->AsyncFunctionResolve()); 193 return Changed(node); 194} 195 196Reduction JSIntrinsicLowering::ReduceAsyncGeneratorAwaitCaught(Node* node) { 197 return Change( 198 node, 199 Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorAwaitCaught), 0); 200} 201 202Reduction JSIntrinsicLowering::ReduceAsyncGeneratorAwaitUncaught(Node* node) { 203 return Change( 204 node, 205 Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorAwaitUncaught), 206 0); 207} 208 209Reduction JSIntrinsicLowering::ReduceAsyncGeneratorReject(Node* node) { 210 return Change( 211 node, Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorReject), 212 0); 213} 214 215Reduction JSIntrinsicLowering::ReduceAsyncGeneratorResolve(Node* node) { 216 return Change( 217 node, Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorResolve), 218 0); 219} 220 221Reduction JSIntrinsicLowering::ReduceAsyncGeneratorYield(Node* node) { 222 return Change( 223 node, Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorYield), 0); 224} 225 226Reduction JSIntrinsicLowering::ReduceGeneratorGetResumeMode(Node* node) { 227 Node* const generator = NodeProperties::GetValueInput(node, 0); 228 Node* const effect = NodeProperties::GetEffectInput(node); 229 Node* const control = NodeProperties::GetControlInput(node); 230 Operator const* const op = 231 simplified()->LoadField(AccessBuilder::ForJSGeneratorObjectResumeMode()); 232 233 return Change(node, op, generator, effect, control); 234} 235 236Reduction JSIntrinsicLowering::ReduceIsInstanceType( 237 Node* node, InstanceType instance_type) { 238 // if (%_IsSmi(value)) { 239 // return false; 240 // } else { 241 // return %_GetInstanceType(%_GetMap(value)) == instance_type; 242 // } 243 Node* value = NodeProperties::GetValueInput(node, 0); 244 Node* effect = NodeProperties::GetEffectInput(node); 245 Node* control = NodeProperties::GetControlInput(node); 246 247 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); 248 Node* branch = graph()->NewNode(common()->Branch(), check, control); 249 250 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 251 Node* etrue = effect; 252 Node* vtrue = jsgraph()->FalseConstant(); 253 254 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 255 Node* efalse = effect; 256 Node* map = efalse = 257 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), value, 258 efalse, if_false); 259 Node* map_instance_type = efalse = graph()->NewNode( 260 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), map, efalse, 261 if_false); 262 Node* vfalse = 263 graph()->NewNode(simplified()->NumberEqual(), map_instance_type, 264 jsgraph()->Constant(instance_type)); 265 266 Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); 267 268 // Replace all effect uses of {node} with the {ephi}. 269 Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge); 270 ReplaceWithValue(node, node, ephi, merge); 271 272 // Turn the {node} into a Phi. 273 return Change(node, common()->Phi(MachineRepresentation::kTagged, 2), vtrue, 274 vfalse, merge); 275} 276 277Reduction JSIntrinsicLowering::ReduceIsJSReceiver(Node* node) { 278 return Change(node, simplified()->ObjectIsReceiver()); 279} 280 281Reduction JSIntrinsicLowering::ReduceTurbofanStaticAssert(Node* node) { 282 if (FLAG_always_opt) { 283 // Ignore static asserts, as we most likely won't have enough information 284 RelaxEffectsAndControls(node); 285 } else { 286 Node* value = NodeProperties::GetValueInput(node, 0); 287 Node* effect = NodeProperties::GetEffectInput(node); 288 Node* assert = graph()->NewNode( 289 common()->StaticAssert("%TurbofanStaticAssert"), value, effect); 290 ReplaceWithValue(node, node, assert, nullptr); 291 } 292 return Changed(jsgraph_->UndefinedConstant()); 293} 294 295Reduction JSIntrinsicLowering::ReduceVerifyType(Node* node) { 296 return Change(node, simplified()->VerifyType()); 297} 298 299Reduction JSIntrinsicLowering::ReduceIsBeingInterpreted(Node* node) { 300 RelaxEffectsAndControls(node); 301 return Changed(jsgraph_->FalseConstant()); 302} 303 304Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op) { 305 // Replace all effect uses of {node} with the effect dependency. 306 RelaxEffectsAndControls(node); 307 // Remove the inputs corresponding to context, effect and control. 308 NodeProperties::RemoveNonValueInputs(node); 309 // Finally update the operator to the new one. 310 NodeProperties::ChangeOp(node, op); 311 return Changed(node); 312} 313 314Reduction JSIntrinsicLowering::ReduceToLength(Node* node) { 315 NodeProperties::ChangeOp(node, javascript()->ToLength()); 316 return Changed(node); 317} 318 319Reduction JSIntrinsicLowering::ReduceToObject(Node* node) { 320 NodeProperties::ChangeOp(node, javascript()->ToObject()); 321 return Changed(node); 322} 323 324Reduction JSIntrinsicLowering::ReduceToString(Node* node) { 325 // ToString is unnecessary if the input is a string. 326 HeapObjectMatcher m(NodeProperties::GetValueInput(node, 0)); 327 if (m.HasResolvedValue() && m.Ref(broker()).IsString()) { 328 ReplaceWithValue(node, m.node()); 329 return Replace(m.node()); 330 } 331 NodeProperties::ChangeOp(node, javascript()->ToString()); 332 return Changed(node); 333} 334 335Reduction JSIntrinsicLowering::ReduceCall(Node* node) { 336 int const arity = 337 static_cast<int>(CallRuntimeParametersOf(node->op()).arity()); 338 static constexpr int kTargetAndReceiver = 2; 339 STATIC_ASSERT(JSCallNode::kFeedbackVectorIsLastInput); 340 Node* feedback = jsgraph()->UndefinedConstant(); 341 node->InsertInput(graph()->zone(), arity, feedback); 342 NodeProperties::ChangeOp( 343 node, 344 javascript()->Call(JSCallNode::ArityForArgc(arity - kTargetAndReceiver))); 345 return Changed(node); 346} 347 348Reduction JSIntrinsicLowering::ReduceIncBlockCounter(Node* node) { 349 DCHECK(!Linkage::NeedsFrameStateInput(Runtime::kIncBlockCounter)); 350 DCHECK(!Linkage::NeedsFrameStateInput(Runtime::kInlineIncBlockCounter)); 351 return Change(node, 352 Builtins::CallableFor(isolate(), Builtin::kIncBlockCounter), 0, 353 kDoesNotNeedFrameState); 354} 355 356Reduction JSIntrinsicLowering::ReduceGetImportMetaObject(Node* node) { 357 NodeProperties::ChangeOp(node, javascript()->GetImportMeta()); 358 return Changed(node); 359} 360 361Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a, 362 Node* b) { 363 RelaxControls(node); 364 node->ReplaceInput(0, a); 365 node->ReplaceInput(1, b); 366 node->TrimInputCount(2); 367 NodeProperties::ChangeOp(node, op); 368 return Changed(node); 369} 370 371Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a, 372 Node* b, Node* c) { 373 RelaxControls(node); 374 node->ReplaceInput(0, a); 375 node->ReplaceInput(1, b); 376 node->ReplaceInput(2, c); 377 node->TrimInputCount(3); 378 NodeProperties::ChangeOp(node, op); 379 return Changed(node); 380} 381 382Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a, 383 Node* b, Node* c, Node* d) { 384 RelaxControls(node); 385 node->ReplaceInput(0, a); 386 node->ReplaceInput(1, b); 387 node->ReplaceInput(2, c); 388 node->ReplaceInput(3, d); 389 node->TrimInputCount(4); 390 NodeProperties::ChangeOp(node, op); 391 return Changed(node); 392} 393 394Reduction JSIntrinsicLowering::Change(Node* node, Callable const& callable, 395 int stack_parameter_count, 396 enum FrameStateFlag frame_state_flag) { 397 CallDescriptor::Flags flags = frame_state_flag == kNeedsFrameState 398 ? CallDescriptor::kNeedsFrameState 399 : CallDescriptor::kNoFlags; 400 auto call_descriptor = Linkage::GetStubCallDescriptor( 401 graph()->zone(), callable.descriptor(), stack_parameter_count, flags, 402 node->op()->properties()); 403 node->InsertInput(graph()->zone(), 0, 404 jsgraph()->HeapConstant(callable.code())); 405 NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); 406 return Changed(node); 407} 408 409Graph* JSIntrinsicLowering::graph() const { return jsgraph()->graph(); } 410 411Isolate* JSIntrinsicLowering::isolate() const { return jsgraph()->isolate(); } 412 413CommonOperatorBuilder* JSIntrinsicLowering::common() const { 414 return jsgraph()->common(); 415} 416 417JSOperatorBuilder* JSIntrinsicLowering::javascript() const { 418 return jsgraph_->javascript(); 419} 420 421SimplifiedOperatorBuilder* JSIntrinsicLowering::simplified() const { 422 return jsgraph()->simplified(); 423} 424 425} // namespace compiler 426} // namespace internal 427} // namespace v8 428