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#ifndef JMEM_H
17#define JMEM_H
18
19#include "jrt.h"
20
21/** \addtogroup mem Memory allocation
22 * @{
23 *
24 * \addtogroup heap Heap
25 * @{
26 */
27
28/**
29 * Logarithm of required alignment for allocated units/blocks
30 */
31#define JMEM_ALIGNMENT_LOG   3
32
33/**
34 * Representation of NULL value for compressed pointers
35 */
36#define JMEM_CP_NULL ((jmem_cpointer_t) 0)
37
38/**
39 * Required alignment for allocated units/blocks
40 */
41#define JMEM_ALIGNMENT (1u << JMEM_ALIGNMENT_LOG)
42
43/**
44 * Pointer value can be directly stored without compression
45 */
46#if UINTPTR_MAX <= UINT32_MAX
47#define JMEM_CAN_STORE_POINTER_VALUE_DIRECTLY
48#endif /* UINTPTR_MAX <= UINT32_MAX */
49
50/**
51 * Mask for tag part in jmem_cpointer_tag_t
52 */
53#define JMEM_TAG_MASK 0x7u
54
55/**
56 * Shift for tag part in jmem_cpointer_tag_t
57 */
58#if defined (JMEM_CAN_STORE_POINTER_VALUE_DIRECTLY) && ENABLED (JERRY_CPOINTER_32_BIT)
59#define JMEM_TAG_SHIFT 0
60#else /* !JMEM_CAN_STORE_POINTER_VALUE_DIRECTLY || !ENABLED (JERRY_CPOINTER_32_BIT) */
61#define JMEM_TAG_SHIFT 3
62#endif /* JMEM_CAN_STORE_POINTER_VALUE_DIRECTLY && ENABLED (JERRY_CPOINTER_32_BIT) */
63
64/**
65 * Bit mask for tag part in jmem_cpointer_tag_t
66 */
67enum
68{
69  JMEM_FIRST_TAG_BIT_MASK   = (1u << 0), /**< first tag bit mask **/
70  JMEM_SECOND_TAG_BIT_MASK  = (1u << 1), /**< second tag bit mask **/
71  JMEM_THIRD_TAG_BIT_MASK   = (1u << 2), /**< third tag bit mask **/
72};
73
74/**
75 * Compressed pointer representations
76 *
77 * 16 bit representation:
78 *   The jmem_cpointer_t is defined as uint16_t
79 *   and it can contain any sixteen bit value.
80 *
81 * 32 bit representation:
82 *   The jmem_cpointer_t is defined as uint32_t.
83 *   The lower JMEM_ALIGNMENT_LOG bits must be zero.
84 *   The other bits can have any value.
85 *
86 * The 16 bit representation always encodes an offset from
87 * a heap base. The 32 bit representation currently encodes
88 * raw 32 bit JMEM_ALIGNMENT aligned pointers on 32 bit systems.
89 * This can be extended to encode a 32 bit offset from a heap
90 * base on 64 bit systems in the future. There are no plans
91 * to support more than 4G address space for JerryScript.
92 */
93
94/**
95 * Compressed pointer
96 */
97#if ENABLED (JERRY_CPOINTER_32_BIT)
98typedef uint32_t jmem_cpointer_t;
99#else /* !ENABLED (JERRY_CPOINTER_32_BIT) */
100typedef uint16_t jmem_cpointer_t;
101#endif /* ENABLED (JERRY_CPOINTER_32_BIT) */
102
103/**
104 * Compressed pointer with tag value
105 */
106typedef uint32_t jmem_cpointer_tag_t;
107
108/**
109 * Memory usage pressure for reclaiming unused memory.
110 *
111 * Each failed allocation will try to reclaim memory with increasing pressure,
112 * until enough memory is freed to fulfill the allocation request.
113 *
114 * If not enough memory is freed and JMEM_PRESSURE_FULL is reached,
115 * then the engine is shut down with ERR_OUT_OF_MEMORY.
116 */
117typedef enum
118{
119  JMEM_PRESSURE_NONE, /**< no memory pressure */
120  JMEM_PRESSURE_LOW,  /**< low memory pressure */
121  JMEM_PRESSURE_HIGH, /**< high memory pressure */
122  JMEM_PRESSURE_FULL, /**< memory full */
123} jmem_pressure_t;
124
125/**
126 * Node for free chunk list
127 */
128typedef struct jmem_pools_chunk_t
129{
130  struct jmem_pools_chunk_t *next_p; /**< pointer to next pool chunk */
131} jmem_pools_chunk_t;
132
133/**
134 *  Free region node
135 */
136typedef struct
137{
138  uint32_t next_offset; /**< Offset of next region in list */
139  uint32_t size; /**< Size of region */
140} jmem_heap_free_t;
141
142void jmem_init (void);
143void jmem_finalize (void);
144
145void *jmem_heap_alloc_block (const size_t size);
146void *jmem_heap_alloc_block_null_on_error (const size_t size);
147void *jmem_heap_realloc_block (void *ptr, const size_t old_size, const size_t new_size);
148void jmem_heap_free_block (void *ptr, const size_t size);
149
150#if ENABLED (JERRY_MEM_STATS)
151/**
152 * Heap memory usage statistics
153 */
154typedef struct
155{
156  size_t size; /**< heap total size */
157
158  size_t allocated_bytes; /**< currently allocated bytes */
159  size_t peak_allocated_bytes; /**< peak allocated bytes */
160
161  size_t waste_bytes; /**< bytes waste due to blocks filled partially */
162  size_t peak_waste_bytes; /**< peak wasted bytes */
163
164  size_t byte_code_bytes; /**< allocated memory for byte code */
165  size_t peak_byte_code_bytes; /**< peak allocated memory for byte code */
166
167  size_t string_bytes; /**< allocated memory for strings */
168  size_t peak_string_bytes; /**< peak allocated memory for strings */
169
170  size_t object_bytes; /**< allocated memory for objects */
171  size_t peak_object_bytes; /**< peak allocated memory for objects */
172
173  size_t property_bytes; /**< allocated memory for properties */
174  size_t peak_property_bytes; /**< peak allocated memory for properties */
175} jmem_heap_stats_t;
176
177void jmem_stats_allocate_byte_code_bytes (size_t property_size);
178void jmem_stats_free_byte_code_bytes (size_t property_size);
179void jmem_stats_allocate_string_bytes (size_t string_size);
180void jmem_stats_free_string_bytes (size_t string_size);
181void jmem_stats_allocate_object_bytes (size_t object_size);
182void jmem_stats_free_object_bytes (size_t string_size);
183void jmem_stats_allocate_property_bytes (size_t property_size);
184void jmem_stats_free_property_bytes (size_t property_size);
185
186void jmem_heap_get_stats (jmem_heap_stats_t *);
187void jmem_heap_stats_reset_peak (void);
188void jmem_heap_stats_print (void);
189#endif /* ENABLED (JERRY_MEM_STATS) */
190
191jmem_cpointer_t JERRY_ATTR_PURE jmem_compress_pointer (const void *pointer_p);
192void * JERRY_ATTR_PURE jmem_decompress_pointer (uintptr_t compressed_pointer);
193
194/**
195 * Define a local array variable and allocate memory for the array on the heap.
196 *
197 * If requested number of elements is zero, assign NULL to the variable.
198 *
199 * Warning:
200 *         if there is not enough memory on the heap, shutdown engine with ERR_OUT_OF_MEMORY.
201 */
202#define JMEM_DEFINE_LOCAL_ARRAY(var_name, number, type) \
203{ \
204  size_t var_name ## ___size = (size_t) (number) * sizeof (type); \
205  type *var_name = (type *) (jmem_heap_alloc_block (var_name ## ___size));
206
207/**
208 * Free the previously defined local array variable, freeing corresponding block on the heap,
209 * if it was allocated (i.e. if the array's size was non-zero).
210 */
211#define JMEM_FINALIZE_LOCAL_ARRAY(var_name) \
212  if (var_name != NULL) \
213  { \
214    JERRY_ASSERT (var_name ## ___size != 0); \
215    \
216    jmem_heap_free_block (var_name, var_name ## ___size); \
217  } \
218  else \
219  { \
220    JERRY_ASSERT (var_name ## ___size == 0); \
221  } \
222}
223
224/**
225 * Get value of pointer from specified non-null compressed pointer value
226 */
227#define JMEM_CP_GET_NON_NULL_POINTER(type, cp_value) \
228  ((type *) (jmem_decompress_pointer (cp_value)))
229
230/**
231 * Get value of pointer from specified compressed pointer value
232 */
233#define JMEM_CP_GET_POINTER(type, cp_value) \
234  (((JERRY_UNLIKELY ((cp_value) == JMEM_CP_NULL)) ? NULL : JMEM_CP_GET_NON_NULL_POINTER (type, cp_value)))
235
236/**
237 * Set value of non-null compressed pointer so that it will correspond
238 * to specified non_compressed_pointer
239 */
240#define JMEM_CP_SET_NON_NULL_POINTER(cp_value, non_compressed_pointer) \
241  (cp_value) = jmem_compress_pointer (non_compressed_pointer)
242
243/**
244 * Set value of compressed pointer so that it will correspond
245 * to specified non_compressed_pointer
246 */
247#define JMEM_CP_SET_POINTER(cp_value, non_compressed_pointer) \
248  do \
249  { \
250    void *ptr_value = (void *) non_compressed_pointer; \
251    \
252    if (JERRY_UNLIKELY ((ptr_value) == NULL)) \
253    { \
254      (cp_value) = JMEM_CP_NULL; \
255    } \
256    else \
257    { \
258      JMEM_CP_SET_NON_NULL_POINTER (cp_value, ptr_value); \
259    } \
260  } while (false);
261
262/**
263 * Set value of pointer-tag value so that it will correspond
264 * to specified non_compressed_pointer along with tag
265 */
266#define JMEM_CP_SET_NON_NULL_POINTER_TAG(cp_value, pointer, tag) \
267  do \
268  { \
269    JERRY_ASSERT ((uintptr_t) tag < (uintptr_t) (JMEM_ALIGNMENT)); \
270    jmem_cpointer_tag_t compressed_ptr = jmem_compress_pointer (pointer); \
271    (cp_value) = (jmem_cpointer_tag_t) ((compressed_ptr << JMEM_TAG_SHIFT) | tag); \
272  } while (false);
273
274/**
275 * Extract value of pointer from specified pointer-tag value
276 */
277#define JMEM_CP_GET_NON_NULL_POINTER_FROM_POINTER_TAG(type, cp_value) \
278  ((type *) (jmem_decompress_pointer ((cp_value & ~JMEM_TAG_MASK) >> JMEM_TAG_SHIFT)))
279
280/**
281 * Get value of each tag from specified pointer-tag value
282 */
283#define JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG(cp_value) \
284  (cp_value & JMEM_FIRST_TAG_BIT_MASK) /**< get first tag bit **/
285#define JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG(cp_value) \
286  (cp_value & JMEM_SECOND_TAG_BIT_MASK) /**< get second tag bit **/
287#define JMEM_CP_GET_THIRD_BIT_FROM_POINTER_TAG(cp_value) \
288  (cp_value & JMEM_THIRD_TAG_BIT_MASK) /**< get third tag bit **/
289
290/**
291 * Set value of each tag to specified pointer-tag value
292 */
293#define JMEM_CP_SET_FIRST_BIT_TO_POINTER_TAG(cp_value) \
294  (cp_value) = (cp_value | JMEM_FIRST_TAG_BIT_MASK) /**< set first tag bit **/
295#define JMEM_CP_SET_SECOND_BIT_TO_POINTER_TAG(cp_value) \
296  (cp_value) = (cp_value | JMEM_SECOND_TAG_BIT_MASK) /**< set second tag bit **/
297#define JMEM_CP_SET_THIRD_BIT_TO_POINTER_TAG(cp_value) \
298  (cp_value) = (cp_value | JMEM_THIRD_TAG_BIT_MASK) /**< set third tag bit **/
299
300/**
301 * @}
302 * \addtogroup poolman Memory pool manager
303 * @{
304 */
305
306void *jmem_pools_alloc (size_t size);
307void jmem_pools_free (void *chunk_p, size_t size);
308void jmem_pools_collect_empty (void);
309
310/**
311 * @}
312 * @}
313 */
314
315#endif /* !JMEM_H */
316