Files
jerryscript/jerry-core/jmem/jmem-heap.c
T
Dániel Bátyai ac1c48eeff Update jerry-port and jerry-ext (#4907)
Notable changes:
  - Updated and the port API interface, new functions have been added
    and some have been changed. The port library is now cleaned up to
    not have any dependency on jerry-core, as it should be. The port library
    is now strictly a collection of functions that implement
    embedding/platform specific behavior.
  - The default port implementation has been split for windows and unix.
    Implemented port functions have been categorized and reorganized,
    and marked with attribute((weak)) for better reusability.
  - External context allocation has been moved to the port API instead
    of a core API callback. The iterface has also been extended with a
    function to free the allocated context. When external context is
    enabled, jerry_init now automatically calls the port implementation
    to allocate the context and jerry_cleanup automatically calls the port
    to free the context.
  - jerry_port_log has been changed to no longer require formatting to
    be implemented by the port. The reason beind this is that it was vague what
    format specifiers were used by the engine, and in what manner. The port
    function now takes a zero-terminated string, and should only implement
    how the string should be logged.
  - Logging and log message formatting is now handled by the core jerry library
    where it can be implemented as necessary. Logging can be done through a new
    core API function, which uses the port to output the final log message.
  - Log level has been moved into jerry-core, and an API function has
    been added to set the log level. It should be the library that
    filters log messages based on the requested log level, instead of
    logging everything and requiring the user to do so.
  - Module resolving logic has been moved into jerry-core. There's no
    reason to have it in the port library and requiring embedders to
    duplicate the code. It also added an unnecessary dependency on
    jerry-core to the port. Platform specific behavior is still used through
    the port API, like resolving module specifiers, and reading source file
    contents. If necessary, the resolving logic can still be overridden as
    previously.
  - The jerry-ext library has also been cleaned up, and many utility
    functions have been added that previously were implemented in
    jerry-main. This allows easier reusability for some common operations,
    like printing unhandled exceptions or providing a repl console.
  - Debugger interaction with logged/printed messages has been fixed, so
    that it's no longer the port implementations responsibility to send
    the output to the debugger, as the port should have no notion of what a
    debugger is.  The printing and logging functions will now pass the
    result message to the debugger, if connected.
  - Cleaned up TZA handling in the date port implementation, and simplified
    the API function prototype.
  - Moved property access helper functions that use ASCII strings as
    keys from jerry-ext to the core API.

JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai@inf.u-szeged.hu
2022-01-20 13:53:47 +01:00

826 lines
28 KiB
C

/* Copyright JS Foundation and other contributors, http://js.foundation
*
* 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.
*/
/**
* Heap implementation
*/
#include "ecma-gc.h"
#include "jcontext.h"
#include "jmem.h"
#include "jrt-bit-fields.h"
#include "jrt-libc-includes.h"
#define JMEM_ALLOCATOR_INTERNAL
#include "jmem-allocator-internal.h"
/** \addtogroup mem Memory allocation
* @{
*
* \addtogroup heap Heap
* @{
*/
#if !JERRY_SYSTEM_ALLOCATOR
/**
* End of list marker.
*/
#define JMEM_HEAP_END_OF_LIST ((uint32_t) 0xffffffff)
/**
* @{
*/
#ifdef ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY
/* In this case we simply store the pointer, since it fits anyway. */
#define JMEM_HEAP_GET_OFFSET_FROM_ADDR(p) ((uint32_t) (p))
#define JMEM_HEAP_GET_ADDR_FROM_OFFSET(u) ((jmem_heap_free_t *) (u))
#else /* !ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */
#define JMEM_HEAP_GET_OFFSET_FROM_ADDR(p) ((uint32_t) ((uint8_t *) (p) -JERRY_HEAP_CONTEXT (area)))
#define JMEM_HEAP_GET_ADDR_FROM_OFFSET(u) ((jmem_heap_free_t *) (JERRY_HEAP_CONTEXT (area) + (u)))
#endif /* ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */
/**
* @}
*/
/**
* Get end of region
*
* @return pointer to the end of the region
*/
static inline jmem_heap_free_t *JERRY_ATTR_ALWAYS_INLINE JERRY_ATTR_PURE
jmem_heap_get_region_end (jmem_heap_free_t *curr_p) /**< current region */
{
return (jmem_heap_free_t *) ((uint8_t *) curr_p + curr_p->size);
} /* jmem_heap_get_region_end */
#endif /* !JERRY_SYSTEM_ALLOCATOR */
/**
* Startup initialization of heap
*/
void
jmem_heap_init (void)
{
#if !JERRY_SYSTEM_ALLOCATOR
#if !JERRY_CPOINTER_32_BIT
/* the maximum heap size for 16bit compressed pointers should be 512K */
JERRY_ASSERT (((UINT16_MAX + 1) << JMEM_ALIGNMENT_LOG) >= JMEM_HEAP_SIZE);
#endif /* !JERRY_CPOINTER_32_BIT */
JERRY_ASSERT ((uintptr_t) JERRY_HEAP_CONTEXT (area) % JMEM_ALIGNMENT == 0);
JERRY_CONTEXT (jmem_heap_limit) = CONFIG_GC_LIMIT;
jmem_heap_free_t *const region_p = (jmem_heap_free_t *) JERRY_HEAP_CONTEXT (area);
region_p->size = JMEM_HEAP_AREA_SIZE;
region_p->next_offset = JMEM_HEAP_END_OF_LIST;
JERRY_HEAP_CONTEXT (first).size = 0;
JERRY_HEAP_CONTEXT (first).next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (region_p);
JERRY_CONTEXT (jmem_heap_list_skip_p) = &JERRY_HEAP_CONTEXT (first);
JMEM_VALGRIND_NOACCESS_SPACE (&JERRY_HEAP_CONTEXT (first), sizeof (jmem_heap_free_t));
JMEM_VALGRIND_NOACCESS_SPACE (JERRY_HEAP_CONTEXT (area), JMEM_HEAP_AREA_SIZE);
#endif /* !JERRY_SYSTEM_ALLOCATOR */
JMEM_HEAP_STAT_INIT ();
} /* jmem_heap_init */
/**
* Finalize heap
*/
void
jmem_heap_finalize (void)
{
JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_allocated_size) == 0);
#if !JERRY_SYSTEM_ALLOCATOR
JMEM_VALGRIND_NOACCESS_SPACE (&JERRY_HEAP_CONTEXT (first), JMEM_HEAP_SIZE);
#endif /* !JERRY_SYSTEM_ALLOCATOR */
} /* jmem_heap_finalize */
/**
* Allocation of memory region.
*
* See also:
* jmem_heap_alloc_block
*
* @return pointer to allocated memory block - if allocation is successful,
* NULL - if there is not enough memory.
*/
static void *JERRY_ATTR_HOT
jmem_heap_alloc (const size_t size) /**< size of requested block */
{
#if !JERRY_SYSTEM_ALLOCATOR
/* Align size. */
const size_t required_size = ((size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT) * JMEM_ALIGNMENT;
jmem_heap_free_t *data_space_p = NULL;
JMEM_VALGRIND_DEFINED_SPACE (&JERRY_HEAP_CONTEXT (first), sizeof (jmem_heap_free_t));
/* Fast path for 8 byte chunks, first region is guaranteed to be sufficient. */
if (required_size == JMEM_ALIGNMENT && JERRY_LIKELY (JERRY_HEAP_CONTEXT (first).next_offset != JMEM_HEAP_END_OF_LIST))
{
data_space_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (JERRY_HEAP_CONTEXT (first).next_offset);
JERRY_ASSERT (jmem_is_heap_pointer (data_space_p));
JMEM_VALGRIND_DEFINED_SPACE (data_space_p, sizeof (jmem_heap_free_t));
JERRY_CONTEXT (jmem_heap_allocated_size) += JMEM_ALIGNMENT;
if (JERRY_CONTEXT (jmem_heap_allocated_size) >= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) += CONFIG_GC_LIMIT;
}
if (data_space_p->size == JMEM_ALIGNMENT)
{
JERRY_HEAP_CONTEXT (first).next_offset = data_space_p->next_offset;
}
else
{
JERRY_ASSERT (data_space_p->size > JMEM_ALIGNMENT);
jmem_heap_free_t *remaining_p;
remaining_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (JERRY_HEAP_CONTEXT (first).next_offset) + 1;
JMEM_VALGRIND_DEFINED_SPACE (remaining_p, sizeof (jmem_heap_free_t));
remaining_p->size = data_space_p->size - JMEM_ALIGNMENT;
remaining_p->next_offset = data_space_p->next_offset;
JMEM_VALGRIND_NOACCESS_SPACE (remaining_p, sizeof (jmem_heap_free_t));
JERRY_HEAP_CONTEXT (first).next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (remaining_p);
}
JMEM_VALGRIND_NOACCESS_SPACE (data_space_p, sizeof (jmem_heap_free_t));
if (JERRY_UNLIKELY (data_space_p == JERRY_CONTEXT (jmem_heap_list_skip_p)))
{
JERRY_CONTEXT (jmem_heap_list_skip_p) = JMEM_HEAP_GET_ADDR_FROM_OFFSET (JERRY_HEAP_CONTEXT (first).next_offset);
}
}
/* Slow path for larger regions. */
else
{
uint32_t current_offset = JERRY_HEAP_CONTEXT (first).next_offset;
jmem_heap_free_t *prev_p = &JERRY_HEAP_CONTEXT (first);
while (JERRY_LIKELY (current_offset != JMEM_HEAP_END_OF_LIST))
{
jmem_heap_free_t *current_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (current_offset);
JERRY_ASSERT (jmem_is_heap_pointer (current_p));
JMEM_VALGRIND_DEFINED_SPACE (current_p, sizeof (jmem_heap_free_t));
const uint32_t next_offset = current_p->next_offset;
JERRY_ASSERT (next_offset == JMEM_HEAP_END_OF_LIST
|| jmem_is_heap_pointer (JMEM_HEAP_GET_ADDR_FROM_OFFSET (next_offset)));
if (current_p->size >= required_size)
{
/* Region is sufficiently big, store address. */
data_space_p = current_p;
/* Region was larger than necessary. */
if (current_p->size > required_size)
{
/* Get address of remaining space. */
jmem_heap_free_t *const remaining_p = (jmem_heap_free_t *) ((uint8_t *) current_p + required_size);
/* Update metadata. */
JMEM_VALGRIND_DEFINED_SPACE (remaining_p, sizeof (jmem_heap_free_t));
remaining_p->size = current_p->size - (uint32_t) required_size;
remaining_p->next_offset = next_offset;
JMEM_VALGRIND_NOACCESS_SPACE (remaining_p, sizeof (jmem_heap_free_t));
/* Update list. */
JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t));
prev_p->next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (remaining_p);
JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t));
}
/* Block is an exact fit. */
else
{
/* Remove the region from the list. */
JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t));
prev_p->next_offset = next_offset;
JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t));
}
JERRY_CONTEXT (jmem_heap_list_skip_p) = prev_p;
/* Found enough space. */
JERRY_CONTEXT (jmem_heap_allocated_size) += required_size;
while (JERRY_CONTEXT (jmem_heap_allocated_size) >= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) += CONFIG_GC_LIMIT;
}
break;
}
JMEM_VALGRIND_NOACCESS_SPACE (current_p, sizeof (jmem_heap_free_t));
/* Next in list. */
prev_p = current_p;
current_offset = next_offset;
}
}
JMEM_VALGRIND_NOACCESS_SPACE (&JERRY_HEAP_CONTEXT (first), sizeof (jmem_heap_free_t));
JERRY_ASSERT ((uintptr_t) data_space_p % JMEM_ALIGNMENT == 0);
JMEM_VALGRIND_MALLOCLIKE_SPACE (data_space_p, size);
return (void *) data_space_p;
#else /* JERRY_SYSTEM_ALLOCATOR */
JERRY_CONTEXT (jmem_heap_allocated_size) += size;
while (JERRY_CONTEXT (jmem_heap_allocated_size) >= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) += CONFIG_GC_LIMIT;
}
return malloc (size);
#endif /* !JERRY_SYSTEM_ALLOCATOR */
} /* jmem_heap_alloc */
/**
* Allocation of memory block, reclaiming memory if the request cannot be fulfilled.
*
* Note:
* Each failed allocation attempt tries to reclaim memory with an increasing pressure,
* up to 'max_pressure', or until a sufficient memory block is found. When JMEM_PRESSURE_FULL
* is reached, the engine is terminated with JERRY_FATAL_OUT_OF_MEMORY. The `max_pressure` argument
* can be used to limit the maximum pressure, and prevent the engine from terminating.
*
* @return NULL, if the required memory size is 0 or not enough memory
* pointer to the allocated memory block, if allocation is successful
*/
static void *
jmem_heap_gc_and_alloc_block (const size_t size, /**< required memory size */
jmem_pressure_t max_pressure) /**< pressure limit */
{
if (JERRY_UNLIKELY (size == 0))
{
return NULL;
}
jmem_pressure_t pressure = JMEM_PRESSURE_NONE;
#if !JERRY_MEM_GC_BEFORE_EACH_ALLOC
if (JERRY_CONTEXT (jmem_heap_allocated_size) + size >= JERRY_CONTEXT (jmem_heap_limit))
{
pressure = JMEM_PRESSURE_LOW;
ecma_free_unused_memory (pressure);
}
#else /* !JERRY_MEM_GC_BEFORE_EACH_ALLOC */
ecma_gc_run ();
#endif /* JERRY_MEM_GC_BEFORE_EACH_ALLOC */
void *data_space_p = jmem_heap_alloc (size);
while (JERRY_UNLIKELY (data_space_p == NULL) && JERRY_LIKELY (pressure < max_pressure))
{
pressure++;
ecma_free_unused_memory (pressure);
data_space_p = jmem_heap_alloc (size);
}
return data_space_p;
} /* jmem_heap_gc_and_alloc_block */
/**
* Internal method for allocating a memory block.
*/
extern inline void *JERRY_ATTR_HOT JERRY_ATTR_ALWAYS_INLINE
jmem_heap_alloc_block_internal (const size_t size) /**< required memory size */
{
return jmem_heap_gc_and_alloc_block (size, JMEM_PRESSURE_FULL);
} /* jmem_heap_alloc_block_internal */
/**
* Allocation of memory block, reclaiming unused memory if there is not enough.
*
* Note:
* If a sufficiently sized block can't be found, the engine will be terminated with JERRY_FATAL_OUT_OF_MEMORY.
*
* @return NULL, if the required memory is 0
* pointer to allocated memory block, otherwise
*/
extern inline void *JERRY_ATTR_HOT JERRY_ATTR_ALWAYS_INLINE
jmem_heap_alloc_block (const size_t size) /**< required memory size */
{
void *block_p = jmem_heap_gc_and_alloc_block (size, JMEM_PRESSURE_FULL);
JMEM_HEAP_STAT_ALLOC (size);
return block_p;
} /* jmem_heap_alloc_block */
/**
* Allocation of memory block, reclaiming unused memory if there is not enough.
*
* Note:
* If a sufficiently sized block can't be found, NULL will be returned.
*
* @return NULL, if the required memory size is 0
* also NULL, if the allocation has failed
* pointer to the allocated memory block, otherwise
*/
extern inline void *JERRY_ATTR_HOT JERRY_ATTR_ALWAYS_INLINE
jmem_heap_alloc_block_null_on_error (const size_t size) /**< required memory size */
{
void *block_p = jmem_heap_gc_and_alloc_block (size, JMEM_PRESSURE_HIGH);
#if JERRY_MEM_STATS
if (block_p != NULL)
{
JMEM_HEAP_STAT_ALLOC (size);
}
#endif /* JERRY_MEM_STATS */
return block_p;
} /* jmem_heap_alloc_block_null_on_error */
#if !JERRY_SYSTEM_ALLOCATOR
/**
* Finds the block in the free block list which preceeds the argument block
*
* @return pointer to the preceeding block
*/
static jmem_heap_free_t *
jmem_heap_find_prev (const jmem_heap_free_t *const block_p) /**< which memory block's predecessor we're looking for */
{
const jmem_heap_free_t *prev_p;
if (block_p > JERRY_CONTEXT (jmem_heap_list_skip_p))
{
prev_p = JERRY_CONTEXT (jmem_heap_list_skip_p);
}
else
{
prev_p = &JERRY_HEAP_CONTEXT (first);
}
JERRY_ASSERT (jmem_is_heap_pointer (block_p));
const uint32_t block_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (block_p);
JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t));
/* Find position of region in the list. */
while (prev_p->next_offset < block_offset)
{
const jmem_heap_free_t *const next_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (prev_p->next_offset);
JERRY_ASSERT (jmem_is_heap_pointer (next_p));
JMEM_VALGRIND_DEFINED_SPACE (next_p, sizeof (jmem_heap_free_t));
JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t));
prev_p = next_p;
}
JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t));
return (jmem_heap_free_t *) prev_p;
} /* jmem_heap_find_prev */
/**
* Inserts the block into the free chain after a specified block.
*
* Note:
* 'jmem_heap_find_prev' can and should be used to find the previous free block
*/
static void
jmem_heap_insert_block (jmem_heap_free_t *block_p, /**< block to insert */
jmem_heap_free_t *prev_p, /**< the free block after which to insert 'block_p' */
const size_t size) /**< size of the inserted block */
{
JERRY_ASSERT ((uintptr_t) block_p % JMEM_ALIGNMENT == 0);
JERRY_ASSERT (size % JMEM_ALIGNMENT == 0);
JMEM_VALGRIND_NOACCESS_SPACE (block_p, size);
JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t));
jmem_heap_free_t *next_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (prev_p->next_offset);
JMEM_VALGRIND_DEFINED_SPACE (block_p, sizeof (jmem_heap_free_t));
JMEM_VALGRIND_DEFINED_SPACE (next_p, sizeof (jmem_heap_free_t));
const uint32_t block_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (block_p);
/* Update prev. */
if (jmem_heap_get_region_end (prev_p) == block_p)
{
/* Can be merged. */
prev_p->size += (uint32_t) size;
JMEM_VALGRIND_NOACCESS_SPACE (block_p, sizeof (jmem_heap_free_t));
block_p = prev_p;
}
else
{
block_p->size = (uint32_t) size;
prev_p->next_offset = block_offset;
}
/* Update next. */
if (jmem_heap_get_region_end (block_p) == next_p)
{
/* Can be merged. */
block_p->size += next_p->size;
block_p->next_offset = next_p->next_offset;
}
else
{
block_p->next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (next_p);
}
JERRY_CONTEXT (jmem_heap_list_skip_p) = prev_p;
JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t));
JMEM_VALGRIND_NOACCESS_SPACE (block_p, sizeof (jmem_heap_free_t));
JMEM_VALGRIND_NOACCESS_SPACE (next_p, sizeof (jmem_heap_free_t));
} /* jmem_heap_insert_block */
#endif /* !JERRY_SYSTEM_ALLOCATOR */
/**
* Internal method for freeing a memory block.
*/
void JERRY_ATTR_HOT
jmem_heap_free_block_internal (void *ptr, /**< pointer to beginning of data space of the block */
const size_t size) /**< size of allocated region */
{
JERRY_ASSERT (size > 0);
JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_limit) >= JERRY_CONTEXT (jmem_heap_allocated_size));
JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_allocated_size) > 0);
#if !JERRY_SYSTEM_ALLOCATOR
/* checking that ptr points to the heap */
JERRY_ASSERT (jmem_is_heap_pointer (ptr));
JERRY_ASSERT ((uintptr_t) ptr % JMEM_ALIGNMENT == 0);
const size_t aligned_size = (size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT * JMEM_ALIGNMENT;
jmem_heap_free_t *const block_p = (jmem_heap_free_t *) ptr;
jmem_heap_free_t *const prev_p = jmem_heap_find_prev (block_p);
jmem_heap_insert_block (block_p, prev_p, aligned_size);
JERRY_CONTEXT (jmem_heap_allocated_size) -= aligned_size;
JMEM_VALGRIND_FREELIKE_SPACE (ptr);
#else /* JERRY_SYSTEM_ALLOCATOR */
JERRY_CONTEXT (jmem_heap_allocated_size) -= size;
free (ptr);
#endif /* !JERRY_SYSTEM_ALLOCATOR */
while (JERRY_CONTEXT (jmem_heap_allocated_size) + CONFIG_GC_LIMIT <= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) -= CONFIG_GC_LIMIT;
}
JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_limit) >= JERRY_CONTEXT (jmem_heap_allocated_size));
} /* jmem_heap_free_block_internal */
/**
* Reallocates the memory region pointed to by 'ptr', changing the size of the allocated region.
*
* @return pointer to the reallocated region
*/
void *JERRY_ATTR_HOT
jmem_heap_realloc_block (void *ptr, /**< memory region to reallocate */
const size_t old_size, /**< current size of the region */
const size_t new_size) /**< desired new size */
{
#if !JERRY_SYSTEM_ALLOCATOR
JERRY_ASSERT (jmem_is_heap_pointer (ptr));
JERRY_ASSERT ((uintptr_t) ptr % JMEM_ALIGNMENT == 0);
JERRY_ASSERT (old_size != 0);
JERRY_ASSERT (new_size != 0);
jmem_heap_free_t *const block_p = (jmem_heap_free_t *) ptr;
const size_t aligned_new_size = (new_size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT * JMEM_ALIGNMENT;
const size_t aligned_old_size = (old_size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT * JMEM_ALIGNMENT;
if (aligned_old_size == aligned_new_size)
{
JMEM_VALGRIND_RESIZE_SPACE (block_p, old_size, new_size);
JMEM_HEAP_STAT_FREE (old_size);
JMEM_HEAP_STAT_ALLOC (new_size);
return block_p;
}
if (aligned_new_size < aligned_old_size)
{
JMEM_VALGRIND_RESIZE_SPACE (block_p, old_size, new_size);
JMEM_HEAP_STAT_FREE (old_size);
JMEM_HEAP_STAT_ALLOC (new_size);
jmem_heap_insert_block ((jmem_heap_free_t *) ((uint8_t *) block_p + aligned_new_size),
jmem_heap_find_prev (block_p),
aligned_old_size - aligned_new_size);
JERRY_CONTEXT (jmem_heap_allocated_size) -= (aligned_old_size - aligned_new_size);
while (JERRY_CONTEXT (jmem_heap_allocated_size) + CONFIG_GC_LIMIT <= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) -= CONFIG_GC_LIMIT;
}
return block_p;
}
void *ret_block_p = NULL;
const size_t required_size = aligned_new_size - aligned_old_size;
#if !JERRY_MEM_GC_BEFORE_EACH_ALLOC
if (JERRY_CONTEXT (jmem_heap_allocated_size) + required_size >= JERRY_CONTEXT (jmem_heap_limit))
{
ecma_free_unused_memory (JMEM_PRESSURE_LOW);
}
#else /* !JERRY_MEM_GC_BEFORE_EACH_ALLOC */
ecma_gc_run ();
#endif /* JERRY_MEM_GC_BEFORE_EACH_ALLOC */
jmem_heap_free_t *prev_p = jmem_heap_find_prev (block_p);
JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t));
jmem_heap_free_t *const next_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (prev_p->next_offset);
/* Check if block can be extended at the end */
if (((jmem_heap_free_t *) ((uint8_t *) block_p + aligned_old_size)) == next_p)
{
JMEM_VALGRIND_DEFINED_SPACE (next_p, sizeof (jmem_heap_free_t));
if (required_size <= next_p->size)
{
/* Block can be extended, update the list. */
if (required_size == next_p->size)
{
prev_p->next_offset = next_p->next_offset;
}
else
{
jmem_heap_free_t *const new_next_p = (jmem_heap_free_t *) ((uint8_t *) next_p + required_size);
JMEM_VALGRIND_DEFINED_SPACE (new_next_p, sizeof (jmem_heap_free_t));
new_next_p->next_offset = next_p->next_offset;
new_next_p->size = (uint32_t) (next_p->size - required_size);
JMEM_VALGRIND_NOACCESS_SPACE (new_next_p, sizeof (jmem_heap_free_t));
prev_p->next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (new_next_p);
}
/* next_p will be marked as undefined space. */
JMEM_VALGRIND_RESIZE_SPACE (block_p, old_size, new_size);
ret_block_p = block_p;
}
else
{
JMEM_VALGRIND_NOACCESS_SPACE (next_p, sizeof (jmem_heap_free_t));
}
JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t));
}
/*
* Check if block can be extended at the front.
* This is less optimal because we need to copy the data, but still better than allocting a new block.
*/
else if (jmem_heap_get_region_end (prev_p) == block_p)
{
if (required_size <= prev_p->size)
{
if (required_size == prev_p->size)
{
JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t));
prev_p = jmem_heap_find_prev (prev_p);
JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t));
prev_p->next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (next_p);
}
else
{
prev_p->size = (uint32_t) (prev_p->size - required_size);
}
JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t));
ret_block_p = (uint8_t *) block_p - required_size;
/* Mark the the new block as undefined so that we are able to write to it. */
JMEM_VALGRIND_UNDEFINED_SPACE (ret_block_p, old_size);
/* The blocks are likely to overlap, so mark the old block as defined memory again. */
JMEM_VALGRIND_DEFINED_SPACE (block_p, old_size);
memmove (ret_block_p, block_p, old_size);
JMEM_VALGRIND_FREELIKE_SPACE (block_p);
JMEM_VALGRIND_MALLOCLIKE_SPACE (ret_block_p, new_size);
JMEM_VALGRIND_DEFINED_SPACE (ret_block_p, old_size);
}
else
{
JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t));
}
}
if (ret_block_p != NULL)
{
/* Managed to extend the block. Update memory usage and the skip pointer. */
JERRY_CONTEXT (jmem_heap_list_skip_p) = prev_p;
JERRY_CONTEXT (jmem_heap_allocated_size) += required_size;
while (JERRY_CONTEXT (jmem_heap_allocated_size) >= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) += CONFIG_GC_LIMIT;
}
}
else
{
/* Could not extend block. Allocate new region and copy the data. */
/* jmem_heap_alloc_block_internal will adjust the allocated_size, but insert_block will not,
so we reduce it here first, so that the limit calculation remains consistent. */
JERRY_CONTEXT (jmem_heap_allocated_size) -= aligned_old_size;
ret_block_p = jmem_heap_alloc_block_internal (new_size);
/* jmem_heap_alloc_block_internal may trigger garbage collection, which can create new free blocks
* in the heap structure, so we need to look up the previous block again. */
prev_p = jmem_heap_find_prev (block_p);
memcpy (ret_block_p, block_p, old_size);
jmem_heap_insert_block (block_p, prev_p, aligned_old_size);
/* jmem_heap_alloc_block_internal will call JMEM_VALGRIND_MALLOCLIKE_SPACE */
JMEM_VALGRIND_FREELIKE_SPACE (block_p);
}
JMEM_HEAP_STAT_FREE (old_size);
JMEM_HEAP_STAT_ALLOC (new_size);
return ret_block_p;
#else /* JERRY_SYSTEM_ALLOCATOR */
const size_t required_size = new_size - old_size;
#if !JERRY_MEM_GC_BEFORE_EACH_ALLOC
if (JERRY_CONTEXT (jmem_heap_allocated_size) + required_size >= JERRY_CONTEXT (jmem_heap_limit))
{
ecma_free_unused_memory (JMEM_PRESSURE_LOW);
}
#else /* !JERRY_MEM_GC_BEFORE_EACH_ALLOC */
ecma_gc_run ();
#endif /* JERRY_MEM_GC_BEFORE_EACH_ALLOC */
JERRY_CONTEXT (jmem_heap_allocated_size) += required_size;
while (JERRY_CONTEXT (jmem_heap_allocated_size) >= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) += CONFIG_GC_LIMIT;
}
while (JERRY_CONTEXT (jmem_heap_allocated_size) + CONFIG_GC_LIMIT <= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) -= CONFIG_GC_LIMIT;
}
JMEM_HEAP_STAT_FREE (old_size);
JMEM_HEAP_STAT_ALLOC (new_size);
return realloc (ptr, new_size);
#endif /* !JERRY_SYSTEM_ALLOCATOR */
} /* jmem_heap_realloc_block */
/**
* Free memory block
*/
extern inline void JERRY_ATTR_HOT JERRY_ATTR_ALWAYS_INLINE
jmem_heap_free_block (void *ptr, /**< pointer to beginning of data space of the block */
const size_t size) /**< size of allocated region */
{
jmem_heap_free_block_internal (ptr, size);
JMEM_HEAP_STAT_FREE (size);
return;
} /* jmem_heap_free_block */
#ifndef JERRY_NDEBUG
/**
* Check whether the pointer points to the heap
*
* Note:
* the routine should be used only for assertion checks
*
* @return true - if pointer points to the heap,
* false - otherwise
*/
bool
jmem_is_heap_pointer (const void *pointer) /**< pointer */
{
#if !JERRY_SYSTEM_ALLOCATOR
return ((uint8_t *) pointer >= JERRY_HEAP_CONTEXT (area)
&& (uint8_t *) pointer <= (JERRY_HEAP_CONTEXT (area) + JMEM_HEAP_AREA_SIZE));
#else /* JERRY_SYSTEM_ALLOCATOR */
JERRY_UNUSED (pointer);
return true;
#endif /* !JERRY_SYSTEM_ALLOCATOR */
} /* jmem_is_heap_pointer */
#endif /* !JERRY_NDEBUG */
#if JERRY_MEM_STATS
/**
* Get heap memory usage statistics
*/
void
jmem_heap_get_stats (jmem_heap_stats_t *out_heap_stats_p) /**< [out] heap stats */
{
JERRY_ASSERT (out_heap_stats_p != NULL);
*out_heap_stats_p = JERRY_CONTEXT (jmem_heap_stats);
} /* jmem_heap_get_stats */
/**
* Print heap memory usage statistics
*/
void
jmem_heap_stats_print (void)
{
jmem_heap_stats_t *heap_stats = &JERRY_CONTEXT (jmem_heap_stats);
JERRY_DEBUG_MSG ("Heap stats:\n");
#if !JERRY_SYSTEM_ALLOCATOR
JERRY_DEBUG_MSG (" Heap size = %zu bytes\n", heap_stats->size);
#endif /* !JERRY_SYSTEM_ALLOCATOR */
JERRY_DEBUG_MSG (" Allocated = %zu bytes\n"
" Peak allocated = %zu bytes\n"
" Waste = %zu bytes\n"
" Peak waste = %zu bytes\n"
" Allocated byte code data = %zu bytes\n"
" Peak allocated byte code data = %zu bytes\n"
" Allocated string data = %zu bytes\n"
" Peak allocated string data = %zu bytes\n"
" Allocated object data = %zu bytes\n"
" Peak allocated object data = %zu bytes\n"
" Allocated property data = %zu bytes\n"
" Peak allocated property data = %zu bytes\n",
heap_stats->allocated_bytes,
heap_stats->peak_allocated_bytes,
heap_stats->waste_bytes,
heap_stats->peak_waste_bytes,
heap_stats->byte_code_bytes,
heap_stats->peak_byte_code_bytes,
heap_stats->string_bytes,
heap_stats->peak_string_bytes,
heap_stats->object_bytes,
heap_stats->peak_object_bytes,
heap_stats->property_bytes,
heap_stats->peak_property_bytes);
} /* jmem_heap_stats_print */
/**
* Initalize heap memory usage statistics account structure
*/
void
jmem_heap_stat_init (void)
{
#if !JERRY_SYSTEM_ALLOCATOR
JERRY_CONTEXT (jmem_heap_stats).size = JMEM_HEAP_AREA_SIZE;
#endif /* !JERRY_SYSTEM_ALLOCATOR */
} /* jmem_heap_stat_init */
/**
* Account allocation
*/
void
jmem_heap_stat_alloc (size_t size) /**< Size of allocated block */
{
const size_t aligned_size = (size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT * JMEM_ALIGNMENT;
const size_t waste_bytes = aligned_size - size;
jmem_heap_stats_t *heap_stats = &JERRY_CONTEXT (jmem_heap_stats);
heap_stats->allocated_bytes += aligned_size;
heap_stats->waste_bytes += waste_bytes;
if (heap_stats->allocated_bytes > heap_stats->peak_allocated_bytes)
{
heap_stats->peak_allocated_bytes = heap_stats->allocated_bytes;
}
if (heap_stats->waste_bytes > heap_stats->peak_waste_bytes)
{
heap_stats->peak_waste_bytes = heap_stats->waste_bytes;
}
} /* jmem_heap_stat_alloc */
/**
* Account freeing
*/
void
jmem_heap_stat_free (size_t size) /**< Size of freed block */
{
const size_t aligned_size = (size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT * JMEM_ALIGNMENT;
const size_t waste_bytes = aligned_size - size;
jmem_heap_stats_t *heap_stats = &JERRY_CONTEXT (jmem_heap_stats);
heap_stats->allocated_bytes -= aligned_size;
heap_stats->waste_bytes -= waste_bytes;
} /* jmem_heap_stat_free */
#endif /* JERRY_MEM_STATS */
/**
* @}
* @}
*/