1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
runtime: 0.346
instruction: 0.345
syscall: 0.309
overflow condition code determined incorrectly after subtraction on s390x
Description of problem:
Paul Eggert found this bug, just by taking a look at the file `qemu/target/s390x/tcg/cc_helper.c`.
The following program
[foo.c](/uploads/c1f425684fd661c4437950d7d8ddf31d/foo.c)
```
#include <stdio.h>
int overflow_32 (int x, int y)
{
int sum;
return __builtin_sub_overflow (x, y, &sum);
}
int overflow_64 (long long x, long long y)
{
long sum;
return __builtin_sub_overflow (x, y, &sum);
}
int a1 = 0;
int b1 = -2147483648;
long long a2 = 0L;
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 = 0x0, b = 0x80000000
no_overflow = 0
a = 0x0, 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 = 0x0, b = 0x80000000
no_overflow = 1
a = 0x0, 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 = 0x0, b = 0x80000000
no_overflow = 1
a = 0x0, 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
sgr %r1,%r3 ;; subtract b from a
bnor %r14 ;; if no overflow, return %r2 = 0
lghi %r2,1
br %r14 ;; otherwise, return %r2 = 1
```
The condition code and the overflow bit are defined in the z/Architecture Principles of Operation (POP) http://publibfi.boulder.ibm.com/epubs/pdf/dz9zr011.pdf page 7-5 / 7-6 / 7-388 : "In mathematical terms, signed addition and subtraction produce a fixed-point overflow when the result is outside the range of representation for signed binary integers."
I conclude that the bug is in QEMU: QEMU does not set the overflow condition code correctly.
Steps to reproduce:
[foo.static.s390x](/uploads/e4b79b019db590f3a4b13cac41e57ba6/foo.static.s390x)
(the result of "s390x-linux-gnu-gcc-10 -static -O2 foo.c -o foo.static.s390x")
1. `qemu-s390x foo.static.s390x`
Additional information:
The attached patch fixes it.
[0002-s390x-Fix-determination-of-overflow-condition-code-a.patch](/uploads/8d414f84fe0ed36bf07bd28f5e7836ab/0002-s390x-Fix-determination-of-overflow-condition-code-a.patch)
|