Skip to content

Make the frontend reusable in one process - #23868

Draft
xoxorwr wants to merge 6 commits into
dlang:masterfrom
xoxorwr:lsp-fixes
Draft

xoxorwr wants to merge 6 commits into
dlang:masterfrom
xoxorwr:lsp-fixes

Conversation

@xoxorwr

@xoxorwr xoxorwr commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

This PR enables the frontend to be run in the same process without leaking memory & state between runs.

No behavior change for normal one shot compilation.

Each commit is independently reviewable.

Disclaimer: This is done with the help of a LLM.

@xoxorwr
xoxorwr marked this pull request as draft September 16, 2026 19:43
Comment thread compiler/src/dmd/tokens.d Outdated
}

// Initialize the identifier pool
shared static this() nothrow

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

????

Modules constructors for this type of work, shouldn't exist, outside of a single central one.

Otherwise things will get missed over time.

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.

True, moved it into Id.initialize()/Id.deinitialize(), and dropped module constructor.

@xoxorwr xoxorwr Sep 17, 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.

Wait, that ctor is pre existing, I was only extracting them so they can be repopulated between runs.

If you can think of a better place for them, please let me know, I can't find where a central way would fit, there is some issues right now with how dmd expects things to init due to how unittests are called.

// Initialize the identifier pool
shared static this() nothrow
{
Identifier.initTable();
foreach (kw; keywords)
{
//printf("keyword[%d] = '%s'\n",kw, Token.tochars[kw].ptr);
Identifier.idPool(Token.tochars[kw], kw);
}
}

@rikkimax
rikkimax requested a review from rainers September 16, 2026 19:48
*/
static IntegerExp createBool(bool b)
{
__gshared IntegerExp trueExp, falseExp;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

you can in theory do this for true/false as done here

*/
static IntegerExp literal(int v)()
{
__gshared IntegerExp theConstant;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

but not this, because it is inside a template.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

though git grep \.literal\! shows only hits for IntegerExp.literal!0 and IntegerExp.literal!1

@thewilsonator thewilsonator left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would try to split this up into separate PRs, much easier to review.

/***********************************************************
*/
// Auto-incremented package tag; reset between sessions.
private __gshared uint packageTag;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

make this a static private member of Package

@rainers rainers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Although Visual D's semantic server tries to work incrementally to some extend (keep syntax-tree of parsing, don't ever release identifiers from the identifier pool, keep types that never change), keeping memory consumption low and free from false pointers is also a considerable advantage. Not sure how well this works for some parts of the semantic server that can currently still run in parallel.

It would be nice if you could split this into a couple of PRs

  • make local __gshared variables resettable
  • remove phobos from frontend.d (not used by Visual D). I suspect some of the reimplemented functions are also available in root/filename.d.
  • add the scratch allocator: not sure if we should add another allocator. Maybe it is easy to add free/realloc support for explicit calls in the exisiting bump-allocator. Note that D array allocation is used and is performed by the (usually disabled) conservative GC to allow requesting array metadata.


static void* xmalloc_noscan(size_t size) pure nothrow
{
if (isArenaEnabled)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Instead of testing isArenaEnabled it seems you can also just enable the GC and initialize arenaBit to zero or non-zero. That way the normal path does not receive an additional if.

if (pred(sv))
{
// Clear the value so the GC-scanned pool doesn't keep it reachable.
sv.value = T.init;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think this works good enough, because the chain of used entries with hash collisions is interrupted. You need a separate marker for "deleted", see https://github.com/rainers/dmd/blob/dmdserver/compiler/src/dmd/root/stringtable.d#L217

import dmd.dtemplate : TemplateDeclaration;

// Cache for object._arrayOp; hoisted so deinitialize can reset it.
private __gshared TemplateDeclaration arrayOpTemplate;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Your extracted variables seem very much in line with what Visual D does, though it abuses the mangling to access them, see https://github.com/rainers/visuald/blob/master/vdc/dmdserver/dmdinit.d#L68
Getting rid of these would be nice.

There are a couple of recursion checks using static nested or similar, though, which might also not get reset on fatal errors.

}

/// Reset the module's global state between analyses.
void deinitialize() nothrow

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Although precedent for deinitialize exists, I'd rather call these methods reset or resetState. Otherwise I would expect to have to call some initialize function on reuse.
Maybe too disruptive for existing methods, though, and inconsistent if only used for new ones.

@0xEAB 0xEAB added AI Generated Code that is generated by an LLM AI. Severity:Enhancement DMD as a library instead of a standalone executable labels Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Generated Code that is generated by an LLM AI. DMD as a library instead of a standalone executable Severity:Enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants