Skip to content

Commit fb8c540

Browse files
committed
literal storage using hashmap
JerryScript-DCO-1.0-Signed-off-by: Ronan Jezequel [email protected]
1 parent cefd391 commit fb8c540

File tree

10 files changed

+695
-27
lines changed

10 files changed

+695
-27
lines changed

jerry-core/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ set(JERRY_SYSTEM_ALLOCATOR OFF CACHE BOOL "Enable system all
4040
set(JERRY_VALGRIND OFF CACHE BOOL "Enable Valgrind support?")
4141
set(JERRY_VM_HALT OFF CACHE BOOL "Enable VM execution stop callback?")
4242
set(JERRY_VM_THROW OFF CACHE BOOL "Enable VM throw callback?")
43+
set(JERRY_LIT_HASHMAP OFF CACHE BOOL "Enable literal hashmap storage?")
4344
set(JERRY_GLOBAL_HEAP_SIZE "(512)" CACHE STRING "Size of memory heap, in kilobytes")
4445
set(JERRY_GC_LIMIT "(0)" CACHE STRING "Heap usage limit to trigger garbage collection")
4546
set(JERRY_STACK_LIMIT "(0)" CACHE STRING "Maximum stack usage size, in kilobytes")
@@ -304,6 +305,7 @@ set(SOURCE_CORE_FILES
304305
lit/lit-char-helpers.c
305306
lit/lit-magic-strings.c
306307
lit/lit-strings.c
308+
lit/lit-hashmap.c
307309
parser/js/byte-code.c
308310
parser/js/common.c
309311
parser/js/js-lexer.c

jerry-core/config.h

+7
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,13 @@
466466
* Advanced section configurations.
467467
*/
468468

469+
/**
470+
* The JERRY_LIT_HASHMAP uses an hashmap for faster literal storage.
471+
*/
472+
#ifndef JERRY_LIT_HASHMAP
473+
#define JERRY_LIT_HASHMAP 0
474+
#endif /* !defined (JERRY_LIT_HASHMAP) */
475+
469476
/**
470477
* Allow configuring attributes on a few constant data inside the engine.
471478
*

jerry-core/ecma/base/ecma-literal-storage.c

+106-27
Original file line numberDiff line numberDiff line change
@@ -153,23 +153,68 @@ ecma_finalize_lit_storage (void)
153153
} /* ecma_finalize_lit_storage */
154154

155155
/**
156-
* Find or create a literal string.
156+
* Create a new literal string slot "pool".
157157
*
158-
* @return ecma_string_t compressed pointer
158+
* @return jmem_cpointer_t slot pointer
159159
*/
160-
ecma_value_t
161-
ecma_find_or_create_literal_string (const lit_utf8_byte_t *chars_p, /**< string to be searched */
162-
lit_utf8_size_t size, /**< size of the string */
163-
bool is_ascii) /**< encode of the string */
160+
161+
static jmem_cpointer_t *
162+
ecma_allocate_new_string_slot (void)
164163
{
165-
ecma_string_t *string_p =
166-
(is_ascii ? ecma_new_ecma_string_from_ascii (chars_p, size) : ecma_new_ecma_string_from_utf8 (chars_p, size));
164+
ecma_lit_storage_item_t *new_item_p;
165+
new_item_p = (ecma_lit_storage_item_t *) jmem_pools_alloc (sizeof (ecma_lit_storage_item_t));
167166

168-
if (ECMA_IS_DIRECT_STRING (string_p))
167+
for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++)
169168
{
170-
return ecma_make_string_value (string_p);
169+
new_item_p->values[i] = JMEM_CP_NULL;
170+
}
171+
172+
new_item_p->next_cp = JERRY_CONTEXT (string_list_first_cp);
173+
JMEM_CP_SET_NON_NULL_POINTER (JERRY_CONTEXT (string_list_first_cp), new_item_p);
174+
175+
return new_item_p->values + 0;
176+
} /* ecma_allocate_new_string_slot */
177+
178+
#if JERRY_LIT_HASHMAP
179+
/**
180+
* Find an empty a literal string slot.
181+
*
182+
* @return jmem_cpointer_t slot pointer
183+
*/
184+
185+
static jmem_cpointer_t *
186+
ecma_find_empty_literal_string_slot (void)
187+
{
188+
jmem_cpointer_t string_list_cp = JERRY_CONTEXT (string_list_first_cp);
189+
190+
while (string_list_cp != JMEM_CP_NULL)
191+
{
192+
ecma_lit_storage_item_t *string_list_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_lit_storage_item_t, string_list_cp);
193+
194+
for (int i = 0; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++)
195+
{
196+
if (string_list_p->values[i] == JMEM_CP_NULL)
197+
{
198+
return string_list_p->values + i;
199+
}
200+
}
201+
string_list_cp = string_list_p->next_cp;
171202
}
172203

204+
return ecma_allocate_new_string_slot ();
205+
} /* ecma_find_empty_literal_string_slot */
206+
#endif /* JERRY_LIT_HASHMAP */
207+
208+
/**
209+
* Find an empty or similar a literal string slot.
210+
*
211+
* @return jmem_cpointer_t slot pointer
212+
*/
213+
214+
#if !JERRY_LIT_HASHMAP
215+
static jmem_cpointer_t *
216+
ecma_find_empty_or_same_literal_string_slot (ecma_string_t *string_p /**< string to be searched */)
217+
{
173218
jmem_cpointer_t string_list_cp = JERRY_CONTEXT (string_list_first_cp);
174219
jmem_cpointer_t *empty_cpointer_p = NULL;
175220

@@ -189,42 +234,76 @@ ecma_find_or_create_literal_string (const lit_utf8_byte_t *chars_p, /**< string
189234
else
190235
{
191236
ecma_string_t *value_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, string_list_p->values[i]);
192-
193237
if (ecma_compare_ecma_strings (string_p, value_p))
194238
{
195-
/* Return with string if found in the list. */
196-
ecma_deref_ecma_string (string_p);
197-
return ecma_make_string_value (value_p);
239+
return string_list_p->values + i;
198240
}
199241
}
200242
}
201243

202244
string_list_cp = string_list_p->next_cp;
203245
}
204246

205-
ECMA_SET_STRING_AS_STATIC (string_p);
206-
jmem_cpointer_t result;
207-
JMEM_CP_SET_NON_NULL_POINTER (result, string_p);
208-
209247
if (empty_cpointer_p != NULL)
210248
{
211-
*empty_cpointer_p = result;
212-
return ecma_make_string_value (string_p);
249+
return empty_cpointer_p;
213250
}
214251

215-
ecma_lit_storage_item_t *new_item_p;
216-
new_item_p = (ecma_lit_storage_item_t *) jmem_pools_alloc (sizeof (ecma_lit_storage_item_t));
252+
return ecma_allocate_new_string_slot ();
253+
} /* ecma_find_empty_or_same_literal_string_slot */
254+
#endif /* !JERRY_LIT_HASHMAP */
217255

218-
new_item_p->values[0] = result;
219-
for (int i = 1; i < ECMA_LIT_STORAGE_VALUE_COUNT; i++)
256+
/**
257+
* Find or create a literal string.
258+
*
259+
* @return ecma_string_t compressed pointer
260+
*/
261+
262+
ecma_value_t
263+
ecma_find_or_create_literal_string (const lit_utf8_byte_t *chars_p, /**< string to be searched */
264+
lit_utf8_size_t size, /**< size of the string */
265+
bool is_ascii) /**< encode of the string */
266+
{
267+
ecma_string_t *string_p =
268+
(is_ascii ? ecma_new_ecma_string_from_ascii (chars_p, size) : ecma_new_ecma_string_from_utf8 (chars_p, size));
269+
270+
if (ECMA_IS_DIRECT_STRING (string_p))
220271
{
221-
new_item_p->values[i] = JMEM_CP_NULL;
272+
return ecma_make_string_value (string_p);
222273
}
223274

224-
new_item_p->next_cp = JERRY_CONTEXT (string_list_first_cp);
225-
JMEM_CP_SET_NON_NULL_POINTER (JERRY_CONTEXT (string_list_first_cp), new_item_p);
275+
#if JERRY_LIT_HASHMAP
276+
const ecma_string_t *hashmap_entry = hashmap_get (&JERRY_CONTEXT (string_hashmap), string_p);
277+
if (hashmap_entry != NULL)
278+
{
279+
ecma_deref_ecma_string (string_p);
280+
return ecma_make_string_value (hashmap_entry);
281+
}
282+
// Since the string is not found, just find an empty slot
283+
jmem_cpointer_t *slot = ecma_find_empty_literal_string_slot ();
284+
#else /* JERRY_LIT_HASHMAP */
285+
jmem_cpointer_t *slot = ecma_find_empty_or_same_literal_string_slot (string_p);
286+
if (*slot != JMEM_CP_NULL)
287+
{
288+
// The string has been found
289+
ecma_string_t *value_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_string_t, *slot);
290+
ecma_deref_ecma_string (string_p);
291+
return ecma_make_string_value (value_p);
292+
}
293+
#endif /* JERRY_LIT_HASHMAP */
294+
295+
// String has not been found...
296+
ECMA_SET_STRING_AS_STATIC (string_p);
297+
jmem_cpointer_t result;
298+
JMEM_CP_SET_NON_NULL_POINTER (result, string_p);
299+
*slot = result;
300+
301+
#if JERRY_LIT_HASHMAP
302+
hashmap_put (&JERRY_CONTEXT (string_hashmap), string_p);
303+
#endif /* JERRY_LIT_HASHMAP */
226304

227305
return ecma_make_string_value (string_p);
306+
228307
} /* ecma_find_or_create_literal_string */
229308

230309
/**

jerry-core/jcontext/jcontext.h

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "debugger.h"
2929
#include "jmem.h"
3030
#include "js-parser-internal.h"
31+
#include "lit-hashmap.h"
3132
#include "re-bytecode.h"
3233
#include "vm-defines.h"
3334

@@ -138,6 +139,10 @@ struct jerry_context_t
138139
#endif /* JERRY_BUILTIN_BIGINT */
139140
jmem_cpointer_t global_symbols_cp[ECMA_BUILTIN_GLOBAL_SYMBOL_COUNT]; /**< global symbols */
140141

142+
#if JERRY_LIT_HASHMAP
143+
struct hashmap_s string_hashmap;
144+
#endif /* JERRY_LIT_HASHMAP */
145+
141146
#if JERRY_MODULE_SYSTEM
142147
ecma_module_t *module_current_p; /**< current module context */
143148
jerry_module_state_changed_cb_t module_state_changed_callback_p; /**< callback which is called after the

jerry-core/jmem/jmem-heap.c

+10
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "jmem.h"
2424
#include "jrt-bit-fields.h"
2525
#include "jrt-libc-includes.h"
26+
#include "lit-hashmap.h"
2627

2728
#define JMEM_ALLOCATOR_INTERNAL
2829
#include "jmem-allocator-internal.h"
@@ -103,6 +104,11 @@ jmem_heap_init (void)
103104
JMEM_VALGRIND_NOACCESS_SPACE (JERRY_HEAP_CONTEXT (area), JMEM_HEAP_AREA_SIZE);
104105

105106
#endif /* !JERRY_SYSTEM_ALLOCATOR */
107+
108+
#if JERRY_LIT_HASHMAP
109+
hashmap_init (&JERRY_CONTEXT (string_hashmap));
110+
#endif /* JERRY_LIT_HASHMAP */
111+
106112
JMEM_HEAP_STAT_INIT ();
107113
} /* jmem_heap_init */
108114

@@ -112,6 +118,10 @@ jmem_heap_init (void)
112118
void
113119
jmem_heap_finalize (void)
114120
{
121+
#if JERRY_LIT_HASHMAP
122+
hashmap_destroy (&JERRY_CONTEXT (string_hashmap));
123+
#endif /* JERRY_LIT_HASHMAP */
124+
115125
JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_allocated_size) == 0);
116126
#if !JERRY_SYSTEM_ALLOCATOR
117127
JMEM_VALGRIND_NOACCESS_SPACE (&JERRY_HEAP_CONTEXT (first), JMEM_HEAP_SIZE);

jerry-core/lit/lit-hashmap-internal.h

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/* Copyright JS Foundation and other contributors, http://js.foundation
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
/*
17+
The lit-hashmap library is based on the public domain software
18+
available from https://github.com/sheredom/hashmap.h
19+
*/
20+
21+
#ifndef LIT_HASHMAP_INTERNAL_H
22+
#define LIT_HASHMAP_INTERNAL_H
23+
24+
#include "lit-hashmap.h"
25+
26+
#define HASHMAP_ALWAYS_INLINE __attribute__ ((always_inline)) inline
27+
#define HASHMAP_LINEAR_PROBE_LENGTH (8)
28+
29+
/**
30+
* hashmap creation options.
31+
*/
32+
33+
typedef struct hashmap_create_options_s
34+
{
35+
hashmap_uint32_t initial_capacity; /**< initial hashmap capacity */
36+
} hashmap_create_options_t;
37+
38+
/// @brief Create a hashmap.
39+
/// @param options The options to create the hashmap with.
40+
/// @param out_hashmap The storage for the created hashmap.
41+
/// @return On success 0 is returned.
42+
///
43+
/// The options members work as follows:
44+
/// - initial_capacity The initial capacity of the hashmap.
45+
/// - hasher Which hashing function to use with the hashmap (by default the
46+
// crc32 with Robert Jenkins' mix is used).
47+
int hashmap_create_ex (struct hashmap_create_options_s options, struct hashmap_s *const out_hashmap);
48+
49+
/// @brief Iterate over all the elements in a hashmap.
50+
/// @param hashmap The hashmap to iterate over.
51+
/// @param iterator The function pointer to call on each element.
52+
/// @param context The context to pass as the first argument to f.
53+
/// @return If the entire hashmap was iterated then 0 is returned.
54+
/// Otherwise if the callback function f returned positive then the positive
55+
/// value is returned. If the callback function returns -1, the current item
56+
/// is removed and iteration continues.
57+
int hashmap_iterate_pairs (struct hashmap_s *const hashmap,
58+
int (*iterator) (void *const, struct hashmap_element_s *const),
59+
void *const context);
60+
61+
/// @brief Get the size of the hashmap.
62+
/// @param hashmap The hashmap to get the size of.
63+
/// @return The size of the hashmap.
64+
HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_num_entries (const struct hashmap_s *const hashmap);
65+
66+
/// @brief Get the capacity of the hashmap.
67+
/// @param hashmap The hashmap to get the size of.
68+
/// @return The capacity of the hashmap.
69+
HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_capacity (const struct hashmap_s *const hashmap);
70+
71+
HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_hash_helper_int_helper (const struct hashmap_s *const m,
72+
const ecma_string_t *const key);
73+
HASHMAP_ALWAYS_INLINE int hashmap_hash_helper (const struct hashmap_s *const m,
74+
const ecma_string_t *const key,
75+
hashmap_uint32_t *const out_index);
76+
int hashmap_rehash_iterator (void *const new_hash, struct hashmap_element_s *const e);
77+
HASHMAP_ALWAYS_INLINE int hashmap_rehash_helper (struct hashmap_s *const m);
78+
HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_clz (const hashmap_uint32_t x);
79+
80+
#endif // LIT_HASHMAP_INTERNAL_H

0 commit comments

Comments
 (0)