Skip to content

Commit 2d40b58

Browse files
committed
Implement linker list
1 parent 0f33b96 commit 2d40b58

File tree

14 files changed

+559
-13
lines changed

14 files changed

+559
-13
lines changed

changelog/dmd.linkerlist.dd

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Linker lists are now supported
2+
3+
A linker list is a list of symbols that a linker puts together into a section, allowing for each disparate module to provide entries within their object files.
4+
They cannot be exported or dllimport, you must register their contents by use of a module constructor (can be C).
5+
6+
To iterate them, you may foreach over them at runtime.
7+
8+
To append to them, you may do so from within a CTFE executed function.
9+
10+
```d
11+
import core.attribute;
12+
13+
__gshared __linkerlist!int myInts;
14+
15+
static assert(() { myInts ~= 42; return true; }());
16+
17+
void main() {
18+
foreach(v; myInts)
19+
assert(v == 42);
20+
}
21+
```
22+
23+
Each declaration for the linker list, must be per executable or shared library.
24+
The declaration may trigger symbol emittance.
25+
26+
If the type is ``const`` or ``immutable`` then the list entries will be marked as read only.
27+
28+
An entry when emitted will go into the object file, associated with the module that initiated the function.
29+
It is recommended therefore that the function should always be templated.
30+
If the template is associated with a module that is not being compiled (imported), it will not be emitted into an object file.
31+
Note: that this module does NOT refer to the source file that contains the template.
32+
33+
If you are already using linker lists using the section attribute, it is strongly recommended to swap to compiler backed linker lists.
34+
They are platform, linker, and compiler specific abstractions that are not portable.

compiler/src/dmd/declaration.d

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -427,15 +427,17 @@ extern (C++) final class OverDeclaration : Declaration
427427
extern (C++) class VarDeclaration : Declaration
428428
{
429429
Initializer _init;
430-
FuncDeclarations nestedrefs; // referenced by these lexically nested functions
431-
TupleDeclaration aliasTuple; // when `this` is really a tuple of declarations
432-
VarDeclaration lastVar; // Linked list of variables for goto-skips-init detection
433-
Expression edtor; // if !=null, does the destruction of the variable
434-
IntRange* range; // if !=null, the variable is known to be within the range
430+
FuncDeclarations nestedrefs; // referenced by these lexically nested functions
431+
TupleDeclaration aliasTuple; // when `this` is really a tuple of declarations
432+
VarDeclaration lastVar; // Linked list of variables for goto-skips-init detection
433+
Expression edtor; // if !=null, does the destruction of the variable
434+
IntRange* range; // if !=null, the variable is known to be within the range
435435

436-
uint endlinnum; // line number of end of scope that this var lives in
436+
VarDeclaration entryForLinkerList; // if !=null, the linker list we're appending to
437+
438+
uint endlinnum; // line number of end of scope that this var lives in
437439
uint offset;
438-
uint sequenceNumber; // order the variables are declared
440+
uint sequenceNumber; // order the variables are declared
439441
structalign_t alignment;
440442

441443
// When interpreting, these point to the value (NULL if value not determinable)
@@ -469,6 +471,8 @@ extern (C++) class VarDeclaration : Declaration
469471
bool dllExport; /// __declspec(dllexport)
470472
mixin VarDeclarationExtra;
471473
bool systemInferred; /// @system was inferred from initializer
474+
475+
bool isLinkerListDeclaration; /// This variable declares a linker list
472476
}
473477

474478
import dmd.common.bitfields : generateBitFields;

compiler/src/dmd/declaration.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ class VarDeclaration : public Declaration
236236
VarDeclaration *lastVar; // Linked list of variables for goto-skips-init detection
237237
Expression *edtor; // if !=NULL, does the destruction of the variable
238238
IntRange *range; // if !NULL, the variable is known to be within the range
239+
VarDeclaration* entryForLinkerList;
239240

240241
unsigned endlinnum; // line number of end of scope that this var lives in
241242
unsigned offset;
@@ -282,6 +283,8 @@ class VarDeclaration : public Declaration
282283
#endif
283284
bool systemInferred() const;
284285
bool systemInferred(bool v);
286+
bool isLinkerListDeclaration() const;
287+
bool isLinkerListDeclaration(bool v);
285288
static VarDeclaration *create(Loc loc, Type *t, Identifier *id, Initializer *init, StorageClass storage_class = STCundefined);
286289
VarDeclaration *syntaxCopy(Dsymbol *) override;
287290
const char *kind() const override;

compiler/src/dmd/dinterpret.d

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4652,6 +4652,47 @@ public:
46524652
Expression pthis = null;
46534653
FuncDeclaration fd = null;
46544654

4655+
if (e.f !is null && e.f.ident is Id.opOpAssign)
4656+
{
4657+
DotVarExp dve = e.e1.isDotVarExp();
4658+
VarExp ve;
4659+
VarDeclaration vd, linkerListEntry;
4660+
4661+
if (dve !is null &&
4662+
(ve = dve.e1.isVarExp()) !is null &&
4663+
(vd = ve.var.isVarDeclaration()) !is null &&
4664+
vd.isLinkerListDeclaration)
4665+
{
4666+
if (istate.fd is null)
4667+
{
4668+
error(e.loc, "cannot append to a linker list without being in a function");
4669+
result = CTFEExp.cantexp;
4670+
}
4671+
else
4672+
{
4673+
auto root = istate.fd.getModule;
4674+
// If we don't have a root module, to put it in we'll ignore it.
4675+
// We ignore it because it may have already been emitted in a different compilation step.
4676+
4677+
if (root.isRoot)
4678+
{
4679+
auto initExp = interpret((*e.arguments)[0], istate).copyRegionExp;
4680+
auto init = new ExpInitializer(e.loc, initExp);
4681+
linkerListEntry = new VarDeclaration(e.loc, null, Identifier.generateIdWithLoc("_d_linkerlistentry", e.loc), init);
4682+
4683+
linkerListEntry.entryForLinkerList = vd;
4684+
linkerListEntry.visibility.kind = Visibility.Kind.private_;
4685+
linkerListEntry.storage_class |= STC.gshared;
4686+
4687+
root.members.push(linkerListEntry);
4688+
linkerListEntry.dsymbolSemantic(root._scope);
4689+
}
4690+
}
4691+
4692+
return;
4693+
}
4694+
}
4695+
46554696
Expression ecall = interpretRegion(e.e1, istate);
46564697
if (exceptionOrCant(ecall))
46574698
return;

compiler/src/dmd/dsymbolsem.d

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2592,6 +2592,23 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
25922592
// Flag variable as error to avoid invalid error messages due to unknown size
25932593
dsym.type = Type.terror;
25942594
}
2595+
else if (ts.sym.ident is Id.__linkerlist)
2596+
{
2597+
// Okay we've got a linker list
2598+
2599+
if (dsym.visibility.kind == Visibility.Kind.export_)
2600+
{
2601+
.error(dsym.loc, "Linker lists cannot be exported");
2602+
dsym.type = Type.terror;
2603+
}
2604+
else if (dsym.storage_class & STC.extern_)
2605+
{
2606+
.error(dsym.loc, "Linker lists cannot be `extern` they must live at declaration site");
2607+
dsym.type = Type.terror;
2608+
}
2609+
else
2610+
dsym.isLinkerListDeclaration = true;
2611+
}
25952612
}
25962613
if ((dsym.storage_class & STC.auto_) && !inferred && !(dsym.storage_class & STC.autoref))
25972614
.error(dsym.loc, "%s `%s` - storage class `auto` has no effect if type is not inferred, did you mean `scope`?", dsym.kind, dsym.toPrettyChars);

compiler/src/dmd/frontend.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6598,6 +6598,7 @@ class VarDeclaration : public Declaration
65986598
VarDeclaration* lastVar;
65996599
Expression* edtor;
66006600
IntRange* range;
6601+
VarDeclaration* entryForLinkerList;
66016602
uint32_t endlinnum;
66026603
uint32_t offset;
66036604
uint32_t sequenceNumber;
@@ -6639,6 +6640,8 @@ class VarDeclaration : public Declaration
66396640
bool inAlignSection(bool v);
66406641
bool systemInferred() const;
66416642
bool systemInferred(bool v);
6643+
bool isLinkerListDeclaration() const;
6644+
bool isLinkerListDeclaration(bool v);
66426645
private:
66436646
uint32_t bitFields;
66446647
public:
@@ -8668,6 +8671,7 @@ struct Id final
86688671
static Identifier* classDelete;
86698672
static Identifier* apply;
86708673
static Identifier* applyReverse;
8674+
static Identifier* linkerlistApply;
86718675
static Identifier* Fempty;
86728676
static Identifier* Ffront;
86738677
static Identifier* Fback;

compiler/src/dmd/glue/e2ir.d

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import dmd.root.ctfloat;
2121
import dmd.root.rmem;
2222
import dmd.rootobject;
2323
import dmd.root.stringtable;
24+
import dmd.common.outbuffer;
2425

2526
import dmd.glue;
2627
import dmd.glue.objc;
@@ -56,6 +57,8 @@ import dmd.id;
5657
import dmd.init;
5758
import dmd.location;
5859
import dmd.mtype;
60+
import dmd.mangle : mangleToBuffer;
61+
import dmd.opover : search_function;
5962
import dmd.printast;
6063
import dmd.sideeffect;
6164
import dmd.statement;
@@ -5408,7 +5411,7 @@ elem* callfunc(Loc loc,
54085411
version (none)
54095412
{
54105413
printf("callfunc(directcall = %d, tret = '%s', ec = %p, fd = %p, op = %d)\n",
5411-
directcall, tret.toChars(), ec, fd);
5414+
directcall, tret.toChars(), ec, fd, op);
54125415
printf("ec: "); elem_print(ec);
54135416
if (fd)
54145417
printf("fd = '%s', vtblIndex = %d, isVirtual() = %d\n", fd.toChars(), fd.vtblIndex, fd.isVirtual());
@@ -5521,7 +5524,7 @@ elem* callfunc(Loc loc,
55215524

55225525
/* Convert arguments[] to elems[] in left-to-right order
55235526
*/
5524-
const n = arguments.length;
5527+
const n = arguments.length + (fd !is null && fd.ident is Id.linkerlistApply);
55255528
debug
55265529
elem*[2] elems_array = void;
55275530
else
@@ -5538,8 +5541,64 @@ elem* callfunc(Loc loc,
55385541
const int j = tf.isDstyleVariadic();
55395542
const osx_aapcs64 = irs.target.isAArch64 && irs.target.os == Target.OS.OSX;
55405543

5544+
void linkerlistArg(Expression arg)
5545+
{
5546+
// We are swapping a __linkerlist!T global variable with its respective start+stop symbols as arguments.
5547+
// The transformation takes place at offset 1 of the elems array, and will result in three arguments total.
5548+
5549+
VarExp ve = arg.isVarExp;
5550+
assert(ve !is null);
5551+
VarDeclaration vd = ve.var.isVarDeclaration;
5552+
assert(vd !is null);
5553+
const canBeReadOnly = !vd.type.isMutable;
5554+
5555+
elem* genStartEndExtern(string name, bool noUnderscore = false)
5556+
{
5557+
OutBuffer nameOB;
5558+
mangleToBuffer(vd, nameOB);
5559+
nameOB.writestring(name);
5560+
5561+
Symbol* s = symbol_calloc(nameOB.extractSlice);
5562+
s.Sclass = SC.extern_;
5563+
s.Sfl = FL.extern_;
5564+
s.Stype = type_fake(TYnptr); // always a pointer in size, regardless of linker list type.
5565+
5566+
if (noUnderscore)
5567+
s.Sflags |= SFLnounderscore;
5568+
5569+
return el_ptr(s);
5570+
}
5571+
5572+
switch (config.objfmt)
5573+
{
5574+
case OBJ_MACH:
5575+
elems[1] = genStartEndExtern(canBeReadOnly ? "section$start$__TEXT$" : "section$start$__DATA$", true);
5576+
elems[2] = genStartEndExtern(canBeReadOnly ? "section$end$__TEXT$" : "section$end$__DATA$", true);
5577+
break;
5578+
5579+
case OBJ_ELF:
5580+
elems[1] = genStartEndExtern("__start_");
5581+
elems[2] = genStartEndExtern("__stop_");
5582+
break;
5583+
case OBJ_MSCOFF:
5584+
elems[1] = genStartEndExtern("_start");
5585+
elems[2] = genStartEndExtern("_end");
5586+
break;
5587+
5588+
default:
5589+
assert(0);
5590+
}
5591+
}
5592+
55415593
foreach (const i, arg; *arguments)
55425594
{
5595+
if (i == 1 && fd !is null && fd.ident is Id.linkerlistApply)
5596+
{
5597+
// do not generate symbol for the linker list global
5598+
linkerlistArg(arg);
5599+
break; // This completes the argument list.
5600+
}
5601+
55435602
elem* ea = toElem(arg, irs);
55445603
Parameter param = null;
55455604

@@ -6555,8 +6614,10 @@ elem* toElemCall(CallExp ce, ref IRState irs, elem* ehidden = null)
65556614
if (ce.e1.op == EXP.dotVariable && t1.ty != Tdelegate)
65566615
{
65576616
DotVarExp dve = cast(DotVarExp)ce.e1;
6617+
FuncDeclaration linkerListApply;
65586618

65596619
fd = dve.var.isFuncDeclaration();
6620+
ectype = dve.e1.type.toBasetype();
65606621

65616622
if (auto sle = dve.e1.isStructLiteralExp())
65626623
{
@@ -6565,6 +6626,17 @@ elem* toElemCall(CallExp ce, ref IRState irs, elem* ehidden = null)
65656626
sle.type.size() <= 8) // more efficient than fPIC
65666627
sle.useStaticInit = false; // don't modify initializer, so make copy
65676628
}
6629+
else if (fd.ident is Id.apply)
6630+
{
6631+
if (auto ts = ectype.isTypeStruct)
6632+
{
6633+
if (ts.sym.ident is Id.__linkerlist)
6634+
{
6635+
Dsymbol applyImpl = search_function(ts.sym, Id.linkerlistApply);
6636+
linkerListApply = applyImpl !is null ? applyImpl.isFuncDeclaration : null;
6637+
}
6638+
}
6639+
}
65686640

65696641
// Special cases for constructor RVO
65706642
if (ehidden && fd && fd.isCtorDeclaration())
@@ -6584,11 +6656,22 @@ elem* toElemCall(CallExp ce, ref IRState irs, elem* ehidden = null)
65846656

65856657
ehidden = null;
65866658
}
6659+
else if (linkerListApply !is null)
6660+
{
6661+
// Point ec to the implementation function and update the function/type to match.
6662+
// Append to the arguments list the global variable that contains the linker list.
6663+
6664+
ec = el_ptr(toSymbol(linkerListApply));
6665+
ectype = linkerListApply.type;
6666+
t1 = linkerListApply.type;
6667+
fd = linkerListApply;
6668+
6669+
assert(ce.arguments !is null && ce.arguments.length == 1);
6670+
ce.arguments.push(dve.e1);
6671+
}
65876672
else
65886673
ec = toElem(dve.e1, irs);
65896674

6590-
ectype = dve.e1.type.toBasetype();
6591-
65926675
/* Recognize:
65936676
* [1] ce: ((S __ctmp = initializer),__ctmp).ctor(args)
65946677
* where the left of the . was turned into [2] or [3] for EH_DWARF:

compiler/src/dmd/glue/tocsym.d

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ Symbol* toSymbol(Dsymbol s)
139139
fprintf(stderr, "VarDeclaration.toSymbol(%s) needThis kind: %s\n", vd.toPrettyChars(), vd.kind());
140140
assert(!vd.needThis());
141141

142+
// foreach is already handled before this, so anything thats hitting it is an error case.
143+
// No need to quit after error, at worse this will result in a linker error.
144+
if (vd.isLinkerListDeclaration)
145+
error(vd.loc, "Linker list declarations cannot be accessed except for iteration via foreach statement or appended during CTFE");
146+
142147
import dmd.common.outbuffer : OutBuffer;
143148
OutBuffer buf;
144149
bool isNRVO = false;

compiler/src/dmd/glue/todt.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1486,7 +1486,7 @@ private extern (C++) class TypeInfoDtVisitor : Visitor
14861486
auto tc = d.tinfo.isTypeStruct();
14871487
StructDeclaration sd = tc.sym;
14881488

1489-
if (!sd.members)
1489+
if (!sd.members || sd.ident is Id.__linkerlist)
14901490
return;
14911491

14921492
if (sd.semanticRun < PASS.semantic3done)

0 commit comments

Comments
 (0)