mirror of
https://github.com/openRuyi-Project/gcc.git
synced 2026-10-03 15:39:28 +00:00
D front-end changes: - Import latest fixes from dmd. D runtime changes: - Import latest fixes from druntime. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd ffbad272b6. * d-tree.h (make_location_t): Add overload taking a const SourceLoc &. * d-codegen.cc (make_location_t): Likewise. * d-diagnostic.cc (d_diagnostic_report_diagnostic): Change first parameter type to const SourceLoc &. (verrorReport): Update for new front-end interface. (verrorReportSupplemental): Likewise. * d-frontend.cc (eval_builtin): Likewise. (getTypeInfoType): Likewise. * d-lang.cc (d_parse_file): Likewise. * d-target.cc (Target::va_listType): Likewise. (Target::getTargetInfo): Likewise. * decl.cc (build_decl_tree): Likewise. * imports.cc (ImportVisitor::visit (Module *)): Likewise. * modules.cc (get_internal_fn): Likewise. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime ffbad272b6.
361 lines
11 KiB
D
361 lines
11 KiB
D
/**
|
|
Functions to manipulate metadata in-block.
|
|
|
|
functionality was moved from rt.lifetime
|
|
*/
|
|
module core.internal.gc.blockmeta;
|
|
|
|
import core.memory;
|
|
|
|
alias BlkInfo = GC.BlkInfo;
|
|
alias BlkAttr = GC.BlkAttr;
|
|
|
|
enum : size_t
|
|
{
|
|
PAGESIZE = 4096,
|
|
BIGLENGTHMASK = ~(PAGESIZE - 1),
|
|
SMALLPAD = 1,
|
|
MEDPAD = ushort.sizeof,
|
|
LARGEPREFIX = 16, // 16 bytes padding at the front of the array
|
|
LARGEPAD = LARGEPREFIX + 1,
|
|
MAXSMALLSIZE = 256-SMALLPAD,
|
|
MAXMEDSIZE = (PAGESIZE / 2) - MEDPAD
|
|
}
|
|
|
|
// size used to store the TypeInfo at the end of an allocation for structs that have a destructor
|
|
size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
|
|
{
|
|
if (ti && typeid(ti) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast
|
|
{
|
|
auto sti = cast(TypeInfo_Struct)cast(void*)ti;
|
|
if (sti.xdtor)
|
|
return size_t.sizeof;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
Set the allocated length of the array block. This is called
|
|
any time an array is appended to or its length is set.
|
|
|
|
The allocated block looks like this for blocks < PAGESIZE:
|
|
|
|
|elem0|elem1|elem2|...|elemN-1|emptyspace|N*elemsize|
|
|
|
|
|
|
The size of the allocated length at the end depends on the block size:
|
|
|
|
a block of 16 to 256 bytes has an 8-bit length.
|
|
|
|
a block with 512 to pagesize/2 bytes has a 16-bit length.
|
|
|
|
For blocks >= pagesize, the length is a size_t and is at the beginning of the
|
|
block. The reason we have to do this is because the block can extend into
|
|
more pages, so we cannot trust the block length if it sits at the end of the
|
|
block, because it might have just been extended. If we can prove in the
|
|
future that the block is unshared, we may be able to change this, but I'm not
|
|
sure it's important.
|
|
|
|
In order to do put the length at the front, we have to provide 16 bytes
|
|
buffer space in case the block has to be aligned properly. In x86, certain
|
|
SSE instructions will only work if the data is 16-byte aligned. In addition,
|
|
we need the sentinel byte to prevent accidental pointers to the next block.
|
|
Because of the extra overhead, we only do this for page size and above, where
|
|
the overhead is minimal compared to the block size.
|
|
|
|
So for those blocks, it looks like:
|
|
|
|
|N*elemsize|padding|elem0|elem1|...|elemN-1|emptyspace|sentinelbyte|
|
|
|
|
where elem0 starts 16 bytes after the first byte.
|
|
*/
|
|
bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength = size_t.max) pure nothrow
|
|
{
|
|
__setBlockFinalizerInfo(info, tinext);
|
|
|
|
size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
|
|
return __setArrayAllocLengthImpl(info, newlength, isshared, oldlength, typeInfoSize);
|
|
}
|
|
|
|
// the impl function, used both above and in core.internal.array.utils
|
|
bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared, size_t oldlength, size_t typeInfoSize) pure nothrow
|
|
{
|
|
import core.atomic;
|
|
|
|
if (info.size <= 256)
|
|
{
|
|
import core.checkedint;
|
|
|
|
bool overflow;
|
|
auto newlength_padded = addu(newlength,
|
|
addu(SMALLPAD, typeInfoSize, overflow),
|
|
overflow);
|
|
|
|
if (newlength_padded > info.size || overflow)
|
|
// new size does not fit inside block
|
|
return false;
|
|
|
|
auto length = cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD);
|
|
if (oldlength != size_t.max)
|
|
{
|
|
if (isshared)
|
|
{
|
|
return cas(cast(shared)length, cast(ubyte)oldlength, cast(ubyte)newlength);
|
|
}
|
|
else
|
|
{
|
|
if (*length == cast(ubyte)oldlength)
|
|
*length = cast(ubyte)newlength;
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// setting the initial length, no cas needed
|
|
*length = cast(ubyte)newlength;
|
|
}
|
|
}
|
|
else if (info.size <= PAGESIZE / 2)
|
|
{
|
|
if (newlength + MEDPAD + typeInfoSize > info.size)
|
|
// new size does not fit inside block
|
|
return false;
|
|
auto length = cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD);
|
|
if (oldlength != size_t.max)
|
|
{
|
|
if (isshared)
|
|
{
|
|
return cas(cast(shared)length, cast(ushort)oldlength, cast(ushort)newlength);
|
|
}
|
|
else
|
|
{
|
|
if (*length == oldlength)
|
|
*length = cast(ushort)newlength;
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// setting the initial length, no cas needed
|
|
*length = cast(ushort)newlength;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (newlength + LARGEPAD > info.size)
|
|
// new size does not fit inside block
|
|
return false;
|
|
auto length = cast(size_t *)(info.base);
|
|
if (oldlength != size_t.max)
|
|
{
|
|
if (isshared)
|
|
{
|
|
return cas(cast(shared)length, cast(size_t)oldlength, cast(size_t)newlength);
|
|
}
|
|
else
|
|
{
|
|
if (*length == oldlength)
|
|
*length = newlength;
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// setting the initial length, no cas needed
|
|
*length = newlength;
|
|
}
|
|
}
|
|
return true; // resize succeeded
|
|
}
|
|
|
|
/**
|
|
The block finalizer info is set separately from the array length, as that is
|
|
only needed on the initial setup of the block. No shared is needed, since
|
|
this should only happen when the block is new.
|
|
If the STRUCTFINAL bit is not set, no finalizer is stored (but if needed the
|
|
slot is zeroed)
|
|
*/
|
|
void __setBlockFinalizerInfo(ref BlkInfo info, const TypeInfo ti) pure nothrow
|
|
{
|
|
if ((info.attr & BlkAttr.APPENDABLE) && info.size > PAGESIZE / 2)
|
|
{
|
|
// if the structfinal bit is not set, we don't have a finalizer. But we
|
|
// should still zero out the finalizer slot.
|
|
auto context = (info.attr & BlkAttr.STRUCTFINAL) ? cast(void*)ti : null;
|
|
|
|
// array used size goes at the beginning. We can stuff the typeinfo
|
|
// right after it, as we need to use 16 bytes anyway.
|
|
//
|
|
auto typeInfo = cast(void**)info.base + 1;
|
|
*typeInfo = context;
|
|
version (D_LP64) {} else
|
|
{
|
|
// zero out the extra padding
|
|
(cast(size_t*)info.base)[2 .. 4] = 0;
|
|
}
|
|
}
|
|
else if(info.attr & BlkAttr.STRUCTFINAL)
|
|
{
|
|
// all other cases the typeinfo gets put at the end of the block
|
|
auto typeInfo = cast(void**)(info.base + info.size) - 1;
|
|
*typeInfo = cast(void*) ti;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Get the finalizer info from the block (typeinfo).
|
|
If called on a block, without STRUCTFINAL set, returns null.
|
|
*/
|
|
const(TypeInfo) __getBlockFinalizerInfo(ref BlkInfo info) pure nothrow
|
|
{
|
|
return __getBlockFinalizerInfo(info.base, info.size, info.attr);
|
|
}
|
|
|
|
/// ditto
|
|
const(TypeInfo) __getBlockFinalizerInfo(void* base, size_t size, uint attr) pure nothrow
|
|
{
|
|
if (!(attr & BlkAttr.STRUCTFINAL))
|
|
return null;
|
|
|
|
bool isLargeArray = (attr & BlkAttr.APPENDABLE) && size > PAGESIZE / 2;
|
|
auto typeInfo = isLargeArray ?
|
|
base + size_t.sizeof :
|
|
base + size - size_t.sizeof;
|
|
assert(*cast(size_t*)typeInfo != 0);
|
|
return *cast(TypeInfo*)typeInfo;
|
|
}
|
|
|
|
/**
|
|
get the used size of the array for the given block
|
|
*/
|
|
size_t __arrayAllocLength(ref BlkInfo info) pure nothrow
|
|
in(info.attr & BlkAttr.APPENDABLE)
|
|
{
|
|
auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
|
|
if (info.size <= 256)
|
|
return *cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD);
|
|
|
|
if (info.size <= PAGESIZE / 2)
|
|
return *cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD);
|
|
|
|
return *cast(size_t *)(info.base);
|
|
}
|
|
|
|
/**
|
|
Atomically get the used size of the array for the given block
|
|
*/
|
|
size_t __arrayAllocLengthAtomic(ref BlkInfo info) pure nothrow
|
|
in(info.attr & BlkAttr.APPENDABLE)
|
|
{
|
|
import core.atomic;
|
|
auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
|
|
if (info.size <= 256)
|
|
return atomicLoad(*cast(shared(ubyte)*)(info.base + info.size - typeInfoSize - SMALLPAD));
|
|
|
|
if (info.size <= PAGESIZE / 2)
|
|
return atomicLoad(*cast(shared(ushort)*)(info.base + info.size - typeInfoSize - MEDPAD));
|
|
|
|
return atomicLoad(*cast(shared(size_t)*)(info.base));
|
|
}
|
|
|
|
/**
|
|
Get the maximum bytes that can be stored in the given block.
|
|
*/
|
|
size_t __arrayAllocCapacity(ref BlkInfo info) pure nothrow
|
|
in(info.attr & BlkAttr.APPENDABLE)
|
|
{
|
|
// Capacity is a calculation based solely on the block info.
|
|
if (info.size > PAGESIZE / 2)
|
|
return info.size - LARGEPAD;
|
|
|
|
auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
|
|
auto padsize = info.size <= 256 ? SMALLPAD : MEDPAD;
|
|
return info.size - typeInfoSize - padsize;
|
|
}
|
|
|
|
/**
|
|
get the padding required to allocate size bytes. Note that the padding is
|
|
NOT included in the passed in size. Therefore, do NOT call this function
|
|
with the size of an allocated block.
|
|
*/
|
|
size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted
|
|
{
|
|
return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext));
|
|
}
|
|
|
|
/**
|
|
get the padding required to allocate size bytes, use the bits to determine
|
|
which metadata must be stored.
|
|
*/
|
|
size_t __allocPad(size_t size, uint bits) nothrow pure @trusted
|
|
{
|
|
auto finalizerSize = (bits & BlkAttr.STRUCTFINAL) ? (void*).sizeof : 0;
|
|
if (bits & BlkAttr.APPENDABLE)
|
|
{
|
|
if (size > MAXMEDSIZE - finalizerSize)
|
|
return LARGEPAD;
|
|
auto pad = (size > MAXSMALLSIZE - finalizerSize) ? MEDPAD : SMALLPAD;
|
|
return pad + finalizerSize;
|
|
}
|
|
|
|
return finalizerSize;
|
|
}
|
|
|
|
/**
|
|
* Get the start of the array for the given block.
|
|
*
|
|
* Params:
|
|
* info = array metadata
|
|
* base = pointer to base of memory block
|
|
* size = size of memory block.
|
|
* Returns:
|
|
* pointer to the start of the array
|
|
*/
|
|
void *__arrayStart()(return scope BlkInfo info) nothrow pure
|
|
{
|
|
return __arrayStart(info.base, info.size);
|
|
}
|
|
/// Ditto
|
|
void *__arrayStart()(return scope void* base, size_t size) nothrow pure
|
|
{
|
|
return base + ((size & BIGLENGTHMASK) ? LARGEPREFIX : 0);
|
|
}
|
|
|
|
/**
|
|
* Trim a block's extents to the known valid data that is not metadata. This
|
|
* takes into account the finalizer and array metadata.
|
|
*/
|
|
void __trimExtents(ref scope void* base, ref size_t blockSize, uint attr) nothrow pure @nogc
|
|
{
|
|
if (attr & BlkAttr.APPENDABLE)
|
|
{
|
|
if (blockSize > PAGESIZE / 2)
|
|
{
|
|
// large block, it's always LARGEPREFIX bytes at the front.
|
|
blockSize = *(cast(size_t*)base);
|
|
base += LARGEPREFIX;
|
|
return;
|
|
}
|
|
|
|
void *pend = base + blockSize;
|
|
if (attr & BlkAttr.STRUCTFINAL)
|
|
// skip the finalizer
|
|
pend -= (void*).sizeof;
|
|
|
|
// get the actual length
|
|
if (blockSize <= 256)
|
|
blockSize = *(cast(ubyte*)pend - 1);
|
|
else // medium array block
|
|
blockSize = *(cast(ushort*)pend - 1);
|
|
}
|
|
else if (attr & BlkAttr.STRUCTFINAL)
|
|
{
|
|
// not appendable, but has a finalizer in the block. This is always
|
|
// stored at the end.
|
|
blockSize -= (void*).sizeof;
|
|
}
|
|
}
|