dmd icon indicating copy to clipboard operation
dmd copied to clipboard

DMD miscompiles `real[1]`

Open apz28 opened this issue 7 months ago • 5 comments

void main()
{
    import std.stdio : writeln;
    import std.meta : AliasSeq;

    static foreach (T; AliasSeq!(float, double, real))
    {
        writeln("T=", T.stringof);

        // Static array
        {
            T v = T.sizeof;
            T[1] tsa;
            tsa[0] = v;
            writeln("v=", v, ", tsa=", tsa, ", sizeof=", tsa.sizeof);
        }
    }
}

apz28 avatar Apr 29 '25 13:04 apz28

Works with LDC so likely a backend issue.

Geod24 avatar Apr 29 '25 14:04 Geod24

The problem can be demonstrated with this simplified program:

void main() {
    import std.stdio : writeln;
    real[1] arr = 0;
    writeln(arr);
}

The problem is the output of a static array of the real type with the size of one element.

Additional details:

std.stdio.write() calls formattedWrite(w, "%s", arg)

Signature of formattedWrite:

uint formattedWrite(Writer, Char, Args...)(auto ref Writer w, const scope Char[] fmt, Args args)

Investigation with gdb reveals a stack corruption issue after calling formattedWrite(): the "fmt" argument has address 0x2. This issue does not occur with static arrays of different sizes or types.

Interestingly, adding auto ref to the last argument (auto ref Args args) prevents the SEGFAULT, but formatting remains incorrect: a seemingly some integer is printed, likely due to incorrect array data type handling. However, with an array of size 2, the issue does not occur. All is well if the array has a different data type.

This appears to be a long-standing issue. It reproduces in dmd 2.094.2 but not in dmd 2.092 (I checked with Godbolt).

vindexbit avatar May 02 '25 16:05 vindexbit

Reduced:

import std.stdio : File;

void write(ref File f, real[1])
{
    auto w = f.lockingTextWriter(); // SEGV
}
void main() {
    import std.stdio : stdout;
    real[1] arr = 0;
    write(stdout, arr);
}

ntrel avatar Oct 03 '25 10:10 ntrel

Reduced more:

struct File
{
    void* _p;
}

void write(ref File f, real[1])
{
    // &f is null here
    auto w = f._p; // SEGV
}
void main() {
    real[1] arr = 0;
    File f;
    write(f, arr);
}

ntrel avatar Oct 03 '25 11:10 ntrel

void fn(int* x, real[1] arr) {
    auto y = *x;  // SEGFAULT, x == null
}

void main() {
    real[1] arr = 0;
    int x;
    fn(&x, arr);
}

I think, the crash is caused by an ABI misclassification for the parameter type real[1]. DMD violates the calling convention by not treating real[1] as an aggregate passed via memory.

vindexbit avatar Oct 07 '25 16:10 vindexbit