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
*/
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;
@ -1719,6 +1732,7 @@ extern (C++) final class SymbolDeclaration : Declaration
this.dsym = dsym;
storage_class |= STC.const_;
}
}
// Eliminate need for dynamic_cast
override inout(SymbolDeclaration) isSymbolDeclaration() inout

View file

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

View file

@ -2139,6 +2139,12 @@ public:
}
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
e = s.dsym.type.defaultInitLiteral(loc);
if (e.op == TOK.error)

View file

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

View file

@ -1922,6 +1922,25 @@ else
}
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))
{
return ret;

View file

@ -1573,23 +1573,32 @@ DValue *DtoSymbolAddress(const Loc &loc, Type *type, Declaration *decl) {
}
if (SymbolDeclaration *sdecl = decl->isSymbolDeclaration()) {
// this seems to be the static initialiser for structs
Type *sdecltype = sdecl->type->toBasetype();
IF_LOG Logger::print("Sym: type=%s\n", sdecltype->toChars());
assert(sdecltype->ty == Tstruct);
TypeStruct *ts = static_cast<TypeStruct *>(sdecltype);
StructDeclaration *sd = ts->sym;
assert(sd);
DtoResolveStruct(sd);
// this is the static initialiser (init symbol) for aggregates
AggregateDeclaration *ad = sdecl->dsym;
IF_LOG Logger::print("Sym: ad=%s\n", ad->toChars());
DtoResolveDsymbol(ad);
auto sd = ad->isStructDeclaration();
// 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) {
error(loc, "no init symbol for zero-initialized struct");
fatal();
}
LLValue *initsym = getIrAggr(sd)->getInitSymbol();
initsym = DtoBitCast(initsym, DtoType(ts->pointerTo()));
return new DLValue(type, initsym);
return new DLValue(type, DtoBitCast(initsym, DtoPtrToType(sd->type)));
}
llvm_unreachable("Unimplemented VarExp type");

View file

@ -71,15 +71,17 @@ public:
LOG_SCOPE;
if (SymbolDeclaration *sdecl = e->var->isSymbolDeclaration()) {
// this seems to be the static initialiser for structs
Type *sdecltype = sdecl->type->toBasetype();
IF_LOG Logger::print("Sym: type=%s\n", sdecltype->toChars());
assert(sdecltype->ty == Tstruct);
TypeStruct *ts = static_cast<TypeStruct *>(sdecltype);
DtoResolveStruct(ts->sym);
result = getIrAggr(ts->sym)->getDefaultInit();
// This is the static initialiser (init symbol) for aggregates.
// Exclude void[]-typed `__traits(initSymbol)` (LDC extension).
if (sdecl->type->toBasetype()->ty == Tstruct) {
const auto sd = sdecl->dsym->isStructDeclaration();
assert(sd);
IF_LOG Logger::print("Sym: sd=%s\n", sd->toChars());
DtoResolveStruct(sd);
result = getIrAggr(sd)->getDefaultInit();
return;
}
}
if (TypeInfoDeclaration *ti = e->var->isTypeInfoDeclaration()) {
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.
if (auto ve = rhs->isVarExp()) {
if (auto symdecl = ve->var->isSymbolDeclaration()) {
Type *t = symdecl->type->toBasetype();
if (auto ts = t->isTypeStruct()) {
// this is the static initializer for a struct (init symbol)
StructDeclaration *sd = ts->sym;
// exclude void[]-typed `__traits(initSymbol)` (LDC extension)
if (symdecl->type->toBasetype()->ty == Tstruct) {
auto sd = symdecl->dsym->isStructDeclaration();
assert(sd);
DtoResolveStruct(sd);
if (sd->zeroInit) {
Logger::println("success, zeroing out");
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)));
}