Files
Iain Buclaw 8b0ccc95f1 d: Merge upstream dmd, druntime e1f6680f50, phobos f87979028
D front-end changes:

	- Import latest fixes from v2.112.0-beta.1.
	- Associative array operations are now lowered to templates
	  defined in `core.internal.newaa'.

D runtime changes:

	- Import latest fixes from v2.112.0-beta.1.

Phobos changes:

	- Import latest fixes from v2.112.0-beta.1.

gcc/d/ChangeLog:

	* dmd/MERGE: Merge upstream dmd e1f6680f50.
	* Make-lang.in (D_FRONTEND_OBJS): Remove d/gluelayer.o, d/iasm.o,
	d/iasmgcc.o.  Add d/iasm-package.o, d/iasm-gcc.o.
	(d/iasm-%.o): New recipe.
	* d-convert.cc (convert_expr): Remove library call to dynamic_cast.
	* d-lang.cc (d_init_options): Set errorSupplementLimit parameter.
	* decl.cc (DeclVisitor::visit): Update for new front-end interface.
	* expr.cc (ExprVisitor::visit (EqualExp *)): Update for front-end
	lowering expression using templates.
	(ExprVisitor::visit (InExp *)): Likewise.
	(ExprVisitor::visit (IndexExp *)): Likewise.
	(ExprVisitor::visit (CastExp *)): Likewise.
	(ExprVisitor::visit (RemoveExp *)): Likewise.
	(ExprVisitor::visit (AddrExp *)): Likewise.
	(ExprVisitor::visit (NewExp *)): Likewise.
	(ExprVisitor::visit (ArrayLiteralExp *)): Likewise.
	(ExprVisitor::visit (AssocArrayLiteralExp *)): Likewise.
	* imports.cc (ImportVisitor::visit (AliasDeclaration *)): Update for
	new front-end interface.
	* runtime.def (DYNAMIC_CAST): Remove.
	(INTERFACE_CAST): Remove.
	(ARRAYLITERALTX): Remove.
	(ADEQ2): Remove.
	(ASSOCARRAYLITERALTX): Remove.
	(AANEW): Remove.
	(AAEQUAL): Remove.
	(AAINX): Remove.
	(AAGETY): Remove.
	(AAGETRVALUEX): Remove.
	(AADELX): Remove.
	* typeinfo.cc (TypeInfoVisitor::visit): Layout xopEquals and xtoHash
	in TypeInfo_AssociativeArray.
	(create_typeinfo): Add new fields to internal typeinfo.
	(check_typeinfo_type): Print instantiation trace of error.
	* types.cc (TypeVisitor::visit (TypeStruct *)): Update for new
	front-end interface.

libphobos/ChangeLog:

	* libdruntime/MERGE: Merge upstream druntime e1f6680f50.
	* libdruntime/Makefile.am (DRUNTIME_DSOURCES): Add
	core/internal/cast_.d.  Remove rt/aaA.d, rt/adi.d, rt/cast_.d.
	(DRUNTIME_DSOURCES_POSIX): Add core/sys/posix/endian.d.
	* libdruntime/Makefile.in: Regenerate.
	* src/MERGE: Merge upstream phobos f87979028.
	* src/Makefile.am (PHOBOS_DSOURCES): Add std/internal/entropy.d.
	* src/Makefile.in: Regenerate.
	* testsuite/libphobos.aa/test_aa.d: Adjust test.
	* testsuite/libphobos.init_fini/custom_gc.d: Likewise.

gcc/testsuite/ChangeLog:

	* gdc.dg/asm1.d: Adjust test.
	* gdc.dg/asm5.d: Likewise.
	* gdc.dg/pr100967.d: Likewise.
	* gdc.dg/rtti1.d: Likewise.
	* gdc.dg/rtti2.d: New test.
2026-01-31 09:37:45 +01:00

257 lines
7.5 KiB
D

/**
BlkInfo thread-local cache. Used for array appending in the conservative GC to avoid the lock when possible.
Note: this used to be in rt.lifetime, but was moved here to allow GCs to take over array operations.
*/
module core.internal.gc.blkcache;
import core.memory;
import core.attribute;
debug (PRINTF) import core.stdc.stdio : printf;
alias BlkInfo = GC.BlkInfo;
alias BlkAttr = GC.BlkAttr;
/**
cache for the lookup of the block info
*/
private enum N_CACHE_BLOCKS = 8;
// note this is TLS, so no need to sync.
BlkInfo *__blkcache_storage;
static if (N_CACHE_BLOCKS == 1)
{
version=single_cache;
}
else
{
//version=simple_cache; // uncomment to test simple cache strategy
//version=random_cache; // uncomment to test random cache strategy
// ensure N_CACHE_BLOCKS is power of 2.
static assert(!((N_CACHE_BLOCKS - 1) & N_CACHE_BLOCKS));
version (random_cache)
{
int __nextRndNum = 0;
}
int __nextBlkIdx;
}
@property BlkInfo *__blkcache() nothrow @nogc
{
if (!__blkcache_storage)
{
import core.stdc.stdlib : calloc;
import core.thread.threadbase;
auto tBase = ThreadBase.getThis();
if (tBase is null)
// if we don't have a thread object, this is a detached thread, and
// this won't be properly maintained by the GC.
return null;
// allocate the block cache for the first time
immutable size = BlkInfo.sizeof * N_CACHE_BLOCKS;
// use C alloc, because this may become a detached thread, and the GC
// would then clean up the cache without zeroing this pointer.
__blkcache_storage = cast(BlkInfo*) calloc(size, 1);
tBase.tlsGCData = __blkcache_storage;
}
return __blkcache_storage;
}
// free the allocation on thread exit.
void cleanupBlkCache(void* storage) nothrow @nogc
{
if (storage)
{
// check if this is the same thread as the current running thread, and
// if so, make sure we don't leave a dangling pointer.
if (__blkcache_storage is storage)
__blkcache_storage = null;
import core.stdc.stdlib : free;
free(storage);
}
}
/**
* Indicates whether an address has been marked by the GC.
*/
enum IsMarked : int
{
no, /// Address is not marked.
yes, /// Address is marked.
unknown, /// Address is not managed by the GC.
}
alias IsMarkedDg = IsMarked delegate(void* addr) nothrow; /// The isMarked callback function.
// we expect this to be called with the lock in place
void processGCMarks(void* data, scope IsMarkedDg isMarked) nothrow
{
if (!data)
return;
auto cache = cast(BlkInfo*) data;
// called after the mark routine to eliminate block cache data when it
// might be ready to sweep
debug(PRINTF) printf("processing GC Marks, %p\n", cache);
debug(PRINTF) foreach (i; 0 .. N_CACHE_BLOCKS)
{
printf("cache entry %d has base ptr %p\tsize %zd\tflags %x\n", i, cache[i].base, cache[i].size, cache[i].attr);
}
auto cache_end = cache + N_CACHE_BLOCKS;
for (;cache < cache_end; ++cache)
{
if (cache.base != null && isMarked(cache.base) == IsMarked.no)
{
debug(PRINTF) printf("clearing cache entry at %p\n", cache.base);
cache.base = null; // clear that data.
}
}
}
unittest
{
// Bugzilla 10701 - segfault in GC
ubyte[] result; result.length = 4096;
GC.free(result.ptr);
GC.collect();
}
/**
Get the cached block info of an interior pointer. Returns null if the
interior pointer's block is not cached.
NOTE: The following note was not valid, but is retained for historical
purposes. The data cannot be cleared because the stack contains a
reference to the affected block (e.g. through `interior`). Therefore,
the element will not be collected, and the data will remain valid.
ORIGINAL: The base ptr in this struct can be cleared asynchronously by the GC,
so any use of the returned BlkInfo should copy it and then check the
base ptr of the copy before actually using it.
*/
BlkInfo *__getBlkInfo(void *interior) nothrow @nogc
{
BlkInfo *ptr = __blkcache;
if (ptr is null)
// if for some reason we don't have a cache, return null.
return null;
version (single_cache)
{
if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size)
return ptr;
return null; // not in cache.
}
else version (simple_cache)
{
foreach (i; 0..N_CACHE_BLOCKS)
{
if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size)
return ptr;
ptr++;
}
}
else
{
// try to do a smart lookup, using __nextBlkIdx as the "head"
auto curi = ptr + __nextBlkIdx;
for (auto i = curi; i >= ptr; --i)
{
if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size)
return i;
}
for (auto i = ptr + N_CACHE_BLOCKS - 1; i > curi; --i)
{
if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size)
return i;
}
}
return null; // not in cache.
}
void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow @nogc
{
auto cache = __blkcache;
if (cache is null)
// no cache to use.
return;
version (single_cache)
{
*cache = bi;
return;
}
else
{
version (simple_cache)
{
if (curpos)
*curpos = bi;
else
{
// note, this is a super-simple algorithm that does not care about
// most recently used. It simply uses a round-robin technique to
// cache block info. This means that the ordering of the cache
// doesn't mean anything. Certain patterns of allocation may
// render the cache near-useless.
cache[__nextBlkIdx] = bi;
__nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1);
}
}
else version (random_cache)
{
// strategy: if the block currently is in the cache, move the
// current block index to the a random element and evict that
// element.
if (!curpos)
{
__nextBlkIdx = (__nextRndNum = 1664525 * __nextRndNum + 1013904223) & (N_CACHE_BLOCKS - 1);
curpos = cache + __nextBlkIdx;
}
else
{
__nextBlkIdx = curpos - cache;
}
*curpos = bi;
}
else
{
//
// strategy: If the block currently is in the cache, swap it with
// the head element. Otherwise, move the head element up by one,
// and insert it there.
//
if (!curpos)
{
__nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1);
curpos = cache + __nextBlkIdx;
}
else if (curpos !is cache + __nextBlkIdx)
{
*curpos = cache[__nextBlkIdx];
curpos = cache + __nextBlkIdx;
}
*curpos = bi;
}
}
}
debug(PRINTF)
{
extern(C) void printArrayCache()
{
auto ptr = __blkcache;
printf("CACHE: \n");
foreach (i; 0 .. N_CACHE_BLOCKS)
{
printf(" %d\taddr:% .8p\tsize:% .10zd\tflags:% .8x\n", i, ptr[i].base, ptr[i].size, ptr[i].attr);
}
}
}