Skip to content

Adopt a TypeInfo emission/codegen strategy similar to LDC's - #23832

Open
kinke wants to merge 8 commits into
dlang:masterfrom
kinke:ti_emission
Open

kinke wants to merge 8 commits into
dlang:masterfrom
kinke:ti_emission

Conversation

@kinke

@kinke kinke commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

The main change is that TypeInfo emission is triggered from effective codegen in the glue layer only, independent from the AST nodes, and no TypeInfo declarations being appended to module members anymore (previously done as side effect of calling getTypeInfoType() (!)).

This primarily means that CTFE-only references of TypeInfos don't lead to the emission of these TypeInfos anymore, avoiding unexpected further link-time dependencies like #23731. And that comes naturally, without brittle isSpeculativeType() logic.

TypeInfo_Class instances (for classes and interfaces, __ClassZ/__InterfaceZ) are still emitted once along with the class/interface declaration. All other (non-builtin, i.e., not druntime-defined) TypeInfos are now lazily emitted (when referenced from codegen somewhere) into one of the referencing object files being generated, per compiler run. This is different to LDC, which lazily emits them into every referencing object file (e.g., better inline-ability for code using TypeInfo indirections). (And don't worry, the duplicates are folded to a single symbol at link-time.)

This adheres to the pay-as-you-go principle - if no codegen'd code needs a struct/interface/enum TypeInfo, it won't be emitted at all anymore (previously, as part of codegenning the type declaration).

@github-actions

github-actions Bot commented Sep 12, 2026

Copy link
Copy Markdown

DMD perf check

Metric Base PR Δ
compile Phobos codegen (instr) 1,473.0 M 1,469.8 M -0.218%
compile vibe.d (instr) 15,115.2 M 15,093.1 M -0.146%
dmd binary size (stripped) 6.87 MB 6.86 MB -0.17%
All measurements
Metric Base PR Δ
compile hello.d (instr) 213.4 M 213.4 M +0.006%
compile hello.d -O -release (instr) 231.7 M 231.7 M +0.004%
compile Phobos (instr) 5,122.6 M 5,121.8 M -0.017%
compile Phobos codegen (instr) 1,473.0 M 1,469.8 M -0.218%
compile vibe.d (instr) 15,115.2 M 15,093.1 M -0.146%
dmd binary size (stripped) 6.87 MB 6.86 MB -0.17%
hello binary size (stripped) 0.72 MB 0.72 MB 0.00%
peak RSS (compile hello.d) 43.09 MB 43.15 MB +0.13%
peak RSS (compile Phobos) 618.5 MB 617.2 MB -0.21%
peak RSS (compile vibe.d) 1917 MB 1912 MB -0.28%
compile dmd itself (wall) 12.3 s 12.2 s -0.81%
compile hello.d (wall) 69.1 ms 67.6 ms -2.10%
compile Phobos (wall) 1,557 ms 1,555 ms -0.10%

4b61038 vs merge-base 6aba772 · about these metrics

else // MARS
{
// FIXME: is there a nicer place for this bool, outside the frontend?
bool hadCodegen; // whether it was codegen'd already

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea if there is a better place, I'm not familiar with DMD enough.

Comment thread compiler/src/dmd/semantic3.d
echo "Expected 1 '${mangled_sym}' symbol definition but got: ${num_typeinfo_syms}"
num_typeinfo_syms=$(nm --defined-only ${objfile} | (grep -F ${mangled_sym} || true) | wc -l)
if [[ "${num_typeinfo_syms}" -gt 1 ]]; then
echo "Expected at most 1 '${mangled_sym}' symbol definition but got: ${num_typeinfo_syms}"

@kinke kinke Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still TODO. With the emission moving to the glue layer, this particular TypeInfo isn't emitted anymore. So my first attempt was to change the test, checking that no TypeInfos are defined at all in the object file for a basically-empty module, thus in a way also testing #23731 (the testcase there depends on Phobos and cannot be used as-is without dustmite reduction). But turns out that there's still a bunch of other TypeInfos being defined. :/

Allowing 0 occurrences of a particular TypeInfo here, as currently done, would make it hard to notice the test generally breaking at one point (e.g., reworked mangling or whatever). So this is just temporary, to get this test to pass for now. I guess I'll have to dig into the remaining TypeInfo definitions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, these unexpected TypeInfo definitions come from -allinst leading to unexpected templates codegen. Without -allinst, there are no defined TypeInfos for this existing test case - but neither with master, so this test case isn't really suited for testing #23731.

if nm --defined-only ${obj_decl} | grep -F TypeInfo; then
echo "Expected no non-TypeInfo_Class TypeInfo definitions along with the declarations!"
exit 1
fi

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v2.113.0 additionally emitted all 3 other TypeInfos into the declarations object file (TypeInfo_{Struct,Interface,Enum}).

if nm --defined-only ${obj_refs} | grep -E '__(Class|Interface)Z'; then
echo "Expected no TypeInfo_Class definitions for referencing object file!"
exit 1
fi

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v2.113.0 did NOT emit the TypeInfo_Interface, but the 3 other ones.

And also emitted these 3 TypeInfos for CTFE-only references - that's the biggest change here.

@kinke kinke changed the title Handle non-class TypeInfo emission in the glue layer Adopt a TypeInfo emission/codegen strategy similar to LDC's Sep 14, 2026
@kinke
kinke marked this pull request as ready for review September 14, 2026 16:31
@kinke
kinke requested a review from ibuclaw as a code owner September 14, 2026 16:31
@kinke

kinke commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

@ibuclaw: Do you already do something like this, or do you currently use isSpeculativeType() etc., which I'm removing here?

@ibuclaw

ibuclaw commented Sep 14, 2026

Copy link
Copy Markdown
Member

I implement is_speculative in C++. Would love to remove it. Over the last fews years all attempts to let gcc discard unused symbols has resulted in undefined references at link-time. Some of these frontend changes might be the cause for that.

…ng the needs-codegen check multiple times for ClassInfos and builtin TypeInfos]
@rainers

rainers commented Sep 17, 2026

Copy link
Copy Markdown
Member

LGTM.

I wonder how much we can avoid the pessimistic semanticTypeInfo calls now that dynamic and associative array operations are templated and use typeid explicitly if necessary instead of passing TypeInfo as a runtime argument only requested from the glue layer.
Are LDC and GDC also using the templated versions? I remember LDC still calling rt.lifetime._d_newclass instead of core.lifetime._d_newclassT some time ago, is this still true?

@kinke

kinke commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

I wonder how much we can avoid the pessimistic semanticTypeInfo calls now that dynamic and associative array operations are templated and use typeid explicitly if necessary instead of passing TypeInfo as a runtime argument only requested from the glue layer.

Yeah, there might be room to avoid some of these after this, but I'm not really familiar with those frontend pieces. Maybe CTFE still requires some extra knowledge about the TypeInfos, potentially requiring extra semantic if the type declaration doesn't get sema3 in that compiler run.

Are LDC and GDC also using the templated versions? I remember LDC still calling rt.lifetime._d_newclass instead of core.lifetime._d_newclassT some time ago, is this still true?

AFAIK, it's only the NewExp::lowering which is still ignored by LDC (edit: for classes and dynamic arrays) - primarily to

  • not totally break the GC2Stack pass (would need to be adapted to the templates without TypeInfos),
  • still allow the dmd.root.rmem link-time overrides to work (=> use bump-pointer allocation for class instances, not the GC),
  • and because we only really use LDC-specific _d_allocclass, with the compiler initializing the instance right afterwards in inline code instead (incl. setting the vptr explicitly, for better devirtualization opportunities)

@rainers

rainers commented Sep 17, 2026

Copy link
Copy Markdown
Member

Maybe CTFE still requires some extra knowledge about the TypeInfos, potentially requiring extra semantic if the type declaration doesn't get sema3 in that compiler run.

I don't think so, vtinfo is only used by the glue layer.

  • not totally break the GC2Stack pass (would need to be adapted to the templates without TypeInfos),

ok

  • still allow the dmd.root.rmem link-time overrides to work (=> use bump-pointer allocation for class instances, not the GC),

When called via GC.malloc it ends up in the BumpPointerGC that forwards to the bump-pointer allocation. This also takes advantage of recent alignment optimizations (with a recent host compiler).

@kinke

kinke commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

it ends up in the BumpPointerGC

Ah, I hadn't seen your #23287 yet - nice, thx for that, one less blocker.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants