Add __traits(initSymbol, <aggregate type>) (#3774)

A clean way of directly accessing struct and class init symbols. It
yields a `const(void[])` slice; the length reflecting the struct/class
instance size, and the pointer either pointing to the init symbol or
being null for zero-initialized structs.

Paves the way for resolving #3773 in druntime as well as accessing class
init symbols without `TypeInfo_Class.initializer()` indirection, and so
with -betterC as well.
This commit is contained in:
Martin Kinkelin 2021-06-28 18:40:28 +02:00 committed by GitHub
parent 383129c3b0
commit c6096a7d27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 107 additions and 24 deletions

View file

@ -1710,6 +1710,19 @@ version (IN_LLVM)
* This is a shell around a back end symbol * This is a shell around a back end symbol
*/ */
extern (C++) final class SymbolDeclaration : Declaration extern (C++) final class SymbolDeclaration : Declaration
{
version (IN_LLVM)
{
AggregateDeclaration dsym;
extern (D) this(const ref Loc loc, AggregateDeclaration dsym)
{
super(loc, dsym.ident);
this.dsym = dsym;
storage_class |= STC.const_;
}
}
else
{ {
StructDeclaration dsym; StructDeclaration dsym;
@ -1719,6 +1732,7 @@ extern (C++) final class SymbolDeclaration : Declaration
this.dsym = dsym; this.dsym = dsym;
storage_class |= STC.const_; storage_class |= STC.const_;
} }
}
// Eliminate need for dynamic_cast // Eliminate need for dynamic_cast
override inout(SymbolDeclaration) isSymbolDeclaration() inout override inout(SymbolDeclaration) isSymbolDeclaration() inout

View file

@ -273,7 +273,11 @@ public:
class SymbolDeclaration : public Declaration class SymbolDeclaration : public Declaration
{ {
public: public:
#if IN_LLVM
AggregateDeclaration *dsym;
#else
StructDeclaration *dsym; StructDeclaration *dsym;
#endif
// Eliminate need for dynamic_cast // Eliminate need for dynamic_cast
SymbolDeclaration *isSymbolDeclaration() { return (SymbolDeclaration *)this; } SymbolDeclaration *isSymbolDeclaration() { return (SymbolDeclaration *)this; }

View file

@ -2139,6 +2139,12 @@ public:
} }
else if (SymbolDeclaration s = d.isSymbolDeclaration()) else if (SymbolDeclaration s = d.isSymbolDeclaration())
{ {
version (IN_LLVM)
{
// exclude void[]-typed `__traits(initSymbol)` (LDC extension)
if (s.type.toBasetype().ty != Tstruct)
return CTFEExp.cantexp;
}
// Struct static initializers, for example // Struct static initializers, for example
e = s.dsym.type.defaultInitLiteral(loc); e = s.dsym.type.defaultInitLiteral(loc);
if (e.op == TOK.error) if (e.op == TOK.error)

View file

@ -520,6 +520,7 @@ immutable Msgtable[] msgtable =
// IN_LLVM: LDC-specific traits. // IN_LLVM: LDC-specific traits.
{ "targetCPU" }, { "targetCPU" },
{ "targetHasFeature" }, { "targetHasFeature" },
{ "initSymbol" },
// IN_LLVM: LDC-specific attributes // IN_LLVM: LDC-specific attributes
{ "ldc" }, { "ldc" },

View file

@ -1922,6 +1922,25 @@ else
} }
version (IN_LLVM) version (IN_LLVM)
{ {
if (e.ident == Id.initSymbol)
{
if (dim != 1)
return dimError(1);
auto o = (*e.args)[0];
Type t = isType(o);
AggregateDeclaration ad = t ? isAggregate(t) : null;
if (!ad)
{
e.error("aggregate type expected as argument to __traits(initSymbol)");
return ErrorExp.get();
}
Declaration d = new SymbolDeclaration(ad.loc, ad);
d.type = Type.tvoid.arrayOf().constOf();
d.storage_class |= STC.rvalue;
return new VarExp(ad.loc, d);
}
if (Expression ret = semanticTraitsHook(e, sc)) if (Expression ret = semanticTraitsHook(e, sc))
{ {
return ret; return ret;

View file

@ -1573,23 +1573,32 @@ DValue *DtoSymbolAddress(const Loc &loc, Type *type, Declaration *decl) {
} }
if (SymbolDeclaration *sdecl = decl->isSymbolDeclaration()) { if (SymbolDeclaration *sdecl = decl->isSymbolDeclaration()) {
// this seems to be the static initialiser for structs // this is the static initialiser (init symbol) for aggregates
Type *sdecltype = sdecl->type->toBasetype(); AggregateDeclaration *ad = sdecl->dsym;
IF_LOG Logger::print("Sym: type=%s\n", sdecltype->toChars()); IF_LOG Logger::print("Sym: ad=%s\n", ad->toChars());
assert(sdecltype->ty == Tstruct); DtoResolveDsymbol(ad);
TypeStruct *ts = static_cast<TypeStruct *>(sdecltype); auto sd = ad->isStructDeclaration();
StructDeclaration *sd = ts->sym;
assert(sd);
DtoResolveStruct(sd);
// LDC extension: void[]-typed `__traits(initSymbol)`, for classes too
auto tb = sdecl->type->toBasetype();
if (tb->ty != Tstruct) {
assert(tb->ty == Tarray && tb->nextOf()->ty == Tvoid);
const auto size = DtoConstSize_t(ad->structsize);
llvm::Constant *ptr =
sd && sd->zeroInit
? getNullValue(getVoidPtrType())
: DtoBitCast(getIrAggr(ad)->getInitSymbol(), getVoidPtrType());
return new DSliceValue(type, size, ptr);
}
assert(sd);
if (sd->zeroInit) { if (sd->zeroInit) {
error(loc, "no init symbol for zero-initialized struct"); error(loc, "no init symbol for zero-initialized struct");
fatal(); fatal();
} }
LLValue *initsym = getIrAggr(sd)->getInitSymbol(); LLValue *initsym = getIrAggr(sd)->getInitSymbol();
initsym = DtoBitCast(initsym, DtoType(ts->pointerTo())); return new DLValue(type, DtoBitCast(initsym, DtoPtrToType(sd->type)));
return new DLValue(type, initsym);
} }
llvm_unreachable("Unimplemented VarExp type"); llvm_unreachable("Unimplemented VarExp type");

View file

@ -71,15 +71,17 @@ public:
LOG_SCOPE; LOG_SCOPE;
if (SymbolDeclaration *sdecl = e->var->isSymbolDeclaration()) { if (SymbolDeclaration *sdecl = e->var->isSymbolDeclaration()) {
// this seems to be the static initialiser for structs // This is the static initialiser (init symbol) for aggregates.
Type *sdecltype = sdecl->type->toBasetype(); // Exclude void[]-typed `__traits(initSymbol)` (LDC extension).
IF_LOG Logger::print("Sym: type=%s\n", sdecltype->toChars()); if (sdecl->type->toBasetype()->ty == Tstruct) {
assert(sdecltype->ty == Tstruct); const auto sd = sdecl->dsym->isStructDeclaration();
TypeStruct *ts = static_cast<TypeStruct *>(sdecltype); assert(sd);
DtoResolveStruct(ts->sym); IF_LOG Logger::print("Sym: sd=%s\n", sd->toChars());
result = getIrAggr(ts->sym)->getDefaultInit(); DtoResolveStruct(sd);
result = getIrAggr(sd)->getDefaultInit();
return; return;
} }
}
if (TypeInfoDeclaration *ti = e->var->isTypeInfoDeclaration()) { if (TypeInfoDeclaration *ti = e->var->isTypeInfoDeclaration()) {
result = DtoTypeInfoOf(e->loc, ti->tinfo, /*base=*/false); result = DtoTypeInfoOf(e->loc, ti->tinfo, /*base=*/false);

View file

@ -2755,13 +2755,11 @@ bool toInPlaceConstruction(DLValue *lhs, Expression *rhs) {
// to initialize a `S[1]` lhs with a `S` rhs. // to initialize a `S[1]` lhs with a `S` rhs.
if (auto ve = rhs->isVarExp()) { if (auto ve = rhs->isVarExp()) {
if (auto symdecl = ve->var->isSymbolDeclaration()) { if (auto symdecl = ve->var->isSymbolDeclaration()) {
Type *t = symdecl->type->toBasetype(); // exclude void[]-typed `__traits(initSymbol)` (LDC extension)
if (auto ts = t->isTypeStruct()) { if (symdecl->type->toBasetype()->ty == Tstruct) {
// this is the static initializer for a struct (init symbol) auto sd = symdecl->dsym->isStructDeclaration();
StructDeclaration *sd = ts->sym;
assert(sd); assert(sd);
DtoResolveStruct(sd); DtoResolveStruct(sd);
if (sd->zeroInit) { if (sd->zeroInit) {
Logger::println("success, zeroing out"); Logger::println("success, zeroing out");
DtoMemSetZero(DtoLVal(lhs)); DtoMemSetZero(DtoLVal(lhs));

View file

@ -0,0 +1,30 @@
// RUN: %ldc -run %s
struct Zero { int x; }
struct NonZero { long x = 1; }
class C { short x = 123; }
void main()
{
auto zeroInit = __traits(initSymbol, Zero);
static assert(is(typeof(zeroInit) == const(void[])));
assert(zeroInit.ptr is null && zeroInit.length == Zero.sizeof);
auto nonZeroInit = __traits(initSymbol, NonZero);
static assert(is(typeof(nonZeroInit) == const(void[])));
assert(nonZeroInit.ptr !is null && nonZeroInit.length == NonZero.sizeof);
assert(cast(const(long[])) nonZeroInit == [1L]);
auto cInit = __traits(initSymbol, C);
static assert(is(typeof(cInit) == const(void[])));
assert(cInit.ptr !is null && cInit.length == __traits(classInstanceSize, C));
scope c = new C;
import core.stdc.string;
assert(memcmp(cast(void*) c, cInit.ptr, cInit.length) == 0);
static assert(!__traits(compiles, __traits(initSymbol, int)));
static assert(!__traits(compiles, __traits(initSymbol, Zero[1])));
static assert(!__traits(compiles, __traits(initSymbol, 123)));
}