mirror of
https://github.com/openRuyi-Project/gcc.git
synced 2026-09-06 14:01:37 +00:00
D front-end changes: - Import dmd v2.112.0. - Bitfields feature is now enabled by default. - The compiler now accepts `-std=d2024' and `-std=d202y'. - An error is now issued for dangling `else' statements. - `finally' statements are no longer rewritten to a sequence if no `Exception' was thrown. - Some forms of `printf' calls are now treated as `@safe'. - Implicit integer conversions in `int op= float` assignments has been deprecated. D runtime changes: - Import druntime v2.112.0. - Added `filterCaughtThrowable' in `core.thread.ThreadBase'. Phobos changes: - Import phobos v2.112.0. gcc/d/ChangeLog: * dmd/VERSION: Bump version to v2.112.0. * dmd/MERGE: Merge upstream dmd 24a41073c2. * d-attribs.cc (build_attributes): Update for new front-end interface. * d-builtins.cc (build_frontend_type): Likewise. (matches_builtin_type): Likewise. (d_init_versions): Predefine D_Profile when compiling with profile enabled. * d-codegen.cc (get_array_length): Update for new front-end interface. (lower_struct_comparison): Likewise. (build_array_from_val): Likewise. (get_function_type): Likewise. (get_frameinfo): Likewise. * d-compiler.cc (Compiler::paintAsType): Likewise. * d-convert.cc (convert_expr): Likewise. (convert_for_rvalue): Likewise. (convert_for_assignment): Likewise. (d_array_convert): Likewise. * d-diagnostic.cc (verrorReport): Rename to ... (vreportDiagnostic): ... this. (verrorReportSupplemental): Rename to ... (vsupplementalDiagnostic): ... this. * d-lang.cc (d_handle_option): Handle -std=d2024 and -std=d202y. (d_parse_file): Update for new front-end interface. * d-target.cc (Target::fieldalign): Likewise. (Target::isVectorTypeSupported): Likewise. (Target::isVectorOpSupported): Likewise. * decl.cc (get_symbol_decl): Likewise. (DeclVisitor::visit): Likewise. (DeclVisitor::visit (FuncDeclaration *)): Do NRVO on `__result' decl. * expr.cc (needs_postblit): Remove. (needs_dtor): Remove. (lvalue_p): Remove. (ExprVisitor::visit): Update for new front-end interface. (ExprVisitor::visit (AssignExp *)): Update for front-end lowering expression using templates. * imports.cc (ImportVisitor::visit): Update for new front-end interface. * intrinsics.def (INTRINSIC_VA_ARG): Update signature. (INTRINSIC_C_VA_ARG): Update signature. (INTRINSIC_VASTART): Update signature. * lang.opt: Add -std=d2024 and -std=d202y. * toir.cc (IRVisitor::visit): Update for new front-end interface. * typeinfo.cc (TypeInfoVisitor::visit): Likewise. (TypeInfoVisitor::visit (TypeInfoStructDeclaration *)): Ensure semantic is ran on all TypeInfo members. (base_vtable_offset): Update for new front-end interface. * types.cc (TypeVisitor::visit): Likewise. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime 24a41073c2. * libdruntime/__importc_builtins.di: Reimplement. * src/MERGE: Merge upstream phobos 808314eb2. * testsuite/libphobos.aa/test_aa.d: Adjust test. * testsuite/libphobos.gc/forkgc2.d: Removed. * testsuite/libphobos.thread/filterthrownglobal.d: New test. * testsuite/libphobos.thread/filterthrownmethod.d: New test. gcc/testsuite/ChangeLog: * gdc.dg/pr90601.d: Adjust test. * lib/gdc-utils.exp: Handle new compiler options.
496 lines
14 KiB
D
496 lines
14 KiB
D
/**
|
|
* Contains a bitfield used by the GC.
|
|
*
|
|
* Copyright: D Language Foundation 2005 - 2021.
|
|
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
|
|
* Authors: Walter Bright, David Friedman, Sean Kelly
|
|
*/
|
|
module core.internal.gc.bits;
|
|
|
|
import core.internal.gc.os;
|
|
|
|
import core.bitop;
|
|
import core.exception : onOutOfMemoryError;
|
|
import core.stdc.stdlib : calloc, free;
|
|
import core.stdc.string : memcpy, memset;
|
|
|
|
// use version gcbitsSingleBitOperation to disable optimizations that use
|
|
// word operands on bulk operation copyRange, setRange, clrRange, etc.
|
|
// version = gcbitsSingleBitOperation;
|
|
|
|
struct GCBits
|
|
{
|
|
@nogc:
|
|
alias size_t wordtype;
|
|
|
|
enum BITS_PER_WORD = (wordtype.sizeof * 8);
|
|
enum BITS_SHIFT = (wordtype.sizeof == 8 ? 6 : 5);
|
|
enum BITS_MASK = (BITS_PER_WORD - 1);
|
|
enum BITS_0 = cast(wordtype)0;
|
|
enum BITS_1 = cast(wordtype)1;
|
|
enum BITS_2 = cast(wordtype)2;
|
|
|
|
wordtype* data;
|
|
size_t nbits;
|
|
|
|
void Dtor(bool share = false) nothrow
|
|
{
|
|
if (data)
|
|
{
|
|
if (!AllocSupportsShared || !share)
|
|
free(data);
|
|
else static if (AllocSupportsShared)
|
|
os_mem_unmap_shared(data, nwords * data[0].sizeof);
|
|
else
|
|
assert(false); // unreachable
|
|
data = null;
|
|
}
|
|
}
|
|
|
|
void alloc(size_t nbits, bool share = false) nothrow
|
|
{
|
|
this.nbits = nbits;
|
|
if (!AllocSupportsShared || !share)
|
|
data = cast(typeof(data[0])*)calloc(nwords, data[0].sizeof);
|
|
else static if (AllocSupportsShared)
|
|
data = cast(typeof(data[0])*)os_mem_map_shared(nwords * data[0].sizeof); // Allocate as MAP_SHARED
|
|
else
|
|
assert(false); // unreachable
|
|
if (!data)
|
|
onOutOfMemoryError();
|
|
}
|
|
|
|
wordtype test(size_t i) const scope @trusted pure nothrow @nogc
|
|
in
|
|
{
|
|
assert(i < nbits);
|
|
}
|
|
do
|
|
{
|
|
return core.bitop.bt(data, i);
|
|
}
|
|
|
|
int set(size_t i) scope @trusted pure nothrow @nogc
|
|
in
|
|
{
|
|
assert(i < nbits);
|
|
}
|
|
do
|
|
{
|
|
return core.bitop.bts(data, i);
|
|
}
|
|
|
|
int clear(size_t i) scope @trusted pure nothrow @nogc
|
|
in
|
|
{
|
|
assert(i <= nbits);
|
|
}
|
|
do
|
|
{
|
|
return core.bitop.btr(data, i);
|
|
}
|
|
|
|
// return non-zero if bit already set
|
|
size_t setLocked(size_t i) scope @trusted pure nothrow @nogc
|
|
{
|
|
version (GNU)
|
|
{
|
|
import gcc.builtins;
|
|
const pos = i >> BITS_SHIFT;
|
|
const mask = BITS_1 << (i & BITS_MASK);
|
|
mixin("auto val = __atomic_fetch_or_" ~ size_t.sizeof.stringof[0]
|
|
~ "(cast(shared)(data + pos), mask, 3);");
|
|
return (val & mask) != 0;
|
|
}
|
|
else version (LDC)
|
|
{
|
|
import ldc.intrinsics;
|
|
const pos = i >> BITS_SHIFT;
|
|
const mask = BITS_1 << (i & BITS_MASK);
|
|
auto val = llvm_atomic_rmw_or(cast(shared)(data + pos), mask);
|
|
return (val & mask) != 0;
|
|
}
|
|
else version (D_InlineAsm_X86)
|
|
{
|
|
asm pure @nogc nothrow {
|
|
mov EAX, this;
|
|
mov ECX, data[EAX];
|
|
mov EDX, i;
|
|
lock;
|
|
bts dword ptr[ECX], EDX;
|
|
sbb EAX,EAX;
|
|
}
|
|
}
|
|
else version (D_InlineAsm_X86_64)
|
|
{
|
|
asm pure @nogc nothrow {
|
|
mov RAX, this;
|
|
mov RAX, data[RAX];
|
|
mov RDX, i;
|
|
lock;
|
|
bts qword ptr[RAX], RDX;
|
|
sbb RAX,RAX;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const pos = i >> BITS_SHIFT;
|
|
auto pdata = cast(shared)(data + pos);
|
|
auto mask = BITS_1 << (i & BITS_MASK);
|
|
auto state = *pdata;
|
|
if (state & mask)
|
|
return state;
|
|
|
|
import core.atomic;
|
|
auto newstate = state | mask;
|
|
while (!cas(pdata, state, newstate))
|
|
{
|
|
state = *pdata;
|
|
if (state & mask)
|
|
return state;
|
|
newstate = state | mask;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
template testAndSet(bool locked)
|
|
{
|
|
static if (locked)
|
|
alias testAndSet = setLocked;
|
|
else
|
|
alias testAndSet = set;
|
|
}
|
|
|
|
|
|
mixin template RangeVars()
|
|
{
|
|
size_t firstWord = (target >> BITS_SHIFT);
|
|
size_t firstOff = target & BITS_MASK;
|
|
size_t last = target + len - 1;
|
|
size_t lastWord = (last >> BITS_SHIFT);
|
|
size_t lastOff = last & BITS_MASK;
|
|
}
|
|
|
|
// extract loops to allow inlining the rest
|
|
void clearWords(size_t firstWord, size_t lastWord) nothrow
|
|
{
|
|
for (size_t w = firstWord; w < lastWord; w++)
|
|
data[w] = 0;
|
|
}
|
|
|
|
void setWords(size_t firstWord, size_t lastWord) nothrow
|
|
{
|
|
for (size_t w = firstWord; w < lastWord; w++)
|
|
data[w] = ~0;
|
|
}
|
|
|
|
void copyWords(size_t firstWord, size_t lastWord, const(wordtype)* source) nothrow
|
|
{
|
|
for (size_t w = firstWord; w < lastWord; w++)
|
|
data[w] = source[w - firstWord];
|
|
}
|
|
|
|
void copyWordsShifted(size_t firstWord, size_t cntWords, size_t firstOff, const(wordtype)* source) nothrow
|
|
{
|
|
wordtype mask = ~BITS_0 << firstOff;
|
|
data[firstWord] = (data[firstWord] & ~mask) | (source[0] << firstOff);
|
|
for (size_t w = 1; w < cntWords; w++)
|
|
data[firstWord + w] = (source[w - 1] >> (BITS_PER_WORD - firstOff)) | (source[w] << firstOff);
|
|
}
|
|
|
|
// target = the biti to start the copy to
|
|
// destlen = the number of bits to copy from source
|
|
void copyRange(size_t target, size_t len, const(wordtype)* source) nothrow
|
|
{
|
|
version (gcbitsSingleBitOperation)
|
|
{
|
|
for (size_t i = 0; i < len; i++)
|
|
if (source[(i >> BITS_SHIFT)] & (BITS_1 << (i & BITS_MASK)))
|
|
set(target+i);
|
|
else
|
|
clear(target+i);
|
|
}
|
|
else
|
|
{
|
|
if (len > 0)
|
|
copyRangeZ(target, len, source);
|
|
}
|
|
}
|
|
|
|
void copyRangeZ(size_t target, size_t len, const(wordtype)* source) nothrow
|
|
{
|
|
mixin RangeVars!();
|
|
|
|
if (firstWord == lastWord)
|
|
{
|
|
wordtype mask = ((BITS_2 << (lastOff - firstOff)) - 1) << firstOff;
|
|
data[firstWord] = (data[firstWord] & ~mask) | ((source[0] << firstOff) & mask);
|
|
}
|
|
else if (firstOff == 0)
|
|
{
|
|
copyWords(firstWord, lastWord, source);
|
|
|
|
wordtype mask = (BITS_2 << lastOff) - 1;
|
|
data[lastWord] = (data[lastWord] & ~mask) | (source[lastWord - firstWord] & mask);
|
|
}
|
|
else
|
|
{
|
|
size_t cntWords = lastWord - firstWord;
|
|
copyWordsShifted(firstWord, cntWords, firstOff, source);
|
|
|
|
wordtype src = (source[cntWords - 1] >> (BITS_PER_WORD - firstOff));
|
|
if (lastOff >= firstOff) // prevent buffer overread
|
|
src |= (source[cntWords] << firstOff);
|
|
wordtype mask = (BITS_2 << lastOff) - 1;
|
|
data[lastWord] = (data[lastWord] & ~mask) | (src & mask);
|
|
}
|
|
}
|
|
|
|
void copyRangeRepeating(size_t target, size_t destlen, const(wordtype)* source, size_t sourcelen) nothrow
|
|
{
|
|
version (gcbitsSingleBitOperation)
|
|
{
|
|
for (size_t i=0; i < destlen; i++)
|
|
{
|
|
bool b;
|
|
size_t j = i % sourcelen;
|
|
b = (source[j >> BITS_SHIFT] & (BITS_1 << (j & BITS_MASK))) != 0;
|
|
if (b) set(target+i);
|
|
else clear(target+i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (destlen > sourcelen)
|
|
{
|
|
copyRange(target, sourcelen, source);
|
|
target += sourcelen;
|
|
destlen -= sourcelen;
|
|
}
|
|
copyRange(target, destlen, source);
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
// simulate broken array append test case in vibe.d
|
|
GCBits bits;
|
|
bits.alloc(10000);
|
|
auto data = bits.data;
|
|
|
|
GCBits src;
|
|
src.alloc(67);
|
|
src.data[0] = 0x4;
|
|
|
|
bits.copyRangeRepeating(2, 10000, src.data, 67);
|
|
|
|
foreach (i; 0 .. 10000)
|
|
if ((i - 2) % 67 == 2)
|
|
assert(bits.test(i));
|
|
else
|
|
assert(!bits.test(i));
|
|
}
|
|
|
|
void setRange(size_t target, size_t len) nothrow
|
|
{
|
|
version (gcbitsSingleBitOperation)
|
|
{
|
|
for (size_t i = 0; i < len; i++)
|
|
set(target+i);
|
|
}
|
|
else
|
|
{
|
|
if (len > 0)
|
|
setRangeZ(target, len);
|
|
}
|
|
}
|
|
|
|
void setRangeZ(size_t target, size_t len) nothrow
|
|
{
|
|
mixin RangeVars!();
|
|
|
|
if (firstWord == lastWord)
|
|
{
|
|
wordtype mask = ((BITS_2 << (lastOff - firstOff)) - 1) << firstOff;
|
|
data[firstWord] |= mask;
|
|
}
|
|
else
|
|
{
|
|
data[firstWord] |= ~BITS_0 << firstOff;
|
|
setWords(firstWord + 1, lastWord);
|
|
wordtype mask = (BITS_2 << lastOff) - 1;
|
|
data[lastWord] |= mask;
|
|
}
|
|
}
|
|
|
|
void clrRange(size_t target, size_t len) nothrow
|
|
{
|
|
version (gcbitsSingleBitOperation)
|
|
{
|
|
for (size_t i = 0; i < len; i++)
|
|
clear(target+i);
|
|
}
|
|
else
|
|
{
|
|
if (len > 0)
|
|
clrRangeZ(target, len);
|
|
}
|
|
}
|
|
|
|
void clrRangeZ(size_t target, size_t len) nothrow
|
|
{
|
|
mixin RangeVars!();
|
|
if (firstWord == lastWord)
|
|
{
|
|
wordtype mask = ((BITS_2 << (lastOff - firstOff)) - 1) << firstOff;
|
|
data[firstWord] &= ~mask;
|
|
}
|
|
else
|
|
{
|
|
data[firstWord] &= ~(~BITS_0 << firstOff);
|
|
clearWords(firstWord + 1, lastWord);
|
|
wordtype mask = (BITS_2 << lastOff) - 1;
|
|
data[lastWord] &= ~mask;
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
GCBits bits;
|
|
bits.alloc(1000);
|
|
auto data = bits.data;
|
|
|
|
bits.setRange(0,1);
|
|
assert(data[0] == 1);
|
|
|
|
bits.clrRange(0,1);
|
|
assert(data[0] == 0);
|
|
|
|
bits.setRange(BITS_PER_WORD-1,1);
|
|
assert(data[0] == BITS_1 << (BITS_PER_WORD-1));
|
|
|
|
bits.clrRange(BITS_PER_WORD-1,1);
|
|
assert(data[0] == 0);
|
|
|
|
bits.setRange(12,7);
|
|
assert(data[0] == 0b0111_1111_0000_0000_0000);
|
|
|
|
bits.clrRange(14,4);
|
|
assert(data[0] == 0b0100_0011_0000_0000_0000);
|
|
|
|
bits.clrRange(0,BITS_PER_WORD);
|
|
assert(data[0] == 0);
|
|
|
|
bits.setRange(0,BITS_PER_WORD);
|
|
assert(data[0] == ~0);
|
|
assert(data[1] == 0);
|
|
|
|
bits.setRange(BITS_PER_WORD,BITS_PER_WORD);
|
|
assert(data[0] == ~0);
|
|
assert(data[1] == ~0);
|
|
assert(data[2] == 0);
|
|
bits.clrRange(BITS_PER_WORD/2,BITS_PER_WORD);
|
|
assert(data[0] == (BITS_1 << (BITS_PER_WORD/2)) - 1);
|
|
assert(data[1] == ~data[0]);
|
|
assert(data[2] == 0);
|
|
|
|
bits.setRange(8*BITS_PER_WORD+1,4*BITS_PER_WORD-2);
|
|
assert(data[8] == ~0 << 1);
|
|
assert(data[9] == ~0);
|
|
assert(data[10] == ~0);
|
|
assert(data[11] == cast(wordtype)~0 >> 1);
|
|
|
|
bits.clrRange(9*BITS_PER_WORD+1,2*BITS_PER_WORD);
|
|
assert(data[8] == ~0 << 1);
|
|
assert(data[9] == 1);
|
|
assert(data[10] == 0);
|
|
assert(data[11] == ((cast(wordtype)~0 >> 1) & ~1));
|
|
|
|
wordtype[4] src = [ 0xa, 0x5, 0xaa, 0x55 ];
|
|
|
|
void testCopyRange(size_t start, size_t len, int repeat = 1)
|
|
{
|
|
bits.setRange(0, bits.nbits);
|
|
if (repeat > 1)
|
|
bits.copyRangeRepeating(start, repeat * len, src.ptr, len);
|
|
else
|
|
bits.copyRange(start, len, src.ptr);
|
|
foreach (i; 0 .. start)
|
|
assert(bits.test(i));
|
|
foreach (r; 0 .. repeat)
|
|
foreach (i; 0 .. len)
|
|
assert(!bits.test(start + r*len + i) == !core.bitop.bt(src.ptr, i));
|
|
foreach (i; start + repeat*len .. 10*BITS_PER_WORD)
|
|
assert(bits.test(i));
|
|
}
|
|
|
|
testCopyRange(20, 10); // short copy range within same word
|
|
testCopyRange(50, 20); // short copy range spanning two words
|
|
testCopyRange(64, 3 * BITS_PER_WORD + 3); // aligned copy range
|
|
testCopyRange(77, 2 * BITS_PER_WORD + 15); // unaligned copy range
|
|
testCopyRange(64, 127); // copy range within critical end alignment
|
|
|
|
testCopyRange(10, 4, 5); // repeating small range within same word
|
|
testCopyRange(20, 5, 10); // repeating small range spanning two words
|
|
testCopyRange(40, 21, 7); // repeating medium range
|
|
testCopyRange(73, 2 * BITS_PER_WORD + 15, 5); // repeating multi-word range
|
|
|
|
testCopyRange(2, 3, 166); // failed with assert
|
|
}
|
|
|
|
void zero() nothrow
|
|
{
|
|
memset(data, 0, nwords * wordtype.sizeof);
|
|
}
|
|
|
|
void setAll() nothrow
|
|
{
|
|
memset(data, 0xFF, nwords * wordtype.sizeof);
|
|
}
|
|
|
|
void copy(GCBits *f) nothrow
|
|
in
|
|
{
|
|
assert(nwords == f.nwords);
|
|
}
|
|
do
|
|
{
|
|
memcpy(data, f.data, nwords * wordtype.sizeof);
|
|
}
|
|
|
|
@property size_t nwords() const pure nothrow
|
|
{
|
|
return (nbits + (BITS_PER_WORD - 1)) >> BITS_SHIFT;
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
GCBits b;
|
|
|
|
b.alloc(786);
|
|
assert(!b.test(123));
|
|
assert(!b.clear(123));
|
|
assert(!b.set(123));
|
|
assert(b.test(123));
|
|
assert(b.clear(123));
|
|
assert(!b.test(123));
|
|
|
|
b.set(785);
|
|
b.set(0);
|
|
assert(b.test(785));
|
|
assert(b.test(0));
|
|
b.zero();
|
|
assert(!b.test(785));
|
|
assert(!b.test(0));
|
|
|
|
GCBits b2;
|
|
b2.alloc(786);
|
|
b2.set(38);
|
|
b.copy(&b2);
|
|
assert(b.test(38));
|
|
b2.Dtor();
|
|
b.Dtor();
|
|
}
|