5656
5757#define BOTTOM_BIT (s ) (((size_t) (s)) & -(size_t) (s))
5858
59+ /*
60+ We want to ensure that every block we hand out is properly aligned for the
61+ object it will be storing. Trying to honor all required alignments without
62+ wasting too much memory would complicate things dramatically, so we make some
63+ assumptions. We assume there is a MAX_ALIGNMENT and a NOT_MULTIPLE_ALIGNMENT.
64+ Any allocation with size that's a multiple of MAX_ALIGNMENT will be assumed
65+ to require no more than that alignment. Other sizes will be assumed to require
66+ no more than NOT_MULTIPLE_ALIGNMENT, though we'll consider that they may require
67+ less (if they're smaller than NOT_MULTIPLE_ALIGNMENT).
68+ */
69+
70+ /*
71+ Determine MAX_ALIGNMENT and NOT_MULTIPLE_ALIGNMENT. If types
72+ were provided then use their alignments; otherwise try to infer the maximum
73+ values on the system.
74+ */
5975#if defined(FXF_MAX_ALIGNMENT_TYPE )
6076# define MAX_ALIGNMENT ALIGNMENT_OF_TYPE(FXF_MAX_ALIGNMENT_TYPE)
6177#else /*FXF_MAX_ALIGNMENT_TYPE*/
@@ -161,8 +177,19 @@ typedef struct {
161177/* TODO: Is the above a good default, sufficiently large for all of our needs without being excessive? */
162178#endif
163179
180+ /*
181+ Determine the maximum amount we'll allow one to allocate. We need to ensure that it's
182+ a multiple of its required alignment, and we're OK with it being overaligned, so we
183+ round it up to the next alignment multiple.
184+ */
164185#define DESIRED_MAX_ALLOC_ALIGNMENT ((FXF_DESIRED_MAX_ALLOC < MAX_ALIGNMENT) ? NOT_MULTIPLE_ALIGNMENT : MAX_ALIGNMENT)
165186#define ROUNDED_DESIRED_MAXIMUM_ALLOC ROUND_UP_TO_ALIGNMENT(FXF_DESIRED_MAX_ALLOC, DESIRED_MAX_ALLOC_ALIGNMENT)
187+
188+ /*
189+ Now determine the actual minimum and maximum allocations. The minimum is new.
190+ We determined the maximum above, but of course we can't allocate more than
191+ a full segment. (Segment sizes should already be maximally aligned.)
192+ */
166193enum
167194{
168195 fxfMINSIZE = sizeof (void * ), /* Different size of fxfMINSIZE for 32-/64/Bit compilation */
@@ -173,26 +200,65 @@ enum
173200#endif
174201};
175202
176- enum {
177- ENSURE_FXFMINSIZE_GT_0 = 1 /(fxfMINSIZE > 0 ),
178- ENSURE_FXFMAXSIZE_GE_FXFMINSIZE = 1 /(fxfMAXSIZE >= fxfMINSIZE ),
203+ STATIC_ASSERT (fxfMINSIZE > 0 , "fxfMINSIZE must be > 0." );
204+ STATIC_ASSERT (fxfMAXSIZE >= fxfMINSIZE , "fxfMAXSIZE must be >= fxfMINSIZE." );
179205#if defined(SEGMENT )
180- ENSURE_SEGMENTS_ALIGNED = 1 /!((ARENA_SEG_SIZE & (((ARENA_SEG_SIZE < MAX_ALIGNMENT ) ? NOT_MULTIPLE_ALIGNMENT : MAX_ALIGNMENT ) - 1U )) &&
181- (ARENA_SEG_SIZE & (ARENA_SEG_SIZE - 1U ))),
206+ STATIC_ASSERT (!((ARENA_SEG_SIZE & (((ARENA_SEG_SIZE < MAX_ALIGNMENT ) ? NOT_MULTIPLE_ALIGNMENT :
207+ MAX_ALIGNMENT )
208+ - 1U )) &&
209+ (ARENA_SEG_SIZE & (ARENA_SEG_SIZE - 1U ))), "Segments must be aligned." );
182210#endif
183- ENSURE_FXFMAXSIZE_ALIGNED = 1 / ((!CLEAR_BOTTOM_BIT (fxfMAXSIZE )) ||
211+ STATIC_ASSERT ((!CLEAR_BOTTOM_BIT (fxfMAXSIZE )) ||
184212 ((fxfMAXSIZE < MAX_ALIGNMENT ) && !(fxfMAXSIZE & (NOT_MULTIPLE_ALIGNMENT - 1U ))) ||
185- !(fxfMAXSIZE & (MAX_ALIGNMENT - 1U ))),
186- ENSURE_ALIGNMENT_ORDERED = 1 /((NOT_MULTIPLE_ALIGNMENT > 0 ) && (NOT_MULTIPLE_ALIGNMENT <= MAX_ALIGNMENT )),
187- ENSURE_ALIGNMENTS_POWERS_OF_2 = 1 /!(CLEAR_BOTTOM_BIT (NOT_MULTIPLE_ALIGNMENT ) || CLEAR_BOTTOM_BIT (MAX_ALIGNMENT ))
188- };
189-
213+ !(fxfMAXSIZE & (MAX_ALIGNMENT - 1U )), "fxfMAXSIZE must be aligned." );
214+ STATIC_ASSERT ((NOT_MULTIPLE_ALIGNMENT > 0 ) && (NOT_MULTIPLE_ALIGNMENT <= MAX_ALIGNMENT ), "Alignments must be properly ordered." );
215+ STATIC_ASSERT (!(CLEAR_BOTTOM_BIT (NOT_MULTIPLE_ALIGNMENT ) || CLEAR_BOTTOM_BIT (MAX_ALIGNMENT )), "Alignments must be powers of 2." );
216+
217+ /*
218+ We'll be rounding allocation sizes to the next multiple of whatever the minimum
219+ alignment we will ever need is. We need to know that minimum alignment to
220+ create space for the free store. Unfortunately, it's hard to compute what we
221+ want under all circumstances at compile-time, espcially since this has to be
222+ correct after any rounding. For example, if NOT_MINIMUM_ALIGNMENT is 4 and
223+ fxfMINSIZE is 3 then
224+ 1. We'd start by assuming that an allocation of size 3 can't require an alignment
225+ greater than 2.
226+ 2. If we assume the minimum alignment is 2, then allocations will be rounded up
227+ to multiples of 2.
228+ 3. Step (2) would round the allocation of size 3 up to 4 bytes.
229+ 4. We'd then observe that an allocation of size 4 can require an alignment as large
230+ as 4.
231+ It seems that if fxfMINSIZE is < NOT_MINIMUM_ALIGNMENT then we want the largest
232+ power of 2 that is < 2 * fxfMINSIZE, but I don't see how to determine that at
233+ compile-time for an arbitrary fxfMINSIZE. What we compute here should be correct
234+ in many cases, and otherwise it should be <= that value; an underestimate here is OK.
235+ */
190236#define MIN_ALIGNMENT_UNDERESTIMATE (((NOT_MULTIPLE_ALIGNMENT>>1) < fxfMINSIZE) ? NOT_MULTIPLE_ALIGNMENT : \
191237 (CLEAR_BOTTOM_BIT(fxfMINSIZE) ? (BOTTOM_BIT(fxfMINSIZE)<<2) : \
192238 fxfMINSIZE))
239+ /*
240+ This will store the actual minimum alignment. We'll compute it at run-time.
241+ */
193242static size_t min_alignment = NOT_MULTIPLE_ALIGNMENT ; /* for now */
194243
244+
245+ /*
246+ This rounds fxfMINSIZE up to a multiple of MIN_ALIGNMENT_UNDERESTIMATE. This should be <= whatever
247+ fxfMINSIZE gets rounded to at run-time, since MIN_ALIGNMENT_UNDERESTIMATE will be <= the computed
248+ min_alignment.
249+ */
195250#define ROUNDED_MIN_SIZE_UNDERESTIMATE ROUND_UP_TO_ALIGNMENT(fxfMINSIZE, MIN_ALIGNMENT_UNDERESTIMATE)
251+
252+ /*
253+ We can now create the array for the free store heads. We need to have a head for sizes between
254+ [whatever fxfMINSIZE gets rounded to, fxfMAXSIZE]
255+ that are multiples of min_alignment. Additional entries aren't a problem, so we'll create entries
256+ for every multiple of (the possible
257+ underestimate) MIN_ALIGNMENT_UNDERESTIMATE in the
258+ [ROUNDED_MIN_SIZE_UNDERESTIMATE, fxfMAXSIZE].
259+ SIZEDATA_SIZE_TO_INDEX maps these values into {0, 1, 2, ..., N}, and we use N to determine how many
260+ heads we need.
261+ */
196262#define SIZEDATA_SIZE_TO_INDEX (s ) (((s) - ROUNDED_MIN_SIZE_UNDERESTIMATE)/MIN_ALIGNMENT_UNDERESTIMATE)
197263#define SIZEDATA_INDEX_TO_SIZE (x ) ((size_t)(((x) * MIN_ALIGNMENT_UNDERESTIMATE) + ROUNDED_MIN_SIZE_UNDERESTIMATE))
198264static SizeHead SizeData [1 + SIZEDATA_SIZE_TO_INDEX (fxfMAXSIZE )];
@@ -383,7 +449,7 @@ size_t fxfInit(size_t Size) {
383449 }
384450 else
385451 {
386- Size = ((Size + 31 )>>5 );
452+ Size = ((Size + 31 )>>5 );
387453 }
388454
389455 FreeMap = (FreeMapType * )nNewCallocUntyped (Size , FreeMapType );
@@ -645,16 +711,32 @@ void *fxfAllocRaw(size_t size, size_t desired_alignment) {
645711#else
646712 (size_t )pointerDifference (BotFreePtr ,Arena );
647713#endif
714+ /*
715+ Determine what alignment this allocation actually requires.
716+ */
648717 size_t needed_alignment_mask = (NOT_MULTIPLE_ALIGNMENT - 1U );
649718 while (needed_alignment_mask >= size )
650719 needed_alignment_mask >>= 1 ;
720+
721+ /*
722+ We only care about our offset modulo the required alignment.
723+ */
651724 curBottomIndex &= needed_alignment_mask ;
652725 if (curBottomIndex ) {
726+ /*
727+ We aren't aligned as strongly as we need to be for this allocation. Let's step until
728+ we are, and we'll add what we skip over to the free store. If we won't have a enough
729+ space left then move on to the next segment.
730+ */
653731 if ((needed_alignment_mask - curBottomIndex ) >= (sizeCurrentSeg - size ))
654732 goto NEXT_SEGMENT ;
655733 do {
656734 size_t const cur_alignment = BOTTOM_BIT (curBottomIndex );
657- pushOntoFreeStore (BotFreePtr , cur_alignment );
735+ assert (cur_alignment >= min_alignment );
736+ if (cur_alignment >= ALIGN_TO_MINIMUM (fxfMINSIZE ))
737+ pushOntoFreeStore (BotFreePtr , cur_alignment );
738+ else
739+ TMDBG (printf (" leaking %" SIZE_T_PRINTF_SPECIFIER " byte(s) instead of adding them to the free store\n" , (size_t_printf_type )cur_alignment ));
658740 BotFreePtr = stepPointer (BotFreePtr , (ptrdiff_t )cur_alignment );
659741 curBottomIndex += cur_alignment ;
660742 } while (curBottomIndex & needed_alignment_mask );
@@ -680,22 +762,40 @@ void *fxfAllocRaw(size_t size, size_t desired_alignment) {
680762NEXT_SEGMENT :
681763#if defined(SEGMENTED )
682764 if ((CurrentSeg + 1 ) < ArenaSegCnt ) {
765+ /*
766+ We're about to move to the next segment, but let's put whatever we can into the free store.
767+ */
683768 size_t curBottomIndex = (size_t )pointerDifference (BotFreePtr ,Arena [CurrentSeg ]);
769+ /*
770+ Add small powers of two until we reach NOT_MULTIPLE_ALIGNMENT.
771+ */
772+ size_t cur_alignment ;
684773 while (curBottomIndex & (NOT_MULTIPLE_ALIGNMENT - 1U ))
685774 {
686- size_t const cur_alignment = BOTTOM_BIT (curBottomIndex );
687- pushOntoFreeStore (BotFreePtr , cur_alignment );
775+ cur_alignment = BOTTOM_BIT (curBottomIndex );
776+ assert (cur_alignment >= min_alignment );
777+ if (cur_alignment >= ALIGN_TO_MINIMUM (fxfMINSIZE ))
778+ pushOntoFreeStore (BotFreePtr , cur_alignment );
779+ else
780+ TMDBG (printf (" leaking %" SIZE_T_PRINTF_SPECIFIER " byte(s) instead of adding them to the free store\n" , (size_t_printf_type )cur_alignment ));
688781 BotFreePtr = stepPointer (BotFreePtr , (ptrdiff_t )cur_alignment );
689782 curBottomIndex += cur_alignment ;
690783 }
691- curBottomIndex = (size_t )pointerDifference (TopFreePtr ,BotFreePtr );
692- if (curBottomIndex >= fxfMINSIZE )
784+ /*
785+ If there's anything left, we can add it all. Here we take advantage of the fact that
786+ the whole segment is fully aligned, so if what's left requires full alignment then
787+ the pointer must be fully aligned when we get here.
788+ */
789+ cur_alignment = (size_t )pointerDifference (TopFreePtr ,BotFreePtr ); /* Now stores the remaining space. */
790+ if (cur_alignment )
693791 {
694- assert (!(curBottomIndex & (NOT_MULTIPLE_ALIGNMENT - 1U )));
695- pushOntoFreeStore (BotFreePtr , curBottomIndex );
792+ assert (!((cur_alignment | curBottomIndex ) & (NOT_MULTIPLE_ALIGNMENT - 1U ))); /* Must be divisible by and aligned for NOT_MULTIPLE_ALIGNMENT. */
793+ assert ((cur_alignment & PTRMASK ) || !(curBottomIndex & PTRMASK )); /* If we're divisible by MAX_ALIGNMENT then we must be properly aligned for that. */
794+ if (cur_alignment >= ALIGN_TO_MINIMUM (fxfMINSIZE ))
795+ pushOntoFreeStore (BotFreePtr , cur_alignment );
796+ else
797+ TMDBG (printf (" leaking %" SIZE_T_PRINTF_SPECIFIER " byte(s) moving from segment %d to segment %d\n" , (size_t_printf_type )cur_alignment , CurrentSeg , CurrentSeg + 1 ));
696798 }
697- else if (curBottomIndex )
698- TMDBG (printf (" leaking %" SIZE_T_PRINTF_SPECIFIER " byte(s) moving from segment %d to segment %d\n" , (size_t_printf_type )curBottomIndex , CurrentSeg , CurrentSeg + 1 ));
699799 TMDBG (fputs (" next seg" , stdout ));
700800 ++ CurrentSeg ;
701801 BotFreePtr = Arena [CurrentSeg ];
@@ -842,7 +942,7 @@ void *fxfReAllocRaw(void *ptr, size_t OldSize, size_t NewSize, size_t desired_al
842942 ptrdiff_t const ptrIndex = pointerDifference (ptr ,Arena );
843943 assert (GlobalSize > (size_t )ptrIndex );
844944# endif
845- assert (ptrIndex >= 0 );
945+ assert (ptrIndex >= 0 );
846946 if (ptrIndex > 0 )
847947 {
848948 size_t allocatedSize = OldSize ;
@@ -869,7 +969,7 @@ void *fxfReAllocRaw(void *ptr, size_t OldSize, size_t NewSize, size_t desired_al
869969 needed_alignment = MAX_ALIGNMENT ;
870970 assert (!(((size_t )ptrIndex ) & (needed_alignment - 1U )));
871971 }
872- }
972+ }
873973#endif
874974 assert (desired_alignment && !CLEAR_BOTTOM_BIT (desired_alignment ));
875975 assert (desired_alignment <= OldSize );
@@ -889,7 +989,7 @@ void *fxfReAllocRaw(void *ptr, size_t OldSize, size_t NewSize, size_t desired_al
889989 if (needed_allocation < fxfMINSIZE )
890990 needed_allocation = fxfMINSIZE ;
891991 needed_allocation = ALIGN_TO_MINIMUM (needed_allocation );
892- if (needed_allocation == original_allocation )
992+ if (needed_allocation == original_allocation )
893993 return ptr ;
894994 /* TODO: Should we try to break apart this chunk? */
895995 }
0 commit comments