Rework literal indexes hash table to simple array in order to improve performance

This commit is contained in:
Ilmir Usmanov 2014-12-15 22:48:27 +03:00
parent 864bd3de06
commit 7e7bdc2aac
8 changed files with 364 additions and 47 deletions

View File

@ -25,7 +25,6 @@
#ifndef HASH_TABLE_H
#define HASH_TABLE_H
#include "linked-list.h"
#include "mem-heap.h"
typedef void* hash_table;

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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)
{

View File

@ -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];
}

View File

@ -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 */

View File

@ -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);
}