Skip to content

dmd.expression: Utilize padding between Expression and derived AST nodes to store bitfields - #23604

Draft
ibuclaw wants to merge 1 commit into
dlang:masterfrom
ibuclaw:expression_scratchpad
Draft

dmd.expression: Utilize padding between Expression and derived AST nodes to store bitfields#23604
ibuclaw wants to merge 1 commit into
dlang:masterfrom
ibuclaw:expression_scratchpad

Conversation

@ibuclaw

@ibuclaw ibuclaw commented Aug 15, 2026

Copy link
Copy Markdown
Member
  • Reduces the class instance size of SliceExp from 57 -> 56.
  • Reduces the class instance size of StructLiteralExp from 72 -> 64.

Can be used to make more space reductions too.

  • DsymbolExp, SymbolExp, DotVarExp, DelegateExp: hasOverloads field
  • StringExp, InterpExp, VectorExp, ArrayLiteralExp, AssocArrayLiteralExp: all contain ownByCtfe and bool flags that fit.
  • NewExp, VarExp, DotIdExp, CallExp, DeleteExp, ArrayExp, CommaExp, IndexExp: all contain bool flags that fit.
  • FuncExp, IsExp, CastExp, AssignExp, DefaultInitExp: all contain one or two byte fields that fit.

@ibuclaw
ibuclaw requested a review from rainers August 15, 2026 08:54
@ibuclaw
ibuclaw force-pushed the expression_scratchpad branch from 76e2bd7 to b8eaefa Compare August 15, 2026 08:55
@ibuclaw ibuclaw mentioned this pull request Aug 15, 2026
* Returns:
* The previous value of `stageflags`
*/
extern (D) StageFlags setStageFlag(StageFlags flag)

@ibuclaw ibuclaw Aug 15, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Moving the stageflags field into bitFields could be done in a separate PR. Though it does nothing to reduce the instance size of StructLiteralExp itself.

@ibuclaw
ibuclaw marked this pull request as draft August 15, 2026 09:07
@ibuclaw

ibuclaw commented Aug 15, 2026

Copy link
Copy Markdown
Member Author

FYI @rikkimax this ushort field could also be used directly as the length field for IntegerExp's, if ever the value field becomes a variable-sized array for representing double (aka cent) or wide (aka bigint) integer values - supporting integers up to ushort.max*ulong.sizeof bytes should be big enough for everyone.

…des to store bitfields

- Reduces the class instance size of SliceExp from 57 -> 56.
- Reduces the class instance size of StructLiteralExp from 72 -> 64.

Can be used to make more space reductions too.
- DsymbolExp, SymbolExp, DotVarExp, DelegateExp: hasOverloads field
- StringExp, InterpExp, VectorExp, ArrayLiteralExp,
  AssocArrayLiteralExp: all contain ownByCtfe and bool flags that fit.
- NewExp, VarExp, DotIdExp, CallExp, DeleteExp, ArrayExp, CommaExp,
  IndexExp: all contain bool flags that fit.
- FuncExp, IsExp, CastExp, AssignExp, DefaultInitExp: all contain one or
  two byte fields that fit.
@ibuclaw
ibuclaw force-pushed the expression_scratchpad branch from b8eaefa to 899d5ee Compare August 15, 2026 14:11
@rainers

rainers commented Aug 15, 2026

Copy link
Copy Markdown
Member

#23519 does the same slightly more transparently for all expressions.

@ibuclaw

ibuclaw commented Aug 15, 2026

Copy link
Copy Markdown
Member Author

#23519 does the same slightly more transparently for all expressions.

Oh nice that someone else came to the same realisation. :-)

You can do it for more expressions. But I agree that the 16 byte allocations used by bump-the-pointer may well make it underwhelming.

I was a bit annoyed of having reduced StringExp to a non-multiple of 8 (52->41), and was thinking of doing something similar to what you have there, but I'll just copy-paste it as-is to save some time. :-)

@rainers

rainers commented Aug 15, 2026

Copy link
Copy Markdown
Member

But I agree that the 16 byte allocations used by bump-the-pointer may well make it underwhelming.

#23557 hopefully helps with that, but it needs a host compiler with a new druntime that has #23595

@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.

My PR didn't touch stageFlags, so can add on top of this by also using astNodeBitFields on other Expressions.

EXP op; // to minimize use of dynamic_cast
private:
uint8_t bitFields;
uint16_t astNodeBitFields;

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 made bitFields an ushort instead in #23519, but that causes a padding byte before it. Your versions allows accessing all 21 remaining bits and is probably simpler most of the time. "Appending" to an existing bitfield might be needed with mutliple derivation steps, but I don't remember actually requiring that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think you can get around that with the bitOff parameter.

class Base {
    ushort padding;
}
class Derive1 : Base {
    struct BitFields { ubyte field; }
    mixin(generateBitFields!(BitFields, ushort, "padding");
}
class Derive2 : Derive1 {
    struct BitFields { bool flag; }
    mixin(generateBitFields!(BitFields, ushort, "padding", 8);
}

It's a bit awkward, and I guess none of this is actually unit-tested. :-)

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 think you can get around that with the bitOff parameter.

That's what the appendBitfields mixin in my PR does, but also evaluates the offset automatically from the base class.

@ibuclaw

ibuclaw commented Aug 15, 2026

Copy link
Copy Markdown
Member Author

@rainers FYI, these were the other two potential follow up commits.

@ibuclaw

ibuclaw commented Aug 15, 2026

Copy link
Copy Markdown
Member Author

But I agree that the 16 byte allocations used by bump-the-pointer may well make it underwhelming.

#23557 hopefully helps with that, but it needs a host compiler with a new druntime that has #23595

I don't think that's much of a concern my end at least.

  1. gdc release is always bootstrapped by itself twice (host -> stage1 -> stage2 -> stage3)
  2. if there's a code path that fails with older host compiler, use static if (__VERSION__ < desired-version).

@ibuclaw

ibuclaw commented Aug 15, 2026

Copy link
Copy Markdown
Member Author

I'll leave this in draft for ~72h, so that everyone has enough time to reflect. Also, so I can consider what's going on in #23519 too.

@rainers

rainers commented Aug 15, 2026

Copy link
Copy Markdown
Member
  1. gdc release is always bootstrapped by itself twice (host -> stage1 -> stage2 -> stage3)

Unfortunately, dmd cannot do it ilke that, it has to be built with LDC or GDC to not increase compile times by about 30%.

  1. if there's a code path that fails with older host compiler, use static if (__VERSION__ < desired-version).

Yes, the PR already does something similar, i.e. checks whether BlkAttr.ALIGNMENT_MASK exists.

@Herringway

Herringway commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

It's only a problem if the builds of LDC or GDC used for that never get updated again, which seems unlikely. It might just take a little while for things to improve.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants