Home | History | Annotate | Download | only in builtins
      1 // Copyright 2017 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/builtins/builtins-arguments.h"
      6 #include "src/builtins/builtins-utils.h"
      7 #include "src/builtins/builtins.h"
      8 #include "src/code-factory.h"
      9 #include "src/code-stub-assembler.h"
     10 #include "src/interface-descriptors.h"
     11 #include "src/objects-inl.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 
     16 typedef compiler::Node Node;
     17 
     18 std::tuple<Node*, Node*, Node*>
     19 ArgumentsBuiltinsAssembler::GetArgumentsFrameAndCount(Node* function,
     20                                                       ParameterMode mode) {
     21   CSA_ASSERT(this, HasInstanceType(function, JS_FUNCTION_TYPE));
     22 
     23   Variable frame_ptr(this, MachineType::PointerRepresentation());
     24   frame_ptr.Bind(LoadParentFramePointer());
     25   CSA_ASSERT(this,
     26              WordEqual(function,
     27                        LoadBufferObject(frame_ptr.value(),
     28                                         StandardFrameConstants::kFunctionOffset,
     29                                         MachineType::Pointer())));
     30   Variable argument_count(this, ParameterRepresentation(mode));
     31   VariableList list({&frame_ptr, &argument_count}, zone());
     32   Label done_argument_count(this, list);
     33 
     34   // Determine the number of passed parameters, which is either the count stored
     35   // in an arguments adapter frame or fetched from the shared function info.
     36   Node* frame_ptr_above = LoadBufferObject(
     37       frame_ptr.value(), StandardFrameConstants::kCallerFPOffset,
     38       MachineType::Pointer());
     39   Node* shared =
     40       LoadObjectField(function, JSFunction::kSharedFunctionInfoOffset);
     41   Node* formal_parameter_count = LoadSharedFunctionInfoSpecialField(
     42       shared, SharedFunctionInfo::kFormalParameterCountOffset, mode);
     43   argument_count.Bind(formal_parameter_count);
     44   Node* marker_or_function = LoadBufferObject(
     45       frame_ptr_above, CommonFrameConstants::kContextOrFrameTypeOffset);
     46   GotoIf(
     47       MarkerIsNotFrameType(marker_or_function, StackFrame::ARGUMENTS_ADAPTOR),
     48       &done_argument_count);
     49   Node* adapted_parameter_count = LoadBufferObject(
     50       frame_ptr_above, ArgumentsAdaptorFrameConstants::kLengthOffset);
     51   frame_ptr.Bind(frame_ptr_above);
     52   argument_count.Bind(TaggedToParameter(adapted_parameter_count, mode));
     53   Goto(&done_argument_count);
     54 
     55   Bind(&done_argument_count);
     56   return std::tuple<Node*, Node*, Node*>(
     57       frame_ptr.value(), argument_count.value(), formal_parameter_count);
     58 }
     59 
     60 std::tuple<Node*, Node*, Node*>
     61 ArgumentsBuiltinsAssembler::AllocateArgumentsObject(Node* map,
     62                                                     Node* arguments_count,
     63                                                     Node* parameter_map_count,
     64                                                     ParameterMode mode,
     65                                                     int base_size) {
     66   // Allocate the parameter object (either a Rest parameter object, a strict
     67   // argument object or a sloppy arguments object) and the elements/mapped
     68   // arguments together.
     69   int elements_offset = base_size;
     70   Node* element_count = arguments_count;
     71   if (parameter_map_count != nullptr) {
     72     base_size += FixedArray::kHeaderSize;
     73     element_count = IntPtrOrSmiAdd(element_count, parameter_map_count, mode);
     74   }
     75   bool empty = IsIntPtrOrSmiConstantZero(arguments_count);
     76   DCHECK_IMPLIES(empty, parameter_map_count == nullptr);
     77   Node* size =
     78       empty ? IntPtrConstant(base_size)
     79             : ElementOffsetFromIndex(element_count, FAST_ELEMENTS, mode,
     80                                      base_size + FixedArray::kHeaderSize);
     81   Node* result = Allocate(size);
     82   Comment("Initialize arguments object");
     83   StoreMapNoWriteBarrier(result, map);
     84   Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
     85   StoreObjectField(result, JSArray::kPropertiesOffset, empty_fixed_array);
     86   Node* smi_arguments_count = ParameterToTagged(arguments_count, mode);
     87   StoreObjectFieldNoWriteBarrier(result, JSArray::kLengthOffset,
     88                                  smi_arguments_count);
     89   Node* arguments = nullptr;
     90   if (!empty) {
     91     arguments = InnerAllocate(result, elements_offset);
     92     StoreObjectFieldNoWriteBarrier(arguments, FixedArray::kLengthOffset,
     93                                    smi_arguments_count);
     94     Node* fixed_array_map = LoadRoot(Heap::kFixedArrayMapRootIndex);
     95     StoreMapNoWriteBarrier(arguments, fixed_array_map);
     96   }
     97   Node* parameter_map = nullptr;
     98   if (parameter_map_count != nullptr) {
     99     Node* parameter_map_offset = ElementOffsetFromIndex(
    100         arguments_count, FAST_ELEMENTS, mode, FixedArray::kHeaderSize);
    101     parameter_map = InnerAllocate(arguments, parameter_map_offset);
    102     StoreObjectFieldNoWriteBarrier(result, JSArray::kElementsOffset,
    103                                    parameter_map);
    104     Node* sloppy_elements_map =
    105         LoadRoot(Heap::kSloppyArgumentsElementsMapRootIndex);
    106     StoreMapNoWriteBarrier(parameter_map, sloppy_elements_map);
    107     parameter_map_count = ParameterToTagged(parameter_map_count, mode);
    108     StoreObjectFieldNoWriteBarrier(parameter_map, FixedArray::kLengthOffset,
    109                                    parameter_map_count);
    110   } else {
    111     if (empty) {
    112       StoreObjectFieldNoWriteBarrier(result, JSArray::kElementsOffset,
    113                                      empty_fixed_array);
    114     } else {
    115       StoreObjectFieldNoWriteBarrier(result, JSArray::kElementsOffset,
    116                                      arguments);
    117     }
    118   }
    119   return std::tuple<Node*, Node*, Node*>(result, arguments, parameter_map);
    120 }
    121 
    122 Node* ArgumentsBuiltinsAssembler::ConstructParametersObjectFromArgs(
    123     Node* map, Node* frame_ptr, Node* arg_count, Node* first_arg,
    124     Node* rest_count, ParameterMode param_mode, int base_size) {
    125   // Allocate the parameter object (either a Rest parameter object, a strict
    126   // argument object or a sloppy arguments object) and the elements together and
    127   // fill in the contents with the arguments above |formal_parameter_count|.
    128   Node* result;
    129   Node* elements;
    130   Node* unused;
    131   std::tie(result, elements, unused) =
    132       AllocateArgumentsObject(map, rest_count, nullptr, param_mode, base_size);
    133   DCHECK(unused == nullptr);
    134   CodeStubArguments arguments(this, arg_count, frame_ptr, param_mode);
    135   Variable offset(this, MachineType::PointerRepresentation());
    136   offset.Bind(IntPtrConstant(FixedArrayBase::kHeaderSize - kHeapObjectTag));
    137   VariableList list({&offset}, zone());
    138   arguments.ForEach(list,
    139                     [this, elements, &offset](Node* arg) {
    140                       StoreNoWriteBarrier(MachineRepresentation::kTagged,
    141                                           elements, offset.value(), arg);
    142                       Increment(offset, kPointerSize);
    143                     },
    144                     first_arg, nullptr, param_mode);
    145   return result;
    146 }
    147 
    148 Node* ArgumentsBuiltinsAssembler::EmitFastNewRestParameter(Node* context,
    149                                                            Node* function) {
    150   Node* frame_ptr;
    151   Node* argument_count;
    152   Node* formal_parameter_count;
    153 
    154   ParameterMode mode = OptimalParameterMode();
    155   Node* zero = IntPtrOrSmiConstant(0, mode);
    156 
    157   std::tie(frame_ptr, argument_count, formal_parameter_count) =
    158       GetArgumentsFrameAndCount(function, mode);
    159 
    160   Variable result(this, MachineRepresentation::kTagged);
    161   Label no_rest_parameters(this), runtime(this, Label::kDeferred),
    162       done(this, &result);
    163 
    164   Node* rest_count =
    165       IntPtrOrSmiSub(argument_count, formal_parameter_count, mode);
    166   Node* const native_context = LoadNativeContext(context);
    167   Node* const array_map = LoadJSArrayElementsMap(FAST_ELEMENTS, native_context);
    168   GotoIf(IntPtrOrSmiLessThanOrEqual(rest_count, zero, mode),
    169          &no_rest_parameters);
    170 
    171   GotoIfFixedArraySizeDoesntFitInNewSpace(
    172       rest_count, &runtime, JSArray::kSize + FixedArray::kHeaderSize, mode);
    173 
    174   // Allocate the Rest JSArray and the elements together and fill in the
    175   // contents with the arguments above |formal_parameter_count|.
    176   result.Bind(ConstructParametersObjectFromArgs(
    177       array_map, frame_ptr, argument_count, formal_parameter_count, rest_count,
    178       mode, JSArray::kSize));
    179   Goto(&done);
    180 
    181   Bind(&no_rest_parameters);
    182   {
    183     Node* arguments;
    184     Node* elements;
    185     Node* unused;
    186     std::tie(arguments, elements, unused) =
    187         AllocateArgumentsObject(array_map, zero, nullptr, mode, JSArray::kSize);
    188     result.Bind(arguments);
    189     Goto(&done);
    190   }
    191 
    192   Bind(&runtime);
    193   {
    194     result.Bind(CallRuntime(Runtime::kNewRestParameter, context, function));
    195     Goto(&done);
    196   }
    197 
    198   Bind(&done);
    199   return result.value();
    200 }
    201 
    202 TF_BUILTIN(FastNewRestParameter, ArgumentsBuiltinsAssembler) {
    203   Node* function = Parameter(FastNewArgumentsDescriptor::kFunction);
    204   Node* context = Parameter(FastNewArgumentsDescriptor::kContext);
    205   Return(EmitFastNewRestParameter(context, function));
    206 }
    207 
    208 Node* ArgumentsBuiltinsAssembler::EmitFastNewStrictArguments(Node* context,
    209                                                              Node* function) {
    210   Variable result(this, MachineRepresentation::kTagged);
    211   Label done(this, &result), empty(this), runtime(this, Label::kDeferred);
    212 
    213   Node* frame_ptr;
    214   Node* argument_count;
    215   Node* formal_parameter_count;
    216 
    217   ParameterMode mode = OptimalParameterMode();
    218   Node* zero = IntPtrOrSmiConstant(0, mode);
    219 
    220   std::tie(frame_ptr, argument_count, formal_parameter_count) =
    221       GetArgumentsFrameAndCount(function, mode);
    222 
    223   GotoIfFixedArraySizeDoesntFitInNewSpace(
    224       argument_count, &runtime,
    225       JSStrictArgumentsObject::kSize + FixedArray::kHeaderSize, mode);
    226 
    227   Node* const native_context = LoadNativeContext(context);
    228   Node* const map =
    229       LoadContextElement(native_context, Context::STRICT_ARGUMENTS_MAP_INDEX);
    230   GotoIf(WordEqual(argument_count, zero), &empty);
    231 
    232   result.Bind(ConstructParametersObjectFromArgs(
    233       map, frame_ptr, argument_count, zero, argument_count, mode,
    234       JSStrictArgumentsObject::kSize));
    235   Goto(&done);
    236 
    237   Bind(&empty);
    238   {
    239     Node* arguments;
    240     Node* elements;
    241     Node* unused;
    242     std::tie(arguments, elements, unused) = AllocateArgumentsObject(
    243         map, zero, nullptr, mode, JSStrictArgumentsObject::kSize);
    244     result.Bind(arguments);
    245     Goto(&done);
    246   }
    247 
    248   Bind(&runtime);
    249   {
    250     result.Bind(CallRuntime(Runtime::kNewStrictArguments, context, function));
    251     Goto(&done);
    252   }
    253 
    254   Bind(&done);
    255   return result.value();
    256 }
    257 
    258 TF_BUILTIN(FastNewStrictArguments, ArgumentsBuiltinsAssembler) {
    259   Node* function = Parameter(FastNewArgumentsDescriptor::kFunction);
    260   Node* context = Parameter(FastNewArgumentsDescriptor::kContext);
    261   Return(EmitFastNewStrictArguments(context, function));
    262 }
    263 
    264 Node* ArgumentsBuiltinsAssembler::EmitFastNewSloppyArguments(Node* context,
    265                                                              Node* function) {
    266   Node* frame_ptr;
    267   Node* argument_count;
    268   Node* formal_parameter_count;
    269   Variable result(this, MachineRepresentation::kTagged);
    270 
    271   ParameterMode mode = OptimalParameterMode();
    272   Node* zero = IntPtrOrSmiConstant(0, mode);
    273 
    274   Label done(this, &result), empty(this), no_parameters(this),
    275       runtime(this, Label::kDeferred);
    276 
    277   std::tie(frame_ptr, argument_count, formal_parameter_count) =
    278       GetArgumentsFrameAndCount(function, mode);
    279 
    280   GotoIf(WordEqual(argument_count, zero), &empty);
    281 
    282   GotoIf(WordEqual(formal_parameter_count, zero), &no_parameters);
    283 
    284   {
    285     Comment("Mapped parameter JSSloppyArgumentsObject");
    286 
    287     Node* mapped_count =
    288         IntPtrOrSmiMin(argument_count, formal_parameter_count, mode);
    289 
    290     Node* parameter_map_size =
    291         IntPtrOrSmiAdd(mapped_count, IntPtrOrSmiConstant(2, mode), mode);
    292 
    293     // Verify that the overall allocation will fit in new space.
    294     Node* elements_allocated =
    295         IntPtrOrSmiAdd(argument_count, parameter_map_size, mode);
    296     GotoIfFixedArraySizeDoesntFitInNewSpace(
    297         elements_allocated, &runtime,
    298         JSSloppyArgumentsObject::kSize + FixedArray::kHeaderSize * 2, mode);
    299 
    300     Node* const native_context = LoadNativeContext(context);
    301     Node* const map = LoadContextElement(
    302         native_context, Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX);
    303     Node* argument_object;
    304     Node* elements;
    305     Node* map_array;
    306     std::tie(argument_object, elements, map_array) =
    307         AllocateArgumentsObject(map, argument_count, parameter_map_size, mode,
    308                                 JSSloppyArgumentsObject::kSize);
    309     StoreObjectFieldNoWriteBarrier(
    310         argument_object, JSSloppyArgumentsObject::kCalleeOffset, function);
    311     StoreFixedArrayElement(map_array, 0, context, SKIP_WRITE_BARRIER);
    312     StoreFixedArrayElement(map_array, 1, elements, SKIP_WRITE_BARRIER);
    313 
    314     Comment("Fill in non-mapped parameters");
    315     Node* argument_offset =
    316         ElementOffsetFromIndex(argument_count, FAST_ELEMENTS, mode,
    317                                FixedArray::kHeaderSize - kHeapObjectTag);
    318     Node* mapped_offset =
    319         ElementOffsetFromIndex(mapped_count, FAST_ELEMENTS, mode,
    320                                FixedArray::kHeaderSize - kHeapObjectTag);
    321     CodeStubArguments arguments(this, argument_count, frame_ptr, mode);
    322     Variable current_argument(this, MachineType::PointerRepresentation());
    323     current_argument.Bind(arguments.AtIndexPtr(argument_count, mode));
    324     VariableList var_list1({&current_argument}, zone());
    325     mapped_offset = BuildFastLoop(
    326         var_list1, argument_offset, mapped_offset,
    327         [this, elements, &current_argument](Node* offset) {
    328           Increment(current_argument, kPointerSize);
    329           Node* arg = LoadBufferObject(current_argument.value(), 0);
    330           StoreNoWriteBarrier(MachineRepresentation::kTagged, elements, offset,
    331                               arg);
    332         },
    333         -kPointerSize, INTPTR_PARAMETERS);
    334 
    335     // Copy the parameter slots and the holes in the arguments.
    336     // We need to fill in mapped_count slots. They index the context,
    337     // where parameters are stored in reverse order, at
    338     //   MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS+argument_count-1
    339     // The mapped parameter thus need to get indices
    340     //   MIN_CONTEXT_SLOTS+parameter_count-1 ..
    341     //       MIN_CONTEXT_SLOTS+argument_count-mapped_count
    342     // We loop from right to left.
    343     Comment("Fill in mapped parameters");
    344     Variable context_index(this, OptimalParameterRepresentation());
    345     context_index.Bind(IntPtrOrSmiSub(
    346         IntPtrOrSmiAdd(IntPtrOrSmiConstant(Context::MIN_CONTEXT_SLOTS, mode),
    347                        formal_parameter_count, mode),
    348         mapped_count, mode));
    349     Node* the_hole = TheHoleConstant();
    350     VariableList var_list2({&context_index}, zone());
    351     const int kParameterMapHeaderSize =
    352         FixedArray::kHeaderSize + 2 * kPointerSize;
    353     Node* adjusted_map_array = IntPtrAdd(
    354         BitcastTaggedToWord(map_array),
    355         IntPtrConstant(kParameterMapHeaderSize - FixedArray::kHeaderSize));
    356     Node* zero_offset = ElementOffsetFromIndex(
    357         zero, FAST_ELEMENTS, mode, FixedArray::kHeaderSize - kHeapObjectTag);
    358     BuildFastLoop(var_list2, mapped_offset, zero_offset,
    359                   [this, the_hole, elements, adjusted_map_array, &context_index,
    360                    mode](Node* offset) {
    361                     StoreNoWriteBarrier(MachineRepresentation::kTagged,
    362                                         elements, offset, the_hole);
    363                     StoreNoWriteBarrier(
    364                         MachineRepresentation::kTagged, adjusted_map_array,
    365                         offset, ParameterToTagged(context_index.value(), mode));
    366                     Increment(context_index, 1, mode);
    367                   },
    368                   -kPointerSize, INTPTR_PARAMETERS);
    369 
    370     result.Bind(argument_object);
    371     Goto(&done);
    372   }
    373 
    374   Bind(&no_parameters);
    375   {
    376     Comment("No parameters JSSloppyArgumentsObject");
    377     GotoIfFixedArraySizeDoesntFitInNewSpace(
    378         argument_count, &runtime,
    379         JSSloppyArgumentsObject::kSize + FixedArray::kHeaderSize, mode);
    380     Node* const native_context = LoadNativeContext(context);
    381     Node* const map =
    382         LoadContextElement(native_context, Context::SLOPPY_ARGUMENTS_MAP_INDEX);
    383     result.Bind(ConstructParametersObjectFromArgs(
    384         map, frame_ptr, argument_count, zero, argument_count, mode,
    385         JSSloppyArgumentsObject::kSize));
    386     StoreObjectFieldNoWriteBarrier(
    387         result.value(), JSSloppyArgumentsObject::kCalleeOffset, function);
    388     Goto(&done);
    389   }
    390 
    391   Bind(&empty);
    392   {
    393     Comment("Empty JSSloppyArgumentsObject");
    394     Node* const native_context = LoadNativeContext(context);
    395     Node* const map =
    396         LoadContextElement(native_context, Context::SLOPPY_ARGUMENTS_MAP_INDEX);
    397     Node* arguments;
    398     Node* elements;
    399     Node* unused;
    400     std::tie(arguments, elements, unused) = AllocateArgumentsObject(
    401         map, zero, nullptr, mode, JSSloppyArgumentsObject::kSize);
    402     result.Bind(arguments);
    403     StoreObjectFieldNoWriteBarrier(
    404         result.value(), JSSloppyArgumentsObject::kCalleeOffset, function);
    405     Goto(&done);
    406   }
    407 
    408   Bind(&runtime);
    409   {
    410     result.Bind(CallRuntime(Runtime::kNewSloppyArguments, context, function));
    411     Goto(&done);
    412   }
    413 
    414   Bind(&done);
    415   return result.value();
    416 }
    417 
    418 TF_BUILTIN(FastNewSloppyArguments, ArgumentsBuiltinsAssembler) {
    419   Node* function = Parameter(FastNewArgumentsDescriptor::kFunction);
    420   Node* context = Parameter(FastNewArgumentsDescriptor::kContext);
    421   Return(EmitFastNewSloppyArguments(context, function));
    422 }
    423 
    424 }  // namespace internal
    425 }  // namespace v8
    426