Bug 16274 – The curses of debugging: short argument passed in 16-bit register, against ABI
Status
RESOLVED
Resolution
FIXED
Severity
major
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
Mac OS X
Creation time
2016-07-13T04:49:17Z
Last change time
2021-05-30T10:30:08Z
Keywords
backend, pull, wrong-code
Assigned to
No Owner
Creator
Luís Marques
Comments
Comment #0 by luis — 2016-07-13T04:49:17Z
On my system (OS X 10.11.5 (15F34), 64-bit), if you compile (with DMD) and run the following program you'll get an executable that behaves incorrectly about half of the executions:
extern(C)
{
void* initscr();
int start_color();
int init_pair(short pair, short f, short b);
int attron(int attrs);
int mvprintw(int y, int x, const(char)* fmt, ...);
int attroff(int attrs);
int getch();
int endwin();
int refresh();
}
enum
{
COLOR_BLACK = 0,
COLOR_RED = 1,
COLOR_GREEN = 2,
COLOR_YELLOW = 3,
COLOR_BLUE = 4,
COLOR_MAGENTA = 5,
COLOR_CYAN = 6,
COLOR_WHITE = 7
}
int COLOR_PAIR(int n)
{
return n << 8;
}
void main()
{
initscr();
start_color();
init_pair(1, COLOR_WHITE, COLOR_BLUE);
int pair = COLOR_PAIR(1);
attron(pair);
mvprintw(1, 1, "Test");
attroff(pair);
refresh();
getch();
endwin();
}
$ rdmd -L-lcurses test.d
If all is well, you'll see the text "Test" in white text on a blue background. If the bug is exercised then you won't see any text. Sometimes it takes quite a few number of executions until it starts or stops exhibiting the problem.
This reduced text case was taken very painstakingly from a large codebase, where changing unrelated things around would make the problem come and go --- probably; it was sometimes hard to tell, given the non-deterministic bug.
Currently, it seems that this program always works fine when using LDC, although my vague recollection is that this wasn't true when I had last looked at this issue (which was before the latest LDC). That might be because of the following difference in compilation:
LDC:
__Dmain:
subq $0x38, %rsp
callq _initscr
movq %rax, 0x28(%rsp)
callq _start_color
movl $0x1, %edi <---
movl $0x7, %esi <---
movl $0x4, %edx <---
movl %eax, 0x24(%rsp)
callq _init_pair
DMD:
__Dmain:
pushq %rbp
movq %rsp, %rbp
subq $0x10, %rsp
callq _initscr
callq _start_color
movw $0x4, %dx <---
movw $0x7, %si <---
movw $0x1, %di <---
callq _init_pair
And indeed, if you change the code to the following the problem goes away:
void main()
{
initscr();
start_color();
asm
{
xor EDX, EDX;
xor ESI, ESI;
xor EDI, EDI;
}
init_pair(1, COLOR_WHITE, COLOR_BLUE);
int pair = COLOR_PAIR(1);
attron(pair);
mvprintw(1, 1, "Test");
attroff(pair);
refresh();
getch();
endwin();
}
I guess the ABI requires the whole register to be used for a 16-bit value?
The docs for my system say "The OS X x86-64 function calling conventions are the same as the function calling conventions described in System V Application Binary Interface AMD64 Architecture Processor Supplement, found at http://people.freebsd.org/~obrien/amd64-elf-abi.pdf. See that document for details."
In that document I found the following:
• Arguments of types (signed and unsigned) _Bool, char, short, int,
long, long long, and pointers are in the INTEGER class
(...)
2. If the class is INTEGER, the next available register of the sequence %rdi,
%rsi, %rdx, %rcx, %r8 and %r9 is used
So I guess this is a DMD code generation bug?
Comment #1 by luis — 2016-07-13T04:58:14Z
(I'm using the latest DMD, v2.071.1, although this problem has existed for quite a while)
Comment #2 by bugzilla — 2020-08-29T08:35:50Z
A smaller version:
extern(C) int init_pair(short pair, short f, short b);
void test() { init_pair(1, 2, 3); }
which generates:
_D4testQfFZv:
push RBP
mov RBP,RSP
mov 8D,3
mov DX,2
mov CX,1
sub RSP,020h
call init_pair
add RSP,020h
pop RBP
ret
Indeed, you are correct in that the upper 16 bits of the register is not set. But I believe this correct code generation. The problem may be in the implementation of init_pair(), which is assuming the upper 16 bits are zero.
Comment #3 by bugzilla — 2020-08-29T08:39:00Z
gcc, however, does indeed set the upper 16 bits to zero. I suppose dmd should behave the same way, regardless of the spec.
Comment #4 by bugzilla — 2020-08-29T09:07:34Z
Curiously, I implemented init_pair() and it turns out that gcc sign extends each parameter before using it.
So this problem does depend on what's going on with init_pair's implementation.
Comment #5 by dlang-bot — 2020-08-30T10:04:22Z
@WalterBright created dlang/dmd pull request #11651 "fix Issue 16274 - The curses of debugging: short argument passed in 1…" fixing this issue:
- fix Issue 16274 - The curses of debugging: short argument passed in 16-bit register, against ABI
https://github.com/dlang/dmd/pull/11651
Comment #6 by dlang-bot — 2020-09-10T10:24:57Z
@ibuclaw created dlang/dmd pull request #11719 "fix Issue 16274 - short argument passed in 16 bit register" fixing this issue:
- fix Issue 16274 - short argument passed in 16 bit register
https://github.com/dlang/dmd/pull/11719
Comment #7 by dlang-bot — 2021-05-30T10:30:08Z
dlang/dmd pull request #11651 "fix Issue 16274 - short argument passed in 16 bit register" was merged into master:
- 00271210bfe8fe9dc4d4bfe17045b17b1621df59 by Walter Bright:
fix Issue 16274 - short argument passed in 16-bit register, against ABI
https://github.com/dlang/dmd/pull/11651