+2004-07-08 Joseph S. Myers <jsm@polyomino.org.uk>
+ Neil Booth <neil@daikokuya.co.uk>
+
+ PR c/2511
+ PR c/3325
+ * c-decl.c (finish_struct): Ensure bit-fields are given the
+ correct type.
+ * c-common.c (c_common_signed_or_unsigned_type): For C, require
+ the precision to match as well as the mode.
+ * expr.c (reduce_to_bit_field_precision): New function.
+ (expand_expr_real_1): Reduce expressions of bit-field type to
+ proper precision.
+ * langhooks.h (reduce_bit_field_operations): New hook.
+ * langhooks-def.h (LANG_HOOKS_REDUCE_BIT_FIELD_OPERATIONS):
+ Define.
+ * c-lang.c, objc/objc-lang.c
+ (LANG_HOOKS_REDUCE_BIT_FIELD_OPERATIONS): Define.
+ * objc/objc-act.c (check_ivars): Convert types to bit-field types
+ before checking.
+ * tree.c (build_nonstandard_integer_type): New function.
+ * tree.h (build_nonstandard_integer_type): New prototype.
+ * tree-ssa.c (tree_ssa_useless_type_conversion_1): Don't treat
+ conversions between integer and boolean types as useless.
+
2004-07-08 Paolo Bonzini <bonzini@gnu.org>
* c-common.c (c_common_nodes_and_builtins): Do not
|| TYPE_UNSIGNED (type) == unsignedp)
return type;
- /* Must check the mode of the types, not the precision. Enumeral types
- in C++ have precision set to match their range, but may use a wider
- mode to match an ABI. If we change modes, we may wind up with bad
- conversions. */
-
- if (TYPE_MODE (type) == TYPE_MODE (signed_char_type_node))
+ /* For ENUMERAL_TYPEs in C++, must check the mode of the types, not
+ the precision; they have precision set to match their range, but
+ may use a wider mode to match an ABI. If we change modes, we may
+ wind up with bad conversions. For INTEGER_TYPEs in C, must check
+ the precision as well, so as to yield correct results for
+ bit-field types. C++ does not have these separate bit-field
+ types, and producing a signed or unsigned variant of an
+ ENUMERAL_TYPE may cause other problems as well. */
+
+#define TYPE_OK(node) \
+ (TYPE_MODE (type) == TYPE_MODE (node) \
+ && (c_dialect_cxx () || TYPE_PRECISION (type) == TYPE_PRECISION (node)))
+ if (TYPE_OK (signed_char_type_node))
return unsignedp ? unsigned_char_type_node : signed_char_type_node;
- if (TYPE_MODE (type) == TYPE_MODE (integer_type_node))
+ if (TYPE_OK (integer_type_node))
return unsignedp ? unsigned_type_node : integer_type_node;
- if (TYPE_MODE (type) == TYPE_MODE (short_integer_type_node))
+ if (TYPE_OK (short_integer_type_node))
return unsignedp ? short_unsigned_type_node : short_integer_type_node;
- if (TYPE_MODE (type) == TYPE_MODE (long_integer_type_node))
+ if (TYPE_OK (long_integer_type_node))
return unsignedp ? long_unsigned_type_node : long_integer_type_node;
- if (TYPE_MODE (type) == TYPE_MODE (long_long_integer_type_node))
+ if (TYPE_OK (long_long_integer_type_node))
return (unsignedp ? long_long_unsigned_type_node
: long_long_integer_type_node);
- if (TYPE_MODE (type) == TYPE_MODE (widest_integer_literal_type_node))
+ if (TYPE_OK (widest_integer_literal_type_node))
return (unsignedp ? widest_unsigned_literal_type_node
: widest_integer_literal_type_node);
#if HOST_BITS_PER_WIDE_INT >= 64
- if (TYPE_MODE (type) == TYPE_MODE (intTI_type_node))
+ if (TYPE_OK (intTI_type_node))
return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
#endif
- if (TYPE_MODE (type) == TYPE_MODE (intDI_type_node))
+ if (TYPE_OK (intDI_type_node))
return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
- if (TYPE_MODE (type) == TYPE_MODE (intSI_type_node))
+ if (TYPE_OK (intSI_type_node))
return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
- if (TYPE_MODE (type) == TYPE_MODE (intHI_type_node))
+ if (TYPE_OK (intHI_type_node))
return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
- if (TYPE_MODE (type) == TYPE_MODE (intQI_type_node))
+ if (TYPE_OK (intQI_type_node))
return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
+#undef TYPE_OK
- return type;
+ if (c_dialect_cxx ())
+ return type;
+ else
+ return build_nonstandard_integer_type (TYPE_PRECISION (type), unsignedp);
}
/* The C version of the register_builtin_type langhook. */
}
/* Install struct as DECL_CONTEXT of each field decl.
- Also process specified field sizes,m which is found in the DECL_INITIAL.
- Store 0 there, except for ": 0" fields (so we can find them
- and delete them, below). */
+ Also process specified field sizes, found in the DECL_INITIAL,
+ storing 0 there after the type has been changed to precision equal
+ to its width, rather than the precision of the specified standard
+ type. (Correct layout requires the original type to have been preserved
+ until now.) */
saw_named_field = 0;
for (x = fieldlist; x; x = TREE_CHAIN (x))
SET_DECL_C_BIT_FIELD (x);
}
- DECL_INITIAL (x) = 0;
-
/* Detect flexible array member in an invalid context. */
if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE
&& TYPE_SIZE (TREE_TYPE (x)) == NULL_TREE
layout_type (t);
- /* Delete all zero-width bit-fields from the fieldlist. */
+ /* Give bit-fields their proper types. */
{
tree *fieldlistp = &fieldlist;
while (*fieldlistp)
- if (TREE_CODE (*fieldlistp) == FIELD_DECL && DECL_INITIAL (*fieldlistp))
- *fieldlistp = TREE_CHAIN (*fieldlistp);
+ if (TREE_CODE (*fieldlistp) == FIELD_DECL && DECL_INITIAL (*fieldlistp)
+ && TREE_TYPE (*fieldlistp) != error_mark_node)
+ {
+ unsigned HOST_WIDE_INT width
+ = tree_low_cst (DECL_INITIAL (*fieldlistp), 1);
+ tree type = TREE_TYPE (*fieldlistp);
+ if (width != TYPE_PRECISION (type))
+ TREE_TYPE (*fieldlistp)
+ = build_nonstandard_integer_type (width, TYPE_UNSIGNED (type));
+ DECL_INITIAL (*fieldlistp) = 0;
+ }
else
fieldlistp = &TREE_CHAIN (*fieldlistp);
}
#define LANG_HOOKS_FINISH_INCOMPLETE_DECL c_finish_incomplete_decl
#undef LANG_HOOKS_UNSAFE_FOR_REEVAL
#define LANG_HOOKS_UNSAFE_FOR_REEVAL c_common_unsafe_for_reeval
+#undef LANG_HOOKS_REDUCE_BIT_FIELD_OPERATIONS
+#define LANG_HOOKS_REDUCE_BIT_FIELD_OPERATIONS true
#undef LANG_HOOKS_STATICP
#define LANG_HOOKS_STATICP c_staticp
#undef LANG_HOOKS_NO_BODY_BLOCKS
static rtx expand_increment (tree, int, int);
static void expand_operands (tree, tree, rtx, rtx*, rtx*,
enum expand_modifier);
+static rtx reduce_to_bit_field_precision (rtx, rtx, tree);
static rtx do_store_flag (tree, rtx, enum machine_mode, int);
#ifdef PUSH_ROUNDING
static void emit_single_push_insn (enum machine_mode, rtx, tree);
rtx subtarget, original_target;
int ignore;
tree context;
+ bool reduce_bit_field = false;
+#define REDUCE_BIT_FIELD(expr) (reduce_bit_field && !ignore \
+ ? reduce_to_bit_field_precision ((expr), \
+ target, \
+ type) \
+ : (expr))
mode = TYPE_MODE (type);
unsignedp = TYPE_UNSIGNED (type);
+ if (lang_hooks.reduce_bit_field_operations
+ && TREE_CODE (type) == INTEGER_TYPE
+ && GET_MODE_PRECISION (mode) > TYPE_PRECISION (type))
+ {
+ /* An operation in what may be a bit-field type needs the
+ result to be reduced to the precision of the bit-field type,
+ which is narrower than that of the type's mode. */
+ reduce_bit_field = true;
+ if (modifier == EXPAND_STACK_PARM)
+ target = 0;
+ }
/* Use subtarget as the target for operand 0 of a binary operation. */
subtarget = get_subtarget (target);
&& GET_CODE (op0) == SUBREG)
SUBREG_PROMOTED_VAR_P (op0) = 0;
- return op0;
+ return REDUCE_BIT_FIELD (op0);
}
op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, mode, modifier);
+ op0 = REDUCE_BIT_FIELD (op0);
if (GET_MODE (op0) == mode)
return op0;
op1 = plus_constant (op1, INTVAL (constant_part));
if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
op1 = force_operand (op1, target);
- return op1;
+ return REDUCE_BIT_FIELD (op1);
}
else if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST
op0 = plus_constant (op0, INTVAL (constant_part));
if (modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
op0 = force_operand (op0, target);
- return op0;
+ return REDUCE_BIT_FIELD (op0);
}
}
expand_operands (TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
subtarget, &op0, &op1, modifier);
- return simplify_gen_binary (PLUS, mode, op0, op1);
+ return REDUCE_BIT_FIELD (simplify_gen_binary (PLUS, mode, op0, op1));
case MINUS_EXPR:
/* For initializers, we are allowed to return a MINUS of two
/* If the last operand is a CONST_INT, use plus_constant of
the negated constant. Else make the MINUS. */
if (GET_CODE (op1) == CONST_INT)
- return plus_constant (op0, - INTVAL (op1));
+ return REDUCE_BIT_FIELD (plus_constant (op0, - INTVAL (op1)));
else
- return gen_rtx_MINUS (mode, op0, op1);
+ return REDUCE_BIT_FIELD (gen_rtx_MINUS (mode, op0, op1));
}
this_optab = ! unsignedp && flag_trapv
if (GET_CODE (op1) == CONST_INT)
{
op1 = negate_rtx (mode, op1);
- return simplify_gen_binary (PLUS, mode, op0, op1);
+ return REDUCE_BIT_FIELD (simplify_gen_binary (PLUS, mode, op0, op1));
}
goto binop2;
if (!REG_P (op0))
op0 = copy_to_mode_reg (mode, op0);
- return gen_rtx_MULT (mode, op0,
+ return REDUCE_BIT_FIELD (gen_rtx_MULT (mode, op0,
gen_int_mode (tree_low_cst (exp1, 0),
- TYPE_MODE (TREE_TYPE (exp1))));
+ TYPE_MODE (TREE_TYPE (exp1)))));
}
if (modifier == EXPAND_STACK_PARM)
zextend_p);
if (htem != hipart)
emit_move_insn (hipart, htem);
- return temp;
+ return REDUCE_BIT_FIELD (temp);
}
}
}
expand_operands (TREE_OPERAND (exp, 0), TREE_OPERAND (exp, 1),
subtarget, &op0, &op1, 0);
- return expand_mult (mode, op0, op1, target, unsignedp);
+ return REDUCE_BIT_FIELD (expand_mult (mode, op0, op1, target, unsignedp));
case TRUNC_DIV_EXPR:
case FLOOR_DIV_EXPR:
? negv_optab : neg_optab, op0, target, 0);
if (temp == 0)
abort ();
- return temp;
+ return REDUCE_BIT_FIELD (temp);
case ABS_EXPR:
op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
case PREINCREMENT_EXPR:
case PREDECREMENT_EXPR:
- return expand_increment (exp, 0, ignore);
+ return REDUCE_BIT_FIELD (expand_increment (exp, 0, ignore));
case POSTINCREMENT_EXPR:
case POSTDECREMENT_EXPR:
/* Faster to treat as pre-increment if result is not used. */
- return expand_increment (exp, ! ignore, ignore);
+ return REDUCE_BIT_FIELD (expand_increment (exp, ! ignore, ignore));
case ADDR_EXPR:
if (modifier == EXPAND_STACK_PARM)
unsignedp, OPTAB_LIB_WIDEN);
if (temp == 0)
abort ();
- return temp;
+ return REDUCE_BIT_FIELD (temp);
+}
+#undef REDUCE_BIT_FIELD
+\f
+/* Subroutine of above: reduce EXP to the precision of TYPE (in the
+ signedness of TYPE), possibly returning the result in TARGET. */
+static rtx
+reduce_to_bit_field_precision (rtx exp, rtx target, tree type)
+{
+ HOST_WIDE_INT prec = TYPE_PRECISION (type);
+ if (target && GET_MODE (target) != GET_MODE (exp))
+ target = 0;
+ if (TYPE_UNSIGNED (type))
+ {
+ rtx mask;
+ if (prec < HOST_BITS_PER_WIDE_INT)
+ mask = immed_double_const (((unsigned HOST_WIDE_INT) 1 << prec) - 1, 0,
+ GET_MODE (exp));
+ else
+ mask = immed_double_const ((unsigned HOST_WIDE_INT) -1,
+ ((unsigned HOST_WIDE_INT) 1
+ << (prec - HOST_BITS_PER_WIDE_INT)) - 1,
+ GET_MODE (exp));
+ return expand_and (GET_MODE (exp), exp, mask, target);
+ }
+ else
+ {
+ tree count = build_int_2 (GET_MODE_BITSIZE (GET_MODE (exp)) - prec, 0);
+ exp = expand_shift (LSHIFT_EXPR, GET_MODE (exp), exp, count, target, 0);
+ return expand_shift (RSHIFT_EXPR, GET_MODE (exp), exp, count, target, 0);
+ }
}
\f
/* Subroutine of above: returns 1 if OFFSET corresponds to an offset that
#define LANG_HOOKS_MAYBE_BUILD_CLEANUP lhd_return_null_tree
#define LANG_HOOKS_SET_DECL_ASSEMBLER_NAME lhd_set_decl_assembler_name
#define LANG_HOOKS_CAN_USE_BIT_FIELDS_P lhd_can_use_bit_fields_p
+#define LANG_HOOKS_REDUCE_BIT_FIELD_OPERATIONS false
#define LANG_HOOKS_HONOR_READONLY false
#define LANG_HOOKS_NO_BODY_BLOCKS false
#define LANG_HOOKS_PRINT_STATISTICS lhd_do_nothing
LANG_HOOKS_MAYBE_BUILD_CLEANUP, \
LANG_HOOKS_SET_DECL_ASSEMBLER_NAME, \
LANG_HOOKS_CAN_USE_BIT_FIELDS_P, \
+ LANG_HOOKS_REDUCE_BIT_FIELD_OPERATIONS, \
LANG_HOOKS_HONOR_READONLY, \
LANG_HOOKS_NO_BODY_BLOCKS, \
LANG_HOOKS_PRINT_STATISTICS, \
optimizations, for instance in fold_truthop(). */
bool (*can_use_bit_fields_p) (void);
+ /* Nonzero if operations on types narrower than their mode should
+ have their results reduced to the precision of the type. */
+ bool reduce_bit_field_operations;
+
/* Nonzero if TYPE_READONLY and TREE_READONLY should always be honored. */
bool honor_readonly;
t1 = TREE_TYPE (intdecls); t2 = TREE_TYPE (impdecls);
+ if (TREE_VALUE (TREE_VALUE (rawimpdecls)))
+ {
+ /* t1 is the bit-field type, so t2 must be converted to the
+ bit-field type for comparison as well. */
+ unsigned HOST_WIDE_INT width
+ = tree_low_cst (TREE_VALUE (TREE_VALUE (rawimpdecls)), 1);
+ if (width != TYPE_PRECISION (t2))
+ t2 = build_nonstandard_integer_type (width, TYPE_UNSIGNED (t2));
+ }
+
if (!comptypes (t1, t2)
|| !tree_int_cst_equal (TREE_VALUE (TREE_VALUE (rawintdecls)),
TREE_VALUE (TREE_VALUE (rawimpdecls))))
#define LANG_HOOKS_FINISH_INCOMPLETE_DECL c_finish_incomplete_decl
#undef LANG_HOOKS_UNSAFE_FOR_REEVAL
#define LANG_HOOKS_UNSAFE_FOR_REEVAL c_common_unsafe_for_reeval
+#undef LANG_HOOKS_REDUCE_BIT_FIELD_OPERATIONS
+#define LANG_HOOKS_REDUCE_BIT_FIELD_OPERATIONS true
#undef LANG_HOOKS_STATICP
#define LANG_HOOKS_STATICP c_staticp
#undef LANG_HOOKS_NO_BODY_BLOCKS
+2004-07-08 Joseph S. Myers <jsm@polyomino.org.uk>
+
+ * gcc.c-torture/execute/bitfld-1.x: Remove.
+ * gcc.c-torture/execute/bitfld-3.c: New test.
+ * gcc.dg/bitfld-2.c: Remove XFAILs.
+
2004-07-07 H.J. Lu <hongjiu.lu@intel.com>
PR c++/16276
+++ /dev/null
-set torture_execute_xfail "*-*-*"
-return 0
--- /dev/null
+/* Test that operations on bit-fields yield results reduced to bit-field
+ type. */
+/* Origin: Joseph Myers <jsm@polyomino.org.uk> */
+
+extern void exit (int);
+extern void abort (void);
+
+struct s {
+ unsigned long long u33: 33;
+ unsigned long long u40: 40;
+ unsigned long long u41: 41;
+};
+
+struct s a = { 0x100000, 0x100000, 0x100000 };
+struct s b = { 0x100000000ULL, 0x100000000ULL, 0x100000000ULL };
+struct s c = { 0x1FFFFFFFFULL, 0, 0 };
+
+int
+main (void)
+{
+ if (a.u33 * a.u33 != 0 || a.u33 * a.u40 != 0 || a.u40 * a.u33 != 0
+ || a.u40 * a.u40 != 0)
+ abort ();
+ if (a.u33 * a.u41 != 0x10000000000ULL
+ || a.u40 * a.u41 != 0x10000000000ULL
+ || a.u41 * a.u33 != 0x10000000000ULL
+ || a.u41 * a.u40 != 0x10000000000ULL
+ || a.u41 * a.u41 != 0x10000000000ULL)
+ abort ();
+ if (b.u33 + b.u33 != 0)
+ abort ();
+ if (b.u33 + b.u40 != 0x200000000ULL
+ || b.u33 + b.u41 != 0x200000000ULL
+ || b.u40 + b.u33 != 0x200000000ULL
+ || b.u40 + b.u40 != 0x200000000ULL
+ || b.u40 + b.u41 != 0x200000000ULL
+ || b.u41 + b.u33 != 0x200000000ULL
+ || b.u41 + b.u40 != 0x200000000ULL
+ || b.u41 + b.u41 != 0x200000000ULL)
+ abort ();
+ if (a.u33 - b.u33 != 0x100100000ULL
+ || a.u33 - b.u40 != 0xFF00100000ULL
+ || a.u33 - b.u41 != 0x1FF00100000ULL
+ || a.u40 - b.u33 != 0xFF00100000ULL
+ || a.u40 - b.u40 != 0xFF00100000ULL
+ || a.u40 - b.u41 != 0x1FF00100000ULL
+ || a.u41 - b.u33 != 0x1FF00100000ULL
+ || a.u41 - b.u40 != 0x1FF00100000ULL
+ || a.u41 - b.u41 != 0x1FF00100000ULL)
+ abort ();
+ if (++c.u33 != 0 || --c.u40 != 0xFFFFFFFFFFULL || c.u41-- != 0)
+ abort ();
+ exit (0);
+}
int b: 2;
};
-struct bf p = {4, 0}; /* { dg-warning "truncated" "" { xfail *-*-* } } */
-struct bf q = {0, 2}; /* { dg-warning "overflow" "" { xfail *-*-* } } */
+struct bf p = {4, 0}; /* { dg-warning "truncated" "" } */
+struct bf q = {0, 2}; /* { dg-warning "overflow" "" } */
struct bf r = {3, -2}; /* { dg-bogus "(truncated|overflow)" } */
void foo ()
{
- p.a = 4, p.b = 0; /* { dg-warning "truncated" "" { xfail *-*-* } } */
- q.a = 0, q.b = 2; /* { dg-warning "overflow" "" { xfail *-*-* } } */
+ p.a = 4, p.b = 0; /* { dg-warning "truncated" "" } */
+ q.a = 0, q.b = 2; /* { dg-warning "overflow" "" } */
r.a = 3, r.b = -2; /* { dg-bogus "(truncated|overflow)" } */
}
/* If both the inner and outer types are integral types, then the
conversion is not necessary if they have the same mode and
- signedness and precision. Note that type _Bool can have size of
- 4 (only happens on powerpc-darwin right now but can happen on any
- target that defines BOOL_TYPE_SIZE to be INT_TYPE_SIZE) and a
- precision of 1 while unsigned int is the same expect for a
- precision of 4 so testing of precision is necessary. */
+ signedness and precision, and both or neither are boolean. Some
+ code assumes an invariant that boolean types stay boolean and do
+ not become 1-bit bit-field types. Note that types with precision
+ not using all bits of the mode (such as bit-field types in C)
+ mean that testing of precision is necessary. */
else if (INTEGRAL_TYPE_P (inner_type)
&& INTEGRAL_TYPE_P (outer_type)
&& TYPE_MODE (inner_type) == TYPE_MODE (outer_type)
&& TYPE_UNSIGNED (inner_type) == TYPE_UNSIGNED (outer_type)
&& TYPE_PRECISION (inner_type) == TYPE_PRECISION (outer_type))
- return true;
+ {
+ bool first_boolean = (TREE_CODE (inner_type) == BOOLEAN_TYPE);
+ bool second_boolean = (TREE_CODE (outer_type) == BOOLEAN_TYPE);
+ if (first_boolean == second_boolean)
+ return true;
+ }
/* Recurse for complex types. */
else if (TREE_CODE (inner_type) == COMPLEX_TYPE
return itype;
}
+/* Builds a signed or unsigned integer type of precision PRECISION.
+ Used for C bitfields whose precision does not match that of
+ built-in target types. */
+tree
+build_nonstandard_integer_type (unsigned HOST_WIDE_INT precision,
+ int unsignedp)
+{
+ tree itype = make_node (INTEGER_TYPE);
+
+ TYPE_PRECISION (itype) = precision;
+
+ if (unsignedp)
+ fixup_unsigned_type (itype);
+ else
+ fixup_signed_type (itype);
+
+ if (host_integerp (TYPE_MAX_VALUE (itype), 1))
+ return type_hash_canon (tree_low_cst (TYPE_MAX_VALUE (itype), 1), itype);
+
+ return itype;
+}
+
/* Create a range of some discrete type TYPE (an INTEGER_TYPE,
ENUMERAL_TYPE, BOOLEAN_TYPE, or CHAR_TYPE), with
low bound LOWVAL and high bound HIGHVAL.
extern void init_ttree (void);
extern void build_common_tree_nodes (int);
extern void build_common_tree_nodes_2 (int);
+extern tree build_nonstandard_integer_type (unsigned HOST_WIDE_INT, int);
extern tree build_range_type (tree, tree, tree);
/* In function.c */