[patch] c-typeck.c: Fix c/19606.

Kazu Hirata kazu@codesourcery.com
Mon Jan 16 14:19:00 GMT 2006


Hi,

Attached is a patch to fix PR c/19606.  Consider

signed char a = -4;

int
foo (void)
{
  return ((unsigned int) (signed int) a) / 2LL;
}

The C front end transforms this into 

  (int) (a / 2)

where the division is done in signed char, which in turn causes foo to
return 0xfffffffe instead of 0x7ffffffe, assuming int is 32 bit wide,
and the value of 'a' stays unchanged.

The problem is that build_binary_op is overly aggressive in narrowing
the type of a computation.  The patch fixes the problem by not
narrowing TRUNC_DIV_EXPR and TRUNC_MOD_EXPR if narrowing changes the
signedness of a division.

Although my patch does fix the PR and a variant of it with
TRUNC_MOD_EXPR, I am not sure if I've caught all the dangerous cases.

Tested on x86_pc-linux-gnu.  OK to apply?

Kazu Hirata

2006-01-16  Kazu Hirata  <kazu@codesourcery.com>

	PR c/19606.
	* c-typeck.c (build_binary_op): Do not narrowing
	TRUNC_DIV_EXPR and TRUNC_MOD_EXPR if narrowing changes the
	signedness of a division.

2006-01-16  Kazu Hirata  <kazu@codesourcery.com>

	PR c/19606.
	* gcc.c-torture/execute/pr19606.c: New.

Index: c-typeck.c
===================================================================
--- c-typeck.c	(revision 109721)
+++ c-typeck.c	(working copy)
@@ -8167,7 +8167,10 @@ build_binary_op (enum tree_code code, tr
 		   && (type
 		       = c_common_signed_or_unsigned_type (unsigned0,
 							   TREE_TYPE (arg0)),
-		       int_fits_type_p (arg1, type)))
+		       int_fits_type_p (arg1, type))
+		   /* Don't change the signedness of a division.  */
+		   && ((code != TRUNC_DIV_EXPR && code != TRUNC_MOD_EXPR)
+		       || (TYPE_UNSIGNED (result_type) != TYPE_UNSIGNED (type))))
 	    result_type = type;
 	}
 
Index: testsuite/gcc.c-torture/execute/pr19606.c
===================================================================
--- /dev/null	2005-09-01 09:08:28.063949816 -0700
+++ testsuite/gcc.c-torture/execute/pr19606.c	2006-01-15 14:41:56.490361111 -0800
@@ -0,0 +1,33 @@
+/* PR c/19606
+   The C front end used to shorten the type of a division even when
+   the signedness changes.  Make sure that won't happen.  */
+
+signed char a = -4;
+
+int
+foo (void)
+{
+  return ((unsigned int) (signed int) a) / 2LL;
+}
+
+int
+bar (void)
+{
+  return ((unsigned int) (signed int) a) % 5LL;
+}
+
+int
+main (void)
+{
+  int r;
+
+  r = foo ();
+  if (r != ((unsigned int) (signed int) (signed char) -4) / 2LL)
+    abort ();
+
+  r = bar ();
+  if (r != ((unsigned int) (signed int) (signed char) -4) % 5LL)
+    abort ();
+
+  exit (0);
+}



More information about the Gcc-patches mailing list