www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - [core.reflect] Detect Padding in structs or classes

reply Stefan Koch <uplink.coder googlemail.com> writes:
There has been a PR to introduce `-vpadding` into dmd which warns 
on padding in structs.
This can actually be trivially done by core reflect.

```D
// detect padding in aggregates
import core.reflect.reflect;
import core.reflect.transitiveVisitor;

struct PadDetctedTuple
{
     string aggregateName;
     ulong number_of_pad_bytes;
}

struct S
{
     ubyte a; // <--- is at offset 0
     ubyte b; // <--- is at offset 1
     // two bytes of padding
     uint d; // <--- is at offset 4
}

static assert(detectPadding(node)[0] == PadDetctedTuple("S", 2)); 
// yes we detected that S has 2 bytes of padding

static assert(S.init.a.offsetof == 0);
static assert(S.init.b.offsetof == 1);
static assert(S.init.d.offsetof == 4);

static immutable sc = currentScope();
static immutable node = nodeFromName("S", ReflectFlags.Members, 
sc);


pragma(msg, detectPadding(node));


PadDetctedTuple[] detectPadding(const Node n)
{
     class PadDetectVisitor : TransitiveVisitor
     {
         alias visit = TransitiveVisitor.visit;

         override void visit(AggregateDeclaration ad)
         {
             ulong lastOffset;
             ulong lastSize;
             ulong wastedBytes;
             foreach(f;ad.fields)
             {
                 wastedBytes += ((f.offset - lastOffset) - 
lastSize);
                 lastOffset = f.offset;
                 lastSize = f.type.size;
             }
             if (wastedBytes)
                 pads ~= PadDetctedTuple(ad.name, wastedBytes);
         }

         PadDetctedTuple[] pads = null;
     }

     scope padDetector = new PadDetectVisitor();
     (cast()n).accept(padDetector);

     return padDetector.pads;
}
```
Oct 01 2021
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Friday, 1 October 2021 at 17:35:24 UTC, Stefan Koch wrote:
 There has been a PR to introduce `-vpadding` into dmd which 
 warns on padding in structs.
 This can actually be trivially done by core reflect.
Ah dang. This code will be bogus for classes as we need it iterate the base classes for this Which we can do but not when visiting the superclass of both Struct and Class Declarations. We need to specialize for classes. And add the following code to the visitor ```D override void visit(ClassDeclaration cd) { ulong lastOffset; ulong lastSize; ulong wastedBytes; // for classes we need to keep a stack of bases // let's go! ClassDeclaration[] baseStack = [cd]; for(auto base = cd.base; base; base = base.base) { baseStack ~= base; } foreach_reverse(base;baseStack) { foreach(f;base.fields) { wastedBytes += ((f.offset - lastOffset) - lastSize); lastOffset = f.offset; lastSize = f.type.size; } } if (wastedBytes) pads ~= PadDetctedTuple(cd.name, wastedBytes); } ``` And once we did that ```D static immutable stdio_n = nodeFromName("std_stdio", ReflectFlags.Members | ReflectFlags.MemberRecursion, sc); pragma(msg, detectPadding(stdio_n)); ``` will result in `[PadDetctedTuple("_IO_FILE", 8LU), PadDetctedTuple("_IO_FILE", 8LU), PadDetctedTuple("StdioException", 16LU)]` So yes the StdioExecption is somewhat fatty. But not 76 bytes of waste ;)
Oct 01 2021
parent Stefan Koch <uplink.coder googlemail.com> writes:
On Friday, 1 October 2021 at 18:30:35 UTC, Stefan Koch wrote:
 On Friday, 1 October 2021 at 17:35:24 UTC, Stefan Koch wrote:
 There has been a PR to introduce `-vpadding` into dmd which 
 warns on padding in structs.
 This can actually be trivially done by core reflect.
Ah dang. This code will be bogus for classes as we need it iterate the base classes for this Which we can do but not when visiting the superclass of both Struct and Class Declarations. We need to specialize for classes. And add the following code to the visitor ```D override void visit(ClassDeclaration cd) { ulong lastOffset; ```
Ah double dang. This code needs to be fixed. For classes we start at offset 16 because of class-metadata like the vtbl and monitor being prepended. So it should read `ulong lastOffset = 16`
Oct 01 2021
prev sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Friday, 1 October 2021 at 17:35:24 UTC, Stefan Koch wrote:
 There has been a PR to introduce `-vpadding` into dmd which 
 warns on padding in structs.
 This can actually be trivially done by core reflect.
And there are some results. ``` diff --git a/std/file.d b/std/file.d index 741656d..0d1c17c 100644 --- a/std/file.d +++ b/std/file.d -4578,7 +4578,7 version (Posix) system unittest /** * Dictates directory spanning policy for $(D_PARAM dirEntries) (see below). */ -enum SpanMode +enum SpanMode : ubyte { /** Only spans one directory. */ shallow, -4635,15 +4635,15 enum SpanMode private struct DirIteratorImpl { safe: - SpanMode _mode; + DirEntry _cur; + DirHandle[] _stack; + DirEntry[] _stashed; //used in depth first mode // Whether we should follow symlinked directories while iterating. // It also indicates whether we should avoid functions which call // stat (since we should only need lstat in this case and it would // be more efficient to not call stat in addition to lstat). bool _followSymlink; - DirEntry _cur; - DirHandle[] _stack; - DirEntry[] _stashed; //used in depth first mode + SpanMode _mode; ``` This patch to phobos saves 4 bytes or so ;) It's not much but it comes practically for free :-) With my `core.reflect` extension it was just a questing of asking in between which fields the padding was. You could save another byte by folding `_followSymlink` into the SpanMode flags. But that and the PR to Phobos are left as an exercise for the reader.
Oct 02 2021