On Mon Jul 6, 2026 at 6:10 PM UTC, Tristan Partin wrote: > Postgres memory allocators, by default, ERROR out on memory allocation > failures. An ERROR leads to a longjmp, which means that the caller of > the allocator will never see a NULL return value. We can explicitly let > the compiler know about this behavior by adding the returns_nonnull > attribute to the allocators that follow this behavior. Postgres does > support _extended versions of some of the allocators that take a flaks > argument. The caller can provide the MCXT_ALLOC_NO_OOM flag to these > allocators to request that they return NULL on allocation failure > instead of ERROR-ing out. The _extended allocators cannot be marked as > returns_nonnull because of that. > > By using returns_nonnull, we can help the compiler to optimize call > sites.
... -- Tristan Partin PostgreSQL Contributors Team AWS (https://aws.amazon.com)
From a1286df5e39faf4b58535c5a01f0bd69780c2d78 Mon Sep 17 00:00:00 2001 From: Tristan Partin <[email protected]> Date: Mon, 6 Jul 2026 17:55:00 +0000 Subject: [PATCH v1] Add returns_nonnull attribute to infallible allocators Postgres memory allocators, by default, ERROR out on memory allocation failures. An ERROR leads to a longjmp, which means that the caller of the allocator will never see a NULL return value. We can explicitly let the compiler know about this behavior by adding the returns_nonnull attribute to the allocators that follow this behavior. Postgres does support _extended versions of some of the allocators that take a flags argument. The caller can provide the MCXT_ALLOC_NO_OOM flag to these allocators to request that they return NULL on allocation failure instead of ERROR-ing out. The _extended allocators cannot be marked as returns_nonnull because of that. By using returns_nonnull, we can help the compiler to optimize call sites. Signed-off-by: Tristan Partin <[email protected]> --- src/include/c.h | 10 ++++++++++ src/include/common/fe_memutils.h | 32 +++++++++++++++--------------- src/include/utils/palloc.h | 34 ++++++++++++++++---------------- 3 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/include/c.h b/src/include/c.h index 0e4aea5d5a3..8011775c81b 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -285,6 +285,16 @@ extern "C++" #define pg_attribute_nonnull(...) #endif +/* + * pg_attribute_returns_nonnull lets the compiler optimize function callers + * based on the knowledge that the return value will never be NULL. + */ +#if __has_attribute (returns_nonnull) +#define pg_attribute_returns_nonnull __attribute__((returns_nonnull)) +#else +#define pg_attribute_returns_nonnull +#endif + /* * pg_attribute_target allows specifying different target options that the * function should be compiled with (e.g., for using special CPU instructions). diff --git a/src/include/common/fe_memutils.h b/src/include/common/fe_memutils.h index d5c6d37bb66..c86966a95cc 100644 --- a/src/include/common/fe_memutils.h +++ b/src/include/common/fe_memutils.h @@ -34,11 +34,11 @@ * "Safe" memory allocation functions --- these exit(1) on failure * (except pg_malloc_extended with MCXT_ALLOC_NO_OOM) */ -extern char *pg_strdup(const char *in); -extern void *pg_malloc(size_t size); -extern void *pg_malloc0(size_t size); +extern char *pg_strdup(const char *in) pg_attribute_returns_nonnull; +extern void *pg_malloc(size_t size) pg_attribute_returns_nonnull; +extern void *pg_malloc0(size_t size) pg_attribute_returns_nonnull; extern void *pg_malloc_extended(size_t size, int flags); -extern void *pg_realloc(void *ptr, size_t size); +extern void *pg_realloc(void *ptr, size_t size) pg_attribute_returns_nonnull; extern void pg_free(void *ptr); /* @@ -46,10 +46,10 @@ extern void pg_free(void *ptr); */ extern Size add_size(Size s1, Size s2); extern Size mul_size(Size s1, Size s2); -extern void *pg_malloc_mul(Size s1, Size s2); -extern void *pg_malloc0_mul(Size s1, Size s2); +extern void *pg_malloc_mul(Size s1, Size s2) pg_attribute_returns_nonnull; +extern void *pg_malloc0_mul(Size s1, Size s2) pg_attribute_returns_nonnull; extern void *pg_malloc_mul_extended(Size s1, Size s2, int flags); -extern void *pg_realloc_mul(void *p, Size s1, Size s2); +extern void *pg_realloc_mul(void *p, Size s1, Size s2) pg_attribute_returns_nonnull; /* * Variants with easier notation and more type safety @@ -75,17 +75,17 @@ extern void *pg_realloc_mul(void *p, Size s1, Size s2); #define pg_realloc_array(pointer, type, count) ((type *) pg_realloc_mul(pointer, sizeof(type), count)) /* Equivalent functions, deliberately named the same as backend functions */ -extern char *pstrdup(const char *in); -extern char *pnstrdup(const char *in, Size size); -extern void *palloc(Size size); -extern void *palloc0(Size size); +extern char *pstrdup(const char *in) pg_attribute_returns_nonnull; +extern char *pnstrdup(const char *in, Size size) pg_attribute_returns_nonnull; +extern void *palloc(Size size) pg_attribute_returns_nonnull; +extern void *palloc0(Size size) pg_attribute_returns_nonnull; extern void *palloc_extended(Size size, int flags); -extern void *repalloc(void *pointer, Size size); +extern void *repalloc(void *pointer, Size size) pg_attribute_returns_nonnull; extern void pfree(void *pointer); -extern void *palloc_mul(Size s1, Size s2); -extern void *palloc0_mul(Size s1, Size s2); +extern void *palloc_mul(Size s1, Size s2) pg_attribute_returns_nonnull; +extern void *palloc0_mul(Size s1, Size s2) pg_attribute_returns_nonnull; extern void *palloc_mul_extended(Size s1, Size s2, int flags); -extern void *repalloc_mul(void *p, Size s1, Size s2); +extern void *repalloc_mul(void *p, Size s1, Size s2) pg_attribute_returns_nonnull; #define palloc_object(type) ((type *) palloc(sizeof(type))) #define palloc0_object(type) ((type *) palloc0(sizeof(type))) @@ -95,7 +95,7 @@ extern void *repalloc_mul(void *p, Size s1, Size s2); #define repalloc_array(pointer, type, count) ((type *) repalloc_mul(pointer, sizeof(type), count)) /* sprintf into a palloc'd buffer --- these are in psprintf.c */ -extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2); +extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2) pg_attribute_returns_nonnull; extern size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) pg_attribute_printf(3, 0); #endif /* FE_MEMUTILS_H */ diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h index 0e934158b60..3496b375433 100644 --- a/src/include/utils/palloc.h +++ b/src/include/utils/palloc.h @@ -68,21 +68,21 @@ extern PGDLLIMPORT MemoryContext CurrentMemoryContext; /* * Fundamental memory-allocation operations (more are in utils/memutils.h) */ -extern void *MemoryContextAlloc(MemoryContext context, Size size); -extern void *MemoryContextAllocZero(MemoryContext context, Size size); +extern void *MemoryContextAlloc(MemoryContext context, Size size) pg_attribute_returns_nonnull; +extern void *MemoryContextAllocZero(MemoryContext context, Size size) pg_attribute_returns_nonnull; extern void *MemoryContextAllocExtended(MemoryContext context, Size size, int flags); extern void *MemoryContextAllocAligned(MemoryContext context, Size size, Size alignto, int flags); -extern void *palloc(Size size); -extern void *palloc0(Size size); +extern void *palloc(Size size) pg_attribute_returns_nonnull; +extern void *palloc0(Size size) pg_attribute_returns_nonnull; extern void *palloc_extended(Size size, int flags); extern void *palloc_aligned(Size size, Size alignto, int flags); -pg_nodiscard extern void *repalloc(void *pointer, Size size); +pg_nodiscard extern void *repalloc(void *pointer, Size size) pg_attribute_returns_nonnull; pg_nodiscard extern void *repalloc_extended(void *pointer, Size size, int flags); -pg_nodiscard extern void *repalloc0(void *pointer, Size oldsize, Size size); +pg_nodiscard extern void *repalloc0(void *pointer, Size oldsize, Size size) pg_attribute_returns_nonnull; extern void pfree(void *pointer); /* @@ -90,10 +90,10 @@ extern void pfree(void *pointer); */ extern Size add_size(Size s1, Size s2); extern Size mul_size(Size s1, Size s2); -extern void *palloc_mul(Size s1, Size s2); -extern void *palloc0_mul(Size s1, Size s2); -extern void *palloc_mul_extended(Size s1, Size s2, int flags); -pg_nodiscard extern void *repalloc_mul(void *p, Size s1, Size s2); +extern void *palloc_mul(Size s1, Size s2) pg_attribute_returns_nonnull; +extern void *palloc0_mul(Size s1, Size s2) pg_attribute_returns_nonnull; +extern void *palloc_mul_extended(Size s1, Size s2, int flags) pg_attribute_returns_nonnull; +pg_nodiscard extern void *repalloc_mul(void *p, Size s1, Size s2) pg_attribute_returns_nonnull; pg_nodiscard extern void *repalloc_mul_extended(void *p, Size s1, Size s2, int flags); @@ -123,8 +123,8 @@ pg_nodiscard extern void *repalloc_mul_extended(void *p, Size s1, Size s2, #define repalloc_array_extended(pointer, type, count, flags) ((type *) repalloc_mul_extended(pointer, sizeof(type), count, flags)) /* Higher-limit allocators. */ -extern void *MemoryContextAllocHuge(MemoryContext context, Size size); -pg_nodiscard extern void *repalloc_huge(void *pointer, Size size); +extern void *MemoryContextAllocHuge(MemoryContext context, Size size) pg_attribute_returns_nonnull; +pg_nodiscard extern void *repalloc_huge(void *pointer, Size size) pg_attribute_returns_nonnull; /* * Although this header file is nominally backend-only, certain frontend @@ -154,14 +154,14 @@ extern void MemoryContextUnregisterResetCallback(MemoryContext context, * These are like standard strdup() except the copied string is * allocated in a context, not with malloc(). */ -extern char *MemoryContextStrdup(MemoryContext context, const char *string); -extern char *pstrdup(const char *in); -extern char *pnstrdup(const char *in, Size len); +extern char *MemoryContextStrdup(MemoryContext context, const char *string) pg_attribute_returns_nonnull; +extern char *pstrdup(const char *in) pg_attribute_returns_nonnull; +extern char *pnstrdup(const char *in, Size len) pg_attribute_returns_nonnull; -extern char *pchomp(const char *in); +extern char *pchomp(const char *in) pg_attribute_returns_nonnull; /* sprintf into a palloc'd buffer --- these are in psprintf.c */ -extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2); +extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2) pg_attribute_returns_nonnull; extern size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) pg_attribute_printf(3, 0); #endif /* PALLOC_H */ -- Tristan Partin https://tristan.partin.io
