From 830011c0330c9eb85a19c78b9eba3368cf465887 Mon Sep 17 00:00:00 2001 From: Robert Fancsik Date: Tue, 19 Nov 2019 18:16:43 +0100 Subject: [PATCH] Eliminate the allocation of spread object (#3333) ECMA_VALUE_SPREAD_ELEMENT can be used to represent that the next argument needs to be spreaded, therefore no allocation is needed. JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu --- jerry-core/ecma/base/ecma-gc.c | 20 ----- jerry-core/ecma/base/ecma-globals.h | 5 +- .../ecma/operations/ecma-spread-object.c | 88 ------------------- .../ecma/operations/ecma-spread-object.h | 42 --------- jerry-core/parser/js/byte-code.h | 4 +- jerry-core/parser/js/js-parser-expr.c | 29 +++--- jerry-core/vm/opcodes.c | 45 +++++----- jerry-core/vm/opcodes.h | 2 +- jerry-core/vm/vm.c | 17 ++-- jerry-core/vm/vm.h | 4 +- 10 files changed, 49 insertions(+), 207 deletions(-) delete mode 100644 jerry-core/ecma/operations/ecma-spread-object.c delete mode 100644 jerry-core/ecma/operations/ecma-spread-object.h diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 05a1f363c..64d52cffc 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -465,17 +465,6 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ break; } #endif /* ENABLED (JERRY_ES2015_BUILTIN_ITERATOR) */ -#if ENABLED (JERRY_ES2015) - case ECMA_PSEUDO_SPREAD_OBJECT: - { - ecma_value_t spread_value = ext_object_p->u.pseudo_array.u2.spread_value; - if (ecma_is_value_object (spread_value)) - { - ecma_gc_set_object_visited (ecma_get_object_from_value (spread_value)); - } - break; - } -#endif /* ENABLED (JERRY_ES2015) */ default: { JERRY_ASSERT (ext_object_p->u.pseudo_array.type == ECMA_PSEUDO_ARRAY_ARGUMENTS); @@ -967,15 +956,6 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ break; } #endif /* ENABLED (JERRY_ES2015_BUILTIN_ITERATOR) */ -#if ENABLED (JERRY_ES2015) - case ECMA_PSEUDO_SPREAD_OBJECT: - { - ecma_value_t spread_value = ext_object_p->u.pseudo_array.u2.spread_value; - ecma_free_value_if_not_object (spread_value); - - break; - } -#endif /* ENABLED (JERRY_ES2015) */ default: { JERRY_ASSERT (ext_object_p->u.pseudo_array.type == ECMA_PSEUDO_ARRAY_TYPEDARRAY diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 98bd00ffd..6dda10099 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -190,6 +190,8 @@ enum * a special "base" value for vm */ ECMA_VALUE_IMPLICIT_CONSTRUCTOR = ECMA_MAKE_VALUE (9), /**< special value for bound class constructors */ ECMA_VALUE_UNINITIALIZED = ECMA_MAKE_VALUE (10), /**< a special value for uninitialized let/const declarations */ + ECMA_VALUE_SPREAD_ELEMENT = ECMA_MAKE_VALUE (11), /**< a special value for spread elements in array initialization + * or function call argument list */ }; #if !ENABLED (JERRY_NUMBER_TYPE_FLOAT64) @@ -630,8 +632,7 @@ typedef enum ECMA_PSEUDO_SET_ITERATOR = 4, /**< Set iterator object (ECMAScript v6, 23.2.5.1) */ ECMA_PSEUDO_MAP_ITERATOR = 5, /**< Map iterator object (ECMAScript v6, 23.1.5.1) */ ECMA_PSEUDO_STRING_ITERATOR = 6, /**< String iterator object (ECMAScript v6, 22.1.5.1) */ - ECMA_PSEUDO_SPREAD_OBJECT = 7, /**< spread object */ - ECMA_PSEUDO_ARRAY__MAX = ECMA_PSEUDO_SPREAD_OBJECT /**< maximum value */ + ECMA_PSEUDO_ARRAY__MAX = ECMA_PSEUDO_STRING_ITERATOR /**< maximum value */ } ecma_pseudo_array_type_t; /** diff --git a/jerry-core/ecma/operations/ecma-spread-object.c b/jerry-core/ecma/operations/ecma-spread-object.c deleted file mode 100644 index 6340d0eb5..000000000 --- a/jerry-core/ecma/operations/ecma-spread-object.c +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ecma-spread-object.h" -#include "ecma-globals.h" -#include "ecma-helpers.h" -#include "ecma-objects.h" - -/** \addtogroup ecma ECMA - * @{ - * - * \addtogroup ecmaspreadeanobject ECMA Spread object related routines - * @{ - */ - -/** - * Spread object creation operation. - * - * @return pseudo array object as an ecma value - * Returned value must be freed with ecma_free_value - */ -ecma_value_t -ecma_op_create_spread_object (ecma_value_t element) /**< value to spread */ -{ - ecma_object_t *object_p = ecma_create_object (NULL, - sizeof (ecma_extended_object_t), - ECMA_OBJECT_TYPE_PSEUDO_ARRAY); - - ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; - ext_object_p->u.pseudo_array.type = ECMA_PSEUDO_SPREAD_OBJECT; - ext_object_p->u.pseudo_array.u2.spread_value = ecma_copy_value_if_not_object (element); - - return ecma_make_object_value (object_p); -} /* ecma_op_create_spread_object */ - -/** - * Spread object creation operation. - * - * @return true - if the argument is a spread object - * false, otherwise - */ -bool -ecma_op_is_spread_object (ecma_value_t value) /**< value to check */ -{ - if (ecma_is_value_object (value)) - { - ecma_object_t *object_p = ecma_get_object_from_value (value); - - if (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_PSEUDO_ARRAY) - { - ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; - return ext_object_p->u.pseudo_array.type == ECMA_PSEUDO_SPREAD_OBJECT; - } - } - - return false; -} /* ecma_op_is_spread_object */ - -/** - * Get the referenced element by the spread object - * - * @return element referenced by the spread object as an ecma-value - */ -ecma_value_t -ecma_op_spread_object_get_spreaded_element (ecma_object_t *object_p) /**< spread object */ -{ - JERRY_ASSERT (ecma_op_is_spread_object (ecma_make_object_value (object_p))); - - ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; - return ecma_copy_value (ext_object_p->u.pseudo_array.u2.spread_value); -} /* ecma_op_spread_object_get_spreaded_element */ - -/** - * @} - * @} - */ diff --git a/jerry-core/ecma/operations/ecma-spread-object.h b/jerry-core/ecma/operations/ecma-spread-object.h deleted file mode 100644 index 44a5ee1a5..000000000 --- a/jerry-core/ecma/operations/ecma-spread-object.h +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ECMA_SPREAD_OBJECT_H -#define ECMA_SPREAD_OBJECT_H - -#include "ecma-globals.h" - -/** \addtogroup ecma ECMA - * @{ - * - * \addtogroup ecmaspreadobject ECMA Spread object related routines - * @{ - */ - -ecma_value_t -ecma_op_create_spread_object (ecma_value_t arg); - -bool -ecma_op_is_spread_object (ecma_value_t value); - -ecma_value_t -ecma_op_spread_object_get_spreaded_element (ecma_object_t *object_p); - -/** - * @} - * @} - */ - -#endif /* !ECMA_SPREAD_OBJECT_H */ diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 65aba2395..f03698540 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -634,8 +634,8 @@ VM_OC_CONSTRUCTOR_RET | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_ERROR, CBC_NO_FLAG, 0, \ VM_OC_ERROR) \ - CBC_OPCODE (CBC_EXT_CREATE_SPREAD_OBJECT, CBC_NO_FLAG, 0, \ - VM_OC_CREATE_SPREAD_OBJECT | VM_OC_GET_STACK | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_PUSH_SPREAD_ELEMENT, CBC_NO_FLAG, 1, \ + VM_OC_PUSH_SPREAD_ELEMENT) \ CBC_OPCODE (CBC_EXT_SPREAD_ARRAY_APPEND, CBC_HAS_POP_STACK_BYTE_ARG, 0, \ VM_OC_APPEND_ARRAY) \ CBC_OPCODE (CBC_EXT_GET_ITERATOR, CBC_NO_FLAG, 1, \ diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 9233eda88..97760911e 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -249,24 +249,17 @@ parser_parse_array_literal (parser_context_t *context_p) /**< context */ else { #if ENABLED (JERRY_ES2015) - bool is_spread = false; if (context_p->token.type == LEXER_THREE_DOTS) { opcode = (uint16_t) (PARSER_TO_EXT_OPCODE (CBC_EXT_SPREAD_ARRAY_APPEND)); - is_spread = true; + pushed_items++; lexer_next_token (context_p); + parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SPREAD_ELEMENT); } #endif /* ENABLED (JERRY_ES2015) */ parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); -#if ENABLED (JERRY_ES2015) - if (is_spread) - { - parser_emit_cbc_ext (context_p, CBC_EXT_CREATE_SPREAD_OBJECT); - } -#endif /* ENABLED (JERRY_ES2015) */ - if (context_p->token.type == LEXER_COMMA) { lexer_next_token (context_p); @@ -1639,7 +1632,7 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */ lexer_next_token (context_p); #if ENABLED (JERRY_ES2015) - bool spread_arguments = false; + uint32_t spread_arguments = 0; #endif /* ENABLED (JERRY_ES2015) */ if (context_p->token.type != LEXER_RIGHT_PAREN) @@ -1652,20 +1645,20 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */ } #if ENABLED (JERRY_ES2015) - bool is_spread = false; - if (context_p->token.type == LEXER_THREE_DOTS) + lexer_token_type_t type = context_p->token.type; + if (type == LEXER_THREE_DOTS) { - spread_arguments = true; - is_spread = true; + spread_arguments++; lexer_next_token (context_p); } #endif /* ENABLED (JERRY_ES2015) */ + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); #if ENABLED (JERRY_ES2015) - if (is_spread) + if (type == LEXER_THREE_DOTS) { - parser_emit_cbc_ext (context_p, CBC_EXT_CREATE_SPREAD_OBJECT); + parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SPREAD_ELEMENT); } #endif /* ENABLED (JERRY_ES2015) */ @@ -1709,7 +1702,7 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */ context_p->status_flags &= (uint32_t) ~PARSER_CLASS_SUPER_PROP_REFERENCE; } - if (spread_arguments) + if (spread_arguments != 0) { uint16_t spread_opcode; @@ -1732,6 +1725,8 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */ spread_opcode = CBC_EXT_SPREAD_SUPER_CALL; } + /* Manually adjust stack usage */ + PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, spread_arguments); parser_emit_cbc_ext_call (context_p, spread_opcode, call_arguments); continue; } diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index 686170669..e3273e110 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -26,7 +26,6 @@ #include "ecma-iterator-object.h" #include "ecma-lex-env.h" #include "ecma-objects.h" -#include "ecma-spread-object.h" #include "ecma-try-catch-macro.h" #include "jcontext.h" #include "opcodes.h" @@ -299,17 +298,18 @@ opfunc_append_to_spread_array (ecma_value_t *stack_top_p, /**< current stack top ecma_extended_object_t *ext_array_obj_p = (ecma_extended_object_t *) array_obj_p; uint32_t old_length = ext_array_obj_p->u.array.length; - for (uint32_t i = 0, j = old_length; i < values_length; i++) + for (uint32_t i = 0, idx = old_length; i < values_length; i++, idx++) { if (ecma_is_value_array_hole (stack_top_p[i])) { continue; } - if (ecma_op_is_spread_object (stack_top_p[i])) + + if (stack_top_p[i] == ECMA_VALUE_SPREAD_ELEMENT) { + i++; ecma_value_t ret_value = ECMA_VALUE_ERROR; - ecma_object_t *spread_object_p = ecma_get_object_from_value (stack_top_p[i]); - ecma_value_t spread_value = ecma_op_spread_object_get_spreaded_element (spread_object_p); + ecma_value_t spread_value = stack_top_p[i]; ecma_value_t iterator = ecma_op_get_iterator (spread_value, ECMA_VALUE_EMPTY); @@ -326,7 +326,7 @@ opfunc_append_to_spread_array (ecma_value_t *stack_top_p, /**< current stack top if (ecma_is_value_false (next)) { - j--; + idx--; ret_value = ECMA_VALUE_EMPTY; break; } @@ -342,11 +342,10 @@ opfunc_append_to_spread_array (ecma_value_t *stack_top_p, /**< current stack top ecma_value_t put_comp; put_comp = ecma_builtin_helper_def_prop_by_index (array_obj_p, - i + j, + idx++, value, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); - j++; JERRY_ASSERT (ecma_is_value_true (put_comp)); ecma_free_value (value); } @@ -357,22 +356,18 @@ opfunc_append_to_spread_array (ecma_value_t *stack_top_p, /**< current stack top if (ECMA_IS_VALUE_ERROR (ret_value)) { - for (uint32_t k = i; k < values_length; k++) + for (uint32_t k = i + 1; k < values_length; k++) { ecma_free_value (stack_top_p[k]); } return ret_value; } - else - { - ecma_deref_object (spread_object_p); - } } else { ecma_value_t put_comp = ecma_builtin_helper_def_prop_by_index (array_obj_p, - i + j, + idx, stack_top_p[i], ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); JERRY_ASSERT (ecma_is_value_true (put_comp)); @@ -390,24 +385,24 @@ opfunc_append_to_spread_array (ecma_value_t *stack_top_p, /**< current stack top * pointer to the ecma-collection with the spreaded arguments, otherwise */ ecma_collection_t * JERRY_ATTR_NOINLINE -opfunc_spread_arguments (ecma_value_t *arguments_list_p, /**< arguments list */ +opfunc_spread_arguments (ecma_value_t **stack_top_p, /**< [out] pointer to the current stack top */ uint8_t arguments_list_len) /**< number of arguments */ { + ecma_value_t *curr_stack_top_p = *stack_top_p; ecma_collection_t *buff_p = ecma_new_collection (); - for (uint8_t i = 0; i < arguments_list_len; i++) + for (uint32_t i = 0; i < arguments_list_len; i++) { - ecma_value_t arg = arguments_list_p[i]; + ecma_value_t arg = *(--curr_stack_top_p); - if (!ecma_op_is_spread_object (arg)) + if (arg != ECMA_VALUE_SPREAD_ELEMENT) { ecma_collection_push_back (buff_p, arg); continue; } ecma_value_t ret_value = ECMA_VALUE_ERROR; - ecma_object_t *spread_object_p = ecma_get_object_from_value (arguments_list_p[i]); - ecma_value_t spread_value = ecma_op_spread_object_get_spreaded_element (spread_object_p); + ecma_value_t spread_value = *(--curr_stack_top_p); ecma_value_t iterator = ecma_op_get_iterator (spread_value, ECMA_VALUE_EMPTY); @@ -446,18 +441,18 @@ opfunc_spread_arguments (ecma_value_t *arguments_list_p, /**< arguments list */ if (ECMA_IS_VALUE_ERROR (ret_value)) { - for (uint32_t k = i; k < arguments_list_len; k++) + for (uint32_t k = i + 1; k < arguments_list_len; k++) { - ecma_free_value (arguments_list_p[k]); + ecma_free_value (*(--curr_stack_top_p)); } ecma_collection_free (buff_p); - return NULL; + buff_p = NULL; + break; } - - ecma_deref_object (spread_object_p); } + *stack_top_p = curr_stack_top_p; return buff_p; } /* opfunc_spread_arguments */ #endif /* ENABLED (JERRY_ES2015) */ diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 5b05e1c8c..a4f46dc06 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -106,7 +106,7 @@ opfunc_append_array (ecma_value_t *stack_top_p, uint16_t values_length); #if ENABLED (JERRY_ES2015) ecma_collection_t * -opfunc_spread_arguments (ecma_value_t *arguments_list_p, uint8_t argument_list_len); +opfunc_spread_arguments (ecma_value_t **stack_top_p, uint8_t argument_list_len); #endif /* ENABLED (JERRY_ES2015) */ /** diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 73c3e1fbf..14d5411cf 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -31,7 +31,6 @@ #include "ecma-objects-general.h" #include "ecma-regexp-object.h" #include "ecma-try-catch-macro.h" -#include "ecma-spread-object.h" #include "jcontext.h" #include "opcodes.h" #include "vm.h" @@ -1581,11 +1580,10 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ case VM_OC_SUPER_CALL: { uint8_t arguments_list_len = *byte_code_p++; - stack_top_p -= arguments_list_len; if (opcode >= CBC_EXT_SPREAD_SUPER_CALL) { - ecma_collection_t *arguments_p = opfunc_spread_arguments (stack_top_p, arguments_list_len); + ecma_collection_t *arguments_p = opfunc_spread_arguments (&stack_top_p, arguments_list_len); if (JERRY_UNLIKELY (arguments_p == NULL)) { @@ -1596,6 +1594,10 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ stack_top_p++; ECMA_SET_INTERNAL_VALUE_POINTER (stack_top_p[-1], arguments_p); } + else + { + stack_top_p -= arguments_list_len; + } frame_ctx_p->call_operation = VM_EXEC_SUPER_CALL; frame_ctx_p->byte_code_p = byte_code_start_p; @@ -1919,10 +1921,10 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ goto error; } - case VM_OC_CREATE_SPREAD_OBJECT: + case VM_OC_PUSH_SPREAD_ELEMENT: { - *stack_top_p++ = ecma_op_create_spread_object (left_value); - goto free_left_value; + *stack_top_p++ = ECMA_VALUE_SPREAD_ELEMENT; + continue; } case VM_OC_GET_ITERATOR: { @@ -2029,9 +2031,8 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ case VM_OC_SPREAD_ARGUMENTS: { uint8_t arguments_list_len = *byte_code_p++; - stack_top_p -= arguments_list_len; - ecma_collection_t *arguments_p = opfunc_spread_arguments (stack_top_p, arguments_list_len); + ecma_collection_t *arguments_p = opfunc_spread_arguments (&stack_top_p, arguments_list_len); if (JERRY_UNLIKELY (arguments_p == NULL)) { diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 82f38e7cb..4198f7999 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -247,7 +247,7 @@ typedef enum VM_OC_PUSH_CONSTRUCTOR_SUPER, /**< push 'super' inside a class constructor */ VM_OC_PUSH_CONSTRUCTOR_THIS, /**< push 'this' inside a class constructor */ VM_OC_CONSTRUCTOR_RET, /**< explicit return from a class constructor */ - VM_OC_CREATE_SPREAD_OBJECT, /**< create spread object */ + VM_OC_PUSH_SPREAD_ELEMENT, /**< push spread element */ VM_OC_GET_ITERATOR, /**< GetIterator abstract operation */ VM_OC_ITERATOR_STEP, /**< IteratorStep abstract operation */ VM_OC_DEFAULT_INITIALIZER, /**< default initializer inside a pattern */ @@ -296,7 +296,7 @@ typedef enum VM_OC_PUSH_CONSTRUCTOR_SUPER = VM_OC_NONE, /**< push 'super' inside a class constructor */ VM_OC_PUSH_CONSTRUCTOR_THIS = VM_OC_NONE, /**< push 'this' inside a class constructor */ VM_OC_CONSTRUCTOR_RET = VM_OC_NONE, /**< explicit return from a class constructor */ - VM_OC_CREATE_SPREAD_OBJECT = VM_OC_NONE, /**< create spread object */ + VM_OC_PUSH_SPREAD_ELEMENT = VM_OC_NONE, /**< push spread element */ VM_OC_GET_ITERATOR = VM_OC_NONE, /**< GetIterator abstract operation */ VM_OC_ITERATOR_STEP = VM_OC_NONE, /**< IteratorStep abstract operation */ VM_OC_DEFAULT_INITIALIZER = VM_OC_NONE, /**< default initializer inside a pattern */