Bug 96270 - [8/9/10/11 Regression] stdarg malfunction with -m32 and -Os
Summary: [8/9/10/11 Regression] stdarg malfunction with -m32 and -Os
Status: RESOLVED DUPLICATE of bug 88240
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: 9.3.0
: P2 normal
Target Milestone: 8.5
Assignee: Richard Biener
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2020-07-21 16:06 UTC by D. Richard Hipp
Modified: 2020-08-04 09:29 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work: 6.4.0
Known to fail: 10.1.0, 11.0, 7.1.0, 8.4.0, 9.2.0
Last reconfirmed: 2020-07-21 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description D. Richard Hipp 2020-07-21 16:06:08 UTC
gcc version 9.3.0 (Ubuntu 9.3.0-10ubuntu2), the Default install on ubuntu 20.04

A long long int varargs parameter goes in as 0xfff7ffffffff but comes out as 0xffffffffffff. In other words, bit 35 is getting set somehow.  (The "7" changes to an "f".) This only happens with -m32 and one of -O2 or -Os.  It works correctly with -O0, -O1 or without -m32.

Test program "b1.c" shown below.  To compile and run:

     gcc -Os -m32 b1.c && ./a.out 1

The test program:

/********************************************/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
struct val {
  union {
    double r;
    long long int i;
  } u;
  unsigned char type;
};
void f1(const char *b, ...){
  long long int v;
  va_list ap;
  va_start(ap, b);
  v = va_arg(ap, long long int);
  printf("IN f1:   %016llx\n", v);
  va_end(ap);
}
void f2(struct val *p){
  if( p->type & 0x01 ){
    f1("1",p->u.i);
  }else{
    f1("3",p->u.r);
  }
}
int main(int argc, char **argv){
  int i;
  struct val x;
  x.u.i = -2251799813685249LL;
  for(i=1; i<argc; i++){
    x.type = atoi(argv[i]);
    printf("in main: %016llx\n", x.u.i);
    f2(&x);
  }
  return 0;
}
/**********************************/
Comment 1 Mikael Pettersson 2020-07-21 16:41:59 UTC
I can reproduce on x86_64 with gcc-7/8/9/10/11 -Os -m32, disappears with -O2.  gcc-11 -fsanitize=undefined gives no diagnostic and also masks the error.  gcc-6 and older work (tested back to 4.1.2).  I cannot reproduce on powerpc64 or m68k.
Comment 2 Jakub Jelinek 2020-07-21 16:55:04 UTC
Changed with r241329.
And the difference between that and the previous revision is
@@ -26,25 +26,25 @@ f2 (struct val * p)
 {
   unsigned char _1;
   unsigned char _8;
-  long long int pretmp_11;
-  double _12;
+  double pretmp_11;
+  long long int _12;
 
   <bb 2>:
   _1 = p_6(D)->type;
   _8 = _1 & 1;
-  pretmp_11 = p_6(D)->u.i;
+  pretmp_11 = p_6(D)->u.r;
   if (_8 != 0)
     goto <bb 3>;
   else
     goto <bb 4>;
 
   <bb 3>:
-  f1 ("1", pretmp_11); [tail call]
+  _12 = VIEW_CONVERT_EXPR<long long int>(pretmp_11);
+  f1 ("1", _12); [tail call]
   goto <bb 5>;
 
   <bb 4>:
-  _12 = VIEW_CONVERT_EXPR<double>(pretmp_11);
-  f1 ("3", _12); [tail call]
+  f1 ("3", pretmp_11); [tail call]
 
   <bb 5>:
   return;

Using a floating point type to hold the pretmp when it also contains non-floating point values (or other floating point types) looks wrong.
Comment 3 Richard Biener 2020-07-22 07:53:17 UTC
I think we have some duplicates about this PRE issue but I also thought this
specific one is already mitigated by 1dc00a8ec9aeba86b74b16bff6f171824bb7b4a1

Now here code hoisting comes into play and with the other outstanding PRE
issues the issue is it operates based on values thus we do not actually
know the expression we operate on and VN value-numbers u.r the same as u.i.
Comment 4 Richard Biener 2020-07-22 08:28:42 UTC
Note the complication is that u.r is of type 'double' and thus DFmode
which has a mode precision of 64.  So this is once again the x86 backend
using float loads/stores that are value-changing (IIRC the ABI specifies
passing 'double' as long double.

So the IL "lies" here and "fixing" this would pessimize targets that do not
lie for no good reason.

(insn 13 12 14 4 (set (mem:DI (pre_dec:SI (reg/f:SI 7 sp)) [0  S8 A64])
        (subreg:DI (reg:DF 84 [ pretmp_11 ]) 0)) "t.c":21:5 50 {*pushdi2}
     (expr_list:REG_ARGS_SIZE (const_int 12 [0xc])
        (nil)))

(what's this REG_ARGS_SIZE?)

eventually gets after LRA

(insn 35 6 7 2 (set (mem/c:DF (plus:SI (reg/f:SI 6 bp)
                (const_int -16 [0xfffffffffffffff0])) [7 %sfp+-8 S8 A64])
        (reg:DF 8 st [orig:84 pretmp_11 ] [84])) "t.c":21:5 135 {*movdf_internal}
     (nil))
...

(insn 13 12 14 3 (set (mem:DI (pre_dec:SI (reg/f:SI 7 sp)) [0  S8 A64])
        (mem/c:DI (plus:SI (reg/f:SI 6 bp)
                (const_int -16 [0xfffffffffffffff0])) [7 %sfp+-8 S8 A64])) "t.c":21:5 50 {*pushdi2}

clearly inheritance could somehow have picked up

(insn 6 3 35 2 (set (reg:DF 8 st [orig:84 pretmp_11 ] [84])
        (mem/j:DF (reg/v/f:SI 0 ax [orig:86 p ] [86]) [4 p_6(D)->u.r+0 S8 A32])) "t.c":21:5 135 {*movdf_internal}
     (nil))

instead but it really looks like the targets fault.

It's unfortunately not possible to fix the code hoisting side without
also pessimizing valid CSE where desirable.  Maybe we need a new target
hook like targetm.can_ldst_unaltered_bits (machine_mode) and x86
has to say no for all x87 modes but long double?  It also means that
spilling would alter values (but IIRC we always _spill_ via long double).
Comment 5 Richard Biener 2020-08-04 09:29:31 UTC
Duplicate of PR88240.
Comment 6 Richard Biener 2020-08-04 09:29:53 UTC
.

*** This bug has been marked as a duplicate of bug 88240 ***