| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/compiler/js-intrinsic-lowering.h" |
| |
| #include <stack> |
| |
| #include "src/codegen/code-factory.h" |
| #include "src/compiler/access-builder.h" |
| #include "src/compiler/js-graph.h" |
| #include "src/compiler/js-heap-broker.h" |
| #include "src/compiler/linkage.h" |
| #include "src/compiler/node-matchers.h" |
| #include "src/compiler/node-properties.h" |
| #include "src/compiler/operator-properties.h" |
| #include "src/logging/counters.h" |
| #include "src/objects/js-generator.h" |
| #include "src/objects/objects-inl.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| JSIntrinsicLowering::JSIntrinsicLowering(Editor* editor, JSGraph* jsgraph, |
| JSHeapBroker* broker) |
| : AdvancedReducer(editor), jsgraph_(jsgraph), broker_(broker) {} |
| |
| Reduction JSIntrinsicLowering::Reduce(Node* node) { |
| DisallowHeapAccessIf no_heap_access(broker()->is_concurrent_inlining()); |
| |
| if (node->opcode() != IrOpcode::kJSCallRuntime) return NoChange(); |
| const Runtime::Function* const f = |
| Runtime::FunctionForId(CallRuntimeParametersOf(node->op()).id()); |
| if (f->function_id == Runtime::kTurbofanStaticAssert) { |
| return ReduceTurbofanStaticAssert(node); |
| } |
| if (f->function_id == Runtime::kIsBeingInterpreted) { |
| return ReduceIsBeingInterpreted(node); |
| } |
| if (f->intrinsic_type != Runtime::IntrinsicType::INLINE) return NoChange(); |
| switch (f->function_id) { |
| case Runtime::kInlineCopyDataProperties: |
| return ReduceCopyDataProperties(node); |
| case Runtime::kInlineCreateIterResultObject: |
| return ReduceCreateIterResultObject(node); |
| case Runtime::kInlineDeoptimizeNow: |
| return ReduceDeoptimizeNow(node); |
| case Runtime::kInlineGeneratorClose: |
| return ReduceGeneratorClose(node); |
| case Runtime::kInlineCreateJSGeneratorObject: |
| return ReduceCreateJSGeneratorObject(node); |
| case Runtime::kInlineAsyncFunctionAwaitCaught: |
| return ReduceAsyncFunctionAwaitCaught(node); |
| case Runtime::kInlineAsyncFunctionAwaitUncaught: |
| return ReduceAsyncFunctionAwaitUncaught(node); |
| case Runtime::kInlineAsyncFunctionEnter: |
| return ReduceAsyncFunctionEnter(node); |
| case Runtime::kInlineAsyncFunctionReject: |
| return ReduceAsyncFunctionReject(node); |
| case Runtime::kInlineAsyncFunctionResolve: |
| return ReduceAsyncFunctionResolve(node); |
| case Runtime::kInlineAsyncGeneratorAwaitCaught: |
| return ReduceAsyncGeneratorAwaitCaught(node); |
| case Runtime::kInlineAsyncGeneratorAwaitUncaught: |
| return ReduceAsyncGeneratorAwaitUncaught(node); |
| case Runtime::kInlineAsyncGeneratorReject: |
| return ReduceAsyncGeneratorReject(node); |
| case Runtime::kInlineAsyncGeneratorResolve: |
| return ReduceAsyncGeneratorResolve(node); |
| case Runtime::kInlineAsyncGeneratorYield: |
| return ReduceAsyncGeneratorYield(node); |
| case Runtime::kInlineGeneratorGetResumeMode: |
| return ReduceGeneratorGetResumeMode(node); |
| case Runtime::kInlineIsArray: |
| return ReduceIsInstanceType(node, JS_ARRAY_TYPE); |
| case Runtime::kInlineIsJSReceiver: |
| return ReduceIsJSReceiver(node); |
| case Runtime::kInlineIsSmi: |
| return ReduceIsSmi(node); |
| case Runtime::kInlineToLength: |
| return ReduceToLength(node); |
| case Runtime::kInlineToObject: |
| return ReduceToObject(node); |
| case Runtime::kInlineToString: |
| return ReduceToString(node); |
| case Runtime::kInlineCall: |
| return ReduceCall(node); |
| case Runtime::kInlineIncBlockCounter: |
| return ReduceIncBlockCounter(node); |
| case Runtime::kInlineGetImportMetaObject: |
| return ReduceGetImportMetaObject(node); |
| default: |
| break; |
| } |
| return NoChange(); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceCopyDataProperties(Node* node) { |
| return Change( |
| node, Builtins::CallableFor(isolate(), Builtins::kCopyDataProperties), 0); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceCreateIterResultObject(Node* node) { |
| Node* const value = NodeProperties::GetValueInput(node, 0); |
| Node* const done = NodeProperties::GetValueInput(node, 1); |
| Node* const context = NodeProperties::GetContextInput(node); |
| Node* const effect = NodeProperties::GetEffectInput(node); |
| return Change(node, javascript()->CreateIterResultObject(), value, done, |
| context, effect); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceDeoptimizeNow(Node* node) { |
| Node* const frame_state = NodeProperties::GetFrameStateInput(node); |
| Node* const effect = NodeProperties::GetEffectInput(node); |
| Node* const control = NodeProperties::GetControlInput(node); |
| |
| // TODO(bmeurer): Move MergeControlToEnd() to the AdvancedReducer. |
| Node* deoptimize = graph()->NewNode( |
| common()->Deoptimize(DeoptimizeKind::kEager, |
| DeoptimizeReason::kDeoptimizeNow, FeedbackSource()), |
| frame_state, effect, control); |
| NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); |
| Revisit(graph()->end()); |
| |
| node->TrimInputCount(0); |
| NodeProperties::ChangeOp(node, common()->Dead()); |
| return Changed(node); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceCreateJSGeneratorObject(Node* node) { |
| Node* const closure = NodeProperties::GetValueInput(node, 0); |
| Node* const receiver = NodeProperties::GetValueInput(node, 1); |
| Node* const context = NodeProperties::GetContextInput(node); |
| Node* const effect = NodeProperties::GetEffectInput(node); |
| Node* const control = NodeProperties::GetControlInput(node); |
| Operator const* const op = javascript()->CreateGeneratorObject(); |
| Node* create_generator = |
| graph()->NewNode(op, closure, receiver, context, effect, control); |
| ReplaceWithValue(node, create_generator, create_generator); |
| return Changed(create_generator); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceGeneratorClose(Node* node) { |
| Node* const generator = NodeProperties::GetValueInput(node, 0); |
| Node* const effect = NodeProperties::GetEffectInput(node); |
| Node* const control = NodeProperties::GetControlInput(node); |
| Node* const closed = jsgraph()->Constant(JSGeneratorObject::kGeneratorClosed); |
| Node* const undefined = jsgraph()->UndefinedConstant(); |
| Operator const* const op = simplified()->StoreField( |
| AccessBuilder::ForJSGeneratorObjectContinuation()); |
| |
| ReplaceWithValue(node, undefined, node); |
| NodeProperties::RemoveType(node); |
| return Change(node, op, generator, closed, effect, control); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceAsyncFunctionAwaitCaught(Node* node) { |
| return Change( |
| node, |
| Builtins::CallableFor(isolate(), Builtins::kAsyncFunctionAwaitCaught), 0); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceAsyncFunctionAwaitUncaught(Node* node) { |
| return Change( |
| node, |
| Builtins::CallableFor(isolate(), Builtins::kAsyncFunctionAwaitUncaught), |
| 0); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceAsyncFunctionEnter(Node* node) { |
| NodeProperties::ChangeOp(node, javascript()->AsyncFunctionEnter()); |
| return Changed(node); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceAsyncFunctionReject(Node* node) { |
| RelaxControls(node); |
| NodeProperties::ChangeOp(node, javascript()->AsyncFunctionReject()); |
| return Changed(node); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceAsyncFunctionResolve(Node* node) { |
| RelaxControls(node); |
| NodeProperties::ChangeOp(node, javascript()->AsyncFunctionResolve()); |
| return Changed(node); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceAsyncGeneratorAwaitCaught(Node* node) { |
| return Change( |
| node, |
| Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorAwaitCaught), |
| 0); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceAsyncGeneratorAwaitUncaught(Node* node) { |
| return Change( |
| node, |
| Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorAwaitUncaught), |
| 0); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceAsyncGeneratorReject(Node* node) { |
| return Change( |
| node, Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorReject), |
| 0); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceAsyncGeneratorResolve(Node* node) { |
| return Change( |
| node, Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorResolve), |
| 0); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceAsyncGeneratorYield(Node* node) { |
| return Change( |
| node, Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorYield), |
| 0); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceGeneratorGetResumeMode(Node* node) { |
| Node* const generator = NodeProperties::GetValueInput(node, 0); |
| Node* const effect = NodeProperties::GetEffectInput(node); |
| Node* const control = NodeProperties::GetControlInput(node); |
| Operator const* const op = |
| simplified()->LoadField(AccessBuilder::ForJSGeneratorObjectResumeMode()); |
| |
| return Change(node, op, generator, effect, control); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceIsInstanceType( |
| Node* node, InstanceType instance_type) { |
| // if (%_IsSmi(value)) { |
| // return false; |
| // } else { |
| // return %_GetInstanceType(%_GetMap(value)) == instance_type; |
| // } |
| Node* value = NodeProperties::GetValueInput(node, 0); |
| Node* effect = NodeProperties::GetEffectInput(node); |
| Node* control = NodeProperties::GetControlInput(node); |
| |
| Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); |
| Node* branch = graph()->NewNode(common()->Branch(), check, control); |
| |
| Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
| Node* etrue = effect; |
| Node* vtrue = jsgraph()->FalseConstant(); |
| |
| Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
| Node* efalse = effect; |
| Node* map = efalse = |
| graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), value, |
| efalse, if_false); |
| Node* map_instance_type = efalse = graph()->NewNode( |
| simplified()->LoadField(AccessBuilder::ForMapInstanceType()), map, efalse, |
| if_false); |
| Node* vfalse = |
| graph()->NewNode(simplified()->NumberEqual(), map_instance_type, |
| jsgraph()->Constant(instance_type)); |
| |
| Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); |
| |
| // Replace all effect uses of {node} with the {ephi}. |
| Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge); |
| ReplaceWithValue(node, node, ephi, merge); |
| |
| // Turn the {node} into a Phi. |
| return Change(node, common()->Phi(MachineRepresentation::kTagged, 2), vtrue, |
| vfalse, merge); |
| } |
| |
| |
| Reduction JSIntrinsicLowering::ReduceIsJSReceiver(Node* node) { |
| return Change(node, simplified()->ObjectIsReceiver()); |
| } |
| |
| |
| Reduction JSIntrinsicLowering::ReduceIsSmi(Node* node) { |
| return Change(node, simplified()->ObjectIsSmi()); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceTurbofanStaticAssert(Node* node) { |
| if (FLAG_always_opt) { |
| // Ignore static asserts, as we most likely won't have enough information |
| RelaxEffectsAndControls(node); |
| } else { |
| Node* value = NodeProperties::GetValueInput(node, 0); |
| Node* effect = NodeProperties::GetEffectInput(node); |
| Node* assert = graph()->NewNode( |
| common()->StaticAssert("%TurbofanStaticAssert"), value, effect); |
| ReplaceWithValue(node, node, assert, nullptr); |
| } |
| return Changed(jsgraph_->UndefinedConstant()); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceIsBeingInterpreted(Node* node) { |
| RelaxEffectsAndControls(node); |
| return Changed(jsgraph_->FalseConstant()); |
| } |
| |
| Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op) { |
| // Replace all effect uses of {node} with the effect dependency. |
| RelaxEffectsAndControls(node); |
| // Remove the inputs corresponding to context, effect and control. |
| NodeProperties::RemoveNonValueInputs(node); |
| // Finally update the operator to the new one. |
| NodeProperties::ChangeOp(node, op); |
| return Changed(node); |
| } |
| |
| |
| Reduction JSIntrinsicLowering::ReduceToLength(Node* node) { |
| NodeProperties::ChangeOp(node, javascript()->ToLength()); |
| return Changed(node); |
| } |
| |
| |
| Reduction JSIntrinsicLowering::ReduceToObject(Node* node) { |
| NodeProperties::ChangeOp(node, javascript()->ToObject()); |
| return Changed(node); |
| } |
| |
| |
| Reduction JSIntrinsicLowering::ReduceToString(Node* node) { |
| // ToString is unnecessary if the input is a string. |
| HeapObjectMatcher m(NodeProperties::GetValueInput(node, 0)); |
| if (m.HasResolvedValue() && m.Ref(broker()).IsString()) { |
| ReplaceWithValue(node, m.node()); |
| return Replace(m.node()); |
| } |
| NodeProperties::ChangeOp(node, javascript()->ToString()); |
| return Changed(node); |
| } |
| |
| |
| Reduction JSIntrinsicLowering::ReduceCall(Node* node) { |
| int const arity = |
| static_cast<int>(CallRuntimeParametersOf(node->op()).arity()); |
| static constexpr int kTargetAndReceiver = 2; |
| STATIC_ASSERT(JSCallNode::kFeedbackVectorIsLastInput); |
| Node* feedback = jsgraph()->UndefinedConstant(); |
| node->InsertInput(graph()->zone(), arity, feedback); |
| NodeProperties::ChangeOp( |
| node, |
| javascript()->Call(JSCallNode::ArityForArgc(arity - kTargetAndReceiver))); |
| return Changed(node); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceIncBlockCounter(Node* node) { |
| DCHECK(!Linkage::NeedsFrameStateInput(Runtime::kIncBlockCounter)); |
| DCHECK(!Linkage::NeedsFrameStateInput(Runtime::kInlineIncBlockCounter)); |
| return Change(node, |
| Builtins::CallableFor(isolate(), Builtins::kIncBlockCounter), 0, |
| kDoesNotNeedFrameState); |
| } |
| |
| Reduction JSIntrinsicLowering::ReduceGetImportMetaObject(Node* node) { |
| NodeProperties::ChangeOp(node, javascript()->GetImportMeta()); |
| return Changed(node); |
| } |
| |
| Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a, |
| Node* b) { |
| RelaxControls(node); |
| node->ReplaceInput(0, a); |
| node->ReplaceInput(1, b); |
| node->TrimInputCount(2); |
| NodeProperties::ChangeOp(node, op); |
| return Changed(node); |
| } |
| |
| |
| Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a, |
| Node* b, Node* c) { |
| RelaxControls(node); |
| node->ReplaceInput(0, a); |
| node->ReplaceInput(1, b); |
| node->ReplaceInput(2, c); |
| node->TrimInputCount(3); |
| NodeProperties::ChangeOp(node, op); |
| return Changed(node); |
| } |
| |
| |
| Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a, |
| Node* b, Node* c, Node* d) { |
| RelaxControls(node); |
| node->ReplaceInput(0, a); |
| node->ReplaceInput(1, b); |
| node->ReplaceInput(2, c); |
| node->ReplaceInput(3, d); |
| node->TrimInputCount(4); |
| NodeProperties::ChangeOp(node, op); |
| return Changed(node); |
| } |
| |
| Reduction JSIntrinsicLowering::Change(Node* node, Callable const& callable, |
| int stack_parameter_count, |
| enum FrameStateFlag frame_state_flag) { |
| CallDescriptor::Flags flags = frame_state_flag == kNeedsFrameState |
| ? CallDescriptor::kNeedsFrameState |
| : CallDescriptor::kNoFlags; |
| auto call_descriptor = Linkage::GetStubCallDescriptor( |
| graph()->zone(), callable.descriptor(), stack_parameter_count, flags, |
| node->op()->properties()); |
| node->InsertInput(graph()->zone(), 0, |
| jsgraph()->HeapConstant(callable.code())); |
| NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); |
| return Changed(node); |
| } |
| |
| Graph* JSIntrinsicLowering::graph() const { return jsgraph()->graph(); } |
| |
| |
| Isolate* JSIntrinsicLowering::isolate() const { return jsgraph()->isolate(); } |
| |
| |
| CommonOperatorBuilder* JSIntrinsicLowering::common() const { |
| return jsgraph()->common(); |
| } |
| |
| JSOperatorBuilder* JSIntrinsicLowering::javascript() const { |
| return jsgraph_->javascript(); |
| } |
| |
| SimplifiedOperatorBuilder* JSIntrinsicLowering::simplified() const { |
| return jsgraph()->simplified(); |
| } |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |