static int operand_equal_for_comparison_p (tree, tree, tree);
static int twoval_comparison_p (tree, tree *, tree *, int *);
static tree eval_subst (location_t, tree, tree, tree, tree, tree);
-static tree make_bit_field_ref (location_t, tree, tree,
- HOST_WIDE_INT, HOST_WIDE_INT, int, int);
static tree optimize_bit_field_compare (location_t, enum tree_code,
tree, tree, tree);
-static tree decode_field_reference (location_t, tree, HOST_WIDE_INT *,
- HOST_WIDE_INT *,
- machine_mode *, int *, int *, int *,
- tree *, tree *);
static int simple_operand_p (const_tree);
static bool simple_operand_p_2 (tree);
static tree range_binop (enum tree_code, tree, tree, int, tree, int);
\f
/* Return a BIT_FIELD_REF of type TYPE to refer to BITSIZE bits of INNER
starting at BITPOS. The field is unsigned if UNSIGNEDP is nonzero
- and uses reverse storage order if REVERSEP is nonzero. */
+ and uses reverse storage order if REVERSEP is nonzero. ORIG_INNER
+ is the original memory reference used to preserve the alias set of
+ the access. */
static tree
-make_bit_field_ref (location_t loc, tree inner, tree type,
+make_bit_field_ref (location_t loc, tree inner, tree orig_inner, tree type,
HOST_WIDE_INT bitsize, HOST_WIDE_INT bitpos,
int unsignedp, int reversep)
{
tree result, bftype;
+ if (get_alias_set (inner) != get_alias_set (orig_inner))
+ inner = fold_build2 (MEM_REF, TREE_TYPE (inner),
+ build_fold_addr_expr (inner),
+ build_int_cst
+ (reference_alias_ptr_type (orig_inner), 0));
+
if (bitpos == 0 && !reversep)
{
tree size = TYPE_SIZE (TREE_TYPE (inner));
and return. */
return fold_build2_loc (loc, code, compare_type,
fold_build2_loc (loc, BIT_AND_EXPR, unsigned_type,
- make_bit_field_ref (loc, linner,
+ make_bit_field_ref (loc, linner, lhs,
unsigned_type,
nbitsize, nbitpos,
1, lreversep),
mask),
fold_build2_loc (loc, BIT_AND_EXPR, unsigned_type,
- make_bit_field_ref (loc, rinner,
+ make_bit_field_ref (loc, rinner, rhs,
unsigned_type,
nbitsize, nbitpos,
1, rreversep),
/* Make a new bitfield reference, shift the constant over the
appropriate number of bits and mask it with the computed mask
(in case this was a signed field). If we changed it, make a new one. */
- lhs = make_bit_field_ref (loc, linner, unsigned_type, nbitsize, nbitpos, 1,
- lreversep);
+ lhs = make_bit_field_ref (loc, linner, lhs, unsigned_type,
+ nbitsize, nbitpos, 1, lreversep);
rhs = const_binop (BIT_AND_EXPR,
const_binop (LSHIFT_EXPR,
do anything with. */
static tree
-decode_field_reference (location_t loc, tree exp, HOST_WIDE_INT *pbitsize,
+decode_field_reference (location_t loc, tree *exp_, HOST_WIDE_INT *pbitsize,
HOST_WIDE_INT *pbitpos, machine_mode *pmode,
int *punsignedp, int *preversep, int *pvolatilep,
tree *pmask, tree *pand_mask)
{
+ tree exp = *exp_;
tree outer_type = 0;
tree and_mask = 0;
tree mask, inner, offset;
|| TREE_CODE (inner) == PLACEHOLDER_EXPR)
return 0;
+ *exp_ = exp;
+
/* If the number of bits in the reference is the same as the bitsize of
the outer type, then the outer type gives the signedness. Otherwise
(in case of a small bitfield) the signedness is unchanged. */
ll_reversep = lr_reversep = rl_reversep = rr_reversep = 0;
volatilep = 0;
- ll_inner = decode_field_reference (loc, ll_arg,
+ ll_inner = decode_field_reference (loc, &ll_arg,
&ll_bitsize, &ll_bitpos, &ll_mode,
&ll_unsignedp, &ll_reversep, &volatilep,
&ll_mask, &ll_and_mask);
- lr_inner = decode_field_reference (loc, lr_arg,
+ lr_inner = decode_field_reference (loc, &lr_arg,
&lr_bitsize, &lr_bitpos, &lr_mode,
&lr_unsignedp, &lr_reversep, &volatilep,
&lr_mask, &lr_and_mask);
- rl_inner = decode_field_reference (loc, rl_arg,
+ rl_inner = decode_field_reference (loc, &rl_arg,
&rl_bitsize, &rl_bitpos, &rl_mode,
&rl_unsignedp, &rl_reversep, &volatilep,
&rl_mask, &rl_and_mask);
- rr_inner = decode_field_reference (loc, rr_arg,
+ rr_inner = decode_field_reference (loc, &rr_arg,
&rr_bitsize, &rr_bitpos, &rr_mode,
&rr_unsignedp, &rr_reversep, &volatilep,
&rr_mask, &rr_and_mask);
lr_mask = const_binop (BIT_IOR_EXPR, lr_mask, rr_mask);
if (lnbitsize == rnbitsize && xll_bitpos == xlr_bitpos)
{
- lhs = make_bit_field_ref (loc, ll_inner, lntype, lnbitsize, lnbitpos,
+ lhs = make_bit_field_ref (loc, ll_inner, ll_arg,
+ lntype, lnbitsize, lnbitpos,
ll_unsignedp || rl_unsignedp, ll_reversep);
if (! all_ones_mask_p (ll_mask, lnbitsize))
lhs = build2 (BIT_AND_EXPR, lntype, lhs, ll_mask);
- rhs = make_bit_field_ref (loc, lr_inner, rntype, rnbitsize, rnbitpos,
+ rhs = make_bit_field_ref (loc, lr_inner, lr_arg,
+ rntype, rnbitsize, rnbitpos,
lr_unsignedp || rr_unsignedp, lr_reversep);
if (! all_ones_mask_p (lr_mask, rnbitsize))
rhs = build2 (BIT_AND_EXPR, rntype, rhs, lr_mask);
{
tree type;
- lhs = make_bit_field_ref (loc, ll_inner, lntype,
+ lhs = make_bit_field_ref (loc, ll_inner, ll_arg, lntype,
ll_bitsize + rl_bitsize,
MIN (ll_bitpos, rl_bitpos),
ll_unsignedp, ll_reversep);
- rhs = make_bit_field_ref (loc, lr_inner, rntype,
+ rhs = make_bit_field_ref (loc, lr_inner, lr_arg, rntype,
lr_bitsize + rr_bitsize,
MIN (lr_bitpos, rr_bitpos),
lr_unsignedp, lr_reversep);
reference we will make. Unless the mask is all ones the width of
that field, perform the mask operation. Then compare with the
merged constant. */
- result = make_bit_field_ref (loc, ll_inner, lntype, lnbitsize, lnbitpos,
+ result = make_bit_field_ref (loc, ll_inner, ll_arg,
+ lntype, lnbitsize, lnbitpos,
ll_unsignedp || rl_unsignedp, ll_reversep);
ll_mask = const_binop (BIT_IOR_EXPR, ll_mask, rl_mask);
--- /dev/null
+// { dg-do run }
+
+using size_t = __SIZE_TYPE__;
+
+inline void* operator new(size_t, void* p) noexcept
+{ return p; }
+
+inline void operator delete(void*, void*)
+{ }
+
+struct long_t
+{
+ size_t is_short : 1;
+ size_t length : (__SIZEOF_SIZE_T__ * __CHAR_BIT__ - 1);
+ size_t capacity;
+ char* pointer;
+};
+
+union long_raw_t {
+ unsigned char data[sizeof(long_t)];
+ struct __attribute__((aligned(alignof(long_t)))) { } align;
+};
+
+struct short_header
+{
+ unsigned char is_short : 1;
+ unsigned char length : (__CHAR_BIT__ - 1);
+};
+
+struct short_t
+{
+ short_header h;
+ char data[23];
+};
+
+union repr_t
+{
+ long_raw_t r;
+ short_t s;
+
+ const short_t& short_repr() const
+ { return s; }
+
+ const long_t& long_repr() const
+ { return *static_cast<const long_t*>(static_cast<const void*>(&r)); }
+
+ short_t& short_repr()
+ { return s; }
+
+ long_t& long_repr()
+ { return *static_cast<long_t*>(static_cast<void*>(&r)); }
+};
+
+class string
+{
+public:
+ string()
+ {
+ short_t& s = m_repr.short_repr();
+ s.h.is_short = 1;
+ s.h.length = 0;
+ s.data[0] = '\0';
+ }
+
+ string(const char* str)
+ {
+ size_t length = __builtin_strlen(str);
+ if (length + 1 > 23) {
+ long_t& l = m_repr.long_repr();
+ l.is_short = 0;
+ l.length = length;
+ l.capacity = length + 1;
+ l.pointer = new char[l.capacity];
+ __builtin_memcpy(l.pointer, str, length + 1);
+ } else {
+ short_t& s = m_repr.short_repr();
+ s.h.is_short = 1;
+ s.h.length = length;
+ __builtin_memcpy(s.data, str, length + 1);
+ }
+ }
+
+ string(string&& other)
+ : string{}
+ {
+ swap_data(other);
+ }
+
+ ~string()
+ {
+ if (!is_short()) {
+ delete[] m_repr.long_repr().pointer;
+ }
+ }
+
+ size_t length() const
+ { return is_short() ? short_length() : long_length(); }
+
+private:
+ bool is_short() const
+ { return m_repr.s.h.is_short != 0; }
+
+ size_t short_length() const
+ { return m_repr.short_repr().h.length; }
+
+ size_t long_length() const
+ { return m_repr.long_repr().length; }
+
+ void swap_data(string& other)
+ {
+ if (is_short()) {
+ if (other.is_short()) {
+ repr_t tmp(m_repr);
+ m_repr = other.m_repr;
+ other.m_repr = tmp;
+ } else {
+ short_t short_backup(m_repr.short_repr());
+ m_repr.short_repr().~short_t();
+ ::new(&m_repr.long_repr()) long_t(other.m_repr.long_repr());
+ other.m_repr.long_repr().~long_t();
+ ::new(&other.m_repr.short_repr()) short_t(short_backup);
+ }
+ } else {
+ if (other.is_short()) {
+ short_t short_backup(other.m_repr.short_repr());
+ other.m_repr.short_repr().~short_t();
+ ::new(&other.m_repr.long_repr()) long_t(m_repr.long_repr());
+ m_repr.long_repr().~long_t();
+ ::new(&m_repr.short_repr()) short_t(short_backup);
+ } else {
+ long_t tmp(m_repr.long_repr());
+ m_repr.long_repr() = other.m_repr.long_repr();
+ other.m_repr.long_repr() = tmp;
+ }
+ }
+ }
+
+ repr_t m_repr;
+};
+
+struct foo
+{
+ __attribute__((noinline))
+ foo(string str)
+ : m_str{static_cast<string&&>(str)},
+ m_len{m_str.length()}
+ { }
+
+ string m_str;
+ size_t m_len;
+};
+
+int main()
+{
+ foo f{"the quick brown fox jumps over the lazy dog"};
+ if (f.m_len == 0) {
+ __builtin_abort();
+ }
+ return 0;
+}