From fb39d7e78ded01d774b14847c125284eae9efbbc Mon Sep 17 00:00:00 2001
From: Miro Kropacek <[EMAIL REDACTED]>
Date: Thu, 1 Oct 2026 15:39:01 +1000
Subject: [PATCH] atari:video: blit optimizations
- use dlmalloc for better allocator and implicitly align every surface
on 16 bytes
- use move16 / move.l in SDL_BlitCopy
---
src/cpuinfo/SDL_cpuinfo.c | 52 +
src/video/SDL_blit.c | 19 +
src/video/SDL_blit.h | 2 +
src/video/ataricommon/SDL_ataricopy.S | 209 +
src/video/ataricommon/SDL_ataricopy_s.h | 35 +
src/video/ataricommon/dlmalloc.c | 4860 +++++++++++++++++++++++
src/video/ataricommon/dlmalloc.h | 1450 +++++++
7 files changed, 6627 insertions(+)
create mode 100644 src/video/ataricommon/SDL_ataricopy.S
create mode 100644 src/video/ataricommon/SDL_ataricopy_s.h
create mode 100644 src/video/ataricommon/dlmalloc.c
create mode 100644 src/video/ataricommon/dlmalloc.h
diff --git a/src/cpuinfo/SDL_cpuinfo.c b/src/cpuinfo/SDL_cpuinfo.c
index b902704fb..a899fa481 100644
--- a/src/cpuinfo/SDL_cpuinfo.c
+++ b/src/cpuinfo/SDL_cpuinfo.c
@@ -69,6 +69,10 @@
#include <swis.h>
#endif
+#ifdef __MINT__
+#include <mint/cookie.h>
+#endif
+
#define CPU_HAS_RDTSC 0x00000001
#define CPU_HAS_MMX 0x00000002
#define CPU_HAS_MMXEXT 0x00000004
@@ -79,6 +83,8 @@
#define CPU_HAS_ALTIVEC 0x00000100
#define CPU_HAS_ARM_SIMD 0x00000200
#define CPU_HAS_NEON 0x00000400
+#define CPU_HAS_M68020 0x00000800
+#define CPU_HAS_MOVE16 0x00001000
#if SDL_ALTIVEC_BLITTERS && HAVE_SETJMP && !__MACOSX__ && !__OpenBSD__
/* This is the brute force way of detecting instruction sets...
@@ -570,6 +576,28 @@ static __inline__ int CPU_haveNEON(void)
#endif
}
+static __inline__ long CPU_getAtariCPU(void)
+{
+#if defined(__MINT__) && !defined(__mcoldfire__)
+ long cpu;
+
+ if ( Getcookie(C__CPU, &cpu) == C_FOUND ) {
+ return cpu;
+ }
+#endif
+ return 0;
+}
+
+static __inline__ int CPU_haveM68020(void)
+{
+ return CPU_getAtariCPU() >= 20;
+}
+
+static __inline__ int CPU_haveMove16(void)
+{
+ return CPU_getAtariCPU() >= 40;
+}
+
static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
static Uint32 SDL_GetCPUFeatures(void)
@@ -606,6 +634,12 @@ static Uint32 SDL_GetCPUFeatures(void)
if ( CPU_haveNEON() ) {
SDL_CPUFeatures |= CPU_HAS_NEON;
}
+ if ( CPU_haveM68020() ) {
+ SDL_CPUFeatures |= CPU_HAS_M68020;
+ }
+ if ( CPU_haveMove16() ) {
+ SDL_CPUFeatures |= CPU_HAS_MOVE16;
+ }
}
return SDL_CPUFeatures;
}
@@ -690,6 +724,22 @@ SDL_bool SDL_HasNEON(void)
return SDL_FALSE;
}
+SDL_bool SDL_HasM68020(void)
+{
+ if ( SDL_GetCPUFeatures() & CPU_HAS_M68020 ) {
+ return SDL_TRUE;
+ }
+ return SDL_FALSE;
+}
+
+SDL_bool SDL_HasMove16(void)
+{
+ if ( SDL_GetCPUFeatures() & CPU_HAS_MOVE16 ) {
+ return SDL_TRUE;
+ }
+ return SDL_FALSE;
+}
+
#ifdef TEST_MAIN
#include <stdio.h>
@@ -706,6 +756,8 @@ int main()
printf("AltiVec: %d\n", SDL_HasAltiVec());
printf("ARM SIMD: %d\n", SDL_HasARMSIMD());
printf("NEON: %d\n", SDL_HasNEON());
+ printf("68020+: %d\n", SDL_HasM68020());
+ printf("MOVE16: %d\n", SDL_HasMove16());
return 0;
}
diff --git a/src/video/SDL_blit.c b/src/video/SDL_blit.c
index a25b758b8..bef0bc196 100644
--- a/src/video/SDL_blit.c
+++ b/src/video/SDL_blit.c
@@ -39,6 +39,10 @@
#include "mmx.h"
#endif
+#if defined(__MINT__) && !defined(__mcoldfire__)
+#include "ataricommon/SDL_ataricopy_s.h"
+#endif
+
/* The general purpose software blit routine */
static int SDL_SoftBlit(SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect)
@@ -172,6 +176,21 @@ static void SDL_BlitCopy(SDL_BlitInfo *info)
srcskip = dstskip = 0;
}
+#if defined(__MINT__) && !defined(__mcoldfire__)
+ /* TT RAM and VRAM are written fastest with move16 if src and dst share
+ * the alignment, everything else (including ST RAM) with move.l */
+ if ( (w >= 16) && (w < 0x01000000) && ((unsigned long)dst >= 0x01000000UL) && SDL_HasMove16() &&
+ ((((unsigned long)src ^ (unsigned long)dst) & 15) == 0) &&
+ (((info->s_skip ^ info->d_skip) & 15) == 0) ) {
+ SDL_Atari_CopyMove16(dst, src, w, h, info->d_skip, info->s_skip);
+ return;
+ }
+ if ( (w >= 4) && (w < 0x01000000) && SDL_HasM68020() ) {
+ SDL_Atari_CopyLong(dst, src, w, h, info->d_skip, info->s_skip);
+ return;
+ }
+#endif
+
#ifdef SSE_ASMBLIT
if(SDL_HasSSE())
{
diff --git a/src/video/SDL_blit.h b/src/video/SDL_blit.h
index ea238bbf4..3d760c574 100644
--- a/src/video/SDL_blit.h
+++ b/src/video/SDL_blit.h
@@ -28,6 +28,8 @@
extern SDL_bool SDL_HasARMSIMD(void); /* whether CPU has ARM SIMD (ARMv6) features */
extern SDL_bool SDL_HasNEON (void); /* whether CPU has ARM NEON features. */
+extern SDL_bool SDL_HasM68020(void); /* whether CPU is 68020 or higher (Atari) */
+extern SDL_bool SDL_HasMove16(void); /* whether CPU has MOVE16 (Atari) */
/* The structure passed to the low level blit functions */
typedef struct {
diff --git a/src/video/ataricommon/SDL_ataricopy.S b/src/video/ataricommon/SDL_ataricopy.S
new file mode 100644
index 000000000..86d946b33
--- /dev/null
+++ b/src/video/ataricommon/SDL_ataricopy.S
@@ -0,0 +1,209 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+
+#include "symbols.h"
+
+/*
+ Memory copy routines
+
+ Miro Kropacek
+*/
+
+ .globl SYM(SDL_Atari_CopyLong)
+ .globl SYM(SDL_Atari_CopyMove16)
+
+/* move16 (a0)+,(a1)+ encoded by hand as the library is assembled for 68000 */
+#define MOVE16_A0_A1 .dc.w 0xf620,0x9000
+
+ .text
+
+| void SDL_Atari_CopyLong(Uint8 *dst, const Uint8 *src, Uint32 w, Uint32 h, Uint32 dstskip, Uint32 srcskip);
+| 68020+ only, 4 <= w < 16 MB, the source may be misaligned
+SYM(SDL_Atari_CopyLong):
+#if !defined(__mcoldfire__)
+ movem.l d2-d6,-(sp)
+#ifdef __FASTCALL__
+ exg a0,a1 | a0: src, a1: dst
+ move.l d0,d3 | d3.l: width in bytes
+ move.l d1,d4 | d4.l: height
+ move.l d2,d5 | d5.l: dst skip
+ move.l (5*4+4,sp),d6 | d6.l: src skip
+#else
+ move.l (5*4+4,sp),a1 | a1: dst
+ move.l (5*4+8,sp),a0 | a0: src
+ move.l (5*4+12,sp),d3 | d3.l: width in bytes
+ move.l (5*4+16,sp),d4 | d4.l: height
+ move.l (5*4+20,sp),d5 | d5.l: dst skip
+ move.l (5*4+24,sp),d6 | d6.l: src skip
+#endif
+ bra.w copylong_row_test
+
+copylong_row:
+ move.l d3,d0 | d0.l: bytes left in this row
+
+ | copy the head up to the next 4-byte boundary of dst
+ move.l a1,d1
+ neg.l d1
+ and.l #3,d1
+ sub.l d1,d0
+ lsr.w #1,d1
+ bcc.s copylong_head2
+ move.b (a0)+,(a1)+
+copylong_head2:
+ lsr.w #1,d1
+ bcc.s copylong_body
+ move.w (a0)+,(a1)+
+
+copylong_body:
+ move.l d0,d1
+ lsr.l #8,d1 | d1.l: 256-byte blocks
+ bra.w copylong_256_test
+copylong_256:
+ .rept 64
+ move.l (a0)+,(a1)+
+ .endr
+copylong_256_test:
+ dbra d1,copylong_256
+
+ move.w d0,d1
+ and.w #0xff,d1
+ lsr.w #2,d1 | d1.w: remaining longs
+ bra.s copylong_longs_test
+copylong_longs:
+ move.l (a0)+,(a1)+
+copylong_longs_test:
+ dbra d1,copylong_longs
+
+ | copy the tail after the last long
+ btst #1,d0
+ beq.s copylong_tail1
+ move.w (a0)+,(a1)+
+copylong_tail1:
+ btst #0,d0
+ beq.s copylong_next
+ move.b (a0)+,(a1)+
+
+copylong_next:
+ add.l d5,a1
+ add.l d6,a0
+copylong_row_test:
+ subq.l #1,d4
+ bpl.w copylong_row
+
+ movem.l (sp)+,d2-d6
+#endif
+ rts
+
+| void SDL_Atari_CopyMove16(Uint8 *dst, const Uint8 *src, Uint32 w, Uint32 h, Uint32 dstskip, Uint32 srcskip);
+| 68040+ only, 16 <= w < 16 MB, src and dst must have the same alignment modulo 16 on every row
+SYM(SDL_Atari_CopyMove16):
+#if !defined(__mcoldfire__)
+ movem.l d2-d6,-(sp)
+#ifdef __FASTCALL__
+ exg a0,a1 | a0: src, a1: dst
+ move.l d0,d3 | d3.l: width in bytes
+ move.l d1,d4 | d4.l: height
+ move.l d2,d5 | d5.l: dst skip
+ move.l (5*4+4,sp),d6 | d6.l: src skip
+#else
+ move.l (5*4+4,sp),a1 | a1: dst
+ move.l (5*4+8,sp),a0 | a0: src
+ move.l (5*4+12,sp),d3 | d3.l: width in bytes
+ move.l (5*4+16,sp),d4 | d4.l: height
+ move.l (5*4+20,sp),d5 | d5.l: dst skip
+ move.l (5*4+24,sp),d6 | d6.l: src skip
+#endif
+ bra.w copymove16_row_test
+
+copymove16_row:
+ move.l d3,d0 | d0.l: bytes left in this row
+
+ | copy the head up to the next 16-byte boundary
+ move.l a1,d1
+ neg.l d1
+ and.l #15,d1
+ sub.l d1,d0
+ lsr.w #1,d1
+ bcc.s copymove16_head2
+ move.b (a0)+,(a1)+
+copymove16_head2:
+ lsr.w #1,d1
+ bcc.s copymove16_head4
+ move.w (a0)+,(a1)+
+copymove16_head4:
+ lsr.w #1,d1
+ bcc.s copymove16_head8
+ move.l (a0)+,(a1)+
+copymove16_head8:
+ lsr.w #1,d1
+ bcc.s copymove16_body
+ move.l (a0)+,(a1)+
+ move.l (a0)+,(a1)+
+
+copymove16_body:
+ move.l d0,d1
+ lsr.l #8,d1 | d1.l: 256-byte blocks
+ bra.s copymove16_256_test
+copymove16_256:
+ .rept 16
+ MOVE16_A0_A1
+ .endr
+copymove16_256_test:
+ dbra d1,copymove16_256
+
+ move.w d0,d1
+ and.w #0xff,d1
+ lsr.w #4,d1 | d1.w: remaining 16-byte blocks
+ bra.s copymove16_16_test
+copymove16_16:
+ MOVE16_A0_A1
+copymove16_16_test:
+ dbra d1,copymove16_16
+
+ | copy the tail after the last 16-byte boundary
+ btst #3,d0
+ beq.s copymove16_tail4
+ move.l (a0)+,(a1)+
+ move.l (a0)+,(a1)+
+copymove16_tail4:
+ btst #2,d0
+ beq.s copymove16_tail2
+ move.l (a0)+,(a1)+
+copymove16_tail2:
+ btst #1,d0
+ beq.s copymove16_tail1
+ move.w (a0)+,(a1)+
+copymove16_tail1:
+ btst #0,d0
+ beq.s copymove16_next
+ move.b (a0)+,(a1)+
+
+copymove16_next:
+ add.l d5,a1
+ add.l d6,a0
+copymove16_row_test:
+ subq.l #1,d4
+ bpl.w copymove16_row
+
+ movem.l (sp)+,d2-d6
+#endif
+ rts
diff --git a/src/video/ataricommon/SDL_ataricopy_s.h b/src/video/ataricommon/SDL_ataricopy_s.h
new file mode 100644
index 000000000..b3ecda6fb
--- /dev/null
+++ b/src/video/ataricommon/SDL_ataricopy_s.h
@@ -0,0 +1,35 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+#ifndef _ATARI_COPY_h
+#define _ATARI_COPY_h
+
+#include "SDL_stdinc.h"
+
+/* Copy with move.l, 68020+ only, 4 <= w < 16 MB */
+void SDL_Atari_CopyLong(Uint8 *dst, const Uint8 *src, Uint32 w, Uint32 h, Uint32 dstskip, Uint32 srcskip);
+
+/* Copy with move16, 68040+ only, 16 <= w < 16 MB, src and dst must have the same alignment modulo 16 on every row */
+void SDL_Atari_CopyMove16(Uint8 *dst, const Uint8 *src, Uint32 w, Uint32 h, Uint32 dstskip, Uint32 srcskip);
+
+#endif /* _ATARI_COPY_h */
diff --git a/src/video/ataricommon/dlmalloc.c b/src/video/ataricommon/dlmalloc.c
new file mode 100644
index 000000000..6b3094a6e
--- /dev/null
+++ b/src/video/ataricommon/dlmalloc.c
@@ -0,0 +1,4860 @@
+#include "dlmalloc.h"
+
+/* GCC 12+ reports false positives on the chunk pointer arithmetic */
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+/* Keep GCC from turning malloc + memset in calloc() into a call to calloc() */
+#pragma GCC optimize ("no-optimize-strlen")
+
+/*------------------------------ internal #includes ---------------------- */
+
+#ifdef _MSC_VER
+#pragma warning( disable : 4146 ) /* no "unsigned" warnings */
+#endif /* _MSC_VER */
+#if !NO_MALLOC_STATS
+#include <stdio.h> /* for printing in malloc_stats */
+#endif /* NO_MALLOC_STATS */
+#ifndef LACKS_ERRNO_H
+#include <errno.h> /* for MALLOC_FAILURE_ACTION */
+#endif /* LACKS_ERRNO_H */
+#ifdef DEBUG
+#if ABORT_ON_ASSERT_FAILURE
+#undef assert
+#define assert(x) if(!(x)) ABORT
+#else /* ABORT_ON_ASSERT_FAILURE */
+#include <assert.h>
+#endif /* ABORT_ON_ASSERT_FAILURE */
+#else /* DEBUG */
+#ifndef assert
+#define assert(x)
+#endif
+#define DEBUG 0
+#endif /* DEBUG */
+#if !defined(WIN32) && !defined(LACKS_TIME_H)
+#include <time.h> /* for magic initialization */
+#endif /* WIN32 */
+#ifndef LACKS_STDLIB_H
+#include <stdlib.h> /* for abort() */
+#endif /* LACKS_STDLIB_H */
+#ifndef LACKS_STRING_H
+#include <string.h> /* for memset etc */
+#endif /* LACKS_STRING_H */
+#if USE_BUILTIN_FFS
+#ifndef LACKS_STRINGS_H
+#include <strings.h> /* for ffs */
+#endif /* LACKS_STRINGS_H */
+#endif /* USE_BUILTIN_FFS */
+#if HAVE_MMAP
+#ifndef LACKS_SYS_MMAN_H
+/* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */
+#if (defined(linux) && !defined(__USE_GNU))
+#define __USE_GNU 1
+#include <sys/mman.h> /* for mmap */
+#undef __USE_GNU
+#else
+#include <sys/mman.h> /* for mmap */
+#endif /* linux */
+#endif /* LACKS_SYS_MMAN_H */
+#ifndef LACKS_FCNTL_H
+#include <fcntl.h>
+#endif /* LACKS_FCNTL_H */
+#endif /* HAVE_MMAP */
+#ifndef LACKS_UNISTD_H
+#include <unistd.h> /* for sbrk, sysconf */
+#else /* LACKS_UNISTD_H */
+#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
+extern void* sbrk(ptrdiff_t);
+#endif /* FreeBSD etc */
+#endif /* LACKS_UNISTD_H */
+
+/* Declarations for locking */
+#if USE_LOCKS
+#ifndef WIN32
+#if defined (__SVR4) && defined (__sun) /* solaris */
+#include <thread.h>
+#elif !defined(LACKS_SCHED_H)
+#include <sched.h>
+#endif /* solaris or LACKS_SCHED_H */
+#if (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0) || !USE_SPIN_LOCKS
+#include <pthread.h>
+#endif /* USE_RECURSIVE_LOCKS ... */
+#elif defined(_MSC_VER)
+#ifndef _M_AMD64
+/* These are already defined on AMD64 builds */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp);
+LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* _M_AMD64 */
+#pragma intrinsic (_InterlockedCompareExchange)
+#pragma intrinsic (_InterlockedExchange)
+#define interlockedcompareexchange _InterlockedCompareExchange
+#define interlockedexchange _InterlockedExchange
+#elif defined(WIN32) && defined(__GNUC__)
+#define interlockedcompareexchange(a, b, c) __sync_val_compare_and_swap(a, c, b)
+#define interlockedexchange __sync_lock_test_and_set
+#endif /* Win32 */
+#else /* USE_LOCKS */
+#endif /* USE_LOCKS */
+
+#ifndef LOCK_AT_FORK
+#define LOCK_AT_FORK 0
+#endif
+
+/* Declarations for bit scanning on win32 */
+#if defined(_MSC_VER) && _MSC_VER>=1300
+#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+unsigned char _BitScanForward(unsigned long *index, unsigned long mask);
+unsigned char _BitScanReverse(unsigned long *index, unsigned long mask);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#define BitScanForward _BitScanForward
+#define BitScanReverse _BitScanReverse
+#pragma intrinsic(_BitScanForward)
+#pragma intrinsic(_BitScanReverse)
+#endif /* BitScanForward */
+#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */
+
+#ifndef WIN32
+#ifndef malloc_getpagesize
+# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */
+# ifndef _SC_PAGE_SIZE
+# define _SC_PAGE_SIZE _SC_PAGESIZE
+# endif
+# endif
+# ifdef _SC_PAGE_SIZE
+# define malloc_getpagesize sysconf(_SC_PAGE_SIZE)
+# else
+# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE)
+ extern size_t getpagesize();
+# define malloc_getpagesize getpagesize()
+# else
+# ifdef WIN32 /* use supplied emulation of getpagesize */
+# define malloc_getpagesize getpagesize()
+# else
+# ifndef LACKS_SYS_PARAM_H
+# include <sys/param.h>
+# endif
+# ifdef EXEC_PAGESIZE
+# define malloc_getpagesize EXEC_PAGESIZE
+# else
+# ifdef NBPG
+# ifndef CLSIZE
+# define malloc_getpagesize NBPG
+# else
+# define malloc_getpagesize (NBPG * CLSIZE)
+# endif
+# else
+# ifdef NBPC
+# define malloc_getpagesize NBPC
+# else
+# ifdef PAGESIZE
+# define malloc_getpagesize PAGESIZE
+# else /* just guess */
+# define malloc_getpagesize ((size_t)4096U)
+# endif
+# endif
+# endif
+# endif
+# endif
+# endif
+# endif
+#endif
+#endif
+
+/* ------------------- size_t and alignment properties -------------------- */
+
+/* The byte and bit size of a size_t */
+#define SIZE_T_SIZE (sizeof(size_t))
+#define SIZE_T_BITSIZE (sizeof(size_t) << 3)
+
+/* Some constants coerced to size_t */
+/* Annoying but necessary to avoid errors on some platforms */
+#define SIZE_T_ZERO ((size_t)0)
+#define SIZE_T_ONE ((size_t)1)
+#define SIZE_T_TWO ((size_t)2)
+#define SIZE_T_FOUR ((size_t)4)
+#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1)
+#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2)
+#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES)
+#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U)
+
+/* The bit mask value corresponding to MALLOC_ALIGNMENT */
+#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE)
+
+/* True if address a has acceptable alignment */
+#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0)
+
+/* the number of bytes to offset an address to align it */
+#define align_offset(A)\
+ ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\
+ ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK))
+
+/* -------------------------- MMAP preliminaries ------------------------- */
+
+/*
+ If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and
+ checks to fail so compiler optimizer can delete code rather than
+ using so many "#if"s.
+*/
+
+
+/* MORECORE and MMAP must return MFAIL on failure */
+#define MFAIL ((void*)(MAX_SIZE_T))
+#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */
+
+#if HAVE_MMAP
+
+#ifndef WIN32
+#define MUNMAP_DEFAULT(a, s) munmap((a), (s))
+#define MMAP_PROT (PROT_READ|PROT_WRITE)
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+#define MAP_ANONYMOUS MAP_ANON
+#endif /* MAP_ANON */
+#ifdef MAP_ANONYMOUS
+#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS)
+#define MMAP_DEFAULT(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0)
+#else /* MAP_ANONYMOUS */
+/*
+ Nearly all versions of mmap support MAP_ANONYMOUS, so the following
+ is unlikely to be needed, but is supplied just in case.
+*/
+#define MMAP_FLAGS (MAP_PRIVATE)
+static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */
+#define MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \
+ (dev_zero_fd = open("/dev/zero", O_RDWR), \
+ mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \
+ mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0))
+#endif /* MAP_ANONYMOUS */
+
+#define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s)
+
+#else /* WIN32 */
+
+/* Win32 MMAP via VirtualAlloc */
+static FORCEINLINE void* win32mmap(size_t size) {
+ void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
+ return (ptr != 0)? ptr: MFAIL;
+}
+
+/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */
+static FORCEINLINE void* win32direct_mmap(size_t size) {
+ void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN,
+ PAGE_READWRITE);
+ return (ptr != 0)? ptr: MFAIL;
+}
+
+/* This function supports releasing coalesed segments */
+static FORCEINLINE int win32munmap(void* ptr, size_t size) {
+ MEMORY_BASIC_INFORMATION minfo;
+ char* cptr = (char*)ptr;
+ while (size) {
+ if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0)
+ return -1;
+ if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr ||
+ minfo.State != MEM_COMMIT || minfo.RegionSize > size)
+ return -1;
+ if (VirtualFree(cptr, 0, MEM_RELEASE) == 0)
+ return -1;
+ cptr += minfo.RegionSize;
+ size -= minfo.RegionSize;
+ }
+ return 0;
+}
+
+#define MMAP_DEFAULT(s) win32mmap(s)
+#define MUNMAP_DEFAULT(a, s) win32munmap((a), (s))
+#define DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s)
+#endif /* WIN32 */
+#endif /* HAVE_MMAP */
+
+#if HAVE_MREMAP
+#ifndef WIN32
+#define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv))
+#endif /* WIN32 */
+#endif /* HAVE_MREMAP */
+
+/**
+ * Define CALL_MORECORE
+ */
+#if HAVE_MORECORE
+ #ifdef MORECORE
+ #define CALL_MORECORE(S) MORECORE(S)
+ #else /* MORECORE */
+ #define CALL_MORECORE(S) MORECORE_DEFAULT(S)
+ #endif /* MORECORE */
+#else /* HAVE_MORECORE */
+ #define CALL_MORECORE(S) MFAIL
+#endif /* HAVE_MORECORE */
+
+/**
+ * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP
+ */
+#if HAVE_MMAP
+ #define USE_MMAP_BIT (SIZE_T_ONE)
+
+ #ifdef MMAP
+ #define CALL_MMAP(s) MMAP(s)
+ #else /* MMAP */
+ #define CALL_MMAP(s) MMAP_DEFAULT(s)
+ #endif /* MMAP */
+ #ifdef MUNMAP
+ #define CALL_MUNMAP(a, s) MUNMAP((a), (s))
+ #else /* MUNMAP */
+ #define CALL_MUNMAP(a, s) MUNMAP_DEFAULT((a), (s))
+ #endif /* MUNMAP */
+ #ifdef DIRECT_MMAP
+ #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s)
+ #else /* DIRECT_MMAP */
+ #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s)
+ #endif /* DIRECT_MMAP */
+#else /* HAVE_MMAP */
+ #define USE_MMAP_BIT (SIZE_T_ZERO)
+
+ #define MMAP(s) MFAIL
+ #define MUNMAP(a, s) (-1)
+ #define DIRECT_MMAP(s) MFAIL
+ #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s)
+ #define CALL_MMAP(s) MMAP(s)
+ #define CALL_MUNMAP(a, s) MUNMAP((a), (s))
+#endif /* HAVE_MMAP */
+
+/**
+ * Define CALL_MREMAP
+ */
+#if HAVE_MMAP && HAVE_MREMAP
+ #ifdef MREMAP
+ #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv))
+ #else /* MREMAP */
+ #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv))
+ #endif /* MREMAP */
+#else /* HAVE_MMAP && HAVE_MREMAP */
+ #define CALL_MREMAP(addr, osz, nsz, mv) MFAIL
+#endif /* HAVE_MMAP && HAVE_MREMAP */
+
+/* mstate bit set if continguous morecore disabled or failed */
+#define USE_NONCONTIGUOUS_BIT (4U)
+
+/* segment bit set in create_mspace_with_base */
+#define EXTERN_BIT (8U)
+
+
+/* --------------------------- Lock preliminaries ------------------------ */
+
+/*
+ When locks are defined, there is one global lock, plus
+ one per-mspace lock.
+
+ The global lock_ensures that mparams.magic and other unique
+ mparams values are initialized only once. It also protects
+ sequences of calls to MORECORE. In many cases sys_alloc requires
+ two calls, that should not be interleaved with calls by other
+ threads. This does not protect against direct calls to MORECORE
+ by other threads not using this lock, so there is still code to
+ cope the best we can on interference.
+
+ Per-mspace locks surround calls to malloc, free, etc.
+ By default, locks are simple non-reentrant mutexes.
+
+ Because lock-protected regions generally have bounded times, it is
+ OK to use the supplied simple spinlocks. Spinlocks are likely to
+ improve performance for lightly contended applications, but worsen
+ performance under heavy contention.
+
+ If USE_LOCKS is > 1, the definitions of lock routines here are
+ bypassed, in which case you will need to define the type MLOCK_T,
+ and at least INITIAL_LOCK, DESTROY_LOCK, ACQUIRE_LOCK, RELEASE_LOCK
+ and TRY_LOCK. You must also declare a
+ static MLOCK_T malloc_global_mutex = { initialization values };.
+
+*/
+
+#if !USE_LOCKS
+#define USE_LOCK_BIT (0U)
+#define INITIAL_LOCK(l) (0)
+#define DESTROY_LOCK(l) (0)
+#define ACQUIRE_MALLOC_GLOBAL_LOCK()
+#define RELEASE_MALLOC_GLOBAL_LOCK()
+
+#else
+#if USE_LOCKS > 1
+/* ----------------------- User-defined locks ------------------------ */
+/* Define your own lock implementation here */
+/* #define INITIAL_LOCK(lk) ... */
+/* #define DESTROY_LOCK(lk) ... */
+/* #define ACQUIRE_LOCK(lk) ... */
+/* #define RELEASE_LOCK(lk) ... */
+/* #define TRY_LOCK(lk) ... */
+/* static MLOCK_T malloc_global_mutex = ... */
+
+#elif USE_SPIN_LOCKS
+
+/* First, define CAS_LOCK and CLEAR_LOCK on ints */
+/* Note CAS_LOCK defined to return 0 on success */
+
+#if defined(__GNUC__)&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1))
+#define CAS_LOCK(sl) __sync_lock_test_and_set(sl, 1)
+#define CLEAR_LOCK(sl) __sync_lock_release(sl)
+
+#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))
+/* Custom spin locks for older gcc on x86 */
+static FORCEINLINE int x86_cas_lock(int *sl) {
+ int ret;
+ int val = 1;
+ int cmp = 0;
+ __asm__ __volatile__ ("lock; cmpxchgl %1, %2"
+ : "=a" (ret)
+ : "r" (val), "m" (*(sl)), "0"(cmp)
+ : "memory", "cc");
+ return ret;
+}
+
+static FORCEINLINE void x86_clear_lock(int* sl) {
+ assert(*sl != 0);
+ int prev = 0;
+ int ret;
+ __asm__ __volatile__ ("lock; xchgl %0, %1"
+ : "=r" (ret)
+ : "m" (*(sl)), "0"(prev)
+ : "memory");
+}
+
+#define CAS_LOCK(sl) x86_cas_lock(sl)
+#define CLEAR_LOCK(sl) x86_clear_lock(sl)
+
+#else /* Win32 MSC */
+#define CAS_LOCK(sl) interlockedexchange(sl, (LONG)1)
+#define CLEAR_LOCK(sl) interlockedexchange (sl, (LONG)0)
+
+#endif /* ... gcc spins locks ... */
+
+/* How to yield for a spin lock */
+#define SPINS_PER_YIELD 63
+#if defined(_MSC_VER)
+#define SLEEP_EX_DURATION 50 /* delay for yield/sleep */
+#define SPIN_LOCK_YIELD SleepEx(SLEEP_EX_DURATION, FALSE)
+#elif defined (__SVR4) && defined (__sun) /* solaris */
+#define SPIN_LOCK_YIELD thr_yield();
+#elif !defined(LACKS_SCHED_H)
+#define SPIN_LOCK_YIELD sched_yield();
+#else
+#define SPIN_LOCK_YIELD
+#endif /* ... yield ... */
+
+#if !defined(USE_RECURSIVE_LOCKS) || USE_RECURSIVE_LOCKS == 0
+/* Plain spin locks use single word (embedded in malloc_states) */
+static int spin_acquire_lock(int *sl) {
+ int spins = 0;
+ while (*(volatile int *)sl != 0 || CAS_LOCK(sl)) {
+ if ((++spins & SPINS_PER_YIELD) == 0) {
+ SPIN_LOCK_YIELD;
+ }
+ }
+ return 0;
+}
+
+#define MLOCK_T int
+#define TRY_LOCK(sl) !CAS_LOCK(sl)
+#define RELEASE_LOCK(sl) CLEAR_LOCK(sl)
+#define ACQUIRE_LOCK(sl) (CAS_LOCK(sl)? spin_acquire_lock(sl) : 0)
+#define INITIAL_LOCK(sl) (*sl = 0)
+#define DESTROY_LOCK(sl) (0)
+static MLOCK_T malloc_global_mutex = 0;
+
+#else /* USE_RECURSIVE_LOCKS */
+/* types for lock owners */
+#ifdef WIN32
+#define THREAD_ID_T DWORD
+#define CURRENT_THREAD GetCurrentThreadId()
+#define EQ_OWNER(X,Y) ((X) == (Y))
+#else
+/*
+ Note: the following assume that pthread_t is a type that can be
+ initialized to (casted) zero. If this is not the case, you will need to
+ somehow redefine these or not use spin locks.
+*/
+#define THREAD_ID_T pthread_t
+#define CURRENT_THREAD pthread_self()
+#define EQ_OWNER(X,Y) pthread_equal(X, Y)
+#endif
+
+struct malloc_recursive_lock {
+ int sl;
+ unsigned int c;
+ THREAD_ID_T threadid;
+};
+
+#define MLOCK_T struct malloc_recursive_lock
+static MLOCK_T malloc_global_mutex = { 0, 0, (THREAD_ID_T)0};
+
+static FORCEINLINE void recursive_release_lock(MLOCK_T *lk) {
+ assert(lk->sl != 0);
+ if (--lk->c == 0) {
+ CLEAR_LOCK(&lk->sl);
+ }
+}
+
+static FORCEINLINE int recursive_acquire_lock(MLOCK_T *lk) {
+ THREAD_ID_T mythreadid = CURRENT_THREAD;
+ int spins = 0;
+ for (;;) {
+ if (*((volatile int *)(&lk->sl)) == 0) {
+ if (!CAS_LOCK(&lk->sl)) {
+ lk->threadid = mythreadid;
+ lk->c = 1;
+ return 0;
+ }
+ }
+ else if (EQ_OWNER(lk->threadid, mythreadid)) {
+ ++lk->c;
+ return 0;
+ }
+ if ((++spins & SPINS_PER_YIELD) == 0) {
+ SPIN_LOCK_YIELD;
+ }
+ }
+}
+
+static FORCEINLINE int recursive_try_lock(MLOCK_T *lk) {
+ THREAD_ID_T mythreadid = CURRENT_THREAD;
+ if (*((volatile int *)(&lk->sl)) == 0) {
+ if (!CAS_LOCK(&lk->sl)) {
+ lk->threadid = mythreadid;
+ lk->c = 1;
+ return 1;
+ }
+ }
+ else if (EQ_OWNER(lk->threadid, mythreadid)) {
+ ++lk->c;
+ return 1;
+ }
+ return 0;
+}
+
+#define RELEASE_LOCK(lk) recursive_release_lock(lk)
+#define TRY_LOCK(lk) recursive_try_lock(lk)
+#define ACQUIRE_LOCK(lk) recursive_acquire_lock(lk)
+#define INITIAL_LOCK(lk) ((lk)->threadid = (THREAD_ID_T)0, (lk)->sl = 0, (lk)->c = 0)
+#define DESTROY_LOCK(lk) (0)
+#endif /* USE_RECURSIVE_LOCKS */
+
+#elif defined(WIN32) /* Win32 critical sections */
+#define MLOCK_T CRITICAL_SECTION
+#define ACQUIRE_LOCK(lk) (EnterCriticalSection(lk), 0)
+#define RELEASE_LOCK(lk) LeaveCriticalSection(lk)
+#define TRY_LOCK(lk) TryEnterCriticalSection(lk)
+#define INITIAL_LOCK(lk) (!InitializeCriticalSectionAndSpinCount((lk), 0x80000000|4000))
+#define DESTROY_LOCK(lk) (DeleteCriticalSection(lk), 0)
+#define NEED_GLOBAL_LOCK_INIT
+
+static MLOCK_T malloc_global_mutex;
+static volatile LONG malloc_global_mutex_status;
+
+/* Use spin loop to initialize global lock */
+static void init_malloc_global_mutex() {
+ for (;;) {
+ long stat = malloc_global_mutex_status;
+ if (stat > 0)
+ return;
+ /* transition to < 0 while initializing, then to > 0) */
+ if (stat == 0 &&
+ interlockedcompareexchange(&malloc_global_mutex_status, (LONG)-1, (LONG)0) == 0) {
+ InitializeCriticalSection(&malloc_global_mutex);
+ interlockedexchange(&malloc_global_mutex_status, (LONG)1);
+ return;
+ }
+ SleepEx(0, FALSE);
+ }
+}
+
+#else /* pthreads-based locks */
+#define MLOCK_T pthread_mutex_t
+#define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk)
+#define RELEASE_LOCK(lk) pthread_mutex_unlock(lk)
+#define TRY_LOCK(lk) (!pthread_mutex_trylock(lk))
+#define INITIAL_LOCK(lk) pthread_init_lock(lk)
+#define DESTROY_LOCK(lk) pthread_mutex_destroy(lk)
+
+#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 && defined(linux) && !defined(PTHREAD_MUTEX_RECURSIVE)
+/* Cope with old-style linux recursive lock initialization by add
(Patch may be truncated, please check the link at the top of this post.)