#include "src/compiler/js-generic-lowering.h"
#include "src/ast/ast.h"
#include "src/builtins/builtins-constructor.h"
#include "src/codegen/code-factory.h"
#include "src/codegen/interface-descriptors-inl.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-heap-broker.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/processed-feedback.h"
#include "src/compiler/simplified-operator.h"
#include "src/objects/scope-info.h"
#include "src/objects/template-objects-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
namespace {
CallDescriptor::Flags FrameStateFlagForCall(Node* node) {
return OperatorProperties::HasFrameStateInput(node->op())
? CallDescriptor::kNeedsFrameState
: CallDescriptor::kNoFlags;
}
}
JSGenericLowering::JSGenericLowering(JSGraph* jsgraph, Editor* editor,
JSHeapBroker* broker)
: AdvancedReducer(editor), jsgraph_(jsgraph), broker_(broker) {}
JSGenericLowering::~JSGenericLowering() = default;
Reduction JSGenericLowering::Reduce(Node* node) {
switch (node->opcode()) {
#define DECLARE_CASE(x, ...) \
case IrOpcode::k##x: \
if constexpr (IrOpcode::k##x == IrOpcode::kJSStackCheck) { \
* return `Changed` for them, otherwise we'll loop infinitely trying to \
* lower them. */ \
return NoChange(); \
} \
Lower##x(node); \
break;
JS_OP_LIST(DECLARE_CASE)
#undef DECLARE_CASE
default:
return NoChange();
}
return Changed(node);
}
#define REPLACE_STUB_CALL(Name) \
void JSGenericLowering::LowerJS##Name(Node* node) { \
ReplaceWithBuiltinCall(node, Builtin::k##Name); \
}
REPLACE_STUB_CALL(ToLength)
REPLACE_STUB_CALL(ToNumber)
REPLACE_STUB_CALL(ToNumberConvertBigInt)
REPLACE_STUB_CALL(ToBigInt)
REPLACE_STUB_CALL(ToBigIntConvertNumber)
REPLACE_STUB_CALL(ToNumeric)
REPLACE_STUB_CALL(ToName)
REPLACE_STUB_CALL(ToObject)
REPLACE_STUB_CALL(ToString)
REPLACE_STUB_CALL(ForInEnumerate)
REPLACE_STUB_CALL(AsyncFunctionEnter)
REPLACE_STUB_CALL(AsyncFunctionReject)
REPLACE_STUB_CALL(AsyncFunctionResolve)
REPLACE_STUB_CALL(FulfillPromise)
REPLACE_STUB_CALL(PerformPromiseThen)
REPLACE_STUB_CALL(PromiseResolve)
REPLACE_STUB_CALL(RejectPromise)
REPLACE_STUB_CALL(ResolvePromise)
#undef REPLACE_STUB_CALL
void JSGenericLowering::ReplaceWithBuiltinCall(Node* node, Builtin builtin) {
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
Callable callable = Builtins::CallableFor(isolate(), builtin);
ReplaceWithBuiltinCall(node, callable, flags);
}
void JSGenericLowering::ReplaceWithBuiltinCall(Node* node, Callable callable,
CallDescriptor::Flags flags) {
ReplaceWithBuiltinCall(node, callable, flags, node->op()->properties());
}
void JSGenericLowering::ReplaceWithBuiltinCall(
Node* node, Callable callable, CallDescriptor::Flags flags,
Operator::Properties properties) {
const CallInterfaceDescriptor& descriptor = callable.descriptor();
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), descriptor, descriptor.GetStackParameterCount(), flags,
properties);
Node* stub_code = jsgraph()->HeapConstantNoHole(callable.code());
node->InsertInput(zone(), 0, stub_code);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
void JSGenericLowering::ReplaceWithRuntimeCall(Node* node,
Runtime::FunctionId f,
int nargs_override) {
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
Operator::Properties properties = node->op()->properties();
const Runtime::Function* fun = Runtime::FunctionForId(f);
int nargs = (nargs_override < 0) ? fun->nargs : nargs_override;
auto call_descriptor =
Linkage::GetRuntimeCallDescriptor(zone(), f, nargs, properties, flags);
Node* ref = jsgraph()->ExternalConstant(ExternalReference::Create(f));
Node* arity = jsgraph()->Int32Constant(nargs);
node->InsertInput(zone(), 0, jsgraph()->CEntryStubConstant(fun->result_size));
node->InsertInput(zone(), nargs + 1, ref);
node->InsertInput(zone(), nargs + 2, arity);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
void JSGenericLowering::ReplaceUnaryOpWithBuiltinCall(
Node* node, Builtin builtin_without_feedback,
Builtin builtin_with_feedback) {
DCHECK(JSOperator::IsUnaryWithFeedback(node->opcode()));
node->RemoveInput(JSUnaryOpNode::FeedbackVectorIndex());
ReplaceWithBuiltinCall(node, builtin_without_feedback);
}
#define DEF_UNARY_LOWERING(Name) \
void JSGenericLowering::LowerJS##Name(Node* node) { \
ReplaceUnaryOpWithBuiltinCall(node, Builtin::k##Name, \
Builtin::k##Name##_WithFeedback); \
}
DEF_UNARY_LOWERING(BitwiseNot)
DEF_UNARY_LOWERING(Decrement)
DEF_UNARY_LOWERING(Increment)
DEF_UNARY_LOWERING(Negate)
#undef DEF_UNARY_LOWERING
void JSGenericLowering::ReplaceBinaryOpWithBuiltinCall(
Node* node, Builtin builtin_without_feedback,
Builtin builtin_with_feedback) {
DCHECK(JSOperator::IsBinaryWithFeedback(node->opcode()));
node->RemoveInput(JSBinaryOpNode::FeedbackVectorIndex());
ReplaceWithBuiltinCall(node, builtin_without_feedback);
}
#define DEF_BINARY_LOWERING(Name) \
void JSGenericLowering::LowerJS##Name(Node* node) { \
ReplaceBinaryOpWithBuiltinCall(node, Builtin::k##Name, \
Builtin::k##Name##_WithFeedback); \
}
DEF_BINARY_LOWERING(Add)
DEF_BINARY_LOWERING(BitwiseAnd)
DEF_BINARY_LOWERING(BitwiseOr)
DEF_BINARY_LOWERING(BitwiseXor)
DEF_BINARY_LOWERING(Divide)
DEF_BINARY_LOWERING(Exponentiate)
DEF_BINARY_LOWERING(Modulus)
DEF_BINARY_LOWERING(Multiply)
DEF_BINARY_LOWERING(ShiftLeft)
DEF_BINARY_LOWERING(ShiftRight)
DEF_BINARY_LOWERING(ShiftRightLogical)
DEF_BINARY_LOWERING(Subtract)
DEF_BINARY_LOWERING(Equal)
DEF_BINARY_LOWERING(GreaterThan)
DEF_BINARY_LOWERING(GreaterThanOrEqual)
DEF_BINARY_LOWERING(InstanceOf)
DEF_BINARY_LOWERING(LessThan)
DEF_BINARY_LOWERING(LessThanOrEqual)
#undef DEF_BINARY_LOWERING
void JSGenericLowering::LowerJSStrictEqual(Node* node) {
NodeProperties::ReplaceContextInput(node, jsgraph()->NoContextConstant());
DCHECK_EQ(node->op()->ControlInputCount(), 1);
node->RemoveInput(NodeProperties::FirstControlIndex(node));
Callable callable = Builtins::CallableFor(isolate(), Builtin::kStrictEqual);
ReplaceWithBuiltinCall(node, callable, CallDescriptor::kNoFlags,
Operator::kEliminatable);
}
namespace {
bool ShouldUseMegamorphicAccessBuiltin(FeedbackSource const& source,
OptionalNameRef name, AccessMode mode,
JSHeapBroker* broker) {
ProcessedFeedback const& feedback =
broker->GetFeedbackForPropertyAccess(source, mode, name);
if (feedback.kind() == ProcessedFeedback::kElementAccess) {
return feedback.AsElementAccess().transition_groups().empty();
} else if (feedback.kind() == ProcessedFeedback::kNamedAccess) {
return feedback.AsNamedAccess().maps().empty();
} else if (feedback.kind() == ProcessedFeedback::kInsufficient) {
return false;
}
UNREACHABLE();
}
}
void JSGenericLowering::LowerJSDetachContextCell(Node* node) {
DCHECK_EQ(IrOpcode::kJSDetachContextCell, node->opcode());
int index = OpParameter<int>(node->op());
node->ReplaceInput(2, jsgraph()->IntPtrConstant(index));
ReplaceWithBuiltinCall(node, Builtin::kDetachContextCell);
}
void JSGenericLowering::LowerJSHasProperty(Node* node) {
JSHasPropertyNode n(node);
const PropertyAccess& p = n.Parameters();
if (!p.feedback().IsValid()) {
node->RemoveInput(JSHasPropertyNode::FeedbackVectorIndex());
ReplaceWithBuiltinCall(node, Builtin::kHasProperty);
} else {
static_assert(n.FeedbackVectorIndex() == 2);
n->InsertInput(zone(), 2,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(node, Builtin::kKeyedHasIC);
}
}
void JSGenericLowering::LowerJSLoadProperty(Node* node) {
JSLoadPropertyNode n(node);
const PropertyAccess& p = n.Parameters();
FrameState frame_state = n.frame_state();
Node* outer_state = frame_state.outer_frame_state();
static_assert(n.FeedbackVectorIndex() == 2);
if (outer_state->opcode() != IrOpcode::kFrameState) {
n->RemoveInput(n.FeedbackVectorIndex());
n->InsertInput(zone(), 2,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(
node, ShouldUseMegamorphicAccessBuiltin(p.feedback(), {},
AccessMode::kLoad, broker())
? Builtin::kKeyedLoadICTrampoline_Megamorphic
: Builtin::kKeyedLoadICTrampoline);
} else {
n->InsertInput(zone(), 2,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(
node, ShouldUseMegamorphicAccessBuiltin(p.feedback(), {},
AccessMode::kLoad, broker())
? Builtin::kKeyedLoadIC_Megamorphic
: Builtin::kKeyedLoadIC);
}
}
void JSGenericLowering::LowerJSLoadNamed(Node* node) {
JSLoadNamedNode n(node);
NamedAccess const& p = n.Parameters();
FrameState frame_state = n.frame_state();
Node* outer_state = frame_state.outer_frame_state();
static_assert(n.FeedbackVectorIndex() == 1);
if (!p.feedback().IsValid()) {
n->RemoveInput(n.FeedbackVectorIndex());
node->InsertInput(zone(), 1, jsgraph()->ConstantNoHole(p.name(), broker()));
ReplaceWithBuiltinCall(node, Builtin::kGetProperty);
} else if (outer_state->opcode() != IrOpcode::kFrameState) {
n->RemoveInput(n.FeedbackVectorIndex());
node->InsertInput(zone(), 1, jsgraph()->ConstantNoHole(p.name(), broker()));
node->InsertInput(zone(), 2,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(
node, ShouldUseMegamorphicAccessBuiltin(p.feedback(), p.name(),
AccessMode::kLoad, broker())
? Builtin::kLoadICTrampoline_Megamorphic
: Builtin::kLoadICTrampoline);
} else {
node->InsertInput(zone(), 1, jsgraph()->ConstantNoHole(p.name(), broker()));
node->InsertInput(zone(), 2,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(
node, ShouldUseMegamorphicAccessBuiltin(p.feedback(), p.name(),
AccessMode::kLoad, broker())
? Builtin::kLoadIC_Megamorphic
: Builtin::kLoadIC);
}
}
void JSGenericLowering::LowerJSLoadNamedFromSuper(Node* node) {
JSLoadNamedFromSuperNode n(node);
NamedAccess const& p = n.Parameters();
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* home_object_map = effect = graph()->NewNode(
jsgraph()->simplified()->LoadField(AccessBuilder::ForMap()),
n.home_object(), effect, control);
Node* home_object_proto = effect = graph()->NewNode(
jsgraph()->simplified()->LoadField(AccessBuilder::ForMapPrototype()),
home_object_map, effect, control);
n->ReplaceInput(n.HomeObjectIndex(), home_object_proto);
NodeProperties::ReplaceEffectInput(node, effect);
static_assert(n.FeedbackVectorIndex() == 2);
DCHECK(p.feedback().IsValid());
node->InsertInput(zone(), 2, jsgraph()->ConstantNoHole(p.name(), broker()));
node->InsertInput(zone(), 3,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(node, Builtin::kLoadSuperIC);
}
void JSGenericLowering::LowerJSLoadGlobal(Node* node) {
JSLoadGlobalNode n(node);
const LoadGlobalParameters& p = n.Parameters();
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
FrameState frame_state = n.frame_state();
Node* outer_state = frame_state.outer_frame_state();
static_assert(n.FeedbackVectorIndex() == 0);
if (outer_state->opcode() != IrOpcode::kFrameState) {
n->RemoveInput(n.FeedbackVectorIndex());
node->InsertInput(zone(), 0, jsgraph()->ConstantNoHole(p.name(), broker()));
node->InsertInput(zone(), 1,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
Callable callable = CodeFactory::LoadGlobalIC(isolate(), p.typeof_mode());
ReplaceWithBuiltinCall(node, callable, flags);
} else {
node->InsertInput(zone(), 0, jsgraph()->ConstantNoHole(p.name(), broker()));
node->InsertInput(zone(), 1,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
Callable callable =
CodeFactory::LoadGlobalICInOptimizedCode(isolate(), p.typeof_mode());
ReplaceWithBuiltinCall(node, callable, flags);
}
}
void JSGenericLowering::LowerJSGetIterator(Node* node) {
JSGetIteratorNode n(node);
GetIteratorParameters const& p = n.Parameters();
Node* load_slot =
jsgraph()->TaggedIndexConstant(p.loadFeedback().slot.ToInt());
Node* call_slot =
jsgraph()->TaggedIndexConstant(p.callFeedback().slot.ToInt());
static_assert(n.FeedbackVectorIndex() == 1);
node->InsertInput(zone(), 1, load_slot);
node->InsertInput(zone(), 2, call_slot);
ReplaceWithBuiltinCall(node, Builtin::kGetIteratorWithFeedback);
}
void JSGenericLowering::LowerJSSetKeyedProperty(Node* node) {
JSSetKeyedPropertyNode n(node);
const PropertyAccess& p = n.Parameters();
FrameState frame_state = n.frame_state();
Node* outer_state = frame_state.outer_frame_state();
static_assert(n.FeedbackVectorIndex() == 3);
if (outer_state->opcode() != IrOpcode::kFrameState) {
n->RemoveInput(n.FeedbackVectorIndex());
node->InsertInput(zone(), 3,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(
node, ShouldUseMegamorphicAccessBuiltin(p.feedback(), {},
AccessMode::kStore, broker())
? Builtin::kKeyedStoreICTrampoline_Megamorphic
: Builtin::kKeyedStoreICTrampoline);
} else {
node->InsertInput(zone(), 3,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(
node, ShouldUseMegamorphicAccessBuiltin(p.feedback(), {},
AccessMode::kStore, broker())
? Builtin::kKeyedStoreIC_Megamorphic
: Builtin::kKeyedStoreIC);
}
}
void JSGenericLowering::LowerJSDefineKeyedOwnProperty(Node* node) {
JSDefineKeyedOwnPropertyNode n(node);
const PropertyAccess& p = n.Parameters();
FrameState frame_state = n.frame_state();
Node* outer_state = frame_state.outer_frame_state();
static_assert(n.FeedbackVectorIndex() == 4);
if (outer_state->opcode() != IrOpcode::kFrameState) {
n->RemoveInput(n.FeedbackVectorIndex());
node->InsertInput(zone(), 4,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(node, Builtin::kDefineKeyedOwnICTrampoline);
} else {
node->InsertInput(zone(), 4,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(node, Builtin::kDefineKeyedOwnIC);
}
}
void JSGenericLowering::LowerJSSetNamedProperty(Node* node) {
JSSetNamedPropertyNode n(node);
NamedAccess const& p = n.Parameters();
FrameState frame_state = n.frame_state();
Node* outer_state = frame_state.outer_frame_state();
static_assert(n.FeedbackVectorIndex() == 2);
if (!p.feedback().IsValid()) {
n->RemoveInput(n.FeedbackVectorIndex());
node->InsertInput(zone(), 1, jsgraph()->ConstantNoHole(p.name(), broker()));
ReplaceWithRuntimeCall(node, Runtime::kSetNamedProperty);
} else if (outer_state->opcode() != IrOpcode::kFrameState) {
n->RemoveInput(n.FeedbackVectorIndex());
node->InsertInput(zone(), 1, jsgraph()->ConstantNoHole(p.name(), broker()));
node->InsertInput(zone(), 3,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(
node, ShouldUseMegamorphicAccessBuiltin(p.feedback(), {},
AccessMode::kStore, broker())
? Builtin::kStoreICTrampoline_Megamorphic
: Builtin::kStoreICTrampoline);
} else {
node->InsertInput(zone(), 1, jsgraph()->ConstantNoHole(p.name(), broker()));
node->InsertInput(zone(), 3,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(
node, ShouldUseMegamorphicAccessBuiltin(p.feedback(), {},
AccessMode::kStore, broker())
? Builtin::kStoreIC_Megamorphic
: Builtin::kStoreIC);
}
}
void JSGenericLowering::LowerJSDefineNamedOwnProperty(Node* node) {
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
JSDefineNamedOwnPropertyNode n(node);
DefineNamedOwnPropertyParameters const& p = n.Parameters();
FrameState frame_state = n.frame_state();
Node* outer_state = frame_state.outer_frame_state();
static_assert(n.FeedbackVectorIndex() == 2);
if (outer_state->opcode() != IrOpcode::kFrameState) {
n->RemoveInput(n.FeedbackVectorIndex());
node->InsertInput(zone(), 1, jsgraph()->ConstantNoHole(p.name(), broker()));
node->InsertInput(zone(), 3,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
Callable callable = CodeFactory::DefineNamedOwnIC(isolate());
ReplaceWithBuiltinCall(node, callable, flags);
} else {
node->InsertInput(zone(), 1, jsgraph()->ConstantNoHole(p.name(), broker()));
node->InsertInput(zone(), 3,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
Callable callable = CodeFactory::DefineNamedOwnICInOptimizedCode(isolate());
ReplaceWithBuiltinCall(node, callable, flags);
}
}
void JSGenericLowering::LowerJSStoreGlobal(Node* node) {
JSStoreGlobalNode n(node);
const StoreGlobalParameters& p = n.Parameters();
FrameState frame_state = n.frame_state();
Node* outer_state = frame_state.outer_frame_state();
static_assert(n.FeedbackVectorIndex() == 1);
if (outer_state->opcode() != IrOpcode::kFrameState) {
n->RemoveInput(n.FeedbackVectorIndex());
node->InsertInput(zone(), 0, jsgraph()->ConstantNoHole(p.name(), broker()));
node->InsertInput(zone(), 2,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(node, Builtin::kStoreGlobalICTrampoline);
} else {
node->InsertInput(zone(), 0, jsgraph()->ConstantNoHole(p.name(), broker()));
node->InsertInput(zone(), 2,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(node, Builtin::kStoreGlobalIC);
}
}
void JSGenericLowering::LowerJSDefineKeyedOwnPropertyInLiteral(Node* node) {
JSDefineKeyedOwnPropertyInLiteralNode n(node);
FeedbackParameter const& p = n.Parameters();
static_assert(n.FeedbackVectorIndex() == 4);
RelaxControls(node);
node->InsertInput(zone(), 5,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithRuntimeCall(node, Runtime::kDefineKeyedOwnPropertyInLiteral);
}
void JSGenericLowering::LowerJSStoreInArrayLiteral(Node* node) {
JSStoreInArrayLiteralNode n(node);
FeedbackParameter const& p = n.Parameters();
static_assert(n.FeedbackVectorIndex() == 3);
RelaxControls(node);
node->InsertInput(zone(), 3,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(node, Builtin::kStoreInArrayLiteralIC);
}
void JSGenericLowering::LowerJSDeleteProperty(Node* node) {
ReplaceWithBuiltinCall(node, Builtin::kDeleteProperty);
}
void JSGenericLowering::LowerJSGetSuperConstructor(Node* node) {
Node* active_function = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* function_map = effect = graph()->NewNode(
jsgraph()->simplified()->LoadField(AccessBuilder::ForMap()),
active_function, effect, control);
RelaxControls(node);
node->ReplaceInput(0, function_map);
node->ReplaceInput(1, effect);
node->ReplaceInput(2, control);
node->TrimInputCount(3);
NodeProperties::ChangeOp(node, jsgraph()->simplified()->LoadField(
AccessBuilder::ForMapPrototype()));
}
void JSGenericLowering::LowerJSFindNonDefaultConstructorOrConstruct(
Node* node) {
ReplaceWithBuiltinCall(node, Builtin::kFindNonDefaultConstructorOrConstruct);
}
void JSGenericLowering::LowerJSHasInPrototypeChain(Node* node) {
ReplaceWithRuntimeCall(node, Runtime::kHasInPrototypeChain);
}
void JSGenericLowering::LowerJSOrdinaryHasInstance(Node* node) {
ReplaceWithBuiltinCall(node, Builtin::kOrdinaryHasInstance);
}
void JSGenericLowering::LowerJSHasContextExtension(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSLoadContextNoCell(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSLoadContext(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSStoreContextNoCell(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSStoreContext(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSCreate(Node* node) {
ReplaceWithBuiltinCall(node, Builtin::kFastNewObject);
}
void JSGenericLowering::LowerJSCreateArguments(Node* node) {
CreateArgumentsType const type = CreateArgumentsTypeOf(node->op());
switch (type) {
case CreateArgumentsType::kMappedArguments:
ReplaceWithRuntimeCall(node, Runtime::kNewSloppyArguments);
break;
case CreateArgumentsType::kUnmappedArguments:
ReplaceWithRuntimeCall(node, Runtime::kNewStrictArguments);
break;
case CreateArgumentsType::kRestParameter:
ReplaceWithRuntimeCall(node, Runtime::kNewRestParameter);
break;
}
}
void JSGenericLowering::LowerJSCreateArray(Node* node) {
CreateArrayParameters const& p = CreateArrayParametersOf(node->op());
int const arity = static_cast<int>(p.arity());
auto interface_descriptor = ArrayConstructorDescriptor{};
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), interface_descriptor, arity + 1, CallDescriptor::kNeedsFrameState,
node->op()->properties());
DCHECK_EQ(interface_descriptor.GetStackParameterCount(), 0);
Node* stub_code = jsgraph()->ArrayConstructorStubConstant();
Node* stub_arity = jsgraph()->Int32Constant(JSParameterCount(arity));
OptionalAllocationSiteRef const site = p.site();
Node* type_info = site.has_value()
? jsgraph()->ConstantNoHole(site.value(), broker())
: jsgraph()->UndefinedConstant();
Node* receiver = jsgraph()->UndefinedConstant();
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 3, stub_arity);
node->InsertInput(zone(), 4, type_info);
node->InsertInput(zone(), 5, receiver);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
void JSGenericLowering::LowerJSCreateArrayIterator(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSCreateAsyncFunctionObject(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSCreateCollectionIterator(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSCreateBoundFunction(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSObjectIsArray(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSCreateObject(Node* node) {
ReplaceWithBuiltinCall(node, Builtin::kCreateObjectWithoutProperties);
}
void JSGenericLowering::LowerJSCreateStringWrapper(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSParseInt(Node* node) {
ReplaceWithBuiltinCall(node, Builtin::kParseInt);
}
void JSGenericLowering::LowerJSRegExpTest(Node* node) {
ReplaceWithBuiltinCall(node, Builtin::kRegExpPrototypeTestFast);
}
void JSGenericLowering::LowerJSCreateClosure(Node* node) {
JSCreateClosureNode n(node);
CreateClosureParameters const& p = n.Parameters();
SharedFunctionInfoRef shared_info = p.shared_info();
static_assert(n.FeedbackCellIndex() == 0);
node->InsertInput(zone(), 0,
jsgraph()->ConstantNoHole(shared_info, broker()));
node->RemoveInput(4);
if (p.allocation() == AllocationType::kYoung) {
ReplaceWithBuiltinCall(node, Builtin::kFastNewClosure);
} else {
ReplaceWithRuntimeCall(node, Runtime::kNewClosure_Tenured);
}
}
void JSGenericLowering::LowerJSCreateFunctionContext(Node* node) {
const CreateFunctionContextParameters& parameters =
CreateFunctionContextParametersOf(node->op());
ScopeInfoRef scope_info = parameters.scope_info();
int slot_count = parameters.slot_count();
ScopeType scope_type = parameters.scope_type();
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
if (slot_count <= ConstructorBuiltins::MaximumFunctionContextSlots()) {
Callable callable =
CodeFactory::FastNewFunctionContext(isolate(), scope_type);
node->InsertInput(zone(), 0,
jsgraph()->ConstantNoHole(scope_info, broker()));
node->InsertInput(zone(), 1, jsgraph()->Int32Constant(slot_count));
ReplaceWithBuiltinCall(node, callable, flags);
} else {
node->InsertInput(zone(), 0,
jsgraph()->ConstantNoHole(scope_info, broker()));
ReplaceWithRuntimeCall(node, Runtime::kNewFunctionContext);
}
}
void JSGenericLowering::LowerJSCreateGeneratorObject(Node* node) {
node->RemoveInput(4);
ReplaceWithBuiltinCall(node, Builtin::kCreateGeneratorObject);
}
void JSGenericLowering::LowerJSCreateIterResultObject(Node* node) {
ReplaceWithBuiltinCall(node, Builtin::kCreateIterResultObject);
}
void JSGenericLowering::LowerJSCreateStringIterator(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSCreateKeyValueArray(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSCreatePromise(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSCreateTypedArray(Node* node) {
ReplaceWithBuiltinCall(node, Builtin::kCreateTypedArray);
}
void JSGenericLowering::LowerJSCreateLiteralArray(Node* node) {
JSCreateLiteralArrayNode n(node);
CreateLiteralParameters const& p = n.Parameters();
static_assert(n.FeedbackVectorIndex() == 0);
node->InsertInput(zone(), 1,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
node->InsertInput(zone(), 2,
jsgraph()->ConstantNoHole(p.constant(), broker()));
node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.flags()));
if ((p.flags() & AggregateLiteral::kIsShallow) != 0 &&
p.length() < ConstructorBuiltins::kMaximumClonedShallowArrayElements) {
ReplaceWithBuiltinCall(node, Builtin::kCreateShallowArrayLiteral);
} else {
ReplaceWithBuiltinCall(node, Builtin::kCreateArrayFromSlowBoilerplate);
}
}
void JSGenericLowering::LowerJSGetTemplateObject(Node* node) {
JSGetTemplateObjectNode n(node);
GetTemplateObjectParameters const& p = n.Parameters();
SharedFunctionInfoRef shared = p.shared();
TemplateObjectDescriptionRef description = p.description();
DCHECK_EQ(node->op()->ControlInputCount(), 1);
node->RemoveInput(NodeProperties::FirstControlIndex(node));
static_assert(JSGetTemplateObjectNode::FeedbackVectorIndex() == 0);
node->InsertInput(zone(), 0, jsgraph()->ConstantNoHole(shared, broker()));
node->InsertInput(zone(), 1,
jsgraph()->ConstantNoHole(description, broker()));
node->InsertInput(zone(), 2,
jsgraph()->UintPtrConstant(p.feedback().index()));
ReplaceWithBuiltinCall(node, Builtin::kGetTemplateObject);
}
void JSGenericLowering::LowerJSCreateEmptyLiteralArray(Node* node) {
JSCreateEmptyLiteralArrayNode n(node);
FeedbackParameter const& p = n.Parameters();
static_assert(n.FeedbackVectorIndex() == 0);
node->InsertInput(zone(), 1,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
node->RemoveInput(4);
ReplaceWithBuiltinCall(node, Builtin::kCreateEmptyArrayLiteral);
}
void JSGenericLowering::LowerJSCreateArrayFromIterable(Node* node) {
ReplaceWithBuiltinCall(node, Builtin::kIterableToListWithSymbolLookup);
}
void JSGenericLowering::LowerJSSetPrototypeProperties(Node* node) {
SetPrototypePropertiesParameters const& p =
SetPrototypePropertiesParametersOf(node->op());
Node* boilerplate_desc = jsgraph()->HeapConstantNoHole(p.constant.object());
Node* feedback_array =
jsgraph()->HeapConstantNoHole(broker()->CanonicalPersistentHandle(
p.source.vector->closure_feedback_cell_array()));
Node* slot = jsgraph()->SmiConstant(p.source.index());
node->InsertInput(zone(), 1, boilerplate_desc);
node->InsertInput(zone(), 2, feedback_array);
node->InsertInput(zone(), 3, slot);
ReplaceWithRuntimeCall(node, Runtime::kSetPrototypeProperties);
}
void JSGenericLowering::LowerJSCreateLiteralObject(Node* node) {
JSCreateLiteralObjectNode n(node);
CreateLiteralParameters const& p = n.Parameters();
static_assert(n.FeedbackVectorIndex() == 0);
node->InsertInput(zone(), 1,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
node->InsertInput(zone(), 2,
jsgraph()->ConstantNoHole(p.constant(), broker()));
node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.flags()));
if ((p.flags() & AggregateLiteral::kIsShallow) != 0 &&
p.length() <=
ConstructorBuiltins::kMaximumClonedShallowObjectProperties) {
ReplaceWithBuiltinCall(node, Builtin::kCreateShallowObjectLiteral);
} else {
ReplaceWithBuiltinCall(node, Builtin::kCreateObjectFromSlowBoilerplate);
}
}
void JSGenericLowering::LowerJSCloneObject(Node* node) {
JSCloneObjectNode n(node);
CloneObjectParameters const& p = n.Parameters();
static_assert(n.FeedbackVectorIndex() == 1);
node->InsertInput(zone(), 1, jsgraph()->SmiConstant(p.flags()));
node->InsertInput(zone(), 2,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
ReplaceWithBuiltinCall(node, Builtin::kCloneObjectIC);
}
void JSGenericLowering::LowerJSCreateEmptyLiteralObject(Node* node) {
ReplaceWithBuiltinCall(node, Builtin::kCreateEmptyLiteralObject);
}
void JSGenericLowering::LowerJSCreateLiteralRegExp(Node* node) {
JSCreateLiteralRegExpNode n(node);
CreateLiteralParameters const& p = n.Parameters();
static_assert(n.FeedbackVectorIndex() == 0);
node->InsertInput(zone(), 1,
jsgraph()->TaggedIndexConstant(p.feedback().index()));
node->InsertInput(zone(), 2,
jsgraph()->ConstantNoHole(p.constant(), broker()));
node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.flags()));
ReplaceWithBuiltinCall(node, Builtin::kCreateRegExpLiteral);
}
void JSGenericLowering::LowerJSCreateCatchContext(Node* node) {
ScopeInfoRef scope_info = ScopeInfoOf(node->op());
node->InsertInput(zone(), 1, jsgraph()->ConstantNoHole(scope_info, broker()));
ReplaceWithRuntimeCall(node, Runtime::kPushCatchContext);
}
void JSGenericLowering::LowerJSCreateWithContext(Node* node) {
ScopeInfoRef scope_info = ScopeInfoOf(node->op());
node->InsertInput(zone(), 1, jsgraph()->ConstantNoHole(scope_info, broker()));
ReplaceWithRuntimeCall(node, Runtime::kPushWithContext);
}
void JSGenericLowering::LowerJSCreateBlockContext(Node* node) {
ScopeInfoRef scope_info = ScopeInfoOf(node->op());
node->InsertInput(zone(), 0, jsgraph()->ConstantNoHole(scope_info, broker()));
ReplaceWithRuntimeCall(node, Runtime::kPushBlockContext);
}
void JSGenericLowering::LowerJSConstructForwardVarargs(Node* node) {
ConstructForwardVarargsParameters p =
ConstructForwardVarargsParametersOf(node->op());
int const arg_count = static_cast<int>(p.arity() - 2);
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
Callable callable = CodeFactory::ConstructForwardVarargs(isolate());
DCHECK_EQ(callable.descriptor().GetStackParameterCount(), 0);
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), arg_count + 1, flags);
Node* stub_code = jsgraph()->HeapConstantNoHole(callable.code());
Node* stub_arity = jsgraph()->Int32Constant(JSParameterCount(arg_count));
Node* start_index = jsgraph()->Uint32Constant(p.start_index());
Node* receiver = jsgraph()->UndefinedConstant();
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 3, stub_arity);
node->InsertInput(zone(), 4, start_index);
node->InsertInput(zone(), 5, receiver);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
void JSGenericLowering::LowerJSConstructForwardAllArgs(Node* node) {
DCHECK(FrameState{NodeProperties::GetFrameStateInput(node)}
.outer_frame_state()
->opcode() != IrOpcode::kFrameState);
JSConstructForwardAllArgsNode n(node);
Callable callable =
Builtins::CallableFor(isolate(), Builtin::kConstructForwardAllArgs);
DCHECK_EQ(callable.descriptor().GetStackParameterCount(), 0);
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), 0, CallDescriptor::kNeedsFrameState);
Node* stub_code = jsgraph()->HeapConstantNoHole(callable.code());
node->RemoveInput(n.FeedbackVectorIndex());
node->InsertInput(zone(), 0, stub_code);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
void JSGenericLowering::LowerJSConstruct(Node* node) {
JSConstructNode n(node);
ConstructParameters const& p = n.Parameters();
int const arg_count = p.arity_without_implicit_args();
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
static constexpr int kReceiver = 1;
const int stack_argument_count = arg_count + kReceiver;
Callable callable = Builtins::CallableFor(isolate(), Builtin::kConstruct);
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), stack_argument_count, flags);
Node* stub_code = jsgraph()->HeapConstantNoHole(callable.code());
Node* stub_arity = jsgraph()->Int32Constant(JSParameterCount(arg_count));
Node* receiver = jsgraph()->UndefinedConstant();
node->RemoveInput(n.FeedbackVectorIndex());
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 3, stub_arity);
node->InsertInput(zone(), 4, receiver);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
void JSGenericLowering::LowerJSConstructWithArrayLike(Node* node) {
JSConstructWithArrayLikeNode n(node);
ConstructParameters const& p = n.Parameters();
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
const int arg_count = p.arity_without_implicit_args();
DCHECK_EQ(arg_count, 1);
static constexpr int kReceiver = 1;
static constexpr int kArgumentList = 1;
const int stack_argument_count = arg_count - kArgumentList + kReceiver;
Callable callable =
Builtins::CallableFor(isolate(), Builtin::kConstructWithArrayLike);
DCHECK_EQ(callable.descriptor().GetStackParameterCount(), 0);
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), stack_argument_count, flags);
Node* stub_code = jsgraph()->HeapConstantNoHole(callable.code());
Node* receiver = jsgraph()->UndefinedConstant();
node->RemoveInput(n.FeedbackVectorIndex());
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 4, receiver);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
void JSGenericLowering::LowerJSConstructWithSpread(Node* node) {
JSConstructWithSpreadNode n(node);
ConstructParameters const& p = n.Parameters();
int const arg_count = p.arity_without_implicit_args();
DCHECK_GE(arg_count, 1);
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
static constexpr int kReceiver = 1;
static constexpr int kTheSpread = 1;
const int stack_argument_count = arg_count + kReceiver - kTheSpread;
Callable callable = CodeFactory::ConstructWithSpread(isolate());
DCHECK_EQ(callable.descriptor().GetStackParameterCount(), 0);
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), stack_argument_count, flags);
Node* stub_code = jsgraph()->HeapConstantNoHole(callable.code());
Node* stub_arity =
jsgraph()->Int32Constant(JSParameterCount(arg_count - kTheSpread));
Node* receiver = jsgraph()->UndefinedConstant();
DCHECK(n.FeedbackVectorIndex() > n.LastArgumentIndex());
node->RemoveInput(n.FeedbackVectorIndex());
Node* spread = node->RemoveInput(n.LastArgumentIndex());
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 3, stub_arity);
node->InsertInput(zone(), 4, spread);
node->InsertInput(zone(), 5, receiver);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
void JSGenericLowering::LowerJSCallForwardVarargs(Node* node) {
CallForwardVarargsParameters p = CallForwardVarargsParametersOf(node->op());
int const arg_count = static_cast<int>(p.arity() - 2);
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
Callable callable = CodeFactory::CallForwardVarargs(isolate());
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), arg_count + 1, flags);
Node* stub_code = jsgraph()->HeapConstantNoHole(callable.code());
Node* stub_arity = jsgraph()->Int32Constant(JSParameterCount(arg_count));
Node* start_index = jsgraph()->Uint32Constant(p.start_index());
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 2, stub_arity);
node->InsertInput(zone(), 3, start_index);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
void JSGenericLowering::LowerJSCall(Node* node) {
JSCallNode n(node);
CallParameters const& p = n.Parameters();
int const arg_count = p.arity_without_implicit_args();
ConvertReceiverMode const mode = p.convert_mode();
node->RemoveInput(n.FeedbackVectorIndex());
Callable callable = CodeFactory::Call(isolate(), mode);
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), arg_count + 1, flags);
Node* stub_code = jsgraph()->HeapConstantNoHole(callable.code());
Node* stub_arity = jsgraph()->Int32Constant(JSParameterCount(arg_count));
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 2, stub_arity);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
void JSGenericLowering::LowerJSCallWithArrayLike(Node* node) {
JSCallWithArrayLikeNode n(node);
CallParameters const& p = n.Parameters();
const int arg_count = p.arity_without_implicit_args();
DCHECK_EQ(arg_count, 1);
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
static constexpr int kArgumentsList = 1;
static constexpr int kReceiver = 1;
const int stack_argument_count = arg_count - kArgumentsList + kReceiver;
Callable callable = CodeFactory::CallWithArrayLike(isolate());
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), stack_argument_count, flags);
Node* stub_code = jsgraph()->HeapConstantNoHole(callable.code());
Node* receiver = n.receiver();
Node* arguments_list = n.Argument(0);
node->RemoveInput(n.FeedbackVectorIndex());
node->InsertInput(zone(), 0, stub_code);
node->ReplaceInput(2, arguments_list);
node->ReplaceInput(3, receiver);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
void JSGenericLowering::LowerJSCallWithSpread(Node* node) {
JSCallWithSpreadNode n(node);
CallParameters const& p = n.Parameters();
int const arg_count = p.arity_without_implicit_args();
DCHECK_GE(arg_count, 1);
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
static constexpr int kReceiver = 1;
static constexpr int kTheSpread = 1;
const int stack_argument_count = arg_count - kTheSpread + kReceiver;
Callable callable = CodeFactory::CallWithSpread(isolate());
DCHECK_EQ(callable.descriptor().GetStackParameterCount(), 0);
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), stack_argument_count, flags);
Node* stub_code = jsgraph()->HeapConstantNoHole(callable.code());
Node* stub_arity =
jsgraph()->Int32Constant(JSParameterCount(arg_count - kTheSpread));
node->RemoveInput(n.FeedbackVectorIndex());
Node* spread = node->RemoveInput(n.LastArgumentIndex());
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 2, stub_arity);
node->InsertInput(zone(), 3, spread);
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
}
void JSGenericLowering::LowerJSCallRuntime(Node* node) {
const CallRuntimeParameters& p = CallRuntimeParametersOf(node->op());
ReplaceWithRuntimeCall(node, p.id(), static_cast<int>(p.arity()));
}
#if V8_ENABLE_WEBASSEMBLY
void JSGenericLowering::LowerJSWasmCall(Node* node) {}
#endif
void JSGenericLowering::LowerJSForInPrepare(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSForInNext(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSForOfNext(Node* node) {
JSForOfNextNode n(node);
ForOfNextParameters const& p = n.Parameters();
Node* call_slot = jsgraph()->SmiConstant(p.callFeedback().slot.ToInt());
static_assert(n.FeedbackVectorIndex() == 2);
node->InsertInput(zone(), 3, call_slot);
ReplaceWithBuiltinCall(node, Builtin::kForOfNext);
}
void JSGenericLowering::LowerJSLoadMessage(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSStoreMessage(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSLoadModule(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSStoreModule(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSGetImportMeta(Node* node) {
ReplaceWithRuntimeCall(node, Runtime::kGetImportMetaObject);
}
void JSGenericLowering::LowerJSGeneratorStore(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSGeneratorRestoreContinuation(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSGeneratorRestoreContextNoCell(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSGeneratorRestoreInputOrDebugPos(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSGeneratorRestoreRegister(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSStackCheck(Node* node) {
UNREACHABLE();
}
void JSGenericLowering::LowerJSDebugger(Node* node) {
ReplaceWithRuntimeCall(node, Runtime::kHandleDebuggerStatement);
}
Zone* JSGenericLowering::zone() const { return graph()->zone(); }
Isolate* JSGenericLowering::isolate() const { return jsgraph()->isolate(); }
TFGraph* JSGenericLowering::graph() const { return jsgraph()->graph(); }
CommonOperatorBuilder* JSGenericLowering::common() const {
return jsgraph()->common();
}
MachineOperatorBuilder* JSGenericLowering::machine() const {
return jsgraph()->machine();
}
}
}
}