From 1fe2817457a149a4b783df9f61848bca2e79600a Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Mon, 26 Oct 2015 16:23:06 +0300 Subject: [PATCH] Introduce parser-time optimization that moves function's argument values to registers. For functions with the optimization applied: - vm puts arguments values immediately to registers without putting them to variables in lexical environment; - number of arguments is extracted from corresponding 'reg_var_decl' instruction's argument; - for functions that also don't have local variables, lexical environments are not created. JerryScript-DCO-1.0-Signed-off-by: Ruben Ayrapetyan r.ayrapetyan@samsung.com --- .../ecma/operations/ecma-function-object.cpp | 163 +++++++++-- jerry-core/parser/js/opcodes-dumper.cpp | 272 ++++++++++++------ jerry-core/parser/js/opcodes-dumper.h | 11 +- jerry-core/parser/js/parser.cpp | 193 +++++++++++-- jerry-core/parser/js/scopes-tree.cpp | 43 +++ jerry-core/parser/js/scopes-tree.h | 2 + jerry-core/parser/js/serializer.cpp | 23 -- jerry-core/parser/js/serializer.h | 2 - jerry-core/vm/opcodes.h | 6 +- jerry-core/vm/pretty-printer.cpp | 12 +- jerry-core/vm/vm-opcodes.inc.h | 5 +- jerry-core/vm/vm-stack.cpp | 24 +- jerry-core/vm/vm-stack.h | 2 +- jerry-core/vm/vm.cpp | 35 ++- jerry-core/vm/vm.h | 3 +- tests/unit/test-parser.cpp | 2 +- 16 files changed, 604 insertions(+), 194 deletions(-) diff --git a/jerry-core/ecma/operations/ecma-function-object.cpp b/jerry-core/ecma/operations/ecma-function-object.cpp index 0429d0a8b..cdc734c16 100644 --- a/jerry-core/ecma/operations/ecma-function-object.cpp +++ b/jerry-core/ecma/operations/ecma-function-object.cpp @@ -46,14 +46,21 @@ static uint32_t ecma_pack_code_internal_property_value (bool is_strict, /**< is code strict? */ bool do_instantiate_args_obj, /**< should an Arguments object be * instantiated for the code */ + bool is_arguments_moved_to_regs, /**< values of the function's arguments + * are placed on registers */ + bool is_no_lex_env, /**< the function needs no lexical environment */ vm_instr_counter_t instr_oc) /**< position of first instruction */ { uint32_t value = instr_oc; const uint32_t is_strict_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 1); const uint32_t do_instantiate_arguments_object_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 2); + const uint32_t arguments_moved_to_regs_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 3); + const uint32_t no_lex_env_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 4); JERRY_ASSERT (((value) & (1u << is_strict_bit_offset)) == 0); JERRY_ASSERT (((value) & (1u << do_instantiate_arguments_object_bit_offset)) == 0); + JERRY_ASSERT (((value) & (1u << arguments_moved_to_regs_bit_offset)) == 0); + JERRY_ASSERT (((value) & (1u << no_lex_env_bit_offset)) == 0); if (is_strict) { @@ -65,6 +72,16 @@ ecma_pack_code_internal_property_value (bool is_strict, /**< is code strict? */ value |= (1u << do_instantiate_arguments_object_bit_offset); } + if (is_arguments_moved_to_regs) + { + value |= (1u << arguments_moved_to_regs_bit_offset); + } + + if (is_no_lex_env) + { + value |= (1u << no_lex_env_bit_offset); + } + return value; } /* ecma_pack_code_internal_property_value */ @@ -77,18 +94,31 @@ ecma_pack_code_internal_property_value (bool is_strict, /**< is code strict? */ static vm_instr_counter_t ecma_unpack_code_internal_property_value (uint32_t value, /**< packed value */ bool* out_is_strict_p, /**< out: is code strict? */ - bool* out_do_instantiate_args_obj_p) /**< should an Arguments object be + bool* out_do_instantiate_args_obj_p, /**< should an Arguments object be * instantiated for the code */ + bool* out_is_arguments_moved_to_regs_p, /**< values of the function's + * arguments are placed + * on registers */ + bool* out_is_no_lex_env_p) /**< the function needs no lexical environment */ { JERRY_ASSERT (out_is_strict_p != NULL); JERRY_ASSERT (out_do_instantiate_args_obj_p != NULL); + JERRY_ASSERT (out_is_arguments_moved_to_regs_p != NULL); + JERRY_ASSERT (out_is_no_lex_env_p != NULL); const uint32_t is_strict_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 1); const uint32_t do_instantiate_arguments_object_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 2); + const uint32_t is_arguments_moved_to_regs_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 3); + const uint32_t is_no_lex_env_bit_offset = (uint32_t) (sizeof (value) * JERRY_BITSINBYTE - 4); *out_is_strict_p = ((value & (1u << is_strict_bit_offset)) != 0); *out_do_instantiate_args_obj_p = ((value & (1u << do_instantiate_arguments_object_bit_offset)) != 0); - value &= ~((1u << is_strict_bit_offset) | (1u << do_instantiate_arguments_object_bit_offset)); + *out_is_arguments_moved_to_regs_p = ((value & (1u << is_arguments_moved_to_regs_bit_offset)) != 0); + *out_is_no_lex_env_p = ((value & (1u << is_no_lex_env_bit_offset)) != 0); + value &= ~((1u << is_strict_bit_offset) + | (1u << do_instantiate_arguments_object_bit_offset) + | (1u << is_arguments_moved_to_regs_bit_offset) + | (1u << is_no_lex_env_bit_offset)); return (vm_instr_counter_t) value; } /* ecma_unpack_code_internal_property_value */ @@ -221,6 +251,8 @@ ecma_op_create_function_object (ecma_collection_header_t *formal_params_collecti { bool is_strict_mode_code = is_decl_in_strict_mode; bool do_instantiate_arguments_object = true; + bool is_arguments_moved_to_regs = false; + bool is_no_lex_env = false; vm_instr_counter_t instr_pos = first_instr_pos; opcode_scope_code_flags_t scope_flags = vm_get_scope_flags (bytecode_header_p, instr_pos++); @@ -229,6 +261,7 @@ ecma_op_create_function_object (ecma_collection_header_t *formal_params_collecti { is_strict_mode_code = true; } + if ((scope_flags & OPCODE_SCOPE_CODE_FLAGS_NOT_REF_ARGUMENTS_IDENTIFIER) && (scope_flags & OPCODE_SCOPE_CODE_FLAGS_NOT_REF_EVAL_IDENTIFIER)) { @@ -238,6 +271,16 @@ ecma_op_create_function_object (ecma_collection_header_t *formal_params_collecti do_instantiate_arguments_object = false; } + if (scope_flags & OPCODE_SCOPE_CODE_FLAGS_ARGUMENTS_ON_REGISTERS) + { + is_arguments_moved_to_regs = true; + } + + if (scope_flags & OPCODE_SCOPE_CODE_FLAGS_NO_LEX_ENV) + { + is_no_lex_env = true; + } + // 1., 4., 13. ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); @@ -279,6 +322,8 @@ ecma_op_create_function_object (ecma_collection_header_t *formal_params_collecti ecma_property_t *code_prop_p = ecma_create_internal_property (f, ECMA_INTERNAL_PROPERTY_CODE_FLAGS_AND_OFFSET); code_prop_p->u.internal_property.value = ecma_pack_code_internal_property_value (is_strict_mode_code, do_instantiate_arguments_object, + is_arguments_moved_to_regs, + is_no_lex_env, instr_pos); // 14. @@ -405,7 +450,26 @@ ecma_op_function_try_lazy_instantiate_property (ecma_object_t *obj_p, /**< the f ECMA_INTERNAL_PROPERTY_FORMAL_PARAMETERS); if (formal_parameters_prop_p == NULL) { - *len_p = 0; + ecma_property_t *bytecode_prop_p = ecma_get_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_CODE_BYTECODE); + ecma_property_t *code_prop_p = ecma_get_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_CODE_FLAGS_AND_OFFSET); + + uint32_t code_prop_value = code_prop_p->u.internal_property.value; + + bool is_strict; + bool do_instantiate_args_obj; + bool is_arguments_moved_to_regs; + bool is_no_lex_env; + + const bytecode_data_header_t *bytecode_header_p; + bytecode_header_p = MEM_CP_GET_POINTER (const bytecode_data_header_t, bytecode_prop_p->u.internal_property.value); + + vm_instr_counter_t code_first_instr_pos = ecma_unpack_code_internal_property_value (code_prop_value, + &is_strict, + &do_instantiate_args_obj, + &is_arguments_moved_to_regs, + &is_no_lex_env); + + *len_p = vm_get_scope_args_num (bytecode_header_p, code_first_instr_pos); } else { @@ -871,11 +935,17 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ // 8. bool is_strict; bool do_instantiate_args_obj; + bool is_arguments_moved_to_regs; + bool is_no_lex_env; + const bytecode_data_header_t *bytecode_data_p; bytecode_data_p = MEM_CP_GET_POINTER (const bytecode_data_header_t, bytecode_prop_p->u.internal_property.value); + vm_instr_counter_t code_first_instr_pos = ecma_unpack_code_internal_property_value (code_prop_value, &is_strict, - &do_instantiate_args_obj); + &do_instantiate_args_obj, + &is_arguments_moved_to_regs, + &is_no_lex_env); ecma_value_t this_binding; // 1. @@ -899,36 +969,75 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ } // 5. - ecma_object_t *local_env_p = ecma_create_decl_lex_env (scope_p); - - // 9. - ECMA_TRY_CATCH (args_var_declaration_ret, - ecma_function_call_setup_args_variables (func_obj_p, - local_env_p, - arg_collection_p, - is_strict, - do_instantiate_args_obj), - ret_value); - - ecma_completion_value_t completion = vm_run_from_pos (bytecode_data_p, - code_first_instr_pos, - this_binding, - local_env_p, - is_strict, - false); - - if (ecma_is_completion_value_return (completion)) + ecma_object_t *local_env_p; + if (is_no_lex_env) { - ret_value = ecma_make_normal_completion_value (ecma_get_completion_value_value (completion)); + local_env_p = scope_p; } else { - ret_value = completion; + local_env_p = ecma_create_decl_lex_env (scope_p); } - ECMA_FINALIZE (args_var_declaration_ret); + if (is_arguments_moved_to_regs) + { + ecma_completion_value_t completion = vm_run_from_pos (bytecode_data_p, + code_first_instr_pos, + this_binding, + local_env_p, + is_strict, + false, + arg_collection_p); + + if (ecma_is_completion_value_return (completion)) + { + ret_value = ecma_make_normal_completion_value (ecma_get_completion_value_value (completion)); + } + else + { + ret_value = completion; + } + } + else + { + // 9. + ECMA_TRY_CATCH (args_var_declaration_ret, + ecma_function_call_setup_args_variables (func_obj_p, + local_env_p, + arg_collection_p, + is_strict, + do_instantiate_args_obj), + ret_value); + + ecma_completion_value_t completion = vm_run_from_pos (bytecode_data_p, + code_first_instr_pos, + this_binding, + local_env_p, + is_strict, + false, + NULL); + + if (ecma_is_completion_value_return (completion)) + { + ret_value = ecma_make_normal_completion_value (ecma_get_completion_value_value (completion)); + } + else + { + ret_value = completion; + } + + ECMA_FINALIZE (args_var_declaration_ret); + } + + if (is_no_lex_env) + { + /* do nothing */ + } + else + { + ecma_deref_object (local_env_p); + } - ecma_deref_object (local_env_p); ecma_free_value (this_binding, true); } } diff --git a/jerry-core/parser/js/opcodes-dumper.cpp b/jerry-core/parser/js/opcodes-dumper.cpp index cea762e12..01d36c8b8 100644 --- a/jerry-core/parser/js/opcodes-dumper.cpp +++ b/jerry-core/parser/js/opcodes-dumper.cpp @@ -42,10 +42,24 @@ static vm_idx_t jsp_reg_max_for_temps; * so the value, if not equal to VM_IDX_EMPTY, is always greater than jsp_reg_max_for_temps. * * See also: - * dumper_try_replace_var_with_reg + * dumper_try_replace_identifier_name_with_reg */ static vm_idx_t jsp_reg_max_for_local_var; +/** + * Maximum identifier of a register, allocated for storage of an argument value. + * + * The value can be VM_IDX_EMPTY, indicating that no registers were allocated for argument values. + * + * Note: + * Registers for argument values are always allocated after registers for variable values, + * so the value, if not equal to VM_IDX_EMPTY, is always greater than jsp_reg_max_for_local_var. + * + * See also: + * dumper_try_replace_identifier_name_with_reg + */ +static vm_idx_t jsp_reg_max_for_args; + enum { U8_global_size @@ -130,12 +144,6 @@ enum }; STATIC_STACK (jsp_reg_id_stack, vm_idx_t) -enum -{ - reg_var_decls_global_size -}; -STATIC_STACK (reg_var_decls, vm_instr_counter_t) - /** * Allocate next register for intermediate value * @@ -145,6 +153,7 @@ static vm_idx_t jsp_alloc_reg_for_temp (void) { JERRY_ASSERT (jsp_reg_max_for_local_var == VM_IDX_EMPTY); + JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); vm_idx_t next_reg = jsp_reg_next++; @@ -166,6 +175,51 @@ jsp_alloc_reg_for_temp (void) } /* jsp_alloc_reg_for_temp */ #ifdef CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER +/** + * Start move of variable values to registers optimization pass + */ +void +dumper_start_move_of_vars_to_regs () +{ + JERRY_ASSERT (jsp_reg_max_for_local_var == VM_IDX_EMPTY); + JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); + + jsp_reg_max_for_local_var = jsp_reg_max_for_temps; +} /* dumper_start_move_of_vars_to_regs */ + +/** + * Start move of argument values to registers optimization pass + * + * @return true - if optimization can be performed successfully (i.e. there are enough free registers), + * false - otherwise. + */ +bool +dumper_start_move_of_args_to_regs (uint32_t args_num) /**< number of arguments */ +{ + JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); + + if (jsp_reg_max_for_local_var == VM_IDX_EMPTY) + { + if (args_num + jsp_reg_max_for_temps >= VM_REG_GENERAL_LAST) + { + return false; + } + + jsp_reg_max_for_args = jsp_reg_max_for_temps; + } + else + { + if (args_num + jsp_reg_max_for_local_var >= VM_REG_GENERAL_LAST) + { + return false; + } + + jsp_reg_max_for_args = jsp_reg_max_for_local_var; + } + + return true; +} /* dumper_start_move_of_args_to_regs */ + /** * Try to move local variable to a register * @@ -183,72 +237,63 @@ jsp_alloc_reg_for_temp (void) * false - otherwise. */ bool -dumper_try_replace_var_with_reg (scopes_tree tree, /**< a function scope, created for - * function declaration or function expresssion */ - op_meta *var_decl_om_p) /**< operation meta of corresponding variable declaration */ +dumper_try_replace_identifier_name_with_reg (scopes_tree tree, /**< a function scope, created for + * function declaration or function expresssion */ + op_meta *om_p) /**< operation meta of corresponding + * variable declaration */ { JERRY_ASSERT (tree->type == SCOPE_TYPE_FUNCTION); - JERRY_ASSERT (var_decl_om_p->op.op_idx == VM_OP_VAR_DECL); - JERRY_ASSERT (var_decl_om_p->lit_id[0].packed_value != NOT_A_LITERAL.packed_value); - JERRY_ASSERT (var_decl_om_p->lit_id[1].packed_value == NOT_A_LITERAL.packed_value); - JERRY_ASSERT (var_decl_om_p->lit_id[2].packed_value == NOT_A_LITERAL.packed_value); + lit_cpointer_t lit_cp; + bool is_arg; - vm_instr_counter_t instr_pos = 0; - - op_meta header_opm = scopes_tree_op_meta (tree, instr_pos++); - JERRY_ASSERT (header_opm.op.op_idx == VM_OP_FUNC_EXPR_N || header_opm.op.op_idx == VM_OP_FUNC_DECL_N); - - while (true) + if (om_p->op.op_idx == VM_OP_VAR_DECL) { - op_meta meta_opm = scopes_tree_op_meta (tree, instr_pos++); - JERRY_ASSERT (meta_opm.op.op_idx == VM_OP_META); + JERRY_ASSERT (om_p->lit_id[0].packed_value != NOT_A_LITERAL.packed_value); + JERRY_ASSERT (om_p->lit_id[1].packed_value == NOT_A_LITERAL.packed_value); + JERRY_ASSERT (om_p->lit_id[2].packed_value == NOT_A_LITERAL.packed_value); - opcode_meta_type meta_type = (opcode_meta_type) meta_opm.op.data.meta.type; + lit_cp = om_p->lit_id[0]; - if (meta_type == OPCODE_META_TYPE_FUNCTION_END) + is_arg = false; + } + else + { + JERRY_ASSERT (om_p->op.op_idx == VM_OP_META); + + JERRY_ASSERT (om_p->op.data.meta.type == OPCODE_META_TYPE_VARG); + JERRY_ASSERT (om_p->lit_id[0].packed_value == NOT_A_LITERAL.packed_value); + JERRY_ASSERT (om_p->lit_id[1].packed_value != NOT_A_LITERAL.packed_value); + JERRY_ASSERT (om_p->lit_id[2].packed_value == NOT_A_LITERAL.packed_value); + + lit_cp = om_p->lit_id[1]; + + is_arg = true; + } + + vm_idx_t reg; + + if (is_arg) + { + JERRY_ASSERT (jsp_reg_max_for_args != VM_IDX_EMPTY); + JERRY_ASSERT (jsp_reg_max_for_args < VM_REG_GENERAL_LAST); + + reg = ++jsp_reg_max_for_args; + } + else + { + JERRY_ASSERT (jsp_reg_max_for_local_var != VM_IDX_EMPTY); + + if (jsp_reg_max_for_local_var == VM_REG_GENERAL_LAST) { - /* marker of function argument list end reached */ - break; + /* not enough registers */ + return false; } - else - { - JERRY_ASSERT (meta_type == OPCODE_META_TYPE_VARG); + JERRY_ASSERT (jsp_reg_max_for_local_var < VM_REG_GENERAL_LAST); - /* the varg specifies argument name, and so should be a string literal */ - JERRY_ASSERT (meta_opm.op.data.meta.data_1 == VM_IDX_REWRITE_LITERAL_UID); - JERRY_ASSERT (meta_opm.lit_id[1].packed_value != NOT_A_LITERAL.packed_value); - - if (meta_opm.lit_id[1].packed_value == var_decl_om_p->lit_id[0].packed_value) - { - /* - * Optimization is not performed, because the variable's name is equal to an argument name, - * and the argument's value would be initialized by its name in run-time. - * - * See also: - * parser_parse_program - */ - return false; - } - } + reg = ++jsp_reg_max_for_local_var; } - if (jsp_reg_max_for_local_var == VM_IDX_EMPTY) - { - jsp_reg_max_for_local_var = jsp_reg_max_for_temps; - } - - if (jsp_reg_max_for_local_var == VM_REG_GENERAL_LAST) - { - /* not enough registers */ - return false; - } - JERRY_ASSERT (jsp_reg_max_for_local_var < VM_REG_GENERAL_LAST); - - vm_idx_t reg = ++jsp_reg_max_for_local_var; - - lit_cpointer_t lit_cp = var_decl_om_p->lit_id[0]; - for (vm_instr_counter_t instr_pos = 0; instr_pos < tree->instrs_count; instr_pos++) @@ -320,9 +365,21 @@ dumper_try_replace_var_with_reg (scopes_tree tree, /**< a function scope, create } return true; -} /* dumper_try_replace_var_with_reg */ +} /* dumper_try_replace_identifier_name_with_reg */ #endif /* CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER */ +/** + * Just allocate register for argument that is never used due to duplicated argument names + */ +void +dumper_alloc_reg_for_unused_arg (void) +{ + JERRY_ASSERT (jsp_reg_max_for_args != VM_IDX_EMPTY); + JERRY_ASSERT (jsp_reg_max_for_args < VM_REG_GENERAL_LAST); + + ++jsp_reg_max_for_args; +} /* dumper_alloc_reg_for_unused_arg */ + /** * Generate instruction with specified opcode and operands * @@ -650,6 +707,7 @@ void dumper_new_scope (void) { JERRY_ASSERT (jsp_reg_max_for_local_var == VM_IDX_EMPTY); + JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); STACK_PUSH (jsp_reg_id_stack, jsp_reg_next); STACK_PUSH (jsp_reg_id_stack, jsp_reg_max_for_temps); @@ -662,6 +720,7 @@ void dumper_finish_scope (void) { JERRY_ASSERT (jsp_reg_max_for_local_var == VM_IDX_EMPTY); + JERRY_ASSERT (jsp_reg_max_for_args == VM_IDX_EMPTY); jsp_reg_max_for_temps = STACK_TOP (jsp_reg_id_stack); STACK_DROP (jsp_reg_id_stack, 1); @@ -1182,6 +1241,23 @@ rewrite_function_end () STACK_DROP (function_ends, 1); } +/** + * Decrement position of 'function_end' instruction (the position is stored in "function_ends" stack) + * + * Note: + * The operation is used upon deleting a 'varg' meta, describing element of a function's formal parameters list + */ +void +dumper_decrement_function_end_pos (void) +{ + vm_instr_counter_t oc = STACK_TOP (function_ends); + + oc--; + + STACK_DROP (function_ends, 1); + STACK_PUSH (function_ends, oc); +} /* dumper_decrement_function_end_pos */ + jsp_operand_t dump_this_res (void) { @@ -2308,27 +2384,6 @@ dump_throw (jsp_operand_t op) dump_single_address (VM_OP_THROW_VALUE, op); } -/** - * Checks if variable is already declared - * - * @return true if variable declaration already exists - * false otherwise - */ -bool -dumper_variable_declaration_exists (lit_cpointer_t lit_id) /**< literal which holds variable's name */ -{ - vm_instr_counter_t var_decls_count = (vm_instr_counter_t) serializer_get_current_var_decls_counter (); - for (vm_instr_counter_t oc = (vm_instr_counter_t) (0); oc < var_decls_count; oc++) - { - const op_meta var_decl_op_meta = serializer_get_var_decl (oc); - if (var_decl_op_meta.lit_id[0].packed_value == lit_id.packed_value) - { - return true; - } - } - return false; -} /* dumper_variable_declaration_exists */ - /** * Dump instruction designating variable declaration */ @@ -2386,20 +2441,30 @@ dump_ret (void) serializer_dump_op_meta (jsp_dmp_create_op_meta_0 (VM_OP_RET)); } -void +/** + * Dump 'reg_var_decl' instruction template + * + * @return position of the dumped instruction + */ +vm_instr_counter_t dump_reg_var_decl_for_rewrite (void) { - STACK_PUSH (reg_var_decls, serializer_get_current_instr_counter ()); - dump_double_address (VM_OP_REG_VAR_DECL, + vm_instr_counter_t oc = serializer_get_current_instr_counter (); + + dump_triple_address (VM_OP_REG_VAR_DECL, + jsp_operand_t::make_unknown_operand (), jsp_operand_t::make_unknown_operand (), jsp_operand_t::make_unknown_operand ()); -} + return oc; +} /* dump_reg_var_decl_for_rewrite */ + +/** + * Rewrite 'reg_var_decl' instruction's template with current scope's register counts + */ void -rewrite_reg_var_decl (void) +rewrite_reg_var_decl (vm_instr_counter_t reg_var_decl_oc) /**< position of dumped 'reg_var_decl' template */ { - vm_instr_counter_t reg_var_decl_oc = STACK_TOP (reg_var_decls); - op_meta opm = serializer_get_op_meta (reg_var_decl_oc); JERRY_ASSERT (opm.op.op_idx == VM_OP_REG_VAR_DECL); @@ -2417,10 +2482,28 @@ rewrite_reg_var_decl (void) opm.op.data.reg_var_decl.local_var_regs_num = 0; } - serializer_rewrite_op_meta (reg_var_decl_oc, opm); + if (jsp_reg_max_for_args != VM_IDX_EMPTY) + { + if (jsp_reg_max_for_local_var != VM_IDX_EMPTY) + { + JERRY_ASSERT (jsp_reg_max_for_args >= jsp_reg_max_for_local_var); + opm.op.data.reg_var_decl.arg_regs_num = (vm_idx_t) (jsp_reg_max_for_args - jsp_reg_max_for_local_var); + } + else + { + JERRY_ASSERT (jsp_reg_max_for_args >= jsp_reg_max_for_temps); + opm.op.data.reg_var_decl.arg_regs_num = (vm_idx_t) (jsp_reg_max_for_args - jsp_reg_max_for_temps); + } - STACK_DROP (reg_var_decls, 1); -} + jsp_reg_max_for_args = VM_IDX_EMPTY; + } + else + { + opm.op.data.reg_var_decl.arg_regs_num = 0; + } + + serializer_rewrite_op_meta (reg_var_decl_oc, opm); +} /* rewrite_reg_var_decl */ void dump_retval (jsp_operand_t op) @@ -2434,6 +2517,7 @@ dumper_init (void) jsp_reg_next = VM_REG_GENERAL_FIRST; jsp_reg_max_for_temps = VM_REG_GENERAL_FIRST; jsp_reg_max_for_local_var = VM_IDX_EMPTY; + jsp_reg_max_for_args = VM_IDX_EMPTY; STACK_INIT (U8); STACK_INIT (varg_headers); @@ -2449,7 +2533,6 @@ dumper_init (void) STACK_INIT (finallies); STACK_INIT (tries); STACK_INIT (jsp_reg_id_stack); - STACK_INIT (reg_var_decls); } void @@ -2469,5 +2552,4 @@ dumper_free (void) STACK_FREE (finallies); STACK_FREE (tries); STACK_FREE (jsp_reg_id_stack); - STACK_FREE (reg_var_decls); } diff --git a/jerry-core/parser/js/opcodes-dumper.h b/jerry-core/parser/js/opcodes-dumper.h index 7ce0f7a86..6ce9eb41b 100644 --- a/jerry-core/parser/js/opcodes-dumper.h +++ b/jerry-core/parser/js/opcodes-dumper.h @@ -305,7 +305,10 @@ bool operand_is_empty (jsp_operand_t); void dumper_init (void); void dumper_free (void); -bool dumper_try_replace_var_with_reg (scopes_tree, op_meta *); +void dumper_start_move_of_vars_to_regs (); +bool dumper_start_move_of_args_to_regs (uint32_t args_num); +bool dumper_try_replace_identifier_name_with_reg (scopes_tree, op_meta *); +void dumper_alloc_reg_for_unused_arg (void); void dumper_new_statement (void); void dumper_new_scope (void); @@ -347,6 +350,7 @@ void dump_prop_setter (jsp_operand_t, jsp_operand_t, jsp_operand_t); void dump_function_end_for_rewrite (void); void rewrite_function_end (); +void dumper_decrement_function_end_pos (void); jsp_operand_t dump_this_res (void); @@ -474,14 +478,13 @@ void rewrite_finally (void); void dump_end_try_catch_finally (void); void dump_throw (jsp_operand_t); -bool dumper_variable_declaration_exists (lit_cpointer_t); void dump_variable_declaration (lit_cpointer_t); vm_instr_counter_t dump_scope_code_flags_for_rewrite (void); void rewrite_scope_code_flags (vm_instr_counter_t, opcode_scope_code_flags_t); -void dump_reg_var_decl_for_rewrite (void); -void rewrite_reg_var_decl (void); +vm_instr_counter_t dump_reg_var_decl_for_rewrite (void); +void rewrite_reg_var_decl (vm_instr_counter_t); void dump_ret (void); void dump_retval (jsp_operand_t); diff --git a/jerry-core/parser/js/parser.cpp b/jerry-core/parser/js/parser.cpp index f4ba7d61a..4abcbc700 100644 --- a/jerry-core/parser/js/parser.cpp +++ b/jerry-core/parser/js/parser.cpp @@ -1852,18 +1852,19 @@ static jsp_operand_t parse_variable_declaration (void) { current_token_must_be (TOK_NAME); - const jsp_operand_t name = literal_operand (token_data_as_lit_cp ()); - if (!dumper_variable_declaration_exists (token_data_as_lit_cp ())) + const lit_cpointer_t lit_cp = token_data_as_lit_cp (); + const jsp_operand_t name = literal_operand (lit_cp); + + if (!scopes_tree_variable_declaration_exists (STACK_TOP (scopes), lit_cp)) { - jsp_early_error_check_for_eval_and_arguments_in_strict_mode (literal_operand (token_data_as_lit_cp ()), - is_strict_mode (), - tok.loc); + jsp_early_error_check_for_eval_and_arguments_in_strict_mode (name, is_strict_mode (), tok.loc); - dump_variable_declaration (token_data_as_lit_cp ()); + dump_variable_declaration (lit_cp); } skip_newlines (); + if (token_is (TOK_EQ)) { skip_newlines (); @@ -3014,7 +3015,7 @@ parse_source_element_list (bool is_global, /**< flag, indicating that we parsing check_directive_prologue_for_use_strict (); - dump_reg_var_decl_for_rewrite (); + vm_instr_counter_t reg_var_decl_oc = dump_reg_var_decl_for_rewrite (); if (inside_eval && !inside_function) @@ -3054,8 +3055,6 @@ parse_source_element_list (bool is_global, /**< flag, indicating that we parsing scope_flags = (opcode_scope_code_flags_t) (scope_flags | OPCODE_SCOPE_CODE_FLAGS_NOT_REF_EVAL_IDENTIFIER); } - rewrite_scope_code_flags (scope_code_flags_oc, scope_flags); - #ifdef CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER if (is_try_replace_local_vars_with_regs && fe_scope_tree->type == SCOPE_TYPE_FUNCTION) @@ -3083,30 +3082,167 @@ parse_source_element_list (bool is_global, /**< flag, indicating that we parsing /* no subscopes, as no function declarations / eval etc. in the scope */ JERRY_ASSERT (fe_scope_tree->t.children_num == 0); - bool are_all_vars_replaced = true; - for (vm_instr_counter_t var_decl_pos = 0; - var_decl_pos < linked_list_get_length (fe_scope_tree->var_decls); - var_decl_pos++) - { - op_meta *om_p = (op_meta *) linked_list_element (fe_scope_tree->var_decls, var_decl_pos); + vm_instr_counter_t instr_pos = 0u; - if (!dumper_try_replace_var_with_reg (fe_scope_tree, om_p)) + const vm_instr_counter_t header_oc = instr_pos++; + op_meta header_opm = scopes_tree_op_meta (fe_scope_tree, header_oc); + JERRY_ASSERT (header_opm.op.op_idx == VM_OP_FUNC_EXPR_N || header_opm.op.op_idx == VM_OP_FUNC_DECL_N); + + vm_instr_counter_t function_end_pos = instr_pos; + while (true) + { + op_meta meta_opm = scopes_tree_op_meta (fe_scope_tree, function_end_pos); + JERRY_ASSERT (meta_opm.op.op_idx == VM_OP_META); + + opcode_meta_type meta_type = (opcode_meta_type) meta_opm.op.data.meta.type; + + if (meta_type == OPCODE_META_TYPE_FUNCTION_END) { - are_all_vars_replaced = false; + /* marker of function argument list end reached */ + break; + } + else + { + JERRY_ASSERT (meta_type == OPCODE_META_TYPE_VARG); + + function_end_pos++; } } - if (are_all_vars_replaced) - { - /* - * All local variables were replaced with registers, so variable declarations could be removed. - * - * TODO: - * Support removal of a particular variable declaration, without removing the whole list. - */ + uint32_t args_num = (uint32_t) (function_end_pos - instr_pos); - linked_list_free (fe_scope_tree->var_decls); - fe_scope_tree->var_decls = linked_list_init (sizeof (op_meta)); + dumper_start_move_of_vars_to_regs (); + + /* remove declarations of variables with names equal to an argument's name */ + vm_instr_counter_t var_decl_pos = 0; + while (var_decl_pos < linked_list_get_length (fe_scope_tree->var_decls)) + { + op_meta *om_p = (op_meta *) linked_list_element (fe_scope_tree->var_decls, var_decl_pos); + bool is_removed = false; + + for (vm_instr_counter_t arg_index = instr_pos; + arg_index < function_end_pos; + arg_index++) + { + op_meta meta_opm = scopes_tree_op_meta (fe_scope_tree, arg_index); + JERRY_ASSERT (meta_opm.op.op_idx == VM_OP_META); + + JERRY_ASSERT (meta_opm.op.data.meta.data_1 == VM_IDX_REWRITE_LITERAL_UID); + JERRY_ASSERT (om_p->op.data.var_decl.variable_name == VM_IDX_REWRITE_LITERAL_UID); + + if (meta_opm.lit_id[1].packed_value == om_p->lit_id[0].packed_value) + { + linked_list_remove_element (fe_scope_tree->var_decls, var_decl_pos); + + is_removed = true; + break; + } + } + + if (!is_removed) + { + if (!dumper_try_replace_identifier_name_with_reg (fe_scope_tree, om_p)) + { + var_decl_pos++; + } + else + { + linked_list_remove_element (fe_scope_tree->var_decls, var_decl_pos); + } + } + } + + if (dumper_start_move_of_args_to_regs (args_num)) + { + scope_flags = (opcode_scope_code_flags_t) (scope_flags | OPCODE_SCOPE_CODE_FLAGS_ARGUMENTS_ON_REGISTERS); + + JERRY_ASSERT (linked_list_get_length (fe_scope_tree->var_decls) == 0); + scope_flags = (opcode_scope_code_flags_t) (scope_flags | OPCODE_SCOPE_CODE_FLAGS_NO_LEX_ENV); + + /* at this point all arguments can be moved to registers */ + if (header_opm.op.op_idx == VM_OP_FUNC_EXPR_N) + { + header_opm.op.data.func_expr_n.arg_list = 0; + } + else + { + JERRY_ASSERT (header_opm.op.op_idx == VM_OP_FUNC_DECL_N); + + header_opm.op.data.func_decl_n.arg_list = 0; + } + + scopes_tree_set_op_meta (fe_scope_tree, header_oc, header_opm); + + /* + * Mark duplicated arguments names as empty, + * leaving only last declaration for each duplicated + * argument name + */ + for (vm_instr_counter_t arg1_index = instr_pos; + arg1_index < function_end_pos; + arg1_index++) + { + op_meta meta_opm1 = scopes_tree_op_meta (fe_scope_tree, arg1_index); + JERRY_ASSERT (meta_opm1.op.op_idx == VM_OP_META); + + for (vm_instr_counter_t arg2_index = (vm_instr_counter_t) (arg1_index + 1u); + arg2_index < function_end_pos; + arg2_index++) + { + op_meta meta_opm2 = scopes_tree_op_meta (fe_scope_tree, arg2_index); + JERRY_ASSERT (meta_opm2.op.op_idx == VM_OP_META); + + if (meta_opm1.lit_id[1].packed_value == meta_opm2.lit_id[1].packed_value) + { + meta_opm1.op.data.meta.data_1 = VM_IDX_EMPTY; + meta_opm1.lit_id[1] = NOT_A_LITERAL; + + scopes_tree_set_op_meta (fe_scope_tree, arg1_index, meta_opm1); + + break; + } + } + } + + while (true) + { + op_meta meta_opm = scopes_tree_op_meta (fe_scope_tree, instr_pos); + JERRY_ASSERT (meta_opm.op.op_idx == VM_OP_META); + + opcode_meta_type meta_type = (opcode_meta_type) meta_opm.op.data.meta.type; + + if (meta_type == OPCODE_META_TYPE_FUNCTION_END) + { + /* marker of function argument list end reached */ + break; + } + else + { + JERRY_ASSERT (meta_type == OPCODE_META_TYPE_VARG); + + if (meta_opm.op.data.meta.data_1 == VM_IDX_EMPTY) + { + JERRY_ASSERT (meta_opm.lit_id[1].packed_value == NOT_A_LITERAL.packed_value); + + dumper_alloc_reg_for_unused_arg (); + } + else + { + /* the varg specifies argument name, and so should be a string literal */ + JERRY_ASSERT (meta_opm.op.data.meta.data_1 == VM_IDX_REWRITE_LITERAL_UID); + JERRY_ASSERT (meta_opm.lit_id[1].packed_value != NOT_A_LITERAL.packed_value); + + bool is_replaced = dumper_try_replace_identifier_name_with_reg (fe_scope_tree, &meta_opm); + JERRY_ASSERT (is_replaced); + } + + scopes_tree_remove_op_meta (fe_scope_tree, instr_pos); + + reg_var_decl_oc--; + scope_code_flags_oc--; + dumper_decrement_function_end_pos (); + } + } } } } @@ -3114,7 +3250,8 @@ parse_source_element_list (bool is_global, /**< flag, indicating that we parsing (void) is_try_replace_local_vars_with_regs; #endif /* !CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER */ - rewrite_reg_var_decl (); + rewrite_scope_code_flags (scope_code_flags_oc, scope_flags); + rewrite_reg_var_decl (reg_var_decl_oc); dumper_finish_scope (); } /* parse_source_element_list */ diff --git a/jerry-core/parser/js/scopes-tree.cpp b/jerry-core/parser/js/scopes-tree.cpp index 78fc95171..0bc55ac04 100644 --- a/jerry-core/parser/js/scopes-tree.cpp +++ b/jerry-core/parser/js/scopes-tree.cpp @@ -118,6 +118,20 @@ scopes_tree_var_decl (scopes_tree tree, /**< scope, from which variable declarat return *(op_meta *) linked_list_element (tree->var_decls, oc); } /* scopes_tree_var_decl */ +/** + * Remove specified instruction from scopes tree node's instructions list + */ +void +scopes_tree_remove_op_meta (scopes_tree tree, /**< scopes tree node */ + vm_instr_counter_t oc) /**< position of instruction to remove */ +{ + assert_tree (tree); + JERRY_ASSERT (oc < tree->instrs_count); + + linked_list_remove_element (tree->instrs, oc); + tree->instrs_count--; +} /* scopes_tree_remove_op_meta */ + vm_instr_counter_t scopes_tree_count_instructions (scopes_tree t) { @@ -132,6 +146,35 @@ scopes_tree_count_instructions (scopes_tree t) return res; } +/** + * Checks if variable declaration exists in the scope + * + * @return true / false + */ +bool +scopes_tree_variable_declaration_exists (scopes_tree tree, /**< scope */ + lit_cpointer_t lit_id) /**< literal which holds variable's name */ +{ + assert_tree (tree); + + for (vm_instr_counter_t oc = 0u; + oc < linked_list_get_length (tree->var_decls); + oc++) + { + const op_meta* var_decl_om_p = (op_meta *) linked_list_element (tree->var_decls, oc); + + JERRY_ASSERT (var_decl_om_p->op.op_idx == VM_OP_VAR_DECL); + JERRY_ASSERT (var_decl_om_p->op.data.var_decl.variable_name == VM_IDX_REWRITE_LITERAL_UID); + + if (var_decl_om_p->lit_id[0].packed_value == lit_id.packed_value) + { + return true; + } + } + + return false; +} /* scopes_tree_variable_declaration_exists */ + static uint16_t lit_id_hash (void * lit_id) { diff --git a/jerry-core/parser/js/scopes-tree.h b/jerry-core/parser/js/scopes-tree.h index 62ce75f26..e7aaa6330 100644 --- a/jerry-core/parser/js/scopes-tree.h +++ b/jerry-core/parser/js/scopes-tree.h @@ -90,8 +90,10 @@ void scopes_tree_set_op_meta (scopes_tree, vm_instr_counter_t, op_meta); void scopes_tree_set_instrs_num (scopes_tree, vm_instr_counter_t); op_meta scopes_tree_op_meta (scopes_tree, vm_instr_counter_t); op_meta scopes_tree_var_decl (scopes_tree, vm_instr_counter_t); +void scopes_tree_remove_op_meta (scopes_tree tree, vm_instr_counter_t oc); size_t scopes_tree_count_literals_in_blocks (scopes_tree); vm_instr_counter_t scopes_tree_count_instructions (scopes_tree); +bool scopes_tree_variable_declaration_exists (scopes_tree, lit_cpointer_t); vm_instr_t *scopes_tree_raw_data (scopes_tree, uint8_t *, size_t, lit_id_hash_table *); void scopes_tree_set_strict_mode (scopes_tree, bool); void scopes_tree_set_arguments_used (scopes_tree); diff --git a/jerry-core/parser/js/serializer.cpp b/jerry-core/parser/js/serializer.cpp index 2288f9dac..ca1874419 100644 --- a/jerry-core/parser/js/serializer.cpp +++ b/jerry-core/parser/js/serializer.cpp @@ -34,18 +34,6 @@ serializer_get_op_meta (vm_instr_counter_t oc) return scopes_tree_op_meta (current_scope, oc); } -/** - * Get variable declaration of the current scope - * - * @return variable declaration instruction - */ -op_meta -serializer_get_var_decl (vm_instr_counter_t oc) /**< index of variable declaration */ -{ - JERRY_ASSERT (current_scope); - return scopes_tree_var_decl (current_scope, oc); -} /* serializer_get_var_decl */ - /** * Get byte-code instruction from current scope, or specified byte-code array * @@ -231,17 +219,6 @@ serializer_get_current_instr_counter (void) return scopes_tree_instrs_num (current_scope); } -/** - * Get number of variable declarations in the current scope - * - * @return count of variable declarations - */ -vm_instr_counter_t -serializer_get_current_var_decls_counter (void) -{ - return scopes_tree_var_decls_num (current_scope); -} /* serializer_get_current_var_decls_counter */ - vm_instr_counter_t serializer_count_instrs_in_subscopes (void) { diff --git a/jerry-core/parser/js/serializer.h b/jerry-core/parser/js/serializer.h index e79637aef..d7c540084 100644 --- a/jerry-core/parser/js/serializer.h +++ b/jerry-core/parser/js/serializer.h @@ -28,7 +28,6 @@ void serializer_init (); void serializer_set_show_instrs (bool); op_meta serializer_get_op_meta (vm_instr_counter_t); -op_meta serializer_get_var_decl (vm_instr_counter_t); vm_instr_t serializer_get_instr (const bytecode_data_header_t *, vm_instr_counter_t); lit_cpointer_t serializer_get_literal_cp_by_uid (uint8_t, const bytecode_data_header_t *, vm_instr_counter_t); void serializer_set_scope (scopes_tree); @@ -37,7 +36,6 @@ const bytecode_data_header_t *serializer_merge_scopes_into_bytecode (void); void serializer_dump_op_meta (op_meta); void serializer_dump_var_decl (op_meta); vm_instr_counter_t serializer_get_current_instr_counter (void); -vm_instr_counter_t serializer_get_current_var_decls_counter (void); vm_instr_counter_t serializer_count_instrs_in_subscopes (void); void serializer_set_writing_position (vm_instr_counter_t); void serializer_rewrite_op_meta (vm_instr_counter_t, op_meta); diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 377f7edf6..44a6debc0 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -164,8 +164,12 @@ typedef enum : vm_idx_t OPCODE_SCOPE_CODE_FLAGS_STRICT = (1u << 0), /**< code is strict mode code */ OPCODE_SCOPE_CODE_FLAGS_NOT_REF_ARGUMENTS_IDENTIFIER = (1u << 1), /**< code doesn't reference * 'arguments' identifier */ - OPCODE_SCOPE_CODE_FLAGS_NOT_REF_EVAL_IDENTIFIER = (1u << 2) /**< code doesn't reference + OPCODE_SCOPE_CODE_FLAGS_NOT_REF_EVAL_IDENTIFIER = (1u << 2), /**< code doesn't reference * 'eval' identifier */ + OPCODE_SCOPE_CODE_FLAGS_ARGUMENTS_ON_REGISTERS = (1u << 3), /**< function's arguments are moved to registers, + * so should be initialized in vm registers, + * and not in lexical environment */ + OPCODE_SCOPE_CODE_FLAGS_NO_LEX_ENV = (1u << 4) /**< no lex. env. is necessary for the function */ } opcode_scope_code_flags_t; /** diff --git a/jerry-core/vm/pretty-printer.cpp b/jerry-core/vm/pretty-printer.cpp index 254f3620e..04c28dc85 100644 --- a/jerry-core/vm/pretty-printer.cpp +++ b/jerry-core/vm/pretty-printer.cpp @@ -221,7 +221,7 @@ pp_op_meta (const bytecode_data_header_t *bytecode_data_p, PP_OP (VM_OP_PRE_INCR, "%s = ++%s;"); PP_OP (VM_OP_PRE_DECR, "%s = --%s;"); PP_OP (VM_OP_THROW_VALUE, "throw %s;"); - PP_OP (VM_OP_REG_VAR_DECL, "%d tmp regs, %d local variable regs"); + PP_OP (VM_OP_REG_VAR_DECL, "%d tmp regs, %d local variable regs, %d argument variable regs"); PP_OP (VM_OP_VAR_DECL, "var %s;"); PP_OP (VM_OP_RETVAL, "return %s;"); PP_OP (VM_OP_RET, "ret;"); @@ -572,6 +572,16 @@ pp_op_meta (const bytecode_data_header_t *bytecode_data_p, printf ("[no 'eval'] "); scope_flags &= (vm_idx_t) ~(OPCODE_SCOPE_CODE_FLAGS_NOT_REF_EVAL_IDENTIFIER); } + if (scope_flags & OPCODE_SCOPE_CODE_FLAGS_ARGUMENTS_ON_REGISTERS) + { + printf ("[arguments are placed on registers] "); + scope_flags &= (vm_idx_t) ~(OPCODE_SCOPE_CODE_FLAGS_ARGUMENTS_ON_REGISTERS); + } + if (scope_flags & OPCODE_SCOPE_CODE_FLAGS_NO_LEX_ENV) + { + printf ("[no lexical environment should be created for the scope] "); + scope_flags &= (vm_idx_t) ~(OPCODE_SCOPE_CODE_FLAGS_NO_LEX_ENV); + } JERRY_ASSERT (scope_flags == 0); } diff --git a/jerry-core/vm/vm-opcodes.inc.h b/jerry-core/vm/vm-opcodes.inc.h index 611d2bf17..2f3e712ad 100644 --- a/jerry-core/vm/vm-opcodes.inc.h +++ b/jerry-core/vm/vm-opcodes.inc.h @@ -292,9 +292,10 @@ VM_OP_3 (is_false_jmp_down, IS_FALSE_JMP_DOWN, VM_OP_1 (var_decl, VAR_DECL, variable_name, VM_OP_ARG_TYPE_STRING) -VM_OP_2 (reg_var_decl, REG_VAR_DECL, +VM_OP_3 (reg_var_decl, REG_VAR_DECL, tmp_regs_num, VM_OP_ARG_TYPE_INTEGER_CONST, - local_var_regs_num, VM_OP_ARG_TYPE_INTEGER_CONST) + local_var_regs_num, VM_OP_ARG_TYPE_INTEGER_CONST, + arg_regs_num, VM_OP_ARG_TYPE_INTEGER_CONST) VM_OP_3 (meta, META, type, VM_OP_ARG_TYPE_INTEGER_CONST, diff --git a/jerry-core/vm/vm-stack.cpp b/jerry-core/vm/vm-stack.cpp index f02697034..ac1cb46f5 100644 --- a/jerry-core/vm/vm-stack.cpp +++ b/jerry-core/vm/vm-stack.cpp @@ -77,8 +77,13 @@ void vm_stack_add_frame (vm_stack_frame_t *frame_p, /**< frame to initialize */ ecma_value_t *regs_p, /**< array of register variables' values */ uint32_t regs_num, /**< total number of register variables */ - uint32_t local_vars_regs_num) /**< number of register variables, + uint32_t local_vars_regs_num, /**< number of register variables, * used for local variables */ + uint32_t arg_regs_num, /**< number of register variables, + * used for arguments */ + ecma_collection_header_t *arguments_p) /**< collection of arguments + * (for case, their values + * are moved to registers) */ { frame_p->prev_frame_p = vm_stack_top_frame_p; vm_stack_top_frame_p = frame_p; @@ -91,17 +96,30 @@ vm_stack_add_frame (vm_stack_frame_t *frame_p, /**< frame to initialize */ JERRY_ASSERT (regs_num >= VM_SPECIAL_REGS_NUMBER); - for (uint32_t i = 0; i < regs_num - local_vars_regs_num; i++) + for (uint32_t i = 0; i < regs_num - local_vars_regs_num - arg_regs_num; i++) { regs_p[i] = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY); } - for (uint32_t i = regs_num - local_vars_regs_num; + for (uint32_t i = regs_num - local_vars_regs_num - arg_regs_num; i < regs_num; i++) { regs_p[i] = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED); } + + if (arg_regs_num != 0) + { + ecma_collection_iterator_t args_iterator; + ecma_collection_iterator_init (&args_iterator, arguments_p); + + for (uint32_t i = regs_num - arg_regs_num; + i < regs_num && ecma_collection_iterator_next (&args_iterator); + i++) + { + regs_p[i] = ecma_copy_value (*args_iterator.current_value_p, false); + } + } } /* vm_stack_add_frame */ /** diff --git a/jerry-core/vm/vm-stack.h b/jerry-core/vm/vm-stack.h index ca6ef1409..609b60760 100644 --- a/jerry-core/vm/vm-stack.h +++ b/jerry-core/vm/vm-stack.h @@ -60,7 +60,7 @@ extern void vm_stack_finalize (void); extern vm_stack_frame_t * vm_stack_get_top_frame (void); extern void -vm_stack_add_frame (vm_stack_frame_t *, ecma_value_t *, uint32_t, uint32_t); +vm_stack_add_frame (vm_stack_frame_t *, ecma_value_t *, uint32_t, uint32_t, uint32_t, ecma_collection_header_t *); extern void vm_stack_free_frame (vm_stack_frame_t *); extern ecma_value_t vm_stack_frame_get_reg_value (vm_stack_frame_t *, uint32_t); extern void vm_stack_frame_set_reg_value (vm_stack_frame_t *, uint32_t, ecma_value_t); diff --git a/jerry-core/vm/vm.cpp b/jerry-core/vm/vm.cpp index 85fa92798..b981a1319 100644 --- a/jerry-core/vm/vm.cpp +++ b/jerry-core/vm/vm.cpp @@ -411,7 +411,8 @@ vm_run_global (void) ecma_make_object_value (glob_obj_p), lex_env_p, is_strict, - false); + false, + NULL); jerry_completion_code_t ret_code; @@ -479,7 +480,8 @@ vm_run_eval (const bytecode_data_header_t *bytecode_data_p, /**< byte-code data this_binding, lex_env_p, is_strict, - true); + true, + NULL); if (ecma_is_completion_value_return (completion)) { @@ -595,7 +597,13 @@ vm_run_from_pos (const bytecode_data_header_t *header_p, /**< byte-code data hea ecma_value_t this_binding_value, /**< value of 'ThisBinding' */ ecma_object_t *lex_env_p, /**< lexical environment to use */ bool is_strict, /**< is the code is strict mode code (ECMA-262 v5, 10.1.1) */ - bool is_eval_code) /**< is the code is eval code (ECMA-262 v5, 10.1) */ + bool is_eval_code, /**< is the code is eval code (ECMA-262 v5, 10.1) */ + ecma_collection_header_t *arg_collection_p) /**< + * - collection of function call arguments, + * if arguments for the called function + * are placed on registers; + * - NULL - otherwise. + */ { ecma_completion_value_t completion; @@ -605,8 +613,9 @@ vm_run_from_pos (const bytecode_data_header_t *header_p, /**< byte-code data hea const uint32_t tmp_regs_num = curr->data.reg_var_decl.tmp_regs_num; const uint32_t local_var_regs_num = curr->data.reg_var_decl.local_var_regs_num; + const uint32_t arg_regs_num = curr->data.reg_var_decl.arg_regs_num; - uint32_t regs_num = VM_SPECIAL_REGS_NUMBER + tmp_regs_num + local_var_regs_num; + uint32_t regs_num = VM_SPECIAL_REGS_NUMBER + tmp_regs_num + local_var_regs_num + arg_regs_num; MEM_DEFINE_LOCAL_ARRAY (regs, regs_num, ecma_value_t); @@ -619,7 +628,7 @@ vm_run_from_pos (const bytecode_data_header_t *header_p, /**< byte-code data hea frame_ctx.is_call_in_direct_eval_form = false; frame_ctx.tmp_num_p = ecma_alloc_number (); - vm_stack_add_frame (&frame_ctx.stack_frame, regs, regs_num, local_var_regs_num); + vm_stack_add_frame (&frame_ctx.stack_frame, regs, regs_num, local_var_regs_num, arg_regs_num, arg_collection_p); vm_stack_frame_set_reg_value (&frame_ctx.stack_frame, VM_REG_SPECIAL_THIS_BINDING, ecma_copy_value (this_binding_value, false)); @@ -676,6 +685,22 @@ vm_get_scope_flags (const bytecode_data_header_t *bytecode_header_p, /**< byte-c return (opcode_scope_code_flags_t) flags_instr.data.meta.data_1; } /* vm_get_scope_flags */ +/** + * Get arguments number, encoded in specified reg_var_decl instruction + * + * @return value of "arguments number" reg_var_decl's parameter + */ +uint8_t +vm_get_scope_args_num (const bytecode_data_header_t *bytecode_header_p, /**< byte-code data */ + vm_instr_counter_t reg_var_decl_oc) /**< position of reg_var_decl instruction */ +{ + const vm_instr_t *instrs_p = bytecode_header_p->instrs_p; + const vm_instr_t *reg_var_decl_instr_p = &instrs_p[reg_var_decl_oc]; + JERRY_ASSERT (reg_var_decl_instr_p->op_idx == VM_OP_REG_VAR_DECL); + + return reg_var_decl_instr_p->data.reg_var_decl.arg_regs_num; +} /* vm_get_scope_args_num */ + /** * Check whether currently executed code is strict mode code * diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 8610ecbfc..42c8f88f1 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -27,10 +27,11 @@ extern ecma_completion_value_t vm_run_eval (const bytecode_data_header_t *, bool extern ecma_completion_value_t vm_loop (vm_frame_ctx_t *, vm_run_scope_t *); extern ecma_completion_value_t vm_run_from_pos (const bytecode_data_header_t *, vm_instr_counter_t, - ecma_value_t, ecma_object_t *, bool, bool); + ecma_value_t, ecma_object_t *, bool, bool, ecma_collection_header_t *); extern vm_instr_t vm_get_instr (const vm_instr_t *, vm_instr_counter_t); extern opcode_scope_code_flags_t vm_get_scope_flags (const bytecode_data_header_t *, vm_instr_counter_t); +extern uint8_t vm_get_scope_args_num (const bytecode_data_header_t *, vm_instr_counter_t); extern bool vm_is_strict_mode (void); extern bool vm_is_direct_eval_form_call (void); diff --git a/tests/unit/test-parser.cpp b/tests/unit/test-parser.cpp index 8e4f0c6dc..5ab05552b 100644 --- a/tests/unit/test-parser.cpp +++ b/tests/unit/test-parser.cpp @@ -121,7 +121,7 @@ main (int __attr_unused___ argc, OPCODE_SCOPE_CODE_FLAGS_NOT_REF_ARGUMENTS_IDENTIFIER | OPCODE_SCOPE_CODE_FLAGS_NOT_REF_EVAL_IDENTIFIER, VM_IDX_EMPTY), - getop_reg_var_decl (1u, 0u), + getop_reg_var_decl (1u, 0u, 0u), getop_var_decl (0), // var a; getop_assignment (VM_REG_GENERAL_FIRST, 1, 1), // $tmp0 = 1; getop_assignment (0, 6, VM_REG_GENERAL_FIRST), // a = $tmp0;