Commit aeb41de0 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Initial support for Array constructor specialization.

Introduce a JSCreateArray operator that represents the Array
constructor, and lower call and construct calls to the Array
constructor to JSCreateArray. Currently we don't yet replace
that with an inline allocation, but always use the specialized
stubs for the Array constructor.

This saves a lot of unnecessary deopts and elements transitions
because now we can actually consume the allocation site feedback
for the transitions.

R=mstarzinger@chromium.org
BUG=v8:4470
LOG=n

Review URL: https://codereview.chromium.org/1466643002

Cr-Commit-Position: refs/heads/master@{#32145}
parent 2fc2cb99
......@@ -2512,7 +2512,8 @@ void AstGraphBuilder::VisitCallSuper(Call* expr) {
VisitForValue(super->new_target_var());
// Create node to perform the super call.
const Operator* call = javascript()->CallConstruct(args->length() + 2);
const Operator* call =
javascript()->CallConstruct(args->length() + 2, VectorSlotPair());
Node* value = ProcessArguments(call, args->length() + 2);
PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
ast_context()->ProduceValue(value);
......@@ -2530,7 +2531,9 @@ void AstGraphBuilder::VisitCallNew(CallNew* expr) {
environment()->Push(environment()->Peek(args->length()));
// Create node to perform the construct call.
const Operator* call = javascript()->CallConstruct(args->length() + 2);
VectorSlotPair feedback = CreateVectorSlotPair(expr->CallNewFeedbackSlot());
const Operator* call =
javascript()->CallConstruct(args->length() + 2, feedback);
Node* value = ProcessArguments(call, args->length() + 2);
PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
ast_context()->ProduceValue(value);
......@@ -3652,8 +3655,7 @@ Node* AstGraphBuilder::BuildLoadGlobalObject() {
Node* AstGraphBuilder::BuildLoadNativeContextField(int index) {
Node* global = BuildLoadGlobalObject();
Node* native_context =
BuildLoadObjectField(global, JSGlobalObject::kNativeContextOffset);
Node* native_context = NewNode(javascript()->LoadNativeContext(), global);
return NewNode(javascript()->LoadContext(0, index, true), native_context);
}
......
......@@ -792,8 +792,9 @@ void BytecodeGraphBuilder::VisitNew(
interpreter::Register first_arg = iterator.GetRegisterOperand(1);
size_t arg_count = iterator.GetCountOperand(2);
const Operator* call =
javascript()->CallConstruct(static_cast<int>(arg_count) + 2);
// TODO(turbofan): Pass the feedback here.
const Operator* call = javascript()->CallConstruct(
static_cast<int>(arg_count) + 2, VectorSlotPair());
Node* value = ProcessCallNewArguments(call, callee, first_arg, arg_count + 2);
AddEmptyFrameStateInputs(value);
environment()->BindAccumulator(value);
......
......@@ -6,7 +6,6 @@
#include "src/compiler/js-graph.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/simplified-operator.h"
#include "src/objects-inl.h"
#include "src/type-feedback-vector-inl.h"
......@@ -40,6 +39,8 @@ VectorSlotPair CallCountFeedback(VectorSlotPair p) {
Reduction JSCallReducer::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kJSCallConstruct:
return ReduceJSCallConstruct(node);
case IrOpcode::kJSCallFunction:
return ReduceJSCallFunction(node);
default:
......@@ -49,6 +50,36 @@ Reduction JSCallReducer::Reduce(Node* node) {
}
// ES6 section 22.1.1 The Array Constructor
Reduction JSCallReducer::ReduceArrayConstructor(Node* node) {
Node* target = NodeProperties::GetValueInput(node, 0);
DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
// Check if we have an allocation site from the CallIC.
Handle<AllocationSite> site;
if (p.feedback().IsValid()) {
CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
Handle<Object> feedback(nexus.GetFeedback(), isolate());
if (feedback->IsAllocationSite()) {
site = Handle<AllocationSite>::cast(feedback);
}
}
// Turn the {node} into a {JSCreateArray} call.
DCHECK_LE(2u, p.arity());
size_t const arity = p.arity() - 2;
NodeProperties::ReplaceValueInput(node, target, 0);
NodeProperties::ReplaceValueInput(node, target, 1);
NodeProperties::RemoveFrameStateInput(node, 1);
// TODO(bmeurer): We might need to propagate the tail call mode to
// the JSCreateArray operator, because an Array call in tail call
// position must always properly consume the parent stack frame.
NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
return Changed(node);
}
// ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray )
Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
......@@ -170,6 +201,7 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
Node* target = NodeProperties::GetValueInput(node, 0);
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
Node* control = NodeProperties::GetControlInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
......@@ -178,8 +210,8 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
HeapObjectMatcher m(target);
if (m.HasValue()) {
if (m.Value()->IsJSFunction()) {
Handle<SharedFunctionInfo> shared(
Handle<JSFunction>::cast(m.Value())->shared(), isolate());
Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
Handle<SharedFunctionInfo> shared(function->shared(), isolate());
// Raise a TypeError if the {target} is a "classConstructor".
if (IsClassConstructor(shared->kind())) {
......@@ -202,7 +234,13 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
break;
}
}
// Check for the ArrayConstructor.
if (*function == function->native_context()->array_function()) {
return ReduceArrayConstructor(node);
}
}
// Don't mess with other {node}s that have a constant {target}.
// TODO(bmeurer): Also support optimizing bound functions and proxies here.
return NoChange();
......@@ -215,14 +253,52 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
if (!p.feedback().IsValid()) return NoChange();
CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
Handle<Object> feedback(nexus.GetFeedback(), isolate());
if (feedback->IsWeakCell()) {
if (feedback->IsAllocationSite()) {
// Retrieve the Array function from the {node}.
Node* array_function;
Handle<Context> native_context;
if (GetNativeContext(node).ToHandle(&native_context)) {
array_function = jsgraph()->HeapConstant(
handle(native_context->array_function(), isolate()));
} else {
Node* global_object = effect = graph()->NewNode(
javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true),
context, context, effect);
Node* native_context = effect = graph()->NewNode(
javascript()->LoadNativeContext(), global_object, context, effect);
array_function = effect = graph()->NewNode(
javascript()->LoadContext(0, Context::ARRAY_FUNCTION_INDEX, true),
native_context, native_context, effect);
}
// Check that the {target} is still the {array_function}.
Node* check = effect =
graph()->NewNode(javascript()->StrictEqual(), target, array_function,
context, effect, control);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* deoptimize =
graph()->NewNode(common()->Deoptimize(), frame_state, effect, if_false);
// TODO(bmeurer): This should be on the AdvancedReducer somehow.
NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
control = graph()->NewNode(common()->IfTrue(), branch);
// Turn the {node} into a {JSCreateArray} call.
NodeProperties::ReplaceValueInput(node, array_function, 0);
NodeProperties::ReplaceEffectInput(node, effect);
NodeProperties::ReplaceControlInput(node, control);
return ReduceArrayConstructor(node);
} else if (feedback->IsWeakCell()) {
Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback);
if (cell->value()->IsJSFunction()) {
Node* target_function =
jsgraph()->Constant(handle(cell->value(), isolate()));
// Check that the {target} is still the {target_function}.
Node* target_function = jsgraph()->HeapConstant(
handle(JSFunction::cast(cell->value()), isolate()));
Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Any()),
target, target_function);
Node* check = effect =
graph()->NewNode(javascript()->StrictEqual(), target, target_function,
context, effect, control);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
......@@ -234,6 +310,7 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
// Specialize the JSCallFunction node to the {target_function}.
NodeProperties::ReplaceValueInput(node, target_function, 0);
NodeProperties::ReplaceEffectInput(node, effect);
NodeProperties::ReplaceControlInput(node, control);
// Try to further reduce the JSCallFunction {node}.
......@@ -245,6 +322,68 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
}
Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
DCHECK_EQ(IrOpcode::kJSCallConstruct, node->opcode());
CallConstructParameters const& p = CallConstructParametersOf(node->op());
DCHECK_LE(2u, p.arity());
int const arity = static_cast<int>(p.arity() - 2);
Node* target = NodeProperties::GetValueInput(node, 0);
Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
// Try to specialize JSCallConstruct {node}s with constant {target}s.
HeapObjectMatcher m(target);
if (m.HasValue()) {
if (m.Value()->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
// Raise a TypeError if the {target} is not a constructor.
if (!function->IsConstructor()) {
NodeProperties::ReplaceValueInputs(node, target);
NodeProperties::ChangeOp(
node,
javascript()->CallRuntime(Runtime::kThrowCalledNonCallable, 1));
return Changed(node);
}
// Check for the ArrayConstructor.
if (*function == function->native_context()->array_function()) {
// Check if we have an allocation site.
Handle<AllocationSite> site;
if (p.feedback().IsValid()) {
Handle<Object> feedback(
p.feedback().vector()->Get(p.feedback().slot()), isolate());
if (feedback->IsAllocationSite()) {
site = Handle<AllocationSite>::cast(feedback);
}
}
// Turn the {node} into a {JSCreateArray} call.
for (int i = arity; i > 0; --i) {
NodeProperties::ReplaceValueInput(
node, NodeProperties::GetValueInput(node, i), i + 1);
}
NodeProperties::ReplaceValueInput(node, new_target, 1);
NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
return Changed(node);
}
}
// Don't mess with other {node}s that have a constant {target}.
// TODO(bmeurer): Also support optimizing bound functions and proxies here.
return NoChange();
}
return NoChange();
}
MaybeHandle<Context> JSCallReducer::GetNativeContext(Node* node) {
Node* const context = NodeProperties::GetContextInput(node);
return NodeProperties::GetSpecializationNativeContext(context,
native_context());
}
Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
......@@ -260,11 +399,6 @@ JSOperatorBuilder* JSCallReducer::javascript() const {
return jsgraph()->javascript();
}
SimplifiedOperatorBuilder* JSCallReducer::simplified() const {
return jsgraph()->simplified();
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -16,11 +16,10 @@ namespace compiler {
class CommonOperatorBuilder;
class JSGraph;
class JSOperatorBuilder;
class SimplifiedOperatorBuilder;
// Performs strength reduction on {JSCallFunction} nodes, which might allow
// inlining or other optimizations to be performed afterwards.
// Performs strength reduction on {JSCallConstruct} and {JSCallFunction} nodes,
// which might allow inlining or other optimizations to be performed afterwards.
class JSCallReducer final : public Reducer {
public:
// Flags that control the mode of operation.
......@@ -30,26 +29,32 @@ class JSCallReducer final : public Reducer {
};
typedef base::Flags<Flag> Flags;
JSCallReducer(JSGraph* jsgraph, Flags flags)
: jsgraph_(jsgraph), flags_(flags) {}
JSCallReducer(JSGraph* jsgraph, Flags flags,
MaybeHandle<Context> native_context)
: jsgraph_(jsgraph), flags_(flags), native_context_(native_context) {}
Reduction Reduce(Node* node) final;
private:
Reduction ReduceArrayConstructor(Node* node);
Reduction ReduceFunctionPrototypeApply(Node* node);
Reduction ReduceFunctionPrototypeCall(Node* node);
Reduction ReduceJSCallConstruct(Node* node);
Reduction ReduceJSCallFunction(Node* node);
MaybeHandle<Context> GetNativeContext(Node* node);
Graph* graph() const;
Flags flags() const { return flags_; }
JSGraph* jsgraph() const { return jsgraph_; }
Isolate* isolate() const;
MaybeHandle<Context> native_context() const { return native_context_; }
CommonOperatorBuilder* common() const;
JSOperatorBuilder* javascript() const;
SimplifiedOperatorBuilder* simplified() const;
JSGraph* const jsgraph_;
Flags const flags_;
MaybeHandle<Context> const native_context_;
};
DEFINE_OPERATORS_FOR_FLAGS(JSCallReducer::Flags)
......
......@@ -429,6 +429,15 @@ void JSGenericLowering::LowerJSStoreContext(Node* node) {
}
void JSGenericLowering::LowerJSLoadNativeContext(Node* node) {
node->ReplaceInput(
1, jsgraph()->IntPtrConstant(JSGlobalObject::kNativeContextOffset -
kHeapObjectTag));
node->AppendInput(zone(), graph()->start());
NodeProperties::ChangeOp(node, machine()->Load(kMachAnyTagged));
}
void JSGenericLowering::LowerJSLoadDynamic(Node* node) {
const DynamicAccess& access = DynamicAccessOf(node->op());
Runtime::FunctionId function_id =
......@@ -465,6 +474,26 @@ void JSGenericLowering::LowerJSCreateArguments(Node* node) {
}
void JSGenericLowering::LowerJSCreateArray(Node* node) {
CreateArrayParameters const& p = CreateArrayParametersOf(node->op());
int const arity = static_cast<int>(p.arity());
Node* new_target = node->InputAt(1);
// TODO(turbofan): We embed the AllocationSite from the Operator at this
// point, which we should not do once we want to both consume the feedback
// but at the same time shared the optimized code across native contexts,
// as the AllocationSite is associated with a single native context (it's
// stored in the type feedback vector after all). Once we go for cross
// context code generation, we should somehow find a way to get to the
// allocation site for the actual native context at runtime.
Node* type_info = p.site().is_null() ? jsgraph()->UndefinedConstant()
: jsgraph()->HeapConstant(p.site());
node->RemoveInput(1);
node->InsertInput(zone(), 1 + arity, new_target);
node->InsertInput(zone(), 2 + arity, type_info);
ReplaceWithRuntimeCall(node, Runtime::kNewArray, arity + 3);
}
void JSGenericLowering::LowerJSCreateClosure(Node* node) {
CreateClosureParameters p = CreateClosureParametersOf(node->op());
node->InsertInput(zone(), 0, jsgraph()->HeapConstant(p.shared_info()));
......@@ -510,8 +539,9 @@ void JSGenericLowering::LowerJSCreateScriptContext(Node* node) {
void JSGenericLowering::LowerJSCallConstruct(Node* node) {
CallConstructParameters const& p = CallConstructParametersOf(node->op());
int const arity = static_cast<int>(p.arity());
// TODO(bmeurer): Use the Construct builtin here.
int arity = OpParameter<int>(node);
CallConstructStub stub(isolate(), SUPER_CONSTRUCTOR_CALL);
CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
......
......@@ -63,6 +63,34 @@ std::ostream& operator<<(std::ostream& os, TailCallMode mode) {
}
bool operator==(CallConstructParameters const& lhs,
CallConstructParameters const& rhs) {
return lhs.arity() == rhs.arity() && lhs.feedback() == rhs.feedback();
}
bool operator!=(CallConstructParameters const& lhs,
CallConstructParameters const& rhs) {
return !(lhs == rhs);
}
size_t hash_value(CallConstructParameters const& p) {
return base::hash_combine(p.arity(), p.feedback());
}
std::ostream& operator<<(std::ostream& os, CallConstructParameters const& p) {
return os << p.arity();
}
CallConstructParameters const& CallConstructParametersOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kJSCallConstruct, op->opcode());
return OpParameter<CallConstructParameters>(op);
}
std::ostream& operator<<(std::ostream& os, CallFunctionParameters const& p) {
os << p.arity() << ", " << p.language_mode() << ", " << p.convert_mode()
<< ", " << p.tail_call_mode();
......@@ -322,6 +350,37 @@ const CreateArgumentsParameters& CreateArgumentsParametersOf(
}
bool operator==(CreateArrayParameters const& lhs,
CreateArrayParameters const& rhs) {
return lhs.arity() == rhs.arity() &&
lhs.site().location() == rhs.site().location();
}
bool operator!=(CreateArrayParameters const& lhs,
CreateArrayParameters const& rhs) {
return !(lhs == rhs);
}
size_t hash_value(CreateArrayParameters const& p) {
return base::hash_combine(p.arity(), p.site().location());
}
std::ostream& operator<<(std::ostream& os, CreateArrayParameters const& p) {
os << p.arity();
if (!p.site().is_null()) os << ", " << Brief(*p.site());
return os;
}
const CreateArrayParameters& CreateArrayParametersOf(const Operator* op) {
DCHECK_EQ(IrOpcode::kJSCreateArray, op->opcode());
return OpParameter<CreateArrayParameters>(op);
}
bool operator==(CreateClosureParameters const& lhs,
CreateClosureParameters const& rhs) {
return lhs.pretenure() == rhs.pretenure() &&
......@@ -374,6 +433,7 @@ const CreateClosureParameters& CreateClosureParametersOf(const Operator* op) {
V(LoadMessage, Operator::kNoThrow, 0, 1) \
V(StoreMessage, Operator::kNoThrow, 1, 0) \
V(StackCheck, Operator::kNoProperties, 0, 0) \
V(LoadNativeContext, Operator::kEliminatable, 1, 1) \
V(CreateWithContext, Operator::kNoProperties, 2, 1) \
V(CreateModuleContext, Operator::kNoProperties, 2, 1)
......@@ -493,12 +553,14 @@ const Operator* JSOperatorBuilder::CallRuntime(Runtime::FunctionId id,
}
const Operator* JSOperatorBuilder::CallConstruct(int arguments) {
return new (zone()) Operator1<int>( // --
const Operator* JSOperatorBuilder::CallConstruct(
size_t arity, VectorSlotPair const& feedback) {
CallConstructParameters parameters(arity, feedback);
return new (zone()) Operator1<CallConstructParameters>( // --
IrOpcode::kJSCallConstruct, Operator::kNoProperties, // opcode
"JSCallConstruct", // name
arguments, 1, 1, 1, 1, 2, // counts
arguments); // parameter
parameters.arity(), 1, 1, 1, 1, 2, // counts
parameters); // parameter
}
......@@ -637,6 +699,19 @@ const Operator* JSOperatorBuilder::CreateArguments(
}
const Operator* JSOperatorBuilder::CreateArray(size_t arity,
Handle<AllocationSite> site) {
// constructor, new_target, arg1, ..., argN
int const value_input_count = static_cast<int>(arity) + 2;
CreateArrayParameters parameters(arity, site);
return new (zone()) Operator1<CreateArrayParameters>( // --
IrOpcode::kJSCreateArray, Operator::kNoProperties, // opcode
"JSCreateArray", // name
value_input_count, 1, 1, 1, 1, 2, // counts
parameters); // parameter
}
const Operator* JSOperatorBuilder::CreateClosure(
Handle<SharedFunctionInfo> shared_info, PretenureFlag pretenure) {
CreateClosureParameters parameters(shared_info, pretenure);
......
......@@ -54,6 +54,31 @@ size_t hash_value(TailCallMode);
std::ostream& operator<<(std::ostream&, TailCallMode);
// Defines the arity and the feedback for a JavaScript constructor call. This is
// used as a parameter by JSCallConstruct operators.
class CallConstructParameters final {
public:
CallConstructParameters(size_t arity, VectorSlotPair const& feedback)
: arity_(arity), feedback_(feedback) {}
size_t arity() const { return arity_; }
VectorSlotPair const& feedback() const { return feedback_; }
private:
size_t const arity_;
VectorSlotPair const feedback_;
};
bool operator==(CallConstructParameters const&, CallConstructParameters const&);
bool operator!=(CallConstructParameters const&, CallConstructParameters const&);
size_t hash_value(CallConstructParameters const&);
std::ostream& operator<<(std::ostream&, CallConstructParameters const&);
CallConstructParameters const& CallConstructParametersOf(Operator const*);
// Defines the arity and the call flags for a JavaScript function call. This is
// used as a parameter by JSCallFunction operators.
class CallFunctionParameters final {
......@@ -327,6 +352,31 @@ const CreateArgumentsParameters& CreateArgumentsParametersOf(
const Operator* op);
// Defines shared information for the array that should be created. This is
// used as parameter by JSCreateArray operators.
class CreateArrayParameters final {
public:
explicit CreateArrayParameters(size_t arity, Handle<AllocationSite> site)
: arity_(arity), site_(site) {}
size_t arity() const { return arity_; }
Handle<AllocationSite> site() const { return site_; }
private:
size_t const arity_;
Handle<AllocationSite> const site_;
};
bool operator==(CreateArrayParameters const&, CreateArrayParameters const&);
bool operator!=(CreateArrayParameters const&, CreateArrayParameters const&);
size_t hash_value(CreateArrayParameters const&);
std::ostream& operator<<(std::ostream&, CreateArrayParameters const&);
const CreateArrayParameters& CreateArrayParametersOf(const Operator* op);
// Defines shared information for the closure that should be created. This is
// used as a parameter by JSCreateClosure operators.
class CreateClosureParameters final {
......@@ -391,6 +441,7 @@ class JSOperatorBuilder final : public ZoneObject {
const Operator* Create();
const Operator* CreateArguments(CreateArgumentsParameters::Type type,
int start_index);
const Operator* CreateArray(size_t arity, Handle<AllocationSite> site);
const Operator* CreateClosure(Handle<SharedFunctionInfo> shared_info,
PretenureFlag pretenure);
const Operator* CreateLiteralArray(int literal_flags);
......@@ -402,7 +453,7 @@ class JSOperatorBuilder final : public ZoneObject {
ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny,
TailCallMode tail_call_mode = TailCallMode::kDisallow);
const Operator* CallRuntime(Runtime::FunctionId id, size_t arity);
const Operator* CallConstruct(int arguments);
const Operator* CallConstruct(size_t arity, VectorSlotPair const& feedback);
const Operator* ConvertReceiver(ConvertReceiverMode convert_mode);
......@@ -430,6 +481,8 @@ class JSOperatorBuilder final : public ZoneObject {
const Operator* LoadContext(size_t depth, size_t index, bool immutable);
const Operator* StoreContext(size_t depth, size_t index);
const Operator* LoadNativeContext();
const Operator* LoadDynamic(const Handle<String>& name,
TypeofMode typeof_mode);
......
......@@ -1232,6 +1232,19 @@ Reduction JSTypedLowering::ReduceJSStoreContext(Node* node) {
}
Reduction JSTypedLowering::ReduceJSLoadNativeContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadNativeContext, node->opcode());
Node* const effect = NodeProperties::GetEffectInput(node);
Node* const control = graph()->start();
node->ReplaceInput(1, effect);
node->ReplaceInput(2, control);
NodeProperties::ChangeOp(
node,
simplified()->LoadField(AccessBuilder::ForJSGlobalObjectNativeContext()));
return Changed(node);
}
Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) {
DCHECK_EQ(IrOpcode::kJSConvertReceiver, node->opcode());
ConvertReceiverMode mode = ConvertReceiverModeOf(node->op());
......@@ -1548,6 +1561,69 @@ Reduction JSTypedLowering::ReduceJSCreateArguments(Node* node) {
}
Reduction JSTypedLowering::ReduceJSCreateArray(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode());
CreateArrayParameters const& p = CreateArrayParametersOf(node->op());
Node* const target = NodeProperties::GetValueInput(node, 0);
Node* const new_target = NodeProperties::GetValueInput(node, 1);
// TODO(bmeurer): Optimize the subclassing case.
if (target != new_target) return NoChange();
// Check if we have a feedback {site} on the {node}.
Handle<AllocationSite> site = p.site();
if (p.site().is_null()) return NoChange();
ElementsKind const elements_kind = site->GetElementsKind();
AllocationSiteOverrideMode override_mode =
(AllocationSite::GetMode(elements_kind) == TRACK_ALLOCATION_SITE)
? DISABLE_ALLOCATION_SITES
: DONT_OVERRIDE;
// Reduce {node} to the appropriate ArrayConstructorStub backend.
// Note that these stubs "behave" like JSFunctions, which means they
// expect a receiver on the stack, which they remove. We just push
// undefined for the receiver.
if (p.arity() == 0) {
ArrayNoArgumentConstructorStub stub(isolate(), elements_kind,
override_mode);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 1,
CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->UndefinedConstant());
node->InsertInput(graph()->zone(), 3, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
return Changed(node);
} else if (p.arity() == 1) {
// TODO(bmeurer): Optimize for the 0 length non-holey case?
ArraySingleArgumentConstructorStub stub(
isolate(), GetHoleyElementsKind(elements_kind), override_mode);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 2,
CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(1));
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
return Changed(node);
} else {
int const arity = static_cast<int>(p.arity());
ArrayNArgumentsConstructorStub stub(isolate(), elements_kind,
override_mode);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(),
arity + 1, CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->UndefinedConstant());
node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(arity));
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
return Changed(node);
}
}
Reduction JSTypedLowering::ReduceJSCreateClosure(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateClosure, node->opcode());
CreateClosureParameters const& p = CreateClosureParametersOf(node->op());
......@@ -2244,12 +2320,16 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSLoadContext(node);
case IrOpcode::kJSStoreContext:
return ReduceJSStoreContext(node);
case IrOpcode::kJSLoadNativeContext:
return ReduceJSLoadNativeContext(node);
case IrOpcode::kJSConvertReceiver:
return ReduceJSConvertReceiver(node);
case IrOpcode::kJSCreate:
return ReduceJSCreate(node);
case IrOpcode::kJSCreateArguments:
return ReduceJSCreateArguments(node);
case IrOpcode::kJSCreateArray:
return ReduceJSCreateArray(node);
case IrOpcode::kJSCreateClosure:
return ReduceJSCreateClosure(node);
case IrOpcode::kJSCreateLiteralArray:
......
......@@ -57,6 +57,7 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceJSInstanceOf(Node* node);
Reduction ReduceJSLoadContext(Node* node);
Reduction ReduceJSStoreContext(Node* node);
Reduction ReduceJSLoadNativeContext(Node* node);
Reduction ReduceJSEqual(Node* node, bool invert);
Reduction ReduceJSStrictEqual(Node* node, bool invert);
Reduction ReduceJSUnaryNot(Node* node);
......@@ -69,6 +70,7 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceJSConvertReceiver(Node* node);
Reduction ReduceJSCreate(Node* node);
Reduction ReduceJSCreateArguments(Node* node);
Reduction ReduceJSCreateArray(Node* node);
Reduction ReduceJSCreateClosure(Node* node);
Reduction ReduceJSCreateLiteralArray(Node* node);
Reduction ReduceJSCreateLiteralObject(Node* node);
......
......@@ -138,6 +138,17 @@ void NodeProperties::ReplaceValueInput(Node* node, Node* value, int index) {
}
// static
void NodeProperties::ReplaceValueInputs(Node* node, Node* value) {
int value_input_count = node->op()->ValueInputCount();
DCHECK_LE(1, value_input_count);
node->ReplaceInput(0, value);
while (--value_input_count > 0) {
node->RemoveInput(value_input_count);
}
}
// static
void NodeProperties::ReplaceContextInput(Node* node, Node* context) {
node->ReplaceInput(FirstContextIndex(node), context);
......
......@@ -88,6 +88,9 @@ class NodeProperties final {
static void RemoveNonValueInputs(Node* node);
static void RemoveValueInputs(Node* node);
// Replaces all value inputs of {node} with the single input {value}.
static void ReplaceValueInputs(Node* node, Node* value);
// Merge the control node {node} into the end of the graph, introducing a
// merge node or expanding an existing merge node if necessary.
static void MergeControlToEnd(Graph* graph, CommonOperatorBuilder* common,
......
......@@ -111,6 +111,7 @@
#define JS_OBJECT_OP_LIST(V) \
V(JSCreate) \
V(JSCreateArguments) \
V(JSCreateArray) \
V(JSCreateClosure) \
V(JSCreateLiteralArray) \
V(JSCreateLiteralObject) \
......@@ -128,6 +129,7 @@
V(JSLoadContext) \
V(JSStoreContext) \
V(JSLoadDynamic) \
V(JSLoadNativeContext) \
V(JSCreateFunctionContext) \
V(JSCreateCatchContext) \
V(JSCreateWithContext) \
......
......@@ -51,6 +51,7 @@ int OperatorProperties::GetFrameStateInputCount(const Operator* op) {
// Object operations
case IrOpcode::kJSCreate:
case IrOpcode::kJSCreateArguments:
case IrOpcode::kJSCreateArray:
case IrOpcode::kJSCreateLiteralArray:
case IrOpcode::kJSCreateLiteralObject:
......
......@@ -198,6 +198,12 @@ class PipelineData {
CommonOperatorBuilder* common() const { return common_; }
JSOperatorBuilder* javascript() const { return javascript_; }
JSGraph* jsgraph() const { return jsgraph_; }
MaybeHandle<Context> native_context() const {
if (info()->is_native_context_specializing()) {
return handle(info()->native_context(), isolate());
}
return MaybeHandle<Context>();
}
LoopAssignmentAnalysis* loop_assignment() const { return loop_assignment_; }
void set_loop_assignment(LoopAssignmentAnalysis* loop_assignment) {
......@@ -508,7 +514,8 @@ struct InliningPhase {
JSCallReducer call_reducer(data->jsgraph(),
data->info()->is_deoptimization_enabled()
? JSCallReducer::kDeoptimizationEnabled
: JSCallReducer::kNoFlags);
: JSCallReducer::kNoFlags,
data->native_context());
JSContextSpecialization context_specialization(
&graph_reducer, data->jsgraph(),
data->info()->is_function_context_specializing()
......@@ -521,19 +528,13 @@ struct InliningPhase {
data->info()->is_deoptimization_enabled()
? JSGlobalObjectSpecialization::kDeoptimizationEnabled
: JSGlobalObjectSpecialization::kNoFlags,
data->info()->is_native_context_specializing()
? handle(data->info()->native_context(), data->isolate())
: MaybeHandle<Context>(),
data->info()->dependencies());
data->native_context(), data->info()->dependencies());
JSNativeContextSpecialization native_context_specialization(
&graph_reducer, data->jsgraph(),
data->info()->is_deoptimization_enabled()
? JSNativeContextSpecialization::kDeoptimizationEnabled
: JSNativeContextSpecialization::kNoFlags,
data->info()->is_native_context_specializing()
? handle(data->info()->native_context(), data->isolate())
: MaybeHandle<Context>(),
data->info()->dependencies(), temp_zone);
data->native_context(), data->info()->dependencies(), temp_zone);
JSInliningHeuristic inlining(&graph_reducer,
data->info()->is_inlining_enabled()
? JSInliningHeuristic::kGeneralInlining
......
......@@ -1215,6 +1215,11 @@ Type* Typer::Visitor::TypeJSCreateArguments(Node* node) {
}
Type* Typer::Visitor::TypeJSCreateArray(Node* node) {
return Type::OtherObject();
}
Type* Typer::Visitor::TypeJSCreateClosure(Node* node) {
return Type::OtherObject();
}
......@@ -1380,6 +1385,11 @@ Type* Typer::Visitor::TypeJSStoreContext(Node* node) {
}
Type* Typer::Visitor::TypeJSLoadNativeContext(Node* node) {
return Type::Intersect(Type::Internal(), Type::TaggedPointer(), zone());
}
Type* Typer::Visitor::TypeJSLoadDynamic(Node* node) { return Type::Any(); }
......
......@@ -511,6 +511,10 @@ void Verifier::Visitor::Check(Node* node) {
// Type is OtherObject.
CheckUpperIs(node, Type::OtherObject());
break;
case IrOpcode::kJSCreateArray:
// Type is OtherObject.
CheckUpperIs(node, Type::OtherObject());
break;
case IrOpcode::kJSCreateClosure:
// Type is Function.
CheckUpperIs(node, Type::OtherObject());
......@@ -545,6 +549,7 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kJSLoadContext:
case IrOpcode::kJSLoadDynamic:
case IrOpcode::kJSLoadNativeContext:
// Type can be anything.
CheckUpperIs(node, Type::Any());
break;
......
......@@ -6258,6 +6258,9 @@ JSObject* JSFunction::global_proxy() {
}
Context* JSFunction::native_context() { return context()->native_context(); }
void JSFunction::set_context(Object* value) {
DCHECK(value->IsUndefined() || value->IsContext());
WRITE_FIELD(this, kContextOffset, value);
......
......@@ -7234,6 +7234,7 @@ class JSFunction: public JSObject {
inline Context* context();
inline void set_context(Object* context);
inline JSObject* global_proxy();
inline Context* native_context();
// [code]: The generated code object for this function. Executed
// when the function is invoked, e.g. foo() or new foo(). See
......
......@@ -266,6 +266,7 @@ TypeImpl<Config>::BitsetType::Lub(i::Map* map) {
// We ought to find a cleaner solution for compiling stubs parameterised
// over type or class variables, esp ones with bounds...
return kDetectable & kTaggedPointer;
case ALLOCATION_SITE_TYPE:
case DECLARED_ACCESSOR_INFO_TYPE:
case EXECUTABLE_ACCESSOR_INFO_TYPE:
case SHARED_FUNCTION_INFO_TYPE:
......@@ -298,7 +299,6 @@ TypeImpl<Config>::BitsetType::Lub(i::Map* map) {
case OBJECT_TEMPLATE_INFO_TYPE:
case SIGNATURE_INFO_TYPE:
case TYPE_SWITCH_INFO_TYPE:
case ALLOCATION_SITE_TYPE:
case ALLOCATION_MEMENTO_TYPE:
case CODE_CACHE_TYPE:
case POLYMORPHIC_CODE_CACHE_TYPE:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment