Bug 9969 – dmd ABI mistake (cfloat static array initialisation)
Status
RESOLVED
Resolution
WORKSFORME
Severity
critical
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
Linux
Creation time
2013-04-20T08:22:40Z
Last change time
2019-11-03T04:41:41Z
Assigned to
No Owner
Creator
John Colvin
Comments
Comment #0 by john.loughran.colvin — 2013-04-20T08:22:40Z
test.d:
import std.stdio;
void main()
{
cfloat a[2];
}
$ dmd test.d
$ ./test
Segmentation fault (core dumped)
The problem is here:
0000000000000000 <_Dmain>:
0: 55 push rbp
1: 48 8b ec mov rbp,rsp
4: 48 83 ec 10 sub rsp,0x10
8: 48 83 ec 08 sub rsp,0x8
c: 48 be 02 00 00 00 00 movabs rsi,0x2
13: 00 00 00
16: ff 35 00 00 00 00 push QWORD PTR [rip+0x0] # 1c <_Dmain$
18: R_X86_64_PC32 .rodata-0x4
1c: 48 8d 7d f0 lea rdi,[rbp-0x10]
20: e8 00 00 00 00 call 25 <_Dmain+0x25>
21: R_X86_64_PC32 _memset64-0x4
25: 48 83 c4 10 add rsp,0x10
29: 31 c0 xor eax,eax
2b: c9 leave
2c: c3 ret
2d: 0f 1f 00 nop DWORD PTR [rax]
0000000000000000 <_memset64>:
0: 55 push rbp
1: 48 8b ec mov rbp,rsp
4: 48 83 ec 10 sub rsp,0x10
8: 49 89 f8 mov r8,rdi
b: 49 89 f1 mov r9,rsi
e: 48 89 f8 mov rax,rdi
11: 49 8d 0c d0 lea rcx,[r8+rdx*8]
15: 4c 3b c1 cmp r8,rcx
18: 73 0c jae 26 <_memset64+0x26>
1a: 4d 89 08 mov QWORD PTR [r8],r9
1d: 49 83 c0 08 add r8,0x8
21: 49 39 c8 cmp r8,rcx
24: 72 f4 jb 1a <_memset64+0x1a>
26: 48 8b e5 mov rsp,rbp
29: 5d pop rbp
2a: c3 ret
long *_memset64(long *p, long value, size_t count)
_memset64 is extern(C). Dmd passes "p" correctly, but then tries to pass "value" on the stack and passes the value for "count" as the second argument. _memset64 attempts to use rdx as the count, which is garbage. Hence, segfault.
Comment #1 by maxim — 2013-04-20T09:18:30Z
Looks like an issue 9449.
I think the problem isn't "ABI mistake" (or a codegen bug as often mentioned here - many people who can spot an error from asm output tend to blame backend) but absence of crosstalk between how dmd assumes memset functions look like and how they are actually defined in druntime.
Comment #2 by john.loughran.colvin — 2013-04-20T10:14:12Z
(In reply to comment #1)
> Looks like an issue 9449.
>
> I think the problem isn't "ABI mistake" (or a codegen bug as often mentioned
> here - many people who can spot an error from asm output tend to blame backend)
> but absence of crosstalk between how dmd assumes memset functions look like and
> how they are actually defined in druntime.
I'm confused as to how trying to pass a variable on the stack to an extern(C) function with less than 6 arguments on linux x64 is not a codegen error of some sorts. It's using the wrong calling convention...
Comment #3 by john.loughran.colvin — 2013-04-20T10:16:49Z
(In reply to comment #2)
> (In reply to comment #1)
> > Looks like an issue 9449.
> >
> > I think the problem isn't "ABI mistake" (or a codegen bug as often mentioned
> > here - many people who can spot an error from asm output tend to blame backend)
> > but absence of crosstalk between how dmd assumes memset functions look like and
> > how they are actually defined in druntime.
>
> I'm confused as to how trying to pass a variable on the stack to an extern(C)
> function with less than 6 arguments on linux x64 is not a codegen error of some
> sorts. It's using the wrong calling convention...
s/codegen/ABI
Comment #4 by maxim — 2013-04-20T11:12:35Z
(In reply to comment #2)
> (In reply to comment #1)
> > Looks like an issue 9449.
> >
> > I think the problem isn't "ABI mistake" (or a codegen bug as often mentioned
> > here - many people who can spot an error from asm output tend to blame backend)
> > but absence of crosstalk between how dmd assumes memset functions look like and
> > how they are actually defined in druntime.
>
> I'm confused as to how trying to pass a variable on the stack to an extern(C)
> function with less than 6 arguments on linux x64 is not a codegen error of some
> sorts. It's using the wrong calling convention...
I do not think that if you can see that compiler passes arguments wrong you may conclude that it is codegen or ABI bug. That is what actually often happens: somebody see error in asm output and blames backend while the problem is really in frontend. From what I know after debugging issue 9449 there is likely error in scope of AssignExp::toElem (https://github.com/D-Programming-Language/dmd/blob/master/src/e2ir.c#L2688) (or setArray()) which does not handle properly cases like this. The problem is in frontend assumption that druntime defines memset function as taking dynamic array. The backend just does what it is told to generate.
Comment #5 by john.loughran.colvin — 2013-04-20T15:18:54Z
(In reply to comment #4)
> (In reply to comment #2)
> > (In reply to comment #1)
> > > Looks like an issue 9449.
> > >
> > > I think the problem isn't "ABI mistake" (or a codegen bug as often mentioned
> > > here - many people who can spot an error from asm output tend to blame backend)
> > > but absence of crosstalk between how dmd assumes memset functions look like and
> > > how they are actually defined in druntime.
> >
> > I'm confused as to how trying to pass a variable on the stack to an extern(C)
> > function with less than 6 arguments on linux x64 is not a codegen error of some
> > sorts. It's using the wrong calling convention...
>
> I do not think that if you can see that compiler passes arguments wrong you may
> conclude that it is codegen or ABI bug. That is what actually often happens:
> somebody see error in asm output and blames backend while the problem is really
> in frontend. From what I know after debugging issue 9449 there is likely error
> in scope of AssignExp::toElem
> (https://github.com/D-Programming-Language/dmd/blob/master/src/e2ir.c#L2688)
> (or setArray()) which does not handle properly cases like this. The problem is
> in frontend assumption that druntime defines memset function as taking dynamic
> array. The backend just does what it is told to generate.
I concede that there may well be a frontend problem as well, but in my limited experience a properly functioning backend should never emit the code we see here.
System V dictates that the first 6 integer/pointer arguments passed in RDI, RSI, RDX, RCX, R8, and R9, the first 8 floating point arguments are passed in XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 and XMM7 an any further arguments are passed on the stack.
If the compiler emits code passing 3 arguments to an extern(C) function, 2 in ESI and EDI and 1 on the stack, then it's a problem relating to the ABI. The actual signature/behaviour of the target function is irrelevant, AFAIK there is no possible System V compliant target function for which that argument passing would be correct.
Comment #6 by maxim — 2013-04-21T04:33:59Z
(In reply to comment #5)
> I concede that there may well be a frontend problem as well, but in my limited
> experience a properly functioning backend should never emit the code we see
> here.
What backend generates isn't determined only by it, bugs in frontend may lead to incorrect instructions. Look at:
extern(C) int printf(const char*, ... );
extern(C) long *_memset64(long *p, long value, size_t count);
void main()
{
long[2] arr = void;
_memset64(arr.ptr, 1, 2);
printf("%d %d\n", arr[0], arr[1]);
//cfloat[2] bug;
}
and _Dmain. In this case call to memset is constructed by CallExp and things are done right, but in case of cfloat[2] call is constructed by AssignExp and setArray (https://github.com/D-Programming-Language/dmd/blob/master/src/e2ir.c#L757) and invalid code is generated.
Anyway, this is critical.
Comment #7 by iamthewilsonator — 2019-11-03T04:41:41Z
extern(C) int printf(const char*, ... );
extern(C) long *_memset64(long *p, long value, size_t count);
void main()
{
cfloat[2] arr = void;
_memset64(cast(long*)arr.ptr, 1, 2);
printf("%d %d\n", *(cast(long*)&arr[0]), *(cast(long*)&arr[1]));
}
works on current as does
extern(C) int printf(const char*, ... );
extern(C) long *_memset64(cfloat *p, long value, size_t count);
void main()
{
cfloat[2] arr = void;
_memset64(arr.ptr, 1, 2);
printf("%d %d\n", *(cast(long*)&arr[0]), *(cast(long*)&arr[1]));
}
as does
void main()
{
cfloat[2] a;
}