This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] Fix x86_64 va_arg gimplification (PR target/44575)
- From: Jakub Jelinek <jakub at redhat dot com>
- To: Uros Bizjak <ubizjak at gmail dot com>, Jan Hubicka <jh at suse dot cz>
- Cc: gcc-patches at gcc dot gnu dot org
- Date: Mon, 21 Jun 2010 18:14:16 +0200
- Subject: [PATCH] Fix x86_64 va_arg gimplification (PR target/44575)
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
Hi!
While emit_store_group/emit_load_group* at the RTL level
properly handle stores or loads from PARALLEL of registers where
the sum of sizes is larger than the type being copied by
doing convert_modes etc., ix86_gimplify_va_arg uses the modes
without considering it and thus in some cases stores to the temporary
more than it should.
Following patch fixes it by handling the case of the last word copy
using smaller mode, or if there is no such mode (hear I mean say 3 bytes),
using memcpy.
Bootstrapped/regtested on x86_64-linux and i686-linux. Ok for trunk?
2010-06-21 Jakub Jelinek <jakub@redhat.com>
PR target/44575
* config/i386/i386.c (ix86_gimplify_va_arg): When copying
va_arg from a set of register save slots into a temporary,
if the container is bigger than type size, do the copying
using smaller mode or using memcpy.
* gcc.c-torture/execute/pr44575.c: New test.
--- gcc/config/i386/i386.c.jj 2010-06-14 07:44:22.000000000 +0200
+++ gcc/config/i386/i386.c 2010-06-21 14:34:17.000000000 +0200
@@ -7267,7 +7267,7 @@ ix86_gimplify_va_arg (tree valist, tree
}
if (need_temp)
{
- int i;
+ int i, prev_size = 0;
tree temp = create_tmp_var (type, "va_arg_tmp");
/* addr = &temp; */
@@ -7279,13 +7279,29 @@ ix86_gimplify_va_arg (tree valist, tree
rtx slot = XVECEXP (container, 0, i);
rtx reg = XEXP (slot, 0);
enum machine_mode mode = GET_MODE (reg);
- tree piece_type = lang_hooks.types.type_for_mode (mode, 1);
- tree addr_type = build_pointer_type (piece_type);
- tree daddr_type = build_pointer_type_for_mode (piece_type,
- ptr_mode, true);
+ tree piece_type;
+ tree addr_type;
+ tree daddr_type;
tree src_addr, src;
int src_offset;
tree dest_addr, dest;
+ int cur_size = GET_MODE_SIZE (mode);
+
+ if (prev_size + cur_size > size)
+ {
+ cur_size = size - prev_size;
+ mode = mode_for_size (cur_size * BITS_PER_UNIT, MODE_INT, 1);
+ if (mode == BLKmode)
+ mode = QImode;
+ }
+ piece_type = lang_hooks.types.type_for_mode (mode, 1);
+ if (mode == GET_MODE (reg))
+ addr_type = build_pointer_type (piece_type);
+ else
+ addr_type = build_pointer_type_for_mode (piece_type, ptr_mode,
+ true);
+ daddr_type = build_pointer_type_for_mode (piece_type, ptr_mode,
+ true);
if (SSE_REGNO_P (REGNO (reg)))
{
@@ -7300,14 +7316,26 @@ ix86_gimplify_va_arg (tree valist, tree
src_addr = fold_convert (addr_type, src_addr);
src_addr = fold_build2 (POINTER_PLUS_EXPR, addr_type, src_addr,
size_int (src_offset));
- src = build_va_arg_indirect_ref (src_addr);
dest_addr = fold_convert (daddr_type, addr);
dest_addr = fold_build2 (POINTER_PLUS_EXPR, daddr_type, dest_addr,
size_int (INTVAL (XEXP (slot, 1))));
- dest = build_va_arg_indirect_ref (dest_addr);
+ if (cur_size == GET_MODE_SIZE (mode))
+ {
+ src = build_va_arg_indirect_ref (src_addr);
+ dest = build_va_arg_indirect_ref (dest_addr);
- gimplify_assign (dest, src, pre_p);
+ gimplify_assign (dest, src, pre_p);
+ }
+ else
+ {
+ tree copy
+ = build_call_expr (implicit_built_in_decls[BUILT_IN_MEMCPY],
+ 3, dest_addr, src_addr,
+ size_int (cur_size));
+ gimplify_and_add (copy, pre_p);
+ }
+ prev_size += cur_size;
}
}
--- gcc/testsuite/gcc.c-torture/execute/pr44575.c.jj 2010-06-21 14:43:19.000000000 +0200
+++ gcc/testsuite/gcc.c-torture/execute/pr44575.c 2010-06-21 14:44:20.000000000 +0200
@@ -0,0 +1,49 @@
+/* PR target/44575 */
+
+#include <stdarg.h>
+
+int fails = 0;
+struct S { float a[3]; };
+struct S a[5];
+
+void
+check (int z, ...)
+{
+ struct S arg, *p;
+ va_list ap;
+ int j = 0, k = 0;
+ int i;
+ va_start (ap, z);
+ for (i = 2; i < 4; ++i)
+ {
+ p = 0;
+ j++;
+ k += 2;
+ switch ((z << 4) | i)
+ {
+ case 0x12:
+ case 0x13:
+ p = &a[2];
+ arg = va_arg (ap, struct S);
+ break;
+ default:
+ ++fails;
+ break;
+ }
+ if (p && p->a[2] != arg.a[2])
+ ++fails;
+ if (fails)
+ break;
+ }
+ va_end (ap);
+}
+
+int
+main ()
+{
+ a[2].a[2] = -49026;
+ check (1, a[2], a[2]);
+ if (fails)
+ abort ();
+ return 0;
+}
Jakub