// 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/code-factory.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/js-graph.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/counters.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
JSIntrinsicLowering::JSIntrinsicLowering(Editor* editor, JSGraph* jsgraph,
DeoptimizationMode mode)
: AdvancedReducer(editor), jsgraph_(jsgraph), mode_(mode) {}
Reduction JSIntrinsicLowering::Reduce(Node* node) {
if (node->opcode() != IrOpcode::kJSCallRuntime) return NoChange();
const Runtime::Function* const f =
Runtime::FunctionForId(CallRuntimeParametersOf(node->op()).id());
if (f->intrinsic_type != Runtime::IntrinsicType::INLINE) return NoChange();
switch (f->function_id) {
case Runtime::kInlineCreateIterResultObject:
return ReduceCreateIterResultObject(node);
case Runtime::kInlineDebugIsActive:
return ReduceDebugIsActive(node);
case Runtime::kInlineDeoptimizeNow:
return ReduceDeoptimizeNow(node);
case Runtime::kInlineGeneratorClose:
return ReduceGeneratorClose(node);
case Runtime::kInlineGeneratorGetInputOrDebugPos:
return ReduceGeneratorGetInputOrDebugPos(node);
case Runtime::kInlineGeneratorGetResumeMode:
return ReduceGeneratorGetResumeMode(node);
case Runtime::kInlineGeneratorGetContext:
return ReduceGeneratorGetContext(node);
case Runtime::kInlineIsArray:
return ReduceIsInstanceType(node, JS_ARRAY_TYPE);
case Runtime::kInlineIsTypedArray:
return ReduceIsInstanceType(node, JS_TYPED_ARRAY_TYPE);
case Runtime::kInlineIsJSProxy:
return ReduceIsInstanceType(node, JS_PROXY_TYPE);
case Runtime::kInlineIsJSReceiver:
return ReduceIsJSReceiver(node);
case Runtime::kInlineIsSmi:
return ReduceIsSmi(node);
case Runtime::kInlineFixedArrayGet:
return ReduceFixedArrayGet(node);
case Runtime::kInlineFixedArraySet:
return ReduceFixedArraySet(node);
case Runtime::kInlineSubString:
return ReduceSubString(node);
case Runtime::kInlineToInteger:
return ReduceToInteger(node);
case Runtime::kInlineToLength:
return ReduceToLength(node);
case Runtime::kInlineToNumber:
return ReduceToNumber(node);
case Runtime::kInlineToObject:
return ReduceToObject(node);
case Runtime::kInlineToString:
return ReduceToString(node);
case Runtime::kInlineCall:
return ReduceCall(node);
case Runtime::kInlineGetSuperConstructor:
return ReduceGetSuperConstructor(node);
case Runtime::kInlineArrayBufferViewGetByteLength:
return ReduceArrayBufferViewField(
node, AccessBuilder::ForJSArrayBufferViewByteLength());
case Runtime::kInlineArrayBufferViewGetByteOffset:
return ReduceArrayBufferViewField(
node, AccessBuilder::ForJSArrayBufferViewByteOffset());
case Runtime::kInlineMaxSmi:
return ReduceMaxSmi(node);
case Runtime::kInlineTypedArrayGetLength:
return ReduceArrayBufferViewField(node,
AccessBuilder::ForJSTypedArrayLength());
case Runtime::kInlineTypedArrayMaxSizeInHeap:
return ReduceTypedArrayMaxSizeInHeap(node);
case Runtime::kInlineJSCollectionGetTable:
return ReduceJSCollectionGetTable(node);
case Runtime::kInlineStringGetRawHashField:
return ReduceStringGetRawHashField(node);
case Runtime::kInlineTheHole:
return ReduceTheHole(node);
case Runtime::kInlineClassOf:
return ReduceClassOf(node);
default:
break;
}
return NoChange();
}
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::ReduceDebugIsActive(Node* node) {
Node* const value = jsgraph()->ExternalConstant(
ExternalReference::debug_is_active_address(isolate()));
Node* const effect = NodeProperties::GetEffectInput(node);
Node* const control = NodeProperties::GetControlInput(node);
Operator const* const op =
simplified()->LoadField(AccessBuilder::ForExternalUint8Value());
return Change(node, op, value, effect, control);
}
Reduction JSIntrinsicLowering::ReduceDeoptimizeNow(Node* node) {
if (mode() != kDeoptimizationEnabled) return NoChange();
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::kNoReason),
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::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::ReduceGeneratorGetInputOrDebugPos(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::ForJSGeneratorObjectInputOrDebugPos());
return Change(node, op, generator, effect, control);
}
Reduction JSIntrinsicLowering::ReduceGeneratorGetContext(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::ForJSGeneratorObjectContext());
return Change(node, op, generator, effect, control);
}
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 = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMapInstanceType()),
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), value,
effect, if_false),
effect, if_false);
Node* vfalse = graph()->NewNode(simplified()->NumberEqual(), efalse,
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);
// 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::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::ReduceFixedArrayGet(Node* node) {
Node* base = node->InputAt(0);
Node* index = node->InputAt(1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
return Change(
node, simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
base, index, effect, control);
}
Reduction JSIntrinsicLowering::ReduceFixedArraySet(Node* node) {
Node* base = node->InputAt(0);
Node* index = node->InputAt(1);
Node* value = node->InputAt(2);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* store = (graph()->NewNode(
simplified()->StoreElement(AccessBuilder::ForFixedArrayElement()), base,
index, value, effect, control));
ReplaceWithValue(node, value, store);
return Changed(store);
}
Reduction JSIntrinsicLowering::ReduceSubString(Node* node) {
return Change(node, CodeFactory::SubString(isolate()), 3);
}
Reduction JSIntrinsicLowering::ReduceToInteger(Node* node) {
NodeProperties::ChangeOp(node, javascript()->ToInteger());
return Changed(node);
}
Reduction JSIntrinsicLowering::ReduceToNumber(Node* node) {
NodeProperties::ChangeOp(node, javascript()->ToNumber());
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) {
NodeProperties::ChangeOp(node, javascript()->ToString());
return Changed(node);
}
Reduction JSIntrinsicLowering::ReduceCall(Node* node) {
size_t const arity = CallRuntimeParametersOf(node->op()).arity();
NodeProperties::ChangeOp(
node,
javascript()->Call(arity, 0.0f, VectorSlotPair(),
ConvertReceiverMode::kAny, TailCallMode::kDisallow));
return Changed(node);
}
Reduction JSIntrinsicLowering::ReduceGetSuperConstructor(Node* node) {
Node* active_function = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* active_function_map = effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
active_function, effect, control);
return Change(node, simplified()->LoadField(AccessBuilder::ForMapPrototype()),
active_function_map, effect, control);
}
Reduction JSIntrinsicLowering::ReduceArrayBufferViewField(
Node* node, FieldAccess const& access) {
Node* receiver = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// Load the {receiver}s field.
Node* value = effect = graph()->NewNode(simplified()->LoadField(access),
receiver, effect, control);
// Check if the {receiver}s buffer was neutered.
Node* receiver_buffer = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
receiver, effect, control);
Node* check = effect = graph()->NewNode(
simplified()->ArrayBufferWasNeutered(), receiver_buffer, effect, control);
// Default to zero if the {receiver}s buffer was neutered.
value = graph()->NewNode(
common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
check, jsgraph()->ZeroConstant(), value);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
Reduction JSIntrinsicLowering::ReduceMaxSmi(Node* node) {
Node* value = jsgraph()->Constant(Smi::kMaxValue);
ReplaceWithValue(node, value);
return Replace(value);
}
Reduction JSIntrinsicLowering::ReduceTypedArrayMaxSizeInHeap(Node* node) {
Node* value = jsgraph()->Constant(FLAG_typed_array_max_size_in_heap);
ReplaceWithValue(node, value);
return Replace(value);
}
Reduction JSIntrinsicLowering::ReduceJSCollectionGetTable(Node* node) {
Node* collection = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
return Change(node,
simplified()->LoadField(AccessBuilder::ForJSCollectionTable()),
collection, effect, control);
}
Reduction JSIntrinsicLowering::ReduceStringGetRawHashField(Node* node) {
Node* string = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
return Change(node,
simplified()->LoadField(AccessBuilder::ForNameHashField()),
string, effect, control);
}
Reduction JSIntrinsicLowering::ReduceTheHole(Node* node) {
Node* value = jsgraph()->TheHoleConstant();
ReplaceWithValue(node, value);
return Replace(value);
}
Reduction JSIntrinsicLowering::ReduceClassOf(Node* node) {
RelaxEffectsAndControls(node);
node->TrimInputCount(2);
NodeProperties::ChangeOp(node, javascript()->ClassOf());
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) {
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), stack_parameter_count,
CallDescriptor::kNeedsFrameState, node->op()->properties());
node->InsertInput(graph()->zone(), 0,
jsgraph()->HeapConstant(callable.code()));
NodeProperties::ChangeOp(node, common()->Call(desc));
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