diff -uNr gcc-4061.orig/gcc/fortran/trans-intrinsic.c gcc-4061/gcc/fortran/trans-intrinsic.c --- gcc-4061.orig/gcc/fortran/trans-intrinsic.c 2004-11-11 10:14:36.000000000 +0900 +++ gcc-4061/gcc/fortran/trans-intrinsic.c 2005-06-02 00:03:50.000000000 +0900 @@ -1781,7 +1781,11 @@ tree arg; tree arg2; tree type; + tree utype; tree tmp; + tree width; + tree num_bits; + tree cond; tree lshift; tree rshift; @@ -1789,23 +1793,33 @@ arg2 = TREE_VALUE (TREE_CHAIN (arg)); arg = TREE_VALUE (arg); type = TREE_TYPE (arg); + utype = gfc_unsigned_type (type); + + width = fold (build1 (ABS_EXPR, TREE_TYPE (arg2), arg2)); /* Left shift if positive. */ lshift = build2 (LSHIFT_EXPR, type, arg, arg2); - /* Right shift if negative. This will perform an arithmetic shift as - we are dealing with signed integers. Section 13.5.7 allows this. */ - tmp = build1 (NEGATE_EXPR, TREE_TYPE (arg2), arg2); - rshift = build2 (RSHIFT_EXPR, type, arg, tmp); + /* Right shift if negative. + We convert to an unsigned type because we want a logical shift. + The standard doesn't define the case of shifting negative + numbers, and we try to be compatible with other compilers, most + notably g77, here. */ + rshift = fold_convert (type, build2 (RSHIFT_EXPR, utype, + convert (type, arg), width)); + + tmp = fold( build2 (GE_EXPR, boolean_type_node, arg2, + build_int_cst (TREE_TYPE (arg2), 0))); + tmp = fold( build3 (COND_EXPR, type, tmp, lshift, rshift)); + + /* The Fortran standard allows shift widths <= BIT_SIZE(I), whereas + gcc requires a shift width < BIT_SIZE(I), so we have to catch this + special case. */ + num_bits = build_int_cst (TREE_TYPE (arg2), TYPE_PRECISION (type)); + cond = fold( build2 (GE_EXPR, boolean_type_node, width, num_bits)); - tmp = build2 (GT_EXPR, boolean_type_node, arg2, - convert (TREE_TYPE (arg2), integer_zero_node)); - rshift = build3 (COND_EXPR, type, tmp, lshift, rshift); - - /* Do nothing if shift == 0. */ - tmp = build2 (EQ_EXPR, boolean_type_node, arg2, - convert (TREE_TYPE (arg2), integer_zero_node)); - se->expr = build3 (COND_EXPR, type, tmp, arg, rshift); + se->expr = fold( build3 (COND_EXPR, type, cond, + build_int_cst (type, 0), tmp)); } /* Circular shift. AKA rotate or barrel shift. */