diff options
author | Ian Lance Taylor <iant@golang.org> | 2022-07-27 10:15:41 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2022-07-27 10:15:41 -0700 |
commit | 9f62ed218fa656607740b386c0caa03e65dcd283 (patch) | |
tree | 6bde49bc5e4c4241266b108e4277baef4b85535d /libphobos/libdruntime/rt | |
parent | 71e955da39cea0ebffcfee3432effa622d14ca99 (diff) | |
parent | 5eb9f117a361538834b9740d59219911680717d1 (diff) | |
download | gcc-9f62ed218fa656607740b386c0caa03e65dcd283.zip gcc-9f62ed218fa656607740b386c0caa03e65dcd283.tar.gz gcc-9f62ed218fa656607740b386c0caa03e65dcd283.tar.bz2 |
Merge from trunk revision 5eb9f117a361538834b9740d59219911680717d1.
Diffstat (limited to 'libphobos/libdruntime/rt')
-rw-r--r-- | libphobos/libdruntime/rt/aApply.d | 108 | ||||
-rw-r--r-- | libphobos/libdruntime/rt/aApplyR.d | 71 | ||||
-rw-r--r-- | libphobos/libdruntime/rt/aaA.d | 43 | ||||
-rw-r--r-- | libphobos/libdruntime/rt/arrayassign.d | 155 | ||||
-rw-r--r-- | libphobos/libdruntime/rt/critical_.d | 4 | ||||
-rw-r--r-- | libphobos/libdruntime/rt/dmain2.d | 18 | ||||
-rw-r--r-- | libphobos/libdruntime/rt/ehalloc.d | 45 | ||||
-rw-r--r-- | libphobos/libdruntime/rt/lifetime.d | 438 | ||||
-rw-r--r-- | libphobos/libdruntime/rt/minfo.d | 216 | ||||
-rw-r--r-- | libphobos/libdruntime/rt/monitor_.d | 6 | ||||
-rw-r--r-- | libphobos/libdruntime/rt/util/typeinfo.d | 289 |
11 files changed, 638 insertions, 755 deletions
diff --git a/libphobos/libdruntime/rt/aApply.d b/libphobos/libdruntime/rt/aApply.d index bea441f..5d5ddb3 100644 --- a/libphobos/libdruntime/rt/aApply.d +++ b/libphobos/libdruntime/rt/aApply.d @@ -1,7 +1,5 @@ /** - * This code handles decoding UTF strings for foreach loops. There are 6 - * combinations of conversions between char, wchar, and dchar, and 2 of each - * of those. + * This code handles decoding UTF strings for foreach loops. * * Copyright: Copyright Digital Mars 2004 - 2010. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). @@ -15,9 +13,64 @@ import core.internal.utf : decode, toUTF8; /**********************************************/ /* 1 argument versions */ -// dg is D, but _aApplycd() is C -extern (D) alias int delegate(void *) dg_t; +/** +Delegate type corresponding to transformed loop body + +The parameter is a pointer to the current `char`, `wchar` or `dchar` + +Returns: non-zero when a `break` statement is hit +*/ +extern (D) alias dg_t = int delegate(void* c); + +// Note: dg is extern(D), but _aApplycd() is extern(C) + +/** +Loop over a string while changing the UTF encoding + +There are 6 combinations of conversions between `char`, `wchar`, and `dchar`, +and 2 of each of those. + +The naming convention is as follows: + +_aApply{c,d,w}{c,d,w}{1,2} +The first letter corresponds to the input string encoding, and the second letter corresponds to the target character type. + +- c = `char` +- w = `wchar` +- d = `dchar` + +The `1` variant only produces the character, the `2` variant also produces a loop index. + +Examples: +--- +void main() +{ + string str; + wtring wstr; + dstring dstr; + + foreach (dchar c; str) {} + // _aApplycd1 + + foreach (wchar c; dstr) {} + // _aApplydw1 + + foreach (i, wchar c; str) {} + // _aApplycw2 + + foreach (wchar w; wstr) {} + // no conversion +} +--- + +Params: + aa = input string + dg = foreach body transformed into a delegate, similar to `opApply` + +Returns: + non-zero when the loop was exited through a `break` +*/ extern (C) int _aApplycd1(in char[] aa, dg_t dg) { int result; @@ -78,8 +131,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplywd1(in wchar[] aa, dg_t dg) { int result; @@ -140,8 +192,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplycw1(in char[] aa, dg_t dg) { int result; @@ -215,8 +266,7 @@ unittest assert(i == 5); } -/*****************************/ - +/// ditto extern (C) int _aApplywc1(in wchar[] aa, dg_t dg) { int result; @@ -296,8 +346,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplydc1(in dchar[] aa, dg_t dg) { int result; @@ -373,8 +422,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplydw1(in dchar[] aa, dg_t dg) { int result; @@ -446,9 +494,20 @@ unittest /****************************************************************************/ /* 2 argument versions */ -// dg is D, but _aApplycd2() is C -extern (D) alias int delegate(void *, void *) dg2_t; +/** +Delegate type corresponding to transformed loop body + +Parameters are pointers to a `size_t` loop index, and the current `char`, `wchar` or `dchar`. +Returns: non-zero when a `break` statement is hit +*/ +extern (D) alias dg2_t = int delegate(void* i, void* c); + +// Note: dg is extern(D), but _aApplycd2() is extern(C) + +/** +Variants of _aApplyXXX that include a loop index. +*/ extern (C) int _aApplycd2(in char[] aa, dg2_t dg) { int result; @@ -516,8 +575,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplywd2(in wchar[] aa, dg2_t dg) { int result; @@ -585,8 +643,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplycw2(in char[] aa, dg2_t dg) { int result; @@ -665,8 +722,7 @@ unittest assert(i == 5); } -/*****************************/ - +/// ditto extern (C) int _aApplywc2(in wchar[] aa, dg2_t dg) { int result; @@ -751,8 +807,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplydc2(in dchar[] aa, dg2_t dg) { int result; @@ -832,8 +887,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplydw2(in dchar[] aa, dg2_t dg) { int result; diff --git a/libphobos/libdruntime/rt/aApplyR.d b/libphobos/libdruntime/rt/aApplyR.d index 7f19fa8..ce3bb9e 100644 --- a/libphobos/libdruntime/rt/aApplyR.d +++ b/libphobos/libdruntime/rt/aApplyR.d @@ -1,7 +1,5 @@ /** - * This code handles decoding UTF strings for foreach_reverse loops. There are - * 6 combinations of conversions between char, wchar, and dchar, and 2 of each - * of those. + * This code handles decoding UTF strings for `foreach_reverse` loops. * * Copyright: Copyright Digital Mars 2004 - 2010. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). @@ -15,9 +13,27 @@ import core.internal.utf; /**********************************************/ /* 1 argument versions */ -// dg is D, but _aApplyRcd() is C -extern (D) alias int delegate(void *) dg_t; +// Note: dg is extern(D), but _aApplyRcd() is extern(C) +/** +Delegate type corresponding to transformed loop body + +The parameter is a pointer to the current `char`, `wchar` or `dchar` + +Returns: non-zero when a `break` statement is hit +*/ +extern (D) alias dg_t = int delegate(void* c); + +/** +Same as `_aApplyXXX` functions, but for `foreach_reverse` + +Params: + aa = input string + dg = foreach body transformed into a delegate, similar to `opApply` + +Returns: + non-zero when the loop was exited through a `break` +*/ extern (C) int _aApplyRcd1(in char[] aa, dg_t dg) { int result; @@ -90,8 +106,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplyRwd1(in wchar[] aa, dg_t dg) { int result; @@ -154,8 +169,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplyRcw1(in char[] aa, dg_t dg) { int result; @@ -241,8 +255,7 @@ unittest assert(i == 5); } -/*****************************/ - +/// ditto extern (C) int _aApplyRwc1(in wchar[] aa, dg_t dg) { int result; @@ -326,8 +339,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplyRdc1(in dchar[] aa, dg_t dg) { int result; @@ -405,8 +417,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplyRdw1(in dchar[] aa, dg_t dg) { int result; @@ -477,9 +488,20 @@ unittest /****************************************************************************/ /* 2 argument versions */ -// dg is D, but _aApplyRcd2() is C -extern (D) alias int delegate(void *, void *) dg2_t; +/** +Delegate type corresponding to transformed loop body + +Parameters are pointers to a `size_t` loop index, and the current `char`, `wchar` or `dchar`. +Returns: non-zero when a `break` statement is hit +*/ +extern (D) alias dg2_t = int delegate(void* i, void* c); + +// Note: dg is extern(D), but _aApplyRcd2() is extern(C) + +/** +Variants of _aApplyRXXX that include a loop index. +*/ extern (C) int _aApplyRcd2(in char[] aa, dg2_t dg) { int result; size_t i; @@ -555,8 +577,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplyRwd2(in wchar[] aa, dg2_t dg) { int result; @@ -621,8 +642,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplyRcw2(in char[] aa, dg2_t dg) { int result; @@ -710,8 +730,7 @@ unittest assert(i == 5); } -/*****************************/ - +/// ditto extern (C) int _aApplyRwc2(in wchar[] aa, dg2_t dg) { int result; @@ -797,8 +816,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplyRdc2(in dchar[] aa, dg2_t dg) { int result; @@ -877,8 +895,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplyRdw2(in dchar[] aa, dg2_t dg) { int result; diff --git a/libphobos/libdruntime/rt/aaA.d b/libphobos/libdruntime/rt/aaA.d index 0c38622..f264b01 100644 --- a/libphobos/libdruntime/rt/aaA.d +++ b/libphobos/libdruntime/rt/aaA.d @@ -50,7 +50,7 @@ struct AA private struct Impl { private: - this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS) + this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS) nothrow { keysz = cast(uint) ti.key.tsize; valsz = cast(uint) ti.value.tsize; @@ -125,7 +125,7 @@ private: } } - void grow(scope const TypeInfo keyti) + void grow(scope const TypeInfo keyti) pure nothrow { // If there are so many deleted entries, that growing would push us // below the shrink threshold, we just purge deleted entries instead. @@ -135,7 +135,7 @@ private: resize(GROW_FAC * dim); } - void shrink(scope const TypeInfo keyti) + void shrink(scope const TypeInfo keyti) pure nothrow { if (dim > INIT_NUM_BUCKETS) resize(dim / GROW_FAC); @@ -233,7 +233,7 @@ package void entryDtor(void* p, const TypeInfo_Struct sti) extra[1].destroy(p + talign(extra[0].tsize, extra[1].talign)); } -private bool hasDtor(const TypeInfo ti) +private bool hasDtor(const TypeInfo ti) pure nothrow { import rt.lifetime : unqualify; @@ -246,7 +246,7 @@ private bool hasDtor(const TypeInfo ti) return false; } -private immutable(void)* getRTInfo(const TypeInfo ti) +private immutable(void)* getRTInfo(const TypeInfo ti) pure nothrow { // classes are references const isNoClass = ti && typeid(ti) !is typeid(TypeInfo_Class); @@ -254,7 +254,7 @@ private immutable(void)* getRTInfo(const TypeInfo ti) } // build type info for Entry with additional key and value fields -TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti) +TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti) nothrow { import rt.lifetime : unqualify; @@ -319,7 +319,8 @@ TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo va } // build appropriate RTInfo at runtime -immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo, immutable(size_t)* valinfo, size_t* rtinfoData, size_t rtinfoSize) +immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo, + immutable(size_t)* valinfo, size_t* rtinfoData, size_t rtinfoSize) pure nothrow { enum bitsPerWord = 8 * size_t.sizeof; @@ -456,7 +457,7 @@ private size_t mix(size_t h) @safe pure nothrow @nogc return h; } -private size_t calcHash(scope const void* pkey, scope const TypeInfo keyti) +private size_t calcHash(scope const void* pkey, scope const TypeInfo keyti) nothrow { immutable hash = keyti.getHash(pkey); // highest bit is set to distinguish empty/deleted from filled buckets @@ -485,6 +486,18 @@ pure nothrow @nogc unittest // API Implementation //------------------------------------------------------------------------------ +/** Allocate associative array data. + * Called for `new SomeAA` expression. + * Params: + * ti = TypeInfo for the associative array + * Returns: + * A new associative array. + */ +extern (C) Impl* _aaNew(const TypeInfo_AssociativeArray ti) +{ + return new Impl(ti); +} + /// Determine number of entries in associative array. extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc { @@ -504,7 +517,7 @@ extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc * If key was not in the aa, a mutable pointer to newly inserted value which * is set to all zeros */ -extern (C) void* _aaGetY(AA* paa, const TypeInfo_AssociativeArray ti, +extern (C) void* _aaGetY(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, scope const void* pkey) { bool found; @@ -525,7 +538,7 @@ extern (C) void* _aaGetY(AA* paa, const TypeInfo_AssociativeArray ti, * If key was not in the aa, a mutable pointer to newly inserted value which * is set to all zeros */ -extern (C) void* _aaGetX(AA* paa, const TypeInfo_AssociativeArray ti, +extern (C) void* _aaGetX(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, scope const void* pkey, out bool found) { // lazily alloc implementation @@ -736,7 +749,15 @@ extern (C) int _aaApply2(AA aa, const size_t keysz, dg2_t dg) return 0; } -/// Construct an associative array of type ti from keys and value +/** Construct an associative array of type ti from corresponding keys and values. + * Called for an AA literal `[k1:v1, k2:v2]`. + * Params: + * ti = TypeInfo for the associative array + * keys = array of keys + * vals = array of values + * Returns: + * A new associative array opaque pointer, or null if `keys` is empty. + */ extern (C) Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] vals) { diff --git a/libphobos/libdruntime/rt/arrayassign.d b/libphobos/libdruntime/rt/arrayassign.d index 21d50b0..c9db2fc 100644 --- a/libphobos/libdruntime/rt/arrayassign.d +++ b/libphobos/libdruntime/rt/arrayassign.d @@ -19,8 +19,10 @@ private debug(PRINTF) import core.stdc.stdio; } -/** - * Keep for backward binary compatibility. This function can be removed in the future. +/* + * Superseded array assignment hook. Does not take into account destructors: + * https://issues.dlang.org/show_bug.cgi?id=13661 + * Kept for backward binary compatibility. This function can be removed in the future. */ extern (C) void[] _d_arrayassign(TypeInfo ti, void[] from, void[] to) { @@ -40,15 +42,44 @@ extern (C) void[] _d_arrayassign(TypeInfo ti, void[] from, void[] to) } /** - * Does array assignment (not construction) from another - * lvalue array of the same element type. - * Handles overlapping copies. - * Input: - * ti TypeInfo of the element type. - * dst Points target memory. Its .length is equal to the element count, not byte length. - * src Points source memory. Its .length is equal to the element count, not byte length. - * ptmp Temporary memory for element swapping. - */ +Does array assignment (not construction) from another array of the same +element type. + +Handles overlapping copies. + +The `_d_arrayassign_l` variant assumes the right hand side is an lvalue, +while `_d_arrayassign_r` assumes it's an rvalue, which means it doesn't have to call copy constructors. + +Used for static array assignment with non-POD element types: +--- +struct S +{ + ~this() {} // destructor, so not Plain Old Data +} + +void main() +{ + S[3] arr; + S[3] lvalue; + + arr = lvalue; + // Generates: + // S _tmp; + // _d_arrayassign_l(typeid(S), (cast(void*) lvalue.ptr)[0..lvalue.length], (cast(void*) arr.ptr)[0..arr.length], &_tmp); + + S[3] getRvalue() {return lvalue;} + arr = getRvalue(); + // Similar, but `_d_arrayassign_r` +} +--- + +Params: + ti = `TypeInfo` of the array element type. + dst = target memory. Its `.length` is equal to the element count, not byte length. + src = source memory. Its `.length` is equal to the element count, not byte length. + ptmp = Temporary memory for element swapping, must have capacity of `ti.tsize` bytes. +Returns: `dst` +*/ extern (C) void[] _d_arrayassign_l(TypeInfo ti, void[] src, void[] dst, void* ptmp) { debug(PRINTF) printf("_d_arrayassign_l(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize); @@ -131,16 +162,7 @@ unittest // Bugzilla 14024 assert(op == "YzXy", op); } -/** - * Does array assignment (not construction) from another - * rvalue array of the same element type. - * Input: - * ti TypeInfo of the element type. - * dst Points target memory. Its .length is equal to the element count, not byte length. - * src Points source memory. Its .length is equal to the element count, not byte length. - * It is always allocated on stack and never overlapping with dst. - * ptmp Temporary memory for element swapping. - */ +/// ditto extern (C) void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* ptmp) { debug(PRINTF) printf("_d_arrayassign_r(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize); @@ -163,48 +185,22 @@ extern (C) void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* pt } /** - * Does array initialization (not assignment) from another - * array of the same element type. - * ti is the element type. - */ -extern (C) void[] _d_arrayctor(TypeInfo ti, void[] from, void[] to) -{ - debug(PRINTF) printf("_d_arrayctor(from = %p,%d, to = %p,%d) size = %d\n", from.ptr, from.length, to.ptr, to.length, ti.tsize); - - - auto element_size = ti.tsize; - - enforceRawArraysConformable("initialization", element_size, from, to); - - size_t i; - try - { - for (i = 0; i < to.length; i++) - { - // Copy construction is defined as bit copy followed by postblit. - memcpy(to.ptr + i * element_size, from.ptr + i * element_size, element_size); - ti.postblit(to.ptr + i * element_size); - } - } - catch (Throwable o) - { - /* Destroy, in reverse order, what we've constructed so far - */ - while (i--) - { - ti.destroy(to.ptr + i * element_size); - } - - throw o; - } - return to; -} - - -/** - * Do assignment to an array. - * p[0 .. count] = value; - */ +Set all elements of an array to a single value. + +--- +p[0 .. count] = value; +--- + +Takes into account postblits and destructors, for Plain Old Data elements, +`rt/memset.d` is used. + +Params: + p = pointer to start of array + value = bytes of the element to set. Size is derived from `ti`. + count = amount of array elements to set + ti = type info of the array element type / `value` +Returns: `p` +*/ extern (C) void* _d_arraysetassign(void* p, void* value, int count, TypeInfo ti) { void* pstart = p; @@ -227,36 +223,3 @@ extern (C) void* _d_arraysetassign(void* p, void* value, int count, TypeInfo ti) free(ptmp); return pstart; } - -/** - * Do construction of an array. - * ti[count] p = value; - */ -extern (C) void* _d_arraysetctor(void* p, void* value, int count, TypeInfo ti) -{ - void* pstart = p; - auto element_size = ti.tsize; - - try - { - foreach (i; 0 .. count) - { - // Copy construction is defined as bit copy followed by postblit. - memcpy(p, value, element_size); - ti.postblit(p); - p += element_size; - } - } - catch (Throwable o) - { - // Destroy, in reverse order, what we've constructed so far - while (p > pstart) - { - p -= element_size; - ti.destroy(p); - } - - throw o; - } - return pstart; -} diff --git a/libphobos/libdruntime/rt/critical_.d b/libphobos/libdruntime/rt/critical_.d index ae18122..36552a3 100644 --- a/libphobos/libdruntime/rt/critical_.d +++ b/libphobos/libdruntime/rt/critical_.d @@ -18,13 +18,13 @@ nothrow: import rt.monitor_, core.atomic; -extern (C) void _d_critical_init() +extern (C) void _d_critical_init() @nogc nothrow { initMutex(cast(Mutex*)&gcs.mtx); head = &gcs; } -extern (C) void _d_critical_term() +extern (C) void _d_critical_term() @nogc nothrow { // This function is only ever called by the runtime shutdown code // and therefore is single threaded so the following cast is fine. diff --git a/libphobos/libdruntime/rt/dmain2.d b/libphobos/libdruntime/rt/dmain2.d index 47b67f1..0739b74 100644 --- a/libphobos/libdruntime/rt/dmain2.d +++ b/libphobos/libdruntime/rt/dmain2.d @@ -62,22 +62,21 @@ struct UnitTestResult bool summarize; } -extern (C) void _d_monitor_staticctor(); -extern (C) void _d_monitor_staticdtor(); -extern (C) void _d_critical_init(); -extern (C) void _d_critical_term(); +extern (C) void _d_monitor_staticctor() @nogc nothrow; +extern (C) void _d_monitor_staticdtor() @nogc nothrow; +extern (C) void _d_critical_init() @nogc nothrow; +extern (C) void _d_critical_term() @nogc nothrow; extern (C) void gc_init(); extern (C) void gc_term(); -extern (C) void thread_init() @nogc; -extern (C) void thread_term() @nogc; -extern (C) void lifetime_init(); +extern (C) void thread_init() @nogc nothrow; +extern (C) void thread_term() @nogc nothrow; extern (C) void rt_moduleCtor(); extern (C) void rt_moduleTlsCtor(); extern (C) void rt_moduleDtor(); extern (C) void rt_moduleTlsDtor(); extern (C) void thread_joinAll(); extern (C) UnitTestResult runModuleUnitTests(); -extern (C) void _d_initMonoTime(); +extern (C) void _d_initMonoTime() @nogc nothrow; version (CRuntime_Microsoft) { @@ -134,7 +133,6 @@ extern (C) int rt_init() thread_init(); // TODO: fixme - calls GC.addRange -> Initializes GC initStaticDataGC(); - lifetime_init(); rt_moduleCtor(); rt_moduleTlsCtor(); return 1; @@ -669,7 +667,7 @@ extern (C) void _d_print_throwable(Throwable t) void sink(in char[] buf) scope nothrow { - fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr); + fwrite(buf.ptr, char.sizeof, buf.length, stderr); } formatThrowable(t, &sink); } diff --git a/libphobos/libdruntime/rt/ehalloc.d b/libphobos/libdruntime/rt/ehalloc.d index 1dcd69a..65f92e3 100644 --- a/libphobos/libdruntime/rt/ehalloc.d +++ b/libphobos/libdruntime/rt/ehalloc.d @@ -18,51 +18,6 @@ debug(PRINTF) import core.stdc.stdio; } -/********************************************** - * Allocate an exception of type `ci` from the exception pool. - * It has the same interface as `rt.lifetime._d_newclass()`. - * The class type must be Throwable or derived from it, - * and cannot be a COM or C++ class. The compiler must enforce - * this. - * Returns: - * default initialized instance of the type - */ - -extern (C) Throwable _d_newThrowable(const TypeInfo_Class ci) -{ - debug(PRINTF) printf("_d_newThrowable(ci = %p, %s)\n", ci, cast(char *)ci.name); - - assert(!(ci.m_flags & TypeInfo_Class.ClassFlags.isCOMclass)); - assert(!(ci.m_flags & TypeInfo_Class.ClassFlags.isCPPclass)); - - import core.stdc.stdlib : malloc; - auto init = ci.initializer; - void* p = malloc(init.length); - if (!p) - { - import core.exception : onOutOfMemoryError; - onOutOfMemoryError(); - } - - debug(PRINTF) printf(" p = %p\n", p); - - // initialize it - p[0 .. init.length] = init[]; - - if (!(ci.m_flags & TypeInfo_Class.ClassFlags.noPointers)) - { - // Inform the GC about the pointers in the object instance - import core.memory : GC; - - GC.addRange(p, init.length, ci); - } - - debug(PRINTF) printf("initialization done\n"); - Throwable t = cast(Throwable)p; - t.refcount() = 1; - return t; -} - /******************************************** * Delete exception instance `t` from the exception pool. diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d index 1f7a81d..026001f 100644 --- a/libphobos/libdruntime/rt/lifetime.d +++ b/libphobos/libdruntime/rt/lifetime.d @@ -40,22 +40,47 @@ private } } -extern (C) void lifetime_init() +// Now-removed symbol, kept around for ABI +// Some programs are dynamically linked, so best to err on the side of keeping symbols around for a while (especially extern(C) ones) +// https://github.com/dlang/druntime/pull/3361 +deprecated extern (C) void lifetime_init() { - // this is run before static ctors, so it is safe to modify immutables } /** - * - */ +Allocate memory using the garbage collector + +DMD uses this to allocate closures: +--- +void f(byte[24] x) +{ + return () => x; // `x` is on stack, must be moved to heap to keep it alive +} +--- + +Params: + sz = number of bytes to allocate + +Returns: pointer to `sz` bytes of free, uninitialized memory, managed by the GC. +*/ extern (C) void* _d_allocmemory(size_t sz) @weak { return GC.malloc(sz); } /** - * - */ +Create a new class instance. + +Allocates memory and sets fields to their initial value, but does not call a constructor. + +--- +new Object() // _d_newclass(typeid(Object)) +--- +Params: + ci = `TypeInfo_Class` object, to provide instance size and initial bytes to copy + +Returns: newly created object +*/ extern (C) Object _d_newclass(const ClassInfo ci) @weak { import core.stdc.stdlib; @@ -163,23 +188,6 @@ extern (C) void _d_delclass(Object* p) @weak } } -/** - * This is called for a delete statement where the value - * being deleted is a pointer to a struct with a destructor - * but doesn't have an overloaded delete operator. - */ -extern (C) void _d_delstruct(void** p, TypeInfo_Struct inf) @weak -{ - if (*p) - { - debug(PRINTF) printf("_d_delstruct(%p, %p)\n", *p, cast(void*)inf); - - inf.destroy(*p); - GC.free(*p); - *p = null; - } -} - // strip const/immutable/shared/inout from type info inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc { @@ -367,7 +375,7 @@ bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, co /** get the allocation size of the array for the given block (without padding or type info) */ -size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow +private size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow { if (info.size <= 256) return *cast(ubyte *)(info.base + info.size - structTypeInfoSize(tinext) - SMALLPAD); @@ -381,7 +389,7 @@ size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow /** get the start of the array for the given block */ -void *__arrayStart(return scope BlkInfo info) nothrow pure +private void *__arrayStart(return scope BlkInfo info) nothrow pure { return info.base + ((info.size & BIGLENGTHMASK) ? LARGEPREFIX : 0); } @@ -391,7 +399,7 @@ void *__arrayStart(return scope BlkInfo info) nothrow pure 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 +private size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted { return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext)); } @@ -416,7 +424,7 @@ private void __arrayClearPad(ref BlkInfo info, size_t arrsize, size_t padsize) n allocate an array memory block by applying the proper padding and assigning block attributes if not inherited from the existing block */ -BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure +private BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure { import core.checkedint; @@ -438,7 +446,7 @@ BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tin return bi; } -BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext) +private BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext) { import core.checkedint; @@ -461,7 +469,7 @@ BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, /** cache for the lookup of the block info */ -enum N_CACHE_BLOCKS=8; +private enum N_CACHE_BLOCKS=8; // note this is TLS, so no need to sync. BlkInfo *__blkcache_storage; @@ -659,10 +667,15 @@ void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow } /** - * Shrink the "allocated" length of an array to be the exact size of the array. - * It doesn't matter what the current allocated length of the array is, the - * user is telling the runtime that he knows what he is doing. - */ +Shrink the "allocated" length of an array to be the exact size of the array. + +It doesn't matter what the current allocated length of the array is, the +user is telling the runtime that he knows what he is doing. + +Params: + ti = `TypeInfo` of array type + arr = array to shrink. Its `.length` is element length, not byte length, despite `void` type +*/ extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) /+nothrow+/ { // note, we do not care about shared. We are setting the length no matter @@ -705,7 +718,7 @@ extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) /+nothrow+/ } } -package bool hasPostblit(in TypeInfo ti) +package bool hasPostblit(in TypeInfo ti) nothrow pure { return (&ti.postblit).funcptr !is &TypeInfo.postblit; } @@ -741,12 +754,21 @@ void __doPostblit(void *ptr, size_t len, const TypeInfo ti) /** - * set the array capacity. If the array capacity isn't currently large enough - * to hold the requested capacity (in number of elements), then the array is - * resized/reallocated to the appropriate size. Pass in a requested capacity - * of 0 to get the current capacity. Returns the number of elements that can - * actually be stored once the resizing is done. - */ +Set the array capacity. + +If the array capacity isn't currently large enough +to hold the requested capacity (in number of elements), then the array is +resized/reallocated to the appropriate size. + +Pass in a requested capacity of 0 to get the current capacity. + +Params: + ti = type info of element type + newcapacity = requested new capacity + p = pointer to array to set. Its `length` is left unchanged. + +Returns: the number of elements that can actually be stored once the resizing is done +*/ extern(C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* p) @weak in { @@ -917,9 +939,18 @@ Lcontinue: } /** - * Allocate a new uninitialized array of length elements. - * ti is the type of the resulting array, or pointer to element. - */ +Allocate an array with the garbage collector. + +Has three variants: +- `_d_newarrayU` leave elements uninitialized +- `_d_newarrayT` initializes to 0 (e.g `new int[]`) +- `_d_newarrayiT` initializes based on initializer retrieved from TypeInfo (e.g `new float[]`) + +Params: + ti = the type of the resulting array, (may also be the corresponding `array.ptr` type) + length = `.length` of resulting array +Returns: newly allocated array +*/ extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow @weak { import core.exception : onOutOfMemoryError; @@ -976,11 +1007,7 @@ Lcontinue: return arrstart[0..length]; } -/** - * 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) - */ +/// ditto extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow @weak { import core.stdc.string; @@ -993,9 +1020,7 @@ extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow @w return result; } -/** - * For when the array has a non-zero initializer. - */ +/// ditto extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @weak { import core.internal.traits : AliasSeq; @@ -1011,8 +1036,12 @@ extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @ foreach (T; AliasSeq!(ubyte, ushort, uint, ulong)) { case T.sizeof: - (cast(T*)result.ptr)[0 .. size * length / T.sizeof] = *cast(T*)init.ptr; - return result; + if (tinext.talign % T.alignof == 0) + { + (cast(T*)result.ptr)[0 .. size * length / T.sizeof] = *cast(T*)init.ptr; + return result; + } + goto default; } default: @@ -1027,10 +1056,10 @@ extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @ } -/** - * +/* + * Helper for creating multi-dimensional arrays */ -void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims) +private void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims) { debug(PRINTF) printf("_d_newarrayOpT(ndims = %d)\n", dims.length); if (dims.length == 0) @@ -1069,8 +1098,30 @@ void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims) /** - * - */ +Create a new multi-dimensional array + +Has two variants: +- `_d_newarraymTX` which initializes to 0 +- `_d_newarraymiTX` which initializes elements based on `TypeInfo` + +--- +void main() +{ + new int[][](10, 20); + // _d_newarraymTX(typeid(float), [10, 20]); + + new float[][][](10, 20, 30); + // _d_newarraymiTX(typeid(float), [10, 20, 30]); +} +--- + +Params: + ti = `TypeInfo` of the array type + dims = array length values for each dimension + +Returns: + newly allocated array +*/ extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) @weak { debug(PRINTF) printf("_d_newarraymT(dims.length = %d)\n", dims.length); @@ -1083,10 +1134,7 @@ extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) @weak } } - -/** - * - */ +/// ditto extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) @weak { debug(PRINTF) printf("_d_newarraymiT(dims.length = %d)\n", dims.length); @@ -1100,9 +1148,31 @@ extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) @weak } /** - * Allocate an uninitialized non-array item. - * This is an optimization to avoid things needed for arrays like the __arrayPad(size). - */ +Allocate an uninitialized non-array item. + +This is an optimization to avoid things needed for arrays like the __arrayPad(size). + +- `_d_newitemU` leaves the item uninitialized +- `_d_newitemT` zero initializes the item +- `_d_newitemiT` uses a non-zero initializer from `TypeInfo` + +Used to allocate struct instances on the heap. +--- +struct Sz {int x = 0;} +struct Si {int x = 3;} + +void main() +{ + new Sz(); // _d_newitemT(typeid(Sz)) + new Si(); // _d_newitemiT(typeid(Si)) +} +--- + +Params: + _ti = `TypeInfo` of item to allocate +Returns: + newly allocated item +*/ extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak { auto ti = unqualify(_ti); @@ -1118,14 +1188,15 @@ extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak if (tiSize) { - *cast(TypeInfo*)(p + itemSize) = null; // the GC might not have cleared this area + // the GC might not have cleared the padding area in the block + *cast(TypeInfo*)(p + (itemSize & ~(size_t.sizeof - 1))) = null; *cast(TypeInfo*)(p + blkInf.size - tiSize) = cast() ti; } return p; } -/// Same as above, zero initializes the item. +/// ditto extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak { import core.stdc.string; @@ -1145,15 +1216,6 @@ extern (C) void* _d_newitemiT(in TypeInfo _ti) pure nothrow @weak return p; } -/** - * - */ -struct Array -{ - size_t length; - byte* data; -} - debug(PRINTF) { extern(C) void printArrayCache() @@ -1436,6 +1498,7 @@ extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true) } } +/// Backwards compatibility extern (C) void rt_finalize(void* p, bool det = true) nothrow { rt_finalize2(p, det, true); @@ -1454,8 +1517,29 @@ extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow /** - * Resize dynamic arrays with 0 initializers. - */ +Resize a dynamic array by setting the `.length` property + +Newly created elements are initialized to their default value. + +Has two variants: +- `_d_arraysetlengthT` for arrays with elements that initialize to 0 +- `_d_arraysetlengthiT` for non-zero initializers retrieved from `TypeInfo` + +--- +void main() +{ + int[] a = [1, 2]; + a.length = 3; // gets lowered to `_d_arraysetlengthT(typeid(int[]), 3, &a)` +} +--- + +Params: + ti = `TypeInfo` of array + newlength = new value for the array's `.length` + p = pointer to array to update the `.length` of. + While it's cast to `void[]`, its `.length` is still treated as element length. +Returns: `*p` after being updated +*/ extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p) @weak in { @@ -1649,15 +1733,7 @@ do return *p; } - -/** - * 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 - */ +/// ditto extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) @weak in { @@ -1867,27 +1943,33 @@ do return *p; } -/** - * Append y[] to array x[] - */ -extern (C) void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y) @weak -{ - import core.stdc.string; - auto length = x.length; - auto tinext = unqualify(ti.next); - auto sizeelem = tinext.tsize; // array element size - _d_arrayappendcTX(ti, x, y.length); - memcpy(x.ptr + length * sizeelem, y.ptr, y.length * sizeelem); - - // do postblit - __doPostblit(x.ptr + length * sizeelem, y.length * sizeelem, tinext); - return x; -} - /** - * - */ +Given an array of length `size` that needs to be expanded to `newlength`, +compute a new capacity. + +Better version by Dave Fladebo: +This uses an inverse logorithmic algorithm to pre-allocate a bit more +space for larger arrays. +- Arrays smaller than PAGESIZE bytes are left as-is, so for the most +common cases, memory allocation is 1 to 1. The small overhead added +doesn't affect 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. + +Params: + newlength = new `.length` + size = old `.length` +Returns: new capacity for array +*/ size_t newCapacity(size_t newlength, size_t size) { version (none) @@ -1896,24 +1978,6 @@ size_t newCapacity(size_t newlength, size_t 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 PAGESIZE bytes are left as-is, so for the most - * common cases, memory allocation is 1 to 1. The small overhead added - * doesn't affect 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; @@ -1967,10 +2031,17 @@ size_t newCapacity(size_t newlength, size_t size) } -/************************************** - * Extend an array by n elements. - * Caller must initialize those elements. - */ +/** +Extend an array by n elements. + +Caller must initialize those elements. + +Params: + ti = type info of array type (not element type) + px = array to append to, cast to `byte[]` while keeping the same `.length`. Will be updated. + n = number of elements to append +Returns: `px` after being appended to +*/ extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak { @@ -2074,8 +2145,21 @@ byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n /** - * Append dchar to char[] - */ +Append `dchar` to `char[]`, converting UTF-32 to UTF-8 + +--- +void main() +{ + char[] s; + s ~= 'α'; +} +--- + +Params: + x = array to append to cast to `byte[]`. Will be modified. + c = `dchar` to append +Returns: updated `x` cast to `void[]` +*/ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) @weak { // c could encode into from 1 to 4 characters @@ -2121,7 +2205,7 @@ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) @weak // Hack because _d_arrayappendT takes `x` as a reference auto xx = cast(shared(char)[])x; - object._d_arrayappendTImpl!(shared(char)[])._d_arrayappendT(xx, cast(shared(char)[])appendthis); + object._d_arrayappendT(xx, cast(shared(char)[])appendthis); x = cast(byte[])xx; return x; } @@ -2156,8 +2240,23 @@ unittest /** - * Append dchar to wchar[] - */ +Append `dchar` to `wchar[]`, converting UTF-32 to UTF-16 + +--- +void main() +{ + dchar x; + wchar[] s; + s ~= 'α'; +} +--- + +Params: + x = array to append to cast to `byte[]`. Will be modified. + c = `dchar` to append + +Returns: updated `x` cast to `void[]` +*/ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak { // c could encode into from 1 to 2 w characters @@ -2182,15 +2281,31 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak // auto xx = (cast(shared(wchar)*)x.ptr)[0 .. x.length]; - object._d_arrayappendTImpl!(shared(wchar)[])._d_arrayappendT(xx, cast(shared(wchar)[])appendthis); + object._d_arrayappendT(xx, cast(shared(wchar)[])appendthis); x = (cast(byte*)xx.ptr)[0 .. xx.length]; return x; } /** - * - */ +Concatenate two arrays into a new array + +--- +void main() +{ + int[] x = [10, 20, 30]; + int[] y = [40, 50]; + int[] c = x ~ y; // _d_arraycatT(typeid(int[]), (cast(byte*) x)[0..x.length], (cast(byte*) y)[0..y.length]); +} +--- + +Params: + ti = type that the two arrays share + x = left hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length + y = right hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length +Returns: + resulting concatenated array, with `.length` equal to new element length despite `byte` type +*/ extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) @weak out (result) { @@ -2255,8 +2370,27 @@ do /** - * - */ +Concatenate multiple arrays at once + +This is more efficient than repeatedly concatenating pairs of arrays because the total size is known in advance. + +``` +void main() +{ + int[] a, b, c; + int[] res = a ~ b ~ c; + // _d_arraycatnTX(typeid(int[]), + // [(cast(byte*)a.ptr)[0..a.length], (cast(byte*)b.ptr)[0..b.length], (cast(byte*)c.ptr)[0..c.length]]); +} +``` + +Params: + ti = type of arrays to concatenate and resulting array + arrs = array of arrays to concatenate, cast to `byte[]` while keeping `.length` the same + +Returns: + newly created concatenated array, `.length` equal to the total element length despite `void` type +*/ extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak { import core.stdc.string; @@ -2295,8 +2429,27 @@ extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak /** - * Allocate the array, rely on the caller to do the initialization of the array. - */ +Allocate an array literal + +Rely on the caller to do the initialization of the array. + +--- +int[] getArr() +{ + return [10, 20]; + // auto res = cast(int*) _d_arrayliteralTX(typeid(int[]), 2); + // res[0] = 10; + // res[1] = 20; + // return res[0..2]; +} +--- + +Params: + ti = `TypeInfo` of resulting array type + length = `.length` of array literal + +Returns: pointer to allocated array +*/ extern (C) void* _d_arrayliteralTX(const TypeInfo ti, size_t length) @weak { @@ -2602,11 +2755,6 @@ deprecated unittest } dtorCount = 0; - S1* s1 = new S1; - _d_delstruct(cast(void**)&s1, typeid(typeof(*s1))); // delete s1; - assert(dtorCount == 1); - - dtorCount = 0; S1[] arr1 = new S1[7]; _d_delarray_t(cast(void[]*)&arr1, typeid(typeof(arr1[0]))); // delete arr1; assert(dtorCount == 7); diff --git a/libphobos/libdruntime/rt/minfo.d b/libphobos/libdruntime/rt/minfo.d index 0d5cd22..7489150 100644 --- a/libphobos/libdruntime/rt/minfo.d +++ b/libphobos/libdruntime/rt/minfo.d @@ -58,7 +58,7 @@ struct ModuleGroup // target modules are involved in a cycle. // // The return value is malloc'd using C, so it must be freed after use. - private size_t[] genCyclePath(size_t srcidx, size_t targetidx, int[][] edges) + private size_t[] genCyclePath(size_t srcidx, size_t targetidx, int[][] edges) nothrow { import core.bitop : bt, btc, bts; @@ -109,7 +109,7 @@ struct ModuleGroup // release mode. if (distance[target] != curdist) { - throw new Error("internal error printing module cycle"); + assert(0, "internal error printing module cycle"); } // determine the path. This is tricky, because we have to @@ -162,14 +162,13 @@ struct ModuleGroup * Throws: * Exception if it fails. */ - void sortCtors(string cycleHandling) + void sortCtors(string cycleHandling) nothrow { import core.bitop : bts, btr, bt, BitRange; import core.internal.container.hashtab; enum OnCycle { - deprecate, abort, print, ignore @@ -180,7 +179,9 @@ struct ModuleGroup switch (cycleHandling) with(OnCycle) { case "deprecate": - onCycle = deprecate; + import core.stdc.stdio : fprintf, stderr; + // Option deprecated in 2.101, remove in 2.111 + fprintf(stderr, "`--DRT-oncycle=deprecate` is no longer supported, using `abort` instead\n"); break; case "abort": onCycle = abort; @@ -196,7 +197,7 @@ struct ModuleGroup break; default: // invalid cycle handling option. - throw new Error("DRT invalid cycle handling option: " ~ cycleHandling); + assert(0, "DRT invalid cycle handling option: " ~ cycleHandling); } debug (printModuleDependencies) @@ -280,7 +281,7 @@ struct ModuleGroup .free(edges.ptr); } - void buildCycleMessage(size_t sourceIdx, size_t cycleIdx, scope void delegate(string) sink) + void buildCycleMessage(size_t sourceIdx, size_t cycleIdx, scope void delegate(string) nothrow sink) { version (Windows) enum EOL = "\r\n"; @@ -312,7 +313,7 @@ struct ModuleGroup // // If a cycle is detected, returns the index of the module that completes the cycle. // Returns: true for success, false for a deprecated cycle error - bool findDeps(size_t idx, size_t* reachable) + bool findDeps(size_t idx, size_t* reachable) nothrow { static struct stackFrame { @@ -356,14 +357,6 @@ struct ModuleGroup // was already started, this is a cycle. final switch (onCycle) with(OnCycle) { - case deprecate: - // check with old algorithm - if (sortCtorsOld(edges)) - { - // unwind to print deprecation message. - return false; // deprecated cycle error - } - goto case abort; // fall through case abort: string errmsg = ""; @@ -418,7 +411,7 @@ struct ModuleGroup // ctor/dtors that must be dealt with. It recurses only when it finds // dependencies that also have static ctor/dtors. // Returns: true for success, false for a deprecated cycle error - bool processMod(size_t curidx) + bool processMod(size_t curidx) nothrow { immutable ModuleInfo* current = _modules[curidx]; @@ -461,7 +454,7 @@ struct ModuleGroup } // returns `false` if deprecated cycle error otherwise set `result`. - bool doSort(size_t relevantFlags, ref immutable(ModuleInfo)*[] result) + bool doSort(size_t relevantFlags, ref immutable(ModuleInfo)*[] result) nothrow { clearFlags(relevant); clearFlags(ctorstart); @@ -533,193 +526,6 @@ struct ModuleGroup sortCtors(rt_configOption("oncycle")); } - /****************************** - * This is the old ctor sorting algorithm that does not find all cycles. - * - * It is here to allow the deprecated behavior from the original algorithm - * until people have fixed their code. - * - * If no cycles are found, the _ctors and _tlsctors are replaced with the - * ones generated by this algorithm to preserve the old incorrect ordering - * behavior. - * - * Params: - * edges = The module edges as found in the `importedModules` member of - * each ModuleInfo. Generated in sortCtors. - * Returns: - * true if no cycle is found, false if one was. - */ - bool sortCtorsOld(int[][] edges) - { - immutable len = edges.length; - assert(len == _modules.length); - - static struct StackRec - { - @property int mod() - { - return _mods[_idx]; - } - - int[] _mods; - size_t _idx; - } - - auto stack = (cast(StackRec*).calloc(len, StackRec.sizeof))[0 .. len]; - // TODO: reuse GCBits by moving it to core.internal.container - immutable nwords = (len + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof); - auto ctorstart = cast(size_t*).malloc(nwords * size_t.sizeof); - auto ctordone = cast(size_t*).malloc(nwords * size_t.sizeof); - int[] initialEdges = (cast(int *)malloc(int.sizeof * len))[0 .. len]; - if (!stack.ptr || ctorstart is null || ctordone is null || !initialEdges.ptr) - assert(0); - scope (exit) - { - .free(stack.ptr); - .free(ctorstart); - .free(ctordone); - .free(initialEdges.ptr); - } - - // initialize the initial edges - foreach (i, ref v; initialEdges) - v = cast(int)i; - - bool sort(ref immutable(ModuleInfo)*[] ctors, uint mask) - { - import core.bitop; - - ctors = (cast(immutable(ModuleInfo)**).malloc(len * size_t.sizeof))[0 .. len]; - if (!ctors.ptr) - assert(0); - - // clean flags - memset(ctorstart, 0, nwords * size_t.sizeof); - memset(ctordone, 0, nwords * size_t.sizeof); - size_t stackidx = 0; - size_t cidx; - - int[] mods = initialEdges; - - size_t idx; - while (true) - { - while (idx < mods.length) - { - auto m = mods[idx]; - - if (bt(ctordone, m)) - { - // this module has already been processed, skip - ++idx; - continue; - } - else if (bt(ctorstart, m)) - { - /* Trace back to the begin of the cycle. - */ - bool ctorInCycle; - size_t start = stackidx; - while (start--) - { - auto sm = stack[start].mod; - if (sm == m) - break; - assert(sm >= 0); - if (bt(ctorstart, sm)) - ctorInCycle = true; - } - assert(stack[start].mod == m); - if (ctorInCycle) - { - return false; - } - else - { - /* This is also a cycle, but the import chain does not constrain - * the order of initialization, either because the imported - * modules have no ctors or the ctors are standalone. - */ - ++idx; - } - } - else - { - auto curmod = _modules[m]; - if (curmod.flags & mask) - { - if (curmod.flags & MIstandalone || !edges[m].length) - { // trivial ctor => sort in - ctors[cidx++] = curmod; - bts(ctordone, m); - } - else - { // non-trivial ctor => defer - bts(ctorstart, m); - } - } - else // no ctor => mark as visited - { - bts(ctordone, m); - } - - if (edges[m].length) - { - /* Internal runtime error, recursion exceeds number of modules. - */ - (stackidx < len) || assert(0); - - // recurse - stack[stackidx++] = StackRec(mods, idx); - idx = 0; - mods = edges[m]; - } - } - } - - if (stackidx) - { // pop old value from stack - --stackidx; - mods = stack[stackidx]._mods; - idx = stack[stackidx]._idx; - auto m = mods[idx++]; - if (bt(ctorstart, m) && !bts(ctordone, m)) - ctors[cidx++] = _modules[m]; - } - else // done - break; - } - // store final number and shrink array - ctors = (cast(immutable(ModuleInfo)**).realloc(ctors.ptr, cidx * size_t.sizeof))[0 .. cidx]; - return true; - } - - /* Do two passes: ctor/dtor, tlsctor/tlsdtor - */ - immutable(ModuleInfo)*[] _ctors2; - immutable(ModuleInfo)*[] _tlsctors2; - auto result = sort(_ctors2, MIctor | MIdtor) && sort(_tlsctors2, MItlsctor | MItlsdtor); - if (result) // no cycle - { - // fall back to original ordering as part of the deprecation. - if (_ctors.ptr) - .free(_ctors.ptr); - _ctors = _ctors2; - if (_tlsctors.ptr) - .free(_tlsctors.ptr); - _tlsctors = _tlsctors2; - } - else - { - // free any allocated memory that will be forgotten - if (_ctors2.ptr) - .free(_ctors2.ptr); - if (_tlsctors2.ptr) - .free(_tlsctors2.ptr); - } - return result; - } - void runCtors() { // run independent ctors diff --git a/libphobos/libdruntime/rt/monitor_.d b/libphobos/libdruntime/rt/monitor_.d index 0f1d0e9..c1f3f3c 100644 --- a/libphobos/libdruntime/rt/monitor_.d +++ b/libphobos/libdruntime/rt/monitor_.d @@ -54,7 +54,7 @@ extern (C) void _d_monitordelete(Object h, bool det) } // does not call dispose events, for internal use only -extern (C) void _d_monitordelete_nogc(Object h) @nogc +extern (C) void _d_monitordelete_nogc(Object h) @nogc nothrow { auto m = getMonitor(h); if (m is null) @@ -148,7 +148,7 @@ extern (C) void rt_detachDisposeEvent(Object h, DEvent e) nothrow: -extern (C) void _d_monitor_staticctor() +extern (C) void _d_monitor_staticctor() @nogc nothrow { version (Posix) { @@ -158,7 +158,7 @@ extern (C) void _d_monitor_staticctor() initMutex(&gmtx); } -extern (C) void _d_monitor_staticdtor() +extern (C) void _d_monitor_staticdtor() @nogc nothrow { destroyMutex(&gmtx); version (Posix) diff --git a/libphobos/libdruntime/rt/util/typeinfo.d b/libphobos/libdruntime/rt/util/typeinfo.d index 26c24c4..7b55693 100644 --- a/libphobos/libdruntime/rt/util/typeinfo.d +++ b/libphobos/libdruntime/rt/util/typeinfo.d @@ -1,8 +1,8 @@ /** - * This module contains utilities for TypeInfo implementation. + * A few predefined implementations for primitive types and arrays thereof. Also a couple of helpers. * * Copyright: Copyright Kenji Hara 2014-. - * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. + * License: <a href="https://boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. * Authors: Kenji Hara * Source: $(DRUNTIMESRC rt/util/_typeinfo.d) */ @@ -10,100 +10,74 @@ module rt.util.typeinfo; import rt.util.utility : d_cfloat, d_cdouble, d_creal, isComplex; static import core.internal.hash; -template Floating(T) -if (is(T == float) || is(T == double) || is(T == real)) +// Three-way compare for integrals: negative if `lhs < rhs`, positive if `lhs > rhs`, 0 otherwise. +pragma(inline, true) +private int cmp3(T)(const T lhs, const T rhs) +if (__traits(isIntegral, T)) { - pure nothrow @safe: - - bool equals(T f1, T f2) - { - return f1 == f2; - } - - int compare(T d1, T d2) - { - if (d1 != d1 || d2 != d2) // if either are NaN - { - if (d1 != d1) - { - if (d2 != d2) - return 0; - return -1; - } - return 1; - } - return (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1); - } + static if (T.sizeof < int.sizeof) + // Taking the difference will always fit in an int. + return int(lhs) - int(rhs); + else + return (lhs > rhs) - (lhs < rhs); +} - public alias hashOf = core.internal.hash.hashOf; +// Three-way compare for real fp types. NaN is smaller than all valid numbers. +// Code is small and fast, see https://godbolt.org/z/fzb877 +pragma(inline, true) +private int cmp3(T)(const T d1, const T d2) +if (is(T == float) || is(T == double) || is(T == real)) +{ + if (d2 != d2) + return d1 == d1; // 0 if both ar NaN, 1 if d1 is valid and d2 is NaN. + // If d1 is NaN, both comparisons are false so we get -1, as needed. + return (d1 > d2) - !(d1 >= d2); } -// @@@DEPRECATED_2.105@@@ -template Floating(T) +// Three-way compare for complex types. +pragma(inline, true) +private int cmp3(T)(const T f1, const T f2) if (isComplex!T) { - pure nothrow @safe: - - bool equals(T f1, T f2) - { - return f1.re == f2.re && f1.im == f2.im; - } - - int compare(T f1, T f2) - { - int result; - - if (f1.re < f2.re) - result = -1; - else if (f1.re > f2.re) - result = 1; - else if (f1.im < f2.im) - result = -1; - else if (f1.im > f2.im) - result = 1; - else - result = 0; + if (int result = cmp3(f1.re, f2.re)) return result; - } - - size_t hashOf(scope const T val) - { - return core.internal.hash.hashOf(val.re, core.internal.hash.hashOf(val.im)); - } + return cmp3(f1.im, f2.im); } -template Array(T) -if (is(T == float) || is(T == double) || is(T == real)) +unittest { - pure nothrow @safe: - - bool equals(T[] s1, T[] s2) - { - size_t len = s1.length; - if (len != s2.length) - return false; - for (size_t u = 0; u < len; u++) - { - if (!Floating!T.equals(s1[u], s2[u])) - return false; - } - return true; - } - - int compare(T[] s1, T[] s2) - { - size_t len = s1.length; - if (s2.length < len) - len = s2.length; - for (size_t u = 0; u < len; u++) - { - if (int c = Floating!T.compare(s1[u], s2[u])) - return c; - } - return (s1.length > s2.length) - (s1.length < s2.length); - } - - public alias hashOf = core.internal.hash.hashOf; + assert(cmp3(short.max, short.min) > 0); + assert(cmp3(42, 42) == 0); + assert(cmp3(int.max, int.min) > 0); + + double x, y; + assert(cmp3(x, y) == 0); + assert(cmp3(y, x) == 0); + x = 42; + assert(cmp3(x, y) > 0); + assert(cmp3(y, x) < 0); + y = 43; + assert(cmp3(x, y) < 0); + assert(cmp3(y, x) > 0); + y = 42; + assert(cmp3(x, y) == 0); + assert(cmp3(y, x) == 0); + + d_cdouble u, v; + assert(cmp3(u, v) == 0); + assert(cmp3(v, u) == 0); + u = d_cdouble(42, 42); + assert(cmp3(u, v) > 0); + assert(cmp3(v, u) < 0); + v = d_cdouble(43, 42); + assert(cmp3(u, v) < 0); + assert(cmp3(v, u) > 0); + v = d_cdouble(42, 43); + assert(cmp3(u, v) < 0); + assert(cmp3(v, u) > 0); + v = d_cdouble(42, 42); + assert(cmp3(u, v) == 0); + assert(cmp3(v, u) == 0); } // @@@DEPRECATED_2.105@@@ @@ -209,7 +183,7 @@ unittest }(); } -// Reduces to `T` if `cond` is `true` or `U` otherwise. +// Reduces to `T` if `cond` is `true` or `U` otherwise. Consider moving elsewhere if useful. private template Select(bool cond, T, U) { static if (cond) alias Select = T; @@ -238,57 +212,38 @@ if (T.sizeof == Base.sizeof && T.alignof == Base.alignof) static if (is(T == Base)) override size_t getHash(scope const void* p) { - static if (__traits(isFloating, T) || isComplex!T) - return Floating!T.hashOf(*cast(T*)p); - else - return hashOf(*cast(const T *)p); + return hashOf(*cast(const T *)p); } // `equals` is the same for `Base` and `T`, introduce it just once. static if (is(T == Base)) override bool equals(in void* p1, in void* p2) { - static if (__traits(isFloating, T) || isComplex!T) - return Floating!T.equals(*cast(T*)p1, *cast(T*)p2); - else - return *cast(T *)p1 == *cast(T *)p2; + return *cast(const T *)p1 == *cast(const T *)p2; } // `T` and `Base` may have different signedness, so this function is introduced conditionally. static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) override int compare(in void* p1, in void* p2) { - static if (__traits(isFloating, T) || isComplex!T) - { - return Floating!T.compare(*cast(T*)p1, *cast(T*)p2); - } - else static if (T.sizeof < int.sizeof) - { - // Taking the difference will always fit in an int. - return int(*cast(T *) p1) - int(*cast(T *) p2); - } - else - { - auto lhs = *cast(T *) p1, rhs = *cast(T *) p2; - return (lhs > rhs) - (lhs < rhs); - } + return cmp3(*cast(const T*) p1, *cast(const T*) p2); } static if (is(T == Base)) - override @property size_t tsize() nothrow pure + override @property size_t tsize() { return T.sizeof; } static if (is(T == Base)) - override @property size_t talign() nothrow pure + override @property size_t talign() { return T.alignof; } // Override initializer only if necessary. static if (is(T == Base) || T.init != Base.init) - override const(void)[] initializer() @trusted + override const(void)[] initializer() { static if (__traits(isZeroInit, T)) { @@ -311,7 +266,7 @@ if (T.sizeof == Base.sizeof && T.alignof == Base.alignof) } static if (is(T == Base) || RTInfo!T != RTInfo!Base) - override @property immutable(void)* rtInfo() nothrow pure const @safe + override @property immutable(void)* rtInfo() { return RTInfo!T; } @@ -377,52 +332,33 @@ private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInf static if (is(T == Base)) override size_t getHash(scope const void* p) @trusted const { - static if (__traits(isFloating, T) || isComplex!T) - return Array!T.hashOf(*cast(T[]*)p); - else - return hashOf(*cast(const T[]*) p); + return hashOf(*cast(const T[]*) p); } static if (is(T == Base)) override bool equals(in void* p1, in void* p2) const { - static if (__traits(isFloating, T) || isComplex!T) - { - return Array!T.equals(*cast(T[]*)p1, *cast(T[]*)p2); - } - else - { - import core.stdc.string; - auto s1 = *cast(T[]*)p1; - auto s2 = *cast(T[]*)p2; - return s1.length == s2.length && - memcmp(s1.ptr, s2.ptr, s1.length) == 0; - } + // Just reuse the builtin. + return *cast(const(T)[]*) p1 == *cast(const(T)[]*) p2; } static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) override int compare(in void* p1, in void* p2) const { - static if (__traits(isFloating, T) || isComplex!T) - { - return Array!T.compare(*cast(T[]*)p1, *cast(T[]*)p2); - } - else + // Can't reuse __cmp in object.d because that handles NaN differently. + // (Q: would it make sense to unify behaviors?) + // return __cmp(*cast(const T[]*) p1, *cast(const T[]*) p2); + auto lhs = *cast(const T[]*) p1; + auto rhs = *cast(const T[]*) p2; + size_t len = lhs.length; + if (rhs.length < len) + len = rhs.length; + for (size_t u = 0; u < len; u++) { - auto s1 = *cast(T[]*)p1; - auto s2 = *cast(T[]*)p2; - auto len = s1.length; - - if (s2.length < len) - len = s2.length; - for (size_t u = 0; u < len; u++) - { - if (int result = (s1[u] > s2[u]) - (s1[u] < s2[u])) - return result; - } - return (s1.length > s2.length) - (s1.length < s2.length); + if (int result = cmp3(lhs.ptr[u], rhs.ptr[u])) + return result; } - } + return cmp3(lhs.length, rhs.length); } override @property inout(TypeInfo) next() inout { @@ -692,52 +628,37 @@ unittest // typeof(null) class TypeInfo_n : TypeInfo { - override string toString() const @safe { return "typeof(null)"; } + const: pure: @nogc: nothrow: @safe: - override size_t getHash(scope const void* p) const - { - return 0; - } + override string toString() { return "typeof(null)"; } - override bool equals(in void* p1, in void* p2) const @trusted - { - return true; - } + override size_t getHash(scope const void*) { return 0; } - override int compare(in void* p1, in void* p2) const @trusted - { - return 0; - } + override bool equals(in void*, in void*) { return true; } - override @property size_t tsize() const - { - return typeof(null).sizeof; - } + override int compare(in void*, in void*) { return 0; } - override const(void)[] initializer() const @trusted - { - __gshared immutable void[typeof(null).sizeof] init; - return init; - } + override @property size_t tsize() { return typeof(null).sizeof; } - override void swap(void *p1, void *p2) const @trusted - { - } + override const(void)[] initializer() @trusted { return (cast(void *)null)[0 .. size_t.sizeof]; } - override @property immutable(void)* rtInfo() nothrow pure const @safe { return rtinfoNoPointers; } + override void swap(void*, void*) {} - unittest + override @property immutable(void)* rtInfo() { return rtinfoNoPointers; } +} + +unittest +{ + with (typeid(typeof(null))) { - with (typeid(typeof(null))) - { - assert(toString == "typeof(null)"); - assert(getHash(null) == 0); - assert(equals(null, null)); - assert(compare(null, null) == 0); - assert(tsize == typeof(null).sizeof); - assert(initializer == new ubyte[(void*).sizeof]); - assert(rtInfo == rtinfoNoPointers); - } + assert(toString == "typeof(null)"); + assert(getHash(null) == 0); + assert(equals(null, null)); + assert(compare(null, null) == 0); + assert(tsize == typeof(null).sizeof); + assert(initializer.ptr is null); + assert(initializer.length == typeof(null).sizeof); + assert(rtInfo == rtinfoNoPointers); } } |