From 7e7bdc2aac084aebff50e999fd39b2476d8d1dee Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Mon, 15 Dec 2014 22:48:27 +0300 Subject: [PATCH] Rework literal indexes hash table to simple array in order to improve performance --- src/libintstructs/hash-table.h | 1 - src/libjsparser/scopes-tree.c | 261 ++++++++++++++++++++++++++- src/libjsparser/scopes-tree.h | 4 +- src/liboptimizer/bytecode-data.h | 5 +- src/liboptimizer/deserializer.c | 25 +-- src/liboptimizer/lit-id-hash-table.c | 66 +++++++ src/liboptimizer/lit-id-hash-table.h | 36 ++++ src/liboptimizer/serializer.c | 13 +- 8 files changed, 364 insertions(+), 47 deletions(-) create mode 100644 src/liboptimizer/lit-id-hash-table.c create mode 100644 src/liboptimizer/lit-id-hash-table.h diff --git a/src/libintstructs/hash-table.h b/src/libintstructs/hash-table.h index c479f9d77..2e47bf9a4 100644 --- a/src/libintstructs/hash-table.h +++ b/src/libintstructs/hash-table.h @@ -25,7 +25,6 @@ #ifndef HASH_TABLE_H #define HASH_TABLE_H -#include "linked-list.h" #include "mem-heap.h" typedef void* hash_table; diff --git a/src/libjsparser/scopes-tree.c b/src/libjsparser/scopes-tree.c index c62b0fb68..cb1b284f1 100644 --- a/src/libjsparser/scopes-tree.c +++ b/src/libjsparser/scopes-tree.c @@ -23,7 +23,7 @@ #define HASH_SIZE 128 static hash_table lit_id_to_uid = null_hash; -static opcode_counter_t global_oc = 0; +static opcode_counter_t global_oc; static idx_t next_uid; static void @@ -150,7 +150,7 @@ is_possible_literal (uint16_t mask, uint8_t index) } static void -change_uid (op_meta *om, hash_table lit_ids, uint16_t mask) +change_uid (op_meta *om, lit_id_hash_table *lit_ids, uint16_t mask) { for (uint8_t i = 0; i < 3; i++) { @@ -164,8 +164,7 @@ change_uid (op_meta *om, hash_table lit_ids, uint16_t mask) if (uid == NULL) { hash_table_insert (lit_id_to_uid, &lit_id, &next_uid); - lit_id_table_key key = create_lit_id_table_key (next_uid, global_oc); - hash_table_insert (lit_ids, &key, &lit_id); + lit_id_hash_table_insert (lit_ids, next_uid, global_oc, lit_id); uid = hash_table_lookup (lit_id_to_uid, &lit_id); JERRY_ASSERT (uid != NULL); JERRY_ASSERT (*uid == next_uid); @@ -185,6 +184,39 @@ change_uid (op_meta *om, hash_table lit_ids, uint16_t mask) } } +static void +insert_uids_to_lit_id_map (op_meta *om, uint16_t mask) +{ + for (uint8_t i = 0; i < 3; i++) + { + if (is_possible_literal (mask, i)) + { + if (get_uid (om, i) == LITERAL_TO_REWRITE) + { + JERRY_ASSERT (om->lit_id[i] != NOT_A_LITERAL); + literal_index_t lit_id = om->lit_id[i]; + idx_t *uid = hash_table_lookup (lit_id_to_uid, &lit_id); + if (uid == NULL) + { + hash_table_insert (lit_id_to_uid, &lit_id, &next_uid); + uid = hash_table_lookup (lit_id_to_uid, &lit_id); + JERRY_ASSERT (uid != NULL); + JERRY_ASSERT (*uid == next_uid); + next_uid++; + } + } + else + { + JERRY_ASSERT (om->lit_id[i] == NOT_A_LITERAL); + } + } + else + { + JERRY_ASSERT (om->lit_id[i] == NOT_A_LITERAL); + } + } +} + static op_meta * extract_op_meta (scopes_tree tree, opcode_counter_t opc_index) { @@ -192,7 +224,7 @@ extract_op_meta (scopes_tree tree, opcode_counter_t opc_index) } static opcode_t -generate_opcode (scopes_tree tree, opcode_counter_t opc_index, hash_table lit_ids) +generate_opcode (scopes_tree tree, opcode_counter_t opc_index, lit_id_hash_table *lit_ids) { start_new_block_if_necessary (); op_meta *om = extract_op_meta (tree, opc_index); @@ -335,8 +367,206 @@ generate_opcode (scopes_tree tree, opcode_counter_t opc_index, hash_table lit_id return om->op; } +static idx_t +count_new_literals_in_opcode (scopes_tree tree, opcode_counter_t opc_index) +{ + start_new_block_if_necessary (); + idx_t current_uid = next_uid; + op_meta *om = extract_op_meta (tree, opc_index); + switch (om->op.op_idx) + { + case OPCODE (prop_getter): + case OPCODE (prop_setter): + case OPCODE (delete_prop): + case OPCODE (b_shift_left): + case OPCODE (b_shift_right): + case OPCODE (b_shift_uright): + case OPCODE (b_and): + case OPCODE (b_or): + case OPCODE (b_xor): + case OPCODE (equal_value): + case OPCODE (not_equal_value): + case OPCODE (equal_value_type): + case OPCODE (not_equal_value_type): + case OPCODE (less_than): + case OPCODE (greater_than): + case OPCODE (less_or_equal_than): + case OPCODE (greater_or_equal_than): + case OPCODE (instanceof): + case OPCODE (in): + case OPCODE (addition): + case OPCODE (substraction): + case OPCODE (division): + case OPCODE (multiplication): + case OPCODE (remainder): + { + insert_uids_to_lit_id_map (om, 0x111); + break; + } + case OPCODE (call_n): + case OPCODE (native_call): + case OPCODE (construct_n): + case OPCODE (func_expr_n): + case OPCODE (delete_var): + case OPCODE (typeof): + case OPCODE (b_not): + case OPCODE (logical_not): + case OPCODE (post_incr): + case OPCODE (post_decr): + case OPCODE (pre_incr): + case OPCODE (pre_decr): + case OPCODE (unary_plus): + case OPCODE (unary_minus): + { + insert_uids_to_lit_id_map (om, 0x110); + break; + } + case OPCODE (assignment): + { + switch (om->op.data.assignment.type_value_right) + { + case OPCODE_ARG_TYPE_SIMPLE: + case OPCODE_ARG_TYPE_SMALLINT: + case OPCODE_ARG_TYPE_SMALLINT_NEGATE: + { + insert_uids_to_lit_id_map (om, 0x100); + break; + } + case OPCODE_ARG_TYPE_NUMBER: + case OPCODE_ARG_TYPE_NUMBER_NEGATE: + case OPCODE_ARG_TYPE_STRING: + case OPCODE_ARG_TYPE_VARIABLE: + { + insert_uids_to_lit_id_map (om, 0x101); + break; + } + } + break; + } + case OPCODE (func_decl_n): + case OPCODE (array_decl): + case OPCODE (obj_decl): + case OPCODE (this): + case OPCODE (with): + case OPCODE (throw): + case OPCODE (is_true_jmp_up): + case OPCODE (is_true_jmp_down): + case OPCODE (is_false_jmp_up): + case OPCODE (is_false_jmp_down): + case OPCODE (var_decl): + case OPCODE (retval): + { + insert_uids_to_lit_id_map (om, 0x100); + break; + } + case OPCODE (exitval): + case OPCODE (ret): + case OPCODE (try): + case OPCODE (jmp_up): + case OPCODE (jmp_down): + case OPCODE (nop): + case OPCODE (reg_var_decl): + { + insert_uids_to_lit_id_map (om, 0x000); + break; + } + case OPCODE (meta): + { + switch (om->op.data.meta.type) + { + case OPCODE_META_TYPE_VARG_PROP_DATA: + case OPCODE_META_TYPE_VARG_PROP_GETTER: + case OPCODE_META_TYPE_VARG_PROP_SETTER: + { + insert_uids_to_lit_id_map (om, 0x011); + break; + } + case OPCODE_META_TYPE_THIS_ARG: + case OPCODE_META_TYPE_VARG: + case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: + { + insert_uids_to_lit_id_map (om, 0x010); + break; + } + case OPCODE_META_TYPE_UNDEFINED: + case OPCODE_META_TYPE_END_WITH: + case OPCODE_META_TYPE_FUNCTION_END: + case OPCODE_META_TYPE_CATCH: + case OPCODE_META_TYPE_FINALLY: + case OPCODE_META_TYPE_END_TRY_CATCH_FINALLY: + case OPCODE_META_TYPE_STRICT_CODE: + { + insert_uids_to_lit_id_map (om, 0x000); + break; + } + } + break; + } + } + return (idx_t) (next_uid - current_uid); +} + +/* Before filling literal indexes 'hash' table we shall initiate it with number of neccesary literal indexes. + Since bytecode is divided into blocks and id of the block is a part of hash key, we shall divide bytecode + into blocks and count unique literal indexes used in each block. */ +size_t +scopes_tree_count_literals_in_blocks (scopes_tree tree) +{ + assert_tree (tree); + size_t result = 0; + if (lit_id_to_uid != null_hash) + { + hash_table_free (lit_id_to_uid); + lit_id_to_uid = null_hash; + } + next_uid = 0; + global_oc = 0; + + assert_tree (tree); + opcode_counter_t opc_index; + bool header = true; + for (opc_index = 0; opc_index < tree->opcodes_num; opc_index++) + { + op_meta *om = extract_op_meta (tree, opc_index); + if (om->op.op_idx != OPCODE (var_decl) + && om->op.op_idx != OPCODE (meta) && !header) + { + break; + } + if (om->op.op_idx == OPCODE (reg_var_decl)) + { + header = false; + } + result += count_new_literals_in_opcode (tree, opc_index); + } + for (uint8_t child_id = 0; child_id < tree->t.children_num; child_id++) + { + result += scopes_tree_count_literals_in_blocks (*(scopes_tree *) linked_list_element (tree->t.children, child_id)); + } + for (; opc_index < tree->opcodes_num; opc_index++) + { + result += count_new_literals_in_opcode (tree, opc_index); + } + + return result; +} + +/* This function performs functions hoisting. + + Each scope consists of four parts: + 1) Header with 'use strict' marker and reg_var_decl opcode + 2) Variable declarations, dumped by the preparser + 3) Function declarations + 4) Computational code + + Header and var_decls are dumped first, + then we shall recursively dump function declaration, + and finally, other opcodes. + + For each opcodes block (size of block is defined in bytecode-data.h) + literal indexes 'hash' table is filled. */ static void -merge_subscopes (scopes_tree tree, opcode_t *data, hash_table lit_ids) +merge_subscopes (scopes_tree tree, opcode_t *data, lit_id_hash_table *lit_ids) { assert_tree (tree); JERRY_ASSERT (data); @@ -369,12 +599,25 @@ merge_subscopes (scopes_tree tree, opcode_t *data, hash_table lit_ids) } } +/* Postparser. + Init literal indexes 'hash' table. + Reorder function declarations. + Rewrite opcodes' temporary uids with their keys in literal indexes 'hash' table. */ opcode_t * -scopes_tree_raw_data (scopes_tree tree, hash_table lit_ids) +scopes_tree_raw_data (scopes_tree tree, lit_id_hash_table *lit_ids) { + JERRY_ASSERT (lit_ids); assert_tree (tree); - opcode_counter_t opcodes_count = scopes_tree_count_opcodes (tree); - size_t size = ((size_t) (opcodes_count + 1) * sizeof (opcode_t)); // +1 for valgrind + if (lit_id_to_uid != null_hash) + { + hash_table_free (lit_id_to_uid); + lit_id_to_uid = null_hash; + } + next_uid = 0; + global_oc = 0; + + /* Dump bytecode and fill literal indexes 'hash' table. */ + size_t size = ((size_t) (scopes_tree_count_opcodes (tree) + 1) * sizeof (opcode_t)); // +1 for valgrind opcode_t *opcodes = (opcode_t *) mem_heap_alloc_block (size, MEM_HEAP_ALLOC_LONG_TERM); __memset (opcodes, 0, size); merge_subscopes (tree, opcodes, lit_ids); diff --git a/src/libjsparser/scopes-tree.h b/src/libjsparser/scopes-tree.h index 250111f95..e5d32cc75 100644 --- a/src/libjsparser/scopes-tree.h +++ b/src/libjsparser/scopes-tree.h @@ -22,6 +22,7 @@ #include "ecma-globals.h" #include "hash-table.h" #include "opcodes.h" +#include "lit-id-hash-table.h" #define NOT_A_LITERAL (INVALID_LITERAL - 1) @@ -50,8 +51,9 @@ void scopes_tree_add_op_meta (scopes_tree, op_meta); void scopes_tree_set_op_meta (scopes_tree, opcode_counter_t, op_meta); void scopes_tree_set_opcodes_num (scopes_tree, opcode_counter_t); op_meta scopes_tree_op_meta (scopes_tree, opcode_counter_t); +size_t scopes_tree_count_literals_in_blocks (scopes_tree); opcode_counter_t scopes_tree_count_opcodes (scopes_tree); -opcode_t *scopes_tree_raw_data (scopes_tree, hash_table); +opcode_t *scopes_tree_raw_data (scopes_tree, lit_id_hash_table *); void scopes_tree_set_strict_mode (scopes_tree, bool); bool scopes_tree_strict_mode (scopes_tree); diff --git a/src/liboptimizer/bytecode-data.h b/src/liboptimizer/bytecode-data.h index 2ceb5d0fd..b7d14331f 100644 --- a/src/liboptimizer/bytecode-data.h +++ b/src/liboptimizer/bytecode-data.h @@ -21,6 +21,7 @@ #include "jerry-libc.h" #include "literal.h" #include "scopes-tree.h" +#include "lit-id-hash-table.h" #define BLOCK_SIZE 64 @@ -36,14 +37,12 @@ struct { const literal *literals; const opcode_t *opcodes; + lit_id_hash_table *lit_id_hash; literal_index_t literals_count; opcode_counter_t opcodes_count; - hash_table lit_id_hash; } bytecode_data; -lit_id_table_key create_lit_id_table_key (idx_t, opcode_counter_t); - scopes_tree current_scope; #endif // BYTECODE_DATA_H diff --git a/src/liboptimizer/deserializer.c b/src/liboptimizer/deserializer.c index 83f1d121a..eb1e3cb76 100644 --- a/src/liboptimizer/deserializer.c +++ b/src/liboptimizer/deserializer.c @@ -19,17 +19,6 @@ const ecma_char_t *strings_buffer; -lit_id_table_key -create_lit_id_table_key (idx_t id, opcode_counter_t oc) -{ - return (lit_id_table_key) - { - .uid = id, - .oc = oc / BLOCK_SIZE, - .reserved = 0 - }; -} - void deserializer_set_strings_buffer (const ecma_char_t *s) { @@ -47,19 +36,11 @@ deserialize_literal_by_id (literal_index_t id) literal_index_t deserialize_lit_id_by_uid (uint8_t id, opcode_counter_t oc) { - // __printf ("uid: %d, oc: %d\n", id, oc); - // if (id == 2 && oc == 64) - // { - // __printf ("HIT!\n"); - // } if (bytecode_data.lit_id_hash == null_hash) { return INVALID_LITERAL; } - lit_id_table_key key = create_lit_id_table_key (id, oc); - void *res = hash_table_lookup (bytecode_data.lit_id_hash, &key); - JERRY_ASSERT (res != NULL); - return *(literal_index_t *) res; + return lit_id_hash_table_lookup (bytecode_data.lit_id_hash, id, oc); } const void * @@ -108,9 +89,9 @@ deserializer_free (void) { mem_heap_free_block ((uint8_t *) strings_buffer); } - if (bytecode_data.lit_id_hash != null_hash) + if (bytecode_data.lit_id_hash != NULL) { - hash_table_free (bytecode_data.lit_id_hash); + lit_id_hash_table_free (bytecode_data.lit_id_hash); } if (bytecode_data.literals != NULL) { diff --git a/src/liboptimizer/lit-id-hash-table.c b/src/liboptimizer/lit-id-hash-table.c new file mode 100644 index 000000000..ac71d2619 --- /dev/null +++ b/src/liboptimizer/lit-id-hash-table.c @@ -0,0 +1,66 @@ +/* Copyright 2014 Samsung Electronics Co., Ltd. + * + * 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 "lit-id-hash-table.h" +#include "mem-heap.h" +#include "jerry-libc.h" +#include "bytecode-data.h" + +lit_id_hash_table * +lit_id_hash_table_init (size_t buckets_count, size_t blocks_count) +{ + size_t size = mem_heap_recommend_allocation_size (sizeof (lit_id_hash_table)); + lit_id_hash_table *table = (lit_id_hash_table *) mem_heap_alloc_block (size, MEM_HEAP_ALLOC_LONG_TERM); + __memset (table, 0, size); + size = mem_heap_recommend_allocation_size (sizeof (literal_index_t) * buckets_count); + table->raw_buckets = (literal_index_t *) mem_heap_alloc_block (size, MEM_HEAP_ALLOC_LONG_TERM); + __memset (table->raw_buckets, 0, size); + size = mem_heap_recommend_allocation_size (sizeof (literal_index_t *) * blocks_count); + table->buckets = (literal_index_t **) mem_heap_alloc_block (size, MEM_HEAP_ALLOC_LONG_TERM); + __memset (table->buckets, 0, size); + table->current_bucket_pos = 0; + return table; +} + +void +lit_id_hash_table_free (lit_id_hash_table *table) +{ + JERRY_ASSERT (table); + mem_heap_free_block ((uint8_t *) table->raw_buckets); + mem_heap_free_block ((uint8_t *) table->buckets); + mem_heap_free_block ((uint8_t *) table); +} + +void +lit_id_hash_table_insert (lit_id_hash_table *table, idx_t uid, opcode_counter_t oc, literal_index_t lit_id) +{ + JERRY_ASSERT (table); + size_t block_id = oc / BLOCK_SIZE; + if (table->buckets[block_id] == NULL) + { + table->buckets[block_id] = table->raw_buckets + table->current_bucket_pos; + } + table->buckets[block_id][uid] = lit_id; + table->current_bucket_pos++; +} + +literal_index_t +lit_id_hash_table_lookup (lit_id_hash_table *table, idx_t uid, opcode_counter_t oc) +{ + JERRY_ASSERT (table); + size_t block_id = oc / BLOCK_SIZE; + JERRY_ASSERT (table->buckets[block_id]); + return table->buckets[block_id][uid]; +} diff --git a/src/liboptimizer/lit-id-hash-table.h b/src/liboptimizer/lit-id-hash-table.h new file mode 100644 index 000000000..53a99a729 --- /dev/null +++ b/src/liboptimizer/lit-id-hash-table.h @@ -0,0 +1,36 @@ +/* Copyright 2014 Samsung Electronics Co., Ltd. + * + * 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 LIT_ID_HASH_TABLE +#define LIT_ID_HASH_TABLE + +#include "globals.h" +#include "ecma-globals.h" +#include "opcodes.h" + +typedef struct +{ + size_t current_bucket_pos; + literal_index_t *raw_buckets; + literal_index_t **buckets; +} +lit_id_hash_table; + +lit_id_hash_table *lit_id_hash_table_init (size_t, size_t); +void lit_id_hash_table_free (lit_id_hash_table *); +void lit_id_hash_table_insert (lit_id_hash_table *, idx_t, opcode_counter_t, literal_index_t); +literal_index_t lit_id_hash_table_lookup (lit_id_hash_table *, idx_t, opcode_counter_t); + +#endif /* LIT_ID_HASH_TABLE */ diff --git a/src/liboptimizer/serializer.c b/src/liboptimizer/serializer.c index dd534bf6c..19953d64a 100644 --- a/src/liboptimizer/serializer.c +++ b/src/liboptimizer/serializer.c @@ -29,22 +29,13 @@ serializer_set_scope (scopes_tree new_scope) current_scope = new_scope; } -static uint16_t -hash_function (void *raw_key) -{ - lit_id_table_key *key = (lit_id_table_key *) raw_key; - JERRY_ASSERT (bytecode_data.opcodes_count > 0); - return (uint16_t) (key->oc + key->uid) & ((1u << CONFIG_LITERAL_HASH_TABLE_KEY_BITS) - 1); -} - void serializer_merge_scopes_into_bytecode (void) { JERRY_ASSERT (bytecode_data.lit_id_hash == null_hash); bytecode_data.opcodes_count = scopes_tree_count_opcodes (current_scope); - bytecode_data.lit_id_hash = hash_table_init (sizeof (lit_id_table_key), sizeof (literal_index_t), - 1u << CONFIG_LITERAL_HASH_TABLE_KEY_BITS, hash_function, - MEM_HEAP_ALLOC_LONG_TERM); + bytecode_data.lit_id_hash = lit_id_hash_table_init (scopes_tree_count_literals_in_blocks (current_scope), + bytecode_data.opcodes_count); bytecode_data.opcodes = scopes_tree_raw_data (current_scope, bytecode_data.lit_id_hash); }