OpenFPGA/libs/libvtrutil/src/vtr_memory.cpp

178 lines
5.0 KiB
C++

#include <cstddef>
#include <cstdlib>
#include "vtr_assert.h"
#include "vtr_list.h"
#include "vtr_memory.h"
#include "vtr_error.h"
#include "vtr_util.h"
#ifndef __GLIBC__
#include <stdlib.h>
#else
#include <malloc.h>
#endif
namespace vtr {
#ifndef __GLIBC__
int malloc_trim(size_t /*pad*/) {
return 0;
}
#else
int malloc_trim(size_t pad) {
return ::malloc_trim(pad);
}
#endif
void* free(void *some){
if(some){
std::free(some);
some = nullptr;
}
return nullptr;
}
void* calloc(size_t nelem, size_t size) {
void *ret;
if (nelem == 0) {
return nullptr ;
}
if ((ret = std::calloc(nelem, size)) == nullptr ) {
throw VtrError("Unable to calloc memory.", __FILE__, __LINE__);
}
return (ret);
}
void* malloc(size_t size) {
void *ret;
if (size == 0) {
return nullptr ;
}
if ((ret = std::malloc(size)) == nullptr && size != 0) {
throw VtrError("Unable to malloc memory.", __FILE__, __LINE__);
}
return (ret);
}
void* realloc(void *ptr, size_t size) {
void *ret;
ret = std::realloc(ptr, size);
if (nullptr == ret && size != 0) {
throw VtrError(string_fmt("Unable to realloc memory (ptr=%p, size=%d).", ptr, size),
__FILE__, __LINE__);
}
return (ret);
}
void* chunk_malloc(size_t size, t_chunk *chunk_info) {
/* This routine should be used for allocating fairly small data *
* structures where memory-efficiency is crucial. This routine allocates *
* large "chunks" of data, and parcels them out as requested. Whenever *
* it mallocs a new chunk it adds it to the linked list pointed to by *
* chunk_info->chunk_ptr_head. This list can be used to free the *
* chunked memory. *
* Information about the currently open "chunk" must be stored by the *
* user program. chunk_info->mem_avail_ptr points to an int storing *
* how many bytes are left in the current chunk, while *
* chunk_info->next_mem_loc_ptr is the address of a pointer to the *
* next free bytes in the chunk. To start a new chunk, simply set *
* chunk_info->mem_avail_ptr = 0. Each independent set of data *
* structures should use a new chunk. */
/* To make sure the memory passed back is properly aligned, I must *
* only send back chunks in multiples of the worst-case alignment *
* restriction of the machine. On most machines this should be *
* a long, but on 64-bit machines it might be a long long or a *
* double. Change the typedef below if this is the case. */
typedef size_t Align;
constexpr int CHUNK_SIZE = 32768;
constexpr int FRAGMENT_THRESHOLD = 100;
char *tmp_ptr;
int aligned_size;
VTR_ASSERT(chunk_info->mem_avail >= 0);
if ((size_t) (chunk_info->mem_avail) < size) { /* Need to malloc more memory. */
if (size > CHUNK_SIZE) { /* Too big, use standard routine. */
tmp_ptr = (char *) vtr::malloc(size);
/* When debugging, uncomment the code below to see if memory allocation size */
/* makes sense */
//#ifdef DEBUG
// vtr_printf("NB: my_chunk_malloc got a request for %d bytes.\n", size);
// vtr_printf("You should consider using vtr::malloc for such big requests.\n");
// #endif
VTR_ASSERT(chunk_info != nullptr);
chunk_info->chunk_ptr_head = insert_in_vptr_list(
chunk_info->chunk_ptr_head, tmp_ptr);
return (tmp_ptr);
}
if (chunk_info->mem_avail < FRAGMENT_THRESHOLD) { /* Only a small scrap left. */
chunk_info->next_mem_loc_ptr = (char *) vtr::malloc(CHUNK_SIZE);
chunk_info->mem_avail = CHUNK_SIZE;
VTR_ASSERT(chunk_info != nullptr);
chunk_info->chunk_ptr_head = insert_in_vptr_list(
chunk_info->chunk_ptr_head, chunk_info->next_mem_loc_ptr);
}
/* Execute else clause only when the chunk we want is pretty big, *
* and would leave too big an unused fragment. Then we use malloc *
* to allocate normally. */
else {
tmp_ptr = (char *) vtr::malloc(size);
VTR_ASSERT(chunk_info != nullptr);
chunk_info->chunk_ptr_head = insert_in_vptr_list(
chunk_info->chunk_ptr_head, tmp_ptr);
return (tmp_ptr);
}
}
/* Find the smallest distance to advance the memory pointer and keep *
* everything aligned. */
if (size % sizeof(Align) == 0) {
aligned_size = size;
} else {
aligned_size = size + sizeof(Align) - size % sizeof(Align);
}
tmp_ptr = chunk_info->next_mem_loc_ptr;
chunk_info->next_mem_loc_ptr += aligned_size;
chunk_info->mem_avail -= aligned_size;
return (tmp_ptr);
}
void free_chunk_memory(t_chunk *chunk_info) {
/* Frees the memory allocated by a sequence of calls to my_chunk_malloc. */
t_linked_vptr *curr_ptr, *prev_ptr;
curr_ptr = chunk_info->chunk_ptr_head;
while (curr_ptr != nullptr ) {
free(curr_ptr->data_vptr); /* Free memory "chunk". */
prev_ptr = curr_ptr;
curr_ptr = curr_ptr->next;
free(prev_ptr); /* Free memory used to track "chunk". */
}
chunk_info->chunk_ptr_head = nullptr;
chunk_info->mem_avail = 0;
chunk_info->next_mem_loc_ptr = nullptr;
}
} //namespace