Commit ed17bab8 authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[turbofan] Support inline allocation of mapped outer arguments.

This adds support for lowering {JSCreateArguments} within outermost
frames of type {CreateArgumentsType::kMappedArguments}. It will hence
enable escape analysis to work with such objects and allow for further
optimization.

This also adds a new {NewMappedArgumentsElements} simplfied operator.
Note that escape analysis support for this new operator will be done as
a follow-up.

R=tebbi@chromium.org

Change-Id: I0e2fac25c654f796433f57b116964053b6b68635
Reviewed-on: https://chromium-review.googlesource.com/641454
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47761}
parent a9f517e2
...@@ -410,11 +410,5 @@ Node* ArgumentsBuiltinsAssembler::EmitFastNewSloppyArguments(Node* context, ...@@ -410,11 +410,5 @@ Node* ArgumentsBuiltinsAssembler::EmitFastNewSloppyArguments(Node* context,
return result.value(); return result.value();
} }
TF_BUILTIN(FastNewSloppyArguments, ArgumentsBuiltinsAssembler) {
Node* function = Parameter(FastNewArgumentsDescriptor::kFunction);
Node* context = Parameter(FastNewArgumentsDescriptor::kContext);
Return(EmitFastNewSloppyArguments(context, function));
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -73,7 +73,6 @@ namespace internal { ...@@ -73,7 +73,6 @@ namespace internal {
TFC(FastNewClosure, FastNewClosure, 1) \ TFC(FastNewClosure, FastNewClosure, 1) \
TFC(FastNewFunctionContextEval, FastNewFunctionContext, 1) \ TFC(FastNewFunctionContextEval, FastNewFunctionContext, 1) \
TFC(FastNewFunctionContextFunction, FastNewFunctionContext, 1) \ TFC(FastNewFunctionContextFunction, FastNewFunctionContext, 1) \
TFC(FastNewSloppyArguments, FastNewArguments, 1) \
TFC(FastCloneRegExp, FastCloneRegExp, 1) \ TFC(FastCloneRegExp, FastCloneRegExp, 1) \
TFC(FastCloneShallowArrayTrack, FastCloneShallowArray, 1) \ TFC(FastCloneShallowArrayTrack, FastCloneShallowArray, 1) \
TFC(FastCloneShallowArrayDontTrack, FastCloneShallowArray, 1) \ TFC(FastCloneShallowArrayDontTrack, FastCloneShallowArray, 1) \
...@@ -170,7 +169,7 @@ namespace internal { ...@@ -170,7 +169,7 @@ namespace internal {
TFS(CopyFastSmiOrObjectElements, kObject) \ TFS(CopyFastSmiOrObjectElements, kObject) \
TFC(GrowFastDoubleElements, GrowArrayElements, 1) \ TFC(GrowFastDoubleElements, GrowArrayElements, 1) \
TFC(GrowFastSmiOrObjectElements, GrowArrayElements, 1) \ TFC(GrowFastSmiOrObjectElements, GrowArrayElements, 1) \
TFC(NewUnmappedArgumentsElements, NewArgumentsElements, 1) \ TFC(NewArgumentsElements, NewArgumentsElements, 1) \
\ \
/* Debugger */ \ /* Debugger */ \
ASM(FrameDropperTrampoline) \ ASM(FrameDropperTrampoline) \
......
...@@ -168,9 +168,10 @@ TF_BUILTIN(GrowFastSmiOrObjectElements, CodeStubAssembler) { ...@@ -168,9 +168,10 @@ TF_BUILTIN(GrowFastSmiOrObjectElements, CodeStubAssembler) {
TailCallRuntime(Runtime::kGrowArrayElements, context, object, key); TailCallRuntime(Runtime::kGrowArrayElements, context, object, key);
} }
TF_BUILTIN(NewUnmappedArgumentsElements, CodeStubAssembler) { TF_BUILTIN(NewArgumentsElements, CodeStubAssembler) {
Node* frame = Parameter(Descriptor::kFrame); Node* frame = Parameter(Descriptor::kFrame);
Node* length = SmiToWord(Parameter(Descriptor::kLength)); Node* length = SmiToWord(Parameter(Descriptor::kLength));
Node* mapped_count = SmiToWord(Parameter(Descriptor::kMappedCount));
// Check if we can allocate in new space. // Check if we can allocate in new space.
ElementsKind kind = PACKED_ELEMENTS; ElementsKind kind = PACKED_ELEMENTS;
...@@ -195,21 +196,49 @@ TF_BUILTIN(NewUnmappedArgumentsElements, CodeStubAssembler) { ...@@ -195,21 +196,49 @@ TF_BUILTIN(NewUnmappedArgumentsElements, CodeStubAssembler) {
// Allocate a FixedArray in new space. // Allocate a FixedArray in new space.
Node* result = AllocateFixedArray(kind, length); Node* result = AllocateFixedArray(kind, length);
// The elements might be used to back mapped arguments. In that case fill
// the mapped elements (i.e. the first {mapped_count}) with the hole, but
// make sure not to overshoot the {length} if some arguments are missing.
Node* number_of_holes =
SelectConstant(IntPtrLessThan(mapped_count, length), mapped_count,
length, MachineType::PointerRepresentation());
Node* the_hole = TheHoleConstant();
// Fill the first elements up to {number_of_holes} with the hole.
VARIABLE(var_index, MachineType::PointerRepresentation());
Label loop1(this, &var_index), done_loop1(this);
var_index.Bind(IntPtrConstant(0));
Goto(&loop1);
BIND(&loop1);
{
// Load the current {index}.
Node* index = var_index.value();
// Check if we are done.
GotoIf(WordEqual(index, number_of_holes), &done_loop1);
// Store the hole into the {result}.
StoreFixedArrayElement(result, index, the_hole, SKIP_WRITE_BARRIER);
// Continue with next {index}.
var_index.Bind(IntPtrAdd(index, IntPtrConstant(1)));
Goto(&loop1);
}
BIND(&done_loop1);
// Compute the effective {offset} into the {frame}. // Compute the effective {offset} into the {frame}.
Node* offset = IntPtrAdd(length, IntPtrConstant(1)); Node* offset = IntPtrAdd(length, IntPtrConstant(1));
// Copy the parameters from {frame} (starting at {offset}) to {result}. // Copy the parameters from {frame} (starting at {offset}) to {result}.
VARIABLE(var_index, MachineType::PointerRepresentation()); Label loop2(this, &var_index), done_loop2(this);
Label loop(this, &var_index), done_loop(this); Goto(&loop2);
var_index.Bind(IntPtrConstant(0)); BIND(&loop2);
Goto(&loop);
BIND(&loop);
{ {
// Load the current {index}. // Load the current {index}.
Node* index = var_index.value(); Node* index = var_index.value();
// Check if we are done. // Check if we are done.
GotoIf(WordEqual(index, length), &done_loop); GotoIf(WordEqual(index, length), &done_loop2);
// Load the parameter at the given {index}. // Load the parameter at the given {index}.
Node* value = Load(MachineType::AnyTagged(), frame, Node* value = Load(MachineType::AnyTagged(), frame,
...@@ -220,10 +249,10 @@ TF_BUILTIN(NewUnmappedArgumentsElements, CodeStubAssembler) { ...@@ -220,10 +249,10 @@ TF_BUILTIN(NewUnmappedArgumentsElements, CodeStubAssembler) {
// Continue with next {index}. // Continue with next {index}.
var_index.Bind(IntPtrAdd(index, IntPtrConstant(1))); var_index.Bind(IntPtrAdd(index, IntPtrConstant(1)));
Goto(&loop); Goto(&loop2);
} }
BIND(&done_loop2);
BIND(&done_loop);
Return(result); Return(result);
} }
} }
...@@ -232,7 +261,8 @@ TF_BUILTIN(NewUnmappedArgumentsElements, CodeStubAssembler) { ...@@ -232,7 +261,8 @@ TF_BUILTIN(NewUnmappedArgumentsElements, CodeStubAssembler) {
{ {
// Allocate in old space (or large object space). // Allocate in old space (or large object space).
TailCallRuntime(Runtime::kNewArgumentsElements, NoContextConstant(), TailCallRuntime(Runtime::kNewArgumentsElements, NoContextConstant(),
BitcastWordToTagged(frame), SmiFromWord(length)); BitcastWordToTagged(frame), SmiFromWord(length),
SmiFromWord(mapped_count));
} }
} }
......
...@@ -751,6 +751,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -751,6 +751,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kArgumentsLength: case IrOpcode::kArgumentsLength:
result = LowerArgumentsLength(node); result = LowerArgumentsLength(node);
break; break;
case IrOpcode::kNewMappedArgumentsElements:
result = LowerNewMappedArgumentsElements(node);
break;
case IrOpcode::kNewUnmappedArgumentsElements: case IrOpcode::kNewUnmappedArgumentsElements:
result = LowerNewUnmappedArgumentsElements(node); result = LowerNewUnmappedArgumentsElements(node);
break; break;
...@@ -2169,18 +2172,33 @@ Node* EffectControlLinearizer::LowerArgumentsFrame(Node* node) { ...@@ -2169,18 +2172,33 @@ Node* EffectControlLinearizer::LowerArgumentsFrame(Node* node) {
return done.PhiAt(0); return done.PhiAt(0);
} }
Node* EffectControlLinearizer::LowerNewMappedArgumentsElements(Node* node) {
Node* frame = NodeProperties::GetValueInput(node, 0);
Node* length = NodeProperties::GetValueInput(node, 1);
int mapped_count = OpParameter<int>(node);
Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kNewArgumentsElements);
Operator::Properties const properties = node->op()->properties();
CallDescriptor::Flags const flags = CallDescriptor::kNoFlags;
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties);
return __ Call(desc, __ HeapConstant(callable.code()), frame, length,
__ SmiConstant(mapped_count), __ NoContextConstant());
}
Node* EffectControlLinearizer::LowerNewUnmappedArgumentsElements(Node* node) { Node* EffectControlLinearizer::LowerNewUnmappedArgumentsElements(Node* node) {
Node* frame = NodeProperties::GetValueInput(node, 0); Node* frame = NodeProperties::GetValueInput(node, 0);
Node* length = NodeProperties::GetValueInput(node, 1); Node* length = NodeProperties::GetValueInput(node, 1);
Callable const callable = Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kNewUnmappedArgumentsElements); Builtins::CallableFor(isolate(), Builtins::kNewArgumentsElements);
Operator::Properties const properties = node->op()->properties(); Operator::Properties const properties = node->op()->properties();
CallDescriptor::Flags const flags = CallDescriptor::kNoFlags; CallDescriptor::Flags const flags = CallDescriptor::kNoFlags;
CallDescriptor* desc = Linkage::GetStubCallDescriptor( CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties); isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties);
return __ Call(desc, __ HeapConstant(callable.code()), frame, length, return __ Call(desc, __ HeapConstant(callable.code()), frame, length,
__ NoContextConstant()); __ SmiConstant(0), __ NoContextConstant());
} }
Node* EffectControlLinearizer::LowerArrayBufferWasNeutered(Node* node) { Node* EffectControlLinearizer::LowerArrayBufferWasNeutered(Node* node) {
......
...@@ -97,6 +97,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ...@@ -97,6 +97,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerObjectIsUndetectable(Node* node); Node* LowerObjectIsUndetectable(Node* node);
Node* LowerArgumentsFrame(Node* node); Node* LowerArgumentsFrame(Node* node);
Node* LowerArgumentsLength(Node* node); Node* LowerArgumentsLength(Node* node);
Node* LowerNewMappedArgumentsElements(Node* node);
Node* LowerNewUnmappedArgumentsElements(Node* node); Node* LowerNewUnmappedArgumentsElements(Node* node);
Node* LowerArrayBufferWasNeutered(Node* node); Node* LowerArrayBufferWasNeutered(Node* node);
Node* LowerStringCharAt(Node* node); Node* LowerStringCharAt(Node* node);
......
...@@ -311,47 +311,38 @@ Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node) { ...@@ -311,47 +311,38 @@ Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node) {
case CreateArgumentsType::kMappedArguments: { case CreateArgumentsType::kMappedArguments: {
// TODO(mstarzinger): Duplicate parameters are not handled yet. // TODO(mstarzinger): Duplicate parameters are not handled yet.
if (shared->has_duplicate_parameters()) return NoChange(); if (shared->has_duplicate_parameters()) return NoChange();
// If there is no aliasing, the arguments object elements are not Node* const callee = NodeProperties::GetValueInput(node, 0);
// special in any way, we can just return an unmapped backing store. Node* const context = NodeProperties::GetContextInput(node);
if (shared->internal_formal_parameter_count() == 0) { Node* effect = NodeProperties::GetEffectInput(node);
Node* const callee = NodeProperties::GetValueInput(node, 0); Node* const arguments_frame =
Node* effect = NodeProperties::GetEffectInput(node); graph()->NewNode(simplified()->ArgumentsFrame());
Node* const arguments_frame = Node* const arguments_length = graph()->NewNode(
graph()->NewNode(simplified()->ArgumentsFrame()); simplified()->ArgumentsLength(
Node* const arguments_length = graph()->NewNode( shared->internal_formal_parameter_count(), false),
simplified()->ArgumentsLength(0, false), arguments_frame); arguments_frame);
// Allocate the elements backing store. // Allocate the elements backing store.
Node* const elements = effect = bool has_aliased_arguments = false;
graph()->NewNode(simplified()->NewUnmappedArgumentsElements(), Node* const elements = effect = AllocateAliasedArguments(
arguments_frame, arguments_length, effect); effect, control, context, arguments_frame, arguments_length, shared,
// Load the arguments object map. &has_aliased_arguments);
Node* const arguments_map = jsgraph()->HeapConstant( // Load the arguments object map.
handle(native_context()->sloppy_arguments_map(), isolate())); Node* const arguments_map = jsgraph()->HeapConstant(
// Actually allocate and initialize the arguments object. handle(has_aliased_arguments
AllocationBuilder a(jsgraph(), effect, control); ? native_context()->fast_aliased_arguments_map()
Node* properties = jsgraph()->EmptyFixedArrayConstant(); : native_context()->sloppy_arguments_map(),
STATIC_ASSERT(JSSloppyArgumentsObject::kSize == 5 * kPointerSize); isolate()));
a.Allocate(JSSloppyArgumentsObject::kSize); // Actually allocate and initialize the arguments object.
a.Store(AccessBuilder::ForMap(), arguments_map); AllocationBuilder a(jsgraph(), effect, control);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties); Node* properties = jsgraph()->EmptyFixedArrayConstant();
a.Store(AccessBuilder::ForJSObjectElements(), elements); STATIC_ASSERT(JSSloppyArgumentsObject::kSize == 5 * kPointerSize);
a.Store(AccessBuilder::ForArgumentsLength(), arguments_length); a.Allocate(JSSloppyArgumentsObject::kSize);
a.Store(AccessBuilder::ForArgumentsCallee(), callee); a.Store(AccessBuilder::ForMap(), arguments_map);
RelaxControls(node); a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
a.FinishAndChange(node); a.Store(AccessBuilder::ForJSObjectElements(), elements);
} else { a.Store(AccessBuilder::ForArgumentsLength(), arguments_length);
Callable callable = Builtins::CallableFor( a.Store(AccessBuilder::ForArgumentsCallee(), callee);
isolate(), Builtins::kFastNewSloppyArguments); RelaxControls(node);
Operator::Properties properties = node->op()->properties(); a.FinishAndChange(node);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0,
CallDescriptor::kNoFlags, properties);
const Operator* new_op = common()->Call(desc);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
node->InsertInput(graph()->zone(), 0, stub_code);
node->RemoveInput(3); // Remove the frame state.
NodeProperties::ChangeOp(node, new_op);
}
return Changed(node); return Changed(node);
} }
case CreateArgumentsType::kUnmappedArguments: { case CreateArgumentsType::kUnmappedArguments: {
...@@ -386,9 +377,9 @@ Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node) { ...@@ -386,9 +377,9 @@ Reduction JSCreateLowering::ReduceJSCreateArguments(Node* node) {
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* const arguments_frame = Node* const arguments_frame =
graph()->NewNode(simplified()->ArgumentsFrame()); graph()->NewNode(simplified()->ArgumentsFrame());
int formal_parameter_count = shared->internal_formal_parameter_count();
Node* const rest_length = graph()->NewNode( Node* const rest_length = graph()->NewNode(
simplified()->ArgumentsLength(formal_parameter_count, true), simplified()->ArgumentsLength(
shared->internal_formal_parameter_count(), true),
arguments_frame); arguments_frame);
// Allocate the elements backing store. Since // Allocate the elements backing store. Since
// NewUnmappedArgumentsElements copies from the end of the arguments // NewUnmappedArgumentsElements copies from the end of the arguments
...@@ -1201,6 +1192,53 @@ Node* JSCreateLowering::AllocateAliasedArguments( ...@@ -1201,6 +1192,53 @@ Node* JSCreateLowering::AllocateAliasedArguments(
return a.Finish(); return a.Finish();
} }
// Helper that allocates a FixedArray serving as a parameter map for values
// unknown at compile-time, the true {arguments_length} and {arguments_frame}
// values can only be determined dynamically at run-time and are provided.
// Serves as backing store for JSCreateArguments nodes.
Node* JSCreateLowering::AllocateAliasedArguments(
Node* effect, Node* control, Node* context, Node* arguments_frame,
Node* arguments_length, Handle<SharedFunctionInfo> shared,
bool* has_aliased_arguments) {
// If there is no aliasing, the arguments object elements are not
// special in any way, we can just return an unmapped backing store.
int parameter_count = shared->internal_formal_parameter_count();
if (parameter_count == 0) {
return graph()->NewNode(simplified()->NewUnmappedArgumentsElements(),
arguments_frame, arguments_length, effect);
}
// From here on we are going to allocate a mapped (aka. aliased) elements
// backing store. We do not statically know how many arguments exist, but
// dynamically selecting the hole for some of the "mapped" elements allows
// using a static shape for the parameter map.
int mapped_count = parameter_count;
*has_aliased_arguments = true;
// The unmapped argument values are stored yet another indirection away and
// then linked into the parameter map below, whereas mapped argument values
// (i.e. the first {mapped_count} elements) are replaced with a hole instead.
Node* arguments =
graph()->NewNode(simplified()->NewMappedArgumentsElements(mapped_count),
arguments_frame, arguments_length, effect);
// Actually allocate the backing store.
AllocationBuilder a(jsgraph(), arguments, control);
a.AllocateArray(mapped_count + 2, factory()->sloppy_arguments_elements_map());
a.Store(AccessBuilder::ForFixedArraySlot(0), context);
a.Store(AccessBuilder::ForFixedArraySlot(1), arguments);
for (int i = 0; i < mapped_count; ++i) {
int idx = Context::MIN_CONTEXT_SLOTS + parameter_count - 1 - i;
Node* value = graph()->NewNode(
common()->Select(MachineRepresentation::kTagged),
graph()->NewNode(simplified()->NumberLessThan(), jsgraph()->Constant(i),
arguments_length),
jsgraph()->Constant(idx), jsgraph()->TheHoleConstant());
a.Store(AccessBuilder::ForFixedArraySlot(i + 2), value);
}
return a.Finish();
}
Node* JSCreateLowering::AllocateElements(Node* effect, Node* control, Node* JSCreateLowering::AllocateElements(Node* effect, Node* control,
ElementsKind elements_kind, ElementsKind elements_kind,
int capacity, int capacity,
......
...@@ -74,6 +74,10 @@ class V8_EXPORT_PRIVATE JSCreateLowering final ...@@ -74,6 +74,10 @@ class V8_EXPORT_PRIVATE JSCreateLowering final
Node* AllocateAliasedArguments(Node* effect, Node* control, Node* frame_state, Node* AllocateAliasedArguments(Node* effect, Node* control, Node* frame_state,
Node* context, Handle<SharedFunctionInfo>, Node* context, Handle<SharedFunctionInfo>,
bool* has_aliased_arguments); bool* has_aliased_arguments);
Node* AllocateAliasedArguments(Node* effect, Node* control, Node* context,
Node* arguments_frame, Node* arguments_length,
Handle<SharedFunctionInfo>,
bool* has_aliased_arguments);
Node* AllocateElements(Node* effect, Node* control, Node* AllocateElements(Node* effect, Node* control,
ElementsKind elements_kind, int capacity, ElementsKind elements_kind, int capacity,
PretenureFlag pretenure); PretenureFlag pretenure);
......
...@@ -363,6 +363,7 @@ ...@@ -363,6 +363,7 @@
V(ObjectIsUndetectable) \ V(ObjectIsUndetectable) \
V(ArgumentsFrame) \ V(ArgumentsFrame) \
V(ArgumentsLength) \ V(ArgumentsLength) \
V(NewMappedArgumentsElements) \
V(NewUnmappedArgumentsElements) \ V(NewUnmappedArgumentsElements) \
V(ArrayBufferWasNeutered) \ V(ArrayBufferWasNeutered) \
V(EnsureWritableFastElements) \ V(EnsureWritableFastElements) \
......
...@@ -2734,6 +2734,7 @@ class RepresentationSelector { ...@@ -2734,6 +2734,7 @@ class RepresentationSelector {
MachineRepresentation::kTaggedSigned); MachineRepresentation::kTaggedSigned);
return; return;
} }
case IrOpcode::kNewMappedArgumentsElements:
case IrOpcode::kNewUnmappedArgumentsElements: { case IrOpcode::kNewUnmappedArgumentsElements: {
VisitBinop(node, UseInfo::PointerInt(), UseInfo::TaggedSigned(), VisitBinop(node, UseInfo::PointerInt(), UseInfo::TaggedSigned(),
MachineRepresentation::kTaggedPointer); MachineRepresentation::kTaggedPointer);
......
...@@ -976,6 +976,16 @@ bool IsRestLengthOf(const Operator* op) { ...@@ -976,6 +976,16 @@ bool IsRestLengthOf(const Operator* op) {
return OpParameter<ArgumentsLengthParameters>(op).is_rest_length; return OpParameter<ArgumentsLengthParameters>(op).is_rest_length;
} }
const Operator* SimplifiedOperatorBuilder::NewMappedArgumentsElements(
int mapped_count) {
return new (zone()) Operator1<int>( // --
IrOpcode::kNewMappedArgumentsElements, // opcode
Operator::kEliminatable, // flags
"NewMappedArgumentsElements", // name
2, 1, 0, 1, 1, 0, // counts
mapped_count); // parameter
}
const Operator* SimplifiedOperatorBuilder::Allocate(Type* type, const Operator* SimplifiedOperatorBuilder::Allocate(Type* type,
PretenureFlag pretenure) { PretenureFlag pretenure) {
return new (zone()) Operator1<AllocateParameters>( return new (zone()) Operator1<AllocateParameters>(
......
...@@ -451,7 +451,10 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -451,7 +451,10 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* ArgumentsLength(int formal_parameter_count, const Operator* ArgumentsLength(int formal_parameter_count,
bool is_rest_length); bool is_rest_length);
// new-unmapped-arguments-elements // new-mapped-arguments-elements arguments-frame, arguments-length
const Operator* NewMappedArgumentsElements(int mapped_count);
// new-unmapped-arguments-elements arguments-frame, arguments-length
const Operator* NewUnmappedArgumentsElements(); const Operator* NewUnmappedArgumentsElements();
// array-buffer-was-neutered buffer // array-buffer-was-neutered buffer
......
...@@ -2001,6 +2001,10 @@ Type* Typer::Visitor::TypeArgumentsFrame(Node* node) { ...@@ -2001,6 +2001,10 @@ Type* Typer::Visitor::TypeArgumentsFrame(Node* node) {
return Type::ExternalPointer(); return Type::ExternalPointer();
} }
Type* Typer::Visitor::TypeNewMappedArgumentsElements(Node* node) {
return Type::OtherInternal();
}
Type* Typer::Visitor::TypeNewUnmappedArgumentsElements(Node* node) { Type* Typer::Visitor::TypeNewUnmappedArgumentsElements(Node* node) {
return Type::OtherInternal(); return Type::OtherInternal();
} }
......
...@@ -1029,6 +1029,7 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -1029,6 +1029,7 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kArgumentsFrame: case IrOpcode::kArgumentsFrame:
CheckTypeIs(node, Type::ExternalPointer()); CheckTypeIs(node, Type::ExternalPointer());
break; break;
case IrOpcode::kNewMappedArgumentsElements:
case IrOpcode::kNewUnmappedArgumentsElements: case IrOpcode::kNewUnmappedArgumentsElements:
CheckValueInputIs(node, 0, Type::ExternalPointer()); CheckValueInputIs(node, 0, Type::ExternalPointer());
CheckValueInputIs(node, 1, Type::Range(-Code::kMaxArguments, CheckValueInputIs(node, 1, Type::Range(-Code::kMaxArguments,
......
...@@ -157,8 +157,8 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate, ...@@ -157,8 +157,8 @@ DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
Handle<StringSet> non_locals = it.GetNonLocals(); Handle<StringSet> non_locals = it.GetNonLocals();
MaterializeReceiver(materialized, local_context, local_function, MaterializeReceiver(materialized, local_context, local_function,
non_locals); non_locals);
frame_inspector.MaterializeStackLocals(materialized, local_function); frame_inspector.MaterializeStackLocals(materialized, local_function,
MaterializeArgumentsObject(materialized, local_function); true);
ContextChainElement context_chain_element; ContextChainElement context_chain_element;
context_chain_element.scope_info = it.CurrentScopeInfo(); context_chain_element.scope_info = it.CurrentScopeInfo();
context_chain_element.materialized_object = materialized; context_chain_element.materialized_object = materialized;
...@@ -223,24 +223,6 @@ void DebugEvaluate::ContextBuilder::UpdateValues() { ...@@ -223,24 +223,6 @@ void DebugEvaluate::ContextBuilder::UpdateValues() {
} }
void DebugEvaluate::ContextBuilder::MaterializeArgumentsObject(
Handle<JSObject> target, Handle<JSFunction> function) {
// Do not materialize the arguments object for eval or top-level code.
// Skip if "arguments" is already taken.
if (function->shared()->is_toplevel()) return;
Maybe<bool> maybe = JSReceiver::HasOwnProperty(
target, isolate_->factory()->arguments_string());
DCHECK(maybe.IsJust());
if (maybe.FromJust()) return;
// FunctionGetArguments can't throw an exception.
Handle<JSObject> arguments = Accessors::FunctionGetArguments(function);
Handle<String> arguments_str = isolate_->factory()->arguments_string();
JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments,
NONE)
.Check();
}
void DebugEvaluate::ContextBuilder::MaterializeReceiver( void DebugEvaluate::ContextBuilder::MaterializeReceiver(
Handle<JSObject> target, Handle<Context> local_context, Handle<JSObject> target, Handle<Context> local_context,
Handle<JSFunction> local_function, Handle<StringSet> non_locals) { Handle<JSFunction> local_function, Handle<StringSet> non_locals) {
......
...@@ -66,11 +66,6 @@ class DebugEvaluate : public AllStatic { ...@@ -66,11 +66,6 @@ class DebugEvaluate : public AllStatic {
Handle<StringSet> whitelist; Handle<StringSet> whitelist;
}; };
// Helper function to find or create the arguments object for
// Runtime_DebugEvaluate.
void MaterializeArgumentsObject(Handle<JSObject> target,
Handle<JSFunction> function);
void MaterializeReceiver(Handle<JSObject> target, void MaterializeReceiver(Handle<JSObject> target,
Handle<Context> local_context, Handle<Context> local_context,
Handle<JSFunction> local_function, Handle<JSFunction> local_function,
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/debug/debug-frames.h" #include "src/debug/debug-frames.h"
#include "src/accessors.h"
#include "src/frames-inl.h" #include "src/frames-inl.h"
#include "src/wasm/wasm-interpreter.h" #include "src/wasm/wasm-interpreter.h"
#include "src/wasm/wasm-objects-inl.h" #include "src/wasm/wasm-objects-inl.h"
...@@ -108,7 +109,8 @@ void FrameInspector::SetArgumentsFrame(StandardFrame* frame) { ...@@ -108,7 +109,8 @@ void FrameInspector::SetArgumentsFrame(StandardFrame* frame) {
// Create a plain JSObject which materializes the local scope for the specified // Create a plain JSObject which materializes the local scope for the specified
// frame. // frame.
void FrameInspector::MaterializeStackLocals(Handle<JSObject> target, void FrameInspector::MaterializeStackLocals(Handle<JSObject> target,
Handle<ScopeInfo> scope_info) { Handle<ScopeInfo> scope_info,
bool materialize_arguments_object) {
HandleScope scope(isolate_); HandleScope scope(isolate_);
// First fill all parameters. // First fill all parameters.
for (int i = 0; i < scope_info->ParameterCount(); ++i) { for (int i = 0; i < scope_info->ParameterCount(); ++i) {
...@@ -139,18 +141,41 @@ void FrameInspector::MaterializeStackLocals(Handle<JSObject> target, ...@@ -139,18 +141,41 @@ void FrameInspector::MaterializeStackLocals(Handle<JSObject> target,
value = isolate_->factory()->undefined_value(); value = isolate_->factory()->undefined_value();
} }
if (value->IsOptimizedOut(isolate_)) { if (value->IsOptimizedOut(isolate_)) {
if (materialize_arguments_object) {
Handle<String> arguments_str = isolate_->factory()->arguments_string();
if (String::Equals(name, arguments_str)) continue;
}
value = isolate_->factory()->undefined_value(); value = isolate_->factory()->undefined_value();
} }
JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check(); JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check();
} }
} }
void FrameInspector::MaterializeStackLocals(Handle<JSObject> target, void FrameInspector::MaterializeStackLocals(Handle<JSObject> target,
Handle<JSFunction> function) { Handle<JSFunction> function,
bool materialize_arguments_object) {
// Do not materialize the arguments object for eval or top-level code.
if (function->shared()->is_toplevel()) materialize_arguments_object = false;
Handle<SharedFunctionInfo> shared(function->shared()); Handle<SharedFunctionInfo> shared(function->shared());
Handle<ScopeInfo> scope_info(shared->scope_info()); Handle<ScopeInfo> scope_info(shared->scope_info());
MaterializeStackLocals(target, scope_info); MaterializeStackLocals(target, scope_info, materialize_arguments_object);
// Third materialize the arguments object.
if (materialize_arguments_object) {
// Skip if "arguments" is already taken and wasn't optimized out (which
// causes {MaterializeStackLocals} above to skip the local variable).
Handle<String> arguments_str = isolate_->factory()->arguments_string();
Maybe<bool> maybe = JSReceiver::HasOwnProperty(target, arguments_str);
DCHECK(maybe.IsJust());
if (maybe.FromJust()) return;
// FunctionGetArguments can't throw an exception.
Handle<JSObject> arguments = Accessors::FunctionGetArguments(function);
JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments,
NONE)
.Check();
}
} }
......
...@@ -49,10 +49,12 @@ class FrameInspector { ...@@ -49,10 +49,12 @@ class FrameInspector {
void SetArgumentsFrame(StandardFrame* frame); void SetArgumentsFrame(StandardFrame* frame);
void MaterializeStackLocals(Handle<JSObject> target, void MaterializeStackLocals(Handle<JSObject> target,
Handle<ScopeInfo> scope_info); Handle<ScopeInfo> scope_info,
bool materialize_arguments_object = false);
void MaterializeStackLocals(Handle<JSObject> target, void MaterializeStackLocals(Handle<JSObject> target,
Handle<JSFunction> function); Handle<JSFunction> function,
bool materialize_arguments_object = false);
void UpdateStackLocalsFromMaterializedObject(Handle<JSObject> object, void UpdateStackLocalsFromMaterializedObject(Handle<JSObject> object,
Handle<ScopeInfo> scope_info); Handle<ScopeInfo> scope_info);
......
...@@ -2361,7 +2361,7 @@ Handle<Object> GetValueForDebugger(TranslatedFrame::iterator it, ...@@ -2361,7 +2361,7 @@ Handle<Object> GetValueForDebugger(TranslatedFrame::iterator it,
Isolate* isolate) { Isolate* isolate) {
if (it->GetRawValue() == isolate->heap()->arguments_marker()) { if (it->GetRawValue() == isolate->heap()->arguments_marker()) {
if (!it->IsMaterializableByDebugger()) { if (!it->IsMaterializableByDebugger()) {
return isolate->factory()->undefined_value(); return isolate->factory()->optimized_out();
} }
} }
return it->GetValue(); return it->GetValue();
......
...@@ -386,8 +386,9 @@ void GrowArrayElementsDescriptor::InitializePlatformSpecific( ...@@ -386,8 +386,9 @@ void GrowArrayElementsDescriptor::InitializePlatformSpecific(
void NewArgumentsElementsDescriptor::InitializePlatformIndependent( void NewArgumentsElementsDescriptor::InitializePlatformIndependent(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
// kFrame, kLength // kFrame, kLength, kMappedCount
MachineType const kMachineTypes[] = {MachineType::Pointer(), MachineType const kMachineTypes[] = {MachineType::Pointer(),
MachineType::TaggedSigned(),
MachineType::TaggedSigned()}; MachineType::TaggedSigned()};
data->InitializePlatformIndependent(arraysize(kMachineTypes), 0, data->InitializePlatformIndependent(arraysize(kMachineTypes), 0,
kMachineTypes); kMachineTypes);
...@@ -395,7 +396,7 @@ void NewArgumentsElementsDescriptor::InitializePlatformIndependent( ...@@ -395,7 +396,7 @@ void NewArgumentsElementsDescriptor::InitializePlatformIndependent(
void NewArgumentsElementsDescriptor::InitializePlatformSpecific( void NewArgumentsElementsDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
DefaultInitializePlatformSpecific(data, 2); DefaultInitializePlatformSpecific(data, 3);
} }
void FastCloneRegExpDescriptor::InitializePlatformIndependent( void FastCloneRegExpDescriptor::InitializePlatformIndependent(
......
...@@ -835,7 +835,7 @@ class GrowArrayElementsDescriptor : public CallInterfaceDescriptor { ...@@ -835,7 +835,7 @@ class GrowArrayElementsDescriptor : public CallInterfaceDescriptor {
class NewArgumentsElementsDescriptor final : public CallInterfaceDescriptor { class NewArgumentsElementsDescriptor final : public CallInterfaceDescriptor {
public: public:
DEFINE_PARAMETERS(kFrame, kLength) DEFINE_PARAMETERS(kFrame, kLength, kMappedCount)
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(NewArgumentsElementsDescriptor, DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(NewArgumentsElementsDescriptor,
CallInterfaceDescriptor) CallInterfaceDescriptor)
}; };
......
...@@ -390,14 +390,14 @@ void PrintSloppyArgumentElements(std::ostream& os, ElementsKind kind, ...@@ -390,14 +390,14 @@ void PrintSloppyArgumentElements(std::ostream& os, ElementsKind kind,
SloppyArgumentsElements* elements) { SloppyArgumentsElements* elements) {
Isolate* isolate = elements->GetIsolate(); Isolate* isolate = elements->GetIsolate();
FixedArray* arguments_store = elements->arguments(); FixedArray* arguments_store = elements->arguments();
os << "\n 0: context= " << Brief(elements->context()) os << "\n 0: context = " << Brief(elements->context())
<< "\n 1: arguments_store= " << Brief(arguments_store) << "\n 1: arguments_store = " << Brief(arguments_store)
<< "\n parameter to context slot map:"; << "\n parameter to context slot map:";
for (uint32_t i = 0; i < elements->parameter_map_length(); i++) { for (uint32_t i = 0; i < elements->parameter_map_length(); i++) {
uint32_t raw_index = i + SloppyArgumentsElements::kParameterMapStart; uint32_t raw_index = i + SloppyArgumentsElements::kParameterMapStart;
Object* mapped_entry = elements->get_mapped_entry(i); Object* mapped_entry = elements->get_mapped_entry(i);
os << "\n " << raw_index << ": param(" << i os << "\n " << raw_index << ": param(" << i
<< ")= " << Brief(mapped_entry); << ") = " << Brief(mapped_entry);
if (mapped_entry->IsTheHole(isolate)) { if (mapped_entry->IsTheHole(isolate)) {
os << " in the arguments_store[" << i << "]"; os << " in the arguments_store[" << i << "]";
} else { } else {
...@@ -414,7 +414,6 @@ void PrintSloppyArgumentElements(std::ostream& os, ElementsKind kind, ...@@ -414,7 +414,6 @@ void PrintSloppyArgumentElements(std::ostream& os, ElementsKind kind,
DCHECK_EQ(kind, SLOW_SLOPPY_ARGUMENTS_ELEMENTS); DCHECK_EQ(kind, SLOW_SLOPPY_ARGUMENTS_ELEMENTS);
PrintDictionaryElements(os, arguments_store); PrintDictionaryElements(os, arguments_store);
} }
os << "\n }";
} }
} // namespace } // namespace
...@@ -422,7 +421,7 @@ void PrintSloppyArgumentElements(std::ostream& os, ElementsKind kind, ...@@ -422,7 +421,7 @@ void PrintSloppyArgumentElements(std::ostream& os, ElementsKind kind,
void JSObject::PrintElements(std::ostream& os) { // NOLINT void JSObject::PrintElements(std::ostream& os) { // NOLINT
// Don't call GetElementsKind, its validation code can cause the printer to // Don't call GetElementsKind, its validation code can cause the printer to
// fail when debugging. // fail when debugging.
os << " - elements= " << Brief(elements()) << " {"; os << " - elements = " << Brief(elements()) << " {";
if (elements()->length() == 0) { if (elements()->length() == 0) {
os << " }\n"; os << " }\n";
return; return;
......
...@@ -607,15 +607,20 @@ RUNTIME_FUNCTION(Runtime_NewSloppyArguments) { ...@@ -607,15 +607,20 @@ RUNTIME_FUNCTION(Runtime_NewSloppyArguments) {
RUNTIME_FUNCTION(Runtime_NewArgumentsElements) { RUNTIME_FUNCTION(Runtime_NewArgumentsElements) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(2, args.length()); DCHECK_EQ(3, args.length());
Object** frame = reinterpret_cast<Object**>(args[0]); Object** frame = reinterpret_cast<Object**>(args[0]);
CONVERT_SMI_ARG_CHECKED(length, 1); CONVERT_SMI_ARG_CHECKED(length, 1);
CONVERT_SMI_ARG_CHECKED(mapped_count, 2);
Handle<FixedArray> result = Handle<FixedArray> result =
isolate->factory()->NewUninitializedFixedArray(length); isolate->factory()->NewUninitializedFixedArray(length);
int const offset = length + 1; int const offset = length + 1;
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc); WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
for (int index = 0; index < length; ++index) { int number_of_holes = Min(mapped_count, length);
for (int index = 0; index < number_of_holes; ++index) {
result->set_the_hole(isolate, index);
}
for (int index = number_of_holes; index < length; ++index) {
result->set(index, frame[offset - index], mode); result->set(index, frame[offset - index], mode);
} }
return *result; return *result;
......
...@@ -497,7 +497,7 @@ namespace internal { ...@@ -497,7 +497,7 @@ namespace internal {
F(NewStrictArguments, 1, 1) \ F(NewStrictArguments, 1, 1) \
F(NewRestParameter, 1, 1) \ F(NewRestParameter, 1, 1) \
F(NewSloppyArguments, 3, 1) \ F(NewSloppyArguments, 3, 1) \
F(NewArgumentsElements, 2, 1) \ F(NewArgumentsElements, 3, 1) \
F(NewClosure, 3, 1) \ F(NewClosure, 3, 1) \
F(NewClosure_Tenured, 3, 1) \ F(NewClosure_Tenured, 3, 1) \
F(NewScriptContext, 2, 1) \ F(NewScriptContext, 2, 1) \
......
...@@ -31,12 +31,12 @@ Debug = debug.Debug ...@@ -31,12 +31,12 @@ Debug = debug.Debug
var error = null; var error = null;
var array = ["a", "b", "c"]; var array = ["a", "b", "c"];
var result = null;
function listener(event, exec_state, event_data, data) { function listener(event, exec_state, event_data, data) {
try { try {
if (event == Debug.DebugEvent.Break) { if (event == Debug.DebugEvent.Break) {
assertArrayEquals(array, result = exec_state.frame(0).evaluate('arguments').value();
exec_state.frame(0).evaluate('arguments').value());
} }
} catch (e) { } catch (e) {
error = e; error = e;
...@@ -51,13 +51,35 @@ function f(a, b) { ...@@ -51,13 +51,35 @@ function f(a, b) {
debugger; // Arguments object is already materialized. debugger; // Arguments object is already materialized.
} }
result = null;
f.apply(this, array); f.apply(this, array);
assertArrayEquals(array, result);
result = null;
f("a", "b", "c"); f("a", "b", "c");
assertArrayEquals(array, result);
assertNull(error); assertNull(error);
function g(a, b) { function g(a, b) {
debugger; // Arguments object is not yet materialized. debugger; // Arguments object is not yet materialized.
} }
result = null;
g.apply(this, array); g.apply(this, array);
assertArrayEquals(array, result);
result = null;
g("a", "b", "c"); g("a", "b", "c");
assertArrayEquals(array, result);
assertNull(error);
function h(a, b) {
var arguments = undefined;
debugger; // Arguments already used as local variable.
}
result = null;
h.apply(this, array);
assertEquals(undefined, result);
result = null;
h("a", "b", "c");
assertEquals(undefined, result);
assertNull(error); assertNull(error);
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