Bug 34281

Summary: [4.3 Regression] ARM: varargs broken with 64bit parameters.
Product: gcc Reporter: David Daney <daney>
Component: targetAssignee: Jakub Jelinek <jakub>
Status: RESOLVED FIXED    
Severity: normal CC: gcc-bugs
Priority: P3 Keywords: wrong-code
Version: 4.3.0   
Target Milestone: 4.3.0   
URL: http://gcc.gnu.org/ml/gcc-patches/2007-12/msg00602.html
Host: Target: armv5tl-none-linuxeabi
Build: Known to work: 3.4.3
Known to fail: 4.3.0 Last reconfirmed: 2007-12-13 14:38:11

Description David Daney 2007-11-29 01:55:48 UTC
With gcc version 4.3.0 20071024 (experimental) [trunk revision 129615]

configured:

../trunk/configure --enable-languages=c,c++,java --disable-static --enable-__cxa_atexit --prefix=/home/daney/mvl_ti/gcj --with-gmp=/home/daney/mp --with-mpfr=/home/daney/mp --with-sysroot=/home/daney/mvl_ti/mv_pro_4.0/montavista/pro/devkit/arm/v5t_le/target --with-float=soft --disable-java-awt --without-x --disable-jvmpi --disable-sjlj-exceptions --target=armv5tl-montavista-linuxeabi

If the last fixed argument in a va_arg function is 64 bits bad things happen.

Here is the test program:

--------8<---------
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>

class A
{
private:
    int foo;
public:
    A(int a) 
    {
        foo = a;
    }
    
    void f(int64_t j,...);
    void f2(int64_t j, int l,...);
    void f1(int64_t j);
    void g(int j,...);
    void h(int j, va_list ap);
};

void
A::f(int64_t j,...)
{
    va_list ap;
    va_start(ap, j);

    int k = va_arg(ap, int);
    

    h(5, ap);

    va_end(ap);
    
    printf("A::f %d %lld, sizeof(j) = %d\n", k, j, sizeof(j));
}

void
A::f2(int64_t j, int l, ...)
{
    va_list ap;
    va_start(ap, l);

    int k = va_arg(ap, int);
    

    h(5, ap);

    va_end(ap);
    
    printf("A::f2 %d %lld, sizeof(j) = %d\n", k, j, sizeof(j));
}

void
A::f1(int64_t j)
{
    printf("A::f1 %lld, sizeof(j) = %d\n", j, sizeof(j));
}

void
A::g(int j,...)
{
    va_list ap;
    va_start(ap, j);

    int k = va_arg(ap, int);
    

    h(7, ap);

    va_end(ap);
    
    printf("A::f %d %d\n", k, j);
}

void
A::h(int j, va_list ap)
{
    int k;
    
    do {
        k = va_arg(ap, int);
        printf("   A::h %d - %d\n", j, k);
        
    }
    while (k >= 0);
}

int
main(int argc, char *argv[])
{
    A *a = new A(9);

    a->f(300, 4,6,8,10,-1);
    a->f2(300, 99, 4,6,8,10,-1);
    a->g(400, 3,5,7,9,11,13,-1);
    a->f1(500);
    
}
-------8<----------

Expected output:
$ ./vargs 
   A::h 5 - 6
   A::h 5 - 8
   A::h 5 - 10
   A::h 5 - -1
A::f 4 300, sizeof(j) = 8
   A::h 5 - 6
   A::h 5 - 8
   A::h 5 - 10
   A::h 5 - -1
A::f2 4 300, sizeof(j) = 8
   A::h 7 - 5
   A::h 7 - 7
   A::h 7 - 9
   A::h 7 - 11
   A::h 7 - 13
   A::h 7 - -1
A::f 3 400
A::f1 500, sizeof(j) = 8

Output on armv5tl-none-linuxeabi:

# ./vargs 
   A::h 5 - 4
   A::h 5 - 6
   A::h 5 - 8
   A::h 5 - 10
   A::h 5 - -1
A::f 0 1288490188809, sizeof(j) = 8
   A::h 5 - 6
   A::h 5 - 8
   A::h 5 - 10
   A::h 5 - -1
A::f2 4 300, sizeof(j) = 8
   A::h 7 - 5
   A::h 7 - 7
   A::h 7 - 9
   A::h 7 - 11
   A::h 7 - 13
   A::h 7 - -1
A::f 3 400
A::f1 500, sizeof(j) = 8

Note that output 'A::f 0 1288490188809,...' is incorrect.
Comment 1 David Daney 2007-12-11 23:20:35 UTC
Verified that it works on MontaVista build 3.4.3.

Still failing on:
armv5tl-montavista-linuxeabi-g++ (GCC) 4.3.0 20071211 (experimental) [trunk revision 130777]
Comment 2 Jakub Jelinek 2007-12-13 14:38:11 UTC
Not sure this should count as a regression, since in 3.4 this was using different ABI.  If you compile with -mabi=atpcs which matches 3.4, it will work even in 4.3.
Anyway, I think:
--- gcc/config/arm/arm.c.jj     2007-12-11 00:23:29.000000000 +0100
+++ gcc/config/arm/arm.c        2007-12-13 15:26:01.000000000 +0100
@@ -17765,14 +17765,20 @@ arm_output_load_gr (rtx *operands)
 
 static void
 arm_setup_incoming_varargs (CUMULATIVE_ARGS *cum,
-                           enum machine_mode mode ATTRIBUTE_UNUSED,
-                           tree type ATTRIBUTE_UNUSED,
+                           enum machine_mode mode,
+                           tree type,
                            int *pretend_size,
                            int second_time ATTRIBUTE_UNUSED)
 {
+  int nregs = cum->nregs;
+  if (nregs & 1
+      && ARM_DOUBLEWORD_ALIGN
+      && arm_needs_doubleword_align (mode, type))
+    nregs++;
+
   cfun->machine->uses_anonymous_args = 1;
-  if (cum->nregs < NUM_ARG_REGS)
-    *pretend_size = (NUM_ARG_REGS - cum->nregs) * UNITS_PER_WORD;
+  if (nregs < NUM_ARG_REGS)
+    *pretend_size = (NUM_ARG_REGS - nregs) * UNITS_PER_WORD;
 }
 
 /* Return nonzero if the CONSUMER instruction (a store) does not need

should fix this, let me poke around a little bit some more and add a testcase.
I'll need some volunteer to bootstrap/regtest it though, no arms here...
Comment 3 Jakub Jelinek 2007-12-27 08:32:30 UTC
Subject: Bug 34281

Author: jakub
Date: Thu Dec 27 08:31:54 2007
New Revision: 131196

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=131196
Log:
	PR target/34281
	* config/arm/arm.c (arm_setup_incoming_varargs): If last named
	argument needs double word alignment and cum->nregs is odd, account
	for the inserted padding.

	* gcc.c-torture/execute/20071213-1.c: New test.

Added:
    trunk/gcc/testsuite/gcc.c-torture/execute/20071213-1.c
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/config/arm/arm.c
    trunk/gcc/testsuite/ChangeLog

Comment 4 Jakub Jelinek 2007-12-27 08:34:14 UTC
http://gcc.gnu.org/ml/gcc-testresults/2007-12/msg01313.html

is with the patch I've just checked in, without the arm.c part there were
additional FAILs for the newly added testcase, other than that identical
results.