overflow condition code determined incorrectly after addition on s390x
Host environment
- Operating system: Ubuntu 20.04
- OS/kernel version: Linux 5.11.0-27-generic x86_64
- Architecture: x86_64
- QEMU flavor: qemu-s390x
- QEMU version: 6.1.0
- QEMU command line:
qemu-s390x a.out
Emulated/Virtualized environment
- Operating system: Linux
- OS/kernel version: none
- Architecture: s390x
Description of problem
The following program foo.c foo.c
#include <stdio.h>
int overflow_32 (int x, int y)
{
int sum;
return ! __builtin_add_overflow (x, y, &sum);
}
int overflow_64 (long long x, long long y)
{
long sum;
return ! __builtin_add_overflow (x, y, &sum);
}
int a1 = -2147483648;
int b1 = -2147483648;
long long a2 = -9223372036854775808L;
long long b2 = -9223372036854775808L;
int main ()
{
{
int a = a1;
int b = b1;
printf ("a = 0x%x, b = 0x%x\n", a, b);
printf ("no_overflow = %d\n", overflow_32 (a, b));
}
{
long long a = a2;
long long b = b2;
printf ("a = 0x%llx, b = 0x%llx\n", a, b);
printf ("no_overflow = %d\n", overflow_64 (a, b));
}
}
should print
a = 0x80000000, b = 0x80000000
no_overflow = 0
a = 0x8000000000000000, b = 0x8000000000000000
no_overflow = 0
However, when compiled as an s390x program and executed through qemu 6.1.0 (Linux user-mode), it prints 'no_overflow = 1' twice.
$ s390x-linux-gnu-gcc-10 --version
s390x-linux-gnu-gcc-10 (Ubuntu 10.3.0-1ubuntu1~20.04) 10.3.0
$ s390x-linux-gnu-gcc-10 -static foo.c
$ ~/inst-qemu/6.1.0/bin/qemu-s390x a.out
a = 0x80000000, b = 0x80000000
no_overflow = 1
a = 0x8000000000000000, b = 0x8000000000000000
no_overflow = 1
$ s390x-linux-gnu-gcc-10 -O2 -static foo.c
$ ~/inst-qemu/6.1.0/bin/qemu-s390x a.out
a = 0x80000000, b = 0x80000000
no_overflow = 1
a = 0x8000000000000000, b = 0x8000000000000000
no_overflow = 1
The code generated by 's390x-linux-gnu-gcc-10 -O2' makes use of the 'o' (overflow / ones) condition code:
overflow_64:
lgr %r1,%r2 ;; copy a into %r1
lghi %r2,0
agr %r1,%r3 ;; add a and b
bnor %r14 ;; if no overflow, return %r2 = 0
lghi %r2,1
br %r14 ;; otherwise, return %r2 = 1
Either the bug is in GCC, that is, GCC produces code that uses the CPU's overflow condition code when it shouldn't.
Or the bug is in QEMU, that is, QEMU does not set the overflow condition code correctly.
This can be decided by running the above program on real Linux/s390x hardware (to which I don't have access).
Steps to reproduce
foo.static.s390x (foo.static.s390x is attached, the result of "s390x-linux-gnu-gcc-10 -static -O2 foo.c -o foo.static.s390x")
qemu-s390x foo.static.s390x
Additional information
If the bug is really in QEMU, the attached patch fixes it.
0001-s390x-Fix-determination-of-overflow-condition-code-a.patch