ldc-developers / ldc

The LLVM-based D Compiler.
http://wiki.dlang.org/LDC
Other
1.19k stars 258 forks source link

RISCV32 codegen: small struct passed by value incorrectly (?) #4741

Open denizzzka opened 2 weeks ago

denizzzka commented 2 weeks ago

(I don't know how to provide a demo. Do we have some kind of test environment for riscv32? Any ideas?)

ldc2 compiler flags is used:

--mtriple=riscv32-unknown-newlib-elf
--mattr=+zicsr,+zifencei
--fthread-model=local-exec
-g

Code:

// Can be compiled as betterC code
struct Color {
    ubyte b;
    ubyte g;
    ubyte r;
}

struct S {
    Color col;
}

extern(C) void main()
{
    auto test = Color(255, 0, 0);
    S s;

    func1(&s, test);
}

// To reproduce it should be implemented in C and compiled by GCC
extern(C) void func1(S* a, Color c) @nogc nothrow
{
    // c contains garbage
    assert(c.b == 255);
    assert(c.g == 0);
    assert(c.r == 0);

    a.col = c;
}

ldc2 versions: 1.40.0-beta2 and 1.39.0

denizzzka commented 2 weeks ago

In RISC-V ABI aN registers are dedicated for arguments

a0 0x3fc98dac    a1 0x3fc98d8c    a2 0x000000ff

ldc2 passes test value by placing its address into a1, but gcc code awaits whole struct value placed into a1

As I understand ABI, such structures (size less than XLEN) should be passed through the register

kinke commented 1 week ago

Only the CI-tested x86[_64] and AArch64 ABIs are working (i.e., compatible with C, and DMD's custom x86 ABI); there's a 64-bit RISC-V implementation in https://github.com/ldc-developers/ldc/blob/master/gen/abi/riscv64.cpp, but I have no idea about its quality. The C[++] interop tests coverage in the DMD testsuite is pretty good, but needs cross-compiling and running them on actual hardware or in an emulator.