SDL-1.2: atari:video: blit optimizations

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.)