[torque] Implement Rest/Strict/Sloppy argument stubs in Torque

Bug: v8:7793
Change-Id: Ib950ce398f101779a4654353d08ce947b8e05a66
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1526016
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Commit-Queue: Daniel Clifford <danno@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65518}
This commit is contained in:
Daniel Clifford 2019-12-19 09:55:02 +01:00 committed by Commit Bot
parent 1b450a1752
commit bc0c25b4a0
17 changed files with 334 additions and 524 deletions

View File

@ -1486,8 +1486,6 @@ v8_source_set("v8_initializers") {
sources = [
### gcmole(all) ###
"src/builtins/builtins-arguments-gen.cc",
"src/builtins/builtins-arguments-gen.h",
"src/builtins/builtins-array-gen.cc",
"src/builtins/builtins-array-gen.h",
"src/builtins/builtins-async-function-gen.cc",

View File

@ -140,6 +140,10 @@ type NoSharedNameSentinel extends Smi;
extern class ScopeInfo extends FixedArray;
operator '[]' macro LoadScopeInfoElement(s: ScopeInfo, i: intptr): Object {
return s.objects[i];
}
// Specialized types. The following three type definitions don't correspond to
// actual C++ classes, but have Is... methods that check additional constraints.
@ -331,6 +335,9 @@ extern enum PrimitiveType { kString, kBoolean, kSymbol, kNumber }
const kNameDictionaryInitialCapacity:
constexpr int32 generates 'NameDictionary::kInitialCapacity';
const kScopeInfoFlagsIndex:
constexpr int32 generates 'ScopeInfo::Fields::kFlags';
type TheHole extends Oddball;
type Null extends Oddball;
type Undefined extends Oddball;
@ -865,6 +872,7 @@ extern macro EmptyFixedArrayConstant(): EmptyFixedArray;
extern macro PromiseCapabilityMapConstant(): Map;
extern macro OneByteStringMapConstant(): Map;
extern macro StringMapConstant(): Map;
extern macro SloppyArgumentsElementsMapConstant(): Map;
const kFixedArrayMap: Map = FixedArrayMapConstant();
const kCOWMap: Map = FixedCOWArrayMapConstant();
@ -875,6 +883,7 @@ const kPromiseCapabilityMap: Map = PromiseCapabilityMapConstant();
const kOneByteStringMap: Map = OneByteStringMapConstant();
// The map of a non-internalized internal SeqTwoByteString.
const kStringMap: Map = StringMapConstant();
const kSloppyArgumentsElementsMap: Map = SloppyArgumentsElementsMapConstant();
extern macro IsPrototypeInitialArrayPrototype(implicit context: Context)(Map):
bool;
@ -985,6 +994,18 @@ macro GetRegExpLastMatchInfo(implicit context: Context)(): RegExpMatchInfo {
return %RawDownCast<RegExpMatchInfo>(LoadNativeContext(
context)[NativeContextSlot::REGEXP_LAST_MATCH_INFO_INDEX]);
}
macro GetStrictArgumentsMap(implicit context: Context)(): Map {
return UnsafeCast<Map>(LoadNativeContext(
context)[NativeContextSlot::STRICT_ARGUMENTS_MAP_INDEX]);
}
macro GetSloppyArgumentsMap(implicit context: Context)(): Map {
return UnsafeCast<Map>(LoadNativeContext(
context)[NativeContextSlot::SLOPPY_ARGUMENTS_MAP_INDEX]);
}
macro GetFastAliasedArgumentsMap(implicit context: Context)(): Map {
return UnsafeCast<Map>(LoadNativeContext(
context)[NativeContextSlot::FAST_ALIASED_ARGUMENTS_MAP_INDEX]);
}
extern transitioning macro Call(Context, Callable, JSAny): JSAny;
extern transitioning macro Call(Context, Callable, JSAny, JSAny): JSAny;
@ -1508,3 +1529,5 @@ macro ReplaceTheHoleWithUndefined(o: JSAny|TheHole): JSAny {
}
}
}
extern macro DecodeScopeInfoHasContextExtension(intptr): intptr;

View File

@ -1,348 +0,0 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/builtins/builtins-arguments-gen.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/codegen/code-factory.h"
#include "src/codegen/code-stub-assembler.h"
#include "src/codegen/interface-descriptors.h"
#include "src/execution/arguments.h"
#include "src/execution/frame-constants.h"
#include "src/objects/arguments.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
ArgumentsBuiltinsAssembler::ArgumentsAllocationResult
ArgumentsBuiltinsAssembler::AllocateArgumentsObject(
TNode<Map> map, TNode<BInt> arguments_count,
TNode<BInt> parameter_map_count, int base_size) {
// Allocate the parameter object (either a Rest parameter object, a strict
// argument object or a sloppy arguments object) and the elements/mapped
// arguments together.
int elements_offset = base_size;
TNode<BInt> element_count = arguments_count;
if (parameter_map_count != nullptr) {
base_size += FixedArray::kHeaderSize;
element_count = IntPtrOrSmiAdd(element_count, parameter_map_count);
}
bool empty = IsIntPtrOrSmiConstantZero(arguments_count);
DCHECK_IMPLIES(empty, parameter_map_count == nullptr);
TNode<IntPtrT> size =
empty ? IntPtrConstant(base_size)
: ElementOffsetFromIndex(element_count, PACKED_ELEMENTS,
base_size + FixedArray::kHeaderSize);
TNode<HeapObject> result = Allocate(size);
Comment("Initialize arguments object");
StoreMapNoWriteBarrier(result, map);
TNode<FixedArray> empty_fixed_array = EmptyFixedArrayConstant();
StoreObjectField(result, JSArray::kPropertiesOrHashOffset, empty_fixed_array);
TNode<Smi> smi_arguments_count = BIntToSmi(arguments_count);
StoreObjectFieldNoWriteBarrier(result, JSArray::kLengthOffset,
smi_arguments_count);
TNode<HeapObject> arguments;
if (!empty) {
arguments = InnerAllocate(result, elements_offset);
StoreObjectFieldNoWriteBarrier(arguments, FixedArray::kLengthOffset,
smi_arguments_count);
TNode<Map> fixed_array_map = FixedArrayMapConstant();
StoreMapNoWriteBarrier(arguments, fixed_array_map);
}
TNode<HeapObject> parameter_map;
if (!parameter_map_count.is_null()) {
TNode<IntPtrT> parameter_map_offset = ElementOffsetFromIndex(
arguments_count, PACKED_ELEMENTS, FixedArray::kHeaderSize);
parameter_map = InnerAllocate(arguments, parameter_map_offset);
StoreObjectFieldNoWriteBarrier(result, JSArray::kElementsOffset,
parameter_map);
TNode<Map> sloppy_elements_map = SloppyArgumentsElementsMapConstant();
StoreMapNoWriteBarrier(parameter_map, sloppy_elements_map);
StoreObjectFieldNoWriteBarrier(parameter_map, FixedArray::kLengthOffset,
BIntToSmi(parameter_map_count));
} else {
if (empty) {
StoreObjectFieldNoWriteBarrier(result, JSArray::kElementsOffset,
empty_fixed_array);
} else {
StoreObjectFieldNoWriteBarrier(result, JSArray::kElementsOffset,
arguments);
}
}
return {CAST(result), UncheckedCast<FixedArray>(arguments),
UncheckedCast<FixedArray>(parameter_map)};
}
TNode<JSObject> ArgumentsBuiltinsAssembler::ConstructParametersObjectFromArgs(
TNode<Map> map, TNode<RawPtrT> frame_ptr, TNode<BInt> arg_count,
TNode<BInt> first_arg, TNode<BInt> rest_count, int base_size) {
// Allocate the parameter object (either a Rest parameter object, a strict
// argument object or a sloppy arguments object) and the elements together and
// fill in the contents with the arguments above |formal_parameter_count|.
ArgumentsAllocationResult alloc_result =
AllocateArgumentsObject(map, rest_count, {}, base_size);
DCHECK(alloc_result.parameter_map.is_null());
CodeStubArguments arguments(this, arg_count, frame_ptr);
TVARIABLE(IntPtrT, offset,
IntPtrConstant(FixedArrayBase::kHeaderSize - kHeapObjectTag));
VariableList list({&offset}, zone());
arguments.ForEach(
list,
[&](TNode<Object> arg) {
StoreNoWriteBarrier(MachineRepresentation::kTagged,
alloc_result.elements, offset.value(), arg);
Increment(&offset, kTaggedSize);
},
first_arg);
return alloc_result.arguments_object;
}
TNode<JSObject> ArgumentsBuiltinsAssembler::EmitFastNewRestParameter(
TNode<Context> context, TNode<JSFunction> function) {
ParameterMode mode = OptimalParameterMode();
TNode<BInt> zero = BIntConstant(0);
TorqueStructArgumentsInfo info = GetArgumentsFrameAndCount(context, function);
TVARIABLE(JSObject, result);
Label no_rest_parameters(this), runtime(this, Label::kDeferred),
done(this, &result);
TNode<BInt> rest_count =
IntPtrOrSmiSub(info.argument_count, info.formal_parameter_count);
const TNode<NativeContext> native_context = LoadNativeContext(context);
const TNode<Map> array_map =
LoadJSArrayElementsMap(PACKED_ELEMENTS, native_context);
GotoIf(IntPtrOrSmiLessThanOrEqual(rest_count, zero), &no_rest_parameters);
GotoIfFixedArraySizeDoesntFitInNewSpace(
rest_count, &runtime, JSArray::kHeaderSize + FixedArray::kHeaderSize,
mode);
// Allocate the Rest JSArray and the elements together and fill in the
// contents with the arguments above |formal_parameter_count|.
result = ConstructParametersObjectFromArgs(
array_map, info.frame, info.argument_count, info.formal_parameter_count,
rest_count, JSArray::kHeaderSize);
Goto(&done);
BIND(&no_rest_parameters);
{
ArgumentsAllocationResult alloc_result =
AllocateArgumentsObject(array_map, zero, {}, JSArray::kHeaderSize);
result = alloc_result.arguments_object;
Goto(&done);
}
BIND(&runtime);
{
result = CAST(CallRuntime(Runtime::kNewRestParameter, context, function));
Goto(&done);
}
BIND(&done);
return result.value();
}
TNode<JSObject> ArgumentsBuiltinsAssembler::EmitFastNewStrictArguments(
TNode<Context> context, TNode<JSFunction> function) {
TVARIABLE(JSObject, result);
Label done(this, &result), empty(this), runtime(this, Label::kDeferred);
ParameterMode mode = OptimalParameterMode();
TNode<BInt> zero = BIntConstant(0);
TorqueStructArgumentsInfo info = GetArgumentsFrameAndCount(context, function);
GotoIfFixedArraySizeDoesntFitInNewSpace(
info.argument_count, &runtime,
JSStrictArgumentsObject::kSize + FixedArray::kHeaderSize, mode);
const TNode<NativeContext> native_context = LoadNativeContext(context);
TNode<Map> map = CAST(
LoadContextElement(native_context, Context::STRICT_ARGUMENTS_MAP_INDEX));
GotoIf(BIntEqual(info.argument_count, zero), &empty);
result = ConstructParametersObjectFromArgs(
map, info.frame, info.argument_count, zero, info.argument_count,
JSStrictArgumentsObject::kSize);
Goto(&done);
BIND(&empty);
{
ArgumentsAllocationResult alloc_result =
AllocateArgumentsObject(map, zero, {}, JSStrictArgumentsObject::kSize);
result = alloc_result.arguments_object;
Goto(&done);
}
BIND(&runtime);
{
result = CAST(CallRuntime(Runtime::kNewStrictArguments, context, function));
Goto(&done);
}
BIND(&done);
return result.value();
}
TNode<JSObject> ArgumentsBuiltinsAssembler::EmitFastNewSloppyArguments(
TNode<Context> context, TNode<JSFunction> function) {
TVARIABLE(JSObject, result);
ParameterMode mode = OptimalParameterMode();
TNode<BInt> zero = BIntConstant(0);
Label done(this, &result), empty(this), no_parameters(this),
runtime(this, Label::kDeferred);
TorqueStructArgumentsInfo info = GetArgumentsFrameAndCount(context, function);
GotoIf(BIntEqual(info.argument_count, zero), &empty);
GotoIf(BIntEqual(info.formal_parameter_count, zero), &no_parameters);
{
Comment("Mapped parameter JSSloppyArgumentsObject");
TNode<BInt> mapped_count =
IntPtrOrSmiMin(info.argument_count, info.formal_parameter_count);
TNode<BInt> parameter_map_size =
IntPtrOrSmiAdd(mapped_count, BIntConstant(2));
// Verify that the overall allocation will fit in new space.
TNode<BInt> elements_allocated =
IntPtrOrSmiAdd(info.argument_count, parameter_map_size);
GotoIfFixedArraySizeDoesntFitInNewSpace(
elements_allocated, &runtime,
JSSloppyArgumentsObject::kSize + FixedArray::kHeaderSize * 2, mode);
const TNode<NativeContext> native_context = LoadNativeContext(context);
const TNode<Map> map = CAST(LoadContextElement(
native_context, Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX));
ArgumentsAllocationResult alloc_result =
AllocateArgumentsObject(map, info.argument_count, parameter_map_size,
JSSloppyArgumentsObject::kSize);
StoreObjectFieldNoWriteBarrier(alloc_result.arguments_object,
JSSloppyArgumentsObject::kCalleeOffset,
function);
StoreFixedArrayElement(alloc_result.parameter_map, 0, context,
SKIP_WRITE_BARRIER);
StoreFixedArrayElement(alloc_result.parameter_map, 1, alloc_result.elements,
SKIP_WRITE_BARRIER);
Comment("Fill in non-mapped parameters");
TNode<IntPtrT> argument_offset =
ElementOffsetFromIndex(info.argument_count, PACKED_ELEMENTS,
FixedArray::kHeaderSize - kHeapObjectTag);
TNode<IntPtrT> mapped_offset =
ElementOffsetFromIndex(mapped_count, PACKED_ELEMENTS,
FixedArray::kHeaderSize - kHeapObjectTag);
CodeStubArguments arguments(this, info.argument_count, info.frame);
TVARIABLE(RawPtrT, current_argument,
arguments.AtIndexPtr(info.argument_count));
VariableList var_list1({&current_argument}, zone());
mapped_offset = BuildFastLoop<IntPtrT>(
var_list1, argument_offset, mapped_offset,
[&](TNode<IntPtrT> offset) {
Increment(&current_argument, kSystemPointerSize);
TNode<Object> arg = LoadBufferObject(
ReinterpretCast<RawPtrT>(current_argument.value()), 0);
StoreNoWriteBarrier(MachineRepresentation::kTagged,
alloc_result.elements, offset, arg);
return;
},
-kTaggedSize);
// Copy the parameter slots and the holes in the arguments.
// We need to fill in mapped_count slots. They index the context,
// where parameters are stored in reverse order, at
// context_header_size .. context_header_size+argument_count-1
// The mapped parameter thus need to get indices
// context_header_size+parameter_count-1 ..
// context_header_size+argument_count-mapped_count
// We loop from right to left.
Comment("Fill in mapped parameters");
STATIC_ASSERT(Context::MIN_CONTEXT_EXTENDED_SLOTS ==
Context::MIN_CONTEXT_SLOTS + 1);
TNode<IntPtrT> flags = LoadAndUntagObjectField(LoadScopeInfo(context),
ScopeInfo::kFlagsOffset);
TNode<BInt> context_header_size = IntPtrOrSmiAdd(
IntPtrToBInt(
Signed(DecodeWord<ScopeInfo::HasContextExtensionSlotField>(flags))),
BIntConstant(Context::MIN_CONTEXT_SLOTS));
TVARIABLE(BInt, context_index,
IntPtrOrSmiSub(IntPtrOrSmiAdd(context_header_size,
info.formal_parameter_count),
mapped_count));
TNode<Oddball> the_hole = TheHoleConstant();
VariableList var_list2({&context_index}, zone());
const int kParameterMapHeaderSize = FixedArray::OffsetOfElementAt(2);
TNode<IntPtrT> adjusted_map_array = IntPtrAdd(
BitcastTaggedToWord(alloc_result.parameter_map),
IntPtrConstant(kParameterMapHeaderSize - FixedArray::kHeaderSize));
TNode<IntPtrT> zero_offset = ElementOffsetFromIndex(
zero, PACKED_ELEMENTS, mode, FixedArray::kHeaderSize - kHeapObjectTag);
BuildFastLoop<IntPtrT>(
var_list2, mapped_offset, zero_offset,
[&](TNode<IntPtrT> offset) {
StoreNoWriteBarrier(MachineRepresentation::kTagged,
alloc_result.elements, offset, the_hole);
StoreNoWriteBarrier(MachineRepresentation::kTagged,
adjusted_map_array, offset,
BIntToSmi(context_index.value()));
Increment(&context_index);
},
-kTaggedSize);
result = alloc_result.arguments_object;
Goto(&done);
}
BIND(&no_parameters);
{
Comment("No parameters JSSloppyArgumentsObject");
GotoIfFixedArraySizeDoesntFitInNewSpace(
info.argument_count, &runtime,
JSSloppyArgumentsObject::kSize + FixedArray::kHeaderSize, mode);
const TNode<NativeContext> native_context = LoadNativeContext(context);
TNode<Map> map = CAST(LoadContextElement(
native_context, Context::SLOPPY_ARGUMENTS_MAP_INDEX));
result = ConstructParametersObjectFromArgs(
map, info.frame, info.argument_count, zero, info.argument_count,
JSSloppyArgumentsObject::kSize);
StoreObjectFieldNoWriteBarrier(
result.value(), JSSloppyArgumentsObject::kCalleeOffset, function);
Goto(&done);
}
BIND(&empty);
{
Comment("Empty JSSloppyArgumentsObject");
const TNode<NativeContext> native_context = LoadNativeContext(context);
const TNode<Map> map = CAST(LoadContextElement(
native_context, Context::SLOPPY_ARGUMENTS_MAP_INDEX));
ArgumentsAllocationResult alloc_result =
AllocateArgumentsObject(map, zero, {}, JSSloppyArgumentsObject::kSize);
result = alloc_result.arguments_object;
StoreObjectFieldNoWriteBarrier(
result.value(), JSSloppyArgumentsObject::kCalleeOffset, function);
Goto(&done);
}
BIND(&runtime);
{
result = CAST(CallRuntime(Runtime::kNewSloppyArguments, context, function));
Goto(&done);
}
BIND(&done);
return result.value();
}
} // namespace internal
} // namespace v8

View File

@ -1,57 +0,0 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_BUILTINS_BUILTINS_ARGUMENTS_GEN_H_
#define V8_BUILTINS_BUILTINS_ARGUMENTS_GEN_H_
#include "src/codegen/code-stub-assembler.h"
namespace v8 {
namespace internal {
// TODO(v8:9396): these declarations pollute the v8::internal scope.
using CodeAssemblerState = compiler::CodeAssemblerState;
using CodeAssemblerLabel = compiler::CodeAssemblerLabel;
class ArgumentsBuiltinsAssembler : public CodeStubAssembler {
public:
explicit ArgumentsBuiltinsAssembler(CodeAssemblerState* state)
: CodeStubAssembler(state) {}
TNode<JSObject> EmitFastNewStrictArguments(TNode<Context> context,
TNode<JSFunction> function);
TNode<JSObject> EmitFastNewSloppyArguments(TNode<Context> context,
TNode<JSFunction> function);
TNode<JSObject> EmitFastNewRestParameter(TNode<Context> context,
TNode<JSFunction> function);
private:
struct ArgumentsAllocationResult {
TNode<JSObject> arguments_object;
TNode<FixedArray> elements;
TNode<FixedArray> parameter_map;
};
// Allocates an an arguments (either rest, strict or sloppy) together with the
// FixedArray elements for the arguments and a parameter map (for sloppy
// arguments only, or empty TNode<> otherwise).
ArgumentsAllocationResult AllocateArgumentsObject(
TNode<Map> map, TNode<BInt> arguments, TNode<BInt> mapped_arguments,
int base_size);
// For Rest parameters and Strict arguments, the copying of parameters from
// the stack into the arguments object is straight-forward and shares much of
// the same underlying logic, which is encapsulated by this function. It
// allocates an arguments-like object of size |base_size| with the map |map|,
// and then copies |rest_count| arguments from the stack frame pointed to by
// |frame_ptr| starting from |first_arg|. |arg_count| == |first_arg| +
// |rest_count|.
TNode<JSObject> ConstructParametersObjectFromArgs(
TNode<Map> map, TNode<RawPtrT> frame_ptr, TNode<BInt> arg_count,
TNode<BInt> first_arg, TNode<BInt> rest_count, int base_size);
};
} // namespace internal
} // namespace v8
#endif // V8_BUILTINS_BUILTINS_ARGUMENTS_GEN_H_

View File

@ -11,6 +11,8 @@
#include "src/objects/js-proxy.h"
#include "src/objects/objects-inl.h"
#include "torque-generated/exported-macros-assembler-tq.h"
namespace v8 {
namespace internal {
@ -58,64 +60,6 @@ TNode<JSProxy> ProxiesCodeStubAssembler::AllocateProxy(
return CAST(proxy);
}
TNode<JSArray> ProxiesCodeStubAssembler::AllocateJSArrayForCodeStubArguments(
TNode<Context> context, const CodeStubArguments& args,
TNode<IntPtrT> argc) {
Comment("AllocateJSArrayForCodeStubArguments");
CSA_ASSERT(this, IsValidPositiveSmi(argc));
Label if_empty_array(this), allocate_js_array(this);
// Do not use AllocateJSArray since {elements} might end up in LOS.
TVARIABLE(FixedArrayBase, elements);
GotoIf(WordEqual(argc, IntPtrConstant(0)), &if_empty_array);
{
Label if_large_object(this, Label::kDeferred);
TNode<FixedArrayBase> allocated_elements = AllocateFixedArray(
PACKED_ELEMENTS, argc, INTPTR_PARAMETERS, kAllowLargeObjectAllocation);
elements = allocated_elements;
TVARIABLE(IntPtrT, offset,
IntPtrConstant(FixedArrayBase::kHeaderSize - kHeapObjectTag));
VariableList list({&offset}, zone());
GotoIf(
UintPtrGreaterThan(argc, IntPtrConstant(FixedArray::kMaxRegularLength)),
&if_large_object);
args.ForEach(list, [&](TNode<Object> arg) {
StoreNoWriteBarrier(MachineRepresentation::kTagged, allocated_elements,
offset.value(), arg);
Increment(&offset, kTaggedSize);
});
Goto(&allocate_js_array);
BIND(&if_large_object);
{
args.ForEach(list, [&](TNode<Object> arg) {
Store(allocated_elements, offset.value(), arg);
Increment(&offset, kTaggedSize);
});
Goto(&allocate_js_array);
}
}
BIND(&if_empty_array);
{
elements = EmptyFixedArrayConstant();
Goto(&allocate_js_array);
}
BIND(&allocate_js_array);
// Allocate the result JSArray.
TNode<NativeContext> native_context = LoadNativeContext(context);
TNode<Map> array_map =
LoadJSArrayElementsMap(PACKED_ELEMENTS, native_context);
TNode<Smi> length = SmiTag(argc);
TNode<JSArray> array = AllocateJSArray(array_map, elements.value(), length);
return array;
}
TNode<Context> ProxiesCodeStubAssembler::CreateProxyRevokeFunctionContext(
TNode<JSProxy> proxy, TNode<NativeContext> native_context) {
const TNode<Context> context =
@ -177,7 +121,9 @@ TF_BUILTIN(CallProxy, ProxiesCodeStubAssembler) {
// 7. Let argArray be CreateArrayFromList(argumentsList).
TNode<JSArray> array =
AllocateJSArrayForCodeStubArguments(context, args, argc_ptr);
EmitFastNewAllArguments(UncheckedCast<Context>(context),
UncheckedCast<RawPtrT>(LoadFramePointer()),
UncheckedCast<IntPtrT>(argc_ptr));
// 8. Return Call(trap, handler, «target, thisArgument, argArray»).
TNode<Object> result = CallJS(CodeFactory::Call(isolate()), context, trap,
@ -230,7 +176,9 @@ TF_BUILTIN(ConstructProxy, ProxiesCodeStubAssembler) {
// 7. Let argArray be CreateArrayFromList(argumentsList).
TNode<JSArray> array =
AllocateJSArrayForCodeStubArguments(context, args, argc_ptr);
EmitFastNewAllArguments(UncheckedCast<Context>(context),
UncheckedCast<RawPtrT>(LoadFramePointer()),
UncheckedCast<IntPtrT>(argc_ptr));
// 8. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »).
TNode<Object> new_obj = CallJS(CodeFactory::Call(isolate()), context, trap,

View File

@ -39,10 +39,6 @@ class ProxiesCodeStubAssembler : public CodeStubAssembler {
kProxyContextLength,
};
TNode<JSArray> AllocateJSArrayForCodeStubArguments(
TNode<Context> context, const CodeStubArguments& args,
TNode<IntPtrT> argc);
private:
TNode<Context> CreateProxyRevokeFunctionContext(
TNode<JSProxy> proxy, TNode<NativeContext> native_context);

View File

@ -228,10 +228,18 @@ extern macro IntPtrToBInt(intptr): bint;
Convert<bint, intptr>(v: intptr): bint {
return IntPtrToBInt(v);
}
extern macro BIntToIntPtr(bint): intptr;
Convert<intptr, bint>(v: bint): intptr {
return BIntToIntPtr(v);
}
extern macro SmiToBInt(Smi): bint;
Convert<bint, Smi>(v: Smi): bint {
return SmiToBInt(v);
}
extern macro BIntToSmi(bint): Smi;
Convert<Smi, bint>(v: bint): Smi {
return BIntToSmi(v);
}
Convert<PromiseState, int32>(s: int32): PromiseState {
return %RawDownCast<PromiseState>(s);
}

View File

@ -10,52 +10,55 @@ struct Arguments {
}
extern operator '[]' macro GetArgumentValue(Arguments, intptr): JSAny;
extern macro GetFrameArguments(FrameWithArguments, intptr): Arguments;
namespace arguments {
@export
struct ArgumentsInfo {
frame: Frame;
argument_count: bint;
formal_parameter_count: bint;
}
// Calculates and returns the frame pointer, argument count and formal
// parameter count to be used to access a function's parameters, taking
// argument adapter frames into account.
//
// TODO(danno):
// This macro is should only be used in builtins that can be called from
// interpreted or JITted code, not from CSA/Torque builtins (the number of
// returned formal parameters would be wrong).
// It is difficult to actually check/assert this, since interpreted or JITted
// frames are StandardFrames, but so are hand-written builtins. Doing that
// more refined check would be prohibitively expensive.
@export
macro GetArgumentsFrameAndCount(implicit context: Context)(f: JSFunction):
ArgumentsInfo {
const frame: Frame = LoadParentFramePointer();
assert(frame.function == f);
const shared: SharedFunctionInfo = f.shared_function_info;
const formalParameterCount: bint =
Convert<bint>(Convert<int32>(shared.formal_parameter_count));
const argumentCount: bint = formalParameterCount;
const adaptor: ArgumentsAdaptorFrame =
Cast<ArgumentsAdaptorFrame>(frame.caller)
otherwise return ArgumentsInfo{
frame,
argument_count: argumentCount,
formal_parameter_count: formalParameterCount
};
return ArgumentsInfo{
frame: adaptor,
argument_count: Convert<bint>(adaptor.length),
formal_parameter_count: formalParameterCount
};
struct ArgumentsIterator {
macro Next(): Object labels NoMore {
if (this.current == this.arguments.length) goto NoMore;
return this.arguments[this.current++];
}
const arguments: Arguments;
current: intptr;
}
struct FrameWithArgumentsInfo {
const frame: FrameWithArguments;
const argument_count: bint;
const formal_parameter_count: bint;
}
// Calculates and returns the frame pointer, argument count and formal
// parameter count to be used to access a function's parameters, taking
// argument adapter frames into account.
//
// TODO(danno):
// This macro is should only be used in builtins that can be called from
// interpreted or JITted code, not from CSA/Torque builtins (the number of
// returned formal parameters would be wrong).
// It is difficult to actually check/assert this, since interpreted or JITted
// frames are StandardFrames, but so are hand-written builtins. Doing that
// more refined check would be prohibitively expensive.
macro GetFrameWithArgumentsInfo(implicit context: Context)():
FrameWithArgumentsInfo {
const frame =
Cast<StandardFrame>(LoadParentFramePointer()) otherwise unreachable;
const f: JSFunction = frame.function;
const shared: SharedFunctionInfo = f.shared_function_info;
const formalParameterCount: bint =
Convert<bint>(Convert<int32>(shared.formal_parameter_count));
const argumentCount: bint = formalParameterCount;
const adaptor = Cast<ArgumentsAdaptorFrame>(frame.caller)
otherwise return FrameWithArgumentsInfo{
frame,
argument_count: argumentCount,
formal_parameter_count: formalParameterCount
};
return FrameWithArgumentsInfo{
frame: adaptor,
argument_count: Convert<bint>(adaptor.length),
formal_parameter_count: formalParameterCount
};
}

View File

@ -13435,5 +13435,10 @@ void PrototypeCheckAssembler::CheckAndBranch(TNode<HeapObject> prototype,
}
}
TNode<IntPtrT> CodeStubAssembler::DecodeScopeInfoHasContextExtension(
TNode<IntPtrT> flags) {
return Signed(DecodeWord<ScopeInfo::HasContextExtensionSlotField>(flags));
}
} // namespace internal
} // namespace v8

View File

@ -2742,6 +2742,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<UintPtrT> DecodeWord(SloppyTNode<WordT> word, uint32_t shift,
uint32_t mask);
TNode<IntPtrT> DecodeScopeInfoHasContextExtension(TNode<IntPtrT> flags);
// Returns a node that contains the updated values of a |BitField|.
template <typename BitField>
TNode<Word32T> UpdateWord32(TNode<Word32T> word, TNode<Uint32T> value) {

View File

@ -7,7 +7,6 @@
#include <array>
#include <tuple>
#include "src/builtins/builtins-arguments-gen.h"
#include "src/builtins/builtins-constructor-gen.h"
#include "src/builtins/builtins-iterator-gen.h"
#include "src/codegen/code-factory.h"
@ -26,6 +25,7 @@
#include "src/objects/shared-function-info.h"
#include "src/objects/source-text-module.h"
#include "src/utils/ostreams.h"
#include "torque-generated/exported-macros-assembler-tq.h"
namespace v8 {
namespace internal {
@ -33,6 +33,7 @@ namespace interpreter {
namespace {
using compiler::CodeAssemblerState;
using compiler::Node;
using Label = CodeStubAssembler::Label;
@ -2830,9 +2831,7 @@ IGNITION_HANDLER(CreateMappedArguments, InterpreterAssembler) {
BIND(&if_not_duplicate_parameters);
{
ArgumentsBuiltinsAssembler constructor_assembler(state());
TNode<JSObject> result =
constructor_assembler.EmitFastNewSloppyArguments(context, closure);
TNode<JSObject> result = EmitFastNewSloppyArguments(context, closure);
SetAccumulator(result);
Dispatch();
}
@ -2852,7 +2851,7 @@ IGNITION_HANDLER(CreateMappedArguments, InterpreterAssembler) {
IGNITION_HANDLER(CreateUnmappedArguments, InterpreterAssembler) {
TNode<Context> context = GetContext();
TNode<JSFunction> closure = CAST(LoadRegister(Register::function_closure()));
ArgumentsBuiltinsAssembler builtins_assembler(state());
TorqueGeneratedExportedMacrosAssembler builtins_assembler(state());
TNode<JSObject> result =
builtins_assembler.EmitFastNewStrictArguments(context, closure);
SetAccumulator(result);
@ -2865,9 +2864,9 @@ IGNITION_HANDLER(CreateUnmappedArguments, InterpreterAssembler) {
IGNITION_HANDLER(CreateRestParameter, InterpreterAssembler) {
TNode<JSFunction> closure = CAST(LoadRegister(Register::function_closure()));
TNode<Context> context = GetContext();
ArgumentsBuiltinsAssembler builtins_assembler(state());
TorqueGeneratedExportedMacrosAssembler builtins_assembler(state());
TNode<JSObject> result =
builtins_assembler.EmitFastNewRestParameter(context, closure);
builtins_assembler.EmitFastNewRestArguments(context, closure);
SetAccumulator(result);
Dispatch();
}

View File

@ -32,3 +32,200 @@ type SloppyArgumentsElements extends FixedArray;
extern class AliasedArgumentsEntry extends Struct {
aliased_context_slot: Smi;
}
// TODO(danno): This should be a namespace {} once supported
namespace arguments {
macro NewJSStrictArgumentsObject(implicit context: Context)(
elements: FixedArray): JSStrictArgumentsObject {
const map = GetStrictArgumentsMap();
return new JSStrictArgumentsObject{
map,
properties_or_hash: kEmptyFixedArray,
elements,
length: elements.length
};
}
macro NewJSSloppyArgumentsObject(implicit context: Context)(
elements: FixedArray, callee: JSFunction): JSSloppyArgumentsObject {
const map = GetSloppyArgumentsMap();
return new JSSloppyArgumentsObject{
map,
properties_or_hash: kEmptyFixedArray,
elements,
length: elements.length,
callee
};
}
macro NewJSFastAliasedArgumentsObject(implicit context: Context)(
elements: FixedArray, length: Smi,
callee: JSFunction): JSSloppyArgumentsObject {
// TODO(danno): FastAliasedArguments should really be a type for itself
const map = GetFastAliasedArgumentsMap();
return new JSSloppyArgumentsObject{
map,
properties_or_hash: kEmptyFixedArray,
elements,
length,
callee
};
}
struct ParameterMapIterator {
macro Next(): Object labels NoMore {
const currentMapSlotCopy = this.currentMapSlot++;
if (currentMapSlotCopy > 1) {
if (this.currentIndex == this.endInterationIndex) goto NoMore;
this.currentIndex--;
return Convert<Smi>(this.currentIndex);
} else if (currentMapSlotCopy == 0) {
return this.context;
} else {
assert(currentMapSlotCopy == 1);
return this.elements;
}
}
const context: Context;
const elements: FixedArray;
currentIndex: intptr;
const endInterationIndex: intptr;
currentMapSlot: intptr;
}
macro NewParameterMapIterator(
context: Context, elements: FixedArray, formalParameterCount: intptr,
mappedCount: intptr): ParameterMapIterator {
const scopeInfoFlags = Convert<intptr>(
UnsafeCast<Smi>(context.scope_info[kScopeInfoFlagsIndex]));
const contextExtentionSize =
DecodeScopeInfoHasContextExtension(scopeInfoFlags);
const contextHeaderSize = MIN_CONTEXT_SLOTS + contextExtentionSize;
// Copy the parameter slots and the holes in the arguments.
// We need to fill in mapped_count slots. They index the context,
// where parameters are stored in reverse order, at
// context_header_size .. context_header_size+argument_count-1
// The mapped parameter thus need to get indices
// context_header_size+parameter_count-1 ..
// context_header_size+argument_count-mapped_count
// We loop from right to left.
const afterLastContextIndex = contextHeaderSize + formalParameterCount;
const firstContextIndex = afterLastContextIndex - mappedCount;
return ParameterMapIterator{
context,
elements,
currentIndex: afterLastContextIndex,
endInterationIndex: firstContextIndex,
currentMapSlot: 0
};
}
struct ParameterValueIterator {
macro Next(): Object labels NoMore() {
if (this.mapped_count != 0) {
this.mapped_count--;
return TheHole;
}
if (this.current == this.arguments.length) goto NoMore;
return this.arguments[this.current++];
}
mapped_count: intptr;
const arguments: Arguments;
current: intptr;
}
macro NewParameterValueIterator(mappedCount: intptr, arguments: Arguments):
ParameterValueIterator {
return ParameterValueIterator{
mapped_count: mappedCount,
arguments,
current: mappedCount
};
}
macro NewAllArguments(implicit context: Context)(
frame: FrameWithArguments, argumentCount: intptr): JSArray {
const map = GetFastPackedElementsJSArrayMap();
const arguments = GetFrameArguments(frame, argumentCount);
const it = ArgumentsIterator{arguments, current: 0};
const elements = NewFixedArray(argumentCount, it);
return NewJSArray(map, elements);
}
macro NewRestArguments(implicit context:
Context)(info: FrameWithArgumentsInfo): JSArray {
const argumentCount = Convert<intptr>(info.argument_count);
const formalParameterCount = Convert<intptr>(info.formal_parameter_count);
const map = GetFastPackedElementsJSArrayMap();
const length = (formalParameterCount >= argumentCount) ?
0 :
argumentCount - formalParameterCount;
const arguments = GetFrameArguments(info.frame, argumentCount);
const it = ArgumentsIterator{arguments, current: formalParameterCount};
const elements = NewFixedArray(length, it);
return NewJSArray(map, elements);
}
macro NewStrictArguments(implicit context: Context)(
info: FrameWithArgumentsInfo): JSStrictArgumentsObject {
const argumentCount = Convert<intptr>(info.argument_count);
const arguments = GetFrameArguments(info.frame, argumentCount);
const it = ArgumentsIterator{arguments, current: 0};
const elements = NewFixedArray(argumentCount, it);
return NewJSStrictArgumentsObject(elements);
}
macro NewSloppyArguments(implicit context: Context)(
info: FrameWithArgumentsInfo,
callee: JSFunction): JSSloppyArgumentsObject {
const argumentCount = Convert<intptr>(info.argument_count);
const arguments = GetFrameArguments(info.frame, argumentCount);
const formalParameterCount = Convert<intptr>(info.formal_parameter_count);
if (formalParameterCount == 0) {
const it = ArgumentsIterator{arguments, current: 0};
const elements = NewFixedArray(argumentCount, it);
return NewJSSloppyArgumentsObject(elements, callee);
}
const mappedCount = IntPtrMin(formalParameterCount, argumentCount);
const it = NewParameterValueIterator(mappedCount, arguments);
const parameterValues = NewFixedArray(argumentCount, it);
let paramIter = NewParameterMapIterator(
context, parameterValues, formalParameterCount, mappedCount);
const elementsLength =
Convert<Smi>(mappedCount + kSloppyArgumentsParameterMapStart);
const map = kSloppyArgumentsElementsMap;
const elements = new
FixedArray{map, length: elementsLength, objects: ...paramIter};
const length = Convert<Smi>(argumentCount);
return NewJSFastAliasedArgumentsObject(elements, length, callee);
}
}
@export
macro EmitFastNewAllArguments(implicit context: Context)(
frame: FrameWithArguments, argc: intptr): JSArray {
return arguments::NewAllArguments(frame, argc);
}
@export
macro EmitFastNewRestArguments(implicit context: Context)(_f: JSFunction):
JSArray {
const info = GetFrameWithArgumentsInfo();
return arguments::NewRestArguments(info);
}
@export
macro EmitFastNewStrictArguments(implicit context: Context)(_f: JSFunction):
JSStrictArgumentsObject {
const info = GetFrameWithArgumentsInfo();
return arguments::NewStrictArguments(info);
}
@export
macro EmitFastNewSloppyArguments(implicit context: Context)(f: JSFunction):
JSSloppyArgumentsObject {
const info = GetFrameWithArgumentsInfo();
return arguments::NewSloppyArguments(info, f);
}

View File

@ -20,6 +20,9 @@ extern class NativeContext extends Context;
extern class ScriptContext extends Context generates 'TNode<Context>';
extern class WithContext extends Context generates 'TNode<Context>';
const MIN_CONTEXT_SLOTS: constexpr int31
generates 'Context::MIN_CONTEXT_SLOTS';
extern enum NativeContextSlot extends intptr constexpr 'Context::Field' {
ARRAY_BUFFER_FUN_INDEX,
ARRAY_BUFFER_NOINIT_FUN_INDEX,
@ -37,6 +40,9 @@ extern enum NativeContextSlot extends intptr constexpr 'Context::Field' {
REGEXP_LAST_MATCH_INFO_INDEX,
INITIAL_STRING_ITERATOR_MAP_INDEX,
SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP,
STRICT_ARGUMENTS_MAP_INDEX,
SLOPPY_ARGUMENTS_MAP_INDEX,
FAST_ALIASED_ARGUMENTS_MAP_INDEX,
PROMISE_FUNCTION_INDEX,
PROMISE_THEN_INDEX,

View File

@ -115,3 +115,9 @@ extern macro ExtractFixedArray(
extern macro ExtractFixedArray(
FixedArray, intptr, intptr, intptr,
constexpr ExtractFixedArrayFlag): FixedArray;
macro NewFixedArray<I: type>(length: intptr, it: I): FixedArray {
if (length == 0) return kEmptyFixedArray;
return new
FixedArray{map: kFixedArrayMap, length: Convert<Smi>(length), objects: ...it};
}

View File

@ -39,7 +39,7 @@ void CheckAlreadyDeclared(const std::string& name, const char* new_type) {
FilterDeclarables<T>(Declarations::TryLookupShallow(QualifiedName(name)));
if (!declarations.empty()) {
Scope* scope = CurrentScope::Get();
ReportError("cannot redeclare ", name, " (type ", new_type, scope, ")");
ReportError("cannot redeclare ", name, " (type ", *new_type, scope, ")");
}
}

View File

@ -720,6 +720,22 @@ TEST(TestBitFieldUintptrOps) {
ft.Call(ft.Val(val2), ft.Val(val3));
}
TEST(TestTestParentFrameArguments) {
CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate());
i::HandleScope scope(isolate);
Handle<Context> context =
Utils::OpenHandle(*v8::Isolate::GetCurrent()->GetCurrentContext());
CodeAssemblerTester asm_tester(isolate, 1);
TestTorqueAssembler m(asm_tester.state());
{
m.TestParentFrameArguments(
m.UncheckedCast<Context>(m.HeapConstant(context)));
m.Return(m.UndefinedConstant());
}
asm_tester.GenerateCode();
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -811,6 +811,14 @@ namespace test {
check(x.TestMethod2() == Null);
}
@export
macro TestParentFrameArguments(implicit context: Context)() {
const parentFrame = LoadParentFramePointer();
const castFrame = Cast<StandardFrame>(parentFrame) otherwise unreachable;
const arguments = GetFrameArguments(castFrame, 1);
ArgumentsIterator{arguments, current: 0};
}
struct TestIterator {
macro Next(): Object labels NoMore {
if (this.count-- == 0) goto NoMore;