From 899d5eeb9ddfe63c7a33fb54a029aa5a8cd4e826 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Sat, 15 Aug 2026 10:31:19 +0200 Subject: [PATCH] dmd.expression: Utilize padding between Expression and derived AST nodes 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. --- compiler/include/dmd/expression.h | 11 +++++------ compiler/src/dmd/dinterpret.d | 9 +++------ compiler/src/dmd/expression.d | 24 +++++++++++++++++++++--- compiler/src/dmd/hdrgen.d | 3 +-- compiler/src/dmd/initsem.d | 3 +-- compiler/src/dmd/inline.d | 3 +-- compiler/src/dmd/optimize.d | 3 +-- compiler/src/dmd/visitor/package.d | 3 +-- compiler/src/dmd/visitor/postorder.d | 3 +-- 9 files changed, 35 insertions(+), 27 deletions(-) diff --git a/compiler/include/dmd/expression.h b/compiler/include/dmd/expression.h index 6d135576e86b..1ca78961501f 100644 --- a/compiler/include/dmd/expression.h +++ b/compiler/include/dmd/expression.h @@ -91,7 +91,10 @@ class Expression : public ASTNode Type *type; // !=NULL means that semantic() has been run Loc loc; // file location EXP op; // to minimize use of dynamic_cast +private: uint8_t bitFields; + uint16_t astNodeBitFields; +public: bool parens() const; bool parens(bool v); @@ -397,8 +400,6 @@ class AssocArrayLiteralExp final : public Expression class StructLiteralExp final : public Expression { public: - uint8_t bitFields; - // if this is true, use the StructDeclaration's init symbol bool useStaticInit() const; bool useStaticInit(bool v); @@ -413,7 +414,8 @@ class StructLiteralExp final : public Expression * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' * (with infinite recursion) of this expression. */ - uint8_t stageflags; + uint8_t stageflags() const; + uint8_t stageflags(uint8_t v); StructDeclaration *sd; // which aggregate this is for Expressions *elements; // parallels sd->fields[] with NULL entries for fields to skip @@ -882,10 +884,7 @@ class SliceExp final : public UnaExp bool lowerIsLessThanUpper(bool v); bool arrayop() const; // an array operation, rather than a slice bool arrayop(bool v); -private: - uint8_t bitFields; -public: SliceExp *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } diff --git a/compiler/src/dmd/dinterpret.d b/compiler/src/dmd/dinterpret.d index 9d8cfbcd347c..8e0a792470b2 100644 --- a/compiler/src/dmd/dinterpret.d +++ b/compiler/src/dmd/dinterpret.d @@ -6568,8 +6568,7 @@ private Expression scrubReturnValue(Loc loc, Expression e) sle.ownedByCtfe = OwnedBy.code; if (!(sle.stageflags & StructLiteralExp.StageFlags.scrub)) { - const old = sle.stageflags; - sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion + const old = sle.setStageFlag(StructLiteralExp.StageFlags.scrub); // prevent infinite recursion if (auto ex = scrubArray(sle.elements, true)) return ex; sle.stageflags = old; @@ -6648,8 +6647,7 @@ private Expression scrubCacheValue(Expression e) sle.ownedByCtfe = OwnedBy.cache; if (!(sle.stageflags & StructLiteralExp.StageFlags.scrub)) { - const old = sle.stageflags; - sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion + const old = sle.setStageFlag(StructLiteralExp.StageFlags.scrub); // prevent infinite recursion if (auto ex = scrubArrayCache(sle.elements)) return ex; sle.stageflags = old; @@ -6725,8 +6723,7 @@ private Expression copyRegionExp(Expression e) { if (1 || !(sle.stageflags & StructLiteralExp.StageFlags.scrub)) { - const old = sle.stageflags; - sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion + const old = sle.setStageFlag(StructLiteralExp.StageFlags.scrub); // prevent infinite recursion copyArray(sle.elements); sle.stageflags = old; } diff --git a/compiler/src/dmd/expression.d b/compiler/src/dmd/expression.d index 091ba5262ff4..1831a8e11a90 100644 --- a/compiler/src/dmd/expression.d +++ b/compiler/src/dmd/expression.d @@ -135,6 +135,11 @@ extern (C++) abstract class Expression : ASTNode import dmd.common.bitfields; mixin(generateBitFields!(BitFields, ubyte)); + // This is the remaining padding between Expression and all of its derived + // classes, it is used as a space to hold per-expression bitfields, flags, + // or a value of these derived AST nodes to save on space. + private ushort astNodeBitFields; + extern (D) this(Loc loc, EXP op) scope @safe { //printf("Expression::Expression(op = %d) this = %p\n", op, this); @@ -1441,10 +1446,10 @@ extern (C++) final class StructLiteralExp : Expression bool useStaticInit; /// if this is true, use the StructDeclaration's init symbol bool isOriginal = false; /// used when moving instances to indicate `this is this.origin` OwnedBy ownedByCtfe = OwnedBy.code; + StageFlags stageflags; } import dmd.common.bitfields; - mixin(generateBitFields!(BitFields, ubyte)); - StageFlags stageflags; + mixin(generateBitFields!(BitFields, ushort, "astNodeBitFields")); StructDeclaration sd; /// which aggregate this is for Expressions* elements; /// parallels sd.fields[] with null entries for fields to skip @@ -1508,6 +1513,19 @@ extern (C++) final class StructLiteralExp : Expression return exp; } + /** Mark `stageflags` with the bit `flag` + * Params: + * flag = StageFlag to set + * Returns: + * The previous value of `stageflags` + */ + extern (D) StageFlags setStageFlag(StageFlags flag) + { + const old = stageflags; + stageflags = StageFlags(old | flag); + return old; + } + override void accept(Visitor v) { v.visit(this); @@ -2730,7 +2748,7 @@ extern (C++) final class SliceExp : UnaExp bool arrayop; // an array operation, rather than a slice } import dmd.common.bitfields : generateBitFields; - mixin(generateBitFields!(BitFields, ubyte)); + mixin(generateBitFields!(BitFields, ushort, "astNodeBitFields")); /************************************************************/ extern (D) this(Loc loc, Expression e1, IntervalExp ie) @safe diff --git a/compiler/src/dmd/hdrgen.d b/compiler/src/dmd/hdrgen.d index 6b43796bcba1..26646c0effbb 100644 --- a/compiler/src/dmd/hdrgen.d +++ b/compiler/src/dmd/hdrgen.d @@ -2482,8 +2482,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.put(""); else { - const old = e.stageflags; - e.stageflags |= StructLiteralExp.StageFlags.toCBuffer; + const old = e.setStageFlag(StructLiteralExp.StageFlags.toCBuffer); argsToBuffer(e.elements, buf, hgs); e.stageflags = old; } diff --git a/compiler/src/dmd/initsem.d b/compiler/src/dmd/initsem.d index 1c90c1a5e960..ee21035e47ff 100644 --- a/compiler/src/dmd/initsem.d +++ b/compiler/src/dmd/initsem.d @@ -1670,8 +1670,7 @@ private bool hasNonConstPointers(Expression e) { if (!(se.stageflags & StructLiteralExp.StageFlags.searchPointers)) { - const old = se.stageflags; - se.stageflags |= StructLiteralExp.StageFlags.searchPointers; + const old = se.setStageFlag(StructLiteralExp.StageFlags.searchPointers); bool ret = checkArray(se.elements); se.stageflags = old; return ret; diff --git a/compiler/src/dmd/inline.d b/compiler/src/dmd/inline.d index 67fef61bea12..bedd01801097 100644 --- a/compiler/src/dmd/inline.d +++ b/compiler/src/dmd/inline.d @@ -1735,8 +1735,7 @@ public: //printf("StructLiteralExp.inlineScan()\n"); if (e.stageflags & StructLiteralExp.StageFlags.inlineScan) return; - const old = e.stageflags; - e.stageflags |= StructLiteralExp.StageFlags.inlineScan; + const old = e.setStageFlag(StructLiteralExp.StageFlags.inlineScan); arrayInlineScan(e.elements); e.stageflags = old; } diff --git a/compiler/src/dmd/optimize.d b/compiler/src/dmd/optimize.d index 7de96ec52fe0..0aa88f0de4a2 100644 --- a/compiler/src/dmd/optimize.d +++ b/compiler/src/dmd/optimize.d @@ -372,8 +372,7 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) { if (e.stageflags & StructLiteralExp.StageFlags.optimize) return; - const old = e.stageflags; - e.stageflags |= StructLiteralExp.StageFlags.optimize; + const old = e.setStageFlag(StructLiteralExp.StageFlags.optimize); if (e.elements) { foreach (ref ex; (*e.elements)[]) diff --git a/compiler/src/dmd/visitor/package.d b/compiler/src/dmd/visitor/package.d index a862ae2b49b1..a0422182d7db 100644 --- a/compiler/src/dmd/visitor/package.d +++ b/compiler/src/dmd/visitor/package.d @@ -152,8 +152,7 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor alias flag = ASTCodegen.StructLiteralExp.StageFlags.toCBuffer; if (!(e.stageflags & flag)) { - const old = e.stageflags; - e.stageflags |= flag; + const old = e.setStageFlag(flag); foreach (el; *e.elements) if (el) el.accept(this); diff --git a/compiler/src/dmd/visitor/postorder.d b/compiler/src/dmd/visitor/postorder.d index ceb8955424f6..e16a9e3c9095 100644 --- a/compiler/src/dmd/visitor/postorder.d +++ b/compiler/src/dmd/visitor/postorder.d @@ -179,8 +179,7 @@ public: { if (e.stageflags & StructLiteralExp.StageFlags.apply) return; - const old = e.stageflags; - e.stageflags |= StructLiteralExp.StageFlags.apply; + const old = e.setStageFlag(StructLiteralExp.StageFlags.apply); doCond(e.elements.peekSlice()) || applyTo(e); e.stageflags = old; }