From b9669a832576ca7fb941c727e6ff46adb3fdcdbf Mon Sep 17 00:00:00 2001
From: Melih Mutlu <m.melihmutlu@gmail.com>
Date: Mon, 12 Jun 2023 08:50:28 +0300
Subject: [PATCH v2] Change memory context fields to uint32

Block sizes and chunk sizes can be upper bounded by
MEMORYCHUNK_MAX_BLOCKOFFSET and MEMORYCHUNK_MAX_VALUE respectively.
Values of both bounds correspond to 1 less than 1GB. This allows us
to store the sizes of any block or chunk size limited by those bounds
in 32 bits.

This patch changes types of such fields that represents block or chunk sizes
from 64-bit Size to 32-bit unsigned integers.

Also; keeper fields from AllocSet and Generation structs are removed and
added AllocSetKeeper/GenerationKeeper macros where needed.
---
 src/backend/utils/mmgr/aset.c       | 52 +++++++++++++++++------------
 src/backend/utils/mmgr/generation.c | 51 ++++++++++++++++------------
 src/backend/utils/mmgr/slab.c       |  6 ++--
 3 files changed, 62 insertions(+), 47 deletions(-)

diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c
index 0bbbf93672..797abef1b5 100644
--- a/src/backend/utils/mmgr/aset.c
+++ b/src/backend/utils/mmgr/aset.c
@@ -156,11 +156,10 @@ typedef struct AllocSetContext
 	AllocBlock	blocks;			/* head of list of blocks in this set */
 	MemoryChunk *freelist[ALLOCSET_NUM_FREELISTS];	/* free chunk lists */
 	/* Allocation parameters for this context: */
-	Size		initBlockSize;	/* initial block size */
-	Size		maxBlockSize;	/* maximum block size */
-	Size		nextBlockSize;	/* next block size to allocate */
-	Size		allocChunkLimit;	/* effective chunk size limit */
-	AllocBlock	keeper;			/* keep this block over resets */
+	uint32		initBlockSize;	/* initial block size */
+	uint32		maxBlockSize;	/* maximum block size */
+	uint32		nextBlockSize;	/* next block size to allocate */
+	uint32		allocChunkLimit;	/* effective chunk size limit */
 	/* freelist this context could be put in, or -1 if not a candidate: */
 	int			freeListIndex;	/* index in context_freelists[], or -1 */
 } AllocSetContext;
@@ -241,6 +240,14 @@ typedef struct AllocBlockData
  */
 #define MAX_FREE_CONTEXTS 100	/* arbitrary limit on freelist length */
 
+/* Obtain the keeper block for an allocation set */
+#define AllocSetKeeper(set) \
+	((AllocBlock) (((char *) set) + MAXALIGN(sizeof(AllocSetContext))))
+
+/* Check if the block is the keeper block of the given allocation set */
+#define IsKeeperBlock(set, block) \
+	((block) == (AllocSetKeeper(set)))
+
 typedef struct AllocSetFreeList
 {
 	int			num_free;		/* current list length */
@@ -345,7 +352,7 @@ AllocSetContextCreateInternal(MemoryContext parent,
 							  Size maxBlockSize)
 {
 	int			freeListIndex;
-	Size		firstBlockSize;
+	uint32		firstBlockSize;
 	AllocSet	set;
 	AllocBlock	block;
 
@@ -417,7 +424,7 @@ AllocSetContextCreateInternal(MemoryContext parent,
 								name);
 
 			((MemoryContext) set)->mem_allocated =
-				set->keeper->endptr - ((char *) set);
+				AllocSetKeeper(set)->endptr - ((char *) set);
 
 			return (MemoryContext) set;
 		}
@@ -452,8 +459,11 @@ AllocSetContextCreateInternal(MemoryContext parent,
 	 * we'd leak the header/initial block if we ereport in this stretch.
 	 */
 
-	/* Fill in the initial block's block header */
-	block = (AllocBlock) (((char *) set) + MAXALIGN(sizeof(AllocSetContext)));
+	/*
+	 * Fill in the initial block's block header. The initial block is not to be
+	 * released at reset time, considered as keeper block.
+	 */
+	block = AllocSetKeeper(set);
 	block->aset = set;
 	block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
 	block->endptr = ((char *) set) + firstBlockSize;
@@ -465,15 +475,13 @@ AllocSetContextCreateInternal(MemoryContext parent,
 
 	/* Remember block as part of block list */
 	set->blocks = block;
-	/* Mark block as not to be released at reset time */
-	set->keeper = block;
 
 	/* Finish filling in aset-specific parts of the context header */
 	MemSetAligned(set->freelist, 0, sizeof(set->freelist));
 
-	set->initBlockSize = initBlockSize;
-	set->maxBlockSize = maxBlockSize;
-	set->nextBlockSize = initBlockSize;
+	set->initBlockSize = (Size) initBlockSize;
+	set->maxBlockSize = (Size) maxBlockSize;
+	set->nextBlockSize = (Size) initBlockSize;
 	set->freeListIndex = freeListIndex;
 
 	/*
@@ -544,7 +552,7 @@ AllocSetReset(MemoryContext context)
 #endif
 
 	/* Remember keeper block size for Assert below */
-	keepersize = set->keeper->endptr - ((char *) set);
+	keepersize = AllocSetKeeper(set)->endptr - ((char *) set);
 
 	/* Clear chunk freelists */
 	MemSetAligned(set->freelist, 0, sizeof(set->freelist));
@@ -552,13 +560,13 @@ AllocSetReset(MemoryContext context)
 	block = set->blocks;
 
 	/* New blocks list will be just the keeper block */
-	set->blocks = set->keeper;
+	set->blocks = AllocSetKeeper(set);
 
 	while (block != NULL)
 	{
 		AllocBlock	next = block->next;
 
-		if (block == set->keeper)
+		if (IsKeeperBlock(set, block))
 		{
 			/* Reset the block, but don't return it to malloc */
 			char	   *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ;
@@ -614,7 +622,7 @@ AllocSetDelete(MemoryContext context)
 #endif
 
 	/* Remember keeper block size for Assert below */
-	keepersize = set->keeper->endptr - ((char *) set);
+	keepersize = AllocSetKeeper(set)->endptr - ((char *) set);
 
 	/*
 	 * If the context is a candidate for a freelist, put it into that freelist
@@ -663,14 +671,14 @@ AllocSetDelete(MemoryContext context)
 	{
 		AllocBlock	next = block->next;
 
-		if (block != set->keeper)
+		if (!IsKeeperBlock(set, block))
 			context->mem_allocated -= block->endptr - ((char *) block);
 
 #ifdef CLOBBER_FREED_MEMORY
 		wipe_mem(block, block->freeptr - ((char *) block));
 #endif
 
-		if (block != set->keeper)
+		if (!IsKeeperBlock(set, block))
 			free(block);
 
 		block = next;
@@ -1547,7 +1555,7 @@ AllocSetCheck(MemoryContext context)
 		long		nchunks = 0;
 		bool		has_external_chunk = false;
 
-		if (set->keeper == block)
+		if (IsKeeperBlock(set, block))
 			total_allocated += block->endptr - ((char *) set);
 		else
 			total_allocated += block->endptr - ((char *) block);
@@ -1557,7 +1565,7 @@ AllocSetCheck(MemoryContext context)
 		 */
 		if (!blk_used)
 		{
-			if (set->keeper != block)
+			if (!IsKeeperBlock(set, block))
 				elog(WARNING, "problem in alloc set %s: empty block %p",
 					 name, block);
 		}
diff --git a/src/backend/utils/mmgr/generation.c b/src/backend/utils/mmgr/generation.c
index 4fb8663cd6..d6a0811a72 100644
--- a/src/backend/utils/mmgr/generation.c
+++ b/src/backend/utils/mmgr/generation.c
@@ -61,17 +61,16 @@ typedef struct GenerationContext
 	MemoryContextData header;	/* Standard memory-context fields */
 
 	/* Generational context parameters */
-	Size		initBlockSize;	/* initial block size */
-	Size		maxBlockSize;	/* maximum block size */
-	Size		nextBlockSize;	/* next block size to allocate */
-	Size		allocChunkLimit;	/* effective chunk size limit */
+	uint32		initBlockSize;	/* initial block size */
+	uint32		maxBlockSize;	/* maximum block size */
+	uint32		nextBlockSize;	/* next block size to allocate */
+	uint32		allocChunkLimit;	/* effective chunk size limit */
 
 	GenerationBlock *block;		/* current (most recently allocated) block, or
 								 * NULL if we've just freed the most recent
 								 * block */
 	GenerationBlock *freeblock; /* pointer to a block that's being recycled,
 								 * or NULL if there's no such block. */
-	GenerationBlock *keeper;	/* keep this block over resets */
 	dlist_head	blocks;			/* list of blocks */
 } GenerationContext;
 
@@ -120,6 +119,14 @@ struct GenerationBlock
 #define ExternalChunkGetBlock(chunk) \
 	(GenerationBlock *) ((char *) chunk - Generation_BLOCKHDRSZ)
 
+/* Obtain the keeper block for a generation context */
+#define GenerationKeeper(set) \
+	((GenerationBlock *) (((char *) set) + MAXALIGN(sizeof(GenerationContext))))
+
+/* Check if the block is the keeper block of the given generation context */
+#define IsKeeperBlock(set, block) \
+	((block) == (GenerationKeeper(set)))
+
 /* Inlined helper functions */
 static inline void GenerationBlockInit(GenerationContext *context,
 									   GenerationBlock *block,
@@ -153,8 +160,8 @@ GenerationContextCreate(MemoryContext parent,
 						Size initBlockSize,
 						Size maxBlockSize)
 {
-	Size		firstBlockSize;
-	Size		allocSize;
+	uint32		firstBlockSize;
+	uint32		allocSize;
 	GenerationContext *set;
 	GenerationBlock *block;
 
@@ -213,8 +220,11 @@ GenerationContextCreate(MemoryContext parent,
 	 */
 	dlist_init(&set->blocks);
 
-	/* Fill in the initial block's block header */
-	block = (GenerationBlock *) (((char *) set) + MAXALIGN(sizeof(GenerationContext)));
+	/*
+	 * Fill in the initial block's block header. The initial block is not to be
+	 * released at reset time, considered as keeper block.
+	 */
+	block = GenerationKeeper(set);
 	/* determine the block size and initialize it */
 	firstBlockSize = allocSize - MAXALIGN(sizeof(GenerationContext));
 	GenerationBlockInit(set, block, firstBlockSize);
@@ -228,13 +238,10 @@ GenerationContextCreate(MemoryContext parent,
 	/* No free block, yet */
 	set->freeblock = NULL;
 
-	/* Mark block as not to be released at reset time */
-	set->keeper = block;
-
 	/* Fill in GenerationContext-specific header fields */
-	set->initBlockSize = initBlockSize;
-	set->maxBlockSize = maxBlockSize;
-	set->nextBlockSize = initBlockSize;
+	set->initBlockSize = (Size) initBlockSize;
+	set->maxBlockSize = (Size) maxBlockSize;
+	set->nextBlockSize = (Size) initBlockSize;
 
 	/*
 	 * Compute the allocation chunk size limit for this context.
@@ -294,14 +301,14 @@ GenerationReset(MemoryContext context)
 	{
 		GenerationBlock *block = dlist_container(GenerationBlock, node, miter.cur);
 
-		if (block == set->keeper)
+		if (IsKeeperBlock(set, block))
 			GenerationBlockMarkEmpty(block);
 		else
 			GenerationBlockFree(set, block);
 	}
 
 	/* set it so new allocations to make use of the keeper block */
-	set->block = set->keeper;
+	set->block = GenerationKeeper(set);
 
 	/* Reset block size allocation sequence, too */
 	set->nextBlockSize = set->initBlockSize;
@@ -440,10 +447,10 @@ GenerationAlloc(MemoryContext context, Size size)
 			 */
 			set->freeblock = NULL;
 		}
-		else if (GenerationBlockIsEmpty(set->keeper) &&
-				 GenerationBlockFreeBytes(set->keeper) >= required_size)
+		else if (GenerationBlockIsEmpty(GenerationKeeper(set)) &&
+				 GenerationBlockFreeBytes(GenerationKeeper(set)) >= required_size)
 		{
-			block = set->keeper;
+			block = GenerationKeeper(set);
 		}
 		else
 		{
@@ -594,7 +601,7 @@ static inline void
 GenerationBlockFree(GenerationContext *set, GenerationBlock *block)
 {
 	/* Make sure nobody tries to free the keeper block */
-	Assert(block != set->keeper);
+	Assert(!IsKeeperBlock(set, block));
 	/* We shouldn't be freeing the freeblock either */
 	Assert(block != set->freeblock);
 
@@ -691,7 +698,7 @@ GenerationFree(void *pointer)
 	set = block->context;
 
 	/* Don't try to free the keeper block, just mark it empty */
-	if (block == set->keeper)
+	if (IsKeeperBlock(set, block))
 	{
 		GenerationBlockMarkEmpty(block);
 		return;
diff --git a/src/backend/utils/mmgr/slab.c b/src/backend/utils/mmgr/slab.c
index 718dd2ba03..d5e0edbede 100644
--- a/src/backend/utils/mmgr/slab.c
+++ b/src/backend/utils/mmgr/slab.c
@@ -104,7 +104,7 @@ typedef struct SlabContext
 {
 	MemoryContextData header;	/* Standard memory-context fields */
 	/* Allocation parameters for this context: */
-	Size		chunkSize;		/* the requested (non-aligned) chunk size */
+	uint32		chunkSize;		/* the requested (non-aligned) chunk size */
 	Size		fullChunkSize;	/* chunk size with chunk header and alignment */
 	Size		blockSize;		/* the size to make each block of chunks */
 	int32		chunksPerBlock; /* number of chunks that fit in 1 block */
@@ -374,7 +374,7 @@ SlabContextCreate(MemoryContext parent,
 	 */
 
 	/* Fill in SlabContext-specific header fields */
-	slab->chunkSize = chunkSize;
+	slab->chunkSize = (Size) chunkSize;
 	slab->fullChunkSize = fullChunkSize;
 	slab->blockSize = blockSize;
 	slab->chunksPerBlock = chunksPerBlock;
@@ -506,7 +506,7 @@ SlabAlloc(MemoryContext context, Size size)
 
 	/* make sure we only allow correct request size */
 	if (unlikely(size != slab->chunkSize))
-		elog(ERROR, "unexpected alloc chunk size %zu (expected %zu)",
+		elog(ERROR, "unexpected alloc chunk size %zu (expected %u)",
 			 size, slab->chunkSize);
 
 	/*
-- 
2.25.1

