Hi! I've committed following fix, approved by Richard on IRC and Jeff in the PR. process_assignment was assuming that no code needs to be emitted for gimple_assign_cast_p if the mode is the same, which is usually true, except for the case when REDUCE_BIT_FIELD in expand_expr_real_2 needs to mask or shift up/down to adjust for reduced precision.
Bootstrapped/regtested on x86_64-linux and i686-linux, committed to trunk and 4.9. 2014-04-29 Jakub Jelinek <ja...@redhat.com> PR tree-optimization/60971 * tree-tailcall.c (process_assignment): Reject conversions which reduce precision. * c-c++-common/turtore/pr60971.c: New test. --- gcc/tree-tailcall.c.jj 2014-04-17 14:48:59.000000000 +0200 +++ gcc/tree-tailcall.c 2014-04-29 12:13:12.649414120 +0200 @@ -285,9 +285,19 @@ process_assignment (gimple stmt, gimple_ { /* Reject a tailcall if the type conversion might need additional code. */ - if (gimple_assign_cast_p (stmt) - && TYPE_MODE (TREE_TYPE (dest)) != TYPE_MODE (TREE_TYPE (src_var))) - return false; + if (gimple_assign_cast_p (stmt)) + { + if (TYPE_MODE (TREE_TYPE (dest)) != TYPE_MODE (TREE_TYPE (src_var))) + return false; + + /* Even if the type modes are the same, if the precision of the + type is smaller than mode's precision, + reduce_to_bit_field_precision would generate additional code. */ + if (INTEGRAL_TYPE_P (TREE_TYPE (dest)) + && (GET_MODE_PRECISION (TYPE_MODE (TREE_TYPE (dest))) + > TYPE_PRECISION (TREE_TYPE (dest)))) + return false; + } if (src_var != *ass_var) return false; --- gcc/testsuite/c-c++-common/torture/pr60971.c.jj 2014-04-29 11:15:06.448764325 +0200 +++ gcc/testsuite/c-c++-common/torture/pr60971.c 2014-04-29 11:14:49.000000000 +0200 @@ -0,0 +1,34 @@ +/* PR tree-optimization/60971 */ +/* { dg-do run } */ + +#ifndef __cplusplus +#define bool _Bool +#endif + +volatile unsigned char c; + +__attribute__((noinline)) unsigned char +foo (void) +{ + return c; +} + +__attribute__((noinline)) bool +bar (void) +{ + return foo () & 1; +} + +int +main () +{ + c = 0x41; + c = bar (); + if (c != 1) + __builtin_abort (); + c = 0x20; + c = bar (); + if (c != 0) + __builtin_abort (); + return 0; +} Jakub