http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1f27b840/lib/luajit/src/lj_alloc.c ---------------------------------------------------------------------- diff --git a/lib/luajit/src/lj_alloc.c b/lib/luajit/src/lj_alloc.c new file mode 100644 index 0000000..7c7ec67 --- /dev/null +++ b/lib/luajit/src/lj_alloc.c @@ -0,0 +1,1396 @@ +/* +** Bundled memory allocator. +** +** Beware: this is a HEAVILY CUSTOMIZED version of dlmalloc. +** The original bears the following remark: +** +** This is a version (aka dlmalloc) of malloc/free/realloc written by +** Doug Lea and released to the public domain, as explained at +** http://creativecommons.org/licenses/publicdomain. +** +** * Version pre-2.8.4 Wed Mar 29 19:46:29 2006 (dl at gee) +** +** No additional copyright is claimed over the customizations. +** Please do NOT bother the original author about this version here! +** +** If you want to use dlmalloc in another project, you should get +** the original from: ftp://gee.cs.oswego.edu/pub/misc/ +** For thread-safe derivatives, take a look at: +** - ptmalloc: http://www.malloc.de/ +** - nedmalloc: http://www.nedprod.com/programs/portable/nedmalloc/ +*/ + +#define lj_alloc_c +#define LUA_CORE + +/* To get the mremap prototype. Must be defined before any system includes. */ +#if defined(__linux__) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif + +#include "lj_def.h" +#include "lj_arch.h" +#include "lj_alloc.h" + +#ifndef LUAJIT_USE_SYSMALLOC + +#define MAX_SIZE_T (~(size_t)0) +#define MALLOC_ALIGNMENT ((size_t)8U) + +#define DEFAULT_GRANULARITY ((size_t)128U * (size_t)1024U) +#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) +#define DEFAULT_MMAP_THRESHOLD ((size_t)128U * (size_t)1024U) +#define MAX_RELEASE_CHECK_RATE 255 + +/* ------------------- 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 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) + +/* The bit mask value corresponding to MALLOC_ALIGNMENT */ +#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) + +/* 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 support ------------------------------- */ + +#define MFAIL ((void *)(MAX_SIZE_T)) +#define CMFAIL ((char *)(MFAIL)) /* defined for convenience */ + +#define IS_DIRECT_BIT (SIZE_T_ONE) + +#if LJ_TARGET_WINDOWS + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#if LJ_64 + +/* Undocumented, but hey, that's what we all love so much about Windows. */ +typedef long (*PNTAVM)(HANDLE handle, void **addr, ULONG zbits, + size_t *size, ULONG alloctype, ULONG prot); +static PNTAVM ntavm; + +/* Number of top bits of the lower 32 bits of an address that must be zero. +** Apparently 0 gives us full 64 bit addresses and 1 gives us the lower 2GB. +*/ +#define NTAVM_ZEROBITS 1 + +static void INIT_MMAP(void) +{ + ntavm = (PNTAVM)GetProcAddress(GetModuleHandleA("ntdll.dll"), + "NtAllocateVirtualMemory"); +} + +/* Win64 32 bit MMAP via NtAllocateVirtualMemory. */ +static LJ_AINLINE void *CALL_MMAP(size_t size) +{ + DWORD olderr = GetLastError(); + void *ptr = NULL; + long st = ntavm(INVALID_HANDLE_VALUE, &ptr, NTAVM_ZEROBITS, &size, + MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + SetLastError(olderr); + return st == 0 ? ptr : MFAIL; +} + +/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ +static LJ_AINLINE void *DIRECT_MMAP(size_t size) +{ + DWORD olderr = GetLastError(); + void *ptr = NULL; + long st = ntavm(INVALID_HANDLE_VALUE, &ptr, NTAVM_ZEROBITS, &size, + MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, PAGE_READWRITE); + SetLastError(olderr); + return st == 0 ? ptr : MFAIL; +} + +#else + +#define INIT_MMAP() ((void)0) + +/* Win32 MMAP via VirtualAlloc */ +static LJ_AINLINE void *CALL_MMAP(size_t size) +{ + DWORD olderr = GetLastError(); + void *ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + SetLastError(olderr); + return ptr ? ptr : MFAIL; +} + +/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ +static LJ_AINLINE void *DIRECT_MMAP(size_t size) +{ + DWORD olderr = GetLastError(); + void *ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, + PAGE_READWRITE); + SetLastError(olderr); + return ptr ? ptr : MFAIL; +} + +#endif + +/* This function supports releasing coalesed segments */ +static LJ_AINLINE int CALL_MUNMAP(void *ptr, size_t size) +{ + DWORD olderr = GetLastError(); + 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; + } + SetLastError(olderr); + return 0; +} + +#else + +#include <errno.h> +#include <sys/mman.h> + +#define MMAP_PROT (PROT_READ|PROT_WRITE) +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif +#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) + +#if LJ_64 +/* 64 bit mode needs special support for allocating memory in the lower 2GB. */ + +#if defined(MAP_32BIT) + +#if defined(__sun__) +#define MMAP_REGION_START ((uintptr_t)0x1000) +#else +/* Actually this only gives us max. 1GB in current Linux kernels. */ +#define MMAP_REGION_START ((uintptr_t)0) +#endif + +static LJ_AINLINE void *CALL_MMAP(size_t size) +{ + int olderr = errno; + void *ptr = mmap((void *)MMAP_REGION_START, size, MMAP_PROT, MAP_32BIT|MMAP_FLAGS, -1, 0); + errno = olderr; + return ptr; +} + +#elif LJ_TARGET_OSX || LJ_TARGET_PS4 || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__sun__) + +/* OSX and FreeBSD mmap() use a naive first-fit linear search. +** That's perfect for us. Except that -pagezero_size must be set for OSX, +** otherwise the lower 4GB are blocked. And the 32GB RLIMIT_DATA needs +** to be reduced to 250MB on FreeBSD. +*/ +#if LJ_TARGET_OSX || defined(__DragonFly__) +#define MMAP_REGION_START ((uintptr_t)0x10000) +#elif LJ_TARGET_PS4 +#define MMAP_REGION_START ((uintptr_t)0x4000) +#else +#define MMAP_REGION_START ((uintptr_t)0x10000000) +#endif +#define MMAP_REGION_END ((uintptr_t)0x80000000) + +#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) && !LJ_TARGET_PS4 +#include <sys/resource.h> +#endif + +static LJ_AINLINE void *CALL_MMAP(size_t size) +{ + int olderr = errno; + /* Hint for next allocation. Doesn't need to be thread-safe. */ + static uintptr_t alloc_hint = MMAP_REGION_START; + int retry = 0; +#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) && !LJ_TARGET_PS4 + static int rlimit_modified = 0; + if (LJ_UNLIKELY(rlimit_modified == 0)) { + struct rlimit rlim; + rlim.rlim_cur = rlim.rlim_max = MMAP_REGION_START; + setrlimit(RLIMIT_DATA, &rlim); /* Ignore result. May fail below. */ + rlimit_modified = 1; + } +#endif + for (;;) { + void *p = mmap((void *)alloc_hint, size, MMAP_PROT, MMAP_FLAGS, -1, 0); + if ((uintptr_t)p >= MMAP_REGION_START && + (uintptr_t)p + size < MMAP_REGION_END) { + alloc_hint = (uintptr_t)p + size; + errno = olderr; + return p; + } + if (p != CMFAIL) munmap(p, size); +#if defined(__sun__) || defined(__DragonFly__) + alloc_hint += 0x1000000; /* Need near-exhaustive linear scan. */ + if (alloc_hint + size < MMAP_REGION_END) continue; +#endif + if (retry) break; + retry = 1; + alloc_hint = MMAP_REGION_START; + } + errno = olderr; + return CMFAIL; +} + +#else + +#error "NYI: need an equivalent of MAP_32BIT for this 64 bit OS" + +#endif + +#else + +/* 32 bit mode is easy. */ +static LJ_AINLINE void *CALL_MMAP(size_t size) +{ + int olderr = errno; + void *ptr = mmap(NULL, size, MMAP_PROT, MMAP_FLAGS, -1, 0); + errno = olderr; + return ptr; +} + +#endif + +#define INIT_MMAP() ((void)0) +#define DIRECT_MMAP(s) CALL_MMAP(s) + +static LJ_AINLINE int CALL_MUNMAP(void *ptr, size_t size) +{ + int olderr = errno; + int ret = munmap(ptr, size); + errno = olderr; + return ret; +} + +#if LJ_TARGET_LINUX +/* Need to define _GNU_SOURCE to get the mremap prototype. */ +static LJ_AINLINE void *CALL_MREMAP_(void *ptr, size_t osz, size_t nsz, + int flags) +{ + int olderr = errno; + ptr = mremap(ptr, osz, nsz, flags); + errno = olderr; + return ptr; +} + +#define CALL_MREMAP(addr, osz, nsz, mv) CALL_MREMAP_((addr), (osz), (nsz), (mv)) +#define CALL_MREMAP_NOMOVE 0 +#define CALL_MREMAP_MAYMOVE 1 +#if LJ_64 +#define CALL_MREMAP_MV CALL_MREMAP_NOMOVE +#else +#define CALL_MREMAP_MV CALL_MREMAP_MAYMOVE +#endif +#endif + +#endif + +#ifndef CALL_MREMAP +#define CALL_MREMAP(addr, osz, nsz, mv) ((void)osz, MFAIL) +#endif + +/* ----------------------- Chunk representations ------------------------ */ + +struct malloc_chunk { + size_t prev_foot; /* Size of previous chunk (if free). */ + size_t head; /* Size and inuse bits. */ + struct malloc_chunk *fd; /* double links -- used only if free. */ + struct malloc_chunk *bk; +}; + +typedef struct malloc_chunk mchunk; +typedef struct malloc_chunk *mchunkptr; +typedef struct malloc_chunk *sbinptr; /* The type of bins of chunks */ +typedef size_t bindex_t; /* Described below */ +typedef unsigned int binmap_t; /* Described below */ +typedef unsigned int flag_t; /* The type of various bit flag sets */ + +/* ------------------- Chunks sizes and alignments ----------------------- */ + +#define MCHUNK_SIZE (sizeof(mchunk)) + +#define CHUNK_OVERHEAD (SIZE_T_SIZE) + +/* Direct chunks need a second word of overhead ... */ +#define DIRECT_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +/* ... and additional padding for fake next-chunk at foot */ +#define DIRECT_FOOT_PAD (FOUR_SIZE_T_SIZES) + +/* The smallest size we can malloc is an aligned minimal chunk */ +#define MIN_CHUNK_SIZE\ + ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* conversion from malloc headers to user pointers, and back */ +#define chunk2mem(p) ((void *)((char *)(p) + TWO_SIZE_T_SIZES)) +#define mem2chunk(mem) ((mchunkptr)((char *)(mem) - TWO_SIZE_T_SIZES)) +/* chunk associated with aligned address A */ +#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) + +/* Bounds on request (not chunk) sizes. */ +#define MAX_REQUEST ((~MIN_CHUNK_SIZE+1) << 2) +#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) + +/* pad request bytes into a usable size */ +#define pad_request(req) \ + (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* pad request, checking for minimum (but not maximum) */ +#define request2size(req) \ + (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) + +/* ------------------ Operations on head and foot fields ----------------- */ + +#define PINUSE_BIT (SIZE_T_ONE) +#define CINUSE_BIT (SIZE_T_TWO) +#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) + +/* Head value for fenceposts */ +#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) + +/* extraction of fields from head words */ +#define cinuse(p) ((p)->head & CINUSE_BIT) +#define pinuse(p) ((p)->head & PINUSE_BIT) +#define chunksize(p) ((p)->head & ~(INUSE_BITS)) + +#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) +#define clear_cinuse(p) ((p)->head &= ~CINUSE_BIT) + +/* Treat space at ptr +/- offset as a chunk */ +#define chunk_plus_offset(p, s) ((mchunkptr)(((char *)(p)) + (s))) +#define chunk_minus_offset(p, s) ((mchunkptr)(((char *)(p)) - (s))) + +/* Ptr to next or previous physical malloc_chunk. */ +#define next_chunk(p) ((mchunkptr)(((char *)(p)) + ((p)->head & ~INUSE_BITS))) +#define prev_chunk(p) ((mchunkptr)(((char *)(p)) - ((p)->prev_foot) )) + +/* extract next chunk's pinuse bit */ +#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) + +/* Get/set size at footer */ +#define get_foot(p, s) (((mchunkptr)((char *)(p) + (s)))->prev_foot) +#define set_foot(p, s) (((mchunkptr)((char *)(p) + (s)))->prev_foot = (s)) + +/* Set size, pinuse bit, and foot */ +#define set_size_and_pinuse_of_free_chunk(p, s)\ + ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) + +/* Set size, pinuse bit, foot, and clear next pinuse */ +#define set_free_with_pinuse(p, s, n)\ + (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) + +#define is_direct(p)\ + (!((p)->head & PINUSE_BIT) && ((p)->prev_foot & IS_DIRECT_BIT)) + +/* Get the internal overhead associated with chunk p */ +#define overhead_for(p)\ + (is_direct(p)? DIRECT_CHUNK_OVERHEAD : CHUNK_OVERHEAD) + +/* ---------------------- Overlaid data structures ----------------------- */ + +struct malloc_tree_chunk { + /* The first four fields must be compatible with malloc_chunk */ + size_t prev_foot; + size_t head; + struct malloc_tree_chunk *fd; + struct malloc_tree_chunk *bk; + + struct malloc_tree_chunk *child[2]; + struct malloc_tree_chunk *parent; + bindex_t index; +}; + +typedef struct malloc_tree_chunk tchunk; +typedef struct malloc_tree_chunk *tchunkptr; +typedef struct malloc_tree_chunk *tbinptr; /* The type of bins of trees */ + +/* A little helper macro for trees */ +#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) + +/* ----------------------------- Segments -------------------------------- */ + +struct malloc_segment { + char *base; /* base address */ + size_t size; /* allocated size */ + struct malloc_segment *next; /* ptr to next segment */ +}; + +typedef struct malloc_segment msegment; +typedef struct malloc_segment *msegmentptr; + +/* ---------------------------- malloc_state ----------------------------- */ + +/* Bin types, widths and sizes */ +#define NSMALLBINS (32U) +#define NTREEBINS (32U) +#define SMALLBIN_SHIFT (3U) +#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) +#define TREEBIN_SHIFT (8U) +#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) +#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) +#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) + +struct malloc_state { + binmap_t smallmap; + binmap_t treemap; + size_t dvsize; + size_t topsize; + mchunkptr dv; + mchunkptr top; + size_t trim_check; + size_t release_checks; + mchunkptr smallbins[(NSMALLBINS+1)*2]; + tbinptr treebins[NTREEBINS]; + msegment seg; +}; + +typedef struct malloc_state *mstate; + +#define is_initialized(M) ((M)->top != 0) + +/* -------------------------- system alloc setup ------------------------- */ + +/* page-align a size */ +#define page_align(S)\ + (((S) + (LJ_PAGESIZE - SIZE_T_ONE)) & ~(LJ_PAGESIZE - SIZE_T_ONE)) + +/* granularity-align a size */ +#define granularity_align(S)\ + (((S) + (DEFAULT_GRANULARITY - SIZE_T_ONE))\ + & ~(DEFAULT_GRANULARITY - SIZE_T_ONE)) + +#if LJ_TARGET_WINDOWS +#define mmap_align(S) granularity_align(S) +#else +#define mmap_align(S) page_align(S) +#endif + +/* True if segment S holds address A */ +#define segment_holds(S, A)\ + ((char *)(A) >= S->base && (char *)(A) < S->base + S->size) + +/* Return segment holding given address */ +static msegmentptr segment_holding(mstate m, char *addr) +{ + msegmentptr sp = &m->seg; + for (;;) { + if (addr >= sp->base && addr < sp->base + sp->size) + return sp; + if ((sp = sp->next) == 0) + return 0; + } +} + +/* Return true if segment contains a segment link */ +static int has_segment_link(mstate m, msegmentptr ss) +{ + msegmentptr sp = &m->seg; + for (;;) { + if ((char *)sp >= ss->base && (char *)sp < ss->base + ss->size) + return 1; + if ((sp = sp->next) == 0) + return 0; + } +} + +/* + TOP_FOOT_SIZE is padding at the end of a segment, including space + that may be needed to place segment records and fenceposts when new + noncontiguous segments are added. +*/ +#define TOP_FOOT_SIZE\ + (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) + +/* ---------------------------- Indexing Bins ---------------------------- */ + +#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) +#define small_index(s) ((s) >> SMALLBIN_SHIFT) +#define small_index2size(i) ((i) << SMALLBIN_SHIFT) +#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) + +/* addressing by index. See above about smallbin repositioning */ +#define smallbin_at(M, i) ((sbinptr)((char *)&((M)->smallbins[(i)<<1]))) +#define treebin_at(M,i) (&((M)->treebins[i])) + +/* assign tree index for size S to variable I */ +#define compute_tree_index(S, I)\ +{\ + unsigned int X = (unsigned int)(S >> TREEBIN_SHIFT);\ + if (X == 0) {\ + I = 0;\ + } else if (X > 0xFFFF) {\ + I = NTREEBINS-1;\ + } else {\ + unsigned int K = lj_fls(X);\ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} + +/* Bit representing maximum resolved size in a treebin at i */ +#define bit_for_tree_index(i) \ + (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) + +/* Shift placing maximum resolved bit in a treebin at i as sign bit */ +#define leftshift_for_tree_index(i) \ + ((i == NTREEBINS-1)? 0 : \ + ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) + +/* The size of the smallest chunk held in bin with index i */ +#define minsize_for_tree_index(i) \ + ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ + (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) + +/* ------------------------ Operations on bin maps ----------------------- */ + +/* bit corresponding to given index */ +#define idx2bit(i) ((binmap_t)(1) << (i)) + +/* Mark/Clear bits with given index */ +#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) +#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) +#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) + +#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) +#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) +#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) + +/* mask with all bits to left of least bit of x on */ +#define left_bits(x) ((x<<1) | (~(x<<1)+1)) + +/* Set cinuse bit and pinuse bit of next chunk */ +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + ((mchunkptr)(((char *)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + ((mchunkptr)(((char *)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set size, cinuse and pinuse bit of this chunk */ +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) + +/* ----------------------- Operations on smallbins ----------------------- */ + +/* Link a free chunk into a smallbin */ +#define insert_small_chunk(M, P, S) {\ + bindex_t I = small_index(S);\ + mchunkptr B = smallbin_at(M, I);\ + mchunkptr F = B;\ + if (!smallmap_is_marked(M, I))\ + mark_smallmap(M, I);\ + else\ + F = B->fd;\ + B->fd = P;\ + F->bk = P;\ + P->fd = F;\ + P->bk = B;\ +} + +/* Unlink a chunk from a smallbin */ +#define unlink_small_chunk(M, P, S) {\ + mchunkptr F = P->fd;\ + mchunkptr B = P->bk;\ + bindex_t I = small_index(S);\ + if (F == B) {\ + clear_smallmap(M, I);\ + } else {\ + F->bk = B;\ + B->fd = F;\ + }\ +} + +/* Unlink the first chunk from a smallbin */ +#define unlink_first_small_chunk(M, B, P, I) {\ + mchunkptr F = P->fd;\ + if (B == F) {\ + clear_smallmap(M, I);\ + } else {\ + B->fd = F;\ + F->bk = B;\ + }\ +} + +/* Replace dv node, binning the old one */ +/* Used only when dvsize known to be small */ +#define replace_dv(M, P, S) {\ + size_t DVS = M->dvsize;\ + if (DVS != 0) {\ + mchunkptr DV = M->dv;\ + insert_small_chunk(M, DV, DVS);\ + }\ + M->dvsize = S;\ + M->dv = P;\ +} + +/* ------------------------- Operations on trees ------------------------- */ + +/* Insert chunk into tree */ +#define insert_large_chunk(M, X, S) {\ + tbinptr *H;\ + bindex_t I;\ + compute_tree_index(S, I);\ + H = treebin_at(M, I);\ + X->index = I;\ + X->child[0] = X->child[1] = 0;\ + if (!treemap_is_marked(M, I)) {\ + mark_treemap(M, I);\ + *H = X;\ + X->parent = (tchunkptr)H;\ + X->fd = X->bk = X;\ + } else {\ + tchunkptr T = *H;\ + size_t K = S << leftshift_for_tree_index(I);\ + for (;;) {\ + if (chunksize(T) != S) {\ + tchunkptr *C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ + K <<= 1;\ + if (*C != 0) {\ + T = *C;\ + } else {\ + *C = X;\ + X->parent = T;\ + X->fd = X->bk = X;\ + break;\ + }\ + } else {\ + tchunkptr F = T->fd;\ + T->fd = F->bk = X;\ + X->fd = F;\ + X->bk = T;\ + X->parent = 0;\ + break;\ + }\ + }\ + }\ +} + +#define unlink_large_chunk(M, X) {\ + tchunkptr XP = X->parent;\ + tchunkptr R;\ + if (X->bk != X) {\ + tchunkptr F = X->fd;\ + R = X->bk;\ + F->bk = R;\ + R->fd = F;\ + } else {\ + tchunkptr *RP;\ + if (((R = *(RP = &(X->child[1]))) != 0) ||\ + ((R = *(RP = &(X->child[0]))) != 0)) {\ + tchunkptr *CP;\ + while ((*(CP = &(R->child[1])) != 0) ||\ + (*(CP = &(R->child[0])) != 0)) {\ + R = *(RP = CP);\ + }\ + *RP = 0;\ + }\ + }\ + if (XP != 0) {\ + tbinptr *H = treebin_at(M, X->index);\ + if (X == *H) {\ + if ((*H = R) == 0) \ + clear_treemap(M, X->index);\ + } else {\ + if (XP->child[0] == X) \ + XP->child[0] = R;\ + else \ + XP->child[1] = R;\ + }\ + if (R != 0) {\ + tchunkptr C0, C1;\ + R->parent = XP;\ + if ((C0 = X->child[0]) != 0) {\ + R->child[0] = C0;\ + C0->parent = R;\ + }\ + if ((C1 = X->child[1]) != 0) {\ + R->child[1] = C1;\ + C1->parent = R;\ + }\ + }\ + }\ +} + +/* Relays to large vs small bin operations */ + +#define insert_chunk(M, P, S)\ + if (is_small(S)) { insert_small_chunk(M, P, S)\ + } else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } + +#define unlink_chunk(M, P, S)\ + if (is_small(S)) { unlink_small_chunk(M, P, S)\ + } else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } + +/* ----------------------- Direct-mmapping chunks ----------------------- */ + +static void *direct_alloc(size_t nb) +{ + size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + if (LJ_LIKELY(mmsize > nb)) { /* Check for wrap around 0 */ + char *mm = (char *)(DIRECT_MMAP(mmsize)); + if (mm != CMFAIL) { + size_t offset = align_offset(chunk2mem(mm)); + size_t psize = mmsize - offset - DIRECT_FOOT_PAD; + mchunkptr p = (mchunkptr)(mm + offset); + p->prev_foot = offset | IS_DIRECT_BIT; + p->head = psize|CINUSE_BIT; + chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; + return chunk2mem(p); + } + } + return NULL; +} + +static mchunkptr direct_resize(mchunkptr oldp, size_t nb) +{ + size_t oldsize = chunksize(oldp); + if (is_small(nb)) /* Can't shrink direct regions below small size */ + return NULL; + /* Keep old chunk if big enough but not too big */ + if (oldsize >= nb + SIZE_T_SIZE && + (oldsize - nb) <= (DEFAULT_GRANULARITY >> 1)) { + return oldp; + } else { + size_t offset = oldp->prev_foot & ~IS_DIRECT_BIT; + size_t oldmmsize = oldsize + offset + DIRECT_FOOT_PAD; + size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + char *cp = (char *)CALL_MREMAP((char *)oldp - offset, + oldmmsize, newmmsize, CALL_MREMAP_MV); + if (cp != CMFAIL) { + mchunkptr newp = (mchunkptr)(cp + offset); + size_t psize = newmmsize - offset - DIRECT_FOOT_PAD; + newp->head = psize|CINUSE_BIT; + chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; + return newp; + } + } + return NULL; +} + +/* -------------------------- mspace management -------------------------- */ + +/* Initialize top chunk and its size */ +static void init_top(mstate m, mchunkptr p, size_t psize) +{ + /* Ensure alignment */ + size_t offset = align_offset(chunk2mem(p)); + p = (mchunkptr)((char *)p + offset); + psize -= offset; + + m->top = p; + m->topsize = psize; + p->head = psize | PINUSE_BIT; + /* set size of fake trailing chunk holding overhead space only once */ + chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; + m->trim_check = DEFAULT_TRIM_THRESHOLD; /* reset on each update */ +} + +/* Initialize bins for a new mstate that is otherwise zeroed out */ +static void init_bins(mstate m) +{ + /* Establish circular links for smallbins */ + bindex_t i; + for (i = 0; i < NSMALLBINS; i++) { + sbinptr bin = smallbin_at(m,i); + bin->fd = bin->bk = bin; + } +} + +/* Allocate chunk and prepend remainder with chunk in successor base. */ +static void *prepend_alloc(mstate m, char *newbase, char *oldbase, size_t nb) +{ + mchunkptr p = align_as_chunk(newbase); + mchunkptr oldfirst = align_as_chunk(oldbase); + size_t psize = (size_t)((char *)oldfirst - (char *)p); + mchunkptr q = chunk_plus_offset(p, nb); + size_t qsize = psize - nb; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + + /* consolidate remainder with first chunk of old base */ + if (oldfirst == m->top) { + size_t tsize = m->topsize += qsize; + m->top = q; + q->head = tsize | PINUSE_BIT; + } else if (oldfirst == m->dv) { + size_t dsize = m->dvsize += qsize; + m->dv = q; + set_size_and_pinuse_of_free_chunk(q, dsize); + } else { + if (!cinuse(oldfirst)) { + size_t nsize = chunksize(oldfirst); + unlink_chunk(m, oldfirst, nsize); + oldfirst = chunk_plus_offset(oldfirst, nsize); + qsize += nsize; + } + set_free_with_pinuse(q, qsize, oldfirst); + insert_chunk(m, q, qsize); + } + + return chunk2mem(p); +} + +/* Add a segment to hold a new noncontiguous region */ +static void add_segment(mstate m, char *tbase, size_t tsize) +{ + /* Determine locations and sizes of segment, fenceposts, old top */ + char *old_top = (char *)m->top; + msegmentptr oldsp = segment_holding(m, old_top); + char *old_end = oldsp->base + oldsp->size; + size_t ssize = pad_request(sizeof(struct malloc_segment)); + char *rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + size_t offset = align_offset(chunk2mem(rawsp)); + char *asp = rawsp + offset; + char *csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; + mchunkptr sp = (mchunkptr)csp; + msegmentptr ss = (msegmentptr)(chunk2mem(sp)); + mchunkptr tnext = chunk_plus_offset(sp, ssize); + mchunkptr p = tnext; + + /* reset top to new space */ + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + + /* Set up segment record */ + set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); + *ss = m->seg; /* Push current record */ + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.next = ss; + + /* Insert trailing fenceposts */ + for (;;) { + mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); + p->head = FENCEPOST_HEAD; + if ((char *)(&(nextp->head)) < old_end) + p = nextp; + else + break; + } + + /* Insert the rest of old top into a bin as an ordinary free chunk */ + if (csp != old_top) { + mchunkptr q = (mchunkptr)old_top; + size_t psize = (size_t)(csp - old_top); + mchunkptr tn = chunk_plus_offset(q, psize); + set_free_with_pinuse(q, psize, tn); + insert_chunk(m, q, psize); + } +} + +/* -------------------------- System allocation -------------------------- */ + +static void *alloc_sys(mstate m, size_t nb) +{ + char *tbase = CMFAIL; + size_t tsize = 0; + + /* Directly map large chunks */ + if (LJ_UNLIKELY(nb >= DEFAULT_MMAP_THRESHOLD)) { + void *mem = direct_alloc(nb); + if (mem != 0) + return mem; + } + + { + size_t req = nb + TOP_FOOT_SIZE + SIZE_T_ONE; + size_t rsize = granularity_align(req); + if (LJ_LIKELY(rsize > nb)) { /* Fail if wraps around zero */ + char *mp = (char *)(CALL_MMAP(rsize)); + if (mp != CMFAIL) { + tbase = mp; + tsize = rsize; + } + } + } + + if (tbase != CMFAIL) { + msegmentptr sp = &m->seg; + /* Try to merge with an existing segment */ + while (sp != 0 && tbase != sp->base + sp->size) + sp = sp->next; + if (sp != 0 && segment_holds(sp, m->top)) { /* append */ + sp->size += tsize; + init_top(m, m->top, m->topsize + tsize); + } else { + sp = &m->seg; + while (sp != 0 && sp->base != tbase + tsize) + sp = sp->next; + if (sp != 0) { + char *oldbase = sp->base; + sp->base = tbase; + sp->size += tsize; + return prepend_alloc(m, tbase, oldbase, nb); + } else { + add_segment(m, tbase, tsize); + } + } + + if (nb < m->topsize) { /* Allocate from new or extended top space */ + size_t rsize = m->topsize -= nb; + mchunkptr p = m->top; + mchunkptr r = m->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + return chunk2mem(p); + } + } + + return NULL; +} + +/* ----------------------- system deallocation -------------------------- */ + +/* Unmap and unlink any mmapped segments that don't contain used chunks */ +static size_t release_unused_segments(mstate m) +{ + size_t released = 0; + size_t nsegs = 0; + msegmentptr pred = &m->seg; + msegmentptr sp = pred->next; + while (sp != 0) { + char *base = sp->base; + size_t size = sp->size; + msegmentptr next = sp->next; + nsegs++; + { + mchunkptr p = align_as_chunk(base); + size_t psize = chunksize(p); + /* Can unmap if first chunk holds entire segment and not pinned */ + if (!cinuse(p) && (char *)p + psize >= base + size - TOP_FOOT_SIZE) { + tchunkptr tp = (tchunkptr)p; + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } else { + unlink_large_chunk(m, tp); + } + if (CALL_MUNMAP(base, size) == 0) { + released += size; + /* unlink obsoleted record */ + sp = pred; + sp->next = next; + } else { /* back out if cannot unmap */ + insert_large_chunk(m, tp, psize); + } + } + } + pred = sp; + sp = next; + } + /* Reset check counter */ + m->release_checks = nsegs > MAX_RELEASE_CHECK_RATE ? + nsegs : MAX_RELEASE_CHECK_RATE; + return released; +} + +static int alloc_trim(mstate m, size_t pad) +{ + size_t released = 0; + if (pad < MAX_REQUEST && is_initialized(m)) { + pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ + + if (m->topsize > pad) { + /* Shrink top space in granularity-size units, keeping at least one */ + size_t unit = DEFAULT_GRANULARITY; + size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - + SIZE_T_ONE) * unit; + msegmentptr sp = segment_holding(m, (char *)m->top); + + if (sp->size >= extra && + !has_segment_link(m, sp)) { /* can't shrink if pinned */ + size_t newsize = sp->size - extra; + /* Prefer mremap, fall back to munmap */ + if ((CALL_MREMAP(sp->base, sp->size, newsize, CALL_MREMAP_NOMOVE) != MFAIL) || + (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { + released = extra; + } + } + + if (released != 0) { + sp->size -= released; + init_top(m, m->top, m->topsize - released); + } + } + + /* Unmap any unused mmapped segments */ + released += release_unused_segments(m); + + /* On failure, disable autotrim to avoid repeated failed future calls */ + if (released == 0 && m->topsize > m->trim_check) + m->trim_check = MAX_SIZE_T; + } + + return (released != 0)? 1 : 0; +} + +/* ---------------------------- malloc support --------------------------- */ + +/* allocate a large request from the best fitting chunk in a treebin */ +static void *tmalloc_large(mstate m, size_t nb) +{ + tchunkptr v = 0; + size_t rsize = ~nb+1; /* Unsigned negation */ + tchunkptr t; + bindex_t idx; + compute_tree_index(nb, idx); + + if ((t = *treebin_at(m, idx)) != 0) { + /* Traverse tree for this bin looking for node with size == nb */ + size_t sizebits = nb << leftshift_for_tree_index(idx); + tchunkptr rst = 0; /* The deepest untaken right subtree */ + for (;;) { + tchunkptr rt; + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + v = t; + if ((rsize = trem) == 0) + break; + } + rt = t->child[1]; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + if (rt != 0 && rt != t) + rst = rt; + if (t == 0) { + t = rst; /* set t to least subtree holding sizes > nb */ + break; + } + sizebits <<= 1; + } + } + + if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ + binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; + if (leftbits != 0) + t = *treebin_at(m, lj_ffs(leftbits)); + } + + while (t != 0) { /* find smallest of tree or subtree */ + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + t = leftmost_child(t); + } + + /* If dv is a better fit, return NULL so malloc will use it */ + if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { + mchunkptr r = chunk_plus_offset(v, nb); + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) { + set_inuse_and_pinuse(m, v, (rsize + nb)); + } else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(m, r, rsize); + } + return chunk2mem(v); + } + return NULL; +} + +/* allocate a small request from the best fitting chunk in a treebin */ +static void *tmalloc_small(mstate m, size_t nb) +{ + tchunkptr t, v; + mchunkptr r; + size_t rsize; + bindex_t i = lj_ffs(m->treemap); + + v = t = *treebin_at(m, i); + rsize = chunksize(t) - nb; + + while ((t = leftmost_child(t)) != 0) { + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + } + + r = chunk_plus_offset(v, nb); + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) { + set_inuse_and_pinuse(m, v, (rsize + nb)); + } else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(m, r, rsize); + } + return chunk2mem(v); +} + +/* ----------------------------------------------------------------------- */ + +void *lj_alloc_create(void) +{ + size_t tsize = DEFAULT_GRANULARITY; + char *tbase; + INIT_MMAP(); + tbase = (char *)(CALL_MMAP(tsize)); + if (tbase != CMFAIL) { + size_t msize = pad_request(sizeof(struct malloc_state)); + mchunkptr mn; + mchunkptr msp = align_as_chunk(tbase); + mstate m = (mstate)(chunk2mem(msp)); + memset(m, 0, msize); + msp->head = (msize|PINUSE_BIT|CINUSE_BIT); + m->seg.base = tbase; + m->seg.size = tsize; + m->release_checks = MAX_RELEASE_CHECK_RATE; + init_bins(m); + mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char *)mn) - TOP_FOOT_SIZE); + return m; + } + return NULL; +} + +void lj_alloc_destroy(void *msp) +{ + mstate ms = (mstate)msp; + msegmentptr sp = &ms->seg; + while (sp != 0) { + char *base = sp->base; + size_t size = sp->size; + sp = sp->next; + CALL_MUNMAP(base, size); + } +} + +static LJ_NOINLINE void *lj_alloc_malloc(void *msp, size_t nsize) +{ + mstate ms = (mstate)msp; + void *mem; + size_t nb; + if (nsize <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (nsize < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(nsize); + idx = small_index(nb); + smallbits = ms->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(ms, idx); + p = b->fd; + unlink_first_small_chunk(ms, b, p, idx); + set_inuse_and_pinuse(ms, p, small_index2size(idx)); + mem = chunk2mem(p); + return mem; + } else if (nb > ms->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + bindex_t i = lj_ffs(leftbits); + b = smallbin_at(ms, i); + p = b->fd; + unlink_first_small_chunk(ms, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) { + set_inuse_and_pinuse(ms, p, small_index2size(i)); + } else { + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(ms, r, rsize); + } + mem = chunk2mem(p); + return mem; + } else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { + return mem; + } + } + } else if (nsize >= MAX_REQUEST) { + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + } else { + nb = pad_request(nsize); + if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { + return mem; + } + } + + if (nb <= ms->dvsize) { + size_t rsize = ms->dvsize - nb; + mchunkptr p = ms->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = ms->dv = chunk_plus_offset(p, nb); + ms->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + } else { /* exhaust dv */ + size_t dvs = ms->dvsize; + ms->dvsize = 0; + ms->dv = 0; + set_inuse_and_pinuse(ms, p, dvs); + } + mem = chunk2mem(p); + return mem; + } else if (nb < ms->topsize) { /* Split top */ + size_t rsize = ms->topsize -= nb; + mchunkptr p = ms->top; + mchunkptr r = ms->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + mem = chunk2mem(p); + return mem; + } + return alloc_sys(ms, nb); +} + +static LJ_NOINLINE void *lj_alloc_free(void *msp, void *ptr) +{ + if (ptr != 0) { + mchunkptr p = mem2chunk(ptr); + mstate fm = (mstate)msp; + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if ((prevsize & IS_DIRECT_BIT) != 0) { + prevsize &= ~IS_DIRECT_BIT; + psize += prevsize + DIRECT_FOOT_PAD; + CALL_MUNMAP((char *)p - prevsize, psize); + return NULL; + } else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + return NULL; + } + } + } + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (tsize > fm->trim_check) + alloc_trim(fm, 0); + return NULL; + } else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + return NULL; + } else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + return NULL; + } + } + } else { + set_free_with_pinuse(p, psize, next); + } + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + } else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + } + return NULL; +} + +static LJ_NOINLINE void *lj_alloc_realloc(void *msp, void *ptr, size_t nsize) +{ + if (nsize >= MAX_REQUEST) { + return NULL; + } else { + mstate m = (mstate)msp; + mchunkptr oldp = mem2chunk(ptr); + size_t oldsize = chunksize(oldp); + mchunkptr next = chunk_plus_offset(oldp, oldsize); + mchunkptr newp = 0; + size_t nb = request2size(nsize); + + /* Try to either shrink or extend into top. Else malloc-copy-free */ + if (is_direct(oldp)) { + newp = direct_resize(oldp, nb); /* this may return NULL. */ + } else if (oldsize >= nb) { /* already big enough */ + size_t rsize = oldsize - nb; + newp = oldp; + if (rsize >= MIN_CHUNK_SIZE) { + mchunkptr rem = chunk_plus_offset(newp, nb); + set_inuse(m, newp, nb); + set_inuse(m, rem, rsize); + lj_alloc_free(m, chunk2mem(rem)); + } + } else if (next == m->top && oldsize + m->topsize > nb) { + /* Expand into top */ + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; + mchunkptr newtop = chunk_plus_offset(oldp, nb); + set_inuse(m, oldp, nb); + newtop->head = newtopsize |PINUSE_BIT; + m->top = newtop; + m->topsize = newtopsize; + newp = oldp; + } + + if (newp != 0) { + return chunk2mem(newp); + } else { + void *newmem = lj_alloc_malloc(m, nsize); + if (newmem != 0) { + size_t oc = oldsize - overhead_for(oldp); + memcpy(newmem, ptr, oc < nsize ? oc : nsize); + lj_alloc_free(m, ptr); + } + return newmem; + } + } +} + +void *lj_alloc_f(void *msp, void *ptr, size_t osize, size_t nsize) +{ + (void)osize; + if (nsize == 0) { + return lj_alloc_free(msp, ptr); + } else if (ptr == NULL) { + return lj_alloc_malloc(msp, nsize); + } else { + return lj_alloc_realloc(msp, ptr, nsize); + } +} + +#endif
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1f27b840/lib/luajit/src/lj_alloc.h ---------------------------------------------------------------------- diff --git a/lib/luajit/src/lj_alloc.h b/lib/luajit/src/lj_alloc.h new file mode 100644 index 0000000..f87a7cf --- /dev/null +++ b/lib/luajit/src/lj_alloc.h @@ -0,0 +1,17 @@ +/* +** Bundled memory allocator. +** Donated to the public domain. +*/ + +#ifndef _LJ_ALLOC_H +#define _LJ_ALLOC_H + +#include "lj_def.h" + +#ifndef LUAJIT_USE_SYSMALLOC +LJ_FUNC void *lj_alloc_create(void); +LJ_FUNC void lj_alloc_destroy(void *msp); +LJ_FUNC void *lj_alloc_f(void *msp, void *ptr, size_t osize, size_t nsize); +#endif + +#endif http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1f27b840/lib/luajit/src/lj_api.c ---------------------------------------------------------------------- diff --git a/lib/luajit/src/lj_api.c b/lib/luajit/src/lj_api.c new file mode 100644 index 0000000..3bedb39 --- /dev/null +++ b/lib/luajit/src/lj_api.c @@ -0,0 +1,1200 @@ +/* +** Public Lua/C API. +** Copyright (C) 2005-2015 Mike Pall. See Copyright Notice in luajit.h +** +** Major portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#define lj_api_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_debug.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_func.h" +#include "lj_udata.h" +#include "lj_meta.h" +#include "lj_state.h" +#include "lj_bc.h" +#include "lj_frame.h" +#include "lj_trace.h" +#include "lj_vm.h" +#include "lj_strscan.h" + +/* -- Common helper functions --------------------------------------------- */ + +#define api_checknelems(L, n) api_check(L, (n) <= (L->top - L->base)) +#define api_checkvalidindex(L, i) api_check(L, (i) != niltv(L)) + +static TValue *index2adr(lua_State *L, int idx) +{ + if (idx > 0) { + TValue *o = L->base + (idx - 1); + return o < L->top ? o : niltv(L); + } else if (idx > LUA_REGISTRYINDEX) { + api_check(L, idx != 0 && -idx <= L->top - L->base); + return L->top + idx; + } else if (idx == LUA_GLOBALSINDEX) { + TValue *o = &G(L)->tmptv; + settabV(L, o, tabref(L->env)); + return o; + } else if (idx == LUA_REGISTRYINDEX) { + return registry(L); + } else { + GCfunc *fn = curr_func(L); + api_check(L, fn->c.gct == ~LJ_TFUNC && !isluafunc(fn)); + if (idx == LUA_ENVIRONINDEX) { + TValue *o = &G(L)->tmptv; + settabV(L, o, tabref(fn->c.env)); + return o; + } else { + idx = LUA_GLOBALSINDEX - idx; + return idx <= fn->c.nupvalues ? &fn->c.upvalue[idx-1] : niltv(L); + } + } +} + +static TValue *stkindex2adr(lua_State *L, int idx) +{ + if (idx > 0) { + TValue *o = L->base + (idx - 1); + return o < L->top ? o : niltv(L); + } else { + api_check(L, idx != 0 && -idx <= L->top - L->base); + return L->top + idx; + } +} + +static GCtab *getcurrenv(lua_State *L) +{ + GCfunc *fn = curr_func(L); + return fn->c.gct == ~LJ_TFUNC ? tabref(fn->c.env) : tabref(L->env); +} + +/* -- Miscellaneous API functions ----------------------------------------- */ + +LUA_API int lua_status(lua_State *L) +{ + return L->status; +} + +LUA_API int lua_checkstack(lua_State *L, int size) +{ + if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK) { + return 0; /* Stack overflow. */ + } else if (size > 0) { + lj_state_checkstack(L, (MSize)size); + } + return 1; +} + +LUALIB_API void luaL_checkstack(lua_State *L, int size, const char *msg) +{ + if (!lua_checkstack(L, size)) + lj_err_callerv(L, LJ_ERR_STKOVM, msg); +} + +LUA_API void lua_xmove(lua_State *from, lua_State *to, int n) +{ + TValue *f, *t; + if (from == to) return; + api_checknelems(from, n); + api_check(from, G(from) == G(to)); + lj_state_checkstack(to, (MSize)n); + f = from->top; + t = to->top = to->top + n; + while (--n >= 0) copyTV(to, --t, --f); + from->top = f; +} + +/* -- Stack manipulation -------------------------------------------------- */ + +LUA_API int lua_gettop(lua_State *L) +{ + return (int)(L->top - L->base); +} + +LUA_API void lua_settop(lua_State *L, int idx) +{ + if (idx >= 0) { + api_check(L, idx <= tvref(L->maxstack) - L->base); + if (L->base + idx > L->top) { + if (L->base + idx >= tvref(L->maxstack)) + lj_state_growstack(L, (MSize)idx - (MSize)(L->top - L->base)); + do { setnilV(L->top++); } while (L->top < L->base + idx); + } else { + L->top = L->base + idx; + } + } else { + api_check(L, -(idx+1) <= (L->top - L->base)); + L->top += idx+1; /* Shrinks top (idx < 0). */ + } +} + +LUA_API void lua_remove(lua_State *L, int idx) +{ + TValue *p = stkindex2adr(L, idx); + api_checkvalidindex(L, p); + while (++p < L->top) copyTV(L, p-1, p); + L->top--; +} + +LUA_API void lua_insert(lua_State *L, int idx) +{ + TValue *q, *p = stkindex2adr(L, idx); + api_checkvalidindex(L, p); + for (q = L->top; q > p; q--) copyTV(L, q, q-1); + copyTV(L, p, L->top); +} + +LUA_API void lua_replace(lua_State *L, int idx) +{ + api_checknelems(L, 1); + if (idx == LUA_GLOBALSINDEX) { + api_check(L, tvistab(L->top-1)); + /* NOBARRIER: A thread (i.e. L) is never black. */ + setgcref(L->env, obj2gco(tabV(L->top-1))); + } else if (idx == LUA_ENVIRONINDEX) { + GCfunc *fn = curr_func(L); + if (fn->c.gct != ~LJ_TFUNC) + lj_err_msg(L, LJ_ERR_NOENV); + api_check(L, tvistab(L->top-1)); + setgcref(fn->c.env, obj2gco(tabV(L->top-1))); + lj_gc_barrier(L, fn, L->top-1); + } else { + TValue *o = index2adr(L, idx); + api_checkvalidindex(L, o); + copyTV(L, o, L->top-1); + if (idx < LUA_GLOBALSINDEX) /* Need a barrier for upvalues. */ + lj_gc_barrier(L, curr_func(L), L->top-1); + } + L->top--; +} + +LUA_API void lua_pushvalue(lua_State *L, int idx) +{ + copyTV(L, L->top, index2adr(L, idx)); + incr_top(L); +} + +/* -- Stack getters ------------------------------------------------------- */ + +LUA_API int lua_type(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + if (tvisnumber(o)) { + return LUA_TNUMBER; +#if LJ_64 + } else if (tvislightud(o)) { + return LUA_TLIGHTUSERDATA; +#endif + } else if (o == niltv(L)) { + return LUA_TNONE; + } else { /* Magic internal/external tag conversion. ORDER LJ_T */ + uint32_t t = ~itype(o); +#if LJ_64 + int tt = (int)((U64x(75a06,98042110) >> 4*t) & 15u); +#else + int tt = (int)(((t < 8 ? 0x98042110u : 0x75a06u) >> 4*(t&7)) & 15u); +#endif + lua_assert(tt != LUA_TNIL || tvisnil(o)); + return tt; + } +} + +LUALIB_API void luaL_checktype(lua_State *L, int idx, int tt) +{ + if (lua_type(L, idx) != tt) + lj_err_argt(L, idx, tt); +} + +LUALIB_API void luaL_checkany(lua_State *L, int idx) +{ + if (index2adr(L, idx) == niltv(L)) + lj_err_arg(L, idx, LJ_ERR_NOVAL); +} + +LUA_API const char *lua_typename(lua_State *L, int t) +{ + UNUSED(L); + return lj_obj_typename[t+1]; +} + +LUA_API int lua_iscfunction(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + return tvisfunc(o) && !isluafunc(funcV(o)); +} + +LUA_API int lua_isnumber(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + return (tvisnumber(o) || (tvisstr(o) && lj_strscan_number(strV(o), &tmp))); +} + +LUA_API int lua_isstring(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + return (tvisstr(o) || tvisnumber(o)); +} + +LUA_API int lua_isuserdata(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + return (tvisudata(o) || tvislightud(o)); +} + +LUA_API int lua_rawequal(lua_State *L, int idx1, int idx2) +{ + cTValue *o1 = index2adr(L, idx1); + cTValue *o2 = index2adr(L, idx2); + return (o1 == niltv(L) || o2 == niltv(L)) ? 0 : lj_obj_equal(o1, o2); +} + +LUA_API int lua_equal(lua_State *L, int idx1, int idx2) +{ + cTValue *o1 = index2adr(L, idx1); + cTValue *o2 = index2adr(L, idx2); + if (tvisint(o1) && tvisint(o2)) { + return intV(o1) == intV(o2); + } else if (tvisnumber(o1) && tvisnumber(o2)) { + return numberVnum(o1) == numberVnum(o2); + } else if (itype(o1) != itype(o2)) { + return 0; + } else if (tvispri(o1)) { + return o1 != niltv(L) && o2 != niltv(L); +#if LJ_64 + } else if (tvislightud(o1)) { + return o1->u64 == o2->u64; +#endif + } else if (gcrefeq(o1->gcr, o2->gcr)) { + return 1; + } else if (!tvistabud(o1)) { + return 0; + } else { + TValue *base = lj_meta_equal(L, gcV(o1), gcV(o2), 0); + if ((uintptr_t)base <= 1) { + return (int)(uintptr_t)base; + } else { + L->top = base+2; + lj_vm_call(L, base, 1+1); + L->top -= 2; + return tvistruecond(L->top+1); + } + } +} + +LUA_API int lua_lessthan(lua_State *L, int idx1, int idx2) +{ + cTValue *o1 = index2adr(L, idx1); + cTValue *o2 = index2adr(L, idx2); + if (o1 == niltv(L) || o2 == niltv(L)) { + return 0; + } else if (tvisint(o1) && tvisint(o2)) { + return intV(o1) < intV(o2); + } else if (tvisnumber(o1) && tvisnumber(o2)) { + return numberVnum(o1) < numberVnum(o2); + } else { + TValue *base = lj_meta_comp(L, o1, o2, 0); + if ((uintptr_t)base <= 1) { + return (int)(uintptr_t)base; + } else { + L->top = base+2; + lj_vm_call(L, base, 1+1); + L->top -= 2; + return tvistruecond(L->top+1); + } + } +} + +LUA_API lua_Number lua_tonumber(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + if (LJ_LIKELY(tvisnumber(o))) + return numberVnum(o); + else if (tvisstr(o) && lj_strscan_num(strV(o), &tmp)) + return numV(&tmp); + else + return 0; +} + +LUALIB_API lua_Number luaL_checknumber(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + if (LJ_LIKELY(tvisnumber(o))) + return numberVnum(o); + else if (!(tvisstr(o) && lj_strscan_num(strV(o), &tmp))) + lj_err_argt(L, idx, LUA_TNUMBER); + return numV(&tmp); +} + +LUALIB_API lua_Number luaL_optnumber(lua_State *L, int idx, lua_Number def) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + if (LJ_LIKELY(tvisnumber(o))) + return numberVnum(o); + else if (tvisnil(o)) + return def; + else if (!(tvisstr(o) && lj_strscan_num(strV(o), &tmp))) + lj_err_argt(L, idx, LUA_TNUMBER); + return numV(&tmp); +} + +LUA_API lua_Integer lua_tointeger(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + lua_Number n; + if (LJ_LIKELY(tvisint(o))) { + return intV(o); + } else if (LJ_LIKELY(tvisnum(o))) { + n = numV(o); + } else { + if (!(tvisstr(o) && lj_strscan_number(strV(o), &tmp))) + return 0; + if (tvisint(&tmp)) + return (lua_Integer)intV(&tmp); + n = numV(&tmp); + } +#if LJ_64 + return (lua_Integer)n; +#else + return lj_num2int(n); +#endif +} + +LUALIB_API lua_Integer luaL_checkinteger(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + lua_Number n; + if (LJ_LIKELY(tvisint(o))) { + return intV(o); + } else if (LJ_LIKELY(tvisnum(o))) { + n = numV(o); + } else { + if (!(tvisstr(o) && lj_strscan_number(strV(o), &tmp))) + lj_err_argt(L, idx, LUA_TNUMBER); + if (tvisint(&tmp)) + return (lua_Integer)intV(&tmp); + n = numV(&tmp); + } +#if LJ_64 + return (lua_Integer)n; +#else + return lj_num2int(n); +#endif +} + +LUALIB_API lua_Integer luaL_optinteger(lua_State *L, int idx, lua_Integer def) +{ + cTValue *o = index2adr(L, idx); + TValue tmp; + lua_Number n; + if (LJ_LIKELY(tvisint(o))) { + return intV(o); + } else if (LJ_LIKELY(tvisnum(o))) { + n = numV(o); + } else if (tvisnil(o)) { + return def; + } else { + if (!(tvisstr(o) && lj_strscan_number(strV(o), &tmp))) + lj_err_argt(L, idx, LUA_TNUMBER); + if (tvisint(&tmp)) + return (lua_Integer)intV(&tmp); + n = numV(&tmp); + } +#if LJ_64 + return (lua_Integer)n; +#else + return lj_num2int(n); +#endif +} + +LUA_API int lua_toboolean(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + return tvistruecond(o); +} + +LUA_API const char *lua_tolstring(lua_State *L, int idx, size_t *len) +{ + TValue *o = index2adr(L, idx); + GCstr *s; + if (LJ_LIKELY(tvisstr(o))) { + s = strV(o); + } else if (tvisnumber(o)) { + lj_gc_check(L); + o = index2adr(L, idx); /* GC may move the stack. */ + s = lj_str_fromnumber(L, o); + setstrV(L, o, s); + } else { + if (len != NULL) *len = 0; + return NULL; + } + if (len != NULL) *len = s->len; + return strdata(s); +} + +LUALIB_API const char *luaL_checklstring(lua_State *L, int idx, size_t *len) +{ + TValue *o = index2adr(L, idx); + GCstr *s; + if (LJ_LIKELY(tvisstr(o))) { + s = strV(o); + } else if (tvisnumber(o)) { + lj_gc_check(L); + o = index2adr(L, idx); /* GC may move the stack. */ + s = lj_str_fromnumber(L, o); + setstrV(L, o, s); + } else { + lj_err_argt(L, idx, LUA_TSTRING); + } + if (len != NULL) *len = s->len; + return strdata(s); +} + +LUALIB_API const char *luaL_optlstring(lua_State *L, int idx, + const char *def, size_t *len) +{ + TValue *o = index2adr(L, idx); + GCstr *s; + if (LJ_LIKELY(tvisstr(o))) { + s = strV(o); + } else if (tvisnil(o)) { + if (len != NULL) *len = def ? strlen(def) : 0; + return def; + } else if (tvisnumber(o)) { + lj_gc_check(L); + o = index2adr(L, idx); /* GC may move the stack. */ + s = lj_str_fromnumber(L, o); + setstrV(L, o, s); + } else { + lj_err_argt(L, idx, LUA_TSTRING); + } + if (len != NULL) *len = s->len; + return strdata(s); +} + +LUALIB_API int luaL_checkoption(lua_State *L, int idx, const char *def, + const char *const lst[]) +{ + ptrdiff_t i; + const char *s = lua_tolstring(L, idx, NULL); + if (s == NULL && (s = def) == NULL) + lj_err_argt(L, idx, LUA_TSTRING); + for (i = 0; lst[i]; i++) + if (strcmp(lst[i], s) == 0) + return (int)i; + lj_err_argv(L, idx, LJ_ERR_INVOPTM, s); +} + +LUA_API size_t lua_objlen(lua_State *L, int idx) +{ + TValue *o = index2adr(L, idx); + if (tvisstr(o)) { + return strV(o)->len; + } else if (tvistab(o)) { + return (size_t)lj_tab_len(tabV(o)); + } else if (tvisudata(o)) { + return udataV(o)->len; + } else if (tvisnumber(o)) { + GCstr *s = lj_str_fromnumber(L, o); + setstrV(L, o, s); + return s->len; + } else { + return 0; + } +} + +LUA_API lua_CFunction lua_tocfunction(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + if (tvisfunc(o)) { + BCOp op = bc_op(*mref(funcV(o)->c.pc, BCIns)); + if (op == BC_FUNCC || op == BC_FUNCCW) + return funcV(o)->c.f; + } + return NULL; +} + +LUA_API void *lua_touserdata(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + if (tvisudata(o)) + return uddata(udataV(o)); + else if (tvislightud(o)) + return lightudV(o); + else + return NULL; +} + +LUA_API lua_State *lua_tothread(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + return (!tvisthread(o)) ? NULL : threadV(o); +} + +LUA_API const void *lua_topointer(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + if (tvisudata(o)) + return uddata(udataV(o)); + else if (tvislightud(o)) + return lightudV(o); + else if (tviscdata(o)) + return cdataptr(cdataV(o)); + else if (tvisgcv(o)) + return gcV(o); + else + return NULL; +} + +/* -- Stack setters (object creation) ------------------------------------- */ + +LUA_API void lua_pushnil(lua_State *L) +{ + setnilV(L->top); + incr_top(L); +} + +LUA_API void lua_pushnumber(lua_State *L, lua_Number n) +{ + setnumV(L->top, n); + if (LJ_UNLIKELY(tvisnan(L->top))) + setnanV(L->top); /* Canonicalize injected NaNs. */ + incr_top(L); +} + +LUA_API void lua_pushinteger(lua_State *L, lua_Integer n) +{ + setintptrV(L->top, n); + incr_top(L); +} + +LUA_API void lua_pushlstring(lua_State *L, const char *str, size_t len) +{ + GCstr *s; + lj_gc_check(L); + s = lj_str_new(L, str, len); + setstrV(L, L->top, s); + incr_top(L); +} + +LUA_API void lua_pushstring(lua_State *L, const char *str) +{ + if (str == NULL) { + setnilV(L->top); + } else { + GCstr *s; + lj_gc_check(L); + s = lj_str_newz(L, str); + setstrV(L, L->top, s); + } + incr_top(L); +} + +LUA_API const char *lua_pushvfstring(lua_State *L, const char *fmt, + va_list argp) +{ + lj_gc_check(L); + return lj_str_pushvf(L, fmt, argp); +} + +LUA_API const char *lua_pushfstring(lua_State *L, const char *fmt, ...) +{ + const char *ret; + va_list argp; + lj_gc_check(L); + va_start(argp, fmt); + ret = lj_str_pushvf(L, fmt, argp); + va_end(argp); + return ret; +} + +LUA_API void lua_pushcclosure(lua_State *L, lua_CFunction f, int n) +{ + GCfunc *fn; + lj_gc_check(L); + api_checknelems(L, n); + fn = lj_func_newC(L, (MSize)n, getcurrenv(L)); + fn->c.f = f; + L->top -= n; + while (n--) + copyTV(L, &fn->c.upvalue[n], L->top+n); + setfuncV(L, L->top, fn); + lua_assert(iswhite(obj2gco(fn))); + incr_top(L); +} + +LUA_API void lua_pushboolean(lua_State *L, int b) +{ + setboolV(L->top, (b != 0)); + incr_top(L); +} + +LUA_API void lua_pushlightuserdata(lua_State *L, void *p) +{ + setlightudV(L->top, checklightudptr(L, p)); + incr_top(L); +} + +LUA_API void lua_createtable(lua_State *L, int narray, int nrec) +{ + GCtab *t; + lj_gc_check(L); + t = lj_tab_new(L, (uint32_t)(narray > 0 ? narray+1 : 0), hsize2hbits(nrec)); + settabV(L, L->top, t); + incr_top(L); +} + +LUALIB_API int luaL_newmetatable(lua_State *L, const char *tname) +{ + GCtab *regt = tabV(registry(L)); + TValue *tv = lj_tab_setstr(L, regt, lj_str_newz(L, tname)); + if (tvisnil(tv)) { + GCtab *mt = lj_tab_new(L, 0, 1); + settabV(L, tv, mt); + settabV(L, L->top++, mt); + lj_gc_anybarriert(L, regt); + return 1; + } else { + copyTV(L, L->top++, tv); + return 0; + } +} + +LUA_API int lua_pushthread(lua_State *L) +{ + setthreadV(L, L->top, L); + incr_top(L); + return (mainthread(G(L)) == L); +} + +LUA_API lua_State *lua_newthread(lua_State *L) +{ + lua_State *L1; + lj_gc_check(L); + L1 = lj_state_new(L); + setthreadV(L, L->top, L1); + incr_top(L); + return L1; +} + +LUA_API void *lua_newuserdata(lua_State *L, size_t size) +{ + GCudata *ud; + lj_gc_check(L); + if (size > LJ_MAX_UDATA) + lj_err_msg(L, LJ_ERR_UDATAOV); + ud = lj_udata_new(L, (MSize)size, getcurrenv(L)); + setudataV(L, L->top, ud); + incr_top(L); + return uddata(ud); +} + +LUA_API void lua_concat(lua_State *L, int n) +{ + api_checknelems(L, n); + if (n >= 2) { + n--; + do { + TValue *top = lj_meta_cat(L, L->top-1, -n); + if (top == NULL) { + L->top -= n; + break; + } + n -= (int)(L->top - top); + L->top = top+2; + lj_vm_call(L, top, 1+1); + L->top--; + copyTV(L, L->top-1, L->top); + } while (--n > 0); + } else if (n == 0) { /* Push empty string. */ + setstrV(L, L->top, &G(L)->strempty); + incr_top(L); + } + /* else n == 1: nothing to do. */ +} + +/* -- Object getters ------------------------------------------------------ */ + +LUA_API void lua_gettable(lua_State *L, int idx) +{ + cTValue *v, *t = index2adr(L, idx); + api_checkvalidindex(L, t); + v = lj_meta_tget(L, t, L->top-1); + if (v == NULL) { + L->top += 2; + lj_vm_call(L, L->top-2, 1+1); + L->top -= 2; + v = L->top+1; + } + copyTV(L, L->top-1, v); +} + +LUA_API void lua_getfield(lua_State *L, int idx, const char *k) +{ + cTValue *v, *t = index2adr(L, idx); + TValue key; + api_checkvalidindex(L, t); + setstrV(L, &key, lj_str_newz(L, k)); + v = lj_meta_tget(L, t, &key); + if (v == NULL) { + L->top += 2; + lj_vm_call(L, L->top-2, 1+1); + L->top -= 2; + v = L->top+1; + } + copyTV(L, L->top, v); + incr_top(L); +} + +LUA_API void lua_rawget(lua_State *L, int idx) +{ + cTValue *t = index2adr(L, idx); + api_check(L, tvistab(t)); + copyTV(L, L->top-1, lj_tab_get(L, tabV(t), L->top-1)); +} + +LUA_API void lua_rawgeti(lua_State *L, int idx, int n) +{ + cTValue *v, *t = index2adr(L, idx); + api_check(L, tvistab(t)); + v = lj_tab_getint(tabV(t), n); + if (v) { + copyTV(L, L->top, v); + } else { + setnilV(L->top); + } + incr_top(L); +} + +LUA_API int lua_getmetatable(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + GCtab *mt = NULL; + if (tvistab(o)) + mt = tabref(tabV(o)->metatable); + else if (tvisudata(o)) + mt = tabref(udataV(o)->metatable); + else + mt = tabref(basemt_obj(G(L), o)); + if (mt == NULL) + return 0; + settabV(L, L->top, mt); + incr_top(L); + return 1; +} + +LUALIB_API int luaL_getmetafield(lua_State *L, int idx, const char *field) +{ + if (lua_getmetatable(L, idx)) { + cTValue *tv = lj_tab_getstr(tabV(L->top-1), lj_str_newz(L, field)); + if (tv && !tvisnil(tv)) { + copyTV(L, L->top-1, tv); + return 1; + } + L->top--; + } + return 0; +} + +LUA_API void lua_getfenv(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + api_checkvalidindex(L, o); + if (tvisfunc(o)) { + settabV(L, L->top, tabref(funcV(o)->c.env)); + } else if (tvisudata(o)) { + settabV(L, L->top, tabref(udataV(o)->env)); + } else if (tvisthread(o)) { + settabV(L, L->top, tabref(threadV(o)->env)); + } else { + setnilV(L->top); + } + incr_top(L); +} + +LUA_API int lua_next(lua_State *L, int idx) +{ + cTValue *t = index2adr(L, idx); + int more; + api_check(L, tvistab(t)); + more = lj_tab_next(L, tabV(t), L->top-1); + if (more) { + incr_top(L); /* Return new key and value slot. */ + } else { /* End of traversal. */ + L->top--; /* Remove key slot. */ + } + return more; +} + +LUA_API const char *lua_getupvalue(lua_State *L, int idx, int n) +{ + TValue *val; + const char *name = lj_debug_uvnamev(index2adr(L, idx), (uint32_t)(n-1), &val); + if (name) { + copyTV(L, L->top, val); + incr_top(L); + } + return name; +} + +LUA_API void *lua_upvalueid(lua_State *L, int idx, int n) +{ + GCfunc *fn = funcV(index2adr(L, idx)); + n--; + api_check(L, (uint32_t)n < fn->l.nupvalues); + return isluafunc(fn) ? (void *)gcref(fn->l.uvptr[n]) : + (void *)&fn->c.upvalue[n]; +} + +LUA_API void lua_upvaluejoin(lua_State *L, int idx1, int n1, int idx2, int n2) +{ + GCfunc *fn1 = funcV(index2adr(L, idx1)); + GCfunc *fn2 = funcV(index2adr(L, idx2)); + n1--; n2--; + api_check(L, isluafunc(fn1) && (uint32_t)n1 < fn1->l.nupvalues); + api_check(L, isluafunc(fn2) && (uint32_t)n2 < fn2->l.nupvalues); + setgcrefr(fn1->l.uvptr[n1], fn2->l.uvptr[n2]); + lj_gc_objbarrier(L, fn1, gcref(fn1->l.uvptr[n1])); +} + +LUALIB_API void *luaL_checkudata(lua_State *L, int idx, const char *tname) +{ + cTValue *o = index2adr(L, idx); + if (tvisudata(o)) { + GCudata *ud = udataV(o); + cTValue *tv = lj_tab_getstr(tabV(registry(L)), lj_str_newz(L, tname)); + if (tv && tvistab(tv) && tabV(tv) == tabref(ud->metatable)) + return uddata(ud); + } + lj_err_argtype(L, idx, tname); + return NULL; /* unreachable */ +} + +/* -- Object setters ------------------------------------------------------ */ + +LUA_API void lua_settable(lua_State *L, int idx) +{ + TValue *o; + cTValue *t = index2adr(L, idx); + api_checknelems(L, 2); + api_checkvalidindex(L, t); + o = lj_meta_tset(L, t, L->top-2); + if (o) { + /* NOBARRIER: lj_meta_tset ensures the table is not black. */ + copyTV(L, o, L->top-1); + L->top -= 2; + } else { + L->top += 3; + copyTV(L, L->top-1, L->top-6); + lj_vm_call(L, L->top-3, 0+1); + L->top -= 3; + } +} + +LUA_API void lua_setfield(lua_State *L, int idx, const char *k) +{ + TValue *o; + TValue key; + cTValue *t = index2adr(L, idx); + api_checknelems(L, 1); + api_checkvalidindex(L, t); + setstrV(L, &key, lj_str_newz(L, k)); + o = lj_meta_tset(L, t, &key); + if (o) { + L->top--; + /* NOBARRIER: lj_meta_tset ensures the table is not black. */ + copyTV(L, o, L->top); + } else { + L->top += 3; + copyTV(L, L->top-1, L->top-6); + lj_vm_call(L, L->top-3, 0+1); + L->top -= 2; + } +} + +LUA_API void lua_rawset(lua_State *L, int idx) +{ + GCtab *t = tabV(index2adr(L, idx)); + TValue *dst, *key; + api_checknelems(L, 2); + key = L->top-2; + dst = lj_tab_set(L, t, key); + copyTV(L, dst, key+1); + lj_gc_anybarriert(L, t); + L->top = key; +} + +LUA_API void lua_rawseti(lua_State *L, int idx, int n) +{ + GCtab *t = tabV(index2adr(L, idx)); + TValue *dst, *src; + api_checknelems(L, 1); + dst = lj_tab_setint(L, t, n); + src = L->top-1; + copyTV(L, dst, src); + lj_gc_barriert(L, t, dst); + L->top = src; +} + +LUA_API int lua_setmetatable(lua_State *L, int idx) +{ + global_State *g; + GCtab *mt; + cTValue *o = index2adr(L, idx); + api_checknelems(L, 1); + api_checkvalidindex(L, o); + if (tvisnil(L->top-1)) { + mt = NULL; + } else { + api_check(L, tvistab(L->top-1)); + mt = tabV(L->top-1); + } + g = G(L); + if (tvistab(o)) { + setgcref(tabV(o)->metatable, obj2gco(mt)); + if (mt) + lj_gc_objbarriert(L, tabV(o), mt); + } else if (tvisudata(o)) { + setgcref(udataV(o)->metatable, obj2gco(mt)); + if (mt) + lj_gc_objbarrier(L, udataV(o), mt); + } else { + /* Flush cache, since traces specialize to basemt. But not during __gc. */ + if (lj_trace_flushall(L)) + lj_err_caller(L, LJ_ERR_NOGCMM); + if (tvisbool(o)) { + /* NOBARRIER: basemt is a GC root. */ + setgcref(basemt_it(g, LJ_TTRUE), obj2gco(mt)); + setgcref(basemt_it(g, LJ_TFALSE), obj2gco(mt)); + } else { + /* NOBARRIER: basemt is a GC root. */ + setgcref(basemt_obj(g, o), obj2gco(mt)); + } + } + L->top--; + return 1; +} + +LUA_API int lua_setfenv(lua_State *L, int idx) +{ + cTValue *o = index2adr(L, idx); + GCtab *t; + api_checknelems(L, 1); + api_checkvalidindex(L, o); + api_check(L, tvistab(L->top-1)); + t = tabV(L->top-1); + if (tvisfunc(o)) { + setgcref(funcV(o)->c.env, obj2gco(t)); + } else if (tvisudata(o)) { + setgcref(udataV(o)->env, obj2gco(t)); + } else if (tvisthread(o)) { + setgcref(threadV(o)->env, obj2gco(t)); + } else { + L->top--; + return 0; + } + lj_gc_objbarrier(L, gcV(o), t); + L->top--; + return 1; +} + +LUA_API const char *lua_setupvalue(lua_State *L, int idx, int n) +{ + cTValue *f = index2adr(L, idx); + TValue *val; + const char *name; + api_checknelems(L, 1); + name = lj_debug_uvnamev(f, (uint32_t)(n-1), &val); + if (name) { + L->top--; + copyTV(L, val, L->top); + lj_gc_barrier(L, funcV(f), L->top); + } + return name; +} + +/* -- Calls --------------------------------------------------------------- */ + +LUA_API void lua_call(lua_State *L, int nargs, int nresults) +{ + api_check(L, L->status == 0 || L->status == LUA_ERRERR); + api_checknelems(L, nargs+1); + lj_vm_call(L, L->top - nargs, nresults+1); +} + +LUA_API int lua_pcall(lua_State *L, int nargs, int nresults, int errfunc) +{ + global_State *g = G(L); + uint8_t oldh = hook_save(g); + ptrdiff_t ef; + int status; + api_check(L, L->status == 0 || L->status == LUA_ERRERR); + api_checknelems(L, nargs+1); + if (errfunc == 0) { + ef = 0; + } else { + cTValue *o = stkindex2adr(L, errfunc); + api_checkvalidindex(L, o); + ef = savestack(L, o); + } + status = lj_vm_pcall(L, L->top - nargs, nresults+1, ef); + if (status) hook_restore(g, oldh); + return status; +} + +static TValue *cpcall(lua_State *L, lua_CFunction func, void *ud) +{ + GCfunc *fn = lj_func_newC(L, 0, getcurrenv(L)); + fn->c.f = func; + setfuncV(L, L->top, fn); + setlightudV(L->top+1, checklightudptr(L, ud)); + cframe_nres(L->cframe) = 1+0; /* Zero results. */ + L->top += 2; + return L->top-1; /* Now call the newly allocated C function. */ +} + +LUA_API int lua_cpcall(lua_State *L, lua_CFunction func, void *ud) +{ + global_State *g = G(L); + uint8_t oldh = hook_save(g); + int status; + api_check(L, L->status == 0 || L->status == LUA_ERRERR); + status = lj_vm_cpcall(L, func, ud, cpcall); + if (status) hook_restore(g, oldh); + return status; +} + +LUALIB_API int luaL_callmeta(lua_State *L, int idx, const char *field) +{ + if (luaL_getmetafield(L, idx, field)) { + TValue *base = L->top--; + copyTV(L, base, index2adr(L, idx)); + L->top = base+1; + lj_vm_call(L, base, 1+1); + return 1; + } + return 0; +} + +/* -- Coroutine yield and resume ------------------------------------------ */ + +LUA_API int lua_yield(lua_State *L, int nresults) +{ + void *cf = L->cframe; + global_State *g = G(L); + if (cframe_canyield(cf)) { + cf = cframe_raw(cf); + if (!hook_active(g)) { /* Regular yield: move results down if needed. */ + cTValue *f = L->top - nresults; + if (f > L->base) { + TValue *t = L->base; + while (--nresults >= 0) copyTV(L, t++, f++); + L->top = t; + } + L->cframe = NULL; + L->status = LUA_YIELD; + return -1; + } else { /* Yield from hook: add a pseudo-frame. */ + TValue *top = L->top; + hook_leave(g); + top->u64 = cframe_multres(cf); + setcont(top+1, lj_cont_hook); + setframe_pc(top+1, cframe_pc(cf)-1); + setframe_gc(top+2, obj2gco(L)); + setframe_ftsz(top+2, (int)((char *)(top+3)-(char *)L->base)+FRAME_CONT); + L->top = L->base = top+3; +#if LJ_TARGET_X64 + lj_err_throw(L, LUA_YIELD); +#else + L->cframe = NULL; + L->status = LUA_YIELD; + lj_vm_unwind_c(cf, LUA_YIELD); +#endif + } + } + lj_err_msg(L, LJ_ERR_CYIELD); + return 0; /* unreachable */ +} + +LUA_API int lua_resume(lua_State *L, int nargs) +{ + if (L->cframe == NULL && L->status <= LUA_YIELD) + return lj_vm_resume(L, L->top - nargs, 0, 0); + L->top = L->base; + setstrV(L, L->top, lj_err_str(L, LJ_ERR_COSUSP)); + incr_top(L); + return LUA_ERRRUN; +} + +/* -- GC and memory management -------------------------------------------- */ + +LUA_API int lua_gc(lua_State *L, int what, int data) +{ + global_State *g = G(L); + int res = 0; + switch (what) { + case LUA_GCSTOP: + g->gc.threshold = LJ_MAX_MEM; + break; + case LUA_GCRESTART: + g->gc.threshold = data == -1 ? (g->gc.total/100)*g->gc.pause : g->gc.total; + break; + case LUA_GCCOLLECT: + lj_gc_fullgc(L); + break; + case LUA_GCCOUNT: + res = (int)(g->gc.total >> 10); + break; + case LUA_GCCOUNTB: + res = (int)(g->gc.total & 0x3ff); + break; + case LUA_GCSTEP: { + MSize a = (MSize)data << 10; + g->gc.threshold = (a <= g->gc.total) ? (g->gc.total - a) : 0; + while (g->gc.total >= g->gc.threshold) + if (lj_gc_step(L) > 0) { + res = 1; + break; + } + break; + } + case LUA_GCSETPAUSE: + res = (int)(g->gc.pause); + g->gc.pause = (MSize)data; + break; + case LUA_GCSETSTEPMUL: + res = (int)(g->gc.stepmul); + g->gc.stepmul = (MSize)data; + break; + default: + res = -1; /* Invalid option. */ + } + return res; +} + +LUA_API lua_Alloc lua_getallocf(lua_State *L, void **ud) +{ + global_State *g = G(L); + if (ud) *ud = g->allocd; + return g->allocf; +} + +LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud) +{ + global_State *g = G(L); + g->allocd = ud; + g->allocf = f; +} + http://git-wip-us.apache.org/repos/asf/trafficserver/blob/1f27b840/lib/luajit/src/lj_arch.h ---------------------------------------------------------------------- diff --git a/lib/luajit/src/lj_arch.h b/lib/luajit/src/lj_arch.h new file mode 100644 index 0000000..d3a9d57 --- /dev/null +++ b/lib/luajit/src/lj_arch.h @@ -0,0 +1,437 @@ +/* +** Target architecture selection. +** Copyright (C) 2005-2015 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_ARCH_H +#define _LJ_ARCH_H + +#include "lua.h" + +/* Target endianess. */ +#define LUAJIT_LE 0 +#define LUAJIT_BE 1 + +/* Target architectures. */ +#define LUAJIT_ARCH_X86 1 +#define LUAJIT_ARCH_x86 1 +#define LUAJIT_ARCH_X64 2 +#define LUAJIT_ARCH_x64 2 +#define LUAJIT_ARCH_ARM 3 +#define LUAJIT_ARCH_arm 3 +#define LUAJIT_ARCH_PPC 4 +#define LUAJIT_ARCH_ppc 4 +#define LUAJIT_ARCH_PPCSPE 5 +#define LUAJIT_ARCH_ppcspe 5 +#define LUAJIT_ARCH_MIPS 6 +#define LUAJIT_ARCH_mips 6 + +/* Target OS. */ +#define LUAJIT_OS_OTHER 0 +#define LUAJIT_OS_WINDOWS 1 +#define LUAJIT_OS_LINUX 2 +#define LUAJIT_OS_OSX 3 +#define LUAJIT_OS_BSD 4 +#define LUAJIT_OS_POSIX 5 + +/* Select native target if no target defined. */ +#ifndef LUAJIT_TARGET + +#if defined(__i386) || defined(__i386__) || defined(_M_IX86) +#define LUAJIT_TARGET LUAJIT_ARCH_X86 +#elif defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define LUAJIT_TARGET LUAJIT_ARCH_X64 +#elif defined(__arm__) || defined(__arm) || defined(__ARM__) || defined(__ARM) +#define LUAJIT_TARGET LUAJIT_ARCH_ARM +#elif defined(__ppc__) || defined(__ppc) || defined(__PPC__) || defined(__PPC) || defined(__powerpc__) || defined(__powerpc) || defined(__POWERPC__) || defined(__POWERPC) || defined(_M_PPC) +#ifdef __NO_FPRS__ +#define LUAJIT_TARGET LUAJIT_ARCH_PPCSPE +#else +#define LUAJIT_TARGET LUAJIT_ARCH_PPC +#endif +#elif defined(__mips__) || defined(__mips) || defined(__MIPS__) || defined(__MIPS) +#define LUAJIT_TARGET LUAJIT_ARCH_MIPS +#else +#error "No support for this architecture (yet)" +#endif + +#endif + +/* Select native OS if no target OS defined. */ +#ifndef LUAJIT_OS + +#if defined(_WIN32) && !defined(_XBOX_VER) +#define LUAJIT_OS LUAJIT_OS_WINDOWS +#elif defined(__linux__) +#define LUAJIT_OS LUAJIT_OS_LINUX +#elif defined(__MACH__) && defined(__APPLE__) +#define LUAJIT_OS LUAJIT_OS_OSX +#elif (defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) || defined(__OpenBSD__) || \ + defined(__DragonFly__)) && !defined(__ORBIS__) +#define LUAJIT_OS LUAJIT_OS_BSD +#elif (defined(__sun__) && defined(__svr4__)) || defined(__CYGWIN__) +#define LUAJIT_OS LUAJIT_OS_POSIX +#else +#define LUAJIT_OS LUAJIT_OS_OTHER +#endif + +#endif + +/* Set target OS properties. */ +#if LUAJIT_OS == LUAJIT_OS_WINDOWS +#define LJ_OS_NAME "Windows" +#elif LUAJIT_OS == LUAJIT_OS_LINUX +#define LJ_OS_NAME "Linux" +#elif LUAJIT_OS == LUAJIT_OS_OSX +#define LJ_OS_NAME "OSX" +#elif LUAJIT_OS == LUAJIT_OS_BSD +#define LJ_OS_NAME "BSD" +#elif LUAJIT_OS == LUAJIT_OS_POSIX +#define LJ_OS_NAME "POSIX" +#else +#define LJ_OS_NAME "Other" +#endif + +#define LJ_TARGET_WINDOWS (LUAJIT_OS == LUAJIT_OS_WINDOWS) +#define LJ_TARGET_LINUX (LUAJIT_OS == LUAJIT_OS_LINUX) +#define LJ_TARGET_OSX (LUAJIT_OS == LUAJIT_OS_OSX) +#define LJ_TARGET_IOS (LJ_TARGET_OSX && LUAJIT_TARGET == LUAJIT_ARCH_ARM) +#define LJ_TARGET_POSIX (LUAJIT_OS > LUAJIT_OS_WINDOWS) +#define LJ_TARGET_DLOPEN LJ_TARGET_POSIX + +#ifdef __CELLOS_LV2__ +#define LJ_TARGET_PS3 1 +#define LJ_TARGET_CONSOLE 1 +#endif + +#ifdef __ORBIS__ +#define LJ_TARGET_PS4 1 +#define LJ_TARGET_CONSOLE 1 +#undef NULL +#define NULL ((void*)0) +#endif + +#ifdef __psp2__ +#define LJ_TARGET_PSVITA 1 +#define LJ_TARGET_CONSOLE 1 +#endif + +#if _XBOX_VER >= 200 +#define LJ_TARGET_XBOX360 1 +#define LJ_TARGET_CONSOLE 1 +#endif + +#define LJ_NUMMODE_SINGLE 0 /* Single-number mode only. */ +#define LJ_NUMMODE_SINGLE_DUAL 1 /* Default to single-number mode. */ +#define LJ_NUMMODE_DUAL 2 /* Dual-number mode only. */ +#define LJ_NUMMODE_DUAL_SINGLE 3 /* Default to dual-number mode. */ + +/* Set target architecture properties. */ +#if LUAJIT_TARGET == LUAJIT_ARCH_X86 + +#define LJ_ARCH_NAME "x86" +#define LJ_ARCH_BITS 32 +#define LJ_ARCH_ENDIAN LUAJIT_LE +#if LJ_TARGET_WINDOWS || __CYGWIN__ +#define LJ_ABI_WIN 1 +#else +#define LJ_ABI_WIN 0 +#endif +#define LJ_TARGET_X86 1 +#define LJ_TARGET_X86ORX64 1 +#define LJ_TARGET_EHRETREG 0 +#define LJ_TARGET_MASKSHIFT 1 +#define LJ_TARGET_MASKROT 1 +#define LJ_TARGET_UNALIGNED 1 +#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE_DUAL + +#elif LUAJIT_TARGET == LUAJIT_ARCH_X64 + +#define LJ_ARCH_NAME "x64" +#define LJ_ARCH_BITS 64 +#define LJ_ARCH_ENDIAN LUAJIT_LE +#define LJ_ABI_WIN LJ_TARGET_WINDOWS +#define LJ_TARGET_X64 1 +#define LJ_TARGET_X86ORX64 1 +#define LJ_TARGET_EHRETREG 0 +#define LJ_TARGET_JUMPRANGE 31 /* +-2^31 = +-2GB */ +#define LJ_TARGET_MASKSHIFT 1 +#define LJ_TARGET_MASKROT 1 +#define LJ_TARGET_UNALIGNED 1 +#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE_DUAL + +#elif LUAJIT_TARGET == LUAJIT_ARCH_ARM + +#define LJ_ARCH_NAME "arm" +#define LJ_ARCH_BITS 32 +#define LJ_ARCH_ENDIAN LUAJIT_LE +#if !defined(LJ_ARCH_HASFPU) && __SOFTFP__ +#define LJ_ARCH_HASFPU 0 +#endif +#if !defined(LJ_ABI_SOFTFP) && !__ARM_PCS_VFP +#define LJ_ABI_SOFTFP 1 +#endif +#define LJ_ABI_EABI 1 +#define LJ_TARGET_ARM 1 +#define LJ_TARGET_EHRETREG 0 +#define LJ_TARGET_JUMPRANGE 25 /* +-2^25 = +-32MB */ +#define LJ_TARGET_MASKSHIFT 0 +#define LJ_TARGET_MASKROT 1 +#define LJ_TARGET_UNIFYROT 2 /* Want only IR_BROR. */ +#define LJ_ARCH_NUMMODE LJ_NUMMODE_DUAL + +#if __ARM_ARCH____ARM_ARCH_8__ || __ARM_ARCH_8A__ +#define LJ_ARCH_VERSION 80 +#elif __ARM_ARCH_7__ || __ARM_ARCH_7A__ || __ARM_ARCH_7R__ || __ARM_ARCH_7S__ || __ARM_ARCH_7VE__ +#define LJ_ARCH_VERSION 70 +#elif __ARM_ARCH_6T2__ +#define LJ_ARCH_VERSION 61 +#elif __ARM_ARCH_6__ || __ARM_ARCH_6J__ || __ARM_ARCH_6K__ || __ARM_ARCH_6Z__ || __ARM_ARCH_6ZK__ +#define LJ_ARCH_VERSION 60 +#else +#define LJ_ARCH_VERSION 50 +#endif + +#elif LUAJIT_TARGET == LUAJIT_ARCH_PPC + +#define LJ_ARCH_NAME "ppc" +#if _LP64 +#define LJ_ARCH_BITS 64 +#else +#define LJ_ARCH_BITS 32 +#endif +#define LJ_ARCH_ENDIAN LUAJIT_BE +#define LJ_TARGET_PPC 1 +#define LJ_TARGET_EHRETREG 3 +#define LJ_TARGET_JUMPRANGE 25 /* +-2^25 = +-32MB */ +#define LJ_TARGET_MASKSHIFT 0 +#define LJ_TARGET_MASKROT 1 +#define LJ_TARGET_UNIFYROT 1 /* Want only IR_BROL. */ +#define LJ_ARCH_NUMMODE LJ_NUMMODE_DUAL_SINGLE + +#if _ARCH_PWR7 +#define LJ_ARCH_VERSION 70 +#elif _ARCH_PWR6 +#define LJ_ARCH_VERSION 60 +#elif _ARCH_PWR5X +#define LJ_ARCH_VERSION 51 +#elif _ARCH_PWR5 +#define LJ_ARCH_VERSION 50 +#elif _ARCH_PWR4 +#define LJ_ARCH_VERSION 40 +#else +#define LJ_ARCH_VERSION 0 +#endif +#if __PPC64__ || __powerpc64__ || LJ_TARGET_CONSOLE +#define LJ_ARCH_PPC64 1 +#define LJ_ARCH_NOFFI 1 +#endif +#if _ARCH_PPCSQ +#define LJ_ARCH_SQRT 1 +#endif +#if _ARCH_PWR5X +#define LJ_ARCH_ROUND 1 +#endif +#if __PPU__ +#define LJ_ARCH_CELL 1 +#endif +#if LJ_TARGET_XBOX360 +#define LJ_ARCH_XENON 1 +#endif + +#elif LUAJIT_TARGET == LUAJIT_ARCH_PPCSPE + +#define LJ_ARCH_NAME "ppcspe" +#define LJ_ARCH_BITS 32 +#define LJ_ARCH_ENDIAN LUAJIT_BE +#ifndef LJ_ABI_SOFTFP +#define LJ_ABI_SOFTFP 1 +#endif +#define LJ_ABI_EABI 1 +#define LJ_TARGET_PPCSPE 1 +#define LJ_TARGET_EHRETREG 3 +#define LJ_TARGET_JUMPRANGE 25 /* +-2^25 = +-32MB */ +#define LJ_TARGET_MASKSHIFT 0 +#define LJ_TARGET_MASKROT 1 +#define LJ_TARGET_UNIFYROT 1 /* Want only IR_BROL. */ +#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE +#define LJ_ARCH_NOFFI 1 /* NYI: comparisons, calls. */ +#define LJ_ARCH_NOJIT 1 + +#elif LUAJIT_TARGET == LUAJIT_ARCH_MIPS + +#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) +#define LJ_ARCH_NAME "mipsel" +#define LJ_ARCH_ENDIAN LUAJIT_LE +#else +#define LJ_ARCH_NAME "mips" +#define LJ_ARCH_ENDIAN LUAJIT_BE +#endif +#define LJ_ARCH_BITS 32 +#define LJ_TARGET_MIPS 1 +#define LJ_TARGET_EHRETREG 4 +#define LJ_TARGET_JUMPRANGE 27 /* 2*2^27 = 256MB-aligned region */ +#define LJ_TARGET_MASKSHIFT 1 +#define LJ_TARGET_MASKROT 1 +#define LJ_TARGET_UNIFYROT 2 /* Want only IR_BROR. */ +#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE + +#if _MIPS_ARCH_MIPS32R2 +#define LJ_ARCH_VERSION 20 +#else +#define LJ_ARCH_VERSION 10 +#endif + +#else +#error "No target architecture defined" +#endif + +#ifndef LJ_PAGESIZE +#define LJ_PAGESIZE 4096 +#endif + +/* Check for minimum required compiler versions. */ +#if defined(__GNUC__) +#if LJ_TARGET_X86 +#if (__GNUC__ < 3) || ((__GNUC__ == 3) && __GNUC_MINOR__ < 4) +#error "Need at least GCC 3.4 or newer" +#endif +#elif LJ_TARGET_X64 +#if __GNUC__ < 4 +#error "Need at least GCC 4.0 or newer" +#endif +#elif LJ_TARGET_ARM +#if (__GNUC__ < 4) || ((__GNUC__ == 4) && __GNUC_MINOR__ < 2) +#error "Need at least GCC 4.2 or newer" +#endif +#elif !LJ_TARGET_PS3 +#if (__GNUC__ < 4) || ((__GNUC__ == 4) && __GNUC_MINOR__ < 3) +#error "Need at least GCC 4.3 or newer" +#endif +#endif +#endif + +/* Check target-specific constraints. */ +#ifndef _BUILDVM_H +#if LJ_TARGET_X64 +#if __USING_SJLJ_EXCEPTIONS__ +#error "Need a C compiler with native exception handling on x64" +#endif +#elif LJ_TARGET_ARM +#if defined(__ARMEB__) +#error "No support for big-endian ARM" +#endif +#if __ARM_ARCH_6M__ || __ARM_ARCH_7M__ || __ARM_ARCH_7EM__ +#error "No support for Cortex-M CPUs" +#endif +#if !(__ARM_EABI__ || LJ_TARGET_IOS) +#error "Only ARM EABI or iOS 3.0+ ABI is supported" +#endif +#elif LJ_TARGET_PPC || LJ_TARGET_PPCSPE +#if defined(_SOFT_FLOAT) || defined(_SOFT_DOUBLE) +#error "No support for PowerPC CPUs without double-precision FPU" +#endif +#if defined(_LITTLE_ENDIAN) +#error "No support for little-endian PowerPC" +#endif +#if defined(_LP64) +#error "No support for PowerPC 64 bit mode" +#endif +#elif LJ_TARGET_MIPS +#if defined(__mips_soft_float) +#error "No support for MIPS CPUs without FPU" +#endif +#if defined(_LP64) +#error "No support for MIPS64" +#endif +#endif +#endif + +/* Enable or disable the dual-number mode for the VM. */ +#if (LJ_ARCH_NUMMODE == LJ_NUMMODE_SINGLE && LUAJIT_NUMMODE == 2) || \ + (LJ_ARCH_NUMMODE == LJ_NUMMODE_DUAL && LUAJIT_NUMMODE == 1) +#error "No support for this number mode on this architecture" +#endif +#if LJ_ARCH_NUMMODE == LJ_NUMMODE_DUAL || \ + (LJ_ARCH_NUMMODE == LJ_NUMMODE_DUAL_SINGLE && LUAJIT_NUMMODE != 1) || \ + (LJ_ARCH_NUMMODE == LJ_NUMMODE_SINGLE_DUAL && LUAJIT_NUMMODE == 2) +#define LJ_DUALNUM 1 +#else +#define LJ_DUALNUM 0 +#endif + +#if LJ_TARGET_IOS || LJ_TARGET_CONSOLE +/* Runtime code generation is restricted on iOS. Complain to Apple, not me. */ +/* Ditto for the consoles. Complain to Sony or MS, not me. */ +#ifndef LUAJIT_ENABLE_JIT +#define LJ_OS_NOJIT 1 +#endif +#endif + +/* Disable or enable the JIT compiler. */ +#if defined(LUAJIT_DISABLE_JIT) || defined(LJ_ARCH_NOJIT) || defined(LJ_OS_NOJIT) +#define LJ_HASJIT 0 +#else +#define LJ_HASJIT 1 +#endif + +/* Disable or enable the FFI extension. */ +#if defined(LUAJIT_DISABLE_FFI) || defined(LJ_ARCH_NOFFI) +#define LJ_HASFFI 0 +#else +#define LJ_HASFFI 1 +#endif + +#ifndef LJ_ARCH_HASFPU +#define LJ_ARCH_HASFPU 1 +#endif +#ifndef LJ_ABI_SOFTFP +#define LJ_ABI_SOFTFP 0 +#endif +#define LJ_SOFTFP (!LJ_ARCH_HASFPU) + +#if LJ_ARCH_ENDIAN == LUAJIT_BE +#define LJ_LE 0 +#define LJ_BE 1 +#define LJ_ENDIAN_SELECT(le, be) be +#define LJ_ENDIAN_LOHI(lo, hi) hi lo +#else +#define LJ_LE 1 +#define LJ_BE 0 +#define LJ_ENDIAN_SELECT(le, be) le +#define LJ_ENDIAN_LOHI(lo, hi) lo hi +#endif + +#if LJ_ARCH_BITS == 32 +#define LJ_32 1 +#define LJ_64 0 +#else +#define LJ_32 0 +#define LJ_64 1 +#endif + +#ifndef LJ_TARGET_UNALIGNED +#define LJ_TARGET_UNALIGNED 0 +#endif + +/* Various workarounds for embedded operating systems. */ +#if (defined(__ANDROID__) && !defined(LJ_TARGET_X86ORX64)) || defined(__symbian__) || LJ_TARGET_XBOX360 +#define LUAJIT_NO_LOG2 +#endif +#if defined(__symbian__) +#define LUAJIT_NO_EXP2 +#endif + +#if defined(LUAJIT_NO_UNWIND) || defined(__symbian__) || LJ_TARGET_IOS || LJ_TARGET_PS3 +#define LJ_NO_UNWIND 1 +#endif + +/* Compatibility with Lua 5.1 vs. 5.2. */ +#ifdef LUAJIT_ENABLE_LUA52COMPAT +#define LJ_52 1 +#else +#define LJ_52 0 +#endif + +#endif