view lphobos/gc/gc.d @ 650:aa6a0b7968f7

Added test case for bug #100 Removed dubious check for not emitting static private global in other modules without access. This should be handled properly somewhere else, it's causing unresolved global errors for stuff that should work (in MiniD)
author Tomas Lindquist Olsen <tomas.l.olsen@gmail.com>
date Sun, 05 Oct 2008 17:28:15 +0200
parents 373489eeaf90
children 88e23f8c2354
line wrap: on
line source

/**
 * Part of the D programming language runtime library.
 */

/*
 *  Copyright (C) 2004-2008 by Digital Mars, www.digitalmars.com
 *  Written by Walter Bright
 *
 *  This software is provided 'as-is', without any express or implied
 *  warranty. In no event will the authors be held liable for any damages
 *  arising from the use of this software.
 *
 *  Permission is granted to anyone to use this software for any purpose,
 *  including commercial applications, and to alter it and redistribute it
 *  freely, subject to the following restrictions:
 *
 *  o  The origin of this software must not be misrepresented; you must not
 *     claim that you wrote the original software. If you use this software
 *     in a product, an acknowledgment in the product documentation would be
 *     appreciated but is not required.
 *  o  Altered source versions must be plainly marked as such, and must not
 *     be misrepresented as being the original software.
 *  o  This notice may not be removed or altered from any source
 *     distribution.
 */

/* NOTE: This file has been patched from the original DMD distribution to
   work with the GDC compiler.

   Modified by David Friedman, February 2007
*/


// Storage allocation

module std.gc;

//debug = PRINTF;

public import std.c.stdarg;
public import std.c.stdlib;
public import std.c.string;
public import gcx;
public import std.outofmemory;
public import gcstats;
public import std.thread;

version=GCCLASS;

version (GCCLASS)
    alias GC gc_t;
else
    alias GC* gc_t;

gc_t _gc;

void addRoot(void *p)		      { _gc.addRoot(p); }
void removeRoot(void *p)	      { _gc.removeRoot(p); }
void addRange(void *pbot, void *ptop) { _gc.addRange(pbot, ptop); }
void removeRange(void *pbot)	      { _gc.removeRange(pbot); }
void fullCollect()		      { _gc.fullCollect(); }
void fullCollectNoStack()	      { _gc.fullCollectNoStack(); }
void genCollect()		      { _gc.genCollect(); }
void minimize()			      { _gc.minimize(); }
void disable()			      { _gc.disable(); }
void enable()			      { _gc.enable(); }
void getStats(out GCStats stats)      { _gc.getStats(stats); }
void hasPointers(void* p)	      { _gc.hasPointers(p); }
void hasNoPointers(void* p)	      { _gc.hasNoPointers(p); }
void setV1_0()			      { _gc.setV1_0(); }

void[] malloc(size_t nbytes)
{
    void* p = _gc.malloc(nbytes);
    return p[0 .. nbytes];
}

void[] realloc(void* p, size_t nbytes)
{
    void* q = _gc.realloc(p, nbytes);
    return q[0 .. nbytes];
}

size_t extend(void* p, size_t minbytes, size_t maxbytes)
{
    return _gc.extend(p, minbytes, maxbytes);
}

size_t capacity(void* p)
{
    return _gc.capacity(p);
}

void setTypeInfo(TypeInfo ti, void* p)
{
    if (ti.flags() & 1)
	hasNoPointers(p);
    else
	hasPointers(p);
}

void* getGCHandle()
{
    return cast(void*)_gc;
}

void setGCHandle(void* p)
{
    void* oldp = getGCHandle();
    gc_t g = cast(gc_t)p;
    if (g.gcversion != gcx.GCVERSION)
	throw new Error("incompatible gc versions");

    // Add our static data to the new gc
    GC.scanStaticData(g);

    _gc = g;
//    return oldp;
}

void endGCHandle()
{
    GC.unscanStaticData(_gc);
}

extern (C)
{

void _d_monitorexit(Object h);


void gc_init()
{
    version (GCCLASS)
    {	void* p;
	ClassInfo ci = GC.classinfo;

	p = std.c.stdlib.malloc(ci.init.length);
	(cast(byte*)p)[0 .. ci.init.length] = ci.init[];
	_gc = cast(GC)p;
    }
    else
    {
	_gc = cast(GC *) std.c.stdlib.calloc(1, GC.sizeof);
    }
    _gc.initialize();
    GC.scanStaticData(_gc);
    std.thread.Thread.thread_init();
}

void gc_term()
{
    _gc.fullCollectNoStack();
    _gc.Dtor();
}

Object _d_newclass(ClassInfo ci)
{
    void *p;

    debug(PRINTF) printf("_d_newclass(ci = %p, %s)\n", ci, cast(char *)ci.name);
    if (ci.flags & 1)			// if COM object
    {
	p = std.c.stdlib.malloc(ci.init.length);
	if (!p)
	    _d_OutOfMemory();
	debug(PRINTF) printf(" COM object p = %p\n", p);
    }
    else
    {
	p = _gc.malloc(ci.init.length);
	debug(PRINTF) printf(" p = %p\n", p);
	_gc.setFinalizer(p, &new_finalizer);
	if (ci.flags & 2)
	    _gc.hasNoPointers(p);
    }

    debug (PRINTF)
    {
	printf("p = %p\n", p);
	printf("ci = %p, ci.init = %p, len = %d\n", ci, ci.init, ci.init.length);
	printf("vptr = %p\n", *cast(void **)ci.init);
	printf("vtbl[0] = %p\n", (*cast(void ***)ci.init)[0]);
	printf("vtbl[1] = %p\n", (*cast(void ***)ci.init)[1]);
	printf("init[0] = %x\n", (cast(uint *)ci.init)[0]);
	printf("init[1] = %x\n", (cast(uint *)ci.init)[1]);
	printf("init[2] = %x\n", (cast(uint *)ci.init)[2]);
	printf("init[3] = %x\n", (cast(uint *)ci.init)[3]);
	printf("init[4] = %x\n", (cast(uint *)ci.init)[4]);
    }


    // Initialize it
    (cast(byte*)p)[0 .. ci.init.length] = ci.init[];

    //printf("initialization done\n");
    return cast(Object)p;
}

extern (D) alias void (*fp_t)(Object);		// generic function pointer

void _d_delinterface(void** p)
{
    if (*p)
    {
	Interface *pi = **cast(Interface ***)*p;
	Object o;

	o = cast(Object)(*p - pi.offset);
	_d_delclass(&o);
	*p = null;
    }
}

void _d_delclass(Object *p)
{
    if (*p)
    {
	debug (PRINTF) printf("_d_delclass(%p)\n", *p);
	version(0)
	{
	    ClassInfo **pc = cast(ClassInfo **)*p;
	    if (*pc)
	    {
		ClassInfo c = **pc;

		if (c.deallocator)
		{
		    _d_callfinalizer(cast(void *)(*p));
		    fp_t fp = cast(fp_t)c.deallocator;
		    (*fp)(*p);			// call deallocator
		    *p = null;
		    return;
		}
	    }
	}
	_gc.free(cast(void*)(*p));
	*p = null;
    }
}

/******************************************
 * Allocate a new array of length elements.
 * ti is the type of the resulting array, or pointer to element.
 */

/* For when the array is initialized to 0 */
void* _d_newarrayT(TypeInfo ti, size_t length)
{
    void* result;
    auto size = ti.next.tsize();		// array element size

    debug(PRINTF) printf("_d_newarrayT(length = x%x, size = %d)\n", length, size);
    if (length && size)
    {
	/*version (D_InlineAsm_X86)
	{
	    asm
	    {
		mov	EAX,size	;
		mul	EAX,length	;
		mov	size,EAX	;
		jc	Loverflow	;
	    }
	}
	else*/
	    size *= length;
	result = cast(byte*) _gc.malloc(size + 1);
	if (!(ti.next.flags() & 1))
	    _gc.hasNoPointers(result);
	memset(result, 0, size);
    }
    return result;

Loverflow:
    _d_OutOfMemory();
}

/* For when the array has a non-zero initializer.
 */
void* _d_newarrayiT(TypeInfo ti, size_t length)
{
    void* result;
    auto size = ti.next.tsize();		// array element size

    debug(PRINTF)
	 printf("_d_newarrayiT(length = %d, size = %d)\n", length, size);
    if (length == 0 || size == 0)
	{ }
    else
    {
	auto initializer = ti.next.init();
	auto isize = initializer.length;
	auto q = initializer.ptr;
	/*version (D_InlineAsm_X86)
	{
	    asm
	    {
		mov	EAX,size	;
		mul	EAX,length	;
		mov	size,EAX	;
		jc	Loverflow	;
	    }
	}
	else*/
	    size *= length;
	auto p = _gc.malloc(size + 1);
	debug(PRINTF) printf(" p = %p\n", p);
	if (!(ti.next.flags() & 1))
	    _gc.hasNoPointers(p);
	if (isize == 1)
	    memset(p, *cast(ubyte*)q, size);
	else if (isize == int.sizeof)
	{
	    int init = *cast(int*)q;
	    size /= int.sizeof;
	    for (size_t u = 0; u < size; u++)
	    {
		(cast(int*)p)[u] = init;
	    }
	}
	else
	{
	    for (size_t u = 0; u < size; u += isize)
	    {
		memcpy(p + u, q, isize);
	    }
	}
	result = cast(byte*) p
;
    }
    return result;

Loverflow:
    _d_OutOfMemory();
}

void[] _d_newarraymTp(TypeInfo ti, int ndims, size_t* pdim)
{
    void[] result = void;

    //debug(PRINTF)
	//printf("_d_newarraymT(ndims = %d)\n", ndims);
    if (ndims == 0)
	result = null;
    else
    {

	void[] foo(TypeInfo ti, size_t* pdim, int ndims)
	{
	    size_t dim = *pdim;
	    void[] p;

	    //printf("foo(ti = %p, ti.next = %p, dim = %d, ndims = %d\n", ti, ti.next, dim, ndims);
	    if (ndims == 1)
	    {
		auto r = _d_newarrayT(ti, dim);
		p = *cast(void[]*)(&r);
	    }
	    else
	    {
		p = _gc.malloc(dim * (void[]).sizeof + 1)[0 .. dim];
		for (int i = 0; i < dim; i++)
		{
		    (cast(void[]*)p.ptr)[i] = foo(ti.next, pdim + 1, ndims - 1);
		}
	    }
	    return p;
	}

	result = foo(ti, pdim, ndims);
	//printf("result = %llx\n", result);

	version (none)
	{
	    for (int i = 0; i < ndims; i++)
	    {
		printf("index %d: %d\n", i, pdim[i]);
	    }
	}
    }
    return result;
}

void[] _d_newarraymiTp(TypeInfo ti, int ndims, size_t* pdim)
{
    void[] result = void;

    //debug(PRINTF)
	//printf("_d_newarraymi(size = %d, ndims = %d)\n", size, ndims);
    if (ndims == 0)
	result = null;
    else
    {

	void[] foo(TypeInfo ti, size_t* pdim, int ndims)
	{
	    size_t dim = *pdim;
	    void[] p;

	    if (ndims == 1)
	    {
		auto r = _d_newarrayiT(ti, dim);
		p = *cast(void[]*)(&r);
	    }
	    else
	    {
		p = _gc.malloc(dim * (void[]).sizeof + 1)[0 .. dim];
		for (int i = 0; i < dim; i++)
		{
		    (cast(void[]*)p.ptr)[i] = foo(ti.next, pdim + 1, ndims - 1);
		}
	    }
	    return p;
	}

	result = foo(ti, pdim, ndims);
	//printf("result = %llx\n", result);

	version (none)
	{
	    for (int i = 0; i < ndims; i++)
	    {
		printf("index %d: %d\n", i, pdim[i]);
		printf("init = %d\n", *cast(int*)pinit);
	    }
	}
    }
    return result;
}

struct Array
{
    size_t length;
    byte *data;
};

// Perhaps we should get a a size argument like _d_new(), so we
// can zero out the array?

void _d_delarray(size_t plength, void* pdata)
{
    assert(!plength || pdata);
    if (pdata) _gc.free(pdata);
}


void _d_delmemory(void* *p)
{
    if (*p)
    {
	_gc.free(*p);
	*p = null;
    }
}


}

void new_finalizer(void *p, bool dummy)
{
    //printf("new_finalizer(p = %p)\n", p);
    _d_callfinalizer(p);
}

extern (C)
void _d_callinterfacefinalizer(void *p)
{
    //printf("_d_callinterfacefinalizer(p = %p)\n", p);
    if (p)
    {
	Interface *pi = **cast(Interface ***)p;
	Object o = cast(Object)(p - pi.offset);
	_d_callfinalizer(cast(void*)o);
    }
}

extern (C)
void _d_callfinalizer(void *p)
{
    //printf("_d_callfinalizer(p = %p)\n", p);
    if (p)	// not necessary if called from gc
    {
	ClassInfo **pc = cast(ClassInfo **)p;
	if (*pc)
	{
	    ClassInfo c = **pc;

	    try
	    {
		do
		{
		    if (c.destructor)
		    {
			fp_t fp = cast(fp_t)c.destructor;
			(*fp)(cast(Object)p);		// call destructor
		    }
		    c = c.base;
		} while (c);
		if ((cast(void**)p)[1])	// if monitor is not null
		    _d_monitorexit(cast(Object)p);
	    }
	    finally
	    {
		*pc = null;			// zero vptr
	    }
	}
    }
}

/+ ------------------------------------------------ +/


/******************************
 * Resize dynamic arrays with 0 initializers.
 */

extern (C)
byte* _d_arraysetlengthT(TypeInfo ti, size_t newlength, size_t plength, byte* pdata)
in
{
    assert(ti);
}
body
{
    byte* newdata;
    size_t sizeelem = ti.next.tsize();

    debug(PRINTF)
    {
	printf("_d_arraysetlengthT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
	if (p)
	    printf("\tpdata = %p, plength = %d\n", pdata, plength);
    }

    if (newlength)
    {
	version (GNU)
	{
	    // required to output the label;
	    static char x = 0;
	    if (x)
		goto Loverflow;
	}

	version (D_InlineAsm_X86)
	{
	    size_t newsize = void;

	    asm
	    {
		mov	EAX,newlength	;
		mul	EAX,sizeelem	;
		mov	newsize,EAX	;
		jc	Loverflow	;
	    }
	}
	else
	{
	    size_t newsize = sizeelem * newlength;

	    if (newsize / newlength != sizeelem)
		goto Loverflow;
	}
	//printf("newsize = %x, newlength = %x\n", newsize, newlength);

	if (pdata)
	{
	    newdata = pdata;
	    if (newlength > plength)
	    {
		size_t size = plength * sizeelem;
		size_t cap = _gc.capacity(pdata);

		if (cap <= newsize)
		{
		    if (cap >= 4096)
		    {	// Try to extend in-place
			auto u = _gc.extend(pdata, (newsize + 1) - cap, (newsize + 1) - cap);
			if (u)
			{
			    goto L1;
			}
		    }
		    newdata = cast(byte *)_gc.malloc(newsize + 1);
		    newdata[0 .. size] = pdata[0 .. size];
		    if (!(ti.next.flags() & 1))
			_gc.hasNoPointers(newdata);
		}
	     L1:
		newdata[size .. newsize] = 0;
	    }
	}
	else
	{
	    newdata = cast(byte *)_gc.calloc(newsize + 1, 1);
	    if (!(ti.next.flags() & 1))
		_gc.hasNoPointers(newdata);
	}
    }
    else
    {
	newdata = pdata;
    }

    pdata = newdata;
    plength = newlength;
    return newdata;

Loverflow:
    _d_OutOfMemory();
}

/**
 * Resize arrays for non-zero initializers.
 *	p		pointer to array lvalue to be updated
 *	newlength	new .length property of array
 *	sizeelem	size of each element of array
 *	initsize	size of initializer
 *	...		initializer
 */
extern (C)
byte* _d_arraysetlengthiT(TypeInfo ti, size_t newlength, size_t plength, byte* pdata)
in
{
    assert(!plength || pdata);
}
body
{
    byte* newdata;
    size_t sizeelem = ti.next.tsize();
    void[] initializer = ti.next.init();
    size_t initsize = initializer.length;

    assert(sizeelem);
    assert(initsize);
    assert(initsize <= sizeelem);
    assert((sizeelem / initsize) * initsize == sizeelem);

    debug(PRINTF)
    {
	printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d, initsize = %d)\n", p, sizeelem, newlength, initsize);
	if (p)
	    printf("\tpdata = %p, plength = %d\n", pdata, plength);
    }

    if (newlength)
    {
	version (GNU)
	{
	    // required to output the label;
	    static char x = 0;
	    if (x)
		goto Loverflow;
	}

	version (D_InlineAsm_X86)
	{
	    size_t newsize = void;

	    asm
	    {
		mov	EAX,newlength	;
		mul	EAX,sizeelem	;
		mov	newsize,EAX	;
		jc	Loverflow	;
	    }
	}
	else
	{
	    size_t newsize = sizeelem * newlength;

	    if (newsize / newlength != sizeelem)
		goto Loverflow;
	}
	//printf("newsize = %x, newlength = %x\n", newsize, newlength);

	size_t size = plength * sizeelem;
	if (pdata)
	{
	    newdata = pdata;
	    if (newlength > plength)
	    {
		size_t cap = _gc.capacity(pdata);

		if (cap <= newsize)
		{
		    if (cap >= 4096)
		    {	// Try to extend in-place
			auto u = _gc.extend(pdata, (newsize + 1) - cap, (newsize + 1) - cap);
			if (u)
			{
			    goto L1;
			}
		    }
		    newdata = cast(byte *)_gc.malloc(newsize + 1);
		    newdata[0 .. size] = pdata[0 .. size];
		L1: ;
		}
	    }
	}
	else
	{
	    newdata = cast(byte *)_gc.malloc(newsize + 1);
	    if (!(ti.next.flags() & 1))
		_gc.hasNoPointers(newdata);
	}

	auto q = initializer.ptr;	// pointer to initializer

	if (newsize > size)
	{
	    if (initsize == 1)
	    {
		//printf("newdata = %p, size = %d, newsize = %d, *q = %d\n", newdata, size, newsize, *cast(byte*)q);
		newdata[size .. newsize] = *(cast(byte*)q);
	    }
	    else
	    {
		for (size_t u = size; u < newsize; u += initsize)
		{
		    memcpy(newdata + u, q, initsize);
		}
	    }
	}
    }
    else
    {
	newdata = pdata;
    }

    pdata = newdata;
    plength = newlength;
    return newdata;

Loverflow:
    _d_OutOfMemory();
}

/****************************************
 * Append y[] to array x[].
 * size is size of each array element.
 */

extern (C)
Array _d_arrayappendT(TypeInfo ti, Array *px, byte[] y)
{
    auto sizeelem = ti.next.tsize();		// array element size
    auto cap = _gc.capacity(px.data);
    auto length = px.length;
    auto newlength = length + y.length;
    auto newsize = newlength * sizeelem;
    if (newsize > cap)
    {   byte* newdata;

	if (cap >= 4096)
	{   // Try to extend in-place
	    auto u = _gc.extend(px.data, (newsize + 1) - cap, (newsize + 1) - cap);
	    if (u)
	    {
		goto L1;
	    }
	}

	newdata = cast(byte *)_gc.malloc(newCapacity(newlength, sizeelem) + 1);
	if (!(ti.next.flags() & 1))
	    _gc.hasNoPointers(newdata);
	memcpy(newdata, px.data, length * sizeelem);
	px.data = newdata;
    }
  L1:
    px.length = newlength;
    memcpy(px.data + length * sizeelem, y.ptr, y.length * sizeelem);
    return *px;
}

size_t newCapacity(size_t newlength, size_t size)
{
    version(none)
    {
	size_t newcap = newlength * size;
    }
    else
    {
	/*
	 * Better version by Dave Fladebo:
	 * This uses an inverse logorithmic algorithm to pre-allocate a bit more
	 * space for larger arrays.
	 * - Arrays smaller than 4096 bytes are left as-is, so for the most
	 * common cases, memory allocation is 1 to 1. The small overhead added
	 * doesn't effect small array perf. (it's virtually the same as
	 * current).
	 * - Larger arrays have some space pre-allocated.
	 * - As the arrays grow, the relative pre-allocated space shrinks.
	 * - The logorithmic algorithm allocates relatively more space for
	 * mid-size arrays, making it very fast for medium arrays (for
	 * mid-to-large arrays, this turns out to be quite a bit faster than the
	 * equivalent realloc() code in C, on Linux at least. Small arrays are
	 * just as fast as GCC).
	 * - Perhaps most importantly, overall memory usage and stress on the GC
	 * is decreased significantly for demanding environments.
	 */
	size_t newcap = newlength * size;
	size_t newext = 0;

	if (newcap > 4096)
	{
	    //double mult2 = 1.0 + (size / log10(pow(newcap * 2.0,2.0)));

	    // Redo above line using only integer math

	    static int log2plus1(size_t c)
	    {   int i;

		if (c == 0)
		    i = -1;
		else
		    for (i = 1; c >>= 1; i++)
			{   }
		return i;
	    }

	    /* The following setting for mult sets how much bigger
	     * the new size will be over what is actually needed.
	     * 100 means the same size, more means proportionally more.
	     * More means faster but more memory consumption.
	     */
	    //long mult = 100 + (1000L * size) / (6 * log2plus1(newcap));
	    long mult = 100 + (1000L * size) / log2plus1(newcap);

	    // testing shows 1.02 for large arrays is about the point of diminishing return
	    if (mult < 102)
		mult = 102;
	    newext = cast(size_t)((newcap * mult) / 100);
	    newext -= newext % size;
	    //printf("mult: %2.2f, mult2: %2.2f, alloc: %2.2f\n",mult/100.0,mult2,newext / cast(double)size);
	}
	newcap = newext > newcap ? newext : newcap;
	//printf("newcap = %d, newlength = %d, size = %d\n", newcap, newlength, size);
    }
    return newcap;
}

extern (C)
byte[] _d_arrayappendcTp(TypeInfo ti, inout byte[] x, byte *argp)
{
    auto sizeelem = ti.next.tsize();		// array element size
    auto cap = _gc.capacity(x.ptr);
    auto length = x.length;
    auto newlength = length + 1;
    auto newsize = newlength * sizeelem;

    assert(cap == 0 || length * sizeelem <= cap);

    //printf("_d_arrayappendc(sizeelem = %d, ptr = %p, length = %d, cap = %d)\n", sizeelem, x.ptr, x.length, cap);

    if (newsize >= cap)
    {   byte* newdata;

	if (cap >= 4096)
	{   // Try to extend in-place
	    auto u = _gc.extend(x.ptr, (newsize + 1) - cap, (newsize + 1) - cap);
	    if (u)
	    {
		goto L1;
	    }
	}

	//printf("_d_arrayappendc(sizeelem = %d, newlength = %d, cap = %d)\n", sizeelem, newlength, cap);
	cap = newCapacity(newlength, sizeelem);
	assert(cap >= newlength * sizeelem);
	newdata = cast(byte *)_gc.malloc(cap + 1);
	if (!(ti.next.flags() & 1))
	    _gc.hasNoPointers(newdata);
	memcpy(newdata, x.ptr, length * sizeelem);
	(cast(void **)(&x))[1] = newdata;
    }
  L1:

    *cast(size_t *)&x = newlength;
    x.ptr[length * sizeelem .. newsize] = argp[0 .. sizeelem];
    assert((cast(size_t)x.ptr & 15) == 0);
    assert(_gc.capacity(x.ptr) >= x.length * sizeelem);
    return x;
}

extern (C)
byte[] _d_arraycatT(TypeInfo ti, byte[] x, byte[] y)
out (result)
{
    auto sizeelem = ti.next.tsize();		// array element size
    //printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d => %d,%p)\n", x.length, x.ptr, y.length, y.ptr, sizeelem, result.length, result.ptr);
    assert(result.length == x.length + y.length);
    for (size_t i = 0; i < x.length * sizeelem; i++)
	assert((cast(byte*)result)[i] == (cast(byte*)x)[i]);
    for (size_t i = 0; i < y.length * sizeelem; i++)
	assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]);

    size_t cap = _gc.capacity(result.ptr);
    assert(!cap || cap > result.length * sizeelem);
}
body
{
    version (none)
    {
	/* Cannot use this optimization because:
	 *  char[] a, b;
	 *  char c = 'a';
	 *	b = a ~ c;
	 *	c = 'b';
	 * will change the contents of b.
	 */
	if (!y.length)
	    return x;
	if (!x.length)
	    return y;
    }

    //printf("_d_arraycatT(%d,%p ~ %d,%p)\n", x.length, x.ptr, y.length, y.ptr);
    auto sizeelem = ti.next.tsize();		// array element size
    //printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d)\n", x.length, x.ptr, y.length, y.ptr, sizeelem);
    size_t xlen = x.length * sizeelem;
    size_t ylen = y.length * sizeelem;
    size_t len = xlen + ylen;
    if (!len)
	return null;

    byte* p = cast(byte*)_gc.malloc(len + 1);
    if (!(ti.next.flags() & 1))
	_gc.hasNoPointers(p);
    memcpy(p, x.ptr, xlen);
    memcpy(p + xlen, y.ptr, ylen);
    p[len] = 0;

    return p[0 .. x.length + y.length];
}


extern (C)
byte[] _d_arraycatnT(TypeInfo ti, uint n, ...)
{   void* a;
    size_t length;
    byte[]* p;
    uint i;
    byte[] b;
    va_list va;
    auto sizeelem = ti.next.tsize();		// array element size

    va_start!(typeof(n))(va, n);

    for (i = 0; i < n; i++)
    {
	b = va_arg!(typeof(b))(va);
	length += b.length;
    }
    if (!length)
	return null;

    a = _gc.malloc(length * sizeelem);
    if (!(ti.next.flags() & 1))
	_gc.hasNoPointers(a);
    va_start!(typeof(n))(va, n);

    uint j = 0;
    for (i = 0; i < n; i++)
    {
	b = va_arg!(typeof(b))(va);
	if (b.length)
	{
	    memcpy(a + j, b.ptr, b.length * sizeelem);
	    j += b.length * sizeelem;
	}
    }

    return (cast(byte*)a)[0..length];
}

version (GNU) { } else
extern (C)
void* _d_arrayliteralT(TypeInfo ti, size_t length, ...)
{
    auto sizeelem = ti.next.tsize();		// array element size
    void* result;

    //printf("_d_arrayliteralT(sizeelem = %d, length = %d)\n", sizeelem, length);
    if (length == 0 || sizeelem == 0)
	result = null;
    else
    {
	result = _gc.malloc(length * sizeelem);
	if (!(ti.next.flags() & 1))
	{
	    _gc.hasNoPointers(result);
	}

	va_list q;
	va_start!(size_t)(q, length);

	size_t stacksize = (sizeelem + int.sizeof - 1) & ~(int.sizeof - 1);

	if (stacksize == sizeelem)
	{
	    memcpy(result, q, length * sizeelem);
	}
	else
	{
	    for (size_t i = 0; i < length; i++)
	    {
		memcpy(result + i * sizeelem, q, sizeelem);
		q += stacksize;
	    }
	}

	va_end(q);
    }
    return result;
}

/**********************************
 * Support for array.dup property.
 */

/*struct Array2
{
    size_t length;
    void* ptr;
}*/

extern(C) void* _d_allocmemoryT(size_t foo) { return malloc(foo).ptr; }