[PATCH] Fix sibcall miscompilation on i?86
Jakub Jelinek
jakub@redhat.com
Wed Jul 13 18:38:00 GMT 2005
Hi!
The attached testcase is miscompiled on i?86, e.g. with
-m32 -O2 -march=i386 or -m32 -Os -march=i386 -mtune=i686
(a regression from GCC 3.4.4).
The problem is that check_sibcall_argument_overlap is very simplistic
and detects only cases when a MEM (with non-BLKmode) is used to
read/write with virtual-incoming-args or virtuan-incoming-args PLUS
const_int as address. But on the testcase below, with -march=i386
we have there:
(insn 8 6 9 1 (parallel [
(set (reg:SI 60)
(plus:SI (reg/f:SI 53 virtual-incoming-args)
(const_int 12 [0xc])))
(clobber (reg:CC 17 flags))
]) -1 (nil)
(nil))
(insn 9 8 10 1 (set (reg:SI 61)
(reg/f:SI 53 virtual-incoming-args)) -1 (nil)
(nil))
...
(insn 11 10 12 1 (parallel [
(set (mem:SI (reg:SI 60) [0 S4 A32])
(mem/s:SI (reg:SI 61) [2 x+0 S4 A32]))
(set (reg:SI 60)
(plus:SI (reg:SI 60)
(const_int 4 [0x4])))
(set (reg:SI 61)
(plus:SI (reg:SI 61)
(const_int 4 [0x4])))
(use (reg:SI 19 dirflag))
]) -1 (nil)
(nil))
...
(but very well could have a :BLK mem rep;movsb etc.) and
with -Os there are memcpy function calls.
Doing address propagation and handling function calls etc.
in check_sibcall_argument_overlap sounds difficult and there is still
the question what to do about :BLK mems, so I'm just
checking the arg->value before it is copied using emit_push_insn.
Ok to commit if bootstrap/regtesting succeeds?
2005-07-13 Jakub Jelinek <jakub@redhat.com>
* calls.c (store_one_arg): Check for sibling call MEM arguments
from already clobbered incoming argument area.
* gcc.c-torture/execute/20050713-1.c: New test.
--- gcc/calls.c.jj 2005-07-02 02:27:09.000000000 +0200
+++ gcc/calls.c 2005-07-13 20:05:36.000000000 +0200
@@ -4062,6 +4062,38 @@ store_one_arg (struct arg_data *arg, rtx
stack_arg_under_construction--;
}
+ /* Check for overlap with already clobbered argument area. */
+ if ((flags & ECF_SIBCALL) && MEM_P (arg->value))
+ {
+ int i = -1;
+ unsigned int k;
+ rtx x = arg->value;
+
+ if (XEXP (x, 0) == current_function_internal_arg_pointer)
+ i = 0;
+ else if (GET_CODE (XEXP (x, 0)) == PLUS
+ && XEXP (XEXP (x, 0), 0) ==
+ current_function_internal_arg_pointer
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
+ i = INTVAL (XEXP (XEXP (x, 0), 1));
+ else
+ i = -1;
+
+ if (i >= 0)
+ {
+#ifdef ARGS_GROW_DOWNWARD
+ i = -i - arg->locate.size.constant;
+#endif
+ for (k = 0; k < arg->locate.size.constant; k++)
+ if (i + k < stored_args_map->n_bits
+ && TEST_BIT (stored_args_map, i + k))
+ {
+ sibcall_failure = 1;
+ break;
+ }
+ }
+ }
+
/* Don't allow anything left on stack from computation
of argument to alloca. */
if (flags & ECF_MAY_BE_ALLOCA)
--- gcc/testsuite/gcc.c-torture/execute/20050713-1.c.jj 2005-07-13 20:23:42.000000000 +0200
+++ gcc/testsuite/gcc.c-torture/execute/20050713-1.c 2005-07-13 20:23:27.000000000 +0200
@@ -0,0 +1,56 @@
+/* Test that sibling call is not used if there is an argument overlap. */
+
+extern void abort (void);
+
+struct S
+{
+ int a, b, c;
+};
+
+int
+foo2 (struct S x, struct S y)
+{
+ if (x.a != 3 || x.b != 4 || x.c != 5)
+ abort ();
+ if (y.a != 6 || y.b != 7 || y.c != 8)
+ abort ();
+ return 0;
+}
+
+int
+foo3 (struct S x, struct S y, struct S z)
+{
+ foo2 (x, y);
+ if (z.a != 9 || z.b != 10 || z.c != 11)
+ abort ();
+ return 0;
+}
+
+int
+bar2 (struct S x, struct S y)
+{
+ return foo2 (y, x);
+}
+
+int
+bar3 (struct S x, struct S y, struct S z)
+{
+ return foo3 (y, x, z);
+}
+
+int
+baz3 (struct S x, struct S y, struct S z)
+{
+ return foo3 (y, z, x);
+}
+
+int
+main (void)
+{
+ struct S a = { 3, 4, 5 }, b = { 6, 7, 8 }, c = { 9, 10, 11 };
+
+ bar2 (b, a);
+ bar3 (b, a, c);
+ baz3 (c, a, b);
+ return 0;
+}
Jakub
More information about the Gcc-patches
mailing list