gmem: Add an inline definition of g_free() to automatically use g_free_sized()

Marco Trevisan requested to merge 3v1n0/glib:free-sized-macro into main

When using GCC we can take the advantage of __builtin_object_size() to know the allocated size of a memory area, this generally only works when some optimization level enabled (-O1 seems enough here) and can provide us with memory size information for lower-level optimizations.

--

This can't be tested directly easily, unless we do a temporary copy of the macro, but we can trust the compiler.

A simple stand-alone test is:

/* Compile with -O1 at least */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void
free_sized(void *ptr, size_t size)
{
    printf("Freeing %p of size %lu\n", ptr, size);
    free (ptr);
}

static void
free_unsized(void *ptr)
{
    printf("Freeing %p of unknown size\n", ptr);
    free (ptr);
}

#define free_value(v) \
  __builtin_object_size (v, 0) != ((size_t) - 1) ? \
    free_sized (v, __builtin_object_size (v, 0)) : free_unsized (v)

typedef struct {
    char *bar;
    int foo;
    unsigned baz;
} MyData;

typedef struct _MyUnknownData MyUnknownData;

void *malloc(size_t s) __attribute__((alloc_size (1)));

int main(void)
{
    char *fooo = strdup("fooo bar");
    free_value (fooo);

    MyData data = {0};
    MyData **data_array = malloc(sizeof(MyData) * 10);
    free_value (data_array);

    MyData *dataptr = malloc(sizeof(MyData));
    dataptr = memcpy(dataptr, &data, sizeof(MyData));
    free_value (dataptr);

    MyUnknownData *unk = NULL;
    free_value(unk);

    return 0;
}

which correctly outputs:

Freeing 0x56385f6432a0 of size 9
Freeing 0x56385f6436d0 of size 160
Freeing 0x56385f6432a0 of size 16
Freeing (nil) of unknown size

Generated code is:

int main(void)
{
  char *__ptr;
  void *__ptr_00;
  undefined (*__ptr_01) [16];
  
  __ptr = strdup("fooo bar");
  printf("Freeing %p of size %lu\n",__ptr,9);
  free(__ptr);
  __ptr_00 = malloc(0xa0);
  printf("Freeing %p of size %lu\n",__ptr_00,0xa0);
  free(__ptr_00);
  __ptr_01 = (undefined (*) [16])malloc(0x10);
  *__ptr_01 = ZEXT816(0);
  printf("Freeing %p of size %lu\n",__ptr_01,0x10);
  free(__ptr_01);
  printf("Freeing %p of unknown size\n",0);
  return 0;
}

/cc @matthiasc

Edited by Marco Trevisan

Merge request reports