extern tree get_field_at_bit_offset (tree record_type, bit_offset_t bit_offset);
+extern bool
+compare_bit_offsets_p (bit_offset_t a,
+ enum tree_code op,
+ bit_offset_t b);
+
/* The location of a region expressesd as an offset relative to a
base region. */
extern bool operator> (const region_offset &, const region_offset &);
extern bool operator>= (const region_offset &, const region_offset &);
+extern tristate
+eval_region_offset_comparison (region_offset lhs_offset,
+ enum tree_code op,
+ region_offset rhs_offset,
+ const region_model &model);
+
extern location_t get_stmt_location (const gimple *stmt, function *fun);
extern bool compat_types_p (tree src_type, tree dst_type);
/* e.g. "struct s2 x = {{'A', 'B', 'C', 'D'}};". */
const svalue *rhs_sval = get_rvalue (rhs1, ctxt);
m_store.set_value (m_mgr->get_store_manager(), lhs_reg, rhs_sval,
- ctxt ? ctxt->get_uncertainty () : nullptr);
+ ctxt ? ctxt->get_uncertainty () : nullptr,
+ *this);
}
break;
}
check_region_for_write (lhs_reg, rhs_sval, ctxt);
m_store.set_value (m_mgr->get_store_manager(), lhs_reg, rhs_sval,
- ctxt ? ctxt->get_uncertainty () : nullptr);
+ ctxt ? ctxt->get_uncertainty () : nullptr,
+ *this);
}
/* Set the value of the region given by LHS to the value given by RHS. */
if (const region_svalue *lhs_ptr = lhs->dyn_cast_region_svalue ())
if (const region_svalue *rhs_ptr = rhs->dyn_cast_region_svalue ())
{
- tristate res = region_svalue::eval_condition (lhs_ptr, op, rhs_ptr);
+ tristate res = region_svalue::eval_condition (lhs_ptr, op, rhs_ptr,
+ *this);
if (res.is_known ())
return res;
/* Otherwise, only known through constraints. */
namespace ana {
+bool
+compare_bit_offsets_p (bit_offset_t a,
+ enum tree_code op,
+ bit_offset_t b)
+{
+ switch (op)
+ {
+ default:
+ gcc_unreachable ();
+ case EQ_EXPR: return a == b;
+ case NE_EXPR: return a != b;
+ case GE_EXPR: return a >= b;
+ case LE_EXPR: return a <= b;
+ case GT_EXPR: return a > b;
+ case LT_EXPR: return a < b;
+ }
+}
+
region_offset
region_offset::make_byte_offset (const region *base_region,
const svalue *num_bytes_sval)
void
region_offset::dump_to_pp (pretty_printer *pp, bool simple) const
{
+ pp_string (pp, "{");
+ m_base_region->dump_to_pp (pp, simple);
if (symbolic_p ())
{
- /* We don't bother showing the base region. */
- pp_string (pp, "byte ");
+ pp_string (pp, " byte ");
m_sym_offset->dump_to_pp (pp, simple);
}
else
{
if (m_offset % BITS_PER_UNIT == 0)
{
- pp_string (pp, "byte ");
+ pp_string (pp, " byte ");
pp_wide_int (pp, m_offset / BITS_PER_UNIT, SIGNED);
}
else
{
- pp_string (pp, "bit ");
+ pp_string (pp, " bit ");
pp_wide_int (pp, m_offset, SIGNED);
}
}
+ pp_string (pp, "}");
}
DEBUG_FUNCTION void
return offset;
}
+/* Ignoring base regions, compare the byte offset of OFFSET_A and OFFSET_B
+ within their respective base regions. */
+
+static tristate
+eval_byte_offset_comparison (region_offset offset_a,
+ enum tree_code op,
+ region_offset offset_b,
+ const region_model &model)
+{
+ if (offset_a.concrete_p ())
+ {
+ if (offset_b.concrete_p ())
+ {
+ // Concrete vs concrete: we know the result:
+ return compare_bit_offsets_p (offset_a.get_bit_offset (),
+ op,
+ offset_b.get_bit_offset ());
+ }
+ else
+ {
+ // Concrete vs symbolic
+ // TODO: There may be room for improved precision here
+ return tristate::unknown ();
+ }
+ }
+ else
+ {
+ if (offset_b.concrete_p ())
+ {
+ // Symbolic vs concrete
+ // TODO: There may be room for improved precision here
+ return tristate::unknown ();
+ }
+ else
+ {
+ // Symbolic vs symbolic
+ const svalue *offset_sval_a = offset_a.get_symbolic_byte_offset ();
+ const svalue *offset_sval_b = offset_b.get_symbolic_byte_offset ();
+ return model.eval_condition (offset_sval_a, op, offset_sval_b);
+ }
+ }
+}
+
+/* Evaluate the condition LHS_OFFSET OP RHS_OFFSET for comparing
+ pointers.
+
+ See if they point to the same base region, and if we know about
+ the offsets within their regions.
+
+ Use MODEL for aliasing information and any knowledge from
+ constraint_manager about ordering of pointers. */
+
+tristate
+eval_region_offset_comparison (region_offset lhs_offset,
+ enum tree_code op,
+ region_offset rhs_offset,
+ const region_model &model)
+{
+ /* Try to determine if they're the same base region. */
+ const tristate same_base_region
+ = model.get_store ()->eval_alias (lhs_offset.get_base_region (),
+ rhs_offset.get_base_region (),
+ model);
+
+ /* Try to determine if they're the same offset relative to their
+ base region. */
+ const tristate same_byte_offset
+ = eval_byte_offset_comparison (lhs_offset, EQ_EXPR, rhs_offset, model);
+
+ /* With that, we might know if they're equal/non-equal. */
+ const tristate equality = same_base_region.and_(same_byte_offset);
+
+ switch (op)
+ {
+ default:
+ gcc_unreachable ();
+
+ case EQ_EXPR:
+ return equality;
+
+ case NE_EXPR:
+ return equality.not_ ();
+
+ case GE_EXPR:
+ case LE_EXPR:
+ if (equality.is_true ())
+ return tristate (true);
+ else if (same_base_region.is_true ())
+ return eval_byte_offset_comparison (lhs_offset, op, rhs_offset, model);
+ else
+ return tristate::unknown ();
+
+ case GT_EXPR:
+ case LT_EXPR:
+ if (equality.is_true ())
+ return tristate (false);
+ else if (same_base_region.is_true ())
+ return eval_byte_offset_comparison (lhs_offset, op, rhs_offset, model);
+ else
+ return tristate::unknown ();
+ }
+}
+
/* class region and its various subclasses. */
/* class region. */
return (*cluster_slot)->get_any_binding (mgr, reg);
}
-/* Set the value of LHS_REG to RHS_SVAL. */
+/* Set the value of LHS_REG to RHS_SVAL.
+ Use MODEL when determining if regions alias each other. */
void
store::set_value (store_manager *mgr, const region *lhs_reg,
const svalue *rhs_sval,
- uncertainty_t *uncertainty)
+ uncertainty_t *uncertainty,
+ const region_model &model)
{
logger *logger = mgr->get_logger ();
LOG_SCOPE (logger);
|| lhs_cluster->symbolic_p ()
|| iter_cluster->symbolic_p ()))
{
- tristate t_alias = eval_alias (lhs_base_reg, iter_base_reg);
+ tristate t_alias = eval_alias (lhs_base_reg, iter_base_reg,
+ model);
switch (t_alias.get_value ())
{
default:
on_maybe_live_values (*mgr, maybe_live_values);
}
-/* Determine if BASE_REG_A could be an alias of BASE_REG_B. */
+/* Determine if BASE_REG_A could be an alias of BASE_REG_B.
+ Use information from MODEL when comparing pointers. */
tristate
store::eval_alias (const region *base_reg_a,
- const region *base_reg_b) const
+ const region *base_reg_b,
+ const region_model &model) const
{
+ if (base_reg_a == base_reg_b)
+ return tristate (true);
+
/* SSA names can't alias. */
tree decl_a = base_reg_a->maybe_get_decl ();
if (decl_a && TREE_CODE (decl_a) == SSA_NAME)
return tristate::TS_FALSE;
/* Try both ways, for symmetry. */
- tristate ts_ab = eval_alias_1 (base_reg_a, base_reg_b);
+ tristate ts_ab = eval_alias_1 (base_reg_a, base_reg_b, model);
if (ts_ab.is_false ())
return tristate::TS_FALSE;
- tristate ts_ba = eval_alias_1 (base_reg_b, base_reg_a);
+ tristate ts_ba = eval_alias_1 (base_reg_b, base_reg_a, model);
if (ts_ba.is_false ())
return tristate::TS_FALSE;
- return tristate::TS_UNKNOWN;
+
+ gcc_assert (base_reg_a != base_reg_b);
+ enum region_kind kind_a = base_reg_a->get_kind ();
+ enum region_kind kind_b = base_reg_b->get_kind ();
+ if (kind_a == kind_b)
+ {
+ /* We have two different base region instances of the same kind.
+ For many kinds of region this implies they must have different
+ addresses. */
+ switch (kind_a)
+ {
+ /* Invalid cases. */
+
+ default:
+ gcc_unreachable ();
+
+ case RK_CODE:
+ case RK_ERRNO:
+ case RK_GLOBALS:
+ case RK_HEAP:
+ case RK_ROOT:
+ case RK_STACK:
+ case RK_THREAD_LOCAL:
+ /* We expect these to be singletons. */
+ gcc_unreachable ();
+
+ case RK_BIT_RANGE:
+ case RK_CAST:
+ case RK_ELEMENT:
+ case RK_FIELD:
+ case RK_OFFSET:
+ case RK_SIZED:
+ /* These aren't base regions. */
+ gcc_unreachable ();
+
+ /* Valid cases. */
+
+ case RK_ALLOCA:
+ case RK_DECL:
+ case RK_FRAME:
+ case RK_FUNCTION:
+ case RK_LABEL:
+ case RK_PRIVATE:
+ case RK_VAR_ARG:
+ /* We expect different regions of these kinds to have
+ different addresses. */
+ return tristate (false);
+
+ case RK_HEAP_ALLOCATED:
+ /* We have results of two different heap allocations.
+ If they were non-zero-sized allocations they are different from
+ each other, but zero-sized allocations might alias.
+ Note that this doesn't handle the case where of a freed pointer
+ possibly being reused, but we don't have a way to distinguish
+ this case from the "two different allocations case", and it seems
+ much more important to keep the latter from aliasing each
+ other (for precision when tracking writes). */
+ {
+ tristate size_a_gt_zero (tristate::unknown ());
+ tristate size_b_gt_zero (tristate::unknown ());
+ const svalue *zero_size
+ = model.get_manager ()->get_or_create_int_cst (size_type_node, 0);
+
+ if (auto sval_size_a = model.get_dynamic_extents (base_reg_a))
+ {
+ size_a_gt_zero = model.eval_condition (sval_size_a,
+ GT_EXPR,
+ zero_size);
+ }
+ if (auto sval_size_b = model.get_dynamic_extents (base_reg_b))
+ {
+ size_b_gt_zero = model.eval_condition (sval_size_b,
+ GT_EXPR,
+ zero_size);
+ }
+ if (size_a_gt_zero.is_known () && size_b_gt_zero.is_known ())
+ {
+ /* We have results of two different heap allocations, and for
+ each we know if the size was non-zero. */
+ if (size_a_gt_zero.is_false () && size_b_gt_zero.is_false ())
+ {
+ /* Different allocations, both zero-sized. We don't
+ know if they have the same address. */
+ return tristate::unknown ();
+ }
+ else
+ {
+ /* We either have different allocations of non-zero size,
+ or different allocations where one has non-zero size, the
+ other is zero-sized. We know these have different
+ addresses. */
+ return tristate (false);
+ }
+ }
+ if (size_a_gt_zero.is_true () || size_b_gt_zero.is_true ())
+ {
+ /* Different allocations, of which at least one > 0 in size.
+ They must have different addresses. */
+ return tristate (false);
+ }
+ /* At least one size might be zero. */
+ return tristate::unknown ();
+ }
+
+ case RK_SYMBOLIC:
+ case RK_UNKNOWN:
+ /* We don't know that such regions are different from each other. */
+ return tristate::unknown ();
+
+ case RK_STRING:
+ /* For now, don't attempt to decide if string literals alias
+ each other. */
+ return tristate::unknown ();
+ }
+ }
+ else
+ {
+ /* We have two base region instances of different kinds.
+ For many kinds of region this implies they must have different
+ addresses. */
+ if (kind_b == RK_SYMBOLIC || kind_b == RK_UNKNOWN)
+ return tristate::unknown ();
+
+ switch (kind_a)
+ {
+ /* Invalid cases. */
+
+ default:
+ gcc_unreachable ();
+
+ case RK_CODE:
+ case RK_ERRNO:
+ case RK_GLOBALS:
+ case RK_HEAP:
+ case RK_ROOT:
+ case RK_STACK:
+ case RK_THREAD_LOCAL:
+ /* We expect these to be singletons. */
+ gcc_unreachable ();
+
+ case RK_BIT_RANGE:
+ case RK_CAST:
+ case RK_ELEMENT:
+ case RK_FIELD:
+ case RK_OFFSET:
+ case RK_SIZED:
+ /* These aren't base regions. */
+ gcc_unreachable ();
+
+ /* Valid cases. */
+
+ case RK_ALLOCA:
+ case RK_DECL:
+ case RK_FRAME:
+ case RK_FUNCTION:
+ case RK_HEAP_ALLOCATED:
+ case RK_LABEL:
+ case RK_PRIVATE:
+ case RK_STRING:
+ case RK_VAR_ARG:
+ /* We expect regions of these kinds to have different addresses
+ from regions of other kinds (apart from symbolic/unknown). */
+ return tristate (false);
+
+ case RK_SYMBOLIC:
+ case RK_UNKNOWN:
+ /* We don't know anything about such regions. */
+ return tristate::unknown ();
+ }
+ }
}
/* Half of store::eval_alias; called twice for symmetry. */
tristate
store::eval_alias_1 (const region *base_reg_a,
- const region *base_reg_b) const
+ const region *base_reg_b,
+ const region_model &model) const
{
+ gcc_assert (base_reg_a != base_reg_b);
+
/* If they're in different memory spaces, they can't alias. */
{
enum memory_space memspace_a = base_reg_a->get_memory_space ();
We want to ensure that "*ptr" can only clobber things
within REGION's base region. */
tristate ts = eval_alias (pointee->get_base_region (),
- base_reg_b);
+ base_reg_b,
+ model);
if (ts.is_false ())
return tristate::TS_FALSE;
}
}
}
+
return tristate::TS_UNKNOWN;
}
caller_sval =
reg_mgr->get_or_create_unknown_svalue (summary_sval->get_type ());
set_value (mgr, caller_dest_reg,
- caller_sval, nullptr /* uncertainty_t * */);
+ caller_sval, nullptr /* uncertainty_t * */,
+ *cd.get_model ());
}
break;
caller_sval =
reg_mgr->get_or_create_unknown_svalue (summary_sval->get_type ());
set_value (mgr, caller_dest_reg,
- caller_sval, nullptr /* uncertainty_t * */);
+ caller_sval, nullptr /* uncertainty_t * */,
+ *cd.get_model ());
}
break;
void set_value (store_manager *mgr, const region *lhs_reg,
const svalue *rhs_sval,
- uncertainty_t *uncertainty);
+ uncertainty_t *uncertainty,
+ const region_model &model);
void clobber_region (store_manager *mgr, const region *reg);
void purge_region (store_manager *mgr, const region *reg);
void fill_region (store_manager *mgr, const region *reg, const svalue *sval);
cluster_map_t::iterator end () const { return m_cluster_map.end (); }
tristate eval_alias (const region *base_reg_a,
- const region *base_reg_b) const;
+ const region *base_reg_b,
+ const region_model &model) const;
void canonicalize (store_manager *mgr);
void loop_replay_fixup (const store *other_store,
void remove_overlapping_bindings (store_manager *mgr, const region *reg,
uncertainty_t *uncertainty);
tristate eval_alias_1 (const region *base_reg_a,
- const region *base_reg_b) const;
+ const region *base_reg_b,
+ const region_model &model) const;
cluster_map_t m_cluster_map;
tristate
region_svalue::eval_condition (const region_svalue *lhs,
enum tree_code op,
- const region_svalue *rhs)
+ const region_svalue *rhs,
+ const region_model &model)
{
- /* See if they point to the same region. */
+ /* Convert to region_offset representation, and work with that. */
const region *lhs_reg = lhs->get_pointee ();
const region *rhs_reg = rhs->get_pointee ();
- bool ptr_equality = lhs_reg == rhs_reg;
- switch (op)
- {
- default:
- gcc_unreachable ();
- case EQ_EXPR:
- if (ptr_equality)
- return tristate::TS_TRUE;
- else
- return tristate::TS_FALSE;
- break;
+ region_model_manager *mgr = model.get_manager ();
+ region_offset lhs_offset = lhs_reg->get_offset (mgr);
+ region_offset rhs_offset = rhs_reg->get_offset (mgr);
- case NE_EXPR:
- if (ptr_equality)
- return tristate::TS_FALSE;
- else
- return tristate::TS_TRUE;
- break;
-
- case GE_EXPR:
- case LE_EXPR:
- if (ptr_equality)
- return tristate::TS_TRUE;
- break;
-
- case GT_EXPR:
- case LT_EXPR:
- if (ptr_equality)
- return tristate::TS_FALSE;
- break;
- }
-
- return tristate::TS_UNKNOWN;
+ return eval_region_offset_comparison (lhs_offset, op, rhs_offset, model);
}
/* class constant_svalue : public svalue. */
static tristate eval_condition (const region_svalue *lhs_ptr,
enum tree_code op,
- const region_svalue *rhs_ptr);
+ const region_svalue *rhs_ptr,
+ const region_model &model);
private:
const region *m_reg;
--- /dev/null
+#include "analyzer-decls.h"
+
+typedef __SIZE_TYPE__ size_t;
+
+void *
+hide_from_optimizer (const void *ptr) __attribute__((noinline));
+
+void *
+hide_from_optimizer (const void *ptr)
+{
+ return (void *)ptr;
+}
+
+unsigned char global_buf[1024];
+
+void
+test_pointer_eq (unsigned *p, int i, int j)
+{
+ unsigned char local_declared_before_local_buf;
+ unsigned char local_buf[16];
+ unsigned char local_declared_after_local_buf;
+
+ __analyzer_eval (p == hide_from_optimizer (p)); // { dg-warning "TRUE" }
+ __analyzer_eval (local_buf == &local_buf[0]); // { dg-warning "TRUE" }
+
+ /* Comparisons with NULL. */
+ __analyzer_eval (hide_from_optimizer (local_buf) == NULL); // { dg-warning "FALSE" }
+ __analyzer_eval (hide_from_optimizer (NULL) == NULL); // { dg-warning "TRUE" }
+ __analyzer_eval (hide_from_optimizer (NULL) == p); // { dg-warning "UNKNOWN" }
+
+ /* Same concrete base region. */
+ {
+ // Same concrete offsets
+ __analyzer_eval (local_buf == hide_from_optimizer (&local_buf[0])); // { dg-warning "TRUE" }
+
+ // Different concrete offsets
+ __analyzer_eval (local_buf == hide_from_optimizer (&local_buf[1])); // { dg-warning "FALSE" }
+
+ // Same symbolic offset
+ __analyzer_eval (&local_buf[i] == hide_from_optimizer (&local_buf[i])); // { dg-warning "TRUE" }
+
+ // Possibly different symbolic offset
+ {
+ __analyzer_eval (&local_buf[i] == hide_from_optimizer (&local_buf[j])); // { dg-warning "UNKNOWN" }
+ if (i == j)
+ {
+ __analyzer_eval (&local_buf[i] == hide_from_optimizer (&local_buf[j])); // { dg-warning "TRUE" "ideal behavior" { xfail *-*-* } }
+ // { dg-message "UNKNOWN" "current behavior" { target *-*-* } .-1 }
+ }
+ else
+ {
+ __analyzer_eval (&local_buf[i] == hide_from_optimizer (&local_buf[j])); // { dg-warning "FALSE" "ideal behavior" { xfail *-*-* } }
+ // { dg-message "UNKNOWN" "current behavior" { target *-*-* } .-1 }
+ }
+ }
+ }
+
+ /* Different concrete base regions. */
+ {
+ __analyzer_eval (local_buf == hide_from_optimizer (global_buf)); // { dg-warning "FALSE" }
+
+ // Within the buffer, we're definitely not pointing at other locals
+ __analyzer_eval (&local_buf[0] == hide_from_optimizer (&local_declared_before_local_buf)); // { dg-warning "FALSE" }
+ __analyzer_eval (&local_buf[15] == hide_from_optimizer (&local_declared_after_local_buf)); // { dg-warning "FALSE" }
+
+ /* Outside the valid bounds of the buffer is undefined behavior.
+ We currently happen to return FALSE for such attempts to form
+ pointers to neighboring locals. */
+ __analyzer_eval (&local_buf[-1] == hide_from_optimizer (&local_declared_before_local_buf)); // { dg-warning "FALSE" }
+ __analyzer_eval (&local_buf[16] == hide_from_optimizer (&local_declared_after_local_buf)); // { dg-warning "FALSE" }
+ }
+
+ /* Concrete vs symbolic where we know they're different
+ (buf is local, so param p can't point to it). */
+ __analyzer_eval (local_buf == hide_from_optimizer (p)); // { dg-warning "FALSE" }
+
+ /* Concrete vs symbolic where we don't know they're different
+ (glob is global, so param p could point to it). */
+ __analyzer_eval (global_buf == hide_from_optimizer (p)); // { dg-warning "UNKNOWN" }
+
+ // What about symbolic, could different offsets be the same?
+ // TODO: what about overflow of locals, to try to alias (undefined behavior)
+}
+
+void
+test_nonempty_heap_allocs (void)
+{
+ int i;
+
+ // Known non-zero size
+ void *p = __builtin_malloc (1024);
+ void *q = __builtin_malloc (1024);
+
+ __analyzer_eval (hide_from_optimizer (p) == p); // { dg-warning "TRUE" }
+ __analyzer_eval (hide_from_optimizer (p) == q); // { dg-warning "FALSE" }
+
+ __analyzer_eval (hide_from_optimizer (p) == &i); // { dg-warning "FALSE" }
+ __analyzer_eval (hide_from_optimizer (q) == &i); // { dg-warning "FALSE" }
+
+ __builtin_free (p);
+ __builtin_free (q);
+}
+
+void
+test_maybe_empty_heap_allocs (size_t sz_p, size_t sz_q)
+{
+ int i;
+
+ /* These could be zero-sized buffers, which some implementations
+ return null for. */
+ void *p = __builtin_malloc (sz_p);
+ void *q = __builtin_malloc (sz_q);
+
+ __analyzer_eval (hide_from_optimizer (p) == p); // { dg-warning "TRUE" }
+ __analyzer_eval (hide_from_optimizer (p) == q); // { dg-warning "UNKNOWN" }
+
+ __analyzer_eval (hide_from_optimizer (p) == &i); // { dg-warning "FALSE" }
+ __analyzer_eval (hide_from_optimizer (q) == &i); // { dg-warning "FALSE" }
+
+ __builtin_free (p);
+ __builtin_free (q);
+}
+
+void
+test_one_past_end (void)
+{
+ int arr[10];
+ int *end = &arr[10];
+ __analyzer_eval (hide_from_optimizer (end) == &arr[10]); // { dg-warning "TRUE" }
+ __analyzer_eval (hide_from_optimizer (end) == &arr[9]); // { dg-warning "FALSE" }
+}
+
+void
+test_string_literals (void)
+{
+ const char *abc = "abc";
+ const char *def = "def";
+
+ __analyzer_eval (hide_from_optimizer (abc) == &abc[0]); // { dg-warning "TRUE" }
+
+ __analyzer_eval (hide_from_optimizer (abc) == &abc[1]); // { dg-warning "FALSE" "ideal" { xfail *-*-* } }
+ // { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 }
+
+ __analyzer_eval (hide_from_optimizer (abc) == &def[0]); // { dg-warning "FALSE" "ideal" { xfail *-*-* } }
+ // { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 }
+
+ __analyzer_eval (hide_from_optimizer (abc) == &def[1]); // { dg-warning "FALSE" "ideal" { xfail *-*-* } }
+ // { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 }
+}
+
+struct coord {int x; int y; };
+
+void
+test_struct_pointers (void)
+{
+ struct coord c1;
+ struct coord c2;
+
+ __analyzer_eval (hide_from_optimizer (&c1) == &c1.x); // { dg-warning "TRUE" }
+ __analyzer_eval (hide_from_optimizer (&c1) == &c1.y); // { dg-warning "FALSE" }
+
+ __analyzer_eval (hide_from_optimizer (&c1) == &c2.x); // { dg-warning "FALSE" }
+ __analyzer_eval (hide_from_optimizer (&c1) == &c2.y); // { dg-warning "FALSE" }
+}
+
+union u {int x; char y; };
+
+void
+test_union_pointers (void)
+{
+ union u u1;
+ union u u2;
+
+ __analyzer_eval (hide_from_optimizer (&u1) == &u1.x); // { dg-warning "TRUE" }
+ __analyzer_eval (hide_from_optimizer (&u1) == &u1.y); // { dg-warning "TRUE" }
+
+ __analyzer_eval (hide_from_optimizer (&u1) == &u2.x); // { dg-warning "FALSE" }
+ __analyzer_eval (hide_from_optimizer (&u1) == &u2.y); // { dg-warning "FALSE" }
+}
--- /dev/null
+/* { dg-additional-options "-Wno-compare-distinct-pointer-types" { target c } } */
+
+#include "analyzer-decls.h"
+
+const void *
+hide_from_optimizer (void *ptr) __attribute__((noinline));
+
+const void *
+hide_from_optimizer (void *ptr)
+{
+ return ptr;
+}
+
+unsigned char global_buf[1024];
+
+void
+test_pointer_ge (unsigned *p, int i, int j)
+{
+ unsigned char local_declared_before_local_buf;
+ unsigned char local_buf[16];
+ unsigned char local_declared_after_local_buf;
+
+ __analyzer_eval (p >= hide_from_optimizer (p)); // { dg-warning "TRUE" }
+ __analyzer_eval (local_buf >= &local_buf[0]); // { dg-warning "TRUE" }
+
+ /* Same concrete base region. */
+ {
+ // Same concrete offsets
+ __analyzer_eval (local_buf >= hide_from_optimizer (&local_buf[0])); // { dg-warning "TRUE" }
+
+ // Different concrete offsets
+ __analyzer_eval (&local_buf[0] >= hide_from_optimizer (&local_buf[1])); // { dg-warning "FALSE" }
+ __analyzer_eval (&local_buf[1] >= hide_from_optimizer (&local_buf[0])); // { dg-warning "TRUE" }
+
+ // Same symbolic offset
+ __analyzer_eval (&local_buf[i] >= hide_from_optimizer (&local_buf[i])); // { dg-warning "TRUE" }
+
+ // Possibly different symbolic offset
+ {
+ __analyzer_eval (&local_buf[i] >= hide_from_optimizer (&local_buf[j])); // { dg-warning "UNKNOWN" }
+ if (i == j)
+ {
+ __analyzer_eval (&local_buf[i] >= hide_from_optimizer (&local_buf[j])); // { dg-warning "TRUE" "ideal behavior" { xfail *-*-* } }
+ // { dg-message "UNKNOWN" "current behavior" { target *-*-* } .-1 }
+ }
+ else
+ {
+ __analyzer_eval (&local_buf[i] >= hide_from_optimizer (&local_buf[j])); // { dg-warning "UNKNOWN" }
+ }
+ }
+ }
+
+ /* Different concrete base regions. */
+ {
+ __analyzer_eval (local_buf >= hide_from_optimizer (global_buf)); // { dg-warning "UNKNOWN" }
+
+ // We can't compare the addresses of locals
+ __analyzer_eval (&local_buf[0] >= hide_from_optimizer (&local_declared_before_local_buf)); // { dg-warning "UNKNOWN" }
+ __analyzer_eval (&local_buf[15] >= hide_from_optimizer (&local_declared_after_local_buf)); // { dg-warning "UNKNOWN" }
+ }
+
+ /* Concrete vs symbolic where we know they're different
+ (buf is local, so param p can't point to it). */
+ __analyzer_eval (local_buf >= hide_from_optimizer (p)); // { dg-warning "UNKNOWN" "ideal behavior" { xfail *-*-* } }
+ // { dg-message "FALSE" "current behavior" { target *-*-* } .-1 }
+
+ /* Concrete vs symbolic where we don't know they're different
+ (glob is global, so param p could point to it). */
+ __analyzer_eval (global_buf >= hide_from_optimizer (p)); // { dg-warning "UNKNOWN" }
+
+ // What about symbolic, could different offsets be the same?
+ // TODO: what about overflow of locals, to try to alias (undefined behavior)
+}
--- /dev/null
+/* { dg-additional-options "-Wno-compare-distinct-pointer-types" { target c } } */
+
+#include "analyzer-decls.h"
+
+const void *
+hide_from_optimizer (void *ptr) __attribute__((noinline));
+
+const void *
+hide_from_optimizer (void *ptr)
+{
+ return ptr;
+}
+
+unsigned char global_buf[1024];
+
+void
+test_pointer_gt (unsigned *p, int i, int j)
+{
+ unsigned char local_declared_before_local_buf;
+ unsigned char local_buf[16];
+ unsigned char local_declared_after_local_buf;
+
+ __analyzer_eval (p > hide_from_optimizer (p)); // { dg-warning "FALSE" }
+ __analyzer_eval (local_buf > &local_buf[0]); // { dg-warning "FALSE" }
+
+ /* Same concrete base region. */
+ {
+ // Same concrete offsets
+ __analyzer_eval (local_buf > hide_from_optimizer (&local_buf[0])); // { dg-warning "FALSE" }
+
+ // Different concrete offsets
+ __analyzer_eval (&local_buf[0] > hide_from_optimizer (&local_buf[1])); // { dg-warning "FALSE" }
+ __analyzer_eval (&local_buf[1] > hide_from_optimizer (&local_buf[0])); // { dg-warning "TRUE" }
+
+ // Same symbolic offset
+ __analyzer_eval (&local_buf[i] > hide_from_optimizer (&local_buf[i])); // { dg-warning "FALSE" }
+
+ // Possibly different symbolic offset
+ {
+ __analyzer_eval (&local_buf[i] > hide_from_optimizer (&local_buf[j])); // { dg-warning "UNKNOWN" }
+ if (i == j)
+ {
+ __analyzer_eval (&local_buf[i] > hide_from_optimizer (&local_buf[j])); // { dg-warning "FALSE" "ideal behavior" { xfail *-*-* } }
+ // { dg-message "UNKNOWN" "current behavior" { target *-*-* } .-1 }
+ }
+ else
+ {
+ __analyzer_eval (&local_buf[i] > hide_from_optimizer (&local_buf[j])); // { dg-warning "TRUE" "ideal behavior" { xfail *-*-* } }
+ // { dg-message "UNKNOWN" "current behavior" { target *-*-* } .-1 }
+ }
+ }
+ }
+
+ /* Different concrete base regions. */
+ {
+ __analyzer_eval (local_buf > hide_from_optimizer (global_buf)); // { dg-warning "UNKNOWN" }
+
+ // We can't compare the addresses of locals
+ __analyzer_eval (&local_buf[0] > hide_from_optimizer (&local_declared_before_local_buf)); // { dg-warning "UNKNOWN" }
+ __analyzer_eval (&local_buf[15] > hide_from_optimizer (&local_declared_after_local_buf)); // { dg-warning "UNKNOWN" }
+ }
+
+ /* Concrete vs symbolic where we know they're different
+ (buf is local, so param p can't point to it). */
+ __analyzer_eval (local_buf > hide_from_optimizer (p)); // { dg-warning "UNKNOWN" "ideal behavior" { xfail *-*-* } }
+ // { dg-message "FALSE" "current behavior" { target *-*-* } .-1 }
+
+ /* Concrete vs symbolic where we don't know they're different
+ (glob is global, so param p could point to it). */
+ __analyzer_eval (global_buf > hide_from_optimizer (p)); // { dg-warning "UNKNOWN" }
+
+ // What about symbolic, could different offsets be the same?
+ // TODO: what about overflow of locals, to try to alias (undefined behavior)
+}
--- /dev/null
+/* { dg-additional-options "-Wno-compare-distinct-pointer-types" { target c } } */
+
+#include "analyzer-decls.h"
+
+const void *
+hide_from_optimizer (void *ptr) __attribute__((noinline));
+
+const void *
+hide_from_optimizer (void *ptr)
+{
+ return ptr;
+}
+
+unsigned char global_buf[1024];
+
+void
+test_pointer_le (unsigned *p, int i, int j)
+{
+ unsigned char local_declared_before_local_buf;
+ unsigned char local_buf[16];
+ unsigned char local_declared_after_local_buf;
+
+ __analyzer_eval (p <= hide_from_optimizer (p)); // { dg-warning "TRUE" }
+ __analyzer_eval (local_buf <= &local_buf[0]); // { dg-warning "TRUE" }
+
+ /* Same concrete base region. */
+ {
+ // Same concrete offsets
+ __analyzer_eval (local_buf <= hide_from_optimizer (&local_buf[0])); // { dg-warning "TRUE" }
+
+ // Different concrete offsets
+ __analyzer_eval (&local_buf[0] <= hide_from_optimizer (&local_buf[1])); // { dg-warning "TRUE" }
+ __analyzer_eval (&local_buf[1] <= hide_from_optimizer (&local_buf[0])); // { dg-warning "FALSE" }
+
+ // Same symbolic offset
+ __analyzer_eval (&local_buf[i] <= hide_from_optimizer (&local_buf[i])); // { dg-warning "TRUE" }
+
+ // Possibly different symbolic offset
+ {
+ __analyzer_eval (&local_buf[i] <= hide_from_optimizer (&local_buf[j])); // { dg-warning "UNKNOWN" }
+ if (i == j)
+ {
+ __analyzer_eval (&local_buf[i] <= hide_from_optimizer (&local_buf[j])); // { dg-warning "TRUE" "ideal behavior" { xfail *-*-* } }
+ // { dg-message "UNKNOWN" "current behavior" { target *-*-* } .-1 }
+ }
+ else
+ {
+ __analyzer_eval (&local_buf[i] <= hide_from_optimizer (&local_buf[j])); // { dg-warning "UNKNOWN" }
+ }
+ }
+ }
+
+ /* Different concrete base regions. */
+ {
+ __analyzer_eval (local_buf <= hide_from_optimizer (global_buf)); // { dg-warning "UNKNOWN" }
+
+ // We can't compare the addresses of locals
+ __analyzer_eval (&local_buf[0] <= hide_from_optimizer (&local_declared_before_local_buf)); // { dg-warning "UNKNOWN" }
+ __analyzer_eval (&local_buf[15] <= hide_from_optimizer (&local_declared_after_local_buf)); // { dg-warning "UNKNOWN" }
+ }
+
+ /* Concrete vs symbolic where we know they're different
+ (buf is local, so param p can't point to it). */
+ __analyzer_eval (local_buf <= hide_from_optimizer (p)); // { dg-warning "UNKNOWN" "ideal behavior" { xfail *-*-* } }
+ // { dg-message "FALSE" "current behavior" { target *-*-* } .-1 }
+
+ /* Concrete vs symbolic where we don't know they're different
+ (glob is global, so param p could point to it). */
+ __analyzer_eval (global_buf <= hide_from_optimizer (p)); // { dg-warning "UNKNOWN" }
+
+ // What about symbolic, could different offsets be the same?
+ // TODO: what about overflow of locals, to try to alias (undefined behavior)
+}
--- /dev/null
+/* { dg-additional-options "-Wno-compare-distinct-pointer-types" { target c } } */
+
+#include "analyzer-decls.h"
+
+const void *
+hide_from_optimizer (void *ptr) __attribute__((noinline));
+
+const void *
+hide_from_optimizer (void *ptr)
+{
+ return ptr;
+}
+
+unsigned char global_buf[1024];
+
+void
+test_pointer_lt (unsigned *p, int i, int j)
+{
+ unsigned char local_declared_before_local_buf;
+ unsigned char local_buf[16];
+ unsigned char local_declared_after_local_buf;
+
+ __analyzer_eval (p < hide_from_optimizer (p)); // { dg-warning "FALSE" }
+ __analyzer_eval (local_buf < &local_buf[0]); // { dg-warning "FALSE" }
+
+ /* Same concrete base region. */
+ {
+ // Same concrete offsets
+ __analyzer_eval (local_buf < hide_from_optimizer (&local_buf[0])); // { dg-warning "FALSE" }
+
+ // Different concrete offsets
+ __analyzer_eval (&local_buf[0] < hide_from_optimizer (&local_buf[1])); // { dg-warning "TRUE" }
+ __analyzer_eval (&local_buf[1] < hide_from_optimizer (&local_buf[0])); // { dg-warning "FALSE" }
+
+ // Same symbolic offset
+ __analyzer_eval (&local_buf[i] < hide_from_optimizer (&local_buf[i])); // { dg-warning "FALSE" }
+
+ // Possibly different symbolic offset
+ {
+ __analyzer_eval (&local_buf[i] < hide_from_optimizer (&local_buf[j])); // { dg-warning "UNKNOWN" }
+ if (i == j)
+ {
+ __analyzer_eval (&local_buf[i] < hide_from_optimizer (&local_buf[j])); // { dg-warning "FALSE" "ideal behavior" { xfail *-*-* } }
+ // { dg-message "UNKNOWN" "current behavior" { target *-*-* } .-1 }
+ }
+ else
+ {
+ __analyzer_eval (&local_buf[i] < hide_from_optimizer (&local_buf[j])); // { dg-warning "TRUE" "ideal behavior" { xfail *-*-* } }
+ // { dg-message "UNKNOWN" "current behavior" { target *-*-* } .-1 }
+ }
+ }
+ }
+
+ /* Different concrete base regions. */
+ {
+ __analyzer_eval (local_buf < hide_from_optimizer (global_buf)); // { dg-warning "UNKNOWN" }
+
+ // We can't compare the addresses of locals
+ __analyzer_eval (&local_buf[0] < hide_from_optimizer (&local_declared_before_local_buf)); // { dg-warning "UNKNOWN" }
+ __analyzer_eval (&local_buf[15] < hide_from_optimizer (&local_declared_after_local_buf)); // { dg-warning "UNKNOWN" }
+ }
+
+ /* Concrete vs symbolic where we know they're different
+ (buf is local, so param p can't point to it). */
+ __analyzer_eval (local_buf < hide_from_optimizer (p)); // { dg-warning "UNKNOWN" "ideal behavior" { xfail *-*-* } }
+ // { dg-message "FALSE" "current behavior" { target *-*-* } .-1 }
+
+ /* Concrete vs symbolic where we don't know they're different
+ (glob is global, so param p could point to it). */
+ __analyzer_eval (global_buf < hide_from_optimizer (p)); // { dg-warning "UNKNOWN" }
+
+ // What about symbolic, could different offsets be the same?
+ // TODO: what about overflow of locals, to try to alias (undefined behavior)
+}
--- /dev/null
+#include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+template <typename Src, typename Dst>
+Dst *
+ptr_cast (Src *ptr) __attribute__((noinline));
+
+template <typename Src, typename Dst>
+Dst *
+ptr_cast (Src *ptr)
+{
+ return (Dst *)ptr;
+}
+
+void test_equality (unsigned *p)
+{
+ __analyzer_eval (p == (unsigned *)ptr_cast<unsigned, signed> (p)); // { dg-warning "TRUE" }
+
+ unsigned char buf[16];
+ __analyzer_eval (buf == &buf[0]); // { dg-warning "TRUE" }
+
+ __analyzer_eval (buf == (unsigned char*)ptr_cast<unsigned char, signed char> (&buf[0])); // { dg-warning "TRUE" }
+}